@hasna/hooks 0.6.8 → 0.7.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.
Files changed (33) hide show
  1. package/README.md +1 -1
  2. package/bin/index.js +886 -418
  3. package/bin/serve.js +613 -148
  4. package/dist/config.d.ts +7 -5
  5. package/dist/db/pg-migrations.d.ts +1 -1
  6. package/dist/index.js +788 -326
  7. package/dist/openapi.d.ts +216 -0
  8. package/dist/storage.js +34 -34
  9. package/hooks/codewith-native-common.test.ts +25 -25
  10. package/hooks/codewith-native-common.ts +1 -1
  11. package/hooks/hook-autoformat/package.json +1 -1
  12. package/hooks/hook-desktopnotify/package.json +1 -1
  13. package/hooks/hook-envsetup/package.json +1 -1
  14. package/hooks/hook-knowledge-context/README.md +1 -1
  15. package/hooks/hook-knowledge-context/src/hook.test.ts +1 -1
  16. package/hooks/hook-permissionguard/package.json +1 -1
  17. package/hooks/hook-protectfiles/package.json +1 -1
  18. package/hooks/hook-spiral-detector/LICENSE +191 -0
  19. package/hooks/hook-spiral-detector/README.md +23 -0
  20. package/hooks/hook-spiral-detector/package.json +12 -0
  21. package/hooks/hook-spiral-detector/src/hook.test.ts +72 -0
  22. package/hooks/hook-spiral-detector/src/hook.ts +93 -0
  23. package/hooks/hook-workspace-repos-guard/README.md +4 -3
  24. package/hooks/hook-workspace-repos-guard/src/hook.test.ts +7 -7
  25. package/hooks/hook-workspace-repos-guard/src/hook.ts +5 -3
  26. package/hooks/mention-context/src/hasna-mention-context.py +2 -2
  27. package/hooks/mention-context/src/hasna-mention-warm.py +1 -1
  28. package/hooks/pre-bash/README.md +1 -1
  29. package/hooks/prompt-guard/README.md +1 -1
  30. package/hooks/session-start/README.md +1 -1
  31. package/hooks/stop-sync/README.md +1 -1
  32. package/hooks/worktree-guard/README.md +1 -1
  33. package/package.json +10 -6
package/dist/index.js CHANGED
@@ -49,7 +49,7 @@ function up(db) {
49
49
  db.exec(idx);
50
50
  }
51
51
  }
