@viberaven/cli 1.1.6 → 1.1.8
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/AGENTS.md +7 -0
- package/LICENSE +21 -0
- package/MCP.md +34 -0
- package/README.md +37 -0
- package/SECURITY.md +53 -53
- package/SKILL.md +69 -0
- package/assets/report/assets/provider-authjs.svg +5 -5
- package/assets/report/assets/provider-aws.svg +5 -5
- package/assets/report/assets/provider-logrocket.svg +4 -4
- package/assets/report/station.js +2 -2
- package/dist/cli.js +1453 -453
- package/dist/cli.js.map +4 -4
- package/dist/console/app.js +251 -0
- package/dist/console/index.html +46 -0
- package/dist/console/styles.css +267 -0
- package/dist/report/assets/provider-authjs.svg +5 -5
- package/dist/report/assets/provider-aws.svg +5 -5
- package/dist/report/assets/provider-logrocket.svg +4 -4
- package/dist/report/station.js +2 -2
- package/package.json +6 -2
package/dist/cli.js
CHANGED
|
@@ -31,9 +31,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
31
31
|
));
|
|
32
32
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
33
33
|
|
|
34
|
-
//
|
|
34
|
+
// ../../../../node_modules/picocolors/picocolors.js
|
|
35
35
|
var require_picocolors = __commonJS({
|
|
36
|
-
"
|
|
36
|
+
"../../../../node_modules/picocolors/picocolors.js"(exports2, module2) {
|
|
37
37
|
var p2 = process || {};
|
|
38
38
|
var argv = p2.argv || [];
|
|
39
39
|
var env = p2.env || {};
|
|
@@ -103,9 +103,9 @@ var require_picocolors = __commonJS({
|
|
|
103
103
|
}
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
-
//
|
|
106
|
+
// ../../../../node_modules/sisteransi/src/index.js
|
|
107
107
|
var require_src = __commonJS({
|
|
108
|
-
"
|
|
108
|
+
"../../../../node_modules/sisteransi/src/index.js"(exports2, module2) {
|
|
109
109
|
"use strict";
|
|
110
110
|
var ESC = "\x1B";
|
|
111
111
|
var CSI = `${ESC}[`;
|
|
@@ -170,8 +170,8 @@ __export(cli_exports, {
|
|
|
170
170
|
runScanCommand: () => runScanCommand
|
|
171
171
|
});
|
|
172
172
|
module.exports = __toCommonJS(cli_exports);
|
|
173
|
-
var
|
|
174
|
-
var
|
|
173
|
+
var import_promises25 = require("node:fs/promises");
|
|
174
|
+
var import_node_path31 = require("node:path");
|
|
175
175
|
|
|
176
176
|
// src/config.ts
|
|
177
177
|
var import_node_os = require("node:os");
|
|
@@ -621,12 +621,12 @@ function createOpenCommand(target, platform = process.platform) {
|
|
|
621
621
|
}
|
|
622
622
|
async function openWithSystemDefault(target) {
|
|
623
623
|
const { command, args, shell } = createOpenCommand(target);
|
|
624
|
-
await new Promise((
|
|
624
|
+
await new Promise((resolve6, reject) => {
|
|
625
625
|
const child = (0, import_node_child_process.spawn)(command, args, { stdio: "ignore", shell });
|
|
626
626
|
child.on("error", reject);
|
|
627
627
|
child.on("exit", (code) => {
|
|
628
628
|
if (code === 0) {
|
|
629
|
-
|
|
629
|
+
resolve6();
|
|
630
630
|
} else {
|
|
631
631
|
reject(new Error(`Could not open browser (exit ${code ?? "unknown"}). Open manually: ${target}`));
|
|
632
632
|
}
|
|
@@ -655,6 +655,7 @@ var PUBLIC_INIT_ALL_COMMAND = `${PUBLIC_COMMAND} init --agents all`;
|
|
|
655
655
|
var PUBLIC_AUDIT_COMMAND = `${PUBLIC_COMMAND} audit --vercel-supabase`;
|
|
656
656
|
var PUBLIC_NEXT_JSON_COMMAND = `${PUBLIC_COMMAND} next --json`;
|
|
657
657
|
var PUBLIC_CLEAN_PLAN_COMMAND = `${PUBLIC_COMMAND} clean --plan`;
|
|
658
|
+
var PUBLIC_ACTIONS_COMMAND = `${PUBLIC_COMMAND} actions`;
|
|
658
659
|
function promptGapCommand(gapId) {
|
|
659
660
|
return `${PUBLIC_COMMAND} prompt --gap ${gapId}`;
|
|
660
661
|
}
|
|
@@ -665,7 +666,7 @@ function healPlanGapCommand(gapId) {
|
|
|
665
666
|
// src/auth.ts
|
|
666
667
|
var PUBLIC_LOGIN_COMMAND = `${PUBLIC_COMMAND} login`;
|
|
667
668
|
function sleep(ms) {
|
|
668
|
-
return new Promise((
|
|
669
|
+
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
669
670
|
}
|
|
670
671
|
async function runDeviceLogin(apiBaseUrl) {
|
|
671
672
|
const signIn = await startManagedSignIn(apiBaseUrl);
|
|
@@ -2215,20 +2216,20 @@ function mapEvidenceSourceToLegacyEvidenceClass(source, status) {
|
|
|
2215
2216
|
}
|
|
2216
2217
|
return "mcp-verifier";
|
|
2217
2218
|
}
|
|
2218
|
-
function isProviderLayerCheck(
|
|
2219
|
-
return
|
|
2219
|
+
function isProviderLayerCheck(check) {
|
|
2220
|
+
return check.evidenceSource === "provider" || check.evidenceSource === "mcp";
|
|
2220
2221
|
}
|
|
2221
|
-
function isRepoLayerCheck(
|
|
2222
|
-
return
|
|
2222
|
+
function isRepoLayerCheck(check) {
|
|
2223
|
+
return check.evidenceSource === "repo";
|
|
2223
2224
|
}
|
|
2224
2225
|
function computeLayerReadinessPercent(checks, layer) {
|
|
2225
2226
|
const filtered = checks.filter(
|
|
2226
|
-
(
|
|
2227
|
+
(check) => layer === "repo" ? isRepoLayerCheck(check) : isProviderLayerCheck(check)
|
|
2227
2228
|
);
|
|
2228
2229
|
if (filtered.length === 0) {
|
|
2229
2230
|
return 100;
|
|
2230
2231
|
}
|
|
2231
|
-
const verified = filtered.filter((
|
|
2232
|
+
const verified = filtered.filter((check) => check.status === "verified").length;
|
|
2232
2233
|
return Math.round(verified / filtered.length * 100);
|
|
2233
2234
|
}
|
|
2234
2235
|
function aggregateReadinessPercents(providerResults) {
|
|
@@ -2260,7 +2261,7 @@ function mergeVerificationIntoMissionGraph(graph, layer) {
|
|
|
2260
2261
|
if (!mission) {
|
|
2261
2262
|
continue;
|
|
2262
2263
|
}
|
|
2263
|
-
if (mission.checks.some((
|
|
2264
|
+
if (mission.checks.some((check) => check.verificationCheckId === layerCheck.id || check.id === missionRowId(layerCheck.id))) {
|
|
2264
2265
|
continue;
|
|
2265
2266
|
}
|
|
2266
2267
|
mission.checks.push(verificationCheckToMissionCheck(layerCheck, mission));
|
|
@@ -2351,7 +2352,7 @@ function readinessPercentForRepoChecks(checks) {
|
|
|
2351
2352
|
if (repoChecks.length === 0) {
|
|
2352
2353
|
return 100;
|
|
2353
2354
|
}
|
|
2354
|
-
const verified = repoChecks.filter((
|
|
2355
|
+
const verified = repoChecks.filter((check) => isVerifiedMissionCheck(check)).length;
|
|
2355
2356
|
return Math.round(verified / repoChecks.length * 100);
|
|
2356
2357
|
}
|
|
2357
2358
|
function readinessPercentForProviderChecks(checks) {
|
|
@@ -2359,29 +2360,29 @@ function readinessPercentForProviderChecks(checks) {
|
|
|
2359
2360
|
if (providerChecks.length === 0) {
|
|
2360
2361
|
return 100;
|
|
2361
2362
|
}
|
|
2362
|
-
const verified = providerChecks.filter((
|
|
2363
|
+
const verified = providerChecks.filter((check) => isVerifiedMissionCheck(check)).length;
|
|
2363
2364
|
return Math.round(verified / providerChecks.length * 100);
|
|
2364
2365
|
}
|
|
2365
|
-
function isRepoLayerMissionCheck(
|
|
2366
|
-
if (
|
|
2366
|
+
function isRepoLayerMissionCheck(check) {
|
|
2367
|
+
if (check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.evidenceSource === "manual") {
|
|
2367
2368
|
return false;
|
|
2368
2369
|
}
|
|
2369
|
-
if (
|
|
2370
|
+
if (check.evidenceSource === "repo") {
|
|
2370
2371
|
return true;
|
|
2371
2372
|
}
|
|
2372
|
-
return
|
|
2373
|
+
return check.evidenceClass === "repo-verified" || check.evidenceClass === "missing-repo-fix";
|
|
2373
2374
|
}
|
|
2374
|
-
function isProviderLayerMissionCheck(
|
|
2375
|
-
if (
|
|
2375
|
+
function isProviderLayerMissionCheck(check) {
|
|
2376
|
+
if (check.evidenceSource === "provider" || check.evidenceSource === "mcp") {
|
|
2376
2377
|
return true;
|
|
2377
2378
|
}
|
|
2378
|
-
return
|
|
2379
|
+
return check.evidenceClass === "mcp-verifier";
|
|
2379
2380
|
}
|
|
2380
|
-
function isVerifiedMissionCheck(
|
|
2381
|
-
if (
|
|
2382
|
-
return
|
|
2381
|
+
function isVerifiedMissionCheck(check) {
|
|
2382
|
+
if (check.verificationStatus) {
|
|
2383
|
+
return check.verificationStatus === "verified";
|
|
2383
2384
|
}
|
|
2384
|
-
return
|
|
2385
|
+
return check.status === "passed" || check.status === "user-confirmed";
|
|
2385
2386
|
}
|
|
2386
2387
|
function rebuildAreas(providerMissions) {
|
|
2387
2388
|
const byArea = /* @__PURE__ */ new Map();
|
|
@@ -2410,7 +2411,7 @@ function rebuildAreas(providerMissions) {
|
|
|
2410
2411
|
const repoReadinessPercent = percentVerified(repoChecks);
|
|
2411
2412
|
const providerReadinessPercent = percentVerified(providerChecks);
|
|
2412
2413
|
const criticalCount = missions.flatMap((m2) => m2.checks).filter(
|
|
2413
|
-
(
|
|
2414
|
+
(check) => isProviderLayerMissionCheck(check) && (check.verificationStatus === "missing" || check.status === "missing" || check.status === "failed")
|
|
2414
2415
|
).length;
|
|
2415
2416
|
return {
|
|
2416
2417
|
key,
|
|
@@ -2427,7 +2428,7 @@ function percentVerified(checks) {
|
|
|
2427
2428
|
if (checks.length === 0) {
|
|
2428
2429
|
return 100;
|
|
2429
2430
|
}
|
|
2430
|
-
const verified = checks.filter((
|
|
2431
|
+
const verified = checks.filter((check) => isVerifiedMissionCheck(check)).length;
|
|
2431
2432
|
return Math.round(verified / checks.length * 100);
|
|
2432
2433
|
}
|
|
2433
2434
|
|
|
@@ -2492,7 +2493,7 @@ function toProviderMission(summary) {
|
|
|
2492
2493
|
promptSubject: summary.promptSubject,
|
|
2493
2494
|
readinessPercent: summary.readinessPercent,
|
|
2494
2495
|
repoReadinessPercent: summary.readinessPercent,
|
|
2495
|
-
providerReadinessPercent: checks.some((
|
|
2496
|
+
providerReadinessPercent: checks.some((check) => check.evidenceClass === "mcp-verifier") ? 0 : 100,
|
|
2496
2497
|
checks
|
|
2497
2498
|
};
|
|
2498
2499
|
}
|
|
@@ -2537,7 +2538,7 @@ function overlaySifgLeaks(providerMissions, graph) {
|
|
|
2537
2538
|
const providerCheckIds = new Map(
|
|
2538
2539
|
providerMissions.map((mission) => [
|
|
2539
2540
|
mission.key,
|
|
2540
|
-
new Set(mission.checks.map((
|
|
2541
|
+
new Set(mission.checks.map((check) => check.id))
|
|
2541
2542
|
])
|
|
2542
2543
|
);
|
|
2543
2544
|
for (const leak of graph.leaks) {
|
|
@@ -2556,9 +2557,9 @@ function overlaySifgLeaks(providerMissions, graph) {
|
|
|
2556
2557
|
continue;
|
|
2557
2558
|
}
|
|
2558
2559
|
const usedCheckIds = providerCheckIds.get(mission.key) ?? /* @__PURE__ */ new Set();
|
|
2559
|
-
const
|
|
2560
|
-
mission.checks.push(
|
|
2561
|
-
usedCheckIds.add(
|
|
2560
|
+
const check = toSifgMissionCheck(mission, pipeline, pipelineId, leaks, usedCheckIds);
|
|
2561
|
+
mission.checks.push(check);
|
|
2562
|
+
usedCheckIds.add(check.id);
|
|
2562
2563
|
providerCheckIds.set(mission.key, usedCheckIds);
|
|
2563
2564
|
}
|
|
2564
2565
|
for (const mission of providerMissions) {
|
|
@@ -2609,11 +2610,11 @@ function stableSifgSuffix(id) {
|
|
|
2609
2610
|
}
|
|
2610
2611
|
function readinessPercentForChecks(checks) {
|
|
2611
2612
|
const actionableChecks = checks.filter(isActionableCheck);
|
|
2612
|
-
const passed = actionableChecks.filter((
|
|
2613
|
+
const passed = actionableChecks.filter((check) => check.status === "passed").length;
|
|
2613
2614
|
return Math.round(passed / Math.max(actionableChecks.length, 1) * 100);
|
|
2614
2615
|
}
|
|
2615
|
-
function isActionableCheck(
|
|
2616
|
-
return
|
|
2616
|
+
function isActionableCheck(check) {
|
|
2617
|
+
return check.evidenceClass !== "manual-dashboard" && check.evidenceClass !== "mcp-verifier";
|
|
2617
2618
|
}
|
|
2618
2619
|
function buildAreas(providerMissions) {
|
|
2619
2620
|
const byArea = /* @__PURE__ */ new Map();
|
|
@@ -2624,15 +2625,15 @@ function buildAreas(providerMissions) {
|
|
|
2624
2625
|
}
|
|
2625
2626
|
return [...byArea.entries()].map(([key, missions]) => {
|
|
2626
2627
|
const actionableChecks = missions.flatMap((mission) => mission.checks).filter(isActionableCheck);
|
|
2627
|
-
const passed = actionableChecks.filter((
|
|
2628
|
-
const missing = actionableChecks.filter((
|
|
2628
|
+
const passed = actionableChecks.filter((check) => check.status === "passed").length;
|
|
2629
|
+
const missing = actionableChecks.filter((check) => check.status === "missing" || check.status === "failed").length;
|
|
2629
2630
|
return {
|
|
2630
2631
|
key,
|
|
2631
2632
|
label: AREA_LABELS[key],
|
|
2632
2633
|
readinessPercent: Math.round(passed / Math.max(actionableChecks.length, 1) * 100),
|
|
2633
2634
|
repoReadinessPercent: Math.round(passed / Math.max(actionableChecks.length, 1) * 100),
|
|
2634
2635
|
providerReadinessPercent: missions.some(
|
|
2635
|
-
(mission) => mission.checks.some((
|
|
2636
|
+
(mission) => mission.checks.some((check) => check.evidenceClass === "mcp-verifier")
|
|
2636
2637
|
) ? 0 : 100,
|
|
2637
2638
|
criticalCount: missing,
|
|
2638
2639
|
providerMissions: missions
|
|
@@ -7586,9 +7587,9 @@ function runVerifier(verifier, ctx) {
|
|
|
7586
7587
|
}
|
|
7587
7588
|
const connectionState = verifier.connectStatus(ctx);
|
|
7588
7589
|
const rawChecks = verifier.runChecks(ctx);
|
|
7589
|
-
const checks = rawChecks.map((
|
|
7590
|
-
...
|
|
7591
|
-
status: coerceProviderVerificationStatus(
|
|
7590
|
+
const checks = rawChecks.map((check) => ({
|
|
7591
|
+
...check,
|
|
7592
|
+
status: coerceProviderVerificationStatus(check.status, connectionState)
|
|
7592
7593
|
}));
|
|
7593
7594
|
const diffs = verifier.buildDiffs(ctx);
|
|
7594
7595
|
return {
|
|
@@ -7991,8 +7992,8 @@ async function runProjectScan(options) {
|
|
|
7991
7992
|
}
|
|
7992
7993
|
|
|
7993
7994
|
// src/artifacts.ts
|
|
7994
|
-
var
|
|
7995
|
-
var
|
|
7995
|
+
var import_promises7 = require("node:fs/promises");
|
|
7996
|
+
var import_node_path10 = require("node:path");
|
|
7996
7997
|
|
|
7997
7998
|
// src/capabilities/classify.ts
|
|
7998
7999
|
function gapText(gap) {
|
|
@@ -8313,24 +8314,24 @@ var CHECK_TO_PLAYBOOK = {
|
|
|
8313
8314
|
"stripe-product": "stripe",
|
|
8314
8315
|
"stripe-keys": "stripe"
|
|
8315
8316
|
};
|
|
8316
|
-
function mapCheckToPlaybook(
|
|
8317
|
-
if (CHECK_TO_PLAYBOOK[
|
|
8318
|
-
return CHECK_TO_PLAYBOOK[
|
|
8317
|
+
function mapCheckToPlaybook(check) {
|
|
8318
|
+
if (CHECK_TO_PLAYBOOK[check.id]) {
|
|
8319
|
+
return CHECK_TO_PLAYBOOK[check.id];
|
|
8319
8320
|
}
|
|
8320
|
-
const providerKey =
|
|
8321
|
-
if (providerKey.includes("vercel") ||
|
|
8321
|
+
const providerKey = check.providerKey.toLowerCase();
|
|
8322
|
+
if (providerKey.includes("vercel") || check.area === "deployment") {
|
|
8322
8323
|
return "vercel";
|
|
8323
8324
|
}
|
|
8324
|
-
if (providerKey.includes("stripe") ||
|
|
8325
|
+
if (providerKey.includes("stripe") || check.area === "payments") {
|
|
8325
8326
|
return "stripe";
|
|
8326
8327
|
}
|
|
8327
|
-
if (providerKey.includes("supabase") &&
|
|
8328
|
+
if (providerKey.includes("supabase") && check.area === "auth") {
|
|
8328
8329
|
return "auth-supabase";
|
|
8329
8330
|
}
|
|
8330
|
-
if (providerKey.includes("supabase") ||
|
|
8331
|
+
if (providerKey.includes("supabase") || check.area === "database") {
|
|
8331
8332
|
return "supabase";
|
|
8332
8333
|
}
|
|
8333
|
-
if (
|
|
8334
|
+
if (check.area === "auth") {
|
|
8334
8335
|
return "auth-supabase";
|
|
8335
8336
|
}
|
|
8336
8337
|
return "vercel";
|
|
@@ -8471,21 +8472,21 @@ function loadPlaybookSync(provider2) {
|
|
|
8471
8472
|
}
|
|
8472
8473
|
|
|
8473
8474
|
// src/playbooks/manualChecks.ts
|
|
8474
|
-
function isManualProviderCheck(
|
|
8475
|
-
return
|
|
8475
|
+
function isManualProviderCheck(check) {
|
|
8476
|
+
return check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown";
|
|
8476
8477
|
}
|
|
8477
8478
|
function collectManualChecks(artifact) {
|
|
8478
8479
|
const refs = [];
|
|
8479
8480
|
for (const area of artifact.missionGraph.areas ?? []) {
|
|
8480
8481
|
for (const mission of area.providerMissions) {
|
|
8481
|
-
for (const
|
|
8482
|
-
if (!isManualProviderCheck(
|
|
8482
|
+
for (const check of mission.checks) {
|
|
8483
|
+
if (!isManualProviderCheck(check)) {
|
|
8483
8484
|
continue;
|
|
8484
8485
|
}
|
|
8485
8486
|
refs.push({
|
|
8486
8487
|
areaLabel: area.label,
|
|
8487
8488
|
providerLabel: mission.providerLabel,
|
|
8488
|
-
check
|
|
8489
|
+
check,
|
|
8489
8490
|
mapCategory: area.key
|
|
8490
8491
|
});
|
|
8491
8492
|
}
|
|
@@ -8751,8 +8752,8 @@ function generateAgentSummary(artifact) {
|
|
|
8751
8752
|
const manualChecks = (artifact.missionGraph.areas ?? []).flatMap(
|
|
8752
8753
|
(area) => area.providerMissions.flatMap(
|
|
8753
8754
|
(mission) => mission.checks.filter(
|
|
8754
|
-
(
|
|
8755
|
-
).map((
|
|
8755
|
+
(check) => check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown"
|
|
8756
|
+
).map((check) => ({ area: area.label, provider: mission.providerLabel, check }))
|
|
8756
8757
|
)
|
|
8757
8758
|
);
|
|
8758
8759
|
lines.push("");
|
|
@@ -9407,28 +9408,379 @@ function sanitizeArtifactForDisk(artifact) {
|
|
|
9407
9408
|
return redactUnknown(artifact);
|
|
9408
9409
|
}
|
|
9409
9410
|
|
|
9411
|
+
// src/actions/artifacts.ts
|
|
9412
|
+
var import_promises6 = require("node:fs/promises");
|
|
9413
|
+
var import_node_path9 = require("node:path");
|
|
9414
|
+
|
|
9415
|
+
// src/actions/canonical.ts
|
|
9416
|
+
var REPO_MARKERS = [
|
|
9417
|
+
"/app/",
|
|
9418
|
+
"/src/",
|
|
9419
|
+
"/pages/",
|
|
9420
|
+
"/components/",
|
|
9421
|
+
"/lib/",
|
|
9422
|
+
"/server/",
|
|
9423
|
+
"/api/",
|
|
9424
|
+
"/supabase/",
|
|
9425
|
+
"/prisma/",
|
|
9426
|
+
"/drizzle/",
|
|
9427
|
+
"/db/",
|
|
9428
|
+
"/migrations/",
|
|
9429
|
+
"/vercel/",
|
|
9430
|
+
"/.github/"
|
|
9431
|
+
];
|
|
9432
|
+
function slashPath(value) {
|
|
9433
|
+
return value.replace(/\\/g, "/").replace(/\/+/g, "/").trim();
|
|
9434
|
+
}
|
|
9435
|
+
function stripQueryAndHash(value) {
|
|
9436
|
+
const queryIndex = value.search(/[?#]/);
|
|
9437
|
+
return queryIndex === -1 ? value : value.slice(0, queryIndex);
|
|
9438
|
+
}
|
|
9439
|
+
function trimSlashes(value) {
|
|
9440
|
+
return value.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
9441
|
+
}
|
|
9442
|
+
function stripWindowsDrive(value) {
|
|
9443
|
+
return value.replace(/^[a-z]:\//i, "");
|
|
9444
|
+
}
|
|
9445
|
+
function stripAbsolutePrefix(value) {
|
|
9446
|
+
const normalized = slashPath(value);
|
|
9447
|
+
const isAbsolute3 = /^[a-z]:\//i.test(normalized) || normalized.startsWith("/");
|
|
9448
|
+
if (!isAbsolute3) {
|
|
9449
|
+
return normalized;
|
|
9450
|
+
}
|
|
9451
|
+
const lower = normalized.toLowerCase();
|
|
9452
|
+
for (const marker of REPO_MARKERS) {
|
|
9453
|
+
const index = lower.lastIndexOf(marker);
|
|
9454
|
+
if (index !== -1) {
|
|
9455
|
+
return normalized.slice(index + 1);
|
|
9456
|
+
}
|
|
9457
|
+
}
|
|
9458
|
+
return stripWindowsDrive(normalized).replace(/^users\/[^/]+\//i, "").replace(/^home\/[^/]+\//i, "");
|
|
9459
|
+
}
|
|
9460
|
+
function normalizeActionPath(path) {
|
|
9461
|
+
return trimSlashes(stripAbsolutePrefix(stripQueryAndHash(path))).toLowerCase();
|
|
9462
|
+
}
|
|
9463
|
+
function displayRepoPath(path) {
|
|
9464
|
+
return trimSlashes(stripAbsolutePrefix(stripQueryAndHash(path)));
|
|
9465
|
+
}
|
|
9466
|
+
function normalizeActionValues(values = []) {
|
|
9467
|
+
return Array.from(new Set(values.map((value) => value.trim().toLowerCase()).filter(Boolean))).sort();
|
|
9468
|
+
}
|
|
9469
|
+
function normalizeSegment(value) {
|
|
9470
|
+
const normalized = value?.trim().toLowerCase();
|
|
9471
|
+
return normalized || void 0;
|
|
9472
|
+
}
|
|
9473
|
+
function buildActionKey(input) {
|
|
9474
|
+
const segments = [
|
|
9475
|
+
normalizeSegment(input.kind),
|
|
9476
|
+
normalizeSegment(input.provider),
|
|
9477
|
+
normalizeSegment(input.category),
|
|
9478
|
+
input.target ? normalizeActionPath(input.target) : void 0
|
|
9479
|
+
].filter((segment) => Boolean(segment));
|
|
9480
|
+
const values = normalizeActionValues(input.values);
|
|
9481
|
+
if (values.length > 0) {
|
|
9482
|
+
segments.push(values.join(","));
|
|
9483
|
+
}
|
|
9484
|
+
return segments.join(":");
|
|
9485
|
+
}
|
|
9486
|
+
function normalizeFingerprintValue(value) {
|
|
9487
|
+
if (Array.isArray(value)) {
|
|
9488
|
+
const normalized = value.map((entry) => normalizeFingerprintValue(entry)).filter((entry) => entry !== void 0);
|
|
9489
|
+
if (normalized.every((entry) => typeof entry === "string")) {
|
|
9490
|
+
return Array.from(new Set(normalized)).sort();
|
|
9491
|
+
}
|
|
9492
|
+
return normalized;
|
|
9493
|
+
}
|
|
9494
|
+
if (value && typeof value === "object") {
|
|
9495
|
+
return Object.fromEntries(
|
|
9496
|
+
Object.entries(value).filter(([, entry]) => entry !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, normalizeFingerprintValue(entry)])
|
|
9497
|
+
);
|
|
9498
|
+
}
|
|
9499
|
+
if (typeof value === "string") {
|
|
9500
|
+
return value.trim();
|
|
9501
|
+
}
|
|
9502
|
+
return value;
|
|
9503
|
+
}
|
|
9504
|
+
function actionFingerprint(value) {
|
|
9505
|
+
return JSON.stringify(normalizeFingerprintValue(value));
|
|
9506
|
+
}
|
|
9507
|
+
|
|
9508
|
+
// src/actions/derive.ts
|
|
9509
|
+
function verifyActionCommand(id) {
|
|
9510
|
+
return `${PUBLIC_COMMAND} verify --action ${id}`;
|
|
9511
|
+
}
|
|
9512
|
+
function providerTitle(task) {
|
|
9513
|
+
const provider2 = task.providerAction?.provider.toLowerCase();
|
|
9514
|
+
if (provider2 === "stripe") {
|
|
9515
|
+
return "Connect Stripe Webhook";
|
|
9516
|
+
}
|
|
9517
|
+
if (provider2 === "supabase") {
|
|
9518
|
+
return "Confirm Supabase Production Proof";
|
|
9519
|
+
}
|
|
9520
|
+
if (provider2 === "vercel") {
|
|
9521
|
+
return "Confirm Vercel Production Setup";
|
|
9522
|
+
}
|
|
9523
|
+
return task.title;
|
|
9524
|
+
}
|
|
9525
|
+
function deriveProviderAction(task) {
|
|
9526
|
+
const provider2 = task.providerAction?.provider.toLowerCase() ?? "provider";
|
|
9527
|
+
const exactStep = task.providerAction?.exactStep ?? task.title;
|
|
9528
|
+
const title = providerTitle(task);
|
|
9529
|
+
const actionKey = buildActionKey({
|
|
9530
|
+
kind: "provider-action",
|
|
9531
|
+
provider: provider2,
|
|
9532
|
+
category: task.gapId,
|
|
9533
|
+
target: exactStep,
|
|
9534
|
+
values: [task.providerAction?.doneSignal ?? ""]
|
|
9535
|
+
});
|
|
9536
|
+
return {
|
|
9537
|
+
id: "pending",
|
|
9538
|
+
actionKey,
|
|
9539
|
+
revision: 1,
|
|
9540
|
+
kind: "provider-action",
|
|
9541
|
+
provider: provider2,
|
|
9542
|
+
title,
|
|
9543
|
+
status: provider2 === "supabase" ? "waiting-on-database-proof" : "waiting-on-provider",
|
|
9544
|
+
severity: task.severity,
|
|
9545
|
+
gapId: task.gapId,
|
|
9546
|
+
readiness: [task.providerAction?.doneSignal ?? "Provider step identified"],
|
|
9547
|
+
target: {
|
|
9548
|
+
type: "provider",
|
|
9549
|
+
label: exactStep,
|
|
9550
|
+
provider: provider2
|
|
9551
|
+
},
|
|
9552
|
+
verifyCommand: verifyActionCommand("pending"),
|
|
9553
|
+
resumeInstruction: `${title} is complete. Continue VibeRaven from pending.`
|
|
9554
|
+
};
|
|
9555
|
+
}
|
|
9556
|
+
function deriveRepoCodeAction(task) {
|
|
9557
|
+
const targetSource = task.file ?? task.gapId;
|
|
9558
|
+
const canonicalFile = normalizeActionPath(targetSource);
|
|
9559
|
+
const displayFile = displayRepoPath(targetSource);
|
|
9560
|
+
const actionKey = buildActionKey({
|
|
9561
|
+
kind: "repo-code",
|
|
9562
|
+
provider: "repo",
|
|
9563
|
+
category: task.gapId,
|
|
9564
|
+
target: canonicalFile,
|
|
9565
|
+
values: [task.mcpTool ?? "", task.exactFix ?? ""]
|
|
9566
|
+
});
|
|
9567
|
+
return {
|
|
9568
|
+
id: "pending",
|
|
9569
|
+
actionKey,
|
|
9570
|
+
revision: 1,
|
|
9571
|
+
kind: "repo-code",
|
|
9572
|
+
title: task.title,
|
|
9573
|
+
status: "active",
|
|
9574
|
+
severity: task.severity,
|
|
9575
|
+
gapId: task.gapId,
|
|
9576
|
+
readiness: [task.exactFix ?? "Repo-code fix is available"],
|
|
9577
|
+
target: task.file ? { type: "file", label: "Repo target", path: displayFile } : { type: "command", label: "Apply repo fix", command: task.mcpTool ?? `${PUBLIC_COMMAND} prompt --gap ${task.gapId}` },
|
|
9578
|
+
fileTargets: task.file ? [{ path: displayFile, reason: task.exactFix }] : void 0,
|
|
9579
|
+
verifyCommand: verifyActionCommand("pending"),
|
|
9580
|
+
resumeInstruction: `${task.title} is complete. Continue VibeRaven from pending.`,
|
|
9581
|
+
mcpTool: task.mcpTool,
|
|
9582
|
+
mcpArgs: task.mcpArgs,
|
|
9583
|
+
fallbackCommand: `${PUBLIC_COMMAND} --heal --apply --gap ${task.gapId} --yes`
|
|
9584
|
+
};
|
|
9585
|
+
}
|
|
9586
|
+
function deriveVerifyAction() {
|
|
9587
|
+
return {
|
|
9588
|
+
id: "pending",
|
|
9589
|
+
actionKey: buildActionKey({
|
|
9590
|
+
kind: "verify",
|
|
9591
|
+
provider: "gate",
|
|
9592
|
+
category: "final",
|
|
9593
|
+
target: `${PUBLIC_COMMAND} --strict`
|
|
9594
|
+
}),
|
|
9595
|
+
revision: 1,
|
|
9596
|
+
kind: "verify",
|
|
9597
|
+
title: "Run Final Verification",
|
|
9598
|
+
status: "blocked",
|
|
9599
|
+
readiness: ["Run after provider and repo-code actions are complete"],
|
|
9600
|
+
target: { type: "command", label: "Final strict gate", command: `${PUBLIC_COMMAND} --strict` },
|
|
9601
|
+
verifyCommand: `${PUBLIC_COMMAND} --strict`,
|
|
9602
|
+
resumeInstruction: "Final verification finished. Continue VibeRaven from pending."
|
|
9603
|
+
};
|
|
9604
|
+
}
|
|
9605
|
+
function deriveCurrentActions(input) {
|
|
9606
|
+
const primaryActions = input.tasks.filter((task) => task.fixType === "provider-action" || task.fixType === "repo-code").map((task) => task.fixType === "provider-action" ? deriveProviderAction(task) : deriveRepoCodeAction(task));
|
|
9607
|
+
return [...primaryActions, deriveVerifyAction()];
|
|
9608
|
+
}
|
|
9609
|
+
|
|
9610
|
+
// src/actions/registry.ts
|
|
9611
|
+
var import_promises5 = require("node:fs/promises");
|
|
9612
|
+
var import_node_path8 = require("node:path");
|
|
9613
|
+
function emptyActionRegistry() {
|
|
9614
|
+
return { version: 1, nextId: 1, actions: {} };
|
|
9615
|
+
}
|
|
9616
|
+
function actionRegistryPath(cwd) {
|
|
9617
|
+
return (0, import_node_path8.join)(getProjectArtifactsDir(cwd), "action-registry.json");
|
|
9618
|
+
}
|
|
9619
|
+
async function loadActionRegistry(cwd) {
|
|
9620
|
+
try {
|
|
9621
|
+
const parsed = JSON.parse(await (0, import_promises5.readFile)(actionRegistryPath(cwd), "utf8"));
|
|
9622
|
+
if (parsed.version === 1 && Number.isInteger(parsed.nextId) && parsed.actions) {
|
|
9623
|
+
return parsed;
|
|
9624
|
+
}
|
|
9625
|
+
} catch {
|
|
9626
|
+
}
|
|
9627
|
+
return emptyActionRegistry();
|
|
9628
|
+
}
|
|
9629
|
+
async function saveActionRegistry(cwd, registry) {
|
|
9630
|
+
const dir = getProjectArtifactsDir(cwd);
|
|
9631
|
+
await (0, import_promises5.mkdir)(dir, { recursive: true });
|
|
9632
|
+
await (0, import_promises5.writeFile)(actionRegistryPath(cwd), `${JSON.stringify(registry, null, 2)}
|
|
9633
|
+
`, "utf8");
|
|
9634
|
+
}
|
|
9635
|
+
function nextActionId(registry) {
|
|
9636
|
+
const id = `VR-A${registry.nextId}`;
|
|
9637
|
+
registry.nextId += 1;
|
|
9638
|
+
return id;
|
|
9639
|
+
}
|
|
9640
|
+
function currentFingerprint(action) {
|
|
9641
|
+
return actionFingerprint({
|
|
9642
|
+
kind: action.kind,
|
|
9643
|
+
title: action.title,
|
|
9644
|
+
provider: action.provider,
|
|
9645
|
+
readiness: action.readiness ?? [],
|
|
9646
|
+
target: action.target,
|
|
9647
|
+
copyPayloads: action.copyPayloads ?? [],
|
|
9648
|
+
fileTargets: action.fileTargets ?? [],
|
|
9649
|
+
verifyCommand: action.verifyCommand,
|
|
9650
|
+
resumeInstruction: action.resumeInstruction
|
|
9651
|
+
});
|
|
9652
|
+
}
|
|
9653
|
+
function replaceActionHandle(value, id) {
|
|
9654
|
+
return value?.replace(/pending|VR-A\d+/g, id);
|
|
9655
|
+
}
|
|
9656
|
+
function replaceActionTarget(action, id) {
|
|
9657
|
+
const { target } = action;
|
|
9658
|
+
if (!target) {
|
|
9659
|
+
return void 0;
|
|
9660
|
+
}
|
|
9661
|
+
if (target.type === "url") {
|
|
9662
|
+
return { ...target, href: replaceActionHandle(target.href, id) ?? target.href };
|
|
9663
|
+
}
|
|
9664
|
+
if (target.type === "command" || target.type === "verify") {
|
|
9665
|
+
return { ...target, command: replaceActionHandle(target.command, id) ?? target.command };
|
|
9666
|
+
}
|
|
9667
|
+
return target;
|
|
9668
|
+
}
|
|
9669
|
+
async function assignActionIds(input) {
|
|
9670
|
+
const now = input.now ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
9671
|
+
const registry = await loadActionRegistry(input.cwd);
|
|
9672
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9673
|
+
const actions = input.actions.map((action) => {
|
|
9674
|
+
seen.add(action.actionKey);
|
|
9675
|
+
const fingerprint = currentFingerprint(action);
|
|
9676
|
+
let entry = registry.actions[action.actionKey];
|
|
9677
|
+
if (!entry) {
|
|
9678
|
+
entry = {
|
|
9679
|
+
id: nextActionId(registry),
|
|
9680
|
+
actionKey: action.actionKey,
|
|
9681
|
+
status: "active",
|
|
9682
|
+
createdAt: now,
|
|
9683
|
+
lastSeenAt: now,
|
|
9684
|
+
revision: 1,
|
|
9685
|
+
fingerprint,
|
|
9686
|
+
title: action.title,
|
|
9687
|
+
gapId: action.gapId,
|
|
9688
|
+
kind: action.kind,
|
|
9689
|
+
provider: action.provider
|
|
9690
|
+
};
|
|
9691
|
+
} else {
|
|
9692
|
+
entry.status = "active";
|
|
9693
|
+
entry.lastSeenAt = now;
|
|
9694
|
+
entry.title = action.title;
|
|
9695
|
+
entry.gapId = action.gapId;
|
|
9696
|
+
entry.kind = action.kind;
|
|
9697
|
+
entry.provider = action.provider;
|
|
9698
|
+
if (entry.fingerprint !== fingerprint) {
|
|
9699
|
+
entry.revision += 1;
|
|
9700
|
+
entry.fingerprint = fingerprint;
|
|
9701
|
+
}
|
|
9702
|
+
}
|
|
9703
|
+
registry.actions[action.actionKey] = entry;
|
|
9704
|
+
return {
|
|
9705
|
+
...action,
|
|
9706
|
+
id: entry.id,
|
|
9707
|
+
revision: entry.revision,
|
|
9708
|
+
verifyCommand: replaceActionHandle(action.verifyCommand, entry.id),
|
|
9709
|
+
resumeInstruction: replaceActionHandle(action.resumeInstruction, entry.id),
|
|
9710
|
+
target: replaceActionTarget(action, entry.id)
|
|
9711
|
+
};
|
|
9712
|
+
});
|
|
9713
|
+
for (const entry of Object.values(registry.actions)) {
|
|
9714
|
+
if (entry.status === "active" && !seen.has(entry.actionKey)) {
|
|
9715
|
+
entry.status = entry.gapId && input.currentGapIds && !input.currentGapIds.has(entry.gapId) ? "resolved" : "stale";
|
|
9716
|
+
}
|
|
9717
|
+
}
|
|
9718
|
+
await saveActionRegistry(input.cwd, registry);
|
|
9719
|
+
return { actions, registry };
|
|
9720
|
+
}
|
|
9721
|
+
async function resolveActionById(cwd, id) {
|
|
9722
|
+
const registry = await loadActionRegistry(cwd);
|
|
9723
|
+
return Object.values(registry.actions).find((entry) => entry.id === id);
|
|
9724
|
+
}
|
|
9725
|
+
|
|
9726
|
+
// src/actions/artifacts.ts
|
|
9727
|
+
function gateStatus2(artifact) {
|
|
9728
|
+
if (!Array.isArray(artifact.gaps)) {
|
|
9729
|
+
return "unknown";
|
|
9730
|
+
}
|
|
9731
|
+
return artifact.gaps.length === 0 ? "clear" : "not_clear";
|
|
9732
|
+
}
|
|
9733
|
+
async function writeActionArtifacts(input) {
|
|
9734
|
+
const derived = deriveCurrentActions({
|
|
9735
|
+
artifact: input.artifact,
|
|
9736
|
+
tasks: input.tasks,
|
|
9737
|
+
paths: input.paths
|
|
9738
|
+
});
|
|
9739
|
+
const currentGapIds = new Set(input.artifact.gaps.map((gap) => gap.id));
|
|
9740
|
+
const assigned = await assignActionIds({
|
|
9741
|
+
cwd: input.cwd,
|
|
9742
|
+
actions: derived,
|
|
9743
|
+
currentGapIds
|
|
9744
|
+
});
|
|
9745
|
+
const manifest = {
|
|
9746
|
+
version: 1,
|
|
9747
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9748
|
+
workspaceRoot: ".",
|
|
9749
|
+
gateStatus: gateStatus2(input.artifact),
|
|
9750
|
+
actions: assigned.actions
|
|
9751
|
+
};
|
|
9752
|
+
const actionsPath = (0, import_node_path9.join)(getProjectArtifactsDir(input.cwd), "actions.json");
|
|
9753
|
+
await (0, import_promises6.writeFile)(actionsPath, `${JSON.stringify(manifest, null, 2)}
|
|
9754
|
+
`, "utf8");
|
|
9755
|
+
return {
|
|
9756
|
+
actionsPath,
|
|
9757
|
+
actionRegistryPath: actionRegistryPath(input.cwd),
|
|
9758
|
+
manifest
|
|
9759
|
+
};
|
|
9760
|
+
}
|
|
9761
|
+
|
|
9410
9762
|
// src/artifacts.ts
|
|
9411
9763
|
async function copyReportAssets(reportAssetsDir) {
|
|
9412
9764
|
const sourceDir = getBundledReportAssetsDir();
|
|
9413
|
-
await (0,
|
|
9765
|
+
await (0, import_promises7.mkdir)((0, import_node_path10.join)(reportAssetsDir, "assets"), { recursive: true });
|
|
9414
9766
|
for (const rel of REPORT_ASSET_FILES) {
|
|
9415
|
-
await (0,
|
|
9767
|
+
await (0, import_promises7.copyFile)((0, import_node_path10.join)(sourceDir, rel), (0, import_node_path10.join)(reportAssetsDir, rel));
|
|
9416
9768
|
}
|
|
9417
9769
|
}
|
|
9418
9770
|
async function writeScanArtifacts(options) {
|
|
9419
9771
|
const cwd = options.cwd ?? options.artifact.workspacePath;
|
|
9420
9772
|
const dir = getProjectArtifactsDir(cwd);
|
|
9421
|
-
await (0,
|
|
9422
|
-
const jsonPath = (0,
|
|
9423
|
-
const gateResultPath = (0,
|
|
9424
|
-
const contextMapPath = (0,
|
|
9425
|
-
const gapsDir = (0,
|
|
9426
|
-
const tasklistPath = (0,
|
|
9427
|
-
const summaryPath = (0,
|
|
9428
|
-
const playbookPath = (0,
|
|
9429
|
-
const reportPath = (0,
|
|
9430
|
-
const reportAssetsDir = (0,
|
|
9431
|
-
await (0,
|
|
9773
|
+
await (0, import_promises7.mkdir)(dir, { recursive: true });
|
|
9774
|
+
const jsonPath = (0, import_node_path10.join)(dir, "last-scan.json");
|
|
9775
|
+
const gateResultPath = (0, import_node_path10.join)(dir, "gate-result.json");
|
|
9776
|
+
const contextMapPath = (0, import_node_path10.join)(dir, "context-map.json");
|
|
9777
|
+
const gapsDir = (0, import_node_path10.join)(dir, "gaps");
|
|
9778
|
+
const tasklistPath = (0, import_node_path10.join)(dir, "agent-tasklist.md");
|
|
9779
|
+
const summaryPath = (0, import_node_path10.join)(dir, "agent-summary.md");
|
|
9780
|
+
const playbookPath = (0, import_node_path10.join)(dir, "launch-playbook.md");
|
|
9781
|
+
const reportPath = (0, import_node_path10.join)(dir, "report.html");
|
|
9782
|
+
const reportAssetsDir = (0, import_node_path10.join)(dir, "report");
|
|
9783
|
+
await (0, import_promises7.mkdir)(gapsDir, { recursive: true });
|
|
9432
9784
|
const safe = sanitizeArtifactForDisk(options.artifact);
|
|
9433
9785
|
const json = `${JSON.stringify(safe, null, 2)}
|
|
9434
9786
|
`;
|
|
@@ -9443,17 +9795,23 @@ async function writeScanArtifacts(options) {
|
|
|
9443
9795
|
`;
|
|
9444
9796
|
const gapEvidenceFiles = generateGapEvidenceFiles(safe);
|
|
9445
9797
|
await copyReportAssets(reportAssetsDir);
|
|
9446
|
-
await (0,
|
|
9447
|
-
await (0,
|
|
9798
|
+
await (0, import_promises7.writeFile)(gateResultPath, gateResult, "utf-8");
|
|
9799
|
+
await (0, import_promises7.writeFile)(contextMapPath, contextMap, "utf-8");
|
|
9448
9800
|
for (const gap of gapEvidenceFiles) {
|
|
9449
|
-
await (0,
|
|
9801
|
+
await (0, import_promises7.writeFile)((0, import_node_path10.join)(dir, "gaps", `${gap.content.id}.json`), `${JSON.stringify(gap.content, null, 2)}
|
|
9450
9802
|
`, "utf-8");
|
|
9451
9803
|
}
|
|
9452
|
-
await (0,
|
|
9453
|
-
await (0,
|
|
9454
|
-
await (0,
|
|
9455
|
-
await (0,
|
|
9456
|
-
await (0,
|
|
9804
|
+
await (0, import_promises7.writeFile)(tasklistPath, tasklist, "utf-8");
|
|
9805
|
+
await (0, import_promises7.writeFile)(jsonPath, json, "utf-8");
|
|
9806
|
+
await (0, import_promises7.writeFile)(summaryPath, summary, "utf-8");
|
|
9807
|
+
await (0, import_promises7.writeFile)(playbookPath, playbook, "utf-8");
|
|
9808
|
+
await (0, import_promises7.writeFile)(reportPath, html, "utf-8");
|
|
9809
|
+
const actionArtifacts = await writeActionArtifacts({
|
|
9810
|
+
cwd,
|
|
9811
|
+
artifact: safe,
|
|
9812
|
+
tasks,
|
|
9813
|
+
paths: { reportPath, playbookPath }
|
|
9814
|
+
});
|
|
9457
9815
|
return {
|
|
9458
9816
|
dir,
|
|
9459
9817
|
jsonPath,
|
|
@@ -9464,7 +9822,9 @@ async function writeScanArtifacts(options) {
|
|
|
9464
9822
|
summaryPath,
|
|
9465
9823
|
playbookPath,
|
|
9466
9824
|
reportPath,
|
|
9467
|
-
reportAssetsDir
|
|
9825
|
+
reportAssetsDir,
|
|
9826
|
+
actionsPath: actionArtifacts.actionsPath,
|
|
9827
|
+
actionRegistryPath: actionArtifacts.actionRegistryPath
|
|
9468
9828
|
};
|
|
9469
9829
|
}
|
|
9470
9830
|
|
|
@@ -9541,12 +9901,12 @@ async function copyToClipboard(text) {
|
|
|
9541
9901
|
}
|
|
9542
9902
|
}
|
|
9543
9903
|
function pipeToCommand(command, text, extraArgs = []) {
|
|
9544
|
-
return new Promise((
|
|
9904
|
+
return new Promise((resolve6, reject) => {
|
|
9545
9905
|
const child = (0, import_node_child_process2.spawn)(command, extraArgs, { stdio: ["pipe", "ignore", "ignore"] });
|
|
9546
9906
|
child.on("error", reject);
|
|
9547
9907
|
child.on("close", (code) => {
|
|
9548
9908
|
if (code === 0) {
|
|
9549
|
-
|
|
9909
|
+
resolve6();
|
|
9550
9910
|
} else {
|
|
9551
9911
|
reject(new Error(`${command} exited with code ${code ?? "unknown"}`));
|
|
9552
9912
|
}
|
|
@@ -9573,9 +9933,9 @@ function missionMatchesProvider(mission, provider2) {
|
|
|
9573
9933
|
return normalizeProviderToken2(mission.provider || mission.providerLabel || mission.key) === current || normalizeProviderToken2(mission.providerLabel || mission.provider || mission.key) === current;
|
|
9574
9934
|
}
|
|
9575
9935
|
function missionEvidenceScore(mission) {
|
|
9576
|
-
const repoVerified = mission.checks.filter((
|
|
9936
|
+
const repoVerified = mission.checks.filter((check) => check.evidenceClass === "repo-verified" || check.status === "passed").length;
|
|
9577
9937
|
const missing = mission.checks.filter(
|
|
9578
|
-
(
|
|
9938
|
+
(check) => check.evidenceClass === "missing-repo-fix" || check.status === "missing" || check.status === "failed"
|
|
9579
9939
|
).length;
|
|
9580
9940
|
return repoVerified * 100 + (mission.readinessPercent ?? 0) - missing;
|
|
9581
9941
|
}
|
|
@@ -9595,7 +9955,7 @@ function openChecksForMission(mission) {
|
|
|
9595
9955
|
return 0;
|
|
9596
9956
|
}
|
|
9597
9957
|
return mission.checks.filter(
|
|
9598
|
-
(
|
|
9958
|
+
(check) => check.status === "missing" || check.status === "failed" || check.status === "needs-connection"
|
|
9599
9959
|
).length;
|
|
9600
9960
|
}
|
|
9601
9961
|
|
|
@@ -9627,7 +9987,7 @@ function manualActionCheckCount(artifact) {
|
|
|
9627
9987
|
return (artifact.missionGraph.areas ?? []).reduce((areaTotal, area) => {
|
|
9628
9988
|
return areaTotal + area.providerMissions.reduce((missionTotal, mission) => {
|
|
9629
9989
|
return missionTotal + mission.checks.filter(
|
|
9630
|
-
(
|
|
9990
|
+
(check) => check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown"
|
|
9631
9991
|
).length;
|
|
9632
9992
|
}, 0);
|
|
9633
9993
|
}, 0);
|
|
@@ -9679,7 +10039,7 @@ function printScanSummary(artifact, paths) {
|
|
|
9679
10039
|
if (next.upgradeUrl) {
|
|
9680
10040
|
console.log(`Upgrade: ${next.upgradeUrl}`);
|
|
9681
10041
|
}
|
|
9682
|
-
console.log(formatAgentStatus(AGENT_ACTION, "
|
|
10042
|
+
console.log(formatAgentStatus(AGENT_ACTION, "Read .viberaven/agent-tasklist.md, run the next command above, then rescan."));
|
|
9683
10043
|
console.log("");
|
|
9684
10044
|
if (artifact.usage) {
|
|
9685
10045
|
const lanes = artifact.usage.unlockedMapCategoryKeys.length;
|
|
@@ -9704,8 +10064,8 @@ function printScanSummary(artifact, paths) {
|
|
|
9704
10064
|
}
|
|
9705
10065
|
|
|
9706
10066
|
// src/runnerConnect.ts
|
|
9707
|
-
var
|
|
9708
|
-
var
|
|
10067
|
+
var import_promises8 = require("node:fs/promises");
|
|
10068
|
+
var import_node_path11 = require("node:path");
|
|
9709
10069
|
var import_node_child_process3 = require("node:child_process");
|
|
9710
10070
|
var import_node_crypto = require("node:crypto");
|
|
9711
10071
|
|
|
@@ -9942,10 +10302,10 @@ function sleepForRunnerPoll(ms, signal) {
|
|
|
9942
10302
|
if (signal?.aborted) {
|
|
9943
10303
|
return Promise.reject(createRunnerWatchAbortError());
|
|
9944
10304
|
}
|
|
9945
|
-
return new Promise((
|
|
10305
|
+
return new Promise((resolve6, reject) => {
|
|
9946
10306
|
const timer = setTimeout(() => {
|
|
9947
10307
|
cleanup();
|
|
9948
|
-
|
|
10308
|
+
resolve6();
|
|
9949
10309
|
}, ms);
|
|
9950
10310
|
const onAbort = () => {
|
|
9951
10311
|
cleanup();
|
|
@@ -10048,12 +10408,12 @@ async function executeSafeFixJob(job, workspaceRoot) {
|
|
|
10048
10408
|
}
|
|
10049
10409
|
async function createSafeFixFile(job, input, targetPath) {
|
|
10050
10410
|
try {
|
|
10051
|
-
await (0,
|
|
10411
|
+
await (0, import_promises8.access)(targetPath);
|
|
10052
10412
|
return safeFixNeedsUser(job, "SAFE_FIX_TARGET_EXISTS", `Refusing to overwrite ${input.path}.`);
|
|
10053
10413
|
} catch {
|
|
10054
10414
|
}
|
|
10055
10415
|
try {
|
|
10056
|
-
await (0,
|
|
10416
|
+
await (0, import_promises8.mkdir)((0, import_node_path11.dirname)(targetPath), { recursive: true });
|
|
10057
10417
|
await writeNewFileAtomically(targetPath, input.content);
|
|
10058
10418
|
} catch {
|
|
10059
10419
|
return safeFixNeedsUser(job, "SAFE_FIX_WRITE_FAILED", `Could not create ${input.path}.`);
|
|
@@ -10063,7 +10423,7 @@ async function createSafeFixFile(job, input, targetPath) {
|
|
|
10063
10423
|
async function applySafeFixReplacement(job, input, targetPath) {
|
|
10064
10424
|
let current;
|
|
10065
10425
|
try {
|
|
10066
|
-
current = await (0,
|
|
10426
|
+
current = await (0, import_promises8.readFile)(targetPath, "utf-8");
|
|
10067
10427
|
} catch {
|
|
10068
10428
|
return safeFixNeedsUser(job, "SAFE_FIX_TARGET_MISSING", `Cannot update missing file ${input.path}.`);
|
|
10069
10429
|
}
|
|
@@ -10121,17 +10481,17 @@ function safeFixNeedsUser(job, code, message) {
|
|
|
10121
10481
|
};
|
|
10122
10482
|
}
|
|
10123
10483
|
async function resolveSafeFixTarget(workspaceRoot, relativePath) {
|
|
10124
|
-
const root = await (0,
|
|
10125
|
-
const target = (0,
|
|
10484
|
+
const root = await (0, import_promises8.realpath)(workspaceRoot).catch(() => (0, import_node_path11.resolve)(workspaceRoot));
|
|
10485
|
+
const target = (0, import_node_path11.resolve)(root, relativePath);
|
|
10126
10486
|
if (!isInsideRoot(root, target)) {
|
|
10127
10487
|
return { ok: false, reason: "Safe fix target escaped the workspace root." };
|
|
10128
10488
|
}
|
|
10129
|
-
const parent = (0,
|
|
10489
|
+
const parent = (0, import_node_path11.dirname)(target);
|
|
10130
10490
|
const parentReal = await realpathNearestExisting(parent, root);
|
|
10131
10491
|
if (!isInsideRoot(root, parentReal)) {
|
|
10132
10492
|
return { ok: false, reason: "Safe fix parent path escaped the workspace root." };
|
|
10133
10493
|
}
|
|
10134
|
-
const targetReal = await (0,
|
|
10494
|
+
const targetReal = await (0, import_promises8.realpath)(target).catch(() => null);
|
|
10135
10495
|
if (targetReal && !isInsideRoot(root, targetReal)) {
|
|
10136
10496
|
return { ok: false, reason: "Safe fix target path escaped the workspace root." };
|
|
10137
10497
|
}
|
|
@@ -10141,10 +10501,10 @@ async function realpathNearestExisting(path, root) {
|
|
|
10141
10501
|
let candidate = path;
|
|
10142
10502
|
while (isInsideRoot(root, candidate)) {
|
|
10143
10503
|
try {
|
|
10144
|
-
await (0,
|
|
10145
|
-
return (0,
|
|
10504
|
+
await (0, import_promises8.lstat)(candidate);
|
|
10505
|
+
return (0, import_promises8.realpath)(candidate);
|
|
10146
10506
|
} catch {
|
|
10147
|
-
const next = (0,
|
|
10507
|
+
const next = (0, import_node_path11.dirname)(candidate);
|
|
10148
10508
|
if (next === candidate) {
|
|
10149
10509
|
break;
|
|
10150
10510
|
}
|
|
@@ -10154,29 +10514,29 @@ async function realpathNearestExisting(path, root) {
|
|
|
10154
10514
|
return root;
|
|
10155
10515
|
}
|
|
10156
10516
|
function isInsideRoot(root, candidate) {
|
|
10157
|
-
const rel = (0,
|
|
10158
|
-
return rel === "" || !rel.startsWith("..") && !(0,
|
|
10517
|
+
const rel = (0, import_node_path11.relative)(root, candidate);
|
|
10518
|
+
return rel === "" || !rel.startsWith("..") && !(0, import_node_path11.isAbsolute)(rel);
|
|
10159
10519
|
}
|
|
10160
10520
|
async function writeNewFileAtomically(targetPath, content) {
|
|
10161
10521
|
const tempPath = tempPathFor(targetPath);
|
|
10162
10522
|
try {
|
|
10163
|
-
await (0,
|
|
10164
|
-
await (0,
|
|
10523
|
+
await (0, import_promises8.writeFile)(tempPath, content, { encoding: "utf-8", flag: "wx" });
|
|
10524
|
+
await (0, import_promises8.link)(tempPath, targetPath);
|
|
10165
10525
|
} finally {
|
|
10166
|
-
await (0,
|
|
10526
|
+
await (0, import_promises8.rm)(tempPath, { force: true }).catch(() => void 0);
|
|
10167
10527
|
}
|
|
10168
10528
|
}
|
|
10169
10529
|
async function replaceFileAtomically(targetPath, content) {
|
|
10170
10530
|
const tempPath = tempPathFor(targetPath);
|
|
10171
10531
|
try {
|
|
10172
|
-
await (0,
|
|
10173
|
-
await (0,
|
|
10532
|
+
await (0, import_promises8.writeFile)(tempPath, content, { encoding: "utf-8", flag: "wx" });
|
|
10533
|
+
await (0, import_promises8.rename)(tempPath, targetPath);
|
|
10174
10534
|
} finally {
|
|
10175
|
-
await (0,
|
|
10535
|
+
await (0, import_promises8.rm)(tempPath, { force: true }).catch(() => void 0);
|
|
10176
10536
|
}
|
|
10177
10537
|
}
|
|
10178
10538
|
function tempPathFor(targetPath) {
|
|
10179
|
-
return (0,
|
|
10539
|
+
return (0, import_node_path11.join)((0, import_node_path11.dirname)(targetPath), `.${(0, import_node_path11.basename)(targetPath)}.${(0, import_node_crypto.randomUUID)()}.tmp`);
|
|
10180
10540
|
}
|
|
10181
10541
|
async function executePackageScriptJob(job, scriptName, options) {
|
|
10182
10542
|
const packageJson = await readPackageJson(options.workspaceRoot);
|
|
@@ -10350,7 +10710,7 @@ function proofItemForJob(job, kind, label, summary, evidence, redactionSecrets =
|
|
|
10350
10710
|
}
|
|
10351
10711
|
async function readPackageJson(workspaceRoot) {
|
|
10352
10712
|
try {
|
|
10353
|
-
const raw = await (0,
|
|
10713
|
+
const raw = await (0, import_promises8.readFile)((0, import_node_path11.join)(workspaceRoot, "package.json"), "utf-8");
|
|
10354
10714
|
const parsed = JSON.parse(raw);
|
|
10355
10715
|
if (isRecord6(parsed) && isRecord6(parsed.scripts)) {
|
|
10356
10716
|
return { scripts: Object.fromEntries(Object.entries(parsed.scripts).filter(([, value]) => typeof value === "string")) };
|
|
@@ -10433,7 +10793,7 @@ async function collectLocalRepoMetadata(workspaceRoot, commandRunner = runComman
|
|
|
10433
10793
|
const headSha = headResult.ok ? normalizeSha(headResult.stdout) : null;
|
|
10434
10794
|
const dirty = statusResult.ok ? statusResult.stdout.trim().length > 0 : void 0;
|
|
10435
10795
|
return {
|
|
10436
|
-
rootName: (0,
|
|
10796
|
+
rootName: (0, import_node_path11.basename)(workspaceRoot) || "workspace",
|
|
10437
10797
|
remotes: remoteResult.ok ? parseGitRemotes(remoteResult.stdout) : [],
|
|
10438
10798
|
branch,
|
|
10439
10799
|
headSha,
|
|
@@ -10451,7 +10811,7 @@ async function detectPackageManager(workspaceRoot) {
|
|
|
10451
10811
|
];
|
|
10452
10812
|
for (const [manager, file] of checks) {
|
|
10453
10813
|
try {
|
|
10454
|
-
await (0,
|
|
10814
|
+
await (0, import_promises8.access)((0, import_node_path11.join)(workspaceRoot, file));
|
|
10455
10815
|
return manager;
|
|
10456
10816
|
} catch {
|
|
10457
10817
|
}
|
|
@@ -10517,9 +10877,9 @@ function formatRepoMatch(repoMatch) {
|
|
|
10517
10877
|
}
|
|
10518
10878
|
}
|
|
10519
10879
|
async function runCommand(command, args, cwd) {
|
|
10520
|
-
return new Promise((
|
|
10880
|
+
return new Promise((resolve6) => {
|
|
10521
10881
|
(0, import_node_child_process3.execFile)(command, args, { cwd, windowsHide: true }, (error, stdout, stderr) => {
|
|
10522
|
-
|
|
10882
|
+
resolve6({
|
|
10523
10883
|
ok: !error,
|
|
10524
10884
|
stdout: String(stdout ?? ""),
|
|
10525
10885
|
stderr: String(stderr ?? "")
|
|
@@ -10642,9 +11002,9 @@ function isRecord6(value) {
|
|
|
10642
11002
|
}
|
|
10643
11003
|
|
|
10644
11004
|
// src/tui/runInteractive.ts
|
|
10645
|
-
var
|
|
11005
|
+
var import_node_path16 = require("node:path");
|
|
10646
11006
|
|
|
10647
|
-
//
|
|
11007
|
+
// ../../../../node_modules/@clack/core/dist/index.mjs
|
|
10648
11008
|
var import_sisteransi = __toESM(require_src(), 1);
|
|
10649
11009
|
var import_node_process = require("node:process");
|
|
10650
11010
|
var g = __toESM(require("node:readline"), 1);
|
|
@@ -11002,7 +11362,7 @@ var LD = class extends x {
|
|
|
11002
11362
|
}
|
|
11003
11363
|
};
|
|
11004
11364
|
|
|
11005
|
-
//
|
|
11365
|
+
// ../../../../node_modules/@clack/prompts/dist/index.mjs
|
|
11006
11366
|
var import_node_process2 = __toESM(require("node:process"), 1);
|
|
11007
11367
|
var import_picocolors2 = __toESM(require_picocolors(), 1);
|
|
11008
11368
|
var import_sisteransi2 = __toESM(require_src(), 1);
|
|
@@ -11229,7 +11589,7 @@ function buildAgentFixPrompt(artifact, gap) {
|
|
|
11229
11589
|
}
|
|
11230
11590
|
|
|
11231
11591
|
// src/version.ts
|
|
11232
|
-
var VERSION = "1.1.
|
|
11592
|
+
var VERSION = "1.1.8";
|
|
11233
11593
|
|
|
11234
11594
|
// src/commands/guide.ts
|
|
11235
11595
|
var import_picocolors3 = __toESM(require_picocolors());
|
|
@@ -11298,8 +11658,8 @@ async function runGuideCommand(options) {
|
|
|
11298
11658
|
}
|
|
11299
11659
|
|
|
11300
11660
|
// src/commands/audit.ts
|
|
11301
|
-
var
|
|
11302
|
-
var
|
|
11661
|
+
var import_promises9 = require("node:fs/promises");
|
|
11662
|
+
var import_node_path12 = require("node:path");
|
|
11303
11663
|
var ENV_FILES = [
|
|
11304
11664
|
".env",
|
|
11305
11665
|
".env.local",
|
|
@@ -11390,7 +11750,7 @@ function buildVercelSupabaseAudit(input) {
|
|
|
11390
11750
|
buildPoolerCheck(input.files),
|
|
11391
11751
|
buildServiceRoleCheck(input.files)
|
|
11392
11752
|
];
|
|
11393
|
-
const status = checks.every((
|
|
11753
|
+
const status = checks.every((check) => check.status === "pass") ? "pass" : "needs_work";
|
|
11394
11754
|
return {
|
|
11395
11755
|
status,
|
|
11396
11756
|
summary: status === "pass" ? "Repo evidence passes the local Vercel/Supabase audit checks." : "Repo evidence needs work before claiming Vercel/Supabase production readiness.",
|
|
@@ -11399,23 +11759,23 @@ function buildVercelSupabaseAudit(input) {
|
|
|
11399
11759
|
}
|
|
11400
11760
|
async function readIfExists(projectRoot, relativePath) {
|
|
11401
11761
|
try {
|
|
11402
|
-
const absolutePath = (0,
|
|
11403
|
-
const fileStat = await (0,
|
|
11762
|
+
const absolutePath = (0, import_node_path12.join)(projectRoot, relativePath);
|
|
11763
|
+
const fileStat = await (0, import_promises9.stat)(absolutePath);
|
|
11404
11764
|
if (!fileStat.isFile()) {
|
|
11405
11765
|
return void 0;
|
|
11406
11766
|
}
|
|
11407
11767
|
return {
|
|
11408
11768
|
path: relativePath,
|
|
11409
|
-
content: await (0,
|
|
11769
|
+
content: await (0, import_promises9.readFile)(absolutePath, "utf8")
|
|
11410
11770
|
};
|
|
11411
11771
|
} catch {
|
|
11412
11772
|
return void 0;
|
|
11413
11773
|
}
|
|
11414
11774
|
}
|
|
11415
11775
|
async function collectSqlFiles(projectRoot, root) {
|
|
11416
|
-
const base = (0,
|
|
11776
|
+
const base = (0, import_node_path12.join)(projectRoot, root);
|
|
11417
11777
|
try {
|
|
11418
|
-
const rootStat = await (0,
|
|
11778
|
+
const rootStat = await (0, import_promises9.stat)(base);
|
|
11419
11779
|
if (!rootStat.isDirectory()) {
|
|
11420
11780
|
return [];
|
|
11421
11781
|
}
|
|
@@ -11426,25 +11786,25 @@ async function collectSqlFiles(projectRoot, root) {
|
|
|
11426
11786
|
async function visit(dir) {
|
|
11427
11787
|
let entries;
|
|
11428
11788
|
try {
|
|
11429
|
-
entries = await (0,
|
|
11789
|
+
entries = await (0, import_promises9.readdir)(dir, { withFileTypes: true });
|
|
11430
11790
|
} catch {
|
|
11431
11791
|
return;
|
|
11432
11792
|
}
|
|
11433
11793
|
for (const entry of entries) {
|
|
11434
11794
|
if (entry.isDirectory()) {
|
|
11435
11795
|
if (!SKIP_DIRS.has(entry.name)) {
|
|
11436
|
-
await visit((0,
|
|
11796
|
+
await visit((0, import_node_path12.join)(dir, entry.name));
|
|
11437
11797
|
}
|
|
11438
11798
|
continue;
|
|
11439
11799
|
}
|
|
11440
11800
|
if (!entry.isFile() || !entry.name.toLowerCase().endsWith(".sql")) {
|
|
11441
11801
|
continue;
|
|
11442
11802
|
}
|
|
11443
|
-
const absolutePath = (0,
|
|
11803
|
+
const absolutePath = (0, import_node_path12.join)(dir, entry.name);
|
|
11444
11804
|
try {
|
|
11445
11805
|
files.push({
|
|
11446
|
-
path: (0,
|
|
11447
|
-
content: await (0,
|
|
11806
|
+
path: (0, import_node_path12.relative)(projectRoot, absolutePath).replace(/\\/g, "/"),
|
|
11807
|
+
content: await (0, import_promises9.readFile)(absolutePath, "utf8")
|
|
11448
11808
|
});
|
|
11449
11809
|
} catch {
|
|
11450
11810
|
}
|
|
@@ -11477,13 +11837,13 @@ function renderVercelSupabaseAudit(result) {
|
|
|
11477
11837
|
"Boundary: this audit uses local repo evidence only. Provider dashboard settings still need manual verification or read-only provider MCP confirmation.",
|
|
11478
11838
|
""
|
|
11479
11839
|
];
|
|
11480
|
-
for (const
|
|
11481
|
-
lines.push(`${
|
|
11482
|
-
lines.push(` ${
|
|
11483
|
-
for (const evidence of
|
|
11840
|
+
for (const check of result.checks) {
|
|
11841
|
+
lines.push(`${check.status === "pass" ? "PASS" : "NEEDS_WORK"} ${check.id}`);
|
|
11842
|
+
lines.push(` ${check.summary}`);
|
|
11843
|
+
for (const evidence of check.evidence.slice(0, 5)) {
|
|
11484
11844
|
lines.push(` - ${evidence}`);
|
|
11485
11845
|
}
|
|
11486
|
-
if (
|
|
11846
|
+
if (check.evidence.length === 0) {
|
|
11487
11847
|
lines.push(" - No local evidence found.");
|
|
11488
11848
|
}
|
|
11489
11849
|
lines.push("");
|
|
@@ -11492,8 +11852,8 @@ function renderVercelSupabaseAudit(result) {
|
|
|
11492
11852
|
}
|
|
11493
11853
|
|
|
11494
11854
|
// src/commands/initRules.ts
|
|
11495
|
-
var
|
|
11496
|
-
var
|
|
11855
|
+
var import_promises12 = require("node:fs/promises");
|
|
11856
|
+
var import_node_path15 = require("node:path");
|
|
11497
11857
|
|
|
11498
11858
|
// src/commands/agentRulesBlock.ts
|
|
11499
11859
|
var VIBERAVEN_BLOCK_START = "<!-- VIBERAVEN:START -->";
|
|
@@ -11515,15 +11875,6 @@ Maps repo evidence to OWASP LLM Top 10 launch risks. Supports SAFECode-style dep
|
|
|
11515
11875
|
var STACK_FRAMING = `Stack context for this repo: Next.js, Supabase, Vercel, VibeRaven.
|
|
11516
11876
|
Do NOT deploy to Vercel or ship to production users without running \`${PUBLIC_AGENT_MODE_COMMAND}\`.
|
|
11517
11877
|
Do NOT rely on manual production checklists when VibeRaven gate artifacts exist in \`.viberaven/\`.`;
|
|
11518
|
-
var PRODUCTION_PROTOCOL = `## VibeRaven Production Protocol
|
|
11519
|
-
|
|
11520
|
-
Before making deploy, auth, database, payment, API, or scaling changes:
|
|
11521
|
-
|
|
11522
|
-
1. Read \`.viberaven/prp.json\`.
|
|
11523
|
-
2. Follow \`.viberaven/mission-map.md\`.
|
|
11524
|
-
3. Do not deploy if \`decision.status\` is \`blocked\`.
|
|
11525
|
-
4. Fix \`nextActions\` in order.
|
|
11526
|
-
5. Re-run \`${PUBLIC_AGENT_MODE_COMMAND}\`.`;
|
|
11527
11878
|
var AGENT_NEGATIVE_CONSTRAINTS = `## Event-Triggered Production Verification
|
|
11528
11879
|
|
|
11529
11880
|
Trigger: modifications to \`package.json\`, \`vercel.json\`, \`supabase/migrations/**/*\`, \`.env.example\`, auth/billing/webhook paths, or deploy workflow files.
|
|
@@ -11549,8 +11900,6 @@ var AGENT_RULES_BODY = `${AGENT_RULES_PREAMBLE}
|
|
|
11549
11900
|
|
|
11550
11901
|
${STACK_FRAMING}
|
|
11551
11902
|
|
|
11552
|
-
${PRODUCTION_PROTOCOL}
|
|
11553
|
-
|
|
11554
11903
|
${AGENT_NEGATIVE_CONSTRAINTS}
|
|
11555
11904
|
|
|
11556
11905
|
## VibeRaven Production-Readiness Gate
|
|
@@ -11600,11 +11949,10 @@ var AGENT_CONTEXT_BODY = `${AGENT_RULES_PREAMBLE}
|
|
|
11600
11949
|
|
|
11601
11950
|
After \`--agent-mode\`, read these artifacts in order:
|
|
11602
11951
|
|
|
11603
|
-
1. \`.viberaven/
|
|
11604
|
-
2. \`.viberaven/
|
|
11605
|
-
3. \`.viberaven/
|
|
11606
|
-
4. \`.viberaven/
|
|
11607
|
-
5. \`.viberaven/context-map.json\``;
|
|
11952
|
+
1. \`.viberaven/mission-map.md\`
|
|
11953
|
+
2. \`.viberaven/agent-tasklist.md\`
|
|
11954
|
+
3. \`.viberaven/gate-result.json\`
|
|
11955
|
+
4. \`.viberaven/context-map.json\``;
|
|
11608
11956
|
var MISSION_MAP_BODY = `${AGENT_RULES_PREAMBLE}
|
|
11609
11957
|
|
|
11610
11958
|
## Mission Map loop
|
|
@@ -11742,8 +12090,8 @@ function escapeRegExp3(value) {
|
|
|
11742
12090
|
}
|
|
11743
12091
|
|
|
11744
12092
|
// src/commands/cursorRulesPack.ts
|
|
11745
|
-
var
|
|
11746
|
-
var
|
|
12093
|
+
var import_promises10 = require("node:fs/promises");
|
|
12094
|
+
var import_node_path13 = require("node:path");
|
|
11747
12095
|
var CURSOR_RULES_DIR = ".cursor/rules";
|
|
11748
12096
|
var LEGACY_CURSOR_RULE_FILE = `${CURSOR_RULES_DIR}/viberaven.mdc`;
|
|
11749
12097
|
var DOMAIN_PATH_POINTER = "Before editing these files, read `.viberaven/agent-context.md` and `.viberaven/mission-map.md`.";
|
|
@@ -11827,25 +12175,25 @@ async function initCursorRulesPack(options) {
|
|
|
11827
12175
|
const pack = buildCursorRulesPack();
|
|
11828
12176
|
for (const rule of pack) {
|
|
11829
12177
|
const file = `${CURSOR_RULES_DIR}/${rule.filename}`;
|
|
11830
|
-
const path = (0,
|
|
12178
|
+
const path = (0, import_node_path13.join)(options.cwd, file);
|
|
11831
12179
|
const existing = await readExistingFile(path);
|
|
11832
12180
|
const changed = !existing.exists || existing.content !== rule.content;
|
|
11833
12181
|
const action = !existing.exists ? "created" : changed ? "updated" : "unchanged";
|
|
11834
12182
|
if (!options.dryRun && changed) {
|
|
11835
|
-
await (0,
|
|
11836
|
-
await (0,
|
|
12183
|
+
await (0, import_promises10.mkdir)((0, import_node_path13.join)(options.cwd, CURSOR_RULES_DIR), { recursive: true });
|
|
12184
|
+
await (0, import_promises10.writeFile)(path, rule.content, "utf-8");
|
|
11837
12185
|
}
|
|
11838
12186
|
results.push({ target: "cursor", file, path, action });
|
|
11839
12187
|
}
|
|
11840
|
-
const legacyPath = (0,
|
|
12188
|
+
const legacyPath = (0, import_node_path13.join)(options.cwd, LEGACY_CURSOR_RULE_FILE);
|
|
11841
12189
|
if (!options.dryRun && await fileExists(legacyPath)) {
|
|
11842
|
-
await (0,
|
|
12190
|
+
await (0, import_promises10.rm)(legacyPath, { force: true });
|
|
11843
12191
|
}
|
|
11844
12192
|
return results;
|
|
11845
12193
|
}
|
|
11846
12194
|
async function readExistingFile(path) {
|
|
11847
12195
|
try {
|
|
11848
|
-
return { exists: true, content: await (0,
|
|
12196
|
+
return { exists: true, content: await (0, import_promises10.readFile)(path, "utf-8") };
|
|
11849
12197
|
} catch (error) {
|
|
11850
12198
|
if (isFileNotFoundError(error)) {
|
|
11851
12199
|
return { exists: false, content: "" };
|
|
@@ -11855,7 +12203,7 @@ async function readExistingFile(path) {
|
|
|
11855
12203
|
}
|
|
11856
12204
|
async function fileExists(path) {
|
|
11857
12205
|
try {
|
|
11858
|
-
await (0,
|
|
12206
|
+
await (0, import_promises10.access)(path);
|
|
11859
12207
|
return true;
|
|
11860
12208
|
} catch {
|
|
11861
12209
|
return false;
|
|
@@ -11949,19 +12297,19 @@ function getAgentRulesTargets(value) {
|
|
|
11949
12297
|
|
|
11950
12298
|
// src/commands/seedPackageJsonScripts.ts
|
|
11951
12299
|
var import_node_fs7 = require("node:fs");
|
|
11952
|
-
var
|
|
11953
|
-
var
|
|
12300
|
+
var import_promises11 = require("node:fs/promises");
|
|
12301
|
+
var import_node_path14 = require("node:path");
|
|
11954
12302
|
var VIBERAVEN_PACKAGE_JSON_SCRIPTS = {
|
|
11955
12303
|
"viberaven:gate": PUBLIC_AGENT_MODE_COMMAND,
|
|
11956
12304
|
"viberaven:verify": PUBLIC_VERIFY_COMMAND,
|
|
11957
12305
|
"viberaven:strict": PUBLIC_STRICT_COMMAND
|
|
11958
12306
|
};
|
|
11959
12307
|
async function seedPackageJsonScripts(options) {
|
|
11960
|
-
const packageJsonPath = (0,
|
|
12308
|
+
const packageJsonPath = (0, import_node_path14.join)(options.cwd, "package.json");
|
|
11961
12309
|
if (!(0, import_node_fs7.existsSync)(packageJsonPath)) {
|
|
11962
12310
|
return null;
|
|
11963
12311
|
}
|
|
11964
|
-
const raw = await (0,
|
|
12312
|
+
const raw = await (0, import_promises11.readFile)(packageJsonPath, "utf-8");
|
|
11965
12313
|
let pkg;
|
|
11966
12314
|
try {
|
|
11967
12315
|
pkg = JSON.parse(raw);
|
|
@@ -11991,7 +12339,7 @@ async function seedPackageJsonScripts(options) {
|
|
|
11991
12339
|
pkg.scripts = scripts;
|
|
11992
12340
|
const output = `${JSON.stringify(pkg, null, 2)}
|
|
11993
12341
|
`;
|
|
11994
|
-
await (0,
|
|
12342
|
+
await (0, import_promises11.writeFile)(packageJsonPath, output, "utf-8");
|
|
11995
12343
|
}
|
|
11996
12344
|
return { action: "updated", added, skipped, changed: true };
|
|
11997
12345
|
}
|
|
@@ -12006,13 +12354,13 @@ async function initAgentRules(options) {
|
|
|
12006
12354
|
continue;
|
|
12007
12355
|
}
|
|
12008
12356
|
const file = AGENT_RULE_TARGETS[target].file;
|
|
12009
|
-
const path = (0,
|
|
12357
|
+
const path = (0, import_node_path15.join)(options.cwd, file);
|
|
12010
12358
|
const existing = await readExistingFile2(path);
|
|
12011
12359
|
const injected = injectAgentRulesBlock(existing.content, renderAgentRulesForTarget(target));
|
|
12012
12360
|
const action = !existing.exists ? "created" : injected.changed ? "updated" : "unchanged";
|
|
12013
12361
|
if (!options.dryRun && injected.changed) {
|
|
12014
|
-
await (0,
|
|
12015
|
-
await (0,
|
|
12362
|
+
await (0, import_promises12.mkdir)((0, import_node_path15.dirname)(path), { recursive: true });
|
|
12363
|
+
await (0, import_promises12.writeFile)(path, injected.content, "utf-8");
|
|
12016
12364
|
}
|
|
12017
12365
|
results.push({ target, file, path, action });
|
|
12018
12366
|
}
|
|
@@ -12080,7 +12428,7 @@ function formatAgentRulesInitSummary(output) {
|
|
|
12080
12428
|
}
|
|
12081
12429
|
async function readExistingFile2(path) {
|
|
12082
12430
|
try {
|
|
12083
|
-
return { exists: true, content: await (0,
|
|
12431
|
+
return { exists: true, content: await (0, import_promises12.readFile)(path, "utf-8") };
|
|
12084
12432
|
} catch (error) {
|
|
12085
12433
|
if (isFileNotFoundError2(error)) {
|
|
12086
12434
|
return { exists: false, content: "" };
|
|
@@ -12388,7 +12736,7 @@ async function runInteractiveSession(startDir = process.cwd()) {
|
|
|
12388
12736
|
Ie(`${import_picocolors4.default.bold("VibeRaven")} ${import_picocolors4.default.dim(VERSION)}`);
|
|
12389
12737
|
const cwd = await resolveWorkspaceRoot(startDir);
|
|
12390
12738
|
const artifactsAt = await findArtifactsWorkspace(startDir);
|
|
12391
|
-
if (artifactsAt && (0,
|
|
12739
|
+
if (artifactsAt && (0, import_node_path16.resolve)(artifactsAt) !== (0, import_node_path16.resolve)(startDir)) {
|
|
12392
12740
|
M2.message(import_picocolors4.default.dim(`Using scan from: ${artifactsAt}`));
|
|
12393
12741
|
} else {
|
|
12394
12742
|
M2.message(import_picocolors4.default.dim(`Project folder: ${cwd}`));
|
|
@@ -12455,29 +12803,29 @@ async function runInteractiveSession(startDir = process.cwd()) {
|
|
|
12455
12803
|
}
|
|
12456
12804
|
|
|
12457
12805
|
// src/commands/condense.ts
|
|
12458
|
-
var
|
|
12459
|
-
var
|
|
12806
|
+
var import_promises13 = require("node:fs/promises");
|
|
12807
|
+
var import_node_path17 = require("node:path");
|
|
12460
12808
|
async function runCondenseCommand(options) {
|
|
12461
12809
|
const dir = getProjectArtifactsDir(options.cwd);
|
|
12462
|
-
const artifact = JSON.parse(await (0,
|
|
12463
|
-
const contextMapPath = (0,
|
|
12464
|
-
await (0,
|
|
12810
|
+
const artifact = JSON.parse(await (0, import_promises13.readFile)((0, import_node_path17.join)(dir, "last-scan.json"), "utf8"));
|
|
12811
|
+
const contextMapPath = (0, import_node_path17.join)(dir, "context-map.json");
|
|
12812
|
+
await (0, import_promises13.writeFile)(contextMapPath, `${JSON.stringify(generateContextMap(artifact), null, 2)}
|
|
12465
12813
|
`, "utf8");
|
|
12466
12814
|
return { contextMapPath };
|
|
12467
12815
|
}
|
|
12468
12816
|
|
|
12469
12817
|
// src/heal/apply.ts
|
|
12470
|
-
var
|
|
12818
|
+
var import_promises15 = require("node:fs/promises");
|
|
12471
12819
|
var import_node_fs10 = require("node:fs");
|
|
12472
|
-
var
|
|
12820
|
+
var import_node_path21 = require("node:path");
|
|
12473
12821
|
|
|
12474
12822
|
// src/heal/pathSafety.ts
|
|
12475
|
-
var
|
|
12823
|
+
var import_node_path18 = require("node:path");
|
|
12476
12824
|
var BLOCKED_SEGMENTS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", ".viberaven"]);
|
|
12477
12825
|
function assertSafeHealTarget(cwd, target) {
|
|
12478
|
-
const root = (0,
|
|
12479
|
-
const absolute = (0,
|
|
12480
|
-
const rel = (0,
|
|
12826
|
+
const root = (0, import_node_path18.resolve)(cwd);
|
|
12827
|
+
const absolute = (0, import_node_path18.resolve)(root, target);
|
|
12828
|
+
const rel = (0, import_node_path18.relative)(root, absolute);
|
|
12481
12829
|
if (rel.startsWith("..") || rel === "" || /^[A-Za-z]:/.test(rel)) {
|
|
12482
12830
|
throw new Error("Heal target must stay inside the workspace");
|
|
12483
12831
|
}
|
|
@@ -12501,8 +12849,8 @@ function applyEmptyCatchRecipe(source) {
|
|
|
12501
12849
|
|
|
12502
12850
|
// src/heal/recipes/index.ts
|
|
12503
12851
|
var import_node_fs9 = require("node:fs");
|
|
12504
|
-
var
|
|
12505
|
-
var
|
|
12852
|
+
var import_promises14 = require("node:fs/promises");
|
|
12853
|
+
var import_node_path20 = require("node:path");
|
|
12506
12854
|
|
|
12507
12855
|
// src/heal/recipes/envAuthSecret.ts
|
|
12508
12856
|
function applyAuthSecretRecipe(source) {
|
|
@@ -12884,7 +13232,7 @@ function applyRateLimitRecipe(source, hasUpstash) {
|
|
|
12884
13232
|
|
|
12885
13233
|
// src/heal/recipes/eslintRestrictedImports.ts
|
|
12886
13234
|
var import_node_fs8 = require("node:fs");
|
|
12887
|
-
var
|
|
13235
|
+
var import_node_path19 = require("node:path");
|
|
12888
13236
|
var VIBERAVEN_ESLINT_MARKER = "VibeRaven heal: eslint_restricted_imports";
|
|
12889
13237
|
var RESTRICTED_IMPORTS_MESSAGE = `Restricted import. Run ${PUBLIC_AGENT_MODE_COMMAND} before substituting packages.`;
|
|
12890
13238
|
var RESTRICTED_PATHS = [
|
|
@@ -12914,7 +13262,7 @@ var ESLINT_CONFIG_CANDIDATES = [
|
|
|
12914
13262
|
];
|
|
12915
13263
|
function detectEslintConfigFile(cwd) {
|
|
12916
13264
|
for (const candidate of ESLINT_CONFIG_CANDIDATES) {
|
|
12917
|
-
if ((0, import_node_fs8.existsSync)((0,
|
|
13265
|
+
if ((0, import_node_fs8.existsSync)((0, import_node_path19.join)(cwd, candidate))) {
|
|
12918
13266
|
return candidate;
|
|
12919
13267
|
}
|
|
12920
13268
|
}
|
|
@@ -13090,13 +13438,13 @@ function defaultTargetFile(gapId) {
|
|
|
13090
13438
|
}
|
|
13091
13439
|
async function readSourceOrEmpty(absolutePath) {
|
|
13092
13440
|
if (!(0, import_node_fs9.existsSync)(absolutePath)) return "";
|
|
13093
|
-
return (0,
|
|
13441
|
+
return (0, import_promises14.readFile)(absolutePath, "utf8");
|
|
13094
13442
|
}
|
|
13095
13443
|
async function detectUpstash(cwd) {
|
|
13096
13444
|
try {
|
|
13097
|
-
const pkgPath = (0,
|
|
13445
|
+
const pkgPath = (0, import_node_path20.join)(cwd, "package.json");
|
|
13098
13446
|
if (!(0, import_node_fs9.existsSync)(pkgPath)) return false;
|
|
13099
|
-
const raw = await (0,
|
|
13447
|
+
const raw = await (0, import_promises14.readFile)(pkgPath, "utf8");
|
|
13100
13448
|
const pkg = JSON.parse(raw);
|
|
13101
13449
|
const deps = {
|
|
13102
13450
|
...pkg["dependencies"] ?? {},
|
|
@@ -13111,7 +13459,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
13111
13459
|
const targetFile = explicitTarget ?? defaultTargetFile(gapId);
|
|
13112
13460
|
if (targetFile === void 0) return null;
|
|
13113
13461
|
if (gapId === "auth_secret_missing" || gapId === "node_env_not_set" || gapId === "database_url_missing") {
|
|
13114
|
-
const absolutePath = (0,
|
|
13462
|
+
const absolutePath = (0, import_node_path20.join)(cwd, targetFile);
|
|
13115
13463
|
const source = await readSourceOrEmpty(absolutePath);
|
|
13116
13464
|
let result;
|
|
13117
13465
|
if (gapId === "auth_secret_missing") result = applyAuthSecretRecipe(source);
|
|
@@ -13125,25 +13473,25 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
13125
13473
|
};
|
|
13126
13474
|
}
|
|
13127
13475
|
if (gapId === "missing_error_boundary") {
|
|
13128
|
-
const absolutePath = (0,
|
|
13476
|
+
const absolutePath = (0, import_node_path20.join)(cwd, "app/error.tsx");
|
|
13129
13477
|
const source = await readSourceOrEmpty(absolutePath);
|
|
13130
13478
|
const result = applyErrorBoundaryRecipe(source);
|
|
13131
13479
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
13132
13480
|
}
|
|
13133
13481
|
if (gapId === "missing_health_route") {
|
|
13134
|
-
const absolutePath = (0,
|
|
13482
|
+
const absolutePath = (0, import_node_path20.join)(cwd, "app/api/health/route.ts");
|
|
13135
13483
|
const source = await readSourceOrEmpty(absolutePath);
|
|
13136
13484
|
const result = applyHealthRouteRecipe(source);
|
|
13137
13485
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
13138
13486
|
}
|
|
13139
13487
|
if (gapId === "missing_loading_state") {
|
|
13140
|
-
const absolutePath = (0,
|
|
13488
|
+
const absolutePath = (0, import_node_path20.join)(cwd, "app/loading.tsx");
|
|
13141
13489
|
const source = await readSourceOrEmpty(absolutePath);
|
|
13142
13490
|
const result = applyLoadingStateRecipe(source);
|
|
13143
13491
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
13144
13492
|
}
|
|
13145
13493
|
if (gapId === "missing_404_page") {
|
|
13146
|
-
const absolutePath = (0,
|
|
13494
|
+
const absolutePath = (0, import_node_path20.join)(cwd, "app/not-found.tsx");
|
|
13147
13495
|
const source = await readSourceOrEmpty(absolutePath);
|
|
13148
13496
|
const result = applyNotFoundRecipe(source);
|
|
13149
13497
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
@@ -13161,10 +13509,10 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
13161
13509
|
}
|
|
13162
13510
|
if (gapId === "missing_csp_header") {
|
|
13163
13511
|
let configFile = "next.config.js";
|
|
13164
|
-
let absolutePath = (0,
|
|
13512
|
+
let absolutePath = (0, import_node_path20.join)(cwd, configFile);
|
|
13165
13513
|
if (!(0, import_node_fs9.existsSync)(absolutePath)) {
|
|
13166
13514
|
configFile = "next.config.mjs";
|
|
13167
|
-
absolutePath = (0,
|
|
13515
|
+
absolutePath = (0, import_node_path20.join)(cwd, configFile);
|
|
13168
13516
|
}
|
|
13169
13517
|
const source = await readSourceOrEmpty(absolutePath);
|
|
13170
13518
|
const result = applyCspHeaderRecipe(source);
|
|
@@ -13176,7 +13524,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
13176
13524
|
}
|
|
13177
13525
|
if (gapId === "missing_rate_limit") {
|
|
13178
13526
|
const hasUpstash = await detectUpstash(cwd);
|
|
13179
|
-
const middlewarePath = (0,
|
|
13527
|
+
const middlewarePath = (0, import_node_path20.join)(cwd, "middleware.ts");
|
|
13180
13528
|
const source = await readSourceOrEmpty(middlewarePath);
|
|
13181
13529
|
const result = applyRateLimitRecipe(source, hasUpstash);
|
|
13182
13530
|
return {
|
|
@@ -13198,7 +13546,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
13198
13546
|
recipeName: gapId
|
|
13199
13547
|
};
|
|
13200
13548
|
}
|
|
13201
|
-
const absolutePath = (0,
|
|
13549
|
+
const absolutePath = (0, import_node_path20.join)(cwd, configFile);
|
|
13202
13550
|
const source = await readSourceOrEmpty(absolutePath);
|
|
13203
13551
|
const result = applyEslintRestrictedImportsRecipe(source, configFile);
|
|
13204
13552
|
return {
|
|
@@ -13273,14 +13621,14 @@ async function applyHeal(options) {
|
|
|
13273
13621
|
rollback: { available: false, instructions: "Recipe matched but no change was needed (already applied or file already exists)." }
|
|
13274
13622
|
};
|
|
13275
13623
|
}
|
|
13276
|
-
const absoluteTarget = (0,
|
|
13624
|
+
const absoluteTarget = (0, import_node_path21.join)(options.cwd, dispatched.targetFile);
|
|
13277
13625
|
assertSafeHealTarget(options.cwd, dispatched.targetFile);
|
|
13278
|
-
await (0,
|
|
13279
|
-
const healDir2 = (0,
|
|
13280
|
-
await (0,
|
|
13281
|
-
const beforeContent = (0, import_node_fs10.existsSync)(absoluteTarget) ? await (0,
|
|
13282
|
-
await (0,
|
|
13283
|
-
await (0,
|
|
13626
|
+
await (0, import_promises15.mkdir)((0, import_node_path21.dirname)(absoluteTarget), { recursive: true });
|
|
13627
|
+
const healDir2 = (0, import_node_path21.join)(options.cwd, ".viberaven", "heal", id);
|
|
13628
|
+
await (0, import_promises15.mkdir)((0, import_node_path21.join)(healDir2, "before"), { recursive: true });
|
|
13629
|
+
const beforeContent = (0, import_node_fs10.existsSync)(absoluteTarget) ? await (0, import_promises15.readFile)(absoluteTarget, "utf8") : "";
|
|
13630
|
+
await (0, import_promises15.writeFile)((0, import_node_path21.join)(healDir2, "before", "target.txt"), beforeContent, "utf8");
|
|
13631
|
+
await (0, import_promises15.writeFile)(absoluteTarget, dispatched.output, "utf8");
|
|
13284
13632
|
const patch2 = [
|
|
13285
13633
|
`--- ${dispatched.targetFile}`,
|
|
13286
13634
|
`+++ ${dispatched.targetFile}`,
|
|
@@ -13290,7 +13638,7 @@ async function applyHeal(options) {
|
|
|
13290
13638
|
dispatched.output,
|
|
13291
13639
|
""
|
|
13292
13640
|
].join("\n");
|
|
13293
|
-
await (0,
|
|
13641
|
+
await (0, import_promises15.writeFile)((0, import_node_path21.join)(healDir2, "patch.diff"), patch2, "utf8");
|
|
13294
13642
|
const result2 = {
|
|
13295
13643
|
$schema: "https://viberaven.dev/schemas/heal-result.schema.json",
|
|
13296
13644
|
schemaVersion: "v1",
|
|
@@ -13301,7 +13649,7 @@ async function applyHeal(options) {
|
|
|
13301
13649
|
gapId: options.gapId,
|
|
13302
13650
|
recipe: dispatched.recipeName,
|
|
13303
13651
|
target: dispatched.targetFile,
|
|
13304
|
-
changedFiles: [(0,
|
|
13652
|
+
changedFiles: [(0, import_node_path21.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
|
|
13305
13653
|
artifacts: {
|
|
13306
13654
|
patch: `.viberaven/heal/${id}/patch.diff`,
|
|
13307
13655
|
result: `.viberaven/heal/${id}/result.json`
|
|
@@ -13311,7 +13659,7 @@ async function applyHeal(options) {
|
|
|
13311
13659
|
instructions: "Restore .viberaven/heal/<healId>/before/target.txt to the target file or apply the reverse patch."
|
|
13312
13660
|
}
|
|
13313
13661
|
};
|
|
13314
|
-
await (0,
|
|
13662
|
+
await (0, import_promises15.writeFile)((0, import_node_path21.join)(healDir2, "result.json"), `${JSON.stringify(result2, null, 2)}
|
|
13315
13663
|
`, "utf8");
|
|
13316
13664
|
return result2;
|
|
13317
13665
|
}
|
|
@@ -13331,7 +13679,7 @@ async function applyHeal(options) {
|
|
|
13331
13679
|
};
|
|
13332
13680
|
}
|
|
13333
13681
|
const absolute = assertSafeHealTarget(options.cwd, options.target);
|
|
13334
|
-
const before = await (0,
|
|
13682
|
+
const before = await (0, import_promises15.readFile)(absolute, "utf8");
|
|
13335
13683
|
const recipe = applyEmptyCatchRecipe(before);
|
|
13336
13684
|
if (!recipe.changed) {
|
|
13337
13685
|
return {
|
|
@@ -13348,10 +13696,10 @@ async function applyHeal(options) {
|
|
|
13348
13696
|
rollback: { available: false, instructions: "No supported heal recipe matched this file." }
|
|
13349
13697
|
};
|
|
13350
13698
|
}
|
|
13351
|
-
const healDir = (0,
|
|
13352
|
-
await (0,
|
|
13353
|
-
await (0,
|
|
13354
|
-
await (0,
|
|
13699
|
+
const healDir = (0, import_node_path21.join)(options.cwd, ".viberaven", "heal", id);
|
|
13700
|
+
await (0, import_promises15.mkdir)((0, import_node_path21.join)(healDir, "before"), { recursive: true });
|
|
13701
|
+
await (0, import_promises15.writeFile)((0, import_node_path21.join)(healDir, "before", "target.txt"), before, "utf8");
|
|
13702
|
+
await (0, import_promises15.writeFile)(absolute, recipe.output, "utf8");
|
|
13355
13703
|
const patch = [
|
|
13356
13704
|
`--- ${options.target}`,
|
|
13357
13705
|
`+++ ${options.target}`,
|
|
@@ -13361,7 +13709,7 @@ async function applyHeal(options) {
|
|
|
13361
13709
|
recipe.output,
|
|
13362
13710
|
""
|
|
13363
13711
|
].join("\n");
|
|
13364
|
-
await (0,
|
|
13712
|
+
await (0, import_promises15.writeFile)((0, import_node_path21.join)(healDir, "patch.diff"), patch, "utf8");
|
|
13365
13713
|
const result = {
|
|
13366
13714
|
$schema: "https://viberaven.dev/schemas/heal-result.schema.json",
|
|
13367
13715
|
schemaVersion: "v1",
|
|
@@ -13372,7 +13720,7 @@ async function applyHeal(options) {
|
|
|
13372
13720
|
gapId: options.gapId,
|
|
13373
13721
|
recipe: "empty-catch-safe-response",
|
|
13374
13722
|
target: options.target,
|
|
13375
|
-
changedFiles: [(0,
|
|
13723
|
+
changedFiles: [(0, import_node_path21.relative)(options.cwd, absolute).replace(/\\/g, "/")],
|
|
13376
13724
|
artifacts: {
|
|
13377
13725
|
patch: `.viberaven/heal/${id}/patch.diff`,
|
|
13378
13726
|
result: `.viberaven/heal/${id}/result.json`
|
|
@@ -13382,20 +13730,20 @@ async function applyHeal(options) {
|
|
|
13382
13730
|
instructions: "Restore .viberaven/heal/<healId>/before/target.txt to the target file or apply the reverse patch."
|
|
13383
13731
|
}
|
|
13384
13732
|
};
|
|
13385
|
-
await (0,
|
|
13733
|
+
await (0, import_promises15.writeFile)((0, import_node_path21.join)(healDir, "result.json"), `${JSON.stringify(result, null, 2)}
|
|
13386
13734
|
`, "utf8");
|
|
13387
13735
|
return result;
|
|
13388
13736
|
}
|
|
13389
13737
|
|
|
13390
13738
|
// src/heal/plan.ts
|
|
13391
|
-
var
|
|
13392
|
-
var
|
|
13739
|
+
var import_promises16 = require("node:fs/promises");
|
|
13740
|
+
var import_node_path22 = require("node:path");
|
|
13393
13741
|
function healId2() {
|
|
13394
13742
|
return `heal_${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14)}`;
|
|
13395
13743
|
}
|
|
13396
13744
|
async function writeHealPlan(options) {
|
|
13397
|
-
const dir = (0,
|
|
13398
|
-
await (0,
|
|
13745
|
+
const dir = (0, import_node_path22.join)(options.cwd, ".viberaven");
|
|
13746
|
+
await (0, import_promises16.mkdir)(dir, { recursive: true });
|
|
13399
13747
|
const id = healId2();
|
|
13400
13748
|
const target = options.target ?? `gap:${options.gapId}`;
|
|
13401
13749
|
const markdown = [
|
|
@@ -13422,21 +13770,21 @@ async function writeHealPlan(options) {
|
|
|
13422
13770
|
artifacts: { plan: ".viberaven/heal-plan.md" },
|
|
13423
13771
|
rollback: { available: false, instructions: "No source files were changed." }
|
|
13424
13772
|
};
|
|
13425
|
-
await (0,
|
|
13426
|
-
await (0,
|
|
13773
|
+
await (0, import_promises16.writeFile)((0, import_node_path22.join)(dir, "heal-plan.md"), markdown, "utf8");
|
|
13774
|
+
await (0, import_promises16.writeFile)((0, import_node_path22.join)(dir, "heal-plan.json"), `${JSON.stringify(result, null, 2)}
|
|
13427
13775
|
`, "utf8");
|
|
13428
13776
|
return result;
|
|
13429
13777
|
}
|
|
13430
13778
|
|
|
13431
13779
|
// src/heal/prompt.ts
|
|
13432
|
-
var
|
|
13433
|
-
var
|
|
13780
|
+
var import_promises17 = require("node:fs/promises");
|
|
13781
|
+
var import_node_path23 = require("node:path");
|
|
13434
13782
|
function healId3() {
|
|
13435
13783
|
return `heal_${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14)}`;
|
|
13436
13784
|
}
|
|
13437
13785
|
async function writeHealPrompt(options) {
|
|
13438
|
-
const dir = (0,
|
|
13439
|
-
await (0,
|
|
13786
|
+
const dir = (0, import_node_path23.join)(options.cwd, ".viberaven");
|
|
13787
|
+
await (0, import_promises17.mkdir)(dir, { recursive: true });
|
|
13440
13788
|
const id = healId3();
|
|
13441
13789
|
const target = options.target ?? `gap:${options.gapId}`;
|
|
13442
13790
|
const prompt = [
|
|
@@ -13463,12 +13811,12 @@ async function writeHealPrompt(options) {
|
|
|
13463
13811
|
artifacts: { prompt: ".viberaven/heal-prompt.md" },
|
|
13464
13812
|
rollback: { available: false, instructions: "No source files were changed." }
|
|
13465
13813
|
};
|
|
13466
|
-
await (0,
|
|
13814
|
+
await (0, import_promises17.writeFile)((0, import_node_path23.join)(dir, "heal-prompt.md"), prompt, "utf8");
|
|
13467
13815
|
return result;
|
|
13468
13816
|
}
|
|
13469
13817
|
|
|
13470
13818
|
// src/loopState.ts
|
|
13471
|
-
var
|
|
13819
|
+
var import_promises18 = require("fs/promises");
|
|
13472
13820
|
var import_path = require("path");
|
|
13473
13821
|
var DEFAULT_LOOP_STATE = {
|
|
13474
13822
|
batchApplied: 0,
|
|
@@ -13481,7 +13829,7 @@ function loopStatePath(workspaceRoot) {
|
|
|
13481
13829
|
}
|
|
13482
13830
|
async function loadLoopState(workspaceRoot) {
|
|
13483
13831
|
try {
|
|
13484
|
-
const raw = await (0,
|
|
13832
|
+
const raw = await (0, import_promises18.readFile)(loopStatePath(workspaceRoot), "utf8");
|
|
13485
13833
|
const parsed = JSON.parse(raw);
|
|
13486
13834
|
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && typeof parsed.batchApplied === "number" && typeof parsed.lastGapCount === "number" && typeof parsed.stalledScans === "number") {
|
|
13487
13835
|
const p2 = parsed;
|
|
@@ -13501,8 +13849,8 @@ async function loadLoopState(workspaceRoot) {
|
|
|
13501
13849
|
async function saveLoopState(workspaceRoot, state) {
|
|
13502
13850
|
try {
|
|
13503
13851
|
const dir = (0, import_path.join)(workspaceRoot, ".viberaven");
|
|
13504
|
-
await (0,
|
|
13505
|
-
await (0,
|
|
13852
|
+
await (0, import_promises18.mkdir)(dir, { recursive: true });
|
|
13853
|
+
await (0, import_promises18.writeFile)(loopStatePath(workspaceRoot), JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
13506
13854
|
} catch (err) {
|
|
13507
13855
|
console.warn("[VibeRaven] Could not save loop-state.json:", err instanceof Error ? err.message : String(err));
|
|
13508
13856
|
}
|
|
@@ -13548,9 +13896,9 @@ async function runHealCommand(options) {
|
|
|
13548
13896
|
}
|
|
13549
13897
|
|
|
13550
13898
|
// src/stackRecommend.ts
|
|
13551
|
-
var
|
|
13899
|
+
var import_promises19 = require("node:fs/promises");
|
|
13552
13900
|
var import_node_fs11 = require("node:fs");
|
|
13553
|
-
var
|
|
13901
|
+
var import_node_path24 = require("node:path");
|
|
13554
13902
|
var DEFAULT_STACK = {
|
|
13555
13903
|
frontend: "react",
|
|
13556
13904
|
ui: "tailwind + shadcn/ui",
|
|
@@ -13561,11 +13909,11 @@ var DEFAULT_STACK = {
|
|
|
13561
13909
|
reason: "Agent-default stack for lowest launch friction when repo signals are ambiguous"
|
|
13562
13910
|
};
|
|
13563
13911
|
async function recommendStack(cwd = process.cwd()) {
|
|
13564
|
-
const pkgPath = (0,
|
|
13912
|
+
const pkgPath = (0, import_node_path24.join)(cwd, "package.json");
|
|
13565
13913
|
if (!(0, import_node_fs11.existsSync)(pkgPath)) {
|
|
13566
13914
|
return DEFAULT_STACK;
|
|
13567
13915
|
}
|
|
13568
|
-
const pkg = JSON.parse(await (0,
|
|
13916
|
+
const pkg = JSON.parse(await (0, import_promises19.readFile)(pkgPath, "utf-8"));
|
|
13569
13917
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
13570
13918
|
const names = Object.keys(deps).join(" ").toLowerCase();
|
|
13571
13919
|
const rec = {
|
|
@@ -13617,8 +13965,8 @@ async function runInitCommand(options) {
|
|
|
13617
13965
|
}
|
|
13618
13966
|
|
|
13619
13967
|
// src/commands/doctorAgents.ts
|
|
13620
|
-
var
|
|
13621
|
-
var
|
|
13968
|
+
var import_promises20 = require("node:fs/promises");
|
|
13969
|
+
var import_node_path25 = require("node:path");
|
|
13622
13970
|
var REQUIRED_EXISTENCE_CHECKS = [
|
|
13623
13971
|
{ id: "agents-md", file: "AGENTS.md" },
|
|
13624
13972
|
{ id: "claude-md", file: "CLAUDE.md" },
|
|
@@ -13638,7 +13986,7 @@ var STALE_PATTERNS = [
|
|
|
13638
13986
|
async function checkAgentInjection(cwd) {
|
|
13639
13987
|
const checks = [];
|
|
13640
13988
|
for (const item3 of REQUIRED_EXISTENCE_CHECKS) {
|
|
13641
|
-
const exists = await fileExists2((0,
|
|
13989
|
+
const exists = await fileExists2((0, import_node_path25.join)(cwd, item3.file));
|
|
13642
13990
|
checks.push({
|
|
13643
13991
|
id: item3.id,
|
|
13644
13992
|
status: exists ? "pass" : "fail",
|
|
@@ -13646,17 +13994,17 @@ async function checkAgentInjection(cwd) {
|
|
|
13646
13994
|
});
|
|
13647
13995
|
}
|
|
13648
13996
|
for (const item3 of OPTIONAL_CURSOR_PACK_CHECKS) {
|
|
13649
|
-
const exists = await fileExists2((0,
|
|
13997
|
+
const exists = await fileExists2((0, import_node_path25.join)(cwd, item3.file));
|
|
13650
13998
|
checks.push({
|
|
13651
13999
|
id: item3.id,
|
|
13652
14000
|
status: exists ? "pass" : "fail",
|
|
13653
14001
|
message: exists ? `${item3.file} exists` : `Missing ${item3.file} \u2014 run npx -y viberaven init --agents all`
|
|
13654
14002
|
});
|
|
13655
14003
|
}
|
|
13656
|
-
const legacyCursorPath = (0,
|
|
14004
|
+
const legacyCursorPath = (0, import_node_path25.join)(cwd, ".cursor/rules/viberaven.mdc");
|
|
13657
14005
|
if (await fileExists2(legacyCursorPath)) {
|
|
13658
|
-
const legacyContent = await (0,
|
|
13659
|
-
const hasCoreSplit = await fileExists2((0,
|
|
14006
|
+
const legacyContent = await (0, import_promises20.readFile)(legacyCursorPath, "utf-8");
|
|
14007
|
+
const hasCoreSplit = await fileExists2((0, import_node_path25.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
|
|
13660
14008
|
if (!hasCoreSplit || legacyContent.includes("alwaysApply: true")) {
|
|
13661
14009
|
checks.push({
|
|
13662
14010
|
id: "cursor-legacy-mdc",
|
|
@@ -13667,7 +14015,7 @@ async function checkAgentInjection(cwd) {
|
|
|
13667
14015
|
}
|
|
13668
14016
|
for (const target of CORE_AGENT_INJECTION_TARGETS) {
|
|
13669
14017
|
const file = target === "cursor" ? ".cursor/rules/viberaven-core.mdc" : AGENT_RULE_TARGETS[target].file;
|
|
13670
|
-
const path = (0,
|
|
14018
|
+
const path = (0, import_node_path25.join)(cwd, file);
|
|
13671
14019
|
const exists = await fileExists2(path);
|
|
13672
14020
|
if (!exists) {
|
|
13673
14021
|
checks.push({
|
|
@@ -13677,7 +14025,7 @@ async function checkAgentInjection(cwd) {
|
|
|
13677
14025
|
});
|
|
13678
14026
|
continue;
|
|
13679
14027
|
}
|
|
13680
|
-
const content = await (0,
|
|
14028
|
+
const content = await (0, import_promises20.readFile)(path, "utf-8");
|
|
13681
14029
|
const hasCommand = content.includes(PUBLIC_AGENT_MODE_COMMAND);
|
|
13682
14030
|
checks.push({
|
|
13683
14031
|
id: `canonical-${target}`,
|
|
@@ -13695,15 +14043,15 @@ async function checkAgentInjection(cwd) {
|
|
|
13695
14043
|
}
|
|
13696
14044
|
}
|
|
13697
14045
|
return {
|
|
13698
|
-
ok: checks.every((
|
|
14046
|
+
ok: checks.every((check) => check.status === "pass"),
|
|
13699
14047
|
checks
|
|
13700
14048
|
};
|
|
13701
14049
|
}
|
|
13702
14050
|
function formatDoctorAgentsReport(report) {
|
|
13703
14051
|
const lines = ["VibeRaven agent injection doctor", ""];
|
|
13704
|
-
for (const
|
|
13705
|
-
const icon =
|
|
13706
|
-
lines.push(`${icon} ${
|
|
14052
|
+
for (const check of report.checks) {
|
|
14053
|
+
const icon = check.status === "pass" ? "\u2713" : "\u2717";
|
|
14054
|
+
lines.push(`${icon} ${check.message}`);
|
|
13707
14055
|
}
|
|
13708
14056
|
lines.push("");
|
|
13709
14057
|
lines.push(report.ok ? "All agent injection checks passed." : "Agent injection checks failed.");
|
|
@@ -13711,7 +14059,7 @@ function formatDoctorAgentsReport(report) {
|
|
|
13711
14059
|
}
|
|
13712
14060
|
async function fileExists2(path) {
|
|
13713
14061
|
try {
|
|
13714
|
-
await (0,
|
|
14062
|
+
await (0, import_promises20.access)(path);
|
|
13715
14063
|
return true;
|
|
13716
14064
|
} catch {
|
|
13717
14065
|
return false;
|
|
@@ -13924,8 +14272,778 @@ async function runAuditCommand(input) {
|
|
|
13924
14272
|
return result.status === "pass" ? 0 : 1;
|
|
13925
14273
|
}
|
|
13926
14274
|
|
|
14275
|
+
// src/commands/actions.ts
|
|
14276
|
+
var import_promises21 = require("node:fs/promises");
|
|
14277
|
+
var import_node_path26 = require("node:path");
|
|
14278
|
+
|
|
14279
|
+
// src/actions/render.ts
|
|
14280
|
+
function redact(text) {
|
|
14281
|
+
return text.replace(
|
|
14282
|
+
/\b([A-Z0-9_]*(?:SECRET|TOKEN|KEY|PASSWORD|DATABASE_URL|POSTGRES_URL|SERVICE_ROLE)[A-Z0-9_]*)=([^,\s]+)/gi,
|
|
14283
|
+
"$1=<redacted>"
|
|
14284
|
+
).replace(/\bpostgres(?:ql)?:\/\/[^@\s]+@/gi, "postgresql://<redacted>@").replace(/[A-Za-z]:\\[^\s`"]+/g, "<repo-relative-path>").replace(/\b\/Users\/[^\s`"]+/g, "<repo-relative-path>").replace(/\b\/home\/[^\s`"]+/g, "<repo-relative-path>").replace(/\beyJ[A-Za-z0-9._-]*\b/g, "<redacted>");
|
|
14285
|
+
}
|
|
14286
|
+
function stringifyPayload(value) {
|
|
14287
|
+
if (Array.isArray(value)) {
|
|
14288
|
+
return value.map((entry) => String(entry)).join("\n");
|
|
14289
|
+
}
|
|
14290
|
+
if (value && typeof value === "object") {
|
|
14291
|
+
return JSON.stringify(value, null, 2);
|
|
14292
|
+
}
|
|
14293
|
+
return String(value ?? "");
|
|
14294
|
+
}
|
|
14295
|
+
function fenceFor(format) {
|
|
14296
|
+
if (format === "bash" || format === "sql" || format === "json") {
|
|
14297
|
+
return format;
|
|
14298
|
+
}
|
|
14299
|
+
return "txt";
|
|
14300
|
+
}
|
|
14301
|
+
function renderTarget(target) {
|
|
14302
|
+
if (!target) {
|
|
14303
|
+
return [];
|
|
14304
|
+
}
|
|
14305
|
+
if (target.type === "url") {
|
|
14306
|
+
return [`Open: ${redact(target.href || target.label)}`];
|
|
14307
|
+
}
|
|
14308
|
+
if (target.type === "file") {
|
|
14309
|
+
return [`File: ${redact(target.path)}`];
|
|
14310
|
+
}
|
|
14311
|
+
if (target.type === "command") {
|
|
14312
|
+
return ["Run:", "```bash", redact(target.command), "```"];
|
|
14313
|
+
}
|
|
14314
|
+
if (target.type === "provider") {
|
|
14315
|
+
return [`Provider: ${redact(target.label)}`];
|
|
14316
|
+
}
|
|
14317
|
+
return ["Verify:", "```bash", redact(target.command), "```"];
|
|
14318
|
+
}
|
|
14319
|
+
function renderReadiness(action) {
|
|
14320
|
+
const readiness = action.readiness?.slice(0, 2).map(redact).filter(Boolean);
|
|
14321
|
+
if (!readiness || readiness.length === 0) {
|
|
14322
|
+
return void 0;
|
|
14323
|
+
}
|
|
14324
|
+
return `Ready: ${readiness.join(", ")}`;
|
|
14325
|
+
}
|
|
14326
|
+
function renderCopyPayload(action) {
|
|
14327
|
+
const payload = action.copyPayloads?.[0];
|
|
14328
|
+
if (!payload) {
|
|
14329
|
+
return [];
|
|
14330
|
+
}
|
|
14331
|
+
const raw = stringifyPayload(payload.value);
|
|
14332
|
+
if (raw.length > 500) {
|
|
14333
|
+
return [];
|
|
14334
|
+
}
|
|
14335
|
+
return [`Copy: ${redact(payload.label)}`, `\`\`\`${fenceFor(payload.format)}`, redact(raw), "```"];
|
|
14336
|
+
}
|
|
14337
|
+
function renderVerifyCommand(action) {
|
|
14338
|
+
if (!action.verifyCommand) {
|
|
14339
|
+
return [];
|
|
14340
|
+
}
|
|
14341
|
+
if (action.target?.type === "verify" && action.target.command === action.verifyCommand) {
|
|
14342
|
+
return [];
|
|
14343
|
+
}
|
|
14344
|
+
return ["Verify:", "```bash", redact(action.verifyCommand), "```"];
|
|
14345
|
+
}
|
|
14346
|
+
function renderAction(action) {
|
|
14347
|
+
return [
|
|
14348
|
+
`[${action.id}] ${redact(action.title)}`,
|
|
14349
|
+
`Status: ${action.status}`,
|
|
14350
|
+
renderReadiness(action),
|
|
14351
|
+
...renderTarget(action.target),
|
|
14352
|
+
...renderCopyPayload(action),
|
|
14353
|
+
...renderVerifyCommand(action),
|
|
14354
|
+
action.resumeInstruction ? `Resume: "${redact(action.resumeInstruction)}"` : void 0
|
|
14355
|
+
].filter((line) => Boolean(line));
|
|
14356
|
+
}
|
|
14357
|
+
function renderActionSurface(manifest, options = {}) {
|
|
14358
|
+
const visibleActions = typeof options.limit === "number" ? manifest.actions.slice(0, options.limit) : manifest.actions;
|
|
14359
|
+
const lines = [
|
|
14360
|
+
"VibeRaven Production Actions",
|
|
14361
|
+
`Gate: ${manifest.gateStatus}`,
|
|
14362
|
+
`Showing: ${visibleActions.length} of ${manifest.actions.length} current actions`,
|
|
14363
|
+
"Full state: .viberaven/actions.json",
|
|
14364
|
+
""
|
|
14365
|
+
];
|
|
14366
|
+
for (const action of visibleActions) {
|
|
14367
|
+
lines.push(...renderAction(action), "");
|
|
14368
|
+
}
|
|
14369
|
+
return `${lines.join("\n").trimEnd()}
|
|
14370
|
+
`;
|
|
14371
|
+
}
|
|
14372
|
+
|
|
14373
|
+
// src/commands/actions.ts
|
|
14374
|
+
async function runActionsCommand(input) {
|
|
14375
|
+
const actionsPath = (0, import_node_path26.join)(getProjectArtifactsDir(input.cwd), "actions.json");
|
|
14376
|
+
let raw;
|
|
14377
|
+
try {
|
|
14378
|
+
raw = await (0, import_promises21.readFile)(actionsPath, "utf8");
|
|
14379
|
+
} catch {
|
|
14380
|
+
process.stderr.write("No VibeRaven actions found. Run `npx -y viberaven --agent-mode` first.\n");
|
|
14381
|
+
return 1;
|
|
14382
|
+
}
|
|
14383
|
+
if (input.json) {
|
|
14384
|
+
process.stdout.write(raw.endsWith("\n") ? raw : `${raw}
|
|
14385
|
+
`);
|
|
14386
|
+
return 0;
|
|
14387
|
+
}
|
|
14388
|
+
const manifest = JSON.parse(raw);
|
|
14389
|
+
process.stdout.write(renderActionSurface(manifest));
|
|
14390
|
+
return 0;
|
|
14391
|
+
}
|
|
14392
|
+
|
|
14393
|
+
// src/commands/verifyAction.ts
|
|
14394
|
+
var import_promises22 = require("node:fs/promises");
|
|
14395
|
+
var import_node_path27 = require("node:path");
|
|
14396
|
+
async function currentActionExists(cwd, actionId) {
|
|
14397
|
+
try {
|
|
14398
|
+
const raw = await (0, import_promises22.readFile)((0, import_node_path27.join)(getProjectArtifactsDir(cwd), "actions.json"), "utf8");
|
|
14399
|
+
const manifest = JSON.parse(raw);
|
|
14400
|
+
return manifest.actions.some((action) => action.id === actionId);
|
|
14401
|
+
} catch {
|
|
14402
|
+
return false;
|
|
14403
|
+
}
|
|
14404
|
+
}
|
|
14405
|
+
async function runVerifyActionCommand(input) {
|
|
14406
|
+
const entry = await resolveActionById(input.cwd, input.actionId);
|
|
14407
|
+
if (!entry) {
|
|
14408
|
+
process.stdout.write(
|
|
14409
|
+
`Action ${input.actionId} is not in the registry. Run \`npx -y viberaven actions\` for current actions.
|
|
14410
|
+
`
|
|
14411
|
+
);
|
|
14412
|
+
return 1;
|
|
14413
|
+
}
|
|
14414
|
+
if (entry.status === "resolved") {
|
|
14415
|
+
process.stdout.write(
|
|
14416
|
+
`Action ${entry.id} (${entry.title ?? "untitled action"}) is already resolved. Run \`npx -y viberaven actions\` for current actions.
|
|
14417
|
+
`
|
|
14418
|
+
);
|
|
14419
|
+
return 0;
|
|
14420
|
+
}
|
|
14421
|
+
if (entry.status === "replaced") {
|
|
14422
|
+
process.stdout.write(
|
|
14423
|
+
`Action ${entry.id} (${entry.title ?? "untitled action"}) was replaced${entry.replacedBy ? ` by ${entry.replacedBy}` : ""}. Run \`npx -y viberaven actions\` for current actions.
|
|
14424
|
+
`
|
|
14425
|
+
);
|
|
14426
|
+
return 1;
|
|
14427
|
+
}
|
|
14428
|
+
if (entry.status === "stale" || !await currentActionExists(input.cwd, input.actionId)) {
|
|
14429
|
+
process.stdout.write(
|
|
14430
|
+
`Action ${entry.id} (${entry.title ?? "untitled action"}) is not in the current action surface. Run \`npx -y viberaven actions\` or rescan with \`npx -y viberaven --agent-mode\`.
|
|
14431
|
+
`
|
|
14432
|
+
);
|
|
14433
|
+
return 1;
|
|
14434
|
+
}
|
|
14435
|
+
process.stdout.write(
|
|
14436
|
+
`Verifying ${entry.id} (${entry.title ?? "untitled action"}).
|
|
14437
|
+
Run \`npx -y viberaven --verify\` to refresh the production gate for this action.
|
|
14438
|
+
`
|
|
14439
|
+
);
|
|
14440
|
+
return 0;
|
|
14441
|
+
}
|
|
14442
|
+
|
|
14443
|
+
// src/console/server.ts
|
|
14444
|
+
var import_node_http = require("node:http");
|
|
14445
|
+
var import_node_fs12 = require("node:fs");
|
|
14446
|
+
var import_promises24 = require("node:fs/promises");
|
|
14447
|
+
var import_node_path29 = require("node:path");
|
|
14448
|
+
|
|
14449
|
+
// src/console/commands.ts
|
|
14450
|
+
var import_node_child_process4 = require("node:child_process");
|
|
14451
|
+
var ACTION_ID_RE = /^VR-A[1-9][0-9]*$/;
|
|
14452
|
+
function isPlainRequestObject(request) {
|
|
14453
|
+
return typeof request === "object" && request !== null && !Array.isArray(request);
|
|
14454
|
+
}
|
|
14455
|
+
function hasOnlyKeys(request, keys) {
|
|
14456
|
+
const actualKeys = Object.keys(request);
|
|
14457
|
+
return actualKeys.length === keys.length && keys.every((key) => Object.prototype.hasOwnProperty.call(request, key));
|
|
14458
|
+
}
|
|
14459
|
+
function resolveConsoleCommandArgs(request) {
|
|
14460
|
+
if (!isPlainRequestObject(request) || typeof request.type !== "string") {
|
|
14461
|
+
throw new Error("Unsupported console command.");
|
|
14462
|
+
}
|
|
14463
|
+
if (request.type === "actions-json" && hasOnlyKeys(request, ["type"])) return ["actions", "--json"];
|
|
14464
|
+
if (request.type === "verify-gate" && hasOnlyKeys(request, ["type"])) return ["--verify"];
|
|
14465
|
+
if (request.type === "agent-mode" && hasOnlyKeys(request, ["type"])) return ["--agent-mode"];
|
|
14466
|
+
if (request.type === "verify-action" && hasOnlyKeys(request, ["type", "actionId"])) {
|
|
14467
|
+
if (typeof request.actionId !== "string" || !ACTION_ID_RE.test(request.actionId)) {
|
|
14468
|
+
throw new Error("Invalid VibeRaven action ID.");
|
|
14469
|
+
}
|
|
14470
|
+
return ["verify", "--action", request.actionId];
|
|
14471
|
+
}
|
|
14472
|
+
throw new Error("Unsupported console command.");
|
|
14473
|
+
}
|
|
14474
|
+
async function runAllowedConsoleCommand(input) {
|
|
14475
|
+
const args = resolveConsoleCommandArgs(input.request);
|
|
14476
|
+
return new Promise((resolve6, reject) => {
|
|
14477
|
+
let settled = false;
|
|
14478
|
+
const settle = (callback) => {
|
|
14479
|
+
if (settled) return;
|
|
14480
|
+
settled = true;
|
|
14481
|
+
callback();
|
|
14482
|
+
};
|
|
14483
|
+
const child = (0, import_node_child_process4.spawn)(process.execPath, [input.cliPath, ...args], {
|
|
14484
|
+
cwd: input.cwd,
|
|
14485
|
+
shell: false,
|
|
14486
|
+
windowsHide: true
|
|
14487
|
+
});
|
|
14488
|
+
let stdout = "";
|
|
14489
|
+
let stderr = "";
|
|
14490
|
+
child.stdout?.on("data", (chunk) => {
|
|
14491
|
+
stdout += String(chunk);
|
|
14492
|
+
});
|
|
14493
|
+
child.stderr?.on("data", (chunk) => {
|
|
14494
|
+
stderr += String(chunk);
|
|
14495
|
+
});
|
|
14496
|
+
child.on("error", (error) => {
|
|
14497
|
+
settle(() => reject(error));
|
|
14498
|
+
});
|
|
14499
|
+
child.on("close", (exitCode) => {
|
|
14500
|
+
settle(() => resolve6({ exitCode, stdout, stderr }));
|
|
14501
|
+
});
|
|
14502
|
+
});
|
|
14503
|
+
}
|
|
14504
|
+
|
|
14505
|
+
// src/console/manifest.ts
|
|
14506
|
+
var import_promises23 = require("node:fs/promises");
|
|
14507
|
+
var import_node_path28 = require("node:path");
|
|
14508
|
+
var ACTIONS_ARTIFACT_PATH = ".viberaven/actions.json";
|
|
14509
|
+
var REPO_PATH_PLACEHOLDER = "<repo-relative-path>";
|
|
14510
|
+
var REDACTED_PLACEHOLDER = "<redacted>";
|
|
14511
|
+
function isSensitiveKey(key) {
|
|
14512
|
+
return /secret|token|key|password|database_url|postgres_url|service_role/i.test(key);
|
|
14513
|
+
}
|
|
14514
|
+
function isAbsoluteLocalPath(value) {
|
|
14515
|
+
return /^[A-Za-z]:[\\/]/.test(value) || /^\/(?:Users|home|tmp|workspace|workspaces|var)\//.test(value);
|
|
14516
|
+
}
|
|
14517
|
+
function redactConsoleSecrets(text) {
|
|
14518
|
+
return redactString(text).replace(/\[REDACTED(?:_(?:SECRET|PRIVATE_KEY))?\]/g, REDACTED_PLACEHOLDER);
|
|
14519
|
+
}
|
|
14520
|
+
function redactConsoleValue(text) {
|
|
14521
|
+
return redactConsoleSecrets(text).replace(
|
|
14522
|
+
/\b([A-Z0-9_]*(?:SECRET|TOKEN|KEY|PASSWORD|DATABASE_URL|POSTGRES_URL|SERVICE_ROLE)[A-Z0-9_]*)=([^,\s]+)/gi,
|
|
14523
|
+
`$1=${REDACTED_PLACEHOLDER}`
|
|
14524
|
+
).replace(/\bpostgres(?:ql)?:\/\/[^@\s]+@/gi, `postgresql://${REDACTED_PLACEHOLDER}@`).replace(/(["'`])([A-Za-z]:[\\/][\s\S]*?)\1/g, `$1${REPO_PATH_PLACEHOLDER}$1`).replace(/(["'`])(\/(?:Users|home|tmp|workspace|workspaces|var)\/[\s\S]*?)\1/g, `$1${REPO_PATH_PLACEHOLDER}$1`).replace(/[A-Za-z]:[\\/][^"'`,;\n|]+[\\/][^"'`,;\n|]*/g, REPO_PATH_PLACEHOLDER).replace(/(^|[\s"'=])\/(?:Users|home|tmp|workspace|workspaces|var)\/[^"'`,;\n|]+\/[^"'`,;\n|]*/g, `$1${REPO_PATH_PLACEHOLDER}`).replace(/[A-Za-z]:[\\/][^\s`"]+/g, REPO_PATH_PLACEHOLDER).replace(/(^|[\s"'=])\/(?:Users|home|tmp|workspace|workspaces|var)\/[^\s`"]+/g, `$1${REPO_PATH_PLACEHOLDER}`).replace(/\beyJ[A-Za-z0-9._-]*\b/g, REDACTED_PLACEHOLDER);
|
|
14525
|
+
}
|
|
14526
|
+
function redactPayloadValue(value) {
|
|
14527
|
+
if (typeof value === "string") {
|
|
14528
|
+
return redactConsoleValue(value);
|
|
14529
|
+
}
|
|
14530
|
+
if (Array.isArray(value)) {
|
|
14531
|
+
return value.map((entry) => redactConsoleValue(entry));
|
|
14532
|
+
}
|
|
14533
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, redactUnknown2(entry, key)]));
|
|
14534
|
+
}
|
|
14535
|
+
function redactUnknown2(value, key) {
|
|
14536
|
+
if (key && isSensitiveKey(key)) {
|
|
14537
|
+
return REDACTED_PLACEHOLDER;
|
|
14538
|
+
}
|
|
14539
|
+
if (typeof value === "string") {
|
|
14540
|
+
return redactConsoleValue(value);
|
|
14541
|
+
}
|
|
14542
|
+
if (Array.isArray(value)) {
|
|
14543
|
+
return value.map((entry) => redactUnknown2(entry));
|
|
14544
|
+
}
|
|
14545
|
+
if (value && typeof value === "object") {
|
|
14546
|
+
return Object.fromEntries(Object.entries(value).map(([entryKey, entry]) => [entryKey, redactUnknown2(entry, entryKey)]));
|
|
14547
|
+
}
|
|
14548
|
+
return value;
|
|
14549
|
+
}
|
|
14550
|
+
function primaryControlFor(action) {
|
|
14551
|
+
if (!action.target) {
|
|
14552
|
+
return action.verifyCommand ? "verify" : "none";
|
|
14553
|
+
}
|
|
14554
|
+
if (action.target.type === "provider") return "provider";
|
|
14555
|
+
if (action.target.type === "file") return "file";
|
|
14556
|
+
if (action.target.type === "command") return "run";
|
|
14557
|
+
if (action.target.type === "verify") return "verify";
|
|
14558
|
+
if (action.target.type === "url") return "open";
|
|
14559
|
+
return "none";
|
|
14560
|
+
}
|
|
14561
|
+
function targetValue(target) {
|
|
14562
|
+
if (!target) return void 0;
|
|
14563
|
+
if (target.type === "url") return target.href;
|
|
14564
|
+
if (target.type === "file") return target.path;
|
|
14565
|
+
if (target.type === "command" || target.type === "verify") return target.command;
|
|
14566
|
+
return target.provider;
|
|
14567
|
+
}
|
|
14568
|
+
function targetView(target) {
|
|
14569
|
+
if (!target) {
|
|
14570
|
+
return { type: "none", label: "" };
|
|
14571
|
+
}
|
|
14572
|
+
const value = targetValue(target);
|
|
14573
|
+
const redactedValue = target.type === "file" && value && isAbsoluteLocalPath(value) ? REPO_PATH_PLACEHOLDER : value ? redactConsoleValue(value) : void 0;
|
|
14574
|
+
return {
|
|
14575
|
+
type: target.type,
|
|
14576
|
+
label: redactConsoleValue(target.label),
|
|
14577
|
+
...redactedValue ? { value: redactedValue } : {}
|
|
14578
|
+
};
|
|
14579
|
+
}
|
|
14580
|
+
function copyPayloadView(payload) {
|
|
14581
|
+
return {
|
|
14582
|
+
label: redactConsoleValue(payload.label),
|
|
14583
|
+
format: payload.format,
|
|
14584
|
+
value: redactPayloadValue(payload.value)
|
|
14585
|
+
};
|
|
14586
|
+
}
|
|
14587
|
+
function toConsoleActionViewModel(action) {
|
|
14588
|
+
const verifyCommand = action.verifyCommand ?? `npx -y viberaven verify --action ${action.id}`;
|
|
14589
|
+
return {
|
|
14590
|
+
id: action.id,
|
|
14591
|
+
title: redactConsoleValue(action.title),
|
|
14592
|
+
kind: action.kind,
|
|
14593
|
+
status: action.status,
|
|
14594
|
+
provider: action.provider ? redactConsoleValue(action.provider) : void 0,
|
|
14595
|
+
readiness: (action.readiness ?? []).map(redactConsoleValue),
|
|
14596
|
+
primaryControl: primaryControlFor(action),
|
|
14597
|
+
target: targetView(action.target),
|
|
14598
|
+
copyPayloads: (action.copyPayloads ?? []).map(copyPayloadView),
|
|
14599
|
+
verify: {
|
|
14600
|
+
actionId: action.id,
|
|
14601
|
+
command: redactConsoleValue(verifyCommand),
|
|
14602
|
+
fallbackCommand: action.fallbackCommand ? redactConsoleValue(action.fallbackCommand) : void 0
|
|
14603
|
+
},
|
|
14604
|
+
resumeInstruction: redactConsoleValue(action.resumeInstruction ?? ""),
|
|
14605
|
+
lifecycle: {
|
|
14606
|
+
replacedBy: action.replacedBy,
|
|
14607
|
+
supersedes: action.supersedes ?? []
|
|
14608
|
+
}
|
|
14609
|
+
};
|
|
14610
|
+
}
|
|
14611
|
+
function isV1ActionsManifest(value) {
|
|
14612
|
+
if (!value || typeof value !== "object") return false;
|
|
14613
|
+
const candidate = value;
|
|
14614
|
+
return candidate.version === 1 && Array.isArray(candidate.actions);
|
|
14615
|
+
}
|
|
14616
|
+
async function loadConsoleActionState(cwd) {
|
|
14617
|
+
const actionsPath = (0, import_node_path28.join)(getProjectArtifactsDir(cwd), "actions.json");
|
|
14618
|
+
try {
|
|
14619
|
+
const manifest = JSON.parse(await (0, import_promises23.readFile)(actionsPath, "utf8"));
|
|
14620
|
+
if (!isV1ActionsManifest(manifest)) {
|
|
14621
|
+
return {
|
|
14622
|
+
ok: false,
|
|
14623
|
+
reason: "invalid",
|
|
14624
|
+
message: "Invalid VibeRaven actions manifest. Run npx -y viberaven --agent-mode.",
|
|
14625
|
+
artifactPath: ACTIONS_ARTIFACT_PATH
|
|
14626
|
+
};
|
|
14627
|
+
}
|
|
14628
|
+
return {
|
|
14629
|
+
ok: true,
|
|
14630
|
+
generatedAt: String(manifest.generatedAt ?? ""),
|
|
14631
|
+
gateStatus: String(manifest.gateStatus ?? ""),
|
|
14632
|
+
artifactPath: ACTIONS_ARTIFACT_PATH,
|
|
14633
|
+
actions: manifest.actions.map(toConsoleActionViewModel)
|
|
14634
|
+
};
|
|
14635
|
+
} catch (error) {
|
|
14636
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
14637
|
+
return {
|
|
14638
|
+
ok: false,
|
|
14639
|
+
reason: "missing",
|
|
14640
|
+
message: "No VibeRaven actions found. Run npx -y viberaven --agent-mode.",
|
|
14641
|
+
artifactPath: ACTIONS_ARTIFACT_PATH
|
|
14642
|
+
};
|
|
14643
|
+
}
|
|
14644
|
+
return {
|
|
14645
|
+
ok: false,
|
|
14646
|
+
reason: "invalid",
|
|
14647
|
+
message: "Unable to read VibeRaven actions manifest. Run npx -y viberaven --agent-mode.",
|
|
14648
|
+
artifactPath: ACTIONS_ARTIFACT_PATH
|
|
14649
|
+
};
|
|
14650
|
+
}
|
|
14651
|
+
}
|
|
14652
|
+
|
|
14653
|
+
// src/console/security.ts
|
|
14654
|
+
var import_node_crypto2 = require("node:crypto");
|
|
14655
|
+
function createConsoleSessionToken() {
|
|
14656
|
+
return (0, import_node_crypto2.randomBytes)(32).toString("hex");
|
|
14657
|
+
}
|
|
14658
|
+
function isAllowedConsoleOrigin(origin, port) {
|
|
14659
|
+
if (!origin) return true;
|
|
14660
|
+
return origin === `http://127.0.0.1:${port}` || origin === `http://localhost:${port}`;
|
|
14661
|
+
}
|
|
14662
|
+
function requireConsoleToken(authorizationHeader, expectedToken) {
|
|
14663
|
+
const prefix = "Bearer ";
|
|
14664
|
+
if (!authorizationHeader?.startsWith(prefix)) return false;
|
|
14665
|
+
const received = Buffer.from(authorizationHeader.slice(prefix.length));
|
|
14666
|
+
const expected = Buffer.from(expectedToken);
|
|
14667
|
+
return received.length === expected.length && (0, import_node_crypto2.timingSafeEqual)(received, expected);
|
|
14668
|
+
}
|
|
14669
|
+
|
|
14670
|
+
// src/console/server.ts
|
|
14671
|
+
var PUBLIC_COMMAND_ERROR_MESSAGES = /* @__PURE__ */ new Set(["Unsupported console command.", "Invalid VibeRaven action ID."]);
|
|
14672
|
+
var STATIC_ASSETS = /* @__PURE__ */ new Map([
|
|
14673
|
+
["/", { filename: "index.html", contentType: "text/html; charset=utf-8" }],
|
|
14674
|
+
["/styles.css", { filename: "styles.css", contentType: "text/css; charset=utf-8" }],
|
|
14675
|
+
["/app.js", { filename: "app.js", contentType: "application/javascript; charset=utf-8" }]
|
|
14676
|
+
]);
|
|
14677
|
+
var ACTION_EVENT_FILES = /* @__PURE__ */ new Set(["actions.json", "action-registry.json", "gate-result.json"]);
|
|
14678
|
+
function sendJson(response, status, body) {
|
|
14679
|
+
response.writeHead(status, { "Content-Type": "application/json" });
|
|
14680
|
+
response.end(JSON.stringify(body));
|
|
14681
|
+
}
|
|
14682
|
+
function sendText(response, status, contentType, body) {
|
|
14683
|
+
response.writeHead(status, { "Content-Type": contentType });
|
|
14684
|
+
response.end(body);
|
|
14685
|
+
}
|
|
14686
|
+
function injectConsoleToken(html, token) {
|
|
14687
|
+
const tokenScript = `<script>window.__VIBERAVEN_TOKEN__ = ${JSON.stringify(token)};</script>`;
|
|
14688
|
+
return html.includes("</head>") ? html.replace("</head>", ` ${tokenScript}
|
|
14689
|
+
</head>`) : `${html}
|
|
14690
|
+
${tokenScript}`;
|
|
14691
|
+
}
|
|
14692
|
+
async function readJsonBody(request) {
|
|
14693
|
+
const chunks = [];
|
|
14694
|
+
for await (const chunk of request) {
|
|
14695
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
14696
|
+
}
|
|
14697
|
+
const body = Buffer.concat(chunks).toString("utf8").trim();
|
|
14698
|
+
if (!body) return {};
|
|
14699
|
+
try {
|
|
14700
|
+
return JSON.parse(body);
|
|
14701
|
+
} catch {
|
|
14702
|
+
throw new Error("Invalid JSON body.");
|
|
14703
|
+
}
|
|
14704
|
+
}
|
|
14705
|
+
function getHeader(request, name) {
|
|
14706
|
+
const value = request.headers[name.toLowerCase()];
|
|
14707
|
+
return Array.isArray(value) ? value[0] : value;
|
|
14708
|
+
}
|
|
14709
|
+
function getServerPort(server) {
|
|
14710
|
+
const address = server.address();
|
|
14711
|
+
if (!address || typeof address === "string") {
|
|
14712
|
+
throw new Error("Console server is not listening on a TCP port.");
|
|
14713
|
+
}
|
|
14714
|
+
return address.port;
|
|
14715
|
+
}
|
|
14716
|
+
function publicCommandErrorMessage(error) {
|
|
14717
|
+
if (error instanceof Error && PUBLIC_COMMAND_ERROR_MESSAGES.has(error.message)) {
|
|
14718
|
+
return error.message;
|
|
14719
|
+
}
|
|
14720
|
+
return "Console command failed.";
|
|
14721
|
+
}
|
|
14722
|
+
function sanitizeCommandOutput(output) {
|
|
14723
|
+
return redactConsoleValue(output).split(/\r?\n/).filter((line) => !/^\s*(?:stack\b|at\s+)/i.test(line)).join("\n");
|
|
14724
|
+
}
|
|
14725
|
+
function sanitizeCommandResult(result) {
|
|
14726
|
+
return {
|
|
14727
|
+
exitCode: result.exitCode,
|
|
14728
|
+
stdout: sanitizeCommandOutput(result.stdout),
|
|
14729
|
+
stderr: sanitizeCommandOutput(result.stderr)
|
|
14730
|
+
};
|
|
14731
|
+
}
|
|
14732
|
+
function resolveConsoleAsset(filename) {
|
|
14733
|
+
const candidates = [
|
|
14734
|
+
(0, import_node_path29.join)(__dirname, "console", filename),
|
|
14735
|
+
(0, import_node_path29.join)(__dirname, "..", "..", "assets", "console", filename)
|
|
14736
|
+
];
|
|
14737
|
+
return candidates.find((candidate) => (0, import_node_fs12.existsSync)(candidate));
|
|
14738
|
+
}
|
|
14739
|
+
function removeEventClient(eventClients, client) {
|
|
14740
|
+
eventClients.delete(client);
|
|
14741
|
+
}
|
|
14742
|
+
function destroyEventClient(eventClients, client) {
|
|
14743
|
+
removeEventClient(eventClients, client);
|
|
14744
|
+
if (client.response.writableEnded || client.response.destroyed) return;
|
|
14745
|
+
if (typeof client.response.destroy === "function") {
|
|
14746
|
+
client.response.destroy();
|
|
14747
|
+
return;
|
|
14748
|
+
}
|
|
14749
|
+
client.response.socket?.destroy();
|
|
14750
|
+
}
|
|
14751
|
+
function sendActionChangedEvent(eventClients, client) {
|
|
14752
|
+
const { response } = client;
|
|
14753
|
+
if (response.writableEnded || response.destroyed) {
|
|
14754
|
+
removeEventClient(eventClients, client);
|
|
14755
|
+
return;
|
|
14756
|
+
}
|
|
14757
|
+
const didFlush = response.write('event: actions\ndata: {"type":"actions-changed"}\n\n');
|
|
14758
|
+
if (!didFlush) {
|
|
14759
|
+
destroyEventClient(eventClients, client);
|
|
14760
|
+
}
|
|
14761
|
+
}
|
|
14762
|
+
async function createConsoleServer(input) {
|
|
14763
|
+
const token = createConsoleSessionToken();
|
|
14764
|
+
const cliPath = input.cliPath ?? process.argv[1] ?? "";
|
|
14765
|
+
const runCommand2 = input.runCommand ?? runAllowedConsoleCommand;
|
|
14766
|
+
const watchDirectory = input.watchDirectory ?? import_node_fs12.watch;
|
|
14767
|
+
const eventClients = /* @__PURE__ */ new Set();
|
|
14768
|
+
const viberavenDir = (0, import_node_path29.join)(input.cwd, ".viberaven");
|
|
14769
|
+
let watcher;
|
|
14770
|
+
function closeWatcher() {
|
|
14771
|
+
watcher?.close();
|
|
14772
|
+
watcher = void 0;
|
|
14773
|
+
}
|
|
14774
|
+
if ((0, import_node_fs12.existsSync)(viberavenDir)) {
|
|
14775
|
+
try {
|
|
14776
|
+
watcher = watchDirectory(viberavenDir, (_eventType, filename) => {
|
|
14777
|
+
const changedFile = typeof filename === "string" ? filename : void 0;
|
|
14778
|
+
if (!changedFile || !ACTION_EVENT_FILES.has(changedFile)) return;
|
|
14779
|
+
for (const client of Array.from(eventClients)) {
|
|
14780
|
+
sendActionChangedEvent(eventClients, client);
|
|
14781
|
+
}
|
|
14782
|
+
});
|
|
14783
|
+
watcher.on("error", closeWatcher);
|
|
14784
|
+
} catch {
|
|
14785
|
+
closeWatcher();
|
|
14786
|
+
}
|
|
14787
|
+
}
|
|
14788
|
+
const server = (0, import_node_http.createServer)(async (request, response) => {
|
|
14789
|
+
const actualPort2 = getServerPort(server);
|
|
14790
|
+
const origin = getHeader(request, "origin");
|
|
14791
|
+
const authorization = getHeader(request, "authorization");
|
|
14792
|
+
const hasValidToken = requireConsoleToken(authorization, token);
|
|
14793
|
+
const hasAllowedOrigin = isAllowedConsoleOrigin(origin, actualPort2);
|
|
14794
|
+
const url = new URL(request.url ?? "/", `http://127.0.0.1:${actualPort2}`);
|
|
14795
|
+
const staticAsset = STATIC_ASSETS.get(url.pathname);
|
|
14796
|
+
if (request.method === "GET" && staticAsset) {
|
|
14797
|
+
const assetPath = resolveConsoleAsset(staticAsset.filename);
|
|
14798
|
+
if (!assetPath) {
|
|
14799
|
+
sendJson(response, 404, { ok: false, error: "Console asset not found." });
|
|
14800
|
+
return;
|
|
14801
|
+
}
|
|
14802
|
+
const assetBody = await (0, import_promises24.readFile)(assetPath, "utf8");
|
|
14803
|
+
sendText(
|
|
14804
|
+
response,
|
|
14805
|
+
200,
|
|
14806
|
+
staticAsset.contentType,
|
|
14807
|
+
staticAsset.filename === "index.html" ? injectConsoleToken(assetBody, token) : assetBody
|
|
14808
|
+
);
|
|
14809
|
+
return;
|
|
14810
|
+
}
|
|
14811
|
+
if (request.method === "GET" && url.pathname === "/api/actions") {
|
|
14812
|
+
if (!hasValidToken && !hasAllowedOrigin) {
|
|
14813
|
+
sendJson(response, 403, { ok: false, error: "Disallowed origin." });
|
|
14814
|
+
return;
|
|
14815
|
+
}
|
|
14816
|
+
sendJson(response, 200, await loadConsoleActionState(input.cwd));
|
|
14817
|
+
return;
|
|
14818
|
+
}
|
|
14819
|
+
if (request.method === "GET" && url.pathname === "/api/events") {
|
|
14820
|
+
if (!hasValidToken && !hasAllowedOrigin) {
|
|
14821
|
+
sendJson(response, 403, { ok: false, error: "Disallowed origin." });
|
|
14822
|
+
return;
|
|
14823
|
+
}
|
|
14824
|
+
response.writeHead(200, {
|
|
14825
|
+
"Content-Type": "text/event-stream",
|
|
14826
|
+
"Cache-Control": "no-cache",
|
|
14827
|
+
Connection: "keep-alive"
|
|
14828
|
+
});
|
|
14829
|
+
response.write(": connected\n\n");
|
|
14830
|
+
input.onEventResponse?.(response);
|
|
14831
|
+
const client = { response };
|
|
14832
|
+
const removeClient = () => removeEventClient(eventClients, client);
|
|
14833
|
+
eventClients.add(client);
|
|
14834
|
+
request.on("close", removeClient);
|
|
14835
|
+
request.on("error", removeClient);
|
|
14836
|
+
response.on("close", removeClient);
|
|
14837
|
+
response.on("error", removeClient);
|
|
14838
|
+
return;
|
|
14839
|
+
}
|
|
14840
|
+
if (request.method === "POST" && url.pathname === "/api/command") {
|
|
14841
|
+
if (!hasAllowedOrigin) {
|
|
14842
|
+
sendJson(response, 403, { ok: false, error: "Disallowed origin." });
|
|
14843
|
+
return;
|
|
14844
|
+
}
|
|
14845
|
+
if (!hasValidToken) {
|
|
14846
|
+
sendJson(response, 401, { ok: false, error: "Unauthorized." });
|
|
14847
|
+
return;
|
|
14848
|
+
}
|
|
14849
|
+
let commandRequest;
|
|
14850
|
+
try {
|
|
14851
|
+
commandRequest = await readJsonBody(request);
|
|
14852
|
+
} catch {
|
|
14853
|
+
sendJson(response, 400, { ok: false, error: "Invalid JSON body." });
|
|
14854
|
+
return;
|
|
14855
|
+
}
|
|
14856
|
+
try {
|
|
14857
|
+
const result = await runCommand2({ cliPath, cwd: input.cwd, request: commandRequest });
|
|
14858
|
+
sendJson(response, 200, { ok: true, result: sanitizeCommandResult(result) });
|
|
14859
|
+
} catch (error) {
|
|
14860
|
+
sendJson(response, 400, { ok: false, error: publicCommandErrorMessage(error) });
|
|
14861
|
+
}
|
|
14862
|
+
return;
|
|
14863
|
+
}
|
|
14864
|
+
sendJson(response, 404, { ok: false, error: "Not found." });
|
|
14865
|
+
});
|
|
14866
|
+
await new Promise((resolve6, reject) => {
|
|
14867
|
+
const handleListenError = (error) => {
|
|
14868
|
+
closeWatcher();
|
|
14869
|
+
reject(error);
|
|
14870
|
+
};
|
|
14871
|
+
server.once("error", handleListenError);
|
|
14872
|
+
server.listen(input.port ?? 0, "127.0.0.1", () => {
|
|
14873
|
+
server.off("error", handleListenError);
|
|
14874
|
+
resolve6();
|
|
14875
|
+
});
|
|
14876
|
+
});
|
|
14877
|
+
const actualPort = getServerPort(server);
|
|
14878
|
+
return {
|
|
14879
|
+
url: `http://127.0.0.1:${actualPort}`,
|
|
14880
|
+
token,
|
|
14881
|
+
close: () => new Promise((resolve6, reject) => {
|
|
14882
|
+
closeWatcher();
|
|
14883
|
+
for (const client of eventClients) {
|
|
14884
|
+
client.response.end();
|
|
14885
|
+
}
|
|
14886
|
+
eventClients.clear();
|
|
14887
|
+
server.close((error) => {
|
|
14888
|
+
if (error) {
|
|
14889
|
+
reject(error);
|
|
14890
|
+
return;
|
|
14891
|
+
}
|
|
14892
|
+
resolve6();
|
|
14893
|
+
});
|
|
14894
|
+
})
|
|
14895
|
+
};
|
|
14896
|
+
}
|
|
14897
|
+
|
|
14898
|
+
// src/commands/console.ts
|
|
14899
|
+
async function runConsoleCliCommand(input) {
|
|
14900
|
+
const closeServer = async (serverToClose) => {
|
|
14901
|
+
try {
|
|
14902
|
+
await serverToClose.close();
|
|
14903
|
+
return 0;
|
|
14904
|
+
} catch (error) {
|
|
14905
|
+
process.stderr.write(
|
|
14906
|
+
`Could not stop console server: ${error instanceof Error ? error.message : String(error)}
|
|
14907
|
+
`
|
|
14908
|
+
);
|
|
14909
|
+
return 1;
|
|
14910
|
+
}
|
|
14911
|
+
};
|
|
14912
|
+
const start = input.start ?? createConsoleServer;
|
|
14913
|
+
const server = await start({
|
|
14914
|
+
cwd: input.cwd,
|
|
14915
|
+
port: input.port,
|
|
14916
|
+
open: false
|
|
14917
|
+
});
|
|
14918
|
+
process.stdout.write(`VibeRaven Console: ${server.url}
|
|
14919
|
+
`);
|
|
14920
|
+
process.stdout.write("Local only. Press Ctrl+C to stop.\n");
|
|
14921
|
+
if (input.open) {
|
|
14922
|
+
try {
|
|
14923
|
+
await openUrlInBrowser(server.url);
|
|
14924
|
+
} catch (error) {
|
|
14925
|
+
process.stderr.write(
|
|
14926
|
+
`Could not open browser: ${error instanceof Error ? error.message : String(error)}. Open manually: ${server.url}
|
|
14927
|
+
`
|
|
14928
|
+
);
|
|
14929
|
+
}
|
|
14930
|
+
}
|
|
14931
|
+
if (input.once) {
|
|
14932
|
+
return closeServer(server);
|
|
14933
|
+
}
|
|
14934
|
+
return new Promise((resolve6) => {
|
|
14935
|
+
const onSigint = () => {
|
|
14936
|
+
process.off("SIGINT", onSigint);
|
|
14937
|
+
void closeServer(server).then(resolve6);
|
|
14938
|
+
};
|
|
14939
|
+
process.once("SIGINT", onSigint);
|
|
14940
|
+
});
|
|
14941
|
+
}
|
|
14942
|
+
|
|
13927
14943
|
// src/commands/preview.ts
|
|
13928
14944
|
var import_picocolors5 = __toESM(require_picocolors());
|
|
14945
|
+
function buildPreviewManifest() {
|
|
14946
|
+
return {
|
|
14947
|
+
version: 1,
|
|
14948
|
+
generatedAt: "2026-06-16T00:00:00.000Z",
|
|
14949
|
+
workspaceRoot: ".",
|
|
14950
|
+
gateStatus: "not_clear",
|
|
14951
|
+
actions: [
|
|
14952
|
+
{
|
|
14953
|
+
id: "VR-A1",
|
|
14954
|
+
actionKey: "provider-action:stripe:stripe-webhook:/api/stripe/webhook:checkout.session.completed,customer.subscription.deleted,customer.subscription.updated",
|
|
14955
|
+
revision: 1,
|
|
14956
|
+
kind: "provider-action",
|
|
14957
|
+
provider: "stripe",
|
|
14958
|
+
title: "Connect Stripe Webhook",
|
|
14959
|
+
status: "waiting-on-provider",
|
|
14960
|
+
severity: "critical",
|
|
14961
|
+
gapId: "stripe_webhook_secret_missing",
|
|
14962
|
+
readiness: ["Webhook endpoint path detected", "Required events prepared"],
|
|
14963
|
+
target: {
|
|
14964
|
+
type: "provider",
|
|
14965
|
+
provider: "stripe",
|
|
14966
|
+
label: "Create Stripe webhook endpoint for /api/stripe/webhook."
|
|
14967
|
+
},
|
|
14968
|
+
copyPayloads: [
|
|
14969
|
+
{
|
|
14970
|
+
label: "Required webhook events",
|
|
14971
|
+
format: "text",
|
|
14972
|
+
value: [
|
|
14973
|
+
"checkout.session.completed",
|
|
14974
|
+
"customer.subscription.updated",
|
|
14975
|
+
"customer.subscription.deleted"
|
|
14976
|
+
]
|
|
14977
|
+
}
|
|
14978
|
+
],
|
|
14979
|
+
verifyCommand: `${PUBLIC_COMMAND} verify --action VR-A1`,
|
|
14980
|
+
resumeInstruction: "Stripe webhook is configured. Continue VibeRaven from VR-A1."
|
|
14981
|
+
},
|
|
14982
|
+
{
|
|
14983
|
+
id: "VR-A2",
|
|
14984
|
+
actionKey: "repo-code:supabase:rls:supabase/migrations/20260615_rls.sql:user-owned-tables",
|
|
14985
|
+
revision: 1,
|
|
14986
|
+
kind: "repo-code",
|
|
14987
|
+
provider: "supabase",
|
|
14988
|
+
title: "Apply Supabase RLS",
|
|
14989
|
+
status: "waiting-on-database-proof",
|
|
14990
|
+
severity: "critical",
|
|
14991
|
+
gapId: "supabase_rls_policy_proof",
|
|
14992
|
+
readiness: ["Affected tables detected", "Migration file target prepared"],
|
|
14993
|
+
target: {
|
|
14994
|
+
type: "file",
|
|
14995
|
+
label: "RLS migration",
|
|
14996
|
+
path: "supabase/migrations/20260615_rls.sql"
|
|
14997
|
+
},
|
|
14998
|
+
copyPayloads: [
|
|
14999
|
+
{
|
|
15000
|
+
label: "Tiny SQL shape",
|
|
15001
|
+
format: "sql",
|
|
15002
|
+
value: 'alter table public.example enable row level security;\ncreate policy "Users manage own rows" on public.example for all using (auth.uid() = user_id);'
|
|
15003
|
+
}
|
|
15004
|
+
],
|
|
15005
|
+
verifyCommand: `${PUBLIC_COMMAND} verify --action VR-A2`,
|
|
15006
|
+
resumeInstruction: "Supabase RLS is applied. Continue VibeRaven from VR-A2."
|
|
15007
|
+
},
|
|
15008
|
+
{
|
|
15009
|
+
id: "VR-A3",
|
|
15010
|
+
actionKey: "verify:gate:final:npx-y-viberaven-strict",
|
|
15011
|
+
revision: 1,
|
|
15012
|
+
kind: "verify",
|
|
15013
|
+
title: "Run Final Verification",
|
|
15014
|
+
status: "blocked",
|
|
15015
|
+
severity: "warning",
|
|
15016
|
+
readiness: ["Run after repo-code and provider actions are complete"],
|
|
15017
|
+
target: {
|
|
15018
|
+
type: "command",
|
|
15019
|
+
label: "Strict gate",
|
|
15020
|
+
command: `${PUBLIC_COMMAND} --strict`
|
|
15021
|
+
},
|
|
15022
|
+
resumeInstruction: "Final verification finished. Continue VibeRaven from VR-A3."
|
|
15023
|
+
}
|
|
15024
|
+
]
|
|
15025
|
+
};
|
|
15026
|
+
}
|
|
15027
|
+
async function runPreviewCommand(options) {
|
|
15028
|
+
const manifest = buildPreviewManifest();
|
|
15029
|
+
if (options.json) {
|
|
15030
|
+
console.log(JSON.stringify({ manifest }, null, 2));
|
|
15031
|
+
return 0;
|
|
15032
|
+
}
|
|
15033
|
+
console.log(import_picocolors5.default.dim(`VibeRaven ${VERSION} preview - local rehearsal, no login or API spend.`));
|
|
15034
|
+
console.log(import_picocolors5.default.dim("This is sample renderer data, not a production verdict for this repository."));
|
|
15035
|
+
console.log("");
|
|
15036
|
+
console.log(renderActionSurface(manifest, { limit: 3 }).trimEnd());
|
|
15037
|
+
if (options.agentMode) {
|
|
15038
|
+
console.log("");
|
|
15039
|
+
console.log(
|
|
15040
|
+
import_picocolors5.default.dim(
|
|
15041
|
+
"Agent note: treat each visible action as the next production action surface. Do not mark provider actions resolved until provider proof exists."
|
|
15042
|
+
)
|
|
15043
|
+
);
|
|
15044
|
+
}
|
|
15045
|
+
return 0;
|
|
15046
|
+
}
|
|
13929
15047
|
|
|
13930
15048
|
// src/output/nextActionBlock.ts
|
|
13931
15049
|
function buildNextActionBlock(tasks, loopState, plan) {
|
|
@@ -13941,11 +15059,7 @@ function buildNextActionBlock(tasks, loopState, plan) {
|
|
|
13941
15059
|
return {
|
|
13942
15060
|
...base,
|
|
13943
15061
|
type: "stalled",
|
|
13944
|
-
title: "Loop stalled
|
|
13945
|
-
agentInstruction: "Stop the scan loop. Explain the blocker plainly, then either guide the provider/user action or report that no safe repo-code recipe is available.",
|
|
13946
|
-
executionPlan: [
|
|
13947
|
-
{ kind: "stop", note: "Do not keep spending scans while the loop is stalled." }
|
|
13948
|
-
],
|
|
15062
|
+
title: "Loop stalled \u2014 no gap reduction after 2+ consecutive scans",
|
|
13949
15063
|
requiresUserAction: true,
|
|
13950
15064
|
stallReason
|
|
13951
15065
|
};
|
|
@@ -13954,67 +15068,42 @@ function buildNextActionBlock(tasks, loopState, plan) {
|
|
|
13954
15068
|
return {
|
|
13955
15069
|
...base,
|
|
13956
15070
|
type: "verify",
|
|
13957
|
-
title: "Batch complete
|
|
13958
|
-
agentInstruction: "Run verify now, read the refreshed VibeRaven artifacts, then continue with the next safe repo fix or provider step. Do not stop at a summary.",
|
|
13959
|
-
executionPlan: [
|
|
13960
|
-
{ kind: "verify", command: "viberaven_verify" },
|
|
13961
|
-
{ kind: "continue-loop", note: "After verify, continue until the gate is clear or a provider/user blocker remains." }
|
|
13962
|
-
],
|
|
15071
|
+
title: "Batch complete \u2014 run verify to rescan before next batch",
|
|
13963
15072
|
mcpTool: "viberaven_verify",
|
|
13964
15073
|
mcpArgs: {},
|
|
13965
15074
|
requiresUserAction: false
|
|
13966
15075
|
};
|
|
13967
15076
|
}
|
|
13968
|
-
const repoCodeTask = tasks.find((
|
|
15077
|
+
const repoCodeTask = tasks.find((t) => t.fixType === "repo-code" && !t.requiresUserAction);
|
|
13969
15078
|
if (repoCodeTask) {
|
|
13970
|
-
const fallbackCommand = repoCodeTask.mcpArgs ? `npx -y viberaven --heal --apply --gap ${repoCodeTask.gapId} --yes` : void 0;
|
|
13971
15079
|
return {
|
|
13972
15080
|
...base,
|
|
13973
15081
|
type: "repo-code",
|
|
13974
15082
|
gapId: repoCodeTask.gapId,
|
|
13975
15083
|
title: repoCodeTask.title,
|
|
13976
|
-
agentInstruction: "Apply this repo-code fix now using the MCP tool or fallback command, then continue the local VibeRaven loop. Do not stop at a summary.",
|
|
13977
|
-
executionPlan: [
|
|
13978
|
-
{
|
|
13979
|
-
kind: "apply-repo-fix",
|
|
13980
|
-
command: repoCodeTask.mcpTool ?? fallbackCommand,
|
|
13981
|
-
note: "Patch only this gap and preserve unrelated user changes."
|
|
13982
|
-
},
|
|
13983
|
-
{ kind: "continue-loop", note: "Apply remaining safe repo fixes until the batch is full, then verify once." }
|
|
13984
|
-
],
|
|
13985
15084
|
mcpTool: repoCodeTask.mcpTool,
|
|
13986
15085
|
mcpArgs: repoCodeTask.mcpArgs,
|
|
13987
|
-
fallbackCommand,
|
|
15086
|
+
fallbackCommand: repoCodeTask.mcpArgs ? `npx -y viberaven --heal --apply --gap ${repoCodeTask.gapId} --yes` : void 0,
|
|
13988
15087
|
requiresUserAction: false
|
|
13989
15088
|
};
|
|
13990
15089
|
}
|
|
13991
|
-
const providerTask = tasks.find((
|
|
15090
|
+
const providerTask = tasks.find((t) => t.fixType === "provider-action");
|
|
13992
15091
|
if (providerTask) {
|
|
13993
15092
|
return {
|
|
13994
15093
|
...base,
|
|
13995
15094
|
type: "provider-action",
|
|
13996
15095
|
gapId: providerTask.gapId,
|
|
13997
15096
|
title: providerTask.title,
|
|
13998
|
-
agentInstruction: "Open or present the provider step now. If browser/tool access is available, open the dashboard link for the user; otherwise show the exact step and copy values. Do not keep telling the user to verify until the provider step is done.",
|
|
13999
|
-
executionPlan: [
|
|
14000
|
-
{ kind: "open-provider-step", note: "Use VIBERAVEN_PROVIDER_ACTION for the exact dashboard step and URL." },
|
|
14001
|
-
{ kind: "wait-for-user", note: "Wait for confirmation or read-only MCP evidence before verifying." }
|
|
14002
|
-
],
|
|
14003
15097
|
requiresUserAction: true
|
|
14004
15098
|
};
|
|
14005
15099
|
}
|
|
14006
|
-
const upgradeTask = tasks.find((
|
|
15100
|
+
const upgradeTask = tasks.find((t) => t.fixType === "upgrade-required");
|
|
14007
15101
|
if (upgradeTask) {
|
|
14008
15102
|
return {
|
|
14009
15103
|
...base,
|
|
14010
15104
|
type: "upgrade-required",
|
|
14011
15105
|
gapId: upgradeTask.gapId,
|
|
14012
15106
|
title: upgradeTask.title,
|
|
14013
|
-
agentInstruction: "Explain that this production lane needs an upgrade or connected provider evidence. Continue with any remaining unlocked repo-code fixes if present; otherwise stop cleanly.",
|
|
14014
|
-
executionPlan: [
|
|
14015
|
-
{ kind: "upgrade", command: "https://viberaven.dev/pricing" },
|
|
14016
|
-
{ kind: "stop", note: "Do not pretend locked lanes or provider state were fixed locally." }
|
|
14017
|
-
],
|
|
14018
15107
|
requiresUserAction: true,
|
|
14019
15108
|
upgradeUrl: "https://viberaven.dev/pricing"
|
|
14020
15109
|
};
|
|
@@ -14022,20 +15111,15 @@ function buildNextActionBlock(tasks, loopState, plan) {
|
|
|
14022
15111
|
return {
|
|
14023
15112
|
...base,
|
|
14024
15113
|
type: "done",
|
|
14025
|
-
title: "All gaps resolved
|
|
14026
|
-
agentInstruction: "The repo-code gate is clear. Run strict before deploy and still verify provider dashboard state through MCP or the provider dashboard.",
|
|
14027
|
-
executionPlan: [
|
|
14028
|
-
{ kind: "verify", command: "npx -y viberaven --strict" },
|
|
14029
|
-
{ kind: "stop", note: "Do not deploy until provider dashboard state is verified where applicable." }
|
|
14030
|
-
],
|
|
15114
|
+
title: "All gaps resolved \u2014 production gate is clear",
|
|
14031
15115
|
requiresUserAction: false
|
|
14032
15116
|
};
|
|
14033
15117
|
}
|
|
14034
15118
|
function resolveStallReason(tasks) {
|
|
14035
15119
|
if (tasks.length === 0) return "no-recipes";
|
|
14036
|
-
const allUpgradeOrEmpty = tasks.every((
|
|
15120
|
+
const allUpgradeOrEmpty = tasks.every((t) => t.fixType === "upgrade-required");
|
|
14037
15121
|
if (allUpgradeOrEmpty) return "no-recipes";
|
|
14038
|
-
const allProviderAction = tasks.every((
|
|
15122
|
+
const allProviderAction = tasks.every((t) => t.fixType === "provider-action");
|
|
14039
15123
|
if (allProviderAction) return "provider-action-required";
|
|
14040
15124
|
return "unknown";
|
|
14041
15125
|
}
|
|
@@ -14064,7 +15148,7 @@ function buildProviderActionBlock(task) {
|
|
|
14064
15148
|
}
|
|
14065
15149
|
function printProviderActionBlock(tasks) {
|
|
14066
15150
|
const task = tasks.find(
|
|
14067
|
-
(
|
|
15151
|
+
(t) => t.fixType === "provider-action" && t.requiresUserAction === true
|
|
14068
15152
|
);
|
|
14069
15153
|
if (!task) return;
|
|
14070
15154
|
const block = buildProviderActionBlock(task);
|
|
@@ -14079,138 +15163,10 @@ function printNextActionBlock(block) {
|
|
|
14079
15163
|
console.log(NEXT_ACTION_END);
|
|
14080
15164
|
}
|
|
14081
15165
|
|
|
14082
|
-
// src/commands/preview.ts
|
|
14083
|
-
function previewArtifact(cwd) {
|
|
14084
|
-
return {
|
|
14085
|
-
version: 1,
|
|
14086
|
-
scannedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14087
|
-
workspacePath: cwd,
|
|
14088
|
-
score: 42,
|
|
14089
|
-
scoreLabel: "Blocked",
|
|
14090
|
-
summary: "Vercel, Supabase, and Stripe are detected, but launch-critical provider setup is not proven yet.",
|
|
14091
|
-
archetype: "ai-built-saas",
|
|
14092
|
-
plan: "free",
|
|
14093
|
-
productionCorePercent: 40,
|
|
14094
|
-
usage: {
|
|
14095
|
-
plan: "free",
|
|
14096
|
-
remainingPrompts: 0,
|
|
14097
|
-
used: 0,
|
|
14098
|
-
limit: 0,
|
|
14099
|
-
period: "lifetime",
|
|
14100
|
-
unlockedMapCategoryKeys: ["appFlow", "frontend", "backend", "auth", "database", "payments"]
|
|
14101
|
-
},
|
|
14102
|
-
gaps: [
|
|
14103
|
-
{
|
|
14104
|
-
id: "auth_secret_missing",
|
|
14105
|
-
category: "SECURITY & AUTH",
|
|
14106
|
-
severity: "critical",
|
|
14107
|
-
title: "Create production auth secret",
|
|
14108
|
-
detail: "The app needs a production session secret before real users sign in.",
|
|
14109
|
-
copyPrompt: "Generate and document NEXTAUTH_SECRET or the equivalent auth secret for this stack.",
|
|
14110
|
-
toolSuggestions: [],
|
|
14111
|
-
mcpSuggestion: null,
|
|
14112
|
-
primaryMapCategory: "auth",
|
|
14113
|
-
affectedMapCategories: []
|
|
14114
|
-
},
|
|
14115
|
-
{
|
|
14116
|
-
id: "rls_disabled",
|
|
14117
|
-
category: "DATABASE & DATA",
|
|
14118
|
-
severity: "critical",
|
|
14119
|
-
title: "Verify Supabase RLS before launch",
|
|
14120
|
-
detail: "Database access must be protected by Row Level Security or equivalent server-side controls.",
|
|
14121
|
-
copyPrompt: "Open Supabase and verify RLS policies for user-owned tables.",
|
|
14122
|
-
toolSuggestions: [],
|
|
14123
|
-
mcpSuggestion: null,
|
|
14124
|
-
primaryMapCategory: "database",
|
|
14125
|
-
affectedMapCategories: []
|
|
14126
|
-
},
|
|
14127
|
-
{
|
|
14128
|
-
id: "stripe_webhook_secret_missing",
|
|
14129
|
-
category: "PAYMENTS",
|
|
14130
|
-
severity: "critical",
|
|
14131
|
-
title: "Connect Stripe webhook secret",
|
|
14132
|
-
detail: "Stripe webhook signature verification needs STRIPE_WEBHOOK_SECRET before live payments.",
|
|
14133
|
-
copyPrompt: "Create a Stripe webhook endpoint and add STRIPE_WEBHOOK_SECRET to the deployment environment.",
|
|
14134
|
-
toolSuggestions: [],
|
|
14135
|
-
mcpSuggestion: null,
|
|
14136
|
-
primaryMapCategory: "payments",
|
|
14137
|
-
affectedMapCategories: []
|
|
14138
|
-
}
|
|
14139
|
-
],
|
|
14140
|
-
missionGraph: {
|
|
14141
|
-
areas: [],
|
|
14142
|
-
byArea: {},
|
|
14143
|
-
byProvider: {},
|
|
14144
|
-
repositoryEvidence: { env: [], security: [] }
|
|
14145
|
-
},
|
|
14146
|
-
stackWiring: { items: [], byKey: {} },
|
|
14147
|
-
providerRegistry: {
|
|
14148
|
-
version: 1,
|
|
14149
|
-
source: "bundled",
|
|
14150
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14151
|
-
status: "fresh",
|
|
14152
|
-
staleAfterDays: 14,
|
|
14153
|
-
providers: []
|
|
14154
|
-
}
|
|
14155
|
-
};
|
|
14156
|
-
}
|
|
14157
|
-
function check(done, text) {
|
|
14158
|
-
const mark = done ? import_picocolors5.default.green("[x]") : import_picocolors5.default.yellow("[ ]");
|
|
14159
|
-
return ` ${mark} ${text}`;
|
|
14160
|
-
}
|
|
14161
|
-
async function runPreviewCommand(options) {
|
|
14162
|
-
const artifact = previewArtifact(options.cwd);
|
|
14163
|
-
const tasks = buildTaskList(artifact);
|
|
14164
|
-
const next = buildNextActionBlock(tasks, {
|
|
14165
|
-
batchApplied: 1,
|
|
14166
|
-
appliedGapIdsSinceScan: ["auth_secret_missing"],
|
|
14167
|
-
lastGapCount: artifact.gaps.length,
|
|
14168
|
-
stalledScans: 0
|
|
14169
|
-
}, "free");
|
|
14170
|
-
if (options.json) {
|
|
14171
|
-
console.log(JSON.stringify({ artifact, tasks, next }, null, 2));
|
|
14172
|
-
return 0;
|
|
14173
|
-
}
|
|
14174
|
-
console.log(import_picocolors5.default.dim(`VibeRaven ${VERSION} preview - local rehearsal, no login or API spend.`));
|
|
14175
|
-
console.log(import_picocolors5.default.bold("VibeRaven"));
|
|
14176
|
-
console.log("Taking this app from demo to production.");
|
|
14177
|
-
console.log("");
|
|
14178
|
-
console.log(`${import_picocolors5.default.bold("Status:")} ${import_picocolors5.default.red("BLOCKED")} | vercel + supabase + stripe | 3 launch gaps`);
|
|
14179
|
-
console.log("");
|
|
14180
|
-
console.log(import_picocolors5.default.bold("Progress"));
|
|
14181
|
-
console.log(check(true, "Understand the stack and launch gaps"));
|
|
14182
|
-
console.log(check(true, "Patch the first repo-owned secret gap"));
|
|
14183
|
-
console.log(check(false, "Open Supabase and verify RLS policies"));
|
|
14184
|
-
console.log(check(false, "Connect Stripe webhook signing secret"));
|
|
14185
|
-
console.log(check(false, "Verify again before launch"));
|
|
14186
|
-
console.log("");
|
|
14187
|
-
console.log(import_picocolors5.default.bold("I can do now"));
|
|
14188
|
-
console.log(" 1. Add missing env names and agent rules.");
|
|
14189
|
-
console.log(" 2. Prepare focused repo fixes the coding agent can apply.");
|
|
14190
|
-
console.log(" 3. Keep the launch loop moving until provider proof is needed.");
|
|
14191
|
-
console.log("");
|
|
14192
|
-
console.log(import_picocolors5.default.bold("I need you for"));
|
|
14193
|
-
console.log(" 1. Supabase: open the project and confirm RLS policies.");
|
|
14194
|
-
console.log(" 2. Stripe: create or open the webhook endpoint.");
|
|
14195
|
-
console.log(" 3. Deployment: paste provider secrets into Vercel or your host.");
|
|
14196
|
-
console.log("");
|
|
14197
|
-
console.log(import_picocolors5.default.bold("Open"));
|
|
14198
|
-
console.log(` supabase: ${import_picocolors5.default.cyan("https://supabase.com/dashboard")}`);
|
|
14199
|
-
console.log(` stripe: ${import_picocolors5.default.cyan("https://dashboard.stripe.com/")}`);
|
|
14200
|
-
console.log("");
|
|
14201
|
-
console.log(`${import_picocolors5.default.bold("Copy env names:")} NEXTAUTH_SECRET, STRIPE_WEBHOOK_SECRET`);
|
|
14202
|
-
console.log(`${import_picocolors5.default.bold("Next:")} I will guide the blocked provider setup, then verify when you confirm it is done.`);
|
|
14203
|
-
if (options.agentMode) {
|
|
14204
|
-
console.log("");
|
|
14205
|
-
console.log(import_picocolors5.default.dim("Agent note: render the checklist above as the live timeline; update [ ] to [x] as work completes."));
|
|
14206
|
-
}
|
|
14207
|
-
return 0;
|
|
14208
|
-
}
|
|
14209
|
-
|
|
14210
15166
|
// src/providerMcpBridge.ts
|
|
14211
|
-
var
|
|
15167
|
+
var import_node_fs13 = require("node:fs");
|
|
14212
15168
|
var import_node_os2 = require("node:os");
|
|
14213
|
-
var
|
|
15169
|
+
var import_node_path30 = require("node:path");
|
|
14214
15170
|
var UPGRADE_URL4 = "https://viberaven.dev/pricing";
|
|
14215
15171
|
var FALLBACK_COMMAND = "npx -y viberaven audit --vercel-supabase --json";
|
|
14216
15172
|
var SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["supabase", "vercel"]);
|
|
@@ -14218,9 +15174,9 @@ var configPathsOverride;
|
|
|
14218
15174
|
function defaultMcpConfigPaths() {
|
|
14219
15175
|
const home = (0, import_node_os2.homedir)();
|
|
14220
15176
|
return [
|
|
14221
|
-
(0,
|
|
14222
|
-
(0,
|
|
14223
|
-
(0,
|
|
15177
|
+
(0, import_node_path30.join)(home, ".config", "claude", "claude_desktop_config.json"),
|
|
15178
|
+
(0, import_node_path30.join)(home, ".cursor", "mcp.json"),
|
|
15179
|
+
(0, import_node_path30.join)(home, ".gemini", "antigravity", "mcp_config.json")
|
|
14224
15180
|
];
|
|
14225
15181
|
}
|
|
14226
15182
|
function resolveConfigPaths() {
|
|
@@ -14249,11 +15205,11 @@ function findServerEntry(servers, provider2) {
|
|
|
14249
15205
|
function findProviderMcpConfig(provider2, configPaths) {
|
|
14250
15206
|
const paths = configPaths ?? resolveConfigPaths();
|
|
14251
15207
|
for (const path of paths) {
|
|
14252
|
-
if (!(0,
|
|
15208
|
+
if (!(0, import_node_fs13.existsSync)(path)) {
|
|
14253
15209
|
continue;
|
|
14254
15210
|
}
|
|
14255
15211
|
try {
|
|
14256
|
-
const raw = JSON.parse((0,
|
|
15212
|
+
const raw = JSON.parse((0, import_node_fs13.readFileSync)(path, "utf8"));
|
|
14257
15213
|
const servers = parseMcpServers(raw);
|
|
14258
15214
|
if (!servers) {
|
|
14259
15215
|
continue;
|
|
@@ -14323,8 +15279,11 @@ Usage:
|
|
|
14323
15279
|
|
|
14324
15280
|
viberaven status
|
|
14325
15281
|
|
|
15282
|
+
viberaven actions [--json] [path]
|
|
15283
|
+
Print current chat-native production action surface
|
|
15284
|
+
|
|
14326
15285
|
viberaven preview [--agent-mode] [--json]
|
|
14327
|
-
Local
|
|
15286
|
+
Local chat-native action preview for videos and onboarding; no login or API spend
|
|
14328
15287
|
|
|
14329
15288
|
viberaven connect --session <id> --token <token> [--once] [--api-url <url>]
|
|
14330
15289
|
Handshake, save runner session, then watch for jobs (Ctrl+C to stop)
|
|
@@ -14403,6 +15362,12 @@ Security:
|
|
|
14403
15362
|
|
|
14404
15363
|
`);
|
|
14405
15364
|
}
|
|
15365
|
+
function resolveCliWorkspacePath(input) {
|
|
15366
|
+
if (!input) {
|
|
15367
|
+
return process.cwd();
|
|
15368
|
+
}
|
|
15369
|
+
return (0, import_node_path31.isAbsolute)(input) ? input : (0, import_node_path31.resolve)(process.cwd(), input);
|
|
15370
|
+
}
|
|
14406
15371
|
function parseArgs(argv) {
|
|
14407
15372
|
const flags = {};
|
|
14408
15373
|
const positional = [];
|
|
@@ -14458,7 +15423,7 @@ function isBooleanFlag(command, key) {
|
|
|
14458
15423
|
return true;
|
|
14459
15424
|
}
|
|
14460
15425
|
if (key === "strict") return true;
|
|
14461
|
-
if (key === "open" && (command === "" || command === "scan" || command === "report")) return true;
|
|
15426
|
+
if (key === "open" && (command === "" || command === "scan" || command === "report" || command === "console")) return true;
|
|
14462
15427
|
if (key === "verify" && command === "") return true;
|
|
14463
15428
|
if (key === "vercel-supabase" && command === "audit") return true;
|
|
14464
15429
|
if (key === "json" && command === "validate-npm-package") return true;
|
|
@@ -14480,7 +15445,7 @@ async function guardEarlyVerifyScan(input) {
|
|
|
14480
15445
|
if (!verifyLike) {
|
|
14481
15446
|
return void 0;
|
|
14482
15447
|
}
|
|
14483
|
-
const workspacePath = input.positional[0] ? (0,
|
|
15448
|
+
const workspacePath = input.positional[0] ? (0, import_node_path31.join)(process.cwd(), input.positional[0]) : await resolveWorkspaceRoot(process.cwd());
|
|
14484
15449
|
const loopState = await loadLoopState(workspacePath);
|
|
14485
15450
|
if (loopState.batchApplied <= 0) {
|
|
14486
15451
|
return void 0;
|
|
@@ -14529,8 +15494,8 @@ async function cmdLogout() {
|
|
|
14529
15494
|
}
|
|
14530
15495
|
async function cmdProviderVerify(flags, positional) {
|
|
14531
15496
|
const provider2 = typeof flags.provider === "string" ? flags.provider : positional[0];
|
|
14532
|
-
const
|
|
14533
|
-
if (!provider2 || !
|
|
15497
|
+
const check = typeof flags.check === "string" ? flags.check : positional[1];
|
|
15498
|
+
if (!provider2 || !check) {
|
|
14534
15499
|
console.error("Usage: viberaven provider-verify --provider <supabase|vercel> --check <id> [--plan free|pro]");
|
|
14535
15500
|
return 1;
|
|
14536
15501
|
}
|
|
@@ -14543,7 +15508,7 @@ async function cmdProviderVerify(flags, positional) {
|
|
|
14543
15508
|
}
|
|
14544
15509
|
const result = await verifyProviderGap({
|
|
14545
15510
|
provider: provider2,
|
|
14546
|
-
check
|
|
15511
|
+
check,
|
|
14547
15512
|
cwd: process.cwd(),
|
|
14548
15513
|
plan
|
|
14549
15514
|
});
|
|
@@ -14556,7 +15521,7 @@ async function cmdStatus(flags, positional) {
|
|
|
14556
15521
|
console.log("Not signed in. Run: viberaven login");
|
|
14557
15522
|
return 1;
|
|
14558
15523
|
}
|
|
14559
|
-
const startDir = positional[0] ? (0,
|
|
15524
|
+
const startDir = positional[0] ? (0, import_node_path31.join)(process.cwd(), positional[0]) : process.cwd();
|
|
14560
15525
|
let artifact;
|
|
14561
15526
|
try {
|
|
14562
15527
|
artifact = await loadLastArtifact(startDir);
|
|
@@ -14710,7 +15675,7 @@ async function cmdWatch(flags) {
|
|
|
14710
15675
|
}
|
|
14711
15676
|
}
|
|
14712
15677
|
async function runScanCommand(flags, positional, options) {
|
|
14713
|
-
const workspacePath = positional[0] ? (0,
|
|
15678
|
+
const workspacePath = positional[0] ? (0, import_node_path31.join)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
|
|
14714
15679
|
const apiBaseUrl = resolveApiBaseUrl(typeof flags["api-url"] === "string" ? flags["api-url"] : void 0);
|
|
14715
15680
|
let accessToken;
|
|
14716
15681
|
try {
|
|
@@ -14776,6 +15741,12 @@ async function runScanCommand(flags, positional, options) {
|
|
|
14776
15741
|
const block = buildNextActionBlock(tasks, updatedState, plan);
|
|
14777
15742
|
printNextActionBlock(block);
|
|
14778
15743
|
printProviderActionBlock(tasks);
|
|
15744
|
+
try {
|
|
15745
|
+
const manifest = JSON.parse(await (0, import_promises25.readFile)(paths.actionsPath, "utf8"));
|
|
15746
|
+
console.log(renderActionSurface(manifest, { limit: 5 }).trimEnd());
|
|
15747
|
+
} catch (error) {
|
|
15748
|
+
console.warn(error instanceof Error ? error.message : String(error));
|
|
15749
|
+
}
|
|
14779
15750
|
await saveLoopState(workspacePath, updatedState);
|
|
14780
15751
|
}
|
|
14781
15752
|
if (flags.open) {
|
|
@@ -14788,7 +15759,7 @@ async function runScanCommand(flags, positional, options) {
|
|
|
14788
15759
|
return { exitCode: 0, artifacts: paths };
|
|
14789
15760
|
}
|
|
14790
15761
|
async function cmdReport(flags, positional) {
|
|
14791
|
-
const startDir = positional[0] ? (0,
|
|
15762
|
+
const startDir = positional[0] ? (0, import_node_path31.join)(process.cwd(), positional[0]) : process.cwd();
|
|
14792
15763
|
try {
|
|
14793
15764
|
const paths = await refreshReportFromDisk(startDir);
|
|
14794
15765
|
console.log(`Report refreshed: ${paths.reportPath}`);
|
|
@@ -14810,7 +15781,7 @@ async function cmdReport(flags, positional) {
|
|
|
14810
15781
|
}
|
|
14811
15782
|
}
|
|
14812
15783
|
async function cmdPrompt(flags, positional) {
|
|
14813
|
-
const startDir = positional[0] ? (0,
|
|
15784
|
+
const startDir = positional[0] ? (0, import_node_path31.join)(process.cwd(), positional[0]) : process.cwd();
|
|
14814
15785
|
let artifact;
|
|
14815
15786
|
try {
|
|
14816
15787
|
artifact = await loadLastArtifact(startDir);
|
|
@@ -14908,7 +15879,7 @@ async function main() {
|
|
|
14908
15879
|
const wantsJsonl = hasFlag(flags, "jsonl");
|
|
14909
15880
|
const wantsStrict = hasFlag(flags, "strict");
|
|
14910
15881
|
if (flags.condense) {
|
|
14911
|
-
const cwd = positional[0] ? (0,
|
|
15882
|
+
const cwd = positional[0] ? (0, import_node_path31.join)(process.cwd(), positional[0]) : process.cwd();
|
|
14912
15883
|
const result = await runCondenseCommand({ cwd });
|
|
14913
15884
|
console.log(`VibeRaven context map refreshed: ${result.contextMapPath}`);
|
|
14914
15885
|
return 0;
|
|
@@ -14926,6 +15897,18 @@ async function main() {
|
|
|
14926
15897
|
console.log(JSON.stringify(result, null, 2));
|
|
14927
15898
|
return result.status.startsWith("refused") || result.status === "failed" ? 1 : 0;
|
|
14928
15899
|
}
|
|
15900
|
+
if (!command && flags.verify === true && typeof flags.action === "string") {
|
|
15901
|
+
return runVerifyActionCommand({
|
|
15902
|
+
cwd: positional[0] ? (0, import_node_path31.join)(process.cwd(), positional[0]) : process.cwd(),
|
|
15903
|
+
actionId: flags.action
|
|
15904
|
+
});
|
|
15905
|
+
}
|
|
15906
|
+
if (command === "verify" && typeof flags.action === "string") {
|
|
15907
|
+
return runVerifyActionCommand({
|
|
15908
|
+
cwd: positional[0] ? (0, import_node_path31.join)(process.cwd(), positional[0]) : process.cwd(),
|
|
15909
|
+
actionId: flags.action
|
|
15910
|
+
});
|
|
15911
|
+
}
|
|
14929
15912
|
if (!command && (isAgentMode || flags.verify === true || wantsJson || wantsJsonl || wantsStrict)) {
|
|
14930
15913
|
const guardedExitCode = await guardEarlyVerifyScan({ flags, positional, wantsStrict });
|
|
14931
15914
|
if (guardedExitCode !== void 0) {
|
|
@@ -14937,7 +15920,7 @@ async function main() {
|
|
|
14937
15920
|
console.error("VibeRaven could not produce machine output because scan artifacts were not written.");
|
|
14938
15921
|
return 3;
|
|
14939
15922
|
}
|
|
14940
|
-
const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0,
|
|
15923
|
+
const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises25.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
|
|
14941
15924
|
const strictExitCode = wantsStrict && gateResult ? exitCodeForStrictGate(gateResult, { failOnWarnings: flags.strict === "warning" }) : scanResult.exitCode;
|
|
14942
15925
|
if (wantsJson && gateResult) {
|
|
14943
15926
|
process.stdout.write(renderGateResultJson(gateResult));
|
|
@@ -14969,16 +15952,33 @@ async function main() {
|
|
|
14969
15952
|
return 0;
|
|
14970
15953
|
case "status":
|
|
14971
15954
|
return cmdStatus(flags, positional);
|
|
15955
|
+
case "actions":
|
|
15956
|
+
return runActionsCommand({
|
|
15957
|
+
cwd: resolveCliWorkspacePath(positional[0]),
|
|
15958
|
+
json: Boolean(flags.json)
|
|
15959
|
+
});
|
|
14972
15960
|
case "preview":
|
|
14973
15961
|
return runPreviewCommand({
|
|
14974
|
-
cwd:
|
|
15962
|
+
cwd: resolveCliWorkspacePath(positional[0]),
|
|
14975
15963
|
agentMode: flags["agent-mode"] === true,
|
|
14976
15964
|
json: Boolean(flags.json)
|
|
14977
15965
|
});
|
|
15966
|
+
case "console":
|
|
15967
|
+
if (process.env.VIBERAVEN_EXPERIMENTAL_CONSOLE !== "1") {
|
|
15968
|
+
console.error("The local VibeRaven Console is experimental and hidden from the public npm release.");
|
|
15969
|
+
console.error("Use chat-native actions instead: viberaven actions");
|
|
15970
|
+
console.error("To preview the experimental console, set VIBERAVEN_EXPERIMENTAL_CONSOLE=1.");
|
|
15971
|
+
return 1;
|
|
15972
|
+
}
|
|
15973
|
+
return runConsoleCliCommand({
|
|
15974
|
+
cwd: resolveCliWorkspacePath(positional[0]),
|
|
15975
|
+
port: typeof flags.port === "string" ? Number.parseInt(flags.port, 10) : void 0,
|
|
15976
|
+
open: flags.open === true
|
|
15977
|
+
});
|
|
14978
15978
|
case "next":
|
|
14979
15979
|
return runNextCommand({
|
|
14980
15980
|
json: Boolean(flags.json),
|
|
14981
|
-
cwd: positional[0] ? (0,
|
|
15981
|
+
cwd: positional[0] ? (0, import_node_path31.join)(process.cwd(), positional[0]) : process.cwd()
|
|
14982
15982
|
});
|
|
14983
15983
|
case "guide": {
|
|
14984
15984
|
const provider2 = positional[0];
|
|
@@ -15016,7 +16016,7 @@ async function main() {
|
|
|
15016
16016
|
case "provider-verify":
|
|
15017
16017
|
return cmdProviderVerify(flags, positional);
|
|
15018
16018
|
case "init": {
|
|
15019
|
-
const cwd = positional[0] ? (0,
|
|
16019
|
+
const cwd = positional[0] ? (0, import_node_path31.join)(process.cwd(), positional[0]) : process.cwd();
|
|
15020
16020
|
const agents = typeof flags.agents === "string" ? flags.agents : void 0;
|
|
15021
16021
|
return runInitCommand({
|
|
15022
16022
|
cwd,
|
|
@@ -15030,7 +16030,7 @@ async function main() {
|
|
|
15030
16030
|
return 1;
|
|
15031
16031
|
}
|
|
15032
16032
|
return runDoctorAgentsCommand({
|
|
15033
|
-
cwd: positional[0] ? (0,
|
|
16033
|
+
cwd: positional[0] ? (0, import_node_path31.join)(process.cwd(), positional[0]) : process.cwd()
|
|
15034
16034
|
});
|
|
15035
16035
|
case "validate-npm-package":
|
|
15036
16036
|
return runValidateNpmPackageCommand({
|
|
@@ -15043,7 +16043,7 @@ async function main() {
|
|
|
15043
16043
|
return 1;
|
|
15044
16044
|
}
|
|
15045
16045
|
return runAuditCommand({
|
|
15046
|
-
cwd: positional[0] ? (0,
|
|
16046
|
+
cwd: positional[0] ? (0, import_node_path31.join)(process.cwd(), positional[0]) : process.cwd(),
|
|
15047
16047
|
json: Boolean(flags.json)
|
|
15048
16048
|
});
|
|
15049
16049
|
default:
|