theokit 0.74.5 → 0.75.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 (98) hide show
  1. package/dist/{actions-virtual-module-MIQJEHKJ.js → actions-virtual-module-MIQPBAZW.js} +5 -5
  2. package/dist/{actions-virtual-module-4J7Z37Y4.js → actions-virtual-module-QEI6QPK4.js} +5 -5
  3. package/dist/adapters/agent-mount.js +1 -1
  4. package/dist/{agent-XPZUWTHR.js → agent-IIF6JUWX.js} +3 -3
  5. package/dist/{app-typed-client-AZMUYIYV.js → app-typed-client-CHSITA46.js} +5 -5
  6. package/dist/{app-typed-client-DSS23KDC.js → app-typed-client-XQJXF646.js} +5 -5
  7. package/dist/{build-F3O5LJHY.js → build-QIO4VAW2.js} +22 -87
  8. package/dist/build-QIO4VAW2.js.map +1 -0
  9. package/dist/{chunk-PZLH4WGU.js → chunk-2BMJQMTH.js} +5 -5
  10. package/dist/{chunk-44E7XIZC.js → chunk-3MKRDJQ2.js} +2 -2
  11. package/dist/{chunk-4ERYQBL6.js → chunk-4Z5XO4O4.js} +2 -2
  12. package/dist/{chunk-FPC6ZEEU.js → chunk-6RVWPEKK.js} +10 -10
  13. package/dist/{chunk-V7ISZGNS.js → chunk-7HGZ3CKL.js} +2 -2
  14. package/dist/{chunk-6R5F5YF2.js → chunk-A6UFN6QQ.js} +16 -11
  15. package/dist/{chunk-6R5F5YF2.js.map → chunk-A6UFN6QQ.js.map} +1 -1
  16. package/dist/{chunk-5KRMDP46.js → chunk-AHYOVVJ7.js} +13 -3
  17. package/dist/chunk-AHYOVVJ7.js.map +1 -0
  18. package/dist/{chunk-WR5B2XNT.js → chunk-FNAEKJHO.js} +2 -2
  19. package/dist/{chunk-HKF6YNY4.js → chunk-FU3LEX2I.js} +26 -12
  20. package/dist/chunk-FU3LEX2I.js.map +1 -0
  21. package/dist/{chunk-VD3BCME6.js → chunk-I5PDEWZZ.js} +2 -2
  22. package/dist/chunk-IWIRHUNV.js +96 -0
  23. package/dist/chunk-IWIRHUNV.js.map +1 -0
  24. package/dist/{chunk-U5PWXS3J.js → chunk-O5JZ6ROY.js} +13 -3
  25. package/dist/chunk-O5JZ6ROY.js.map +1 -0
  26. package/dist/{chunk-VOXLI66L.js → chunk-OZB2TKGJ.js} +2 -2
  27. package/dist/{chunk-ET2TAY3Z.js → chunk-RAUYMVUO.js} +279 -15
  28. package/dist/chunk-RAUYMVUO.js.map +1 -0
  29. package/dist/{chunk-XUAWD5HK.js → chunk-RKRFJRJ3.js} +10 -10
  30. package/dist/{chunk-DA3GGICE.js → chunk-RQKMY3JW.js} +2 -2
  31. package/dist/{chunk-D5KBBTVD.js → chunk-RY6D4EXL.js} +20 -13
  32. package/dist/chunk-RY6D4EXL.js.map +1 -0
  33. package/dist/cli/index.js +10 -10
  34. package/dist/{dev-TW6QD7F3.js → dev-J2XZIENF.js} +8 -8
  35. package/dist/{dev-emit-T3X6RR7M.js → dev-emit-AXTJK56H.js} +3 -3
  36. package/dist/{dev-emit-ZK5GCHXN.js → dev-emit-XAPVJBM4.js} +3 -3
  37. package/dist/{generate-EN6EDITF.js → generate-RW3NO3NR.js} +239 -141
  38. package/dist/generate-RW3NO3NR.js.map +1 -0
  39. package/dist/index.js +5 -5
  40. package/dist/{info-XURFOBRF.js → info-6NAUW5XY.js} +3 -3
  41. package/dist/{internal-api-XPT4GLYX.js → internal-api-A5HA7TYF.js} +5 -5
  42. package/dist/{internal-api-ATSPOXI2.js → internal-api-R7TTYMCY.js} +5 -5
  43. package/dist/{load-config-2DXLI7NW.js → load-config-CLFWJOXN.js} +3 -3
  44. package/dist/{mcp-LMV3NQEP.js → mcp-MWTG3LRA.js} +4 -4
  45. package/dist/{openapi-UJSUQPNT.js → openapi-RVFUXXCR.js} +5 -5
  46. package/dist/{preview-AMNBV2DP.js → preview-ML4CFUD6.js} +3 -3
  47. package/dist/{routes-ENMU4GAN.js → routes-SMNSI2ZX.js} +3 -3
  48. package/dist/server/agent/index.d.ts +99 -6
  49. package/dist/server/agent/index.js +9 -1
  50. package/dist/server/cron/index.js +2 -2
  51. package/dist/server/index.d.ts +1 -1
  52. package/dist/server/index.js +15 -7
  53. package/dist/server/index.js.map +1 -1
  54. package/dist/server/jobs/index.js +2 -2
  55. package/dist/server/scan/index.js +4 -4
  56. package/dist/{server-boundary-K4YQXDQT.js → server-boundary-HYLYGFZY.js} +5 -5
  57. package/dist/{server-boundary-JM72DQLA.js → server-boundary-ZT6HN7ON.js} +5 -5
  58. package/dist/{start-GAULIE5E.js → start-L5TGWGLU.js} +7 -7
  59. package/dist/vite-plugin/index.js +5 -5
  60. package/dist/{vite-plugin-4ZMBW3KV.js → vite-plugin-OJND2LU5.js} +8 -8
  61. package/package.json +2 -2
  62. package/dist/build-F3O5LJHY.js.map +0 -1
  63. package/dist/chunk-5KRMDP46.js.map +0 -1
  64. package/dist/chunk-D5KBBTVD.js.map +0 -1
  65. package/dist/chunk-ET2TAY3Z.js.map +0 -1
  66. package/dist/chunk-HKF6YNY4.js.map +0 -1
  67. package/dist/chunk-U5PWXS3J.js.map +0 -1
  68. package/dist/generate-EN6EDITF.js.map +0 -1
  69. /package/dist/{actions-virtual-module-MIQJEHKJ.js.map → actions-virtual-module-MIQPBAZW.js.map} +0 -0
  70. /package/dist/{actions-virtual-module-4J7Z37Y4.js.map → actions-virtual-module-QEI6QPK4.js.map} +0 -0
  71. /package/dist/{agent-XPZUWTHR.js.map → agent-IIF6JUWX.js.map} +0 -0
  72. /package/dist/{app-typed-client-AZMUYIYV.js.map → app-typed-client-CHSITA46.js.map} +0 -0
  73. /package/dist/{app-typed-client-DSS23KDC.js.map → app-typed-client-XQJXF646.js.map} +0 -0
  74. /package/dist/{chunk-PZLH4WGU.js.map → chunk-2BMJQMTH.js.map} +0 -0
  75. /package/dist/{chunk-44E7XIZC.js.map → chunk-3MKRDJQ2.js.map} +0 -0
  76. /package/dist/{chunk-4ERYQBL6.js.map → chunk-4Z5XO4O4.js.map} +0 -0
  77. /package/dist/{chunk-FPC6ZEEU.js.map → chunk-6RVWPEKK.js.map} +0 -0
  78. /package/dist/{chunk-V7ISZGNS.js.map → chunk-7HGZ3CKL.js.map} +0 -0
  79. /package/dist/{chunk-WR5B2XNT.js.map → chunk-FNAEKJHO.js.map} +0 -0
  80. /package/dist/{chunk-VD3BCME6.js.map → chunk-I5PDEWZZ.js.map} +0 -0
  81. /package/dist/{chunk-VOXLI66L.js.map → chunk-OZB2TKGJ.js.map} +0 -0
  82. /package/dist/{chunk-XUAWD5HK.js.map → chunk-RKRFJRJ3.js.map} +0 -0
  83. /package/dist/{chunk-DA3GGICE.js.map → chunk-RQKMY3JW.js.map} +0 -0
  84. /package/dist/{dev-TW6QD7F3.js.map → dev-J2XZIENF.js.map} +0 -0
  85. /package/dist/{dev-emit-T3X6RR7M.js.map → dev-emit-AXTJK56H.js.map} +0 -0
  86. /package/dist/{dev-emit-ZK5GCHXN.js.map → dev-emit-XAPVJBM4.js.map} +0 -0
  87. /package/dist/{info-XURFOBRF.js.map → info-6NAUW5XY.js.map} +0 -0
  88. /package/dist/{internal-api-XPT4GLYX.js.map → internal-api-A5HA7TYF.js.map} +0 -0
  89. /package/dist/{internal-api-ATSPOXI2.js.map → internal-api-R7TTYMCY.js.map} +0 -0
  90. /package/dist/{load-config-2DXLI7NW.js.map → load-config-CLFWJOXN.js.map} +0 -0
  91. /package/dist/{mcp-LMV3NQEP.js.map → mcp-MWTG3LRA.js.map} +0 -0
  92. /package/dist/{openapi-UJSUQPNT.js.map → openapi-RVFUXXCR.js.map} +0 -0
  93. /package/dist/{preview-AMNBV2DP.js.map → preview-ML4CFUD6.js.map} +0 -0
  94. /package/dist/{routes-ENMU4GAN.js.map → routes-SMNSI2ZX.js.map} +0 -0
  95. /package/dist/{server-boundary-K4YQXDQT.js.map → server-boundary-HYLYGFZY.js.map} +0 -0
  96. /package/dist/{server-boundary-JM72DQLA.js.map → server-boundary-ZT6HN7ON.js.map} +0 -0
  97. /package/dist/{start-GAULIE5E.js.map → start-L5TGWGLU.js.map} +0 -0
  98. /package/dist/{vite-plugin-4ZMBW3KV.js.map → vite-plugin-OJND2LU5.js.map} +0 -0
