@hasna/hooks 0.11.2 → 0.11.4

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.
@@ -143,6 +143,8 @@ function runReadinessProcess(command, args, options) {
143
143
  var import_node_fs = require("node:fs");
144
144
  var import_node_os = require("node:os");
145
145
  var import_node_path = require("node:path");
146
+ var TRASH_READINESS_MAX_TIMEOUT_MS = 60000;
147
+ var TRASH_READINESS_NATIVE_TIMEOUT_SECONDS = TRASH_READINESS_MAX_TIMEOUT_MS / 1000 + 5;
146
148
 
147
149
  class ReadinessError extends Error {
148
150
  code;
@@ -171,7 +173,7 @@ function inside(home, path) {
171
173
  function parseTrashReadinessBinding(input, provider, home) {
172
174
  const b = input;
173
175
  need(isReadinessProvider(provider) && absolute(home), "binding_invalid");
174
- need(keys(b, ["schema", "provider", "home", "runtime", "trash", "plan", "timeoutMs"]) && b.schema === "hasna.hooks.trash-readiness/v1" && b.provider === provider && b.home === home && pin(b.runtime) && pin(b.plan) && keys(b.trash, ["root", "version", "cliSha256"]) && absolute(b.trash.root) && typeof b.trash.version === "string" && b.trash.version.length <= 128 && /^\d+\.\d+\.\d+$/.test(b.trash.version) && digest(b.trash.cliSha256) && Number.isInteger(b.timeoutMs) && b.timeoutMs >= 1000 && b.timeoutMs <= 15000 && inside(home, b.plan.path) && inside(home, b.trash.root), "binding_invalid");
176
+ need(keys(b, ["schema", "provider", "home", "runtime", "trash", "plan", "timeoutMs"]) && b.schema === "hasna.hooks.trash-readiness/v1" && b.provider === provider && b.home === home && pin(b.runtime) && pin(b.plan) && keys(b.trash, ["root", "version", "cliSha256"]) && absolute(b.trash.root) && typeof b.trash.version === "string" && b.trash.version.length <= 128 && /^\d+\.\d+\.\d+$/.test(b.trash.version) && digest(b.trash.cliSha256) && Number.isInteger(b.timeoutMs) && b.timeoutMs >= 1000 && b.timeoutMs <= TRASH_READINESS_MAX_TIMEOUT_MS && inside(home, b.plan.path) && inside(home, b.trash.root), "binding_invalid");
175
177
  return structuredClone(b);
176
178
  }
177
179
  function trashReadinessBindingPath(provider, home = import_node_os.homedir()) {
package/dist/index.js CHANGED
@@ -7547,6 +7547,66 @@ import { join as join10, dirname as dirname7, basename as basename3, resolve as
7547
7547
  import { homedir as homedir6 } from "os";
7548
7548
  import { fileURLToPath as fileURLToPath2 } from "url";
7549
7549
 
7550
+ // src/lib/readiness-process.ts
7551
+ var supervisor = String.raw`
7552
+ const {spawn}=require("node:child_process");
7553
+ const spec=JSON.parse(process.argv.at(-1));
7554
+ // This runtime was created detached and is its group's live leader. Its
7555
+ // independent timer and parent pipe remain effective if the observer exits
7556
+ // or its event loop stops responding. Only this freshly created tree is hit.
7557
+ const cleanup=()=>{try{process.kill(-process.pid,"SIGKILL")}catch{process.exit(1)}};
7558
+ setTimeout(cleanup,spec.timeout);
7559
+ process.stdin.once("end",cleanup);
7560
+ process.stdin.once("error",cleanup);
7561
+ process.stdin.on("data",cleanup);
7562
+ process.stdin.resume();
7563
+ for(const signal of ["SIGTERM","SIGINT","SIGHUP"])process.once(signal,cleanup);
7564
+ let done=false, out=Buffer.alloc(0), err=Buffer.alloc(0);
7565
+ function report(status,error){
7566
+ if(done)return; done=true;
7567
+ process.stdout.write(JSON.stringify({status,error,stdout:out.toString("utf8"),stderr:err.toString("utf8")})+"\n");
7568
+ }
7569
+ const child=spawn(spec.command,spec.args,{stdio:["ignore","pipe","pipe"],env:process.env});
7570
+ for(const [stream,name] of [[child.stdout,"stdout"],[child.stderr,"stderr"]]){
7571
+ stream.on("data",bytes=>{
7572
+ if(done)return;
7573
+ const before=name==="stdout"?out:err;
7574
+ if(before.length+bytes.length>spec.maxBuffer){report(null,{code:"ENOBUFS"});return}
7575
+ if(name==="stdout")out=Buffer.concat([out,bytes]);else err=Buffer.concat([err,bytes]);
7576
+ });
7577
+ stream.on("error",()=>report(null,{code:"ECHILD"}));
7578
+ }
7579
+ child.on("error",()=>report(null,{code:"ECHILD"}));
7580
+ // A successful observation includes complete streams. A nested client that
7581
+ // keeps them open after its parent exits is a failure requiring group cleanup.
7582
+ child.on("exit",()=>setTimeout(()=>report(null,{code:"ECHILD"}),100));
7583
+ child.on("close",(status,signal)=>report(status,signal?{code:"ECHILD"}:undefined));
7584
+ `;
7585
+
7586
+ // src/native-readiness.ts
7587
+ var TRASH_READINESS_MAX_TIMEOUT_MS = 60000;
7588
+ var TRASH_READINESS_NATIVE_TIMEOUT_SECONDS = TRASH_READINESS_MAX_TIMEOUT_MS / 1000 + 5;
7589
+ function isReadinessProvider(value) {
7590
+ return value === "claude" || value === "codex" || value === "sumi";
7591
+ }
7592
+ var safeFailureCodes = new Set([
7593
+ "unsupported_platform",
7594
+ "station_detection_failed",
7595
+ "station_mismatch",
7596
+ "hosted_unavailable",
7597
+ "authentication_refused",
7598
+ "station_setup_required",
7599
+ "invalid_status",
7600
+ "deadline_exceeded",
7601
+ "retention_mismatch",
7602
+ "managed_instructions_refused",
7603
+ "removal_instruction_missing",
7604
+ "instructions_changed",
7605
+ "native_config_changed",
7606
+ "guard_refused",
7607
+ "owner_refused"
7608
+ ]);
7609
+
7550
7610
  // src/lib/resolve.ts
7551
7611
  import { dirname as dirname2, join as join6 } from "path";
7552
7612
  import { existsSync as existsSync5 } from "fs";
@@ -7774,66 +7834,6 @@ import { createHash as createHash2 } from "crypto";
7774
7834
  import { constants as constants2, closeSync as closeSync2, existsSync as existsSync7, fstatSync as fstatSync2, lstatSync as lstatSync2, openSync as openSync2, readFileSync as readFileSync4, realpathSync as realpathSync3 } from "fs";
7775
7835
  import { dirname as dirname4, isAbsolute as isAbsolute3, join as join8 } from "path";
7776
7836
  import { homedir as homedir4 } from "os";
7777
-
7778
- // src/lib/readiness-process.ts
7779
- var supervisor = String.raw`
7780
- const {spawn}=require("node:child_process");
7781
- const spec=JSON.parse(process.argv.at(-1));
7782
- // This runtime was created detached and is its group's live leader. Its
7783
- // independent timer and parent pipe remain effective if the observer exits
7784
- // or its event loop stops responding. Only this freshly created tree is hit.
7785
- const cleanup=()=>{try{process.kill(-process.pid,"SIGKILL")}catch{process.exit(1)}};
7786
- setTimeout(cleanup,spec.timeout);
7787
- process.stdin.once("end",cleanup);
7788
- process.stdin.once("error",cleanup);
7789
- process.stdin.on("data",cleanup);
7790
- process.stdin.resume();
7791
- for(const signal of ["SIGTERM","SIGINT","SIGHUP"])process.once(signal,cleanup);
7792
- let done=false, out=Buffer.alloc(0), err=Buffer.alloc(0);
7793
- function report(status,error){
7794
- if(done)return; done=true;
7795
- process.stdout.write(JSON.stringify({status,error,stdout:out.toString("utf8"),stderr:err.toString("utf8")})+"\n");
7796
- }
7797
- const child=spawn(spec.command,spec.args,{stdio:["ignore","pipe","pipe"],env:process.env});
7798
- for(const [stream,name] of [[child.stdout,"stdout"],[child.stderr,"stderr"]]){
7799
- stream.on("data",bytes=>{
7800
- if(done)return;
7801
- const before=name==="stdout"?out:err;
7802
- if(before.length+bytes.length>spec.maxBuffer){report(null,{code:"ENOBUFS"});return}
7803
- if(name==="stdout")out=Buffer.concat([out,bytes]);else err=Buffer.concat([err,bytes]);
7804
- });
7805
- stream.on("error",()=>report(null,{code:"ECHILD"}));
7806
- }
7807
- child.on("error",()=>report(null,{code:"ECHILD"}));
7808
- // A successful observation includes complete streams. A nested client that
7809
- // keeps them open after its parent exits is a failure requiring group cleanup.
7810
- child.on("exit",()=>setTimeout(()=>report(null,{code:"ECHILD"}),100));
7811
- child.on("close",(status,signal)=>report(status,signal?{code:"ECHILD"}:undefined));
7812
- `;
7813
-
7814
- // src/native-readiness.ts
7815
- function isReadinessProvider(value) {
7816
- return value === "claude" || value === "codex" || value === "sumi";
7817
- }
7818
- var safeFailureCodes = new Set([
7819
- "unsupported_platform",
7820
- "station_detection_failed",
7821
- "station_mismatch",
7822
- "hosted_unavailable",
7823
- "authentication_refused",
7824
- "station_setup_required",
7825
- "invalid_status",
7826
- "deadline_exceeded",
7827
- "retention_mismatch",
7828
- "managed_instructions_refused",
7829
- "removal_instruction_missing",
7830
- "instructions_changed",
7831
- "native_config_changed",
7832
- "guard_refused",
7833
- "owner_refused"
7834
- ]);
7835
-
7836
- // src/lib/native-readiness-command.ts
7837
7837
  var quote2 = (value) => "'" + value.replaceAll("'", "'\\''") + "'";
7838
7838
  var bootstrap = String.raw`/* hasna-native-readiness-v1 */ (() => {
7839
7839
  const fs = require("node:fs"), path = require("node:path"), crypto = require("node:crypto");
@@ -39769,7 +39769,7 @@ function registerHook(name, scope = "global", target = "claude", profile, mement
39769
39769
  name,
39770
39770
  event: "SessionStart",
39771
39771
  matcher: "^(startup|resume|clear|fork)$",
39772
- timeoutSeconds: 20,
39772
+ timeoutSeconds: TRASH_READINESS_NATIVE_TIMEOUT_SECONDS,
39773
39773
  displayName: "Trash Readiness",
39774
39774
  description: "Read-only native startup readiness",
39775
39775
  version: "0.1.0",
@@ -41351,7 +41351,7 @@ async function verifyNativeSafetyCommand(command, home = homedir7()) {
41351
41351
  }
41352
41352
 
41353
41353
  // src/lib/codex-safety-check.ts
41354
- 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", "codex-cli 0.157.0", "codex-cli 0.157.1"]);
41354
+ var versions = new Set(["codex-cli 0.153.0", "codex-cli 0.154.0", "codex-cli 0.154.0-alpha.6.1", "codex-cli 0.155.0", "codex-cli 0.155.1", "codex-cli 0.156.1", "codex-cli 0.157.0", "codex-cli 0.157.1"]);
41355
41355
  var matcher = "^(Bash|apply_patch|ApplyPatch|functions\\.apply_patch)$";
41356
41356
 
41357
41357
  class CodexSafetyError extends Error {
@@ -41563,7 +41563,7 @@ function selectCodexSafety(discovery, configuration, home, cwd, featureEnabled,
41563
41563
  need5(selected.length !== 0, "guard_missing");
41564
41564
  need5(selected.length === 1, "guard_ambiguous");
41565
41565
  const hook = selected[0], binding = readiness ? nativeReadinessCommandBinding(hook.command) : nativeSafetyCommandBinding(hook.command);
41566
- need5(binding.home === home && (!readiness || binding?.provider === "codex") && hook.eventName === (readiness ? "sessionStart" : "preToolUse") && hook.handlerType === "command" && hook.matcher === (readiness ? "^(startup|resume|clear|fork)$" : matcher) && hook.timeoutSec === (readiness ? 20 : 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");
41566
+ need5(binding.home === home && (!readiness || binding?.provider === "codex") && hook.eventName === (readiness ? "sessionStart" : "preToolUse") && hook.handlerType === "command" && hook.matcher === (readiness ? "^(startup|resume|clear|fork)$" : matcher) && hook.timeoutSec === (readiness ? TRASH_READINESS_NATIVE_TIMEOUT_SECONDS : 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");
41567
41567
  need5(typeof hook.enabled === "boolean" && ["trusted", "untrusted", "modified"].includes(hook.trustStatus), "guard_definition_invalid");
41568
41568
  if (requireReady) {
41569
41569
  need5(hook.enabled === true, "guard_disabled");
@@ -1,5 +1,8 @@
1
1
  import { type ReadinessChildResult, type ReadinessProcessOptions } from "./lib/readiness-process";
2
2
  export type ReadinessProvider = "claude" | "codex" | "sumi";
3
+ export declare const TRASH_READINESS_DEFAULT_TIMEOUT_MS = 45000;
4
+ export declare const TRASH_READINESS_MAX_TIMEOUT_MS = 60000;
5
+ export declare const TRASH_READINESS_NATIVE_TIMEOUT_SECONDS: number;
3
6
  type Pin = {
4
7
  path: string;
5
8
  sha256: string;
@@ -144,6 +144,9 @@ function runReadinessProcess(command, args, options) {
144
144
  import { closeSync, constants, fstatSync, lstatSync, openSync, readSync, realpathSync } from "fs";
145
145
  import { homedir } from "os";
146
146
  import { dirname, isAbsolute, join, relative, resolve } from "path";
147
+ var TRASH_READINESS_DEFAULT_TIMEOUT_MS = 45000;
148
+ var TRASH_READINESS_MAX_TIMEOUT_MS = 60000;
149
+ var TRASH_READINESS_NATIVE_TIMEOUT_SECONDS = TRASH_READINESS_MAX_TIMEOUT_MS / 1000 + 5;
147
150
 
148
151
  class ReadinessError extends Error {
149
152
  code;
@@ -172,7 +175,7 @@ function inside(home, path) {
172
175
  function parseTrashReadinessBinding(input, provider, home) {
173
176
  const b = input;
174
177
  need(isReadinessProvider(provider) && absolute(home), "binding_invalid");
175
- need(keys(b, ["schema", "provider", "home", "runtime", "trash", "plan", "timeoutMs"]) && b.schema === "hasna.hooks.trash-readiness/v1" && b.provider === provider && b.home === home && pin(b.runtime) && pin(b.plan) && keys(b.trash, ["root", "version", "cliSha256"]) && absolute(b.trash.root) && typeof b.trash.version === "string" && b.trash.version.length <= 128 && /^\d+\.\d+\.\d+$/.test(b.trash.version) && digest(b.trash.cliSha256) && Number.isInteger(b.timeoutMs) && b.timeoutMs >= 1000 && b.timeoutMs <= 15000 && inside(home, b.plan.path) && inside(home, b.trash.root), "binding_invalid");
178
+ need(keys(b, ["schema", "provider", "home", "runtime", "trash", "plan", "timeoutMs"]) && b.schema === "hasna.hooks.trash-readiness/v1" && b.provider === provider && b.home === home && pin(b.runtime) && pin(b.plan) && keys(b.trash, ["root", "version", "cliSha256"]) && absolute(b.trash.root) && typeof b.trash.version === "string" && b.trash.version.length <= 128 && /^\d+\.\d+\.\d+$/.test(b.trash.version) && digest(b.trash.cliSha256) && Number.isInteger(b.timeoutMs) && b.timeoutMs >= 1000 && b.timeoutMs <= TRASH_READINESS_MAX_TIMEOUT_MS && inside(home, b.plan.path) && inside(home, b.trash.root), "binding_invalid");
176
179
  return structuredClone(b);
177
180
  }
178
181
  function trashReadinessBindingPath(provider, home = homedir()) {
@@ -324,5 +327,8 @@ export {
324
327
  parseTrashReadinessBinding,
325
328
  isReadinessProvider,
326
329
  handleReadinessSessionStart,
327
- checkTrashReadinessBinding
330
+ checkTrashReadinessBinding,
331
+ TRASH_READINESS_NATIVE_TIMEOUT_SECONDS,
332
+ TRASH_READINESS_MAX_TIMEOUT_MS,
333
+ TRASH_READINESS_DEFAULT_TIMEOUT_MS
328
334
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/hooks",
3
- "version": "0.11.2",
3
+ "version": "0.11.4",
4
4
  "description": "Open source hooks library for AI coding agents - Install safety, quality, and automation hooks with a single command",
5
5
  "type": "module",
6
6
  "bin": {