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
@@ -11,12 +11,12 @@ import {
11
11
  } from "./chunk-KTMQUYED.js";
12
12
  import {
13
13
  theoPluginAsync
14
- } from "./chunk-XUAWD5HK.js";
15
- import "./chunk-VD3BCME6.js";
14
+ } from "./chunk-RKRFJRJ3.js";
15
+ import "./chunk-I5PDEWZZ.js";
16
16
  import "./chunk-BDH5GKFZ.js";
17
17
  import "./chunk-3PWQQWT6.js";
18
18
  import "./chunk-FDOOBTXJ.js";
19
- import "./chunk-DA3GGICE.js";
19
+ import "./chunk-RQKMY3JW.js";
20
20
  import "./chunk-BINRCILF.js";
21
21
  import "./chunk-FNJVSFEC.js";
22
22
  import "./chunk-WSMLSIS5.js";
@@ -24,17 +24,17 @@ import "./chunk-MX63AWEK.js";
24
24
  import "./chunk-C27VRUVN.js";
25
25
  import "./chunk-7UZZHCRN.js";
26
26
  import "./chunk-47MM2JUK.js";
27
- import "./chunk-WR5B2XNT.js";
28
- import "./chunk-HKF6YNY4.js";
27
+ import "./chunk-FNAEKJHO.js";
28
+ import "./chunk-FU3LEX2I.js";
29
29
  import "./chunk-4WP7A7RX.js";
30
30
  import "./chunk-Z7CJHDLS.js";
31
31
  import {
32
32
  loadConfig
33
- } from "./chunk-VOXLI66L.js";
33
+ } from "./chunk-OZB2TKGJ.js";
34
34
  import {
35
35
  loadEnv
36
36
  } from "./chunk-3QPVEJ7D.js";
37
- import "./chunk-U5PWXS3J.js";
37
+ import "./chunk-O5JZ6ROY.js";
38
38
  import "./chunk-NX3P5MLH.js";
39
39
  import {
40
40
  orchestrateDev
@@ -128,4 +128,4 @@ export {
128
128
  devCommand,
129
129
  startDevServer
130
130
  };
131
- //# sourceMappingURL=dev-TW6QD7F3.js.map
131
+ //# sourceMappingURL=dev-J2XZIENF.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  generateManifest
3
- } from "./chunk-V7ISZGNS.js";
4
- import "./chunk-6R5F5YF2.js";
3
+ } from "./chunk-7HGZ3CKL.js";
4
+ import "./chunk-A6UFN6QQ.js";
5
5
  import "./chunk-DV34VG3N.js";
6
6
  import "./chunk-SOYOYGWY.js";
7
7
  import "./chunk-ZWN3AODA.js";
@@ -339,4 +339,4 @@ export {
339
339
  _resetInFlightForTests,
340
340
  reEmitOpenApi
341
341
  };
342
- //# sourceMappingURL=dev-emit-T3X6RR7M.js.map
342
+ //# sourceMappingURL=dev-emit-AXTJK56H.js.map
@@ -6,8 +6,8 @@ import {
6
6
  } from "./chunk-47VNWTL4.js";
7
7
  import {
8
8
  generateManifest
9
- } from "./chunk-WR5B2XNT.js";
10
- import "./chunk-HKF6YNY4.js";
9
+ } from "./chunk-FNAEKJHO.js";
10
+ import "./chunk-FU3LEX2I.js";
11
11
  import "./chunk-4WP7A7RX.js";
12
12
  import "./chunk-Z7CJHDLS.js";
13
13
 
@@ -41,4 +41,4 @@ export {
41
41
  _resetInFlightForTests,
42
42
  reEmitOpenApi
43
43
  };
44
- //# sourceMappingURL=dev-emit-ZK5GCHXN.js.map
44
+ //# sourceMappingURL=dev-emit-XAPVJBM4.js.map
@@ -1,9 +1,41 @@
1
1
  #!/usr/bin/env node
2
2
  import "tsx/esm";
