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,18 +1,21 @@
|
|
|
1
|
-
import { existsSync,
|
|
1
|
+
import { existsSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
3
|
import { basename, join } from "node:path";
|
|
4
|
-
import { debug } from "../debug.js";
|
|
5
|
-
import { DashboardPendingActions } from "../dashboard/pending-actions.js";
|
|
6
4
|
import { addDashboardOperationFailure, clearDashboardOperationFailures, listDashboardOperationFailures, } from "../dashboard/operation-failures.js";
|
|
7
5
|
import { composeDashboardWorktreeGroups } from "./dashboard-model.js";
|
|
8
6
|
import { loadDaemonInfo } from "../daemon.js";
|
|
9
|
-
import {
|
|
7
|
+
import { getProjectStateDir, getStatePath } from "../paths.js";
|
|
10
8
|
import { loadMetadataState } from "../metadata-store.js";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
9
|
+
import { createRuntimeExchangeStore } from "../runtime-core/exchange-store.js";
|
|
10
|
+
import { renderCurrentDashboardView as renderCurrentDashboardViewImpl } from "./runtime-state.js";
|
|
11
|
+
import { listWorktreeGraveyardEntries as listWorktreeGraveyardEntriesImpl, listWorktreeGraveyardPaths, } from "./worktree-graveyard.js";
|
|
13
12
|
import { loadStatusline, renderTmuxStatuslineFromData } from "../tmux/statusline.js";
|
|
14
13
|
import { ensureTmuxStatuslineDir, invalidateTmuxStatuslineArtifacts } from "../tmux/statusline-cache.js";
|
|
15
14
|
import { markLastUsed } from "../last-used.js";
|
|
15
|
+
import { isTeammateSession } from "../team.js";
|
|
16
|
+
import { listTopologySessionStates, removeTopologySessionsForWorktree, resurrectTopologySession, } from "../runtime-core/topology-sessions.js";
|
|
17
|
+
import { removeTopologyServicesForWorktree, upsertTopologyService } from "../runtime-core/topology-services.js";
|
|
18
|
+
import { deleteTopologyWorktreeGraveyardEntry, listTopologyWorktreeGraveyard, moveTopologyWorktreeToGraveyard, removeTopologyWorktree, resurrectTopologyWorktreeFromGraveyard, upsertTopologyWorktree, } from "../runtime-core/topology-worktrees.js";
|
|
16
19
|
import { findMainRepo, getWorktreeBaseDir, getWorktreeAddArgs, getWorktreeCreatePath, isToolInternalWorktree, listWorktrees as listAllWorktrees, } from "../worktree.js";
|
|
17
20
|
function recordDashboardFailure(host, input) {
|
|
18
21
|
const failure = addDashboardOperationFailure(input);
|
|
@@ -25,21 +28,37 @@ function recordDashboardFailure(host, input) {
|
|
|
25
28
|
});
|
|
26
29
|
return failure;
|
|
27
30
|
}
|
|
31
|
+
function refreshDashboardWorktreeProjection(host) {
|
|
32
|
+
host.invalidateDesktopStateSnapshot();
|
|
33
|
+
host.refreshLocalDashboardModel();
|
|
34
|
+
host.metadataServer?.notifyChange?.();
|
|
35
|
+
if (host.mode === "dashboard") {
|
|
36
|
+
host.renderDashboard();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function orderStatuslineItemsByWorktree(items, orderForWorktree) {
|
|
40
|
+
const grouped = new Map();
|
|
41
|
+
for (const item of items) {
|
|
42
|
+
const key = item.worktreePath ?? "__main__";
|
|
43
|
+
const group = grouped.get(key) ?? { worktreePath: item.worktreePath, items: [] };
|
|
44
|
+
group.items.push(item);
|
|
45
|
+
grouped.set(key, group);
|
|
46
|
+
}
|
|
47
|
+
return [...grouped.values()].flatMap((group) => orderForWorktree(group.items, group.worktreePath));
|
|
48
|
+
}
|
|
49
|
+
function exchangeTaskCounts() {
|
|
50
|
+
try {
|
|
51
|
+
const exchange = createRuntimeExchangeStore().read();
|
|
52
|
+
return {
|
|
53
|
+
pending: exchange.tasks.filter((task) => task.status === "pending").length,
|
|
54
|
+
assigned: exchange.tasks.filter((task) => task.status === "assigned" || task.status === "in_progress" || task.status === "blocked").length,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return { pending: 0, assigned: 0 };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
28
61
|
export const persistenceMethods = {
|
|
29
|
-
writeSessionsFile() {
|
|
30
|
-
const dir = getLocalAimuxDir();
|
|
31
|
-
if (!existsSync(dir))
|
|
32
|
-
mkdirSync(dir, { recursive: true });
|
|
33
|
-
const localSessions = this.sessions.map((s) => ({
|
|
34
|
-
id: s.id,
|
|
35
|
-
tool: s.command,
|
|
36
|
-
createdAt: s.startTime ? new Date(s.startTime).toISOString() : undefined,
|
|
37
|
-
backendSessionId: s.backendSessionId,
|
|
38
|
-
worktreePath: this.sessionWorktreePaths.get(s.id),
|
|
39
|
-
}));
|
|
40
|
-
const data = this.instanceDirectory.buildSessionsFileEntries(localSessions, this.instanceDirectory.getRemoteInstancesSafe(this.instanceId, process.cwd()));
|
|
41
|
-
writeFileSync(`${dir}/sessions.json`, JSON.stringify(data, null, 2) + "\n");
|
|
42
|
-
},
|
|
43
62
|
writeStatuslineFile(input) {
|
|
44
63
|
try {
|
|
45
64
|
if (this.mode !== "project-service")
|
|
@@ -48,6 +67,7 @@ export const persistenceMethods = {
|
|
|
48
67
|
for (const session of this.sessions) {
|
|
49
68
|
this.syncTmuxWindowMetadata(session.id);
|
|
50
69
|
}
|
|
70
|
+
this.dashboardUiStateStore.loadSharedState(this.dashboardState);
|
|
51
71
|
this.refreshDesktopStateSnapshot();
|
|
52
72
|
const dir = getProjectStateDir();
|
|
53
73
|
const filePath = join(dir, "statusline.json");
|
|
@@ -115,7 +135,7 @@ export const persistenceMethods = {
|
|
|
115
135
|
});
|
|
116
136
|
this.writeStatuslineTextFile("top-dashboard.txt", dashboardTop);
|
|
117
137
|
this.writeStatuslineTextFile("bottom-dashboard.txt", dashboardBottom);
|
|
118
|
-
for (const entry of data.sessions) {
|
|
138
|
+
for (const entry of [...data.sessions, ...(data.teammates ?? [])]) {
|
|
119
139
|
if (!entry.tmuxWindowId)
|
|
120
140
|
continue;
|
|
121
141
|
const renderOptions = {
|
|
@@ -139,7 +159,7 @@ export const persistenceMethods = {
|
|
|
139
159
|
project: basename(process.cwd()),
|
|
140
160
|
sessions: [],
|
|
141
161
|
metadata: {},
|
|
142
|
-
tasks:
|
|
162
|
+
tasks: exchangeTaskCounts(),
|
|
143
163
|
controlPlane: { daemonAlive: true, projectServiceAlive: false },
|
|
144
164
|
flash: null,
|
|
145
165
|
updatedAt: new Date().toISOString(),
|
|
@@ -154,11 +174,17 @@ export const persistenceMethods = {
|
|
|
154
174
|
},
|
|
155
175
|
buildStatuslineSnapshot() {
|
|
156
176
|
const desktopState = this.desktopStateSnapshot ?? this.buildDesktopStateSnapshot();
|
|
177
|
+
const orderedSessions = orderStatuslineItemsByWorktree(desktopState.sessions, (sessions, worktreePath) => this.dashboardUiStateStore.orderSessionsForWorktree(sessions, worktreePath));
|
|
178
|
+
const teammateSessions = this.dashboardPendingActions
|
|
179
|
+
.applyToSessions(desktopState.teammates ?? [], { includeTeammates: true })
|
|
180
|
+
.filter((session) => isTeammateSession(session));
|
|
181
|
+
const orderedTeammates = orderStatuslineItemsByWorktree(teammateSessions, (sessions, worktreePath) => this.dashboardUiStateStore.orderSessionsForWorktree(sessions, worktreePath));
|
|
182
|
+
const orderedServices = orderStatuslineItemsByWorktree(desktopState.services, (services, worktreePath) => this.dashboardUiStateStore.orderServicesForWorktree(services, worktreePath));
|
|
157
183
|
return {
|
|
158
184
|
project: basename(process.cwd()),
|
|
159
185
|
dashboardScreen: this.dashboardState.screen,
|
|
160
186
|
sessions: [
|
|
161
|
-
...
|
|
187
|
+
...orderedSessions.map((session) => ({
|
|
162
188
|
id: session.id,
|
|
163
189
|
kind: "agent",
|
|
164
190
|
tool: session.command,
|
|
@@ -173,7 +199,7 @@ export const persistenceMethods = {
|
|
|
173
199
|
worktreePath: session.worktreePath,
|
|
174
200
|
semantic: session.semantic,
|
|
175
201
|
})),
|
|
176
|
-
...
|
|
202
|
+
...orderedServices.map((service) => ({
|
|
177
203
|
id: service.id,
|
|
178
204
|
kind: "service",
|
|
179
205
|
tool: service.command,
|
|
@@ -185,9 +211,26 @@ export const persistenceMethods = {
|
|
|
185
211
|
status: service.status,
|
|
186
212
|
active: service.active,
|
|
187
213
|
worktreePath: service.worktreePath,
|
|
214
|
+
launchCommandLine: service.launchCommandLine,
|
|
188
215
|
})),
|
|
189
216
|
],
|
|
190
|
-
|
|
217
|
+
teammates: orderedTeammates.map((session) => ({
|
|
218
|
+
id: session.id,
|
|
219
|
+
kind: "agent",
|
|
220
|
+
tool: session.command,
|
|
221
|
+
label: session.label,
|
|
222
|
+
tmuxWindowId: session.tmuxWindowId,
|
|
223
|
+
tmuxWindowIndex: session.tmuxWindowIndex,
|
|
224
|
+
windowName: session.command,
|
|
225
|
+
headline: session.headline,
|
|
226
|
+
status: session.status,
|
|
227
|
+
role: session.role,
|
|
228
|
+
active: session.active,
|
|
229
|
+
worktreePath: session.worktreePath,
|
|
230
|
+
semantic: session.semantic,
|
|
231
|
+
team: session.team,
|
|
232
|
+
})),
|
|
233
|
+
tasks: exchangeTaskCounts(),
|
|
191
234
|
controlPlane: {
|
|
192
235
|
daemonAlive: Boolean(loadDaemonInfo()),
|
|
193
236
|
projectServiceAlive: true,
|
|
@@ -203,48 +246,33 @@ export const persistenceMethods = {
|
|
|
203
246
|
}
|
|
204
247
|
const desktopState = this.desktopStateSnapshot ?? this.buildDesktopStateSnapshot();
|
|
205
248
|
return {
|
|
206
|
-
sessions: desktopState.sessions,
|
|
207
|
-
|
|
249
|
+
sessions: this.dashboardPendingActions.applyToSessions(desktopState.sessions),
|
|
250
|
+
teammates: this.dashboardPendingActions
|
|
251
|
+
.applyToSessions(desktopState.teammates ?? [], { includeTeammates: true })
|
|
252
|
+
.filter((session) => isTeammateSession(session)),
|
|
253
|
+
services: this.dashboardPendingActions.applyToServices(desktopState.services),
|
|
208
254
|
statusline: this.buildStatuslineSnapshot(),
|
|
209
|
-
worktrees: desktopState.worktrees,
|
|
255
|
+
worktrees: this.dashboardPendingActions.applyToWorktrees(desktopState.worktrees),
|
|
210
256
|
operationFailures: desktopState.operationFailures,
|
|
211
257
|
mainCheckoutInfo: desktopState.mainCheckoutInfo,
|
|
212
258
|
mainCheckoutPath: desktopState.mainCheckoutPath,
|
|
213
259
|
};
|
|
214
260
|
},
|
|
215
261
|
reapplyDashboardPendingActions() {
|
|
216
|
-
this.dashboardSessionsCache = this.dashboardPendingActions.applyToSessions(this.dashboardSessionsCache.map(({ pendingAction: _pendingAction, optimistic: _optimistic, ...session }) => session));
|
|
217
|
-
this.
|
|
218
|
-
|
|
262
|
+
this.dashboardSessionsCache = this.dashboardPendingActions.applyToSessions(this.dashboardSessionsCache.map(({ pending: _pending, pendingAction: _pendingAction, optimistic: _optimistic, ...session }) => session));
|
|
263
|
+
this.dashboardTeammatesCache = this.dashboardPendingActions
|
|
264
|
+
.applyToSessions((this.dashboardTeammatesCache ?? []).map(({ pending: _pending, pendingAction: _pendingAction, optimistic: _optimistic, ...session }) => session), { includeTeammates: true })
|
|
265
|
+
.filter((session) => isTeammateSession(session));
|
|
266
|
+
this.dashboardServicesCache = this.dashboardPendingActions.applyToServices(this.dashboardServicesCache.map(({ pending: _pending, pendingAction: _pendingAction, optimistic: _optimistic, ...service }) => service));
|
|
267
|
+
this.dashboardWorktreeGroupsCache = this.dashboardUiStateStore.orderWorktreeGroups(composeDashboardWorktreeGroups(this.dashboardPendingActions.applyToWorktrees(this.dashboardWorktreeGroupsCache.map(({ pendingAction: _pendingAction, optimistic: _optimistic, pending: _pending, removing: _removing, ...wt }) => wt)), this.dashboardSessionsCache, this.dashboardServicesCache));
|
|
219
268
|
},
|
|
220
269
|
listDesktopWorktrees() {
|
|
221
|
-
const pendingCreates = this.pendingWorktreeCreates;
|
|
222
|
-
const pendingRemovals = this.pendingWorktreeRemovals;
|
|
223
270
|
const hiddenPaths = listWorktreeGraveyardPaths();
|
|
224
271
|
const worktrees = listAllWorktrees()
|
|
225
272
|
.filter((wt) => !wt.isBare && !hiddenPaths.has(wt.path) && !isToolInternalWorktree(wt))
|
|
226
273
|
.map((wt) => ({
|
|
227
274
|
...wt,
|
|
228
|
-
pending: pendingRemovals?.has(wt.path) ?? false,
|
|
229
|
-
removing: pendingRemovals?.has(wt.path) ?? false,
|
|
230
275
|
}));
|
|
231
|
-
if (pendingCreates?.size) {
|
|
232
|
-
for (const path of pendingCreates.keys()) {
|
|
233
|
-
if (worktrees.some((wt) => wt.path === path))
|
|
234
|
-
continue;
|
|
235
|
-
worktrees.push({
|
|
236
|
-
name: basename(path),
|
|
237
|
-
path,
|
|
238
|
-
branch: "(creating)",
|
|
239
|
-
isBare: false,
|
|
240
|
-
createdAt: this.worktreeCreateJob?.path === path
|
|
241
|
-
? new Date(this.worktreeCreateJob.startedAt).toISOString()
|
|
242
|
-
: undefined,
|
|
243
|
-
pending: true,
|
|
244
|
-
pendingAction: "creating",
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
276
|
const worktreePaths = new Set(worktrees.map((worktree) => worktree.path));
|
|
249
277
|
for (const failure of listDashboardOperationFailures()) {
|
|
250
278
|
if (failure.targetKind !== "worktree" || failure.operation !== "create" || !failure.worktreePath)
|
|
@@ -264,147 +292,76 @@ export const persistenceMethods = {
|
|
|
264
292
|
sortDesktopWorktrees(worktrees);
|
|
265
293
|
return worktrees;
|
|
266
294
|
},
|
|
295
|
+
listProjectedDesktopWorktrees() {
|
|
296
|
+
return this.dashboardPendingActions.applyToWorktrees(this.listDesktopWorktrees());
|
|
297
|
+
},
|
|
267
298
|
listWorktreeGraveyardEntries() {
|
|
268
299
|
return listWorktreeGraveyardEntriesImpl();
|
|
269
300
|
},
|
|
270
301
|
async graveyardDesktopWorktree(path) {
|
|
271
|
-
this.syncSessionsFromState();
|
|
272
302
|
const mainRepo = findMainRepo();
|
|
273
303
|
if (path === mainRepo) {
|
|
274
304
|
throw new Error("Cannot graveyard the main checkout");
|
|
275
305
|
}
|
|
276
|
-
const matching =
|
|
277
|
-
.filter((worktree) => !worktree.isBare)
|
|
278
|
-
.find((worktree) => worktree.path === path);
|
|
306
|
+
const matching = this.listDesktopWorktrees().find((worktree) => worktree.path === path);
|
|
279
307
|
if (!matching) {
|
|
280
308
|
throw new Error(`Worktree "${path}" not found`);
|
|
281
309
|
}
|
|
282
|
-
const
|
|
283
|
-
if (
|
|
284
|
-
throw new Error(`
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
createdAt: matching.createdAt,
|
|
301
|
-
graveyardedAt: new Date().toISOString(),
|
|
302
|
-
agents: attachedAgents,
|
|
303
|
-
services: attachedServices,
|
|
304
|
-
},
|
|
305
|
-
];
|
|
306
|
-
writeWorktreeGraveyardEntries(nextEntries);
|
|
307
|
-
this.worktreeGraveyardEntries = nextEntries;
|
|
308
|
-
this.offlineSessions = this.offlineSessions.filter((session) => session.worktreePath !== path);
|
|
309
|
-
this.saveState();
|
|
310
|
-
this.invalidateDesktopStateSnapshot();
|
|
311
|
-
this.refreshLocalDashboardModel();
|
|
310
|
+
const attachedSession = this.sessions?.find((session) => this.sessionWorktreePaths?.get(session.id) === path && this.isSessionRuntimeLive?.(session));
|
|
311
|
+
if (attachedSession) {
|
|
312
|
+
throw new Error(`Cannot graveyard "${matching.name}" while agent "${attachedSession.label || attachedSession.id}" is attached`);
|
|
313
|
+
}
|
|
314
|
+
stopWorktreeServicesForGraveyard(this, path);
|
|
315
|
+
upsertTopologyWorktree({
|
|
316
|
+
path,
|
|
317
|
+
name: matching.name,
|
|
318
|
+
branch: matching.branch,
|
|
319
|
+
createdAt: matching.createdAt,
|
|
320
|
+
}, "active");
|
|
321
|
+
const moved = moveTopologyWorktreeToGraveyard(path, { reason: "user-requested" });
|
|
322
|
+
if (!moved) {
|
|
323
|
+
throw new Error(`Unable to graveyard worktree "${path}"`);
|
|
324
|
+
}
|
|
325
|
+
this.saveState?.();
|
|
326
|
+
this.invalidateDesktopStateSnapshot?.();
|
|
327
|
+
this.refreshLocalDashboardModel?.();
|
|
312
328
|
this.metadataServer?.notifyChange?.();
|
|
313
|
-
if (this.mode === "dashboard") {
|
|
314
|
-
this.renderDashboard();
|
|
315
|
-
}
|
|
316
329
|
return { path, status: "graveyarded" };
|
|
317
330
|
},
|
|
318
331
|
async resurrectGraveyardWorktree(path) {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if (!entry) {
|
|
322
|
-
throw new Error(`Graveyard worktree "${path}" not found`);
|
|
323
|
-
}
|
|
324
|
-
const nextEntries = entries.filter((candidate) => candidate.path !== path);
|
|
325
|
-
writeWorktreeGraveyardEntries(nextEntries);
|
|
326
|
-
this.worktreeGraveyardEntries = nextEntries;
|
|
327
|
-
const flatAgents = takeFlatGraveyardAgentsForWorktree(path);
|
|
328
|
-
const seen = new Set(this.offlineSessions.map((session) => session.id));
|
|
329
|
-
for (const agent of [...entry.agents, ...flatAgents]) {
|
|
330
|
-
if (seen.has(agent.id))
|
|
331
|
-
continue;
|
|
332
|
-
this.offlineSessions.push(agent);
|
|
333
|
-
seen.add(agent.id);
|
|
332
|
+
if (!existsSync(path)) {
|
|
333
|
+
throw new Error(`Cannot resurrect worktree "${path}" because the checkout is missing`);
|
|
334
334
|
}
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
continue;
|
|
339
|
-
this.offlineServices.push(service);
|
|
340
|
-
serviceSeen.add(service.id);
|
|
335
|
+
const resurrected = resurrectTopologyWorktreeFromGraveyard(path);
|
|
336
|
+
if (!resurrected) {
|
|
337
|
+
throw new Error(`Graveyard worktree "${path}" not found`);
|
|
341
338
|
}
|
|
342
|
-
this.
|
|
343
|
-
this.
|
|
344
|
-
this.refreshLocalDashboardModel();
|
|
339
|
+
this.invalidateDesktopStateSnapshot?.();
|
|
340
|
+
this.refreshLocalDashboardModel?.();
|
|
345
341
|
this.metadataServer?.notifyChange?.();
|
|
346
|
-
|
|
347
|
-
this.renderDashboard();
|
|
348
|
-
}
|
|
349
|
-
return { path, status: "offline" };
|
|
342
|
+
return { path, status: "active" };
|
|
350
343
|
},
|
|
351
344
|
async deleteGraveyardWorktree(path) {
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
if (!entry) {
|
|
345
|
+
const existing = listTopologyWorktreeGraveyard().find((entry) => entry.path === path);
|
|
346
|
+
if (!existing) {
|
|
355
347
|
throw new Error(`Graveyard worktree "${path}" not found`);
|
|
356
348
|
}
|
|
357
349
|
const mainRepo = findMainRepo();
|
|
358
|
-
if (path
|
|
359
|
-
|
|
360
|
-
await removeOrphanedDesktopWorktree(this, mainRepo, path);
|
|
361
|
-
}
|
|
362
|
-
else {
|
|
363
|
-
await new Promise((resolve, reject) => {
|
|
364
|
-
let stderr = "";
|
|
365
|
-
let child;
|
|
366
|
-
try {
|
|
367
|
-
child = spawn("git", ["worktree", "remove", path, "--force"], {
|
|
368
|
-
cwd: mainRepo,
|
|
369
|
-
stdio: ["ignore", "ignore", "pipe"],
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
catch (error) {
|
|
373
|
-
reject(error);
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
child.stderr.on("data", (chunk) => {
|
|
377
|
-
stderr += chunk.toString();
|
|
378
|
-
});
|
|
379
|
-
child.on("error", reject);
|
|
380
|
-
child.on("close", (code) => {
|
|
381
|
-
if (code === 0) {
|
|
382
|
-
resolve();
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
const detail = stderr
|
|
386
|
-
.split("\n")
|
|
387
|
-
.map((line) => line.trim())
|
|
388
|
-
.filter(Boolean)
|
|
389
|
-
.at(-1);
|
|
390
|
-
reject(new Error(detail || `git worktree remove exited with code ${code ?? 1}`));
|
|
391
|
-
});
|
|
392
|
-
});
|
|
393
|
-
}
|
|
350
|
+
if (path === mainRepo) {
|
|
351
|
+
throw new Error("Cannot remove the main checkout");
|
|
394
352
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
this.worktreeGraveyardEntries = nextEntries;
|
|
398
|
-
this.offlineSessions = this.offlineSessions.filter((session) => session.worktreePath !== path);
|
|
399
|
-
this.offlineServices = this.offlineServices.filter((service) => service.worktreePath !== path);
|
|
400
|
-
removeFlatGraveyardAgentsForWorktree(path);
|
|
401
|
-
this.saveState();
|
|
402
|
-
this.invalidateDesktopStateSnapshot();
|
|
403
|
-
this.refreshLocalDashboardModel();
|
|
404
|
-
this.metadataServer?.notifyChange?.();
|
|
405
|
-
if (this.mode === "dashboard") {
|
|
406
|
-
this.renderDashboard();
|
|
353
|
+
if (existsSync(path)) {
|
|
354
|
+
await removeGraveyardedDesktopWorktree(this, mainRepo, path);
|
|
407
355
|
}
|
|
356
|
+
else {
|
|
357
|
+
removeWorktreeDependents(this, path);
|
|
358
|
+
removeTopologyWorktree(path);
|
|
359
|
+
this.saveState?.();
|
|
360
|
+
}
|
|
361
|
+
deleteTopologyWorktreeGraveyardEntry(path);
|
|
362
|
+
this.invalidateDesktopStateSnapshot?.();
|
|
363
|
+
this.refreshLocalDashboardModel?.();
|
|
364
|
+
this.metadataServer?.notifyChange?.();
|
|
408
365
|
return { path, status: "removed" };
|
|
409
366
|
},
|
|
410
367
|
createDesktopWorktree(name) {
|
|
@@ -425,6 +382,18 @@ export const persistenceMethods = {
|
|
|
425
382
|
rejectCreate = reject;
|
|
426
383
|
});
|
|
427
384
|
pendingCreates.set(targetPath, createPromise);
|
|
385
|
+
const createStartedAt = Date.now();
|
|
386
|
+
this.worktreeCreateJob = {
|
|
387
|
+
path: targetPath,
|
|
388
|
+
name,
|
|
389
|
+
startedAt: createStartedAt,
|
|
390
|
+
};
|
|
391
|
+
upsertTopologyWorktree({
|
|
392
|
+
path: targetPath,
|
|
393
|
+
name,
|
|
394
|
+
branch: name,
|
|
395
|
+
createdAt: new Date(createStartedAt).toISOString(),
|
|
396
|
+
}, "creating");
|
|
428
397
|
const clearPendingCreate = () => {
|
|
429
398
|
if (pendingCreates.get(targetPath) === createPromise) {
|
|
430
399
|
pendingCreates.delete(targetPath);
|
|
@@ -433,7 +402,17 @@ export const persistenceMethods = {
|
|
|
433
402
|
this.worktreeCreateJob = null;
|
|
434
403
|
}
|
|
435
404
|
};
|
|
436
|
-
this.dashboardPendingActions.
|
|
405
|
+
this.dashboardPendingActions.setWorktreeAction(targetPath, "creating", {
|
|
406
|
+
worktreeSeed: {
|
|
407
|
+
name,
|
|
408
|
+
branch: name,
|
|
409
|
+
path: targetPath,
|
|
410
|
+
createdAt: new Date(createStartedAt).toISOString(),
|
|
411
|
+
status: "offline",
|
|
412
|
+
isBare: false,
|
|
413
|
+
sessions: [],
|
|
414
|
+
services: [],
|
|
415
|
+
},
|
|
437
416
|
timeoutMs: 180_000,
|
|
438
417
|
onTimeout: () => {
|
|
439
418
|
clearPendingCreate();
|
|
@@ -455,11 +434,6 @@ export const persistenceMethods = {
|
|
|
455
434
|
}
|
|
456
435
|
},
|
|
457
436
|
});
|
|
458
|
-
this.worktreeCreateJob = {
|
|
459
|
-
path: targetPath,
|
|
460
|
-
name,
|
|
461
|
-
startedAt: Date.now(),
|
|
462
|
-
};
|
|
463
437
|
this.invalidateDesktopStateSnapshot();
|
|
464
438
|
this.refreshLocalDashboardModel();
|
|
465
439
|
if (this.mode === "dashboard") {
|
|
@@ -498,9 +472,23 @@ export const persistenceMethods = {
|
|
|
498
472
|
reject(new Error(detail || `git worktree add exited with code ${code ?? 1}`));
|
|
499
473
|
});
|
|
500
474
|
});
|
|
475
|
+
upsertTopologyWorktree({
|
|
476
|
+
path: targetPath,
|
|
477
|
+
name,
|
|
478
|
+
branch: name,
|
|
479
|
+
basePath: mainRepo,
|
|
480
|
+
createdAt: new Date(createStartedAt).toISOString(),
|
|
481
|
+
}, "active");
|
|
501
482
|
resolveCreate({ path: targetPath, status: "created" });
|
|
502
483
|
}
|
|
503
484
|
catch (error) {
|
|
485
|
+
upsertTopologyWorktree({
|
|
486
|
+
path: targetPath,
|
|
487
|
+
name,
|
|
488
|
+
branch: name,
|
|
489
|
+
createdAt: new Date(createStartedAt).toISOString(),
|
|
490
|
+
operationFailure: error instanceof Error ? error.message : String(error),
|
|
491
|
+
}, "error");
|
|
504
492
|
rejectCreate(error);
|
|
505
493
|
}
|
|
506
494
|
})();
|
|
@@ -528,7 +516,7 @@ export const persistenceMethods = {
|
|
|
528
516
|
});
|
|
529
517
|
const finalizeCreate = () => {
|
|
530
518
|
clearPendingCreate();
|
|
531
|
-
this.dashboardPendingActions.
|
|
519
|
+
this.dashboardPendingActions.clearWorktreeAction(targetPath);
|
|
532
520
|
this.invalidateDesktopStateSnapshot();
|
|
533
521
|
this.refreshLocalDashboardModel();
|
|
534
522
|
this.metadataServer?.notifyChange?.();
|
|
@@ -541,8 +529,30 @@ export const persistenceMethods = {
|
|
|
541
529
|
},
|
|
542
530
|
async removeDesktopWorktree(path) {
|
|
543
531
|
const pendingRemovals = this.pendingWorktreeRemovals;
|
|
532
|
+
const setRemovePending = () => {
|
|
533
|
+
this.dashboardPendingActions.setWorktreeAction(path, "removing", {
|
|
534
|
+
timeoutMs: 180_000,
|
|
535
|
+
onTimeout: () => {
|
|
536
|
+
const name = path.split("/").pop() ?? path;
|
|
537
|
+
const message = `Timed out removing worktree "${name}"`;
|
|
538
|
+
recordDashboardFailure(this, {
|
|
539
|
+
targetKind: "worktree",
|
|
540
|
+
operation: "remove",
|
|
541
|
+
title: `Failed to remove worktree "${name}"`,
|
|
542
|
+
message,
|
|
543
|
+
worktreePath: path,
|
|
544
|
+
worktreeName: name,
|
|
545
|
+
});
|
|
546
|
+
this.footerFlash = message;
|
|
547
|
+
this.footerFlashTicks = 5;
|
|
548
|
+
refreshDashboardWorktreeProjection(this);
|
|
549
|
+
},
|
|
550
|
+
});
|
|
551
|
+
refreshDashboardWorktreeProjection(this);
|
|
552
|
+
};
|
|
544
553
|
const existingRemoval = pendingRemovals.get(path);
|
|
545
554
|
if (existingRemoval) {
|
|
555
|
+
setRemovePending();
|
|
546
556
|
return existingRemoval;
|
|
547
557
|
}
|
|
548
558
|
let resolveRemoval;
|
|
@@ -552,42 +562,18 @@ export const persistenceMethods = {
|
|
|
552
562
|
rejectRemoval = reject;
|
|
553
563
|
});
|
|
554
564
|
pendingRemovals.set(path, removalPromise);
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
onTimeout: () => {
|
|
558
|
-
const name = path.split("/").pop() ?? path;
|
|
559
|
-
const message = `Timed out removing worktree "${name}"`;
|
|
560
|
-
recordDashboardFailure(this, {
|
|
561
|
-
targetKind: "worktree",
|
|
562
|
-
operation: "remove",
|
|
563
|
-
title: `Failed to remove worktree "${name}"`,
|
|
564
|
-
message,
|
|
565
|
-
worktreePath: path,
|
|
566
|
-
worktreeName: name,
|
|
567
|
-
});
|
|
568
|
-
this.footerFlash = message;
|
|
569
|
-
this.footerFlashTicks = 5;
|
|
570
|
-
this.invalidateDesktopStateSnapshot();
|
|
571
|
-
this.refreshLocalDashboardModel();
|
|
572
|
-
if (this.mode === "dashboard") {
|
|
573
|
-
this.renderDashboard();
|
|
574
|
-
}
|
|
575
|
-
},
|
|
576
|
-
});
|
|
577
|
-
this.invalidateDesktopStateSnapshot();
|
|
578
|
-
this.refreshLocalDashboardModel();
|
|
579
|
-
if (this.mode === "dashboard") {
|
|
580
|
-
this.renderDashboard();
|
|
581
|
-
}
|
|
565
|
+
setRemovePending();
|
|
566
|
+
let startedRemoval = false;
|
|
582
567
|
void (async () => {
|
|
583
568
|
try {
|
|
584
|
-
this.
|
|
569
|
+
this.syncSessionsFromTopology();
|
|
585
570
|
const mainRepo = findMainRepo();
|
|
586
571
|
if (path === mainRepo) {
|
|
587
572
|
throw new Error("Cannot remove the main checkout");
|
|
588
573
|
}
|
|
589
574
|
if (!existsSync(path)) {
|
|
590
575
|
await removeOrphanedDesktopWorktree(this, mainRepo, path);
|
|
576
|
+
removeTopologyWorktree(path);
|
|
591
577
|
clearDashboardOperationFailures({ targetKind: "worktree", operation: "remove", worktreePath: path });
|
|
592
578
|
resolveRemoval({ path, status: "removed" });
|
|
593
579
|
return;
|
|
@@ -597,16 +583,24 @@ export const persistenceMethods = {
|
|
|
597
583
|
const worktreeBaseDir = getWorktreeBaseDir();
|
|
598
584
|
if (path.startsWith(`${worktreeBaseDir}/`) || path === worktreeBaseDir) {
|
|
599
585
|
await removeOrphanedDesktopWorktree(this, mainRepo, path);
|
|
586
|
+
removeTopologyWorktree(path);
|
|
600
587
|
clearDashboardOperationFailures({ targetKind: "worktree", operation: "remove", worktreePath: path });
|
|
601
588
|
resolveRemoval({ path, status: "removed" });
|
|
602
589
|
return;
|
|
603
590
|
}
|
|
604
591
|
throw new Error(`Worktree "${path}" not found`);
|
|
605
592
|
}
|
|
606
|
-
const attachedSession = this.sessions.find((session) => session.
|
|
593
|
+
const attachedSession = this.sessions.find((session) => this.sessionWorktreePaths?.get(session.id) === path && this.isSessionRuntimeLive(session));
|
|
607
594
|
if (attachedSession) {
|
|
608
595
|
throw new Error(`Cannot remove "${matching.name}" while agent "${attachedSession.label || attachedSession.id}" is attached`);
|
|
609
596
|
}
|
|
597
|
+
upsertTopologyWorktree({
|
|
598
|
+
path,
|
|
599
|
+
name: matching.name,
|
|
600
|
+
branch: matching.branch,
|
|
601
|
+
createdAt: matching.createdAt,
|
|
602
|
+
}, "removing");
|
|
603
|
+
startedRemoval = true;
|
|
610
604
|
detachWorktreeServices(this, path);
|
|
611
605
|
await new Promise((resolve, reject) => {
|
|
612
606
|
let stderr = "";
|
|
@@ -645,14 +639,20 @@ export const persistenceMethods = {
|
|
|
645
639
|
reject(new Error(detail || `git worktree remove exited with code ${code ?? 1}`));
|
|
646
640
|
});
|
|
647
641
|
});
|
|
648
|
-
this
|
|
649
|
-
|
|
650
|
-
removeFlatGraveyardAgentsForWorktree(path);
|
|
642
|
+
removeWorktreeDependents(this, path);
|
|
643
|
+
removeTopologyWorktree(path);
|
|
651
644
|
this.saveState();
|
|
652
645
|
clearDashboardOperationFailures({ targetKind: "worktree", operation: "remove", worktreePath: path });
|
|
653
646
|
resolveRemoval({ path, status: "removed" });
|
|
654
647
|
}
|
|
655
648
|
catch (error) {
|
|
649
|
+
if (startedRemoval) {
|
|
650
|
+
upsertTopologyWorktree({
|
|
651
|
+
path,
|
|
652
|
+
name: path.split("/").pop() ?? path,
|
|
653
|
+
operationFailure: error instanceof Error ? error.message : String(error),
|
|
654
|
+
}, "error");
|
|
655
|
+
}
|
|
656
656
|
rejectRemoval(error);
|
|
657
657
|
}
|
|
658
658
|
})();
|
|
@@ -671,55 +671,39 @@ export const persistenceMethods = {
|
|
|
671
671
|
this.footerFlashTicks = 5;
|
|
672
672
|
});
|
|
673
673
|
const finalizeRemoval = () => {
|
|
674
|
-
this.dashboardPendingActions.
|
|
674
|
+
this.dashboardPendingActions.clearWorktreeAction(path);
|
|
675
675
|
pendingRemovals.delete(path);
|
|
676
|
-
this
|
|
677
|
-
this.refreshLocalDashboardModel();
|
|
678
|
-
this.metadataServer?.notifyChange?.();
|
|
679
|
-
if (this.mode === "dashboard") {
|
|
680
|
-
this.renderDashboard();
|
|
681
|
-
}
|
|
676
|
+
refreshDashboardWorktreeProjection(this);
|
|
682
677
|
};
|
|
683
678
|
void removalPromise.then(finalizeRemoval, finalizeRemoval);
|
|
684
679
|
return removalPromise;
|
|
685
680
|
},
|
|
686
681
|
listGraveyardEntries() {
|
|
687
|
-
|
|
688
|
-
const content = readFileSync(getGraveyardPath(), "utf-8");
|
|
689
|
-
return JSON.parse(content);
|
|
690
|
-
}
|
|
691
|
-
catch {
|
|
692
|
-
return [];
|
|
693
|
-
}
|
|
682
|
+
return listTopologySessionStates({ statuses: ["graveyard"] });
|
|
694
683
|
},
|
|
695
684
|
async resurrectGraveyardSession(sessionId) {
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
const entry = graveyardEntries.find((candidate) => candidate.id === sessionId);
|
|
699
|
-
if (!entry) {
|
|
685
|
+
const restored = resurrectTopologySession(sessionId);
|
|
686
|
+
if (!restored) {
|
|
700
687
|
throw new Error(`Graveyard session "${sessionId}" not found`);
|
|
701
688
|
}
|
|
702
|
-
const
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
689
|
+
const offlineEntry = { ...restored, lifecycle: "offline", status: "offline" };
|
|
690
|
+
if (Array.isArray(this.offlineSessions)) {
|
|
691
|
+
const existingIndex = this.offlineSessions.findIndex((session) => session.id === sessionId);
|
|
692
|
+
if (existingIndex >= 0) {
|
|
693
|
+
this.offlineSessions[existingIndex] = { ...this.offlineSessions[existingIndex], ...offlineEntry };
|
|
694
|
+
}
|
|
695
|
+
else {
|
|
696
|
+
this.offlineSessions.push(offlineEntry);
|
|
710
697
|
}
|
|
711
|
-
state.sessions.push(entry);
|
|
712
|
-
writeFileSync(statePath, JSON.stringify(state, null, 2) + "\n");
|
|
713
698
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
unlinkSync(`${getLocalAimuxDir()}/sessions.json`);
|
|
699
|
+
this.loadOfflineTopologySessions?.();
|
|
700
|
+
this.invalidateDesktopStateSnapshot?.();
|
|
701
|
+
this.writeStatuslineFile?.();
|
|
702
|
+
this.metadataServer?.notifyChange?.();
|
|
703
|
+
if (this.mode === "dashboard") {
|
|
704
|
+
this.renderCurrentDashboardView?.();
|
|
721
705
|
}
|
|
722
|
-
|
|
706
|
+
return { sessionId, status: "offline" };
|
|
723
707
|
},
|
|
724
708
|
stripAnsi(text) {
|
|
725
709
|
return text.replace(/\u001B\[[0-9;]*m/g, "");
|
|
@@ -764,57 +748,53 @@ function sortDesktopWorktrees(worktrees) {
|
|
|
764
748
|
return a.name.localeCompare(b.name);
|
|
765
749
|
});
|
|
766
750
|
}
|
|
767
|
-
function collectWorktreeAgents(host, path) {
|
|
768
|
-
const byId = new Map();
|
|
769
|
-
for (const session of host.offlineSessions) {
|
|
770
|
-
if (session.worktreePath !== path)
|
|
771
|
-
continue;
|
|
772
|
-
byId.set(session.id, session);
|
|
773
|
-
}
|
|
774
|
-
return [...byId.values()].sort((a, b) => {
|
|
775
|
-
const aTime = a.createdAt ? Date.parse(a.createdAt) : 0;
|
|
776
|
-
const bTime = b.createdAt ? Date.parse(b.createdAt) : 0;
|
|
777
|
-
return bTime - aTime || a.id.localeCompare(b.id);
|
|
778
|
-
});
|
|
779
|
-
}
|
|
780
|
-
function collectWorktreeServices(host, path) {
|
|
781
|
-
const byId = new Map();
|
|
782
|
-
for (const service of host.offlineServices ?? []) {
|
|
783
|
-
if (service.worktreePath !== path)
|
|
784
|
-
continue;
|
|
785
|
-
byId.set(service.id, service);
|
|
786
|
-
}
|
|
787
|
-
for (const service of host.buildLiveServiceStates?.() ?? []) {
|
|
788
|
-
if (service.worktreePath !== path)
|
|
789
|
-
continue;
|
|
790
|
-
byId.set(service.id, service);
|
|
791
|
-
}
|
|
792
|
-
return [...byId.values()].sort((a, b) => {
|
|
793
|
-
const aTime = a.createdAt ? Date.parse(a.createdAt) : 0;
|
|
794
|
-
const bTime = b.createdAt ? Date.parse(b.createdAt) : 0;
|
|
795
|
-
return bTime - aTime || a.id.localeCompare(b.id);
|
|
796
|
-
});
|
|
797
|
-
}
|
|
798
|
-
async function waitForWorktreeSessionsToStop(host, path, timeoutMs = 10_000) {
|
|
799
|
-
const deadline = Date.now() + timeoutMs;
|
|
800
|
-
while (Date.now() < deadline) {
|
|
801
|
-
const remaining = host.sessions.some((session) => host.sessionWorktreePaths.get(session.id) === path && host.isSessionRuntimeLive(session) && !session.exited);
|
|
802
|
-
if (!remaining)
|
|
803
|
-
return;
|
|
804
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
805
|
-
}
|
|
806
|
-
throw new Error(`Timed out offlining agents for worktree "${basename(path)}"`);
|
|
807
|
-
}
|
|
808
751
|
async function removeOrphanedDesktopWorktree(host, mainRepo, path) {
|
|
809
752
|
await pruneGitWorktrees(mainRepo);
|
|
810
753
|
if (existsSync(path)) {
|
|
811
754
|
rmSync(path, { recursive: true, force: true });
|
|
812
755
|
}
|
|
813
756
|
await pruneGitWorktrees(mainRepo);
|
|
814
|
-
host
|
|
815
|
-
host.offlineServices = host.offlineServices.filter((service) => service.worktreePath !== path);
|
|
757
|
+
removeWorktreeDependents(host, path);
|
|
816
758
|
host.saveState();
|
|
817
759
|
}
|
|
760
|
+
async function removeGraveyardedDesktopWorktree(host, mainRepo, path) {
|
|
761
|
+
await removeGitWorktreeCheckout(mainRepo, path);
|
|
762
|
+
removeWorktreeDependents(host, path);
|
|
763
|
+
removeTopologyWorktree(path);
|
|
764
|
+
host.saveState?.();
|
|
765
|
+
}
|
|
766
|
+
async function removeGitWorktreeCheckout(mainRepo, path) {
|
|
767
|
+
await new Promise((resolve, reject) => {
|
|
768
|
+
let stderr = "";
|
|
769
|
+
let child;
|
|
770
|
+
try {
|
|
771
|
+
child = spawn("git", ["worktree", "remove", path, "--force"], {
|
|
772
|
+
cwd: mainRepo,
|
|
773
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
catch (error) {
|
|
777
|
+
reject(error);
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
child.stderr.on("data", (chunk) => {
|
|
781
|
+
stderr += chunk.toString();
|
|
782
|
+
});
|
|
783
|
+
child.on("error", reject);
|
|
784
|
+
child.on("close", (code) => {
|
|
785
|
+
if (code === 0) {
|
|
786
|
+
resolve();
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
const detail = stderr
|
|
790
|
+
.split("\n")
|
|
791
|
+
.map((line) => line.trim())
|
|
792
|
+
.filter(Boolean)
|
|
793
|
+
.at(-1);
|
|
794
|
+
reject(new Error(detail || `git worktree remove exited with code ${code ?? 1}`));
|
|
795
|
+
});
|
|
796
|
+
});
|
|
797
|
+
}
|
|
818
798
|
function detachWorktreeServices(host, path) {
|
|
819
799
|
for (const { target, metadata } of host.tmuxRuntimeManager.listProjectManagedWindows(process.cwd())) {
|
|
820
800
|
if (metadata.kind !== "service" || metadata.worktreePath !== path)
|
|
@@ -826,6 +806,44 @@ function detachWorktreeServices(host, path) {
|
|
|
826
806
|
catch { }
|
|
827
807
|
}
|
|
828
808
|
host.offlineServices = host.offlineServices.filter((service) => service.worktreePath !== path);
|
|
809
|
+
removeTopologyServicesForWorktree(path);
|
|
810
|
+
removePersistedServicesForWorktree(path);
|
|
811
|
+
}
|
|
812
|
+
function stopWorktreeServicesForGraveyard(host, path) {
|
|
813
|
+
for (const { target, metadata } of host.tmuxRuntimeManager.listProjectManagedWindows(process.cwd())) {
|
|
814
|
+
if (metadata.kind !== "service" || metadata.worktreePath !== path)
|
|
815
|
+
continue;
|
|
816
|
+
markLifecycleUsed(host, metadata.sessionId);
|
|
817
|
+
try {
|
|
818
|
+
host.tmuxRuntimeManager.killWindow(target);
|
|
819
|
+
}
|
|
820
|
+
catch { }
|
|
821
|
+
const serviceState = {
|
|
822
|
+
id: metadata.sessionId,
|
|
823
|
+
command: metadata.command,
|
|
824
|
+
args: metadata.args ?? [],
|
|
825
|
+
launchCommandLine: metadata.launchCommandLine,
|
|
826
|
+
worktreePath: metadata.worktreePath,
|
|
827
|
+
cwd: metadata.worktreePath,
|
|
828
|
+
label: metadata.label,
|
|
829
|
+
createdAt: metadata.createdAt,
|
|
830
|
+
};
|
|
831
|
+
upsertTopologyService(serviceState, "stopped");
|
|
832
|
+
host.offlineServices ??= [];
|
|
833
|
+
const existingIndex = host.offlineServices.findIndex((service) => service.id === metadata.sessionId);
|
|
834
|
+
if (existingIndex >= 0) {
|
|
835
|
+
host.offlineServices[existingIndex] = { ...host.offlineServices[existingIndex], ...serviceState };
|
|
836
|
+
}
|
|
837
|
+
else {
|
|
838
|
+
host.offlineServices.push(serviceState);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
function removeWorktreeDependents(host, path) {
|
|
843
|
+
host.offlineSessions = (host.offlineSessions ?? []).filter((session) => session.worktreePath !== path);
|
|
844
|
+
host.offlineServices = (host.offlineServices ?? []).filter((service) => service.worktreePath !== path);
|
|
845
|
+
removeTopologySessionsForWorktree(path);
|
|
846
|
+
removeTopologyServicesForWorktree(path);
|
|
829
847
|
removePersistedServicesForWorktree(path);
|
|
830
848
|
}
|
|
831
849
|
function removePersistedServicesForWorktree(path) {
|
|
@@ -856,26 +874,3 @@ function markLifecycleUsed(host, itemId) {
|
|
|
856
874
|
}
|
|
857
875
|
catch { }
|
|
858
876
|
}
|
|
859
|
-
function takeFlatGraveyardAgentsForWorktree(path) {
|
|
860
|
-
const graveyardPath = getGraveyardPath();
|
|
861
|
-
if (!existsSync(graveyardPath))
|
|
862
|
-
return [];
|
|
863
|
-
try {
|
|
864
|
-
const entries = JSON.parse(readFileSync(graveyardPath, "utf-8"));
|
|
865
|
-
if (!Array.isArray(entries))
|
|
866
|
-
return [];
|
|
867
|
-
const matching = entries.filter((entry) => entry?.worktreePath === path);
|
|
868
|
-
const remaining = entries.filter((entry) => entry?.worktreePath !== path);
|
|
869
|
-
if (matching.length > 0) {
|
|
870
|
-
writeFileSync(graveyardPath, JSON.stringify(remaining, null, 2) + "\n");
|
|
871
|
-
}
|
|
872
|
-
return matching;
|
|
873
|
-
}
|
|
874
|
-
catch {
|
|
875
|
-
return [];
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
function removeFlatGraveyardAgentsForWorktree(path) {
|
|
879
|
-
void takeFlatGraveyardAgentsForWorktree(path);
|
|
880
|
-
}
|
|
881
|
-
//# sourceMappingURL=persistence-methods.js.map
|