aimux-cli 0.1.16 → 0.1.19
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/README.md +184 -67
- package/bin/aimux-dev +10 -0
- package/dist/agent-events.js +0 -1
- package/dist/agent-output-parser.js +0 -1
- package/dist/agent-prompt-delivery.js +0 -1
- package/dist/agent-tracker.js +0 -1
- package/dist/agent-watcher.js +0 -1
- package/dist/alert-display.d.ts +21 -0
- package/dist/alert-display.js +85 -0
- package/dist/atomic-write.js +0 -1
- package/dist/attachment-store.d.ts +0 -7
- package/dist/attachment-store.js +2 -87
- package/dist/builtin-metadata-watchers.js +4 -5
- package/dist/claude-hooks.d.ts +1 -0
- package/dist/claude-hooks.js +25 -1
- package/dist/config.d.ts +19 -13
- package/dist/config.js +28 -15
- package/dist/connection-targets.d.ts +8 -0
- package/dist/connection-targets.js +27 -0
- package/dist/context/compactor.js +0 -1
- package/dist/context/context-bridge.js +0 -1
- package/dist/context/context-file.js +0 -1
- package/dist/context/history.js +0 -1
- package/dist/credentials.d.ts +12 -0
- package/dist/credentials.js +48 -0
- package/dist/daemon.d.ts +23 -0
- package/dist/daemon.js +391 -67
- package/dist/dashboard/command-spec.js +0 -1
- package/dist/dashboard/feedback.js +0 -1
- package/dist/dashboard/index.d.ts +13 -10
- package/dist/dashboard/index.js +3 -27
- package/dist/dashboard/operation-failures.js +0 -1
- package/dist/dashboard/order.d.ts +22 -0
- package/dist/dashboard/order.js +54 -0
- package/dist/dashboard/pending-actions.d.ts +39 -10
- package/dist/dashboard/pending-actions.js +166 -37
- package/dist/dashboard/quick-jump.d.ts +2 -1
- package/dist/dashboard/quick-jump.js +7 -5
- package/dist/dashboard/runtime-evidence.js +0 -1
- package/dist/dashboard/session-actions.d.ts +4 -4
- package/dist/dashboard/session-actions.js +1 -2
- package/dist/dashboard/session-registry.d.ts +4 -3
- package/dist/dashboard/session-registry.js +16 -51
- package/dist/dashboard/sort.js +0 -1
- package/dist/dashboard/state.d.ts +1 -1
- package/dist/dashboard/state.js +0 -1
- package/dist/dashboard/targets.js +0 -1
- package/dist/dashboard/ui-state-store.d.ts +16 -1
- package/dist/dashboard/ui-state-store.js +73 -3
- package/dist/debug-state.d.ts +97 -0
- package/dist/debug-state.js +540 -0
- package/dist/debug.d.ts +38 -0
- package/dist/debug.js +219 -16
- package/dist/default-plugins/gh-pr-context.d.ts +2 -1
- package/dist/default-plugins/gh-pr-context.js +17 -12
- package/dist/default-plugins/transcript-length.js +15 -3
- package/dist/fast-control.js +37 -20
- package/dist/hotkeys.js +0 -1
- package/dist/http-client.js +31 -3
- package/dist/key-parser.js +0 -1
- package/dist/last-used.js +0 -1
- package/dist/local-ui-server.d.ts +22 -0
- package/dist/local-ui-server.js +185 -0
- package/dist/login-flow.d.ts +7 -0
- package/dist/login-flow.js +119 -0
- package/dist/main.js +821 -152
- package/dist/managed-launch-env.js +14 -1
- package/dist/metadata-server.d.ts +36 -36
- package/dist/metadata-server.js +638 -138
- package/dist/metadata-store.d.ts +4 -1
- package/dist/metadata-store.js +30 -3
- package/dist/multiplexer/agent-io-methods.d.ts +2 -10
- package/dist/multiplexer/agent-io-methods.js +12 -44
- package/dist/multiplexer/archives.js +8 -10
- package/dist/multiplexer/dashboard-actions-methods.js +0 -1
- package/dist/multiplexer/dashboard-control.js +45 -14
- package/dist/multiplexer/dashboard-interaction.d.ts +8 -2
- package/dist/multiplexer/dashboard-interaction.js +187 -29
- package/dist/multiplexer/dashboard-model.d.ts +10 -3
- package/dist/multiplexer/dashboard-model.js +417 -36
- package/dist/multiplexer/dashboard-ops.d.ts +9 -7
- package/dist/multiplexer/dashboard-ops.js +178 -69
- package/dist/multiplexer/dashboard-state-methods.d.ts +2 -1
- package/dist/multiplexer/dashboard-state-methods.js +3 -3
- package/dist/multiplexer/dashboard-tail-methods.d.ts +22 -10
- package/dist/multiplexer/dashboard-tail-methods.js +164 -48
- package/dist/multiplexer/dashboard-view-methods.d.ts +1 -1
- package/dist/multiplexer/dashboard-view-methods.js +23 -9
- package/dist/multiplexer/graveyard-view-model.d.ts +9 -1
- package/dist/multiplexer/graveyard-view-model.js +39 -1
- package/dist/multiplexer/index.d.ts +15 -12
- package/dist/multiplexer/index.js +64 -44
- package/dist/multiplexer/navigation.js +0 -1
- package/dist/multiplexer/notifications.js +107 -25
- package/dist/multiplexer/persistence-methods.d.ts +31 -4
- package/dist/multiplexer/persistence-methods.js +304 -309
- package/dist/multiplexer/runtime-lifecycle-methods.d.ts +8 -10
- package/dist/multiplexer/runtime-lifecycle-methods.js +104 -87
- package/dist/multiplexer/runtime-state.d.ts +8 -10
- package/dist/multiplexer/runtime-state.js +82 -146
- package/dist/multiplexer/runtime-sync.d.ts +2 -10
- package/dist/multiplexer/runtime-sync.js +3 -19
- package/dist/multiplexer/service-state-snapshot.d.ts +2 -4
- package/dist/multiplexer/service-state-snapshot.js +4 -52
- package/dist/multiplexer/services.d.ts +1 -0
- package/dist/multiplexer/services.js +55 -6
- package/dist/multiplexer/session-capture.d.ts +1 -0
- package/dist/multiplexer/session-capture.js +23 -0
- package/dist/multiplexer/session-launch.d.ts +4 -1
- package/dist/multiplexer/session-launch.js +152 -64
- package/dist/multiplexer/session-runtime-core.d.ts +8 -20
- package/dist/multiplexer/session-runtime-core.js +40 -136
- package/dist/multiplexer/subscreens.js +10 -4
- package/dist/multiplexer/tool-picker.js +0 -1
- package/dist/multiplexer/worktree-graveyard.d.ts +0 -1
- package/dist/multiplexer/worktree-graveyard.js +15 -17
- package/dist/multiplexer/worktrees.js +96 -41
- package/dist/notification-context.js +8 -5
- package/dist/notifications.js +163 -102
- package/dist/notify.d.ts +4 -0
- package/dist/notify.js +14 -1
- package/dist/orchestration-actions.js +0 -1
- package/dist/orchestration-routing.js +0 -1
- package/dist/orchestration.js +0 -1
- package/dist/osc-notifications.js +0 -1
- package/dist/paths.d.ts +32 -7
- package/dist/paths.js +82 -59
- package/dist/pending-actions.d.ts +5 -0
- package/dist/pending-actions.js +13 -0
- package/dist/plugin-runtime.js +9 -3
- package/dist/project-events.d.ts +1 -10
- package/dist/project-events.js +0 -11
- package/dist/project-scanner.d.ts +2 -3
- package/dist/project-scanner.js +58 -130
- package/dist/project-service-manifest.d.ts +1 -3
- package/dist/project-service-manifest.js +1 -4
- package/dist/recency.js +0 -1
- package/dist/recorder.js +0 -1
- package/dist/relay-client.d.ts +30 -0
- package/dist/relay-client.js +190 -0
- package/dist/remote-access.d.ts +16 -0
- package/dist/remote-access.js +90 -0
- package/dist/runtime-core/exchange-derived.d.ts +2 -0
- package/dist/runtime-core/exchange-derived.js +153 -0
- package/dist/runtime-core/exchange-import.d.ts +24 -0
- package/dist/runtime-core/exchange-import.js +317 -0
- package/dist/runtime-core/exchange-store.d.ts +157 -0
- package/dist/runtime-core/exchange-store.js +452 -0
- package/dist/runtime-core/topology-services.d.ts +38 -0
- package/dist/runtime-core/topology-services.js +170 -0
- package/dist/runtime-core/topology-sessions.d.ts +52 -0
- package/dist/runtime-core/topology-sessions.js +238 -0
- package/dist/runtime-core/topology-store.d.ts +171 -0
- package/dist/runtime-core/topology-store.js +419 -0
- package/dist/runtime-core/topology-worktrees.d.ts +60 -0
- package/dist/runtime-core/topology-worktrees.js +199 -0
- package/dist/runtime-migration.d.ts +69 -0
- package/dist/runtime-migration.js +398 -0
- package/dist/session-bootstrap.d.ts +8 -6
- package/dist/session-bootstrap.js +51 -159
- package/dist/session-runtime.d.ts +2 -0
- package/dist/session-runtime.js +1 -1
- package/dist/session-semantics.d.ts +12 -4
- package/dist/session-semantics.js +14 -1
- package/dist/shell-args.js +0 -1
- package/dist/shell-hooks.js +32 -11
- package/dist/shell-state.d.ts +2 -0
- package/dist/shell-state.js +26 -2
- package/dist/status-detector.js +0 -1
- package/dist/statusline-model.d.ts +10 -2
- package/dist/statusline-model.js +106 -31
- package/dist/task-workflow.d.ts +6 -9
- package/dist/task-workflow.js +37 -85
- package/dist/tasks.d.ts +6 -33
- package/dist/tasks.js +46 -89
- package/dist/team.d.ts +29 -0
- package/dist/team.js +40 -1
- package/dist/terminal-host.js +0 -1
- package/dist/threads.d.ts +6 -35
- package/dist/threads.js +89 -99
- package/dist/tmux/doctor.js +0 -1
- package/dist/tmux/inbox-popup.js +37 -16
- package/dist/tmux/runtime-manager.d.ts +3 -0
- package/dist/tmux/runtime-manager.js +21 -5
- package/dist/tmux/session-transport.js +0 -1
- package/dist/tmux/statusline-cache.js +0 -1
- package/dist/tmux/statusline.js +49 -10
- package/dist/tmux/switcher.js +0 -1
- package/dist/tmux/window-open.js +1 -3
- package/dist/tool-output-watchers.d.ts +0 -18
- package/dist/tool-output-watchers.js +0 -323
- package/dist/tui/render/box.js +0 -1
- package/dist/tui/render/text.js +0 -1
- package/dist/tui/screens/dashboard-renderers.js +37 -26
- package/dist/tui/screens/overlay-renderers.d.ts +2 -0
- package/dist/tui/screens/overlay-renderers.js +37 -2
- package/dist/tui/screens/subscreen-renderers.js +7 -1
- package/dist/workflow.js +0 -1
- package/dist/worktree.js +17 -1
- package/dist-ui/_expo/static/css/web-30453ede1678c16acb08b97e83e8646d.css +1 -0
- package/dist-ui/_expo/static/js/web/entry-477c745b2adc79367a4380ecf07d9ff6.js +14620 -0
- package/dist-ui/assets/assets/images/icon.a5413dcd2e811c9f2317d01a28118d8a.png +0 -0
- package/dist-ui/assets/node_modules/@react-navigation/elements/lib/module/assets/back-icon-mask.0a328cd9c1afd0afe8e3b1ec5165b1b4.png +0 -0
- package/dist-ui/assets/node_modules/@react-navigation/elements/lib/module/assets/back-icon.35ba0eaec5a4f5ed12ca16fabeae451d.png +0 -0
- package/dist-ui/assets/node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55.png +0 -0
- package/dist-ui/assets/node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@2x.png +0 -0
- package/dist-ui/assets/node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@3x.png +0 -0
- package/dist-ui/assets/node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@4x.png +0 -0
- package/dist-ui/assets/node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7.png +0 -0
- package/dist-ui/assets/node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@2x.png +0 -0
- package/dist-ui/assets/node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@3x.png +0 -0
- package/dist-ui/assets/node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@4x.png +0 -0
- package/dist-ui/assets/node_modules/@react-navigation/elements/lib/module/assets/search-icon.286d67d3f74808a60a78d3ebf1a5fb57.png +0 -0
- package/dist-ui/assets/node_modules/expo-router/assets/arrow_down.017bc6ba3fc25503e5eb5e53826d48a8.png +0 -0
- package/dist-ui/assets/node_modules/expo-router/assets/error.d1ea1496f9057eb392d5bbf3732a61b7.png +0 -0
- package/dist-ui/assets/node_modules/expo-router/assets/file.19eeb73b9593a38f8e9f418337fc7d10.png +0 -0
- package/dist-ui/assets/node_modules/expo-router/assets/forward.d8b800c443b8972542883e0b9de2bdc6.png +0 -0
- package/dist-ui/assets/node_modules/expo-router/assets/pkg.ab19f4cbc543357183a20571f68380a3.png +0 -0
- package/dist-ui/assets/node_modules/expo-router/assets/sitemap.412dd9275b6b48ad28f5e3d81bb1f626.png +0 -0
- package/dist-ui/assets/node_modules/expo-router/assets/unmatched.20e71bdf79e3a97bf55fd9e164041578.png +0 -0
- package/dist-ui/favicon.ico +0 -0
- package/dist-ui/index.html +38 -0
- package/dist-ui/metadata.json +1 -0
- package/package.json +29 -12
- package/dist/agent-events.js.map +0 -1
- package/dist/agent-message-parts.d.ts +0 -17
- package/dist/agent-message-parts.js +0 -31
- package/dist/agent-message-parts.js.map +0 -1
- package/dist/agent-output-parser.js.map +0 -1
- package/dist/agent-prompt-delivery.js.map +0 -1
- package/dist/agent-tracker.js.map +0 -1
- package/dist/agent-watcher.js.map +0 -1
- package/dist/atomic-write.js.map +0 -1
- package/dist/attachment-store.js.map +0 -1
- package/dist/builtin-metadata-watchers.js.map +0 -1
- package/dist/claude-hooks.js.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/context/compactor.js.map +0 -1
- package/dist/context/context-bridge.js.map +0 -1
- package/dist/context/context-file.js.map +0 -1
- package/dist/context/history.js.map +0 -1
- package/dist/daemon.js.map +0 -1
- package/dist/dashboard/command-spec.js.map +0 -1
- package/dist/dashboard/feedback.js.map +0 -1
- package/dist/dashboard/index.js.map +0 -1
- package/dist/dashboard/operation-failures.js.map +0 -1
- package/dist/dashboard/pending-actions.js.map +0 -1
- package/dist/dashboard/quick-jump.js.map +0 -1
- package/dist/dashboard/runtime-evidence.js.map +0 -1
- package/dist/dashboard/session-actions.js.map +0 -1
- package/dist/dashboard/session-registry.js.map +0 -1
- package/dist/dashboard/sort.js.map +0 -1
- package/dist/dashboard/state.js.map +0 -1
- package/dist/dashboard/targets.js.map +0 -1
- package/dist/dashboard/ui-state-store.js.map +0 -1
- package/dist/debug.js.map +0 -1
- package/dist/default-plugins/gh-pr-context.js.map +0 -1
- package/dist/default-plugins/transcript-length.js.map +0 -1
- package/dist/fast-control.js.map +0 -1
- package/dist/hotkeys.js.map +0 -1
- package/dist/http-client.js.map +0 -1
- package/dist/instance-directory.d.ts +0 -32
- package/dist/instance-directory.js +0 -82
- package/dist/instance-directory.js.map +0 -1
- package/dist/instance-registry.d.ts +0 -39
- package/dist/instance-registry.js +0 -208
- package/dist/instance-registry.js.map +0 -1
- package/dist/key-parser.js.map +0 -1
- package/dist/last-used.js.map +0 -1
- package/dist/main.js.map +0 -1
- package/dist/managed-launch-env.js.map +0 -1
- package/dist/metadata-server.js.map +0 -1
- package/dist/metadata-store.js.map +0 -1
- package/dist/multiplexer/agent-io-methods.js.map +0 -1
- package/dist/multiplexer/archives.js.map +0 -1
- package/dist/multiplexer/dashboard-actions-methods.js.map +0 -1
- package/dist/multiplexer/dashboard-control.js.map +0 -1
- package/dist/multiplexer/dashboard-interaction.js.map +0 -1
- package/dist/multiplexer/dashboard-model.js.map +0 -1
- package/dist/multiplexer/dashboard-ops.js.map +0 -1
- package/dist/multiplexer/dashboard-state-methods.js.map +0 -1
- package/dist/multiplexer/dashboard-tail-methods.js.map +0 -1
- package/dist/multiplexer/dashboard-view-methods.js.map +0 -1
- package/dist/multiplexer/graveyard-view-model.js.map +0 -1
- package/dist/multiplexer/index.js.map +0 -1
- package/dist/multiplexer/navigation.js.map +0 -1
- package/dist/multiplexer/notifications.js.map +0 -1
- package/dist/multiplexer/persistence-methods.js.map +0 -1
- package/dist/multiplexer/runtime-lifecycle-methods.js.map +0 -1
- package/dist/multiplexer/runtime-state.js.map +0 -1
- package/dist/multiplexer/runtime-sync.js.map +0 -1
- package/dist/multiplexer/service-state-snapshot.js.map +0 -1
- package/dist/multiplexer/services.js.map +0 -1
- package/dist/multiplexer/session-actions.d.ts +0 -40
- package/dist/multiplexer/session-actions.js +0 -110
- package/dist/multiplexer/session-actions.js.map +0 -1
- package/dist/multiplexer/session-launch.js.map +0 -1
- package/dist/multiplexer/session-runtime-core.js.map +0 -1
- package/dist/multiplexer/subscreens.js.map +0 -1
- package/dist/multiplexer/tool-picker.js.map +0 -1
- package/dist/multiplexer/worktree-graveyard.js.map +0 -1
- package/dist/multiplexer/worktrees.js.map +0 -1
- package/dist/notification-context.js.map +0 -1
- package/dist/notifications.js.map +0 -1
- package/dist/notify.js.map +0 -1
- package/dist/orchestration-actions.js.map +0 -1
- package/dist/orchestration-dispatcher.d.ts +0 -25
- package/dist/orchestration-dispatcher.js +0 -59
- package/dist/orchestration-dispatcher.js.map +0 -1
- package/dist/orchestration-routing.js.map +0 -1
- package/dist/orchestration.js.map +0 -1
- package/dist/osc-notifications.js.map +0 -1
- package/dist/paths.js.map +0 -1
- package/dist/plugin-runtime.js.map +0 -1
- package/dist/project-events.js.map +0 -1
- package/dist/project-scanner.js.map +0 -1
- package/dist/project-service-manifest.js.map +0 -1
- package/dist/recency.js.map +0 -1
- package/dist/recorder.js.map +0 -1
- package/dist/session-bootstrap.js.map +0 -1
- package/dist/session-input-operations.d.ts +0 -19
- package/dist/session-input-operations.js +0 -46
- package/dist/session-input-operations.js.map +0 -1
- package/dist/session-message-history.d.ts +0 -27
- package/dist/session-message-history.js +0 -105
- package/dist/session-message-history.js.map +0 -1
- package/dist/session-runtime.js.map +0 -1
- package/dist/session-semantics.js.map +0 -1
- package/dist/shell-args.js.map +0 -1
- package/dist/shell-hooks.js.map +0 -1
- package/dist/shell-state.js.map +0 -1
- package/dist/status-detector.js.map +0 -1
- package/dist/statusline-model.js.map +0 -1
- package/dist/task-dispatcher.d.ts +0 -64
- package/dist/task-dispatcher.js +0 -213
- package/dist/task-dispatcher.js.map +0 -1
- package/dist/task-workflow.js.map +0 -1
- package/dist/tasks.js.map +0 -1
- package/dist/team.js.map +0 -1
- package/dist/terminal-host.js.map +0 -1
- package/dist/threads.js.map +0 -1
- package/dist/tmux/doctor.js.map +0 -1
- package/dist/tmux/inbox-popup.js.map +0 -1
- package/dist/tmux/runtime-manager.js.map +0 -1
- package/dist/tmux/session-transport.js.map +0 -1
- package/dist/tmux/statusline-cache.js.map +0 -1
- package/dist/tmux/statusline.js.map +0 -1
- package/dist/tmux/switcher.js.map +0 -1
- package/dist/tmux/window-open.js.map +0 -1
- package/dist/tool-output-watchers.js.map +0 -1
- package/dist/tui/render/box.js.map +0 -1
- package/dist/tui/render/text.js.map +0 -1
- package/dist/tui/screens/dashboard-renderers.js.map +0 -1
- package/dist/tui/screens/overlay-renderers.js.map +0 -1
- package/dist/tui/screens/subscreen-renderers.js.map +0 -1
- package/dist/workflow.js.map +0 -1
- package/dist/worktree.js.map +0 -1
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import { existsSync
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
2
|
import { loadConfig } from "../config.js";
|
|
3
3
|
import { loadMetadataState, updateSessionMetadata } from "../metadata-store.js";
|
|
4
|
-
import { getGraveyardPath, getLocalAimuxDir, getStatePath } from "../paths.js";
|
|
5
4
|
import { isToolInternalWorktree, listWorktrees as listAllWorktrees } from "../worktree.js";
|
|
6
5
|
import { isDashboardWindowName } from "../tmux/runtime-manager.js";
|
|
7
6
|
import { TmuxSessionTransport } from "../tmux/session-transport.js";
|
|
8
7
|
import { markLastUsed } from "../last-used.js";
|
|
9
8
|
import { listWorktreeGraveyardPaths } from "./worktree-graveyard.js";
|
|
9
|
+
import { getServiceLaunchCommandLine } from "./services.js";
|
|
10
|
+
import { listTopologySessionStates, moveTopologySessionToGraveyard, upsertTopologySession, } from "../runtime-core/topology-sessions.js";
|
|
11
|
+
import { listTopologyServiceStates } from "../runtime-core/topology-services.js";
|
|
12
|
+
const DASHBOARD_BACKGROUND_REFRESH_MS = 2000;
|
|
10
13
|
function isAvailableWorktreePath(worktreePath, graveyardPaths = listWorktreeGraveyardPaths()) {
|
|
11
14
|
if (!worktreePath)
|
|
12
15
|
return true;
|
|
@@ -110,41 +113,6 @@ export function startStatusRefresh(host) {
|
|
|
110
113
|
return;
|
|
111
114
|
host.statusInterval = setInterval(() => {
|
|
112
115
|
let dashboardNeedsRender = false;
|
|
113
|
-
if (host.mode === "project-service") {
|
|
114
|
-
host.taskDispatcher?.tick(host.sessions.map((s) => s.id));
|
|
115
|
-
host.orchestrationDispatcher?.tick(host.sessions.map((s) => s.id));
|
|
116
|
-
}
|
|
117
|
-
const events = host.taskDispatcher?.drainEvents() ?? [];
|
|
118
|
-
for (const ev of events) {
|
|
119
|
-
if (ev.type === "assigned") {
|
|
120
|
-
host.footerFlash = `⧫ Task assigned → ${ev.sessionId}`;
|
|
121
|
-
}
|
|
122
|
-
else if (ev.type === "completed") {
|
|
123
|
-
host.footerFlash = `✓ Task done by ${ev.sessionId}`;
|
|
124
|
-
}
|
|
125
|
-
else if (ev.type === "failed") {
|
|
126
|
-
host.footerFlash = `✗ Task failed: ${ev.sessionId}`;
|
|
127
|
-
}
|
|
128
|
-
else if (ev.type === "review_created") {
|
|
129
|
-
host.footerFlash = `⧫ Review created: ${ev.description}`;
|
|
130
|
-
}
|
|
131
|
-
else if (ev.type === "review_approved") {
|
|
132
|
-
host.footerFlash = `✓ Review approved: ${ev.description}`;
|
|
133
|
-
}
|
|
134
|
-
else if (ev.type === "changes_requested") {
|
|
135
|
-
host.footerFlash = `↻ Changes requested: ${ev.description}`;
|
|
136
|
-
}
|
|
137
|
-
host.footerFlashTicks = 3;
|
|
138
|
-
dashboardNeedsRender = true;
|
|
139
|
-
}
|
|
140
|
-
const orchestrationEvents = host.orchestrationDispatcher?.drainEvents() ?? [];
|
|
141
|
-
for (const event of orchestrationEvents) {
|
|
142
|
-
if (event.type === "message_delivered") {
|
|
143
|
-
host.footerFlash = `✉ Message delivered → ${event.sessionId}`;
|
|
144
|
-
host.footerFlashTicks = 3;
|
|
145
|
-
dashboardNeedsRender = true;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
116
|
if (host.dashboardFeedback.tickFlashVisibilityChanged()) {
|
|
149
117
|
dashboardNeedsRender = true;
|
|
150
118
|
}
|
|
@@ -166,7 +134,7 @@ export function startStatusRefresh(host) {
|
|
|
166
134
|
if (host.mode === "dashboard") {
|
|
167
135
|
const now = Date.now();
|
|
168
136
|
if (now >= host.dashboardNextBackgroundRefreshAt) {
|
|
169
|
-
host.dashboardNextBackgroundRefreshAt = now +
|
|
137
|
+
host.dashboardNextBackgroundRefreshAt = now + DASHBOARD_BACKGROUND_REFRESH_MS;
|
|
170
138
|
void host.refreshDashboardModelFromService().then((refreshed) => {
|
|
171
139
|
if (refreshed || dashboardNeedsRender) {
|
|
172
140
|
host.renderCurrentDashboardView();
|
|
@@ -185,45 +153,38 @@ export function stopStatusRefresh(host) {
|
|
|
185
153
|
host.statusInterval = null;
|
|
186
154
|
}
|
|
187
155
|
}
|
|
188
|
-
export function
|
|
189
|
-
const
|
|
190
|
-
|
|
156
|
+
export function syncSessionsFromTopology(host) {
|
|
157
|
+
const state = host.constructor.loadState();
|
|
158
|
+
const liveAgentWindows = restoreTmuxSessionsFromTopology(host);
|
|
159
|
+
loadOfflineTopologySessions(host, liveAgentWindows);
|
|
191
160
|
loadOfflineServices(host, state);
|
|
192
161
|
host.invalidateDesktopStateSnapshot();
|
|
193
162
|
}
|
|
194
|
-
export function
|
|
195
|
-
|
|
163
|
+
export function loadOfflineTopologySessions(host, liveAgentWindows = listLiveAgentWindows(host)) {
|
|
164
|
+
const savedSessions = listTopologySessionStates({ statuses: ["offline"] });
|
|
165
|
+
if (savedSessions.length === 0) {
|
|
196
166
|
const changed = host.offlineSessions.length > 0;
|
|
197
167
|
host.offlineSessions = [];
|
|
198
168
|
return changed;
|
|
199
169
|
}
|
|
200
170
|
const liveIds = new Set(liveAgentWindows.map(({ metadata }) => metadata.sessionId));
|
|
201
|
-
const liveBackendIds = new Set(liveAgentWindows
|
|
202
|
-
.map(({ metadata }) => metadata.backendSessionId)
|
|
203
|
-
.filter((value) => Boolean(value)));
|
|
204
171
|
const ownedIds = new Set();
|
|
205
172
|
for (const s of host.sessions)
|
|
206
173
|
ownedIds.add(s.id);
|
|
207
|
-
for (const inst of host.getRemoteInstancesSafe()) {
|
|
208
|
-
for (const rs of inst.sessions)
|
|
209
|
-
ownedIds.add(rs.id);
|
|
210
|
-
}
|
|
211
174
|
const ownedBackendIds = new Set(host.sessions
|
|
212
175
|
.map((session) => session.backendSessionId)
|
|
213
176
|
.filter((value) => Boolean(value)));
|
|
214
|
-
const nextOfflineSessions =
|
|
177
|
+
const nextOfflineSessions = savedSessions
|
|
215
178
|
.filter((s) => {
|
|
216
179
|
if (!isIntentionalOfflineSession(s))
|
|
217
180
|
return false;
|
|
218
181
|
if (liveIds.has(s.id))
|
|
219
182
|
return false;
|
|
220
|
-
if (s.backendSessionId && liveBackendIds.has(s.backendSessionId))
|
|
221
|
-
return false;
|
|
222
183
|
if (ownedIds.has(s.id))
|
|
223
184
|
return false;
|
|
224
185
|
if (s.backendSessionId && ownedBackendIds.has(s.backendSessionId))
|
|
225
186
|
return false;
|
|
226
|
-
if (host.dashboardPendingActions?.
|
|
187
|
+
if (host.dashboardPendingActions?.getSessionAction?.(s.id) === "starting")
|
|
227
188
|
return false;
|
|
228
189
|
if (!isAvailableWorktreePath(s.worktreePath))
|
|
229
190
|
return false;
|
|
@@ -231,19 +192,20 @@ export function loadOfflineSessions(host, state = host.constructor.loadState(),
|
|
|
231
192
|
})
|
|
232
193
|
.map(offlineSessionState);
|
|
233
194
|
const previousKey = host.offlineSessions
|
|
234
|
-
.map((session) => `${session.id}:${session.label ?? ""}:${session.worktreePath ?? ""}`)
|
|
195
|
+
.map((session) => `${session.id}:${session.label ?? ""}:${session.worktreePath ?? ""}:${session.backendSessionId ?? ""}:${JSON.stringify(session.team ?? null)}`)
|
|
235
196
|
.join("|");
|
|
236
197
|
const nextKey = nextOfflineSessions
|
|
237
|
-
.map((session) => `${session.id}:${session.label ?? ""}:${session.worktreePath ?? ""}`)
|
|
198
|
+
.map((session) => `${session.id}:${session.label ?? ""}:${session.worktreePath ?? ""}:${session.backendSessionId ?? ""}:${JSON.stringify(session.team ?? null)}`)
|
|
238
199
|
.join("|");
|
|
239
200
|
host.offlineSessions = nextOfflineSessions;
|
|
240
201
|
if (host.offlineSessions.length > 0) {
|
|
241
|
-
host.debug?.(`loaded ${host.offlineSessions.length} offline session(s) from
|
|
202
|
+
host.debug?.(`loaded ${host.offlineSessions.length} offline session(s) from runtime topology`, "session");
|
|
242
203
|
}
|
|
243
204
|
return previousKey !== nextKey;
|
|
244
205
|
}
|
|
245
206
|
export function loadOfflineServices(host, state = host.constructor.loadState()) {
|
|
246
|
-
const
|
|
207
|
+
const topologyServices = listTopologyServiceStates({ statuses: ["stopped", "offline"] });
|
|
208
|
+
const savedServices = topologyServices.length > 0 ? topologyServices : (state?.services ?? []);
|
|
247
209
|
if (savedServices.length === 0) {
|
|
248
210
|
const changed = host.offlineServices.length > 0;
|
|
249
211
|
host.offlineServices = [];
|
|
@@ -259,7 +221,7 @@ export function loadOfflineServices(host, state = host.constructor.loadState())
|
|
|
259
221
|
const nextOfflineServices = savedServices.filter((service) => {
|
|
260
222
|
if (liveServiceIds.has(service.id))
|
|
261
223
|
return false;
|
|
262
|
-
if (host.dashboardPendingActions?.
|
|
224
|
+
if (host.dashboardPendingActions?.getServiceAction?.(service.id) === "starting")
|
|
263
225
|
return false;
|
|
264
226
|
if (!isAvailableWorktreePath(service.worktreePath))
|
|
265
227
|
return false;
|
|
@@ -288,7 +250,7 @@ export function buildLiveServiceStates(host) {
|
|
|
288
250
|
if (seen.has(metadata.sessionId))
|
|
289
251
|
continue;
|
|
290
252
|
seen.add(metadata.sessionId);
|
|
291
|
-
const launchCommandLine = metadata.
|
|
253
|
+
const launchCommandLine = metadata.launchCommandLine?.trim() || getServiceLaunchCommandLine(metadata);
|
|
292
254
|
liveServices.push({
|
|
293
255
|
id: metadata.sessionId,
|
|
294
256
|
createdAt: metadata.createdAt,
|
|
@@ -301,8 +263,9 @@ export function buildLiveServiceStates(host) {
|
|
|
301
263
|
}
|
|
302
264
|
return liveServices;
|
|
303
265
|
}
|
|
304
|
-
export function
|
|
305
|
-
const
|
|
266
|
+
export function restoreTmuxSessionsFromTopology(host) {
|
|
267
|
+
const savedSessions = listTopologySessionStates({ statuses: ["running", "idle", "offline"] });
|
|
268
|
+
const savedById = new Map(savedSessions.map((session) => [session.id, session]));
|
|
306
269
|
const cols = process.stdout.columns ?? 80;
|
|
307
270
|
const rows = process.stdout.rows ?? 24;
|
|
308
271
|
const liveAgentWindows = listLiveAgentWindows(host);
|
|
@@ -330,9 +293,8 @@ export function restoreTmuxSessionsFromState(host, state = host.constructor.load
|
|
|
330
293
|
if (existing)
|
|
331
294
|
continue;
|
|
332
295
|
const transport = new TmuxSessionTransport(metadata.sessionId, metadata.command, target, host.tmuxRuntimeManager, cols, rows);
|
|
333
|
-
transport.backendSessionId = metadata.backendSessionId;
|
|
334
296
|
host.sessionTmuxTargets.set(metadata.sessionId, target);
|
|
335
|
-
host.registerManagedSession(transport, metadata.args, metadata.toolConfigKey, metadata.worktreePath, metadata.role, metadata.createdAt ? Date.parse(metadata.createdAt) : undefined);
|
|
297
|
+
host.registerManagedSession(transport, metadata.args, metadata.toolConfigKey, metadata.worktreePath, metadata.role, metadata.createdAt ? Date.parse(metadata.createdAt) : undefined, metadata.team);
|
|
336
298
|
const saved = savedById.get(metadata.sessionId);
|
|
337
299
|
const label = metadata.label ?? saved?.label;
|
|
338
300
|
if (label) {
|
|
@@ -343,7 +305,6 @@ export function restoreTmuxSessionsFromState(host, state = host.constructor.load
|
|
|
343
305
|
}
|
|
344
306
|
host.syncTmuxWindowMetadata(metadata.sessionId);
|
|
345
307
|
}
|
|
346
|
-
host.writeSessionsFile?.();
|
|
347
308
|
host.updateContextWatcherSessions?.();
|
|
348
309
|
return liveAgentWindows;
|
|
349
310
|
}
|
|
@@ -351,6 +312,7 @@ export function stopSessionToOffline(host, session) {
|
|
|
351
312
|
if (host.stoppingSessionIds.has(session.id))
|
|
352
313
|
return;
|
|
353
314
|
markLifecycleUsed(host, session.id);
|
|
315
|
+
const backendSessionId = session.backendSessionId;
|
|
354
316
|
const offlineEntry = {
|
|
355
317
|
id: session.id,
|
|
356
318
|
tool: session.command,
|
|
@@ -359,16 +321,22 @@ export function stopSessionToOffline(host, session) {
|
|
|
359
321
|
args: host.sessionOriginalArgs.get(session.id) ?? [],
|
|
360
322
|
lifecycle: "offline",
|
|
361
323
|
createdAt: session.startTime ? new Date(session.startTime).toISOString() : undefined,
|
|
362
|
-
backendSessionId
|
|
324
|
+
backendSessionId,
|
|
325
|
+
team: session.team,
|
|
363
326
|
worktreePath: host.sessionWorktreePaths.get(session.id),
|
|
364
327
|
label: host.getSessionLabel(session.id),
|
|
365
328
|
headline: host.deriveHeadline(session.id),
|
|
366
329
|
};
|
|
367
|
-
|
|
330
|
+
const existingIndex = host.offlineSessions.findIndex((entry) => entry.id === session.id);
|
|
331
|
+
if (existingIndex >= 0) {
|
|
332
|
+
host.offlineSessions[existingIndex] = { ...host.offlineSessions[existingIndex], ...offlineEntry };
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
368
335
|
host.offlineSessions.push(offlineEntry);
|
|
369
336
|
}
|
|
370
337
|
host.stoppingSessionIds.add(session.id);
|
|
371
338
|
host.startedInDashboard = true;
|
|
339
|
+
upsertTopologySession(offlineEntry, "offline");
|
|
372
340
|
host.saveState();
|
|
373
341
|
session.kill();
|
|
374
342
|
host.debug?.(`stopped session ${session.id} → offline`, "session");
|
|
@@ -390,30 +358,18 @@ export function adjustAfterRemove(host, hasWorktrees) {
|
|
|
390
358
|
}
|
|
391
359
|
}
|
|
392
360
|
}
|
|
393
|
-
export function graveyardSession(host, sessionId) {
|
|
394
|
-
const session = host.offlineSessions.find((s) => s.id === sessionId)
|
|
361
|
+
export function graveyardSession(host, sessionId, _sessionSeed) {
|
|
362
|
+
const session = host.offlineSessions.find((s) => s.id === sessionId) ??
|
|
363
|
+
listTopologySessionStates({ statuses: ["running", "idle", "offline"] }).find((s) => s.id === sessionId);
|
|
395
364
|
if (!session)
|
|
396
365
|
return;
|
|
397
366
|
markLifecycleUsed(host, sessionId);
|
|
398
367
|
host.offlineSessions = host.offlineSessions.filter((s) => s.id !== sessionId);
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
}
|
|
405
|
-
catch { }
|
|
406
|
-
}
|
|
407
|
-
graveyard.push({ ...session, id: session.id });
|
|
408
|
-
writeFileSync(graveyardPath, JSON.stringify(graveyard, null, 2) + "\n");
|
|
409
|
-
const statePath = getStatePath();
|
|
410
|
-
if (existsSync(statePath)) {
|
|
411
|
-
try {
|
|
412
|
-
const state = JSON.parse(readFileSync(statePath, "utf-8"));
|
|
413
|
-
state.sessions = state.sessions.filter((s) => s.id !== sessionId);
|
|
414
|
-
writeFileSync(statePath, JSON.stringify(state, null, 2) + "\n");
|
|
415
|
-
}
|
|
416
|
-
catch { }
|
|
368
|
+
moveTopologySessionToGraveyard(sessionId);
|
|
369
|
+
host.invalidateDesktopStateSnapshot?.();
|
|
370
|
+
host.writeStatuslineFile?.();
|
|
371
|
+
if (host.mode === "dashboard") {
|
|
372
|
+
host.renderCurrentDashboardView?.();
|
|
417
373
|
}
|
|
418
374
|
host.debug?.(`graveyarded session ${sessionId}`, "session");
|
|
419
375
|
}
|
|
@@ -443,7 +399,6 @@ export function evictZombieSession(host, runtime) {
|
|
|
443
399
|
}
|
|
444
400
|
host.stoppingSessionIds.delete(runtime.id);
|
|
445
401
|
host.sessionTmuxTargets.delete(runtime.id);
|
|
446
|
-
host.writeSessionsFile();
|
|
447
402
|
host.updateContextWatcherSessions();
|
|
448
403
|
host.saveState();
|
|
449
404
|
}
|
|
@@ -464,16 +419,20 @@ export function resumeOfflineSession(host, session) {
|
|
|
464
419
|
return;
|
|
465
420
|
const derived = loadMetadataState().sessions[session.id]?.derived;
|
|
466
421
|
const relaunchFresh = derived?.activity === "error" || derived?.attention === "error";
|
|
467
|
-
const
|
|
422
|
+
const backendSessionId = session.backendSessionId;
|
|
423
|
+
const useBackendResume = !relaunchFresh && host.sessionBootstrap.canResumeWithBackendSessionId(toolCfg, backendSessionId);
|
|
468
424
|
let actionArgs;
|
|
469
425
|
if (useBackendResume) {
|
|
470
|
-
actionArgs = toolCfg.resumeArgs.map((a) => a.replace("{sessionId}",
|
|
426
|
+
actionArgs = toolCfg.resumeArgs.map((a) => a.replace("{sessionId}", backendSessionId));
|
|
471
427
|
}
|
|
472
428
|
else if (relaunchFresh) {
|
|
473
429
|
actionArgs = [];
|
|
474
430
|
}
|
|
475
431
|
else {
|
|
476
|
-
|
|
432
|
+
// Targeted dashboard restore must never use "latest session" style fallbacks
|
|
433
|
+
// such as Claude --continue or Codex resume --last; those can resurrect the
|
|
434
|
+
// wrong agent after a crash or stale-state mismatch.
|
|
435
|
+
throw new Error(`Cannot restore session "${session.id}" without an exact resumable backend session id for "${session.toolConfigKey}"`);
|
|
477
436
|
}
|
|
478
437
|
const args = [...(toolCfg.args ?? []), ...actionArgs];
|
|
479
438
|
if (relaunchFresh) {
|
|
@@ -492,31 +451,42 @@ export function resumeOfflineSession(host, session) {
|
|
|
492
451
|
if (preservedLabel) {
|
|
493
452
|
host.sessionLabels.set(session.id, preservedLabel);
|
|
494
453
|
}
|
|
495
|
-
host.debug?.(`resuming offline session ${session.id} (${relaunchFresh ? "fresh" : useBackendResume ? `backend=${
|
|
496
|
-
host.createSession(session.command, args, toolCfg.preambleFlag, session.toolConfigKey, undefined, undefined, session.worktreePath, useBackendResume ?
|
|
454
|
+
host.debug?.(`resuming offline session ${session.id} (${relaunchFresh ? "fresh" : useBackendResume ? `backend=${backendSessionId ?? "none"}` : "fallback"})`, "session");
|
|
455
|
+
host.createSession(session.command, args, toolCfg.preambleFlag, session.toolConfigKey, undefined, undefined, session.worktreePath, useBackendResume ? backendSessionId : undefined, session.id, true, useBackendResume, session.team);
|
|
456
|
+
}
|
|
457
|
+
export function recordSessionBackendSessionId(host, sessionId, backendSessionId) {
|
|
458
|
+
const normalizedBackendSessionId = backendSessionId.trim();
|
|
459
|
+
if (!sessionId.trim()) {
|
|
460
|
+
throw new Error("sessionId is required");
|
|
461
|
+
}
|
|
462
|
+
if (!normalizedBackendSessionId) {
|
|
463
|
+
throw new Error("backendSessionId is required");
|
|
464
|
+
}
|
|
465
|
+
const runtime = host.sessions.find((session) => session.id === sessionId);
|
|
466
|
+
const offline = host.offlineSessions.find((session) => session.id === sessionId);
|
|
467
|
+
if (!runtime && !offline) {
|
|
468
|
+
throw new Error(`Agent "${sessionId}" is not managed by this runtime`);
|
|
469
|
+
}
|
|
470
|
+
const existing = runtime?.backendSessionId ?? offline?.backendSessionId;
|
|
471
|
+
if (existing && existing !== normalizedBackendSessionId) {
|
|
472
|
+
throw new Error(`Agent "${sessionId}" already has backend session "${existing}", cannot replace with "${normalizedBackendSessionId}"`);
|
|
473
|
+
}
|
|
474
|
+
const selectedBackendSessionId = existing ?? normalizedBackendSessionId;
|
|
475
|
+
if (runtime) {
|
|
476
|
+
runtime.backendSessionId = selectedBackendSessionId;
|
|
477
|
+
host.syncTmuxWindowMetadata?.(sessionId);
|
|
478
|
+
}
|
|
479
|
+
if (offline) {
|
|
480
|
+
offline.backendSessionId = selectedBackendSessionId;
|
|
481
|
+
}
|
|
482
|
+
host.saveState?.();
|
|
483
|
+
host.invalidateDesktopStateSnapshot?.();
|
|
484
|
+
host.writeStatuslineFile?.();
|
|
485
|
+
return { sessionId, backendSessionId: selectedBackendSessionId };
|
|
497
486
|
}
|
|
498
487
|
export function startHeartbeat(host) {
|
|
499
488
|
host.runtimeSync.startHeartbeat();
|
|
500
489
|
}
|
|
501
|
-
export function handleSessionClaimed(host, sessionId) {
|
|
502
|
-
const session = host.sessions.find((s) => s.id === sessionId);
|
|
503
|
-
if (!session)
|
|
504
|
-
return;
|
|
505
|
-
host.debug?.(`session ${sessionId} was claimed by another instance, killing local PTY`, "instance");
|
|
506
|
-
session.kill();
|
|
507
|
-
const idx = host.sessions.indexOf(session);
|
|
508
|
-
if (idx >= 0) {
|
|
509
|
-
host.sessions.splice(idx, 1);
|
|
510
|
-
host.sessionToolKeys.delete(sessionId);
|
|
511
|
-
host.sessionOriginalArgs.delete(sessionId);
|
|
512
|
-
host.sessionWorktreePaths.delete(sessionId);
|
|
513
|
-
host.sessionTmuxTargets.delete(sessionId);
|
|
514
|
-
}
|
|
515
|
-
if (host.activeIndex >= host.sessions.length) {
|
|
516
|
-
host.activeIndex = Math.max(0, host.sessions.length - 1);
|
|
517
|
-
}
|
|
518
|
-
host.renderDashboard();
|
|
519
|
-
}
|
|
520
490
|
export function stopHeartbeat(host) {
|
|
521
491
|
host.runtimeSync.stopHeartbeat();
|
|
522
492
|
}
|
|
@@ -526,40 +496,6 @@ export function startProjectServiceRefresh(host) {
|
|
|
526
496
|
export function stopProjectServiceRefresh(host) {
|
|
527
497
|
host.runtimeSync.stopProjectServiceRefresh();
|
|
528
498
|
}
|
|
529
|
-
export function getRemoteInstancesSafe(host) {
|
|
530
|
-
return host.instanceDirectory.getRemoteInstancesSafe(host.instanceId, process.cwd());
|
|
531
|
-
}
|
|
532
|
-
export function getRemoteOwnedSessionKeys(host) {
|
|
533
|
-
return host.instanceDirectory.getRemoteOwnedSessionKeys(host.instanceId, process.cwd());
|
|
534
|
-
}
|
|
535
|
-
export function getInstanceSessionRefs(host) {
|
|
536
|
-
return host.sessions.map((s) => ({
|
|
537
|
-
id: s.id,
|
|
538
|
-
tool: s.command,
|
|
539
|
-
backendSessionId: s.backendSessionId,
|
|
540
|
-
worktreePath: host.sessionWorktreePaths.get(s.id),
|
|
541
|
-
}));
|
|
542
|
-
}
|
|
543
|
-
export function writeSessionsFile(host) {
|
|
544
|
-
const dir = getLocalAimuxDir();
|
|
545
|
-
if (!existsSync(dir))
|
|
546
|
-
mkdirSync(dir, { recursive: true });
|
|
547
|
-
const localSessions = host.sessions.map((s) => ({
|
|
548
|
-
id: s.id,
|
|
549
|
-
tool: s.command,
|
|
550
|
-
backendSessionId: s.backendSessionId,
|
|
551
|
-
worktreePath: host.sessionWorktreePaths.get(s.id),
|
|
552
|
-
}));
|
|
553
|
-
const data = host.instanceDirectory.buildSessionsFileEntries(localSessions, host.instanceDirectory.getRemoteInstancesSafe(host.instanceId, process.cwd()));
|
|
554
|
-
writeFileSync(`${dir}/sessions.json`, JSON.stringify(data, null, 2) + "\n");
|
|
555
|
-
}
|
|
556
|
-
export function removeSessionsFile() {
|
|
557
|
-
try {
|
|
558
|
-
unlinkSync(`${getLocalAimuxDir()}/sessions.json`);
|
|
559
|
-
}
|
|
560
|
-
catch { }
|
|
561
|
-
}
|
|
562
499
|
export function listDesktopWorktrees(_host) {
|
|
563
500
|
return listAllWorktrees().filter((wt) => !wt.isBare && !isToolInternalWorktree(wt));
|
|
564
501
|
}
|
|
565
|
-
//# sourceMappingURL=runtime-state.js.map
|
|
@@ -1,22 +1,14 @@
|
|
|
1
|
-
import type { InstanceDirectory } from "../instance-directory.js";
|
|
2
|
-
import type { InstanceSessionRef } from "../instance-registry.js";
|
|
3
1
|
export declare class MultiplexerRuntimeSync {
|
|
4
2
|
private readonly deps;
|
|
5
3
|
private heartbeatInterval;
|
|
6
4
|
private projectServiceInterval;
|
|
7
5
|
constructor(deps: {
|
|
8
|
-
instanceDirectory: InstanceDirectory;
|
|
9
|
-
instanceId: string;
|
|
10
6
|
cwd: string;
|
|
11
7
|
getMode: () => "dashboard" | "project-service";
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
getInstanceSessionRefs: () => InstanceSessionRef[];
|
|
15
|
-
syncSessionsFromState: () => void;
|
|
16
|
-
loadOfflineSessions: () => boolean;
|
|
8
|
+
syncSessionsFromTopology: () => void;
|
|
9
|
+
loadOfflineTopologySessions: () => boolean;
|
|
17
10
|
renderCurrentDashboardView: () => void;
|
|
18
11
|
renderDashboard: () => void;
|
|
19
|
-
handleSessionClaimed: (sessionId: string) => void;
|
|
20
12
|
writeStatuslineFile: () => void;
|
|
21
13
|
});
|
|
22
14
|
startHeartbeat(): void;
|
|
@@ -10,25 +10,10 @@ export class MultiplexerRuntimeSync {
|
|
|
10
10
|
return;
|
|
11
11
|
this.heartbeatInterval = setInterval(() => {
|
|
12
12
|
if (this.deps.getMode() === "project-service") {
|
|
13
|
-
this.deps.
|
|
13
|
+
this.deps.syncSessionsFromTopology();
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
|
-
|
|
17
|
-
const sessions = this.deps.getInstanceSessionRefs();
|
|
18
|
-
this.deps.instanceDirectory
|
|
19
|
-
.reconcileHeartbeat(this.deps.instanceId, sessions, this.deps.cwd, this.deps.getConfirmedRegistered())
|
|
20
|
-
.then((result) => {
|
|
21
|
-
for (const id of result.claimedIds) {
|
|
22
|
-
this.deps.handleSessionClaimed(id);
|
|
23
|
-
dashboardNeedsRender = true;
|
|
24
|
-
}
|
|
25
|
-
this.deps.setConfirmedRegistered(result.confirmedIds);
|
|
26
|
-
if (dashboardNeedsRender && this.deps.getMode() === "dashboard") {
|
|
27
|
-
this.deps.renderCurrentDashboardView();
|
|
28
|
-
}
|
|
29
|
-
})
|
|
30
|
-
.catch(() => { });
|
|
31
|
-
const offlineChanged = this.deps.loadOfflineSessions();
|
|
16
|
+
const offlineChanged = this.deps.loadOfflineTopologySessions();
|
|
32
17
|
if (offlineChanged && this.deps.getMode() === "dashboard") {
|
|
33
18
|
this.deps.renderCurrentDashboardView();
|
|
34
19
|
}
|
|
@@ -44,7 +29,7 @@ export class MultiplexerRuntimeSync {
|
|
|
44
29
|
if (this.projectServiceInterval)
|
|
45
30
|
return;
|
|
46
31
|
this.projectServiceInterval = setInterval(() => {
|
|
47
|
-
this.deps.
|
|
32
|
+
this.deps.syncSessionsFromTopology();
|
|
48
33
|
}, 2000);
|
|
49
34
|
}
|
|
50
35
|
stopProjectServiceRefresh() {
|
|
@@ -54,4 +39,3 @@ export class MultiplexerRuntimeSync {
|
|
|
54
39
|
}
|
|
55
40
|
}
|
|
56
41
|
}
|
|
57
|
-
//# sourceMappingURL=runtime-sync.js.map
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import type { TmuxRuntimeManager } from "../tmux/runtime-manager.js";
|
|
2
|
-
import type { SavedState, ServiceState
|
|
2
|
+
import type { SavedState, ServiceState } from "./index.js";
|
|
3
3
|
export declare function mergeRuntimeSnapshots(state: SavedState | null, snapshots: {
|
|
4
|
-
sessions?: SessionState[];
|
|
5
4
|
services?: ServiceState[];
|
|
6
5
|
}, cwd: string, savedAt?: string): SavedState;
|
|
7
6
|
export declare function mergeServiceSnapshots(state: SavedState | null, snapshots: ServiceState[], cwd: string, savedAt?: string): SavedState;
|
|
8
|
-
export declare function snapshotProjectAgentWindows(projectRoot: string, tmux: TmuxRuntimeManager): SessionState[];
|
|
9
7
|
export declare function snapshotProjectServiceWindows(projectRoot: string, tmux: TmuxRuntimeManager): ServiceState[];
|
|
10
8
|
export declare function persistProjectRuntimeSnapshotsBeforeTmuxStop(projectRoot: string, tmux: TmuxRuntimeManager): {
|
|
11
|
-
sessions:
|
|
9
|
+
sessions: [];
|
|
12
10
|
services: ServiceState[];
|
|
13
11
|
};
|
|
14
12
|
export declare function persistProjectServiceSnapshotsBeforeRuntimeStop(projectRoot: string, tmux: TmuxRuntimeManager): ServiceState[];
|
|
@@ -2,13 +2,6 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { getStatePath } from "../paths.js";
|
|
3
3
|
import { buildServiceStateFromMetadata } from "./services.js";
|
|
4
4
|
import { listWorktreeGraveyardPaths } from "./worktree-graveyard.js";
|
|
5
|
-
function sessionStateKey(session) {
|
|
6
|
-
return session.backendSessionId ? `backend:${session.backendSessionId}` : `id:${session.id}`;
|
|
7
|
-
}
|
|
8
|
-
function sanitizeSnapshotSession(session) {
|
|
9
|
-
const { tmuxTarget: _tmuxTarget, ...rest } = session;
|
|
10
|
-
return { ...rest, lifecycle: "offline" };
|
|
11
|
-
}
|
|
12
5
|
function isAvailableSnapshotWorktree(worktreePath, graveyardPaths = listWorktreeGraveyardPaths()) {
|
|
13
6
|
if (!worktreePath)
|
|
14
7
|
return true;
|
|
@@ -17,14 +10,6 @@ function isAvailableSnapshotWorktree(worktreePath, graveyardPaths = listWorktree
|
|
|
17
10
|
return existsSync(worktreePath);
|
|
18
11
|
}
|
|
19
12
|
export function mergeRuntimeSnapshots(state, snapshots, cwd, savedAt = new Date().toISOString()) {
|
|
20
|
-
const sessionByKey = new Map();
|
|
21
|
-
for (const session of state?.sessions ?? []) {
|
|
22
|
-
sessionByKey.set(sessionStateKey(session), session);
|
|
23
|
-
}
|
|
24
|
-
for (const session of snapshots.sessions ?? []) {
|
|
25
|
-
const offline = sanitizeSnapshotSession(session);
|
|
26
|
-
sessionByKey.set(sessionStateKey(offline), offline);
|
|
27
|
-
}
|
|
28
13
|
const byId = new Map();
|
|
29
14
|
for (const service of state?.services ?? []) {
|
|
30
15
|
byId.set(service.id, service);
|
|
@@ -39,43 +24,12 @@ export function mergeRuntimeSnapshots(state, snapshots, cwd, savedAt = new Date(
|
|
|
39
24
|
return {
|
|
40
25
|
savedAt,
|
|
41
26
|
cwd: state?.cwd ?? cwd,
|
|
42
|
-
sessions: [...sessionByKey.values()],
|
|
43
27
|
services: [...byId.values()],
|
|
44
28
|
};
|
|
45
29
|
}
|
|
46
30
|
export function mergeServiceSnapshots(state, snapshots, cwd, savedAt = new Date().toISOString()) {
|
|
47
31
|
return mergeRuntimeSnapshots(state, { services: snapshots }, cwd, savedAt);
|
|
48
32
|
}
|
|
49
|
-
export function snapshotProjectAgentWindows(projectRoot, tmux) {
|
|
50
|
-
const seen = new Set();
|
|
51
|
-
const graveyardPaths = listWorktreeGraveyardPaths();
|
|
52
|
-
const snapshots = [];
|
|
53
|
-
for (const { target, metadata } of tmux.listProjectManagedWindows(projectRoot)) {
|
|
54
|
-
if (metadata.kind !== "agent")
|
|
55
|
-
continue;
|
|
56
|
-
if (seen.has(metadata.sessionId))
|
|
57
|
-
continue;
|
|
58
|
-
if (!isAvailableSnapshotWorktree(metadata.worktreePath, graveyardPaths))
|
|
59
|
-
continue;
|
|
60
|
-
if (tmux.isWindowAlive && !tmux.isWindowAlive(target))
|
|
61
|
-
continue;
|
|
62
|
-
seen.add(metadata.sessionId);
|
|
63
|
-
snapshots.push({
|
|
64
|
-
id: metadata.sessionId,
|
|
65
|
-
tool: metadata.command,
|
|
66
|
-
toolConfigKey: metadata.toolConfigKey ?? metadata.command,
|
|
67
|
-
command: metadata.command,
|
|
68
|
-
args: metadata.args ?? [],
|
|
69
|
-
lifecycle: "offline",
|
|
70
|
-
createdAt: metadata.createdAt,
|
|
71
|
-
backendSessionId: metadata.backendSessionId,
|
|
72
|
-
worktreePath: metadata.worktreePath,
|
|
73
|
-
label: metadata.label,
|
|
74
|
-
headline: metadata.statusText,
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
return snapshots;
|
|
78
|
-
}
|
|
79
33
|
export function snapshotProjectServiceWindows(projectRoot, tmux) {
|
|
80
34
|
const seen = new Set();
|
|
81
35
|
const graveyardPaths = listWorktreeGraveyardPaths();
|
|
@@ -97,10 +51,9 @@ export function snapshotProjectServiceWindows(projectRoot, tmux) {
|
|
|
97
51
|
return snapshots;
|
|
98
52
|
}
|
|
99
53
|
export function persistProjectRuntimeSnapshotsBeforeTmuxStop(projectRoot, tmux) {
|
|
100
|
-
const sessions = snapshotProjectAgentWindows(projectRoot, tmux);
|
|
101
54
|
const services = snapshotProjectServiceWindows(projectRoot, tmux);
|
|
102
|
-
if (
|
|
103
|
-
return { sessions, services };
|
|
55
|
+
if (services.length === 0)
|
|
56
|
+
return { sessions: [], services };
|
|
104
57
|
const statePath = getStatePath();
|
|
105
58
|
let existing = null;
|
|
106
59
|
if (existsSync(statePath)) {
|
|
@@ -111,11 +64,10 @@ export function persistProjectRuntimeSnapshotsBeforeTmuxStop(projectRoot, tmux)
|
|
|
111
64
|
existing = null;
|
|
112
65
|
}
|
|
113
66
|
}
|
|
114
|
-
const nextState = mergeRuntimeSnapshots(existing, {
|
|
67
|
+
const nextState = mergeRuntimeSnapshots(existing, { services }, projectRoot);
|
|
115
68
|
writeFileSync(statePath, JSON.stringify(nextState, null, 2) + "\n");
|
|
116
|
-
return { sessions, services };
|
|
69
|
+
return { sessions: [], services };
|
|
117
70
|
}
|
|
118
71
|
export function persistProjectServiceSnapshotsBeforeRuntimeStop(projectRoot, tmux) {
|
|
119
72
|
return persistProjectRuntimeSnapshotsBeforeTmuxStop(projectRoot, tmux).services;
|
|
120
73
|
}
|
|
121
|
-
//# sourceMappingURL=service-state-snapshot.js.map
|