@hasna/instructions 0.4.36 → 0.4.42

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 (57) hide show
  1. package/README.md +9 -8
  2. package/assets/skills/inbox/SKILL.md +86 -0
  3. package/dashboard/README.md +34 -70
  4. package/dist/cli/index.js +1673 -516
  5. package/dist/cli/raw-store-root.test.d.ts +2 -0
  6. package/dist/cli/raw-store-root.test.d.ts.map +1 -0
  7. package/dist/data/config-store.d.ts +4 -4
  8. package/dist/data/config-store.d.ts.map +1 -1
  9. package/dist/db/configs.d.ts.map +1 -1
  10. package/dist/db/database.d.ts.map +1 -1
  11. package/dist/generated/storage-kit/backend.d.ts +20 -0
  12. package/dist/generated/storage-kit/backend.d.ts.map +1 -0
  13. package/dist/generated/storage-kit/index.d.ts +2 -2
  14. package/dist/generated/storage-kit/index.d.ts.map +1 -1
  15. package/dist/generated/storage-kit/migrations.d.ts.map +1 -1
  16. package/dist/generated/storage-kit/own.d.ts +11 -0
  17. package/dist/generated/storage-kit/own.d.ts.map +1 -0
  18. package/dist/generated/storage-kit/pool.d.ts +7 -6
  19. package/dist/generated/storage-kit/pool.d.ts.map +1 -1
  20. package/dist/generated/storage-kit/tls.d.ts +30 -3
  21. package/dist/generated/storage-kit/tls.d.ts.map +1 -1
  22. package/dist/index.d.ts +4 -2
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +945 -372
  25. package/dist/lib/instruction-graph.d.ts.map +1 -1
  26. package/dist/lib/managed-skill-runtimes.d.ts +80 -0
  27. package/dist/lib/managed-skill-runtimes.d.ts.map +1 -0
  28. package/dist/lib/managed-skill-runtimes.test.d.ts +2 -0
  29. package/dist/lib/managed-skill-runtimes.test.d.ts.map +1 -0
  30. package/dist/lib/raw-store-root.d.ts +17 -0
  31. package/dist/lib/raw-store-root.d.ts.map +1 -0
  32. package/dist/lib/retired-storage-mode.d.ts +8 -0
  33. package/dist/lib/retired-storage-mode.d.ts.map +1 -0
  34. package/dist/lib/session-apply.d.ts.map +1 -1
  35. package/dist/lib/session-authority.d.ts +27 -0
  36. package/dist/lib/session-authority.d.ts.map +1 -0
  37. package/dist/lib/session-authority.test.d.ts +2 -0
  38. package/dist/lib/session-authority.test.d.ts.map +1 -0
  39. package/dist/lib/session-render.d.ts +7 -3
  40. package/dist/lib/session-render.d.ts.map +1 -1
  41. package/dist/mcp/index.js +177 -115
  42. package/dist/server/cloud-auth.test.d.ts +2 -0
  43. package/dist/server/cloud-auth.test.d.ts.map +1 -0
  44. package/dist/server/cloud.d.ts +7 -4
  45. package/dist/server/cloud.d.ts.map +1 -1
  46. package/dist/server/cloud.test.d.ts +2 -0
  47. package/dist/server/cloud.test.d.ts.map +1 -0
  48. package/dist/server/index.d.ts +1 -1
  49. package/dist/server/index.js +781 -325
  50. package/dist/status.d.ts +11 -1
  51. package/dist/status.d.ts.map +1 -1
  52. package/dist/storage/cloud-store.d.ts.map +1 -1
  53. package/dist/storage/cloud-store.test.d.ts +2 -0
  54. package/dist/storage/cloud-store.test.d.ts.map +1 -0
  55. package/package.json +10 -7
  56. package/dist/generated/storage-kit/mode.d.ts +0 -48
  57. package/dist/generated/storage-kit/mode.d.ts.map +0 -1
@@ -16,7 +16,7 @@ var __export = (target, all) => {
16
16
  };
17
17
  var __require = import.meta.require;
18
18
 
