tmux-ide 2.9.0-beta.21 → 2.9.0-beta.26
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/bin/cli.js +46791 -43589
- package/package.json +2 -2
- package/packages/contracts/src/__tests__/daemon-wire.test.ts +2 -2
- package/packages/contracts/src/__tests__/fixtures/daemon-wire-v3.json +35 -0
- package/packages/contracts/src/__tests__/issue-error.test.ts +1 -1
- package/packages/contracts/src/__tests__/multiplexer-verbs.test.ts +3 -1
- package/packages/contracts/src/__tests__/pane-stream.test.ts +17 -5
- package/packages/contracts/src/__tests__/tmux-server-scope.test.ts +166 -0
- package/packages/contracts/src/__tests__/window-links.test.ts +243 -0
- package/packages/contracts/src/actions-contract.ts +12 -0
- package/packages/contracts/src/daemon-resource-request.test.ts +1 -1
- package/packages/contracts/src/daemon-wire.ts +10 -5
- package/packages/contracts/src/fleet-client-state.ts +3 -0
- package/packages/contracts/src/fleet-lifecycle.ts +5 -0
- package/packages/contracts/src/index.ts +2 -0
- package/packages/contracts/src/interaction-receipts.ts +39 -0
- package/packages/contracts/src/issue-error.ts +1 -1
- package/packages/contracts/src/multiplexer-verbs.ts +25 -1
- package/packages/contracts/src/pane-stream.ts +51 -8
- package/packages/contracts/src/tmux-server-scope.ts +172 -0
- package/packages/contracts/src/window-links.ts +76 -0
- package/packages/contracts/src/workspace-multiplexer.ts +54 -0
- package/packages/core/src/interaction-receipts.ts +4 -0
- package/packages/daemon/dist/command-center/actions/command-definitions.js +6 -0
- package/packages/daemon/dist/command-center/actions/handlers/workspace-multiplexer.js +2 -0
- package/packages/daemon/dist/command-center/actions/registry.js +13 -1
- package/packages/daemon/dist/command-center/discovery.js +2 -9
- package/packages/daemon/dist/command-center/server.js +21 -1
- package/packages/daemon/dist/command-center/tmux-server-native-backing.js +37 -0
- package/packages/daemon/dist/command-center/tmux-servers.js +351 -0
- package/packages/daemon/dist/lib/canonical-daemon-bootstrap.js +64 -8
- package/packages/daemon/dist/lib/canonical-daemon.js +5 -2
- package/packages/daemon/dist/lib/daemon-embed.js +198 -30
- package/packages/daemon/dist/lib/embedded-tmux-server-owners.js +147 -0
- package/packages/daemon/dist/lib/fleet-lifecycle-authority.js +31 -11
- package/packages/daemon/dist/lib/headless-daemon.js +10 -1
- package/packages/daemon/dist/lib/runtime-namespace.js +17 -3
- package/packages/daemon/dist/lib/tmux-client-execution.js +59 -0
- package/packages/daemon/dist/lib/tmux-named-socket-fence.js +4 -0
- package/packages/daemon/dist/lib/tmux-server-generation-runner.js +62 -0
- package/packages/daemon/dist/lib/tmux-server-owner.js +285 -0
- package/packages/daemon/dist/lib/tmux-server-owners.js +192 -0
- package/packages/daemon/dist/lib/tmux-server-proof.js +82 -0
- package/packages/daemon/dist/lib/tmux-server-registration.js +85 -0
- package/packages/daemon/dist/lib/tmux-server-session-create.js +43 -0
- package/packages/daemon/dist/lib/tmux-server-session-open.js +101 -0
- package/packages/daemon/dist/lib/tmux-servers-cli.js +134 -0
- package/packages/daemon/dist/lib/tmux-session-mutation-fence.js +55 -0
- package/packages/daemon/dist/lib/tmux-window-link-guard.js +88 -0
- package/packages/daemon/dist/lib/workspace-config-loader.js +3 -0
- package/packages/daemon/dist/lib/workspace-multiplexer-verbs.js +82 -1
- package/packages/daemon/dist/lib/workspace-pane-creation.js +13 -57
- package/packages/daemon/dist/lib/workspace-promotion.js +91 -12
- package/packages/daemon/dist/lib/workspace-registry.js +4 -0
- package/packages/daemon/dist/server/pane-stream-upgrade.js +20 -6
- package/packages/daemon/dist/terminal/attachments/native-runtime.js +61 -17
- package/packages/daemon/dist/terminal/mirror/__tests__/scripted-channel.js +9 -1
- package/packages/daemon/dist/terminal/mirror/__tests__/simulated-channel.js +11 -2
- package/packages/daemon/dist/terminal/mirror/control-channel.js +65 -6
- package/packages/daemon/dist/terminal/mirror/mirror-service.js +44 -0
- package/packages/daemon/dist/terminal/mirror/session-channel.js +210 -50
- package/packages/daemon/dist/terminal/mirror/window-link-authority.js +160 -0
- package/packages/daemon/dist/terminal/pane-stream/lease-manager.js +3 -3
- package/packages/daemon/dist/terminal/pane-stream/pane-stream-websocket.js +40 -6
- package/packages/daemon/dist/terminal/protocol/live-session-identity.js +9 -0
- package/packages/daemon/dist/terminal/protocol/native-backing-client.js +2 -1
- package/packages/daemon/dist/terminal/session-runtime/interaction-receipt-facts.js +9 -0
- package/packages/daemon/dist/terminal/session-runtime/registry.js +6 -0
- package/packages/daemon/dist/terminal/session-runtime/semantic-mutation-executor.js +1 -1
- package/packages/daemon/dist/terminal/session-runtime/semantic-mutation-resource-changes.js +1 -0
- package/packages/daemon/dist/tui/mirror/application-shell-daemon-connection.js +7 -2
- package/packages/daemon/dist/tui/mirror/application-shell-server-connection.js +114 -0
- package/packages/daemon/dist/tui/mirror/menu-model.js +1 -1
- package/packages/daemon/dist/tui/mirror/open-tui-verified-routing.js +34 -4
- package/packages/daemon/dist/tui/mirror/open-tui-workspace-runtime-port.js +29 -8
- package/packages/daemon/dist/tui/mirror/runtime/application-guided-tour-integration.jsx +119 -0
- package/packages/daemon/dist/tui/mirror/runtime/application-guided-tour-owner.js +132 -0
- package/packages/daemon/dist/tui/mirror/runtime/application-home-agent-roster.jsx +21 -9
- package/packages/daemon/dist/tui/mirror/runtime/application-home-agent-transport.js +63 -1
- package/packages/daemon/dist/tui/mirror/runtime/application-home-agents-owner.js +8 -2
- package/packages/daemon/dist/tui/mirror/runtime/application-home-agents.js +3 -1
- package/packages/daemon/dist/tui/mirror/runtime/application-home-catalog-owner.js +11 -5
- package/packages/daemon/dist/tui/mirror/runtime/application-home-catalog.js +140 -75
- package/packages/daemon/dist/tui/mirror/runtime/application-home-experience.js +40 -0
- package/packages/daemon/dist/tui/mirror/runtime/application-home-fleet.js +132 -0
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-agents.js +9 -0
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-catalog.js +12 -2
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-navigation.js +211 -31
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-sidebar.jsx +91 -37
- package/packages/daemon/dist/tui/mirror/runtime/application-performance-log.js +20 -7
- package/packages/daemon/dist/tui/mirror/runtime/application-root-configuration.js +9 -3
- package/packages/daemon/dist/tui/mirror/runtime/application-route-connection.js +95 -2
- package/packages/daemon/dist/tui/mirror/runtime/application-shell-home.jsx +6 -2
- package/packages/daemon/dist/tui/mirror/runtime/application-terminal-interaction-controller.js +158 -2
- package/packages/daemon/dist/tui/mirror/runtime/application-terminal-workspace-policy.js +43 -0
- package/packages/daemon/dist/tui/mirror/runtime/application-terminal-workspace.jsx +78 -25
- package/packages/daemon/dist/tui/mirror/runtime/application-theme-repaint.js +9 -0
- package/packages/daemon/dist/tui/mirror/runtime/fleet-lifecycle-client.js +44 -1
- package/packages/daemon/dist/tui/mirror/runtime/guided-tour-coach.jsx +54 -0
- package/packages/daemon/dist/tui/mirror/runtime/guided-tour-storage.js +67 -0
- package/packages/daemon/dist/tui/mirror/runtime/guided-tour.js +113 -0
- package/packages/daemon/dist/tui/mirror/runtime/open-tui-generation-host.js +1 -1
- package/packages/daemon/dist/tui/mirror/runtime/resize-terminal-pane.js +4 -0
- package/packages/daemon/dist/tui/mirror/runtime/select-terminal-pane.js +2 -1
- package/packages/daemon/dist/tui/mirror/runtime/semantic-shell-viewport-resize.js +12 -4
- package/packages/daemon/dist/tui/mirror/runtime/terminal-pane-input-router.js +6 -3
- package/packages/daemon/dist/tui/mirror/workspace/application-command-description.js +11 -2
- package/packages/daemon/dist/tui/mirror/workspace/terminal-window-strip.jsx +2 -1
- package/packages/daemon/src/command-center/actions/command-definitions.ts +6 -0
- package/packages/daemon/src/command-center/actions/errors.ts +4 -0
- package/packages/daemon/src/command-center/actions/handlers/workspace-multiplexer.ts +2 -0
- package/packages/daemon/src/command-center/actions/registry.ts +14 -0
- package/packages/daemon/src/command-center/discovery.ts +2 -10
- package/packages/daemon/src/command-center/server.ts +29 -1
- package/packages/daemon/src/command-center/tmux-server-native-backing.ts +49 -0
- package/packages/daemon/src/command-center/tmux-servers.ts +479 -0
- package/packages/daemon/src/lib/canonical-daemon-bootstrap.ts +111 -4
- package/packages/daemon/src/lib/canonical-daemon.ts +15 -3
- package/packages/daemon/src/lib/daemon-embed.ts +241 -28
- package/packages/daemon/src/lib/embedded-tmux-server-owners.ts +190 -0
- package/packages/daemon/src/lib/fleet-lifecycle-authority.ts +33 -9
- package/packages/daemon/src/lib/headless-daemon.ts +18 -1
- package/packages/daemon/src/lib/runtime-namespace.ts +24 -3
- package/packages/daemon/src/lib/tmux-client-execution.ts +56 -0
- package/packages/daemon/src/lib/tmux-named-socket-fence.ts +5 -0
- package/packages/daemon/src/lib/tmux-server-generation-runner.ts +81 -0
- package/packages/daemon/src/lib/tmux-server-owner.ts +336 -0
- package/packages/daemon/src/lib/tmux-server-owners.ts +228 -0
- package/packages/daemon/src/lib/tmux-server-proof.ts +95 -0
- package/packages/daemon/src/lib/tmux-server-registration.ts +105 -0
- package/packages/daemon/src/lib/tmux-server-session-create.ts +52 -0
- package/packages/daemon/src/lib/tmux-server-session-open.ts +106 -0
- package/packages/daemon/src/lib/tmux-servers-cli.ts +186 -0
- package/packages/daemon/src/lib/tmux-session-mutation-fence.ts +62 -0
- package/packages/daemon/src/lib/tmux-window-link-guard.ts +112 -0
- package/packages/daemon/src/lib/workspace-config-loader.ts +3 -0
- package/packages/daemon/src/lib/workspace-multiplexer-verbs.ts +121 -1
- package/packages/daemon/src/lib/workspace-pane-creation.ts +16 -55
- package/packages/daemon/src/lib/workspace-promotion.ts +98 -12
- package/packages/daemon/src/lib/workspace-registry.ts +5 -0
- package/packages/daemon/src/server/pane-stream-upgrade.ts +25 -5
- package/packages/daemon/src/terminal/attachments/native-runtime.ts +70 -16
- package/packages/daemon/src/terminal/mirror/control-channel.ts +64 -7
- package/packages/daemon/src/terminal/mirror/events.ts +2 -0
- package/packages/daemon/src/terminal/mirror/mirror-service.ts +56 -0
- package/packages/daemon/src/terminal/mirror/session-channel.ts +245 -50
- package/packages/daemon/src/terminal/mirror/window-link-authority.ts +203 -0
- package/packages/daemon/src/terminal/pane-stream/lease-manager.ts +3 -3
- package/packages/daemon/src/terminal/pane-stream/pane-stream-websocket.ts +50 -6
- package/packages/daemon/src/terminal/protocol/live-session-identity.ts +14 -0
- package/packages/daemon/src/terminal/protocol/native-backing-client.ts +4 -1
- package/packages/daemon/src/terminal/session-runtime/interaction-receipt-facts.ts +9 -0
- package/packages/daemon/src/terminal/session-runtime/registry.ts +9 -0
- package/packages/daemon/src/terminal/session-runtime/semantic-mutation-executor.ts +2 -2
- package/packages/daemon/src/terminal/session-runtime/semantic-mutation-resource-changes.ts +1 -0
- package/packages/daemon/src/tui/mirror/application-shell-daemon-connection.ts +28 -2
- package/packages/daemon/src/tui/mirror/application-shell-server-connection.ts +154 -0
- package/packages/daemon/src/tui/mirror/menu-model.ts +1 -1
- package/packages/daemon/src/tui/mirror/open-tui-verified-routing.ts +41 -3
- package/packages/daemon/src/tui/mirror/open-tui-workspace-runtime-port.ts +47 -6
- package/packages/daemon/src/tui/mirror/runtime/application-entry.ts +2 -1
- package/packages/daemon/src/tui/mirror/runtime/application-fleet-session-actions.tsx +10 -4
- package/packages/daemon/src/tui/mirror/runtime/application-fleet-tabs.ts +2 -0
- package/packages/daemon/src/tui/mirror/runtime/application-guided-tour-integration.tsx +199 -0
- package/packages/daemon/src/tui/mirror/runtime/application-guided-tour-owner.ts +162 -0
- package/packages/daemon/src/tui/mirror/runtime/application-home-agent-roster.tsx +36 -10
- package/packages/daemon/src/tui/mirror/runtime/application-home-agent-transport.ts +69 -0
- package/packages/daemon/src/tui/mirror/runtime/application-home-agents-owner.ts +12 -2
- package/packages/daemon/src/tui/mirror/runtime/application-home-agents.ts +13 -2
- package/packages/daemon/src/tui/mirror/runtime/application-home-catalog-owner.ts +13 -3
- package/packages/daemon/src/tui/mirror/runtime/application-home-catalog.ts +172 -100
- package/packages/daemon/src/tui/mirror/runtime/application-home-experience.ts +59 -0
- package/packages/daemon/src/tui/mirror/runtime/application-home-fleet.ts +175 -0
- package/packages/daemon/src/tui/mirror/runtime/application-machine-agents.ts +9 -1
- package/packages/daemon/src/tui/mirror/runtime/application-machine-catalog.ts +19 -7
- package/packages/daemon/src/tui/mirror/runtime/application-machine-navigation.ts +285 -36
- package/packages/daemon/src/tui/mirror/runtime/application-machine-overlays.tsx +1 -1
- package/packages/daemon/src/tui/mirror/runtime/application-machine-sidebar.tsx +124 -40
- package/packages/daemon/src/tui/mirror/runtime/application-palette-preview.tsx +3 -0
- package/packages/daemon/src/tui/mirror/runtime/application-performance-log.ts +18 -6
- package/packages/daemon/src/tui/mirror/runtime/application-root-configuration.ts +12 -3
- package/packages/daemon/src/tui/mirror/runtime/application-root-v2.tsx +40 -40
- package/packages/daemon/src/tui/mirror/runtime/application-route-connection.ts +93 -1
- package/packages/daemon/src/tui/mirror/runtime/application-shell-catalog.tsx +4 -0
- package/packages/daemon/src/tui/mirror/runtime/application-shell-home.tsx +20 -1
- package/packages/daemon/src/tui/mirror/runtime/application-shell-view.tsx +10 -0
- package/packages/daemon/src/tui/mirror/runtime/application-terminal-interaction-controller.ts +177 -2
- package/packages/daemon/src/tui/mirror/runtime/application-terminal-workspace-policy.ts +62 -0
- package/packages/daemon/src/tui/mirror/runtime/application-terminal-workspace.tsx +87 -30
- package/packages/daemon/src/tui/mirror/runtime/application-theme-repaint.ts +19 -0
- package/packages/daemon/src/tui/mirror/runtime/fleet-lifecycle-client.ts +55 -0
- package/packages/daemon/src/tui/mirror/runtime/guided-tour-coach.tsx +118 -0
- package/packages/daemon/src/tui/mirror/runtime/guided-tour-storage.ts +72 -0
- package/packages/daemon/src/tui/mirror/runtime/guided-tour.ts +147 -0
- package/packages/daemon/src/tui/mirror/runtime/open-tui-generation-host.ts +1 -1
- package/packages/daemon/src/tui/mirror/runtime/resize-terminal-pane.ts +3 -0
- package/packages/daemon/src/tui/mirror/runtime/select-terminal-pane.ts +3 -0
- package/packages/daemon/src/tui/mirror/runtime/semantic-shell-viewport-resize.ts +11 -4
- package/packages/daemon/src/tui/mirror/runtime/terminal-pane-input-router.ts +15 -4
- package/packages/daemon/src/tui/mirror/workspace/application-command-description.ts +13 -2
- package/packages/daemon/src/tui/mirror/workspace/terminal-window-strip.tsx +5 -1
- package/packages/daemon-client/package.json +3 -1
- package/packages/daemon-client/src/index.ts +2 -0
- package/packages/daemon-client/src/pane-stream-client.test.ts +108 -38
- package/packages/daemon-client/src/pane-stream-client.ts +26 -0
- package/packages/daemon-client/src/scoped-tmux-server-transport.test.ts +114 -0
- package/packages/daemon-client/src/scoped-tmux-server-transport.ts +385 -0
- package/packages/daemon-client/src/tmux-server-client.test.ts +279 -0
- package/packages/daemon-client/src/tmux-server-client.ts +287 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { createSignal, onCleanup } from "solid-js";
|
|
2
|
+
import {
|
|
3
|
+
initialGuidedTourState,
|
|
4
|
+
reduceGuidedTour,
|
|
5
|
+
sameGuidedTourPractice,
|
|
6
|
+
type GuidedTourEvent,
|
|
7
|
+
type GuidedTourPractice,
|
|
8
|
+
type GuidedTourState,
|
|
9
|
+
} from "./guided-tour.ts";
|
|
10
|
+
import { readGuidedTourState, writeGuidedTourState } from "./guided-tour-storage.ts";
|
|
11
|
+
|
|
12
|
+
export interface GuidedTourObservation {
|
|
13
|
+
readonly practice: GuidedTourPractice | null;
|
|
14
|
+
readonly panes: readonly {
|
|
15
|
+
readonly id: string;
|
|
16
|
+
readonly width: number;
|
|
17
|
+
readonly height: number;
|
|
18
|
+
}[];
|
|
19
|
+
readonly focusedPane: string | null;
|
|
20
|
+
readonly surface: "home" | "terminals";
|
|
21
|
+
readonly paletteOpen: boolean;
|
|
22
|
+
readonly theme: string;
|
|
23
|
+
}
|
|
24
|
+
/** Compare confirmed snapshots; unrelated sessions and request attempts never count. */
|
|
25
|
+
export function observedGuidedTourEvent(
|
|
26
|
+
state: GuidedTourState,
|
|
27
|
+
previous: GuidedTourObservation | null,
|
|
28
|
+
current: GuidedTourObservation,
|
|
29
|
+
): GuidedTourEvent | null {
|
|
30
|
+
if (!state.active || !previous) return null;
|
|
31
|
+
if (state.step === "commands" && current.paletteOpen && !previous.paletteOpen)
|
|
32
|
+
return { type: "commands-opened" };
|
|
33
|
+
if (state.step === "theme" && current.theme !== previous.theme) return { type: "theme-changed" };
|
|
34
|
+
if (state.step === "leave" && current.surface === "home") return { type: "home-opened" };
|
|
35
|
+
if (
|
|
36
|
+
!current.practice ||
|
|
37
|
+
!sameGuidedTourPractice(state.practice, current.practice) ||
|
|
38
|
+
current.surface !== "terminals"
|
|
39
|
+
)
|
|
40
|
+
return null;
|
|
41
|
+
if (
|
|
42
|
+
state.step === "reopen" &&
|
|
43
|
+
(previous.surface !== "terminals" ||
|
|
44
|
+
!sameGuidedTourPractice(previous.practice, current.practice))
|
|
45
|
+
)
|
|
46
|
+
return { type: "reopened", practice: current.practice };
|
|
47
|
+
if (!previous.practice || !sameGuidedTourPractice(previous.practice, current.practice))
|
|
48
|
+
return null;
|
|
49
|
+
if (
|
|
50
|
+
state.step === "split" &&
|
|
51
|
+
previous.panes.length > 0 &&
|
|
52
|
+
current.panes.length > previous.panes.length &&
|
|
53
|
+
previous.panes.every((old) => current.panes.some((p) => p.id === old.id)) &&
|
|
54
|
+
current.panes.some((p) => !previous.panes.some((old) => old.id === p.id))
|
|
55
|
+
)
|
|
56
|
+
return { type: "split-confirmed", practice: current.practice };
|
|
57
|
+
if (
|
|
58
|
+
state.step === "focus" &&
|
|
59
|
+
previous.focusedPane &&
|
|
60
|
+
current.focusedPane &&
|
|
61
|
+
previous.focusedPane !== current.focusedPane &&
|
|
62
|
+
current.panes.some((p) => p.id === current.focusedPane) &&
|
|
63
|
+
current.panes.some((p) => p.id === previous.focusedPane)
|
|
64
|
+
)
|
|
65
|
+
return { type: "focus-confirmed", practice: current.practice };
|
|
66
|
+
// A divider reallocates space between the same panes: one grows while
|
|
67
|
+
// another shrinks on the same axis. Whole-app growth/shrink is not practice.
|
|
68
|
+
const samePanes =
|
|
69
|
+
current.panes.length >= 2 &&
|
|
70
|
+
current.panes.length === previous.panes.length &&
|
|
71
|
+
previous.panes.every((old) => current.panes.some((pane) => pane.id === old.id));
|
|
72
|
+
const reallocated = (["width", "height"] as const).some((axis) => {
|
|
73
|
+
const changes = current.panes.map(
|
|
74
|
+
(pane) =>
|
|
75
|
+
pane[axis] - (previous.panes.find((old) => old.id === pane.id)?.[axis] ?? pane[axis]),
|
|
76
|
+
);
|
|
77
|
+
return changes.some((change) => change > 0) && changes.some((change) => change < 0);
|
|
78
|
+
});
|
|
79
|
+
if (state.step === "resize" && samePanes && reallocated)
|
|
80
|
+
return { type: "resize-confirmed", practice: current.practice };
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
export function createApplicationGuidedTourOwner(options: {
|
|
84
|
+
createPractice(): Promise<GuidedTourPractice>;
|
|
85
|
+
openPractice(practice: GuidedTourPractice): Promise<void> | void;
|
|
86
|
+
read?: () => GuidedTourState;
|
|
87
|
+
write?: (state: GuidedTourState) => boolean;
|
|
88
|
+
}) {
|
|
89
|
+
const [state, setState] = createSignal((options.read ?? readGuidedTourState)());
|
|
90
|
+
const [busy, setBusy] = createSignal(false);
|
|
91
|
+
const [error, setError] = createSignal<string | null>(null);
|
|
92
|
+
let previous: GuidedTourObservation | null = null;
|
|
93
|
+
let disposed = false;
|
|
94
|
+
onCleanup(() => {
|
|
95
|
+
disposed = true;
|
|
96
|
+
});
|
|
97
|
+
const send = (event: GuidedTourEvent) => {
|
|
98
|
+
const next = reduceGuidedTour(state(), event);
|
|
99
|
+
if (next === state()) return;
|
|
100
|
+
setState(next);
|
|
101
|
+
if (!(options.write ?? writeGuidedTourState)(next))
|
|
102
|
+
setError("Progress could not be saved. You can still continue this tour.");
|
|
103
|
+
};
|
|
104
|
+
return {
|
|
105
|
+
state,
|
|
106
|
+
busy,
|
|
107
|
+
error,
|
|
108
|
+
label: () =>
|
|
109
|
+
state().step === "complete"
|
|
110
|
+
? "Replay walkthrough"
|
|
111
|
+
: state().step === initialGuidedTourState().step
|
|
112
|
+
? "Learn tmux-ide"
|
|
113
|
+
: "Resume walkthrough",
|
|
114
|
+
open() {
|
|
115
|
+
previous = null;
|
|
116
|
+
send({ type: state().step === "complete" ? "replay" : "resume" });
|
|
117
|
+
},
|
|
118
|
+
replay() {
|
|
119
|
+
previous = null;
|
|
120
|
+
send({ type: "replay" });
|
|
121
|
+
},
|
|
122
|
+
pause() {
|
|
123
|
+
send({ type: "pause" });
|
|
124
|
+
},
|
|
125
|
+
welcomeRead() {
|
|
126
|
+
send({ type: "welcome-read" });
|
|
127
|
+
},
|
|
128
|
+
observe(current: GuidedTourObservation) {
|
|
129
|
+
const event = observedGuidedTourEvent(state(), previous, current);
|
|
130
|
+
previous = current;
|
|
131
|
+
if (event) send(event);
|
|
132
|
+
},
|
|
133
|
+
async createPractice() {
|
|
134
|
+
if (busy() || state().step !== "practice") return;
|
|
135
|
+
setBusy(true);
|
|
136
|
+
setError(null);
|
|
137
|
+
try {
|
|
138
|
+
const practice = await options.createPractice();
|
|
139
|
+
if (disposed) return;
|
|
140
|
+
send({ type: "practice-created", practice });
|
|
141
|
+
if (state().active) await options.openPractice(practice);
|
|
142
|
+
} catch (cause) {
|
|
143
|
+
if (!disposed)
|
|
144
|
+
setError(
|
|
145
|
+
cause instanceof Error ? cause.message : "Could not create the practice session.",
|
|
146
|
+
);
|
|
147
|
+
} finally {
|
|
148
|
+
if (!disposed) setBusy(false);
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
async openPractice() {
|
|
152
|
+
const practice = state().practice;
|
|
153
|
+
if (!practice || busy()) return;
|
|
154
|
+
setError(null);
|
|
155
|
+
try {
|
|
156
|
+
if (state().active) await options.openPractice(practice);
|
|
157
|
+
} catch (cause) {
|
|
158
|
+
setError(cause instanceof Error ? cause.message : "Practice session is unavailable.");
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
@@ -14,6 +14,9 @@ import {
|
|
|
14
14
|
import type { HomeAgentSelectionSnapshot } from "./application-home-agent-selection.ts";
|
|
15
15
|
|
|
16
16
|
export interface HomeAgentRosterProps {
|
|
17
|
+
readonly filterLabel?: string;
|
|
18
|
+
readonly onCycleMachine?: () => void;
|
|
19
|
+
readonly onToggleAttention?: () => void;
|
|
17
20
|
readonly theme: SemanticThemeSnapshot;
|
|
18
21
|
readonly width: number;
|
|
19
22
|
readonly height: number;
|
|
@@ -46,6 +49,7 @@ export function HomeAgentRoster(props: HomeAgentRosterProps) {
|
|
|
46
49
|
const width = () => Math.max(0, Math.floor(props.width));
|
|
47
50
|
const height = () => Math.max(0, Math.floor(props.height));
|
|
48
51
|
const stale = (row: HomeAgentRow) =>
|
|
52
|
+
row.disabled ||
|
|
49
53
|
(props.snapshot.refreshingSessionKeys ?? []).includes(row.sessionKey) ||
|
|
50
54
|
(props.snapshot.unavailableSessionKeys ?? []).includes(row.sessionKey);
|
|
51
55
|
const showRecovery = () =>
|
|
@@ -83,9 +87,12 @@ export function HomeAgentRoster(props: HomeAgentRosterProps) {
|
|
|
83
87
|
return "No agents reported";
|
|
84
88
|
const count = props.snapshot.rows.length;
|
|
85
89
|
const attention = props.snapshot.rows.filter(
|
|
86
|
-
(row) =>
|
|
90
|
+
(row) =>
|
|
91
|
+
!stale(row) && (row.attention || row.activity === "waiting" || row.activity === "failed"),
|
|
92
|
+
).length;
|
|
93
|
+
const working = props.snapshot.rows.filter(
|
|
94
|
+
(row) => !stale(row) && row.activity === "running",
|
|
87
95
|
).length;
|
|
88
|
-
const working = props.snapshot.rows.filter((row) => row.activity === "running").length;
|
|
89
96
|
return `${count} observed ${count === 1 ? "agent" : "agents"} · ${attention} ${attention === 1 ? "needs" : "need"} attention · ${working} working`;
|
|
90
97
|
};
|
|
91
98
|
const coverage = () =>
|
|
@@ -102,13 +109,22 @@ export function HomeAgentRoster(props: HomeAgentRosterProps) {
|
|
|
102
109
|
if (visibleCount() === 0) return "Enlarge the terminal to view agents.";
|
|
103
110
|
const selected = props.snapshot.rows.find((row) => row.key === props.selection.selectedKey);
|
|
104
111
|
if (selected && stale(selected))
|
|
105
|
-
return `${selected.sessionName} · last observed; waiting for fresh signals`;
|
|
106
|
-
if (width() < 44 && selected)
|
|
112
|
+
return `${[selected.machineLabel, selected.serverLabel, selected.sessionName].filter(Boolean).join(" / ")} · last observed; waiting for fresh signals`;
|
|
113
|
+
if (selected?.machineLabel || (width() < 44 && selected))
|
|
114
|
+
return `${[selected?.machineLabel, selected?.serverLabel, selected?.sessionName].filter(Boolean).join(" / ")} · Enter open`;
|
|
107
115
|
return `${offset() + 1}–${Math.min(props.snapshot.rows.length, offset() + visibleCount())} of ${props.snapshot.rows.length} · ↑↓ select · Enter open`;
|
|
108
116
|
};
|
|
109
117
|
useKeyboardRoute((event) => {
|
|
110
118
|
if (!props.inputActive || event.eventType !== "press" || event.ctrl || event.meta) return false;
|
|
111
119
|
const key = event.name.toLowerCase();
|
|
120
|
+
const filter =
|
|
121
|
+
key === "f" ? props.onCycleMachine : key === "a" ? props.onToggleAttention : undefined;
|
|
122
|
+
if (filter) {
|
|
123
|
+
event.preventDefault();
|
|
124
|
+
event.stopPropagation();
|
|
125
|
+
filter();
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
112
128
|
const retry =
|
|
113
129
|
key === "r" &&
|
|
114
130
|
(props.snapshot.phase === "unavailable" || props.snapshot.unavailableSessions > 0) &&
|
|
@@ -160,14 +176,19 @@ export function HomeAgentRoster(props: HomeAgentRosterProps) {
|
|
|
160
176
|
<text width={width()} height={1} flexShrink={0} fg={props.theme.roles.text.muted}>
|
|
161
177
|
{clipTerminal(coverage(), width())}
|
|
162
178
|
</text>
|
|
163
|
-
<
|
|
179
|
+
<text width={width()} height={1} flexShrink={0} fg={props.theme.roles.text.secondary}>
|
|
180
|
+
{clipTerminal(
|
|
181
|
+
props.filterLabel ? `${props.filterLabel} · f machine · a attention` : "",
|
|
182
|
+
width(),
|
|
183
|
+
)}
|
|
184
|
+
</text>
|
|
164
185
|
<Show
|
|
165
186
|
when={props.snapshot.rows.length > 0}
|
|
166
187
|
fallback={<box height={Math.max(0, visibleCount() + 1)} flexShrink={0} />}
|
|
167
188
|
>
|
|
168
189
|
<text width={width()} height={1} flexShrink={0} fg={props.theme.roles.text.muted}>
|
|
169
190
|
{clipTerminal(
|
|
170
|
-
` ${padCells("AGENT", columns().agent)}${padCells("SESSION", columns().session)} ${padCells("STATUS", columns().status)}`,
|
|
191
|
+
` ${padCells("AGENT", columns().agent)}${padCells(props.filterLabel ? "MACHINE / SERVER / SESSION" : "SESSION", columns().session)} ${padCells("STATUS", columns().status)}`,
|
|
171
192
|
width(),
|
|
172
193
|
)}
|
|
173
194
|
</text>
|
|
@@ -195,10 +216,15 @@ export function HomeAgentRoster(props: HomeAgentRosterProps) {
|
|
|
195
216
|
width={width()}
|
|
196
217
|
label={
|
|
197
218
|
padCells(row().name, columns().agent) +
|
|
198
|
-
padCells(
|
|
219
|
+
padCells(
|
|
220
|
+
[row().machineLabel, row().serverLabel, row().sessionName]
|
|
221
|
+
.filter(Boolean)
|
|
222
|
+
.join(" / "),
|
|
223
|
+
columns().session,
|
|
224
|
+
)
|
|
199
225
|
}
|
|
200
226
|
detail={padCells(
|
|
201
|
-
`${row().attention ? "! " : ""}${
|
|
227
|
+
`${!stale(row()) && row().attention ? "! " : ""}${stale(row()) ? "last seen" : homeAgentStatusLabel(row().activity)}`,
|
|
202
228
|
columns().status,
|
|
203
229
|
)}
|
|
204
230
|
detailAlign="end"
|
|
@@ -206,8 +232,8 @@ export function HomeAgentRoster(props: HomeAgentRosterProps) {
|
|
|
206
232
|
selected={props.selection.selectedKey === key}
|
|
207
233
|
focused={props.inputActive && props.selection.selectedKey === key}
|
|
208
234
|
hovered={hovered() === key}
|
|
209
|
-
attention={row().attention}
|
|
210
|
-
status={statusTone(row())}
|
|
235
|
+
attention={!stale(row()) && row().attention}
|
|
236
|
+
status={stale(row()) ? "unknown" : statusTone(row())}
|
|
211
237
|
disabled={row().paneId === null || stale(row())}
|
|
212
238
|
onActivate={(source) => {
|
|
213
239
|
if (!props.inputActive || row().paneId === null || stale(row())) return;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { readApplicationDaemonInfo as readCanonicalDaemonInfo } from "./application-daemon-authority.ts";
|
|
2
2
|
import {
|
|
3
3
|
ApplicationShellResourceV2SchemaZ,
|
|
4
|
+
ApplicationShellProjectionInputV2SchemaZ,
|
|
5
|
+
TmuxServerScopeSchemaZ,
|
|
4
6
|
DaemonEventClientFrameSchemaZ,
|
|
5
7
|
DaemonEventServerFrameSchemaZ,
|
|
6
8
|
WorkspaceCatalogResourceV3SchemaZ,
|
|
@@ -66,6 +68,25 @@ export function createApplicationHomeAgentTransport(
|
|
|
66
68
|
if (!response.ok) throw new Error(`Agent observation returned HTTP ${response.status}.`);
|
|
67
69
|
return response.json();
|
|
68
70
|
};
|
|
71
|
+
if (session.server) {
|
|
72
|
+
const server = TmuxServerScopeSchemaZ.parse(session.server);
|
|
73
|
+
const base = `/api/v1/tmux-servers/${server.serverId}/${server.generation}`;
|
|
74
|
+
const raw = await get(
|
|
75
|
+
`${base}/application-shell/${encodeURIComponent(session.workspaceName ?? session.name)}?liveSessionId=${encodeURIComponent(session.liveSessionId ?? "")}`,
|
|
76
|
+
);
|
|
77
|
+
const actual = TmuxServerScopeSchemaZ.parse(raw.server);
|
|
78
|
+
if (
|
|
79
|
+
raw.version !== 1 ||
|
|
80
|
+
actual.serverId !== server.serverId ||
|
|
81
|
+
actual.generation !== server.generation
|
|
82
|
+
)
|
|
83
|
+
throw new Error("Agent server incarnation changed.");
|
|
84
|
+
return {
|
|
85
|
+
version: 2,
|
|
86
|
+
daemon,
|
|
87
|
+
resource: ApplicationShellProjectionInputV2SchemaZ.parse(raw.resource),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
69
90
|
const shell = ApplicationShellResourceV2SchemaZ.parse(
|
|
70
91
|
await get(`/api/project/${encodeURIComponent(session.name)}/application-shell?version=2`),
|
|
71
92
|
);
|
|
@@ -87,6 +108,54 @@ export function createApplicationHomeAgentTransport(
|
|
|
87
108
|
return shell;
|
|
88
109
|
},
|
|
89
110
|
connect(daemon, sessions, handlers) {
|
|
111
|
+
const scoped = sessions.filter((session) => session.server);
|
|
112
|
+
if (scoped.length > 0) {
|
|
113
|
+
// Scoped session events currently carry topology, not agent activity.
|
|
114
|
+
// Refresh metadata with a bounded cadence; never attach terminal streams.
|
|
115
|
+
// Keep legacy observations independent from scoped owner availability.
|
|
116
|
+
const pushed = sessions.filter(
|
|
117
|
+
(session) => !session.server || session.server.generation === daemon.instanceId,
|
|
118
|
+
);
|
|
119
|
+
let closed = false;
|
|
120
|
+
let pushHealthy = false;
|
|
121
|
+
const interval = setInterval(() => {
|
|
122
|
+
if (!closed)
|
|
123
|
+
for (const session of scoped) {
|
|
124
|
+
if (!pushHealthy || session.server?.generation !== daemon.instanceId)
|
|
125
|
+
handlers.invalidate(session.id);
|
|
126
|
+
}
|
|
127
|
+
}, 5_000);
|
|
128
|
+
interval.unref?.();
|
|
129
|
+
const connection = pushed.length
|
|
130
|
+
? createApplicationHomeAgentTransport(options).connect(
|
|
131
|
+
daemon,
|
|
132
|
+
pushed.map((session) => ({ ...session, server: undefined })),
|
|
133
|
+
{
|
|
134
|
+
...handlers,
|
|
135
|
+
// The root event stream is advisory for scoped reads. Failure must
|
|
136
|
+
// not stall independent scoped owners or suppress their rows.
|
|
137
|
+
ready: (unavailable) => {
|
|
138
|
+
pushHealthy = unavailable.length === 0;
|
|
139
|
+
if (!closed) for (const session of pushed) handlers.invalidate(session.id);
|
|
140
|
+
},
|
|
141
|
+
unavailable: () => {
|
|
142
|
+
pushHealthy = false;
|
|
143
|
+
if (!closed) for (const session of pushed) handlers.invalidate(session.id);
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
)
|
|
147
|
+
: null;
|
|
148
|
+
queueMicrotask(() => {
|
|
149
|
+
if (!closed) handlers.ready([]);
|
|
150
|
+
});
|
|
151
|
+
return {
|
|
152
|
+
close() {
|
|
153
|
+
closed = true;
|
|
154
|
+
clearInterval(interval);
|
|
155
|
+
connection?.close();
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
90
159
|
let closed = false;
|
|
91
160
|
let failed = false;
|
|
92
161
|
const sockets: HomeAgentEventSocket[] = [];
|
|
@@ -27,6 +27,9 @@ import {
|
|
|
27
27
|
|
|
28
28
|
export type ApplicationHomeAgentPresentation = Pick<
|
|
29
29
|
ApplicationHomeSurfaceProps,
|
|
30
|
+
| "agentFilterLabel"
|
|
31
|
+
| "onCycleAgentMachine"
|
|
32
|
+
| "onToggleAgentAttention"
|
|
30
33
|
| "agentRoster"
|
|
31
34
|
| "agentSelection"
|
|
32
35
|
| "agentInputActive"
|
|
@@ -197,6 +200,7 @@ export function createApplicationHomeAgentsOwner(options: {
|
|
|
197
200
|
|
|
198
201
|
/** Compose Home navigation with competing chrome intents; physical input stays in the root. */
|
|
199
202
|
export function createApplicationHomeNavigationOwner(options: {
|
|
203
|
+
readonly fleetHome?: ApplicationHomeAgentPresentation;
|
|
200
204
|
readonly fleetCommands?: () => readonly ApplicationPaletteCommand[];
|
|
201
205
|
readonly openFleet?: (
|
|
202
206
|
command: Exclude<ApplicationPaletteCommand, string>,
|
|
@@ -236,7 +240,7 @@ export function createApplicationHomeNavigationOwner(options: {
|
|
|
236
240
|
);
|
|
237
241
|
const homeAgents = createApplicationHomeAgentsOwner({
|
|
238
242
|
catalog: options.catalog.snapshot,
|
|
239
|
-
active: () => options.activeSurface() === "home",
|
|
243
|
+
active: () => !options.fleetHome && options.activeSurface() === "home",
|
|
240
244
|
inputActive: () =>
|
|
241
245
|
options.activeSurface() === "home" &&
|
|
242
246
|
!paneRename.draft() &&
|
|
@@ -307,5 +311,11 @@ export function createApplicationHomeNavigationOwner(options: {
|
|
|
307
311
|
onNavigationIntent: homeAgents.cancel,
|
|
308
312
|
});
|
|
309
313
|
const paletteCommandList = paletteCommands.commands;
|
|
310
|
-
return {
|
|
314
|
+
return {
|
|
315
|
+
homeAgents: options.fleetHome ? { ...homeAgents, presentation: options.fleetHome } : homeAgents,
|
|
316
|
+
paneRename,
|
|
317
|
+
paletteCommands,
|
|
318
|
+
paletteCommandList,
|
|
319
|
+
openAgent,
|
|
320
|
+
};
|
|
311
321
|
}
|
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
AgentActivity,
|
|
3
|
+
ApplicationShellResourceV2,
|
|
4
|
+
TmuxServerScope,
|
|
5
|
+
} from "@tmux-ide/contracts";
|
|
2
6
|
|
|
3
7
|
import type { ApplicationHomeCatalogSession } from "./application-home-catalog.ts";
|
|
4
8
|
import { terminalAgentStatusLabel } from "./application-terminal-workspace-policy.ts";
|
|
5
9
|
|
|
6
10
|
export interface HomeAgentRow {
|
|
7
11
|
readonly key: string;
|
|
12
|
+
readonly machineId?: string;
|
|
13
|
+
readonly machineLabel?: string;
|
|
14
|
+
readonly server?: TmuxServerScope;
|
|
15
|
+
readonly serverLabel?: string;
|
|
16
|
+
readonly disabled?: boolean;
|
|
8
17
|
readonly sessionKey: string;
|
|
9
18
|
readonly sessionName: string;
|
|
10
19
|
readonly liveSessionId: string;
|
|
@@ -56,11 +65,13 @@ export function projectHomeAgentRows(
|
|
|
56
65
|
return shell.resource.workspace.sidebar.agents.map((agent) => ({
|
|
57
66
|
key: `${session.id}\u0000${agent.id}`,
|
|
58
67
|
sessionKey: session.id,
|
|
68
|
+
server: session.server,
|
|
69
|
+
serverLabel: session.serverLabel,
|
|
59
70
|
sessionName: session.name,
|
|
60
71
|
// Older catalog fixtures/callers retain a generation-qualified incarnation
|
|
61
72
|
// key even when they do not expose the separately named wire field.
|
|
62
73
|
liveSessionId: session.liveSessionId ?? session.id,
|
|
63
|
-
daemonInstanceId: shell.daemon.instanceId,
|
|
74
|
+
daemonInstanceId: session.server?.generation ?? shell.daemon.instanceId,
|
|
64
75
|
agentId: agent.id,
|
|
65
76
|
paneId: agent.paneId,
|
|
66
77
|
windowId:
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
moveHomeCatalogSelection,
|
|
7
7
|
selectedHomeCatalogIndex,
|
|
8
8
|
type ApplicationHomeCatalog,
|
|
9
|
+
type ApplicationHomeCatalogSession,
|
|
9
10
|
type ApplicationHomeCatalogSnapshot,
|
|
10
11
|
} from "./application-home-catalog.ts";
|
|
11
12
|
import { createFleetSession } from "./fleet-lifecycle-client.ts";
|
|
@@ -26,6 +27,7 @@ export interface ApplicationHomeCatalogOwnerOptions {
|
|
|
26
27
|
readonly lifecycle: Pick<TuiApplicationLifecycle, "registerCloser">;
|
|
27
28
|
readonly automaticOpen: boolean;
|
|
28
29
|
readonly automaticOpenAllowed?: () => boolean;
|
|
30
|
+
readonly openCatalogSession?: (session: ApplicationHomeCatalogSession) => Promise<unknown> | void;
|
|
29
31
|
readonly startGeneration: (sessionName: string) => Promise<unknown> | void;
|
|
30
32
|
readonly setNote?: (note: string | null) => void;
|
|
31
33
|
readonly catalog?: ApplicationHomeCatalog;
|
|
@@ -85,7 +87,12 @@ export function createApplicationHomeCatalogOwner(
|
|
|
85
87
|
const currentSessions = next.sessions;
|
|
86
88
|
if (automaticOpen && next.phase === "live") {
|
|
87
89
|
automaticOpen = false;
|
|
88
|
-
if (currentSessions.length === 1)
|
|
90
|
+
if (currentSessions.length === 1) {
|
|
91
|
+
const session = currentSessions[0]!;
|
|
92
|
+
void (options.openCatalogSession
|
|
93
|
+
? options.openCatalogSession(session)
|
|
94
|
+
: options.startGeneration(session.name));
|
|
95
|
+
}
|
|
89
96
|
}
|
|
90
97
|
if (currentSessions.length === 0) {
|
|
91
98
|
if (current !== null) setSelectedSessionId(null);
|
|
@@ -120,8 +127,11 @@ export function createApplicationHomeCatalogOwner(
|
|
|
120
127
|
return true;
|
|
121
128
|
}
|
|
122
129
|
if (name === "return" || name === "enter") {
|
|
123
|
-
const
|
|
124
|
-
if (
|
|
130
|
+
const session = sessions()[selectedSessionIndex()];
|
|
131
|
+
if (session)
|
|
132
|
+
void (options.openCatalogSession
|
|
133
|
+
? options.openCatalogSession(session)
|
|
134
|
+
: options.startGeneration(session.name));
|
|
125
135
|
return true;
|
|
126
136
|
}
|
|
127
137
|
return false;
|