@hasna/hooks 0.11.7 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +128 -3
- package/bin/hooks-mcp.js +39 -7
- package/bin/index.js +736 -127
- package/bin/native-safety-entry.js +15 -1718
- package/bin/serve.js +36 -9
- package/dist/index.js +49 -16
- package/dist/lib/native-safety-registration.d.ts +49 -6
- package/dist/lib/registry.d.ts +15 -0
- package/dist/native-safety.d.ts +4 -0
- package/dist/native-safety.js +8 -4
- package/dist/sdk/index.js +1 -1
- package/hooks/hook-signed-link-guard/README.md +228 -0
- package/hooks/hook-signed-link-guard/package.json +12 -0
- package/hooks/hook-signed-link-guard/src/classify.ts +1280 -0
- package/hooks/hook-signed-link-guard/src/hook.ts +72 -0
- package/hooks/hook-signed-link-guard/src/jq.ts +545 -0
- package/hooks/hook-signed-link-guard/src/links.ts +136 -0
- package/hooks/hook-signed-link-output/README.md +47 -0
- package/hooks/hook-signed-link-output/package.json +12 -0
- package/hooks/hook-signed-link-output/src/hook.ts +111 -0
- package/hooks/hook-trash-guard/src/hook.ts +204 -80
- package/hooks/native-safety-entry.ts +28 -7
- package/package.json +2 -2
- package/scripts/validate-package.ts +12 -2
package/bin/index.js
CHANGED
|
@@ -165,6 +165,9 @@ var init_app_home = __esm(() => {
|
|
|
165
165
|
});
|
|
166
166
|
|
|
167
167
|
// src/lib/registry.ts
|
|
168
|
+
function matcherFor(meta, target) {
|
|
169
|
+
return meta.targetMatchers?.[target] ?? meta.matcher;
|
|
170
|
+
}
|
|
168
171
|
function getHooksByCategory(category) {
|
|
169
172
|
return HOOKS.filter((h) => h.category === category);
|
|
170
173
|
}
|
|
@@ -178,7 +181,7 @@ function searchHooks(query) {
|
|
|
178
181
|
function getHook(name) {
|
|
179
182
|
return HOOKS.find((h) => h.name === name);
|
|
180
183
|
}
|
|
181
|
-
var CATEGORIES, HOOKS;
|
|
184
|
+
var CLAUDE_TRASH_GUARD_MATCHER = "^(Bash|Monitor|apply_patch|ApplyPatch|functions\\.apply_patch)$", CATEGORIES, HOOKS;
|
|
182
185
|
var init_registry = __esm(() => {
|
|
183
186
|
init_app_home();
|
|
184
187
|
CATEGORIES = [
|
|
@@ -243,6 +246,8 @@ var init_registry = __esm(() => {
|
|
|
243
246
|
category: "Git Safety",
|
|
244
247
|
event: "PreToolUse",
|
|
245
248
|
matcher: "^(Bash|Write|Edit|MultiEdit|NotebookEdit|apply_patch|ApplyPatch|functions\\.apply_patch)$",
|
|
249
|
+
targetMatchers: { claude: "^(Bash|Monitor|Write|Edit|MultiEdit|NotebookEdit|apply_patch|ApplyPatch|functions\\.apply_patch)$" },
|
|
250
|
+
legacyMatchers: ["^(Bash|Write|Edit|MultiEdit|NotebookEdit|apply_patch|ApplyPatch|functions\\.apply_patch)$"],
|
|
246
251
|
tags: ["workspace", "repos", "structure", "guard", "safety", "orgs", "multi-agent"]
|
|
247
252
|
},
|
|
248
253
|
{
|
|
@@ -253,6 +258,8 @@ var init_registry = __esm(() => {
|
|
|
253
258
|
category: "Git Safety",
|
|
254
259
|
event: "PreToolUse",
|
|
255
260
|
matcher: "^(Bash|apply_patch|ApplyPatch|functions\\.apply_patch)$",
|
|
261
|
+
targetMatchers: { claude: CLAUDE_TRASH_GUARD_MATCHER },
|
|
262
|
+
legacyMatchers: ["^(Bash|apply_patch|ApplyPatch|functions\\.apply_patch)$"],
|
|
256
263
|
tags: ["rm", "delete", "trash", "recoverable", "guard", "safety", "bash"],
|
|
257
264
|
rewritesInput: true,
|
|
258
265
|
timeoutSeconds: 10
|
|
@@ -357,6 +364,28 @@ var init_registry = __esm(() => {
|
|
|
357
364
|
matcher: "Bash",
|
|
358
365
|
tags: ["security", "credentials", "secrets", "detection", "audit"]
|
|
359
366
|
},
|
|
367
|
+
{
|
|
368
|
+
name: "signed-link-guard",
|
|
369
|
+
displayName: "Signed Link Guard",
|
|
370
|
+
description: "Refuses gh reads that would print composite GitHub content (bodies, comments, reviews, commit messages, check links and details, raw issue/PR/check API objects) where signed action links appear; bounded scalar projections, diffs, lists and writes pass",
|
|
371
|
+
version: "0.1.0",
|
|
372
|
+
category: "Security",
|
|
373
|
+
event: "PreToolUse",
|
|
374
|
+
matcher: "^(Bash|Monitor)$",
|
|
375
|
+
tags: ["github", "gh", "signed-links", "capabilities", "transcript", "guard", "security"],
|
|
376
|
+
timeoutSeconds: 10
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
name: "signed-link-output",
|
|
380
|
+
displayName: "Signed Link Output",
|
|
381
|
+
description: "Claude PostToolUse backstop: replaces signed-link URLs in tool output with a redaction marker (updatedToolOutput, where the running Claude Code supports it) and tells the model not to repeat or store the link. It runs after the tool, so it cannot undo display or other persistence",
|
|
382
|
+
version: "0.1.0",
|
|
383
|
+
category: "Security",
|
|
384
|
+
event: "PostToolUse",
|
|
385
|
+
matcher: "^(Bash|WebFetch|mcp__.*)$",
|
|
386
|
+
tags: ["github", "signed-links", "capabilities", "redaction", "transcript", "security"],
|
|
387
|
+
timeoutSeconds: 10
|
|
388
|
+
},
|
|
360
389
|
{
|
|
361
390
|
name: "phonenotify",
|
|
362
391
|
displayName: "Phone Notify",
|
|
@@ -2520,7 +2549,7 @@ var init_codex_settings = __esm(() => {
|
|
|
2520
2549
|
snapshots = new WeakMap;
|
|
2521
2550
|
});
|
|
2522
2551
|
|
|
2523
|
-
//
|
|
2552
|
+
// ../../node_modules/.bun/@hasna+skills@0.9.17+a00d3064559565bb/node_modules/@hasna/skills/dist/index.js
|
|
2524
2553
|
import { existsSync as existsSync10 } from "fs";
|
|
2525
2554
|
import { homedir as homedir6 } from "os";
|
|
2526
2555
|
import { join as join10, resolve as resolve4 } from "path";
|
|
@@ -33989,6 +34018,7 @@ function registerHook(name, scope = "global", target = "claude", profile, mement
|
|
|
33989
34018
|
if (uniqueEventKeys.length === 0) {
|
|
33990
34019
|
throw new Error(`Hook '${name}' has no installable events for target '${target}'`);
|
|
33991
34020
|
}
|
|
34021
|
+
const matcher = matcherFor(meta3, target);
|
|
33992
34022
|
const settings = readSettings2(scope, target);
|
|
33993
34023
|
if (!settings.hooks)
|
|
33994
34024
|
settings.hooks = {};
|
|
@@ -34023,10 +34053,12 @@ function registerHook(name, scope = "global", target = "claude", profile, mement
|
|
|
34023
34053
|
}
|
|
34024
34054
|
}
|
|
34025
34055
|
if (positions.length) {
|
|
34026
|
-
if (positions.length !== uniqueEventKeys.length || uniqueEventKeys.some((event) => positions.filter((position) => position.event === event).length !== 1) || positions.some((position) => (position.entry.matcher ?? "") !== (meta3.matcher ?? ""))) {
|
|
34056
|
+
if (positions.length !== uniqueEventKeys.length || uniqueEventKeys.some((event) => positions.filter((position) => position.event === event).length !== 1) || positions.some((position) => (position.entry.matcher ?? "") !== matcher && !(meta3.legacyMatchers ?? []).includes(position.entry.matcher ?? ""))) {
|
|
34027
34057
|
throw new Error("Codex hook events, matchers or duplicate registrations require explicit reconciliation; no settings were changed.");
|
|
34028
34058
|
}
|
|
34029
34059
|
for (const { entry, index } of positions) {
|
|
34060
|
+
if (matcher)
|
|
34061
|
+
entry.matcher = matcher;
|
|
34030
34062
|
entry.hooks[index] = { ...entry.hooks[index], type: "command", command: hookCommand };
|
|
34031
34063
|
if (typeof meta3.timeoutSeconds === "number" && meta3.timeoutSeconds > 0)
|
|
34032
34064
|
entry.hooks[index].timeout = meta3.timeoutSeconds;
|
|
@@ -34046,8 +34078,8 @@ function registerHook(name, scope = "global", target = "claude", profile, mement
|
|
|
34046
34078
|
const entry = {
|
|
34047
34079
|
hooks: [hookEntry]
|
|
34048
34080
|
};
|
|
34049
|
-
if (
|
|
34050
|
-
entry.matcher =
|
|
34081
|
+
if (matcher) {
|
|
34082
|
+
entry.matcher = matcher;
|
|
34051
34083
|
}
|
|
34052
34084
|
settings.hooks[eventKey].push(entry);
|
|
34053
34085
|
}
|
|
@@ -34466,11 +34498,16 @@ function syncDirectory(path) {
|
|
|
34466
34498
|
closeSync6(fd);
|
|
34467
34499
|
}
|
|
34468
34500
|
}
|
|
34501
|
+
function nativeSafetyRecordText(command) {
|
|
34502
|
+
const record3 = { schema: "hasna.hooks.native-safety.v1", harness: "sumi", command };
|
|
34503
|
+
return JSON.stringify(record3) + `
|
|
34504
|
+
`;
|
|
34505
|
+
}
|
|
34469
34506
|
function readNativeSafetyRegistration(home = homedir8()) {
|
|
34470
34507
|
try {
|
|
34471
34508
|
home = canonicalHome(home);
|
|
34472
34509
|
const path = join12(home, ".hasna/hooks/native", filename), text3 = readOwned(path);
|
|
34473
|
-
return { record: parseRecord(text3, home), sha256: hash(text3), path };
|
|
34510
|
+
return { record: parseRecord(text3, home), sha256: hash(text3), path, text: text3 };
|
|
34474
34511
|
} catch (error61) {
|
|
34475
34512
|
if (error61 instanceof NativeSafetyError)
|
|
34476
34513
|
throw error61;
|
|
@@ -34496,9 +34533,7 @@ function nativeSafetyBindingIntegrityReason(home = homedir8()) {
|
|
|
34496
34533
|
}
|
|
34497
34534
|
function registerNativeSafety(options = {}) {
|
|
34498
34535
|
const home = canonicalHome(options.home ?? homedir8());
|
|
34499
|
-
const
|
|
34500
|
-
const text3 = JSON.stringify(record3) + `
|
|
34501
|
-
`;
|
|
34536
|
+
const text3 = nativeSafetyRecordText(options.command ?? installedNativeSafetyCommand("trash-guard"));
|
|
34502
34537
|
parseRecord(text3, home);
|
|
34503
34538
|
for (const dir of [join12(home, ".hasna"), join12(home, ".hasna/hooks"), join12(home, ".hasna/hooks/native")]) {
|
|
34504
34539
|
try {
|
|
@@ -34954,13 +34989,115 @@ async function applyRegistration(options, dependencies = {}) {
|
|
|
34954
34989
|
throw new NativeRegistrationError("apply_outcome_uncertain", operationId);
|
|
34955
34990
|
}
|
|
34956
34991
|
}
|
|
34992
|
+
function inspectSumi(dependencies) {
|
|
34993
|
+
let home, command;
|
|
34994
|
+
try {
|
|
34995
|
+
home = realpathSync7(dependencies.home ?? homedir9());
|
|
34996
|
+
} catch {
|
|
34997
|
+
throw new NativeRegistrationError("settings_unsafe");
|
|
34998
|
+
}
|
|
34999
|
+
try {
|
|
35000
|
+
command = dependencies.command ?? installedNativeSafetyCommand("trash-guard");
|
|
35001
|
+
} catch {
|
|
35002
|
+
throw new NativeRegistrationError("guard_failed");
|
|
35003
|
+
}
|
|
35004
|
+
const binding = nativeSafetyCommandBinding(command);
|
|
35005
|
+
need5(binding?.name === "trash-guard" && binding.home === home, "input_invalid");
|
|
35006
|
+
const directory2 = join13(home, ".hasna", "hooks", "native"), path = join13(directory2, "sumi-trash-guard.json");
|
|
35007
|
+
let existing = directory2;
|
|
35008
|
+
while (!lstatSync9(existing, { throwIfNoEntry: false }))
|
|
35009
|
+
existing = dirname11(existing);
|
|
35010
|
+
safeDirectory2(existing);
|
|
35011
|
+
let before;
|
|
35012
|
+
try {
|
|
35013
|
+
before = readNativeSafetyRegistration(home);
|
|
35014
|
+
} catch (error61) {
|
|
35015
|
+
if (!(error61 instanceof NativeSafetyError) || error61.code !== "binding_missing")
|
|
35016
|
+
throw new NativeRegistrationError(nativeSafetyBindingIntegrityReason(home) ? "settings_unsafe" : "settings_invalid");
|
|
35017
|
+
}
|
|
35018
|
+
const beforeSHA256 = before?.sha256 ?? "absent", desiredSHA256 = sha4(nativeSafetyRecordText(command));
|
|
35019
|
+
const base = {
|
|
35020
|
+
schema: "hasna.hooks.native-readiness-registration/v1",
|
|
35021
|
+
target: "sumi",
|
|
35022
|
+
settingsPath: path,
|
|
35023
|
+
resolvedPath: path,
|
|
35024
|
+
beforeSHA256,
|
|
35025
|
+
desiredSHA256,
|
|
35026
|
+
commandSHA256: sha4(command),
|
|
35027
|
+
action: beforeSHA256 === desiredSHA256 ? "unchanged" : "register",
|
|
35028
|
+
guardVerified: true,
|
|
35029
|
+
nativeAdoptionVerified: false
|
|
35030
|
+
};
|
|
35031
|
+
const plan = { ...base, planDigest: sha4(canonical4(base)) };
|
|
35032
|
+
return { home, command, before, plan };
|
|
35033
|
+
}
|
|
35034
|
+
async function planSumiRegistration(dependencies) {
|
|
35035
|
+
const before = inspectSumi(dependencies);
|
|
35036
|
+
try {
|
|
35037
|
+
await (dependencies.verify ?? verifyNativeSafetyCommand)(before.command, before.home);
|
|
35038
|
+
} catch {
|
|
35039
|
+
throw new NativeRegistrationError("guard_failed");
|
|
35040
|
+
}
|
|
35041
|
+
need5(inspectSumi(dependencies).plan.planDigest === before.plan.planDigest, "settings_changed");
|
|
35042
|
+
return before.plan;
|
|
35043
|
+
}
|
|
35044
|
+
async function applySumiRegistration(expectedPlanDigest, dependencies) {
|
|
35045
|
+
need5(typeof expectedPlanDigest === "string" && /^[a-f0-9]{64}$/.test(expectedPlanDigest), "input_invalid");
|
|
35046
|
+
const plan = await planSumiRegistration(dependencies);
|
|
35047
|
+
need5(plan.planDigest === expectedPlanDigest, "settings_changed");
|
|
35048
|
+
if (plan.action === "unchanged")
|
|
35049
|
+
return { ok: true, changed: false, ...plan };
|
|
35050
|
+
const current2 = inspectSumi(dependencies);
|
|
35051
|
+
need5(current2.plan.planDigest === plan.planDigest, "settings_changed");
|
|
35052
|
+
const operationId = randomUUID5();
|
|
35053
|
+
let directory2 = current2.home;
|
|
35054
|
+
for (const part of [".hasna", "hooks", "native", "trash-readiness-registration", operationId]) {
|
|
35055
|
+
directory2 = join13(directory2, part);
|
|
35056
|
+
try {
|
|
35057
|
+
mkdirSync11(directory2, { mode: 448 });
|
|
35058
|
+
syncDirectory2(dirname11(directory2));
|
|
35059
|
+
} catch (error61) {
|
|
35060
|
+
if (error61.code !== "EEXIST")
|
|
35061
|
+
throw new NativeRegistrationError("settings_unsafe");
|
|
35062
|
+
}
|
|
35063
|
+
safeDirectory2(directory2);
|
|
35064
|
+
}
|
|
35065
|
+
if (current2.before)
|
|
35066
|
+
exclusive(join13(directory2, "settings.before"), current2.before.text);
|
|
35067
|
+
exclusive(join13(directory2, "intent.json"), JSON.stringify({ operationId, plan }) + `
|
|
35068
|
+
`);
|
|
35069
|
+
try {
|
|
35070
|
+
need5(inspectSumi(dependencies).plan.planDigest === plan.planDigest, "settings_changed");
|
|
35071
|
+
const registered = registerNativeSafety({ home: current2.home, command: current2.command, expectedSHA256: plan.beforeSHA256 });
|
|
35072
|
+
const after = readNativeSafetyRegistration(current2.home);
|
|
35073
|
+
need5(registered.changed && registered.sha256 === plan.desiredSHA256 && after.sha256 === plan.desiredSHA256 && after.record.command === current2.command, "apply_outcome_uncertain");
|
|
35074
|
+
await (dependencies.verify ?? verifyNativeSafetyCommand)(after.record.command, current2.home);
|
|
35075
|
+
need5(readNativeSafetyRegistration(current2.home).sha256 === plan.desiredSHA256, "apply_outcome_uncertain");
|
|
35076
|
+
const receipt = {
|
|
35077
|
+
ok: true,
|
|
35078
|
+
changed: true,
|
|
35079
|
+
operationId,
|
|
35080
|
+
planDigest: plan.planDigest,
|
|
35081
|
+
settingsSHA256: after.sha256,
|
|
35082
|
+
guardVerified: true,
|
|
35083
|
+
...current2.before ? { backup: plan.settingsPath + ".before-" + plan.beforeSHA256 } : {},
|
|
35084
|
+
nativeAdoptionVerified: false
|
|
35085
|
+
};
|
|
35086
|
+
exclusive(join13(directory2, "receipt.json"), JSON.stringify(receipt) + `
|
|
35087
|
+
`);
|
|
35088
|
+
return receipt;
|
|
35089
|
+
} catch {
|
|
35090
|
+
throw new NativeRegistrationError("apply_outcome_uncertain", operationId);
|
|
35091
|
+
}
|
|
35092
|
+
}
|
|
34957
35093
|
var NativeRegistrationError, need5 = (value, code2) => {
|
|
34958
35094
|
if (!value)
|
|
34959
35095
|
throw new NativeRegistrationError(code2);
|
|
34960
|
-
}, sha4 = (value) => createHash17("sha256").update(value).digest("hex"), planNativeSafetyRegistration = (options, dependencies = {}) => planRegistration({ target: options.target }, dependencies), applyNativeSafetyRegistration = (options, dependencies = {}) => applyRegistration({ target: options.target, expectedPlanDigest: options.expectedPlanDigest }, dependencies), planNativeReadinessRegistration = (options) => planRegistration({ target: options
|
|
35096
|
+
}, sha4 = (value) => createHash17("sha256").update(value).digest("hex"), planNativeSafetyRegistration = (options, dependencies = {}) => planRegistration({ target: options.target }, dependencies), applyNativeSafetyRegistration = (options, dependencies = {}) => applyRegistration({ target: options.target, expectedPlanDigest: options.expectedPlanDigest }, dependencies), planNativeReadinessRegistration = (options, sumiDependencies = {}) => options?.target === "sumi" ? planSumiRegistration(sumiDependencies) : planRegistration({ target: options?.target, readiness: true }), applyNativeReadinessRegistration = (options, sumiDependencies = {}) => options?.target === "sumi" ? applySumiRegistration(options.expectedPlanDigest, sumiDependencies) : applyRegistration({ target: options?.target, expectedPlanDigest: options?.expectedPlanDigest, readiness: true });
|
|
34961
35097
|
var init_native_safety_registration = __esm(() => {
|
|
34962
35098
|
init_installer();
|
|
34963
35099
|
init_native_safety2();
|
|
35100
|
+
init_native_safety();
|
|
34964
35101
|
init_native_readiness_command();
|
|
34965
35102
|
init_registration();
|
|
34966
35103
|
NativeRegistrationError = class NativeRegistrationError extends Error {
|
|
@@ -35495,7 +35632,7 @@ var init_codex_safety_trust = __esm(() => {
|
|
|
35495
35632
|
};
|
|
35496
35633
|
});
|
|
35497
35634
|
|
|
35498
|
-
//
|
|
35635
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/error.js
|
|
35499
35636
|
var require_error = __commonJS((exports) => {
|
|
35500
35637
|
class CommanderError extends Error {
|
|
35501
35638
|
constructor(exitCode, code2, message) {
|
|
@@ -35519,7 +35656,7 @@ var require_error = __commonJS((exports) => {
|
|
|
35519
35656
|
exports.InvalidArgumentError = InvalidArgumentError;
|
|
35520
35657
|
});
|
|
35521
35658
|
|
|
35522
|
-
//
|
|
35659
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/argument.js
|
|
35523
35660
|
var require_argument = __commonJS((exports) => {
|
|
35524
35661
|
var { InvalidArgumentError } = require_error();
|
|
35525
35662
|
|
|
@@ -35598,7 +35735,7 @@ var require_argument = __commonJS((exports) => {
|
|
|
35598
35735
|
exports.humanReadableArgName = humanReadableArgName;
|
|
35599
35736
|
});
|
|
35600
35737
|
|
|
35601
|
-
//
|
|
35738
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/help.js
|
|
35602
35739
|
var require_help = __commonJS((exports) => {
|
|
35603
35740
|
var { humanReadableArgName } = require_argument();
|
|
35604
35741
|
|
|
@@ -35847,7 +35984,7 @@ var require_help = __commonJS((exports) => {
|
|
|
35847
35984
|
exports.Help = Help;
|
|
35848
35985
|
});
|
|
35849
35986
|
|
|
35850
|
-
//
|
|
35987
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/option.js
|
|
35851
35988
|
var require_option = __commonJS((exports) => {
|
|
35852
35989
|
var { InvalidArgumentError } = require_error();
|
|
35853
35990
|
|
|
@@ -35998,7 +36135,7 @@ var require_option = __commonJS((exports) => {
|
|
|
35998
36135
|
exports.DualOptions = DualOptions;
|
|
35999
36136
|
});
|
|
36000
36137
|
|
|
36001
|
-
//
|
|
36138
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/suggestSimilar.js
|
|
36002
36139
|
var require_suggestSimilar = __commonJS((exports) => {
|
|
36003
36140
|
var maxDistance = 3;
|
|
36004
36141
|
function editDistance(a, b) {
|
|
@@ -36071,7 +36208,7 @@ var require_suggestSimilar = __commonJS((exports) => {
|
|
|
36071
36208
|
exports.suggestSimilar = suggestSimilar;
|
|
36072
36209
|
});
|
|
36073
36210
|
|
|
36074
|
-
//
|
|
36211
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/command.js
|
|
36075
36212
|
var require_command = __commonJS((exports) => {
|
|
36076
36213
|
var EventEmitter = __require("events").EventEmitter;
|
|
36077
36214
|
var childProcess = __require("child_process");
|
|
@@ -37314,7 +37451,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
37314
37451
|
exports.Command = Command;
|
|
37315
37452
|
});
|
|
37316
37453
|
|
|
37317
|
-
//
|
|
37454
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/index.js
|
|
37318
37455
|
var require_commander = __commonJS((exports) => {
|
|
37319
37456
|
var { Argument } = require_argument();
|
|
37320
37457
|
var { Command } = require_command();
|
|
@@ -37334,7 +37471,7 @@ var require_commander = __commonJS((exports) => {
|
|
|
37334
37471
|
exports.InvalidOptionArgumentError = InvalidArgumentError;
|
|
37335
37472
|
});
|
|
37336
37473
|
|
|
37337
|
-
//
|
|
37474
|
+
// ../../node_modules/.bun/cli-spinners@2.9.2/node_modules/cli-spinners/spinners.json
|
|
37338
37475
|
var require_spinners = __commonJS((exports, module) => {
|
|
37339
37476
|
module.exports = {
|
|
37340
37477
|
dots: {
|
|
@@ -38960,7 +39097,7 @@ var require_spinners = __commonJS((exports, module) => {
|
|
|
38960
39097
|
};
|
|
38961
39098
|
});
|
|
38962
39099
|
|
|
38963
|
-
//
|
|
39100
|
+
// ../../node_modules/.bun/cli-spinners@2.9.2/node_modules/cli-spinners/index.js
|
|
38964
39101
|
var require_cli_spinners = __commonJS((exports, module) => {
|
|
38965
39102
|
var spinners = Object.assign({}, require_spinners());
|
|
38966
39103
|
var spinnersList = Object.keys(spinners);
|
|
@@ -39041,10 +39178,10 @@ var init_redact = __esm(() => {
|
|
|
39041
39178
|
|
|
39042
39179
|
// src/lib/profiles.ts
|
|
39043
39180
|
import { existsSync as existsSync14, mkdirSync as mkdirSync13, readFileSync as readFileSync19, writeFileSync as writeFileSync12, readdirSync as readdirSync8, rmSync, cpSync as cpSync2 } from "fs";
|
|
39044
|
-
import { join as
|
|
39181
|
+
import { join as join29 } from "path";
|
|
39045
39182
|
function getProfilesDir() {
|
|
39046
|
-
const newDir =
|
|
39047
|
-
const oldDir =
|
|
39183
|
+
const newDir = join29(getEffectiveDataRoot(), "profiles");
|
|
39184
|
+
const oldDir = join29(getHomeDir(), ".hooks", "profiles");
|
|
39048
39185
|
if (!existsSync14(newDir) && existsSync14(oldDir)) {
|
|
39049
39186
|
mkdirSync13(newDir, { recursive: true });
|
|
39050
39187
|
cpSync2(oldDir, newDir, { recursive: true });
|
|
@@ -39058,7 +39195,7 @@ function ensureProfilesDir() {
|
|
|
39058
39195
|
}
|
|
39059
39196
|
}
|
|
39060
39197
|
function profilePath(id) {
|
|
39061
|
-
return
|
|
39198
|
+
return join29(getProfilesDir(), `${id}.json`);
|
|
39062
39199
|
}
|
|
39063
39200
|
function shortUuid() {
|
|
39064
39201
|
return crypto.randomUUID().slice(0, 8);
|
|
@@ -39100,7 +39237,7 @@ function listProfiles() {
|
|
|
39100
39237
|
const profiles = [];
|
|
39101
39238
|
for (const file2 of files) {
|
|
39102
39239
|
try {
|
|
39103
|
-
const content = readFileSync19(
|
|
39240
|
+
const content = readFileSync19(join29(dir, file2), "utf-8");
|
|
39104
39241
|
profiles.push(JSON.parse(content));
|
|
39105
39242
|
} catch {}
|
|
39106
39243
|
}
|
|
@@ -39144,18 +39281,18 @@ var init_profiles = __esm(() => {
|
|
|
39144
39281
|
init_app_home();
|
|
39145
39282
|
});
|
|
39146
39283
|
|
|
39147
|
-
//
|
|
39148
|
-
import { isIP } from "net";
|
|
39284
|
+
// ../../node_modules/.bun/@hasna+contracts@1.2.2+944b86d8024e79fa/node_modules/@hasna/contracts/dist/client/transport.js
|
|
39285
|
+
import { isIP as isIP2 } from "net";
|
|
39149
39286
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
39150
39287
|
import { closeSync as closeSync22, fstatSync as fstatSync22, openSync as openSync22, readFileSync as readFileSync22 } from "fs";
|
|
39151
39288
|
import { O_NOFOLLOW as O_NOFOLLOW2, O_NONBLOCK as O_NONBLOCK2, O_RDONLY as O_RDONLY2 } from "constants";
|
|
39152
39289
|
import { hostname as osHostname } from "os";
|
|
39153
39290
|
import { isAbsolute as isAbsolute21, join as join28 } from "path";
|
|
39154
|
-
import { createRequire as
|
|
39291
|
+
import { createRequire as createRequire3 } from "module";
|
|
39155
39292
|
import { fileURLToPath as fileURLToPath22 } from "url";
|
|
39156
39293
|
import { closeSync as closeSync20, fstatSync as fstatSync19, lstatSync as lstatSync24, openSync as openSync20, readFileSync as readFileSync20, realpathSync as realpathSync12, statSync as statSync2 } from "fs";
|
|
39157
39294
|
import { O_NOFOLLOW, O_NONBLOCK, O_RDONLY } from "constants";
|
|
39158
|
-
import { basename as basename9, dirname as dirname22, join as
|
|
39295
|
+
import { basename as basename9, dirname as dirname22, join as join30, relative as relative5, sep as sep6 } from "path";
|
|
39159
39296
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "url";
|
|
39160
39297
|
function envToken2(name) {
|
|
39161
39298
|
return name.toUpperCase().replace(/-/g, "_");
|
|
@@ -39230,11 +39367,11 @@ function o(e2, r2, t2) {
|
|
|
39230
39367
|
}
|
|
39231
39368
|
function entryPoint(directory2) {
|
|
39232
39369
|
const root = realpathSync12(directory2);
|
|
39233
|
-
const fd = openSync20(
|
|
39370
|
+
const fd = openSync20(join30(root, "package.json"), O_RDONLY | O_NOFOLLOW | O_NONBLOCK);
|
|
39234
39371
|
let pkg;
|
|
39235
39372
|
try {
|
|
39236
39373
|
const stat2 = fstatSync19(fd);
|
|
39237
|
-
if (!stat2.isFile() || stat2.size >
|
|
39374
|
+
if (!stat2.isFile() || stat2.size > MAX_PACKAGE_BYTES3)
|
|
39238
39375
|
throw new Error("Invalid Secrets package metadata");
|
|
39239
39376
|
pkg = JSON.parse(readFileSync20(fd, "utf8"));
|
|
39240
39377
|
} finally {
|
|
@@ -39250,7 +39387,7 @@ function entryPoint(directory2) {
|
|
|
39250
39387
|
if (/[\\%?#\0]/.test(target) || parts.some((part) => !part || part === "." || part === ".." || part === "node_modules")) {
|
|
39251
39388
|
throw new Error("Invalid Secrets import export");
|
|
39252
39389
|
}
|
|
39253
|
-
const file2 = realpathSync12(
|
|
39390
|
+
const file2 = realpathSync12(join30(root, ...parts));
|
|
39254
39391
|
const within = relative5(root, file2);
|
|
39255
39392
|
if (!within || within === ".." || within.startsWith(`..${sep6}`) || !statSync2(file2).isFile()) {
|
|
39256
39393
|
throw new Error("Secrets import export escapes its package or is not a file");
|
|
@@ -39261,7 +39398,7 @@ function resolveInstalledSecrets(parentUrl) {
|
|
|
39261
39398
|
let directory2 = dirname22(fileURLToPath3(parentUrl));
|
|
39262
39399
|
for (;; ) {
|
|
39263
39400
|
if (basename9(directory2) !== "node_modules") {
|
|
39264
|
-
const candidate =
|
|
39401
|
+
const candidate = join30(directory2, "node_modules", PACKAGE);
|
|
39265
39402
|
let absent = false;
|
|
39266
39403
|
try {
|
|
39267
39404
|
lstatSync24(candidate);
|
|
@@ -39381,7 +39518,7 @@ function readAppConfigFile(path) {
|
|
|
39381
39518
|
const uid2 = process.getuid?.() ?? process.geteuid?.();
|
|
39382
39519
|
if (uid2 !== undefined && before.uid !== uid2)
|
|
39383
39520
|
unsafe("the file is not owned by the current user");
|
|
39384
|
-
if (before.size >
|
|
39521
|
+
if (before.size > MAX_CREDENTIAL_FILE_BYTES3)
|
|
39385
39522
|
unsafe("the file exceeds the size limit");
|
|
39386
39523
|
const bytes = readFileSync22(fd);
|
|
39387
39524
|
const after = fstatSync22(fd);
|
|
@@ -39473,13 +39610,13 @@ function sealCredential(fields) {
|
|
|
39473
39610
|
configurable: false
|
|
39474
39611
|
});
|
|
39475
39612
|
}
|
|
39476
|
-
Object.defineProperty(sealed,
|
|
39613
|
+
Object.defineProperty(sealed, INSPECT_CUSTOM3, {
|
|
39477
39614
|
value: () => ({ ...visible, apiKey: "[redacted]" }),
|
|
39478
39615
|
enumerable: false,
|
|
39479
39616
|
writable: false,
|
|
39480
39617
|
configurable: false
|
|
39481
39618
|
});
|
|
39482
|
-
Object.defineProperty(sealed,
|
|
39619
|
+
Object.defineProperty(sealed, CREDENTIAL_SEAL3, {
|
|
39483
39620
|
value: true,
|
|
39484
39621
|
enumerable: false,
|
|
39485
39622
|
writable: false,
|
|
@@ -39510,7 +39647,7 @@ function firstEnvValue(env, keys2) {
|
|
|
39510
39647
|
return null;
|
|
39511
39648
|
}
|
|
39512
39649
|
function isAmbientEnvironment(env) {
|
|
39513
|
-
return env === process.env || env[
|
|
39650
|
+
return env === process.env || env[AMBIENT_ENVIRONMENT3] === true;
|
|
39514
39651
|
}
|
|
39515
39652
|
function defaultKeychainRunner(argv) {
|
|
39516
39653
|
const result = spawnSync2(KEYCHAIN_SECURITY_BIN, [...argv], {
|
|
@@ -39604,7 +39741,7 @@ function snapshotClientEnvironment(name, env) {
|
|
|
39604
39741
|
snapshot2[key] = descriptor.value;
|
|
39605
39742
|
}
|
|
39606
39743
|
if (ambient) {
|
|
39607
|
-
Object.defineProperty(snapshot2,
|
|
39744
|
+
Object.defineProperty(snapshot2, AMBIENT_ENVIRONMENT3, {
|
|
39608
39745
|
value: true,
|
|
39609
39746
|
enumerable: false,
|
|
39610
39747
|
writable: false,
|
|
@@ -39777,7 +39914,7 @@ async function completePointerCredential(name, pointerResolution, env = process.
|
|
|
39777
39914
|
let secretsSdk;
|
|
39778
39915
|
try {
|
|
39779
39916
|
const sdkUrl = resolveInstalledSecrets(import.meta.url);
|
|
39780
|
-
secretsSdk =
|
|
39917
|
+
secretsSdk = requireSecretsSdk3(fileURLToPath22(sdkUrl));
|
|
39781
39918
|
} catch {
|
|
39782
39919
|
throw new CredentialResolutionError(name, `${pointerEnvKey} names vault item '${vaultKey}', but the secrets SDK (@hasna/secrets) is not installed ` + `in this process. A vault pointer is TERMINAL: install @hasna/secrets to resolve it, or unset ${pointerEnvKey}.`, [pointerEnvKey]);
|
|
39783
39920
|
}
|
|
@@ -39861,7 +39998,7 @@ function canonicalAuthorityHostname(authority) {
|
|
|
39861
39998
|
}
|
|
39862
39999
|
assertCanonicalPort(portSuffix.slice(1));
|
|
39863
40000
|
}
|
|
39864
|
-
if (
|
|
40001
|
+
if (isIP2(rawHostname.slice(1, -1)) !== 6) {
|
|
39865
40002
|
throw new Error("API URL authority must contain a canonical IPv6 literal.");
|
|
39866
40003
|
}
|
|
39867
40004
|
} else {
|
|
@@ -39877,7 +40014,7 @@ function canonicalAuthorityHostname(authority) {
|
|
|
39877
40014
|
} else {
|
|
39878
40015
|
rawHostname = authority;
|
|
39879
40016
|
}
|
|
39880
|
-
const ipVersion =
|
|
40017
|
+
const ipVersion = isIP2(rawHostname);
|
|
39881
40018
|
const numericAddressParts = rawHostname.split(".");
|
|
39882
40019
|
const looksLikeNonCanonicalIpv4 = numericAddressParts.every((part) => /^(?:0x[0-9a-f]+|[0-9]+)$/i.test(part));
|
|
39883
40020
|
if (ipVersion !== 4 && looksLikeNonCanonicalIpv4 || ipVersion !== 4 && !isValidDnsDomain(rawHostname.toLowerCase())) {
|
|
@@ -40013,9 +40150,9 @@ function credentialDiskSourcesForMessage(name, env) {
|
|
|
40013
40150
|
const paths = credentialDiskSources(name, env);
|
|
40014
40151
|
return paths.length > 0 ? paths.join(" or ") : "<no HOME or HASNA_HOME set in this environment, so no credential file was consulted>";
|
|
40015
40152
|
}
|
|
40016
|
-
var CREDENTIAL_PROFILE_ENV_KEY = "HASNA_PROFILE", PACKAGE = "@hasna/secrets",
|
|
40153
|
+
var CREDENTIAL_PROFILE_ENV_KEY = "HASNA_PROFILE", PACKAGE = "@hasna/secrets", MAX_PACKAGE_BYTES3, CredentialResolutionError, CredentialFileUnsafeError, HASNA_HOME_ENV_KEY = "HASNA_HOME", HASNA_CONFIG_HOME_ENV_KEY = "HASNA_CONFIG_HOME", KEYCHAIN_STATION_ENV_KEY = "HASNA_STATION", HASNA_HOME_DIR = ".hasna", CONFIG_SUBDIR = "config", CREDENTIALS_FILE = "credentials", KEYCHAIN_SECURITY_BIN = "/usr/bin/security", KEYCHAIN_SERVICE_PREFIX = "hasna.credentials", KEYCHAIN_ITEM_NOT_FOUND_STATUS = 44, KEYCHAIN_SPAWN_TIMEOUT_MS = 1e4, MAX_CREDENTIAL_FILE_BYTES3, SAFE_APP_SLUG, SAFE_PROFILE, ILLEGAL_IN_HEADER_VALUE, VAULT_POINTER_SHAPE, CREDENTIAL_SHAPED_KEY, INSPECT_CUSTOM3, CREDENTIAL_SEAL3, AMBIENT_ENVIRONMENT3, requireSecretsSdk3, DEFAULT_FLEET_GATEWAY_ORIGIN = "https://api.hasna.com", DEFAULT_AUTHORITY_SOURCE = "default", ASCII_CONTROL_PATTERN, DNS_LABEL_PATTERN, ClientTransportConfigurationError, IDEMPOTENT_METHODS3, AUTHORITY_OVERRIDE_HEADERS3;
|
|
40017
40154
|
var init_transport = __esm(() => {
|
|
40018
|
-
|
|
40155
|
+
MAX_PACKAGE_BYTES3 = 1024 * 1024;
|
|
40019
40156
|
CredentialResolutionError = class CredentialResolutionError extends Error {
|
|
40020
40157
|
appName;
|
|
40021
40158
|
attempted;
|
|
@@ -40034,16 +40171,16 @@ var init_transport = __esm(() => {
|
|
|
40034
40171
|
this.path = path;
|
|
40035
40172
|
}
|
|
40036
40173
|
};
|
|
40037
|
-
|
|
40174
|
+
MAX_CREDENTIAL_FILE_BYTES3 = 64 * 1024;
|
|
40038
40175
|
SAFE_APP_SLUG = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
40039
40176
|
SAFE_PROFILE = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;
|
|
40040
40177
|
ILLEGAL_IN_HEADER_VALUE = /[^\t\x20-\x7e]/;
|
|
40041
40178
|
VAULT_POINTER_SHAPE = /^[a-z0-9][a-z0-9-]*(?:\/[a-z0-9][a-z0-9-_.]*){2,}$/;
|
|
40042
40179
|
CREDENTIAL_SHAPED_KEY = /(?:^|_)(?:API_KEY|KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|AUTH)(?:_|$)/;
|
|
40043
|
-
|
|
40044
|
-
|
|
40045
|
-
|
|
40046
|
-
|
|
40180
|
+
INSPECT_CUSTOM3 = Symbol.for("nodejs.util.inspect.custom");
|
|
40181
|
+
CREDENTIAL_SEAL3 = Symbol.for("hasna:contracts:sealedCredential");
|
|
40182
|
+
AMBIENT_ENVIRONMENT3 = Symbol.for("hasna:contracts:ambientClientEnvironment");
|
|
40183
|
+
requireSecretsSdk3 = createRequire3(import.meta.url);
|
|
40047
40184
|
ASCII_CONTROL_PATTERN = /[\u0000-\u001f\u007f]/;
|
|
40048
40185
|
DNS_LABEL_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
40049
40186
|
ClientTransportConfigurationError = class ClientTransportConfigurationError extends Error {
|
|
@@ -40056,8 +40193,8 @@ var init_transport = __esm(() => {
|
|
|
40056
40193
|
this.sources = Object.freeze([...sources]);
|
|
40057
40194
|
}
|
|
40058
40195
|
};
|
|
40059
|
-
|
|
40060
|
-
|
|
40196
|
+
IDEMPOTENT_METHODS3 = new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
|
|
40197
|
+
AUTHORITY_OVERRIDE_HEADERS3 = new Set([
|
|
40061
40198
|
"host",
|
|
40062
40199
|
":authority",
|
|
40063
40200
|
"forwarded",
|
|
@@ -40568,7 +40705,7 @@ __export(exports_run, {
|
|
|
40568
40705
|
});
|
|
40569
40706
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
40570
40707
|
import { rmSync as rmSync2, writeFileSync as writeFileSync13 } from "fs";
|
|
40571
|
-
import { basename as basename11, dirname as dirname24, isAbsolute as isAbsolute25, join as
|
|
40708
|
+
import { basename as basename11, dirname as dirname24, isAbsolute as isAbsolute25, join as join31 } from "path";
|
|
40572
40709
|
function extensionOf(scriptPath) {
|
|
40573
40710
|
const base = basename11(scriptPath);
|
|
40574
40711
|
const dot = base.lastIndexOf(".");
|
|
@@ -40639,7 +40776,7 @@ async function readPipeWithDeadline(stream, deadlineMs) {
|
|
|
40639
40776
|
async function executeVerifiedScript(options) {
|
|
40640
40777
|
const scriptDir = dirname24(options.scriptPath);
|
|
40641
40778
|
const interpreter = interpreterFor(options.name, options.scriptPath, options.content);
|
|
40642
|
-
const tempPath =
|
|
40779
|
+
const tempPath = join31(scriptDir, `.hooks-verified-${randomBytes2(12).toString("hex")}.${interpreter.tempExt}`);
|
|
40643
40780
|
let proc = null;
|
|
40644
40781
|
let timedOut = false;
|
|
40645
40782
|
const timeoutTimer = options.timeout ? setTimeout(() => {
|
|
@@ -40731,10 +40868,10 @@ __export(exports_custom_install, {
|
|
|
40731
40868
|
ensureCustomDir: () => ensureCustomDir
|
|
40732
40869
|
});
|
|
40733
40870
|
import { existsSync as existsSync16, mkdirSync as mkdirSync14, mkdtempSync, readFileSync as readFileSync21, rmSync as rmSync4 } from "fs";
|
|
40734
|
-
import { join as
|
|
40871
|
+
import { join as join32 } from "path";
|
|
40735
40872
|
import { tmpdir as tmpdir2 } from "os";
|
|
40736
40873
|
function manifestAt(dir) {
|
|
40737
|
-
const path =
|
|
40874
|
+
const path = join32(dir, "manifest.json");
|
|
40738
40875
|
if (!existsSync16(path))
|
|
40739
40876
|
return;
|
|
40740
40877
|
try {
|
|
@@ -40757,8 +40894,8 @@ function writeFromManifest(manifest, srcDir, source, kind) {
|
|
|
40757
40894
|
return { name, version: manifest.version, kind, source, dir, scriptPath };
|
|
40758
40895
|
}
|
|
40759
40896
|
async function cloneToTemp(gitUrl) {
|
|
40760
|
-
const tmp = mkdtempSync(
|
|
40761
|
-
const proc = Bun.spawn(["git", "clone", "--depth", "1", "--quiet", gitUrl,
|
|
40897
|
+
const tmp = mkdtempSync(join32(tmpdir2(), "hooks-install-"));
|
|
40898
|
+
const proc = Bun.spawn(["git", "clone", "--depth", "1", "--quiet", gitUrl, join32(tmp, "repo")], {
|
|
40762
40899
|
stdout: "pipe",
|
|
40763
40900
|
stderr: "pipe"
|
|
40764
40901
|
});
|
|
@@ -40771,7 +40908,7 @@ async function cloneToTemp(gitUrl) {
|
|
|
40771
40908
|
rmSync4(tmp, { recursive: true, force: true });
|
|
40772
40909
|
throw new Error(`git clone failed (${exitCode}): ${stderr.trim()}`);
|
|
40773
40910
|
}
|
|
40774
|
-
return
|
|
40911
|
+
return join32(tmp, "repo");
|
|
40775
40912
|
}
|
|
40776
40913
|
function isGitUrl(value) {
|
|
40777
40914
|
return value.startsWith("git@") || value.startsWith("ssh://") || value.startsWith("file://") || value.endsWith(".git");
|
|
@@ -42010,11 +42147,11 @@ __export(exports_serve, {
|
|
|
42010
42147
|
DEFAULT_SERVE_PORT: () => DEFAULT_SERVE_PORT
|
|
42011
42148
|
});
|
|
42012
42149
|
import { readFileSync as readFileSync25 } from "fs";
|
|
42013
|
-
import { dirname as dirname25, join as
|
|
42150
|
+
import { dirname as dirname25, join as join33 } from "path";
|
|
42014
42151
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
42015
42152
|
function packageVersion() {
|
|
42016
42153
|
try {
|
|
42017
|
-
const pkgPath =
|
|
42154
|
+
const pkgPath = join33(dirname25(fileURLToPath4(import.meta.url)), "..", "package.json");
|
|
42018
42155
|
return JSON.parse(readFileSync25(pkgPath, "utf-8")).version || "0.0.0";
|
|
42019
42156
|
} catch {
|
|
42020
42157
|
return "0.0.0";
|
|
@@ -42316,7 +42453,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
42316
42453
|
import { z as z3 } from "zod";
|
|
42317
42454
|
import { createServer } from "http";
|
|
42318
42455
|
import { existsSync as existsSync17, readFileSync as readFileSync26 } from "fs";
|
|
42319
|
-
import { join as
|
|
42456
|
+
import { join as join34, dirname as dirname26 } from "path";
|
|
42320
42457
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
42321
42458
|
async function runVerifiedHook(name, stdin, timeoutMs) {
|
|
42322
42459
|
const { resolveHook: resolveHook2, resolveScriptPath: resolveScriptPath2 } = await Promise.resolve().then(() => (init_resolve(), exports_resolve));
|
|
@@ -42550,7 +42687,7 @@ function createHooksServer() {
|
|
|
42550
42687
|
continue;
|
|
42551
42688
|
}
|
|
42552
42689
|
const hookDir = getHookPath(name);
|
|
42553
|
-
if (!existsSync17(
|
|
42690
|
+
if (!existsSync17(join34(hookDir, "src", "hook.ts"))) {
|
|
42554
42691
|
issues.push({ hook: name, issue: "Missing src/hook.ts in package", severity: "error" });
|
|
42555
42692
|
hookHealthy = false;
|
|
42556
42693
|
}
|
|
@@ -42558,7 +42695,7 @@ function createHooksServer() {
|
|
|
42558
42695
|
if (meta3 && settingsExist) {
|
|
42559
42696
|
try {
|
|
42560
42697
|
const settings = JSON.parse(readFileSync26(settingsPath, "utf-8"));
|
|
42561
|
-
if (!hookRegisteredInSettings(settings, name, meta3.event, meta3
|
|
42698
|
+
if (!hookRegisteredInSettings(settings, name, meta3.event, matcherFor(meta3, "claude"))) {
|
|
42562
42699
|
const eventName = meta3.event.split(":")[0];
|
|
42563
42700
|
issues.push({ hook: name, issue: `Not registered under correct event (${eventName})`, severity: "error" });
|
|
42564
42701
|
hookHealthy = false;
|
|
@@ -42594,7 +42731,7 @@ function createHooksServer() {
|
|
|
42594
42731
|
return { content: [{ type: "text", text: JSON.stringify({ error: `Hook '${name}' not found` }) }] };
|
|
42595
42732
|
}
|
|
42596
42733
|
const hookPath = getHookPath(name);
|
|
42597
|
-
const readmePath =
|
|
42734
|
+
const readmePath = join34(hookPath, "README.md");
|
|
42598
42735
|
let readme = "";
|
|
42599
42736
|
if (existsSync17(readmePath)) {
|
|
42600
42737
|
readme = readFileSync26(readmePath, "utf-8");
|
|
@@ -43324,7 +43461,7 @@ var init_server = __esm(() => {
|
|
|
43324
43461
|
pkg = { name: "@hasna/hooks", version: "0.0.0" };
|
|
43325
43462
|
try {
|
|
43326
43463
|
for (const rel of ["../../package.json", "../package.json", "../../../package.json"]) {
|
|
43327
|
-
const p =
|
|
43464
|
+
const p = join34(__dirname3, rel);
|
|
43328
43465
|
if (existsSync17(p)) {
|
|
43329
43466
|
pkg = JSON.parse(readFileSync26(p, "utf-8"));
|
|
43330
43467
|
break;
|
|
@@ -43403,7 +43540,7 @@ __export(exports_native_readiness_binding, {
|
|
|
43403
43540
|
import { createHash as createHash25, randomUUID as randomUUID8 } from "crypto";
|
|
43404
43541
|
import { closeSync as closeSync21, constants as constants19, fsyncSync as fsyncSync8, lstatSync as lstatSync25, mkdirSync as mkdirSync15, openSync as openSync21, realpathSync as realpathSync14, writeFileSync as writeFileSync14 } from "fs";
|
|
43405
43542
|
import { homedir as homedir15 } from "os";
|
|
43406
|
-
import { dirname as dirname27, join as
|
|
43543
|
+
import { dirname as dirname27, join as join35 } from "path";
|
|
43407
43544
|
function need10(value, code2) {
|
|
43408
43545
|
if (!value)
|
|
43409
43546
|
throw new ReadinessBindingError(code2);
|
|
@@ -43461,7 +43598,7 @@ async function registerTrashReadinessBinding(input2, expectedSHA256) {
|
|
|
43461
43598
|
const operationId = randomUUID8();
|
|
43462
43599
|
let directory2 = home;
|
|
43463
43600
|
for (const part of [".hasna", "hooks", "native", "trash-readiness"]) {
|
|
43464
|
-
directory2 =
|
|
43601
|
+
directory2 = join35(directory2, part);
|
|
43465
43602
|
try {
|
|
43466
43603
|
mkdirSync15(directory2, { mode: 448 });
|
|
43467
43604
|
syncDirectory4(dirname27(directory2));
|
|
@@ -43471,12 +43608,12 @@ async function registerTrashReadinessBinding(input2, expectedSHA256) {
|
|
|
43471
43608
|
}
|
|
43472
43609
|
safeDirectory4(directory2);
|
|
43473
43610
|
}
|
|
43474
|
-
const operation =
|
|
43611
|
+
const operation = join35(directory2, operationId);
|
|
43475
43612
|
mkdirSync15(operation, { mode: 448 });
|
|
43476
43613
|
syncDirectory4(directory2);
|
|
43477
43614
|
if (before)
|
|
43478
|
-
exclusive3(
|
|
43479
|
-
exclusive3(
|
|
43615
|
+
exclusive3(join35(operation, "binding.before"), before.raw);
|
|
43616
|
+
exclusive3(join35(operation, "intent.json"), JSON.stringify({ operationId, target, expectedSHA256, desiredSHA256: digest4 }) + `
|
|
43480
43617
|
`);
|
|
43481
43618
|
try {
|
|
43482
43619
|
need10((read4()?.sha256 ?? "absent") === expectedSHA256, "binding_changed");
|
|
@@ -43490,7 +43627,7 @@ async function registerTrashReadinessBinding(input2, expectedSHA256) {
|
|
|
43490
43627
|
syncDirectory4(directory2);
|
|
43491
43628
|
need10(read4()?.sha256 === digest4, "binding_changed");
|
|
43492
43629
|
const receipt = { ok: true, changed: true, operationId, path: target, sha256: digest4, configurationReady: true, nativeAdoptionVerified: false };
|
|
43493
|
-
exclusive3(
|
|
43630
|
+
exclusive3(join35(operation, "receipt.json"), JSON.stringify(receipt) + `
|
|
43494
43631
|
`);
|
|
43495
43632
|
return receipt;
|
|
43496
43633
|
} catch {
|
|
@@ -43514,11 +43651,12 @@ var init_native_readiness_binding = __esm(() => {
|
|
|
43514
43651
|
|
|
43515
43652
|
// src/cli/index.tsx
|
|
43516
43653
|
init_native_safety_registration();
|
|
43517
|
-
import { resolve as
|
|
43654
|
+
import { resolve as resolve23 } from "path";
|
|
43518
43655
|
|
|
43519
43656
|
// src/lib/claude-safety-check.ts
|
|
43520
43657
|
init_native_safety();
|
|
43521
43658
|
init_native_safety2();
|
|
43659
|
+
init_registry();
|
|
43522
43660
|
import { constants as constants15, closeSync as closeSync15, fstatSync as fstatSync7, lstatSync as lstatSync10, openSync as openSync15, readFileSync as readFileSync12, realpathSync as realpathSync8 } from "fs";
|
|
43523
43661
|
import { createHash as createHash19 } from "crypto";
|
|
43524
43662
|
import { dirname as dirname17, isAbsolute as isAbsolute17, join as join17, resolve as resolve18 } from "path";
|
|
@@ -43602,7 +43740,7 @@ async function verifyClaudeSafetyConfiguration(options = {}, dependencies = {})
|
|
|
43602
43740
|
need7(!/^(?:hooks run trash-guard|hook-trash-guard)(?:\s|$)/.test(handler.command.trim()), "guard_definition_invalid");
|
|
43603
43741
|
if (binding?.name !== "trash-guard")
|
|
43604
43742
|
continue;
|
|
43605
|
-
need7(binding.home === home && event === "PreToolUse" && handler.type === "command" && group.matcher ===
|
|
43743
|
+
need7(binding.home === home && event === "PreToolUse" && handler.type === "command" && group.matcher === CLAUDE_TRASH_GUARD_MATCHER && handler.timeout === 10 && (handler.async === undefined || handler.async === false) && handler.asyncRewake !== true, "guard_definition_invalid");
|
|
43606
43744
|
guards.push({ command: handler.command, path: file2.path });
|
|
43607
43745
|
}
|
|
43608
43746
|
}
|
|
@@ -43633,16 +43771,25 @@ async function verifyClaudeSafetyConfiguration(options = {}, dependencies = {})
|
|
|
43633
43771
|
// src/cli/index.tsx
|
|
43634
43772
|
init_codex_safety_trust();
|
|
43635
43773
|
|
|
43636
|
-
//
|
|
43774
|
+
// ../events/dist/commander.js
|
|
43637
43775
|
import { chmod, mkdir, readFile as readFile2, rename, writeFile } from "fs/promises";
|
|
43638
43776
|
import { Buffer as Buffer2 } from "buffer";
|
|
43777
|
+
import { existsSync as existsSync22 } from "fs";
|
|
43778
|
+
import { join as join22 } from "path";
|
|
43639
43779
|
import { existsSync as existsSync13 } from "fs";
|
|
43640
43780
|
import { homedir as homedir13 } from "os";
|
|
43641
|
-
import { join as join20 } from "path";
|
|
43781
|
+
import { join as join20, resolve as resolve20 } from "path";
|
|
43782
|
+
import { homedir as pathsResolverHomedir3 } from "os";
|
|
43783
|
+
import { join as pathsResolverJoin3 } from "path";
|
|
43642
43784
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
43785
|
+
import { lookup as dnsLookup } from "dns/promises";
|
|
43786
|
+
import { isIP } from "net";
|
|
43643
43787
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
43644
43788
|
import { spawn as spawn3 } from "child_process";
|
|
43789
|
+
import { request as nodeHttpRequest } from "http";
|
|
43790
|
+
import { request as nodeHttpsRequest } from "https";
|
|
43645
43791
|
import { randomUUID as randomUUID22 } from "crypto";
|
|
43792
|
+
import { createRequire as createRequire2 } from "module";
|
|
43646
43793
|
function getPathValue(input2, path) {
|
|
43647
43794
|
return path.split(".").reduce((value, part) => {
|
|
43648
43795
|
if (value && typeof value === "object" && part in value) {
|
|
@@ -43738,13 +43885,103 @@ function channelMatchesEvent(channel, event) {
|
|
|
43738
43885
|
return true;
|
|
43739
43886
|
return channel.filters.some((filter) => eventMatchesFilter(event, filter));
|
|
43740
43887
|
}
|
|
43888
|
+
var PATHS_RESOLVER_KIND_ENV2 = {
|
|
43889
|
+
config: "HASNA_CONFIG_HOME",
|
|
43890
|
+
data: "HASNA_DATA_HOME",
|
|
43891
|
+
state: "HASNA_STATE_HOME",
|
|
43892
|
+
cache: "HASNA_CACHE_HOME"
|
|
43893
|
+
};
|
|
43894
|
+
var PATHS_RESOLVER_APP_SLUG_RE3 = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
43895
|
+
function pathsResolverAssertApp3(app) {
|
|
43896
|
+
if (typeof app !== "string" || app.length === 0) {
|
|
43897
|
+
throw new TypeError("paths: app must be a non-empty string");
|
|
43898
|
+
}
|
|
43899
|
+
if (!PATHS_RESOLVER_APP_SLUG_RE3.test(app)) {
|
|
43900
|
+
throw new TypeError(`paths: invalid app slug "${app}" \u2014 expected lowercase kebab-case ([a-z0-9]+(-[a-z0-9]+)*)`);
|
|
43901
|
+
}
|
|
43902
|
+
}
|
|
43903
|
+
function pathsResolverAssertKind2(kind) {
|
|
43904
|
+
if (!Object.keys(PATHS_RESOLVER_KIND_ENV2).includes(kind)) {
|
|
43905
|
+
throw new TypeError(`paths: invalid path kind "${kind}" \u2014 expected one of ${Object.keys(PATHS_RESOLVER_KIND_ENV2).join(", ")}`);
|
|
43906
|
+
}
|
|
43907
|
+
}
|
|
43908
|
+
function pathsResolverBaseDir2(kind, options) {
|
|
43909
|
+
pathsResolverAssertKind2(kind);
|
|
43910
|
+
const env = options.env ?? process.env;
|
|
43911
|
+
const override = env[PATHS_RESOLVER_KIND_ENV2[kind]];
|
|
43912
|
+
if (typeof override === "string" && override.length > 0)
|
|
43913
|
+
return override;
|
|
43914
|
+
const home = options.home ?? pathsResolverHomedir3();
|
|
43915
|
+
const platform = options.platform ?? process.platform;
|
|
43916
|
+
if (platform === "darwin") {
|
|
43917
|
+
switch (kind) {
|
|
43918
|
+
case "config":
|
|
43919
|
+
case "data":
|
|
43920
|
+
return pathsResolverJoin3(home, "Library", "Application Support", "Hasna");
|
|
43921
|
+
case "cache":
|
|
43922
|
+
return pathsResolverJoin3(home, "Library", "Caches", "Hasna");
|
|
43923
|
+
case "state":
|
|
43924
|
+
return pathsResolverJoin3(home, "Library", "Logs", "Hasna");
|
|
43925
|
+
}
|
|
43926
|
+
}
|
|
43927
|
+
switch (kind) {
|
|
43928
|
+
case "config":
|
|
43929
|
+
return pathsResolverJoin3(home, ".config", "hasna");
|
|
43930
|
+
case "data":
|
|
43931
|
+
return pathsResolverJoin3(home, ".local", "share", "hasna");
|
|
43932
|
+
case "state":
|
|
43933
|
+
return pathsResolverJoin3(home, ".local", "state", "hasna");
|
|
43934
|
+
case "cache":
|
|
43935
|
+
return pathsResolverJoin3(home, ".cache", "hasna");
|
|
43936
|
+
}
|
|
43937
|
+
}
|
|
43938
|
+
function pathsResolverResolve2(kind, options) {
|
|
43939
|
+
pathsResolverAssertApp3(options.app);
|
|
43940
|
+
const appSegment = options.internal === true ? pathsResolverJoin3("internal", options.app) : options.app;
|
|
43941
|
+
return pathsResolverJoin3(pathsResolverBaseDir2(kind, options), appSegment);
|
|
43942
|
+
}
|
|
43943
|
+
function dataDir3(options) {
|
|
43944
|
+
return pathsResolverResolve2("data", options);
|
|
43945
|
+
}
|
|
43741
43946
|
var HASNA_EVENTS_DIR_ENV = "HASNA_EVENTS_DIR";
|
|
43742
43947
|
var HASNA_EVENTS_HOME_ENV = "HASNA_EVENTS_HOME";
|
|
43948
|
+
var EVENTS_STORE_SENTINEL_FILE = "events.json";
|
|
43949
|
+
function effectiveHome2() {
|
|
43950
|
+
return process.env["HOME"] || process.env["USERPROFILE"] || homedir13();
|
|
43951
|
+
}
|
|
43952
|
+
function legacyHomeDir() {
|
|
43953
|
+
return join20(effectiveHome2(), ".hasna", "events");
|
|
43954
|
+
}
|
|
43955
|
+
function resolverHome() {
|
|
43956
|
+
return dataDir3({ app: "events", home: effectiveHome2() || undefined });
|
|
43957
|
+
}
|
|
43958
|
+
function adoptResolverHome(resolved, env = process.env) {
|
|
43959
|
+
const dataOverride = env.HASNA_DATA_HOME;
|
|
43960
|
+
if (typeof dataOverride === "string" && dataOverride.trim().length > 0)
|
|
43961
|
+
return true;
|
|
43962
|
+
return existsSync13(join20(resolved, EVENTS_STORE_SENTINEL_FILE));
|
|
43963
|
+
}
|
|
43964
|
+
function exactEventsHome() {
|
|
43965
|
+
const dir = process.env[HASNA_EVENTS_DIR_ENV];
|
|
43966
|
+
if (dir && dir.trim())
|
|
43967
|
+
return dir.trim();
|
|
43968
|
+
const home = process.env[HASNA_EVENTS_HOME_ENV];
|
|
43969
|
+
if (home && home.trim())
|
|
43970
|
+
return home.trim();
|
|
43971
|
+
return;
|
|
43972
|
+
}
|
|
43973
|
+
function getEventsHome() {
|
|
43974
|
+
const exact2 = exactEventsHome();
|
|
43975
|
+
if (exact2)
|
|
43976
|
+
return resolve20(exact2);
|
|
43977
|
+
const resolved = resolverHome();
|
|
43978
|
+
return adoptResolverHome(resolved) ? resolve20(resolved) : resolve20(legacyHomeDir());
|
|
43979
|
+
}
|
|
43743
43980
|
var LOCAL_JSON_EVENT_CURSOR_PREFIX = "local-json-v1:";
|
|
43744
43981
|
var DEFAULT_EVENT_PAGE_LIMIT = 100;
|
|
43745
43982
|
var MAX_EVENT_PAGE_LIMIT = 1000;
|
|
43746
43983
|
function getEventsDataDir(override) {
|
|
43747
|
-
return override ||
|
|
43984
|
+
return override || getEventsHome();
|
|
43748
43985
|
}
|
|
43749
43986
|
function getActiveEventsDirEnv() {
|
|
43750
43987
|
if (process.env[HASNA_EVENTS_DIR_ENV])
|
|
@@ -43760,12 +43997,12 @@ class JsonEventsStore {
|
|
|
43760
43997
|
channelsPath;
|
|
43761
43998
|
eventsPath;
|
|
43762
43999
|
deliveriesPath;
|
|
43763
|
-
constructor(
|
|
43764
|
-
this.dataDir =
|
|
43765
|
-
this.runtime = localJsonRuntime(
|
|
43766
|
-
this.channelsPath =
|
|
43767
|
-
this.eventsPath =
|
|
43768
|
-
this.deliveriesPath =
|
|
44000
|
+
constructor(dataDir22 = getEventsDataDir()) {
|
|
44001
|
+
this.dataDir = dataDir22;
|
|
44002
|
+
this.runtime = localJsonRuntime(dataDir22);
|
|
44003
|
+
this.channelsPath = join22(dataDir22, "channels.json");
|
|
44004
|
+
this.eventsPath = join22(dataDir22, "events.json");
|
|
44005
|
+
this.deliveriesPath = join22(dataDir22, "deliveries.json");
|
|
43769
44006
|
}
|
|
43770
44007
|
async init() {
|
|
43771
44008
|
await mkdir(this.dataDir, { recursive: true, mode: 448 });
|
|
@@ -43882,7 +44119,7 @@ class JsonEventsStore {
|
|
|
43882
44119
|
};
|
|
43883
44120
|
}
|
|
43884
44121
|
async ensureArrayFile(path) {
|
|
43885
|
-
if (!
|
|
44122
|
+
if (!existsSync22(path)) {
|
|
43886
44123
|
await writeFile(path, `[]
|
|
43887
44124
|
`, { encoding: "utf-8", mode: 384 });
|
|
43888
44125
|
}
|
|
@@ -43912,7 +44149,7 @@ class JsonEventsStore {
|
|
|
43912
44149
|
});
|
|
43913
44150
|
}
|
|
43914
44151
|
}
|
|
43915
|
-
function localJsonRuntime(
|
|
44152
|
+
function localJsonRuntime(dataDir22 = getEventsDataDir()) {
|
|
43916
44153
|
return {
|
|
43917
44154
|
mode: "local-files",
|
|
43918
44155
|
name: "json-events-store",
|
|
@@ -43925,7 +44162,7 @@ function localJsonRuntime(dataDir3 = getEventsDataDir()) {
|
|
|
43925
44162
|
durable: true,
|
|
43926
44163
|
idempotency: "best-effort-local",
|
|
43927
44164
|
replayCursors: true,
|
|
43928
|
-
description: `Local JSON files in ${
|
|
44165
|
+
description: `Local JSON files in ${dataDir22}; no SQLite, Postgres, S3, or AWS runtime is configured by this store.`
|
|
43929
44166
|
};
|
|
43930
44167
|
}
|
|
43931
44168
|
function encodeLocalJsonEventCursor(offset, options = {}) {
|
|
@@ -43989,8 +44226,8 @@ function assertCursorFilter(name, cursorValue, optionValue) {
|
|
|
43989
44226
|
function findEventByIdentity(events2, identity3) {
|
|
43990
44227
|
return events2.find((event) => identity3.id !== undefined && event.id === identity3.id || identity3.dedupeKey !== undefined && event.dedupeKey === identity3.dedupeKey);
|
|
43991
44228
|
}
|
|
43992
|
-
async function getEventsStatus(
|
|
43993
|
-
const store = new JsonEventsStore(
|
|
44229
|
+
async function getEventsStatus(dataDir22) {
|
|
44230
|
+
const store = new JsonEventsStore(dataDir22);
|
|
43994
44231
|
await store.init();
|
|
43995
44232
|
const [channels, events2, deliveries] = await Promise.all([
|
|
43996
44233
|
store.listChannels(),
|
|
@@ -44032,9 +44269,9 @@ async function getEventsStatus(dataDir3) {
|
|
|
44032
44269
|
}
|
|
44033
44270
|
};
|
|
44034
44271
|
}
|
|
44035
|
-
function statusFile(
|
|
44036
|
-
const path =
|
|
44037
|
-
return { path, exists:
|
|
44272
|
+
function statusFile(dataDir22, fileName, records) {
|
|
44273
|
+
const path = join22(dataDir22, fileName);
|
|
44274
|
+
return { path, exists: existsSync22(path), records };
|
|
44038
44275
|
}
|
|
44039
44276
|
var DEFAULT_SIGNATURE_TOLERANCE_MS = 5 * 60 * 1000;
|
|
44040
44277
|
function buildSignatureBase(timestamp, body) {
|
|
@@ -44044,6 +44281,214 @@ function signPayload(secret, timestamp, body) {
|
|
|
44044
44281
|
const digest4 = createHmac("sha256", secret).update(buildSignatureBase(timestamp, body)).digest("hex");
|
|
44045
44282
|
return `sha256=${digest4}`;
|
|
44046
44283
|
}
|
|
44284
|
+
var DEFAULT_MAX_REDIRECTS = 5;
|
|
44285
|
+
var IPV4_PRIVATE_RANGES = [
|
|
44286
|
+
[0, 16777215],
|
|
44287
|
+
[167772160, 184549375],
|
|
44288
|
+
[1681915904, 1686110207],
|
|
44289
|
+
[2130706432, 2147483647],
|
|
44290
|
+
[2851995648, 2852061183],
|
|
44291
|
+
[2886729728, 2887778303],
|
|
44292
|
+
[3221225472, 3221225727],
|
|
44293
|
+
[3221225984, 3221226239],
|
|
44294
|
+
[3227017984, 3227018239],
|
|
44295
|
+
[3232235520, 3232301055],
|
|
44296
|
+
[3323068416, 3323199487],
|
|
44297
|
+
[3325256704, 3325256959],
|
|
44298
|
+
[3405803776, 3405804031],
|
|
44299
|
+
[3758096384, 4294967295]
|
|
44300
|
+
];
|
|
44301
|
+
var IPV6_SPECIAL_PREFIXES = [
|
|
44302
|
+
{ groups: [0, 0, 0, 0, 0, 0, 0, 0], bits: 128 },
|
|
44303
|
+
{ groups: [0, 0, 0, 0, 0, 0, 0, 1], bits: 128 },
|
|
44304
|
+
{ groups: [0, 0, 0, 0, 0, 65535, 0, 0], bits: 96 },
|
|
44305
|
+
{ groups: [100, 65435, 0, 0, 0, 0, 0, 0], bits: 96 },
|
|
44306
|
+
{ groups: [256, 0, 0, 0, 0, 0, 0, 0], bits: 64 },
|
|
44307
|
+
{ groups: [8193, 0, 0, 0, 0, 0, 0, 0], bits: 32 },
|
|
44308
|
+
{ groups: [8193, 2, 0, 0, 0, 0, 0, 0], bits: 48 },
|
|
44309
|
+
{ groups: [8193, 16, 0, 0, 0, 0, 0, 0], bits: 28 },
|
|
44310
|
+
{ groups: [8193, 3512, 0, 0, 0, 0, 0, 0], bits: 32 },
|
|
44311
|
+
{ groups: [8194, 0, 0, 0, 0, 0, 0, 0], bits: 16 },
|
|
44312
|
+
{ groups: [16383, 0, 0, 0, 0, 0, 0, 0], bits: 20 },
|
|
44313
|
+
{ groups: [64512, 0, 0, 0, 0, 0, 0, 0], bits: 7 },
|
|
44314
|
+
{ groups: [65152, 0, 0, 0, 0, 0, 0, 0], bits: 10 },
|
|
44315
|
+
{ groups: [65216, 0, 0, 0, 0, 0, 0, 0], bits: 10 },
|
|
44316
|
+
{ groups: [65280, 0, 0, 0, 0, 0, 0, 0], bits: 8 }
|
|
44317
|
+
];
|
|
44318
|
+
function isPrivateAddress(address) {
|
|
44319
|
+
const normalized = stripZoneId(address);
|
|
44320
|
+
const version2 = isIP(normalized);
|
|
44321
|
+
if (version2 === 4) {
|
|
44322
|
+
const integer3 = ipv4ToInt(normalized);
|
|
44323
|
+
if (integer3 === undefined)
|
|
44324
|
+
return true;
|
|
44325
|
+
return IPV4_PRIVATE_RANGES.some(([low, high]) => integer3 >= low && integer3 <= high);
|
|
44326
|
+
}
|
|
44327
|
+
if (version2 === 6) {
|
|
44328
|
+
const groups = ipv6Groups(normalized);
|
|
44329
|
+
if (!groups)
|
|
44330
|
+
return true;
|
|
44331
|
+
for (const prefix3 of IPV6_SPECIAL_PREFIXES) {
|
|
44332
|
+
if (!ipv6MatchesPrefix(groups, prefix3.groups, prefix3.bits))
|
|
44333
|
+
continue;
|
|
44334
|
+
if (prefix3.bits === 96 && groups[5] === 65535) {
|
|
44335
|
+
return isPrivateAddress(ipv4IntToString(groups[6] << 16 | groups[7]));
|
|
44336
|
+
}
|
|
44337
|
+
if (prefix3.bits === 16 && groups[0] === 8194) {
|
|
44338
|
+
return isPrivateAddress(ipv4IntToString(groups[1] << 16 | groups[2]));
|
|
44339
|
+
}
|
|
44340
|
+
return true;
|
|
44341
|
+
}
|
|
44342
|
+
return false;
|
|
44343
|
+
}
|
|
44344
|
+
return true;
|
|
44345
|
+
}
|
|
44346
|
+
async function resolveWebhookTarget(url2, policy = {}) {
|
|
44347
|
+
const hostname3 = normalizeHostname(url2.hostname);
|
|
44348
|
+
const allowlist = (policy.allowPrivateHosts ?? []).map((entry) => normalizeHostname(entry.toLowerCase()));
|
|
44349
|
+
if (allowlist.includes(hostname3)) {
|
|
44350
|
+
const version22 = isIP(hostname3);
|
|
44351
|
+
if (version22 === 4 || version22 === 6) {
|
|
44352
|
+
return { hostname: hostname3, addresses: [hostname3] };
|
|
44353
|
+
}
|
|
44354
|
+
const lookup2 = policy.lookup ?? defaultTargetLookup;
|
|
44355
|
+
let resolved2;
|
|
44356
|
+
try {
|
|
44357
|
+
resolved2 = await lookup2(hostname3);
|
|
44358
|
+
} catch {
|
|
44359
|
+
throw new Error(`Webhook target ${hostname3} could not be resolved`);
|
|
44360
|
+
}
|
|
44361
|
+
if (!Array.isArray(resolved2) || resolved2.length === 0) {
|
|
44362
|
+
throw new Error(`Webhook target ${hostname3} resolved to no addresses`);
|
|
44363
|
+
}
|
|
44364
|
+
const addresses = resolved2.map((entry) => normalizeHostname(entry.address));
|
|
44365
|
+
return { hostname: hostname3, addresses };
|
|
44366
|
+
}
|
|
44367
|
+
const version2 = isIP(hostname3);
|
|
44368
|
+
if (version2 === 4 || version2 === 6) {
|
|
44369
|
+
if (isPrivateAddress(hostname3)) {
|
|
44370
|
+
throw new Error(`Webhook target ${hostname3} is a private or special-use address`);
|
|
44371
|
+
}
|
|
44372
|
+
return { hostname: hostname3, addresses: [hostname3] };
|
|
44373
|
+
}
|
|
44374
|
+
const lookup = policy.lookup ?? defaultTargetLookup;
|
|
44375
|
+
let resolved;
|
|
44376
|
+
try {
|
|
44377
|
+
resolved = await lookup(hostname3);
|
|
44378
|
+
} catch {
|
|
44379
|
+
throw new Error(`Webhook target ${hostname3} could not be resolved`);
|
|
44380
|
+
}
|
|
44381
|
+
if (!Array.isArray(resolved) || resolved.length === 0) {
|
|
44382
|
+
throw new Error(`Webhook target ${hostname3} resolved to no addresses`);
|
|
44383
|
+
}
|
|
44384
|
+
const allowed = [];
|
|
44385
|
+
for (const entry of resolved) {
|
|
44386
|
+
const address = normalizeHostname(entry.address);
|
|
44387
|
+
if (isPrivateAddress(address)) {
|
|
44388
|
+
if (allowlist.includes(address)) {
|
|
44389
|
+
allowed.push(address);
|
|
44390
|
+
continue;
|
|
44391
|
+
}
|
|
44392
|
+
throw new Error(`Webhook target ${hostname3} resolves to private or special-use address ${address}`);
|
|
44393
|
+
}
|
|
44394
|
+
allowed.push(address);
|
|
44395
|
+
}
|
|
44396
|
+
if (allowed.length === 0) {
|
|
44397
|
+
throw new Error(`Webhook target ${hostname3} resolved to no public addresses`);
|
|
44398
|
+
}
|
|
44399
|
+
return { hostname: hostname3, addresses: allowed };
|
|
44400
|
+
}
|
|
44401
|
+
function normalizeMaxRedirects(value) {
|
|
44402
|
+
if (value === undefined)
|
|
44403
|
+
return DEFAULT_MAX_REDIRECTS;
|
|
44404
|
+
if (!Number.isInteger(value) || value < 0)
|
|
44405
|
+
throw new Error("webhookTargetPolicy.maxRedirects must be a non-negative integer");
|
|
44406
|
+
return value;
|
|
44407
|
+
}
|
|
44408
|
+
var defaultTargetLookup = async (hostname3) => {
|
|
44409
|
+
return dnsLookup(hostname3, { all: true, verbatim: false });
|
|
44410
|
+
};
|
|
44411
|
+
function normalizeHostname(hostname3) {
|
|
44412
|
+
const lower = hostname3.toLowerCase();
|
|
44413
|
+
if (lower.startsWith("[") && lower.endsWith("]"))
|
|
44414
|
+
return lower.slice(1, -1);
|
|
44415
|
+
return lower;
|
|
44416
|
+
}
|
|
44417
|
+
function stripZoneId(address) {
|
|
44418
|
+
const percent = address.indexOf("%");
|
|
44419
|
+
return percent === -1 ? address : address.slice(0, percent);
|
|
44420
|
+
}
|
|
44421
|
+
function ipv4ToInt(address) {
|
|
44422
|
+
const parts = address.split(".");
|
|
44423
|
+
if (parts.length !== 4)
|
|
44424
|
+
return;
|
|
44425
|
+
let value = 0;
|
|
44426
|
+
for (const part of parts) {
|
|
44427
|
+
if (!/^\d{1,3}$/.test(part))
|
|
44428
|
+
return;
|
|
44429
|
+
const octet = Number(part);
|
|
44430
|
+
if (octet > 255)
|
|
44431
|
+
return;
|
|
44432
|
+
value = value << 8 | octet;
|
|
44433
|
+
}
|
|
44434
|
+
return value >>> 0;
|
|
44435
|
+
}
|
|
44436
|
+
function ipv4IntToString(integer3) {
|
|
44437
|
+
return [
|
|
44438
|
+
integer3 >>> 24 & 255,
|
|
44439
|
+
integer3 >>> 16 & 255,
|
|
44440
|
+
integer3 >>> 8 & 255,
|
|
44441
|
+
integer3 & 255
|
|
44442
|
+
].join(".");
|
|
44443
|
+
}
|
|
44444
|
+
function ipv6Groups(address) {
|
|
44445
|
+
const raw = stripZoneId(address);
|
|
44446
|
+
const doubleColon = raw.indexOf("::");
|
|
44447
|
+
const headText = doubleColon === -1 ? raw : raw.slice(0, doubleColon);
|
|
44448
|
+
const tailText = doubleColon === -1 ? "" : raw.slice(doubleColon + 2);
|
|
44449
|
+
const parseGroups = (text3) => {
|
|
44450
|
+
if (text3 === "")
|
|
44451
|
+
return [];
|
|
44452
|
+
const out = [];
|
|
44453
|
+
for (const part of text3.split(":")) {
|
|
44454
|
+
if (part.includes(".")) {
|
|
44455
|
+
const v4 = ipv4ToInt(part);
|
|
44456
|
+
if (v4 === undefined)
|
|
44457
|
+
return;
|
|
44458
|
+
out.push(v4 >>> 16 & 65535, v4 & 65535);
|
|
44459
|
+
} else {
|
|
44460
|
+
if (!/^[0-9a-fA-F]{1,4}$/.test(part))
|
|
44461
|
+
return;
|
|
44462
|
+
out.push(parseInt(part, 16));
|
|
44463
|
+
}
|
|
44464
|
+
}
|
|
44465
|
+
return out;
|
|
44466
|
+
};
|
|
44467
|
+
const head = parseGroups(headText);
|
|
44468
|
+
if (!head)
|
|
44469
|
+
return;
|
|
44470
|
+
const tail = parseGroups(tailText);
|
|
44471
|
+
if (!tail)
|
|
44472
|
+
return;
|
|
44473
|
+
const total = head.length + tail.length;
|
|
44474
|
+
if (doubleColon === -1) {
|
|
44475
|
+
return total === 8 ? head : undefined;
|
|
44476
|
+
}
|
|
44477
|
+
if (total >= 8)
|
|
44478
|
+
return;
|
|
44479
|
+
return [...head, ...new Array(8 - total).fill(0), ...tail];
|
|
44480
|
+
}
|
|
44481
|
+
function ipv6MatchesPrefix(groups, prefixGroups, prefixBits) {
|
|
44482
|
+
let remaining = prefixBits;
|
|
44483
|
+
for (let index = 0;index < prefixGroups.length && remaining > 0; index += 1) {
|
|
44484
|
+
const take = Math.min(16, remaining);
|
|
44485
|
+
const mask = 65535 << 16 - take & 65535;
|
|
44486
|
+
if ((groups[index] & mask) !== (prefixGroups[index] & mask))
|
|
44487
|
+
return false;
|
|
44488
|
+
remaining -= take;
|
|
44489
|
+
}
|
|
44490
|
+
return true;
|
|
44491
|
+
}
|
|
44047
44492
|
function now() {
|
|
44048
44493
|
return new Date().toISOString();
|
|
44049
44494
|
}
|
|
@@ -44074,9 +44519,18 @@ function buildWebhookRequest(event, channel, options = {}) {
|
|
|
44074
44519
|
}
|
|
44075
44520
|
return { body, headers };
|
|
44076
44521
|
}
|
|
44522
|
+
function normalizeWebhookUrl(raw) {
|
|
44523
|
+
const url2 = new URL(raw);
|
|
44524
|
+
if (url2.username !== "" || url2.password !== "") {
|
|
44525
|
+
url2.username = "";
|
|
44526
|
+
url2.password = "";
|
|
44527
|
+
}
|
|
44528
|
+
return url2.toString();
|
|
44529
|
+
}
|
|
44077
44530
|
async function dispatchWebhook(event, channel, options = {}) {
|
|
44078
44531
|
if (!channel.webhook)
|
|
44079
44532
|
throw new Error(`Channel ${channel.id} has no webhook config`);
|
|
44533
|
+
const webhookUrl = normalizeWebhookUrl(channel.webhook.url);
|
|
44080
44534
|
const startedAt = now();
|
|
44081
44535
|
let secret = channel.webhook.secret;
|
|
44082
44536
|
if (channel.webhook.secretRef) {
|
|
@@ -44093,10 +44547,14 @@ async function dispatchWebhook(event, channel, options = {}) {
|
|
|
44093
44547
|
}
|
|
44094
44548
|
const timestamp = (options.now?.() ?? new Date).toISOString();
|
|
44095
44549
|
const { body, headers } = buildWebhookRequest(event, channel, { secret, timestamp });
|
|
44550
|
+
const validateTargets = options.webhookTargetPolicy !== undefined || options.fetchImpl === undefined;
|
|
44551
|
+
if (validateTargets) {
|
|
44552
|
+
return dispatchValidatedWebhook(event, channel, { body, headers, startedAt, options });
|
|
44553
|
+
}
|
|
44096
44554
|
const controller = new AbortController;
|
|
44097
44555
|
const timeout = setTimeout(() => controller.abort(), channel.webhook.timeoutMs ?? 15000);
|
|
44098
44556
|
try {
|
|
44099
|
-
const response = await (options.fetchImpl ?? fetch)(
|
|
44557
|
+
const response = await (options.fetchImpl ?? fetch)(webhookUrl, {
|
|
44100
44558
|
method: "POST",
|
|
44101
44559
|
headers,
|
|
44102
44560
|
body,
|
|
@@ -44124,6 +44582,130 @@ async function dispatchWebhook(event, channel, options = {}) {
|
|
|
44124
44582
|
clearTimeout(timeout);
|
|
44125
44583
|
}
|
|
44126
44584
|
}
|
|
44585
|
+
function isRedirectStatus(status) {
|
|
44586
|
+
return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
|
|
44587
|
+
}
|
|
44588
|
+
function redirectKeepsBody(status) {
|
|
44589
|
+
return status === 307 || status === 308;
|
|
44590
|
+
}
|
|
44591
|
+
async function pinnedNativeRequest(target, addresses, method, headers, body, signal, tls) {
|
|
44592
|
+
const isHttps = target.protocol === "https:";
|
|
44593
|
+
if (!isHttps && target.protocol !== "http:") {
|
|
44594
|
+
throw new Error(`Webhook target uses unsupported protocol ${target.protocol}`);
|
|
44595
|
+
}
|
|
44596
|
+
const defaultPort = isHttps ? 443 : 80;
|
|
44597
|
+
const port = target.port ? Number(target.port) : defaultPort;
|
|
44598
|
+
const requestOptions = {
|
|
44599
|
+
hostname: target.hostname,
|
|
44600
|
+
port,
|
|
44601
|
+
path: `${target.pathname}${target.search}`,
|
|
44602
|
+
method,
|
|
44603
|
+
headers,
|
|
44604
|
+
...tls?.ca ? { ca: tls.ca } : {},
|
|
44605
|
+
lookup: (hostname3, _options, callback) => {
|
|
44606
|
+
const entries = addresses.map((address) => ({
|
|
44607
|
+
address,
|
|
44608
|
+
family: address.includes(":") ? 6 : 4
|
|
44609
|
+
}));
|
|
44610
|
+
callback(null, entries);
|
|
44611
|
+
}
|
|
44612
|
+
};
|
|
44613
|
+
return new Promise((resolve23, reject) => {
|
|
44614
|
+
const request = isHttps ? nodeHttpsRequest(requestOptions, onResponse) : nodeHttpRequest(requestOptions, onResponse);
|
|
44615
|
+
const onAbort = () => {
|
|
44616
|
+
const error61 = new Error("The operation was aborted.");
|
|
44617
|
+
error61.name = "AbortError";
|
|
44618
|
+
request.destroy(error61);
|
|
44619
|
+
};
|
|
44620
|
+
if (signal.aborted)
|
|
44621
|
+
onAbort();
|
|
44622
|
+
else
|
|
44623
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
44624
|
+
request.on("error", reject);
|
|
44625
|
+
if (body !== undefined)
|
|
44626
|
+
request.write(body);
|
|
44627
|
+
request.end();
|
|
44628
|
+
function onResponse(response) {
|
|
44629
|
+
const chunks = [];
|
|
44630
|
+
response.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
44631
|
+
response.on("error", reject);
|
|
44632
|
+
response.on("end", () => {
|
|
44633
|
+
const headersRecord = {};
|
|
44634
|
+
for (const [name, value] of Object.entries(response.headers)) {
|
|
44635
|
+
if (typeof value === "string")
|
|
44636
|
+
headersRecord[name] = value;
|
|
44637
|
+
else if (Array.isArray(value))
|
|
44638
|
+
headersRecord[name] = value.join(", ");
|
|
44639
|
+
}
|
|
44640
|
+
resolve23(new Response(Buffer.concat(chunks), { status: response.statusCode ?? 200, headers: headersRecord }));
|
|
44641
|
+
});
|
|
44642
|
+
}
|
|
44643
|
+
});
|
|
44644
|
+
}
|
|
44645
|
+
async function dispatchValidatedWebhook(event, channel, input2) {
|
|
44646
|
+
const { body, headers, startedAt, options } = input2;
|
|
44647
|
+
const webhook = channel.webhook;
|
|
44648
|
+
if (!webhook)
|
|
44649
|
+
throw new Error(`Channel ${channel.id} has no webhook config`);
|
|
44650
|
+
const policy = options.webhookTargetPolicy ?? {};
|
|
44651
|
+
const maxRedirects = normalizeMaxRedirects(policy.maxRedirects);
|
|
44652
|
+
const controller = new AbortController;
|
|
44653
|
+
const timeout = setTimeout(() => controller.abort(), webhook.timeoutMs ?? 15000);
|
|
44654
|
+
try {
|
|
44655
|
+
let target = new URL(normalizeWebhookUrl(webhook.url));
|
|
44656
|
+
let requestHeaders = headers;
|
|
44657
|
+
let method = "POST";
|
|
44658
|
+
let requestBody = body;
|
|
44659
|
+
let redirectsFollowed = 0;
|
|
44660
|
+
for (;; ) {
|
|
44661
|
+
const resolved = await resolveWebhookTarget(target, policy).catch((error61) => {
|
|
44662
|
+
throw new Error(`Webhook target rejected by SSRF guard: ${error61.message}`);
|
|
44663
|
+
});
|
|
44664
|
+
const response = options.fetchImpl ? await options.fetchImpl(target, {
|
|
44665
|
+
method,
|
|
44666
|
+
headers: requestHeaders,
|
|
44667
|
+
body: requestBody,
|
|
44668
|
+
signal: controller.signal,
|
|
44669
|
+
redirect: "manual"
|
|
44670
|
+
}) : await pinnedNativeRequest(target, resolved.addresses, method, requestHeaders, requestBody, controller.signal, options.tls);
|
|
44671
|
+
const location = response.headers.get("location");
|
|
44672
|
+
if (isRedirectStatus(response.status) && location) {
|
|
44673
|
+
if (redirectsFollowed >= maxRedirects) {
|
|
44674
|
+
return failedAttempt(startedAt, `Webhook target exceeded ${maxRedirects} redirects`);
|
|
44675
|
+
}
|
|
44676
|
+
redirectsFollowed += 1;
|
|
44677
|
+
const next = new URL(location, target);
|
|
44678
|
+
target = next;
|
|
44679
|
+
if (!redirectKeepsBody(response.status)) {
|
|
44680
|
+
method = "GET";
|
|
44681
|
+
requestBody = undefined;
|
|
44682
|
+
requestHeaders = Object.fromEntries(Object.entries(requestHeaders).filter(([name]) => name.toLowerCase() !== "content-type" && name.toLowerCase() !== "content-length"));
|
|
44683
|
+
}
|
|
44684
|
+
continue;
|
|
44685
|
+
}
|
|
44686
|
+
const responseBody = truncate(await response.text());
|
|
44687
|
+
return {
|
|
44688
|
+
attempt: 1,
|
|
44689
|
+
status: response.ok ? "success" : "failed",
|
|
44690
|
+
startedAt,
|
|
44691
|
+
completedAt: now(),
|
|
44692
|
+
responseStatus: response.status,
|
|
44693
|
+
responseBody,
|
|
44694
|
+
error: response.ok ? undefined : `Webhook returned HTTP ${response.status}`
|
|
44695
|
+
};
|
|
44696
|
+
}
|
|
44697
|
+
} catch (error61) {
|
|
44698
|
+
return {
|
|
44699
|
+
attempt: 1,
|
|
44700
|
+
status: "failed",
|
|
44701
|
+
startedAt,
|
|
44702
|
+
completedAt: now(),
|
|
44703
|
+
error: error61 instanceof Error ? error61.message : String(error61)
|
|
44704
|
+
};
|
|
44705
|
+
} finally {
|
|
44706
|
+
clearTimeout(timeout);
|
|
44707
|
+
}
|
|
44708
|
+
}
|
|
44127
44709
|
function failedAttempt(startedAt, error61) {
|
|
44128
44710
|
return {
|
|
44129
44711
|
attempt: 1,
|
|
@@ -44152,7 +44734,7 @@ async function dispatchCommand(event, channel) {
|
|
|
44152
44734
|
HASNA_EVENT_SCHEMA_VERSION: event.schemaVersion,
|
|
44153
44735
|
HASNA_EVENT_JSON: eventJson
|
|
44154
44736
|
};
|
|
44155
|
-
return new Promise((
|
|
44737
|
+
return new Promise((resolve23) => {
|
|
44156
44738
|
const child = spawn3(channel.command.command, channel.command.args ?? [], {
|
|
44157
44739
|
cwd: channel.command.cwd,
|
|
44158
44740
|
env,
|
|
@@ -44170,7 +44752,7 @@ async function dispatchCommand(event, channel) {
|
|
|
44170
44752
|
});
|
|
44171
44753
|
child.on("error", (error61) => {
|
|
44172
44754
|
clearTimeout(timeout);
|
|
44173
|
-
|
|
44755
|
+
resolve23({
|
|
44174
44756
|
attempt: 1,
|
|
44175
44757
|
status: "failed",
|
|
44176
44758
|
startedAt,
|
|
@@ -44183,7 +44765,7 @@ async function dispatchCommand(event, channel) {
|
|
|
44183
44765
|
child.on("close", (code2, signal) => {
|
|
44184
44766
|
clearTimeout(timeout);
|
|
44185
44767
|
const success2 = code2 === 0;
|
|
44186
|
-
|
|
44768
|
+
resolve23({
|
|
44187
44769
|
attempt: 1,
|
|
44188
44770
|
status: success2 ? "success" : "failed",
|
|
44189
44771
|
startedAt,
|
|
@@ -44305,6 +44887,22 @@ function setPath(input2, path, replacement) {
|
|
|
44305
44887
|
if (last && last in cursor)
|
|
44306
44888
|
cursor[last] = replacement;
|
|
44307
44889
|
}
|
|
44890
|
+
var MAX_PACKAGE_BYTES2 = 1024 * 1024;
|
|
44891
|
+
var MAX_CREDENTIAL_FILE_BYTES2 = 64 * 1024;
|
|
44892
|
+
var INSPECT_CUSTOM2 = Symbol.for("nodejs.util.inspect.custom");
|
|
44893
|
+
var CREDENTIAL_SEAL2 = Symbol.for("hasna:contracts:sealedCredential");
|
|
44894
|
+
var AMBIENT_ENVIRONMENT2 = Symbol.for("hasna:contracts:ambientClientEnvironment");
|
|
44895
|
+
var requireSecretsSdk2 = createRequire2(import.meta.url);
|
|
44896
|
+
var IDEMPOTENT_METHODS2 = new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
|
|
44897
|
+
var AUTHORITY_OVERRIDE_HEADERS2 = new Set([
|
|
44898
|
+
"host",
|
|
44899
|
+
":authority",
|
|
44900
|
+
"forwarded",
|
|
44901
|
+
"x-forwarded-host",
|
|
44902
|
+
"x-original-host"
|
|
44903
|
+
]);
|
|
44904
|
+
var MAX_ENVELOPE_BYTES = 256 * 1024;
|
|
44905
|
+
var MAX_REQUEST_BYTES = MAX_ENVELOPE_BYTES * 2 + 8192;
|
|
44308
44906
|
function createEvent(input2) {
|
|
44309
44907
|
return {
|
|
44310
44908
|
id: input2.id ?? randomUUID22(),
|
|
@@ -44333,7 +44931,9 @@ class EventsClient {
|
|
|
44333
44931
|
this.transportOptions = {
|
|
44334
44932
|
fetchImpl: options.fetchImpl,
|
|
44335
44933
|
secretResolver: options.secretResolver,
|
|
44336
|
-
now: options.now
|
|
44934
|
+
now: options.now,
|
|
44935
|
+
tls: options.tls,
|
|
44936
|
+
webhookTargetPolicy: options.webhookTargetPolicy
|
|
44337
44937
|
};
|
|
44338
44938
|
this.catalog = options.catalog ?? defaultEventTypeCatalog;
|
|
44339
44939
|
this.validateCatalogTypes = options.validateCatalogTypes ?? false;
|
|
@@ -44620,6 +45220,13 @@ function parseMatcherExpression(value, label) {
|
|
|
44620
45220
|
negated: false
|
|
44621
45221
|
};
|
|
44622
45222
|
}
|
|
45223
|
+
function webhookTargetPolicyFromEnv() {
|
|
45224
|
+
const value = process.env.HASNA_EVENTS_ALLOW_PRIVATE_WEBHOOK_TARGETS;
|
|
45225
|
+
if (!value)
|
|
45226
|
+
return;
|
|
45227
|
+
const hosts = value.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
45228
|
+
return hosts.length > 0 ? { allowPrivateHosts: hosts } : undefined;
|
|
45229
|
+
}
|
|
44623
45230
|
var DEFAULT_EVENT_LIST_LIMIT = 100;
|
|
44624
45231
|
function parseJsonObject(value, fallback) {
|
|
44625
45232
|
if (!value)
|
|
@@ -44645,7 +45252,7 @@ function parseHeaders(values) {
|
|
|
44645
45252
|
function createClient(options) {
|
|
44646
45253
|
if (options.createClient)
|
|
44647
45254
|
return options.createClient();
|
|
44648
|
-
return new EventsClient({ store: new JsonEventsStore(options.dataDir) });
|
|
45255
|
+
return new EventsClient({ store: new JsonEventsStore(options.dataDir), webhookTargetPolicy: webhookTargetPolicyFromEnv() });
|
|
44649
45256
|
}
|
|
44650
45257
|
function print(value, json2, text3) {
|
|
44651
45258
|
if (json2)
|
|
@@ -44726,6 +45333,8 @@ function registerChannelCommands(program2, options) {
|
|
|
44726
45333
|
metadata: parseJsonObject(actionOptions.metadata, {})
|
|
44727
45334
|
}, { honorFilters: actionOptions.honorFilters });
|
|
44728
45335
|
print(result, json2, `${result.status}: ${result.channelId}`);
|
|
45336
|
+
if (result.status === "failed")
|
|
45337
|
+
process.exitCode = 1;
|
|
44729
45338
|
} catch (error61) {
|
|
44730
45339
|
fail(error61, json2);
|
|
44731
45340
|
}
|
|
@@ -44818,7 +45427,7 @@ function replaySummary(events2, deliveries, nextCursor) {
|
|
|
44818
45427
|
// src/cli/index.tsx
|
|
44819
45428
|
import { render } from "ink";
|
|
44820
45429
|
|
|
44821
|
-
//
|
|
45430
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/esm.mjs
|
|
44822
45431
|
var import__ = __toESM(require_commander(), 1);
|
|
44823
45432
|
var {
|
|
44824
45433
|
program: program2,
|
|
@@ -44837,18 +45446,18 @@ var {
|
|
|
44837
45446
|
// src/cli/index.tsx
|
|
44838
45447
|
import chalk2 from "chalk";
|
|
44839
45448
|
import { existsSync as existsSync18, readFileSync as readFileSync27, rmSync as rmSync6 } from "fs";
|
|
44840
|
-
import { join as
|
|
45449
|
+
import { join as join36, dirname as dirname28 } from "path";
|
|
44841
45450
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
44842
45451
|
|
|
44843
45452
|
// src/cli/components/App.tsx
|
|
44844
45453
|
import { useState as useState7 } from "react";
|
|
44845
45454
|
import { Box as Box8, Text as Text10, useApp, useInput as useInput5 } from "ink";
|
|
44846
45455
|
|
|
44847
|
-
//
|
|
45456
|
+
// ../../node_modules/.bun/ink-select-input@6.2.0+fa875fef9f949ebd/node_modules/ink-select-input/build/Indicator.js
|
|
44848
45457
|
import React from "react";
|
|
44849
45458
|
import { Box, Text } from "ink";
|
|
44850
45459
|
|
|
44851
|
-
//
|
|
45460
|
+
// ../../node_modules/.bun/is-unicode-supported@2.1.0/node_modules/is-unicode-supported/index.js
|
|
44852
45461
|
import process3 from "process";
|
|
44853
45462
|
function isUnicodeSupported() {
|
|
44854
45463
|
const { env } = process3;
|
|
@@ -44859,7 +45468,7 @@ function isUnicodeSupported() {
|
|
|
44859
45468
|
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";
|
|
44860
45469
|
}
|
|
44861
45470
|
|
|
44862
|
-
//
|
|
45471
|
+
// ../../node_modules/.bun/figures@6.1.0/node_modules/figures/index.js
|
|
44863
45472
|
var common2 = {
|
|
44864
45473
|
circleQuestionMark: "(?)",
|
|
44865
45474
|
questionMarkPrefix: "(?)",
|
|
@@ -45135,23 +45744,23 @@ var figures = shouldUseMain ? mainSymbols : fallbackSymbols;
|
|
|
45135
45744
|
var figures_default = figures;
|
|
45136
45745
|
var replacements = Object.entries(specialMainSymbols);
|
|
45137
45746
|
|
|
45138
|
-
//
|
|
45747
|
+
// ../../node_modules/.bun/ink-select-input@6.2.0+fa875fef9f949ebd/node_modules/ink-select-input/build/Indicator.js
|
|
45139
45748
|
function Indicator({ isSelected = false }) {
|
|
45140
45749
|
return React.createElement(Box, { marginRight: 1 }, isSelected ? React.createElement(Text, { color: "blue" }, figures_default.pointer) : React.createElement(Text, null, " "));
|
|
45141
45750
|
}
|
|
45142
45751
|
var Indicator_default = Indicator;
|
|
45143
|
-
//
|
|
45752
|
+
// ../../node_modules/.bun/ink-select-input@6.2.0+fa875fef9f949ebd/node_modules/ink-select-input/build/Item.js
|
|
45144
45753
|
import * as React2 from "react";
|
|
45145
45754
|
import { Text as Text2 } from "ink";
|
|
45146
45755
|
function Item({ isSelected = false, label }) {
|
|
45147
45756
|
return React2.createElement(Text2, { color: isSelected ? "blue" : undefined }, label);
|
|
45148
45757
|
}
|
|
45149
45758
|
var Item_default = Item;
|
|
45150
|
-
//
|
|
45759
|
+
// ../../node_modules/.bun/ink-select-input@6.2.0+fa875fef9f949ebd/node_modules/ink-select-input/build/SelectInput.js
|
|
45151
45760
|
import { isDeepStrictEqual } from "util";
|
|
45152
45761
|
import React3, { useState, useEffect, useRef, useCallback } from "react";
|
|
45153
45762
|
|
|
45154
|
-
//
|
|
45763
|
+
// ../../node_modules/.bun/to-rotated@1.0.0/node_modules/to-rotated/index.js
|
|
45155
45764
|
function toRotated(array3, steps) {
|
|
45156
45765
|
if (!Array.isArray(array3)) {
|
|
45157
45766
|
throw new TypeError(`Expected an array, got \`${typeof array3}\`.`);
|
|
@@ -45173,7 +45782,7 @@ function toRotated(array3, steps) {
|
|
|
45173
45782
|
];
|
|
45174
45783
|
}
|
|
45175
45784
|
|
|
45176
|
-
//
|
|
45785
|
+
// ../../node_modules/.bun/ink-select-input@6.2.0+fa875fef9f949ebd/node_modules/ink-select-input/build/SelectInput.js
|
|
45177
45786
|
import { Box as Box2, useInput } from "ink";
|
|
45178
45787
|
function SelectInput({ items = [], isFocused = true, initialIndex = 0, indicatorComponent = Indicator_default, itemComponent = Item_default, limit: customLimit, onSelect, onHighlight }) {
|
|
45179
45788
|
const hasLimit = typeof customLimit === "number" && items.length > customLimit;
|
|
@@ -45469,7 +46078,7 @@ function HookSelect({
|
|
|
45469
46078
|
import { useState as useState4, useEffect as useEffect3 } from "react";
|
|
45470
46079
|
import { Box as Box6, Text as Text7, useInput as useInput4 } from "ink";
|
|
45471
46080
|
|
|
45472
|
-
//
|
|
46081
|
+
// ../../node_modules/.bun/ink-text-input@6.0.0+fa875fef9f949ebd/node_modules/ink-text-input/build/index.js
|
|
45473
46082
|
import React5, { useState as useState3, useEffect as useEffect2 } from "react";
|
|
45474
46083
|
import { Text as Text6, useInput as useInput3 } from "ink";
|
|
45475
46084
|
import chalk from "chalk";
|
|
@@ -45647,7 +46256,7 @@ function SearchView({
|
|
|
45647
46256
|
import { useState as useState6, useEffect as useEffect5 } from "react";
|
|
45648
46257
|
import { Box as Box7, Text as Text9 } from "ink";
|
|
45649
46258
|
|
|
45650
|
-
//
|
|
46259
|
+
// ../../node_modules/.bun/ink-spinner@5.0.0+fa875fef9f949ebd/node_modules/ink-spinner/build/index.js
|
|
45651
46260
|
var import_cli_spinners = __toESM(require_cli_spinners(), 1);
|
|
45652
46261
|
import React7, { useState as useState5, useEffect as useEffect4 } from "react";
|
|
45653
46262
|
import { Text as Text8 } from "ink";
|
|
@@ -46014,10 +46623,10 @@ init_native_safety();
|
|
|
46014
46623
|
|
|
46015
46624
|
// src/lib/sumi-binding.ts
|
|
46016
46625
|
import { homedir as homedir14 } from "os";
|
|
46017
|
-
import { join as
|
|
46626
|
+
import { join as join27, sep as sep3 } from "path";
|
|
46018
46627
|
|
|
46019
46628
|
// hooks/hook-trash-guard/src/binary.ts
|
|
46020
|
-
import { dirname as dirname21, isAbsolute as isAbsolute20, join as
|
|
46629
|
+
import { dirname as dirname21, isAbsolute as isAbsolute20, join as join24, resolve as resolve21 } from "path";
|
|
46021
46630
|
import { accessSync, closeSync as closeSync19, constants as constants18, fstatSync as fstatSync18, lstatSync as lstatSync23, openSync as openSync19, readFileSync as readFileSync17, readlinkSync as readlinkSync3, readSync as readSync3, realpathSync as realpathSync11, statSync } from "fs";
|
|
46022
46631
|
import { spawnSync } from "child_process";
|
|
46023
46632
|
function trustedOwner(uid2) {
|
|
@@ -46039,7 +46648,7 @@ function safeExecutableFile(path, systemBinAlias = false) {
|
|
|
46039
46648
|
parent = dirname21(parent);
|
|
46040
46649
|
continue;
|
|
46041
46650
|
}
|
|
46042
|
-
const next =
|
|
46651
|
+
const next = join24(parent, part), info2 = lstatSync23(next);
|
|
46043
46652
|
if (systemBinAlias && next === "/bin" && info2.isSymbolicLink() && info2.uid === 0) {
|
|
46044
46653
|
const target = realpathSync11(next);
|
|
46045
46654
|
if (!safeBinaryDirectory(target))
|
|
@@ -46051,7 +46660,7 @@ function safeExecutableFile(path, systemBinAlias = false) {
|
|
|
46051
46660
|
return false;
|
|
46052
46661
|
parent = next;
|
|
46053
46662
|
}
|
|
46054
|
-
const candidate =
|
|
46663
|
+
const candidate = join24(parent, name), info = lstatSync23(candidate);
|
|
46055
46664
|
if (!trustedOwner(info.uid))
|
|
46056
46665
|
return false;
|
|
46057
46666
|
if (info.isSymbolicLink()) {
|
|
@@ -46064,9 +46673,9 @@ function safeExecutableFile(path, systemBinAlias = false) {
|
|
|
46064
46673
|
return false;
|
|
46065
46674
|
}
|
|
46066
46675
|
function safeInterpreterSearchDirectory(path) {
|
|
46067
|
-
if (!isAbsolute20(path) ||
|
|
46676
|
+
if (!isAbsolute20(path) || resolve21(path) !== (path.replace(/\/+$/, "") || "/"))
|
|
46068
46677
|
return false;
|
|
46069
|
-
for (let cursor =
|
|
46678
|
+
for (let cursor = resolve21(path);; cursor = dirname21(cursor)) {
|
|
46070
46679
|
try {
|
|
46071
46680
|
lstatSync23(cursor);
|
|
46072
46681
|
return safeBinaryDirectory(cursor);
|
|
@@ -46098,7 +46707,7 @@ function safeScriptInterpreter(executable2, pathValue) {
|
|
|
46098
46707
|
for (const dir of pathValue.split(":").slice(0, 64)) {
|
|
46099
46708
|
if (!safeInterpreterSearchDirectory(dir))
|
|
46100
46709
|
return false;
|
|
46101
|
-
const interpreter =
|
|
46710
|
+
const interpreter = resolve21(dir, "bun");
|
|
46102
46711
|
try {
|
|
46103
46712
|
if (!statSync(interpreter).isFile())
|
|
46104
46713
|
continue;
|
|
@@ -46133,7 +46742,7 @@ function inspectTrashBinary(env = process.env) {
|
|
|
46133
46742
|
for (const dir of pathValue.split(":").slice(0, 64)) {
|
|
46134
46743
|
if (!isAbsolute20(dir) || Date.now() >= deadline)
|
|
46135
46744
|
continue;
|
|
46136
|
-
const candidate =
|
|
46745
|
+
const candidate = resolve21(dir, "trash");
|
|
46137
46746
|
try {
|
|
46138
46747
|
const stat2 = statSync(candidate);
|
|
46139
46748
|
if (!stat2.isFile() || !trustedOwner(stat2.uid) || (stat2.mode & 73) === 0 || (stat2.mode & 18) !== 0) {
|
|
@@ -46141,7 +46750,7 @@ function inspectTrashBinary(env = process.env) {
|
|
|
46141
46750
|
continue;
|
|
46142
46751
|
}
|
|
46143
46752
|
const executable2 = realpathSync11(candidate);
|
|
46144
|
-
if (!safeBinaryDirectory(
|
|
46753
|
+
if (!safeBinaryDirectory(resolve21(dir)) || !safeBinaryDirectory(dirname21(executable2))) {
|
|
46145
46754
|
reject("unsafe_directory");
|
|
46146
46755
|
continue;
|
|
46147
46756
|
}
|
|
@@ -46149,15 +46758,15 @@ function inspectTrashBinary(env = process.env) {
|
|
|
46149
46758
|
reject("unsafe_directory");
|
|
46150
46759
|
continue;
|
|
46151
46760
|
}
|
|
46152
|
-
const packageRoot =
|
|
46153
|
-
const manifestPath =
|
|
46761
|
+
const packageRoot = resolve21(dirname21(executable2), "../..");
|
|
46762
|
+
const manifestPath = join24(packageRoot, "package.json");
|
|
46154
46763
|
const manifestStat = statSync(manifestPath);
|
|
46155
46764
|
if (!manifestStat.isFile() || !trustedOwner(manifestStat.uid) || manifestStat.size > 16384 || (manifestStat.mode & 18) !== 0 || lstatSync23(manifestPath).isSymbolicLink()) {
|
|
46156
46765
|
reject("unsafe_manifest");
|
|
46157
46766
|
continue;
|
|
46158
46767
|
}
|
|
46159
46768
|
const manifest = JSON.parse(readFileSync17(manifestPath, "utf8"));
|
|
46160
|
-
if (manifest.name !== "@hasna/trash" || typeof manifest.version !== "string" || typeof manifest.bin?.trash !== "string" || realpathSync11(
|
|
46769
|
+
if (manifest.name !== "@hasna/trash" || typeof manifest.version !== "string" || typeof manifest.bin?.trash !== "string" || realpathSync11(resolve21(packageRoot, manifest.bin.trash)) !== executable2) {
|
|
46161
46770
|
reject("unrecognized_package");
|
|
46162
46771
|
continue;
|
|
46163
46772
|
}
|
|
@@ -46202,7 +46811,7 @@ function failure(code2, reason) {
|
|
|
46202
46811
|
return { ok: false, target: "sumi", code: code2, ...reason ? { reason: reason.slice(0, 300) } : {} };
|
|
46203
46812
|
}
|
|
46204
46813
|
function nativeSafetyBindingDurable(worker, home) {
|
|
46205
|
-
return !(worker.startsWith(
|
|
46814
|
+
return !(worker.startsWith(join27(home, ".bun") + sep3) || worker.includes(`${sep3}node_modules${sep3}.bun${sep3}`));
|
|
46206
46815
|
}
|
|
46207
46816
|
async function provisionSumiSafetyBinding(options = {}) {
|
|
46208
46817
|
const home = options.home ?? homedir14();
|
|
@@ -46277,7 +46886,7 @@ init_transport2();
|
|
|
46277
46886
|
init_db();
|
|
46278
46887
|
import { jsxDEV as jsxDEV8 } from "react/jsx-dev-runtime";
|
|
46279
46888
|
var __dirname4 = dirname28(fileURLToPath6(import.meta.url));
|
|
46280
|
-
var pkgPath = existsSync18(
|
|
46889
|
+
var pkgPath = existsSync18(join36(__dirname4, "..", "package.json")) ? join36(__dirname4, "..", "package.json") : join36(__dirname4, "..", "..", "package.json");
|
|
46281
46890
|
var pkg2 = JSON.parse(readFileSync27(pkgPath, "utf-8"));
|
|
46282
46891
|
var program3 = new Command;
|
|
46283
46892
|
function resolveScope(options) {
|
|
@@ -46510,7 +47119,7 @@ program3.command("init").description("Register a new agent profile with a unique
|
|
|
46510
47119
|
if (profile.name) {
|
|
46511
47120
|
console.log(` ${chalk2.dim("Name:")} ${profile.name}`);
|
|
46512
47121
|
}
|
|
46513
|
-
console.log(` ${chalk2.dim("Profile:")} ${
|
|
47122
|
+
console.log(` ${chalk2.dim("Profile:")} ${join36(getProfilesDir(), `${profile.agent_id}.json`)}`);
|
|
46514
47123
|
console.log();
|
|
46515
47124
|
console.log(chalk2.dim(" Install hooks with this profile:"));
|
|
46516
47125
|
console.log(` hooks install gitguard --profile ${profile.agent_id}`);
|
|
@@ -47165,7 +47774,7 @@ program3.command("doctor").option("-g, --global", "Check global settings", false
|
|
|
47165
47774
|
continue;
|
|
47166
47775
|
}
|
|
47167
47776
|
const hookDir = getHookPath(name);
|
|
47168
|
-
const hookScript =
|
|
47777
|
+
const hookScript = join36(hookDir, "src", "hook.ts");
|
|
47169
47778
|
if (!existsSync18(hookScript)) {
|
|
47170
47779
|
issues.push({ hook: name, issue: "Missing src/hook.ts in package", severity: "error" });
|
|
47171
47780
|
hookHealthy = false;
|
|
@@ -47174,7 +47783,7 @@ program3.command("doctor").option("-g, --global", "Check global settings", false
|
|
|
47174
47783
|
if (meta3 && settingsExist) {
|
|
47175
47784
|
try {
|
|
47176
47785
|
const settings = JSON.parse(readFileSync27(settingsPath, "utf-8"));
|
|
47177
|
-
if (!hookRegisteredInSettings(settings, name, meta3.event, meta3
|
|
47786
|
+
if (!hookRegisteredInSettings(settings, name, meta3.event, matcherFor(meta3, "claude"))) {
|
|
47178
47787
|
const eventName = meta3.event.split(":")[0];
|
|
47179
47788
|
issues.push({ hook: name, issue: `Not registered under correct event (${eventName})`, severity: "error" });
|
|
47180
47789
|
hookHealthy = false;
|
|
@@ -47376,7 +47985,7 @@ program3.command("docs").argument("[hook]", "Hook name (shows general docs if om
|
|
|
47376
47985
|
return;
|
|
47377
47986
|
}
|
|
47378
47987
|
const hookPath = getHookPath(hook);
|
|
47379
|
-
const readmePath =
|
|
47988
|
+
const readmePath = join36(hookPath, "README.md");
|
|
47380
47989
|
let readme = "";
|
|
47381
47990
|
if (existsSync18(readmePath)) {
|
|
47382
47991
|
readme = readFileSync27(readmePath, "utf-8");
|
|
@@ -48042,13 +48651,13 @@ startup.command("check").requiredOption("--target <target>", "codex, claude or s
|
|
|
48042
48651
|
process.exitCode = 1;
|
|
48043
48652
|
});
|
|
48044
48653
|
for (const action of ["registration-plan", "registration-apply"]) {
|
|
48045
|
-
const command = startup.command(action).requiredOption("--target <target>", "codex or
|
|
48654
|
+
const command = startup.command(action).requiredOption("--target <target>", "codex, claude or sumi (Sumi: its native guard record)");
|
|
48046
48655
|
if (action === "registration-apply")
|
|
48047
48656
|
command.requiredOption("--expected-plan-digest <sha256>", "Exact registration-plan digest");
|
|
48048
48657
|
command.action(async (options) => {
|
|
48049
48658
|
try {
|
|
48050
48659
|
const { planNativeReadinessRegistration: planNativeReadinessRegistration2, applyNativeReadinessRegistration: applyNativeReadinessRegistration2 } = await Promise.resolve().then(() => (init_native_safety_registration(), exports_native_safety_registration));
|
|
48051
|
-
if (options.target !== "codex" && options.target !== "claude")
|
|
48660
|
+
if (options.target !== "codex" && options.target !== "claude" && options.target !== "sumi")
|
|
48052
48661
|
throw new NativeRegistrationError("input_invalid");
|
|
48053
48662
|
console.log(JSON.stringify(action === "registration-plan" ? await planNativeReadinessRegistration2({ target: options.target }) : await applyNativeReadinessRegistration2({ target: options.target, expectedPlanDigest: options.expectedPlanDigest })));
|
|
48054
48663
|
} catch (error61) {
|
|
@@ -48174,7 +48783,7 @@ safety.command("verify").requiredOption("--target <target>", "sumi, codex or cla
|
|
|
48174
48783
|
throw new ClaudeSafetyError("settings_invalid");
|
|
48175
48784
|
console.log(JSON.stringify(await verifyClaudeSafetyConfiguration({
|
|
48176
48785
|
cwd: options.cwd,
|
|
48177
|
-
...process.env.CLAUDE_CONFIG_DIR ? { userSettingsPath:
|
|
48786
|
+
...process.env.CLAUDE_CONFIG_DIR ? { userSettingsPath: resolve23(process.env.CLAUDE_CONFIG_DIR, "settings.json") } : {}
|
|
48178
48787
|
})));
|
|
48179
48788
|
} catch (error61) {
|
|
48180
48789
|
console.log(JSON.stringify({ ok: false, target: "claude", code: error61 instanceof ClaudeSafetyError ? error61.code : "settings_invalid", nativeAdoptionVerified: false }));
|