@@ -4,7 +4,7 @@ import {
4
4
  RouterConventionError,
5
5
  createFileStampCache,
6
6
  scanAgents
7
- } from "./chunk-6R5F5YF2.js";
7
+ } from "./chunk-A6UFN6QQ.js";
8
8
  import {
9
9
  HTTP_METHODS
10
10
  } from "./chunk-DV34VG3N.js";
@@ -637,4 +637,4 @@ export {
637
637
  writeManifest,
638
638
  loadManifest
639
639
  };
640
- //# sourceMappingURL=chunk-V7ISZGNS.js.map
640
+ //# sourceMappingURL=chunk-7HGZ3CKL.js.map
@@ -250,24 +250,29 @@ function scanAgents(projectRoot, configured) {
250
250
  }
251
251
  const results = [];
252
252
  walkSourceFiles(agentsDir, { extensions: AGENT_EXTENSIONS }, (absPath) => {
253
- let rel = relative(agentsDir, absPath);
254
- rel = rel.replace(/\\/g, "/");
255
- rel = rel.slice(0, -extname(rel).length);
256
- if (TEST_FILE.test(rel)) return;
257
- const dirs = rel.split("/").slice(0, -1);
258
- if (dirs.some((segment) => AGENT_SUBFOLDERS.has(segment))) return;
259
- if (rel.endsWith("/index")) rel = rel.slice(0, -6);
260
- if (rel === "index" || rel === "") return;
261
- const agentPath = `/api/agents/${rel}`;
253
+ const name = agentNameOf(agentsDir, absPath);
254
+ if (name === void 0) return;
255
+ const agentPath = `/api/agents/${name}`;
262
256
  assertAgentDeclaresPolicy(absPath, agentPath);
263
257
  results.push({
264
258
  filePath: absPath,
265
259
  agentPath,
266
- name: rel
260
+ name
267
261
  });
268
262
  });
269
263
  return results;
270
264
  }
265
+ function agentNameOf(agentsDir, absPath) {
266
+ let rel = relative(agentsDir, absPath);
267
+ rel = rel.replace(/\\/g, "/");
268
+ rel = rel.slice(0, -extname(rel).length);
269
+ if (TEST_FILE.test(rel)) return void 0;
270
+ const dirs = rel.split("/").slice(0, -1);
271
+ if (dirs.some((segment) => AGENT_SUBFOLDERS.has(segment))) return void 0;
272
+ if (rel.endsWith("/index")) rel = rel.slice(0, -6);
273
+ if (rel === "index" || rel === "") return void 0;
274
+ return rel;
275
+ }
271
276
  var policyDeclarationCache = createFileStampCache();
