@kynver-app/runtime 0.1.92 → 0.1.95
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 +294 -250
- package/dist/cli.js.map +4 -4
- package/dist/doctor/runtime-takeover.probes.d.ts +0 -2
- package/dist/index.js +562 -484
- package/dist/index.js.map +4 -4
- package/dist/installed-package-versions.d.ts +2 -0
- package/dist/vercel/index.d.ts +2 -1
- package/dist/vercel/vercel-api.d.ts +17 -0
- package/dist/vercel/vercel-evidence.d.ts +15 -15
- package/dist/vercel/vercel-url.d.ts +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -35,306 +35,21 @@ function handleCliVersionFlag(argv, moduleUrl = import.meta.url, binName) {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
// src/memory-cost-package-version-guard.ts
|
|
38
|
-
import { existsSync as
|
|
39
|
-
import
|
|
40
|
-
var MEMORY_COST_PACKAGE_MIN_VERSIONS = {
|
|
41
|
-
"@kynver-app/runtime": "0.1.83",
|
|
42
|
-
"@kynver-app/openclaw-agent-os": "0.1.43",
|
|
43
|
-
"@kynver-app/mcp-agent-os": "0.3.34"
|
|
44
|
-
};
|
|
45
|
-
var MEMORY_COST_MANAGED_PACKAGES = Object.keys(
|
|
46
|
-
MEMORY_COST_PACKAGE_MIN_VERSIONS
|
|
47
|
-
);
|
|
48
|
-
var DISPLAY_NAMES = {
|
|
49
|
-
"@kynver-app/runtime": "Kynver runtime",
|
|
50
|
-
"@kynver-app/openclaw-agent-os": "OpenClaw AgentOS plugin",
|
|
51
|
-
"@kynver-app/mcp-agent-os": "AgentOS MCP server"
|
|
52
|
-
};
|
|
53
|
-
var REPO_PACKAGE_JSON_RELATIVE = {
|
|
54
|
-
"@kynver-app/runtime": "packages/kynver-runtime/package.json",
|
|
55
|
-
"@kynver-app/openclaw-agent-os": "packages/kynver-openclaw-agent-os/package.json",
|
|
56
|
-
"@kynver-app/mcp-agent-os": "packages/kynver-mcp-agent-os/package.json"
|
|
57
|
-
};
|
|
58
|
-
function parseSemverParts(version) {
|
|
59
|
-
const core = version.trim().split("-")[0]?.split("+")[0];
|
|
60
|
-
if (!core) return null;
|
|
61
|
-
const parts = core.split(".");
|
|
62
|
-
if (parts.length < 1 || parts.length > 3) return null;
|
|
63
|
-
const nums = parts.map((p) => Number.parseInt(p, 10));
|
|
64
|
-
if (nums.some((n) => !Number.isFinite(n) || n < 0)) return null;
|
|
65
|
-
while (nums.length < 3) nums.push(0);
|
|
66
|
-
return [nums[0], nums[1], nums[2]];
|
|
67
|
-
}
|
|
68
|
-
function compareSemver(a, b) {
|
|
69
|
-
const pa = parseSemverParts(a);
|
|
70
|
-
const pb = parseSemverParts(b);
|
|
71
|
-
if (!pa || !pb) return 0;
|
|
72
|
-
for (let i = 0; i < 3; i += 1) {
|
|
73
|
-
if (pa[i] > pb[i]) return 1;
|
|
74
|
-
if (pa[i] < pb[i]) return -1;
|
|
75
|
-
}
|
|
76
|
-
return 0;
|
|
77
|
-
}
|
|
78
|
-
function semverAtLeast(version, minimum) {
|
|
79
|
-
return compareSemver(version, minimum) >= 0;
|
|
80
|
-
}
|
|
81
|
-
function maxSemver(versions) {
|
|
82
|
-
let best = null;
|
|
83
|
-
for (const version of versions) {
|
|
84
|
-
if (!best || compareSemver(version, best) > 0) best = version;
|
|
85
|
-
}
|
|
86
|
-
return best;
|
|
87
|
-
}
|
|
88
|
-
function readPackageJsonVersion(packageJsonPath) {
|
|
89
|
-
try {
|
|
90
|
-
const parsed = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
|
|
91
|
-
return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version.trim() : null;
|
|
92
|
-
} catch {
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
function resolveRepoRoot(cwd, explicitRepoRoot) {
|
|
97
|
-
const candidates = [explicitRepoRoot, process.env.KYNVER_REPO, cwd].filter(
|
|
98
|
-
(value) => Boolean(value?.trim())
|
|
99
|
-
);
|
|
100
|
-
for (const candidate of candidates) {
|
|
101
|
-
const resolved = path.resolve(candidate);
|
|
102
|
-
if (existsSync2(path.join(resolved, "packages/kynver-runtime/package.json")) && existsSync2(path.join(resolved, "package.json"))) {
|
|
103
|
-
return resolved;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return null;
|
|
107
|
-
}
|
|
108
|
-
function probeRepoPackageVersions(input = {}) {
|
|
109
|
-
const cwd = input.cwd ?? process.cwd();
|
|
110
|
-
const repoRoot = resolveRepoRoot(cwd, input.repoRoot);
|
|
111
|
-
if (!repoRoot) return {};
|
|
112
|
-
const out = {};
|
|
113
|
-
for (const packageName of MEMORY_COST_MANAGED_PACKAGES) {
|
|
114
|
-
const packageJsonPath = path.join(repoRoot, REPO_PACKAGE_JSON_RELATIVE[packageName]);
|
|
115
|
-
const version = readPackageJsonVersion(packageJsonPath);
|
|
116
|
-
if (!version) continue;
|
|
117
|
-
out[packageName] = { version, source: "repo", path: packageJsonPath };
|
|
118
|
-
}
|
|
119
|
-
return out;
|
|
120
|
-
}
|
|
121
|
-
function repoSourceCommands(packageName) {
|
|
122
|
-
if (packageName === "@kynver-app/runtime") {
|
|
123
|
-
return ["npm run kynver:build", "npm run kynver"];
|
|
124
|
-
}
|
|
125
|
-
return [`npm run build -w ${packageName}`];
|
|
126
|
-
}
|
|
127
|
-
function buildRemediation(input) {
|
|
128
|
-
const { packageName, minimumVersion, effectiveVersion, effectiveSource, repoVersion } = input;
|
|
129
|
-
const lines = [];
|
|
130
|
-
if (repoVersion && semverAtLeast(repoVersion, minimumVersion)) {
|
|
131
|
-
lines.push(
|
|
132
|
-
`Use the monorepo checkout (${repoVersion}) instead of a stale npm install: ${repoSourceCommands(packageName).join("; ")}.`
|
|
133
|
-
);
|
|
134
|
-
lines.push("Do not publish npm packages or wait on an operator release.");
|
|
135
|
-
return lines;
|
|
136
|
-
}
|
|
137
|
-
if (effectiveVersion) {
|
|
138
|
-
lines.push(
|
|
139
|
-
`Upgrade ${packageName} from ${effectiveVersion} to >= ${minimumVersion} (npm install -g ${packageName}@latest or align OpenClaw npm prefix).`
|
|
140
|
-
);
|
|
141
|
-
} else {
|
|
142
|
-
lines.push(`Install ${packageName} >= ${minimumVersion} before running memory-heavy AgentOS paths.`);
|
|
143
|
-
}
|
|
144
|
-
if (packageName === "@kynver-app/runtime") {
|
|
145
|
-
lines.push("Repo-source alternative: npm run kynver:build && npm run kynver");
|
|
146
|
-
}
|
|
147
|
-
if (effectiveSource === "installed" && repoVersion) {
|
|
148
|
-
lines.push(`Repo checkout reports ${repoVersion}; rebuild/link repo source if you develop from the monorepo.`);
|
|
149
|
-
}
|
|
150
|
-
return lines;
|
|
151
|
-
}
|
|
152
|
-
function pickEffectiveCandidate(candidates) {
|
|
153
|
-
if (candidates.length === 0) return { version: null, source: "unknown" };
|
|
154
|
-
const best = maxSemver(candidates.map((candidate) => candidate.version));
|
|
155
|
-
if (!best) return { version: null, source: "unknown" };
|
|
156
|
-
const winner = candidates.find((candidate) => candidate.version === best) ?? candidates[0];
|
|
157
|
-
return { version: winner.version, source: winner.source };
|
|
158
|
-
}
|
|
159
|
-
function evaluateMemoryCostPackageVersionGuard(input = {}) {
|
|
160
|
-
const normalize2 = (value, fallbackSource) => {
|
|
161
|
-
if (!value) return null;
|
|
162
|
-
if (typeof value === "string") return { version: value, source: fallbackSource };
|
|
163
|
-
return value;
|
|
164
|
-
};
|
|
165
|
-
const packages = MEMORY_COST_MANAGED_PACKAGES.map((packageName) => {
|
|
166
|
-
const minimumVersion = MEMORY_COST_PACKAGE_MIN_VERSIONS[packageName];
|
|
167
|
-
const candidates = [];
|
|
168
|
-
const installed = normalize2(input.installed?.[packageName], "installed");
|
|
169
|
-
const repo = normalize2(input.repo?.[packageName], "repo");
|
|
170
|
-
const self = normalize2(input.self?.[packageName], "self");
|
|
171
|
-
if (installed) candidates.push(installed);
|
|
172
|
-
if (repo) candidates.push(repo);
|
|
173
|
-
if (self) candidates.push(self);
|
|
174
|
-
const { version: effectiveVersion, source: effectiveSource } = pickEffectiveCandidate(candidates);
|
|
175
|
-
const repoVersion = repo?.version ?? null;
|
|
176
|
-
const ok2 = effectiveVersion ? semverAtLeast(effectiveVersion, minimumVersion) : false;
|
|
177
|
-
const remediation = ok2 ? [] : buildRemediation({
|
|
178
|
-
packageName,
|
|
179
|
-
minimumVersion,
|
|
180
|
-
effectiveVersion,
|
|
181
|
-
effectiveSource,
|
|
182
|
-
repoVersion
|
|
183
|
-
});
|
|
184
|
-
const summary2 = ok2 ? `${DISPLAY_NAMES[packageName]} ${effectiveVersion} meets memory-cost minimum ${minimumVersion} (${effectiveSource}).` : `${DISPLAY_NAMES[packageName]} is below memory-cost minimum ${minimumVersion}` + (effectiveVersion ? ` (effective ${effectiveVersion} via ${effectiveSource})` : " (no version detected)") + ".";
|
|
185
|
-
return {
|
|
186
|
-
packageName,
|
|
187
|
-
displayName: DISPLAY_NAMES[packageName],
|
|
188
|
-
minimumVersion,
|
|
189
|
-
effectiveVersion,
|
|
190
|
-
effectiveSource,
|
|
191
|
-
ok: ok2,
|
|
192
|
-
summary: summary2,
|
|
193
|
-
remediation
|
|
194
|
-
};
|
|
195
|
-
});
|
|
196
|
-
const violations = packages.filter((row) => !row.ok);
|
|
197
|
-
const ok = violations.length === 0;
|
|
198
|
-
const summary = ok ? "All managed AgentOS packages meet memory-cost minimum versions." : `Memory-cost package guard blocked ${violations.length} stale package(s): ${violations.map((row) => `${row.packageName} < ${row.minimumVersion}`).join("; ")}.`;
|
|
199
|
-
return { ok, summary, packages };
|
|
200
|
-
}
|
|
201
|
-
var MemoryCostPackageVersionGuardError = class extends Error {
|
|
202
|
-
result;
|
|
203
|
-
constructor(result) {
|
|
204
|
-
const lines = [
|
|
205
|
-
result.summary,
|
|
206
|
-
...result.packages.filter((row) => !row.ok).flatMap((row) => [`- ${row.summary}`, ...row.remediation.map((line) => ` \u2192 ${line}`)])
|
|
207
|
-
];
|
|
208
|
-
super(lines.join("\n"));
|
|
209
|
-
this.name = "MemoryCostPackageVersionGuardError";
|
|
210
|
-
this.result = result;
|
|
211
|
-
}
|
|
212
|
-
};
|
|
213
|
-
function assertMemoryCostPackageVersionGuard(input = {}) {
|
|
214
|
-
const result = evaluateMemoryCostPackageVersionGuard(input);
|
|
215
|
-
if (!result.ok) throw new MemoryCostPackageVersionGuardError(result);
|
|
216
|
-
return result;
|
|
217
|
-
}
|
|
218
|
-
function formatMemoryCostPackageGuardError(result) {
|
|
219
|
-
return new MemoryCostPackageVersionGuardError(result).message;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// src/installed-package-versions.ts
|
|
223
|
-
import { readFile } from "node:fs/promises";
|
|
224
|
-
import { homedir } from "node:os";
|
|
225
|
-
import path2 from "node:path";
|
|
226
|
-
var MANAGED_PACKAGES = [
|
|
227
|
-
"@kynver-app/runtime",
|
|
228
|
-
"@kynver-app/openclaw-agent-os",
|
|
229
|
-
"@kynver-app/mcp-agent-os"
|
|
230
|
-
];
|
|
231
|
-
function trim(value) {
|
|
232
|
-
const out = value?.trim();
|
|
233
|
-
return out ? out : null;
|
|
234
|
-
}
|
|
235
|
-
function unique(values) {
|
|
236
|
-
return [...new Set(values.filter((value) => Boolean(value)))];
|
|
237
|
-
}
|
|
238
|
-
function moduleRoots() {
|
|
239
|
-
const home = homedir();
|
|
240
|
-
const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ?? path2.join(home, ".openclaw", "npm");
|
|
241
|
-
const npmGlobalRoot = trim(process.env.KYNVER_NPM_GLOBAL_ROOT) ?? trim(process.env.KYNVER_NPM_GLOBAL_MODULES_ROOT) ?? (trim(process.env.NPM_CONFIG_PREFIX) ? path2.join(trim(process.env.NPM_CONFIG_PREFIX), "lib", "node_modules") : path2.join(home, ".npm-global", "lib", "node_modules"));
|
|
242
|
-
return unique([
|
|
243
|
-
path2.join(openClawPrefix, "lib", "node_modules"),
|
|
244
|
-
path2.join(openClawPrefix, "node_modules"),
|
|
245
|
-
npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot : path2.join(npmGlobalRoot, "lib", "node_modules")
|
|
246
|
-
]);
|
|
247
|
-
}
|
|
248
|
-
async function readVersion(packageJsonPath) {
|
|
249
|
-
try {
|
|
250
|
-
const parsed = JSON.parse(await readFile(packageJsonPath, "utf8"));
|
|
251
|
-
return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version.trim() : null;
|
|
252
|
-
} catch {
|
|
253
|
-
return null;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
async function collectInstalledPackageVersions(observedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
257
|
-
const roots = moduleRoots();
|
|
258
|
-
const out = {};
|
|
259
|
-
for (const packageName of MANAGED_PACKAGES) {
|
|
260
|
-
for (const root of roots) {
|
|
261
|
-
const packageJsonPath = path2.join(root, packageName, "package.json");
|
|
262
|
-
const version = await readVersion(packageJsonPath);
|
|
263
|
-
if (!version) continue;
|
|
264
|
-
out[packageName] = { version, observedAt, path: packageJsonPath };
|
|
265
|
-
break;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
return out;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// src/memory-cost-package-version-guard-enforce.ts
|
|
272
|
-
function installedVersionMap(observed) {
|
|
273
|
-
const out = {};
|
|
274
|
-
for (const [packageName, row] of Object.entries(observed)) {
|
|
275
|
-
if (!row?.version) continue;
|
|
276
|
-
out[packageName] = {
|
|
277
|
-
version: row.version,
|
|
278
|
-
source: "installed",
|
|
279
|
-
path: row.path
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
return out;
|
|
283
|
-
}
|
|
284
|
-
async function buildMemoryCostPackageGuardInput(input = {}) {
|
|
285
|
-
const [installed, repo] = await Promise.all([
|
|
286
|
-
collectInstalledPackageVersions(),
|
|
287
|
-
Promise.resolve(probeRepoPackageVersions({ cwd: input.cwd, repoRoot: input.repoRoot }))
|
|
288
|
-
]);
|
|
289
|
-
const self = {};
|
|
290
|
-
if (input.selfPackageName && input.selfVersion) {
|
|
291
|
-
self[input.selfPackageName] = { version: input.selfVersion, source: "self" };
|
|
292
|
-
} else {
|
|
293
|
-
self["@kynver-app/runtime"] = { version: PACKAGE_VERSION, source: "self" };
|
|
294
|
-
}
|
|
295
|
-
return {
|
|
296
|
-
installed: installedVersionMap(installed),
|
|
297
|
-
repo,
|
|
298
|
-
self
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
async function evaluateMemoryCostPackageGuardAtStartup(input = {}) {
|
|
302
|
-
const guardInput = await buildMemoryCostPackageGuardInput(input);
|
|
303
|
-
return evaluateMemoryCostPackageVersionGuard(guardInput);
|
|
304
|
-
}
|
|
305
|
-
async function enforceMemoryCostPackageGuardAtStartup(input = {}) {
|
|
306
|
-
const guardInput = await buildMemoryCostPackageGuardInput(input);
|
|
307
|
-
return assertMemoryCostPackageVersionGuard(guardInput);
|
|
308
|
-
}
|
|
309
|
-
function shouldEnforceMemoryCostPackageGuardCli(scope, action) {
|
|
310
|
-
if (!scope) return false;
|
|
311
|
-
if (scope === "daemon") return true;
|
|
312
|
-
if (scope === "worker") return true;
|
|
313
|
-
if (scope === "monitor") return true;
|
|
314
|
-
if (scope === "run" && (action === "dispatch" || action === "sweep" || action === "reconcile" || action === "unblock")) {
|
|
315
|
-
return true;
|
|
316
|
-
}
|
|
317
|
-
if (scope === "cron" && action === "tick") return true;
|
|
318
|
-
return false;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
// src/config.ts
|
|
322
|
-
import { existsSync as existsSync11, mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync2 } from "node:fs";
|
|
323
|
-
import { homedir as homedir5, totalmem } from "node:os";
|
|
324
|
-
import path10 from "node:path";
|
|
38
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
39
|
+
import path4 from "node:path";
|
|
325
40
|
|
|
326
41
|
// src/default-repo-discovery.ts
|
|
327
|
-
import { existsSync as
|
|
328
|
-
import { homedir as
|
|
329
|
-
import
|
|
42
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
43
|
+
import { homedir as homedir2 } from "node:os";
|
|
44
|
+
import path3 from "node:path";
|
|
330
45
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
331
46
|
|
|
332
47
|
// src/git.ts
|
|
333
48
|
import { spawnSync } from "node:child_process";
|
|
334
49
|
|
|
335
50
|
// src/util.ts
|
|
336
|
-
import { existsSync as
|
|
337
|
-
import
|
|
51
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
52
|
+
import path from "node:path";
|
|
338
53
|
function fail(message) {
|
|
339
54
|
console.error(message);
|
|
340
55
|
process.exit(1);
|
|
@@ -356,14 +71,14 @@ function safeJson(line) {
|
|
|
356
71
|
}
|
|
357
72
|
function readJson(file, fallback) {
|
|
358
73
|
try {
|
|
359
|
-
return JSON.parse(
|
|
74
|
+
return JSON.parse(readFileSync2(file, "utf8"));
|
|
360
75
|
} catch (error) {
|
|
361
76
|
if (arguments.length > 1) return fallback;
|
|
362
77
|
fail(`failed to read ${file}: ${error.message}`);
|
|
363
78
|
}
|
|
364
79
|
}
|
|
365
80
|
function writeJson(file, value) {
|
|
366
|
-
mkdirSync(
|
|
81
|
+
mkdirSync(path.dirname(file), { recursive: true });
|
|
367
82
|
writeFileSync(file, `${JSON.stringify(value, null, 2)}
|
|
368
83
|
`);
|
|
369
84
|
}
|
|
@@ -394,12 +109,12 @@ function fileMtime(file) {
|
|
|
394
109
|
}
|
|
395
110
|
}
|
|
396
111
|
function tailFile(file, lines) {
|
|
397
|
-
if (!
|
|
398
|
-
const data =
|
|
112
|
+
if (!existsSync2(file)) return "";
|
|
113
|
+
const data = readFileSync2(file, "utf8");
|
|
399
114
|
return data.split("\n").slice(-lines).join("\n");
|
|
400
115
|
}
|
|
401
116
|
function readMaybeFile(file) {
|
|
402
|
-
return file ?
|
|
117
|
+
return file ? readFileSync2(path.resolve(file), "utf8") : "";
|
|
403
118
|
}
|
|
404
119
|
function sleepMs(ms) {
|
|
405
120
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
@@ -586,124 +301,433 @@ function computeGitAncestry(worktreePath, baseOrOptions = "origin/main") {
|
|
|
586
301
|
...error ? { error } : {}
|
|
587
302
|
};
|
|
588
303
|
}
|
|
589
|
-
const relation = baseIsAncestorOfHead.isAncestor ? "ahead" : headIsAncestorOfBase.isAncestor ? "merged" : "diverged";
|
|
590
|
-
return {
|
|
591
|
-
checked: true,
|
|
592
|
-
base: baseLabel,
|
|
593
|
-
head: headSha,
|
|
594
|
-
baseHead: baseSha,
|
|
595
|
-
baseIsAncestorOfHead: baseIsAncestorOfHead.isAncestor,
|
|
596
|
-
headIsAncestorOfBase: headIsAncestorOfBase.isAncestor,
|
|
597
|
-
relation,
|
|
598
|
-
...error ? { error } : {}
|
|
599
|
-
};
|
|
304
|
+
const relation = baseIsAncestorOfHead.isAncestor ? "ahead" : headIsAncestorOfBase.isAncestor ? "merged" : "diverged";
|
|
305
|
+
return {
|
|
306
|
+
checked: true,
|
|
307
|
+
base: baseLabel,
|
|
308
|
+
head: headSha,
|
|
309
|
+
baseHead: baseSha,
|
|
310
|
+
baseIsAncestorOfHead: baseIsAncestorOfHead.isAncestor,
|
|
311
|
+
headIsAncestorOfBase: headIsAncestorOfBase.isAncestor,
|
|
312
|
+
relation,
|
|
313
|
+
...error ? { error } : {}
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
function unknownAncestry(base, error, head = null) {
|
|
317
|
+
return {
|
|
318
|
+
checked: false,
|
|
319
|
+
base,
|
|
320
|
+
head,
|
|
321
|
+
baseHead: null,
|
|
322
|
+
baseIsAncestorOfHead: null,
|
|
323
|
+
headIsAncestorOfBase: null,
|
|
324
|
+
relation: "unknown",
|
|
325
|
+
error
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// src/path-values.ts
|
|
330
|
+
import { homedir } from "node:os";
|
|
331
|
+
import path2 from "node:path";
|
|
332
|
+
function expandHomePath(value) {
|
|
333
|
+
if (value === "~") return homedir();
|
|
334
|
+
if (value.startsWith("~/") || value.startsWith("~\\")) {
|
|
335
|
+
return path2.join(homedir(), value.slice(2));
|
|
336
|
+
}
|
|
337
|
+
return value;
|
|
338
|
+
}
|
|
339
|
+
function resolveUserPath(value) {
|
|
340
|
+
return path2.resolve(expandHomePath(value));
|
|
341
|
+
}
|
|
342
|
+
function redactHomePath(value) {
|
|
343
|
+
const expanded = expandHomePath(value);
|
|
344
|
+
const resolved = path2.resolve(expanded);
|
|
345
|
+
const home = path2.resolve(homedir());
|
|
346
|
+
if (resolved === home) return "~";
|
|
347
|
+
if (resolved.startsWith(`${home}${path2.sep}`)) {
|
|
348
|
+
return `~/${path2.relative(home, resolved).split(path2.sep).join("/")}`;
|
|
349
|
+
}
|
|
350
|
+
return resolved.replace(/^\/home\/[^/]+(?=\/|$)/, "~").replace(/^\/Users\/[^/]+(?=\/|$)/, "~");
|
|
351
|
+
}
|
|
352
|
+
function displayUserPath(value) {
|
|
353
|
+
return redactHomePath(value);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// src/default-repo-discovery.ts
|
|
357
|
+
var WELL_KNOWN_REPO_DIRS = [
|
|
358
|
+
"Kynver",
|
|
359
|
+
"repos/Kynver",
|
|
360
|
+
"repos/kynver-source-main",
|
|
361
|
+
"code/Kynver",
|
|
362
|
+
"projects/Kynver"
|
|
363
|
+
];
|
|
364
|
+
function readPackageName(repoRoot) {
|
|
365
|
+
const pkgPath = path3.join(repoRoot, "package.json");
|
|
366
|
+
if (!existsSync3(pkgPath)) return null;
|
|
367
|
+
try {
|
|
368
|
+
const pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
|
|
369
|
+
return typeof pkg.name === "string" ? pkg.name.trim() : null;
|
|
370
|
+
} catch {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function isKynverMonorepoRoot(repoRoot) {
|
|
375
|
+
return readPackageName(repoRoot) === "kynver";
|
|
376
|
+
}
|
|
377
|
+
function gitRepoRoot(startDir) {
|
|
378
|
+
const resolvedStart = path3.resolve(startDir);
|
|
379
|
+
if (!existsSync3(resolvedStart)) return null;
|
|
380
|
+
const probe = gitCapture(resolvedStart, ["rev-parse", "--show-toplevel"]);
|
|
381
|
+
if (probe.status !== 0) return null;
|
|
382
|
+
const root = probe.stdout.trim();
|
|
383
|
+
return root.length ? path3.resolve(root) : null;
|
|
384
|
+
}
|
|
385
|
+
function resolveRuntimePackageRoot(moduleUrl = import.meta.url) {
|
|
386
|
+
let dir = path3.dirname(fileURLToPath2(moduleUrl));
|
|
387
|
+
for (let depth = 0; depth < 8; depth += 1) {
|
|
388
|
+
const pkgPath = path3.join(dir, "package.json");
|
|
389
|
+
if (existsSync3(pkgPath)) {
|
|
390
|
+
try {
|
|
391
|
+
const pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
|
|
392
|
+
if (pkg.name === "@kynver-app/runtime") return dir;
|
|
393
|
+
} catch {
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
const parent = path3.dirname(dir);
|
|
397
|
+
if (parent === dir) break;
|
|
398
|
+
dir = parent;
|
|
399
|
+
}
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
function pushCandidate(seen, out, repo, source) {
|
|
403
|
+
if (!repo) return;
|
|
404
|
+
const resolved = path3.resolve(repo);
|
|
405
|
+
if (seen.has(resolved)) return;
|
|
406
|
+
if (!isKynverMonorepoRoot(resolved)) return;
|
|
407
|
+
seen.add(resolved);
|
|
408
|
+
out.push({ repo: resolved, source });
|
|
409
|
+
}
|
|
410
|
+
function discoverDefaultRepoCandidates(opts) {
|
|
411
|
+
const cwd = opts?.cwd ?? process.cwd();
|
|
412
|
+
const seen = /* @__PURE__ */ new Set();
|
|
413
|
+
const candidates = [];
|
|
414
|
+
pushCandidate(seen, candidates, gitRepoRoot(cwd), "cwd_git");
|
|
415
|
+
const runtimePkgRoot = resolveRuntimePackageRoot(opts?.runtimeModuleUrl ?? import.meta.url);
|
|
416
|
+
if (runtimePkgRoot) {
|
|
417
|
+
pushCandidate(seen, candidates, gitRepoRoot(runtimePkgRoot), "runtime_checkout");
|
|
418
|
+
}
|
|
419
|
+
const home = homedir2();
|
|
420
|
+
for (const rel of WELL_KNOWN_REPO_DIRS) {
|
|
421
|
+
pushCandidate(seen, candidates, resolveUserPath(path3.join(home, rel)), "well_known_path");
|
|
422
|
+
}
|
|
423
|
+
return candidates;
|
|
424
|
+
}
|
|
425
|
+
function discoverDefaultRepo(opts) {
|
|
426
|
+
return discoverDefaultRepoCandidates(opts)[0] ?? null;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// src/memory-cost-package-version-guard.ts
|
|
430
|
+
var MEMORY_COST_PACKAGE_MIN_VERSIONS = {
|
|
431
|
+
"@kynver-app/runtime": "0.1.83",
|
|
432
|
+
"@kynver-app/openclaw-agent-os": "0.1.43",
|
|
433
|
+
"@kynver-app/mcp-agent-os": "0.3.34"
|
|
434
|
+
};
|
|
435
|
+
var MEMORY_COST_MANAGED_PACKAGES = Object.keys(
|
|
436
|
+
MEMORY_COST_PACKAGE_MIN_VERSIONS
|
|
437
|
+
);
|
|
438
|
+
var DISPLAY_NAMES = {
|
|
439
|
+
"@kynver-app/runtime": "Kynver runtime",
|
|
440
|
+
"@kynver-app/openclaw-agent-os": "OpenClaw AgentOS plugin",
|
|
441
|
+
"@kynver-app/mcp-agent-os": "AgentOS MCP server"
|
|
442
|
+
};
|
|
443
|
+
var REPO_PACKAGE_JSON_RELATIVE = {
|
|
444
|
+
"@kynver-app/runtime": "packages/kynver-runtime/package.json",
|
|
445
|
+
"@kynver-app/openclaw-agent-os": "packages/kynver-openclaw-agent-os/package.json",
|
|
446
|
+
"@kynver-app/mcp-agent-os": "packages/kynver-mcp-agent-os/package.json"
|
|
447
|
+
};
|
|
448
|
+
function parseSemverParts(version) {
|
|
449
|
+
const core = version.trim().split("-")[0]?.split("+")[0];
|
|
450
|
+
if (!core) return null;
|
|
451
|
+
const parts = core.split(".");
|
|
452
|
+
if (parts.length < 1 || parts.length > 3) return null;
|
|
453
|
+
const nums = parts.map((p) => Number.parseInt(p, 10));
|
|
454
|
+
if (nums.some((n) => !Number.isFinite(n) || n < 0)) return null;
|
|
455
|
+
while (nums.length < 3) nums.push(0);
|
|
456
|
+
return [nums[0], nums[1], nums[2]];
|
|
457
|
+
}
|
|
458
|
+
function compareSemver(a, b) {
|
|
459
|
+
const pa = parseSemverParts(a);
|
|
460
|
+
const pb = parseSemverParts(b);
|
|
461
|
+
if (!pa || !pb) return 0;
|
|
462
|
+
for (let i = 0; i < 3; i += 1) {
|
|
463
|
+
if (pa[i] > pb[i]) return 1;
|
|
464
|
+
if (pa[i] < pb[i]) return -1;
|
|
465
|
+
}
|
|
466
|
+
return 0;
|
|
467
|
+
}
|
|
468
|
+
function semverAtLeast(version, minimum) {
|
|
469
|
+
return compareSemver(version, minimum) >= 0;
|
|
470
|
+
}
|
|
471
|
+
function maxSemver(versions) {
|
|
472
|
+
let best = null;
|
|
473
|
+
for (const version of versions) {
|
|
474
|
+
if (!best || compareSemver(version, best) > 0) best = version;
|
|
475
|
+
}
|
|
476
|
+
return best;
|
|
477
|
+
}
|
|
478
|
+
function readPackageJsonVersion(packageJsonPath) {
|
|
479
|
+
try {
|
|
480
|
+
const parsed = JSON.parse(readFileSync4(packageJsonPath, "utf8"));
|
|
481
|
+
return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version.trim() : null;
|
|
482
|
+
} catch {
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
function resolveRepoRoot(cwd, explicitRepoRoot) {
|
|
487
|
+
const candidates = [explicitRepoRoot, process.env.KYNVER_REPO, cwd].filter(
|
|
488
|
+
(value) => Boolean(value?.trim())
|
|
489
|
+
);
|
|
490
|
+
for (const candidate of candidates) {
|
|
491
|
+
const resolved = path4.resolve(candidate);
|
|
492
|
+
if (existsSync4(path4.join(resolved, "packages/kynver-runtime/package.json")) && existsSync4(path4.join(resolved, "package.json"))) {
|
|
493
|
+
return resolved;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
const discovered = discoverDefaultRepo({ cwd });
|
|
497
|
+
return discovered?.repo ?? null;
|
|
498
|
+
}
|
|
499
|
+
function probeRepoPackageVersions(input = {}) {
|
|
500
|
+
const cwd = input.cwd ?? process.cwd();
|
|
501
|
+
const repoRoot = resolveRepoRoot(cwd, input.repoRoot);
|
|
502
|
+
if (!repoRoot) return {};
|
|
503
|
+
const out = {};
|
|
504
|
+
for (const packageName of MEMORY_COST_MANAGED_PACKAGES) {
|
|
505
|
+
const packageJsonPath = path4.join(repoRoot, REPO_PACKAGE_JSON_RELATIVE[packageName]);
|
|
506
|
+
const version = readPackageJsonVersion(packageJsonPath);
|
|
507
|
+
if (!version) continue;
|
|
508
|
+
out[packageName] = { version, source: "repo", path: packageJsonPath };
|
|
509
|
+
}
|
|
510
|
+
return out;
|
|
511
|
+
}
|
|
512
|
+
function repoSourceCommands(packageName) {
|
|
513
|
+
if (packageName === "@kynver-app/runtime") {
|
|
514
|
+
return ["npm run kynver:build", "npm run kynver"];
|
|
515
|
+
}
|
|
516
|
+
return [`npm run build -w ${packageName}`];
|
|
517
|
+
}
|
|
518
|
+
function buildRemediation(input) {
|
|
519
|
+
const { packageName, minimumVersion, effectiveVersion, effectiveSource, repoVersion } = input;
|
|
520
|
+
const lines = [];
|
|
521
|
+
if (repoVersion && semverAtLeast(repoVersion, minimumVersion)) {
|
|
522
|
+
lines.push(
|
|
523
|
+
`Use the monorepo checkout (${repoVersion}) instead of a stale npm install: ${repoSourceCommands(packageName).join("; ")}.`
|
|
524
|
+
);
|
|
525
|
+
lines.push("Do not publish npm packages or wait on an operator release.");
|
|
526
|
+
return lines;
|
|
527
|
+
}
|
|
528
|
+
if (effectiveVersion) {
|
|
529
|
+
lines.push(
|
|
530
|
+
`Upgrade ${packageName} from ${effectiveVersion} to >= ${minimumVersion} (npm install -g ${packageName}@latest or align OpenClaw npm prefix).`
|
|
531
|
+
);
|
|
532
|
+
} else {
|
|
533
|
+
lines.push(`Install ${packageName} >= ${minimumVersion} before running memory-heavy AgentOS paths.`);
|
|
534
|
+
}
|
|
535
|
+
if (packageName === "@kynver-app/runtime") {
|
|
536
|
+
lines.push("Repo-source alternative: npm run kynver:build && npm run kynver");
|
|
537
|
+
}
|
|
538
|
+
if (effectiveSource === "installed" && repoVersion) {
|
|
539
|
+
lines.push(`Repo checkout reports ${repoVersion}; rebuild/link repo source if you develop from the monorepo.`);
|
|
540
|
+
}
|
|
541
|
+
return lines;
|
|
542
|
+
}
|
|
543
|
+
function pickEffectiveCandidate(candidates) {
|
|
544
|
+
if (candidates.length === 0) return { version: null, source: "unknown" };
|
|
545
|
+
const best = maxSemver(candidates.map((candidate) => candidate.version));
|
|
546
|
+
if (!best) return { version: null, source: "unknown" };
|
|
547
|
+
const winner = candidates.find((candidate) => candidate.version === best) ?? candidates[0];
|
|
548
|
+
return { version: winner.version, source: winner.source };
|
|
549
|
+
}
|
|
550
|
+
function evaluateMemoryCostPackageVersionGuard(input = {}) {
|
|
551
|
+
const normalize2 = (value, fallbackSource) => {
|
|
552
|
+
if (!value) return null;
|
|
553
|
+
if (typeof value === "string") return { version: value, source: fallbackSource };
|
|
554
|
+
return value;
|
|
555
|
+
};
|
|
556
|
+
const packages = MEMORY_COST_MANAGED_PACKAGES.map((packageName) => {
|
|
557
|
+
const minimumVersion = MEMORY_COST_PACKAGE_MIN_VERSIONS[packageName];
|
|
558
|
+
const candidates = [];
|
|
559
|
+
const installed = normalize2(input.installed?.[packageName], "installed");
|
|
560
|
+
const repo = normalize2(input.repo?.[packageName], "repo");
|
|
561
|
+
const self = normalize2(input.self?.[packageName], "self");
|
|
562
|
+
if (installed) candidates.push(installed);
|
|
563
|
+
if (repo) candidates.push(repo);
|
|
564
|
+
if (self) candidates.push(self);
|
|
565
|
+
const { version: effectiveVersion, source: effectiveSource } = pickEffectiveCandidate(candidates);
|
|
566
|
+
const repoVersion = repo?.version ?? null;
|
|
567
|
+
const ok2 = effectiveVersion ? semverAtLeast(effectiveVersion, minimumVersion) : false;
|
|
568
|
+
const remediation = ok2 ? [] : buildRemediation({
|
|
569
|
+
packageName,
|
|
570
|
+
minimumVersion,
|
|
571
|
+
effectiveVersion,
|
|
572
|
+
effectiveSource,
|
|
573
|
+
repoVersion
|
|
574
|
+
});
|
|
575
|
+
const summary2 = ok2 ? `${DISPLAY_NAMES[packageName]} ${effectiveVersion} meets memory-cost minimum ${minimumVersion} (${effectiveSource}).` : `${DISPLAY_NAMES[packageName]} is below memory-cost minimum ${minimumVersion}` + (effectiveVersion ? ` (effective ${effectiveVersion} via ${effectiveSource})` : " (no version detected)") + ".";
|
|
576
|
+
return {
|
|
577
|
+
packageName,
|
|
578
|
+
displayName: DISPLAY_NAMES[packageName],
|
|
579
|
+
minimumVersion,
|
|
580
|
+
effectiveVersion,
|
|
581
|
+
effectiveSource,
|
|
582
|
+
ok: ok2,
|
|
583
|
+
summary: summary2,
|
|
584
|
+
remediation
|
|
585
|
+
};
|
|
586
|
+
});
|
|
587
|
+
const violations = packages.filter((row) => !row.ok);
|
|
588
|
+
const ok = violations.length === 0;
|
|
589
|
+
const summary = ok ? "All managed AgentOS packages meet memory-cost minimum versions." : `Memory-cost package guard blocked ${violations.length} stale package(s): ${violations.map((row) => `${row.packageName} < ${row.minimumVersion}`).join("; ")}.`;
|
|
590
|
+
return { ok, summary, packages };
|
|
591
|
+
}
|
|
592
|
+
var MemoryCostPackageVersionGuardError = class extends Error {
|
|
593
|
+
result;
|
|
594
|
+
constructor(result) {
|
|
595
|
+
const lines = [
|
|
596
|
+
result.summary,
|
|
597
|
+
...result.packages.filter((row) => !row.ok).flatMap((row) => [`- ${row.summary}`, ...row.remediation.map((line) => ` \u2192 ${line}`)])
|
|
598
|
+
];
|
|
599
|
+
super(lines.join("\n"));
|
|
600
|
+
this.name = "MemoryCostPackageVersionGuardError";
|
|
601
|
+
this.result = result;
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
function assertMemoryCostPackageVersionGuard(input = {}) {
|
|
605
|
+
const result = evaluateMemoryCostPackageVersionGuard(input);
|
|
606
|
+
if (!result.ok) throw new MemoryCostPackageVersionGuardError(result);
|
|
607
|
+
return result;
|
|
600
608
|
}
|
|
601
|
-
function
|
|
602
|
-
return
|
|
603
|
-
checked: false,
|
|
604
|
-
base,
|
|
605
|
-
head,
|
|
606
|
-
baseHead: null,
|
|
607
|
-
baseIsAncestorOfHead: null,
|
|
608
|
-
headIsAncestorOfBase: null,
|
|
609
|
-
relation: "unknown",
|
|
610
|
-
error
|
|
611
|
-
};
|
|
609
|
+
function formatMemoryCostPackageGuardError(result) {
|
|
610
|
+
return new MemoryCostPackageVersionGuardError(result).message;
|
|
612
611
|
}
|
|
613
612
|
|
|
614
|
-
// src/
|
|
615
|
-
import {
|
|
616
|
-
import
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
return
|
|
613
|
+
// src/installed-package-versions.ts
|
|
614
|
+
import { readFile } from "node:fs/promises";
|
|
615
|
+
import { homedir as homedir3 } from "node:os";
|
|
616
|
+
import path5 from "node:path";
|
|
617
|
+
var MANAGED_PACKAGES = [
|
|
618
|
+
"@kynver-app/runtime",
|
|
619
|
+
"@kynver-app/openclaw-agent-os",
|
|
620
|
+
"@kynver-app/mcp-agent-os"
|
|
621
|
+
];
|
|
622
|
+
function trim(value) {
|
|
623
|
+
const out = value?.trim();
|
|
624
|
+
return out ? out : null;
|
|
626
625
|
}
|
|
627
|
-
function
|
|
628
|
-
|
|
629
|
-
const resolved = path4.resolve(expanded);
|
|
630
|
-
const home = path4.resolve(homedir2());
|
|
631
|
-
if (resolved === home) return "~";
|
|
632
|
-
if (resolved.startsWith(`${home}${path4.sep}`)) {
|
|
633
|
-
return `~/${path4.relative(home, resolved).split(path4.sep).join("/")}`;
|
|
634
|
-
}
|
|
635
|
-
return resolved.replace(/^\/home\/[^/]+(?=\/|$)/, "~").replace(/^\/Users\/[^/]+(?=\/|$)/, "~");
|
|
626
|
+
function unique(values) {
|
|
627
|
+
return [...new Set(values.filter((value) => Boolean(value)))];
|
|
636
628
|
}
|
|
637
|
-
function
|
|
638
|
-
|
|
629
|
+
function moduleRoots() {
|
|
630
|
+
const home = homedir3();
|
|
631
|
+
const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ?? path5.join(home, ".openclaw", "npm");
|
|
632
|
+
const npmGlobalRoot = trim(process.env.KYNVER_NPM_GLOBAL_ROOT) ?? trim(process.env.KYNVER_NPM_GLOBAL_MODULES_ROOT) ?? (trim(process.env.NPM_CONFIG_PREFIX) ? path5.join(trim(process.env.NPM_CONFIG_PREFIX), "lib", "node_modules") : path5.join(home, ".npm-global", "lib", "node_modules"));
|
|
633
|
+
return unique([
|
|
634
|
+
path5.join(openClawPrefix, "lib", "node_modules"),
|
|
635
|
+
path5.join(openClawPrefix, "node_modules"),
|
|
636
|
+
npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot : path5.join(npmGlobalRoot, "lib", "node_modules")
|
|
637
|
+
]);
|
|
639
638
|
}
|
|
640
|
-
|
|
641
|
-
// src/default-repo-discovery.ts
|
|
642
|
-
var WELL_KNOWN_REPO_DIRS = ["Kynver", "repos/Kynver", "code/Kynver", "projects/Kynver"];
|
|
643
|
-
function readPackageName(repoRoot) {
|
|
644
|
-
const pkgPath = path5.join(repoRoot, "package.json");
|
|
645
|
-
if (!existsSync4(pkgPath)) return null;
|
|
639
|
+
async function readVersion(packageJsonPath) {
|
|
646
640
|
try {
|
|
647
|
-
const
|
|
648
|
-
return typeof
|
|
641
|
+
const parsed = JSON.parse(await readFile(packageJsonPath, "utf8"));
|
|
642
|
+
return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version.trim() : null;
|
|
649
643
|
} catch {
|
|
650
644
|
return null;
|
|
651
645
|
}
|
|
652
646
|
}
|
|
653
|
-
function
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
const
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
647
|
+
function installedPackageJsonCandidates(packageName) {
|
|
648
|
+
const roots = moduleRoots();
|
|
649
|
+
const seen = /* @__PURE__ */ new Set();
|
|
650
|
+
const out = [];
|
|
651
|
+
for (const root of roots) {
|
|
652
|
+
const candidate = path5.join(root, packageName, "package.json");
|
|
653
|
+
if (seen.has(candidate)) continue;
|
|
654
|
+
seen.add(candidate);
|
|
655
|
+
out.push(candidate);
|
|
656
|
+
}
|
|
657
|
+
return out;
|
|
663
658
|
}
|
|
664
|
-
function
|
|
665
|
-
|
|
666
|
-
for (
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
659
|
+
async function collectInstalledPackageVersions(observedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
660
|
+
const out = {};
|
|
661
|
+
for (const packageName of MANAGED_PACKAGES) {
|
|
662
|
+
let best = null;
|
|
663
|
+
for (const packageJsonPath of installedPackageJsonCandidates(packageName)) {
|
|
664
|
+
const version = await readVersion(packageJsonPath);
|
|
665
|
+
if (!version) continue;
|
|
666
|
+
if (!best || compareSemver(version, best.version) > 0) {
|
|
667
|
+
best = { version, path: packageJsonPath };
|
|
673
668
|
}
|
|
674
669
|
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
670
|
+
if (best) {
|
|
671
|
+
out[packageName] = { version: best.version, observedAt, path: best.path };
|
|
672
|
+
}
|
|
678
673
|
}
|
|
679
|
-
return
|
|
680
|
-
}
|
|
681
|
-
function pushCandidate(seen, out, repo, source) {
|
|
682
|
-
if (!repo) return;
|
|
683
|
-
const resolved = path5.resolve(repo);
|
|
684
|
-
if (seen.has(resolved)) return;
|
|
685
|
-
if (!isKynverMonorepoRoot(resolved)) return;
|
|
686
|
-
seen.add(resolved);
|
|
687
|
-
out.push({ repo: resolved, source });
|
|
674
|
+
return out;
|
|
688
675
|
}
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
const
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
676
|
+
|
|
677
|
+
// src/memory-cost-package-version-guard-enforce.ts
|
|
678
|
+
function installedVersionMap(observed) {
|
|
679
|
+
const out = {};
|
|
680
|
+
for (const [packageName, row] of Object.entries(observed)) {
|
|
681
|
+
if (!row?.version) continue;
|
|
682
|
+
out[packageName] = {
|
|
683
|
+
version: row.version,
|
|
684
|
+
source: "installed",
|
|
685
|
+
path: row.path
|
|
686
|
+
};
|
|
697
687
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
688
|
+
return out;
|
|
689
|
+
}
|
|
690
|
+
async function buildMemoryCostPackageGuardInput(input = {}) {
|
|
691
|
+
const [installed, repo] = await Promise.all([
|
|
692
|
+
collectInstalledPackageVersions(),
|
|
693
|
+
Promise.resolve(probeRepoPackageVersions({ cwd: input.cwd, repoRoot: input.repoRoot }))
|
|
694
|
+
]);
|
|
695
|
+
const self = {};
|
|
696
|
+
const runtimeSelfVersion = input.selfPackageName === "@kynver-app/runtime" && input.selfVersion ? input.selfVersion : PACKAGE_VERSION;
|
|
697
|
+
self["@kynver-app/runtime"] = { version: runtimeSelfVersion, source: "self" };
|
|
698
|
+
if (input.selfPackageName && input.selfVersion && input.selfPackageName !== "@kynver-app/runtime") {
|
|
699
|
+
self[input.selfPackageName] = { version: input.selfVersion, source: "self" };
|
|
701
700
|
}
|
|
702
|
-
return
|
|
701
|
+
return {
|
|
702
|
+
installed: installedVersionMap(installed),
|
|
703
|
+
repo,
|
|
704
|
+
self
|
|
705
|
+
};
|
|
703
706
|
}
|
|
704
|
-
function
|
|
705
|
-
|
|
707
|
+
async function evaluateMemoryCostPackageGuardAtStartup(input = {}) {
|
|
708
|
+
const guardInput = await buildMemoryCostPackageGuardInput(input);
|
|
709
|
+
return evaluateMemoryCostPackageVersionGuard(guardInput);
|
|
706
710
|
}
|
|
711
|
+
async function enforceMemoryCostPackageGuardAtStartup(input = {}) {
|
|
712
|
+
const guardInput = await buildMemoryCostPackageGuardInput(input);
|
|
713
|
+
return assertMemoryCostPackageVersionGuard(guardInput);
|
|
714
|
+
}
|
|
715
|
+
function shouldEnforceMemoryCostPackageGuardCli(scope, action) {
|
|
716
|
+
if (!scope) return false;
|
|
717
|
+
if (scope === "daemon") return true;
|
|
718
|
+
if (scope === "worker") return true;
|
|
719
|
+
if (scope === "monitor") return true;
|
|
720
|
+
if (scope === "run" && (action === "dispatch" || action === "sweep" || action === "reconcile" || action === "unblock")) {
|
|
721
|
+
return true;
|
|
722
|
+
}
|
|
723
|
+
if (scope === "cron" && action === "tick") return true;
|
|
724
|
+
return false;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// src/config.ts
|
|
728
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync2 } from "node:fs";
|
|
729
|
+
import { homedir as homedir5, totalmem } from "node:os";
|
|
730
|
+
import path10 from "node:path";
|
|
707
731
|
|
|
708
732
|
// src/box-identity.ts
|
|
709
733
|
function normalizeWorkerPoolBoxKind(raw) {
|
|
@@ -4438,14 +4462,15 @@ function buildPrompt(input) {
|
|
|
4438
4462
|
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."
|
|
4439
4463
|
];
|
|
4440
4464
|
const mergeGateLines = compact ? [
|
|
4441
|
-
"Merge-gate cost control: do not use Vercel previews/builds for PR verification. Run `node scripts/agent-os-pr-merge-gate.mjs --pr <url> --agent-os-id <id>` (or `verify-pr-local.mjs --from-pr` + POST pr-merge-gate/refresh) before any GitHub Actions run; request merge-gate only via refresh then POST pr-merge-gate/request-run (one Actions run per PR head unless human approves extra)."
|
|
4465
|
+
"Merge-gate cost control: do not use Vercel previews/builds for PR verification. Run `node scripts/agent-os-pr-merge-gate.mjs --pr <url> --agent-os-id <id>` (or `verify-pr-local.mjs --from-pr` + POST pr-merge-gate/refresh) before any GitHub Actions run; request merge-gate only via refresh then POST pr-merge-gate/request-run (one Actions run per PR head unless human approves extra). Per-PR Vercel preview builds are off by default \u2014 production deploy runs via release batch after merge."
|
|
4442
4466
|
] : [
|
|
4443
4467
|
"GitHub Actions merge-gate cost control (Kynver/Hermes PRs):",
|
|
4444
4468
|
"- Prefer local cached package verification (`node scripts/verify-pr-local.mjs --emit-json`) before GitHub Actions. Do not use Vercel previews/builds as PR evidence.",
|
|
4445
4469
|
"- Do not push empty commits to re-trigger CI. One budgeted merge-gate Actions run per PR candidate (head SHA) unless a human approves extra.",
|
|
4446
4470
|
"- Record evidence: POST `/api/agent-os/by-id/<agentOsId>/pr-merge-gate/refresh` with prUrl + local payloads.",
|
|
4447
4471
|
"- Request the final Actions run only when local verification is green: POST `.../pr-merge-gate/request-run` (applies `merge-gate` label).",
|
|
4448
|
-
"- Empty failed Actions jobs (no runner/steps/logs) are infra/quota \u2014 do not enter repair loops; escalate to operator."
|
|
4472
|
+
"- Empty failed Actions jobs (no runner/steps/logs) are infra/quota \u2014 do not enter repair loops; escalate to operator.",
|
|
4473
|
+
"- After merge, landed PRs accumulate into a release batch; run batch verification then manual Vercel production deploy from Command Center (release train). PR landing completes with: verification complete, awaiting release batch/deploy."
|
|
4449
4474
|
];
|
|
4450
4475
|
const planArtifactLines = compact ? [
|
|
4451
4476
|
"Plan artifacts: when authoring/revising docs/superpowers/plans/, open a GitHub PR early and iterate from that PR branch; do not leave the canonical plan only in the harness worktree."
|
|
@@ -7410,7 +7435,7 @@ async function dispatchRun(args) {
|
|
|
7410
7435
|
runnerPresence,
|
|
7411
7436
|
harnessBoardSnapshot: buildRunBoard(run.id),
|
|
7412
7437
|
...args.lane ? { lane: resolveDispatchNextLaneFilter(String(args.lane)) } : {},
|
|
7413
|
-
|
|
7438
|
+
...args.executor ? { executor: String(args.executor) } : {},
|
|
7414
7439
|
...args.diskPath ? { diskPath: String(args.diskPath) } : {},
|
|
7415
7440
|
...args.targetTaskId ? { targetTaskId: String(args.targetTaskId) } : {},
|
|
7416
7441
|
...!args.targetTaskId && args.targetTaskIds ? {
|
|
@@ -13483,9 +13508,7 @@ var defaultRuntimeTakeoverProbes = {
|
|
|
13483
13508
|
harnessRoot: () => resolveHarnessRoot(),
|
|
13484
13509
|
legacyOpenclawHarnessRoot: () => path66.join(homedir16(), ".openclaw", "harness"),
|
|
13485
13510
|
pathExists: (target) => existsSync46(target),
|
|
13486
|
-
pathWritable: (target) => isWritable(target)
|
|
13487
|
-
vercelVersion: () => captureCommand("vercel", ["--version"]),
|
|
13488
|
-
vercelWhoami: () => captureCommand("vercel", ["whoami"])
|
|
13511
|
+
pathWritable: (target) => isWritable(target)
|
|
13489
13512
|
};
|
|
13490
13513
|
|
|
13491
13514
|
// src/doctor/runtime-takeover-scheduler.ts
|
|
@@ -13852,29 +13875,38 @@ function assessRunnerToken(probes) {
|
|
|
13852
13875
|
];
|
|
13853
13876
|
return { id: "runner_token", label: "Runner token readiness", checks };
|
|
13854
13877
|
}
|
|
13855
|
-
function
|
|
13856
|
-
const
|
|
13857
|
-
const
|
|
13858
|
-
const
|
|
13878
|
+
function assessVercelDeployEvidence(probes) {
|
|
13879
|
+
const globalCli = probes.commandOnPath("vercel");
|
|
13880
|
+
const cliInstalled = globalCli.ok;
|
|
13881
|
+
const githubToken = Boolean(
|
|
13882
|
+
process.env.GITHUB_TOKEN?.trim() || process.env.GH_TOKEN?.trim()
|
|
13883
|
+
);
|
|
13884
|
+
const vercelToken = Boolean(process.env.VERCEL_TOKEN?.trim());
|
|
13859
13885
|
return {
|
|
13860
|
-
id: "
|
|
13861
|
-
label: "Vercel
|
|
13886
|
+
id: "vercel_deploy_evidence",
|
|
13887
|
+
label: "Vercel deploy evidence",
|
|
13862
13888
|
checks: [
|
|
13863
13889
|
check2({
|
|
13864
|
-
id: "
|
|
13865
|
-
label: "Vercel CLI installed",
|
|
13866
|
-
status:
|
|
13867
|
-
summary:
|
|
13868
|
-
remediation:
|
|
13869
|
-
details: {
|
|
13890
|
+
id: "vercel_global_cli_absent",
|
|
13891
|
+
label: "Global Vercel CLI not installed",
|
|
13892
|
+
status: cliInstalled ? "warn" : "pass",
|
|
13893
|
+
summary: cliInstalled ? `Global Vercel CLI found (${globalCli.stdout || "on PATH"}) \u2014 uninstall after operator approval` : "No global Vercel CLI on PATH",
|
|
13894
|
+
remediation: cliInstalled ? "Uninstall global Vercel CLI (`npm uninstall -g vercel`) \u2014 use GitHub commit status or scoped VERCEL_TOKEN + REST API instead." : void 0,
|
|
13895
|
+
details: { path: cliInstalled ? globalCli.stdout || null : null }
|
|
13896
|
+
}),
|
|
13897
|
+
check2({
|
|
13898
|
+
id: "github_token_for_vercel_status",
|
|
13899
|
+
label: "GitHub token for Vercel status",
|
|
13900
|
+
status: githubToken ? "pass" : "warn",
|
|
13901
|
+
summary: githubToken ? "GITHUB_TOKEN/GH_TOKEN present for GitHub Vercel StatusContext evidence" : "No GITHUB_TOKEN \u2014 merge-gate Vercel evidence requires GitHub commit status API",
|
|
13902
|
+
remediation: githubToken ? void 0 : "Export GITHUB_TOKEN (or GH_TOKEN) for PR head commit status lookups."
|
|
13870
13903
|
}),
|
|
13871
13904
|
check2({
|
|
13872
|
-
id: "
|
|
13873
|
-
label: "Vercel
|
|
13874
|
-
status:
|
|
13875
|
-
summary:
|
|
13876
|
-
remediation:
|
|
13877
|
-
details: { account: whoami.ok ? whoami.stdout : null }
|
|
13905
|
+
id: "vercel_api_token_optional",
|
|
13906
|
+
label: "Vercel API token (optional fallback)",
|
|
13907
|
+
status: vercelToken ? "pass" : "warn",
|
|
13908
|
+
summary: vercelToken ? "VERCEL_TOKEN configured for optional REST API deployment lookup" : "VERCEL_TOKEN unset \u2014 GitHub status is the default path; API fallback disabled",
|
|
13909
|
+
remediation: vercelToken ? void 0 : "Set ephemeral/scoped VERCEL_TOKEN only when GitHub status is insufficient; avoid persistent ~/.vercel auth on runner hosts."
|
|
13878
13910
|
})
|
|
13879
13911
|
]
|
|
13880
13912
|
};
|
|
@@ -14013,7 +14045,7 @@ function assessRuntimeTakeoverReadiness(probes = defaultRuntimeTakeoverProbes) {
|
|
|
14013
14045
|
assessCliPackage(probes),
|
|
14014
14046
|
assessUserConfig(probes),
|
|
14015
14047
|
assessRunnerToken(probes),
|
|
14016
|
-
|
|
14048
|
+
assessVercelDeployEvidence(probes),
|
|
14017
14049
|
assessHarnessDirs(probes),
|
|
14018
14050
|
assessCallbackAuth(probes),
|
|
14019
14051
|
assessOpenclawHotspots(probes)
|
|
@@ -14363,7 +14395,19 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
14363
14395
|
mkdirSync8(runsDir, { recursive: true });
|
|
14364
14396
|
mkdirSync8(worktreesDir, { recursive: true });
|
|
14365
14397
|
if (shouldEnforceMemoryCostPackageGuardCli(scope, action)) {
|
|
14366
|
-
|
|
14398
|
+
let repoRoot;
|
|
14399
|
+
const runId = args.run ? String(args.run).trim() : "";
|
|
14400
|
+
if (runId) {
|
|
14401
|
+
try {
|
|
14402
|
+
repoRoot = loadRun(runId).repo;
|
|
14403
|
+
} catch {
|
|
14404
|
+
repoRoot = void 0;
|
|
14405
|
+
}
|
|
14406
|
+
}
|
|
14407
|
+
await enforceMemoryCostPackageGuardAtStartup({
|
|
14408
|
+
repoRoot,
|
|
14409
|
+
cwd: repoRoot
|
|
14410
|
+
});
|
|
14367
14411
|
}
|
|
14368
14412
|
if (scope === "login") return void await runLogin(args);
|
|
14369
14413
|
if (scope === "runner" && action === "credential") return void await mintRunnerCredential(args);
|
|
@@ -14435,6 +14479,76 @@ if (isCliEntry) {
|
|
|
14435
14479
|
});
|
|
14436
14480
|
}
|
|
14437
14481
|
|
|
14482
|
+
// src/vercel/vercel-api.ts
|
|
14483
|
+
var VERCEL_API = "https://api.vercel.com";
|
|
14484
|
+
function resolveToken(explicit) {
|
|
14485
|
+
const trimmed = explicit?.trim();
|
|
14486
|
+
if (trimmed) return trimmed;
|
|
14487
|
+
return process.env.VERCEL_TOKEN?.trim() || null;
|
|
14488
|
+
}
|
|
14489
|
+
function mapReadyState(state) {
|
|
14490
|
+
return (state ?? "").trim().toUpperCase();
|
|
14491
|
+
}
|
|
14492
|
+
function previewUrlFromDeployment(url) {
|
|
14493
|
+
if (typeof url !== "string" || !url.trim()) return null;
|
|
14494
|
+
const trimmed = url.trim();
|
|
14495
|
+
return trimmed.startsWith("http") ? trimmed : `https://${trimmed}`;
|
|
14496
|
+
}
|
|
14497
|
+
async function fetchVercelDeploymentStatus(deploymentIdOrUrl, options = {}) {
|
|
14498
|
+
const target = deploymentIdOrUrl.trim();
|
|
14499
|
+
if (!target) {
|
|
14500
|
+
return { ok: false, readyState: null, previewUrl: null, error: "missing deployment id or URL" };
|
|
14501
|
+
}
|
|
14502
|
+
const token = resolveToken(options.token);
|
|
14503
|
+
if (!token) {
|
|
14504
|
+
return {
|
|
14505
|
+
ok: false,
|
|
14506
|
+
readyState: null,
|
|
14507
|
+
previewUrl: null,
|
|
14508
|
+
error: "VERCEL_TOKEN not configured"
|
|
14509
|
+
};
|
|
14510
|
+
}
|
|
14511
|
+
const teamId = options.teamId?.trim() || process.env.VERCEL_TEAM_ID?.trim();
|
|
14512
|
+
const teamQuery = teamId ? `?teamId=${encodeURIComponent(teamId)}` : "";
|
|
14513
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
14514
|
+
try {
|
|
14515
|
+
const res = await fetchImpl(`${VERCEL_API}/v13/deployments/${encodeURIComponent(target)}${teamQuery}`, {
|
|
14516
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
14517
|
+
});
|
|
14518
|
+
if (!res.ok) {
|
|
14519
|
+
const body = await res.text();
|
|
14520
|
+
return {
|
|
14521
|
+
ok: false,
|
|
14522
|
+
readyState: null,
|
|
14523
|
+
previewUrl: null,
|
|
14524
|
+
error: `Vercel API ${res.status}: ${body.slice(0, 200)}`
|
|
14525
|
+
};
|
|
14526
|
+
}
|
|
14527
|
+
const data = await res.json();
|
|
14528
|
+
const readyState = mapReadyState(data.readyState ?? data.state);
|
|
14529
|
+
return {
|
|
14530
|
+
ok: true,
|
|
14531
|
+
readyState: readyState || null,
|
|
14532
|
+
previewUrl: previewUrlFromDeployment(data.url),
|
|
14533
|
+
error: null
|
|
14534
|
+
};
|
|
14535
|
+
} catch (err) {
|
|
14536
|
+
return {
|
|
14537
|
+
ok: false,
|
|
14538
|
+
readyState: null,
|
|
14539
|
+
previewUrl: null,
|
|
14540
|
+
error: err instanceof Error ? err.message : "Vercel API fetch failed"
|
|
14541
|
+
};
|
|
14542
|
+
}
|
|
14543
|
+
}
|
|
14544
|
+
function mapVercelReadyStateToEvidenceStatus(readyState, githubPending) {
|
|
14545
|
+
const state = mapReadyState(readyState);
|
|
14546
|
+
if (state === "READY") return "ready";
|
|
14547
|
+
if (state === "ERROR" || state === "CANCELED") return "error";
|
|
14548
|
+
if (state === "BUILDING" || state === "QUEUED" || state === "INITIALIZING") return "building";
|
|
14549
|
+
return githubPending ? "building" : "error";
|
|
14550
|
+
}
|
|
14551
|
+
|
|
14438
14552
|
// src/vercel/vercel-url.ts
|
|
14439
14553
|
var VERCEL_HOST_RE = /(^|\.)vercel\.app$/i;
|
|
14440
14554
|
var DPL_ID_RE = /^dpl_[a-z0-9]+$/i;
|
|
@@ -14565,8 +14679,6 @@ function pickVercelStatusContext(statuses) {
|
|
|
14565
14679
|
}
|
|
14566
14680
|
|
|
14567
14681
|
// src/vercel/vercel-evidence.ts
|
|
14568
|
-
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
14569
|
-
var DEFAULT_INSPECT_WAIT_SECONDS = 120;
|
|
14570
14682
|
function mapGitHubStateToEvidence(state) {
|
|
14571
14683
|
if (state === "success") return "ready";
|
|
14572
14684
|
if (state === "pending") return "building";
|
|
@@ -14589,7 +14701,7 @@ function resolveVercelInspectTarget(rawUrl) {
|
|
|
14589
14701
|
return {
|
|
14590
14702
|
target: null,
|
|
14591
14703
|
classified,
|
|
14592
|
-
reason: classified.deploymentId ? "dashboard deployment id is not
|
|
14704
|
+
reason: classified.deploymentId ? "dashboard deployment id is not API-inspectable; trust GitHub Vercel status" : "dashboard URL is not valid for Vercel API lookup"
|
|
14593
14705
|
};
|
|
14594
14706
|
}
|
|
14595
14707
|
return { target: null, classified, reason: "unrecognized Vercel URL" };
|
|
@@ -14620,55 +14732,16 @@ function evidenceFromGitHubVercelStatus(statuses, options = {}) {
|
|
|
14620
14732
|
summary: evidenceSummary([
|
|
14621
14733
|
`GitHub ${row.context}=${row.state}`,
|
|
14622
14734
|
row.description ?? "",
|
|
14623
|
-
row.dashboardUrl ? "dashboard target_url (
|
|
14735
|
+
row.dashboardUrl ? "dashboard target_url (API lookup skipped)" : ""
|
|
14624
14736
|
]),
|
|
14625
14737
|
observedAt,
|
|
14626
14738
|
inspectSkipped: true,
|
|
14627
|
-
inspectReason: row.dashboardUrl && row.state === "success" ? "trusted_github_status_dashboard_url" : row.dashboardUrl ? "
|
|
14739
|
+
inspectReason: row.dashboardUrl && row.state === "success" ? "trusted_github_status_dashboard_url" : row.dashboardUrl ? "dashboard_url_not_api_inspectable" : "github_status_only",
|
|
14628
14740
|
githubState: row.state,
|
|
14629
14741
|
vercelContext: row.context
|
|
14630
14742
|
};
|
|
14631
14743
|
}
|
|
14632
|
-
function
|
|
14633
|
-
if (!isInspectableVercelTarget(target)) {
|
|
14634
|
-
return {
|
|
14635
|
-
ok: false,
|
|
14636
|
-
exitCode: 1,
|
|
14637
|
-
stdout: "",
|
|
14638
|
-
stderr: "",
|
|
14639
|
-
error: "refusing vercel inspect on non-inspectable target (dashboard URLs use GitHub status)"
|
|
14640
|
-
};
|
|
14641
|
-
}
|
|
14642
|
-
const args = ["inspect", target, "--wait", String(waitSeconds)];
|
|
14643
|
-
const result = spawnSync10("vercel", args, {
|
|
14644
|
-
encoding: "utf8",
|
|
14645
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
14646
|
-
});
|
|
14647
|
-
if (result.error) {
|
|
14648
|
-
return {
|
|
14649
|
-
ok: false,
|
|
14650
|
-
exitCode: result.status,
|
|
14651
|
-
stdout: "",
|
|
14652
|
-
stderr: "",
|
|
14653
|
-
error: result.error.message
|
|
14654
|
-
};
|
|
14655
|
-
}
|
|
14656
|
-
return {
|
|
14657
|
-
ok: result.status === 0,
|
|
14658
|
-
exitCode: result.status ?? 1,
|
|
14659
|
-
stdout: (result.stdout ?? "").trim(),
|
|
14660
|
-
stderr: (result.stderr ?? "").trim(),
|
|
14661
|
-
error: null
|
|
14662
|
-
};
|
|
14663
|
-
}
|
|
14664
|
-
function parseInspectReady(stdout, stderr) {
|
|
14665
|
-
const blob = `${stdout}
|
|
14666
|
-
${stderr}`.toLowerCase();
|
|
14667
|
-
if (/\b(status|state)\s*[:=]\s*(ready|completed)\b/.test(blob)) return true;
|
|
14668
|
-
if (/\bready\b/.test(blob) && !/\bnot ready\b/.test(blob)) return true;
|
|
14669
|
-
return false;
|
|
14670
|
-
}
|
|
14671
|
-
function collectVercelEvidence(input) {
|
|
14744
|
+
async function collectVercelEvidence(input) {
|
|
14672
14745
|
const observedAt = input.observedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
14673
14746
|
const base = evidenceFromGitHubVercelStatus(input.statuses ?? [], { observedAt });
|
|
14674
14747
|
const row = pickVercelStatusContext(input.statuses ?? []);
|
|
@@ -14684,7 +14757,7 @@ function collectVercelEvidence(input) {
|
|
|
14684
14757
|
return {
|
|
14685
14758
|
...base,
|
|
14686
14759
|
inspectSkipped: true,
|
|
14687
|
-
inspectReason: "
|
|
14760
|
+
inspectReason: "api_lookup_disabled"
|
|
14688
14761
|
};
|
|
14689
14762
|
}
|
|
14690
14763
|
const { target, reason } = resolveVercelInspectTarget(row.targetUrl);
|
|
@@ -14692,39 +14765,37 @@ function collectVercelEvidence(input) {
|
|
|
14692
14765
|
return {
|
|
14693
14766
|
...base,
|
|
14694
14767
|
inspectSkipped: true,
|
|
14695
|
-
inspectReason: reason ?? "
|
|
14768
|
+
inspectReason: reason ?? "not_api_inspectable",
|
|
14696
14769
|
summary: evidenceSummary([
|
|
14697
14770
|
base.summary ?? "",
|
|
14698
|
-
reason ?? "skipped
|
|
14771
|
+
reason ?? "skipped Vercel API lookup"
|
|
14699
14772
|
])
|
|
14700
14773
|
};
|
|
14701
14774
|
}
|
|
14702
|
-
const
|
|
14703
|
-
const
|
|
14704
|
-
const inspected = runInspect(target, waitSeconds);
|
|
14775
|
+
const runLookup = input.runInspect ?? fetchVercelDeploymentStatus;
|
|
14776
|
+
const lookedUp = await runLookup(target);
|
|
14705
14777
|
const observed = (/* @__PURE__ */ new Date()).toISOString();
|
|
14706
|
-
if (
|
|
14778
|
+
if (!lookedUp.ok && lookedUp.error?.includes("VERCEL_TOKEN")) {
|
|
14707
14779
|
return {
|
|
14708
14780
|
status: "unavailable",
|
|
14709
14781
|
previewUrl: base.previewUrl,
|
|
14710
14782
|
deploymentUrl: base.deploymentUrl,
|
|
14711
|
-
summary: "
|
|
14783
|
+
summary: "VERCEL_TOKEN not configured for API fallback",
|
|
14712
14784
|
observedAt: observed,
|
|
14713
14785
|
inspectSkipped: true,
|
|
14714
|
-
inspectReason: "
|
|
14786
|
+
inspectReason: "vercel_token_missing",
|
|
14715
14787
|
githubState: row.state,
|
|
14716
14788
|
vercelContext: row.context
|
|
14717
14789
|
};
|
|
14718
14790
|
}
|
|
14719
|
-
if (!
|
|
14720
|
-
const detail = [inspected.stderr, inspected.stdout, inspected.error].filter(Boolean).join("\n").trim().slice(0, 500);
|
|
14791
|
+
if (!lookedUp.ok) {
|
|
14721
14792
|
return {
|
|
14722
14793
|
status: row.state === "pending" ? "building" : "error",
|
|
14723
14794
|
previewUrl: base.previewUrl,
|
|
14724
14795
|
deploymentUrl: target.startsWith("http") ? target : base.deploymentUrl,
|
|
14725
14796
|
summary: evidenceSummary([
|
|
14726
|
-
`
|
|
14727
|
-
|
|
14797
|
+
`Vercel API lookup ${target} failed`,
|
|
14798
|
+
lookedUp.error ?? ""
|
|
14728
14799
|
]),
|
|
14729
14800
|
observedAt: observed,
|
|
14730
14801
|
inspectSkipped: false,
|
|
@@ -14733,12 +14804,19 @@ function collectVercelEvidence(input) {
|
|
|
14733
14804
|
vercelContext: row.context
|
|
14734
14805
|
};
|
|
14735
14806
|
}
|
|
14736
|
-
const
|
|
14807
|
+
const mapped = mapVercelReadyStateToEvidenceStatus(
|
|
14808
|
+
lookedUp.readyState,
|
|
14809
|
+
row.state === "pending"
|
|
14810
|
+
);
|
|
14811
|
+
const previewUrl = lookedUp.previewUrl ?? (target.startsWith("http") ? target : base.previewUrl);
|
|
14737
14812
|
return {
|
|
14738
|
-
status:
|
|
14739
|
-
previewUrl
|
|
14740
|
-
deploymentUrl:
|
|
14741
|
-
summary:
|
|
14813
|
+
status: mapped,
|
|
14814
|
+
previewUrl,
|
|
14815
|
+
deploymentUrl: previewUrl ?? base.deploymentUrl,
|
|
14816
|
+
summary: evidenceSummary([
|
|
14817
|
+
`Vercel API ${target}=${lookedUp.readyState ?? "unknown"}`,
|
|
14818
|
+
mapped === "ready" ? "preview ready" : ""
|
|
14819
|
+
]),
|
|
14742
14820
|
observedAt: observed,
|
|
14743
14821
|
inspectSkipped: false,
|
|
14744
14822
|
inspectReason: null,
|