@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/index.js
CHANGED
|
@@ -393,12 +393,12 @@ var DEFAULT_CRITICAL_FREE_BYTES = 15 * 1024 * 1024 * 1024;
|
|
|
393
393
|
var DEFAULT_MAX_USED_PERCENT = 80;
|
|
394
394
|
var DEFAULT_HARD_MAX_USED_PERCENT = 90;
|
|
395
395
|
function observeRunnerDiskGate(input = {}) {
|
|
396
|
-
const
|
|
396
|
+
const path16 = input.diskPath?.trim() || "/";
|
|
397
397
|
const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
|
|
398
398
|
const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
|
|
399
399
|
const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
|
|
400
400
|
const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
|
|
401
|
-
const stats = statfsSync(
|
|
401
|
+
const stats = statfsSync(path16);
|
|
402
402
|
const freeBytes = Number(stats.bavail) * Number(stats.bsize);
|
|
403
403
|
const totalBytes = Number(stats.blocks) * Number(stats.bsize);
|
|
404
404
|
const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
|
|
@@ -418,7 +418,7 @@ function observeRunnerDiskGate(input = {}) {
|
|
|
418
418
|
}
|
|
419
419
|
return {
|
|
420
420
|
ok,
|
|
421
|
-
path:
|
|
421
|
+
path: path16,
|
|
422
422
|
freeBytes,
|
|
423
423
|
totalBytes,
|
|
424
424
|
usedPercent,
|
|
@@ -908,8 +908,8 @@ function observeRunnerResourceGate(input) {
|
|
|
908
908
|
}
|
|
909
909
|
|
|
910
910
|
// src/supervisor.ts
|
|
911
|
-
import { existsSync as
|
|
912
|
-
import
|
|
911
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3 } from "node:fs";
|
|
912
|
+
import path9 from "node:path";
|
|
913
913
|
|
|
914
914
|
// src/prompt.ts
|
|
915
915
|
function buildPrompt(input) {
|
|
@@ -1083,6 +1083,369 @@ function resolveWorkerProvider(name) {
|
|
|
1083
1083
|
return provider;
|
|
1084
1084
|
}
|
|
1085
1085
|
|
|
1086
|
+
// src/auto-complete.ts
|
|
1087
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
1088
|
+
import { existsSync as existsSync8, openSync as openSync3, closeSync as closeSync3 } from "node:fs";
|
|
1089
|
+
import path8 from "node:path";
|
|
1090
|
+
import { fileURLToPath } from "node:url";
|
|
1091
|
+
|
|
1092
|
+
// src/worker-ops.ts
|
|
1093
|
+
import path7 from "node:path";
|
|
1094
|
+
async function postCompletion(url, secret, body) {
|
|
1095
|
+
const res = await fetch(url, {
|
|
1096
|
+
method: "POST",
|
|
1097
|
+
headers: buildHarnessCallbackHeaders(secret),
|
|
1098
|
+
body: JSON.stringify(body)
|
|
1099
|
+
});
|
|
1100
|
+
let parsed = null;
|
|
1101
|
+
try {
|
|
1102
|
+
parsed = await res.json();
|
|
1103
|
+
} catch {
|
|
1104
|
+
parsed = null;
|
|
1105
|
+
}
|
|
1106
|
+
return { ok: res.ok, status: res.status, parsed };
|
|
1107
|
+
}
|
|
1108
|
+
function completionErrorText(parsed) {
|
|
1109
|
+
if (parsed && typeof parsed === "object") {
|
|
1110
|
+
const err = parsed.error;
|
|
1111
|
+
if (typeof err === "string" && err.trim()) return err.trim();
|
|
1112
|
+
}
|
|
1113
|
+
return void 0;
|
|
1114
|
+
}
|
|
1115
|
+
function persistCompletionBlocker(worker, reason) {
|
|
1116
|
+
const current = worker.completionBlocker;
|
|
1117
|
+
if ((current ?? void 0) === (reason ?? void 0)) return;
|
|
1118
|
+
if (reason) worker.completionBlocker = reason;
|
|
1119
|
+
else delete worker.completionBlocker;
|
|
1120
|
+
saveWorker(worker.runId, worker);
|
|
1121
|
+
}
|
|
1122
|
+
async function tryCompleteWorker(args) {
|
|
1123
|
+
const worker = loadWorker(String(args.run), String(args.name));
|
|
1124
|
+
const status = computeWorkerStatus(worker);
|
|
1125
|
+
const agentOsId = (args.agentOsId ? String(args.agentOsId) : worker.agentOsId) || "";
|
|
1126
|
+
const taskId = (args.taskId ? String(args.taskId) : worker.taskId) || null;
|
|
1127
|
+
if (!agentOsId) {
|
|
1128
|
+
return { ok: false, reason: "missing agentOsId" };
|
|
1129
|
+
}
|
|
1130
|
+
if (!isFinishedWorkerStatus(status)) {
|
|
1131
|
+
return { ok: true, skipped: true, reason: "worker-not-finished" };
|
|
1132
|
+
}
|
|
1133
|
+
const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
|
|
1134
|
+
const explicitSecret = args.secret ? String(args.secret) : void 0;
|
|
1135
|
+
let secret = await resolveCallbackSecretWithMint(explicitSecret, agentOsId, { baseUrl: base });
|
|
1136
|
+
const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/harness/completion`;
|
|
1137
|
+
const body = {
|
|
1138
|
+
source: "openclaw-harness",
|
|
1139
|
+
agentOsId,
|
|
1140
|
+
runId: worker.runId,
|
|
1141
|
+
workerName: worker.name,
|
|
1142
|
+
taskId,
|
|
1143
|
+
startedAt: worker.startedAt,
|
|
1144
|
+
finishedAt: status.lastActivityAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1145
|
+
status
|
|
1146
|
+
};
|
|
1147
|
+
let result = await postCompletion(url, secret, body);
|
|
1148
|
+
if ((result.status === 401 || result.status === 403) && !explicitSecret) {
|
|
1149
|
+
const refreshed = await refreshRunnerToken(agentOsId, { baseUrl: base });
|
|
1150
|
+
if (refreshed && refreshed !== secret) {
|
|
1151
|
+
secret = refreshed;
|
|
1152
|
+
result = await postCompletion(url, secret, body);
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
if (result.ok) {
|
|
1156
|
+
persistCompletionBlocker(worker, void 0);
|
|
1157
|
+
return { ok: true, httpStatus: result.status, response: result.parsed };
|
|
1158
|
+
}
|
|
1159
|
+
const authRejected = result.status === 401 || result.status === 403;
|
|
1160
|
+
const detail = completionErrorText(result.parsed) ?? (authRejected ? "runner token unauthorized" : "non-2xx response");
|
|
1161
|
+
const reason = authRejected ? `completion replay rejected (${result.status}): ${detail}` : `completion replay failed (${result.status}): ${detail}`;
|
|
1162
|
+
persistCompletionBlocker(worker, reason);
|
|
1163
|
+
return { ok: false, httpStatus: result.status, response: result.parsed, completionBlocked: true };
|
|
1164
|
+
}
|
|
1165
|
+
async function completeWorker(args) {
|
|
1166
|
+
try {
|
|
1167
|
+
const worker = loadWorker(String(args.run), String(args.name));
|
|
1168
|
+
const status = computeWorkerStatus(worker);
|
|
1169
|
+
const agentOsId = (args.agentOsId ? String(args.agentOsId) : worker.agentOsId) || "";
|
|
1170
|
+
const taskId = (args.taskId ? String(args.taskId) : worker.taskId) || null;
|
|
1171
|
+
if (!agentOsId) {
|
|
1172
|
+
console.error("worker complete requires --agent-os-id (or an agentOsId persisted at worker start)");
|
|
1173
|
+
process.exit(1);
|
|
1174
|
+
}
|
|
1175
|
+
if (!isFinishedWorkerStatus(status)) {
|
|
1176
|
+
console.log(
|
|
1177
|
+
JSON.stringify(
|
|
1178
|
+
{
|
|
1179
|
+
worker: worker.name,
|
|
1180
|
+
runId: worker.runId,
|
|
1181
|
+
status: "skipped",
|
|
1182
|
+
reason: "worker-not-finished",
|
|
1183
|
+
workerStatus: status.status,
|
|
1184
|
+
alive: status.alive
|
|
1185
|
+
},
|
|
1186
|
+
null,
|
|
1187
|
+
2
|
|
1188
|
+
)
|
|
1189
|
+
);
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
const result = await tryCompleteWorker(args);
|
|
1193
|
+
console.log(
|
|
1194
|
+
JSON.stringify(
|
|
1195
|
+
{
|
|
1196
|
+
worker: worker.name,
|
|
1197
|
+
runId: worker.runId,
|
|
1198
|
+
agentOsId,
|
|
1199
|
+
taskId,
|
|
1200
|
+
httpStatus: result.httpStatus,
|
|
1201
|
+
response: result.response
|
|
1202
|
+
},
|
|
1203
|
+
null,
|
|
1204
|
+
2
|
|
1205
|
+
)
|
|
1206
|
+
);
|
|
1207
|
+
if (!result.ok) process.exit(1);
|
|
1208
|
+
} catch (error) {
|
|
1209
|
+
console.error(`worker complete failed: ${error.message}`);
|
|
1210
|
+
process.exit(1);
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
function workerStatus(args) {
|
|
1214
|
+
const worker = loadWorker(String(args.run), String(args.name));
|
|
1215
|
+
const status = computeWorkerStatus(worker);
|
|
1216
|
+
writeJson(path7.join(worker.workerDir, "last-status.json"), status);
|
|
1217
|
+
console.log(JSON.stringify(status, null, 2));
|
|
1218
|
+
}
|
|
1219
|
+
function runStatus(args) {
|
|
1220
|
+
const run = loadRun(String(args.run));
|
|
1221
|
+
const names = Object.keys(run.workers || {});
|
|
1222
|
+
const workers = names.map((name) => {
|
|
1223
|
+
const worker = readJson(
|
|
1224
|
+
path7.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1225
|
+
void 0
|
|
1226
|
+
);
|
|
1227
|
+
if (!worker) {
|
|
1228
|
+
return { worker: name, status: "missing", attention: "needs_attention", attentionReason: "worker.json not found" };
|
|
1229
|
+
}
|
|
1230
|
+
const status = computeWorkerStatus(worker, { base: run.base });
|
|
1231
|
+
const rawBlocker = worker.completionBlocker;
|
|
1232
|
+
const completionBlocker = typeof rawBlocker === "string" && rawBlocker ? rawBlocker : void 0;
|
|
1233
|
+
return {
|
|
1234
|
+
worker: status.worker,
|
|
1235
|
+
status: completionBlocker ? "blocked" : status.status,
|
|
1236
|
+
attention: completionBlocker ? "blocked" : status.attention.state,
|
|
1237
|
+
attentionReason: completionBlocker ?? status.attention.reason,
|
|
1238
|
+
pid: status.pid,
|
|
1239
|
+
alive: status.alive,
|
|
1240
|
+
currentTool: status.currentTool,
|
|
1241
|
+
lastActivityAt: status.lastActivityAt,
|
|
1242
|
+
lastHeartbeatPhase: status.lastHeartbeatPhase,
|
|
1243
|
+
lastHeartbeatSummary: status.lastHeartbeatSummary,
|
|
1244
|
+
heartbeatBlocker: status.heartbeatBlocker,
|
|
1245
|
+
changedFileCount: status.changedFiles.length,
|
|
1246
|
+
branch: status.branch,
|
|
1247
|
+
ancestry: status.gitAncestry.relation,
|
|
1248
|
+
ancestryChecked: status.gitAncestry.checked
|
|
1249
|
+
};
|
|
1250
|
+
});
|
|
1251
|
+
const board = {
|
|
1252
|
+
runId: run.id,
|
|
1253
|
+
name: run.name,
|
|
1254
|
+
status: deriveRunStatus(run.status, workers),
|
|
1255
|
+
repo: run.repo,
|
|
1256
|
+
workerCount: workers.length,
|
|
1257
|
+
needsAttention: workers.filter((w) => w.attention && w.attention !== "ok" && w.attention !== "done").map((w) => w.worker),
|
|
1258
|
+
workers
|
|
1259
|
+
};
|
|
1260
|
+
writeJson(path7.join(runDirectory(run.id), "last-board.json"), board);
|
|
1261
|
+
console.log(JSON.stringify(board, null, 2));
|
|
1262
|
+
}
|
|
1263
|
+
function tailWorker(args) {
|
|
1264
|
+
const worker = loadWorker(String(args.run), String(args.name));
|
|
1265
|
+
const raw = tailFile(worker.stdoutPath, Number(args.lines || 40));
|
|
1266
|
+
if (args.raw === true || args.raw === "true") {
|
|
1267
|
+
process.stdout.write(raw);
|
|
1268
|
+
return;
|
|
1269
|
+
}
|
|
1270
|
+
for (const line of raw.split("\n").filter(Boolean)) {
|
|
1271
|
+
const event = safeJson(line);
|
|
1272
|
+
const summary = event ? summarizeEvent(event) : line;
|
|
1273
|
+
if (summary) console.log(summary);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
function stopWorker(args) {
|
|
1277
|
+
const worker = loadWorker(String(args.run), String(args.name));
|
|
1278
|
+
if (!isPidAlive(worker.pid)) {
|
|
1279
|
+
console.log(JSON.stringify({ worker: worker.name, pid: worker.pid, status: "not_running" }, null, 2));
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
killWorkerProcess(worker.pid, "SIGTERM");
|
|
1283
|
+
sleepMs(1500);
|
|
1284
|
+
if (isPidAlive(worker.pid)) {
|
|
1285
|
+
killWorkerProcess(worker.pid, "SIGKILL");
|
|
1286
|
+
console.log(JSON.stringify({ worker: worker.name, pid: worker.pid, status: "sigkill_sent" }, null, 2));
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
1289
|
+
console.log(JSON.stringify({ worker: worker.name, pid: worker.pid, status: "stopped" }, null, 2));
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
// src/auto-complete.ts
|
|
1293
|
+
var DEFAULT_POLL_MS = 5e3;
|
|
1294
|
+
var DEFAULT_MAX_TOTAL_MS = 6 * 60 * 60 * 1e3;
|
|
1295
|
+
var DEFAULT_COMPLETE_ATTEMPTS = 3;
|
|
1296
|
+
var DEFAULT_COMPLETE_BACKOFF_MS = 5e3;
|
|
1297
|
+
function readArgs(raw) {
|
|
1298
|
+
return {
|
|
1299
|
+
run: String(raw.run || ""),
|
|
1300
|
+
name: String(raw.name || ""),
|
|
1301
|
+
agentOsId: raw.agentOsId ? String(raw.agentOsId) : void 0,
|
|
1302
|
+
pollMs: Number(raw.pollMs) > 0 ? Math.floor(Number(raw.pollMs)) : void 0,
|
|
1303
|
+
maxTotalMs: Number(raw.maxTotalMs) > 0 ? Math.floor(Number(raw.maxTotalMs)) : void 0,
|
|
1304
|
+
completeAttempts: Number(raw.completeAttempts) > 0 ? Math.floor(Number(raw.completeAttempts)) : void 0,
|
|
1305
|
+
completeBackoffMs: Number(raw.completeBackoffMs) > 0 ? Math.floor(Number(raw.completeBackoffMs)) : void 0,
|
|
1306
|
+
baseUrl: raw.baseUrl ? String(raw.baseUrl) : void 0,
|
|
1307
|
+
secret: raw.secret ? String(raw.secret) : void 0
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
async function autoCompleteWorker(raw) {
|
|
1311
|
+
const args = readArgs(raw);
|
|
1312
|
+
const pollMs = args.pollMs ?? DEFAULT_POLL_MS;
|
|
1313
|
+
const maxTotalMs = args.maxTotalMs ?? DEFAULT_MAX_TOTAL_MS;
|
|
1314
|
+
const completeAttempts = args.completeAttempts ?? DEFAULT_COMPLETE_ATTEMPTS;
|
|
1315
|
+
const completeBackoffMs = args.completeBackoffMs ?? DEFAULT_COMPLETE_BACKOFF_MS;
|
|
1316
|
+
const worker = loadWorker(args.run, args.name);
|
|
1317
|
+
if (!worker.agentOsId || !worker.taskId) {
|
|
1318
|
+
return {
|
|
1319
|
+
worker: worker.name,
|
|
1320
|
+
runId: worker.runId,
|
|
1321
|
+
outcome: "missing_link",
|
|
1322
|
+
attempts: 0,
|
|
1323
|
+
reason: "worker has no agentOsId/taskId \u2014 nothing to attribute completion to"
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
const startMs = Date.now();
|
|
1327
|
+
while (true) {
|
|
1328
|
+
const status = computeWorkerStatus(worker);
|
|
1329
|
+
if (isFinishedWorkerStatus(status)) break;
|
|
1330
|
+
if (!isPidAlive(worker.pid)) break;
|
|
1331
|
+
if (Date.now() - startMs > maxTotalMs) {
|
|
1332
|
+
return {
|
|
1333
|
+
worker: worker.name,
|
|
1334
|
+
runId: worker.runId,
|
|
1335
|
+
outcome: "timed_out",
|
|
1336
|
+
attempts: 0,
|
|
1337
|
+
reason: `worker did not finish within ${maxTotalMs}ms`
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
sleepMs(pollMs);
|
|
1341
|
+
}
|
|
1342
|
+
let lastHttpStatus;
|
|
1343
|
+
let lastReason;
|
|
1344
|
+
for (let attempt = 1; attempt <= completeAttempts; attempt++) {
|
|
1345
|
+
const result = await tryCompleteWorker({
|
|
1346
|
+
run: args.run,
|
|
1347
|
+
name: args.name,
|
|
1348
|
+
...args.agentOsId ? { agentOsId: args.agentOsId } : {},
|
|
1349
|
+
...args.baseUrl ? { baseUrl: args.baseUrl } : {},
|
|
1350
|
+
...args.secret ? { secret: args.secret } : {}
|
|
1351
|
+
});
|
|
1352
|
+
lastHttpStatus = result.httpStatus;
|
|
1353
|
+
if (result.ok) {
|
|
1354
|
+
return {
|
|
1355
|
+
worker: worker.name,
|
|
1356
|
+
runId: worker.runId,
|
|
1357
|
+
outcome: "completed",
|
|
1358
|
+
httpStatus: result.httpStatus,
|
|
1359
|
+
attempts: attempt
|
|
1360
|
+
};
|
|
1361
|
+
}
|
|
1362
|
+
const authRejected = result.httpStatus === 401 || result.httpStatus === 403;
|
|
1363
|
+
if (authRejected) {
|
|
1364
|
+
lastReason = typeof result.reason === "string" ? result.reason : "completion replay refused";
|
|
1365
|
+
return {
|
|
1366
|
+
worker: worker.name,
|
|
1367
|
+
runId: worker.runId,
|
|
1368
|
+
outcome: "blocked",
|
|
1369
|
+
httpStatus: result.httpStatus,
|
|
1370
|
+
attempts: attempt,
|
|
1371
|
+
reason: lastReason
|
|
1372
|
+
};
|
|
1373
|
+
}
|
|
1374
|
+
lastReason = typeof result.reason === "string" ? result.reason : "transient failure";
|
|
1375
|
+
if (attempt < completeAttempts) sleepMs(completeBackoffMs);
|
|
1376
|
+
}
|
|
1377
|
+
return {
|
|
1378
|
+
worker: worker.name,
|
|
1379
|
+
runId: worker.runId,
|
|
1380
|
+
outcome: "blocked",
|
|
1381
|
+
httpStatus: lastHttpStatus,
|
|
1382
|
+
attempts: completeAttempts,
|
|
1383
|
+
reason: lastReason ?? "completion failed after retries"
|
|
1384
|
+
};
|
|
1385
|
+
}
|
|
1386
|
+
async function autoCompleteWorkerCli(raw) {
|
|
1387
|
+
try {
|
|
1388
|
+
const outcome = await autoCompleteWorker(raw);
|
|
1389
|
+
console.log(JSON.stringify(outcome, null, 2));
|
|
1390
|
+
if (outcome.outcome === "missing_link" || outcome.outcome === "timed_out") {
|
|
1391
|
+
process.exitCode = 1;
|
|
1392
|
+
}
|
|
1393
|
+
} catch (error) {
|
|
1394
|
+
console.error(`worker auto-complete failed: ${error.message}`);
|
|
1395
|
+
process.exitCode = 1;
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
function resolveDefaultCliPath() {
|
|
1399
|
+
return path8.join(fileURLToPath(new URL(".", import.meta.url)), "cli.js");
|
|
1400
|
+
}
|
|
1401
|
+
function spawnCompletionSidecar(opts) {
|
|
1402
|
+
const cliPath = opts.cliPath ?? resolveDefaultCliPath();
|
|
1403
|
+
if (!existsSync8(cliPath)) return void 0;
|
|
1404
|
+
const logPath = path8.join(opts.workerDir, "auto-complete.log");
|
|
1405
|
+
let logFd;
|
|
1406
|
+
try {
|
|
1407
|
+
logFd = openSync3(logPath, "a");
|
|
1408
|
+
} catch {
|
|
1409
|
+
logFd = void 0;
|
|
1410
|
+
}
|
|
1411
|
+
const stdio = [
|
|
1412
|
+
"ignore",
|
|
1413
|
+
logFd ?? "ignore",
|
|
1414
|
+
logFd ?? "ignore"
|
|
1415
|
+
];
|
|
1416
|
+
const nodeExecutable = opts.nodeExecutable ?? process.execPath;
|
|
1417
|
+
const args = [
|
|
1418
|
+
cliPath,
|
|
1419
|
+
"worker",
|
|
1420
|
+
"auto-complete",
|
|
1421
|
+
"--run",
|
|
1422
|
+
opts.runId,
|
|
1423
|
+
"--name",
|
|
1424
|
+
opts.workerName
|
|
1425
|
+
];
|
|
1426
|
+
if (opts.agentOsId) args.push("--agent-os-id", opts.agentOsId);
|
|
1427
|
+
if (opts.baseUrl) args.push("--base-url", opts.baseUrl);
|
|
1428
|
+
if (opts.secret) args.push("--secret", opts.secret);
|
|
1429
|
+
try {
|
|
1430
|
+
const child = spawn3(nodeExecutable, args, {
|
|
1431
|
+
detached: true,
|
|
1432
|
+
stdio,
|
|
1433
|
+
env: process.env
|
|
1434
|
+
});
|
|
1435
|
+
if (logFd !== void 0) closeSync3(logFd);
|
|
1436
|
+
child.unref();
|
|
1437
|
+
return { pid: child.pid, logPath, cliPath };
|
|
1438
|
+
} catch {
|
|
1439
|
+
if (logFd !== void 0) {
|
|
1440
|
+
try {
|
|
1441
|
+
closeSync3(logFd);
|
|
1442
|
+
} catch {
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
return void 0;
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1086
1449
|
// src/supervisor.ts
|
|
1087
1450
|
function spawnWorkerProcess(run, opts) {
|
|
1088
1451
|
const rawName = typeof opts.name === "string" ? opts.name.trim() : "";
|
|
@@ -1093,16 +1456,16 @@ function spawnWorkerProcess(run, opts) {
|
|
|
1093
1456
|
if (run.workers?.[name]) throw new Error(`worker already exists in run ${run.id}: ${name}`);
|
|
1094
1457
|
if (!opts.task) throw new Error(`missing task text for worker ${name}`);
|
|
1095
1458
|
const { worktreesDir } = getPaths();
|
|
1096
|
-
const workerDir =
|
|
1459
|
+
const workerDir = path9.join(runDirectory(run.id), "workers", name);
|
|
1097
1460
|
mkdirSync3(workerDir, { recursive: true });
|
|
1098
|
-
const worktreePath =
|
|
1461
|
+
const worktreePath = path9.join(worktreesDir, run.id, name);
|
|
1099
1462
|
const branch = opts.branch || `agent/${run.id}/${name}`;
|
|
1100
|
-
if (
|
|
1463
|
+
if (existsSync9(worktreePath)) throw new Error(`worktree path already exists: ${worktreePath}`);
|
|
1101
1464
|
git(run.repo, ["fetch", "origin", "--prune"], { allowFailure: true });
|
|
1102
1465
|
git(run.repo, ["worktree", "add", "-b", branch, worktreePath, run.baseCommit], { throwError: true });
|
|
1103
|
-
const stdoutPath =
|
|
1104
|
-
const stderrPath =
|
|
1105
|
-
const heartbeatPath =
|
|
1466
|
+
const stdoutPath = path9.join(workerDir, "stdout.jsonl");
|
|
1467
|
+
const stderrPath = path9.join(workerDir, "stderr.log");
|
|
1468
|
+
const heartbeatPath = path9.join(workerDir, "heartbeat.jsonl");
|
|
1106
1469
|
const prompt = buildPrompt({
|
|
1107
1470
|
task: opts.task,
|
|
1108
1471
|
ownedPaths: opts.ownedPaths || [],
|
|
@@ -1152,9 +1515,20 @@ function spawnWorkerProcess(run, opts) {
|
|
|
1152
1515
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1153
1516
|
};
|
|
1154
1517
|
saveWorker(run.id, worker);
|
|
1155
|
-
run.workers = { ...run.workers || {}, [name]: { workerDir, statusPath:
|
|
1518
|
+
run.workers = { ...run.workers || {}, [name]: { workerDir, statusPath: path9.join(workerDir, "worker.json") } };
|
|
1156
1519
|
run.status = "running";
|
|
1157
1520
|
saveRun(run);
|
|
1521
|
+
if (worker.agentOsId && worker.taskId) {
|
|
1522
|
+
try {
|
|
1523
|
+
spawnCompletionSidecar({
|
|
1524
|
+
runId: run.id,
|
|
1525
|
+
workerName: name,
|
|
1526
|
+
workerDir,
|
|
1527
|
+
agentOsId: worker.agentOsId
|
|
1528
|
+
});
|
|
1529
|
+
} catch {
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1158
1532
|
return worker;
|
|
1159
1533
|
}
|
|
1160
1534
|
function startWorker(args) {
|
|
@@ -1364,7 +1738,7 @@ function redactHarness(text, secret) {
|
|
|
1364
1738
|
}
|
|
1365
1739
|
|
|
1366
1740
|
// src/validate.ts
|
|
1367
|
-
import
|
|
1741
|
+
import path10 from "node:path";
|
|
1368
1742
|
var RUN_ID_RE = /^[a-z0-9][a-z0-9._-]{0,127}$/i;
|
|
1369
1743
|
var WORKER_NAME_RE = /^[a-z0-9][a-z0-9._-]{0,63}$/i;
|
|
1370
1744
|
function validateRunId(runId) {
|
|
@@ -1378,15 +1752,15 @@ function validateWorkerName(name) {
|
|
|
1378
1752
|
return trimmed;
|
|
1379
1753
|
}
|
|
1380
1754
|
function validateRepo(repo) {
|
|
1381
|
-
const resolved =
|
|
1755
|
+
const resolved = path10.resolve(repo);
|
|
1382
1756
|
if (resolved.includes("..")) throw new Error("repo path must not contain .. segments");
|
|
1383
1757
|
return resolved;
|
|
1384
1758
|
}
|
|
1385
1759
|
function validateOwnedPaths(repoRoot, ownedPaths) {
|
|
1386
1760
|
return ownedPaths.map((owned) => {
|
|
1387
|
-
const resolved =
|
|
1388
|
-
const rel =
|
|
1389
|
-
if (rel.startsWith("..") ||
|
|
1761
|
+
const resolved = path10.resolve(repoRoot, owned);
|
|
1762
|
+
const rel = path10.relative(repoRoot, resolved);
|
|
1763
|
+
if (rel.startsWith("..") || path10.isAbsolute(rel)) {
|
|
1390
1764
|
throw new Error(`owned path escapes repo: ${owned}`);
|
|
1391
1765
|
}
|
|
1392
1766
|
return resolved;
|
|
@@ -1398,14 +1772,14 @@ function validateTailLines(lines) {
|
|
|
1398
1772
|
}
|
|
1399
1773
|
|
|
1400
1774
|
// src/worktree.ts
|
|
1401
|
-
import { existsSync as
|
|
1402
|
-
import
|
|
1775
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4 } from "node:fs";
|
|
1776
|
+
import path11 from "node:path";
|
|
1403
1777
|
function createRun(args) {
|
|
1404
1778
|
const repo = validateRepo(required(String(args.repo || ""), "--repo"));
|
|
1405
1779
|
ensureGitRepo(repo);
|
|
1406
1780
|
const id = args.id ? validateRunId(String(args.id)) : timestampSlug(String(args.name || "run"));
|
|
1407
1781
|
const dir = runDirectory(id);
|
|
1408
|
-
if (
|
|
1782
|
+
if (existsSync10(dir)) failExists(`run already exists: ${id}`);
|
|
1409
1783
|
mkdirSync4(dir, { recursive: true });
|
|
1410
1784
|
const base = String(args.base || "origin/main");
|
|
1411
1785
|
const baseCommit = git(repo, ["rev-parse", base]).trim();
|
|
@@ -1419,12 +1793,12 @@ function createRun(args) {
|
|
|
1419
1793
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1420
1794
|
workers: {}
|
|
1421
1795
|
};
|
|
1422
|
-
writeJson(
|
|
1796
|
+
writeJson(path11.join(dir, "run.json"), run);
|
|
1423
1797
|
console.log(JSON.stringify({ runId: id, runDir: dir, repo, base, baseCommit }, null, 2));
|
|
1424
1798
|
}
|
|
1425
1799
|
function listRuns() {
|
|
1426
1800
|
const { runsDir } = getPaths();
|
|
1427
|
-
const rows = listRunIds(runsDir).map((id) => readJson(
|
|
1801
|
+
const rows = listRunIds(runsDir).map((id) => readJson(path11.join(runDirectory(id), "run.json"), void 0)).filter(Boolean).map((run) => ({
|
|
1428
1802
|
id: run.id,
|
|
1429
1803
|
name: run.name,
|
|
1430
1804
|
status: run.status,
|
|
@@ -1439,7 +1813,7 @@ function failExists(message) {
|
|
|
1439
1813
|
}
|
|
1440
1814
|
|
|
1441
1815
|
// src/sweep.ts
|
|
1442
|
-
import
|
|
1816
|
+
import path12 from "node:path";
|
|
1443
1817
|
async function sweepRun(args) {
|
|
1444
1818
|
const pipeline = args.pipeline === true || args.pipeline === "true";
|
|
1445
1819
|
try {
|
|
@@ -1451,7 +1825,7 @@ async function sweepRun(args) {
|
|
|
1451
1825
|
const releasedLocalOrphans = [];
|
|
1452
1826
|
for (const name of Object.keys(run.workers || {})) {
|
|
1453
1827
|
const worker = readJson(
|
|
1454
|
-
|
|
1828
|
+
path12.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1455
1829
|
void 0
|
|
1456
1830
|
);
|
|
1457
1831
|
if (!worker || !worker.dispatched || !worker.taskId) continue;
|
|
@@ -1493,215 +1867,15 @@ async function sweepRun(args) {
|
|
|
1493
1867
|
}
|
|
1494
1868
|
}
|
|
1495
1869
|
|
|
1496
|
-
// src/worker-ops.ts
|
|
1497
|
-
import path11 from "node:path";
|
|
1498
|
-
async function postCompletion(url, secret, body) {
|
|
1499
|
-
const res = await fetch(url, {
|
|
1500
|
-
method: "POST",
|
|
1501
|
-
headers: buildHarnessCallbackHeaders(secret),
|
|
1502
|
-
body: JSON.stringify(body)
|
|
1503
|
-
});
|
|
1504
|
-
let parsed = null;
|
|
1505
|
-
try {
|
|
1506
|
-
parsed = await res.json();
|
|
1507
|
-
} catch {
|
|
1508
|
-
parsed = null;
|
|
1509
|
-
}
|
|
1510
|
-
return { ok: res.ok, status: res.status, parsed };
|
|
1511
|
-
}
|
|
1512
|
-
function completionErrorText(parsed) {
|
|
1513
|
-
if (parsed && typeof parsed === "object") {
|
|
1514
|
-
const err = parsed.error;
|
|
1515
|
-
if (typeof err === "string" && err.trim()) return err.trim();
|
|
1516
|
-
}
|
|
1517
|
-
return void 0;
|
|
1518
|
-
}
|
|
1519
|
-
function persistCompletionBlocker(worker, reason) {
|
|
1520
|
-
const current = worker.completionBlocker;
|
|
1521
|
-
if ((current ?? void 0) === (reason ?? void 0)) return;
|
|
1522
|
-
if (reason) worker.completionBlocker = reason;
|
|
1523
|
-
else delete worker.completionBlocker;
|
|
1524
|
-
saveWorker(worker.runId, worker);
|
|
1525
|
-
}
|
|
1526
|
-
async function tryCompleteWorker(args) {
|
|
1527
|
-
const worker = loadWorker(String(args.run), String(args.name));
|
|
1528
|
-
const status = computeWorkerStatus(worker);
|
|
1529
|
-
const agentOsId = (args.agentOsId ? String(args.agentOsId) : worker.agentOsId) || "";
|
|
1530
|
-
const taskId = (args.taskId ? String(args.taskId) : worker.taskId) || null;
|
|
1531
|
-
if (!agentOsId) {
|
|
1532
|
-
return { ok: false, reason: "missing agentOsId" };
|
|
1533
|
-
}
|
|
1534
|
-
if (!isFinishedWorkerStatus(status)) {
|
|
1535
|
-
return { ok: true, skipped: true, reason: "worker-not-finished" };
|
|
1536
|
-
}
|
|
1537
|
-
const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
|
|
1538
|
-
const explicitSecret = args.secret ? String(args.secret) : void 0;
|
|
1539
|
-
let secret = await resolveCallbackSecretWithMint(explicitSecret, agentOsId, { baseUrl: base });
|
|
1540
|
-
const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/harness/completion`;
|
|
1541
|
-
const body = {
|
|
1542
|
-
source: "openclaw-harness",
|
|
1543
|
-
agentOsId,
|
|
1544
|
-
runId: worker.runId,
|
|
1545
|
-
workerName: worker.name,
|
|
1546
|
-
taskId,
|
|
1547
|
-
startedAt: worker.startedAt,
|
|
1548
|
-
finishedAt: status.lastActivityAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1549
|
-
status
|
|
1550
|
-
};
|
|
1551
|
-
let result = await postCompletion(url, secret, body);
|
|
1552
|
-
if ((result.status === 401 || result.status === 403) && !explicitSecret) {
|
|
1553
|
-
const refreshed = await refreshRunnerToken(agentOsId, { baseUrl: base });
|
|
1554
|
-
if (refreshed && refreshed !== secret) {
|
|
1555
|
-
secret = refreshed;
|
|
1556
|
-
result = await postCompletion(url, secret, body);
|
|
1557
|
-
}
|
|
1558
|
-
}
|
|
1559
|
-
if (result.ok) {
|
|
1560
|
-
persistCompletionBlocker(worker, void 0);
|
|
1561
|
-
return { ok: true, httpStatus: result.status, response: result.parsed };
|
|
1562
|
-
}
|
|
1563
|
-
const authRejected = result.status === 401 || result.status === 403;
|
|
1564
|
-
const detail = completionErrorText(result.parsed) ?? (authRejected ? "runner token unauthorized" : "non-2xx response");
|
|
1565
|
-
const reason = authRejected ? `completion replay rejected (${result.status}): ${detail}` : `completion replay failed (${result.status}): ${detail}`;
|
|
1566
|
-
persistCompletionBlocker(worker, reason);
|
|
1567
|
-
return { ok: false, httpStatus: result.status, response: result.parsed, completionBlocked: true };
|
|
1568
|
-
}
|
|
1569
|
-
async function completeWorker(args) {
|
|
1570
|
-
try {
|
|
1571
|
-
const worker = loadWorker(String(args.run), String(args.name));
|
|
1572
|
-
const status = computeWorkerStatus(worker);
|
|
1573
|
-
const agentOsId = (args.agentOsId ? String(args.agentOsId) : worker.agentOsId) || "";
|
|
1574
|
-
const taskId = (args.taskId ? String(args.taskId) : worker.taskId) || null;
|
|
1575
|
-
if (!agentOsId) {
|
|
1576
|
-
console.error("worker complete requires --agent-os-id (or an agentOsId persisted at worker start)");
|
|
1577
|
-
process.exit(1);
|
|
1578
|
-
}
|
|
1579
|
-
if (!isFinishedWorkerStatus(status)) {
|
|
1580
|
-
console.log(
|
|
1581
|
-
JSON.stringify(
|
|
1582
|
-
{
|
|
1583
|
-
worker: worker.name,
|
|
1584
|
-
runId: worker.runId,
|
|
1585
|
-
status: "skipped",
|
|
1586
|
-
reason: "worker-not-finished",
|
|
1587
|
-
workerStatus: status.status,
|
|
1588
|
-
alive: status.alive
|
|
1589
|
-
},
|
|
1590
|
-
null,
|
|
1591
|
-
2
|
|
1592
|
-
)
|
|
1593
|
-
);
|
|
1594
|
-
return;
|
|
1595
|
-
}
|
|
1596
|
-
const result = await tryCompleteWorker(args);
|
|
1597
|
-
console.log(
|
|
1598
|
-
JSON.stringify(
|
|
1599
|
-
{
|
|
1600
|
-
worker: worker.name,
|
|
1601
|
-
runId: worker.runId,
|
|
1602
|
-
agentOsId,
|
|
1603
|
-
taskId,
|
|
1604
|
-
httpStatus: result.httpStatus,
|
|
1605
|
-
response: result.response
|
|
1606
|
-
},
|
|
1607
|
-
null,
|
|
1608
|
-
2
|
|
1609
|
-
)
|
|
1610
|
-
);
|
|
1611
|
-
if (!result.ok) process.exit(1);
|
|
1612
|
-
} catch (error) {
|
|
1613
|
-
console.error(`worker complete failed: ${error.message}`);
|
|
1614
|
-
process.exit(1);
|
|
1615
|
-
}
|
|
1616
|
-
}
|
|
1617
|
-
function workerStatus(args) {
|
|
1618
|
-
const worker = loadWorker(String(args.run), String(args.name));
|
|
1619
|
-
const status = computeWorkerStatus(worker);
|
|
1620
|
-
writeJson(path11.join(worker.workerDir, "last-status.json"), status);
|
|
1621
|
-
console.log(JSON.stringify(status, null, 2));
|
|
1622
|
-
}
|
|
1623
|
-
function runStatus(args) {
|
|
1624
|
-
const run = loadRun(String(args.run));
|
|
1625
|
-
const names = Object.keys(run.workers || {});
|
|
1626
|
-
const workers = names.map((name) => {
|
|
1627
|
-
const worker = readJson(
|
|
1628
|
-
path11.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1629
|
-
void 0
|
|
1630
|
-
);
|
|
1631
|
-
if (!worker) {
|
|
1632
|
-
return { worker: name, status: "missing", attention: "needs_attention", attentionReason: "worker.json not found" };
|
|
1633
|
-
}
|
|
1634
|
-
const status = computeWorkerStatus(worker, { base: run.base });
|
|
1635
|
-
const rawBlocker = worker.completionBlocker;
|
|
1636
|
-
const completionBlocker = typeof rawBlocker === "string" && rawBlocker ? rawBlocker : void 0;
|
|
1637
|
-
return {
|
|
1638
|
-
worker: status.worker,
|
|
1639
|
-
status: completionBlocker ? "blocked" : status.status,
|
|
1640
|
-
attention: completionBlocker ? "blocked" : status.attention.state,
|
|
1641
|
-
attentionReason: completionBlocker ?? status.attention.reason,
|
|
1642
|
-
pid: status.pid,
|
|
1643
|
-
alive: status.alive,
|
|
1644
|
-
currentTool: status.currentTool,
|
|
1645
|
-
lastActivityAt: status.lastActivityAt,
|
|
1646
|
-
lastHeartbeatPhase: status.lastHeartbeatPhase,
|
|
1647
|
-
lastHeartbeatSummary: status.lastHeartbeatSummary,
|
|
1648
|
-
heartbeatBlocker: status.heartbeatBlocker,
|
|
1649
|
-
changedFileCount: status.changedFiles.length,
|
|
1650
|
-
branch: status.branch,
|
|
1651
|
-
ancestry: status.gitAncestry.relation,
|
|
1652
|
-
ancestryChecked: status.gitAncestry.checked
|
|
1653
|
-
};
|
|
1654
|
-
});
|
|
1655
|
-
const board = {
|
|
1656
|
-
runId: run.id,
|
|
1657
|
-
name: run.name,
|
|
1658
|
-
status: deriveRunStatus(run.status, workers),
|
|
1659
|
-
repo: run.repo,
|
|
1660
|
-
workerCount: workers.length,
|
|
1661
|
-
needsAttention: workers.filter((w) => w.attention && w.attention !== "ok" && w.attention !== "done").map((w) => w.worker),
|
|
1662
|
-
workers
|
|
1663
|
-
};
|
|
1664
|
-
writeJson(path11.join(runDirectory(run.id), "last-board.json"), board);
|
|
1665
|
-
console.log(JSON.stringify(board, null, 2));
|
|
1666
|
-
}
|
|
1667
|
-
function tailWorker(args) {
|
|
1668
|
-
const worker = loadWorker(String(args.run), String(args.name));
|
|
1669
|
-
const raw = tailFile(worker.stdoutPath, Number(args.lines || 40));
|
|
1670
|
-
if (args.raw === true || args.raw === "true") {
|
|
1671
|
-
process.stdout.write(raw);
|
|
1672
|
-
return;
|
|
1673
|
-
}
|
|
1674
|
-
for (const line of raw.split("\n").filter(Boolean)) {
|
|
1675
|
-
const event = safeJson(line);
|
|
1676
|
-
const summary = event ? summarizeEvent(event) : line;
|
|
1677
|
-
if (summary) console.log(summary);
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
|
-
function stopWorker(args) {
|
|
1681
|
-
const worker = loadWorker(String(args.run), String(args.name));
|
|
1682
|
-
if (!isPidAlive(worker.pid)) {
|
|
1683
|
-
console.log(JSON.stringify({ worker: worker.name, pid: worker.pid, status: "not_running" }, null, 2));
|
|
1684
|
-
return;
|
|
1685
|
-
}
|
|
1686
|
-
killWorkerProcess(worker.pid, "SIGTERM");
|
|
1687
|
-
sleepMs(1500);
|
|
1688
|
-
if (isPidAlive(worker.pid)) {
|
|
1689
|
-
killWorkerProcess(worker.pid, "SIGKILL");
|
|
1690
|
-
console.log(JSON.stringify({ worker: worker.name, pid: worker.pid, status: "sigkill_sent" }, null, 2));
|
|
1691
|
-
return;
|
|
1692
|
-
}
|
|
1693
|
-
console.log(JSON.stringify({ worker: worker.name, pid: worker.pid, status: "stopped" }, null, 2));
|
|
1694
|
-
}
|
|
1695
|
-
|
|
1696
1870
|
// src/cli.ts
|
|
1697
1871
|
import { mkdirSync as mkdirSync5, realpathSync } from "node:fs";
|
|
1698
|
-
import { fileURLToPath } from "node:url";
|
|
1872
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1699
1873
|
|
|
1700
1874
|
// src/pipeline-tick.ts
|
|
1701
|
-
import
|
|
1875
|
+
import path15 from "node:path";
|
|
1702
1876
|
|
|
1703
1877
|
// src/finalize.ts
|
|
1704
|
-
import
|
|
1878
|
+
import path13 from "node:path";
|
|
1705
1879
|
var ACTIVE_RUN_STATUSES = /* @__PURE__ */ new Set(["running", "dispatching", "pending", "queued"]);
|
|
1706
1880
|
function terminalStatusFor(run) {
|
|
1707
1881
|
const names = Object.keys(run.workers || {});
|
|
@@ -1711,7 +1885,7 @@ function terminalStatusFor(run) {
|
|
|
1711
1885
|
let anyCompletionBlocked = false;
|
|
1712
1886
|
for (const name of names) {
|
|
1713
1887
|
const worker = readJson(
|
|
1714
|
-
|
|
1888
|
+
path13.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1715
1889
|
void 0
|
|
1716
1890
|
);
|
|
1717
1891
|
if (!worker) continue;
|
|
@@ -1744,7 +1918,7 @@ function finalizeStaleRuns() {
|
|
|
1744
1918
|
}
|
|
1745
1919
|
|
|
1746
1920
|
// src/plan-progress-daemon-sync.ts
|
|
1747
|
-
import
|
|
1921
|
+
import path14 from "node:path";
|
|
1748
1922
|
|
|
1749
1923
|
// src/plan-progress-sync.ts
|
|
1750
1924
|
async function syncPlanProgress(args) {
|
|
@@ -1768,7 +1942,7 @@ async function syncActiveWorkerPlanProgress(runId, args) {
|
|
|
1768
1942
|
const outcomes = [];
|
|
1769
1943
|
for (const name of Object.keys(run.workers || {})) {
|
|
1770
1944
|
const worker = readJson(
|
|
1771
|
-
|
|
1945
|
+
path14.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1772
1946
|
void 0
|
|
1773
1947
|
);
|
|
1774
1948
|
if (!worker?.dispatched || !worker.taskId) continue;
|
|
@@ -1822,7 +1996,7 @@ async function completeFinishedWorkers(runId, args) {
|
|
|
1822
1996
|
const outcomes = [];
|
|
1823
1997
|
for (const name of Object.keys(run.workers || {})) {
|
|
1824
1998
|
const worker = readJson(
|
|
1825
|
-
|
|
1999
|
+
path15.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1826
2000
|
void 0
|
|
1827
2001
|
);
|
|
1828
2002
|
if (!worker?.taskId) continue;
|
|
@@ -2065,6 +2239,7 @@ function usage(code = 0) {
|
|
|
2065
2239
|
" kynver worker tail --run RUN_ID --name worker [--lines 40] [--raw]",
|
|
2066
2240
|
" kynver worker stop --run RUN_ID --name worker",
|
|
2067
2241
|
" kynver worker complete --run RUN_ID --name worker [--agent-os-id AOS_ID] [--task-id TASK_ID] [--base-url URL] [--secret SECRET]",
|
|
2242
|
+
" 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]",
|
|
2068
2243
|
" 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]",
|
|
2069
2244
|
" kynver plan verify --plan PLAN_ID [--worktree PATH] [--task TASK_ID] [--human-override]"
|
|
2070
2245
|
].join("\n")
|
|
@@ -2103,9 +2278,10 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2103
2278
|
if (scope === "worker" && action === "tail") return tailWorker(args);
|
|
2104
2279
|
if (scope === "worker" && action === "stop") return stopWorker(args);
|
|
2105
2280
|
if (scope === "worker" && action === "complete") return void await completeWorker(args);
|
|
2281
|
+
if (scope === "worker" && action === "auto-complete") return void await autoCompleteWorkerCli(args);
|
|
2106
2282
|
unknownCommand(scope, action);
|
|
2107
2283
|
}
|
|
2108
|
-
var isCliEntry = process.argv[1] && realpathSync.native(process.argv[1]) === realpathSync.native(
|
|
2284
|
+
var isCliEntry = process.argv[1] && realpathSync.native(process.argv[1]) === realpathSync.native(fileURLToPath2(import.meta.url));
|
|
2109
2285
|
if (isCliEntry) {
|
|
2110
2286
|
void main().catch((error) => {
|
|
2111
2287
|
console.error(error);
|
|
@@ -2114,6 +2290,8 @@ if (isCliEntry) {
|
|
|
2114
2290
|
}
|
|
2115
2291
|
export {
|
|
2116
2292
|
DEFAULT_DISPATCH_LEASE_MS,
|
|
2293
|
+
autoCompleteWorker,
|
|
2294
|
+
autoCompleteWorkerCli,
|
|
2117
2295
|
buildDispatchTaskText,
|
|
2118
2296
|
buildPrompt,
|
|
2119
2297
|
completeWorker,
|
|
@@ -2140,6 +2318,7 @@ export {
|
|
|
2140
2318
|
runDaemon,
|
|
2141
2319
|
runStatus,
|
|
2142
2320
|
saveUserConfig,
|
|
2321
|
+
spawnCompletionSidecar,
|
|
2143
2322
|
spawnWorkerProcess,
|
|
2144
2323
|
startWorker,
|
|
2145
2324
|
stopWorker,
|