@scopebond/hook 0.2.1 → 0.4.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 +36 -6
- package/dist/cli.js +289 -23
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +25 -12
- package/dist/init.js.map +1 -1
- package/dist/install.d.ts +26 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +132 -0
- package/dist/install.js.map +1 -0
- package/dist/map.d.ts +12 -6
- package/dist/map.d.ts.map +1 -1
- package/dist/map.js +116 -49
- package/dist/map.js.map +1 -1
- package/dist/minimize.d.ts +5 -0
- package/dist/minimize.d.ts.map +1 -1
- package/dist/minimize.js +58 -12
- package/dist/minimize.js.map +1 -1
- package/dist/runtime.d.ts +11 -3
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +57 -9
- package/dist/runtime.js.map +1 -1
- package/dist/shell.d.ts +26 -0
- package/dist/shell.d.ts.map +1 -0
- package/dist/shell.js +331 -0
- package/dist/shell.js.map +1 -0
- package/dist/version.d.ts +11 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +30 -0
- package/dist/version.js.map +1 -0
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -10,17 +10,47 @@ optionally mirrored to your Scopebond Cloud workspace for monitoring.
|
|
|
10
10
|
- **Ceiling:** it governs actions the harness routes through its tool system
|
|
11
11
|
(shell, file, MCP, web). A process started outside the harness is not covered.
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Install once for your machine
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
npm i -g @scopebond/hook
|
|
17
|
+
scopebond install # detects Cursor; add --claude / --cursor to pick
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`install` sets Scopebond up **once per developer machine**, not per repository: it
|
|
21
|
+
scaffolds a user-level home (`~/.scopebond`, override `SCOPEBOND_HOME`) with a
|
|
22
|
+
signing key, a countersigning key and a starter policy, and registers the hook by
|
|
23
|
+
absolute path in your user-level `~/.claude/settings.json` (and `~/.cursor/hooks.json`
|
|
24
|
+
when Cursor is present). Every project you open is then governed, and a project-local
|
|
25
|
+
`.scopebond/` still takes precedence when you want a per-repo policy.
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
scopebond status # home, which agents are configured, cloud, receipts
|
|
29
|
+
scopebond doctor # node version, config location, cloud reachability
|
|
30
|
+
scopebond log # recent decisions scopebond verify # offline
|
|
31
|
+
scopebond uninstall # remove the hook (add --purge to delete keys too)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Install as a Claude Code plugin
|
|
35
|
+
|
|
36
|
+
Scopebond is also a Claude Code plugin (this repo is a plugin marketplace):
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
/plugin marketplace add avouro-com/scopebond
|
|
40
|
+
/plugin install scopebond
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Per-repository enroll (alternative)
|
|
14
44
|
|
|
15
45
|
```
|
|
16
46
|
npx @scopebond/hook init # Claude Code (or: init --cursor)
|
|
17
47
|
```
|
|
18
48
|
|
|
19
|
-
`init` scaffolds `.scopebond/` (a machine signing key, a
|
|
20
|
-
starter policy — "protect main and production paths" — and a
|
|
21
|
-
of it is committed) and
|
|
22
|
-
`.
|
|
23
|
-
|
|
49
|
+
`init` scaffolds `.scopebond/` in the current project (a machine signing key, a
|
|
50
|
+
countersigning key, a starter policy — "protect main and production paths" — and a
|
|
51
|
+
`.gitignore` so none of it is committed) and configures `.claude/settings.json` or
|
|
52
|
+
`.cursor/hooks.json`. Then run one safe command in the agent and see the receipt in
|
|
53
|
+
`.scopebond/receipts.db`.
|
|
24
54
|
|
|
25
55
|
## Connect it to your workspace (optional)
|
|
26
56
|
|
package/dist/cli.js
CHANGED
|
@@ -4,19 +4,42 @@
|
|
|
4
4
|
//
|
|
5
5
|
// scopebond-hook claude evaluate a Claude Code PreToolUse call (stdin JSON)
|
|
6
6
|
// scopebond-hook cursor evaluate a Cursor hook event (stdin JSON)
|
|
7
|
-
// scopebond-hook init [--cursor]
|
|
7
|
+
// scopebond-hook init [--cursor] [--no-install] scaffold keys + starter policy and configure the agent
|
|
8
8
|
// scopebond-hook connect <url> <bundle.json> [--cursor]
|
|
9
9
|
// enroll with a Cloud workspace and start exporting
|
|
10
|
+
// scopebond-hook log [-n N] show the most recent local receipts
|
|
11
|
+
// scopebond-hook verify verify every local receipt offline against the attester key
|
|
12
|
+
// scopebond-hook test "<shell command>" show the decision for a command without recording it
|
|
10
13
|
// scopebond-hook flush deliver any queued receipts to Cloud now
|
|
11
14
|
//
|
|
12
15
|
// Config dir: $SCOPEBOND_HOOK_DIR, else ./.scopebond
|
|
13
16
|
// Fail-closed: any error denies the action with a repair message.
|
|
14
|
-
import { readFileSync, existsSync } from "node:fs";
|
|
17
|
+
import { readFileSync, existsSync, mkdtempSync, rmSync } from "node:fs";
|
|
15
18
|
import { join } from "node:path";
|
|
16
|
-
import {
|
|
19
|
+
import { tmpdir } from "node:os";
|
|
20
|
+
import { execFileSync } from "node:child_process";
|
|
21
|
+
import { verifyReceipt } from "@scopebond/gateway";
|
|
22
|
+
import { openReceiptStore, loadOrCreateAttester } from "@scopebond/gateway/node";
|
|
23
|
+
import { mapClaudeToolUse, mapCursorEvent, fillPushBranch } from "./map.js";
|
|
17
24
|
import { createHookRuntime } from "./runtime.js";
|
|
18
25
|
import { scaffold, harnessSnippet, installHarness } from "./init.js";
|
|
26
|
+
import { userHome, userHarnessFile, resolveConfigDir, writeHarnessConfig, removeHarnessConfig, cursorDetected, absoluteHookCommand, isHarnessConfigured, purgeHome, } from "./install.js";
|
|
19
27
|
import { connectCloud, loadConnection } from "./cloud.js";
|
|
28
|
+
import { cliCommand, hookVersion } from "./version.js";
|
|
29
|
+
import { fileURLToPath } from "node:url";
|
|
30
|
+
/** The current git branch in `cwd` (best-effort). A bare `git push` pushes it, so
|
|
31
|
+
* the runtime fills it in before evaluating; on failure the ref stays absent and
|
|
32
|
+
* the starter policy fails closed. */
|
|
33
|
+
function currentBranch(cwd) {
|
|
34
|
+
try {
|
|
35
|
+
return execFileSync("git", ["-C", cwd, "symbolic-ref", "--quiet", "--short", "HEAD"], {
|
|
36
|
+
encoding: "utf8", stdio: ["ignore", "pipe", "ignore"],
|
|
37
|
+
}).trim() || null;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
20
43
|
/** Read an enrollment bundle from a file path, an inline base64 blob, or inline raw
|
|
21
44
|
* JSON (the portal hands out a base64 blob so it is one clean argument). */
|
|
22
45
|
function readBundleArg(bundleArg, stdin) {
|
|
@@ -79,22 +102,19 @@ async function runClaude() {
|
|
|
79
102
|
denyClaude("hook received invalid JSON on stdin");
|
|
80
103
|
}
|
|
81
104
|
try {
|
|
82
|
-
const
|
|
83
|
-
const
|
|
105
|
+
const cwd = input?.cwd ? String(input.cwd) : process.cwd();
|
|
106
|
+
const runtime = createHookRuntime(runtimePaths(resolveConfigDir(cwd)));
|
|
107
|
+
const decision = await runtime.evaluate(fillPushBranch(mapClaudeToolUse(input), currentBranch(cwd)));
|
|
84
108
|
await runtime.flush();
|
|
85
109
|
if (decision.decision === "deny")
|
|
86
110
|
denyClaude(decision.reason);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}) + "\n");
|
|
91
|
-
process.exit(0);
|
|
92
|
-
}
|
|
93
|
-
// not_evaluated: defer to the harness default (grant nothing, block nothing).
|
|
111
|
+
// allow and not_evaluated both DEFER to Claude Code's own permission flow: the
|
|
112
|
+
// hook records the receipt but never returns permissionDecision:"allow", which
|
|
113
|
+
// would suppress the user's normal review. Scopebond blocks; it does not approve.
|
|
94
114
|
process.exit(0);
|
|
95
115
|
}
|
|
96
116
|
catch (error) {
|
|
97
|
-
denyClaude(`Scopebond hook failed closed: ${error.message}. Repair: run
|
|
117
|
+
denyClaude(`Scopebond hook failed closed: ${error.message}. Repair: run \`${cliCommand("init")}\`.`);
|
|
98
118
|
}
|
|
99
119
|
}
|
|
100
120
|
async function runCursor() {
|
|
@@ -107,15 +127,18 @@ async function runCursor() {
|
|
|
107
127
|
let permission = "deny";
|
|
108
128
|
let message = "Scopebond hook failed closed";
|
|
109
129
|
try {
|
|
110
|
-
const
|
|
111
|
-
const
|
|
130
|
+
const cwd = input?.cwd ? String(input.cwd) : process.cwd();
|
|
131
|
+
const runtime = createHookRuntime(runtimePaths(resolveConfigDir(cwd)));
|
|
132
|
+
const decision = await runtime.evaluate(fillPushBranch(mapCursorEvent(event, input), currentBranch(cwd)));
|
|
112
133
|
await runtime.flush();
|
|
113
|
-
|
|
134
|
+
// Only an out-of-policy action is denied outright; an allowed or unevaluated
|
|
135
|
+
// action defers to Cursor's own prompt ("ask"), never a silent auto-allow.
|
|
136
|
+
permission = decision.decision === "deny" ? "deny" : "ask";
|
|
114
137
|
message = decision.reason;
|
|
115
138
|
}
|
|
116
139
|
catch (error) {
|
|
117
140
|
permission = "deny";
|
|
118
|
-
message = `Scopebond hook failed closed: ${error.message}. Repair: run
|
|
141
|
+
message = `Scopebond hook failed closed: ${error.message}. Repair: run \`${cliCommand("init")}\`.`;
|
|
119
142
|
}
|
|
120
143
|
process.stdout.write(JSON.stringify({ permission, agentMessage: message }) + "\n");
|
|
121
144
|
process.exit(0);
|
|
@@ -128,11 +151,127 @@ function runInit(args) {
|
|
|
128
151
|
console.log(` machine key ${agentKid}`);
|
|
129
152
|
console.log(` policy ${policyPath} (starter — edit the limits)`);
|
|
130
153
|
console.log("");
|
|
131
|
-
|
|
132
|
-
|
|
154
|
+
// Configure the agent automatically by default (idempotent), so there is no
|
|
155
|
+
// hand-editing step; --no-install prints the snippet instead.
|
|
156
|
+
if (!args.includes("--no-install")) {
|
|
157
|
+
const file = installHarness(harness);
|
|
158
|
+
console.log(`✓ ${harness === "cursor" ? "Cursor" : "Claude Code"} configured in ${file}`);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
console.log(`Add this to your ${harness === "cursor" ? ".cursor/hooks.json" : ".claude/settings.json"}:`);
|
|
162
|
+
console.log(harnessSnippet(harness));
|
|
163
|
+
}
|
|
133
164
|
console.log("");
|
|
134
|
-
|
|
135
|
-
|
|
165
|
+
// Print the runnable `npx` form: after `npx @scopebond/hook init` there is no
|
|
166
|
+
// `scopebond-hook` binary on PATH, so a bare `scopebond-hook log` would fail.
|
|
167
|
+
console.log(`Next: run one command in the agent, then \`${cliCommand("log")}\` to see the receipt`);
|
|
168
|
+
console.log(`and \`${cliCommand("verify")}\` to check it offline. Try \`${cliCommand('test "rm -rf /"')}\`.`);
|
|
169
|
+
console.log(`To send receipts to a workspace: ${cliCommand("connect <workspace-url> <enrollment>")}`);
|
|
170
|
+
}
|
|
171
|
+
function decisionOf(payload) {
|
|
172
|
+
const rr = String(payload.realtime_result ?? "");
|
|
173
|
+
const state = String(payload.execution?.state ?? "");
|
|
174
|
+
if (state === "observed_not_evaluated")
|
|
175
|
+
return "not_evaluated";
|
|
176
|
+
if (rr === "deny")
|
|
177
|
+
return state === "cooperative_allow" || state === "executed" ? "monitor" : "deny";
|
|
178
|
+
return "allow";
|
|
179
|
+
}
|
|
180
|
+
function describeIntent(payload) {
|
|
181
|
+
const intent = (payload.intent ?? {});
|
|
182
|
+
const p = (intent.params ?? {});
|
|
183
|
+
const bits = intent.action_type === "shell.exec" ? String(p.program ?? "")
|
|
184
|
+
: intent.action_type === "git.push" ? `${p.remote ?? ""} ${p.ref ?? ""}`.trim()
|
|
185
|
+
: intent.action_type === "file.write" || intent.action_type === "file.read" ? String(p.path ?? "")
|
|
186
|
+
: intent.action_type === "mcp.tool.call" ? `${p.server ?? ""}/${p.tool ?? ""}`
|
|
187
|
+
: intent.action_type === "net.fetch" ? String(p.host ?? "") : "";
|
|
188
|
+
return `${String(intent.action_type ?? "?")}${bits ? ` ${bits}` : ""}`;
|
|
189
|
+
}
|
|
190
|
+
async function runLog(args) {
|
|
191
|
+
const dir = resolveConfigDir(process.cwd());
|
|
192
|
+
const dbPath = join(dir, "receipts.db");
|
|
193
|
+
if (!existsSync(dbPath)) {
|
|
194
|
+
console.log("no receipts yet — run a command in the agent first.");
|
|
195
|
+
process.exit(0);
|
|
196
|
+
}
|
|
197
|
+
const nIdx = args.indexOf("-n");
|
|
198
|
+
const n = nIdx >= 0 ? Math.max(1, Number(args[nIdx + 1]) || 20) : 20;
|
|
199
|
+
const { store } = openReceiptStore({ db: dbPath });
|
|
200
|
+
const all = await Promise.resolve(store.list());
|
|
201
|
+
const recent = all.slice(-n);
|
|
202
|
+
if (recent.length === 0) {
|
|
203
|
+
console.log("no receipts yet.");
|
|
204
|
+
process.exit(0);
|
|
205
|
+
}
|
|
206
|
+
for (const r of recent) {
|
|
207
|
+
const p = r.payload;
|
|
208
|
+
console.log(`${String(p.timestamp ?? "")} ${decisionOf(p).padEnd(13)} ${describeIntent(p)}`);
|
|
209
|
+
}
|
|
210
|
+
console.log(`\n${recent.length} of ${all.length} receipt(s). Verify them: ${cliCommand("verify")}`);
|
|
211
|
+
}
|
|
212
|
+
async function runVerify() {
|
|
213
|
+
const dir = resolveConfigDir(process.cwd());
|
|
214
|
+
const dbPath = join(dir, "receipts.db");
|
|
215
|
+
const attesterPath = join(dir, "attester.key");
|
|
216
|
+
if (!existsSync(dbPath) || !existsSync(attesterPath)) {
|
|
217
|
+
console.log("nothing to verify yet (no receipts or no attester key).");
|
|
218
|
+
process.exit(0);
|
|
219
|
+
}
|
|
220
|
+
const { attester } = loadOrCreateAttester({ file: attesterPath });
|
|
221
|
+
const { store } = openReceiptStore({ db: dbPath });
|
|
222
|
+
const all = await Promise.resolve(store.list());
|
|
223
|
+
let ok = 0;
|
|
224
|
+
const bad = [];
|
|
225
|
+
for (const r of all) {
|
|
226
|
+
const result = verifyReceipt(r, attester.publicKeyPem);
|
|
227
|
+
if (result.valid)
|
|
228
|
+
ok += 1;
|
|
229
|
+
else {
|
|
230
|
+
const failed = Object.entries(result).filter(([k, v]) => k.endsWith("_valid") && v === false).map(([k]) => k);
|
|
231
|
+
bad.push(`${String(r.payload.timestamp ?? "")}: ${failed.join(", ") || "invalid"}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
console.log(`${ok}/${all.length} receipt(s) verify offline against ${attesterPath}.`);
|
|
235
|
+
if (bad.length) {
|
|
236
|
+
for (const b of bad)
|
|
237
|
+
console.error(` ✗ ${b}`);
|
|
238
|
+
process.exit(1);
|
|
239
|
+
}
|
|
240
|
+
process.exit(0);
|
|
241
|
+
}
|
|
242
|
+
async function runTest(args) {
|
|
243
|
+
const command = args.find((a) => !a.startsWith("-"));
|
|
244
|
+
if (!command) {
|
|
245
|
+
console.error('usage: scopebond-hook test "<shell command>"');
|
|
246
|
+
process.exit(1);
|
|
247
|
+
}
|
|
248
|
+
const dir = resolveConfigDir(process.cwd());
|
|
249
|
+
if (!existsSync(join(dir, "policy.json"))) {
|
|
250
|
+
console.error("no policy yet — run `scopebond-hook init` first.");
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
// Evaluate against the real policy and keys, but a throwaway store, so `test`
|
|
254
|
+
// never records a receipt or exports anything.
|
|
255
|
+
const tmp = mkdtempSync(join(tmpdir(), "sb-hook-test-"));
|
|
256
|
+
try {
|
|
257
|
+
const runtime = createHookRuntime({
|
|
258
|
+
policyPath: join(dir, "policy.json"), keyPath: join(dir, "agent.key"),
|
|
259
|
+
attesterPath: join(dir, "attester.key"), dbPath: join(tmp, "receipts.db"),
|
|
260
|
+
strict: process.argv.includes("--strict"),
|
|
261
|
+
});
|
|
262
|
+
const mapped = fillPushBranch(mapClaudeToolUse({ tool_name: "Bash", tool_input: { command } }), currentBranch(process.cwd()));
|
|
263
|
+
console.log(`command: ${command}`);
|
|
264
|
+
for (const m of mapped) {
|
|
265
|
+
const d = await runtime.evaluateOne(m);
|
|
266
|
+
console.log(` ${describeIntent({ intent: m.intent }).padEnd(28)} → ${d.decision}${d.reason ? ` (${d.reason})` : ""}`);
|
|
267
|
+
}
|
|
268
|
+
const overall = await runtime.evaluate(mapped);
|
|
269
|
+
console.log(`\noverall: ${overall.decision}${overall.reason ? ` · ${overall.reason}` : ""}`);
|
|
270
|
+
process.exit(overall.decision === "deny" ? 2 : 0);
|
|
271
|
+
}
|
|
272
|
+
finally {
|
|
273
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
274
|
+
}
|
|
136
275
|
}
|
|
137
276
|
async function runConnect(args) {
|
|
138
277
|
const positional = args.filter((a) => !a.startsWith("--"));
|
|
@@ -179,7 +318,7 @@ async function runConnect(args) {
|
|
|
179
318
|
}
|
|
180
319
|
}
|
|
181
320
|
async function runFlush() {
|
|
182
|
-
const dir =
|
|
321
|
+
const dir = resolveConfigDir(process.cwd());
|
|
183
322
|
if (!loadConnection(dir)) {
|
|
184
323
|
console.error("not connected to a workspace; run `scopebond-hook connect` first");
|
|
185
324
|
process.exit(1);
|
|
@@ -190,6 +329,109 @@ async function runFlush() {
|
|
|
190
329
|
console.log(`flushed; ${status?.pending ?? 0} receipt(s) still pending${status?.lastError ? ` (last error: ${status.lastError})` : ""}`);
|
|
191
330
|
process.exit(status && status.pending > 0 ? 1 : 0);
|
|
192
331
|
}
|
|
332
|
+
/** The absolute path to this CLI file, for registering the hook by absolute path. */
|
|
333
|
+
function cliPath() {
|
|
334
|
+
return fileURLToPath(import.meta.url);
|
|
335
|
+
}
|
|
336
|
+
/** `install` — the once-per-machine, user-level install (SB112). Scaffolds the
|
|
337
|
+
* user home and registers the hook by absolute path in the user-level agent config,
|
|
338
|
+
* so every project a developer opens is governed without a per-repo `init`. */
|
|
339
|
+
function runInstall(args) {
|
|
340
|
+
const dir = userHome();
|
|
341
|
+
const { agentKid, policyPath } = scaffold(dir, { force: args.includes("--force") });
|
|
342
|
+
console.log(`Scopebond installed for this user in ${dir}`);
|
|
343
|
+
console.log(` machine key ${agentKid}`);
|
|
344
|
+
console.log(` policy ${policyPath} (starter — edit the limits)`);
|
|
345
|
+
console.log("");
|
|
346
|
+
const harnesses = args.includes("--cursor")
|
|
347
|
+
? ["cursor"]
|
|
348
|
+
: args.includes("--claude") ? ["claude"] : (cursorDetected() ? ["claude", "cursor"] : ["claude"]);
|
|
349
|
+
if (!args.includes("--no-install")) {
|
|
350
|
+
for (const h of harnesses) {
|
|
351
|
+
const file = writeHarnessConfig(userHarnessFile(h), h, absoluteHookCommand(cliPath(), h));
|
|
352
|
+
console.log(`✓ ${h === "cursor" ? "Cursor" : "Claude Code"} configured in ${file}`);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
console.log("Add this to your user-level agent config:");
|
|
357
|
+
console.log(harnessSnippet(harnesses[0]));
|
|
358
|
+
}
|
|
359
|
+
console.log("");
|
|
360
|
+
console.log("A project-local .scopebond still takes precedence when present.");
|
|
361
|
+
console.log(`Check it: ${cliCommand("doctor")} · see decisions: ${cliCommand("log")}`);
|
|
362
|
+
console.log(`To send receipts to a workspace: ${cliCommand("connect <workspace-url> <enrollment>")}`);
|
|
363
|
+
}
|
|
364
|
+
function runStatus() {
|
|
365
|
+
const home = userHome();
|
|
366
|
+
const installed = existsSync(join(home, "policy.json"));
|
|
367
|
+
const claude = isHarnessConfigured(userHarnessFile("claude"));
|
|
368
|
+
const cursor = isHarnessConfigured(userHarnessFile("cursor"));
|
|
369
|
+
const connected = !!loadConnection(resolveConfigDir(process.cwd()));
|
|
370
|
+
const dbPath = join(resolveConfigDir(process.cwd()), "receipts.db");
|
|
371
|
+
console.log(`Scopebond hook ${hookVersion()}`);
|
|
372
|
+
console.log(` user home ${home} ${installed ? "(installed)" : "(not installed — run `scopebond install`)"}`);
|
|
373
|
+
console.log(` active config ${resolveConfigDir(process.cwd())}`);
|
|
374
|
+
console.log(` Claude Code ${claude ? "configured" : "not configured"}`);
|
|
375
|
+
console.log(` Cursor ${cursor ? "configured" : cursorDetected() ? "detected, not configured" : "not detected"}`);
|
|
376
|
+
console.log(` cloud workspace ${connected ? "connected" : "not connected (local only)"}`);
|
|
377
|
+
console.log(` local receipts ${existsSync(dbPath) ? dbPath : "none yet"}`);
|
|
378
|
+
}
|
|
379
|
+
async function runDoctor() {
|
|
380
|
+
const problems = [];
|
|
381
|
+
const [major, minor] = process.versions.node.split(".").map(Number);
|
|
382
|
+
const nodeOk = major > 22 || (major === 22 && minor >= 13);
|
|
383
|
+
console.log(`Scopebond doctor`);
|
|
384
|
+
console.log(` node ${process.versions.node} ${nodeOk ? "ok" : "TOO OLD (need >=22.13)"}`);
|
|
385
|
+
if (!nodeOk)
|
|
386
|
+
problems.push("node >=22.13 is required (the Cloud outbox uses node:sqlite)");
|
|
387
|
+
const cli = cliPath();
|
|
388
|
+
console.log(` cli ${cli} ${existsSync(cli) ? "ok" : "MISSING"}`);
|
|
389
|
+
const active = resolveConfigDir(process.cwd());
|
|
390
|
+
const hasPolicy = existsSync(join(active, "policy.json"));
|
|
391
|
+
console.log(` active config ${active} ${hasPolicy ? "ok" : "no policy (run `scopebond install` or `init`)"}`);
|
|
392
|
+
if (!hasPolicy)
|
|
393
|
+
problems.push("no policy found in the active config dir");
|
|
394
|
+
const connection = loadConnection(active);
|
|
395
|
+
if (!connection) {
|
|
396
|
+
console.log(` cloud not connected (local only) — receipts stay on this machine`);
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
let reachable = "unknown";
|
|
400
|
+
try {
|
|
401
|
+
const res = await fetch(new URL("/healthz", connection.url).toString(), { method: "GET" });
|
|
402
|
+
reachable = res.ok ? "reachable" : `unhealthy (${res.status})`;
|
|
403
|
+
}
|
|
404
|
+
catch (error) {
|
|
405
|
+
reachable = `unreachable (${error.message})`;
|
|
406
|
+
}
|
|
407
|
+
console.log(` cloud ${connection.url} — ${reachable}`);
|
|
408
|
+
}
|
|
409
|
+
console.log(problems.length ? `\n${problems.length} problem(s): ${problems.join("; ")}` : `\nAll good.`);
|
|
410
|
+
process.exit(problems.length ? 1 : 0);
|
|
411
|
+
}
|
|
412
|
+
function runUninstall(args) {
|
|
413
|
+
let removed = 0;
|
|
414
|
+
for (const h of ["claude", "cursor"]) {
|
|
415
|
+
if (removeHarnessConfig(userHarnessFile(h))) {
|
|
416
|
+
console.log(`✓ removed the Scopebond hook from ${userHarnessFile(h)}`);
|
|
417
|
+
removed++;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
if (removed === 0)
|
|
421
|
+
console.log("no user-level harness config found.");
|
|
422
|
+
if (args.includes("--purge")) {
|
|
423
|
+
purgeHome();
|
|
424
|
+
console.log(`✓ purged ${userHome()} (keys, policy, receipts)`);
|
|
425
|
+
}
|
|
426
|
+
else
|
|
427
|
+
console.log(`Kept ${userHome()} (keys, policy, receipts). Use --purge to remove it too.`);
|
|
428
|
+
}
|
|
429
|
+
function runLogin() {
|
|
430
|
+
console.log("Device-code login is not available yet.");
|
|
431
|
+
console.log(`For now, connect with a one-time enrollment from your workspace:`);
|
|
432
|
+
console.log(` ${cliCommand("connect <workspace-url> <enrollment>")}`);
|
|
433
|
+
process.exit(0);
|
|
434
|
+
}
|
|
193
435
|
const [cmd, ...rest] = process.argv.slice(2);
|
|
194
436
|
if (cmd === "claude") {
|
|
195
437
|
await runClaude();
|
|
@@ -200,14 +442,38 @@ else if (cmd === "cursor") {
|
|
|
200
442
|
else if (cmd === "init") {
|
|
201
443
|
runInit(rest);
|
|
202
444
|
}
|
|
445
|
+
else if (cmd === "install") {
|
|
446
|
+
runInstall(rest);
|
|
447
|
+
}
|
|
203
448
|
else if (cmd === "connect") {
|
|
204
449
|
await runConnect(rest);
|
|
205
450
|
}
|
|
451
|
+
else if (cmd === "log") {
|
|
452
|
+
await runLog(rest);
|
|
453
|
+
}
|
|
454
|
+
else if (cmd === "verify") {
|
|
455
|
+
await runVerify();
|
|
456
|
+
}
|
|
457
|
+
else if (cmd === "test") {
|
|
458
|
+
await runTest(rest);
|
|
459
|
+
}
|
|
206
460
|
else if (cmd === "flush") {
|
|
207
461
|
await runFlush();
|
|
208
462
|
}
|
|
463
|
+
else if (cmd === "status") {
|
|
464
|
+
runStatus();
|
|
465
|
+
}
|
|
466
|
+
else if (cmd === "doctor") {
|
|
467
|
+
await runDoctor();
|
|
468
|
+
}
|
|
469
|
+
else if (cmd === "uninstall") {
|
|
470
|
+
runUninstall(rest);
|
|
471
|
+
}
|
|
472
|
+
else if (cmd === "login") {
|
|
473
|
+
runLogin();
|
|
474
|
+
}
|
|
209
475
|
else {
|
|
210
|
-
console.error("usage: scopebond
|
|
476
|
+
console.error("usage: scopebond <claude|cursor|init|install|connect|log|verify|test|flush|status|doctor|uninstall|login> [--cursor] [--claude] [--force] [--strict] [--no-install] [--purge]");
|
|
211
477
|
process.exit(1);
|
|
212
478
|
}
|
|
213
479
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n// scopebond-hook — govern a coding agent's tool calls against policy, in-path,\n// before they run, with a signed local receipt.\n//\n// scopebond-hook claude evaluate a Claude Code PreToolUse call (stdin JSON)\n// scopebond-hook cursor evaluate a Cursor hook event (stdin JSON)\n// scopebond-hook init [--cursor] scaffold keys + starter policy, print the config\n// scopebond-hook connect <url> <bundle.json> [--cursor]\n// enroll with a Cloud workspace and start exporting\n// scopebond-hook flush deliver any queued receipts to Cloud now\n//\n// Config dir: $SCOPEBOND_HOOK_DIR, else ./.scopebond\n// Fail-closed: any error denies the action with a repair message.\n\nimport { readFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { CloudEnrollmentBundle } from \"@scopebond/gateway\";\nimport { mapClaudeToolUse, mapCursorEvent } from \"./map.js\";\nimport { createHookRuntime } from \"./runtime.js\";\nimport { scaffold, harnessSnippet, installHarness } from \"./init.js\";\nimport { connectCloud, loadConnection, connectionPath } from \"./cloud.js\";\n\n/** Read an enrollment bundle from a file path, an inline base64 blob, or inline raw\n * JSON (the portal hands out a base64 blob so it is one clean argument). */\nfunction readBundleArg(bundleArg: string | undefined, stdin: () => string): CloudEnrollmentBundle {\n let text: string;\n if (!bundleArg) text = stdin();\n else if (existsSync(bundleArg)) text = readFileSync(bundleArg, \"utf8\");\n else if (bundleArg.trim().startsWith(\"{\")) text = bundleArg;\n else { try { text = Buffer.from(bundleArg, \"base64\").toString(\"utf8\"); } catch { text = bundleArg; } }\n return JSON.parse(text) as CloudEnrollmentBundle;\n}\n\nfunction configDir(): string {\n return process.env.SCOPEBOND_HOOK_DIR ?? join(process.cwd(), \".scopebond\");\n}\nfunction runtimePaths(dir: string) {\n const connection = loadConnection(dir);\n return {\n policyPath: join(dir, \"policy.json\"),\n keyPath: join(dir, \"agent.key\"),\n attesterPath: join(dir, \"attester.key\"),\n dbPath: join(dir, \"receipts.db\"),\n // Strict: deny (not just observe) tools with no taxonomy mapping.\n strict: process.argv.includes(\"--strict\") || process.env.SCOPEBOND_HOOK_STRICT === \"1\",\n // When connected, auto-export receipts. A short bounded flush keeps the hot path\n // fast; undelivered receipts persist in the durable outbox and flush next time\n // (or via `scopebond-hook flush`, e.g. on a session-end hook).\n ...(connection ? { cloud: { connection, flushTimeoutMs: Number(process.env.SCOPEBOND_HOOK_FLUSH_MS ?? 800) } } : {}),\n };\n}\nfunction readStdin(): string {\n try { return readFileSync(0, \"utf8\"); } catch { return \"\"; }\n}\n\nfunction denyClaude(reason: string): never {\n process.stdout.write(JSON.stringify({\n hookSpecificOutput: { hookEventName: \"PreToolUse\", permissionDecision: \"deny\", permissionDecisionReason: reason },\n }) + \"\\n\");\n process.stderr.write(`Scopebond: ${reason}\\n`);\n process.exit(2);\n}\n\nasync function runClaude(): Promise<void> {\n let input: Record<string, unknown>;\n try { input = JSON.parse(readStdin()); } catch { denyClaude(\"hook received invalid JSON on stdin\"); }\n try {\n const runtime = createHookRuntime(runtimePaths(configDir()));\n const decision = await runtime.evaluate(mapClaudeToolUse(input!));\n await runtime.flush();\n if (decision.decision === \"deny\") denyClaude(decision.reason);\n if (decision.decision === \"allow\") {\n process.stdout.write(JSON.stringify({\n hookSpecificOutput: { hookEventName: \"PreToolUse\", permissionDecision: \"allow\", permissionDecisionReason: decision.reason },\n }) + \"\\n\");\n process.exit(0);\n }\n // not_evaluated: defer to the harness default (grant nothing, block nothing).\n process.exit(0);\n } catch (error) {\n denyClaude(`Scopebond hook failed closed: ${(error as Error).message}. Repair: run \\`scopebond-hook init\\`.`);\n }\n}\n\nasync function runCursor(): Promise<void> {\n let input: Record<string, unknown> = {};\n try { input = JSON.parse(readStdin()); } catch { /* fall through to fail-closed deny below */ }\n const event = String(input?.hook_event_name ?? input?.event ?? process.argv[3] ?? \"\");\n let permission: \"allow\" | \"deny\" | \"ask\" = \"deny\";\n let message = \"Scopebond hook failed closed\";\n try {\n const runtime = createHookRuntime(runtimePaths(configDir()));\n const decision = await runtime.evaluate(mapCursorEvent(event, input));\n await runtime.flush();\n permission = decision.decision === \"deny\" ? \"deny\" : decision.decision === \"allow\" ? \"allow\" : \"ask\";\n message = decision.reason;\n } catch (error) {\n permission = \"deny\";\n message = `Scopebond hook failed closed: ${(error as Error).message}. Repair: run \\`scopebond-hook init\\`.`;\n }\n process.stdout.write(JSON.stringify({ permission, agentMessage: message }) + \"\\n\");\n process.exit(0);\n}\n\nfunction runInit(args: string[]): void {\n const harness = args.includes(\"--cursor\") ? \"cursor\" : \"claude\";\n const dir = configDir();\n const { agentKid, policyPath } = scaffold(dir, { force: args.includes(\"--force\") });\n console.log(`Scopebond hook enrolled in ${dir}`);\n console.log(` machine key ${agentKid}`);\n console.log(` policy ${policyPath} (starter — edit the limits)`);\n console.log(\"\");\n console.log(`Add this to your ${harness === \"cursor\" ? \".cursor/hooks.json\" : \".claude/settings.json\"}:`);\n console.log(harnessSnippet(harness));\n console.log(\"\");\n console.log(\"Then run one safe command in the agent and see the receipt in .scopebond/receipts.db.\");\n console.log(\"To send receipts to your workspace, run: scopebond-hook connect <workspace-url> scopebond-enrollment.json\");\n}\n\nasync function runConnect(args: string[]): Promise<void> {\n const positional = args.filter((a) => !a.startsWith(\"--\"));\n const url = positional[0];\n const bundleArg = positional[1];\n const harness = args.includes(\"--cursor\") ? \"cursor\" : \"claude\";\n if (!url) {\n console.error(\"usage: scopebond-hook connect <workspace-url> <enrollment> [--cursor] [--no-install]\");\n process.exit(1);\n }\n const dir = configDir();\n // One command sets everything up: scaffold the key, attester and starter policy if\n // they do not exist, then enroll and persist the scoped machine credential. The\n // enrollment can be a file, an inline base64 blob (what the portal hands out) or\n // raw JSON — the user never has to save or open a JSON file.\n scaffold(dir, {});\n let bundle: CloudEnrollmentBundle;\n try { bundle = readBundleArg(bundleArg, readStdin); }\n catch { console.error(\"could not read the enrollment (expected a file, inline blob, or JSON on stdin)\"); process.exit(1); }\n try {\n const c = await connectCloud(dir, url, bundle);\n console.log(`✓ Connected to ${c.url}`);\n // Configure the agent automatically (merges into the existing config), unless the\n // caller opts out. This removes the \"paste this snippet\" step.\n if (!args.includes(\"--no-install\")) {\n const file = installHarness(harness);\n console.log(`✓ ${harness === \"cursor\" ? \"Cursor\" : \"Claude Code\"} configured in ${file}`);\n } else {\n console.log(`Add this to your ${harness === \"cursor\" ? \".cursor/hooks.json\" : \".claude/settings.json\"}:`);\n console.log(harnessSnippet(harness));\n }\n console.log(\"\");\n console.log(\"Run your agent — the first action appears in your workspace within seconds.\");\n } catch (error) {\n console.error(`connect failed: ${(error as Error).message}`);\n process.exit(1);\n }\n}\n\nasync function runFlush(): Promise<void> {\n const dir = configDir();\n if (!loadConnection(dir)) { console.error(\"not connected to a workspace; run `scopebond-hook connect` first\"); process.exit(1); }\n const runtime = createHookRuntime(runtimePaths(dir));\n await runtime.exporter?.flush();\n const status = runtime.exporter?.status();\n console.log(`flushed; ${status?.pending ?? 0} receipt(s) still pending${status?.lastError ? ` (last error: ${status.lastError})` : \"\"}`);\n process.exit(status && status.pending > 0 ? 1 : 0);\n}\n\nconst [cmd, ...rest] = process.argv.slice(2);\nif (cmd === \"claude\") { await runClaude(); }\nelse if (cmd === \"cursor\") { await runCursor(); }\nelse if (cmd === \"init\") { runInit(rest); }\nelse if (cmd === \"connect\") { await runConnect(rest); }\nelse if (cmd === \"flush\") { await runFlush(); }\nelse {\n console.error(\"usage: scopebond-hook <claude|cursor|init|connect|flush> [--cursor] [--force] [--strict]\");\n process.exit(1);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n// scopebond-hook — govern a coding agent's tool calls against policy, in-path,\n// before they run, with a signed local receipt.\n//\n// scopebond-hook claude evaluate a Claude Code PreToolUse call (stdin JSON)\n// scopebond-hook cursor evaluate a Cursor hook event (stdin JSON)\n// scopebond-hook init [--cursor] [--no-install] scaffold keys + starter policy and configure the agent\n// scopebond-hook connect <url> <bundle.json> [--cursor]\n// enroll with a Cloud workspace and start exporting\n// scopebond-hook log [-n N] show the most recent local receipts\n// scopebond-hook verify verify every local receipt offline against the attester key\n// scopebond-hook test \"<shell command>\" show the decision for a command without recording it\n// scopebond-hook flush deliver any queued receipts to Cloud now\n//\n// Config dir: $SCOPEBOND_HOOK_DIR, else ./.scopebond\n// Fail-closed: any error denies the action with a repair message.\n\nimport { readFileSync, existsSync, mkdtempSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport { execFileSync } from \"node:child_process\";\nimport type { CloudEnrollmentBundle } from \"@scopebond/gateway\";\nimport { verifyReceipt } from \"@scopebond/gateway\";\nimport { openReceiptStore, loadOrCreateAttester } from \"@scopebond/gateway/node\";\nimport { mapClaudeToolUse, mapCursorEvent, fillPushBranch } from \"./map.js\";\nimport { createHookRuntime } from \"./runtime.js\";\nimport { scaffold, harnessSnippet, installHarness } from \"./init.js\";\nimport {\n userHome, userHarnessFile, resolveConfigDir, writeHarnessConfig, removeHarnessConfig,\n cursorDetected, absoluteHookCommand, isHarnessConfigured, purgeHome, type Harness,\n} from \"./install.js\";\nimport { connectCloud, loadConnection, connectionPath } from \"./cloud.js\";\nimport { cliCommand, hookVersion } from \"./version.js\";\nimport { fileURLToPath } from \"node:url\";\n\n/** The current git branch in `cwd` (best-effort). A bare `git push` pushes it, so\n * the runtime fills it in before evaluating; on failure the ref stays absent and\n * the starter policy fails closed. */\nfunction currentBranch(cwd: string): string | null {\n try {\n return execFileSync(\"git\", [\"-C\", cwd, \"symbolic-ref\", \"--quiet\", \"--short\", \"HEAD\"], {\n encoding: \"utf8\", stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim() || null;\n } catch { return null; }\n}\n\n/** Read an enrollment bundle from a file path, an inline base64 blob, or inline raw\n * JSON (the portal hands out a base64 blob so it is one clean argument). */\nfunction readBundleArg(bundleArg: string | undefined, stdin: () => string): CloudEnrollmentBundle {\n let text: string;\n if (!bundleArg) text = stdin();\n else if (existsSync(bundleArg)) text = readFileSync(bundleArg, \"utf8\");\n else if (bundleArg.trim().startsWith(\"{\")) text = bundleArg;\n else { try { text = Buffer.from(bundleArg, \"base64\").toString(\"utf8\"); } catch { text = bundleArg; } }\n return JSON.parse(text) as CloudEnrollmentBundle;\n}\n\nfunction configDir(): string {\n return process.env.SCOPEBOND_HOOK_DIR ?? join(process.cwd(), \".scopebond\");\n}\nfunction runtimePaths(dir: string) {\n const connection = loadConnection(dir);\n return {\n policyPath: join(dir, \"policy.json\"),\n keyPath: join(dir, \"agent.key\"),\n attesterPath: join(dir, \"attester.key\"),\n dbPath: join(dir, \"receipts.db\"),\n // Strict: deny (not just observe) tools with no taxonomy mapping.\n strict: process.argv.includes(\"--strict\") || process.env.SCOPEBOND_HOOK_STRICT === \"1\",\n // When connected, auto-export receipts. A short bounded flush keeps the hot path\n // fast; undelivered receipts persist in the durable outbox and flush next time\n // (or via `scopebond-hook flush`, e.g. on a session-end hook).\n ...(connection ? { cloud: { connection, flushTimeoutMs: Number(process.env.SCOPEBOND_HOOK_FLUSH_MS ?? 800) } } : {}),\n };\n}\nfunction readStdin(): string {\n try { return readFileSync(0, \"utf8\"); } catch { return \"\"; }\n}\n\nfunction denyClaude(reason: string): never {\n process.stdout.write(JSON.stringify({\n hookSpecificOutput: { hookEventName: \"PreToolUse\", permissionDecision: \"deny\", permissionDecisionReason: reason },\n }) + \"\\n\");\n process.stderr.write(`Scopebond: ${reason}\\n`);\n process.exit(2);\n}\n\nasync function runClaude(): Promise<void> {\n let input: Record<string, unknown>;\n try { input = JSON.parse(readStdin()); } catch { denyClaude(\"hook received invalid JSON on stdin\"); }\n try {\n const cwd = input?.cwd ? String(input.cwd) : process.cwd();\n const runtime = createHookRuntime(runtimePaths(resolveConfigDir(cwd)));\n const decision = await runtime.evaluate(fillPushBranch(mapClaudeToolUse(input!), currentBranch(cwd)));\n await runtime.flush();\n if (decision.decision === \"deny\") denyClaude(decision.reason);\n // allow and not_evaluated both DEFER to Claude Code's own permission flow: the\n // hook records the receipt but never returns permissionDecision:\"allow\", which\n // would suppress the user's normal review. Scopebond blocks; it does not approve.\n process.exit(0);\n } catch (error) {\n denyClaude(`Scopebond hook failed closed: ${(error as Error).message}. Repair: run \\`${cliCommand(\"init\")}\\`.`);\n }\n}\n\nasync function runCursor(): Promise<void> {\n let input: Record<string, unknown> = {};\n try { input = JSON.parse(readStdin()); } catch { /* fall through to fail-closed deny below */ }\n const event = String(input?.hook_event_name ?? input?.event ?? process.argv[3] ?? \"\");\n let permission: \"allow\" | \"deny\" | \"ask\" = \"deny\";\n let message = \"Scopebond hook failed closed\";\n try {\n const cwd = input?.cwd ? String(input.cwd) : process.cwd();\n const runtime = createHookRuntime(runtimePaths(resolveConfigDir(cwd)));\n const decision = await runtime.evaluate(fillPushBranch(mapCursorEvent(event, input), currentBranch(cwd)));\n await runtime.flush();\n // Only an out-of-policy action is denied outright; an allowed or unevaluated\n // action defers to Cursor's own prompt (\"ask\"), never a silent auto-allow.\n permission = decision.decision === \"deny\" ? \"deny\" : \"ask\";\n message = decision.reason;\n } catch (error) {\n permission = \"deny\";\n message = `Scopebond hook failed closed: ${(error as Error).message}. Repair: run \\`${cliCommand(\"init\")}\\`.`;\n }\n process.stdout.write(JSON.stringify({ permission, agentMessage: message }) + \"\\n\");\n process.exit(0);\n}\n\nfunction runInit(args: string[]): void {\n const harness = args.includes(\"--cursor\") ? \"cursor\" : \"claude\";\n const dir = configDir();\n const { agentKid, policyPath } = scaffold(dir, { force: args.includes(\"--force\") });\n console.log(`Scopebond hook enrolled in ${dir}`);\n console.log(` machine key ${agentKid}`);\n console.log(` policy ${policyPath} (starter — edit the limits)`);\n console.log(\"\");\n // Configure the agent automatically by default (idempotent), so there is no\n // hand-editing step; --no-install prints the snippet instead.\n if (!args.includes(\"--no-install\")) {\n const file = installHarness(harness);\n console.log(`✓ ${harness === \"cursor\" ? \"Cursor\" : \"Claude Code\"} configured in ${file}`);\n } else {\n console.log(`Add this to your ${harness === \"cursor\" ? \".cursor/hooks.json\" : \".claude/settings.json\"}:`);\n console.log(harnessSnippet(harness));\n }\n console.log(\"\");\n // Print the runnable `npx` form: after `npx @scopebond/hook init` there is no\n // `scopebond-hook` binary on PATH, so a bare `scopebond-hook log` would fail.\n console.log(`Next: run one command in the agent, then \\`${cliCommand(\"log\")}\\` to see the receipt`);\n console.log(`and \\`${cliCommand(\"verify\")}\\` to check it offline. Try \\`${cliCommand('test \"rm -rf /\"')}\\`.`);\n console.log(`To send receipts to a workspace: ${cliCommand(\"connect <workspace-url> <enrollment>\")}`);\n}\n\nfunction decisionOf(payload: Record<string, unknown>): string {\n const rr = String(payload.realtime_result ?? \"\");\n const state = String((payload.execution as Record<string, unknown> | undefined)?.state ?? \"\");\n if (state === \"observed_not_evaluated\") return \"not_evaluated\";\n if (rr === \"deny\") return state === \"cooperative_allow\" || state === \"executed\" ? \"monitor\" : \"deny\";\n return \"allow\";\n}\n\nfunction describeIntent(payload: Record<string, unknown>): string {\n const intent = (payload.intent ?? {}) as Record<string, unknown>;\n const p = (intent.params ?? {}) as Record<string, unknown>;\n const bits = intent.action_type === \"shell.exec\" ? String(p.program ?? \"\")\n : intent.action_type === \"git.push\" ? `${p.remote ?? \"\"} ${p.ref ?? \"\"}`.trim()\n : intent.action_type === \"file.write\" || intent.action_type === \"file.read\" ? String(p.path ?? \"\")\n : intent.action_type === \"mcp.tool.call\" ? `${p.server ?? \"\"}/${p.tool ?? \"\"}`\n : intent.action_type === \"net.fetch\" ? String(p.host ?? \"\") : \"\";\n return `${String(intent.action_type ?? \"?\")}${bits ? ` ${bits}` : \"\"}`;\n}\n\nasync function runLog(args: string[]): Promise<void> {\n const dir = resolveConfigDir(process.cwd());\n const dbPath = join(dir, \"receipts.db\");\n if (!existsSync(dbPath)) { console.log(\"no receipts yet — run a command in the agent first.\"); process.exit(0); }\n const nIdx = args.indexOf(\"-n\");\n const n = nIdx >= 0 ? Math.max(1, Number(args[nIdx + 1]) || 20) : 20;\n const { store } = openReceiptStore({ db: dbPath });\n const all = await Promise.resolve(store.list());\n const recent = all.slice(-n);\n if (recent.length === 0) { console.log(\"no receipts yet.\"); process.exit(0); }\n for (const r of recent) {\n const p = r.payload as unknown as Record<string, unknown>;\n console.log(`${String(p.timestamp ?? \"\")} ${decisionOf(p).padEnd(13)} ${describeIntent(p)}`);\n }\n console.log(`\\n${recent.length} of ${all.length} receipt(s). Verify them: ${cliCommand(\"verify\")}`);\n}\n\nasync function runVerify(): Promise<void> {\n const dir = resolveConfigDir(process.cwd());\n const dbPath = join(dir, \"receipts.db\");\n const attesterPath = join(dir, \"attester.key\");\n if (!existsSync(dbPath) || !existsSync(attesterPath)) { console.log(\"nothing to verify yet (no receipts or no attester key).\"); process.exit(0); }\n const { attester } = loadOrCreateAttester({ file: attesterPath });\n const { store } = openReceiptStore({ db: dbPath });\n const all = await Promise.resolve(store.list());\n let ok = 0;\n const bad: string[] = [];\n for (const r of all) {\n const result = verifyReceipt(r, attester.publicKeyPem) as unknown as Record<string, boolean>;\n if (result.valid) ok += 1;\n else {\n const failed = Object.entries(result).filter(([k, v]) => k.endsWith(\"_valid\") && v === false).map(([k]) => k);\n bad.push(`${String((r.payload as unknown as Record<string, unknown>).timestamp ?? \"\")}: ${failed.join(\", \") || \"invalid\"}`);\n }\n }\n console.log(`${ok}/${all.length} receipt(s) verify offline against ${attesterPath}.`);\n if (bad.length) { for (const b of bad) console.error(` ✗ ${b}`); process.exit(1); }\n process.exit(0);\n}\n\nasync function runTest(args: string[]): Promise<void> {\n const command = args.find((a) => !a.startsWith(\"-\"));\n if (!command) { console.error('usage: scopebond-hook test \"<shell command>\"'); process.exit(1); }\n const dir = resolveConfigDir(process.cwd());\n if (!existsSync(join(dir, \"policy.json\"))) { console.error(\"no policy yet — run `scopebond-hook init` first.\"); process.exit(1); }\n // Evaluate against the real policy and keys, but a throwaway store, so `test`\n // never records a receipt or exports anything.\n const tmp = mkdtempSync(join(tmpdir(), \"sb-hook-test-\"));\n try {\n const runtime = createHookRuntime({\n policyPath: join(dir, \"policy.json\"), keyPath: join(dir, \"agent.key\"),\n attesterPath: join(dir, \"attester.key\"), dbPath: join(tmp, \"receipts.db\"),\n strict: process.argv.includes(\"--strict\"),\n });\n const mapped = fillPushBranch(mapClaudeToolUse({ tool_name: \"Bash\", tool_input: { command } }), currentBranch(process.cwd()));\n console.log(`command: ${command}`);\n for (const m of mapped) {\n const d = await runtime.evaluateOne(m);\n console.log(` ${describeIntent({ intent: m.intent }).padEnd(28)} → ${d.decision}${d.reason ? ` (${d.reason})` : \"\"}`);\n }\n const overall = await runtime.evaluate(mapped);\n console.log(`\\noverall: ${overall.decision}${overall.reason ? ` · ${overall.reason}` : \"\"}`);\n process.exit(overall.decision === \"deny\" ? 2 : 0);\n } finally {\n rmSync(tmp, { recursive: true, force: true });\n }\n}\n\nasync function runConnect(args: string[]): Promise<void> {\n const positional = args.filter((a) => !a.startsWith(\"--\"));\n const url = positional[0];\n const bundleArg = positional[1];\n const harness = args.includes(\"--cursor\") ? \"cursor\" : \"claude\";\n if (!url) {\n console.error(\"usage: scopebond-hook connect <workspace-url> <enrollment> [--cursor] [--no-install]\");\n process.exit(1);\n }\n const dir = configDir();\n // One command sets everything up: scaffold the key, attester and starter policy if\n // they do not exist, then enroll and persist the scoped machine credential. The\n // enrollment can be a file, an inline base64 blob (what the portal hands out) or\n // raw JSON — the user never has to save or open a JSON file.\n scaffold(dir, {});\n let bundle: CloudEnrollmentBundle;\n try { bundle = readBundleArg(bundleArg, readStdin); }\n catch { console.error(\"could not read the enrollment (expected a file, inline blob, or JSON on stdin)\"); process.exit(1); }\n try {\n const c = await connectCloud(dir, url, bundle);\n console.log(`✓ Connected to ${c.url}`);\n // Configure the agent automatically (merges into the existing config), unless the\n // caller opts out. This removes the \"paste this snippet\" step.\n if (!args.includes(\"--no-install\")) {\n const file = installHarness(harness);\n console.log(`✓ ${harness === \"cursor\" ? \"Cursor\" : \"Claude Code\"} configured in ${file}`);\n } else {\n console.log(`Add this to your ${harness === \"cursor\" ? \".cursor/hooks.json\" : \".claude/settings.json\"}:`);\n console.log(harnessSnippet(harness));\n }\n console.log(\"\");\n console.log(\"Run your agent — the first action appears in your workspace within seconds.\");\n } catch (error) {\n console.error(`connect failed: ${(error as Error).message}`);\n process.exit(1);\n }\n}\n\nasync function runFlush(): Promise<void> {\n const dir = resolveConfigDir(process.cwd());\n if (!loadConnection(dir)) { console.error(\"not connected to a workspace; run `scopebond-hook connect` first\"); process.exit(1); }\n const runtime = createHookRuntime(runtimePaths(dir));\n await runtime.exporter?.flush();\n const status = runtime.exporter?.status();\n console.log(`flushed; ${status?.pending ?? 0} receipt(s) still pending${status?.lastError ? ` (last error: ${status.lastError})` : \"\"}`);\n process.exit(status && status.pending > 0 ? 1 : 0);\n}\n\n/** The absolute path to this CLI file, for registering the hook by absolute path. */\nfunction cliPath(): string {\n return fileURLToPath(import.meta.url);\n}\n\n/** `install` — the once-per-machine, user-level install (SB112). Scaffolds the\n * user home and registers the hook by absolute path in the user-level agent config,\n * so every project a developer opens is governed without a per-repo `init`. */\nfunction runInstall(args: string[]): void {\n const dir = userHome();\n const { agentKid, policyPath } = scaffold(dir, { force: args.includes(\"--force\") });\n console.log(`Scopebond installed for this user in ${dir}`);\n console.log(` machine key ${agentKid}`);\n console.log(` policy ${policyPath} (starter — edit the limits)`);\n console.log(\"\");\n const harnesses: Harness[] = args.includes(\"--cursor\")\n ? [\"cursor\"]\n : args.includes(\"--claude\") ? [\"claude\"] : (cursorDetected() ? [\"claude\", \"cursor\"] : [\"claude\"]);\n if (!args.includes(\"--no-install\")) {\n for (const h of harnesses) {\n const file = writeHarnessConfig(userHarnessFile(h), h, absoluteHookCommand(cliPath(), h));\n console.log(`✓ ${h === \"cursor\" ? \"Cursor\" : \"Claude Code\"} configured in ${file}`);\n }\n } else {\n console.log(\"Add this to your user-level agent config:\");\n console.log(harnessSnippet(harnesses[0]));\n }\n console.log(\"\");\n console.log(\"A project-local .scopebond still takes precedence when present.\");\n console.log(`Check it: ${cliCommand(\"doctor\")} · see decisions: ${cliCommand(\"log\")}`);\n console.log(`To send receipts to a workspace: ${cliCommand(\"connect <workspace-url> <enrollment>\")}`);\n}\n\nfunction runStatus(): void {\n const home = userHome();\n const installed = existsSync(join(home, \"policy.json\"));\n const claude = isHarnessConfigured(userHarnessFile(\"claude\"));\n const cursor = isHarnessConfigured(userHarnessFile(\"cursor\"));\n const connected = !!loadConnection(resolveConfigDir(process.cwd()));\n const dbPath = join(resolveConfigDir(process.cwd()), \"receipts.db\");\n console.log(`Scopebond hook ${hookVersion()}`);\n console.log(` user home ${home} ${installed ? \"(installed)\" : \"(not installed — run `scopebond install`)\"}`);\n console.log(` active config ${resolveConfigDir(process.cwd())}`);\n console.log(` Claude Code ${claude ? \"configured\" : \"not configured\"}`);\n console.log(` Cursor ${cursor ? \"configured\" : cursorDetected() ? \"detected, not configured\" : \"not detected\"}`);\n console.log(` cloud workspace ${connected ? \"connected\" : \"not connected (local only)\"}`);\n console.log(` local receipts ${existsSync(dbPath) ? dbPath : \"none yet\"}`);\n}\n\nasync function runDoctor(): Promise<void> {\n const problems: string[] = [];\n const [major, minor] = process.versions.node.split(\".\").map(Number);\n const nodeOk = major > 22 || (major === 22 && minor >= 13);\n console.log(`Scopebond doctor`);\n console.log(` node ${process.versions.node} ${nodeOk ? \"ok\" : \"TOO OLD (need >=22.13)\"}`);\n if (!nodeOk) problems.push(\"node >=22.13 is required (the Cloud outbox uses node:sqlite)\");\n const cli = cliPath();\n console.log(` cli ${cli} ${existsSync(cli) ? \"ok\" : \"MISSING\"}`);\n const active = resolveConfigDir(process.cwd());\n const hasPolicy = existsSync(join(active, \"policy.json\"));\n console.log(` active config ${active} ${hasPolicy ? \"ok\" : \"no policy (run `scopebond install` or `init`)\"}`);\n if (!hasPolicy) problems.push(\"no policy found in the active config dir\");\n const connection = loadConnection(active);\n if (!connection) {\n console.log(` cloud not connected (local only) — receipts stay on this machine`);\n } else {\n let reachable = \"unknown\";\n try {\n const res = await fetch(new URL(\"/healthz\", connection.url).toString(), { method: \"GET\" });\n reachable = res.ok ? \"reachable\" : `unhealthy (${res.status})`;\n } catch (error) { reachable = `unreachable (${(error as Error).message})`; }\n console.log(` cloud ${connection.url} — ${reachable}`);\n }\n console.log(problems.length ? `\\n${problems.length} problem(s): ${problems.join(\"; \")}` : `\\nAll good.`);\n process.exit(problems.length ? 1 : 0);\n}\n\nfunction runUninstall(args: string[]): void {\n let removed = 0;\n for (const h of [\"claude\", \"cursor\"] as Harness[]) {\n if (removeHarnessConfig(userHarnessFile(h))) { console.log(`✓ removed the Scopebond hook from ${userHarnessFile(h)}`); removed++; }\n }\n if (removed === 0) console.log(\"no user-level harness config found.\");\n if (args.includes(\"--purge\")) { purgeHome(); console.log(`✓ purged ${userHome()} (keys, policy, receipts)`); }\n else console.log(`Kept ${userHome()} (keys, policy, receipts). Use --purge to remove it too.`);\n}\n\nfunction runLogin(): void {\n console.log(\"Device-code login is not available yet.\");\n console.log(`For now, connect with a one-time enrollment from your workspace:`);\n console.log(` ${cliCommand(\"connect <workspace-url> <enrollment>\")}`);\n process.exit(0);\n}\n\nconst [cmd, ...rest] = process.argv.slice(2);\nif (cmd === \"claude\") { await runClaude(); }\nelse if (cmd === \"cursor\") { await runCursor(); }\nelse if (cmd === \"init\") { runInit(rest); }\nelse if (cmd === \"install\") { runInstall(rest); }\nelse if (cmd === \"connect\") { await runConnect(rest); }\nelse if (cmd === \"log\") { await runLog(rest); }\nelse if (cmd === \"verify\") { await runVerify(); }\nelse if (cmd === \"test\") { await runTest(rest); }\nelse if (cmd === \"flush\") { await runFlush(); }\nelse if (cmd === \"status\") { runStatus(); }\nelse if (cmd === \"doctor\") { await runDoctor(); }\nelse if (cmd === \"uninstall\") { runUninstall(rest); }\nelse if (cmd === \"login\") { runLogin(); }\nelse {\n console.error(\"usage: scopebond <claude|cursor|init|install|connect|log|verify|test|flush|status|doctor|uninstall|login> [--cursor] [--claude] [--force] [--strict] [--no-install] [--purge]\");\n process.exit(1);\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
export { mapClaudeToolUse, mapCursorEvent } from "./map.js";
|
|
1
|
+
export { mapClaudeToolUse, mapCursorEvent, fillPushBranch } from "./map.js";
|
|
2
2
|
export type { Mapped, NormalizedIntent } from "./map.js";
|
|
3
3
|
export { createHookRuntime, starterPolicy } from "./runtime.js";
|
|
4
4
|
export type { RuntimeConfig, Decision } from "./runtime.js";
|
|
5
5
|
export { scaffold, harnessSnippet, installHarness } from "./init.js";
|
|
6
|
+
export { userHome, userHarnessFile, resolveConfigDir, writeHarnessConfig, removeHarnessConfig, cursorDetected, absoluteHookCommand, isHarnessConfigured, purgeHome, } from "./install.js";
|
|
7
|
+
export type { Harness } from "./install.js";
|
|
6
8
|
export { connectCloud, loadConnection, attachExporter, flushBounded, connectionPath } from "./cloud.js";
|
|
7
9
|
export type { HookConnection } from "./cloud.js";
|
|
8
|
-
export { scrubSecrets, redactCommand, digest, sha256 } from "./minimize.js";
|
|
10
|
+
export { scrubSecrets, scrubParam, redactCommand, digest, sha256 } from "./minimize.js";
|
|
9
11
|
//# sourceMappingURL=index.d.ts.map
|
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,cAAc,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC5E,YAAY,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAChE,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EACL,QAAQ,EAAE,eAAe,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,mBAAmB,EACpF,cAAc,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,SAAS,GACpE,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,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
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// @scopebond/hook — the Claude Code + Cursor connector. The library surface is
|
|
2
2
|
// the deterministic mapper and the check-only runtime; the `scopebond-hook`
|
|
3
3
|
// binary is the thin harness adapter over them.
|
|
4
|
-
export { mapClaudeToolUse, mapCursorEvent } from "./map.js";
|
|
4
|
+
export { mapClaudeToolUse, mapCursorEvent, fillPushBranch } from "./map.js";
|
|
5
5
|
export { createHookRuntime, starterPolicy } from "./runtime.js";
|
|
6
6
|
export { scaffold, harnessSnippet, installHarness } from "./init.js";
|
|
7
|
+
export { userHome, userHarnessFile, resolveConfigDir, writeHarnessConfig, removeHarnessConfig, cursorDetected, absoluteHookCommand, isHarnessConfigured, purgeHome, } from "./install.js";
|
|
7
8
|
export { connectCloud, loadConnection, attachExporter, flushBounded, connectionPath } from "./cloud.js";
|
|
8
|
-
export { scrubSecrets, redactCommand, digest, sha256 } from "./minimize.js";
|
|
9
|
+
export { scrubSecrets, scrubParam, redactCommand, digest, sha256 } from "./minimize.js";
|
|
9
10
|
//# 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,+EAA+E;AAC/E,4EAA4E;AAC5E,gDAAgD;AAEhD,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,4EAA4E;AAC5E,gDAAgD;AAEhD,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE5E,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAEhE,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EACL,QAAQ,EAAE,eAAe,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,mBAAmB,EACpF,cAAc,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,SAAS,GACpE,MAAM,cAAc,CAAC;AAEtB,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 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, mapCursorEvent, fillPushBranch } from \"./map.js\";\nexport type { Mapped, NormalizedIntent } from \"./map.js\";\nexport { createHookRuntime, starterPolicy } from \"./runtime.js\";\nexport type { RuntimeConfig, Decision } from \"./runtime.js\";\nexport { scaffold, harnessSnippet, installHarness } from \"./init.js\";\nexport {\n userHome, userHarnessFile, resolveConfigDir, writeHarnessConfig, removeHarnessConfig,\n cursorDetected, absoluteHookCommand, isHarnessConfigured, purgeHome,\n} from \"./install.js\";\nexport type { Harness } from \"./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"]}
|