272
277
  function assertAgentDeclaresPolicy(filePath, agentPath) {
273
278
  const declared = policyDeclarationCache.get(
@@ -284,4 +289,4 @@ export {
284
289
  createFileStampCache,
285
290
  scanAgents
286
291
  };
287
- //# sourceMappingURL=chunk-6R5F5YF2.js.map
292
+ //# sourceMappingURL=chunk-A6UFN6QQ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server/scan/agent-scan.ts","../src/server/scan/detect-agent-policy.ts","../src/server/scan/errors.ts","../src/server/scan/file-stamp-cache.ts"],"mappings":"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","names":["statSync","statSync"]}
1
+ {"version":3,"sources":["../src/server/scan/agent-scan.ts","../src/server/scan/detect-agent-policy.ts","../src/server/scan/errors.ts","../src/server/scan/file-stamp-cache.ts"],"mappings":"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","names":["statSync","statSync"]}
@@ -25,12 +25,22 @@ async function importUserModule(absoluteFilePath, parentURL = import.meta.url) {
25
25
  return await import(fileURL);
26
26
  } catch (err) {
27
27
  if (!isLoaderInteropError(err)) throw err;
28
- const { tsImport } = await import("tsx/esm/api");
29
- return await tsImport(fileURL, parentURL);
28
+ const scopedImport = await tsxNamespace();
29
+ return await scopedImport(fileURL, parentURL);
30
30
  }
31
31
  }
32
+ var namespacedImport;
33
+ function tsxNamespace() {
34
+ namespacedImport ??= (async () => {
35
+ const namespace = `theokit-user-modules-${process.pid}`;
36
+ const [esm, cjs] = await Promise.all([import("tsx/esm/api"), import("tsx/cjs/api")]);
37
+ cjs.register({ namespace });
38
+ return esm.register({ namespace }).import;
39
+ })();
40
+ return namespacedImport;
41
+ }
32
42
 
33
43
  export {
34
44
  importUserModule
35
45
  };
36
- //# sourceMappingURL=chunk-5KRMDP46.js.map
46
+ //# sourceMappingURL=chunk-AHYOVVJ7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/import-user-module.ts"],"mappings":";AA2CA,SAAS,qBAAqB;AAE9B,IAAM,iBAAiB;AAAA;AAAA;AAAA,EAGrB;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA;AAAA,EAGA;AAAA;AAAA,EAEA;AACF;AAEA,SAAS,qBAAqB,KAAuB;AACnD,MAAI,EAAE,eAAe,OAAQ,QAAO;AACpC,QAAM,MAAM,IAAI;AAChB,SAAO,eAAe,KAAK,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC;AACnD;AAQA,eAAsB,iBACpB,kBACA,YAAoB,YAAY,KACE;AAClC,QAAM,UAAU,cAAc,gBAAgB,EAAE;AAChD,MAAI;AACF,WAAQ,MAAM,OAAO;AAAA,EACvB,SAAS,KAAK;AACZ,QAAI,CAAC,qBAAqB,GAAG,EAAG,OAAM;AAUtC,UAAM,eAAe,MAAM,aAAa;AACxC,WAAQ,MAAM,aAAa,SAAS,SAAS;AAAA,EAC/C;AACF;AAIA,IAAI;AAMJ,SAAS,eAAsC;AAC7C,wBAAsB,YAAY;AAChC,UAAM,YAAY,wBAAwB,QAAQ,GAAG;AACrD,UAAM,CAAC,KAAK,GAAG,IAAI,MAAM,QAAQ,IAAI,CAAC,OAAO,aAAa,GAAG,OAAO,aAAa,CAAC,CAAC;AACnF,QAAI,SAAS,EAAE,UAAU,CAAC;AAC1B,WAAO,IAAI,SAAS,EAAE,UAAU,CAAC,EAAE;AAAA,EACrC,GAAG;AACH,SAAO;AACT;","names":[]}
@@ -2,7 +2,7 @@
2
2
  import "tsx/esm";
3
3
  import {
4
4
  scanAgents
5
- } from "./chunk-HKF6YNY4.js";
5
+ } from "./chunk-FU3LEX2I.js";
6
6
  import {
7
7
  compilePattern,
8
8
  scanServerActions,
@@ -87,4 +87,4 @@ export {
87
87
  writeManifest,
88
88
  loadManifest
89
89
  };
90
- //# sourceMappingURL=chunk-WR5B2XNT.js.map
90
+ //# sourceMappingURL=chunk-FNAEKJHO.js.map
@@ -88,24 +88,37 @@ function scanAgents(projectRoot, configured) {
88
88
  }
89
89
  const results = [];
90
90
  walkSourceFiles(agentsDir, { extensions: AGENT_EXTENSIONS }, (absPath) => {
91
- let rel = relative(agentsDir, absPath);
92
- rel = rel.replace(/\\/g, "/");
93
- rel = rel.slice(0, -extname(rel).length);
94
- if (TEST_FILE.test(rel)) return;
95
- const dirs = rel.split("/").slice(0, -1);
96
- if (dirs.some((segment) => AGENT_SUBFOLDERS.has(segment))) return;
97
- if (rel.endsWith("/index")) rel = rel.slice(0, -6);
98
- if (rel === "index" || rel === "") return;
99
- const agentPath = `/api/agents/${rel}`;
91
+ const name = agentNameOf(agentsDir, absPath);
92
+ if (name === void 0) return;
93
+ const agentPath = `/api/agents/${name}`;
100
94
  assertAgentDeclaresPolicy(absPath, agentPath);
101
95
  results.push({
102
96
  filePath: absPath,
103
97
  agentPath,
104
- name: rel
98
+ name
105
99
  });
106
100
  });
107
101
  return results;
108
102
  }
103
+ function agentNameOf(agentsDir, absPath) {
104
+ let rel = relative(agentsDir, absPath);
105
+ rel = rel.replace(/\\/g, "/");
106
+ rel = rel.slice(0, -extname(rel).length);
107
+ if (TEST_FILE.test(rel)) return void 0;
108
+ const dirs = rel.split("/").slice(0, -1);
109
+ if (dirs.some((segment) => AGENT_SUBFOLDERS.has(segment))) return void 0;
110
+ if (rel.endsWith("/index")) rel = rel.slice(0, -6);
111
+ if (rel === "index" || rel === "") return void 0;
112
+ return rel;
113
+ }
114
+ function findAgentFile(agentsDir, name) {
115
+ if (!existsSync(agentsDir) || !statSync(agentsDir).isDirectory()) return void 0;
116
+ let found;
117
+ walkSourceFiles(agentsDir, { extensions: AGENT_EXTENSIONS }, (absPath) => {
118
+ if (found === void 0 && agentNameOf(agentsDir, absPath) === name) found = absPath;
119
+ });
120
+ return found;
121
+ }
109
122
  var policyDeclarationCache = createFileStampCache();
