Class: Esp::HttpServer

Inherits:
Object
  • Object
show all
Defined in:
lib/esp/http_server.rb

Overview

Localhost HTTP API mirroring the CLI surface. A thin WEBrick shell over Esp::Operations — same operations as ‘bin/esp` and `esp mcp serve`, same payload shapes, different transport. Intended as the backing store for the GUI. WEBrick is thread-per-request, so the long-lived /agent stream doesn’t block other routes.

Routes:

POST /agent             — body: { prompt, provider?, model? } → text/event-stream
GET  /up                — { status, version }  (liveness)
GET  /version           — { version }
GET  /commands          — full Esp::Introspection.command_tree
GET  /providers         — { providers:[{id,default_model,configured}], default }
GET  /active-project    — { root: String|null }
POST /open-project      — body: { root } → { root }  (sets active project)
GET  /projects/recent   — { projects: [{root,name,opened_at}, ...] }
POST /projects/new      — body: { project, mod, format?, author? } → { root, mod, source }
GET  /preferences       — { mods_home, ... } (merged over defaults)
POST /preferences       — body: { mods_home?, ... } → resolved prefs
POST /build             — body: { mod, locale? }
POST /build-all         — body: { locale? }
POST /unpack            — body: { plugin, name? }
POST /install           — body: { mod }
POST /lint              — body: { mod }
POST /i18n/check        — body: { mod }
POST /scaffold          — body: { mod, format?, author?, description?, force? }
POST /extract-scripts   — body: { mod }
POST /records/read      — body: { mod }
POST /records/write     — body: { mod, record }
POST /dialogue/write    — body: { mod, spec }
GET  /plugins           — query: config?
GET  /refs/find         — query: q, type, like, limit, show
POST /refs/resolve      — body: { ids: [String] } → { types: {id => type} }
GET  /ollama/models     — { models: [String] } from Ollama's /api/tags
GET  /diff              — query: scope?, root?  (working-tree changes + diffs)
POST /approve           — body: { paths, root? }  (git add)
POST /reject            — body: { paths, root? }  (restore/delete — destructive)

CORS is wide open (Access-Control-Allow-Origin: *) — dev-only tool.

Constant Summary collapse

DEFAULT_PORT =
4567
ROUTES =

[verb, path, Operations method]. Every route is a thin pass of the parsed input (query for GET, JSON body for POST) to one operation, so adding an endpoint is a one-line row here.

[
  [:get,  '/up',              :health],
  [:get,  '/version',         :version],
  [:get,  '/commands',        :commands],
  [:get,  '/providers',       :providers],
  [:get,  '/active-project',  :active_project],
  [:post, '/open-project',    :open_project],
  [:get,  '/projects/recent', :projects_recent],
  [:post, '/projects/new',    :projects_new],
  [:get,  '/preferences',     :preferences],
  [:post, '/preferences',     :preferences_update],
  [:get,  '/plugins',         :plugins_list],
  [:get,  '/refs/find',       :refs_find],
  [:post, '/refs/resolve',    :refs_resolve],
  [:get,  '/ollama/models',   :ollama_models],
  [:get,  '/diff',            :diff],
  [:post, '/approve',         :approve],
  [:post, '/reject',          :reject],
  [:post, '/build',           :build],
  [:post, '/build-all',       :build_all],
  [:post, '/unpack',          :unpack],
  [:post, '/install',         :install],
  [:post, '/lint',            :lint],
  [:post, '/i18n/check',      :i18n_check],
  [:post, '/scaffold',        :scaffold],
  [:post, '/extract-scripts', :extract_scripts],
  [:post, '/records/read',    :records_read],
  [:post, '/records/write',   :record_write],
  [:post, '/dialogue/write',  :dialogue_write]
].freeze

Instance Method Summary collapse

Constructor Details

#initialize(port: DEFAULT_PORT, logger: nil, agent: nil) ⇒ HttpServer

Returns a new instance of HttpServer.



94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/esp/http_server.rb', line 94

def initialize(port: DEFAULT_PORT, logger: nil, agent: nil)
  @port = port
  # An injected agent (tests) is used as-is; otherwise each /agent request
  # builds one for its chosen provider/model.
  @agent = agent
  @server = WEBrick::HTTPServer.new(
    Port: port,
    Logger: logger || WEBrick::Log.new($stdout, WEBrick::Log::WARN),
    AccessLog: []
  )
  register_routes
end

Instance Method Details

#startObject



107
108
109
110
111
# File 'lib/esp/http_server.rb', line 107

def start
  trap('INT')  { stop }
  trap('TERM') { stop }
  @server.start
end

#stopObject



113
114
115
# File 'lib/esp/http_server.rb', line 113

def stop
  @server.shutdown
end