@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/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 saveApiKey(apiKey) {
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({ apiKey }, null, 2)}
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 secret = argsSecret || process.env.KYNVER_RUNTIME_SECRET || process.env.OPENCLAW_CRON_SECRET;
140
- if (!secret) failConfig("requires --secret, KYNVER_RUNTIME_SECRET, or OPENCLAW_CRON_SECRET");
141
- return String(secret);
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). Advanced RAM tuning stays internal."
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 path14 = input.diskPath?.trim() || "/";
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(path14);
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: path14,
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 path13 from "node:path";
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
- path12.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
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) : void 0
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] && path13.resolve(process.argv[1]) === path13.resolve(fileURLToPath(import.meta.url));
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);