@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/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";
|
|
@@ -207,6 +207,18 @@ async function resolveCallbackSecretWithMint(argsSecret, agentOsId, opts) {
|
|
|
207
207
|
"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"
|
|
208
208
|
);
|
|
209
209
|
}
|
|
210
|
+
async function refreshRunnerToken(agentOsId, opts) {
|
|
211
|
+
const apiKey = loadApiKey();
|
|
212
|
+
const baseUrl = resolveConfiguredBaseUrl(opts?.baseUrl);
|
|
213
|
+
if (!apiKey || !agentOsId || !baseUrl) return null;
|
|
214
|
+
try {
|
|
215
|
+
const token = await fetchRunnerCredential(agentOsId, { baseUrl, apiKey });
|
|
216
|
+
saveRunnerToken(agentOsId, token);
|
|
217
|
+
return token;
|
|
218
|
+
} catch {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
210
222
|
async function fetchRunnerCredential(agentOsId, opts) {
|
|
211
223
|
const apiKey = opts?.apiKey || loadApiKey();
|
|
212
224
|
if (!apiKey) throw new Error("API key required \u2014 run `kynver login` first");
|
|
@@ -380,12 +392,12 @@ var DEFAULT_CRITICAL_FREE_BYTES = 15 * 1024 * 1024 * 1024;
|
|
|
380
392
|
var DEFAULT_MAX_USED_PERCENT = 80;
|
|
381
393
|
var DEFAULT_HARD_MAX_USED_PERCENT = 90;
|
|
382
394
|
function observeRunnerDiskGate(input = {}) {
|
|
383
|
-
const
|
|
395
|
+
const path16 = input.diskPath?.trim() || "/";
|
|
384
396
|
const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
|
|
385
397
|
const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
|
|
386
398
|
const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
|
|
387
399
|
const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
|
|
388
|
-
const stats = statfsSync(
|
|
400
|
+
const stats = statfsSync(path16);
|
|
389
401
|
const freeBytes = Number(stats.bavail) * Number(stats.bsize);
|
|
390
402
|
const totalBytes = Number(stats.blocks) * Number(stats.bsize);
|
|
391
403
|
const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
|
|
@@ -405,7 +417,7 @@ function observeRunnerDiskGate(input = {}) {
|
|
|
405
417
|
}
|
|
406
418
|
return {
|
|
407
419
|
ok,
|
|
408
|
-
path:
|
|
420
|
+
path: path16,
|
|
409
421
|
freeBytes,
|
|
410
422
|
totalBytes,
|
|
411
423
|
usedPercent,
|
|
@@ -895,8 +907,8 @@ function observeRunnerResourceGate(input) {
|
|
|
895
907
|
}
|
|
896
908
|
|
|
897
909
|
// src/supervisor.ts
|
|
898
|
-
import { existsSync as
|
|
899
|
-
import
|
|
910
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3 } from "node:fs";
|
|
911
|
+
import path9 from "node:path";
|
|
900
912
|
|
|
901
913
|
// src/prompt.ts
|
|
902
914
|
function buildPrompt(input) {
|
|
@@ -1070,6 +1082,369 @@ function resolveWorkerProvider(name) {
|
|
|
1070
1082
|
return provider;
|
|
1071
1083
|
}
|
|
1072
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
|
+
|
|
1073
1448
|
// src/supervisor.ts
|
|
1074
1449
|
function spawnWorkerProcess(run, opts) {
|
|
1075
1450
|
const rawName = typeof opts.name === "string" ? opts.name.trim() : "";
|
|
@@ -1080,16 +1455,16 @@ function spawnWorkerProcess(run, opts) {
|
|
|
1080
1455
|
if (run.workers?.[name]) throw new Error(`worker already exists in run ${run.id}: ${name}`);
|
|
1081
1456
|
if (!opts.task) throw new Error(`missing task text for worker ${name}`);
|
|
1082
1457
|
const { worktreesDir } = getPaths();
|
|
1083
|
-
const workerDir =
|
|
1458
|
+
const workerDir = path9.join(runDirectory(run.id), "workers", name);
|
|
1084
1459
|
mkdirSync3(workerDir, { recursive: true });
|
|
1085
|
-
const worktreePath =
|
|
1460
|
+
const worktreePath = path9.join(worktreesDir, run.id, name);
|
|
1086
1461
|
const branch = opts.branch || `agent/${run.id}/${name}`;
|
|
1087
|
-
if (
|
|
1462
|
+
if (existsSync9(worktreePath)) throw new Error(`worktree path already exists: ${worktreePath}`);
|
|
1088
1463
|
git(run.repo, ["fetch", "origin", "--prune"], { allowFailure: true });
|
|
1089
1464
|
git(run.repo, ["worktree", "add", "-b", branch, worktreePath, run.baseCommit], { throwError: true });
|
|
1090
|
-
const stdoutPath =
|
|
1091
|
-
const stderrPath =
|
|
1092
|
-
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");
|
|
1093
1468
|
const prompt = buildPrompt({
|
|
1094
1469
|
task: opts.task,
|
|
1095
1470
|
ownedPaths: opts.ownedPaths || [],
|
|
@@ -1139,9 +1514,20 @@ function spawnWorkerProcess(run, opts) {
|
|
|
1139
1514
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1140
1515
|
};
|
|
1141
1516
|
saveWorker(run.id, worker);
|
|
1142
|
-
run.workers = { ...run.workers || {}, [name]: { workerDir, statusPath:
|
|
1517
|
+
run.workers = { ...run.workers || {}, [name]: { workerDir, statusPath: path9.join(workerDir, "worker.json") } };
|
|
1143
1518
|
run.status = "running";
|
|
1144
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
|
+
}
|
|
1145
1531
|
return worker;
|
|
1146
1532
|
}
|
|
1147
1533
|
function startWorker(args) {
|
|
@@ -1342,7 +1728,7 @@ async function dispatchRun(args) {
|
|
|
1342
1728
|
}
|
|
1343
1729
|
|
|
1344
1730
|
// src/sweep.ts
|
|
1345
|
-
import
|
|
1731
|
+
import path10 from "node:path";
|
|
1346
1732
|
async function sweepRun(args) {
|
|
1347
1733
|
const pipeline = args.pipeline === true || args.pipeline === "true";
|
|
1348
1734
|
try {
|
|
@@ -1354,7 +1740,7 @@ async function sweepRun(args) {
|
|
|
1354
1740
|
const releasedLocalOrphans = [];
|
|
1355
1741
|
for (const name of Object.keys(run.workers || {})) {
|
|
1356
1742
|
const worker = readJson(
|
|
1357
|
-
|
|
1743
|
+
path10.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1358
1744
|
void 0
|
|
1359
1745
|
);
|
|
1360
1746
|
if (!worker || !worker.dispatched || !worker.taskId) continue;
|
|
@@ -1397,11 +1783,11 @@ async function sweepRun(args) {
|
|
|
1397
1783
|
}
|
|
1398
1784
|
|
|
1399
1785
|
// src/worktree.ts
|
|
1400
|
-
import { existsSync as
|
|
1401
|
-
import
|
|
1786
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4 } from "node:fs";
|
|
1787
|
+
import path12 from "node:path";
|
|
1402
1788
|
|
|
1403
1789
|
// src/validate.ts
|
|
1404
|
-
import
|
|
1790
|
+
import path11 from "node:path";
|
|
1405
1791
|
var RUN_ID_RE = /^[a-z0-9][a-z0-9._-]{0,127}$/i;
|
|
1406
1792
|
function validateRunId(runId) {
|
|
1407
1793
|
const trimmed = runId.trim();
|
|
@@ -1409,7 +1795,7 @@ function validateRunId(runId) {
|
|
|
1409
1795
|
return trimmed;
|
|
1410
1796
|
}
|
|
1411
1797
|
function validateRepo(repo) {
|
|
1412
|
-
const resolved =
|
|
1798
|
+
const resolved = path11.resolve(repo);
|
|
1413
1799
|
if (resolved.includes("..")) throw new Error("repo path must not contain .. segments");
|
|
1414
1800
|
return resolved;
|
|
1415
1801
|
}
|
|
@@ -1420,7 +1806,7 @@ function createRun(args) {
|
|
|
1420
1806
|
ensureGitRepo(repo);
|
|
1421
1807
|
const id = args.id ? validateRunId(String(args.id)) : timestampSlug(String(args.name || "run"));
|
|
1422
1808
|
const dir = runDirectory(id);
|
|
1423
|
-
if (
|
|
1809
|
+
if (existsSync10(dir)) failExists(`run already exists: ${id}`);
|
|
1424
1810
|
mkdirSync4(dir, { recursive: true });
|
|
1425
1811
|
const base = String(args.base || "origin/main");
|
|
1426
1812
|
const baseCommit = git(repo, ["rev-parse", base]).trim();
|
|
@@ -1434,12 +1820,12 @@ function createRun(args) {
|
|
|
1434
1820
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1435
1821
|
workers: {}
|
|
1436
1822
|
};
|
|
1437
|
-
writeJson(
|
|
1823
|
+
writeJson(path12.join(dir, "run.json"), run);
|
|
1438
1824
|
console.log(JSON.stringify({ runId: id, runDir: dir, repo, base, baseCommit }, null, 2));
|
|
1439
1825
|
}
|
|
1440
1826
|
function listRuns() {
|
|
1441
1827
|
const { runsDir } = getPaths();
|
|
1442
|
-
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) => ({
|
|
1443
1829
|
id: run.id,
|
|
1444
1830
|
name: run.name,
|
|
1445
1831
|
status: run.status,
|
|
@@ -1453,184 +1839,21 @@ function failExists(message) {
|
|
|
1453
1839
|
process.exit(1);
|
|
1454
1840
|
}
|
|
1455
1841
|
|
|
1456
|
-
// src/worker-ops.ts
|
|
1457
|
-
import path11 from "node:path";
|
|
1458
|
-
async function tryCompleteWorker(args) {
|
|
1459
|
-
const worker = loadWorker(String(args.run), String(args.name));
|
|
1460
|
-
const status = computeWorkerStatus(worker);
|
|
1461
|
-
const agentOsId = (args.agentOsId ? String(args.agentOsId) : worker.agentOsId) || "";
|
|
1462
|
-
const taskId = (args.taskId ? String(args.taskId) : worker.taskId) || null;
|
|
1463
|
-
if (!agentOsId) {
|
|
1464
|
-
return { ok: false, reason: "missing agentOsId" };
|
|
1465
|
-
}
|
|
1466
|
-
if (!isFinishedWorkerStatus(status)) {
|
|
1467
|
-
return { ok: true, skipped: true, reason: "worker-not-finished" };
|
|
1468
|
-
}
|
|
1469
|
-
const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
|
|
1470
|
-
const secret = await resolveCallbackSecretWithMint(args.secret ? String(args.secret) : void 0, agentOsId, { baseUrl: base });
|
|
1471
|
-
const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/harness/completion`;
|
|
1472
|
-
const body = {
|
|
1473
|
-
source: "openclaw-harness",
|
|
1474
|
-
agentOsId,
|
|
1475
|
-
runId: worker.runId,
|
|
1476
|
-
workerName: worker.name,
|
|
1477
|
-
taskId,
|
|
1478
|
-
startedAt: worker.startedAt,
|
|
1479
|
-
finishedAt: status.lastActivityAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1480
|
-
status
|
|
1481
|
-
};
|
|
1482
|
-
const res = await fetch(url, {
|
|
1483
|
-
method: "POST",
|
|
1484
|
-
headers: buildHarnessCallbackHeaders(secret),
|
|
1485
|
-
body: JSON.stringify(body)
|
|
1486
|
-
});
|
|
1487
|
-
let parsed = null;
|
|
1488
|
-
try {
|
|
1489
|
-
parsed = await res.json();
|
|
1490
|
-
} catch {
|
|
1491
|
-
parsed = null;
|
|
1492
|
-
}
|
|
1493
|
-
return { ok: res.ok, httpStatus: res.status, response: parsed };
|
|
1494
|
-
}
|
|
1495
|
-
async function completeWorker(args) {
|
|
1496
|
-
try {
|
|
1497
|
-
const worker = loadWorker(String(args.run), String(args.name));
|
|
1498
|
-
const status = computeWorkerStatus(worker);
|
|
1499
|
-
const agentOsId = (args.agentOsId ? String(args.agentOsId) : worker.agentOsId) || "";
|
|
1500
|
-
const taskId = (args.taskId ? String(args.taskId) : worker.taskId) || null;
|
|
1501
|
-
if (!agentOsId) {
|
|
1502
|
-
console.error("worker complete requires --agent-os-id (or an agentOsId persisted at worker start)");
|
|
1503
|
-
process.exit(1);
|
|
1504
|
-
}
|
|
1505
|
-
if (!isFinishedWorkerStatus(status)) {
|
|
1506
|
-
console.log(
|
|
1507
|
-
JSON.stringify(
|
|
1508
|
-
{
|
|
1509
|
-
worker: worker.name,
|
|
1510
|
-
runId: worker.runId,
|
|
1511
|
-
status: "skipped",
|
|
1512
|
-
reason: "worker-not-finished",
|
|
1513
|
-
workerStatus: status.status,
|
|
1514
|
-
alive: status.alive
|
|
1515
|
-
},
|
|
1516
|
-
null,
|
|
1517
|
-
2
|
|
1518
|
-
)
|
|
1519
|
-
);
|
|
1520
|
-
return;
|
|
1521
|
-
}
|
|
1522
|
-
const result = await tryCompleteWorker(args);
|
|
1523
|
-
console.log(
|
|
1524
|
-
JSON.stringify(
|
|
1525
|
-
{
|
|
1526
|
-
worker: worker.name,
|
|
1527
|
-
runId: worker.runId,
|
|
1528
|
-
agentOsId,
|
|
1529
|
-
taskId,
|
|
1530
|
-
httpStatus: result.httpStatus,
|
|
1531
|
-
response: result.response
|
|
1532
|
-
},
|
|
1533
|
-
null,
|
|
1534
|
-
2
|
|
1535
|
-
)
|
|
1536
|
-
);
|
|
1537
|
-
if (!result.ok) process.exit(1);
|
|
1538
|
-
} catch (error) {
|
|
1539
|
-
console.error(`worker complete failed: ${error.message}`);
|
|
1540
|
-
process.exit(1);
|
|
1541
|
-
}
|
|
1542
|
-
}
|
|
1543
|
-
function workerStatus(args) {
|
|
1544
|
-
const worker = loadWorker(String(args.run), String(args.name));
|
|
1545
|
-
const status = computeWorkerStatus(worker);
|
|
1546
|
-
writeJson(path11.join(worker.workerDir, "last-status.json"), status);
|
|
1547
|
-
console.log(JSON.stringify(status, null, 2));
|
|
1548
|
-
}
|
|
1549
|
-
function runStatus(args) {
|
|
1550
|
-
const run = loadRun(String(args.run));
|
|
1551
|
-
const names = Object.keys(run.workers || {});
|
|
1552
|
-
const workers = names.map((name) => {
|
|
1553
|
-
const worker = readJson(
|
|
1554
|
-
path11.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1555
|
-
void 0
|
|
1556
|
-
);
|
|
1557
|
-
if (!worker) {
|
|
1558
|
-
return { worker: name, status: "missing", attention: "needs_attention", attentionReason: "worker.json not found" };
|
|
1559
|
-
}
|
|
1560
|
-
const status = computeWorkerStatus(worker, { base: run.base });
|
|
1561
|
-
return {
|
|
1562
|
-
worker: status.worker,
|
|
1563
|
-
status: status.status,
|
|
1564
|
-
attention: status.attention.state,
|
|
1565
|
-
attentionReason: status.attention.reason,
|
|
1566
|
-
pid: status.pid,
|
|
1567
|
-
alive: status.alive,
|
|
1568
|
-
currentTool: status.currentTool,
|
|
1569
|
-
lastActivityAt: status.lastActivityAt,
|
|
1570
|
-
lastHeartbeatPhase: status.lastHeartbeatPhase,
|
|
1571
|
-
lastHeartbeatSummary: status.lastHeartbeatSummary,
|
|
1572
|
-
heartbeatBlocker: status.heartbeatBlocker,
|
|
1573
|
-
changedFileCount: status.changedFiles.length,
|
|
1574
|
-
branch: status.branch,
|
|
1575
|
-
ancestry: status.gitAncestry.relation,
|
|
1576
|
-
ancestryChecked: status.gitAncestry.checked
|
|
1577
|
-
};
|
|
1578
|
-
});
|
|
1579
|
-
const board = {
|
|
1580
|
-
runId: run.id,
|
|
1581
|
-
name: run.name,
|
|
1582
|
-
status: deriveRunStatus(run.status, workers),
|
|
1583
|
-
repo: run.repo,
|
|
1584
|
-
workerCount: workers.length,
|
|
1585
|
-
needsAttention: workers.filter((w) => w.attention && w.attention !== "ok" && w.attention !== "done").map((w) => w.worker),
|
|
1586
|
-
workers
|
|
1587
|
-
};
|
|
1588
|
-
writeJson(path11.join(runDirectory(run.id), "last-board.json"), board);
|
|
1589
|
-
console.log(JSON.stringify(board, null, 2));
|
|
1590
|
-
}
|
|
1591
|
-
function tailWorker(args) {
|
|
1592
|
-
const worker = loadWorker(String(args.run), String(args.name));
|
|
1593
|
-
const raw = tailFile(worker.stdoutPath, Number(args.lines || 40));
|
|
1594
|
-
if (args.raw === true || args.raw === "true") {
|
|
1595
|
-
process.stdout.write(raw);
|
|
1596
|
-
return;
|
|
1597
|
-
}
|
|
1598
|
-
for (const line of raw.split("\n").filter(Boolean)) {
|
|
1599
|
-
const event = safeJson(line);
|
|
1600
|
-
const summary = event ? summarizeEvent(event) : line;
|
|
1601
|
-
if (summary) console.log(summary);
|
|
1602
|
-
}
|
|
1603
|
-
}
|
|
1604
|
-
function stopWorker(args) {
|
|
1605
|
-
const worker = loadWorker(String(args.run), String(args.name));
|
|
1606
|
-
if (!isPidAlive(worker.pid)) {
|
|
1607
|
-
console.log(JSON.stringify({ worker: worker.name, pid: worker.pid, status: "not_running" }, null, 2));
|
|
1608
|
-
return;
|
|
1609
|
-
}
|
|
1610
|
-
killWorkerProcess(worker.pid, "SIGTERM");
|
|
1611
|
-
sleepMs(1500);
|
|
1612
|
-
if (isPidAlive(worker.pid)) {
|
|
1613
|
-
killWorkerProcess(worker.pid, "SIGKILL");
|
|
1614
|
-
console.log(JSON.stringify({ worker: worker.name, pid: worker.pid, status: "sigkill_sent" }, null, 2));
|
|
1615
|
-
return;
|
|
1616
|
-
}
|
|
1617
|
-
console.log(JSON.stringify({ worker: worker.name, pid: worker.pid, status: "stopped" }, null, 2));
|
|
1618
|
-
}
|
|
1619
|
-
|
|
1620
1842
|
// src/pipeline-tick.ts
|
|
1621
|
-
import
|
|
1843
|
+
import path15 from "node:path";
|
|
1622
1844
|
|
|
1623
1845
|
// src/finalize.ts
|
|
1624
|
-
import
|
|
1846
|
+
import path13 from "node:path";
|
|
1625
1847
|
var ACTIVE_RUN_STATUSES = /* @__PURE__ */ new Set(["running", "dispatching", "pending", "queued"]);
|
|
1626
1848
|
function terminalStatusFor(run) {
|
|
1627
1849
|
const names = Object.keys(run.workers || {});
|
|
1628
1850
|
if (names.length === 0) return "failed";
|
|
1629
1851
|
let anyAlive = false;
|
|
1630
1852
|
let anyResult = false;
|
|
1853
|
+
let anyCompletionBlocked = false;
|
|
1631
1854
|
for (const name of names) {
|
|
1632
1855
|
const worker = readJson(
|
|
1633
|
-
|
|
1856
|
+
path13.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1634
1857
|
void 0
|
|
1635
1858
|
);
|
|
1636
1859
|
if (!worker) continue;
|
|
@@ -1639,9 +1862,13 @@ function terminalStatusFor(run) {
|
|
|
1639
1862
|
anyAlive = true;
|
|
1640
1863
|
break;
|
|
1641
1864
|
}
|
|
1865
|
+
if (typeof worker.completionBlocker === "string" && worker.completionBlocker) {
|
|
1866
|
+
anyCompletionBlocked = true;
|
|
1867
|
+
}
|
|
1642
1868
|
if (status.finalResult) anyResult = true;
|
|
1643
1869
|
}
|
|
1644
1870
|
if (anyAlive) return null;
|
|
1871
|
+
if (anyCompletionBlocked) return null;
|
|
1645
1872
|
return anyResult ? "completed" : "failed";
|
|
1646
1873
|
}
|
|
1647
1874
|
function finalizeStaleRuns() {
|
|
@@ -1659,7 +1886,7 @@ function finalizeStaleRuns() {
|
|
|
1659
1886
|
}
|
|
1660
1887
|
|
|
1661
1888
|
// src/plan-progress-daemon-sync.ts
|
|
1662
|
-
import
|
|
1889
|
+
import path14 from "node:path";
|
|
1663
1890
|
|
|
1664
1891
|
// src/plan-progress-sync.ts
|
|
1665
1892
|
async function syncPlanProgress(args) {
|
|
@@ -1683,7 +1910,7 @@ async function syncActiveWorkerPlanProgress(runId, args) {
|
|
|
1683
1910
|
const outcomes = [];
|
|
1684
1911
|
for (const name of Object.keys(run.workers || {})) {
|
|
1685
1912
|
const worker = readJson(
|
|
1686
|
-
|
|
1913
|
+
path14.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1687
1914
|
void 0
|
|
1688
1915
|
);
|
|
1689
1916
|
if (!worker?.dispatched || !worker.taskId) continue;
|
|
@@ -1737,12 +1964,13 @@ async function completeFinishedWorkers(runId, args) {
|
|
|
1737
1964
|
const outcomes = [];
|
|
1738
1965
|
for (const name of Object.keys(run.workers || {})) {
|
|
1739
1966
|
const worker = readJson(
|
|
1740
|
-
|
|
1967
|
+
path15.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1741
1968
|
void 0
|
|
1742
1969
|
);
|
|
1743
|
-
if (!worker?.
|
|
1970
|
+
if (!worker?.taskId) continue;
|
|
1744
1971
|
const status = computeWorkerStatus(worker);
|
|
1745
1972
|
if (!isFinishedWorkerStatus(status)) continue;
|
|
1973
|
+
if (!worker.dispatched && !status.finalResult) continue;
|
|
1746
1974
|
const result = await tryCompleteWorker({
|
|
1747
1975
|
run: runId,
|
|
1748
1976
|
name,
|
|
@@ -1770,8 +1998,8 @@ async function runPipelineTick(args) {
|
|
|
1770
1998
|
const agentOsId = String(required(String(args.agentOsId || ""), "--agent-os-id"));
|
|
1771
1999
|
const execute = args.execute !== false && args.execute !== "false";
|
|
1772
2000
|
runStatus({ run: runId });
|
|
1773
|
-
const finalizedStaleRuns = finalizeStaleRuns();
|
|
1774
2001
|
const completedWorkers = await completeFinishedWorkers(runId, args);
|
|
2002
|
+
const finalizedStaleRuns = finalizeStaleRuns();
|
|
1775
2003
|
const planProgressSync = await syncActiveWorkerPlanProgress(runId, args);
|
|
1776
2004
|
const workspacePrefs = await fetchWorkspaceRuntimePreferences(agentOsId, args);
|
|
1777
2005
|
const resourceGate = observeRunnerResourceGate({
|
|
@@ -1979,6 +2207,7 @@ function usage(code = 0) {
|
|
|
1979
2207
|
" kynver worker tail --run RUN_ID --name worker [--lines 40] [--raw]",
|
|
1980
2208
|
" kynver worker stop --run RUN_ID --name worker",
|
|
1981
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]",
|
|
1982
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]",
|
|
1983
2212
|
" kynver plan verify --plan PLAN_ID [--worktree PATH] [--task TASK_ID] [--human-override]"
|
|
1984
2213
|
].join("\n")
|
|
@@ -2017,9 +2246,10 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2017
2246
|
if (scope === "worker" && action === "tail") return tailWorker(args);
|
|
2018
2247
|
if (scope === "worker" && action === "stop") return stopWorker(args);
|
|
2019
2248
|
if (scope === "worker" && action === "complete") return void await completeWorker(args);
|
|
2249
|
+
if (scope === "worker" && action === "auto-complete") return void await autoCompleteWorkerCli(args);
|
|
2020
2250
|
unknownCommand(scope, action);
|
|
2021
2251
|
}
|
|
2022
|
-
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));
|
|
2023
2253
|
if (isCliEntry) {
|
|
2024
2254
|
void main().catch((error) => {
|
|
2025
2255
|
console.error(error);
|