@kynver-app/runtime 0.1.48 → 0.1.49
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 +160 -34
- package/dist/cli.js.map +4 -4
- package/dist/doctor/runtime-takeover-scheduler.d.ts +20 -0
- package/dist/doctor/runtime-takeover.probes.d.ts +2 -0
- package/dist/index.js +160 -34
- package/dist/index.js.map +4 -4
- package/dist/pr-handoff/pr-handoff-assess.d.ts +10 -1
- package/dist/pr-handoff/pr-handoff.types.d.ts +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { DoctorCheck } from "./doctor.types.js";
|
|
2
|
+
export interface RuntimeTakeoverSchedulerEnv {
|
|
3
|
+
kynverSchedulerProvider?: string;
|
|
4
|
+
/** True when this process has QStash credentials (typical Kynver server deploy). */
|
|
5
|
+
qstashTokenPresent?: boolean;
|
|
6
|
+
/** Explicit hosted-deploy marker (`KYNVER_HOSTED_DEPLOYMENT=1|true|yes`). */
|
|
7
|
+
kynverHostedDeployment?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface RuntimeTakeoverSchedulerContext {
|
|
10
|
+
agentOsId?: string | null;
|
|
11
|
+
apiBaseUrl?: string | null;
|
|
12
|
+
hasScopedRunnerToken: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Scheduler readiness for runtime takeover.
|
|
16
|
+
*
|
|
17
|
+
* Runner hosts drive dispatch via `kynver daemon` → `pipeline-tick` → `operator/tick`
|
|
18
|
+
* and must not depend on OpenClaw cron. Server/hosted deploys should resolve to QStash.
|
|
19
|
+
*/
|
|
20
|
+
export declare function assessRuntimeTakeoverScheduler(env: RuntimeTakeoverSchedulerEnv, ctx: RuntimeTakeoverSchedulerContext): DoctorCheck;
|
|
@@ -26,6 +26,8 @@ export interface RuntimeTakeoverProbes {
|
|
|
26
26
|
kynverHarnessRoot?: string;
|
|
27
27
|
opusHarnessRoot?: string;
|
|
28
28
|
kynverSchedulerProvider?: string;
|
|
29
|
+
qstashTokenPresent?: boolean;
|
|
30
|
+
kynverHostedDeployment?: boolean;
|
|
29
31
|
};
|
|
30
32
|
harnessRoot(): string;
|
|
31
33
|
legacyOpenclawHarnessRoot(): string;
|
package/dist/index.js
CHANGED
|
@@ -1956,7 +1956,7 @@ function inferModelRoutingFromTask(task) {
|
|
|
1956
1956
|
rule: "lane:landing"
|
|
1957
1957
|
};
|
|
1958
1958
|
}
|
|
1959
|
-
if (ref.includes("review") || title
|
|
1959
|
+
if (ref.includes("review") || /^review[\s:]/.test(title) || roleLane.includes("review")) {
|
|
1960
1960
|
if (isOpusLane(ref, title) || roleLane === "deep_reviewer") {
|
|
1961
1961
|
return { model: "claude-opus-4-7", provider: "claude", rule: "lane:deep_review" };
|
|
1962
1962
|
}
|
|
@@ -2419,6 +2419,11 @@ function isHarnessExpertReviewWorker(worker) {
|
|
|
2419
2419
|
|
|
2420
2420
|
// src/pr-handoff/pr-handoff-assess.ts
|
|
2421
2421
|
var REVIEW_LANE_RULE = /^(lane:)?(review|deep_review|planning|landing)(:|$)/i;
|
|
2422
|
+
var REVIEW_PERSONA_SLUGS = /* @__PURE__ */ new Set(["lorentz", "sentinel"]);
|
|
2423
|
+
var NO_PR_COMMITS_BETWEEN_RE = /no commits between/i;
|
|
2424
|
+
function isGhNoCommitsBetweenError(detail) {
|
|
2425
|
+
return Boolean(detail && NO_PR_COMMITS_BETWEEN_RE.test(detail));
|
|
2426
|
+
}
|
|
2422
2427
|
function trimOrNull4(value) {
|
|
2423
2428
|
if (typeof value !== "string") return null;
|
|
2424
2429
|
const trimmed = value.trim();
|
|
@@ -2437,8 +2442,30 @@ function extractPrUrlFromText(value) {
|
|
|
2437
2442
|
);
|
|
2438
2443
|
return m ? trimOrNull4(m[0]) : null;
|
|
2439
2444
|
}
|
|
2440
|
-
function
|
|
2445
|
+
function countCommitsAheadOfBase(worktreePath, baseRef, exec) {
|
|
2446
|
+
const base = baseRef.trim();
|
|
2447
|
+
if (!base) return null;
|
|
2448
|
+
const count = exec.git(worktreePath, ["rev-list", "--count", `${base}..HEAD`]);
|
|
2449
|
+
if (count.status !== 0) return null;
|
|
2450
|
+
const n = Number.parseInt(count.stdout.trim(), 10);
|
|
2451
|
+
return Number.isFinite(n) ? n : null;
|
|
2452
|
+
}
|
|
2453
|
+
function isReviewArtifactWorker(worker, snapshot) {
|
|
2454
|
+
if (snapshot.changedFiles.length > 0) return false;
|
|
2455
|
+
const persona = trimOrNull4(worker.personaSlug)?.toLowerCase();
|
|
2456
|
+
if (persona && REVIEW_PERSONA_SLUGS.has(persona)) return true;
|
|
2457
|
+
const rule = trimOrNull4(worker.routingRule) ?? "";
|
|
2458
|
+
if (rule && REVIEW_LANE_RULE.test(rule)) return true;
|
|
2459
|
+
return false;
|
|
2460
|
+
}
|
|
2461
|
+
function hasWorkProduct(snapshot, options) {
|
|
2441
2462
|
if (snapshot.changedFiles.length > 0) return true;
|
|
2463
|
+
const baseRef = trimOrNull4(options?.baseRef);
|
|
2464
|
+
if (baseRef && options?.exec && options.worktreePath) {
|
|
2465
|
+
const ahead = countCommitsAheadOfBase(options.worktreePath, baseRef, options.exec);
|
|
2466
|
+
if (ahead === 0) return false;
|
|
2467
|
+
if (ahead !== null && ahead > 0) return true;
|
|
2468
|
+
}
|
|
2442
2469
|
if (trimOrNull4(snapshot.headCommit)) return true;
|
|
2443
2470
|
if (committedHead(snapshot.gitAncestry)) return true;
|
|
2444
2471
|
return false;
|
|
@@ -2459,6 +2486,13 @@ function assessPrHandoffRequirement(input) {
|
|
|
2459
2486
|
if (rule && REVIEW_LANE_RULE.test(rule)) {
|
|
2460
2487
|
return { required: false, reason: "review_lane" };
|
|
2461
2488
|
}
|
|
2489
|
+
const workerCtx = input.worker ?? {
|
|
2490
|
+
personaSlug: input.personaSlug,
|
|
2491
|
+
routingRule: input.routingRule
|
|
2492
|
+
};
|
|
2493
|
+
if (isReviewArtifactWorker(workerCtx, input.snapshot)) {
|
|
2494
|
+
return { required: false, reason: "review_artifact" };
|
|
2495
|
+
}
|
|
2462
2496
|
if (trimOrNull4(input.patchPath) || trimOrNull4(input.artifactBundlePath)) {
|
|
2463
2497
|
return { required: false, reason: "patch_or_bundle" };
|
|
2464
2498
|
}
|
|
@@ -2466,7 +2500,12 @@ function assessPrHandoffRequirement(input) {
|
|
|
2466
2500
|
if (prUrl) {
|
|
2467
2501
|
return { required: false, reason: "already_has_pr" };
|
|
2468
2502
|
}
|
|
2469
|
-
|
|
2503
|
+
const workProductOpts = input.exec && input.baseRef ? {
|
|
2504
|
+
baseRef: input.baseRef,
|
|
2505
|
+
exec: input.exec,
|
|
2506
|
+
worktreePath: input.snapshot.worktreePath
|
|
2507
|
+
} : void 0;
|
|
2508
|
+
if (!hasWorkProduct(input.snapshot, workProductOpts)) {
|
|
2470
2509
|
return { required: false, reason: "no_work_product" };
|
|
2471
2510
|
}
|
|
2472
2511
|
return { required: true, snapshot: input.snapshot };
|
|
@@ -2675,15 +2714,19 @@ function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
|
|
|
2675
2714
|
prUrl: prUrlHint,
|
|
2676
2715
|
headCommit: null
|
|
2677
2716
|
});
|
|
2717
|
+
const baseRef = input.run.baseCommit?.trim() || input.run.base?.trim() || "origin/main";
|
|
2678
2718
|
const requirement = assessPrHandoffRequirement({
|
|
2679
2719
|
dispatched: input.worker.dispatched,
|
|
2680
2720
|
routingRule: input.worker.routingRule,
|
|
2721
|
+
personaSlug: input.worker.personaSlug,
|
|
2681
2722
|
prUrl: prUrlHint,
|
|
2682
2723
|
taskTitle: input.worker.taskTitle,
|
|
2683
2724
|
executorRef: input.worker.executorRef,
|
|
2684
2725
|
parentTaskId: input.worker.parentTaskId,
|
|
2685
|
-
personaSlug: input.worker.personaSlug,
|
|
2686
2726
|
taskPrUrl: input.worker.taskPrUrl,
|
|
2727
|
+
baseRef,
|
|
2728
|
+
exec,
|
|
2729
|
+
worker: input.worker,
|
|
2687
2730
|
snapshot
|
|
2688
2731
|
});
|
|
2689
2732
|
if (!requirement.required) {
|
|
@@ -2768,6 +2811,14 @@ function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
|
|
|
2768
2811
|
exec
|
|
2769
2812
|
});
|
|
2770
2813
|
if (!pr.ok || !pr.prUrl) {
|
|
2814
|
+
if (isGhNoCommitsBetweenError(pr.detail)) {
|
|
2815
|
+
return {
|
|
2816
|
+
ok: true,
|
|
2817
|
+
headCommit: headCommit ?? void 0,
|
|
2818
|
+
committed,
|
|
2819
|
+
pushed
|
|
2820
|
+
};
|
|
2821
|
+
}
|
|
2771
2822
|
const dirty = snapshot.changedFiles.length;
|
|
2772
2823
|
const detail = dirty ? `${dirty} uncommitted change(s) and no PR URL after handoff attempt` : "no PR URL after handoff attempt";
|
|
2773
2824
|
return {
|
|
@@ -6638,7 +6689,12 @@ var defaultRuntimeTakeoverProbes = {
|
|
|
6638
6689
|
openclawCronSecret: Boolean(process.env.OPENCLAW_CRON_SECRET?.trim()),
|
|
6639
6690
|
kynverHarnessRoot: process.env.KYNVER_HARNESS_ROOT?.trim() || void 0,
|
|
6640
6691
|
opusHarnessRoot: process.env.OPUS_HARNESS_ROOT?.trim() || void 0,
|
|
6641
|
-
kynverSchedulerProvider: process.env.KYNVER_SCHEDULER_PROVIDER?.trim() || void 0
|
|
6692
|
+
kynverSchedulerProvider: process.env.KYNVER_SCHEDULER_PROVIDER?.trim() || void 0,
|
|
6693
|
+
qstashTokenPresent: Boolean(process.env.QSTASH_TOKEN?.trim()),
|
|
6694
|
+
kynverHostedDeployment: (() => {
|
|
6695
|
+
const v = process.env.KYNVER_HOSTED_DEPLOYMENT?.trim().toLowerCase();
|
|
6696
|
+
return v === "1" || v === "true" || v === "yes";
|
|
6697
|
+
})()
|
|
6642
6698
|
}),
|
|
6643
6699
|
harnessRoot: () => resolveHarnessRoot(),
|
|
6644
6700
|
legacyOpenclawHarnessRoot: () => path36.join(homedir6(), ".openclaw", "harness"),
|
|
@@ -6648,10 +6704,76 @@ var defaultRuntimeTakeoverProbes = {
|
|
|
6648
6704
|
vercelWhoami: () => captureCommand("vercel", ["whoami"])
|
|
6649
6705
|
};
|
|
6650
6706
|
|
|
6651
|
-
// src/doctor/runtime-takeover.ts
|
|
6707
|
+
// src/doctor/runtime-takeover-scheduler.ts
|
|
6652
6708
|
function check(partial) {
|
|
6653
6709
|
return partial;
|
|
6654
6710
|
}
|
|
6711
|
+
function assessRuntimeTakeoverScheduler(env, ctx) {
|
|
6712
|
+
const runnerOpenclaw = env.kynverSchedulerProvider === "openclaw-cron";
|
|
6713
|
+
const runnerQstash = env.kynverSchedulerProvider === "qstash";
|
|
6714
|
+
const hostedDeployment = Boolean(env.qstashTokenPresent) || Boolean(env.kynverHostedDeployment);
|
|
6715
|
+
const daemonDispatchReady = Boolean(ctx.agentOsId?.trim()) && Boolean(ctx.apiBaseUrl?.trim()) && ctx.hasScopedRunnerToken;
|
|
6716
|
+
const deploymentNeedsQstash = hostedDeployment && !env.qstashTokenPresent && env.kynverSchedulerProvider !== "qstash";
|
|
6717
|
+
const deploymentOpenclaw = hostedDeployment && env.kynverSchedulerProvider === "openclaw-cron";
|
|
6718
|
+
if (runnerOpenclaw) {
|
|
6719
|
+
return check({
|
|
6720
|
+
id: "hotspot_openclaw_scheduler",
|
|
6721
|
+
label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
|
|
6722
|
+
status: "warn",
|
|
6723
|
+
summary: "KYNVER_SCHEDULER_PROVIDER=openclaw-cron on this runner \u2014 hosted dispatch still depends on the OpenClaw local-cron adapter",
|
|
6724
|
+
remediation: "Unset KYNVER_SCHEDULER_PROVIDER on user runners. Use `kynver daemon` (pipeline-tick \u2192 operator/tick). On the Kynver server set KYNVER_SCHEDULER_PROVIDER=qstash when QStash is configured.",
|
|
6725
|
+
details: { schedulerProvider: env.kynverSchedulerProvider ?? null, hostedDeployment }
|
|
6726
|
+
});
|
|
6727
|
+
}
|
|
6728
|
+
if (deploymentOpenclaw || deploymentNeedsQstash) {
|
|
6729
|
+
return check({
|
|
6730
|
+
id: "hotspot_openclaw_scheduler",
|
|
6731
|
+
label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
|
|
6732
|
+
status: "warn",
|
|
6733
|
+
summary: deploymentOpenclaw ? "Hosted deployment has KYNVER_SCHEDULER_PROVIDER=openclaw-cron \u2014 AgentOS scheduled ticks should use QStash" : "Hosted deployment without QSTASH_TOKEN \u2014 scheduler may fall back to openclaw-cron",
|
|
6734
|
+
remediation: "Set QSTASH_TOKEN and KYNVER_SCHEDULER_PROVIDER=qstash on the Kynver server. User runners use `kynver daemon` for dispatch and should not set a scheduler provider.",
|
|
6735
|
+
details: {
|
|
6736
|
+
schedulerProvider: env.kynverSchedulerProvider ?? null,
|
|
6737
|
+
qstashTokenPresent: env.qstashTokenPresent ?? false,
|
|
6738
|
+
hostedDeployment
|
|
6739
|
+
}
|
|
6740
|
+
});
|
|
6741
|
+
}
|
|
6742
|
+
if (daemonDispatchReady) {
|
|
6743
|
+
return check({
|
|
6744
|
+
id: "hotspot_openclaw_scheduler",
|
|
6745
|
+
label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
|
|
6746
|
+
status: "pass",
|
|
6747
|
+
summary: runnerQstash ? "Runner override qstash present; hosted dispatch still owned by kynver daemon pipeline-tick" : "Hosted dispatch owned by kynver daemon (pipeline-tick \u2192 operator/tick); no OpenClaw cron on runner",
|
|
6748
|
+
details: {
|
|
6749
|
+
schedulerProvider: env.kynverSchedulerProvider ?? null,
|
|
6750
|
+
dispatchPath: "kynver-daemon-pipeline-tick",
|
|
6751
|
+
hostedDeployment
|
|
6752
|
+
}
|
|
6753
|
+
});
|
|
6754
|
+
}
|
|
6755
|
+
if (!env.kynverSchedulerProvider) {
|
|
6756
|
+
return check({
|
|
6757
|
+
id: "hotspot_openclaw_scheduler",
|
|
6758
|
+
label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
|
|
6759
|
+
status: "pass",
|
|
6760
|
+
summary: "No KYNVER_SCHEDULER_PROVIDER on runner (expected) \u2014 finish runner setup so daemon pipeline-tick owns dispatch",
|
|
6761
|
+
details: { schedulerProvider: null, dispatchPath: "kynver-daemon-pipeline-tick-pending" }
|
|
6762
|
+
});
|
|
6763
|
+
}
|
|
6764
|
+
return check({
|
|
6765
|
+
id: "hotspot_openclaw_scheduler",
|
|
6766
|
+
label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
|
|
6767
|
+
status: "pass",
|
|
6768
|
+
summary: `KYNVER_SCHEDULER_PROVIDER=${env.kynverSchedulerProvider}`,
|
|
6769
|
+
details: { schedulerProvider: env.kynverSchedulerProvider ?? null }
|
|
6770
|
+
});
|
|
6771
|
+
}
|
|
6772
|
+
|
|
6773
|
+
// src/doctor/runtime-takeover.ts
|
|
6774
|
+
function check2(partial) {
|
|
6775
|
+
return partial;
|
|
6776
|
+
}
|
|
6655
6777
|
function summarizeCounts(sections) {
|
|
6656
6778
|
const counts = { pass: 0, warn: 0, fail: 0 };
|
|
6657
6779
|
for (const section of sections) {
|
|
@@ -6668,14 +6790,14 @@ function assessCliPackage(probes) {
|
|
|
6668
6790
|
const firstPath = onPath ? which.stdout.split(/\r?\n/)[0]?.trim() : void 0;
|
|
6669
6791
|
const displayCliPath = firstPath ? displayUserPath(firstPath) : void 0;
|
|
6670
6792
|
const checks = [
|
|
6671
|
-
|
|
6793
|
+
check2({
|
|
6672
6794
|
id: "cli_running_version",
|
|
6673
6795
|
label: "Running @kynver-app/runtime version",
|
|
6674
6796
|
status: "pass",
|
|
6675
6797
|
summary: `@kynver-app/runtime ${runningVersion}`,
|
|
6676
6798
|
details: { version: runningVersion }
|
|
6677
6799
|
}),
|
|
6678
|
-
|
|
6800
|
+
check2({
|
|
6679
6801
|
id: "cli_on_path",
|
|
6680
6802
|
label: "kynver executable on PATH",
|
|
6681
6803
|
status: onPath ? "pass" : "warn",
|
|
@@ -6689,7 +6811,7 @@ function assessCliPackage(probes) {
|
|
|
6689
6811
|
const installedVersion = versionProbe.stdout.replace(/^kynver\s+/i, "").trim() || void 0;
|
|
6690
6812
|
const versionMatch = versionProbe.ok && (!installedVersion || installedVersion === runningVersion);
|
|
6691
6813
|
checks.push(
|
|
6692
|
-
|
|
6814
|
+
check2({
|
|
6693
6815
|
id: "cli_installed_version",
|
|
6694
6816
|
label: "Installed kynver CLI version matches running package",
|
|
6695
6817
|
status: versionMatch ? "pass" : "warn",
|
|
@@ -6707,7 +6829,7 @@ function assessUserConfig(probes) {
|
|
|
6707
6829
|
const exists = probes.pathExists(configPath);
|
|
6708
6830
|
const config = probes.loadConfig();
|
|
6709
6831
|
const checks = [
|
|
6710
|
-
|
|
6832
|
+
check2({
|
|
6711
6833
|
id: "config_file",
|
|
6712
6834
|
label: "~/.kynver/config.json present",
|
|
6713
6835
|
status: exists ? "pass" : "fail",
|
|
@@ -6722,7 +6844,7 @@ function assessUserConfig(probes) {
|
|
|
6722
6844
|
const defaultRepo = config.defaultRepo?.trim();
|
|
6723
6845
|
const displayDefaultRepo = defaultRepo ? displayUserPath(defaultRepo) : null;
|
|
6724
6846
|
checks.push(
|
|
6725
|
-
|
|
6847
|
+
check2({
|
|
6726
6848
|
id: "config_api_base_url",
|
|
6727
6849
|
label: "Default API base URL",
|
|
6728
6850
|
status: apiBaseUrl ? "pass" : "warn",
|
|
@@ -6730,7 +6852,7 @@ function assessUserConfig(probes) {
|
|
|
6730
6852
|
remediation: apiBaseUrl ? void 0 : "Set `apiBaseUrl` via `kynver setup --api-base-url https://\u2026`.",
|
|
6731
6853
|
details: { apiBaseUrl: apiBaseUrl ?? null }
|
|
6732
6854
|
}),
|
|
6733
|
-
|
|
6855
|
+
check2({
|
|
6734
6856
|
id: "config_agent_os_id",
|
|
6735
6857
|
label: "Default AgentOS id",
|
|
6736
6858
|
status: agentOsId ? "pass" : "warn",
|
|
@@ -6738,7 +6860,7 @@ function assessUserConfig(probes) {
|
|
|
6738
6860
|
remediation: agentOsId ? void 0 : "Set `agentOsId` via `kynver setup --agent-os-id <uuid>`.",
|
|
6739
6861
|
details: { agentOsId: agentOsId ?? null, agentOsSlug: config.agentOsSlug ?? null }
|
|
6740
6862
|
}),
|
|
6741
|
-
|
|
6863
|
+
check2({
|
|
6742
6864
|
id: "config_default_repo",
|
|
6743
6865
|
label: "Default repo path",
|
|
6744
6866
|
status: defaultRepo ? "pass" : "warn",
|
|
@@ -6766,7 +6888,7 @@ function assessRunnerToken(probes) {
|
|
|
6766
6888
|
const scopedSaved = Boolean(savedToken) && (!targetAgentOsId || !savedAgentOsId || savedAgentOsId === targetAgentOsId);
|
|
6767
6889
|
const hasScoped = Boolean(envToken?.startsWith("krc1.")) || scopedSaved && savedToken?.startsWith("krc1.");
|
|
6768
6890
|
const checks = [
|
|
6769
|
-
|
|
6891
|
+
check2({
|
|
6770
6892
|
id: "runner_token_scoped",
|
|
6771
6893
|
label: "Scoped runner token (krc1.*) ready",
|
|
6772
6894
|
status: hasScoped ? "pass" : "fail",
|
|
@@ -6779,7 +6901,7 @@ function assessRunnerToken(probes) {
|
|
|
6779
6901
|
credentialsPath: displayCredPath
|
|
6780
6902
|
}
|
|
6781
6903
|
}),
|
|
6782
|
-
|
|
6904
|
+
check2({
|
|
6783
6905
|
id: "runner_token_agent_os_match",
|
|
6784
6906
|
label: "Saved runner token matches configured agentOsId",
|
|
6785
6907
|
status: !savedToken || !targetAgentOsId || !savedAgentOsId || savedAgentOsId === targetAgentOsId ? "pass" : "warn",
|
|
@@ -6787,7 +6909,7 @@ function assessRunnerToken(probes) {
|
|
|
6787
6909
|
remediation: savedToken && targetAgentOsId && savedAgentOsId && savedAgentOsId !== targetAgentOsId ? "`kynver runner credential --agent-os-id <configured-id>` to mint a workspace-bound token." : void 0,
|
|
6788
6910
|
details: { configuredAgentOsId: targetAgentOsId ?? null, savedAgentOsId: savedAgentOsId ?? null }
|
|
6789
6911
|
}),
|
|
6790
|
-
|
|
6912
|
+
check2({
|
|
6791
6913
|
id: "runner_api_key_for_refresh",
|
|
6792
6914
|
label: "API key available for token refresh",
|
|
6793
6915
|
status: creds.hasApiKey || Boolean(process.env.KYNVER_API_KEY?.trim()) ? "pass" : "warn",
|
|
@@ -6806,7 +6928,7 @@ function assessVercelCli(probes) {
|
|
|
6806
6928
|
id: "vercel_cli",
|
|
6807
6929
|
label: "Vercel CLI",
|
|
6808
6930
|
checks: [
|
|
6809
|
-
|
|
6931
|
+
check2({
|
|
6810
6932
|
id: "vercel_installed",
|
|
6811
6933
|
label: "Vercel CLI installed",
|
|
6812
6934
|
status: installed ? "pass" : "warn",
|
|
@@ -6814,7 +6936,7 @@ function assessVercelCli(probes) {
|
|
|
6814
6936
|
remediation: installed ? void 0 : "Install Vercel CLI (`npm i -g vercel`) for deploy evidence and env pulls.",
|
|
6815
6937
|
details: { stderr: version.stderr || null }
|
|
6816
6938
|
}),
|
|
6817
|
-
|
|
6939
|
+
check2({
|
|
6818
6940
|
id: "vercel_authenticated",
|
|
6819
6941
|
label: "Vercel CLI authenticated",
|
|
6820
6942
|
status: !installed ? "warn" : whoami.ok ? "pass" : "warn",
|
|
@@ -6838,14 +6960,14 @@ function assessHarnessDirs(probes) {
|
|
|
6838
6960
|
id: "harness_dirs",
|
|
6839
6961
|
label: "Harness / daemon directories",
|
|
6840
6962
|
checks: [
|
|
6841
|
-
|
|
6963
|
+
check2({
|
|
6842
6964
|
id: "harness_root",
|
|
6843
6965
|
label: "Harness root resolved",
|
|
6844
6966
|
status: "pass",
|
|
6845
6967
|
summary: displayHarnessRoot,
|
|
6846
6968
|
details: { harnessRoot: displayHarnessRoot }
|
|
6847
6969
|
}),
|
|
6848
|
-
|
|
6970
|
+
check2({
|
|
6849
6971
|
id: "harness_runs_dir",
|
|
6850
6972
|
label: "Runs directory ready",
|
|
6851
6973
|
status: runsExists && probes.pathWritable(runsDir) ? "pass" : runsExists ? "warn" : "warn",
|
|
@@ -6853,7 +6975,7 @@ function assessHarnessDirs(probes) {
|
|
|
6853
6975
|
remediation: runsExists && !probes.pathWritable(runsDir) ? `Fix permissions on ${displayRunsDir} or set KYNVER_HARNESS_ROOT to a writable path.` : void 0,
|
|
6854
6976
|
details: { runsDir: displayRunsDir, exists: runsExists, writable: probes.pathWritable(runsDir) }
|
|
6855
6977
|
}),
|
|
6856
|
-
|
|
6978
|
+
check2({
|
|
6857
6979
|
id: "harness_worktrees_dir",
|
|
6858
6980
|
label: "Worktrees directory ready",
|
|
6859
6981
|
status: worktreesExists && probes.pathWritable(worktreesDir) ? "pass" : "warn",
|
|
@@ -6874,7 +6996,7 @@ function assessCallbackAuth(probes) {
|
|
|
6874
6996
|
const creds = probes.readCredentials();
|
|
6875
6997
|
const savedScoped = creds.runnerTokenPrefix?.startsWith("krc1.");
|
|
6876
6998
|
const checks = [
|
|
6877
|
-
|
|
6999
|
+
check2({
|
|
6878
7000
|
id: "callback_base_url",
|
|
6879
7001
|
label: "Callback base URL configured",
|
|
6880
7002
|
status: baseUrl ? usingLegacyBase ? "warn" : "pass" : "fail",
|
|
@@ -6885,7 +7007,7 @@ function assessCallbackAuth(probes) {
|
|
|
6885
7007
|
baseUrl: baseUrl ?? null
|
|
6886
7008
|
}
|
|
6887
7009
|
}),
|
|
6888
|
-
|
|
7010
|
+
check2({
|
|
6889
7011
|
id: "callback_auth_mode",
|
|
6890
7012
|
label: "Callback auth uses scoped runner token",
|
|
6891
7013
|
status: envScoped || savedScoped ? "pass" : legacySecret ? "warn" : "fail",
|
|
@@ -6901,15 +7023,22 @@ function assessCallbackAuth(probes) {
|
|
|
6901
7023
|
}
|
|
6902
7024
|
function assessOpenclawHotspots(probes) {
|
|
6903
7025
|
const env = probes.envSnapshot();
|
|
7026
|
+
const config = probes.loadConfig();
|
|
7027
|
+
const creds = probes.readCredentials();
|
|
6904
7028
|
const harnessRoot = probes.harnessRoot();
|
|
6905
7029
|
const legacyRoot = probes.legacyOpenclawHarnessRoot();
|
|
6906
7030
|
const displayHarnessRoot = redactHomePath(harnessRoot);
|
|
6907
7031
|
const displayLegacyRoot = redactHomePath(legacyRoot);
|
|
6908
7032
|
const displayOpusHarnessRoot = env.opusHarnessRoot ? redactHomePath(env.opusHarnessRoot) : null;
|
|
6909
7033
|
const legacyHarnessActive = harnessRoot === legacyRoot && probes.pathExists(legacyRoot);
|
|
6910
|
-
const
|
|
7034
|
+
const targetAgentOsId = config.agentOsId?.trim();
|
|
7035
|
+
const envToken = env.kynverRunnerTokenPrefix;
|
|
7036
|
+
const savedToken = creds.runnerTokenPrefix;
|
|
7037
|
+
const savedAgentOsId = creds.runnerTokenAgentOsId;
|
|
7038
|
+
const scopedSaved = Boolean(savedToken) && (!targetAgentOsId || !savedAgentOsId || savedAgentOsId === targetAgentOsId);
|
|
7039
|
+
const hasScopedRunnerToken = Boolean(envToken?.startsWith("krc1.")) || scopedSaved && Boolean(savedToken?.startsWith("krc1."));
|
|
6911
7040
|
const checks = [
|
|
6912
|
-
|
|
7041
|
+
check2({
|
|
6913
7042
|
id: "hotspot_legacy_harness_root",
|
|
6914
7043
|
label: "Legacy ~/.openclaw/harness still active",
|
|
6915
7044
|
status: legacyHarnessActive ? "warn" : "pass",
|
|
@@ -6917,7 +7046,7 @@ function assessOpenclawHotspots(probes) {
|
|
|
6917
7046
|
remediation: legacyHarnessActive ? "Set KYNVER_HARNESS_ROOT=~/.kynver/harness (or run setup), migrate artifacts, retire OPUS_HARNESS_ROOT." : env.opusHarnessRoot ? "Prefer KYNVER_HARNESS_ROOT over OPUS_HARNESS_ROOT." : void 0,
|
|
6918
7047
|
details: { harnessRoot: displayHarnessRoot, legacyRoot: displayLegacyRoot, opusHarnessRoot: displayOpusHarnessRoot }
|
|
6919
7048
|
}),
|
|
6920
|
-
|
|
7049
|
+
check2({
|
|
6921
7050
|
id: "hotspot_openclaw_env_secrets",
|
|
6922
7051
|
label: "OpenClaw deployment secrets in runner env",
|
|
6923
7052
|
status: env.openclawCronSecret || env.openclawCronFireBaseUrl ? "warn" : "pass",
|
|
@@ -6927,15 +7056,12 @@ function assessOpenclawHotspots(probes) {
|
|
|
6927
7056
|
].filter(Boolean).join("; ") : "No OpenClaw cron env overrides on this runner",
|
|
6928
7057
|
remediation: env.openclawCronSecret || env.openclawCronFireBaseUrl ? "Move to KYNVER_API_URL + scoped runner tokens; unset OpenClaw cron env on user-hosted runners." : void 0
|
|
6929
7058
|
}),
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
summary: schedulerOpenclaw ? env.kynverSchedulerProvider === "openclaw-cron" ? "KYNVER_SCHEDULER_PROVIDER=openclaw-cron \u2014 AgentOS ticks still routed via OpenClaw local cron adapter" : "KYNVER_SCHEDULER_PROVIDER unset \u2014 server may fall back to openclaw-cron when QStash is absent" : `KYNVER_SCHEDULER_PROVIDER=${env.kynverSchedulerProvider}`,
|
|
6935
|
-
remediation: schedulerOpenclaw ? "On Kynver-hosted scheduler: set KYNVER_SCHEDULER_PROVIDER=qstash where QStash is configured; retire openclaw-cron stub when runtime daemon owns dispatch." : void 0,
|
|
6936
|
-
details: { schedulerProvider: env.kynverSchedulerProvider ?? null }
|
|
7059
|
+
assessRuntimeTakeoverScheduler(env, {
|
|
7060
|
+
agentOsId: targetAgentOsId ?? null,
|
|
7061
|
+
apiBaseUrl: config.apiBaseUrl?.trim() ?? env.kynverApiUrl ?? null,
|
|
7062
|
+
hasScopedRunnerToken
|
|
6937
7063
|
}),
|
|
6938
|
-
|
|
7064
|
+
check2({
|
|
6939
7065
|
id: "hotspot_lease_source_names",
|
|
6940
7066
|
label: "Harness lease/completion source names",
|
|
6941
7067
|
status: "pass",
|