tmux-ide 2.9.0-beta.19 → 2.9.0-beta.21
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 +1979 -783
- package/package.json +4 -2
- package/packages/contracts/src/__tests__/daemon-wire.test.ts +32 -0
- package/packages/contracts/src/daemon-events.ts +12 -12
- package/packages/contracts/src/daemon-wire.ts +30 -0
- package/packages/daemon/dist/command-center/agent-status-watch.js +9 -2
- package/packages/daemon/dist/command-center/diagnostics.js +65 -0
- package/packages/daemon/dist/command-center/log-stream.js +9 -0
- package/packages/daemon/dist/command-center/resources/fleet-preview-route.js +22 -6
- package/packages/daemon/dist/command-center/server.js +7 -0
- package/packages/daemon/dist/doctor.js +62 -0
- package/packages/daemon/dist/lib/__tests__/installed-recovery-fixture.js +400 -0
- package/packages/daemon/dist/lib/app-config.js +4 -2
- package/packages/daemon/dist/lib/canonical-daemon.js +1 -0
- package/packages/daemon/dist/lib/daemon-embed.js +24 -0
- package/packages/daemon/dist/lib/daemon-provenance.js +575 -0
- package/packages/daemon/dist/lib/headless-daemon.js +1 -0
- package/packages/daemon/dist/lib/log-sanitize.js +199 -0
- package/packages/daemon/dist/lib/log.js +123 -13
- package/packages/daemon/dist/lib/soak-diagnostics.js +124 -0
- package/packages/daemon/dist/lib/soak-verdict.js +472 -0
- package/packages/daemon/dist/lib/terminal-host-color.js +33 -0
- package/packages/daemon/dist/lib/tmux-external-interaction-observer.js +187 -39
- package/packages/daemon/dist/lib/tmux-interaction-retention.js +21 -0
- package/packages/daemon/dist/lib/workspace-promotion.js +55 -37
- package/packages/daemon/dist/terminal/mirror/session-channel.js +2 -1
- package/packages/daemon/dist/tui/mirror/automatic-contrast.js +161 -0
- package/packages/daemon/dist/tui/mirror/open-tui-workspace-runtime-port.js +9 -3
- package/packages/daemon/dist/tui/mirror/pane-surface.jsx +7 -5
- package/packages/daemon/dist/tui/mirror/resize-transaction.js +48 -20
- package/packages/daemon/dist/tui/mirror/runtime/application-appearance-owner.js +40 -6
- package/packages/daemon/dist/tui/mirror/runtime/application-fleet-preview.js +4 -1
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-sidebar.jsx +63 -42
- package/packages/daemon/dist/tui/mirror/runtime/application-terminal-interaction-controller.js +138 -67
- package/packages/daemon/dist/tui/mirror/runtime/application-terminal-palette-owner.js +44 -28
- package/packages/daemon/dist/tui/mirror/runtime/application-terminal-workspace.jsx +49 -11
- package/packages/daemon/dist/tui/mirror/runtime/semantic-shell-viewport-resize.js +140 -2
- package/packages/daemon/dist/tui/mirror/runtime/workspace-terminal-fast-lane.js +3 -1
- package/packages/daemon/dist/tui/mirror/semantic-pane-render-source.js +2 -1
- package/packages/daemon/dist/tui/mirror/theme.js +4 -31
- package/packages/daemon/dist/tui/mirror/workspace/terminal-pane-header.jsx +15 -8
- package/packages/daemon/dist/tui/team/wait-receipts.js +112 -33
- package/packages/daemon/src/command-center/agent-status-watch.ts +8 -2
- package/packages/daemon/src/command-center/diagnostics.ts +75 -0
- package/packages/daemon/src/command-center/log-stream.ts +8 -0
- package/packages/daemon/src/command-center/resources/fleet-preview-route.ts +27 -5
- package/packages/daemon/src/command-center/server.ts +8 -0
- package/packages/daemon/src/doctor.ts +70 -0
- package/packages/daemon/src/lib/app-config.ts +11 -3
- package/packages/daemon/src/lib/canonical-daemon.ts +1 -0
- package/packages/daemon/src/lib/daemon-embed.ts +30 -0
- package/packages/daemon/src/lib/daemon-provenance.ts +769 -0
- package/packages/daemon/src/lib/fleet-preview-model.ts +1 -0
- package/packages/daemon/src/lib/headless-daemon.ts +1 -0
- package/packages/daemon/src/lib/log-sanitize.ts +248 -0
- package/packages/daemon/src/lib/log.ts +165 -14
- package/packages/daemon/src/lib/soak-diagnostics.ts +183 -0
- package/packages/daemon/src/lib/soak-verdict.ts +785 -0
- package/packages/daemon/src/lib/terminal-host-color.ts +43 -0
- package/packages/daemon/src/lib/tmux-external-interaction-observer.ts +233 -37
- package/packages/daemon/src/lib/tmux-interaction-retention.ts +24 -0
- package/packages/daemon/src/lib/workspace-promotion.ts +74 -45
- package/packages/daemon/src/terminal/mirror/session-channel.ts +2 -1
- package/packages/daemon/src/tui/mirror/automatic-contrast.ts +180 -0
- package/packages/daemon/src/tui/mirror/open-tui-workspace-runtime-port.ts +26 -5
- package/packages/daemon/src/tui/mirror/pane-surface.tsx +9 -8
- package/packages/daemon/src/tui/mirror/resize-transaction.ts +46 -22
- package/packages/daemon/src/tui/mirror/runtime/application-appearance-owner.ts +45 -6
- package/packages/daemon/src/tui/mirror/runtime/application-fleet-preview.ts +4 -0
- package/packages/daemon/src/tui/mirror/runtime/application-fleet-switcher.tsx +2 -1
- package/packages/daemon/src/tui/mirror/runtime/application-machine-sidebar.tsx +99 -75
- package/packages/daemon/src/tui/mirror/runtime/application-palette-preview.tsx +13 -3
- package/packages/daemon/src/tui/mirror/runtime/application-reference-sheet.tsx +125 -0
- package/packages/daemon/src/tui/mirror/runtime/application-root-v2.tsx +2 -1
- package/packages/daemon/src/tui/mirror/runtime/application-shell-overlays.tsx +199 -157
- package/packages/daemon/src/tui/mirror/runtime/application-shell-view.tsx +2 -0
- package/packages/daemon/src/tui/mirror/runtime/application-terminal-interaction-controller.ts +148 -69
- package/packages/daemon/src/tui/mirror/runtime/application-terminal-palette-owner.ts +55 -28
- package/packages/daemon/src/tui/mirror/runtime/application-terminal-workspace.tsx +59 -17
- package/packages/daemon/src/tui/mirror/runtime/semantic-shell-viewport-resize.ts +151 -3
- package/packages/daemon/src/tui/mirror/runtime/workspace-terminal-fast-lane.ts +3 -1
- package/packages/daemon/src/tui/mirror/semantic-pane-render-source.ts +2 -1
- package/packages/daemon/src/tui/mirror/theme.ts +4 -39
- package/packages/daemon/src/tui/mirror/workspace/terminal-pane-header.tsx +21 -8
- package/packages/daemon/src/tui/team/wait-receipts.ts +115 -40
- package/packages/daemon-client/src/terminal-fast-lane.test.ts +18 -0
- package/packages/daemon-client/src/terminal-fast-lane.ts +7 -2
- package/packages/tmux-bridge/src/index.ts +2 -0
- package/packages/tmux-bridge/src/runner.test.ts +41 -0
- package/packages/tmux-bridge/src/runner.ts +41 -2
|
@@ -19,6 +19,7 @@ export async function streamBoundedLogs(
|
|
|
19
19
|
entries?: number;
|
|
20
20
|
bytes?: number;
|
|
21
21
|
writeTimeoutMs?: number;
|
|
22
|
+
heartbeatIntervalMs?: number;
|
|
22
23
|
},
|
|
23
24
|
): Promise<void> {
|
|
24
25
|
const maxEntries = options.entries ?? 256;
|
|
@@ -29,6 +30,7 @@ export async function streamBoundedLogs(
|
|
|
29
30
|
let wake: (() => void) | null = null;
|
|
30
31
|
let unsubscribe = () => {};
|
|
31
32
|
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
33
|
+
let heartbeatTimer: ReturnType<typeof setInterval> | null = null;
|
|
32
34
|
let cancelWrite: (() => void) | null = null;
|
|
33
35
|
const cleanup = () => {
|
|
34
36
|
if (closed) return;
|
|
@@ -38,6 +40,8 @@ export async function streamBoundedLogs(
|
|
|
38
40
|
bytes = 0;
|
|
39
41
|
if (timer) clearTimeout(timer);
|
|
40
42
|
timer = null;
|
|
43
|
+
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
44
|
+
heartbeatTimer = null;
|
|
41
45
|
wake?.();
|
|
42
46
|
wake = null;
|
|
43
47
|
cancelWrite?.();
|
|
@@ -67,6 +71,10 @@ export async function streamBoundedLogs(
|
|
|
67
71
|
unsubscribe();
|
|
68
72
|
return;
|
|
69
73
|
}
|
|
74
|
+
heartbeatTimer = setInterval(() => {
|
|
75
|
+
if (!closed && queue.length === 0 && !push({ event: "heartbeat", data: "keep-alive" })) abort();
|
|
76
|
+
}, options.heartbeatIntervalMs ?? 15_000);
|
|
77
|
+
heartbeatTimer.unref?.();
|
|
70
78
|
try {
|
|
71
79
|
// Keep newest bounded history, reserving room for gap/bookmark metadata.
|
|
72
80
|
const retained: Frame[] = [];
|
|
@@ -15,6 +15,7 @@ type Capture = ((
|
|
|
15
15
|
liveSessionId: string,
|
|
16
16
|
signal?: AbortSignal,
|
|
17
17
|
windowId?: string,
|
|
18
|
+
paneId?: string,
|
|
18
19
|
) => Promise<FleetPreviewSnapshot | null>;
|
|
19
20
|
};
|
|
20
21
|
const cleanText = (value: string) => stripVTControlCharacters(value).replace(/[^\P{Cc}\n\t]/gu, "");
|
|
@@ -26,6 +27,7 @@ export function createFleetPreviewCapture(
|
|
|
26
27
|
liveSessionId: string,
|
|
27
28
|
signal?: AbortSignal,
|
|
28
29
|
windowId?: string,
|
|
30
|
+
paneId?: string,
|
|
29
31
|
): Promise<FleetPreviewSnapshot | null> => {
|
|
30
32
|
const readSessions = async () => {
|
|
31
33
|
const raw = await run(
|
|
@@ -76,8 +78,14 @@ export function createFleetPreviewCapture(
|
|
|
76
78
|
);
|
|
77
79
|
}
|
|
78
80
|
const windowRows = rows.filter((row) => /^@\d+$/u.test(row[3] ?? ""));
|
|
81
|
+
const requestedPane = paneId ? windowRows.find((row) => row[0] === paneId) : undefined;
|
|
82
|
+
if (paneId && (!requestedPane || (windowId && requestedPane[3] !== windowId))) return null;
|
|
79
83
|
const selectedWindowId =
|
|
80
|
-
|
|
84
|
+
requestedPane?.[3] ??
|
|
85
|
+
windowId ??
|
|
86
|
+
windowRows.find((row) => row[1] === "1")?.[3] ??
|
|
87
|
+
windowRows[0]?.[3] ??
|
|
88
|
+
null;
|
|
81
89
|
if (windowId && !windowRows.some((row) => row[3] === windowId)) return null;
|
|
82
90
|
const toWindow = (row: string[]): FleetPreviewWindow => ({
|
|
83
91
|
id: row[3]!,
|
|
@@ -116,7 +124,7 @@ export function createFleetPreviewCapture(
|
|
|
116
124
|
}
|
|
117
125
|
windows.sort((a, b) => a.index - b.index);
|
|
118
126
|
const candidates = selectedWindowId ? rows.filter((r) => r[3] === selectedWindowId) : rows;
|
|
119
|
-
const pane = candidates.find((r) => r[2] === "1")?.[0] ?? candidates[0]?.[0];
|
|
127
|
+
const pane = paneId ?? candidates.find((r) => r[2] === "1")?.[0] ?? candidates[0]?.[0];
|
|
120
128
|
if (!pane || !/^%\d+$/u.test(pane)) return null;
|
|
121
129
|
// Passive capture never changes active windows, size, input or terminal ownership.
|
|
122
130
|
const captured = await run(["capture-pane", "-p", "-t", pane, "-S", "-24"], signal);
|
|
@@ -126,6 +134,7 @@ export function createFleetPreviewCapture(
|
|
|
126
134
|
return null;
|
|
127
135
|
return {
|
|
128
136
|
windows,
|
|
137
|
+
selectedPaneId: pane,
|
|
129
138
|
selectedWindowId,
|
|
130
139
|
text: cleanText(captured)
|
|
131
140
|
.split("\n")
|
|
@@ -141,6 +150,10 @@ export function createFleetPreviewCapture(
|
|
|
141
150
|
}
|
|
142
151
|
const requestSchema = z.strictObject({
|
|
143
152
|
expectedInstanceId: z.uuid(),
|
|
153
|
+
paneId: z
|
|
154
|
+
.string()
|
|
155
|
+
.regex(/^%\d+$/u)
|
|
156
|
+
.optional(),
|
|
144
157
|
windowId: z
|
|
145
158
|
.string()
|
|
146
159
|
.regex(/^@\d+$/u)
|
|
@@ -189,10 +202,15 @@ export function mountFleetPreviewRoute(
|
|
|
189
202
|
pending = true;
|
|
190
203
|
try {
|
|
191
204
|
const signal = AbortSignal.any([c.req.raw.signal, AbortSignal.timeout(1500)]);
|
|
192
|
-
if (input.data.windowId && !options.capture.snapshot)
|
|
205
|
+
if ((input.data.windowId || input.data.paneId) && !options.capture.snapshot)
|
|
193
206
|
return c.json({ error: "Window preview unavailable" }, 503);
|
|
194
207
|
const snapshot = options.capture.snapshot
|
|
195
|
-
? await options.capture.snapshot(
|
|
208
|
+
? await options.capture.snapshot(
|
|
209
|
+
input.data.liveSessionId,
|
|
210
|
+
signal,
|
|
211
|
+
input.data.windowId,
|
|
212
|
+
input.data.paneId,
|
|
213
|
+
)
|
|
196
214
|
: null;
|
|
197
215
|
// Older capture providers remain valid for the original text-only contract.
|
|
198
216
|
const legacy = !options.capture.snapshot
|
|
@@ -206,7 +224,11 @@ export function mountFleetPreviewRoute(
|
|
|
206
224
|
liveSessionId: input.data.liveSessionId,
|
|
207
225
|
text,
|
|
208
226
|
...(snapshot
|
|
209
|
-
? {
|
|
227
|
+
? {
|
|
228
|
+
windows: snapshot.windows,
|
|
229
|
+
selectedWindowId: snapshot.selectedWindowId,
|
|
230
|
+
selectedPaneId: snapshot.selectedPaneId,
|
|
231
|
+
}
|
|
210
232
|
: {}),
|
|
211
233
|
});
|
|
212
234
|
} catch {
|
|
@@ -155,6 +155,7 @@ import { mountWorkspaceResourceRoutes } from "./resources/workspace-resource-rou
|
|
|
155
155
|
import { mountFleetResourceRoute } from "./resources/fleet-resource-route.ts";
|
|
156
156
|
import { mountWorkspaceMissionsRoute } from "./resources/workspace-missions-route.ts";
|
|
157
157
|
import { ownerAuthorityGate, requireOwnerAuthority } from "./owner-authority.ts";
|
|
158
|
+
import { mountDiagnosticsRoute } from "./diagnostics.ts";
|
|
158
159
|
import {
|
|
159
160
|
mountStartupReadinessRoute,
|
|
160
161
|
type StartupReadinessAttachmentAuthority,
|
|
@@ -532,6 +533,13 @@ export function createApp(options: CreateAppOptions = {}): Hono {
|
|
|
532
533
|
// Allow cross-origin (Next.js dashboard, Tailscale, etc.)
|
|
533
534
|
app.use("/*", cors());
|
|
534
535
|
|
|
536
|
+
// Owner-only reads use the same early routing boundary as issuance below:
|
|
537
|
+
// remote/project credentials neither authorize nor block the owner bearer.
|
|
538
|
+
mountDiagnosticsRoute(app, {
|
|
539
|
+
daemon: daemonInstanceIdentity,
|
|
540
|
+
ownerToken: options.remoteAccess?.ownerToken ?? null,
|
|
541
|
+
});
|
|
542
|
+
|
|
535
543
|
// This exact route carries its own owner-only bearer and correlation gate.
|
|
536
544
|
// Mount it before remote and project auth so a valid host capability is
|
|
537
545
|
// neither rejected nor confused with any remotely shared user credential.
|
|
@@ -9,6 +9,10 @@ import { findCompiledTui, hasDevelopmentTuiSource, isBunAvailable } from "./tui/
|
|
|
9
9
|
import { claudeSettingsPath } from "./tui/integrations/claude.ts";
|
|
10
10
|
import { readNotificationPrefs, resolveNativeMacosNotifierPath } from "./tui/chrome/notify.ts";
|
|
11
11
|
import { resolveConfig } from "./lib/resolved-config.ts";
|
|
12
|
+
import {
|
|
13
|
+
collectDaemonProvenanceReport,
|
|
14
|
+
type DaemonProvenanceReport,
|
|
15
|
+
} from "./lib/daemon-provenance.ts";
|
|
12
16
|
|
|
13
17
|
interface CheckResult {
|
|
14
18
|
label: string;
|
|
@@ -108,6 +112,55 @@ export function notifierRow(present: boolean): CheckResult {
|
|
|
108
112
|
};
|
|
109
113
|
}
|
|
110
114
|
|
|
115
|
+
/**
|
|
116
|
+
* PURE — the "canonical daemon" row from a provenance report. Optional: a
|
|
117
|
+
* machine without a running daemon is healthy; the row exists so triage sees
|
|
118
|
+
* identity, supervisor and the real log destination without a second command.
|
|
119
|
+
*/
|
|
120
|
+
export function daemonProvenanceRow(report: DaemonProvenanceReport): CheckResult {
|
|
121
|
+
const label = "canonical daemon";
|
|
122
|
+
const historical = report.logFiles.filter((f) => f.status === "historical").length;
|
|
123
|
+
const suffix = historical
|
|
124
|
+
? ` — ${historical} historical log file${historical === 1 ? "" : "s"} (see \`tmux-ide daemon info\`)`
|
|
125
|
+
: "";
|
|
126
|
+
const d = report.daemon;
|
|
127
|
+
switch (report.status) {
|
|
128
|
+
case "running":
|
|
129
|
+
return {
|
|
130
|
+
label,
|
|
131
|
+
pass: true,
|
|
132
|
+
detail:
|
|
133
|
+
`running v${d!.productVersion} pid ${d!.pid} (${d!.supervisor ?? "unknown supervisor"}) · ` +
|
|
134
|
+
`logs → ${d!.logDestination}${suffix}`,
|
|
135
|
+
optional: true,
|
|
136
|
+
};
|
|
137
|
+
case "stale-record":
|
|
138
|
+
return {
|
|
139
|
+
label,
|
|
140
|
+
pass: false,
|
|
141
|
+
detail: `record names pid ${d!.pid} (v${d!.productVersion}) but it is not running${suffix}`,
|
|
142
|
+
optional: true,
|
|
143
|
+
};
|
|
144
|
+
case "record-invalid":
|
|
145
|
+
return {
|
|
146
|
+
label,
|
|
147
|
+
pass: false,
|
|
148
|
+
detail: `record invalid (${report.record.status === "invalid" ? report.record.reason : "unknown"})${suffix}`,
|
|
149
|
+
optional: true,
|
|
150
|
+
};
|
|
151
|
+
default:
|
|
152
|
+
return {
|
|
153
|
+
label,
|
|
154
|
+
pass: true,
|
|
155
|
+
detail:
|
|
156
|
+
report.record.status === "reserved"
|
|
157
|
+
? `not running (reserved for supervisor ${report.record.supervisionId})${suffix}`
|
|
158
|
+
: `not running${suffix}`,
|
|
159
|
+
optional: true,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
111
164
|
function check(
|
|
112
165
|
label: string,
|
|
113
166
|
fn: () => string,
|
|
@@ -328,6 +381,23 @@ export async function doctor({
|
|
|
328
381
|
),
|
|
329
382
|
);
|
|
330
383
|
|
|
384
|
+
// Canonical daemon identity + log provenance (credential-free), so a triage
|
|
385
|
+
// never chases a stale, manually redirected log file.
|
|
386
|
+
checks.push(
|
|
387
|
+
(() => {
|
|
388
|
+
try {
|
|
389
|
+
return daemonProvenanceRow(collectDaemonProvenanceReport());
|
|
390
|
+
} catch (e) {
|
|
391
|
+
return {
|
|
392
|
+
label: "canonical daemon",
|
|
393
|
+
pass: false,
|
|
394
|
+
detail: `provenance unavailable: ${(e as Error).message}`,
|
|
395
|
+
optional: true,
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
})(),
|
|
399
|
+
);
|
|
400
|
+
|
|
331
401
|
// Native branded sender: only when the macOS channel is actually on.
|
|
332
402
|
if (process.platform === "darwin" && readNotificationPrefs().macos) {
|
|
333
403
|
checks.push(notifierRow(resolveNativeMacosNotifierPath() !== null));
|
|
@@ -84,7 +84,9 @@ export interface AppThemeGlyphs {
|
|
|
84
84
|
* reads as one system.
|
|
85
85
|
*/
|
|
86
86
|
export interface AppTheme {
|
|
87
|
-
/**
|
|
87
|
+
/** Improve composed app text contrast. Default true. */
|
|
88
|
+
automaticContrast: boolean;
|
|
89
|
+
/** Explicit palette mode or terminal-following mode. Defaults to following the terminal. */
|
|
88
90
|
mode: ThemeModeSetting;
|
|
89
91
|
preset?: string;
|
|
90
92
|
/** Primary/brand accent (default `colour75`). */
|
|
@@ -263,7 +265,8 @@ export const DEFAULT_APP_CONFIG: AppConfig = {
|
|
|
263
265
|
panels: { explorer: "M-e", changes: "M-g", config: "M-," },
|
|
264
266
|
},
|
|
265
267
|
theme: {
|
|
266
|
-
|
|
268
|
+
automaticContrast: true,
|
|
269
|
+
mode: "system",
|
|
267
270
|
accent: "colour75",
|
|
268
271
|
muted: "colour240",
|
|
269
272
|
fg: "colour250",
|
|
@@ -395,7 +398,12 @@ export function parseAppConfig(input: unknown): AppConfig {
|
|
|
395
398
|
},
|
|
396
399
|
},
|
|
397
400
|
theme: {
|
|
398
|
-
|
|
401
|
+
automaticContrast: pickBool(theme.automaticContrast, D.theme.automaticContrast),
|
|
402
|
+
mode: pickChoice(
|
|
403
|
+
theme.mode,
|
|
404
|
+
["dark", "light", "system"],
|
|
405
|
+
findVisualThemePreset(theme.preset)?.appearance ?? D.theme.mode,
|
|
406
|
+
),
|
|
399
407
|
...(findVisualThemePreset(theme.preset) ? { preset: String(theme.preset) } : {}),
|
|
400
408
|
accent: pickString(theme.accent, D.theme.accent),
|
|
401
409
|
muted: pickString(theme.muted, D.theme.muted),
|
|
@@ -646,6 +646,7 @@ export function writeCanonicalDaemonInfo(
|
|
|
646
646
|
...(info.environmentId !== undefined ? { environmentId: info.environmentId } : {}),
|
|
647
647
|
bindHostname: info.bindHostname,
|
|
648
648
|
authToken: info.authToken,
|
|
649
|
+
...(info.provenance !== undefined ? { provenance: info.provenance } : {}),
|
|
649
650
|
};
|
|
650
651
|
writeFileSync(tmpPath, JSON.stringify(persisted, null, 2) + "\n", {
|
|
651
652
|
encoding: "utf-8",
|
|
@@ -104,6 +104,8 @@ import {
|
|
|
104
104
|
} from "./pane-source-credentials.ts";
|
|
105
105
|
import { setActivationBackend, type ProjectActivationOptions } from "./active-projects.ts";
|
|
106
106
|
import { readOrMintEnvironmentId } from "./environment-identity.ts";
|
|
107
|
+
import { captureDaemonProvenance } from "./daemon-provenance.ts";
|
|
108
|
+
import { registerLogSecret, setLogIdentity } from "./log.ts";
|
|
107
109
|
import {
|
|
108
110
|
canonicalDaemonUrl,
|
|
109
111
|
clearCanonicalDaemonInfoIfOwned,
|
|
@@ -163,6 +165,12 @@ export function resolveDaemonProductVersion(
|
|
|
163
165
|
export interface EmbeddedDaemonOptions {
|
|
164
166
|
/** @internal Headless lifecycle only; explicit reservation, never inferred from ancestry. */
|
|
165
167
|
supervisionId?: string;
|
|
168
|
+
/**
|
|
169
|
+
* @internal How this process came to own the daemon: the foreground
|
|
170
|
+
* `--headless` owner or a host embedding `startEmbeddedDaemon` directly.
|
|
171
|
+
* Stamped into the daemon record for triage; defaults to "embedded".
|
|
172
|
+
*/
|
|
173
|
+
launcher?: "headless" | "embedded";
|
|
166
174
|
/** @internal Successful previous generation stop in this same process. */
|
|
167
175
|
predecessor?: CanonicalDaemonPredecessor;
|
|
168
176
|
/** @internal Let the foreground lifecycle owner serialize settings restarts. */
|
|
@@ -1190,6 +1198,9 @@ async function startEmbeddedDaemonGeneration(
|
|
|
1190
1198
|
internalReadOwnerToken: localBypassToken,
|
|
1191
1199
|
registry: workspaceRegistry,
|
|
1192
1200
|
tmuxAuthority,
|
|
1201
|
+
// A gap cannot reconstruct historical interactions. Refresh inventory
|
|
1202
|
+
// facts while authored operations retain their observation deadlines.
|
|
1203
|
+
onGap: () => terminalInventoryRuntime?.invalidate(),
|
|
1193
1204
|
onObserved: ({ workspaceName, semanticPaneId, operationKind, operationId }) => {
|
|
1194
1205
|
if (operationKind !== "workspace.pane.read") terminalInventoryRuntime?.invalidate();
|
|
1195
1206
|
if (operationId) {
|
|
@@ -1558,6 +1569,24 @@ async function startEmbeddedDaemonGeneration(
|
|
|
1558
1569
|
console.error("[daemon] Direct terminal startup rollback reported cleanup failures.");
|
|
1559
1570
|
}
|
|
1560
1571
|
};
|
|
1572
|
+
// Provenance (launcher, supervisor, actual stdout/stderr destination) is
|
|
1573
|
+
// derived from this process, never from file-name conventions, so a later
|
|
1574
|
+
// `daemon info` reads the truth from the record. Capture never throws;
|
|
1575
|
+
// whatever it could not determine is reported as a warning.
|
|
1576
|
+
const provenance = captureDaemonProvenance({
|
|
1577
|
+
launcher: opts.launcher ?? "embedded",
|
|
1578
|
+
...(opts.supervisionId ? { supervisionId: opts.supervisionId } : {}),
|
|
1579
|
+
});
|
|
1580
|
+
if (provenance.warnings?.length && !opts.silent) {
|
|
1581
|
+
for (const warning of provenance.warnings) {
|
|
1582
|
+
console.warn(`[daemon] log provenance degraded: ${warning}`);
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
// Every structured record now carries this generation's identity, and the
|
|
1586
|
+
// local bypass / shared tokens are redacted wherever they might appear.
|
|
1587
|
+
setLogIdentity({ instanceId, version: productVersion });
|
|
1588
|
+
registerLogSecret(localBypassToken);
|
|
1589
|
+
registerLogSecret(opts.authToken);
|
|
1561
1590
|
try {
|
|
1562
1591
|
writeCanonicalDaemonInfo(
|
|
1563
1592
|
{
|
|
@@ -1571,6 +1600,7 @@ async function startEmbeddedDaemonGeneration(
|
|
|
1571
1600
|
environmentId,
|
|
1572
1601
|
bindHostname,
|
|
1573
1602
|
authToken: localBypassToken,
|
|
1603
|
+
provenance,
|
|
1574
1604
|
},
|
|
1575
1605
|
claim,
|
|
1576
1606
|
);
|