3
+ import {
4
+ cronScanDirs,
5
+ isDiscoverableCronFile,
6
+ listCronFiles
7
+ } from "./chunk-IWIRHUNV.js";
8
+ import {
9
+ findAgentFile
10
+ } from "./chunk-FU3LEX2I.js";
11
+ import "./chunk-Z7CJHDLS.js";
12
+ import {
13
+ loadConfig
14
+ } from "./chunk-OZB2TKGJ.js";
15
+ import "./chunk-3QPVEJ7D.js";
16
+ import "./chunk-O5JZ6ROY.js";
17
+ import "./chunk-NX3P5MLH.js";
18
+ import "./chunk-L4V5V2TI.js";
19
+ import "./chunk-D2R5SJEL.js";
3
20
 
4
21
  // src/cli/commands/generate.ts
5
22
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
6
- import { resolve as resolve2, dirname as dirname2 } from "path";
23
+ import { resolve as resolve4, dirname as dirname3 } from "path";
24
+
25
+ // src/cli/commands/generate-containment.ts
26
+ import { isAbsolute, relative, resolve, sep } from "path";
27
+ function isPathInside(cwd, filePath) {
28
+ if (filePath.includes("\0")) return false;
29
+ const fromRoot = relative(resolve(cwd), filePath);
30
+ return fromRoot !== "" && !isAbsolute(fromRoot) && fromRoot.split(sep)[0] !== "..";
31
+ }
32
+ function pathTraversalRefusal(cwd, filePath) {
33
+ if (isPathInside(cwd, filePath)) return void 0;
34
+ return {
35
+ status: "invalid_name",
36
+ message: `Path traversal denied: "${filePath}" is outside the project root ${resolve(cwd)}.`
37
+ };
38
+ }
7
39
 
8
40
  // src/cli/commands/generate-policy-placeholder.ts
9
41
  var UNDECIDED_POLICY_IDENT = "undecidedPolicy";
