@viberaven/cli 1.5.3 → 1.6.0
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 +746 -346
- 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.0";
|
|
14694
14694
|
}
|
|
14695
14695
|
});
|
|
14696
14696
|
|
|
@@ -17100,6 +17100,323 @@ 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
|
+
|
|
17103
17420
|
// src/commands/doctorAgents.ts
|
|
17104
17421
|
function managedBlocks(content) {
|
|
17105
17422
|
const blocks = content.match(/<!-- (?:VIBERAVEN:START|viberaven:agent-rules:start) -->[\s\S]*?<!-- (?:VIBERAVEN:END|viberaven:agent-rules:end) -->/g);
|
|
@@ -17108,7 +17425,7 @@ function managedBlocks(content) {
|
|
|
17108
17425
|
async function checkAgentInjection(cwd) {
|
|
17109
17426
|
const checks = [];
|
|
17110
17427
|
for (const item4 of REQUIRED_EXISTENCE_CHECKS) {
|
|
17111
|
-
const exists = await fileExists2((0,
|
|
17428
|
+
const exists = await fileExists2((0, import_node_path32.join)(cwd, item4.file));
|
|
17112
17429
|
checks.push({
|
|
17113
17430
|
id: item4.id,
|
|
17114
17431
|
status: exists ? "pass" : "fail",
|
|
@@ -17116,17 +17433,17 @@ async function checkAgentInjection(cwd) {
|
|
|
17116
17433
|
});
|
|
17117
17434
|
}
|
|
17118
17435
|
for (const item4 of OPTIONAL_CURSOR_PACK_CHECKS) {
|
|
17119
|
-
const exists = await fileExists2((0,
|
|
17436
|
+
const exists = await fileExists2((0, import_node_path32.join)(cwd, item4.file));
|
|
17120
17437
|
checks.push({
|
|
17121
17438
|
id: item4.id,
|
|
17122
17439
|
status: exists ? "pass" : "fail",
|
|
17123
17440
|
message: exists ? `${item4.file} exists` : `Missing ${item4.file} \u2014 run npx -y viberaven init --agents all`
|
|
17124
17441
|
});
|
|
17125
17442
|
}
|
|
17126
|
-
const legacyCursorPath = (0,
|
|
17443
|
+
const legacyCursorPath = (0, import_node_path32.join)(cwd, ".cursor/rules/viberaven.mdc");
|
|
17127
17444
|
if (await fileExists2(legacyCursorPath)) {
|
|
17128
|
-
const legacyContent = await (0,
|
|
17129
|
-
const hasCoreSplit = await fileExists2((0,
|
|
17445
|
+
const legacyContent = await (0, import_promises25.readFile)(legacyCursorPath, "utf-8");
|
|
17446
|
+
const hasCoreSplit = await fileExists2((0, import_node_path32.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
|
|
17130
17447
|
if (!hasCoreSplit || legacyContent.includes("alwaysApply: true")) {
|
|
17131
17448
|
checks.push({
|
|
17132
17449
|
id: "cursor-legacy-mdc",
|
|
@@ -17137,7 +17454,7 @@ async function checkAgentInjection(cwd) {
|
|
|
17137
17454
|
}
|
|
17138
17455
|
for (const target of CORE_AGENT_INJECTION_TARGETS) {
|
|
17139
17456
|
const file = target === "cursor" ? ".cursor/rules/viberaven-core.mdc" : AGENT_RULE_TARGETS[target].file;
|
|
17140
|
-
const path = (0,
|
|
17457
|
+
const path = (0, import_node_path32.join)(cwd, file);
|
|
17141
17458
|
const exists = await fileExists2(path);
|
|
17142
17459
|
if (!exists) {
|
|
17143
17460
|
checks.push({
|
|
@@ -17147,7 +17464,7 @@ async function checkAgentInjection(cwd) {
|
|
|
17147
17464
|
});
|
|
17148
17465
|
continue;
|
|
17149
17466
|
}
|
|
17150
|
-
const content = await (0,
|
|
17467
|
+
const content = await (0, import_promises25.readFile)(path, "utf-8");
|
|
17151
17468
|
const hasCommand = content.includes(PUBLIC_CHECK_COMMAND);
|
|
17152
17469
|
checks.push({
|
|
17153
17470
|
id: `canonical-${target}`,
|
|
@@ -17190,18 +17507,18 @@ function formatDoctorAgentsReport(report) {
|
|
|
17190
17507
|
}
|
|
17191
17508
|
async function fileExists2(path) {
|
|
17192
17509
|
try {
|
|
17193
|
-
await (0,
|
|
17510
|
+
await (0, import_promises25.access)(path);
|
|
17194
17511
|
return true;
|
|
17195
17512
|
} catch {
|
|
17196
17513
|
return false;
|
|
17197
17514
|
}
|
|
17198
17515
|
}
|
|
17199
|
-
var
|
|
17516
|
+
var import_promises25, import_node_path32, REQUIRED_EXISTENCE_CHECKS, OPTIONAL_CURSOR_PACK_CHECKS, STALE_PATTERNS, GATE_RULE_PHRASES_1_5_2;
|
|
17200
17517
|
var init_doctorAgents = __esm({
|
|
17201
17518
|
"src/commands/doctorAgents.ts"() {
|
|
17202
17519
|
"use strict";
|
|
17203
|
-
|
|
17204
|
-
|
|
17520
|
+
import_promises25 = require("node:fs/promises");
|
|
17521
|
+
import_node_path32 = require("node:path");
|
|
17205
17522
|
init_commands();
|
|
17206
17523
|
init_agentTargets();
|
|
17207
17524
|
REQUIRED_EXISTENCE_CHECKS = [
|
|
@@ -17472,7 +17789,7 @@ function buildContextCleanupPlan(input) {
|
|
|
17472
17789
|
const items = [];
|
|
17473
17790
|
const seen = /* @__PURE__ */ new Set();
|
|
17474
17791
|
for (const file of input.files) {
|
|
17475
|
-
const path = normalizePath5((0,
|
|
17792
|
+
const path = normalizePath5((0, import_node_path33.relative)(input.projectRoot, file.path) || file.path);
|
|
17476
17793
|
const fullPath = normalizePath5(file.path);
|
|
17477
17794
|
const category = categorizeCleanupFile(path, fullPath, file);
|
|
17478
17795
|
if (!category || seen.has(path)) {
|
|
@@ -17498,10 +17815,10 @@ async function collectCleanupFiles(projectRoot) {
|
|
|
17498
17815
|
return files;
|
|
17499
17816
|
}
|
|
17500
17817
|
async function writeCleanupPlan(projectRoot, plan) {
|
|
17501
|
-
const outputDir = (0,
|
|
17502
|
-
const outputPath = (0,
|
|
17503
|
-
await (0,
|
|
17504
|
-
await (0,
|
|
17818
|
+
const outputDir = (0, import_node_path33.join)(projectRoot, ".viberaven");
|
|
17819
|
+
const outputPath = (0, import_node_path33.join)(outputDir, "context-cleanup.md");
|
|
17820
|
+
await (0, import_promises26.mkdir)(outputDir, { recursive: true });
|
|
17821
|
+
await (0, import_promises26.writeFile)(outputPath, renderCleanupPlan(plan), "utf8");
|
|
17505
17822
|
return outputPath;
|
|
17506
17823
|
}
|
|
17507
17824
|
function categorizeCleanupFile(relativePath, fullPath, file) {
|
|
@@ -17549,7 +17866,7 @@ async function walkCleanupFiles(root, dir, result, depth) {
|
|
|
17549
17866
|
}
|
|
17550
17867
|
let entries;
|
|
17551
17868
|
try {
|
|
17552
|
-
entries = await (0,
|
|
17869
|
+
entries = await (0, import_promises26.readdir)(dir, { withFileTypes: true });
|
|
17553
17870
|
} catch {
|
|
17554
17871
|
return;
|
|
17555
17872
|
}
|
|
@@ -17557,9 +17874,9 @@ async function walkCleanupFiles(root, dir, result, depth) {
|
|
|
17557
17874
|
if (SKIP_DIRECTORIES.has(entry.name)) {
|
|
17558
17875
|
continue;
|
|
17559
17876
|
}
|
|
17560
|
-
const absolute = (0,
|
|
17877
|
+
const absolute = (0, import_node_path33.join)(dir, entry.name);
|
|
17561
17878
|
if (entry.isDirectory()) {
|
|
17562
|
-
if (isCachePath(normalizePath5((0,
|
|
17879
|
+
if (isCachePath(normalizePath5((0, import_node_path33.relative)(root, absolute)))) {
|
|
17563
17880
|
result.push({ path: absolute, sizeBytes: 0, kind: "directory" });
|
|
17564
17881
|
continue;
|
|
17565
17882
|
}
|
|
@@ -17570,7 +17887,7 @@ async function walkCleanupFiles(root, dir, result, depth) {
|
|
|
17570
17887
|
continue;
|
|
17571
17888
|
}
|
|
17572
17889
|
try {
|
|
17573
|
-
const fileStat = await (0,
|
|
17890
|
+
const fileStat = await (0, import_promises26.stat)(absolute);
|
|
17574
17891
|
result.push({ path: absolute, sizeBytes: fileStat.size, kind: "file" });
|
|
17575
17892
|
} catch {
|
|
17576
17893
|
continue;
|
|
@@ -17580,12 +17897,12 @@ async function walkCleanupFiles(root, dir, result, depth) {
|
|
|
17580
17897
|
function normalizePath5(path) {
|
|
17581
17898
|
return path.replace(/\\/g, "/");
|
|
17582
17899
|
}
|
|
17583
|
-
var
|
|
17900
|
+
var import_promises26, import_node_path33, MAX_WALK_DEPTH, LARGE_LOG_BYTES, SKIP_DIRECTORIES;
|
|
17584
17901
|
var init_cleanPlan = __esm({
|
|
17585
17902
|
"src/commands/cleanPlan.ts"() {
|
|
17586
17903
|
"use strict";
|
|
17587
|
-
|
|
17588
|
-
|
|
17904
|
+
import_promises26 = require("node:fs/promises");
|
|
17905
|
+
import_node_path33 = require("node:path");
|
|
17589
17906
|
MAX_WALK_DEPTH = 5;
|
|
17590
17907
|
LARGE_LOG_BYTES = 8192;
|
|
17591
17908
|
SKIP_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
@@ -17698,10 +18015,10 @@ var init_render = __esm({
|
|
|
17698
18015
|
|
|
17699
18016
|
// src/commands/actions.ts
|
|
17700
18017
|
async function runActionsCommand(input) {
|
|
17701
|
-
const actionsPath = (0,
|
|
18018
|
+
const actionsPath = (0, import_node_path34.join)(getProjectArtifactsDir(input.cwd), "actions.json");
|
|
17702
18019
|
let raw;
|
|
17703
18020
|
try {
|
|
17704
|
-
raw = await (0,
|
|
18021
|
+
raw = await (0, import_promises27.readFile)(actionsPath, "utf8");
|
|
17705
18022
|
} catch {
|
|
17706
18023
|
process.stderr.write("No VibeRaven actions found. Run `npx -y viberaven --agent-mode` first.\n");
|
|
17707
18024
|
return 1;
|
|
@@ -17715,12 +18032,12 @@ async function runActionsCommand(input) {
|
|
|
17715
18032
|
process.stdout.write(renderActionSurface(manifest));
|
|
17716
18033
|
return 0;
|
|
17717
18034
|
}
|
|
17718
|
-
var
|
|
18035
|
+
var import_promises27, import_node_path34;
|
|
17719
18036
|
var init_actions = __esm({
|
|
17720
18037
|
"src/commands/actions.ts"() {
|
|
17721
18038
|
"use strict";
|
|
17722
|
-
|
|
17723
|
-
|
|
18039
|
+
import_promises27 = require("node:fs/promises");
|
|
18040
|
+
import_node_path34 = require("node:path");
|
|
17724
18041
|
init_render();
|
|
17725
18042
|
init_config();
|
|
17726
18043
|
}
|
|
@@ -17729,7 +18046,7 @@ var init_actions = __esm({
|
|
|
17729
18046
|
// src/commands/verifyAction.ts
|
|
17730
18047
|
async function currentActionExists(cwd, actionId) {
|
|
17731
18048
|
try {
|
|
17732
|
-
const raw = await (0,
|
|
18049
|
+
const raw = await (0, import_promises28.readFile)((0, import_node_path35.join)(getProjectArtifactsDir(cwd), "actions.json"), "utf8");
|
|
17733
18050
|
const manifest = JSON.parse(raw);
|
|
17734
18051
|
return manifest.actions.some((action) => action.id === actionId);
|
|
17735
18052
|
} catch {
|
|
@@ -17773,12 +18090,12 @@ This only looks the action up. Run \`npx -y viberaven --verify\` to scan the rep
|
|
|
17773
18090
|
);
|
|
17774
18091
|
return 0;
|
|
17775
18092
|
}
|
|
17776
|
-
var
|
|
18093
|
+
var import_promises28, import_node_path35;
|
|
17777
18094
|
var init_verifyAction = __esm({
|
|
17778
18095
|
"src/commands/verifyAction.ts"() {
|
|
17779
18096
|
"use strict";
|
|
17780
|
-
|
|
17781
|
-
|
|
18097
|
+
import_promises28 = require("node:fs/promises");
|
|
18098
|
+
import_node_path35 = require("node:path");
|
|
17782
18099
|
init_registry2();
|
|
17783
18100
|
init_config();
|
|
17784
18101
|
}
|
|
@@ -17902,11 +18219,36 @@ var init_preview = __esm({
|
|
|
17902
18219
|
async function runCheckCommand(options) {
|
|
17903
18220
|
const result = await runProjectScan({ workspacePath: options.cwd });
|
|
17904
18221
|
if (!result.ok) {
|
|
18222
|
+
if (options.markdown) console.log(`${COMMENT_MARKER}
|
|
18223
|
+
### VibeRaven could not check this change
|
|
18224
|
+
|
|
18225
|
+
${result.message}
|
|
18226
|
+
`);
|
|
17905
18227
|
console.error(formatAgentStatus(ERROR, result.message));
|
|
17906
18228
|
return 2;
|
|
17907
18229
|
}
|
|
17908
18230
|
const artifact = result.artifact;
|
|
17909
18231
|
const paths = await writeScanArtifacts({ artifact, cwd: options.cwd });
|
|
18232
|
+
let change;
|
|
18233
|
+
if (options.base) {
|
|
18234
|
+
try {
|
|
18235
|
+
change = await checkChange(options.cwd, options.base, artifact.gaps);
|
|
18236
|
+
} catch (error) {
|
|
18237
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
18238
|
+
if (options.markdown) console.log(`${COMMENT_MARKER}
|
|
18239
|
+
### VibeRaven could not compare this change
|
|
18240
|
+
|
|
18241
|
+
${message}
|
|
18242
|
+
`);
|
|
18243
|
+
console.error(formatAgentStatus(ERROR, message));
|
|
18244
|
+
return 2;
|
|
18245
|
+
}
|
|
18246
|
+
}
|
|
18247
|
+
const failing = change ? change.introduced.some((g2) => g2.severity === "critical") : artifact.gaps.some((g2) => g2.severity === "critical");
|
|
18248
|
+
if (options.markdown) {
|
|
18249
|
+
console.log(renderCheckMarkdown({ head: artifact.gaps, change, version: VERSION, score: artifact.score }));
|
|
18250
|
+
return failing ? 1 : 0;
|
|
18251
|
+
}
|
|
17910
18252
|
if (options.json) {
|
|
17911
18253
|
console.log(
|
|
17912
18254
|
JSON.stringify(
|
|
@@ -17921,13 +18263,22 @@ async function runCheckCommand(options) {
|
|
|
17921
18263
|
title: gap.title,
|
|
17922
18264
|
detail: gap.detail
|
|
17923
18265
|
})),
|
|
18266
|
+
...change ? {
|
|
18267
|
+
change: {
|
|
18268
|
+
base: change.base,
|
|
18269
|
+
baseCommit: change.baseCommit,
|
|
18270
|
+
introduced: change.introduced.map((gap) => gap.id),
|
|
18271
|
+
resolved: change.resolved.map((gap) => gap.id),
|
|
18272
|
+
unchanged: change.unchanged.map((gap) => gap.id)
|
|
18273
|
+
}
|
|
18274
|
+
} : {},
|
|
17924
18275
|
artifacts: paths
|
|
17925
18276
|
},
|
|
17926
18277
|
null,
|
|
17927
18278
|
2
|
|
17928
18279
|
)
|
|
17929
18280
|
);
|
|
17930
|
-
return
|
|
18281
|
+
return failing ? 1 : 0;
|
|
17931
18282
|
}
|
|
17932
18283
|
const sorted = [...artifact.gaps].sort(
|
|
17933
18284
|
(a, b3) => SEVERITY_ORDER3[a.severity] - SEVERITY_ORDER3[b3.severity]
|
|
@@ -17937,14 +18288,37 @@ async function runCheckCommand(options) {
|
|
|
17937
18288
|
console.log("");
|
|
17938
18289
|
console.log(`viberaven check \xB7 ${options.cwd}`);
|
|
17939
18290
|
console.log("");
|
|
18291
|
+
if (change) {
|
|
18292
|
+
const line = (gap) => `${SEVERITY_ICON[gap.severity]} ${gap.title} (${gap.id})`;
|
|
18293
|
+
console.log(`Compared with ${change.base} (${change.baseCommit.slice(0, 7)})`);
|
|
18294
|
+
console.log("");
|
|
18295
|
+
console.log(change.introduced.length ? "New in this change:" : "New in this change: nothing");
|
|
18296
|
+
for (const gap of change.introduced) {
|
|
18297
|
+
console.log(` ${line(gap)}${gap.subjects?.length ? ` new: ${gap.subjects.join(", ")}` : ""}`);
|
|
18298
|
+
if (!options.quiet && !gap.subjects?.length && gap.detail) console.log(` ${gap.detail.split("\n")[0]}`);
|
|
18299
|
+
}
|
|
18300
|
+
if (change.resolved.length) {
|
|
18301
|
+
console.log("Fixed by this change:");
|
|
18302
|
+
for (const gap of change.resolved) console.log(` \u2705 ${gap.title}${gap.subjects?.length ? ` for ${gap.subjects.join(", ")}` : ""} (${gap.id})`);
|
|
18303
|
+
}
|
|
18304
|
+
if (change.unchanged.length) console.log(`Already there before: ${change.unchanged.length}`);
|
|
18305
|
+
const newCritical = change.introduced.filter((g2) => g2.severity === "critical").length;
|
|
18306
|
+
console.log("");
|
|
18307
|
+
console.log(
|
|
18308
|
+
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}`
|
|
18309
|
+
);
|
|
18310
|
+
console.log(`Fix: viberaven fix \xB7 Details: ${paths.tasklistPath}`);
|
|
18311
|
+
console.log("");
|
|
18312
|
+
return failing ? 1 : 0;
|
|
18313
|
+
}
|
|
17940
18314
|
if (sorted.length === 0) {
|
|
17941
18315
|
console.log("\u2705 No launch blockers found in repo evidence.");
|
|
17942
18316
|
} else {
|
|
17943
18317
|
for (const gap of sorted) {
|
|
17944
18318
|
console.log(`${SEVERITY_ICON[gap.severity]} ${gap.title} (${gap.id})`);
|
|
17945
18319
|
if (!options.quiet && gap.detail) {
|
|
17946
|
-
const
|
|
17947
|
-
console.log(` ${
|
|
18320
|
+
const firstLine3 = gap.detail.split("\n")[0];
|
|
18321
|
+
console.log(` ${firstLine3}`);
|
|
17948
18322
|
}
|
|
17949
18323
|
}
|
|
17950
18324
|
}
|
|
@@ -17963,6 +18337,8 @@ var init_check = __esm({
|
|
|
17963
18337
|
init_runScan();
|
|
17964
18338
|
init_artifacts2();
|
|
17965
18339
|
init_statusLabels();
|
|
18340
|
+
init_version();
|
|
18341
|
+
init_checkChange();
|
|
17966
18342
|
SEVERITY_ICON = {
|
|
17967
18343
|
critical: "\u{1F534}",
|
|
17968
18344
|
// red circle
|
|
@@ -18006,12 +18382,12 @@ function parsePermissionsMatrix(text) {
|
|
|
18006
18382
|
} else {
|
|
18007
18383
|
const seen = /* @__PURE__ */ new Set();
|
|
18008
18384
|
for (const [key, value] of Object.entries(raw.tables)) {
|
|
18009
|
-
const
|
|
18010
|
-
if (!
|
|
18011
|
-
const full = `${
|
|
18385
|
+
const table2 = parseTable(key, value, problems);
|
|
18386
|
+
if (!table2) continue;
|
|
18387
|
+
const full = `${table2.schema}.${table2.name}`;
|
|
18012
18388
|
if (seen.has(full)) problems.push(`tables[${show(key)}]: ${full} is listed twice`);
|
|
18013
18389
|
seen.add(full);
|
|
18014
|
-
tables.push(
|
|
18390
|
+
tables.push(table2);
|
|
18015
18391
|
}
|
|
18016
18392
|
}
|
|
18017
18393
|
if (problems.length > 0) throw new MatrixError(problems);
|
|
@@ -18085,10 +18461,10 @@ function parseTable(key, value, problems) {
|
|
|
18085
18461
|
}
|
|
18086
18462
|
const parsed = {};
|
|
18087
18463
|
for (const action of MATRIX_ACTIONS) {
|
|
18088
|
-
const
|
|
18089
|
-
if (
|
|
18090
|
-
else if (!CELL_VALUES.includes(
|
|
18091
|
-
else parsed[action] =
|
|
18464
|
+
const cell2 = cells[action];
|
|
18465
|
+
if (cell2 === void 0) problems.push(`${actorAt}.${action}: missing; write ${quoteList(CELL_VALUES)}`);
|
|
18466
|
+
else if (!CELL_VALUES.includes(cell2)) problems.push(`${actorAt}.${action}: must be ${quoteList(CELL_VALUES)} (got ${show(cell2)})`);
|
|
18467
|
+
else parsed[action] = cell2;
|
|
18092
18468
|
}
|
|
18093
18469
|
if (cells.reassign !== void 0) {
|
|
18094
18470
|
if (!CELL_VALUES.includes(cells.reassign)) problems.push(`${actorAt}.reassign: must be ${quoteList(CELL_VALUES)} (got ${show(cells.reassign)})`);
|
|
@@ -18308,13 +18684,13 @@ async function createWorkerDatabase(options) {
|
|
|
18308
18684
|
const call = (message, timeoutMs) => {
|
|
18309
18685
|
if (dead) return Promise.reject(dead);
|
|
18310
18686
|
const id = ++nextId;
|
|
18311
|
-
return new Promise((
|
|
18687
|
+
return new Promise((resolve10, reject) => {
|
|
18312
18688
|
const timer = setTimeout(() => {
|
|
18313
18689
|
const error = new RlsTimeoutError(`timed out after ${Math.round(timeoutMs / 1e3)} s`);
|
|
18314
18690
|
failAll(error);
|
|
18315
18691
|
void worker.terminate();
|
|
18316
18692
|
}, timeoutMs);
|
|
18317
|
-
pending.set(id, { resolve:
|
|
18693
|
+
pending.set(id, { resolve: resolve10, reject, timer });
|
|
18318
18694
|
worker.postMessage({ ...message, id });
|
|
18319
18695
|
});
|
|
18320
18696
|
};
|
|
@@ -18399,22 +18775,22 @@ parentPort.on('message', async (m) => {
|
|
|
18399
18775
|
// src/rls/rlsRunner.ts
|
|
18400
18776
|
function sqlError(error) {
|
|
18401
18777
|
const e2 = error;
|
|
18402
|
-
return { code: typeof e2?.code === "string" ? e2.code : "unknown", message:
|
|
18778
|
+
return { code: typeof e2?.code === "string" ? e2.code : "unknown", message: firstLine2(typeof e2?.message === "string" ? e2.message : String(error)) };
|
|
18403
18779
|
}
|
|
18404
18780
|
function isSqlError(error) {
|
|
18405
18781
|
return !(error instanceof RlsTimeoutError) && typeof error?.code === "string";
|
|
18406
18782
|
}
|
|
18407
18783
|
function displayPath(projectDir, path) {
|
|
18408
18784
|
for (const base of [projectDir, process.cwd()]) {
|
|
18409
|
-
const rel = (0,
|
|
18410
|
-
if (rel && !rel.startsWith("..") && !(0,
|
|
18785
|
+
const rel = (0, import_node_path36.relative)(base, path);
|
|
18786
|
+
if (rel && !rel.startsWith("..") && !(0, import_node_path36.isAbsolute)(rel)) return rel.split("\\").join("/");
|
|
18411
18787
|
}
|
|
18412
18788
|
return path;
|
|
18413
18789
|
}
|
|
18414
18790
|
async function gitCommit(projectDir, paths) {
|
|
18415
18791
|
const run = async (args) => {
|
|
18416
18792
|
const call = await safeGitInvocation(projectDir, args);
|
|
18417
|
-
const { stdout } = await (0,
|
|
18793
|
+
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
18794
|
return String(stdout);
|
|
18419
18795
|
};
|
|
18420
18796
|
let sha;
|
|
@@ -18487,11 +18863,11 @@ function valueCandidates(column, label) {
|
|
|
18487
18863
|
if (column.category === "A") return u2 ? [] : ["{}"];
|
|
18488
18864
|
return [];
|
|
18489
18865
|
}
|
|
18490
|
-
async function describeTable(db,
|
|
18491
|
-
const display = `${
|
|
18866
|
+
async function describeTable(db, table2) {
|
|
18867
|
+
const display = `${table2.schema}.${table2.name}`;
|
|
18492
18868
|
const rel = await db.query(
|
|
18493
18869
|
`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
|
-
[
|
|
18870
|
+
[table2.schema, table2.name]
|
|
18495
18871
|
);
|
|
18496
18872
|
if (rel.rows.length === 0) return { reason: `${display} does not exist after the migrations; check the table name in the matrix.` };
|
|
18497
18873
|
const kind = String(rel.rows[0].kind);
|
|
@@ -18530,35 +18906,35 @@ async function describeTable(db, table) {
|
|
|
18530
18906
|
};
|
|
18531
18907
|
});
|
|
18532
18908
|
const byName = new Map(columns.map((c) => [c.name, c]));
|
|
18533
|
-
const owner = byName.get(
|
|
18534
|
-
if (!owner) return { reason: `${display} has no column ${
|
|
18909
|
+
const owner = byName.get(table2.ownerColumn);
|
|
18910
|
+
if (!owner) return { reason: `${display} has no column ${table2.ownerColumn} (the matrix owner column).` };
|
|
18535
18911
|
if (owner.typname !== "uuid" && owner.category !== "S") {
|
|
18536
|
-
return { reason: `${display}.${
|
|
18912
|
+
return { reason: `${display}.${table2.ownerColumn} is ${owner.type}; the owner column must hold auth.uid() (uuid or text).` };
|
|
18537
18913
|
}
|
|
18538
18914
|
const pk = columns.filter((c) => c.isPk);
|
|
18539
18915
|
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(
|
|
18916
|
+
for (const column of Object.keys(table2.seed)) {
|
|
18541
18917
|
if (!byName.has(column)) return { reason: `the matrix seeds ${display}.${column}, which does not exist.` };
|
|
18542
18918
|
}
|
|
18543
|
-
if (
|
|
18544
|
-
const column = byName.get(
|
|
18545
|
-
if (!column) return { reason: `the matrix updateColumn ${display}.${
|
|
18919
|
+
if (table2.updateColumn) {
|
|
18920
|
+
const column = byName.get(table2.updateColumn);
|
|
18921
|
+
if (!column) return { reason: `the matrix updateColumn ${display}.${table2.updateColumn} does not exist.` };
|
|
18546
18922
|
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
18923
|
}
|
|
18548
18924
|
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
|
|
18925
|
+
const required = columns.filter((c) => c.name !== owner.name && c.notNull && !c.hasDefault && writable(c) && !(c.name in table2.seed));
|
|
18550
18926
|
for (const column of required) {
|
|
18551
18927
|
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
18928
|
if (valueCandidates(column, "a").length === 0) {
|
|
18553
18929
|
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
18930
|
}
|
|
18555
18931
|
}
|
|
18556
|
-
const seedColumns = Object.keys(
|
|
18932
|
+
const seedColumns = Object.keys(table2.seed);
|
|
18557
18933
|
return {
|
|
18558
18934
|
shape: {
|
|
18559
|
-
table,
|
|
18935
|
+
table: table2,
|
|
18560
18936
|
oid,
|
|
18561
|
-
qualified: `${quoteIdent2(
|
|
18937
|
+
qualified: `${quoteIdent2(table2.schema)}.${quoteIdent2(table2.name)}`,
|
|
18562
18938
|
display,
|
|
18563
18939
|
columns,
|
|
18564
18940
|
pk,
|
|
@@ -18566,7 +18942,7 @@ async function describeTable(db, table) {
|
|
|
18566
18942
|
ownerUnique: unique3.has(owner.name),
|
|
18567
18943
|
ownerInPk: pk.some((c) => c.name === owner.name),
|
|
18568
18944
|
insertColumns: [owner.name, ...seedColumns, ...required.map((c) => c.name)],
|
|
18569
|
-
rowValues: (label, ownerId) => [ownerId, ...seedColumns.map((c) =>
|
|
18945
|
+
rowValues: (label, ownerId) => [ownerId, ...seedColumns.map((c) => table2.seed[c]), ...required.map((c) => valueCandidates(c, label)[0])]
|
|
18570
18946
|
}
|
|
18571
18947
|
};
|
|
18572
18948
|
}
|
|
@@ -18662,10 +19038,10 @@ function writeControl(filtered, changed) {
|
|
|
18662
19038
|
if (!changed(run)) {
|
|
18663
19039
|
return {
|
|
18664
19040
|
ok: false,
|
|
18665
|
-
detail: filtered ? `as the superuser the same statement changed ${
|
|
19041
|
+
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
19042
|
};
|
|
18667
19043
|
}
|
|
18668
|
-
return { ok: true, detail: `as the superuser the same statement changed ${
|
|
19044
|
+
return { ok: true, detail: `as the superuser the same statement changed ${plural3(filtered ? run.affected ?? 0 : 1, "row")}` };
|
|
18669
19045
|
};
|
|
18670
19046
|
}
|
|
18671
19047
|
async function differingValue(db, shape, target, column) {
|
|
@@ -18692,7 +19068,7 @@ async function selectPlans(c) {
|
|
|
18692
19068
|
return [
|
|
18693
19069
|
{
|
|
18694
19070
|
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 ${
|
|
19071
|
+
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
19072
|
judge: (run) => {
|
|
18697
19073
|
const denied = privilegeDenied(run);
|
|
18698
19074
|
if (denied) return denied;
|
|
@@ -18747,7 +19123,7 @@ function insertPlans(c) {
|
|
|
18747
19123
|
prep,
|
|
18748
19124
|
controlOk: (run) => {
|
|
18749
19125
|
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 ${
|
|
19126
|
+
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
19127
|
},
|
|
18752
19128
|
judge: (run) => {
|
|
18753
19129
|
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 +19135,7 @@ function insertPlans(c) {
|
|
|
18759
19135
|
if ((run.affected ?? 0) === 0 && run.after.digest === run.before.digest) return { verdict: "denied", detail: "0 rows added, table unchanged" };
|
|
18760
19136
|
return {
|
|
18761
19137
|
verdict: "unknown",
|
|
18762
|
-
detail: `reported ${
|
|
19138
|
+
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
19139
|
};
|
|
18764
19140
|
}
|
|
18765
19141
|
}
|
|
@@ -18807,7 +19183,7 @@ async function updatePlans(c) {
|
|
|
18807
19183
|
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
19184
|
if (!filtered) return { verdict: "denied", detail: "the owner row is unchanged" };
|
|
18809
19185
|
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 ${
|
|
19186
|
+
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
19187
|
}
|
|
18812
19188
|
});
|
|
18813
19189
|
};
|
|
@@ -18859,7 +19235,7 @@ function reassignPlans(c) {
|
|
|
18859
19235
|
if (moved(run)) return { verdict: "allowed", detail: `the owner row now belongs to the other user` };
|
|
18860
19236
|
if (!filtered && run.after.target === run.before.target) return { verdict: "denied", detail: "the owner row is unchanged" };
|
|
18861
19237
|
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 ${
|
|
19238
|
+
return { verdict: "unknown", detail: `reported ${plural3(run.affected ?? 0, "row")}, but the owner column did not move` };
|
|
18863
19239
|
}
|
|
18864
19240
|
}
|
|
18865
19241
|
];
|
|
@@ -18888,7 +19264,7 @@ function deletePlans(c) {
|
|
|
18888
19264
|
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
19265
|
return {
|
|
18890
19266
|
verdict: "unknown",
|
|
18891
|
-
detail: `reported ${
|
|
19267
|
+
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
19268
|
};
|
|
18893
19269
|
}
|
|
18894
19270
|
}
|
|
@@ -18959,25 +19335,25 @@ async function runCell(ctx, privilegesByRole, actor, action, expected) {
|
|
|
18959
19335
|
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
19336
|
return { ...base, outcome: pass ? "Pass" : "Fail", observed, reason };
|
|
18961
19337
|
}
|
|
18962
|
-
function cellsFor(
|
|
19338
|
+
function cellsFor(table2) {
|
|
18963
19339
|
const out = [];
|
|
18964
19340
|
for (const actor of TESTED_ACTORS) {
|
|
18965
|
-
for (const action of MATRIX_ACTIONS) out.push({ actor, action, expected:
|
|
18966
|
-
const reassign =
|
|
19341
|
+
for (const action of MATRIX_ACTIONS) out.push({ actor, action, expected: table2.expect[actor][action] });
|
|
19342
|
+
const reassign = table2.expect[actor].reassign;
|
|
18967
19343
|
if (reassign) out.push({ actor, action: "reassign", expected: reassign });
|
|
18968
19344
|
}
|
|
18969
19345
|
return out;
|
|
18970
19346
|
}
|
|
18971
|
-
function unknownCells(
|
|
18972
|
-
const display = `${
|
|
18973
|
-
const cells = cellsFor(
|
|
19347
|
+
function unknownCells(table2, reason, summary) {
|
|
19348
|
+
const display = `${table2.schema}.${table2.name}`;
|
|
19349
|
+
const cells = cellsFor(table2).map(
|
|
18974
19350
|
({ 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
19351
|
);
|
|
18976
|
-
return [...cells, ...serviceRoleCells(
|
|
19352
|
+
return [...cells, ...serviceRoleCells(table2)];
|
|
18977
19353
|
}
|
|
18978
|
-
function serviceRoleCells(
|
|
18979
|
-
if (!
|
|
18980
|
-
const display = `${
|
|
19354
|
+
function serviceRoleCells(table2) {
|
|
19355
|
+
if (!table2.serviceRoleExcluded) return [];
|
|
19356
|
+
const display = `${table2.schema}.${table2.name}`;
|
|
18981
19357
|
return MATRIX_ACTIONS.map((action) => ({
|
|
18982
19358
|
table: display,
|
|
18983
19359
|
actor: "service_role",
|
|
@@ -18997,40 +19373,40 @@ function summarise(tables) {
|
|
|
18997
19373
|
function verdictFor(summary) {
|
|
18998
19374
|
if (summary.fail > 0) {
|
|
18999
19375
|
return {
|
|
19000
|
-
verdict: `Not ready within tested scope: ${
|
|
19376
|
+
verdict: `Not ready within tested scope: ${plural3(summary.fail, "cell does", "cells do")} not match the matrix; ${plural3(summary.unknown, "unknown")}.`,
|
|
19001
19377
|
exitCode: 1
|
|
19002
19378
|
};
|
|
19003
19379
|
}
|
|
19004
19380
|
if (summary.pass === 0) {
|
|
19005
|
-
return { verdict: `Nothing was observed: ${
|
|
19381
|
+
return { verdict: `Nothing was observed: ${plural3(summary.unknown, "unknown")}, ${summary.notApplicable} N/A.`, exitCode: 2 };
|
|
19006
19382
|
}
|
|
19007
19383
|
if (summary.unknown > 0) {
|
|
19008
|
-
return { verdict: `No blockers found within tested scope; ${
|
|
19384
|
+
return { verdict: `No blockers found within tested scope; ${plural3(summary.unknown, "unknown")}.`, exitCode: 2 };
|
|
19009
19385
|
}
|
|
19010
19386
|
return { verdict: "No blockers found within tested scope; 0 unknowns. Scenario passed locally.", exitCode: 0 };
|
|
19011
19387
|
}
|
|
19012
19388
|
async function listMigrations(dir) {
|
|
19013
19389
|
try {
|
|
19014
|
-
const entries = await (0,
|
|
19390
|
+
const entries = await (0, import_promises29.readdir)(dir, { withFileTypes: true });
|
|
19015
19391
|
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
19392
|
} catch {
|
|
19017
19393
|
return [];
|
|
19018
19394
|
}
|
|
19019
19395
|
}
|
|
19020
19396
|
async function runRlsTest(options) {
|
|
19021
|
-
const projectDir = (0,
|
|
19022
|
-
const matrixPath = (0,
|
|
19023
|
-
const migrationsDir = (0,
|
|
19397
|
+
const projectDir = (0, import_node_path36.resolve)(options.projectDir);
|
|
19398
|
+
const matrixPath = (0, import_node_path36.resolve)(projectDir, options.matrixPath ?? DEFAULT_MATRIX_FILE);
|
|
19399
|
+
const migrationsDir = (0, import_node_path36.resolve)(projectDir, options.migrationsDir ?? (0, import_node_path36.join)("supabase", "migrations"));
|
|
19024
19400
|
const timeouts = { ...DEFAULT_TIMEOUTS, ...options.timeouts };
|
|
19025
|
-
if ((0,
|
|
19026
|
-
throw new RlsInputError(`Refusing to read ${(0,
|
|
19401
|
+
if ((0, import_node_path36.basename)(matrixPath).toLowerCase().startsWith(".env")) {
|
|
19402
|
+
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
19403
|
}
|
|
19028
19404
|
const filesRead = [];
|
|
19029
19405
|
let matrixText;
|
|
19030
19406
|
try {
|
|
19031
|
-
const info = await (0,
|
|
19407
|
+
const info = await (0, import_promises29.stat)(matrixPath);
|
|
19032
19408
|
if (!info.isFile()) throw new Error("not a file");
|
|
19033
|
-
matrixText = normalise(await (0,
|
|
19409
|
+
matrixText = normalise(await (0, import_promises29.readFile)(matrixPath, "utf8"));
|
|
19034
19410
|
} catch {
|
|
19035
19411
|
throw new RlsInputError(
|
|
19036
19412
|
`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 +19417,8 @@ async function runRlsTest(options) {
|
|
|
19041
19417
|
const names = await listMigrations(migrationsDir);
|
|
19042
19418
|
const migrationFiles = [];
|
|
19043
19419
|
for (const name of names) {
|
|
19044
|
-
const sql = normalise(await (0,
|
|
19045
|
-
filesRead.push(displayPath(projectDir, (0,
|
|
19420
|
+
const sql = normalise(await (0, import_promises29.readFile)((0, import_node_path36.join)(migrationsDir, name), "utf8"));
|
|
19421
|
+
filesRead.push(displayPath(projectDir, (0, import_node_path36.join)(migrationsDir, name)));
|
|
19046
19422
|
migrationFiles.push({ name, sha256: sha256(sql), sql });
|
|
19047
19423
|
}
|
|
19048
19424
|
const migrationsHash = sha256(migrationFiles.map((f) => `${f.name}\0${f.sha256}`).join("\n"));
|
|
@@ -19094,7 +19470,7 @@ async function runRlsTest(options) {
|
|
|
19094
19470
|
engineLoaded = true;
|
|
19095
19471
|
db = await engine.create({ env: allowlistedEnv(process.env), statementTimeoutMs: timeouts.statementMs });
|
|
19096
19472
|
} catch (error) {
|
|
19097
|
-
result.engine.error =
|
|
19473
|
+
result.engine.error = firstLine2(error instanceof Error ? error.message : String(error));
|
|
19098
19474
|
if (engineLoaded) return allUnknown(`PGlite could not start: ${result.engine.error}. Nothing was tested.`, "not run: PGlite startup failed");
|
|
19099
19475
|
const missing = error?.code === "MODULE_NOT_FOUND" || error?.code === "ERR_MODULE_NOT_FOUND";
|
|
19100
19476
|
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 +19530,13 @@ async function runRlsTest(options) {
|
|
|
19154
19530
|
result.roleChecks.push({ role, ok, detail: row ? `superuser ${Boolean(row.su)}, BYPASSRLS ${Boolean(row.bypass)}` : "the role does not exist after the migrations" });
|
|
19155
19531
|
}
|
|
19156
19532
|
const badRoles = result.roleChecks.filter((c) => !c.ok);
|
|
19157
|
-
for (const
|
|
19158
|
-
result.tables.push(await runTable(db,
|
|
19533
|
+
for (const table2 of matrix.tables) {
|
|
19534
|
+
result.tables.push(await runTable(db, table2, badRoles, result.roleChecks));
|
|
19159
19535
|
}
|
|
19160
19536
|
return finish();
|
|
19161
19537
|
} catch (error) {
|
|
19162
19538
|
if (error instanceof RlsTimeoutError || !isSqlError(error)) {
|
|
19163
|
-
const message =
|
|
19539
|
+
const message = firstLine2(error instanceof Error ? error.message : String(error));
|
|
19164
19540
|
return allUnknown(
|
|
19165
19541
|
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
19542
|
"not run: the database stopped"
|
|
@@ -19186,13 +19562,13 @@ async function seedRow(db, shape, label, userId) {
|
|
|
19186
19562
|
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
19563
|
return { key: keyOf2(r.rows[0]), reused: false };
|
|
19188
19564
|
}
|
|
19189
|
-
async function runTable(db,
|
|
19190
|
-
const display = `${
|
|
19191
|
-
const unknownTable = (reason, extra = {}) => ({ name: display, ...extra, unknownReason: reason, cells: unknownCells(
|
|
19565
|
+
async function runTable(db, table2, badRoles, roleChecks) {
|
|
19566
|
+
const display = `${table2.schema}.${table2.name}`;
|
|
19567
|
+
const unknownTable = (reason, extra = {}) => ({ name: display, ...extra, unknownReason: reason, cells: unknownCells(table2, reason, "not run") });
|
|
19192
19568
|
if (badRoles.length > 0) {
|
|
19193
19569
|
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
19570
|
}
|
|
19195
|
-
const described = await describeTable(db,
|
|
19571
|
+
const described = await describeTable(db, table2);
|
|
19196
19572
|
if (!described.shape) return unknownTable(described.reason ?? "unknown table problem");
|
|
19197
19573
|
const shape = described.shape;
|
|
19198
19574
|
const rel = await db.query(
|
|
@@ -19226,15 +19602,15 @@ async function runTable(db, table, badRoles, roleChecks) {
|
|
|
19226
19602
|
const privilegesByRole = /* @__PURE__ */ new Map();
|
|
19227
19603
|
for (const role of ["anon", "authenticated"]) privilegesByRole.set(role, await privilegesFor(db, shape, role));
|
|
19228
19604
|
const cells = [];
|
|
19229
|
-
for (const { actor, action, expected } of cellsFor(
|
|
19605
|
+
for (const { actor, action, expected } of cellsFor(table2)) {
|
|
19230
19606
|
cells.push(await runCell({ db, shape, target: ownerSeed.key }, privilegesByRole, actor, action, expected));
|
|
19231
19607
|
}
|
|
19232
|
-
cells.push(...serviceRoleCells(
|
|
19608
|
+
cells.push(...serviceRoleCells(table2));
|
|
19233
19609
|
const columns = {};
|
|
19234
19610
|
shape.insertColumns.forEach((c) => {
|
|
19235
19611
|
columns[c] = c === shape.owner.name ? "the test user id" : "generated test value";
|
|
19236
19612
|
});
|
|
19237
|
-
for (const c of Object.keys(
|
|
19613
|
+
for (const c of Object.keys(table2.seed)) columns[c] = "from the matrix seed";
|
|
19238
19614
|
return {
|
|
19239
19615
|
name: display,
|
|
19240
19616
|
rls,
|
|
@@ -19242,16 +19618,16 @@ async function runTable(db, table, badRoles, roleChecks) {
|
|
|
19242
19618
|
cells
|
|
19243
19619
|
};
|
|
19244
19620
|
}
|
|
19245
|
-
var
|
|
19621
|
+
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
19622
|
var init_rlsRunner = __esm({
|
|
19247
19623
|
"src/rls/rlsRunner.ts"() {
|
|
19248
19624
|
"use strict";
|
|
19249
|
-
|
|
19625
|
+
import_node_child_process8 = require("node:child_process");
|
|
19250
19626
|
import_node_crypto3 = require("node:crypto");
|
|
19251
|
-
|
|
19627
|
+
import_promises29 = require("node:fs/promises");
|
|
19252
19628
|
import_node_net = __toESM(require("node:net"));
|
|
19253
|
-
|
|
19254
|
-
|
|
19629
|
+
import_node_path36 = require("node:path");
|
|
19630
|
+
import_node_util6 = require("node:util");
|
|
19255
19631
|
init_safeGit();
|
|
19256
19632
|
init_permissionsMatrix();
|
|
19257
19633
|
init_supabaseImitation();
|
|
@@ -19267,15 +19643,15 @@ var init_rlsRunner = __esm({
|
|
|
19267
19643
|
};
|
|
19268
19644
|
normalise = (text) => text.replace(/\r\n/g, "\n");
|
|
19269
19645
|
quoteIdent2 = (name) => `"${name.replace(/"/g, '""')}"`;
|
|
19270
|
-
|
|
19271
|
-
|
|
19646
|
+
firstLine2 = (message) => message.split("\n")[0].trim();
|
|
19647
|
+
plural3 = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
|
|
19272
19648
|
NUMBERS = { a: ["1001"], b: ["1002"], i: ["1003"], u: ["1004", "7", "1"] };
|
|
19273
19649
|
ACTOR_SESSION = {
|
|
19274
19650
|
owner: { role: "authenticated", uid: OWNER_ID, claims: { sub: OWNER_ID, role: "authenticated", aud: "authenticated", email: "owner@example.test" } },
|
|
19275
19651
|
other_user: { role: "authenticated", uid: OTHER_USER_ID, claims: { sub: OTHER_USER_ID, role: "authenticated", aud: "authenticated", email: "other-user@example.test" } },
|
|
19276
19652
|
anon: { role: "anon", uid: null, claims: { role: "anon" } }
|
|
19277
19653
|
};
|
|
19278
|
-
changedRows = (n) => `${
|
|
19654
|
+
changedRows = (n) => `${plural3(n ?? 0, "row")} changed`;
|
|
19279
19655
|
isRlsRefusal = (run) => run.error?.code === PERMISSION_DENIED && RLS_REFUSAL.test(run.error.message);
|
|
19280
19656
|
isPrivilegeRefusal = (run) => run.error?.code === PERMISSION_DENIED && !RLS_REFUSAL.test(run.error.message);
|
|
19281
19657
|
SELF = Symbol("self");
|
|
@@ -19322,25 +19698,25 @@ function renderRlsText(result) {
|
|
|
19322
19698
|
lines.push("");
|
|
19323
19699
|
if (result.engine.error) lines.push(`(${result.engine.error})`);
|
|
19324
19700
|
}
|
|
19325
|
-
for (const
|
|
19701
|
+
for (const table2 of result.tables) {
|
|
19326
19702
|
lines.push("");
|
|
19327
|
-
const rls =
|
|
19328
|
-
lines.push(`${
|
|
19329
|
-
if (
|
|
19330
|
-
const reused = [
|
|
19703
|
+
const rls = table2.rls ? `RLS ${table2.rls.enabled ? "on" : "off"}${table2.rls.forced ? ", forced" : ""}, owner ${table2.rls.owner}` : "not inspected";
|
|
19704
|
+
lines.push(`${table2.name} (${rls})`);
|
|
19705
|
+
if (table2.seed) {
|
|
19706
|
+
const reused = [table2.seed.reused.owner ? "the owner" : "", table2.seed.reused.otherUser ? "the other user" : ""].filter(Boolean);
|
|
19331
19707
|
lines.push(
|
|
19332
|
-
` One row for the owner and one for another user (${
|
|
19708
|
+
` 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
19709
|
);
|
|
19334
19710
|
}
|
|
19335
|
-
if (
|
|
19336
|
-
const tested =
|
|
19711
|
+
if (table2.unknownReason) lines.push(` Unknown: ${table2.unknownReason}`);
|
|
19712
|
+
const tested = table2.cells.filter((c) => c.actor !== "service_role");
|
|
19337
19713
|
const actorWidth = 14;
|
|
19338
19714
|
const actionWidth = 10;
|
|
19339
19715
|
lines.push(` ${"actor".padEnd(actorWidth)}${"action".padEnd(actionWidth)}${"expected".padEnd(10)}${"result".padEnd(9)}observed`);
|
|
19340
|
-
for (const
|
|
19341
|
-
lines.push(` ${
|
|
19716
|
+
for (const cell2 of tested) {
|
|
19717
|
+
lines.push(` ${cell2.actor.padEnd(actorWidth)}${cell2.action.padEnd(actionWidth)}${cell2.expected.padEnd(10)}${cell2.outcome.padEnd(9)}${cell2.observed.summary}`);
|
|
19342
19718
|
}
|
|
19343
|
-
const service =
|
|
19719
|
+
const service = table2.cells.filter((c) => c.actor === "service_role");
|
|
19344
19720
|
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
19721
|
}
|
|
19346
19722
|
const s = result.summary;
|
|
@@ -19348,8 +19724,8 @@ function renderRlsText(result) {
|
|
|
19348
19724
|
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
19725
|
lines.push(result.verdict);
|
|
19350
19726
|
const failed = result.tables.flatMap((t) => t.cells).filter((c) => c.outcome === "Fail");
|
|
19351
|
-
for (const
|
|
19352
|
-
lines.push(` Fail ${
|
|
19727
|
+
for (const cell2 of failed) {
|
|
19728
|
+
lines.push(` Fail ${cell2.actor} ${cell2.action} on ${cell2.table}: ${cell2.reason}`);
|
|
19353
19729
|
}
|
|
19354
19730
|
const unknown = result.tables.flatMap((t) => t.cells).filter((c) => c.outcome === "Unknown");
|
|
19355
19731
|
const unknownReasons = [...new Set(unknown.map((c) => c.reason))];
|
|
@@ -20078,7 +20454,7 @@ function buildMissionCards(artifact, providers) {
|
|
|
20078
20454
|
};
|
|
20079
20455
|
}
|
|
20080
20456
|
function projectNameFromPath(pathValue) {
|
|
20081
|
-
return (0,
|
|
20457
|
+
return (0, import_node_path37.basename)(pathValue.replace(/[\\/]$/, "")) || "current repo";
|
|
20082
20458
|
}
|
|
20083
20459
|
function gapSearchText(gap) {
|
|
20084
20460
|
return [
|
|
@@ -20489,19 +20865,19 @@ function mapArtifactToLocalUiState(artifact, presentProviderIds) {
|
|
|
20489
20865
|
selectedProviderId: selectedProvider.id,
|
|
20490
20866
|
currentPrompt: selectedProvider.nextFix?.prompt ?? "",
|
|
20491
20867
|
artifacts: {
|
|
20492
|
-
tasklist: (0,
|
|
20493
|
-
contextMap: (0,
|
|
20494
|
-
gateResult: (0,
|
|
20495
|
-
lastScan: (0,
|
|
20868
|
+
tasklist: (0, import_node_path37.join)(".viberaven", "agent-tasklist.md"),
|
|
20869
|
+
contextMap: (0, import_node_path37.join)(".viberaven", "context-map.json"),
|
|
20870
|
+
gateResult: (0, import_node_path37.join)(".viberaven", "gate-result.json"),
|
|
20871
|
+
lastScan: (0, import_node_path37.join)(".viberaven", "last-scan.json")
|
|
20496
20872
|
},
|
|
20497
20873
|
empty: false
|
|
20498
20874
|
};
|
|
20499
20875
|
}
|
|
20500
|
-
var
|
|
20876
|
+
var import_node_path37, CONNECT_PROVIDER_PRIORITY, providerDashboardUrls, providerHints, PROVIDER_OWNED_GAP;
|
|
20501
20877
|
var init_artifactAdapter = __esm({
|
|
20502
20878
|
"src/local-ui/artifactAdapter.ts"() {
|
|
20503
20879
|
"use strict";
|
|
20504
|
-
|
|
20880
|
+
import_node_path37 = require("node:path");
|
|
20505
20881
|
init_providerLogos();
|
|
20506
20882
|
init_buildTaskList();
|
|
20507
20883
|
init_prompts();
|
|
@@ -20804,18 +21180,18 @@ var init_planLimits = __esm({
|
|
|
20804
21180
|
// src/hosted/credentials.ts
|
|
20805
21181
|
function defaultConfigDir(env = process.env, platform = process.platform) {
|
|
20806
21182
|
const override = env.VIBERAVEN_CONFIG_DIR?.trim();
|
|
20807
|
-
if (override) return (0,
|
|
21183
|
+
if (override) return (0, import_node_path38.resolve)(override);
|
|
20808
21184
|
if (platform === "win32") {
|
|
20809
|
-
return (0,
|
|
21185
|
+
return (0, import_node_path38.join)(env.APPDATA?.trim() || (0, import_node_path38.join)((0, import_node_os2.homedir)(), "AppData", "Roaming"), "viberaven");
|
|
20810
21186
|
}
|
|
20811
21187
|
if (platform === "darwin") {
|
|
20812
|
-
return (0,
|
|
21188
|
+
return (0, import_node_path38.join)((0, import_node_os2.homedir)(), "Library", "Application Support", "viberaven");
|
|
20813
21189
|
}
|
|
20814
|
-
return (0,
|
|
21190
|
+
return (0, import_node_path38.join)(env.XDG_CONFIG_HOME?.trim() || (0, import_node_path38.join)((0, import_node_os2.homedir)(), ".config"), "viberaven");
|
|
20815
21191
|
}
|
|
20816
21192
|
function isInside(child, parent) {
|
|
20817
|
-
const rel = (0,
|
|
20818
|
-
return rel === "" || !rel.startsWith("..") && !(0,
|
|
21193
|
+
const rel = (0, import_node_path38.relative)((0, import_node_path38.resolve)(parent), (0, import_node_path38.resolve)(child));
|
|
21194
|
+
return rel === "" || !rel.startsWith("..") && !(0, import_node_path38.isAbsolute)(rel);
|
|
20819
21195
|
}
|
|
20820
21196
|
async function retryTransient(work) {
|
|
20821
21197
|
for (let attempt = 1; ; attempt += 1) {
|
|
@@ -20828,15 +21204,15 @@ async function retryTransient(work) {
|
|
|
20828
21204
|
}
|
|
20829
21205
|
}
|
|
20830
21206
|
}
|
|
20831
|
-
var
|
|
21207
|
+
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
21208
|
var init_credentials = __esm({
|
|
20833
21209
|
"src/hosted/credentials.ts"() {
|
|
20834
21210
|
"use strict";
|
|
20835
|
-
|
|
21211
|
+
import_node_child_process9 = require("node:child_process");
|
|
20836
21212
|
import_node_crypto4 = require("node:crypto");
|
|
20837
|
-
|
|
20838
|
-
|
|
20839
|
-
|
|
21213
|
+
import_promises30 = require("node:fs/promises");
|
|
21214
|
+
import_node_os2 = require("node:os");
|
|
21215
|
+
import_node_path38 = require("node:path");
|
|
20840
21216
|
CREDENTIALS_FILE_NAME = "credentials.json";
|
|
20841
21217
|
CredentialLocationError = class extends Error {
|
|
20842
21218
|
};
|
|
@@ -20847,10 +21223,10 @@ var init_credentials = __esm({
|
|
|
20847
21223
|
}
|
|
20848
21224
|
};
|
|
20849
21225
|
restrictWithIcacls = (path) => {
|
|
20850
|
-
const name = (0,
|
|
21226
|
+
const name = (0, import_node_os2.userInfo)().username;
|
|
20851
21227
|
const user = process.env.USERDOMAIN ? `${process.env.USERDOMAIN}\\${name}` : name;
|
|
20852
21228
|
return new Promise((resolveRestrict, reject) => {
|
|
20853
|
-
(0,
|
|
21229
|
+
(0, import_node_child_process9.execFile)("icacls", [path, "/inheritance:r", "/grant:r", `${user}:F`], { windowsHide: true, timeout: 1e4 }, (error) => {
|
|
20854
21230
|
if (error) reject(error);
|
|
20855
21231
|
else resolveRestrict();
|
|
20856
21232
|
});
|
|
@@ -20863,9 +21239,9 @@ var init_credentials = __esm({
|
|
|
20863
21239
|
constructor(configDir, workspacePath, options = {}) {
|
|
20864
21240
|
this.configDir = configDir;
|
|
20865
21241
|
this.workspacePath = workspacePath;
|
|
20866
|
-
this.path = (0,
|
|
21242
|
+
this.path = (0, import_node_path38.join)(configDir, CREDENTIALS_FILE_NAME);
|
|
20867
21243
|
this.restrict = options.restrict !== void 0 ? options.restrict : process.platform === "win32" ? restrictWithIcacls : null;
|
|
20868
|
-
this.fs = { readFile: (path, encoding) => (0,
|
|
21244
|
+
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
21245
|
}
|
|
20870
21246
|
path;
|
|
20871
21247
|
restrict;
|
|
@@ -20914,15 +21290,15 @@ var init_credentials = __esm({
|
|
|
20914
21290
|
`Refusing to store the VibeRaven sign-in inside this project (${this.configDir}). Set VIBERAVEN_CONFIG_DIR to a folder outside the repo.`
|
|
20915
21291
|
);
|
|
20916
21292
|
}
|
|
20917
|
-
await (0,
|
|
21293
|
+
await (0, import_promises30.mkdir)(this.configDir, { recursive: true, mode: 448 });
|
|
20918
21294
|
const temp = `${this.path}.${(0, import_node_crypto4.randomUUID)()}.tmp`;
|
|
20919
|
-
await (0,
|
|
21295
|
+
await (0, import_promises30.writeFile)(temp, `${JSON.stringify(body, null, 2)}
|
|
20920
21296
|
`, { encoding: "utf8", mode: 384 });
|
|
20921
|
-
await (0,
|
|
21297
|
+
await (0, import_promises30.chmod)(temp, 384).catch(() => void 0);
|
|
20922
21298
|
try {
|
|
20923
21299
|
if (this.restrict) await this.restrict(temp);
|
|
20924
21300
|
} catch (error) {
|
|
20925
|
-
await (0,
|
|
21301
|
+
await (0, import_promises30.rm)(temp, { force: true });
|
|
20926
21302
|
const code = error?.code;
|
|
20927
21303
|
const toolMissing = code === "ENOENT";
|
|
20928
21304
|
throw new CredentialPermissionError(
|
|
@@ -20933,17 +21309,17 @@ var init_credentials = __esm({
|
|
|
20933
21309
|
if (guard) {
|
|
20934
21310
|
const onDisk = await this.readFor(guard.apiBaseUrl);
|
|
20935
21311
|
if (onDisk?.accessToken !== guard.accessToken || guard.isCurrent && !guard.isCurrent()) {
|
|
20936
|
-
await (0,
|
|
21312
|
+
await (0, import_promises30.rm)(temp, { force: true });
|
|
20937
21313
|
return false;
|
|
20938
21314
|
}
|
|
20939
21315
|
}
|
|
20940
21316
|
try {
|
|
20941
21317
|
await retryTransient(() => this.fs.rename(temp, this.path));
|
|
20942
21318
|
} catch (error) {
|
|
20943
|
-
await (0,
|
|
21319
|
+
await (0, import_promises30.rm)(temp, { force: true });
|
|
20944
21320
|
throw error;
|
|
20945
21321
|
}
|
|
20946
|
-
await (0,
|
|
21322
|
+
await (0, import_promises30.chmod)(this.path, 384).catch(() => void 0);
|
|
20947
21323
|
return true;
|
|
20948
21324
|
}
|
|
20949
21325
|
/** Keeps the stored plan label in step with what the API last said. */
|
|
@@ -21312,7 +21688,7 @@ async function buildHostedCheckPayload(workspacePath) {
|
|
|
21312
21688
|
throw new Error("Could not assemble the full check request.");
|
|
21313
21689
|
}
|
|
21314
21690
|
function finalizePayload(payload) {
|
|
21315
|
-
const folder = (0,
|
|
21691
|
+
const folder = (0, import_node_path39.basename)(payload.workspacePath.split("\\").join("/").replace(/\/+$/, "")) || "project";
|
|
21316
21692
|
const redacted = redactPayload({ ...payload, workspacePath: folder });
|
|
21317
21693
|
return { payload: redacted.payload, redactions: redacted.count };
|
|
21318
21694
|
}
|
|
@@ -21343,13 +21719,13 @@ function splitSections(prompt) {
|
|
|
21343
21719
|
chars: section.length
|
|
21344
21720
|
}));
|
|
21345
21721
|
}
|
|
21346
|
-
var
|
|
21722
|
+
var import_node_path39, PayloadCaptured;
|
|
21347
21723
|
var init_payload = __esm({
|
|
21348
21724
|
"src/hosted/payload.ts"() {
|
|
21349
21725
|
"use strict";
|
|
21350
21726
|
init_fileScanner();
|
|
21351
21727
|
init_orchestrator();
|
|
21352
|
-
|
|
21728
|
+
import_node_path39 = require("node:path");
|
|
21353
21729
|
init_config();
|
|
21354
21730
|
init_redact();
|
|
21355
21731
|
PayloadCaptured = class extends Error {
|
|
@@ -21387,7 +21763,7 @@ function shapeResult(response, now = /* @__PURE__ */ new Date()) {
|
|
|
21387
21763
|
const usage = toUsage(response.usage);
|
|
21388
21764
|
const freeKeys = new Set(FREE_TRIAL_UNLOCKED_MAP_CATEGORY_KEYS);
|
|
21389
21765
|
const unlocked = (key) => usage.plan === "pro" || freeKeys.has(key) && response.usage.unlockedMapCategoryKeys.includes(key);
|
|
21390
|
-
const sorted = [...output.gaps].sort((a, b3) =>
|
|
21766
|
+
const sorted = [...output.gaps].sort((a, b3) => SEVERITY_RANK4[a.severity] - SEVERITY_RANK4[b3.severity]);
|
|
21391
21767
|
const areas = PRODUCTION_MAP_LANES.map((lane) => {
|
|
21392
21768
|
const gaps = sorted.filter((gap) => gap.primaryMapCategory === lane.extensionKey);
|
|
21393
21769
|
const locked = !unlocked(lane.extensionKey);
|
|
@@ -21425,7 +21801,7 @@ function readServerMessage(body) {
|
|
|
21425
21801
|
return body.slice(0, 300);
|
|
21426
21802
|
}
|
|
21427
21803
|
}
|
|
21428
|
-
var import_node_crypto5, PRICING_URL, FREE_LIMIT_MESSAGE, PREVIEW_TTL_MS, STATUS_TIMEOUT_MS, STATUS_CACHE_MS, MAX_PREVIEWS, TRANSIENT_REASONS,
|
|
21804
|
+
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
21805
|
var init_fullCheck = __esm({
|
|
21430
21806
|
"src/hosted/fullCheck.ts"() {
|
|
21431
21807
|
"use strict";
|
|
@@ -21444,7 +21820,7 @@ var init_fullCheck = __esm({
|
|
|
21444
21820
|
STATUS_CACHE_MS = 5e3;
|
|
21445
21821
|
MAX_PREVIEWS = 5;
|
|
21446
21822
|
TRANSIENT_REASONS = /* @__PURE__ */ new Set(["network", "timeout", "server", "rate_limited", "busy"]);
|
|
21447
|
-
|
|
21823
|
+
SEVERITY_RANK4 = { critical: 0, warning: 1, info: 2 };
|
|
21448
21824
|
FullCheckService = class {
|
|
21449
21825
|
constructor(workspacePath, options = {}) {
|
|
21450
21826
|
this.workspacePath = workspacePath;
|
|
@@ -21815,10 +22191,10 @@ async function connectProviderMcp(cwd, providerId) {
|
|
|
21815
22191
|
if (!recipe) {
|
|
21816
22192
|
return { ok: false, message: `No known MCP server for ${providerId} yet. Add one manually to .mcp.json.` };
|
|
21817
22193
|
}
|
|
21818
|
-
const configPath = (0,
|
|
22194
|
+
const configPath = (0, import_node_path40.join)(cwd, ".mcp.json");
|
|
21819
22195
|
let config = {};
|
|
21820
22196
|
try {
|
|
21821
|
-
const raw = await (0,
|
|
22197
|
+
const raw = await (0, import_promises31.readFile)(configPath, "utf8");
|
|
21822
22198
|
const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
|
|
21823
22199
|
if (!isRecord5(parsed) || parsed.mcpServers !== void 0 && !isRecord5(parsed.mcpServers)) {
|
|
21824
22200
|
return { ok: false, message: "Existing .mcp.json has an unsupported structure. It was left unchanged." };
|
|
@@ -21835,7 +22211,7 @@ async function connectProviderMcp(cwd, providerId) {
|
|
|
21835
22211
|
}
|
|
21836
22212
|
servers[providerId] = recipe.entry;
|
|
21837
22213
|
const next = { ...config, mcpServers: servers };
|
|
21838
|
-
await (0,
|
|
22214
|
+
await (0, import_promises31.writeFile)(configPath, `${JSON.stringify(next, null, 2)}
|
|
21839
22215
|
`, "utf8");
|
|
21840
22216
|
const suffix = recipe.note ? ` ${recipe.note}` : "";
|
|
21841
22217
|
return { ok: true, message: `Added ${providerId} MCP server to .mcp.json.${suffix}`, path: ".mcp.json" };
|
|
@@ -21888,7 +22264,7 @@ async function defaultScanRunner(flags, positional) {
|
|
|
21888
22264
|
}
|
|
21889
22265
|
async function readPackageJson(cwd) {
|
|
21890
22266
|
try {
|
|
21891
|
-
const raw = await (0,
|
|
22267
|
+
const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, "package.json"), "utf8");
|
|
21892
22268
|
const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
|
|
21893
22269
|
return isRecord5(parsed) ? parsed : {};
|
|
21894
22270
|
} catch {
|
|
@@ -21911,7 +22287,7 @@ async function listProjectFiles(cwd, maxFiles = 1800) {
|
|
|
21911
22287
|
if (files.length >= maxFiles) return;
|
|
21912
22288
|
let entries;
|
|
21913
22289
|
try {
|
|
21914
|
-
entries = await (0,
|
|
22290
|
+
entries = await (0, import_promises31.readdir)(dir, { withFileTypes: true });
|
|
21915
22291
|
} catch {
|
|
21916
22292
|
return;
|
|
21917
22293
|
}
|
|
@@ -21919,7 +22295,7 @@ async function listProjectFiles(cwd, maxFiles = 1800) {
|
|
|
21919
22295
|
if (files.length >= maxFiles) return;
|
|
21920
22296
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
21921
22297
|
if (entry.isDirectory()) {
|
|
21922
|
-
if (!skipped.has(entry.name)) await walk((0,
|
|
22298
|
+
if (!skipped.has(entry.name)) await walk((0, import_node_path40.join)(dir, entry.name), rel);
|
|
21923
22299
|
continue;
|
|
21924
22300
|
}
|
|
21925
22301
|
if (entry.isFile()) files.push(rel.toLowerCase());
|
|
@@ -21938,7 +22314,7 @@ async function readEnvKeys(cwd) {
|
|
|
21938
22314
|
const keys = /* @__PURE__ */ new Set();
|
|
21939
22315
|
for (const name of [".env.example", ".env"]) {
|
|
21940
22316
|
try {
|
|
21941
|
-
const raw = await (0,
|
|
22317
|
+
const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, name), "utf8");
|
|
21942
22318
|
for (const line of raw.split(/\r?\n/)) {
|
|
21943
22319
|
const trimmed = line.trim();
|
|
21944
22320
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -22032,7 +22408,7 @@ function applyProviderDetection(state, deps, files) {
|
|
|
22032
22408
|
}
|
|
22033
22409
|
function runGit(cwd, args, timeoutMs = 1500) {
|
|
22034
22410
|
return safeGitInvocation(cwd, args).then(
|
|
22035
|
-
(
|
|
22411
|
+
(git2) => spawnGit(cwd, git2.args, git2.env, timeoutMs),
|
|
22036
22412
|
(error) => {
|
|
22037
22413
|
if (error instanceof SafeGitRefusal) throw error;
|
|
22038
22414
|
return "";
|
|
@@ -22041,7 +22417,7 @@ function runGit(cwd, args, timeoutMs = 1500) {
|
|
|
22041
22417
|
}
|
|
22042
22418
|
function spawnGit(cwd, args, env, timeoutMs) {
|
|
22043
22419
|
return new Promise((resolveGit) => {
|
|
22044
|
-
const child = (0,
|
|
22420
|
+
const child = (0, import_node_child_process10.spawn)("git", args, {
|
|
22045
22421
|
cwd,
|
|
22046
22422
|
env,
|
|
22047
22423
|
windowsHide: true,
|
|
@@ -22104,7 +22480,7 @@ function parseGitShortstat(raw) {
|
|
|
22104
22480
|
}
|
|
22105
22481
|
function runGitCommand(cwd, args, timeoutMs = 5e3) {
|
|
22106
22482
|
return safeGitInvocation(cwd, args).then(
|
|
22107
|
-
(
|
|
22483
|
+
(git2) => spawnGitCommand(cwd, git2.args, git2.env, timeoutMs),
|
|
22108
22484
|
(error) => ({
|
|
22109
22485
|
code: 1,
|
|
22110
22486
|
stdout: "",
|
|
@@ -22114,7 +22490,7 @@ function runGitCommand(cwd, args, timeoutMs = 5e3) {
|
|
|
22114
22490
|
}
|
|
22115
22491
|
function spawnGitCommand(cwd, args, env, timeoutMs) {
|
|
22116
22492
|
return new Promise((resolveCommand) => {
|
|
22117
|
-
const child = (0,
|
|
22493
|
+
const child = (0, import_node_child_process10.spawn)("git", args, {
|
|
22118
22494
|
cwd,
|
|
22119
22495
|
env,
|
|
22120
22496
|
windowsHide: true,
|
|
@@ -22256,7 +22632,7 @@ async function codexModelOptions() {
|
|
|
22256
22632
|
{ id: "gpt-5.4-mini", label: "GPT-5.4 Mini" }
|
|
22257
22633
|
];
|
|
22258
22634
|
try {
|
|
22259
|
-
const raw = await (0,
|
|
22635
|
+
const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)((0, import_node_os3.homedir)(), ".codex", "models_cache.json"), "utf8");
|
|
22260
22636
|
const parsed = JSON.parse(raw);
|
|
22261
22637
|
const list = Array.isArray(parsed) ? parsed : parsed.models ?? [];
|
|
22262
22638
|
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 +22656,7 @@ function cliModelDefaults(id) {
|
|
|
22280
22656
|
}
|
|
22281
22657
|
function detectCommand(command, cwd) {
|
|
22282
22658
|
return new Promise((resolveDetect) => {
|
|
22283
|
-
const child = (0,
|
|
22659
|
+
const child = (0, import_node_child_process10.spawn)(process.platform === "win32" ? "where.exe" : "sh", process.platform === "win32" ? [command] : ["-lc", `command -v ${command}`], {
|
|
22284
22660
|
cwd,
|
|
22285
22661
|
windowsHide: true,
|
|
22286
22662
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -22307,7 +22683,7 @@ function commandMissingFromOutput(text) {
|
|
|
22307
22683
|
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
22684
|
}
|
|
22309
22685
|
function redactProbeOutput(output) {
|
|
22310
|
-
const home = (0,
|
|
22686
|
+
const home = (0, import_node_os3.homedir)();
|
|
22311
22687
|
const homeVariants = [home, home.split("\\").join("\\\\"), home.split("\\").join("/")].filter((value) => value.length > 3);
|
|
22312
22688
|
let scrubbed = output;
|
|
22313
22689
|
for (const variant of homeVariants) scrubbed = scrubbed.split(variant).join("~");
|
|
@@ -22367,7 +22743,7 @@ function setupCommandFor(cliId, step) {
|
|
|
22367
22743
|
function runShellCommand(command, cwd, timeoutMs = 18e4) {
|
|
22368
22744
|
return new Promise((resolveRun) => {
|
|
22369
22745
|
const useCmd = process.platform === "win32";
|
|
22370
|
-
const child = (0,
|
|
22746
|
+
const child = (0, import_node_child_process10.spawn)(useCmd ? process.env.ComSpec || "cmd.exe" : "sh", useCmd ? ["/d", "/s", "/c", command] : ["-lc", command], {
|
|
22371
22747
|
cwd,
|
|
22372
22748
|
windowsHide: true,
|
|
22373
22749
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -22401,7 +22777,7 @@ function openSystemTerminal(command, cwd) {
|
|
|
22401
22777
|
const fullCommand = process.platform === "win32" ? `cd /d ${quotedCwd} && ${command}` : `cd ${quotedCwd} && ${command}`;
|
|
22402
22778
|
let child;
|
|
22403
22779
|
if (process.platform === "win32") {
|
|
22404
|
-
child = (0,
|
|
22780
|
+
child = (0, import_node_child_process10.spawn)("cmd.exe", ["/c", "start", "cmd.exe", "/k", fullCommand], {
|
|
22405
22781
|
cwd,
|
|
22406
22782
|
windowsHide: true,
|
|
22407
22783
|
stdio: "ignore",
|
|
@@ -22409,9 +22785,9 @@ function openSystemTerminal(command, cwd) {
|
|
|
22409
22785
|
});
|
|
22410
22786
|
} else if (process.platform === "darwin") {
|
|
22411
22787
|
const script = `tell application "Terminal" to do script "${fullCommand.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
22412
|
-
child = (0,
|
|
22788
|
+
child = (0, import_node_child_process10.spawn)("osascript", ["-e", script], { cwd, windowsHide: true, stdio: "ignore", detached: true });
|
|
22413
22789
|
} else {
|
|
22414
|
-
child = (0,
|
|
22790
|
+
child = (0, import_node_child_process10.spawn)("x-terminal-emulator", ["-e", `bash -lc ${JSON.stringify(fullCommand)}`], {
|
|
22415
22791
|
cwd,
|
|
22416
22792
|
windowsHide: true,
|
|
22417
22793
|
stdio: "ignore",
|
|
@@ -22459,19 +22835,19 @@ function isPlaceholderProjectName(value) {
|
|
|
22459
22835
|
async function readMcpEvidence(cwd) {
|
|
22460
22836
|
const candidates = MCP_CONFIG_PATHS.map((relativePath) => ({
|
|
22461
22837
|
source: relativePath.replace(/\\/g, "/"),
|
|
22462
|
-
path: (0,
|
|
22838
|
+
path: (0, import_node_path40.resolve)(cwd, relativePath)
|
|
22463
22839
|
}));
|
|
22464
22840
|
const home = process.env.USERPROFILE || process.env.HOME;
|
|
22465
22841
|
if (home) {
|
|
22466
22842
|
candidates.push({
|
|
22467
22843
|
source: "~/.codex/config.toml",
|
|
22468
|
-
path: (0,
|
|
22844
|
+
path: (0, import_node_path40.resolve)(home, ".codex", "config.toml")
|
|
22469
22845
|
});
|
|
22470
22846
|
}
|
|
22471
22847
|
const evidence = [];
|
|
22472
22848
|
for (const candidate of candidates) {
|
|
22473
22849
|
try {
|
|
22474
|
-
const text = await (0,
|
|
22850
|
+
const text = await (0, import_promises31.readFile)(candidate.path, "utf8");
|
|
22475
22851
|
if (/mcp/i.test(text)) evidence.push({ source: candidate.source, text });
|
|
22476
22852
|
} catch {
|
|
22477
22853
|
}
|
|
@@ -22562,7 +22938,7 @@ async function enrichProjectMetadata(state, cwd) {
|
|
|
22562
22938
|
async function readState(cwd) {
|
|
22563
22939
|
let state;
|
|
22564
22940
|
try {
|
|
22565
|
-
const raw = await (0,
|
|
22941
|
+
const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, ".viberaven", "last-scan.json"), "utf8");
|
|
22566
22942
|
const packageJson = await readPackageJson(cwd);
|
|
22567
22943
|
const deps = dependencyNames(packageJson);
|
|
22568
22944
|
const files = await listProjectFiles(cwd);
|
|
@@ -22808,7 +23184,7 @@ function quoteWindowsCmdArg(value) {
|
|
|
22808
23184
|
}
|
|
22809
23185
|
async function whereExe(command, cwd) {
|
|
22810
23186
|
return new Promise((resolvePath) => {
|
|
22811
|
-
const child = (0,
|
|
23187
|
+
const child = (0, import_node_child_process10.spawn)("where.exe", [command], {
|
|
22812
23188
|
cwd,
|
|
22813
23189
|
windowsHide: true,
|
|
22814
23190
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -22829,7 +23205,7 @@ async function whereExe(command, cwd) {
|
|
|
22829
23205
|
}
|
|
22830
23206
|
function expandShimPath(value, base) {
|
|
22831
23207
|
const relative8 = value.replace(/^%(?:dp0%\\+|~dp0\\*)/i, "");
|
|
22832
|
-
return relative8 === value ? value : (0,
|
|
23208
|
+
return relative8 === value ? value : (0, import_node_path40.join)(base, ...relative8.split(/[\\/]+/));
|
|
22833
23209
|
}
|
|
22834
23210
|
function findShimTarget(lines, pattern, accept = () => true) {
|
|
22835
23211
|
for (const line of lines) {
|
|
@@ -22840,21 +23216,21 @@ function findShimTarget(lines, pattern, accept = () => true) {
|
|
|
22840
23216
|
}
|
|
22841
23217
|
async function resolveWindowsShimTarget(shimPath) {
|
|
22842
23218
|
try {
|
|
22843
|
-
const content = await (0,
|
|
22844
|
-
const base = (0,
|
|
23219
|
+
const content = await (0, import_promises31.readFile)(shimPath, "utf8");
|
|
23220
|
+
const base = (0, import_node_path40.dirname)(shimPath);
|
|
22845
23221
|
const lines = content.split(/\r?\n/);
|
|
22846
23222
|
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
23223
|
const script = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.js)"/i);
|
|
22848
23224
|
if (script) {
|
|
22849
23225
|
return {
|
|
22850
23226
|
command: process.execPath,
|
|
22851
|
-
args: [(0,
|
|
23227
|
+
args: [(0, import_node_path40.join)(base, ...script.match[1].split(/[\\/]+/)), ...trailingArgs(script.line, script.match.index + script.match[0].length)]
|
|
22852
23228
|
};
|
|
22853
23229
|
}
|
|
22854
|
-
const binary = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.exe)"/i, (target) => (0,
|
|
23230
|
+
const binary = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.exe)"/i, (target) => (0, import_node_path40.basename)(target).toLowerCase() !== "node.exe");
|
|
22855
23231
|
if (binary) {
|
|
22856
23232
|
return {
|
|
22857
|
-
command: (0,
|
|
23233
|
+
command: (0, import_node_path40.join)(base, ...binary.match[1].split(/[\\/]+/)),
|
|
22858
23234
|
args: trailingArgs(binary.line, binary.match.index + binary.match[0].length)
|
|
22859
23235
|
};
|
|
22860
23236
|
}
|
|
@@ -22875,7 +23251,7 @@ function killProcessTree(pid, fallback) {
|
|
|
22875
23251
|
return;
|
|
22876
23252
|
}
|
|
22877
23253
|
if (process.platform === "win32") {
|
|
22878
|
-
const killer = (0,
|
|
23254
|
+
const killer = (0, import_node_child_process10.spawn)("taskkill", ["/pid", String(pid), "/T", "/F"], { windowsHide: true, stdio: "ignore" });
|
|
22879
23255
|
killer.once("error", fallback);
|
|
22880
23256
|
killer.once("close", (code) => {
|
|
22881
23257
|
if (code !== 0) fallback();
|
|
@@ -22900,7 +23276,7 @@ async function runAgentCommand(command, args, cwd, stdin, runId) {
|
|
|
22900
23276
|
const spawnCommand = shim?.command ?? (useCmdShim ? process.env.ComSpec || "cmd.exe" : command);
|
|
22901
23277
|
const spawnArgs = shim ? [...shim.args, ...args] : useCmdShim ? ["/d", "/s", "/c", [command, ...args.map(quoteWindowsCmdArg)].join(" ")] : args;
|
|
22902
23278
|
return new Promise((resolveRun) => {
|
|
22903
|
-
const child = (0,
|
|
23279
|
+
const child = (0, import_node_child_process10.spawn)(spawnCommand, spawnArgs, {
|
|
22904
23280
|
cwd,
|
|
22905
23281
|
windowsHide: true,
|
|
22906
23282
|
// A separate process group lets Stop and timeout terminate descendants on Unix too.
|
|
@@ -22979,8 +23355,8 @@ async function writeChatContextArtifact(cwd, request, prompt) {
|
|
|
22979
23355
|
"",
|
|
22980
23356
|
prompt
|
|
22981
23357
|
].filter((line) => typeof line === "string");
|
|
22982
|
-
await (0,
|
|
22983
|
-
await (0,
|
|
23358
|
+
await (0, import_promises31.mkdir)((0, import_node_path40.join)(cwd, ".viberaven"), { recursive: true });
|
|
23359
|
+
await (0, import_promises31.writeFile)((0, import_node_path40.join)(cwd, ".viberaven", "chat-context.md"), `${lines.join("\n")}
|
|
22984
23360
|
`, "utf8");
|
|
22985
23361
|
} catch {
|
|
22986
23362
|
}
|
|
@@ -23000,13 +23376,13 @@ async function runAgentChat(cwd, request) {
|
|
|
23000
23376
|
}
|
|
23001
23377
|
const prompt = await buildAgentChatPrompt(request, cwd);
|
|
23002
23378
|
await writeChatContextArtifact(cwd, request, prompt);
|
|
23003
|
-
const outputPath = request.cliId === "codex" ? (0,
|
|
23004
|
-
if (outputPath) await (0,
|
|
23379
|
+
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;
|
|
23380
|
+
if (outputPath) await (0, import_promises31.mkdir)((0, import_node_path40.dirname)(outputPath), { recursive: true });
|
|
23005
23381
|
const { command, args, stdin } = commandForAgentChat(request, prompt, cwd, outputPath);
|
|
23006
23382
|
const result = await runAgentCommand(command, args, cwd, stdin, request.runId);
|
|
23007
23383
|
if (outputPath) {
|
|
23008
23384
|
try {
|
|
23009
|
-
const finalMessage = (await (0,
|
|
23385
|
+
const finalMessage = (await (0, import_promises31.readFile)(outputPath, "utf8")).trim();
|
|
23010
23386
|
if (finalMessage) return { ...result, output: finalMessage };
|
|
23011
23387
|
} catch {
|
|
23012
23388
|
}
|
|
@@ -23018,8 +23394,8 @@ function firstGapId(state) {
|
|
|
23018
23394
|
return state.providers.find((provider2) => provider2.nextFix?.id)?.nextFix?.id;
|
|
23019
23395
|
}
|
|
23020
23396
|
async function runLocalAction(cwd, intent) {
|
|
23021
|
-
const artifactDir = (0,
|
|
23022
|
-
await (0,
|
|
23397
|
+
const artifactDir = (0, import_node_path40.join)(cwd, ".viberaven");
|
|
23398
|
+
await (0, import_promises31.mkdir)(artifactDir, { recursive: true });
|
|
23023
23399
|
if (intent === "preview_rehearsal") {
|
|
23024
23400
|
const state2 = await readState(cwd);
|
|
23025
23401
|
const topProvider = state2.providers[0];
|
|
@@ -23060,7 +23436,7 @@ async function runLocalAction(cwd, intent) {
|
|
|
23060
23436
|
proof,
|
|
23061
23437
|
""
|
|
23062
23438
|
].join("\n");
|
|
23063
|
-
await (0,
|
|
23439
|
+
await (0, import_promises31.writeFile)((0, import_node_path40.join)(artifactDir, "preview-rehearsal.md"), markdown, "utf8");
|
|
23064
23440
|
return {
|
|
23065
23441
|
title: "Preview rehearsal ready",
|
|
23066
23442
|
summary: `${projectName}: ${gateLabel} with ${state2.providers.length} provider beat${state2.providers.length === 1 ? "" : "s"}`,
|
|
@@ -23070,7 +23446,7 @@ async function runLocalAction(cwd, intent) {
|
|
|
23070
23446
|
if (intent === "audit_vercel_supabase") {
|
|
23071
23447
|
const auditInput = await collectVercelSupabaseAuditInput(cwd);
|
|
23072
23448
|
const result2 = buildVercelSupabaseAudit(auditInput);
|
|
23073
|
-
await (0,
|
|
23449
|
+
await (0, import_promises31.writeFile)((0, import_node_path40.join)(artifactDir, "audit-vercel-supabase.txt"), `${renderVercelSupabaseAudit(result2)}
|
|
23074
23450
|
`, "utf8");
|
|
23075
23451
|
return {
|
|
23076
23452
|
title: result2.status === "pass" ? "Audit passed" : "Audit needs work",
|
|
@@ -23098,7 +23474,7 @@ async function runLocalAction(cwd, intent) {
|
|
|
23098
23474
|
const mode = intent === "heal_prompt" ? "prompt" : intent === "heal_apply" ? "apply" : "plan";
|
|
23099
23475
|
const result = await runHealCommand({ cwd, mode, gapId, yes: mode === "apply", noVerify: true });
|
|
23100
23476
|
if (mode === "apply") {
|
|
23101
|
-
await (0,
|
|
23477
|
+
await (0, import_promises31.writeFile)((0, import_node_path40.join)(artifactDir, "heal-apply-result.json"), `${JSON.stringify(result, null, 2)}
|
|
23102
23478
|
`, "utf8");
|
|
23103
23479
|
}
|
|
23104
23480
|
return {
|
|
@@ -23108,14 +23484,14 @@ async function runLocalAction(cwd, intent) {
|
|
|
23108
23484
|
};
|
|
23109
23485
|
}
|
|
23110
23486
|
async function sendArtifact(cwd, rel, response, contentType = "text/plain; charset=utf-8") {
|
|
23111
|
-
const absolute = (0,
|
|
23112
|
-
const root = (0,
|
|
23487
|
+
const absolute = (0, import_node_path40.resolve)(cwd, rel);
|
|
23488
|
+
const root = (0, import_node_path40.resolve)(cwd);
|
|
23113
23489
|
if (!absolute.startsWith(root)) {
|
|
23114
23490
|
sendText(response, 403, "Forbidden");
|
|
23115
23491
|
return;
|
|
23116
23492
|
}
|
|
23117
23493
|
try {
|
|
23118
|
-
const text = await (0,
|
|
23494
|
+
const text = await (0, import_promises31.readFile)(absolute, "utf8");
|
|
23119
23495
|
response.writeHead(200, { "content-type": contentType });
|
|
23120
23496
|
response.end(text);
|
|
23121
23497
|
} catch {
|
|
@@ -23124,7 +23500,7 @@ async function sendArtifact(cwd, rel, response, contentType = "text/plain; chars
|
|
|
23124
23500
|
}
|
|
23125
23501
|
async function sendReportArtifact(cwd, response) {
|
|
23126
23502
|
try {
|
|
23127
|
-
const text = await (0,
|
|
23503
|
+
const text = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, ".viberaven", "report.html"), "utf8");
|
|
23128
23504
|
response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
23129
23505
|
response.end(text);
|
|
23130
23506
|
return;
|
|
@@ -23251,7 +23627,7 @@ async function sendReportArtifact(cwd, response) {
|
|
|
23251
23627
|
}
|
|
23252
23628
|
async function sendExtensionIcon(cwd, response) {
|
|
23253
23629
|
try {
|
|
23254
|
-
const body = await (0,
|
|
23630
|
+
const body = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, "media", "extension-icon.png"));
|
|
23255
23631
|
sendBinary(response, 200, "image/png", body);
|
|
23256
23632
|
} catch {
|
|
23257
23633
|
sendText(response, 404, "Icon not found");
|
|
@@ -23264,15 +23640,15 @@ async function sendMangaAsset(pathname, response) {
|
|
|
23264
23640
|
return;
|
|
23265
23641
|
}
|
|
23266
23642
|
const candidates = [
|
|
23267
|
-
(0,
|
|
23643
|
+
(0, import_node_path40.join)(__dirname, "manga", name),
|
|
23268
23644
|
// installed + dev: <pkg>/dist/manga
|
|
23269
|
-
(0,
|
|
23645
|
+
(0, import_node_path40.join)(__dirname, "..", "assets", "manga", name),
|
|
23270
23646
|
// fallback: <pkg>/assets/manga (source)
|
|
23271
|
-
(0,
|
|
23647
|
+
(0, import_node_path40.join)(__dirname, "..", "..", "assets", "manga", name)
|
|
23272
23648
|
];
|
|
23273
23649
|
for (const candidate of candidates) {
|
|
23274
23650
|
try {
|
|
23275
|
-
const body = await (0,
|
|
23651
|
+
const body = await (0, import_promises31.readFile)(candidate);
|
|
23276
23652
|
response.setHeader("cache-control", "no-cache");
|
|
23277
23653
|
sendBinary(response, 200, contentTypeForAsset(candidate), body);
|
|
23278
23654
|
return;
|
|
@@ -23282,7 +23658,7 @@ async function sendMangaAsset(pathname, response) {
|
|
|
23282
23658
|
sendText(response, 404, "Asset not found");
|
|
23283
23659
|
}
|
|
23284
23660
|
function contentTypeForAsset(pathname) {
|
|
23285
|
-
switch ((0,
|
|
23661
|
+
switch ((0, import_node_path40.extname)(pathname).toLowerCase()) {
|
|
23286
23662
|
case ".svg":
|
|
23287
23663
|
return "image/svg+xml; charset=utf-8";
|
|
23288
23664
|
case ".png":
|
|
@@ -23299,14 +23675,14 @@ function contentTypeForAsset(pathname) {
|
|
|
23299
23675
|
}
|
|
23300
23676
|
async function sendBundledReportAsset(pathname, response) {
|
|
23301
23677
|
const rel = decodeURIComponent(pathname.replace(/^\/report\//, ""));
|
|
23302
|
-
const assetRoot = (0,
|
|
23303
|
-
const absolute = (0,
|
|
23678
|
+
const assetRoot = (0, import_node_path40.resolve)(getBundledReportAssetsDir());
|
|
23679
|
+
const absolute = (0, import_node_path40.resolve)(assetRoot, rel);
|
|
23304
23680
|
if (!absolute.startsWith(assetRoot)) {
|
|
23305
23681
|
sendText(response, 403, "Forbidden");
|
|
23306
23682
|
return;
|
|
23307
23683
|
}
|
|
23308
23684
|
try {
|
|
23309
|
-
const body = await (0,
|
|
23685
|
+
const body = await (0, import_promises31.readFile)(absolute);
|
|
23310
23686
|
sendBinary(response, 200, contentTypeForAsset(absolute), body);
|
|
23311
23687
|
} catch {
|
|
23312
23688
|
sendText(response, 404, "Asset not found");
|
|
@@ -23328,18 +23704,18 @@ async function bundleStackClient(entryPath) {
|
|
|
23328
23704
|
return output.text;
|
|
23329
23705
|
}
|
|
23330
23706
|
async function sendStackAppJs(response) {
|
|
23331
|
-
const distPath = (0,
|
|
23707
|
+
const distPath = (0, import_node_path40.join)(__dirname, "stack", "app.js");
|
|
23332
23708
|
try {
|
|
23333
|
-
const body = await (0,
|
|
23709
|
+
const body = await (0, import_promises31.readFile)(distPath);
|
|
23334
23710
|
response.writeHead(200, { "content-type": "application/javascript; charset=utf-8", "cache-control": "no-store" });
|
|
23335
23711
|
response.end(body);
|
|
23336
23712
|
return;
|
|
23337
23713
|
} catch {
|
|
23338
23714
|
}
|
|
23339
|
-
const entryPath = (0,
|
|
23715
|
+
const entryPath = (0, import_node_path40.join)(__dirname, "stack", "main.ts");
|
|
23340
23716
|
let mtimeMs;
|
|
23341
23717
|
try {
|
|
23342
|
-
mtimeMs = (await (0,
|
|
23718
|
+
mtimeMs = (await (0, import_promises31.stat)(entryPath)).mtimeMs;
|
|
23343
23719
|
} catch {
|
|
23344
23720
|
sendText(response, 404, "Stack client entry not found: src/local-ui/stack/main.ts is missing.");
|
|
23345
23721
|
return;
|
|
@@ -23358,7 +23734,7 @@ async function sendStackAppJs(response) {
|
|
|
23358
23734
|
}
|
|
23359
23735
|
async function sendStackAppCss(response) {
|
|
23360
23736
|
try {
|
|
23361
|
-
const body = await (0,
|
|
23737
|
+
const body = await (0, import_promises31.readFile)((0, import_node_path40.join)(__dirname, "stack", "stack.css"));
|
|
23362
23738
|
response.writeHead(200, { "content-type": "text/css; charset=utf-8", "cache-control": "no-store" });
|
|
23363
23739
|
response.end(body);
|
|
23364
23740
|
} catch {
|
|
@@ -23367,7 +23743,7 @@ async function sendStackAppCss(response) {
|
|
|
23367
23743
|
}
|
|
23368
23744
|
async function readProviderVerifyLedger(cwd) {
|
|
23369
23745
|
try {
|
|
23370
|
-
const raw = await (0,
|
|
23746
|
+
const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, ".viberaven", "provider-verify.json"), "utf8");
|
|
23371
23747
|
const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
|
|
23372
23748
|
return isRecord5(parsed) ? parsed : {};
|
|
23373
23749
|
} catch {
|
|
@@ -23377,8 +23753,8 @@ async function readProviderVerifyLedger(cwd) {
|
|
|
23377
23753
|
async function writeProviderVerifyEntry(cwd, providerId, entry) {
|
|
23378
23754
|
const ledger = await readProviderVerifyLedger(cwd);
|
|
23379
23755
|
ledger[providerId] = entry;
|
|
23380
|
-
await (0,
|
|
23381
|
-
await (0,
|
|
23756
|
+
await (0, import_promises31.mkdir)((0, import_node_path40.join)(cwd, ".viberaven"), { recursive: true });
|
|
23757
|
+
await (0, import_promises31.writeFile)((0, import_node_path40.join)(cwd, ".viberaven", "provider-verify.json"), `${JSON.stringify(ledger, null, 2)}
|
|
23382
23758
|
`, "utf8");
|
|
23383
23759
|
}
|
|
23384
23760
|
async function handleRequest(request, response, options) {
|
|
@@ -23421,6 +23797,18 @@ async function handleRequest(request, response, options) {
|
|
|
23421
23797
|
sendText(response, 404, "This VibeRaven surface has been removed. Use the Launch Canvas at /.");
|
|
23422
23798
|
return;
|
|
23423
23799
|
}
|
|
23800
|
+
if (request.method === "GET" && url.pathname === "/api/github-workflow") {
|
|
23801
|
+
sendJson(response, await githubWorkflowStatus(options.cwd));
|
|
23802
|
+
return;
|
|
23803
|
+
}
|
|
23804
|
+
if (request.method === "POST" && url.pathname === "/api/github-workflow") {
|
|
23805
|
+
try {
|
|
23806
|
+
sendJson(response, await writeGithubWorkflow(options.cwd));
|
|
23807
|
+
} catch (error) {
|
|
23808
|
+
sendJsonStatus(response, 500, { error: `Could not write the workflow: ${error instanceof Error ? error.message : String(error)}` });
|
|
23809
|
+
}
|
|
23810
|
+
return;
|
|
23811
|
+
}
|
|
23424
23812
|
if (request.method === "GET" && url.pathname === "/api/project") {
|
|
23425
23813
|
sendJson(response, await readState(options.cwd));
|
|
23426
23814
|
return;
|
|
@@ -23439,13 +23827,13 @@ async function handleRequest(request, response, options) {
|
|
|
23439
23827
|
return;
|
|
23440
23828
|
}
|
|
23441
23829
|
if (request.method === "GET" && url.pathname === "/api/architecture-map") {
|
|
23442
|
-
const mapPath = (0,
|
|
23443
|
-
const markdown = await (0,
|
|
23830
|
+
const mapPath = (0, import_node_path40.join)(options.cwd, ".viberaven", "architecture-map.md");
|
|
23831
|
+
const markdown = await (0, import_promises31.readFile)(mapPath, "utf8").catch(() => null);
|
|
23444
23832
|
if (markdown === null) {
|
|
23445
23833
|
sendJsonStatus(response, 404, { error: "No architecture map yet. Run a scan first (it is written on every scan)." });
|
|
23446
23834
|
return;
|
|
23447
23835
|
}
|
|
23448
|
-
const dataRaw = await (0,
|
|
23836
|
+
const dataRaw = await (0, import_promises31.readFile)((0, import_node_path40.join)(options.cwd, ".viberaven", "architecture-map.json"), "utf8").catch(() => null);
|
|
23449
23837
|
let data = null;
|
|
23450
23838
|
if (dataRaw) {
|
|
23451
23839
|
try {
|
|
@@ -23454,7 +23842,7 @@ async function handleRequest(request, response, options) {
|
|
|
23454
23842
|
data = null;
|
|
23455
23843
|
}
|
|
23456
23844
|
}
|
|
23457
|
-
const contextRaw = await (0,
|
|
23845
|
+
const contextRaw = await (0, import_promises31.readFile)((0, import_node_path40.join)(options.cwd, ".viberaven", "context-map.json"), "utf8").catch(() => null);
|
|
23458
23846
|
let gaps = [];
|
|
23459
23847
|
if (contextRaw) {
|
|
23460
23848
|
try {
|
|
@@ -23720,35 +24108,35 @@ async function handleRequest(request, response, options) {
|
|
|
23720
24108
|
return;
|
|
23721
24109
|
}
|
|
23722
24110
|
if (request.method === "GET" && url.pathname === "/api/artifacts/tasklist") {
|
|
23723
|
-
await sendArtifact(options.cwd, (0,
|
|
24111
|
+
await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "agent-tasklist.md"), response);
|
|
23724
24112
|
return;
|
|
23725
24113
|
}
|
|
23726
24114
|
if (request.method === "GET" && url.pathname === "/api/artifacts/playbook") {
|
|
23727
|
-
await sendArtifact(options.cwd, (0,
|
|
24115
|
+
await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "launch-playbook.md"), response);
|
|
23728
24116
|
return;
|
|
23729
24117
|
}
|
|
23730
24118
|
if (request.method === "GET" && url.pathname === "/api/artifacts/audit") {
|
|
23731
|
-
await sendArtifact(options.cwd, (0,
|
|
24119
|
+
await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "audit-vercel-supabase.txt"), response);
|
|
23732
24120
|
return;
|
|
23733
24121
|
}
|
|
23734
24122
|
if (request.method === "GET" && url.pathname === "/api/artifacts/cleanup") {
|
|
23735
|
-
await sendArtifact(options.cwd, (0,
|
|
24123
|
+
await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "context-cleanup.md"), response);
|
|
23736
24124
|
return;
|
|
23737
24125
|
}
|
|
23738
24126
|
if (request.method === "GET" && url.pathname === "/api/artifacts/preview") {
|
|
23739
|
-
await sendArtifact(options.cwd, (0,
|
|
24127
|
+
await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "preview-rehearsal.md"), response);
|
|
23740
24128
|
return;
|
|
23741
24129
|
}
|
|
23742
24130
|
if (request.method === "GET" && url.pathname === "/api/artifacts/heal-plan") {
|
|
23743
|
-
await sendArtifact(options.cwd, (0,
|
|
24131
|
+
await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "heal-plan.md"), response);
|
|
23744
24132
|
return;
|
|
23745
24133
|
}
|
|
23746
24134
|
if (request.method === "GET" && url.pathname === "/api/artifacts/heal-prompt") {
|
|
23747
|
-
await sendArtifact(options.cwd, (0,
|
|
24135
|
+
await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "heal-prompt.md"), response);
|
|
23748
24136
|
return;
|
|
23749
24137
|
}
|
|
23750
24138
|
if (request.method === "GET" && url.pathname === "/api/artifacts/heal-apply") {
|
|
23751
|
-
await sendArtifact(options.cwd, (0,
|
|
24139
|
+
await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "heal-apply-result.json"), response, "application/json; charset=utf-8");
|
|
23752
24140
|
return;
|
|
23753
24141
|
}
|
|
23754
24142
|
if (request.method === "GET" && url.pathname === "/api/artifacts/report") {
|
|
@@ -23774,12 +24162,12 @@ async function handleRequest(request, response, options) {
|
|
|
23774
24162
|
}
|
|
23775
24163
|
if (request.method === "GET" && url.pathname === "/vendor/anime.umd.min.js") {
|
|
23776
24164
|
for (const candidate of [
|
|
23777
|
-
(0,
|
|
23778
|
-
(0,
|
|
23779
|
-
(0,
|
|
24165
|
+
(0, import_node_path40.join)(__dirname, "vendor", "anime.umd.min.js"),
|
|
24166
|
+
(0, import_node_path40.join)(__dirname, "..", "assets", "vendor", "anime.umd.min.js"),
|
|
24167
|
+
(0, import_node_path40.join)(__dirname, "..", "..", "assets", "vendor", "anime.umd.min.js")
|
|
23780
24168
|
]) {
|
|
23781
24169
|
try {
|
|
23782
|
-
const body = await (0,
|
|
24170
|
+
const body = await (0, import_promises31.readFile)(candidate);
|
|
23783
24171
|
response.setHeader("cache-control", "public, max-age=604800, immutable");
|
|
23784
24172
|
sendBinary(response, 200, "text/javascript; charset=utf-8", body);
|
|
23785
24173
|
return;
|
|
@@ -23996,7 +24384,7 @@ function listen(server, startPort) {
|
|
|
23996
24384
|
});
|
|
23997
24385
|
}
|
|
23998
24386
|
async function startLocalUiServer(options) {
|
|
23999
|
-
const cwd = (0,
|
|
24387
|
+
const cwd = (0, import_node_path40.resolve)(options.cwd);
|
|
24000
24388
|
const scanRunner = options.scanRunner ?? defaultScanRunner;
|
|
24001
24389
|
const autoScan = { state: "idle" };
|
|
24002
24390
|
const fullCheck = new FullCheckService(cwd, options.fullCheck);
|
|
@@ -24013,7 +24401,7 @@ async function startLocalUiServer(options) {
|
|
|
24013
24401
|
});
|
|
24014
24402
|
const port = await listen(server, options.port ?? 4177);
|
|
24015
24403
|
guard.port = port;
|
|
24016
|
-
const hasArtifact = await (0,
|
|
24404
|
+
const hasArtifact = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, ".viberaven", "last-scan.json"), "utf8").then(
|
|
24017
24405
|
() => true,
|
|
24018
24406
|
() => false
|
|
24019
24407
|
);
|
|
@@ -24052,16 +24440,17 @@ async function waitForServerShutdown() {
|
|
|
24052
24440
|
process.once("SIGTERM", done);
|
|
24053
24441
|
});
|
|
24054
24442
|
}
|
|
24055
|
-
var import_node_http,
|
|
24443
|
+
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
24444
|
var init_server = __esm({
|
|
24057
24445
|
"src/local-ui/server.ts"() {
|
|
24058
24446
|
"use strict";
|
|
24059
24447
|
import_node_http = require("node:http");
|
|
24060
|
-
|
|
24448
|
+
import_node_child_process10 = require("node:child_process");
|
|
24061
24449
|
import_node_crypto6 = require("node:crypto");
|
|
24062
|
-
|
|
24063
|
-
|
|
24064
|
-
|
|
24450
|
+
import_promises31 = require("node:fs/promises");
|
|
24451
|
+
import_node_os3 = require("node:os");
|
|
24452
|
+
import_node_path40 = require("node:path");
|
|
24453
|
+
init_initGithub();
|
|
24065
24454
|
init_cleanPlan();
|
|
24066
24455
|
init_audit();
|
|
24067
24456
|
init_heal();
|
|
@@ -24102,8 +24491,8 @@ var init_server = __esm({
|
|
|
24102
24491
|
MCP_CONFIG_PATHS = [
|
|
24103
24492
|
".mcp.json",
|
|
24104
24493
|
"mcp.json",
|
|
24105
|
-
(0,
|
|
24106
|
-
(0,
|
|
24494
|
+
(0, import_node_path40.join)(".cursor", "mcp.json"),
|
|
24495
|
+
(0, import_node_path40.join)(".vscode", "mcp.json")
|
|
24107
24496
|
];
|
|
24108
24497
|
MCP_PROVIDER_NEEDLES = {
|
|
24109
24498
|
supabase: ["supabase", "@supabase/mcp", "mcp-server-supabase"],
|
|
@@ -24272,11 +24661,11 @@ var init_nextActionBlock = __esm({
|
|
|
24272
24661
|
|
|
24273
24662
|
// src/providerMcpBridge.ts
|
|
24274
24663
|
function defaultMcpConfigPaths() {
|
|
24275
|
-
const home = (0,
|
|
24664
|
+
const home = (0, import_node_os4.homedir)();
|
|
24276
24665
|
return [
|
|
24277
|
-
(0,
|
|
24278
|
-
(0,
|
|
24279
|
-
(0,
|
|
24666
|
+
(0, import_node_path41.join)(home, ".config", "claude", "claude_desktop_config.json"),
|
|
24667
|
+
(0, import_node_path41.join)(home, ".cursor", "mcp.json"),
|
|
24668
|
+
(0, import_node_path41.join)(home, ".gemini", "antigravity", "mcp_config.json")
|
|
24280
24669
|
];
|
|
24281
24670
|
}
|
|
24282
24671
|
function resolveConfigPaths() {
|
|
@@ -24305,11 +24694,11 @@ function findServerEntry(servers, provider2) {
|
|
|
24305
24694
|
function findProviderMcpConfig(provider2, configPaths) {
|
|
24306
24695
|
const paths = configPaths ?? resolveConfigPaths();
|
|
24307
24696
|
for (const path of paths) {
|
|
24308
|
-
if (!(0,
|
|
24697
|
+
if (!(0, import_node_fs15.existsSync)(path)) {
|
|
24309
24698
|
continue;
|
|
24310
24699
|
}
|
|
24311
24700
|
try {
|
|
24312
|
-
const raw = JSON.parse((0,
|
|
24701
|
+
const raw = JSON.parse((0, import_node_fs15.readFileSync)(path, "utf8"));
|
|
24313
24702
|
const servers = parseMcpServers(raw);
|
|
24314
24703
|
if (!servers) {
|
|
24315
24704
|
continue;
|
|
@@ -24353,13 +24742,13 @@ async function verifyProviderGap(options) {
|
|
|
24353
24742
|
fallbackCommand: FALLBACK_COMMAND
|
|
24354
24743
|
};
|
|
24355
24744
|
}
|
|
24356
|
-
var
|
|
24745
|
+
var import_node_fs15, import_node_os4, import_node_path41, FALLBACK_COMMAND, SUPPORTED_PROVIDERS, configPathsOverride;
|
|
24357
24746
|
var init_providerMcpBridge = __esm({
|
|
24358
24747
|
"src/providerMcpBridge.ts"() {
|
|
24359
24748
|
"use strict";
|
|
24360
|
-
|
|
24361
|
-
|
|
24362
|
-
|
|
24749
|
+
import_node_fs15 = require("node:fs");
|
|
24750
|
+
import_node_os4 = require("node:os");
|
|
24751
|
+
import_node_path41 = require("node:path");
|
|
24363
24752
|
FALLBACK_COMMAND = "npx -y viberaven audit --vercel-supabase --json";
|
|
24364
24753
|
SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["supabase", "vercel"]);
|
|
24365
24754
|
}
|
|
@@ -24388,6 +24777,8 @@ Usage:
|
|
|
24388
24777
|
viberaven tui Interactive terminal menu
|
|
24389
24778
|
|
|
24390
24779
|
viberaven check [--json] [--quiet] [path]
|
|
24780
|
+
viberaven check --base <ref> [--markdown] What this change introduced and fixed, compared with <ref>
|
|
24781
|
+
viberaven init --github [--dry-run] Check every pull request in GitHub Actions
|
|
24391
24782
|
Local production check: one line per finding, exit 1 on blockers
|
|
24392
24783
|
|
|
24393
24784
|
viberaven fix [--gap <id>] [--dry-run] [path]
|
|
@@ -24537,6 +24928,9 @@ function isBooleanFlag(command, key) {
|
|
|
24537
24928
|
if (key === "open" && (command === "" || command === "scan" || command === "report")) return true;
|
|
24538
24929
|
if (key === "verify" && command === "") return true;
|
|
24539
24930
|
if (key === "quiet" && command === "check") return true;
|
|
24931
|
+
if (key === "markdown" && command === "check") return true;
|
|
24932
|
+
if (key === "github" && command === "init") return true;
|
|
24933
|
+
if (key === "force" && command === "init") return true;
|
|
24540
24934
|
if (key === "vercel-supabase" && command === "audit") return true;
|
|
24541
24935
|
if (key === "json" && command === "validate-npm-package") return true;
|
|
24542
24936
|
if (key === "dry-run" && (command === "init" || command === "fix")) return true;
|
|
@@ -24570,7 +24964,7 @@ async function cmdProviderVerify(flags, positional) {
|
|
|
24570
24964
|
return result.verified ? 0 : 1;
|
|
24571
24965
|
}
|
|
24572
24966
|
async function cmdStatus(flags, positional) {
|
|
24573
|
-
const startDir = positional[0] ? (0,
|
|
24967
|
+
const startDir = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24574
24968
|
let artifact;
|
|
24575
24969
|
try {
|
|
24576
24970
|
artifact = await loadLastArtifact(startDir);
|
|
@@ -24609,7 +25003,7 @@ async function cmdStatus(flags, positional) {
|
|
|
24609
25003
|
return 0;
|
|
24610
25004
|
}
|
|
24611
25005
|
async function runScanCommand(flags, positional, options) {
|
|
24612
|
-
const workspacePath = positional[0] ? (0,
|
|
25006
|
+
const workspacePath = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
|
|
24613
25007
|
if (!options?.deferMachineOutput) {
|
|
24614
25008
|
console.log(`Scanning ${workspacePath}\u2026 (local, offline)`);
|
|
24615
25009
|
}
|
|
@@ -24636,7 +25030,7 @@ async function runScanCommand(flags, positional, options) {
|
|
|
24636
25030
|
printNextActionBlock(block);
|
|
24637
25031
|
printProviderActionBlock(tasks);
|
|
24638
25032
|
try {
|
|
24639
|
-
const manifest = JSON.parse(await (0,
|
|
25033
|
+
const manifest = JSON.parse(await (0, import_promises32.readFile)(paths.actionsPath, "utf8"));
|
|
24640
25034
|
console.log(renderActionSurface(manifest, { limit: 5 }).trimEnd());
|
|
24641
25035
|
} catch (error) {
|
|
24642
25036
|
console.warn(error instanceof Error ? error.message : String(error));
|
|
@@ -24653,7 +25047,7 @@ async function runScanCommand(flags, positional, options) {
|
|
|
24653
25047
|
return { exitCode: 0, artifacts: paths };
|
|
24654
25048
|
}
|
|
24655
25049
|
async function cmdReport(flags, positional) {
|
|
24656
|
-
const startDir = positional[0] ? (0,
|
|
25050
|
+
const startDir = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24657
25051
|
try {
|
|
24658
25052
|
const paths = await refreshReportFromDisk(startDir);
|
|
24659
25053
|
console.log(`Report refreshed: ${paths.reportPath}`);
|
|
@@ -24675,7 +25069,7 @@ async function cmdReport(flags, positional) {
|
|
|
24675
25069
|
}
|
|
24676
25070
|
}
|
|
24677
25071
|
async function cmdPrompt(flags, positional) {
|
|
24678
|
-
const startDir = positional[0] ? (0,
|
|
25072
|
+
const startDir = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24679
25073
|
let artifact;
|
|
24680
25074
|
try {
|
|
24681
25075
|
artifact = await loadLastArtifact(startDir);
|
|
@@ -24772,7 +25166,7 @@ async function main() {
|
|
|
24772
25166
|
const wantsJsonl = hasFlag(flags, "jsonl");
|
|
24773
25167
|
const wantsStrict = hasFlag(flags, "strict");
|
|
24774
25168
|
if (flags.condense) {
|
|
24775
|
-
const cwd = positional[0] ? (0,
|
|
25169
|
+
const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24776
25170
|
const result = await runCondenseCommand({ cwd });
|
|
24777
25171
|
console.log(`VibeRaven context map refreshed: ${result.contextMapPath}`);
|
|
24778
25172
|
return 0;
|
|
@@ -24792,7 +25186,7 @@ async function main() {
|
|
|
24792
25186
|
}
|
|
24793
25187
|
if (!command && flags.verify === true && typeof flags.action === "string") {
|
|
24794
25188
|
return runVerifyActionCommand({
|
|
24795
|
-
cwd: positional[0] ? (0,
|
|
25189
|
+
cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
24796
25190
|
actionId: flags.action
|
|
24797
25191
|
});
|
|
24798
25192
|
}
|
|
@@ -24803,7 +25197,7 @@ async function main() {
|
|
|
24803
25197
|
console.error("VibeRaven could not produce machine output because scan artifacts were not written.");
|
|
24804
25198
|
return 3;
|
|
24805
25199
|
}
|
|
24806
|
-
const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0,
|
|
25200
|
+
const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises32.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
|
|
24807
25201
|
const strictExitCode = wantsStrict && gateResult ? exitCodeForStrictGate(gateResult, { failOnWarnings: flags.strict === "warning" }) : scanResult.exitCode;
|
|
24808
25202
|
if (wantsJson && gateResult) {
|
|
24809
25203
|
process.stdout.write(renderGateResultJson(gateResult));
|
|
@@ -24825,7 +25219,7 @@ async function main() {
|
|
|
24825
25219
|
env: process.env
|
|
24826
25220
|
});
|
|
24827
25221
|
if (mode === "local-ui") {
|
|
24828
|
-
const cwd = positional[0] ? (0,
|
|
25222
|
+
const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24829
25223
|
const handle = await startLocalUiServer({ cwd });
|
|
24830
25224
|
console.log(`VibeRaven local UI: ${handle.url}`);
|
|
24831
25225
|
await waitForServerShutdown();
|
|
@@ -24845,7 +25239,7 @@ async function main() {
|
|
|
24845
25239
|
await runInteractiveSession();
|
|
24846
25240
|
return 0;
|
|
24847
25241
|
case "ui": {
|
|
24848
|
-
const cwd = positional[0] ? (0,
|
|
25242
|
+
const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24849
25243
|
const envPort = Number(process.env.PORT);
|
|
24850
25244
|
const handle = await startLocalUiServer({ cwd, port: Number.isFinite(envPort) && envPort > 0 ? envPort : void 0 });
|
|
24851
25245
|
console.log(`VibeRaven local UI: ${handle.url}`);
|
|
@@ -24857,12 +25251,14 @@ async function main() {
|
|
|
24857
25251
|
return cmdStatus(flags, positional);
|
|
24858
25252
|
case "check":
|
|
24859
25253
|
return runCheckCommand({
|
|
24860
|
-
cwd: positional[0] ? (0,
|
|
25254
|
+
cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
24861
25255
|
json: Boolean(flags.json),
|
|
24862
|
-
quiet: Boolean(flags.quiet)
|
|
25256
|
+
quiet: Boolean(flags.quiet),
|
|
25257
|
+
base: typeof flags.base === "string" ? flags.base : void 0,
|
|
25258
|
+
markdown: flags.markdown === true
|
|
24863
25259
|
});
|
|
24864
25260
|
case "fix": {
|
|
24865
|
-
const fixCwd = positional[0] ? (0,
|
|
25261
|
+
const fixCwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24866
25262
|
const gapId = typeof flags.gap === "string" ? flags.gap : void 0;
|
|
24867
25263
|
if (!gapId) {
|
|
24868
25264
|
const artifact = await loadLastArtifact(fixCwd).catch(() => void 0);
|
|
@@ -24891,19 +25287,19 @@ async function main() {
|
|
|
24891
25287
|
}
|
|
24892
25288
|
case "actions":
|
|
24893
25289
|
return runActionsCommand({
|
|
24894
|
-
cwd: positional[0] ? (0,
|
|
25290
|
+
cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
24895
25291
|
json: Boolean(flags.json)
|
|
24896
25292
|
});
|
|
24897
25293
|
case "preview":
|
|
24898
25294
|
return runPreviewCommand({
|
|
24899
|
-
cwd: positional[0] ? (0,
|
|
25295
|
+
cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
24900
25296
|
agentMode: flags["agent-mode"] === true,
|
|
24901
25297
|
json: Boolean(flags.json)
|
|
24902
25298
|
});
|
|
24903
25299
|
case "next":
|
|
24904
25300
|
return runNextCommand({
|
|
24905
25301
|
json: Boolean(flags.json),
|
|
24906
|
-
cwd: positional[0] ? (0,
|
|
25302
|
+
cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd()
|
|
24907
25303
|
});
|
|
24908
25304
|
case "guide": {
|
|
24909
25305
|
const provider2 = positional[0];
|
|
@@ -24937,8 +25333,11 @@ async function main() {
|
|
|
24937
25333
|
case "provider-verify":
|
|
24938
25334
|
return cmdProviderVerify(flags, positional);
|
|
24939
25335
|
case "init": {
|
|
24940
|
-
const cwd = positional[0] ? (0,
|
|
25336
|
+
const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24941
25337
|
const agents = typeof flags.agents === "string" ? flags.agents : void 0;
|
|
25338
|
+
if (flags.github === true) {
|
|
25339
|
+
return runInitGithubCommand({ cwd, dryRun: flags["dry-run"] === true, force: flags.force === true });
|
|
25340
|
+
}
|
|
24942
25341
|
return runInitCommand({
|
|
24943
25342
|
cwd,
|
|
24944
25343
|
agents,
|
|
@@ -24951,7 +25350,7 @@ async function main() {
|
|
|
24951
25350
|
return 1;
|
|
24952
25351
|
}
|
|
24953
25352
|
return runDoctorAgentsCommand({
|
|
24954
|
-
cwd: positional[0] ? (0,
|
|
25353
|
+
cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd()
|
|
24955
25354
|
});
|
|
24956
25355
|
case "validate-npm-package":
|
|
24957
25356
|
return runValidateNpmPackageCommand({
|
|
@@ -24964,7 +25363,7 @@ async function main() {
|
|
|
24964
25363
|
return 1;
|
|
24965
25364
|
}
|
|
24966
25365
|
return runAuditCommand({
|
|
24967
|
-
cwd: positional[0] ? (0,
|
|
25366
|
+
cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
24968
25367
|
json: Boolean(flags.json)
|
|
24969
25368
|
});
|
|
24970
25369
|
case "clean": {
|
|
@@ -24972,7 +25371,7 @@ async function main() {
|
|
|
24972
25371
|
console.error("Usage: viberaven clean --plan [path]");
|
|
24973
25372
|
return 1;
|
|
24974
25373
|
}
|
|
24975
|
-
const cwd = positional[0] ? (0,
|
|
25374
|
+
const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24976
25375
|
const plan = buildContextCleanupPlan({ projectRoot: cwd, files: await collectCleanupFiles(cwd) });
|
|
24977
25376
|
const outputPath = await writeCleanupPlan(cwd, plan);
|
|
24978
25377
|
console.log(`Wrote ${outputPath}
|
|
@@ -24982,9 +25381,9 @@ async function main() {
|
|
|
24982
25381
|
}
|
|
24983
25382
|
case "rls-test":
|
|
24984
25383
|
return runRlsTestCommand({
|
|
24985
|
-
cwd: positional[0] ? (0,
|
|
24986
|
-
matrix: typeof flags.matrix === "string" ? (0,
|
|
24987
|
-
migrations: typeof flags.migrations === "string" ? (0,
|
|
25384
|
+
cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
25385
|
+
matrix: typeof flags.matrix === "string" ? (0, import_node_path42.resolve)(process.cwd(), flags.matrix) : void 0,
|
|
25386
|
+
migrations: typeof flags.migrations === "string" ? (0, import_node_path42.resolve)(process.cwd(), flags.migrations) : void 0,
|
|
24988
25387
|
json: Boolean(flags.json)
|
|
24989
25388
|
});
|
|
24990
25389
|
default:
|
|
@@ -24993,11 +25392,11 @@ async function main() {
|
|
|
24993
25392
|
return 1;
|
|
24994
25393
|
}
|
|
24995
25394
|
}
|
|
24996
|
-
var
|
|
25395
|
+
var import_promises32, import_node_path42, STACK_AREAS;
|
|
24997
25396
|
var init_cli = __esm({
|
|
24998
25397
|
"src/cli.ts"() {
|
|
24999
|
-
|
|
25000
|
-
|
|
25398
|
+
import_promises32 = require("node:fs/promises");
|
|
25399
|
+
import_node_path42 = require("node:path");
|
|
25001
25400
|
init_config();
|
|
25002
25401
|
init_runScan();
|
|
25003
25402
|
init_artifacts2();
|
|
@@ -25021,6 +25420,7 @@ var init_cli = __esm({
|
|
|
25021
25420
|
init_stackRecommend();
|
|
25022
25421
|
init_commands();
|
|
25023
25422
|
init_runInit();
|
|
25423
|
+
init_initGithub();
|
|
25024
25424
|
init_runDoctor();
|
|
25025
25425
|
init_runValidateNpmPackage();
|
|
25026
25426
|
init_runAudit();
|