dahrk-node 0.1.16 → 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/dist/main.js +142 -25
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -902,6 +902,10 @@ function mapUsage2(u) {
|
|
|
902
902
|
|
|
903
903
|
// ../../packages/executor-worktree/src/codex-adapter.ts
|
|
904
904
|
var COALESCE_MS2 = Number(process.env.DAHRK_COALESCE_MS ?? process.env.SKAKEL_COALESCE_MS ?? 40);
|
|
905
|
+
var CODEX_COST_UNAVAILABLE_NOTE = "codex-adapter: cost reporting unavailable for the Codex runtime (the SDK reports tokens, not price); costUsd left unset, not $0\n";
|
|
906
|
+
function warnCostUnavailable(write = (s) => void process.stderr.write(s)) {
|
|
907
|
+
write(CODEX_COST_UNAVAILABLE_NOTE);
|
|
908
|
+
}
|
|
905
909
|
function runtimeEnvOptions2(ctx) {
|
|
906
910
|
if (!ctx.runtimeEnv) return {};
|
|
907
911
|
const env = {};
|
|
@@ -961,6 +965,7 @@ function createCodexRunner() {
|
|
|
961
965
|
}
|
|
962
966
|
captureThreadId(t);
|
|
963
967
|
if (cancelled) status = "fail";
|
|
968
|
+
warnCostUnavailable();
|
|
964
969
|
return { status, ...sessionId ? { sessionId } : {} };
|
|
965
970
|
},
|
|
966
971
|
async runInteractive(ctx, turns, onTrace) {
|
|
@@ -1031,6 +1036,7 @@ function createCodexRunner() {
|
|
|
1031
1036
|
summary = "(stage cancelled)";
|
|
1032
1037
|
}
|
|
1033
1038
|
captureThreadId(t);
|
|
1039
|
+
warnCostUnavailable();
|
|
1034
1040
|
return { status, summary, ...sessionId ? { sessionId } : {} };
|
|
1035
1041
|
},
|
|
1036
1042
|
async summarise(ctx) {
|
|
@@ -1186,6 +1192,10 @@ function createPiRunner(deps = {}) {
|
|
|
1186
1192
|
const captureSessionId = (s) => {
|
|
1187
1193
|
if (s.sessionId) sessionId = s.sessionId;
|
|
1188
1194
|
};
|
|
1195
|
+
const readSessionCost = (s) => {
|
|
1196
|
+
const cost = s.getSessionStats?.()?.cost;
|
|
1197
|
+
return typeof cost === "number" ? cost : void 0;
|
|
1198
|
+
};
|
|
1189
1199
|
return {
|
|
1190
1200
|
runtime: "pi",
|
|
1191
1201
|
async runBatch(ctx, onTrace) {
|
|
@@ -1209,7 +1219,8 @@ function createPiRunner(deps = {}) {
|
|
|
1209
1219
|
}
|
|
1210
1220
|
captureSessionId(s);
|
|
1211
1221
|
if (cancelled) status = "fail";
|
|
1212
|
-
|
|
1222
|
+
const costUsd = readSessionCost(s);
|
|
1223
|
+
return { status, ...sessionId ? { sessionId } : {}, ...costUsd !== void 0 ? { costUsd } : {} };
|
|
1213
1224
|
},
|
|
1214
1225
|
async runInteractive(ctx, turns, onTrace) {
|
|
1215
1226
|
const emit = makeEmit("pi", onTrace);
|
|
@@ -1304,7 +1315,13 @@ function createPiRunner(deps = {}) {
|
|
|
1304
1315
|
}
|
|
1305
1316
|
unsub();
|
|
1306
1317
|
captureSessionId(s);
|
|
1307
|
-
|
|
1318
|
+
const costUsd = readSessionCost(s);
|
|
1319
|
+
return {
|
|
1320
|
+
status,
|
|
1321
|
+
summary,
|
|
1322
|
+
...sessionId ? { sessionId } : {},
|
|
1323
|
+
...costUsd !== void 0 ? { costUsd } : {}
|
|
1324
|
+
};
|
|
1308
1325
|
},
|
|
1309
1326
|
async summarise(ctx) {
|
|
1310
1327
|
if (!session) return "(no summary: session not established)";
|
|
@@ -3867,6 +3884,9 @@ function createStageRunner(deps) {
|
|
|
3867
3884
|
|
|
3868
3885
|
// ../../packages/edge/src/ws-client.ts
|
|
3869
3886
|
var ENROLMENT_REJECTED_EXIT_CODE = 78;
|
|
3887
|
+
var RECONNECT_BASE_MS = 500;
|
|
3888
|
+
var RECONNECT_MAX_MS = 3e4;
|
|
3889
|
+
var PARK_POLL_MS = 6e4;
|
|
3870
3890
|
function classifyError(e) {
|
|
3871
3891
|
const msg = e instanceof Error ? e.message : String(e);
|
|
3872
3892
|
if (/\bgit\b|worktree|clone|fetch|branch|checkout|remote/i.test(msg)) return "git";
|
|
@@ -3902,7 +3922,11 @@ async function startEdgeNode(opts) {
|
|
|
3902
3922
|
let lastPongAt = 0;
|
|
3903
3923
|
let shuttingDown = false;
|
|
3904
3924
|
let connectCount = 0;
|
|
3925
|
+
let enrolToken = opts.enrolToken;
|
|
3926
|
+
let reconnectAttempts = 0;
|
|
3927
|
+
let parkTimer;
|
|
3905
3928
|
let onFatal;
|
|
3929
|
+
let onParked;
|
|
3906
3930
|
const send = (msg) => {
|
|
3907
3931
|
if (ws && ws.readyState === WebSocket.OPEN) ws.send(encode(msg));
|
|
3908
3932
|
};
|
|
@@ -4025,7 +4049,7 @@ async function startEdgeNode(opts) {
|
|
|
4025
4049
|
const sendHello = () => {
|
|
4026
4050
|
send({
|
|
4027
4051
|
type: "hello",
|
|
4028
|
-
enrolToken:
|
|
4052
|
+
enrolToken: enrolToken ?? "",
|
|
4029
4053
|
detectedRuntimes: currentRuntimes,
|
|
4030
4054
|
servesRepoIds: opts.servesRepoIds ?? [],
|
|
4031
4055
|
...opts.credentialModeExplicit && opts.credentialMode ? { credentialMode: opts.credentialMode } : {},
|
|
@@ -4085,6 +4109,7 @@ async function startEdgeNode(opts) {
|
|
|
4085
4109
|
const msg = decode(raw);
|
|
4086
4110
|
if (msg.type === "welcome") {
|
|
4087
4111
|
stageDeps.tenantId = msg.tenantId;
|
|
4112
|
+
reconnectAttempts = 0;
|
|
4088
4113
|
if (opts.retention === void 0 && msg.retention) stageDeps.retention = msg.retention;
|
|
4089
4114
|
if (opts.heartbeatMs === void 0 && msg.heartbeatMs > 0 && ws) {
|
|
4090
4115
|
startHeartbeat(ws, msg.heartbeatMs);
|
|
@@ -4269,19 +4294,59 @@ async function startEdgeNode(opts) {
|
|
|
4269
4294
|
if (heartbeat) clearInterval(heartbeat);
|
|
4270
4295
|
heartbeat = void 0;
|
|
4271
4296
|
if (isEnrolmentRejection(code)) {
|
|
4272
|
-
shuttingDown = true;
|
|
4273
4297
|
const detail = reason.toString() || "enrolment rejected";
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4298
|
+
const refresh = opts.refreshEnrolToken;
|
|
4299
|
+
log.error({ closeCode: code, detail, fatal: !refresh }, `EDGE_REJECTED:${code} ${detail}`);
|
|
4300
|
+
if (!refresh) {
|
|
4301
|
+
shuttingDown = true;
|
|
4302
|
+
process.exitCode = ENROLMENT_REJECTED_EXIT_CODE;
|
|
4303
|
+
onFatal?.(new Error(`hub rejected edge enrolment (${code}): ${detail}`));
|
|
4304
|
+
return;
|
|
4305
|
+
}
|
|
4306
|
+
park(refresh);
|
|
4277
4307
|
return;
|
|
4278
4308
|
}
|
|
4279
4309
|
if (!shuttingDown) {
|
|
4280
|
-
|
|
4281
|
-
|
|
4310
|
+
const delay = reconnectDelay();
|
|
4311
|
+
log.warn(
|
|
4312
|
+
{ closeCode: code, reason: reason.toString(), connectCount, delayMs: delay, attempt: reconnectAttempts },
|
|
4313
|
+
`EDGE_DISCONNECTED:${code} reconnecting in ${delay}ms`
|
|
4314
|
+
);
|
|
4315
|
+
reconnectTimer = setTimeout(connect, delay);
|
|
4282
4316
|
}
|
|
4283
4317
|
});
|
|
4284
4318
|
};
|
|
4319
|
+
const reconnectDelay = () => {
|
|
4320
|
+
const ceiling = Math.min(RECONNECT_BASE_MS * 2 ** reconnectAttempts, RECONNECT_MAX_MS);
|
|
4321
|
+
reconnectAttempts++;
|
|
4322
|
+
return Math.round(ceiling / 2 + Math.random() * (ceiling / 2));
|
|
4323
|
+
};
|
|
4324
|
+
const park = (refresh) => {
|
|
4325
|
+
const rejected = enrolToken;
|
|
4326
|
+
if (parkTimer) return;
|
|
4327
|
+
const pollMs = opts.parkPollMs ?? PARK_POLL_MS;
|
|
4328
|
+
log.error(
|
|
4329
|
+
{ pollMs },
|
|
4330
|
+
"EDGE_PARKED:enrolment rejected - the node is up but serving nothing. Re-enrol with `dahrk start --token <token>` (get one at https://app.dahrk.ai); it will reconnect on its own."
|
|
4331
|
+
);
|
|
4332
|
+
parkTimer = setInterval(() => {
|
|
4333
|
+
let fresh;
|
|
4334
|
+
try {
|
|
4335
|
+
fresh = refresh();
|
|
4336
|
+
} catch (e) {
|
|
4337
|
+
log.warn({ err: e }, `EDGE_PARK_REFRESH_FAILED ${e.message}`);
|
|
4338
|
+
return;
|
|
4339
|
+
}
|
|
4340
|
+
if (!fresh || fresh === rejected) return;
|
|
4341
|
+
if (parkTimer) clearInterval(parkTimer);
|
|
4342
|
+
parkTimer = void 0;
|
|
4343
|
+
enrolToken = fresh;
|
|
4344
|
+
reconnectAttempts = 0;
|
|
4345
|
+
log.info({}, "EDGE_UNPARKED:a new enrolment token was found, reconnecting");
|
|
4346
|
+
connect();
|
|
4347
|
+
}, pollMs);
|
|
4348
|
+
onParked?.();
|
|
4349
|
+
};
|
|
4285
4350
|
opts.signal?.addEventListener(
|
|
4286
4351
|
"abort",
|
|
4287
4352
|
() => {
|
|
@@ -4292,6 +4357,8 @@ async function startEdgeNode(opts) {
|
|
|
4292
4357
|
reconnectTimer = void 0;
|
|
4293
4358
|
if (reprobeTimer) clearInterval(reprobeTimer);
|
|
4294
4359
|
reprobeTimer = void 0;
|
|
4360
|
+
if (parkTimer) clearInterval(parkTimer);
|
|
4361
|
+
parkTimer = void 0;
|
|
4295
4362
|
ws?.close(1e3, "shutting down");
|
|
4296
4363
|
},
|
|
4297
4364
|
{ once: true }
|
|
@@ -4308,6 +4375,10 @@ async function startEdgeNode(opts) {
|
|
|
4308
4375
|
clearInterval(t);
|
|
4309
4376
|
reject(err);
|
|
4310
4377
|
};
|
|
4378
|
+
onParked = () => {
|
|
4379
|
+
clearInterval(t);
|
|
4380
|
+
resolve3();
|
|
4381
|
+
};
|
|
4311
4382
|
});
|
|
4312
4383
|
}
|
|
4313
4384
|
|
|
@@ -5720,6 +5791,7 @@ function persistEnrolment(env, enrolment) {
|
|
|
5720
5791
|
});
|
|
5721
5792
|
}
|
|
5722
5793
|
function resolveEnrolToken(env, opts = {}) {
|
|
5794
|
+
if (opts.supervised && !opts.ephemeral) return readPersistedToken(env) ?? env.DAHRK_ENROL_TOKEN;
|
|
5723
5795
|
if (env.DAHRK_ENROL_TOKEN) return env.DAHRK_ENROL_TOKEN;
|
|
5724
5796
|
if (opts.ephemeral) return void 0;
|
|
5725
5797
|
return readPersistedToken(env);
|
|
@@ -5739,7 +5811,6 @@ function serviceArgv(inputs) {
|
|
|
5739
5811
|
}
|
|
5740
5812
|
function serviceEnv(inputs) {
|
|
5741
5813
|
return {
|
|
5742
|
-
DAHRK_ENROL_TOKEN: inputs.token,
|
|
5743
5814
|
DAHRK_SUPERVISED: "1",
|
|
5744
5815
|
...inputs.hubUrl ? { DAHRK_HUB_URL: inputs.hubUrl } : {},
|
|
5745
5816
|
...inputs.name ? { DAHRK_NODE_NAME: inputs.name } : {},
|
|
@@ -5911,7 +5982,6 @@ async function runServiceInstall(inputs, deps = {}) {
|
|
|
5911
5982
|
manager,
|
|
5912
5983
|
nodeBin: d.nodeBin,
|
|
5913
5984
|
scriptPath: d.scriptPath,
|
|
5914
|
-
token: inputs.token,
|
|
5915
5985
|
...inputs.name ? { name: inputs.name } : {},
|
|
5916
5986
|
...inputs.hubUrl ? { hubUrl: inputs.hubUrl } : {},
|
|
5917
5987
|
...d.pathEnv ? { pathEnv: d.pathEnv } : {},
|
|
@@ -5979,21 +6049,19 @@ async function runNodeStart(inputs, deps = {}, opts = {}) {
|
|
|
5979
6049
|
manager,
|
|
5980
6050
|
nodeBin: d.nodeBin,
|
|
5981
6051
|
scriptPath: d.scriptPath,
|
|
5982
|
-
token: inputs.token ?? "-",
|
|
5983
6052
|
...inputs.name ? { name: inputs.name } : {},
|
|
5984
6053
|
...inputs.hubUrl ? { hubUrl: inputs.hubUrl } : {},
|
|
5985
6054
|
...d.pathEnv ? { pathEnv: d.pathEnv } : {},
|
|
5986
6055
|
homeDir: d.homeDir,
|
|
5987
6056
|
logDir: d.logDir
|
|
5988
6057
|
});
|
|
5989
|
-
const
|
|
5990
|
-
const current = canRender && unitExists && unitIsCurrent(plan, d.readFile(filePath));
|
|
6058
|
+
const current = unitExists && unitIsCurrent(plan, d.readFile(filePath));
|
|
5991
6059
|
if (!opts.alwaysLoad) {
|
|
5992
6060
|
const probe2 = unitExists ? d.capture(statusCommand(manager)) : { code: 1, stdout: "" };
|
|
5993
6061
|
const status = parseServiceStatus(manager, unitExists, probe2);
|
|
5994
6062
|
if (current && status.running) return { kind: "running", code: 0, already: true };
|
|
5995
6063
|
}
|
|
5996
|
-
if (
|
|
6064
|
+
if (!current) {
|
|
5997
6065
|
try {
|
|
5998
6066
|
d.mkdirp(d.logDir);
|
|
5999
6067
|
d.mkdirp(dirOf(plan.filePath));
|
|
@@ -6023,7 +6091,6 @@ async function runNodeRestart(inputs, deps = {}) {
|
|
|
6023
6091
|
manager,
|
|
6024
6092
|
nodeBin: d.nodeBin,
|
|
6025
6093
|
scriptPath: d.scriptPath,
|
|
6026
|
-
token: "-",
|
|
6027
6094
|
homeDir: d.homeDir,
|
|
6028
6095
|
logDir: d.logDir
|
|
6029
6096
|
});
|
|
@@ -6065,7 +6132,6 @@ async function runNodeStop(inputs = {}, deps = {}) {
|
|
|
6065
6132
|
manager,
|
|
6066
6133
|
nodeBin: d.nodeBin,
|
|
6067
6134
|
scriptPath: d.scriptPath,
|
|
6068
|
-
token: "-",
|
|
6069
6135
|
homeDir: d.homeDir,
|
|
6070
6136
|
logDir: d.logDir
|
|
6071
6137
|
});
|
|
@@ -6102,7 +6168,6 @@ async function runServiceUninstall(deps = {}) {
|
|
|
6102
6168
|
manager,
|
|
6103
6169
|
nodeBin: d.nodeBin,
|
|
6104
6170
|
scriptPath: d.scriptPath,
|
|
6105
|
-
token: "-",
|
|
6106
6171
|
homeDir: d.homeDir,
|
|
6107
6172
|
logDir: d.logDir
|
|
6108
6173
|
});
|
|
@@ -6166,9 +6231,9 @@ var defaultDeps3 = () => ({
|
|
|
6166
6231
|
// npm-global bins) that a supervisor's minimal PATH would otherwise hide.
|
|
6167
6232
|
pathEnv: process.env.PATH,
|
|
6168
6233
|
mkdirp: (dir) => void mkdirSync12(dir, { recursive: true }),
|
|
6169
|
-
// The unit
|
|
6170
|
-
//
|
|
6171
|
-
// too - re-installing over a unit an older client left at 0644 must tighten it
|
|
6234
|
+
// The unit holds no secret any more, but it stays owner-only: it names paths on this host, and there is
|
|
6235
|
+
// no reason for it to be world-readable. `writeFileSync`'s `mode` applies only when it CREATES the file,
|
|
6236
|
+
// so chmod explicitly too - re-installing over a unit an older client left at 0644 must tighten it.
|
|
6172
6237
|
writeFile: (path, content) => {
|
|
6173
6238
|
writeFileSync11(path, content, { mode: UNIT_FILE_MODE });
|
|
6174
6239
|
chmodSync3(path, UNIT_FILE_MODE);
|
|
@@ -6510,6 +6575,12 @@ function renderStatus(f) {
|
|
|
6510
6575
|
lines.push(kv("Work", jobLine(first, f.now)));
|
|
6511
6576
|
for (const j of rest) lines.push(kv("", jobLine(j, f.now)));
|
|
6512
6577
|
}
|
|
6578
|
+
if (isEnrolmentBlocked(f.connection)) {
|
|
6579
|
+
lines.push("");
|
|
6580
|
+
lines.push(verdict("fail", "The hub rejected this node's enrolment token; it is serving no Jobs."));
|
|
6581
|
+
lines.push("");
|
|
6582
|
+
lines.push(hint("Re-enrol with `dahrk start --token <token>`. Get one at https://app.dahrk.ai."));
|
|
6583
|
+
}
|
|
6513
6584
|
lines.push(...presenceHints(f).flatMap((h) => ["", h]));
|
|
6514
6585
|
lines.push("", dim(` State file: ${f.stateFile}`));
|
|
6515
6586
|
return lines;
|
|
@@ -6532,7 +6603,7 @@ function jobLine(j, now) {
|
|
|
6532
6603
|
return `${where} ${dim(humanDuration(now - j.startedAt))}`;
|
|
6533
6604
|
}
|
|
6534
6605
|
function isUnhealthy(f) {
|
|
6535
|
-
return f.presence.kind === "crashed";
|
|
6606
|
+
return f.presence.kind === "crashed" || isEnrolmentBlocked(f.connection);
|
|
6536
6607
|
}
|
|
6537
6608
|
function resolvePresence(service, lockedPid, desired) {
|
|
6538
6609
|
if (service?.running) return { kind: "running", ...service.pid ? { pid: service.pid } : {} };
|
|
@@ -6575,10 +6646,21 @@ async function gatherFacts(inputs, deps) {
|
|
|
6575
6646
|
}
|
|
6576
6647
|
var CONNECTION_MARKERS = [
|
|
6577
6648
|
{ prefix: "EDGE_WELCOMED", event: "welcomed", detail: false },
|
|
6649
|
+
// The two markers whose absence here was itself the bug. `EDGE_CONNECTED` is written when the SOCKET
|
|
6650
|
+
// opens, which is before the hub has looked at our token - so a node whose enrolment was rejected has
|
|
6651
|
+
// `EDGE_CONNECTED` as its last marker, and `status` used to read that and cheerfully report a connected
|
|
6652
|
+
// node while it served nothing. The rejection is the newer marker, and it is the true one.
|
|
6653
|
+
{ prefix: "EDGE_REJECTED", event: "rejected", detail: true },
|
|
6654
|
+
{ prefix: "EDGE_PARKED", event: "parked", detail: false },
|
|
6655
|
+
{ prefix: "EDGE_UNPARKED", event: "reconnecting", detail: false },
|
|
6578
6656
|
{ prefix: "EDGE_CONNECTED", event: "connected", detail: false },
|
|
6579
6657
|
{ prefix: "EDGE_DISCONNECTED", event: "disconnected", detail: true },
|
|
6580
6658
|
{ prefix: "EDGE_STALE", event: "went stale", detail: false }
|
|
6581
6659
|
];
|
|
6660
|
+
var BLOCKED_EVENTS = /* @__PURE__ */ new Set(["rejected", "parked"]);
|
|
6661
|
+
function isEnrolmentBlocked(connection) {
|
|
6662
|
+
return connection !== void 0 && BLOCKED_EVENTS.has(connection.event);
|
|
6663
|
+
}
|
|
6582
6664
|
function lastConnection(records) {
|
|
6583
6665
|
for (let i = records.length - 1; i >= 0; i--) {
|
|
6584
6666
|
const r = records[i];
|
|
@@ -6604,7 +6686,7 @@ async function runStatus(inputs, deps) {
|
|
|
6604
6686
|
}
|
|
6605
6687
|
|
|
6606
6688
|
// src/main.ts
|
|
6607
|
-
var CLIENT_VERSION = "0.1.
|
|
6689
|
+
var CLIENT_VERSION = "0.1.18";
|
|
6608
6690
|
var DEFAULT_HUB_URL = "wss://api.dahrk.ai";
|
|
6609
6691
|
var list = (v) => (v ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
6610
6692
|
var RUNTIMES2 = ["claude-code", "codex", "pi"];
|
|
@@ -6689,6 +6771,8 @@ function wantsForeground(env, flags) {
|
|
|
6689
6771
|
async function start(flags) {
|
|
6690
6772
|
const env = envWithFlags(process.env, flags);
|
|
6691
6773
|
if (wantsForeground(env, flags)) return startForeground(env, flags);
|
|
6774
|
+
const enrolled = await enrolToDisk(env);
|
|
6775
|
+
if (enrolled !== 0) return enrolled;
|
|
6692
6776
|
await offerUpdate(env);
|
|
6693
6777
|
const outcome = await runNodeStart(serviceInputs(env));
|
|
6694
6778
|
if (outcome.kind === "error") return outcome.code;
|
|
@@ -6708,6 +6792,28 @@ var serviceInputs = (env) => ({
|
|
|
6708
6792
|
...env.DAHRK_NODE_NAME ? { name: env.DAHRK_NODE_NAME } : {},
|
|
6709
6793
|
...env.DAHRK_HUB_URL ? { hubUrl: env.DAHRK_HUB_URL } : {}
|
|
6710
6794
|
});
|
|
6795
|
+
async function enrolToDisk(env) {
|
|
6796
|
+
const token = resolveEnrolToken(env);
|
|
6797
|
+
if (!token || token === readPersistedToken(env)) return 0;
|
|
6798
|
+
const hubUrl = env.DAHRK_HUB_URL ?? DEFAULT_HUB_URL;
|
|
6799
|
+
const probe2 = await probeHub({ hubUrl, enrolToken: token, clientVersion: CLIENT_VERSION });
|
|
6800
|
+
if (!probe2.ok && probe2.reason === "rejected") {
|
|
6801
|
+
out("");
|
|
6802
|
+
out(verdict("fail", `The hub rejected that enrolment token: ${probe2.detail}`));
|
|
6803
|
+
out("");
|
|
6804
|
+
out(hint("Get a fresh token at https://app.dahrk.ai, then run `dahrk start --token <token>` again."));
|
|
6805
|
+
return 2;
|
|
6806
|
+
}
|
|
6807
|
+
if (!probe2.ok) {
|
|
6808
|
+
out("");
|
|
6809
|
+
out(verdict("warn", `Could not reach ${hubUrl} to check the token (${probe2.detail}); using it anyway.`));
|
|
6810
|
+
}
|
|
6811
|
+
persistEnrolment(env, {
|
|
6812
|
+
token,
|
|
6813
|
+
...probe2.ok ? { name: probe2.name, tenantId: probe2.tenantId } : {}
|
|
6814
|
+
});
|
|
6815
|
+
return 0;
|
|
6816
|
+
}
|
|
6711
6817
|
async function restart(flags, force) {
|
|
6712
6818
|
const env = envWithFlags(process.env, flags);
|
|
6713
6819
|
out("");
|
|
@@ -6746,7 +6852,10 @@ async function startForeground(env, flags) {
|
|
|
6746
6852
|
clientVersion: CLIENT_VERSION,
|
|
6747
6853
|
...env.DAHRK_CRASH_EXIT === "1" ? { exitOnCrash: true } : {}
|
|
6748
6854
|
});
|
|
6749
|
-
const token = resolveEnrolToken(env, {
|
|
6855
|
+
const token = resolveEnrolToken(env, {
|
|
6856
|
+
ephemeral: flags.ephemeral,
|
|
6857
|
+
supervised: env.DAHRK_SUPERVISED === "1"
|
|
6858
|
+
});
|
|
6750
6859
|
if (token) env.DAHRK_ENROL_TOKEN = token;
|
|
6751
6860
|
const runtimes = await resolveRuntimes(env);
|
|
6752
6861
|
if (!flags.ephemeral) {
|
|
@@ -6785,7 +6894,15 @@ async function startForeground(env, flags) {
|
|
|
6785
6894
|
...flags.ephemeral ? {} : { jobLedger: fileJobLedger(jobLedgerFile(stateDir(env))) },
|
|
6786
6895
|
...persist ? {
|
|
6787
6896
|
onEnrolled: (welcome) => persistEnrolment(env, { token, name: welcome.name, tenantId: welcome.tenantId })
|
|
6788
|
-
} : {}
|
|
6897
|
+
} : {},
|
|
6898
|
+
// How a rejected node heals. Reads `node.json` DIRECTLY rather than going through
|
|
6899
|
+
// `resolveEnrolToken`, and that is the whole point: the disk is where re-enrolment writes, so it is
|
|
6900
|
+
// the only place a *newer* token can appear. (Deliberately narrower than boot-time resolution, which
|
|
6901
|
+
// still lets an explicit `DAHRK_ENROL_TOKEN` win - but a stale env var must never be able to shadow
|
|
6902
|
+
// the fresh token on disk forever, which is precisely how a node used to wedge.)
|
|
6903
|
+
//
|
|
6904
|
+
// Only for a persistent node: an ephemeral one has no disk to heal from and must still fail fast.
|
|
6905
|
+
...flags.ephemeral ? {} : { refreshEnrolToken: () => readPersistedToken(env) }
|
|
6789
6906
|
});
|
|
6790
6907
|
return 0;
|
|
6791
6908
|
}
|