@spencer-kit/coder-studio 0.4.3 → 0.4.4
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/CHANGELOG.md +8 -0
- package/dist/esm/bin.mjs +428 -89
- package/dist/esm/bin.mjs.map +4 -4
- package/dist/esm/server-runner.mjs +406 -67
- package/dist/esm/server-runner.mjs.map +4 -4
- package/dist/esm/update-worker.mjs +118 -3
- package/dist/esm/update-worker.mjs.map +2 -2
- package/dist/web/assets/components-C4SKshs2.js +110 -0
- package/dist/web/assets/components-C4SKshs2.js.map +1 -0
- package/dist/web/assets/{components-AKM1pxhf.css → components-CMahvybm.css} +1 -1
- package/dist/web/assets/{main-CEv6hML1.js → main-CZuF2VZA.js} +2 -2
- package/dist/web/assets/{main-CEv6hML1.js.map → main-CZuF2VZA.js.map} +1 -1
- package/dist/web/assets/{ui-preview-Cd3euuid.js → ui-preview-DCeC0YmD.js} +3 -3
- package/dist/web/assets/{ui-preview-Cd3euuid.js.map → ui-preview-DCeC0YmD.js.map} +1 -1
- package/dist/web/index.html +3 -3
- package/dist/web/ui-preview.html +3 -3
- package/package.json +1 -1
- package/src/update-worker.test.ts +53 -12
- package/src/update-worker.ts +158 -3
- package/dist/web/assets/components-BZf_jLGv.js +0 -110
- package/dist/web/assets/components-BZf_jLGv.js.map +0 -1
package/dist/web/index.html
CHANGED
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
<meta name="description" content="Coder Studio - Agent-First Development Environment" />
|
|
7
7
|
<title>Coder Studio</title>
|
|
8
8
|
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
|
9
|
-
<script type="module" crossorigin src="/assets/main-
|
|
9
|
+
<script type="module" crossorigin src="/assets/main-CZuF2VZA.js"></script>
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-CpWojdLp.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/monaco-editor-VTbRDH-J.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/xterm-BVlcrOZ1.js">
|
|
13
|
-
<link rel="modulepreload" crossorigin href="/assets/components-
|
|
13
|
+
<link rel="modulepreload" crossorigin href="/assets/components-C4SKshs2.js">
|
|
14
14
|
<link rel="stylesheet" crossorigin href="/assets/monaco-editor-Br_kD0ds.css">
|
|
15
15
|
<link rel="stylesheet" crossorigin href="/assets/xterm-BrP-ENHg.css">
|
|
16
|
-
<link rel="stylesheet" crossorigin href="/assets/components-
|
|
16
|
+
<link rel="stylesheet" crossorigin href="/assets/components-CMahvybm.css">
|
|
17
17
|
</head>
|
|
18
18
|
<body>
|
|
19
19
|
<div id="root"></div>
|
package/dist/web/ui-preview.html
CHANGED
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Coder Studio UI Preview</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/ui-preview-
|
|
7
|
+
<script type="module" crossorigin src="/assets/ui-preview-DCeC0YmD.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-CpWojdLp.js">
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/monaco-editor-VTbRDH-J.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/xterm-BVlcrOZ1.js">
|
|
11
|
-
<link rel="modulepreload" crossorigin href="/assets/components-
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/components-C4SKshs2.js">
|
|
12
12
|
<link rel="stylesheet" crossorigin href="/assets/monaco-editor-Br_kD0ds.css">
|
|
13
13
|
<link rel="stylesheet" crossorigin href="/assets/xterm-BrP-ENHg.css">
|
|
14
|
-
<link rel="stylesheet" crossorigin href="/assets/components-
|
|
14
|
+
<link rel="stylesheet" crossorigin href="/assets/components-CMahvybm.css">
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
17
17
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@ import { mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
-
import { runUpdateWorker } from "./update-worker.js";
|
|
5
|
+
import { runRestartHandoff, runUpdateWorker } from "./update-worker.js";
|
|
6
6
|
|
|
7
7
|
describe("update-worker", () => {
|
|
8
8
|
const tempDirs: string[] = [];
|
|
@@ -29,13 +29,16 @@ describe("update-worker", () => {
|
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
it("writes restarting state
|
|
32
|
+
it("writes restarting state and spawns a detached restart handoff after install success", async () => {
|
|
33
33
|
const env = createEnv();
|
|
34
34
|
const runCommand = vi.fn(async () => {});
|
|
35
|
+
const spawnDetachedProcess = vi.fn(async () => {});
|
|
35
36
|
|
|
36
37
|
await runUpdateWorker(env, {
|
|
37
38
|
runCommand,
|
|
38
39
|
now: () => 1000,
|
|
40
|
+
processId: 4242,
|
|
41
|
+
spawnDetachedProcess,
|
|
39
42
|
});
|
|
40
43
|
|
|
41
44
|
const state = JSON.parse(readFileSync(env.stateFilePath, "utf-8")) as { updateStatus: string };
|
|
@@ -46,11 +49,13 @@ describe("update-worker", () => {
|
|
|
46
49
|
["install", "-g", "@spencer-kit/coder-studio@0.5.0"],
|
|
47
50
|
expect.any(Object)
|
|
48
51
|
);
|
|
49
|
-
expect(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
expect(spawnDetachedProcess).toHaveBeenCalledWith(
|
|
53
|
+
process.execPath,
|
|
54
|
+
expect.any(Array),
|
|
55
|
+
expect.objectContaining({
|
|
56
|
+
CODER_STUDIO_UPDATE_WORKER_MODE: "restart-handoff",
|
|
57
|
+
CODER_STUDIO_UPDATE_PARENT_PID: "4242",
|
|
58
|
+
})
|
|
54
59
|
);
|
|
55
60
|
});
|
|
56
61
|
|
|
@@ -77,14 +82,14 @@ describe("update-worker", () => {
|
|
|
77
82
|
|
|
78
83
|
it("marks restart failures with manual restart guidance", async () => {
|
|
79
84
|
const env = createEnv();
|
|
80
|
-
const runCommand = vi
|
|
81
|
-
|
|
82
|
-
.mockResolvedValueOnce(undefined)
|
|
83
|
-
.mockRejectedValueOnce(new Error("pm2 restart failed"));
|
|
85
|
+
const runCommand = vi.fn().mockRejectedValueOnce(new Error("pm2 restart failed"));
|
|
86
|
+
const waitForProcessExit = vi.fn(async () => {});
|
|
84
87
|
|
|
85
|
-
await
|
|
88
|
+
await runRestartHandoff(env, {
|
|
86
89
|
runCommand,
|
|
87
90
|
now: () => 1000,
|
|
91
|
+
waitForProcessExit,
|
|
92
|
+
restartParentPid: 999,
|
|
88
93
|
});
|
|
89
94
|
|
|
90
95
|
const state = JSON.parse(readFileSync(env.stateFilePath, "utf-8")) as {
|
|
@@ -95,11 +100,13 @@ describe("update-worker", () => {
|
|
|
95
100
|
expect(state.updateStatus).toBe("failed");
|
|
96
101
|
expect(state.manualCommand).toBe("coder-studio serve --restart");
|
|
97
102
|
expect(state.errorSummary).toContain("restart failed");
|
|
103
|
+
expect(waitForProcessExit).toHaveBeenCalledWith(999);
|
|
98
104
|
});
|
|
99
105
|
|
|
100
106
|
it("sanitizes pm2 and runtime override env before invoking install and restart commands", async () => {
|
|
101
107
|
const env = createEnv();
|
|
102
108
|
const runCommand = vi.fn(async () => {});
|
|
109
|
+
const spawnDetachedProcess = vi.fn(async () => {});
|
|
103
110
|
const originalEnv = {
|
|
104
111
|
PM2_HOME: process.env.PM2_HOME,
|
|
105
112
|
PM2_PROGRAMMATIC: process.env.PM2_PROGRAMMATIC,
|
|
@@ -130,6 +137,8 @@ describe("update-worker", () => {
|
|
|
130
137
|
await runUpdateWorker(env, {
|
|
131
138
|
runCommand,
|
|
132
139
|
now: () => 1000,
|
|
140
|
+
processId: 4242,
|
|
141
|
+
spawnDetachedProcess,
|
|
133
142
|
});
|
|
134
143
|
} finally {
|
|
135
144
|
for (const [key, value] of Object.entries(originalEnv)) {
|
|
@@ -155,5 +164,37 @@ describe("update-worker", () => {
|
|
|
155
164
|
expect(options.env?.CODER_STUDIO_UPDATE_STATE_PATH).toBeUndefined();
|
|
156
165
|
expect(options.env?.pm_id).toBeUndefined();
|
|
157
166
|
}
|
|
167
|
+
|
|
168
|
+
const handoffEnv = spawnDetachedProcess.mock.calls[0]?.[2] as NodeJS.ProcessEnv | undefined;
|
|
169
|
+
expect(handoffEnv?.PM2_HOME).toBe("/tmp/custom-pm2-home");
|
|
170
|
+
expect(handoffEnv?.PM2_PROGRAMMATIC).toBeUndefined();
|
|
171
|
+
expect(handoffEnv?.PM2_JSON_PROCESSING).toBeUndefined();
|
|
172
|
+
expect(handoffEnv?.PM2_INTERACTOR_PROCESSING).toBeUndefined();
|
|
173
|
+
expect(handoffEnv?.NODE_APP_INSTANCE).toBeUndefined();
|
|
174
|
+
expect(handoffEnv?.NODE_CHANNEL_FD).toBeUndefined();
|
|
175
|
+
expect(handoffEnv?.NODE_CHANNEL_SERIALIZATION_MODE).toBeUndefined();
|
|
176
|
+
expect(handoffEnv?.CODER_STUDIO_RUNTIME_JSON_PATH).toBeUndefined();
|
|
177
|
+
expect(handoffEnv?.CODER_STUDIO_SESSION_ID).toBeUndefined();
|
|
178
|
+
expect(handoffEnv?.pm_id).toBeUndefined();
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("waits for the install worker to exit before running the restart command", async () => {
|
|
182
|
+
const env = createEnv();
|
|
183
|
+
const waitForProcessExit = vi.fn(async () => {});
|
|
184
|
+
const runCommand = vi.fn(async () => {});
|
|
185
|
+
|
|
186
|
+
await runRestartHandoff(env, {
|
|
187
|
+
runCommand,
|
|
188
|
+
now: () => 1000,
|
|
189
|
+
waitForProcessExit,
|
|
190
|
+
restartParentPid: 777,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
expect(waitForProcessExit).toHaveBeenCalledWith(777);
|
|
194
|
+
expect(runCommand).toHaveBeenCalledWith(
|
|
195
|
+
"coder-studio",
|
|
196
|
+
["serve", "--restart"],
|
|
197
|
+
expect.any(Object)
|
|
198
|
+
);
|
|
158
199
|
});
|
|
159
200
|
});
|
package/src/update-worker.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import { createWriteStream } from "node:fs";
|
|
3
3
|
import { mkdir } from "node:fs/promises";
|
|
4
4
|
import { dirname } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
5
6
|
|
|
6
7
|
interface UpdateStateSnapshot {
|
|
7
8
|
version: 1;
|
|
@@ -37,6 +38,13 @@ interface WorkerEnv {
|
|
|
37
38
|
installArgsPrefix: string[];
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
type WorkerMode = "install" | "restart-handoff";
|
|
42
|
+
|
|
43
|
+
const RESTART_HANDOFF_MODE: WorkerMode = "restart-handoff";
|
|
44
|
+
const DEFAULT_MODE: WorkerMode = "install";
|
|
45
|
+
const RESTART_HANDOFF_WAIT_MS = 5_000;
|
|
46
|
+
const WORKER_ENTRY_PATH = fileURLToPath(import.meta.url);
|
|
47
|
+
|
|
40
48
|
async function writeState(filePath: string, value: UpdateStateSnapshot): Promise<void> {
|
|
41
49
|
await mkdir(dirname(filePath), { recursive: true });
|
|
42
50
|
await import("node:fs/promises").then(({ writeFile }) =>
|
|
@@ -44,6 +52,16 @@ async function writeState(filePath: string, value: UpdateStateSnapshot): Promise
|
|
|
44
52
|
);
|
|
45
53
|
}
|
|
46
54
|
|
|
55
|
+
function closeLogStream(stream: NodeJS.WritableStream): Promise<void> {
|
|
56
|
+
return new Promise((resolve, reject) => {
|
|
57
|
+
stream.once("error", reject);
|
|
58
|
+
stream.end(() => {
|
|
59
|
+
stream.off("error", reject);
|
|
60
|
+
resolve();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
47
65
|
function parseJsonArray(value: string | undefined, fallback: string[]): string[] {
|
|
48
66
|
if (!value) {
|
|
49
67
|
return fallback;
|
|
@@ -97,6 +115,22 @@ function buildManualCommand(input: WorkerEnv): string {
|
|
|
97
115
|
].join("\n");
|
|
98
116
|
}
|
|
99
117
|
|
|
118
|
+
function readWorkerMode(env = process.env): WorkerMode {
|
|
119
|
+
return env.CODER_STUDIO_UPDATE_WORKER_MODE === RESTART_HANDOFF_MODE
|
|
120
|
+
? RESTART_HANDOFF_MODE
|
|
121
|
+
: DEFAULT_MODE;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function readRestartParentPid(env = process.env): number | null {
|
|
125
|
+
const raw = env.CODER_STUDIO_UPDATE_PARENT_PID;
|
|
126
|
+
if (!raw) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const pid = Number.parseInt(raw, 10);
|
|
131
|
+
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
132
|
+
}
|
|
133
|
+
|
|
100
134
|
const INTERNAL_ENV_KEYS = new Set([
|
|
101
135
|
"CODER_STUDIO_RUNTIME_JSON_PATH",
|
|
102
136
|
"CODER_STUDIO_SESSION_ID",
|
|
@@ -125,6 +159,71 @@ function buildChildProcessEnv(env = process.env): NodeJS.ProcessEnv {
|
|
|
125
159
|
return nextEnv;
|
|
126
160
|
}
|
|
127
161
|
|
|
162
|
+
function buildWorkerEnv(input: WorkerEnv): NodeJS.ProcessEnv {
|
|
163
|
+
return {
|
|
164
|
+
CODER_STUDIO_UPDATE_STATE_PATH: input.stateFilePath,
|
|
165
|
+
CODER_STUDIO_UPDATE_LOG_PATH: input.logFilePath,
|
|
166
|
+
CODER_STUDIO_UPDATE_PACKAGE_NAME: input.packageName,
|
|
167
|
+
CODER_STUDIO_UPDATE_TARGET_VERSION: input.targetVersion,
|
|
168
|
+
CODER_STUDIO_UPDATE_CLI_COMMAND: input.cliCommand,
|
|
169
|
+
CODER_STUDIO_UPDATE_CURRENT_VERSION: input.currentVersion,
|
|
170
|
+
CODER_STUDIO_UPDATE_NPM_COMMAND: input.npmCommand,
|
|
171
|
+
CODER_STUDIO_UPDATE_RESTART_ARGS: JSON.stringify(input.restartArgs),
|
|
172
|
+
CODER_STUDIO_UPDATE_INSTALL_ARGS_PREFIX: JSON.stringify(input.installArgsPrefix),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function spawnDetachedProcess(
|
|
177
|
+
command: string,
|
|
178
|
+
args: string[],
|
|
179
|
+
env: NodeJS.ProcessEnv
|
|
180
|
+
): Promise<void> {
|
|
181
|
+
return new Promise((resolve, reject) => {
|
|
182
|
+
const child = spawn(command, args, {
|
|
183
|
+
detached: true,
|
|
184
|
+
stdio: "ignore",
|
|
185
|
+
env,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
child.on("error", reject);
|
|
189
|
+
child.unref();
|
|
190
|
+
resolve();
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const isMissingProcessError = (error: unknown): boolean =>
|
|
195
|
+
Boolean(
|
|
196
|
+
error &&
|
|
197
|
+
typeof error === "object" &&
|
|
198
|
+
"code" in error &&
|
|
199
|
+
(error as NodeJS.ErrnoException).code === "ESRCH"
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
async function waitForProcessExit(pid: number, waitMs = RESTART_HANDOFF_WAIT_MS): Promise<void> {
|
|
203
|
+
const deadline = Date.now() + waitMs;
|
|
204
|
+
|
|
205
|
+
while (Date.now() <= deadline) {
|
|
206
|
+
try {
|
|
207
|
+
process.kill(pid, 0);
|
|
208
|
+
} catch (error) {
|
|
209
|
+
if (isMissingProcessError(error)) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const remainingMs = deadline - Date.now();
|
|
217
|
+
if (remainingMs <= 0) {
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
await new Promise((resolve) => {
|
|
222
|
+
setTimeout(resolve, Math.min(100, remainingMs));
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
128
227
|
function runCommand(
|
|
129
228
|
command: string,
|
|
130
229
|
args: string[],
|
|
@@ -163,6 +262,8 @@ export async function runUpdateWorker(
|
|
|
163
262
|
deps?: {
|
|
164
263
|
runCommand?: typeof runCommand;
|
|
165
264
|
now?: () => number;
|
|
265
|
+
processId?: number;
|
|
266
|
+
spawnDetachedProcess?: typeof spawnDetachedProcess;
|
|
166
267
|
}
|
|
167
268
|
): Promise<void> {
|
|
168
269
|
const now = deps?.now ?? Date.now;
|
|
@@ -170,6 +271,8 @@ export async function runUpdateWorker(
|
|
|
170
271
|
const logStream = createWriteStream(input.logFilePath, { flags: "a" });
|
|
171
272
|
const execute = deps?.runCommand ?? runCommand;
|
|
172
273
|
const childEnv = buildChildProcessEnv(process.env);
|
|
274
|
+
const processId = deps?.processId ?? process.pid;
|
|
275
|
+
const spawnRestartHandoff = deps?.spawnDetachedProcess ?? spawnDetachedProcess;
|
|
173
276
|
|
|
174
277
|
try {
|
|
175
278
|
await execute(
|
|
@@ -196,7 +299,7 @@ export async function runUpdateWorker(
|
|
|
196
299
|
manualCommand: permissionRelated ? buildManualCommand(input) : null,
|
|
197
300
|
errorSummary: message,
|
|
198
301
|
});
|
|
199
|
-
logStream
|
|
302
|
+
await closeLogStream(logStream);
|
|
200
303
|
return;
|
|
201
304
|
}
|
|
202
305
|
|
|
@@ -216,6 +319,55 @@ export async function runUpdateWorker(
|
|
|
216
319
|
});
|
|
217
320
|
|
|
218
321
|
try {
|
|
322
|
+
await spawnRestartHandoff(process.execPath, [WORKER_ENTRY_PATH], {
|
|
323
|
+
...childEnv,
|
|
324
|
+
...buildWorkerEnv(input),
|
|
325
|
+
CODER_STUDIO_UPDATE_WORKER_MODE: RESTART_HANDOFF_MODE,
|
|
326
|
+
CODER_STUDIO_UPDATE_PARENT_PID: String(processId),
|
|
327
|
+
});
|
|
328
|
+
} catch (error) {
|
|
329
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
330
|
+
await writeState(input.stateFilePath, {
|
|
331
|
+
version: 1,
|
|
332
|
+
currentVersion: input.currentVersion,
|
|
333
|
+
latestVersion: input.targetVersion,
|
|
334
|
+
availability: "update_available",
|
|
335
|
+
updateStatus: "failed",
|
|
336
|
+
lastCheckedAt: now(),
|
|
337
|
+
targetVersion: input.targetVersion,
|
|
338
|
+
startedAt: now(),
|
|
339
|
+
finishedAt: now(),
|
|
340
|
+
requiresManualStep: true,
|
|
341
|
+
manualCommand: `${input.cliCommand} ${input.restartArgs.join(" ")}`,
|
|
342
|
+
errorSummary: `new version installed but service restart failed: ${message}`,
|
|
343
|
+
});
|
|
344
|
+
} finally {
|
|
345
|
+
await closeLogStream(logStream);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export async function runRestartHandoff(
|
|
350
|
+
input = readEnv(),
|
|
351
|
+
deps?: {
|
|
352
|
+
runCommand?: typeof runCommand;
|
|
353
|
+
now?: () => number;
|
|
354
|
+
waitForProcessExit?: typeof waitForProcessExit;
|
|
355
|
+
restartParentPid?: number | null;
|
|
356
|
+
}
|
|
357
|
+
): Promise<void> {
|
|
358
|
+
const now = deps?.now ?? Date.now;
|
|
359
|
+
await mkdir(dirname(input.logFilePath), { recursive: true });
|
|
360
|
+
const logStream = createWriteStream(input.logFilePath, { flags: "a" });
|
|
361
|
+
const execute = deps?.runCommand ?? runCommand;
|
|
362
|
+
const waitForParentExit = deps?.waitForProcessExit ?? waitForProcessExit;
|
|
363
|
+
const childEnv = buildChildProcessEnv(process.env);
|
|
364
|
+
const restartParentPid = deps?.restartParentPid ?? readRestartParentPid(process.env);
|
|
365
|
+
|
|
366
|
+
try {
|
|
367
|
+
if (restartParentPid !== null) {
|
|
368
|
+
await waitForParentExit(restartParentPid);
|
|
369
|
+
}
|
|
370
|
+
|
|
219
371
|
await execute(input.cliCommand, input.restartArgs, { logStream, env: childEnv });
|
|
220
372
|
} catch (error) {
|
|
221
373
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -234,12 +386,15 @@ export async function runUpdateWorker(
|
|
|
234
386
|
errorSummary: `new version installed but service restart failed: ${message}`,
|
|
235
387
|
});
|
|
236
388
|
} finally {
|
|
237
|
-
logStream
|
|
389
|
+
await closeLogStream(logStream);
|
|
238
390
|
}
|
|
239
391
|
}
|
|
240
392
|
|
|
241
393
|
if (process.env.CODER_STUDIO_UPDATE_STATE_PATH) {
|
|
242
|
-
|
|
394
|
+
const run =
|
|
395
|
+
readWorkerMode(process.env) === RESTART_HANDOFF_MODE ? runRestartHandoff : runUpdateWorker;
|
|
396
|
+
|
|
397
|
+
void run().catch((error) => {
|
|
243
398
|
console.error("[update-worker]", error);
|
|
244
399
|
process.exitCode = 1;
|
|
245
400
|
});
|