@kynver-app/runtime 0.1.16 → 0.1.17
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/cli.js +405 -229
- package/dist/cli.js.map +4 -4
- package/dist/index.js +411 -232
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { mkdirSync as mkdirSync5, realpathSync } from "node:fs";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
6
6
|
|
|
7
7
|
// src/config.ts
|
|
8
8
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -392,12 +392,12 @@ var DEFAULT_CRITICAL_FREE_BYTES = 15 * 1024 * 1024 * 1024;
|
|
|
392
392
|
var DEFAULT_MAX_USED_PERCENT = 80;
|
|
393
393
|
var DEFAULT_HARD_MAX_USED_PERCENT = 90;
|
|
394
394
|
function observeRunnerDiskGate(input = {}) {
|
|
395
|
-
const
|
|
395
|
+
const path16 = input.diskPath?.trim() || "/";
|
|
396
396
|
const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
|
|
397
397
|
const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
|
|
398
398
|
const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
|
|
399
399
|
const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
|
|
400
|
-
const stats = statfsSync(
|
|
400
|
+
const stats = statfsSync(path16);
|
|
401
401
|
const freeBytes = Number(stats.bavail) * Number(stats.bsize);
|
|
402
402
|
const totalBytes = Number(stats.blocks) * Number(stats.bsize);
|
|
403
403
|
const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
|
|
@@ -417,7 +417,7 @@ function observeRunnerDiskGate(input = {}) {
|
|
|
417
417
|
}
|
|
418
418
|
return {
|
|
419
419
|
ok,
|
|
420
|
-
path:
|
|
420
|
+
path: path16,
|
|
421
421
|
freeBytes,
|
|
422
422
|
totalBytes,
|
|
423
423
|
usedPercent,
|
|
@@ -907,8 +907,8 @@ function observeRunnerResourceGate(input) {
|
|
|
907
907
|
}
|
|
908
908
|
|
|
909
909
|
// src/supervisor.ts
|
|
910
|
-
import { existsSync as
|
|
911
|
-
import
|
|
910
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3 } from "node:fs";
|
|
911
|
+
import path9 from "node:path";
|
|
912
912
|
|
|
913
913
|
// src/prompt.ts
|
|
914
914
|
function buildPrompt(input) {
|
|
@@ -1082,6 +1082,369 @@ function resolveWorkerProvider(name) {
|
|
|
1082
1082
|
return provider;
|
|
1083
1083
|
}
|
|
1084
1084
|
|
|
1085
|
+
// src/auto-complete.ts
|
|
1086
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
1087
|
+
import { existsSync as existsSync8, openSync as openSync3, closeSync as closeSync3 } from "node:fs";
|
|
1088
|
+
import path8 from "node:path";
|
|
1089
|
+
import { fileURLToPath } from "node:url";
|
|
1090
|
+
|
|
1091
|
+
// src/worker-ops.ts
|
|
1092
|
+
import path7 from "node:path";
|
|
1093
|
+
async function postCompletion(url, secret, body) {
|
|
1094
|
+
const res = await fetch(url, {
|
|
1095
|
+
method: "POST",
|
|
1096
|
+
headers: buildHarnessCallbackHeaders(secret),
|
|
1097
|
+
body: JSON.stringify(body)
|
|
1098
|
+
});
|
|
1099
|
+
let parsed = null;
|
|
1100
|
+
try {
|
|
1101
|
+
parsed = await res.json();
|
|
1102
|
+
} catch {
|
|
1103
|
+
parsed = null;
|
|
1104
|
+
}
|
|
1105
|
+
return { ok: res.ok, status: res.status, parsed };
|
|
1106
|
+
}
|
|
1107
|
+
function completionErrorText(parsed) {
|
|
1108
|
+
if (parsed && typeof parsed === "object") {
|
|
1109
|
+
const err = parsed.error;
|
|
1110
|
+
if (typeof err === "string" && err.trim()) return err.trim();
|
|
1111
|
+
}
|
|
1112
|
+
return void 0;
|
|
1113
|
+
}
|
|
1114
|
+
function persistCompletionBlocker(worker, reason) {
|
|
1115
|
+
const current = worker.completionBlocker;
|
|
1116
|
+
if ((current ?? void 0) === (reason ?? void 0)) return;
|
|
1117
|
+
if (reason) worker.completionBlocker = reason;
|
|
1118
|
+
else delete worker.completionBlocker;
|
|
1119
|
+
saveWorker(worker.runId, worker);
|
|
1120
|
+
}
|
|
1121
|
+
async function tryCompleteWorker(args) {
|
|
1122
|
+
const worker = loadWorker(String(args.run), String(args.name));
|
|
1123
|
+
const status = computeWorkerStatus(worker);
|
|
1124
|
+
const agentOsId = (args.agentOsId ? String(args.agentOsId) : worker.agentOsId) || "";
|
|
1125
|
+
const taskId = (args.taskId ? String(args.taskId) : worker.taskId) || null;
|
|
1126
|
+
if (!agentOsId) {
|
|
1127
|
+
return { ok: false, reason: "missing agentOsId" };
|
|
1128
|
+
}
|
|
1129
|
+
if (!isFinishedWorkerStatus(status)) {
|
|
1130
|
+
return { ok: true, skipped: true, reason: "worker-not-finished" };
|
|
1131
|
+
}
|
|
1132
|
+
const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
|
|
1133
|
+
const explicitSecret = args.secret ? String(args.secret) : void 0;
|
|
1134
|
+
let secret = await resolveCallbackSecretWithMint(explicitSecret, agentOsId, { baseUrl: base });
|
|
1135
|
+
const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/harness/completion`;
|
|
1136
|
+
const body = {
|
|
1137
|
+
source: "openclaw-harness",
|
|
1138
|
+
agentOsId,
|
|
1139
|
+
runId: worker.runId,
|
|
1140
|
+
workerName: worker.name,
|
|
1141
|
+
taskId,
|
|
1142
|
+
startedAt: worker.startedAt,
|
|
1143
|
+
finishedAt: status.lastActivityAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1144
|
+
status
|
|
1145
|
+
};
|
|
1146
|
+
let result = await postCompletion(url, secret, body);
|
|
1147
|
+
if ((result.status === 401 || result.status === 403) && !explicitSecret) {
|
|
1148
|
+
const refreshed = await refreshRunnerToken(agentOsId, { baseUrl: base });
|
|
1149
|
+
if (refreshed && refreshed !== secret) {
|
|
1150
|
+
secret = refreshed;
|
|
1151
|
+
result = await postCompletion(url, secret, body);
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
if (result.ok) {
|
|
1155
|
+
persistCompletionBlocker(worker, void 0);
|
|
1156
|
+
return { ok: true, httpStatus: result.status, response: result.parsed };
|
|
1157
|
+
}
|
|
1158
|
+
const authRejected = result.status === 401 || result.status === 403;
|
|
1159
|
+
const detail = completionErrorText(result.parsed) ?? (authRejected ? "runner token unauthorized" : "non-2xx response");
|
|
1160
|
+
const reason = authRejected ? `completion replay rejected (${result.status}): ${detail}` : `completion replay failed (${result.status}): ${detail}`;
|
|
1161
|
+
persistCompletionBlocker(worker, reason);
|
|
1162
|
+
return { ok: false, httpStatus: result.status, response: result.parsed, completionBlocked: true };
|
|
1163
|
+
}
|
|
1164
|
+
async function completeWorker(args) {
|
|
1165
|
+
try {
|
|
1166
|
+
const worker = loadWorker(String(args.run), String(args.name));
|
|
1167
|
+
const status = computeWorkerStatus(worker);
|
|
1168
|
+
const agentOsId = (args.agentOsId ? String(args.agentOsId) : worker.agentOsId) || "";
|
|
1169
|
+
const taskId = (args.taskId ? String(args.taskId) : worker.taskId) || null;
|
|
1170
|
+
if (!agentOsId) {
|
|
1171
|
+
console.error("worker complete requires --agent-os-id (or an agentOsId persisted at worker start)");
|
|
1172
|
+
process.exit(1);
|
|
1173
|
+
}
|
|
1174
|
+
if (!isFinishedWorkerStatus(status)) {
|
|
1175
|
+
console.log(
|
|
1176
|
+
JSON.stringify(
|
|
1177
|
+
{
|
|
1178
|
+
worker: worker.name,
|
|
1179
|
+
runId: worker.runId,
|
|
1180
|
+
status: "skipped",
|
|
1181
|
+
reason: "worker-not-finished",
|
|
1182
|
+
workerStatus: status.status,
|
|
1183
|
+
alive: status.alive
|
|
1184
|
+
},
|
|
1185
|
+
null,
|
|
1186
|
+
2
|
|
1187
|
+
)
|
|
1188
|
+
);
|
|
1189
|
+
return;
|
|
1190
|
+
}
|
|
1191
|
+
const result = await tryCompleteWorker(args);
|
|
1192
|
+
console.log(
|
|
1193
|
+
JSON.stringify(
|
|
1194
|
+
{
|
|
1195
|
+
worker: worker.name,
|
|
1196
|
+
runId: worker.runId,
|
|
1197
|
+
agentOsId,
|
|
1198
|
+
taskId,
|
|
1199
|
+
httpStatus: result.httpStatus,
|
|
1200
|
+
response: result.response
|
|
1201
|
+
},
|
|
1202
|
+
null,
|
|
1203
|
+
2
|
|
1204
|
+
)
|
|
1205
|
+
);
|
|
1206
|
+
if (!result.ok) process.exit(1);
|
|
1207
|
+
} catch (error) {
|
|
1208
|
+
console.error(`worker complete failed: ${error.message}`);
|
|
1209
|
+
process.exit(1);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
function workerStatus(args) {
|
|
1213
|
+
const worker = loadWorker(String(args.run), String(args.name));
|
|
1214
|
+
const status = computeWorkerStatus(worker);
|
|
1215
|
+
writeJson(path7.join(worker.workerDir, "last-status.json"), status);
|
|
1216
|
+
console.log(JSON.stringify(status, null, 2));
|
|
1217
|
+
}
|
|
1218
|
+
function runStatus(args) {
|
|
1219
|
+
const run = loadRun(String(args.run));
|
|
1220
|
+
const names = Object.keys(run.workers || {});
|
|
1221
|
+
const workers = names.map((name) => {
|
|
1222
|
+
const worker = readJson(
|
|
1223
|
+
path7.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1224
|
+
void 0
|
|
1225
|
+
);
|
|
1226
|
+
if (!worker) {
|
|
1227
|
+
return { worker: name, status: "missing", attention: "needs_attention", attentionReason: "worker.json not found" };
|
|
1228
|
+
}
|
|
1229
|
+
const status = computeWorkerStatus(worker, { base: run.base });
|
|
1230
|
+
const rawBlocker = worker.completionBlocker;
|
|
1231
|
+
const completionBlocker = typeof rawBlocker === "string" && rawBlocker ? rawBlocker : void 0;
|
|
1232
|
+
return {
|
|
1233
|
+
worker: status.worker,
|
|
1234
|
+
status: completionBlocker ? "blocked" : status.status,
|
|
1235
|
+
attention: completionBlocker ? "blocked" : status.attention.state,
|
|
1236
|
+
attentionReason: completionBlocker ?? status.attention.reason,
|
|
1237
|
+
pid: status.pid,
|
|
1238
|
+
alive: status.alive,
|
|
1239
|
+
currentTool: status.currentTool,
|
|
1240
|
+
lastActivityAt: status.lastActivityAt,
|
|
1241
|
+
lastHeartbeatPhase: status.lastHeartbeatPhase,
|
|
1242
|
+
lastHeartbeatSummary: status.lastHeartbeatSummary,
|
|
1243
|
+
heartbeatBlocker: status.heartbeatBlocker,
|
|
1244
|
+
changedFileCount: status.changedFiles.length,
|
|
1245
|
+
branch: status.branch,
|
|
1246
|
+
ancestry: status.gitAncestry.relation,
|
|
1247
|
+
ancestryChecked: status.gitAncestry.checked
|
|
1248
|
+
};
|
|
1249
|
+
});
|
|
1250
|
+
const board = {
|
|
1251
|
+
runId: run.id,
|
|
1252
|
+
name: run.name,
|
|
1253
|
+
status: deriveRunStatus(run.status, workers),
|
|
1254
|
+
repo: run.repo,
|
|
1255
|
+
workerCount: workers.length,
|
|
1256
|
+
needsAttention: workers.filter((w) => w.attention && w.attention !== "ok" && w.attention !== "done").map((w) => w.worker),
|
|
1257
|
+
workers
|
|
1258
|
+
};
|
|
1259
|
+
writeJson(path7.join(runDirectory(run.id), "last-board.json"), board);
|
|
1260
|
+
console.log(JSON.stringify(board, null, 2));
|
|
1261
|
+
}
|
|
1262
|
+
function tailWorker(args) {
|
|
1263
|
+
const worker = loadWorker(String(args.run), String(args.name));
|
|
1264
|
+
const raw = tailFile(worker.stdoutPath, Number(args.lines || 40));
|
|
1265
|
+
if (args.raw === true || args.raw === "true") {
|
|
1266
|
+
process.stdout.write(raw);
|
|
1267
|
+
return;
|
|
1268
|
+
}
|
|
1269
|
+
for (const line of raw.split("\n").filter(Boolean)) {
|
|
1270
|
+
const event = safeJson(line);
|
|
1271
|
+
const summary = event ? summarizeEvent(event) : line;
|
|
1272
|
+
if (summary) console.log(summary);
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
function stopWorker(args) {
|
|
1276
|
+
const worker = loadWorker(String(args.run), String(args.name));
|
|
1277
|
+
if (!isPidAlive(worker.pid)) {
|
|
1278
|
+
console.log(JSON.stringify({ worker: worker.name, pid: worker.pid, status: "not_running" }, null, 2));
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
killWorkerProcess(worker.pid, "SIGTERM");
|
|
1282
|
+
sleepMs(1500);
|
|
1283
|
+
if (isPidAlive(worker.pid)) {
|
|
1284
|
+
killWorkerProcess(worker.pid, "SIGKILL");
|
|
1285
|
+
console.log(JSON.stringify({ worker: worker.name, pid: worker.pid, status: "sigkill_sent" }, null, 2));
|
|
1286
|
+
return;
|
|
1287
|
+
}
|
|
1288
|
+
console.log(JSON.stringify({ worker: worker.name, pid: worker.pid, status: "stopped" }, null, 2));
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
// src/auto-complete.ts
|
|
1292
|
+
var DEFAULT_POLL_MS = 5e3;
|
|
1293
|
+
var DEFAULT_MAX_TOTAL_MS = 6 * 60 * 60 * 1e3;
|
|
1294
|
+
var DEFAULT_COMPLETE_ATTEMPTS = 3;
|
|
1295
|
+
var DEFAULT_COMPLETE_BACKOFF_MS = 5e3;
|
|
1296
|
+
function readArgs(raw) {
|
|
1297
|
+
return {
|
|
1298
|
+
run: String(raw.run || ""),
|
|
1299
|
+
name: String(raw.name || ""),
|
|
1300
|
+
agentOsId: raw.agentOsId ? String(raw.agentOsId) : void 0,
|
|
1301
|
+
pollMs: Number(raw.pollMs) > 0 ? Math.floor(Number(raw.pollMs)) : void 0,
|
|
1302
|
+
maxTotalMs: Number(raw.maxTotalMs) > 0 ? Math.floor(Number(raw.maxTotalMs)) : void 0,
|
|
1303
|
+
completeAttempts: Number(raw.completeAttempts) > 0 ? Math.floor(Number(raw.completeAttempts)) : void 0,
|
|
1304
|
+
completeBackoffMs: Number(raw.completeBackoffMs) > 0 ? Math.floor(Number(raw.completeBackoffMs)) : void 0,
|
|
1305
|
+
baseUrl: raw.baseUrl ? String(raw.baseUrl) : void 0,
|
|
1306
|
+
secret: raw.secret ? String(raw.secret) : void 0
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
async function autoCompleteWorker(raw) {
|
|
1310
|
+
const args = readArgs(raw);
|
|
1311
|
+
const pollMs = args.pollMs ?? DEFAULT_POLL_MS;
|
|
1312
|
+
const maxTotalMs = args.maxTotalMs ?? DEFAULT_MAX_TOTAL_MS;
|
|
1313
|
+
const completeAttempts = args.completeAttempts ?? DEFAULT_COMPLETE_ATTEMPTS;
|
|
1314
|
+
const completeBackoffMs = args.completeBackoffMs ?? DEFAULT_COMPLETE_BACKOFF_MS;
|
|
1315
|
+
const worker = loadWorker(args.run, args.name);
|
|
1316
|
+
if (!worker.agentOsId || !worker.taskId) {
|
|
1317
|
+
return {
|
|
1318
|
+
worker: worker.name,
|
|
1319
|
+
runId: worker.runId,
|
|
1320
|
+
outcome: "missing_link",
|
|
1321
|
+
attempts: 0,
|
|
1322
|
+
reason: "worker has no agentOsId/taskId \u2014 nothing to attribute completion to"
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
const startMs = Date.now();
|
|
1326
|
+
while (true) {
|
|
1327
|
+
const status = computeWorkerStatus(worker);
|
|
1328
|
+
if (isFinishedWorkerStatus(status)) break;
|
|
1329
|
+
if (!isPidAlive(worker.pid)) break;
|
|
1330
|
+
if (Date.now() - startMs > maxTotalMs) {
|
|
1331
|
+
return {
|
|
1332
|
+
worker: worker.name,
|
|
1333
|
+
runId: worker.runId,
|
|
1334
|
+
outcome: "timed_out",
|
|
1335
|
+
attempts: 0,
|
|
1336
|
+
reason: `worker did not finish within ${maxTotalMs}ms`
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
sleepMs(pollMs);
|
|
1340
|
+
}
|
|
1341
|
+
let lastHttpStatus;
|
|
1342
|
+
let lastReason;
|
|
1343
|
+
for (let attempt = 1; attempt <= completeAttempts; attempt++) {
|
|
1344
|
+
const result = await tryCompleteWorker({
|
|
1345
|
+
run: args.run,
|
|
1346
|
+
name: args.name,
|
|
1347
|
+
...args.agentOsId ? { agentOsId: args.agentOsId } : {},
|
|
1348
|
+
...args.baseUrl ? { baseUrl: args.baseUrl } : {},
|
|
1349
|
+
...args.secret ? { secret: args.secret } : {}
|
|
1350
|
+
});
|
|
1351
|
+
lastHttpStatus = result.httpStatus;
|
|
1352
|
+
if (result.ok) {
|
|
1353
|
+
return {
|
|
1354
|
+
worker: worker.name,
|
|
1355
|
+
runId: worker.runId,
|
|
1356
|
+
outcome: "completed",
|
|
1357
|
+
httpStatus: result.httpStatus,
|
|
1358
|
+
attempts: attempt
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1361
|
+
const authRejected = result.httpStatus === 401 || result.httpStatus === 403;
|
|
1362
|
+
if (authRejected) {
|
|
1363
|
+
lastReason = typeof result.reason === "string" ? result.reason : "completion replay refused";
|
|
1364
|
+
return {
|
|
1365
|
+
worker: worker.name,
|
|
1366
|
+
runId: worker.runId,
|
|
1367
|
+
outcome: "blocked",
|
|
1368
|
+
httpStatus: result.httpStatus,
|
|
1369
|
+
attempts: attempt,
|
|
1370
|
+
reason: lastReason
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1373
|
+
lastReason = typeof result.reason === "string" ? result.reason : "transient failure";
|
|
1374
|
+
if (attempt < completeAttempts) sleepMs(completeBackoffMs);
|
|
1375
|
+
}
|
|
1376
|
+
return {
|
|
1377
|
+
worker: worker.name,
|
|
1378
|
+
runId: worker.runId,
|
|
1379
|
+
outcome: "blocked",
|
|
1380
|
+
httpStatus: lastHttpStatus,
|
|
1381
|
+
attempts: completeAttempts,
|
|
1382
|
+
reason: lastReason ?? "completion failed after retries"
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
async function autoCompleteWorkerCli(raw) {
|
|
1386
|
+
try {
|
|
1387
|
+
const outcome = await autoCompleteWorker(raw);
|
|
1388
|
+
console.log(JSON.stringify(outcome, null, 2));
|
|
1389
|
+
if (outcome.outcome === "missing_link" || outcome.outcome === "timed_out") {
|
|
1390
|
+
process.exitCode = 1;
|
|
1391
|
+
}
|
|
1392
|
+
} catch (error) {
|
|
1393
|
+
console.error(`worker auto-complete failed: ${error.message}`);
|
|
1394
|
+
process.exitCode = 1;
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
function resolveDefaultCliPath() {
|
|
1398
|
+
return path8.join(fileURLToPath(new URL(".", import.meta.url)), "cli.js");
|
|
1399
|
+
}
|
|
1400
|
+
function spawnCompletionSidecar(opts) {
|
|
1401
|
+
const cliPath = opts.cliPath ?? resolveDefaultCliPath();
|
|
1402
|
+
if (!existsSync8(cliPath)) return void 0;
|
|
1403
|
+
const logPath = path8.join(opts.workerDir, "auto-complete.log");
|
|
1404
|
+
let logFd;
|
|
1405
|
+
try {
|
|
1406
|
+
logFd = openSync3(logPath, "a");
|
|
1407
|
+
} catch {
|
|
1408
|
+
logFd = void 0;
|
|
1409
|
+
}
|
|
1410
|
+
const stdio = [
|
|
1411
|
+
"ignore",
|
|
1412
|
+
logFd ?? "ignore",
|
|
1413
|
+
logFd ?? "ignore"
|
|
1414
|
+
];
|
|
1415
|
+
const nodeExecutable = opts.nodeExecutable ?? process.execPath;
|
|
1416
|
+
const args = [
|
|
1417
|
+
cliPath,
|
|
1418
|
+
"worker",
|
|
1419
|
+
"auto-complete",
|
|
1420
|
+
"--run",
|
|
1421
|
+
opts.runId,
|
|
1422
|
+
"--name",
|
|
1423
|
+
opts.workerName
|
|
1424
|
+
];
|
|
1425
|
+
if (opts.agentOsId) args.push("--agent-os-id", opts.agentOsId);
|
|
1426
|
+
if (opts.baseUrl) args.push("--base-url", opts.baseUrl);
|
|
1427
|
+
if (opts.secret) args.push("--secret", opts.secret);
|
|
1428
|
+
try {
|
|
1429
|
+
const child = spawn3(nodeExecutable, args, {
|
|
1430
|
+
detached: true,
|
|
1431
|
+
stdio,
|
|
1432
|
+
env: process.env
|
|
1433
|
+
});
|
|
1434
|
+
if (logFd !== void 0) closeSync3(logFd);
|
|
1435
|
+
child.unref();
|
|
1436
|
+
return { pid: child.pid, logPath, cliPath };
|
|
1437
|
+
} catch {
|
|
1438
|
+
if (logFd !== void 0) {
|
|
1439
|
+
try {
|
|
1440
|
+
closeSync3(logFd);
|
|
1441
|
+
} catch {
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
return void 0;
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1085
1448
|
// src/supervisor.ts
|
|
1086
1449
|
function spawnWorkerProcess(run, opts) {
|
|
1087
1450
|
const rawName = typeof opts.name === "string" ? opts.name.trim() : "";
|
|
@@ -1092,16 +1455,16 @@ function spawnWorkerProcess(run, opts) {
|
|
|
1092
1455
|
if (run.workers?.[name]) throw new Error(`worker already exists in run ${run.id}: ${name}`);
|
|
1093
1456
|
if (!opts.task) throw new Error(`missing task text for worker ${name}`);
|
|
1094
1457
|
const { worktreesDir } = getPaths();
|
|
1095
|
-
const workerDir =
|
|
1458
|
+
const workerDir = path9.join(runDirectory(run.id), "workers", name);
|
|
1096
1459
|
mkdirSync3(workerDir, { recursive: true });
|
|
1097
|
-
const worktreePath =
|
|
1460
|
+
const worktreePath = path9.join(worktreesDir, run.id, name);
|
|
1098
1461
|
const branch = opts.branch || `agent/${run.id}/${name}`;
|
|
1099
|
-
if (
|
|
1462
|
+
if (existsSync9(worktreePath)) throw new Error(`worktree path already exists: ${worktreePath}`);
|
|
1100
1463
|
git(run.repo, ["fetch", "origin", "--prune"], { allowFailure: true });
|
|
1101
1464
|
git(run.repo, ["worktree", "add", "-b", branch, worktreePath, run.baseCommit], { throwError: true });
|
|
1102
|
-
const stdoutPath =
|
|
1103
|
-
const stderrPath =
|
|
1104
|
-
const heartbeatPath =
|
|
1465
|
+
const stdoutPath = path9.join(workerDir, "stdout.jsonl");
|
|
1466
|
+
const stderrPath = path9.join(workerDir, "stderr.log");
|
|
1467
|
+
const heartbeatPath = path9.join(workerDir, "heartbeat.jsonl");
|
|
1105
1468
|
const prompt = buildPrompt({
|
|
1106
1469
|
task: opts.task,
|
|
1107
1470
|
ownedPaths: opts.ownedPaths || [],
|
|
@@ -1151,9 +1514,20 @@ function spawnWorkerProcess(run, opts) {
|
|
|
1151
1514
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1152
1515
|
};
|
|
1153
1516
|
saveWorker(run.id, worker);
|
|
1154
|
-
run.workers = { ...run.workers || {}, [name]: { workerDir, statusPath:
|
|
1517
|
+
run.workers = { ...run.workers || {}, [name]: { workerDir, statusPath: path9.join(workerDir, "worker.json") } };
|
|
1155
1518
|
run.status = "running";
|
|
1156
1519
|
saveRun(run);
|
|
1520
|
+
if (worker.agentOsId && worker.taskId) {
|
|
1521
|
+
try {
|
|
1522
|
+
spawnCompletionSidecar({
|
|
1523
|
+
runId: run.id,
|
|
1524
|
+
workerName: name,
|
|
1525
|
+
workerDir,
|
|
1526
|
+
agentOsId: worker.agentOsId
|
|
1527
|
+
});
|
|
1528
|
+
} catch {
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1157
1531
|
return worker;
|
|
1158
1532
|
}
|
|
1159
1533
|
function startWorker(args) {
|
|
@@ -1354,7 +1728,7 @@ async function dispatchRun(args) {
|
|
|
1354
1728
|
}
|
|
1355
1729
|
|
|
1356
1730
|
// src/sweep.ts
|
|
1357
|
-
import
|
|
1731
|
+
import path10 from "node:path";
|
|
1358
1732
|
async function sweepRun(args) {
|
|
1359
1733
|
const pipeline = args.pipeline === true || args.pipeline === "true";
|
|
1360
1734
|
try {
|
|
@@ -1366,7 +1740,7 @@ async function sweepRun(args) {
|
|
|
1366
1740
|
const releasedLocalOrphans = [];
|
|
1367
1741
|
for (const name of Object.keys(run.workers || {})) {
|
|
1368
1742
|
const worker = readJson(
|
|
1369
|
-
|
|
1743
|
+
path10.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1370
1744
|
void 0
|
|
1371
1745
|
);
|
|
1372
1746
|
if (!worker || !worker.dispatched || !worker.taskId) continue;
|
|
@@ -1409,11 +1783,11 @@ async function sweepRun(args) {
|
|
|
1409
1783
|
}
|
|
1410
1784
|
|
|
1411
1785
|
// src/worktree.ts
|
|
1412
|
-
import { existsSync as
|
|
1413
|
-
import
|
|
1786
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4 } from "node:fs";
|
|
1787
|
+
import path12 from "node:path";
|
|
1414
1788
|
|
|
1415
1789
|
// src/validate.ts
|
|
1416
|
-
import
|
|
1790
|
+
import path11 from "node:path";
|
|
1417
1791
|
var RUN_ID_RE = /^[a-z0-9][a-z0-9._-]{0,127}$/i;
|
|
1418
1792
|
function validateRunId(runId) {
|
|
1419
1793
|
const trimmed = runId.trim();
|
|
@@ -1421,7 +1795,7 @@ function validateRunId(runId) {
|
|
|
1421
1795
|
return trimmed;
|
|
1422
1796
|
}
|
|
1423
1797
|
function validateRepo(repo) {
|
|
1424
|
-
const resolved =
|
|
1798
|
+
const resolved = path11.resolve(repo);
|
|
1425
1799
|
if (resolved.includes("..")) throw new Error("repo path must not contain .. segments");
|
|
1426
1800
|
return resolved;
|
|
1427
1801
|
}
|
|
@@ -1432,7 +1806,7 @@ function createRun(args) {
|
|
|
1432
1806
|
ensureGitRepo(repo);
|
|
1433
1807
|
const id = args.id ? validateRunId(String(args.id)) : timestampSlug(String(args.name || "run"));
|
|
1434
1808
|
const dir = runDirectory(id);
|
|
1435
|
-
if (
|
|
1809
|
+
if (existsSync10(dir)) failExists(`run already exists: ${id}`);
|
|
1436
1810
|
mkdirSync4(dir, { recursive: true });
|
|
1437
1811
|
const base = String(args.base || "origin/main");
|
|
1438
1812
|
const baseCommit = git(repo, ["rev-parse", base]).trim();
|
|
@@ -1446,12 +1820,12 @@ function createRun(args) {
|
|
|
1446
1820
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1447
1821
|
workers: {}
|
|
1448
1822
|
};
|
|
1449
|
-
writeJson(
|
|
1823
|
+
writeJson(path12.join(dir, "run.json"), run);
|
|
1450
1824
|
console.log(JSON.stringify({ runId: id, runDir: dir, repo, base, baseCommit }, null, 2));
|
|
1451
1825
|
}
|
|
1452
1826
|
function listRuns() {
|
|
1453
1827
|
const { runsDir } = getPaths();
|
|
1454
|
-
const rows = listRunIds(runsDir).map((id) => readJson(
|
|
1828
|
+
const rows = listRunIds(runsDir).map((id) => readJson(path12.join(runDirectory(id), "run.json"), void 0)).filter(Boolean).map((run) => ({
|
|
1455
1829
|
id: run.id,
|
|
1456
1830
|
name: run.name,
|
|
1457
1831
|
status: run.status,
|
|
@@ -1465,211 +1839,11 @@ function failExists(message) {
|
|
|
1465
1839
|
process.exit(1);
|
|
1466
1840
|
}
|
|
1467
1841
|
|
|
1468
|
-
// src/worker-ops.ts
|
|
1469
|
-
import path11 from "node:path";
|
|
1470
|
-
async function postCompletion(url, secret, body) {
|
|
1471
|
-
const res = await fetch(url, {
|
|
1472
|
-
method: "POST",
|
|
1473
|
-
headers: buildHarnessCallbackHeaders(secret),
|
|
1474
|
-
body: JSON.stringify(body)
|
|
1475
|
-
});
|
|
1476
|
-
let parsed = null;
|
|
1477
|
-
try {
|
|
1478
|
-
parsed = await res.json();
|
|
1479
|
-
} catch {
|
|
1480
|
-
parsed = null;
|
|
1481
|
-
}
|
|
1482
|
-
return { ok: res.ok, status: res.status, parsed };
|
|
1483
|
-
}
|
|
1484
|
-
function completionErrorText(parsed) {
|
|
1485
|
-
if (parsed && typeof parsed === "object") {
|
|
1486
|
-
const err = parsed.error;
|
|
1487
|
-
if (typeof err === "string" && err.trim()) return err.trim();
|
|
1488
|
-
}
|
|
1489
|
-
return void 0;
|
|
1490
|
-
}
|
|
1491
|
-
function persistCompletionBlocker(worker, reason) {
|
|
1492
|
-
const current = worker.completionBlocker;
|
|
1493
|
-
if ((current ?? void 0) === (reason ?? void 0)) return;
|
|
1494
|
-
if (reason) worker.completionBlocker = reason;
|
|
1495
|
-
else delete worker.completionBlocker;
|
|
1496
|
-
saveWorker(worker.runId, worker);
|
|
1497
|
-
}
|
|
1498
|
-
async function tryCompleteWorker(args) {
|
|
1499
|
-
const worker = loadWorker(String(args.run), String(args.name));
|
|
1500
|
-
const status = computeWorkerStatus(worker);
|
|
1501
|
-
const agentOsId = (args.agentOsId ? String(args.agentOsId) : worker.agentOsId) || "";
|
|
1502
|
-
const taskId = (args.taskId ? String(args.taskId) : worker.taskId) || null;
|
|
1503
|
-
if (!agentOsId) {
|
|
1504
|
-
return { ok: false, reason: "missing agentOsId" };
|
|
1505
|
-
}
|
|
1506
|
-
if (!isFinishedWorkerStatus(status)) {
|
|
1507
|
-
return { ok: true, skipped: true, reason: "worker-not-finished" };
|
|
1508
|
-
}
|
|
1509
|
-
const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
|
|
1510
|
-
const explicitSecret = args.secret ? String(args.secret) : void 0;
|
|
1511
|
-
let secret = await resolveCallbackSecretWithMint(explicitSecret, agentOsId, { baseUrl: base });
|
|
1512
|
-
const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/harness/completion`;
|
|
1513
|
-
const body = {
|
|
1514
|
-
source: "openclaw-harness",
|
|
1515
|
-
agentOsId,
|
|
1516
|
-
runId: worker.runId,
|
|
1517
|
-
workerName: worker.name,
|
|
1518
|
-
taskId,
|
|
1519
|
-
startedAt: worker.startedAt,
|
|
1520
|
-
finishedAt: status.lastActivityAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1521
|
-
status
|
|
1522
|
-
};
|
|
1523
|
-
let result = await postCompletion(url, secret, body);
|
|
1524
|
-
if ((result.status === 401 || result.status === 403) && !explicitSecret) {
|
|
1525
|
-
const refreshed = await refreshRunnerToken(agentOsId, { baseUrl: base });
|
|
1526
|
-
if (refreshed && refreshed !== secret) {
|
|
1527
|
-
secret = refreshed;
|
|
1528
|
-
result = await postCompletion(url, secret, body);
|
|
1529
|
-
}
|
|
1530
|
-
}
|
|
1531
|
-
if (result.ok) {
|
|
1532
|
-
persistCompletionBlocker(worker, void 0);
|
|
1533
|
-
return { ok: true, httpStatus: result.status, response: result.parsed };
|
|
1534
|
-
}
|
|
1535
|
-
const authRejected = result.status === 401 || result.status === 403;
|
|
1536
|
-
const detail = completionErrorText(result.parsed) ?? (authRejected ? "runner token unauthorized" : "non-2xx response");
|
|
1537
|
-
const reason = authRejected ? `completion replay rejected (${result.status}): ${detail}` : `completion replay failed (${result.status}): ${detail}`;
|
|
1538
|
-
persistCompletionBlocker(worker, reason);
|
|
1539
|
-
return { ok: false, httpStatus: result.status, response: result.parsed, completionBlocked: true };
|
|
1540
|
-
}
|
|
1541
|
-
async function completeWorker(args) {
|
|
1542
|
-
try {
|
|
1543
|
-
const worker = loadWorker(String(args.run), String(args.name));
|
|
1544
|
-
const status = computeWorkerStatus(worker);
|
|
1545
|
-
const agentOsId = (args.agentOsId ? String(args.agentOsId) : worker.agentOsId) || "";
|
|
1546
|
-
const taskId = (args.taskId ? String(args.taskId) : worker.taskId) || null;
|
|
1547
|
-
if (!agentOsId) {
|
|
1548
|
-
console.error("worker complete requires --agent-os-id (or an agentOsId persisted at worker start)");
|
|
1549
|
-
process.exit(1);
|
|
1550
|
-
}
|
|
1551
|
-
if (!isFinishedWorkerStatus(status)) {
|
|
1552
|
-
console.log(
|
|
1553
|
-
JSON.stringify(
|
|
1554
|
-
{
|
|
1555
|
-
worker: worker.name,
|
|
1556
|
-
runId: worker.runId,
|
|
1557
|
-
status: "skipped",
|
|
1558
|
-
reason: "worker-not-finished",
|
|
1559
|
-
workerStatus: status.status,
|
|
1560
|
-
alive: status.alive
|
|
1561
|
-
},
|
|
1562
|
-
null,
|
|
1563
|
-
2
|
|
1564
|
-
)
|
|
1565
|
-
);
|
|
1566
|
-
return;
|
|
1567
|
-
}
|
|
1568
|
-
const result = await tryCompleteWorker(args);
|
|
1569
|
-
console.log(
|
|
1570
|
-
JSON.stringify(
|
|
1571
|
-
{
|
|
1572
|
-
worker: worker.name,
|
|
1573
|
-
runId: worker.runId,
|
|
1574
|
-
agentOsId,
|
|
1575
|
-
taskId,
|
|
1576
|
-
httpStatus: result.httpStatus,
|
|
1577
|
-
response: result.response
|
|
1578
|
-
},
|
|
1579
|
-
null,
|
|
1580
|
-
2
|
|
1581
|
-
)
|
|
1582
|
-
);
|
|
1583
|
-
if (!result.ok) process.exit(1);
|
|
1584
|
-
} catch (error) {
|
|
1585
|
-
console.error(`worker complete failed: ${error.message}`);
|
|
1586
|
-
process.exit(1);
|
|
1587
|
-
}
|
|
1588
|
-
}
|
|
1589
|
-
function workerStatus(args) {
|
|
1590
|
-
const worker = loadWorker(String(args.run), String(args.name));
|
|
1591
|
-
const status = computeWorkerStatus(worker);
|
|
1592
|
-
writeJson(path11.join(worker.workerDir, "last-status.json"), status);
|
|
1593
|
-
console.log(JSON.stringify(status, null, 2));
|
|
1594
|
-
}
|
|
1595
|
-
function runStatus(args) {
|
|
1596
|
-
const run = loadRun(String(args.run));
|
|
1597
|
-
const names = Object.keys(run.workers || {});
|
|
1598
|
-
const workers = names.map((name) => {
|
|
1599
|
-
const worker = readJson(
|
|
1600
|
-
path11.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1601
|
-
void 0
|
|
1602
|
-
);
|
|
1603
|
-
if (!worker) {
|
|
1604
|
-
return { worker: name, status: "missing", attention: "needs_attention", attentionReason: "worker.json not found" };
|
|
1605
|
-
}
|
|
1606
|
-
const status = computeWorkerStatus(worker, { base: run.base });
|
|
1607
|
-
const rawBlocker = worker.completionBlocker;
|
|
1608
|
-
const completionBlocker = typeof rawBlocker === "string" && rawBlocker ? rawBlocker : void 0;
|
|
1609
|
-
return {
|
|
1610
|
-
worker: status.worker,
|
|
1611
|
-
status: completionBlocker ? "blocked" : status.status,
|
|
1612
|
-
attention: completionBlocker ? "blocked" : status.attention.state,
|
|
1613
|
-
attentionReason: completionBlocker ?? status.attention.reason,
|
|
1614
|
-
pid: status.pid,
|
|
1615
|
-
alive: status.alive,
|
|
1616
|
-
currentTool: status.currentTool,
|
|
1617
|
-
lastActivityAt: status.lastActivityAt,
|
|
1618
|
-
lastHeartbeatPhase: status.lastHeartbeatPhase,
|
|
1619
|
-
lastHeartbeatSummary: status.lastHeartbeatSummary,
|
|
1620
|
-
heartbeatBlocker: status.heartbeatBlocker,
|
|
1621
|
-
changedFileCount: status.changedFiles.length,
|
|
1622
|
-
branch: status.branch,
|
|
1623
|
-
ancestry: status.gitAncestry.relation,
|
|
1624
|
-
ancestryChecked: status.gitAncestry.checked
|
|
1625
|
-
};
|
|
1626
|
-
});
|
|
1627
|
-
const board = {
|
|
1628
|
-
runId: run.id,
|
|
1629
|
-
name: run.name,
|
|
1630
|
-
status: deriveRunStatus(run.status, workers),
|
|
1631
|
-
repo: run.repo,
|
|
1632
|
-
workerCount: workers.length,
|
|
1633
|
-
needsAttention: workers.filter((w) => w.attention && w.attention !== "ok" && w.attention !== "done").map((w) => w.worker),
|
|
1634
|
-
workers
|
|
1635
|
-
};
|
|
1636
|
-
writeJson(path11.join(runDirectory(run.id), "last-board.json"), board);
|
|
1637
|
-
console.log(JSON.stringify(board, null, 2));
|
|
1638
|
-
}
|
|
1639
|
-
function tailWorker(args) {
|
|
1640
|
-
const worker = loadWorker(String(args.run), String(args.name));
|
|
1641
|
-
const raw = tailFile(worker.stdoutPath, Number(args.lines || 40));
|
|
1642
|
-
if (args.raw === true || args.raw === "true") {
|
|
1643
|
-
process.stdout.write(raw);
|
|
1644
|
-
return;
|
|
1645
|
-
}
|
|
1646
|
-
for (const line of raw.split("\n").filter(Boolean)) {
|
|
1647
|
-
const event = safeJson(line);
|
|
1648
|
-
const summary = event ? summarizeEvent(event) : line;
|
|
1649
|
-
if (summary) console.log(summary);
|
|
1650
|
-
}
|
|
1651
|
-
}
|
|
1652
|
-
function stopWorker(args) {
|
|
1653
|
-
const worker = loadWorker(String(args.run), String(args.name));
|
|
1654
|
-
if (!isPidAlive(worker.pid)) {
|
|
1655
|
-
console.log(JSON.stringify({ worker: worker.name, pid: worker.pid, status: "not_running" }, null, 2));
|
|
1656
|
-
return;
|
|
1657
|
-
}
|
|
1658
|
-
killWorkerProcess(worker.pid, "SIGTERM");
|
|
1659
|
-
sleepMs(1500);
|
|
1660
|
-
if (isPidAlive(worker.pid)) {
|
|
1661
|
-
killWorkerProcess(worker.pid, "SIGKILL");
|
|
1662
|
-
console.log(JSON.stringify({ worker: worker.name, pid: worker.pid, status: "sigkill_sent" }, null, 2));
|
|
1663
|
-
return;
|
|
1664
|
-
}
|
|
1665
|
-
console.log(JSON.stringify({ worker: worker.name, pid: worker.pid, status: "stopped" }, null, 2));
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
1842
|
// src/pipeline-tick.ts
|
|
1669
|
-
import
|
|
1843
|
+
import path15 from "node:path";
|
|
1670
1844
|
|
|
1671
1845
|
// src/finalize.ts
|
|
1672
|
-
import
|
|
1846
|
+
import path13 from "node:path";
|
|
1673
1847
|
var ACTIVE_RUN_STATUSES = /* @__PURE__ */ new Set(["running", "dispatching", "pending", "queued"]);
|
|
1674
1848
|
function terminalStatusFor(run) {
|
|
1675
1849
|
const names = Object.keys(run.workers || {});
|
|
@@ -1679,7 +1853,7 @@ function terminalStatusFor(run) {
|
|
|
1679
1853
|
let anyCompletionBlocked = false;
|
|
1680
1854
|
for (const name of names) {
|
|
1681
1855
|
const worker = readJson(
|
|
1682
|
-
|
|
1856
|
+
path13.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1683
1857
|
void 0
|
|
1684
1858
|
);
|
|
1685
1859
|
if (!worker) continue;
|
|
@@ -1712,7 +1886,7 @@ function finalizeStaleRuns() {
|
|
|
1712
1886
|
}
|
|
1713
1887
|
|
|
1714
1888
|
// src/plan-progress-daemon-sync.ts
|
|
1715
|
-
import
|
|
1889
|
+
import path14 from "node:path";
|
|
1716
1890
|
|
|
1717
1891
|
// src/plan-progress-sync.ts
|
|
1718
1892
|
async function syncPlanProgress(args) {
|
|
@@ -1736,7 +1910,7 @@ async function syncActiveWorkerPlanProgress(runId, args) {
|
|
|
1736
1910
|
const outcomes = [];
|
|
1737
1911
|
for (const name of Object.keys(run.workers || {})) {
|
|
1738
1912
|
const worker = readJson(
|
|
1739
|
-
|
|
1913
|
+
path14.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1740
1914
|
void 0
|
|
1741
1915
|
);
|
|
1742
1916
|
if (!worker?.dispatched || !worker.taskId) continue;
|
|
@@ -1790,7 +1964,7 @@ async function completeFinishedWorkers(runId, args) {
|
|
|
1790
1964
|
const outcomes = [];
|
|
1791
1965
|
for (const name of Object.keys(run.workers || {})) {
|
|
1792
1966
|
const worker = readJson(
|
|
1793
|
-
|
|
1967
|
+
path15.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1794
1968
|
void 0
|
|
1795
1969
|
);
|
|
1796
1970
|
if (!worker?.taskId) continue;
|
|
@@ -2033,6 +2207,7 @@ function usage(code = 0) {
|
|
|
2033
2207
|
" kynver worker tail --run RUN_ID --name worker [--lines 40] [--raw]",
|
|
2034
2208
|
" kynver worker stop --run RUN_ID --name worker",
|
|
2035
2209
|
" kynver worker complete --run RUN_ID --name worker [--agent-os-id AOS_ID] [--task-id TASK_ID] [--base-url URL] [--secret SECRET]",
|
|
2210
|
+
" kynver worker auto-complete --run RUN_ID --name worker [--agent-os-id AOS_ID] [--poll-ms 5000] [--max-total-ms 21600000] [--complete-attempts 3] [--complete-backoff-ms 5000] [--base-url URL] [--secret SECRET]",
|
|
2036
2211
|
" kynver plan progress --plan PLAN_ID --row ROW_KEY --role ROLE --status STATUS [--task TASK_ID] [--note NOTE] [--evidence type:value] [--agent-os-id AOS_ID]",
|
|
2037
2212
|
" kynver plan verify --plan PLAN_ID [--worktree PATH] [--task TASK_ID] [--human-override]"
|
|
2038
2213
|
].join("\n")
|
|
@@ -2071,9 +2246,10 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2071
2246
|
if (scope === "worker" && action === "tail") return tailWorker(args);
|
|
2072
2247
|
if (scope === "worker" && action === "stop") return stopWorker(args);
|
|
2073
2248
|
if (scope === "worker" && action === "complete") return void await completeWorker(args);
|
|
2249
|
+
if (scope === "worker" && action === "auto-complete") return void await autoCompleteWorkerCli(args);
|
|
2074
2250
|
unknownCommand(scope, action);
|
|
2075
2251
|
}
|
|
2076
|
-
var isCliEntry = process.argv[1] && realpathSync.native(process.argv[1]) === realpathSync.native(
|
|
2252
|
+
var isCliEntry = process.argv[1] && realpathSync.native(process.argv[1]) === realpathSync.native(fileURLToPath2(import.meta.url));
|
|
2077
2253
|
if (isCliEntry) {
|
|
2078
2254
|
void main().catch((error) => {
|
|
2079
2255
|
console.error(error);
|