@viberaven/cli 1.5.3 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +770 -350
- package/dist/stack/app.js +16 -16
- package/dist/stack/stack.css +154 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -6970,7 +6970,7 @@ var init_mockSupabaseVerifier = __esm({
|
|
|
6970
6970
|
providerObservationMet: false,
|
|
6971
6971
|
fixType: "mcp-connect",
|
|
6972
6972
|
severity: tables.length > 0 ? "critical" : "info",
|
|
6973
|
-
repoSignals: tables.map((
|
|
6973
|
+
repoSignals: tables.map((table2) => `table referenced: ${table2}`),
|
|
6974
6974
|
providerSignals: ["Supabase policy list API or MCP"],
|
|
6975
6975
|
requiredEvidence: ["Policies exist for tables used in application code"]
|
|
6976
6976
|
}),
|
|
@@ -6986,7 +6986,7 @@ var init_mockSupabaseVerifier = __esm({
|
|
|
6986
6986
|
providerObservationMet: false,
|
|
6987
6987
|
fixType: "provider-config",
|
|
6988
6988
|
severity: "warning",
|
|
6989
|
-
repoSignals: tables.map((
|
|
6989
|
+
repoSignals: tables.map((table2) => `.from("${table2}")`),
|
|
6990
6990
|
providerSignals: ["Live table policy map"],
|
|
6991
6991
|
requiredEvidence: ["Each referenced public table has RLS + policy in production"]
|
|
6992
6992
|
})
|
|
@@ -7008,7 +7008,7 @@ var init_mockSupabaseVerifier = __esm({
|
|
|
7008
7008
|
providerActual: "Live RLS state unknown without Supabase MCP",
|
|
7009
7009
|
severity: "critical",
|
|
7010
7010
|
suggestedFix: "repo-fix",
|
|
7011
|
-
evidenceRefs: tables.map((
|
|
7011
|
+
evidenceRefs: tables.map((table2) => `table: ${table2}`)
|
|
7012
7012
|
});
|
|
7013
7013
|
}
|
|
7014
7014
|
if (rlsInRepo) {
|
|
@@ -8303,7 +8303,7 @@ function replayProject(files, root) {
|
|
|
8303
8303
|
}
|
|
8304
8304
|
}
|
|
8305
8305
|
const replayed = [...tables.values()];
|
|
8306
|
-
for (const
|
|
8306
|
+
for (const table2 of replayed) table2.root = root;
|
|
8307
8307
|
return { tables: replayed, functions: [...functions.values()], statementsSeen };
|
|
8308
8308
|
}
|
|
8309
8309
|
function isSqlTestPath(path) {
|
|
@@ -8574,38 +8574,38 @@ function analyzeRls(replay) {
|
|
|
8574
8574
|
const noPolicies = [];
|
|
8575
8575
|
const permissive = [];
|
|
8576
8576
|
const rlsEnabledEvidence = [];
|
|
8577
|
-
for (const
|
|
8578
|
-
if (
|
|
8579
|
-
if (
|
|
8580
|
-
rlsEnabledEvidence.push(`${at(
|
|
8577
|
+
for (const table2 of replay.tables) {
|
|
8578
|
+
if (table2.schema !== "public") continue;
|
|
8579
|
+
if (table2.rls) {
|
|
8580
|
+
rlsEnabledEvidence.push(`${at(table2.rlsLoc)}: row level security enabled on ${safe(table2.display)}`);
|
|
8581
8581
|
}
|
|
8582
|
-
if (!
|
|
8583
|
-
const where =
|
|
8582
|
+
if (!table2.rls && (table2.created || table2.disabledLoc)) {
|
|
8583
|
+
const where = table2.disabledLoc ?? table2.arrivedLoc ?? table2.created;
|
|
8584
8584
|
const risk = "Anyone holding the anon key your frontend ships can read and change every row through the Data API (unless the table's grants were revoked).";
|
|
8585
|
-
const message =
|
|
8585
|
+
const message = table2.disabledLoc ? `${table2.display} has row level security turned off at ${at(table2.disabledLoc)}. ${risk}` : table2.arrivedLoc ? `${table2.display} is moved into public at ${at(table2.arrivedLoc)} without row level security, and no later migration enables it. ${risk}` : `${table2.display} is created at ${at(table2.created)} and no migration enables row level security on it. ${risk}`;
|
|
8586
8586
|
missingRls.push({
|
|
8587
8587
|
id: "rls_disabled",
|
|
8588
8588
|
severity: "critical",
|
|
8589
8589
|
message: safe(message),
|
|
8590
8590
|
file: where?.file,
|
|
8591
8591
|
line: where?.line,
|
|
8592
|
-
table:
|
|
8593
|
-
fix: `alter table ${
|
|
8592
|
+
table: table2.display,
|
|
8593
|
+
fix: `alter table ${table2.display} enable row level security;`
|
|
8594
8594
|
});
|
|
8595
8595
|
}
|
|
8596
|
-
if (
|
|
8596
|
+
if (table2.rls && table2.policies.size === 0) {
|
|
8597
8597
|
noPolicies.push({
|
|
8598
8598
|
id: "rls_no_policies",
|
|
8599
8599
|
severity: "info",
|
|
8600
8600
|
message: safe(
|
|
8601
|
-
`${
|
|
8601
|
+
`${table2.display} has row level security on and no policies, so it is locked to the service role: the browser client gets no rows back and cannot write. Fine for server-only tables; add policies if the app reads it from the frontend.`
|
|
8602
8602
|
),
|
|
8603
|
-
file:
|
|
8604
|
-
line:
|
|
8605
|
-
table:
|
|
8603
|
+
file: table2.rlsLoc?.file,
|
|
8604
|
+
line: table2.rlsLoc?.line,
|
|
8605
|
+
table: table2.display
|
|
8606
8606
|
});
|
|
8607
8607
|
}
|
|
8608
|
-
for (const policy of
|
|
8608
|
+
for (const policy of table2.policies.values()) {
|
|
8609
8609
|
if (!policy.permissive) continue;
|
|
8610
8610
|
const roles = policy.roles.filter((r) => r === "public" || r === "anon" || r === "authenticated");
|
|
8611
8611
|
if (roles.length === 0) continue;
|
|
@@ -8618,11 +8618,11 @@ function analyzeRls(replay) {
|
|
|
8618
8618
|
id: "rls_policy_allows_all_read",
|
|
8619
8619
|
severity: "high",
|
|
8620
8620
|
message: safe(
|
|
8621
|
-
`Policy "${policy.name}" on ${
|
|
8621
|
+
`Policy "${policy.name}" on ${table2.display} (${at(policy.loc)}) lets ${who} read every row: using (true). Fine for truly public content; wrong for anything per-user.`
|
|
8622
8622
|
),
|
|
8623
8623
|
file: policy.loc.file,
|
|
8624
8624
|
line: policy.loc.line,
|
|
8625
|
-
table:
|
|
8625
|
+
table: table2.display,
|
|
8626
8626
|
policy: policy.name
|
|
8627
8627
|
});
|
|
8628
8628
|
continue;
|
|
@@ -8650,10 +8650,10 @@ function analyzeRls(replay) {
|
|
|
8650
8650
|
permissive.push({
|
|
8651
8651
|
id: "rls_policy_allows_all_write",
|
|
8652
8652
|
severity,
|
|
8653
|
-
message: safe(`Policy "${policy.name}" on ${
|
|
8653
|
+
message: safe(`Policy "${policy.name}" on ${table2.display} (${at(policy.loc)}) lets ${who} ${verb}: ${clause}.`),
|
|
8654
8654
|
file: policy.loc.file,
|
|
8655
8655
|
line: policy.loc.line,
|
|
8656
|
-
table:
|
|
8656
|
+
table: table2.display,
|
|
8657
8657
|
policy: policy.name
|
|
8658
8658
|
});
|
|
8659
8659
|
}
|
|
@@ -8733,35 +8733,35 @@ function analyzeGrants(replay, uses, autoExpose) {
|
|
|
8733
8733
|
if (existing) use.ops.forEach((op) => existing.ops.add(op));
|
|
8734
8734
|
else byName.set(use.table, { ...use, ops: new Set(use.ops) });
|
|
8735
8735
|
}
|
|
8736
|
-
for (const
|
|
8737
|
-
if (
|
|
8738
|
-
const granted =
|
|
8739
|
-
if (granted && !
|
|
8740
|
-
const roles2 = [...new Set([...
|
|
8736
|
+
for (const table2 of replay.tables) {
|
|
8737
|
+
if (table2.schema !== "public") continue;
|
|
8738
|
+
const granted = table2.grants.size > 0;
|
|
8739
|
+
if (granted && !table2.rls) {
|
|
8740
|
+
const roles2 = [...new Set([...table2.grants].map((g2) => g2.split(":")[0]))].join(" and ");
|
|
8741
8741
|
out.push({
|
|
8742
8742
|
id: "data_api_grant_without_rls",
|
|
8743
8743
|
severity: "critical",
|
|
8744
8744
|
message: safe(
|
|
8745
|
-
`${
|
|
8745
|
+
`${table2.display} is granted to ${roles2} (${at(table2.grantLoc)}) but row level security is off. A grant without RLS is the dangerous combination: the Data API serves every row to that role. Enable RLS and add policies before granting.`
|
|
8746
8746
|
),
|
|
8747
|
-
file:
|
|
8748
|
-
line:
|
|
8749
|
-
table:
|
|
8750
|
-
fix: `alter table ${
|
|
8747
|
+
file: table2.grantLoc?.file,
|
|
8748
|
+
line: table2.grantLoc?.line,
|
|
8749
|
+
table: table2.display,
|
|
8750
|
+
fix: `alter table ${table2.display} enable row level security;`
|
|
8751
8751
|
});
|
|
8752
8752
|
continue;
|
|
8753
8753
|
}
|
|
8754
|
-
if (!
|
|
8754
|
+
if (!table2.created || !table2.rls) continue;
|
|
8755
8755
|
const configOff = autoExpose && !autoExpose.value ? autoExpose : void 0;
|
|
8756
|
-
const revokedAtCreate =
|
|
8756
|
+
const revokedAtCreate = table2.defaultsRevoked ?? /* @__PURE__ */ new Set();
|
|
8757
8757
|
if (revokedAtCreate.size === 0 && !configOff) continue;
|
|
8758
|
-
const use = byName.get(
|
|
8758
|
+
const use = byName.get(table2.name);
|
|
8759
8759
|
if (!use) continue;
|
|
8760
|
-
const lacks = (role, priv) => !
|
|
8760
|
+
const lacks = (role, priv) => !table2.grants.has(`${role}:${priv}`) && (configOff !== void 0 || revokedAtCreate.has(`${role}:${priv}`));
|
|
8761
8761
|
const rolesFor = (priv) => {
|
|
8762
|
-
if (!
|
|
8762
|
+
if (!table2.rls) return [...DATA_API_ROLES];
|
|
8763
8763
|
return expandRoles(
|
|
8764
|
-
[...
|
|
8764
|
+
[...table2.policies.values()].filter((p2) => p2.permissive && (p2.command === priv || p2.command === "all")).flatMap((p2) => p2.roles)
|
|
8765
8765
|
);
|
|
8766
8766
|
};
|
|
8767
8767
|
const blocked = /* @__PURE__ */ new Map();
|
|
@@ -8773,17 +8773,17 @@ function analyzeGrants(replay, uses, autoExpose) {
|
|
|
8773
8773
|
if (blocked.size === 0) continue;
|
|
8774
8774
|
const privs = [...blocked.keys()];
|
|
8775
8775
|
const roles = [...new Set([...blocked.values()].flat())];
|
|
8776
|
-
const reason = configOff ? `${configOff.file}:${configOff.line} sets auto_expose_new_tables = false, so new public tables get no default grants` : `the default grants for new public tables are revoked at ${at(
|
|
8777
|
-
const fix = `grant ${privs.join(", ")} on table ${
|
|
8776
|
+
const reason = configOff ? `${configOff.file}:${configOff.line} sets auto_expose_new_tables = false, so new public tables get no default grants` : `the default grants for new public tables are revoked at ${at(table2.defaultsRevokedLoc)}`;
|
|
8777
|
+
const fix = `grant ${privs.join(", ")} on table ${table2.display} to ${roles.join(", ")};`;
|
|
8778
8778
|
out.push({
|
|
8779
8779
|
id: "data_api_grant_missing",
|
|
8780
8780
|
severity: "warning",
|
|
8781
8781
|
message: safe(
|
|
8782
|
-
`${
|
|
8782
|
+
`${table2.display} is used from the client (${use.file}:${use.line}) for ${privs.join(", ")}, but ${roles.join(" and ")} ${roles.length === 1 ? "has" : "have"} no ${privs.join("/")} grant on it: ${reason}, and no migration grants it back. Those calls get "permission denied for table ${table2.name}". Add to a migration: ${fix}`
|
|
8783
8783
|
),
|
|
8784
|
-
file:
|
|
8785
|
-
line:
|
|
8786
|
-
table:
|
|
8784
|
+
file: table2.created.file,
|
|
8785
|
+
line: table2.created.line,
|
|
8786
|
+
table: table2.display,
|
|
8787
8787
|
fix
|
|
8788
8788
|
});
|
|
8789
8789
|
}
|
|
@@ -10291,10 +10291,10 @@ async function listTrackedIgnoredFiles(workspacePath) {
|
|
|
10291
10291
|
return { paths: [], incomplete: false };
|
|
10292
10292
|
}
|
|
10293
10293
|
try {
|
|
10294
|
-
const
|
|
10295
|
-
const { stdout } = await execFileAsync2("git",
|
|
10294
|
+
const git2 = await safeGitInvocation(workspacePath, ["ls-files", "-c", "-i", "--exclude-standard", "-z"]);
|
|
10295
|
+
const { stdout } = await execFileAsync2("git", git2.args, {
|
|
10296
10296
|
cwd: workspacePath,
|
|
10297
|
-
env:
|
|
10297
|
+
env: git2.env,
|
|
10298
10298
|
timeout: GIT_TIMEOUT_MS,
|
|
10299
10299
|
maxBuffer: GIT_MAX_OUTPUT_BYTES,
|
|
10300
10300
|
windowsHide: true
|
|
@@ -10318,14 +10318,14 @@ async function collectDatabaseEvidence(workspacePath, scan) {
|
|
|
10318
10318
|
[...scan.fileTree.split("\n"), ...scan.secretsFound, ...walk.listed, ...trackedIgnored.paths].map((p2) => normalizedPath(p2.trim()))
|
|
10319
10319
|
)
|
|
10320
10320
|
].filter((p2) => p2.length > 0 && !isOwnArtifact(p2) && !fullyLoaded.has(p2));
|
|
10321
|
-
const
|
|
10321
|
+
const wanted2 = [
|
|
10322
10322
|
...candidates.filter((p2) => EVIDENCE_MANIFEST.test(p2)),
|
|
10323
10323
|
...candidates.filter((p2) => !EVIDENCE_MANIFEST.test(p2) && EVIDENCE_CODE.test(p2))
|
|
10324
10324
|
];
|
|
10325
|
-
if (
|
|
10325
|
+
if (wanted2.length > EVIDENCE_MAX_FILES) incomplete = true;
|
|
10326
10326
|
const files = [];
|
|
10327
10327
|
let bytesRead = 0;
|
|
10328
|
-
for (const path of
|
|
10328
|
+
for (const path of wanted2.slice(0, EVIDENCE_MAX_FILES)) {
|
|
10329
10329
|
if (bytesRead >= EVIDENCE_MAX_TOTAL_BYTES) {
|
|
10330
10330
|
incomplete = true;
|
|
10331
10331
|
break;
|
|
@@ -10669,8 +10669,8 @@ function noReleaseTagsSeed(commitCount) {
|
|
|
10669
10669
|
}
|
|
10670
10670
|
async function detectReleaseTagGap(workspacePath) {
|
|
10671
10671
|
try {
|
|
10672
|
-
const
|
|
10673
|
-
const [tags, count] = await Promise.all([
|
|
10672
|
+
const git2 = (args) => safeGitInvocation(workspacePath, args).then((call) => execFileAsync3("git", call.args, { cwd: workspacePath, env: call.env, windowsHide: true }));
|
|
10673
|
+
const [tags, count] = await Promise.all([git2(["tag", "--list"]), git2(["rev-list", "--count", "HEAD"])]);
|
|
10674
10674
|
const tagCount = tags.stdout.split("\n").filter((line) => line.trim().length > 0).length;
|
|
10675
10675
|
const commitCount = Number.parseInt(count.stdout.trim(), 10) || 0;
|
|
10676
10676
|
if (tagCount === 0 && commitCount >= 20) {
|
|
@@ -12420,7 +12420,7 @@ var init_agentSummary = __esm({
|
|
|
12420
12420
|
|
|
12421
12421
|
// src/heal/rlsMigrationTarget.ts
|
|
12422
12422
|
function rlsEnableStatements(tables) {
|
|
12423
|
-
return [...new Set(tables)].map((
|
|
12423
|
+
return [...new Set(tables)].map((table2) => `alter table ${table2} enable row level security;`);
|
|
12424
12424
|
}
|
|
12425
12425
|
function migrationStamp(now) {
|
|
12426
12426
|
return now.toISOString().replace(/\D/g, "").slice(0, 14);
|
|
@@ -13688,12 +13688,12 @@ function createAppWindowOpenCommands(target, platform = process.platform, env =
|
|
|
13688
13688
|
}
|
|
13689
13689
|
async function openWithSystemDefault(target) {
|
|
13690
13690
|
const { command, args, shell } = createOpenCommand(target);
|
|
13691
|
-
await new Promise((
|
|
13691
|
+
await new Promise((resolve10, reject) => {
|
|
13692
13692
|
const child = (0, import_node_child_process4.spawn)(command, args, { stdio: "ignore", shell });
|
|
13693
13693
|
child.on("error", reject);
|
|
13694
13694
|
child.on("exit", (code) => {
|
|
13695
13695
|
if (code === 0) {
|
|
13696
|
-
|
|
13696
|
+
resolve10();
|
|
13697
13697
|
} else {
|
|
13698
13698
|
reject(new Error(`Could not open browser (exit ${code ?? "unknown"}). Open manually: ${target}`));
|
|
13699
13699
|
}
|
|
@@ -13704,7 +13704,7 @@ async function spawnDetached(command) {
|
|
|
13704
13704
|
if (/^[A-Za-z]:[\\/]/.test(command.command) && !(0, import_node_fs8.existsSync)(command.command)) {
|
|
13705
13705
|
throw new Error(`Browser not found: ${command.command}`);
|
|
13706
13706
|
}
|
|
13707
|
-
await new Promise((
|
|
13707
|
+
await new Promise((resolve10, reject) => {
|
|
13708
13708
|
const child = (0, import_node_child_process4.spawn)(command.command, command.args, {
|
|
13709
13709
|
detached: true,
|
|
13710
13710
|
stdio: "ignore",
|
|
@@ -13712,7 +13712,7 @@ async function spawnDetached(command) {
|
|
|
13712
13712
|
});
|
|
13713
13713
|
child.once("spawn", () => {
|
|
13714
13714
|
child.unref();
|
|
13715
|
-
|
|
13715
|
+
resolve10();
|
|
13716
13716
|
});
|
|
13717
13717
|
child.once("error", reject);
|
|
13718
13718
|
});
|
|
@@ -13780,12 +13780,12 @@ async function copyToClipboard(text) {
|
|
|
13780
13780
|
}
|
|
13781
13781
|
}
|
|
13782
13782
|
function pipeToCommand(command, text, extraArgs = []) {
|
|
13783
|
-
return new Promise((
|
|
13783
|
+
return new Promise((resolve10, reject) => {
|
|
13784
13784
|
const child = (0, import_node_child_process5.spawn)(command, extraArgs, { stdio: ["pipe", "ignore", "ignore"] });
|
|
13785
13785
|
child.on("error", reject);
|
|
13786
13786
|
child.on("close", (code) => {
|
|
13787
13787
|
if (code === 0) {
|
|
13788
|
-
|
|
13788
|
+
resolve10();
|
|
13789
13789
|
} else {
|
|
13790
13790
|
reject(new Error(`${command} exited with code ${code ?? "unknown"}`));
|
|
13791
13791
|
}
|
|
@@ -14690,7 +14690,7 @@ var VERSION;
|
|
|
14690
14690
|
var init_version = __esm({
|
|
14691
14691
|
"src/version.ts"() {
|
|
14692
14692
|
"use strict";
|
|
14693
|
-
VERSION = "1.
|
|
14693
|
+
VERSION = "1.6.1";
|
|
14694
14694
|
}
|
|
14695
14695
|
});
|
|
14696
14696
|
|
|
@@ -17100,6 +17100,334 @@ var init_runInit = __esm({
|
|
|
17100
17100
|
}
|
|
17101
17101
|
});
|
|
17102
17102
|
|
|
17103
|
+
// src/commands/checkChange.ts
|
|
17104
|
+
function findingKey(finding) {
|
|
17105
|
+
return `${finding.id}\0${finding.title}`;
|
|
17106
|
+
}
|
|
17107
|
+
function findingSubjects(finding) {
|
|
17108
|
+
const line = (finding.detail.split("\n").find((part) => part.trim()) ?? "").replace(LOCATION_TEXT, " ");
|
|
17109
|
+
return [...new Set((line.match(SUBJECT) ?? []).map((subject) => subject.toLowerCase()))].sort();
|
|
17110
|
+
}
|
|
17111
|
+
function bySeverity(a, b3) {
|
|
17112
|
+
return SEVERITY_RANK3[a.severity] - SEVERITY_RANK3[b3.severity] || a.title.localeCompare(b3.title);
|
|
17113
|
+
}
|
|
17114
|
+
function compareFindings(base, head, ref, baseCommit) {
|
|
17115
|
+
const baseByKey = new Map(base.map((finding) => [findingKey(finding), finding]));
|
|
17116
|
+
const headKeys = new Set(head.map(findingKey));
|
|
17117
|
+
const introduced = [];
|
|
17118
|
+
const resolved = base.filter((finding) => !headKeys.has(findingKey(finding)));
|
|
17119
|
+
const unchanged = [];
|
|
17120
|
+
for (const finding of head) {
|
|
17121
|
+
const before = baseByKey.get(findingKey(finding));
|
|
17122
|
+
if (!before) {
|
|
17123
|
+
introduced.push(finding);
|
|
17124
|
+
continue;
|
|
17125
|
+
}
|
|
17126
|
+
const was = new Set(findingSubjects(before));
|
|
17127
|
+
const now = findingSubjects(finding);
|
|
17128
|
+
const added = now.filter((subject) => !was.has(subject));
|
|
17129
|
+
const removed = [...was].filter((subject) => !now.includes(subject));
|
|
17130
|
+
if (added.length) introduced.push({ ...finding, subjects: added });
|
|
17131
|
+
else unchanged.push(finding);
|
|
17132
|
+
if (removed.length) resolved.push({ ...before, subjects: removed });
|
|
17133
|
+
}
|
|
17134
|
+
return { base: ref, baseCommit, introduced: introduced.sort(bySeverity), resolved: resolved.sort(bySeverity), unchanged: unchanged.sort(bySeverity) };
|
|
17135
|
+
}
|
|
17136
|
+
async function git(cwd, args) {
|
|
17137
|
+
const call = await safeGitInvocation(cwd, args);
|
|
17138
|
+
const { stdout } = await execFileAsync4("git", call.args, { cwd, env: call.env, windowsHide: true, maxBuffer: 64 * 1024 * 1024 });
|
|
17139
|
+
return stdout;
|
|
17140
|
+
}
|
|
17141
|
+
async function resolveBaseCommit(cwd, ref) {
|
|
17142
|
+
let commit;
|
|
17143
|
+
try {
|
|
17144
|
+
commit = (await git(cwd, ["rev-parse", "--verify", "--end-of-options", `${ref}^{commit}`])).trim();
|
|
17145
|
+
} catch {
|
|
17146
|
+
throw new Error(
|
|
17147
|
+
`Could not find "${ref}" in this repository. Fetch it first (git fetch origin ${ref.replace(/^origin\//, "")}); in GitHub Actions, check out with fetch-depth: 0.`
|
|
17148
|
+
);
|
|
17149
|
+
}
|
|
17150
|
+
try {
|
|
17151
|
+
const mergeBase = (await git(cwd, ["merge-base", commit, "HEAD"])).trim();
|
|
17152
|
+
return mergeBase || commit;
|
|
17153
|
+
} catch {
|
|
17154
|
+
return commit;
|
|
17155
|
+
}
|
|
17156
|
+
}
|
|
17157
|
+
function parseTree(output) {
|
|
17158
|
+
const entries = [];
|
|
17159
|
+
for (const record of output.split("\0")) {
|
|
17160
|
+
if (!record) continue;
|
|
17161
|
+
const tab = record.indexOf(" ");
|
|
17162
|
+
const [mode, type, sha, size] = record.slice(0, tab).trim().split(/\s+/);
|
|
17163
|
+
entries.push({ mode, type, sha, size: Number.parseInt(size, 10) || 0, path: record.slice(tab + 1) });
|
|
17164
|
+
}
|
|
17165
|
+
return entries;
|
|
17166
|
+
}
|
|
17167
|
+
function wanted(entry) {
|
|
17168
|
+
if (entry.type !== "blob" || entry.mode !== "100644" && entry.mode !== "100755") return false;
|
|
17169
|
+
if (entry.size > MAX_BLOB_BYTES) return false;
|
|
17170
|
+
return !entry.path.split("/").some((segment) => SKIP_SEGMENTS.has(segment));
|
|
17171
|
+
}
|
|
17172
|
+
async function readBlobs(cwd, shas) {
|
|
17173
|
+
const blobs = /* @__PURE__ */ new Map();
|
|
17174
|
+
if (!shas.length) return blobs;
|
|
17175
|
+
const call = await safeGitInvocation(cwd, ["cat-file", "--batch"]);
|
|
17176
|
+
const output = await new Promise((resolveOutput, reject) => {
|
|
17177
|
+
const child = (0, import_node_child_process6.spawn)("git", call.args, { cwd, env: call.env, windowsHide: true });
|
|
17178
|
+
const chunks = [];
|
|
17179
|
+
let stderr = "";
|
|
17180
|
+
child.stdout.on("data", (chunk) => chunks.push(chunk));
|
|
17181
|
+
child.stderr.on("data", (chunk) => {
|
|
17182
|
+
stderr += chunk.toString();
|
|
17183
|
+
});
|
|
17184
|
+
child.on("error", reject);
|
|
17185
|
+
child.on("close", (code) => code === 0 ? resolveOutput(Buffer.concat(chunks)) : reject(new Error(stderr.trim() || `git cat-file exited with ${code}`)));
|
|
17186
|
+
child.stdin.end(`${shas.join("\n")}
|
|
17187
|
+
`);
|
|
17188
|
+
});
|
|
17189
|
+
let offset = 0;
|
|
17190
|
+
while (offset < output.length) {
|
|
17191
|
+
const newline = output.indexOf(10, offset);
|
|
17192
|
+
if (newline === -1) break;
|
|
17193
|
+
const [sha, type, sizeText] = output.subarray(offset, newline).toString().split(" ");
|
|
17194
|
+
offset = newline + 1;
|
|
17195
|
+
if (type === "missing" || sizeText === void 0) continue;
|
|
17196
|
+
const size = Number.parseInt(sizeText, 10);
|
|
17197
|
+
blobs.set(sha, output.subarray(offset, offset + size));
|
|
17198
|
+
offset += size + 1;
|
|
17199
|
+
}
|
|
17200
|
+
return blobs;
|
|
17201
|
+
}
|
|
17202
|
+
async function exportCommit(cwd, commit) {
|
|
17203
|
+
const entries = parseTree(await git(cwd, ["ls-tree", "-r", "-z", "-l", "--full-tree", commit])).filter(wanted);
|
|
17204
|
+
const blobs = await readBlobs(cwd, [...new Set(entries.map((entry) => entry.sha))]);
|
|
17205
|
+
const root = await (0, import_promises23.mkdtemp)((0, import_node_path30.join)((0, import_node_os.tmpdir)(), "viberaven-base-"));
|
|
17206
|
+
for (const entry of entries) {
|
|
17207
|
+
const target = (0, import_node_path30.resolve)(root, ...entry.path.split("/"));
|
|
17208
|
+
if (!target.startsWith(root + import_node_path30.sep)) continue;
|
|
17209
|
+
const content = blobs.get(entry.sha);
|
|
17210
|
+
if (!content) continue;
|
|
17211
|
+
await (0, import_promises23.mkdir)((0, import_node_path30.dirname)(target), { recursive: true });
|
|
17212
|
+
await (0, import_promises23.writeFile)(target, content);
|
|
17213
|
+
}
|
|
17214
|
+
return root;
|
|
17215
|
+
}
|
|
17216
|
+
async function checkChange(cwd, ref, head) {
|
|
17217
|
+
const baseCommit = await resolveBaseCommit(cwd, ref);
|
|
17218
|
+
const folder = await exportCommit(cwd, baseCommit);
|
|
17219
|
+
try {
|
|
17220
|
+
const result = await runProjectScan({ workspacePath: folder });
|
|
17221
|
+
if (!result.ok) throw new Error(`The check on ${ref} did not finish: ${result.message}`);
|
|
17222
|
+
return compareFindings(result.artifact.gaps, head, ref, baseCommit);
|
|
17223
|
+
} finally {
|
|
17224
|
+
await (0, import_promises23.rm)(folder, { recursive: true, force: true, maxRetries: 3, retryDelay: 200 }).catch(() => void 0);
|
|
17225
|
+
}
|
|
17226
|
+
}
|
|
17227
|
+
function cell(text) {
|
|
17228
|
+
return text.replace(/\r?\n/g, " ").replace(/\|/g, "\\|").trim();
|
|
17229
|
+
}
|
|
17230
|
+
function firstLine(detail) {
|
|
17231
|
+
const line = detail.split("\n").map((part) => part.trim()).find(Boolean) ?? "";
|
|
17232
|
+
return line.length > 180 ? `${line.slice(0, 177)}\u2026` : line;
|
|
17233
|
+
}
|
|
17234
|
+
function plural2(count, word) {
|
|
17235
|
+
return `${count} ${word}${count === 1 ? "" : "s"}`;
|
|
17236
|
+
}
|
|
17237
|
+
function table(findings) {
|
|
17238
|
+
return [
|
|
17239
|
+
"| | Finding | Check |",
|
|
17240
|
+
"|---|---|---|",
|
|
17241
|
+
...findings.map((finding) => {
|
|
17242
|
+
const why = finding.subjects?.length ? `New in this change: ${finding.subjects.join(", ")}` : firstLine(finding.detail);
|
|
17243
|
+
return `| ${DOT[finding.severity]} | **${cell(finding.title)}**${why ? `<br>${cell(why)}` : ""} | \`${cell(finding.id)}\` |`;
|
|
17244
|
+
})
|
|
17245
|
+
];
|
|
17246
|
+
}
|
|
17247
|
+
function fixedLabel(finding) {
|
|
17248
|
+
return `\u2705 ${cell(finding.title)}${finding.subjects?.length ? ` for ${cell(finding.subjects.join(", "))}` : ""} (\`${cell(finding.id)}\`)`;
|
|
17249
|
+
}
|
|
17250
|
+
function renderCheckMarkdown({ head, change, version, score }) {
|
|
17251
|
+
const lines = [COMMENT_MARKER];
|
|
17252
|
+
const sorted = [...head].sort(bySeverity);
|
|
17253
|
+
const critical = sorted.filter((finding) => finding.severity === "critical").length;
|
|
17254
|
+
const warnings = sorted.filter((finding) => finding.severity === "warning").length;
|
|
17255
|
+
if (change) {
|
|
17256
|
+
const newCritical = change.introduced.filter((finding) => finding.severity === "critical").length;
|
|
17257
|
+
const headline = newCritical ? `${plural2(newCritical, "new blocker")} in this change` : change.introduced.length ? `${plural2(change.introduced.length, "new finding")} in this change, no new blockers` : "No new findings in this change";
|
|
17258
|
+
lines.push(`### VibeRaven \xB7 ${headline}`, "");
|
|
17259
|
+
lines.push(`Compared with \`${change.base}\` (${change.baseCommit.slice(0, 7)}).${change.resolved.length ? ` This change fixes ${plural2(change.resolved.length, "finding")}.` : ""}`, "");
|
|
17260
|
+
if (change.introduced.length) lines.push(...table(change.introduced), "");
|
|
17261
|
+
if (change.resolved.length) {
|
|
17262
|
+
lines.push(`**Fixed by this change:** ${change.resolved.map(fixedLabel).join(" \xB7 ")}`, "");
|
|
17263
|
+
}
|
|
17264
|
+
if (change.unchanged.length) {
|
|
17265
|
+
lines.push(`<details><summary>Already in ${cell(change.base)} before this change (${change.unchanged.length})</summary>`, "", ...table(change.unchanged), "", "</details>", "");
|
|
17266
|
+
}
|
|
17267
|
+
const firstNew = change.introduced.find((finding) => finding.severity === "critical") ?? change.introduced[0];
|
|
17268
|
+
if (firstNew) lines.push(`Fix it with your coding agent: \`npx -y viberaven fix --gap ${firstNew.id}\`, or open the Studio with \`npx -y viberaven\`.`, "");
|
|
17269
|
+
} else {
|
|
17270
|
+
const headline = critical ? `${plural2(critical, "blocker")}, ${plural2(warnings, "warning")}` : `No blockers, ${plural2(warnings, "warning")}`;
|
|
17271
|
+
lines.push(`### VibeRaven \xB7 ${headline}`, "");
|
|
17272
|
+
if (sorted.length) lines.push(...table(sorted), "");
|
|
17273
|
+
else lines.push("No launch findings in the repo evidence.", "");
|
|
17274
|
+
}
|
|
17275
|
+
lines.push(
|
|
17276
|
+
`<sub>viberaven ${version}${typeof score === "number" ? ` \xB7 readiness ${score}/100` : ""} \xB7 checked where it ran, nothing was uploaded. Advice, not a gate: a clear result reads the repo, it is not a security audit.</sub>`
|
|
17277
|
+
);
|
|
17278
|
+
return `${lines.join("\n")}
|
|
17279
|
+
`;
|
|
17280
|
+
}
|
|
17281
|
+
var import_node_child_process6, import_promises23, import_node_os, import_node_path30, import_node_util4, execFileAsync4, MAX_BLOB_BYTES, SKIP_SEGMENTS, SEVERITY_RANK3, LOCATION_TEXT, SUBJECT, COMMENT_MARKER, DOT;
|
|
17282
|
+
var init_checkChange = __esm({
|
|
17283
|
+
"src/commands/checkChange.ts"() {
|
|
17284
|
+
"use strict";
|
|
17285
|
+
import_node_child_process6 = require("node:child_process");
|
|
17286
|
+
import_promises23 = require("node:fs/promises");
|
|
17287
|
+
import_node_os = require("node:os");
|
|
17288
|
+
import_node_path30 = require("node:path");
|
|
17289
|
+
import_node_util4 = require("node:util");
|
|
17290
|
+
init_runScan();
|
|
17291
|
+
init_safeGit();
|
|
17292
|
+
execFileAsync4 = (0, import_node_util4.promisify)(import_node_child_process6.execFile);
|
|
17293
|
+
MAX_BLOB_BYTES = 1e6;
|
|
17294
|
+
SKIP_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", ".git", ".next", ".turbo", ".vercel", ".output", "dist", "build", "coverage"]);
|
|
17295
|
+
SEVERITY_RANK3 = { critical: 0, warning: 1, info: 2 };
|
|
17296
|
+
LOCATION_TEXT = /[\w./\\-]+\.(?:sql|tsx?|jsx?|mjs|cjs|json|toml|ya?ml|env|prisma|md)(?::\d+)?/gi;
|
|
17297
|
+
SUBJECT = /\b[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_]*\b/gi;
|
|
17298
|
+
COMMENT_MARKER = "<!-- viberaven-check -->";
|
|
17299
|
+
DOT = { critical: "\u{1F534}", warning: "\u{1F7E1}", info: "\u26AA" };
|
|
17300
|
+
}
|
|
17301
|
+
});
|
|
17302
|
+
|
|
17303
|
+
// src/commands/initGithub.ts
|
|
17304
|
+
function renderWorkflow(version = VERSION) {
|
|
17305
|
+
return [
|
|
17306
|
+
"# VibeRaven checks every pull request in this repository's own GitHub Actions run.",
|
|
17307
|
+
"# The code is read on the runner and never uploaded. Written by `npx -y viberaven init --github`.",
|
|
17308
|
+
"name: VibeRaven",
|
|
17309
|
+
"",
|
|
17310
|
+
"on:",
|
|
17311
|
+
" pull_request:",
|
|
17312
|
+
"",
|
|
17313
|
+
"permissions:",
|
|
17314
|
+
" contents: read",
|
|
17315
|
+
" pull-requests: write",
|
|
17316
|
+
"",
|
|
17317
|
+
"jobs:",
|
|
17318
|
+
" check:",
|
|
17319
|
+
" name: Launch check",
|
|
17320
|
+
" runs-on: ubuntu-latest",
|
|
17321
|
+
" steps:",
|
|
17322
|
+
" - uses: actions/checkout@v4",
|
|
17323
|
+
" with:",
|
|
17324
|
+
" fetch-depth: 0",
|
|
17325
|
+
"",
|
|
17326
|
+
" - uses: actions/setup-node@v4",
|
|
17327
|
+
" with:",
|
|
17328
|
+
" node-version: 20",
|
|
17329
|
+
"",
|
|
17330
|
+
" - name: Check this change",
|
|
17331
|
+
" id: check",
|
|
17332
|
+
" env:",
|
|
17333
|
+
" BASE_REF: ${{ github.base_ref }}",
|
|
17334
|
+
" run: |",
|
|
17335
|
+
" code=0",
|
|
17336
|
+
` npx -y viberaven@${version} check --base "origin/$BASE_REF" --markdown > viberaven-check.md || code=$?`,
|
|
17337
|
+
' cat viberaven-check.md >> "$GITHUB_STEP_SUMMARY"',
|
|
17338
|
+
' echo "code=$code" >> "$GITHUB_OUTPUT"',
|
|
17339
|
+
"",
|
|
17340
|
+
" # Pull requests from forks get a read-only token: they keep the job summary and skip the comment.",
|
|
17341
|
+
" - name: Comment on the pull request",
|
|
17342
|
+
" if: github.event.pull_request.head.repo.full_name == github.repository",
|
|
17343
|
+
" env:",
|
|
17344
|
+
" GH_TOKEN: ${{ github.token }}",
|
|
17345
|
+
" PR: ${{ github.event.pull_request.number }}",
|
|
17346
|
+
" run: |",
|
|
17347
|
+
` id=$(gh api "repos/$GITHUB_REPOSITORY/issues/$PR/comments" --paginate --jq '.[] | select(.body | contains("${COMMENT_MARKER}")) | .id' | tail -n 1)`,
|
|
17348
|
+
' if [ -n "$id" ]; then',
|
|
17349
|
+
' gh api --method PATCH "repos/$GITHUB_REPOSITORY/issues/comments/$id" -F body=@viberaven-check.md > /dev/null',
|
|
17350
|
+
" else",
|
|
17351
|
+
' gh pr comment "$PR" --body-file viberaven-check.md',
|
|
17352
|
+
" fi",
|
|
17353
|
+
"",
|
|
17354
|
+
" # Advice, not a gate. To block merging when a change adds a launch blocker, uncomment this step",
|
|
17355
|
+
' # and mark "Launch check" as required in the branch protection settings.',
|
|
17356
|
+
" # - name: Fail on new blockers",
|
|
17357
|
+
" # if: steps.check.outputs.code == '1'",
|
|
17358
|
+
" # run: exit 1",
|
|
17359
|
+
""
|
|
17360
|
+
].join("\n");
|
|
17361
|
+
}
|
|
17362
|
+
async function hasGithubRemote(cwd) {
|
|
17363
|
+
try {
|
|
17364
|
+
const call = await safeGitInvocation(cwd, ["remote", "-v"]);
|
|
17365
|
+
const { stdout } = await execFileAsync5("git", call.args, { cwd, env: call.env, windowsHide: true });
|
|
17366
|
+
return /github\.com[:/]/i.test(stdout);
|
|
17367
|
+
} catch {
|
|
17368
|
+
return false;
|
|
17369
|
+
}
|
|
17370
|
+
}
|
|
17371
|
+
async function githubWorkflowStatus(cwd) {
|
|
17372
|
+
return { exists: (0, import_node_fs14.existsSync)((0, import_node_path31.join)(cwd, WORKFLOW_PATH)), path: DISPLAY_PATH, onGithub: await hasGithubRemote(cwd) };
|
|
17373
|
+
}
|
|
17374
|
+
async function writeGithubWorkflow(cwd, options = {}) {
|
|
17375
|
+
const target = (0, import_node_path31.join)(cwd, WORKFLOW_PATH);
|
|
17376
|
+
if ((0, import_node_fs14.existsSync)(target) && !options.force) return { written: false, status: await githubWorkflowStatus(cwd) };
|
|
17377
|
+
await (0, import_promises24.mkdir)((0, import_node_path31.dirname)(target), { recursive: true });
|
|
17378
|
+
await (0, import_promises24.writeFile)(target, renderWorkflow(), "utf8");
|
|
17379
|
+
return { written: true, status: await githubWorkflowStatus(cwd) };
|
|
17380
|
+
}
|
|
17381
|
+
async function runInitGithubCommand(options) {
|
|
17382
|
+
if (options.dryRun) {
|
|
17383
|
+
console.log(`Would write ${DISPLAY_PATH}:
|
|
17384
|
+
`);
|
|
17385
|
+
console.log(renderWorkflow());
|
|
17386
|
+
return 0;
|
|
17387
|
+
}
|
|
17388
|
+
const { written, status } = await writeGithubWorkflow(options.cwd, { force: options.force });
|
|
17389
|
+
if (!written) {
|
|
17390
|
+
console.log(`${DISPLAY_PATH} already exists. Nothing changed. Run with --force to replace it.`);
|
|
17391
|
+
return 0;
|
|
17392
|
+
}
|
|
17393
|
+
console.log(`Wrote ${DISPLAY_PATH}.`);
|
|
17394
|
+
console.log("");
|
|
17395
|
+
console.log("Commit it, and every pull request gets one VibeRaven comment with what the change added or fixed:");
|
|
17396
|
+
console.log(` git add ${DISPLAY_PATH} && git commit -m "Check pull requests with VibeRaven"`);
|
|
17397
|
+
console.log("");
|
|
17398
|
+
console.log("It runs in your own GitHub Actions, so private repositories work and the code is never uploaded.");
|
|
17399
|
+
if (!status.onGithub) console.log("This repository has no github.com remote yet: the workflow runs once it is pushed to GitHub.");
|
|
17400
|
+
return 0;
|
|
17401
|
+
}
|
|
17402
|
+
var import_node_child_process7, import_node_fs14, import_promises24, import_node_path31, import_node_util5, execFileAsync5, WORKFLOW_PATH, DISPLAY_PATH;
|
|
17403
|
+
var init_initGithub = __esm({
|
|
17404
|
+
"src/commands/initGithub.ts"() {
|
|
17405
|
+
"use strict";
|
|
17406
|
+
import_node_child_process7 = require("node:child_process");
|
|
17407
|
+
import_node_fs14 = require("node:fs");
|
|
17408
|
+
import_promises24 = require("node:fs/promises");
|
|
17409
|
+
import_node_path31 = require("node:path");
|
|
17410
|
+
import_node_util5 = require("node:util");
|
|
17411
|
+
init_safeGit();
|
|
17412
|
+
init_version();
|
|
17413
|
+
init_checkChange();
|
|
17414
|
+
execFileAsync5 = (0, import_node_util5.promisify)(import_node_child_process7.execFile);
|
|
17415
|
+
WORKFLOW_PATH = (0, import_node_path31.join)(".github", "workflows", "viberaven.yml");
|
|
17416
|
+
DISPLAY_PATH = ".github/workflows/viberaven.yml";
|
|
17417
|
+
}
|
|
17418
|
+
});
|
|
17419
|
+
|
|
17420
|
+
// src/local-ui/stack/setupLink.ts
|
|
17421
|
+
function setupFragment(ids) {
|
|
17422
|
+
const safe2 = [...new Set(ids.filter((id) => /^[a-z][a-z0-9-]{0,39}$/.test(id)))].slice(0, 30);
|
|
17423
|
+
return `#${new URLSearchParams({ stack: safe2.join(",") })}`;
|
|
17424
|
+
}
|
|
17425
|
+
var init_setupLink = __esm({
|
|
17426
|
+
"src/local-ui/stack/setupLink.ts"() {
|
|
17427
|
+
"use strict";
|
|
17428
|
+
}
|
|
17429
|
+
});
|
|
17430
|
+
|
|
17103
17431
|
// src/commands/doctorAgents.ts
|
|
17104
17432
|
function managedBlocks(content) {
|
|
17105
17433
|
const blocks = content.match(/<!-- (?:VIBERAVEN:START|viberaven:agent-rules:start) -->[\s\S]*?<!-- (?:VIBERAVEN:END|viberaven:agent-rules:end) -->/g);
|
|
@@ -17108,7 +17436,7 @@ function managedBlocks(content) {
|
|
|
17108
17436
|
async function checkAgentInjection(cwd) {
|
|
17109
17437
|
const checks = [];
|
|
17110
17438
|
for (const item4 of REQUIRED_EXISTENCE_CHECKS) {
|
|
17111
|
-
const exists = await fileExists2((0,
|
|
17439
|
+
const exists = await fileExists2((0, import_node_path32.join)(cwd, item4.file));
|
|
17112
17440
|
checks.push({
|
|
17113
17441
|
id: item4.id,
|
|
17114
17442
|
status: exists ? "pass" : "fail",
|
|
@@ -17116,17 +17444,17 @@ async function checkAgentInjection(cwd) {
|
|
|
17116
17444
|
});
|
|
17117
17445
|
}
|
|
17118
17446
|
for (const item4 of OPTIONAL_CURSOR_PACK_CHECKS) {
|
|
17119
|
-
const exists = await fileExists2((0,
|
|
17447
|
+
const exists = await fileExists2((0, import_node_path32.join)(cwd, item4.file));
|
|
17120
17448
|
checks.push({
|
|
17121
17449
|
id: item4.id,
|
|
17122
17450
|
status: exists ? "pass" : "fail",
|
|
17123
17451
|
message: exists ? `${item4.file} exists` : `Missing ${item4.file} \u2014 run npx -y viberaven init --agents all`
|
|
17124
17452
|
});
|
|
17125
17453
|
}
|
|
17126
|
-
const legacyCursorPath = (0,
|
|
17454
|
+
const legacyCursorPath = (0, import_node_path32.join)(cwd, ".cursor/rules/viberaven.mdc");
|
|
17127
17455
|
if (await fileExists2(legacyCursorPath)) {
|
|
17128
|
-
const legacyContent = await (0,
|
|
17129
|
-
const hasCoreSplit = await fileExists2((0,
|
|
17456
|
+
const legacyContent = await (0, import_promises25.readFile)(legacyCursorPath, "utf-8");
|
|
17457
|
+
const hasCoreSplit = await fileExists2((0, import_node_path32.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
|
|
17130
17458
|
if (!hasCoreSplit || legacyContent.includes("alwaysApply: true")) {
|
|
17131
17459
|
checks.push({
|
|
17132
17460
|
id: "cursor-legacy-mdc",
|
|
@@ -17137,7 +17465,7 @@ async function checkAgentInjection(cwd) {
|
|
|
17137
17465
|
}
|
|
17138
17466
|
for (const target of CORE_AGENT_INJECTION_TARGETS) {
|
|
17139
17467
|
const file = target === "cursor" ? ".cursor/rules/viberaven-core.mdc" : AGENT_RULE_TARGETS[target].file;
|
|
17140
|
-
const path = (0,
|
|
17468
|
+
const path = (0, import_node_path32.join)(cwd, file);
|
|
17141
17469
|
const exists = await fileExists2(path);
|
|
17142
17470
|
if (!exists) {
|
|
17143
17471
|
checks.push({
|
|
@@ -17147,7 +17475,7 @@ async function checkAgentInjection(cwd) {
|
|
|
17147
17475
|
});
|
|
17148
17476
|
continue;
|
|
17149
17477
|
}
|
|
17150
|
-
const content = await (0,
|
|
17478
|
+
const content = await (0, import_promises25.readFile)(path, "utf-8");
|
|
17151
17479
|
const hasCommand = content.includes(PUBLIC_CHECK_COMMAND);
|
|
17152
17480
|
checks.push({
|
|
17153
17481
|
id: `canonical-${target}`,
|
|
@@ -17190,18 +17518,18 @@ function formatDoctorAgentsReport(report) {
|
|
|
17190
17518
|
}
|
|
17191
17519
|
async function fileExists2(path) {
|
|
17192
17520
|
try {
|
|
17193
|
-
await (0,
|
|
17521
|
+
await (0, import_promises25.access)(path);
|
|
17194
17522
|
return true;
|
|
17195
17523
|
} catch {
|
|
17196
17524
|
return false;
|
|
17197
17525
|
}
|
|
17198
17526
|
}
|
|
17199
|
-
var
|
|
17527
|
+
var import_promises25, import_node_path32, REQUIRED_EXISTENCE_CHECKS, OPTIONAL_CURSOR_PACK_CHECKS, STALE_PATTERNS, GATE_RULE_PHRASES_1_5_2;
|
|
17200
17528
|
var init_doctorAgents = __esm({
|
|
17201
17529
|
"src/commands/doctorAgents.ts"() {
|
|
17202
17530
|
"use strict";
|
|
17203
|
-
|
|
17204
|
-
|
|
17531
|
+
import_promises25 = require("node:fs/promises");
|
|
17532
|
+
import_node_path32 = require("node:path");
|
|
17205
17533
|
init_commands();
|
|
17206
17534
|
init_agentTargets();
|
|
17207
17535
|
REQUIRED_EXISTENCE_CHECKS = [
|
|
@@ -17472,7 +17800,7 @@ function buildContextCleanupPlan(input) {
|
|
|
17472
17800
|
const items = [];
|
|
17473
17801
|
const seen = /* @__PURE__ */ new Set();
|
|
17474
17802
|
for (const file of input.files) {
|
|
17475
|
-
const path = normalizePath5((0,
|
|
17803
|
+
const path = normalizePath5((0, import_node_path33.relative)(input.projectRoot, file.path) || file.path);
|
|
17476
17804
|
const fullPath = normalizePath5(file.path);
|
|
17477
17805
|
const category = categorizeCleanupFile(path, fullPath, file);
|
|
17478
17806
|
if (!category || seen.has(path)) {
|
|
@@ -17498,10 +17826,10 @@ async function collectCleanupFiles(projectRoot) {
|
|
|
17498
17826
|
return files;
|
|
17499
17827
|
}
|
|
17500
17828
|
async function writeCleanupPlan(projectRoot, plan) {
|
|
17501
|
-
const outputDir = (0,
|
|
17502
|
-
const outputPath = (0,
|
|
17503
|
-
await (0,
|
|
17504
|
-
await (0,
|
|
17829
|
+
const outputDir = (0, import_node_path33.join)(projectRoot, ".viberaven");
|
|
17830
|
+
const outputPath = (0, import_node_path33.join)(outputDir, "context-cleanup.md");
|
|
17831
|
+
await (0, import_promises26.mkdir)(outputDir, { recursive: true });
|
|
17832
|
+
await (0, import_promises26.writeFile)(outputPath, renderCleanupPlan(plan), "utf8");
|
|
17505
17833
|
return outputPath;
|
|
17506
17834
|
}
|
|
17507
17835
|
function categorizeCleanupFile(relativePath, fullPath, file) {
|
|
@@ -17549,7 +17877,7 @@ async function walkCleanupFiles(root, dir, result, depth) {
|
|
|
17549
17877
|
}
|
|
17550
17878
|
let entries;
|
|
17551
17879
|
try {
|
|
17552
|
-
entries = await (0,
|
|
17880
|
+
entries = await (0, import_promises26.readdir)(dir, { withFileTypes: true });
|
|
17553
17881
|
} catch {
|
|
17554
17882
|
return;
|
|
17555
17883
|
}
|
|
@@ -17557,9 +17885,9 @@ async function walkCleanupFiles(root, dir, result, depth) {
|
|
|
17557
17885
|
if (SKIP_DIRECTORIES.has(entry.name)) {
|
|
17558
17886
|
continue;
|
|
17559
17887
|
}
|
|
17560
|
-
const absolute = (0,
|
|
17888
|
+
const absolute = (0, import_node_path33.join)(dir, entry.name);
|
|
17561
17889
|
if (entry.isDirectory()) {
|
|
17562
|
-
if (isCachePath(normalizePath5((0,
|
|
17890
|
+
if (isCachePath(normalizePath5((0, import_node_path33.relative)(root, absolute)))) {
|
|
17563
17891
|
result.push({ path: absolute, sizeBytes: 0, kind: "directory" });
|
|
17564
17892
|
continue;
|
|
17565
17893
|
}
|
|
@@ -17570,7 +17898,7 @@ async function walkCleanupFiles(root, dir, result, depth) {
|
|
|
17570
17898
|
continue;
|
|
17571
17899
|
}
|
|
17572
17900
|
try {
|
|
17573
|
-
const fileStat = await (0,
|
|
17901
|
+
const fileStat = await (0, import_promises26.stat)(absolute);
|
|
17574
17902
|
result.push({ path: absolute, sizeBytes: fileStat.size, kind: "file" });
|
|
17575
17903
|
} catch {
|
|
17576
17904
|
continue;
|
|
@@ -17580,12 +17908,12 @@ async function walkCleanupFiles(root, dir, result, depth) {
|
|
|
17580
17908
|
function normalizePath5(path) {
|
|
17581
17909
|
return path.replace(/\\/g, "/");
|
|
17582
17910
|
}
|
|
17583
|
-
var
|
|
17911
|
+
var import_promises26, import_node_path33, MAX_WALK_DEPTH, LARGE_LOG_BYTES, SKIP_DIRECTORIES;
|
|
17584
17912
|
var init_cleanPlan = __esm({
|
|
17585
17913
|
"src/commands/cleanPlan.ts"() {
|
|
17586
17914
|
"use strict";
|
|
17587
|
-
|
|
17588
|
-
|
|
17915
|
+
import_promises26 = require("node:fs/promises");
|
|
17916
|
+
import_node_path33 = require("node:path");
|
|
17589
17917
|
MAX_WALK_DEPTH = 5;
|
|
17590
17918
|
LARGE_LOG_BYTES = 8192;
|
|
17591
17919
|
SKIP_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
@@ -17698,10 +18026,10 @@ var init_render = __esm({
|
|
|
17698
18026
|
|
|
17699
18027
|
// src/commands/actions.ts
|
|
17700
18028
|
async function runActionsCommand(input) {
|
|
17701
|
-
const actionsPath = (0,
|
|
18029
|
+
const actionsPath = (0, import_node_path34.join)(getProjectArtifactsDir(input.cwd), "actions.json");
|
|
17702
18030
|
let raw;
|
|
17703
18031
|
try {
|
|
17704
|
-
raw = await (0,
|
|
18032
|
+
raw = await (0, import_promises27.readFile)(actionsPath, "utf8");
|
|
17705
18033
|
} catch {
|
|
17706
18034
|
process.stderr.write("No VibeRaven actions found. Run `npx -y viberaven --agent-mode` first.\n");
|
|
17707
18035
|
return 1;
|
|
@@ -17715,12 +18043,12 @@ async function runActionsCommand(input) {
|
|
|
17715
18043
|
process.stdout.write(renderActionSurface(manifest));
|
|
17716
18044
|
return 0;
|
|
17717
18045
|
}
|
|
17718
|
-
var
|
|
18046
|
+
var import_promises27, import_node_path34;
|
|
17719
18047
|
var init_actions = __esm({
|
|
17720
18048
|
"src/commands/actions.ts"() {
|
|
17721
18049
|
"use strict";
|
|
17722
|
-
|
|
17723
|
-
|
|
18050
|
+
import_promises27 = require("node:fs/promises");
|
|
18051
|
+
import_node_path34 = require("node:path");
|
|
17724
18052
|
init_render();
|
|
17725
18053
|
init_config();
|
|
17726
18054
|
}
|
|
@@ -17729,7 +18057,7 @@ var init_actions = __esm({
|
|
|
17729
18057
|
// src/commands/verifyAction.ts
|
|
17730
18058
|
async function currentActionExists(cwd, actionId) {
|
|
17731
18059
|
try {
|
|
17732
|
-
const raw = await (0,
|
|
18060
|
+
const raw = await (0, import_promises28.readFile)((0, import_node_path35.join)(getProjectArtifactsDir(cwd), "actions.json"), "utf8");
|
|
17733
18061
|
const manifest = JSON.parse(raw);
|
|
17734
18062
|
return manifest.actions.some((action) => action.id === actionId);
|
|
17735
18063
|
} catch {
|
|
@@ -17773,12 +18101,12 @@ This only looks the action up. Run \`npx -y viberaven --verify\` to scan the rep
|
|
|
17773
18101
|
);
|
|
17774
18102
|
return 0;
|
|
17775
18103
|
}
|
|
17776
|
-
var
|
|
18104
|
+
var import_promises28, import_node_path35;
|
|
17777
18105
|
var init_verifyAction = __esm({
|
|
17778
18106
|
"src/commands/verifyAction.ts"() {
|
|
17779
18107
|
"use strict";
|
|
17780
|
-
|
|
17781
|
-
|
|
18108
|
+
import_promises28 = require("node:fs/promises");
|
|
18109
|
+
import_node_path35 = require("node:path");
|
|
17782
18110
|
init_registry2();
|
|
17783
18111
|
init_config();
|
|
17784
18112
|
}
|
|
@@ -17902,11 +18230,36 @@ var init_preview = __esm({
|
|
|
17902
18230
|
async function runCheckCommand(options) {
|
|
17903
18231
|
const result = await runProjectScan({ workspacePath: options.cwd });
|
|
17904
18232
|
if (!result.ok) {
|
|
18233
|
+
if (options.markdown) console.log(`${COMMENT_MARKER}
|
|
18234
|
+
### VibeRaven could not check this change
|
|
18235
|
+
|
|
18236
|
+
${result.message}
|
|
18237
|
+
`);
|
|
17905
18238
|
console.error(formatAgentStatus(ERROR, result.message));
|
|
17906
18239
|
return 2;
|
|
17907
18240
|
}
|
|
17908
18241
|
const artifact = result.artifact;
|
|
17909
18242
|
const paths = await writeScanArtifacts({ artifact, cwd: options.cwd });
|
|
18243
|
+
let change;
|
|
18244
|
+
if (options.base) {
|
|
18245
|
+
try {
|
|
18246
|
+
change = await checkChange(options.cwd, options.base, artifact.gaps);
|
|
18247
|
+
} catch (error) {
|
|
18248
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
18249
|
+
if (options.markdown) console.log(`${COMMENT_MARKER}
|
|
18250
|
+
### VibeRaven could not compare this change
|
|
18251
|
+
|
|
18252
|
+
${message}
|
|
18253
|
+
`);
|
|
18254
|
+
console.error(formatAgentStatus(ERROR, message));
|
|
18255
|
+
return 2;
|
|
18256
|
+
}
|
|
18257
|
+
}
|
|
18258
|
+
const failing = change ? change.introduced.some((g2) => g2.severity === "critical") : artifact.gaps.some((g2) => g2.severity === "critical");
|
|
18259
|
+
if (options.markdown) {
|
|
18260
|
+
console.log(renderCheckMarkdown({ head: artifact.gaps, change, version: VERSION, score: artifact.score }));
|
|
18261
|
+
return failing ? 1 : 0;
|
|
18262
|
+
}
|
|
17910
18263
|
if (options.json) {
|
|
17911
18264
|
console.log(
|
|
17912
18265
|
JSON.stringify(
|
|
@@ -17921,13 +18274,22 @@ async function runCheckCommand(options) {
|
|
|
17921
18274
|
title: gap.title,
|
|
17922
18275
|
detail: gap.detail
|
|
17923
18276
|
})),
|
|
18277
|
+
...change ? {
|
|
18278
|
+
change: {
|
|
18279
|
+
base: change.base,
|
|
18280
|
+
baseCommit: change.baseCommit,
|
|
18281
|
+
introduced: change.introduced.map((gap) => gap.id),
|
|
18282
|
+
resolved: change.resolved.map((gap) => gap.id),
|
|
18283
|
+
unchanged: change.unchanged.map((gap) => gap.id)
|
|
18284
|
+
}
|
|
18285
|
+
} : {},
|
|
17924
18286
|
artifacts: paths
|
|
17925
18287
|
},
|
|
17926
18288
|
null,
|
|
17927
18289
|
2
|
|
17928
18290
|
)
|
|
17929
18291
|
);
|
|
17930
|
-
return
|
|
18292
|
+
return failing ? 1 : 0;
|
|
17931
18293
|
}
|
|
17932
18294
|
const sorted = [...artifact.gaps].sort(
|
|
17933
18295
|
(a, b3) => SEVERITY_ORDER3[a.severity] - SEVERITY_ORDER3[b3.severity]
|
|
@@ -17937,14 +18299,37 @@ async function runCheckCommand(options) {
|
|
|
17937
18299
|
console.log("");
|
|
17938
18300
|
console.log(`viberaven check \xB7 ${options.cwd}`);
|
|
17939
18301
|
console.log("");
|
|
18302
|
+
if (change) {
|
|
18303
|
+
const line = (gap) => `${SEVERITY_ICON[gap.severity]} ${gap.title} (${gap.id})`;
|
|
18304
|
+
console.log(`Compared with ${change.base} (${change.baseCommit.slice(0, 7)})`);
|
|
18305
|
+
console.log("");
|
|
18306
|
+
console.log(change.introduced.length ? "New in this change:" : "New in this change: nothing");
|
|
18307
|
+
for (const gap of change.introduced) {
|
|
18308
|
+
console.log(` ${line(gap)}${gap.subjects?.length ? ` new: ${gap.subjects.join(", ")}` : ""}`);
|
|
18309
|
+
if (!options.quiet && !gap.subjects?.length && gap.detail) console.log(` ${gap.detail.split("\n")[0]}`);
|
|
18310
|
+
}
|
|
18311
|
+
if (change.resolved.length) {
|
|
18312
|
+
console.log("Fixed by this change:");
|
|
18313
|
+
for (const gap of change.resolved) console.log(` \u2705 ${gap.title}${gap.subjects?.length ? ` for ${gap.subjects.join(", ")}` : ""} (${gap.id})`);
|
|
18314
|
+
}
|
|
18315
|
+
if (change.unchanged.length) console.log(`Already there before: ${change.unchanged.length}`);
|
|
18316
|
+
const newCritical = change.introduced.filter((g2) => g2.severity === "critical").length;
|
|
18317
|
+
console.log("");
|
|
18318
|
+
console.log(
|
|
18319
|
+
newCritical > 0 ? `Verdict: \u274C ${newCritical} new blocker${newCritical === 1 ? "" : "s"} in this change \xB7 score ${artifact.score}` : `Verdict: \u2705 no new blockers in this change \xB7 score ${artifact.score}`
|
|
18320
|
+
);
|
|
18321
|
+
console.log(`Fix: viberaven fix \xB7 Details: ${paths.tasklistPath}`);
|
|
18322
|
+
console.log("");
|
|
18323
|
+
return failing ? 1 : 0;
|
|
18324
|
+
}
|
|
17940
18325
|
if (sorted.length === 0) {
|
|
17941
18326
|
console.log("\u2705 No launch blockers found in repo evidence.");
|
|
17942
18327
|
} else {
|
|
17943
18328
|
for (const gap of sorted) {
|
|
17944
18329
|
console.log(`${SEVERITY_ICON[gap.severity]} ${gap.title} (${gap.id})`);
|
|
17945
18330
|
if (!options.quiet && gap.detail) {
|
|
17946
|
-
const
|
|
17947
|
-
console.log(` ${
|
|
18331
|
+
const firstLine3 = gap.detail.split("\n")[0];
|
|
18332
|
+
console.log(` ${firstLine3}`);
|
|
17948
18333
|
}
|
|
17949
18334
|
}
|
|
17950
18335
|
}
|
|
@@ -17963,6 +18348,8 @@ var init_check = __esm({
|
|
|
17963
18348
|
init_runScan();
|
|
17964
18349
|
init_artifacts2();
|
|
17965
18350
|
init_statusLabels();
|
|
18351
|
+
init_version();
|
|
18352
|
+
init_checkChange();
|
|
17966
18353
|
SEVERITY_ICON = {
|
|
17967
18354
|
critical: "\u{1F534}",
|
|
17968
18355
|
// red circle
|
|
@@ -18006,12 +18393,12 @@ function parsePermissionsMatrix(text) {
|
|
|
18006
18393
|
} else {
|
|
18007
18394
|
const seen = /* @__PURE__ */ new Set();
|
|
18008
18395
|
for (const [key, value] of Object.entries(raw.tables)) {
|
|
18009
|
-
const
|
|
18010
|
-
if (!
|
|
18011
|
-
const full = `${
|
|
18396
|
+
const table2 = parseTable(key, value, problems);
|
|
18397
|
+
if (!table2) continue;
|
|
18398
|
+
const full = `${table2.schema}.${table2.name}`;
|
|
18012
18399
|
if (seen.has(full)) problems.push(`tables[${show(key)}]: ${full} is listed twice`);
|
|
18013
18400
|
seen.add(full);
|
|
18014
|
-
tables.push(
|
|
18401
|
+
tables.push(table2);
|
|
18015
18402
|
}
|
|
18016
18403
|
}
|
|
18017
18404
|
if (problems.length > 0) throw new MatrixError(problems);
|
|
@@ -18085,10 +18472,10 @@ function parseTable(key, value, problems) {
|
|
|
18085
18472
|
}
|
|
18086
18473
|
const parsed = {};
|
|
18087
18474
|
for (const action of MATRIX_ACTIONS) {
|
|
18088
|
-
const
|
|
18089
|
-
if (
|
|
18090
|
-
else if (!CELL_VALUES.includes(
|
|
18091
|
-
else parsed[action] =
|
|
18475
|
+
const cell2 = cells[action];
|
|
18476
|
+
if (cell2 === void 0) problems.push(`${actorAt}.${action}: missing; write ${quoteList(CELL_VALUES)}`);
|
|
18477
|
+
else if (!CELL_VALUES.includes(cell2)) problems.push(`${actorAt}.${action}: must be ${quoteList(CELL_VALUES)} (got ${show(cell2)})`);
|
|
18478
|
+
else parsed[action] = cell2;
|
|
18092
18479
|
}
|
|
18093
18480
|
if (cells.reassign !== void 0) {
|
|
18094
18481
|
if (!CELL_VALUES.includes(cells.reassign)) problems.push(`${actorAt}.reassign: must be ${quoteList(CELL_VALUES)} (got ${show(cells.reassign)})`);
|
|
@@ -18308,13 +18695,13 @@ async function createWorkerDatabase(options) {
|
|
|
18308
18695
|
const call = (message, timeoutMs) => {
|
|
18309
18696
|
if (dead) return Promise.reject(dead);
|
|
18310
18697
|
const id = ++nextId;
|
|
18311
|
-
return new Promise((
|
|
18698
|
+
return new Promise((resolve10, reject) => {
|
|
18312
18699
|
const timer = setTimeout(() => {
|
|
18313
18700
|
const error = new RlsTimeoutError(`timed out after ${Math.round(timeoutMs / 1e3)} s`);
|
|
18314
18701
|
failAll(error);
|
|
18315
18702
|
void worker.terminate();
|
|
18316
18703
|
}, timeoutMs);
|
|
18317
|
-
pending.set(id, { resolve:
|
|
18704
|
+
pending.set(id, { resolve: resolve10, reject, timer });
|
|
18318
18705
|
worker.postMessage({ ...message, id });
|
|
18319
18706
|
});
|
|
18320
18707
|
};
|
|
@@ -18399,22 +18786,22 @@ parentPort.on('message', async (m) => {
|
|
|
18399
18786
|
// src/rls/rlsRunner.ts
|
|
18400
18787
|
function sqlError(error) {
|
|
18401
18788
|
const e2 = error;
|
|
18402
|
-
return { code: typeof e2?.code === "string" ? e2.code : "unknown", message:
|
|
18789
|
+
return { code: typeof e2?.code === "string" ? e2.code : "unknown", message: firstLine2(typeof e2?.message === "string" ? e2.message : String(error)) };
|
|
18403
18790
|
}
|
|
18404
18791
|
function isSqlError(error) {
|
|
18405
18792
|
return !(error instanceof RlsTimeoutError) && typeof error?.code === "string";
|
|
18406
18793
|
}
|
|
18407
18794
|
function displayPath(projectDir, path) {
|
|
18408
18795
|
for (const base of [projectDir, process.cwd()]) {
|
|
18409
|
-
const rel = (0,
|
|
18410
|
-
if (rel && !rel.startsWith("..") && !(0,
|
|
18796
|
+
const rel = (0, import_node_path36.relative)(base, path);
|
|
18797
|
+
if (rel && !rel.startsWith("..") && !(0, import_node_path36.isAbsolute)(rel)) return rel.split("\\").join("/");
|
|
18411
18798
|
}
|
|
18412
18799
|
return path;
|
|
18413
18800
|
}
|
|
18414
18801
|
async function gitCommit(projectDir, paths) {
|
|
18415
18802
|
const run = async (args) => {
|
|
18416
18803
|
const call = await safeGitInvocation(projectDir, args);
|
|
18417
|
-
const { stdout } = await (0,
|
|
18804
|
+
const { stdout } = await (0, import_node_util6.promisify)(import_node_child_process8.execFile)("git", call.args, { cwd: projectDir, env: call.env, windowsHide: true, timeout: 5e3, encoding: "utf8" });
|
|
18418
18805
|
return String(stdout);
|
|
18419
18806
|
};
|
|
18420
18807
|
let sha;
|
|
@@ -18487,11 +18874,11 @@ function valueCandidates(column, label) {
|
|
|
18487
18874
|
if (column.category === "A") return u2 ? [] : ["{}"];
|
|
18488
18875
|
return [];
|
|
18489
18876
|
}
|
|
18490
|
-
async function describeTable(db,
|
|
18491
|
-
const display = `${
|
|
18877
|
+
async function describeTable(db, table2) {
|
|
18878
|
+
const display = `${table2.schema}.${table2.name}`;
|
|
18492
18879
|
const rel = await db.query(
|
|
18493
18880
|
`select c.oid::int as oid, c.relkind::text as kind from pg_class c join pg_namespace n on n.oid = c.relnamespace where n.nspname = $1 and c.relname = $2`,
|
|
18494
|
-
[
|
|
18881
|
+
[table2.schema, table2.name]
|
|
18495
18882
|
);
|
|
18496
18883
|
if (rel.rows.length === 0) return { reason: `${display} does not exist after the migrations; check the table name in the matrix.` };
|
|
18497
18884
|
const kind = String(rel.rows[0].kind);
|
|
@@ -18530,35 +18917,35 @@ async function describeTable(db, table) {
|
|
|
18530
18917
|
};
|
|
18531
18918
|
});
|
|
18532
18919
|
const byName = new Map(columns.map((c) => [c.name, c]));
|
|
18533
|
-
const owner = byName.get(
|
|
18534
|
-
if (!owner) return { reason: `${display} has no column ${
|
|
18920
|
+
const owner = byName.get(table2.ownerColumn);
|
|
18921
|
+
if (!owner) return { reason: `${display} has no column ${table2.ownerColumn} (the matrix owner column).` };
|
|
18535
18922
|
if (owner.typname !== "uuid" && owner.category !== "S") {
|
|
18536
|
-
return { reason: `${display}.${
|
|
18923
|
+
return { reason: `${display}.${table2.ownerColumn} is ${owner.type}; the owner column must hold auth.uid() (uuid or text).` };
|
|
18537
18924
|
}
|
|
18538
18925
|
const pk = columns.filter((c) => c.isPk);
|
|
18539
18926
|
if (pk.length === 0) return { reason: `${display} has no primary key; rls-test targets the owner's row by primary key.` };
|
|
18540
|
-
for (const column of Object.keys(
|
|
18927
|
+
for (const column of Object.keys(table2.seed)) {
|
|
18541
18928
|
if (!byName.has(column)) return { reason: `the matrix seeds ${display}.${column}, which does not exist.` };
|
|
18542
18929
|
}
|
|
18543
|
-
if (
|
|
18544
|
-
const column = byName.get(
|
|
18545
|
-
if (!column) return { reason: `the matrix updateColumn ${display}.${
|
|
18930
|
+
if (table2.updateColumn) {
|
|
18931
|
+
const column = byName.get(table2.updateColumn);
|
|
18932
|
+
if (!column) return { reason: `the matrix updateColumn ${display}.${table2.updateColumn} does not exist.` };
|
|
18546
18933
|
if (valueCandidates(column, "u").length === 0) return { reason: `the test cannot invent a new value for ${display}.${column.name} (${column.type}); pick another updateColumn.` };
|
|
18547
18934
|
}
|
|
18548
18935
|
const writable = (c) => c.identity === "" && c.generated === "";
|
|
18549
|
-
const required = columns.filter((c) => c.name !== owner.name && c.notNull && !c.hasDefault && writable(c) && !(c.name in
|
|
18936
|
+
const required = columns.filter((c) => c.name !== owner.name && c.notNull && !c.hasDefault && writable(c) && !(c.name in table2.seed));
|
|
18550
18937
|
for (const column of required) {
|
|
18551
18938
|
if (column.isFk) return { reason: `${display}.${column.name} is a NOT NULL foreign key the test cannot invent; give it a value under "seed" in the matrix.` };
|
|
18552
18939
|
if (valueCandidates(column, "a").length === 0) {
|
|
18553
18940
|
return { reason: `${display}.${column.name} (${column.type}) is NOT NULL and the test cannot invent a value; give it one under "seed" in the matrix.` };
|
|
18554
18941
|
}
|
|
18555
18942
|
}
|
|
18556
|
-
const seedColumns = Object.keys(
|
|
18943
|
+
const seedColumns = Object.keys(table2.seed);
|
|
18557
18944
|
return {
|
|
18558
18945
|
shape: {
|
|
18559
|
-
table,
|
|
18946
|
+
table: table2,
|
|
18560
18947
|
oid,
|
|
18561
|
-
qualified: `${quoteIdent2(
|
|
18948
|
+
qualified: `${quoteIdent2(table2.schema)}.${quoteIdent2(table2.name)}`,
|
|
18562
18949
|
display,
|
|
18563
18950
|
columns,
|
|
18564
18951
|
pk,
|
|
@@ -18566,7 +18953,7 @@ async function describeTable(db, table) {
|
|
|
18566
18953
|
ownerUnique: unique3.has(owner.name),
|
|
18567
18954
|
ownerInPk: pk.some((c) => c.name === owner.name),
|
|
18568
18955
|
insertColumns: [owner.name, ...seedColumns, ...required.map((c) => c.name)],
|
|
18569
|
-
rowValues: (label, ownerId) => [ownerId, ...seedColumns.map((c) =>
|
|
18956
|
+
rowValues: (label, ownerId) => [ownerId, ...seedColumns.map((c) => table2.seed[c]), ...required.map((c) => valueCandidates(c, label)[0])]
|
|
18570
18957
|
}
|
|
18571
18958
|
};
|
|
18572
18959
|
}
|
|
@@ -18662,10 +19049,10 @@ function writeControl(filtered, changed) {
|
|
|
18662
19049
|
if (!changed(run)) {
|
|
18663
19050
|
return {
|
|
18664
19051
|
ok: false,
|
|
18665
|
-
detail: filtered ? `as the superuser the same statement changed ${
|
|
19052
|
+
detail: filtered ? `as the superuser the same statement changed ${plural3(run.affected ?? 0, "row")}, expected 1` : "as the superuser the same statement did not change the owner row"
|
|
18666
19053
|
};
|
|
18667
19054
|
}
|
|
18668
|
-
return { ok: true, detail: `as the superuser the same statement changed ${
|
|
19055
|
+
return { ok: true, detail: `as the superuser the same statement changed ${plural3(filtered ? run.affected ?? 0 : 1, "row")}` };
|
|
18669
19056
|
};
|
|
18670
19057
|
}
|
|
18671
19058
|
async function differingValue(db, shape, target, column) {
|
|
@@ -18692,7 +19079,7 @@ async function selectPlans(c) {
|
|
|
18692
19079
|
return [
|
|
18693
19080
|
{
|
|
18694
19081
|
stmt: { name: "select", sql: `select ${pkList} from ${shape.qualified} where ${pkCondition(shape, 1)}`, params: target, display: `select the owner's row from ${shape.display} by primary key`, filtered: true, select: "pk" },
|
|
18695
|
-
controlOk: (run) => run.error ? { ok: false, detail: `as the superuser the same statement failed: ${run.error.message} (${run.error.code})` } : run.rows?.length === 1 ? { ok: true, detail: "as the superuser the same statement returned 1 row" } : { ok: false, detail: `as the superuser the same statement returned ${
|
|
19082
|
+
controlOk: (run) => run.error ? { ok: false, detail: `as the superuser the same statement failed: ${run.error.message} (${run.error.code})` } : run.rows?.length === 1 ? { ok: true, detail: "as the superuser the same statement returned 1 row" } : { ok: false, detail: `as the superuser the same statement returned ${plural3(run.rows?.length ?? 0, "row")}, expected 1` },
|
|
18696
19083
|
judge: (run) => {
|
|
18697
19084
|
const denied = privilegeDenied(run);
|
|
18698
19085
|
if (denied) return denied;
|
|
@@ -18747,7 +19134,7 @@ function insertPlans(c) {
|
|
|
18747
19134
|
prep,
|
|
18748
19135
|
controlOk: (run) => {
|
|
18749
19136
|
if (run.error) return { ok: false, detail: `as the superuser the same statement failed: ${run.error.message} (${run.error.code})` };
|
|
18750
|
-
return added(run) ? { ok: true, detail: "as the superuser the same statement changed 1 row" } : { ok: false, detail: `as the superuser the same statement changed ${
|
|
19137
|
+
return added(run) ? { ok: true, detail: "as the superuser the same statement changed 1 row" } : { ok: false, detail: `as the superuser the same statement changed ${plural3(run.affected ?? 0, "row")}, expected 1` };
|
|
18751
19138
|
},
|
|
18752
19139
|
judge: (run) => {
|
|
18753
19140
|
if (isRlsRefusal(run)) return { verdict: "unknown", detail: `refused by WITH CHECK: ${run.error.message} (42501). Only the generated row was tried; other values may allow insertion` };
|
|
@@ -18759,7 +19146,7 @@ function insertPlans(c) {
|
|
|
18759
19146
|
if ((run.affected ?? 0) === 0 && run.after.digest === run.before.digest) return { verdict: "denied", detail: "0 rows added, table unchanged" };
|
|
18760
19147
|
return {
|
|
18761
19148
|
verdict: "unknown",
|
|
18762
|
-
detail: `reported ${
|
|
19149
|
+
detail: `reported ${plural3(run.affected ?? 0, "row")}, but the table went from ${run.before.count} to ${run.after.count} rows and the owner's rows from ${run.before.owned} to ${run.after.owned}`
|
|
18763
19150
|
};
|
|
18764
19151
|
}
|
|
18765
19152
|
}
|
|
@@ -18807,7 +19194,7 @@ async function updatePlans(c) {
|
|
|
18807
19194
|
if (changed(run)) return { verdict: "allowed", detail: noop ? "1 row updated (rewrote its own value)" : `${changedRows(filtered ? run.affected : 1)} (${sets.map((s) => s.column).join(" and ")} rewritten)` };
|
|
18808
19195
|
if (!filtered) return { verdict: "denied", detail: "the owner row is unchanged" };
|
|
18809
19196
|
if ((run.affected ?? 0) === 0 && run.after.digest === run.before.digest) return { verdict: "denied", detail: "0 rows changed, the row is still there and unchanged" };
|
|
18810
|
-
return { verdict: "unknown", detail: `reported ${
|
|
19197
|
+
return { verdict: "unknown", detail: `reported ${plural3(run.affected ?? 0, "row")}, but the owner row ${run.after.target === null ? "is gone" : "did not change"} and the table ${run.after.digest === run.before.digest ? "did not" : "did"} change` };
|
|
18811
19198
|
}
|
|
18812
19199
|
});
|
|
18813
19200
|
};
|
|
@@ -18859,7 +19246,7 @@ function reassignPlans(c) {
|
|
|
18859
19246
|
if (moved(run)) return { verdict: "allowed", detail: `the owner row now belongs to the other user` };
|
|
18860
19247
|
if (!filtered && run.after.target === run.before.target) return { verdict: "denied", detail: "the owner row is unchanged" };
|
|
18861
19248
|
if ((run.affected ?? 0) === 0 && run.after.digest === run.before.digest) return { verdict: "denied", detail: "0 rows changed, the row is still there and unchanged" };
|
|
18862
|
-
return { verdict: "unknown", detail: `reported ${
|
|
19249
|
+
return { verdict: "unknown", detail: `reported ${plural3(run.affected ?? 0, "row")}, but the owner column did not move` };
|
|
18863
19250
|
}
|
|
18864
19251
|
}
|
|
18865
19252
|
];
|
|
@@ -18888,7 +19275,7 @@ function deletePlans(c) {
|
|
|
18888
19275
|
if ((run.affected ?? 0) === 0 && run.after.digest === run.before.digest) return { verdict: "denied", detail: "0 rows changed, the row is still there and unchanged" };
|
|
18889
19276
|
return {
|
|
18890
19277
|
verdict: "unknown",
|
|
18891
|
-
detail: `reported ${
|
|
19278
|
+
detail: `reported ${plural3(run.affected ?? 0, "row")}, but the owner row is ${run.after.target === null ? "gone" : "still there"} and the table ${run.after.digest === run.before.digest ? "did not" : "did"} change`
|
|
18892
19279
|
};
|
|
18893
19280
|
}
|
|
18894
19281
|
}
|
|
@@ -18959,25 +19346,25 @@ async function runCell(ctx, privilegesByRole, actor, action, expected) {
|
|
|
18959
19346
|
const reason = pass ? `Expected ${expected}, observed ${allowed ? "allowed" : "denied"}.` : want ? `Expected ${actorText} to be allowed to ${action}, but ${observed.summary}.${action !== "select" && !r.error ? " Postgres reports rows no policy allows as 0 rows, not as an error, so the app sees no failure." : ""}` : `Expected ${actorText} to be denied ${action}, but ${observed.summary}.`;
|
|
18960
19347
|
return { ...base, outcome: pass ? "Pass" : "Fail", observed, reason };
|
|
18961
19348
|
}
|
|
18962
|
-
function cellsFor(
|
|
19349
|
+
function cellsFor(table2) {
|
|
18963
19350
|
const out = [];
|
|
18964
19351
|
for (const actor of TESTED_ACTORS) {
|
|
18965
|
-
for (const action of MATRIX_ACTIONS) out.push({ actor, action, expected:
|
|
18966
|
-
const reassign =
|
|
19352
|
+
for (const action of MATRIX_ACTIONS) out.push({ actor, action, expected: table2.expect[actor][action] });
|
|
19353
|
+
const reassign = table2.expect[actor].reassign;
|
|
18967
19354
|
if (reassign) out.push({ actor, action: "reassign", expected: reassign });
|
|
18968
19355
|
}
|
|
18969
19356
|
return out;
|
|
18970
19357
|
}
|
|
18971
|
-
function unknownCells(
|
|
18972
|
-
const display = `${
|
|
18973
|
-
const cells = cellsFor(
|
|
19358
|
+
function unknownCells(table2, reason, summary) {
|
|
19359
|
+
const display = `${table2.schema}.${table2.name}`;
|
|
19360
|
+
const cells = cellsFor(table2).map(
|
|
18974
19361
|
({ actor, action, expected }) => expected === "skip" ? { table: display, actor, action, expected, outcome: "N/A", observed: { summary: "not tested (skip in the matrix)" }, reason: "The matrix marks this cell skip." } : { table: display, actor, action, expected, outcome: "Unknown", observed: { summary }, reason }
|
|
18975
19362
|
);
|
|
18976
|
-
return [...cells, ...serviceRoleCells(
|
|
19363
|
+
return [...cells, ...serviceRoleCells(table2)];
|
|
18977
19364
|
}
|
|
18978
|
-
function serviceRoleCells(
|
|
18979
|
-
if (!
|
|
18980
|
-
const display = `${
|
|
19365
|
+
function serviceRoleCells(table2) {
|
|
19366
|
+
if (!table2.serviceRoleExcluded) return [];
|
|
19367
|
+
const display = `${table2.schema}.${table2.name}`;
|
|
18981
19368
|
return MATRIX_ACTIONS.map((action) => ({
|
|
18982
19369
|
table: display,
|
|
18983
19370
|
actor: "service_role",
|
|
@@ -18997,40 +19384,40 @@ function summarise(tables) {
|
|
|
18997
19384
|
function verdictFor(summary) {
|
|
18998
19385
|
if (summary.fail > 0) {
|
|
18999
19386
|
return {
|
|
19000
|
-
verdict: `Not ready within tested scope: ${
|
|
19387
|
+
verdict: `Not ready within tested scope: ${plural3(summary.fail, "cell does", "cells do")} not match the matrix; ${plural3(summary.unknown, "unknown")}.`,
|
|
19001
19388
|
exitCode: 1
|
|
19002
19389
|
};
|
|
19003
19390
|
}
|
|
19004
19391
|
if (summary.pass === 0) {
|
|
19005
|
-
return { verdict: `Nothing was observed: ${
|
|
19392
|
+
return { verdict: `Nothing was observed: ${plural3(summary.unknown, "unknown")}, ${summary.notApplicable} N/A.`, exitCode: 2 };
|
|
19006
19393
|
}
|
|
19007
19394
|
if (summary.unknown > 0) {
|
|
19008
|
-
return { verdict: `No blockers found within tested scope; ${
|
|
19395
|
+
return { verdict: `No blockers found within tested scope; ${plural3(summary.unknown, "unknown")}.`, exitCode: 2 };
|
|
19009
19396
|
}
|
|
19010
19397
|
return { verdict: "No blockers found within tested scope; 0 unknowns. Scenario passed locally.", exitCode: 0 };
|
|
19011
19398
|
}
|
|
19012
19399
|
async function listMigrations(dir) {
|
|
19013
19400
|
try {
|
|
19014
|
-
const entries = await (0,
|
|
19401
|
+
const entries = await (0, import_promises29.readdir)(dir, { withFileTypes: true });
|
|
19015
19402
|
return entries.filter((e2) => e2.isFile() && /\.sql$/i.test(e2.name) && !e2.name.startsWith(".")).map((e2) => e2.name).sort((a, b3) => a < b3 ? -1 : a > b3 ? 1 : 0);
|
|
19016
19403
|
} catch {
|
|
19017
19404
|
return [];
|
|
19018
19405
|
}
|
|
19019
19406
|
}
|
|
19020
19407
|
async function runRlsTest(options) {
|
|
19021
|
-
const projectDir = (0,
|
|
19022
|
-
const matrixPath = (0,
|
|
19023
|
-
const migrationsDir = (0,
|
|
19408
|
+
const projectDir = (0, import_node_path36.resolve)(options.projectDir);
|
|
19409
|
+
const matrixPath = (0, import_node_path36.resolve)(projectDir, options.matrixPath ?? DEFAULT_MATRIX_FILE);
|
|
19410
|
+
const migrationsDir = (0, import_node_path36.resolve)(projectDir, options.migrationsDir ?? (0, import_node_path36.join)("supabase", "migrations"));
|
|
19024
19411
|
const timeouts = { ...DEFAULT_TIMEOUTS, ...options.timeouts };
|
|
19025
|
-
if ((0,
|
|
19026
|
-
throw new RlsInputError(`Refusing to read ${(0,
|
|
19412
|
+
if ((0, import_node_path36.basename)(matrixPath).toLowerCase().startsWith(".env")) {
|
|
19413
|
+
throw new RlsInputError(`Refusing to read ${(0, import_node_path36.basename)(matrixPath)}: rls-test never reads .env files. Point --matrix at a viberaven.permissions.json.`);
|
|
19027
19414
|
}
|
|
19028
19415
|
const filesRead = [];
|
|
19029
19416
|
let matrixText;
|
|
19030
19417
|
try {
|
|
19031
|
-
const info = await (0,
|
|
19418
|
+
const info = await (0, import_promises29.stat)(matrixPath);
|
|
19032
19419
|
if (!info.isFile()) throw new Error("not a file");
|
|
19033
|
-
matrixText = normalise(await (0,
|
|
19420
|
+
matrixText = normalise(await (0, import_promises29.readFile)(matrixPath, "utf8"));
|
|
19034
19421
|
} catch {
|
|
19035
19422
|
throw new RlsInputError(
|
|
19036
19423
|
`No permissions matrix at ${matrixPath}. Write one by hand (schema: schemas/permissions-matrix.schema.json in the @viberaven/cli package); rls-test never infers it from your policies.`
|
|
@@ -19041,8 +19428,8 @@ async function runRlsTest(options) {
|
|
|
19041
19428
|
const names = await listMigrations(migrationsDir);
|
|
19042
19429
|
const migrationFiles = [];
|
|
19043
19430
|
for (const name of names) {
|
|
19044
|
-
const sql = normalise(await (0,
|
|
19045
|
-
filesRead.push(displayPath(projectDir, (0,
|
|
19431
|
+
const sql = normalise(await (0, import_promises29.readFile)((0, import_node_path36.join)(migrationsDir, name), "utf8"));
|
|
19432
|
+
filesRead.push(displayPath(projectDir, (0, import_node_path36.join)(migrationsDir, name)));
|
|
19046
19433
|
migrationFiles.push({ name, sha256: sha256(sql), sql });
|
|
19047
19434
|
}
|
|
19048
19435
|
const migrationsHash = sha256(migrationFiles.map((f) => `${f.name}\0${f.sha256}`).join("\n"));
|
|
@@ -19094,7 +19481,7 @@ async function runRlsTest(options) {
|
|
|
19094
19481
|
engineLoaded = true;
|
|
19095
19482
|
db = await engine.create({ env: allowlistedEnv(process.env), statementTimeoutMs: timeouts.statementMs });
|
|
19096
19483
|
} catch (error) {
|
|
19097
|
-
result.engine.error =
|
|
19484
|
+
result.engine.error = firstLine2(error instanceof Error ? error.message : String(error));
|
|
19098
19485
|
if (engineLoaded) return allUnknown(`PGlite could not start: ${result.engine.error}. Nothing was tested.`, "not run: PGlite startup failed");
|
|
19099
19486
|
const missing = error?.code === "MODULE_NOT_FOUND" || error?.code === "ERR_MODULE_NOT_FOUND";
|
|
19100
19487
|
return missing ? allUnknown(PGLITE_INSTALL_HINT, "not run: PGlite is not installed") : allUnknown(`PGlite could not load: ${result.engine.error}. Nothing was tested.`, "not run: PGlite loading failed");
|
|
@@ -19154,13 +19541,13 @@ async function runRlsTest(options) {
|
|
|
19154
19541
|
result.roleChecks.push({ role, ok, detail: row ? `superuser ${Boolean(row.su)}, BYPASSRLS ${Boolean(row.bypass)}` : "the role does not exist after the migrations" });
|
|
19155
19542
|
}
|
|
19156
19543
|
const badRoles = result.roleChecks.filter((c) => !c.ok);
|
|
19157
|
-
for (const
|
|
19158
|
-
result.tables.push(await runTable(db,
|
|
19544
|
+
for (const table2 of matrix.tables) {
|
|
19545
|
+
result.tables.push(await runTable(db, table2, badRoles, result.roleChecks));
|
|
19159
19546
|
}
|
|
19160
19547
|
return finish();
|
|
19161
19548
|
} catch (error) {
|
|
19162
19549
|
if (error instanceof RlsTimeoutError || !isSqlError(error)) {
|
|
19163
|
-
const message =
|
|
19550
|
+
const message = firstLine2(error instanceof Error ? error.message : String(error));
|
|
19164
19551
|
return allUnknown(
|
|
19165
19552
|
error instanceof RlsTimeoutError ? `A statement did not finish within ${Math.round(timeouts.statementMs / 1e3)} s, so the PGlite worker was stopped and no cell is reported as passed.` : `The PGlite worker stopped (${message}), so no cell is reported as passed.`,
|
|
19166
19553
|
"not run: the database stopped"
|
|
@@ -19186,13 +19573,13 @@ async function seedRow(db, shape, label, userId) {
|
|
|
19186
19573
|
if (r.rows.length !== 1) throw Object.assign(new Error("the insert returned no row (a trigger or rule may drop it)"), { code: "vr000" });
|
|
19187
19574
|
return { key: keyOf2(r.rows[0]), reused: false };
|
|
19188
19575
|
}
|
|
19189
|
-
async function runTable(db,
|
|
19190
|
-
const display = `${
|
|
19191
|
-
const unknownTable = (reason, extra = {}) => ({ name: display, ...extra, unknownReason: reason, cells: unknownCells(
|
|
19576
|
+
async function runTable(db, table2, badRoles, roleChecks) {
|
|
19577
|
+
const display = `${table2.schema}.${table2.name}`;
|
|
19578
|
+
const unknownTable = (reason, extra = {}) => ({ name: display, ...extra, unknownReason: reason, cells: unknownCells(table2, reason, "not run") });
|
|
19192
19579
|
if (badRoles.length > 0) {
|
|
19193
19580
|
return unknownTable(`Role check failed (${badRoles.map((c) => `${c.role}: ${c.detail}`).join("; ")}), so RLS would not apply to the probes the way it does for the Data API.`);
|
|
19194
19581
|
}
|
|
19195
|
-
const described = await describeTable(db,
|
|
19582
|
+
const described = await describeTable(db, table2);
|
|
19196
19583
|
if (!described.shape) return unknownTable(described.reason ?? "unknown table problem");
|
|
19197
19584
|
const shape = described.shape;
|
|
19198
19585
|
const rel = await db.query(
|
|
@@ -19226,15 +19613,15 @@ async function runTable(db, table, badRoles, roleChecks) {
|
|
|
19226
19613
|
const privilegesByRole = /* @__PURE__ */ new Map();
|
|
19227
19614
|
for (const role of ["anon", "authenticated"]) privilegesByRole.set(role, await privilegesFor(db, shape, role));
|
|
19228
19615
|
const cells = [];
|
|
19229
|
-
for (const { actor, action, expected } of cellsFor(
|
|
19616
|
+
for (const { actor, action, expected } of cellsFor(table2)) {
|
|
19230
19617
|
cells.push(await runCell({ db, shape, target: ownerSeed.key }, privilegesByRole, actor, action, expected));
|
|
19231
19618
|
}
|
|
19232
|
-
cells.push(...serviceRoleCells(
|
|
19619
|
+
cells.push(...serviceRoleCells(table2));
|
|
19233
19620
|
const columns = {};
|
|
19234
19621
|
shape.insertColumns.forEach((c) => {
|
|
19235
19622
|
columns[c] = c === shape.owner.name ? "the test user id" : "generated test value";
|
|
19236
19623
|
});
|
|
19237
|
-
for (const c of Object.keys(
|
|
19624
|
+
for (const c of Object.keys(table2.seed)) columns[c] = "from the matrix seed";
|
|
19238
19625
|
return {
|
|
19239
19626
|
name: display,
|
|
19240
19627
|
rls,
|
|
@@ -19242,16 +19629,16 @@ async function runTable(db, table, badRoles, roleChecks) {
|
|
|
19242
19629
|
cells
|
|
19243
19630
|
};
|
|
19244
19631
|
}
|
|
19245
|
-
var
|
|
19632
|
+
var import_node_child_process8, import_node_crypto3, import_promises29, import_node_net, import_node_path36, import_node_util6, NON_AUTHORITATIVE_LABEL, PGLITE_INSTALL_HINT, ENV_ALLOWLIST, PERMISSION_DENIED, RLS_REFUSAL, DEFAULT_TIMEOUTS, RlsInputError, normalise, quoteIdent2, firstLine2, plural3, NUMBERS, ACTOR_SESSION, changedRows, isRlsRefusal, isPrivilegeRefusal, SELF, PROBE_EXCLUSIONS, SEED_HINTS;
|
|
19246
19633
|
var init_rlsRunner = __esm({
|
|
19247
19634
|
"src/rls/rlsRunner.ts"() {
|
|
19248
19635
|
"use strict";
|
|
19249
|
-
|
|
19636
|
+
import_node_child_process8 = require("node:child_process");
|
|
19250
19637
|
import_node_crypto3 = require("node:crypto");
|
|
19251
|
-
|
|
19638
|
+
import_promises29 = require("node:fs/promises");
|
|
19252
19639
|
import_node_net = __toESM(require("node:net"));
|
|
19253
|
-
|
|
19254
|
-
|
|
19640
|
+
import_node_path36 = require("node:path");
|
|
19641
|
+
import_node_util6 = require("node:util");
|
|
19255
19642
|
init_safeGit();
|
|
19256
19643
|
init_permissionsMatrix();
|
|
19257
19644
|
init_supabaseImitation();
|
|
@@ -19267,15 +19654,15 @@ var init_rlsRunner = __esm({
|
|
|
19267
19654
|
};
|
|
19268
19655
|
normalise = (text) => text.replace(/\r\n/g, "\n");
|
|
19269
19656
|
quoteIdent2 = (name) => `"${name.replace(/"/g, '""')}"`;
|
|
19270
|
-
|
|
19271
|
-
|
|
19657
|
+
firstLine2 = (message) => message.split("\n")[0].trim();
|
|
19658
|
+
plural3 = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
|
|
19272
19659
|
NUMBERS = { a: ["1001"], b: ["1002"], i: ["1003"], u: ["1004", "7", "1"] };
|
|
19273
19660
|
ACTOR_SESSION = {
|
|
19274
19661
|
owner: { role: "authenticated", uid: OWNER_ID, claims: { sub: OWNER_ID, role: "authenticated", aud: "authenticated", email: "owner@example.test" } },
|
|
19275
19662
|
other_user: { role: "authenticated", uid: OTHER_USER_ID, claims: { sub: OTHER_USER_ID, role: "authenticated", aud: "authenticated", email: "other-user@example.test" } },
|
|
19276
19663
|
anon: { role: "anon", uid: null, claims: { role: "anon" } }
|
|
19277
19664
|
};
|
|
19278
|
-
changedRows = (n) => `${
|
|
19665
|
+
changedRows = (n) => `${plural3(n ?? 0, "row")} changed`;
|
|
19279
19666
|
isRlsRefusal = (run) => run.error?.code === PERMISSION_DENIED && RLS_REFUSAL.test(run.error.message);
|
|
19280
19667
|
isPrivilegeRefusal = (run) => run.error?.code === PERMISSION_DENIED && !RLS_REFUSAL.test(run.error.message);
|
|
19281
19668
|
SELF = Symbol("self");
|
|
@@ -19322,25 +19709,25 @@ function renderRlsText(result) {
|
|
|
19322
19709
|
lines.push("");
|
|
19323
19710
|
if (result.engine.error) lines.push(`(${result.engine.error})`);
|
|
19324
19711
|
}
|
|
19325
|
-
for (const
|
|
19712
|
+
for (const table2 of result.tables) {
|
|
19326
19713
|
lines.push("");
|
|
19327
|
-
const rls =
|
|
19328
|
-
lines.push(`${
|
|
19329
|
-
if (
|
|
19330
|
-
const reused = [
|
|
19714
|
+
const rls = table2.rls ? `RLS ${table2.rls.enabled ? "on" : "off"}${table2.rls.forced ? ", forced" : ""}, owner ${table2.rls.owner}` : "not inspected";
|
|
19715
|
+
lines.push(`${table2.name} (${rls})`);
|
|
19716
|
+
if (table2.seed) {
|
|
19717
|
+
const reused = [table2.seed.reused.owner ? "the owner" : "", table2.seed.reused.otherUser ? "the other user" : ""].filter(Boolean);
|
|
19331
19718
|
lines.push(
|
|
19332
|
-
` One row for the owner and one for another user (${
|
|
19719
|
+
` One row for the owner and one for another user (${table2.seed.rowsBefore} rows in the table)${reused.length > 0 ? `; reused the rows the migrations created for ${reused.join(" and ")}` : ""}.`
|
|
19333
19720
|
);
|
|
19334
19721
|
}
|
|
19335
|
-
if (
|
|
19336
|
-
const tested =
|
|
19722
|
+
if (table2.unknownReason) lines.push(` Unknown: ${table2.unknownReason}`);
|
|
19723
|
+
const tested = table2.cells.filter((c) => c.actor !== "service_role");
|
|
19337
19724
|
const actorWidth = 14;
|
|
19338
19725
|
const actionWidth = 10;
|
|
19339
19726
|
lines.push(` ${"actor".padEnd(actorWidth)}${"action".padEnd(actionWidth)}${"expected".padEnd(10)}${"result".padEnd(9)}observed`);
|
|
19340
|
-
for (const
|
|
19341
|
-
lines.push(` ${
|
|
19727
|
+
for (const cell2 of tested) {
|
|
19728
|
+
lines.push(` ${cell2.actor.padEnd(actorWidth)}${cell2.action.padEnd(actionWidth)}${cell2.expected.padEnd(10)}${cell2.outcome.padEnd(9)}${cell2.observed.summary}`);
|
|
19342
19729
|
}
|
|
19343
|
-
const service =
|
|
19730
|
+
const service = table2.cells.filter((c) => c.actor === "service_role");
|
|
19344
19731
|
if (service.length > 0) lines.push(` ${"service_role".padEnd(actorWidth)}${"all".padEnd(actionWidth)}${"excluded".padEnd(10)}${"N/A".padEnd(9)}bypasses RLS by design, not tested`);
|
|
19345
19732
|
}
|
|
19346
19733
|
const s = result.summary;
|
|
@@ -19348,8 +19735,8 @@ function renderRlsText(result) {
|
|
|
19348
19735
|
lines.push(`Result: ${s.pass} Pass, ${s.fail} Fail, ${s.unknown} Unknown, ${s.notApplicable} N/A \xB7 ${s.pass + s.fail} of ${s.tested} tested cells observed (${s.total} cells in the matrix)`);
|
|
19349
19736
|
lines.push(result.verdict);
|
|
19350
19737
|
const failed = result.tables.flatMap((t) => t.cells).filter((c) => c.outcome === "Fail");
|
|
19351
|
-
for (const
|
|
19352
|
-
lines.push(` Fail ${
|
|
19738
|
+
for (const cell2 of failed) {
|
|
19739
|
+
lines.push(` Fail ${cell2.actor} ${cell2.action} on ${cell2.table}: ${cell2.reason}`);
|
|
19353
19740
|
}
|
|
19354
19741
|
const unknown = result.tables.flatMap((t) => t.cells).filter((c) => c.outcome === "Unknown");
|
|
19355
19742
|
const unknownReasons = [...new Set(unknown.map((c) => c.reason))];
|
|
@@ -20078,7 +20465,7 @@ function buildMissionCards(artifact, providers) {
|
|
|
20078
20465
|
};
|
|
20079
20466
|
}
|
|
20080
20467
|
function projectNameFromPath(pathValue) {
|
|
20081
|
-
return (0,
|
|
20468
|
+
return (0, import_node_path37.basename)(pathValue.replace(/[\\/]$/, "")) || "current repo";
|
|
20082
20469
|
}
|
|
20083
20470
|
function gapSearchText(gap) {
|
|
20084
20471
|
return [
|
|
@@ -20489,19 +20876,19 @@ function mapArtifactToLocalUiState(artifact, presentProviderIds) {
|
|
|
20489
20876
|
selectedProviderId: selectedProvider.id,
|
|
20490
20877
|
currentPrompt: selectedProvider.nextFix?.prompt ?? "",
|
|
20491
20878
|
artifacts: {
|
|
20492
|
-
tasklist: (0,
|
|
20493
|
-
contextMap: (0,
|
|
20494
|
-
gateResult: (0,
|
|
20495
|
-
lastScan: (0,
|
|
20879
|
+
tasklist: (0, import_node_path37.join)(".viberaven", "agent-tasklist.md"),
|
|
20880
|
+
contextMap: (0, import_node_path37.join)(".viberaven", "context-map.json"),
|
|
20881
|
+
gateResult: (0, import_node_path37.join)(".viberaven", "gate-result.json"),
|
|
20882
|
+
lastScan: (0, import_node_path37.join)(".viberaven", "last-scan.json")
|
|
20496
20883
|
},
|
|
20497
20884
|
empty: false
|
|
20498
20885
|
};
|
|
20499
20886
|
}
|
|
20500
|
-
var
|
|
20887
|
+
var import_node_path37, CONNECT_PROVIDER_PRIORITY, providerDashboardUrls, providerHints, PROVIDER_OWNED_GAP;
|
|
20501
20888
|
var init_artifactAdapter = __esm({
|
|
20502
20889
|
"src/local-ui/artifactAdapter.ts"() {
|
|
20503
20890
|
"use strict";
|
|
20504
|
-
|
|
20891
|
+
import_node_path37 = require("node:path");
|
|
20505
20892
|
init_providerLogos();
|
|
20506
20893
|
init_buildTaskList();
|
|
20507
20894
|
init_prompts();
|
|
@@ -20804,18 +21191,18 @@ var init_planLimits = __esm({
|
|
|
20804
21191
|
// src/hosted/credentials.ts
|
|
20805
21192
|
function defaultConfigDir(env = process.env, platform = process.platform) {
|
|
20806
21193
|
const override = env.VIBERAVEN_CONFIG_DIR?.trim();
|
|
20807
|
-
if (override) return (0,
|
|
21194
|
+
if (override) return (0, import_node_path38.resolve)(override);
|
|
20808
21195
|
if (platform === "win32") {
|
|
20809
|
-
return (0,
|
|
21196
|
+
return (0, import_node_path38.join)(env.APPDATA?.trim() || (0, import_node_path38.join)((0, import_node_os2.homedir)(), "AppData", "Roaming"), "viberaven");
|
|
20810
21197
|
}
|
|
20811
21198
|
if (platform === "darwin") {
|
|
20812
|
-
return (0,
|
|
21199
|
+
return (0, import_node_path38.join)((0, import_node_os2.homedir)(), "Library", "Application Support", "viberaven");
|
|
20813
21200
|
}
|
|
20814
|
-
return (0,
|
|
21201
|
+
return (0, import_node_path38.join)(env.XDG_CONFIG_HOME?.trim() || (0, import_node_path38.join)((0, import_node_os2.homedir)(), ".config"), "viberaven");
|
|
20815
21202
|
}
|
|
20816
21203
|
function isInside(child, parent) {
|
|
20817
|
-
const rel = (0,
|
|
20818
|
-
return rel === "" || !rel.startsWith("..") && !(0,
|
|
21204
|
+
const rel = (0, import_node_path38.relative)((0, import_node_path38.resolve)(parent), (0, import_node_path38.resolve)(child));
|
|
21205
|
+
return rel === "" || !rel.startsWith("..") && !(0, import_node_path38.isAbsolute)(rel);
|
|
20819
21206
|
}
|
|
20820
21207
|
async function retryTransient(work) {
|
|
20821
21208
|
for (let attempt = 1; ; attempt += 1) {
|
|
@@ -20828,15 +21215,15 @@ async function retryTransient(work) {
|
|
|
20828
21215
|
}
|
|
20829
21216
|
}
|
|
20830
21217
|
}
|
|
20831
|
-
var
|
|
21218
|
+
var import_node_child_process9, import_node_crypto4, import_promises30, import_node_os2, import_node_path38, CREDENTIALS_FILE_NAME, CredentialLocationError, CredentialPermissionError, restrictWithIcacls, TRANSIENT_FS_CODES, FS_ATTEMPTS, FS_BACKOFF_MS, CredentialStore;
|
|
20832
21219
|
var init_credentials = __esm({
|
|
20833
21220
|
"src/hosted/credentials.ts"() {
|
|
20834
21221
|
"use strict";
|
|
20835
|
-
|
|
21222
|
+
import_node_child_process9 = require("node:child_process");
|
|
20836
21223
|
import_node_crypto4 = require("node:crypto");
|
|
20837
|
-
|
|
20838
|
-
|
|
20839
|
-
|
|
21224
|
+
import_promises30 = require("node:fs/promises");
|
|
21225
|
+
import_node_os2 = require("node:os");
|
|
21226
|
+
import_node_path38 = require("node:path");
|
|
20840
21227
|
CREDENTIALS_FILE_NAME = "credentials.json";
|
|
20841
21228
|
CredentialLocationError = class extends Error {
|
|
20842
21229
|
};
|
|
@@ -20847,10 +21234,10 @@ var init_credentials = __esm({
|
|
|
20847
21234
|
}
|
|
20848
21235
|
};
|
|
20849
21236
|
restrictWithIcacls = (path) => {
|
|
20850
|
-
const name = (0,
|
|
21237
|
+
const name = (0, import_node_os2.userInfo)().username;
|
|
20851
21238
|
const user = process.env.USERDOMAIN ? `${process.env.USERDOMAIN}\\${name}` : name;
|
|
20852
21239
|
return new Promise((resolveRestrict, reject) => {
|
|
20853
|
-
(0,
|
|
21240
|
+
(0, import_node_child_process9.execFile)("icacls", [path, "/inheritance:r", "/grant:r", `${user}:F`], { windowsHide: true, timeout: 1e4 }, (error) => {
|
|
20854
21241
|
if (error) reject(error);
|
|
20855
21242
|
else resolveRestrict();
|
|
20856
21243
|
});
|
|
@@ -20863,9 +21250,9 @@ var init_credentials = __esm({
|
|
|
20863
21250
|
constructor(configDir, workspacePath, options = {}) {
|
|
20864
21251
|
this.configDir = configDir;
|
|
20865
21252
|
this.workspacePath = workspacePath;
|
|
20866
|
-
this.path = (0,
|
|
21253
|
+
this.path = (0, import_node_path38.join)(configDir, CREDENTIALS_FILE_NAME);
|
|
20867
21254
|
this.restrict = options.restrict !== void 0 ? options.restrict : process.platform === "win32" ? restrictWithIcacls : null;
|
|
20868
|
-
this.fs = { readFile: (path, encoding) => (0,
|
|
21255
|
+
this.fs = { readFile: (path, encoding) => (0, import_promises30.readFile)(path, encoding), rename: (from, to) => (0, import_promises30.rename)(from, to), rm: (path) => (0, import_promises30.rm)(path, { force: true }), ...options.fs };
|
|
20869
21256
|
}
|
|
20870
21257
|
path;
|
|
20871
21258
|
restrict;
|
|
@@ -20914,15 +21301,15 @@ var init_credentials = __esm({
|
|
|
20914
21301
|
`Refusing to store the VibeRaven sign-in inside this project (${this.configDir}). Set VIBERAVEN_CONFIG_DIR to a folder outside the repo.`
|
|
20915
21302
|
);
|
|
20916
21303
|
}
|
|
20917
|
-
await (0,
|
|
21304
|
+
await (0, import_promises30.mkdir)(this.configDir, { recursive: true, mode: 448 });
|
|
20918
21305
|
const temp = `${this.path}.${(0, import_node_crypto4.randomUUID)()}.tmp`;
|
|
20919
|
-
await (0,
|
|
21306
|
+
await (0, import_promises30.writeFile)(temp, `${JSON.stringify(body, null, 2)}
|
|
20920
21307
|
`, { encoding: "utf8", mode: 384 });
|
|
20921
|
-
await (0,
|
|
21308
|
+
await (0, import_promises30.chmod)(temp, 384).catch(() => void 0);
|
|
20922
21309
|
try {
|
|
20923
21310
|
if (this.restrict) await this.restrict(temp);
|
|
20924
21311
|
} catch (error) {
|
|
20925
|
-
await (0,
|
|
21312
|
+
await (0, import_promises30.rm)(temp, { force: true });
|
|
20926
21313
|
const code = error?.code;
|
|
20927
21314
|
const toolMissing = code === "ENOENT";
|
|
20928
21315
|
throw new CredentialPermissionError(
|
|
@@ -20933,17 +21320,17 @@ var init_credentials = __esm({
|
|
|
20933
21320
|
if (guard) {
|
|
20934
21321
|
const onDisk = await this.readFor(guard.apiBaseUrl);
|
|
20935
21322
|
if (onDisk?.accessToken !== guard.accessToken || guard.isCurrent && !guard.isCurrent()) {
|
|
20936
|
-
await (0,
|
|
21323
|
+
await (0, import_promises30.rm)(temp, { force: true });
|
|
20937
21324
|
return false;
|
|
20938
21325
|
}
|
|
20939
21326
|
}
|
|
20940
21327
|
try {
|
|
20941
21328
|
await retryTransient(() => this.fs.rename(temp, this.path));
|
|
20942
21329
|
} catch (error) {
|
|
20943
|
-
await (0,
|
|
21330
|
+
await (0, import_promises30.rm)(temp, { force: true });
|
|
20944
21331
|
throw error;
|
|
20945
21332
|
}
|
|
20946
|
-
await (0,
|
|
21333
|
+
await (0, import_promises30.chmod)(this.path, 384).catch(() => void 0);
|
|
20947
21334
|
return true;
|
|
20948
21335
|
}
|
|
20949
21336
|
/** Keeps the stored plan label in step with what the API last said. */
|
|
@@ -21312,7 +21699,7 @@ async function buildHostedCheckPayload(workspacePath) {
|
|
|
21312
21699
|
throw new Error("Could not assemble the full check request.");
|
|
21313
21700
|
}
|
|
21314
21701
|
function finalizePayload(payload) {
|
|
21315
|
-
const folder = (0,
|
|
21702
|
+
const folder = (0, import_node_path39.basename)(payload.workspacePath.split("\\").join("/").replace(/\/+$/, "")) || "project";
|
|
21316
21703
|
const redacted = redactPayload({ ...payload, workspacePath: folder });
|
|
21317
21704
|
return { payload: redacted.payload, redactions: redacted.count };
|
|
21318
21705
|
}
|
|
@@ -21343,13 +21730,13 @@ function splitSections(prompt) {
|
|
|
21343
21730
|
chars: section.length
|
|
21344
21731
|
}));
|
|
21345
21732
|
}
|
|
21346
|
-
var
|
|
21733
|
+
var import_node_path39, PayloadCaptured;
|
|
21347
21734
|
var init_payload = __esm({
|
|
21348
21735
|
"src/hosted/payload.ts"() {
|
|
21349
21736
|
"use strict";
|
|
21350
21737
|
init_fileScanner();
|
|
21351
21738
|
init_orchestrator();
|
|
21352
|
-
|
|
21739
|
+
import_node_path39 = require("node:path");
|
|
21353
21740
|
init_config();
|
|
21354
21741
|
init_redact();
|
|
21355
21742
|
PayloadCaptured = class extends Error {
|
|
@@ -21387,7 +21774,7 @@ function shapeResult(response, now = /* @__PURE__ */ new Date()) {
|
|
|
21387
21774
|
const usage = toUsage(response.usage);
|
|
21388
21775
|
const freeKeys = new Set(FREE_TRIAL_UNLOCKED_MAP_CATEGORY_KEYS);
|
|
21389
21776
|
const unlocked = (key) => usage.plan === "pro" || freeKeys.has(key) && response.usage.unlockedMapCategoryKeys.includes(key);
|
|
21390
|
-
const sorted = [...output.gaps].sort((a, b3) =>
|
|
21777
|
+
const sorted = [...output.gaps].sort((a, b3) => SEVERITY_RANK4[a.severity] - SEVERITY_RANK4[b3.severity]);
|
|
21391
21778
|
const areas = PRODUCTION_MAP_LANES.map((lane) => {
|
|
21392
21779
|
const gaps = sorted.filter((gap) => gap.primaryMapCategory === lane.extensionKey);
|
|
21393
21780
|
const locked = !unlocked(lane.extensionKey);
|
|
@@ -21425,7 +21812,7 @@ function readServerMessage(body) {
|
|
|
21425
21812
|
return body.slice(0, 300);
|
|
21426
21813
|
}
|
|
21427
21814
|
}
|
|
21428
|
-
var import_node_crypto5, PRICING_URL, FREE_LIMIT_MESSAGE, PREVIEW_TTL_MS, STATUS_TIMEOUT_MS, STATUS_CACHE_MS, MAX_PREVIEWS, TRANSIENT_REASONS,
|
|
21815
|
+
var import_node_crypto5, PRICING_URL, FREE_LIMIT_MESSAGE, PREVIEW_TTL_MS, STATUS_TIMEOUT_MS, STATUS_CACHE_MS, MAX_PREVIEWS, TRANSIENT_REASONS, SEVERITY_RANK4, FullCheckService;
|
|
21429
21816
|
var init_fullCheck = __esm({
|
|
21430
21817
|
"src/hosted/fullCheck.ts"() {
|
|
21431
21818
|
"use strict";
|
|
@@ -21444,7 +21831,7 @@ var init_fullCheck = __esm({
|
|
|
21444
21831
|
STATUS_CACHE_MS = 5e3;
|
|
21445
21832
|
MAX_PREVIEWS = 5;
|
|
21446
21833
|
TRANSIENT_REASONS = /* @__PURE__ */ new Set(["network", "timeout", "server", "rate_limited", "busy"]);
|
|
21447
|
-
|
|
21834
|
+
SEVERITY_RANK4 = { critical: 0, warning: 1, info: 2 };
|
|
21448
21835
|
FullCheckService = class {
|
|
21449
21836
|
constructor(workspacePath, options = {}) {
|
|
21450
21837
|
this.workspacePath = workspacePath;
|
|
@@ -21815,10 +22202,10 @@ async function connectProviderMcp(cwd, providerId) {
|
|
|
21815
22202
|
if (!recipe) {
|
|
21816
22203
|
return { ok: false, message: `No known MCP server for ${providerId} yet. Add one manually to .mcp.json.` };
|
|
21817
22204
|
}
|
|
21818
|
-
const configPath = (0,
|
|
22205
|
+
const configPath = (0, import_node_path40.join)(cwd, ".mcp.json");
|
|
21819
22206
|
let config = {};
|
|
21820
22207
|
try {
|
|
21821
|
-
const raw = await (0,
|
|
22208
|
+
const raw = await (0, import_promises31.readFile)(configPath, "utf8");
|
|
21822
22209
|
const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
|
|
21823
22210
|
if (!isRecord5(parsed) || parsed.mcpServers !== void 0 && !isRecord5(parsed.mcpServers)) {
|
|
21824
22211
|
return { ok: false, message: "Existing .mcp.json has an unsupported structure. It was left unchanged." };
|
|
@@ -21835,7 +22222,7 @@ async function connectProviderMcp(cwd, providerId) {
|
|
|
21835
22222
|
}
|
|
21836
22223
|
servers[providerId] = recipe.entry;
|
|
21837
22224
|
const next = { ...config, mcpServers: servers };
|
|
21838
|
-
await (0,
|
|
22225
|
+
await (0, import_promises31.writeFile)(configPath, `${JSON.stringify(next, null, 2)}
|
|
21839
22226
|
`, "utf8");
|
|
21840
22227
|
const suffix = recipe.note ? ` ${recipe.note}` : "";
|
|
21841
22228
|
return { ok: true, message: `Added ${providerId} MCP server to .mcp.json.${suffix}`, path: ".mcp.json" };
|
|
@@ -21888,7 +22275,7 @@ async function defaultScanRunner(flags, positional) {
|
|
|
21888
22275
|
}
|
|
21889
22276
|
async function readPackageJson(cwd) {
|
|
21890
22277
|
try {
|
|
21891
|
-
const raw = await (0,
|
|
22278
|
+
const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, "package.json"), "utf8");
|
|
21892
22279
|
const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
|
|
21893
22280
|
return isRecord5(parsed) ? parsed : {};
|
|
21894
22281
|
} catch {
|
|
@@ -21911,7 +22298,7 @@ async function listProjectFiles(cwd, maxFiles = 1800) {
|
|
|
21911
22298
|
if (files.length >= maxFiles) return;
|
|
21912
22299
|
let entries;
|
|
21913
22300
|
try {
|
|
21914
|
-
entries = await (0,
|
|
22301
|
+
entries = await (0, import_promises31.readdir)(dir, { withFileTypes: true });
|
|
21915
22302
|
} catch {
|
|
21916
22303
|
return;
|
|
21917
22304
|
}
|
|
@@ -21919,7 +22306,7 @@ async function listProjectFiles(cwd, maxFiles = 1800) {
|
|
|
21919
22306
|
if (files.length >= maxFiles) return;
|
|
21920
22307
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
21921
22308
|
if (entry.isDirectory()) {
|
|
21922
|
-
if (!skipped.has(entry.name)) await walk((0,
|
|
22309
|
+
if (!skipped.has(entry.name)) await walk((0, import_node_path40.join)(dir, entry.name), rel);
|
|
21923
22310
|
continue;
|
|
21924
22311
|
}
|
|
21925
22312
|
if (entry.isFile()) files.push(rel.toLowerCase());
|
|
@@ -21938,7 +22325,7 @@ async function readEnvKeys(cwd) {
|
|
|
21938
22325
|
const keys = /* @__PURE__ */ new Set();
|
|
21939
22326
|
for (const name of [".env.example", ".env"]) {
|
|
21940
22327
|
try {
|
|
21941
|
-
const raw = await (0,
|
|
22328
|
+
const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, name), "utf8");
|
|
21942
22329
|
for (const line of raw.split(/\r?\n/)) {
|
|
21943
22330
|
const trimmed = line.trim();
|
|
21944
22331
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -22032,7 +22419,7 @@ function applyProviderDetection(state, deps, files) {
|
|
|
22032
22419
|
}
|
|
22033
22420
|
function runGit(cwd, args, timeoutMs = 1500) {
|
|
22034
22421
|
return safeGitInvocation(cwd, args).then(
|
|
22035
|
-
(
|
|
22422
|
+
(git2) => spawnGit(cwd, git2.args, git2.env, timeoutMs),
|
|
22036
22423
|
(error) => {
|
|
22037
22424
|
if (error instanceof SafeGitRefusal) throw error;
|
|
22038
22425
|
return "";
|
|
@@ -22041,7 +22428,7 @@ function runGit(cwd, args, timeoutMs = 1500) {
|
|
|
22041
22428
|
}
|
|
22042
22429
|
function spawnGit(cwd, args, env, timeoutMs) {
|
|
22043
22430
|
return new Promise((resolveGit) => {
|
|
22044
|
-
const child = (0,
|
|
22431
|
+
const child = (0, import_node_child_process10.spawn)("git", args, {
|
|
22045
22432
|
cwd,
|
|
22046
22433
|
env,
|
|
22047
22434
|
windowsHide: true,
|
|
@@ -22104,7 +22491,7 @@ function parseGitShortstat(raw) {
|
|
|
22104
22491
|
}
|
|
22105
22492
|
function runGitCommand(cwd, args, timeoutMs = 5e3) {
|
|
22106
22493
|
return safeGitInvocation(cwd, args).then(
|
|
22107
|
-
(
|
|
22494
|
+
(git2) => spawnGitCommand(cwd, git2.args, git2.env, timeoutMs),
|
|
22108
22495
|
(error) => ({
|
|
22109
22496
|
code: 1,
|
|
22110
22497
|
stdout: "",
|
|
@@ -22114,7 +22501,7 @@ function runGitCommand(cwd, args, timeoutMs = 5e3) {
|
|
|
22114
22501
|
}
|
|
22115
22502
|
function spawnGitCommand(cwd, args, env, timeoutMs) {
|
|
22116
22503
|
return new Promise((resolveCommand) => {
|
|
22117
|
-
const child = (0,
|
|
22504
|
+
const child = (0, import_node_child_process10.spawn)("git", args, {
|
|
22118
22505
|
cwd,
|
|
22119
22506
|
env,
|
|
22120
22507
|
windowsHide: true,
|
|
@@ -22256,7 +22643,7 @@ async function codexModelOptions() {
|
|
|
22256
22643
|
{ id: "gpt-5.4-mini", label: "GPT-5.4 Mini" }
|
|
22257
22644
|
];
|
|
22258
22645
|
try {
|
|
22259
|
-
const raw = await (0,
|
|
22646
|
+
const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)((0, import_node_os3.homedir)(), ".codex", "models_cache.json"), "utf8");
|
|
22260
22647
|
const parsed = JSON.parse(raw);
|
|
22261
22648
|
const list = Array.isArray(parsed) ? parsed : parsed.models ?? [];
|
|
22262
22649
|
const options = list.filter((model) => typeof model?.slug === "string").filter((model) => !/auto-review|reserve/i.test(model.slug)).map((model) => ({ id: model.slug, label: model.display_name?.trim() || model.slug }));
|
|
@@ -22280,7 +22667,7 @@ function cliModelDefaults(id) {
|
|
|
22280
22667
|
}
|
|
22281
22668
|
function detectCommand(command, cwd) {
|
|
22282
22669
|
return new Promise((resolveDetect) => {
|
|
22283
|
-
const child = (0,
|
|
22670
|
+
const child = (0, import_node_child_process10.spawn)(process.platform === "win32" ? "where.exe" : "sh", process.platform === "win32" ? [command] : ["-lc", `command -v ${command}`], {
|
|
22284
22671
|
cwd,
|
|
22285
22672
|
windowsHide: true,
|
|
22286
22673
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -22307,7 +22694,7 @@ function commandMissingFromOutput(text) {
|
|
|
22307
22694
|
return /not recognized|not found|unknown command|no such file|command not found|is not recognized as an internal or external command/.test(text.toLowerCase());
|
|
22308
22695
|
}
|
|
22309
22696
|
function redactProbeOutput(output) {
|
|
22310
|
-
const home = (0,
|
|
22697
|
+
const home = (0, import_node_os3.homedir)();
|
|
22311
22698
|
const homeVariants = [home, home.split("\\").join("\\\\"), home.split("\\").join("/")].filter((value) => value.length > 3);
|
|
22312
22699
|
let scrubbed = output;
|
|
22313
22700
|
for (const variant of homeVariants) scrubbed = scrubbed.split(variant).join("~");
|
|
@@ -22367,7 +22754,7 @@ function setupCommandFor(cliId, step) {
|
|
|
22367
22754
|
function runShellCommand(command, cwd, timeoutMs = 18e4) {
|
|
22368
22755
|
return new Promise((resolveRun) => {
|
|
22369
22756
|
const useCmd = process.platform === "win32";
|
|
22370
|
-
const child = (0,
|
|
22757
|
+
const child = (0, import_node_child_process10.spawn)(useCmd ? process.env.ComSpec || "cmd.exe" : "sh", useCmd ? ["/d", "/s", "/c", command] : ["-lc", command], {
|
|
22371
22758
|
cwd,
|
|
22372
22759
|
windowsHide: true,
|
|
22373
22760
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -22401,7 +22788,7 @@ function openSystemTerminal(command, cwd) {
|
|
|
22401
22788
|
const fullCommand = process.platform === "win32" ? `cd /d ${quotedCwd} && ${command}` : `cd ${quotedCwd} && ${command}`;
|
|
22402
22789
|
let child;
|
|
22403
22790
|
if (process.platform === "win32") {
|
|
22404
|
-
child = (0,
|
|
22791
|
+
child = (0, import_node_child_process10.spawn)("cmd.exe", ["/c", "start", "cmd.exe", "/k", fullCommand], {
|
|
22405
22792
|
cwd,
|
|
22406
22793
|
windowsHide: true,
|
|
22407
22794
|
stdio: "ignore",
|
|
@@ -22409,9 +22796,9 @@ function openSystemTerminal(command, cwd) {
|
|
|
22409
22796
|
});
|
|
22410
22797
|
} else if (process.platform === "darwin") {
|
|
22411
22798
|
const script = `tell application "Terminal" to do script "${fullCommand.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
22412
|
-
child = (0,
|
|
22799
|
+
child = (0, import_node_child_process10.spawn)("osascript", ["-e", script], { cwd, windowsHide: true, stdio: "ignore", detached: true });
|
|
22413
22800
|
} else {
|
|
22414
|
-
child = (0,
|
|
22801
|
+
child = (0, import_node_child_process10.spawn)("x-terminal-emulator", ["-e", `bash -lc ${JSON.stringify(fullCommand)}`], {
|
|
22415
22802
|
cwd,
|
|
22416
22803
|
windowsHide: true,
|
|
22417
22804
|
stdio: "ignore",
|
|
@@ -22459,19 +22846,19 @@ function isPlaceholderProjectName(value) {
|
|
|
22459
22846
|
async function readMcpEvidence(cwd) {
|
|
22460
22847
|
const candidates = MCP_CONFIG_PATHS.map((relativePath) => ({
|
|
22461
22848
|
source: relativePath.replace(/\\/g, "/"),
|
|
22462
|
-
path: (0,
|
|
22849
|
+
path: (0, import_node_path40.resolve)(cwd, relativePath)
|
|
22463
22850
|
}));
|
|
22464
22851
|
const home = process.env.USERPROFILE || process.env.HOME;
|
|
22465
22852
|
if (home) {
|
|
22466
22853
|
candidates.push({
|
|
22467
22854
|
source: "~/.codex/config.toml",
|
|
22468
|
-
path: (0,
|
|
22855
|
+
path: (0, import_node_path40.resolve)(home, ".codex", "config.toml")
|
|
22469
22856
|
});
|
|
22470
22857
|
}
|
|
22471
22858
|
const evidence = [];
|
|
22472
22859
|
for (const candidate of candidates) {
|
|
22473
22860
|
try {
|
|
22474
|
-
const text = await (0,
|
|
22861
|
+
const text = await (0, import_promises31.readFile)(candidate.path, "utf8");
|
|
22475
22862
|
if (/mcp/i.test(text)) evidence.push({ source: candidate.source, text });
|
|
22476
22863
|
} catch {
|
|
22477
22864
|
}
|
|
@@ -22562,7 +22949,7 @@ async function enrichProjectMetadata(state, cwd) {
|
|
|
22562
22949
|
async function readState(cwd) {
|
|
22563
22950
|
let state;
|
|
22564
22951
|
try {
|
|
22565
|
-
const raw = await (0,
|
|
22952
|
+
const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, ".viberaven", "last-scan.json"), "utf8");
|
|
22566
22953
|
const packageJson = await readPackageJson(cwd);
|
|
22567
22954
|
const deps = dependencyNames(packageJson);
|
|
22568
22955
|
const files = await listProjectFiles(cwd);
|
|
@@ -22808,7 +23195,7 @@ function quoteWindowsCmdArg(value) {
|
|
|
22808
23195
|
}
|
|
22809
23196
|
async function whereExe(command, cwd) {
|
|
22810
23197
|
return new Promise((resolvePath) => {
|
|
22811
|
-
const child = (0,
|
|
23198
|
+
const child = (0, import_node_child_process10.spawn)("where.exe", [command], {
|
|
22812
23199
|
cwd,
|
|
22813
23200
|
windowsHide: true,
|
|
22814
23201
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -22829,7 +23216,7 @@ async function whereExe(command, cwd) {
|
|
|
22829
23216
|
}
|
|
22830
23217
|
function expandShimPath(value, base) {
|
|
22831
23218
|
const relative8 = value.replace(/^%(?:dp0%\\+|~dp0\\*)/i, "");
|
|
22832
|
-
return relative8 === value ? value : (0,
|
|
23219
|
+
return relative8 === value ? value : (0, import_node_path40.join)(base, ...relative8.split(/[\\/]+/));
|
|
22833
23220
|
}
|
|
22834
23221
|
function findShimTarget(lines, pattern, accept = () => true) {
|
|
22835
23222
|
for (const line of lines) {
|
|
@@ -22840,21 +23227,21 @@ function findShimTarget(lines, pattern, accept = () => true) {
|
|
|
22840
23227
|
}
|
|
22841
23228
|
async function resolveWindowsShimTarget(shimPath) {
|
|
22842
23229
|
try {
|
|
22843
|
-
const content = await (0,
|
|
22844
|
-
const base = (0,
|
|
23230
|
+
const content = await (0, import_promises31.readFile)(shimPath, "utf8");
|
|
23231
|
+
const base = (0, import_node_path40.dirname)(shimPath);
|
|
22845
23232
|
const lines = content.split(/\r?\n/);
|
|
22846
23233
|
const trailingArgs = (line, after) => [...line.slice(after).split("%*")[0].matchAll(/"([^"]*)"|([^\s"&|<>]+)/g)].map((token) => expandShimPath(token[1] ?? token[2], base)).filter((value) => value.length > 0 && !value.includes("%"));
|
|
22847
23234
|
const script = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.js)"/i);
|
|
22848
23235
|
if (script) {
|
|
22849
23236
|
return {
|
|
22850
23237
|
command: process.execPath,
|
|
22851
|
-
args: [(0,
|
|
23238
|
+
args: [(0, import_node_path40.join)(base, ...script.match[1].split(/[\\/]+/)), ...trailingArgs(script.line, script.match.index + script.match[0].length)]
|
|
22852
23239
|
};
|
|
22853
23240
|
}
|
|
22854
|
-
const binary = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.exe)"/i, (target) => (0,
|
|
23241
|
+
const binary = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.exe)"/i, (target) => (0, import_node_path40.basename)(target).toLowerCase() !== "node.exe");
|
|
22855
23242
|
if (binary) {
|
|
22856
23243
|
return {
|
|
22857
|
-
command: (0,
|
|
23244
|
+
command: (0, import_node_path40.join)(base, ...binary.match[1].split(/[\\/]+/)),
|
|
22858
23245
|
args: trailingArgs(binary.line, binary.match.index + binary.match[0].length)
|
|
22859
23246
|
};
|
|
22860
23247
|
}
|
|
@@ -22875,7 +23262,7 @@ function killProcessTree(pid, fallback) {
|
|
|
22875
23262
|
return;
|
|
22876
23263
|
}
|
|
22877
23264
|
if (process.platform === "win32") {
|
|
22878
|
-
const killer = (0,
|
|
23265
|
+
const killer = (0, import_node_child_process10.spawn)("taskkill", ["/pid", String(pid), "/T", "/F"], { windowsHide: true, stdio: "ignore" });
|
|
22879
23266
|
killer.once("error", fallback);
|
|
22880
23267
|
killer.once("close", (code) => {
|
|
22881
23268
|
if (code !== 0) fallback();
|
|
@@ -22900,7 +23287,7 @@ async function runAgentCommand(command, args, cwd, stdin, runId) {
|
|
|
22900
23287
|
const spawnCommand = shim?.command ?? (useCmdShim ? process.env.ComSpec || "cmd.exe" : command);
|
|
22901
23288
|
const spawnArgs = shim ? [...shim.args, ...args] : useCmdShim ? ["/d", "/s", "/c", [command, ...args.map(quoteWindowsCmdArg)].join(" ")] : args;
|
|
22902
23289
|
return new Promise((resolveRun) => {
|
|
22903
|
-
const child = (0,
|
|
23290
|
+
const child = (0, import_node_child_process10.spawn)(spawnCommand, spawnArgs, {
|
|
22904
23291
|
cwd,
|
|
22905
23292
|
windowsHide: true,
|
|
22906
23293
|
// A separate process group lets Stop and timeout terminate descendants on Unix too.
|
|
@@ -22979,8 +23366,8 @@ async function writeChatContextArtifact(cwd, request, prompt) {
|
|
|
22979
23366
|
"",
|
|
22980
23367
|
prompt
|
|
22981
23368
|
].filter((line) => typeof line === "string");
|
|
22982
|
-
await (0,
|
|
22983
|
-
await (0,
|
|
23369
|
+
await (0, import_promises31.mkdir)((0, import_node_path40.join)(cwd, ".viberaven"), { recursive: true });
|
|
23370
|
+
await (0, import_promises31.writeFile)((0, import_node_path40.join)(cwd, ".viberaven", "chat-context.md"), `${lines.join("\n")}
|
|
22984
23371
|
`, "utf8");
|
|
22985
23372
|
} catch {
|
|
22986
23373
|
}
|
|
@@ -23000,13 +23387,13 @@ async function runAgentChat(cwd, request) {
|
|
|
23000
23387
|
}
|
|
23001
23388
|
const prompt = await buildAgentChatPrompt(request, cwd);
|
|
23002
23389
|
await writeChatContextArtifact(cwd, request, prompt);
|
|
23003
|
-
const outputPath = request.cliId === "codex" ? (0,
|
|
23004
|
-
if (outputPath) await (0,
|
|
23390
|
+
const outputPath = request.cliId === "codex" ? (0, import_node_path40.join)(cwd, ".viberaven", `studio-agent-${Date.now()}-${Math.random().toString(36).slice(2)}.md`) : void 0;
|
|
23391
|
+
if (outputPath) await (0, import_promises31.mkdir)((0, import_node_path40.dirname)(outputPath), { recursive: true });
|
|
23005
23392
|
const { command, args, stdin } = commandForAgentChat(request, prompt, cwd, outputPath);
|
|
23006
23393
|
const result = await runAgentCommand(command, args, cwd, stdin, request.runId);
|
|
23007
23394
|
if (outputPath) {
|
|
23008
23395
|
try {
|
|
23009
|
-
const finalMessage = (await (0,
|
|
23396
|
+
const finalMessage = (await (0, import_promises31.readFile)(outputPath, "utf8")).trim();
|
|
23010
23397
|
if (finalMessage) return { ...result, output: finalMessage };
|
|
23011
23398
|
} catch {
|
|
23012
23399
|
}
|
|
@@ -23018,8 +23405,8 @@ function firstGapId(state) {
|
|
|
23018
23405
|
return state.providers.find((provider2) => provider2.nextFix?.id)?.nextFix?.id;
|
|
23019
23406
|
}
|
|
23020
23407
|
async function runLocalAction(cwd, intent) {
|
|
23021
|
-
const artifactDir = (0,
|
|
23022
|
-
await (0,
|
|
23408
|
+
const artifactDir = (0, import_node_path40.join)(cwd, ".viberaven");
|
|
23409
|
+
await (0, import_promises31.mkdir)(artifactDir, { recursive: true });
|
|
23023
23410
|
if (intent === "preview_rehearsal") {
|
|
23024
23411
|
const state2 = await readState(cwd);
|
|
23025
23412
|
const topProvider = state2.providers[0];
|
|
@@ -23060,7 +23447,7 @@ async function runLocalAction(cwd, intent) {
|
|
|
23060
23447
|
proof,
|
|
23061
23448
|
""
|
|
23062
23449
|
].join("\n");
|
|
23063
|
-
await (0,
|
|
23450
|
+
await (0, import_promises31.writeFile)((0, import_node_path40.join)(artifactDir, "preview-rehearsal.md"), markdown, "utf8");
|
|
23064
23451
|
return {
|
|
23065
23452
|
title: "Preview rehearsal ready",
|
|
23066
23453
|
summary: `${projectName}: ${gateLabel} with ${state2.providers.length} provider beat${state2.providers.length === 1 ? "" : "s"}`,
|
|
@@ -23070,7 +23457,7 @@ async function runLocalAction(cwd, intent) {
|
|
|
23070
23457
|
if (intent === "audit_vercel_supabase") {
|
|
23071
23458
|
const auditInput = await collectVercelSupabaseAuditInput(cwd);
|
|
23072
23459
|
const result2 = buildVercelSupabaseAudit(auditInput);
|
|
23073
|
-
await (0,
|
|
23460
|
+
await (0, import_promises31.writeFile)((0, import_node_path40.join)(artifactDir, "audit-vercel-supabase.txt"), `${renderVercelSupabaseAudit(result2)}
|
|
23074
23461
|
`, "utf8");
|
|
23075
23462
|
return {
|
|
23076
23463
|
title: result2.status === "pass" ? "Audit passed" : "Audit needs work",
|
|
@@ -23098,7 +23485,7 @@ async function runLocalAction(cwd, intent) {
|
|
|
23098
23485
|
const mode = intent === "heal_prompt" ? "prompt" : intent === "heal_apply" ? "apply" : "plan";
|
|
23099
23486
|
const result = await runHealCommand({ cwd, mode, gapId, yes: mode === "apply", noVerify: true });
|
|
23100
23487
|
if (mode === "apply") {
|
|
23101
|
-
await (0,
|
|
23488
|
+
await (0, import_promises31.writeFile)((0, import_node_path40.join)(artifactDir, "heal-apply-result.json"), `${JSON.stringify(result, null, 2)}
|
|
23102
23489
|
`, "utf8");
|
|
23103
23490
|
}
|
|
23104
23491
|
return {
|
|
@@ -23108,14 +23495,14 @@ async function runLocalAction(cwd, intent) {
|
|
|
23108
23495
|
};
|
|
23109
23496
|
}
|
|
23110
23497
|
async function sendArtifact(cwd, rel, response, contentType = "text/plain; charset=utf-8") {
|
|
23111
|
-
const absolute = (0,
|
|
23112
|
-
const root = (0,
|
|
23498
|
+
const absolute = (0, import_node_path40.resolve)(cwd, rel);
|
|
23499
|
+
const root = (0, import_node_path40.resolve)(cwd);
|
|
23113
23500
|
if (!absolute.startsWith(root)) {
|
|
23114
23501
|
sendText(response, 403, "Forbidden");
|
|
23115
23502
|
return;
|
|
23116
23503
|
}
|
|
23117
23504
|
try {
|
|
23118
|
-
const text = await (0,
|
|
23505
|
+
const text = await (0, import_promises31.readFile)(absolute, "utf8");
|
|
23119
23506
|
response.writeHead(200, { "content-type": contentType });
|
|
23120
23507
|
response.end(text);
|
|
23121
23508
|
} catch {
|
|
@@ -23124,7 +23511,7 @@ async function sendArtifact(cwd, rel, response, contentType = "text/plain; chars
|
|
|
23124
23511
|
}
|
|
23125
23512
|
async function sendReportArtifact(cwd, response) {
|
|
23126
23513
|
try {
|
|
23127
|
-
const text = await (0,
|
|
23514
|
+
const text = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, ".viberaven", "report.html"), "utf8");
|
|
23128
23515
|
response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
23129
23516
|
response.end(text);
|
|
23130
23517
|
return;
|
|
@@ -23251,7 +23638,7 @@ async function sendReportArtifact(cwd, response) {
|
|
|
23251
23638
|
}
|
|
23252
23639
|
async function sendExtensionIcon(cwd, response) {
|
|
23253
23640
|
try {
|
|
23254
|
-
const body = await (0,
|
|
23641
|
+
const body = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, "media", "extension-icon.png"));
|
|
23255
23642
|
sendBinary(response, 200, "image/png", body);
|
|
23256
23643
|
} catch {
|
|
23257
23644
|
sendText(response, 404, "Icon not found");
|
|
@@ -23264,15 +23651,15 @@ async function sendMangaAsset(pathname, response) {
|
|
|
23264
23651
|
return;
|
|
23265
23652
|
}
|
|
23266
23653
|
const candidates = [
|
|
23267
|
-
(0,
|
|
23654
|
+
(0, import_node_path40.join)(__dirname, "manga", name),
|
|
23268
23655
|
// installed + dev: <pkg>/dist/manga
|
|
23269
|
-
(0,
|
|
23656
|
+
(0, import_node_path40.join)(__dirname, "..", "assets", "manga", name),
|
|
23270
23657
|
// fallback: <pkg>/assets/manga (source)
|
|
23271
|
-
(0,
|
|
23658
|
+
(0, import_node_path40.join)(__dirname, "..", "..", "assets", "manga", name)
|
|
23272
23659
|
];
|
|
23273
23660
|
for (const candidate of candidates) {
|
|
23274
23661
|
try {
|
|
23275
|
-
const body = await (0,
|
|
23662
|
+
const body = await (0, import_promises31.readFile)(candidate);
|
|
23276
23663
|
response.setHeader("cache-control", "no-cache");
|
|
23277
23664
|
sendBinary(response, 200, contentTypeForAsset(candidate), body);
|
|
23278
23665
|
return;
|
|
@@ -23282,7 +23669,7 @@ async function sendMangaAsset(pathname, response) {
|
|
|
23282
23669
|
sendText(response, 404, "Asset not found");
|
|
23283
23670
|
}
|
|
23284
23671
|
function contentTypeForAsset(pathname) {
|
|
23285
|
-
switch ((0,
|
|
23672
|
+
switch ((0, import_node_path40.extname)(pathname).toLowerCase()) {
|
|
23286
23673
|
case ".svg":
|
|
23287
23674
|
return "image/svg+xml; charset=utf-8";
|
|
23288
23675
|
case ".png":
|
|
@@ -23299,14 +23686,14 @@ function contentTypeForAsset(pathname) {
|
|
|
23299
23686
|
}
|
|
23300
23687
|
async function sendBundledReportAsset(pathname, response) {
|
|
23301
23688
|
const rel = decodeURIComponent(pathname.replace(/^\/report\//, ""));
|
|
23302
|
-
const assetRoot = (0,
|
|
23303
|
-
const absolute = (0,
|
|
23689
|
+
const assetRoot = (0, import_node_path40.resolve)(getBundledReportAssetsDir());
|
|
23690
|
+
const absolute = (0, import_node_path40.resolve)(assetRoot, rel);
|
|
23304
23691
|
if (!absolute.startsWith(assetRoot)) {
|
|
23305
23692
|
sendText(response, 403, "Forbidden");
|
|
23306
23693
|
return;
|
|
23307
23694
|
}
|
|
23308
23695
|
try {
|
|
23309
|
-
const body = await (0,
|
|
23696
|
+
const body = await (0, import_promises31.readFile)(absolute);
|
|
23310
23697
|
sendBinary(response, 200, contentTypeForAsset(absolute), body);
|
|
23311
23698
|
} catch {
|
|
23312
23699
|
sendText(response, 404, "Asset not found");
|
|
@@ -23328,18 +23715,18 @@ async function bundleStackClient(entryPath) {
|
|
|
23328
23715
|
return output.text;
|
|
23329
23716
|
}
|
|
23330
23717
|
async function sendStackAppJs(response) {
|
|
23331
|
-
const distPath = (0,
|
|
23718
|
+
const distPath = (0, import_node_path40.join)(__dirname, "stack", "app.js");
|
|
23332
23719
|
try {
|
|
23333
|
-
const body = await (0,
|
|
23720
|
+
const body = await (0, import_promises31.readFile)(distPath);
|
|
23334
23721
|
response.writeHead(200, { "content-type": "application/javascript; charset=utf-8", "cache-control": "no-store" });
|
|
23335
23722
|
response.end(body);
|
|
23336
23723
|
return;
|
|
23337
23724
|
} catch {
|
|
23338
23725
|
}
|
|
23339
|
-
const entryPath = (0,
|
|
23726
|
+
const entryPath = (0, import_node_path40.join)(__dirname, "stack", "main.ts");
|
|
23340
23727
|
let mtimeMs;
|
|
23341
23728
|
try {
|
|
23342
|
-
mtimeMs = (await (0,
|
|
23729
|
+
mtimeMs = (await (0, import_promises31.stat)(entryPath)).mtimeMs;
|
|
23343
23730
|
} catch {
|
|
23344
23731
|
sendText(response, 404, "Stack client entry not found: src/local-ui/stack/main.ts is missing.");
|
|
23345
23732
|
return;
|
|
@@ -23358,7 +23745,7 @@ async function sendStackAppJs(response) {
|
|
|
23358
23745
|
}
|
|
23359
23746
|
async function sendStackAppCss(response) {
|
|
23360
23747
|
try {
|
|
23361
|
-
const body = await (0,
|
|
23748
|
+
const body = await (0, import_promises31.readFile)((0, import_node_path40.join)(__dirname, "stack", "stack.css"));
|
|
23362
23749
|
response.writeHead(200, { "content-type": "text/css; charset=utf-8", "cache-control": "no-store" });
|
|
23363
23750
|
response.end(body);
|
|
23364
23751
|
} catch {
|
|
@@ -23367,7 +23754,7 @@ async function sendStackAppCss(response) {
|
|
|
23367
23754
|
}
|
|
23368
23755
|
async function readProviderVerifyLedger(cwd) {
|
|
23369
23756
|
try {
|
|
23370
|
-
const raw = await (0,
|
|
23757
|
+
const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, ".viberaven", "provider-verify.json"), "utf8");
|
|
23371
23758
|
const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
|
|
23372
23759
|
return isRecord5(parsed) ? parsed : {};
|
|
23373
23760
|
} catch {
|
|
@@ -23377,8 +23764,8 @@ async function readProviderVerifyLedger(cwd) {
|
|
|
23377
23764
|
async function writeProviderVerifyEntry(cwd, providerId, entry) {
|
|
23378
23765
|
const ledger = await readProviderVerifyLedger(cwd);
|
|
23379
23766
|
ledger[providerId] = entry;
|
|
23380
|
-
await (0,
|
|
23381
|
-
await (0,
|
|
23767
|
+
await (0, import_promises31.mkdir)((0, import_node_path40.join)(cwd, ".viberaven"), { recursive: true });
|
|
23768
|
+
await (0, import_promises31.writeFile)((0, import_node_path40.join)(cwd, ".viberaven", "provider-verify.json"), `${JSON.stringify(ledger, null, 2)}
|
|
23382
23769
|
`, "utf8");
|
|
23383
23770
|
}
|
|
23384
23771
|
async function handleRequest(request, response, options) {
|
|
@@ -23421,6 +23808,18 @@ async function handleRequest(request, response, options) {
|
|
|
23421
23808
|
sendText(response, 404, "This VibeRaven surface has been removed. Use the Launch Canvas at /.");
|
|
23422
23809
|
return;
|
|
23423
23810
|
}
|
|
23811
|
+
if (request.method === "GET" && url.pathname === "/api/github-workflow") {
|
|
23812
|
+
sendJson(response, await githubWorkflowStatus(options.cwd));
|
|
23813
|
+
return;
|
|
23814
|
+
}
|
|
23815
|
+
if (request.method === "POST" && url.pathname === "/api/github-workflow") {
|
|
23816
|
+
try {
|
|
23817
|
+
sendJson(response, await writeGithubWorkflow(options.cwd));
|
|
23818
|
+
} catch (error) {
|
|
23819
|
+
sendJsonStatus(response, 500, { error: `Could not write the workflow: ${error instanceof Error ? error.message : String(error)}` });
|
|
23820
|
+
}
|
|
23821
|
+
return;
|
|
23822
|
+
}
|
|
23424
23823
|
if (request.method === "GET" && url.pathname === "/api/project") {
|
|
23425
23824
|
sendJson(response, await readState(options.cwd));
|
|
23426
23825
|
return;
|
|
@@ -23439,13 +23838,13 @@ async function handleRequest(request, response, options) {
|
|
|
23439
23838
|
return;
|
|
23440
23839
|
}
|
|
23441
23840
|
if (request.method === "GET" && url.pathname === "/api/architecture-map") {
|
|
23442
|
-
const mapPath = (0,
|
|
23443
|
-
const markdown = await (0,
|
|
23841
|
+
const mapPath = (0, import_node_path40.join)(options.cwd, ".viberaven", "architecture-map.md");
|
|
23842
|
+
const markdown = await (0, import_promises31.readFile)(mapPath, "utf8").catch(() => null);
|
|
23444
23843
|
if (markdown === null) {
|
|
23445
23844
|
sendJsonStatus(response, 404, { error: "No architecture map yet. Run a scan first (it is written on every scan)." });
|
|
23446
23845
|
return;
|
|
23447
23846
|
}
|
|
23448
|
-
const dataRaw = await (0,
|
|
23847
|
+
const dataRaw = await (0, import_promises31.readFile)((0, import_node_path40.join)(options.cwd, ".viberaven", "architecture-map.json"), "utf8").catch(() => null);
|
|
23449
23848
|
let data = null;
|
|
23450
23849
|
if (dataRaw) {
|
|
23451
23850
|
try {
|
|
@@ -23454,7 +23853,7 @@ async function handleRequest(request, response, options) {
|
|
|
23454
23853
|
data = null;
|
|
23455
23854
|
}
|
|
23456
23855
|
}
|
|
23457
|
-
const contextRaw = await (0,
|
|
23856
|
+
const contextRaw = await (0, import_promises31.readFile)((0, import_node_path40.join)(options.cwd, ".viberaven", "context-map.json"), "utf8").catch(() => null);
|
|
23458
23857
|
let gaps = [];
|
|
23459
23858
|
if (contextRaw) {
|
|
23460
23859
|
try {
|
|
@@ -23720,35 +24119,35 @@ async function handleRequest(request, response, options) {
|
|
|
23720
24119
|
return;
|
|
23721
24120
|
}
|
|
23722
24121
|
if (request.method === "GET" && url.pathname === "/api/artifacts/tasklist") {
|
|
23723
|
-
await sendArtifact(options.cwd, (0,
|
|
24122
|
+
await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "agent-tasklist.md"), response);
|
|
23724
24123
|
return;
|
|
23725
24124
|
}
|
|
23726
24125
|
if (request.method === "GET" && url.pathname === "/api/artifacts/playbook") {
|
|
23727
|
-
await sendArtifact(options.cwd, (0,
|
|
24126
|
+
await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "launch-playbook.md"), response);
|
|
23728
24127
|
return;
|
|
23729
24128
|
}
|
|
23730
24129
|
if (request.method === "GET" && url.pathname === "/api/artifacts/audit") {
|
|
23731
|
-
await sendArtifact(options.cwd, (0,
|
|
24130
|
+
await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "audit-vercel-supabase.txt"), response);
|
|
23732
24131
|
return;
|
|
23733
24132
|
}
|
|
23734
24133
|
if (request.method === "GET" && url.pathname === "/api/artifacts/cleanup") {
|
|
23735
|
-
await sendArtifact(options.cwd, (0,
|
|
24134
|
+
await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "context-cleanup.md"), response);
|
|
23736
24135
|
return;
|
|
23737
24136
|
}
|
|
23738
24137
|
if (request.method === "GET" && url.pathname === "/api/artifacts/preview") {
|
|
23739
|
-
await sendArtifact(options.cwd, (0,
|
|
24138
|
+
await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "preview-rehearsal.md"), response);
|
|
23740
24139
|
return;
|
|
23741
24140
|
}
|
|
23742
24141
|
if (request.method === "GET" && url.pathname === "/api/artifacts/heal-plan") {
|
|
23743
|
-
await sendArtifact(options.cwd, (0,
|
|
24142
|
+
await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "heal-plan.md"), response);
|
|
23744
24143
|
return;
|
|
23745
24144
|
}
|
|
23746
24145
|
if (request.method === "GET" && url.pathname === "/api/artifacts/heal-prompt") {
|
|
23747
|
-
await sendArtifact(options.cwd, (0,
|
|
24146
|
+
await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "heal-prompt.md"), response);
|
|
23748
24147
|
return;
|
|
23749
24148
|
}
|
|
23750
24149
|
if (request.method === "GET" && url.pathname === "/api/artifacts/heal-apply") {
|
|
23751
|
-
await sendArtifact(options.cwd, (0,
|
|
24150
|
+
await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "heal-apply-result.json"), response, "application/json; charset=utf-8");
|
|
23752
24151
|
return;
|
|
23753
24152
|
}
|
|
23754
24153
|
if (request.method === "GET" && url.pathname === "/api/artifacts/report") {
|
|
@@ -23774,12 +24173,12 @@ async function handleRequest(request, response, options) {
|
|
|
23774
24173
|
}
|
|
23775
24174
|
if (request.method === "GET" && url.pathname === "/vendor/anime.umd.min.js") {
|
|
23776
24175
|
for (const candidate of [
|
|
23777
|
-
(0,
|
|
23778
|
-
(0,
|
|
23779
|
-
(0,
|
|
24176
|
+
(0, import_node_path40.join)(__dirname, "vendor", "anime.umd.min.js"),
|
|
24177
|
+
(0, import_node_path40.join)(__dirname, "..", "assets", "vendor", "anime.umd.min.js"),
|
|
24178
|
+
(0, import_node_path40.join)(__dirname, "..", "..", "assets", "vendor", "anime.umd.min.js")
|
|
23780
24179
|
]) {
|
|
23781
24180
|
try {
|
|
23782
|
-
const body = await (0,
|
|
24181
|
+
const body = await (0, import_promises31.readFile)(candidate);
|
|
23783
24182
|
response.setHeader("cache-control", "public, max-age=604800, immutable");
|
|
23784
24183
|
sendBinary(response, 200, "text/javascript; charset=utf-8", body);
|
|
23785
24184
|
return;
|
|
@@ -23996,7 +24395,7 @@ function listen(server, startPort) {
|
|
|
23996
24395
|
});
|
|
23997
24396
|
}
|
|
23998
24397
|
async function startLocalUiServer(options) {
|
|
23999
|
-
const cwd = (0,
|
|
24398
|
+
const cwd = (0, import_node_path40.resolve)(options.cwd);
|
|
24000
24399
|
const scanRunner = options.scanRunner ?? defaultScanRunner;
|
|
24001
24400
|
const autoScan = { state: "idle" };
|
|
24002
24401
|
const fullCheck = new FullCheckService(cwd, options.fullCheck);
|
|
@@ -24013,7 +24412,7 @@ async function startLocalUiServer(options) {
|
|
|
24013
24412
|
});
|
|
24014
24413
|
const port = await listen(server, options.port ?? 4177);
|
|
24015
24414
|
guard.port = port;
|
|
24016
|
-
const hasArtifact = await (0,
|
|
24415
|
+
const hasArtifact = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, ".viberaven", "last-scan.json"), "utf8").then(
|
|
24017
24416
|
() => true,
|
|
24018
24417
|
() => false
|
|
24019
24418
|
);
|
|
@@ -24052,16 +24451,17 @@ async function waitForServerShutdown() {
|
|
|
24052
24451
|
process.once("SIGTERM", done);
|
|
24053
24452
|
});
|
|
24054
24453
|
}
|
|
24055
|
-
var import_node_http,
|
|
24454
|
+
var import_node_http, import_node_child_process10, import_node_crypto6, import_promises31, import_node_os3, import_node_path40, CLI_AGENT_SETUP, MCP_CONFIG_PATHS, MCP_PROVIDER_NEEDLES, MCP_CONNECT_COMMANDS, MCP_SERVER_RECIPES, STUDIO_TOKEN_HEADER, PROVIDER_IDS, STUDIO_STATUS_ARGS, CLI_AUTH_PROBE, AGENT_RUN_TIMEOUT_MS, activeAgentRuns, stackBundleCache;
|
|
24056
24455
|
var init_server = __esm({
|
|
24057
24456
|
"src/local-ui/server.ts"() {
|
|
24058
24457
|
"use strict";
|
|
24059
24458
|
import_node_http = require("node:http");
|
|
24060
|
-
|
|
24459
|
+
import_node_child_process10 = require("node:child_process");
|
|
24061
24460
|
import_node_crypto6 = require("node:crypto");
|
|
24062
|
-
|
|
24063
|
-
|
|
24064
|
-
|
|
24461
|
+
import_promises31 = require("node:fs/promises");
|
|
24462
|
+
import_node_os3 = require("node:os");
|
|
24463
|
+
import_node_path40 = require("node:path");
|
|
24464
|
+
init_initGithub();
|
|
24065
24465
|
init_cleanPlan();
|
|
24066
24466
|
init_audit();
|
|
24067
24467
|
init_heal();
|
|
@@ -24102,8 +24502,8 @@ var init_server = __esm({
|
|
|
24102
24502
|
MCP_CONFIG_PATHS = [
|
|
24103
24503
|
".mcp.json",
|
|
24104
24504
|
"mcp.json",
|
|
24105
|
-
(0,
|
|
24106
|
-
(0,
|
|
24505
|
+
(0, import_node_path40.join)(".cursor", "mcp.json"),
|
|
24506
|
+
(0, import_node_path40.join)(".vscode", "mcp.json")
|
|
24107
24507
|
];
|
|
24108
24508
|
MCP_PROVIDER_NEEDLES = {
|
|
24109
24509
|
supabase: ["supabase", "@supabase/mcp", "mcp-server-supabase"],
|
|
@@ -24272,11 +24672,11 @@ var init_nextActionBlock = __esm({
|
|
|
24272
24672
|
|
|
24273
24673
|
// src/providerMcpBridge.ts
|
|
24274
24674
|
function defaultMcpConfigPaths() {
|
|
24275
|
-
const home = (0,
|
|
24675
|
+
const home = (0, import_node_os4.homedir)();
|
|
24276
24676
|
return [
|
|
24277
|
-
(0,
|
|
24278
|
-
(0,
|
|
24279
|
-
(0,
|
|
24677
|
+
(0, import_node_path41.join)(home, ".config", "claude", "claude_desktop_config.json"),
|
|
24678
|
+
(0, import_node_path41.join)(home, ".cursor", "mcp.json"),
|
|
24679
|
+
(0, import_node_path41.join)(home, ".gemini", "antigravity", "mcp_config.json")
|
|
24280
24680
|
];
|
|
24281
24681
|
}
|
|
24282
24682
|
function resolveConfigPaths() {
|
|
@@ -24305,11 +24705,11 @@ function findServerEntry(servers, provider2) {
|
|
|
24305
24705
|
function findProviderMcpConfig(provider2, configPaths) {
|
|
24306
24706
|
const paths = configPaths ?? resolveConfigPaths();
|
|
24307
24707
|
for (const path of paths) {
|
|
24308
|
-
if (!(0,
|
|
24708
|
+
if (!(0, import_node_fs15.existsSync)(path)) {
|
|
24309
24709
|
continue;
|
|
24310
24710
|
}
|
|
24311
24711
|
try {
|
|
24312
|
-
const raw = JSON.parse((0,
|
|
24712
|
+
const raw = JSON.parse((0, import_node_fs15.readFileSync)(path, "utf8"));
|
|
24313
24713
|
const servers = parseMcpServers(raw);
|
|
24314
24714
|
if (!servers) {
|
|
24315
24715
|
continue;
|
|
@@ -24353,13 +24753,13 @@ async function verifyProviderGap(options) {
|
|
|
24353
24753
|
fallbackCommand: FALLBACK_COMMAND
|
|
24354
24754
|
};
|
|
24355
24755
|
}
|
|
24356
|
-
var
|
|
24756
|
+
var import_node_fs15, import_node_os4, import_node_path41, FALLBACK_COMMAND, SUPPORTED_PROVIDERS, configPathsOverride;
|
|
24357
24757
|
var init_providerMcpBridge = __esm({
|
|
24358
24758
|
"src/providerMcpBridge.ts"() {
|
|
24359
24759
|
"use strict";
|
|
24360
|
-
|
|
24361
|
-
|
|
24362
|
-
|
|
24760
|
+
import_node_fs15 = require("node:fs");
|
|
24761
|
+
import_node_os4 = require("node:os");
|
|
24762
|
+
import_node_path41 = require("node:path");
|
|
24363
24763
|
FALLBACK_COMMAND = "npx -y viberaven audit --vercel-supabase --json";
|
|
24364
24764
|
SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["supabase", "vercel"]);
|
|
24365
24765
|
}
|
|
@@ -24373,7 +24773,8 @@ __export(cli_exports, {
|
|
|
24373
24773
|
parseArgs: () => parseArgs,
|
|
24374
24774
|
printHelp: () => printHelp,
|
|
24375
24775
|
resolveDefaultEntrypointMode: () => resolveDefaultEntrypointMode,
|
|
24376
|
-
runScanCommand: () => runScanCommand
|
|
24776
|
+
runScanCommand: () => runScanCommand,
|
|
24777
|
+
stackInitialPath: () => stackInitialPath
|
|
24377
24778
|
});
|
|
24378
24779
|
module.exports = __toCommonJS(cli_exports);
|
|
24379
24780
|
function printHelp() {
|
|
@@ -24388,6 +24789,9 @@ Usage:
|
|
|
24388
24789
|
viberaven tui Interactive terminal menu
|
|
24389
24790
|
|
|
24390
24791
|
viberaven check [--json] [--quiet] [path]
|
|
24792
|
+
viberaven --stack supabase,vercel,stripe Open the Studio with these services already picked
|
|
24793
|
+
viberaven check --base <ref> [--markdown] What this change introduced and fixed, compared with <ref>
|
|
24794
|
+
viberaven init --github [--dry-run] Check every pull request in GitHub Actions
|
|
24391
24795
|
Local production check: one line per finding, exit 1 on blockers
|
|
24392
24796
|
|
|
24393
24797
|
viberaven fix [--gap <id>] [--dry-run] [path]
|
|
@@ -24537,12 +24941,20 @@ function isBooleanFlag(command, key) {
|
|
|
24537
24941
|
if (key === "open" && (command === "" || command === "scan" || command === "report")) return true;
|
|
24538
24942
|
if (key === "verify" && command === "") return true;
|
|
24539
24943
|
if (key === "quiet" && command === "check") return true;
|
|
24944
|
+
if (key === "markdown" && command === "check") return true;
|
|
24945
|
+
if (key === "github" && command === "init") return true;
|
|
24946
|
+
if (key === "force" && command === "init") return true;
|
|
24540
24947
|
if (key === "vercel-supabase" && command === "audit") return true;
|
|
24541
24948
|
if (key === "json" && command === "validate-npm-package") return true;
|
|
24542
24949
|
if (key === "dry-run" && (command === "init" || command === "fix")) return true;
|
|
24543
24950
|
if (key === "agents" && command === "doctor") return true;
|
|
24544
24951
|
return false;
|
|
24545
24952
|
}
|
|
24953
|
+
function stackInitialPath(flags) {
|
|
24954
|
+
if (typeof flags.stack !== "string") return void 0;
|
|
24955
|
+
const ids = flags.stack.split(",").map((id) => id.trim().toLowerCase()).filter(Boolean);
|
|
24956
|
+
return ids.length ? `/${setupFragment(ids)}` : void 0;
|
|
24957
|
+
}
|
|
24546
24958
|
function hasFlag(flags, key) {
|
|
24547
24959
|
return flags[key] === true || typeof flags[key] === "string";
|
|
24548
24960
|
}
|
|
@@ -24570,7 +24982,7 @@ async function cmdProviderVerify(flags, positional) {
|
|
|
24570
24982
|
return result.verified ? 0 : 1;
|
|
24571
24983
|
}
|
|
24572
24984
|
async function cmdStatus(flags, positional) {
|
|
24573
|
-
const startDir = positional[0] ? (0,
|
|
24985
|
+
const startDir = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24574
24986
|
let artifact;
|
|
24575
24987
|
try {
|
|
24576
24988
|
artifact = await loadLastArtifact(startDir);
|
|
@@ -24609,7 +25021,7 @@ async function cmdStatus(flags, positional) {
|
|
|
24609
25021
|
return 0;
|
|
24610
25022
|
}
|
|
24611
25023
|
async function runScanCommand(flags, positional, options) {
|
|
24612
|
-
const workspacePath = positional[0] ? (0,
|
|
25024
|
+
const workspacePath = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
|
|
24613
25025
|
if (!options?.deferMachineOutput) {
|
|
24614
25026
|
console.log(`Scanning ${workspacePath}\u2026 (local, offline)`);
|
|
24615
25027
|
}
|
|
@@ -24636,7 +25048,7 @@ async function runScanCommand(flags, positional, options) {
|
|
|
24636
25048
|
printNextActionBlock(block);
|
|
24637
25049
|
printProviderActionBlock(tasks);
|
|
24638
25050
|
try {
|
|
24639
|
-
const manifest = JSON.parse(await (0,
|
|
25051
|
+
const manifest = JSON.parse(await (0, import_promises32.readFile)(paths.actionsPath, "utf8"));
|
|
24640
25052
|
console.log(renderActionSurface(manifest, { limit: 5 }).trimEnd());
|
|
24641
25053
|
} catch (error) {
|
|
24642
25054
|
console.warn(error instanceof Error ? error.message : String(error));
|
|
@@ -24653,7 +25065,7 @@ async function runScanCommand(flags, positional, options) {
|
|
|
24653
25065
|
return { exitCode: 0, artifacts: paths };
|
|
24654
25066
|
}
|
|
24655
25067
|
async function cmdReport(flags, positional) {
|
|
24656
|
-
const startDir = positional[0] ? (0,
|
|
25068
|
+
const startDir = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24657
25069
|
try {
|
|
24658
25070
|
const paths = await refreshReportFromDisk(startDir);
|
|
24659
25071
|
console.log(`Report refreshed: ${paths.reportPath}`);
|
|
@@ -24675,7 +25087,7 @@ async function cmdReport(flags, positional) {
|
|
|
24675
25087
|
}
|
|
24676
25088
|
}
|
|
24677
25089
|
async function cmdPrompt(flags, positional) {
|
|
24678
|
-
const startDir = positional[0] ? (0,
|
|
25090
|
+
const startDir = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24679
25091
|
let artifact;
|
|
24680
25092
|
try {
|
|
24681
25093
|
artifact = await loadLastArtifact(startDir);
|
|
@@ -24772,7 +25184,7 @@ async function main() {
|
|
|
24772
25184
|
const wantsJsonl = hasFlag(flags, "jsonl");
|
|
24773
25185
|
const wantsStrict = hasFlag(flags, "strict");
|
|
24774
25186
|
if (flags.condense) {
|
|
24775
|
-
const cwd = positional[0] ? (0,
|
|
25187
|
+
const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24776
25188
|
const result = await runCondenseCommand({ cwd });
|
|
24777
25189
|
console.log(`VibeRaven context map refreshed: ${result.contextMapPath}`);
|
|
24778
25190
|
return 0;
|
|
@@ -24792,7 +25204,7 @@ async function main() {
|
|
|
24792
25204
|
}
|
|
24793
25205
|
if (!command && flags.verify === true && typeof flags.action === "string") {
|
|
24794
25206
|
return runVerifyActionCommand({
|
|
24795
|
-
cwd: positional[0] ? (0,
|
|
25207
|
+
cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
24796
25208
|
actionId: flags.action
|
|
24797
25209
|
});
|
|
24798
25210
|
}
|
|
@@ -24803,7 +25215,7 @@ async function main() {
|
|
|
24803
25215
|
console.error("VibeRaven could not produce machine output because scan artifacts were not written.");
|
|
24804
25216
|
return 3;
|
|
24805
25217
|
}
|
|
24806
|
-
const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0,
|
|
25218
|
+
const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises32.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
|
|
24807
25219
|
const strictExitCode = wantsStrict && gateResult ? exitCodeForStrictGate(gateResult, { failOnWarnings: flags.strict === "warning" }) : scanResult.exitCode;
|
|
24808
25220
|
if (wantsJson && gateResult) {
|
|
24809
25221
|
process.stdout.write(renderGateResultJson(gateResult));
|
|
@@ -24825,8 +25237,8 @@ async function main() {
|
|
|
24825
25237
|
env: process.env
|
|
24826
25238
|
});
|
|
24827
25239
|
if (mode === "local-ui") {
|
|
24828
|
-
const cwd = positional[0] ? (0,
|
|
24829
|
-
const handle = await startLocalUiServer({ cwd });
|
|
25240
|
+
const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
25241
|
+
const handle = await startLocalUiServer({ cwd, initialPath: stackInitialPath(flags) });
|
|
24830
25242
|
console.log(`VibeRaven local UI: ${handle.url}`);
|
|
24831
25243
|
await waitForServerShutdown();
|
|
24832
25244
|
await handle.close();
|
|
@@ -24845,9 +25257,9 @@ async function main() {
|
|
|
24845
25257
|
await runInteractiveSession();
|
|
24846
25258
|
return 0;
|
|
24847
25259
|
case "ui": {
|
|
24848
|
-
const cwd = positional[0] ? (0,
|
|
25260
|
+
const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24849
25261
|
const envPort = Number(process.env.PORT);
|
|
24850
|
-
const handle = await startLocalUiServer({ cwd, port: Number.isFinite(envPort) && envPort > 0 ? envPort : void 0 });
|
|
25262
|
+
const handle = await startLocalUiServer({ cwd, port: Number.isFinite(envPort) && envPort > 0 ? envPort : void 0, initialPath: stackInitialPath(flags) });
|
|
24851
25263
|
console.log(`VibeRaven local UI: ${handle.url}`);
|
|
24852
25264
|
await waitForServerShutdown();
|
|
24853
25265
|
await handle.close();
|
|
@@ -24857,12 +25269,14 @@ async function main() {
|
|
|
24857
25269
|
return cmdStatus(flags, positional);
|
|
24858
25270
|
case "check":
|
|
24859
25271
|
return runCheckCommand({
|
|
24860
|
-
cwd: positional[0] ? (0,
|
|
25272
|
+
cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
24861
25273
|
json: Boolean(flags.json),
|
|
24862
|
-
quiet: Boolean(flags.quiet)
|
|
25274
|
+
quiet: Boolean(flags.quiet),
|
|
25275
|
+
base: typeof flags.base === "string" ? flags.base : void 0,
|
|
25276
|
+
markdown: flags.markdown === true
|
|
24863
25277
|
});
|
|
24864
25278
|
case "fix": {
|
|
24865
|
-
const fixCwd = positional[0] ? (0,
|
|
25279
|
+
const fixCwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24866
25280
|
const gapId = typeof flags.gap === "string" ? flags.gap : void 0;
|
|
24867
25281
|
if (!gapId) {
|
|
24868
25282
|
const artifact = await loadLastArtifact(fixCwd).catch(() => void 0);
|
|
@@ -24891,19 +25305,19 @@ async function main() {
|
|
|
24891
25305
|
}
|
|
24892
25306
|
case "actions":
|
|
24893
25307
|
return runActionsCommand({
|
|
24894
|
-
cwd: positional[0] ? (0,
|
|
25308
|
+
cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
24895
25309
|
json: Boolean(flags.json)
|
|
24896
25310
|
});
|
|
24897
25311
|
case "preview":
|
|
24898
25312
|
return runPreviewCommand({
|
|
24899
|
-
cwd: positional[0] ? (0,
|
|
25313
|
+
cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
24900
25314
|
agentMode: flags["agent-mode"] === true,
|
|
24901
25315
|
json: Boolean(flags.json)
|
|
24902
25316
|
});
|
|
24903
25317
|
case "next":
|
|
24904
25318
|
return runNextCommand({
|
|
24905
25319
|
json: Boolean(flags.json),
|
|
24906
|
-
cwd: positional[0] ? (0,
|
|
25320
|
+
cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd()
|
|
24907
25321
|
});
|
|
24908
25322
|
case "guide": {
|
|
24909
25323
|
const provider2 = positional[0];
|
|
@@ -24937,8 +25351,11 @@ async function main() {
|
|
|
24937
25351
|
case "provider-verify":
|
|
24938
25352
|
return cmdProviderVerify(flags, positional);
|
|
24939
25353
|
case "init": {
|
|
24940
|
-
const cwd = positional[0] ? (0,
|
|
25354
|
+
const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24941
25355
|
const agents = typeof flags.agents === "string" ? flags.agents : void 0;
|
|
25356
|
+
if (flags.github === true) {
|
|
25357
|
+
return runInitGithubCommand({ cwd, dryRun: flags["dry-run"] === true, force: flags.force === true });
|
|
25358
|
+
}
|
|
24942
25359
|
return runInitCommand({
|
|
24943
25360
|
cwd,
|
|
24944
25361
|
agents,
|
|
@@ -24951,7 +25368,7 @@ async function main() {
|
|
|
24951
25368
|
return 1;
|
|
24952
25369
|
}
|
|
24953
25370
|
return runDoctorAgentsCommand({
|
|
24954
|
-
cwd: positional[0] ? (0,
|
|
25371
|
+
cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd()
|
|
24955
25372
|
});
|
|
24956
25373
|
case "validate-npm-package":
|
|
24957
25374
|
return runValidateNpmPackageCommand({
|
|
@@ -24964,7 +25381,7 @@ async function main() {
|
|
|
24964
25381
|
return 1;
|
|
24965
25382
|
}
|
|
24966
25383
|
return runAuditCommand({
|
|
24967
|
-
cwd: positional[0] ? (0,
|
|
25384
|
+
cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
24968
25385
|
json: Boolean(flags.json)
|
|
24969
25386
|
});
|
|
24970
25387
|
case "clean": {
|
|
@@ -24972,7 +25389,7 @@ async function main() {
|
|
|
24972
25389
|
console.error("Usage: viberaven clean --plan [path]");
|
|
24973
25390
|
return 1;
|
|
24974
25391
|
}
|
|
24975
|
-
const cwd = positional[0] ? (0,
|
|
25392
|
+
const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24976
25393
|
const plan = buildContextCleanupPlan({ projectRoot: cwd, files: await collectCleanupFiles(cwd) });
|
|
24977
25394
|
const outputPath = await writeCleanupPlan(cwd, plan);
|
|
24978
25395
|
console.log(`Wrote ${outputPath}
|
|
@@ -24982,9 +25399,9 @@ async function main() {
|
|
|
24982
25399
|
}
|
|
24983
25400
|
case "rls-test":
|
|
24984
25401
|
return runRlsTestCommand({
|
|
24985
|
-
cwd: positional[0] ? (0,
|
|
24986
|
-
matrix: typeof flags.matrix === "string" ? (0,
|
|
24987
|
-
migrations: typeof flags.migrations === "string" ? (0,
|
|
25402
|
+
cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
25403
|
+
matrix: typeof flags.matrix === "string" ? (0, import_node_path42.resolve)(process.cwd(), flags.matrix) : void 0,
|
|
25404
|
+
migrations: typeof flags.migrations === "string" ? (0, import_node_path42.resolve)(process.cwd(), flags.migrations) : void 0,
|
|
24988
25405
|
json: Boolean(flags.json)
|
|
24989
25406
|
});
|
|
24990
25407
|
default:
|
|
@@ -24993,11 +25410,11 @@ async function main() {
|
|
|
24993
25410
|
return 1;
|
|
24994
25411
|
}
|
|
24995
25412
|
}
|
|
24996
|
-
var
|
|
25413
|
+
var import_promises32, import_node_path42, STACK_AREAS;
|
|
24997
25414
|
var init_cli = __esm({
|
|
24998
25415
|
"src/cli.ts"() {
|
|
24999
|
-
|
|
25000
|
-
|
|
25416
|
+
import_promises32 = require("node:fs/promises");
|
|
25417
|
+
import_node_path42 = require("node:path");
|
|
25001
25418
|
init_config();
|
|
25002
25419
|
init_runScan();
|
|
25003
25420
|
init_artifacts2();
|
|
@@ -25021,6 +25438,8 @@ var init_cli = __esm({
|
|
|
25021
25438
|
init_stackRecommend();
|
|
25022
25439
|
init_commands();
|
|
25023
25440
|
init_runInit();
|
|
25441
|
+
init_initGithub();
|
|
25442
|
+
init_setupLink();
|
|
25024
25443
|
init_runDoctor();
|
|
25025
25444
|
init_runValidateNpmPackage();
|
|
25026
25445
|
init_runAudit();
|
|
@@ -25058,5 +25477,6 @@ init_cli();
|
|
|
25058
25477
|
parseArgs,
|
|
25059
25478
|
printHelp,
|
|
25060
25479
|
resolveDefaultEntrypointMode,
|
|
25061
|
-
runScanCommand
|
|
25480
|
+
runScanCommand,
|
|
25481
|
+
stackInitialPath
|
|
25062
25482
|
});
|