110
123
  function assertAgentDeclaresPolicy(filePath, agentPath) {
111
124
  const declared = policyDeclarationCache.get(
@@ -116,6 +129,7 @@ function assertAgentDeclaresPolicy(filePath, agentPath) {
116
129
  }
117
130
 
118
131
  export {
119
- scanAgents
132
+ scanAgents,
133
+ findAgentFile
120
134
  };
121
- //# sourceMappingURL=chunk-HKF6YNY4.js.map
135
+ //# sourceMappingURL=chunk-FU3LEX2I.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/scan/agent-scan.ts","../src/server/scan/detect-agent-policy.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * Build-time scanner: walks `<projectRoot>/agents/` derived from cwd.\n * No HTTP input ever reaches these fs calls.\n */\nimport { existsSync, readFileSync, statSync } from 'node:fs'\nimport { extname, join, relative } from 'node:path'\n\nimport { walkSourceFiles } from '../_internal/scan-walker.js'\n\nimport { declaresAgentPolicy } from './detect-agent-policy.js'\nimport { MissingAgentPolicyError } from './errors.js'\nimport { createFileStampCache } from './file-stamp-cache.js'\n\nconst AGENT_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx'])\n// Convention § 5: a co-located test file is not an agent.\nconst TEST_FILE = /\\.(test|spec)$/\n\n/**\n * Folder-semantic discovery (Eve-style \"file = identity\"): an agent may be a self-contained folder that\n * co-locates its composition — `agents/<name>/{index.ts, tools/, skills/, prompts/, lib/}`. Files INSIDE\n * these conventional sub-folders are that concern (a tool, a skill, a prompt, a helper), NOT a routed\n * agent, so the scanner skips any file whose path passes through one of them. Only `<name>.ts` or\n * `<name>/index.ts` is served at `/api/agents/<name>`. A flat file literally named `tools.ts` is still a\n * valid agent (the reserved names only apply to intermediate DIRECTORIES, never the agent file itself).\n */\nconst AGENT_SUBFOLDERS = new Set([\n 'tools',\n 'skills',\n 'prompts',\n 'lib',\n 'hooks',\n 'channels',\n 'connections',\n 'subagents',\n 'schedules',\n 'sandbox',\n 'workflows',\n 'evals',\n 'memory',\n])\n\n/**\n * A discovered agent file. `name` is the client-facing key; `agentPath` is the mounted\n * SSE route (M0/M1 `UIMessageStream`).\n */\nexport interface AgentNode {\n filePath: string\n agentPath: string\n name: string\n}\n\n/**\n * M2 — scan the TOP-LEVEL `agents/` convention (sibling of `server/`, per the LOCKED naming\n * decision). Mirrors `scanWebSocketRoutes`: one file → one endpoint, `index` stripped.\n */\n/**\n * Resolved paths already reported, so a per-request scan says it once — B-249, NFR-001.\n *\n * Keyed by the RESOLVED path rather than by the process: a test harness legitimately scans two\n * roots, and each distinct mistake deserves its own line. Same shape as `warnedAboutLegacySalt` in\n * `packages/http/src/action-encryption.ts`, and for the same reason: `scanAgents` runs per request\n * on a scanned deploy target, so an unbounded warning is a log flood on the exact path an anonymous\n * caller can drive.\n */\nconst reportedMissingDirs = new Set<string>()\n\nexport function scanAgents(projectRoot: string, configured?: string): AgentNode[] {\n const agentsDirName = configured ?? 'agents'\n // `agentsDirName` (config `agentsDir`, default \"agents\") — the dir holding `<name>.ts` agent\n // definitions, relative to the project root. May be nested (e.g. \"core/agents\"). (#95 follow-up)\n const agentsDir = join(projectRoot, agentsDirName)\n if (!existsSync(agentsDir) || !statSync(agentsDir).isDirectory()) {\n // B-249 — the same `[]` as before, and no longer the same silence.\n //\n // A configured value that resolves to nothing produced a result byte-identical to \"this app\n // declares no agents\", so a deployed `/api/agents/<name>` answered 404 with an empty log and an\n // operator had no reason to suspect configuration. The loudest way in is an ABSOLUTE path —\n // this parameter is a NAME joined onto the root, so `join()` silently yields\n // `<root><abs>` — but a typo, a missing nesting level and a directory a build did not copy all\n // land in exactly the same place, and one rule covers all four.\n //\n // Silent when NOTHING was configured: a project with no agents is the ordinary case, and a gate\n // that fires on ordinary work is a gate somebody disables. The parameter is OPTIONAL rather\n // than defaulted for exactly that reason — a default erases the difference between \"nobody\n // configured this\" and \"somebody configured 'agents'\", and only the first should stay quiet.\n //\n // The return value does not move (FR-003) and the contract stays relative (FR-004): reporting a\n // path that does not exist tells the operator what went wrong without widening a documented\n // public contract, which is a decision for a plan rather than a repair.\n if (configured !== undefined && !reportedMissingDirs.has(agentsDir)) {\n reportedMissingDirs.add(agentsDir)\n console.warn(\n `[theokit] agentsDir ${JSON.stringify(agentsDirName)} resolves to ${JSON.stringify(agentsDir)}, ` +\n `which is not a directory, so NO agents were found and every /api/agents/* route will 404. ` +\n `The value is a path relative to the project root ${JSON.stringify(projectRoot)} — an ` +\n `absolute path is joined onto it and cannot resolve.`,\n )\n }\n return []\n }\n\n const results: AgentNode[] = []\n walkSourceFiles(agentsDir, { extensions: AGENT_EXTENSIONS }, (absPath) => {\n const name = agentNameOf(agentsDir, absPath)\n if (name === undefined) return\n const agentPath = `/api/agents/${name}`\n assertAgentDeclaresPolicy(absPath, agentPath)\n results.push({\n filePath: absPath,\n agentPath,\n name,\n })\n })\n return results\n}\n\n/**\n * The name the agent at `absPath` is served under, or `undefined` when the file is not an agent:\n * a test file, a file under a composition sub-folder, or a bare `index`. The one definition of\n * \"which file is agent `<name>`\", shared by `scanAgents` and `findAgentFile`.\n */\nfunction agentNameOf(agentsDir: string, absPath: string): string | undefined {\n let rel = relative(agentsDir, absPath)\n rel = rel.replace(/\\\\/g, '/')\n rel = rel.slice(0, -extname(rel).length)\n if (TEST_FILE.test(rel)) return undefined\n // Eve-style: a file living under a composition sub-folder (tools/, skills/, prompts/, …) is that\n // concern, not a routed agent. Check the intermediate DIRECTORIES only — the agent file itself may\n // be named anything.\n const dirs = rel.split('/').slice(0, -1)\n if (dirs.some((segment) => AGENT_SUBFOLDERS.has(segment))) return undefined\n // Unlike routes/ws, an agent needs an explicit name — a bare `agents/index.ts`\n // (name `''` → `/api/agents/`) is nonsensical for a typed `useAgent(name)` binding.\n // `agents/foo/index.ts` still collapses to `foo` (a named nested agent).\n if (rel.endsWith('/index')) rel = rel.slice(0, -6)\n if (rel === 'index' || rel === '') return undefined\n return rel\n}\n\n/**\n * The file `scanAgents` would serve as agent `name` under the absolute directory `agentsDir`, or\n * `undefined` when there is none. Unlike `scanAgents` it does not refuse an agent that declares no\n * policy: a caller asking where one agent lives (the schedule generator) is not mounting it, and\n * the build still refuses the undeclared one when it scans.\n */\nexport function findAgentFile(agentsDir: string, name: string): string | undefined {\n if (!existsSync(agentsDir) || !statSync(agentsDir).isDirectory()) return undefined\n let found: string | undefined\n walkSourceFiles(agentsDir, { extensions: AGENT_EXTENSIONS }, (absPath) => {\n if (found === undefined && agentNameOf(agentsDir, absPath) === name) found = absPath\n })\n return found\n}\n\n/**\n * Cache of the policy-declaration answer, keyed by path + mtime + size.\n *\n * `theokit dev` re-scans the agents directory on EVERY request, so parsing each agent file with the\n * TypeScript AST per request would turn a build-time check into per-request latency. Keying on the\n * file's own mtime and size means an edit invalidates the entry without anyone remembering to, and\n * a `theokit build` (one process, one scan) never notices the cache exists.\n */\nconst policyDeclarationCache = createFileStampCache<boolean>()\n\n/** Test seam — the module-level cache would otherwise outlive a fixture directory. */\nexport function _resetAgentPolicyCacheForTests(): void {\n policyDeclarationCache.clear()\n}\n\n/**\n * Refuse an agent file that declares no access policy (usetheokit/theokit#365).\n *\n * The refusal lives in the scanner, next to the reserved-name and empty-name refusals, because this\n * is the one place every entry point passes through: `theokit build`, `theokit start`, `theokit dev`\n * and the manifest generator all reach agents by calling `scanAgents`.\n *\n * The blast radius stops at the file system, exactly as it does for routes. A module handed to\n * `mountAgent` in memory — a test, an embedder, an `@Expose`d controller method — never passed a\n * scanner, and `admitAgentRequest` still treats an undeclared policy as \"not declared\" rather than\n * as denial. Absence is refused where an application DECLARES its agents, not where a caller passes\n * one.\n */\nfunction assertAgentDeclaresPolicy(filePath: string, agentPath: string): void {\n // The stamping and the `has`-vs-truthy subtlety moved into `createFileStampCache` (#417), where\n // the route scanner shares them rather than growing a second copy keyed \"the same way\".\n const declared = policyDeclarationCache.get(filePath, () =>\n declaresAgentPolicy(filePath, readFileSync(filePath, 'utf8')),\n )\n if (!declared) throw new MissingAgentPolicyError({ file: filePath, agentPath })\n}\n","/**\n * Does this agent file declare an access policy? (usetheokit/theokit#365)\n *\n * The sibling of `detect-route-policy.ts`, and deliberately the same instrument: the TypeScript\n * AST rather than a regex, because `// policy: ...` in a comment and the word `policy` in a doc\n * block are both things an agent file legitimately contains, and neither declares anything.\n *\n * ## What it looks for, and why it is simpler than the route detector\n *\n * A route file declares one policy PER exported HTTP method, so the route detector has to return a\n * set and reason about which method carried which config. An agent file is one agent, so the\n * question is a single yes or no: is there a top-level export named `policy`?\n *\n * ```ts\n * export const policy = 'public'\n * export const policy = ({ subject, params }) => requireOwner(subject, ownerOf(params.sessionId))\n * const policy = ...; export { policy }\n * export { agentPolicy as policy } from './shared' // ← also counts; see below\n * ```\n *\n * ## Where this is deliberately laxer than the route detector\n *\n * The route detector refuses `export { GET } from './shared'`, on the reasoning that it cannot see\n * whether the re-exported value declares a policy. Here there is nothing to look inside: the export\n * IS the policy, so a re-export names one as surely as a local const does. The value's SHAPE is\n * checked at runtime by `readAgentPolicy`, which throws on anything that is neither `'public'` nor\n * a function — so a wrong type fails loudly rather than being served open.\n */\nimport { createRequire } from 'node:module'\n\nimport type * as TS from 'typescript'\n\n/**\n * The compiler, loaded on first use and never at import time.\n *\n * This was module scope, and a Cloudflare deploy found what that costs: `theokit/server/scan` is\n * imported by the generated worker (through `agent-scan.ts`), Cloudflare EXECUTES the module during\n * validation, and there `import.meta.url` is undefined — so `createRequire` threw before the worker\n * ever answered a request (B-263, error 10021). `wrangler deploy --dry-run` had returned exit 0 on\n * the same bundle minutes earlier: it bundles and does not execute.\n *\n * The second reason is independent of Workers and would hold anyway: this is a BUILD-TIME AST\n * scanner, and nothing that merely imports the module should pay for the TypeScript compiler.\n *\n * Cached, so scanning many files still loads it once.\n */\nlet compiler: typeof TS | undefined\n\nfunction ts_(): typeof TS {\n compiler ??= createRequire(import.meta.url)('typescript') as typeof TS\n return compiler\n}\n\nconst POLICY_EXPORT = 'policy'\n\nfunction hasExportModifier(modifiers: readonly TS.Modifier[] | undefined): boolean {\n if (!modifiers) return false\n for (const m of modifiers) {\n if (m.kind === ts_().SyntaxKind.ExportKeyword) return true\n }\n return false\n}\n\n/** `export const policy = ...` (also `let`/`var`, which are legal if unusual). */\nfunction variableStatementDeclaresPolicy(stmt: TS.VariableStatement): boolean {\n if (!hasExportModifier(ts_().getModifiers(stmt))) return false\n return stmt.declarationList.declarations.some(\n (decl) => ts_().isIdentifier(decl.name) && decl.name.text === POLICY_EXPORT,\n )\n}\n\n/** `export { policy }`, `export { p as policy }`, `export { p as policy } from './shared'`. */\nfunction exportDeclarationDeclaresPolicy(stmt: TS.ExportDeclaration): boolean {\n if (!stmt.exportClause || !ts_().isNamedExports(stmt.exportClause)) return false\n return stmt.exportClause.elements.some((spec) => spec.name.text === POLICY_EXPORT)\n}\n\n/** `export function policy() {}` — a function declaration is a policy as much as a const is. */\nfunction functionDeclarationDeclaresPolicy(stmt: TS.FunctionDeclaration): boolean {\n return hasExportModifier(ts_().getModifiers(stmt)) && stmt.name?.text === POLICY_EXPORT\n}\n\n/**\n * True when the file has a top-level export named `policy`.\n *\n * `content` is passed in rather than read here so the caller reads each file once — the same\n * contract `detectRoutePolicyKinds` follows.\n */\nexport function declaresAgentPolicy(filePath: string, content: string): boolean {\n const sourceFile = ts_().createSourceFile(\n filePath,\n content,\n ts_().ScriptTarget.Latest,\n /* setParentNodes */ false,\n ts_().ScriptKind.TS,\n )\n for (const stmt of sourceFile.statements) {\n if (ts_().isVariableStatement(stmt) && variableStatementDeclaresPolicy(stmt)) return true\n if (ts_().isExportDeclaration(stmt) && exportDeclarationDeclaresPolicy(stmt)) return true\n if (ts_().isFunctionDeclaration(stmt) && functionDeclarationDeclaresPolicy(stmt)) return true\n }\n return false\n}\n"],"mappings":"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","names":[]}
@@ -26,7 +26,7 @@ import {
26
26
  parseAgentRequestBody,
27
27
  readAgentPolicy,
28
28
  subjectFromContext
29
- } from "./chunk-DA3GGICE.js";
29
+ } from "./chunk-RQKMY3JW.js";
30
30
  import {
31
31
  getApprovalRegistry
32
32
  } from "./chunk-BINRCILF.js";
@@ -901,4 +901,4 @@ export {
901
901
  applyNonceToInlineScripts,
902
902
  setupSsrDevMiddleware
903
903
  };
904
- //# sourceMappingURL=chunk-VD3BCME6.js.map
904
+ //# sourceMappingURL=chunk-I5PDEWZZ.js.map
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ import "tsx/esm";
3
+ import {
4
+ compareByCodeUnit,
5
+ walkSourceFiles
6
+ } from "./chunk-Z7CJHDLS.js";
7
+ import {
8
+ importUserModule
9
+ } from "./chunk-O5JZ6ROY.js";
10
+
11
+ // src/server/cron/cron-scan.ts
12
+ import { existsSync } from "fs";
13
+ import { basename, resolve } from "path";
14
+ var DuplicateCronNameError = class extends Error {
15
+ constructor(cronName, filePaths) {
16
+ super(
17
+ `Duplicate cron name "${cronName}" defined in: ${filePaths.join(", ")}. Cron names must be unique across server/crons/ and agents/schedules/.`
18
+ );
19
+ this.cronName = cronName;
20
+ this.filePaths = filePaths;
21
+ this.name = "DuplicateCronNameError";
22
+ }
23
+ cronName;
24
+ filePaths;
25
+ code = "DUPLICATE_CRON_NAME";
26
+ };
27
+ var CRON_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".mjs"]);
28
+ function isCronDefinition(value) {
29
+ if (typeof value !== "object" || value === null) return false;
30
+ const def = value;
31
+ return typeof def.name === "string" && typeof def.schedule === "string" && typeof def.handler === "function" && (def.concurrency === "forbid" || def.concurrency === "allow");
32
+ }
33
+ function cronScanDirs(cwd, serverDir, agentsDir) {
34
+ return {
35
+ crons: resolve(cwd, serverDir, "crons"),
36
+ schedules: resolve(cwd, agentsDir, "schedules")
37
+ };
38
+ }
39
+ function isDiscoverableCronFile(fileName) {
40
+ return !fileName.startsWith("_") && !fileName.startsWith(".");
41
+ }
42
+ function listCronFiles(dirs) {
43
+ const filePaths = [];
44
+ for (const dir of dirs) {
45
+ if (!existsSync(dir)) continue;
46
+ walkSourceFiles(dir, { extensions: CRON_EXTENSIONS }, (p) => {
47
+ if (isDiscoverableCronFile(basename(p))) filePaths.push(p);
48
+ });
49
+ }
50
+ return filePaths;
51
+ }
52
+ async function scanCronDirs(dirs) {
53
+ const filePaths = listCronFiles(dirs);
54
+ const nodes = [];
55
+ for (const filePath of filePaths) {
56
+ let mod;
57
+ try {
58
+ mod = await importUserModule(filePath);
59
+ } catch (err) {
60
+ const reason = err instanceof Error ? err.message : String(err);
61
+ throw new Error(`Failed to import cron file "${filePath}": ${reason}`);
62
+ }
63
+ const exported = mod.default;
64
+ if (!isCronDefinition(exported)) {
65
+ throw new Error(
66
+ `Cron file "${filePath}" is missing a valid default export. Expected \`export default defineCron(name, { schedule, handler })\`.`
67
+ );
68
+ }
69
+ nodes.push({
70
+ name: exported.name,
71
+ filePath,
72
+ schedule: exported.schedule,
73
+ concurrency: exported.concurrency
74
+ });
75
+ }
76
+ const byName = /* @__PURE__ */ new Map();
77
+ for (const node of nodes) {
78
+ const bucket = byName.get(node.name) ?? [];
79
+ bucket.push(node.filePath);
80
+ byName.set(node.name, bucket);
81
+ }
82
+ for (const [name, paths] of byName) {
83
+ if (paths.length > 1) {
84
+ throw new DuplicateCronNameError(name, paths);
85
+ }
86
+ }
87
+ return nodes.sort((a, b) => compareByCodeUnit(a.name, b.name));
88
+ }
89
+
90
+ export {
91
+ cronScanDirs,
92
+ isDiscoverableCronFile,
93
+ listCronFiles,
94
+ scanCronDirs
95
+ };
96
+ //# sourceMappingURL=chunk-IWIRHUNV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/cron/cron-scan.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * Build-time scanner: caller-controlled directory paths only. No HTTP\n * input ever reaches these fs calls.\n */\nimport { existsSync } from 'node:fs'\nimport { basename, resolve } from 'node:path'\n\nimport { importUserModule } from '../../config/import-user-module.js'\nimport { compareByCodeUnit } from '../_internal/compare-by-code-unit.js'\nimport { walkSourceFiles } from '../_internal/scan-walker.js'\n\nimport type { CronConcurrencyPolicy, CronDefinition } from './cron-types.js'\n\n/**\n * One discovered cron from build-time scan. The handler is intentionally\n * NOT included — manifest is platform-neutral and consumed by adapters\n * that emit static config. Runtime dispatch loads the handler at fire time.\n */\nexport interface CronNode {\n readonly name: string\n readonly filePath: string\n readonly schedule: string\n readonly concurrency: CronConcurrencyPolicy\n}\n\nexport class DuplicateCronNameError extends Error {\n readonly code = 'DUPLICATE_CRON_NAME'\n constructor(\n public readonly cronName: string,\n public readonly filePaths: readonly string[],\n ) {\n super(\n `Duplicate cron name \"${cronName}\" defined in: ${filePaths.join(', ')}. ` +\n 'Cron names must be unique across server/crons/ and agents/schedules/.',\n )\n this.name = 'DuplicateCronNameError'\n }\n}\n\nconst CRON_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.mjs'])\n\ninterface CronModule {\n default?: unknown\n}\n\nfunction isCronDefinition(value: unknown): value is CronDefinition {\n if (typeof value !== 'object' || value === null) return false\n const def = value as Record<string, unknown>\n return (\n typeof def.name === 'string' &&\n typeof def.schedule === 'string' &&\n typeof def.handler === 'function' &&\n (def.concurrency === 'forbid' || def.concurrency === 'allow')\n )\n}\n\n/**\n * The two homes `theokit build` scans for crons, absolute: `<serverDir>/crons` (a backend trigger)\n * and `<agentsDir>/schedules` (a scheduled agent run). One definition, read by the build and by the\n * schedule generator, so a generated schedule lands where the build looks.\n */\nexport function cronScanDirs(\n cwd: string,\n serverDir: string,\n agentsDir: string,\n): { readonly crons: string; readonly schedules: string } {\n return {\n crons: resolve(cwd, serverDir, 'crons'),\n schedules: resolve(cwd, agentsDir, 'schedules'),\n }\n}\n\n/**\n * Whether the scanner treats a file named `fileName` as a cron. Private helpers (`_helper.ts`) and\n * dotfiles (`.DS_Store`, or `.ts` from an empty name) are skipped.\n */\nexport function isDiscoverableCronFile(fileName: string): boolean {\n return !fileName.startsWith('_') && !fileName.startsWith('.')\n}\n\n/**\n * The cron files {@link scanCronDirs} would import from `dirs`, without importing them. Missing\n * dirs are skipped.\n */\nexport function listCronFiles(dirs: readonly string[]): string[] {\n const filePaths: string[] = []\n for (const dir of dirs) {\n if (!existsSync(dir)) continue\n walkSourceFiles(dir, { extensions: CRON_EXTENSIONS }, (p) => {\n if (isDiscoverableCronFile(basename(p))) filePaths.push(p)\n })\n }\n return filePaths\n}\n\n/**\n * Scan a directory for cron definition files and return the discovered\n * `CronNode[]` sorted by name. Throws `DuplicateCronNameError` on name\n * collision and `Error` on missing default export.\n *\n * Sequential by design — module imports are awaited one-by-one to keep\n * error messages anchored to the file that failed.\n */\nexport async function scanCrons(cronsDir: string): Promise<CronNode[]> {\n return scanCronDirs([cronsDir])\n}\n\n/**\n * Scan MULTIPLE cron directories and merge the results with a UNIFIED\n * duplicate-name guard across all of them. The framework discovers crons in\n * two conventional homes: `server/crons/` (a backend trigger) and\n * `agents/schedules/` (a scheduled agent run — kept in the agent domain). Both\n * feed the same manifest + deploy translation; a name may not collide across\n * the two dirs. Missing dirs are skipped (no error). Nodes are returned sorted\n * by name for a stable manifest.\n */\nexport async function scanCronDirs(dirs: readonly string[]): Promise<CronNode[]> {\n const filePaths = listCronFiles(dirs)\n\n const nodes: CronNode[] = []\n for (const filePath of filePaths) {\n let mod: CronModule\n try {\n mod = await importUserModule(filePath)\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err)\n throw new Error(`Failed to import cron file \"${filePath}\": ${reason}`)\n }\n const exported = mod.default\n if (!isCronDefinition(exported)) {\n throw new Error(\n `Cron file \"${filePath}\" is missing a valid default export. ` +\n 'Expected `export default defineCron(name, { schedule, handler })`.',\n )\n }\n nodes.push({\n name: exported.name,\n filePath,\n schedule: exported.schedule,\n concurrency: exported.concurrency,\n })\n }\n\n // Dup-name detection (unified across every scanned dir).\n const byName = new Map<string, string[]>()\n for (const node of nodes) {\n const bucket = byName.get(node.name) ?? []\n bucket.push(node.filePath)\n byName.set(node.name, bucket)\n }\n for (const [name, paths] of byName) {\n if (paths.length > 1) {\n throw new DuplicateCronNameError(name, paths)\n }\n }\n\n return nodes.sort((a, b) => compareByCodeUnit(a.name, b.name))\n}\n"],"mappings":";;;;;;;;;;;AAIA,SAAS,kBAAkB;AAC3B,SAAS,UAAU,eAAe;AAoB3B,IAAM,yBAAN,cAAqC,MAAM;AAAA,EAEhD,YACkB,UACA,WAChB;AACA;AAAA,MACE,wBAAwB,QAAQ,iBAAiB,UAAU,KAAK,IAAI,CAAC;AAAA,IAEvE;AANgB;AACA;AAMhB,SAAK,OAAO;AAAA,EACd;AAAA,EARkB;AAAA,EACA;AAAA,EAHT,OAAO;AAWlB;AAEA,IAAM,kBAAkB,oBAAI,IAAI,CAAC,OAAO,QAAQ,OAAO,MAAM,CAAC;AAM9D,SAAS,iBAAiB,OAAyC;AACjE,MAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,QAAM,MAAM;AACZ,SACE,OAAO,IAAI,SAAS,YACpB,OAAO,IAAI,aAAa,YACxB,OAAO,IAAI,YAAY,eACtB,IAAI,gBAAgB,YAAY,IAAI,gBAAgB;AAEzD;AAOO,SAAS,aACd,KACA,WACA,WACwD;AACxD,SAAO;AAAA,IACL,OAAO,QAAQ,KAAK,WAAW,OAAO;AAAA,IACtC,WAAW,QAAQ,KAAK,WAAW,WAAW;AAAA,EAChD;AACF;AAMO,SAAS,uBAAuB,UAA2B;AAChE,SAAO,CAAC,SAAS,WAAW,GAAG,KAAK,CAAC,SAAS,WAAW,GAAG;AAC9D;AAMO,SAAS,cAAc,MAAmC;AAC/D,QAAM,YAAsB,CAAC;AAC7B,aAAW,OAAO,MAAM;AACtB,QAAI,CAAC,WAAW,GAAG,EAAG;AACtB,oBAAgB,KAAK,EAAE,YAAY,gBAAgB,GAAG,CAAC,MAAM;AAC3D,UAAI,uBAAuB,SAAS,CAAC,CAAC,EAAG,WAAU,KAAK,CAAC;AAAA,IAC3D,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAuBA,eAAsB,aAAa,MAA8C;AAC/E,QAAM,YAAY,cAAc,IAAI;AAEpC,QAAM,QAAoB,CAAC;AAC3B,aAAW,YAAY,WAAW;AAChC,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,iBAAiB,QAAQ;AAAA,IACvC,SAAS,KAAK;AACZ,YAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC9D,YAAM,IAAI,MAAM,+BAA+B,QAAQ,MAAM,MAAM,EAAE;AAAA,IACvE;AACA,UAAM,WAAW,IAAI;AACrB,QAAI,CAAC,iBAAiB,QAAQ,GAAG;AAC/B,YAAM,IAAI;AAAA,QACR,cAAc,QAAQ;AAAA,MAExB;AAAA,IACF;AACA,UAAM,KAAK;AAAA,MACT,MAAM,SAAS;AAAA,MACf;AAAA,MACA,UAAU,SAAS;AAAA,MACnB,aAAa,SAAS;AAAA,IACxB,CAAC;AAAA,EACH;AAGA,QAAM,SAAS,oBAAI,IAAsB;AACzC,aAAW,QAAQ,OAAO;AACxB,UAAM,SAAS,OAAO,IAAI,KAAK,IAAI,KAAK,CAAC;AACzC,WAAO,KAAK,KAAK,QAAQ;AACzB,WAAO,IAAI,KAAK,MAAM,MAAM;AAAA,EAC9B;AACA,aAAW,CAAC,MAAM,KAAK,KAAK,QAAQ;AAClC,QAAI,MAAM,SAAS,GAAG;AACpB,YAAM,IAAI,uBAAuB,MAAM,KAAK;AAAA,IAC9C;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,CAAC,GAAG,MAAM,kBAAkB,EAAE,MAAM,EAAE,IAAI,CAAC;AAC/D;","names":[]}
@@ -28,12 +28,22 @@ async function importUserModule(absoluteFilePath, parentURL = import.meta.url) {
28
28
  return await import(fileURL);
29
29
  } catch (err) {
30
30
  if (!isLoaderInteropError(err)) throw err;
31
- const { tsImport } = await import("tsx/esm/api");
32
- return await tsImport(fileURL, parentURL);
31
+ const scopedImport = await tsxNamespace();
32
+ return await scopedImport(fileURL, parentURL);
33
33
  }
