@hasna/hooks 0.10.6 → 0.10.8
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 +82 -1
- package/bin/hooks-mcp.js +100 -44
- package/bin/index.js +7835 -6947
- package/bin/native-safety-entry.js +196 -15
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1082 -68
- package/dist/lib/claude-safety-check.d.ts +32 -0
- package/dist/lib/codex-safety-check.d.ts +58 -0
- package/dist/lib/codex-safety-trust.d.ts +54 -0
- package/dist/lib/codex-settings.d.ts +5 -1
- package/dist/lib/installer.d.ts +8 -0
- package/dist/lib/native-safety-registration.d.ts +52 -0
- package/dist/lib/native-safety.d.ts +4 -0
- package/dist/native-safety.d.ts +6 -0
- package/dist/native-safety.js +18 -0
- package/hooks/hook-workspace-repos-guard/README.md +12 -0
- package/hooks/hook-workspace-repos-guard/src/hook.ts +247 -14
- package/package.json +2 -2
- package/scripts/ensure-profiles-dir.mjs +65 -1
|
@@ -5,7 +5,13 @@
|
|
|
5
5
|
// resolver data root once adopted (HASNA_DATA_HOME set or hooks.db already
|
|
6
6
|
// present there), then the legacy ~/.hasna/hooks default. Best-effort — the
|
|
7
7
|
// package must never fail to install because this script cannot run.
|
|
8
|
+
//
|
|
9
|
+
// It also provisions the required Sumi native-safety binding for the installing
|
|
10
|
+
// user (see the second block below). Same best-effort contract.
|
|
8
11
|
import { existsSync, mkdirSync } from "node:fs";
|
|
12
|
+
import { spawnSync } from "node:child_process";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
import { dirname } from "node:path";
|
|
9
15
|
// --- Local path resolver -------------------------------------------------
|
|
10
16
|
// @hasna/paths was deleted (hasna/apps#1535, 2026-09-03); this in-package
|
|
11
17
|
// implementation preserves the resolver contract (XDG / macOS home layout
|
|
@@ -58,7 +64,7 @@ function dataDir(options) {
|
|
|
58
64
|
return pathsResolverResolve("data", options);
|
|
59
65
|
}
|
|
60
66
|
import { homedir } from "node:os";
|
|
61
|
-
import { join, resolve } from "node:path";
|
|
67
|
+
import { join, resolve, sep } from "node:path";
|
|
62
68
|
|
|
63
69
|
const HOME = process.env.HOME || process.env.USERPROFILE || homedir();
|
|
64
70
|
|
|
@@ -97,3 +103,61 @@ try {
|
|
|
97
103
|
} catch {
|
|
98
104
|
// ignore
|
|
99
105
|
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* The bun runtime that must execute the provisioning entry: the package's own
|
|
109
|
+
* bin targets are bun bundles (`#!/usr/bin/env bun`) and `installedNativeSafetyCommand`
|
|
110
|
+
* pins `process.execPath`, so the runtime that runs this decides the binding.
|
|
111
|
+
* Well-known install locations first, then PATH; null when neither exists.
|
|
112
|
+
*/
|
|
113
|
+
function resolveBun() {
|
|
114
|
+
const wellKnown = [
|
|
115
|
+
process.env.BUN_INSTALL ? join(process.env.BUN_INSTALL, "bin", "bun") : null,
|
|
116
|
+
join(HOME, ".bun", "bin", "bun"),
|
|
117
|
+
];
|
|
118
|
+
for (const candidate of wellKnown) {
|
|
119
|
+
if (candidate && existsSync(candidate)) return candidate;
|
|
120
|
+
}
|
|
121
|
+
return spawnSync("bun", ["--version"], { encoding: "utf8" }).status === 0 ? "bun" : null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
// Required Sumi native-safety binding (hasna/apps incidents 798053).
|
|
126
|
+
//
|
|
127
|
+
// Sumi refuses EVERY shell command while
|
|
128
|
+
// `~/.hasna/hooks/native/sumi-trash-guard.json` is absent, and neither a package
|
|
129
|
+
// install nor an upgrade used to create it: a rollout could install this package
|
|
130
|
+
// and still leave every Sumi seat on the station without a shell.
|
|
131
|
+
//
|
|
132
|
+
// The packaged CLI owns the policy (`hooks safety provision`, also applied by
|
|
133
|
+
// `hooks install` and `hooks upgrade`): idempotent, a healthy existing binding is
|
|
134
|
+
// left intact, a stale one is replaced with its predecessor preserved beside it,
|
|
135
|
+
// and an unrecognized one is refused rather than overwritten. This lifecycle
|
|
136
|
+
// surface only reaches installs whose scripts actually run — bun blocks
|
|
137
|
+
// lifecycle scripts for untrusted dependencies and `--ignore-scripts` skips
|
|
138
|
+
// them, which is why the CLI surfaces apply the same policy.
|
|
139
|
+
//
|
|
140
|
+
// Two gates keep a source checkout from touching the operator's home: the
|
|
141
|
+
// package must be an INSTALLED copy (inside a node_modules directory) and its
|
|
142
|
+
// built CLI must exist. Best-effort — install never fails on a native guard.
|
|
143
|
+
try {
|
|
144
|
+
const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
145
|
+
const cli = join(packageRoot, "bin", "index.js");
|
|
146
|
+
if (packageRoot.includes(`${sep}node_modules${sep}`) && existsSync(cli)) {
|
|
147
|
+
const bun = resolveBun();
|
|
148
|
+
if (!bun) {
|
|
149
|
+
process.stderr.write("[@hasna/hooks] Sumi native-safety binding not provisioned: no bun runtime found; run `hooks safety provision`.\n");
|
|
150
|
+
} else {
|
|
151
|
+
const provisioned = spawnSync(bun, [cli, "safety", "provision", "--target", "sumi"], {
|
|
152
|
+
encoding: "utf8", timeout: 60_000, env: { ...process.env, HOME },
|
|
153
|
+
});
|
|
154
|
+
let result = null;
|
|
155
|
+
try { result = JSON.parse(provisioned.stdout?.trim() ?? ""); } catch { /* reported below */ }
|
|
156
|
+
if (!result?.ok) {
|
|
157
|
+
process.stderr.write(`[@hasna/hooks] Sumi native-safety binding not provisioned (${result?.code ?? "provision_failed"}); run \`hooks safety provision\` on this station before a Sumi seat needs its shell.\n`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
} catch {
|
|
162
|
+
// ignore
|
|
163
|
+
}
|