totemlayer 0.1.17 → 0.1.18
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/agent-runner.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { constants } from "node:fs";
|
|
4
|
-
import { lstat, mkdir, open, opendir, rename, unlink } from "node:fs/promises";
|
|
4
|
+
import { lstat, mkdir, open, opendir, rename, unlink, readFile } from "node:fs/promises";
|
|
5
5
|
import { dirname, isAbsolute, join, parse, resolve, sep } from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { parseArgs } from "node:util";
|
|
@@ -11,6 +11,9 @@ import { localClientEnvironment } from "./platform-client.mjs";
|
|
|
11
11
|
import { Totem } from "./sdk/index.js";
|
|
12
12
|
import { RUNTIME_ERROR_CODES, runtimeErrorCode } from "./runtime-diagnostics.mjs";
|
|
13
13
|
|
|
14
|
+
const PACKAGE_VERSION = JSON.parse(
|
|
15
|
+
await readFile(new URL("../package.json", import.meta.url), "utf8"),
|
|
16
|
+
).version;
|
|
14
17
|
const CLI_PATH = fileURLToPath(new URL("./totem.mjs", import.meta.url));
|
|
15
18
|
const UUID = /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/i;
|
|
16
19
|
const PHASES = new Set([
|
|
@@ -230,6 +233,7 @@ export async function readAgentRuntimeStatus(profile, { isAlive = alive, now = D
|
|
|
230
233
|
? record.phase
|
|
231
234
|
: "interrupted",
|
|
232
235
|
runId: record.runId,
|
|
236
|
+
...(typeof record.packageVersion === "string" ? { packageVersion: record.packageVersion } : {}),
|
|
233
237
|
updatedAt: record.updatedAt,
|
|
234
238
|
...(RUNTIME_ERROR_CODES.has(record.blockerCode) ? { blockerCode: record.blockerCode } : {}),
|
|
235
239
|
...(UUID.test(record.missionId ?? "") ? { missionId: record.missionId } : {}),
|
|
@@ -287,6 +291,7 @@ export async function runAgentSession(profile, options = {}) {
|
|
|
287
291
|
if (options.signal?.aborted) controller.abort();
|
|
288
292
|
let status = {
|
|
289
293
|
version: 1,
|
|
294
|
+
packageVersion: PACKAGE_VERSION,
|
|
290
295
|
runId,
|
|
291
296
|
pid: process.pid,
|
|
292
297
|
agentId: profile.agentId,
|
|
@@ -442,7 +447,7 @@ function runnerOptions(args) {
|
|
|
442
447
|
return parsed.values;
|
|
443
448
|
}
|
|
444
449
|
|
|
445
|
-
async function requestStop(profile, runId) {
|
|
450
|
+
export async function requestStop(profile, runId) {
|
|
446
451
|
if (!UUID.test(runId)) throw new Error("Invalid agent run identifier.");
|
|
447
452
|
const directory = await runtimeDirectory(profile, true);
|
|
448
453
|
const path = join(directory, `${runId}.stop`);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { opendir, rm } from "node:fs/promises";
|
|
1
|
+
import { opendir, rm, readFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { readAgentProfile } from "./agent-profile.mjs";
|
|
6
|
-
import { agentStartMain, readAgentRuntimeStatus } from "./agent-runner.mjs";
|
|
6
|
+
import { agentStartMain, readAgentRuntimeStatus, requestStop } from "./agent-runner.mjs";
|
|
7
7
|
import {
|
|
8
8
|
privateDirectory,
|
|
9
9
|
privateRead,
|
|
@@ -12,6 +12,10 @@ import {
|
|
|
12
12
|
} from "./runtime-installation.mjs";
|
|
13
13
|
import { resumeDeviceLogin } from "./device-login.mjs";
|
|
14
14
|
|
|
15
|
+
const PACKAGE_VERSION = JSON.parse(
|
|
16
|
+
await readFile(new URL("../package.json", import.meta.url), "utf8"),
|
|
17
|
+
).version;
|
|
18
|
+
|
|
15
19
|
export async function setAutomaticEnabled(profile, enabled) {
|
|
16
20
|
await privateWrite(join(profile.stateDirectory, "automatic.json"), { enabled });
|
|
17
21
|
}
|
|
@@ -42,7 +46,22 @@ export async function superviseOnce(env = process.env, options = {}) {
|
|
|
42
46
|
const profile = await readAgentProfile(entry.name, { environment: env });
|
|
43
47
|
if (!(await automaticEnabled(profile))) continue;
|
|
44
48
|
const state = await (options.readStatus ?? readAgentRuntimeStatus)(profile);
|
|
45
|
-
if (state.running)
|
|
49
|
+
if (state.running) {
|
|
50
|
+
if (state.packageVersion === (options.packageVersion ?? PACKAGE_VERSION)) continue;
|
|
51
|
+
// Finish active work before upgrading. Never overlap two workers or clear journals.
|
|
52
|
+
if (!["connected", "waiting", "blocked", "reconnecting"].includes(state.phase)) continue;
|
|
53
|
+
await (options.stop ?? requestStop)(profile, state.runId);
|
|
54
|
+
const deadline = Date.now() + (options.stopTimeoutMs ?? 10000);
|
|
55
|
+
let stopped = false;
|
|
56
|
+
while (Date.now() < deadline) {
|
|
57
|
+
if (!(await (options.readStatus ?? readAgentRuntimeStatus)(profile)).running) {
|
|
58
|
+
stopped = true;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
await sleep(100);
|
|
62
|
+
}
|
|
63
|
+
if (!stopped || !(await automaticEnabled(profile))) continue;
|
|
64
|
+
}
|
|
46
65
|
// The run-local journal decides whether any interrupted work can safely resume.
|
|
47
66
|
await (options.start ?? agentStartMain)(["--profile", entry.name, "--background", "--json"], {
|
|
48
67
|
env,
|
|
@@ -154,7 +154,9 @@ export function createCommandAdapter(config) {
|
|
|
154
154
|
if (code !== 0) {
|
|
155
155
|
try {
|
|
156
156
|
return reject(
|
|
157
|
-
new WorkerError(
|
|
157
|
+
new WorkerError(
|
|
158
|
+
config.classifyFailure?.(`${diagnosticOutput}\n${output}`) ?? "AGENT_EXIT_FAILED",
|
|
159
|
+
),
|
|
158
160
|
);
|
|
159
161
|
} catch {
|
|
160
162
|
return reject(new WorkerError("AGENT_EXIT_FAILED"));
|