samagotchi 0.91.0 → 0.93.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +88 -1
- data/README.md +5 -2
- data/bin/chi +8 -8
- data/docs/architecture.md +20 -16
- data/docs/bundle-authoring.md +2 -1
- data/docs/cli.md +5 -5
- data/docs/configuration.md +3 -1
- data/docs/context.md +5 -3
- data/docs/guardrails.md +4 -5
- data/docs/hooks.md +3 -3
- data/docs/internals/background-tasks.md +2 -2
- data/docs/internals/bundles.md +4 -4
- data/docs/internals/engine.md +27 -8
- data/docs/internals/tool-guardrails.md +10 -2
- data/docs/memory.md +6 -0
- data/docs/plugins.md +30 -13
- data/docs/releasing.md +3 -0
- data/docs/sessions.md +4 -4
- data/docs/testing.md +2 -1
- data/lib/samagotchi/bootstrap/probe.rb +2 -2
- data/lib/samagotchi/bridge.rb +8 -9
- data/lib/samagotchi/bundle_command/check.rb +43 -12
- data/lib/samagotchi/bundle_command/scaffold.rb +12 -0
- data/lib/samagotchi/bundle_command/test_run.rb +12 -0
- data/lib/samagotchi/bundle_command/try/command.rb +164 -0
- data/lib/samagotchi/bundle_command/try/render.rb +80 -0
- data/lib/samagotchi/bundle_command/try/run.rb +35 -17
- data/lib/samagotchi/bundle_command/try/sandbox.rb +113 -34
- data/lib/samagotchi/bundle_command/try/trace.rb +20 -26
- data/lib/samagotchi/bundle_command.rb +11 -213
- data/lib/samagotchi/bundle_session_settings.rb +82 -45
- data/lib/samagotchi/bundles/core/manifest.yml +2 -2
- data/lib/samagotchi/bundles/cost-cap/manifest.yml +6 -5
- data/lib/samagotchi/bundles/cost-cap/plugin.rb +57 -12
- data/lib/samagotchi/bundles/system/manifest.yml +1 -1
- data/lib/samagotchi/child_env.rb +50 -0
- data/lib/samagotchi/child_reports.rb +12 -14
- data/lib/samagotchi/client_id.rb +18 -0
- data/lib/samagotchi/config/restart_watch.rb +112 -0
- data/lib/samagotchi/config.rb +18 -59
- data/lib/samagotchi/engine.rb +26 -26
- data/lib/samagotchi/guardrails/data_readers.rb +34 -30
- data/lib/samagotchi/guardrails/parent_levels.rb +31 -0
- data/lib/samagotchi/guardrails/parent_policy.rb +9 -20
- data/lib/samagotchi/guardrails/shell_lex.rb +28 -7
- data/lib/samagotchi/hook_question.rb +21 -0
- data/lib/samagotchi/hook_routes.rb +6 -6
- data/lib/samagotchi/hooks/registry.rb +7 -5
- data/lib/samagotchi/host_registry.rb +1 -1
- data/lib/samagotchi/kernel_loop.rb +14 -3
- data/lib/samagotchi/llm/anthropic_messages.rb +5 -0
- data/lib/samagotchi/llm/chat_loop.rb +3 -1
- data/lib/samagotchi/llm/errors.rb +4 -4
- data/lib/samagotchi/memory_bundle/builder.rb +3 -2
- data/lib/samagotchi/memory_bundle/installed_bundle.rb +7 -2
- data/lib/samagotchi/memory_bundle/installer.rb +1 -0
- data/lib/samagotchi/memory_bundle/manifest.rb +42 -8
- data/lib/samagotchi/memory_bundle/provenance.rb +2 -1
- data/lib/samagotchi/parent_answers_command.rb +6 -6
- data/lib/samagotchi/parent_report.rb +12 -0
- data/lib/samagotchi/plugin/context.rb +6 -5
- data/lib/samagotchi/plugin/http_transport.rb +14 -3
- data/lib/samagotchi/plugin/sessions.rb +2 -3
- data/lib/samagotchi/question_desk.rb +12 -9
- data/lib/samagotchi/reply_wait/poll.rb +267 -0
- data/lib/samagotchi/reply_wait.rb +51 -190
- data/lib/samagotchi/sandbox_layout.rb +66 -0
- data/lib/samagotchi/send_command/parent_gate.rb +83 -0
- data/lib/samagotchi/send_command/wait_target.rb +55 -0
- data/lib/samagotchi/send_command.rb +34 -103
- data/lib/samagotchi/session.rb +10 -9
- data/lib/samagotchi/session_inbox.rb +31 -8
- data/lib/samagotchi/session_manager.rb +60 -35
- data/lib/samagotchi/session_metrics.rb +22 -3
- data/lib/samagotchi/sessions_command.rb +4 -4
- data/lib/samagotchi/task_watch.rb +64 -21
- data/lib/samagotchi/terminal_ui/attached_loop.rb +3 -3
- data/lib/samagotchi/terminal_ui/formatting.rb +8 -5
- data/lib/samagotchi/testing/bundle.rb +33 -20
- data/lib/samagotchi/testing/host.rb +2 -8
- data/lib/samagotchi/testing/install.rb +26 -17
- data/lib/samagotchi/testing/recorder.rb +6 -9
- data/lib/samagotchi/testing/sandbox.rb +45 -37
- data/lib/samagotchi/testing/waiting.rb +5 -7
- data/lib/samagotchi/tool_response.rb +28 -6
- data/lib/samagotchi/tools/delegate.rb +10 -10
- data/lib/samagotchi/tools/delegate_cursor.rb +32 -8
- data/lib/samagotchi/tools/delegate_relay.rb +31 -16
- data/lib/samagotchi/tools/delegate_wait.rb +4 -4
- data/lib/samagotchi/tools/task_runtime.rb +2 -2
- data/lib/samagotchi/turn_checkpoint.rb +23 -10
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +46 -63
- data/lib/samagotchi/web/public/app.js +130 -554
- data/lib/samagotchi/web/public/cards_place.js +4 -1
- data/lib/samagotchi/web/public/images.js +6 -0
- data/lib/samagotchi/web/public/index.html +1 -1
- data/lib/samagotchi/web/public/model_pick.js +10 -0
- data/lib/samagotchi/web/public/send_turn.js +173 -0
- data/lib/samagotchi/web/public/session_actions.js +203 -0
- data/lib/samagotchi/web/public/start_page.js +236 -0
- data/lib/samagotchi/web/public/toasts.js +17 -9
- data/lib/samagotchi/web/public/unqueue.js +20 -4
- data/lib/samagotchi/worker.rb +77 -185
- data/lib/samagotchi/worker_idle_exit.rb +20 -5
- data/lib/samagotchi/worker_inbound.rb +5 -5
- data/lib/samagotchi/worker_task_wakes.rb +160 -0
- data/lib/samagotchi/worker_wakes.rb +0 -3
- metadata +15 -1
data/lib/samagotchi/web/app.rb
CHANGED
|
@@ -152,7 +152,8 @@ module Samagotchi
|
|
|
152
152
|
private
|
|
153
153
|
|
|
154
154
|
# The routes, first match wins: a handler takes the request and the
|
|
155
|
-
# path's captures (the session id, an image's name).
|
|
155
|
+
# path's captures (the session id, an image's name). A :session route
|
|
156
|
+
# runs in #session_route: an ArgumentError is its unknown session.
|
|
156
157
|
ROUTES = [
|
|
157
158
|
["GET", %r{\A/(?:index\.html)?\z}, :serve_index],
|
|
158
159
|
["GET", %r{\A/api/sessions\z}, :handle_list],
|
|
@@ -163,26 +164,26 @@ module Samagotchi
|
|
|
163
164
|
["GET", %r{\A/api/events\z}, :handle_events],
|
|
164
165
|
["POST", %r{\A/api/render\z}, :handle_render],
|
|
165
166
|
["GET", %r{\A/api/sessions/([^/]+)/stream\z}, :handle_stream],
|
|
166
|
-
["GET", %r{\A/api/sessions/([^/]+)/output\z}, :handle_output],
|
|
167
|
-
["POST", %r{\A/api/sessions/([^/]+)/cancel\z}, :handle_cancel],
|
|
168
|
-
["POST", %r{\A/api/sessions/([^/]+)/tasks/([^/]+)/stop\z}, :handle_task_stop],
|
|
169
|
-
["POST", %r{\A/api/sessions/([^/]+)/turn/([^/]+)/dequeue\z}, :handle_dequeue_turn],
|
|
170
|
-
["POST", %r{\A/api/sessions/([^/]+)/stop\z}, :handle_stop],
|
|
171
|
-
["POST", %r{\A/api/sessions/([^/]+)/restart\z}, :handle_restart],
|
|
172
|
-
["POST", %r{\A/api/sessions/([^/]+)/archive\z}, :handle_archive],
|
|
173
|
-
["POST", %r{\A/api/sessions/([^/]+)/unarchive\z}, :handle_unarchive],
|
|
174
|
-
["POST", %r{\A/api/sessions/([^/]+)/turn\z}, :handle_turn],
|
|
167
|
+
["GET", %r{\A/api/sessions/([^/]+)/output\z}, :handle_output, :session],
|
|
168
|
+
["POST", %r{\A/api/sessions/([^/]+)/cancel\z}, :handle_cancel, :session],
|
|
169
|
+
["POST", %r{\A/api/sessions/([^/]+)/tasks/([^/]+)/stop\z}, :handle_task_stop, :session],
|
|
170
|
+
["POST", %r{\A/api/sessions/([^/]+)/turn/([^/]+)/dequeue\z}, :handle_dequeue_turn, :session],
|
|
171
|
+
["POST", %r{\A/api/sessions/([^/]+)/stop\z}, :handle_stop, :session],
|
|
172
|
+
["POST", %r{\A/api/sessions/([^/]+)/restart\z}, :handle_restart, :session],
|
|
173
|
+
["POST", %r{\A/api/sessions/([^/]+)/archive\z}, :handle_archive, :session],
|
|
174
|
+
["POST", %r{\A/api/sessions/([^/]+)/unarchive\z}, :handle_unarchive, :session],
|
|
175
|
+
["POST", %r{\A/api/sessions/([^/]+)/turn\z}, :handle_turn, :session],
|
|
175
176
|
["POST", %r{\A/api/sessions/([^/]+)/answer\z}, :handle_question_answer],
|
|
176
177
|
["POST", %r{\A/api/sessions/([^/]+)/question/dismiss\z}, :handle_question_dismiss],
|
|
177
|
-
["POST", %r{\A/api/sessions/([^/]+)/command\z}, :handle_command],
|
|
178
|
+
["POST", %r{\A/api/sessions/([^/]+)/command\z}, :handle_command, :session],
|
|
178
179
|
["POST", %r{\A/api/sessions/([^/]+)/images\z}, :handle_image_upload],
|
|
179
180
|
["GET", %r{\A/api/sessions/([^/]+)/images/([^/]+)\z}, :handle_image],
|
|
180
|
-
["GET", %r{\A/api/sessions/([^/]+)/context\z}, :handle_context_list],
|
|
181
|
-
["GET", %r{\A/api/sessions/([^/]+)/context/([^/]+)\z}, :handle_context_show],
|
|
182
|
-
["POST", %r{\A/api/sessions/([^/]+)/context\z}, :handle_context_add],
|
|
183
|
-
["DELETE", %r{\A/api/sessions/([^/]+)/context/([^/]+)\z}, :handle_context_delete],
|
|
184
|
-
["GET", %r{\A/api/sessions/([^/]+)\z}, :handle_show],
|
|
185
|
-
["DELETE", %r{\A/api/sessions/([^/]+)\z}, :handle_delete]
|
|
181
|
+
["GET", %r{\A/api/sessions/([^/]+)/context\z}, :handle_context_list, :session],
|
|
182
|
+
["GET", %r{\A/api/sessions/([^/]+)/context/([^/]+)\z}, :handle_context_show, :session],
|
|
183
|
+
["POST", %r{\A/api/sessions/([^/]+)/context\z}, :handle_context_add, :session],
|
|
184
|
+
["DELETE", %r{\A/api/sessions/([^/]+)/context/([^/]+)\z}, :handle_context_delete, :session],
|
|
185
|
+
["GET", %r{\A/api/sessions/([^/]+)\z}, :handle_show, :session],
|
|
186
|
+
["DELETE", %r{\A/api/sessions/([^/]+)\z}, :handle_delete, :session]
|
|
186
187
|
].freeze
|
|
187
188
|
|
|
188
189
|
def route(env)
|
|
@@ -195,7 +196,7 @@ module Samagotchi
|
|
|
195
196
|
return denied if denied
|
|
196
197
|
end
|
|
197
198
|
|
|
198
|
-
ROUTES.each do |verb, pattern, handler|
|
|
199
|
+
ROUTES.each do |verb, pattern, handler, kind|
|
|
199
200
|
next unless req.request_method == verb && (m = pattern.match(req.path_info))
|
|
200
201
|
# Every /api/sessions/:id route: an id that isn't one (Session.valid_id?,
|
|
201
202
|
# "..", a %2F) is an unknown session before any handler makes a path.
|
|
@@ -203,6 +204,8 @@ module Samagotchi
|
|
|
203
204
|
return error_response(404, "not_found", "Session not found: #{m.captures.first.to_s[0, 80]}")
|
|
204
205
|
end
|
|
205
206
|
|
|
207
|
+
return session_route { send(handler, req, *m.captures) } if kind == :session
|
|
208
|
+
|
|
206
209
|
return send(handler, req, *m.captures)
|
|
207
210
|
end
|
|
208
211
|
# Anything else is a file under public_dir (/assets/*, /public/* or
|
|
@@ -213,6 +216,16 @@ module Samagotchi
|
|
|
213
216
|
error_response(500, "internal_error", e.message)
|
|
214
217
|
end
|
|
215
218
|
|
|
219
|
+
# A :session route's handler: the ArgumentError its session lookup
|
|
220
|
+
# raises (Session.load, the manager's find) is a 404 not_found with
|
|
221
|
+
# its message. Only these routes: elsewhere an ArgumentError is a bug,
|
|
222
|
+
# #route's 500. A handler's own rescues run first.
|
|
223
|
+
def session_route
|
|
224
|
+
yield
|
|
225
|
+
rescue ArgumentError => e
|
|
226
|
+
error_response(404, "not_found", e.message)
|
|
227
|
+
end
|
|
228
|
+
|
|
216
229
|
# API requests at debug level (not the page and its assets): method,
|
|
217
230
|
# path, status, time. Never a body or the query; only a tail=1 read
|
|
218
231
|
# (the page's end-of-turn re-read) and a timing=1 one (its fallback
|
|
@@ -737,7 +750,7 @@ module Samagotchi
|
|
|
737
750
|
rescue SessionManager::ArchiveRefused => e
|
|
738
751
|
[nil, error_response(409, e.reason == :scratch ? "scratch" : "busy", e.message)]
|
|
739
752
|
rescue SessionManager::OwnedByTUI
|
|
740
|
-
[nil,
|
|
753
|
+
[nil, owned_by_repl(given)]
|
|
741
754
|
rescue ModelProfile::MissingModel => e
|
|
742
755
|
[nil, error_response(400, "invalid_model", e.message)]
|
|
743
756
|
rescue Session::AmbiguousId => e
|
|
@@ -839,8 +852,6 @@ module Samagotchi
|
|
|
839
852
|
last_event_id: turn_snapshot && turn_snapshot["event_id"],
|
|
840
853
|
timing: timing
|
|
841
854
|
})
|
|
842
|
-
rescue ArgumentError => e
|
|
843
|
-
error_response(404, "not_found", e.message)
|
|
844
855
|
end
|
|
845
856
|
|
|
846
857
|
# A list in the worker's turn +snapshot+ (queued prompts, commands,
|
|
@@ -948,8 +959,6 @@ module Samagotchi
|
|
|
948
959
|
**viewer_capabilities(req),
|
|
949
960
|
timing: timing
|
|
950
961
|
})
|
|
951
|
-
rescue ArgumentError => e
|
|
952
|
-
error_response(404, "not_found", e.message)
|
|
953
962
|
end
|
|
954
963
|
|
|
955
964
|
# The folder the live tail's answer's file refs must stay under: the
|
|
@@ -1132,8 +1141,6 @@ module Samagotchi
|
|
|
1132
1141
|
end
|
|
1133
1142
|
rescue SessionManager::OwnedByTUI => e
|
|
1134
1143
|
error_response(409, "owned_by_tui", e.message)
|
|
1135
|
-
rescue ArgumentError => e
|
|
1136
|
-
error_response(404, "not_found", e.message)
|
|
1137
1144
|
end
|
|
1138
1145
|
|
|
1139
1146
|
# Hand a request to the session's live Bridge (+client+, nil without
|
|
@@ -1169,6 +1176,12 @@ module Samagotchi
|
|
|
1169
1176
|
not_live(id)
|
|
1170
1177
|
end
|
|
1171
1178
|
|
|
1179
|
+
# 409 owned_by_tui in the words a refused archive, delete or continue
|
|
1180
|
+
# shows (stop, restart, a turn and a command show OwnedByTUI's own).
|
|
1181
|
+
def owned_by_repl(id)
|
|
1182
|
+
error_response(409, "owned_by_tui", SessionManager::OwnedByTUI.new(id).message)
|
|
1183
|
+
end
|
|
1184
|
+
|
|
1172
1185
|
def not_live(id)
|
|
1173
1186
|
error_response(503, "not_live", "no live bridge for session #{id}")
|
|
1174
1187
|
end
|
|
@@ -1204,8 +1217,6 @@ module Samagotchi
|
|
|
1204
1217
|
# Strip wire tokens server-side so the client never sees <|tool_call> etc.
|
|
1205
1218
|
rendered = raw.map { |chunk| OutputFormatter.strip(chunk) }.reject(&:empty?)
|
|
1206
1219
|
json_response(200, { session_id: session.id, output: rendered })
|
|
1207
|
-
rescue ArgumentError => e
|
|
1208
|
-
error_response(404, "not_found", e.message)
|
|
1209
1220
|
end
|
|
1210
1221
|
|
|
1211
1222
|
def handle_turn(req, id)
|
|
@@ -1231,18 +1242,16 @@ module Samagotchi
|
|
|
1231
1242
|
result = SessionManager.deliver_turn(id, prompt: prompt.to_s, client_id: client_id, images: images,
|
|
1232
1243
|
delivery: delivery, state_dir: @state_dir, manager: @manager,
|
|
1233
1244
|
bridge: -> { live_bridge_client(id) })
|
|
1234
|
-
case result
|
|
1245
|
+
case result.status
|
|
1235
1246
|
when :accepted
|
|
1236
1247
|
record_history(body, prompt)
|
|
1237
|
-
json_response(202, result
|
|
1238
|
-
when :refused then json_response(result
|
|
1239
|
-
when :timeout then error_response(504, "worker_timeout", result
|
|
1248
|
+
json_response(202, result.ack)
|
|
1249
|
+
when :refused then json_response(result.code, result.ack)
|
|
1250
|
+
when :timeout then error_response(504, "worker_timeout", result.detail)
|
|
1240
1251
|
else error_response(500, "enqueue_failed", "could not write turn input")
|
|
1241
1252
|
end
|
|
1242
1253
|
rescue SessionManager::OwnedByTUI => e
|
|
1243
1254
|
error_response(409, "owned_by_tui", e.message)
|
|
1244
|
-
rescue ArgumentError => e
|
|
1245
|
-
error_response(404, "not_found", e.message)
|
|
1246
1255
|
end
|
|
1247
1256
|
|
|
1248
1257
|
# A turn's images as [{file:, name:}] (refs to uploads already in the
|
|
@@ -1323,8 +1332,7 @@ module Samagotchi
|
|
|
1323
1332
|
def handle_cancel(req, id)
|
|
1324
1333
|
@session_class.load(id, state_dir: default_state_dir)
|
|
1325
1334
|
|
|
1326
|
-
|
|
1327
|
-
parsed = body.nil? || body.strip.empty? ? nil : parse_json(body)
|
|
1335
|
+
parsed = parse_json(request_body(req))
|
|
1328
1336
|
reason = (parsed["reason"] if parsed.is_a?(Hash)).to_s.strip
|
|
1329
1337
|
reason = "user" if reason.empty?
|
|
1330
1338
|
|
|
@@ -1337,8 +1345,6 @@ module Samagotchi
|
|
|
1337
1345
|
when 409 then json_response(409, { error: "not_running", detail: "no active turn to cancel", session_id: id })
|
|
1338
1346
|
end
|
|
1339
1347
|
end
|
|
1340
|
-
rescue ArgumentError => e
|
|
1341
|
-
error_response(404, "not_found", e.message)
|
|
1342
1348
|
end
|
|
1343
1349
|
|
|
1344
1350
|
# A task id as TaskRuntime.generate_task_id makes it.
|
|
@@ -1361,8 +1367,6 @@ module Samagotchi
|
|
|
1361
1367
|
when 409 then json_response(409, { error: "not_running", status: body["status"], task_id: task_id, session_id: id })
|
|
1362
1368
|
end
|
|
1363
1369
|
end
|
|
1364
|
-
rescue ArgumentError => e
|
|
1365
|
-
error_response(404, "not_found", e.message)
|
|
1366
1370
|
end
|
|
1367
1371
|
|
|
1368
1372
|
# POST /api/sessions/:id/turn/:enqueued_id/dequeue {client_id?}: the ×
|
|
@@ -1374,8 +1378,7 @@ module Samagotchi
|
|
|
1374
1378
|
# 404 unknown_enqueued_id pass through; the rest as every relay.
|
|
1375
1379
|
def handle_dequeue_turn(req, id, enqueued_id)
|
|
1376
1380
|
@session_class.load(id, state_dir: default_state_dir)
|
|
1377
|
-
|
|
1378
|
-
parsed = body.nil? || body.strip.empty? ? nil : parse_json(body)
|
|
1381
|
+
parsed = parse_json(request_body(req))
|
|
1379
1382
|
client_id = (parsed["client_id"] if parsed.is_a?(Hash))
|
|
1380
1383
|
|
|
1381
1384
|
request = ->(client) { client.dequeue(enqueued_id: enqueued_id, client_id: client_id) }
|
|
@@ -1388,8 +1391,6 @@ module Samagotchi
|
|
|
1388
1391
|
json_response(404, { error: "unknown_enqueued_id", enqueued_id: enqueued_id, session_id: id }) if body["error"] == "unknown_enqueued_id"
|
|
1389
1392
|
end
|
|
1390
1393
|
end
|
|
1391
|
-
rescue ArgumentError => e
|
|
1392
|
-
error_response(404, "not_found", e.message)
|
|
1393
1394
|
end
|
|
1394
1395
|
|
|
1395
1396
|
def handle_stop(_req, id)
|
|
@@ -1400,8 +1401,6 @@ module Samagotchi
|
|
|
1400
1401
|
json_response(200, { status: "stopped", session_id: id })
|
|
1401
1402
|
rescue SessionManager::OwnedByTUI => e
|
|
1402
1403
|
error_response(409, "owned_by_tui", e.message)
|
|
1403
|
-
rescue ArgumentError => e
|
|
1404
|
-
error_response(404, "not_found", e.message)
|
|
1405
1404
|
end
|
|
1406
1405
|
|
|
1407
1406
|
# POST /api/sessions/:id/restart: hand the session to a new worker on
|
|
@@ -1420,8 +1419,6 @@ module Samagotchi
|
|
|
1420
1419
|
json_response(409, { error: error, reason: e.reason.to_s, detail: e.message, session_id: id })
|
|
1421
1420
|
rescue SessionManager::OwnedByTUI => e
|
|
1422
1421
|
error_response(409, "owned_by_tui", e.message)
|
|
1423
|
-
rescue ArgumentError => e
|
|
1424
|
-
error_response(404, "not_found", e.message)
|
|
1425
1422
|
end
|
|
1426
1423
|
|
|
1427
1424
|
# POST /api/sessions/:id/archive: hide it (and its delegates) from the
|
|
@@ -1434,11 +1431,9 @@ module Samagotchi
|
|
|
1434
1431
|
json_response(200, { status: "archived", session_id: result[:id], archived: result[:archived],
|
|
1435
1432
|
stopped: result[:stopped], discarded: result[:discarded] })
|
|
1436
1433
|
rescue SessionManager::OwnedByTUI
|
|
1437
|
-
|
|
1434
|
+
owned_by_repl(id)
|
|
1438
1435
|
rescue SessionManager::ArchiveRefused => e
|
|
1439
1436
|
error_response(409, e.reason == :scratch ? "scratch" : "busy", e.message)
|
|
1440
|
-
rescue ArgumentError => e
|
|
1441
|
-
error_response(404, "not_found", e.message)
|
|
1442
1437
|
end
|
|
1443
1438
|
|
|
1444
1439
|
# POST /api/sessions/:id/unarchive: back to the lists, delegates too.
|
|
@@ -1446,8 +1441,6 @@ module Samagotchi
|
|
|
1446
1441
|
result = @manager.unarchive_session(id, state_dir: @state_dir)
|
|
1447
1442
|
result[:unarchived].each { |sid| @hub&.touch(sid) }
|
|
1448
1443
|
json_response(200, { status: "unarchived", session_id: result[:id], unarchived: result[:unarchived] })
|
|
1449
|
-
rescue ArgumentError => e
|
|
1450
|
-
error_response(404, "not_found", e.message)
|
|
1451
1444
|
end
|
|
1452
1445
|
|
|
1453
1446
|
# DELETE /api/sessions/:id: the session for good. A live worker is
|
|
@@ -1458,11 +1451,9 @@ module Samagotchi
|
|
|
1458
1451
|
@hub&.touch(result[:id])
|
|
1459
1452
|
json_response(200, { status: "deleted", session_id: result[:id], stopped: result[:stopped] })
|
|
1460
1453
|
rescue SessionManager::OwnedByTUI
|
|
1461
|
-
|
|
1454
|
+
owned_by_repl(id)
|
|
1462
1455
|
rescue SessionManager::DeleteRefused => e
|
|
1463
1456
|
error_response(409, e.reason.to_s, "#{e.message}; try again in a moment")
|
|
1464
|
-
rescue ArgumentError => e
|
|
1465
|
-
error_response(404, "not_found", e.message)
|
|
1466
1457
|
end
|
|
1467
1458
|
|
|
1468
1459
|
# ── Attached context (ContextSources): the session bar's chips ───────
|
|
@@ -1478,8 +1469,6 @@ module Samagotchi
|
|
|
1478
1469
|
subs = own.subscriptions
|
|
1479
1470
|
rows = attached_context(session).map { |attached| context_row(attached, own, subs) }
|
|
1480
1471
|
json_response(200, { session_id: id, sources: rows, can_add_url: ContextProviders.any? })
|
|
1481
|
-
rescue ArgumentError => e
|
|
1482
|
-
error_response(404, "not_found", e.message)
|
|
1483
1472
|
end
|
|
1484
1473
|
|
|
1485
1474
|
# GET /api/sessions/:id/context/:name: one source with its text.
|
|
@@ -1488,8 +1477,6 @@ module Samagotchi
|
|
|
1488
1477
|
attached = find_context(session, name) or return error_response(404, "not_found", "no source #{name[0, 40]}")
|
|
1489
1478
|
own = ContextSources.session_location(id, state_dir: default_state_dir)
|
|
1490
1479
|
json_response(200, context_row(attached, own, own.subscriptions).merge(text: attached.snapshot.text))
|
|
1491
|
-
rescue ArgumentError => e
|
|
1492
|
-
error_response(404, "not_found", e.message)
|
|
1493
1480
|
end
|
|
1494
1481
|
|
|
1495
1482
|
# POST /api/sessions/:id/context {url, why}: the source an installed
|
|
@@ -1512,8 +1499,6 @@ module Samagotchi
|
|
|
1512
1499
|
error_response(422, "no_provider", e.message)
|
|
1513
1500
|
rescue ContextSources::Invalid => e
|
|
1514
1501
|
error_response(409, "exists", e.message)
|
|
1515
|
-
rescue ArgumentError => e
|
|
1516
|
-
error_response(404, "not_found", e.message)
|
|
1517
1502
|
end
|
|
1518
1503
|
|
|
1519
1504
|
def web_source(resolved, why, scope)
|
|
@@ -1535,8 +1520,6 @@ module Samagotchi
|
|
|
1535
1520
|
ContextSources.session_location(id, state_dir: default_state_dir).mute(attached.name)
|
|
1536
1521
|
json_response(200, { status: "muted", name: attached.name })
|
|
1537
1522
|
end
|
|
1538
|
-
rescue ArgumentError => e
|
|
1539
|
-
error_response(404, "not_found", e.message)
|
|
1540
1523
|
end
|
|
1541
1524
|
|
|
1542
1525
|
def attached_context(session)
|