52
- var init_001_initial = __esm(() => {
52
+ var init__001_initial = __esm(() => {
53
53
  init_schema();
54
54
  });
55
55
 
@@ -78,7 +78,7 @@ function up2(db) {
78
78
  throw err;
79
79
  }
80
80
  }
81
- var init_002_session_events = __esm(() => {
81
+ var init__002_session_events = __esm(() => {
82
82
  init_schema();
83
83
  });
84
84
 
@@ -107,7 +107,7 @@ function up3(db) {
107
107
  throw err;
108
108
  }
109
109
  }
110
- var init_003_user_prompt_submit_event = __esm(() => {
110
+ var init__003_user_prompt_submit_event = __esm(() => {
111
111
  init_schema();
112
112
  });
113
113
 
@@ -131,7 +131,7 @@ var CREATE_HOOKS_TABLE = `
131
131
  last_verified_at TEXT
132
132
  )
133
133
  `, CREATE_HOOKS_INDEXES;
134
- var init_004_hooks_table = __esm(() => {
134
+ var init__004_hooks_table = __esm(() => {
135
135
  CREATE_HOOKS_INDEXES = [
136
136
  `CREATE INDEX IF NOT EXISTS idx_hooks_name ON hooks (name)`
137
137
  ];
@@ -162,7 +162,7 @@ function up5(db) {
162
162
  throw err;
163
163
  }
164
164
  }
165
- var init_005_subagent_start_event = __esm(() => {
165
+ var init__005_subagent_start_event = __esm(() => {
166
166
  init_schema();
167
167
  });
168
168
 
@@ -194,11 +194,11 @@ function runMigrations(db) {
194
194
  }
195
195
  var MIGRATIONS;
196
196
  var init_migrations = __esm(() => {
197
- init_001_initial();
198
- init_002_session_events();
199
- init_003_user_prompt_submit_event();
200
- init_004_hooks_table();
201
- init_005_subagent_start_event();
197
+ init__001_initial();
198
+ init__002_session_events();
199
+ init__003_user_prompt_submit_event();
200
+ init__004_hooks_table();
201
+ init__005_subagent_start_event();
202
202
  MIGRATIONS = [
203
203
  { version: "001_initial", up },
204
204
  { version: "002_session_events", up: up2 },
@@ -4296,113 +4296,113 @@ var init_types = __esm(() => {
4296
4296
  // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/external.js
4297
4297
  var exports_external = {};
4298
4298
  __export(exports_external, {
4299
- void: () => voidType,
4300
- util: () => util,
4301
- unknown: () => unknownType,
4302
- union: () => unionType,
4303
- undefined: () => undefinedType,
4304
- tuple: () => tupleType,
4305
- transformer: () => effectsType,
4306
- symbol: () => symbolType,
4307
- string: () => stringType,
4308
- strictObject: () => strictObjectType,
4309
- setErrorMap: () => setErrorMap,
4310
- set: () => setType,
4311
- record: () => recordType,
4312
- quotelessJson: () => quotelessJson,
4313
- promise: () => promiseType,
4314
- preprocess: () => preprocessType,
4315
- pipeline: () => pipelineType,
4316
- ostring: () => ostring,
4317
- optional: () => optionalType,
4318
- onumber: () => onumber,
4319
- oboolean: () => oboolean,
4320
- objectUtil: () => objectUtil,
4321
- object: () => objectType,
4322
- number: () => numberType,
4323
- nullable: () => nullableType,
4324
- null: () => nullType,
4325
- never: () => neverType,
4326
- nativeEnum: () => nativeEnumType,
4327
- nan: () => nanType,
4328
- map: () => mapType,
4329
- makeIssue: () => makeIssue,
4330
- literal: () => literalType,
4331
- lazy: () => lazyType,
4332
- late: () => late,
4333
- isValid: () => isValid,
4334
- isDirty: () => isDirty,
4335
- isAsync: () => isAsync,
4336
- isAborted: () => isAborted,
4337
- intersection: () => intersectionType,
4338
- instanceof: () => instanceOfType,
4339
- getParsedType: () => getParsedType,
4340
- getErrorMap: () => getErrorMap,
4341
- function: () => functionType,
4342
- enum: () => enumType,
4343
- effect: () => effectsType,
4344
- discriminatedUnion: () => discriminatedUnionType,
4345
- defaultErrorMap: () => en_default,
4346
- datetimeRegex: () => datetimeRegex,
4347
- date: () => dateType,
4348
- custom: () => custom,
4349
- coerce: () => coerce,
4350
- boolean: () => booleanType,
4351
- bigint: () => bigIntType,
4352
- array: () => arrayType,
4353
- any: () => anyType,
4354
- addIssueToContext: () => addIssueToContext,
4355
- ZodVoid: () => ZodVoid,
4356
- ZodUnknown: () => ZodUnknown,
4357
- ZodUnion: () => ZodUnion,
4358
- ZodUndefined: () => ZodUndefined,
4359
- ZodType: () => ZodType,
4360
- ZodTuple: () => ZodTuple,
4361
- ZodTransformer: () => ZodEffects,
4362
- ZodSymbol: () => ZodSymbol,
4363
- ZodString: () => ZodString,
4364
- ZodSet: () => ZodSet,
4365
- ZodSchema: () => ZodType,
4366
- ZodRecord: () => ZodRecord,
4367
- ZodReadonly: () => ZodReadonly,
4368
- ZodPromise: () => ZodPromise,
4369
- ZodPipeline: () => ZodPipeline,
4370
- ZodParsedType: () => ZodParsedType,
4371
- ZodOptional: () => ZodOptional,
4372
- ZodObject: () => ZodObject,
4373
- ZodNumber: () => ZodNumber,
4374
- ZodNullable: () => ZodNullable,
4375
- ZodNull: () => ZodNull,
4376
- ZodNever: () => ZodNever,
4377
- ZodNativeEnum: () => ZodNativeEnum,
4378
- ZodNaN: () => ZodNaN,
4379
- ZodMap: () => ZodMap,
4380
- ZodLiteral: () => ZodLiteral,
4381
- ZodLazy: () => ZodLazy,
4382
- ZodIssueCode: () => ZodIssueCode,
4383
- ZodIntersection: () => ZodIntersection,
4384
- ZodFunction: () => ZodFunction,
4385
- ZodFirstPartyTypeKind: () => ZodFirstPartyTypeKind,
4386
- ZodError: () => ZodError,
4387
- ZodEnum: () => ZodEnum,
4388
- ZodEffects: () => ZodEffects,
4389
- ZodDiscriminatedUnion: () => ZodDiscriminatedUnion,
4390
- ZodDefault: () => ZodDefault,
4391
- ZodDate: () => ZodDate,
4392
- ZodCatch: () => ZodCatch,
4393
- ZodBranded: () => ZodBranded,
4394
- ZodBoolean: () => ZodBoolean,
4395
- ZodBigInt: () => ZodBigInt,
4396
- ZodArray: () => ZodArray,
4397
- ZodAny: () => ZodAny,
4398
- Schema: () => ZodType,
4399
- ParseStatus: () => ParseStatus,
4400
- OK: () => OK,
4401
- NEVER: () => NEVER,
4402
- INVALID: () => INVALID,
4403
- EMPTY_PATH: () => EMPTY_PATH,
4299
+ BRAND: () => BRAND,
4404
4300
  DIRTY: () => DIRTY,
4405
- BRAND: () => BRAND
4301
+ EMPTY_PATH: () => EMPTY_PATH,
4302
+ INVALID: () => INVALID,
4303
+ NEVER: () => NEVER,
4304
+ OK: () => OK,
4305
+ ParseStatus: () => ParseStatus,
4306
+ Schema: () => ZodType,
4307
+ ZodAny: () => ZodAny,
4308
+ ZodArray: () => ZodArray,
4309
+ ZodBigInt: () => ZodBigInt,
4310
+ ZodBoolean: () => ZodBoolean,
4311
+ ZodBranded: () => ZodBranded,
4312
+ ZodCatch: () => ZodCatch,
4313
+ ZodDate: () => ZodDate,
4314
+ ZodDefault: () => ZodDefault,
4315
+ ZodDiscriminatedUnion: () => ZodDiscriminatedUnion,
4316
+ ZodEffects: () => ZodEffects,
4317
+ ZodEnum: () => ZodEnum,
4318
+ ZodError: () => ZodError,
4319
+ ZodFirstPartyTypeKind: () => ZodFirstPartyTypeKind,
4320
+ ZodFunction: () => ZodFunction,
4321
+ ZodIntersection: () => ZodIntersection,
4322
+ ZodIssueCode: () => ZodIssueCode,
4323
+ ZodLazy: () => ZodLazy,
4324
+ ZodLiteral: () => ZodLiteral,
4325
+ ZodMap: () => ZodMap,
4326
+ ZodNaN: () => ZodNaN,
4327
+ ZodNativeEnum: () => ZodNativeEnum,
4328
+ ZodNever: () => ZodNever,
4329
+ ZodNull: () => ZodNull,
4330
+ ZodNullable: () => ZodNullable,
4331
+ ZodNumber: () => ZodNumber,
4332
+ ZodObject: () => ZodObject,
4333
+ ZodOptional: () => ZodOptional,
4334
+ ZodParsedType: () => ZodParsedType,
4335
+ ZodPipeline: () => ZodPipeline,
4336
+ ZodPromise: () => ZodPromise,
4337
+ ZodReadonly: () => ZodReadonly,
4338
+ ZodRecord: () => ZodRecord,
4339
+ ZodSchema: () => ZodType,
4340
+ ZodSet: () => ZodSet,
4341
+ ZodString: () => ZodString,
4342
+ ZodSymbol: () => ZodSymbol,
4343
+ ZodTransformer: () => ZodEffects,
4344
+ ZodTuple: () => ZodTuple,
4345
+ ZodType: () => ZodType,
4346
+ ZodUndefined: () => ZodUndefined,
4347
+ ZodUnion: () => ZodUnion,
4348
+ ZodUnknown: () => ZodUnknown,
4349
+ ZodVoid: () => ZodVoid,
4350
+ addIssueToContext: () => addIssueToContext,
4351
+ any: () => anyType,
4352
+ array: () => arrayType,
4353
+ bigint: () => bigIntType,
4354
+ boolean: () => booleanType,
4355
+ coerce: () => coerce,
4356
+ custom: () => custom,
4357
+ date: () => dateType,
4358
+ datetimeRegex: () => datetimeRegex,
4359
+ defaultErrorMap: () => en_default,
4360
+ discriminatedUnion: () => discriminatedUnionType,
4361
+ effect: () => effectsType,
4362
+ enum: () => enumType,
4363
+ function: () => functionType,
4364
+ getErrorMap: () => getErrorMap,
4365
+ getParsedType: () => getParsedType,
4366
+ instanceof: () => instanceOfType,
4367
+ intersection: () => intersectionType,
4368
+ isAborted: () => isAborted,
4369
+ isAsync: () => isAsync,
4370
+ isDirty: () => isDirty,
4371
+ isValid: () => isValid,
4372
+ late: () => late,
4373
+ lazy: () => lazyType,
4374
+ literal: () => literalType,
4375
+ makeIssue: () => makeIssue,
4376
+ map: () => mapType,
4377
+ nan: () => nanType,
4378
+ nativeEnum: () => nativeEnumType,
4379
+ never: () => neverType,
4380
+ null: () => nullType,
4381
+ nullable: () => nullableType,
4382
+ number: () => numberType,
4383
+ object: () => objectType,
4384
+ objectUtil: () => objectUtil,
4385
+ oboolean: () => oboolean,
4386
+ onumber: () => onumber,
4387
+ optional: () => optionalType,
4388
+ ostring: () => ostring,
4389
+ pipeline: () => pipelineType,
4390
+ preprocess: () => preprocessType,
4391
+ promise: () => promiseType,
4392
+ quotelessJson: () => quotelessJson,
4393
+ record: () => recordType,
4394
+ set: () => setType,
4395
+ setErrorMap: () => setErrorMap,
4396
+ strictObject: () => strictObjectType,
4397
+ string: () => stringType,
4398
+ symbol: () => symbolType,
4399
+ transformer: () => effectsType,
4400
+ tuple: () => tupleType,
4401
+ undefined: () => undefinedType,
4402
+ union: () => unionType,
4403
+ unknown: () => unknownType,
4404
+ util: () => util,
4405
+ void: () => voidType
4406
4406
  });
4407
4407
  var init_external = __esm(() => {
4408
4408
  init_errors();
@@ -4419,44 +4419,318 @@ var init_zod = __esm(() => {
4419
4419
  init_external();
4420
4420
  });
4421
4421
 
4422
- // src/config.ts
4423
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
4422
+ // ../contracts/dist/client/transport.js
4423
+ import { readFileSync as readFileSync2, statSync } from "fs";
4424
4424
  import { join as join3 } from "path";
4425
+ function envToken(name) {
4426
+ return name.toUpperCase().replace(/-/g, "_");
4427
+ }
4428
+ function clientTransportEnvKeys(name) {
4429
+ const envSegment = envToken(name);
4430
+ return {
4431
+ apiUrlKeys: [`HASNA_${envSegment}_API_URL`, `${envSegment}_API_URL`],
4432
+ apiKeyKeys: [`HASNA_${envSegment}_API_KEY`, `${envSegment}_API_KEY`]
4433
+ };
4434
+ }
4435
+ function credentialOverrideEnvKey(name) {
4436
+ return `HASNA_${envToken(name)}_API_KEY_OVERRIDE`;
4437
+ }
4438
+ function homeDir(env) {
4439
+ const home = env.HOME?.trim();
4440
+ return home ? home : null;
4441
+ }
4442
+ function credentialDiskSources(name, env) {
4443
+ return profileDiskSources(name, env, null);
4444
+ }
4445
+ function profileDiskSources(name, env, profile) {
4446
+ const home = homeDir(env);
4447
+ if (!home || !SAFE_APP_SLUG.test(name))
4448
+ return [];
4449
+ const stem = profile ? `${name}.${profile}` : name;
4450
+ const configStem = profile ? `${name}-${profile}` : name;
4451
+ return [
4452
+ join3(home, HASNA_STATE_DIR, FLEET_CREDENTIAL_DIR, `${stem}.env`),
4453
+ join3(home, CONFIG_DIR, CONFIG_NAMESPACE, `${configStem}-cloud.env`)
4454
+ ];
4455
+ }
4456
+ function parseEnvFile(text) {
4457
+ const values = new Map;
4458
+ for (const rawLine of text.split(/\r?\n/)) {
4459
+ const line = rawLine.trim();
4460
+ if (line.length === 0 || line.startsWith("#"))
4461
+ continue;
4462
+ const withoutExport = line.startsWith("export ") ? line.slice("export ".length).trim() : line;
4463
+ const equals = withoutExport.indexOf("=");
4464
+ if (equals <= 0)
4465
+ continue;
4466
+ const key = withoutExport.slice(0, equals).trim();
4467
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
4468
+ continue;
4469
+ let value = withoutExport.slice(equals + 1).trim();
4470
+ const quote = value[0];
4471
+ if (quote === '"' || quote === "'") {
4472
+ if (value.length < 2 || !value.endsWith(quote))
4473
+ continue;
4474
+ value = value.slice(1, -1);
4475
+ }
4476
+ if (value.length === 0)
4477
+ continue;
4478
+ values.set(key, value);
4479
+ }
4480
+ return values;
4481
+ }
4482
+ function readAppConfigFile(path) {
4483
+ let text;
4484
+ try {
4485
+ const stats = statSync(path);
4486
+ if (!stats.isFile() || stats.size > MAX_CREDENTIAL_FILE_BYTES)
4487
+ return null;
4488
+ text = readFileSync2(path, "utf8");
4489
+ } catch {
4490
+ return null;
4491
+ }
4492
+ return parseEnvFile(text);
4493
+ }
4494
+ function readCredentialFile(path, apiKeyKeys) {
4495
+ const values = readAppConfigFile(path);
4496
+ if (!values)
4497
+ return null;
4498
+ for (const key of apiKeyKeys) {
4499
+ const value = values.get(key)?.trim();
4500
+ if (value)
4501
+ return value;
4502
+ }
4503
+ return null;
4504
+ }
4505
+ function assertUsableCredential(appName, source, value) {
4506
+ if (!ILLEGAL_IN_HEADER_VALUE.test(value))
4507
+ return;
4508
+ throw new CredentialResolutionError(appName, `The credential from ${source} contains characters that cannot be sent in an HTTP header ` + `(a control character or non-ASCII byte). A file written with CR-only line endings is the usual ` + `cause. Rewrite that credential file with one LF-terminated KEY=value line. ` + `The value is not shown here, and is deliberately never logged.`, [source]);
4509
+ }
4510
+ function sealCredential(fields) {
4511
+ const { apiKey } = fields;
4512
+ const visible = {
4513
+ tier: fields.tier,
4514
+ source: fields.source,
4515
+ deliberate: fields.deliberate,
4516
+ deprecated: fields.deprecated,
4517
+ diskCandidates: Object.freeze([...fields.diskCandidates]),
4518
+ warning: fields.warning
4519
+ };
4520
+ const sealed = { ...visible };
4521
+ Object.defineProperty(sealed, "apiKey", {
4522
+ value: apiKey,
4523
+ enumerable: false,
4524
+ writable: false,
4525
+ configurable: false
4526
+ });
4527
+ Object.defineProperty(sealed, INSPECT_CUSTOM, {
4528
+ value: () => ({ ...visible, apiKey: "[redacted]" }),
4529
+ enumerable: false,
4530
+ writable: false,
4531
+ configurable: false
4532
+ });
4533
+ Object.defineProperty(sealed, CREDENTIAL_SEAL, {
4534
+ value: true,
4535
+ enumerable: false,
4536
+ writable: false,
4537
+ configurable: false
4538
+ });
4539
+ return Object.freeze(sealed);
4540
+ }
4541
+ function firstEnvValue(env, keys) {
4542
+ for (const key of keys) {
4543
+ const value = env[key]?.trim();
4544
+ if (value)
4545
+ return { key, value };
4546
+ }
4547
+ return null;
4548
+ }
4549
+ function deprecationNotified() {
4550
+ const host = globalThis;
4551
+ const existing = host[DEPRECATION_REGISTRY];
4552
+ if (existing instanceof Set)
4553
+ return existing;
4554
+ const created = new Set;
4555
+ host[DEPRECATION_REGISTRY] = created;
4556
+ return created;
4557
+ }
4558
+ function defaultDeprecationSink(message) {
4559
+ if (typeof process !== "undefined" && process.stderr) {
4560
+ process.stderr.write(`${message}
4561
+ `);
4562
+ }
4563
+ }
4564
+ function resolveCredential(name, env, options = {}) {
4565
+ const { apiKeyKeys } = clientTransportEnvKeys(name);
4566
+ const diskPaths = credentialDiskSources(name, env);
4567
+ const explicitKey = options.apiKey?.trim();
4568
+ if (explicitKey) {
4569
+ assertUsableCredential(name, "the explicit apiKey argument", explicitKey);
4570
+ return sealCredential({
4571
+ apiKey: explicitKey,
4572
+ tier: "argument",
4573
+ source: "explicit apiKey argument",
4574
+ deliberate: true,
4575
+ deprecated: false,
4576
+ diskCandidates: diskPaths,
4577
+ warning: null
4578
+ });
4579
+ }
4580
+ const overrideKeyName = credentialOverrideEnvKey(name);
4581
+ const overrideRaw = env[overrideKeyName];
4582
+ if (overrideRaw !== undefined) {
4583
+ const override = overrideRaw.trim();
4584
+ if (!override) {
4585
+ throw new CredentialResolutionError(name, `${overrideKeyName} is set but empty. It is a deliberate override, so it is not resolved around: ` + `either give it a real key or unset it to fall back to the credential on disk.`, [overrideKeyName]);
4586
+ }
4587
+ assertUsableCredential(name, overrideKeyName, override);
4588
+ return sealCredential({
4589
+ apiKey: override,
4590
+ tier: "override",
4591
+ source: overrideKeyName,
4592
+ deliberate: true,
4593
+ deprecated: false,
4594
+ diskCandidates: diskPaths,
4595
+ warning: null
4596
+ });
4597
+ }
4598
+ const profile = options.profile?.trim() || env[CREDENTIAL_PROFILE_ENV_KEY]?.trim();
4599
+ if (profile) {
4600
+ const profileSource = options.profile?.trim() ? "explicit profile argument" : CREDENTIAL_PROFILE_ENV_KEY;
4601
+ if (!SAFE_PROFILE.test(profile)) {
4602
+ throw new CredentialResolutionError(name, `Profile name from ${profileSource} is not usable in a path. ` + `Use letters, digits, dot, dash, or underscore.`, [profileSource]);
4603
+ }
4604
+ const paths = profileDiskSources(name, env, profile);
4605
+ for (const path of paths) {
4606
+ const value = readCredentialFile(path, apiKeyKeys);
4607
+ if (value) {
4608
+ assertUsableCredential(name, path, value);
4609
+ return sealCredential({
4610
+ apiKey: value,
4611
+ tier: "profile",
4612
+ source: path,
4613
+ deliberate: true,
4614
+ deprecated: false,
4615
+ diskCandidates: paths,
4616
+ warning: null
4617
+ });
4618
+ }
4619
+ }
4620
+ throw new CredentialResolutionError(name, `Profile '${profile}' (from ${profileSource}) has no ${apiKeyKeys[0]} for '${name}'. ` + `Looked in: ${paths.join(", ") || "<no HOME in this environment>"}. ` + `A profile names WHICH identity to use, so it is never resolved around \u2014 ` + `create the profile's credential file or unset ${CREDENTIAL_PROFILE_ENV_KEY}.`, paths);
4621
+ }
4622
+ const diskHits = diskPaths.map((path) => ({ path, value: readCredentialFile(path, apiKeyKeys) })).filter((hit) => hit.value !== null);
4623
+ if (diskHits.length > 0) {
4624
+ const winner = diskHits[0];
4625
+ assertUsableCredential(name, winner.path, winner.value);
4626
+ const divergentSources = [
4627
+ ...diskHits.slice(1).filter((hit) => hit.value !== winner.value).map((hit) => hit.path),
4628
+ ...(() => {
4629
+ const legacyHit = firstEnvValue(env, apiKeyKeys);
4630
+ return legacyHit && legacyHit.value !== winner.value ? [legacyHit.key] : [];
4631
+ })()
4632
+ ];
4633
+ const warning = divergentSources.length > 0 ? `Credential sources disagree for '${name}': ${winner.path} and ` + `${divergentSources.join(", ")} hold different keys. ${winner.path} wins, because a file on ` + `disk is re-read on every call while an environment variable is a snapshot. Reconcile them \u2014 ` + `a rotation that updated only one leaves the other to fail 401 wherever it is loaded first.` : null;
4634
+ return sealCredential({
4635
+ apiKey: winner.value,
4636
+ tier: "disk",
4637
+ source: winner.path,
4638
+ deliberate: false,
4639
+ deprecated: false,
4640
+ diskCandidates: diskPaths,
4641
+ warning
4642
+ });
4643
+ }
4644
+ const legacy = firstEnvValue(env, apiKeyKeys);
4645
+ if (legacy) {
4646
+ assertUsableCredential(name, legacy.key, legacy.value);
4647
+ const where = diskPaths.length > 0 ? `Put the current key in ${diskPaths[0]} \u2014 it is re-read on every call, so rotations take effect immediately.` : `This environment has no HOME, so no credential file could be consulted at all; the disk tier is ` + `unavailable here and this process will keep using the environment snapshot.`;
4648
+ const message = `[${name}] DEPRECATED: the API key came from ${legacy.key} in this process's environment. ` + `Environment variables are a snapshot taken when this process started, so a shell that started ` + `before a key rotation keeps using the old key until it exits. ${where}`;
4649
+ const sink = options.onDeprecation ?? defaultDeprecationSink;
4650
+ const notified = deprecationNotified();
4651
+ if (!notified.has(name)) {
4652
+ notified.add(name);
4653
+ sink(message);
4654
+ }
4655
+ return sealCredential({
4656
+ apiKey: legacy.value,
4657
+ tier: "legacy-env",
4658
+ source: legacy.key,
4659
+ deliberate: false,
4660
+ deprecated: true,
4661
+ diskCandidates: diskPaths,
4662
+ warning: message
4663
+ });
4664
+ }
4665
+ return null;
4666
+ }
4667
+ var CREDENTIAL_PROFILE_ENV_KEY = "HASNA_PROFILE", CredentialResolutionError, HASNA_STATE_DIR = ".hasna", FLEET_CREDENTIAL_DIR = "cloud", CONFIG_DIR = ".config", CONFIG_NAMESPACE = "hasna", MAX_CREDENTIAL_FILE_BYTES, SAFE_APP_SLUG, SAFE_PROFILE, ILLEGAL_IN_HEADER_VALUE, INSPECT_CUSTOM, CREDENTIAL_SEAL, DEPRECATION_REGISTRY, IDEMPOTENT_METHODS, AUTHORITY_OVERRIDE_HEADERS;
4668
+ var init_transport = __esm(() => {
4669
+ CredentialResolutionError = class CredentialResolutionError extends Error {
4670
+ appName;
4671
+ attempted;
4672
+ constructor(appName, message, attempted) {
4673
+ super(message);
4674
+ this.name = "CredentialResolutionError";
4675
+ this.appName = appName;
4676
+ this.attempted = attempted;
4677
+ }
4678
+ };
4679
+ MAX_CREDENTIAL_FILE_BYTES = 64 * 1024;
4680
+ SAFE_APP_SLUG = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
4681
+ SAFE_PROFILE = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;
4682
+ ILLEGAL_IN_HEADER_VALUE = /[^\t\x20-\x7e]/;
4683
+ INSPECT_CUSTOM = Symbol.for("nodejs.util.inspect.custom");
4684
+ CREDENTIAL_SEAL = Symbol.for("hasna:contracts:sealedCredential");
4685
+ DEPRECATION_REGISTRY = Symbol.for("hasna:contracts:credentialDeprecationNotices");
4686
+ IDEMPOTENT_METHODS = new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
4687
+ AUTHORITY_OVERRIDE_HEADERS = new Set([
4688
+ "host",
4689
+ ":authority",
4690
+ "forwarded",
4691
+ "x-forwarded-host",
4692
+ "x-original-host"
4693
+ ]);
4694
+ });
4695
+
4696
+ // src/config.ts
4697
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync } from "fs";
4698
+ import { join as join4 } from "path";
4425
4699
  import { homedir as homedir3 } from "os";
4426
4700
  function getHooksDataDir() {
4427
4701
  const explicit = process.env.HASNA_HOOKS_DATA_DIR ?? process.env.HOOKS_DATA_DIR;
4428
4702
  if (explicit)
4429
4703
  return explicit;
4430
- return join3(homedir3(), ".hasna", "hooks");
4704
+ return join4(homedir3(), ".hasna", "hooks");
4431
4705
  }
4432
4706
  function getCustomHooksDir() {
4433
- return join3(getHooksDataDir(), "hooks");
4707
+ return join4(getHooksDataDir(), "hooks");
4434
4708
  }
4435
4709
  function getLockPath() {
4436
4710
  const explicit = process.env.HASNA_HOOKS_LOCK_PATH ?? process.env.HOOKS_LOCK_PATH;
4437
4711
  if (explicit)
4438
4712
  return explicit;
4439
- return join3(getHooksDataDir(), "hooks.lock");
4713
+ return join4(getHooksDataDir(), "hooks.lock");
4440
4714
  }
4441
4715
  function getConfigPath() {
4442
4716
  const explicit = process.env.HASNA_HOOKS_CONFIG_PATH ?? process.env.HOOKS_CONFIG_PATH;
4443
4717
  if (explicit)
4444
4718
  return explicit;
4445
- return join3(getHooksDataDir(), "config.json");
4719
+ return join4(getHooksDataDir(), "config.json");
4446
4720
  }
4447
4721
  function readConfig() {
4448
4722
  try {
4449
4723
  const path = getConfigPath();
4450
4724
  if (!existsSync3(path))
4451
4725
  return {};
4452
- return JSON.parse(readFileSync2(path, "utf-8"));
4726
+ return JSON.parse(readFileSync3(path, "utf-8"));
4453
4727
  } catch {
4454
4728
  return {};
4455
4729
  }
4456
4730
  }
4457
4731
  function writeConfig(config) {
4458
4732
  const path = getConfigPath();
4459
- mkdirSync2(join3(path, ".."), { recursive: true });
4733
+ mkdirSync2(join4(path, ".."), { recursive: true });
4460
4734
  writeFileSync(path, JSON.stringify(config, null, 2) + `
4461
4735
  `, "utf-8");
4462
4736
  return path;
@@ -4469,9 +4743,11 @@ function resolveApiUrl() {
4469
4743
  return cfg.api_url ? cfg.api_url.replace(/\/+$/, "") : undefined;
4470
4744
  }
4471
4745
  function resolveApiKey() {
4472
- return process.env.HASNA_HOOKS_API_KEY ?? process.env.HOOKS_API_KEY;
4746
+ return resolveCredential("hooks", process.env)?.apiKey;
4473
4747
  }
4474
- var init_config = () => {};
4748
+ var init_config = __esm(() => {
4749
+ init_transport();
4750
+ });
4475
4751
 
4476
4752
  // src/lib/semver.ts
4477
4753
  var NUMERIC_ID = "(0|[1-9]\\d{0,15})", ALNUM_ID = "\\d*[A-Za-z-][0-9A-Za-z-]*", SEMVER_PATTERN;
@@ -4482,23 +4758,23 @@ var init_semver = __esm(() => {
4482
4758
  // src/lib/manifest.ts
4483
4759
  var exports_manifest = {};
4484
4760
  __export(exports_manifest, {
4485
- writeCustomHook: () => writeCustomHook,
4486
- shortManifestName: () => shortManifestName,
4487
- scriptRelFor: () => scriptRelFor,
4488
- resolveScript: () => resolveScript,
4489
- readCustomManifest: () => readCustomManifest,
4490
- parseManifest: () => parseManifest,
4491
- normalizeManifestName: () => normalizeManifestName,
4492
- manifestSchema: () => manifestSchema,
4493
- listCustomHooks: () => listCustomHooks,
4494
- isInlineScript: () => isInlineScript,
4495
- customHookDir: () => customHookDir,
4496
- assertContained: () => assertContained,
4761
+ HOOK_NAME_RE: () => HOOK_NAME_RE,
4497
4762
  ScriptContainmentError: () => ScriptContainmentError,
4498
- HOOK_NAME_RE: () => HOOK_NAME_RE
4763
+ assertContained: () => assertContained,
4764
+ customHookDir: () => customHookDir,
4765
+ isInlineScript: () => isInlineScript,
4766
+ listCustomHooks: () => listCustomHooks,
4767
+ manifestSchema: () => manifestSchema,
4768
+ normalizeManifestName: () => normalizeManifestName,
4769
+ parseManifest: () => parseManifest,
4770
+ readCustomManifest: () => readCustomManifest,
4771
+ resolveScript: () => resolveScript,
4772
+ scriptRelFor: () => scriptRelFor,
4773
+ shortManifestName: () => shortManifestName,
4774
+ writeCustomHook: () => writeCustomHook
4499
4775
  });
4500
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync3, readdirSync as readdirSync2, realpathSync, writeFileSync as writeFileSync2 } from "fs";
4501
- import { basename, dirname, isAbsolute, join as join4, normalize, resolve, sep } from "path";
4776
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, readdirSync as readdirSync2, realpathSync, writeFileSync as writeFileSync2 } from "fs";
4777
+ import { basename, dirname, isAbsolute, join as join5, normalize, resolve, sep } from "path";
4502
4778
  function parseManifest(json) {
4503
4779
  const parsed = JSON.parse(json);
4504
4780
  return manifestSchema.parse(parsed);
@@ -4530,7 +4806,7 @@ function realpathNearest(path) {
4530
4806
  tail.unshift(basename(cur));
4531
4807
  cur = parent;
4532
4808
  }
4533
- return join4(realpathSync(cur), ...tail);
4809
+ return join5(realpathSync(cur), ...tail);
4534
4810
  }
4535
4811
  function assertContainedReal(root, target, raw) {
4536
4812
  const rootReal = realpathSync(root);
@@ -4563,25 +4839,25 @@ function resolveScript(manifest, manifestDir) {
4563
4839
  if (isInlineScript(manifest))
4564
4840
  return { path: scriptRelFor(manifest), content: manifest.script };
4565
4841
  const rel = scriptRelFor(manifest);
4566
- const resolved = assertContained(join4(manifestDir, rel), manifestDir, manifest.script);
4842
+ const resolved = assertContained(join5(manifestDir, rel), manifestDir, manifest.script);
4567
4843
  if (!existsSync4(resolved)) {
4568
4844
  throw new Error(`manifest script file not found: ${resolved}`);
4569
4845
  }
4570
- return { path: rel, content: readFileSync3(resolved, "utf-8") };
4846
+ return { path: rel, content: readFileSync4(resolved, "utf-8") };
4571
4847
  }
4572
4848
  function customHookDir(name) {
4573
- return join4(getCustomHooksDir(), shortManifestName(name));
4849
+ return join5(getCustomHooksDir(), shortManifestName(name));
4574
4850
  }
4575
4851
  function readCustomManifest(name) {
4576
4852
  const dir = customHookDir(name);
4577
- const manifestPath = join4(dir, "manifest.json");
4853
+ const manifestPath = join5(dir, "manifest.json");
4578
4854
  if (!existsSync4(manifestPath))
4579
4855
  return;
4580
4856
  try {
4581
- const manifest = parseManifest(readFileSync3(manifestPath, "utf-8"));
4857
+ const manifest = parseManifest(readFileSync4(manifestPath, "utf-8"));
4582
4858
  const script = resolveScript(manifest, dir);
4583
4859
  const scriptIsInline = isInlineScript(manifest);
4584
- return { manifest, scriptPath: join4(dir, script.path), scriptContent: script.content, scriptIsInline };
4860
+ return { manifest, scriptPath: join5(dir, script.path), scriptContent: script.content, scriptIsInline };
4585
4861
  } catch (err) {
4586
4862
  if (err instanceof ScriptContainmentError)
4587
4863
  throw err;
@@ -4605,7 +4881,7 @@ function writeCustomHook(name, manifest, scriptContent, scriptRelPath) {
4605
4881
  if (isAbsolute(scriptRelPath)) {
4606
4882
  throw new ScriptContainmentError(scriptRelPath, dir);
4607
4883
  }
4608
- const scriptPath = scriptRelPath.includes("/") ? join4(dir, scriptRelPath) : join4(dir, scriptRelPath);
4884
+ const scriptPath = scriptRelPath.includes("/") ? join5(dir, scriptRelPath) : join5(dir, scriptRelPath);
4609
4885
  assertContainedLexical(scriptPath, dir, scriptRelPath);
4610
4886
  mkdirSync3(dir, { recursive: true });
4611
4887
  assertContainedReal(dir, scriptPath, scriptRelPath);
@@ -4613,7 +4889,7 @@ function writeCustomHook(name, manifest, scriptContent, scriptRelPath) {
4613
4889
  if (scriptDir !== dir)
4614
4890
  mkdirSync3(scriptDir, { recursive: true });
4615
4891
  writeFileSync2(scriptPath, scriptContent, "utf-8");
4616
- writeFileSync2(join4(dir, "manifest.json"), JSON.stringify(manifest, null, 2) + `
4892
+ writeFileSync2(join5(dir, "manifest.json"), JSON.stringify(manifest, null, 2) + `
4617
4893
  `, "utf-8");
4618
4894
  return { dir, scriptPath };
4619
4895
  }
@@ -4652,26 +4928,26 @@ var init_manifest = __esm(() => {
4652
4928
  // src/lib/store.ts
4653
4929
  var exports_store = {};
4654
4930
  __export(exports_store, {
4655
- writeLock: () => writeLock,
4656
- verifyScriptHash: () => verifyScriptHash,
4657
- upsertHookRecord: () => upsertHookRecord,
4658
- sha256Of: () => sha256Of,
4659
- sha256File: () => sha256File,
4660
- setPinnedHook: () => setPinnedHook,
4661
- retrustHook: () => retrustHook,
4662
- removePinnedHook: () => removePinnedHook,
4663
- removeHookRecord: () => removeHookRecord,
4664
- removeHookFromStore: () => removeHookFromStore,
4665
- readLock: () => readLock,
4666
- pinInstalledHook: () => pinInstalledHook,
4667
- listHookRecords: () => listHookRecords,
4668
- getPinnedHook: () => getPinnedHook,
4669
- getHookRecord: () => getHookRecord,
4931
+ LockFileError: () => LockFileError,
4670
4932
  checkScriptHash: () => checkScriptHash,
4671
- LockFileError: () => LockFileError
4933
+ getHookRecord: () => getHookRecord,
4934
+ getPinnedHook: () => getPinnedHook,
4935
+ listHookRecords: () => listHookRecords,
4936
+ pinInstalledHook: () => pinInstalledHook,
4937
+ readLock: () => readLock,
4938
+ removeHookFromStore: () => removeHookFromStore,
4939
+ removeHookRecord: () => removeHookRecord,
4940
+ removePinnedHook: () => removePinnedHook,
4941
+ retrustHook: () => retrustHook,
4942
+ setPinnedHook: () => setPinnedHook,
4943
+ sha256File: () => sha256File,
4944
+ sha256Of: () => sha256Of,
4945
+ upsertHookRecord: () => upsertHookRecord,
4946
+ verifyScriptHash: () => verifyScriptHash,
4947
+ writeLock: () => writeLock
4672
4948
  });
4673
4949
  import { createHash } from "crypto";
4674
- import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync } from "fs";
4950
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync3, renameSync } from "fs";
4675
4951
  import { readFile } from "fs/promises";
4676
4952
  import { randomBytes } from "crypto";
4677
4953
  function sha256Of(input) {
@@ -4737,7 +5013,7 @@ function readLock() {
4737
5013
  return { hooks: {} };
4738
5014
  let raw;
4739
5015
  try {
4740
- raw = readFileSync4(path, "utf-8");
5016
+ raw = readFileSync5(path, "utf-8");
4741
5017
  } catch {
4742
5018
  throw new LockFileError(`hooks.lock (${path}) could not be read. Repair: fix permissions or move the file aside and run 'hooks sync'.`);
4743
5019
  }
@@ -4834,7 +5110,7 @@ async function verifyScriptHash(name, scriptPath) {
4834
5110
  return checkScriptHash(name, await sha256File(scriptPath));
4835
5111
  }
4836
5112
  function retrustHook(name, scriptPath, version, source) {
4837
- const actual = sha256Of(readFileSync4(scriptPath));
5113
+ const actual = sha256Of(readFileSync5(scriptPath));
4838
5114
  const db = getDb();
4839
5115
  upsertHookRecord(db, {
4840
5116
  name,
@@ -4861,24 +5137,24 @@ var init_store = __esm(() => {
4861
5137
  // src/lib/profiles.ts
4862
5138
  var exports_profiles = {};
4863
5139
  __export(exports_profiles, {
4864
- updateProfile: () => updateProfile,
4865
- touchProfile: () => touchProfile,
4866
- listProfiles: () => listProfiles,
4867
- importProfiles: () => importProfiles,
4868
- getProfilesDir: () => getProfilesDir,
4869
- getProfile: () => getProfile,
4870
- exportProfiles: () => exportProfiles,
5140
+ createProfile: () => createProfile,
4871
5141
  deleteProfile: () => deleteProfile,
4872
- createProfile: () => createProfile
5142
+ exportProfiles: () => exportProfiles,
5143
+ getProfile: () => getProfile,
5144
+ getProfilesDir: () => getProfilesDir,
5145
+ importProfiles: () => importProfiles,
5146
+ listProfiles: () => listProfiles,
5147
+ touchProfile: () => touchProfile,
5148
+ updateProfile: () => updateProfile
4873
5149
  });
4874
- import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5, readdirSync as readdirSync3, rmSync as rmSync2, cpSync as cpSync2 } from "fs";
4875
- import { join as join7 } from "path";
5150
+ import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync5, readdirSync as readdirSync3, rmSync as rmSync2, cpSync as cpSync2 } from "fs";
5151
+ import { join as join8 } from "path";
4876
5152
  import { homedir as homedir5 } from "os";
4877
5153
  function resolveProfilesDir() {
4878
- const newDir = join7(homedir5(), ".hasna", "hooks", "profiles");
4879
- const oldDir = join7(homedir5(), ".hooks", "profiles");
5154
+ const newDir = join8(homedir5(), ".hasna", "hooks", "profiles");
5155
+ const oldDir = join8(homedir5(), ".hooks", "profiles");
4880
5156
  if (!existsSync8(newDir) && existsSync8(oldDir)) {
4881
- mkdirSync6(join7(homedir5(), ".hasna", "hooks"), { recursive: true });
5157
+ mkdirSync6(join8(homedir5(), ".hasna", "hooks"), { recursive: true });
4882
5158
  cpSync2(oldDir, newDir, { recursive: true });
4883
5159
  }
4884
5160
  return newDir;
@@ -4889,7 +5165,7 @@ function ensureProfilesDir() {
4889
5165
  }
4890
5166
  }
4891
5167
  function profilePath(id) {
4892
- return join7(PROFILES_DIR, `${id}.json`);
5168
+ return join8(PROFILES_DIR, `${id}.json`);
4893
5169
  }
4894
5170
  function shortUuid() {
4895
5171
  return crypto.randomUUID().slice(0, 8);
@@ -4920,7 +5196,7 @@ function getProfile(id) {
4920
5196
  try {
4921
5197
  if (!existsSync8(path))
4922
5198
  return null;
4923
- return JSON.parse(readFileSync6(path, "utf-8"));
5199
+ return JSON.parse(readFileSync7(path, "utf-8"));
4924
5200
  } catch {
4925
5201
  return null;
4926
5202
  }
@@ -4933,7 +5209,7 @@ function listProfiles() {
4933
5209
  const profiles = [];
4934
5210
  for (const file of files) {
4935
5211
  try {
4936
- const content = readFileSync6(join7(PROFILES_DIR, file), "utf-8");
5212
+ const content = readFileSync7(join8(PROFILES_DIR, file), "utf-8");
4937
5213
  profiles.push(JSON.parse(content));
4938
5214
  } catch {}
4939
5215
  }
@@ -5002,8 +5278,8 @@ var init_profiles = __esm(() => {
5002
5278
  });
5003
5279
 
5004
5280
  // src/lib/hook-env.ts
5005
- import { dirname as dirname4, isAbsolute as isAbsolute2 } from "path";
5006
- import { existsSync as existsSync10, statSync } from "fs";
5281
+ import { dirname as dirname5, isAbsolute as isAbsolute2 } from "path";
5282
+ import { existsSync as existsSync10, statSync as statSync2 } from "fs";
5007
5283
  function isDeniedEnvName(name) {
5008
5284
  const upper = name.toUpperCase();
5009
5285
  if (DENY_PREFIXES.some((prefix) => upper.startsWith(prefix)))
@@ -5030,7 +5306,7 @@ function isUnsafePathEntry(entry, home) {
5030
5306
  if (entry === "/var/tmp" || entry.startsWith("/var/tmp/"))
5031
5307
  return true;
5032
5308
  try {
5033
- if ((statSync(entry).mode & 2) !== 0)
5309
+ if ((statSync2(entry).mode & 2) !== 0)
5034
5310
  return true;
5035
5311
  } catch {}
5036
5312
  return false;
@@ -5039,7 +5315,7 @@ function sanitizeHookPath(pathValue, home) {
5039
5315
  const baseline = PATH_BASELINE.slice();
5040
5316
  if (process.platform === "darwin" && existsSync10("/opt/homebrew/bin"))
5041
5317
  baseline.push("/opt/homebrew/bin");
5042
- const bunDir = dirname4(process.execPath);
5318
+ const bunDir = dirname5(process.execPath);
5043
5319
  const seen = new Set;
5044
5320
  const out = [];
5045
5321
  for (const dir of baseline) {
@@ -5158,12 +5434,12 @@ var init_hook_env = __esm(() => {
5158
5434
  // src/lib/run.ts
5159
5435
  var exports_run = {};
5160
5436
  __export(exports_run, {
5161
- executeVerifiedScript: () => executeVerifiedScript,
5162
- HookTimeoutError: () => HookTimeoutError
5437
+ HookTimeoutError: () => HookTimeoutError,
5438
+ executeVerifiedScript: () => executeVerifiedScript
5163
5439
  });
5164
5440
  import { randomBytes as randomBytes2 } from "crypto";
5165
5441
  import { rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
5166
- import { basename as basename2, dirname as dirname5, isAbsolute as isAbsolute3, join as join9 } from "path";
5442
+ import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute3, join as join11 } from "path";
5167
5443
  function extensionOf(scriptPath) {
5168
5444
  const base = basename2(scriptPath);
5169
5445
  const dot = base.lastIndexOf(".");
@@ -5232,9 +5508,9 @@ async function readPipeWithDeadline(stream, deadlineMs) {
5232
5508
  return chunks.join("");
5233
5509
  }
5234
5510
  async function executeVerifiedScript(options) {
5235
- const scriptDir = dirname5(options.scriptPath);
5511
+ const scriptDir = dirname6(options.scriptPath);
5236
5512
  const interpreter = interpreterFor(options.name, options.scriptPath, options.content);
5237
- const tempPath = join9(scriptDir, `.hooks-verified-${randomBytes2(12).toString("hex")}.${interpreter.tempExt}`);
5513
+ const tempPath = join11(scriptDir, `.hooks-verified-${randomBytes2(12).toString("hex")}.${interpreter.tempExt}`);
5238
5514
  let proc = null;
5239
5515
  let timedOut = false;
5240
5516
  const timeoutTimer = options.timeout ? setTimeout(() => {
@@ -5321,10 +5597,10 @@ var init_run = __esm(() => {
5321
5597
  // src/lib/db-writer.ts
5322
5598
  var exports_db_writer = {};
5323
5599
  __export(exports_db_writer, {
5324
- writeHookEvent: () => writeHookEvent,
5325
- resolveEventType: () => resolveEventType,
5600
+ normalizeEventType: () => normalizeEventType,
5326
5601
  recordHookRun: () => recordHookRun,
5327
- normalizeEventType: () => normalizeEventType
5602
+ resolveEventType: () => resolveEventType,
5603
+ writeHookEvent: () => writeHookEvent
5328
5604
  });
5329
5605
  function nanoid2() {
5330
5606
  return crypto.randomUUID().replace(/-/g, "").slice(0, 21);
@@ -6223,6 +6499,16 @@ var HOOKS = [
6223
6499
  matcher: "",
6224
6500
  tags: ["errors", "failures", "logging", "debugging"]
6225
6501
  },
6502
+ {
6503
+ name: "spiral-detector",
6504
+ displayName: "Spiral Detector",
6505
+ description: "Interrupts sessions after five consecutive identical command failures",
6506
+ version: "0.1.0",
6507
+ category: "Observability",
6508
+ event: "PostToolUse",
6509
+ matcher: "",
6510
+ tags: ["spiral", "failures", "retries", "safety", "interrupt"]
6511
+ },
6226
6512
  {
6227
6513
  name: "stylescheck",
6228
6514
  displayName: "Styles Check",
@@ -6388,21 +6674,21 @@ function getHook(name) {
6388
6674
  return HOOKS.find((h) => h.name === name);
6389
6675
  }
6390
6676
  // src/lib/installer.ts
6391
- import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync4 } from "fs";
6392
- import { join as join6, dirname as dirname3 } from "path";
6677
+ import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync6, rmSync, writeFileSync as writeFileSync4 } from "fs";
6678
+ import { join as join7, dirname as dirname3 } from "path";
6393
6679
  import { homedir as homedir4 } from "os";
6394
6680
  import { fileURLToPath as fileURLToPath2 } from "url";
6395
6681
 
6396
6682
  // src/lib/resolve.ts
6397
- import { dirname as dirname2, join as join5 } from "path";
6683
+ import { dirname as dirname2, join as join6 } from "path";
6398
6684
  import { existsSync as existsSync5 } from "fs";
6399
6685
  import { fileURLToPath } from "url";
6400
6686
  init_manifest();
6401
6687
  function resolveBundledHooksDir(moduleDir = dirname2(fileURLToPath(import.meta.url))) {
6402
- const sourceLayout = join5(moduleDir, "..", "..", "hooks");
6403
- if (existsSync5(join5(sourceLayout, "hook-gitguard")))
6688
+ const sourceLayout = join6(moduleDir, "..", "..", "hooks");
6689
+ if (existsSync5(join6(sourceLayout, "hook-gitguard")))
6404
6690
  return sourceLayout;
6405
- return join5(moduleDir, "..", "hooks");
6691
+ return join6(moduleDir, "..", "hooks");
6406
6692
  }
6407
6693
  function resolveHookDir(name) {
6408
6694
  const custom2 = readCustomManifest(name);
@@ -6413,10 +6699,10 @@ function resolveHookDir(name) {
6413
6699
  return;
6414
6700
  const short = shortManifestName(name);
6415
6701
  const bundledBase = resolveBundledHooksDir();
6416
- const direct = join5(bundledBase, short);
6702
+ const direct = join6(bundledBase, short);
6417
6703
  if (existsSync5(direct))
6418
6704
  return direct;
6419
- const prefixed = join5(bundledBase, `hook-${short}`);
6705
+ const prefixed = join6(bundledBase, `hook-${short}`);
6420
6706
  return existsSync5(prefixed) ? prefixed : undefined;
6421
6707
  }
6422
6708
  function resolveScriptPath(name) {
@@ -6426,7 +6712,7 @@ function resolveScriptPath(name) {
6426
6712
  const dir = resolveHookDir(name);
6427
6713
  if (!dir)
6428
6714
  return;
6429
- const script = join5(dir, "src", "hook.ts");
6715
+ const script = join6(dir, "src", "hook.ts");
6430
6716
  return existsSync5(script) ? script : undefined;
6431
6717
  }
6432
6718
  function resolveHookMeta(name) {
@@ -6482,7 +6768,7 @@ function resolveHook(name) {
6482
6768
  init_manifest();
6483
6769
  init_store();
6484
6770
  var __dirname2 = dirname3(fileURLToPath2(import.meta.url));
6485
- var HOOKS_DIR = existsSync7(join6(__dirname2, "..", "..", "hooks", "hook-gitguard")) ? join6(__dirname2, "..", "..", "hooks") : join6(__dirname2, "..", "hooks");
6771
+ var HOOKS_DIR = existsSync7(join7(__dirname2, "..", "..", "hooks", "hook-gitguard")) ? join7(__dirname2, "..", "..", "hooks") : join7(__dirname2, "..", "hooks");
6486
6772
  function normalizeHookName(name) {
6487
6773
  return name.startsWith("hook-") ? name : `hook-${name}`;
6488
6774
  }
@@ -6552,16 +6838,16 @@ function getSettingsPath(scope = "global", target = "claude", codewithConfigPath
6552
6838
  return globalOverride;
6553
6839
  const dir = getTargetSettingsDir(target);
6554
6840
  if (scope === "project") {
6555
- return target === "codewith" ? join6(process.cwd(), dir, "config.toml") : join6(process.cwd(), dir, "settings.json");
6841
+ return target === "codewith" ? join7(process.cwd(), dir, "config.toml") : join7(process.cwd(), dir, "settings.json");
6556
6842
  }
6557
- return target === "codewith" ? join6(homedir4(), dir, "config.toml") : join6(homedir4(), dir, "settings.json");
6843
+ return target === "codewith" ? join7(homedir4(), dir, "config.toml") : join7(homedir4(), dir, "settings.json");
6558
6844
  }
6559
6845
  function getHookPath(name) {
6560
6846
  const shortName = shortHookName(name);
6561
6847
  const resolved = resolveHookDir(shortName);
6562
6848
  if (resolved)
6563
6849
  return resolved;
6564
- return join6(HOOKS_DIR, normalizeHookName(shortName));
6850
+ return join7(HOOKS_DIR, normalizeHookName(shortName));
6565
6851
  }
6566
6852
  function hookExists(name) {
6567
6853
  return existsSync7(getHookPath(name));
@@ -6570,7 +6856,7 @@ function readSettings(scope = "global", target = "claude") {
6570
6856
  const path = getSettingsPath(scope, target);
6571
6857
  try {
6572
6858
  if (existsSync7(path)) {
6573
- return JSON.parse(readFileSync5(path, "utf-8"));
6859
+ return JSON.parse(readFileSync6(path, "utf-8"));
6574
6860
  }
6575
6861
  } catch (error) {
6576
6862
  console.warn(`[hooks] Failed to read settings at ${path}: ${error instanceof Error ? error.message : String(error)}`);
@@ -6669,7 +6955,7 @@ function buildCodewithTomlFragment(name, profile) {
6669
6955
  function readCodewithConfig(scope, configPath) {
6670
6956
  const path = getSettingsPath(scope, "codewith", configPath);
6671
6957
  try {
6672
- return existsSync7(path) ? readFileSync5(path, "utf-8") : "";
6958
+ return existsSync7(path) ? readFileSync6(path, "utf-8") : "";
6673
6959
  } catch {
6674
6960
  return "";
6675
6961
  }
@@ -6679,7 +6965,7 @@ function appendCodewithFragment(fragment, scope, configPath) {
6679
6965
  const dir = dirname3(path);
6680
6966
  if (!existsSync7(dir))
6681
6967
  mkdirSync5(dir, { recursive: true });
6682
- const existing = existsSync7(path) ? readFileSync5(path, "utf-8") : "";
6968
+ const existing = existsSync7(path) ? readFileSync6(path, "utf-8") : "";
6683
6969
  const sep2 = existing.trim() ? `
6684
6970
 
6685
6971
  ` : "";
@@ -6724,7 +7010,7 @@ function installForTarget(name, scope, overwrite, target, profile, codewithMode
6724
7010
  target,
6725
7011
  fragment,
6726
7012
  applied: false,
6727
- note: "Codewith install is fragment-only by default; open-configs should own applying this TOML."
7013
+ note: "Codewith install is fragment-only by default; configs should own applying this TOML."
6728
7014
  };
6729
7015
  }
6730
7016
  if (!codewithConfigPath) {
@@ -6751,7 +7037,7 @@ function installForTarget(name, scope, overwrite, target, profile, codewithMode
6751
7037
  fragment,
6752
7038
  applied: true,
6753
7039
  configPath: path,
6754
- note: "Direct Codewith config write was explicitly requested; prefer open-configs for managed machines."
7040
+ note: "Direct Codewith config write was explicitly requested; prefer configs for managed machines."
6755
7041
  };
6756
7042
  } catch (error) {
6757
7043
  return {
@@ -6983,7 +7269,7 @@ function codewithHasHookEntry(name, scope = "global") {
6983
7269
  if (!existsSync7(path))
6984
7270
  return false;
6985
7271
  try {
6986
- return codewithHasHookEntryInText(readFileSync5(path, "utf-8"), name);
7272
+ return codewithHasHookEntryInText(readFileSync6(path, "utf-8"), name);
6987
7273
  } catch {
6988
7274
  return false;
6989
7275
  }
@@ -7015,7 +7301,7 @@ function uninstallHook(name, scope = "global", target = "claude") {
7015
7301
  if (target === "codewith" || target === "all") {
7016
7302
  const codewithPath = getSettingsPath(scope, "codewith");
7017
7303
  if (existsSync7(codewithPath)) {
7018
- const before = readFileSync5(codewithPath, "utf-8");
7304
+ const before = readFileSync6(codewithPath, "utf-8");
7019
7305
  const after = removeCodewithHookEntry(before, shortName);
7020
7306
  if (after.removed) {
7021
7307
  writeFileSync4(codewithPath, after.text, "utf-8");
@@ -7059,20 +7345,20 @@ init_profiles();
7059
7345
  init_config();
7060
7346
  init_store();
7061
7347
  init_manifest();
7062
- import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
7348
+ import { existsSync as existsSync11, readFileSync as readFileSync11 } from "fs";
7063
7349
 
7064
7350
  // src/lib/custom-install.ts
7065
7351
  init_config();
7066
7352
  init_manifest();
7067
- import { existsSync as existsSync9, mkdirSync as mkdirSync7, mkdtempSync, readFileSync as readFileSync7, rmSync as rmSync3 } from "fs";
7068
- import { join as join8 } from "path";
7353
+ import { existsSync as existsSync9, mkdirSync as mkdirSync7, mkdtempSync, readFileSync as readFileSync8, rmSync as rmSync3 } from "fs";
7354
+ import { join as join9 } from "path";
7069
7355
  import { tmpdir } from "os";
7070
7356
  function manifestAt(dir) {
7071
- const path = join8(dir, "manifest.json");
7357
+ const path = join9(dir, "manifest.json");
7072
7358
  if (!existsSync9(path))
7073
7359
  return;
7074
7360
  try {
7075
- return parseManifest(readFileSync7(path, "utf-8"));
7361
+ return parseManifest(readFileSync8(path, "utf-8"));
7076
7362
  } catch {
7077
7363
  return;
7078
7364
  }
@@ -7091,8 +7377,8 @@ function writeFromManifest(manifest, srcDir, source, kind) {
7091
7377
  return { name, version: manifest.version, kind, source, dir, scriptPath };
7092
7378
  }
7093
7379
  async function cloneToTemp(gitUrl) {
7094
- const tmp = mkdtempSync(join8(tmpdir(), "hooks-install-"));
7095
- const proc = Bun.spawn(["git", "clone", "--depth", "1", "--quiet", gitUrl, join8(tmp, "repo")], {
7380
+ const tmp = mkdtempSync(join9(tmpdir(), "hooks-install-"));
7381
+ const proc = Bun.spawn(["git", "clone", "--depth", "1", "--quiet", gitUrl, join9(tmp, "repo")], {
7096
7382
  stdout: "pipe",
7097
7383
  stderr: "pipe"
7098
7384
  });
@@ -7105,7 +7391,7 @@ async function cloneToTemp(gitUrl) {
7105
7391
  rmSync3(tmp, { recursive: true, force: true });
7106
7392
  throw new Error(`git clone failed (${exitCode}): ${stderr.trim()}`);
7107
7393
  }
7108
- return join8(tmp, "repo");
7394
+ return join9(tmp, "repo");
7109
7395
  }
7110
7396
  function isGitUrl(value) {
7111
7397
  return value.startsWith("git@") || value.startsWith("ssh://") || value.startsWith("file://") || value.endsWith(".git");
@@ -7133,7 +7419,7 @@ async function installCustomSource(source) {
7133
7419
  throw new Error(`No such local path: ${source}`);
7134
7420
  }
7135
7421
  if (source.endsWith("manifest.json")) {
7136
- const manifest = parseManifest(readFileSync7(source, "utf-8"));
7422
+ const manifest = parseManifest(readFileSync8(source, "utf-8"));
7137
7423
  return writeFromManifest(manifest, source.substring(0, source.lastIndexOf("/")), source, "local");
7138
7424
  }
7139
7425
  return copyManifestDir(source, source, "local");
@@ -7164,7 +7450,7 @@ async function installManifestUrl(url) {
7164
7450
  }
7165
7451
  // src/lib/sync.ts
7166
7452
  import { createHash as createHash2 } from "crypto";
7167
- import { readFileSync as readFileSync8 } from "fs";
7453
+ import { readFileSync as readFileSync9 } from "fs";
7168
7454
  init_config();
7169
7455
  init_db();
7170
7456
  init_store();
@@ -7173,7 +7459,7 @@ function sha256OfText(text) {
7173
7459
  return createHash2("sha256").update(text).digest("hex");
7174
7460
  }
7175
7461
  function sha256OfPath(path) {
7176
- return createHash2("sha256").update(readFileSync8(path)).digest("hex");
7462
+ return createHash2("sha256").update(readFileSync9(path)).digest("hex");
7177
7463
  }
7178
7464
  function collectBundledCatalog() {
7179
7465
  const out = [];
@@ -7428,6 +7714,9 @@ async function fetchPinnedHook(name, version, apiUrl) {
7428
7714
  init_manifest();
7429
7715
  init_store();
7430
7716
  init_config();
7717
+ import { readFileSync as readFileSync10 } from "fs";
7718
+ import { dirname as dirname4, join as join10 } from "path";
7719
+ import { fileURLToPath as fileURLToPath3 } from "url";
7431
7720
 
7432
7721
  // src/lib/secure-compare.ts
7433
7722
  function secureEqual(a, b) {
@@ -7442,11 +7731,170 @@ function secureEqual(a, b) {
7442
7731
  return diff === 0;
7443
7732
  }
7444
7733
 
7734
+ // src/openapi.ts
7735
+ var openApiDocument = {
7736
+ openapi: "3.1.0",
7737
+ info: {
7738
+ title: "Hooks Registry API",
7739
+ version: "0.1.0",
7740
+ description: "Local registry HTTP API for @hasna/hooks: catalog, lock, and artifact reads are open; publishing (PUT /api/v1/hooks) requires the API key (authorization: Bearer or x-api-key)."
7741
+ },
7742
+ servers: [{ url: "/" }],
7743
+ paths: {
7744
+ "/health": {
7745
+ get: {
7746
+ summary: "Liveness probe",
7747
+ operationId: "getHealth",
7748
+ responses: {
7749
+ "200": {
7750
+ description: "Service is alive",
7751
+ content: {
7752
+ "application/json": {
7753
+ schema: { type: "object", additionalProperties: true }
7754
+ }
7755
+ }
7756
+ }
7757
+ }
7758
+ }
7759
+ },
7760
+ "/ready": {
7761
+ get: {
7762
+ summary: "Readiness probe \u2014 the hooks.lock store is readable",
7763
+ operationId: "getReady",
7764
+ responses: {
7765
+ "200": { description: "Lock store is readable" },
7766
+ "503": { description: "Lock store is not readable" }
7767
+ }
7768
+ }
7769
+ },
7770
+ "/version": {
7771
+ get: {
7772
+ summary: "Package version",
7773
+ operationId: "getVersion",
7774
+ responses: {
7775
+ "200": {
7776
+ description: "The installed @hasna/hooks version",
7777
+ content: {
7778
+ "application/json": {
7779
+ schema: { type: "object", properties: { version: { type: "string" } } }
7780
+ }
7781
+ }
7782
+ }
7783
+ }
7784
+ }
7785
+ },
7786
+ "/openapi.json": {
7787
+ get: {
7788
+ summary: "This OpenAPI document",
7789
+ operationId: "getOpenApi",
7790
+ responses: { "200": { description: "OpenAPI 3.1 document" } }
7791
+ }
7792
+ },
7793
+ "/api/v1/catalog": {
7794
+ get: {
7795
+ summary: "List the full hook catalog with versions and sha256",
7796
+ operationId: "getCatalog",
7797
+ responses: {
7798
+ "200": {
7799
+ description: "Catalog entries",
7800
+ content: {
7801
+ "application/json": {
7802
+ schema: {
7803
+ type: "object",
7804
+ properties: {
7805
+ hooks: {
7806
+ type: "array",
7807
+ items: {
7808
+ type: "object",
7809
+ properties: {
7810
+ name: { type: "string" },
7811
+ version: { type: "string" },
7812
+ sha256: { type: "string" },
7813
+ events: { type: "array", items: { type: "string" } }
7814
+ }
7815
+ }
7816
+ }
7817
+ }
7818
+ }
7819
+ }
7820
+ }
7821
+ }
7822
+ }
7823
+ }
7824
+ },
7825
+ "/api/v1/lock": {
7826
+ get: {
7827
+ summary: "Read the published hooks.lock with per-hook versions",
7828
+ operationId: "getLock",
7829
+ responses: { "200": { description: "The lock file" } }
7830
+ }
7831
+ },
7832
+ "/api/v1/hooks/{name}/{version}": {
7833
+ get: {
7834
+ summary: "Fetch one hook's manifest and script",
7835
+ operationId: "getHookArtifact",
7836
+ parameters: [
7837
+ { name: "name", in: "path", required: true, schema: { type: "string" } },
7838
+ { name: "version", in: "path", required: true, schema: { type: "string" } }
7839
+ ],
7840
+ responses: {
7841
+ "200": {
7842
+ description: "Hook artifact; x-hook-sha256 header carries the script hash"
7843
+ },
7844
+ "400": { description: "Invalid URL encoding or semver version" },
7845
+ "404": { description: "Hook not found locally" }
7846
+ }
7847
+ }
7848
+ },
7849
+ "/api/v1/hooks": {
7850
+ put: {
7851
+ summary: "Publish/retrust a hook (requires the API key)",
7852
+ operationId: "putHook",
7853
+ requestBody: {
7854
+ required: true,
7855
+ content: {
7856
+ "application/json": {
7857
+ schema: {
7858
+ type: "object",
7859
+ properties: { name: { type: "string" }, version: { type: "string" } }
7860
+ }
7861
+ }
7862
+ }
7863
+ },
7864
+ responses: {
7865
+ "200": { description: "Hook retrusted and pin updated" },
7866
+ "401": { description: "Missing or invalid API key" },
7867
+ "404": { description: "Hook not found in local store" },
7868
+ "409": { description: "Version mismatch with the local store" }
7869
+ },
7870
+ security: [{ apiKey: [] }]
7871
+ }
7872
+ }
7873
+ },
7874
+ components: {
7875
+ securitySchemes: {
7876
+ apiKey: {
7877
+ type: "http",
7878
+ scheme: "bearer",
7879
+ description: "The registry API key (HASNA_HOOKS_API_KEY / HOOKS_API_KEY)."
7880
+ }
7881
+ }
7882
+ }
7883
+ };
7884
+
7445
7885
  // src/serve.ts
7446
7886
  init_semver();
7447
7887
  var DEFAULT_SERVE_PORT = 39428;
7448
7888
  var SERVE_HOST = "127.0.0.1";
7449
7889
  var SERVE_SERVICE_NAME = "hooks-registry";
7890
+ function packageVersion() {
7891
+ try {
7892
+ const pkgPath = join10(dirname4(fileURLToPath3(import.meta.url)), "..", "package.json");
7893
+ return JSON.parse(readFileSync10(pkgPath, "utf-8")).version || "0.0.0";
7894
+ } catch {
7895
+ return "0.0.0";
7896
+ }
7897
+ }
7450
7898
  async function buildCatalog() {
7451
7899
  const byName = new Map;
7452
7900
  for (const meta of HOOKS) {
@@ -7519,6 +7967,20 @@ function handleServeRequest(req, apiKey) {
7519
7967
  if (url.pathname === "/health" && req.method === "GET") {
7520
7968
  return Promise.resolve(json({ status: "ok", name: SERVE_SERVICE_NAME }));
7521
7969
  }
7970
+ if (url.pathname === "/ready" && req.method === "GET") {
7971
+ try {
7972
+ readLock();
7973
+ return Promise.resolve(json({ status: "ready", name: SERVE_SERVICE_NAME }));
7974
+ } catch {
7975
+ return Promise.resolve(json({ status: "not-ready", name: SERVE_SERVICE_NAME }, 503));
7976
+ }
7977
+ }
7978
+ if (url.pathname === "/version" && req.method === "GET") {
7979
+ return Promise.resolve(json({ version: packageVersion() }));
7980
+ }
7981
+ if (url.pathname === "/openapi.json" && req.method === "GET") {
7982
+ return Promise.resolve(json(openApiDocument));
7983
+ }
7522
7984
  if (url.pathname === "/api/v1/catalog" && req.method === "GET") {
7523
7985
  return buildCatalog().then((catalog) => json({ hooks: catalog }));
7524
7986
  }
@@ -7693,7 +8155,7 @@ async function runHook(name, input, options = {}) {
7693
8155
  if (!script)
7694
8156
  throw new Error(`Hook script not found: ${name}`);
7695
8157
  const { sha256Of: sha256Of2, checkScriptHash: checkScriptHash2 } = await Promise.resolve().then(() => (init_store(), exports_store));
7696
- const content = readFileSync9(script);
8158
+ const content = readFileSync11(script);
7697
8159
  const check = checkScriptHash2(name, sha256Of2(content));
7698
8160
  if (!check.ok) {
7699
8161
  throw new Error(`Hook '${name}' script changed since it was trusted (sha256 ${check.expected} != ${check.actual}). Run 'hooks trust ${name}' to trust the new content.`);
@@ -7782,102 +8244,102 @@ async function runHook(name, input, options = {}) {
7782
8244
  return { output, stderr: stderrText, exitCode };
7783
8245
  }
7784
8246
  export {
7785
- writeLock,
7786
- writeCustomHook,
7787
- writeConfig,
7788
- verifyScriptHash,
7789
- upsertHookRecord,
7790
- updateProfile,
7791
- uninstallHook,
7792
- touchProfile,
7793
- syncHooks,
7794
- storageSync,
7795
- storagePush,
7796
- storagePull,
7797
- startServeServer,
7798
- sha256Of,
7799
- sha256File,
7800
- setPinnedHook,
7801
- searchHooks,
7802
- runStorageMigrations,
7803
- runHook,
7804
- retrustHook,
7805
- resolveTables,
7806
- resolveScriptPath,
7807
- resolveScript,
7808
- resolveHookMeta,
7809
- resolveHookDir,
7810
- resolveHook,
7811
- resolveApiUrl,
7812
- resolveApiKey,
7813
- removeProjectHook,
7814
- removePinnedHook,
7815
- removeHookRecord,
7816
- removeHookFromStore,
7817
- removeHook,
7818
- readLock,
7819
- readCustomManifest,
7820
- readConfig,
7821
- provisionCloudflareResources,
7822
- planSync,
7823
- pinInstalledHook,
7824
- parseStorageTables,
7825
- parseManifest,
7826
- listProjectHooks,
7827
- listProfiles,
7828
- listHookRecords,
7829
- listCustomHooks,
7830
- isEventSupported,
7831
- isCustomSource,
7832
- installHooksForProject,
7833
- installHooks,
7834
- installHookForProject,
7835
- installHook,
7836
- installCustomSource,
7837
- importProfiles,
7838
- hookExists,
7839
- handleServeRequest,
7840
- getSyncMetaAll,
7841
- getStorageStatus,
7842
- getStoragePg,
7843
- getStorageDatabaseUrl,
7844
- getStorageDatabaseEnvName,
7845
- getStorageDatabaseEnv,
7846
- getStorageBackend,
7847
- getSettingsPath,
7848
- getRegisteredHooksForTarget,
7849
- getRegisteredHooks,
7850
- getProfilesDir,
7851
- getProfile,
7852
- getPinnedHook,
7853
- getLockPath,
7854
- getInstalledHooks,
7855
- getHooksDataDir,
7856
- getHooksByCategory,
7857
- getHookRecord,
7858
- getHookPath,
7859
- getHookEvents,
7860
- getHook,
7861
- getCustomHooksDir,
7862
- getConfigPath,
7863
- fetchPinnedHook,
7864
- exportProfiles,
7865
- deleteProfile,
7866
- createProfile,
7867
- checkScriptHash,
7868
- buildCodewithTomlFragment,
7869
- STORAGE_TABLES,
7870
- STORAGE_DATABASE_ENV,
7871
- STORAGE_BACKEND_ENV,
7872
- STORAGE_BACKENDS,
7873
- RETIRED_STORAGE_MODE_ENV,
7874
- HOOK_EVENTS,
7875
- HOOKS_STORAGE_TABLES,
7876
- HOOKS_STORAGE_FALLBACK_ENV,
7877
- HOOKS_STORAGE_ENV,
7878
- HOOKS_STORAGE_BACKEND_FALLBACK_ENV,
7879
- HOOKS_STORAGE_BACKEND_ENV,
7880
- HOOKS,
8247
+ CATEGORIES,
7881
8248
  DEFAULT_SERVE_PORT,
7882
- CATEGORIES
8249
+ HOOKS,
8250
+ HOOKS_STORAGE_BACKEND_ENV,
8251
+ HOOKS_STORAGE_BACKEND_FALLBACK_ENV,
8252
+ HOOKS_STORAGE_ENV,
8253
+ HOOKS_STORAGE_FALLBACK_ENV,
8254
+ HOOKS_STORAGE_TABLES,
8255
+ HOOK_EVENTS,
8256
+ RETIRED_STORAGE_MODE_ENV,
8257
+ STORAGE_BACKENDS,
8258
+ STORAGE_BACKEND_ENV,
8259
+ STORAGE_DATABASE_ENV,
8260
+ STORAGE_TABLES,
8261
+ buildCodewithTomlFragment,
8262
+ checkScriptHash,
8263
+ createProfile,
8264
+ deleteProfile,
8265
+ exportProfiles,
8266
+ fetchPinnedHook,
8267
+ getConfigPath,
8268
+ getCustomHooksDir,
8269
+ getHook,
8270
+ getHookEvents,
8271
+ getHookPath,
8272
+ getHookRecord,
8273
+ getHooksByCategory,
8274
+ getHooksDataDir,
8275
+ getInstalledHooks,
8276
+ getLockPath,
8277
+ getPinnedHook,
8278
+ getProfile,
8279
+ getProfilesDir,
8280
+ getRegisteredHooks,
8281
+ getRegisteredHooksForTarget,
8282
+ getSettingsPath,
8283
+ getStorageBackend,
8284
+ getStorageDatabaseEnv,
8285
+ getStorageDatabaseEnvName,
8286
+ getStorageDatabaseUrl,
8287
+ getStoragePg,
8288
+ getStorageStatus,
8289
+ getSyncMetaAll,
8290
+ handleServeRequest,
8291
+ hookExists,
8292
+ importProfiles,
8293
+ installCustomSource,
8294
+ installHook,
8295
+ installHookForProject,
8296
+ installHooks,
8297
+ installHooksForProject,
8298
+ isCustomSource,
8299
+ isEventSupported,
8300
+ listCustomHooks,
8301
+ listHookRecords,
8302
+ listProfiles,
8303
+ listProjectHooks,
8304
+ parseManifest,
8305
+ parseStorageTables,
8306
+ pinInstalledHook,
8307
+ planSync,
8308
+ provisionCloudflareResources,
8309
+ readConfig,
8310
+ readCustomManifest,
8311
+ readLock,
8312
+ removeHook,
8313
+ removeHookFromStore,
8314
+ removeHookRecord,
8315
+ removePinnedHook,
8316
+ removeProjectHook,
8317
+ resolveApiKey,
8318
+ resolveApiUrl,
8319
+ resolveHook,
8320
+ resolveHookDir,
8321
+ resolveHookMeta,
8322
+ resolveScript,
8323
+ resolveScriptPath,
8324
+ resolveTables,
8325
+ retrustHook,
8326
+ runHook,
8327
+ runStorageMigrations,
8328
+ searchHooks,
8329
+ setPinnedHook,
8330
+ sha256File,
8331
+ sha256Of,
8332
+ startServeServer,
8333
+ storagePull,
8334
+ storagePush,
8335
+ storageSync,
8336
+ syncHooks,
8337
+ touchProfile,
8338
+ uninstallHook,
8339
+ updateProfile,
8340
+ upsertHookRecord,
8341
+ verifyScriptHash,
8342
+ writeConfig,
8343
+ writeCustomHook,
8344
+ writeLock
7883
8345
  };