@scopebond/hook 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +116 -15
- package/dist/cli.js.map +1 -1
- package/dist/cloud.d.ts +24 -2
- package/dist/cloud.d.ts.map +1 -1
- package/dist/cloud.js +77 -9
- package/dist/cloud.js.map +1 -1
- package/dist/delivery-report.d.ts +16 -0
- package/dist/delivery-report.d.ts.map +1 -0
- package/dist/delivery-report.js +51 -0
- package/dist/delivery-report.js.map +1 -0
- package/dist/delivery-state.d.ts +30 -0
- package/dist/delivery-state.d.ts.map +1 -0
- package/dist/delivery-state.js +86 -0
- package/dist/delivery-state.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/install.d.ts.map +1 -1
- package/dist/install.js +8 -2
- package/dist/install.js.map +1 -1
- package/dist/managed.d.ts +3 -1
- package/dist/managed.d.ts.map +1 -1
- package/dist/managed.js +50 -4
- package/dist/managed.js.map +1 -1
- package/dist/obs-emitter.d.ts.map +1 -1
- package/dist/obs-emitter.js +2 -2
- package/dist/obs-emitter.js.map +1 -1
- package/dist/policy-sync.d.ts.map +1 -1
- package/dist/policy-sync.js +6 -2
- package/dist/policy-sync.js.map +1 -1
- package/dist/proof.d.ts +1 -1
- package/dist/proof.d.ts.map +1 -1
- package/dist/proof.js +2 -1
- package/dist/proof.js.map +1 -1
- package/dist/recover.d.ts +33 -0
- package/dist/recover.d.ts.map +1 -0
- package/dist/recover.js +201 -0
- package/dist/recover.js.map +1 -0
- package/dist/runtime.d.ts +2 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +12 -2
- package/dist/runtime.js.map +1 -1
- package/dist/version.d.ts +4 -4
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +5 -5
- package/dist/version.js.map +1 -1
- package/package.json +3 -3
package/dist/install.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"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","sourcesContent":["// SB112 — the user-level installer. One install per developer machine (not per repo):\n// keys and a starter policy live in a user-level home (~/.scopebond, override\n// SCOPEBOND_HOME), and the hook is registered by absolute path in the user-level agent\n// config (~/.claude/settings.json, ~/.cursor/hooks.json, ~/.codex/hooks.json). When a\n// user-level install exists, a project-local `.scopebond` is used only if the user\n// trusted that exact policy (`scopebond trust`, or `init` in the project): a cloned\n// repository, or an agent inside it, must not be able to swap in its own policy.\n\nimport { execFileSync } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { mkdirSync, writeFileSync, existsSync, readFileSync, rmSync, copyFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { basename, dirname, isAbsolute, join, resolve } from \"node:path\";\n\nexport type Harness = \"claude\" | \"cursor\" | \"codex\";\n\n/** The user-level Scopebond home. `SCOPEBOND_HOME` overrides it (tests, CI). */\nexport function userHome(): string {\n return process.env.SCOPEBOND_HOME ?? join(homedir(), \".scopebond\");\n}\n\n/** The user-level agent config file for a harness. `HOME`/`USERPROFILE` (via homedir)\n * roots it; SCOPEBOND_HOME does not move the agent's own config. */\nexport function userHarnessFile(harness: Harness): string {\n if (harness === \"cursor\") return join(homedir(), \".cursor\", \"hooks.json\");\n if (harness === \"codex\") return join(homedir(), \".codex\", \"hooks.json\");\n return join(homedir(), \".claude\", \"settings.json\");\n}\n\n/** The project-level agent config file for a harness — what `init` writes, so the\n * hook travels with the repository rather than the machine. */\nexport function projectHarnessFile(harness: Harness, cwd: string = process.cwd()): string {\n if (harness === \"cursor\") return join(cwd, \".cursor\", \"hooks.json\");\n if (harness === \"codex\") return join(cwd, \".codex\", \"hooks.json\");\n return join(cwd, \".claude\", \"settings.json\");\n}\n\n/** The personal, per-machine project config for a harness, or null when the harness\n * has none. Claude Code reads `.claude/settings.local.json` beside `settings.json` and\n * merges the hooks of both; it is the place for anything that names a path on this\n * machine. Cursor and Codex have no such file. */\nexport function localHarnessFile(harness: Harness, cwd: string = process.cwd()): string | null {\n return harness === \"claude\" ? join(cwd, \".claude\", \"settings.local.json\") : null;\n}\n\n/** Which config files actually carry a Scopebond hook for this harness. There are three\n * places it can live — the shared project config, the personal project config (Claude\n * Code's `settings.local.json`, where `init` pins the fast command) and the user config\n * `install` writes — and `status`/`doctor` must look at all of them: checking only the\n * user one told everyone who ran `init` that their install had failed. */\nexport interface HarnessScopes { project: string | null; local: string | null; user: string | null }\n\nexport function harnessScopes(harness: Harness, cwd: string = process.cwd()): HarnessScopes {\n const project = projectHarnessFile(harness, cwd);\n const local = localHarnessFile(harness, cwd);\n const user = userHarnessFile(harness);\n return {\n project: isHarnessConfigured(project) ? project : null,\n local: local && isHarnessConfigured(local) ? local : null,\n user: isHarnessConfigured(user) ? user : null,\n };\n}\n\n/** One word for where a harness is wired, for a status line. */\nexport function harnessScopeLabel(scopes: HarnessScopes): string {\n const where = [\n scopes.project ? \"project\" : \"\",\n scopes.local ? \"this machine\" : \"\",\n scopes.user ? \"user\" : \"\",\n ].filter(Boolean);\n if (where.length === 0) return \"\";\n if (where.length === 1 && scopes.project) return \"configured (this project)\";\n if (where.length === 1 && scopes.local) return \"configured (this project, this machine only)\";\n if (where.length === 1 && scopes.user) return \"configured (user-level)\";\n return `configured (${where.join(\" + \")})`;\n}\n\n/** Whether a hook command names a path that exists only on this machine — the pinned\n * `\"<node>\" \"<…/cli.js>\" claude` form — rather than the portable `npx` form. */\nexport function isMachineSpecificCommand(command: string): boolean {\n if (/(^|\\s)npx(\\.cmd)?\\s/.test(command)) return false;\n const first = (command.match(/\"[^\"]+\"|\\S+/) ?? [\"\"])[0].replace(/^\"|\"$/g, \"\");\n return isAbsolute(first) || /^[A-Za-z]:[\\\\/]/.test(first);\n}\n\n/** How git treats a config file: committed (`tracked`), kept out (`ignored`), neither\n * yet (`untracked`, so a `git add .` would commit it), or not in a repository / no git\n * on this machine (`none`). A file git shares must never carry a machine-specific\n * command: on a teammate's machine the path does not exist, the command cannot start,\n * and Claude Code, Cursor and Codex all treat a hook that cannot start as a non-blocking\n * error — the agent runs with no check at all. */\nexport type GitShare = \"tracked\" | \"ignored\" | \"untracked\" | \"none\";\n\nexport function gitShareState(file: string): GitShare {\n const cwd = dirname(file);\n const git = (args: string[]): number => {\n try {\n mkdirSync(cwd, { recursive: true });\n execFileSync(\"git\", args, { cwd, stdio: \"ignore\", timeout: 5000 });\n return 0;\n } catch (error) {\n const status = (error as { status?: number | null }).status;\n return typeof status === \"number\" ? status : -1;\n }\n };\n if (git([\"rev-parse\", \"--is-inside-work-tree\"]) !== 0) return \"none\";\n if (git([\"ls-files\", \"--error-unmatch\", \"--\", file]) === 0) return \"tracked\";\n return git([\"check-ignore\", \"-q\", \"--\", file]) === 0 ? \"ignored\" : \"untracked\";\n}\n\n/** Keep a file out of git for this clone only, through `.git/info/exclude` — never the\n * repository's own `.gitignore`, which is the team's file. Returns true when the file is\n * ignored afterwards. */\nexport function excludeFromGit(file: string): boolean {\n try {\n const cwd = dirname(file);\n mkdirSync(cwd, { recursive: true });\n // The entry is built from git's own view of where this directory sits in the work\n // tree (`--show-prefix`), not from comparing file-system paths: the same directory\n // can be spelled two ways (a Windows 8.3 short name, a symlinked temp dir), and a\n // path computed across the two spellings excludes the wrong file.\n const prefix = execFileSync(\"git\", [\"rev-parse\", \"--show-prefix\"], { cwd, encoding: \"utf8\", timeout: 5000 }).trim();\n const exclude = resolve(cwd, execFileSync(\"git\", [\"rev-parse\", \"--git-path\", \"info/exclude\"], { cwd, encoding: \"utf8\", timeout: 5000 }).trim());\n const entry = \"/\" + prefix + basename(file);\n const existing = existsSync(exclude) ? readFileSync(exclude, \"utf8\") : \"\";\n if (!existing.split(/\\r?\\n/).includes(entry)) {\n mkdirSync(dirname(exclude), { recursive: true });\n writeFileSync(exclude, `${existing}${existing && !existing.endsWith(\"\\n\") ? \"\\n\" : \"\"}${entry}\\n`);\n }\n return gitShareState(file) === \"ignored\";\n } catch { return false; }\n}\n\n/** Remove the Scopebond entries whose command `remove` selects, leaving every other\n * setting alone. Returns the number of entries removed. */\nexport function pruneHarnessEntries(file: string, remove: (command: string) => boolean): number {\n if (!existsSync(file)) return 0;\n let config: Record<string, unknown>;\n try { config = readHarnessConfig(file); } catch { return 0; }\n const hooks = isRecord(config.hooks) ? config.hooks : null;\n if (!hooks) return 0;\n let removed = 0;\n const commandsOf = (e: unknown): string[] => {\n if (!isRecord(e)) return [];\n const own = isScopebond(e.command) ? [String(e.command)] : [];\n const nested = Array.isArray(e.hooks) ? e.hooks.flatMap(commandsOf) : [];\n return [...own, ...nested];\n };\n for (const [event, value] of Object.entries(hooks)) {\n if (!Array.isArray(value)) continue;\n const kept = value.filter((e) => !(entryMatches(e) && commandsOf(e).some(remove)));\n removed += value.length - kept.length;\n (hooks as Record<string, unknown[]>)[event] = kept;\n }\n if (removed > 0) writeFileSync(file, JSON.stringify(config, null, 2) + \"\\n\");\n return removed;\n}\n\n/** The file in the user home that pins trusted project policies: absolute project\n * config dir → SHA-256 of its policy.json. It lives under the protected home, so the\n * governed agent cannot write it. */\nexport function trustedProjectsFile(): string {\n return join(userHome(), \"trusted-projects.json\");\n}\n\nfunction policyDigest(dir: string): string | null {\n try { return createHash(\"sha256\").update(readFileSync(join(dir, \"policy.json\"))).digest(\"hex\"); } catch { return null; }\n}\n\nfunction readTrusted(): Record<string, string> {\n try { const p = JSON.parse(readFileSync(trustedProjectsFile(), \"utf8\")); return isRecord(p) ? (p as Record<string, string>) : {}; } catch { return {}; }\n}\n\nconst trustKey = (dir: string): string => resolve(dir).replace(/\\\\/g, \"/\").toLowerCase();\n\n/** Pin the project's current policy as trusted. Editing the policy afterwards un-trusts\n * it until the user trusts it again, so a rewrite by the agent never takes effect. */\nexport function trustProjectPolicy(dir: string): string {\n const digest = policyDigest(dir);\n if (!digest) throw new Error(`no policy at ${join(dir, \"policy.json\")}`);\n const trusted = readTrusted();\n trusted[trustKey(dir)] = digest;\n mkdirSync(userHome(), { recursive: true });\n writeFileSync(trustedProjectsFile(), JSON.stringify(trusted, null, 2) + \"\\n\");\n return digest;\n}\n\n/** Whether the project's policy is exactly the one the user trusted. */\nexport function isTrustedProject(dir: string): boolean {\n const digest = policyDigest(dir);\n return !!digest && readTrusted()[trustKey(dir)] === digest;\n}\n\n/** Resolve the config dir for a hook event, most specific first:\n * explicit override → the payload's project → $CLAUDE_PROJECT_DIR → the user home.\n * A project dir counts only when it has been scaffolded (a policy.json), so a bare\n * working directory never shadows the user-level install — and, once a user-level\n * install exists, only when its policy is trusted (`isTrustedProject`). Otherwise the\n * user's own policy governs: a repository cannot bring a weaker one with it. */\nexport function resolveConfigDir(payloadCwd: string | undefined): string {\n if (process.env.SCOPEBOND_HOOK_DIR) return process.env.SCOPEBOND_HOOK_DIR;\n const candidates: string[] = [];\n if (payloadCwd) candidates.push(join(payloadCwd, \".scopebond\"));\n if (process.env.CLAUDE_PROJECT_DIR) candidates.push(join(process.env.CLAUDE_PROJECT_DIR, \".scopebond\"));\n const home = userHome();\n const homeInstalled = existsSync(join(home, \"policy.json\"));\n for (const dir of candidates) {\n if (existsSync(join(dir, \"policy.json\")) && (!homeInstalled || isTrustedProject(dir))) return dir;\n }\n if (homeInstalled) return home;\n // Nothing scaffolded yet: prefer the payload project, else the user home.\n return candidates[0] ?? home;\n}\n\n/** A project policy present but ignored because it is not trusted (for status/doctor). */\nexport function untrustedProjectPolicy(payloadCwd: string | undefined): string | null {\n if (process.env.SCOPEBOND_HOOK_DIR || !existsSync(join(userHome(), \"policy.json\"))) return null;\n const dirs = [payloadCwd, process.env.CLAUDE_PROJECT_DIR].filter(Boolean).map((d) => join(d as string, \".scopebond\"));\n return dirs.find((d) => existsSync(join(d, \"policy.json\")) && !isTrustedProject(d)) ?? null;\n}\n\nconst isRecord = (v: unknown): v is Record<string, unknown> => typeof v === \"object\" && v !== null && !Array.isArray(v);\n\n/** Whether a harness command string is one of ours. This is the single matcher for\n * every form the installer has ever written, and it is what makes re-running `init`\n * or `install` replace the entry instead of appending a second one:\n * - `npx -y @scopebond/hook@0.6.0 claude` (portable)\n * - `\"<node>\" \"<…/@scopebond/hook/dist/cli.js>\" claude` (pinned, POSIX)\n * - `\"<node>\" \"<…\\@scopebond\\hook\\dist\\cli.js>\" claude` (pinned, Windows)\n * - `scopebond-hook claude` (legacy global binary)\n * The separator class matters: a Windows pinned path spells the scope\n * `@scopebond\\hook`, and matching only `@scopebond/hook` let a second `init`\n * install a duplicate hook — which would double-check every tool call.\n *\n * Written with substring and token tests rather than one regular expression: the\n * previous `(^|[\"\\s])scopebond([\"'\\s]).*\\b(claude|cursor|codex)\\b` backtracked\n * polynomially on a long run of `\\tscopebond\\t` (CodeQL js/polynomial-redos). The\n * input is a command out of a config file rather than a request, so this was not\n * reachable by an attacker — but a linear check is also the clearer one. */\nconst HARNESS_SUBCOMMANDS = new Set([\"claude\", \"cursor\", \"codex\"]);\n/** Long enough for any real command; a bound keeps the work linear and small. */\nconst MAX_COMMAND = 4096;\nconst unquote = (token: string): string => token.replace(/^[\"']+|[\"']+$/g, \"\");\n\nexport const isScopebondHookCommand = (cmd: unknown): boolean => {\n if (typeof cmd !== \"string\" || cmd.length === 0 || cmd.length > MAX_COMMAND) return false;\n const lower = cmd.toLowerCase();\n // The package itself, in either path spelling (`@scopebond/hook`, `@scopebond\\hook`).\n if (lower.includes(\"@scopebond/hook\") || lower.includes(\"@scopebond\\\\hook\")) return true;\n // The legacy global binary, as a whole token.\n if (/(^|[\\s\"'])scopebond-hook([\\s\"']|$)/.test(cmd)) return true;\n // An absolute or bare form whose last argument is one of our harness subcommands:\n // `<node> <…/cli.js> claude`, `scopebond cursor`.\n const tokens = cmd.split(/\\s+/).filter(Boolean);\n const last = unquote(tokens[tokens.length - 1] ?? \"\");\n if (!HARNESS_SUBCOMMANDS.has(last)) return false;\n return tokens.some((token) => {\n const t = unquote(token).toLowerCase();\n return t.includes(\"scopebond\") || /cli\\.(m|c)?js$/.test(t);\n });\n};\nconst isScopebond = isScopebondHookCommand;\n\n/** Whether a harness hook entry (either shape: a bare command, or a group with a\n * nested `hooks` array) is a Scopebond entry. */\nexport const harnessEntryMatches = (e: unknown): boolean =>\n isRecord(e) && (isScopebond(e.command) || (Array.isArray(e.hooks) && e.hooks.some((h) => isRecord(h) && isScopebond(h.command))));\nconst entryMatches = harnessEntryMatches;\n\n/** Read an agent's JSON config for merging. A missing file is an empty config; a file\n * that exists but does not parse as a JSON object is an error — rewriting it would\n * silently delete the user's other settings, so the installer stops instead. */\nexport function readHarnessConfig(file: string): Record<string, unknown> {\n if (!existsSync(file)) return {};\n const text = readFileSync(file, \"utf8\");\n if (!text.trim()) return {};\n let parsed: unknown;\n try { parsed = JSON.parse(text); } catch (error) {\n throw new Error(`${file} is not valid JSON (${(error as Error).message}); fix or move it, then run this again — it was left unchanged`);\n }\n if (!isRecord(parsed)) throw new Error(`${file} is not a JSON object; fix or move it, then run this again — it was left unchanged`);\n return parsed;\n}\n\n/** Copy an agent config aside before changing it, once. Re-running an installer must not\n * overwrite the backup with the already-modified file, or the original is lost on the\n * second run — so an existing backup is left alone. Returns the backup path when one was\n * written.\n *\n * This exists because these installers rewrite files the user did not create and may not\n * know are involved (`~/.claude/settings.json` holds their theme, plugins and\n * permissions), and the undo was previously \"hope the merge was right\". */\nexport function backupHarnessConfig(file: string): string | null {\n if (!existsSync(file)) return null;\n const backup = `${file}.scopebond-backup`;\n if (existsSync(backup)) return null;\n try { copyFileSync(file, backup); return backup; } catch { return null; }\n}\n\n/** Write (idempotently) a harness hook entry pointing at an explicit command string.\n * Shared by the project installer (npx command) and the user installer (absolute path). */\nexport function writeHarnessConfig(file: string, harness: Harness, command: string): string {\n const config = readHarnessConfig(file);\n backupHarnessConfig(file);\n mkdirSync(dirname(file), { recursive: true });\n const hooks = isRecord(config.hooks) ? config.hooks : (config.hooks = {});\n if (harness === \"cursor\") {\n config.version = config.version ?? 1;\n for (const event of [\"beforeShellExecution\", \"beforeMCPExecution\", \"beforeReadFile\", \"afterFileEdit\"]) {\n const list = Array.isArray((hooks as Record<string, unknown>)[event]) ? (hooks as Record<string, unknown[]>)[event] : ((hooks as Record<string, unknown[]>)[event] = []);\n const at = list.findIndex(entryMatches);\n const entry = { command: command.replace(/\\bclaude$/, \"cursor\") };\n if (at >= 0) list[at] = entry; else list.push(entry);\n }\n } else {\n const list = Array.isArray((hooks as Record<string, unknown>).PreToolUse) ? (hooks as Record<string, unknown[]>).PreToolUse : ((hooks as Record<string, unknown[]>).PreToolUse = []);\n const at = list.findIndex(entryMatches);\n // Codex matchers are regular expressions; omitting the matcher means every tool.\n // A literal \"*\" is not a valid regular expression and causes Codex to skip the group.\n const entry = harness === \"codex\"\n ? { hooks: [{ type: \"command\", command, timeout: 30, statusMessage: \"Checking this action with Scopebond\" }] }\n : { matcher: \"*\", hooks: [{ type: \"command\", command }] };\n if (at >= 0) list[at] = entry; else list.push(entry);\n }\n writeFileSync(file, JSON.stringify(config, null, 2) + \"\\n\");\n return file;\n}\n\n/** Remove any Scopebond hook entry from a user-level harness config, leaving the rest\n * intact. Returns true if the file existed. */\nexport function removeHarnessConfig(file: string): boolean {\n if (!existsSync(file)) return false;\n let config: Record<string, unknown>;\n try { config = readHarnessConfig(file); } catch { return true; } // unreadable: leave it untouched\n const hooks = isRecord(config.hooks) ? config.hooks : {};\n for (const [event, value] of Object.entries(hooks)) {\n if (Array.isArray(value)) (hooks as Record<string, unknown[]>)[event] = value.filter((e) => !entryMatches(e));\n }\n writeFileSync(file, JSON.stringify(config, null, 2) + \"\\n\");\n return true;\n}\n\n/** Detect a Cursor install by its user config directory. */\nexport function cursorDetected(): boolean {\n return existsSync(join(homedir(), \".cursor\"));\n}\n\n/** Detect a Codex install by its user config directory. */\nexport function codexDetected(): boolean {\n return existsSync(join(homedir(), \".codex\"));\n}\n\n/** The absolute command a harness entry runs: the current Node executable and the\n * absolute path to this CLI, so it does not depend on PATH.\n *\n * Both paths are always quoted on Windows, even without a space in them. A harness\n * runs this string through a shell, and that shell may be bash (Git Bash, WSL, a\n * dev container), where an unquoted Windows path loses every backslash and the\n * hook dies with MODULE_NOT_FOUND. Inside double quotes bash leaves a backslash\n * alone unless it precedes $ ` \" \\ or a newline — none of which occur in a Windows\n * path — and cmd.exe and PowerShell accept quoted paths too. */\nexport function absoluteHookCommand(cliPath: string, harness: Harness): string {\n const quote = (s: string) => (process.platform === \"win32\" || /\\s/.test(s) ? `\"${s}\"` : s);\n return `${quote(process.execPath)} ${quote(cliPath)} ${harness}`;\n}\n\nexport function isHarnessConfigured(file: string): boolean {\n if (!existsSync(file)) return false;\n try {\n const p = JSON.parse(readFileSync(file, \"utf8\"));\n if (!isRecord(p) || !isRecord(p.hooks)) return false;\n return Object.values(p.hooks).some((v) => Array.isArray(v) && v.some(entryMatches));\n } catch { return false; }\n}\n\n/** Every Scopebond command string a harness config currently runs. `doctor` uses it to\n * check that a pinned command still resolves: a hook entry that cannot start is worse\n * than none, because a harness can read the failure as \"no hook\". */\nexport function configuredHookCommands(file: string): string[] {\n if (!existsSync(file)) return [];\n const found: string[] = [];\n const collect = (entry: unknown): void => {\n if (!isRecord(entry)) return;\n if (isScopebond(entry.command)) found.push(String(entry.command));\n if (Array.isArray(entry.hooks)) for (const h of entry.hooks) collect(h);\n };\n try {\n const p = JSON.parse(readFileSync(file, \"utf8\"));\n if (!isRecord(p) || !isRecord(p.hooks)) return [];\n for (const list of Object.values(p.hooks)) if (Array.isArray(list)) for (const e of list) collect(e);\n } catch { return []; }\n return found;\n}\n\n/** Whether a configured hook command can actually start. An `npx` form always can\n * (npm resolves it); a pinned command names two absolute paths — the Node binary and\n * the CLI — and either can go away: a Node version manager switching versions, or the\n * Scopebond home being cleared. Both are exactly what this check exists to catch. */\nexport function hookCommandResolves(command: string): boolean {\n if (/(^|\\s)npx(\\.cmd)?\\s/.test(command)) return true;\n // `\"<node>\" \"<cli.js>\" claude`: check every token that names a file on disk.\n const tokens = command.match(/\"[^\"]+\"|\\S+/g) ?? [];\n let checked = 0;\n for (const raw of tokens) {\n const candidate = raw.replace(/^\"|\"$/g, \"\");\n if (!/\\.(m|c)?js$|\\.exe$/i.test(candidate)) continue;\n checked += 1;\n if (!existsSync(candidate)) return false;\n }\n // A bare `node cli.js` form on POSIX has no .exe to check; the .js still counted.\n return checked > 0 || !/[\\\\/]/.test(command.split(/\\s+/)[0] ?? \"\");\n}\n\n/** Remove the user-level home (keys, policy, receipts). */\nexport function purgeHome(): void {\n rmSync(userHome(), { recursive: true, force: true });\n}\n\n/** The Claude Code events the observation emitters listen to, beyond PreToolUse. */\nexport const LIFECYCLE_EVENTS = [\"SessionStart\", \"SessionEnd\", \"PostToolUse\", \"PostToolUseFailure\"] as const;\n\n/** Add (idempotently) the session lifecycle and after-action hook entries to a Claude Code\n * config file, running the same Scopebond command that already handles PreToolUse: the\n * command tells the events apart. Only Claude Code is wired: its lifecycle events are the\n * ones the hook's tested mapping covers. Nothing else in the file changes. */\nexport function wireLifecycleHooks(file: string, command: string): string {\n const config = readHarnessConfig(file);\n backupHarnessConfig(file);\n mkdirSync(dirname(file), { recursive: true });\n const hooks = isRecord(config.hooks) ? config.hooks : (config.hooks = {});\n for (const event of LIFECYCLE_EVENTS) {\n const list = Array.isArray((hooks as Record<string, unknown>)[event]) ? (hooks as Record<string, unknown[]>)[event] : ((hooks as Record<string, unknown[]>)[event] = []);\n const at = list.findIndex(entryMatches);\n const entry = event.startsWith(\"Session\")\n ? { hooks: [{ type: \"command\", command }] }\n : { matcher: \"*\", hooks: [{ type: \"command\", command }] };\n if (at >= 0) list[at] = entry; else list.push(entry);\n }\n writeFileSync(file, JSON.stringify(config, null, 2) + \"\\n\");\n return file;\n}\n\n/** Remove only the lifecycle entries `wireLifecycleHooks` added, leaving PreToolUse and every\n * other setting alone. Returns the number removed. */\nexport function unwireLifecycleHooks(file: string): number {\n if (!existsSync(file)) return 0;\n let config: Record<string, unknown>;\n try { config = readHarnessConfig(file); } catch { return 0; }\n const hooks = isRecord(config.hooks) ? config.hooks : null;\n if (!hooks) return 0;\n let removed = 0;\n for (const event of LIFECYCLE_EVENTS) {\n const value = (hooks as Record<string, unknown>)[event];\n if (!Array.isArray(value)) continue;\n const kept = value.filter((e) => !entryMatches(e));\n removed += value.length - kept.length;\n if (kept.length) (hooks as Record<string, unknown[]>)[event] = kept; else delete (hooks as Record<string, unknown>)[event];\n }\n if (removed > 0) writeFileSync(file, JSON.stringify(config, null, 2) + \"\\n\");\n return removed;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,8EAA8E;AAC9E,uFAAuF;AACvF,sFAAsF;AACtF,mFAAmF;AACnF,oFAAoF;AACpF,iFAAiF;AAEjF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnG,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIzE,gFAAgF;AAChF,MAAM,UAAU,QAAQ;IACtB,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;AACrE,CAAC;AAED;qEACqE;AACrE,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,IAAI,OAAO,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;IAC1E,IAAI,OAAO,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;IACxE,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;AACrD,CAAC;AAED;gEACgE;AAChE,MAAM,UAAU,kBAAkB,CAAC,OAAgB,EAAE,MAAc,OAAO,CAAC,GAAG,EAAE;IAC9E,IAAI,OAAO,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;IACpE,IAAI,OAAO,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;IAClE,OAAO,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;AAC/C,CAAC;AAED;;;mDAGmD;AACnD,MAAM,UAAU,gBAAgB,CAAC,OAAgB,EAAE,MAAc,OAAO,CAAC,GAAG,EAAE;IAC5E,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACnF,CAAC;AASD,MAAM,UAAU,aAAa,CAAC,OAAgB,EAAE,MAAc,OAAO,CAAC,GAAG,EAAE;IACzE,MAAM,OAAO,GAAG,kBAAkB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,gBAAgB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC7C,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IACtC,OAAO;QACL,OAAO,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;QACtD,KAAK,EAAE,KAAK,IAAI,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;QACzD,IAAI,EAAE,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;KAC9C,CAAC;AACJ,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,iBAAiB,CAAC,MAAqB;IACrD,MAAM,KAAK,GAAG;QACZ,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;QAC/B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE;QAClC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;KAC1B,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAClB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,2BAA2B,CAAC;IAC7E,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK;QAAE,OAAO,8CAA8C,CAAC;IAC9F,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI;QAAE,OAAO,yBAAyB,CAAC;IACxE,OAAO,eAAe,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;AAC7C,CAAC;AAED;iFACiF;AACjF,MAAM,UAAU,wBAAwB,CAAC,OAAe;IACtD,IAAI,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC9E,OAAO,UAAU,CAAC,KAAK,CAAC,IAAI,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC5D,CAAC;AAUD,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,MAAM,GAAG,GAAG,CAAC,IAAc,EAAU,EAAE;QACrC,IAAI,CAAC;YACH,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACpC,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACnE,OAAO,CAAC,CAAC;QACX,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,MAAM,GAAI,KAAoC,CAAC,MAAM,CAAC;YAC5D,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAClD,CAAC;IACH,CAAC,CAAC;IACF,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,uBAAuB,CAAC,CAAC,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC;IACrE,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,iBAAiB,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC7E,OAAO,GAAG,CAAC,CAAC,cAAc,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC;AACjF,CAAC;AAED;;0BAE0B;AAC1B,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC1B,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACpC,kFAAkF;QAClF,mFAAmF;QACnF,kFAAkF;QAClF,kEAAkE;QAClE,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,eAAe,CAAC,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACpH,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,EAAE,YAAY,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,YAAY,EAAE,cAAc,CAAC,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAChJ,MAAM,KAAK,GAAG,GAAG,GAAG,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1E,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7C,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACjD,aAAa,CAAC,OAAO,EAAE,GAAG,QAAQ,GAAG,QAAQ,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC,CAAC;QACrG,CAAC;QACD,OAAO,aAAa,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,KAAK,CAAC;IAAC,CAAC;AAC3B,CAAC;AAED;4DAC4D;AAC5D,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,MAAoC;IACpF,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IAChC,IAAI,MAA+B,CAAC;IACpC,IAAI,CAAC;QAAC,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,CAAC,CAAC;IAAC,CAAC;IAC7D,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IAC3D,IAAI,CAAC,KAAK;QAAE,OAAO,CAAC,CAAC;IACrB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,UAAU,GAAG,CAAC,CAAU,EAAY,EAAE;QAC1C,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,OAAO,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACzE,OAAO,CAAC,GAAG,GAAG,EAAE,GAAG,MAAM,CAAC,CAAC;IAC7B,CAAC,CAAC;IACF,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACnD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,SAAS;QACpC,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACnF,OAAO,IAAI,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QACrC,KAAmC,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IACrD,CAAC;IACD,IAAI,OAAO,GAAG,CAAC;QAAE,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC7E,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;sCAEsC;AACtC,MAAM,UAAU,mBAAmB;IACjC,OAAO,IAAI,CAAC,QAAQ,EAAE,EAAE,uBAAuB,CAAC,CAAC;AACnD,CAAC;AAED,SAAS,YAAY,CAAC,GAAW;IAC/B,IAAI,CAAC;QAAC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;AAC1H,CAAC;AAED,SAAS,WAAW;IAClB,IAAI,CAAC;QAAC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,mBAAmB,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;QAAC,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAA4B,CAAC,CAAC,CAAC,EAAE,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC;IAAC,CAAC;AAC1J,CAAC;AAED,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAU,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;AAEzF;uFACuF;AACvF,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAC5C,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,EAAE,CAAC,CAAC;IACzE,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;IAC9B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC;IAChC,SAAS,CAAC,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,aAAa,CAAC,mBAAmB,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC9E,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IACjC,OAAO,CAAC,CAAC,MAAM,IAAI,WAAW,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,MAAM,CAAC;AAC7D,CAAC;AAED;;;;;iFAKiF;AACjF,MAAM,UAAU,gBAAgB,CAAC,UAA8B;IAC7D,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;IAC1E,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,IAAI,UAAU;QAAE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC;IAChE,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB;QAAE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,YAAY,CAAC,CAAC,CAAC;IACxG,MAAM,IAAI,GAAG,QAAQ,EAAE,CAAC;IACxB,MAAM,aAAa,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC;IAC5D,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,IAAI,CAAC,CAAC,aAAa,IAAI,gBAAgB,CAAC,GAAG,CAAC,CAAC;YAAE,OAAO,GAAG,CAAC;IACpG,CAAC;IACD,IAAI,aAAa;QAAE,OAAO,IAAI,CAAC;IAC/B,0EAA0E;IAC1E,OAAO,UAAU,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC/B,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,sBAAsB,CAAC,UAA8B;IACnE,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,aAAa,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAChG,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAW,EAAE,YAAY,CAAC,CAAC,CAAC;IACtH,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC9F,CAAC;AAED,MAAM,QAAQ,GAAG,CAAC,CAAU,EAAgC,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAExH;;;;;;;;;;;;;;;6EAe6E;AAC7E,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;AACnE,iFAAiF;AACjF,MAAM,WAAW,GAAG,IAAI,CAAC;AACzB,MAAM,OAAO,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;AAE/E,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,GAAY,EAAW,EAAE;IAC9D,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,WAAW;QAAE,OAAO,KAAK,CAAC;IAC1F,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAChC,sFAAsF;IACtF,IAAI,KAAK,CAAC,QAAQ,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,kBAAkB,CAAC;QAAE,OAAO,IAAI,CAAC;IACzF,8CAA8C;IAC9C,IAAI,oCAAoC,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAChE,kFAAkF;IAClF,kDAAkD;IAClD,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACtD,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACjD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;QAC3B,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;QACvC,OAAO,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AACF,MAAM,WAAW,GAAG,sBAAsB,CAAC;AAE3C;kDACkD;AAClD,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAU,EAAW,EAAE,CACzD,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AACpI,MAAM,YAAY,GAAG,mBAAmB,CAAC;AAEzC;;iFAEiF;AACjF,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC;IAC5B,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAAC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,uBAAwB,KAAe,CAAC,OAAO,gEAAgE,CAAC,CAAC;IAC1I,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,oFAAoF,CAAC,CAAC;IACpI,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;4EAO4E;AAC5E,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,MAAM,MAAM,GAAG,GAAG,IAAI,mBAAmB,CAAC;IAC1C,IAAI,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,IAAI,CAAC;QAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAAC,OAAO,MAAM,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;AAC3E,CAAC;AAED;4FAC4F;AAC5F,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,OAAgB,EAAE,OAAe;IAChF,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChE,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;IAC1E,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzB,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,CAAC,CAAC;QACrC,KAAK,MAAM,KAAK,IAAI,CAAC,sBAAsB,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,eAAe,CAAC,EAAE,CAAC;YACtG,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAE,KAAiC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAE,KAAmC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAE,KAAmC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;YACzK,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;YACxC,MAAM,KAAK,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,EAAE,CAAC;YAClE,IAAI,EAAE,IAAI,CAAC;gBAAE,IAAI,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC;;gBAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAE,KAAiC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAE,KAAmC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAE,KAAmC,CAAC,UAAU,GAAG,EAAE,CAAC,CAAC;QACrL,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QACxC,iFAAiF;QACjF,sFAAsF;QACtF,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO;YAC/B,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,aAAa,EAAE,qCAAqC,EAAE,CAAC,EAAE;YAC9G,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QAC5D,IAAI,EAAE,IAAI,CAAC;YAAE,IAAI,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC;;YAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACvD,CAAC;IACD,qFAAqF;IACrF,oFAAoF;IACpF,0FAA0F;IAC1F,IAAI,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IACtE,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC1B,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC5D,OAAO,IAAI,CAAC;AACd,CAAC;AAED;gDACgD;AAChD,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACpC,IAAI,MAA+B,CAAC;IACpC,IAAI,CAAC;QAAC,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC,CAAC,iCAAiC;IAClG,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IACzD,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACnD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAAG,KAAmC,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAChH,CAAC;IACD,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC5D,OAAO,IAAI,CAAC;AACd,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,cAAc;IAC5B,OAAO,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC;AAChD,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,aAAa;IAC3B,OAAO,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED;;;;;;;;iEAQiE;AACjE,MAAM,UAAU,mBAAmB,CAAC,OAAe,EAAE,OAAgB;IACnE,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3F,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,OAAO,EAAE,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACpC,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;QACjD,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACrD,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;IACtF,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,KAAK,CAAC;IAAC,CAAC;AAC3B,CAAC;AAED;;sEAEsE;AACtE,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAG,CAAC,KAAc,EAAQ,EAAE;QACvC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO;QAC7B,IAAI,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAClE,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;YAAE,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK;gBAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAC1E,CAAC,CAAC;IACF,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;QACjD,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAClD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;YAAE,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;gBAAE,KAAK,MAAM,CAAC,IAAI,IAAI;oBAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACvG,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC;IAAC,CAAC;IACtB,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;sFAGsF;AACtF,MAAM,UAAU,mBAAmB,CAAC,OAAe;IACjD,IAAI,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IACrD,6EAA6E;IAC7E,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;IACnD,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QACzB,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAC5C,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,SAAS,CAAC;YAAE,SAAS;QACrD,OAAO,IAAI,CAAC,CAAC;QACb,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,KAAK,CAAC;IAC3C,CAAC;IACD,kFAAkF;IAClF,OAAO,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AACrE,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,SAAS;IACvB,MAAM,CAAC,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACvD,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,oBAAoB,CAAU,CAAC;AAE7G;;;+EAG+E;AAC/E,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,OAAe;IAC9D,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACvC,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC1B,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;IAC1E,KAAK,MAAM,KAAK,IAAI,gBAAgB,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAE,KAAiC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAE,KAAmC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAE,KAAmC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;QACzK,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QACxC,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC;YACvC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;YAC3C,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QAC5D,IAAI,EAAE,IAAI,CAAC;YAAE,IAAI,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC;;YAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACvD,CAAC;IACD,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC5D,OAAO,IAAI,CAAC;AACd,CAAC;AAED;uDACuD;AACvD,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IAChC,IAAI,MAA+B,CAAC;IACpC,IAAI,CAAC;QAAC,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,CAAC,CAAC;IAAC,CAAC;IAC7D,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IAC3D,IAAI,CAAC,KAAK;QAAE,OAAO,CAAC,CAAC;IACrB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,KAAK,IAAI,gBAAgB,EAAE,CAAC;QACrC,MAAM,KAAK,GAAI,KAAiC,CAAC,KAAK,CAAC,CAAC;QACxD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,SAAS;QACpC,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;QACnD,OAAO,IAAI,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QACtC,IAAI,IAAI,CAAC,MAAM;YAAG,KAAmC,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;;YAAM,OAAQ,KAAiC,CAAC,KAAK,CAAC,CAAC;IAC7H,CAAC;IACD,IAAI,OAAO,GAAG,CAAC;QAAE,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC7E,OAAO,OAAO,CAAC;AACjB,CAAC","sourcesContent":["// SB112 — the user-level installer. One install per developer machine (not per repo):\n// keys and a starter policy live in a user-level home (~/.scopebond, override\n// SCOPEBOND_HOME), and the hook is registered by absolute path in the user-level agent\n// config (~/.claude/settings.json, ~/.cursor/hooks.json, ~/.codex/hooks.json). When a\n// user-level install exists, a project-local `.scopebond` is used only if the user\n// trusted that exact policy (`scopebond trust`, or `init` in the project): a cloned\n// repository, or an agent inside it, must not be able to swap in its own policy.\n\nimport { execFileSync } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { mkdirSync, writeFileSync, existsSync, readFileSync, rmSync, copyFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { basename, dirname, isAbsolute, join, resolve } from \"node:path\";\n\nexport type Harness = \"claude\" | \"cursor\" | \"codex\";\n\n/** The user-level Scopebond home. `SCOPEBOND_HOME` overrides it (tests, CI). */\nexport function userHome(): string {\n return process.env.SCOPEBOND_HOME ?? join(homedir(), \".scopebond\");\n}\n\n/** The user-level agent config file for a harness. `HOME`/`USERPROFILE` (via homedir)\n * roots it; SCOPEBOND_HOME does not move the agent's own config. */\nexport function userHarnessFile(harness: Harness): string {\n if (harness === \"cursor\") return join(homedir(), \".cursor\", \"hooks.json\");\n if (harness === \"codex\") return join(homedir(), \".codex\", \"hooks.json\");\n return join(homedir(), \".claude\", \"settings.json\");\n}\n\n/** The project-level agent config file for a harness — what `init` writes, so the\n * hook travels with the repository rather than the machine. */\nexport function projectHarnessFile(harness: Harness, cwd: string = process.cwd()): string {\n if (harness === \"cursor\") return join(cwd, \".cursor\", \"hooks.json\");\n if (harness === \"codex\") return join(cwd, \".codex\", \"hooks.json\");\n return join(cwd, \".claude\", \"settings.json\");\n}\n\n/** The personal, per-machine project config for a harness, or null when the harness\n * has none. Claude Code reads `.claude/settings.local.json` beside `settings.json` and\n * merges the hooks of both; it is the place for anything that names a path on this\n * machine. Cursor and Codex have no such file. */\nexport function localHarnessFile(harness: Harness, cwd: string = process.cwd()): string | null {\n return harness === \"claude\" ? join(cwd, \".claude\", \"settings.local.json\") : null;\n}\n\n/** Which config files actually carry a Scopebond hook for this harness. There are three\n * places it can live — the shared project config, the personal project config (Claude\n * Code's `settings.local.json`, where `init` pins the fast command) and the user config\n * `install` writes — and `status`/`doctor` must look at all of them: checking only the\n * user one told everyone who ran `init` that their install had failed. */\nexport interface HarnessScopes { project: string | null; local: string | null; user: string | null }\n\nexport function harnessScopes(harness: Harness, cwd: string = process.cwd()): HarnessScopes {\n const project = projectHarnessFile(harness, cwd);\n const local = localHarnessFile(harness, cwd);\n const user = userHarnessFile(harness);\n return {\n project: isHarnessConfigured(project) ? project : null,\n local: local && isHarnessConfigured(local) ? local : null,\n user: isHarnessConfigured(user) ? user : null,\n };\n}\n\n/** One word for where a harness is wired, for a status line. */\nexport function harnessScopeLabel(scopes: HarnessScopes): string {\n const where = [\n scopes.project ? \"project\" : \"\",\n scopes.local ? \"this machine\" : \"\",\n scopes.user ? \"user\" : \"\",\n ].filter(Boolean);\n if (where.length === 0) return \"\";\n if (where.length === 1 && scopes.project) return \"configured (this project)\";\n if (where.length === 1 && scopes.local) return \"configured (this project, this machine only)\";\n if (where.length === 1 && scopes.user) return \"configured (user-level)\";\n return `configured (${where.join(\" + \")})`;\n}\n\n/** Whether a hook command names a path that exists only on this machine — the pinned\n * `\"<node>\" \"<…/cli.js>\" claude` form — rather than the portable `npx` form. */\nexport function isMachineSpecificCommand(command: string): boolean {\n if (/(^|\\s)npx(\\.cmd)?\\s/.test(command)) return false;\n const first = (command.match(/\"[^\"]+\"|\\S+/) ?? [\"\"])[0].replace(/^\"|\"$/g, \"\");\n return isAbsolute(first) || /^[A-Za-z]:[\\\\/]/.test(first);\n}\n\n/** How git treats a config file: committed (`tracked`), kept out (`ignored`), neither\n * yet (`untracked`, so a `git add .` would commit it), or not in a repository / no git\n * on this machine (`none`). A file git shares must never carry a machine-specific\n * command: on a teammate's machine the path does not exist, the command cannot start,\n * and Claude Code, Cursor and Codex all treat a hook that cannot start as a non-blocking\n * error — the agent runs with no check at all. */\nexport type GitShare = \"tracked\" | \"ignored\" | \"untracked\" | \"none\";\n\nexport function gitShareState(file: string): GitShare {\n const cwd = dirname(file);\n const git = (args: string[]): number => {\n try {\n mkdirSync(cwd, { recursive: true });\n execFileSync(\"git\", args, { cwd, stdio: \"ignore\", timeout: 5000 });\n return 0;\n } catch (error) {\n const status = (error as { status?: number | null }).status;\n return typeof status === \"number\" ? status : -1;\n }\n };\n if (git([\"rev-parse\", \"--is-inside-work-tree\"]) !== 0) return \"none\";\n if (git([\"ls-files\", \"--error-unmatch\", \"--\", file]) === 0) return \"tracked\";\n return git([\"check-ignore\", \"-q\", \"--\", file]) === 0 ? \"ignored\" : \"untracked\";\n}\n\n/** Keep a file out of git for this clone only, through `.git/info/exclude` — never the\n * repository's own `.gitignore`, which is the team's file. Returns true when the file is\n * ignored afterwards. */\nexport function excludeFromGit(file: string): boolean {\n try {\n const cwd = dirname(file);\n mkdirSync(cwd, { recursive: true });\n // The entry is built from git's own view of where this directory sits in the work\n // tree (`--show-prefix`), not from comparing file-system paths: the same directory\n // can be spelled two ways (a Windows 8.3 short name, a symlinked temp dir), and a\n // path computed across the two spellings excludes the wrong file.\n const prefix = execFileSync(\"git\", [\"rev-parse\", \"--show-prefix\"], { cwd, encoding: \"utf8\", timeout: 5000 }).trim();\n const exclude = resolve(cwd, execFileSync(\"git\", [\"rev-parse\", \"--git-path\", \"info/exclude\"], { cwd, encoding: \"utf8\", timeout: 5000 }).trim());\n const entry = \"/\" + prefix + basename(file);\n const existing = existsSync(exclude) ? readFileSync(exclude, \"utf8\") : \"\";\n if (!existing.split(/\\r?\\n/).includes(entry)) {\n mkdirSync(dirname(exclude), { recursive: true });\n writeFileSync(exclude, `${existing}${existing && !existing.endsWith(\"\\n\") ? \"\\n\" : \"\"}${entry}\\n`);\n }\n return gitShareState(file) === \"ignored\";\n } catch { return false; }\n}\n\n/** Remove the Scopebond entries whose command `remove` selects, leaving every other\n * setting alone. Returns the number of entries removed. */\nexport function pruneHarnessEntries(file: string, remove: (command: string) => boolean): number {\n if (!existsSync(file)) return 0;\n let config: Record<string, unknown>;\n try { config = readHarnessConfig(file); } catch { return 0; }\n const hooks = isRecord(config.hooks) ? config.hooks : null;\n if (!hooks) return 0;\n let removed = 0;\n const commandsOf = (e: unknown): string[] => {\n if (!isRecord(e)) return [];\n const own = isScopebond(e.command) ? [String(e.command)] : [];\n const nested = Array.isArray(e.hooks) ? e.hooks.flatMap(commandsOf) : [];\n return [...own, ...nested];\n };\n for (const [event, value] of Object.entries(hooks)) {\n if (!Array.isArray(value)) continue;\n const kept = value.filter((e) => !(entryMatches(e) && commandsOf(e).some(remove)));\n removed += value.length - kept.length;\n (hooks as Record<string, unknown[]>)[event] = kept;\n }\n if (removed > 0) writeFileSync(file, JSON.stringify(config, null, 2) + \"\\n\");\n return removed;\n}\n\n/** The file in the user home that pins trusted project policies: absolute project\n * config dir → SHA-256 of its policy.json. It lives under the protected home, so the\n * governed agent cannot write it. */\nexport function trustedProjectsFile(): string {\n return join(userHome(), \"trusted-projects.json\");\n}\n\nfunction policyDigest(dir: string): string | null {\n try { return createHash(\"sha256\").update(readFileSync(join(dir, \"policy.json\"))).digest(\"hex\"); } catch { return null; }\n}\n\nfunction readTrusted(): Record<string, string> {\n try { const p = JSON.parse(readFileSync(trustedProjectsFile(), \"utf8\")); return isRecord(p) ? (p as Record<string, string>) : {}; } catch { return {}; }\n}\n\nconst trustKey = (dir: string): string => resolve(dir).replace(/\\\\/g, \"/\").toLowerCase();\n\n/** Pin the project's current policy as trusted. Editing the policy afterwards un-trusts\n * it until the user trusts it again, so a rewrite by the agent never takes effect. */\nexport function trustProjectPolicy(dir: string): string {\n const digest = policyDigest(dir);\n if (!digest) throw new Error(`no policy at ${join(dir, \"policy.json\")}`);\n const trusted = readTrusted();\n trusted[trustKey(dir)] = digest;\n mkdirSync(userHome(), { recursive: true });\n writeFileSync(trustedProjectsFile(), JSON.stringify(trusted, null, 2) + \"\\n\");\n return digest;\n}\n\n/** Whether the project's policy is exactly the one the user trusted. */\nexport function isTrustedProject(dir: string): boolean {\n const digest = policyDigest(dir);\n return !!digest && readTrusted()[trustKey(dir)] === digest;\n}\n\n/** Resolve the config dir for a hook event, most specific first:\n * explicit override → the payload's project → $CLAUDE_PROJECT_DIR → the user home.\n * A project dir counts only when it has been scaffolded (a policy.json), so a bare\n * working directory never shadows the user-level install — and, once a user-level\n * install exists, only when its policy is trusted (`isTrustedProject`). Otherwise the\n * user's own policy governs: a repository cannot bring a weaker one with it. */\nexport function resolveConfigDir(payloadCwd: string | undefined): string {\n if (process.env.SCOPEBOND_HOOK_DIR) return process.env.SCOPEBOND_HOOK_DIR;\n const candidates: string[] = [];\n if (payloadCwd) candidates.push(join(payloadCwd, \".scopebond\"));\n if (process.env.CLAUDE_PROJECT_DIR) candidates.push(join(process.env.CLAUDE_PROJECT_DIR, \".scopebond\"));\n const home = userHome();\n const homeInstalled = existsSync(join(home, \"policy.json\"));\n for (const dir of candidates) {\n if (existsSync(join(dir, \"policy.json\")) && (!homeInstalled || isTrustedProject(dir))) return dir;\n }\n if (homeInstalled) return home;\n // Nothing scaffolded yet: prefer the payload project, else the user home.\n return candidates[0] ?? home;\n}\n\n/** A project policy present but ignored because it is not trusted (for status/doctor). */\nexport function untrustedProjectPolicy(payloadCwd: string | undefined): string | null {\n if (process.env.SCOPEBOND_HOOK_DIR || !existsSync(join(userHome(), \"policy.json\"))) return null;\n const dirs = [payloadCwd, process.env.CLAUDE_PROJECT_DIR].filter(Boolean).map((d) => join(d as string, \".scopebond\"));\n return dirs.find((d) => existsSync(join(d, \"policy.json\")) && !isTrustedProject(d)) ?? null;\n}\n\nconst isRecord = (v: unknown): v is Record<string, unknown> => typeof v === \"object\" && v !== null && !Array.isArray(v);\n\n/** Whether a harness command string is one of ours. This is the single matcher for\n * every form the installer has ever written, and it is what makes re-running `init`\n * or `install` replace the entry instead of appending a second one:\n * - `npx -y @scopebond/hook@0.6.0 claude` (portable)\n * - `\"<node>\" \"<…/@scopebond/hook/dist/cli.js>\" claude` (pinned, POSIX)\n * - `\"<node>\" \"<…\\@scopebond\\hook\\dist\\cli.js>\" claude` (pinned, Windows)\n * - `scopebond-hook claude` (legacy global binary)\n * The separator class matters: a Windows pinned path spells the scope\n * `@scopebond\\hook`, and matching only `@scopebond/hook` let a second `init`\n * install a duplicate hook — which would double-check every tool call.\n *\n * Written with substring and token tests rather than one regular expression: the\n * previous `(^|[\"\\s])scopebond([\"'\\s]).*\\b(claude|cursor|codex)\\b` backtracked\n * polynomially on a long run of `\\tscopebond\\t` (CodeQL js/polynomial-redos). The\n * input is a command out of a config file rather than a request, so this was not\n * reachable by an attacker — but a linear check is also the clearer one. */\nconst HARNESS_SUBCOMMANDS = new Set([\"claude\", \"cursor\", \"codex\"]);\n/** Long enough for any real command; a bound keeps the work linear and small. */\nconst MAX_COMMAND = 4096;\nconst unquote = (token: string): string => token.replace(/^[\"']+|[\"']+$/g, \"\");\n\nexport const isScopebondHookCommand = (cmd: unknown): boolean => {\n if (typeof cmd !== \"string\" || cmd.length === 0 || cmd.length > MAX_COMMAND) return false;\n const lower = cmd.toLowerCase();\n // The package itself, in either path spelling (`@scopebond/hook`, `@scopebond\\hook`).\n if (lower.includes(\"@scopebond/hook\") || lower.includes(\"@scopebond\\\\hook\")) return true;\n // The legacy global binary, as a whole token.\n if (/(^|[\\s\"'])scopebond-hook([\\s\"']|$)/.test(cmd)) return true;\n // An absolute or bare form whose last argument is one of our harness subcommands:\n // `<node> <…/cli.js> claude`, `scopebond cursor`.\n const tokens = cmd.split(/\\s+/).filter(Boolean);\n const last = unquote(tokens[tokens.length - 1] ?? \"\");\n if (!HARNESS_SUBCOMMANDS.has(last)) return false;\n return tokens.some((token) => {\n const t = unquote(token).toLowerCase();\n return t.includes(\"scopebond\") || /cli\\.(m|c)?js$/.test(t);\n });\n};\nconst isScopebond = isScopebondHookCommand;\n\n/** Whether a harness hook entry (either shape: a bare command, or a group with a\n * nested `hooks` array) is a Scopebond entry. */\nexport const harnessEntryMatches = (e: unknown): boolean =>\n isRecord(e) && (isScopebond(e.command) || (Array.isArray(e.hooks) && e.hooks.some((h) => isRecord(h) && isScopebond(h.command))));\nconst entryMatches = harnessEntryMatches;\n\n/** Read an agent's JSON config for merging. A missing file is an empty config; a file\n * that exists but does not parse as a JSON object is an error — rewriting it would\n * silently delete the user's other settings, so the installer stops instead. */\nexport function readHarnessConfig(file: string): Record<string, unknown> {\n if (!existsSync(file)) return {};\n const text = readFileSync(file, \"utf8\");\n if (!text.trim()) return {};\n let parsed: unknown;\n try { parsed = JSON.parse(text); } catch (error) {\n throw new Error(`${file} is not valid JSON (${(error as Error).message}); fix or move it, then run this again — it was left unchanged`);\n }\n if (!isRecord(parsed)) throw new Error(`${file} is not a JSON object; fix or move it, then run this again — it was left unchanged`);\n return parsed;\n}\n\n/** Copy an agent config aside before changing it, once. Re-running an installer must not\n * overwrite the backup with the already-modified file, or the original is lost on the\n * second run — so an existing backup is left alone. Returns the backup path when one was\n * written.\n *\n * This exists because these installers rewrite files the user did not create and may not\n * know are involved (`~/.claude/settings.json` holds their theme, plugins and\n * permissions), and the undo was previously \"hope the merge was right\". */\nexport function backupHarnessConfig(file: string): string | null {\n if (!existsSync(file)) return null;\n const backup = `${file}.scopebond-backup`;\n if (existsSync(backup)) return null;\n try { copyFileSync(file, backup); return backup; } catch { return null; }\n}\n\n/** Write (idempotently) a harness hook entry pointing at an explicit command string.\n * Shared by the project installer (npx command) and the user installer (absolute path). */\nexport function writeHarnessConfig(file: string, harness: Harness, command: string): string {\n const config = readHarnessConfig(file);\n const before = existsSync(file) ? JSON.stringify(config) : null;\n const hooks = isRecord(config.hooks) ? config.hooks : (config.hooks = {});\n if (harness === \"cursor\") {\n config.version = config.version ?? 1;\n for (const event of [\"beforeShellExecution\", \"beforeMCPExecution\", \"beforeReadFile\", \"afterFileEdit\"]) {\n const list = Array.isArray((hooks as Record<string, unknown>)[event]) ? (hooks as Record<string, unknown[]>)[event] : ((hooks as Record<string, unknown[]>)[event] = []);\n const at = list.findIndex(entryMatches);\n const entry = { command: command.replace(/\\bclaude$/, \"cursor\") };\n if (at >= 0) list[at] = entry; else list.push(entry);\n }\n } else {\n const list = Array.isArray((hooks as Record<string, unknown>).PreToolUse) ? (hooks as Record<string, unknown[]>).PreToolUse : ((hooks as Record<string, unknown[]>).PreToolUse = []);\n const at = list.findIndex(entryMatches);\n // Codex matchers are regular expressions; omitting the matcher means every tool.\n // A literal \"*\" is not a valid regular expression and causes Codex to skip the group.\n const entry = harness === \"codex\"\n ? { hooks: [{ type: \"command\", command, timeout: 30, statusMessage: \"Checking this action with Scopebond\" }] }\n : { matcher: \"*\", hooks: [{ type: \"command\", command }] };\n if (at >= 0) list[at] = entry; else list.push(entry);\n }\n // SB276: a settings file that already holds exactly this entry is left byte-for-byte\n // untouched — no rewrite, no backup. A running agent session watches this file, and\n // rewriting it (even with the same content) is what disturbed a live session on re-login.\n if (before !== null && JSON.stringify(config) === before) return file;\n backupHarnessConfig(file);\n mkdirSync(dirname(file), { recursive: true });\n writeFileSync(file, JSON.stringify(config, null, 2) + \"\\n\");\n return file;\n}\n\n/** Remove any Scopebond hook entry from a user-level harness config, leaving the rest\n * intact. Returns true if the file existed. */\nexport function removeHarnessConfig(file: string): boolean {\n if (!existsSync(file)) return false;\n let config: Record<string, unknown>;\n try { config = readHarnessConfig(file); } catch { return true; } // unreadable: leave it untouched\n const hooks = isRecord(config.hooks) ? config.hooks : {};\n for (const [event, value] of Object.entries(hooks)) {\n if (Array.isArray(value)) (hooks as Record<string, unknown[]>)[event] = value.filter((e) => !entryMatches(e));\n }\n writeFileSync(file, JSON.stringify(config, null, 2) + \"\\n\");\n return true;\n}\n\n/** Detect a Cursor install by its user config directory. */\nexport function cursorDetected(): boolean {\n return existsSync(join(homedir(), \".cursor\"));\n}\n\n/** Detect a Codex install by its user config directory. */\nexport function codexDetected(): boolean {\n return existsSync(join(homedir(), \".codex\"));\n}\n\n/** The absolute command a harness entry runs: the current Node executable and the\n * absolute path to this CLI, so it does not depend on PATH.\n *\n * Both paths are always quoted on Windows, even without a space in them. A harness\n * runs this string through a shell, and that shell may be bash (Git Bash, WSL, a\n * dev container), where an unquoted Windows path loses every backslash and the\n * hook dies with MODULE_NOT_FOUND. Inside double quotes bash leaves a backslash\n * alone unless it precedes $ ` \" \\ or a newline — none of which occur in a Windows\n * path — and cmd.exe and PowerShell accept quoted paths too. */\nexport function absoluteHookCommand(cliPath: string, harness: Harness): string {\n const quote = (s: string) => (process.platform === \"win32\" || /\\s/.test(s) ? `\"${s}\"` : s);\n return `${quote(process.execPath)} ${quote(cliPath)} ${harness}`;\n}\n\nexport function isHarnessConfigured(file: string): boolean {\n if (!existsSync(file)) return false;\n try {\n const p = JSON.parse(readFileSync(file, \"utf8\"));\n if (!isRecord(p) || !isRecord(p.hooks)) return false;\n return Object.values(p.hooks).some((v) => Array.isArray(v) && v.some(entryMatches));\n } catch { return false; }\n}\n\n/** Every Scopebond command string a harness config currently runs. `doctor` uses it to\n * check that a pinned command still resolves: a hook entry that cannot start is worse\n * than none, because a harness can read the failure as \"no hook\". */\nexport function configuredHookCommands(file: string): string[] {\n if (!existsSync(file)) return [];\n const found: string[] = [];\n const collect = (entry: unknown): void => {\n if (!isRecord(entry)) return;\n if (isScopebond(entry.command)) found.push(String(entry.command));\n if (Array.isArray(entry.hooks)) for (const h of entry.hooks) collect(h);\n };\n try {\n const p = JSON.parse(readFileSync(file, \"utf8\"));\n if (!isRecord(p) || !isRecord(p.hooks)) return [];\n for (const list of Object.values(p.hooks)) if (Array.isArray(list)) for (const e of list) collect(e);\n } catch { return []; }\n return found;\n}\n\n/** Whether a configured hook command can actually start. An `npx` form always can\n * (npm resolves it); a pinned command names two absolute paths — the Node binary and\n * the CLI — and either can go away: a Node version manager switching versions, or the\n * Scopebond home being cleared. Both are exactly what this check exists to catch. */\nexport function hookCommandResolves(command: string): boolean {\n if (/(^|\\s)npx(\\.cmd)?\\s/.test(command)) return true;\n // `\"<node>\" \"<cli.js>\" claude`: check every token that names a file on disk.\n const tokens = command.match(/\"[^\"]+\"|\\S+/g) ?? [];\n let checked = 0;\n for (const raw of tokens) {\n const candidate = raw.replace(/^\"|\"$/g, \"\");\n if (!/\\.(m|c)?js$|\\.exe$/i.test(candidate)) continue;\n checked += 1;\n if (!existsSync(candidate)) return false;\n }\n // A bare `node cli.js` form on POSIX has no .exe to check; the .js still counted.\n return checked > 0 || !/[\\\\/]/.test(command.split(/\\s+/)[0] ?? \"\");\n}\n\n/** Remove the user-level home (keys, policy, receipts). */\nexport function purgeHome(): void {\n rmSync(userHome(), { recursive: true, force: true });\n}\n\n/** The Claude Code events the observation emitters listen to, beyond PreToolUse. */\nexport const LIFECYCLE_EVENTS = [\"SessionStart\", \"SessionEnd\", \"PostToolUse\", \"PostToolUseFailure\"] as const;\n\n/** Add (idempotently) the session lifecycle and after-action hook entries to a Claude Code\n * config file, running the same Scopebond command that already handles PreToolUse: the\n * command tells the events apart. Only Claude Code is wired: its lifecycle events are the\n * ones the hook's tested mapping covers. Nothing else in the file changes. */\nexport function wireLifecycleHooks(file: string, command: string): string {\n const config = readHarnessConfig(file);\n backupHarnessConfig(file);\n mkdirSync(dirname(file), { recursive: true });\n const hooks = isRecord(config.hooks) ? config.hooks : (config.hooks = {});\n for (const event of LIFECYCLE_EVENTS) {\n const list = Array.isArray((hooks as Record<string, unknown>)[event]) ? (hooks as Record<string, unknown[]>)[event] : ((hooks as Record<string, unknown[]>)[event] = []);\n const at = list.findIndex(entryMatches);\n const entry = event.startsWith(\"Session\")\n ? { hooks: [{ type: \"command\", command }] }\n : { matcher: \"*\", hooks: [{ type: \"command\", command }] };\n if (at >= 0) list[at] = entry; else list.push(entry);\n }\n writeFileSync(file, JSON.stringify(config, null, 2) + \"\\n\");\n return file;\n}\n\n/** Remove only the lifecycle entries `wireLifecycleHooks` added, leaving PreToolUse and every\n * other setting alone. Returns the number removed. */\nexport function unwireLifecycleHooks(file: string): number {\n if (!existsSync(file)) return 0;\n let config: Record<string, unknown>;\n try { config = readHarnessConfig(file); } catch { return 0; }\n const hooks = isRecord(config.hooks) ? config.hooks : null;\n if (!hooks) return 0;\n let removed = 0;\n for (const event of LIFECYCLE_EVENTS) {\n const value = (hooks as Record<string, unknown>)[event];\n if (!Array.isArray(value)) continue;\n const kept = value.filter((e) => !entryMatches(e));\n removed += value.length - kept.length;\n if (kept.length) (hooks as Record<string, unknown[]>)[event] = kept; else delete (hooks as Record<string, unknown>)[event];\n }\n if (removed > 0) writeFileSync(file, JSON.stringify(config, null, 2) + \"\\n\");\n return removed;\n}\n"]}
|
package/dist/managed.d.ts
CHANGED
|
@@ -5,9 +5,10 @@ export declare const PREVIOUS_POLICY_FILE = "policy.previous.json";
|
|
|
5
5
|
export declare const MANAGED_RULE_IDS: readonly ["force-push-protected", "push-protected", "destructive-shell", "secret-read", "ci-config-write", "network-egress"];
|
|
6
6
|
export type ManagedRuleId = (typeof MANAGED_RULE_IDS)[number];
|
|
7
7
|
type ListKey = "protected_branches" | "destructive_programs" | "allowed_hosts";
|
|
8
|
+
type ExclusionKey = "excluded_paths" | "excluded_branches";
|
|
8
9
|
export type ManagedRule = {
|
|
9
10
|
mode: "monitor" | "block";
|
|
10
|
-
} & Partial<Record<ListKey, string[]>>;
|
|
11
|
+
} & Partial<Record<ListKey | ExclusionKey, string[]>>;
|
|
11
12
|
export interface ManagedDocument {
|
|
12
13
|
type: "scopebond:managed-rules";
|
|
13
14
|
version: 1;
|
|
@@ -33,6 +34,7 @@ export declare const digestRules: (rules: unknown) => string;
|
|
|
33
34
|
export declare function inspectManaged(raw: unknown, context: {
|
|
34
35
|
installationId: string;
|
|
35
36
|
currentRevision: number | null;
|
|
37
|
+
currentDigest?: string | null;
|
|
36
38
|
}): Inspection;
|
|
37
39
|
/** The policy this computer enforces under workspace rules: its own compiled rules, adjusted rule by rule. Pure. */
|
|
38
40
|
export declare function compileManaged(local: RuleSet, doc: ManagedDocument, agentKid: string): Record<string, unknown>;
|
package/dist/managed.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"managed.d.ts","sourceRoot":"","sources":["../src/managed.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"managed.d.ts","sourceRoot":"","sources":["../src/managed.ts"],"names":[],"mappings":"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"}
|
package/dist/managed.js
CHANGED
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
// to the computer's own lists (protected branches, programs, allowed sites). It never sends a pattern: this module compiles the
|
|
6
6
|
// workspace's choices with the same compiler `rules apply` uses, so what decides a block lives in one place.
|
|
7
7
|
//
|
|
8
|
+
// A workspace may also name exact targets one rule skips: paths for the secret-read and CI-configuration rules, branches for the
|
|
9
|
+
// push rule. An exclusion matches exactly (same case; a trailing `/**` covers a folder), so it is never broader than what was typed,
|
|
10
|
+
// and an exclusion that would touch the always-on protection of the hook's own settings is dropped when the policy is compiled.
|
|
11
|
+
//
|
|
8
12
|
// What a workspace cannot change: the protection of the hook's own settings (and the settings of the agents it guards), the
|
|
9
13
|
// machine key policy, fail-closed handling of anything unparseable, and the computer's own opt-in extras (`allowed_roots`,
|
|
10
14
|
// `protect_remote_database`). A rule set to Monitor is still covered by an allow clause, so the action is recorded and passes;
|
|
@@ -17,7 +21,7 @@ import { copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync,
|
|
|
17
21
|
import { join } from "node:path";
|
|
18
22
|
import { createHash, randomBytes } from "node:crypto";
|
|
19
23
|
import { canonical } from "@scopebond/policy-schema/canonical";
|
|
20
|
-
import { GUARDRAIL_WRITE_PATTERN, ci } from "./runtime.js";
|
|
24
|
+
import { GUARDRAIL_LOOKAHEADS, GUARDRAIL_WRITE_PATTERN, ci } from "./runtime.js";
|
|
21
25
|
import { compile, defaultRules, loadRules } from "./rules.js";
|
|
22
26
|
export const MANAGED_DOC_FILE = "managed-rules.json";
|
|
23
27
|
export const MANAGED_META_FILE = "managed-meta.json";
|
|
@@ -32,6 +36,16 @@ const LIST_PATTERN = {
|
|
|
32
36
|
destructive_programs: /^[a-z0-9][a-z0-9._-]{0,63}$/,
|
|
33
37
|
allowed_hosts: /^(?=.{1,253}$)(\*\.)?([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/,
|
|
34
38
|
};
|
|
39
|
+
/** The exact targets a rule may skip, and the one list each rule accepts. */
|
|
40
|
+
const EXCLUSIONS_OF = {
|
|
41
|
+
"secret-read": "excluded_paths", "ci-config-write": "excluded_paths", "push-protected": "excluded_branches",
|
|
42
|
+
};
|
|
43
|
+
const EXCLUSION_PATTERN = {
|
|
44
|
+
// A relative path with forward slashes: no empty, "." or ".." segment, optionally ending in /** for a folder.
|
|
45
|
+
excluded_paths: /^(?!\/)(?!.*\/\/)(?!(?:.*\/)?\.{1,2}(?:\/|$))[A-Za-z0-9._@+-][A-Za-z0-9._@+/-]{0,199}?(?:\/\*\*)?$/,
|
|
46
|
+
// One exact branch name; it starts with a letter or digit, so it can never be a push flag such as --all.
|
|
47
|
+
excluded_branches: /^[A-Za-z0-9][A-Za-z0-9._/-]{0,99}$/,
|
|
48
|
+
};
|
|
35
49
|
const HEX64 = /^[0-9a-f]{64}$/;
|
|
36
50
|
const OPAQUE = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
37
51
|
export const digestRules = (rules) => createHash("sha256").update(canonical(rules)).digest("hex");
|
|
@@ -65,10 +79,12 @@ export function inspectManaged(raw, context) {
|
|
|
65
79
|
if (key === "mode")
|
|
66
80
|
continue;
|
|
67
81
|
const listKey = LIST_OF[id];
|
|
68
|
-
|
|
82
|
+
const exclusionKey = EXCLUSIONS_OF[id];
|
|
83
|
+
const pattern = key === listKey ? LIST_PATTERN[listKey] : key === exclusionKey ? EXCLUSION_PATTERN[exclusionKey] : null;
|
|
84
|
+
if (!pattern)
|
|
69
85
|
return bad(`rule ${id} has an unknown setting`);
|
|
70
86
|
const list = rule[key];
|
|
71
|
-
if (!Array.isArray(list) || list.length > 50 || !list.every((v) => typeof v === "string" &&
|
|
87
|
+
if (!Array.isArray(list) || list.length > 50 || !list.every((v) => typeof v === "string" && pattern.test(v) && !v.includes(".."))) {
|
|
72
88
|
return bad(`rule ${id} has an invalid list`);
|
|
73
89
|
}
|
|
74
90
|
}
|
|
@@ -78,7 +94,10 @@ export function inspectManaged(raw, context) {
|
|
|
78
94
|
return bad("network blocking needs at least one allowed site");
|
|
79
95
|
if (digestRules(rules) !== rulesDigest)
|
|
80
96
|
return bad("the rules do not match their digest; the document was changed or damaged");
|
|
81
|
-
|
|
97
|
+
// The same version with different rules is a newer document too: the workspace can change what reaches this computer without a new
|
|
98
|
+
// version (an agent moved to another team, or this hook started reading a setting it did not read before). The export id binds it here.
|
|
99
|
+
const sameVersionChanged = context.currentRevision !== null && revision === context.currentRevision && !!context.currentDigest && rulesDigest !== context.currentDigest;
|
|
100
|
+
if (context.currentRevision !== null && revision <= context.currentRevision && !sameVersionChanged) {
|
|
82
101
|
return { ok: false, reason: "stale_revision", message: `version ${String(revision)} is not newer than the version in force (${context.currentRevision})` };
|
|
83
102
|
}
|
|
84
103
|
return { ok: true, doc: raw };
|
|
@@ -86,6 +105,19 @@ export function inspectManaged(raw, context) {
|
|
|
86
105
|
const union = (a, b) => [...new Set([...a, ...(b ?? [])])];
|
|
87
106
|
const branchGlob = (b) => (b.endsWith("/*") ? `${b.slice(0, -1)}**` : b);
|
|
88
107
|
const hostPattern = (hosts) => `^(?:${hosts.map((h) => (h.startsWith("*.") ? `(?:[^.]+\\.)+${ci(h.slice(2).replace(/\./g, "\\."))}` : ci(h.replace(/\./g, "\\.")))).join("|")})$`;
|
|
108
|
+
const escapeRegex = (text) => text.replace(/[\\^$.*+?()[\]{}|/]/g, "\\$&");
|
|
109
|
+
/** Exclusions that passed validation (the document check refuses the rest; this is a second line of defence). */
|
|
110
|
+
const safeExclusions = (paths) => (paths ?? []).filter((path) => EXCLUSION_PATTERN.excluded_paths.test(path) && !path.includes(".."));
|
|
111
|
+
/** An allow pattern that also allows exactly these paths (a trailing /** allows everything under the folder). The added alternative
|
|
112
|
+
* carries the always-on floor and refuses any path with a "." or ".." segment, so no exclusion — however it is written — can reach
|
|
113
|
+
* Scopebond's own settings or the agents' hook settings, or climb out of the folder it names. */
|
|
114
|
+
const withPathExclusions = (pattern, paths) => paths.length === 0 ? pattern
|
|
115
|
+
: `^(?!(?:.*/)?\\.{1,2}(?:/|$))${GUARDRAIL_LOOKAHEADS}(?:${paths.map((p) => p.endsWith("/**") ? `${escapeRegex(p.slice(0, -3))}/.+` : escapeRegex(p)).join("|")})$|${pattern}`;
|
|
116
|
+
/** An allow pattern for pushes that also allows exactly these branches. */
|
|
117
|
+
const withBranchExclusions = (pattern, branches) => branches.length === 0 ? pattern
|
|
118
|
+
: `^(?:refs/heads/)?(?:${branches.map(escapeRegex).join("|")})$|${pattern}`;
|
|
119
|
+
const boundPattern = (clause, key) => String((clause.param_bounds?.[key])?.pattern ?? "");
|
|
120
|
+
const withBound = (clause, key, pattern) => ({ ...clause, param_bounds: { ...clause.param_bounds, [key]: { pattern } } });
|
|
89
121
|
/** The policy this computer enforces under workspace rules: its own compiled rules, adjusted rule by rule. Pure. */
|
|
90
122
|
export function compileManaged(local, doc, agentKid) {
|
|
91
123
|
const r = doc.rules;
|
|
@@ -113,6 +145,20 @@ export function compileManaged(local, doc, agentKid) {
|
|
|
113
145
|
clauses.push({ ...clause, param_bounds: { path: { pattern: GUARDRAIL_WRITE_PATTERN } },
|
|
114
146
|
description: "Allow workspace writes, but never to Scopebond's own settings or the agents' hook settings (always on). Other protected files are recorded, not blocked: set by your workspace." });
|
|
115
147
|
}
|
|
148
|
+
else if (clause.id === "protect-read" && r["secret-read"].excluded_paths?.length) {
|
|
149
|
+
clauses.push(withBound(clause, "path", withPathExclusions(boundPattern(clause, "path"), safeExclusions(r["secret-read"].excluded_paths))));
|
|
150
|
+
}
|
|
151
|
+
else if (clause.id === "protect-write" && r["ci-config-write"].excluded_paths?.length) {
|
|
152
|
+
clauses.push(withBound(clause, "path", withPathExclusions(boundPattern(clause, "path"), safeExclusions(r["ci-config-write"].excluded_paths))));
|
|
153
|
+
}
|
|
154
|
+
else if (clause.id === "protect-branches" && r["push-protected"].excluded_branches?.length) {
|
|
155
|
+
clauses.push(withBound(clause, "ref", withBranchExclusions(boundPattern(clause, "ref"), r["push-protected"].excluded_branches)));
|
|
156
|
+
// Skipping ordinary pushes to a branch never skips history protection: force-pushes, deletions and mirror pushes stay stopped.
|
|
157
|
+
if (r["force-push-protected"].mode === "block") {
|
|
158
|
+
clauses.push({ id: "protect-branch-history", type: "force_push_guard", mode: "enforce", protected_refs: rules.protected_branches.map(branchGlob),
|
|
159
|
+
description: "Deny force-pushes, deletions and mirror pushes that reach a protected branch. Set by your workspace." });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
116
162
|
else
|
|
117
163
|
clauses.push(clause);
|
|
118
164
|
}
|
package/dist/managed.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"managed.js","sourceRoot":"","sources":["../src/managed.ts"],"names":[],"mappings":"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","sourcesContent":["// Rules managed from a Scopebond Cloud workspace.\n//\n// A connected computer keeps its own rules (`rules.json` compiled into `policy.json`) until the workspace sets rules for it.\n// From then on the workspace decides, rule by rule, whether a matching action is blocked or only recorded, and may add entries\n// to the computer's own lists (protected branches, programs, allowed sites). It never sends a pattern: this module compiles the\n// workspace's choices with the same compiler `rules apply` uses, so what decides a block lives in one place.\n//\n// What a workspace cannot change: the protection of the hook's own settings (and the settings of the agents it guards), the\n// machine key policy, fail-closed handling of anything unparseable, and the computer's own opt-in extras (`allowed_roots`,\n// `protect_remote_database`). A rule set to Monitor is still covered by an allow clause, so the action is recorded and passes;\n// leaving it out would block the whole action type, because the verifier denies an action no clause covers.\n//\n// Loading is checked and atomic: the document must be well formed, name this computer, carry a matching digest and be newer\n// than the one in force; the merged policy must build; policy.json is written through a temp file and a rename, the previous\n// one kept. Disconnecting, or a workspace that stops managing this computer, restores the computer's own rules.\n\nimport { copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { createHash, randomBytes } from \"node:crypto\";\nimport { canonical } from \"@scopebond/policy-schema/canonical\";\nimport { GUARDRAIL_WRITE_PATTERN, ci } from \"./runtime.js\";\nimport { compile, defaultRules, loadRules, type RuleSet } from \"./rules.js\";\n\nexport const MANAGED_DOC_FILE = \"managed-rules.json\";\nexport const MANAGED_META_FILE = \"managed-meta.json\";\nexport const PREVIOUS_POLICY_FILE = \"policy.previous.json\";\n\nexport const MANAGED_RULE_IDS = [\"force-push-protected\", \"push-protected\", \"destructive-shell\", \"secret-read\", \"ci-config-write\", \"network-egress\"] as const;\nexport type ManagedRuleId = (typeof MANAGED_RULE_IDS)[number];\ntype ListKey = \"protected_branches\" | \"destructive_programs\" | \"allowed_hosts\";\nconst LIST_OF: Partial<Record<ManagedRuleId, ListKey>> = {\n \"force-push-protected\": \"protected_branches\", \"push-protected\": \"protected_branches\",\n \"destructive-shell\": \"destructive_programs\", \"network-egress\": \"allowed_hosts\",\n};\nconst LIST_PATTERN: Record<ListKey, RegExp> = {\n protected_branches: /^[A-Za-z0-9._/*-]{1,100}$/,\n destructive_programs: /^[a-z0-9][a-z0-9._-]{0,63}$/,\n allowed_hosts: /^(?=.{1,253}$)(\\*\\.)?([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,63}$/,\n};\nconst HEX64 = /^[0-9a-f]{64}$/;\nconst OPAQUE = /^[A-Za-z0-9._:-]{1,128}$/;\n\nexport type ManagedRule = { mode: \"monitor\" | \"block\" } & Partial<Record<ListKey, string[]>>;\nexport interface ManagedDocument {\n type: \"scopebond:managed-rules\";\n version: 1;\n revision: number;\n export_id: string;\n environment_id: string;\n agent_id: string;\n installation_id: string;\n rules: Record<ManagedRuleId, ManagedRule>;\n rules_digest: string;\n}\n\nexport type RefusalReason = \"invalid_document\" | \"stale_revision\" | \"wrong_computer\" | \"unsupported\" | \"write_failed\";\nexport type Inspection = { ok: true; doc: ManagedDocument } | { ok: false; reason: RefusalReason; message: string };\n\nexport const digestRules = (rules: unknown): string => createHash(\"sha256\").update(canonical(rules)).digest(\"hex\");\n\nconst isObject = (v: unknown): v is Record<string, unknown> => typeof v === \"object\" && v !== null && !Array.isArray(v);\n\n/** Check a document from the workspace without touching disk. Pure. */\nexport function inspectManaged(raw: unknown, context: { installationId: string; currentRevision: number | null }): Inspection {\n const bad = (message: string): Inspection => ({ ok: false, reason: \"invalid_document\", message });\n if (!isObject(raw) || raw.type !== \"scopebond:managed-rules\") return bad(\"this is not a Scopebond workspace rules document\");\n if (raw.version !== 1) return { ok: false, reason: \"unsupported\", message: `unsupported rules document version ${String(raw.version)}` };\n const { revision, export_id: exportId, environment_id: environmentId, agent_id: agentId, installation_id: installationId, rules, rules_digest: rulesDigest } = raw;\n if (!Number.isInteger(revision) || (revision as number) < 1 || (revision as number) > 2_147_483_647\n || typeof exportId !== \"string\" || typeof environmentId !== \"string\" || !OPAQUE.test(environmentId)\n || typeof agentId !== \"string\" || !OPAQUE.test(agentId) || typeof installationId !== \"string\" || !OPAQUE.test(installationId)\n || typeof rulesDigest !== \"string\" || !HEX64.test(rulesDigest) || !isObject(rules)) {\n return bad(\"the rules document is incomplete or malformed\");\n }\n if (installationId !== context.installationId || exportId !== `rev-${revision}-${installationId}`) {\n return { ok: false, reason: \"wrong_computer\", message: \"these rules were issued for a different computer\" };\n }\n const keys = Object.keys(rules);\n if (keys.length !== MANAGED_RULE_IDS.length || !MANAGED_RULE_IDS.every((id) => keys.includes(id))) {\n return { ok: false, reason: \"unsupported\", message: \"the rules document names rules this version does not know; update Scopebond\" };\n }\n for (const id of MANAGED_RULE_IDS) {\n const rule = rules[id];\n if (!isObject(rule) || (rule.mode !== \"monitor\" && rule.mode !== \"block\")) return bad(`rule ${id} has no valid mode`);\n for (const key of Object.keys(rule)) {\n if (key === \"mode\") continue;\n const listKey = LIST_OF[id];\n if (key !== listKey) return bad(`rule ${id} has an unknown setting`);\n const list = rule[key];\n if (!Array.isArray(list) || list.length > 50 || !list.every((v) => typeof v === \"string\" && LIST_PATTERN[listKey].test(v) && !v.includes(\"..\"))) {\n return bad(`rule ${id} has an invalid list`);\n }\n }\n }\n const network = rules[\"network-egress\"] as ManagedRule;\n if (network.mode === \"block\" && !(network.allowed_hosts?.length)) return bad(\"network blocking needs at least one allowed site\");\n if (digestRules(rules) !== rulesDigest) return bad(\"the rules do not match their digest; the document was changed or damaged\");\n if (context.currentRevision !== null && (revision as number) <= context.currentRevision) {\n return { ok: false, reason: \"stale_revision\", message: `version ${String(revision)} is not newer than the version in force (${context.currentRevision})` };\n }\n return { ok: true, doc: raw as unknown as ManagedDocument };\n}\n\nconst union = (a: readonly string[], b: readonly string[] | undefined): string[] => [...new Set([...a, ...(b ?? [])])];\nconst branchGlob = (b: string): string => (b.endsWith(\"/*\") ? `${b.slice(0, -1)}**` : b);\nconst hostPattern = (hosts: readonly string[]): string =>\n `^(?:${hosts.map((h) => (h.startsWith(\"*.\") ? `(?:[^.]+\\\\.)+${ci(h.slice(2).replace(/\\./g, \"\\\\.\"))}` : ci(h.replace(/\\./g, \"\\\\.\")))).join(\"|\")})$`;\n\ntype Clause = Record<string, unknown> & { id: string };\n\n/** The policy this computer enforces under workspace rules: its own compiled rules, adjusted rule by rule. Pure. */\nexport function compileManaged(local: RuleSet, doc: ManagedDocument, agentKid: string): Record<string, unknown> {\n const r = doc.rules;\n const rules: RuleSet = {\n ...local,\n protected_branches: union(local.protected_branches, [...(r[\"push-protected\"].protected_branches ?? []), ...(r[\"force-push-protected\"].protected_branches ?? [])]),\n destructive_programs: union(local.destructive_programs, r[\"destructive-shell\"].destructive_programs),\n };\n const policy = compile(rules, agentKid) as { clauses: Clause[] } & Record<string, unknown>;\n const allowAll = (clause: Clause): Clause => { const { param_bounds: _dropped, ...rest } = clause; return { ...rest, mode: \"enforce\", description: `${String(clause.description ?? \"\").split(\".\")[0]}. Recorded, not blocked: set by your workspace.` }; };\n const clauses: Clause[] = [];\n for (const clause of policy.clauses) {\n if (clause.id === \"protect-branches\" && r[\"push-protected\"].mode === \"monitor\") {\n clauses.push(allowAll(clause));\n if (r[\"force-push-protected\"].mode === \"block\") {\n clauses.push({ id: \"protect-branch-history\", type: \"force_push_guard\", mode: \"enforce\", protected_refs: rules.protected_branches.map(branchGlob),\n description: \"Deny force-pushes, deletions and mirror pushes that reach a protected branch; ordinary pushes are allowed. Set by your workspace.\" });\n }\n } else if (clause.id === \"safe-shell\" && r[\"destructive-shell\"].mode === \"monitor\") clauses.push(allowAll(clause));\n else if (clause.id === \"protect-read\" && r[\"secret-read\"].mode === \"monitor\") clauses.push(allowAll(clause));\n else if (clause.id === \"protect-write\" && r[\"ci-config-write\"].mode === \"monitor\") {\n clauses.push({ ...clause, param_bounds: { path: { pattern: GUARDRAIL_WRITE_PATTERN } },\n description: \"Allow workspace writes, but never to Scopebond's own settings or the agents' hook settings (always on). Other protected files are recorded, not blocked: set by your workspace.\" });\n } else clauses.push(clause);\n }\n if (r[\"network-egress\"].mode === \"block\") {\n const keysAt = clauses.findIndex((c) => c.id === \"keys\");\n clauses.splice(keysAt < 0 ? clauses.length : keysAt, 0, {\n id: \"allowed-sites\", type: \"action_allowlist\", mode: \"enforce\", action_types: [\"net.fetch\"],\n param_bounds: { host: { pattern: hostPattern(r[\"network-egress\"].allowed_hosts ?? []) } },\n description: `Deny web requests to sites other than ${r[\"network-egress\"].allowed_hosts!.join(\", \")}. Set by your workspace.`,\n });\n }\n return { ...policy, policy_id: \"coding-agent\", version: doc.revision, clauses };\n}\n\n// --- on disk ---------------------------------------------------------------------------------------------------------------------\n\nexport interface ManagedMeta {\n revision: number | null;\n rules_digest: string | null;\n export_id: string | null;\n etag: string | null;\n checked_at: string | null;\n last_ack: { revision: number; result: \"loaded\" | \"rejected\"; at: string } | null;\n last_error: string | null;\n}\nconst EMPTY_META: ManagedMeta = { revision: null, rules_digest: null, export_id: null, etag: null, checked_at: null, last_ack: null, last_error: null };\n\nexport function readMeta(dir: string): ManagedMeta {\n try { return { ...EMPTY_META, ...(JSON.parse(readFileSync(join(dir, MANAGED_META_FILE), \"utf8\")) as Partial<ManagedMeta>) }; } catch { return { ...EMPTY_META }; }\n}\nexport function writeMeta(dir: string, meta: ManagedMeta): void { writeAtomic(join(dir, MANAGED_META_FILE), `${JSON.stringify(meta, null, 2)}\\n`); }\n\n/** Whether a workspace currently manages the rules on this computer. */\nexport const isManaged = (dir: string): boolean => existsSync(join(dir, MANAGED_DOC_FILE));\n\nexport function writeAtomic(file: string, text: string): void {\n const temp = `${file}.${randomBytes(6).toString(\"hex\")}.tmp`;\n try { writeFileSync(temp, text, { mode: 0o600 }); renameSync(temp, file); }\n catch (error) { try { rmSync(temp, { force: true }); } catch { /* nothing to remove */ } throw error; }\n}\n\n/** Install a checked document: write the merged policy (previous kept) and the document. `policyBuilds` is the caller's check\n * that the gateway can load the result. */\nexport function installManaged(dir: string, doc: ManagedDocument, agentKid: string, policyBuilds: (policy: Record<string, unknown>) => boolean): { ok: true } | { ok: false; reason: RefusalReason; message: string } {\n const local = loadRules(dir) ?? defaultRules();\n const policy = compileManaged(local, doc, agentKid);\n if (!policyBuilds(policy)) return { ok: false, reason: \"unsupported\", message: \"this version of Scopebond cannot load the workspace rules; update it\" };\n try {\n mkdirSync(dir, { recursive: true });\n const policyPath = join(dir, \"policy.json\");\n if (existsSync(policyPath)) copyFileSync(policyPath, join(dir, PREVIOUS_POLICY_FILE));\n writeAtomic(policyPath, `${JSON.stringify(policy, null, 2)}\\n`);\n writeAtomic(join(dir, MANAGED_DOC_FILE), `${JSON.stringify(doc, null, 2)}\\n`);\n return { ok: true };\n } catch (error) {\n return { ok: false, reason: \"write_failed\", message: `could not write the rules (${(error as Error).name})` };\n }\n}\n\n/** Go back to this computer's own rules: recompile policy.json from rules.json and forget the workspace document. */\nexport function restoreLocal(dir: string, agentKid: string): void {\n const local = loadRules(dir) ?? defaultRules();\n const policyPath = join(dir, \"policy.json\");\n if (existsSync(policyPath)) copyFileSync(policyPath, join(dir, PREVIOUS_POLICY_FILE));\n writeAtomic(policyPath, `${JSON.stringify(compile(local, agentKid), null, 2)}\\n`);\n rmSync(join(dir, MANAGED_DOC_FILE), { force: true });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"managed.js","sourceRoot":"","sources":["../src/managed.ts"],"names":[],"mappings":"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","sourcesContent":["// Rules managed from a Scopebond Cloud workspace.\n//\n// A connected computer keeps its own rules (`rules.json` compiled into `policy.json`) until the workspace sets rules for it.\n// From then on the workspace decides, rule by rule, whether a matching action is blocked or only recorded, and may add entries\n// to the computer's own lists (protected branches, programs, allowed sites). It never sends a pattern: this module compiles the\n// workspace's choices with the same compiler `rules apply` uses, so what decides a block lives in one place.\n//\n// A workspace may also name exact targets one rule skips: paths for the secret-read and CI-configuration rules, branches for the\n// push rule. An exclusion matches exactly (same case; a trailing `/**` covers a folder), so it is never broader than what was typed,\n// and an exclusion that would touch the always-on protection of the hook's own settings is dropped when the policy is compiled.\n//\n// What a workspace cannot change: the protection of the hook's own settings (and the settings of the agents it guards), the\n// machine key policy, fail-closed handling of anything unparseable, and the computer's own opt-in extras (`allowed_roots`,\n// `protect_remote_database`). A rule set to Monitor is still covered by an allow clause, so the action is recorded and passes;\n// leaving it out would block the whole action type, because the verifier denies an action no clause covers.\n//\n// Loading is checked and atomic: the document must be well formed, name this computer, carry a matching digest and be newer\n// than the one in force; the merged policy must build; policy.json is written through a temp file and a rename, the previous\n// one kept. Disconnecting, or a workspace that stops managing this computer, restores the computer's own rules.\n\nimport { copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { createHash, randomBytes } from \"node:crypto\";\nimport { canonical } from \"@scopebond/policy-schema/canonical\";\nimport { GUARDRAIL_LOOKAHEADS, GUARDRAIL_WRITE_PATTERN, ci } from \"./runtime.js\";\nimport { compile, defaultRules, loadRules, type RuleSet } from \"./rules.js\";\n\nexport const MANAGED_DOC_FILE = \"managed-rules.json\";\nexport const MANAGED_META_FILE = \"managed-meta.json\";\nexport const PREVIOUS_POLICY_FILE = \"policy.previous.json\";\n\nexport const MANAGED_RULE_IDS = [\"force-push-protected\", \"push-protected\", \"destructive-shell\", \"secret-read\", \"ci-config-write\", \"network-egress\"] as const;\nexport type ManagedRuleId = (typeof MANAGED_RULE_IDS)[number];\ntype ListKey = \"protected_branches\" | \"destructive_programs\" | \"allowed_hosts\";\ntype ExclusionKey = \"excluded_paths\" | \"excluded_branches\";\nconst LIST_OF: Partial<Record<ManagedRuleId, ListKey>> = {\n \"force-push-protected\": \"protected_branches\", \"push-protected\": \"protected_branches\",\n \"destructive-shell\": \"destructive_programs\", \"network-egress\": \"allowed_hosts\",\n};\nconst LIST_PATTERN: Record<ListKey, RegExp> = {\n protected_branches: /^[A-Za-z0-9._/*-]{1,100}$/,\n destructive_programs: /^[a-z0-9][a-z0-9._-]{0,63}$/,\n allowed_hosts: /^(?=.{1,253}$)(\\*\\.)?([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,63}$/,\n};\n/** The exact targets a rule may skip, and the one list each rule accepts. */\nconst EXCLUSIONS_OF: Partial<Record<ManagedRuleId, ExclusionKey>> = {\n \"secret-read\": \"excluded_paths\", \"ci-config-write\": \"excluded_paths\", \"push-protected\": \"excluded_branches\",\n};\nconst EXCLUSION_PATTERN: Record<ExclusionKey, RegExp> = {\n // A relative path with forward slashes: no empty, \".\" or \"..\" segment, optionally ending in /** for a folder.\n excluded_paths: /^(?!\\/)(?!.*\\/\\/)(?!(?:.*\\/)?\\.{1,2}(?:\\/|$))[A-Za-z0-9._@+-][A-Za-z0-9._@+/-]{0,199}?(?:\\/\\*\\*)?$/,\n // One exact branch name; it starts with a letter or digit, so it can never be a push flag such as --all.\n excluded_branches: /^[A-Za-z0-9][A-Za-z0-9._/-]{0,99}$/,\n};\nconst HEX64 = /^[0-9a-f]{64}$/;\nconst OPAQUE = /^[A-Za-z0-9._:-]{1,128}$/;\n\nexport type ManagedRule = { mode: \"monitor\" | \"block\" } & Partial<Record<ListKey | ExclusionKey, string[]>>;\nexport interface ManagedDocument {\n type: \"scopebond:managed-rules\";\n version: 1;\n revision: number;\n export_id: string;\n environment_id: string;\n agent_id: string;\n installation_id: string;\n rules: Record<ManagedRuleId, ManagedRule>;\n rules_digest: string;\n}\n\nexport type RefusalReason = \"invalid_document\" | \"stale_revision\" | \"wrong_computer\" | \"unsupported\" | \"write_failed\";\nexport type Inspection = { ok: true; doc: ManagedDocument } | { ok: false; reason: RefusalReason; message: string };\n\nexport const digestRules = (rules: unknown): string => createHash(\"sha256\").update(canonical(rules)).digest(\"hex\");\n\nconst isObject = (v: unknown): v is Record<string, unknown> => typeof v === \"object\" && v !== null && !Array.isArray(v);\n\n/** Check a document from the workspace without touching disk. Pure. */\nexport function inspectManaged(raw: unknown, context: { installationId: string; currentRevision: number | null; currentDigest?: string | null }): Inspection {\n const bad = (message: string): Inspection => ({ ok: false, reason: \"invalid_document\", message });\n if (!isObject(raw) || raw.type !== \"scopebond:managed-rules\") return bad(\"this is not a Scopebond workspace rules document\");\n if (raw.version !== 1) return { ok: false, reason: \"unsupported\", message: `unsupported rules document version ${String(raw.version)}` };\n const { revision, export_id: exportId, environment_id: environmentId, agent_id: agentId, installation_id: installationId, rules, rules_digest: rulesDigest } = raw;\n if (!Number.isInteger(revision) || (revision as number) < 1 || (revision as number) > 2_147_483_647\n || typeof exportId !== \"string\" || typeof environmentId !== \"string\" || !OPAQUE.test(environmentId)\n || typeof agentId !== \"string\" || !OPAQUE.test(agentId) || typeof installationId !== \"string\" || !OPAQUE.test(installationId)\n || typeof rulesDigest !== \"string\" || !HEX64.test(rulesDigest) || !isObject(rules)) {\n return bad(\"the rules document is incomplete or malformed\");\n }\n if (installationId !== context.installationId || exportId !== `rev-${revision}-${installationId}`) {\n return { ok: false, reason: \"wrong_computer\", message: \"these rules were issued for a different computer\" };\n }\n const keys = Object.keys(rules);\n if (keys.length !== MANAGED_RULE_IDS.length || !MANAGED_RULE_IDS.every((id) => keys.includes(id))) {\n return { ok: false, reason: \"unsupported\", message: \"the rules document names rules this version does not know; update Scopebond\" };\n }\n for (const id of MANAGED_RULE_IDS) {\n const rule = rules[id];\n if (!isObject(rule) || (rule.mode !== \"monitor\" && rule.mode !== \"block\")) return bad(`rule ${id} has no valid mode`);\n for (const key of Object.keys(rule)) {\n if (key === \"mode\") continue;\n const listKey = LIST_OF[id];\n const exclusionKey = EXCLUSIONS_OF[id];\n const pattern = key === listKey ? LIST_PATTERN[listKey] : key === exclusionKey ? EXCLUSION_PATTERN[exclusionKey] : null;\n if (!pattern) return bad(`rule ${id} has an unknown setting`);\n const list = rule[key];\n if (!Array.isArray(list) || list.length > 50 || !list.every((v) => typeof v === \"string\" && pattern.test(v) && !v.includes(\"..\"))) {\n return bad(`rule ${id} has an invalid list`);\n }\n }\n }\n const network = rules[\"network-egress\"] as ManagedRule;\n if (network.mode === \"block\" && !(network.allowed_hosts?.length)) return bad(\"network blocking needs at least one allowed site\");\n if (digestRules(rules) !== rulesDigest) return bad(\"the rules do not match their digest; the document was changed or damaged\");\n // The same version with different rules is a newer document too: the workspace can change what reaches this computer without a new\n // version (an agent moved to another team, or this hook started reading a setting it did not read before). The export id binds it here.\n const sameVersionChanged = context.currentRevision !== null && revision === context.currentRevision && !!context.currentDigest && rulesDigest !== context.currentDigest;\n if (context.currentRevision !== null && (revision as number) <= context.currentRevision && !sameVersionChanged) {\n return { ok: false, reason: \"stale_revision\", message: `version ${String(revision)} is not newer than the version in force (${context.currentRevision})` };\n }\n return { ok: true, doc: raw as unknown as ManagedDocument };\n}\n\nconst union = (a: readonly string[], b: readonly string[] | undefined): string[] => [...new Set([...a, ...(b ?? [])])];\nconst branchGlob = (b: string): string => (b.endsWith(\"/*\") ? `${b.slice(0, -1)}**` : b);\nconst hostPattern = (hosts: readonly string[]): string =>\n `^(?:${hosts.map((h) => (h.startsWith(\"*.\") ? `(?:[^.]+\\\\.)+${ci(h.slice(2).replace(/\\./g, \"\\\\.\"))}` : ci(h.replace(/\\./g, \"\\\\.\")))).join(\"|\")})$`;\n\ntype Clause = Record<string, unknown> & { id: string };\n\nconst escapeRegex = (text: string): string => text.replace(/[\\\\^$.*+?()[\\]{}|/]/g, \"\\\\$&\");\n/** Exclusions that passed validation (the document check refuses the rest; this is a second line of defence). */\nconst safeExclusions = (paths: readonly string[] | undefined): string[] => (paths ?? []).filter((path) => EXCLUSION_PATTERN.excluded_paths.test(path) && !path.includes(\"..\"));\n/** An allow pattern that also allows exactly these paths (a trailing /** allows everything under the folder). The added alternative\n * carries the always-on floor and refuses any path with a \".\" or \"..\" segment, so no exclusion — however it is written — can reach\n * Scopebond's own settings or the agents' hook settings, or climb out of the folder it names. */\nconst withPathExclusions = (pattern: string, paths: readonly string[]): string => paths.length === 0 ? pattern\n : `^(?!(?:.*/)?\\\\.{1,2}(?:/|$))${GUARDRAIL_LOOKAHEADS}(?:${paths.map((p) => p.endsWith(\"/**\") ? `${escapeRegex(p.slice(0, -3))}/.+` : escapeRegex(p)).join(\"|\")})$|${pattern}`;\n/** An allow pattern for pushes that also allows exactly these branches. */\nconst withBranchExclusions = (pattern: string, branches: readonly string[]): string => branches.length === 0 ? pattern\n : `^(?:refs/heads/)?(?:${branches.map(escapeRegex).join(\"|\")})$|${pattern}`;\nconst boundPattern = (clause: Clause, key: string): string => String(((clause.param_bounds as Record<string, { pattern: string }> | undefined)?.[key])?.pattern ?? \"\");\nconst withBound = (clause: Clause, key: string, pattern: string): Clause => ({ ...clause, param_bounds: { ...(clause.param_bounds as Record<string, unknown>), [key]: { pattern } } });\n\n/** The policy this computer enforces under workspace rules: its own compiled rules, adjusted rule by rule. Pure. */\nexport function compileManaged(local: RuleSet, doc: ManagedDocument, agentKid: string): Record<string, unknown> {\n const r = doc.rules;\n const rules: RuleSet = {\n ...local,\n protected_branches: union(local.protected_branches, [...(r[\"push-protected\"].protected_branches ?? []), ...(r[\"force-push-protected\"].protected_branches ?? [])]),\n destructive_programs: union(local.destructive_programs, r[\"destructive-shell\"].destructive_programs),\n };\n const policy = compile(rules, agentKid) as { clauses: Clause[] } & Record<string, unknown>;\n const allowAll = (clause: Clause): Clause => { const { param_bounds: _dropped, ...rest } = clause; return { ...rest, mode: \"enforce\", description: `${String(clause.description ?? \"\").split(\".\")[0]}. Recorded, not blocked: set by your workspace.` }; };\n const clauses: Clause[] = [];\n for (const clause of policy.clauses) {\n if (clause.id === \"protect-branches\" && r[\"push-protected\"].mode === \"monitor\") {\n clauses.push(allowAll(clause));\n if (r[\"force-push-protected\"].mode === \"block\") {\n clauses.push({ id: \"protect-branch-history\", type: \"force_push_guard\", mode: \"enforce\", protected_refs: rules.protected_branches.map(branchGlob),\n description: \"Deny force-pushes, deletions and mirror pushes that reach a protected branch; ordinary pushes are allowed. Set by your workspace.\" });\n }\n } else if (clause.id === \"safe-shell\" && r[\"destructive-shell\"].mode === \"monitor\") clauses.push(allowAll(clause));\n else if (clause.id === \"protect-read\" && r[\"secret-read\"].mode === \"monitor\") clauses.push(allowAll(clause));\n else if (clause.id === \"protect-write\" && r[\"ci-config-write\"].mode === \"monitor\") {\n clauses.push({ ...clause, param_bounds: { path: { pattern: GUARDRAIL_WRITE_PATTERN } },\n description: \"Allow workspace writes, but never to Scopebond's own settings or the agents' hook settings (always on). Other protected files are recorded, not blocked: set by your workspace.\" });\n } else if (clause.id === \"protect-read\" && r[\"secret-read\"].excluded_paths?.length) {\n clauses.push(withBound(clause, \"path\", withPathExclusions(boundPattern(clause, \"path\"), safeExclusions(r[\"secret-read\"].excluded_paths))));\n } else if (clause.id === \"protect-write\" && r[\"ci-config-write\"].excluded_paths?.length) {\n clauses.push(withBound(clause, \"path\", withPathExclusions(boundPattern(clause, \"path\"), safeExclusions(r[\"ci-config-write\"].excluded_paths))));\n } else if (clause.id === \"protect-branches\" && r[\"push-protected\"].excluded_branches?.length) {\n clauses.push(withBound(clause, \"ref\", withBranchExclusions(boundPattern(clause, \"ref\"), r[\"push-protected\"].excluded_branches)));\n // Skipping ordinary pushes to a branch never skips history protection: force-pushes, deletions and mirror pushes stay stopped.\n if (r[\"force-push-protected\"].mode === \"block\") {\n clauses.push({ id: \"protect-branch-history\", type: \"force_push_guard\", mode: \"enforce\", protected_refs: rules.protected_branches.map(branchGlob),\n description: \"Deny force-pushes, deletions and mirror pushes that reach a protected branch. Set by your workspace.\" });\n }\n } else clauses.push(clause);\n }\n if (r[\"network-egress\"].mode === \"block\") {\n const keysAt = clauses.findIndex((c) => c.id === \"keys\");\n clauses.splice(keysAt < 0 ? clauses.length : keysAt, 0, {\n id: \"allowed-sites\", type: \"action_allowlist\", mode: \"enforce\", action_types: [\"net.fetch\"],\n param_bounds: { host: { pattern: hostPattern(r[\"network-egress\"].allowed_hosts ?? []) } },\n description: `Deny web requests to sites other than ${r[\"network-egress\"].allowed_hosts!.join(\", \")}. Set by your workspace.`,\n });\n }\n return { ...policy, policy_id: \"coding-agent\", version: doc.revision, clauses };\n}\n\n// --- on disk ---------------------------------------------------------------------------------------------------------------------\n\nexport interface ManagedMeta {\n revision: number | null;\n rules_digest: string | null;\n export_id: string | null;\n etag: string | null;\n checked_at: string | null;\n last_ack: { revision: number; result: \"loaded\" | \"rejected\"; at: string } | null;\n last_error: string | null;\n}\nconst EMPTY_META: ManagedMeta = { revision: null, rules_digest: null, export_id: null, etag: null, checked_at: null, last_ack: null, last_error: null };\n\nexport function readMeta(dir: string): ManagedMeta {\n try { return { ...EMPTY_META, ...(JSON.parse(readFileSync(join(dir, MANAGED_META_FILE), \"utf8\")) as Partial<ManagedMeta>) }; } catch { return { ...EMPTY_META }; }\n}\nexport function writeMeta(dir: string, meta: ManagedMeta): void { writeAtomic(join(dir, MANAGED_META_FILE), `${JSON.stringify(meta, null, 2)}\\n`); }\n\n/** Whether a workspace currently manages the rules on this computer. */\nexport const isManaged = (dir: string): boolean => existsSync(join(dir, MANAGED_DOC_FILE));\n\nexport function writeAtomic(file: string, text: string): void {\n const temp = `${file}.${randomBytes(6).toString(\"hex\")}.tmp`;\n try { writeFileSync(temp, text, { mode: 0o600 }); renameSync(temp, file); }\n catch (error) { try { rmSync(temp, { force: true }); } catch { /* nothing to remove */ } throw error; }\n}\n\n/** Install a checked document: write the merged policy (previous kept) and the document. `policyBuilds` is the caller's check\n * that the gateway can load the result. */\nexport function installManaged(dir: string, doc: ManagedDocument, agentKid: string, policyBuilds: (policy: Record<string, unknown>) => boolean): { ok: true } | { ok: false; reason: RefusalReason; message: string } {\n const local = loadRules(dir) ?? defaultRules();\n const policy = compileManaged(local, doc, agentKid);\n if (!policyBuilds(policy)) return { ok: false, reason: \"unsupported\", message: \"this version of Scopebond cannot load the workspace rules; update it\" };\n try {\n mkdirSync(dir, { recursive: true });\n const policyPath = join(dir, \"policy.json\");\n if (existsSync(policyPath)) copyFileSync(policyPath, join(dir, PREVIOUS_POLICY_FILE));\n writeAtomic(policyPath, `${JSON.stringify(policy, null, 2)}\\n`);\n writeAtomic(join(dir, MANAGED_DOC_FILE), `${JSON.stringify(doc, null, 2)}\\n`);\n return { ok: true };\n } catch (error) {\n return { ok: false, reason: \"write_failed\", message: `could not write the rules (${(error as Error).name})` };\n }\n}\n\n/** Go back to this computer's own rules: recompile policy.json from rules.json and forget the workspace document. */\nexport function restoreLocal(dir: string, agentKid: string): void {\n const local = loadRules(dir) ?? defaultRules();\n const policyPath = join(dir, \"policy.json\");\n if (existsSync(policyPath)) copyFileSync(policyPath, join(dir, PREVIOUS_POLICY_FILE));\n writeAtomic(policyPath, `${JSON.stringify(compile(local, agentKid), null, 2)}\\n`);\n rmSync(join(dir, MANAGED_DOC_FILE), { force: true });\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"obs-emitter.d.ts","sourceRoot":"","sources":["../src/obs-emitter.ts"],"names":[],"mappings":"AA4BA,OAAO,
|
|
1
|
+
{"version":3,"file":"obs-emitter.d.ts","sourceRoot":"","sources":["../src/obs-emitter.ts"],"names":[],"mappings":"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"}
|
package/dist/obs-emitter.js
CHANGED
|
@@ -25,7 +25,7 @@ import { createRequire } from "node:module";
|
|
|
25
25
|
import { spawn, execFileSync } from "node:child_process";
|
|
26
26
|
import { join } from "node:path";
|
|
27
27
|
import { fileURLToPath } from "node:url";
|
|
28
|
-
import { loadConnection } from "./cloud.js";
|
|
28
|
+
import { ingestUrl, loadConnection } from "./cloud.js";
|
|
29
29
|
import { operationsForCall, capabilityProofData, digestPolicy, heartbeatData, intentData, loadOrCreateBindingKey, observationSigner, outcomeData, policyAckData, queueData, sessionStartData, sessionStopData, sourceReceiptHash, } from "./observation.js";
|
|
30
30
|
import { openApprovalBinder } from "@scopebond/gateway/node";
|
|
31
31
|
import { OBSERVATION_DB, ObservationStore } from "./obs-store.js";
|
|
@@ -132,7 +132,7 @@ export class ObservationEmitter {
|
|
|
132
132
|
async flush(timeoutMs = this.options.flushTimeoutMs ?? 800) {
|
|
133
133
|
try {
|
|
134
134
|
return await uploadPending(this.store, {
|
|
135
|
-
url: this.connection
|
|
135
|
+
url: ingestUrl(this.connection), credential: this.connection.credential,
|
|
136
136
|
fetch: this.options.fetch, now: this.options.now, timeoutMs,
|
|
137
137
|
});
|
|
138
138
|
}
|