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
|
@@ -1,6 +1,14 @@
|
|
|
1
|
+
import WebSocket from "ws";
|
|
2
|
+
import {
|
|
3
|
+
createWorkspaceEventSupervisor,
|
|
4
|
+
type WorkspaceEventSocket,
|
|
5
|
+
} from "@tmux-ide/daemon-client/workspace-event-supervisor";
|
|
1
6
|
import { readApplicationDaemonInfo as readCanonicalDaemonInfo } from "./application-daemon-authority.ts";
|
|
2
7
|
import {
|
|
3
|
-
|
|
8
|
+
TmuxServersResourceSchemaZ,
|
|
9
|
+
TmuxServerSessionsResourceSchemaZ,
|
|
10
|
+
type TmuxServerScope,
|
|
11
|
+
type TmuxServerDescriptor,
|
|
4
12
|
type CanonicalDaemonInfo,
|
|
5
13
|
type WorkspaceCatalogResourceV3,
|
|
6
14
|
} from "@tmux-ide/contracts";
|
|
@@ -9,18 +17,16 @@ import {
|
|
|
9
17
|
type PushResourceSessionAdapter,
|
|
10
18
|
type PushResourceSessionOptions,
|
|
11
19
|
} from "@tmux-ide/daemon-client/push-resource-session";
|
|
12
|
-
import {
|
|
13
|
-
createWorkspaceEventSupervisor,
|
|
14
|
-
type WorkspaceEventSocket,
|
|
15
|
-
} from "@tmux-ide/daemon-client/workspace-event-supervisor";
|
|
16
|
-
import WebSocket from "ws";
|
|
17
20
|
|
|
18
21
|
import { canonicalDaemonUrl } from "../../../lib/canonical-daemon.ts";
|
|
19
22
|
|
|
20
23
|
export type ApplicationHomeCatalogResourceKey = "live-catalog";
|
|
21
24
|
export type ApplicationHomeCatalogResource = {
|
|
22
25
|
readonly kind: "live-catalog";
|
|
23
|
-
readonly value
|
|
26
|
+
readonly value?: WorkspaceCatalogResourceV3;
|
|
27
|
+
readonly daemonInstanceId?: string;
|
|
28
|
+
readonly servers?: readonly TmuxServerDescriptor[];
|
|
29
|
+
readonly scopedSessions?: readonly ApplicationHomeCatalogSession[];
|
|
24
30
|
};
|
|
25
31
|
export interface ApplicationHomeCatalogTarget {
|
|
26
32
|
readonly daemon: CanonicalDaemonInfo;
|
|
@@ -35,6 +41,8 @@ export interface ApplicationHomeCatalogFailure {
|
|
|
35
41
|
|
|
36
42
|
export interface ApplicationHomeCatalogSession {
|
|
37
43
|
readonly id: string;
|
|
44
|
+
readonly server?: TmuxServerScope;
|
|
45
|
+
readonly serverLabel?: string;
|
|
38
46
|
readonly liveSessionId?: string;
|
|
39
47
|
readonly workspaceName?: string;
|
|
40
48
|
readonly name: string;
|
|
@@ -45,6 +53,7 @@ export interface ApplicationHomeCatalogSnapshot {
|
|
|
45
53
|
readonly phase: "loading" | "live" | "unavailable";
|
|
46
54
|
readonly daemonInstanceId: string | null;
|
|
47
55
|
readonly sessions: readonly ApplicationHomeCatalogSession[];
|
|
56
|
+
readonly servers?: readonly TmuxServerDescriptor[];
|
|
48
57
|
readonly note: string | null;
|
|
49
58
|
}
|
|
50
59
|
|
|
@@ -117,7 +126,7 @@ export function closeApplicationHomeCatalogTransport(
|
|
|
117
126
|
supervisor.dispose();
|
|
118
127
|
}
|
|
119
128
|
|
|
120
|
-
function createApplicationHomeCatalogAdapter(
|
|
129
|
+
function createApplicationHomeCatalogAdapter(_onTransportRetired: () => void): HomeCatalogAdapter {
|
|
121
130
|
return {
|
|
122
131
|
validateTarget(value) {
|
|
123
132
|
if (
|
|
@@ -139,15 +148,9 @@ function createApplicationHomeCatalogAdapter(onTransportRetired: () => void): Ho
|
|
|
139
148
|
};
|
|
140
149
|
},
|
|
141
150
|
async fetch(target, _key, signal) {
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
target.daemon.port,
|
|
146
|
-
"/api/resources/workspace-catalog?version=3",
|
|
147
|
-
);
|
|
148
|
-
let response: Response;
|
|
149
|
-
try {
|
|
150
|
-
response = await fetch(url, {
|
|
151
|
+
const base = canonicalDaemonUrl("http", target.daemon.bindHostname, target.daemon.port);
|
|
152
|
+
const read = async (path: string) => {
|
|
153
|
+
const response = await fetch(base + path, {
|
|
151
154
|
headers: {
|
|
152
155
|
accept: "application/json",
|
|
153
156
|
...(target.daemon.authToken
|
|
@@ -157,88 +160,139 @@ function createApplicationHomeCatalogAdapter(onTransportRetired: () => void): Ho
|
|
|
157
160
|
credentials: "omit",
|
|
158
161
|
cache: "no-store",
|
|
159
162
|
redirect: "error",
|
|
160
|
-
signal,
|
|
163
|
+
signal: AbortSignal.any([signal, AbortSignal.timeout(3_000)]),
|
|
161
164
|
});
|
|
162
|
-
|
|
163
|
-
return
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
165
|
+
if (!response.ok) throw new Error(`Discovery returned HTTP ${response.status}`);
|
|
166
|
+
return response.json();
|
|
167
|
+
};
|
|
168
|
+
try {
|
|
169
|
+
const inventory = TmuxServersResourceSchemaZ.parse(await read("/api/v1/tmux-servers"));
|
|
170
|
+
const sessions = await Promise.allSettled(
|
|
171
|
+
inventory.servers.map(async (descriptor) => {
|
|
172
|
+
if (descriptor.state !== "online") return [];
|
|
173
|
+
const server = { serverId: descriptor.serverId, generation: descriptor.generation };
|
|
174
|
+
const result = TmuxServerSessionsResourceSchemaZ.parse(
|
|
175
|
+
await read(`/api/v1/tmux-servers/${server.serverId}/${server.generation}/sessions`),
|
|
176
|
+
);
|
|
177
|
+
if (
|
|
178
|
+
result.server.serverId !== server.serverId ||
|
|
179
|
+
result.server.generation !== server.generation
|
|
180
|
+
)
|
|
181
|
+
throw new Error("Server discovery identity changed");
|
|
182
|
+
return result.sessions.map((session) => ({
|
|
183
|
+
id: JSON.stringify([server.serverId, server.generation, session.liveSessionId]),
|
|
184
|
+
server,
|
|
185
|
+
serverLabel: descriptor.label,
|
|
186
|
+
liveSessionId: session.liveSessionId,
|
|
187
|
+
name: session.sessionName,
|
|
188
|
+
...(session.workspaceName ? { workspaceName: session.workspaceName } : {}),
|
|
189
|
+
paneCount: session.paneCount,
|
|
190
|
+
}));
|
|
191
|
+
}),
|
|
192
|
+
);
|
|
169
193
|
return {
|
|
170
|
-
status: "
|
|
171
|
-
|
|
172
|
-
"
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
194
|
+
status: "ok",
|
|
195
|
+
resource: {
|
|
196
|
+
kind: "live-catalog",
|
|
197
|
+
daemonInstanceId: target.daemon.instanceId,
|
|
198
|
+
servers: inventory.servers.map((server, index) =>
|
|
199
|
+
sessions[index]?.status === "rejected"
|
|
200
|
+
? {
|
|
201
|
+
serverId: server.serverId,
|
|
202
|
+
label: server.label,
|
|
203
|
+
state: "offline" as const,
|
|
204
|
+
generation: null,
|
|
205
|
+
}
|
|
206
|
+
: server,
|
|
207
|
+
),
|
|
208
|
+
scopedSessions: sessions.flatMap((result) =>
|
|
209
|
+
result.status === "fulfilled" ? result.value : [],
|
|
210
|
+
),
|
|
211
|
+
},
|
|
176
212
|
};
|
|
177
|
-
let body: unknown;
|
|
178
|
-
try {
|
|
179
|
-
body = await response.json();
|
|
180
213
|
} catch {
|
|
181
214
|
return {
|
|
182
215
|
status: "failed",
|
|
183
|
-
failure: catalogFailure("
|
|
216
|
+
failure: catalogFailure("network", "Server session discovery failed.", true),
|
|
184
217
|
};
|
|
185
218
|
}
|
|
186
|
-
const parsed = WorkspaceCatalogResourceV3SchemaZ.safeParse(body);
|
|
187
|
-
if (!parsed.success || !sameDaemon(target.daemon, parsed.data.daemon))
|
|
188
|
-
return {
|
|
189
|
-
status: "failed",
|
|
190
|
-
failure: catalogFailure(
|
|
191
|
-
"schema",
|
|
192
|
-
"Live session discovery failed daemon identity validation.",
|
|
193
|
-
false,
|
|
194
|
-
),
|
|
195
|
-
};
|
|
196
|
-
return { status: "ok", resource: { kind: "live-catalog", value: parsed.data } };
|
|
197
219
|
},
|
|
198
220
|
async connect(target, _interests, handlers, signal) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
target.daemon.bindHostname,
|
|
202
|
-
target.daemon.port,
|
|
203
|
-
"/ws/events?mode=semantic",
|
|
204
|
-
);
|
|
205
|
-
const socket = new WebSocket(socketUrl, {
|
|
206
|
-
headers: target.daemon.authToken
|
|
207
|
-
? { Authorization: `Bearer ${target.daemon.authToken}` }
|
|
208
|
-
: undefined,
|
|
209
|
-
}) as unknown as WorkspaceEventSocket;
|
|
210
|
-
const supervisor = createWorkspaceEventSupervisor({
|
|
211
|
-
socket,
|
|
212
|
-
daemon: target.daemon,
|
|
213
|
-
workspaceName: target.workspaceName,
|
|
214
|
-
sessionName: target.workspaceName,
|
|
215
|
-
fetchTerminalRuntimeInventory: () =>
|
|
216
|
-
Promise.reject(new Error("Home catalog owns no terminal inventory.")),
|
|
217
|
-
onRetired: onTransportRetired,
|
|
218
|
-
});
|
|
219
|
-
const subscription = supervisor.connectWorkspaceCatalog(
|
|
220
|
-
() => handlers.invalidate(["live-catalog"]),
|
|
221
|
-
{ terminalFirst: false },
|
|
222
|
-
);
|
|
221
|
+
// Root push is an advisory invalidation only: every refresh still resolves
|
|
222
|
+
// the full scoped inventory. The timer discovers changes on other owners.
|
|
223
223
|
let closed = false;
|
|
224
|
-
|
|
225
|
-
|
|
224
|
+
let pending = false;
|
|
225
|
+
let closePush: (() => void) | null = null;
|
|
226
|
+
const invalidate = () => {
|
|
227
|
+
if (!closed) handlers.invalidate(["live-catalog"]);
|
|
228
|
+
};
|
|
229
|
+
const startPush = async () => {
|
|
230
|
+
if (closed || pending || closePush) return;
|
|
231
|
+
pending = true;
|
|
232
|
+
const socket = new WebSocket(
|
|
233
|
+
canonicalDaemonUrl(
|
|
234
|
+
"ws",
|
|
235
|
+
target.daemon.bindHostname,
|
|
236
|
+
target.daemon.port,
|
|
237
|
+
"/ws/events?mode=semantic",
|
|
238
|
+
),
|
|
239
|
+
{
|
|
240
|
+
headers: target.daemon.authToken
|
|
241
|
+
? { Authorization: `Bearer ${target.daemon.authToken}` }
|
|
242
|
+
: undefined,
|
|
243
|
+
},
|
|
244
|
+
) as unknown as WorkspaceEventSocket;
|
|
245
|
+
const supervisor = createWorkspaceEventSupervisor({
|
|
246
|
+
socket,
|
|
247
|
+
daemon: target.daemon,
|
|
248
|
+
workspaceName: target.workspaceName,
|
|
249
|
+
sessionName: target.workspaceName,
|
|
250
|
+
fetchTerminalRuntimeInventory: () =>
|
|
251
|
+
Promise.reject(new Error("Home catalog owns no terminal inventory.")),
|
|
252
|
+
onRetired: () => {
|
|
253
|
+
const cleanup = closePush;
|
|
254
|
+
closePush = null;
|
|
255
|
+
cleanup?.();
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
const subscription = supervisor.connectWorkspaceCatalog(invalidate, {
|
|
259
|
+
terminalFirst: false,
|
|
260
|
+
});
|
|
261
|
+
const cleanup = () => closeApplicationHomeCatalogTransport(subscription, supervisor);
|
|
262
|
+
closePush = cleanup;
|
|
263
|
+
try {
|
|
264
|
+
await subscription.ready;
|
|
265
|
+
} catch {
|
|
266
|
+
if (closePush === cleanup) closePush = null;
|
|
267
|
+
cleanup();
|
|
268
|
+
} finally {
|
|
269
|
+
pending = false;
|
|
270
|
+
if (closed) {
|
|
271
|
+
cleanup();
|
|
272
|
+
closePush = null;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
const timer = setInterval(() => {
|
|
277
|
+
invalidate();
|
|
278
|
+
void startPush().catch(() => {
|
|
279
|
+
pending = false;
|
|
280
|
+
});
|
|
281
|
+
}, 5_000);
|
|
282
|
+
const close = () => {
|
|
226
283
|
closed = true;
|
|
284
|
+
clearInterval(timer);
|
|
227
285
|
signal.removeEventListener("abort", close);
|
|
228
|
-
|
|
229
|
-
|
|
286
|
+
const cleanup = closePush;
|
|
287
|
+
closePush = null;
|
|
288
|
+
cleanup?.();
|
|
230
289
|
};
|
|
231
290
|
signal.addEventListener("abort", close, { once: true });
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
if (signal.aborted) {
|
|
239
|
-
close();
|
|
240
|
-
throw signal.reason;
|
|
241
|
-
}
|
|
291
|
+
if (signal.aborted) close();
|
|
292
|
+
else
|
|
293
|
+
void startPush().catch(() => {
|
|
294
|
+
pending = false;
|
|
295
|
+
});
|
|
242
296
|
return { status: "connected", close };
|
|
243
297
|
},
|
|
244
298
|
rejectionFailure: () =>
|
|
@@ -264,31 +318,37 @@ const initialSnapshot = (): ApplicationHomeCatalogSnapshot => ({
|
|
|
264
318
|
note: "Discovering live tmux sessions…",
|
|
265
319
|
});
|
|
266
320
|
|
|
267
|
-
function projectCatalog(
|
|
321
|
+
function projectCatalog(
|
|
322
|
+
resource: WorkspaceCatalogResourceV3,
|
|
323
|
+
scopedSessions?: readonly ApplicationHomeCatalogSession[],
|
|
324
|
+
): ApplicationHomeCatalogSnapshot {
|
|
268
325
|
return {
|
|
269
326
|
phase: "live",
|
|
270
327
|
daemonInstanceId: resource.daemon.instanceId,
|
|
271
|
-
sessions:
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
liveSessionId,
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
328
|
+
sessions:
|
|
329
|
+
scopedSessions ??
|
|
330
|
+
resource.liveSessions
|
|
331
|
+
.map(({ liveSessionId, sessionName, paneCount }) => ({
|
|
332
|
+
id: `${resource.daemon.instanceId}:${liveSessionId}`,
|
|
333
|
+
liveSessionId,
|
|
334
|
+
workspaceName: resource.intents.find((intent) => intent.sessionName === sessionName)
|
|
335
|
+
?.workspaceName,
|
|
336
|
+
name: sessionName,
|
|
337
|
+
paneCount,
|
|
338
|
+
}))
|
|
339
|
+
.sort((left, right) => left.name.localeCompare(right.name)),
|
|
281
340
|
note:
|
|
282
|
-
resource.liveSessions.length === 0
|
|
341
|
+
(scopedSessions?.length ?? resource.liveSessions.length) === 0
|
|
283
342
|
? "No live tmux sessions yet. This list updates automatically."
|
|
284
343
|
: null,
|
|
285
344
|
};
|
|
286
345
|
}
|
|
287
346
|
|
|
288
347
|
/**
|
|
289
|
-
* Daemon-authoritative
|
|
290
|
-
*
|
|
291
|
-
*
|
|
348
|
+
* Daemon-authoritative fleet metadata. Root push preserves immediate updates,
|
|
349
|
+
* while a five-second refresh discovers independent
|
|
350
|
+
* server owners without opening pane streams. Failed reads use bounded backoff;
|
|
351
|
+
* each failed server is excluded without suppressing healthy siblings.
|
|
292
352
|
*/
|
|
293
353
|
export function createApplicationHomeCatalog(
|
|
294
354
|
overrides: Partial<ApplicationHomeCatalogDependencies> = {},
|
|
@@ -373,7 +433,19 @@ export function createApplicationHomeCatalog(
|
|
|
373
433
|
if (slot?.status === "loaded" && !slot.refreshing && slot.resource.kind === "live-catalog") {
|
|
374
434
|
clearRetry();
|
|
375
435
|
retryAttempt = 0;
|
|
376
|
-
publish(
|
|
436
|
+
publish(
|
|
437
|
+
slot.resource.value
|
|
438
|
+
? projectCatalog(slot.resource.value)
|
|
439
|
+
: {
|
|
440
|
+
phase: "live",
|
|
441
|
+
daemonInstanceId: slot.resource.daemonInstanceId ?? null,
|
|
442
|
+
sessions: slot.resource.scopedSessions ?? [],
|
|
443
|
+
servers: slot.resource.servers,
|
|
444
|
+
note: slot.resource.scopedSessions?.length
|
|
445
|
+
? null
|
|
446
|
+
: "No live tmux sessions yet. This list updates automatically.",
|
|
447
|
+
},
|
|
448
|
+
);
|
|
377
449
|
return;
|
|
378
450
|
}
|
|
379
451
|
if (slot?.status === "error") {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TmuxServerDescriptor } from "@tmux-ide/contracts";
|
|
1
2
|
import type { FleetCachedRoute } from "@tmux-ide/contracts/fleet-client-state";
|
|
2
3
|
import { groupFleetEnvironments } from "@tmux-ide/daemon-client/fleet-environments";
|
|
3
4
|
import {
|
|
@@ -27,6 +28,7 @@ export interface ApplicationMachineCatalogGroup {
|
|
|
27
28
|
readonly label: string;
|
|
28
29
|
readonly state: "ready" | "connecting" | "disconnected";
|
|
29
30
|
readonly sessions: readonly ApplicationMachineCatalogSession[];
|
|
31
|
+
readonly servers?: readonly TmuxServerDescriptor[];
|
|
30
32
|
readonly note: string | null;
|
|
31
33
|
readonly diagnostic?: FleetConnectionStatus;
|
|
32
34
|
readonly environmentId?: string | null;
|
|
@@ -114,6 +116,7 @@ export function createApplicationMachineCatalog(
|
|
|
114
116
|
diagnostic && diagnostic.phase !== "ready"
|
|
115
117
|
? fleetConnectionMessage(diagnostic)
|
|
116
118
|
: (entry?.snapshot.note ?? null),
|
|
119
|
+
servers: entry?.snapshot.servers,
|
|
117
120
|
sessions: (entry?.lastSessions ?? []).map((session) => ({
|
|
118
121
|
...session,
|
|
119
122
|
sourceId: session.id,
|
|
@@ -152,7 +155,13 @@ export function createApplicationMachineCatalog(
|
|
|
152
155
|
...session,
|
|
153
156
|
id:
|
|
154
157
|
session.liveSessionId && joined.environmentId && !joined.conflict
|
|
155
|
-
? JSON.stringify([
|
|
158
|
+
? JSON.stringify([
|
|
159
|
+
joined.environmentId,
|
|
160
|
+
"session",
|
|
161
|
+
session.server?.serverId,
|
|
162
|
+
session.server?.generation,
|
|
163
|
+
session.liveSessionId,
|
|
164
|
+
])
|
|
156
165
|
: session.id,
|
|
157
166
|
})),
|
|
158
167
|
};
|
|
@@ -214,12 +223,15 @@ export function createApplicationMachineCatalog(
|
|
|
214
223
|
environmentId: entry.environmentId,
|
|
215
224
|
generation: entry.generation,
|
|
216
225
|
seenAt: entry.seenAt,
|
|
217
|
-
sessions: value.sessions
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
226
|
+
sessions: value.sessions
|
|
227
|
+
.slice(0, 64)
|
|
228
|
+
.map(({ id, liveSessionId, name, paneCount, server, serverLabel }) => ({
|
|
229
|
+
id,
|
|
230
|
+
...(liveSessionId ? { liveSessionId } : {}),
|
|
231
|
+
name,
|
|
232
|
+
paneCount,
|
|
233
|
+
...(server ? { server, serverLabel } : {}),
|
|
234
|
+
})),
|
|
223
235
|
});
|
|
224
236
|
} catch {
|
|
225
237
|
/* Cache failure must never prevent live catalog publication. */
|