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
|
@@ -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;
|
|
76
111
|
}
|
|
77
|
-
|
|
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;
|
|
124
|
+
}
|
|
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,28 @@ export function createApplicationMachineNavigation(options) {
|
|
|
217
302
|
catalog,
|
|
218
303
|
agents,
|
|
219
304
|
selectedMachineId: () => manager.snapshot().selectedMachineId,
|
|
305
|
+
openHomeAgent: (row, source) => {
|
|
306
|
+
if (!agents.isCurrentTarget(row.machineId, row) || row.disabled)
|
|
307
|
+
return;
|
|
308
|
+
const session = snapshot()
|
|
309
|
+
.groups.find((group) => group.id === row.machineId)
|
|
310
|
+
?.sessions.find((session) => session.liveSessionId === row.liveSessionId &&
|
|
311
|
+
!session.disabled &&
|
|
312
|
+
session.server?.serverId === row.server?.serverId &&
|
|
313
|
+
session.server?.generation === row.server?.generation);
|
|
314
|
+
if (!session) {
|
|
315
|
+
options.setNote("That agent session has changed. Select it again.");
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
options.cancelOpen();
|
|
319
|
+
if (!select(row.machineId, row.liveSessionId, row.server))
|
|
320
|
+
return;
|
|
321
|
+
setActiveSessionKey(session.id);
|
|
322
|
+
navigation++;
|
|
323
|
+
setFocused(false);
|
|
324
|
+
return options.openAgent?.(row, source);
|
|
325
|
+
},
|
|
326
|
+
openSelectedSession: (name, source, key) => open(manager.snapshot().selectedMachineId, name, source, true, undefined, undefined, key),
|
|
220
327
|
automaticOpen: manager.snapshot().machines.length === 1,
|
|
221
328
|
automaticOpenAllowed: () => navigation === 0 && manager.snapshot().selectedMachineId === "local",
|
|
222
329
|
sidebar,
|
|
@@ -235,7 +342,7 @@ export function createApplicationMachineNavigation(options) {
|
|
|
235
342
|
options.setNote("That session is unavailable or has been replaced.");
|
|
236
343
|
return;
|
|
237
344
|
}
|
|
238
|
-
if (await open(group.id, session.name, "keyboard", false))
|
|
345
|
+
if (await open(group.id, session.name, "keyboard", false, session.liveSessionId, session.server, session.id))
|
|
239
346
|
historyIndex = next;
|
|
240
347
|
},
|
|
241
348
|
cycleTab(direction, close = false) {
|
|
@@ -253,10 +360,20 @@ export function createApplicationMachineNavigation(options) {
|
|
|
253
360
|
void tabs.activate(target.key);
|
|
254
361
|
},
|
|
255
362
|
closeSwitcher: () => setSwitching(false),
|
|
363
|
+
switcherFilter: (command) => {
|
|
364
|
+
const focus = serverFocus();
|
|
365
|
+
return (!focus ||
|
|
366
|
+
(typeof command === "object" &&
|
|
367
|
+
command.fleet?.machineId === focus.machineId &&
|
|
368
|
+
command.fleet?.server?.serverId === focus.server.serverId &&
|
|
369
|
+
command.fleet?.server?.generation === focus.server.generation));
|
|
370
|
+
},
|
|
256
371
|
paletteCommands() {
|
|
257
372
|
const groupsById = new Map(agentGroups().map((group) => [group.machineId, group]));
|
|
258
373
|
return snapshot().groups.flatMap((group) => {
|
|
259
|
-
const agentGroup =
|
|
374
|
+
const agentGroup = !group.sessions.some((session) => isDefaultSession(group.id, session))
|
|
375
|
+
? undefined
|
|
376
|
+
: groupsById.get(group.id);
|
|
260
377
|
const agentsBySession = new Map();
|
|
261
378
|
for (const agent of agentGroup?.agents ?? []) {
|
|
262
379
|
if (!agent.liveSessionId || !agent.paneId)
|
|
@@ -276,8 +393,9 @@ export function createApplicationMachineNavigation(options) {
|
|
|
276
393
|
? saved().recent.indexOf(session.id)
|
|
277
394
|
: 1000,
|
|
278
395
|
liveSessionId: session.liveSessionId,
|
|
279
|
-
|
|
280
|
-
|
|
396
|
+
server: session.server,
|
|
397
|
+
hostLabel: `${group.label}${session.serverLabel ? ` / ${session.serverLabel}` : ""}`,
|
|
398
|
+
agentActivities: isDefaultSession(group.id, session) && agentGroup?.available
|
|
281
399
|
? (agentsBySession.get(session.liveSessionId) ?? []).map((a) => ({
|
|
282
400
|
paneId: a.paneId,
|
|
283
401
|
attention: a.attention,
|
|
@@ -289,7 +407,9 @@ export function createApplicationMachineNavigation(options) {
|
|
|
289
407
|
};
|
|
290
408
|
return [
|
|
291
409
|
{ kind: "open-session", sessionName: session.name, label: session.name, fleet },
|
|
292
|
-
...(
|
|
410
|
+
...(isDefaultSession(group.id, session)
|
|
411
|
+
? (agentsBySession.get(session.liveSessionId) ?? [])
|
|
412
|
+
: []).map((a) => ({
|
|
293
413
|
kind: "jump-agent",
|
|
294
414
|
sessionName: session.name,
|
|
295
415
|
paneId: a.paneId,
|
|
@@ -311,6 +431,21 @@ export function createApplicationMachineNavigation(options) {
|
|
|
311
431
|
disabled: group.state !== "ready",
|
|
312
432
|
},
|
|
313
433
|
},
|
|
434
|
+
...(group.servers ?? []).map((server) => ({
|
|
435
|
+
kind: "open-machine",
|
|
436
|
+
sessionName: "",
|
|
437
|
+
label: `${server.label} · ${server.serverId.slice(-6)} · New session ^N`,
|
|
438
|
+
fleet: {
|
|
439
|
+
machineId: group.id,
|
|
440
|
+
hostLabel: `${group.label} / ${server.label}`,
|
|
441
|
+
liveSessionId: "",
|
|
442
|
+
daemonInstanceId: daemon?.instanceId ?? "",
|
|
443
|
+
disabled: group.state !== "ready" || server.state !== "online",
|
|
444
|
+
...(server.state === "online"
|
|
445
|
+
? { server: { serverId: server.serverId, generation: server.generation } }
|
|
446
|
+
: {}),
|
|
447
|
+
},
|
|
448
|
+
})),
|
|
314
449
|
...sessions,
|
|
315
450
|
];
|
|
316
451
|
});
|
|
@@ -321,7 +456,9 @@ export function createApplicationMachineNavigation(options) {
|
|
|
321
456
|
const target = command.fleet;
|
|
322
457
|
const session = snapshot()
|
|
323
458
|
.groups.find((g) => g.id === target.machineId)
|
|
324
|
-
?.sessions.find((s) => s.liveSessionId === target.liveSessionId
|
|
459
|
+
?.sessions.find((s) => s.liveSessionId === target.liveSessionId &&
|
|
460
|
+
s.server?.serverId === target.server?.serverId &&
|
|
461
|
+
s.server?.generation === target.server?.generation);
|
|
325
462
|
if (session)
|
|
326
463
|
preferences.change({
|
|
327
464
|
type: "favorite",
|
|
@@ -335,6 +472,19 @@ export function createApplicationMachineNavigation(options) {
|
|
|
335
472
|
return;
|
|
336
473
|
const handle = manager.getMachine(target.machineId);
|
|
337
474
|
if (command.kind === "open-machine") {
|
|
475
|
+
if (target.server) {
|
|
476
|
+
const descriptor = snapshot()
|
|
477
|
+
.groups.find((group) => group.id === target.machineId)
|
|
478
|
+
?.servers?.find((server) => server.serverId === target.server?.serverId &&
|
|
479
|
+
server.generation === target.server.generation &&
|
|
480
|
+
server.state === "online");
|
|
481
|
+
if (!descriptor || handle?.read()?.instanceId !== target.daemonInstanceId) {
|
|
482
|
+
options.setNote("That server has changed. Select it again.");
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
sidebar.onSelectServer?.(target.machineId, target.server);
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
338
488
|
if (handle?.endpoint().state === "ready" &&
|
|
339
489
|
handle.read()?.instanceId === target.daemonInstanceId)
|
|
340
490
|
sidebar.onSelectMachine(target.machineId, source);
|
|
@@ -343,7 +493,10 @@ export function createApplicationMachineNavigation(options) {
|
|
|
343
493
|
return;
|
|
344
494
|
}
|
|
345
495
|
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 &&
|
|
496
|
+
const session = group?.sessions.find((s) => s.liveSessionId === target.liveSessionId &&
|
|
497
|
+
!s.disabled &&
|
|
498
|
+
s.server?.serverId === target.server?.serverId &&
|
|
499
|
+
s.server?.generation === target.server?.generation);
|
|
347
500
|
if (!session ||
|
|
348
501
|
handle?.endpoint().state !== "ready" ||
|
|
349
502
|
handle.read()?.instanceId !== target.daemonInstanceId) {
|
|
@@ -353,7 +506,7 @@ export function createApplicationMachineNavigation(options) {
|
|
|
353
506
|
if (command.kind === "jump-agent")
|
|
354
507
|
sidebar.onOpenAgent?.(target.machineId, session.name, command.paneId, source);
|
|
355
508
|
else
|
|
356
|
-
await open(target.machineId, session.name, source, true, target.liveSessionId);
|
|
509
|
+
await open(target.machineId, session.name, source, true, target.liveSessionId, target.server);
|
|
357
510
|
},
|
|
358
511
|
adding,
|
|
359
512
|
alias,
|
|
@@ -402,17 +555,44 @@ export function createApplicationMachineNavigation(options) {
|
|
|
402
555
|
setError(cause instanceof Error ? cause.message : "Could not add this machine.");
|
|
403
556
|
}
|
|
404
557
|
},
|
|
405
|
-
start(target) {
|
|
558
|
+
start(target, serverId) {
|
|
406
559
|
agents.start();
|
|
407
560
|
catalog.start();
|
|
408
|
-
if (!target)
|
|
561
|
+
if (!target && !serverId)
|
|
409
562
|
return;
|
|
410
563
|
const id = manager.snapshot().selectedMachineId;
|
|
411
564
|
const token = navigation;
|
|
565
|
+
let machineReady = false;
|
|
566
|
+
const attempt = () => {
|
|
567
|
+
if (!machineReady)
|
|
568
|
+
return;
|
|
569
|
+
if (token !== navigation) {
|
|
570
|
+
attemptStartup = null;
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
const group = snapshot().groups.find((group) => group.id === id);
|
|
574
|
+
if (group?.state !== "ready")
|
|
575
|
+
return;
|
|
576
|
+
const matches = group.sessions.filter((row) => !row.disabled &&
|
|
577
|
+
(!serverId || row.server?.serverId === serverId) &&
|
|
578
|
+
(!target || row.name === target));
|
|
579
|
+
if (matches.length !== 1) {
|
|
580
|
+
options.setNote(matches.length
|
|
581
|
+
? "Select a session under the requested tmux server."
|
|
582
|
+
: "Requested server or session is unavailable. Select it again.");
|
|
583
|
+
attemptStartup = null;
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
const row = matches[0];
|
|
587
|
+
attemptStartup = null;
|
|
588
|
+
void open(id, row.name, "keyboard", true, row.liveSessionId, row.server, row.id);
|
|
589
|
+
};
|
|
590
|
+
attemptStartup = attempt;
|
|
412
591
|
void manager.getMachine(id)?.ready.then((ready) => {
|
|
413
|
-
|
|
414
|
-
|
|
592
|
+
machineReady = ready;
|
|
593
|
+
attemptStartup?.();
|
|
415
594
|
});
|
|
595
|
+
attempt();
|
|
416
596
|
},
|
|
417
597
|
};
|
|
418
598
|
}
|
|
@@ -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>
|