Module: Xeno::Standalone

Defined in:
lib/xeno/standalone.rb,
lib/xeno/standalone/local_secret.rb,
lib/xeno/standalone/model_refresh.rb

Overview

:nodoc:

Class Method Summary collapse

Class Method Details

.boot!Object

Boot: initialize the app, then bring the database up to date — the engine's migrations plus Solid Queue's tables (loaded straight from the solid_queue gem's install template).



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/xeno/standalone.rb', line 76

def boot!
  ENV["RAILS_ENV"] ||= "development"
  FileUtils.mkdir_p(StandaloneApplication::APP_ROOT.join("storage"))

  app = Rails.application || StandaloneApplication.instance
  unless app.initialized?
    app.initialize!
    app.routes.draw do
      mount Xeno::Engine => "/agent"
      root to: redirect("/agent/dev")
    end
  end

  migrate!
  refresh_models!
  write_recurring_config!
  Rails.application
end

.local_secret(root) ⇒ Object

A persisted local secret so standalone sessions survive restarts without anyone managing SECRET_KEY_BASE. Owner-only (0600) — it signs cookies/sessions; looser pre-existing files are tightened on read.

Lives apart from xeno/standalone so tests can exercise it without defining the Rails::Application (which self-registers on inheritance).



16
17
18
19
20
21
22
23
24
25
# File 'lib/xeno/standalone/local_secret.rb', line 16

def local_secret(root)
  path = root.join("storage", ".local_secret")
  FileUtils.mkdir_p(path.dirname)
  if path.file?
    File.chmod(0o600, path)
    return path.read.strip
  end

  SecureRandom.hex(64).tap { |secret| File.write(path, secret, perm: 0o600) }
end

.migrate!Object



113
114
115
116
117
118
119
120
# File 'lib/xeno/standalone.rb', line 113

def migrate!
  ActiveRecord::MigrationContext.new([ Xeno::Engine.root.join("db", "migrate").to_s ]).migrate

  connection = ActiveRecord::Base.lease_connection
  unless connection.table_exists?("solid_queue_jobs")
    load queue_schema_path
  end
end

.puma_config_pathObject

The gem-owned puma config used by xeno server — Solid Queue runs inside Puma (SOLID_QUEUE_IN_PUMA), one process total.



129
130
131
# File 'lib/xeno/standalone.rb', line 129

def puma_config_path
  File.expand_path("standalone/puma.rb", __dir__)
end

.queue_schema_pathObject



122
123
124
125
# File 'lib/xeno/standalone.rb', line 122

def queue_schema_path
  spec = Gem::Specification.find_by_name("solid_queue")
  File.join(spec.gem_dir, "lib", "generators", "solid_queue", "install", "templates", "db", "queue_schema.rb")
end

.refresh_models!Object

First boot only (the registry table is empty): pulls the live model catalog so current model ids resolve without a manual refresh. The definition loads first because agent.rb's RubyLLM.configure sets the provider keys. Never fatal, never repeated once the table has rows; XENO_SKIP_MODEL_REFRESH=1 skips it.



11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/xeno/standalone/model_refresh.rb', line 11

def refresh_models!
  return if ENV["XENO_SKIP_MODEL_REFRESH"]

  Xeno.definition
  store = RubyLLM.config.model_registry_store
  return unless store.respond_to?(:none?) && store.none?

  Rails.logger.info "xeno: first boot — refreshing the model registry"
  RubyLLM.models.refresh!
  Rails.logger.info "xeno: model registry ready (#{store.count} models)"
rescue StandardError => error
  Rails.logger.warn "xeno: model registry refresh failed (#{error.class}: #{error.message}). " \
                    "Continuing; run RubyLLM.models.refresh! manually if a model fails to resolve."
end

.write_recurring_config!Object

Solid Queue's recurring machinery fires the reaper (always) and the compiled schedules (outside development) in standalone mode. The file lives under storage/ so the scaffold stays four visible files; the env var points Solid Queue's supervisor at it.



98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/xeno/standalone.rb', line 98

def write_recurring_config!
  section = { Xeno::Schedules::REAPER_KEY => Xeno::Schedules.reaper_entry }
  unless Rails.env.development?
    Xeno.definition.schedules.each do |name, schedule|
      section["#{Xeno::Schedules::MANAGED_PREFIX}#{name}"] = {
        "class" => "Xeno::ScheduleJob", "args" => [ name ], "schedule" => schedule.cron
      }
    end
  end

  path = StandaloneApplication::APP_ROOT.join("storage", "recurring.yml")
  File.write(path, YAML.dump({ Rails.env.to_s => section }))
  ENV["SOLID_QUEUE_RECURRING_SCHEDULE"] ||= File.join("storage", "recurring.yml")
end