@scopebond/hook 0.5.0 → 0.7.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 +139 -12
- package/dist/cli.d.ts +2 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +667 -69
- package/dist/cli.js.map +1 -1
- package/dist/cloud.d.ts +2 -0
- package/dist/cloud.d.ts.map +1 -1
- package/dist/cloud.js +1 -1
- package/dist/cloud.js.map +1 -1
- package/dist/explain.d.ts +44 -0
- package/dist/explain.d.ts.map +1 -0
- package/dist/explain.js +75 -0
- package/dist/explain.js.map +1 -0
- package/dist/index.d.ts +9 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +9 -3
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +36 -28
- package/dist/init.js.map +1 -1
- package/dist/install.d.ts +63 -3
- package/dist/install.d.ts.map +1 -1
- package/dist/install.js +242 -28
- package/dist/install.js.map +1 -1
- package/dist/map.d.ts +12 -3
- package/dist/map.d.ts.map +1 -1
- package/dist/map.js +765 -60
- package/dist/map.js.map +1 -1
- package/dist/minimize.d.ts +6 -3
- package/dist/minimize.d.ts.map +1 -1
- package/dist/minimize.js +24 -4
- package/dist/minimize.js.map +1 -1
- package/dist/rules.d.ts +51 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +216 -0
- package/dist/rules.js.map +1 -0
- package/dist/runtime-install.d.ts +25 -0
- package/dist/runtime-install.d.ts.map +1 -0
- package/dist/runtime-install.js +106 -0
- package/dist/runtime-install.js.map +1 -0
- package/dist/runtime.d.ts +17 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +122 -11
- package/dist/runtime.js.map +1 -1
- package/dist/shell.d.ts +53 -4
- package/dist/shell.d.ts.map +1 -1
- package/dist/shell.js +737 -81
- package/dist/shell.js.map +1 -1
- package/dist/version.d.ts +5 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +6 -2
- package/dist/version.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC7F,YAAY,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC7F,YAAY,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACtF,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EACL,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,GAC/F,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EACL,QAAQ,EAAE,eAAe,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,mBAAmB,EACxG,cAAc,EAAE,aAAa,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,SAAS,EAClF,iBAAiB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,mBAAmB,EACpG,aAAa,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,mBAAmB,EAC7E,sBAAsB,EAAE,mBAAmB,GAC5C,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACvE,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClG,OAAO,EACL,oBAAoB,EAAE,eAAe,EAAE,iBAAiB,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,GACpG,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACxG,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
// the deterministic mapper and the check-only runtime; the `scopebond-hook`
|
|
3
3
|
// binary is the thin harness adapter over them.
|
|
4
4
|
export { mapClaudeToolUse, mapCodexToolUse, mapCursorEvent, fillPushBranch } from "./map.js";
|
|
5
|
-
export { createHookRuntime, starterPolicy } from "./runtime.js";
|
|
5
|
+
export { createHookRuntime, starterPolicy, upgradeStarterPolicy } from "./runtime.js";
|
|
6
|
+
export { compile, defaultRules, describeRules, loadRules, saveRules, rulesPath, pathRuleFor, RULES_FILE, } from "./rules.js";
|
|
6
7
|
export { scaffold, harnessSnippet, installHarness } from "./init.js";
|
|
7
|
-
export { userHome, userHarnessFile, resolveConfigDir, writeHarnessConfig, removeHarnessConfig, cursorDetected, codexDetected, absoluteHookCommand, isHarnessConfigured, purgeHome, } from "./install.js";
|
|
8
|
+
export { userHome, userHarnessFile, projectHarnessFile, resolveConfigDir, writeHarnessConfig, removeHarnessConfig, cursorDetected, codexDetected, absoluteHookCommand, isHarnessConfigured, purgeHome, readHarnessConfig, trustProjectPolicy, isTrustedProject, untrustedProjectPolicy, trustedProjectsFile, harnessScopes, harnessScopeLabel, configuredHookCommands, hookCommandResolves, isScopebondHookCommand, harnessEntryMatches, } from "./install.js";
|
|
9
|
+
export { explainDeny, describeAction, findClause } from "./explain.js";
|
|
10
|
+
export { ensureDurableRuntime, isEphemeralPath, nodeModulesRootOf, runtimeRoot, runtimeDirFor, pinnedCliPath, } from "./runtime-install.js";
|
|
8
11
|
export { connectCloud, loadConnection, attachExporter, flushBounded, connectionPath } from "./cloud.js";
|
|
9
12
|
export { scrubSecrets, scrubParam, redactCommand, digest, sha256 } from "./minimize.js";
|
|
10
13
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,4EAA4E;AAC5E,gDAAgD;AAEhD,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE7F,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,4EAA4E;AAC5E,gDAAgD;AAEhD,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE7F,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEtF,OAAO,EACL,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,GAC/F,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EACL,QAAQ,EAAE,eAAe,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,mBAAmB,EACxG,cAAc,EAAE,aAAa,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,SAAS,EAClF,iBAAiB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,mBAAmB,EACpG,aAAa,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,mBAAmB,EAC7E,sBAAsB,EAAE,mBAAmB,GAC5C,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAEvE,OAAO,EACL,oBAAoB,EAAE,eAAe,EAAE,iBAAiB,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,GACpG,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAExG,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC","sourcesContent":["// @scopebond/hook — the Claude Code, Cursor + Codex connector. The library surface is\n// the deterministic mapper and the check-only runtime; the `scopebond-hook`\n// binary is the thin harness adapter over them.\n\nexport { mapClaudeToolUse, mapCodexToolUse, mapCursorEvent, fillPushBranch } from \"./map.js\";\nexport type { Mapped, NormalizedIntent } from \"./map.js\";\nexport { createHookRuntime, starterPolicy, upgradeStarterPolicy } from \"./runtime.js\";\nexport type { RuntimeConfig, Decision } from \"./runtime.js\";\nexport {\n compile, defaultRules, describeRules, loadRules, saveRules, rulesPath, pathRuleFor, RULES_FILE,\n} from \"./rules.js\";\nexport type { RuleSet, PathRule } from \"./rules.js\";\nexport { scaffold, harnessSnippet, installHarness } from \"./init.js\";\nexport {\n userHome, userHarnessFile, projectHarnessFile, resolveConfigDir, writeHarnessConfig, removeHarnessConfig,\n cursorDetected, codexDetected, absoluteHookCommand, isHarnessConfigured, purgeHome,\n readHarnessConfig, trustProjectPolicy, isTrustedProject, untrustedProjectPolicy, trustedProjectsFile,\n harnessScopes, harnessScopeLabel, configuredHookCommands, hookCommandResolves,\n isScopebondHookCommand, harnessEntryMatches,\n} from \"./install.js\";\nexport type { Harness, HarnessScopes } from \"./install.js\";\nexport { explainDeny, describeAction, findClause } from \"./explain.js\";\nexport type { ExplainDenyInput, ExplainIntent, ExplainPolicy, ExplainClause } from \"./explain.js\";\nexport {\n ensureDurableRuntime, isEphemeralPath, nodeModulesRootOf, runtimeRoot, runtimeDirFor, pinnedCliPath,\n} from \"./runtime-install.js\";\nexport type { PinResult } from \"./runtime-install.js\";\nexport { connectCloud, loadConnection, attachExporter, flushBounded, connectionPath } from \"./cloud.js\";\nexport type { HookConnection } from \"./cloud.js\";\nexport { scrubSecrets, scrubParam, redactCommand, digest, sha256 } from \"./minimize.js\";\n"]}
|
package/dist/init.d.ts
CHANGED
|
@@ -3,12 +3,18 @@ export declare function scaffold(dir: string, opts?: {
|
|
|
3
3
|
}): {
|
|
4
4
|
agentKid: string;
|
|
5
5
|
policyPath: string;
|
|
6
|
+
rulesPath: string;
|
|
6
7
|
};
|
|
7
8
|
/** Install the hook into the agent's own config file, merging with anything already
|
|
8
9
|
* there (idempotent — safe to run twice). Returns the file it wrote. This is what
|
|
9
10
|
* lets `connect` finish setup without the user hand-editing JSON.
|
|
10
11
|
* `cwd` defaults to the current directory (the project root the user runs it in). */
|
|
11
|
-
export declare function installHarness(harness: "claude" | "cursor" | "codex", cwd?: string
|
|
12
|
-
/** The
|
|
13
|
-
|
|
12
|
+
export declare function installHarness(harness: "claude" | "cursor" | "codex", cwd?: string,
|
|
13
|
+
/** The command to run per tool call. Defaults to the portable `npx` form; `init`
|
|
14
|
+
* passes a pinned absolute path when it has one, which is ~7x faster to start. */
|
|
15
|
+
command?: string): string;
|
|
16
|
+
/** The harness configuration snippet to install the hook by hand. Defaults to the
|
|
17
|
+
* version-pinned `npx` form; `init --no-install` passes the pinned path it chose, so
|
|
18
|
+
* the printed snippet matches what `init` would have written. */
|
|
19
|
+
export declare function harnessSnippet(harness: "claude" | "cursor" | "codex", command?: string): string;
|
|
14
20
|
//# sourceMappingURL=init.d.ts.map
|
package/dist/init.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAYA,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAuBjI;AAED;;;sFAGsF;AACtF,wBAAgB,cAAc,CAC5B,OAAO,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,EACtC,GAAG,GAAE,MAAsB;AAC3B;mFACmF;AACnF,OAAO,CAAC,EAAE,MAAM,GACf,MAAM,CAmCR;AAED;;kEAEkE;AAClE,wBAAgB,cAAc,CAAC,OAAO,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CA6B/F"}
|
package/dist/init.js
CHANGED
|
@@ -5,7 +5,8 @@ import { mkdirSync, writeFileSync, existsSync, readFileSync } from "node:fs";
|
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
6
6
|
import { loadOrCreateAttester } from "@scopebond/gateway/node";
|
|
7
7
|
import { createSigner } from "@scopebond/sdk";
|
|
8
|
-
import {
|
|
8
|
+
import { compile, defaultRules, loadRules, saveRules, rulesPath } from "./rules.js";
|
|
9
|
+
import { readHarnessConfig, projectHarnessFile, harnessEntryMatches, backupHarnessConfig } from "./install.js";
|
|
9
10
|
import { hookCommand } from "./version.js";
|
|
10
11
|
export function scaffold(dir, opts = {}) {
|
|
11
12
|
mkdirSync(dir, { recursive: true });
|
|
@@ -20,38 +21,42 @@ export function scaffold(dir, opts = {}) {
|
|
|
20
21
|
loadOrCreateAttester({ file: keyPath });
|
|
21
22
|
loadOrCreateAttester({ file: attesterPath });
|
|
22
23
|
const agent = createSigner({ privateKeyPem: readFileSync(keyPath, "utf8") });
|
|
24
|
+
// The editable rule set beside the compiled policy, so "edit the limits" means editing a
|
|
25
|
+
// readable list rather than a 700-character lookahead. `compile(defaultRules())` produces
|
|
26
|
+
// the starter policy's exact patterns — `rules.test.mjs` pins that — so writing either
|
|
27
|
+
// form enforces the same thing.
|
|
28
|
+
const rulesFile = rulesPath(dir);
|
|
29
|
+
if (!existsSync(rulesFile) || opts.force)
|
|
30
|
+
saveRules(dir, defaultRules());
|
|
23
31
|
if (!existsSync(policyPath) || opts.force) {
|
|
24
|
-
|
|
32
|
+
const rules = loadRules(dir) ?? defaultRules();
|
|
33
|
+
writeFileSync(policyPath, JSON.stringify(compile(rules, agent.kid), null, 2) + "\n");
|
|
25
34
|
}
|
|
26
|
-
return { agentKid: agent.kid, policyPath };
|
|
35
|
+
return { agentKid: agent.kid, policyPath, rulesPath: rulesFile };
|
|
27
36
|
}
|
|
28
37
|
/** Install the hook into the agent's own config file, merging with anything already
|
|
29
38
|
* there (idempotent — safe to run twice). Returns the file it wrote. This is what
|
|
30
39
|
* lets `connect` finish setup without the user hand-editing JSON.
|
|
31
40
|
* `cwd` defaults to the current directory (the project root the user runs it in). */
|
|
32
|
-
export function installHarness(harness, cwd = process.cwd()
|
|
41
|
+
export function installHarness(harness, cwd = process.cwd(),
|
|
42
|
+
/** The command to run per tool call. Defaults to the portable `npx` form; `init`
|
|
43
|
+
* passes a pinned absolute path when it has one, which is ~7x faster to start. */
|
|
44
|
+
command) {
|
|
33
45
|
const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
34
|
-
const file = harness
|
|
35
|
-
|
|
36
|
-
|
|
46
|
+
const file = projectHarnessFile(harness, cwd);
|
|
47
|
+
const config = readHarnessConfig(file); // throws, leaving the file intact, if it is not a JSON object
|
|
48
|
+
backupHarnessConfig(file); // keep the user's original beside it before the first change
|
|
37
49
|
mkdirSync(dirname(file), { recursive: true });
|
|
38
|
-
let config = {};
|
|
39
|
-
if (existsSync(file)) {
|
|
40
|
-
try {
|
|
41
|
-
const p = JSON.parse(readFileSync(file, "utf8"));
|
|
42
|
-
if (isRecord(p))
|
|
43
|
-
config = p;
|
|
44
|
-
}
|
|
45
|
-
catch { /* start fresh on unreadable */ }
|
|
46
|
-
}
|
|
47
50
|
const hooks = isRecord(config.hooks) ? config.hooks : (config.hooks = {});
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
const forHarness = (h) => command ? command.replace(/\b(claude|cursor|codex)$/, h) : hookCommand(h);
|
|
52
|
+
const cursorCmd = forHarness("cursor");
|
|
53
|
+
const claudeCmd = forHarness("claude");
|
|
54
|
+
const codexCmd = forHarness("codex");
|
|
55
|
+
// Match any existing Scopebond hook entry — npx, pinned absolute (either path
|
|
56
|
+
// separator) or a legacy bare `scopebond-hook` — so re-running replaces the entry
|
|
57
|
+
// instead of appending a second one. One shared matcher with `install`: a private
|
|
58
|
+
// copy here missed the pinned Windows form and duplicated the hook.
|
|
59
|
+
const entryMatches = harnessEntryMatches;
|
|
55
60
|
if (harness === "cursor") {
|
|
56
61
|
config.version = config.version ?? 1;
|
|
57
62
|
for (const event of ["beforeShellExecution", "beforeMCPExecution", "beforeReadFile", "afterFileEdit"]) {
|
|
@@ -78,10 +83,13 @@ export function installHarness(harness, cwd = process.cwd()) {
|
|
|
78
83
|
writeFileSync(file, JSON.stringify(config, null, 2) + "\n");
|
|
79
84
|
return file;
|
|
80
85
|
}
|
|
81
|
-
/** The harness configuration snippet to install the hook
|
|
82
|
-
|
|
86
|
+
/** The harness configuration snippet to install the hook by hand. Defaults to the
|
|
87
|
+
* version-pinned `npx` form; `init --no-install` passes the pinned path it chose, so
|
|
88
|
+
* the printed snippet matches what `init` would have written. */
|
|
89
|
+
export function harnessSnippet(harness, command) {
|
|
90
|
+
const cmdFor = (h) => command ? command.replace(/\b(claude|cursor|codex)$/, h) : hookCommand(h);
|
|
83
91
|
if (harness === "cursor") {
|
|
84
|
-
const cmd =
|
|
92
|
+
const cmd = cmdFor("cursor");
|
|
85
93
|
return JSON.stringify({
|
|
86
94
|
version: 1,
|
|
87
95
|
hooks: {
|
|
@@ -96,14 +104,14 @@ export function harnessSnippet(harness) {
|
|
|
96
104
|
return JSON.stringify({
|
|
97
105
|
hooks: {
|
|
98
106
|
PreToolUse: [{
|
|
99
|
-
hooks: [{ type: "command", command:
|
|
107
|
+
hooks: [{ type: "command", command: cmdFor("codex"), timeout: 30, statusMessage: "Checking this action with Scopebond" }],
|
|
100
108
|
}],
|
|
101
109
|
},
|
|
102
110
|
}, null, 2);
|
|
103
111
|
}
|
|
104
112
|
return JSON.stringify({
|
|
105
113
|
hooks: {
|
|
106
|
-
PreToolUse: [{ matcher: "*", hooks: [{ type: "command", command:
|
|
114
|
+
PreToolUse: [{ matcher: "*", hooks: [{ type: "command", command: cmdFor("claude") }] }],
|
|
107
115
|
},
|
|
108
116
|
}, null, 2);
|
|
109
117
|
}
|
package/dist/init.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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","sourcesContent":["// `scopebond-hook init` — scaffold the local enrollment: a machine signing key, a\n// gateway countersigning key and a starter policy. Keys and receipts stay on the\n// machine; no credentials are handled.\n\nimport { mkdirSync, writeFileSync, existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { loadOrCreateAttester } from \"@scopebond/gateway/node\";\nimport { createSigner } from \"@scopebond/sdk\";\nimport { starterPolicy } from \"./runtime.js\";\nimport { hookCommand } from \"./version.js\";\n\nexport function scaffold(dir: string, opts: { force?: boolean } = {}): { agentKid: string; policyPath: string } {\n mkdirSync(dir, { recursive: true });\n const keyPath = join(dir, \"agent.key\");\n const attesterPath = join(dir, \"attester.key\");\n const policyPath = join(dir, \"policy.json\");\n // Never let the signing keys, the Cloud credential or the local log be committed.\n const gitignorePath = join(dir, \".gitignore\");\n if (!existsSync(gitignorePath)) writeFileSync(gitignorePath, \"*\\n\");\n // Machine signing key (agent) + gateway countersigning key (attester). Reused if present.\n loadOrCreateAttester({ file: keyPath });\n loadOrCreateAttester({ file: attesterPath });\n const agent = createSigner({ privateKeyPem: readFileSync(keyPath, \"utf8\") });\n if (!existsSync(policyPath) || opts.force) {\n writeFileSync(policyPath, JSON.stringify(starterPolicy(agent.kid), null, 2) + \"\\n\");\n }\n return { agentKid: agent.kid, policyPath };\n}\n\n/** Install the hook into the agent's own config file, merging with anything already\n * there (idempotent — safe to run twice). Returns the file it wrote. This is what\n * lets `connect` finish setup without the user hand-editing JSON.\n * `cwd` defaults to the current directory (the project root the user runs it in). */\nexport function installHarness(harness: \"claude\" | \"cursor\" | \"codex\", cwd: string = process.cwd()): string {\n const isRecord = (v: unknown): v is Record<string, unknown> => typeof v === \"object\" && v !== null && !Array.isArray(v);\n const file = harness === \"cursor\" ? join(cwd, \".cursor\", \"hooks.json\")\n : harness === \"codex\" ? join(cwd, \".codex\", \"hooks.json\")\n : join(cwd, \".claude\", \"settings.json\");\n mkdirSync(dirname(file), { recursive: true });\n let config: Record<string, unknown> = {};\n if (existsSync(file)) { try { const p = JSON.parse(readFileSync(file, \"utf8\")); if (isRecord(p)) config = p; } catch { /* start fresh on unreadable */ } }\n const hooks = isRecord(config.hooks) ? config.hooks : (config.hooks = {});\n const cursorCmd = hookCommand(\"cursor\");\n const claudeCmd = hookCommand(\"claude\");\n const codexCmd = hookCommand(\"codex\");\n // Match any existing Scopebond hook entry (pinned or a legacy bare `scopebond-hook`)\n // so re-running never appends a duplicate and an old bare command is replaced.\n const isScopebond = (cmd: unknown): boolean => typeof cmd === \"string\" && /(^|\\s)(npx\\s+.*)?@scopebond\\/hook|(^|\\s)scopebond-hook(\\s|$)/.test(cmd);\n const entryMatches = (e: unknown): boolean =>\n isRecord(e) && (isScopebond(e.command) || (Array.isArray(e.hooks) && e.hooks.some((h) => isRecord(h) && isScopebond(h.command))));\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 existing = list.findIndex(entryMatches);\n if (existing >= 0) list[existing] = { command: cursorCmd }; else list.push({ command: cursorCmd });\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 existing = list.findIndex(entryMatches);\n // Codex matchers are regular expressions. No matcher means all supported tools.\n const entry = harness === \"codex\"\n ? { hooks: [{ type: \"command\", command: codexCmd, timeout: 30, statusMessage: \"Checking this action with Scopebond\" }] }\n : { matcher: \"*\", hooks: [{ type: \"command\", command: claudeCmd }] };\n if (existing >= 0) list[existing] = entry; else list.push(entry);\n }\n writeFileSync(file, JSON.stringify(config, null, 2) + \"\\n\");\n return file;\n}\n\n/** The harness configuration snippet to install the hook (version-pinned npx). */\nexport function harnessSnippet(harness: \"claude\" | \"cursor\" | \"codex\"): string {\n if (harness === \"cursor\") {\n const cmd = hookCommand(\"cursor\");\n return JSON.stringify({\n version: 1,\n hooks: {\n beforeShellExecution: [{ command: cmd }],\n beforeMCPExecution: [{ command: cmd }],\n beforeReadFile: [{ command: cmd }],\n afterFileEdit: [{ command: cmd }],\n },\n }, null, 2);\n }\n if (harness === \"codex\") {\n return JSON.stringify({\n hooks: {\n PreToolUse: [{\n hooks: [{ type: \"command\", command: hookCommand(\"codex\"), timeout: 30, statusMessage: \"Checking this action with Scopebond\" }],\n }],\n },\n }, null, 2);\n }\n return JSON.stringify({\n hooks: {\n PreToolUse: [{ matcher: \"*\", hooks: [{ type: \"command\", command: hookCommand(\"claude\") }] }],\n },\n }, null, 2);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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","sourcesContent":["// `scopebond-hook init` — scaffold the local enrollment: a machine signing key, a\n// gateway countersigning key and a starter policy. Keys and receipts stay on the\n// machine; no credentials are handled.\n\nimport { mkdirSync, writeFileSync, existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { loadOrCreateAttester } from \"@scopebond/gateway/node\";\nimport { createSigner } from \"@scopebond/sdk\";\nimport { compile, defaultRules, loadRules, saveRules, rulesPath } from \"./rules.js\";\nimport { readHarnessConfig, projectHarnessFile, harnessEntryMatches, backupHarnessConfig } from \"./install.js\";\nimport { hookCommand } from \"./version.js\";\n\nexport function scaffold(dir: string, opts: { force?: boolean } = {}): { agentKid: string; policyPath: string; rulesPath: string } {\n mkdirSync(dir, { recursive: true });\n const keyPath = join(dir, \"agent.key\");\n const attesterPath = join(dir, \"attester.key\");\n const policyPath = join(dir, \"policy.json\");\n // Never let the signing keys, the Cloud credential or the local log be committed.\n const gitignorePath = join(dir, \".gitignore\");\n if (!existsSync(gitignorePath)) writeFileSync(gitignorePath, \"*\\n\");\n // Machine signing key (agent) + gateway countersigning key (attester). Reused if present.\n loadOrCreateAttester({ file: keyPath });\n loadOrCreateAttester({ file: attesterPath });\n const agent = createSigner({ privateKeyPem: readFileSync(keyPath, \"utf8\") });\n // The editable rule set beside the compiled policy, so \"edit the limits\" means editing a\n // readable list rather than a 700-character lookahead. `compile(defaultRules())` produces\n // the starter policy's exact patterns — `rules.test.mjs` pins that — so writing either\n // form enforces the same thing.\n const rulesFile = rulesPath(dir);\n if (!existsSync(rulesFile) || opts.force) saveRules(dir, defaultRules());\n if (!existsSync(policyPath) || opts.force) {\n const rules = loadRules(dir) ?? defaultRules();\n writeFileSync(policyPath, JSON.stringify(compile(rules, agent.kid), null, 2) + \"\\n\");\n }\n return { agentKid: agent.kid, policyPath, rulesPath: rulesFile };\n}\n\n/** Install the hook into the agent's own config file, merging with anything already\n * there (idempotent — safe to run twice). Returns the file it wrote. This is what\n * lets `connect` finish setup without the user hand-editing JSON.\n * `cwd` defaults to the current directory (the project root the user runs it in). */\nexport function installHarness(\n harness: \"claude\" | \"cursor\" | \"codex\",\n cwd: string = process.cwd(),\n /** The command to run per tool call. Defaults to the portable `npx` form; `init`\n * passes a pinned absolute path when it has one, which is ~7x faster to start. */\n command?: string,\n): string {\n const isRecord = (v: unknown): v is Record<string, unknown> => typeof v === \"object\" && v !== null && !Array.isArray(v);\n const file = projectHarnessFile(harness, cwd);\n const config = readHarnessConfig(file); // throws, leaving the file intact, if it is not a JSON object\n backupHarnessConfig(file); // keep the user's original beside it before the first change\n mkdirSync(dirname(file), { recursive: true });\n const hooks = isRecord(config.hooks) ? config.hooks : (config.hooks = {});\n const forHarness = (h: \"claude\" | \"cursor\" | \"codex\"): string =>\n command ? command.replace(/\\b(claude|cursor|codex)$/, h) : hookCommand(h);\n const cursorCmd = forHarness(\"cursor\");\n const claudeCmd = forHarness(\"claude\");\n const codexCmd = forHarness(\"codex\");\n // Match any existing Scopebond hook entry — npx, pinned absolute (either path\n // separator) or a legacy bare `scopebond-hook` — so re-running replaces the entry\n // instead of appending a second one. One shared matcher with `install`: a private\n // copy here missed the pinned Windows form and duplicated the hook.\n const entryMatches = harnessEntryMatches;\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 existing = list.findIndex(entryMatches);\n if (existing >= 0) list[existing] = { command: cursorCmd }; else list.push({ command: cursorCmd });\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 existing = list.findIndex(entryMatches);\n // Codex matchers are regular expressions. No matcher means all supported tools.\n const entry = harness === \"codex\"\n ? { hooks: [{ type: \"command\", command: codexCmd, timeout: 30, statusMessage: \"Checking this action with Scopebond\" }] }\n : { matcher: \"*\", hooks: [{ type: \"command\", command: claudeCmd }] };\n if (existing >= 0) list[existing] = entry; else list.push(entry);\n }\n writeFileSync(file, JSON.stringify(config, null, 2) + \"\\n\");\n return file;\n}\n\n/** The harness configuration snippet to install the hook by hand. Defaults to the\n * version-pinned `npx` form; `init --no-install` passes the pinned path it chose, so\n * the printed snippet matches what `init` would have written. */\nexport function harnessSnippet(harness: \"claude\" | \"cursor\" | \"codex\", command?: string): string {\n const cmdFor = (h: \"claude\" | \"cursor\" | \"codex\"): string =>\n command ? command.replace(/\\b(claude|cursor|codex)$/, h) : hookCommand(h);\n if (harness === \"cursor\") {\n const cmd = cmdFor(\"cursor\");\n return JSON.stringify({\n version: 1,\n hooks: {\n beforeShellExecution: [{ command: cmd }],\n beforeMCPExecution: [{ command: cmd }],\n beforeReadFile: [{ command: cmd }],\n afterFileEdit: [{ command: cmd }],\n },\n }, null, 2);\n }\n if (harness === \"codex\") {\n return JSON.stringify({\n hooks: {\n PreToolUse: [{\n hooks: [{ type: \"command\", command: cmdFor(\"codex\"), timeout: 30, statusMessage: \"Checking this action with Scopebond\" }],\n }],\n },\n }, null, 2);\n }\n return JSON.stringify({\n hooks: {\n PreToolUse: [{ matcher: \"*\", hooks: [{ type: \"command\", command: cmdFor(\"claude\") }] }],\n },\n }, null, 2);\n}\n"]}
|
package/dist/install.d.ts
CHANGED
|
@@ -4,11 +4,55 @@ export declare function userHome(): string;
|
|
|
4
4
|
/** The user-level agent config file for a harness. `HOME`/`USERPROFILE` (via homedir)
|
|
5
5
|
* roots it; SCOPEBOND_HOME does not move the agent's own config. */
|
|
6
6
|
export declare function userHarnessFile(harness: Harness): string;
|
|
7
|
+
/** The project-level agent config file for a harness — what `init` writes, so the
|
|
8
|
+
* hook travels with the repository rather than the machine. */
|
|
9
|
+
export declare function projectHarnessFile(harness: Harness, cwd?: string): string;
|
|
10
|
+
/** Which config files actually carry a Scopebond hook for this harness. There are two
|
|
11
|
+
* places it can live — the project config `init` writes and the user config `install`
|
|
12
|
+
* writes — and `status`/`doctor` must look at both: checking only the user one told
|
|
13
|
+
* everyone who ran `init` that their install had failed. */
|
|
14
|
+
export interface HarnessScopes {
|
|
15
|
+
project: string | null;
|
|
16
|
+
user: string | null;
|
|
17
|
+
}
|
|
18
|
+
export declare function harnessScopes(harness: Harness, cwd?: string): HarnessScopes;
|
|
19
|
+
/** One word for where a harness is wired, for a status line. */
|
|
20
|
+
export declare function harnessScopeLabel(scopes: HarnessScopes): string;
|
|
21
|
+
/** The file in the user home that pins trusted project policies: absolute project
|
|
22
|
+
* config dir → SHA-256 of its policy.json. It lives under the protected home, so the
|
|
23
|
+
* governed agent cannot write it. */
|
|
24
|
+
export declare function trustedProjectsFile(): string;
|
|
25
|
+
/** Pin the project's current policy as trusted. Editing the policy afterwards un-trusts
|
|
26
|
+
* it until the user trusts it again, so a rewrite by the agent never takes effect. */
|
|
27
|
+
export declare function trustProjectPolicy(dir: string): string;
|
|
28
|
+
/** Whether the project's policy is exactly the one the user trusted. */
|
|
29
|
+
export declare function isTrustedProject(dir: string): boolean;
|
|
7
30
|
/** Resolve the config dir for a hook event, most specific first:
|
|
8
31
|
* explicit override → the payload's project → $CLAUDE_PROJECT_DIR → the user home.
|
|
9
32
|
* A project dir counts only when it has been scaffolded (a policy.json), so a bare
|
|
10
|
-
* working directory never shadows the user-level install
|
|
33
|
+
* working directory never shadows the user-level install — and, once a user-level
|
|
34
|
+
* install exists, only when its policy is trusted (`isTrustedProject`). Otherwise the
|
|
35
|
+
* user's own policy governs: a repository cannot bring a weaker one with it. */
|
|
11
36
|
export declare function resolveConfigDir(payloadCwd: string | undefined): string;
|
|
37
|
+
/** A project policy present but ignored because it is not trusted (for status/doctor). */
|
|
38
|
+
export declare function untrustedProjectPolicy(payloadCwd: string | undefined): string | null;
|
|
39
|
+
export declare const isScopebondHookCommand: (cmd: unknown) => boolean;
|
|
40
|
+
/** Whether a harness hook entry (either shape: a bare command, or a group with a
|
|
41
|
+
* nested `hooks` array) is a Scopebond entry. */
|
|
42
|
+
export declare const harnessEntryMatches: (e: unknown) => boolean;
|
|
43
|
+
/** Read an agent's JSON config for merging. A missing file is an empty config; a file
|
|
44
|
+
* that exists but does not parse as a JSON object is an error — rewriting it would
|
|
45
|
+
* silently delete the user's other settings, so the installer stops instead. */
|
|
46
|
+
export declare function readHarnessConfig(file: string): Record<string, unknown>;
|
|
47
|
+
/** Copy an agent config aside before changing it, once. Re-running an installer must not
|
|
48
|
+
* overwrite the backup with the already-modified file, or the original is lost on the
|
|
49
|
+
* second run — so an existing backup is left alone. Returns the backup path when one was
|
|
50
|
+
* written.
|
|
51
|
+
*
|
|
52
|
+
* This exists because these installers rewrite files the user did not create and may not
|
|
53
|
+
* know are involved (`~/.claude/settings.json` holds their theme, plugins and
|
|
54
|
+
* permissions), and the undo was previously "hope the merge was right". */
|
|
55
|
+
export declare function backupHarnessConfig(file: string): string | null;
|
|
12
56
|
/** Write (idempotently) a harness hook entry pointing at an explicit command string.
|
|
13
57
|
* Shared by the project installer (npx command) and the user installer (absolute path). */
|
|
14
58
|
export declare function writeHarnessConfig(file: string, harness: Harness, command: string): string;
|
|
@@ -19,10 +63,26 @@ export declare function removeHarnessConfig(file: string): boolean;
|
|
|
19
63
|
export declare function cursorDetected(): boolean;
|
|
20
64
|
/** Detect a Codex install by its user config directory. */
|
|
21
65
|
export declare function codexDetected(): boolean;
|
|
22
|
-
/** The absolute command a
|
|
23
|
-
*
|
|
66
|
+
/** The absolute command a harness entry runs: the current Node executable and the
|
|
67
|
+
* absolute path to this CLI, so it does not depend on PATH.
|
|
68
|
+
*
|
|
69
|
+
* Both paths are always quoted on Windows, even without a space in them. A harness
|
|
70
|
+
* runs this string through a shell, and that shell may be bash (Git Bash, WSL, a
|
|
71
|
+
* dev container), where an unquoted Windows path loses every backslash and the
|
|
72
|
+
* hook dies with MODULE_NOT_FOUND. Inside double quotes bash leaves a backslash
|
|
73
|
+
* alone unless it precedes $ ` " \ or a newline — none of which occur in a Windows
|
|
74
|
+
* path — and cmd.exe and PowerShell accept quoted paths too. */
|
|
24
75
|
export declare function absoluteHookCommand(cliPath: string, harness: Harness): string;
|
|
25
76
|
export declare function isHarnessConfigured(file: string): boolean;
|
|
77
|
+
/** Every Scopebond command string a harness config currently runs. `doctor` uses it to
|
|
78
|
+
* check that a pinned command still resolves: a hook entry that cannot start is worse
|
|
79
|
+
* than none, because a harness can read the failure as "no hook". */
|
|
80
|
+
export declare function configuredHookCommands(file: string): string[];
|
|
81
|
+
/** Whether a configured hook command can actually start. An `npx` form always can
|
|
82
|
+
* (npm resolves it); a pinned command names two absolute paths — the Node binary and
|
|
83
|
+
* the CLI — and either can go away: a Node version manager switching versions, or the
|
|
84
|
+
* Scopebond home being cleared. Both are exactly what this check exists to catch. */
|
|
85
|
+
export declare function hookCommandResolves(command: string): boolean;
|
|
26
86
|
/** Remove the user-level home (keys, policy, receipts). */
|
|
27
87
|
export declare function purgeHome(): void;
|
|
28
88
|
//# sourceMappingURL=install.d.ts.map
|
package/dist/install.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEpD,gFAAgF;AAChF,wBAAgB,QAAQ,IAAI,MAAM,CAEjC;AAED;qEACqE;AACrE,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAIxD;AAED;gEACgE;AAChE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,GAAE,MAAsB,GAAG,MAAM,CAIxF;AAED;;;6DAG6D;AAC7D,MAAM,WAAW,aAAa;IAAG,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE;AAE9E,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,GAAE,MAAsB,GAAG,aAAa,CAO1F;AAED,gEAAgE;AAChE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAK/D;AAED;;sCAEsC;AACtC,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAYD;uFACuF;AACvF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAQtD;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGrD;AAED;;;;;iFAKiF;AACjF,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAavE;AAED,0FAA0F;AAC1F,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAIpF;AAyBD,eAAO,MAAM,sBAAsB,GAAI,KAAK,OAAO,KAAG,OAgBrD,CAAC;AAGF;kDACkD;AAClD,eAAO,MAAM,mBAAmB,GAAI,GAAG,OAAO,KAAG,OACkF,CAAC;AAGpI;;iFAEiF;AACjF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUvE;AAED;;;;;;;4EAO4E;AAC5E,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK/D;AAED;4FAC4F;AAC5F,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAyB1F;AAED;gDACgD;AAChD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUzD;AAED,4DAA4D;AAC5D,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAED,2DAA2D;AAC3D,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAED;;;;;;;;iEAQiE;AACjE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAG7E;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOzD;AAED;;sEAEsE;AACtE,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAc7D;AAED;;;sFAGsF;AACtF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAa5D;AAED,2DAA2D;AAC3D,wBAAgB,SAAS,IAAI,IAAI,CAEhC"}
|