@kynver-app/runtime 0.1.13 → 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 +425 -195
- package/dist/cli.js.map +4 -4
- package/dist/index.js +431 -198
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -208,6 +208,18 @@ async function resolveCallbackSecretWithMint(argsSecret, agentOsId, opts) {
|
|
|
208
208
|
"requires --secret, KYNVER_RUNNER_TOKEN, a scoped runner token (`kynver runner credential`), ~/.kynver/credentials runnerToken, KYNVER_API_KEY with an API base URL to mint one, or (legacy) KYNVER_RUNTIME_SECRET / OPENCLAW_CRON_SECRET"
|
|
209
209
|
);
|
|
210
210
|
}
|
|
211
|
+
async function refreshRunnerToken(agentOsId, opts) {
|
|
212
|
+
const apiKey = loadApiKey();
|
|
213
|
+
const baseUrl = resolveConfiguredBaseUrl(opts?.baseUrl);
|
|
214
|
+
if (!apiKey || !agentOsId || !baseUrl) return null;
|
|
215
|
+
try {
|
|
216
|
+
const token = await fetchRunnerCredential(agentOsId, { baseUrl, apiKey });
|
|
217
|
+
saveRunnerToken(agentOsId, token);
|
|
218
|
+
return token;
|
|
219
|
+
} catch {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
211
223
|
async function fetchRunnerCredential(agentOsId, opts) {
|
|
212
224
|
const apiKey = opts?.apiKey || loadApiKey();
|
|
213
225
|
if (!apiKey) throw new Error("API key required \u2014 run `kynver login` first");
|
|
@@ -381,12 +393,12 @@ var DEFAULT_CRITICAL_FREE_BYTES = 15 * 1024 * 1024 * 1024;
|
|
|
381
393
|
var DEFAULT_MAX_USED_PERCENT = 80;
|
|
382
394
|
var DEFAULT_HARD_MAX_USED_PERCENT = 90;
|
|
383
395
|
function observeRunnerDiskGate(input = {}) {
|
|
384
|
-
const
|
|
396
|
+
const path16 = input.diskPath?.trim() || "/";
|
|
385
397
|
const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
|
|
386
398
|
const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
|
|
387
399
|
const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
|
|
388
400
|
const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
|
|
389
|
-
const stats = statfsSync(
|
|
401
|
+
const stats = statfsSync(path16);
|
|
390
402
|
const freeBytes = Number(stats.bavail) * Number(stats.bsize);
|
|
391
403
|
const totalBytes = Number(stats.blocks) * Number(stats.bsize);
|
|
392
404
|
const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
|
|
@@ -406,7 +418,7 @@ function observeRunnerDiskGate(input = {}) {
|
|
|
406
418
|
}
|
|
407
419
|
return {
|
|
408
420
|
ok,
|
|
409
|
-
path:
|
|
421
|
+
path: path16,
|
|
410
422
|
freeBytes,
|
|
411
423
|
totalBytes,
|
|
412
424
|
usedPercent,
|
|
@@ -896,8 +908,8 @@ function observeRunnerResourceGate(input) {
|
|
|
896
908
|
}
|
|
897
909
|
|
|
898
910
|
// src/supervisor.ts
|
|
899
|
-
import { existsSync as
|
|
900
|
-
import
|
|
911
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3 } from "node:fs";
|
|
912
|
+
import path9 from "node:path";
|
|
901
913
|
|
|
902
914
|
// src/prompt.ts
|
|
903
915
|
function buildPrompt(input) {
|
|
@@ -1071,6 +1083,369 @@ function resolveWorkerProvider(name) {
|
|
|
1071
1083
|
return provider;
|
|
1072
1084
|
}
|
|
1073
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
|
+
|
|
1074
1449
|
// src/supervisor.ts
|
|
1075
1450
|
function spawnWorkerProcess(run, opts) {
|
|
1076
1451
|
const rawName = typeof opts.name === "string" ? opts.name.trim() : "";
|
|
@@ -1081,16 +1456,16 @@ function spawnWorkerProcess(run, opts) {
|
|
|
1081
1456
|
if (run.workers?.[name]) throw new Error(`worker already exists in run ${run.id}: ${name}`);
|
|
1082
1457
|
if (!opts.task) throw new Error(`missing task text for worker ${name}`);
|
|
1083
1458
|
const { worktreesDir } = getPaths();
|
|
1084
|
-
const workerDir =
|
|
1459
|
+
const workerDir = path9.join(runDirectory(run.id), "workers", name);
|
|
1085
1460
|
mkdirSync3(workerDir, { recursive: true });
|
|
1086
|
-
const worktreePath =
|
|
1461
|
+
const worktreePath = path9.join(worktreesDir, run.id, name);
|
|
1087
1462
|
const branch = opts.branch || `agent/${run.id}/${name}`;
|
|
1088
|
-
if (
|
|
1463
|
+
if (existsSync9(worktreePath)) throw new Error(`worktree path already exists: ${worktreePath}`);
|
|
1089
1464
|
git(run.repo, ["fetch", "origin", "--prune"], { allowFailure: true });
|
|
1090
1465
|
git(run.repo, ["worktree", "add", "-b", branch, worktreePath, run.baseCommit], { throwError: true });
|
|
1091
|
-
const stdoutPath =
|
|
1092
|
-
const stderrPath =
|
|
1093
|
-
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");
|
|
1094
1469
|
const prompt = buildPrompt({
|
|
1095
1470
|
task: opts.task,
|
|
1096
1471
|
ownedPaths: opts.ownedPaths || [],
|
|
@@ -1140,9 +1515,20 @@ function spawnWorkerProcess(run, opts) {
|
|
|
1140
1515
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1141
1516
|
};
|
|
1142
1517
|
saveWorker(run.id, worker);
|
|
1143
|
-
run.workers = { ...run.workers || {}, [name]: { workerDir, statusPath:
|
|
1518
|
+
run.workers = { ...run.workers || {}, [name]: { workerDir, statusPath: path9.join(workerDir, "worker.json") } };
|
|
1144
1519
|
run.status = "running";
|
|
1145
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
|
+
}
|
|
1146
1532
|
return worker;
|
|
1147
1533
|
}
|
|
1148
1534
|
function startWorker(args) {
|
|
@@ -1352,7 +1738,7 @@ function redactHarness(text, secret) {
|
|
|
1352
1738
|
}
|
|
1353
1739
|
|
|
1354
1740
|
// src/validate.ts
|
|
1355
|
-
import
|
|
1741
|
+
import path10 from "node:path";
|
|
1356
1742
|
var RUN_ID_RE = /^[a-z0-9][a-z0-9._-]{0,127}$/i;
|
|
1357
1743
|
var WORKER_NAME_RE = /^[a-z0-9][a-z0-9._-]{0,63}$/i;
|
|
1358
1744
|
function validateRunId(runId) {
|
|
@@ -1366,15 +1752,15 @@ function validateWorkerName(name) {
|
|
|
1366
1752
|
return trimmed;
|
|
1367
1753
|
}
|
|
1368
1754
|
function validateRepo(repo) {
|
|
1369
|
-
const resolved =
|
|
1755
|
+
const resolved = path10.resolve(repo);
|
|
1370
1756
|
if (resolved.includes("..")) throw new Error("repo path must not contain .. segments");
|
|
1371
1757
|
return resolved;
|
|
1372
1758
|
}
|
|
1373
1759
|
function validateOwnedPaths(repoRoot, ownedPaths) {
|
|
1374
1760
|
return ownedPaths.map((owned) => {
|
|
1375
|
-
const resolved =
|
|
1376
|
-
const rel =
|
|
1377
|
-
if (rel.startsWith("..") ||
|
|
1761
|
+
const resolved = path10.resolve(repoRoot, owned);
|
|
1762
|
+
const rel = path10.relative(repoRoot, resolved);
|
|
1763
|
+
if (rel.startsWith("..") || path10.isAbsolute(rel)) {
|
|
1378
1764
|
throw new Error(`owned path escapes repo: ${owned}`);
|
|
1379
1765
|
}
|
|
1380
1766
|
return resolved;
|
|
@@ -1386,14 +1772,14 @@ function validateTailLines(lines) {
|
|
|
1386
1772
|
}
|
|
1387
1773
|
|
|
1388
1774
|
// src/worktree.ts
|
|
1389
|
-
import { existsSync as
|
|
1390
|
-
import
|
|
1775
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4 } from "node:fs";
|
|
1776
|
+
import path11 from "node:path";
|
|
1391
1777
|
function createRun(args) {
|
|
1392
1778
|
const repo = validateRepo(required(String(args.repo || ""), "--repo"));
|
|
1393
1779
|
ensureGitRepo(repo);
|
|
1394
1780
|
const id = args.id ? validateRunId(String(args.id)) : timestampSlug(String(args.name || "run"));
|
|
1395
1781
|
const dir = runDirectory(id);
|
|
1396
|
-
if (
|
|
1782
|
+
if (existsSync10(dir)) failExists(`run already exists: ${id}`);
|
|
1397
1783
|
mkdirSync4(dir, { recursive: true });
|
|
1398
1784
|
const base = String(args.base || "origin/main");
|
|
1399
1785
|
const baseCommit = git(repo, ["rev-parse", base]).trim();
|
|
@@ -1407,12 +1793,12 @@ function createRun(args) {
|
|
|
1407
1793
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1408
1794
|
workers: {}
|
|
1409
1795
|
};
|
|
1410
|
-
writeJson(
|
|
1796
|
+
writeJson(path11.join(dir, "run.json"), run);
|
|
1411
1797
|
console.log(JSON.stringify({ runId: id, runDir: dir, repo, base, baseCommit }, null, 2));
|
|
1412
1798
|
}
|
|
1413
1799
|
function listRuns() {
|
|
1414
1800
|
const { runsDir } = getPaths();
|
|
1415
|
-
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) => ({
|
|
1416
1802
|
id: run.id,
|
|
1417
1803
|
name: run.name,
|
|
1418
1804
|
status: run.status,
|
|
@@ -1427,7 +1813,7 @@ function failExists(message) {
|
|
|
1427
1813
|
}
|
|
1428
1814
|
|
|
1429
1815
|
// src/sweep.ts
|
|
1430
|
-
import
|
|
1816
|
+
import path12 from "node:path";
|
|
1431
1817
|
async function sweepRun(args) {
|
|
1432
1818
|
const pipeline = args.pipeline === true || args.pipeline === "true";
|
|
1433
1819
|
try {
|
|
@@ -1439,7 +1825,7 @@ async function sweepRun(args) {
|
|
|
1439
1825
|
const releasedLocalOrphans = [];
|
|
1440
1826
|
for (const name of Object.keys(run.workers || {})) {
|
|
1441
1827
|
const worker = readJson(
|
|
1442
|
-
|
|
1828
|
+
path12.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1443
1829
|
void 0
|
|
1444
1830
|
);
|
|
1445
1831
|
if (!worker || !worker.dispatched || !worker.taskId) continue;
|
|
@@ -1481,188 +1867,25 @@ async function sweepRun(args) {
|
|
|
1481
1867
|
}
|
|
1482
1868
|
}
|
|
1483
1869
|
|
|
1484
|
-
// src/worker-ops.ts
|
|
1485
|
-
import path11 from "node:path";
|
|
1486
|
-
async function tryCompleteWorker(args) {
|
|
1487
|
-
const worker = loadWorker(String(args.run), String(args.name));
|
|
1488
|
-
const status = computeWorkerStatus(worker);
|
|
1489
|
-
const agentOsId = (args.agentOsId ? String(args.agentOsId) : worker.agentOsId) || "";
|
|
1490
|
-
const taskId = (args.taskId ? String(args.taskId) : worker.taskId) || null;
|
|
1491
|
-
if (!agentOsId) {
|
|
1492
|
-
return { ok: false, reason: "missing agentOsId" };
|
|
1493
|
-
}
|
|
1494
|
-
if (!isFinishedWorkerStatus(status)) {
|
|
1495
|
-
return { ok: true, skipped: true, reason: "worker-not-finished" };
|
|
1496
|
-
}
|
|
1497
|
-
const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
|
|
1498
|
-
const secret = await resolveCallbackSecretWithMint(args.secret ? String(args.secret) : void 0, agentOsId, { baseUrl: base });
|
|
1499
|
-
const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/harness/completion`;
|
|
1500
|
-
const body = {
|
|
1501
|
-
source: "openclaw-harness",
|
|
1502
|
-
agentOsId,
|
|
1503
|
-
runId: worker.runId,
|
|
1504
|
-
workerName: worker.name,
|
|
1505
|
-
taskId,
|
|
1506
|
-
startedAt: worker.startedAt,
|
|
1507
|
-
finishedAt: status.lastActivityAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1508
|
-
status
|
|
1509
|
-
};
|
|
1510
|
-
const res = await fetch(url, {
|
|
1511
|
-
method: "POST",
|
|
1512
|
-
headers: buildHarnessCallbackHeaders(secret),
|
|
1513
|
-
body: JSON.stringify(body)
|
|
1514
|
-
});
|
|
1515
|
-
let parsed = null;
|
|
1516
|
-
try {
|
|
1517
|
-
parsed = await res.json();
|
|
1518
|
-
} catch {
|
|
1519
|
-
parsed = null;
|
|
1520
|
-
}
|
|
1521
|
-
return { ok: res.ok, httpStatus: res.status, response: parsed };
|
|
1522
|
-
}
|
|
1523
|
-
async function completeWorker(args) {
|
|
1524
|
-
try {
|
|
1525
|
-
const worker = loadWorker(String(args.run), String(args.name));
|
|
1526
|
-
const status = computeWorkerStatus(worker);
|
|
1527
|
-
const agentOsId = (args.agentOsId ? String(args.agentOsId) : worker.agentOsId) || "";
|
|
1528
|
-
const taskId = (args.taskId ? String(args.taskId) : worker.taskId) || null;
|
|
1529
|
-
if (!agentOsId) {
|
|
1530
|
-
console.error("worker complete requires --agent-os-id (or an agentOsId persisted at worker start)");
|
|
1531
|
-
process.exit(1);
|
|
1532
|
-
}
|
|
1533
|
-
if (!isFinishedWorkerStatus(status)) {
|
|
1534
|
-
console.log(
|
|
1535
|
-
JSON.stringify(
|
|
1536
|
-
{
|
|
1537
|
-
worker: worker.name,
|
|
1538
|
-
runId: worker.runId,
|
|
1539
|
-
status: "skipped",
|
|
1540
|
-
reason: "worker-not-finished",
|
|
1541
|
-
workerStatus: status.status,
|
|
1542
|
-
alive: status.alive
|
|
1543
|
-
},
|
|
1544
|
-
null,
|
|
1545
|
-
2
|
|
1546
|
-
)
|
|
1547
|
-
);
|
|
1548
|
-
return;
|
|
1549
|
-
}
|
|
1550
|
-
const result = await tryCompleteWorker(args);
|
|
1551
|
-
console.log(
|
|
1552
|
-
JSON.stringify(
|
|
1553
|
-
{
|
|
1554
|
-
worker: worker.name,
|
|
1555
|
-
runId: worker.runId,
|
|
1556
|
-
agentOsId,
|
|
1557
|
-
taskId,
|
|
1558
|
-
httpStatus: result.httpStatus,
|
|
1559
|
-
response: result.response
|
|
1560
|
-
},
|
|
1561
|
-
null,
|
|
1562
|
-
2
|
|
1563
|
-
)
|
|
1564
|
-
);
|
|
1565
|
-
if (!result.ok) process.exit(1);
|
|
1566
|
-
} catch (error) {
|
|
1567
|
-
console.error(`worker complete failed: ${error.message}`);
|
|
1568
|
-
process.exit(1);
|
|
1569
|
-
}
|
|
1570
|
-
}
|
|
1571
|
-
function workerStatus(args) {
|
|
1572
|
-
const worker = loadWorker(String(args.run), String(args.name));
|
|
1573
|
-
const status = computeWorkerStatus(worker);
|
|
1574
|
-
writeJson(path11.join(worker.workerDir, "last-status.json"), status);
|
|
1575
|
-
console.log(JSON.stringify(status, null, 2));
|
|
1576
|
-
}
|
|
1577
|
-
function runStatus(args) {
|
|
1578
|
-
const run = loadRun(String(args.run));
|
|
1579
|
-
const names = Object.keys(run.workers || {});
|
|
1580
|
-
const workers = names.map((name) => {
|
|
1581
|
-
const worker = readJson(
|
|
1582
|
-
path11.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1583
|
-
void 0
|
|
1584
|
-
);
|
|
1585
|
-
if (!worker) {
|
|
1586
|
-
return { worker: name, status: "missing", attention: "needs_attention", attentionReason: "worker.json not found" };
|
|
1587
|
-
}
|
|
1588
|
-
const status = computeWorkerStatus(worker, { base: run.base });
|
|
1589
|
-
return {
|
|
1590
|
-
worker: status.worker,
|
|
1591
|
-
status: status.status,
|
|
1592
|
-
attention: status.attention.state,
|
|
1593
|
-
attentionReason: status.attention.reason,
|
|
1594
|
-
pid: status.pid,
|
|
1595
|
-
alive: status.alive,
|
|
1596
|
-
currentTool: status.currentTool,
|
|
1597
|
-
lastActivityAt: status.lastActivityAt,
|
|
1598
|
-
lastHeartbeatPhase: status.lastHeartbeatPhase,
|
|
1599
|
-
lastHeartbeatSummary: status.lastHeartbeatSummary,
|
|
1600
|
-
heartbeatBlocker: status.heartbeatBlocker,
|
|
1601
|
-
changedFileCount: status.changedFiles.length,
|
|
1602
|
-
branch: status.branch,
|
|
1603
|
-
ancestry: status.gitAncestry.relation,
|
|
1604
|
-
ancestryChecked: status.gitAncestry.checked
|
|
1605
|
-
};
|
|
1606
|
-
});
|
|
1607
|
-
const board = {
|
|
1608
|
-
runId: run.id,
|
|
1609
|
-
name: run.name,
|
|
1610
|
-
status: deriveRunStatus(run.status, workers),
|
|
1611
|
-
repo: run.repo,
|
|
1612
|
-
workerCount: workers.length,
|
|
1613
|
-
needsAttention: workers.filter((w) => w.attention && w.attention !== "ok" && w.attention !== "done").map((w) => w.worker),
|
|
1614
|
-
workers
|
|
1615
|
-
};
|
|
1616
|
-
writeJson(path11.join(runDirectory(run.id), "last-board.json"), board);
|
|
1617
|
-
console.log(JSON.stringify(board, null, 2));
|
|
1618
|
-
}
|
|
1619
|
-
function tailWorker(args) {
|
|
1620
|
-
const worker = loadWorker(String(args.run), String(args.name));
|
|
1621
|
-
const raw = tailFile(worker.stdoutPath, Number(args.lines || 40));
|
|
1622
|
-
if (args.raw === true || args.raw === "true") {
|
|
1623
|
-
process.stdout.write(raw);
|
|
1624
|
-
return;
|
|
1625
|
-
}
|
|
1626
|
-
for (const line of raw.split("\n").filter(Boolean)) {
|
|
1627
|
-
const event = safeJson(line);
|
|
1628
|
-
const summary = event ? summarizeEvent(event) : line;
|
|
1629
|
-
if (summary) console.log(summary);
|
|
1630
|
-
}
|
|
1631
|
-
}
|
|
1632
|
-
function stopWorker(args) {
|
|
1633
|
-
const worker = loadWorker(String(args.run), String(args.name));
|
|
1634
|
-
if (!isPidAlive(worker.pid)) {
|
|
1635
|
-
console.log(JSON.stringify({ worker: worker.name, pid: worker.pid, status: "not_running" }, null, 2));
|
|
1636
|
-
return;
|
|
1637
|
-
}
|
|
1638
|
-
killWorkerProcess(worker.pid, "SIGTERM");
|
|
1639
|
-
sleepMs(1500);
|
|
1640
|
-
if (isPidAlive(worker.pid)) {
|
|
1641
|
-
killWorkerProcess(worker.pid, "SIGKILL");
|
|
1642
|
-
console.log(JSON.stringify({ worker: worker.name, pid: worker.pid, status: "sigkill_sent" }, null, 2));
|
|
1643
|
-
return;
|
|
1644
|
-
}
|
|
1645
|
-
console.log(JSON.stringify({ worker: worker.name, pid: worker.pid, status: "stopped" }, null, 2));
|
|
1646
|
-
}
|
|
1647
|
-
|
|
1648
1870
|
// src/cli.ts
|
|
1649
1871
|
import { mkdirSync as mkdirSync5, realpathSync } from "node:fs";
|
|
1650
|
-
import { fileURLToPath } from "node:url";
|
|
1872
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1651
1873
|
|
|
1652
1874
|
// src/pipeline-tick.ts
|
|
1653
|
-
import
|
|
1875
|
+
import path15 from "node:path";
|
|
1654
1876
|
|
|
1655
1877
|
// src/finalize.ts
|
|
1656
|
-
import
|
|
1878
|
+
import path13 from "node:path";
|
|
1657
1879
|
var ACTIVE_RUN_STATUSES = /* @__PURE__ */ new Set(["running", "dispatching", "pending", "queued"]);
|
|
1658
1880
|
function terminalStatusFor(run) {
|
|
1659
1881
|
const names = Object.keys(run.workers || {});
|
|
1660
1882
|
if (names.length === 0) return "failed";
|
|
1661
1883
|
let anyAlive = false;
|
|
1662
1884
|
let anyResult = false;
|
|
1885
|
+
let anyCompletionBlocked = false;
|
|
1663
1886
|
for (const name of names) {
|
|
1664
1887
|
const worker = readJson(
|
|
1665
|
-
|
|
1888
|
+
path13.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1666
1889
|
void 0
|
|
1667
1890
|
);
|
|
1668
1891
|
if (!worker) continue;
|
|
@@ -1671,9 +1894,13 @@ function terminalStatusFor(run) {
|
|
|
1671
1894
|
anyAlive = true;
|
|
1672
1895
|
break;
|
|
1673
1896
|
}
|
|
1897
|
+
if (typeof worker.completionBlocker === "string" && worker.completionBlocker) {
|
|
1898
|
+
anyCompletionBlocked = true;
|
|
1899
|
+
}
|
|
1674
1900
|
if (status.finalResult) anyResult = true;
|
|
1675
1901
|
}
|
|
1676
1902
|
if (anyAlive) return null;
|
|
1903
|
+
if (anyCompletionBlocked) return null;
|
|
1677
1904
|
return anyResult ? "completed" : "failed";
|
|
1678
1905
|
}
|
|
1679
1906
|
function finalizeStaleRuns() {
|
|
@@ -1691,7 +1918,7 @@ function finalizeStaleRuns() {
|
|
|
1691
1918
|
}
|
|
1692
1919
|
|
|
1693
1920
|
// src/plan-progress-daemon-sync.ts
|
|
1694
|
-
import
|
|
1921
|
+
import path14 from "node:path";
|
|
1695
1922
|
|
|
1696
1923
|
// src/plan-progress-sync.ts
|
|
1697
1924
|
async function syncPlanProgress(args) {
|
|
@@ -1715,7 +1942,7 @@ async function syncActiveWorkerPlanProgress(runId, args) {
|
|
|
1715
1942
|
const outcomes = [];
|
|
1716
1943
|
for (const name of Object.keys(run.workers || {})) {
|
|
1717
1944
|
const worker = readJson(
|
|
1718
|
-
|
|
1945
|
+
path14.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1719
1946
|
void 0
|
|
1720
1947
|
);
|
|
1721
1948
|
if (!worker?.dispatched || !worker.taskId) continue;
|
|
@@ -1769,12 +1996,13 @@ async function completeFinishedWorkers(runId, args) {
|
|
|
1769
1996
|
const outcomes = [];
|
|
1770
1997
|
for (const name of Object.keys(run.workers || {})) {
|
|
1771
1998
|
const worker = readJson(
|
|
1772
|
-
|
|
1999
|
+
path15.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1773
2000
|
void 0
|
|
1774
2001
|
);
|
|
1775
|
-
if (!worker?.
|
|
2002
|
+
if (!worker?.taskId) continue;
|
|
1776
2003
|
const status = computeWorkerStatus(worker);
|
|
1777
2004
|
if (!isFinishedWorkerStatus(status)) continue;
|
|
2005
|
+
if (!worker.dispatched && !status.finalResult) continue;
|
|
1778
2006
|
const result = await tryCompleteWorker({
|
|
1779
2007
|
run: runId,
|
|
1780
2008
|
name,
|
|
@@ -1802,8 +2030,8 @@ async function runPipelineTick(args) {
|
|
|
1802
2030
|
const agentOsId = String(required(String(args.agentOsId || ""), "--agent-os-id"));
|
|
1803
2031
|
const execute = args.execute !== false && args.execute !== "false";
|
|
1804
2032
|
runStatus({ run: runId });
|
|
1805
|
-
const finalizedStaleRuns = finalizeStaleRuns();
|
|
1806
2033
|
const completedWorkers = await completeFinishedWorkers(runId, args);
|
|
2034
|
+
const finalizedStaleRuns = finalizeStaleRuns();
|
|
1807
2035
|
const planProgressSync = await syncActiveWorkerPlanProgress(runId, args);
|
|
1808
2036
|
const workspacePrefs = await fetchWorkspaceRuntimePreferences(agentOsId, args);
|
|
1809
2037
|
const resourceGate = observeRunnerResourceGate({
|
|
@@ -2011,6 +2239,7 @@ function usage(code = 0) {
|
|
|
2011
2239
|
" kynver worker tail --run RUN_ID --name worker [--lines 40] [--raw]",
|
|
2012
2240
|
" kynver worker stop --run RUN_ID --name worker",
|
|
2013
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]",
|
|
2014
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]",
|
|
2015
2244
|
" kynver plan verify --plan PLAN_ID [--worktree PATH] [--task TASK_ID] [--human-override]"
|
|
2016
2245
|
].join("\n")
|
|
@@ -2049,9 +2278,10 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2049
2278
|
if (scope === "worker" && action === "tail") return tailWorker(args);
|
|
2050
2279
|
if (scope === "worker" && action === "stop") return stopWorker(args);
|
|
2051
2280
|
if (scope === "worker" && action === "complete") return void await completeWorker(args);
|
|
2281
|
+
if (scope === "worker" && action === "auto-complete") return void await autoCompleteWorkerCli(args);
|
|
2052
2282
|
unknownCommand(scope, action);
|
|
2053
2283
|
}
|
|
2054
|
-
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));
|
|
2055
2285
|
if (isCliEntry) {
|
|
2056
2286
|
void main().catch((error) => {
|
|
2057
2287
|
console.error(error);
|
|
@@ -2060,6 +2290,8 @@ if (isCliEntry) {
|
|
|
2060
2290
|
}
|
|
2061
2291
|
export {
|
|
2062
2292
|
DEFAULT_DISPATCH_LEASE_MS,
|
|
2293
|
+
autoCompleteWorker,
|
|
2294
|
+
autoCompleteWorkerCli,
|
|
2063
2295
|
buildDispatchTaskText,
|
|
2064
2296
|
buildPrompt,
|
|
2065
2297
|
completeWorker,
|
|
@@ -2086,6 +2318,7 @@ export {
|
|
|
2086
2318
|
runDaemon,
|
|
2087
2319
|
runStatus,
|
|
2088
2320
|
saveUserConfig,
|
|
2321
|
+
spawnCompletionSidecar,
|
|
2089
2322
|
spawnWorkerProcess,
|
|
2090
2323
|
startWorker,
|
|
2091
2324
|
stopWorker,
|