@maudecode/talaria-web 1.17.2 → 1.19.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.
- package/_release.json +8 -8
- package/dist/api/auth-raw.d.ts +1 -1
- package/dist/api/auth-raw.js +20 -20
- package/dist/api/auth-raw.js.map +1 -1
- package/dist/api/automation-raw.d.ts +7 -1
- package/dist/api/automation-raw.js +74 -27
- package/dist/api/automation-raw.js.map +1 -1
- package/dist/api/automation-router.d.ts +128 -23
- package/dist/api/automation-router.js +29 -13
- package/dist/api/automation-router.js.map +1 -1
- package/dist/api/chat-router.d.ts +1845 -40
- package/dist/api/chat-router.js +196 -114
- package/dist/api/chat-router.js.map +1 -1
- package/dist/api/git-router.d.ts +37 -3
- package/dist/api/git-router.js +47 -48
- package/dist/api/git-router.js.map +1 -1
- package/dist/api/raw-routes.d.ts +9 -1
- package/dist/api/raw-routes.js +286 -73
- package/dist/api/raw-routes.js.map +1 -1
- package/dist/api/router.d.ts +16 -6
- package/dist/api/router.js +28 -26
- package/dist/api/router.js.map +1 -1
- package/dist/api/session-visibility.d.ts +4 -4
- package/dist/api/session-visibility.js +26 -16
- package/dist/api/session-visibility.js.map +1 -1
- package/dist/api/sessions-router.d.ts +3312 -131
- package/dist/api/sessions-router.js +287 -157
- package/dist/api/sessions-router.js.map +1 -1
- package/dist/api/settings-router.d.ts +457 -8
- package/dist/api/settings-router.js +54 -33
- package/dist/api/settings-router.js.map +1 -1
- package/dist/api/sse-routes.d.ts +7 -2
- package/dist/api/sse-routes.js +109 -62
- package/dist/api/sse-routes.js.map +1 -1
- package/dist/api/tools-raw.d.ts +5 -0
- package/dist/api/tools-raw.js +63 -55
- package/dist/api/tools-raw.js.map +1 -1
- package/dist/api/tools-router.d.ts +998 -85
- package/dist/api/tools-router.js +447 -61
- package/dist/api/tools-router.js.map +1 -1
- package/dist/api/transcribe-stream.d.ts +16 -0
- package/dist/api/transcribe-stream.js +172 -0
- package/dist/api/transcribe-stream.js.map +1 -0
- package/dist/app.d.ts +4 -1
- package/dist/app.js +98 -59
- package/dist/app.js.map +1 -1
- package/dist/auth/gate.js +15 -11
- package/dist/auth/gate.js.map +1 -1
- package/dist/auth/oidc.d.ts +5 -2
- package/dist/auth/oidc.js +8 -7
- package/dist/auth/oidc.js.map +1 -1
- package/dist/auth/passkeys.d.ts +15 -14
- package/dist/auth/passkeys.js +89 -91
- package/dist/auth/passkeys.js.map +1 -1
- package/dist/auth/store.d.ts +17 -7
- package/dist/auth/store.js +93 -52
- package/dist/auth/store.js.map +1 -1
- package/dist/bin/talaria-web.js +12 -81
- package/dist/bin/talaria-web.js.map +1 -1
- package/dist/cli/ctl.js +2 -1
- package/dist/cli/ctl.js.map +1 -1
- package/dist/cli/dotenv.d.ts +5 -0
- package/dist/cli/dotenv.js +15 -2
- package/dist/cli/dotenv.js.map +1 -1
- package/dist/cli/launcher.js +5 -5
- package/dist/cli/launcher.js.map +1 -1
- package/dist/cli/main.d.ts +1 -0
- package/dist/cli/main.js +85 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/supervise.d.ts +2 -0
- package/dist/cli/supervise.js +67 -4
- package/dist/cli/supervise.js.map +1 -1
- package/dist/config/agent-config.d.ts +34 -11
- package/dist/config/agent-config.js +116 -60
- package/dist/config/agent-config.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.js +19 -6
- package/dist/config.js.map +1 -1
- package/dist/fs/atomic.d.ts +1 -0
- package/dist/fs/atomic.js +4 -1
- package/dist/fs/atomic.js.map +1 -1
- package/dist/fs/stat.d.ts +1 -0
- package/dist/fs/stat.js +11 -0
- package/dist/fs/stat.js.map +1 -0
- package/dist/http/context.d.ts +31 -12
- package/dist/http/context.js +60 -24
- package/dist/http/context.js.map +1 -1
- package/dist/http/csp.d.ts +2 -1
- package/dist/http/csp.js +3 -2
- package/dist/http/csp.js.map +1 -1
- package/dist/onboarding.d.ts +2 -2
- package/dist/onboarding.js +10 -8
- package/dist/onboarding.js.map +1 -1
- package/dist/profiles/profiles.d.ts +20 -4
- package/dist/profiles/profiles.js +115 -30
- package/dist/profiles/profiles.js.map +1 -1
- package/dist/projects.d.ts +15 -6
- package/dist/projects.js +55 -47
- package/dist/projects.js.map +1 -1
- package/dist/providers/catalog.d.ts +84 -14
- package/dist/providers/catalog.js +474 -151
- package/dist/providers/catalog.js.map +1 -1
- package/dist/providers/env-file.d.ts +10 -3
- package/dist/providers/env-file.js +24 -75
- package/dist/providers/env-file.js.map +1 -1
- package/dist/providers/runtime-env.d.ts +4 -2
- package/dist/providers/runtime-env.js +9 -6
- package/dist/providers/runtime-env.js.map +1 -1
- package/dist/providers/tables.d.ts +3 -1
- package/dist/providers/tables.js +4 -2
- package/dist/providers/tables.js.map +1 -1
- package/dist/raster-image.d.ts +7 -0
- package/dist/raster-image.js +267 -0
- package/dist/raster-image.js.map +1 -0
- package/dist/redact.js +342 -46
- package/dist/redact.js.map +1 -1
- package/dist/release.d.ts +7 -0
- package/dist/release.js +14 -0
- package/dist/release.js.map +1 -1
- package/dist/runtime.d.ts +5 -1
- package/dist/runtime.js +305 -96
- package/dist/runtime.js.map +1 -1
- package/dist/server.js +15 -0
- package/dist/server.js.map +1 -1
- package/dist/sessions/anchor.d.ts +9 -3
- package/dist/sessions/anchor.js +28 -36
- package/dist/sessions/anchor.js.map +1 -1
- package/dist/sessions/background-events.d.ts +2 -0
- package/dist/sessions/background-events.js +10 -0
- package/dist/sessions/background-events.js.map +1 -0
- package/dist/sessions/background-tasks.d.ts +26 -27
- package/dist/sessions/background-tasks.js +89 -97
- package/dist/sessions/background-tasks.js.map +1 -1
- package/dist/sessions/background-updates.d.ts +15 -8
- package/dist/sessions/background-updates.js +101 -25
- package/dist/sessions/background-updates.js.map +1 -1
- package/dist/sessions/cli-sessions.d.ts +27 -6
- package/dist/sessions/cli-sessions.js +152 -42
- package/dist/sessions/cli-sessions.js.map +1 -1
- package/dist/sessions/completions.d.ts +77 -9
- package/dist/sessions/completions.js +482 -45
- package/dist/sessions/completions.js.map +1 -1
- package/dist/sessions/compress.d.ts +8 -0
- package/dist/sessions/compress.js +2 -2
- package/dist/sessions/compress.js.map +1 -1
- package/dist/sessions/drafts.d.ts +14 -10
- package/dist/sessions/drafts.js +38 -43
- package/dist/sessions/drafts.js.map +1 -1
- package/dist/sessions/export.d.ts +5 -0
- package/dist/sessions/export.js +30 -0
- package/dist/sessions/export.js.map +1 -1
- package/dist/sessions/gateway-sessions.d.ts +46 -0
- package/dist/sessions/gateway-sessions.js +378 -0
- package/dist/sessions/gateway-sessions.js.map +1 -0
- package/dist/sessions/gateway-watcher.d.ts +7 -3
- package/dist/sessions/gateway-watcher.js +31 -19
- package/dist/sessions/gateway-watcher.js.map +1 -1
- package/dist/sessions/goals.d.ts +31 -0
- package/dist/sessions/goals.js +75 -0
- package/dist/sessions/goals.js.map +1 -0
- package/dist/sessions/handoff.d.ts +32 -0
- package/dist/sessions/handoff.js +114 -0
- package/dist/sessions/handoff.js.map +1 -0
- package/dist/sessions/journal.d.ts +5 -79
- package/dist/sessions/journal.js +15 -606
- package/dist/sessions/journal.js.map +1 -1
- package/dist/sessions/list.d.ts +31 -16
- package/dist/sessions/list.js +139 -129
- package/dist/sessions/list.js.map +1 -1
- package/dist/sessions/merge.d.ts +69 -13
- package/dist/sessions/merge.js +265 -64
- package/dist/sessions/merge.js.map +1 -1
- package/dist/sessions/notices.d.ts +22 -0
- package/dist/sessions/notices.js +72 -0
- package/dist/sessions/notices.js.map +1 -0
- package/dist/sessions/pending.d.ts +10 -4
- package/dist/sessions/pending.js +83 -7
- package/dist/sessions/pending.js.map +1 -1
- package/dist/sessions/prefix-index.d.ts +82 -0
- package/dist/sessions/prefix-index.js +232 -0
- package/dist/sessions/prefix-index.js.map +1 -0
- package/dist/sessions/recovery.d.ts +19 -0
- package/dist/sessions/recovery.js +322 -0
- package/dist/sessions/recovery.js.map +1 -0
- package/dist/sessions/relay.d.ts +38 -19
- package/dist/sessions/relay.js +302 -79
- package/dist/sessions/relay.js.map +1 -1
- package/dist/sessions/service.d.ts +260 -55
- package/dist/sessions/service.js +1448 -462
- package/dist/sessions/service.js.map +1 -1
- package/dist/sessions/session.d.ts +24 -2
- package/dist/sessions/session.js +69 -7
- package/dist/sessions/session.js.map +1 -1
- package/dist/sessions/shares.d.ts +7 -6
- package/dist/sessions/shares.js +22 -45
- package/dist/sessions/shares.js.map +1 -1
- package/dist/sessions/source-kind.js +1 -0
- package/dist/sessions/source-kind.js.map +1 -1
- package/dist/sessions/state-db-identity.d.ts +18 -0
- package/dist/sessions/state-db-identity.js +67 -0
- package/dist/sessions/state-db-identity.js.map +1 -0
- package/dist/sessions/state-db-worker.d.ts +1 -0
- package/dist/sessions/state-db-worker.js +35 -0
- package/dist/sessions/state-db-worker.js.map +1 -0
- package/dist/sessions/state-db.d.ts +113 -7
- package/dist/sessions/state-db.js +861 -44
- package/dist/sessions/state-db.js.map +1 -1
- package/dist/sessions/storage/backend.d.ts +162 -0
- package/dist/sessions/storage/backend.js +19 -0
- package/dist/sessions/storage/backend.js.map +1 -0
- package/dist/sessions/storage/conformance.d.ts +20 -0
- package/dist/sessions/storage/conformance.js +237 -0
- package/dist/sessions/storage/conformance.js.map +1 -0
- package/dist/sessions/storage/document.d.ts +16 -0
- package/dist/sessions/storage/document.js +44 -0
- package/dist/sessions/storage/document.js.map +1 -0
- package/dist/sessions/storage/json-documents.d.ts +23 -0
- package/dist/sessions/storage/json-documents.js +233 -0
- package/dist/sessions/storage/json-documents.js.map +1 -0
- package/dist/sessions/storage/json-files.d.ts +12 -0
- package/dist/sessions/storage/json-files.js +124 -0
- package/dist/sessions/storage/json-files.js.map +1 -0
- package/dist/sessions/storage/json-installation.d.ts +3 -0
- package/dist/sessions/storage/json-installation.js +31 -0
- package/dist/sessions/storage/json-installation.js.map +1 -0
- package/dist/sessions/storage/json-journal.d.ts +114 -0
- package/dist/sessions/storage/json-journal.js +686 -0
- package/dist/sessions/storage/json-journal.js.map +1 -0
- package/dist/sessions/storage/json-recovery.d.ts +4 -0
- package/dist/sessions/storage/json-recovery.js +195 -0
- package/dist/sessions/storage/json-recovery.js.map +1 -0
- package/dist/sessions/storage/json.d.ts +68 -0
- package/dist/sessions/storage/json.js +258 -0
- package/dist/sessions/storage/json.js.map +1 -0
- package/dist/sessions/storage/row.d.ts +6 -0
- package/dist/sessions/storage/row.js +49 -0
- package/dist/sessions/storage/row.js.map +1 -0
- package/dist/sessions/storage/search-text.d.ts +4 -0
- package/dist/sessions/storage/search-text.js +16 -0
- package/dist/sessions/storage/search-text.js.map +1 -0
- package/dist/sessions/store.d.ts +65 -68
- package/dist/sessions/store.js +305 -602
- package/dist/sessions/store.js.map +1 -1
- package/dist/sessions/streams.d.ts +4 -0
- package/dist/sessions/streams.js +9 -1
- package/dist/sessions/streams.js.map +1 -1
- package/dist/sessions/titles.d.ts +2 -0
- package/dist/sessions/titles.js +5 -1
- package/dist/sessions/titles.js.map +1 -1
- package/dist/sessions/todo.d.ts +5 -0
- package/dist/sessions/todo.js +28 -21
- package/dist/sessions/todo.js.map +1 -1
- package/dist/sessions/tool-display.d.ts +5 -4
- package/dist/sessions/tool-display.js +30 -50
- package/dist/sessions/tool-display.js.map +1 -1
- package/dist/sessions/turn-context.d.ts +11 -2
- package/dist/sessions/turn-context.js +18 -11
- package/dist/sessions/turn-context.js.map +1 -1
- package/dist/sessions/turn.d.ts +122 -17
- package/dist/sessions/turn.js +939 -358
- package/dist/sessions/turn.js.map +1 -1
- package/dist/sessions/wakeup-pause.d.ts +54 -0
- package/dist/sessions/wakeup-pause.js +163 -0
- package/dist/sessions/wakeup-pause.js.map +1 -0
- package/dist/sessions/window.js +13 -12
- package/dist/sessions/window.js.map +1 -1
- package/dist/settings.d.ts +19 -7
- package/dist/settings.js +178 -184
- package/dist/settings.js.map +1 -1
- package/dist/sidecar/client.d.ts +17 -3
- package/dist/sidecar/client.js +78 -8
- package/dist/sidecar/client.js.map +1 -1
- package/dist/sidecar/discover.d.ts +2 -1
- package/dist/sidecar/discover.js +93 -10
- package/dist/sidecar/discover.js.map +1 -1
- package/dist/sidecar/env.d.ts +4 -0
- package/dist/sidecar/env.js +3 -0
- package/dist/sidecar/env.js.map +1 -0
- package/dist/sidecar/fake.d.ts +2 -2
- package/dist/sidecar/fake.js +19 -2
- package/dist/sidecar/fake.js.map +1 -1
- package/dist/startup.d.ts +13 -5
- package/dist/startup.js +48 -0
- package/dist/startup.js.map +1 -1
- package/dist/test/env-file.d.ts +5 -0
- package/dist/test/env-file.js +88 -0
- package/dist/test/env-file.js.map +1 -0
- package/dist/test/harness.d.ts +1 -0
- package/dist/test/harness.js +1 -1
- package/dist/test/harness.js.map +1 -1
- package/dist/test/passkeys.d.ts +22 -0
- package/dist/test/passkeys.js +62 -0
- package/dist/test/passkeys.js.map +1 -0
- package/dist/test/storage.d.ts +15 -0
- package/dist/test/storage.js +26 -0
- package/dist/test/storage.js.map +1 -0
- package/dist/test/ts-worker-hooks.d.ts +1 -0
- package/dist/test/ts-worker-hooks.js +24 -0
- package/dist/test/ts-worker-hooks.js.map +1 -0
- package/dist/text/diff.d.ts +9 -14
- package/dist/text/diff.js +74 -108
- package/dist/text/diff.js.map +1 -1
- package/dist/tools/commands.d.ts +6 -1
- package/dist/tools/commands.js +8 -2
- package/dist/tools/commands.js.map +1 -1
- package/dist/tools/connectors.d.ts +33 -0
- package/dist/tools/connectors.js +121 -0
- package/dist/tools/connectors.js.map +1 -0
- package/dist/tools/crons.d.ts +38 -1
- package/dist/tools/crons.js +122 -18
- package/dist/tools/crons.js.map +1 -1
- package/dist/tools/dashboard-plugins.d.ts +53 -0
- package/dist/tools/dashboard-plugins.js +188 -0
- package/dist/tools/dashboard-plugins.js.map +1 -0
- package/dist/tools/extensions.d.ts +18 -5
- package/dist/tools/extensions.js +344 -266
- package/dist/tools/extensions.js.map +1 -1
- package/dist/tools/gateway-restart.d.ts +62 -0
- package/dist/tools/gateway-restart.js +142 -0
- package/dist/tools/gateway-restart.js.map +1 -0
- package/dist/tools/health.d.ts +44 -0
- package/dist/tools/health.js +201 -46
- package/dist/tools/health.js.map +1 -1
- package/dist/tools/hygiene.d.ts +4 -3
- package/dist/tools/hygiene.js +23 -9
- package/dist/tools/hygiene.js.map +1 -1
- package/dist/tools/kanban.d.ts +4 -2
- package/dist/tools/kanban.js +28 -19
- package/dist/tools/kanban.js.map +1 -1
- package/dist/tools/mcp.d.ts +13 -0
- package/dist/tools/mcp.js +88 -0
- package/dist/tools/mcp.js.map +1 -1
- package/dist/tools/memory-masks.d.ts +5 -0
- package/dist/tools/memory-masks.js +256 -0
- package/dist/tools/memory-masks.js.map +1 -0
- package/dist/tools/memory.d.ts +29 -3
- package/dist/tools/memory.js +69 -27
- package/dist/tools/memory.js.map +1 -1
- package/dist/tools/plugin-settings.d.ts +15 -0
- package/dist/tools/plugin-settings.js +90 -0
- package/dist/tools/plugin-settings.js.map +1 -0
- package/dist/tools/project-os.d.ts +11 -0
- package/dist/tools/project-os.js +267 -0
- package/dist/tools/project-os.js.map +1 -0
- package/dist/tools/skills.d.ts +14 -5
- package/dist/tools/skills.js +49 -16
- package/dist/tools/skills.js.map +1 -1
- package/dist/tools/update-notifications.d.ts +34 -15
- package/dist/tools/update-notifications.js +141 -57
- package/dist/tools/update-notifications.js.map +1 -1
- package/dist/tools/update-rollback.d.ts +11 -0
- package/dist/tools/update-rollback.js +28 -0
- package/dist/tools/update-rollback.js.map +1 -0
- package/dist/tools/updates.d.ts +110 -32
- package/dist/tools/updates.js +1112 -294
- package/dist/tools/updates.js.map +1 -1
- package/dist/tools/wiki.d.ts +68 -0
- package/dist/tools/wiki.js +413 -0
- package/dist/tools/wiki.js.map +1 -0
- package/dist/util.d.ts +2 -0
- package/dist/util.js +5 -0
- package/dist/util.js.map +1 -1
- package/dist/workspace/escape.d.ts +61 -0
- package/dist/workspace/escape.js +149 -0
- package/dist/workspace/escape.js.map +1 -0
- package/dist/workspace/extract.d.ts +22 -0
- package/dist/workspace/extract.js +308 -0
- package/dist/workspace/extract.js.map +1 -0
- package/dist/workspace/fs.d.ts +12 -0
- package/dist/workspace/fs.js +92 -23
- package/dist/workspace/fs.js.map +1 -1
- package/dist/workspace/git.d.ts +34 -1
- package/dist/workspace/git.js +328 -35
- package/dist/workspace/git.js.map +1 -1
- package/dist/workspace/media.d.ts +16 -13
- package/dist/workspace/media.js +43 -61
- package/dist/workspace/media.js.map +1 -1
- package/dist/workspace/rollback.d.ts +36 -22
- package/dist/workspace/rollback.js +178 -272
- package/dist/workspace/rollback.js.map +1 -1
- package/dist/workspace/unzip.js +4 -1
- package/dist/workspace/unzip.js.map +1 -1
- package/dist/workspace/upload.d.ts +7 -4
- package/dist/workspace/upload.js +5 -2
- package/dist/workspace/upload.js.map +1 -1
- package/dist/workspace/workspaces.d.ts +15 -19
- package/dist/workspace/workspaces.js +55 -127
- package/dist/workspace/workspaces.js.map +1 -1
- package/dist/workspace/worktrees.d.ts +2 -1
- package/dist/workspace/worktrees.js +179 -42
- package/dist/workspace/worktrees.js.map +1 -1
- package/package.json +7 -3
- package/sidecar/agent_dependency.json +4 -4
- package/sidecar/agent_support.json +26 -0
- package/sidecar/scripts/record-fixtures.py +12 -3
- package/sidecar/scripts/replay_sidecar.py +32 -0
- package/sidecar/scripts/suite_runner.py +302 -0
- package/sidecar/scripts/test.sh +12 -4
- package/sidecar/talaria_sidecar/__init__.py +6 -1
- package/sidecar/talaria_sidecar/__main__.py +4 -2
- package/sidecar/talaria_sidecar/agent_mutation.py +975 -0
- package/sidecar/talaria_sidecar/agent_services.py +1053 -0
- package/sidecar/talaria_sidecar/dependency_sync.py +85 -0
- package/sidecar/talaria_sidecar/home.py +56 -3
- package/sidecar/talaria_sidecar/methods/__init__.py +2 -2
- package/sidecar/talaria_sidecar/methods/aux.py +166 -24
- package/sidecar/talaria_sidecar/methods/chat.py +852 -135
- package/sidecar/talaria_sidecar/methods/checkpoints.py +89 -0
- package/sidecar/talaria_sidecar/methods/commands.py +294 -4
- package/sidecar/talaria_sidecar/methods/config.py +123 -4
- package/sidecar/talaria_sidecar/methods/connectors.py +343 -0
- package/sidecar/talaria_sidecar/methods/cron.py +395 -36
- package/sidecar/talaria_sidecar/methods/cron_execution.py +284 -0
- package/sidecar/talaria_sidecar/methods/gateway.py +212 -55
- package/sidecar/talaria_sidecar/methods/gateway_sessions.py +234 -0
- package/sidecar/talaria_sidecar/methods/goals.py +81 -41
- package/sidecar/talaria_sidecar/methods/kanban.py +108 -34
- package/sidecar/talaria_sidecar/methods/mcp.py +1 -1
- package/sidecar/talaria_sidecar/methods/memory.py +84 -0
- package/sidecar/talaria_sidecar/methods/oauth.py +5 -2
- package/sidecar/talaria_sidecar/methods/process.py +218 -15
- package/sidecar/talaria_sidecar/methods/profiles.py +121 -13
- package/sidecar/talaria_sidecar/methods/providers.py +217 -28
- package/sidecar/talaria_sidecar/methods/runtime.py +69 -1
- package/sidecar/talaria_sidecar/methods/skills.py +67 -4
- package/sidecar/talaria_sidecar/methods/state_db.py +316 -75
- package/sidecar/talaria_sidecar/methods/stt.py +159 -78
- package/sidecar/talaria_sidecar/methods/text.py +14 -1
- package/sidecar/talaria_sidecar/methods/toolsets.py +49 -0
- package/sidecar/talaria_sidecar/methods/usage.py +113 -14
- package/sidecar/talaria_sidecar/methods/worktree.py +26 -19
- package/sidecar/talaria_sidecar/methods/write_approval.py +181 -0
- package/sidecar/talaria_sidecar/rpc.py +45 -4
- package/sidecar/talaria_sidecar/runtime.py +98 -13
- package/sidecar/talaria_sidecar/speed.py +35 -0
- package/static/dist/FILES.txt +126 -126
- package/static/dist/assets/AppShell-cey33v9s.js +8 -0
- package/static/dist/assets/ChatPage-i8wnugr7.js +18 -0
- package/static/dist/assets/ConnectTargets-hg19553x.js +1 -0
- package/static/dist/assets/ExtensionRoute-cly4apm5.js +1 -0
- package/static/dist/assets/Field-j8uqoigz.js +9 -0
- package/static/dist/assets/HubRoute-e4bgz41j.js +2 -0
- package/static/dist/assets/Markdown-lahuichm.js +598 -0
- package/static/dist/assets/RightPanel-lbo4i0fn.js +1 -0
- package/static/dist/assets/Select-gqatm2e0.js +1 -0
- package/static/dist/assets/SessionListPanel-mfqayfjh.js +1 -0
- package/static/dist/assets/SettingsLayout-b8980u7k.js +1 -0
- package/static/dist/assets/States-e7dkgtxf.js +1 -0
- package/static/dist/assets/Toaster-bfqqy786.js +1 -0
- package/static/dist/assets/_app-n1n6pfx1.js +1 -0
- package/static/dist/assets/{_app.ext._extensionId-opyh9y0v.js → _app.ext._extensionId-buxjxav3.js} +1 -1
- package/static/dist/assets/_app.index-jt8zwiu2.js +1 -0
- package/static/dist/assets/_app.insights-cv76ml71.js +1 -0
- package/static/dist/assets/_app.kanban-h6ts53j7.js +1 -0
- package/static/dist/assets/_app.logs-dm873z90.js +1 -0
- package/static/dist/assets/_app.memory-glk5y6m8.js +1 -0
- package/static/dist/assets/_app.profiles-ka6zpstq.js +1 -0
- package/static/dist/assets/{_app.session._sessionId-bfd4h3id.js → _app.session._sessionId-g2vzghvc.js} +1 -1
- package/static/dist/assets/_app.settings-hgjdhzcd.js +1 -0
- package/static/dist/assets/_app.settings._section-jf2kmhae.js +3 -0
- package/static/dist/assets/_app.skills-cii852yq.js +1 -0
- package/static/dist/assets/_app.tasks-fp9q7atm.js +1 -0
- package/static/dist/assets/_app.todos-dw94qzue.js +1 -0
- package/static/dist/assets/_app.workspaces-inyygzze.js +1 -0
- package/static/dist/assets/{abnfDiagram-VCTEODGH-b2x1lqot.js → abnfDiagram-VCTEODGH-gjayq3ra.js} +1 -1
- package/static/dist/assets/architecture-7GRP2DOG-lp56972r.js +1 -0
- package/static/dist/assets/{architectureDiagram-5GKGNRK7-h38o5l7b.js → architectureDiagram-5GKGNRK7-nwb3xovn.js} +1 -1
- package/static/dist/assets/archive-b1j004vn.js +1 -0
- package/static/dist/assets/back-g74gkozu.js +1 -0
- package/static/dist/assets/{blockDiagram-I7D4REHJ-bqquxv2u.js → blockDiagram-I7D4REHJ-joedghi3.js} +1 -1
- package/static/dist/assets/{c4Diagram-7LVT6UL2-f3fb8w6z.js → c4Diagram-7LVT6UL2-hyrjrohe.js} +1 -1
- package/static/dist/assets/channel-es95o4y8.js +1 -0
- package/static/dist/assets/{chunk-4HAMMTFA-bzfyyzp3.js → chunk-4HAMMTFA-koouqbut.js} +1 -1
- package/static/dist/assets/{chunk-75Z2AOVW-ly99rfx5.js → chunk-75Z2AOVW-bl4bhdnt.js} +1 -1
- package/static/dist/assets/{chunk-DU6HZSFF-bg6ecfqf.js → chunk-DU6HZSFF-gpedttsp.js} +3 -3
- package/static/dist/assets/{chunk-F27PBJKO-e8k7mmqw.js → chunk-F27PBJKO-d8xxi0zi.js} +1 -1
- package/static/dist/assets/{chunk-GMAD6QVW-cmalnoaj.js → chunk-GMAD6QVW-jro0hl73.js} +1 -1
- package/static/dist/assets/{chunk-GVQU2GXP-gmif5o01.js → chunk-GVQU2GXP-djn6gbyu.js} +1 -1
- package/static/dist/assets/{chunk-IMKFNOWR-f1nwwh6u.js → chunk-IMKFNOWR-vw4p5p7o.js} +1 -1
- package/static/dist/assets/{chunk-L3NEJ4N5-oktfh5v7.js → chunk-L3NEJ4N5-n1hj77q8.js} +1 -1
- package/static/dist/assets/{chunk-OSK3NFVY-b1sdx96z.js → chunk-OSK3NFVY-oy0gwn2n.js} +1 -1
- package/static/dist/assets/{chunk-P2QGCYS3-fhu4nzxt.js → chunk-P2QGCYS3-onf1hz6c.js} +1 -1
- package/static/dist/assets/{chunk-POPQ4Y6H-em31rnef.js → chunk-POPQ4Y6H-m8sqn7cs.js} +1 -1
- package/static/dist/assets/{chunk-PWAF6VOD-bxhhyftw.js → chunk-PWAF6VOD-ek4smc8x.js} +1 -1
- package/static/dist/assets/{chunk-SHT3W25Y-fmrvwcav.js → chunk-SHT3W25Y-g9gfvefo.js} +3 -3
- package/static/dist/assets/{chunk-SVP7TREG-lymukl2j.js → chunk-SVP7TREG-f9nfv2cu.js} +1 -1
- package/static/dist/assets/{chunk-TICWLB2K-lmza6k23.js → chunk-TICWLB2K-bnk3m1mq.js} +1 -1
- package/static/dist/assets/classDiagram-ZZMXUADV-kmihoqxh.js +1 -0
- package/static/dist/assets/classDiagram-v2-VYDZK3BY-kmihoqxh.js +1 -0
- package/static/dist/assets/copied-bli9tbcx.js +1 -0
- package/static/dist/assets/{copy-fw5b1pc6.js → copy-jamtd1pf.js} +1 -1
- package/static/dist/assets/{cynefin-OW5HDTMX-czlhbyhs.js → cynefin-OW5HDTMX-f8xfdd3f.js} +1 -1
- package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-gq7mvswb.js → cynefinDiagram-5FMLGOSQ-n9lb99h9.js} +1 -1
- package/static/dist/assets/{dagre-GXQ25YYZ-cqdbf7dr.js → dagre-GXQ25YYZ-dgailddi.js} +4 -4
- package/static/dist/assets/{diagram-S7CK7UJ4-o6arxa58.js → diagram-S7CK7UJ4-m6aeoe7c.js} +1 -1
- package/static/dist/assets/{diagram-UQ7AKVKN-nhnd68tz.js → diagram-UQ7AKVKN-jall3j0x.js} +1 -1
- package/static/dist/assets/{diagram-VSXAHHWV-dk2sclzn.js → diagram-VSXAHHWV-hbawi7hn.js} +1 -1
- package/static/dist/assets/{diagram-VX7I27RA-n3k5nyx3.js → diagram-VX7I27RA-hendl2tt.js} +1 -1
- package/static/dist/assets/{diagram-Z3DM3KII-m6q9cwdh.js → diagram-Z3DM3KII-igwc35p3.js} +1 -1
- package/static/dist/assets/{ebnfDiagram-PWID7BFC-lsa5641p.js → ebnfDiagram-PWID7BFC-c64knz08.js} +1 -1
- package/static/dist/assets/{erDiagram-RLTQ6QDP-fo2ckbe6.js → erDiagram-RLTQ6QDP-brdu9lcm.js} +1 -1
- package/static/dist/assets/eventmodeling-NTZA5JFV-njk1j8wl.js +1 -0
- package/static/dist/assets/flowDiagram-HODETNUW-nma6f0ga.js +1 -0
- package/static/dist/assets/folder-plus-lvt9kudb.js +1 -0
- package/static/dist/assets/{ganttDiagram-EL5Y4UJY-nzqz6x9g.js → ganttDiagram-EL5Y4UJY-ma6jxj5i.js} +1 -1
- package/static/dist/assets/{gitGraph-4MIJSDKK-gukvcxe6.js → gitGraph-4MIJSDKK-naa9w6xa.js} +1 -1
- package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-lqhdp8f0.js → gitGraphDiagram-WWUBYQGX-hzltvsod.js} +1 -1
- package/static/dist/assets/{highlighted-body-KPVGNVTW-igw6cp39.js → highlighted-body-KPVGNVTW-ghoavj8d.js} +1 -1
- package/static/dist/assets/host-hzm8n85n.js +1 -0
- package/static/dist/assets/index-drdcaqeq.css +2 -0
- package/static/dist/assets/index-jzm844he.js +11 -0
- package/static/dist/assets/{info-A6RAGUB7-gvu7a93l.js → info-A6RAGUB7-mn2ut5ko.js} +1 -1
- package/static/dist/assets/{infoDiagram-27XIBGKW-m8t8g6w1.js → infoDiagram-27XIBGKW-h6kuqvsb.js} +1 -1
- package/static/dist/assets/{ishikawaDiagram-5VMMS53U-l07vkbgj.js → ishikawaDiagram-5VMMS53U-e7bnwq7h.js} +1 -1
- package/static/dist/assets/{journeyDiagram-3NMN7TZE-hybkgj8l.js → journeyDiagram-3NMN7TZE-etd1bi6p.js} +1 -1
- package/static/dist/assets/{kanban-definition-UXKFOSKX-by13uvtn.js → kanban-definition-UXKFOSKX-emb1g9dz.js} +4 -4
- package/static/dist/assets/lazyRouteComponent-o816ytzl.js +1 -0
- package/static/dist/assets/{line-osxq6fq7.js → line-h0ojzsvs.js} +1 -1
- package/static/dist/assets/link-k01ryaf9.js +1 -0
- package/static/dist/assets/login-bsae49ew.js +1 -0
- package/static/dist/assets/mermaid-HWGCJPDP-ihl34s74.js +1 -0
- package/static/dist/assets/{mermaid-parser.core-i91oxmou.js → mermaid-parser.core-bnre27u8.js} +3 -3
- package/static/dist/assets/{mindmap-definition-YA3MSWOX-m82mx8ww.js → mindmap-definition-YA3MSWOX-o1vathns.js} +1 -1
- package/static/dist/assets/onboarding-c65tysya.js +1 -0
- package/static/dist/assets/onboarding_model_label-gpn28uix.js +1 -0
- package/static/dist/assets/{packet-AYTQ26CC-nbtgge2o.js → packet-AYTQ26CC-hatq7m2e.js} +1 -1
- package/static/dist/assets/pcmTap.worklet-74EKjWAZ.js +1 -0
- package/static/dist/assets/{pegDiagram-XKGWAZYB-f1zxzga1.js → pegDiagram-XKGWAZYB-gwisz8jv.js} +1 -1
- package/static/dist/assets/{pie-WAS4IAKB-of8vuhxs.js → pie-WAS4IAKB-fpftf0mk.js} +1 -1
- package/static/dist/assets/{pieDiagram-E7YTZNPT-o7y8ftio.js → pieDiagram-E7YTZNPT-khm1o9a6.js} +1 -1
- package/static/dist/assets/{quadrantDiagram-AXDQQJYC-iqiwia1t.js → quadrantDiagram-AXDQQJYC-owqc4uqn.js} +1 -1
- package/static/dist/assets/{radar-RG4KPBEZ-delnsy2v.js → radar-RG4KPBEZ-cntfwjpq.js} +1 -1
- package/static/dist/assets/{railroad-74A4TZTK-nspnyq9u.js → railroad-74A4TZTK-a7t5eibp.js} +1 -1
- package/static/dist/assets/railroad-abnf-HS5TGJTU-ol8cdktf.js +1 -0
- package/static/dist/assets/railroad-ebnf-LZEXJU2U-i8vzd429.js +1 -0
- package/static/dist/assets/railroad-peg-WCYAUIDC-nw3ulewz.js +1 -0
- package/static/dist/assets/{railroadDiagram-O6MQD6OU-z41tqx5z.js → railroadDiagram-O6MQD6OU-etuh0qa0.js} +1 -1
- package/static/dist/assets/{refresh-cw-nt1dtsx4.js → refresh-cw-mqbkv2a3.js} +1 -1
- package/static/dist/assets/refresh-l29ljp76.js +1 -0
- package/static/dist/assets/{requirementDiagram-BXWQKSXE-bdvif194.js → requirementDiagram-BXWQKSXE-oq2ulk22.js} +1 -1
- package/static/dist/assets/{sankeyDiagram-P5KCCOFB-h73e0rol.js → sankeyDiagram-P5KCCOFB-iqc02emv.js} +1 -1
- package/static/dist/assets/saved-ndd4t2xb.js +1 -0
- package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-iv95mb1z.js → sequenceDiagram-WJ2MYXX4-i0v116q0.js} +2 -2
- package/static/dist/assets/settings_section_extensions_title-jhfo6o8z.js +1 -0
- package/static/dist/assets/share._token-c1ri0hy1.js +2 -0
- package/static/dist/assets/sliders-horizontal-j3jjt9tv.js +1 -0
- package/static/dist/assets/{stateDiagram-D77RDMKH-k417ixmf.js → stateDiagram-D77RDMKH-dnpt1ato.js} +1 -1
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-bxjacpk6.js +1 -0
- package/static/dist/assets/{swimlanes-42K2YHIH-esola70w.js → swimlanes-42K2YHIH-loqbdazb.js} +1 -1
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-id62p9hz.js +8 -0
- package/static/dist/assets/{timeline-definition-24CTP7MA-getrz2y8.js → timeline-definition-24CTP7MA-ol2hnvma.js} +1 -1
- package/static/dist/assets/toast-i3h34v6o.js +64 -0
- package/static/dist/assets/{treeView-Q6P3EWNA-hc7ap1q7.js → treeView-Q6P3EWNA-idrj6iox.js} +1 -1
- package/static/dist/assets/{treemap-WGGIJYW6-b7k1syy5.js → treemap-WGGIJYW6-lzr49vv5.js} +1 -1
- package/static/dist/assets/untitled-oaoya4qj.js +1 -0
- package/static/dist/assets/useForm-nfaxgmsk.js +1 -0
- package/static/dist/assets/{useParams-lhg566ew.js → useParams-gjc70qru.js} +1 -1
- package/static/dist/assets/{useSearch-lbrh6ejn.js → useSearch-e6ae539u.js} +1 -1
- package/static/dist/assets/{vennDiagram-4TSXK5OY-n6pcy5qb.js → vennDiagram-4TSXK5OY-bvwr9o2z.js} +1 -1
- package/static/dist/assets/{wardley-WFR3VGLG-lpa18vz1.js → wardley-WFR3VGLG-31ui8tma.js} +1 -1
- package/static/dist/assets/{wardleyDiagram-VM6X3IG4-kpclwwg7.js → wardleyDiagram-VM6X3IG4-k5wqtx4b.js} +1 -1
- package/static/dist/assets/{xychartDiagram-S5SC5T6Z-paa2n8y0.js → xychartDiagram-S5SC5T6Z-aqbgvujg.js} +1 -1
- package/static/dist/index.html +1 -1
- package/static/dist/sw.js +1 -1
- package/static/dist/assets/AppShell-fb2td9zi.js +0 -1
- package/static/dist/assets/ChatPage-cj96kiy9.js +0 -25
- package/static/dist/assets/Dialog-kxcg7ard.js +0 -1
- package/static/dist/assets/ExtensionRoute-jf7h0754.js +0 -1
- package/static/dist/assets/HubRoute-gmanpd5y.js +0 -2
- package/static/dist/assets/Markdown-mdwlvj3w.js +0 -598
- package/static/dist/assets/RightPanel-ftvrjlcp.js +0 -1
- package/static/dist/assets/Select-bilaypej.js +0 -1
- package/static/dist/assets/SessionListPanel-og8n36qs.js +0 -1
- package/static/dist/assets/SettingsLayout-jonjefmj.js +0 -1
- package/static/dist/assets/States-nmejrwrm.js +0 -1
- package/static/dist/assets/Toaster-nnl8zkt9.js +0 -1
- package/static/dist/assets/_app-gdidzd94.js +0 -1
- package/static/dist/assets/_app.index-hbarzbjx.js +0 -1
- package/static/dist/assets/_app.insights-luywlarz.js +0 -1
- package/static/dist/assets/_app.kanban-kincnbr8.js +0 -1
- package/static/dist/assets/_app.logs-oi2gr3gs.js +0 -1
- package/static/dist/assets/_app.memory-b6jfivt6.js +0 -1
- package/static/dist/assets/_app.profiles-lpvixmqp.js +0 -1
- package/static/dist/assets/_app.settings-vowx3iq0.js +0 -1
- package/static/dist/assets/_app.settings._section-cnntg1tu.js +0 -3
- package/static/dist/assets/_app.skills-fb57lpau.js +0 -1
- package/static/dist/assets/_app.tasks-eszpx1q7.js +0 -2
- package/static/dist/assets/_app.todos-nr05ac6w.js +0 -1
- package/static/dist/assets/_app.workspaces-d2luomu6.js +0 -1
- package/static/dist/assets/architecture-7GRP2DOG-hd0o7bde.js +0 -1
- package/static/dist/assets/back-bemaer09.js +0 -1
- package/static/dist/assets/cancel-drd0g6d9.js +0 -1
- package/static/dist/assets/channel-cpdgf867.js +0 -1
- package/static/dist/assets/classDiagram-ZZMXUADV-ko09sguk.js +0 -1
- package/static/dist/assets/classDiagram-v2-VYDZK3BY-ko09sguk.js +0 -1
- package/static/dist/assets/copied-ima3pysb.js +0 -1
- package/static/dist/assets/endpoints-d33siokx.js +0 -64
- package/static/dist/assets/eventmodeling-NTZA5JFV-emctlo64.js +0 -1
- package/static/dist/assets/flowDiagram-HODETNUW-bfi3moqa.js +0 -1
- package/static/dist/assets/index-cxq58xb1.js +0 -11
- package/static/dist/assets/index-hgtx08n5.css +0 -2
- package/static/dist/assets/lazyRouteComponent-af0lvgjx.js +0 -1
- package/static/dist/assets/link-jyxicdhg.js +0 -1
- package/static/dist/assets/login-ec21t39x.js +0 -1
- package/static/dist/assets/mermaid-HWGCJPDP-gfl8z3ep.js +0 -1
- package/static/dist/assets/onboarding-btnltvyz.js +0 -1
- package/static/dist/assets/onboarding_model_label-dp7xffoh.js +0 -1
- package/static/dist/assets/railroad-abnf-HS5TGJTU-nst1qxyn.js +0 -1
- package/static/dist/assets/railroad-ebnf-LZEXJU2U-ci37x8qy.js +0 -1
- package/static/dist/assets/railroad-peg-WCYAUIDC-aimkk07z.js +0 -1
- package/static/dist/assets/refresh-b4uams1i.js +0 -1
- package/static/dist/assets/saved-kg52csy7.js +0 -1
- package/static/dist/assets/settings_section_extensions_title-ej0iapxe.js +0 -1
- package/static/dist/assets/share._token-f97e94r0.js +0 -2
- package/static/dist/assets/sliders-horizontal-fj18it47.js +0 -1
- package/static/dist/assets/sse-fwpqtzbg.js +0 -1
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-hwn4apct.js +0 -1
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-d1h97oyg.js +0 -8
- package/static/dist/assets/todoStore-eginf9h4.js +0 -1
- package/static/dist/assets/tts-lxdxnmjd.js +0 -1
- package/static/dist/assets/untitled-ovpfthg4.js +0 -1
- package/static/dist/assets/useForm-bcblv1rx.js +0 -1
- package/static/dist/assets/useLocale-fi5jb8ma.js +0 -1
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"""Exact manual-fire recovery. The Agent owns admission, delivery and bookkeeping formats.
|
|
2
|
+
|
|
3
|
+
Its admission journal lacks the Web caller's cancellation intent and tail ownership.
|
|
4
|
+
This additive record retains those two obligations across a lost submitter or sidecar.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import contextlib
|
|
10
|
+
import importlib
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import time
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from ..dependency_sync import _lock
|
|
17
|
+
from ..errors import RpcError
|
|
18
|
+
from ..home import scoped_home
|
|
19
|
+
|
|
20
|
+
SETTLEMENT_SECONDS = 30.0
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Unverified(RpcError):
|
|
24
|
+
def __init__(self, detail):
|
|
25
|
+
self.detail = str(detail)
|
|
26
|
+
super().__init__(f"Task outcome is unverified. {detail}",
|
|
27
|
+
condition="cron_execution_unverified")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def path(home, job_id):
|
|
31
|
+
return Path(home) / "cron" / "manual_runs" / (job_id + ".json")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _read_record(home, job_id):
|
|
35
|
+
try:
|
|
36
|
+
record = json.loads(path(home, job_id).read_text())
|
|
37
|
+
except FileNotFoundError:
|
|
38
|
+
return None
|
|
39
|
+
except (OSError, ValueError) as exc:
|
|
40
|
+
raise Unverified("The retained task record could not be read.") from exc
|
|
41
|
+
job = record.get("job") if isinstance(record, dict) else None
|
|
42
|
+
if not isinstance(job, dict) or job.get("id") != job_id:
|
|
43
|
+
raise Unverified("Recovery identity does not match this task.")
|
|
44
|
+
return record
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def pending_job(home, job_id):
|
|
48
|
+
"""The frozen job keeps an externally deleted execution reachable through Web's list/get preflight."""
|
|
49
|
+
record = _read_record(home, job_id)
|
|
50
|
+
if record is None:
|
|
51
|
+
return None
|
|
52
|
+
job = record["job"]
|
|
53
|
+
return dict(job, recovery_only=True, state="paused", enabled=False, last_run_at=None, last_status="error",
|
|
54
|
+
last_error="Task outcome is unverified. Use Run now to check it.")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def pending_jobs(home):
|
|
58
|
+
from .cron import _job_id
|
|
59
|
+
result = []
|
|
60
|
+
for file in sorted((Path(home) / "cron" / "manual_runs").glob("*.json")):
|
|
61
|
+
job = pending_job(home, _job_id({"job_id": file.stem}))
|
|
62
|
+
if job is not None:
|
|
63
|
+
result.append(job)
|
|
64
|
+
return result
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def supported(job, *, recovery=False):
|
|
68
|
+
if job.get("no_agent"):
|
|
69
|
+
return False
|
|
70
|
+
try:
|
|
71
|
+
authority = importlib.import_module("cron.scheduler_authority")
|
|
72
|
+
if recovery:
|
|
73
|
+
surfaces = ((authority, ("journal_path", "_lift_pause")),
|
|
74
|
+
(importlib.import_module("cron.scheduler_bookkeeping"), ("fail_empty_response", "finish_completed_run")),
|
|
75
|
+
(importlib.import_module("cron.executions"), ("give_up_execution", "recover_receipted_execution")),
|
|
76
|
+
(importlib.import_module("hermes_cli.gateway_client"), ("connect_gateway",)),
|
|
77
|
+
(importlib.import_module("hermes_cli.gateway_runtime"), ("discover_gateway_endpoint",)))
|
|
78
|
+
return all(callable(getattr(module, name, None)) for module, names in surfaces for name in names)
|
|
79
|
+
except ImportError as exc:
|
|
80
|
+
if recovery or (isinstance(exc, ModuleNotFoundError) and exc.name in {"cron.scheduler_authority", "cron"}):
|
|
81
|
+
return False
|
|
82
|
+
raise
|
|
83
|
+
return True
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def recover(job, execution_id, cancel_event):
|
|
87
|
+
"""Observe only. A lost submission reply never authorizes another submit."""
|
|
88
|
+
from hermes_cli.gateway_client import connect_gateway
|
|
89
|
+
|
|
90
|
+
async def observe():
|
|
91
|
+
endpoint = None
|
|
92
|
+
if not os.environ.get("HERMES_TUI_GATEWAY_URL", "").strip():
|
|
93
|
+
from hermes_cli.gateway_runtime import discover_gateway_endpoint
|
|
94
|
+
from hermes_constants import get_hermes_home
|
|
95
|
+
discovery = await asyncio.to_thread(discover_gateway_endpoint, get_hermes_home())
|
|
96
|
+
if discovery.state != "ready" or discovery.endpoint is None:
|
|
97
|
+
raise Unverified("The owning gateway is unavailable.")
|
|
98
|
+
endpoint = discovery.endpoint
|
|
99
|
+
async with connect_gateway(endpoint=endpoint) as client:
|
|
100
|
+
state = await client.rpc("cron.recover", job_id=job["id"], request_id=execution_id, extra_prompt=None)
|
|
101
|
+
if state["status"] == "missing":
|
|
102
|
+
return False, "", "", "Cron admission missing: the owner never admitted this fire"
|
|
103
|
+
if state["status"] == "unknown":
|
|
104
|
+
raise Unverified("The owner cannot prove this admission's outcome.")
|
|
105
|
+
receipt = {key: state[key] for key in ("session_id", "admission_id")}
|
|
106
|
+
while True:
|
|
107
|
+
if state["status"] == "terminal":
|
|
108
|
+
job.update(state.get("job_flags") or {})
|
|
109
|
+
return tuple(state["result"])
|
|
110
|
+
if state["status"] not in {"queued", "started"}:
|
|
111
|
+
raise Unverified(f"Owner status: {state['status']}")
|
|
112
|
+
if cancel_event.is_set():
|
|
113
|
+
await client.rpc("cron.cancel", **receipt)
|
|
114
|
+
await asyncio.sleep(.2)
|
|
115
|
+
state = await client.rpc("cron.status", **receipt)
|
|
116
|
+
return asyncio.run(observe())
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class ManualExecution:
|
|
120
|
+
def __init__(self, home, execution_home, job, owner):
|
|
121
|
+
self.home = Path(home)
|
|
122
|
+
self.file = path(home, job["id"])
|
|
123
|
+
self.execution_home = Path(execution_home or home)
|
|
124
|
+
self.job, self.owner = dict(job), owner
|
|
125
|
+
self.record = None
|
|
126
|
+
self.recovering = False
|
|
127
|
+
self.settled = False
|
|
128
|
+
|
|
129
|
+
def save(self):
|
|
130
|
+
from utils import atomic_json_write
|
|
131
|
+
atomic_json_write(self.file, self.record, mode=0o600, fsync_dir=True)
|
|
132
|
+
|
|
133
|
+
@contextlib.contextmanager
|
|
134
|
+
def acquire(self):
|
|
135
|
+
self.file.parent.mkdir(parents=True, exist_ok=True)
|
|
136
|
+
with self.file.with_suffix(".lock").open("a+b") as lease:
|
|
137
|
+
try:
|
|
138
|
+
_lock(lease)
|
|
139
|
+
except OSError as exc:
|
|
140
|
+
raise RpcError("This manual task is still being reconciled", condition="cron_execution_unverified") from exc
|
|
141
|
+
yield
|
|
142
|
+
|
|
143
|
+
def prepare(self, *, recovery_only=False):
|
|
144
|
+
from cron import executions, jobs
|
|
145
|
+
self.record = _read_record(self.home, self.job["id"])
|
|
146
|
+
if self.record is not None:
|
|
147
|
+
self.job, self.owner = self.record["job"], self.record["owner"]
|
|
148
|
+
self.execution_home = Path(self.record["execution_home"])
|
|
149
|
+
self.recovering = True
|
|
150
|
+
else:
|
|
151
|
+
if recovery_only:
|
|
152
|
+
raise Unverified("The retained task record is no longer available. Refresh its status.")
|
|
153
|
+
with scoped_home(self.execution_home):
|
|
154
|
+
row = executions.create_execution(self.job["id"], source="web-manual")
|
|
155
|
+
executions.mark_execution_running(row["id"])
|
|
156
|
+
self.record = {"job": self.job, "owner": self.owner, "execution_home": str(self.execution_home),
|
|
157
|
+
"execution_id": row["id"], "cancel_reason": None, "tail_started": False}
|
|
158
|
+
self.execution_id = row["id"]
|
|
159
|
+
self.save()
|
|
160
|
+
self.execution_id = self.record["execution_id"]
|
|
161
|
+
if self.recovering:
|
|
162
|
+
self.cancel("submitter interrupted")
|
|
163
|
+
# Pause before dispatch: a dead sidecar must not let claim expiry re-fire an accepted task.
|
|
164
|
+
# Only this exact pause is lifted. Existing operator pauses are never changed.
|
|
165
|
+
with jobs.use_cron_store(self.home):
|
|
166
|
+
from .cron import _owner_fence
|
|
167
|
+
with _owner_fence(jobs, self.job["id"], self.owner, protect_unclaimed=True) as owns:
|
|
168
|
+
current = jobs.get_job(self.job["id"])
|
|
169
|
+
if not owns and not self.recovering:
|
|
170
|
+
self.cancel("claim_lost")
|
|
171
|
+
self.record["result"] = [False, "", "", "Fire claim lost before submission"]
|
|
172
|
+
self.save()
|
|
173
|
+
if owns and current and jobs.is_job_runnable(current) and not self.recovering:
|
|
174
|
+
current = jobs.pause_job(self.job["id"], reason=f"Manual execution pending: {self.execution_id}")
|
|
175
|
+
# The job write may have committed before the submitter died saving its pause receipt.
|
|
176
|
+
if (current and current.get("state") == "paused" and not self.record.get("pause")
|
|
177
|
+
and current.get("paused_reason") == f"Manual execution pending: {self.execution_id}"):
|
|
178
|
+
self.record["pause"] = {"paused_at": current["paused_at"], "reason": current["paused_reason"]}
|
|
179
|
+
self.save()
|
|
180
|
+
|
|
181
|
+
def cancel(self, reason):
|
|
182
|
+
if self.record["cancel_reason"] is None:
|
|
183
|
+
self.record["cancel_reason"] = reason
|
|
184
|
+
self.save()
|
|
185
|
+
|
|
186
|
+
def finish(self, result):
|
|
187
|
+
from cron import executions, jobs, scheduler
|
|
188
|
+
from cron.scheduler_authority import journal_path
|
|
189
|
+
from .cron import _delivery_home, _owner_fence, _ClaimLost
|
|
190
|
+
|
|
191
|
+
success, output, answer, error = result
|
|
192
|
+
with scoped_home(self.execution_home), jobs.use_cron_store(self.home), _owner_fence(jobs, self.job["id"], self.owner, protect_unclaimed=True) as owns:
|
|
193
|
+
current = jobs.get_job(self.job["id"]) or {}
|
|
194
|
+
row = executions.get_execution(self.execution_id)
|
|
195
|
+
marked = self.execution_id in current.get("canonical_completions", [])
|
|
196
|
+
booked = marked or (row and row["status"] in {"completed", "failed"})
|
|
197
|
+
# Our own mark releases the claim before pause cleanup. A successor's claim never qualifies.
|
|
198
|
+
may_finish = bool(current) and (owns or (marked and not current.get("fire_claim")))
|
|
199
|
+
if not booked and self.record["tail_started"]:
|
|
200
|
+
raise Unverified("Bookkeeping was interrupted; its side effects will not be repeated.")
|
|
201
|
+
# Stop the Agent's orphan journal reader from replaying OUR tail after a crash.
|
|
202
|
+
# Our record now owns recovery, including the at-most-once tail barrier.
|
|
203
|
+
self.record["result"] = list(result)
|
|
204
|
+
self.save()
|
|
205
|
+
journal_path(self.job["id"], self.execution_id).unlink(missing_ok=True)
|
|
206
|
+
if not may_finish:
|
|
207
|
+
self._finish_ledger(False, "Fire claim ownership lost before terminal completion.")
|
|
208
|
+
elif booked:
|
|
209
|
+
self._finish_ledger(success, error)
|
|
210
|
+
else:
|
|
211
|
+
self.record["tail_started"] = True
|
|
212
|
+
self.save()
|
|
213
|
+
self.job["execution_id"] = self.execution_id
|
|
214
|
+
with _delivery_home(scheduler, self.home):
|
|
215
|
+
from cron.scheduler_bookkeeping import fail_empty_response, finish_completed_run
|
|
216
|
+
success, error, declared = scheduler._apply_agent_failure_marker(self.job, success, error, answer)
|
|
217
|
+
delivery = scheduler._RunDelivery(self.job, success, error, agent_declared=declared)
|
|
218
|
+
scheduler._save_compose_deliver(delivery, scheduler._FireOwnership(self.job), answer, output,
|
|
219
|
+
adapters=None, loop=None, verbose=False, execution_token=None)
|
|
220
|
+
fail_empty_response(delivery, answer)
|
|
221
|
+
if not finish_completed_run(delivery, self.owner, self.execution_id, recovered=self.recovering):
|
|
222
|
+
raise Unverified("A prior submitter still owns this execution's bookkeeping.")
|
|
223
|
+
success, error = delivery.success, delivery.error
|
|
224
|
+
pause = self.record.get("pause")
|
|
225
|
+
if may_finish and pause:
|
|
226
|
+
from cron.scheduler_authority import _lift_pause
|
|
227
|
+
_lift_pause(self.job["id"], pause)
|
|
228
|
+
self.file.unlink()
|
|
229
|
+
self.settled = True
|
|
230
|
+
reason = self.record["cancel_reason"]
|
|
231
|
+
if not may_finish or reason == "claim_lost":
|
|
232
|
+
return {"job_id": self.job["id"], "status": "failed", "success": False, "error": str(_ClaimLost())}
|
|
233
|
+
if reason == "cancelled" and not success:
|
|
234
|
+
raise RpcError("cancelled", condition="cancelled")
|
|
235
|
+
if reason and not success:
|
|
236
|
+
return {"job_id": self.job["id"], "status": "failed", "success": False, "error": reason}
|
|
237
|
+
return {"job_id": self.job["id"], "status": "completed", "success": bool(success), "error": error}
|
|
238
|
+
|
|
239
|
+
def _finish_ledger(self, success, error):
|
|
240
|
+
from cron import executions
|
|
241
|
+
finish = executions.recover_receipted_execution if self.recovering else executions.finish_execution
|
|
242
|
+
kwargs = {"job_id": self.job["id"]} if self.recovering else {}
|
|
243
|
+
finish(self.execution_id, success=success, error=error, **kwargs)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def run(job, home, execution_home, ctx, owner, *, recovery_only=False):
|
|
247
|
+
from . import cron
|
|
248
|
+
# Refuse before prepare/cancel can rewrite retained evidence or cleanup can enter a removed owner API.
|
|
249
|
+
if (recovery_only or path(home, job["id"]).exists()) and not supported({}, recovery=True):
|
|
250
|
+
raise Unverified("This Agent has an unsupported recovery protocol. Reconcile the retained manual task "
|
|
251
|
+
"with a capable Agent before retrying. No task was resubmitted or settled.")
|
|
252
|
+
execution = ManualExecution(home, execution_home, job, owner)
|
|
253
|
+
with execution.acquire():
|
|
254
|
+
with cron._RUNNING_LOCK:
|
|
255
|
+
cron._RUNNING.setdefault(job["id"], time.time())
|
|
256
|
+
try:
|
|
257
|
+
execution.prepare(recovery_only=recovery_only)
|
|
258
|
+
if recovery_only:
|
|
259
|
+
ctx.emit("started", {"job_id": job["id"]})
|
|
260
|
+
heartbeat = cron._heartbeat(home, execution.job["id"], execution.owner) if execution.owner else None
|
|
261
|
+
if execution.record.get("result") is not None:
|
|
262
|
+
result = execution.record["result"]
|
|
263
|
+
else:
|
|
264
|
+
result = cron._run_in_child(execution.job, execution.execution_home, ctx, heartbeat, execution)
|
|
265
|
+
return execution.finish(result)
|
|
266
|
+
except Exception as exc:
|
|
267
|
+
if execution.settled:
|
|
268
|
+
raise
|
|
269
|
+
if execution.record is not None and hasattr(execution, "execution_id"):
|
|
270
|
+
execution.cancel(execution.record["cancel_reason"] or "submitter interrupted")
|
|
271
|
+
# The Web record, not the Agent's generic orphan replay, now owns cancellation.
|
|
272
|
+
# Give up only our own audit row; this never releases or stamps a fire claim.
|
|
273
|
+
with scoped_home(execution.execution_home):
|
|
274
|
+
from cron.scheduler_authority import journal_path
|
|
275
|
+
from cron.executions import give_up_execution
|
|
276
|
+
journal_path(job["id"], execution.execution_id).unlink(missing_ok=True)
|
|
277
|
+
give_up_execution(execution.execution_id, reason=str(exc))
|
|
278
|
+
if isinstance(exc, Unverified):
|
|
279
|
+
raise
|
|
280
|
+
raise Unverified(str(exc)) from exc
|
|
281
|
+
finally:
|
|
282
|
+
# The durable record guards an unverified owner; this map guards only an active call.
|
|
283
|
+
with cron._RUNNING_LOCK:
|
|
284
|
+
cron._RUNNING.pop(job["id"], None)
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
"""``gateway.*``: Hermes gateway lifecycle through the ``hermes`` CLI
|
|
1
|
+
"""``gateway.*``: Hermes gateway lifecycle (start, stop, restart) through the ``hermes`` CLI."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
6
|
import os
|
|
7
|
+
import re
|
|
7
8
|
import shutil
|
|
8
9
|
import subprocess
|
|
9
10
|
import sys
|
|
@@ -12,11 +13,22 @@ import time
|
|
|
12
13
|
from pathlib import Path
|
|
13
14
|
|
|
14
15
|
from ..errors import InvalidParams
|
|
15
|
-
from ..home import profile_home_param
|
|
16
|
+
from ..home import _is_named_profile, profile_home_param
|
|
16
17
|
from ..rpc import CallContext
|
|
18
|
+
from .profiles import _bounded, gateway_mode
|
|
17
19
|
|
|
18
20
|
log = logging.getLogger("talaria_sidecar.gateway")
|
|
19
|
-
|
|
21
|
+
# One lifecycle action at a time across every action: overlapping `hermes gateway` subprocesses race each other.
|
|
22
|
+
_ACTION_LOCK = threading.Lock()
|
|
23
|
+
ACTIONS = ("start", "stop", "restart")
|
|
24
|
+
_DONE = {"start": "started", "stop": "stopped", "restart": "restarted"}
|
|
25
|
+
# EX_CONFIG: Agent v0.21.6 refuses a named profile's own start/restart with it, since the host gateway serves every profile.
|
|
26
|
+
_REFUSED = 78
|
|
27
|
+
_SERVED_WAIT_SECONDS = 15.0
|
|
28
|
+
# A restart waits out the CLI's own exit budget plus this headroom; 240 s on an Agent without the budget helper.
|
|
29
|
+
_RESTART_HEADROOM_SECONDS = 30.0
|
|
30
|
+
_RESTART_FALLBACK_SECONDS = 240.0
|
|
31
|
+
_ANSI = re.compile(r"\x1b\[[0-9;]*[A-Za-z]")
|
|
20
32
|
|
|
21
33
|
|
|
22
34
|
def _hermes_command() -> str:
|
|
@@ -39,68 +51,213 @@ def _drain(stream, sink: list[str]) -> None:
|
|
|
39
51
|
pass
|
|
40
52
|
|
|
41
53
|
|
|
42
|
-
def
|
|
43
|
-
|
|
44
|
-
|
|
54
|
+
def _reason(stdout: str, stderr: str) -> str | None:
|
|
55
|
+
"""The Agent's first error line: its first ``✗`` line, else the first line it wrote; one bounded printable line without colour codes."""
|
|
56
|
+
def lines(text: str) -> list[str]:
|
|
57
|
+
return [line.strip() for line in _ANSI.sub("", text).splitlines() if line.strip()]
|
|
58
|
+
|
|
59
|
+
first = next((line for line in lines(f"{stdout}\n{stderr}") if line.startswith("✗")), None) or next(iter(lines(stderr or stdout)), "")
|
|
60
|
+
return _bounded(first.lstrip("✗ "), 300) or None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _host_root(home: Path) -> Path | None:
|
|
64
|
+
"""The Hermes root of a ``<root>/profiles/<name>`` home, as the Agent's ``profile_name_for_home`` reads a named profile."""
|
|
65
|
+
return home.parent.parent if home.parent.name == "profiles" and not home.name.startswith(".") and home.name != "default" else None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _host_serves(home: Path, budget_deadline: float) -> bool:
|
|
69
|
+
"""True once the host gateway serves the named profile at ``home`` (TAL-805 ``served``); the host may still be coming up."""
|
|
70
|
+
deadline = min(time.monotonic() + _SERVED_WAIT_SECONDS, budget_deadline)
|
|
71
|
+
while True:
|
|
72
|
+
try:
|
|
73
|
+
if gateway_mode(home, home.name, False) == "served":
|
|
74
|
+
return True
|
|
75
|
+
except Exception: # noqa: BLE001
|
|
76
|
+
log.debug("gateway mode probe failed for %s", home, exc_info=True)
|
|
77
|
+
return False
|
|
78
|
+
if time.monotonic() >= deadline:
|
|
79
|
+
return False
|
|
80
|
+
time.sleep(0.5)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def control(action: str, home: Path, cli_profile: str | None, ctx: CallContext, *, quick_timeout: float, background_wait: float) -> dict:
|
|
84
|
+
if action not in ACTIONS:
|
|
85
|
+
raise InvalidParams("unsupported gateway action")
|
|
86
|
+
if not _ACTION_LOCK.acquire(blocking=False):
|
|
87
|
+
return {"status": "busy", "message": "Another gateway action is already in progress. Please wait a moment and try again."}
|
|
45
88
|
try:
|
|
89
|
+
# One budget for the whole action, host fallback included: each step gets only what is left of it.
|
|
90
|
+
deadline = time.monotonic() + quick_timeout + background_wait
|
|
91
|
+
|
|
92
|
+
def run(action: str, home: Path, cli_profile: str | None) -> dict:
|
|
93
|
+
left = deadline - time.monotonic()
|
|
94
|
+
if left <= 0:
|
|
95
|
+
return {"status": "failed", "message": f"Gateway {action} ran out of time before it could start", "timed_out": True}
|
|
96
|
+
quick = min(quick_timeout, left)
|
|
97
|
+
return _run(action, home, cli_profile, ctx, quick_timeout=quick, background_wait=left - quick)
|
|
98
|
+
|
|
99
|
+
result = run(action, home, cli_profile)
|
|
100
|
+
root = _host_root(home)
|
|
101
|
+
# TAL-884: Agent v0.21.6 gives a named profile no gateway of its own. Start the host gateway from the root profile
|
|
102
|
+
# (`--profile default`, so a sticky active profile cannot retarget it), then rerun the profile's action, which
|
|
103
|
+
# serves or unparks it there. Never `--force`: that grows the per-profile fleet member the Agent refuses.
|
|
104
|
+
if action == "stop" or root is None or result.get("returncode") != _REFUSED:
|
|
105
|
+
return result
|
|
106
|
+
log.info("Gateway %s refused for %s; starting the host gateway from %s", action, home, root)
|
|
107
|
+
host = run("start", root, "default")
|
|
108
|
+
if host["status"] != "completed":
|
|
109
|
+
return host
|
|
110
|
+
result = run(action, home, cli_profile)
|
|
111
|
+
if result.get("returncode") == _REFUSED and _host_serves(home, deadline):
|
|
112
|
+
return {"status": "completed", "message": f"Gateway service {_DONE[action]} successfully", "detail": result.get("detail", "")}
|
|
113
|
+
return result
|
|
114
|
+
finally:
|
|
115
|
+
_ACTION_LOCK.release()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _profile_selector(home: Path) -> str | None:
|
|
119
|
+
"""The profile the Agent names ``home`` (``default`` for the root; None for no profile home): a selector-less verb would
|
|
120
|
+
follow the sticky ``active_profile``. Raises ImportError on an Agent without ``profile_name_for_home``."""
|
|
121
|
+
from hermes_constants import profile_name_for_home
|
|
122
|
+
|
|
123
|
+
return profile_name_for_home(home)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _child_env(home: Path, profile: str | None, action: str) -> dict:
|
|
127
|
+
"""The CLI's environment. The sidecar's ``os.environ`` carries its launch profile's ``.env`` once chat ran, so only the
|
|
128
|
+
launch home's own root-profile action keeps it; any other profile starts scrubbed of it and loads its own ``.env``."""
|
|
129
|
+
if profile is None or (profile == "default" and not _is_named_profile(home)):
|
|
46
130
|
env = dict(os.environ)
|
|
47
131
|
env["HERMES_HOME"] = str(home)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
132
|
+
else:
|
|
133
|
+
from hermes_cli.web_server_gateway import _profile_action_environment
|
|
134
|
+
|
|
135
|
+
env = _profile_action_environment(["--profile", profile, "gateway", action])
|
|
136
|
+
env.pop("HERMES_EXEC_ASK", None) # the sidecar's own approval posture, not the gateway's
|
|
137
|
+
return env
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
# The CLI's own restart budget, read as the CLI reads it: in the child's environment once its home's .env has loaded.
|
|
141
|
+
_BUDGET_SCRIPT = (
|
|
142
|
+
"import os, sys; from pathlib import Path; sys.path.insert(0, sys.argv[1]); "
|
|
143
|
+
"from hermes_cli.env_loader import load_hermes_dotenv; "
|
|
144
|
+
"load_hermes_dotenv(hermes_home=os.environ['HERMES_HOME'], project_env=Path(sys.argv[1]) / '.env'); "
|
|
145
|
+
"from hermes_cli.gateway import _get_restart_exit_wait_budget; print(_get_restart_exit_wait_budget())"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _agent_restart_budget(env: dict) -> float:
|
|
150
|
+
import hermes_cli
|
|
151
|
+
|
|
152
|
+
agent_dir = str(Path(hermes_cli.__file__).resolve().parent.parent)
|
|
153
|
+
out = subprocess.run([sys.executable, "-c", _BUDGET_SCRIPT, agent_dir], env=env, capture_output=True, text=True, timeout=60, check=True).stdout
|
|
154
|
+
return float(out.split()[-1])
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _restart_wait(home: Path, cli_profile: str | None = None) -> float:
|
|
158
|
+
"""The Agent CLI's own restart wait for ``home`` plus headroom, so Web never kills a restart the CLI still waits on."""
|
|
159
|
+
try:
|
|
160
|
+
profile = cli_profile or _profile_selector(home)
|
|
161
|
+
return _agent_restart_budget(_child_env(home, profile, "restart")) + _RESTART_HEADROOM_SECONDS
|
|
162
|
+
except Exception: # noqa: BLE001 - an Agent without the helper, or an unreadable config, gets the historical wait
|
|
163
|
+
log.warning("Could not read the Agent's restart budget for %s", home, exc_info=True)
|
|
164
|
+
return _RESTART_FALLBACK_SECONDS
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _run(action: str, home: Path, cli_profile: str | None, ctx: CallContext, *, quick_timeout: float, background_wait: float) -> dict:
|
|
168
|
+
"""One ``hermes --profile <name> gateway <action>`` subprocess for ``home``; the caller holds ``_ACTION_LOCK``."""
|
|
169
|
+
try:
|
|
170
|
+
profile = cli_profile or _profile_selector(home)
|
|
171
|
+
except ImportError:
|
|
172
|
+
# Selector-less, the CLI would follow the sticky profile, and a named profile's would inherit the launch profile's credentials.
|
|
173
|
+
return {"status": "failed", "message": f"Gateway {action} refused: the installed Hermes Agent cannot name the profile at {home}"}
|
|
174
|
+
try:
|
|
175
|
+
env = _child_env(home, profile, action)
|
|
176
|
+
except Exception as exc: # noqa: BLE001 - never start a named profile's gateway with another profile's credentials
|
|
177
|
+
log.warning("Could not build the %s profile's gateway environment", profile, exc_info=True)
|
|
178
|
+
return {"status": "failed", "message": f"Could not isolate the {profile} profile's environment: {getattr(exc, 'detail', None) or exc}"}
|
|
179
|
+
cmd = [_hermes_command()]
|
|
180
|
+
if profile:
|
|
181
|
+
cmd.extend(["--profile", profile])
|
|
182
|
+
cmd.extend(["gateway", action])
|
|
183
|
+
log.info("Running gateway %s via %s (HERMES_HOME=%s)", action, " ".join(cmd), home)
|
|
184
|
+
try:
|
|
54
185
|
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, env=env)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
186
|
+
except FileNotFoundError:
|
|
187
|
+
return {"status": "failed", "message": "Hermes CLI not found"}
|
|
188
|
+
# Drain both pipes from the start: a chatty restart must never block on a full pipe and be mistaken for a hang.
|
|
189
|
+
out: list[str] = []
|
|
190
|
+
err: list[str] = []
|
|
191
|
+
drainers = [threading.Thread(target=_drain, args=(proc.stdout, out), daemon=True), threading.Thread(target=_drain, args=(proc.stderr, err), daemon=True)]
|
|
192
|
+
for t in drainers:
|
|
193
|
+
t.start()
|
|
194
|
+
|
|
195
|
+
def collected() -> tuple[str, str]:
|
|
59
196
|
for t in drainers:
|
|
60
|
-
t.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
time.sleep(0.1)
|
|
77
|
-
if proc.poll() is not None:
|
|
78
|
-
return outcome()
|
|
79
|
-
ctx.emit("progress", {"phase": "draining"})
|
|
80
|
-
deadline = time.monotonic() + background_wait
|
|
81
|
-
while proc.poll() is None and time.monotonic() < deadline:
|
|
82
|
-
if ctx.cancelled:
|
|
83
|
-
break
|
|
84
|
-
time.sleep(0.5)
|
|
85
|
-
if proc.poll() is None:
|
|
86
|
-
proc.terminate()
|
|
87
|
-
try:
|
|
88
|
-
proc.wait(timeout=5)
|
|
89
|
-
except subprocess.TimeoutExpired:
|
|
90
|
-
proc.kill()
|
|
91
|
-
proc.wait(timeout=5)
|
|
92
|
-
collected()
|
|
93
|
-
return {"status": "failed", "message": f"Gateway restart process timed out after {background_wait:.0f}s and was terminated"}
|
|
197
|
+
t.join(timeout=5)
|
|
198
|
+
return "".join(out).strip(), "".join(err).strip()
|
|
199
|
+
|
|
200
|
+
def outcome() -> dict:
|
|
201
|
+
stdout, stderr = collected()
|
|
202
|
+
if proc.returncode == 0:
|
|
203
|
+
return {"status": "completed", "message": f"Gateway service {_DONE[action]} successfully", "detail": stdout or stderr}
|
|
204
|
+
failed = {"status": "failed", "message": f"{action.capitalize()} failed: {stderr or stdout}", "detail": stdout or stderr, "returncode": proc.returncode}
|
|
205
|
+
reason = _reason(stdout, stderr)
|
|
206
|
+
return {**failed, "reason": reason} if reason else failed
|
|
207
|
+
|
|
208
|
+
ctx.emit("progress", {"phase": "started"})
|
|
209
|
+
deadline = time.monotonic() + quick_timeout
|
|
210
|
+
while proc.poll() is None and time.monotonic() < deadline:
|
|
211
|
+
time.sleep(0.1)
|
|
212
|
+
if proc.poll() is not None:
|
|
94
213
|
return outcome()
|
|
95
|
-
|
|
96
|
-
|
|
214
|
+
ctx.emit("progress", {"phase": "draining"})
|
|
215
|
+
deadline = time.monotonic() + background_wait
|
|
216
|
+
while proc.poll() is None and time.monotonic() < deadline:
|
|
217
|
+
if ctx.cancelled:
|
|
218
|
+
break
|
|
219
|
+
time.sleep(0.5)
|
|
220
|
+
if proc.poll() is None:
|
|
221
|
+
proc.terminate()
|
|
222
|
+
try:
|
|
223
|
+
proc.wait(timeout=5)
|
|
224
|
+
except subprocess.TimeoutExpired:
|
|
225
|
+
proc.kill()
|
|
226
|
+
proc.wait(timeout=5)
|
|
227
|
+
collected()
|
|
228
|
+
if ctx.cancelled:
|
|
229
|
+
return {"status": "failed", "message": f"Gateway {action} was cancelled and its process terminated", "timed_out": True}
|
|
230
|
+
return {"status": "failed", "message": f"Gateway {action} process timed out after {background_wait:.0f}s and was terminated", "timed_out": True}
|
|
231
|
+
return outcome()
|
|
97
232
|
|
|
98
233
|
|
|
99
234
|
def register(registry) -> None:
|
|
100
|
-
@registry.method("gateway.
|
|
101
|
-
def
|
|
235
|
+
@registry.method("gateway.session")
|
|
236
|
+
def session_(ctx: CallContext, params: dict) -> dict:
|
|
237
|
+
from .gateway_sessions import session
|
|
238
|
+
|
|
239
|
+
return session(ctx, params)
|
|
240
|
+
|
|
241
|
+
def run(ctx: CallContext, params: dict, action: str) -> dict:
|
|
102
242
|
home = profile_home_param(params)
|
|
103
243
|
cli_profile = params.get("cli_profile")
|
|
104
244
|
if cli_profile is not None and not isinstance(cli_profile, str):
|
|
105
245
|
raise InvalidParams("cli_profile must be a string")
|
|
106
|
-
|
|
246
|
+
# A restart drains the old gateway first, for as long as the CLI's own budget allows (infinite: no deadline).
|
|
247
|
+
wait = params.get("background_wait_seconds") or (_restart_wait(home, cli_profile or None) if action == "restart" else 60.0)
|
|
248
|
+
return control(action, home, cli_profile or None, ctx, quick_timeout=float(params.get("quick_timeout_seconds") or 2.0), background_wait=float(wait))
|
|
249
|
+
|
|
250
|
+
@registry.method("gateway.control", requires_agent=False)
|
|
251
|
+
def control_(ctx: CallContext, params: dict) -> dict:
|
|
252
|
+
return run(ctx, params, str(params.get("action") or ""))
|
|
253
|
+
|
|
254
|
+
@registry.method("gateway.restart", requires_agent=False)
|
|
255
|
+
def restart_(ctx: CallContext, params: dict) -> dict:
|
|
256
|
+
return run(ctx, params, "restart")
|
|
257
|
+
|
|
258
|
+
@registry.method("gateway.mode")
|
|
259
|
+
def mode_(ctx: CallContext, params: dict) -> dict:
|
|
260
|
+
name = params.get("name")
|
|
261
|
+
if not isinstance(name, str) or not name.strip():
|
|
262
|
+
raise InvalidParams("name is required")
|
|
263
|
+
return {"gateway_mode": gateway_mode(profile_home_param(params), name, params.get("is_default") is True)}
|