@kynver-app/runtime 0.1.4 → 0.1.6
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/README.md +13 -2
- package/dist/cli.js +218 -44
- package/dist/cli.js.map +4 -4
- package/dist/index.js +218 -44
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -125,20 +125,112 @@ function saveUserConfig(config) {
|
|
|
125
125
|
writeFileSync2(CONFIG_FILE, `${JSON.stringify(config, null, 2)}
|
|
126
126
|
`, { mode: 384 });
|
|
127
127
|
}
|
|
128
|
-
function
|
|
128
|
+
function loadCredentialsFile() {
|
|
129
|
+
if (!existsSync2(CREDENTIALS_FILE)) return {};
|
|
130
|
+
try {
|
|
131
|
+
return JSON.parse(readFileSync2(CREDENTIALS_FILE, "utf8"));
|
|
132
|
+
} catch {
|
|
133
|
+
return {};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function saveCredentialsFile(parsed) {
|
|
129
137
|
mkdirSync2(CONFIG_DIR, { recursive: true });
|
|
130
|
-
writeFileSync2(CREDENTIALS_FILE, `${JSON.stringify(
|
|
138
|
+
writeFileSync2(CREDENTIALS_FILE, `${JSON.stringify(parsed, null, 2)}
|
|
131
139
|
`, { mode: 384 });
|
|
132
140
|
}
|
|
141
|
+
function loadApiKey() {
|
|
142
|
+
if (process.env.KYNVER_API_KEY) return process.env.KYNVER_API_KEY;
|
|
143
|
+
return loadCredentialsFile().apiKey;
|
|
144
|
+
}
|
|
145
|
+
function saveApiKey(apiKey) {
|
|
146
|
+
saveCredentialsFile({ ...loadCredentialsFile(), apiKey });
|
|
147
|
+
}
|
|
148
|
+
function loadRunnerToken(agentOsId) {
|
|
149
|
+
const creds = loadCredentialsFile();
|
|
150
|
+
if (!creds.runnerToken) return void 0;
|
|
151
|
+
if (agentOsId && creds.runnerTokenAgentOsId && creds.runnerTokenAgentOsId !== agentOsId) {
|
|
152
|
+
return void 0;
|
|
153
|
+
}
|
|
154
|
+
return creds.runnerToken;
|
|
155
|
+
}
|
|
156
|
+
function saveRunnerToken(agentOsId, token) {
|
|
157
|
+
saveCredentialsFile({
|
|
158
|
+
...loadCredentialsFile(),
|
|
159
|
+
runnerToken: token,
|
|
160
|
+
runnerTokenAgentOsId: agentOsId
|
|
161
|
+
});
|
|
162
|
+
}
|
|
133
163
|
function resolveBaseUrl(argsBaseUrl) {
|
|
134
164
|
const baseUrl = argsBaseUrl || process.env.KYNVER_API_URL || process.env.OPENCLAW_CRON_FIRE_BASE_URL || loadUserConfig().apiBaseUrl;
|
|
135
165
|
if (!baseUrl) failConfig("requires --base-url, KYNVER_API_URL, OPENCLAW_CRON_FIRE_BASE_URL, or ~/.kynver/config.json apiBaseUrl");
|
|
136
166
|
return trimTrailingSlash(String(baseUrl));
|
|
137
167
|
}
|
|
138
|
-
function resolveCallbackSecret(argsSecret) {
|
|
139
|
-
const
|
|
140
|
-
if (
|
|
141
|
-
|
|
168
|
+
function resolveCallbackSecret(argsSecret, agentOsId) {
|
|
169
|
+
const scoped = argsSecret || loadRunnerToken(agentOsId) || loadRunnerToken(loadUserConfig().agentOsId);
|
|
170
|
+
if (scoped) return String(scoped);
|
|
171
|
+
const globalSecret = process.env.KYNVER_RUNTIME_SECRET || process.env.OPENCLAW_CRON_SECRET;
|
|
172
|
+
if (globalSecret) {
|
|
173
|
+
console.warn(
|
|
174
|
+
"[kynver] using deployment-level callback secret; run `kynver runner credential --agent-os-id <id>` for a scoped token"
|
|
175
|
+
);
|
|
176
|
+
return String(globalSecret);
|
|
177
|
+
}
|
|
178
|
+
failConfig(
|
|
179
|
+
"requires --secret, a scoped runner token (`kynver runner credential`), ~/.kynver/credentials runnerToken, or (legacy) KYNVER_RUNTIME_SECRET / OPENCLAW_CRON_SECRET"
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
async function fetchRunnerCredential(agentOsId, opts) {
|
|
183
|
+
const apiKey = opts?.apiKey || loadApiKey();
|
|
184
|
+
if (!apiKey) throw new Error("API key required \u2014 run `kynver login` first");
|
|
185
|
+
const base = resolveBaseUrl(opts?.baseUrl);
|
|
186
|
+
const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/runner-credentials`;
|
|
187
|
+
const res = await fetch(url, {
|
|
188
|
+
method: "POST",
|
|
189
|
+
headers: {
|
|
190
|
+
"Content-Type": "application/json",
|
|
191
|
+
Authorization: `Bearer ${apiKey}`
|
|
192
|
+
},
|
|
193
|
+
body: JSON.stringify({})
|
|
194
|
+
});
|
|
195
|
+
const text = await res.text();
|
|
196
|
+
let parsed = null;
|
|
197
|
+
try {
|
|
198
|
+
parsed = JSON.parse(text);
|
|
199
|
+
} catch {
|
|
200
|
+
parsed = null;
|
|
201
|
+
}
|
|
202
|
+
if (!res.ok || !parsed?.token) {
|
|
203
|
+
throw new Error(
|
|
204
|
+
`runner credential mint failed (${res.status}): ${parsed?.error ?? text.slice(0, 200)}`
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
return parsed.token;
|
|
208
|
+
}
|
|
209
|
+
async function mintRunnerCredential(args) {
|
|
210
|
+
const agentOsId = (args.agentOsId ? String(args.agentOsId) : loadUserConfig().agentOsId) || "";
|
|
211
|
+
if (!agentOsId) failConfig("runner credential requires --agent-os-id or agentOsId in ~/.kynver/config.json");
|
|
212
|
+
try {
|
|
213
|
+
const token = await fetchRunnerCredential(agentOsId, {
|
|
214
|
+
baseUrl: args.baseUrl ? String(args.baseUrl) : void 0
|
|
215
|
+
});
|
|
216
|
+
saveRunnerToken(agentOsId, token);
|
|
217
|
+
console.log(
|
|
218
|
+
JSON.stringify(
|
|
219
|
+
{
|
|
220
|
+
ok: true,
|
|
221
|
+
agentOsId,
|
|
222
|
+
credentialsPath: CREDENTIALS_FILE,
|
|
223
|
+
tokenPrefix: `${token.slice(0, 12)}\u2026`,
|
|
224
|
+
note: "Scoped runner token saved; callbacks use X-Kynver-Runner-Token."
|
|
225
|
+
},
|
|
226
|
+
null,
|
|
227
|
+
2
|
|
228
|
+
)
|
|
229
|
+
);
|
|
230
|
+
} catch (err) {
|
|
231
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
142
234
|
}
|
|
143
235
|
function failConfig(message) {
|
|
144
236
|
console.error(message);
|
|
@@ -173,13 +265,28 @@ async function runSetup(args) {
|
|
|
173
265
|
workerProvider: typeof args.provider === "string" ? args.provider : existing.workerProvider || "claude"
|
|
174
266
|
};
|
|
175
267
|
saveUserConfig(config);
|
|
268
|
+
let runnerCredentialNote;
|
|
269
|
+
const apiKey = loadApiKey();
|
|
270
|
+
const agentOsId = config.agentOsId;
|
|
271
|
+
if (apiKey && agentOsId) {
|
|
272
|
+
try {
|
|
273
|
+
const token = await fetchRunnerCredential(agentOsId, {
|
|
274
|
+
baseUrl: typeof args.apiBaseUrl === "string" ? args.apiBaseUrl : config.apiBaseUrl,
|
|
275
|
+
apiKey
|
|
276
|
+
});
|
|
277
|
+
saveRunnerToken(agentOsId, token);
|
|
278
|
+
runnerCredentialNote = "Scoped runner token minted and saved to ~/.kynver/credentials.";
|
|
279
|
+
} catch {
|
|
280
|
+
runnerCredentialNote = "Runner token not minted (server offline or master secret unset). Run `kynver runner credential` after deploy.";
|
|
281
|
+
}
|
|
282
|
+
}
|
|
176
283
|
console.log(
|
|
177
284
|
JSON.stringify(
|
|
178
285
|
{
|
|
179
286
|
ok: true,
|
|
180
287
|
configPath: CONFIG_FILE,
|
|
181
288
|
config,
|
|
182
|
-
note: "Set worker limit once with --max-workers N (or omit to auto-size from RAM).
|
|
289
|
+
note: runnerCredentialNote ?? "Set worker limit once with --max-workers N (or omit to auto-size from RAM). Run `kynver login` + `kynver runner credential` for scoped callbacks."
|
|
183
290
|
},
|
|
184
291
|
null,
|
|
185
292
|
2
|
|
@@ -193,15 +300,27 @@ async function runLogin(args) {
|
|
|
193
300
|
console.log(JSON.stringify({ ok: true, credentialsPath: CREDENTIALS_FILE }, null, 2));
|
|
194
301
|
}
|
|
195
302
|
|
|
303
|
+
// src/callback-headers.ts
|
|
304
|
+
function buildHarnessCallbackHeaders(secret) {
|
|
305
|
+
const trimmed = String(secret).trim();
|
|
306
|
+
if (trimmed.startsWith("krc1.")) {
|
|
307
|
+
return {
|
|
308
|
+
"Content-Type": "application/json",
|
|
309
|
+
"X-Kynver-Runner-Token": trimmed
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
return {
|
|
313
|
+
"Content-Type": "application/json",
|
|
314
|
+
"X-OpenClaw-Cron-Secret": trimmed,
|
|
315
|
+
"X-Kynver-Runtime-Secret": trimmed
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
196
319
|
// src/callbacks.ts
|
|
197
320
|
async function postJson(url, secret, body) {
|
|
198
321
|
const res = await fetch(url, {
|
|
199
322
|
method: "POST",
|
|
200
|
-
headers:
|
|
201
|
-
"Content-Type": "application/json",
|
|
202
|
-
"X-OpenClaw-Cron-Secret": String(secret),
|
|
203
|
-
"X-Kynver-Runtime-Secret": String(secret)
|
|
204
|
-
},
|
|
323
|
+
headers: buildHarnessCallbackHeaders(secret),
|
|
205
324
|
body: JSON.stringify(body)
|
|
206
325
|
});
|
|
207
326
|
let response = null;
|
|
@@ -215,10 +334,7 @@ async function postJson(url, secret, body) {
|
|
|
215
334
|
async function getJson(url, secret) {
|
|
216
335
|
const res = await fetch(url, {
|
|
217
336
|
method: "GET",
|
|
218
|
-
headers:
|
|
219
|
-
"X-OpenClaw-Cron-Secret": String(secret),
|
|
220
|
-
"X-Kynver-Runtime-Secret": String(secret)
|
|
221
|
-
}
|
|
337
|
+
headers: buildHarnessCallbackHeaders(secret)
|
|
222
338
|
});
|
|
223
339
|
let response = null;
|
|
224
340
|
try {
|
|
@@ -236,12 +352,12 @@ var DEFAULT_CRITICAL_FREE_BYTES = 15 * 1024 * 1024 * 1024;
|
|
|
236
352
|
var DEFAULT_MAX_USED_PERCENT = 80;
|
|
237
353
|
var DEFAULT_HARD_MAX_USED_PERCENT = 90;
|
|
238
354
|
function observeRunnerDiskGate(input = {}) {
|
|
239
|
-
const
|
|
355
|
+
const path15 = input.diskPath?.trim() || "/";
|
|
240
356
|
const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
|
|
241
357
|
const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
|
|
242
358
|
const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
|
|
243
359
|
const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
|
|
244
|
-
const stats = statfsSync(
|
|
360
|
+
const stats = statfsSync(path15);
|
|
245
361
|
const freeBytes = Number(stats.bavail) * Number(stats.bsize);
|
|
246
362
|
const totalBytes = Number(stats.blocks) * Number(stats.bsize);
|
|
247
363
|
const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
|
|
@@ -261,7 +377,7 @@ function observeRunnerDiskGate(input = {}) {
|
|
|
261
377
|
}
|
|
262
378
|
return {
|
|
263
379
|
ok,
|
|
264
|
-
path:
|
|
380
|
+
path: path15,
|
|
265
381
|
freeBytes,
|
|
266
382
|
totalBytes,
|
|
267
383
|
usedPercent,
|
|
@@ -638,6 +754,16 @@ import path7 from "node:path";
|
|
|
638
754
|
// src/prompt.ts
|
|
639
755
|
function buildPrompt(input) {
|
|
640
756
|
const ownership = input.ownedPaths.length ? `Owned paths: ${input.ownedPaths.join(", ")}. Do not edit outside these paths without stopping and reporting why.` : "Owned paths: unrestricted for this worker, but keep edits tightly scoped.";
|
|
757
|
+
const progressLines = [
|
|
758
|
+
"Structured plan progress (required when planId is set):",
|
|
759
|
+
"- Harness checkpoints only: `kynver plan progress --plan <planId> --row <rowKey> --role implementer --status running|partial|blocked` (the by-id harness route rejects `done` and confirm events).",
|
|
760
|
+
"- When a slice is finished, emit `partial` with evidence (`--evidence pr:<url>`, `--evidence path:<file>`, or `--evidence command:<cmd>`). Do not propose or confirm row `done` from the worker CLI.",
|
|
761
|
+
"- Propose/confirm row `done` is MCP/session only: chat agents use `agent_os_plan_progress_event_append` on the slug route (implementer proposes with `proposed: true`; report_reviewer/deep_reviewer confirm with `proposed: false`).",
|
|
762
|
+
"- When blocked on operator/Ghost/runtime review, create a linked review task (MCP `agent_os_plan_review_task_create` or API) and pass `--review-task <taskId>`.",
|
|
763
|
+
"- Before the completion report: mark completion-report rows partial with evidence; do not skip report review.",
|
|
764
|
+
"- After implementation: wait for report_reviewer then deep_reviewer confirmation (via MCP/session agents) before follow-up rows close.",
|
|
765
|
+
input.planId ? `Active planId: ${input.planId}${input.taskId ? ` \xB7 taskId: ${input.taskId}` : ""}` : "No planId on this worker \u2014 still emit progress when you touch plan-scoped work."
|
|
766
|
+
];
|
|
641
767
|
return [
|
|
642
768
|
"You are running under the Kynver AgentOS runtime.",
|
|
643
769
|
"Immediately state your plan before editing.",
|
|
@@ -647,6 +773,8 @@ function buildPrompt(input) {
|
|
|
647
773
|
"After each major step, append one JSON line to the heartbeat file with fields: ts, phase, summary, changedFiles, blocker.",
|
|
648
774
|
"Final response must include files changed, verification commands, and unresolved risks.",
|
|
649
775
|
"",
|
|
776
|
+
...progressLines,
|
|
777
|
+
"",
|
|
650
778
|
"Task:",
|
|
651
779
|
input.task
|
|
652
780
|
].join("\n");
|
|
@@ -855,6 +983,7 @@ function spawnWorkerProcess(run, opts) {
|
|
|
855
983
|
ownedPaths: opts.ownedPaths,
|
|
856
984
|
...opts.agentOsId ? { agentOsId: String(opts.agentOsId) } : {},
|
|
857
985
|
...opts.taskId ? { taskId: String(opts.taskId) } : {},
|
|
986
|
+
...opts.planId ? { planId: String(opts.planId) } : {},
|
|
858
987
|
...opts.leaseOwner ? { leaseOwner: String(opts.leaseOwner) } : {},
|
|
859
988
|
...opts.dispatched ? { dispatched: true } : {},
|
|
860
989
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -921,7 +1050,7 @@ async function dispatchRun(args) {
|
|
|
921
1050
|
const run = loadRun(String(required(String(args.run || ""), "--run")));
|
|
922
1051
|
const agentOsId = String(required(String(args.agentOsId || ""), "--agent-os-id"));
|
|
923
1052
|
const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
|
|
924
|
-
const secret = resolveCallbackSecret(args.secret ? String(args.secret) : void 0);
|
|
1053
|
+
const secret = resolveCallbackSecret(args.secret ? String(args.secret) : void 0, agentOsId);
|
|
925
1054
|
const execute = args.execute === true || args.execute === "true";
|
|
926
1055
|
const dryRun = !execute;
|
|
927
1056
|
const leaseOwner = `openclaw-harness:${run.id}`;
|
|
@@ -995,6 +1124,7 @@ async function dispatchRun(args) {
|
|
|
995
1124
|
const task = decision.task;
|
|
996
1125
|
const name = safeSlug(`t-${task.id}-a${task.attempt}`);
|
|
997
1126
|
try {
|
|
1127
|
+
const planId = task.planId ? String(task.planId) : void 0;
|
|
998
1128
|
const worker = spawnWorkerProcess(run, {
|
|
999
1129
|
name,
|
|
1000
1130
|
task: buildDispatchTaskText(task, agentOsId),
|
|
@@ -1002,6 +1132,7 @@ async function dispatchRun(args) {
|
|
|
1002
1132
|
model: args.model ? String(args.model) : void 0,
|
|
1003
1133
|
agentOsId,
|
|
1004
1134
|
taskId: String(task.id),
|
|
1135
|
+
planId,
|
|
1005
1136
|
leaseOwner,
|
|
1006
1137
|
dispatched: true
|
|
1007
1138
|
});
|
|
@@ -1147,7 +1278,7 @@ async function sweepRun(args) {
|
|
|
1147
1278
|
const run = loadRun(String(required(String(args.run || ""), "--run")));
|
|
1148
1279
|
const agentOsId = String(required(String(args.agentOsId || ""), "--agent-os-id"));
|
|
1149
1280
|
const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
|
|
1150
|
-
const secret = resolveCallbackSecret(args.secret ? String(args.secret) : void 0);
|
|
1281
|
+
const secret = resolveCallbackSecret(args.secret ? String(args.secret) : void 0, agentOsId);
|
|
1151
1282
|
const leaseOwner = `openclaw-harness:${run.id}`;
|
|
1152
1283
|
const releasedLocalOrphans = [];
|
|
1153
1284
|
for (const name of Object.keys(run.workers || {})) {
|
|
@@ -1208,7 +1339,7 @@ async function tryCompleteWorker(args) {
|
|
|
1208
1339
|
return { ok: true, skipped: true, reason: "worker-not-finished" };
|
|
1209
1340
|
}
|
|
1210
1341
|
const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
|
|
1211
|
-
const secret = resolveCallbackSecret(args.secret ? String(args.secret) : void 0);
|
|
1342
|
+
const secret = resolveCallbackSecret(args.secret ? String(args.secret) : void 0, agentOsId);
|
|
1212
1343
|
const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/harness/completion`;
|
|
1213
1344
|
const body = {
|
|
1214
1345
|
source: "openclaw-harness",
|
|
@@ -1222,11 +1353,7 @@ async function tryCompleteWorker(args) {
|
|
|
1222
1353
|
};
|
|
1223
1354
|
const res = await fetch(url, {
|
|
1224
1355
|
method: "POST",
|
|
1225
|
-
headers:
|
|
1226
|
-
"Content-Type": "application/json",
|
|
1227
|
-
"X-OpenClaw-Cron-Secret": secret,
|
|
1228
|
-
"X-Kynver-Runtime-Secret": secret
|
|
1229
|
-
},
|
|
1356
|
+
headers: buildHarnessCallbackHeaders(secret),
|
|
1230
1357
|
body: JSON.stringify(body)
|
|
1231
1358
|
});
|
|
1232
1359
|
let parsed = null;
|
|
@@ -1362,16 +1489,61 @@ function stopWorker(args) {
|
|
|
1362
1489
|
|
|
1363
1490
|
// src/cli.ts
|
|
1364
1491
|
import { mkdirSync as mkdirSync5 } from "node:fs";
|
|
1365
|
-
import
|
|
1492
|
+
import path14 from "node:path";
|
|
1366
1493
|
import { fileURLToPath } from "node:url";
|
|
1367
1494
|
|
|
1368
1495
|
// src/pipeline-tick.ts
|
|
1496
|
+
import path13 from "node:path";
|
|
1497
|
+
|
|
1498
|
+
// src/plan-progress-daemon-sync.ts
|
|
1369
1499
|
import path12 from "node:path";
|
|
1370
1500
|
|
|
1501
|
+
// src/plan-progress-sync.ts
|
|
1502
|
+
async function syncPlanProgress(args) {
|
|
1503
|
+
const base = resolveBaseUrl(args.baseUrl);
|
|
1504
|
+
const secret = resolveCallbackSecret(args.secret);
|
|
1505
|
+
const url = `${base}/api/agent-os/by-id/${encodeURIComponent(args.agentOsId)}/tasks/${encodeURIComponent(args.taskId)}/plan-progress-sync`;
|
|
1506
|
+
const res = await postJson(url, secret, {
|
|
1507
|
+
phase: args.phase,
|
|
1508
|
+
taskId: args.taskId,
|
|
1509
|
+
blocker: args.blocker,
|
|
1510
|
+
artifact: args.artifact
|
|
1511
|
+
});
|
|
1512
|
+
return { ok: res.ok, status: res.status, response: res.response };
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
// src/plan-progress-daemon-sync.ts
|
|
1516
|
+
async function syncActiveWorkerPlanProgress(runId, args) {
|
|
1517
|
+
const run = loadRun(runId);
|
|
1518
|
+
const agentOsId = String(args.agentOsId || "");
|
|
1519
|
+
if (!agentOsId) return [];
|
|
1520
|
+
const outcomes = [];
|
|
1521
|
+
for (const name of Object.keys(run.workers || {})) {
|
|
1522
|
+
const worker = readJson(
|
|
1523
|
+
path12.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1524
|
+
null
|
|
1525
|
+
);
|
|
1526
|
+
if (!worker?.dispatched || !worker.taskId) continue;
|
|
1527
|
+
const status = computeWorkerStatus(worker);
|
|
1528
|
+
if (status.heartbeatBlocker) {
|
|
1529
|
+
const res = await syncPlanProgress({
|
|
1530
|
+
agentOsId,
|
|
1531
|
+
taskId: worker.taskId,
|
|
1532
|
+
phase: "heartbeat_blocker",
|
|
1533
|
+
blocker: status.heartbeatBlocker,
|
|
1534
|
+
baseUrl: args.baseUrl ? String(args.baseUrl) : void 0,
|
|
1535
|
+
secret: args.secret ? String(args.secret) : void 0
|
|
1536
|
+
});
|
|
1537
|
+
outcomes.push({ worker: name, phase: "heartbeat_blocker", ok: res.ok });
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
return outcomes;
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1371
1543
|
// src/workspace-runtime-config.ts
|
|
1372
1544
|
async function fetchWorkspaceRuntimePreferences(agentOsId, args) {
|
|
1373
1545
|
const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
|
|
1374
|
-
const secret = resolveCallbackSecret(args.secret ? String(args.secret) : void 0);
|
|
1546
|
+
const secret = resolveCallbackSecret(args.secret ? String(args.secret) : void 0, agentOsId);
|
|
1375
1547
|
const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/runtime`;
|
|
1376
1548
|
try {
|
|
1377
1549
|
const res = await getJson(url, secret);
|
|
@@ -1395,7 +1567,7 @@ async function completeFinishedWorkers(runId, args) {
|
|
|
1395
1567
|
const outcomes = [];
|
|
1396
1568
|
for (const name of Object.keys(run.workers || {})) {
|
|
1397
1569
|
const worker = readJson(
|
|
1398
|
-
|
|
1570
|
+
path13.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
1399
1571
|
null
|
|
1400
1572
|
);
|
|
1401
1573
|
if (!worker?.dispatched || !worker.taskId) continue;
|
|
@@ -1414,7 +1586,7 @@ async function completeFinishedWorkers(runId, args) {
|
|
|
1414
1586
|
}
|
|
1415
1587
|
async function postOperatorTick(agentOsId, runId, resourceGate, args) {
|
|
1416
1588
|
const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
|
|
1417
|
-
const secret = resolveCallbackSecret(args.secret ? String(args.secret) : void 0);
|
|
1589
|
+
const secret = resolveCallbackSecret(args.secret ? String(args.secret) : void 0, agentOsId);
|
|
1418
1590
|
const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/operator/tick`;
|
|
1419
1591
|
const res = await postJson(url, secret, {
|
|
1420
1592
|
agentOsId,
|
|
@@ -1430,6 +1602,7 @@ async function runPipelineTick(args) {
|
|
|
1430
1602
|
const execute = args.execute !== false && args.execute !== "false";
|
|
1431
1603
|
runStatus({ run: runId });
|
|
1432
1604
|
const completedWorkers = await completeFinishedWorkers(runId, args);
|
|
1605
|
+
const planProgressSync = await syncActiveWorkerPlanProgress(runId, args);
|
|
1433
1606
|
const workspacePrefs = await fetchWorkspaceRuntimePreferences(agentOsId, args);
|
|
1434
1607
|
const resourceGate = observeRunnerResourceGate({
|
|
1435
1608
|
runId,
|
|
@@ -1469,6 +1642,7 @@ async function runPipelineTick(args) {
|
|
|
1469
1642
|
execute,
|
|
1470
1643
|
resourceGate,
|
|
1471
1644
|
completedWorkers,
|
|
1645
|
+
planProgressSync,
|
|
1472
1646
|
operatorTick,
|
|
1473
1647
|
sweep,
|
|
1474
1648
|
dispatch,
|
|
@@ -1520,14 +1694,14 @@ function parseEvidenceArg(raw) {
|
|
|
1520
1694
|
return { type: raw.slice(0, idx), value: raw.slice(idx + 1) };
|
|
1521
1695
|
}
|
|
1522
1696
|
async function emitPlanProgress(args) {
|
|
1523
|
-
const planId = required(args, "plan");
|
|
1697
|
+
const planId = required(args.plan ? String(args.plan) : void 0, "plan");
|
|
1524
1698
|
const agentOsId = (args.agentOsId ? String(args.agentOsId) : loadUserConfig().agentOsId) || "";
|
|
1525
1699
|
if (!agentOsId) {
|
|
1526
1700
|
console.error("requires --agent-os-id or agentOsId in ~/.kynver/config.json");
|
|
1527
1701
|
process.exit(1);
|
|
1528
1702
|
}
|
|
1529
|
-
const roleLane = required(args, "role");
|
|
1530
|
-
const status = required(args, "status");
|
|
1703
|
+
const roleLane = required(args.role ? String(args.role) : void 0, "role");
|
|
1704
|
+
const status = required(args.status ? String(args.status) : void 0, "status");
|
|
1531
1705
|
const evidence = [];
|
|
1532
1706
|
const rawEvidence = args.evidence;
|
|
1533
1707
|
if (Array.isArray(rawEvidence)) {
|
|
@@ -1536,8 +1710,10 @@ async function emitPlanProgress(args) {
|
|
|
1536
1710
|
evidence.push(parseEvidenceArg(rawEvidence));
|
|
1537
1711
|
}
|
|
1538
1712
|
const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
|
|
1539
|
-
const secret = resolveCallbackSecret(args.secret ? String(args.secret) : void 0);
|
|
1713
|
+
const secret = resolveCallbackSecret(args.secret ? String(args.secret) : void 0, agentOsId);
|
|
1540
1714
|
const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/plans/${encodeURIComponent(planId)}/progress-events`;
|
|
1715
|
+
const cfg = loadUserConfig();
|
|
1716
|
+
const provider = cfg.workerProvider ? `provider:${cfg.workerProvider}` : void 0;
|
|
1541
1717
|
const body = {
|
|
1542
1718
|
rowKey: args.row ? String(args.row) : void 0,
|
|
1543
1719
|
rowId: args.rowId ? String(args.rowId) : void 0,
|
|
@@ -1549,15 +1725,11 @@ async function emitPlanProgress(args) {
|
|
|
1549
1725
|
remainingWork: args.remaining ? String(args.remaining) : void 0,
|
|
1550
1726
|
evidence: evidence.length ? evidence : void 0,
|
|
1551
1727
|
proposed: args.proposed === true || args.proposed === "true",
|
|
1552
|
-
executorRef: args.executorRef ? String(args.executorRef) :
|
|
1728
|
+
executorRef: args.executorRef ? String(args.executorRef) : provider
|
|
1553
1729
|
};
|
|
1554
1730
|
const res = await fetch(url, {
|
|
1555
1731
|
method: "POST",
|
|
1556
|
-
headers:
|
|
1557
|
-
"Content-Type": "application/json",
|
|
1558
|
-
"X-OpenClaw-Cron-Secret": secret,
|
|
1559
|
-
"X-Kynver-Runtime-Secret": secret
|
|
1560
|
-
},
|
|
1732
|
+
headers: buildHarnessCallbackHeaders(secret),
|
|
1561
1733
|
body: JSON.stringify(body)
|
|
1562
1734
|
});
|
|
1563
1735
|
const text = await res.text();
|
|
@@ -1574,7 +1746,7 @@ async function emitPlanProgress(args) {
|
|
|
1574
1746
|
console.log(JSON.stringify(parsed, null, 2));
|
|
1575
1747
|
}
|
|
1576
1748
|
async function verifyPlan(args) {
|
|
1577
|
-
const planId = required(args, "plan");
|
|
1749
|
+
const planId = required(args.plan ? String(args.plan) : void 0, "plan");
|
|
1578
1750
|
const slug = loadUserConfig().agentOsSlug;
|
|
1579
1751
|
if (!slug) {
|
|
1580
1752
|
console.error("requires agentOsSlug in ~/.kynver/config.json for verify (session route)");
|
|
@@ -1623,6 +1795,7 @@ function usage(code = 0) {
|
|
|
1623
1795
|
[
|
|
1624
1796
|
"Usage:",
|
|
1625
1797
|
" kynver login --api-key KEY",
|
|
1798
|
+
" kynver runner credential [--agent-os-id ID] [--base-url URL]",
|
|
1626
1799
|
" kynver setup [--api-base-url URL] [--agent-os-id ID] [--agent-os-slug SLUG] [--repo PATH] [--max-workers N] [--provider claude|cursor]",
|
|
1627
1800
|
" kynver daemon --run RUN_ID --agent-os-id AOS_ID [--execute] [--interval-ms MS]",
|
|
1628
1801
|
" kynver run create --repo /path/repo [--name name] [--base origin/main]",
|
|
@@ -1646,7 +1819,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
1646
1819
|
const scope = argv.shift();
|
|
1647
1820
|
let action;
|
|
1648
1821
|
let rest;
|
|
1649
|
-
if (scope === "run" || scope === "worker" || scope === "plan") {
|
|
1822
|
+
if (scope === "run" || scope === "worker" || scope === "plan" || scope === "runner") {
|
|
1650
1823
|
action = argv.shift();
|
|
1651
1824
|
rest = argv;
|
|
1652
1825
|
} else {
|
|
@@ -1657,6 +1830,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
1657
1830
|
mkdirSync5(runsDir, { recursive: true });
|
|
1658
1831
|
mkdirSync5(worktreesDir, { recursive: true });
|
|
1659
1832
|
if (scope === "login") return void await runLogin(args);
|
|
1833
|
+
if (scope === "runner" && action === "credential") return void await mintRunnerCredential(args);
|
|
1660
1834
|
if (scope === "setup") return void await runSetup(args);
|
|
1661
1835
|
if (scope === "daemon") return void await runDaemon(args);
|
|
1662
1836
|
if (scope === "plan" && action === "progress") return void await emitPlanProgress(args);
|
|
@@ -1673,7 +1847,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
1673
1847
|
if (scope === "worker" && action === "complete") return void await completeWorker(args);
|
|
1674
1848
|
unknownCommand(scope, action);
|
|
1675
1849
|
}
|
|
1676
|
-
var isCliEntry = process.argv[1] &&
|
|
1850
|
+
var isCliEntry = process.argv[1] && path14.resolve(process.argv[1]) === path14.resolve(fileURLToPath(import.meta.url));
|
|
1677
1851
|
if (isCliEntry) {
|
|
1678
1852
|
void main().catch((error) => {
|
|
1679
1853
|
console.error(error);
|