@viberaven/cli 1.7.0 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +64 -64
- package/LICENSE +21 -21
- package/README.md +67 -67
- package/SECURITY.md +59 -59
- package/dist/cli.js +697 -632
- package/dist/console/app.js +251 -251
- package/dist/console/index.html +46 -46
- package/dist/console/styles.css +267 -267
- package/dist/github-ci.js +1 -1
- package/dist/playbooks/_stack-default.json +11 -11
- package/dist/playbooks/auth-supabase.json +31 -31
- package/dist/playbooks/stripe.json +66 -66
- package/dist/playbooks/supabase.json +51 -51
- package/dist/playbooks/vercel.json +42 -42
- package/dist/report/assets/provider-authjs.svg +5 -5
- package/dist/report/assets/provider-aws.svg +5 -5
- package/dist/report/assets/provider-logrocket.svg +4 -4
- package/dist/report/report-cli.css +1466 -1466
- package/dist/report/station.css +11854 -11854
- package/dist/report/station.js +8319 -8319
- package/dist/stack/scene.css +522 -522
- package/dist/stack/stack.css +1691 -1691
- package/dist/vendor/anime.umd.min.js +7 -7
- package/package.json +96 -96
- package/playbooks/_stack-default.json +11 -11
- package/playbooks/auth-supabase.json +31 -31
- package/playbooks/stripe.json +66 -66
- package/playbooks/supabase.json +51 -51
- package/playbooks/vercel.json +42 -42
- package/schemas/permissions-matrix.schema.json +90 -90
- package/schemas/verification-plan.schema.json +350 -350
- package/schemas/verification-report.schema.json +550 -550
- package/templates/AGENTS.snippet.md +56 -56
- package/templates/CLAUDE.snippet.md +14 -14
- package/templates/CURSOR.snippet.md +14 -14
package/dist/cli.js
CHANGED
|
@@ -14406,9 +14406,9 @@ var init_clipboard = __esm({
|
|
|
14406
14406
|
}
|
|
14407
14407
|
});
|
|
14408
14408
|
|
|
14409
|
-
// node_modules/picocolors/picocolors.js
|
|
14409
|
+
// ../../../../pipeline-release-integration/VibeRice/packages/cli/node_modules/picocolors/picocolors.js
|
|
14410
14410
|
var require_picocolors = __commonJS({
|
|
14411
|
-
"node_modules/picocolors/picocolors.js"(exports2, module2) {
|
|
14411
|
+
"../../../../pipeline-release-integration/VibeRice/packages/cli/node_modules/picocolors/picocolors.js"(exports2, module2) {
|
|
14412
14412
|
var p2 = process || {};
|
|
14413
14413
|
var argv = p2.argv || [];
|
|
14414
14414
|
var env = p2.env || {};
|
|
@@ -14599,9 +14599,9 @@ var init_terminalSummary = __esm({
|
|
|
14599
14599
|
}
|
|
14600
14600
|
});
|
|
14601
14601
|
|
|
14602
|
-
// node_modules/sisteransi/src/index.js
|
|
14602
|
+
// ../../../../pipeline-release-integration/VibeRice/packages/cli/node_modules/sisteransi/src/index.js
|
|
14603
14603
|
var require_src = __commonJS({
|
|
14604
|
-
"node_modules/sisteransi/src/index.js"(exports2, module2) {
|
|
14604
|
+
"../../../../pipeline-release-integration/VibeRice/packages/cli/node_modules/sisteransi/src/index.js"(exports2, module2) {
|
|
14605
14605
|
"use strict";
|
|
14606
14606
|
var ESC = "\x1B";
|
|
14607
14607
|
var CSI = `${ESC}[`;
|
|
@@ -14655,7 +14655,7 @@ var require_src = __commonJS({
|
|
|
14655
14655
|
}
|
|
14656
14656
|
});
|
|
14657
14657
|
|
|
14658
|
-
// node_modules/@clack/core/dist/index.mjs
|
|
14658
|
+
// ../../../../pipeline-release-integration/VibeRice/packages/cli/node_modules/@clack/core/dist/index.mjs
|
|
14659
14659
|
function DD({ onlyFirst: e2 = false } = {}) {
|
|
14660
14660
|
const t = ["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?(?:\\u0007|\\u001B\\u005C|\\u009C))", "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"].join("|");
|
|
14661
14661
|
return new RegExp(t, e2 ? void 0 : "g");
|
|
@@ -14767,7 +14767,7 @@ function fD({ input: e2 = import_node_process.stdin, output: u2 = import_node_pr
|
|
|
14767
14767
|
}
|
|
14768
14768
|
var import_sisteransi, import_node_process, g, import_node_readline, import_node_stream, import_picocolors2, uD, W, tD, eD, FD, sD, w, N, I, R, C, iD, rD, ED, d, oD, y, V, nD, G, _, z, K, aD, k, hD, lD, xD, B, AD, S, gD, vD, h, x, A, TD, jD, U, MD, OD, PD, J, LD;
|
|
14769
14769
|
var init_dist = __esm({
|
|
14770
|
-
"node_modules/@clack/core/dist/index.mjs"() {
|
|
14770
|
+
"../../../../pipeline-release-integration/VibeRice/packages/cli/node_modules/@clack/core/dist/index.mjs"() {
|
|
14771
14771
|
import_sisteransi = __toESM(require_src(), 1);
|
|
14772
14772
|
import_node_process = require("node:process");
|
|
14773
14773
|
g = __toESM(require("node:readline"), 1);
|
|
@@ -15040,13 +15040,13 @@ var init_dist = __esm({
|
|
|
15040
15040
|
}
|
|
15041
15041
|
});
|
|
15042
15042
|
|
|
15043
|
-
// node_modules/@clack/prompts/dist/index.mjs
|
|
15043
|
+
// ../../../../pipeline-release-integration/VibeRice/packages/cli/node_modules/@clack/prompts/dist/index.mjs
|
|
15044
15044
|
function ce() {
|
|
15045
15045
|
return import_node_process2.default.platform !== "win32" ? import_node_process2.default.env.TERM !== "linux" : !!import_node_process2.default.env.CI || !!import_node_process2.default.env.WT_SESSION || !!import_node_process2.default.env.TERMINUS_SUBLIME || import_node_process2.default.env.ConEmuTask === "{cmd::Cmder}" || import_node_process2.default.env.TERM_PROGRAM === "Terminus-Sublime" || import_node_process2.default.env.TERM_PROGRAM === "vscode" || import_node_process2.default.env.TERM === "xterm-256color" || import_node_process2.default.env.TERM === "alacritty" || import_node_process2.default.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
|
|
15046
15046
|
}
|
|
15047
15047
|
var import_node_process2, import_picocolors3, import_sisteransi2, V2, u, le, L2, W2, C2, ue, o, d2, k2, P2, A2, T, F, $e, _2, me, de, pe, q, D, U2, K2, b2, G2, ge, ve, xe, Ie, Se, M2, J2, Y2;
|
|
15048
15048
|
var init_dist2 = __esm({
|
|
15049
|
-
"node_modules/@clack/prompts/dist/index.mjs"() {
|
|
15049
|
+
"../../../../pipeline-release-integration/VibeRice/packages/cli/node_modules/@clack/prompts/dist/index.mjs"() {
|
|
15050
15050
|
init_dist();
|
|
15051
15051
|
init_dist();
|
|
15052
15052
|
import_node_process2 = __toESM(require("node:process"), 1);
|
|
@@ -15300,7 +15300,7 @@ var VERSION;
|
|
|
15300
15300
|
var init_version = __esm({
|
|
15301
15301
|
"src/version.ts"() {
|
|
15302
15302
|
"use strict";
|
|
15303
|
-
VERSION = "1.7.
|
|
15303
|
+
VERSION = "1.7.1";
|
|
15304
15304
|
}
|
|
15305
15305
|
});
|
|
15306
15306
|
|
|
@@ -15525,7 +15525,7 @@ This is advice, not a gate: the user decides when to ship. It is a repository ch
|
|
|
15525
15525
|
|
|
15526
15526
|
Agent loop: run \`${PUBLIC_CHECK_COMMAND}\`, read \`.viberaven/agent-tasklist.md\`, fix one launch gap, re-run \`${PUBLIC_CHECK_COMMAND}\` once per batch of fixes.`;
|
|
15527
15527
|
STACK_FRAMING = `Supported stack: AI-built apps that deploy on Vercel and use Supabase, typically from Lovable, Bolt, Cursor, Claude Code or Codex.
|
|
15528
|
-
|
|
15528
|
+
Use \`viberaven_check_readiness\` only when that tool is in your tool list (the local \`@viberaven/mcp\` server); otherwise \`${PUBLIC_CHECK_COMMAND}\`. The hosted server at \`https://viberaven.dev/mcp\` cannot see the repository: pass it migration SQL or an exact error.`;
|
|
15529
15529
|
AGENT_NEGATIVE_CONSTRAINTS = `## Working with VibeRaven results
|
|
15530
15530
|
|
|
15531
15531
|
1. Do not call the app "production ready" or "secure" because \`gate.status\` is \`clear\`; it covers the repo checks only.
|
|
@@ -15609,7 +15609,7 @@ After \`${PUBLIC_CHECK_COMMAND}\`, read these artifacts in order:
|
|
|
15609
15609
|
function buildShortAgentRulesBlock() {
|
|
15610
15610
|
return wrapViberavenBlock(SHORT_AGENT_RULES_BODY);
|
|
15611
15611
|
}
|
|
15612
|
-
var SHORT_AGENT_RULES_BODY, AGENT_SKILL_FILE, AGENT_SKILL_CONTENT;
|
|
15612
|
+
var SHORT_AGENT_RULES_BODY, AGENT_SKILL_FILE, CODEX_AGENT_SKILL_FILE, AGENT_SKILL_CONTENT;
|
|
15613
15613
|
var init_agentSkill = __esm({
|
|
15614
15614
|
"src/commands/agentSkill.ts"() {
|
|
15615
15615
|
"use strict";
|
|
@@ -15628,7 +15628,67 @@ When you change or review Supabase migrations or RLS, auth, Stripe webhooks, env
|
|
|
15628
15628
|
|
|
15629
15629
|
This is advice, not a gate: the user decides when to ship. Full loop: \`.viberaven/agent-rules.md\`. For requested task proof, follow that guide using \`.viberaven/task-contract.template.json\`.`;
|
|
15630
15630
|
AGENT_SKILL_FILE = ".claude/skills/viberaven/SKILL.md";
|
|
15631
|
-
AGENT_SKILL_CONTENT = '---\nname: viberaven\ndescription: "Use VibeRaven when working on an AI-built app that must ship to real users; when the user asks to ship, deploy, go to production, connect auth, database, payments, RLS, env vars, webhooks, or monitoring; when production-only errors appear; or when the agent needs product context (versions, providers, launch risks) before patching. Fully local: no login, no API key."\n---\n\n# VibeRaven Skill\n\nVibeRaven is the control layer for AI-built products. Its repository checks run fully locally: no login, no API key, no telemetry. They map the product (stack, providers, releases, launch risks) into `.viberaven/` artifacts any agent can read, and list the launch gaps found in the repo, each with the file that caused it. They are advice, not a gate: the user decides when to ship. Repository checks are not live database or security tests. Optional live steps have a separate account, consent and allowance boundary.\n\n## When To Use\n\nUse when the user wants production-ready, launch-ready, "what is missing before ship", a production audit, or a safe deploy for an AI-coded codebase: auth, database, payments, monitoring, error handling, rate limits, env vars. Also use when you need product context before editing: which providers the app uses, what changed between releases, and where the danger zones are.\n\n## The Loop\n\n1. Run `npx -y viberaven check` from the project root. It runs offline checks and prints \u{1F534}/\u{1F7E1}/\u26AA findings with a verdict. Exit code 1 means blockers exist.\n2. Read `.viberaven/agent-tasklist.md` first, then `.viberaven/gate-result.json` and `.viberaven/context-map.json`.\n3. Run `npx -y viberaven fix` to list gaps with safe automatic repo-code recipes. Preview a permitted fix with `npx -y viberaven fix --gap <id> --dry-run`, then apply it with `npx -y viberaven fix --gap <id>`. Respect `requiresUserAction`: finish other permitted repo fixes first, then ask before changes needing approval, such as enabling RLS without policies.\n4. For gaps without a recipe, use `npx -y viberaven prompt --gap <id>` for focused guidance and patch one gap yourself.\n5. Re-run `npx -y viberaven check` after a batch of fixes, not after every file patch.\n6. `npx -y viberaven --strict` returns the verdict as an exit code for CI when the user wants one.\n\nStop when `gate.status === "clear"` in `.viberaven/gate-result.json`, when only provider or user steps remain, or when the user decides to move on. A clear `gate.status` covers the repo checks only; the user decides when to ship. Local checks need no credentials. Do not ask for secret values or bypass a required approval.\n\n## Provider-aware feature planning\n\nFor feature work, get the relevant versioned pack with `provider_playbook` and `{"provider":"supabase"}` in MCP, or `viberaven provider supabase --json` in the CLI. Use the installed candidate version; this command is not claimed to exist in the published release yet. The pack is the single source for setup, gotchas, dated changes, provider seams, repo-check IDs and planned live checks. Do not duplicate its guidance in this Skill. Unsupported providers return the available packs; do not invent one.\n\nUse the pack to plan the feature, let the person\'s coding agent make the authorized edits, then run relevant repo checks and behavior tests. Pack retrieval is free, offline and needs no account. It is knowledge, not evidence that a check ran: report `not_checked` for unobserved provider state. Read the available pack list from the tool. Better feature results than ordinary Claude remain an experiment, not an established advantage.\n\n## Optional live catalog check (local candidate)\n\nAfter Supabase work, when actual provider metadata is relevant, offer `viberaven_live_start` with the exact project reference and selected schemas, or `viberaven live start --provider supabase --project <ref> --schemas public --json`. This only saves the scope locally. Use the installed candidate, not an assumed published version. Vercel live reads remain blocked until a metadata-only transport is available.\n\nExplain the named project/schema read and get the person\'s approval before `viberaven_live_resume` with `approvedRead: true` or `viberaven live resume --id <stepId> --approve-read --json`. The live-enabled account API must be configured. Show sign-in/payment action URLs to the person; keep the same step ID afterward. The provider token belongs in the hidden local terminal prompt, never in chat, MCP arguments or a command flag. An MCP resume without a locally stored encrypted credential returns instructions to continue in the terminal. Tokens are session-only by default; the person may choose Windows DPAPI persistence using `--persist-token` in their terminal.\n\nCatalog facts returned by the local tool may contain schema/object names and enter the connected coding agent\'s context. The account API receives project/scope IDs and bounded outcome counts, not catalog facts or provider credentials. A complete scoped result consumes one live step, including a detected mismatch; blocked or incomplete checks release the reservation. Local checks, plans and status remain free without login. `viberaven_live_status` reads the saved result; `viberaven_live_disconnect` removes this task\'s stored provider credential and evidence. A metadata result does not establish row-level behavior or completion of the feature; run the application\'s tests separately.\n\n## Studio\n\n`npx -y viberaven` opens the local Studio: the product auto-mapped (stack, providers, git releases, launch verdict), drag-context into agent chat, provider MCP connect, and release diffs. Suggest it when the user wants to see or control the product rather than read terminal output.\n\n## MCP\n\nWhen the VibeRaven MCP server is configured, prefer `viberaven_check_readiness`, then `viberaven_heal_apply` for supported safe repo-code fixes, then `viberaven_verify`. Before `npm install` of a new package, call `viberaven_validate_npm_package`.\n\n## Vercel + Supabase\n\n```bash\nnpx -y viberaven audit --vercel-supabase\n```\n\nCheck RLS, service-role exposure, and pooler ports `5432` / `6543` before making launch claims.\n\n## Local task proof onboarding (local candidate)\n\nFor requested feature proof, follow the shared `.viberaven/agent-rules.md` workflow installed by `init` for both Claude and Codex. Start from `.viberaven/task-contract.template.json`: copy it to a task-specific file, replace the request placeholder from the actual user\'s request, and fill testable acceptance conditions, preservation conditions and actual repository-relative scope. It is a schema-valid draft, not evidence or an executable task. Mark it ready only after review. Ready contracts are immutable; changed requirements need a newly reviewed task.\n\nThe shared guide leads through local `work start` with the selected CLI/access, a reviewed `work prepare` plan outside the project BEFORE editing, direct authorized coding-agent edits, then authorized `work verify` and `work report` with the same task ID. No `work resume`, login or account is needed for this local route. Keep the useful free check/fix/recheck loop. Unsupported provider/browser behavior stays `not_checked`; a report exit code of 0 means export succeeded, so inspect its outcome. The sections below describe optional account execution and the existing independent adapters in detail. Onboarding does not enable `init --github` or upload a report to its separate trusted static-check workflow.\n\n## Optional account-linked work (local candidate)\n\nOnly when the user chooses account-linked agent execution, first agree on one task contract: `schemaVersion: 1`, `status: "ready"`, the exact `request`, `acceptance` and `mustNotBreak` lists of `{id, description}`, and `scope` with `environment`, relative repository `paths`, and `providers`. Use `environment: "local"` and an empty provider list for local work. Do not include credentials or shell commands. Choose the person\'s installed agent; `ask` plans and `approve` permits local edits through the existing engine.\n\nSave it with `viberaven work start --contract task.json --cli codex --access approve --json`, or `viberaven_work_start` with the `contract` object. Start and status are local and need no login. Request-only start remains available but creates a draft that cannot execute. Before a remote session exists, upgrade the same draft with `viberaven work resume --id <taskId> --contract task.json --json`, keeping the original request. Ready definitions are immutable. Provider, preview and test-mode contracts can be saved for planning; their execution is not implemented in this candidate.\n\nShow the saved acceptance conditions, preservation conditions and scope to the person. Only after their approval, use `viberaven work resume --id <taskId> --approve-contract --json`, or `viberaven_work_resume` with `approvedContract: true`. Approval is tied to the exact contract hash. Explain that resume shares the short request and redacted results with the VibeRaven account; the full contract stays in the local engine context. Keep the same task ID and approved definition through sign-in, quota/payment and interrupted delivery. Show any action URL to the person; never authorize sign-in or payment for them. The matching work-enabled backend is required.\n\nRead the receipt with `viberaven work status --id <taskId> --json` or `viberaven_work_status`. Report execution state separately from condition evidence: an agent\'s successful exit, checkout return or account connection does not prove the task is complete. Conditions remain `not_checked` unless the separately prepared independent local verifier observes them. A historical passing receipt becomes stale after a source change; read current status before claiming it still applies. Review an interrupted or legacy task rather than starting a duplicate to hide failure. Scope paths describe intent, not an enforced filesystem sandbox.\n\nThis uses the person\'s local coding agent and computer, not an included hosted model. Free checks and direct agent repairs remain available without an account. Use the installed candidate; do not assume these contract commands exist in the published release.\n\n## Independent local observations (local candidate)\n\nBefore the coding agent edits an existing Node module, propose a declarative verification plan for the agreed task conditions. Have the person review expected behavior independently of the implementation. Use `viberaven_work_prepare` with `{taskId, verification}` or `viberaven work prepare --id <taskId> --verification /outside/project/plan.json --json`. This freezes dirty/untracked baseline bytes and the plan outside the project. The contract and plan are immutable; do not weaken them after seeing a failure.\n\nThe first plan format is `{schemaVersion: 1, checks: [...]}`. A module check has `id`, `conditionId`, `adapter: "module"`, `module` (relative path), `export` and `cases: [{args: [...], expected: <JSON>}]`. Use an existing baseline module and concrete expected values. A preservation check can instead use `adapter: "node-test"`, `entry` and `frozenPaths` for original test/helpers. An HTTP check uses `adapter: "http"`, an existing `module`/`export` returning an unlistened Node HTTP server, and sequential `cases: [{request: {method, path, headers?, body?}, expected: {status, json}}]` (or `text` instead of `json`). Only synthetic fixture headers and origin-relative paths are supported; expected values stay outside the container. The CLI accepts a bounded JSON file; MCP inline transport is limited to 4800 UTF-8 bytes. These are observations, not shell commands or supplied proof.\n\nAfter the authorized edit, run `viberaven_work_verify` with `approvedVerification: true` or `viberaven work verify --id <taskId> --approve-verification --json` only after the person approves the reviewed plan\'s isolated execution. This needs the local pinned Docker image, but no account, payment, provider token or network. It neither runs the coding agent again nor installs dependencies. Status returns each condition as `checked` with its outcome, or `not_checked` with a reason, plus snapshot/provenance. Read failures, uncovered conditions, source-scope changes and exclusions before reporting completion.\n\nSupport is bounded to Node JS/erasable TS module observations, local HTTP loopback and frozen Node preservation tests. HTTP observations share one server per check inside network-isolated Docker; they do not prove database persistence, real authentication, browser or provider flows. Unsupported dependencies, secrets/excluded required inputs or unavailable Docker do not establish completion. The plan\'s quality and coverage remain review decisions; do not invent a benchmark advantage from these checks.\n\n## Local proof export (local candidate)\n\nAfter reading current status, use `viberaven work report --id <taskId>` for Markdown, add `--json` for the structured report plus Markdown, or call `viberaven_work_report` with `{taskId}`. This reads the same immutable verifier state and current source freshness as status. It performs no login, network call, Docker run, agent execution or file write. Export goes to stdout only. A successful export exits 0 even when evidence is `failed` or `not_checked`; read `outcome`, condition counts and scope, not the export exit code. Missing or corrupt saved state fails with nonzero exit and no passing report.\n\nThe standalone MCP report route requires an already installed `@viberaven/cli` with the exact MCP version, resolvable from the MCP installation. For a local candidate, the host may set `VIBERAVEN_CLI_PATH` to the absolute matching `dist/cli.js` outside the task repository; this is host configuration, never an MCP argument. Report calls execute that installed CLI directly and never run npm or download/install packages. If the matching CLI is unavailable, reporting fails until it is installed/configured separately.\n\nThe local draft includes the request, acceptance and must-not-break conditions, checked/passed/failed/unknown counts, open items, scope/exclusion counts, snapshot hashes, descriptive HEAD SHAs and receipt hashes. Only current independently checked conditions and checked scope can yield `passed`. Execution state is separate. HEAD does not imply a clean source tree; actual-byte snapshot hashes identify proof. Human text is redacted and explicitly marked if truncated; the contract hash binds the original definition. No source, response bodies, output streams, private path lists, account actions or artifact file references are exported. Pattern redaction cannot find every secret: have a person review the entire draft before sharing.\n\nDo not treat this local report as authoritative CI or feed it into the trusted GitHub static-check comment path. Export posts no comment, proves no deployment/provider state and does not complete the L1.3 live GitHub pilot. An explicitly authorized live finding/fix/update-same-comment sequence remains separate future work.\n\n## Boundaries\n\n- Repo-code edits never prove provider dashboard state. Billing, DNS, webhooks, quotas, and live provider verification need dashboard or MCP proof; say so explicitly.\n- Never ask for passwords, tokens, cookies, or secret values.\n- `check`, `scan`, `audit` and `clean --plan` write only under `.viberaven/`. `init` and `fix --gap` edit repo files: `init` writes agent rule files such as `AGENTS.md` and adds `viberaven:*` scripts to `package.json` (preview with `init --dry-run`), and `fix --gap` edits code (preview with `npx -y viberaven fix --gap <id> --dry-run`).\n\nNext skill: `architecture-context` when feature work is vague and product questions are missing; `architecture-plan` once those questions are answered; `what-broke` when a working app regressed; `production-context` to record durable risk in `.viberaven/production-context.md`; `go-live` when the user wants the app pushed to GitHub and live on Vercel.\n';
|
|
15631
|
+
CODEX_AGENT_SKILL_FILE = ".agents/skills/viberaven/SKILL.md";
|
|
15632
|
+
AGENT_SKILL_CONTENT = '---\nname: viberaven\ndescription: "Use for Supabase RLS and migrations, grants and 42501 errors, a service role key in client code, Vercel env vars, Stripe webhook signature checks, or a Vercel + Supabase review before deploy. Choose local repository checks or hosted SQL/error guidance by the actual tools available. Local checks are free: no login or API key."\n---\n\n# VibeRaven Skill\n\nVibeRaven is the control layer for AI-built products. Its repository checks run fully locally: no login, no API key, no telemetry. They map the product (stack, providers, releases, launch risks) into `.viberaven/` artifacts any agent can read, and list the launch gaps found in the repo, each with the file that caused it. They are advice, not a gate: the user decides when to ship. Repository checks are not live database or security tests. Optional live steps have a separate account, consent and allowance boundary.\n\n## When To Use\n\nUse when the user wants production-ready, launch-ready, "what is missing before ship", a production audit, or a safe deploy for an AI-coded codebase: auth, database, payments, monitoring, error handling, rate limits, env vars. Also use when you need product context before editing: which providers the app uses, what changed between releases, and where the danger zones are.\n\n## The Loop\n\n1. Run `npx -y viberaven check` from the project root. It runs offline checks and prints \u{1F534}/\u{1F7E1}/\u26AA findings with a verdict. Exit code 1 means blockers exist.\n2. Read `.viberaven/agent-tasklist.md` first, then `.viberaven/gate-result.json` and `.viberaven/context-map.json`.\n3. Run `npx -y viberaven fix` to list gaps with safe automatic repo-code recipes. Preview a permitted fix with `npx -y viberaven fix --gap <id> --dry-run`, then apply it with `npx -y viberaven fix --gap <id>`. Respect `requiresUserAction`: finish other permitted repo fixes first, then ask before changes needing approval, such as enabling RLS without policies.\n4. For gaps without a recipe, use `npx -y viberaven prompt --gap <id>` for focused guidance and patch one gap yourself.\n5. Re-run `npx -y viberaven check` after a batch of fixes, not after every file patch.\n6. `npx -y viberaven --strict` returns the verdict as an exit code for CI when the user wants one.\n\nStop when `gate.status === "clear"` in `.viberaven/gate-result.json`, when only provider or user steps remain, or when the user decides to move on. A clear `gate.status` covers the repo checks only; the user decides when to ship. Local checks need no credentials. Do not ask for secret values or bypass a required approval.\n\n## Provider-aware feature planning\n\nFor feature work, get the relevant versioned pack with `provider_playbook` and `{"provider":"supabase"}` in MCP, or `viberaven provider supabase --json` in the CLI. Use the installed candidate version; this command is not claimed to exist in the published release yet. The pack is the single source for setup, gotchas, dated changes, provider seams, repo-check IDs and planned live checks. Do not duplicate its guidance in this Skill. Unsupported providers return the available packs; do not invent one.\n\nUse the pack to plan the feature, let the person\'s coding agent make the authorized edits, then run relevant repo checks and behavior tests. Pack retrieval is free, offline and needs no account. It is knowledge, not evidence that a check ran: report `not_checked` for unobserved provider state. Read the available pack list from the tool. Better feature results than ordinary Claude remain an experiment, not an established advantage.\n\n## Optional local behavioral RLS test (local candidate)\n\nAfter reviewing a Supabase migration or ownership policy, optionally run already reviewed permissions scenarios. Derive expectations from intended access for owner, other_user and anon; do not rewrite expectations to match failures. Prefer `defaultGrants: none` in the matrix with actual GRANT SQL for new schemas; disclose `supabase-legacy` defaults otherwise.\n\nIf `viberaven_rls_test` is in your tool list, call it with `{matrix: "PATH", cwd: "PROJECT"}` and optional `migrations`. Otherwise use the installed candidate CLI: `viberaven rls-test --matrix PATH --json`. Read each Pass, Fail and Unknown with its evidence and coverage; Unknown is inconclusive. This is an experimental PGlite imitation, not live Supabase proof or a security claim.\n\nThe optional PGlite dependency must already be installed next to the CLI that runs the test. The local MCP host needs a matching installed CLI with PGlite alongside it; a dependency installed only in the target project may be unavailable to that host. Missing setup is a tool error with CLI guidance. Do not install dependencies without normal user authorization. This optional local test needs no login, provider access or payment; keep the free check/fix/recheck loop.\n\n## Optional live catalog check (local candidate)\n\nAfter Supabase work, when actual provider metadata is relevant, offer `viberaven_live_start` with the exact project reference and selected schemas, or `viberaven live start --provider supabase --project <ref> --schemas public --json`. This only saves the scope locally. Use the installed candidate, not an assumed published version. Vercel live reads remain blocked until a metadata-only transport is available.\n\nExplain the named project/schema read and get the person\'s approval before `viberaven_live_resume` with `approvedRead: true` or `viberaven live resume --id <stepId> --approve-read --json`. The live-enabled account API must be configured. Show sign-in/payment action URLs to the person; keep the same step ID afterward. The provider token belongs in the hidden local terminal prompt, never in chat, MCP arguments or a command flag. An MCP resume without a locally stored encrypted credential returns instructions to continue in the terminal. Tokens are session-only by default; the person may choose Windows DPAPI persistence using `--persist-token` in their terminal.\n\nCatalog facts returned by the local tool may contain schema/object names and enter the connected coding agent\'s context. The account API receives project/scope IDs and bounded outcome counts, not catalog facts or provider credentials. A complete scoped result consumes one live step, including a detected mismatch; blocked or incomplete checks release the reservation. Local checks, plans and status remain free without login. `viberaven_live_status` reads the saved result; `viberaven_live_disconnect` removes this task\'s stored provider credential and evidence. A metadata result does not establish row-level behavior or completion of the feature; run the application\'s tests separately.\n\n## Studio\n\n`npx -y viberaven` opens the local Studio: the product auto-mapped (stack, providers, git releases, launch verdict), drag-context into agent chat, provider MCP connect, and release diffs. Suggest it when the user wants to see or control the product rather than read terminal output.\n\n## MCP\n\nChoose by the tools you actually have, not by the server\'s name. Two different servers are both called VibeRaven:\n\n- If `viberaven_check_readiness` is in your tool list (the local `@viberaven/mcp` server, which runs the CLI in a repository), call it on the project root you are working in, then `viberaven_heal_apply` for supported safe repo-code fixes, then `viberaven_verify`. Before `npm install` of a new package, call `viberaven_validate_npm_package`.\n- If you only have the hosted server (`https://viberaven.dev/mcp`, with tools such as `check_supabase_migrations` and `search_guides`), it cannot see the repository. Pass it the migration SQL or the exact error text, and say that the result covers only what you passed. For a repository check, run `npx -y viberaven check` in the project root.\n- With no VibeRaven tools, run `npx -y viberaven check` for a Vercel + Supabase repository. Answer a general question without running anything.\n\n## Vercel + Supabase\n\n```bash\nnpx -y viberaven audit --vercel-supabase\n```\n\nCheck RLS, service-role exposure, and pooler ports `5432` / `6543` before making launch claims.\n\n## Local task proof onboarding (local candidate)\n\nFor requested feature proof, follow the shared `.viberaven/agent-rules.md` workflow installed by `init` for both Claude and Codex. Start from `.viberaven/task-contract.template.json`: copy it to a task-specific file, replace the request placeholder from the actual user\'s request, and fill testable acceptance conditions, preservation conditions and actual repository-relative scope. It is a schema-valid draft, not evidence or an executable task. Mark it ready only after review. Ready contracts are immutable; changed requirements need a newly reviewed task.\n\nThe shared guide leads through local `work start` with the selected CLI/access, a reviewed `work prepare` plan outside the project BEFORE editing, direct authorized coding-agent edits, then authorized `work verify` and `work report` with the same task ID. No `work resume`, login or account is needed for this local route. Keep the useful free check/fix/recheck loop. Unsupported provider/browser behavior stays `not_checked`; a report exit code of 0 means export succeeded, so inspect its outcome. The sections below describe optional account execution and the existing independent adapters in detail. Onboarding does not enable `init --github` or upload a report to its separate trusted static-check workflow.\n\n## Optional account-linked work (local candidate)\n\nOnly when the user chooses account-linked agent execution, first agree on one task contract: `schemaVersion: 1`, `status: "ready"`, the exact `request`, `acceptance` and `mustNotBreak` lists of `{id, description}`, and `scope` with `environment`, relative repository `paths`, and `providers`. Use `environment: "local"` and an empty provider list for local work. Do not include credentials or shell commands. Choose the person\'s installed agent; `ask` plans and `approve` permits local edits through the existing engine.\n\nSave it with `viberaven work start --contract task.json --cli codex --access approve --json`, or `viberaven_work_start` with the `contract` object. Start and status are local and need no login. Request-only start remains available but creates a draft that cannot execute. Before a remote session exists, upgrade the same draft with `viberaven work resume --id <taskId> --contract task.json --json`, keeping the original request. Ready definitions are immutable. Provider, preview and test-mode contracts can be saved for planning; their execution is not implemented in this candidate.\n\nShow the saved acceptance conditions, preservation conditions and scope to the person. Only after their approval, use `viberaven work resume --id <taskId> --approve-contract --json`, or `viberaven_work_resume` with `approvedContract: true`. Approval is tied to the exact contract hash. Explain that resume shares the short request and redacted results with the VibeRaven account; the full contract stays in the local engine context. Keep the same task ID and approved definition through sign-in, quota/payment and interrupted delivery. Show any action URL to the person; never authorize sign-in or payment for them. The matching work-enabled backend is required.\n\nRead the receipt with `viberaven work status --id <taskId> --json` or `viberaven_work_status`. Report execution state separately from condition evidence: an agent\'s successful exit, checkout return or account connection does not prove the task is complete. Conditions remain `not_checked` unless the separately prepared independent local verifier observes them. A historical passing receipt becomes stale after a source change; read current status before claiming it still applies. Review an interrupted or legacy task rather than starting a duplicate to hide failure. Scope paths describe intent, not an enforced filesystem sandbox.\n\nThis uses the person\'s local coding agent and computer, not an included hosted model. Free checks and direct agent repairs remain available without an account. Use the installed candidate; do not assume these contract commands exist in the published release.\n\n## Independent local observations (local candidate)\n\nBefore the coding agent edits an existing Node module, propose a declarative verification plan for the agreed task conditions. Have the person review expected behavior independently of the implementation. Use `viberaven_work_prepare` with `{taskId, verification}` or `viberaven work prepare --id <taskId> --verification /outside/project/plan.json --json`. This freezes dirty/untracked baseline bytes and the plan outside the project. The contract and plan are immutable; do not weaken them after seeing a failure.\n\nThe first plan format is `{schemaVersion: 1, checks: [...]}`. A module check has `id`, `conditionId`, `adapter: "module"`, `module` (relative path), `export` and `cases: [{args: [...], expected: <JSON>}]`. Use an existing baseline module and concrete expected values. A preservation check can instead use `adapter: "node-test"`, `entry` and `frozenPaths` for original test/helpers. An HTTP check uses `adapter: "http"`, an existing `module`/`export` returning an unlistened Node HTTP server, and sequential `cases: [{request: {method, path, headers?, body?}, expected: {status, json}}]` (or `text` instead of `json`). Only synthetic fixture headers and origin-relative paths are supported; expected values stay outside the container. The CLI accepts a bounded JSON file; MCP inline transport is limited to 4800 UTF-8 bytes. These are observations, not shell commands or supplied proof.\n\nAfter the authorized edit, run `viberaven_work_verify` with `approvedVerification: true` or `viberaven work verify --id <taskId> --approve-verification --json` only after the person approves the reviewed plan\'s isolated execution. This needs the local pinned Docker image, but no account, payment, provider token or network. It neither runs the coding agent again nor installs dependencies. Status returns each condition as `checked` with its outcome, or `not_checked` with a reason, plus snapshot/provenance. Read failures, uncovered conditions, source-scope changes and exclusions before reporting completion.\n\nSupport is bounded to Node JS/erasable TS module observations, local HTTP loopback and frozen Node preservation tests. HTTP observations share one server per check inside network-isolated Docker; they do not prove database persistence, real authentication, browser or provider flows. Unsupported dependencies, secrets/excluded required inputs or unavailable Docker do not establish completion. The plan\'s quality and coverage remain review decisions; do not invent a benchmark advantage from these checks.\n\n## Local proof export (local candidate)\n\nAfter reading current status, use `viberaven work report --id <taskId>` for Markdown, add `--json` for the structured report plus Markdown, or call `viberaven_work_report` with `{taskId}`. This reads the same immutable verifier state and current source freshness as status. It performs no login, network call, Docker run, agent execution or file write. Export goes to stdout only. A successful export exits 0 even when evidence is `failed` or `not_checked`; read `outcome`, condition counts and scope, not the export exit code. Missing or corrupt saved state fails with nonzero exit and no passing report.\n\nThe standalone MCP report route requires an already installed `@viberaven/cli` with the exact MCP version, resolvable from the MCP installation. For a local candidate, the host may set `VIBERAVEN_CLI_PATH` to the absolute matching `dist/cli.js` outside the task repository; this is host configuration, never an MCP argument. Report calls execute that installed CLI directly and never run npm or download/install packages. If the matching CLI is unavailable, reporting fails until it is installed/configured separately.\n\nThe local draft includes the request, acceptance and must-not-break conditions, checked/passed/failed/unknown counts, open items, scope/exclusion counts, snapshot hashes, descriptive HEAD SHAs and receipt hashes. Only current independently checked conditions and checked scope can yield `passed`. Execution state is separate. HEAD does not imply a clean source tree; actual-byte snapshot hashes identify proof. Human text is redacted and explicitly marked if truncated; the contract hash binds the original definition. No source, response bodies, output streams, private path lists, account actions or artifact file references are exported. Pattern redaction cannot find every secret: have a person review the entire draft before sharing.\n\nDo not treat this local report as authoritative CI or feed it into the trusted GitHub static-check comment path. Export posts no comment, proves no deployment/provider state and does not complete the L1.3 live GitHub pilot. An explicitly authorized live finding/fix/update-same-comment sequence remains separate future work.\n\n## Boundaries\n\n- Repo-code edits never prove provider dashboard state. Billing, DNS, webhooks, quotas, and live provider verification need dashboard or MCP proof; say so explicitly.\n- Never ask for passwords, tokens, cookies, or secret values.\n- `check`, `scan`, `audit` and `clean --plan` write only under `.viberaven/`. `init` and `fix --gap` edit repo files: `init` writes agent rule files such as `AGENTS.md` and adds `viberaven:*` scripts to `package.json` (preview with `init --dry-run`), and `fix --gap` edits code (preview with `npx -y viberaven fix --gap <id> --dry-run`).\n\nNext skill: `architecture-context` when feature work is vague and product questions are missing; `architecture-plan` once those questions are answered; `what-broke` when a working app regressed; `production-context` to record durable risk in `.viberaven/production-context.md`; `go-live` when the user wants the app pushed to GitHub and live on Vercel.\n';
|
|
15633
|
+
}
|
|
15634
|
+
});
|
|
15635
|
+
|
|
15636
|
+
// src/commands/initAgentSkill.ts
|
|
15637
|
+
async function initAgentSkill(options) {
|
|
15638
|
+
const path2 = (0, import_node_path18.join)(options.cwd, options.file);
|
|
15639
|
+
const parents = options.file.split("/").slice(0, -1);
|
|
15640
|
+
const regularParents = async (create) => {
|
|
15641
|
+
let parent = options.cwd;
|
|
15642
|
+
for (const part of parents) {
|
|
15643
|
+
parent = (0, import_node_path18.join)(parent, part);
|
|
15644
|
+
let stat12 = await (0, import_promises12.lstat)(parent).catch((error) => {
|
|
15645
|
+
if (error.code === "ENOENT") return null;
|
|
15646
|
+
throw error;
|
|
15647
|
+
});
|
|
15648
|
+
if (!stat12 && create) {
|
|
15649
|
+
try {
|
|
15650
|
+
await (0, import_promises12.mkdir)(parent);
|
|
15651
|
+
} catch (error) {
|
|
15652
|
+
if (error.code !== "EEXIST") throw error;
|
|
15653
|
+
}
|
|
15654
|
+
stat12 = await (0, import_promises12.lstat)(parent);
|
|
15655
|
+
}
|
|
15656
|
+
if (!stat12) return true;
|
|
15657
|
+
if (!stat12.isDirectory() || stat12.isSymbolicLink()) return false;
|
|
15658
|
+
}
|
|
15659
|
+
return true;
|
|
15660
|
+
};
|
|
15661
|
+
const existing = async () => {
|
|
15662
|
+
if (!await regularParents(false)) return "preserved";
|
|
15663
|
+
try {
|
|
15664
|
+
const stat12 = await (0, import_promises12.lstat)(path2);
|
|
15665
|
+
if (!stat12.isFile() || stat12.isSymbolicLink()) return "preserved";
|
|
15666
|
+
const content = await (0, import_promises12.readFile)(path2, "utf8");
|
|
15667
|
+
return content.replace(/\r\n/g, "\n") === AGENT_SKILL_CONTENT ? "unchanged" : "preserved";
|
|
15668
|
+
} catch (error) {
|
|
15669
|
+
if (error.code === "ENOENT") return null;
|
|
15670
|
+
throw error;
|
|
15671
|
+
}
|
|
15672
|
+
};
|
|
15673
|
+
const found = await existing();
|
|
15674
|
+
if (found) return found;
|
|
15675
|
+
if (options.dryRun) return "created";
|
|
15676
|
+
if (!await regularParents(true)) return "preserved";
|
|
15677
|
+
try {
|
|
15678
|
+
await (0, import_promises12.writeFile)(path2, AGENT_SKILL_CONTENT, { encoding: "utf8", flag: "wx" });
|
|
15679
|
+
return "created";
|
|
15680
|
+
} catch (error) {
|
|
15681
|
+
if (error.code !== "EEXIST") throw error;
|
|
15682
|
+
return await existing() ?? "preserved";
|
|
15683
|
+
}
|
|
15684
|
+
}
|
|
15685
|
+
var import_promises12, import_node_path18;
|
|
15686
|
+
var init_initAgentSkill = __esm({
|
|
15687
|
+
"src/commands/initAgentSkill.ts"() {
|
|
15688
|
+
"use strict";
|
|
15689
|
+
import_promises12 = require("node:fs/promises");
|
|
15690
|
+
import_node_path18 = require("node:path");
|
|
15691
|
+
init_agentSkill();
|
|
15632
15692
|
}
|
|
15633
15693
|
});
|
|
15634
15694
|
|
|
@@ -15715,25 +15775,25 @@ async function initCursorRulesPack(options) {
|
|
|
15715
15775
|
const pack = buildCursorRulesPack();
|
|
15716
15776
|
for (const rule of pack) {
|
|
15717
15777
|
const file = `${CURSOR_RULES_DIR}/${rule.filename}`;
|
|
15718
|
-
const path2 = (0,
|
|
15778
|
+
const path2 = (0, import_node_path19.join)(options.cwd, file);
|
|
15719
15779
|
const existing = await readExistingFile(path2);
|
|
15720
15780
|
const changed = !existing.exists || existing.content !== rule.content;
|
|
15721
15781
|
const action = !existing.exists ? "created" : changed ? "updated" : "unchanged";
|
|
15722
15782
|
if (!options.dryRun && changed) {
|
|
15723
|
-
await (0,
|
|
15724
|
-
await (0,
|
|
15783
|
+
await (0, import_promises13.mkdir)((0, import_node_path19.join)(options.cwd, CURSOR_RULES_DIR), { recursive: true });
|
|
15784
|
+
await (0, import_promises13.writeFile)(path2, rule.content, "utf-8");
|
|
15725
15785
|
}
|
|
15726
15786
|
results.push({ target: "cursor", file, path: path2, action });
|
|
15727
15787
|
}
|
|
15728
|
-
const legacyPath = (0,
|
|
15788
|
+
const legacyPath = (0, import_node_path19.join)(options.cwd, LEGACY_CURSOR_RULE_FILE);
|
|
15729
15789
|
if (!options.dryRun && await fileExists(legacyPath)) {
|
|
15730
|
-
await (0,
|
|
15790
|
+
await (0, import_promises13.rm)(legacyPath, { force: true });
|
|
15731
15791
|
}
|
|
15732
15792
|
return results;
|
|
15733
15793
|
}
|
|
15734
15794
|
async function readExistingFile(path2) {
|
|
15735
15795
|
try {
|
|
15736
|
-
return { exists: true, content: await (0,
|
|
15796
|
+
return { exists: true, content: await (0, import_promises13.readFile)(path2, "utf-8") };
|
|
15737
15797
|
} catch (error) {
|
|
15738
15798
|
if (isFileNotFoundError(error)) {
|
|
15739
15799
|
return { exists: false, content: "" };
|
|
@@ -15743,7 +15803,7 @@ async function readExistingFile(path2) {
|
|
|
15743
15803
|
}
|
|
15744
15804
|
async function fileExists(path2) {
|
|
15745
15805
|
try {
|
|
15746
|
-
await (0,
|
|
15806
|
+
await (0, import_promises13.access)(path2);
|
|
15747
15807
|
return true;
|
|
15748
15808
|
} catch {
|
|
15749
15809
|
return false;
|
|
@@ -15752,12 +15812,12 @@ async function fileExists(path2) {
|
|
|
15752
15812
|
function isFileNotFoundError(error) {
|
|
15753
15813
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
15754
15814
|
}
|
|
15755
|
-
var
|
|
15815
|
+
var import_promises13, import_node_path19, CURSOR_RULES_DIR, LEGACY_CURSOR_RULE_FILE, DOMAIN_PATH_POINTER;
|
|
15756
15816
|
var init_cursorRulesPack = __esm({
|
|
15757
15817
|
"src/commands/cursorRulesPack.ts"() {
|
|
15758
15818
|
"use strict";
|
|
15759
|
-
|
|
15760
|
-
|
|
15819
|
+
import_promises13 = require("node:fs/promises");
|
|
15820
|
+
import_node_path19 = require("node:path");
|
|
15761
15821
|
init_commands();
|
|
15762
15822
|
CURSOR_RULES_DIR = ".cursor/rules";
|
|
15763
15823
|
LEGACY_CURSOR_RULE_FILE = `${CURSOR_RULES_DIR}/viberaven.mdc`;
|
|
@@ -15770,7 +15830,7 @@ function renderAgentRulesForTarget(target) {
|
|
|
15770
15830
|
if (target === "claude") {
|
|
15771
15831
|
return ["@AGENTS.md", "", buildShortAgentRulesBlock()].join("\n");
|
|
15772
15832
|
}
|
|
15773
|
-
if (target === "skill") {
|
|
15833
|
+
if (target === "skill" || target === "codexSkill") {
|
|
15774
15834
|
return AGENT_SKILL_CONTENT;
|
|
15775
15835
|
}
|
|
15776
15836
|
if (target === "agentGuide") {
|
|
@@ -15826,6 +15886,7 @@ var init_agentTargets = __esm({
|
|
|
15826
15886
|
missionMap: { file: ".viberaven/mission-map.md", aliases: ["mission-map"] },
|
|
15827
15887
|
agentGuide: { file: ".viberaven/agent-rules.md", aliases: ["agent-rules"] },
|
|
15828
15888
|
skill: { file: AGENT_SKILL_FILE, aliases: ["claude-skill"] },
|
|
15889
|
+
codexSkill: { file: CODEX_AGENT_SKILL_FILE, aliases: ["codex-skill"] },
|
|
15829
15890
|
devin: { file: ".devin/rules/viberaven.md" },
|
|
15830
15891
|
windsurf: { file: ".windsurf/rules/viberaven.md" },
|
|
15831
15892
|
cline: { file: ".clinerules/viberaven.md" },
|
|
@@ -15842,7 +15903,8 @@ var init_agentTargets = __esm({
|
|
|
15842
15903
|
"agentContext",
|
|
15843
15904
|
"missionMap",
|
|
15844
15905
|
"agentGuide",
|
|
15845
|
-
"skill"
|
|
15906
|
+
"skill",
|
|
15907
|
+
"codexSkill"
|
|
15846
15908
|
];
|
|
15847
15909
|
EXTENDED_AGENT_RULE_TARGETS = [
|
|
15848
15910
|
"cursorLegacy",
|
|
@@ -15862,17 +15924,17 @@ var init_agentTargets = __esm({
|
|
|
15862
15924
|
...(AGENT_RULE_TARGETS[target].aliases ?? []).map((alias) => [alias, target])
|
|
15863
15925
|
]);
|
|
15864
15926
|
AGENT_RULE_TARGET_ALIASES = new Map(AGENT_RULE_TARGET_ALIAS_ENTRIES);
|
|
15865
|
-
VALID_AGENT_RULES_TARGET_TEXT = "all, codex, claude, cursor, cursor-legacy, copilot, github-copilot, gemini, agent-context, mission-map, agent-rules, claude-skill, devin, windsurf, cline, roo, junie, zed";
|
|
15927
|
+
VALID_AGENT_RULES_TARGET_TEXT = "all, codex, claude, cursor, cursor-legacy, copilot, github-copilot, gemini, agent-context, mission-map, agent-rules, claude-skill, codex-skill, devin, windsurf, cline, roo, junie, zed";
|
|
15866
15928
|
}
|
|
15867
15929
|
});
|
|
15868
15930
|
|
|
15869
15931
|
// src/commands/seedPackageJsonScripts.ts
|
|
15870
15932
|
async function seedPackageJsonScripts(options) {
|
|
15871
|
-
const packageJsonPath = (0,
|
|
15933
|
+
const packageJsonPath = (0, import_node_path20.join)(options.cwd, "package.json");
|
|
15872
15934
|
if (!(0, import_node_fs10.existsSync)(packageJsonPath)) {
|
|
15873
15935
|
return null;
|
|
15874
15936
|
}
|
|
15875
|
-
const raw = await (0,
|
|
15937
|
+
const raw = await (0, import_promises14.readFile)(packageJsonPath, "utf-8");
|
|
15876
15938
|
let pkg;
|
|
15877
15939
|
try {
|
|
15878
15940
|
pkg = JSON.parse(raw);
|
|
@@ -15902,17 +15964,17 @@ async function seedPackageJsonScripts(options) {
|
|
|
15902
15964
|
pkg.scripts = scripts;
|
|
15903
15965
|
const output = `${JSON.stringify(pkg, null, 2)}
|
|
15904
15966
|
`;
|
|
15905
|
-
await (0,
|
|
15967
|
+
await (0, import_promises14.writeFile)(packageJsonPath, output, "utf-8");
|
|
15906
15968
|
}
|
|
15907
15969
|
return { action: "updated", added, skipped, changed: true };
|
|
15908
15970
|
}
|
|
15909
|
-
var import_node_fs10,
|
|
15971
|
+
var import_node_fs10, import_promises14, import_node_path20, VIBERAVEN_PACKAGE_JSON_SCRIPTS;
|
|
15910
15972
|
var init_seedPackageJsonScripts = __esm({
|
|
15911
15973
|
"src/commands/seedPackageJsonScripts.ts"() {
|
|
15912
15974
|
"use strict";
|
|
15913
15975
|
import_node_fs10 = require("node:fs");
|
|
15914
|
-
|
|
15915
|
-
|
|
15976
|
+
import_promises14 = require("node:fs/promises");
|
|
15977
|
+
import_node_path20 = require("node:path");
|
|
15916
15978
|
init_commands();
|
|
15917
15979
|
VIBERAVEN_PACKAGE_JSON_SCRIPTS = {
|
|
15918
15980
|
"viberaven:check": PUBLIC_CHECK_COMMAND
|
|
@@ -16058,19 +16120,19 @@ var init_taskContract = __esm({
|
|
|
16058
16120
|
|
|
16059
16121
|
// src/commands/initTaskContract.ts
|
|
16060
16122
|
async function initTaskContractTemplate(options) {
|
|
16061
|
-
const path2 = (0,
|
|
16062
|
-
const parent = (0,
|
|
16123
|
+
const path2 = (0, import_node_path21.join)(options.cwd, TASK_CONTRACT_TEMPLATE_FILE);
|
|
16124
|
+
const parent = (0, import_node_path21.dirname)(path2);
|
|
16063
16125
|
const result = (action) => ({ file: TASK_CONTRACT_TEMPLATE_FILE, path: path2, action });
|
|
16064
|
-
const parentStat = await (0,
|
|
16126
|
+
const parentStat = await (0, import_promises15.lstat)(parent).catch((error) => {
|
|
16065
16127
|
if (error.code === "ENOENT") return null;
|
|
16066
16128
|
throw error;
|
|
16067
16129
|
});
|
|
16068
16130
|
if (parentStat && (!parentStat.isDirectory() || parentStat.isSymbolicLink())) return result("preserved");
|
|
16069
16131
|
const existing = async () => {
|
|
16070
16132
|
try {
|
|
16071
|
-
const stat12 = await (0,
|
|
16133
|
+
const stat12 = await (0, import_promises15.lstat)(path2);
|
|
16072
16134
|
if (!stat12.isFile() || stat12.isSymbolicLink() || stat12.size !== Buffer.byteLength(TASK_CONTRACT_TEMPLATE_CONTENT, "utf8")) return result("preserved");
|
|
16073
|
-
return result((await (0,
|
|
16135
|
+
return result((await (0, import_promises15.readFile)(path2)).equals(Buffer.from(TASK_CONTRACT_TEMPLATE_CONTENT, "utf8")) ? "unchanged" : "preserved");
|
|
16074
16136
|
} catch (error) {
|
|
16075
16137
|
if (error.code === "ENOENT") return null;
|
|
16076
16138
|
throw error;
|
|
@@ -16079,23 +16141,23 @@ async function initTaskContractTemplate(options) {
|
|
|
16079
16141
|
const found = await existing();
|
|
16080
16142
|
if (found) return found;
|
|
16081
16143
|
if (options.dryRun) return result("created");
|
|
16082
|
-
await (0,
|
|
16083
|
-
const checkedParent = await (0,
|
|
16144
|
+
await (0, import_promises15.mkdir)(parent, { recursive: true });
|
|
16145
|
+
const checkedParent = await (0, import_promises15.lstat)(parent);
|
|
16084
16146
|
if (!checkedParent.isDirectory() || checkedParent.isSymbolicLink()) return result("preserved");
|
|
16085
16147
|
try {
|
|
16086
|
-
await (0,
|
|
16148
|
+
await (0, import_promises15.writeFile)(path2, TASK_CONTRACT_TEMPLATE_CONTENT, { encoding: "utf8", flag: "wx" });
|
|
16087
16149
|
return result("created");
|
|
16088
16150
|
} catch (error) {
|
|
16089
16151
|
if (error.code !== "EEXIST") throw error;
|
|
16090
16152
|
return await existing() ?? result("preserved");
|
|
16091
16153
|
}
|
|
16092
16154
|
}
|
|
16093
|
-
var
|
|
16155
|
+
var import_promises15, import_node_path21, TASK_CONTRACT_TEMPLATE_FILE, TASK_CONTRACT_TEMPLATE_CONTENT;
|
|
16094
16156
|
var init_initTaskContract = __esm({
|
|
16095
16157
|
"src/commands/initTaskContract.ts"() {
|
|
16096
16158
|
"use strict";
|
|
16097
|
-
|
|
16098
|
-
|
|
16159
|
+
import_promises15 = require("node:fs/promises");
|
|
16160
|
+
import_node_path21 = require("node:path");
|
|
16099
16161
|
init_taskContract();
|
|
16100
16162
|
TASK_CONTRACT_TEMPLATE_FILE = ".viberaven/task-contract.template.json";
|
|
16101
16163
|
TASK_CONTRACT_TEMPLATE_CONTENT = `${JSON.stringify(draftTaskContract(
|
|
@@ -16115,22 +16177,18 @@ async function initAgentRules(options) {
|
|
|
16115
16177
|
continue;
|
|
16116
16178
|
}
|
|
16117
16179
|
const file = AGENT_RULE_TARGETS[target].file;
|
|
16118
|
-
const path2 = (0,
|
|
16119
|
-
|
|
16120
|
-
|
|
16121
|
-
|
|
16122
|
-
if (!options.dryRun && !existing.exists) {
|
|
16123
|
-
await (0, import_promises15.mkdir)((0, import_node_path21.dirname)(path2), { recursive: true });
|
|
16124
|
-
await (0, import_promises15.writeFile)(path2, AGENT_SKILL_CONTENT, "utf-8");
|
|
16125
|
-
}
|
|
16126
|
-
results.push({ target, file, path: path2, action: !existing.exists ? "created" : same ? "unchanged" : "preserved" });
|
|
16180
|
+
const path2 = (0, import_node_path22.join)(options.cwd, file);
|
|
16181
|
+
if (target === "skill" || target === "codexSkill") {
|
|
16182
|
+
const action2 = await initAgentSkill({ ...options, file });
|
|
16183
|
+
results.push({ target, file, path: path2, action: action2 });
|
|
16127
16184
|
continue;
|
|
16128
16185
|
}
|
|
16186
|
+
const existing = await readExistingFile2(path2);
|
|
16129
16187
|
const injected = injectAgentRulesBlock(existing.content, renderAgentRulesForTarget(target));
|
|
16130
16188
|
const action = !existing.exists ? "created" : injected.changed ? "updated" : "unchanged";
|
|
16131
16189
|
if (!options.dryRun && injected.changed) {
|
|
16132
|
-
await (0,
|
|
16133
|
-
await (0,
|
|
16190
|
+
await (0, import_promises16.mkdir)((0, import_node_path22.dirname)(path2), { recursive: true });
|
|
16191
|
+
await (0, import_promises16.writeFile)(path2, injected.content, "utf-8");
|
|
16134
16192
|
}
|
|
16135
16193
|
results.push({ target, file, path: path2, action });
|
|
16136
16194
|
}
|
|
@@ -16144,6 +16202,7 @@ async function initAgentRules(options) {
|
|
|
16144
16202
|
function withTargetDependencies(requested) {
|
|
16145
16203
|
const targets = [...new Set(requested)];
|
|
16146
16204
|
if (targets.includes("claude") && !targets.includes("skill")) targets.push("skill");
|
|
16205
|
+
if (targets.includes("codex") && !targets.includes("codexSkill")) targets.push("codexSkill");
|
|
16147
16206
|
const standalone = ["cursor", "agentContext", "missionMap", "agentGuide"];
|
|
16148
16207
|
if (targets.some((target) => !standalone.includes(target)) && !targets.includes("agentGuide")) {
|
|
16149
16208
|
targets.push("agentGuide");
|
|
@@ -16228,7 +16287,7 @@ function formatAgentRulesInitSummary(output) {
|
|
|
16228
16287
|
}
|
|
16229
16288
|
async function readExistingFile2(path2) {
|
|
16230
16289
|
try {
|
|
16231
|
-
return { exists: true, content: await (0,
|
|
16290
|
+
return { exists: true, content: await (0, import_promises16.readFile)(path2, "utf-8") };
|
|
16232
16291
|
} catch (error) {
|
|
16233
16292
|
if (isFileNotFoundError2(error)) {
|
|
16234
16293
|
return { exists: false, content: "" };
|
|
@@ -16239,14 +16298,14 @@ async function readExistingFile2(path2) {
|
|
|
16239
16298
|
function isFileNotFoundError2(error) {
|
|
16240
16299
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
16241
16300
|
}
|
|
16242
|
-
var
|
|
16301
|
+
var import_promises16, import_node_path22;
|
|
16243
16302
|
var init_initRules = __esm({
|
|
16244
16303
|
"src/commands/initRules.ts"() {
|
|
16245
16304
|
"use strict";
|
|
16246
|
-
|
|
16247
|
-
|
|
16305
|
+
import_promises16 = require("node:fs/promises");
|
|
16306
|
+
import_node_path22 = require("node:path");
|
|
16248
16307
|
init_agentRulesBlock();
|
|
16249
|
-
|
|
16308
|
+
init_initAgentSkill();
|
|
16250
16309
|
init_agentTargets();
|
|
16251
16310
|
init_cursorRulesPack();
|
|
16252
16311
|
init_seedPackageJsonScripts();
|
|
@@ -16509,7 +16568,7 @@ async function runInteractiveSession(startDir = process.cwd()) {
|
|
|
16509
16568
|
Ie(`${import_picocolors5.default.bold("VibeRaven")} ${import_picocolors5.default.dim(VERSION)}`);
|
|
16510
16569
|
const cwd = await resolveWorkspaceRoot(startDir);
|
|
16511
16570
|
const artifactsAt = await findArtifactsWorkspace(startDir);
|
|
16512
|
-
if (artifactsAt && (0,
|
|
16571
|
+
if (artifactsAt && (0, import_node_path23.resolve)(artifactsAt) !== (0, import_node_path23.resolve)(startDir)) {
|
|
16513
16572
|
M2.message(import_picocolors5.default.dim(`Using scan from: ${artifactsAt}`));
|
|
16514
16573
|
} else {
|
|
16515
16574
|
M2.message(import_picocolors5.default.dim(`Project folder: ${cwd}`));
|
|
@@ -16568,11 +16627,11 @@ async function runInteractiveSession(startDir = process.cwd()) {
|
|
|
16568
16627
|
}
|
|
16569
16628
|
Se(import_picocolors5.default.dim("Run viberaven anytime for the interactive menu."));
|
|
16570
16629
|
}
|
|
16571
|
-
var
|
|
16630
|
+
var import_node_path23, import_picocolors5;
|
|
16572
16631
|
var init_runInteractive = __esm({
|
|
16573
16632
|
"src/tui/runInteractive.ts"() {
|
|
16574
16633
|
"use strict";
|
|
16575
|
-
|
|
16634
|
+
import_node_path23 = require("node:path");
|
|
16576
16635
|
init_dist2();
|
|
16577
16636
|
import_picocolors5 = __toESM(require_picocolors());
|
|
16578
16637
|
init_config();
|
|
@@ -16597,18 +16656,18 @@ var init_runInteractive = __esm({
|
|
|
16597
16656
|
// src/commands/condense.ts
|
|
16598
16657
|
async function runCondenseCommand(options) {
|
|
16599
16658
|
const dir = getProjectArtifactsDir(options.cwd);
|
|
16600
|
-
const artifact2 = JSON.parse(await (0,
|
|
16601
|
-
const contextMapPath = (0,
|
|
16602
|
-
await (0,
|
|
16659
|
+
const artifact2 = JSON.parse(await (0, import_promises17.readFile)((0, import_node_path24.join)(dir, "last-scan.json"), "utf8"));
|
|
16660
|
+
const contextMapPath = (0, import_node_path24.join)(dir, "context-map.json");
|
|
16661
|
+
await (0, import_promises17.writeFile)(contextMapPath, `${JSON.stringify(generateContextMap(artifact2), null, 2)}
|
|
16603
16662
|
`, "utf8");
|
|
16604
16663
|
return { contextMapPath };
|
|
16605
16664
|
}
|
|
16606
|
-
var
|
|
16665
|
+
var import_promises17, import_node_path24;
|
|
16607
16666
|
var init_condense = __esm({
|
|
16608
16667
|
"src/commands/condense.ts"() {
|
|
16609
16668
|
"use strict";
|
|
16610
|
-
|
|
16611
|
-
|
|
16669
|
+
import_promises17 = require("node:fs/promises");
|
|
16670
|
+
import_node_path24 = require("node:path");
|
|
16612
16671
|
init_config();
|
|
16613
16672
|
init_contextMap();
|
|
16614
16673
|
}
|
|
@@ -16616,9 +16675,9 @@ var init_condense = __esm({
|
|
|
16616
16675
|
|
|
16617
16676
|
// src/heal/pathSafety.ts
|
|
16618
16677
|
function assertSafeHealTarget(cwd, target) {
|
|
16619
|
-
const root = (0,
|
|
16620
|
-
const absolute = (0,
|
|
16621
|
-
const rel = (0,
|
|
16678
|
+
const root = (0, import_node_path25.resolve)(cwd);
|
|
16679
|
+
const absolute = (0, import_node_path25.resolve)(root, target);
|
|
16680
|
+
const rel = (0, import_node_path25.relative)(root, absolute);
|
|
16622
16681
|
if (rel.startsWith("..") || rel === "" || /^[A-Za-z]:/.test(rel)) {
|
|
16623
16682
|
throw new Error("Heal target must stay inside the workspace");
|
|
16624
16683
|
}
|
|
@@ -16628,11 +16687,11 @@ function assertSafeHealTarget(cwd, target) {
|
|
|
16628
16687
|
}
|
|
16629
16688
|
return absolute;
|
|
16630
16689
|
}
|
|
16631
|
-
var
|
|
16690
|
+
var import_node_path25, BLOCKED_SEGMENTS;
|
|
16632
16691
|
var init_pathSafety = __esm({
|
|
16633
16692
|
"src/heal/pathSafety.ts"() {
|
|
16634
16693
|
"use strict";
|
|
16635
|
-
|
|
16694
|
+
import_node_path25 = require("node:path");
|
|
16636
16695
|
BLOCKED_SEGMENTS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", ".viberaven"]);
|
|
16637
16696
|
}
|
|
16638
16697
|
});
|
|
@@ -16658,13 +16717,13 @@ function newHealId(now2 = /* @__PURE__ */ new Date()) {
|
|
|
16658
16717
|
return `heal_${now2.toISOString().replace(/\D/g, "").slice(0, 17)}_${(0, import_node_crypto2.randomBytes)(3).toString("hex")}`;
|
|
16659
16718
|
}
|
|
16660
16719
|
async function createHealDir(cwd, makeId = () => newHealId()) {
|
|
16661
|
-
const parent = (0,
|
|
16662
|
-
await (0,
|
|
16720
|
+
const parent = (0, import_node_path26.join)(cwd, ".viberaven", "heal");
|
|
16721
|
+
await (0, import_promises18.mkdir)(parent, { recursive: true });
|
|
16663
16722
|
for (let attempt = 0; attempt < 10; attempt += 1) {
|
|
16664
16723
|
const id2 = makeId();
|
|
16665
|
-
const dir = (0,
|
|
16724
|
+
const dir = (0, import_node_path26.join)(parent, id2);
|
|
16666
16725
|
try {
|
|
16667
|
-
await (0,
|
|
16726
|
+
await (0, import_promises18.mkdir)(dir);
|
|
16668
16727
|
return { id: id2, dir };
|
|
16669
16728
|
} catch (error) {
|
|
16670
16729
|
if (error.code !== "EEXIST") throw error;
|
|
@@ -16672,13 +16731,13 @@ async function createHealDir(cwd, makeId = () => newHealId()) {
|
|
|
16672
16731
|
}
|
|
16673
16732
|
throw new Error("Could not create a new heal directory under .viberaven/heal.");
|
|
16674
16733
|
}
|
|
16675
|
-
var import_node_crypto2,
|
|
16734
|
+
var import_node_crypto2, import_promises18, import_node_path26;
|
|
16676
16735
|
var init_healId = __esm({
|
|
16677
16736
|
"src/heal/healId.ts"() {
|
|
16678
16737
|
"use strict";
|
|
16679
16738
|
import_node_crypto2 = require("node:crypto");
|
|
16680
|
-
|
|
16681
|
-
|
|
16739
|
+
import_promises18 = require("node:fs/promises");
|
|
16740
|
+
import_node_path26 = require("node:path");
|
|
16682
16741
|
}
|
|
16683
16742
|
});
|
|
16684
16743
|
|
|
@@ -17145,7 +17204,7 @@ export const config = {
|
|
|
17145
17204
|
// src/heal/recipes/eslintRestrictedImports.ts
|
|
17146
17205
|
function detectEslintConfigFile(cwd) {
|
|
17147
17206
|
for (const candidate of ESLINT_CONFIG_CANDIDATES) {
|
|
17148
|
-
if ((0, import_node_fs11.existsSync)((0,
|
|
17207
|
+
if ((0, import_node_fs11.existsSync)((0, import_node_path27.join)(cwd, candidate))) {
|
|
17149
17208
|
return candidate;
|
|
17150
17209
|
}
|
|
17151
17210
|
}
|
|
@@ -17300,12 +17359,12 @@ function applyEslintRestrictedImportsRecipe(source, configFile) {
|
|
|
17300
17359
|
}
|
|
17301
17360
|
return applyLegacyModuleRecipe(source);
|
|
17302
17361
|
}
|
|
17303
|
-
var import_node_fs11,
|
|
17362
|
+
var import_node_fs11, import_node_path27, VIBERAVEN_ESLINT_MARKER, RESTRICTED_IMPORTS_MESSAGE, RESTRICTED_PATHS, RESTRICTED_PATTERNS, ESLINT_CONFIG_CANDIDATES;
|
|
17304
17363
|
var init_eslintRestrictedImports = __esm({
|
|
17305
17364
|
"src/heal/recipes/eslintRestrictedImports.ts"() {
|
|
17306
17365
|
"use strict";
|
|
17307
17366
|
import_node_fs11 = require("node:fs");
|
|
17308
|
-
|
|
17367
|
+
import_node_path27 = require("node:path");
|
|
17309
17368
|
init_commands();
|
|
17310
17369
|
VIBERAVEN_ESLINT_MARKER = "VibeRaven heal: eslint_restricted_imports";
|
|
17311
17370
|
RESTRICTED_IMPORTS_MESSAGE = `Restricted import. Run ${PUBLIC_AGENT_MODE_COMMAND} before substituting packages.`;
|
|
@@ -17357,13 +17416,13 @@ function defaultTargetFile(gapId) {
|
|
|
17357
17416
|
}
|
|
17358
17417
|
async function readSourceOrEmpty(absolutePath) {
|
|
17359
17418
|
if (!(0, import_node_fs12.existsSync)(absolutePath)) return "";
|
|
17360
|
-
return (0,
|
|
17419
|
+
return (0, import_promises19.readFile)(absolutePath, "utf8");
|
|
17361
17420
|
}
|
|
17362
17421
|
async function detectUpstash(cwd) {
|
|
17363
17422
|
try {
|
|
17364
|
-
const pkgPath = (0,
|
|
17423
|
+
const pkgPath = (0, import_node_path28.join)(cwd, "package.json");
|
|
17365
17424
|
if (!(0, import_node_fs12.existsSync)(pkgPath)) return false;
|
|
17366
|
-
const raw = await (0,
|
|
17425
|
+
const raw = await (0, import_promises19.readFile)(pkgPath, "utf8");
|
|
17367
17426
|
const pkg = JSON.parse(raw);
|
|
17368
17427
|
const deps = {
|
|
17369
17428
|
...pkg["dependencies"] ?? {},
|
|
@@ -17378,7 +17437,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
17378
17437
|
const targetFile = explicitTarget ?? defaultTargetFile(gapId);
|
|
17379
17438
|
if (targetFile === void 0) return null;
|
|
17380
17439
|
if (gapId === "auth_secret_missing" || gapId === "node_env_not_set" || gapId === "database_url_missing") {
|
|
17381
|
-
const absolutePath = (0,
|
|
17440
|
+
const absolutePath = (0, import_node_path28.join)(cwd, targetFile);
|
|
17382
17441
|
const source = await readSourceOrEmpty(absolutePath);
|
|
17383
17442
|
let result;
|
|
17384
17443
|
if (gapId === "auth_secret_missing") result = applyAuthSecretRecipe(source);
|
|
@@ -17392,25 +17451,25 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
17392
17451
|
};
|
|
17393
17452
|
}
|
|
17394
17453
|
if (gapId === "missing_error_boundary") {
|
|
17395
|
-
const absolutePath = (0,
|
|
17454
|
+
const absolutePath = (0, import_node_path28.join)(cwd, "app/error.tsx");
|
|
17396
17455
|
const source = await readSourceOrEmpty(absolutePath);
|
|
17397
17456
|
const result = applyErrorBoundaryRecipe(source);
|
|
17398
17457
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
17399
17458
|
}
|
|
17400
17459
|
if (gapId === "missing_health_route") {
|
|
17401
|
-
const absolutePath = (0,
|
|
17460
|
+
const absolutePath = (0, import_node_path28.join)(cwd, "app/api/health/route.ts");
|
|
17402
17461
|
const source = await readSourceOrEmpty(absolutePath);
|
|
17403
17462
|
const result = applyHealthRouteRecipe(source);
|
|
17404
17463
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
17405
17464
|
}
|
|
17406
17465
|
if (gapId === "missing_loading_state") {
|
|
17407
|
-
const absolutePath = (0,
|
|
17466
|
+
const absolutePath = (0, import_node_path28.join)(cwd, "app/loading.tsx");
|
|
17408
17467
|
const source = await readSourceOrEmpty(absolutePath);
|
|
17409
17468
|
const result = applyLoadingStateRecipe(source);
|
|
17410
17469
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
17411
17470
|
}
|
|
17412
17471
|
if (gapId === "missing_404_page") {
|
|
17413
|
-
const absolutePath = (0,
|
|
17472
|
+
const absolutePath = (0, import_node_path28.join)(cwd, "app/not-found.tsx");
|
|
17414
17473
|
const source = await readSourceOrEmpty(absolutePath);
|
|
17415
17474
|
const result = applyNotFoundRecipe(source);
|
|
17416
17475
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
@@ -17427,10 +17486,10 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
17427
17486
|
}
|
|
17428
17487
|
if (gapId === "missing_csp_header") {
|
|
17429
17488
|
let configFile = "next.config.js";
|
|
17430
|
-
let absolutePath = (0,
|
|
17489
|
+
let absolutePath = (0, import_node_path28.join)(cwd, configFile);
|
|
17431
17490
|
if (!(0, import_node_fs12.existsSync)(absolutePath)) {
|
|
17432
17491
|
configFile = "next.config.mjs";
|
|
17433
|
-
absolutePath = (0,
|
|
17492
|
+
absolutePath = (0, import_node_path28.join)(cwd, configFile);
|
|
17434
17493
|
}
|
|
17435
17494
|
const source = await readSourceOrEmpty(absolutePath);
|
|
17436
17495
|
const result = applyCspHeaderRecipe(source);
|
|
@@ -17442,7 +17501,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
17442
17501
|
}
|
|
17443
17502
|
if (gapId === "missing_rate_limit") {
|
|
17444
17503
|
const hasUpstash = await detectUpstash(cwd);
|
|
17445
|
-
const middlewarePath = (0,
|
|
17504
|
+
const middlewarePath = (0, import_node_path28.join)(cwd, "middleware.ts");
|
|
17446
17505
|
const source = await readSourceOrEmpty(middlewarePath);
|
|
17447
17506
|
const result = applyRateLimitRecipe(source, hasUpstash);
|
|
17448
17507
|
return {
|
|
@@ -17464,7 +17523,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
17464
17523
|
recipeName: gapId
|
|
17465
17524
|
};
|
|
17466
17525
|
}
|
|
17467
|
-
const absolutePath = (0,
|
|
17526
|
+
const absolutePath = (0, import_node_path28.join)(cwd, configFile);
|
|
17468
17527
|
const source = await readSourceOrEmpty(absolutePath);
|
|
17469
17528
|
const result = applyEslintRestrictedImportsRecipe(source, configFile);
|
|
17470
17529
|
return {
|
|
@@ -17478,13 +17537,13 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
17478
17537
|
}
|
|
17479
17538
|
return null;
|
|
17480
17539
|
}
|
|
17481
|
-
var import_node_fs12,
|
|
17540
|
+
var import_node_fs12, import_promises19, import_node_path28;
|
|
17482
17541
|
var init_recipes = __esm({
|
|
17483
17542
|
"src/heal/recipes/index.ts"() {
|
|
17484
17543
|
"use strict";
|
|
17485
17544
|
import_node_fs12 = require("node:fs");
|
|
17486
|
-
|
|
17487
|
-
|
|
17545
|
+
import_promises19 = require("node:fs/promises");
|
|
17546
|
+
import_node_path28 = require("node:path");
|
|
17488
17547
|
init_envAuthSecret();
|
|
17489
17548
|
init_envNodeEnv();
|
|
17490
17549
|
init_envDatabaseUrl();
|
|
@@ -17561,17 +17620,17 @@ async function applyHeal(options) {
|
|
|
17561
17620
|
rollback: { available: false, instructions: "Recipe matched but no change was needed (already applied or file already exists)." }
|
|
17562
17621
|
};
|
|
17563
17622
|
}
|
|
17564
|
-
const absoluteTarget = (0,
|
|
17623
|
+
const absoluteTarget = (0, import_node_path29.join)(options.cwd, dispatched.targetFile);
|
|
17565
17624
|
assertSafeHealTarget(options.cwd, dispatched.targetFile);
|
|
17566
|
-
await (0,
|
|
17625
|
+
await (0, import_promises20.mkdir)((0, import_node_path29.dirname)(absoluteTarget), { recursive: true });
|
|
17567
17626
|
const created2 = await createHealDir(options.cwd);
|
|
17568
17627
|
id2 = created2.id;
|
|
17569
17628
|
const healDir2 = created2.dir;
|
|
17570
|
-
await (0,
|
|
17629
|
+
await (0, import_promises20.mkdir)((0, import_node_path29.join)(healDir2, "before"));
|
|
17571
17630
|
const existedBefore = (0, import_node_fs13.existsSync)(absoluteTarget);
|
|
17572
|
-
const beforeContent = existedBefore ? await (0,
|
|
17573
|
-
await (0,
|
|
17574
|
-
await (0,
|
|
17631
|
+
const beforeContent = existedBefore ? await (0, import_promises20.readFile)(absoluteTarget, "utf8") : "";
|
|
17632
|
+
await (0, import_promises20.writeFile)((0, import_node_path29.join)(healDir2, "before", "target.txt"), beforeContent, "utf8");
|
|
17633
|
+
await (0, import_promises20.writeFile)(absoluteTarget, dispatched.output, "utf8");
|
|
17575
17634
|
const patch2 = [
|
|
17576
17635
|
`--- ${dispatched.targetFile}`,
|
|
17577
17636
|
`+++ ${dispatched.targetFile}`,
|
|
@@ -17581,7 +17640,7 @@ async function applyHeal(options) {
|
|
|
17581
17640
|
dispatched.output,
|
|
17582
17641
|
""
|
|
17583
17642
|
].join("\n");
|
|
17584
|
-
await (0,
|
|
17643
|
+
await (0, import_promises20.writeFile)((0, import_node_path29.join)(healDir2, "patch.diff"), patch2, "utf8");
|
|
17585
17644
|
const result2 = {
|
|
17586
17645
|
$schema: "https://viberaven.dev/schemas/heal-result.schema.json",
|
|
17587
17646
|
schemaVersion: "v1",
|
|
@@ -17593,17 +17652,17 @@ async function applyHeal(options) {
|
|
|
17593
17652
|
recipe: dispatched.recipeName,
|
|
17594
17653
|
...dispatched.warnings?.length ? { warnings: dispatched.warnings } : {},
|
|
17595
17654
|
target: dispatched.targetFile,
|
|
17596
|
-
changedFiles: [(0,
|
|
17655
|
+
changedFiles: [(0, import_node_path29.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
|
|
17597
17656
|
artifacts: {
|
|
17598
17657
|
patch: `.viberaven/heal/${id2}/patch.diff`,
|
|
17599
17658
|
result: `.viberaven/heal/${id2}/result.json`
|
|
17600
17659
|
},
|
|
17601
17660
|
rollback: {
|
|
17602
17661
|
available: true,
|
|
17603
|
-
instructions: rollbackInstructions(id2, (0,
|
|
17662
|
+
instructions: rollbackInstructions(id2, (0, import_node_path29.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/"), existedBefore)
|
|
17604
17663
|
}
|
|
17605
17664
|
};
|
|
17606
|
-
await (0,
|
|
17665
|
+
await (0, import_promises20.writeFile)((0, import_node_path29.join)(healDir2, "result.json"), `${JSON.stringify(result2, null, 2)}
|
|
17607
17666
|
`, "utf8");
|
|
17608
17667
|
return result2;
|
|
17609
17668
|
}
|
|
@@ -17623,7 +17682,7 @@ async function applyHeal(options) {
|
|
|
17623
17682
|
};
|
|
17624
17683
|
}
|
|
17625
17684
|
const absolute = assertSafeHealTarget(options.cwd, options.target);
|
|
17626
|
-
const before = await (0,
|
|
17685
|
+
const before = await (0, import_promises20.readFile)(absolute, "utf8");
|
|
17627
17686
|
const recipe = applyEmptyCatchRecipe(before);
|
|
17628
17687
|
if (!recipe.changed) {
|
|
17629
17688
|
return {
|
|
@@ -17643,9 +17702,9 @@ async function applyHeal(options) {
|
|
|
17643
17702
|
const created = await createHealDir(options.cwd);
|
|
17644
17703
|
id2 = created.id;
|
|
17645
17704
|
const healDir = created.dir;
|
|
17646
|
-
await (0,
|
|
17647
|
-
await (0,
|
|
17648
|
-
await (0,
|
|
17705
|
+
await (0, import_promises20.mkdir)((0, import_node_path29.join)(healDir, "before"));
|
|
17706
|
+
await (0, import_promises20.writeFile)((0, import_node_path29.join)(healDir, "before", "target.txt"), before, "utf8");
|
|
17707
|
+
await (0, import_promises20.writeFile)(absolute, recipe.output, "utf8");
|
|
17649
17708
|
const patch = [
|
|
17650
17709
|
`--- ${options.target}`,
|
|
17651
17710
|
`+++ ${options.target}`,
|
|
@@ -17655,7 +17714,7 @@ async function applyHeal(options) {
|
|
|
17655
17714
|
recipe.output,
|
|
17656
17715
|
""
|
|
17657
17716
|
].join("\n");
|
|
17658
|
-
await (0,
|
|
17717
|
+
await (0, import_promises20.writeFile)((0, import_node_path29.join)(healDir, "patch.diff"), patch, "utf8");
|
|
17659
17718
|
const result = {
|
|
17660
17719
|
$schema: "https://viberaven.dev/schemas/heal-result.schema.json",
|
|
17661
17720
|
schemaVersion: "v1",
|
|
@@ -17666,7 +17725,7 @@ async function applyHeal(options) {
|
|
|
17666
17725
|
gapId: options.gapId,
|
|
17667
17726
|
recipe: "empty-catch-safe-response",
|
|
17668
17727
|
target: options.target,
|
|
17669
|
-
changedFiles: [(0,
|
|
17728
|
+
changedFiles: [(0, import_node_path29.relative)(options.cwd, absolute).replace(/\\/g, "/")],
|
|
17670
17729
|
artifacts: {
|
|
17671
17730
|
patch: `.viberaven/heal/${id2}/patch.diff`,
|
|
17672
17731
|
result: `.viberaven/heal/${id2}/result.json`
|
|
@@ -17674,20 +17733,20 @@ async function applyHeal(options) {
|
|
|
17674
17733
|
rollback: {
|
|
17675
17734
|
available: true,
|
|
17676
17735
|
// The empty-catch recipe only edits a file that already exists.
|
|
17677
|
-
instructions: rollbackInstructions(id2, (0,
|
|
17736
|
+
instructions: rollbackInstructions(id2, (0, import_node_path29.relative)(options.cwd, absolute).replace(/\\/g, "/"), true)
|
|
17678
17737
|
}
|
|
17679
17738
|
};
|
|
17680
|
-
await (0,
|
|
17739
|
+
await (0, import_promises20.writeFile)((0, import_node_path29.join)(healDir, "result.json"), `${JSON.stringify(result, null, 2)}
|
|
17681
17740
|
`, "utf8");
|
|
17682
17741
|
return result;
|
|
17683
17742
|
}
|
|
17684
|
-
var
|
|
17743
|
+
var import_promises20, import_node_fs13, import_node_path29;
|
|
17685
17744
|
var init_apply = __esm({
|
|
17686
17745
|
"src/heal/apply.ts"() {
|
|
17687
17746
|
"use strict";
|
|
17688
|
-
|
|
17747
|
+
import_promises20 = require("node:fs/promises");
|
|
17689
17748
|
import_node_fs13 = require("node:fs");
|
|
17690
|
-
|
|
17749
|
+
import_node_path29 = require("node:path");
|
|
17691
17750
|
init_pathSafety();
|
|
17692
17751
|
init_emptyCatch();
|
|
17693
17752
|
init_healId();
|
|
@@ -17697,8 +17756,8 @@ var init_apply = __esm({
|
|
|
17697
17756
|
|
|
17698
17757
|
// src/heal/plan.ts
|
|
17699
17758
|
async function writeHealPlan(options) {
|
|
17700
|
-
const dir = (0,
|
|
17701
|
-
await (0,
|
|
17759
|
+
const dir = (0, import_node_path30.join)(options.cwd, ".viberaven");
|
|
17760
|
+
await (0, import_promises21.mkdir)(dir, { recursive: true });
|
|
17702
17761
|
const id2 = newHealId();
|
|
17703
17762
|
const target = options.target ?? `gap:${options.gapId}`;
|
|
17704
17763
|
const proposal = [];
|
|
@@ -17750,18 +17809,18 @@ async function writeHealPlan(options) {
|
|
|
17750
17809
|
artifacts: { plan: ".viberaven/heal-plan.md" },
|
|
17751
17810
|
rollback: { available: false, instructions: "No source files were changed." }
|
|
17752
17811
|
};
|
|
17753
|
-
await (0,
|
|
17754
|
-
await (0,
|
|
17812
|
+
await (0, import_promises21.writeFile)((0, import_node_path30.join)(dir, "heal-plan.md"), markdown, "utf8");
|
|
17813
|
+
await (0, import_promises21.writeFile)((0, import_node_path30.join)(dir, "heal-plan.json"), `${JSON.stringify(result, null, 2)}
|
|
17755
17814
|
`, "utf8");
|
|
17756
17815
|
return result;
|
|
17757
17816
|
}
|
|
17758
|
-
var
|
|
17817
|
+
var import_promises21, import_node_path30;
|
|
17759
17818
|
var init_plan = __esm({
|
|
17760
17819
|
"src/heal/plan.ts"() {
|
|
17761
17820
|
"use strict";
|
|
17762
|
-
|
|
17821
|
+
import_promises21 = require("node:fs/promises");
|
|
17763
17822
|
init_healId();
|
|
17764
|
-
|
|
17823
|
+
import_node_path30 = require("node:path");
|
|
17765
17824
|
init_commands();
|
|
17766
17825
|
init_supabaseRls2();
|
|
17767
17826
|
}
|
|
@@ -17769,8 +17828,8 @@ var init_plan = __esm({
|
|
|
17769
17828
|
|
|
17770
17829
|
// src/heal/prompt.ts
|
|
17771
17830
|
async function writeHealPrompt(options) {
|
|
17772
|
-
const dir = (0,
|
|
17773
|
-
await (0,
|
|
17831
|
+
const dir = (0, import_node_path31.join)(options.cwd, ".viberaven");
|
|
17832
|
+
await (0, import_promises22.mkdir)(dir, { recursive: true });
|
|
17774
17833
|
const id2 = newHealId();
|
|
17775
17834
|
const target = options.target ?? `gap:${options.gapId}`;
|
|
17776
17835
|
const prompt = [
|
|
@@ -17797,16 +17856,16 @@ async function writeHealPrompt(options) {
|
|
|
17797
17856
|
artifacts: { prompt: ".viberaven/heal-prompt.md" },
|
|
17798
17857
|
rollback: { available: false, instructions: "No source files were changed." }
|
|
17799
17858
|
};
|
|
17800
|
-
await (0,
|
|
17859
|
+
await (0, import_promises22.writeFile)((0, import_node_path31.join)(dir, "heal-prompt.md"), prompt, "utf8");
|
|
17801
17860
|
return result;
|
|
17802
17861
|
}
|
|
17803
|
-
var
|
|
17862
|
+
var import_promises22, import_node_path31;
|
|
17804
17863
|
var init_prompt = __esm({
|
|
17805
17864
|
"src/heal/prompt.ts"() {
|
|
17806
17865
|
"use strict";
|
|
17807
|
-
|
|
17866
|
+
import_promises22 = require("node:fs/promises");
|
|
17808
17867
|
init_healId();
|
|
17809
|
-
|
|
17868
|
+
import_node_path31 = require("node:path");
|
|
17810
17869
|
init_commands();
|
|
17811
17870
|
}
|
|
17812
17871
|
});
|
|
@@ -17817,7 +17876,7 @@ function loopStatePath(workspaceRoot) {
|
|
|
17817
17876
|
}
|
|
17818
17877
|
async function loadLoopState(workspaceRoot) {
|
|
17819
17878
|
try {
|
|
17820
|
-
const raw = await (0,
|
|
17879
|
+
const raw = await (0, import_promises23.readFile)(loopStatePath(workspaceRoot), "utf8");
|
|
17821
17880
|
const parsed = JSON.parse(raw);
|
|
17822
17881
|
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && typeof parsed.batchApplied === "number" && typeof parsed.lastGapCount === "number" && typeof parsed.stalledScans === "number") {
|
|
17823
17882
|
const p2 = parsed;
|
|
@@ -17837,8 +17896,8 @@ async function loadLoopState(workspaceRoot) {
|
|
|
17837
17896
|
async function saveLoopState(workspaceRoot, state) {
|
|
17838
17897
|
try {
|
|
17839
17898
|
const dir = (0, import_path.join)(workspaceRoot, ".viberaven");
|
|
17840
|
-
await (0,
|
|
17841
|
-
await (0,
|
|
17899
|
+
await (0, import_promises23.mkdir)(dir, { recursive: true });
|
|
17900
|
+
await (0, import_promises23.writeFile)(loopStatePath(workspaceRoot), JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
17842
17901
|
} catch (err) {
|
|
17843
17902
|
console.warn("[VibeRaven] Could not save loop-state.json:", err instanceof Error ? err.message : String(err));
|
|
17844
17903
|
}
|
|
@@ -17864,11 +17923,11 @@ function resetBatch(state, newGapCount) {
|
|
|
17864
17923
|
appliedGapIdsSinceScan: []
|
|
17865
17924
|
};
|
|
17866
17925
|
}
|
|
17867
|
-
var
|
|
17926
|
+
var import_promises23, import_path, DEFAULT_LOOP_STATE;
|
|
17868
17927
|
var init_loopState = __esm({
|
|
17869
17928
|
"src/loopState.ts"() {
|
|
17870
17929
|
"use strict";
|
|
17871
|
-
|
|
17930
|
+
import_promises23 = require("fs/promises");
|
|
17872
17931
|
import_path = require("path");
|
|
17873
17932
|
DEFAULT_LOOP_STATE = {
|
|
17874
17933
|
batchApplied: 0,
|
|
@@ -17909,11 +17968,11 @@ var init_heal = __esm({
|
|
|
17909
17968
|
|
|
17910
17969
|
// src/stackRecommend.ts
|
|
17911
17970
|
async function recommendStack(cwd = process.cwd()) {
|
|
17912
|
-
const pkgPath = (0,
|
|
17971
|
+
const pkgPath = (0, import_node_path32.join)(cwd, "package.json");
|
|
17913
17972
|
if (!(0, import_node_fs14.existsSync)(pkgPath)) {
|
|
17914
17973
|
return DEFAULT_STACK;
|
|
17915
17974
|
}
|
|
17916
|
-
const pkg = JSON.parse(await (0,
|
|
17975
|
+
const pkg = JSON.parse(await (0, import_promises24.readFile)(pkgPath, "utf-8"));
|
|
17917
17976
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
17918
17977
|
const names = Object.keys(deps).join(" ").toLowerCase();
|
|
17919
17978
|
const rec = {
|
|
@@ -17941,13 +18000,13 @@ async function recommendStack(cwd = process.cwd()) {
|
|
|
17941
18000
|
}
|
|
17942
18001
|
return rec;
|
|
17943
18002
|
}
|
|
17944
|
-
var
|
|
18003
|
+
var import_promises24, import_node_fs14, import_node_path32, DEFAULT_STACK;
|
|
17945
18004
|
var init_stackRecommend = __esm({
|
|
17946
18005
|
"src/stackRecommend.ts"() {
|
|
17947
18006
|
"use strict";
|
|
17948
|
-
|
|
18007
|
+
import_promises24 = require("node:fs/promises");
|
|
17949
18008
|
import_node_fs14 = require("node:fs");
|
|
17950
|
-
|
|
18009
|
+
import_node_path32 = require("node:path");
|
|
17951
18010
|
DEFAULT_STACK = {
|
|
17952
18011
|
frontend: "react",
|
|
17953
18012
|
ui: "tailwind + shadcn/ui",
|
|
@@ -18104,13 +18163,13 @@ async function hasGithubRemote(cwd) {
|
|
|
18104
18163
|
}
|
|
18105
18164
|
}
|
|
18106
18165
|
async function githubWorkflowStatus(cwd) {
|
|
18107
|
-
return { exists: (0, import_node_fs15.existsSync)((0,
|
|
18166
|
+
return { exists: (0, import_node_fs15.existsSync)((0, import_node_path33.join)(cwd, WORKFLOW_PATH)), path: DISPLAY_PATH, publisherExists: (0, import_node_fs15.existsSync)((0, import_node_path33.join)(cwd, PUBLISHER_PATH)), publisherPath: DISPLAY_PUBLISHER, onGithub: await hasGithubRemote(cwd) };
|
|
18108
18167
|
}
|
|
18109
18168
|
async function writeGithubWorkflow(cwd, options = {}) {
|
|
18110
|
-
const targets = [(0,
|
|
18111
|
-
for (const parent of [(0,
|
|
18169
|
+
const targets = [(0, import_node_path33.join)(cwd, WORKFLOW_PATH), (0, import_node_path33.join)(cwd, PUBLISHER_PATH)];
|
|
18170
|
+
for (const parent of [(0, import_node_path33.join)(cwd, ".github"), (0, import_node_path33.join)(cwd, ".github", "workflows")]) {
|
|
18112
18171
|
try {
|
|
18113
|
-
const stat12 = await (0,
|
|
18172
|
+
const stat12 = await (0, import_promises25.lstat)(parent);
|
|
18114
18173
|
if (!stat12.isDirectory() || stat12.isSymbolicLink()) throw new Error("Workflow directory must be a regular directory");
|
|
18115
18174
|
} catch (error) {
|
|
18116
18175
|
if (error.code !== "ENOENT") throw error;
|
|
@@ -18119,31 +18178,31 @@ async function writeGithubWorkflow(cwd, options = {}) {
|
|
|
18119
18178
|
if (!options.force && targets.some(import_node_fs15.existsSync)) return { written: false, status: await githubWorkflowStatus(cwd) };
|
|
18120
18179
|
const originals = await Promise.all(targets.map(async (p2) => {
|
|
18121
18180
|
try {
|
|
18122
|
-
if (!(await (0,
|
|
18123
|
-
return await (0,
|
|
18181
|
+
if (!(await (0, import_promises25.lstat)(p2)).isFile()) throw new Error("Workflow target must be a regular file");
|
|
18182
|
+
return await (0, import_promises25.readFile)(p2);
|
|
18124
18183
|
} catch (error) {
|
|
18125
18184
|
if (error.code === "ENOENT") return null;
|
|
18126
18185
|
throw error;
|
|
18127
18186
|
}
|
|
18128
18187
|
}));
|
|
18129
|
-
await (0,
|
|
18130
|
-
const stagingDirectory = await (0,
|
|
18131
|
-
const staged = targets.map((_3, i) => (0,
|
|
18188
|
+
await (0, import_promises25.mkdir)((0, import_node_path33.dirname)(targets[0]), { recursive: true });
|
|
18189
|
+
const stagingDirectory = await (0, import_promises25.mkdtemp)((0, import_node_path33.join)((0, import_node_path33.dirname)(targets[0]), ".viberaven-stage-"));
|
|
18190
|
+
const staged = targets.map((_3, i) => (0, import_node_path33.join)(stagingDirectory, `${i}.yml`));
|
|
18132
18191
|
let replaced = 0;
|
|
18133
18192
|
try {
|
|
18134
|
-
for (const [i, content] of [renderWorkflow(), renderPublisherWorkflow()].entries()) await (0,
|
|
18193
|
+
for (const [i, content] of [renderWorkflow(), renderPublisherWorkflow()].entries()) await (0, import_promises25.writeFile)(staged[i], content, { encoding: "utf8", flag: "wx" });
|
|
18135
18194
|
for (const [i, target] of targets.entries()) {
|
|
18136
|
-
await (0,
|
|
18195
|
+
await (0, import_promises25.rename)(staged[i], target);
|
|
18137
18196
|
replaced++;
|
|
18138
18197
|
}
|
|
18139
18198
|
} catch (error) {
|
|
18140
18199
|
for (let i = 0; i < replaced; i++) {
|
|
18141
|
-
if (originals[i] === null) await (0,
|
|
18142
|
-
else await (0,
|
|
18200
|
+
if (originals[i] === null) await (0, import_promises25.rm)(targets[i], { force: true });
|
|
18201
|
+
else await (0, import_promises25.writeFile)(targets[i], originals[i]);
|
|
18143
18202
|
}
|
|
18144
18203
|
throw error;
|
|
18145
18204
|
} finally {
|
|
18146
|
-
await (0,
|
|
18205
|
+
await (0, import_promises25.rm)(stagingDirectory, { recursive: true, force: true });
|
|
18147
18206
|
}
|
|
18148
18207
|
return { written: true, status: await githubWorkflowStatus(cwd) };
|
|
18149
18208
|
}
|
|
@@ -18167,20 +18226,20 @@ ${renderPublisherWorkflow()}`);
|
|
|
18167
18226
|
if (!status.onGithub) console.log("No github.com remote found; these workflows run after you push them to GitHub.");
|
|
18168
18227
|
return 0;
|
|
18169
18228
|
}
|
|
18170
|
-
var import_node_child_process6, import_node_fs15,
|
|
18229
|
+
var import_node_child_process6, import_node_fs15, import_promises25, import_node_path33, import_node_util4, execFileAsync4, WORKFLOW_PATH, PUBLISHER_PATH, SCAN_NAME, PUBLISHER_NAME, DISPLAY_PATH, DISPLAY_PUBLISHER, CHECKOUT, NODE, UPLOAD;
|
|
18171
18230
|
var init_initGithub = __esm({
|
|
18172
18231
|
"src/commands/initGithub.ts"() {
|
|
18173
18232
|
"use strict";
|
|
18174
18233
|
import_node_child_process6 = require("node:child_process");
|
|
18175
18234
|
import_node_fs15 = require("node:fs");
|
|
18176
|
-
|
|
18177
|
-
|
|
18235
|
+
import_promises25 = require("node:fs/promises");
|
|
18236
|
+
import_node_path33 = require("node:path");
|
|
18178
18237
|
import_node_util4 = require("node:util");
|
|
18179
18238
|
init_safeGit();
|
|
18180
18239
|
init_version();
|
|
18181
18240
|
execFileAsync4 = (0, import_node_util4.promisify)(import_node_child_process6.execFile);
|
|
18182
|
-
WORKFLOW_PATH = (0,
|
|
18183
|
-
PUBLISHER_PATH = (0,
|
|
18241
|
+
WORKFLOW_PATH = (0, import_node_path33.join)(".github", "workflows", "viberaven.yml");
|
|
18242
|
+
PUBLISHER_PATH = (0, import_node_path33.join)(".github", "workflows", "viberaven-comment.yml");
|
|
18184
18243
|
SCAN_NAME = "VibeRaven";
|
|
18185
18244
|
PUBLISHER_NAME = "VibeRaven feedback";
|
|
18186
18245
|
DISPLAY_PATH = ".github/workflows/viberaven.yml";
|
|
@@ -18210,7 +18269,7 @@ function managedBlocks(content) {
|
|
|
18210
18269
|
async function checkAgentInjection(cwd) {
|
|
18211
18270
|
const checks = [];
|
|
18212
18271
|
for (const item4 of REQUIRED_EXISTENCE_CHECKS) {
|
|
18213
|
-
const exists2 = await fileExists2((0,
|
|
18272
|
+
const exists2 = await fileExists2((0, import_node_path34.join)(cwd, item4.file));
|
|
18214
18273
|
checks.push({
|
|
18215
18274
|
id: item4.id,
|
|
18216
18275
|
status: exists2 ? "pass" : "fail",
|
|
@@ -18218,17 +18277,17 @@ async function checkAgentInjection(cwd) {
|
|
|
18218
18277
|
});
|
|
18219
18278
|
}
|
|
18220
18279
|
for (const item4 of OPTIONAL_CURSOR_PACK_CHECKS) {
|
|
18221
|
-
const exists2 = await fileExists2((0,
|
|
18280
|
+
const exists2 = await fileExists2((0, import_node_path34.join)(cwd, item4.file));
|
|
18222
18281
|
checks.push({
|
|
18223
18282
|
id: item4.id,
|
|
18224
18283
|
status: exists2 ? "pass" : "fail",
|
|
18225
18284
|
message: exists2 ? `${item4.file} exists` : `Missing ${item4.file} \u2014 run npx -y viberaven init --agents all`
|
|
18226
18285
|
});
|
|
18227
18286
|
}
|
|
18228
|
-
const legacyCursorPath = (0,
|
|
18287
|
+
const legacyCursorPath = (0, import_node_path34.join)(cwd, ".cursor/rules/viberaven.mdc");
|
|
18229
18288
|
if (await fileExists2(legacyCursorPath)) {
|
|
18230
|
-
const legacyContent = await (0,
|
|
18231
|
-
const hasCoreSplit = await fileExists2((0,
|
|
18289
|
+
const legacyContent = await (0, import_promises26.readFile)(legacyCursorPath, "utf-8");
|
|
18290
|
+
const hasCoreSplit = await fileExists2((0, import_node_path34.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
|
|
18232
18291
|
if (!hasCoreSplit || legacyContent.includes("alwaysApply: true")) {
|
|
18233
18292
|
checks.push({
|
|
18234
18293
|
id: "cursor-legacy-mdc",
|
|
@@ -18239,7 +18298,7 @@ async function checkAgentInjection(cwd) {
|
|
|
18239
18298
|
}
|
|
18240
18299
|
for (const target of CORE_AGENT_INJECTION_TARGETS) {
|
|
18241
18300
|
const file = target === "cursor" ? ".cursor/rules/viberaven-core.mdc" : AGENT_RULE_TARGETS[target].file;
|
|
18242
|
-
const path2 = (0,
|
|
18301
|
+
const path2 = (0, import_node_path34.join)(cwd, file);
|
|
18243
18302
|
const exists2 = await fileExists2(path2);
|
|
18244
18303
|
if (!exists2) {
|
|
18245
18304
|
checks.push({
|
|
@@ -18249,7 +18308,7 @@ async function checkAgentInjection(cwd) {
|
|
|
18249
18308
|
});
|
|
18250
18309
|
continue;
|
|
18251
18310
|
}
|
|
18252
|
-
const content = await (0,
|
|
18311
|
+
const content = await (0, import_promises26.readFile)(path2, "utf-8");
|
|
18253
18312
|
const hasCommand = content.includes(PUBLIC_CHECK_COMMAND);
|
|
18254
18313
|
checks.push({
|
|
18255
18314
|
id: `canonical-${target}`,
|
|
@@ -18292,18 +18351,18 @@ function formatDoctorAgentsReport(report2) {
|
|
|
18292
18351
|
}
|
|
18293
18352
|
async function fileExists2(path2) {
|
|
18294
18353
|
try {
|
|
18295
|
-
await (0,
|
|
18354
|
+
await (0, import_promises26.access)(path2);
|
|
18296
18355
|
return true;
|
|
18297
18356
|
} catch {
|
|
18298
18357
|
return false;
|
|
18299
18358
|
}
|
|
18300
18359
|
}
|
|
18301
|
-
var
|
|
18360
|
+
var import_promises26, import_node_path34, REQUIRED_EXISTENCE_CHECKS, OPTIONAL_CURSOR_PACK_CHECKS, STALE_PATTERNS, GATE_RULE_PHRASES_1_5_2;
|
|
18302
18361
|
var init_doctorAgents = __esm({
|
|
18303
18362
|
"src/commands/doctorAgents.ts"() {
|
|
18304
18363
|
"use strict";
|
|
18305
|
-
|
|
18306
|
-
|
|
18364
|
+
import_promises26 = require("node:fs/promises");
|
|
18365
|
+
import_node_path34 = require("node:path");
|
|
18307
18366
|
init_commands();
|
|
18308
18367
|
init_agentTargets();
|
|
18309
18368
|
REQUIRED_EXISTENCE_CHECKS = [
|
|
@@ -18574,7 +18633,7 @@ function buildContextCleanupPlan(input) {
|
|
|
18574
18633
|
const items = [];
|
|
18575
18634
|
const seen = /* @__PURE__ */ new Set();
|
|
18576
18635
|
for (const file of input.files) {
|
|
18577
|
-
const path2 = normalizePath5((0,
|
|
18636
|
+
const path2 = normalizePath5((0, import_node_path35.relative)(input.projectRoot, file.path) || file.path);
|
|
18578
18637
|
const fullPath = normalizePath5(file.path);
|
|
18579
18638
|
const category = categorizeCleanupFile(path2, fullPath, file);
|
|
18580
18639
|
if (!category || seen.has(path2)) {
|
|
@@ -18600,10 +18659,10 @@ async function collectCleanupFiles(projectRoot) {
|
|
|
18600
18659
|
return files;
|
|
18601
18660
|
}
|
|
18602
18661
|
async function writeCleanupPlan(projectRoot, plan) {
|
|
18603
|
-
const outputDir = (0,
|
|
18604
|
-
const outputPath = (0,
|
|
18605
|
-
await (0,
|
|
18606
|
-
await (0,
|
|
18662
|
+
const outputDir = (0, import_node_path35.join)(projectRoot, ".viberaven");
|
|
18663
|
+
const outputPath = (0, import_node_path35.join)(outputDir, "context-cleanup.md");
|
|
18664
|
+
await (0, import_promises27.mkdir)(outputDir, { recursive: true });
|
|
18665
|
+
await (0, import_promises27.writeFile)(outputPath, renderCleanupPlan(plan), "utf8");
|
|
18607
18666
|
return outputPath;
|
|
18608
18667
|
}
|
|
18609
18668
|
function categorizeCleanupFile(relativePath, fullPath, file) {
|
|
@@ -18651,7 +18710,7 @@ async function walkCleanupFiles(root, dir, result, depth) {
|
|
|
18651
18710
|
}
|
|
18652
18711
|
let entries;
|
|
18653
18712
|
try {
|
|
18654
|
-
entries = await (0,
|
|
18713
|
+
entries = await (0, import_promises27.readdir)(dir, { withFileTypes: true });
|
|
18655
18714
|
} catch {
|
|
18656
18715
|
return;
|
|
18657
18716
|
}
|
|
@@ -18659,9 +18718,9 @@ async function walkCleanupFiles(root, dir, result, depth) {
|
|
|
18659
18718
|
if (SKIP_DIRECTORIES.has(entry.name)) {
|
|
18660
18719
|
continue;
|
|
18661
18720
|
}
|
|
18662
|
-
const absolute = (0,
|
|
18721
|
+
const absolute = (0, import_node_path35.join)(dir, entry.name);
|
|
18663
18722
|
if (entry.isDirectory()) {
|
|
18664
|
-
if (isCachePath(normalizePath5((0,
|
|
18723
|
+
if (isCachePath(normalizePath5((0, import_node_path35.relative)(root, absolute)))) {
|
|
18665
18724
|
result.push({ path: absolute, sizeBytes: 0, kind: "directory" });
|
|
18666
18725
|
continue;
|
|
18667
18726
|
}
|
|
@@ -18672,7 +18731,7 @@ async function walkCleanupFiles(root, dir, result, depth) {
|
|
|
18672
18731
|
continue;
|
|
18673
18732
|
}
|
|
18674
18733
|
try {
|
|
18675
|
-
const fileStat = await (0,
|
|
18734
|
+
const fileStat = await (0, import_promises27.stat)(absolute);
|
|
18676
18735
|
result.push({ path: absolute, sizeBytes: fileStat.size, kind: "file" });
|
|
18677
18736
|
} catch {
|
|
18678
18737
|
continue;
|
|
@@ -18682,12 +18741,12 @@ async function walkCleanupFiles(root, dir, result, depth) {
|
|
|
18682
18741
|
function normalizePath5(path2) {
|
|
18683
18742
|
return path2.replace(/\\/g, "/");
|
|
18684
18743
|
}
|
|
18685
|
-
var
|
|
18744
|
+
var import_promises27, import_node_path35, MAX_WALK_DEPTH, LARGE_LOG_BYTES, SKIP_DIRECTORIES;
|
|
18686
18745
|
var init_cleanPlan = __esm({
|
|
18687
18746
|
"src/commands/cleanPlan.ts"() {
|
|
18688
18747
|
"use strict";
|
|
18689
|
-
|
|
18690
|
-
|
|
18748
|
+
import_promises27 = require("node:fs/promises");
|
|
18749
|
+
import_node_path35 = require("node:path");
|
|
18691
18750
|
MAX_WALK_DEPTH = 5;
|
|
18692
18751
|
LARGE_LOG_BYTES = 8192;
|
|
18693
18752
|
SKIP_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
@@ -18807,10 +18866,10 @@ var init_render = __esm({
|
|
|
18807
18866
|
|
|
18808
18867
|
// src/commands/actions.ts
|
|
18809
18868
|
async function runActionsCommand(input) {
|
|
18810
|
-
const actionsPath = (0,
|
|
18869
|
+
const actionsPath = (0, import_node_path36.join)(getProjectArtifactsDir(input.cwd), "actions.json");
|
|
18811
18870
|
let raw;
|
|
18812
18871
|
try {
|
|
18813
|
-
raw = await (0,
|
|
18872
|
+
raw = await (0, import_promises28.readFile)(actionsPath, "utf8");
|
|
18814
18873
|
} catch {
|
|
18815
18874
|
process.stderr.write("No VibeRaven actions found. Run `npx -y viberaven --agent-mode` first.\n");
|
|
18816
18875
|
return 1;
|
|
@@ -18824,12 +18883,12 @@ async function runActionsCommand(input) {
|
|
|
18824
18883
|
process.stdout.write(renderActionSurface(manifest));
|
|
18825
18884
|
return 0;
|
|
18826
18885
|
}
|
|
18827
|
-
var
|
|
18886
|
+
var import_promises28, import_node_path36;
|
|
18828
18887
|
var init_actions = __esm({
|
|
18829
18888
|
"src/commands/actions.ts"() {
|
|
18830
18889
|
"use strict";
|
|
18831
|
-
|
|
18832
|
-
|
|
18890
|
+
import_promises28 = require("node:fs/promises");
|
|
18891
|
+
import_node_path36 = require("node:path");
|
|
18833
18892
|
init_render();
|
|
18834
18893
|
init_config();
|
|
18835
18894
|
}
|
|
@@ -18838,7 +18897,7 @@ var init_actions = __esm({
|
|
|
18838
18897
|
// src/commands/verifyAction.ts
|
|
18839
18898
|
async function currentActionExists(cwd, actionId) {
|
|
18840
18899
|
try {
|
|
18841
|
-
const raw = await (0,
|
|
18900
|
+
const raw = await (0, import_promises29.readFile)((0, import_node_path37.join)(getProjectArtifactsDir(cwd), "actions.json"), "utf8");
|
|
18842
18901
|
const manifest = JSON.parse(raw);
|
|
18843
18902
|
return manifest.actions.some((action) => action.id === actionId);
|
|
18844
18903
|
} catch {
|
|
@@ -18882,12 +18941,12 @@ This only looks the action up. Run \`npx -y viberaven --verify\` to scan the rep
|
|
|
18882
18941
|
);
|
|
18883
18942
|
return 0;
|
|
18884
18943
|
}
|
|
18885
|
-
var
|
|
18944
|
+
var import_promises29, import_node_path37;
|
|
18886
18945
|
var init_verifyAction = __esm({
|
|
18887
18946
|
"src/commands/verifyAction.ts"() {
|
|
18888
18947
|
"use strict";
|
|
18889
|
-
|
|
18890
|
-
|
|
18948
|
+
import_promises29 = require("node:fs/promises");
|
|
18949
|
+
import_node_path37 = require("node:path");
|
|
18891
18950
|
init_registry2();
|
|
18892
18951
|
init_config();
|
|
18893
18952
|
}
|
|
@@ -19111,19 +19170,19 @@ async function exportCommit(cwd, commit) {
|
|
|
19111
19170
|
throw new Error("Git snapshot exceeds the supported size limit (32 MB per file, 128 MB total). The comparison is incomplete.");
|
|
19112
19171
|
}
|
|
19113
19172
|
const blobs = await readBlobs(cwd, [...new Set(entries.map((entry) => entry.sha))]);
|
|
19114
|
-
const root = await (0,
|
|
19173
|
+
const root = await (0, import_promises30.mkdtemp)((0, import_node_path38.join)((0, import_node_os.tmpdir)(), "viberaven-base-"));
|
|
19115
19174
|
try {
|
|
19116
19175
|
for (const entry of entries) {
|
|
19117
|
-
const target = (0,
|
|
19118
|
-
if (!target.startsWith(root +
|
|
19176
|
+
const target = (0, import_node_path38.resolve)(root, ...entry.path.split("/"));
|
|
19177
|
+
if (!target.startsWith(root + import_node_path38.sep)) throw new Error("Invalid Git snapshot path");
|
|
19119
19178
|
const content = blobs.get(entry.sha);
|
|
19120
19179
|
if (!content || content.length !== entry.size) throw new Error("Git snapshot blob is missing or truncated");
|
|
19121
|
-
await (0,
|
|
19122
|
-
await (0,
|
|
19180
|
+
await (0, import_promises30.mkdir)((0, import_node_path38.dirname)(target), { recursive: true });
|
|
19181
|
+
await (0, import_promises30.writeFile)(target, content);
|
|
19123
19182
|
}
|
|
19124
19183
|
return root;
|
|
19125
19184
|
} catch (error) {
|
|
19126
|
-
await (0,
|
|
19185
|
+
await (0, import_promises30.rm)(root, { recursive: true, force: true, maxRetries: 3, retryDelay: 200 }).catch(() => void 0);
|
|
19127
19186
|
throw error;
|
|
19128
19187
|
}
|
|
19129
19188
|
}
|
|
@@ -19135,7 +19194,7 @@ async function checkChange(cwd, ref, head) {
|
|
|
19135
19194
|
if (!result.ok) throw new Error(`The check on ${ref} did not finish: ${result.message}`);
|
|
19136
19195
|
return compareFindings(result.artifact.gaps, head, ref, baseCommit);
|
|
19137
19196
|
} finally {
|
|
19138
|
-
await (0,
|
|
19197
|
+
await (0, import_promises30.rm)(folder, { recursive: true, force: true, maxRetries: 3, retryDelay: 200 }).catch(() => void 0);
|
|
19139
19198
|
}
|
|
19140
19199
|
}
|
|
19141
19200
|
function cell(text2) {
|
|
@@ -19192,14 +19251,14 @@ function renderCheckMarkdown({ head, change, version, score }) {
|
|
|
19192
19251
|
return `${lines.join("\n")}
|
|
19193
19252
|
`;
|
|
19194
19253
|
}
|
|
19195
|
-
var import_node_child_process7,
|
|
19254
|
+
var import_node_child_process7, import_promises30, import_node_os, import_node_path38, import_node_util5, execFileAsync5, MAX_BLOB_BYTES, MAX_SNAPSHOT_BYTES, SKIP_SEGMENTS, SEVERITY_RANK3, LOCATION_TEXT, SUBJECT, COMMENT_MARKER, DOT;
|
|
19196
19255
|
var init_checkChange = __esm({
|
|
19197
19256
|
"src/commands/checkChange.ts"() {
|
|
19198
19257
|
"use strict";
|
|
19199
19258
|
import_node_child_process7 = require("node:child_process");
|
|
19200
|
-
|
|
19259
|
+
import_promises30 = require("node:fs/promises");
|
|
19201
19260
|
import_node_os = require("node:os");
|
|
19202
|
-
|
|
19261
|
+
import_node_path38 = require("node:path");
|
|
19203
19262
|
import_node_util5 = require("node:util");
|
|
19204
19263
|
init_runScan();
|
|
19205
19264
|
init_safeGit();
|
|
@@ -19356,7 +19415,7 @@ ${message}
|
|
|
19356
19415
|
}
|
|
19357
19416
|
async function readUserFollowUps(actionsPath) {
|
|
19358
19417
|
try {
|
|
19359
|
-
return JSON.parse(await (0,
|
|
19418
|
+
return JSON.parse(await (0, import_promises31.readFile)(actionsPath, "utf8")).userFollowUps ?? [];
|
|
19360
19419
|
} catch {
|
|
19361
19420
|
return [];
|
|
19362
19421
|
}
|
|
@@ -19369,11 +19428,11 @@ async function printUserFollowUps(actionsPath) {
|
|
|
19369
19428
|
for (const step of followUp.userSteps) console.log(` - ${step} (${followUp.gapId ?? followUp.id})`);
|
|
19370
19429
|
}
|
|
19371
19430
|
}
|
|
19372
|
-
var
|
|
19431
|
+
var import_promises31, SEVERITY_ICON, SEVERITY_ORDER3, CHECK_NEXT_STEP;
|
|
19373
19432
|
var init_check = __esm({
|
|
19374
19433
|
"src/commands/check.ts"() {
|
|
19375
19434
|
"use strict";
|
|
19376
|
-
|
|
19435
|
+
import_promises31 = require("node:fs/promises");
|
|
19377
19436
|
init_runScan();
|
|
19378
19437
|
init_artifacts2();
|
|
19379
19438
|
init_statusLabels();
|
|
@@ -19400,18 +19459,18 @@ var init_check = __esm({
|
|
|
19400
19459
|
// src/hosted/credentials.ts
|
|
19401
19460
|
function defaultConfigDir(env = process.env, platform = process.platform) {
|
|
19402
19461
|
const override = env.VIBERAVEN_CONFIG_DIR?.trim();
|
|
19403
|
-
if (override) return (0,
|
|
19462
|
+
if (override) return (0, import_node_path39.resolve)(override);
|
|
19404
19463
|
if (platform === "win32") {
|
|
19405
|
-
return (0,
|
|
19464
|
+
return (0, import_node_path39.join)(env.APPDATA?.trim() || (0, import_node_path39.join)((0, import_node_os2.homedir)(), "AppData", "Roaming"), "viberaven");
|
|
19406
19465
|
}
|
|
19407
19466
|
if (platform === "darwin") {
|
|
19408
|
-
return (0,
|
|
19467
|
+
return (0, import_node_path39.join)((0, import_node_os2.homedir)(), "Library", "Application Support", "viberaven");
|
|
19409
19468
|
}
|
|
19410
|
-
return (0,
|
|
19469
|
+
return (0, import_node_path39.join)(env.XDG_CONFIG_HOME?.trim() || (0, import_node_path39.join)((0, import_node_os2.homedir)(), ".config"), "viberaven");
|
|
19411
19470
|
}
|
|
19412
19471
|
function isInside(child, parent) {
|
|
19413
|
-
const rel = (0,
|
|
19414
|
-
return rel === "" || !rel.startsWith("..") && !(0,
|
|
19472
|
+
const rel = (0, import_node_path39.relative)((0, import_node_path39.resolve)(parent), (0, import_node_path39.resolve)(child));
|
|
19473
|
+
return rel === "" || !rel.startsWith("..") && !(0, import_node_path39.isAbsolute)(rel);
|
|
19415
19474
|
}
|
|
19416
19475
|
async function retryTransient(work) {
|
|
19417
19476
|
for (let attempt = 1; ; attempt += 1) {
|
|
@@ -19424,15 +19483,15 @@ async function retryTransient(work) {
|
|
|
19424
19483
|
}
|
|
19425
19484
|
}
|
|
19426
19485
|
}
|
|
19427
|
-
var import_node_child_process8, import_node_crypto3,
|
|
19486
|
+
var import_node_child_process8, import_node_crypto3, import_promises32, import_node_os2, import_node_path39, CREDENTIALS_FILE_NAME, CredentialLocationError, CredentialPermissionError, restrictWithIcacls, TRANSIENT_FS_CODES, FS_ATTEMPTS, FS_BACKOFF_MS, CredentialStore;
|
|
19428
19487
|
var init_credentials = __esm({
|
|
19429
19488
|
"src/hosted/credentials.ts"() {
|
|
19430
19489
|
"use strict";
|
|
19431
19490
|
import_node_child_process8 = require("node:child_process");
|
|
19432
19491
|
import_node_crypto3 = require("node:crypto");
|
|
19433
|
-
|
|
19492
|
+
import_promises32 = require("node:fs/promises");
|
|
19434
19493
|
import_node_os2 = require("node:os");
|
|
19435
|
-
|
|
19494
|
+
import_node_path39 = require("node:path");
|
|
19436
19495
|
CREDENTIALS_FILE_NAME = "credentials.json";
|
|
19437
19496
|
CredentialLocationError = class extends Error {
|
|
19438
19497
|
};
|
|
@@ -19459,9 +19518,9 @@ var init_credentials = __esm({
|
|
|
19459
19518
|
constructor(configDir, workspacePath, options = {}) {
|
|
19460
19519
|
this.configDir = configDir;
|
|
19461
19520
|
this.workspacePath = workspacePath;
|
|
19462
|
-
this.path = (0,
|
|
19521
|
+
this.path = (0, import_node_path39.join)(configDir, CREDENTIALS_FILE_NAME);
|
|
19463
19522
|
this.restrict = options.restrict !== void 0 ? options.restrict : process.platform === "win32" ? restrictWithIcacls : null;
|
|
19464
|
-
this.fs = { readFile: (path2, encoding) => (0,
|
|
19523
|
+
this.fs = { readFile: (path2, encoding) => (0, import_promises32.readFile)(path2, encoding), rename: (from, to) => (0, import_promises32.rename)(from, to), rm: (path2) => (0, import_promises32.rm)(path2, { force: true }), ...options.fs };
|
|
19465
19524
|
}
|
|
19466
19525
|
path;
|
|
19467
19526
|
restrict;
|
|
@@ -19510,15 +19569,15 @@ var init_credentials = __esm({
|
|
|
19510
19569
|
`Refusing to store the VibeRaven sign-in inside this project (${this.configDir}). Set VIBERAVEN_CONFIG_DIR to a folder outside the repo.`
|
|
19511
19570
|
);
|
|
19512
19571
|
}
|
|
19513
|
-
await (0,
|
|
19572
|
+
await (0, import_promises32.mkdir)(this.configDir, { recursive: true, mode: 448 });
|
|
19514
19573
|
const temp = `${this.path}.${(0, import_node_crypto3.randomUUID)()}.tmp`;
|
|
19515
|
-
await (0,
|
|
19574
|
+
await (0, import_promises32.writeFile)(temp, `${JSON.stringify(body, null, 2)}
|
|
19516
19575
|
`, { encoding: "utf8", mode: 384 });
|
|
19517
|
-
await (0,
|
|
19576
|
+
await (0, import_promises32.chmod)(temp, 384).catch(() => void 0);
|
|
19518
19577
|
try {
|
|
19519
19578
|
if (this.restrict) await this.restrict(temp);
|
|
19520
19579
|
} catch (error) {
|
|
19521
|
-
await (0,
|
|
19580
|
+
await (0, import_promises32.rm)(temp, { force: true });
|
|
19522
19581
|
const code = error?.code;
|
|
19523
19582
|
const toolMissing = code === "ENOENT";
|
|
19524
19583
|
throw new CredentialPermissionError(
|
|
@@ -19529,17 +19588,17 @@ var init_credentials = __esm({
|
|
|
19529
19588
|
if (guard) {
|
|
19530
19589
|
const onDisk = await this.readFor(guard.apiBaseUrl);
|
|
19531
19590
|
if (onDisk?.accessToken !== guard.accessToken || guard.isCurrent && !guard.isCurrent()) {
|
|
19532
|
-
await (0,
|
|
19591
|
+
await (0, import_promises32.rm)(temp, { force: true });
|
|
19533
19592
|
return false;
|
|
19534
19593
|
}
|
|
19535
19594
|
}
|
|
19536
19595
|
try {
|
|
19537
19596
|
await retryTransient(() => this.fs.rename(temp, this.path));
|
|
19538
19597
|
} catch (error) {
|
|
19539
|
-
await (0,
|
|
19598
|
+
await (0, import_promises32.rm)(temp, { force: true });
|
|
19540
19599
|
throw error;
|
|
19541
19600
|
}
|
|
19542
|
-
await (0,
|
|
19601
|
+
await (0, import_promises32.chmod)(this.path, 384).catch(() => void 0);
|
|
19543
19602
|
return true;
|
|
19544
19603
|
}
|
|
19545
19604
|
/** Keeps the stored plan label in step with what the API last said. */
|
|
@@ -20566,7 +20625,7 @@ function buildMissionCards(artifact2, providers) {
|
|
|
20566
20625
|
};
|
|
20567
20626
|
}
|
|
20568
20627
|
function projectNameFromPath(pathValue) {
|
|
20569
|
-
return (0,
|
|
20628
|
+
return (0, import_node_path40.basename)(pathValue.replace(/[\\/]$/, "")) || "current repo";
|
|
20570
20629
|
}
|
|
20571
20630
|
function gapSearchText(gap) {
|
|
20572
20631
|
return [
|
|
@@ -20978,19 +21037,19 @@ function mapArtifactToLocalUiState(artifact2, presentProviderIds) {
|
|
|
20978
21037
|
selectedProviderId: selectedProvider.id,
|
|
20979
21038
|
currentPrompt: selectedProvider.nextFix?.prompt ?? "",
|
|
20980
21039
|
artifacts: {
|
|
20981
|
-
tasklist: (0,
|
|
20982
|
-
contextMap: (0,
|
|
20983
|
-
gateResult: (0,
|
|
20984
|
-
lastScan: (0,
|
|
21040
|
+
tasklist: (0, import_node_path40.join)(".viberaven", "agent-tasklist.md"),
|
|
21041
|
+
contextMap: (0, import_node_path40.join)(".viberaven", "context-map.json"),
|
|
21042
|
+
gateResult: (0, import_node_path40.join)(".viberaven", "gate-result.json"),
|
|
21043
|
+
lastScan: (0, import_node_path40.join)(".viberaven", "last-scan.json")
|
|
20985
21044
|
},
|
|
20986
21045
|
empty: false
|
|
20987
21046
|
};
|
|
20988
21047
|
}
|
|
20989
|
-
var
|
|
21048
|
+
var import_node_path40, GATE_LABEL, CONNECT_PROVIDER_PRIORITY, providerDashboardUrls, providerHints, PROVIDER_OWNED_GAP;
|
|
20990
21049
|
var init_artifactAdapter = __esm({
|
|
20991
21050
|
"src/local-ui/artifactAdapter.ts"() {
|
|
20992
21051
|
"use strict";
|
|
20993
|
-
|
|
21052
|
+
import_node_path40 = require("node:path");
|
|
20994
21053
|
init_providerLogos();
|
|
20995
21054
|
init_buildTaskList();
|
|
20996
21055
|
init_prompts();
|
|
@@ -21332,7 +21391,7 @@ async function buildHostedCheckPayload(workspacePath) {
|
|
|
21332
21391
|
throw new Error("Could not assemble the full check request.");
|
|
21333
21392
|
}
|
|
21334
21393
|
function finalizePayload(payload) {
|
|
21335
|
-
const folder = (0,
|
|
21394
|
+
const folder = (0, import_node_path41.basename)(payload.workspacePath.split("\\").join("/").replace(/\/+$/, "")) || "project";
|
|
21336
21395
|
const redacted = redactPayload({ ...payload, workspacePath: folder });
|
|
21337
21396
|
return { payload: redacted.payload, redactions: redacted.count };
|
|
21338
21397
|
}
|
|
@@ -21363,13 +21422,13 @@ function splitSections(prompt) {
|
|
|
21363
21422
|
chars: section.length
|
|
21364
21423
|
}));
|
|
21365
21424
|
}
|
|
21366
|
-
var
|
|
21425
|
+
var import_node_path41, PayloadCaptured;
|
|
21367
21426
|
var init_payload = __esm({
|
|
21368
21427
|
"src/hosted/payload.ts"() {
|
|
21369
21428
|
"use strict";
|
|
21370
21429
|
init_fileScanner();
|
|
21371
21430
|
init_orchestrator();
|
|
21372
|
-
|
|
21431
|
+
import_node_path41 = require("node:path");
|
|
21373
21432
|
init_config();
|
|
21374
21433
|
init_redact();
|
|
21375
21434
|
PayloadCaptured = class extends Error {
|
|
@@ -21452,13 +21511,13 @@ function readServerMessage(body) {
|
|
|
21452
21511
|
return body.slice(0, 300);
|
|
21453
21512
|
}
|
|
21454
21513
|
}
|
|
21455
|
-
var import_node_crypto4,
|
|
21514
|
+
var import_node_crypto4, import_promises33, import_node_path42, PRICING_URL, FREE_LIMIT_MESSAGE, PREVIEW_TTL_MS, STATUS_TIMEOUT_MS, STATUS_CACHE_MS, MAX_PREVIEWS, TRANSIENT_REASONS, SEVERITY_RANK4, isRecordValue, isFinding, FullCheckService;
|
|
21456
21515
|
var init_fullCheck = __esm({
|
|
21457
21516
|
"src/hosted/fullCheck.ts"() {
|
|
21458
21517
|
"use strict";
|
|
21459
21518
|
import_node_crypto4 = require("node:crypto");
|
|
21460
|
-
|
|
21461
|
-
|
|
21519
|
+
import_promises33 = require("node:fs/promises");
|
|
21520
|
+
import_node_path42 = require("node:path");
|
|
21462
21521
|
init_engines();
|
|
21463
21522
|
init_planLimits();
|
|
21464
21523
|
init_productionMapLanes();
|
|
@@ -21774,27 +21833,27 @@ var init_fullCheck = __esm({
|
|
|
21774
21833
|
*/
|
|
21775
21834
|
async latest() {
|
|
21776
21835
|
try {
|
|
21777
|
-
const saved = JSON.parse(await (0,
|
|
21836
|
+
const saved = JSON.parse(await (0, import_promises33.readFile)(this.latestPath(), "utf8"));
|
|
21778
21837
|
return isSavedResult(saved) ? saved : null;
|
|
21779
21838
|
} catch {
|
|
21780
21839
|
return null;
|
|
21781
21840
|
}
|
|
21782
21841
|
}
|
|
21783
21842
|
latestPath() {
|
|
21784
|
-
const key = (0, import_node_crypto4.createHash)("sha256").update(process.platform === "win32" ? (0,
|
|
21785
|
-
return (0,
|
|
21843
|
+
const key = (0, import_node_crypto4.createHash)("sha256").update(process.platform === "win32" ? (0, import_node_path42.resolve)(this.workspacePath).toLowerCase() : (0, import_node_path42.resolve)(this.workspacePath)).digest("hex").slice(0, 24);
|
|
21844
|
+
return (0, import_node_path42.join)(this.configDir, "full-checks", `${key}.json`);
|
|
21786
21845
|
}
|
|
21787
21846
|
/** Best effort: a result the user paid for is shown even if it cannot be written. */
|
|
21788
21847
|
async saveLatest(result) {
|
|
21789
21848
|
const path2 = this.latestPath();
|
|
21790
21849
|
const temp = `${path2}.${(0, import_node_crypto4.randomUUID)()}.tmp`;
|
|
21791
21850
|
try {
|
|
21792
|
-
await (0,
|
|
21793
|
-
await (0,
|
|
21851
|
+
await (0, import_promises33.mkdir)((0, import_node_path42.join)(this.configDir, "full-checks"), { recursive: true });
|
|
21852
|
+
await (0, import_promises33.writeFile)(temp, `${JSON.stringify(result, null, 2)}
|
|
21794
21853
|
`, "utf8");
|
|
21795
|
-
await (0,
|
|
21854
|
+
await (0, import_promises33.rename)(temp, path2);
|
|
21796
21855
|
} catch {
|
|
21797
|
-
await (0,
|
|
21856
|
+
await (0, import_promises33.unlink)(temp).catch(() => void 0);
|
|
21798
21857
|
}
|
|
21799
21858
|
}
|
|
21800
21859
|
signInView(pending) {
|
|
@@ -21954,12 +22013,12 @@ function publicRun(run) {
|
|
|
21954
22013
|
const { events: _events, ...rest } = run;
|
|
21955
22014
|
return rest;
|
|
21956
22015
|
}
|
|
21957
|
-
var
|
|
22016
|
+
var import_promises34, import_node_path43, import_node_crypto5, TERMINAL_STATES, FINISHED_STEP, MAX_EVENTS, MAX_TEXT, RunStore;
|
|
21958
22017
|
var init_runs = __esm({
|
|
21959
22018
|
"src/local-ui/actions/runs.ts"() {
|
|
21960
22019
|
"use strict";
|
|
21961
|
-
|
|
21962
|
-
|
|
22020
|
+
import_promises34 = require("node:fs/promises");
|
|
22021
|
+
import_node_path43 = require("node:path");
|
|
21963
22022
|
import_node_crypto5 = require("node:crypto");
|
|
21964
22023
|
TERMINAL_STATES = /* @__PURE__ */ new Set(["succeeded", "partially_completed", "failed", "cancelled", "interrupted"]);
|
|
21965
22024
|
FINISHED_STEP = /* @__PURE__ */ new Set(["done", "failed", "skipped", "unknown"]);
|
|
@@ -21981,13 +22040,13 @@ var init_runs = __esm({
|
|
|
21981
22040
|
async readAll() {
|
|
21982
22041
|
let names = [];
|
|
21983
22042
|
try {
|
|
21984
|
-
names = (await (0,
|
|
22043
|
+
names = (await (0, import_promises34.readdir)(this.dir)).filter((name) => /^run_[a-z0-9_]+\.json$/.test(name));
|
|
21985
22044
|
} catch {
|
|
21986
22045
|
return;
|
|
21987
22046
|
}
|
|
21988
22047
|
for (const name of names) {
|
|
21989
22048
|
try {
|
|
21990
|
-
const parsed = JSON.parse(await (0,
|
|
22049
|
+
const parsed = JSON.parse(await (0, import_promises34.readFile)((0, import_node_path43.join)(this.dir, name), "utf8"));
|
|
21991
22050
|
if (!parsed?.id || !Array.isArray(parsed.steps) || !Array.isArray(parsed.events)) continue;
|
|
21992
22051
|
this.runs.set(parsed.id, parsed);
|
|
21993
22052
|
if (!TERMINAL_STATES.has(parsed.state)) {
|
|
@@ -22113,11 +22172,11 @@ var init_runs = __esm({
|
|
|
22113
22172
|
const previous = this.writes.get(id2) ?? Promise.resolve();
|
|
22114
22173
|
const next = previous.then(async () => {
|
|
22115
22174
|
try {
|
|
22116
|
-
await (0,
|
|
22117
|
-
const target = (0,
|
|
22175
|
+
await (0, import_promises34.mkdir)(this.dir, { recursive: true });
|
|
22176
|
+
const target = (0, import_node_path43.join)(this.dir, `${id2}.json`);
|
|
22118
22177
|
const temp = `${target}.${process.pid}.tmp`;
|
|
22119
|
-
await (0,
|
|
22120
|
-
await (0,
|
|
22178
|
+
await (0, import_promises34.writeFile)(temp, JSON.stringify(run), "utf8");
|
|
22179
|
+
await (0, import_promises34.rename)(temp, target);
|
|
22121
22180
|
} catch {
|
|
22122
22181
|
}
|
|
22123
22182
|
});
|
|
@@ -22136,9 +22195,9 @@ function isSecretPath(path2) {
|
|
|
22136
22195
|
return SECRET_FILE.test(path2);
|
|
22137
22196
|
}
|
|
22138
22197
|
function inside(root, path2) {
|
|
22139
|
-
const base2 = (0,
|
|
22140
|
-
const target = (0,
|
|
22141
|
-
return target.startsWith(base2 +
|
|
22198
|
+
const base2 = (0, import_node_path44.resolve)(root);
|
|
22199
|
+
const target = (0, import_node_path44.resolve)(base2, path2);
|
|
22200
|
+
return target.startsWith(base2 + import_node_path44.sep) ? target : null;
|
|
22142
22201
|
}
|
|
22143
22202
|
function runGitProcess(cwd, invocation, timeoutMs) {
|
|
22144
22203
|
return new Promise((resolveRun) => {
|
|
@@ -22175,7 +22234,7 @@ function hashOf(content) {
|
|
|
22175
22234
|
}
|
|
22176
22235
|
async function readMaybe(path2) {
|
|
22177
22236
|
try {
|
|
22178
|
-
return await (0,
|
|
22237
|
+
return await (0, import_promises35.readFile)(path2);
|
|
22179
22238
|
} catch {
|
|
22180
22239
|
return null;
|
|
22181
22240
|
}
|
|
@@ -22216,7 +22275,7 @@ async function takeBaseline(cwd) {
|
|
|
22216
22275
|
const dirty = {};
|
|
22217
22276
|
let total = 0;
|
|
22218
22277
|
for (const [index, entry] of entries.entries()) {
|
|
22219
|
-
const content = await readMaybe((0,
|
|
22278
|
+
const content = await readMaybe((0, import_node_path44.join)(root, entry.path));
|
|
22220
22279
|
const secret = isSecretPath(entry.path);
|
|
22221
22280
|
const small = !secret && content !== null && content.length <= MAX_SNAPSHOT_BYTES2 && index < MAX_SNAPSHOT_FILES && total + content.length <= MAX_SNAPSHOT_TOTAL;
|
|
22222
22281
|
if (small) total += content.length;
|
|
@@ -22248,8 +22307,8 @@ async function checkoutForms(cwd, root, head, dirty, budget) {
|
|
|
22248
22307
|
const forms = {};
|
|
22249
22308
|
let scanned = 0;
|
|
22250
22309
|
const look = async ({ path: path2, id: id2 }) => {
|
|
22251
|
-
const target = (0,
|
|
22252
|
-
const size = await (0,
|
|
22310
|
+
const target = (0, import_node_path44.join)(root, path2);
|
|
22311
|
+
const size = await (0, import_promises35.stat)(target).then((info) => info.size, () => null);
|
|
22253
22312
|
if (size === null || scanned + size > MAX_CHECKOUT_SCAN) return;
|
|
22254
22313
|
scanned += size;
|
|
22255
22314
|
const content = await readMaybe(target);
|
|
@@ -22306,7 +22365,7 @@ async function observeChanges(baseline, cwd) {
|
|
|
22306
22365
|
const candidates = /* @__PURE__ */ new Set([...Object.keys(baseline.dirty), ...nowStatus.keys()]);
|
|
22307
22366
|
const changes = [];
|
|
22308
22367
|
for (const path2 of [...candidates].sort()) {
|
|
22309
|
-
const current = await readMaybe((0,
|
|
22368
|
+
const current = await readMaybe((0, import_node_path44.join)(baseline.root, path2));
|
|
22310
22369
|
const hash2 = current ? hashOf(current) : null;
|
|
22311
22370
|
const before = baseline.dirty[path2];
|
|
22312
22371
|
if (before && before.hash === hash2) continue;
|
|
@@ -22321,7 +22380,7 @@ async function observeChanges(baseline, cwd) {
|
|
|
22321
22380
|
}
|
|
22322
22381
|
async function diffChanges(baseline, paths) {
|
|
22323
22382
|
if (!baseline.root) return [];
|
|
22324
|
-
const scratch = await (0,
|
|
22383
|
+
const scratch = await (0, import_promises35.mkdtemp)((0, import_node_path44.join)((0, import_node_os3.tmpdir)(), "viberaven-diff-"));
|
|
22325
22384
|
try {
|
|
22326
22385
|
const out = [];
|
|
22327
22386
|
for (const [index, path2] of paths.slice(0, 60).entries()) {
|
|
@@ -22330,11 +22389,11 @@ async function diffChanges(baseline, paths) {
|
|
|
22330
22389
|
continue;
|
|
22331
22390
|
}
|
|
22332
22391
|
const before = await baseContent(baseline, path2);
|
|
22333
|
-
const after = await readMaybe((0,
|
|
22334
|
-
const left = (0,
|
|
22335
|
-
const right = (0,
|
|
22336
|
-
await (0,
|
|
22337
|
-
await (0,
|
|
22392
|
+
const after = await readMaybe((0, import_node_path44.join)(baseline.root, path2));
|
|
22393
|
+
const left = (0, import_node_path44.join)(scratch, `${index}-a`);
|
|
22394
|
+
const right = (0, import_node_path44.join)(scratch, `${index}-b`);
|
|
22395
|
+
await (0, import_promises35.writeFile)(left, before ?? Buffer.alloc(0));
|
|
22396
|
+
await (0, import_promises35.writeFile)(right, after ?? Buffer.alloc(0));
|
|
22338
22397
|
const result = await runGitProcess(scratch, await safeNoIndexDiffInvocation(scratch, ["--no-color", "-U3", "--", left, right]), 8e3);
|
|
22339
22398
|
const lines = result.stdout.toString("utf8").split("\n");
|
|
22340
22399
|
const body = lines.slice(lines.findIndex((line) => line.startsWith("@@")));
|
|
@@ -22346,7 +22405,7 @@ ${body.join("\n")}`.trimEnd() });
|
|
|
22346
22405
|
}
|
|
22347
22406
|
return out;
|
|
22348
22407
|
} finally {
|
|
22349
|
-
await (0,
|
|
22408
|
+
await (0, import_promises35.rm)(scratch, { recursive: true, force: true }).catch(() => void 0);
|
|
22350
22409
|
}
|
|
22351
22410
|
}
|
|
22352
22411
|
function replayWrites(base2, writes) {
|
|
@@ -22375,9 +22434,9 @@ function pathKey(path2) {
|
|
|
22375
22434
|
return process.platform === "win32" ? path2.toLowerCase() : path2;
|
|
22376
22435
|
}
|
|
22377
22436
|
function repoPath(root, cwd, path2) {
|
|
22378
|
-
const rel = (0,
|
|
22379
|
-
if (!rel || rel.startsWith("..") || (0,
|
|
22380
|
-
return rel.split(
|
|
22437
|
+
const rel = (0, import_node_path44.relative)((0, import_node_path44.resolve)(root), (0, import_node_path44.resolve)(cwd, path2));
|
|
22438
|
+
if (!rel || rel.startsWith("..") || (0, import_node_path44.resolve)(rel) === rel) return null;
|
|
22439
|
+
return rel.split(import_node_path44.sep).join("/");
|
|
22381
22440
|
}
|
|
22382
22441
|
async function attributeChanges(baseline, cwd, changes, writes) {
|
|
22383
22442
|
const owned = {};
|
|
@@ -22392,7 +22451,7 @@ async function attributeChanges(baseline, cwd, changes, writes) {
|
|
|
22392
22451
|
for (const change of changes) {
|
|
22393
22452
|
const mine = byPath.get(pathKey(change.path));
|
|
22394
22453
|
const base2 = mine?.length && !isSecretPath(change.path) ? await baseContent(baseline, change.path) : void 0;
|
|
22395
|
-
const current = base2 !== void 0 ? await readMaybe((0,
|
|
22454
|
+
const current = base2 !== void 0 ? await readMaybe((0, import_node_path44.join)(baseline.root, change.path)) : null;
|
|
22396
22455
|
const expected = base2 !== void 0 && mine ? replayWrites(base2 === null ? null : base2.toString("utf8"), mine) : void 0;
|
|
22397
22456
|
if (current !== null && expected !== void 0 && lf(current.toString("utf8")) === expected) owned[change.path] = hashOf(current);
|
|
22398
22457
|
else unattributed.push(change.path);
|
|
@@ -22409,10 +22468,10 @@ async function putBack(baseline, path2, target, current, keep) {
|
|
|
22409
22468
|
}
|
|
22410
22469
|
try {
|
|
22411
22470
|
if (before === null) {
|
|
22412
|
-
if (current !== null) await (0,
|
|
22471
|
+
if (current !== null) await (0, import_promises35.unlink)(target);
|
|
22413
22472
|
} else {
|
|
22414
|
-
await (0,
|
|
22415
|
-
await (0,
|
|
22473
|
+
await (0, import_promises35.mkdir)((0, import_node_path44.dirname)(target), { recursive: true });
|
|
22474
|
+
await (0, import_promises35.writeFile)(target, before);
|
|
22416
22475
|
}
|
|
22417
22476
|
return null;
|
|
22418
22477
|
} catch (error) {
|
|
@@ -22462,41 +22521,41 @@ async function restoreFile(baseline, path2, keep) {
|
|
|
22462
22521
|
return failure ? { error: failure } : { ok: true };
|
|
22463
22522
|
}
|
|
22464
22523
|
function backupsPath(runsDir, runId) {
|
|
22465
|
-
return (0,
|
|
22524
|
+
return (0, import_node_path44.join)(runsDir, `${runId}.backups.json`);
|
|
22466
22525
|
}
|
|
22467
22526
|
async function saveBackup(runsDir, runId, backup) {
|
|
22468
|
-
await (0,
|
|
22527
|
+
await (0, import_promises35.mkdir)(runsDir, { recursive: true });
|
|
22469
22528
|
let list = [];
|
|
22470
22529
|
try {
|
|
22471
|
-
list = JSON.parse(await (0,
|
|
22530
|
+
list = JSON.parse(await (0, import_promises35.readFile)(backupsPath(runsDir, runId), "utf8"));
|
|
22472
22531
|
} catch {
|
|
22473
22532
|
}
|
|
22474
22533
|
list.push(backup);
|
|
22475
|
-
await (0,
|
|
22534
|
+
await (0, import_promises35.writeFile)(backupsPath(runsDir, runId), JSON.stringify(list), "utf8");
|
|
22476
22535
|
}
|
|
22477
22536
|
function baselinePath(runsDir, runId) {
|
|
22478
|
-
return (0,
|
|
22537
|
+
return (0, import_node_path44.join)(runsDir, `${runId}.baseline.json`);
|
|
22479
22538
|
}
|
|
22480
22539
|
async function saveBaseline(runsDir, runId, baseline) {
|
|
22481
|
-
await (0,
|
|
22482
|
-
await (0,
|
|
22540
|
+
await (0, import_promises35.mkdir)(runsDir, { recursive: true });
|
|
22541
|
+
await (0, import_promises35.writeFile)(baselinePath(runsDir, runId), JSON.stringify(baseline), "utf8");
|
|
22483
22542
|
}
|
|
22484
22543
|
async function loadBaseline(runsDir, runId) {
|
|
22485
22544
|
try {
|
|
22486
|
-
return JSON.parse(await (0,
|
|
22545
|
+
return JSON.parse(await (0, import_promises35.readFile)(baselinePath(runsDir, runId), "utf8"));
|
|
22487
22546
|
} catch {
|
|
22488
22547
|
return null;
|
|
22489
22548
|
}
|
|
22490
22549
|
}
|
|
22491
|
-
var import_node_child_process9, import_node_crypto6,
|
|
22550
|
+
var import_node_child_process9, import_node_crypto6, import_promises35, import_node_os3, import_node_path44, STATUS_PATHSPEC, MAX_SNAPSHOT_FILES, MAX_SNAPSHOT_BYTES2, MAX_SNAPSHOT_TOTAL, MAX_CHECKOUT_SCAN, CHECKOUT_READS_AT_ONCE, SECRET_FILE, withCrlf, withoutCrlf, lf, NOT_PROVEN;
|
|
22492
22551
|
var init_workspace = __esm({
|
|
22493
22552
|
"src/local-ui/actions/workspace.ts"() {
|
|
22494
22553
|
"use strict";
|
|
22495
22554
|
import_node_child_process9 = require("node:child_process");
|
|
22496
22555
|
import_node_crypto6 = require("node:crypto");
|
|
22497
|
-
|
|
22556
|
+
import_promises35 = require("node:fs/promises");
|
|
22498
22557
|
import_node_os3 = require("node:os");
|
|
22499
|
-
|
|
22558
|
+
import_node_path44 = require("node:path");
|
|
22500
22559
|
init_safeGit();
|
|
22501
22560
|
STATUS_PATHSPEC = ["--", ".", ":(exclude).viberaven", ":(exclude)**/.viberaven/**"];
|
|
22502
22561
|
MAX_SNAPSHOT_FILES = 300;
|
|
@@ -22519,14 +22578,14 @@ function reloadsItself(script) {
|
|
|
22519
22578
|
async function detectPreviewCommand(cwd) {
|
|
22520
22579
|
let pkg;
|
|
22521
22580
|
try {
|
|
22522
|
-
pkg = JSON.parse(await (0,
|
|
22581
|
+
pkg = JSON.parse(await (0, import_promises36.readFile)((0, import_node_path45.join)(cwd, "package.json"), "utf8"));
|
|
22523
22582
|
} catch {
|
|
22524
22583
|
return null;
|
|
22525
22584
|
}
|
|
22526
22585
|
const scripts = pkg.scripts ?? {};
|
|
22527
22586
|
const script = ["dev", "start", "preview"].find((name) => typeof scripts[name] === "string" && scripts[name].trim());
|
|
22528
22587
|
if (!script) return null;
|
|
22529
|
-
const exists2 = async (name) => (0,
|
|
22588
|
+
const exists2 = async (name) => (0, import_promises36.stat)((0, import_node_path45.join)(cwd, name)).then(() => true, () => false);
|
|
22530
22589
|
const packageManager = await exists2("pnpm-lock.yaml") ? "pnpm" : await exists2("yarn.lock") ? "yarn" : await exists2("bun.lockb") || await exists2("bun.lock") ? "bun" : "npm";
|
|
22531
22590
|
const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
22532
22591
|
const framework = FRAMEWORKS.find(([dep]) => dep in deps)?.[1] ?? null;
|
|
@@ -22574,14 +22633,14 @@ function previewFailureReason(log) {
|
|
|
22574
22633
|
if (/Missing script/i.test(log)) return "The script VibeRaven picked is not in package.json.";
|
|
22575
22634
|
return void 0;
|
|
22576
22635
|
}
|
|
22577
|
-
var import_node_child_process10,
|
|
22636
|
+
var import_node_child_process10, import_promises36, import_node_net, import_node_path45, URL_PATTERN, READY_TIMEOUT_MS, LOG_LIMIT, FRAMEWORKS, RELOADS_ITSELF, SEVERAL_PROCESSES, PreviewManager;
|
|
22578
22637
|
var init_preview2 = __esm({
|
|
22579
22638
|
"src/local-ui/preview.ts"() {
|
|
22580
22639
|
"use strict";
|
|
22581
22640
|
import_node_child_process10 = require("node:child_process");
|
|
22582
|
-
|
|
22641
|
+
import_promises36 = require("node:fs/promises");
|
|
22583
22642
|
import_node_net = require("node:net");
|
|
22584
|
-
|
|
22643
|
+
import_node_path45 = require("node:path");
|
|
22585
22644
|
init_runs();
|
|
22586
22645
|
URL_PATTERN = /https?:\/\/(?:localhost|127\.0\.0\.1|\[::1\]|0\.0\.0\.0):(\d{2,5})(?:\/[^\s'"`]*)?/i;
|
|
22587
22646
|
READY_TIMEOUT_MS = 12e4;
|
|
@@ -22767,21 +22826,21 @@ var init_preview2 = __esm({
|
|
|
22767
22826
|
|
|
22768
22827
|
// src/local-ui/actions/engine.ts
|
|
22769
22828
|
async function exists(path2) {
|
|
22770
|
-
return (0,
|
|
22829
|
+
return (0, import_promises37.stat)(path2).then(() => true, () => false);
|
|
22771
22830
|
}
|
|
22772
22831
|
async function detectChecks(cwd) {
|
|
22773
22832
|
let pkg;
|
|
22774
22833
|
try {
|
|
22775
|
-
pkg = JSON.parse(await (0,
|
|
22834
|
+
pkg = JSON.parse(await (0, import_promises37.readFile)((0, import_node_path46.join)(cwd, "package.json"), "utf8"));
|
|
22776
22835
|
} catch {
|
|
22777
22836
|
return [];
|
|
22778
22837
|
}
|
|
22779
22838
|
const scripts = pkg.scripts ?? {};
|
|
22780
|
-
const pm = await exists((0,
|
|
22839
|
+
const pm = await exists((0, import_node_path46.join)(cwd, "pnpm-lock.yaml")) ? "pnpm" : await exists((0, import_node_path46.join)(cwd, "yarn.lock")) ? "yarn" : "npm";
|
|
22781
22840
|
const checks = [];
|
|
22782
22841
|
if (scripts.typecheck) checks.push({ id: "typecheck", label: "Types", display: `${pm} run typecheck`, command: `${pm} run typecheck` });
|
|
22783
|
-
else if (await exists((0,
|
|
22784
|
-
checks.push({ id: "typecheck", label: "Types", display: "tsc --noEmit", command: `node "${(0,
|
|
22842
|
+
else if (await exists((0, import_node_path46.join)(cwd, "tsconfig.json")) && await exists((0, import_node_path46.join)(cwd, "node_modules", "typescript", "bin", "tsc"))) {
|
|
22843
|
+
checks.push({ id: "typecheck", label: "Types", display: "tsc --noEmit", command: `node "${(0, import_node_path46.join)("node_modules", "typescript", "bin", "tsc")}" --noEmit --incremental false -p .` });
|
|
22785
22844
|
}
|
|
22786
22845
|
if (scripts.test && !NPM_DEFAULT_TEST.test(scripts.test)) checks.push({ id: "test", label: "Tests", display: `${pm} run test`, command: `${pm} run test`, env: { CI: "1" } });
|
|
22787
22846
|
return checks;
|
|
@@ -22852,14 +22911,14 @@ function envKeys(text2) {
|
|
|
22852
22911
|
async function missingClientSettings(cwd) {
|
|
22853
22912
|
let example = "";
|
|
22854
22913
|
try {
|
|
22855
|
-
example = await (0,
|
|
22914
|
+
example = await (0, import_promises37.readFile)((0, import_node_path46.join)(cwd, ".env.example"), "utf8");
|
|
22856
22915
|
} catch {
|
|
22857
22916
|
return [];
|
|
22858
22917
|
}
|
|
22859
22918
|
const present = new Set(Object.keys(process.env));
|
|
22860
22919
|
for (const name of [".env", ".env.local", ".env.development", ".env.development.local"]) {
|
|
22861
22920
|
try {
|
|
22862
|
-
for (const key of envKeys(await (0,
|
|
22921
|
+
for (const key of envKeys(await (0, import_promises37.readFile)((0, import_node_path46.join)(cwd, name), "utf8"))) present.add(key);
|
|
22863
22922
|
} catch {
|
|
22864
22923
|
}
|
|
22865
22924
|
}
|
|
@@ -22869,15 +22928,15 @@ async function isGitWorkTree(cwd) {
|
|
|
22869
22928
|
const result = await safeGit(cwd, ["rev-parse", "--is-inside-work-tree"], 4e3);
|
|
22870
22929
|
return result.code === 0 && result.stdout.toString("utf8").trim() === "true";
|
|
22871
22930
|
}
|
|
22872
|
-
var import_node_child_process11, import_node_crypto7,
|
|
22931
|
+
var import_node_child_process11, import_node_crypto7, import_promises37, import_node_path46, CHECK_TIMEOUT_MS, NPM_DEFAULT_TEST, MAX_REQUEST, ActionEngine;
|
|
22873
22932
|
var init_engine = __esm({
|
|
22874
22933
|
"src/local-ui/actions/engine.ts"() {
|
|
22875
22934
|
"use strict";
|
|
22876
22935
|
init_taskContract();
|
|
22877
22936
|
import_node_child_process11 = require("node:child_process");
|
|
22878
22937
|
import_node_crypto7 = require("node:crypto");
|
|
22879
|
-
|
|
22880
|
-
|
|
22938
|
+
import_promises37 = require("node:fs/promises");
|
|
22939
|
+
import_node_path46 = require("node:path");
|
|
22881
22940
|
init_catalog();
|
|
22882
22941
|
init_runs();
|
|
22883
22942
|
init_workspace();
|
|
@@ -22899,7 +22958,7 @@ var init_engine = __esm({
|
|
|
22899
22958
|
const [command, checks, hasPackageJson, git3] = await Promise.all([
|
|
22900
22959
|
detectPreviewCommand(cwd),
|
|
22901
22960
|
detectChecks(cwd),
|
|
22902
|
-
exists((0,
|
|
22961
|
+
exists((0, import_node_path46.join)(cwd, "package.json")),
|
|
22903
22962
|
isGitWorkTree(cwd)
|
|
22904
22963
|
]);
|
|
22905
22964
|
return {
|
|
@@ -23418,10 +23477,10 @@ async function connectProviderMcp(cwd, providerId) {
|
|
|
23418
23477
|
if (!recipe) {
|
|
23419
23478
|
return { ok: false, message: `No known MCP server for ${providerId} yet. Add one manually to .mcp.json.` };
|
|
23420
23479
|
}
|
|
23421
|
-
const configPath = (0,
|
|
23480
|
+
const configPath = (0, import_node_path47.join)(cwd, ".mcp.json");
|
|
23422
23481
|
let config = {};
|
|
23423
23482
|
try {
|
|
23424
|
-
const raw = await (0,
|
|
23483
|
+
const raw = await (0, import_promises38.readFile)(configPath, "utf8");
|
|
23425
23484
|
const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
|
|
23426
23485
|
if (!isRecord5(parsed) || parsed.mcpServers !== void 0 && !isRecord5(parsed.mcpServers)) {
|
|
23427
23486
|
return { ok: false, message: "Existing .mcp.json has an unsupported structure. It was left unchanged." };
|
|
@@ -23438,7 +23497,7 @@ async function connectProviderMcp(cwd, providerId) {
|
|
|
23438
23497
|
}
|
|
23439
23498
|
servers[providerId] = recipe.entry;
|
|
23440
23499
|
const next = { ...config, mcpServers: servers };
|
|
23441
|
-
await (0,
|
|
23500
|
+
await (0, import_promises38.writeFile)(configPath, `${JSON.stringify(next, null, 2)}
|
|
23442
23501
|
`, "utf8");
|
|
23443
23502
|
const suffix = recipe.note ? ` ${recipe.note}` : "";
|
|
23444
23503
|
return { ok: true, message: `Added ${providerId} MCP server to .mcp.json.${suffix}`, path: ".mcp.json" };
|
|
@@ -23491,7 +23550,7 @@ async function defaultScanRunner(flags, positional) {
|
|
|
23491
23550
|
}
|
|
23492
23551
|
async function readPackageJson(cwd) {
|
|
23493
23552
|
try {
|
|
23494
|
-
const raw = await (0,
|
|
23553
|
+
const raw = await (0, import_promises38.readFile)((0, import_node_path47.join)(cwd, "package.json"), "utf8");
|
|
23495
23554
|
const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
|
|
23496
23555
|
return isRecord5(parsed) ? parsed : {};
|
|
23497
23556
|
} catch {
|
|
@@ -23514,7 +23573,7 @@ async function listProjectFiles(cwd, maxFiles = 1800) {
|
|
|
23514
23573
|
if (files.length >= maxFiles) return;
|
|
23515
23574
|
let entries;
|
|
23516
23575
|
try {
|
|
23517
|
-
entries = await (0,
|
|
23576
|
+
entries = await (0, import_promises38.readdir)(dir, { withFileTypes: true });
|
|
23518
23577
|
} catch {
|
|
23519
23578
|
return;
|
|
23520
23579
|
}
|
|
@@ -23522,7 +23581,7 @@ async function listProjectFiles(cwd, maxFiles = 1800) {
|
|
|
23522
23581
|
if (files.length >= maxFiles) return;
|
|
23523
23582
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
23524
23583
|
if (entry.isDirectory()) {
|
|
23525
|
-
if (!skipped.has(entry.name)) await walk((0,
|
|
23584
|
+
if (!skipped.has(entry.name)) await walk((0, import_node_path47.join)(dir, entry.name), rel);
|
|
23526
23585
|
continue;
|
|
23527
23586
|
}
|
|
23528
23587
|
if (entry.isFile()) files.push(rel.toLowerCase());
|
|
@@ -23541,7 +23600,7 @@ async function readEnvKeys(cwd) {
|
|
|
23541
23600
|
const keys2 = /* @__PURE__ */ new Set();
|
|
23542
23601
|
for (const name of [".env.example", ".env"]) {
|
|
23543
23602
|
try {
|
|
23544
|
-
const raw = await (0,
|
|
23603
|
+
const raw = await (0, import_promises38.readFile)((0, import_node_path47.join)(cwd, name), "utf8");
|
|
23545
23604
|
for (const line of raw.split(/\r?\n/)) {
|
|
23546
23605
|
const trimmed = line.trim();
|
|
23547
23606
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -23859,7 +23918,7 @@ async function codexModelOptions() {
|
|
|
23859
23918
|
{ id: "gpt-5.4-mini", label: "GPT-5.4 Mini" }
|
|
23860
23919
|
];
|
|
23861
23920
|
try {
|
|
23862
|
-
const raw = await (0,
|
|
23921
|
+
const raw = await (0, import_promises38.readFile)((0, import_node_path47.join)((0, import_node_os4.homedir)(), ".codex", "models_cache.json"), "utf8");
|
|
23863
23922
|
const parsed = JSON.parse(raw);
|
|
23864
23923
|
const list = Array.isArray(parsed) ? parsed : parsed.models ?? [];
|
|
23865
23924
|
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 }));
|
|
@@ -24062,19 +24121,19 @@ function isPlaceholderProjectName(value) {
|
|
|
24062
24121
|
async function readMcpEvidence(cwd) {
|
|
24063
24122
|
const candidates = MCP_CONFIG_PATHS.map((relativePath) => ({
|
|
24064
24123
|
source: relativePath.replace(/\\/g, "/"),
|
|
24065
|
-
path: (0,
|
|
24124
|
+
path: (0, import_node_path47.resolve)(cwd, relativePath)
|
|
24066
24125
|
}));
|
|
24067
24126
|
const home = process.env.USERPROFILE || process.env.HOME;
|
|
24068
24127
|
if (home) {
|
|
24069
24128
|
candidates.push({
|
|
24070
24129
|
source: "~/.codex/config.toml",
|
|
24071
|
-
path: (0,
|
|
24130
|
+
path: (0, import_node_path47.resolve)(home, ".codex", "config.toml")
|
|
24072
24131
|
});
|
|
24073
24132
|
}
|
|
24074
24133
|
const evidence = [];
|
|
24075
24134
|
for (const candidate of candidates) {
|
|
24076
24135
|
try {
|
|
24077
|
-
const text2 = await (0,
|
|
24136
|
+
const text2 = await (0, import_promises38.readFile)(candidate.path, "utf8");
|
|
24078
24137
|
if (/mcp/i.test(text2)) evidence.push({ source: candidate.source, text: text2 });
|
|
24079
24138
|
} catch {
|
|
24080
24139
|
}
|
|
@@ -24165,7 +24224,7 @@ async function enrichProjectMetadata(state, cwd) {
|
|
|
24165
24224
|
async function readState(cwd) {
|
|
24166
24225
|
let state;
|
|
24167
24226
|
try {
|
|
24168
|
-
const raw = await (0,
|
|
24227
|
+
const raw = await (0, import_promises38.readFile)((0, import_node_path47.join)(cwd, ".viberaven", "last-scan.json"), "utf8");
|
|
24169
24228
|
const packageJson = await readPackageJson(cwd);
|
|
24170
24229
|
const deps = dependencyNames(packageJson);
|
|
24171
24230
|
const files = await listProjectFiles(cwd);
|
|
@@ -24433,7 +24492,7 @@ async function whereExe(command, cwd) {
|
|
|
24433
24492
|
}
|
|
24434
24493
|
function expandShimPath(value, base2) {
|
|
24435
24494
|
const relative11 = value.replace(/^%(?:dp0%\\+|~dp0\\*)/i, "");
|
|
24436
|
-
return relative11 === value ? value : (0,
|
|
24495
|
+
return relative11 === value ? value : (0, import_node_path47.join)(base2, ...relative11.split(/[\\/]+/));
|
|
24437
24496
|
}
|
|
24438
24497
|
function findShimTarget(lines, pattern, accept = () => true) {
|
|
24439
24498
|
for (const line of lines) {
|
|
@@ -24444,21 +24503,21 @@ function findShimTarget(lines, pattern, accept = () => true) {
|
|
|
24444
24503
|
}
|
|
24445
24504
|
async function resolveWindowsShimTarget(shimPath) {
|
|
24446
24505
|
try {
|
|
24447
|
-
const content = await (0,
|
|
24448
|
-
const base2 = (0,
|
|
24506
|
+
const content = await (0, import_promises38.readFile)(shimPath, "utf8");
|
|
24507
|
+
const base2 = (0, import_node_path47.dirname)(shimPath);
|
|
24449
24508
|
const lines = content.split(/\r?\n/);
|
|
24450
24509
|
const trailingArgs = (line, after) => [...line.slice(after).split("%*")[0].matchAll(/"([^"]*)"|([^\s"&|<>]+)/g)].map((token) => expandShimPath(token[1] ?? token[2], base2)).filter((value) => value.length > 0 && !value.includes("%"));
|
|
24451
24510
|
const script = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.js)"/i);
|
|
24452
24511
|
if (script) {
|
|
24453
24512
|
return {
|
|
24454
24513
|
command: process.execPath,
|
|
24455
|
-
args: [(0,
|
|
24514
|
+
args: [(0, import_node_path47.join)(base2, ...script.match[1].split(/[\\/]+/)), ...trailingArgs(script.line, script.match.index + script.match[0].length)]
|
|
24456
24515
|
};
|
|
24457
24516
|
}
|
|
24458
|
-
const binary = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.exe)"/i, (target) => (0,
|
|
24517
|
+
const binary = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.exe)"/i, (target) => (0, import_node_path47.basename)(target).toLowerCase() !== "node.exe");
|
|
24459
24518
|
if (binary) {
|
|
24460
24519
|
return {
|
|
24461
|
-
command: (0,
|
|
24520
|
+
command: (0, import_node_path47.join)(base2, ...binary.match[1].split(/[\\/]+/)),
|
|
24462
24521
|
args: trailingArgs(binary.line, binary.match.index + binary.match[0].length)
|
|
24463
24522
|
};
|
|
24464
24523
|
}
|
|
@@ -24583,8 +24642,8 @@ async function writeChatContextArtifact(cwd, request, prompt) {
|
|
|
24583
24642
|
"",
|
|
24584
24643
|
prompt
|
|
24585
24644
|
].filter((line) => typeof line === "string");
|
|
24586
|
-
await (0,
|
|
24587
|
-
await (0,
|
|
24645
|
+
await (0, import_promises38.mkdir)((0, import_node_path47.join)(cwd, ".viberaven"), { recursive: true });
|
|
24646
|
+
await (0, import_promises38.writeFile)((0, import_node_path47.join)(cwd, ".viberaven", "chat-context.md"), `${lines.join("\n")}
|
|
24588
24647
|
`, "utf8");
|
|
24589
24648
|
} catch {
|
|
24590
24649
|
}
|
|
@@ -24607,13 +24666,13 @@ async function runAgentChat(cwd, request) {
|
|
|
24607
24666
|
return executeAgentPrompt(cwd, request, prompt);
|
|
24608
24667
|
}
|
|
24609
24668
|
async function executeAgentPrompt(cwd, request, prompt, options = {}) {
|
|
24610
|
-
const outputPath = request.cliId === "codex" ? (0,
|
|
24611
|
-
if (outputPath) await (0,
|
|
24669
|
+
const outputPath = request.cliId === "codex" ? (0, import_node_path47.join)(cwd, ".viberaven", `studio-agent-${Date.now()}-${Math.random().toString(36).slice(2)}.md`) : void 0;
|
|
24670
|
+
if (outputPath) await (0, import_promises38.mkdir)((0, import_node_path47.dirname)(outputPath), { recursive: true });
|
|
24612
24671
|
const { command, args, stdin } = commandForAgentChat(request, prompt, cwd, outputPath, options);
|
|
24613
24672
|
const result = await runAgentCommand(command, args, cwd, stdin, request.runId, options.editRecord ? 16e6 : 48e3);
|
|
24614
24673
|
if (outputPath) {
|
|
24615
24674
|
try {
|
|
24616
|
-
const finalMessage = (await (0,
|
|
24675
|
+
const finalMessage = (await (0, import_promises38.readFile)(outputPath, "utf8")).trim();
|
|
24617
24676
|
if (finalMessage) return { ...result, output: finalMessage };
|
|
24618
24677
|
} catch {
|
|
24619
24678
|
}
|
|
@@ -24638,8 +24697,8 @@ function firstGapId(state) {
|
|
|
24638
24697
|
return state.providers.find((provider2) => provider2.nextFix?.id)?.nextFix?.id;
|
|
24639
24698
|
}
|
|
24640
24699
|
async function runLocalAction(cwd, intent) {
|
|
24641
|
-
const artifactDir = (0,
|
|
24642
|
-
await (0,
|
|
24700
|
+
const artifactDir = (0, import_node_path47.join)(cwd, ".viberaven");
|
|
24701
|
+
await (0, import_promises38.mkdir)(artifactDir, { recursive: true });
|
|
24643
24702
|
if (intent === "preview_rehearsal") {
|
|
24644
24703
|
const state2 = await readState(cwd);
|
|
24645
24704
|
const topProvider = state2.providers[0];
|
|
@@ -24680,7 +24739,7 @@ async function runLocalAction(cwd, intent) {
|
|
|
24680
24739
|
proof2,
|
|
24681
24740
|
""
|
|
24682
24741
|
].join("\n");
|
|
24683
|
-
await (0,
|
|
24742
|
+
await (0, import_promises38.writeFile)((0, import_node_path47.join)(artifactDir, "preview-rehearsal.md"), markdown, "utf8");
|
|
24684
24743
|
return {
|
|
24685
24744
|
title: "Preview rehearsal ready",
|
|
24686
24745
|
summary: `${projectName}: ${gateLabel} with ${state2.providers.length} provider beat${state2.providers.length === 1 ? "" : "s"}`,
|
|
@@ -24690,7 +24749,7 @@ async function runLocalAction(cwd, intent) {
|
|
|
24690
24749
|
if (intent === "audit_vercel_supabase") {
|
|
24691
24750
|
const auditInput = await collectVercelSupabaseAuditInput(cwd);
|
|
24692
24751
|
const result2 = buildVercelSupabaseAudit(auditInput);
|
|
24693
|
-
await (0,
|
|
24752
|
+
await (0, import_promises38.writeFile)((0, import_node_path47.join)(artifactDir, "audit-vercel-supabase.txt"), `${renderVercelSupabaseAudit(result2)}
|
|
24694
24753
|
`, "utf8");
|
|
24695
24754
|
return {
|
|
24696
24755
|
title: result2.status === "pass" ? "Audit passed" : "Audit needs work",
|
|
@@ -24718,7 +24777,7 @@ async function runLocalAction(cwd, intent) {
|
|
|
24718
24777
|
const mode = intent === "heal_prompt" ? "prompt" : intent === "heal_apply" ? "apply" : "plan";
|
|
24719
24778
|
const result = await runHealCommand({ cwd, mode, gapId, yes: mode === "apply", noVerify: true });
|
|
24720
24779
|
if (mode === "apply") {
|
|
24721
|
-
await (0,
|
|
24780
|
+
await (0, import_promises38.writeFile)((0, import_node_path47.join)(artifactDir, "heal-apply-result.json"), `${JSON.stringify(result, null, 2)}
|
|
24722
24781
|
`, "utf8");
|
|
24723
24782
|
}
|
|
24724
24783
|
return {
|
|
@@ -24728,14 +24787,14 @@ async function runLocalAction(cwd, intent) {
|
|
|
24728
24787
|
};
|
|
24729
24788
|
}
|
|
24730
24789
|
async function sendArtifact(cwd, rel, response, contentType = "text/plain; charset=utf-8") {
|
|
24731
|
-
const absolute = (0,
|
|
24732
|
-
const root = (0,
|
|
24790
|
+
const absolute = (0, import_node_path47.resolve)(cwd, rel);
|
|
24791
|
+
const root = (0, import_node_path47.resolve)(cwd);
|
|
24733
24792
|
if (!absolute.startsWith(root)) {
|
|
24734
24793
|
sendText(response, 403, "Forbidden");
|
|
24735
24794
|
return;
|
|
24736
24795
|
}
|
|
24737
24796
|
try {
|
|
24738
|
-
const text2 = await (0,
|
|
24797
|
+
const text2 = await (0, import_promises38.readFile)(absolute, "utf8");
|
|
24739
24798
|
response.writeHead(200, { "content-type": contentType });
|
|
24740
24799
|
response.end(text2);
|
|
24741
24800
|
} catch {
|
|
@@ -24744,7 +24803,7 @@ async function sendArtifact(cwd, rel, response, contentType = "text/plain; chars
|
|
|
24744
24803
|
}
|
|
24745
24804
|
async function sendReportArtifact(cwd, response) {
|
|
24746
24805
|
try {
|
|
24747
|
-
const text2 = await (0,
|
|
24806
|
+
const text2 = await (0, import_promises38.readFile)((0, import_node_path47.join)(cwd, ".viberaven", "report.html"), "utf8");
|
|
24748
24807
|
response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
24749
24808
|
response.end(text2);
|
|
24750
24809
|
return;
|
|
@@ -24871,7 +24930,7 @@ async function sendReportArtifact(cwd, response) {
|
|
|
24871
24930
|
}
|
|
24872
24931
|
async function sendExtensionIcon(cwd, response) {
|
|
24873
24932
|
try {
|
|
24874
|
-
const body = await (0,
|
|
24933
|
+
const body = await (0, import_promises38.readFile)((0, import_node_path47.join)(cwd, "media", "extension-icon.png"));
|
|
24875
24934
|
sendBinary(response, 200, "image/png", body);
|
|
24876
24935
|
} catch {
|
|
24877
24936
|
sendText(response, 404, "Icon not found");
|
|
@@ -24884,15 +24943,15 @@ async function sendMangaAsset(pathname, response) {
|
|
|
24884
24943
|
return;
|
|
24885
24944
|
}
|
|
24886
24945
|
const candidates = [
|
|
24887
|
-
(0,
|
|
24946
|
+
(0, import_node_path47.join)(__dirname, "manga", name),
|
|
24888
24947
|
// installed + dev: <pkg>/dist/manga
|
|
24889
|
-
(0,
|
|
24948
|
+
(0, import_node_path47.join)(__dirname, "..", "assets", "manga", name),
|
|
24890
24949
|
// fallback: <pkg>/assets/manga (source)
|
|
24891
|
-
(0,
|
|
24950
|
+
(0, import_node_path47.join)(__dirname, "..", "..", "assets", "manga", name)
|
|
24892
24951
|
];
|
|
24893
24952
|
for (const candidate of candidates) {
|
|
24894
24953
|
try {
|
|
24895
|
-
const body = await (0,
|
|
24954
|
+
const body = await (0, import_promises38.readFile)(candidate);
|
|
24896
24955
|
response.setHeader("cache-control", "no-cache");
|
|
24897
24956
|
sendBinary(response, 200, contentTypeForAsset(candidate), body);
|
|
24898
24957
|
return;
|
|
@@ -24902,7 +24961,7 @@ async function sendMangaAsset(pathname, response) {
|
|
|
24902
24961
|
sendText(response, 404, "Asset not found");
|
|
24903
24962
|
}
|
|
24904
24963
|
function contentTypeForAsset(pathname) {
|
|
24905
|
-
switch ((0,
|
|
24964
|
+
switch ((0, import_node_path47.extname)(pathname).toLowerCase()) {
|
|
24906
24965
|
case ".svg":
|
|
24907
24966
|
return "image/svg+xml; charset=utf-8";
|
|
24908
24967
|
case ".png":
|
|
@@ -24919,14 +24978,14 @@ function contentTypeForAsset(pathname) {
|
|
|
24919
24978
|
}
|
|
24920
24979
|
async function sendBundledReportAsset(pathname, response) {
|
|
24921
24980
|
const rel = decodeURIComponent(pathname.replace(/^\/report\//, ""));
|
|
24922
|
-
const assetRoot = (0,
|
|
24923
|
-
const absolute = (0,
|
|
24981
|
+
const assetRoot = (0, import_node_path47.resolve)(getBundledReportAssetsDir());
|
|
24982
|
+
const absolute = (0, import_node_path47.resolve)(assetRoot, rel);
|
|
24924
24983
|
if (!absolute.startsWith(assetRoot)) {
|
|
24925
24984
|
sendText(response, 403, "Forbidden");
|
|
24926
24985
|
return;
|
|
24927
24986
|
}
|
|
24928
24987
|
try {
|
|
24929
|
-
const body = await (0,
|
|
24988
|
+
const body = await (0, import_promises38.readFile)(absolute);
|
|
24930
24989
|
sendBinary(response, 200, contentTypeForAsset(absolute), body);
|
|
24931
24990
|
} catch {
|
|
24932
24991
|
sendText(response, 404, "Asset not found");
|
|
@@ -24948,18 +25007,18 @@ async function bundleStackClient(entryPath) {
|
|
|
24948
25007
|
return output.text;
|
|
24949
25008
|
}
|
|
24950
25009
|
async function sendStackAppJs(response) {
|
|
24951
|
-
const distPath = (0,
|
|
25010
|
+
const distPath = (0, import_node_path47.join)(__dirname, "stack", "app.js");
|
|
24952
25011
|
try {
|
|
24953
|
-
const body = await (0,
|
|
25012
|
+
const body = await (0, import_promises38.readFile)(distPath);
|
|
24954
25013
|
response.writeHead(200, { "content-type": "application/javascript; charset=utf-8", "cache-control": "no-store" });
|
|
24955
25014
|
response.end(body);
|
|
24956
25015
|
return;
|
|
24957
25016
|
} catch {
|
|
24958
25017
|
}
|
|
24959
|
-
const entryPath = (0,
|
|
25018
|
+
const entryPath = (0, import_node_path47.join)(__dirname, "stack", "main.ts");
|
|
24960
25019
|
let mtimeMs;
|
|
24961
25020
|
try {
|
|
24962
|
-
mtimeMs = (await (0,
|
|
25021
|
+
mtimeMs = (await (0, import_promises38.stat)(entryPath)).mtimeMs;
|
|
24963
25022
|
} catch {
|
|
24964
25023
|
sendText(response, 404, "Stack client entry not found: src/local-ui/stack/main.ts is missing.");
|
|
24965
25024
|
return;
|
|
@@ -24979,8 +25038,8 @@ async function sendStackAppJs(response) {
|
|
|
24979
25038
|
async function sendStackAppCss(response) {
|
|
24980
25039
|
try {
|
|
24981
25040
|
const [shell, scene] = await Promise.all([
|
|
24982
|
-
(0,
|
|
24983
|
-
(0,
|
|
25041
|
+
(0, import_promises38.readFile)((0, import_node_path47.join)(__dirname, "stack", "stack.css"), "utf8"),
|
|
25042
|
+
(0, import_promises38.readFile)((0, import_node_path47.join)(__dirname, "stack", "scene.css"), "utf8").catch(() => "")
|
|
24984
25043
|
]);
|
|
24985
25044
|
response.writeHead(200, { "content-type": "text/css; charset=utf-8", "cache-control": "no-store" });
|
|
24986
25045
|
response.end(`${shell}
|
|
@@ -24991,7 +25050,7 @@ ${scene}`);
|
|
|
24991
25050
|
}
|
|
24992
25051
|
async function readProviderVerifyLedger(cwd) {
|
|
24993
25052
|
try {
|
|
24994
|
-
const raw = await (0,
|
|
25053
|
+
const raw = await (0, import_promises38.readFile)((0, import_node_path47.join)(cwd, ".viberaven", "provider-verify.json"), "utf8");
|
|
24995
25054
|
const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
|
|
24996
25055
|
return isRecord5(parsed) ? parsed : {};
|
|
24997
25056
|
} catch {
|
|
@@ -25001,8 +25060,8 @@ async function readProviderVerifyLedger(cwd) {
|
|
|
25001
25060
|
async function writeProviderVerifyEntry(cwd, providerId, entry) {
|
|
25002
25061
|
const ledger = await readProviderVerifyLedger(cwd);
|
|
25003
25062
|
ledger[providerId] = entry;
|
|
25004
|
-
await (0,
|
|
25005
|
-
await (0,
|
|
25063
|
+
await (0, import_promises38.mkdir)((0, import_node_path47.join)(cwd, ".viberaven"), { recursive: true });
|
|
25064
|
+
await (0, import_promises38.writeFile)((0, import_node_path47.join)(cwd, ".viberaven", "provider-verify.json"), `${JSON.stringify(ledger, null, 2)}
|
|
25006
25065
|
`, "utf8");
|
|
25007
25066
|
}
|
|
25008
25067
|
function isCliId(value) {
|
|
@@ -25209,13 +25268,13 @@ async function handleRequest(request, response, options) {
|
|
|
25209
25268
|
return;
|
|
25210
25269
|
}
|
|
25211
25270
|
if (request.method === "GET" && url.pathname === "/api/architecture-map") {
|
|
25212
|
-
const mapPath = (0,
|
|
25213
|
-
const markdown = await (0,
|
|
25271
|
+
const mapPath = (0, import_node_path47.join)(options.cwd, ".viberaven", "architecture-map.md");
|
|
25272
|
+
const markdown = await (0, import_promises38.readFile)(mapPath, "utf8").catch(() => null);
|
|
25214
25273
|
if (markdown === null) {
|
|
25215
25274
|
sendJsonStatus(response, 404, { error: "No architecture map yet. Run a scan first (it is written on every scan)." });
|
|
25216
25275
|
return;
|
|
25217
25276
|
}
|
|
25218
|
-
const dataRaw = await (0,
|
|
25277
|
+
const dataRaw = await (0, import_promises38.readFile)((0, import_node_path47.join)(options.cwd, ".viberaven", "architecture-map.json"), "utf8").catch(() => null);
|
|
25219
25278
|
let data = null;
|
|
25220
25279
|
if (dataRaw) {
|
|
25221
25280
|
try {
|
|
@@ -25224,7 +25283,7 @@ async function handleRequest(request, response, options) {
|
|
|
25224
25283
|
data = null;
|
|
25225
25284
|
}
|
|
25226
25285
|
}
|
|
25227
|
-
const contextRaw = await (0,
|
|
25286
|
+
const contextRaw = await (0, import_promises38.readFile)((0, import_node_path47.join)(options.cwd, ".viberaven", "context-map.json"), "utf8").catch(() => null);
|
|
25228
25287
|
let gaps = [];
|
|
25229
25288
|
if (contextRaw) {
|
|
25230
25289
|
try {
|
|
@@ -25491,35 +25550,35 @@ async function handleRequest(request, response, options) {
|
|
|
25491
25550
|
return;
|
|
25492
25551
|
}
|
|
25493
25552
|
if (request.method === "GET" && url.pathname === "/api/artifacts/tasklist") {
|
|
25494
|
-
await sendArtifact(options.cwd, (0,
|
|
25553
|
+
await sendArtifact(options.cwd, (0, import_node_path47.join)(".viberaven", "agent-tasklist.md"), response);
|
|
25495
25554
|
return;
|
|
25496
25555
|
}
|
|
25497
25556
|
if (request.method === "GET" && url.pathname === "/api/artifacts/playbook") {
|
|
25498
|
-
await sendArtifact(options.cwd, (0,
|
|
25557
|
+
await sendArtifact(options.cwd, (0, import_node_path47.join)(".viberaven", "launch-playbook.md"), response);
|
|
25499
25558
|
return;
|
|
25500
25559
|
}
|
|
25501
25560
|
if (request.method === "GET" && url.pathname === "/api/artifacts/audit") {
|
|
25502
|
-
await sendArtifact(options.cwd, (0,
|
|
25561
|
+
await sendArtifact(options.cwd, (0, import_node_path47.join)(".viberaven", "audit-vercel-supabase.txt"), response);
|
|
25503
25562
|
return;
|
|
25504
25563
|
}
|
|
25505
25564
|
if (request.method === "GET" && url.pathname === "/api/artifacts/cleanup") {
|
|
25506
|
-
await sendArtifact(options.cwd, (0,
|
|
25565
|
+
await sendArtifact(options.cwd, (0, import_node_path47.join)(".viberaven", "context-cleanup.md"), response);
|
|
25507
25566
|
return;
|
|
25508
25567
|
}
|
|
25509
25568
|
if (request.method === "GET" && url.pathname === "/api/artifacts/preview") {
|
|
25510
|
-
await sendArtifact(options.cwd, (0,
|
|
25569
|
+
await sendArtifact(options.cwd, (0, import_node_path47.join)(".viberaven", "preview-rehearsal.md"), response);
|
|
25511
25570
|
return;
|
|
25512
25571
|
}
|
|
25513
25572
|
if (request.method === "GET" && url.pathname === "/api/artifacts/heal-plan") {
|
|
25514
|
-
await sendArtifact(options.cwd, (0,
|
|
25573
|
+
await sendArtifact(options.cwd, (0, import_node_path47.join)(".viberaven", "heal-plan.md"), response);
|
|
25515
25574
|
return;
|
|
25516
25575
|
}
|
|
25517
25576
|
if (request.method === "GET" && url.pathname === "/api/artifacts/heal-prompt") {
|
|
25518
|
-
await sendArtifact(options.cwd, (0,
|
|
25577
|
+
await sendArtifact(options.cwd, (0, import_node_path47.join)(".viberaven", "heal-prompt.md"), response);
|
|
25519
25578
|
return;
|
|
25520
25579
|
}
|
|
25521
25580
|
if (request.method === "GET" && url.pathname === "/api/artifacts/heal-apply") {
|
|
25522
|
-
await sendArtifact(options.cwd, (0,
|
|
25581
|
+
await sendArtifact(options.cwd, (0, import_node_path47.join)(".viberaven", "heal-apply-result.json"), response, "application/json; charset=utf-8");
|
|
25523
25582
|
return;
|
|
25524
25583
|
}
|
|
25525
25584
|
if (request.method === "GET" && url.pathname === "/api/artifacts/report") {
|
|
@@ -25545,12 +25604,12 @@ async function handleRequest(request, response, options) {
|
|
|
25545
25604
|
}
|
|
25546
25605
|
if (request.method === "GET" && url.pathname === "/vendor/anime.umd.min.js") {
|
|
25547
25606
|
for (const candidate of [
|
|
25548
|
-
(0,
|
|
25549
|
-
(0,
|
|
25550
|
-
(0,
|
|
25607
|
+
(0, import_node_path47.join)(__dirname, "vendor", "anime.umd.min.js"),
|
|
25608
|
+
(0, import_node_path47.join)(__dirname, "..", "assets", "vendor", "anime.umd.min.js"),
|
|
25609
|
+
(0, import_node_path47.join)(__dirname, "..", "..", "assets", "vendor", "anime.umd.min.js")
|
|
25551
25610
|
]) {
|
|
25552
25611
|
try {
|
|
25553
|
-
const body = await (0,
|
|
25612
|
+
const body = await (0, import_promises38.readFile)(candidate);
|
|
25554
25613
|
response.setHeader("cache-control", "public, max-age=604800, immutable");
|
|
25555
25614
|
sendBinary(response, 200, "text/javascript; charset=utf-8", body);
|
|
25556
25615
|
return;
|
|
@@ -25771,8 +25830,8 @@ function listen(server, startPort) {
|
|
|
25771
25830
|
});
|
|
25772
25831
|
}
|
|
25773
25832
|
function createActionStudio(cwd, options = {}) {
|
|
25774
|
-
const root = (0,
|
|
25775
|
-
const runsDir = (0,
|
|
25833
|
+
const root = (0, import_node_path47.resolve)(cwd);
|
|
25834
|
+
const runsDir = (0, import_node_path47.join)(root, ".viberaven", "runs");
|
|
25776
25835
|
const runs = new RunStore(runsDir, (0, import_node_os4.homedir)());
|
|
25777
25836
|
const preview = new PreviewManager(root, (0, import_node_os4.homedir)());
|
|
25778
25837
|
const agentReady = new Map(Object.entries(options.agentReady ?? {}));
|
|
@@ -25807,7 +25866,7 @@ function createActionStudio(cwd, options = {}) {
|
|
|
25807
25866
|
};
|
|
25808
25867
|
}
|
|
25809
25868
|
async function startLocalUiServer(options) {
|
|
25810
|
-
const cwd = (0,
|
|
25869
|
+
const cwd = (0, import_node_path47.resolve)(options.cwd);
|
|
25811
25870
|
const scanRunner = options.scanRunner ?? defaultScanRunner;
|
|
25812
25871
|
const autoScan = { state: "idle" };
|
|
25813
25872
|
const fullCheck = new FullCheckService(cwd, options.fullCheck);
|
|
@@ -25826,7 +25885,7 @@ async function startLocalUiServer(options) {
|
|
|
25826
25885
|
});
|
|
25827
25886
|
const port = await listen(server, options.port ?? 4177);
|
|
25828
25887
|
guard.port = port;
|
|
25829
|
-
const hasArtifact = await (0,
|
|
25888
|
+
const hasArtifact = await (0, import_promises38.readFile)((0, import_node_path47.join)(cwd, ".viberaven", "last-scan.json"), "utf8").then(
|
|
25830
25889
|
() => true,
|
|
25831
25890
|
() => false
|
|
25832
25891
|
);
|
|
@@ -25867,16 +25926,16 @@ async function waitForServerShutdown() {
|
|
|
25867
25926
|
process.once("SIGTERM", done);
|
|
25868
25927
|
});
|
|
25869
25928
|
}
|
|
25870
|
-
var import_node_http, import_node_child_process12, import_node_crypto8,
|
|
25929
|
+
var import_node_http, import_node_child_process12, import_node_crypto8, import_promises38, import_node_os4, import_node_path47, 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, AGENT_LABEL;
|
|
25871
25930
|
var init_server = __esm({
|
|
25872
25931
|
"src/local-ui/server.ts"() {
|
|
25873
25932
|
"use strict";
|
|
25874
25933
|
import_node_http = require("node:http");
|
|
25875
25934
|
import_node_child_process12 = require("node:child_process");
|
|
25876
25935
|
import_node_crypto8 = require("node:crypto");
|
|
25877
|
-
|
|
25936
|
+
import_promises38 = require("node:fs/promises");
|
|
25878
25937
|
import_node_os4 = require("node:os");
|
|
25879
|
-
|
|
25938
|
+
import_node_path47 = require("node:path");
|
|
25880
25939
|
init_initGithub();
|
|
25881
25940
|
init_cleanPlan();
|
|
25882
25941
|
init_audit();
|
|
@@ -25922,8 +25981,8 @@ var init_server = __esm({
|
|
|
25922
25981
|
MCP_CONFIG_PATHS = [
|
|
25923
25982
|
".mcp.json",
|
|
25924
25983
|
"mcp.json",
|
|
25925
|
-
(0,
|
|
25926
|
-
(0,
|
|
25984
|
+
(0, import_node_path47.join)(".cursor", "mcp.json"),
|
|
25985
|
+
(0, import_node_path47.join)(".vscode", "mcp.json")
|
|
25927
25986
|
];
|
|
25928
25987
|
MCP_PROVIDER_NEEDLES = {
|
|
25929
25988
|
supabase: ["supabase", "@supabase/mcp", "mcp-server-supabase"],
|
|
@@ -25978,11 +26037,11 @@ function readChangeInput(input) {
|
|
|
25978
26037
|
return { request, accessMode, cliId };
|
|
25979
26038
|
}
|
|
25980
26039
|
function jobRunsPath(cwd) {
|
|
25981
|
-
return (0,
|
|
26040
|
+
return (0, import_node_path48.join)(cwd, ".viberaven", "runner-jobs.json");
|
|
25982
26041
|
}
|
|
25983
26042
|
async function readJobRuns(cwd) {
|
|
25984
26043
|
try {
|
|
25985
|
-
const parsed = JSON.parse(await (0,
|
|
26044
|
+
const parsed = JSON.parse(await (0, import_promises39.readFile)(jobRunsPath(cwd), "utf8"));
|
|
25986
26045
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
25987
26046
|
} catch {
|
|
25988
26047
|
return {};
|
|
@@ -25991,8 +26050,8 @@ async function readJobRuns(cwd) {
|
|
|
25991
26050
|
async function rememberJobRun(cwd, jobId, runId) {
|
|
25992
26051
|
const map = await readJobRuns(cwd);
|
|
25993
26052
|
map[jobId] = runId;
|
|
25994
|
-
await (0,
|
|
25995
|
-
await (0,
|
|
26053
|
+
await (0, import_promises39.mkdir)((0, import_node_path48.dirname)(jobRunsPath(cwd)), { recursive: true });
|
|
26054
|
+
await (0, import_promises39.writeFile)(jobRunsPath(cwd), JSON.stringify(map, null, 2), "utf8");
|
|
25996
26055
|
}
|
|
25997
26056
|
function progressFromRun(run, previewUrl) {
|
|
25998
26057
|
return {
|
|
@@ -26152,13 +26211,13 @@ async function runAgentUndoJob(job, ctx) {
|
|
|
26152
26211
|
progress: final
|
|
26153
26212
|
};
|
|
26154
26213
|
}
|
|
26155
|
-
var
|
|
26214
|
+
var import_promises39, import_node_path48, CLI_IDS, MAX_REQUEST2, MAX_DIFF_FILES, MAX_DIFF_CHARS, TERMINAL, SIGNALS, now, clean;
|
|
26156
26215
|
var init_connectAgent = __esm({
|
|
26157
26216
|
"src/commands/connectAgent.ts"() {
|
|
26158
26217
|
"use strict";
|
|
26159
26218
|
init_taskContract();
|
|
26160
|
-
|
|
26161
|
-
|
|
26219
|
+
import_promises39 = require("node:fs/promises");
|
|
26220
|
+
import_node_path48 = require("node:path");
|
|
26162
26221
|
init_redact();
|
|
26163
26222
|
CLI_IDS = ["claude", "codex", "gemini"];
|
|
26164
26223
|
MAX_REQUEST2 = 1200;
|
|
@@ -26204,9 +26263,9 @@ async function readLocalRepo(cwd) {
|
|
|
26204
26263
|
}
|
|
26205
26264
|
const branch = await git2(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
26206
26265
|
const status = await git2(cwd, ["status", "--porcelain"]);
|
|
26207
|
-
const lock = (file) => (0, import_node_fs16.existsSync)((0,
|
|
26266
|
+
const lock = (file) => (0, import_node_fs16.existsSync)((0, import_node_path49.join)(cwd, file));
|
|
26208
26267
|
return {
|
|
26209
|
-
rootName: (0,
|
|
26268
|
+
rootName: (0, import_node_path49.basename)(cwd) || "workspace",
|
|
26210
26269
|
remotes: [...remotes.values()],
|
|
26211
26270
|
branch: branch && branch !== "HEAD" ? branch : null,
|
|
26212
26271
|
headSha: await git2(cwd, ["rev-parse", "HEAD"]),
|
|
@@ -26453,13 +26512,13 @@ function runConnectCommand(options) {
|
|
|
26453
26512
|
await jobs.close();
|
|
26454
26513
|
});
|
|
26455
26514
|
}
|
|
26456
|
-
var import_node_child_process13, import_node_fs16,
|
|
26515
|
+
var import_node_child_process13, import_node_fs16, import_node_path49, import_node_util6, execFileAsync6, HEARTBEAT_MS, SITE;
|
|
26457
26516
|
var init_connect = __esm({
|
|
26458
26517
|
"src/commands/connect.ts"() {
|
|
26459
26518
|
"use strict";
|
|
26460
26519
|
import_node_child_process13 = require("node:child_process");
|
|
26461
26520
|
import_node_fs16 = require("node:fs");
|
|
26462
|
-
|
|
26521
|
+
import_node_path49 = require("node:path");
|
|
26463
26522
|
import_node_util6 = require("node:util");
|
|
26464
26523
|
init_safeGit();
|
|
26465
26524
|
init_runScan();
|
|
@@ -26488,14 +26547,14 @@ function normalizeWorkContract(task) {
|
|
|
26488
26547
|
if (task.result && task.job && task.result.jobId !== task.job.id) throw new Error("Saved result differs from its job identity.");
|
|
26489
26548
|
if (task.contract.request !== task.request) throw new Error("Saved request differs from its frozen Task contract.");
|
|
26490
26549
|
}
|
|
26491
|
-
var import_node_crypto9,
|
|
26550
|
+
var import_node_crypto9, import_promises40, import_node_path50, WORK_ID, WorkStore;
|
|
26492
26551
|
var init_workStore = __esm({
|
|
26493
26552
|
"src/commands/workStore.ts"() {
|
|
26494
26553
|
"use strict";
|
|
26495
26554
|
init_taskContract();
|
|
26496
26555
|
import_node_crypto9 = require("node:crypto");
|
|
26497
|
-
|
|
26498
|
-
|
|
26556
|
+
import_promises40 = require("node:fs/promises");
|
|
26557
|
+
import_node_path50 = require("node:path");
|
|
26499
26558
|
init_credentials();
|
|
26500
26559
|
WORK_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
26501
26560
|
WorkStore = class {
|
|
@@ -26505,23 +26564,23 @@ var init_workStore = __esm({
|
|
|
26505
26564
|
this.namespace = namespace;
|
|
26506
26565
|
}
|
|
26507
26566
|
async dir(create = true) {
|
|
26508
|
-
const root = await (0,
|
|
26509
|
-
const target = (0,
|
|
26510
|
-
if (create) await (0,
|
|
26511
|
-
const canonical2 = await (0,
|
|
26512
|
-
const rel = (0,
|
|
26513
|
-
if (!rel || !rel.startsWith("..") && !(0,
|
|
26514
|
-
const dir = (0,
|
|
26515
|
-
if (create) await (0,
|
|
26567
|
+
const root = await (0, import_promises40.realpath)(this.cwd);
|
|
26568
|
+
const target = (0, import_node_path50.resolve)(this.configDir);
|
|
26569
|
+
if (create) await (0, import_promises40.mkdir)(target, { recursive: true, mode: 448 });
|
|
26570
|
+
const canonical2 = await (0, import_promises40.realpath)(target);
|
|
26571
|
+
const rel = (0, import_node_path50.relative)(root, canonical2);
|
|
26572
|
+
if (!rel || !rel.startsWith("..") && !(0, import_node_path50.isAbsolute)(rel)) throw new Error("Work storage must be outside the project.");
|
|
26573
|
+
const dir = (0, import_node_path50.join)(canonical2, this.namespace, (0, import_node_crypto9.createHash)("sha256").update(root).digest("hex"));
|
|
26574
|
+
if (create) await (0, import_promises40.mkdir)(dir, { recursive: true, mode: 448 });
|
|
26516
26575
|
return dir;
|
|
26517
26576
|
}
|
|
26518
26577
|
async path(id2, create = true) {
|
|
26519
26578
|
if (!WORK_ID.test(id2)) throw new Error("Invalid task id.");
|
|
26520
|
-
return (0,
|
|
26579
|
+
return (0, import_node_path50.join)(await this.dir(create), `${id2}.json`);
|
|
26521
26580
|
}
|
|
26522
26581
|
async read(id2) {
|
|
26523
|
-
const task = JSON.parse(await (0,
|
|
26524
|
-
if (task.version !== 1 || task.id !== id2 || task.cwd !== await (0,
|
|
26582
|
+
const task = JSON.parse(await (0, import_promises40.readFile)(await this.path(id2, false), "utf8"));
|
|
26583
|
+
if (task.version !== 1 || task.id !== id2 || task.cwd !== await (0, import_promises40.realpath)(this.cwd)) throw new Error("Task does not belong to this workspace.");
|
|
26525
26584
|
if (this.namespace === "work") normalizeWorkContract(task);
|
|
26526
26585
|
return task;
|
|
26527
26586
|
}
|
|
@@ -26532,7 +26591,7 @@ var init_workStore = __esm({
|
|
|
26532
26591
|
normalizeWorkContract(next);
|
|
26533
26592
|
let previous;
|
|
26534
26593
|
try {
|
|
26535
|
-
previous = JSON.parse(await (0,
|
|
26594
|
+
previous = JSON.parse(await (0, import_promises40.readFile)(path2, "utf8"));
|
|
26536
26595
|
} catch (error) {
|
|
26537
26596
|
if (error.code !== "ENOENT") throw error;
|
|
26538
26597
|
}
|
|
@@ -26544,22 +26603,22 @@ var init_workStore = __esm({
|
|
|
26544
26603
|
}
|
|
26545
26604
|
const temp = `${path2}.${(0, import_node_crypto9.randomUUID)()}.tmp`;
|
|
26546
26605
|
try {
|
|
26547
|
-
await (0,
|
|
26606
|
+
await (0, import_promises40.writeFile)(temp, JSON.stringify(task), { encoding: "utf8", mode: 384, flag: "wx" });
|
|
26548
26607
|
if (process.platform === "win32") await restrictWithIcacls(temp);
|
|
26549
|
-
await (0,
|
|
26608
|
+
await (0, import_promises40.rename)(temp, path2);
|
|
26550
26609
|
} finally {
|
|
26551
|
-
await (0,
|
|
26610
|
+
await (0, import_promises40.rm)(temp, { force: true });
|
|
26552
26611
|
}
|
|
26553
26612
|
}
|
|
26554
26613
|
async lock(id2) {
|
|
26555
26614
|
const path2 = `${await this.path(id2)}.lock`;
|
|
26556
26615
|
try {
|
|
26557
|
-
const file = await (0,
|
|
26616
|
+
const file = await (0, import_promises40.open)(path2, "wx", 384);
|
|
26558
26617
|
await file.writeFile(String(process.pid));
|
|
26559
26618
|
await file.close();
|
|
26560
26619
|
} catch (error) {
|
|
26561
26620
|
if (error.code !== "EEXIST") throw error;
|
|
26562
|
-
const pid = Number(await (0,
|
|
26621
|
+
const pid = Number(await (0, import_promises40.readFile)(path2, "utf8"));
|
|
26563
26622
|
if (!Number.isInteger(pid) || pid < 1) throw new Error("Task lock needs manual review.");
|
|
26564
26623
|
try {
|
|
26565
26624
|
process.kill(pid, 0);
|
|
@@ -26571,7 +26630,7 @@ var init_workStore = __esm({
|
|
|
26571
26630
|
throw new Error("This task is already being resumed.");
|
|
26572
26631
|
}
|
|
26573
26632
|
return async () => {
|
|
26574
|
-
await (0,
|
|
26633
|
+
await (0, import_promises40.rm)(path2, { force: true });
|
|
26575
26634
|
};
|
|
26576
26635
|
}
|
|
26577
26636
|
};
|
|
@@ -26873,41 +26932,41 @@ function snapshotHash(snapshot) {
|
|
|
26873
26932
|
return hashJson(snapshot);
|
|
26874
26933
|
}
|
|
26875
26934
|
function inside2(root, target) {
|
|
26876
|
-
const rel = (0,
|
|
26877
|
-
return !!rel && !rel.startsWith(`..${
|
|
26935
|
+
const rel = (0, import_node_path51.relative)((0, import_node_path51.resolve)(root), (0, import_node_path51.resolve)(target));
|
|
26936
|
+
return !!rel && !rel.startsWith(`..${import_node_path51.sep}`) && rel !== ".." && !(0, import_node_path51.isAbsolute)(rel);
|
|
26878
26937
|
}
|
|
26879
26938
|
async function headSha(root) {
|
|
26880
26939
|
const regular = async (path2, limit = 4096) => {
|
|
26881
|
-
const info = await (0,
|
|
26940
|
+
const info = await (0, import_promises41.lstat)(path2);
|
|
26882
26941
|
if (!info.isFile() || info.isSymbolicLink() || info.size > limit) throw new Error("Unsupported Git metadata.");
|
|
26883
|
-
return (0,
|
|
26942
|
+
return (0, import_promises41.readFile)(path2, "utf8");
|
|
26884
26943
|
};
|
|
26885
26944
|
try {
|
|
26886
|
-
let gitDir = (0,
|
|
26887
|
-
const info = await (0,
|
|
26945
|
+
let gitDir = (0, import_node_path51.join)(root, ".git");
|
|
26946
|
+
const info = await (0, import_promises41.lstat)(gitDir);
|
|
26888
26947
|
if (info.isSymbolicLink()) return null;
|
|
26889
26948
|
if (info.isFile()) {
|
|
26890
26949
|
const pointer = /^gitdir: (.+)\r?\n?$/.exec(await regular(gitDir));
|
|
26891
26950
|
if (!pointer) return null;
|
|
26892
|
-
gitDir = (0,
|
|
26951
|
+
gitDir = (0, import_node_path51.resolve)(root, pointer[1].trim());
|
|
26893
26952
|
}
|
|
26894
|
-
if (!(await (0,
|
|
26895
|
-
const head = (await regular((0,
|
|
26953
|
+
if (!(await (0, import_promises41.lstat)(gitDir)).isDirectory() || (await (0, import_promises41.lstat)(gitDir)).isSymbolicLink()) return null;
|
|
26954
|
+
const head = (await regular((0, import_node_path51.join)(gitDir, "HEAD"))).trim();
|
|
26896
26955
|
if (/^[a-f0-9]{40,64}$/.test(head)) return head;
|
|
26897
26956
|
const ref = /^ref: (refs\/[A-Za-z0-9_./-]+)$/.exec(head)?.[1];
|
|
26898
26957
|
if (!ref || !validVerificationPath(ref)) return null;
|
|
26899
26958
|
let common2 = gitDir;
|
|
26900
26959
|
try {
|
|
26901
|
-
const value = (await regular((0,
|
|
26902
|
-
if (value) common2 = (0,
|
|
26960
|
+
const value = (await regular((0, import_node_path51.join)(gitDir, "commondir"))).trim();
|
|
26961
|
+
if (value) common2 = (0, import_node_path51.resolve)(gitDir, value);
|
|
26903
26962
|
} catch {
|
|
26904
26963
|
}
|
|
26905
26964
|
try {
|
|
26906
|
-
const sha2 = (await regular((0,
|
|
26965
|
+
const sha2 = (await regular((0, import_node_path51.join)(common2, ref))).trim();
|
|
26907
26966
|
if (/^[a-f0-9]{40,64}$/.test(sha2)) return sha2;
|
|
26908
26967
|
} catch {
|
|
26909
26968
|
}
|
|
26910
|
-
const packed = await regular((0,
|
|
26969
|
+
const packed = await regular((0, import_node_path51.join)(common2, "packed-refs"), 4e6);
|
|
26911
26970
|
for (const line of packed.split(/\r?\n/)) {
|
|
26912
26971
|
const [sha2, name] = line.split(" ");
|
|
26913
26972
|
if (name === ref && /^[a-f0-9]{40,64}$/.test(sha2)) return sha2;
|
|
@@ -26918,18 +26977,18 @@ async function headSha(root) {
|
|
|
26918
26977
|
}
|
|
26919
26978
|
}
|
|
26920
26979
|
async function captureSnapshot(root, destination) {
|
|
26921
|
-
root = await (0,
|
|
26922
|
-
if (destination && ((0,
|
|
26980
|
+
root = await (0, import_promises41.realpath)(root);
|
|
26981
|
+
if (destination && ((0, import_node_path51.resolve)(destination) === root || inside2(root, destination))) throw new Error("Verification snapshots must stay outside the repository.");
|
|
26923
26982
|
const files = [], excluded = [];
|
|
26924
26983
|
let totalBytes = 0, entries = 0;
|
|
26925
|
-
if (destination) await (0,
|
|
26984
|
+
if (destination) await (0, import_promises41.mkdir)(destination, { recursive: true, mode: 493 });
|
|
26926
26985
|
async function walk(folder, prefix, depth) {
|
|
26927
26986
|
if (depth > SNAPSHOT_LIMITS.depth) throw new Error("Snapshot depth exceeds the complete-input limit.");
|
|
26928
|
-
for (const name of (await (0,
|
|
26987
|
+
for (const name of (await (0, import_promises41.readdir)(folder)).sort()) {
|
|
26929
26988
|
if (++entries > SNAPSHOT_LIMITS.files * 2) throw new Error("Snapshot directory entry limit exceeded; no complete proof is possible.");
|
|
26930
|
-
const path2 = prefix ? `${prefix}/${name}` : name, absolute = (0,
|
|
26931
|
-
const info = await (0,
|
|
26932
|
-
if (info.isSymbolicLink() || await (0,
|
|
26989
|
+
const path2 = prefix ? `${prefix}/${name}` : name, absolute = (0, import_node_path51.join)(folder, name);
|
|
26990
|
+
const info = await (0, import_promises41.lstat)(absolute);
|
|
26991
|
+
if (info.isSymbolicLink() || await (0, import_promises41.realpath)(absolute) !== absolute) throw new Error("Snapshot refuses symlinks, junctions and noncanonical paths.");
|
|
26933
26992
|
if (!validVerificationPath(path2)) throw new Error("Snapshot contains an unsupported path.");
|
|
26934
26993
|
if (excludedDirectories.has(name.toLowerCase())) {
|
|
26935
26994
|
excluded.push({ path: path2, reason: "generated_or_private" });
|
|
@@ -26945,13 +27004,13 @@ async function captureSnapshot(root, destination) {
|
|
|
26945
27004
|
}
|
|
26946
27005
|
if (!info.isFile()) throw new Error("Snapshot refuses non-regular input files.");
|
|
26947
27006
|
if (files.length >= SNAPSHOT_LIMITS.files || info.size > SNAPSHOT_LIMITS.fileBytes || totalBytes + info.size > SNAPSHOT_LIMITS.totalBytes) throw new Error("Snapshot byte/file limit exceeded; inputs are incomplete.");
|
|
26948
|
-
const bytes = await (0,
|
|
27007
|
+
const bytes = await (0, import_promises41.readFile)(absolute), after = await (0, import_promises41.lstat)(absolute);
|
|
26949
27008
|
if (after.isSymbolicLink() || info.size !== bytes.length || after.size !== info.size || after.mtimeMs !== info.mtimeMs || after.ino !== info.ino || after.ctimeMs !== info.ctimeMs) throw new Error("Repository changed while snapshotting; retry with a stable worktree.");
|
|
26950
27009
|
totalBytes += bytes.length;
|
|
26951
27010
|
files.push({ path: path2, sha256: hashBytes(bytes), bytes: bytes.length });
|
|
26952
27011
|
if (destination) {
|
|
26953
|
-
await (0,
|
|
26954
|
-
await (0,
|
|
27012
|
+
await (0, import_promises41.mkdir)((0, import_node_path51.join)(destination, ...path2.split("/").slice(0, -1)), { recursive: true, mode: 493 });
|
|
27013
|
+
await (0, import_promises41.writeFile)((0, import_node_path51.join)(destination, path2), bytes, { mode: 292, flag: "wx" });
|
|
26955
27014
|
}
|
|
26956
27015
|
}
|
|
26957
27016
|
}
|
|
@@ -26960,7 +27019,7 @@ async function captureSnapshot(root, destination) {
|
|
|
26960
27019
|
return { ...data, hash: snapshotHash(data), headSha: await headSha(root) };
|
|
26961
27020
|
}
|
|
26962
27021
|
async function validateSnapshot(root, saved) {
|
|
26963
|
-
if ((await (0,
|
|
27022
|
+
if ((await (0, import_promises41.lstat)(root)).isSymbolicLink() || await (0, import_promises41.realpath)(root) !== (0, import_node_path51.resolve)(root)) throw new Error("Saved snapshot root cannot be a symlink or junction.");
|
|
26964
27023
|
if (!saved || saved.version !== 1 || saved.hash !== snapshotHash({ version: 1, files: saved.files, excluded: saved.excluded, totalBytes: saved.totalBytes })) throw new Error("Saved snapshot manifest hash mismatch.");
|
|
26965
27024
|
const actual = await captureSnapshot(root);
|
|
26966
27025
|
if (hashJson(actual.files) !== hashJson(saved.files) || actual.totalBytes !== saved.totalBytes || actual.excluded.length) throw new Error("Saved snapshot bytes were changed or are incomplete.");
|
|
@@ -26969,12 +27028,12 @@ function changedPaths(before, after) {
|
|
|
26969
27028
|
const a = new Map(before.files.map((f) => [f.path, f.sha256])), b3 = new Map(after.files.map((f) => [f.path, f.sha256]));
|
|
26970
27029
|
return [.../* @__PURE__ */ new Set([...a.keys(), ...b3.keys()])].filter((path2) => a.get(path2) !== b3.get(path2)).sort();
|
|
26971
27030
|
}
|
|
26972
|
-
var
|
|
27031
|
+
var import_promises41, import_node_path51, SNAPSHOT_LIMITS, excludedDirectories, verificationSecretPath;
|
|
26973
27032
|
var init_snapshot = __esm({
|
|
26974
27033
|
"src/verification/snapshot.ts"() {
|
|
26975
27034
|
"use strict";
|
|
26976
|
-
|
|
26977
|
-
|
|
27035
|
+
import_promises41 = require("node:fs/promises");
|
|
27036
|
+
import_node_path51 = require("node:path");
|
|
26978
27037
|
init_workspace();
|
|
26979
27038
|
init_verificationPlan();
|
|
26980
27039
|
SNAPSHOT_LIMITS = { files: 3e3, fileBytes: 2e6, totalBytes: 32e6, depth: 32 };
|
|
@@ -27045,13 +27104,13 @@ emit(JSON.stringify({protocol:1,nonce:input.nonce,...output})+'\\n',()=>exit(0))
|
|
|
27045
27104
|
|
|
27046
27105
|
// src/verification/runtime.ts
|
|
27047
27106
|
async function resolveDockerExecutable(searchPath = process.env.PATH ?? process.env.Path ?? "", projectCwd = process.cwd()) {
|
|
27048
|
-
const project = await (0,
|
|
27049
|
-
for (let folder of searchPath.split(
|
|
27107
|
+
const project = await (0, import_promises42.realpath)(projectCwd);
|
|
27108
|
+
for (let folder of searchPath.split(import_node_path52.delimiter)) {
|
|
27050
27109
|
folder = folder.replace(/^"(.*)"$/, "$1");
|
|
27051
|
-
if (!(0,
|
|
27110
|
+
if (!(0, import_node_path52.isAbsolute)(folder) || /^(?:\\\\|\/\/)/.test(folder)) continue;
|
|
27052
27111
|
try {
|
|
27053
|
-
const executable = await (0,
|
|
27054
|
-
if (executable === project || inside2(project, executable) || !(await (0,
|
|
27112
|
+
const executable = await (0, import_promises42.realpath)((0, import_node_path52.join)(folder, process.platform === "win32" ? "docker.exe" : "docker"));
|
|
27113
|
+
if (executable === project || inside2(project, executable) || !(await (0, import_promises42.lstat)(executable)).isFile()) continue;
|
|
27055
27114
|
return executable;
|
|
27056
27115
|
} catch {
|
|
27057
27116
|
}
|
|
@@ -27059,7 +27118,7 @@ async function resolveDockerExecutable(searchPath = process.env.PATH ?? process.
|
|
|
27059
27118
|
return null;
|
|
27060
27119
|
}
|
|
27061
27120
|
async function dockerCommand(args, config, timeoutMs, signal, projectCwd = process.cwd()) {
|
|
27062
|
-
const executable = await resolveDockerExecutable(void 0, projectCwd), launchCwd = (0,
|
|
27121
|
+
const executable = await resolveDockerExecutable(void 0, projectCwd), launchCwd = (0, import_node_path52.resolve)(config);
|
|
27063
27122
|
if (!executable) return { code: null, stdout: "", stderr: "", truncated: false, timedOut: false, cancelled: !!signal?.aborted };
|
|
27064
27123
|
const env = { DOCKER_CONFIG: config };
|
|
27065
27124
|
for (const key of ["SystemRoot", "SYSTEMROOT", "WINDIR", "TEMP", "TMP"]) if (process.env[key]) env[key] = process.env[key];
|
|
@@ -27257,14 +27316,14 @@ function verificationInputs(check, nonce) {
|
|
|
27257
27316
|
if (check.adapter === "http") return { nonce, module: check.module, export: check.export, requests: check.cases.map((c) => c.request) };
|
|
27258
27317
|
return { nonce, entry: check.entry };
|
|
27259
27318
|
}
|
|
27260
|
-
var import_node_child_process14, import_node_crypto11,
|
|
27319
|
+
var import_node_child_process14, import_node_crypto11, import_promises42, import_node_path52, import_node_util7, DEFAULT_IMAGE, RUNTIME_LIMITS, HTTP_ERRORS, MODULE_RUNNER, TEST_RUNNER, dockerRuntime;
|
|
27261
27320
|
var init_runtime = __esm({
|
|
27262
27321
|
"src/verification/runtime.ts"() {
|
|
27263
27322
|
"use strict";
|
|
27264
27323
|
import_node_child_process14 = require("node:child_process");
|
|
27265
27324
|
import_node_crypto11 = require("node:crypto");
|
|
27266
|
-
|
|
27267
|
-
|
|
27325
|
+
import_promises42 = require("node:fs/promises");
|
|
27326
|
+
import_node_path52 = require("node:path");
|
|
27268
27327
|
import_node_util7 = require("node:util");
|
|
27269
27328
|
init_verificationPlan();
|
|
27270
27329
|
init_httpRunner();
|
|
@@ -27318,38 +27377,38 @@ try {
|
|
|
27318
27377
|
dockerRuntime = {
|
|
27319
27378
|
async cleanup(runId, scratchDir, projectCwd) {
|
|
27320
27379
|
if (!/^[a-f0-9-]{36}$/.test(runId)) throw new Error("Invalid owned verification container ID.");
|
|
27321
|
-
await dockerCommand(["rm", "--force", `vr-verify-${runId}`], (0,
|
|
27380
|
+
await dockerCommand(["rm", "--force", `vr-verify-${runId}`], (0, import_node_path52.join)(scratchDir, "docker-config"), 5e3, void 0, projectCwd);
|
|
27322
27381
|
},
|
|
27323
27382
|
async resolveImage(scratchDir, projectCwd) {
|
|
27324
|
-
const config = (0,
|
|
27325
|
-
await (0,
|
|
27383
|
+
const config = (0, import_node_path52.join)(scratchDir, "docker-config");
|
|
27384
|
+
await (0, import_promises42.mkdir)(config, { recursive: true, mode: 448 });
|
|
27326
27385
|
const result = await dockerCommand(["image", "inspect", "--format", "{{.Id}}", DEFAULT_IMAGE], config, 5e3, void 0, projectCwd);
|
|
27327
27386
|
return result.code === 0 && /^sha256:[a-f0-9]{64}$/.test(result.stdout.trim()) ? result.stdout.trim() : null;
|
|
27328
27387
|
},
|
|
27329
27388
|
async run({ check, snapshotDir, scratchDir, image, signal, runId = (0, import_node_crypto11.randomUUID)(), projectCwd = snapshotDir }) {
|
|
27330
27389
|
if (!/^[a-f0-9-]{36}$/.test(runId)) throw new Error("Invalid owned verification container ID.");
|
|
27331
27390
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString(), start = Date.now(), name = `vr-verify-${runId}`;
|
|
27332
|
-
const dir = await (0,
|
|
27391
|
+
const dir = await (0, import_promises42.mkdtemp)((0, import_node_path52.join)(scratchDir, "oracle-")), config = (0, import_node_path52.join)(scratchDir, "docker-config"), nonce = (0, import_node_crypto11.randomUUID)();
|
|
27333
27392
|
try {
|
|
27334
|
-
await (0,
|
|
27335
|
-
await (0,
|
|
27393
|
+
await (0, import_promises42.chmod)(dir, 493);
|
|
27394
|
+
await (0, import_promises42.mkdir)(config, { recursive: true, mode: 448 });
|
|
27336
27395
|
const inputs = verificationInputs(check, nonce);
|
|
27337
|
-
await (0,
|
|
27338
|
-
await (0,
|
|
27396
|
+
await (0, import_promises42.writeFile)((0, import_node_path52.join)(dir, "input.json"), JSON.stringify(inputs), { mode: 292 });
|
|
27397
|
+
await (0, import_promises42.writeFile)((0, import_node_path52.join)(dir, "runner.mjs"), check.adapter === "module" ? MODULE_RUNNER : check.adapter === "http" ? HTTP_RUNNER : TEST_RUNNER, { mode: 292 });
|
|
27339
27398
|
const raw = await dockerCommand(dockerRunArgs(name, image, snapshotDir, dir), config, RUNTIME_LIMITS.timeoutMs, signal, projectCwd);
|
|
27340
27399
|
const observation = compareObservation(check, raw, nonce, image, startedAt, Date.now() - start);
|
|
27341
27400
|
const sanitized = verificationArtifactText(check, raw);
|
|
27342
|
-
const artifactId = (0, import_node_crypto11.randomUUID)(), artifacts = (0,
|
|
27343
|
-
await (0,
|
|
27344
|
-
await (0,
|
|
27345
|
-
await (0,
|
|
27401
|
+
const artifactId = (0, import_node_crypto11.randomUUID)(), artifacts = (0, import_node_path52.join)(scratchDir, "artifacts", artifactId);
|
|
27402
|
+
await (0, import_promises42.mkdir)(artifacts, { recursive: true, mode: 448 });
|
|
27403
|
+
await (0, import_promises42.writeFile)((0, import_node_path52.join)(artifacts, "stdout.txt"), sanitized.stdout, { mode: 384 });
|
|
27404
|
+
await (0, import_promises42.writeFile)((0, import_node_path52.join)(artifacts, "stderr.txt"), sanitized.stderr, { mode: 384 });
|
|
27346
27405
|
observation.artifacts.stdoutRef = `runtime/artifacts/${artifactId}/stdout.txt`;
|
|
27347
27406
|
observation.artifacts.stderrRef = `runtime/artifacts/${artifactId}/stderr.txt`;
|
|
27348
27407
|
return observation;
|
|
27349
27408
|
} finally {
|
|
27350
27409
|
await dockerCommand(["rm", "--force", name], config, 5e3, void 0, projectCwd);
|
|
27351
27410
|
if (!inside2(scratchDir, dir)) throw new Error("Invalid owned oracle directory.");
|
|
27352
|
-
await (0,
|
|
27411
|
+
await (0, import_promises42.rm)(dir, { recursive: true, force: true });
|
|
27353
27412
|
}
|
|
27354
27413
|
}
|
|
27355
27414
|
};
|
|
@@ -27363,12 +27422,12 @@ async function storage(store, id2) {
|
|
|
27363
27422
|
async function saveState(dir, state, reservedBytes = 0) {
|
|
27364
27423
|
const serialized = JSON.stringify(state);
|
|
27365
27424
|
if (Buffer.byteLength(serialized, "utf8") + reservedBytes > MAX_VERIFICATION_STATE_BYTES) throw new Error("Verification aggregate state budget (8000000 UTF-8 bytes) reached. Previous receipts are preserved; create a new prepared task.");
|
|
27366
|
-
const temp = (0,
|
|
27425
|
+
const temp = (0, import_node_path53.join)(dir, `${(0, import_node_crypto12.randomUUID)()}.tmp`);
|
|
27367
27426
|
try {
|
|
27368
|
-
await (0,
|
|
27369
|
-
await (0,
|
|
27427
|
+
await (0, import_promises43.writeFile)(temp, serialized, { flag: "wx", mode: 384 });
|
|
27428
|
+
await (0, import_promises43.rename)(temp, (0, import_node_path53.join)(dir, "state.json"));
|
|
27370
27429
|
} finally {
|
|
27371
|
-
await (0,
|
|
27430
|
+
await (0, import_promises43.rm)(temp, { force: true });
|
|
27372
27431
|
}
|
|
27373
27432
|
}
|
|
27374
27433
|
function receiptReserve(state, attempt) {
|
|
@@ -27378,25 +27437,25 @@ function receiptReserve(state, attempt) {
|
|
|
27378
27437
|
}).length, 0) * RECEIPT_RESERVE_BYTES;
|
|
27379
27438
|
}
|
|
27380
27439
|
async function removeUnsavedAttempt(dir, id2) {
|
|
27381
|
-
const parent = (0,
|
|
27440
|
+
const parent = (0, import_node_path53.join)(dir, "attempts"), target = (0, import_node_path53.join)(parent, id2);
|
|
27382
27441
|
let actual;
|
|
27383
27442
|
try {
|
|
27384
|
-
actual = await (0,
|
|
27443
|
+
actual = await (0, import_promises43.realpath)(target);
|
|
27385
27444
|
} catch (error) {
|
|
27386
27445
|
if (error.code === "ENOENT") return;
|
|
27387
27446
|
throw error;
|
|
27388
27447
|
}
|
|
27389
|
-
if (!/^[a-f0-9-]{36}$/.test(id2) || actual !== (0,
|
|
27390
|
-
await (0,
|
|
27448
|
+
if (!/^[a-f0-9-]{36}$/.test(id2) || actual !== (0, import_node_path53.resolve)(target) || !inside2(parent, actual)) throw new Error("Refusing cleanup outside the owned unsaved attempt.");
|
|
27449
|
+
await (0, import_promises43.rm)(actual, { recursive: true, force: true });
|
|
27391
27450
|
}
|
|
27392
27451
|
async function readState2(store, task) {
|
|
27393
27452
|
normalizeWorkContract(task);
|
|
27394
27453
|
if (!task.verification) throw new Error("Prepare a reviewed verification plan before editing or verifying.");
|
|
27395
27454
|
const dir = await storage(store, task.id);
|
|
27396
|
-
const stateInfo = await (0,
|
|
27397
|
-
if ((await (0,
|
|
27455
|
+
const stateInfo = await (0, import_promises43.lstat)((0, import_node_path53.join)(dir, "state.json"));
|
|
27456
|
+
if ((await (0, import_promises43.lstat)(dir)).isSymbolicLink() || stateInfo.isSymbolicLink() || !stateInfo.isFile()) throw new Error("Verification storage must be regular, not a symlink.");
|
|
27398
27457
|
if (stateInfo.size > MAX_VERIFICATION_STATE_BYTES) throw new Error("Verification state exceeds its size bound.");
|
|
27399
|
-
const bytes = await (0,
|
|
27458
|
+
const bytes = await (0, import_promises43.readFile)((0, import_node_path53.join)(dir, "state.json"));
|
|
27400
27459
|
if (bytes.length > MAX_VERIFICATION_STATE_BYTES) throw new Error("Verification state exceeds its size bound.");
|
|
27401
27460
|
const state = JSON.parse(bytes.toString("utf8"));
|
|
27402
27461
|
if (state.version !== 1 || hashJson(state.prepared) !== hashJson(task.verification) || state.prepared.contractHash !== task.contractHash || hashJson(parseVerificationPlan(state.plan, task.contract)) !== state.prepared.planHash || state.baseline.hash !== state.prepared.baselineHash) throw new Error("Saved verification plan or baseline hash mismatch.");
|
|
@@ -27404,7 +27463,7 @@ async function readState2(store, task) {
|
|
|
27404
27463
|
if (state.attempts.length && !state.approvedAt || state.approvedAt && !Number.isFinite(Date.parse(state.approvedAt))) throw new Error("Invalid saved verification approval.");
|
|
27405
27464
|
for (const a of state.attempts) {
|
|
27406
27465
|
if (!/^[a-f0-9-]{36}$/.test(a.id) || !/^sha256:[a-f0-9]{64}$/.test(a.image) || !Array.isArray(a.receipts)) throw new Error("Invalid verification attempt.");
|
|
27407
|
-
await validateSnapshot((0,
|
|
27466
|
+
await validateSnapshot((0, import_node_path53.join)(dir, "attempts", a.id, "source"), a.candidate);
|
|
27408
27467
|
}
|
|
27409
27468
|
for (const list of [state.baselineReceipts, ...state.attempts.map((a) => a.receipts)]) {
|
|
27410
27469
|
if (list.length > 32 || new Set(list.map((r) => r.checkId)).size !== list.length) throw new Error("Invalid verification receipt count.");
|
|
@@ -27415,13 +27474,13 @@ async function readState2(store, task) {
|
|
|
27415
27474
|
const ref = receipt.observation?.artifacts[`${stream}Ref`];
|
|
27416
27475
|
if (ref) {
|
|
27417
27476
|
if (!new RegExp(`^runtime/artifacts/[a-f0-9-]{36}/${stream}\\.txt$`).test(ref)) throw new Error("Invalid verification artifact reference.");
|
|
27418
|
-
const file = (0,
|
|
27419
|
-
if (!info.isFile() || info.isSymbolicLink() || info.size > 131072 || hashBytes(await (0,
|
|
27477
|
+
const file = (0, import_node_path53.join)(dir, ref), info = await (0, import_promises43.lstat)(file);
|
|
27478
|
+
if (!info.isFile() || info.isSymbolicLink() || info.size > 131072 || hashBytes(await (0, import_promises43.readFile)(file)) !== receipt.observation.artifacts[`${stream}Sha256`]) throw new Error("Saved verification artifact hash mismatch.");
|
|
27420
27479
|
}
|
|
27421
27480
|
}
|
|
27422
27481
|
}
|
|
27423
27482
|
}
|
|
27424
|
-
await validateSnapshot((0,
|
|
27483
|
+
await validateSnapshot((0, import_node_path53.join)(dir, "baseline"), state.baseline);
|
|
27425
27484
|
return { dir, state };
|
|
27426
27485
|
}
|
|
27427
27486
|
async function prepareVerification(store, task, rawPlan) {
|
|
@@ -27437,8 +27496,8 @@ async function prepareVerification(store, task, rawPlan) {
|
|
|
27437
27496
|
for (const path2 of check.adapter === "node-test" ? [check.entry, ...check.frozenPaths] : [check.module]) if (verificationSecretPath(path2)) throw new Error("Secret paths cannot be verification inputs.");
|
|
27438
27497
|
}
|
|
27439
27498
|
const dir = await storage(store, task.id);
|
|
27440
|
-
await (0,
|
|
27441
|
-
const baseline = await captureSnapshot(task.cwd, (0,
|
|
27499
|
+
await (0, import_promises43.mkdir)(dir, { mode: 448 });
|
|
27500
|
+
const baseline = await captureSnapshot(task.cwd, (0, import_node_path53.join)(dir, "baseline"));
|
|
27442
27501
|
const stable = await captureSnapshot(task.cwd);
|
|
27443
27502
|
if (stable.hash !== baseline.hash) throw new Error("Repository changed while preparing; create a fresh prepared task.");
|
|
27444
27503
|
const prepared = { version: 1, contractHash: task.contractHash, planHash, baselineHash: baseline.hash, preparedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
@@ -27532,19 +27591,19 @@ async function verificationView(store, task) {
|
|
|
27532
27591
|
}
|
|
27533
27592
|
}
|
|
27534
27593
|
async function frozenTestSource(dir, state, attempt, check) {
|
|
27535
|
-
const source = (0,
|
|
27594
|
+
const source = (0, import_node_path53.join)(dir, "attempts", attempt.id, "source");
|
|
27536
27595
|
if (check.adapter !== "node-test") return source;
|
|
27537
|
-
const target = (0,
|
|
27596
|
+
const target = (0, import_node_path53.join)(dir, "attempts", attempt.id, `frozen-${check.id}`);
|
|
27538
27597
|
if (!inside2(dir, target)) throw new Error("Invalid owned frozen-test directory.");
|
|
27539
|
-
await (0,
|
|
27540
|
-
await (0,
|
|
27598
|
+
await (0, import_promises43.rm)(target, { recursive: true, force: true });
|
|
27599
|
+
await (0, import_promises43.mkdir)(target, { recursive: true, mode: 493 });
|
|
27541
27600
|
const frozen = (path2) => frozenVerificationPath(path2) || check.frozenPaths.some((p2) => within(path2, p2));
|
|
27542
|
-
for (const [snapshot, root, isBaseline] of [[attempt.candidate, source, false], [state.baseline, (0,
|
|
27601
|
+
for (const [snapshot, root, isBaseline] of [[attempt.candidate, source, false], [state.baseline, (0, import_node_path53.join)(dir, "baseline"), true]]) {
|
|
27543
27602
|
for (const file of snapshot.files) {
|
|
27544
27603
|
if (frozen(file.path) !== isBaseline) continue;
|
|
27545
|
-
const to = (0,
|
|
27546
|
-
await (0,
|
|
27547
|
-
await (0,
|
|
27604
|
+
const to = (0, import_node_path53.join)(target, file.path);
|
|
27605
|
+
await (0, import_promises43.mkdir)((0, import_node_path53.dirname)(to), { recursive: true, mode: 493 });
|
|
27606
|
+
await (0, import_promises43.copyFile)((0, import_node_path53.join)(root, file.path), to);
|
|
27548
27607
|
}
|
|
27549
27608
|
}
|
|
27550
27609
|
return target;
|
|
@@ -27554,8 +27613,8 @@ async function verifyVerification(store, task, approved, runtime = dockerRuntime
|
|
|
27554
27613
|
const { dir, state } = await readState2(store, task);
|
|
27555
27614
|
state.approvedAt ??= (/* @__PURE__ */ new Date()).toISOString();
|
|
27556
27615
|
await saveState(dir, state);
|
|
27557
|
-
const scratch = (0,
|
|
27558
|
-
await (0,
|
|
27616
|
+
const scratch = (0, import_node_path53.join)(dir, "runtime");
|
|
27617
|
+
await (0, import_promises43.mkdir)(scratch, { recursive: true, mode: 493 });
|
|
27559
27618
|
const image = await runtime.resolveImage(scratch, task.cwd);
|
|
27560
27619
|
if (!image) {
|
|
27561
27620
|
const previous = state.attempts.at(-1);
|
|
@@ -27573,7 +27632,7 @@ async function verifyVerification(store, task, approved, runtime = dockerRuntime
|
|
|
27573
27632
|
if (state.attempts.length >= 16) throw new Error("Verification attempt limit (16) reached. Create a new task.");
|
|
27574
27633
|
const id2 = (0, import_node_crypto12.randomUUID)();
|
|
27575
27634
|
try {
|
|
27576
|
-
const candidate = await captureSnapshot(task.cwd, (0,
|
|
27635
|
+
const candidate = await captureSnapshot(task.cwd, (0, import_node_path53.join)(dir, "attempts", id2, "source"));
|
|
27577
27636
|
if (candidate.hash !== current.hash) throw new Error("Repository changed while capturing candidate. Retry with a stable worktree.");
|
|
27578
27637
|
attempt = { id: id2, candidate, image, createdAt: (/* @__PURE__ */ new Date()).toISOString(), receipts: [] };
|
|
27579
27638
|
state.attempts.push(attempt);
|
|
@@ -27611,7 +27670,7 @@ async function verifyVerification(store, task, approved, runtime = dockerRuntime
|
|
|
27611
27670
|
const needed = check.adapter === "node-test" ? [check.entry, ...check.frozenPaths] : [check.module];
|
|
27612
27671
|
if (needed.some((p2) => !state.baseline.files.some((f) => within(f.path, p2))) || check.adapter !== "node-test" && !snapshot.files.some((f) => f.path === check.module)) receipt.observation = unknownObservation("missing_frozen_input", image, check);
|
|
27613
27672
|
else {
|
|
27614
|
-
const snapshotDir = phase === "baseline" ? (0,
|
|
27673
|
+
const snapshotDir = phase === "baseline" ? (0, import_node_path53.join)(dir, "baseline") : await frozenTestSource(dir, state, active, check);
|
|
27615
27674
|
receipt.observation = await runtime.run({ check, snapshotDir, scratchDir: scratch, image, signal, runId: receipt.id, projectCwd: task.cwd });
|
|
27616
27675
|
}
|
|
27617
27676
|
} catch {
|
|
@@ -27625,13 +27684,13 @@ async function verifyVerification(store, task, approved, runtime = dockerRuntime
|
|
|
27625
27684
|
const after = await captureSnapshot(task.cwd);
|
|
27626
27685
|
return report(task, state, active, after.hash !== active.candidate.hash);
|
|
27627
27686
|
}
|
|
27628
|
-
var import_node_crypto12,
|
|
27687
|
+
var import_node_crypto12, import_promises43, import_node_path53, MAX_VERIFICATION_STATE_BYTES, RECEIPT_RESERVE_BYTES, within, emptyArtifacts, unknownObservation, receiptHash;
|
|
27629
27688
|
var init_verification2 = __esm({
|
|
27630
27689
|
"src/verification/index.ts"() {
|
|
27631
27690
|
"use strict";
|
|
27632
27691
|
import_node_crypto12 = require("node:crypto");
|
|
27633
|
-
|
|
27634
|
-
|
|
27692
|
+
import_promises43 = require("node:fs/promises");
|
|
27693
|
+
import_node_path53 = require("node:path");
|
|
27635
27694
|
init_workStore();
|
|
27636
27695
|
init_verificationPlan();
|
|
27637
27696
|
init_snapshot();
|
|
@@ -27680,7 +27739,7 @@ function workView(task, extra = {}) {
|
|
|
27680
27739
|
async function runWork(options, deps) {
|
|
27681
27740
|
const url = new URL(deps.api);
|
|
27682
27741
|
if (url.username || url.password || url.search || url.hash || url.protocol !== "https:" && !(url.protocol === "http:" && ["127.0.0.1", "localhost", "[::1]"].includes(url.hostname))) throw new Error("Work API must use HTTPS (HTTP is allowed only on loopback).");
|
|
27683
|
-
const cwd = await (0,
|
|
27742
|
+
const cwd = await (0, import_promises44.realpath)(options.cwd);
|
|
27684
27743
|
const store = new WorkStore(cwd, deps.configDir);
|
|
27685
27744
|
const credentials = new CredentialStore(deps.configDir, cwd);
|
|
27686
27745
|
const call2 = async (method, path2, body, token) => {
|
|
@@ -27703,9 +27762,9 @@ async function runWork(options, deps) {
|
|
|
27703
27762
|
if (options.approvedContract !== void 0 && typeof options.approvedContract !== "boolean") throw new Error("Contract approval must be a boolean.");
|
|
27704
27763
|
let supplied;
|
|
27705
27764
|
if (options.contractPath !== void 0) {
|
|
27706
|
-
const path2 = (0,
|
|
27707
|
-
if ((await (0,
|
|
27708
|
-
supplied = parseTaskContract(JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(await (0,
|
|
27765
|
+
const path2 = (0, import_node_path54.resolve)(cwd, options.contractPath);
|
|
27766
|
+
if ((await (0, import_promises44.stat)(path2)).size > TASK_CONTRACT_MAX_BYTES) throw new Error("Task contract file exceeds 4800 UTF-8 bytes.");
|
|
27767
|
+
supplied = parseTaskContract(JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(await (0, import_promises44.readFile)(path2))));
|
|
27709
27768
|
} else if (options.contractBase64 !== void 0) supplied = decodeTaskContract(options.contractBase64);
|
|
27710
27769
|
else if (options.contract !== void 0) supplied = parseTaskContract(options.contract);
|
|
27711
27770
|
let task;
|
|
@@ -27739,10 +27798,10 @@ async function runWork(options, deps) {
|
|
|
27739
27798
|
if (options.action === "prepare") {
|
|
27740
27799
|
let plan = options.verification;
|
|
27741
27800
|
if (options.verificationPath !== void 0) {
|
|
27742
|
-
const path2 = await (0,
|
|
27801
|
+
const path2 = await (0, import_promises44.realpath)((0, import_node_path54.resolve)(cwd, options.verificationPath));
|
|
27743
27802
|
if (path2 === cwd || inside2(cwd, path2)) throw new Error("Keep the reviewed verification plan outside the repository so expectations cannot enter candidate snapshots.");
|
|
27744
|
-
if ((await (0,
|
|
27745
|
-
plan = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(await (0,
|
|
27803
|
+
if ((await (0, import_promises44.stat)(path2)).size > VERIFICATION_PLAN_MAX_BYTES) throw new Error("Verification plan exceeds 65536 UTF-8 bytes.");
|
|
27804
|
+
plan = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(await (0, import_promises44.readFile)(path2)));
|
|
27746
27805
|
} else if (options.verificationBase64 !== void 0) plan = decodeVerificationPlan(options.verificationBase64);
|
|
27747
27806
|
verification = await prepareVerification(store, task, plan);
|
|
27748
27807
|
} else verification = await verifyVerification(store, task, options.approvedVerification, deps.verificationRuntime, deps.signal);
|
|
@@ -27961,12 +28020,12 @@ function contractExecutionBlock(contract) {
|
|
|
27961
28020
|
function assertWorkJob(task, job, executing) {
|
|
27962
28021
|
if (job.kind !== "agent_change" || task.job && task.job.id !== job.id || job.deploySessionId !== task.sessionId || job.input?.requestId !== task.id || executing && (job.input?.request !== task.request || job.input?.cliId !== task.cliId || job.input?.accessMode !== task.accessMode) || job.input?.request !== void 0 && job.input.request !== task.request || job.input?.cliId !== void 0 && job.input.cliId !== task.cliId || job.input?.accessMode !== void 0 && job.input.accessMode !== task.accessMode) throw new Error("Remote job differs from the saved Task contract or job identity. No execution was started.");
|
|
27963
28022
|
}
|
|
27964
|
-
var
|
|
28023
|
+
var import_promises44, import_node_path54, import_node_crypto13, WorkHttpError, terminal, repoKey;
|
|
27965
28024
|
var init_work = __esm({
|
|
27966
28025
|
"src/commands/work.ts"() {
|
|
27967
28026
|
"use strict";
|
|
27968
|
-
|
|
27969
|
-
|
|
28027
|
+
import_promises44 = require("node:fs/promises");
|
|
28028
|
+
import_node_path54 = require("node:path");
|
|
27970
28029
|
init_taskContract();
|
|
27971
28030
|
import_node_crypto13 = require("node:crypto");
|
|
27972
28031
|
init_credentials();
|
|
@@ -28589,7 +28648,7 @@ async function runLive(options, deps) {
|
|
|
28589
28648
|
const api = new URL(deps.api);
|
|
28590
28649
|
if (api.username || api.password || api.search || api.hash || api.protocol !== "https:" && !(api.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(api.hostname))) throw new Error("Account API must use HTTPS; HTTP is restricted to loopback.");
|
|
28591
28650
|
if (!["start", "resume", "status", "disconnect"].includes(options.action)) throw new Error("Use live start, resume, status or disconnect.");
|
|
28592
|
-
const cwd = await (0,
|
|
28651
|
+
const cwd = await (0, import_promises45.realpath)(options.cwd);
|
|
28593
28652
|
const store = new WorkStore(cwd, deps.configDir, "live");
|
|
28594
28653
|
const credentials = new CredentialStore(deps.configDir, cwd);
|
|
28595
28654
|
const call2 = async (method, path2, body, token) => {
|
|
@@ -28754,12 +28813,12 @@ async function runLiveCommand(options) {
|
|
|
28754
28813
|
return 1;
|
|
28755
28814
|
}
|
|
28756
28815
|
}
|
|
28757
|
-
var import_node_crypto15,
|
|
28816
|
+
var import_node_crypto15, import_promises45, LiveHttpError, terminal2;
|
|
28758
28817
|
var init_live = __esm({
|
|
28759
28818
|
"src/commands/live.ts"() {
|
|
28760
28819
|
"use strict";
|
|
28761
28820
|
import_node_crypto15 = require("node:crypto");
|
|
28762
|
-
|
|
28821
|
+
import_promises45 = require("node:fs/promises");
|
|
28763
28822
|
init_credentials();
|
|
28764
28823
|
init_hostedClient();
|
|
28765
28824
|
init_workStore();
|
|
@@ -29207,8 +29266,8 @@ function isSqlError(error) {
|
|
|
29207
29266
|
}
|
|
29208
29267
|
function displayPath(projectDir, path2) {
|
|
29209
29268
|
for (const base2 of [projectDir, process.cwd()]) {
|
|
29210
|
-
const rel = (0,
|
|
29211
|
-
if (rel && !rel.startsWith("..") && !(0,
|
|
29269
|
+
const rel = (0, import_node_path55.relative)(base2, path2);
|
|
29270
|
+
if (rel && !rel.startsWith("..") && !(0, import_node_path55.isAbsolute)(rel)) return rel.split("\\").join("/");
|
|
29212
29271
|
}
|
|
29213
29272
|
return path2;
|
|
29214
29273
|
}
|
|
@@ -29466,7 +29525,7 @@ function writeControl(filtered, changed) {
|
|
|
29466
29525
|
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"
|
|
29467
29526
|
};
|
|
29468
29527
|
}
|
|
29469
|
-
return { ok: true, detail: `as the superuser the same statement changed ${plural3(
|
|
29528
|
+
return { ok: true, detail: `as the superuser the same statement changed ${plural3(run.affected ?? 0, "row")}` };
|
|
29470
29529
|
};
|
|
29471
29530
|
}
|
|
29472
29531
|
async function differingValue(db, shape, target, column) {
|
|
@@ -29566,6 +29625,9 @@ function insertPlans(c) {
|
|
|
29566
29625
|
}
|
|
29567
29626
|
];
|
|
29568
29627
|
}
|
|
29628
|
+
function writeFilters(pkSelectable, noPrivilege) {
|
|
29629
|
+
return noPrivilege ? [true] : pkSelectable ? [true, false] : [false];
|
|
29630
|
+
}
|
|
29569
29631
|
function freeOtherUserSlot(db, shape) {
|
|
29570
29632
|
if (!shape.ownerUnique) return void 0;
|
|
29571
29633
|
return async () => void await db.query(`delete from ${shape.qualified} where ${quoteIdent2(shape.owner.name)}::text = $1`, [OTHER_USER_ID]);
|
|
@@ -29582,50 +29644,55 @@ async function updatePlans(c) {
|
|
|
29582
29644
|
const { db, shape, target, privileges, actor } = c;
|
|
29583
29645
|
const pkSelectable = shape.pk.every((col) => privileges.column.get(col.name)?.select);
|
|
29584
29646
|
const noPrivilege = !privileges.table.update && !privileges.any.update;
|
|
29585
|
-
const filtered = pkSelectable || noPrivilege;
|
|
29586
|
-
const where = filtered ? ` where ${pkCondition(shape, 1)}` : "";
|
|
29587
|
-
const baseParams = filtered ? target : [];
|
|
29588
29647
|
const ownerUpdatable = Boolean(privileges.column.get(shape.owner.name)?.update) || noPrivilege;
|
|
29589
29648
|
const column = pickUpdateColumn(shape, privileges);
|
|
29590
29649
|
const value = column ? await differingValue(db, shape, target, column) : void 0;
|
|
29591
29650
|
const plans = [];
|
|
29592
|
-
const
|
|
29593
|
-
|
|
29594
|
-
const
|
|
29595
|
-
const
|
|
29596
|
-
const
|
|
29597
|
-
|
|
29598
|
-
|
|
29599
|
-
|
|
29600
|
-
|
|
29601
|
-
|
|
29602
|
-
|
|
29603
|
-
|
|
29604
|
-
|
|
29605
|
-
|
|
29651
|
+
for (const filtered of writeFilters(pkSelectable, noPrivilege)) {
|
|
29652
|
+
const where = filtered ? ` where ${pkCondition(shape, 1)}` : "";
|
|
29653
|
+
const baseParams = filtered ? target : [];
|
|
29654
|
+
const planStart = plans.length;
|
|
29655
|
+
const add = (name, sets, display, noop = false, prep) => {
|
|
29656
|
+
let n = baseParams.length;
|
|
29657
|
+
const assignments = sets.map((s) => `${quoteIdent2(s.column)} = ${s.value === SELF ? quoteIdent2(s.column) : `$${++n}`}`).join(", ");
|
|
29658
|
+
const sql = `update ${shape.qualified} set ${assignments}${where}`;
|
|
29659
|
+
const params = [...baseParams, ...sets.filter((s) => s.value !== SELF).map((s) => s.value)];
|
|
29660
|
+
const changed = (run) => noop ? (run.affected ?? 0) >= 1 && run.after.target !== null : run.after.target !== null && run.after.target !== run.before.target;
|
|
29661
|
+
plans.push({
|
|
29662
|
+
stmt: { name: filtered ? name : `blind ${name}`, sql, params, display: `${display}${filtered ? "" : ", no row filter (blind write)"}`, filtered },
|
|
29663
|
+
prep,
|
|
29664
|
+
controlOk: writeControl(filtered, changed),
|
|
29665
|
+
judge: (run) => {
|
|
29666
|
+
if (isRlsRefusal(run)) return { verdict: "unknown", detail: `refused by WITH CHECK: ${run.error.message}. This does not establish denial of the owner row` };
|
|
29667
|
+
if (isPrivilegeRefusal(run)) {
|
|
29668
|
+
return noPrivilege ? { verdict: "denied", detail: `refused: ${run.error.message} (42501), no UPDATE on any column` } : { verdict: "unknown", detail: `refused for column privileges (${run.error.message}), but the role can update some columns` };
|
|
29669
|
+
}
|
|
29670
|
+
if (run.error) return { verdict: "unknown", detail: `error ${run.error.code}: ${run.error.message}` };
|
|
29671
|
+
if (noop && !filtered && (run.affected ?? 0) > 0) {
|
|
29672
|
+
return { verdict: "unknown", detail: `${changedRows(run.affected)}, but an unfiltered no-op cannot identify whether the owner row was updated` };
|
|
29673
|
+
}
|
|
29674
|
+
if (changed(run)) return { verdict: "allowed", detail: noop ? `${plural3(run.affected ?? 0, "row")} updated (rewrote its own value)` : `${changedRows(run.affected)} (${sets.map((s) => s.column).join(" and ")} rewritten; owner row changed)` };
|
|
29675
|
+
if (!filtered) return { verdict: "denied", detail: "the owner row is unchanged" };
|
|
29676
|
+
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" };
|
|
29677
|
+
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` };
|
|
29606
29678
|
}
|
|
29607
|
-
|
|
29608
|
-
|
|
29609
|
-
|
|
29610
|
-
|
|
29611
|
-
|
|
29612
|
-
}
|
|
29613
|
-
|
|
29614
|
-
|
|
29615
|
-
|
|
29616
|
-
|
|
29617
|
-
|
|
29618
|
-
|
|
29619
|
-
|
|
29620
|
-
|
|
29621
|
-
|
|
29622
|
-
|
|
29623
|
-
|
|
29624
|
-
freeOtherUserSlot(db, shape)
|
|
29625
|
-
);
|
|
29626
|
-
}
|
|
29627
|
-
if (plans.length === 0) {
|
|
29628
|
-
add("update noop", [{ column: shape.owner.name, value: SELF }], `update the owner's row in ${shape.display} (${shape.owner.name} = itself)`, true);
|
|
29679
|
+
});
|
|
29680
|
+
};
|
|
29681
|
+
if (column && value !== void 0) add("update value", [{ column: column.name, value }], `update ${column.name} on the owner's row in ${shape.display}`);
|
|
29682
|
+
if (actor === "other_user" && ownerUpdatable && !shape.ownerInPk) {
|
|
29683
|
+
const sets = [{ column: shape.owner.name, value: OTHER_USER_ID }];
|
|
29684
|
+
if (column && value !== void 0) sets.push({ column: column.name, value });
|
|
29685
|
+
add(
|
|
29686
|
+
"update claiming the row",
|
|
29687
|
+
sets,
|
|
29688
|
+
`update the owner's row setting ${shape.owner.name} to the other user${column && value !== void 0 ? ` and ${column.name}` : ""}`,
|
|
29689
|
+
false,
|
|
29690
|
+
freeOtherUserSlot(db, shape)
|
|
29691
|
+
);
|
|
29692
|
+
}
|
|
29693
|
+
if (plans.length === planStart) {
|
|
29694
|
+
add("update noop", [{ column: shape.owner.name, value: SELF }], `update the owner's row in ${shape.display} (${shape.owner.name} = itself)`, true);
|
|
29695
|
+
}
|
|
29629
29696
|
}
|
|
29630
29697
|
return plans;
|
|
29631
29698
|
}
|
|
@@ -29634,20 +29701,19 @@ function reassignPlans(c) {
|
|
|
29634
29701
|
const pkSelectable = shape.pk.every((col) => privileges.column.get(col.name)?.select);
|
|
29635
29702
|
const ownerUpdatable = Boolean(privileges.column.get(shape.owner.name)?.update);
|
|
29636
29703
|
const noPrivilege = !ownerUpdatable;
|
|
29637
|
-
|
|
29638
|
-
|
|
29639
|
-
|
|
29640
|
-
|
|
29641
|
-
|
|
29642
|
-
|
|
29643
|
-
|
|
29644
|
-
|
|
29645
|
-
|
|
29646
|
-
|
|
29647
|
-
|
|
29648
|
-
|
|
29649
|
-
|
|
29650
|
-
{
|
|
29704
|
+
return writeFilters(pkSelectable, noPrivilege).map((filtered) => {
|
|
29705
|
+
const where = filtered ? ` where ${pkCondition(shape, 1)}` : "";
|
|
29706
|
+
const baseParams = filtered ? target : [];
|
|
29707
|
+
const owner = quoteIdent2(shape.owner.name);
|
|
29708
|
+
const moved = (run) => run.after.targetOwner === OTHER_USER_ID;
|
|
29709
|
+
const stmt = {
|
|
29710
|
+
name: filtered ? "reassign" : "blind reassign",
|
|
29711
|
+
sql: `update ${shape.qualified} set ${owner} = $${baseParams.length + 1}${where}`,
|
|
29712
|
+
params: [...baseParams, OTHER_USER_ID],
|
|
29713
|
+
display: `set ${shape.owner.name} on the owner's row in ${shape.display} to the other user${filtered ? "" : ", no row filter (blind write)"}`,
|
|
29714
|
+
filtered
|
|
29715
|
+
};
|
|
29716
|
+
return {
|
|
29651
29717
|
stmt,
|
|
29652
29718
|
prep: freeOtherUserSlot(db, shape),
|
|
29653
29719
|
controlOk: writeControl(filtered, moved),
|
|
@@ -29662,38 +29728,35 @@ function reassignPlans(c) {
|
|
|
29662
29728
|
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" };
|
|
29663
29729
|
return { verdict: "unknown", detail: `reported ${plural3(run.affected ?? 0, "row")}, but the owner column did not move` };
|
|
29664
29730
|
}
|
|
29665
|
-
}
|
|
29666
|
-
|
|
29731
|
+
};
|
|
29732
|
+
});
|
|
29667
29733
|
}
|
|
29668
29734
|
function deletePlans(c) {
|
|
29669
29735
|
const { shape, target, privileges } = c;
|
|
29670
29736
|
const pkSelectable = shape.pk.every((col) => privileges.column.get(col.name)?.select);
|
|
29671
29737
|
const noPrivilege = !privileges.table.delete;
|
|
29672
|
-
const filtered = pkSelectable || noPrivilege;
|
|
29673
29738
|
const gone = (run) => run.after.target === null && run.before.target !== null;
|
|
29674
|
-
return
|
|
29675
|
-
{
|
|
29676
|
-
|
|
29677
|
-
|
|
29678
|
-
|
|
29679
|
-
|
|
29680
|
-
|
|
29681
|
-
|
|
29682
|
-
|
|
29683
|
-
|
|
29684
|
-
|
|
29685
|
-
|
|
29686
|
-
|
|
29687
|
-
|
|
29688
|
-
|
|
29689
|
-
|
|
29690
|
-
|
|
29691
|
-
|
|
29692
|
-
|
|
29693
|
-
};
|
|
29694
|
-
}
|
|
29739
|
+
return writeFilters(pkSelectable, noPrivilege).map((filtered) => ({
|
|
29740
|
+
stmt: {
|
|
29741
|
+
name: filtered ? "delete" : "blind delete",
|
|
29742
|
+
sql: `delete from ${shape.qualified}${filtered ? ` where ${pkCondition(shape, 1)}` : ""}`,
|
|
29743
|
+
params: filtered ? target : [],
|
|
29744
|
+
display: `delete the owner's row from ${shape.display}${filtered ? "" : ", no row filter (blind write)"}`,
|
|
29745
|
+
filtered
|
|
29746
|
+
},
|
|
29747
|
+
controlOk: writeControl(filtered, gone),
|
|
29748
|
+
judge: (run) => {
|
|
29749
|
+
if (isPrivilegeRefusal(run) && noPrivilege) return { verdict: "denied", detail: `refused: ${run.error.message} (42501), no DELETE privilege` };
|
|
29750
|
+
if (run.error) return { verdict: "unknown", detail: `error ${run.error.code}: ${run.error.message}` };
|
|
29751
|
+
if (gone(run)) return { verdict: "allowed", detail: `${plural3(run.affected ?? 0, "row")} deleted, the owner row is gone (${run.before.count} to ${run.after.count} rows)` };
|
|
29752
|
+
if (!filtered) return { verdict: "denied", detail: "the owner row is still there" };
|
|
29753
|
+
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" };
|
|
29754
|
+
return {
|
|
29755
|
+
verdict: "unknown",
|
|
29756
|
+
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`
|
|
29757
|
+
};
|
|
29695
29758
|
}
|
|
29696
|
-
|
|
29759
|
+
}));
|
|
29697
29760
|
}
|
|
29698
29761
|
async function runCell(ctx, privilegesByRole, actor, action, expected) {
|
|
29699
29762
|
const base2 = { table: ctx.shape.display, actor, action, expected };
|
|
@@ -29707,11 +29770,11 @@ async function runCell(ctx, privilegesByRole, actor, action, expected) {
|
|
|
29707
29770
|
const plans = action === "select" ? await selectPlans(c) : action === "insert" ? insertPlans(c) : action === "update" ? await updatePlans(c) : action === "reassign" ? reassignPlans(c) : deletePlans(c);
|
|
29708
29771
|
const records = [];
|
|
29709
29772
|
let decisive;
|
|
29710
|
-
let
|
|
29773
|
+
let first;
|
|
29774
|
+
let uncertain;
|
|
29711
29775
|
for (const plan of plans) {
|
|
29712
29776
|
const control = plan.controlOk(await runStatement(ctx.db, ctx.shape, ctx.target, plan.stmt, null, plan.prep));
|
|
29713
29777
|
const run = await runStatement(ctx.db, ctx.shape, ctx.target, plan.stmt, session, plan.prep);
|
|
29714
|
-
firstRun = firstRun ?? run;
|
|
29715
29778
|
let judged;
|
|
29716
29779
|
const id2 = run.identity;
|
|
29717
29780
|
if (!id2 || id2.superuser || id2.bypass || id2.user !== session.role) {
|
|
@@ -29723,13 +29786,15 @@ async function runCell(ctx, privilegesByRole, actor, action, expected) {
|
|
|
29723
29786
|
}
|
|
29724
29787
|
const record = { name: plan.stmt.name, statement: plan.stmt.display, verdict: judged.verdict, detail: judged.detail, affectedRows: run.affected, ...run.error ? { error: run.error } : {}, control };
|
|
29725
29788
|
records.push(record);
|
|
29789
|
+
first = first ?? { run, record };
|
|
29790
|
+
if (judged.verdict === "unknown" && !uncertain) uncertain = { run, record };
|
|
29726
29791
|
if (judged.verdict === "allowed" && !decisive) decisive = { run, record };
|
|
29727
29792
|
}
|
|
29728
29793
|
let verdict;
|
|
29729
29794
|
if (decisive) verdict = "allowed";
|
|
29730
29795
|
else if (records.length > 0 && records.every((r2) => r2.verdict === "denied")) verdict = "denied";
|
|
29731
29796
|
else verdict = "unknown";
|
|
29732
|
-
const shown = decisive ??
|
|
29797
|
+
const shown = decisive ?? uncertain ?? first;
|
|
29733
29798
|
const r = shown.run;
|
|
29734
29799
|
const observed = {
|
|
29735
29800
|
statement: shown.record.statement,
|
|
@@ -29812,26 +29877,26 @@ function verdictFor(summary) {
|
|
|
29812
29877
|
}
|
|
29813
29878
|
async function listMigrations(dir) {
|
|
29814
29879
|
try {
|
|
29815
|
-
const entries = await (0,
|
|
29880
|
+
const entries = await (0, import_promises46.readdir)(dir, { withFileTypes: true });
|
|
29816
29881
|
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);
|
|
29817
29882
|
} catch {
|
|
29818
29883
|
return [];
|
|
29819
29884
|
}
|
|
29820
29885
|
}
|
|
29821
29886
|
async function runRlsTest(options) {
|
|
29822
|
-
const projectDir = (0,
|
|
29823
|
-
const matrixPath = (0,
|
|
29824
|
-
const migrationsDir = (0,
|
|
29887
|
+
const projectDir = (0, import_node_path55.resolve)(options.projectDir);
|
|
29888
|
+
const matrixPath = (0, import_node_path55.resolve)(projectDir, options.matrixPath ?? DEFAULT_MATRIX_FILE);
|
|
29889
|
+
const migrationsDir = (0, import_node_path55.resolve)(projectDir, options.migrationsDir ?? (0, import_node_path55.join)("supabase", "migrations"));
|
|
29825
29890
|
const timeouts = { ...DEFAULT_TIMEOUTS, ...options.timeouts };
|
|
29826
|
-
if ((0,
|
|
29827
|
-
throw new RlsInputError(`Refusing to read ${(0,
|
|
29891
|
+
if ((0, import_node_path55.basename)(matrixPath).toLowerCase().startsWith(".env")) {
|
|
29892
|
+
throw new RlsInputError(`Refusing to read ${(0, import_node_path55.basename)(matrixPath)}: rls-test never reads .env files. Point --matrix at a viberaven.permissions.json.`);
|
|
29828
29893
|
}
|
|
29829
29894
|
const filesRead = [];
|
|
29830
29895
|
let matrixText;
|
|
29831
29896
|
try {
|
|
29832
|
-
const info = await (0,
|
|
29897
|
+
const info = await (0, import_promises46.stat)(matrixPath);
|
|
29833
29898
|
if (!info.isFile()) throw new Error("not a file");
|
|
29834
|
-
matrixText = normalise(await (0,
|
|
29899
|
+
matrixText = normalise(await (0, import_promises46.readFile)(matrixPath, "utf8"));
|
|
29835
29900
|
} catch {
|
|
29836
29901
|
throw new RlsInputError(
|
|
29837
29902
|
`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.`
|
|
@@ -29842,8 +29907,8 @@ async function runRlsTest(options) {
|
|
|
29842
29907
|
const names = await listMigrations(migrationsDir);
|
|
29843
29908
|
const migrationFiles = [];
|
|
29844
29909
|
for (const name of names) {
|
|
29845
|
-
const sql = normalise(await (0,
|
|
29846
|
-
filesRead.push(displayPath(projectDir, (0,
|
|
29910
|
+
const sql = normalise(await (0, import_promises46.readFile)((0, import_node_path55.join)(migrationsDir, name), "utf8"));
|
|
29911
|
+
filesRead.push(displayPath(projectDir, (0, import_node_path55.join)(migrationsDir, name)));
|
|
29847
29912
|
migrationFiles.push({ name, sha256: sha256(sql), sql });
|
|
29848
29913
|
}
|
|
29849
29914
|
const migrationsHash = sha256(migrationFiles.map((f) => `${f.name}\0${f.sha256}`).join("\n"));
|
|
@@ -29962,6 +30027,7 @@ async function runRlsTest(options) {
|
|
|
29962
30027
|
} catch (error) {
|
|
29963
30028
|
if (error instanceof RlsTimeoutError || !isSqlError(error)) {
|
|
29964
30029
|
const message = firstLine2(error instanceof Error ? error.message : String(error));
|
|
30030
|
+
result.engine.error = message;
|
|
29965
30031
|
return allUnknown(
|
|
29966
30032
|
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.`,
|
|
29967
30033
|
"not run: the database stopped"
|
|
@@ -30043,15 +30109,15 @@ async function runTable(db, table2, badRoles, roleChecks) {
|
|
|
30043
30109
|
cells
|
|
30044
30110
|
};
|
|
30045
30111
|
}
|
|
30046
|
-
var import_node_child_process16, import_node_crypto17,
|
|
30112
|
+
var import_node_child_process16, import_node_crypto17, import_promises46, import_node_net2, import_node_path55, import_node_util8, 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;
|
|
30047
30113
|
var init_rlsRunner = __esm({
|
|
30048
30114
|
"src/rls/rlsRunner.ts"() {
|
|
30049
30115
|
"use strict";
|
|
30050
30116
|
import_node_child_process16 = require("node:child_process");
|
|
30051
30117
|
import_node_crypto17 = require("node:crypto");
|
|
30052
|
-
|
|
30118
|
+
import_promises46 = require("node:fs/promises");
|
|
30053
30119
|
import_node_net2 = __toESM(require("node:net"));
|
|
30054
|
-
|
|
30120
|
+
import_node_path55 = require("node:path");
|
|
30055
30121
|
import_node_util8 = require("node:util");
|
|
30056
30122
|
init_safeGit();
|
|
30057
30123
|
init_permissionsMatrix();
|
|
@@ -30083,8 +30149,7 @@ var init_rlsRunner = __esm({
|
|
|
30083
30149
|
PROBE_EXCLUSIONS = [
|
|
30084
30150
|
"Policies that look up other tables (teams, organisations, memberships) are only exercised with the rows the migrations create: no membership rows are seeded.",
|
|
30085
30151
|
"WITH CHECK expressions are tried with the generated values only; when every value tried is refused, the cell is Unknown, not a pass.",
|
|
30086
|
-
"
|
|
30087
|
-
"When the primary key is readable, writes are filtered by it; unfiltered UPDATE and DELETE statements are not also tested.",
|
|
30152
|
+
"Writes with applicable privileges also run without a row filter to test writes that SELECT policies may hide, and are judged by the owner row. Filtered writes also run when the primary key is readable.",
|
|
30088
30153
|
"One row per test user is used; a row the migrations create for the user (an auth.users trigger) is reused.",
|
|
30089
30154
|
"Side effects of triggers on other tables are not checked."
|
|
30090
30155
|
];
|
|
@@ -30353,9 +30418,9 @@ var init_nextActionBlock = __esm({
|
|
|
30353
30418
|
function defaultMcpConfigPaths() {
|
|
30354
30419
|
const home = (0, import_node_os5.homedir)();
|
|
30355
30420
|
return [
|
|
30356
|
-
(0,
|
|
30357
|
-
(0,
|
|
30358
|
-
(0,
|
|
30421
|
+
(0, import_node_path56.join)(home, ".config", "claude", "claude_desktop_config.json"),
|
|
30422
|
+
(0, import_node_path56.join)(home, ".cursor", "mcp.json"),
|
|
30423
|
+
(0, import_node_path56.join)(home, ".gemini", "antigravity", "mcp_config.json")
|
|
30359
30424
|
];
|
|
30360
30425
|
}
|
|
30361
30426
|
function resolveConfigPaths() {
|
|
@@ -30432,13 +30497,13 @@ async function verifyProviderGap(options) {
|
|
|
30432
30497
|
fallbackCommand: FALLBACK_COMMAND
|
|
30433
30498
|
};
|
|
30434
30499
|
}
|
|
30435
|
-
var import_node_fs17, import_node_os5,
|
|
30500
|
+
var import_node_fs17, import_node_os5, import_node_path56, FALLBACK_COMMAND, SUPPORTED_PROVIDERS, configPathsOverride;
|
|
30436
30501
|
var init_providerMcpBridge = __esm({
|
|
30437
30502
|
"src/providerMcpBridge.ts"() {
|
|
30438
30503
|
"use strict";
|
|
30439
30504
|
import_node_fs17 = require("node:fs");
|
|
30440
30505
|
import_node_os5 = require("node:os");
|
|
30441
|
-
|
|
30506
|
+
import_node_path56 = require("node:path");
|
|
30442
30507
|
FALLBACK_COMMAND = "npx -y viberaven audit --vercel-supabase --json";
|
|
30443
30508
|
SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["supabase", "vercel"]);
|
|
30444
30509
|
}
|
|
@@ -30675,7 +30740,7 @@ async function cmdProviderVerify(flags, positional) {
|
|
|
30675
30740
|
return result.verified ? 0 : 1;
|
|
30676
30741
|
}
|
|
30677
30742
|
async function cmdStatus(flags, positional) {
|
|
30678
|
-
const startDir = positional[0] ? (0,
|
|
30743
|
+
const startDir = positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
30679
30744
|
let artifact2;
|
|
30680
30745
|
try {
|
|
30681
30746
|
artifact2 = await loadLastArtifact(startDir);
|
|
@@ -30714,7 +30779,7 @@ async function cmdStatus(flags, positional) {
|
|
|
30714
30779
|
return 0;
|
|
30715
30780
|
}
|
|
30716
30781
|
async function runScanCommand(flags, positional, options) {
|
|
30717
|
-
const workspacePath = positional[0] ? (0,
|
|
30782
|
+
const workspacePath = positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
|
|
30718
30783
|
if (!options?.deferMachineOutput) {
|
|
30719
30784
|
console.log(`Scanning ${workspacePath}\u2026 (local, offline)`);
|
|
30720
30785
|
}
|
|
@@ -30740,7 +30805,7 @@ async function runScanCommand(flags, positional, options) {
|
|
|
30740
30805
|
const block = buildNextActionBlock(tasks, updatedState);
|
|
30741
30806
|
let manifest;
|
|
30742
30807
|
try {
|
|
30743
|
-
manifest = JSON.parse(await (0,
|
|
30808
|
+
manifest = JSON.parse(await (0, import_promises47.readFile)(paths.actionsPath, "utf8"));
|
|
30744
30809
|
} catch (error) {
|
|
30745
30810
|
console.warn(error instanceof Error ? error.message : String(error));
|
|
30746
30811
|
}
|
|
@@ -30770,7 +30835,7 @@ async function runScanCommand(flags, positional, options) {
|
|
|
30770
30835
|
return { exitCode: 0, artifacts: paths };
|
|
30771
30836
|
}
|
|
30772
30837
|
async function cmdReport(flags, positional) {
|
|
30773
|
-
const startDir = positional[0] ? (0,
|
|
30838
|
+
const startDir = positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
30774
30839
|
try {
|
|
30775
30840
|
const paths = await refreshReportFromDisk(startDir);
|
|
30776
30841
|
console.log(`Report refreshed: ${paths.reportPath}`);
|
|
@@ -30792,7 +30857,7 @@ async function cmdReport(flags, positional) {
|
|
|
30792
30857
|
}
|
|
30793
30858
|
}
|
|
30794
30859
|
async function cmdPrompt(flags, positional) {
|
|
30795
|
-
const startDir = positional[0] ? (0,
|
|
30860
|
+
const startDir = positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
30796
30861
|
let artifact2;
|
|
30797
30862
|
try {
|
|
30798
30863
|
artifact2 = await loadLastArtifact(startDir);
|
|
@@ -30889,7 +30954,7 @@ async function main() {
|
|
|
30889
30954
|
const wantsJsonl = hasFlag(flags, "jsonl");
|
|
30890
30955
|
const wantsStrict = hasFlag(flags, "strict");
|
|
30891
30956
|
if (flags.condense) {
|
|
30892
|
-
const cwd = positional[0] ? (0,
|
|
30957
|
+
const cwd = positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
30893
30958
|
const result = await runCondenseCommand({ cwd });
|
|
30894
30959
|
console.log(`VibeRaven context map refreshed: ${result.contextMapPath}`);
|
|
30895
30960
|
return 0;
|
|
@@ -30909,7 +30974,7 @@ async function main() {
|
|
|
30909
30974
|
}
|
|
30910
30975
|
if (!command && flags.verify === true && typeof flags.action === "string") {
|
|
30911
30976
|
return runVerifyActionCommand({
|
|
30912
|
-
cwd: positional[0] ? (0,
|
|
30977
|
+
cwd: positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
30913
30978
|
actionId: flags.action
|
|
30914
30979
|
});
|
|
30915
30980
|
}
|
|
@@ -30920,7 +30985,7 @@ async function main() {
|
|
|
30920
30985
|
console.error("VibeRaven could not produce machine output because scan artifacts were not written.");
|
|
30921
30986
|
return 3;
|
|
30922
30987
|
}
|
|
30923
|
-
const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0,
|
|
30988
|
+
const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises47.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
|
|
30924
30989
|
const strictExitCode = wantsStrict && gateResult ? exitCodeForStrictGate(gateResult, { failOnWarnings: flags.strict === "warning" }) : scanResult.exitCode;
|
|
30925
30990
|
if (wantsJson && gateResult) {
|
|
30926
30991
|
process.stdout.write(renderGateResultJson(gateResult));
|
|
@@ -30942,7 +31007,7 @@ async function main() {
|
|
|
30942
31007
|
env: process.env
|
|
30943
31008
|
});
|
|
30944
31009
|
if (mode === "local-ui") {
|
|
30945
|
-
const cwd = positional[0] ? (0,
|
|
31010
|
+
const cwd = positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
30946
31011
|
const handle = await startLocalUiServer({ cwd, initialPath: stackInitialPath(flags) });
|
|
30947
31012
|
console.log(`VibeRaven local UI: ${handle.url}`);
|
|
30948
31013
|
await waitForServerShutdown();
|
|
@@ -30962,7 +31027,7 @@ async function main() {
|
|
|
30962
31027
|
await runInteractiveSession();
|
|
30963
31028
|
return 0;
|
|
30964
31029
|
case "ui": {
|
|
30965
|
-
const cwd = positional[0] ? (0,
|
|
31030
|
+
const cwd = positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
30966
31031
|
const envPort = Number(process.env.PORT);
|
|
30967
31032
|
const handle = await startLocalUiServer({ cwd, port: Number.isFinite(envPort) && envPort > 0 ? envPort : void 0, initialPath: stackInitialPath(flags) });
|
|
30968
31033
|
console.log(`VibeRaven local UI: ${handle.url}`);
|
|
@@ -31009,20 +31074,20 @@ async function main() {
|
|
|
31009
31074
|
});
|
|
31010
31075
|
case "connect":
|
|
31011
31076
|
return runConnectCommand({
|
|
31012
|
-
cwd: positional[0] ? (0,
|
|
31077
|
+
cwd: positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
31013
31078
|
session: typeof flags.session === "string" ? flags.session : void 0,
|
|
31014
31079
|
token: typeof flags.token === "string" ? flags.token : void 0
|
|
31015
31080
|
});
|
|
31016
31081
|
case "check":
|
|
31017
31082
|
return runCheckCommand({
|
|
31018
|
-
cwd: positional[0] ? (0,
|
|
31083
|
+
cwd: positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
31019
31084
|
json: Boolean(flags.json),
|
|
31020
31085
|
quiet: Boolean(flags.quiet),
|
|
31021
31086
|
base: typeof flags.base === "string" ? flags.base : void 0,
|
|
31022
31087
|
markdown: flags.markdown === true
|
|
31023
31088
|
});
|
|
31024
31089
|
case "fix": {
|
|
31025
|
-
const fixCwd = positional[0] ? (0,
|
|
31090
|
+
const fixCwd = positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
31026
31091
|
const gapId = typeof flags.gap === "string" ? flags.gap : void 0;
|
|
31027
31092
|
if (!gapId) {
|
|
31028
31093
|
const artifact2 = await loadLastArtifact(fixCwd).catch(() => void 0);
|
|
@@ -31051,19 +31116,19 @@ async function main() {
|
|
|
31051
31116
|
}
|
|
31052
31117
|
case "actions":
|
|
31053
31118
|
return runActionsCommand({
|
|
31054
|
-
cwd: positional[0] ? (0,
|
|
31119
|
+
cwd: positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
31055
31120
|
json: Boolean(flags.json)
|
|
31056
31121
|
});
|
|
31057
31122
|
case "preview":
|
|
31058
31123
|
return runPreviewCommand({
|
|
31059
|
-
cwd: positional[0] ? (0,
|
|
31124
|
+
cwd: positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
31060
31125
|
agentMode: flags["agent-mode"] === true,
|
|
31061
31126
|
json: Boolean(flags.json)
|
|
31062
31127
|
});
|
|
31063
31128
|
case "next":
|
|
31064
31129
|
return runNextCommand({
|
|
31065
31130
|
json: Boolean(flags.json),
|
|
31066
|
-
cwd: positional[0] ? (0,
|
|
31131
|
+
cwd: positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd()
|
|
31067
31132
|
});
|
|
31068
31133
|
case "guide": {
|
|
31069
31134
|
const provider2 = positional[0];
|
|
@@ -31097,7 +31162,7 @@ async function main() {
|
|
|
31097
31162
|
case "provider-verify":
|
|
31098
31163
|
return cmdProviderVerify(flags, positional);
|
|
31099
31164
|
case "init": {
|
|
31100
|
-
const cwd = positional[0] ? (0,
|
|
31165
|
+
const cwd = positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
31101
31166
|
const agents = typeof flags.agents === "string" ? flags.agents : void 0;
|
|
31102
31167
|
if (flags.github === true) {
|
|
31103
31168
|
return runInitGithubCommand({ cwd, dryRun: flags["dry-run"] === true, force: flags.force === true });
|
|
@@ -31114,7 +31179,7 @@ async function main() {
|
|
|
31114
31179
|
return 1;
|
|
31115
31180
|
}
|
|
31116
31181
|
return runDoctorAgentsCommand({
|
|
31117
|
-
cwd: positional[0] ? (0,
|
|
31182
|
+
cwd: positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd()
|
|
31118
31183
|
});
|
|
31119
31184
|
case "validate-npm-package":
|
|
31120
31185
|
return runValidateNpmPackageCommand({
|
|
@@ -31127,7 +31192,7 @@ async function main() {
|
|
|
31127
31192
|
return 1;
|
|
31128
31193
|
}
|
|
31129
31194
|
return runAuditCommand({
|
|
31130
|
-
cwd: positional[0] ? (0,
|
|
31195
|
+
cwd: positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
31131
31196
|
json: Boolean(flags.json)
|
|
31132
31197
|
});
|
|
31133
31198
|
case "clean": {
|
|
@@ -31135,7 +31200,7 @@ async function main() {
|
|
|
31135
31200
|
console.error("Usage: viberaven clean --plan [path]");
|
|
31136
31201
|
return 1;
|
|
31137
31202
|
}
|
|
31138
|
-
const cwd = positional[0] ? (0,
|
|
31203
|
+
const cwd = positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
31139
31204
|
const plan = buildContextCleanupPlan({ projectRoot: cwd, files: await collectCleanupFiles(cwd) });
|
|
31140
31205
|
const outputPath = await writeCleanupPlan(cwd, plan);
|
|
31141
31206
|
console.log(`Wrote ${outputPath}
|
|
@@ -31145,9 +31210,9 @@ async function main() {
|
|
|
31145
31210
|
}
|
|
31146
31211
|
case "rls-test":
|
|
31147
31212
|
return runRlsTestCommand({
|
|
31148
|
-
cwd: positional[0] ? (0,
|
|
31149
|
-
matrix: typeof flags.matrix === "string" ? (0,
|
|
31150
|
-
migrations: typeof flags.migrations === "string" ? (0,
|
|
31213
|
+
cwd: positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
31214
|
+
matrix: typeof flags.matrix === "string" ? (0, import_node_path57.resolve)(process.cwd(), flags.matrix) : void 0,
|
|
31215
|
+
migrations: typeof flags.migrations === "string" ? (0, import_node_path57.resolve)(process.cwd(), flags.migrations) : void 0,
|
|
31151
31216
|
json: Boolean(flags.json)
|
|
31152
31217
|
});
|
|
31153
31218
|
default:
|
|
@@ -31156,11 +31221,11 @@ async function main() {
|
|
|
31156
31221
|
return 1;
|
|
31157
31222
|
}
|
|
31158
31223
|
}
|
|
31159
|
-
var
|
|
31224
|
+
var import_promises47, import_node_path57, STACK_AREAS;
|
|
31160
31225
|
var init_cli = __esm({
|
|
31161
31226
|
"src/cli.ts"() {
|
|
31162
|
-
|
|
31163
|
-
|
|
31227
|
+
import_promises47 = require("node:fs/promises");
|
|
31228
|
+
import_node_path57 = require("node:path");
|
|
31164
31229
|
init_config();
|
|
31165
31230
|
init_runScan();
|
|
31166
31231
|
init_artifacts2();
|