@scopebond/hook 0.11.0 → 0.13.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 +16 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +59 -9
- package/dist/cli.js.map +1 -1
- package/dist/cloud.d.ts.map +1 -1
- package/dist/cloud.js +6 -1
- package/dist/cloud.js.map +1 -1
- package/dist/credential-refresh.d.ts +11 -0
- package/dist/credential-refresh.d.ts.map +1 -0
- package/dist/credential-refresh.js +55 -0
- package/dist/credential-refresh.js.map +1 -0
- package/dist/delivery-report.d.ts +21 -0
- package/dist/delivery-report.d.ts.map +1 -0
- package/dist/delivery-report.js +54 -0
- package/dist/delivery-report.js.map +1 -0
- package/dist/delivery-state.d.ts +30 -0
- package/dist/delivery-state.d.ts.map +1 -0
- package/dist/delivery-state.js +86 -0
- package/dist/delivery-state.js.map +1 -0
- package/dist/install.d.ts.map +1 -1
- package/dist/install.js +8 -2
- package/dist/install.js.map +1 -1
- package/dist/policy-sync.d.ts.map +1 -1
- package/dist/policy-sync.js +25 -1
- package/dist/policy-sync.js.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +10 -2
- package/dist/runtime.js.map +1 -1
- package/dist/version.d.ts +4 -4
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +5 -5
- package/dist/version.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -200,7 +200,22 @@ portal's **Connect** step (it gives you a one-use enrollment bundle), save it as
|
|
|
200
200
|
npx @scopebond/hook connect https://<your-workspace> scopebond-enrollment.json
|
|
201
201
|
```
|
|
202
202
|
|
|
203
|
-
In Windows PowerShell, use `npx.cmd` instead of `npx` if script execution policy blocks `npx.ps1`; no execution-policy change is needed.
|
|
203
|
+
In Windows PowerShell, use `npx.cmd` instead of `npx` if script execution policy blocks `npx.ps1`; no execution-policy change is needed. Every command the hook prints for you to run uses `npx.cmd` on Windows.
|
|
204
|
+
|
|
205
|
+
### Is it delivering?
|
|
206
|
+
|
|
207
|
+
`status` shows when this computer last delivered records, how many are waiting to send and the
|
|
208
|
+
last problem. Records are never dropped from the queue while they wait: a long outage only makes
|
|
209
|
+
the queue longer. If the workspace refuses this computer's connection (it was revoked, replaced or
|
|
210
|
+
removed), `status` says **NOT DELIVERING** with the time it stopped and the one command that fixes
|
|
211
|
+
it, which is signing in again. `doctor` also checks that the workspace still accepts the connection,
|
|
212
|
+
not only that it is reachable, and fails when it does not.
|
|
213
|
+
|
|
214
|
+
The connection renews itself: in the last 30 days of its 90-day credential the hook renews it during its rules check, proving it still holds the key it enrolled with. With each rules check it also tells the workspace how many records wait to send (counts and one error line, never a record), so the portal can show a computer that checks in but is not delivering.
|
|
215
|
+
|
|
216
|
+
Signing in puts the hook in your user-level agent settings (`~/.claude/settings.json`, `~/.cursor/hooks.json` or `~/.codex/hooks.json`), so every project on this computer is checked; `--project` connects one project instead. Signing in again never rewrites a settings file that already holds the right hook entry.
|
|
217
|
+
|
|
218
|
+
Enrollment registers both the gateway attester and the hook's separate agent signing key with proof of possession, so Cloud can verify authenticated receipts.
|
|
204
219
|
|
|
205
220
|
`connect` does the whole setup in one command: it scaffolds `.scopebond/` if needed,
|
|
206
221
|
enrolls this machine's countersigning key, stores a scoped machine credential in
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAmqBA,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM,GAAG,IAAI,CAS7F"}
|
package/dist/cli.js
CHANGED
|
@@ -52,6 +52,8 @@ import { runDispatchCommand } from "./dispatch-cli.js";
|
|
|
52
52
|
import { describeAction } from "./explain.js";
|
|
53
53
|
import { ensureDurableRuntime, pinnedCliPath, isEphemeralPath } from "./runtime-install.js";
|
|
54
54
|
import { cliCommand, hookCommand, hookVersion } from "./version.js";
|
|
55
|
+
import { recordRulesCredential } from "./delivery-state.js";
|
|
56
|
+
import { describeDelivery } from "./delivery-report.js";
|
|
55
57
|
import { computeManifest, renderManifest } from "./capabilities.js";
|
|
56
58
|
import { runProofFixtures, loadProofs, saveProofs, proofPassed, deliverProofReceipts } from "./proof.js";
|
|
57
59
|
import { fileURLToPath } from "node:url";
|
|
@@ -920,7 +922,14 @@ async function finishConnect(dir, url, bundle, harness, args) {
|
|
|
920
922
|
console.log(`✓ ${harnessName(harness)} already configured in ${existing}`);
|
|
921
923
|
}
|
|
922
924
|
else if (!args.includes("--no-install")) {
|
|
923
|
-
|
|
925
|
+
// A connection for this computer (the user home, the default) covers every project,
|
|
926
|
+
// so the hook goes into the user-level agent settings. Placing it in the current
|
|
927
|
+
// folder's project settings — what this did before — left every other project
|
|
928
|
+
// unchecked, and a sign-in run from a scratch folder governed only that folder.
|
|
929
|
+
// `--project` (a per-project connection) keeps the project placement.
|
|
930
|
+
const file = dir === userHome()
|
|
931
|
+
? writeHarnessConfig(userHarnessFile(harness), harness, durableHookCommand(harness))
|
|
932
|
+
: placeHook(harness, process.cwd(), undefined).file;
|
|
924
933
|
console.log(`✓ ${harnessName(harness)} configured in ${file}`);
|
|
925
934
|
if (harness === "codex")
|
|
926
935
|
console.log(`\nOne last step: ${codexTrustStep}`);
|
|
@@ -1279,16 +1288,17 @@ function cliPath() {
|
|
|
1279
1288
|
/** `install` — the once-per-machine, user-level install (SB112). Scaffolds the
|
|
1280
1289
|
* user home and registers the hook by absolute path in the user-level agent config,
|
|
1281
1290
|
* so every project a developer opens is governed without a per-repo `init`. */
|
|
1291
|
+
/** The command a user-level agent setting runs. Run through `npx`, this CLI lives in npm's
|
|
1292
|
+
* throwaway cache; registering that path would leave a hook that stops starting whenever
|
|
1293
|
+
* npm clears it — and a hook that cannot start lets every action through. Pin the durable
|
|
1294
|
+
* copy, as `init` does, and fall back to the portable `npx` command when none can be made. */
|
|
1295
|
+
function durableHookCommand(h) {
|
|
1296
|
+
const pin = ensureDurableRuntime(cliPath(), hookVersion());
|
|
1297
|
+
return pin.cli ? absoluteHookCommand(pin.cli, h) : hookCommand(h);
|
|
1298
|
+
}
|
|
1282
1299
|
function runInstall(args) {
|
|
1283
1300
|
const dir = userHome();
|
|
1284
|
-
|
|
1285
|
-
// would leave a hook that stops starting whenever npm clears it — and a hook that
|
|
1286
|
-
// cannot start lets every action through. Pin the durable copy, as `init` does, and
|
|
1287
|
-
// fall back to the portable `npx` command when none can be made.
|
|
1288
|
-
const commandFor = (h) => {
|
|
1289
|
-
const pin = ensureDurableRuntime(cliPath(), hookVersion());
|
|
1290
|
-
return pin.cli ? absoluteHookCommand(pin.cli, h) : hookCommand(h);
|
|
1291
|
-
};
|
|
1301
|
+
const commandFor = durableHookCommand;
|
|
1292
1302
|
const harnessesFor = () => args.includes("--codex") ? ["codex"]
|
|
1293
1303
|
: args.includes("--cursor") ? ["cursor"]
|
|
1294
1304
|
: args.includes("--claude") ? ["claude"]
|
|
@@ -1393,6 +1403,13 @@ function runStatus() {
|
|
|
1393
1403
|
console.log(` Cursor ${harnessScopeLabel(cursor) || (cursorDetected() ? "detected, not configured" : "not detected")}`);
|
|
1394
1404
|
console.log(` Codex ${codex.project || codex.user ? `${harnessScopeLabel(codex)} — approve once with /hooks` : codexDetected() ? "detected, not configured" : "not detected"}`);
|
|
1395
1405
|
console.log(` cloud workspace ${connected ? "connected" : "not connected (local only)"}`);
|
|
1406
|
+
{
|
|
1407
|
+
const activeDir = resolveConfigDir(process.cwd());
|
|
1408
|
+
const connection = loadConnection(activeDir);
|
|
1409
|
+
if (connection)
|
|
1410
|
+
for (const line of describeDelivery(activeDir, connection).lines)
|
|
1411
|
+
console.log(` ${line}`);
|
|
1412
|
+
}
|
|
1396
1413
|
{
|
|
1397
1414
|
const activeDir = resolveConfigDir(process.cwd());
|
|
1398
1415
|
const meta = readMeta(activeDir);
|
|
@@ -1557,6 +1574,33 @@ async function runDoctor() {
|
|
|
1557
1574
|
reachable = `unreachable (${error.message})`;
|
|
1558
1575
|
}
|
|
1559
1576
|
console.log(` cloud ${connection.url} — ${reachable}`);
|
|
1577
|
+
// SB273: an authenticated check. Reaching the workspace says nothing about whether it
|
|
1578
|
+
// still accepts this computer; the rules endpoint answers 401 when it does not.
|
|
1579
|
+
let accepted = "unknown";
|
|
1580
|
+
try {
|
|
1581
|
+
const res = await fetch(new URL("/v1/policy", connection.url).toString(), {
|
|
1582
|
+
headers: { authorization: `Bearer ${connection.credential}`, "x-scopebond-hook-version": hookVersion() },
|
|
1583
|
+
redirect: "error", signal: AbortSignal.timeout(10_000),
|
|
1584
|
+
});
|
|
1585
|
+
if (res.status === 401) {
|
|
1586
|
+
accepted = "REFUSED (401)";
|
|
1587
|
+
recordRulesCredential(active, false, Date.now());
|
|
1588
|
+
}
|
|
1589
|
+
else if (res.ok || res.status === 204 || res.status === 304) {
|
|
1590
|
+
accepted = "accepted";
|
|
1591
|
+
recordRulesCredential(active, true, Date.now());
|
|
1592
|
+
}
|
|
1593
|
+
else
|
|
1594
|
+
accepted = `unknown (HTTP ${res.status})`;
|
|
1595
|
+
}
|
|
1596
|
+
catch (error) {
|
|
1597
|
+
accepted = `not checked (${error.message})`;
|
|
1598
|
+
}
|
|
1599
|
+
console.log(` connection ${accepted}`);
|
|
1600
|
+
const delivery = describeDelivery(active, connection);
|
|
1601
|
+
for (const line of delivery.lines)
|
|
1602
|
+
console.log(` ${line}`);
|
|
1603
|
+
problems.push(...delivery.problems);
|
|
1560
1604
|
}
|
|
1561
1605
|
console.log(problems.length ? `\n${problems.length} problem(s): ${problems.join("; ")}` : `\nAll good.`);
|
|
1562
1606
|
process.exitCode = problems.length ? 1 : 0;
|
|
@@ -1630,6 +1674,12 @@ async function runLogin(args) {
|
|
|
1630
1674
|
});
|
|
1631
1675
|
return { status: response.status, json: await response.json().catch(() => ({})) };
|
|
1632
1676
|
};
|
|
1677
|
+
// SB276: run from inside an agent session (its own terminal or tool call), a sign-in can
|
|
1678
|
+
// land in the agent's working folder rather than this person's, and the person may never
|
|
1679
|
+
// see the code to approve. Say so plainly; the hook settings are left alone either way.
|
|
1680
|
+
if (process.env.CLAUDECODE || process.env.CODEX_SANDBOX || process.env.CURSOR_AGENT) {
|
|
1681
|
+
console.error("Note: this looks like a coding agent's own terminal. Signing in works best from your own terminal window, outside the agent.");
|
|
1682
|
+
}
|
|
1633
1683
|
let start;
|
|
1634
1684
|
try {
|
|
1635
1685
|
start = await post("/v1/device/code", { client_name: hostname(), harness });
|