@hasna/hooks 0.10.5 → 0.10.6
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 +30 -0
- package/bin/index.js +331 -56
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -92,6 +92,36 @@ Repair unsafe installation permissions with a verified package installer, then
|
|
|
92
92
|
check again. This check alone does not prove hosted authentication, native hook
|
|
93
93
|
trust or a running agent's interception.
|
|
94
94
|
|
|
95
|
+
## Codex safety readiness
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
hooks safety verify --target codex --cwd /path/to/project --codex-command /absolute/path/to/native/codex
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
This bounded, read-only check starts its own fresh Codex app-server and asks the
|
|
102
|
+
native consumer for the selected project's hook definitions and feature state.
|
|
103
|
+
It requires exactly one enabled, trusted, synchronous bundled Trash guard, checks
|
|
104
|
+
its installed supervisor by classifying data, and verifies that the native
|
|
105
|
+
definition stayed unchanged. It returns compact JSON and a nonzero exit on
|
|
106
|
+
missing, disabled, untrusted, changed or unsafe state. No deletion is executed.
|
|
107
|
+
|
|
108
|
+
The command accepts a native Linux ELF or macOS Mach-O executable for the host
|
|
109
|
+
architecture (arm64 or x64); script launchers are refused before execution. When
|
|
110
|
+
Codex is installed through npm, pass the platform package's native binary rather
|
|
111
|
+
than its JavaScript launcher. Executable ownership, permissions, ancestry and
|
|
112
|
+
bytes are checked. Supported protocol versions are 0.153.0, 0.154.0, 0.155.0,
|
|
113
|
+
0.155.1 and 0.156.1; other versions refuse pending compatibility validation.
|
|
114
|
+
The version probe has a five-second forced timeout and native discovery a
|
|
115
|
+
fifteen-second lifetime budget. Neither step restarts an existing agent.
|
|
116
|
+
|
|
117
|
+
Success reports `nativeDiscoveryVerified: true`, `guardVerified: true` and
|
|
118
|
+
`nativeAdoptionVerified: false`. It does not enroll trust, change configuration,
|
|
119
|
+
authenticate Trash, verify MCP or Instructions, or prove interception by an
|
|
120
|
+
already-running session. Complete those separate setup checks and a fresh native
|
|
121
|
+
interception proof before declaring station support. Use this small readiness
|
|
122
|
+
check after installation, meaningful upgrades and during fresh-agent startup;
|
|
123
|
+
never substitute repeated deletion smoke tests for a startup check.
|
|
124
|
+
|
|
95
125
|
## Optional Mementos prompt context
|
|
96
126
|
|
|
97
127
|
`hooks install mementos-context --target codex` registers a native prompt hook.
|
package/bin/index.js
CHANGED
|
@@ -37388,7 +37388,7 @@ var init_redact = __esm(() => {
|
|
|
37388
37388
|
});
|
|
37389
37389
|
|
|
37390
37390
|
// src/lib/profiles.ts
|
|
37391
|
-
import { existsSync as existsSync15, mkdirSync as mkdirSync11, readFileSync as
|
|
37391
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync11, readFileSync as readFileSync15, writeFileSync as writeFileSync10, readdirSync as readdirSync8, rmSync, cpSync as cpSync2 } from "fs";
|
|
37392
37392
|
import { join as join19 } from "path";
|
|
37393
37393
|
function getProfilesDir() {
|
|
37394
37394
|
const newDir = join19(getEffectiveDataRoot(), "profiles");
|
|
@@ -37434,7 +37434,7 @@ function getProfile(id) {
|
|
|
37434
37434
|
try {
|
|
37435
37435
|
if (!existsSync15(path))
|
|
37436
37436
|
return null;
|
|
37437
|
-
return JSON.parse(
|
|
37437
|
+
return JSON.parse(readFileSync15(path, "utf-8"));
|
|
37438
37438
|
} catch {
|
|
37439
37439
|
return null;
|
|
37440
37440
|
}
|
|
@@ -37448,7 +37448,7 @@ function listProfiles() {
|
|
|
37448
37448
|
const profiles = [];
|
|
37449
37449
|
for (const file2 of files) {
|
|
37450
37450
|
try {
|
|
37451
|
-
const content =
|
|
37451
|
+
const content = readFileSync15(join19(dir, file2), "utf-8");
|
|
37452
37452
|
profiles.push(JSON.parse(content));
|
|
37453
37453
|
} catch {}
|
|
37454
37454
|
}
|
|
@@ -37498,12 +37498,12 @@ import { spawnSync as spawnSync2 } from "child_process";
|
|
|
37498
37498
|
import { closeSync as closeSync22, fstatSync as fstatSync22, openSync as openSync22, readFileSync as readFileSync22 } from "fs";
|
|
37499
37499
|
import { O_NOFOLLOW as O_NOFOLLOW2, O_NONBLOCK as O_NONBLOCK2, O_RDONLY as O_RDONLY2 } from "constants";
|
|
37500
37500
|
import { hostname as osHostname } from "os";
|
|
37501
|
-
import { isAbsolute as
|
|
37501
|
+
import { isAbsolute as isAbsolute18, join as join22 } from "path";
|
|
37502
37502
|
import { createRequire as createRequire3 } from "module";
|
|
37503
37503
|
import { fileURLToPath as fileURLToPath22 } from "url";
|
|
37504
|
-
import { closeSync as
|
|
37504
|
+
import { closeSync as closeSync7, fstatSync as fstatSync6, lstatSync as lstatSync15, openSync as openSync7, readFileSync as readFileSync17, realpathSync as realpathSync10, statSync as statSync2 } from "fs";
|
|
37505
37505
|
import { O_NOFOLLOW, O_NONBLOCK, O_RDONLY } from "constants";
|
|
37506
|
-
import { basename as basename5, dirname as
|
|
37506
|
+
import { basename as basename5, dirname as dirname20, join as join20, relative, sep as sep3 } from "path";
|
|
37507
37507
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "url";
|
|
37508
37508
|
function envToken2(name) {
|
|
37509
37509
|
return name.toUpperCase().replace(/-/g, "_");
|
|
@@ -37577,16 +37577,16 @@ function o(e2, r2, t2) {
|
|
|
37577
37577
|
}
|
|
37578
37578
|
}
|
|
37579
37579
|
function entryPoint(directory2) {
|
|
37580
|
-
const root =
|
|
37581
|
-
const fd =
|
|
37580
|
+
const root = realpathSync10(directory2);
|
|
37581
|
+
const fd = openSync7(join20(root, "package.json"), O_RDONLY | O_NOFOLLOW | O_NONBLOCK);
|
|
37582
37582
|
let pkg;
|
|
37583
37583
|
try {
|
|
37584
|
-
const stat2 =
|
|
37584
|
+
const stat2 = fstatSync6(fd);
|
|
37585
37585
|
if (!stat2.isFile() || stat2.size > MAX_PACKAGE_BYTES3)
|
|
37586
37586
|
throw new Error("Invalid Secrets package metadata");
|
|
37587
|
-
pkg = JSON.parse(
|
|
37587
|
+
pkg = JSON.parse(readFileSync17(fd, "utf8"));
|
|
37588
37588
|
} finally {
|
|
37589
|
-
|
|
37589
|
+
closeSync7(fd);
|
|
37590
37590
|
}
|
|
37591
37591
|
if (!pkg || typeof pkg !== "object" || !("name" in pkg) || pkg.name !== PACKAGE) {
|
|
37592
37592
|
throw new Error("Invalid Secrets package identity");
|
|
@@ -37598,7 +37598,7 @@ function entryPoint(directory2) {
|
|
|
37598
37598
|
if (/[\\%?#\0]/.test(target) || parts.some((part) => !part || part === "." || part === ".." || part === "node_modules")) {
|
|
37599
37599
|
throw new Error("Invalid Secrets import export");
|
|
37600
37600
|
}
|
|
37601
|
-
const file2 =
|
|
37601
|
+
const file2 = realpathSync10(join20(root, ...parts));
|
|
37602
37602
|
const within = relative(root, file2);
|
|
37603
37603
|
if (!within || within === ".." || within.startsWith(`..${sep3}`) || !statSync2(file2).isFile()) {
|
|
37604
37604
|
throw new Error("Secrets import export escapes its package or is not a file");
|
|
@@ -37606,13 +37606,13 @@ function entryPoint(directory2) {
|
|
|
37606
37606
|
return pathToFileURL(file2).href;
|
|
37607
37607
|
}
|
|
37608
37608
|
function resolveInstalledSecrets(parentUrl) {
|
|
37609
|
-
let directory2 =
|
|
37609
|
+
let directory2 = dirname20(fileURLToPath3(parentUrl));
|
|
37610
37610
|
for (;; ) {
|
|
37611
37611
|
if (basename5(directory2) !== "node_modules") {
|
|
37612
37612
|
const candidate = join20(directory2, "node_modules", PACKAGE);
|
|
37613
37613
|
let absent = false;
|
|
37614
37614
|
try {
|
|
37615
|
-
|
|
37615
|
+
lstatSync15(candidate);
|
|
37616
37616
|
} catch (error61) {
|
|
37617
37617
|
if (error61.code !== "ENOENT")
|
|
37618
37618
|
throw error61;
|
|
@@ -37621,7 +37621,7 @@ function resolveInstalledSecrets(parentUrl) {
|
|
|
37621
37621
|
if (!absent)
|
|
37622
37622
|
return entryPoint(candidate);
|
|
37623
37623
|
}
|
|
37624
|
-
const parent =
|
|
37624
|
+
const parent = dirname20(directory2);
|
|
37625
37625
|
if (parent === directory2)
|
|
37626
37626
|
throw new Error("Secrets SDK is not installed for this consumer");
|
|
37627
37627
|
directory2 = parent;
|
|
@@ -37633,7 +37633,7 @@ function homeDir(env) {
|
|
|
37633
37633
|
}
|
|
37634
37634
|
function absoluteOverride(env, key) {
|
|
37635
37635
|
const value = env[key]?.trim();
|
|
37636
|
-
return value &&
|
|
37636
|
+
return value && isAbsolute18(value) ? value : null;
|
|
37637
37637
|
}
|
|
37638
37638
|
function hasnaHomeDir(env) {
|
|
37639
37639
|
const override = absoluteOverride(env, HASNA_HOME_ENV_KEY);
|
|
@@ -38740,8 +38740,8 @@ var init_db_writer = __esm(() => {
|
|
|
38740
38740
|
});
|
|
38741
38741
|
|
|
38742
38742
|
// src/lib/hook-env.ts
|
|
38743
|
-
import { dirname as
|
|
38744
|
-
import { existsSync as existsSync16, realpathSync as
|
|
38743
|
+
import { dirname as dirname21, isAbsolute as isAbsolute19 } from "path";
|
|
38744
|
+
import { existsSync as existsSync16, realpathSync as realpathSync11, statSync as statSync4 } from "fs";
|
|
38745
38745
|
import { tmpdir } from "os";
|
|
38746
38746
|
function isDeniedEnvName(name) {
|
|
38747
38747
|
const upper = name.toUpperCase();
|
|
@@ -38760,7 +38760,7 @@ function isBashFunctionEnvName(name) {
|
|
|
38760
38760
|
return name.startsWith("BASH_FUNC_");
|
|
38761
38761
|
}
|
|
38762
38762
|
function isUnsafePathEntry(entry, home) {
|
|
38763
|
-
if (entry === "" || !
|
|
38763
|
+
if (entry === "" || !isAbsolute19(entry))
|
|
38764
38764
|
return true;
|
|
38765
38765
|
if (home && (entry === home || entry.startsWith(home + "/")))
|
|
38766
38766
|
return true;
|
|
@@ -38770,7 +38770,7 @@ function isUnsafePathEntry(entry, home) {
|
|
|
38770
38770
|
return true;
|
|
38771
38771
|
const canonical4 = (path) => {
|
|
38772
38772
|
try {
|
|
38773
|
-
return
|
|
38773
|
+
return realpathSync11(path);
|
|
38774
38774
|
} catch {
|
|
38775
38775
|
return path;
|
|
38776
38776
|
}
|
|
@@ -38791,7 +38791,7 @@ function sanitizeHookPath(pathValue, home) {
|
|
|
38791
38791
|
const baseline = PATH_BASELINE.slice();
|
|
38792
38792
|
if (process.platform === "darwin" && existsSync16("/opt/homebrew/bin"))
|
|
38793
38793
|
baseline.push("/opt/homebrew/bin");
|
|
38794
|
-
const bunDir =
|
|
38794
|
+
const bunDir = dirname21(process.execPath);
|
|
38795
38795
|
const seen = new Set;
|
|
38796
38796
|
const out = [];
|
|
38797
38797
|
for (const dir of baseline) {
|
|
@@ -38916,7 +38916,7 @@ __export(exports_run, {
|
|
|
38916
38916
|
});
|
|
38917
38917
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
38918
38918
|
import { rmSync as rmSync2, writeFileSync as writeFileSync11 } from "fs";
|
|
38919
|
-
import { basename as basename9, dirname as
|
|
38919
|
+
import { basename as basename9, dirname as dirname22, isAbsolute as isAbsolute20, join as join24 } from "path";
|
|
38920
38920
|
function extensionOf(scriptPath) {
|
|
38921
38921
|
const base = basename9(scriptPath);
|
|
38922
38922
|
const dot = base.lastIndexOf(".");
|
|
@@ -38985,7 +38985,7 @@ async function readPipeWithDeadline(stream, deadlineMs) {
|
|
|
38985
38985
|
return chunks.join("");
|
|
38986
38986
|
}
|
|
38987
38987
|
async function executeVerifiedScript(options) {
|
|
38988
|
-
const scriptDir =
|
|
38988
|
+
const scriptDir = dirname22(options.scriptPath);
|
|
38989
38989
|
const interpreter = interpreterFor(options.name, options.scriptPath, options.content);
|
|
38990
38990
|
const tempPath = join24(scriptDir, `.hooks-verified-${randomBytes2(12).toString("hex")}.${interpreter.tempExt}`);
|
|
38991
38991
|
let proc = null;
|
|
@@ -38996,7 +38996,7 @@ async function executeVerifiedScript(options) {
|
|
|
38996
38996
|
}, options.timeout) : null;
|
|
38997
38997
|
try {
|
|
38998
38998
|
writeFileSync11(tempPath, options.content, { flag: "wx", mode: 384 });
|
|
38999
|
-
proc = Bun.spawn([...interpreter.command,
|
|
38999
|
+
proc = Bun.spawn([...interpreter.command, isAbsolute20(tempPath) ? tempPath : `./${tempPath}`, ...options.args ?? []], {
|
|
39000
39000
|
stdin: new Response(options.stdin),
|
|
39001
39001
|
stdout: "pipe",
|
|
39002
39002
|
stderr: "pipe",
|
|
@@ -39078,7 +39078,7 @@ __export(exports_custom_install, {
|
|
|
39078
39078
|
installCustomSource: () => installCustomSource,
|
|
39079
39079
|
ensureCustomDir: () => ensureCustomDir
|
|
39080
39080
|
});
|
|
39081
|
-
import { existsSync as existsSync17, mkdirSync as mkdirSync12, mkdtempSync, readFileSync as
|
|
39081
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync12, mkdtempSync, readFileSync as readFileSync19, rmSync as rmSync4 } from "fs";
|
|
39082
39082
|
import { join as join27 } from "path";
|
|
39083
39083
|
import { tmpdir as tmpdir2 } from "os";
|
|
39084
39084
|
function manifestAt(dir) {
|
|
@@ -39086,7 +39086,7 @@ function manifestAt(dir) {
|
|
|
39086
39086
|
if (!existsSync17(path))
|
|
39087
39087
|
return;
|
|
39088
39088
|
try {
|
|
39089
|
-
return parseManifest(
|
|
39089
|
+
return parseManifest(readFileSync19(path, "utf-8"));
|
|
39090
39090
|
} catch {
|
|
39091
39091
|
return;
|
|
39092
39092
|
}
|
|
@@ -39147,7 +39147,7 @@ async function installCustomSource(source) {
|
|
|
39147
39147
|
throw new Error(`No such local path: ${source}`);
|
|
39148
39148
|
}
|
|
39149
39149
|
if (source.endsWith("manifest.json")) {
|
|
39150
|
-
const manifest = parseManifest(
|
|
39150
|
+
const manifest = parseManifest(readFileSync19(source, "utf-8"));
|
|
39151
39151
|
return writeFromManifest(manifest, source.substring(0, source.lastIndexOf("/")), source, "local");
|
|
39152
39152
|
}
|
|
39153
39153
|
return copyManifestDir(source, source, "local");
|
|
@@ -39196,13 +39196,13 @@ __export(exports_sync, {
|
|
|
39196
39196
|
fetchPinnedHook: () => fetchPinnedHook,
|
|
39197
39197
|
commitSyncArtifacts: () => commitSyncArtifacts
|
|
39198
39198
|
});
|
|
39199
|
-
import { createHash as
|
|
39200
|
-
import { readFileSync as
|
|
39199
|
+
import { createHash as createHash17 } from "crypto";
|
|
39200
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
39201
39201
|
function sha256OfText(text3) {
|
|
39202
|
-
return
|
|
39202
|
+
return createHash17("sha256").update(text3).digest("hex");
|
|
39203
39203
|
}
|
|
39204
39204
|
function sha256OfPath(path) {
|
|
39205
|
-
return
|
|
39205
|
+
return createHash17("sha256").update(readFileSync20(path)).digest("hex");
|
|
39206
39206
|
}
|
|
39207
39207
|
function collectBundledCatalog() {
|
|
39208
39208
|
const out = [];
|
|
@@ -40357,13 +40357,13 @@ __export(exports_serve, {
|
|
|
40357
40357
|
SERVE_HOST: () => SERVE_HOST,
|
|
40358
40358
|
DEFAULT_SERVE_PORT: () => DEFAULT_SERVE_PORT
|
|
40359
40359
|
});
|
|
40360
|
-
import { readFileSync as
|
|
40361
|
-
import { dirname as
|
|
40360
|
+
import { readFileSync as readFileSync21 } from "fs";
|
|
40361
|
+
import { dirname as dirname23, join as join29 } from "path";
|
|
40362
40362
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
40363
40363
|
function packageVersion() {
|
|
40364
40364
|
try {
|
|
40365
|
-
const pkgPath = join29(
|
|
40366
|
-
return JSON.parse(
|
|
40365
|
+
const pkgPath = join29(dirname23(fileURLToPath4(import.meta.url)), "..", "package.json");
|
|
40366
|
+
return JSON.parse(readFileSync21(pkgPath, "utf-8")).version || "0.0.0";
|
|
40367
40367
|
} catch {
|
|
40368
40368
|
return "0.0.0";
|
|
40369
40369
|
}
|
|
@@ -40663,8 +40663,8 @@ import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
|
40663
40663
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
40664
40664
|
import { z as z3 } from "zod";
|
|
40665
40665
|
import { createServer } from "http";
|
|
40666
|
-
import { existsSync as existsSync18, readFileSync as
|
|
40667
|
-
import { join as join30, dirname as
|
|
40666
|
+
import { existsSync as existsSync18, readFileSync as readFileSync23 } from "fs";
|
|
40667
|
+
import { join as join30, dirname as dirname24 } from "path";
|
|
40668
40668
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
40669
40669
|
async function runVerifiedHook(name, stdin, timeoutMs) {
|
|
40670
40670
|
const { resolveHook: resolveHook2, resolveScriptPath: resolveScriptPath2 } = await Promise.resolve().then(() => (init_resolve(), exports_resolve));
|
|
@@ -40673,7 +40673,7 @@ async function runVerifiedHook(name, stdin, timeoutMs) {
|
|
|
40673
40673
|
if (!resolved || !hookScript || !existsSync18(hookScript)) {
|
|
40674
40674
|
throw new Error(`Hook '${name}' not found`);
|
|
40675
40675
|
}
|
|
40676
|
-
const content =
|
|
40676
|
+
const content = readFileSync23(hookScript);
|
|
40677
40677
|
const sha4 = sha256Of(content);
|
|
40678
40678
|
const check2 = checkScriptHash(name, sha4);
|
|
40679
40679
|
if (!check2.ok) {
|
|
@@ -40905,7 +40905,7 @@ function createHooksServer() {
|
|
|
40905
40905
|
}
|
|
40906
40906
|
if (meta3 && settingsExist) {
|
|
40907
40907
|
try {
|
|
40908
|
-
const settings = JSON.parse(
|
|
40908
|
+
const settings = JSON.parse(readFileSync23(settingsPath, "utf-8"));
|
|
40909
40909
|
if (!hookRegisteredInSettings(settings, name, meta3.event, meta3.matcher)) {
|
|
40910
40910
|
const eventName = meta3.event.split(":")[0];
|
|
40911
40911
|
issues.push({ hook: name, issue: `Not registered under correct event (${eventName})`, severity: "error" });
|
|
@@ -40945,7 +40945,7 @@ function createHooksServer() {
|
|
|
40945
40945
|
const readmePath = join30(hookPath, "README.md");
|
|
40946
40946
|
let readme = "";
|
|
40947
40947
|
if (existsSync18(readmePath)) {
|
|
40948
|
-
readme =
|
|
40948
|
+
readme = readFileSync23(readmePath, "utf-8");
|
|
40949
40949
|
}
|
|
40950
40950
|
if (verbose) {
|
|
40951
40951
|
return { content: [{ type: "text", text: JSON.stringify({ ...meta3, readme }) }] };
|
|
@@ -41275,7 +41275,7 @@ function createHooksServer() {
|
|
|
41275
41275
|
let settings = {};
|
|
41276
41276
|
try {
|
|
41277
41277
|
if (existsSync18(settingsPath))
|
|
41278
|
-
settings = JSON.parse(
|
|
41278
|
+
settings = JSON.parse(readFileSync23(settingsPath, "utf-8"));
|
|
41279
41279
|
} catch {}
|
|
41280
41280
|
if (!settings.hooks)
|
|
41281
41281
|
settings.hooks = {};
|
|
@@ -41298,7 +41298,7 @@ function createHooksServer() {
|
|
|
41298
41298
|
let settings = {};
|
|
41299
41299
|
try {
|
|
41300
41300
|
if (existsSync18(settingsPath))
|
|
41301
|
-
settings = JSON.parse(
|
|
41301
|
+
settings = JSON.parse(readFileSync23(settingsPath, "utf-8"));
|
|
41302
41302
|
} catch {}
|
|
41303
41303
|
if (settings.hooks?.__disabled) {
|
|
41304
41304
|
settings.hooks.__disabled = settings.hooks.__disabled.filter((n2) => n2 !== name);
|
|
@@ -41668,13 +41668,13 @@ var init_server = __esm(() => {
|
|
|
41668
41668
|
init_redact();
|
|
41669
41669
|
init_authority();
|
|
41670
41670
|
init_storage();
|
|
41671
|
-
__dirname3 =
|
|
41671
|
+
__dirname3 = dirname24(fileURLToPath5(import.meta.url));
|
|
41672
41672
|
pkg = { name: "@hasna/hooks", version: "0.0.0" };
|
|
41673
41673
|
try {
|
|
41674
41674
|
for (const rel of ["../../package.json", "../package.json", "../../../package.json"]) {
|
|
41675
41675
|
const p = join30(__dirname3, rel);
|
|
41676
41676
|
if (existsSync18(p)) {
|
|
41677
|
-
pkg = JSON.parse(
|
|
41677
|
+
pkg = JSON.parse(readFileSync23(p, "utf-8"));
|
|
41678
41678
|
break;
|
|
41679
41679
|
}
|
|
41680
41680
|
}
|
|
@@ -43416,8 +43416,8 @@ var {
|
|
|
43416
43416
|
|
|
43417
43417
|
// src/cli/index.tsx
|
|
43418
43418
|
import chalk2 from "chalk";
|
|
43419
|
-
import { existsSync as existsSync19, readFileSync as
|
|
43420
|
-
import { join as join31, dirname as
|
|
43419
|
+
import { existsSync as existsSync19, readFileSync as readFileSync25, rmSync as rmSync6 } from "fs";
|
|
43420
|
+
import { join as join31, dirname as dirname25 } from "path";
|
|
43421
43421
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
43422
43422
|
|
|
43423
43423
|
// src/cli/components/App.tsx
|
|
@@ -45023,6 +45023,268 @@ function inspectTrashBinary(env = process.env) {
|
|
|
45023
45023
|
return { ok: false, code: failure };
|
|
45024
45024
|
}
|
|
45025
45025
|
|
|
45026
|
+
// src/lib/codex-safety-check.ts
|
|
45027
|
+
init_native_safety();
|
|
45028
|
+
import { spawn as spawn2, execFile as execFile2 } from "child_process";
|
|
45029
|
+
import { createHash as createHash12 } from "crypto";
|
|
45030
|
+
import { constants as constants12, openSync as openSync6, closeSync as closeSync6, fstatSync as fstatSync5, lstatSync as lstatSync10, readFileSync as readFileSync14, realpathSync as realpathSync9 } from "fs";
|
|
45031
|
+
import { homedir as homedir8 } from "os";
|
|
45032
|
+
import { dirname as dirname19, isAbsolute as isAbsolute17 } from "path";
|
|
45033
|
+
import { promisify } from "util";
|
|
45034
|
+
var versions = new Set(["codex-cli 0.153.0", "codex-cli 0.154.0", "codex-cli 0.155.0", "codex-cli 0.155.1", "codex-cli 0.156.1"]);
|
|
45035
|
+
var matcher = "^(Bash|apply_patch|ApplyPatch|functions\\.apply_patch)$";
|
|
45036
|
+
|
|
45037
|
+
class CodexSafetyError extends Error {
|
|
45038
|
+
code;
|
|
45039
|
+
constructor(code2) {
|
|
45040
|
+
super(`Codex deletion safety check failed (${code2}).`);
|
|
45041
|
+
this.code = code2;
|
|
45042
|
+
this.name = "CodexSafetyError";
|
|
45043
|
+
}
|
|
45044
|
+
}
|
|
45045
|
+
function need5(condition, code2) {
|
|
45046
|
+
if (!condition)
|
|
45047
|
+
throw new CodexSafetyError(code2);
|
|
45048
|
+
}
|
|
45049
|
+
var digest3 = (bytes) => createHash12("sha256").update(bytes).digest("hex");
|
|
45050
|
+
function nativeHeader(bytes) {
|
|
45051
|
+
if (bytes.length < 64 || !["arm64", "x64"].includes(process.arch))
|
|
45052
|
+
return false;
|
|
45053
|
+
if (process.platform === "darwin") {
|
|
45054
|
+
const cpu = process.arch === "arm64" ? 16777228 : 16777223;
|
|
45055
|
+
return bytes.readUInt32LE(0) === 4277009103 && bytes.readUInt32LE(4) === cpu && bytes.readUInt32LE(12) === 2 && bytes.readUInt32LE(16) > 0 && bytes.readUInt32LE(16) <= 4096 && bytes.readUInt32LE(20) > 0 && bytes.readUInt32LE(20) <= bytes.length - 32;
|
|
45056
|
+
}
|
|
45057
|
+
if (process.platform === "linux") {
|
|
45058
|
+
const offset = bytes.readBigUInt64LE(32), count = bytes.readUInt16LE(56);
|
|
45059
|
+
return bytes.subarray(0, 7).equals(Buffer.from([127, 69, 76, 70, 2, 1, 1])) && [2, 3].includes(bytes.readUInt16LE(16)) && bytes.readUInt16LE(18) === (process.arch === "arm64" ? 183 : 62) && bytes.readUInt32LE(20) === 1 && bytes.readUInt16LE(52) === 64 && bytes.readUInt16LE(54) === 56 && count > 0 && count <= 1024 && offset >= 64n && offset + BigInt(count * 56) <= BigInt(bytes.length);
|
|
45060
|
+
}
|
|
45061
|
+
return false;
|
|
45062
|
+
}
|
|
45063
|
+
function executable(command) {
|
|
45064
|
+
const found = isAbsolute17(command) ? command : Bun.which(command);
|
|
45065
|
+
need5(found, "native_unavailable");
|
|
45066
|
+
try {
|
|
45067
|
+
const path = realpathSync9(found);
|
|
45068
|
+
for (let parent = dirname19(path);; parent = dirname19(parent)) {
|
|
45069
|
+
const stat2 = lstatSync10(parent);
|
|
45070
|
+
need5(stat2.isDirectory() && [0, process.getuid?.()].includes(stat2.uid) && !(stat2.mode & 18), "native_unsafe_executable");
|
|
45071
|
+
if (parent === dirname19(parent))
|
|
45072
|
+
break;
|
|
45073
|
+
}
|
|
45074
|
+
const fd = openSync6(path, constants12.O_RDONLY | constants12.O_NOFOLLOW | constants12.O_NONBLOCK);
|
|
45075
|
+
try {
|
|
45076
|
+
const stat2 = fstatSync5(fd);
|
|
45077
|
+
need5(stat2.isFile() && [0, process.getuid?.()].includes(stat2.uid) && !(stat2.mode & 18) && !!(stat2.mode & 64) && stat2.size <= 256000000, "native_unsafe_executable");
|
|
45078
|
+
const bytes = readFileSync14(fd);
|
|
45079
|
+
need5(bytes.length <= 256000000, "native_unavailable");
|
|
45080
|
+
need5(nativeHeader(bytes), "native_executable_required");
|
|
45081
|
+
return { path, sha256: digest3(bytes) };
|
|
45082
|
+
} finally {
|
|
45083
|
+
closeSync6(fd);
|
|
45084
|
+
}
|
|
45085
|
+
} catch (error61) {
|
|
45086
|
+
if (error61 instanceof CodexSafetyError)
|
|
45087
|
+
throw error61;
|
|
45088
|
+
throw new CodexSafetyError("native_unavailable");
|
|
45089
|
+
}
|
|
45090
|
+
}
|
|
45091
|
+
async function connectCodexSafety(options) {
|
|
45092
|
+
const command = options.command ?? "codex", before = executable(command);
|
|
45093
|
+
const env = { ...process.env, HOME: options.home, NO_COLOR: "1" };
|
|
45094
|
+
for (const key of ["NODE_OPTIONS", "NODE_PATH", "BUN_OPTIONS", "BASH_ENV", "ENV", "DYLD_INSERT_LIBRARIES", "LD_PRELOAD"])
|
|
45095
|
+
delete env[key];
|
|
45096
|
+
if (options.codexHome === undefined)
|
|
45097
|
+
delete env.CODEX_HOME;
|
|
45098
|
+
else
|
|
45099
|
+
env.CODEX_HOME = options.codexHome;
|
|
45100
|
+
let version2;
|
|
45101
|
+
try {
|
|
45102
|
+
version2 = (await promisify(execFile2)(before.path, ["--version"], { env, cwd: options.home, timeout: 5000, killSignal: "SIGKILL", maxBuffer: 4096 })).stdout.trim();
|
|
45103
|
+
} catch {
|
|
45104
|
+
throw new CodexSafetyError("native_unavailable");
|
|
45105
|
+
}
|
|
45106
|
+
need5(versions.has(version2), "native_unsupported");
|
|
45107
|
+
need5(JSON.stringify(executable(command)) === JSON.stringify(before), "guard_changed");
|
|
45108
|
+
const child = spawn2(before.path, ["app-server", "--strict-config", "--stdio"], { env, cwd: options.home, stdio: ["pipe", "pipe", "pipe"] });
|
|
45109
|
+
let stopped = false, buffer = Buffer.alloc(0), total = 0, nextId = 1;
|
|
45110
|
+
const pending = new Map;
|
|
45111
|
+
const fail2 = () => {
|
|
45112
|
+
stopped = true;
|
|
45113
|
+
for (const waiter of pending.values())
|
|
45114
|
+
waiter.reject(new CodexSafetyError("native_failed"));
|
|
45115
|
+
pending.clear();
|
|
45116
|
+
};
|
|
45117
|
+
const deadline = setTimeout(() => {
|
|
45118
|
+
fail2();
|
|
45119
|
+
child.kill("SIGKILL");
|
|
45120
|
+
}, 15000);
|
|
45121
|
+
const exited = new Promise((done) => {
|
|
45122
|
+
child.once("exit", () => {
|
|
45123
|
+
fail2();
|
|
45124
|
+
clearTimeout(deadline);
|
|
45125
|
+
done();
|
|
45126
|
+
});
|
|
45127
|
+
child.once("error", () => {
|
|
45128
|
+
fail2();
|
|
45129
|
+
clearTimeout(deadline);
|
|
45130
|
+
done();
|
|
45131
|
+
});
|
|
45132
|
+
});
|
|
45133
|
+
child.stdin.on("error", fail2);
|
|
45134
|
+
const account = (length) => {
|
|
45135
|
+
total += length;
|
|
45136
|
+
if (total > 8388608) {
|
|
45137
|
+
fail2();
|
|
45138
|
+
child.kill("SIGKILL");
|
|
45139
|
+
return false;
|
|
45140
|
+
}
|
|
45141
|
+
return true;
|
|
45142
|
+
};
|
|
45143
|
+
child.stderr.on("data", (bytes) => {
|
|
45144
|
+
account(bytes.length);
|
|
45145
|
+
});
|
|
45146
|
+
child.stdout.on("data", (bytes) => {
|
|
45147
|
+
if (stopped || !account(bytes.length))
|
|
45148
|
+
return;
|
|
45149
|
+
buffer = Buffer.concat([buffer, bytes]);
|
|
45150
|
+
for (;; ) {
|
|
45151
|
+
const end = buffer.indexOf(10);
|
|
45152
|
+
if (end < 0)
|
|
45153
|
+
return;
|
|
45154
|
+
const line = buffer.subarray(0, end);
|
|
45155
|
+
buffer = buffer.subarray(end + 1);
|
|
45156
|
+
try {
|
|
45157
|
+
const value = JSON.parse(line.toString("utf8"));
|
|
45158
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
45159
|
+
throw Error();
|
|
45160
|
+
if (!Object.hasOwn(value, "id"))
|
|
45161
|
+
continue;
|
|
45162
|
+
const waiter = pending.get(value.id);
|
|
45163
|
+
if (!waiter)
|
|
45164
|
+
throw Error();
|
|
45165
|
+
pending.delete(value.id);
|
|
45166
|
+
if (value.error || !Object.hasOwn(value, "result"))
|
|
45167
|
+
waiter.reject(new CodexSafetyError("native_failed"));
|
|
45168
|
+
else
|
|
45169
|
+
waiter.resolve(value.result);
|
|
45170
|
+
} catch {
|
|
45171
|
+
fail2();
|
|
45172
|
+
child.kill("SIGKILL");
|
|
45173
|
+
return;
|
|
45174
|
+
}
|
|
45175
|
+
}
|
|
45176
|
+
});
|
|
45177
|
+
const request = (method, params) => new Promise((accept, reject) => {
|
|
45178
|
+
if (stopped) {
|
|
45179
|
+
reject(new CodexSafetyError("native_failed"));
|
|
45180
|
+
return;
|
|
45181
|
+
}
|
|
45182
|
+
const id = nextId++;
|
|
45183
|
+
pending.set(id, { resolve: accept, reject });
|
|
45184
|
+
child.stdin.write(JSON.stringify({ id, method, params }) + `
|
|
45185
|
+
`);
|
|
45186
|
+
});
|
|
45187
|
+
const rpc = {
|
|
45188
|
+
version: version2,
|
|
45189
|
+
request,
|
|
45190
|
+
async close() {
|
|
45191
|
+
fail2();
|
|
45192
|
+
child.stdin.end();
|
|
45193
|
+
const force = setTimeout(() => child.kill("SIGKILL"), 1000);
|
|
45194
|
+
await exited;
|
|
45195
|
+
clearTimeout(force);
|
|
45196
|
+
clearTimeout(deadline);
|
|
45197
|
+
need5(JSON.stringify(executable(command)) === JSON.stringify(before), "guard_changed");
|
|
45198
|
+
}
|
|
45199
|
+
};
|
|
45200
|
+
try {
|
|
45201
|
+
await request("initialize", { clientInfo: { name: "hooks-safety-check", version: "1" }, capabilities: { experimentalApi: true } });
|
|
45202
|
+
child.stdin.write(`{"method":"initialized"}
|
|
45203
|
+
`);
|
|
45204
|
+
return rpc;
|
|
45205
|
+
} catch {
|
|
45206
|
+
await rpc.close();
|
|
45207
|
+
throw new CodexSafetyError("native_failed");
|
|
45208
|
+
}
|
|
45209
|
+
}
|
|
45210
|
+
function selectCodexSafety(discovery, configuration, home, cwd, featureEnabled) {
|
|
45211
|
+
const data = discovery?.data;
|
|
45212
|
+
need5(Array.isArray(data) && data.length === 1 && data[0]?.cwd === cwd && Array.isArray(data[0]?.hooks) && data[0].hooks.length <= 1e4 && Array.isArray(data[0].errors) && data[0].errors.length === 0 && Array.isArray(data[0].warnings) && data[0].warnings.length === 0, "native_discovery_failed");
|
|
45213
|
+
const config2 = configuration?.config;
|
|
45214
|
+
need5(config2 && typeof config2 === "object" && !Array.isArray(config2), "native_discovery_failed");
|
|
45215
|
+
need5((config2.features?.hooks ?? featureEnabled) === true, "hooks_disabled");
|
|
45216
|
+
for (const hook2 of data[0].hooks)
|
|
45217
|
+
if (typeof hook2?.command === "string") {
|
|
45218
|
+
if (/\bhasna-native-safety-v\d+\b/.test(hook2.command))
|
|
45219
|
+
need5(nativeSafetyCommandBinding(hook2.command), "guard_definition_invalid");
|
|
45220
|
+
need5(!/^(?:hooks run trash-guard|hook-trash-guard)(?:\s|$)/.test(hook2.command.trim()), "guard_definition_invalid");
|
|
45221
|
+
}
|
|
45222
|
+
const selected = data[0].hooks.filter((hook2) => typeof hook2?.command === "string" && nativeSafetyCommandBinding(hook2.command)?.name === "trash-guard");
|
|
45223
|
+
need5(selected.length !== 0, "guard_missing");
|
|
45224
|
+
need5(selected.length === 1, "guard_ambiguous");
|
|
45225
|
+
const hook = selected[0], binding = nativeSafetyCommandBinding(hook.command);
|
|
45226
|
+
need5(binding.home === home && hook.eventName === "preToolUse" && hook.handlerType === "command" && hook.matcher === matcher && hook.timeoutSec === 10 && hook.async === false && hook.statusMessage === null && hook.additionalContextLimit === null && typeof hook.key === "string" && hook.key.length <= 4096 && typeof hook.sourcePath === "string" && isAbsolute17(hook.sourcePath) && hook.pluginId === null && hook.isManaged === false && ["user", "project"].includes(hook.source) && typeof hook.currentHash === "string" && /^sha256:[a-f0-9]{64}$/.test(hook.currentHash), "guard_definition_invalid");
|
|
45227
|
+
need5(hook.enabled === true, "guard_disabled");
|
|
45228
|
+
need5(hook.trustStatus === "trusted", "guard_untrusted");
|
|
45229
|
+
return { key: hook.key, command: hook.command, currentHash: hook.currentHash, sourcePath: hook.sourcePath, enabled: true, trustStatus: "trusted" };
|
|
45230
|
+
}
|
|
45231
|
+
async function verifyCodexNativeSafety(options = {}, dependencies = {}) {
|
|
45232
|
+
const home = realpathSync9(options.home ?? homedir8()), cwd = realpathSync9(options.cwd ?? process.cwd());
|
|
45233
|
+
need5(isAbsolute17(home) && isAbsolute17(cwd), "native_unavailable");
|
|
45234
|
+
const rpc = await (dependencies.connect ?? connectCodexSafety)({ ...options, home });
|
|
45235
|
+
try {
|
|
45236
|
+
need5(versions.has(rpc.version), "native_unsupported");
|
|
45237
|
+
const inspect = async () => {
|
|
45238
|
+
const hooks = await rpc.request("hooks/list", { cwds: [cwd] });
|
|
45239
|
+
const configuration = await rpc.request("config/read", { cwd, includeLayers: false });
|
|
45240
|
+
let enabled;
|
|
45241
|
+
if (configuration?.config?.features?.hooks === undefined) {
|
|
45242
|
+
let cursor = null, count = 0;
|
|
45243
|
+
const seen = new Set;
|
|
45244
|
+
do {
|
|
45245
|
+
const page3 = await rpc.request("experimentalFeature/list", { cursor, limit: 100 });
|
|
45246
|
+
need5(Array.isArray(page3?.data) && page3.data.length <= 100 && (page3.nextCursor === null || typeof page3.nextCursor === "string"), "native_discovery_failed");
|
|
45247
|
+
count += page3.data.length;
|
|
45248
|
+
need5(count <= 1000, "native_discovery_failed");
|
|
45249
|
+
for (const feature of page3.data)
|
|
45250
|
+
if (feature?.name === "hooks") {
|
|
45251
|
+
need5(enabled === undefined && typeof feature.enabled === "boolean", "native_discovery_failed");
|
|
45252
|
+
enabled = feature.enabled;
|
|
45253
|
+
}
|
|
45254
|
+
cursor = page3.nextCursor;
|
|
45255
|
+
if (cursor !== null) {
|
|
45256
|
+
need5(cursor.length > 0 && cursor.length <= 4096 && !seen.has(cursor) && page3.data.length > 0, "native_discovery_failed");
|
|
45257
|
+
seen.add(cursor);
|
|
45258
|
+
}
|
|
45259
|
+
} while (cursor !== null);
|
|
45260
|
+
}
|
|
45261
|
+
return selectCodexSafety(hooks, configuration, home, cwd, enabled);
|
|
45262
|
+
};
|
|
45263
|
+
const before = await inspect();
|
|
45264
|
+
try {
|
|
45265
|
+
await (dependencies.verify ?? verifyNativeSafetyCommand)(before.command, home);
|
|
45266
|
+
} catch {
|
|
45267
|
+
throw new CodexSafetyError("guard_failed");
|
|
45268
|
+
}
|
|
45269
|
+
need5(JSON.stringify(await inspect()) === JSON.stringify(before), "guard_changed");
|
|
45270
|
+
return {
|
|
45271
|
+
ok: true,
|
|
45272
|
+
target: "codex",
|
|
45273
|
+
nativeVersion: rpc.version,
|
|
45274
|
+
cwd,
|
|
45275
|
+
key: before.key,
|
|
45276
|
+
currentHash: before.currentHash,
|
|
45277
|
+
enabled: true,
|
|
45278
|
+
trusted: true,
|
|
45279
|
+
guardVerified: true,
|
|
45280
|
+
nativeDiscoveryVerified: true,
|
|
45281
|
+
nativeAdoptionVerified: false
|
|
45282
|
+
};
|
|
45283
|
+
} finally {
|
|
45284
|
+
await rpc.close();
|
|
45285
|
+
}
|
|
45286
|
+
}
|
|
45287
|
+
|
|
45026
45288
|
// src/cli/index.tsx
|
|
45027
45289
|
init_registry();
|
|
45028
45290
|
init_installer();
|
|
@@ -45039,9 +45301,9 @@ init_local_opt_in();
|
|
|
45039
45301
|
init_transport2();
|
|
45040
45302
|
init_db();
|
|
45041
45303
|
import { jsxDEV as jsxDEV8 } from "react/jsx-dev-runtime";
|
|
45042
|
-
var __dirname4 =
|
|
45304
|
+
var __dirname4 = dirname25(fileURLToPath6(import.meta.url));
|
|
45043
45305
|
var pkgPath = existsSync19(join31(__dirname4, "..", "package.json")) ? join31(__dirname4, "..", "package.json") : join31(__dirname4, "..", "..", "package.json");
|
|
45044
|
-
var pkg2 = JSON.parse(
|
|
45306
|
+
var pkg2 = JSON.parse(readFileSync25(pkgPath, "utf-8"));
|
|
45045
45307
|
var program3 = new Command;
|
|
45046
45308
|
function resolveScope(options) {
|
|
45047
45309
|
if (options.project)
|
|
@@ -45121,7 +45383,7 @@ async function openLocalStoreOrExit(json3) {
|
|
|
45121
45383
|
function readToken(tokenFile) {
|
|
45122
45384
|
if (tokenFile) {
|
|
45123
45385
|
try {
|
|
45124
|
-
return
|
|
45386
|
+
return readFileSync25(tokenFile, "utf-8").trim();
|
|
45125
45387
|
} catch {
|
|
45126
45388
|
return;
|
|
45127
45389
|
}
|
|
@@ -45135,9 +45397,9 @@ function readmePreview(readme, max = 280) {
|
|
|
45135
45397
|
return preview2 ? truncateText2(preview2, max) : undefined;
|
|
45136
45398
|
}
|
|
45137
45399
|
function hookSummaryLine(hook, options = {}) {
|
|
45138
|
-
const
|
|
45400
|
+
const matcher2 = hook.matcher ? ` ${hook.matcher}` : "";
|
|
45139
45401
|
const description = options.verbose ? ` - ${truncateText2(hook.description, 110)}` : "";
|
|
45140
|
-
return ` ${chalk2.cyan(hook.name.padEnd(17))} ${chalk2.dim(`[${hook.event}${
|
|
45402
|
+
return ` ${chalk2.cyan(hook.name.padEnd(17))} ${chalk2.dim(`[${hook.event}${matcher2}]`)} ${chalk2.dim(hook.category)}${description}`;
|
|
45141
45403
|
}
|
|
45142
45404
|
function listStoreHooks() {
|
|
45143
45405
|
const out = [];
|
|
@@ -45287,7 +45549,7 @@ program3.command("run").argument("<hook>", "Hook to run").option("--profile <id>
|
|
|
45287
45549
|
console.error(JSON.stringify({ error: `Hook script not found: ${hookScript ?? hook}` }));
|
|
45288
45550
|
process.exit(1);
|
|
45289
45551
|
}
|
|
45290
|
-
const content =
|
|
45552
|
+
const content = readFileSync25(hookScript);
|
|
45291
45553
|
const sha4 = sha256Of2(content);
|
|
45292
45554
|
const check2 = checkScriptHash2(hook, sha4);
|
|
45293
45555
|
if (!check2.ok) {
|
|
@@ -45889,7 +46151,7 @@ program3.command("doctor").option("-g, --global", "Check global settings", false
|
|
|
45889
46151
|
let wiringCount = 0;
|
|
45890
46152
|
if (settingsExist) {
|
|
45891
46153
|
try {
|
|
45892
|
-
const settings = JSON.parse(
|
|
46154
|
+
const settings = JSON.parse(readFileSync25(settingsPath, "utf-8"));
|
|
45893
46155
|
wiringCount = countSettingsWiring(settings);
|
|
45894
46156
|
} catch {}
|
|
45895
46157
|
}
|
|
@@ -45919,7 +46181,7 @@ program3.command("doctor").option("-g, --global", "Check global settings", false
|
|
|
45919
46181
|
}
|
|
45920
46182
|
if (meta3 && settingsExist) {
|
|
45921
46183
|
try {
|
|
45922
|
-
const settings = JSON.parse(
|
|
46184
|
+
const settings = JSON.parse(readFileSync25(settingsPath, "utf-8"));
|
|
45923
46185
|
if (!hookRegisteredInSettings(settings, name, meta3.event, meta3.matcher)) {
|
|
45924
46186
|
const eventName = meta3.event.split(":")[0];
|
|
45925
46187
|
issues.push({ hook: name, issue: `Not registered under correct event (${eventName})`, severity: "error" });
|
|
@@ -46125,7 +46387,7 @@ program3.command("docs").argument("[hook]", "Hook name (shows general docs if om
|
|
|
46125
46387
|
const readmePath = join31(hookPath, "README.md");
|
|
46126
46388
|
let readme = "";
|
|
46127
46389
|
if (existsSync19(readmePath)) {
|
|
46128
|
-
readme =
|
|
46390
|
+
readme = readFileSync25(readmePath, "utf-8");
|
|
46129
46391
|
}
|
|
46130
46392
|
if (options.json) {
|
|
46131
46393
|
console.log(JSON.stringify({ ...meta3, readme }));
|
|
@@ -46795,8 +47057,21 @@ safety.command("install <guard>").option("--target <target>", "codex, claude or
|
|
|
46795
47057
|
if (!result.success)
|
|
46796
47058
|
process.exitCode = 1;
|
|
46797
47059
|
});
|
|
46798
|
-
safety.command("verify").requiredOption("--target <target>", "sumi").option("--execution", "Also require the pinned native execution capability; no file operation is run").description("Verify a required native binding and its supervisor without running a file operation").action(async (options) => {
|
|
47060
|
+
safety.command("verify").requiredOption("--target <target>", "sumi or codex").option("--cwd <path>", "Codex project context to inspect (defaults to the current directory)").option("--codex-command <path>", "Installed Codex executable").option("--execution", "Also require the pinned native execution capability; no file operation is run").description("Verify a required native binding and its supervisor without running a file operation").action(async (options) => {
|
|
47061
|
+
if (options.target === "codex") {
|
|
47062
|
+
try {
|
|
47063
|
+
if (options.execution)
|
|
47064
|
+
throw new CodexSafetyError("native_unsupported");
|
|
47065
|
+
console.log(JSON.stringify(await verifyCodexNativeSafety({ command: options.codexCommand, cwd: options.cwd, codexHome: process.env.CODEX_HOME })));
|
|
47066
|
+
} catch (error61) {
|
|
47067
|
+
console.log(JSON.stringify({ ok: false, target: "codex", code: error61 instanceof CodexSafetyError ? error61.code : "native_failed" }));
|
|
47068
|
+
process.exitCode = 1;
|
|
47069
|
+
}
|
|
47070
|
+
return;
|
|
47071
|
+
}
|
|
46799
47072
|
try {
|
|
47073
|
+
if (options.cwd || options.codexCommand)
|
|
47074
|
+
throw new NativeSafetyError("input_invalid");
|
|
46800
47075
|
if (options.target !== "sumi")
|
|
46801
47076
|
throw new NativeSafetyError("input_invalid");
|
|
46802
47077
|
const current2 = await (options.execution ? verifyNativeSafetyExecutionRegistration() : verifyNativeSafetyRegistration());
|
package/package.json
CHANGED