@hasna/hooks 0.11.3 → 0.11.5
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 +6 -1
- package/bin/hooks-mcp.js +67 -65
- package/bin/index.js +404 -398
- package/bin/native-readiness-entry.js +3 -1
- package/bin/native-safety-entry.js +96 -8
- package/bin/serve.js +9 -9
- package/dist/index.js +74 -74
- package/dist/native-readiness.d.ts +3 -0
- package/dist/native-readiness.js +8 -2
- package/dist/sdk/index.js +1 -1
- package/hooks/hook-trash-guard/src/hook.ts +40 -10
- package/hooks/hook-workspace-repos-guard/src/hook.ts +46 -0
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -732,19 +732,350 @@ var init_registry = __esm(() => {
|
|
|
732
732
|
];
|
|
733
733
|
});
|
|
734
734
|
|
|
735
|
+
// src/lib/readiness-process.ts
|
|
736
|
+
import { spawn } from "child_process";
|
|
737
|
+
function runReadinessProcess(command, args, options) {
|
|
738
|
+
if (!["darwin", "linux"].includes(process.platform)) {
|
|
739
|
+
return Promise.resolve({ status: null, error: { code: "ENOTSUP" }, stdout: "", stderr: "" });
|
|
740
|
+
}
|
|
741
|
+
if (options.signal?.aborted) {
|
|
742
|
+
return Promise.resolve({ status: null, error: { code: "ABORT_ERR" }, stdout: "", stderr: "" });
|
|
743
|
+
}
|
|
744
|
+
return new Promise((resolve2) => {
|
|
745
|
+
const child = spawn(command, ["--config=/dev/null", "--no-env-file", "--no-macros", "--no-install", "--eval", supervisor, "--", JSON.stringify({ command, args, maxBuffer: options.maxBuffer, timeout: options.timeout })], {
|
|
746
|
+
cwd: options.cwd,
|
|
747
|
+
env: options.env,
|
|
748
|
+
detached: true,
|
|
749
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
750
|
+
});
|
|
751
|
+
let status = null, error;
|
|
752
|
+
let stopping = false, exited = false, finished = false;
|
|
753
|
+
let stdout = "", stderr = "", control = "";
|
|
754
|
+
const maximumEnvelope = options.maxBuffer * 12 + 1024;
|
|
755
|
+
let cleanup;
|
|
756
|
+
const finish = () => {
|
|
757
|
+
if (finished)
|
|
758
|
+
return;
|
|
759
|
+
finished = true;
|
|
760
|
+
clearTimeout(deadline);
|
|
761
|
+
clearTimeout(cleanup);
|
|
762
|
+
options.signal?.removeEventListener("abort", abort);
|
|
763
|
+
child.stdin?.destroy();
|
|
764
|
+
child.stdout?.destroy();
|
|
765
|
+
child.stderr?.destroy();
|
|
766
|
+
resolve2({ status, error, stdout, stderr });
|
|
767
|
+
};
|
|
768
|
+
const stop = (code) => {
|
|
769
|
+
if (code && !finished)
|
|
770
|
+
error ??= { code };
|
|
771
|
+
if (finished || stopping)
|
|
772
|
+
return;
|
|
773
|
+
stopping = true;
|
|
774
|
+
if (exited || !child.pid) {
|
|
775
|
+
error ??= { code: "ECHILD" };
|
|
776
|
+
} else {
|
|
777
|
+
try {
|
|
778
|
+
child.stdin.end();
|
|
779
|
+
} catch {
|
|
780
|
+
error = { code: "ECHILD" };
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
cleanup = setTimeout(() => {
|
|
784
|
+
error = { code: "ECHILD" };
|
|
785
|
+
finish();
|
|
786
|
+
}, 1000);
|
|
787
|
+
};
|
|
788
|
+
const abort = () => stop("ABORT_ERR");
|
|
789
|
+
const deadline = setTimeout(() => stop("ETIMEDOUT"), options.timeout);
|
|
790
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
791
|
+
if (options.signal?.aborted)
|
|
792
|
+
abort();
|
|
793
|
+
child.stdin?.on("error", () => {
|
|
794
|
+
if (!stopping)
|
|
795
|
+
stop("ECHILD");
|
|
796
|
+
});
|
|
797
|
+
child.stdout?.on("data", (bytes) => {
|
|
798
|
+
if (stopping)
|
|
799
|
+
return;
|
|
800
|
+
control += bytes.toString("utf8");
|
|
801
|
+
if (Buffer.byteLength(control) > maximumEnvelope)
|
|
802
|
+
return stop("ENOBUFS");
|
|
803
|
+
if (!control.endsWith(`
|
|
804
|
+
`))
|
|
805
|
+
return;
|
|
806
|
+
try {
|
|
807
|
+
const value = JSON.parse(control);
|
|
808
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((key) => !["status", "error", "stdout", "stderr"].includes(key)) || !(value.status === null || Number.isInteger(value.status) && value.status >= 0 && value.status <= 255) || typeof value.stdout !== "string" || typeof value.stderr !== "string" || Buffer.byteLength(value.stdout) > options.maxBuffer || Buffer.byteLength(value.stderr) > options.maxBuffer || value.error !== undefined && (Object.keys(value.error).join() !== "code" || !["ENOBUFS", "ECHILD"].includes(value.error.code))) {
|
|
809
|
+
return stop("EPROTO");
|
|
810
|
+
}
|
|
811
|
+
status = value.status;
|
|
812
|
+
stdout = value.stdout;
|
|
813
|
+
stderr = value.stderr;
|
|
814
|
+
stop(value.error?.code);
|
|
815
|
+
} catch {
|
|
816
|
+
stop("EPROTO");
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
child.stdout?.on("error", () => stop("EPROTO"));
|
|
820
|
+
child.stderr?.on("data", () => stop("ECHILD"));
|
|
821
|
+
child.stderr?.on("error", () => stop("ECHILD"));
|
|
822
|
+
child.on("error", () => {
|
|
823
|
+
error = { code: "ECHILD" };
|
|
824
|
+
stop();
|
|
825
|
+
});
|
|
826
|
+
child.on("exit", () => {
|
|
827
|
+
exited = true;
|
|
828
|
+
if (!stopping)
|
|
829
|
+
stop("ECHILD");
|
|
830
|
+
});
|
|
831
|
+
child.on("close", () => {
|
|
832
|
+
if (!stopping)
|
|
833
|
+
error = { code: "ECHILD" };
|
|
834
|
+
finish();
|
|
835
|
+
});
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
var supervisor;
|
|
839
|
+
var init_readiness_process = __esm(() => {
|
|
840
|
+
supervisor = String.raw`
|
|
841
|
+
const {spawn}=require("node:child_process");
|
|
842
|
+
const spec=JSON.parse(process.argv.at(-1));
|
|
843
|
+
// This runtime was created detached and is its group's live leader. Its
|
|
844
|
+
// independent timer and parent pipe remain effective if the observer exits
|
|
845
|
+
// or its event loop stops responding. Only this freshly created tree is hit.
|
|
846
|
+
const cleanup=()=>{try{process.kill(-process.pid,"SIGKILL")}catch{process.exit(1)}};
|
|
847
|
+
setTimeout(cleanup,spec.timeout);
|
|
848
|
+
process.stdin.once("end",cleanup);
|
|
849
|
+
process.stdin.once("error",cleanup);
|
|
850
|
+
process.stdin.on("data",cleanup);
|
|
851
|
+
process.stdin.resume();
|
|
852
|
+
for(const signal of ["SIGTERM","SIGINT","SIGHUP"])process.once(signal,cleanup);
|
|
853
|
+
let done=false, out=Buffer.alloc(0), err=Buffer.alloc(0);
|
|
854
|
+
function report(status,error){
|
|
855
|
+
if(done)return; done=true;
|
|
856
|
+
process.stdout.write(JSON.stringify({status,error,stdout:out.toString("utf8"),stderr:err.toString("utf8")})+"\n");
|
|
857
|
+
}
|
|
858
|
+
const child=spawn(spec.command,spec.args,{stdio:["ignore","pipe","pipe"],env:process.env});
|
|
859
|
+
for(const [stream,name] of [[child.stdout,"stdout"],[child.stderr,"stderr"]]){
|
|
860
|
+
stream.on("data",bytes=>{
|
|
861
|
+
if(done)return;
|
|
862
|
+
const before=name==="stdout"?out:err;
|
|
863
|
+
if(before.length+bytes.length>spec.maxBuffer){report(null,{code:"ENOBUFS"});return}
|
|
864
|
+
if(name==="stdout")out=Buffer.concat([out,bytes]);else err=Buffer.concat([err,bytes]);
|
|
865
|
+
});
|
|
866
|
+
stream.on("error",()=>report(null,{code:"ECHILD"}));
|
|
867
|
+
}
|
|
868
|
+
child.on("error",()=>report(null,{code:"ECHILD"}));
|
|
869
|
+
// A successful observation includes complete streams. A nested client that
|
|
870
|
+
// keeps them open after its parent exits is a failure requiring group cleanup.
|
|
871
|
+
child.on("exit",()=>setTimeout(()=>report(null,{code:"ECHILD"}),100));
|
|
872
|
+
child.on("close",(status,signal)=>report(status,signal?{code:"ECHILD"}:undefined));
|
|
873
|
+
`;
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
// src/native-readiness.ts
|
|
877
|
+
var exports_native_readiness = {};
|
|
878
|
+
__export(exports_native_readiness, {
|
|
879
|
+
trashReadinessBindingPath: () => trashReadinessBindingPath,
|
|
880
|
+
runTrashReadiness: () => runTrashReadiness,
|
|
881
|
+
readinessSessionStartOutput: () => readinessSessionStartOutput,
|
|
882
|
+
readinessContext: () => readinessContext,
|
|
883
|
+
readTrashReadinessBindingFile: () => readTrashReadinessBindingFile,
|
|
884
|
+
parseTrashReadinessBinding: () => parseTrashReadinessBinding,
|
|
885
|
+
isReadinessProvider: () => isReadinessProvider,
|
|
886
|
+
handleReadinessSessionStart: () => handleReadinessSessionStart,
|
|
887
|
+
checkTrashReadinessBinding: () => checkTrashReadinessBinding,
|
|
888
|
+
TRASH_READINESS_NATIVE_TIMEOUT_SECONDS: () => TRASH_READINESS_NATIVE_TIMEOUT_SECONDS,
|
|
889
|
+
TRASH_READINESS_MAX_TIMEOUT_MS: () => TRASH_READINESS_MAX_TIMEOUT_MS,
|
|
890
|
+
TRASH_READINESS_DEFAULT_TIMEOUT_MS: () => TRASH_READINESS_DEFAULT_TIMEOUT_MS
|
|
891
|
+
});
|
|
892
|
+
import { createHash } from "crypto";
|
|
893
|
+
import { closeSync, constants, fstatSync, lstatSync, openSync, readSync, realpathSync } from "fs";
|
|
894
|
+
import { homedir as homedir2 } from "os";
|
|
895
|
+
import { dirname, isAbsolute, join as join2, relative, resolve as resolve2 } from "path";
|
|
896
|
+
function need(value, code) {
|
|
897
|
+
if (!value)
|
|
898
|
+
throw new ReadinessError(code);
|
|
899
|
+
}
|
|
900
|
+
function isReadinessProvider(value) {
|
|
901
|
+
return value === "claude" || value === "codex" || value === "sumi";
|
|
902
|
+
}
|
|
903
|
+
function inside(home, path) {
|
|
904
|
+
const part = relative(home, path);
|
|
905
|
+
return part !== "" && part !== ".." && !part.startsWith("../") && !isAbsolute(part);
|
|
906
|
+
}
|
|
907
|
+
function parseTrashReadinessBinding(input, provider, home) {
|
|
908
|
+
const b = input;
|
|
909
|
+
need(isReadinessProvider(provider) && absolute(home), "binding_invalid");
|
|
910
|
+
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");
|
|
911
|
+
return structuredClone(b);
|
|
912
|
+
}
|
|
913
|
+
function trashReadinessBindingPath(provider, home = homedir2()) {
|
|
914
|
+
need(isReadinessProvider(provider) && absolute(home), "binding_invalid");
|
|
915
|
+
return join2(home, ".hasna", "hooks", "native", "trash-readiness", provider + ".json");
|
|
916
|
+
}
|
|
917
|
+
function readSafe(path, maximum, executable = false) {
|
|
918
|
+
need(absolute(path) && realpathSync(path) === path, "unsafe_path");
|
|
919
|
+
for (let parent = dirname(path);; parent = dirname(parent)) {
|
|
920
|
+
const stat = lstatSync(parent);
|
|
921
|
+
need(stat.isDirectory() && [0, process.getuid?.()].includes(stat.uid) && !(stat.mode & 18), "unsafe_path");
|
|
922
|
+
if (parent === dirname(parent))
|
|
923
|
+
break;
|
|
924
|
+
}
|
|
925
|
+
const fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
926
|
+
try {
|
|
927
|
+
const stat = fstatSync(fd);
|
|
928
|
+
need(stat.isFile() && [0, process.getuid?.()].includes(stat.uid) && !(stat.mode & 18) && stat.size <= maximum && (!executable || !!(stat.mode & 64)), "unsafe_path");
|
|
929
|
+
const bytes = Buffer.alloc(stat.size + 1);
|
|
930
|
+
let length = 0;
|
|
931
|
+
while (length < bytes.length) {
|
|
932
|
+
const count = readSync(fd, bytes, length, bytes.length - length, null);
|
|
933
|
+
if (!count)
|
|
934
|
+
break;
|
|
935
|
+
length += count;
|
|
936
|
+
}
|
|
937
|
+
const after = fstatSync(fd);
|
|
938
|
+
need(length === stat.size && after.size === stat.size && after.mtimeMs === stat.mtimeMs && after.ctimeMs === stat.ctimeMs, "file_changed");
|
|
939
|
+
return bytes.subarray(0, length);
|
|
940
|
+
} finally {
|
|
941
|
+
closeSync(fd);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
function readTrashReadinessBindingFile(path, provider, home = realpathSync(homedir2())) {
|
|
945
|
+
const raw = readSafe(path, 65536);
|
|
946
|
+
return { binding: parseTrashReadinessBinding(JSON.parse(raw.toString("utf8")), provider, home), raw, sha256: sha(raw) };
|
|
947
|
+
}
|
|
948
|
+
async function runTrashReadiness(provider, d = {}) {
|
|
949
|
+
const started = performance.now();
|
|
950
|
+
const result = (code, extra = {}) => ({
|
|
951
|
+
schema: "hasna.hooks.trash-readiness-result/v1",
|
|
952
|
+
provider,
|
|
953
|
+
ready: code === "ready",
|
|
954
|
+
code,
|
|
955
|
+
nativeAdoptionVerified: false,
|
|
956
|
+
...extra
|
|
957
|
+
});
|
|
958
|
+
let phase = "binding";
|
|
959
|
+
try {
|
|
960
|
+
const home = d.home ?? realpathSync(homedir2()), read = d.read ?? readSafe;
|
|
961
|
+
need(isReadinessProvider(provider), "binding_invalid");
|
|
962
|
+
const bindingPath = trashReadinessBindingPath(provider, home), bindingBytes = read(bindingPath, 65536);
|
|
963
|
+
const b = parseTrashReadinessBinding(JSON.parse(bindingBytes.toString("utf8")), provider, home);
|
|
964
|
+
phase = "consumer";
|
|
965
|
+
const checkPin = (p, maximum, executable = false) => {
|
|
966
|
+
const bytes = read(p.path, maximum, executable);
|
|
967
|
+
need(sha(bytes) === p.sha256, "file_changed");
|
|
968
|
+
return bytes;
|
|
969
|
+
};
|
|
970
|
+
checkPin(b.runtime, 200000000, true);
|
|
971
|
+
const manifestPath = join2(b.trash.root, "package.json"), manifestBytes = read(manifestPath, 65536);
|
|
972
|
+
const manifest = JSON.parse(manifestBytes.toString("utf8"));
|
|
973
|
+
need(manifest.name === "@hasna/trash" && manifest.version === b.trash.version && manifest.bin?.trash === "dist/cli/index.js", "package_mismatch");
|
|
974
|
+
const cli = join2(b.trash.root, "dist", "cli", "index.js");
|
|
975
|
+
checkPin({ path: cli, sha256: b.trash.cliSha256 }, 64 * 1024 * 1024, true);
|
|
976
|
+
const plan = JSON.parse(checkPin(b.plan, 65536).toString("utf8"));
|
|
977
|
+
need(plan?.schema === "hasna.trash.station-setup-plan/v1" && plan.stage === "ready" && plan.configurationReady === true && plan.nativeAdoptionVerified === false && digest(plan.planDigest) && plan.spec?.provider === provider && plan.spec.home === home && typeof plan.spec.station === "string" && /^[a-z0-9][a-z0-9-]{0,62}$/.test(plan.spec.station) && plan.spec.runtime?.path === b.runtime.path && plan.spec.runtime?.sha256 === b.runtime.sha256 && absolute(plan.spec.cwd) && (plan.spec.cwd === home || inside(home, plan.spec.cwd)) && typeof plan.spec.path === "string" && plan.spec.path.length <= 8192 && plan.spec.path.split(":").every(absolute), "plan_invalid");
|
|
978
|
+
const args = ["--config=/dev/null", "--no-env-file", "--no-macros", "--no-install", cli, "station", "check", "--plan", b.plan.path];
|
|
979
|
+
const remaining = () => Math.floor(b.timeoutMs - (performance.now() - started));
|
|
980
|
+
const timeout = remaining();
|
|
981
|
+
need(timeout > 0, "deadline_exceeded");
|
|
982
|
+
const child = await (d.spawn ?? runReadinessProcess)(b.runtime.path, args, {
|
|
983
|
+
cwd: home,
|
|
984
|
+
env: { HOME: home, PATH: plan.spec.path, LANG: "C", NO_COLOR: "1" },
|
|
985
|
+
timeout,
|
|
986
|
+
maxBuffer: 16384,
|
|
987
|
+
signal: d.signal
|
|
988
|
+
});
|
|
989
|
+
need(remaining() > 0, "deadline_exceeded");
|
|
990
|
+
if (child.error && "code" in child.error && child.error.code === "ETIMEDOUT")
|
|
991
|
+
return result("deadline_exceeded");
|
|
992
|
+
if (child.error || child.signal || child.status === null)
|
|
993
|
+
return result("execution_failed");
|
|
994
|
+
need(typeof child.stdout === "string" && typeof child.stderr === "string" && Buffer.byteLength(child.stdout) <= 16384 && Buffer.byteLength(child.stderr) <= 16384, "output_invalid");
|
|
995
|
+
let value;
|
|
996
|
+
try {
|
|
997
|
+
value = JSON.parse(child.stdout);
|
|
998
|
+
} catch {
|
|
999
|
+
return result("output_invalid");
|
|
1000
|
+
}
|
|
1001
|
+
if (child.status !== 0 || value?.configurationReady !== true) {
|
|
1002
|
+
return result(safeFailureCodes.has(value?.code) ? value.code : "check_failed");
|
|
1003
|
+
}
|
|
1004
|
+
need(value.schema === "hasna.trash.station-setup-result/v1" && value.station === plan.spec.station && value.provider === provider && value.planDigest === plan.planDigest && value.serviceReady === true && value.guardVerified === true && value.managedInstructionsVerified === true && value.mcpConfigurationVerified === true && value.mcpExecutionVerified === false && value.nativeAdoptionVerified === false && child.stderr.length === 0, "output_invalid");
|
|
1005
|
+
checkPin(b.plan, 65536);
|
|
1006
|
+
checkPin({ path: cli, sha256: b.trash.cliSha256 }, 64 * 1024 * 1024, true);
|
|
1007
|
+
checkPin(b.runtime, 200000000, true);
|
|
1008
|
+
need(read(bindingPath, 65536).equals(bindingBytes) && read(manifestPath, 65536).equals(manifestBytes), "file_changed");
|
|
1009
|
+
need(remaining() > 0, "deadline_exceeded");
|
|
1010
|
+
return result("ready", { station: value.station, planDigest: value.planDigest });
|
|
1011
|
+
} catch (error) {
|
|
1012
|
+
return result(error instanceof ReadinessError ? error.code : phase === "binding" && error?.code === "ENOENT" ? "binding_missing" : "binding_invalid");
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
function checkTrashReadinessBinding(input) {
|
|
1016
|
+
const home = realpathSync(homedir2()), b = parseTrashReadinessBinding(input, input?.provider, home);
|
|
1017
|
+
const path = trashReadinessBindingPath(b.provider, home), raw = Buffer.from(JSON.stringify(b) + `
|
|
1018
|
+
`);
|
|
1019
|
+
return runTrashReadiness(b.provider, { home, read: (selected, maximum, executable) => selected === path ? raw : readSafe(selected, maximum, executable) });
|
|
1020
|
+
}
|
|
1021
|
+
function readinessContext(result) {
|
|
1022
|
+
return result.ready ? `Trash readiness verified for ${result.station} (${result.provider}). Use Trash for recoverable file removal.` : `Trash readiness needs attention (${result.provider}: ${result.code}). Preserve files; do not fall back to direct deletion. Refresh the station setup before removal.`;
|
|
1023
|
+
}
|
|
1024
|
+
function readinessSessionStartOutput(result) {
|
|
1025
|
+
return { hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: readinessContext(result) } };
|
|
1026
|
+
}
|
|
1027
|
+
async function handleReadinessSessionStart(provider, event, dependencies = {}) {
|
|
1028
|
+
const input = event;
|
|
1029
|
+
if (!object(input) || input.hook_event_name !== "SessionStart" || !["startup", "resume", "clear", "fork", "compact"].includes(input.source)) {
|
|
1030
|
+
return readinessSessionStartOutput({ schema: "hasna.hooks.trash-readiness-result/v1", provider, ready: false, code: "input_invalid", nativeAdoptionVerified: false });
|
|
1031
|
+
}
|
|
1032
|
+
if (input.source === "compact")
|
|
1033
|
+
return {};
|
|
1034
|
+
return readinessSessionStartOutput(await runTrashReadiness(provider, dependencies));
|
|
1035
|
+
}
|
|
1036
|
+
var TRASH_READINESS_DEFAULT_TIMEOUT_MS = 45000, TRASH_READINESS_MAX_TIMEOUT_MS = 60000, TRASH_READINESS_NATIVE_TIMEOUT_SECONDS, ReadinessError, sha = (value) => createHash("sha256").update(value).digest("hex"), digest = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v), absolute = (v) => typeof v === "string" && v.length <= 4096 && isAbsolute(v) && resolve2(v) === v && !/[\0\r\n]/.test(v), object = (v) => v !== null && typeof v === "object" && !Array.isArray(v), keys = (v, expected) => object(v) && Object.keys(v).sort().join() === expected.sort().join(), pin = (v) => keys(v, ["path", "sha256"]) && absolute(v.path) && digest(v.sha256), safeFailureCodes;
|
|
1037
|
+
var init_native_readiness = __esm(() => {
|
|
1038
|
+
init_readiness_process();
|
|
1039
|
+
TRASH_READINESS_NATIVE_TIMEOUT_SECONDS = TRASH_READINESS_MAX_TIMEOUT_MS / 1000 + 5;
|
|
1040
|
+
ReadinessError = class ReadinessError extends Error {
|
|
1041
|
+
code;
|
|
1042
|
+
constructor(code) {
|
|
1043
|
+
super(code);
|
|
1044
|
+
this.code = code;
|
|
1045
|
+
}
|
|
1046
|
+
};
|
|
1047
|
+
safeFailureCodes = new Set([
|
|
1048
|
+
"unsupported_platform",
|
|
1049
|
+
"station_detection_failed",
|
|
1050
|
+
"station_mismatch",
|
|
1051
|
+
"hosted_unavailable",
|
|
1052
|
+
"authentication_refused",
|
|
1053
|
+
"station_setup_required",
|
|
1054
|
+
"invalid_status",
|
|
1055
|
+
"deadline_exceeded",
|
|
1056
|
+
"retention_mismatch",
|
|
1057
|
+
"managed_instructions_refused",
|
|
1058
|
+
"removal_instruction_missing",
|
|
1059
|
+
"instructions_changed",
|
|
1060
|
+
"native_config_changed",
|
|
1061
|
+
"guard_refused",
|
|
1062
|
+
"owner_refused"
|
|
1063
|
+
]);
|
|
1064
|
+
});
|
|
1065
|
+
|
|
735
1066
|
// src/config.ts
|
|
736
|
-
import { join as
|
|
1067
|
+
import { join as join3 } from "path";
|
|
737
1068
|
function getHooksDataDir() {
|
|
738
1069
|
return getEffectiveDataRoot();
|
|
739
1070
|
}
|
|
740
1071
|
function getCustomHooksDir() {
|
|
741
|
-
return
|
|
1072
|
+
return join3(getHooksDataDir(), "hooks");
|
|
742
1073
|
}
|
|
743
1074
|
function getLockPath() {
|
|
744
1075
|
const explicit = process.env.HASNA_HOOKS_LOCK_PATH ?? process.env.HOOKS_LOCK_PATH;
|
|
745
1076
|
if (explicit)
|
|
746
1077
|
return explicit;
|
|
747
|
-
return
|
|
1078
|
+
return join3(getHooksDataDir(), "hooks.lock");
|
|
748
1079
|
}
|
|
749
1080
|
var init_config = __esm(() => {
|
|
750
1081
|
init_app_home();
|
|
@@ -774,8 +1105,8 @@ __export(exports_manifest, {
|
|
|
774
1105
|
ScriptContainmentError: () => ScriptContainmentError,
|
|
775
1106
|
HOOK_NAME_RE: () => HOOK_NAME_RE
|
|
776
1107
|
});
|
|
777
|
-
import { existsSync as existsSync2, mkdirSync, readFileSync, readdirSync, realpathSync, writeFileSync } from "fs";
|
|
778
|
-
import { basename, dirname, isAbsolute, join as
|
|
1108
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync, readdirSync, realpathSync as realpathSync2, writeFileSync } from "fs";
|
|
1109
|
+
import { basename, dirname as dirname2, isAbsolute as isAbsolute2, join as join4, normalize, resolve as resolve3, sep } from "path";
|
|
779
1110
|
import { z } from "zod";
|
|
780
1111
|
function parseManifest(json) {
|
|
781
1112
|
const parsed = JSON.parse(json);
|
|
@@ -791,8 +1122,8 @@ function isWithin(child, parent) {
|
|
|
791
1122
|
return child === parent || child.startsWith(parent + sep);
|
|
792
1123
|
}
|
|
793
1124
|
function assertContainedLexical(target, root, raw) {
|
|
794
|
-
const lexical =
|
|
795
|
-
const rootLexical =
|
|
1125
|
+
const lexical = resolve3(target);
|
|
1126
|
+
const rootLexical = resolve3(root);
|
|
796
1127
|
if (!isWithin(lexical, rootLexical)) {
|
|
797
1128
|
throw new ScriptContainmentError(target, root, raw);
|
|
798
1129
|
}
|
|
@@ -802,16 +1133,16 @@ function realpathNearest(path) {
|
|
|
802
1133
|
const tail = [];
|
|
803
1134
|
let cur = path;
|
|
804
1135
|
while (!existsSync2(cur)) {
|
|
805
|
-
const parent =
|
|
1136
|
+
const parent = dirname2(cur);
|
|
806
1137
|
if (parent === cur)
|
|
807
1138
|
break;
|
|
808
1139
|
tail.unshift(basename(cur));
|
|
809
1140
|
cur = parent;
|
|
810
1141
|
}
|
|
811
|
-
return
|
|
1142
|
+
return join4(realpathSync2(cur), ...tail);
|
|
812
1143
|
}
|
|
813
1144
|
function assertContainedReal(root, target, raw) {
|
|
814
|
-
const rootReal =
|
|
1145
|
+
const rootReal = realpathSync2(root);
|
|
815
1146
|
const targetReal = realpathNearest(target);
|
|
816
1147
|
if (!isWithin(targetReal, rootReal)) {
|
|
817
1148
|
throw new ScriptContainmentError(target, root, raw);
|
|
@@ -832,7 +1163,7 @@ function scriptRelFor(manifest) {
|
|
|
832
1163
|
return `script${ext}`;
|
|
833
1164
|
}
|
|
834
1165
|
const rel = normalize(manifest.script);
|
|
835
|
-
if (
|
|
1166
|
+
if (isAbsolute2(rel)) {
|
|
836
1167
|
throw new Error(`manifest script must be relative or inline, got absolute path '${manifest.script}'`);
|
|
837
1168
|
}
|
|
838
1169
|
return rel;
|
|
@@ -841,25 +1172,25 @@ function resolveScript(manifest, manifestDir) {
|
|
|
841
1172
|
if (isInlineScript(manifest))
|
|
842
1173
|
return { path: scriptRelFor(manifest), content: manifest.script };
|
|
843
1174
|
const rel = scriptRelFor(manifest);
|
|
844
|
-
const resolved = assertContained(
|
|
1175
|
+
const resolved = assertContained(join4(manifestDir, rel), manifestDir, manifest.script);
|
|
845
1176
|
if (!existsSync2(resolved)) {
|
|
846
1177
|
throw new Error(`manifest script file not found: ${resolved}`);
|
|
847
1178
|
}
|
|
848
1179
|
return { path: rel, content: readFileSync(resolved, "utf-8") };
|
|
849
1180
|
}
|
|
850
1181
|
function customHookDir(name) {
|
|
851
|
-
return
|
|
1182
|
+
return join4(getCustomHooksDir(), shortManifestName(name));
|
|
852
1183
|
}
|
|
853
1184
|
function readCustomManifest(name) {
|
|
854
1185
|
const dir = customHookDir(name);
|
|
855
|
-
const manifestPath =
|
|
1186
|
+
const manifestPath = join4(dir, "manifest.json");
|
|
856
1187
|
if (!existsSync2(manifestPath))
|
|
857
1188
|
return;
|
|
858
1189
|
try {
|
|
859
1190
|
const manifest = parseManifest(readFileSync(manifestPath, "utf-8"));
|
|
860
1191
|
const script = resolveScript(manifest, dir);
|
|
861
1192
|
const scriptIsInline = isInlineScript(manifest);
|
|
862
|
-
return { manifest, scriptPath:
|
|
1193
|
+
return { manifest, scriptPath: join4(dir, script.path), scriptContent: script.content, scriptIsInline };
|
|
863
1194
|
} catch (err) {
|
|
864
1195
|
if (err instanceof ScriptContainmentError)
|
|
865
1196
|
throw err;
|
|
@@ -880,18 +1211,18 @@ function listCustomHooks() {
|
|
|
880
1211
|
}
|
|
881
1212
|
function writeCustomHook(name, manifest, scriptContent, scriptRelPath) {
|
|
882
1213
|
const dir = customHookDir(name);
|
|
883
|
-
if (
|
|
1214
|
+
if (isAbsolute2(scriptRelPath)) {
|
|
884
1215
|
throw new ScriptContainmentError(scriptRelPath, dir);
|
|
885
1216
|
}
|
|
886
|
-
const scriptPath = scriptRelPath.includes("/") ?
|
|
1217
|
+
const scriptPath = scriptRelPath.includes("/") ? join4(dir, scriptRelPath) : join4(dir, scriptRelPath);
|
|
887
1218
|
assertContainedLexical(scriptPath, dir, scriptRelPath);
|
|
888
1219
|
mkdirSync(dir, { recursive: true });
|
|
889
1220
|
assertContainedReal(dir, scriptPath, scriptRelPath);
|
|
890
|
-
const scriptDir =
|
|
1221
|
+
const scriptDir = dirname2(scriptPath);
|
|
891
1222
|
if (scriptDir !== dir)
|
|
892
1223
|
mkdirSync(scriptDir, { recursive: true });
|
|
893
1224
|
writeFileSync(scriptPath, scriptContent, "utf-8");
|
|
894
|
-
writeFileSync(
|
|
1225
|
+
writeFileSync(join4(dir, "manifest.json"), JSON.stringify(manifest, null, 2) + `
|
|
895
1226
|
`, "utf-8");
|
|
896
1227
|
return { dir, scriptPath };
|
|
897
1228
|
}
|
|
@@ -935,14 +1266,14 @@ __export(exports_resolve, {
|
|
|
935
1266
|
resolveHook: () => resolveHook,
|
|
936
1267
|
resolveBundledHooksDir: () => resolveBundledHooksDir
|
|
937
1268
|
});
|
|
938
|
-
import { dirname as
|
|
1269
|
+
import { dirname as dirname3, join as join5 } from "path";
|
|
939
1270
|
import { existsSync as existsSync3 } from "fs";
|
|
940
1271
|
import { fileURLToPath } from "url";
|
|
941
|
-
function resolveBundledHooksDir(moduleDir =
|
|
942
|
-
const sourceLayout =
|
|
943
|
-
if (existsSync3(
|
|
1272
|
+
function resolveBundledHooksDir(moduleDir = dirname3(fileURLToPath(import.meta.url))) {
|
|
1273
|
+
const sourceLayout = join5(moduleDir, "..", "..", "hooks");
|
|
1274
|
+
if (existsSync3(join5(sourceLayout, "hook-gitguard")))
|
|
944
1275
|
return sourceLayout;
|
|
945
|
-
return
|
|
1276
|
+
return join5(moduleDir, "..", "hooks");
|
|
946
1277
|
}
|
|
947
1278
|
function resolveHookDir(name) {
|
|
948
1279
|
const custom = readCustomManifest(name);
|
|
@@ -953,10 +1284,10 @@ function resolveHookDir(name) {
|
|
|
953
1284
|
return;
|
|
954
1285
|
const short = shortManifestName(name);
|
|
955
1286
|
const bundledBase = resolveBundledHooksDir();
|
|
956
|
-
const direct =
|
|
1287
|
+
const direct = join5(bundledBase, short);
|
|
957
1288
|
if (existsSync3(direct))
|
|
958
1289
|
return direct;
|
|
959
|
-
const prefixed =
|
|
1290
|
+
const prefixed = join5(bundledBase, `hook-${short}`);
|
|
960
1291
|
return existsSync3(prefixed) ? prefixed : undefined;
|
|
961
1292
|
}
|
|
962
1293
|
function resolveScriptPath(name) {
|
|
@@ -966,7 +1297,7 @@ function resolveScriptPath(name) {
|
|
|
966
1297
|
const dir = resolveHookDir(name);
|
|
967
1298
|
if (!dir)
|
|
968
1299
|
return;
|
|
969
|
-
const script =
|
|
1300
|
+
const script = join5(dir, "src", "hook.ts");
|
|
970
1301
|
return existsSync3(script) ? script : undefined;
|
|
971
1302
|
}
|
|
972
1303
|
function resolveHookMeta(name) {
|
|
@@ -1023,15 +1354,15 @@ var init_resolve = __esm(() => {
|
|
|
1023
1354
|
});
|
|
1024
1355
|
|
|
1025
1356
|
// src/lib/native-safety.ts
|
|
1026
|
-
import { createHash } from "crypto";
|
|
1027
|
-
import { closeSync, constants, existsSync as existsSync4, fstatSync, lstatSync, openSync, readFileSync as readFileSync2, realpathSync as
|
|
1028
|
-
import { dirname as
|
|
1029
|
-
import { homedir as
|
|
1357
|
+
import { createHash as createHash2 } from "crypto";
|
|
1358
|
+
import { closeSync as closeSync2, constants as constants2, existsSync as existsSync4, fstatSync as fstatSync2, lstatSync as lstatSync2, openSync as openSync2, readFileSync as readFileSync2, realpathSync as realpathSync3 } from "fs";
|
|
1359
|
+
import { dirname as dirname4, isAbsolute as isAbsolute3, join as join6 } from "path";
|
|
1360
|
+
import { homedir as homedir3 } from "os";
|
|
1030
1361
|
function isNativeSafetyName(name) {
|
|
1031
1362
|
return name === "trash-guard" || name === "workspace-repos-guard";
|
|
1032
1363
|
}
|
|
1033
1364
|
function validateBinding(b) {
|
|
1034
|
-
if (!b || !isNativeSafetyName(b.name) || ![b.runtime, b.worker, b.home].every((p) => typeof p === "string" &&
|
|
1365
|
+
if (!b || !isNativeSafetyName(b.name) || ![b.runtime, b.worker, b.home].every((p) => typeof p === "string" && isAbsolute3(p) && !/[\0\r\n]/.test(p)) || ![b.runtimeSHA256, b.workerSHA256].every((h) => /^[0-9a-f]{64}$/.test(h)) || typeof b.path !== "string" || b.path.split(":").some((p) => !isAbsolute3(p) || /[\0\r\n]/.test(p)))
|
|
1035
1366
|
throw new Error("Invalid native safety binding");
|
|
1036
1367
|
}
|
|
1037
1368
|
function buildNativeSafetyCommand(binding) {
|
|
@@ -1056,27 +1387,27 @@ function nativeSafetyCommandBinding(command) {
|
|
|
1056
1387
|
}
|
|
1057
1388
|
}
|
|
1058
1389
|
function pinEligibleFile(file, maximum, executable = false) {
|
|
1059
|
-
if (!
|
|
1390
|
+
if (!isAbsolute3(file) || realpathSync3(file) !== file)
|
|
1060
1391
|
throw new Error("Native safety requires canonical files");
|
|
1061
|
-
for (let parent =
|
|
1062
|
-
const s =
|
|
1392
|
+
for (let parent = dirname4(file);; ) {
|
|
1393
|
+
const s = lstatSync2(parent);
|
|
1063
1394
|
if (!s.isDirectory() || ![0, process.getuid()].includes(s.uid) || s.mode & 18)
|
|
1064
1395
|
throw new Error("Native safety requires trusted, non-writable file ancestry");
|
|
1065
|
-
if (parent ===
|
|
1396
|
+
if (parent === dirname4(parent))
|
|
1066
1397
|
break;
|
|
1067
|
-
parent =
|
|
1398
|
+
parent = dirname4(parent);
|
|
1068
1399
|
}
|
|
1069
|
-
const fd =
|
|
1400
|
+
const fd = openSync2(file, constants2.O_RDONLY | constants2.O_NOFOLLOW);
|
|
1070
1401
|
try {
|
|
1071
|
-
const s =
|
|
1402
|
+
const s = fstatSync2(fd);
|
|
1072
1403
|
if (!s.isFile() || ![0, process.getuid()].includes(s.uid) || s.mode & 18 || s.size > maximum || executable && !(s.mode & 64))
|
|
1073
1404
|
throw new Error("Native safety requires an eligible installed file");
|
|
1074
1405
|
const bytes = readFileSync2(fd);
|
|
1075
1406
|
if (bytes.length > maximum)
|
|
1076
1407
|
throw new Error("Native safety file exceeds its bound");
|
|
1077
|
-
return
|
|
1408
|
+
return createHash2("sha256").update(bytes).digest("hex");
|
|
1078
1409
|
} finally {
|
|
1079
|
-
|
|
1410
|
+
closeSync2(fd);
|
|
1080
1411
|
}
|
|
1081
1412
|
}
|
|
1082
1413
|
function pinVerified(file, expected, maximum, executable = false) {
|
|
@@ -1093,14 +1424,14 @@ function nativeSafetyBindingVerified(command) {
|
|
|
1093
1424
|
return pinVerified(binding.runtime, binding.runtimeSHA256, 200000000, true) && pinVerified(binding.worker, binding.workerSHA256, 256000);
|
|
1094
1425
|
}
|
|
1095
1426
|
function installedNativeSafetyCommand(name) {
|
|
1096
|
-
const home =
|
|
1097
|
-
const runtime =
|
|
1098
|
-
const candidates = [
|
|
1427
|
+
const home = realpathSync3(homedir3());
|
|
1428
|
+
const runtime = realpathSync3(process.execPath);
|
|
1429
|
+
const candidates = [join6(import.meta.dir, "../../bin/native-safety-entry.js"), join6(import.meta.dir, "../bin/native-safety-entry.js"), join6(import.meta.dir, "native-safety-entry.js")];
|
|
1099
1430
|
const worker = candidates.find(existsSync4);
|
|
1100
1431
|
if (!worker)
|
|
1101
1432
|
throw new Error("Bundled native safety runner is missing; build or reinstall @hasna/hooks before installing its guards.");
|
|
1102
|
-
const canonicalWorker =
|
|
1103
|
-
return buildNativeSafetyCommand({ name, runtime, runtimeSHA256: pinEligibleFile(runtime, 200000000, true), worker: canonicalWorker, workerSHA256: pinEligibleFile(canonicalWorker, 256000), home, path: `${
|
|
1433
|
+
const canonicalWorker = realpathSync3(worker);
|
|
1434
|
+
return buildNativeSafetyCommand({ name, runtime, runtimeSHA256: pinEligibleFile(runtime, 200000000, true), worker: canonicalWorker, workerSHA256: pinEligibleFile(canonicalWorker, 256000), home, path: `${dirname4(runtime)}:${join6(home, ".bun/bin")}:/usr/local/bin:/usr/bin:/bin` });
|
|
1104
1435
|
}
|
|
1105
1436
|
var supervisorSource, quote = (value) => "'" + value.replaceAll("'", "'\\''") + "'", program, shell, prefix;
|
|
1106
1437
|
var init_native_safety = __esm(() => {
|
|
@@ -1171,333 +1502,6 @@ if [ "$result" -ne 0 ]; then printf '%s\\n' 'Native safety guard refused: verifi
|
|
|
1171
1502
|
prefix = `/usr/bin/env -i /bin/sh -c ${quote(shell)} hasna-native-safety-v1 `;
|
|
1172
1503
|
});
|
|
1173
1504
|
|
|
1174
|
-
// src/lib/readiness-process.ts
|
|
1175
|
-
import { spawn } from "child_process";
|
|
1176
|
-
function runReadinessProcess(command, args, options) {
|
|
1177
|
-
if (!["darwin", "linux"].includes(process.platform)) {
|
|
1178
|
-
return Promise.resolve({ status: null, error: { code: "ENOTSUP" }, stdout: "", stderr: "" });
|
|
1179
|
-
}
|
|
1180
|
-
if (options.signal?.aborted) {
|
|
1181
|
-
return Promise.resolve({ status: null, error: { code: "ABORT_ERR" }, stdout: "", stderr: "" });
|
|
1182
|
-
}
|
|
1183
|
-
return new Promise((resolve3) => {
|
|
1184
|
-
const child = spawn(command, ["--config=/dev/null", "--no-env-file", "--no-macros", "--no-install", "--eval", supervisor, "--", JSON.stringify({ command, args, maxBuffer: options.maxBuffer, timeout: options.timeout })], {
|
|
1185
|
-
cwd: options.cwd,
|
|
1186
|
-
env: options.env,
|
|
1187
|
-
detached: true,
|
|
1188
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
1189
|
-
});
|
|
1190
|
-
let status = null, error;
|
|
1191
|
-
let stopping = false, exited = false, finished = false;
|
|
1192
|
-
let stdout = "", stderr = "", control = "";
|
|
1193
|
-
const maximumEnvelope = options.maxBuffer * 12 + 1024;
|
|
1194
|
-
let cleanup;
|
|
1195
|
-
const finish = () => {
|
|
1196
|
-
if (finished)
|
|
1197
|
-
return;
|
|
1198
|
-
finished = true;
|
|
1199
|
-
clearTimeout(deadline);
|
|
1200
|
-
clearTimeout(cleanup);
|
|
1201
|
-
options.signal?.removeEventListener("abort", abort);
|
|
1202
|
-
child.stdin?.destroy();
|
|
1203
|
-
child.stdout?.destroy();
|
|
1204
|
-
child.stderr?.destroy();
|
|
1205
|
-
resolve3({ status, error, stdout, stderr });
|
|
1206
|
-
};
|
|
1207
|
-
const stop = (code) => {
|
|
1208
|
-
if (code && !finished)
|
|
1209
|
-
error ??= { code };
|
|
1210
|
-
if (finished || stopping)
|
|
1211
|
-
return;
|
|
1212
|
-
stopping = true;
|
|
1213
|
-
if (exited || !child.pid) {
|
|
1214
|
-
error ??= { code: "ECHILD" };
|
|
1215
|
-
} else {
|
|
1216
|
-
try {
|
|
1217
|
-
child.stdin.end();
|
|
1218
|
-
} catch {
|
|
1219
|
-
error = { code: "ECHILD" };
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
cleanup = setTimeout(() => {
|
|
1223
|
-
error = { code: "ECHILD" };
|
|
1224
|
-
finish();
|
|
1225
|
-
}, 1000);
|
|
1226
|
-
};
|
|
1227
|
-
const abort = () => stop("ABORT_ERR");
|
|
1228
|
-
const deadline = setTimeout(() => stop("ETIMEDOUT"), options.timeout);
|
|
1229
|
-
options.signal?.addEventListener("abort", abort, { once: true });
|
|
1230
|
-
if (options.signal?.aborted)
|
|
1231
|
-
abort();
|
|
1232
|
-
child.stdin?.on("error", () => {
|
|
1233
|
-
if (!stopping)
|
|
1234
|
-
stop("ECHILD");
|
|
1235
|
-
});
|
|
1236
|
-
child.stdout?.on("data", (bytes) => {
|
|
1237
|
-
if (stopping)
|
|
1238
|
-
return;
|
|
1239
|
-
control += bytes.toString("utf8");
|
|
1240
|
-
if (Buffer.byteLength(control) > maximumEnvelope)
|
|
1241
|
-
return stop("ENOBUFS");
|
|
1242
|
-
if (!control.endsWith(`
|
|
1243
|
-
`))
|
|
1244
|
-
return;
|
|
1245
|
-
try {
|
|
1246
|
-
const value = JSON.parse(control);
|
|
1247
|
-
if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((key) => !["status", "error", "stdout", "stderr"].includes(key)) || !(value.status === null || Number.isInteger(value.status) && value.status >= 0 && value.status <= 255) || typeof value.stdout !== "string" || typeof value.stderr !== "string" || Buffer.byteLength(value.stdout) > options.maxBuffer || Buffer.byteLength(value.stderr) > options.maxBuffer || value.error !== undefined && (Object.keys(value.error).join() !== "code" || !["ENOBUFS", "ECHILD"].includes(value.error.code))) {
|
|
1248
|
-
return stop("EPROTO");
|
|
1249
|
-
}
|
|
1250
|
-
status = value.status;
|
|
1251
|
-
stdout = value.stdout;
|
|
1252
|
-
stderr = value.stderr;
|
|
1253
|
-
stop(value.error?.code);
|
|
1254
|
-
} catch {
|
|
1255
|
-
stop("EPROTO");
|
|
1256
|
-
}
|
|
1257
|
-
});
|
|
1258
|
-
child.stdout?.on("error", () => stop("EPROTO"));
|
|
1259
|
-
child.stderr?.on("data", () => stop("ECHILD"));
|
|
1260
|
-
child.stderr?.on("error", () => stop("ECHILD"));
|
|
1261
|
-
child.on("error", () => {
|
|
1262
|
-
error = { code: "ECHILD" };
|
|
1263
|
-
stop();
|
|
1264
|
-
});
|
|
1265
|
-
child.on("exit", () => {
|
|
1266
|
-
exited = true;
|
|
1267
|
-
if (!stopping)
|
|
1268
|
-
stop("ECHILD");
|
|
1269
|
-
});
|
|
1270
|
-
child.on("close", () => {
|
|
1271
|
-
if (!stopping)
|
|
1272
|
-
error = { code: "ECHILD" };
|
|
1273
|
-
finish();
|
|
1274
|
-
});
|
|
1275
|
-
});
|
|
1276
|
-
}
|
|
1277
|
-
var supervisor;
|
|
1278
|
-
var init_readiness_process = __esm(() => {
|
|
1279
|
-
supervisor = String.raw`
|
|
1280
|
-
const {spawn}=require("node:child_process");
|
|
1281
|
-
const spec=JSON.parse(process.argv.at(-1));
|
|
1282
|
-
// This runtime was created detached and is its group's live leader. Its
|
|
1283
|
-
// independent timer and parent pipe remain effective if the observer exits
|
|
1284
|
-
// or its event loop stops responding. Only this freshly created tree is hit.
|
|
1285
|
-
const cleanup=()=>{try{process.kill(-process.pid,"SIGKILL")}catch{process.exit(1)}};
|
|
1286
|
-
setTimeout(cleanup,spec.timeout);
|
|
1287
|
-
process.stdin.once("end",cleanup);
|
|
1288
|
-
process.stdin.once("error",cleanup);
|
|
1289
|
-
process.stdin.on("data",cleanup);
|
|
1290
|
-
process.stdin.resume();
|
|
1291
|
-
for(const signal of ["SIGTERM","SIGINT","SIGHUP"])process.once(signal,cleanup);
|
|
1292
|
-
let done=false, out=Buffer.alloc(0), err=Buffer.alloc(0);
|
|
1293
|
-
function report(status,error){
|
|
1294
|
-
if(done)return; done=true;
|
|
1295
|
-
process.stdout.write(JSON.stringify({status,error,stdout:out.toString("utf8"),stderr:err.toString("utf8")})+"\n");
|
|
1296
|
-
}
|
|
1297
|
-
const child=spawn(spec.command,spec.args,{stdio:["ignore","pipe","pipe"],env:process.env});
|
|
1298
|
-
for(const [stream,name] of [[child.stdout,"stdout"],[child.stderr,"stderr"]]){
|
|
1299
|
-
stream.on("data",bytes=>{
|
|
1300
|
-
if(done)return;
|
|
1301
|
-
const before=name==="stdout"?out:err;
|
|
1302
|
-
if(before.length+bytes.length>spec.maxBuffer){report(null,{code:"ENOBUFS"});return}
|
|
1303
|
-
if(name==="stdout")out=Buffer.concat([out,bytes]);else err=Buffer.concat([err,bytes]);
|
|
1304
|
-
});
|
|
1305
|
-
stream.on("error",()=>report(null,{code:"ECHILD"}));
|
|
1306
|
-
}
|
|
1307
|
-
child.on("error",()=>report(null,{code:"ECHILD"}));
|
|
1308
|
-
// A successful observation includes complete streams. A nested client that
|
|
1309
|
-
// keeps them open after its parent exits is a failure requiring group cleanup.
|
|
1310
|
-
child.on("exit",()=>setTimeout(()=>report(null,{code:"ECHILD"}),100));
|
|
1311
|
-
child.on("close",(status,signal)=>report(status,signal?{code:"ECHILD"}:undefined));
|
|
1312
|
-
`;
|
|
1313
|
-
});
|
|
1314
|
-
|
|
1315
|
-
// src/native-readiness.ts
|
|
1316
|
-
var exports_native_readiness = {};
|
|
1317
|
-
__export(exports_native_readiness, {
|
|
1318
|
-
trashReadinessBindingPath: () => trashReadinessBindingPath,
|
|
1319
|
-
runTrashReadiness: () => runTrashReadiness,
|
|
1320
|
-
readinessSessionStartOutput: () => readinessSessionStartOutput,
|
|
1321
|
-
readinessContext: () => readinessContext,
|
|
1322
|
-
readTrashReadinessBindingFile: () => readTrashReadinessBindingFile,
|
|
1323
|
-
parseTrashReadinessBinding: () => parseTrashReadinessBinding,
|
|
1324
|
-
isReadinessProvider: () => isReadinessProvider,
|
|
1325
|
-
handleReadinessSessionStart: () => handleReadinessSessionStart,
|
|
1326
|
-
checkTrashReadinessBinding: () => checkTrashReadinessBinding
|
|
1327
|
-
});
|
|
1328
|
-
import { createHash as createHash2 } from "crypto";
|
|
1329
|
-
import { closeSync as closeSync2, constants as constants2, fstatSync as fstatSync2, lstatSync as lstatSync2, openSync as openSync2, readSync, realpathSync as realpathSync3 } from "fs";
|
|
1330
|
-
import { homedir as homedir3 } from "os";
|
|
1331
|
-
import { dirname as dirname4, isAbsolute as isAbsolute3, join as join6, relative, resolve as resolve3 } from "path";
|
|
1332
|
-
function need(value, code) {
|
|
1333
|
-
if (!value)
|
|
1334
|
-
throw new ReadinessError(code);
|
|
1335
|
-
}
|
|
1336
|
-
function isReadinessProvider(value) {
|
|
1337
|
-
return value === "claude" || value === "codex" || value === "sumi";
|
|
1338
|
-
}
|
|
1339
|
-
function inside(home, path) {
|
|
1340
|
-
const part = relative(home, path);
|
|
1341
|
-
return part !== "" && part !== ".." && !part.startsWith("../") && !isAbsolute3(part);
|
|
1342
|
-
}
|
|
1343
|
-
function parseTrashReadinessBinding(input, provider, home) {
|
|
1344
|
-
const b = input;
|
|
1345
|
-
need(isReadinessProvider(provider) && absolute(home), "binding_invalid");
|
|
1346
|
-
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");
|
|
1347
|
-
return structuredClone(b);
|
|
1348
|
-
}
|
|
1349
|
-
function trashReadinessBindingPath(provider, home = homedir3()) {
|
|
1350
|
-
need(isReadinessProvider(provider) && absolute(home), "binding_invalid");
|
|
1351
|
-
return join6(home, ".hasna", "hooks", "native", "trash-readiness", provider + ".json");
|
|
1352
|
-
}
|
|
1353
|
-
function readSafe(path, maximum, executable = false) {
|
|
1354
|
-
need(absolute(path) && realpathSync3(path) === path, "unsafe_path");
|
|
1355
|
-
for (let parent = dirname4(path);; parent = dirname4(parent)) {
|
|
1356
|
-
const stat = lstatSync2(parent);
|
|
1357
|
-
need(stat.isDirectory() && [0, process.getuid?.()].includes(stat.uid) && !(stat.mode & 18), "unsafe_path");
|
|
1358
|
-
if (parent === dirname4(parent))
|
|
1359
|
-
break;
|
|
1360
|
-
}
|
|
1361
|
-
const fd = openSync2(path, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
|
|
1362
|
-
try {
|
|
1363
|
-
const stat = fstatSync2(fd);
|
|
1364
|
-
need(stat.isFile() && [0, process.getuid?.()].includes(stat.uid) && !(stat.mode & 18) && stat.size <= maximum && (!executable || !!(stat.mode & 64)), "unsafe_path");
|
|
1365
|
-
const bytes = Buffer.alloc(stat.size + 1);
|
|
1366
|
-
let length = 0;
|
|
1367
|
-
while (length < bytes.length) {
|
|
1368
|
-
const count = readSync(fd, bytes, length, bytes.length - length, null);
|
|
1369
|
-
if (!count)
|
|
1370
|
-
break;
|
|
1371
|
-
length += count;
|
|
1372
|
-
}
|
|
1373
|
-
const after = fstatSync2(fd);
|
|
1374
|
-
need(length === stat.size && after.size === stat.size && after.mtimeMs === stat.mtimeMs && after.ctimeMs === stat.ctimeMs, "file_changed");
|
|
1375
|
-
return bytes.subarray(0, length);
|
|
1376
|
-
} finally {
|
|
1377
|
-
closeSync2(fd);
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
|
-
function readTrashReadinessBindingFile(path, provider, home = realpathSync3(homedir3())) {
|
|
1381
|
-
const raw = readSafe(path, 65536);
|
|
1382
|
-
return { binding: parseTrashReadinessBinding(JSON.parse(raw.toString("utf8")), provider, home), raw, sha256: sha(raw) };
|
|
1383
|
-
}
|
|
1384
|
-
async function runTrashReadiness(provider, d = {}) {
|
|
1385
|
-
const started = performance.now();
|
|
1386
|
-
const result = (code, extra = {}) => ({
|
|
1387
|
-
schema: "hasna.hooks.trash-readiness-result/v1",
|
|
1388
|
-
provider,
|
|
1389
|
-
ready: code === "ready",
|
|
1390
|
-
code,
|
|
1391
|
-
nativeAdoptionVerified: false,
|
|
1392
|
-
...extra
|
|
1393
|
-
});
|
|
1394
|
-
let phase = "binding";
|
|
1395
|
-
try {
|
|
1396
|
-
const home = d.home ?? realpathSync3(homedir3()), read = d.read ?? readSafe;
|
|
1397
|
-
need(isReadinessProvider(provider), "binding_invalid");
|
|
1398
|
-
const bindingPath = trashReadinessBindingPath(provider, home), bindingBytes = read(bindingPath, 65536);
|
|
1399
|
-
const b = parseTrashReadinessBinding(JSON.parse(bindingBytes.toString("utf8")), provider, home);
|
|
1400
|
-
phase = "consumer";
|
|
1401
|
-
const checkPin = (p, maximum, executable = false) => {
|
|
1402
|
-
const bytes = read(p.path, maximum, executable);
|
|
1403
|
-
need(sha(bytes) === p.sha256, "file_changed");
|
|
1404
|
-
return bytes;
|
|
1405
|
-
};
|
|
1406
|
-
checkPin(b.runtime, 200000000, true);
|
|
1407
|
-
const manifestPath = join6(b.trash.root, "package.json"), manifestBytes = read(manifestPath, 65536);
|
|
1408
|
-
const manifest = JSON.parse(manifestBytes.toString("utf8"));
|
|
1409
|
-
need(manifest.name === "@hasna/trash" && manifest.version === b.trash.version && manifest.bin?.trash === "dist/cli/index.js", "package_mismatch");
|
|
1410
|
-
const cli = join6(b.trash.root, "dist", "cli", "index.js");
|
|
1411
|
-
checkPin({ path: cli, sha256: b.trash.cliSha256 }, 64 * 1024 * 1024, true);
|
|
1412
|
-
const plan = JSON.parse(checkPin(b.plan, 65536).toString("utf8"));
|
|
1413
|
-
need(plan?.schema === "hasna.trash.station-setup-plan/v1" && plan.stage === "ready" && plan.configurationReady === true && plan.nativeAdoptionVerified === false && digest(plan.planDigest) && plan.spec?.provider === provider && plan.spec.home === home && typeof plan.spec.station === "string" && /^[a-z0-9][a-z0-9-]{0,62}$/.test(plan.spec.station) && plan.spec.runtime?.path === b.runtime.path && plan.spec.runtime?.sha256 === b.runtime.sha256 && absolute(plan.spec.cwd) && (plan.spec.cwd === home || inside(home, plan.spec.cwd)) && typeof plan.spec.path === "string" && plan.spec.path.length <= 8192 && plan.spec.path.split(":").every(absolute), "plan_invalid");
|
|
1414
|
-
const args = ["--config=/dev/null", "--no-env-file", "--no-macros", "--no-install", cli, "station", "check", "--plan", b.plan.path];
|
|
1415
|
-
const remaining = () => Math.floor(b.timeoutMs - (performance.now() - started));
|
|
1416
|
-
const timeout = remaining();
|
|
1417
|
-
need(timeout > 0, "deadline_exceeded");
|
|
1418
|
-
const child = await (d.spawn ?? runReadinessProcess)(b.runtime.path, args, {
|
|
1419
|
-
cwd: home,
|
|
1420
|
-
env: { HOME: home, PATH: plan.spec.path, LANG: "C", NO_COLOR: "1" },
|
|
1421
|
-
timeout,
|
|
1422
|
-
maxBuffer: 16384,
|
|
1423
|
-
signal: d.signal
|
|
1424
|
-
});
|
|
1425
|
-
need(remaining() > 0, "deadline_exceeded");
|
|
1426
|
-
if (child.error && "code" in child.error && child.error.code === "ETIMEDOUT")
|
|
1427
|
-
return result("deadline_exceeded");
|
|
1428
|
-
if (child.error || child.signal || child.status === null)
|
|
1429
|
-
return result("execution_failed");
|
|
1430
|
-
need(typeof child.stdout === "string" && typeof child.stderr === "string" && Buffer.byteLength(child.stdout) <= 16384 && Buffer.byteLength(child.stderr) <= 16384, "output_invalid");
|
|
1431
|
-
let value;
|
|
1432
|
-
try {
|
|
1433
|
-
value = JSON.parse(child.stdout);
|
|
1434
|
-
} catch {
|
|
1435
|
-
return result("output_invalid");
|
|
1436
|
-
}
|
|
1437
|
-
if (child.status !== 0 || value?.configurationReady !== true) {
|
|
1438
|
-
return result(safeFailureCodes.has(value?.code) ? value.code : "check_failed");
|
|
1439
|
-
}
|
|
1440
|
-
need(value.schema === "hasna.trash.station-setup-result/v1" && value.station === plan.spec.station && value.provider === provider && value.planDigest === plan.planDigest && value.serviceReady === true && value.guardVerified === true && value.managedInstructionsVerified === true && value.mcpConfigurationVerified === true && value.mcpExecutionVerified === false && value.nativeAdoptionVerified === false && child.stderr.length === 0, "output_invalid");
|
|
1441
|
-
checkPin(b.plan, 65536);
|
|
1442
|
-
checkPin({ path: cli, sha256: b.trash.cliSha256 }, 64 * 1024 * 1024, true);
|
|
1443
|
-
checkPin(b.runtime, 200000000, true);
|
|
1444
|
-
need(read(bindingPath, 65536).equals(bindingBytes) && read(manifestPath, 65536).equals(manifestBytes), "file_changed");
|
|
1445
|
-
need(remaining() > 0, "deadline_exceeded");
|
|
1446
|
-
return result("ready", { station: value.station, planDigest: value.planDigest });
|
|
1447
|
-
} catch (error) {
|
|
1448
|
-
return result(error instanceof ReadinessError ? error.code : phase === "binding" && error?.code === "ENOENT" ? "binding_missing" : "binding_invalid");
|
|
1449
|
-
}
|
|
1450
|
-
}
|
|
1451
|
-
function checkTrashReadinessBinding(input) {
|
|
1452
|
-
const home = realpathSync3(homedir3()), b = parseTrashReadinessBinding(input, input?.provider, home);
|
|
1453
|
-
const path = trashReadinessBindingPath(b.provider, home), raw = Buffer.from(JSON.stringify(b) + `
|
|
1454
|
-
`);
|
|
1455
|
-
return runTrashReadiness(b.provider, { home, read: (selected, maximum, executable) => selected === path ? raw : readSafe(selected, maximum, executable) });
|
|
1456
|
-
}
|
|
1457
|
-
function readinessContext(result) {
|
|
1458
|
-
return result.ready ? `Trash readiness verified for ${result.station} (${result.provider}). Use Trash for recoverable file removal.` : `Trash readiness needs attention (${result.provider}: ${result.code}). Preserve files; do not fall back to direct deletion. Refresh the station setup before removal.`;
|
|
1459
|
-
}
|
|
1460
|
-
function readinessSessionStartOutput(result) {
|
|
1461
|
-
return { hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: readinessContext(result) } };
|
|
1462
|
-
}
|
|
1463
|
-
async function handleReadinessSessionStart(provider, event, dependencies = {}) {
|
|
1464
|
-
const input = event;
|
|
1465
|
-
if (!object(input) || input.hook_event_name !== "SessionStart" || !["startup", "resume", "clear", "fork", "compact"].includes(input.source)) {
|
|
1466
|
-
return readinessSessionStartOutput({ schema: "hasna.hooks.trash-readiness-result/v1", provider, ready: false, code: "input_invalid", nativeAdoptionVerified: false });
|
|
1467
|
-
}
|
|
1468
|
-
if (input.source === "compact")
|
|
1469
|
-
return {};
|
|
1470
|
-
return readinessSessionStartOutput(await runTrashReadiness(provider, dependencies));
|
|
1471
|
-
}
|
|
1472
|
-
var ReadinessError, sha = (value) => createHash2("sha256").update(value).digest("hex"), digest = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v), absolute = (v) => typeof v === "string" && v.length <= 4096 && isAbsolute3(v) && resolve3(v) === v && !/[\0\r\n]/.test(v), object = (v) => v !== null && typeof v === "object" && !Array.isArray(v), keys = (v, expected) => object(v) && Object.keys(v).sort().join() === expected.sort().join(), pin = (v) => keys(v, ["path", "sha256"]) && absolute(v.path) && digest(v.sha256), safeFailureCodes;
|
|
1473
|
-
var init_native_readiness = __esm(() => {
|
|
1474
|
-
init_readiness_process();
|
|
1475
|
-
ReadinessError = class ReadinessError extends Error {
|
|
1476
|
-
code;
|
|
1477
|
-
constructor(code) {
|
|
1478
|
-
super(code);
|
|
1479
|
-
this.code = code;
|
|
1480
|
-
}
|
|
1481
|
-
};
|
|
1482
|
-
safeFailureCodes = new Set([
|
|
1483
|
-
"unsupported_platform",
|
|
1484
|
-
"station_detection_failed",
|
|
1485
|
-
"station_mismatch",
|
|
1486
|
-
"hosted_unavailable",
|
|
1487
|
-
"authentication_refused",
|
|
1488
|
-
"station_setup_required",
|
|
1489
|
-
"invalid_status",
|
|
1490
|
-
"deadline_exceeded",
|
|
1491
|
-
"retention_mismatch",
|
|
1492
|
-
"managed_instructions_refused",
|
|
1493
|
-
"removal_instruction_missing",
|
|
1494
|
-
"instructions_changed",
|
|
1495
|
-
"native_config_changed",
|
|
1496
|
-
"guard_refused",
|
|
1497
|
-
"owner_refused"
|
|
1498
|
-
]);
|
|
1499
|
-
});
|
|
1500
|
-
|
|
1501
1505
|
// src/lib/native-readiness-command.ts
|
|
1502
1506
|
import { createHash as createHash3 } from "crypto";
|
|
1503
1507
|
import { constants as constants3, closeSync as closeSync3, existsSync as existsSync5, fstatSync as fstatSync3, lstatSync as lstatSync3, openSync as openSync3, readFileSync as readFileSync3, realpathSync as realpathSync4 } from "fs";
|
|
@@ -2516,7 +2520,7 @@ var init_codex_settings = __esm(() => {
|
|
|
2516
2520
|
snapshots = new WeakMap;
|
|
2517
2521
|
});
|
|
2518
2522
|
|
|
2519
|
-
//
|
|
2523
|
+
// ../../node_modules/.bun/@hasna+skills@0.9.17+a00d3064559565bb/node_modules/@hasna/skills/dist/index.js
|
|
2520
2524
|
import { existsSync as existsSync10 } from "fs";
|
|
2521
2525
|
import { homedir as homedir6 } from "os";
|
|
2522
2526
|
import { join as join10, resolve as resolve4 } from "path";
|
|
@@ -33965,7 +33969,7 @@ function registerHook(name, scope = "global", target = "claude", profile, mement
|
|
|
33965
33969
|
name,
|
|
33966
33970
|
event: "SessionStart",
|
|
33967
33971
|
matcher: "^(startup|resume|clear|fork)$",
|
|
33968
|
-
timeoutSeconds:
|
|
33972
|
+
timeoutSeconds: TRASH_READINESS_NATIVE_TIMEOUT_SECONDS,
|
|
33969
33973
|
displayName: "Trash Readiness",
|
|
33970
33974
|
description: "Read-only native startup readiness",
|
|
33971
33975
|
version: "0.1.0",
|
|
@@ -34351,6 +34355,7 @@ function uninstallHook(name, scope = "global", target = "claude") {
|
|
|
34351
34355
|
var __dirname2, HOOKS_DIR, EVENT_MAP, settingsPreimages, getInstalledHooks, JSON_SETTINGS_TARGETS;
|
|
34352
34356
|
var init_installer = __esm(() => {
|
|
34353
34357
|
init_registry();
|
|
34358
|
+
init_native_readiness();
|
|
34354
34359
|
init_resolve();
|
|
34355
34360
|
init_manifest();
|
|
34356
34361
|
init_registration();
|
|
@@ -35177,7 +35182,7 @@ function selectCodexSafety(discovery, configuration, home, cwd, featureEnabled,
|
|
|
35177
35182
|
need8(selected.length !== 0, "guard_missing");
|
|
35178
35183
|
need8(selected.length === 1, "guard_ambiguous");
|
|
35179
35184
|
const hook = selected[0], binding = readiness ? nativeReadinessCommandBinding(hook.command) : nativeSafetyCommandBinding(hook.command);
|
|
35180
|
-
need8(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 ?
|
|
35185
|
+
need8(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" && isAbsolute18(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");
|
|
35181
35186
|
need8(typeof hook.enabled === "boolean" && ["trusted", "untrusted", "modified"].includes(hook.trustStatus), "guard_definition_invalid");
|
|
35182
35187
|
if (requireReady) {
|
|
35183
35188
|
need8(hook.enabled === true, "guard_disabled");
|
|
@@ -35248,6 +35253,7 @@ var init_codex_safety_check = __esm(() => {
|
|
|
35248
35253
|
init_native_safety();
|
|
35249
35254
|
init_native_readiness_command();
|
|
35250
35255
|
init_native_safety2();
|
|
35256
|
+
init_native_readiness();
|
|
35251
35257
|
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"]);
|
|
35252
35258
|
CodexSafetyError = class CodexSafetyError extends Error {
|
|
35253
35259
|
code;
|
|
@@ -35489,7 +35495,7 @@ var init_codex_safety_trust = __esm(() => {
|
|
|
35489
35495
|
};
|
|
35490
35496
|
});
|
|
35491
35497
|
|
|
35492
|
-
//
|
|
35498
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/error.js
|
|
35493
35499
|
var require_error = __commonJS((exports) => {
|
|
35494
35500
|
class CommanderError extends Error {
|
|
35495
35501
|
constructor(exitCode, code2, message) {
|
|
@@ -35513,7 +35519,7 @@ var require_error = __commonJS((exports) => {
|
|
|
35513
35519
|
exports.InvalidArgumentError = InvalidArgumentError;
|
|
35514
35520
|
});
|
|
35515
35521
|
|
|
35516
|
-
//
|
|
35522
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/argument.js
|
|
35517
35523
|
var require_argument = __commonJS((exports) => {
|
|
35518
35524
|
var { InvalidArgumentError } = require_error();
|
|
35519
35525
|
|
|
@@ -35592,7 +35598,7 @@ var require_argument = __commonJS((exports) => {
|
|
|
35592
35598
|
exports.humanReadableArgName = humanReadableArgName;
|
|
35593
35599
|
});
|
|
35594
35600
|
|
|
35595
|
-
//
|
|
35601
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/help.js
|
|
35596
35602
|
var require_help = __commonJS((exports) => {
|
|
35597
35603
|
var { humanReadableArgName } = require_argument();
|
|
35598
35604
|
|
|
@@ -35841,7 +35847,7 @@ var require_help = __commonJS((exports) => {
|
|
|
35841
35847
|
exports.Help = Help;
|
|
35842
35848
|
});
|
|
35843
35849
|
|
|
35844
|
-
//
|
|
35850
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/option.js
|
|
35845
35851
|
var require_option = __commonJS((exports) => {
|
|
35846
35852
|
var { InvalidArgumentError } = require_error();
|
|
35847
35853
|
|
|
@@ -35992,7 +35998,7 @@ var require_option = __commonJS((exports) => {
|
|
|
35992
35998
|
exports.DualOptions = DualOptions;
|
|
35993
35999
|
});
|
|
35994
36000
|
|
|
35995
|
-
//
|
|
36001
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/suggestSimilar.js
|
|
35996
36002
|
var require_suggestSimilar = __commonJS((exports) => {
|
|
35997
36003
|
var maxDistance = 3;
|
|
35998
36004
|
function editDistance(a, b) {
|
|
@@ -36065,7 +36071,7 @@ var require_suggestSimilar = __commonJS((exports) => {
|
|
|
36065
36071
|
exports.suggestSimilar = suggestSimilar;
|
|
36066
36072
|
});
|
|
36067
36073
|
|
|
36068
|
-
//
|
|
36074
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/command.js
|
|
36069
36075
|
var require_command = __commonJS((exports) => {
|
|
36070
36076
|
var EventEmitter = __require("events").EventEmitter;
|
|
36071
36077
|
var childProcess = __require("child_process");
|
|
@@ -37308,7 +37314,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
37308
37314
|
exports.Command = Command;
|
|
37309
37315
|
});
|
|
37310
37316
|
|
|
37311
|
-
//
|
|
37317
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/index.js
|
|
37312
37318
|
var require_commander = __commonJS((exports) => {
|
|
37313
37319
|
var { Argument } = require_argument();
|
|
37314
37320
|
var { Command } = require_command();
|
|
@@ -37328,7 +37334,7 @@ var require_commander = __commonJS((exports) => {
|
|
|
37328
37334
|
exports.InvalidOptionArgumentError = InvalidArgumentError;
|
|
37329
37335
|
});
|
|
37330
37336
|
|
|
37331
|
-
//
|
|
37337
|
+
// ../../node_modules/.bun/cli-spinners@2.9.2/node_modules/cli-spinners/spinners.json
|
|
37332
37338
|
var require_spinners = __commonJS((exports, module) => {
|
|
37333
37339
|
module.exports = {
|
|
37334
37340
|
dots: {
|
|
@@ -38954,7 +38960,7 @@ var require_spinners = __commonJS((exports, module) => {
|
|
|
38954
38960
|
};
|
|
38955
38961
|
});
|
|
38956
38962
|
|
|
38957
|
-
//
|
|
38963
|
+
// ../../node_modules/.bun/cli-spinners@2.9.2/node_modules/cli-spinners/index.js
|
|
38958
38964
|
var require_cli_spinners = __commonJS((exports, module) => {
|
|
38959
38965
|
var spinners = Object.assign({}, require_spinners());
|
|
38960
38966
|
var spinnersList = Object.keys(spinners);
|
|
@@ -39138,7 +39144,7 @@ var init_profiles = __esm(() => {
|
|
|
39138
39144
|
init_app_home();
|
|
39139
39145
|
});
|
|
39140
39146
|
|
|
39141
|
-
//
|
|
39147
|
+
// ../../node_modules/.bun/@hasna+contracts@1.2.2+944b86d8024e79fa/node_modules/@hasna/contracts/dist/client/transport.js
|
|
39142
39148
|
import { isIP as isIP2 } from "net";
|
|
39143
39149
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
39144
39150
|
import { closeSync as closeSync22, fstatSync as fstatSync22, openSync as openSync22, readFileSync as readFileSync22 } from "fs";
|
|
@@ -43627,7 +43633,7 @@ async function verifyClaudeSafetyConfiguration(options = {}, dependencies = {})
|
|
|
43627
43633
|
// src/cli/index.tsx
|
|
43628
43634
|
init_codex_safety_trust();
|
|
43629
43635
|
|
|
43630
|
-
//
|
|
43636
|
+
// ../events/dist/commander.js
|
|
43631
43637
|
import { chmod, mkdir, readFile as readFile2, rename, writeFile } from "fs/promises";
|
|
43632
43638
|
import { Buffer as Buffer2 } from "buffer";
|
|
43633
43639
|
import { existsSync as existsSync22 } from "fs";
|
|
@@ -45283,7 +45289,7 @@ function replaySummary(events2, deliveries, nextCursor) {
|
|
|
45283
45289
|
// src/cli/index.tsx
|
|
45284
45290
|
import { render } from "ink";
|
|
45285
45291
|
|
|
45286
|
-
//
|
|
45292
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/esm.mjs
|
|
45287
45293
|
var import__ = __toESM(require_commander(), 1);
|
|
45288
45294
|
var {
|
|
45289
45295
|
program: program2,
|
|
@@ -45309,11 +45315,11 @@ import { fileURLToPath as fileURLToPath6 } from "url";
|
|
|
45309
45315
|
import { useState as useState7 } from "react";
|
|
45310
45316
|
import { Box as Box8, Text as Text10, useApp, useInput as useInput5 } from "ink";
|
|
45311
45317
|
|
|
45312
|
-
//
|
|
45318
|
+
// ../../node_modules/.bun/ink-select-input@6.2.0+fa875fef9f949ebd/node_modules/ink-select-input/build/Indicator.js
|
|
45313
45319
|
import React from "react";
|
|
45314
45320
|
import { Box, Text } from "ink";
|
|
45315
45321
|
|
|
45316
|
-
//
|
|
45322
|
+
// ../../node_modules/.bun/is-unicode-supported@2.1.0/node_modules/is-unicode-supported/index.js
|
|
45317
45323
|
import process3 from "process";
|
|
45318
45324
|
function isUnicodeSupported() {
|
|
45319
45325
|
const { env } = process3;
|
|
@@ -45324,7 +45330,7 @@ function isUnicodeSupported() {
|
|
|
45324
45330
|
return Boolean(env.WT_SESSION) || Boolean(env.TERMINUS_SUBLIME) || env.ConEmuTask === "{cmd::Cmder}" || TERM_PROGRAM === "Terminus-Sublime" || TERM_PROGRAM === "vscode" || TERM === "xterm-256color" || TERM === "alacritty" || TERM === "rxvt-unicode" || TERM === "rxvt-unicode-256color" || env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
|
|
45325
45331
|
}
|
|
45326
45332
|
|
|
45327
|
-
//
|
|
45333
|
+
// ../../node_modules/.bun/figures@6.1.0/node_modules/figures/index.js
|
|
45328
45334
|
var common2 = {
|
|
45329
45335
|
circleQuestionMark: "(?)",
|
|
45330
45336
|
questionMarkPrefix: "(?)",
|
|
@@ -45600,23 +45606,23 @@ var figures = shouldUseMain ? mainSymbols : fallbackSymbols;
|
|
|
45600
45606
|
var figures_default = figures;
|
|
45601
45607
|
var replacements = Object.entries(specialMainSymbols);
|
|
45602
45608
|
|
|
45603
|
-
//
|
|
45609
|
+
// ../../node_modules/.bun/ink-select-input@6.2.0+fa875fef9f949ebd/node_modules/ink-select-input/build/Indicator.js
|
|
45604
45610
|
function Indicator({ isSelected = false }) {
|
|
45605
45611
|
return React.createElement(Box, { marginRight: 1 }, isSelected ? React.createElement(Text, { color: "blue" }, figures_default.pointer) : React.createElement(Text, null, " "));
|
|
45606
45612
|
}
|
|
45607
45613
|
var Indicator_default = Indicator;
|
|
45608
|
-
//
|
|
45614
|
+
// ../../node_modules/.bun/ink-select-input@6.2.0+fa875fef9f949ebd/node_modules/ink-select-input/build/Item.js
|
|
45609
45615
|
import * as React2 from "react";
|
|
45610
45616
|
import { Text as Text2 } from "ink";
|
|
45611
45617
|
function Item({ isSelected = false, label }) {
|
|
45612
45618
|
return React2.createElement(Text2, { color: isSelected ? "blue" : undefined }, label);
|
|
45613
45619
|
}
|
|
45614
45620
|
var Item_default = Item;
|
|
45615
|
-
//
|
|
45621
|
+
// ../../node_modules/.bun/ink-select-input@6.2.0+fa875fef9f949ebd/node_modules/ink-select-input/build/SelectInput.js
|
|
45616
45622
|
import { isDeepStrictEqual } from "util";
|
|
45617
45623
|
import React3, { useState, useEffect, useRef, useCallback } from "react";
|
|
45618
45624
|
|
|
45619
|
-
//
|
|
45625
|
+
// ../../node_modules/.bun/to-rotated@1.0.0/node_modules/to-rotated/index.js
|
|
45620
45626
|
function toRotated(array3, steps) {
|
|
45621
45627
|
if (!Array.isArray(array3)) {
|
|
45622
45628
|
throw new TypeError(`Expected an array, got \`${typeof array3}\`.`);
|
|
@@ -45638,7 +45644,7 @@ function toRotated(array3, steps) {
|
|
|
45638
45644
|
];
|
|
45639
45645
|
}
|
|
45640
45646
|
|
|
45641
|
-
//
|
|
45647
|
+
// ../../node_modules/.bun/ink-select-input@6.2.0+fa875fef9f949ebd/node_modules/ink-select-input/build/SelectInput.js
|
|
45642
45648
|
import { Box as Box2, useInput } from "ink";
|
|
45643
45649
|
function SelectInput({ items = [], isFocused = true, initialIndex = 0, indicatorComponent = Indicator_default, itemComponent = Item_default, limit: customLimit, onSelect, onHighlight }) {
|
|
45644
45650
|
const hasLimit = typeof customLimit === "number" && items.length > customLimit;
|
|
@@ -45934,7 +45940,7 @@ function HookSelect({
|
|
|
45934
45940
|
import { useState as useState4, useEffect as useEffect3 } from "react";
|
|
45935
45941
|
import { Box as Box6, Text as Text7, useInput as useInput4 } from "ink";
|
|
45936
45942
|
|
|
45937
|
-
//
|
|
45943
|
+
// ../../node_modules/.bun/ink-text-input@6.0.0+fa875fef9f949ebd/node_modules/ink-text-input/build/index.js
|
|
45938
45944
|
import React5, { useState as useState3, useEffect as useEffect2 } from "react";
|
|
45939
45945
|
import { Text as Text6, useInput as useInput3 } from "ink";
|
|
45940
45946
|
import chalk from "chalk";
|
|
@@ -46112,7 +46118,7 @@ function SearchView({
|
|
|
46112
46118
|
import { useState as useState6, useEffect as useEffect5 } from "react";
|
|
46113
46119
|
import { Box as Box7, Text as Text9 } from "ink";
|
|
46114
46120
|
|
|
46115
|
-
//
|
|
46121
|
+
// ../../node_modules/.bun/ink-spinner@5.0.0+fa875fef9f949ebd/node_modules/ink-spinner/build/index.js
|
|
46116
46122
|
var import_cli_spinners = __toESM(require_cli_spinners(), 1);
|
|
46117
46123
|
import React7, { useState as useState5, useEffect as useEffect4 } from "react";
|
|
46118
46124
|
import { Text as Text8 } from "ink";
|