34
34
  }
35
+ var namespacedImport;
36
+ function tsxNamespace() {
37
+ namespacedImport ??= (async () => {
38
+ const namespace = `theokit-user-modules-${process.pid}`;
39
+ const [esm, cjs] = await Promise.all([import("tsx/esm/api"), import("tsx/cjs/api")]);
40
+ cjs.register({ namespace });
41
+ return esm.register({ namespace }).import;
42
+ })();
43
+ return namespacedImport;
44
+ }
35
45
 
36
46
  export {
37
47
  importUserModule
38
48
  };
39
- //# sourceMappingURL=chunk-U5PWXS3J.js.map
49
+ //# sourceMappingURL=chunk-O5JZ6ROY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/import-user-module.ts"],"sourcesContent":["/**\n * Helper for loading user-authored TypeScript modules at runtime.\n *\n * Why this exists: theokit framework code (`load-config.ts`, `job-scan.ts`,\n * `cron-scan.ts`, `integrate-ui.ts`, `static.ts`) dynamically imports\n * `.ts` files from user space (`theo.config.ts`, `server/jobs/*.ts`,\n * `static-paths.ts`, etc.). Two execution environments must work:\n *\n * 1. **Production CLI** — `dist/cli/index.js` starts with\n * `import \"tsx/esm\"` which registers a global Node ESM loader hook.\n * Subsequent `await import('foo.ts')` calls are intercepted by the\n * hook, transformed in-place, and returned as ESM namespaces.\n *\n * 2. **Vitest tests** — Vitest runs framework code through Vite's SSR\n * pipeline. Dynamic imports inside framework modules are hijacked\n * by Vite's `loadAndTransform`, which cannot resolve absolute paths\n * outside the Vite project root (e.g., `/tmp/test-fixture/foo.ts`).\n * Even with `server.deps.external` configured to hand the path\n * back to Node, the registered tsx/esm loader hits a `require()`\n * ESM cycle error on Node 22 because the calling stack contains\n * both the loader's own evaluation context AND the importer.\n *\n * A namespaced `register()` from `tsx/esm/api` is the escape hatch for\n * exactly this scenario: it transforms the source file via tsx's\n * standalone pipeline and evaluates the result inside its namespace,\n * bypassing both Vite SSR and any registered global hook.\n *\n * The namespace is registered ONCE per process and reused. `tsImport()`\n * registers a new one on every call and never removes it, so the loader\n * hooks pile up; on Node 22.12 the fourth or fifth load in a process\n * stalled forever. Reusing one namespace means a file already loaded is\n * served from the module cache, which is what the native `import()` above\n * does too.\n *\n * Strategy:\n * - Try native `await import()` first (zero overhead in production\n * where tsx hook is registered).\n * - On failure with the well-known cycle/resolution errors, fall back\n * to `tsImport()`. Other errors (genuine syntax errors, missing\n * module, etc.) propagate unchanged.\n *\n * Idempotent and side-effect-free.\n */\nimport { pathToFileURL } from 'node:url'\n\nconst CYCLE_PATTERNS = [\n // Node 22 strict ESM cycle detection when a registered loader hook\n // re-enters its own evaluation context.\n 'require() ES Module',\n 'in a cycle',\n // Vite SSR throws this when it cannot resolve an out-of-root URL.\n 'Failed to load url',\n 'Does the file exist?',\n // Node native ESM resolver throws this for `.ts` files when no\n // tsx loader is registered globally (the common test-worker case).\n 'Cannot find module',\n // ERR_UNKNOWN_FILE_EXTENSION for `.ts` files.\n 'Unknown file extension',\n]\n\nfunction isLoaderInteropError(err: unknown): boolean {\n if (!(err instanceof Error)) return false\n const msg = err.message\n return CYCLE_PATTERNS.some((p) => msg.includes(p))\n}\n\n/**\n * Load a user-authored TS/JS module at the given absolute file path.\n * `parentURL` defaults to the URL of this module file.\n *\n * @returns module namespace object (matches `await import(...)` return)\n */\nexport async function importUserModule(\n absoluteFilePath: string,\n parentURL: string = import.meta.url,\n): Promise<Record<string, unknown>> {\n const fileURL = pathToFileURL(absoluteFilePath).href\n try {\n return (await import(fileURL)) as Record<string, unknown>\n } catch (err) {\n if (!isLoaderInteropError(err)) throw err\n // Fallback: tsx programmatic API. Imports are scoped to one namespace, no\n // global hook mutation, no Vite SSR interference. Slower than a registered\n // loader, but only used as a last resort.\n //\n // No tsconfig is passed — tsx's default bundler-style resolver handles\n // `.js → .ts` fallback for relative imports under the user's module.\n // Passing an explicit tsconfig sometimes shadows the default and\n // breaks the `.js → .ts` rewrite chain inside the namespaced loader\n // (verified empirically in cron-scan/job-scan vitest workers).\n const scopedImport = await tsxNamespace()\n return (await scopedImport(fileURL, parentURL)) as Record<string, unknown>\n }\n}\n\ntype ScopedImport = (specifier: string, parentURL: string) => Promise<unknown>\n\nlet namespacedImport: Promise<ScopedImport> | undefined\n\n/**\n * The one tsx namespace this process loads user modules through. Registered on first use, with\n * the CommonJS hook beside the ESM one, the same pair `tsImport()` registers per call.\n */\nfunction tsxNamespace(): Promise<ScopedImport> {\n namespacedImport ??= (async () => {\n const namespace = `theokit-user-modules-${process.pid}`\n const [esm, cjs] = await Promise.all([import('tsx/esm/api'), import('tsx/cjs/api')])\n cjs.register({ namespace })\n return esm.register({ namespace }).import\n })()\n return namespacedImport\n}\n"],"mappings":";;;;AA2CA,SAAS,qBAAqB;AAE9B,IAAM,iBAAiB;AAAA;AAAA;AAAA,EAGrB;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA;AAAA,EAGA;AAAA;AAAA,EAEA;AACF;AAEA,SAAS,qBAAqB,KAAuB;AACnD,MAAI,EAAE,eAAe,OAAQ,QAAO;AACpC,QAAM,MAAM,IAAI;AAChB,SAAO,eAAe,KAAK,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC;AACnD;AAQA,eAAsB,iBACpB,kBACA,YAAoB,YAAY,KACE;AAClC,QAAM,UAAU,cAAc,gBAAgB,EAAE;AAChD,MAAI;AACF,WAAQ,MAAM,OAAO;AAAA,EACvB,SAAS,KAAK;AACZ,QAAI,CAAC,qBAAqB,GAAG,EAAG,OAAM;AAUtC,UAAM,eAAe,MAAM,aAAa;AACxC,WAAQ,MAAM,aAAa,SAAS,SAAS;AAAA,EAC/C;AACF;AAIA,IAAI;AAMJ,SAAS,eAAsC;AAC7C,wBAAsB,YAAY;AAChC,UAAM,YAAY,wBAAwB,QAAQ,GAAG;AACrD,UAAM,CAAC,KAAK,GAAG,IAAI,MAAM,QAAQ,IAAI,CAAC,OAAO,aAAa,GAAG,OAAO,aAAa,CAAC,CAAC;AACnF,QAAI,SAAS,EAAE,UAAU,CAAC;AAC1B,WAAO,IAAI,SAAS,EAAE,UAAU,CAAC,EAAE;AAAA,EACrC,GAAG;AACH,SAAO;AACT;","names":[]}
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-3QPVEJ7D.js";
6
6
  import {
7
7
  importUserModule
8
- } from "./chunk-U5PWXS3J.js";
8
+ } from "./chunk-O5JZ6ROY.js";
9
9
  import {
10
10
  theoConfigSchema
11
11
  } from "./chunk-NX3P5MLH.js";
@@ -110,4 +110,4 @@ export {
110
110
  deepMerge,
111
111
  loadConfig
112
112
  };
113
- //# sourceMappingURL=chunk-VOXLI66L.js.map
113
+ //# sourceMappingURL=chunk-OZB2TKGJ.js.map