aimux-cli 0.1.16 → 0.1.18
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/alert-display.d.ts +21 -0
- package/dist/alert-display.js +86 -0
- package/dist/alert-display.js.map +1 -0
- package/dist/attachment-store.d.ts +0 -7
- package/dist/attachment-store.js +2 -86
- package/dist/attachment-store.js.map +1 -1
- package/dist/builtin-metadata-watchers.js +4 -4
- package/dist/builtin-metadata-watchers.js.map +1 -1
- package/dist/claude-hooks.d.ts +1 -0
- package/dist/claude-hooks.js +25 -0
- package/dist/claude-hooks.js.map +1 -1
- package/dist/config.d.ts +19 -13
- package/dist/config.js +28 -14
- package/dist/config.js.map +1 -1
- package/dist/connection-targets.d.ts +8 -0
- package/dist/connection-targets.js +28 -0
- package/dist/connection-targets.js.map +1 -0
- package/dist/credentials.d.ts +12 -0
- package/dist/credentials.js +49 -0
- package/dist/credentials.js.map +1 -0
- package/dist/daemon.d.ts +23 -0
- package/dist/daemon.js +391 -66
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/index.d.ts +13 -10
- package/dist/dashboard/index.js +3 -26
- package/dist/dashboard/index.js.map +1 -1
- package/dist/dashboard/order.d.ts +22 -0
- package/dist/dashboard/order.js +55 -0
- package/dist/dashboard/order.js.map +1 -0
- package/dist/dashboard/pending-actions.d.ts +39 -10
- package/dist/dashboard/pending-actions.js +166 -36
- package/dist/dashboard/pending-actions.js.map +1 -1
- package/dist/dashboard/quick-jump.d.ts +2 -1
- package/dist/dashboard/quick-jump.js +7 -4
- package/dist/dashboard/quick-jump.js.map +1 -1
- package/dist/dashboard/session-actions.d.ts +4 -4
- package/dist/dashboard/session-actions.js +1 -1
- package/dist/dashboard/session-actions.js.map +1 -1
- package/dist/dashboard/session-registry.d.ts +4 -3
- package/dist/dashboard/session-registry.js +16 -50
- package/dist/dashboard/session-registry.js.map +1 -1
- package/dist/dashboard/state.d.ts +1 -1
- package/dist/dashboard/state.js.map +1 -1
- package/dist/dashboard/ui-state-store.d.ts +16 -1
- package/dist/dashboard/ui-state-store.js +73 -2
- package/dist/dashboard/ui-state-store.js.map +1 -1
- package/dist/debug-state.d.ts +97 -0
- package/dist/debug-state.js +541 -0
- package/dist/debug-state.js.map +1 -0
- package/dist/debug.d.ts +38 -0
- package/dist/debug.js +219 -15
- package/dist/debug.js.map +1 -1
- package/dist/default-plugins/gh-pr-context.d.ts +2 -1
- package/dist/default-plugins/gh-pr-context.js +17 -11
- package/dist/default-plugins/gh-pr-context.js.map +1 -1
- package/dist/default-plugins/transcript-length.js +15 -2
- package/dist/default-plugins/transcript-length.js.map +1 -1
- package/dist/fast-control.js +37 -19
- package/dist/fast-control.js.map +1 -1
- package/dist/http-client.js +31 -2
- package/dist/http-client.js.map +1 -1
- package/dist/local-ui-server.d.ts +22 -0
- package/dist/local-ui-server.js +186 -0
- package/dist/local-ui-server.js.map +1 -0
- package/dist/login-flow.d.ts +7 -0
- package/dist/login-flow.js +120 -0
- package/dist/login-flow.js.map +1 -0
- package/dist/main.js +821 -151
- package/dist/main.js.map +1 -1
- package/dist/managed-launch-env.js +14 -0
- package/dist/managed-launch-env.js.map +1 -1
- package/dist/metadata-server.d.ts +36 -36
- package/dist/metadata-server.js +638 -137
- package/dist/metadata-server.js.map +1 -1
- package/dist/metadata-store.d.ts +4 -1
- package/dist/metadata-store.js +30 -2
- package/dist/metadata-store.js.map +1 -1
- package/dist/multiplexer/agent-io-methods.d.ts +2 -10
- package/dist/multiplexer/agent-io-methods.js +12 -43
- package/dist/multiplexer/agent-io-methods.js.map +1 -1
- package/dist/multiplexer/archives.js +8 -9
- package/dist/multiplexer/archives.js.map +1 -1
- package/dist/multiplexer/dashboard-control.js +45 -13
- package/dist/multiplexer/dashboard-control.js.map +1 -1
- package/dist/multiplexer/dashboard-interaction.d.ts +8 -2
- package/dist/multiplexer/dashboard-interaction.js +187 -28
- package/dist/multiplexer/dashboard-interaction.js.map +1 -1
- package/dist/multiplexer/dashboard-model.d.ts +10 -3
- package/dist/multiplexer/dashboard-model.js +417 -35
- package/dist/multiplexer/dashboard-model.js.map +1 -1
- package/dist/multiplexer/dashboard-ops.d.ts +9 -7
- package/dist/multiplexer/dashboard-ops.js +178 -68
- package/dist/multiplexer/dashboard-ops.js.map +1 -1
- package/dist/multiplexer/dashboard-state-methods.d.ts +2 -1
- package/dist/multiplexer/dashboard-state-methods.js +3 -2
- package/dist/multiplexer/dashboard-state-methods.js.map +1 -1
- package/dist/multiplexer/dashboard-tail-methods.d.ts +22 -10
- package/dist/multiplexer/dashboard-tail-methods.js +164 -47
- package/dist/multiplexer/dashboard-tail-methods.js.map +1 -1
- package/dist/multiplexer/dashboard-view-methods.d.ts +1 -1
- package/dist/multiplexer/dashboard-view-methods.js +23 -8
- package/dist/multiplexer/dashboard-view-methods.js.map +1 -1
- package/dist/multiplexer/graveyard-view-model.d.ts +9 -1
- package/dist/multiplexer/graveyard-view-model.js +39 -0
- package/dist/multiplexer/graveyard-view-model.js.map +1 -1
- package/dist/multiplexer/index.d.ts +15 -12
- package/dist/multiplexer/index.js +64 -43
- package/dist/multiplexer/index.js.map +1 -1
- package/dist/multiplexer/notifications.js +107 -24
- package/dist/multiplexer/notifications.js.map +1 -1
- package/dist/multiplexer/persistence-methods.d.ts +31 -4
- package/dist/multiplexer/persistence-methods.js +304 -308
- package/dist/multiplexer/persistence-methods.js.map +1 -1
- package/dist/multiplexer/runtime-lifecycle-methods.d.ts +8 -10
- package/dist/multiplexer/runtime-lifecycle-methods.js +104 -86
- package/dist/multiplexer/runtime-lifecycle-methods.js.map +1 -1
- package/dist/multiplexer/runtime-state.d.ts +8 -10
- package/dist/multiplexer/runtime-state.js +82 -145
- package/dist/multiplexer/runtime-state.js.map +1 -1
- package/dist/multiplexer/runtime-sync.d.ts +2 -10
- package/dist/multiplexer/runtime-sync.js +3 -18
- package/dist/multiplexer/runtime-sync.js.map +1 -1
- package/dist/multiplexer/service-state-snapshot.d.ts +2 -4
- package/dist/multiplexer/service-state-snapshot.js +4 -51
- package/dist/multiplexer/service-state-snapshot.js.map +1 -1
- package/dist/multiplexer/services.d.ts +1 -0
- package/dist/multiplexer/services.js +55 -5
- package/dist/multiplexer/services.js.map +1 -1
- package/dist/multiplexer/session-capture.d.ts +1 -0
- package/dist/multiplexer/session-capture.js +24 -0
- package/dist/multiplexer/session-capture.js.map +1 -0
- package/dist/multiplexer/session-launch.d.ts +4 -1
- package/dist/multiplexer/session-launch.js +152 -63
- package/dist/multiplexer/session-launch.js.map +1 -1
- package/dist/multiplexer/session-runtime-core.d.ts +8 -20
- package/dist/multiplexer/session-runtime-core.js +40 -135
- package/dist/multiplexer/session-runtime-core.js.map +1 -1
- package/dist/multiplexer/subscreens.js +10 -3
- package/dist/multiplexer/subscreens.js.map +1 -1
- package/dist/multiplexer/worktree-graveyard.d.ts +0 -1
- package/dist/multiplexer/worktree-graveyard.js +15 -16
- package/dist/multiplexer/worktree-graveyard.js.map +1 -1
- package/dist/multiplexer/worktrees.js +96 -40
- package/dist/multiplexer/worktrees.js.map +1 -1
- package/dist/notification-context.js +8 -4
- package/dist/notification-context.js.map +1 -1
- package/dist/notifications.js +163 -101
- package/dist/notifications.js.map +1 -1
- package/dist/notify.d.ts +4 -0
- package/dist/notify.js +14 -0
- package/dist/notify.js.map +1 -1
- package/dist/paths.d.ts +32 -7
- package/dist/paths.js +82 -58
- package/dist/paths.js.map +1 -1
- package/dist/pending-actions.d.ts +5 -0
- package/dist/pending-actions.js +14 -0
- package/dist/pending-actions.js.map +1 -0
- package/dist/plugin-runtime.js +9 -2
- package/dist/plugin-runtime.js.map +1 -1
- package/dist/project-events.d.ts +1 -10
- package/dist/project-events.js +0 -10
- package/dist/project-events.js.map +1 -1
- package/dist/project-scanner.d.ts +2 -3
- package/dist/project-scanner.js +58 -129
- package/dist/project-scanner.js.map +1 -1
- package/dist/project-service-manifest.d.ts +1 -3
- package/dist/project-service-manifest.js +1 -3
- package/dist/project-service-manifest.js.map +1 -1
- package/dist/relay-client.d.ts +30 -0
- package/dist/relay-client.js +191 -0
- package/dist/relay-client.js.map +1 -0
- package/dist/remote-access.d.ts +16 -0
- package/dist/remote-access.js +91 -0
- package/dist/remote-access.js.map +1 -0
- package/dist/runtime-core/exchange-derived.d.ts +2 -0
- package/dist/runtime-core/exchange-derived.js +154 -0
- package/dist/runtime-core/exchange-derived.js.map +1 -0
- package/dist/runtime-core/exchange-import.d.ts +24 -0
- package/dist/runtime-core/exchange-import.js +318 -0
- package/dist/runtime-core/exchange-import.js.map +1 -0
- package/dist/runtime-core/exchange-store.d.ts +157 -0
- package/dist/runtime-core/exchange-store.js +453 -0
- package/dist/runtime-core/exchange-store.js.map +1 -0
- package/dist/runtime-core/topology-services.d.ts +38 -0
- package/dist/runtime-core/topology-services.js +171 -0
- package/dist/runtime-core/topology-services.js.map +1 -0
- package/dist/runtime-core/topology-sessions.d.ts +52 -0
- package/dist/runtime-core/topology-sessions.js +239 -0
- package/dist/runtime-core/topology-sessions.js.map +1 -0
- package/dist/runtime-core/topology-store.d.ts +171 -0
- package/dist/runtime-core/topology-store.js +420 -0
- package/dist/runtime-core/topology-store.js.map +1 -0
- package/dist/runtime-core/topology-worktrees.d.ts +60 -0
- package/dist/runtime-core/topology-worktrees.js +200 -0
- package/dist/runtime-core/topology-worktrees.js.map +1 -0
- package/dist/runtime-migration.d.ts +69 -0
- package/dist/runtime-migration.js +399 -0
- package/dist/runtime-migration.js.map +1 -0
- package/dist/session-bootstrap.d.ts +8 -6
- package/dist/session-bootstrap.js +51 -158
- package/dist/session-bootstrap.js.map +1 -1
- package/dist/session-runtime.d.ts +2 -0
- package/dist/session-runtime.js +1 -0
- package/dist/session-runtime.js.map +1 -1
- package/dist/session-semantics.d.ts +12 -4
- package/dist/session-semantics.js +14 -0
- package/dist/session-semantics.js.map +1 -1
- package/dist/shell-hooks.js +32 -10
- package/dist/shell-hooks.js.map +1 -1
- package/dist/shell-state.d.ts +2 -0
- package/dist/shell-state.js +26 -1
- package/dist/shell-state.js.map +1 -1
- package/dist/statusline-model.d.ts +10 -2
- package/dist/statusline-model.js +106 -30
- package/dist/statusline-model.js.map +1 -1
- package/dist/task-workflow.d.ts +6 -9
- package/dist/task-workflow.js +37 -84
- package/dist/task-workflow.js.map +1 -1
- package/dist/tasks.d.ts +6 -33
- package/dist/tasks.js +46 -88
- package/dist/tasks.js.map +1 -1
- package/dist/team.d.ts +29 -0
- package/dist/team.js +40 -0
- package/dist/team.js.map +1 -1
- package/dist/threads.d.ts +6 -35
- package/dist/threads.js +89 -98
- package/dist/threads.js.map +1 -1
- package/dist/tmux/inbox-popup.js +37 -15
- package/dist/tmux/inbox-popup.js.map +1 -1
- package/dist/tmux/runtime-manager.d.ts +3 -0
- package/dist/tmux/runtime-manager.js +21 -4
- package/dist/tmux/runtime-manager.js.map +1 -1
- package/dist/tmux/statusline.js +49 -9
- package/dist/tmux/statusline.js.map +1 -1
- package/dist/tmux/window-open.js +1 -2
- package/dist/tmux/window-open.js.map +1 -1
- package/dist/tool-output-watchers.d.ts +0 -18
- package/dist/tool-output-watchers.js +0 -322
- package/dist/tool-output-watchers.js.map +1 -1
- package/dist/tui/screens/dashboard-renderers.js +37 -25
- package/dist/tui/screens/dashboard-renderers.js.map +1 -1
- package/dist/tui/screens/overlay-renderers.d.ts +2 -0
- package/dist/tui/screens/overlay-renderers.js +37 -1
- package/dist/tui/screens/overlay-renderers.js.map +1 -1
- package/dist/tui/screens/subscreen-renderers.js +7 -0
- package/dist/tui/screens/subscreen-renderers.js.map +1 -1
- package/dist/worktree.js +17 -0
- package/dist/worktree.js.map +1 -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 +28 -12
- 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/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/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/orchestration-dispatcher.d.ts +0 -25
- package/dist/orchestration-dispatcher.js +0 -59
- package/dist/orchestration-dispatcher.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/task-dispatcher.d.ts +0 -64
- package/dist/task-dispatcher.js +0 -213
- package/dist/task-dispatcher.js.map +0 -1
|
@@ -8,18 +8,344 @@ import { listThreadSummaries, readMessages } from "../threads.js";
|
|
|
8
8
|
import { deriveSessionSemantics } from "../session-semantics.js";
|
|
9
9
|
import { summarizeUnreadNotificationsBySession } from "../notifications.js";
|
|
10
10
|
import { requestJson } from "../http-client.js";
|
|
11
|
+
import { isTeammateSession, selectDirectTeammates } from "../team.js";
|
|
11
12
|
import { buildWorkflowEntries, describeWorkflowNextAction } from "../workflow.js";
|
|
12
13
|
import { ensureDaemonRunning, ensureProjectService } from "../daemon.js";
|
|
13
14
|
import { isDashboardWindowName } from "../tmux/runtime-manager.js";
|
|
14
15
|
import { dashboardCreatedSortKey, sortDashboardEntriesByCreatedAt } from "../dashboard/sort.js";
|
|
15
16
|
import { listDashboardOperationFailures } from "../dashboard/operation-failures.js";
|
|
16
17
|
import { listWorktreeGraveyardPaths } from "./worktree-graveyard.js";
|
|
18
|
+
import { setPendingDashboardServiceAction, setPendingDashboardSessionAction } from "./dashboard-ops.js";
|
|
19
|
+
import { listTopologySessionStates } from "../runtime-core/topology-sessions.js";
|
|
20
|
+
const METADATA_PENDING_SETTLE_TIMEOUT_MS = 10_000;
|
|
21
|
+
const METADATA_PENDING_SETTLE_INTERVAL_MS = 100;
|
|
22
|
+
function sleep(ms) {
|
|
23
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
24
|
+
}
|
|
25
|
+
function listOfflineSessionsForAction(host) {
|
|
26
|
+
const sessionsById = new Map();
|
|
27
|
+
for (const session of host.offlineSessions ?? []) {
|
|
28
|
+
if (session?.id)
|
|
29
|
+
sessionsById.set(session.id, session);
|
|
30
|
+
}
|
|
31
|
+
for (const session of listTopologySessionStates({ statuses: ["offline"] })) {
|
|
32
|
+
if (session?.id && !sessionsById.has(session.id))
|
|
33
|
+
sessionsById.set(session.id, session);
|
|
34
|
+
}
|
|
35
|
+
return [...sessionsById.values()];
|
|
36
|
+
}
|
|
37
|
+
function reconcileSessionsForLifecycleAction(host) {
|
|
38
|
+
host.syncSessionsFromTopology?.();
|
|
39
|
+
host.saveState?.();
|
|
40
|
+
}
|
|
41
|
+
function resolveOfflineSessionForAction(host, sessionId) {
|
|
42
|
+
return listOfflineSessionsForAction(host).find((session) => session.id === sessionId);
|
|
43
|
+
}
|
|
44
|
+
function findDashboardSessionSeed(host, sessionId, fallback) {
|
|
45
|
+
const cached = host.dashboardSessionsCache?.find?.((entry) => entry.id === sessionId);
|
|
46
|
+
if (cached)
|
|
47
|
+
return cached;
|
|
48
|
+
return toDashboardSessionSeed(host.sessions?.find?.((entry) => entry.id === sessionId) ??
|
|
49
|
+
host.offlineSessions?.find?.((entry) => entry.id === sessionId) ??
|
|
50
|
+
(fallback?.id ? fallback : undefined));
|
|
51
|
+
}
|
|
52
|
+
function findDashboardServiceSeed(host, serviceId) {
|
|
53
|
+
const cached = host.dashboardServicesCache?.find?.((entry) => entry.id === serviceId);
|
|
54
|
+
if (cached)
|
|
55
|
+
return cached;
|
|
56
|
+
return toDashboardServiceSeed(host.services?.find?.((entry) => entry.id === serviceId) ??
|
|
57
|
+
host.offlineServices?.find?.((entry) => entry.id === serviceId));
|
|
58
|
+
}
|
|
59
|
+
function toDashboardSessionSeed(seed) {
|
|
60
|
+
if (!seed?.id || !seed.command)
|
|
61
|
+
return undefined;
|
|
62
|
+
return {
|
|
63
|
+
index: typeof seed.index === "number" ? seed.index : -1,
|
|
64
|
+
id: seed.id,
|
|
65
|
+
command: seed.command,
|
|
66
|
+
label: seed.label,
|
|
67
|
+
status: seed.status ?? (seed.lifecycle === "offline" ? "offline" : "running"),
|
|
68
|
+
active: Boolean(seed.active),
|
|
69
|
+
worktreePath: seed.worktreePath,
|
|
70
|
+
createdAt: seed.createdAt,
|
|
71
|
+
backendSessionId: seed.backendSessionId,
|
|
72
|
+
headline: seed.headline,
|
|
73
|
+
team: seed.team,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function toDashboardServiceSeed(seed) {
|
|
77
|
+
if (!seed?.id)
|
|
78
|
+
return undefined;
|
|
79
|
+
return {
|
|
80
|
+
id: seed.id,
|
|
81
|
+
command: seed.command ?? seed.launchCommandLine ?? seed.label ?? "service",
|
|
82
|
+
args: Array.isArray(seed.args) ? seed.args : [],
|
|
83
|
+
label: seed.label,
|
|
84
|
+
status: seed.status ?? (seed.lifecycle === "offline" ? "offline" : "running"),
|
|
85
|
+
active: Boolean(seed.active),
|
|
86
|
+
worktreePath: seed.worktreePath,
|
|
87
|
+
createdAt: seed.createdAt,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function buildMetadataPendingSessionSeed(input) {
|
|
91
|
+
return {
|
|
92
|
+
index: -1,
|
|
93
|
+
id: input.sessionId,
|
|
94
|
+
command: input.tool,
|
|
95
|
+
label: input.tool,
|
|
96
|
+
createdAt: new Date().toISOString(),
|
|
97
|
+
status: "waiting",
|
|
98
|
+
active: false,
|
|
99
|
+
worktreePath: input.worktreePath,
|
|
100
|
+
pendingAction: input.pendingAction,
|
|
101
|
+
optimistic: true,
|
|
102
|
+
team: input.team,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
async function waitForMetadataCondition(host, predicate, timeoutMs = METADATA_PENDING_SETTLE_TIMEOUT_MS) {
|
|
106
|
+
const deadline = Date.now() + timeoutMs;
|
|
107
|
+
while (Date.now() < deadline) {
|
|
108
|
+
try {
|
|
109
|
+
host.invalidateDesktopStateSnapshot?.();
|
|
110
|
+
host.refreshLocalDashboardModel?.();
|
|
111
|
+
}
|
|
112
|
+
catch { }
|
|
113
|
+
if (predicate())
|
|
114
|
+
return true;
|
|
115
|
+
await sleep(METADATA_PENDING_SETTLE_INTERVAL_MS);
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
host.invalidateDesktopStateSnapshot?.();
|
|
119
|
+
host.refreshLocalDashboardModel?.();
|
|
120
|
+
}
|
|
121
|
+
catch { }
|
|
122
|
+
return predicate();
|
|
123
|
+
}
|
|
124
|
+
function hasLiveManagedAgentWindow(host, sessionId) {
|
|
125
|
+
try {
|
|
126
|
+
if (!host.tmuxRuntimeManager?.listProjectManagedWindows)
|
|
127
|
+
return false;
|
|
128
|
+
return host.tmuxRuntimeManager.listProjectManagedWindows(process.cwd()).some(({ target, metadata }) => {
|
|
129
|
+
if (isDashboardWindowName(target.windowName))
|
|
130
|
+
return false;
|
|
131
|
+
if (metadata.kind !== "agent" || metadata.sessionId !== sessionId)
|
|
132
|
+
return false;
|
|
133
|
+
if (host.tmuxRuntimeManager.isWindowAlive && !host.tmuxRuntimeManager.isWindowAlive(target))
|
|
134
|
+
return false;
|
|
135
|
+
return true;
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function hasLiveManagedServiceWindow(host, serviceId) {
|
|
143
|
+
try {
|
|
144
|
+
if (!host.tmuxRuntimeManager?.listProjectManagedWindows)
|
|
145
|
+
return false;
|
|
146
|
+
return host.tmuxRuntimeManager.listProjectManagedWindows(process.cwd()).some(({ target, metadata }) => {
|
|
147
|
+
if (isDashboardWindowName(target.windowName))
|
|
148
|
+
return false;
|
|
149
|
+
if (metadata.kind !== "service" || metadata.sessionId !== serviceId)
|
|
150
|
+
return false;
|
|
151
|
+
if (host.tmuxRuntimeManager.isWindowAlive && !host.tmuxRuntimeManager.isWindowAlive(target))
|
|
152
|
+
return false;
|
|
153
|
+
return true;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function isMetadataSessionRunning(host, sessionId) {
|
|
161
|
+
if (host.sessions?.some?.((session) => session.id === sessionId && !session.exited))
|
|
162
|
+
return true;
|
|
163
|
+
if (host.sessionTmuxTargets?.has?.(sessionId))
|
|
164
|
+
return true;
|
|
165
|
+
return hasLiveManagedAgentWindow(host, sessionId);
|
|
166
|
+
}
|
|
167
|
+
async function waitForMetadataSessionRunning(host, sessionId) {
|
|
168
|
+
if (typeof host.waitForSessionStart === "function") {
|
|
169
|
+
try {
|
|
170
|
+
if (await host.waitForSessionStart(sessionId, METADATA_PENDING_SETTLE_TIMEOUT_MS))
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
catch { }
|
|
174
|
+
}
|
|
175
|
+
return waitForMetadataCondition(host, () => isMetadataSessionRunning(host, sessionId));
|
|
176
|
+
}
|
|
177
|
+
function isMetadataServiceRunning(host, serviceId) {
|
|
178
|
+
const offline = host.offlineServices?.some?.((service) => service.id === serviceId);
|
|
179
|
+
if (offline)
|
|
180
|
+
return false;
|
|
181
|
+
if (host.services?.some?.((service) => service.id === serviceId && service.status !== "offline"))
|
|
182
|
+
return true;
|
|
183
|
+
return hasLiveManagedServiceWindow(host, serviceId);
|
|
184
|
+
}
|
|
185
|
+
function isMetadataServiceOffline(host, serviceId) {
|
|
186
|
+
return Boolean(host.offlineServices?.some?.((service) => service.id === serviceId));
|
|
187
|
+
}
|
|
188
|
+
function isMetadataServiceRemoved(host, serviceId) {
|
|
189
|
+
const offline = host.offlineServices?.some?.((service) => service.id === serviceId);
|
|
190
|
+
if (offline)
|
|
191
|
+
return false;
|
|
192
|
+
return !hasLiveManagedServiceWindow(host, serviceId);
|
|
193
|
+
}
|
|
194
|
+
async function settleMetadataPending(host, description, settle, result) {
|
|
195
|
+
if (!settle)
|
|
196
|
+
return;
|
|
197
|
+
try {
|
|
198
|
+
const settled = await settle(result);
|
|
199
|
+
if (!settled) {
|
|
200
|
+
host.debug?.(`metadata pending action did not settle: ${description}`, "dashboard");
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
host.debug?.(`metadata pending action settle failed: ${description}: ${error instanceof Error ? error.message : String(error)}`, "dashboard");
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function clearMetadataSessionPendingAfterSettle(host, sessionId, kind, token, settle, result) {
|
|
208
|
+
void (async () => {
|
|
209
|
+
await settleMetadataPending(host, `session ${kind} ${sessionId}`, settle, result);
|
|
210
|
+
if (typeof token === "number") {
|
|
211
|
+
if (host.dashboardPendingActions?.clearSessionActionIfToken?.(sessionId, token)) {
|
|
212
|
+
host.reapplyDashboardPendingActions?.();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else if (host.dashboardPendingActions?.getSessionAction?.(sessionId) === kind) {
|
|
216
|
+
setPendingDashboardSessionAction(host, sessionId, null);
|
|
217
|
+
}
|
|
218
|
+
})();
|
|
219
|
+
}
|
|
220
|
+
function clearMetadataServicePendingAfterSettle(host, serviceId, kind, token, settle, result) {
|
|
221
|
+
void (async () => {
|
|
222
|
+
await settleMetadataPending(host, `service ${kind} ${serviceId}`, settle, result);
|
|
223
|
+
if (typeof token === "number") {
|
|
224
|
+
if (host.dashboardPendingActions?.clearServiceActionIfToken?.(serviceId, token)) {
|
|
225
|
+
host.reapplyDashboardPendingActions?.();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
else if (host.dashboardPendingActions?.getServiceAction?.(serviceId) === kind) {
|
|
229
|
+
setPendingDashboardServiceAction(host, serviceId, null);
|
|
230
|
+
}
|
|
231
|
+
})();
|
|
232
|
+
}
|
|
233
|
+
export async function withMetadataSessionPending(host, sessionId, kind, work, sessionSeed, settle) {
|
|
234
|
+
let token;
|
|
235
|
+
if (sessionId) {
|
|
236
|
+
token = setPendingDashboardSessionAction(host, sessionId, kind, { sessionSeed });
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
239
|
+
const result = await work();
|
|
240
|
+
if (sessionId) {
|
|
241
|
+
clearMetadataSessionPendingAfterSettle(host, sessionId, kind, token, settle, result);
|
|
242
|
+
}
|
|
243
|
+
return result;
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
if (sessionId) {
|
|
247
|
+
if (typeof token === "number") {
|
|
248
|
+
if (host.dashboardPendingActions?.clearSessionActionIfToken?.(sessionId, token)) {
|
|
249
|
+
host.reapplyDashboardPendingActions?.();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
setPendingDashboardSessionAction(host, sessionId, null);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
throw error;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
export async function withMetadataServicePending(host, serviceId, kind, work, settle) {
|
|
260
|
+
const token = setPendingDashboardServiceAction(host, serviceId, kind, {
|
|
261
|
+
serviceSeed: findDashboardServiceSeed(host, serviceId),
|
|
262
|
+
});
|
|
263
|
+
try {
|
|
264
|
+
const result = await work();
|
|
265
|
+
clearMetadataServicePendingAfterSettle(host, serviceId, kind, token, settle, result);
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
if (host.dashboardPendingActions?.clearServiceActionIfToken?.(serviceId, token)) {
|
|
270
|
+
host.reapplyDashboardPendingActions?.();
|
|
271
|
+
}
|
|
272
|
+
throw error;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
function lifecycleFailureMessage(action, failures) {
|
|
276
|
+
const noun = failures.length === 1 ? "teammate" : "teammates";
|
|
277
|
+
const details = failures
|
|
278
|
+
.map(({ sessionId, error }) => `${sessionId}: ${error instanceof Error ? error.message : String(error)}`)
|
|
279
|
+
.join("; ");
|
|
280
|
+
return `Failed to ${action} ${failures.length} ${noun}: ${details}`;
|
|
281
|
+
}
|
|
282
|
+
async function resumeOfflineAgentWithPending(host, sessionId) {
|
|
283
|
+
return withMetadataSessionPending(host, sessionId, "starting", () => {
|
|
284
|
+
reconcileSessionsForLifecycleAction(host);
|
|
285
|
+
const offline = resolveOfflineSessionForAction(host, sessionId);
|
|
286
|
+
if (!offline) {
|
|
287
|
+
throw new Error(`Agent "${sessionId}" not found`);
|
|
288
|
+
}
|
|
289
|
+
host.resumeOfflineSession(offline);
|
|
290
|
+
return { sessionId, status: "running" };
|
|
291
|
+
}, findDashboardSessionSeed(host, sessionId), () => waitForMetadataSessionRunning(host, sessionId));
|
|
292
|
+
}
|
|
293
|
+
async function resumeOfflineAgentWithPendingAndSettle(host, sessionId) {
|
|
294
|
+
const result = await resumeOfflineAgentWithPending(host, sessionId);
|
|
295
|
+
await waitForMetadataSessionRunning(host, sessionId);
|
|
296
|
+
return result;
|
|
297
|
+
}
|
|
298
|
+
async function resumeAgentAndDirectTeammates(host, sessionId) {
|
|
299
|
+
reconcileSessionsForLifecycleAction(host);
|
|
300
|
+
const offline = resolveOfflineSessionForAction(host, sessionId);
|
|
301
|
+
if (!offline) {
|
|
302
|
+
throw new Error(`Agent "${sessionId}" not found`);
|
|
303
|
+
}
|
|
304
|
+
const teammates = isTeammateSession(offline)
|
|
305
|
+
? []
|
|
306
|
+
: selectDirectTeammates(listOfflineSessionsForAction(host), sessionId);
|
|
307
|
+
const result = await resumeOfflineAgentWithPendingAndSettle(host, sessionId);
|
|
308
|
+
const teammateFailures = [];
|
|
309
|
+
for (const teammate of teammates) {
|
|
310
|
+
try {
|
|
311
|
+
await resumeOfflineAgentWithPendingAndSettle(host, teammate.id);
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
teammateFailures.push({ sessionId: teammate.id, error });
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (teammateFailures.length > 0) {
|
|
318
|
+
return {
|
|
319
|
+
...result,
|
|
320
|
+
warning: lifecycleFailureMessage("resume", teammateFailures),
|
|
321
|
+
teammateFailures: teammateFailures.map(({ sessionId, error }) => ({
|
|
322
|
+
sessionId,
|
|
323
|
+
error: error instanceof Error ? error.message : String(error),
|
|
324
|
+
})),
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
return result;
|
|
328
|
+
}
|
|
17
329
|
function runProjectServiceUiRefresh(host) {
|
|
18
330
|
host.writeStatuslineFile();
|
|
19
331
|
if (host.mode === "dashboard") {
|
|
20
332
|
host.renderCurrentDashboardView();
|
|
21
333
|
}
|
|
22
334
|
}
|
|
335
|
+
const projectServiceAgentResumeQueues = new WeakMap();
|
|
336
|
+
async function enqueueProjectServiceAgentResume(host, work) {
|
|
337
|
+
const previous = projectServiceAgentResumeQueues.get(host) ?? Promise.resolve();
|
|
338
|
+
const current = previous.catch(() => undefined).then(work);
|
|
339
|
+
const tracked = current
|
|
340
|
+
.catch(() => undefined)
|
|
341
|
+
.finally(() => {
|
|
342
|
+
if (projectServiceAgentResumeQueues.get(host) === tracked) {
|
|
343
|
+
projectServiceAgentResumeQueues.delete(host);
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
projectServiceAgentResumeQueues.set(host, tracked);
|
|
347
|
+
return current;
|
|
348
|
+
}
|
|
23
349
|
function scheduleProjectServiceUiRefresh(host) {
|
|
24
350
|
if (host.projectServiceStartupMetadataSettling) {
|
|
25
351
|
host.projectServiceUiRefreshPending = true;
|
|
@@ -82,22 +408,28 @@ export function composeDashboardWorktreeGroups(worktreeGroups, dashSessions, das
|
|
|
82
408
|
};
|
|
83
409
|
}));
|
|
84
410
|
}
|
|
85
|
-
export function applyDashboardModel(host, dashSessions, dashServices, worktreeGroups, mainCheckoutInfo, operationFailures = []) {
|
|
411
|
+
export function applyDashboardModel(host, dashSessions, dashTeammates, dashServices, worktreeGroups, mainCheckoutInfo, operationFailures = []) {
|
|
86
412
|
const snapshotKey = JSON.stringify({
|
|
87
413
|
sessions: dashSessions,
|
|
414
|
+
teammates: dashTeammates,
|
|
88
415
|
services: dashServices,
|
|
89
416
|
worktreeGroups,
|
|
90
417
|
mainCheckoutInfo,
|
|
91
418
|
operationFailures,
|
|
419
|
+
pendingActionsVersion: host.dashboardPendingActions.getVersion?.() ?? 0,
|
|
92
420
|
});
|
|
93
421
|
if (snapshotKey === host.dashboardModelSnapshotKey) {
|
|
94
422
|
host.dashboardModelRefreshedAt = Date.now();
|
|
95
423
|
return false;
|
|
96
424
|
}
|
|
97
425
|
host.dashboardModelSnapshotKey = snapshotKey;
|
|
426
|
+
host.dashboardRawWorktreeGroupsCache = worktreeGroups;
|
|
98
427
|
host.dashboardSessionsCache = host.dashboardPendingActions.applyToSessions(dashSessions);
|
|
428
|
+
host.dashboardTeammatesCache = host.dashboardPendingActions
|
|
429
|
+
.applyToSessions(dashTeammates, { includeTeammates: true })
|
|
430
|
+
.filter((session) => isTeammateSession(session));
|
|
99
431
|
host.dashboardServicesCache = host.dashboardPendingActions.applyToServices(dashServices);
|
|
100
|
-
host.dashboardWorktreeGroupsCache = composeDashboardWorktreeGroups(host.dashboardPendingActions.applyToWorktrees(worktreeGroups), host.dashboardSessionsCache, host.dashboardServicesCache);
|
|
432
|
+
host.dashboardWorktreeGroupsCache = host.dashboardUiStateStore.orderWorktreeGroups(composeDashboardWorktreeGroups(host.dashboardPendingActions.applyToWorktrees(worktreeGroups), host.dashboardSessionsCache, host.dashboardServicesCache));
|
|
101
433
|
host.dashboardOperationFailuresCache = operationFailures;
|
|
102
434
|
host.dashboardMainCheckoutInfoCache = mainCheckoutInfo;
|
|
103
435
|
host.dashboardModelVersion = (host.dashboardModelVersion ?? 0) + 1;
|
|
@@ -111,7 +443,7 @@ export function invalidateDesktopStateSnapshot(host) {
|
|
|
111
443
|
export function refreshDesktopStateSnapshot(host) {
|
|
112
444
|
host.desktopStateSnapshot = buildDesktopStateSnapshot(host);
|
|
113
445
|
}
|
|
114
|
-
export function computeDashboardSessions(host) {
|
|
446
|
+
export function computeDashboardSessions(host, options = {}) {
|
|
115
447
|
const lastUsedState = loadLastUsedState(process.cwd());
|
|
116
448
|
const metadata = loadMetadataState().sessions;
|
|
117
449
|
const threadSummaries = listThreadSummaries();
|
|
@@ -187,6 +519,7 @@ export function computeDashboardSessions(host) {
|
|
|
187
519
|
id: session.id,
|
|
188
520
|
command: session.command,
|
|
189
521
|
backendSessionId: session.backendSessionId,
|
|
522
|
+
team: session.team,
|
|
190
523
|
createdAt: session.startTime ? new Date(session.startTime).toISOString() : undefined,
|
|
191
524
|
status: session.status,
|
|
192
525
|
worktreePath: host.sessionWorktreePaths.get(session.id),
|
|
@@ -194,12 +527,12 @@ export function computeDashboardSessions(host) {
|
|
|
194
527
|
})),
|
|
195
528
|
activeIndex: host.activeIndex,
|
|
196
529
|
offlineSessions: host.offlineSessions,
|
|
197
|
-
remoteInstances: [],
|
|
198
530
|
hiddenWorktreePaths: listWorktreeGraveyardPaths(),
|
|
199
531
|
mainRepoPath,
|
|
532
|
+
includeTeammates: options.includeTeammates,
|
|
200
533
|
getSessionLabel: (sessionId) => host.getSessionLabel(sessionId),
|
|
201
534
|
getSessionHeadline: (sessionId) => host.deriveHeadline(sessionId),
|
|
202
|
-
getSessionTaskDescription: (
|
|
535
|
+
getSessionTaskDescription: () => undefined,
|
|
203
536
|
getSessionRole: (sessionId) => host.sessionRoles.get(sessionId),
|
|
204
537
|
getSessionContext: (sessionId) => metadata[sessionId]?.context,
|
|
205
538
|
getSessionDerived: (sessionId) => metadata[sessionId]?.derived,
|
|
@@ -265,6 +598,7 @@ export function computeDashboardSessions(host) {
|
|
|
265
598
|
export function computeDashboardServices(host, worktrees = host.listDesktopWorktrees()) {
|
|
266
599
|
const hiddenWorktreePaths = listWorktreeGraveyardPaths();
|
|
267
600
|
const lastUsedState = loadLastUsedState(process.cwd());
|
|
601
|
+
const sessionMetadata = loadMetadataState().sessions;
|
|
268
602
|
const offlineServiceIds = new Set(host.offlineServices.map((service) => service.id));
|
|
269
603
|
const worktreeByPath = new Map(worktrees.map((wt) => [wt.path, wt]));
|
|
270
604
|
const liveServices = host.tmuxRuntimeManager
|
|
@@ -275,6 +609,7 @@ export function computeDashboardServices(host, worktrees = host.listDesktopWorkt
|
|
|
275
609
|
.map(({ target, metadata }) => {
|
|
276
610
|
const worktree = metadata.worktreePath ? worktreeByPath.get(metadata.worktreePath) : undefined;
|
|
277
611
|
const info = readTmuxProcessInfo(host, target);
|
|
612
|
+
const shellMetadata = sessionMetadata[metadata.sessionId]?.derived;
|
|
278
613
|
return {
|
|
279
614
|
id: metadata.sessionId,
|
|
280
615
|
command: metadata.command,
|
|
@@ -291,6 +626,8 @@ export function computeDashboardServices(host, worktrees = host.listDesktopWorkt
|
|
|
291
626
|
label: metadata.label,
|
|
292
627
|
cwd: host.tmuxRuntimeManager.displayMessage("#{pane_current_path}", target.windowId) ?? metadata.worktreePath,
|
|
293
628
|
foregroundCommand: info.command,
|
|
629
|
+
shellCommand: shellMetadata?.shellCommand,
|
|
630
|
+
shellCommandState: shellMetadata?.shellCommandState,
|
|
294
631
|
pid: info.pid,
|
|
295
632
|
previewLine: info.previewLine,
|
|
296
633
|
};
|
|
@@ -303,6 +640,7 @@ export function computeDashboardServices(host, worktrees = host.listDesktopWorkt
|
|
|
303
640
|
const worktree = service.worktreePath ? worktreeByPath.get(service.worktreePath) : undefined;
|
|
304
641
|
const label = service.label ?? host.serviceLabelForCommand(service.launchCommandLine ?? "");
|
|
305
642
|
const previewLine = service.launchCommandLine?.trim() || "Interactive shell";
|
|
643
|
+
const shellMetadata = sessionMetadata[service.id]?.derived;
|
|
306
644
|
return {
|
|
307
645
|
id: service.id,
|
|
308
646
|
command: service.launchCommandLine?.trim() ?? "",
|
|
@@ -317,6 +655,8 @@ export function computeDashboardServices(host, worktrees = host.listDesktopWorkt
|
|
|
317
655
|
label,
|
|
318
656
|
cwd: service.cwd ?? service.worktreePath,
|
|
319
657
|
foregroundCommand: label,
|
|
658
|
+
shellCommand: shellMetadata?.shellCommand,
|
|
659
|
+
shellCommandState: shellMetadata?.shellCommandState,
|
|
320
660
|
previewLine,
|
|
321
661
|
};
|
|
322
662
|
});
|
|
@@ -342,7 +682,7 @@ export function readTmuxProcessInfo(host, target) {
|
|
|
342
682
|
};
|
|
343
683
|
}
|
|
344
684
|
export function buildDesktopStateSnapshot(host) {
|
|
345
|
-
host.
|
|
685
|
+
host.syncSessionsFromTopology();
|
|
346
686
|
const worktrees = host.listDesktopWorktrees();
|
|
347
687
|
const realizedWorktreePaths = new Set(worktrees.filter((worktree) => !worktree.operationFailure).map((worktree) => worktree.path));
|
|
348
688
|
const operationFailures = listDashboardOperationFailures().filter((failure) => !(failure.targetKind === "worktree" &&
|
|
@@ -360,6 +700,7 @@ export function buildDesktopStateSnapshot(host) {
|
|
|
360
700
|
}
|
|
361
701
|
return {
|
|
362
702
|
sessions: computeDashboardSessions(host),
|
|
703
|
+
teammates: computeDashboardSessions(host, { includeTeammates: true }).filter((session) => isTeammateSession(session)),
|
|
363
704
|
services: computeDashboardServices(host, worktrees),
|
|
364
705
|
worktrees,
|
|
365
706
|
operationFailures,
|
|
@@ -383,15 +724,16 @@ export async function refreshDashboardModelFromService(host, force = false) {
|
|
|
383
724
|
if (endpoint) {
|
|
384
725
|
try {
|
|
385
726
|
const { status, json } = await requestJson(`http://${endpoint.host}:${endpoint.port}/desktop-state`, {
|
|
386
|
-
timeoutMs:
|
|
727
|
+
timeoutMs: force ? 2000 : 750,
|
|
387
728
|
});
|
|
388
729
|
if (status >= 200 && status < 300) {
|
|
389
730
|
const body = json;
|
|
390
731
|
const dashSessions = body.sessions ?? [];
|
|
732
|
+
const dashTeammates = body.teammates ?? [];
|
|
391
733
|
const dashServices = body.services ?? [];
|
|
392
734
|
const worktrees = body.worktrees ?? [];
|
|
393
735
|
const worktreeGroups = buildDashboardWorktreeGroups(host, dashSessions, dashServices, worktrees, body.mainCheckoutPath);
|
|
394
|
-
return applyDashboardModel(host, dashSessions, dashServices, worktreeGroups, body.mainCheckoutInfo ?? { name: "Main Checkout", branch: "" }, body.operationFailures ?? []);
|
|
736
|
+
return applyDashboardModel(host, dashSessions, dashTeammates, dashServices, worktreeGroups, body.mainCheckoutInfo ?? { name: "Main Checkout", branch: "" }, body.operationFailures ?? []);
|
|
395
737
|
}
|
|
396
738
|
}
|
|
397
739
|
catch {
|
|
@@ -416,7 +758,7 @@ export async function refreshDashboardModelFromService(host, force = false) {
|
|
|
416
758
|
export function refreshLocalDashboardModel(host) {
|
|
417
759
|
const snapshot = buildDesktopStateSnapshot(host);
|
|
418
760
|
const worktreeGroups = buildDashboardWorktreeGroups(host, snapshot.sessions, snapshot.services, snapshot.worktrees, snapshot.mainCheckoutPath);
|
|
419
|
-
applyDashboardModel(host, snapshot.sessions, snapshot.services, worktreeGroups, snapshot.mainCheckoutInfo, snapshot.operationFailures);
|
|
761
|
+
applyDashboardModel(host, snapshot.sessions, snapshot.teammates, snapshot.services, worktreeGroups, snapshot.mainCheckoutInfo, snapshot.operationFailures);
|
|
420
762
|
}
|
|
421
763
|
export async function startProjectServices(host) {
|
|
422
764
|
if (host.metadataServer)
|
|
@@ -427,20 +769,27 @@ export async function startProjectServices(host) {
|
|
|
427
769
|
events: { bus: host.eventBus },
|
|
428
770
|
desktop: {
|
|
429
771
|
getState: () => host.buildDesktopState(),
|
|
430
|
-
listWorktrees: () => host.
|
|
772
|
+
listWorktrees: () => host.listProjectedDesktopWorktrees(),
|
|
431
773
|
getSessionDisplayContext: (sessionId) => {
|
|
432
774
|
const session = host.dashboardSessionsCache.find((entry) => entry.id === sessionId) ??
|
|
433
775
|
host.sessions.find((entry) => entry.id === sessionId);
|
|
434
|
-
const
|
|
776
|
+
const service = host.dashboardServicesCache.find((entry) => entry.id === sessionId) ??
|
|
777
|
+
host.services?.find?.((entry) => entry.id === sessionId) ??
|
|
778
|
+
host.offlineServices?.find?.((entry) => entry.id === sessionId);
|
|
779
|
+
const worktreePath = host.sessionWorktreePaths.get(sessionId) ?? session?.worktreePath ?? service?.worktreePath;
|
|
435
780
|
const group = worktreePath
|
|
436
781
|
? host.dashboardWorktreeGroupsCache.find((entry) => entry.path === worktreePath)
|
|
437
782
|
: host.dashboardWorktreeGroupsCache.find((entry) => !entry.path);
|
|
438
783
|
return {
|
|
439
|
-
label: host.getSessionLabel(sessionId) ??
|
|
440
|
-
|
|
784
|
+
label: host.getSessionLabel(sessionId) ??
|
|
785
|
+
session?.label ??
|
|
786
|
+
service?.label ??
|
|
787
|
+
(service ? host.serviceLabelForCommand?.(service.launchCommandLine ?? service.command ?? "") : undefined) ??
|
|
788
|
+
session?.command,
|
|
789
|
+
command: session?.command ?? service?.command ?? service?.launchCommandLine,
|
|
441
790
|
worktreePath,
|
|
442
|
-
worktreeName: session?.worktreeName ?? group?.name,
|
|
443
|
-
branch: session?.worktreeBranch ?? group?.branch,
|
|
791
|
+
worktreeName: session?.worktreeName ?? service?.worktreeName ?? group?.name,
|
|
792
|
+
branch: session?.worktreeBranch ?? service?.worktreeBranch ?? group?.branch,
|
|
444
793
|
};
|
|
445
794
|
},
|
|
446
795
|
refreshStatusline: ({ sessionId, force }) => host.refreshProjectStatusline({ sessionId, force }),
|
|
@@ -451,17 +800,10 @@ export async function startProjectServices(host) {
|
|
|
451
800
|
resurrectGraveyardWorktree: ({ path }) => host.resurrectGraveyardWorktree(path),
|
|
452
801
|
deleteGraveyardWorktree: ({ path }) => host.deleteGraveyardWorktree(path),
|
|
453
802
|
createService: ({ command, worktreePath, serviceId }) => host.createService(command ?? "", worktreePath, { serviceId }),
|
|
454
|
-
stopService: ({ serviceId }) => host.stopService(serviceId),
|
|
455
|
-
resumeService: ({ serviceId }) => host.resumeOfflineServiceById(serviceId),
|
|
456
|
-
removeService: ({ serviceId }) => host.removeOfflineService(serviceId),
|
|
457
|
-
resumeAgent: ({ sessionId }) =>
|
|
458
|
-
const offline = host.offlineSessions.find((session) => session.id === sessionId);
|
|
459
|
-
if (!offline) {
|
|
460
|
-
throw new Error(`Agent "${sessionId}" not found`);
|
|
461
|
-
}
|
|
462
|
-
host.resumeOfflineSession(offline);
|
|
463
|
-
return { sessionId, status: "running" };
|
|
464
|
-
},
|
|
803
|
+
stopService: ({ serviceId }) => withMetadataServicePending(host, serviceId, "stopping", () => host.stopService(serviceId), () => waitForMetadataCondition(host, () => isMetadataServiceOffline(host, serviceId))),
|
|
804
|
+
resumeService: ({ serviceId }) => withMetadataServicePending(host, serviceId, "starting", () => host.resumeOfflineServiceById(serviceId), () => waitForMetadataCondition(host, () => isMetadataServiceRunning(host, serviceId))),
|
|
805
|
+
removeService: ({ serviceId }) => withMetadataServicePending(host, serviceId, "removing", () => host.removeOfflineService(serviceId), () => waitForMetadataCondition(host, () => isMetadataServiceRemoved(host, serviceId))),
|
|
806
|
+
resumeAgent: ({ sessionId }) => enqueueProjectServiceAgentResume(host, () => resumeAgentAndDirectTeammates(host, sessionId)),
|
|
465
807
|
listGraveyard: () => host.listGraveyardEntries(),
|
|
466
808
|
resurrectGraveyard: ({ sessionId }) => host.resurrectGraveyardSession(sessionId),
|
|
467
809
|
},
|
|
@@ -472,14 +814,48 @@ export async function startProjectServices(host) {
|
|
|
472
814
|
sendHandoff: (input) => host.sendHandoffMessage(input),
|
|
473
815
|
},
|
|
474
816
|
lifecycle: {
|
|
475
|
-
spawnAgent: (input) => host.spawnAgent({
|
|
817
|
+
spawnAgent: (input) => withMetadataSessionPending(host, input.sessionId, "creating", () => host.spawnAgent({
|
|
476
818
|
toolConfigKey: input.tool,
|
|
477
819
|
targetSessionId: input.sessionId,
|
|
478
820
|
targetWorktreePath: input.worktreePath,
|
|
479
821
|
open: input.open ?? false,
|
|
480
822
|
extraArgs: input.extraArgs,
|
|
481
|
-
}),
|
|
482
|
-
|
|
823
|
+
}), input.sessionId
|
|
824
|
+
? buildMetadataPendingSessionSeed({
|
|
825
|
+
sessionId: input.sessionId,
|
|
826
|
+
tool: input.tool,
|
|
827
|
+
worktreePath: input.worktreePath,
|
|
828
|
+
pendingAction: "creating",
|
|
829
|
+
})
|
|
830
|
+
: undefined, (result) => waitForMetadataSessionRunning(host, result.sessionId)),
|
|
831
|
+
createTeammateAgent: (input) => withMetadataSessionPending(host, input.sessionId, "creating", () => host.createTeammateAgent({
|
|
832
|
+
parentSessionId: input.parentSessionId,
|
|
833
|
+
role: input.role,
|
|
834
|
+
label: input.label,
|
|
835
|
+
toolConfigKey: input.tool,
|
|
836
|
+
targetSessionId: input.sessionId,
|
|
837
|
+
targetWorktreePath: input.worktreePath,
|
|
838
|
+
open: input.open ?? false,
|
|
839
|
+
extraArgs: input.extraArgs,
|
|
840
|
+
order: input.order,
|
|
841
|
+
}), input.sessionId
|
|
842
|
+
? buildMetadataPendingSessionSeed({
|
|
843
|
+
sessionId: input.sessionId,
|
|
844
|
+
tool: input.tool,
|
|
845
|
+
worktreePath: input.worktreePath,
|
|
846
|
+
pendingAction: "creating",
|
|
847
|
+
team: {
|
|
848
|
+
teamId: `team-${input.parentSessionId}`,
|
|
849
|
+
parentSessionId: input.parentSessionId,
|
|
850
|
+
role: typeof input.role === "string" && input.role.trim() ? input.role.trim() : undefined,
|
|
851
|
+
label: typeof input.label === "string" && input.label.trim() ? input.label.trim() : undefined,
|
|
852
|
+
order: typeof input.order === "number" ? input.order : undefined,
|
|
853
|
+
},
|
|
854
|
+
})
|
|
855
|
+
: undefined, (result) => result?.reused && result.sessionId !== input.sessionId
|
|
856
|
+
? true
|
|
857
|
+
: waitForMetadataSessionRunning(host, result.sessionId)),
|
|
858
|
+
forkAgent: (input) => withMetadataSessionPending(host, input.targetSessionId, "forking", () => host.forkAgent({
|
|
483
859
|
sourceSessionId: input.sourceSessionId,
|
|
484
860
|
targetToolConfigKey: input.tool,
|
|
485
861
|
targetSessionId: input.targetSessionId,
|
|
@@ -487,15 +863,21 @@ export async function startProjectServices(host) {
|
|
|
487
863
|
targetWorktreePath: input.worktreePath,
|
|
488
864
|
open: input.open ?? false,
|
|
489
865
|
extraArgs: input.extraArgs,
|
|
490
|
-
}),
|
|
491
|
-
|
|
866
|
+
}), input.targetSessionId
|
|
867
|
+
? buildMetadataPendingSessionSeed({
|
|
868
|
+
sessionId: input.targetSessionId,
|
|
869
|
+
tool: input.tool,
|
|
870
|
+
worktreePath: input.worktreePath,
|
|
871
|
+
pendingAction: "forking",
|
|
872
|
+
})
|
|
873
|
+
: undefined, (result) => waitForMetadataSessionRunning(host, result.sessionId)),
|
|
874
|
+
stopAgent: (input) => withMetadataSessionPending(host, input.sessionId, "stopping", () => host.stopAgent(input.sessionId), findDashboardSessionSeed(host, input.sessionId)),
|
|
492
875
|
interruptAgent: (input) => host.interruptAgent(input.sessionId),
|
|
493
876
|
renameAgent: (input) => host.renameAgent(input.sessionId, input.label),
|
|
494
|
-
migrateAgent: (input) => host.migrateAgent(input.sessionId, input.worktreePath),
|
|
495
|
-
killAgent: (input) => host.sendAgentToGraveyard(input.sessionId),
|
|
496
|
-
|
|
877
|
+
migrateAgent: (input) => withMetadataSessionPending(host, input.sessionId, "migrating", () => host.migrateAgent(input.sessionId, input.worktreePath), findDashboardSessionSeed(host, input.sessionId)),
|
|
878
|
+
killAgent: (input) => withMetadataSessionPending(host, input.sessionId, "graveyarding", () => host.sendAgentToGraveyard(input.sessionId), findDashboardSessionSeed(host, input.sessionId)),
|
|
879
|
+
sendAgentInput: (input) => host.sendAgentInput(input.sessionId, input.text),
|
|
497
880
|
readAgentOutput: (input) => host.readAgentOutput(input.sessionId, input.startLine),
|
|
498
|
-
readAgentHistory: (input) => host.readAgentHistory(input.sessionId, input.lastN),
|
|
499
881
|
},
|
|
500
882
|
onChange: () => {
|
|
501
883
|
scheduleProjectServiceUiRefresh(host);
|