tmux-ide 2.9.0-beta.21 → 2.9.0-beta.22
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 +52253 -49070
- package/package.json +1 -1
- 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/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 +345 -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 +16 -10
- 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-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-machine-catalog.js +12 -2
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-navigation.js +190 -31
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-sidebar.jsx +91 -37
- 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-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/fleet-lifecycle-client.js +42 -1
- package/packages/daemon/dist/tui/mirror/runtime/open-tui-generation-host.js +1 -1
- package/packages/daemon/dist/tui/mirror/runtime/select-terminal-pane.js +2 -1
- 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 +468 -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 +16 -8
- 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-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-machine-catalog.ts +19 -7
- package/packages/daemon/src/tui/mirror/runtime/application-machine-navigation.ts +263 -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-root-configuration.ts +12 -3
- package/packages/daemon/src/tui/mirror/runtime/application-root-v2.tsx +24 -24
- package/packages/daemon/src/tui/mirror/runtime/application-route-connection.ts +93 -1
- package/packages/daemon/src/tui/mirror/runtime/application-shell-view.tsx +4 -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/fleet-lifecycle-client.ts +52 -0
- package/packages/daemon/src/tui/mirror/runtime/open-tui-generation-host.ts +1 -1
- package/packages/daemon/src/tui/mirror/runtime/select-terminal-pane.ts +3 -0
- 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
|
@@ -7,6 +7,16 @@ import { createSignal, onCleanup } from "solid-js";
|
|
|
7
7
|
import { applicationMachineAuthorityManager as manager } from "./application-machine-authority.js";
|
|
8
8
|
import { createApplicationMachineCatalog } from "./application-machine-catalog.js";
|
|
9
9
|
import { ephemeralMachineProfile } from "./application-machine-startup.js";
|
|
10
|
+
/** Exact attached authority; an unscoped default connection still proves its incarnation. */
|
|
11
|
+
function attachedMachineNavigationTarget(active) {
|
|
12
|
+
if (active?.status !== "live" || !active.connection?.liveSessionId || !active.daemonGeneration)
|
|
13
|
+
return null;
|
|
14
|
+
return {
|
|
15
|
+
liveSessionId: active.connection.liveSessionId,
|
|
16
|
+
daemonGeneration: active.daemonGeneration,
|
|
17
|
+
...(active.connection.server ? { server: active.connection.server } : {}),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
10
20
|
export function createApplicationMachineNavigation(options) {
|
|
11
21
|
const preferences = createApplicationFleetPreferences({
|
|
12
22
|
onError: options.setNote,
|
|
@@ -31,10 +41,12 @@ export function createApplicationMachineNavigation(options) {
|
|
|
31
41
|
const [snapshot, setSnapshot] = createSignal(catalog.getSnapshot());
|
|
32
42
|
const [focused, setFocused] = createSignal(false);
|
|
33
43
|
const [switching, setSwitching] = createSignal(false);
|
|
44
|
+
const [serverFocus, setServerFocus] = createSignal(null);
|
|
34
45
|
const [attentionOnly, setAttentionOnly] = createSignal(false);
|
|
35
46
|
const showSwitcher = (attention) => {
|
|
36
47
|
navigation++;
|
|
37
48
|
options.cancelOpen();
|
|
49
|
+
setServerFocus(null);
|
|
38
50
|
setAttentionOnly(attention);
|
|
39
51
|
setFocused(false);
|
|
40
52
|
setSwitching(true);
|
|
@@ -42,39 +54,80 @@ export function createApplicationMachineNavigation(options) {
|
|
|
42
54
|
const [adding, setAdding] = createSignal(false);
|
|
43
55
|
const [alias, setAlias] = createSignal("");
|
|
44
56
|
const [error, setError] = createSignal(null);
|
|
45
|
-
|
|
57
|
+
let attemptStartup = null;
|
|
58
|
+
const stop = catalog.subscribe((value) => {
|
|
59
|
+
setSnapshot(value);
|
|
60
|
+
attemptStartup?.();
|
|
61
|
+
});
|
|
46
62
|
let navigation = 0;
|
|
63
|
+
const [activeSessionKey, setActiveSessionKey] = createSignal(null);
|
|
47
64
|
const [tabRevision, setTabRevision] = createSignal(0);
|
|
48
65
|
const history = [...saved().recent].reverse();
|
|
49
66
|
let historyIndex = history.length - 1;
|
|
50
|
-
const select = (id, expectedLiveSessionId) => {
|
|
67
|
+
const select = (id, expectedLiveSessionId, server) => {
|
|
51
68
|
if (!manager.getMachine(id))
|
|
52
69
|
return false;
|
|
53
70
|
if (manager.snapshot().selectedMachineId !== id) {
|
|
54
71
|
navigation++;
|
|
55
72
|
options.cancelOpen();
|
|
56
73
|
// Synchronously remove the old input/shell owner before changing the route.
|
|
57
|
-
|
|
74
|
+
if (server)
|
|
75
|
+
options.resetWorkspace(id, expectedLiveSessionId, server);
|
|
76
|
+
else
|
|
77
|
+
options.resetWorkspace(id, expectedLiveSessionId);
|
|
58
78
|
manager.select(id);
|
|
59
79
|
}
|
|
60
80
|
else if (expectedLiveSessionId) {
|
|
61
|
-
options.
|
|
81
|
+
const attached = options.attachedTarget?.() ??
|
|
82
|
+
attachedMachineNavigationTarget(options.attachedGeneration?.() ?? null);
|
|
83
|
+
const sameAuthority = attached?.liveSessionId === expectedLiveSessionId &&
|
|
84
|
+
(server
|
|
85
|
+
? attached.server
|
|
86
|
+
? attached.server.serverId === server.serverId &&
|
|
87
|
+
attached.server.generation === server.generation
|
|
88
|
+
: attached.daemonGeneration === server.generation &&
|
|
89
|
+
manager.getMachine(id)?.read()?.instanceId === server.generation
|
|
90
|
+
: !attached.server);
|
|
91
|
+
if (sameAuthority)
|
|
92
|
+
return true;
|
|
93
|
+
if (server)
|
|
94
|
+
options.resetWorkspace(id, expectedLiveSessionId, server);
|
|
95
|
+
else
|
|
96
|
+
options.resetWorkspace(id, expectedLiveSessionId);
|
|
62
97
|
}
|
|
63
98
|
return true;
|
|
64
99
|
};
|
|
65
|
-
const open = async (id, name, source, remember = true, expectedLiveSessionId) => {
|
|
100
|
+
const open = async (id, name, source, remember = true, expectedLiveSessionId, server, sessionKey) => {
|
|
66
101
|
// A same-machine session click supersedes any pending exact-agent focus too.
|
|
67
102
|
if (manager.snapshot().selectedMachineId === id)
|
|
68
103
|
options.cancelOpen();
|
|
69
104
|
// A sidebar selection can leave a tab whose route is pinned to a different
|
|
70
105
|
// live incarnation on this same machine. Select a new exact owner before
|
|
71
106
|
// preparation; never apply that old tab's identity fence to the new name.
|
|
72
|
-
if (
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
107
|
+
if (manager.getMachine(id)?.endpoint().state !== "ready") {
|
|
108
|
+
select(id);
|
|
109
|
+
options.setNote("This machine is disconnected. Select the session after reconnecting.");
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
const candidates = snapshot()
|
|
113
|
+
.groups.find((group) => group.id === id || group.routeIds?.includes(id))
|
|
114
|
+
?.sessions.filter((row) => row.name === name &&
|
|
115
|
+
!row.disabled &&
|
|
116
|
+
(!sessionKey || row.id === sessionKey) &&
|
|
117
|
+
(!expectedLiveSessionId || row.liveSessionId === expectedLiveSessionId) &&
|
|
118
|
+
(!server ||
|
|
119
|
+
(row.server?.serverId === server.serverId &&
|
|
120
|
+
row.server.generation === server.generation))) ?? [];
|
|
121
|
+
if (candidates.length !== 1) {
|
|
122
|
+
options.setNote("Select the session under its tmux server; this target is ambiguous or has changed.");
|
|
123
|
+
return false;
|
|
76
124
|
}
|
|
77
|
-
|
|
125
|
+
const selected = candidates[0];
|
|
126
|
+
const sameSelection = activeSessionKey() === selected.id && options.sessionName() === name;
|
|
127
|
+
setActiveSessionKey(selected.id);
|
|
128
|
+
expectedLiveSessionId = selected.liveSessionId;
|
|
129
|
+
server = selected.server;
|
|
130
|
+
if (!select(id, sameSelection ? undefined : expectedLiveSessionId, server))
|
|
78
131
|
return;
|
|
79
132
|
const token = ++navigation;
|
|
80
133
|
setFocused(false);
|
|
@@ -92,15 +145,21 @@ export function createApplicationMachineNavigation(options) {
|
|
|
92
145
|
if (token === navigation) {
|
|
93
146
|
const session = snapshot()
|
|
94
147
|
.groups.find((group) => group.id === id || group.routeIds?.includes(id))
|
|
95
|
-
?.sessions.find((row) => row.
|
|
148
|
+
?.sessions.find((row) => row.id === selected.id && !row.disabled);
|
|
96
149
|
if (session) {
|
|
97
150
|
if (session.liveSessionId)
|
|
98
151
|
tabs.remember({
|
|
99
|
-
key: JSON.stringify([
|
|
152
|
+
key: JSON.stringify([
|
|
153
|
+
id,
|
|
154
|
+
session.server?.serverId,
|
|
155
|
+
session.server?.generation,
|
|
156
|
+
session.liveSessionId,
|
|
157
|
+
]),
|
|
158
|
+
server: session.server,
|
|
100
159
|
machineId: id,
|
|
101
160
|
liveSessionId: session.liveSessionId,
|
|
102
161
|
label: session.name,
|
|
103
|
-
hostLabel: manager.getMachine(id)?.label ?? id
|
|
162
|
+
hostLabel: `${manager.getMachine(id)?.label ?? id}${session.serverLabel ? ` / ${session.serverLabel}` : ""}`,
|
|
104
163
|
});
|
|
105
164
|
preferences.change({ type: "visit", key: session.id });
|
|
106
165
|
if (remember && history[historyIndex] !== session.id) {
|
|
@@ -118,26 +177,39 @@ export function createApplicationMachineNavigation(options) {
|
|
|
118
177
|
if (manager.getMachine(target.machineId)?.endpoint().state !== "ready")
|
|
119
178
|
return null;
|
|
120
179
|
const group = snapshot().groups.find((g) => g.id === target.machineId || g.routeIds?.includes(target.machineId));
|
|
121
|
-
const row = group?.sessions.find((s) => s.liveSessionId === target.liveSessionId &&
|
|
180
|
+
const row = group?.sessions.find((s) => s.liveSessionId === target.liveSessionId &&
|
|
181
|
+
!s.disabled &&
|
|
182
|
+
s.server?.serverId === target.server?.serverId &&
|
|
183
|
+
s.server?.generation === target.server?.generation);
|
|
122
184
|
return row ? { ...target, label: row.name } : null;
|
|
123
185
|
};
|
|
124
186
|
const tabs = createFleetTabs({
|
|
125
187
|
resolve: resolveTab,
|
|
126
188
|
retireActive: () => {
|
|
189
|
+
setActiveSessionKey(null);
|
|
127
190
|
navigation++;
|
|
128
191
|
options.cancelOpen();
|
|
129
192
|
options.resetWorkspace(manager.snapshot().selectedMachineId);
|
|
130
193
|
options.setSurface("home");
|
|
131
194
|
},
|
|
132
|
-
open: async (target) => !!(await open(target.machineId, target.label, "keyboard", true, target.liveSessionId)),
|
|
195
|
+
open: async (target) => !!(await open(target.machineId, target.label, "keyboard", true, target.liveSessionId, target.server)),
|
|
133
196
|
publish: () => setTabRevision((value) => value + 1),
|
|
134
197
|
unavailable: () => options.setNote("That tab's session is unavailable or has been replaced."),
|
|
135
198
|
});
|
|
199
|
+
const isDefaultSession = (machineId, session) => !session.server ||
|
|
200
|
+
session.server.generation === manager.getMachine(machineId)?.read()?.instanceId;
|
|
136
201
|
const sidebar = {
|
|
137
202
|
groups: () => snapshot().groups.map((group) => ({
|
|
138
203
|
...group,
|
|
139
|
-
agentsAvailable:
|
|
140
|
-
|
|
204
|
+
agentsAvailable: group.sessions.some((session) => isDefaultSession(group.id, session)) &&
|
|
205
|
+
(agentGroups().find((value) => value.machineId === group.id)?.available ?? false),
|
|
206
|
+
agents: !group.sessions.some((session) => isDefaultSession(group.id, session))
|
|
207
|
+
? []
|
|
208
|
+
: (agentGroups().find((value) => value.machineId === group.id)?.agents ?? []).map((agent) => ({
|
|
209
|
+
...agent,
|
|
210
|
+
server: group.sessions.find((session) => session.liveSessionId === agent.liveSessionId &&
|
|
211
|
+
isDefaultSession(group.id, session))?.server,
|
|
212
|
+
})),
|
|
141
213
|
})),
|
|
142
214
|
tabs: () => {
|
|
143
215
|
tabRevision();
|
|
@@ -158,6 +230,7 @@ export function createApplicationMachineNavigation(options) {
|
|
|
158
230
|
onCollapse: (key, enabled) => preferences.change({ type: "collapse", key, enabled }),
|
|
159
231
|
activeMachineId: () => snapshot().selectedMachineId,
|
|
160
232
|
activeSessionName: options.sessionName,
|
|
233
|
+
activeSessionKey,
|
|
161
234
|
activePaneId: options.activePaneId,
|
|
162
235
|
focused,
|
|
163
236
|
onFocus: () => {
|
|
@@ -166,8 +239,14 @@ export function createApplicationMachineNavigation(options) {
|
|
|
166
239
|
setFocused(true);
|
|
167
240
|
},
|
|
168
241
|
onBlur: () => setFocused(false),
|
|
169
|
-
onOpen: (id, name, source) => void open(id, name, source),
|
|
242
|
+
onOpen: (id, name, source, sessionKey) => void open(id, name, source, true, undefined, undefined, sessionKey),
|
|
170
243
|
onOpenAgent: (id, sessionName, paneId, source) => {
|
|
244
|
+
const sessions = snapshot().groups.find((group) => group.id === id)?.sessions ?? [];
|
|
245
|
+
const selectedSession = sessions.find((session) => session.name === sessionName && isDefaultSession(id, session));
|
|
246
|
+
if (sessions.length && !selectedSession) {
|
|
247
|
+
options.setNote("Open this agent through its server session first.");
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
171
250
|
const row = agentGroups()
|
|
172
251
|
.find((group) => group.machineId === id)
|
|
173
252
|
?.agents.find((agent) => agent.sessionName === sessionName && agent.paneId === paneId);
|
|
@@ -176,12 +255,18 @@ export function createApplicationMachineNavigation(options) {
|
|
|
176
255
|
return;
|
|
177
256
|
}
|
|
178
257
|
options.cancelOpen();
|
|
179
|
-
if (!select(id))
|
|
258
|
+
if (!select(id, selectedSession?.liveSessionId, selectedSession?.server))
|
|
180
259
|
return;
|
|
260
|
+
if (selectedSession)
|
|
261
|
+
setActiveSessionKey(selectedSession.id);
|
|
181
262
|
navigation++;
|
|
182
263
|
setFocused(false);
|
|
183
264
|
void options.openAgent?.(row, source);
|
|
184
265
|
},
|
|
266
|
+
onSelectServer: (machineId, server) => {
|
|
267
|
+
showSwitcher(false);
|
|
268
|
+
setServerFocus({ machineId, server });
|
|
269
|
+
},
|
|
185
270
|
onSelectMachine: (id) => {
|
|
186
271
|
tabs.suspend();
|
|
187
272
|
navigation++;
|
|
@@ -217,6 +302,7 @@ export function createApplicationMachineNavigation(options) {
|
|
|
217
302
|
catalog,
|
|
218
303
|
agents,
|
|
219
304
|
selectedMachineId: () => manager.snapshot().selectedMachineId,
|
|
305
|
+
openSelectedSession: (name, source, key) => open(manager.snapshot().selectedMachineId, name, source, true, undefined, undefined, key),
|
|
220
306
|
automaticOpen: manager.snapshot().machines.length === 1,
|
|
221
307
|
automaticOpenAllowed: () => navigation === 0 && manager.snapshot().selectedMachineId === "local",
|
|
222
308
|
sidebar,
|
|
@@ -235,7 +321,7 @@ export function createApplicationMachineNavigation(options) {
|
|
|
235
321
|
options.setNote("That session is unavailable or has been replaced.");
|
|
236
322
|
return;
|
|
237
323
|
}
|
|
238
|
-
if (await open(group.id, session.name, "keyboard", false))
|
|
324
|
+
if (await open(group.id, session.name, "keyboard", false, session.liveSessionId, session.server, session.id))
|
|
239
325
|
historyIndex = next;
|
|
240
326
|
},
|
|
241
327
|
cycleTab(direction, close = false) {
|
|
@@ -253,10 +339,20 @@ export function createApplicationMachineNavigation(options) {
|
|
|
253
339
|
void tabs.activate(target.key);
|
|
254
340
|
},
|
|
255
341
|
closeSwitcher: () => setSwitching(false),
|
|
342
|
+
switcherFilter: (command) => {
|
|
343
|
+
const focus = serverFocus();
|
|
344
|
+
return (!focus ||
|
|
345
|
+
(typeof command === "object" &&
|
|
346
|
+
command.fleet?.machineId === focus.machineId &&
|
|
347
|
+
command.fleet?.server?.serverId === focus.server.serverId &&
|
|
348
|
+
command.fleet?.server?.generation === focus.server.generation));
|
|
349
|
+
},
|
|
256
350
|
paletteCommands() {
|
|
257
351
|
const groupsById = new Map(agentGroups().map((group) => [group.machineId, group]));
|
|
258
352
|
return snapshot().groups.flatMap((group) => {
|
|
259
|
-
const agentGroup =
|
|
353
|
+
const agentGroup = !group.sessions.some((session) => isDefaultSession(group.id, session))
|
|
354
|
+
? undefined
|
|
355
|
+
: groupsById.get(group.id);
|
|
260
356
|
const agentsBySession = new Map();
|
|
261
357
|
for (const agent of agentGroup?.agents ?? []) {
|
|
262
358
|
if (!agent.liveSessionId || !agent.paneId)
|
|
@@ -276,8 +372,9 @@ export function createApplicationMachineNavigation(options) {
|
|
|
276
372
|
? saved().recent.indexOf(session.id)
|
|
277
373
|
: 1000,
|
|
278
374
|
liveSessionId: session.liveSessionId,
|
|
279
|
-
|
|
280
|
-
|
|
375
|
+
server: session.server,
|
|
376
|
+
hostLabel: `${group.label}${session.serverLabel ? ` / ${session.serverLabel}` : ""}`,
|
|
377
|
+
agentActivities: isDefaultSession(group.id, session) && agentGroup?.available
|
|
281
378
|
? (agentsBySession.get(session.liveSessionId) ?? []).map((a) => ({
|
|
282
379
|
paneId: a.paneId,
|
|
283
380
|
attention: a.attention,
|
|
@@ -289,7 +386,9 @@ export function createApplicationMachineNavigation(options) {
|
|
|
289
386
|
};
|
|
290
387
|
return [
|
|
291
388
|
{ kind: "open-session", sessionName: session.name, label: session.name, fleet },
|
|
292
|
-
...(
|
|
389
|
+
...(isDefaultSession(group.id, session)
|
|
390
|
+
? (agentsBySession.get(session.liveSessionId) ?? [])
|
|
391
|
+
: []).map((a) => ({
|
|
293
392
|
kind: "jump-agent",
|
|
294
393
|
sessionName: session.name,
|
|
295
394
|
paneId: a.paneId,
|
|
@@ -311,6 +410,21 @@ export function createApplicationMachineNavigation(options) {
|
|
|
311
410
|
disabled: group.state !== "ready",
|
|
312
411
|
},
|
|
313
412
|
},
|
|
413
|
+
...(group.servers ?? []).map((server) => ({
|
|
414
|
+
kind: "open-machine",
|
|
415
|
+
sessionName: "",
|
|
416
|
+
label: `${server.label} · ${server.serverId.slice(-6)} · New session ^N`,
|
|
417
|
+
fleet: {
|
|
418
|
+
machineId: group.id,
|
|
419
|
+
hostLabel: `${group.label} / ${server.label}`,
|
|
420
|
+
liveSessionId: "",
|
|
421
|
+
daemonInstanceId: daemon?.instanceId ?? "",
|
|
422
|
+
disabled: group.state !== "ready" || server.state !== "online",
|
|
423
|
+
...(server.state === "online"
|
|
424
|
+
? { server: { serverId: server.serverId, generation: server.generation } }
|
|
425
|
+
: {}),
|
|
426
|
+
},
|
|
427
|
+
})),
|
|
314
428
|
...sessions,
|
|
315
429
|
];
|
|
316
430
|
});
|
|
@@ -321,7 +435,9 @@ export function createApplicationMachineNavigation(options) {
|
|
|
321
435
|
const target = command.fleet;
|
|
322
436
|
const session = snapshot()
|
|
323
437
|
.groups.find((g) => g.id === target.machineId)
|
|
324
|
-
?.sessions.find((s) => s.liveSessionId === target.liveSessionId
|
|
438
|
+
?.sessions.find((s) => s.liveSessionId === target.liveSessionId &&
|
|
439
|
+
s.server?.serverId === target.server?.serverId &&
|
|
440
|
+
s.server?.generation === target.server?.generation);
|
|
325
441
|
if (session)
|
|
326
442
|
preferences.change({
|
|
327
443
|
type: "favorite",
|
|
@@ -335,6 +451,19 @@ export function createApplicationMachineNavigation(options) {
|
|
|
335
451
|
return;
|
|
336
452
|
const handle = manager.getMachine(target.machineId);
|
|
337
453
|
if (command.kind === "open-machine") {
|
|
454
|
+
if (target.server) {
|
|
455
|
+
const descriptor = snapshot()
|
|
456
|
+
.groups.find((group) => group.id === target.machineId)
|
|
457
|
+
?.servers?.find((server) => server.serverId === target.server?.serverId &&
|
|
458
|
+
server.generation === target.server.generation &&
|
|
459
|
+
server.state === "online");
|
|
460
|
+
if (!descriptor || handle?.read()?.instanceId !== target.daemonInstanceId) {
|
|
461
|
+
options.setNote("That server has changed. Select it again.");
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
sidebar.onSelectServer?.(target.machineId, target.server);
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
338
467
|
if (handle?.endpoint().state === "ready" &&
|
|
339
468
|
handle.read()?.instanceId === target.daemonInstanceId)
|
|
340
469
|
sidebar.onSelectMachine(target.machineId, source);
|
|
@@ -343,7 +472,10 @@ export function createApplicationMachineNavigation(options) {
|
|
|
343
472
|
return;
|
|
344
473
|
}
|
|
345
474
|
const group = snapshot().groups.find((g) => g.id === target.machineId || g.routeIds?.includes(target.machineId));
|
|
346
|
-
const session = group?.sessions.find((s) => s.liveSessionId === target.liveSessionId &&
|
|
475
|
+
const session = group?.sessions.find((s) => s.liveSessionId === target.liveSessionId &&
|
|
476
|
+
!s.disabled &&
|
|
477
|
+
s.server?.serverId === target.server?.serverId &&
|
|
478
|
+
s.server?.generation === target.server?.generation);
|
|
347
479
|
if (!session ||
|
|
348
480
|
handle?.endpoint().state !== "ready" ||
|
|
349
481
|
handle.read()?.instanceId !== target.daemonInstanceId) {
|
|
@@ -353,7 +485,7 @@ export function createApplicationMachineNavigation(options) {
|
|
|
353
485
|
if (command.kind === "jump-agent")
|
|
354
486
|
sidebar.onOpenAgent?.(target.machineId, session.name, command.paneId, source);
|
|
355
487
|
else
|
|
356
|
-
await open(target.machineId, session.name, source, true, target.liveSessionId);
|
|
488
|
+
await open(target.machineId, session.name, source, true, target.liveSessionId, target.server);
|
|
357
489
|
},
|
|
358
490
|
adding,
|
|
359
491
|
alias,
|
|
@@ -402,17 +534,44 @@ export function createApplicationMachineNavigation(options) {
|
|
|
402
534
|
setError(cause instanceof Error ? cause.message : "Could not add this machine.");
|
|
403
535
|
}
|
|
404
536
|
},
|
|
405
|
-
start(target) {
|
|
537
|
+
start(target, serverId) {
|
|
406
538
|
agents.start();
|
|
407
539
|
catalog.start();
|
|
408
|
-
if (!target)
|
|
540
|
+
if (!target && !serverId)
|
|
409
541
|
return;
|
|
410
542
|
const id = manager.snapshot().selectedMachineId;
|
|
411
543
|
const token = navigation;
|
|
544
|
+
let machineReady = false;
|
|
545
|
+
const attempt = () => {
|
|
546
|
+
if (!machineReady)
|
|
547
|
+
return;
|
|
548
|
+
if (token !== navigation) {
|
|
549
|
+
attemptStartup = null;
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
const group = snapshot().groups.find((group) => group.id === id);
|
|
553
|
+
if (group?.state !== "ready")
|
|
554
|
+
return;
|
|
555
|
+
const matches = group.sessions.filter((row) => !row.disabled &&
|
|
556
|
+
(!serverId || row.server?.serverId === serverId) &&
|
|
557
|
+
(!target || row.name === target));
|
|
558
|
+
if (matches.length !== 1) {
|
|
559
|
+
options.setNote(matches.length
|
|
560
|
+
? "Select a session under the requested tmux server."
|
|
561
|
+
: "Requested server or session is unavailable. Select it again.");
|
|
562
|
+
attemptStartup = null;
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
const row = matches[0];
|
|
566
|
+
attemptStartup = null;
|
|
567
|
+
void open(id, row.name, "keyboard", true, row.liveSessionId, row.server, row.id);
|
|
568
|
+
};
|
|
569
|
+
attemptStartup = attempt;
|
|
412
570
|
void manager.getMachine(id)?.ready.then((ready) => {
|
|
413
|
-
|
|
414
|
-
|
|
571
|
+
machineReady = ready;
|
|
572
|
+
attemptStartup?.();
|
|
415
573
|
});
|
|
574
|
+
attempt();
|
|
416
575
|
},
|
|
417
576
|
};
|
|
418
577
|
}
|
|
@@ -43,9 +43,15 @@ export function ApplicationMachineSidebar(props) {
|
|
|
43
43
|
? row.group.state === "ready" &&
|
|
44
44
|
!row.agent.disabled &&
|
|
45
45
|
row.agent.sessionName === props.model.activeSessionName() &&
|
|
46
|
+
(!props.model.activeSessionKey ||
|
|
47
|
+
row.group.sessions.some((session) => session.id === props.model.activeSessionKey?.() &&
|
|
48
|
+
session.server?.serverId === row.agent?.server?.serverId &&
|
|
49
|
+
session.server?.generation === row.agent?.server?.generation)) &&
|
|
46
50
|
row.agent.paneId !== null &&
|
|
47
51
|
row.agent.paneId === props.model.activePaneId?.()
|
|
48
|
-
:
|
|
52
|
+
: props.model.activeSessionKey
|
|
53
|
+
? row.session?.id === props.model.activeSessionKey()
|
|
54
|
+
: row.session?.name === props.model.activeSessionName());
|
|
49
55
|
const preferenceKey = (group) => group.environmentId ?? group.id;
|
|
50
56
|
const rows = createMemo(() => props.model.groups().flatMap((group) => [
|
|
51
57
|
{ key: JSON.stringify([group.id]), group },
|
|
@@ -53,12 +59,29 @@ export function ApplicationMachineSidebar(props) {
|
|
|
53
59
|
.filter((session) => !collapsed().has(preferenceKey(group)) ||
|
|
54
60
|
props.model.favorites?.().includes(session.id) ||
|
|
55
61
|
(group.id === props.model.activeMachineId() &&
|
|
56
|
-
|
|
57
|
-
|
|
62
|
+
(props.model.activeSessionKey
|
|
63
|
+
? session.id === props.model.activeSessionKey()
|
|
64
|
+
: session.name === props.model.activeSessionName())))
|
|
65
|
+
.map((session, index, sessions) => ({
|
|
66
|
+
serverHeading: (group.servers?.length ??
|
|
67
|
+
new Set(group.sessions.map((row) => row.server?.serverId)).size) > 1 &&
|
|
68
|
+
session.server &&
|
|
69
|
+
(index === 0 || sessions[index - 1]?.server?.serverId !== session.server.serverId)
|
|
70
|
+
? `${session.serverLabel ?? "Server"} · ${session.server.serverId.slice(-6)}`
|
|
71
|
+
: undefined,
|
|
58
72
|
key: JSON.stringify([group.id, "session", session.id]),
|
|
59
73
|
group,
|
|
60
74
|
session,
|
|
61
75
|
})),
|
|
76
|
+
...(collapsed().has(preferenceKey(group))
|
|
77
|
+
? []
|
|
78
|
+
: (group.servers ?? [])
|
|
79
|
+
.filter((server) => !group.sessions.some((session) => session.server?.serverId === server.serverId))
|
|
80
|
+
.map((server) => ({
|
|
81
|
+
key: JSON.stringify([group.id, "server", server.serverId]),
|
|
82
|
+
group,
|
|
83
|
+
server,
|
|
84
|
+
}))),
|
|
62
85
|
...(!collapsed().has(preferenceKey(group))
|
|
63
86
|
? (group.agents ?? []).map((agent, index) => ({
|
|
64
87
|
key: JSON.stringify([group.id, "agent", agent.id]),
|
|
@@ -79,7 +102,10 @@ export function ApplicationMachineSidebar(props) {
|
|
|
79
102
|
? group
|
|
80
103
|
: null;
|
|
81
104
|
};
|
|
82
|
-
const activity = (row) => summarizeFleetActivity((row.group.agents ?? []).filter((agent) => !row.session ||
|
|
105
|
+
const activity = (row) => summarizeFleetActivity((row.group.agents ?? []).filter((agent) => !row.session ||
|
|
106
|
+
(agent.sessionName === row.session.name &&
|
|
107
|
+
agent.server?.serverId === row.session.server?.serverId &&
|
|
108
|
+
agent.server?.generation === row.session.server?.generation)), row.group.state === "ready" && row.group.agentsAvailable !== false && !row.session?.disabled);
|
|
83
109
|
const connectionDetail = (group) => {
|
|
84
110
|
const diagnostic = group.diagnostic;
|
|
85
111
|
if (!diagnostic)
|
|
@@ -111,6 +137,14 @@ export function ApplicationMachineSidebar(props) {
|
|
|
111
137
|
setSelectedKey(row.key);
|
|
112
138
|
setLocalFocused(true);
|
|
113
139
|
props.model.onFocus?.();
|
|
140
|
+
if (row.server) {
|
|
141
|
+
if (row.server.state === "online")
|
|
142
|
+
props.model.onSelectServer?.(row.group.id, {
|
|
143
|
+
serverId: row.server.serverId,
|
|
144
|
+
generation: row.server.generation,
|
|
145
|
+
});
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
114
148
|
if (row.agent) {
|
|
115
149
|
if (row.group.state === "ready" &&
|
|
116
150
|
!row.agent.disabled &&
|
|
@@ -123,7 +157,7 @@ export function ApplicationMachineSidebar(props) {
|
|
|
123
157
|
}
|
|
124
158
|
else if (row.session) {
|
|
125
159
|
if (row.group.state === "ready" && !row.session.disabled)
|
|
126
|
-
props.model.onOpen(row.group.id, row.session.name, source);
|
|
160
|
+
props.model.onOpen(row.group.id, row.session.name, source, row.session.id);
|
|
127
161
|
}
|
|
128
162
|
else if (row.group.state !== "ready" ||
|
|
129
163
|
(row.group.sessions.length === 0 && !row.group.agents?.length))
|
|
@@ -134,7 +168,7 @@ export function ApplicationMachineSidebar(props) {
|
|
|
134
168
|
const revealFocusedRow = () => {
|
|
135
169
|
if (!focused() || !scroll)
|
|
136
170
|
return;
|
|
137
|
-
const rowHeight = (row) =>
|
|
171
|
+
const rowHeight = (row) => row.agent ? (row.agentHeading ? 3 : 2) : row.serverHeading ? 2 : 1;
|
|
138
172
|
const y = rows()
|
|
139
173
|
.slice(0, index())
|
|
140
174
|
.reduce((sum, row) => sum + rowHeight(row), 0);
|
|
@@ -298,48 +332,68 @@ export function ApplicationMachineSidebar(props) {
|
|
|
298
332
|
get agent() {
|
|
299
333
|
return current().agent;
|
|
300
334
|
},
|
|
335
|
+
get server() {
|
|
336
|
+
return current().server;
|
|
337
|
+
},
|
|
338
|
+
get serverHeading() {
|
|
339
|
+
return current().serverHeading;
|
|
340
|
+
},
|
|
301
341
|
get agentHeading() {
|
|
302
342
|
return current().agentHeading;
|
|
303
343
|
},
|
|
304
344
|
};
|
|
305
|
-
return (<box width={Math.max(1, props.width - 1)} height={row.agent ? (row.agentHeading ? 3 : 2) : 1} flexShrink={0} flexDirection="column">
|
|
345
|
+
return (<box width={Math.max(1, props.width - 1)} height={row.agent ? (row.agentHeading ? 3 : 2) : row.serverHeading ? 2 : 1} flexShrink={0} flexDirection="column">
|
|
346
|
+
<Show when={row.serverHeading}>
|
|
347
|
+
<NavigationRow theme={props.theme} width={Math.max(1, props.width - 1)} id={`server:${row.group.id}:${row.session?.server?.serverId}`} label={row.serverHeading ?? "Server"} marker=" ▾" onActivate={() => {
|
|
348
|
+
if (row.session?.server)
|
|
349
|
+
props.model.onSelectServer?.(row.group.id, row.session.server);
|
|
350
|
+
}}/>
|
|
351
|
+
</Show>
|
|
306
352
|
<Show when={row.agentHeading}>
|
|
307
353
|
<text height={1} fg={props.theme.roles.text.secondary}>
|
|
308
354
|
{" "}
|
|
309
355
|
Agents
|
|
310
356
|
</text>
|
|
311
357
|
</Show>
|
|
312
|
-
<NavigationRow theme={props.theme} id={`machine:${row.key}`} width={Math.max(1, props.width - 1)} labelColor={!row.agent && !row.session ? fleetHostColor(row.group) : undefined} label={row.
|
|
313
|
-
? row.
|
|
314
|
-
: row.
|
|
315
|
-
?
|
|
316
|
-
: row.
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
?
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
358
|
+
<NavigationRow theme={props.theme} id={`machine:${row.key}`} width={Math.max(1, props.width - 1)} labelColor={!row.agent && !row.session ? fleetHostColor(row.group) : undefined} label={row.server
|
|
359
|
+
? ` ${row.server.label} · ${row.server.serverId.slice(-6)}`
|
|
360
|
+
: row.agent
|
|
361
|
+
? row.agent.name
|
|
362
|
+
: row.session
|
|
363
|
+
? friendlySessionLabel(row.session.name)
|
|
364
|
+
: row.group.label} marker={row.server
|
|
365
|
+
? " "
|
|
366
|
+
: row.agent
|
|
367
|
+
? active(row)
|
|
368
|
+
? row.agent.attention
|
|
369
|
+
? "›!"
|
|
370
|
+
: "›"
|
|
371
|
+
: row.agent.attention
|
|
372
|
+
? "!"
|
|
373
|
+
: "•"
|
|
374
|
+
: row.session
|
|
375
|
+
? props.model.favorites?.().includes(row.session.id)
|
|
376
|
+
? " ★"
|
|
377
|
+
: active(row)
|
|
378
|
+
? " ›"
|
|
379
|
+
: " "
|
|
380
|
+
: collapsed().has(preferenceKey(row.group))
|
|
381
|
+
? "▸"
|
|
382
|
+
: "▾"} detail={row.server
|
|
383
|
+
? row.server.state === "offline"
|
|
384
|
+
? "offline"
|
|
385
|
+
: "empty"
|
|
386
|
+
: row.agent
|
|
387
|
+
? row.group.state !== "ready" || row.agent.disabled
|
|
338
388
|
? "unavailable"
|
|
339
|
-
:
|
|
340
|
-
: row.
|
|
341
|
-
?
|
|
342
|
-
|
|
389
|
+
: `[${terminalAgentStatusLabel(row.agent.activity)}]`
|
|
390
|
+
: row.session
|
|
391
|
+
? row.group.state !== "ready" || row.session.disabled
|
|
392
|
+
? "unavailable"
|
|
393
|
+
: `${row.session.paneCount}p${row.group.agents?.length ? ` ${activity(row).label}` : ""}`
|
|
394
|
+
: row.group.agents?.length && row.group.state === "ready"
|
|
395
|
+
? activity(row).label
|
|
396
|
+
: `${connectionDetail(row.group)}${row.group.agents?.length ? " ?" : ""}`} selected={Boolean((row.session || row.agent) && active(row))} focused={Boolean(focused() && row.key === selectedKey())} attention={row.agent?.attention ?? activity(row).kind === "attention"} onActivate={(source) => activate(row, source)}/>
|
|
343
397
|
<Show when={row.agent}>
|
|
344
398
|
{(agent) => (<text height={1} fg={props.theme.roles.text.muted} content={clipTerminal(` ${friendlySessionLabel(agent().sessionName)}`, Math.max(1, props.width - 1))}/>)}
|
|
345
399
|
</Show>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TmuxServerIdSchemaZ } from "@tmux-ide/contracts";
|
|
1
2
|
import { parseArgs } from "node:util";
|
|
2
3
|
import { loadAppConfig } from "../../../lib/app-config.js";
|
|
3
4
|
import { tuiPerfMark } from "./application-performance-log.js";
|
|
@@ -6,16 +7,21 @@ export const parseApplicationArgs = (argv) => {
|
|
|
6
7
|
args: [...argv],
|
|
7
8
|
allowPositionals: true,
|
|
8
9
|
strict: false,
|
|
9
|
-
options: { target: { type: "string" } },
|
|
10
|
+
options: { target: { type: "string" }, server: { type: "string" } },
|
|
10
11
|
});
|
|
11
12
|
const positional = parsed.positionals.find((value) => value !== "app") ?? null;
|
|
12
13
|
const target = typeof parsed.values.target === "string" ? parsed.values.target : positional;
|
|
13
|
-
return Object.freeze({
|
|
14
|
+
return Object.freeze({
|
|
15
|
+
target: target && target !== "home" ? target : null,
|
|
16
|
+
serverId: typeof parsed.values.server === "string"
|
|
17
|
+
? TmuxServerIdSchemaZ.parse(parsed.values.server)
|
|
18
|
+
: null,
|
|
19
|
+
});
|
|
14
20
|
};
|
|
15
21
|
export async function loadApplicationConfig(args) {
|
|
16
22
|
tuiPerfMark("config-load-start", { requestedTarget: args.target });
|
|
17
23
|
// Home discovery is daemon-authoritative and remains live after mount. The
|
|
18
24
|
// immutable bootstrap config only carries an explicit user target.
|
|
19
25
|
tuiPerfMark("config-load-end", { target: args.target });
|
|
20
|
-
return Object.freeze({ app: loadAppConfig(), target: args.target });
|
|
26
|
+
return Object.freeze({ app: loadAppConfig(), target: args.target, serverId: args.serverId });
|
|
21
27
|
}
|