@treeport/treeport 0.5.0 → 0.7.0
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/README.md +1 -1
- package/bin/treeport.mjs +26 -1
- package/dist/dist-BsLn2Gbc.js +1630 -0
- package/dist/node/cli/index.js +226 -73
- package/dist/node/server/core/launcher.js +13 -22
- package/dist/node/server/index.js +10440 -5957
- package/dist/node/server/terminal-host-entry.js +962 -0
- package/dist/{shell-integration-Be_c91lw.js → shell-integration-CPmrVa3B.js} +46 -46
- package/dist/terminal-host-protocol-DZkQRAUF.js +378 -0
- package/dist/{update-BW-a6Bd-.js → update-UYS2lMdD.js} +86 -447
- package/dist/web/assets/index-BWYDUD7N.css +2 -0
- package/dist/web/assets/index-C5cx0N4G.js +84 -0
- package/dist/web/index.html +2 -2
- package/drizzle/0010_browser_panels_and_permissions.sql +21 -0
- package/drizzle/0011_free_magdalene.sql +1 -0
- package/drizzle/0012_terminal_host_cutover.sql +41 -0
- package/drizzle/0013_workspace_item_order.sql +13 -0
- package/drizzle/meta/0010_snapshot.json +923 -0
- package/drizzle/meta/0011_snapshot.json +931 -0
- package/drizzle/meta/0012_snapshot.json +919 -0
- package/drizzle/meta/0013_snapshot.json +987 -0
- package/drizzle/meta/_journal.json +28 -0
- package/package.json +21 -13
- package/skills/treeport/SKILL.md +62 -6
- package/dist/web/assets/index-Cr4UkmRD.js +0 -146
- package/dist/web/assets/index-he-SubzL.css +0 -2
|
@@ -1,402 +1,12 @@
|
|
|
1
|
+
import { Ct as projectsResponseSchema, U as decodeUnknownOrNull, ct as healthResponseSchema } from "./dist-BsLn2Gbc.js";
|
|
1
2
|
import fs from "node:fs/promises";
|
|
2
3
|
import path from "node:path";
|
|
3
|
-
import { z } from "zod";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
5
|
import crypto from "node:crypto";
|
|
6
6
|
import fsSync, { constants } from "node:fs";
|
|
7
7
|
import os from "node:os";
|
|
8
|
+
import { z } from "zod";
|
|
8
9
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
9
|
-
//#region ../../packages/shared/dist/terminal-protocol.js
|
|
10
|
-
const SOCKET_IO_PATH = "/api/socket.io/";
|
|
11
|
-
const TERMINAL_CONTROLLER_GRACE_MS = 1e4;
|
|
12
|
-
const TERMINAL_OUTPUT_HIGH_WATERMARK = 256 * 1024;
|
|
13
|
-
const TERMINAL_OUTPUT_LOW_WATERMARK = 64 * 1024;
|
|
14
|
-
const TERMINAL_OUTPUT_STALL_TIMEOUT_MS = 3e4;
|
|
15
|
-
const TERMINAL_MAX_CLIENT_MESSAGE_BYTES = 128 * 1024;
|
|
16
|
-
const TERMINAL_MAX_INPUT_BYTES = 64 * 1024;
|
|
17
|
-
const TERMINAL_SCROLL_EXIT_SEQUENCE = "\x1B[9000~";
|
|
18
|
-
const TERMINAL_SELECTION_START_SEQUENCE = "\x1B[9001~";
|
|
19
|
-
const TERMINAL_SELECTION_STOP_SEQUENCE = "\x1B[9002~";
|
|
20
|
-
const TERMINAL_SELECTION_CLEAR_SEQUENCE = "\x1B[9003~";
|
|
21
|
-
const TERMINAL_SELECTION_RESTORE_SEQUENCE = "\x1B[9004~";
|
|
22
|
-
z.unknown();
|
|
23
|
-
const terminalId = z.string().min(1).max(128);
|
|
24
|
-
const clientId = z.string().min(1).max(128);
|
|
25
|
-
const streamId = z.string().min(1).max(128);
|
|
26
|
-
const generation = z.number().int().nonnegative();
|
|
27
|
-
const dimensions = {
|
|
28
|
-
cols: z.number().int().min(2).max(1e3),
|
|
29
|
-
rows: z.number().int().min(2).max(500)
|
|
30
|
-
};
|
|
31
|
-
const terminalSizeSchema = z.strictObject(dimensions);
|
|
32
|
-
const terminalProgressSchema = z.strictObject({
|
|
33
|
-
state: z.enum([
|
|
34
|
-
"normal",
|
|
35
|
-
"error",
|
|
36
|
-
"indeterminate",
|
|
37
|
-
"paused"
|
|
38
|
-
]),
|
|
39
|
-
value: z.number().int().min(0).max(100).nullable()
|
|
40
|
-
});
|
|
41
|
-
const terminalProgramSchema = z.enum([
|
|
42
|
-
"pi",
|
|
43
|
-
"claude",
|
|
44
|
-
"codex"
|
|
45
|
-
]);
|
|
46
|
-
const terminalRuntimeMetadataSchema = z.strictObject({
|
|
47
|
-
terminalId: z.string().min(1),
|
|
48
|
-
title: z.string().max(256).nullable(),
|
|
49
|
-
program: terminalProgramSchema.nullable().default(null),
|
|
50
|
-
hasForegroundProcess: z.boolean().nullable().optional(),
|
|
51
|
-
progress: terminalProgressSchema.nullable(),
|
|
52
|
-
progressStartedAt: z.string().datetime().nullable().default(null),
|
|
53
|
-
progressClearedAt: z.string().datetime().nullable().default(null),
|
|
54
|
-
bell: z.strictObject({
|
|
55
|
-
sequence: z.number().int().positive(),
|
|
56
|
-
at: z.string().datetime(),
|
|
57
|
-
unread: z.boolean()
|
|
58
|
-
}).nullable().default(null)
|
|
59
|
-
});
|
|
60
|
-
const terminalBellAcknowledgementSchema = z.strictObject({ sequence: z.number().int().positive() });
|
|
61
|
-
function parseTerminalProgress(data) {
|
|
62
|
-
const [command, rawState, rawValue, ...extra] = data.split(";");
|
|
63
|
-
if (command !== "4" || extra.length > 0 || !/^[0-4]$/.test(rawState ?? "")) return;
|
|
64
|
-
const state = Number(rawState);
|
|
65
|
-
if (state === 0) return null;
|
|
66
|
-
if (rawValue !== void 0 && rawValue !== "" && !/^\d{1,3}$/.test(rawValue)) return;
|
|
67
|
-
const value = rawValue === void 0 || rawValue === "" ? null : Number(rawValue);
|
|
68
|
-
if (value !== null && value > 100) return;
|
|
69
|
-
return {
|
|
70
|
-
state: [
|
|
71
|
-
void 0,
|
|
72
|
-
"normal",
|
|
73
|
-
"error",
|
|
74
|
-
"indeterminate",
|
|
75
|
-
"paused"
|
|
76
|
-
][state],
|
|
77
|
-
value
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
const terminalAuthSchema = z.strictObject({
|
|
81
|
-
terminalId,
|
|
82
|
-
clientId,
|
|
83
|
-
...dimensions
|
|
84
|
-
});
|
|
85
|
-
const terminalInputSchema = z.strictObject({
|
|
86
|
-
generation,
|
|
87
|
-
data: z.string().max(TERMINAL_MAX_INPUT_BYTES)
|
|
88
|
-
});
|
|
89
|
-
const terminalBinarySchema = z.strictObject({
|
|
90
|
-
generation,
|
|
91
|
-
data: z.string().max(TERMINAL_MAX_INPUT_BYTES)
|
|
92
|
-
});
|
|
93
|
-
const terminalResizeSchema = z.strictObject({
|
|
94
|
-
generation,
|
|
95
|
-
...dimensions
|
|
96
|
-
});
|
|
97
|
-
const terminalTakeControlSchema = z.strictObject({
|
|
98
|
-
generation,
|
|
99
|
-
...dimensions
|
|
100
|
-
});
|
|
101
|
-
const terminalLegacyTakeControlSchema = z.strictObject({ generation });
|
|
102
|
-
const terminalOutputAckSchema = z.strictObject({
|
|
103
|
-
streamId,
|
|
104
|
-
sequence: z.number().int().nonnegative()
|
|
105
|
-
});
|
|
106
|
-
const terminalReadyBase = {
|
|
107
|
-
connectionId: z.string().min(1).max(128),
|
|
108
|
-
streamId,
|
|
109
|
-
generation,
|
|
110
|
-
controller: z.boolean(),
|
|
111
|
-
reset: z.literal("full")
|
|
112
|
-
};
|
|
113
|
-
const terminalLegacyReadySchema = z.strictObject(terminalReadyBase);
|
|
114
|
-
const terminalReadyV2Schema = z.strictObject({
|
|
115
|
-
...terminalReadyBase,
|
|
116
|
-
...dimensions,
|
|
117
|
-
revision: z.number().int().positive()
|
|
118
|
-
});
|
|
119
|
-
z.union([terminalLegacyReadySchema, terminalReadyV2Schema]);
|
|
120
|
-
z.strictObject({
|
|
121
|
-
...dimensions,
|
|
122
|
-
revision: z.number().int().positive()
|
|
123
|
-
});
|
|
124
|
-
z.strictObject({
|
|
125
|
-
streamId,
|
|
126
|
-
sequence: z.number().int().positive(),
|
|
127
|
-
data: z.string()
|
|
128
|
-
});
|
|
129
|
-
z.strictObject({ title: z.string().max(256) });
|
|
130
|
-
z.strictObject({ progress: terminalProgressSchema.nullable() });
|
|
131
|
-
z.strictObject({ viewing: z.boolean() });
|
|
132
|
-
z.strictObject({
|
|
133
|
-
generation,
|
|
134
|
-
controller: z.boolean()
|
|
135
|
-
});
|
|
136
|
-
z.strictObject({ exitCode: z.number().int().nullable() });
|
|
137
|
-
z.strictObject({
|
|
138
|
-
code: z.string().min(1).max(80),
|
|
139
|
-
message: z.string().min(1).max(1e3),
|
|
140
|
-
retryable: z.boolean()
|
|
141
|
-
});
|
|
142
|
-
function parseTerminalAuth(value) {
|
|
143
|
-
const parsed = terminalAuthSchema.safeParse(value);
|
|
144
|
-
return parsed.success ? parsed.data : null;
|
|
145
|
-
}
|
|
146
|
-
//#endregion
|
|
147
|
-
//#region ../../packages/shared/dist/socket-protocol.js
|
|
148
|
-
const identifierSchema = z.string().min(1).max(128);
|
|
149
|
-
const eventEnvelope = (type, data) => z.strictObject({
|
|
150
|
-
id: identifierSchema,
|
|
151
|
-
type: z.literal(type),
|
|
152
|
-
at: z.string().datetime(),
|
|
153
|
-
data
|
|
154
|
-
});
|
|
155
|
-
const projectEventDataSchema = z.strictObject({
|
|
156
|
-
projectId: identifierSchema,
|
|
157
|
-
worktreeId: z.null()
|
|
158
|
-
});
|
|
159
|
-
const worktreeEventDataSchema = z.strictObject({ worktreeId: identifierSchema });
|
|
160
|
-
const projectWorktreeEventDataSchema = z.strictObject({
|
|
161
|
-
projectId: identifierSchema,
|
|
162
|
-
worktreeId: identifierSchema
|
|
163
|
-
});
|
|
164
|
-
const operationEventDataSchema = z.strictObject({
|
|
165
|
-
operationId: identifierSchema,
|
|
166
|
-
worktreeId: identifierSchema
|
|
167
|
-
});
|
|
168
|
-
const productEventSchema = z.discriminatedUnion("type", [
|
|
169
|
-
eventEnvelope("project.created", projectEventDataSchema),
|
|
170
|
-
eventEnvelope("project.updated", projectEventDataSchema),
|
|
171
|
-
eventEnvelope("project.removed", projectEventDataSchema),
|
|
172
|
-
eventEnvelope("worktree.created", projectWorktreeEventDataSchema),
|
|
173
|
-
eventEnvelope("worktree.updated", worktreeEventDataSchema),
|
|
174
|
-
eventEnvelope("worktree.removed", projectWorktreeEventDataSchema),
|
|
175
|
-
eventEnvelope("create.started", z.strictObject({
|
|
176
|
-
projectId: identifierSchema,
|
|
177
|
-
operationId: identifierSchema,
|
|
178
|
-
worktreeId: z.null()
|
|
179
|
-
})),
|
|
180
|
-
eventEnvelope("create.completed", z.strictObject({
|
|
181
|
-
projectId: identifierSchema,
|
|
182
|
-
operationId: identifierSchema,
|
|
183
|
-
worktreeId: identifierSchema
|
|
184
|
-
})),
|
|
185
|
-
eventEnvelope("create.failed", z.strictObject({
|
|
186
|
-
projectId: identifierSchema,
|
|
187
|
-
operationId: identifierSchema,
|
|
188
|
-
worktreeId: z.null()
|
|
189
|
-
})),
|
|
190
|
-
eventEnvelope("terminal.created", z.strictObject({
|
|
191
|
-
projectId: identifierSchema.optional(),
|
|
192
|
-
worktreeId: identifierSchema,
|
|
193
|
-
terminalId: identifierSchema
|
|
194
|
-
})),
|
|
195
|
-
eventEnvelope("terminal.updated", z.strictObject({
|
|
196
|
-
worktreeId: identifierSchema,
|
|
197
|
-
terminalId: identifierSchema
|
|
198
|
-
})),
|
|
199
|
-
eventEnvelope("terminal.removed", z.strictObject({
|
|
200
|
-
worktreeId: identifierSchema,
|
|
201
|
-
terminalId: identifierSchema
|
|
202
|
-
})),
|
|
203
|
-
eventEnvelope("terminal.metadata", terminalRuntimeMetadataSchema.extend({ worktreeId: z.null() })),
|
|
204
|
-
eventEnvelope("terminal.controller_changed", z.strictObject({
|
|
205
|
-
terminalId: identifierSchema,
|
|
206
|
-
controlled: z.boolean(),
|
|
207
|
-
worktreeId: z.null()
|
|
208
|
-
})),
|
|
209
|
-
eventEnvelope("panel.created", z.strictObject({
|
|
210
|
-
worktreeId: identifierSchema,
|
|
211
|
-
panelId: identifierSchema
|
|
212
|
-
})),
|
|
213
|
-
eventEnvelope("panel.updated", z.strictObject({
|
|
214
|
-
worktreeId: identifierSchema,
|
|
215
|
-
panelId: identifierSchema
|
|
216
|
-
})),
|
|
217
|
-
eventEnvelope("panel.open_requested", z.strictObject({
|
|
218
|
-
worktreeId: identifierSchema,
|
|
219
|
-
panelId: identifierSchema,
|
|
220
|
-
sourceTerminalId: identifierSchema.nullable()
|
|
221
|
-
})),
|
|
222
|
-
eventEnvelope("panel.removed", z.strictObject({
|
|
223
|
-
worktreeId: identifierSchema,
|
|
224
|
-
panelId: identifierSchema
|
|
225
|
-
})),
|
|
226
|
-
eventEnvelope("workspace.open_requested", z.strictObject({
|
|
227
|
-
worktreeId: identifierSchema,
|
|
228
|
-
sourceTerminalId: identifierSchema
|
|
229
|
-
})),
|
|
230
|
-
eventEnvelope("remove.started", operationEventDataSchema.extend({ kind: z.literal("remove") })),
|
|
231
|
-
eventEnvelope("remove.completed", operationEventDataSchema),
|
|
232
|
-
eventEnvelope("remove.failed", operationEventDataSchema.extend({ error: z.string() }))
|
|
233
|
-
]);
|
|
234
|
-
const webPanelSnapshotSchema = z.strictObject({
|
|
235
|
-
id: z.string().min(1),
|
|
236
|
-
kind: z.literal("web"),
|
|
237
|
-
worktreeId: z.string().min(1),
|
|
238
|
-
definitionId: z.string().min(1),
|
|
239
|
-
title: z.string().min(1),
|
|
240
|
-
launch: z.strictObject({
|
|
241
|
-
input: z.record(z.string(), z.json()).nullable(),
|
|
242
|
-
cwd: z.string().nullable()
|
|
243
|
-
}),
|
|
244
|
-
sandbox: z.strictObject({ allowSameOrigin: z.boolean() }),
|
|
245
|
-
createdAt: z.string(),
|
|
246
|
-
updatedAt: z.string()
|
|
247
|
-
});
|
|
248
|
-
const eventsSnapshotSchema = z.strictObject({
|
|
249
|
-
at: z.string().datetime(),
|
|
250
|
-
terminalMetadata: z.array(terminalRuntimeMetadataSchema),
|
|
251
|
-
webPanels: z.array(webPanelSnapshotSchema)
|
|
252
|
-
});
|
|
253
|
-
z.unknown();
|
|
254
|
-
function parseEventsSnapshot(value) {
|
|
255
|
-
const parsed = eventsSnapshotSchema.safeParse(value);
|
|
256
|
-
return parsed.success ? parsed.data : null;
|
|
257
|
-
}
|
|
258
|
-
function parseProductEvent(value) {
|
|
259
|
-
const parsed = productEventSchema.safeParse(value);
|
|
260
|
-
return parsed.success ? parsed.data : null;
|
|
261
|
-
}
|
|
262
|
-
//#endregion
|
|
263
|
-
//#region ../../packages/shared/dist/index.js
|
|
264
|
-
const TERMINAL_MAX_UPLOAD_BYTES = 50 * 1024 * 1024;
|
|
265
|
-
const TERMINAL_EXECUTABLE_MAX_LENGTH = 4096;
|
|
266
|
-
const TERMINAL_ARGUMENT_MAX_LENGTH = 4096;
|
|
267
|
-
const TERMINAL_CAPTURE_MAX_LINES = 5e3;
|
|
268
|
-
const WEB_PANEL_INPUT_MAX_BYTES = 64 * 1024;
|
|
269
|
-
function formatCommandLine(argv) {
|
|
270
|
-
return argv.map((value) => {
|
|
271
|
-
if (value === "") return "\"\"";
|
|
272
|
-
if (!/[\s"'\\]/.test(value)) return value;
|
|
273
|
-
return `"${value.replace(/["\\]/g, "\\$&")}"`;
|
|
274
|
-
}).join(" ");
|
|
275
|
-
}
|
|
276
|
-
const PROJECT_COLORS = [
|
|
277
|
-
"rose",
|
|
278
|
-
"orange",
|
|
279
|
-
"amber",
|
|
280
|
-
"emerald",
|
|
281
|
-
"cyan",
|
|
282
|
-
"blue",
|
|
283
|
-
"violet",
|
|
284
|
-
"pink"
|
|
285
|
-
];
|
|
286
|
-
const browseDirectoryQuerySchema = z.object({
|
|
287
|
-
input: z.string().trim().min(1).max(4096),
|
|
288
|
-
hidden: z.enum(["true", "false"]).optional().default("false").transform((value) => value === "true")
|
|
289
|
-
});
|
|
290
|
-
const terminalCaptureQuerySchema = z.object({ lines: z.coerce.number().int().min(1).max(TERMINAL_CAPTURE_MAX_LINES).optional().default(200) });
|
|
291
|
-
const registerProjectSchema = z.object({
|
|
292
|
-
path: z.string().trim().min(1),
|
|
293
|
-
name: z.string().trim().min(1).max(120).optional()
|
|
294
|
-
});
|
|
295
|
-
const updateProjectSchema = z.object({ color: z.enum(PROJECT_COLORS).nullable() });
|
|
296
|
-
const terminalNameSchema = z.string().trim().min(1).max(120);
|
|
297
|
-
const terminalArgvSchema = z.array(z.string()).min(1).max(128);
|
|
298
|
-
const terminalPresetArgumentSchema = z.string().max(TERMINAL_ARGUMENT_MAX_LENGTH);
|
|
299
|
-
const terminalPresetFields = {
|
|
300
|
-
name: terminalNameSchema,
|
|
301
|
-
executable: z.string().min(1).max(TERMINAL_EXECUTABLE_MAX_LENGTH).refine((value) => value.trim().length > 0, { message: "Executable cannot be blank" }),
|
|
302
|
-
args: z.array(terminalPresetArgumentSchema).max(127),
|
|
303
|
-
closeOnSuccess: z.boolean().default(false)
|
|
304
|
-
};
|
|
305
|
-
const repositoryTerminalPresetSchema = z.strictObject(terminalPresetFields);
|
|
306
|
-
const repositoryTerminalPresetIdSchema = z.string().regex(/^[a-z0-9][a-z0-9._-]{0,119}$/, { message: "Preset IDs must contain only lowercase letters, numbers, dots, underscores, and hyphens" });
|
|
307
|
-
const repositoryTerminalPresetsFileSchema = z.strictObject({
|
|
308
|
-
version: z.literal(1),
|
|
309
|
-
presets: z.record(repositoryTerminalPresetIdSchema, z.unknown())
|
|
310
|
-
});
|
|
311
|
-
const terminalPresetRevisionSchema = z.string().min(1).max(64);
|
|
312
|
-
const initialTerminalSchema = z.object({
|
|
313
|
-
name: terminalNameSchema,
|
|
314
|
-
argv: terminalArgvSchema.optional(),
|
|
315
|
-
returnToShell: z.boolean().optional(),
|
|
316
|
-
initialSize: terminalSizeSchema.optional()
|
|
317
|
-
});
|
|
318
|
-
const createWorktreeSchema = z.object({
|
|
319
|
-
name: z.string().trim().min(1).max(120),
|
|
320
|
-
base: z.enum(["default", "current"]).default("default"),
|
|
321
|
-
sourceWorktreeId: z.string().min(1).optional(),
|
|
322
|
-
initialTerminal: initialTerminalSchema.optional()
|
|
323
|
-
}).superRefine((value, context) => {
|
|
324
|
-
if (value.base === "current" && !value.sourceWorktreeId) context.addIssue({
|
|
325
|
-
code: "custom",
|
|
326
|
-
path: ["sourceWorktreeId"],
|
|
327
|
-
message: "A source tree is required when starting from current"
|
|
328
|
-
});
|
|
329
|
-
});
|
|
330
|
-
const terminalCwdSchema = z.string().min(1).max(4096).refine((value) => value.trim().length > 0 && !value.includes("\0"), { message: "Working directory cannot be blank or contain NUL" });
|
|
331
|
-
const terminalEnvironmentKeySchema = z.string().min(1).max(256).refine((value) => !value.includes("=") && !value.includes("\0"), { message: "Environment keys cannot contain equals or NUL" });
|
|
332
|
-
const terminalShellCommandSchema = z.string().min(1).max(TERMINAL_ARGUMENT_MAX_LENGTH).refine((value) => value.trim().length > 0 && !value.includes("\0"), { message: "Shell command cannot be blank or contain NUL" });
|
|
333
|
-
const terminalEnvironmentSchema = z.record(terminalEnvironmentKeySchema, z.string().max(TERMINAL_ARGUMENT_MAX_LENGTH).refine((value) => !value.includes("\0"), { message: "Environment values cannot contain NUL" })).refine((value) => Object.keys(value).length <= 128, { message: "Environment cannot contain more than 128 variables" });
|
|
334
|
-
const createTerminalSchema = z.object({
|
|
335
|
-
name: terminalNameSchema,
|
|
336
|
-
argv: terminalArgvSchema.optional(),
|
|
337
|
-
shellCommand: terminalShellCommandSchema.optional(),
|
|
338
|
-
cwd: terminalCwdSchema.optional(),
|
|
339
|
-
env: terminalEnvironmentSchema.optional(),
|
|
340
|
-
returnToShell: z.boolean().optional(),
|
|
341
|
-
closeOnSuccess: z.boolean().optional(),
|
|
342
|
-
initialSize: terminalSizeSchema.optional()
|
|
343
|
-
}).refine((value) => !(value.argv && value.shellCommand), { message: "A terminal cannot have both argv and a shell command" }).refine((value) => !(value.returnToShell && value.closeOnSuccess), { message: "A terminal cannot return to a shell and close on success" });
|
|
344
|
-
const updateTerminalSchema = z.object({ name: terminalNameSchema });
|
|
345
|
-
const webPanelInputSchema = z.record(z.string(), z.json());
|
|
346
|
-
const createWebPanelSchema = z.object({
|
|
347
|
-
definitionId: z.string().min(1).max(256),
|
|
348
|
-
input: webPanelInputSchema.nullable().optional(),
|
|
349
|
-
launchCwd: z.string().max(4096).nullable().optional()
|
|
350
|
-
});
|
|
351
|
-
const openWebPanelSchema = createWebPanelSchema.extend({
|
|
352
|
-
newInstance: z.boolean().optional(),
|
|
353
|
-
sourceTerminalId: z.string().min(1).max(128).nullable().optional()
|
|
354
|
-
});
|
|
355
|
-
const requestWorkspaceOpenSchema = z.object({ sourceTerminalId: z.string().min(1).max(128) });
|
|
356
|
-
const webPanelStorageKeySchema = z.string().min(1).max(128);
|
|
357
|
-
const getWebPanelStorageSchema = z.object({ key: webPanelStorageKeySchema });
|
|
358
|
-
const setWebPanelStorageSchema = z.object({
|
|
359
|
-
key: webPanelStorageKeySchema,
|
|
360
|
-
value: z.json()
|
|
361
|
-
});
|
|
362
|
-
const deleteWebPanelStorageSchema = z.object({ key: webPanelStorageKeySchema });
|
|
363
|
-
const createTerminalPresetSchema = z.object(terminalPresetFields);
|
|
364
|
-
const updateTerminalPresetSchema = z.object({
|
|
365
|
-
...terminalPresetFields,
|
|
366
|
-
closeOnSuccess: z.boolean().optional(),
|
|
367
|
-
expectedUpdatedAt: terminalPresetRevisionSchema
|
|
368
|
-
});
|
|
369
|
-
const deleteTerminalPresetSchema = z.object({ expectedUpdatedAt: terminalPresetRevisionSchema });
|
|
370
|
-
const packageProjectQuerySchema = z.object({ path: z.string().trim().min(1).max(4096) });
|
|
371
|
-
const packageInstallSchema = z.object({
|
|
372
|
-
source: z.string().trim().min(1).max(4096),
|
|
373
|
-
projectId: z.string().min(1).optional()
|
|
374
|
-
});
|
|
375
|
-
const packageRemoveSchema = z.object({
|
|
376
|
-
source: z.string().trim().min(1).max(4096),
|
|
377
|
-
projectId: z.string().min(1).optional()
|
|
378
|
-
});
|
|
379
|
-
const packageUpdateSchema = z.object({ source: z.string().trim().min(1).max(4096).optional() });
|
|
380
|
-
const packageReloadSchema = z.object({ projectId: z.string().min(1).optional() });
|
|
381
|
-
const removeWorktreeSchema = z.object({
|
|
382
|
-
confirmationToken: z.string().length(64),
|
|
383
|
-
confirmDestructive: z.boolean()
|
|
384
|
-
});
|
|
385
|
-
z.object({
|
|
386
|
-
project: z.string().min(1),
|
|
387
|
-
worktreeName: z.string().trim().min(1).max(120),
|
|
388
|
-
name: terminalNameSchema,
|
|
389
|
-
argv: terminalArgvSchema.optional(),
|
|
390
|
-
base: z.enum(["default", "current"]).default("default"),
|
|
391
|
-
sourceWorktreeId: z.string().min(1).optional()
|
|
392
|
-
}).superRefine((value, context) => {
|
|
393
|
-
if (value.base === "current" && !value.sourceWorktreeId) context.addIssue({
|
|
394
|
-
code: "custom",
|
|
395
|
-
path: ["sourceWorktreeId"],
|
|
396
|
-
message: "A source tree is required when starting from current"
|
|
397
|
-
});
|
|
398
|
-
});
|
|
399
|
-
//#endregion
|
|
400
10
|
//#region src/duration.ts
|
|
401
11
|
const DURATION_UNITS = /* @__PURE__ */ new Map([
|
|
402
12
|
["ms", 1],
|
|
@@ -477,21 +87,6 @@ const daemonRecordSchema = z.strictObject({
|
|
|
477
87
|
"external"
|
|
478
88
|
])
|
|
479
89
|
});
|
|
480
|
-
const healthRecordSchema = z.strictObject({
|
|
481
|
-
ok: z.literal(true),
|
|
482
|
-
version: z.string(),
|
|
483
|
-
protocolVersion: z.number(),
|
|
484
|
-
hostname: z.string().optional(),
|
|
485
|
-
pid: z.number(),
|
|
486
|
-
instanceId: z.string().nullable(),
|
|
487
|
-
installationMethod: z.string(),
|
|
488
|
-
daemonLifecycle: z.enum([
|
|
489
|
-
"treeport",
|
|
490
|
-
"service",
|
|
491
|
-
"external"
|
|
492
|
-
]),
|
|
493
|
-
url: z.string()
|
|
494
|
-
});
|
|
495
90
|
async function preferences(env = process.env) {
|
|
496
91
|
return await readJson$1(localPaths(env).preferencesPath, preferencesSchema) ?? {};
|
|
497
92
|
}
|
|
@@ -542,15 +137,18 @@ async function daemonHealth(apiUrl, timeoutMs = 1500) {
|
|
|
542
137
|
const signal = AbortSignal.timeout(timeoutMs);
|
|
543
138
|
return fetch(`${apiUrl}/api/health`, { signal }).then(async (response) => {
|
|
544
139
|
if (!response.ok) return null;
|
|
545
|
-
|
|
546
|
-
return result.success ? result.data : null;
|
|
140
|
+
return decodeUnknownOrNull(healthResponseSchema, await response.json());
|
|
547
141
|
}).catch(() => null);
|
|
548
142
|
}
|
|
549
143
|
function matchesOwnership(state, observed) {
|
|
550
144
|
return observed.pid === state.pid && observed.instanceId === state.instanceId && path.resolve(state.dataDir) === localPaths().dataDir;
|
|
551
145
|
}
|
|
552
146
|
async function readState() {
|
|
553
|
-
|
|
147
|
+
const paths = localPaths();
|
|
148
|
+
return await fs.readFile(paths.lockPath, "utf8").then((value) => daemonRecordSchema.parse(JSON.parse(value))).catch((error) => {
|
|
149
|
+
if (error.code === "ENOENT") return null;
|
|
150
|
+
throw new Error(`Cannot verify daemon ownership at ${paths.lockPath}. Inspect the daemon log before starting or stopping Treeport.`, { cause: error });
|
|
151
|
+
}) ?? readJson$1(paths.statePath, daemonRecordSchema);
|
|
554
152
|
}
|
|
555
153
|
async function removeStaleState(state) {
|
|
556
154
|
const paths = localPaths();
|
|
@@ -764,11 +362,7 @@ async function disableTailscaleRemote() {
|
|
|
764
362
|
}
|
|
765
363
|
async function runDoctor() {
|
|
766
364
|
const paths = localPaths();
|
|
767
|
-
const
|
|
768
|
-
const tmuxPath = process.env.TREEPORT_TMUX_PATH?.trim() || "tmux";
|
|
769
|
-
const [git, tmux] = await Promise.all([executableCheck(gitPath, ["--version"]), executableCheck(tmuxPath, ["-V"])]);
|
|
770
|
-
const tmuxMatch = /tmux\s+(\d+)\.(\d+)/i.exec(tmux.detail);
|
|
771
|
-
const tmuxSupported = Boolean(tmux.ok && tmuxMatch && (Number(tmuxMatch[1]) > 3 || Number(tmuxMatch[1]) === 3 && Number(tmuxMatch[2]) >= 2));
|
|
365
|
+
const git = await executableCheck(process.env.TREEPORT_GIT_PATH?.trim() || "git", ["--version"]);
|
|
772
366
|
const checkDirectory = (directoryPath) => fs.mkdir(directoryPath, {
|
|
773
367
|
recursive: true,
|
|
774
368
|
mode: 448
|
|
@@ -790,11 +384,6 @@ async function runDoctor() {
|
|
|
790
384
|
name: "Git",
|
|
791
385
|
...git
|
|
792
386
|
},
|
|
793
|
-
{
|
|
794
|
-
name: "tmux",
|
|
795
|
-
ok: tmuxSupported,
|
|
796
|
-
detail: tmuxSupported ? tmux.detail : `${tmux.detail}. Treeport requires tmux 3.2 or newer.`
|
|
797
|
-
},
|
|
798
387
|
{
|
|
799
388
|
name: "Data directory",
|
|
800
389
|
...dataDirectory
|
|
@@ -1274,7 +863,6 @@ function createServiceEnvironment(input) {
|
|
|
1274
863
|
TREEPORT_CACHE_DIR: cacheDirectory(input.user.homedir, env),
|
|
1275
864
|
TREEPORT_DATABASE_PATH: env.TREEPORT_DATABASE_PATH?.trim() || path.join(input.paths.dataDir, "treeport.db"),
|
|
1276
865
|
TREEPORT_SHELL: env.TREEPORT_SHELL?.trim() || env.SHELL?.trim() || "/bin/sh",
|
|
1277
|
-
TREEPORT_TMUX_PATH: env.TREEPORT_TMUX_PATH?.trim() || "tmux",
|
|
1278
866
|
TREEPORT_GIT_PATH: env.TREEPORT_GIT_PATH?.trim() || "git",
|
|
1279
867
|
TREEPORT_GH_PATH: env.TREEPORT_GH_PATH?.trim() || "gh",
|
|
1280
868
|
TREEPORT_DAEMON_LIFECYCLE: "service",
|
|
@@ -2238,16 +1826,18 @@ async function createUpdateStartupReporter(config) {
|
|
|
2238
1826
|
const paths = updatePaths(config.dataDir);
|
|
2239
1827
|
const pending = await fs.readFile(paths.pending, "utf8").then((value) => pendingSchema.safeParse(JSON.parse(value))).then((result) => result.success ? result.data : null).catch(() => null);
|
|
2240
1828
|
const active = pending && pending.targetVersion === config.appVersion ? pending : null;
|
|
1829
|
+
const previous = await readUpdateStartupReport(config.dataDir);
|
|
1830
|
+
const previousState = active && previous?.operationId === active.operationId && previous.targetVersion === active.targetVersion ? previous.migrationState : "unknown";
|
|
2241
1831
|
const report = active ? {
|
|
2242
1832
|
schemaVersion: 1,
|
|
2243
1833
|
operationId: active.operationId,
|
|
2244
1834
|
targetVersion: active.targetVersion,
|
|
2245
1835
|
instanceId: config.instanceId ?? null,
|
|
2246
|
-
migrationState:
|
|
1836
|
+
migrationState: previousState,
|
|
2247
1837
|
ready: false,
|
|
2248
1838
|
error: null,
|
|
2249
1839
|
logPath: path.join(config.dataDir, "logs", "daemon.log"),
|
|
2250
|
-
snapshotPaths: [],
|
|
1840
|
+
snapshotPaths: previous?.operationId === active.operationId && previous.targetVersion === active.targetVersion ? previous.snapshotPaths : [],
|
|
2251
1841
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2252
1842
|
} : null;
|
|
2253
1843
|
const save = async () => {
|
|
@@ -2259,14 +1849,20 @@ async function createUpdateStartupReporter(config) {
|
|
|
2259
1849
|
return {
|
|
2260
1850
|
async databaseOpening() {
|
|
2261
1851
|
if (report) {
|
|
2262
|
-
report.migrationState = "unknown";
|
|
1852
|
+
report.migrationState = previousState === "advanced" ? "advanced" : "unknown";
|
|
1853
|
+
await save();
|
|
1854
|
+
}
|
|
1855
|
+
},
|
|
1856
|
+
async snapshotCreated(snapshotPath) {
|
|
1857
|
+
if (report) {
|
|
1858
|
+
report.snapshotPaths = [.../* @__PURE__ */ new Set([...report.snapshotPaths, snapshotPath])];
|
|
2263
1859
|
await save();
|
|
2264
1860
|
}
|
|
2265
1861
|
},
|
|
2266
1862
|
async databaseOpened(input) {
|
|
2267
1863
|
if (report) {
|
|
2268
|
-
report.migrationState = input.migrationState;
|
|
2269
|
-
report.snapshotPaths = input.snapshotPaths;
|
|
1864
|
+
report.migrationState = previousState === "advanced" || input.migrationState === "advanced" ? "advanced" : previousState === "unknown" ? "unknown" : "unchanged";
|
|
1865
|
+
report.snapshotPaths = [.../* @__PURE__ */ new Set([...report.snapshotPaths, ...input.snapshotPaths])];
|
|
2270
1866
|
await save();
|
|
2271
1867
|
}
|
|
2272
1868
|
},
|
|
@@ -2378,6 +1974,15 @@ const packedReleaseSchema = z.tuple([z.looseObject({
|
|
|
2378
1974
|
filename: z.string().min(1),
|
|
2379
1975
|
integrity: z.string().min(1)
|
|
2380
1976
|
})]);
|
|
1977
|
+
function formatLocalUpdateError(message, details = {}) {
|
|
1978
|
+
return [...new Set([
|
|
1979
|
+
message,
|
|
1980
|
+
details.cause,
|
|
1981
|
+
details.recovery,
|
|
1982
|
+
details.logPath ? `Daemon log: ${details.logPath}` : null,
|
|
1983
|
+
...(details.snapshotPaths ?? []).map((snapshot) => `Pre-migration snapshot: ${snapshot}`)
|
|
1984
|
+
].filter(Boolean))].join("\n");
|
|
1985
|
+
}
|
|
2381
1986
|
var LocalUpdateError = class extends Error {
|
|
2382
1987
|
code;
|
|
2383
1988
|
details;
|
|
@@ -2498,10 +2103,24 @@ async function replaceSymlink(linkPath, target) {
|
|
|
2498
2103
|
await fs.rename(temporaryPath, linkPath);
|
|
2499
2104
|
}
|
|
2500
2105
|
async function terminalIds(apiUrl) {
|
|
2501
|
-
const
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2106
|
+
const parsed = decodeUnknownOrNull(projectsResponseSchema, await fetch(`${apiUrl}/api/projects`).then(async (response) => response.ok ? response.json() : null).catch(() => null));
|
|
2107
|
+
if (!parsed) throw new Error("Treeport could not read the terminal inventory.");
|
|
2108
|
+
return parsed.projects.flatMap((project) => project.worktrees).flatMap((worktree) => worktree.terminals).map((terminal) => terminal.id).sort();
|
|
2109
|
+
}
|
|
2110
|
+
function updateMigrationState(operation, report) {
|
|
2111
|
+
if (operation.migrationState === "advanced") return "advanced";
|
|
2112
|
+
if (report?.operationId === operation.operationId && report.targetVersion === operation.toVersion) return report.migrationState;
|
|
2113
|
+
return ["stop", "activate"].includes(operation.phase) && operation.migrationState === "not_started" ? "not_started" : "unknown";
|
|
2114
|
+
}
|
|
2115
|
+
async function stopUpdateDaemon(lifecycle) {
|
|
2116
|
+
if (lifecycle === "service") {
|
|
2117
|
+
if ((await serviceStop()).administratorCommand) throw new LocalUpdateError("UPDATE_SERVICE_ADMINISTRATOR_ACTION_REQUIRED", "The service requires administrator action and was not stopped.", { phase: "stop" });
|
|
2118
|
+
const deadline = Date.now() + 7e3;
|
|
2119
|
+
while ((await daemonStatus()).state) {
|
|
2120
|
+
if (Date.now() >= deadline) throw new Error("Treeport could not verify that the service daemon stopped. Inspect the daemon log before changing the installed version.");
|
|
2121
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
2122
|
+
}
|
|
2123
|
+
} else await daemonDown();
|
|
2505
2124
|
}
|
|
2506
2125
|
async function startThroughStableEntrypoint(entrypoint, environment) {
|
|
2507
2126
|
const result = await runCommand(entrypoint, ["start", "--json"], environment);
|
|
@@ -2673,6 +2292,7 @@ async function runLocalUpdate(options = {}) {
|
|
|
2673
2292
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2674
2293
|
};
|
|
2675
2294
|
let recoveryOperation = null;
|
|
2295
|
+
let recoveryReport = null;
|
|
2676
2296
|
const save = async (phase) => {
|
|
2677
2297
|
operation = {
|
|
2678
2298
|
...operation,
|
|
@@ -2692,12 +2312,17 @@ async function runLocalUpdate(options = {}) {
|
|
|
2692
2312
|
activeTarget: installation.managed ? await fs.realpath(installation.currentLink).catch(() => installation.prefix) : installation.prefix
|
|
2693
2313
|
};
|
|
2694
2314
|
if (staleOperation && staleOperation.daemonWasRunning && DESTRUCTIVE_PHASES.has(staleOperation.phase) && !(await daemonStatus()).running) {
|
|
2315
|
+
await stopUpdateDaemon(staleOperation.daemonLifecycle);
|
|
2695
2316
|
const staleReport = await readUpdateStartupReport(paths.dataDir);
|
|
2696
|
-
|
|
2317
|
+
staleOperation.migrationState = updateMigrationState(staleOperation, staleReport);
|
|
2318
|
+
if (["advanced", "unknown"].includes(staleOperation.migrationState)) {
|
|
2319
|
+
recoveryReport = staleReport?.operationId === staleOperation.operationId && staleReport.targetVersion === staleOperation.toVersion ? staleReport : null;
|
|
2697
2320
|
if (staleOperation.previousTarget && operation.activeTarget === staleOperation.previousTarget) throw new LocalUpdateError("UPDATE_RECOVERY_REQUIRED", "The older Treeport version is active after a database migration may have started. Treeport will not start it.", {
|
|
2698
2321
|
phase: "recovery_required",
|
|
2699
2322
|
operationId: staleOperation.operationId,
|
|
2700
|
-
migrationState:
|
|
2323
|
+
migrationState: staleOperation.migrationState,
|
|
2324
|
+
logPath: recoveryReport?.logPath ?? paths.logPath,
|
|
2325
|
+
snapshotPaths: recoveryReport?.snapshotPaths ?? [],
|
|
2701
2326
|
recovery: "Install the same or a newer Treeport release and inspect the daemon log."
|
|
2702
2327
|
});
|
|
2703
2328
|
recoveryOperation = staleOperation;
|
|
@@ -2779,6 +2404,8 @@ async function runLocalUpdate(options = {}) {
|
|
|
2779
2404
|
phase: "recovery_required",
|
|
2780
2405
|
operationId: recoveryOperation.operationId,
|
|
2781
2406
|
migrationState: recoveryOperation.migrationState,
|
|
2407
|
+
logPath: recoveryReport?.logPath ?? paths.logPath,
|
|
2408
|
+
snapshotPaths: recoveryReport?.snapshotPaths ?? [],
|
|
2782
2409
|
recovery: "Install the next Treeport release when it is available and run `treeport update` again."
|
|
2783
2410
|
});
|
|
2784
2411
|
if (comparison === 0) {
|
|
@@ -2920,6 +2547,7 @@ async function runLocalUpdate(options = {}) {
|
|
|
2920
2547
|
phase: "verify",
|
|
2921
2548
|
operationId
|
|
2922
2549
|
});
|
|
2550
|
+
operation.migrationState = recoveryOperation?.migrationState ?? "not_started";
|
|
2923
2551
|
operation.daemonWasRunning = daemonBefore.running && daemonBefore.verified || recoveryOperation !== null;
|
|
2924
2552
|
operation.daemonLifecycle = recoveryOperation ? recoveryOperation.daemonLifecycle : operation.daemonWasRunning ? daemonBefore.health?.daemonLifecycle === "service" ? "service" : "treeport" : installedService ? "service" : "treeport";
|
|
2925
2553
|
operation.serviceMode = recoveryOperation?.serviceMode ?? serviceBefore?.mode ?? null;
|
|
@@ -2930,12 +2558,7 @@ async function runLocalUpdate(options = {}) {
|
|
|
2930
2558
|
});
|
|
2931
2559
|
await save("stop");
|
|
2932
2560
|
progress("Stopping the Treeport daemon and preserving terminals…");
|
|
2933
|
-
if (operation.daemonWasRunning)
|
|
2934
|
-
if ((await serviceStop()).administratorCommand) throw new LocalUpdateError("UPDATE_SERVICE_ADMINISTRATOR_ACTION_REQUIRED", "The service requires administrator action and was not stopped.", {
|
|
2935
|
-
phase: "stop",
|
|
2936
|
-
operationId
|
|
2937
|
-
});
|
|
2938
|
-
} else await daemonDown();
|
|
2561
|
+
if (operation.daemonWasRunning) await stopUpdateDaemon(operation.daemonLifecycle);
|
|
2939
2562
|
await save("activate");
|
|
2940
2563
|
progress(`Activating Treeport ${release.version}…`);
|
|
2941
2564
|
await fs.rm(targetPath, {
|
|
@@ -2966,7 +2589,19 @@ async function runLocalUpdate(options = {}) {
|
|
|
2966
2589
|
targetVersion: release.version,
|
|
2967
2590
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2968
2591
|
});
|
|
2969
|
-
await
|
|
2592
|
+
await writeJson(path.join(updateDirectory, "startup-report.json"), {
|
|
2593
|
+
schemaVersion: 1,
|
|
2594
|
+
operationId,
|
|
2595
|
+
targetVersion: release.version,
|
|
2596
|
+
instanceId: null,
|
|
2597
|
+
migrationState: operation.migrationState,
|
|
2598
|
+
ready: false,
|
|
2599
|
+
error: null,
|
|
2600
|
+
logPath: paths.logPath,
|
|
2601
|
+
snapshotPaths: recoveryReport?.snapshotPaths ?? [],
|
|
2602
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2603
|
+
});
|
|
2604
|
+
operation.migrationState = operation.migrationState === "advanced" ? "advanced" : "unknown";
|
|
2970
2605
|
await save("restart");
|
|
2971
2606
|
progress(`Restarting the ${operation.daemonLifecycle === "service" ? "Treeport service" : "Treeport daemon"}…`);
|
|
2972
2607
|
await startThroughStableEntrypoint(installation.entrypoint, environment);
|
|
@@ -2979,7 +2614,7 @@ async function runLocalUpdate(options = {}) {
|
|
|
2979
2614
|
daemonAfter = await daemonStatus();
|
|
2980
2615
|
report = await readUpdateStartupReport(paths.dataDir);
|
|
2981
2616
|
}
|
|
2982
|
-
operation.migrationState =
|
|
2617
|
+
operation.migrationState = updateMigrationState(operation, report);
|
|
2983
2618
|
if (!daemonAfter.running || !daemonAfter.verified || daemonAfter.health?.version !== release.version || daemonAfter.health.daemonLifecycle !== operation.daemonLifecycle || path.resolve(daemonAfter.state.dataDir) !== paths.dataDir || report?.operationId !== operationId || !report.ready) throw new LocalUpdateError("UPDATE_HEALTH_VERIFICATION_FAILED", `Treeport ${release.version} did not pass startup verification.`, {
|
|
2984
2619
|
phase: "health_check",
|
|
2985
2620
|
operationId
|
|
@@ -3042,13 +2677,14 @@ async function runLocalUpdate(options = {}) {
|
|
|
3042
2677
|
toVersion: operation.toVersion
|
|
3043
2678
|
});
|
|
3044
2679
|
}
|
|
3045
|
-
const
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
2680
|
+
const stopError = operation.daemonWasRunning ? await stopUpdateDaemon(operation.daemonLifecycle).then(() => null, (cause) => cause instanceof Error ? cause.message : String(cause)) : null;
|
|
2681
|
+
const observedReport = await readUpdateStartupReport(paths.dataDir);
|
|
2682
|
+
const startupReport = observedReport?.operationId === operationId && observedReport.targetVersion === operation.toVersion ? observedReport : null;
|
|
2683
|
+
operation.migrationState = updateMigrationState(operation, startupReport);
|
|
2684
|
+
if (!(!stopError && ["not_started", "unchanged"].includes(operation.migrationState))) {
|
|
2685
|
+
operation.recoveryAction = stopError ? `Keep the new version installed. Stop the daemon, then inspect the daemon log. Stop failed: ${stopError}` : "Keep the new version installed. Inspect the daemon log and repair with the same or a newer Treeport release.";
|
|
3050
2686
|
await save("recovery_required");
|
|
3051
|
-
throw new LocalUpdateError("UPDATE_RECOVERY_REQUIRED", "
|
|
2687
|
+
throw new LocalUpdateError("UPDATE_RECOVERY_REQUIRED", "Treeport could not prove that rollback is safe. Treeport did not start the older daemon.", {
|
|
3052
2688
|
operationId,
|
|
3053
2689
|
phase: failedPhase,
|
|
3054
2690
|
fromVersion: operation.fromVersion,
|
|
@@ -3059,6 +2695,7 @@ async function runLocalUpdate(options = {}) {
|
|
|
3059
2695
|
safe: false,
|
|
3060
2696
|
succeeded: false
|
|
3061
2697
|
},
|
|
2698
|
+
cause: startupReport?.error ?? (error instanceof Error ? error.message : String(error)),
|
|
3062
2699
|
logPath: startupReport?.logPath ?? paths.logPath,
|
|
3063
2700
|
snapshotPaths: startupReport?.snapshotPaths ?? [],
|
|
3064
2701
|
recovery: operation.recoveryAction
|
|
@@ -3087,6 +2724,8 @@ async function runLocalUpdate(options = {}) {
|
|
|
3087
2724
|
succeeded: rollbackError === null
|
|
3088
2725
|
},
|
|
3089
2726
|
cause: error instanceof Error ? error.message : String(error),
|
|
2727
|
+
logPath: startupReport?.logPath ?? paths.logPath,
|
|
2728
|
+
snapshotPaths: startupReport?.snapshotPaths ?? [],
|
|
3090
2729
|
recovery: operation.recoveryAction
|
|
3091
2730
|
});
|
|
3092
2731
|
} finally {
|
|
@@ -3104,4 +2743,4 @@ async function runLocalUpdate(options = {}) {
|
|
|
3104
2743
|
}
|
|
3105
2744
|
}
|
|
3106
2745
|
//#endregion
|
|
3107
|
-
export {
|
|
2746
|
+
export { tailscaleRemoteStatus as A, daemonUp as C, resolveLocalApiUrl as D, readDaemonLogs as E, assertLoopbackHost as M, parseDurationMs as N, resolvePackagePath as O, daemonStatus as S, enableTailscaleRemote as T, serviceStart as _, isCanonicalTreeportVersion as a, daemonDown as b, runLocalUpdate as c, serviceApply as d, serviceDisable as f, serviceRun as g, serviceInstalled as h, inspectLocalUpdateInstallation as i, treeportVersion as j, runDoctor as k, createUpdateStartupReporter as l, serviceEnable as m, compareTreeportVersions as n, readLocalUpdateProgress as o, serviceDoctorCheck as p, formatLocalUpdateError as r, resolveLatestTreeportRelease as s, LocalUpdateError as t, readServiceLogs as u, serviceStatus as v, disableTailscaleRemote as w, daemonHealth as x, serviceStop as y };
|