19
- // node_modules/hono/dist/compose.js
19
+ // ../../node_modules/.bun/hono@4.13.1/node_modules/hono/dist/compose.js
20
20
  var compose = (middleware, onError, onNotFound) => {
21
21
  return (context, next) => {
22
22
  let index = -1;
@@ -60,21 +60,42 @@ var compose = (middleware, onError, onNotFound) => {
60
60
  };
61
61
  };
62
62
 
63
- // node_modules/hono/dist/request/constants.js
63
+ // ../../node_modules/.bun/hono@4.13.1/node_modules/hono/dist/request/constants.js
64
64
  var GET_MATCH_RESULT = /* @__PURE__ */ Symbol();
65
65
 
66
- // node_modules/hono/dist/utils/body.js
66
+ // ../../node_modules/.bun/hono@4.13.1/node_modules/hono/dist/utils/buffer.js
67
+ var bufferToFormData = (arrayBuffer, contentType) => {
68
+ const response = new Response(arrayBuffer, {
69
+ headers: {
70
+ "Content-Type": contentType.replace(/^[^;]+/, (mediaType) => mediaType.toLowerCase())
71
+ }
72
+ });
73
+ return response.formData();
74
+ };
75
+
76
+ // ../../node_modules/.bun/hono@4.13.1/node_modules/hono/dist/utils/body.js
77
+ var isRawRequest = (request) => ("headers" in request);
67
78
  var parseBody = async (request, options = /* @__PURE__ */ Object.create(null)) => {
68
79
  const { all = false, dot = false } = options;
69
- const headers = request instanceof HonoRequest ? request.raw.headers : request.headers;
80
+ const headers = isRawRequest(request) ? request.headers : request.raw.headers;
70
81
  const contentType = headers.get("Content-Type");
71
- if (contentType?.startsWith("multipart/form-data") || contentType?.startsWith("application/x-www-form-urlencoded")) {
82
+ const mediaType = contentType?.split(";")[0].trim().toLowerCase();
83
+ if (mediaType === "multipart/form-data" || mediaType === "application/x-www-form-urlencoded") {
72
84
  return parseFormData(request, { all, dot });
73
85
  }
74
86
  return {};
75
87
  };
76
88
  async function parseFormData(request, options) {
77
- const formData = await request.formData();
89
+ if (!isRawRequest(request) && request.bodyCache.formData) {
90
+ return convertFormDataToBodyData(await request.bodyCache.formData, options);
91
+ }
92
+ const headers = isRawRequest(request) ? request.headers : request.raw.headers;
93
+ const arrayBuffer = await request.arrayBuffer();
94
+ const formDataPromise = bufferToFormData(arrayBuffer, headers.get("Content-Type") || "");
95
+ if (!isRawRequest(request)) {
96
+ request.bodyCache.formData = formDataPromise;
97
+ }
98
+ const formData = await formDataPromise;
78
99
  if (formData) {
79
100
  return convertFormDataToBodyData(formData, options);
80
101
  }
@@ -134,7 +155,7 @@ var handleParsingNestedValues = (form, key, value) => {
134
155
  });
135
156
  };
136
157
 
137
- // node_modules/hono/dist/utils/url.js
158
+ // ../../node_modules/.bun/hono@4.13.1/node_modules/hono/dist/utils/url.js
138
159
  var splitPath = (path) => {
139
160
  const paths = path.split("/");
140
161
  if (paths[0] === "") {
@@ -256,18 +277,16 @@ var checkOptionalParameter = (path) => {
256
277
  });
257
278
  return results.filter((v, i, a) => a.indexOf(v) === i);
258
279
  };
280
+ var tryDecodeURIComponent = (str) => str.indexOf("%") !== -1 ? tryDecode(str, decodeURIComponent_) : str;
259
281
  var _decodeURI = (value) => {
260
- if (!/[%+]/.test(value)) {
261
- return value;
262
- }
263
282
  if (value.indexOf("+") !== -1) {
264
283
  value = value.replace(/\+/g, " ");
265
284
  }
266
- return value.indexOf("%") !== -1 ? tryDecode(value, decodeURIComponent_) : value;
285
+ return tryDecodeURIComponent(value);
267
286
  };
268
287
  var _getQueryParam = (url, key, multiple) => {
269
288
  let encoded;
270
- if (!multiple && key && !/[%+]/.test(key)) {
289
+ if (!multiple && key && key.indexOf("%") === -1 && key.indexOf("+") === -1) {
271
290
  let keyIndex2 = url.indexOf("?", 8);
272
291
  if (keyIndex2 === -1) {
273
292
  return;
@@ -291,7 +310,7 @@ var _getQueryParam = (url, key, multiple) => {
291
310
  return;
292
311
  }
293
312
  }
294
- const results = {};
313
+ const results = /* @__PURE__ */ Object.create(null);
295
314
  encoded ??= /[%+]/.test(url);
296
315
  let keyIndex = url.indexOf("?", 8);
297
316
  while (keyIndex !== -1) {
@@ -334,8 +353,7 @@ var getQueryParams = (url, key) => {
334
353
  };
335
354
  var decodeURIComponent_ = decodeURIComponent;
336
355
 
337
- // node_modules/hono/dist/request.js
338
- var tryDecodeURIComponent = (str) => tryDecode(str, decodeURIComponent_);
356
+ // ../../node_modules/.bun/hono@4.13.1/node_modules/hono/dist/request.js
339
357
  var HonoRequest = class {
340
358
  raw;
341
359
  #validatedData;
@@ -347,7 +365,6 @@ var HonoRequest = class {
347
365
  this.raw = request;
348
366
  this.path = path;
349
367
  this.#matchResult = matchResult;
350
- this.#validatedData = {};
351
368
  }
352
369
  param(key) {
353
370
  return key ? this.#getDecodedParam(key) : this.#getAllDecodedParams();
@@ -355,7 +372,7 @@ var HonoRequest = class {
355
372
  #getDecodedParam(key) {
356
373
  const paramKey = this.#matchResult[0][this.routeIndex][1][key];
357
374
  const param = this.#getParamValue(paramKey);
358
- return param && /\%/.test(param) ? tryDecodeURIComponent(param) : param;
375
+ return param && tryDecodeURIComponent(param);
359
376
  }
360
377
  #getAllDecodedParams() {
361
378
  const decoded = {};
@@ -363,7 +380,7 @@ var HonoRequest = class {
363
380
  for (const key of keys) {
364
381
  const value = this.#getParamValue(this.#matchResult[0][this.routeIndex][1][key]);
365
382
  if (value !== undefined) {
366
- decoded[key] = /\%/.test(value) ? tryDecodeURIComponent(value) : value;
383
+ decoded[key] = tryDecodeURIComponent(value);
367
384
  }
368
385
  }
369
386
  return decoded;
@@ -381,7 +398,7 @@ var HonoRequest = class {
381
398
  if (name) {
382
399
  return this.raw.headers.get(name) ?? undefined;
383
400
  }
384
- const headerData = {};
401
+ const headerData = /* @__PURE__ */ Object.create(null);
385
402
  this.raw.headers.forEach((value, key) => {
386
403
  headerData[key] = value;
387
404
  });
@@ -396,8 +413,7 @@ var HonoRequest = class {
396
413
  if (cachedBody) {
397
414
  return cachedBody;
398
415
  }
399
- const anyCachedKey = Object.keys(bodyCache)[0];
400
- if (anyCachedKey) {
416
+ for (const anyCachedKey in bodyCache) {
401
417
  return bodyCache[anyCachedKey].then((body) => {
402
418
  if (anyCachedKey === "json") {
403
419
  body = JSON.stringify(body);
@@ -416,6 +432,9 @@ var HonoRequest = class {
416
432
  arrayBuffer() {
417
433
  return this.#cachedBody("arrayBuffer");
418
434
  }
435
+ bytes() {
436
+ return this.#cachedBody("arrayBuffer").then((buffer) => new Uint8Array(buffer));
437
+ }
419
438
  blob() {
420
439
  return this.#cachedBody("blob");
421
440
  }
@@ -423,10 +442,10 @@ var HonoRequest = class {
423
442
  return this.#cachedBody("formData");
424
443
  }
425
444
  addValidatedData(target, data) {
426
- this.#validatedData[target] = data;
445
+ (this.#validatedData ??= {})[target] = data;
427
446
  }
428
447
  valid(target) {
429
- return this.#validatedData[target];
448
+ return this.#validatedData?.[target];
430
449
  }
431
450
  get url() {
432
451
  return this.raw.url;
@@ -445,7 +464,7 @@ var HonoRequest = class {
445
464
  }
446
465
  };
447
466
 
448
- // node_modules/hono/dist/utils/html.js
467
+ // ../../node_modules/.bun/hono@4.13.1/node_modules/hono/dist/utils/html.js
449
468
  var HtmlEscapedCallbackPhase = {
450
469
  Stringify: 1,
451
470
  BeforeStream: 2,
@@ -483,7 +502,7 @@ var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) =>
483
502
  }
484
503
  };
485
504
 
486
- // node_modules/hono/dist/context.js
505
+ // ../../node_modules/.bun/hono@4.13.1/node_modules/hono/dist/context.js
487
506
  var TEXT_PLAIN = "text/plain; charset=UTF-8";
488
507
  var setDefaultContentType = (contentType, headers) => {
489
508
  return {
@@ -601,11 +620,11 @@ var Context = class {
601
620
  return Object.fromEntries(this.#var);
602
621
  }
603
622
  #newResponse(data, arg, headers) {
604
- const responseHeaders = this.#res ? new Headers(this.#res.headers) : this.#preparedHeaders ?? new Headers;
605
- if (typeof arg === "object" && "headers" in arg) {
606
- const argHeaders = arg.headers instanceof Headers ? arg.headers : new Headers(arg.headers);
607
- for (const [key, value] of argHeaders) {
608
- if (key.toLowerCase() === "set-cookie") {
623
+ let responseHeaders = this.#res ? new Headers(this.#res.headers) : this.#preparedHeaders;
624
+ if (typeof arg === "object" && arg.headers) {
625
+ responseHeaders ??= new Headers;
626
+ for (const [key, value] of new Headers(arg.headers)) {
627
+ if (key === "set-cookie") {
609
628
  responseHeaders.append(key, value);
610
629
  } else {
611
630
  responseHeaders.set(key, value);
@@ -613,19 +632,34 @@ var Context = class {
613
632
  }
614
633
  }
615
634
  if (headers) {
616
- for (const [k, v] of Object.entries(headers)) {
617
- if (typeof v === "string") {
618
- responseHeaders.set(k, v);
619
- } else {
620
- responseHeaders.delete(k);
621
- for (const v2 of v) {
622
- responseHeaders.append(k, v2);
635
+ if (!responseHeaders) {
636
+ let count = 0;
637
+ for (const k in headers) {
638
+ if (++count > 1 || typeof headers[k] !== "string") {
639
+ responseHeaders = new Headers;
640
+ break;
641
+ }
642
+ }
643
+ }
644
+ if (responseHeaders) {
645
+ for (const k in headers) {
646
+ const v = headers[k];
647
+ if (typeof v === "string") {
648
+ responseHeaders.set(k, v);
649
+ } else {
650
+ responseHeaders.delete(k);
651
+ for (const v2 of v) {
652
+ responseHeaders.append(k, v2);
653
+ }
623
654
  }
624
655
  }
625
656
  }
626
657
  }
627
658
  const status = typeof arg === "number" ? arg : arg?.status ?? this.#status;
628
- return createResponseInstance(data, { status, headers: responseHeaders });
659
+ return createResponseInstance(data, {
660
+ status,
661
+ headers: responseHeaders ?? headers
662
+ });
629
663
  }
630
664
  newResponse = (...args) => this.#newResponse(...args);
631
665
  body = (data, arg, headers) => this.#newResponse(data, arg, headers);
@@ -650,18 +684,18 @@ var Context = class {
650
684
  };
651
685
  };
652
686
 
653
- // node_modules/hono/dist/router.js
687
+ // ../../node_modules/.bun/hono@4.13.1/node_modules/hono/dist/router.js
654
688
  var METHOD_NAME_ALL = "ALL";
655
689
  var METHOD_NAME_ALL_LOWERCASE = "all";
656
- var METHODS = ["get", "post", "put", "delete", "options", "patch"];
690
+ var METHODS = ["get", "post", "put", "delete", "options", "patch", "query"];
657
691
  var MESSAGE_MATCHER_IS_ALREADY_BUILT = "Can not add a route since the matcher is already built.";
658
692
  var UnsupportedPathError = class extends Error {
659
693
  };
660
694
 
661
- // node_modules/hono/dist/utils/constants.js
695
+ // ../../node_modules/.bun/hono@4.13.1/node_modules/hono/dist/utils/constants.js
662
696
  var COMPOSED_HANDLER = "__COMPOSED_HANDLER";
663
697
 
664
- // node_modules/hono/dist/hono-base.js
698
+ // ../../node_modules/.bun/hono@4.13.1/node_modules/hono/dist/hono-base.js
665
699
  var notFoundHandler = (c) => {
666
700
  return c.text("404 Not Found", 404);
667
701
  };
@@ -680,6 +714,7 @@ var Hono = class _Hono {
680
714
  delete;
681
715
  options;
682
716
  patch;
717
+ query;
683
718
  all;
684
719
  on;
685
720
  use;
@@ -752,7 +787,7 @@ var Hono = class _Hono {
752
787
  handler = async (c, next) => (await compose([], app.errorHandler)(c, () => r.handler(c, next))).res;
753
788
  handler[COMPOSED_HANDLER] = r.handler;
754
789
  }
755
- subApp.#addRoute(r.method, r.path, handler);
790
+ subApp.#addRoute(r.method, r.path, handler, r.basePath);
756
791
  });
757
792
  return this;
758
793
  }
@@ -799,7 +834,7 @@ var Hono = class _Hono {
799
834
  const pathPrefixLength = mergedPath === "/" ? 0 : mergedPath.length;
800
835
  return (request) => {
801
836
  const url = new URL(request.url);
802
- url.pathname = url.pathname.slice(pathPrefixLength) || "/";
837
+ url.pathname = this.getPath(request).slice(pathPrefixLength) || "/";
803
838
  return new Request(url, request);
804
839
  };
805
840
  })();
@@ -813,10 +848,15 @@ var Hono = class _Hono {
813
848
  this.#addRoute(METHOD_NAME_ALL, mergePath(path, "*"), handler);
814
849
  return this;
815
850
  }
816
- #addRoute(method, path, handler) {
851
+ #addRoute(method, path, handler, baseRoutePath) {
817
852
  method = method.toUpperCase();
818
853
  path = mergePath(this._basePath, path);
819
- const r = { basePath: this._basePath, path, method, handler };
854
+ const r = {
855
+ basePath: baseRoutePath !== undefined ? mergePath(this._basePath, baseRoutePath) : this._basePath,
856
+ path,
857
+ method,
858
+ handler
859
+ };
820
860
  this.router.add(method, path, [handler, r]);
821
861
  this.routes.push(r);
822
862
  }
@@ -880,7 +920,7 @@ var Hono = class _Hono {
880
920
  };
881
921
  };
882
922
 
883
- // node_modules/hono/dist/router/reg-exp-router/matcher.js
923
+ // ../../node_modules/.bun/hono@4.13.1/node_modules/hono/dist/router/reg-exp-router/matcher.js
884
924
  var emptyParam = [];
885
925
  function match(method, path) {
886
926
  const matchers = this.buildAllMatchers();
@@ -901,7 +941,7 @@ function match(method, path) {
901
941
  return match2(method, path);
902
942
  }
903
943
 
904
- // node_modules/hono/dist/router/reg-exp-router/node.js
944
+ // ../../node_modules/.bun/hono@4.13.1/node_modules/hono/dist/router/reg-exp-router/node.js
905
945
  var LABEL_REG_EXP_STR = "[^/]+";
906
946
  var ONLY_WILDCARD_REG_EXP_STR = ".*";
907
947
  var TAIL_WILDCARD_REG_EXP_STR = "(?:|/.*)";
@@ -915,7 +955,7 @@ function compareKey(a, b) {
915
955
  return 1;
916
956
  }
917
957
  if (a === ONLY_WILDCARD_REG_EXP_STR || a === TAIL_WILDCARD_REG_EXP_STR) {
918
- return 1;
958
+ return b === TAIL_WILDCARD_REG_EXP_STR ? -1 : 1;
919
959
  } else if (b === ONLY_WILDCARD_REG_EXP_STR || b === TAIL_WILDCARD_REG_EXP_STR) {
920
960
  return -1;
921
961
  }
@@ -930,69 +970,68 @@ var Node = class _Node {
930
970
  #index;
931
971
  #varIndex;
932
972
  #children = /* @__PURE__ */ Object.create(null);
933
- insert(tokens, index, paramMap, context, pathErrorCheckOnly) {
934
- if (tokens.length === 0) {
935
- if (this.#index !== undefined) {
936
- throw PATH_ERROR;
937
- }
938
- if (pathErrorCheckOnly) {
939
- return;
940
- }
941
- this.#index = index;
942
- return;
943
- }
944
- const [token, ...restTokens] = tokens;
945
- const pattern = token === "*" ? restTokens.length === 0 ? ["", "", ONLY_WILDCARD_REG_EXP_STR] : ["", "", LABEL_REG_EXP_STR] : token === "/*" ? ["", "", TAIL_WILDCARD_REG_EXP_STR] : token.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
946
- let node;
947
- if (pattern) {
948
- const name = pattern[1];
949
- let regexpStr = pattern[2] || LABEL_REG_EXP_STR;
950
- if (name && pattern[2]) {
951
- if (regexpStr === ".*") {
952
- throw PATH_ERROR;
953
- }
954
- regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:");
955
- if (/\((?!\?:)/.test(regexpStr)) {
956
- throw PATH_ERROR;
957
- }
958
- }
959
- node = this.#children[regexpStr];
960
- if (!node) {
961
- if (Object.keys(this.#children).some((k) => k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR)) {
962
- throw PATH_ERROR;
973
+ insert(tokens, index, paramMap, context, isStatic) {
974
+ let node = this;
975
+ for (let i = 0, len = tokens.length;i < len; i++) {
976
+ const token = tokens[i];
977
+ const pattern = token.length === 1 ? token === "*" ? i === len - 1 ? ["", "", ONLY_WILDCARD_REG_EXP_STR] : ["", "", LABEL_REG_EXP_STR] : null : token === "/*" ? ["", "", TAIL_WILDCARD_REG_EXP_STR] : token.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
978
+ let nextNode;
979
+ if (pattern) {
980
+ const name = pattern[1];
981
+ let regexpStr = pattern[2] || LABEL_REG_EXP_STR;
982
+ if (name && pattern[2]) {
983
+ if (regexpStr === ".*") {
984
+ throw PATH_ERROR;
985
+ }
986
+ regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:");
987
+ if (/\((?!\?:)/.test(regexpStr)) {
988
+ throw PATH_ERROR;
989
+ }
990
+ if (regexpStr.length === 1 && regExpMetaChars.has(regexpStr)) {
991
+ throw PATH_ERROR;
992
+ }
963
993
  }
964
- if (pathErrorCheckOnly) {
965
- return;
994
+ nextNode = node.#children[regexpStr];
995
+ if (!nextNode) {
996
+ if (regexpStr !== ONLY_WILDCARD_REG_EXP_STR && regexpStr !== TAIL_WILDCARD_REG_EXP_STR) {
997
+ for (const k in node.#children) {
998
+ if ((regexpStr.length > 1 || k.length > 1) && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR) {
999
+ throw PATH_ERROR;
1000
+ }
1001
+ }
1002
+ }
1003
+ nextNode = node.#children[regexpStr] = new _Node;
966
1004
  }
967
- node = this.#children[regexpStr] = new _Node;
968
1005
  if (name !== "") {
969
- node.#varIndex = context.varIndex++;
1006
+ nextNode.#varIndex ??= context.varIndex++;
1007
+ paramMap.push([name, nextNode.#varIndex]);
970
1008
  }
971
- }
972
- if (!pathErrorCheckOnly && name !== "") {
973
- paramMap.push([name, node.#varIndex]);
974
- }
975
- } else {
976
- node = this.#children[token];
977
- if (!node) {
978
- if (Object.keys(this.#children).some((k) => k.length > 1 && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR)) {
979
- throw PATH_ERROR;
980
- }
981
- if (pathErrorCheckOnly) {
982
- return;
1009
+ } else {
1010
+ nextNode = node.#children[token];
1011
+ if (!nextNode) {
1012
+ for (const k in node.#children) {
1013
+ if (k.length > 1 && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR) {
1014
+ throw PATH_ERROR;
1015
+ }
1016
+ }
1017
+ nextNode = node.#children[token] = new _Node;
983
1018
  }
984
- node = this.#children[token] = new _Node;
985
1019
  }
1020
+ node = nextNode;
1021
+ }
1022
+ if (node.#index !== undefined) {
1023
+ throw PATH_ERROR;
986
1024
  }
987
- node.insert(restTokens, index, paramMap, context, pathErrorCheckOnly);
1025
+ node.#index = isStatic ? -1 : index;
988
1026
  }
989
1027
  buildRegExpStr() {
990
1028
  const childKeys = Object.keys(this.#children).sort(compareKey);
991
1029
  const strList = childKeys.map((k) => {
992
1030
  const c = this.#children[k];
993
- return (typeof c.#varIndex === "number" ? `(${k})@${c.#varIndex}` : regExpMetaChars.has(k) ? `\\${k}` : k) + c.buildRegExpStr();
994
- });
995
- if (typeof this.#index === "number") {
1031
+ const childStr = c.buildRegExpStr();
1032
+ return childStr === "" ? "" : (typeof c.#varIndex === "number" ? `(${k})@${c.#varIndex}` : regExpMetaChars.has(k) ? `\\${k}` : k) + childStr;
1033
+ }).filter(Boolean);
1034
+ if (typeof this.#index === "number" && this.#index !== -1) {
996
1035
  strList.unshift(`#${this.#index}`);
997
1036
  }
998
1037
  if (strList.length === 0) {
@@ -1005,16 +1044,23 @@ var Node = class _Node {
1005
1044
  }
1006
1045
  };
1007
1046
 
1008
- // node_modules/hono/dist/router/reg-exp-router/trie.js
1047
+ // ../../node_modules/.bun/hono@4.13.1/node_modules/hono/dist/router/reg-exp-router/trie.js
1009
1048
  var Trie = class {
1010
1049
  #context = { varIndex: 0 };
1011
1050
  #root = new Node;
1012
- insert(path, index, pathErrorCheckOnly) {
1051
+ #index = 0;
1052
+ paths = /* @__PURE__ */ Object.create(null);
1053
+ insert(path, isStatic) {
1054
+ if (isStatic) {
1055
+ this.#root.insert(path.split(""), 0, [], this.#context, true);
1056
+ return;
1057
+ }
1013
1058
  const paramAssoc = [];
1014
1059
  const groups = [];
1060
+ let markedPath = path;
1015
1061
  for (let i = 0;; ) {
1016
1062
  let replaced = false;
1017
- path = path.replace(/\{[^}]+\}/g, (m) => {
1063
+ markedPath = markedPath.replace(/\{[^}]+\}/g, (m) => {
1018
1064
  const mark = `@\\${i}`;
1019
1065
  groups[i] = [mark, m];
1020
1066
  i++;
@@ -1025,7 +1071,7 @@ var Trie = class {
1025
1071
  break;
1026
1072
  }
1027
1073
  }
1028
- const tokens = path.match(/(?::[^\/]+)|(?:\/\*$)|./g) || [];
1074
+ const tokens = markedPath.match(/(?::[^\/]+)|(?:\/\*$)|./g) || [];
1029
1075
  for (let i = groups.length - 1;i >= 0; i--) {
1030
1076
  const [mark] = groups[i];
1031
1077
  for (let j = tokens.length - 1;j >= 0; j--) {
@@ -1035,8 +1081,8 @@ var Trie = class {
1035
1081
  }
1036
1082
  }
1037
1083
  }
1038
- this.#root.insert(tokens, index, paramAssoc, this.#context, pathErrorCheckOnly);
1039
- return paramAssoc;
1084
+ this.#root.insert(tokens, this.#index, paramAssoc, this.#context, false);
1085
+ this.paths[path] = [this.#index++, paramAssoc];
1040
1086
  }
1041
1087
  buildRegExp() {
1042
1088
  let regexp = this.#root.buildRegExpStr();
@@ -1061,8 +1107,7 @@ var Trie = class {
1061
1107
  }
1062
1108
  };
1063
1109
 
1064
- // node_modules/hono/dist/router/reg-exp-router/router.js
1065
- var nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)];
1110
+ // ../../node_modules/.bun/hono@4.13.1/node_modules/hono/dist/router/reg-exp-router/router.js
1066
1111
  var wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
1067
1112
  function buildWildcardRegExp(path) {
1068
1113
  return wildcardRegExpCache[path] ??= new RegExp(path === "*" ? "" : `^${path.replace(/\/\*$|([.\\+*[^\]$()])/g, (_, metaChar) => metaChar ? `\\${metaChar}` : "(?:|/.*)")}$`);
@@ -1070,59 +1115,6 @@ function buildWildcardRegExp(path) {
1070
1115
  function clearWildcardRegExpCache() {
1071
1116
  wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
1072
1117
  }
1073
- function buildMatcherFromPreprocessedRoutes(routes) {
1074
- const trie = new Trie;
1075
- const handlerData = [];
1076
- if (routes.length === 0) {
1077
- return nullMatcher;
1078
- }
1079
- const routesWithStaticPathFlag = routes.map((route) => [!/\*|\/:/.test(route[0]), ...route]).sort(([isStaticA, pathA], [isStaticB, pathB]) => isStaticA ? 1 : isStaticB ? -1 : pathA.length - pathB.length);
1080
- const staticMap = /* @__PURE__ */ Object.create(null);
1081
- for (let i = 0, j = -1, len = routesWithStaticPathFlag.length;i < len; i++) {
1082
- const [pathErrorCheckOnly, path, handlers] = routesWithStaticPathFlag[i];
1083
- if (pathErrorCheckOnly) {
1084
- staticMap[path] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];
1085
- } else {
1086
- j++;
1087
- }
1088
- let paramAssoc;
1089
- try {
1090
- paramAssoc = trie.insert(path, j, pathErrorCheckOnly);
1091
- } catch (e) {
1092
- throw e === PATH_ERROR ? new UnsupportedPathError(path) : e;
1093
- }
1094
- if (pathErrorCheckOnly) {
1095
- continue;
1096
- }
1097
- handlerData[j] = handlers.map(([h, paramCount]) => {
1098
- const paramIndexMap = /* @__PURE__ */ Object.create(null);
1099
- paramCount -= 1;
1100
- for (;paramCount >= 0; paramCount--) {
1101
- const [key, value] = paramAssoc[paramCount];
1102
- paramIndexMap[key] = value;
1103
- }
1104
- return [h, paramIndexMap];
1105
- });
1106
- }
1107
- const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
1108
- for (let i = 0, len = handlerData.length;i < len; i++) {
1109
- for (let j = 0, len2 = handlerData[i].length;j < len2; j++) {
1110
- const map = handlerData[i][j]?.[1];
1111
- if (!map) {
1112
- continue;
1113
- }
1114
- const keys = Object.keys(map);
1115
- for (let k = 0, len3 = keys.length;k < len3; k++) {
1116
- map[keys[k]] = paramReplacementMap[map[keys[k]]];
1117
- }
1118
- }
1119
- }
1120
- const handlerMap = [];
1121
- for (const i in indexReplacementMap) {
1122
- handlerMap[i] = handlerData[indexReplacementMap[i]];
1123
- }
1124
- return [regexp, handlerMap, staticMap];
1125
- }
1126
1118
  function findMiddleware(middleware, path) {
1127
1119
  if (!middleware) {
1128
1120
  return;
@@ -1138,9 +1130,18 @@ var RegExpRouter = class {
1138
1130
  name = "RegExpRouter";
1139
1131
  #middleware;
1140
1132
  #routes;
1133
+ #tries;
1141
1134
  constructor() {
1142
1135
  this.#middleware = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
1143
1136
  this.#routes = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
1137
+ this.#tries = { [METHOD_NAME_ALL]: new Trie };
1138
+ }
1139
+ #insertPath(method, path) {
1140
+ try {
1141
+ this.#tries[method].insert(path, !/\*|\/:/.test(path));
1142
+ } catch (e) {
1143
+ throw e === PATH_ERROR ? new UnsupportedPathError(path) : e;
1144
+ }
1144
1145
  }
1145
1146
  add(method, path, handler) {
1146
1147
  const middleware = this.#middleware;
@@ -1149,10 +1150,12 @@ var RegExpRouter = class {
1149
1150
  throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
1150
1151
  }
1151
1152
  if (!middleware[method]) {
1153
+ this.#tries[method] = new Trie;
1152
1154
  [middleware, routes].forEach((handlerMap) => {
1153
1155
  handlerMap[method] = /* @__PURE__ */ Object.create(null);
1154
1156
  Object.keys(handlerMap[METHOD_NAME_ALL]).forEach((p) => {
1155
1157
  handlerMap[method][p] = [...handlerMap[METHOD_NAME_ALL][p]];
1158
+ this.#insertPath(method, p);
1156
1159
  });
1157
1160
  });
1158
1161
  }
@@ -1162,13 +1165,12 @@ var RegExpRouter = class {
1162
1165
  const paramCount = (path.match(/\/:/g) || []).length;
1163
1166
  if (/\*$/.test(path)) {
1164
1167
  const re = buildWildcardRegExp(path);
1165
- if (method === METHOD_NAME_ALL) {
1166
- Object.keys(middleware).forEach((m) => {
1167
- middleware[m][path] ||= findMiddleware(middleware[m], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
1168
- });
1169
- } else {
1170
- middleware[method][path] ||= findMiddleware(middleware[method], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
1171
- }
1168
+ Object.keys(middleware).forEach((m) => {
1169
+ if ((method === METHOD_NAME_ALL || method === m) && !middleware[m][path]) {
1170
+ this.#insertPath(m, path);
1171
+ middleware[m][path] = findMiddleware(middleware[m], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
1172
+ }
1173
+ });
1172
1174
  Object.keys(middleware).forEach((m) => {
1173
1175
  if (method === METHOD_NAME_ALL || method === m) {
1174
1176
  Object.keys(middleware[m]).forEach((p) => {
@@ -1188,9 +1190,12 @@ var RegExpRouter = class {
1188
1190
  const path2 = paths[i];
1189
1191
  Object.keys(routes).forEach((m) => {
1190
1192
  if (method === METHOD_NAME_ALL || method === m) {
1191
- routes[m][path2] ||= [
1192
- ...findMiddleware(middleware[m], path2) || findMiddleware(middleware[METHOD_NAME_ALL], path2) || []
1193
- ];
1193
+ if (!routes[m][path2]) {
1194
+ this.#insertPath(m, path2);
1195
+ routes[m][path2] = [
1196
+ ...findMiddleware(middleware[m], path2) || findMiddleware(middleware[METHOD_NAME_ALL], path2) || []
1197
+ ];
1198
+ }
1194
1199
  routes[m][path2].push([handler, paramCount - len + i + 1]);
1195
1200
  }
1196
1201
  });
@@ -1202,31 +1207,58 @@ var RegExpRouter = class {
1202
1207
  Object.keys(this.#routes).concat(Object.keys(this.#middleware)).forEach((method) => {
1203
1208
  matchers[method] ||= this.#buildMatcher(method);
1204
1209
  });
1205
- this.#middleware = this.#routes = undefined;
1210
+ this.#middleware = this.#routes = this.#tries = undefined;
1206
1211
  clearWildcardRegExpCache();
1207
1212
  return matchers;
1208
1213
  }
1209
1214
  #buildMatcher(method) {
1210
- const routes = [];
1211
- let hasOwnRoute = method === METHOD_NAME_ALL;
1212
- [this.#middleware, this.#routes].forEach((r) => {
1213
- const ownRoute = r[method] ? Object.keys(r[method]).map((path) => [path, r[method][path]]) : [];
1214
- if (ownRoute.length !== 0) {
1215
- hasOwnRoute ||= true;
1216
- routes.push(...ownRoute);
1217
- } else if (method !== METHOD_NAME_ALL) {
1218
- routes.push(...Object.keys(r[METHOD_NAME_ALL]).map((path) => [path, r[METHOD_NAME_ALL][path]]));
1215
+ const middleware = this.#middleware[method];
1216
+ const routes = this.#routes[method];
1217
+ const trie = this.#tries[method];
1218
+ const staticMap = /* @__PURE__ */ Object.create(null);
1219
+ const handlerData = [];
1220
+ [middleware, routes].forEach((r) => {
1221
+ for (const path in r) {
1222
+ const handlers = r[path];
1223
+ const pathData = trie.paths[path];
1224
+ if (!pathData) {
1225
+ staticMap[path] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];
1226
+ continue;
1227
+ }
1228
+ const paramAssoc = pathData[1];
1229
+ handlerData[pathData[0]] = handlers.map(([h, paramCount]) => {
1230
+ const paramIndexMap = /* @__PURE__ */ Object.create(null);
1231
+ paramCount -= 1;
1232
+ for (;paramCount >= 0; paramCount--) {
1233
+ const [key, value] = paramAssoc[paramCount];
1234
+ paramIndexMap[key] = value;
1235
+ }
1236
+ return [h, paramIndexMap];
1237
+ });
1219
1238
  }
1220
1239
  });
1221
- if (!hasOwnRoute) {
1222
- return null;
1223
- } else {
1224
- return buildMatcherFromPreprocessedRoutes(routes);
1240
+ const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
1241
+ for (let i = 0, len = handlerData.length;i < len; i++) {
1242
+ for (let j = 0, len2 = handlerData[i].length;j < len2; j++) {
1243
+ const map = handlerData[i][j]?.[1];
1244
+ if (!map) {
1245
+ continue;
1246
+ }
1247
+ const keys = Object.keys(map);
1248
+ for (let k = 0, len3 = keys.length;k < len3; k++) {
1249
+ map[keys[k]] = paramReplacementMap[map[keys[k]]];
1250
+ }
1251
+ }
1252
+ }
1253
+ const handlerMap = [];
1254
+ for (const i in indexReplacementMap) {
1255
+ handlerMap[i] = handlerData[indexReplacementMap[i]];
1225
1256
  }
1257
+ return [regexp, handlerMap, staticMap];
1226
1258
  }
1227
1259
  };
1228
1260
 
1229
- // node_modules/hono/dist/router/reg-exp-router/prepared-router.js
1261
+ // ../../node_modules/.bun/hono@4.13.1/node_modules/hono/dist/router/reg-exp-router/prepared-router.js
1230
1262
  var PreparedRegExpRouter = class {
1231
1263
  name = "PreparedRegExpRouter";
1232
1264
  #matchers;
@@ -1298,7 +1330,7 @@ var PreparedRegExpRouter = class {
1298
1330
  match = match;
1299
1331
  };
1300
1332
 
1301
- // node_modules/hono/dist/router/smart-router/router.js
1333
+ // ../../node_modules/.bun/hono@4.13.1/node_modules/hono/dist/router/smart-router/router.js
1302
1334
  var SmartRouter = class {
1303
1335
  name = "SmartRouter";
1304
1336
  #routers = [];
@@ -1353,7 +1385,7 @@ var SmartRouter = class {
1353
1385
  }
1354
1386
  };
1355
1387
 
1356
- // node_modules/hono/dist/router/trie-router/node.js
1388
+ // ../../node_modules/.bun/hono@4.13.1/node_modules/hono/dist/router/trie-router/node.js
1357
1389
  var emptyParams = /* @__PURE__ */ Object.create(null);
1358
1390
  var hasChildren = (children) => {
1359
1391
  for (const _ in children) {
@@ -1487,9 +1519,12 @@ var Node2 = class _Node2 {
1487
1519
  if (m) {
1488
1520
  params[name] = m[0];
1489
1521
  this.#pushHandlerSets(handlerSets, child, method, node.#params, params);
1522
+ if (m[0].length === restPathString.length && child.#children["*"]) {
1523
+ this.#pushHandlerSets(handlerSets, child.#children["*"], method, node.#params, params);
1524
+ }
1490
1525
  if (hasChildren(child.#children)) {
1491
1526
  child.#params = params;
1492
- const componentCount = m[0].match(/\//)?.length ?? 0;
1527
+ const componentCount = m[0].match(/\//g)?.length ?? 0;
1493
1528
  const targetCurNodes = curNodesQueue[componentCount] ||= [];
1494
1529
  targetCurNodes.push(child);
1495
1530
  }
@@ -1522,7 +1557,7 @@ var Node2 = class _Node2 {
1522
1557
  }
1523
1558
  };
1524
1559
 
1525
- // node_modules/hono/dist/router/trie-router/router.js
1560
+ // ../../node_modules/.bun/hono@4.13.1/node_modules/hono/dist/router/trie-router/router.js
1526
1561
  var TrieRouter = class {
1527
1562
  name = "TrieRouter";
1528
1563
  #node;
@@ -1544,7 +1579,7 @@ var TrieRouter = class {
1544
1579
  }
1545
1580
  };
1546
1581
 
1547
- // node_modules/hono/dist/hono.js
1582
+ // ../../node_modules/.bun/hono@4.13.1/node_modules/hono/dist/hono.js
1548
1583
  var Hono2 = class extends Hono {
1549
1584
  constructor(options = {}) {
1550
1585
  super(options);
@@ -1554,24 +1589,18 @@ var Hono2 = class extends Hono {
1554
1589
  }
1555
1590
  };
1556
1591
 
1557
- // node_modules/hono/dist/middleware/cors/index.js
1592
+ // ../../node_modules/.bun/hono@4.13.1/node_modules/hono/dist/middleware/cors/index.js
1558
1593
  var cors = (options) => {
1559
- const defaults = {
1594
+ const opts = {
1560
1595
  origin: "*",
1561
- allowMethods: ["GET", "HEAD", "PUT", "POST", "DELETE", "PATCH"],
1596
+ allowMethods: ["GET", "HEAD", "PUT", "POST", "DELETE", "PATCH", "QUERY"],
1562
1597
  allowHeaders: [],
1563
- exposeHeaders: []
1564
- };
1565
- const opts = {
1566
- ...defaults,
1598
+ exposeHeaders: [],
1567
1599
  ...options
1568
1600
  };
1569
1601
  const findAllowOrigin = ((optsOrigin) => {
1570
1602
  if (typeof optsOrigin === "string") {
1571
1603
  if (optsOrigin === "*") {
1572
- if (opts.credentials) {
1573
- return (origin) => origin || null;
1574
- }
1575
1604
  return () => optsOrigin;
1576
1605
  } else {
1577
1606
  return (origin) => optsOrigin === origin ? origin : null;
@@ -1606,7 +1635,7 @@ var cors = (options) => {
1606
1635
  set("Access-Control-Expose-Headers", opts.exposeHeaders.join(","));
1607
1636
  }
1608
1637
  if (c.req.method === "OPTIONS") {
1609
- if (opts.origin !== "*" || opts.credentials) {
1638
+ if (opts.origin !== "*") {
1610
1639
  set("Vary", "Origin");
1611
1640
  }
1612
1641
  if (opts.maxAge != null) {
@@ -1620,7 +1649,7 @@ var cors = (options) => {
1620
1649
  if (!headers?.length) {
1621
1650
  const requestHeaders = c.req.header("Access-Control-Request-Headers");
1622
1651
  if (requestHeaders) {
1623
- headers = requestHeaders.split(/\s*,\s*/);
1652
+ headers = requestHeaders.split(",").map((h) => h.trim());
1624
1653
  }
1625
1654
  }
1626
1655
  if (headers?.length) {
@@ -1636,7 +1665,7 @@ var cors = (options) => {
1636
1665
  });
1637
1666
  }
1638
1667
  await next();
1639
- if (opts.origin !== "*" || opts.credentials) {
1668
+ if (opts.origin !== "*") {
1640
1669
  c.header("Vary", "Origin", { append: true });
1641
1670
  }
1642
1671
  };
@@ -1715,14 +1744,90 @@ class ProfileNotFoundError extends Error {
1715
1744
  }
1716
1745
  }
1717
1746
 
1718
- // node_modules/@hasna/contracts/dist/auth/index.js
1747
+ // ../contracts/dist/auth/index.js
1719
1748
  import { createHash, createHmac, randomBytes, timingSafeEqual } from "crypto";
1749
+ var MAX_TENANT_ID_LENGTH = 64;
1750
+ var TENANT_ID_PATTERN = new RegExp(`^[A-Za-z0-9][A-Za-z0-9._-]{0,${MAX_TENANT_ID_LENGTH - 1}}$`);
1751
+ var UUID_HEX = "[0-9a-fA-F]";
1752
+ var UUID_PATTERN = new RegExp(`^\\{?(?:${UUID_HEX}{8}-${UUID_HEX}{4}-${UUID_HEX}{4}-${UUID_HEX}{4}-${UUID_HEX}{12}|${UUID_HEX}{32})\\}?$`);
1753
+ function isValidTenantId(value) {
1754
+ return typeof value === "string" && TENANT_ID_PATTERN.test(value);
1755
+ }
1756
+ function isUuidTenantId(value) {
1757
+ return typeof value === "string" && UUID_PATTERN.test(value);
1758
+ }
1759
+ function canonicalizeTenantId(value) {
1760
+ if (!isUuidTenantId(value))
1761
+ return value;
1762
+ const hex = value.replace(/[{}-]/g, "").toLowerCase();
1763
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
1764
+ }
1765
+ function normalizeTenantId(value) {
1766
+ const trimmed = typeof value === "string" ? value.trim() : "";
1767
+ const canonical = canonicalizeTenantId(trimmed);
1768
+ if (!isValidTenantId(canonical)) {
1769
+ throw new Error(`Invalid tenant id '${value}'. Expected 1-${MAX_TENANT_ID_LENGTH} characters matching ${TENANT_ID_PATTERN} (a UUID, ULID, slug, or prefixed id).`);
1770
+ }
1771
+ return canonical;
1772
+ }
1773
+ function tenantIdsEqual(left, right) {
1774
+ const canonical = (value) => {
1775
+ if (typeof value !== "string")
1776
+ return null;
1777
+ const folded = canonicalizeTenantId(value.trim());
1778
+ return isValidTenantId(folded) ? folded : null;
1779
+ };
1780
+ const a = canonical(left);
1781
+ const b = canonical(right);
1782
+ return a !== null && b !== null && a === b;
1783
+ }
1784
+ function ownTenantId(source) {
1785
+ return Object.hasOwn(source, "tid") ? source.tid : undefined;
1786
+ }
1720
1787
  var API_KEY_TOKEN_VERSION = 1;
1721
1788
  var API_KEY_NAMESPACE = "hasna";
1722
- var TOKEN_PATTERN = /^hasna_([a-z][a-z0-9-]*)_([A-Za-z0-9_-]+)\.([A-Za-z0-9_-]+)$/;
1789
+ var API_KEY_TOKEN_PATTERN = /^hasna_([a-z][a-z0-9-]*)_([A-Za-z0-9_-]+)\.([A-Za-z0-9_-]+)$/;
1790
+ var TOKEN_PATTERN = API_KEY_TOKEN_PATTERN;
1723
1791
  var DEFAULT_API_KEY_TTL_SECONDS = 90 * 24 * 60 * 60;
1792
+ function ownAgentClaim(source) {
1793
+ return Object.hasOwn(source, "agent") && typeof source.agent === "string" ? source.agent : null;
1794
+ }
1795
+ function ownScopesClaim(source) {
1796
+ return Object.hasOwn(source, "scopes") && Array.isArray(source.scopes) ? source.scopes : null;
1797
+ }
1798
+ function ownOption(options, name) {
1799
+ return Object.hasOwn(options, name) ? options[name] : undefined;
1800
+ }
1801
+ var typedArrayPrototype = Object.getPrototypeOf(Uint8Array.prototype);
1802
+ var intrinsicViewBuffer = Object.getOwnPropertyDescriptor(typedArrayPrototype, "buffer").get;
1803
+ var intrinsicViewByteOffset = Object.getOwnPropertyDescriptor(typedArrayPrototype, "byteOffset").get;
1804
+ var intrinsicViewByteLength = Object.getOwnPropertyDescriptor(typedArrayPrototype, "byteLength").get;
1805
+ var intrinsicDataViewBuffer = Object.getOwnPropertyDescriptor(DataView.prototype, "buffer").get;
1806
+ var intrinsicDataViewByteOffset = Object.getOwnPropertyDescriptor(DataView.prototype, "byteOffset").get;
1807
+ var intrinsicDataViewByteLength = Object.getOwnPropertyDescriptor(DataView.prototype, "byteLength").get;
1808
+ function viewWindow(view) {
1809
+ try {
1810
+ return [
1811
+ intrinsicViewBuffer.call(view),
1812
+ intrinsicViewByteOffset.call(view),
1813
+ intrinsicViewByteLength.call(view)
1814
+ ];
1815
+ } catch {
1816
+ return [
1817
+ intrinsicDataViewBuffer.call(view),
1818
+ intrinsicDataViewByteOffset.call(view),
1819
+ intrinsicDataViewByteLength.call(view)
1820
+ ];
1821
+ }
1822
+ }
1724
1823
  function toBuffer(secret) {
1725
- return typeof secret === "string" ? Buffer.from(secret, "utf8") : secret;
1824
+ if (typeof secret === "string")
1825
+ return Buffer.from(secret, "utf8");
1826
+ if (ArrayBuffer.isView(secret)) {
1827
+ const [store, byteOffset, byteLength] = viewWindow(secret);
1828
+ return Buffer.from(store, byteOffset, byteLength);
1829
+ }
1830
+ return Buffer.from(secret);
1726
1831
  }
1727
1832
  function hmac(signingSecret, message) {
1728
1833
  return createHmac("sha256", toBuffer(signingSecret)).update(message, "utf8").digest();
@@ -1745,12 +1850,23 @@ function parseApiKey(token) {
1745
1850
  } catch {
1746
1851
  return null;
1747
1852
  }
1748
- if (typeof claims !== "object" || claims === null || typeof claims.kid !== "string" || typeof claims.app !== "string" || !Array.isArray(claims.scopes)) {
1853
+ if (typeof claims !== "object" || claims === null || typeof claims.kid !== "string" || typeof claims.app !== "string" || ownScopesClaim(claims) === null) {
1854
+ return null;
1855
+ }
1856
+ const claimedTid = ownTenantId(claims);
1857
+ if (claimedTid !== undefined && !isValidTenantId(claimedTid)) {
1749
1858
  return null;
1750
1859
  }
1751
1860
  return { app, body, sig, claims };
1752
1861
  }
1753
1862
  function verifyApiKeyToken(token, options) {
1863
+ const optSigningSecret = ownOption(options, "signingSecret");
1864
+ const optExpectedApp = ownOption(options, "expectedApp");
1865
+ const optNowMs = ownOption(options, "nowMs");
1866
+ const optLeewaySeconds = ownOption(options, "leewaySeconds");
1867
+ const optRequiredScopes = ownOption(options, "requiredScopes");
1868
+ const optRequireTenant = ownOption(options, "requireTenant");
1869
+ const optExpectedTid = ownOption(options, "expectedTid");
1754
1870
  const parsed = parseApiKey(token);
1755
1871
  if (!parsed) {
1756
1872
  return { ok: false, reason: "malformed", message: "Token is malformed." };
@@ -1762,10 +1878,10 @@ function verifyApiKeyToken(token, options) {
1762
1878
  if (claims.app !== app) {
1763
1879
  return { ok: false, reason: "app_mismatch", message: "Token prefix app does not match claims." };
1764
1880
  }
1765
- if (options.expectedApp !== undefined && app !== options.expectedApp) {
1766
- return { ok: false, reason: "app_mismatch", message: `Token is for app '${app}', expected '${options.expectedApp}'.` };
1881
+ if (optExpectedApp !== undefined && app !== optExpectedApp) {
1882
+ return { ok: false, reason: "app_mismatch", message: `Token is for app '${app}', expected '${optExpectedApp}'.` };
1767
1883
  }
1768
- const expected = hmac(options.signingSecret, `${apiKeyPrefix(app)}${body}`);
1884
+ const expected = hmac(optSigningSecret, `${apiKeyPrefix(app)}${body}`);
1769
1885
  let provided;
1770
1886
  try {
1771
1887
  provided = Buffer.from(sig, "base64url");
@@ -1775,16 +1891,41 @@ function verifyApiKeyToken(token, options) {
1775
1891
  if (provided.length !== expected.length || !timingSafeEqual(provided, expected)) {
1776
1892
  return { ok: false, reason: "bad_signature", message: "Signature verification failed." };
1777
1893
  }
1778
- const now = Math.floor((options.nowMs ?? Date.now()) / 1000);
1779
- const leeway = options.leewaySeconds ?? 0;
1894
+ const agent = ownAgentClaim(claims);
1895
+ const now = Math.floor((optNowMs ?? Date.now()) / 1000);
1896
+ const leeway = optLeewaySeconds ?? 0;
1780
1897
  if (typeof claims.iat === "number" && now + leeway < claims.iat) {
1781
- return { ok: false, reason: "not_yet_valid", message: "Token is not yet valid." };
1898
+ return { ok: false, reason: "not_yet_valid", message: "Token is not yet valid.", agent };
1782
1899
  }
1783
1900
  if (claims.exp !== null && typeof claims.exp === "number" && now - leeway >= claims.exp) {
1784
- return { ok: false, reason: "expired", message: "Token has expired." };
1901
+ return { ok: false, reason: "expired", message: "Token has expired.", agent };
1902
+ }
1903
+ const verifiedTid = ownTenantId(claims);
1904
+ const tid = verifiedTid === undefined ? null : canonicalizeTenantId(verifiedTid);
1905
+ const tenantRequired = Boolean(optRequireTenant) || optExpectedTid !== undefined;
1906
+ if (tenantRequired && tid === null) {
1907
+ return {
1908
+ ok: false,
1909
+ reason: "tenant_required",
1910
+ message: "Token carries no tenant id ('tid') and this service requires one.",
1911
+ kid: claims.kid,
1912
+ tid: null,
1913
+ agent
1914
+ };
1785
1915
  }
1786
- if (options.requiredScopes && options.requiredScopes.length > 0) {
1787
- const granted = claims.scopes;
1916
+ if (optExpectedTid !== undefined && !tenantIdsEqual(tid, optExpectedTid)) {
1917
+ const expectationIsWellFormed = typeof optExpectedTid === "string" && isValidTenantId(optExpectedTid.trim());
1918
+ return {
1919
+ ok: false,
1920
+ reason: "tenant_mismatch",
1921
+ message: expectationIsWellFormed ? "Token is for a different tenant than the one this service accepts." : "Token tenant cannot be checked: the expected tenant id is not a valid tenant id.",
1922
+ kid: claims.kid,
1923
+ tid,
1924
+ agent
1925
+ };
1926
+ }
1927
+ if (optRequiredScopes && optRequiredScopes.length > 0) {
1928
+ const granted = ownScopesClaim(claims) ?? [];
1788
1929
  const satisfies = (required) => granted.some((g) => {
1789
1930
  if (g === "*")
1790
1931
  return true;
@@ -1798,15 +1939,16 @@ function verifyApiKeyToken(token, options) {
1798
1939
  const rAction = required.slice(ri + 1);
1799
1940
  return (gApp === "*" || gApp === rApp) && (gAction === "*" || gAction === rAction);
1800
1941
  });
1801
- for (const required of options.requiredScopes) {
1942
+ for (const required of optRequiredScopes) {
1802
1943
  if (!satisfies(required)) {
1803
- return { ok: false, reason: "insufficient_scope", message: `Missing required scope '${required}'.` };
1944
+ return { ok: false, reason: "insufficient_scope", message: `Missing required scope '${required}'.`, agent };
1804
1945
  }
1805
1946
  }
1806
1947
  }
1807
- return { ok: true, claims, kid: claims.kid, app };
1948
+ return { ok: true, claims, kid: claims.kid, app, tid, agent };
1808
1949
  }
1809
1950
  var DEFAULT_API_KEYS_TABLE = "api_keys";
1951
+ var API_KEY_ISSUANCE_PENDING_REASON = "credential_delivery_pending";
1810
1952
  function createTableSql(table) {
1811
1953
  return `CREATE TABLE IF NOT EXISTS ${table} (
1812
1954
  kid TEXT PRIMARY KEY,
@@ -1830,6 +1972,11 @@ function apiKeyMigrations(table = DEFAULT_API_KEYS_TABLE) {
1830
1972
  id: `hasna_auth_0002_${table}_indexes`,
1831
1973
  sql: `CREATE INDEX IF NOT EXISTS ${table}_app_idx ON ${table} (app);
1832
1974
  CREATE INDEX IF NOT EXISTS ${table}_token_hash_idx ON ${table} (token_hash);`
1975
+ },
1976
+ {
1977
+ id: `hasna_auth_0003_${table}_tenant`,
1978
+ sql: `ALTER TABLE ${table} ADD COLUMN IF NOT EXISTS tid TEXT;
1979
+ CREATE INDEX IF NOT EXISTS ${table}_tid_idx ON ${table} (tid);`
1833
1980
  }
1834
1981
  ];
1835
1982
  }
@@ -1854,10 +2001,13 @@ function parseScopes(value) {
1854
2001
  return [];
1855
2002
  }
1856
2003
  function rowToRecord(row) {
2004
+ const tid = ownTenantId(row);
2005
+ const agentValue = Object.hasOwn(row, "agent") ? row.agent : null;
1857
2006
  return {
1858
2007
  kid: String(row.kid),
1859
2008
  app: String(row.app),
1860
- agent: row.agent === null || row.agent === undefined ? null : String(row.agent),
2009
+ agent: agentValue === null || agentValue === undefined ? null : String(agentValue),
2010
+ tid: tid === null || tid === undefined ? null : String(tid),
1861
2011
  scopes: parseScopes(row.scopes),
1862
2012
  tokenHash: String(row.token_hash),
1863
2013
  issuedAt: toIso(row.issued_at) ?? new Date(0).toISOString(),
@@ -1888,31 +2038,63 @@ class ApiKeyStore {
1888
2038
  }
1889
2039
  }
1890
2040
  async insert(input) {
2041
+ await this.insertWithLifecycle(input, null, null);
2042
+ }
2043
+ async insertWithLifecycle(input, revokedAt, revokedReason) {
2044
+ const tid = ownTenantId(input);
2045
+ const agent = ownAgentClaim(input);
1891
2046
  await this.client.execute(`INSERT INTO ${this.table}
1892
- (kid, app, agent, scopes, token_hash, issued_at, expires_at, created_by)
1893
- VALUES ($1, $2, $3, $4::jsonb, $5, $6, $7, $8)`, [
2047
+ (kid, app, agent, tid, scopes, token_hash, issued_at, expires_at, created_by, revoked_at, revoked_reason)
2048
+ VALUES ($1, $2, $3, $4, $5::jsonb, $6, $7, $8, $9, $10, $11)`, [
1894
2049
  input.kid,
1895
2050
  input.app,
1896
- input.agent ?? null,
2051
+ agent,
2052
+ tid === undefined || tid === null ? null : normalizeTenantId(tid),
1897
2053
  JSON.stringify(input.scopes),
1898
2054
  input.tokenHash,
1899
2055
  input.issuedAt.toISOString(),
1900
2056
  input.expiresAt ? input.expiresAt.toISOString() : null,
1901
- input.createdBy ?? null
2057
+ input.createdBy ?? null,
2058
+ revokedAt,
2059
+ revokedReason
1902
2060
  ]);
1903
2061
  }
1904
- async insertMinted(minted, createdBy) {
2062
+ mintedInput(minted, createdBy) {
1905
2063
  const claims = minted.claims;
1906
- await this.insert({
2064
+ return {
1907
2065
  kid: minted.kid,
1908
2066
  app: claims.app,
1909
- agent: claims.agent ?? null,
2067
+ agent: ownAgentClaim(claims),
2068
+ tid: ownTenantId(claims) ?? null,
1910
2069
  scopes: claims.scopes,
1911
2070
  tokenHash: minted.tokenHash,
1912
2071
  issuedAt: new Date(claims.iat * 1000),
1913
2072
  expiresAt: claims.exp === null ? null : new Date(claims.exp * 1000),
1914
2073
  createdBy: createdBy ?? null
1915
- });
2074
+ };
2075
+ }
2076
+ async insertMinted(minted, createdBy) {
2077
+ await this.insert(this.mintedInput(minted, createdBy));
2078
+ }
2079
+ async insertMintedPending(minted, createdBy, atMs = Date.now()) {
2080
+ await this.insertWithLifecycle(this.mintedInput(minted, createdBy), new Date(atMs).toISOString(), API_KEY_ISSUANCE_PENDING_REASON);
2081
+ }
2082
+ async activatePending(kid, tokenHash) {
2083
+ const row = await this.client.get(`UPDATE ${this.table}
2084
+ SET revoked_at = NULL, revoked_reason = NULL
2085
+ WHERE kid = $1
2086
+ AND revoked_at IS NOT NULL
2087
+ AND revoked_reason = $2
2088
+ AND token_hash = $3
2089
+ RETURNING kid`, [kid, API_KEY_ISSUANCE_PENDING_REASON, tokenHash]);
2090
+ if (row)
2091
+ return true;
2092
+ const active = await this.client.get(`SELECT kid FROM ${this.table}
2093
+ WHERE kid = $1
2094
+ AND token_hash = $2
2095
+ AND revoked_at IS NULL
2096
+ AND revoked_reason IS NULL`, [kid, tokenHash]);
2097
+ return active !== null;
1916
2098
  }
1917
2099
  async findByKid(kid) {
1918
2100
  const row = await this.client.get(`SELECT * FROM ${this.table} WHERE kid = $1`, [kid]);
@@ -1938,6 +2120,9 @@ class ApiKeyStore {
1938
2120
  return "expired";
1939
2121
  return "active";
1940
2122
  }
2123
+ keyStatus = async (kid) => {
2124
+ return this.status(kid);
2125
+ };
1941
2126
  statusChecker() {
1942
2127
  return async (kid) => {
1943
2128
  const status = await this.status(kid);
@@ -1964,11 +2149,16 @@ class ApiKeyStore {
1964
2149
  params.push(options.app);
1965
2150
  clauses.push(`app = $${params.length}`);
1966
2151
  }
2152
+ const tid = ownTenantId(options);
2153
+ if (tid !== undefined) {
2154
+ params.push(normalizeTenantId(tid));
2155
+ clauses.push(`tid = $${params.length}`);
2156
+ }
1967
2157
  if (!options.includeRevoked) {
1968
2158
  clauses.push("revoked_at IS NULL");
1969
2159
  }
1970
2160
  const where = clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : "";
1971
- const rows = await this.client.many(`SELECT * FROM ${this.table} ${where} ORDER BY issued_at DESC`);
2161
+ const rows = await this.client.many(`SELECT * FROM ${this.table} ${where} ORDER BY issued_at DESC`, params);
1972
2162
  return rows.map(rowToRecord);
1973
2163
  }
1974
2164
  async revokedKids() {
@@ -2005,27 +2195,65 @@ function extractToken(source, headerName = "x-api-key", scheme = "Bearer") {
2005
2195
  }
2006
2196
  return null;
2007
2197
  }
2198
+ function ownOption2(bag, name) {
2199
+ return Object.hasOwn(bag, name) ? bag[name] : undefined;
2200
+ }
2008
2201
  function verifyApiKey(options) {
2009
- if (!options.app)
2202
+ const optionApp = ownOption2(options, "app");
2203
+ const optionSigningSecret = ownOption2(options, "signingSecret");
2204
+ const optionExpectedTid = ownOption2(options, "expectedTid");
2205
+ const optionRequiredScopes = ownOption2(options, "requiredScopes");
2206
+ const optionRequireTenant = ownOption2(options, "requireTenant");
2207
+ const optionLeewaySeconds = ownOption2(options, "leewaySeconds");
2208
+ const audit = ownOption2(options, "audit");
2209
+ const headerName = ownOption2(options, "headerName") ?? "x-api-key";
2210
+ const scheme = ownOption2(options, "scheme") ?? "Bearer";
2211
+ const clock = ownOption2(options, "nowMs") ?? (() => Date.now());
2212
+ if (!optionApp)
2010
2213
  throw new Error("verifyApiKey requires an 'app' slug.");
2011
- if (!options.signingSecret) {
2214
+ if (!optionSigningSecret) {
2012
2215
  throw new Error("verifyApiKey requires a 'signingSecret'. Set it from HASNA_<APP>_API_SIGNING_KEY.");
2013
2216
  }
2014
- const headerName = options.headerName ?? "x-api-key";
2015
- const scheme = options.scheme ?? "Bearer";
2016
- const clock = options.nowMs ?? (() => Date.now());
2217
+ if (optionExpectedTid !== undefined && !isValidTenantId(optionExpectedTid)) {
2218
+ throw new Error(`verifyApiKey received an invalid 'expectedTid': '${optionExpectedTid}'.`);
2219
+ }
2220
+ const app = optionApp;
2221
+ const signingSecret = optionSigningSecret;
2222
+ const ownKeyStatus = ownOption2(options, "keyStatus");
2223
+ const ownIsRevoked = ownOption2(options, "isRevoked");
2224
+ const allowUnregistered = ownOption2(options, "allowUnregisteredKeys") === true;
2225
+ if (ownKeyStatus && ownIsRevoked) {
2226
+ throw new Error("verifyApiKey received both 'keyStatus' and 'isRevoked'. Supply exactly one \u2014 " + "letting one silently win would hide which check is actually guarding the service. " + "Use 'keyStatus' (store.keyStatus); drop 'isRevoked'.");
2227
+ }
2228
+ if (!ownKeyStatus && !allowUnregistered) {
2229
+ throw new Error(ownIsRevoked ? "verifyApiKey was given only 'isRevoked', which cannot refuse a key this service has " + "no record of: it returns false both for an active key and for one that was never " + "registered, so an unregistered key is irrevocable. Wire 'keyStatus: store.keyStatus' " + "(or 'isRevoked: store.statusChecker()'), or set 'allowUnregisteredKeys: true' to " + "accept that risk explicitly." : "verifyApiKey requires a key-status hook. Without one this service performs NO " + "revocation check and cannot turn any of its keys off. Wire " + "'keyStatus: store.keyStatus', or set 'allowUnregisteredKeys: true' to declare that " + "this service intentionally cannot revoke keys.");
2230
+ }
2017
2231
  async function emit(event) {
2018
- if (!options.audit)
2232
+ if (!audit)
2019
2233
  return;
2020
2234
  try {
2021
- await options.audit(event);
2235
+ await audit(event);
2022
2236
  } catch {}
2023
2237
  }
2024
2238
  async function authenticate(headers, context = {}) {
2025
- const method = context.method ?? null;
2026
- const path = context.path ?? null;
2027
- const requiredScopes = [...options.requiredScopes ?? [], ...context.requiredScopes ?? []];
2239
+ const method = ownOption2(context, "method") ?? null;
2240
+ const path = ownOption2(context, "path") ?? null;
2241
+ const requiredScopes = [
2242
+ ...optionRequiredScopes ?? [],
2243
+ ...ownOption2(context, "requiredScopes") ?? []
2244
+ ];
2028
2245
  const at = new Date(clock()).toISOString();
2246
+ const perCallTid = ownOption2(context, "expectedTid");
2247
+ const expectedTid = perCallTid !== undefined ? perCallTid : optionExpectedTid;
2248
+ if (perCallTid !== undefined && optionExpectedTid !== undefined && !tenantIdsEqual(perCallTid, optionExpectedTid)) {
2249
+ await emit({ outcome: "deny", app, kid: null, tid: null, reason: "tenant_mismatch", scopesRequired: requiredScopes, method, path, status: 403, at });
2250
+ return {
2251
+ ok: false,
2252
+ status: 403,
2253
+ reason: "tenant_mismatch",
2254
+ message: "This route addresses a tenant other than the one this service is pinned to."
2255
+ };
2256
+ }
2029
2257
  const token = extractToken(headers, headerName, scheme);
2030
2258
  if (!token) {
2031
2259
  const decision = {
@@ -2034,25 +2262,72 @@ function verifyApiKey(options) {
2034
2262
  reason: "missing_token",
2035
2263
  message: `Missing API key. Send it as '${headerName}: <key>' or 'Authorization: ${scheme} <key>'.`
2036
2264
  };
2037
- await emit({ outcome: "deny", app: options.app, kid: null, reason: "missing_token", scopesRequired: requiredScopes, method, path, status: 401, at });
2265
+ await emit({ outcome: "deny", app, kid: null, tid: null, reason: "missing_token", scopesRequired: requiredScopes, method, path, status: 401, at });
2038
2266
  return decision;
2039
2267
  }
2040
2268
  const verified = verifyApiKeyToken(token, {
2041
- signingSecret: options.signingSecret,
2042
- expectedApp: options.app,
2269
+ signingSecret,
2270
+ expectedApp: app,
2043
2271
  nowMs: clock(),
2044
- ...options.leewaySeconds !== undefined ? { leewaySeconds: options.leewaySeconds } : {},
2272
+ ...optionLeewaySeconds !== undefined ? { leewaySeconds: optionLeewaySeconds } : {},
2273
+ ...optionRequireTenant !== undefined ? { requireTenant: optionRequireTenant } : {},
2274
+ ...expectedTid !== undefined ? { expectedTid } : {},
2045
2275
  requiredScopes
2046
2276
  });
2047
2277
  if (!verified.ok) {
2048
- const status = verified.reason === "insufficient_scope" ? 403 : 401;
2049
- await emit({ outcome: "deny", app: options.app, kid: null, reason: verified.reason, scopesRequired: requiredScopes, method, path, status, at });
2278
+ const status = verified.reason === "insufficient_scope" || verified.reason === "tenant_mismatch" || verified.reason === "tenant_required" ? 403 : 401;
2279
+ await emit({
2280
+ outcome: "deny",
2281
+ app,
2282
+ kid: ownOption2(verified, "kid") ?? null,
2283
+ tid: ownTenantId(verified) ?? null,
2284
+ ...Object.hasOwn(verified, "agent") ? { agent: verified.agent } : {},
2285
+ reason: verified.reason,
2286
+ scopesRequired: requiredScopes,
2287
+ method,
2288
+ path,
2289
+ status,
2290
+ at
2291
+ });
2050
2292
  return { ok: false, status, reason: verified.reason, message: verified.message };
2051
2293
  }
2052
- if (options.isRevoked) {
2053
- const revoked = await options.isRevoked(verified.kid);
2294
+ if (ownKeyStatus) {
2295
+ let status;
2296
+ try {
2297
+ status = await ownKeyStatus(verified.kid);
2298
+ } catch {
2299
+ await emit({ outcome: "deny", app, kid: verified.kid, tid: verified.tid, agent: verified.agent, reason: "status_unavailable", scopesRequired: requiredScopes, method, path, status: 503, at });
2300
+ return {
2301
+ ok: false,
2302
+ status: 503,
2303
+ reason: "status_unavailable",
2304
+ message: "Could not verify API key status. Try again shortly."
2305
+ };
2306
+ }
2307
+ if (status !== "active") {
2308
+ const known = status === "revoked" || status === "expired" || status === "unknown";
2309
+ if (!(status === "unknown" && allowUnregistered)) {
2310
+ const reason = status === "revoked" || status === "expired" ? status : "unknown_key";
2311
+ const message = reason === "unknown_key" ? known ? "API key is not registered with this service." : "API key status could not be recognized." : status === "expired" ? "API key has expired." : "API key has been revoked.";
2312
+ await emit({ outcome: "deny", app, kid: verified.kid, tid: verified.tid, agent: verified.agent, reason, scopesRequired: requiredScopes, method, path, status: 401, at });
2313
+ return { ok: false, status: 401, reason, message };
2314
+ }
2315
+ }
2316
+ } else if (ownIsRevoked) {
2317
+ let revoked;
2318
+ try {
2319
+ revoked = await ownIsRevoked(verified.kid);
2320
+ } catch {
2321
+ await emit({ outcome: "deny", app, kid: verified.kid, tid: verified.tid, agent: verified.agent, reason: "status_unavailable", scopesRequired: requiredScopes, method, path, status: 503, at });
2322
+ return {
2323
+ ok: false,
2324
+ status: 503,
2325
+ reason: "status_unavailable",
2326
+ message: "Could not verify API key status. Try again shortly."
2327
+ };
2328
+ }
2054
2329
  if (revoked) {
2055
- await emit({ outcome: "deny", app: options.app, kid: verified.kid, reason: "revoked", scopesRequired: requiredScopes, method, path, status: 401, at });
2330
+ await emit({ outcome: "deny", app, kid: verified.kid, tid: verified.tid, agent: verified.agent, reason: "revoked", scopesRequired: requiredScopes, method, path, status: 401, at });
2056
2331
  return { ok: false, status: 401, reason: "revoked", message: "API key has been revoked." };
2057
2332
  }
2058
2333
  }
@@ -2060,13 +2335,14 @@ function verifyApiKey(options) {
2060
2335
  kid: verified.kid,
2061
2336
  app: verified.app,
2062
2337
  scopes: verified.claims.scopes,
2063
- agent: verified.claims.agent ?? null,
2338
+ agent: verified.agent,
2339
+ tid: verified.tid,
2064
2340
  claims: verified.claims
2065
2341
  };
2066
- await emit({ outcome: "allow", app: options.app, kid: verified.kid, reason: null, scopesRequired: requiredScopes, method, path, status: 200, at });
2342
+ await emit({ outcome: "allow", app, kid: verified.kid, tid: verified.tid, agent: verified.agent, reason: null, scopesRequired: requiredScopes, method, path, status: 200, at });
2067
2343
  return { ok: true, status: 200, principal };
2068
2344
  }
2069
- return { authenticate, app: options.app };
2345
+ return { authenticate, app };
2070
2346
  }
2071
2347
  function honoApiKey(options) {
2072
2348
  const verifier = verifyApiKey(options);
@@ -2082,53 +2358,153 @@ function honoApiKey(options) {
2082
2358
  return c.json({ error: decision.message, reason: decision.reason }, decision.status);
2083
2359
  };
2084
2360
  }
2361
+ var MAX_FLEET_TOKEN_TTL_SECONDS = 24 * 60 * 60;
2362
+
2363
+ // src/generated/storage-kit/own.ts
2364
+ function ownProp(source, key) {
2365
+ if (source === null || source === undefined)
2366
+ return;
2367
+ const kind = typeof source;
2368
+ if (kind !== "object" && kind !== "function")
2369
+ return;
2370
+ if (!Object.hasOwn(source, key))
2371
+ return;
2372
+ return source[key];
2373
+ }
2374
+ function ownString(source, key) {
2375
+ const value = ownProp(source, key);
2376
+ return typeof value === "string" ? value : undefined;
2377
+ }
2085
2378
  // src/generated/storage-kit/tls.ts
2086
2379
  import { readFileSync as readFileSync2 } from "fs";
2087
- function sslModeFromConnectionString(connectionString) {
2380
+ var PG_TLS_QUERY_PARAMETERS = new Set([
2381
+ "ssl",
2382
+ "sslmode",
2383
+ "sslrootcert",
2384
+ "sslcert",
2385
+ "sslkey",
2386
+ "sslpassword",
2387
+ "sslnegotiation",
2388
+ "uselibpqcompat"
2389
+ ]);
2390
+ var EXPLICIT_SSL_ON_VALUES = new Set(["1", "true", "yes", "on", "require"]);
2391
+ var EXPLICIT_SSL_OFF_VALUES = new Set(["0", "false", "no", "off", "disable"]);
2392
+ var SSLMODE_VALUES = new Map([
2393
+ ["disable", "disable"],
2394
+ ["allow", "prefer"],
2395
+ ["prefer", "prefer"],
2396
+ ["require", "require"],
2397
+ ["verify-ca", "verify-ca"],
2398
+ ["verify-full", "verify-full"]
2399
+ ]);
2400
+ function connectionStringParts(connectionString) {
2088
2401
  const queryStart = connectionString.indexOf("?");
2089
- const params = new URLSearchParams(queryStart === -1 ? "" : connectionString.slice(queryStart + 1));
2090
- const sslmode = params.get("sslmode")?.trim().toLowerCase();
2091
- if (sslmode) {
2092
- switch (sslmode) {
2093
- case "disable":
2094
- case "prefer":
2095
- case "require":
2096
- case "verify-ca":
2097
- case "verify-full":
2098
- return sslmode;
2099
- case "allow":
2100
- return "prefer";
2101
- default:
2102
- throw new Error(`Unknown sslmode '${sslmode}' in connection string.`);
2103
- }
2104
- }
2105
- const ssl = params.get("ssl")?.trim().toLowerCase();
2106
- if (ssl && ["1", "true", "yes", "on", "require"].includes(ssl))
2402
+ if (queryStart === -1) {
2403
+ return { base: connectionString, fragment: "", params: new URLSearchParams };
2404
+ }
2405
+ const base = connectionString.slice(0, queryStart);
2406
+ const queryAndFragment = connectionString.slice(queryStart + 1);
2407
+ const fragmentStart = queryAndFragment.indexOf("#");
2408
+ const query = fragmentStart === -1 ? queryAndFragment : queryAndFragment.slice(0, fragmentStart);
2409
+ const fragment = fragmentStart === -1 ? "" : queryAndFragment.slice(fragmentStart);
2410
+ return { base, fragment, params: new URLSearchParams(query) };
2411
+ }
2412
+ function tlsQueryValues(connectionString) {
2413
+ const values = new Map;
2414
+ for (const [key, value] of connectionStringParts(connectionString).params) {
2415
+ const normalized = key.toLowerCase();
2416
+ if (PG_TLS_QUERY_PARAMETERS.has(normalized))
2417
+ values.set(normalized, value);
2418
+ }
2419
+ return values;
2420
+ }
2421
+ function connectionStringWithoutTlsParameters(connectionString) {
2422
+ const { base, fragment, params } = connectionStringParts(connectionString);
2423
+ for (const key of [...params.keys()]) {
2424
+ if (PG_TLS_QUERY_PARAMETERS.has(key.toLowerCase()))
2425
+ params.delete(key);
2426
+ }
2427
+ const query = params.toString();
2428
+ return `${base}${query ? `?${query}` : ""}${fragment}`;
2429
+ }
2430
+ function rawSslMode(values) {
2431
+ const raw2 = values.get("sslmode");
2432
+ return raw2 === undefined ? undefined : raw2.trim().toLowerCase();
2433
+ }
2434
+ function sslNegotiationFromConnectionString(connectionString) {
2435
+ const value = tlsQueryValues(connectionString).get("sslnegotiation")?.trim().toLowerCase();
2436
+ if (!value)
2437
+ return;
2438
+ if (value === "postgres" || value === "direct")
2439
+ return value;
2440
+ throw new Error(`Unknown sslnegotiation '${value}' in connection string; expected postgres or direct.`);
2441
+ }
2442
+ function sslModeFromConnectionString(connectionString) {
2443
+ const values = tlsQueryValues(connectionString);
2444
+ const sslmode = rawSslMode(values);
2445
+ if (sslmode !== undefined) {
2446
+ const resolved = SSLMODE_VALUES.get(sslmode);
2447
+ if (resolved)
2448
+ return resolved;
2449
+ throw new Error(`Unknown sslmode '${sslmode}' in connection string; expected one of ` + `${[...SSLMODE_VALUES.keys()].join(", ")}. Remove the parameter entirely to defer to ` + `PGSSLMODE \u2014 an empty value is not how that is spelled.`);
2450
+ }
2451
+ if (values.has("ssl")) {
2452
+ const ssl = values.get("ssl")?.trim().toLowerCase() ?? "";
2453
+ if (EXPLICIT_SSL_ON_VALUES.has(ssl))
2454
+ return "require";
2455
+ if (!EXPLICIT_SSL_OFF_VALUES.has(ssl)) {
2456
+ throw new Error(`Unknown ssl value '${ssl}' in connection string.`);
2457
+ }
2458
+ return "disable";
2459
+ }
2460
+ const sslnegotiation = values.get("sslnegotiation")?.trim().toLowerCase();
2461
+ if (sslnegotiation === "direct")
2107
2462
  return "require";
2108
2463
  return "disable";
2109
2464
  }
2110
- function loadCaBundle(options) {
2111
- const env = options.env ?? process.env;
2112
- if (options.ca && options.ca.trim())
2113
- return options.ca;
2114
- const path = options.caCertPath ?? env.PGSSLROOTCERT ?? env.NODE_EXTRA_CA_CERTS;
2465
+ function loadCaBundle(connectionString, options) {
2466
+ const env = ownProp(options, "env") ?? process.env;
2467
+ const ca = ownString(options, "ca");
2468
+ if (ca && ca.trim())
2469
+ return ca;
2470
+ const sslRootCert = tlsQueryValues(connectionString).get("sslrootcert")?.trim();
2471
+ const path = ownString(options, "caCertPath") ?? (sslRootCert ? sslRootCert : undefined) ?? ownString(env, "PGSSLROOTCERT") ?? ownString(env, "NODE_EXTRA_CA_CERTS");
2115
2472
  if (path && path.trim())
2116
2473
  return readFileSync2(path.trim(), "utf8");
2117
2474
  return null;
2118
2475
  }
2476
+ function loadClientCertificate(connectionString) {
2477
+ const values = tlsQueryValues(connectionString);
2478
+ const material = {};
2479
+ const certPath = values.get("sslcert")?.trim();
2480
+ if (certPath)
2481
+ material.cert = readFileSync2(certPath, "utf8");
2482
+ const keyPath = values.get("sslkey")?.trim();
2483
+ if (keyPath)
2484
+ material.key = readFileSync2(keyPath, "utf8");
2485
+ const passphrase = values.get("sslpassword");
2486
+ if (passphrase)
2487
+ material.passphrase = passphrase;
2488
+ return material;
2489
+ }
2119
2490
  function resolveTlsConfig(connectionString, options = {}) {
2120
2491
  const mode = sslModeFromConnectionString(connectionString);
2121
- if (mode === "disable" || mode === "prefer") {
2122
- return;
2123
- }
2124
- const ca = loadCaBundle(options);
2125
- if (mode === "require") {
2126
- return ca ? { rejectUnauthorized: false, ca } : { rejectUnauthorized: false };
2492
+ if (mode === "disable") {
2493
+ const values = tlsQueryValues(connectionString);
2494
+ const sslmode = rawSslMode(values);
2495
+ const ssl = values.get("ssl")?.trim().toLowerCase();
2496
+ const explicitlyOff = sslmode === "disable" || ssl !== undefined && EXPLICIT_SSL_OFF_VALUES.has(ssl);
2497
+ return explicitlyOff ? false : undefined;
2498
+ }
2499
+ const ca = loadCaBundle(connectionString, options);
2500
+ const clientCertificate = loadClientCertificate(connectionString);
2501
+ if (mode === "prefer" || mode === "require") {
2502
+ return { rejectUnauthorized: true, ...ca ? { ca } : {}, ...clientCertificate };
2127
2503
  }
2128
2504
  if (!ca) {
2129
2505
  throw new Error(`sslmode=${mode} requires a CA bundle. Set PGSSLROOTCERT (or pass caCertPath/ca) to the ` + `Amazon RDS global bundle: https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem`);
2130
2506
  }
2131
- return { rejectUnauthorized: true, ca };
2507
+ return { rejectUnauthorized: true, ca, ...clientCertificate };
2132
2508
  }
2133
2509
  // src/generated/storage-kit/query.ts
2134
2510
  function wrapExecutor(executor) {
@@ -2185,23 +2561,58 @@ function createQueryClient(pool) {
2185
2561
  }
2186
2562
  // src/generated/storage-kit/pool.ts
2187
2563
  import pg from "pg";
2564
+ function ownPoolOptions(options) {
2565
+ const own = Object.create(null);
2566
+ const ca = ownString(options, "ca");
2567
+ if (ca !== undefined)
2568
+ own.ca = ca;
2569
+ const caCertPath = ownString(options, "caCertPath");
2570
+ if (caCertPath !== undefined)
2571
+ own.caCertPath = caCertPath;
2572
+ const env = ownProp(options, "env");
2573
+ if (env !== undefined)
2574
+ own.env = env;
2575
+ const max = ownProp(options, "max");
2576
+ if (max !== undefined)
2577
+ own.max = max;
2578
+ const idleTimeoutMillis = ownProp(options, "idleTimeoutMillis");
2579
+ if (idleTimeoutMillis !== undefined)
2580
+ own.idleTimeoutMillis = idleTimeoutMillis;
2581
+ const connectionTimeoutMillis = ownProp(options, "connectionTimeoutMillis");
2582
+ if (connectionTimeoutMillis !== undefined)
2583
+ own.connectionTimeoutMillis = connectionTimeoutMillis;
2584
+ const applicationName = ownString(options, "applicationName");
2585
+ if (applicationName !== undefined)
2586
+ own.applicationName = applicationName;
2587
+ return own;
2588
+ }
2188
2589
  function createPgPool(options) {
2189
- const ssl = resolveTlsConfig(options.connectionString, {
2190
- ...options.ca !== undefined ? { ca: options.ca } : {},
2191
- ...options.caCertPath !== undefined ? { caCertPath: options.caCertPath } : {},
2192
- ...options.env !== undefined ? { env: options.env } : {}
2590
+ const connectionString = ownString(options, "connectionString");
2591
+ if (!connectionString || !connectionString.trim()) {
2592
+ throw new Error("createPgPool requires an own `connectionString` on the options object.");
2593
+ }
2594
+ const own = ownPoolOptions(options);
2595
+ const ssl = resolveTlsConfig(connectionString, {
2596
+ ...own.ca !== undefined ? { ca: own.ca } : {},
2597
+ ...own.caCertPath !== undefined ? { caCertPath: own.caCertPath } : {},
2598
+ ...own.env !== undefined ? { env: own.env } : {}
2193
2599
  });
2194
- const config = { connectionString: options.connectionString };
2600
+ const config = {
2601
+ connectionString: connectionStringWithoutTlsParameters(connectionString)
2602
+ };
2195
2603
  if (ssl !== undefined)
2196
2604
  config.ssl = ssl;
2197
- if (options.max !== undefined)
2198
- config.max = options.max;
2199
- if (options.idleTimeoutMillis !== undefined)
2200
- config.idleTimeoutMillis = options.idleTimeoutMillis;
2201
- if (options.connectionTimeoutMillis !== undefined)
2202
- config.connectionTimeoutMillis = options.connectionTimeoutMillis;
2203
- if (options.applicationName !== undefined)
2204
- config.application_name = options.applicationName;
2605
+ const sslnegotiation = sslNegotiationFromConnectionString(connectionString);
2606
+ if (sslnegotiation !== undefined)
2607
+ config.sslnegotiation = sslnegotiation;
2608
+ if (own.max !== undefined)
2609
+ config.max = own.max;
2610
+ if (own.idleTimeoutMillis !== undefined)
2611
+ config.idleTimeoutMillis = own.idleTimeoutMillis;
2612
+ if (own.connectionTimeoutMillis !== undefined)
2613
+ config.connectionTimeoutMillis = own.connectionTimeoutMillis;
2614
+ if (own.applicationName !== undefined)
2615
+ config.application_name = own.applicationName;
2205
2616
  return new pg.Pool(config);
2206
2617
  }
2207
2618
  // src/storage/schema.ts
@@ -2282,15 +2693,37 @@ function instructionsSchemaSql() {
2282
2693
  ];
2283
2694
  }
2284
2695
 
2696
+ // src/lib/retired-storage-mode.ts
2697
+ var LEGACY_STORAGE_MODE_KEYS = [
2698
+ "HASNA_INSTRUCTIONS_STORAGE_MODE",
2699
+ "HASNA_INSTRUCTIONS_MODE",
2700
+ "INSTRUCTIONS_STORAGE_MODE",
2701
+ "INSTRUCTIONS_MODE"
2702
+ ];
2703
+ function firstDefinedEnvKey(env, keys) {
2704
+ for (const key of keys) {
2705
+ if (Object.hasOwn(env, key) && env[key] !== undefined)
2706
+ return key;
2707
+ }
2708
+ return null;
2709
+ }
2710
+ function assertNoLegacyStorageMode(env = process.env) {
2711
+ const legacyKey = firstDefinedEnvKey(env, LEGACY_STORAGE_MODE_KEYS);
2712
+ if (!legacyKey)
2713
+ return;
2714
+ throw new Error(`${legacyKey} was removed. Deployment modes no longer exist: delete the storage-mode variable. ` + `The client uses the local SQLite store, or the HTTP API selected by ` + `HASNA_INSTRUCTIONS_API_URL + HASNA_INSTRUCTIONS_API_KEY. ` + `On the server, set HASNA_INSTRUCTIONS_DATABASE_URL to select the postgresql backend, ` + `or leave it unset for sqlite.`);
2715
+ }
2716
+
2285
2717
  // src/server/cloud.ts
2286
2718
  var INSTRUCTIONS_APP_SLUG = "instructions";
2287
2719
  function resolveCloudDatabaseUrl(env = process.env) {
2720
+ assertNoLegacyStorageMode(env);
2288
2721
  return env.HASNA_INSTRUCTIONS_DATABASE_URL || env.INSTRUCTIONS_DATABASE_URL || env.DATABASE_URL || undefined;
2289
2722
  }
2290
2723
  function resolveSigningSecret(env = process.env) {
2291
2724
  return env.HASNA_INSTRUCTIONS_API_SIGNING_KEY || env.HASNA_API_SIGNING_KEY || env.API_KEY_SIGNING_SECRET || undefined;
2292
2725
  }
2293
- function isCloudModeEnabled(env = process.env) {
2726
+ function isPostgresBackendEnabled(env = process.env) {
2294
2727
  return Boolean(resolveCloudDatabaseUrl(env));
2295
2728
  }
2296
2729
  var cachedClient = null;
@@ -2363,7 +2796,7 @@ function getHonoAuthMiddleware(requiredScopes) {
2363
2796
  const mw = honoApiKey({
2364
2797
  app: INSTRUCTIONS_APP_SLUG,
2365
2798
  signingSecret,
2366
- isRevoked: store.isRevoked,
2799
+ keyStatus: store.keyStatus,
2367
2800
  requiredScopes
2368
2801
  });
2369
2802
  honoMiddlewareCache.set(key, mw);
@@ -2748,7 +3181,7 @@ Policy reference: \`${CODEWITH_SHARED_TODOS_STORAGE_POLICY_REFERENCE}\`
2748
3181
  // src/lib/project-context.ts
2749
3182
  import { basename, dirname as dirname2, isAbsolute, join as join2, parse, relative, resolve } from "path";
2750
3183
 
2751
- // node_modules/zod/v3/external.js
3184
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/external.js
2752
3185
  var exports_external = {};
2753
3186
  __export(exports_external, {
2754
3187
  void: () => voidType,
@@ -2860,7 +3293,7 @@ __export(exports_external, {
2860
3293
  BRAND: () => BRAND
2861
3294
  });
2862
3295
 
2863
- // node_modules/zod/v3/helpers/util.js
3296
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/util.js
2864
3297
  var util;
2865
3298
  (function(util2) {
2866
3299
  util2.assertEqual = (_) => {};
@@ -2991,7 +3424,7 @@ var getParsedType = (data) => {
2991
3424
  }
2992
3425
  };
2993
3426
 
2994
- // node_modules/zod/v3/ZodError.js
3427
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/ZodError.js
2995
3428
  var ZodIssueCode = util.arrayToEnum([
2996
3429
  "invalid_type",
2997
3430
  "invalid_literal",
@@ -3110,7 +3543,7 @@ ZodError.create = (issues) => {
3110
3543
  return error;
3111
3544
  };
3112
3545
 
3113
- // node_modules/zod/v3/locales/en.js
3546
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/locales/en.js
3114
3547
  var errorMap = (issue, _ctx) => {
3115
3548
  let message;
3116
3549
  switch (issue.code) {
@@ -3213,7 +3646,7 @@ var errorMap = (issue, _ctx) => {
3213
3646
  };
3214
3647
  var en_default = errorMap;
3215
3648
 
3216
- // node_modules/zod/v3/errors.js
3649
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/errors.js
3217
3650
  var overrideErrorMap = en_default;
3218
3651
  function setErrorMap(map) {
3219
3652
  overrideErrorMap = map;
@@ -3221,7 +3654,7 @@ function setErrorMap(map) {
3221
3654
  function getErrorMap() {
3222
3655
  return overrideErrorMap;
3223
3656
  }
3224
- // node_modules/zod/v3/helpers/parseUtil.js
3657
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
3225
3658
  var makeIssue = (params) => {
3226
3659
  const { data, path, errorMaps, issueData } = params;
3227
3660
  const fullPath = [...path, ...issueData.path || []];
@@ -3327,14 +3760,14 @@ var isAborted = (x) => x.status === "aborted";
3327
3760
  var isDirty = (x) => x.status === "dirty";
3328
3761
  var isValid = (x) => x.status === "valid";
3329
3762
  var isAsync = (x) => typeof Promise !== "undefined" && x instanceof Promise;
3330
- // node_modules/zod/v3/helpers/errorUtil.js
3763
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/errorUtil.js
3331
3764
  var errorUtil;
3332
3765
  (function(errorUtil2) {
3333
3766
  errorUtil2.errToObj = (message) => typeof message === "string" ? { message } : message || {};
3334
3767
  errorUtil2.toString = (message) => typeof message === "string" ? message : message?.message;
3335
3768
  })(errorUtil || (errorUtil = {}));
3336
3769
 
3337
- // node_modules/zod/v3/types.js
3770
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/types.js
3338
3771
  class ParseInputLazyPath {
3339
3772
  constructor(parent, value, path, key) {
3340
3773
  this._cachedPath = [];
@@ -6960,6 +7393,9 @@ var DEPRECATED_CONFIG_AGENT_SET = new Set(DEPRECATED_CONFIG_AGENTS);
6960
7393
 
6961
7394
  // src/lib/cursor-authority.ts
6962
7395
  var CURSOR_GLOBAL_AUTHORITY_MAX_BYTES = 256 * 1024;
7396
+
7397
+ // src/lib/session-authority.ts
7398
+ var CLAUDE_LEGACY_AUTHORITY_MAX_BYTES = 256 * 1024;
6963
7399
  // src/lib/session-render.ts
6964
7400
  var SESSION_RENDER_PROFILE_ENTRYPOINTS = [
6965
7401
  ".claude/CLAUDE.md",
@@ -7152,7 +7588,12 @@ function capability(provider, providerVersionRange, selectedRepresentation, load
7152
7588
  });
7153
7589
  }
7154
7590
  var DEFAULT_PROVIDER_CAPABILITIES = Object.freeze({
7155
- claude: capability("claude", ">=1.0.0", "native-import", "CLAUDE.md @ imports", { native_imports: true, asset_surface: "code" }),
7591
+ claude: capability("claude", ">=1.0.0", "native-import", "CLAUDE.md @ imports plus path-gated rules", {
7592
+ activation_modes: ["always", "glob"],
7593
+ native_imports: true,
7594
+ conditional_artifacts: true,
7595
+ asset_surface: "code"
7596
+ }),
7156
7597
  codex: capability("codex", ">=0.1.0", "flattened", "AGENTS.md", { asset_surface: "cli" }),
7157
7598
  cursor: capability("cursor", ">=1.0.0", "cursor-rule", ".cursor/rules/*.mdc", { activation_modes: ["always", "glob"], conditional_artifacts: true, asset_surface: "ide" }),
7158
7599
  opencode: capability("opencode", ">=1.0.0", "managed-fragment", "opencode.json instructions", { provider_variant: "v1-instructions", session_surface: "opencode-config-instructions" }),
@@ -7257,15 +7698,15 @@ function normalizeActivation(value) {
7257
7698
  if (!INSTRUCTION_ACTIVATION_MODES.includes(record["mode"])) {
7258
7699
  throw new Error(`Invalid instruction activation mode: ${String(record["mode"])}`);
7259
7700
  }
7260
- const mode2 = record["mode"];
7701
+ const mode = record["mode"];
7261
7702
  const globs = stringArray(record["globs"], "activation.globs");
7262
7703
  const models = stringArray(record["models"], "activation.models");
7263
- if (mode2 === "glob" && (!globs || globs.length === 0))
7704
+ if (mode === "glob" && (!globs || globs.length === 0))
7264
7705
  throw new Error("Glob activation requires at least one glob.");
7265
- if (mode2 === "model" && (!models || models.length === 0))
7706
+ if (mode === "model" && (!models || models.length === 0))
7266
7707
  throw new Error("Model activation requires at least one model.");
7267
7708
  return {
7268
- mode: mode2,
7709
+ mode,
7269
7710
  ...globs ? { globs } : {},
7270
7711
  ...models ? { models } : {},
7271
7712
  ...optionalString(record["description"], "activation.description") ? { description: record["description"] } : {},
@@ -7417,9 +7858,15 @@ async function createConfig2(client, input) {
7417
7858
  throw new Error("category is required");
7418
7859
  const id = randomUUID();
7419
7860
  const slug = await uniqueSlug(client, input.name);
7420
- await client.execute(`INSERT INTO configs
7421
- (id, name, slug, kind, category, agent, target_path, outputs, format, content, description, tags, is_template, version, created_at, updated_at)
7422
- VALUES ($1,$2,$3,$4,$5,$6,$7,$8::jsonb,$9,$10,$11,$12::jsonb,$13,1,now(),now())`, [
7861
+ const snapshotId = randomUUID();
7862
+ await client.execute(`WITH inserted_config AS (
7863
+ INSERT INTO configs
7864
+ (id, name, slug, kind, category, agent, target_path, outputs, format, content, description, tags, is_template, version, created_at, updated_at)
7865
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8::jsonb,$9,$10,$11,$12::jsonb,$13,1,now(),now())
7866
+ RETURNING id, content, version
7867
+ )
7868
+ INSERT INTO config_snapshots (id, config_id, content, version, created_at)
7869
+ SELECT $14, id, content, version, now() FROM inserted_config`, [
7423
7870
  id,
7424
7871
  input.name,
7425
7872
  slug,
@@ -7432,7 +7879,8 @@ async function createConfig2(client, input) {
7432
7879
  input.content ?? "",
7433
7880
  input.description ?? null,
7434
7881
  JSON.stringify(input.tags ?? []),
7435
- input.is_template ?? false
7882
+ input.is_template ?? false,
7883
+ snapshotId
7436
7884
  ]);
7437
7885
  return getConfig2(client, id);
7438
7886
  }
@@ -7471,7 +7919,15 @@ async function updateConfig2(client, idOrSlug, input) {
7471
7919
  if (input.synced_at !== undefined)
7472
7920
  set("synced_at", input.synced_at, "::timestamptz");
7473
7921
  params.push(existing.id);
7474
- await client.execute(`UPDATE configs SET ${sets.join(", ")} WHERE id = $${params.length}`, params);
7922
+ const configIdParam = params.length;
7923
+ params.push(randomUUID());
7924
+ const snapshotIdParam = params.length;
7925
+ await client.execute(`WITH updated_config AS (
7926
+ UPDATE configs SET ${sets.join(", ")} WHERE id = $${configIdParam}
7927
+ RETURNING id, content, version
7928
+ )
7929
+ INSERT INTO config_snapshots (id, config_id, content, version, created_at)
7930
+ SELECT $${snapshotIdParam}, id, content, version, now() FROM updated_config`, params);
7475
7931
  return getConfig2(client, existing.id);
7476
7932
  }
7477
7933
  async function deleteConfig2(client, idOrSlug) {
@@ -8655,22 +9111,22 @@ if (process.argv.includes("--version") || process.argv.includes("-V")) {
8655
9111
  var PORT = Number(process.env["PORT"] ?? process.env["INSTRUCTIONS_PORT"] ?? 3457);
8656
9112
  var app = new Hono2;
8657
9113
  app.use("*", cors());
8658
- function serviceMode() {
8659
- return isCloudModeEnabled() ? "cloud" : "local";
9114
+ function serviceBackend() {
9115
+ return isPostgresBackendEnabled() ? "postgresql" : "sqlite";
8660
9116
  }
8661
- app.get("/health", (c) => c.json({ status: "ok", version: getPackageVersion(), mode: serviceMode(), name: "instructions" }));
8662
- app.get("/version", (c) => c.json({ status: "ok", version: getPackageVersion(), mode: serviceMode(), name: "instructions" }));
9117
+ app.get("/health", (c) => c.json({ status: "ok", version: getPackageVersion(), backend: serviceBackend(), name: "instructions" }));
9118
+ app.get("/version", (c) => c.json({ status: "ok", version: getPackageVersion(), backend: serviceBackend(), name: "instructions" }));
8663
9119
  app.get("/ready", async (c) => {
8664
9120
  const version = getPackageVersion();
8665
- const mode2 = serviceMode();
8666
- if (mode2 === "cloud") {
9121
+ const backend2 = serviceBackend();
9122
+ if (backend2 === "postgresql") {
8667
9123
  try {
8668
9124
  await pingCloud();
8669
9125
  } catch (e) {
8670
- return c.json({ status: "unavailable", version, mode: mode2, error: e.message }, 503);
9126
+ return c.json({ status: "unavailable", version, backend: backend2, error: e.message }, 503);
8671
9127
  }
8672
9128
  }
8673
- return c.json({ status: "ready", version, mode: mode2 });
9129
+ return c.json({ status: "ready", version, backend: backend2 });
8674
9130
  });
8675
9131
  app.get("/openapi.json", (c) => c.json(buildV1OpenApiDocument()));
8676
9132
  app.get("/v1/openapi.json", (c) => c.json(buildV1OpenApiDocument()));
@@ -8722,7 +9178,7 @@ if (dashDir) {
8722
9178
  });
8723
9179
  }
8724
9180
  var HOST = process.env["HOST"] ?? process.env["INSTRUCTIONS_HOST"] ?? "localhost";
8725
- console.log(`instructions-serve listening on http://${HOST}:${PORT} (mode: ${serviceMode()})${dashDir ? " (dashboard: /)" : " (no dashboard)"}`);
9181
+ console.log(`instructions-serve listening on http://${HOST}:${PORT} (backend: ${serviceBackend()})${dashDir ? " (dashboard: /)" : " (no dashboard)"}`);
8726
9182
  var server_default = { port: PORT, hostname: HOST, fetch: app.fetch };
8727
9183
  export {
8728
9184
  server_default as default