@@ -25,7 +57,7 @@ function undecidedPolicyDeclaration(routePath, what) {
25
57
 
26
58
  // src/cli/commands/generate-resource.ts
27
59
  import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
28
- import { resolve, dirname } from "path";
60
+ import { resolve as resolve2, dirname } from "path";
29
61
  var ALLOWED_FIELD_TYPES = /* @__PURE__ */ new Set(["string", "text", "number", "boolean"]);
30
62
  var RESERVED_FIELDS = /* @__PURE__ */ new Set(["id", "createdAt", "created_at"]);
31
63
  function parseResourceFields(args) {
@@ -205,10 +237,10 @@ function generateResource(cwd, name, fieldArgs) {
205
237
  } catch (err) {
206
238
  return { status: "invalid_name", message: err instanceof Error ? err.message : String(err) };
207
239
  }
208
- const schemaPath = resolve(cwd, "server/db/schema.ts");
209
- const routeIndexPath = resolve(cwd, `server/routes/${name}/index.ts`);
210
- const routeIdPath = resolve(cwd, `server/routes/${name}/[id].ts`);
211
- const testPath = resolve(cwd, `tests/${name}.test.ts`);
240
+ const schemaPath = resolve2(cwd, "server/db/schema.ts");
241
+ const routeIndexPath = resolve2(cwd, `server/routes/${name}/index.ts`);
242
+ const routeIdPath = resolve2(cwd, `server/routes/${name}/[id].ts`);
243
+ const testPath = resolve2(cwd, `tests/${name}.test.ts`);
212
244
  if (!existsSync(schemaPath)) {
213
245
  return {
214
246
  status: "not_a_project",
@@ -239,6 +271,133 @@ function generateResource(cwd, name, fieldArgs) {
239
271
  return { status: "created", filePath: routeIndexPath, kind: "resource", name };
240
272
  }
241
273
 
274
+ // src/cli/commands/generate-schedule.ts
275
+ import { basename, dirname as dirname2, extname, relative as relative2, resolve as resolve3, sep as sep2 } from "path";
276
+
277
+ // src/server/cron/define-cron.ts
278
+ var NAME_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
279
+ var CRON_NAME_RULE = "Must be 1-64 chars, lowercase alphanumeric + hyphen, starting with [a-z0-9].";
280
+ function isValidCronName(name) {
281
+ return typeof name === "string" && NAME_RE.test(name);
282
+ }
283
+
284
+ // src/cli/commands/generate-schedule.ts
285
+ async function resolveScheduleTarget(cwd, name) {
286
+ const malformed = cronNameRefusal(name);
287
+ if (malformed !== void 0) return malformed;
288
+ let config;
289
+ try {
290
+ config = await loadConfig(cwd);
291
+ } catch (err) {
292
+ return {
293
+ status: "invalid_config",
294
+ message: `Cannot read ${resolve3(cwd, "theo.config.ts")}: ${err.message}`
295
+ };
296
+ }
297
+ const { agentsDir, serverDir } = config;
298
+ const agentsRoot = resolve3(cwd, agentsDir);
299
+ const cronDirs = cronScanDirs(cwd, serverDir, agentsDir);
300
+ const filePath = resolve3(cronDirs.schedules, `${name}.ts`);
301
+ const escaped = pathTraversalRefusal(cwd, filePath);
302
+ if (escaped !== void 0) return escaped;
303
+ const chatPath = findAgentFile(agentsRoot, "chat");
304
+ if (chatPath === void 0) {
305
+ return {
306
+ status: "agent_not_found",
307
+ message: `No "chat" agent in ${agentsRoot} (the build serves it from chat.{ts,tsx,js,jsx} or chat/index.{ts,tsx,js,jsx}). A schedule runs the chat agent; create it first.`
308
+ };
309
+ }
310
+ const taken = cronNameTaken(name, filePath, [cronDirs.crons, cronDirs.schedules]);
311
+ if (taken !== void 0) return taken;
312
+ return { filePath, content: generateScheduleTemplate(name, importSpecifier(filePath, chatPath)) };
313
+ }
314
+ function cronNameOf(name) {
315
+ return name.split("/").pop() ?? name;
316
+ }
317
+ function cronNameRefusal(name) {
318
+ const cronName = cronNameOf(name);
319
+ if (!isDiscoverableCronFile(`${cronName}.ts`)) {
320
+ return {
321
+ status: "invalid_name",
322
+ message: `Invalid schedule name "${name}": the build does not discover a cron file named "${cronName}.ts".`
323
+ };
324
+ }
325
+ if (!isValidCronName(cronName)) {
326
+ return {
327
+ status: "invalid_name",
328
+ message: `Invalid schedule name "${name}": its cron name "${cronName}" is one defineCron rejects. ${CRON_NAME_RULE}`
329
+ };
330
+ }
331
+ return void 0;
332
+ }
333
+ function cronNameTaken(name, filePath, cronDirs) {
334
+ const cronName = cronNameOf(name);
335
+ const clash = listCronFiles(cronDirs).find(
336
+ (path) => path !== filePath && basename(path, extname(path)) === cronName
337
+ );
338
+ if (clash === void 0) return void 0;
339
+ return {
340
+ status: "invalid_name",
341
+ message: `Invalid schedule name "${name}": the cron name "${cronName}" is already used by ${clash}. Cron names must be unique across server/crons/ and agents/schedules/.`
342
+ };
343
+ }
344
+ function importSpecifier(fromFile, toFile) {
345
+ const path = relative2(dirname2(fromFile), toFile).split(sep2).join("/").replace(/\.tsx?$/, ".js");
346
+ return path.startsWith(".") ? path : `./${path}`;
347
+ }
348
+ function generateScheduleTemplate(name, chatSpecifier) {
349
+ const base = cronNameOf(name);
350
+ return [
351
+ `import { defineCron } from 'theokit/server/cron'`,
352
+ `import { resolveProvider } from 'theokit/server/agent'`,
353
+ `import { streamAgentTurnInProcess } from '@theokit/agents'`,
354
+ ``,
355
+ `import chat from '${chatSpecifier}'`,
356
+ ``,
357
+ `/**`,
358
+ ` * A scheduled agent run, a first-class TheoKit cron. \`theokit build\` discovers it and translates the`,
359
+ ` * schedule to your deploy target's native cron; \`theokit start\` runs it in-process.`,
360
+ ` *`,
361
+ ` * Each fire is one model run of your \`chat\` agent, so it spends tokens. Nobody is present to approve`,
362
+ ` * a gated tool, so the run uses the \`auto-reject\` posture: every approval is answered "no" and the`,
363
+ ` * gated tool does not run.`,
364
+ ` *`,
365
+ ` * Schedules are UTC (https://crontab.guru). \`signal\` aborts when the scheduler stops.`,
366
+ ` */`,
367
+ `export default defineCron('${base}', {`,
368
+ ` schedule: '0 9 * * *', // every day at 09:00 UTC`,
369
+ ` async handler({ traceId, scheduledAt, signal }) {`,
370
+ ` let outcome = 'ok'`,
371
+ ` try {`,
372
+ ` const agent = chat as { model?: string; plugins?: readonly unknown[] }`,
373
+ ` const { apiKey } = resolveProvider(agent.model, { plugins: agent.plugins })`,
374
+ ` const run = streamAgentTurnInProcess(chat, apiKey, {`,
375
+ ` message: \`Scheduled run "${base}" at \${scheduledAt.toISOString()}\`,`,
376
+ ` signal,`,
377
+ ` approvals: {`,
378
+ ` kind: 'auto-reject',`,
379
+ ` reason: 'unattended schedule "${base}": nobody is present to approve',`,
380
+ ` },`,
381
+ ` })`,
382
+ ` for await (const chunk of run) {`,
383
+ ` // The SDK can report a failed run as a chunk rather than a rejection.`,
384
+ ` if (chunk.type === 'error') {`,
385
+ ` throw new Error(\`[${base}] agent run failed: \${chunk.errorText ?? 'no error text'}\`)`,
386
+ ` }`,
387
+ ` }`,
388
+ ` } catch (err) {`,
389
+ ` outcome = \`failed: \${err instanceof Error ? err.message : String(err)}\``,
390
+ ` throw err`,
391
+ ` } finally {`,
392
+ ` // One traced line per fire, failed fires included: the scheduler's own error line has no trace id.`,
393
+ ` console.log(\`[${base}] fire \${outcome} at \${scheduledAt.toISOString()} (trace \${traceId})\`)`,
394
+ ` }`,
395
+ ` },`,
396
+ `})`,
397
+ ``
398
+ ].join("\n");
399
+ }
400
+
242
401
  // src/cli/commands/generate-types.ts
243
402
  var VALID_TYPES = [
244
403
  "route",
@@ -268,16 +427,8 @@ var RESERVED_BASENAMES = /* @__PURE__ */ new Set([
268
427
  "hasOwnProperty"
269
428
  ]);
270
429
  function hasReservedSegment(name) {
271
- const basename = name.includes("/") ? name.split("/").pop() ?? name : name;
272
- return RESERVED_BASENAMES.has(basename);
273
- }
274
- function isPathInside(cwd, targetSubpath) {
275
- if (targetSubpath.includes("\0")) return false;
276
- if (targetSubpath.startsWith("/") || targetSubpath.startsWith("\\")) return false;
277
- const resolved = resolve2(cwd, targetSubpath);
278
- const cwdResolved = resolve2(cwd);
279
- const sep = process.platform === "win32" ? "\\" : "/";
280
- return resolved === cwdResolved || resolved.startsWith(cwdResolved + sep);
430
+ const basename2 = name.includes("/") ? name.split("/").pop() ?? name : name;
431
+ return RESERVED_BASENAMES.has(basename2);
281
432
  }
282
433
  function toPascalCase(name) {
283
434
  return name.split(/[-/]/).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
@@ -439,30 +590,6 @@ function generateSandboxTemplate(name) {
439
590
  ``
440
591
  ].join("\n");
441
592
  }
442
- function generateScheduleTemplate(name) {
443
- const base = name.split("/").pop() ?? name;
444
- return [
445
- `import { defineCron } from 'theokit/server/cron'`,
446
- ``,
447
- `/**`,
448
- ` * A scheduled agent run \u2014 a first-class TheoKit cron. \`theokit build\` discovers it automatically and`,
449
- ` * translates the schedule to your deploy target's native cron (Vercel / Cloudflare / AWS). No manual`,
450
- ` * scheduler to start. The handler is where you invoke your agent \u2014 POST to \`/api/agents/chat\`, or use`,
451
- ` * \`@theokit/sdk\`'s \`Agent\` with the same model + system prompt as \`agents/chat.ts\`.`,
452
- ` *`,
453
- ` * Schedules are UTC (https://crontab.guru). \`signal\` aborts when the scheduler stops.`,
454
- ` */`,
455
- `export default defineCron('${base}', {`,
456
- ` schedule: '0 9 * * *', // every day at 09:00 UTC`,
457
- ` async handler({ traceId, scheduledAt, signal }) {`,
458
- ` void signal`,
459
- ` // Invoke your agent here \u2014 e.g. fetch your own \`/api/agents/chat\` endpoint, or call the SDK Agent.`,
460
- ` console.log(\`[${base}] fired at \${scheduledAt.toISOString()} (trace \${traceId})\`)`,
461
- ` },`,
462
- `})`,
463
- ``
464
- ].join("\n");
465
- }
466
593
  function generateMemoryTemplate(_name) {
467
594
  return [
468
595
  `/**`,
@@ -482,71 +609,32 @@ function generateMemoryTemplate(_name) {
482
609
  ``
483
610
  ].join("\n");
484
611
  }
485
- function resolveTemplate(cwd, type, name) {
486
- switch (type) {
487
- // Agent-capability generators. These live UNDER `agents/` — they are facets of the agent domain,
488
- // not standalone top-level concerns — and the folder-semantic scanner treats each as composition
489
- // (never a phantom route).
490
- case "workflow":
491
- return {
492
- filePath: resolve2(cwd, "agents/workflows", `${name}.ts`),
493
- content: generateWorkflowTemplate(name)
494
- };
495
- case "eval":
496
- return {
497
- filePath: resolve2(cwd, "agents/evals", `${name}.ts`),
498
- content: generateEvalTemplate(name)
499
- };
500
- case "sandbox":
501
- return {
502
- filePath: resolve2(cwd, "agents/sandbox", `${name}.ts`),
503
- content: generateSandboxTemplate(name)
504
- };
505
- case "schedule":
506
- return {
507
- filePath: resolve2(cwd, "agents/schedules", `${name}.ts`),
508
- content: generateScheduleTemplate(name)
509
- };
510
- case "memory":
511
- return {
512
- filePath: resolve2(cwd, "agents/memory", `${name}.ts`),
513
- content: generateMemoryTemplate(name)
514
- };
515
- case "route":
516
- return {
517
- filePath: resolve2(cwd, "server/routes", `${name}.ts`),
518
- content: generateRouteTemplate(name)
519
- };
520
- case "action":
521
- return {
522
- filePath: resolve2(cwd, "server/actions", `${name}.ts`),
523
- content: generateActionTemplate(name)
524
- };
525
- case "page":
526
- return { filePath: resolve2(cwd, `app/${name}/page.tsx`), content: generatePageTemplate(name) };
527
- case "ws":
528
- return {
529
- filePath: resolve2(cwd, "server/ws", `${name}.ts`),
530
- content: generateWsTemplate(name)
531
- };
532
- case "controller":
533
- return {
534
- filePath: resolve2(cwd, "server/controllers", `${name}.controller.ts`),
535
- content: generateControllerTemplate(name)
536
- };
537
- case "agent":
538
- return {
539
- filePath: resolve2(cwd, "server/agents", `${name}.agent.ts`),
540
- content: generateAgentTemplate(name)
541
- };
542
- case "toolbox":
543
- return {
544
- filePath: resolve2(cwd, "server/toolboxes", `${name}.tools.ts`),
545
- content: generateToolboxTemplate(name)
546
- };
547
- default:
548
- return null;
612
+ var SINGLE_FILE_KINDS = {
613
+ // Agent-capability generators. These live UNDER `agents/` — they are facets of the agent domain,
614
+ // not standalone top-level concerns — and the folder-semantic scanner treats each as composition
615
+ // (never a phantom route).
616
+ workflow: { path: (name) => `agents/workflows/${name}.ts`, template: generateWorkflowTemplate },
617
+ eval: { path: (name) => `agents/evals/${name}.ts`, template: generateEvalTemplate },
618
+ sandbox: { path: (name) => `agents/sandbox/${name}.ts`, template: generateSandboxTemplate },
619
+ memory: { path: (name) => `agents/memory/${name}.ts`, template: generateMemoryTemplate },
620
+ route: { path: (name) => `server/routes/${name}.ts`, template: generateRouteTemplate },
621
+ action: { path: (name) => `server/actions/${name}.ts`, template: generateActionTemplate },
622
+ page: { path: (name) => `app/${name}/page.tsx`, template: generatePageTemplate },
623
+ ws: { path: (name) => `server/ws/${name}.ts`, template: generateWsTemplate },
624
+ controller: {
625
+ path: (name) => `server/controllers/${name}.controller.ts`,
626
+ template: generateControllerTemplate
627
+ },
628
+ agent: { path: (name) => `server/agents/${name}.agent.ts`, template: generateAgentTemplate },
629
+ toolbox: {
630
+ path: (name) => `server/toolboxes/${name}.tools.ts`,
631
+ template: generateToolboxTemplate
549
632
  }
633
+ };
634
+ function resolveTemplate(cwd, kind, name) {
635
+ const spec = SINGLE_FILE_KINDS[kind];
636
+ if (spec === void 0) return null;
637
+ return { filePath: resolve4(cwd, spec.path(name)), content: spec.template(name) };
550
638
  }
551
639
  function generateActionTestTemplate(name) {
552
640
  const camel = toCamelCase(name);
@@ -588,15 +676,17 @@ function generateWsTemplate(_name) {
588
676
  ``
589
677
  ].join("\n");
590
678
  }
591
- async function generate(opts) {
592
- const { cwd, type, name } = opts;
593
- if (!existsSync2(resolve2(cwd, "theo.config.ts")) && !existsSync2(resolve2(cwd, "theo.config.js"))) {
679
+ function isGeneratorType(value) {
680
+ return VALID_TYPES.includes(value);
681
+ }
682
+ function refuseOptions({ cwd, type, name }) {
683
+ if (!existsSync2(resolve4(cwd, "theo.config.ts")) && !existsSync2(resolve4(cwd, "theo.config.js"))) {
594
684
  return {
595
685
  status: "not_a_project",
596
686
  message: "Not a Theo project. cwd has no theo.config.ts or theo.config.js"
597
687
  };
598
688
  }
599
- if (!VALID_TYPES.includes(type)) {
689
+ if (!isGeneratorType(type)) {
600
690
  return {
601
691
  status: "invalid_kind",
602
692
  message: `Invalid generator type "${type}". Available: ${VALID_TYPES.join(", ")}`
@@ -605,7 +695,7 @@ async function generate(opts) {
605
695
  if (!name || !toKebabCase(name)) {
606
696
  return {
607
697
  status: "invalid_name",
608
- message: `Invalid name "${name}". Use kebab-case: lowercase letters, numbers, hyphens.`
698
+ message: `Invalid name "${name}". Use kebab-case: lowercase letters, numbers, hyphens. Example: my-route`
609
699
  };
610
700
  }
611
701
  if (hasReservedSegment(name)) {
@@ -614,25 +704,21 @@ async function generate(opts) {
614
704
  message: `Reserved name "${name}" \u2014 basename collides with built-in identifier (index/constructor/__proto__/prototype/hasOwnProperty).`
615
705
  };
616
706
  }
617
- if (type === "resource") {
618
- return generateResource(cwd, name, opts.fields ?? []);
619
- }
620
- const resolved = resolveTemplate(cwd, type, name);
621
- if (resolved === null) {
622
- return { status: "invalid_kind", message: `Unknown type: ${type}` };
623
- }
624
- const { filePath, content } = resolved;
625
- const relativeFromCwd = filePath.startsWith(resolve2(cwd)) ? filePath.slice(resolve2(cwd).length + 1) : filePath;
626
- if (!isPathInside(cwd, relativeFromCwd)) {
627
- return {
628
- status: "invalid_name",
629
- message: `Path traversal denied: "${name}" would escape project root.`
630
- };
631
- }
707
+ return void 0;
708
+ }
709
+ async function resolveTarget(cwd, type, name) {
710
+ if (type === "schedule") return resolveScheduleTarget(cwd, name);
711
+ const target = resolveTemplate(cwd, type, name);
712
+ if (target === null) return { status: "invalid_kind", message: `Unknown type: ${type}` };
713
+ return target;
714
+ }
715
+ function writeTarget(cwd, type, name, { filePath, content }) {
716
+ const escaped = pathTraversalRefusal(cwd, filePath);
717
+ if (escaped !== void 0) return escaped;
632
718
  if (existsSync2(filePath)) {
633
719
  return { status: "already_exists", filePath, kind: type, name };
634
720
  }
635
- mkdirSync2(dirname2(filePath), { recursive: true });
721
+ mkdirSync2(dirname3(filePath), { recursive: true });
636
722
  writeFileSync2(filePath, content);
637
723
  if (type === "action") {
638
724
  const testPath = filePath.replace(/\.ts$/, ".test.ts");
@@ -642,31 +728,43 @@ async function generate(opts) {
642
728
  }
643
729
  return { status: "created", filePath, kind: type, name };
644
730
  }
731
+ async function generate(opts) {
732
+ const refused = refuseOptions(opts);
733
+ if (refused !== void 0) return refused;
734
+ const { cwd, name } = opts;
735
+ const kind = opts.type;
736
+ if (kind === "resource") {
737
+ return generateResource(cwd, name, opts.fields ?? []);
738
+ }
739
+ const resolved = await resolveTarget(cwd, kind, name);
740
+ if ("status" in resolved) return resolved;
741
+ return writeTarget(cwd, kind, name, resolved);
742
+ }
743
+ var CLI_REFUSALS = {
744
+ not_a_project: () => "Not a Theo project. Run this from a project root with theo.config.ts",
745
+ invalid_kind: (result) => result.message ?? "Invalid kind",
746
+ // `generate` returns invalid_name for a malformed name, a reserved name and a path that
747
+ // resolves outside the project; each message says which, so the CLI must not replace it.
748
+ invalid_name: (result, name) => result.message ?? `Invalid name "${name}"`,
749
+ invalid_config: (result) => result.message ?? "Invalid theo.config.ts",
750
+ agent_not_found: (result) => result.message ?? "No chat agent found"
751
+ };
645
752
  async function generateCommand(type, name, fields) {
646
753
  const result = await generate({ cwd: process.cwd(), type, name, fields });
647
- switch (result.status) {
648
- case "not_a_project":
649
- throw new Error("Not a Theo project. Run this from a project root with theo.config.ts");
650
- case "invalid_kind":
651
- throw new Error(result.message ?? "Invalid kind");
652
- case "invalid_name":
653
- throw new Error(
654
- `Invalid name "${name}". Use kebab-case: lowercase letters, numbers, hyphens. Example: my-route`
655
- );
656
- case "already_exists":
657
- console.log(`
754
+ if (result.status === "already_exists") {
755
+ console.log(`
658
756
  \u26A0 ${result.filePath} already exists. Skipping.
659
757
  `);
660
- return;
661
- case "created":
662
- console.log(`
758
+ } else if (result.status === "created") {
759
+ console.log(`
663
760
  \u2713 Created ${type}: ${result.filePath}
664
761
  `);
665
- return;
762
+ } else {
763
+ throw new Error(CLI_REFUSALS[result.status](result, name));
666
764
  }
667
765
  }
668
766
  export {
669
767
  generate,
670
768
  generateCommand
671
769
  };
672
- //# sourceMappingURL=generate-EN6EDITF.js.map
770
+ //# sourceMappingURL=generate-RW3NO3NR.js.map