theokit 0.74.5 → 0.75.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{actions-virtual-module-MIQJEHKJ.js → actions-virtual-module-MIQPBAZW.js} +5 -5
- package/dist/{actions-virtual-module-4J7Z37Y4.js → actions-virtual-module-QEI6QPK4.js} +5 -5
- package/dist/adapters/agent-mount.js +1 -1
- package/dist/{agent-XPZUWTHR.js → agent-N4OV5MDY.js} +3 -3
- package/dist/{app-typed-client-AZMUYIYV.js → app-typed-client-CHSITA46.js} +5 -5
- package/dist/{app-typed-client-DSS23KDC.js → app-typed-client-XQJXF646.js} +5 -5
- package/dist/{build-F3O5LJHY.js → build-GNKTCEES.js} +24 -89
- package/dist/build-GNKTCEES.js.map +1 -0
- package/dist/{chunk-44E7XIZC.js → chunk-3MKRDJQ2.js} +2 -2
- package/dist/{chunk-4ERYQBL6.js → chunk-4Z5XO4O4.js} +2 -2
- package/dist/{chunk-V7ISZGNS.js → chunk-7HGZ3CKL.js} +2 -2
- package/dist/{chunk-6R5F5YF2.js → chunk-A6UFN6QQ.js} +16 -11
- package/dist/{chunk-6R5F5YF2.js.map → chunk-A6UFN6QQ.js.map} +1 -1
- package/dist/{chunk-5KRMDP46.js → chunk-AHYOVVJ7.js} +13 -3
- package/dist/chunk-AHYOVVJ7.js.map +1 -0
- package/dist/{chunk-FPC6ZEEU.js → chunk-E5QTCOKC.js} +10 -10
- package/dist/{chunk-WR5B2XNT.js → chunk-FNAEKJHO.js} +2 -2
- package/dist/{chunk-HKF6YNY4.js → chunk-FU3LEX2I.js} +26 -12
- package/dist/chunk-FU3LEX2I.js.map +1 -0
- package/dist/{chunk-VD3BCME6.js → chunk-I5PDEWZZ.js} +2 -2
- package/dist/chunk-IWIRHUNV.js +96 -0
- package/dist/chunk-IWIRHUNV.js.map +1 -0
- package/dist/{chunk-XUAWD5HK.js → chunk-J2KYVTPD.js} +10 -10
- package/dist/{chunk-U5PWXS3J.js → chunk-O5JZ6ROY.js} +13 -3
- package/dist/chunk-O5JZ6ROY.js.map +1 -0
- package/dist/{chunk-VOXLI66L.js → chunk-OZB2TKGJ.js} +2 -2
- package/dist/{chunk-ET2TAY3Z.js → chunk-RAUYMVUO.js} +279 -15
- package/dist/chunk-RAUYMVUO.js.map +1 -0
- package/dist/{chunk-DA3GGICE.js → chunk-RQKMY3JW.js} +2 -2
- package/dist/{chunk-D5KBBTVD.js → chunk-RY6D4EXL.js} +20 -13
- package/dist/chunk-RY6D4EXL.js.map +1 -0
- package/dist/{chunk-PZLH4WGU.js → chunk-UWV3B3RY.js} +5 -5
- package/dist/{chunk-47VNWTL4.js → chunk-XXHR7X35.js} +9 -5
- package/dist/chunk-XXHR7X35.js.map +1 -0
- package/dist/cli/index.js +10 -10
- package/dist/{cloudflare-PRTTYWRD.js → cloudflare-7EZQE4UV.js} +2 -2
- package/dist/{cloudflare-PRTTYWRD.js.map → cloudflare-7EZQE4UV.js.map} +1 -1
- package/dist/{dev-TW6QD7F3.js → dev-7PMNIIOF.js} +8 -8
- package/dist/{dev-emit-T3X6RR7M.js → dev-emit-BR6ERY63.js} +11 -7
- package/dist/dev-emit-BR6ERY63.js.map +1 -0
- package/dist/{dev-emit-ZK5GCHXN.js → dev-emit-GFGLZZOH.js} +4 -4
- package/dist/{generate-EN6EDITF.js → generate-RW3NO3NR.js} +239 -141
- package/dist/generate-RW3NO3NR.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -5
- package/dist/{info-XURFOBRF.js → info-6NAUW5XY.js} +3 -3
- package/dist/{internal-api-XPT4GLYX.js → internal-api-A5HA7TYF.js} +5 -5
- package/dist/{internal-api-ATSPOXI2.js → internal-api-R7TTYMCY.js} +5 -5
- package/dist/{load-config-2DXLI7NW.js → load-config-CLFWJOXN.js} +3 -3
- package/dist/{mcp-LMV3NQEP.js → mcp-3NRZAQKY.js} +4 -4
- package/dist/{openapi-UJSUQPNT.js → openapi-ZHKJB3XL.js} +6 -6
- package/dist/{preview-AMNBV2DP.js → preview-SNS7VJWH.js} +3 -3
- package/dist/{registry-SUE4HJXL.js → registry-3SVWZVIJ.js} +2 -2
- package/dist/{routes-ENMU4GAN.js → routes-SMNSI2ZX.js} +3 -3
- package/dist/server/agent/index.d.ts +99 -6
- package/dist/server/agent/index.js +9 -1
- package/dist/server/cron/index.js +2 -2
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +15 -7
- package/dist/server/index.js.map +1 -1
- package/dist/server/jobs/index.js +2 -2
- package/dist/server/scan/index.js +4 -4
- package/dist/{server-boundary-K4YQXDQT.js → server-boundary-HYLYGFZY.js} +5 -5
- package/dist/{server-boundary-JM72DQLA.js → server-boundary-ZT6HN7ON.js} +5 -5
- package/dist/{start-GAULIE5E.js → start-L5TGWGLU.js} +7 -7
- package/dist/vite-plugin/index.js +5 -5
- package/dist/{vite-plugin-4ZMBW3KV.js → vite-plugin-UXEXT7GX.js} +8 -8
- package/package.json +5 -5
- package/dist/build-F3O5LJHY.js.map +0 -1
- package/dist/chunk-47VNWTL4.js.map +0 -1
- package/dist/chunk-5KRMDP46.js.map +0 -1
- package/dist/chunk-D5KBBTVD.js.map +0 -1
- package/dist/chunk-ET2TAY3Z.js.map +0 -1
- package/dist/chunk-HKF6YNY4.js.map +0 -1
- package/dist/chunk-U5PWXS3J.js.map +0 -1
- package/dist/dev-emit-T3X6RR7M.js.map +0 -1
- package/dist/generate-EN6EDITF.js.map +0 -1
- /package/dist/{actions-virtual-module-MIQJEHKJ.js.map → actions-virtual-module-MIQPBAZW.js.map} +0 -0
- /package/dist/{actions-virtual-module-4J7Z37Y4.js.map → actions-virtual-module-QEI6QPK4.js.map} +0 -0
- /package/dist/{agent-XPZUWTHR.js.map → agent-N4OV5MDY.js.map} +0 -0
- /package/dist/{app-typed-client-AZMUYIYV.js.map → app-typed-client-CHSITA46.js.map} +0 -0
- /package/dist/{app-typed-client-DSS23KDC.js.map → app-typed-client-XQJXF646.js.map} +0 -0
- /package/dist/{chunk-44E7XIZC.js.map → chunk-3MKRDJQ2.js.map} +0 -0
- /package/dist/{chunk-4ERYQBL6.js.map → chunk-4Z5XO4O4.js.map} +0 -0
- /package/dist/{chunk-V7ISZGNS.js.map → chunk-7HGZ3CKL.js.map} +0 -0
- /package/dist/{chunk-FPC6ZEEU.js.map → chunk-E5QTCOKC.js.map} +0 -0
- /package/dist/{chunk-WR5B2XNT.js.map → chunk-FNAEKJHO.js.map} +0 -0
- /package/dist/{chunk-VD3BCME6.js.map → chunk-I5PDEWZZ.js.map} +0 -0
- /package/dist/{chunk-XUAWD5HK.js.map → chunk-J2KYVTPD.js.map} +0 -0
- /package/dist/{chunk-VOXLI66L.js.map → chunk-OZB2TKGJ.js.map} +0 -0
- /package/dist/{chunk-DA3GGICE.js.map → chunk-RQKMY3JW.js.map} +0 -0
- /package/dist/{chunk-PZLH4WGU.js.map → chunk-UWV3B3RY.js.map} +0 -0
- /package/dist/{dev-TW6QD7F3.js.map → dev-7PMNIIOF.js.map} +0 -0
- /package/dist/{dev-emit-ZK5GCHXN.js.map → dev-emit-GFGLZZOH.js.map} +0 -0
- /package/dist/{info-XURFOBRF.js.map → info-6NAUW5XY.js.map} +0 -0
- /package/dist/{internal-api-XPT4GLYX.js.map → internal-api-A5HA7TYF.js.map} +0 -0
- /package/dist/{internal-api-ATSPOXI2.js.map → internal-api-R7TTYMCY.js.map} +0 -0
- /package/dist/{load-config-2DXLI7NW.js.map → load-config-CLFWJOXN.js.map} +0 -0
- /package/dist/{mcp-LMV3NQEP.js.map → mcp-3NRZAQKY.js.map} +0 -0
- /package/dist/{openapi-UJSUQPNT.js.map → openapi-ZHKJB3XL.js.map} +0 -0
- /package/dist/{preview-AMNBV2DP.js.map → preview-SNS7VJWH.js.map} +0 -0
- /package/dist/{registry-SUE4HJXL.js.map → registry-3SVWZVIJ.js.map} +0 -0
- /package/dist/{routes-ENMU4GAN.js.map → routes-SMNSI2ZX.js.map} +0 -0
- /package/dist/{server-boundary-K4YQXDQT.js.map → server-boundary-HYLYGFZY.js.map} +0 -0
- /package/dist/{server-boundary-JM72DQLA.js.map → server-boundary-ZT6HN7ON.js.map} +0 -0
- /package/dist/{start-GAULIE5E.js.map → start-L5TGWGLU.js.map} +0 -0
- /package/dist/{vite-plugin-4ZMBW3KV.js.map → vite-plugin-UXEXT7GX.js.map} +0 -0
|
@@ -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
|
|
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 =
|
|
209
|
-
const routeIndexPath =
|
|
210
|
-
const routeIdPath =
|
|
211
|
-
const testPath =
|
|
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
|
|
272
|
-
return RESERVED_BASENAMES.has(
|
|
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
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
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
|
-
|
|
592
|
-
|
|
593
|
-
|
|
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 (!
|
|
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
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
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(
|
|
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
|
-
|
|
648
|
-
|
|
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
|
-
|
|
661
|
-
|
|
662
|
-
console.log(`
|
|
758
|
+
} else if (result.status === "created") {
|
|
759
|
+
console.log(`
|
|
663
760
|
\u2713 Created ${type}: ${result.filePath}
|
|
664
761
|
`);
|
|
665
|
-
|
|
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-
|
|
770
|
+
//# sourceMappingURL=generate-RW3NO3NR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/generate.ts","../src/cli/commands/generate-containment.ts","../src/cli/commands/generate-policy-placeholder.ts","../src/cli/commands/generate-resource.ts","../src/cli/commands/generate-schedule.ts","../src/server/cron/define-cron.ts","../src/cli/commands/generate-types.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\n\nimport { pathTraversalRefusal } from './generate-containment.js'\nimport {\n UNDECIDED_POLICY_IDENT,\n undecidedPolicyDeclaration,\n} from './generate-policy-placeholder.js'\nimport { generateResource } from './generate-resource.js'\nimport { resolveScheduleTarget } from './generate-schedule.js'\nimport {\n VALID_TYPES,\n type GeneratorType,\n type GenerateOptions,\n type GenerateResult,\n type GenerateStatus,\n} from './generate-types.js'\n\nexport {}\n\nfunction toKebabCase(name: string): boolean {\n return /^[a-z][a-z0-9/-]*$/.test(name)\n}\n\n/**\n * Reserved JS identifier names that conflict with object prototype machinery\n * or would shadow built-in module shapes if used as action/route names.\n * Mirrors action-scan's RESERVED_NAMES (T1.4).\n */\nconst RESERVED_BASENAMES = new Set([\n 'index',\n 'constructor',\n '__proto__',\n 'prototype',\n 'hasOwnProperty',\n])\n\n/**\n * Validate that a name segment doesn't hit the reserved-identifier list.\n * Checks the BASENAME (last `/` segment) — nested `admin/constructor` still\n * rejected because the file `constructor.ts` would be the conflict.\n */\nfunction hasReservedSegment(name: string): boolean {\n const basename = name.includes('/') ? (name.split('/').pop() ?? name) : name\n return RESERVED_BASENAMES.has(basename)\n}\n\nfunction toPascalCase(name: string): string {\n return name\n .split(/[-/]/)\n .map((s) => s.charAt(0).toUpperCase() + s.slice(1))\n .join('')\n}\n\nfunction toCamelCase(name: string): string {\n const pascal = toPascalCase(name)\n return pascal.charAt(0).toLowerCase() + pascal.slice(1)\n}\n\nfunction generateRouteTemplate(name: string): string {\n return [\n `import { route, type AccessDecision } from 'theokit/server'`,\n `import { z } from 'zod'`,\n ``,\n ...undecidedPolicyDeclaration(\n `server/routes/${name}.ts`,\n 'a stub today is a real handler tomorrow',\n ),\n ``,\n `export const GET = route()`,\n ` .policy(${UNDECIDED_POLICY_IDENT})`,\n ` .handler(({ ctx }) => {`,\n ` return { message: 'TODO: implement ${name} GET' }`,\n ` })`,\n ` .build()`,\n ``,\n ].join('\\n')\n}\n\nfunction generateControllerTemplate(name: string): string {\n const className = toPascalCase(name) + 'Controller'\n const baseName = name.split('/').pop() ?? name\n return [\n `// AUTO-GENERATED by \\`theokit generate controller ${name}\\``,\n `import { Controller, Get } from '@theokit/http'`,\n ``,\n `@Controller('${baseName}')`,\n `export class ${className} {`,\n ` @Get()`,\n ` findAll(): string {`,\n ` return 'This action returns all ${baseName}'`,\n ` }`,\n `}`,\n ``,\n ].join('\\n')\n}\n\nfunction generateActionTemplate(name: string): string {\n const camel = toCamelCase(name)\n return [\n `import { action } from 'theokit/server'`,\n `import { z } from 'zod'`,\n ``,\n `export const ${camel} = action()`,\n ` .input(z.object({}))`,\n ` .handler(({ input, ctx }) => {`,\n ` return { message: 'TODO: implement ${name}' }`,\n ` })`,\n ` .build()`,\n ``,\n ].join('\\n')\n}\n\n/**\n * Co-located test template for `theokit generate action <name>` (T5.2 +\n * plan Q4 scenario 1: roundtrip serialize). Skeleton uses vitest BDD shape\n * per testing.md.\n */\nfunction generateAgentTemplate(name: string): string {\n return [\n `import { AgentBuilder } from '@theokit/agents'`,\n `import { z } from 'zod'`,\n ``,\n `// Zero-config: this file is auto-served at POST /api/agents/${name}.`,\n `// Add tools with tool('name')...build() and chain them via .tool(...).`,\n `export default AgentBuilder.create()`,\n ` .input(z.object({ message: z.string() }))`,\n ` .model('openai/gpt-4o-mini')`,\n ` .system('You are a helpful ${name} assistant.')`,\n ` .build()`,\n ``,\n ].join('\\n')\n}\n\nfunction generateToolboxTemplate(name: string): string {\n const camel = toCamelCase(name)\n return [\n `import { tool } from 'theokit/server'`,\n `import { z } from 'zod'`,\n ``,\n `export const ${camel}Hello = tool('${name}_hello')`,\n ` .describe('Say hello')`,\n ` .input(z.object({ name: z.string() }))`,\n ` .execute(({ name }) => \\`Hello, \\${name}!\\`)`,\n ` .build()`,\n ``,\n ].join('\\n')\n}\n\nfunction generateWorkflowTemplate(name: string): string {\n const camel = toCamelCase(name)\n return [\n `import { Workflow, fn } from '@theokit/sdk/workflow'`,\n ``,\n `/**`,\n ` * A minimal workflow — a typed, multi-step pipeline (@theokit/sdk). Each \\`fn(...)\\` step is a pure`,\n ` * function; a step can also run an agent via \\`agentStep(name, agent, mapInput)\\`. Import + run it from`,\n ` * a route, action, cron job, or tool:`,\n ` *`,\n ` * const run = await ${camel}Workflow.run({ name: 'Ada' })`,\n ` * console.log(run.status, run.output) // 'completed' 'Hello, Ada!'`,\n ` */`,\n `export const ${camel}Workflow = Workflow.create({ name: '${name}' })`,\n ` .then(fn('normalize', (input: { name: string }) => ({ name: input.name.trim() })))`,\n ` .then(fn('greet', (step: { name: string }) => \\`Hello, \\${step.name}!\\`))`,\n ` .commit()`,\n ``,\n ].join('\\n')\n}\n\nfunction generateEvalTemplate(name: string): string {\n const camel = toCamelCase(name)\n return [\n `import { Eval, Scorers } from '@theokit/sdk/eval'`,\n ``,\n `/**`,\n ` * An eval for your agent (@theokit/sdk) — runs the model over a dataset and scores each output.`,\n ` * To score the REAL agent, mirror its config: point \\`agent.model\\` (and, ideally, a \\`systemPrompt\\`)`,\n ` * at the same values your \\`agents/chat.ts\\` uses. Run it (needs a provider key in the environment):`,\n ` *`,\n ` * const run = await ${camel}Eval.run()`,\n ` * console.log(run.aggregate.meanScore)`,\n ` */`,\n `export const ${camel}Eval = Eval.create({`,\n ` name: '${name}',`,\n ` dataset: [{ input: 'Say ok', expected: 'ok' }],`,\n ` scorers: [Scorers.containsExpected({ caseSensitive: false })],`,\n ` // Mirror your agent: same model (+ systemPrompt) so this evaluates the agent your users talk to.`,\n ` agent: { apiKey: process.env.OPENROUTER_API_KEY ?? '', model: { id: 'openai/gpt-4o-mini' } },`,\n `})`,\n ``,\n ].join('\\n')\n}\n\nfunction generateSandboxTemplate(name: string): string {\n const camel = toCamelCase(name)\n const base = name.split('/').pop() ?? name\n const snake = base.replace(/-/g, '_')\n return [\n `import { tool } from 'theokit/server'`,\n `import { LocalSandbox } from '@theokit/sdk/sandbox'`,\n `import { z } from 'zod'`,\n ``,\n `/**`,\n ` * A sandbox-backed agent tool. \\`LocalSandbox\\` (@theokit/sdk) runs a shell command with a timeout +`,\n ` * output cap, isolated from your app process — so the agent can run commands SAFELY. This IS an agent`,\n ` * capability: connect it to your agent with \\`.tool(...)\\`:`,\n ` *`,\n ` * // agents/chat.ts`,\n ` * import { ${camel}Tool } from './sandbox/${base}.js'`,\n ` * AgentBuilder.create().model(...).tool(${camel}Tool).build()`,\n ` */`,\n `export const ${camel}Tool = tool('${snake}')`,\n ` .describe('Run a shell command in an isolated sandbox and return its output.')`,\n ` .input(z.object({ command: z.string().describe('The shell command to run, e.g. \"ls -la\"') }))`,\n ` .execute(async ({ command }) => {`,\n ` const sandbox = new LocalSandbox({ timeoutMs: 5_000 })`,\n ` const { stdout, stderr, exitCode } = await sandbox.execute(command)`,\n ` return exitCode === 0 ? stdout : \\`exit \\${exitCode}: \\${stderr}\\``,\n ` })`,\n ` .build()`,\n ``,\n ].join('\\n')\n}\n\nfunction generateMemoryTemplate(_name: string): string {\n return [\n `/**`,\n ` * Conversation memory — nothing to wire.`,\n ` *`,\n ` * Since @theokit/sdk 4.0, every agent session persists AUTOMATICALLY to a native Claude-shaped`,\n ` * transcript. TheoKit roots it at your app under \\`.data/agent-sessions/projects/<cwd>/<agentId>.jsonl\\`,`,\n ` * keyed by the conversation's session id — a same-session follow-up request resumes prior turns with`,\n ` * zero setup. There is no storage adapter to construct or pass to the agent anymore.`,\n ` *`,\n ` * Keep these transcripts out of git — add \\`.data/\\` to your \\`.gitignore\\`.`,\n ` *`,\n ` * Advanced: to change WHERE sessions are written (e.g. \\`~/.claude\\` so the Claude Code CLI can`,\n ` * \\`--continue\\` them), set the SDK's \\`local.baseDir\\` where you create the agent. Most apps never need to.`,\n ` */`,\n `export {}`,\n ``,\n ].join('\\n')\n}\n\n/** Where a single-file kind is written, relative to the project root, and what it says. */\ninterface SingleFileKind {\n path: (name: string) => string\n template: (name: string) => string\n}\n\n/**\n * The single-file kinds. `resource` writes several files and `schedule` reads its directory from\n * the project config, so neither is here.\n */\nconst SINGLE_FILE_KINDS: Partial<Record<GeneratorType, SingleFileKind>> = {\n // Agent-capability generators. These live UNDER `agents/` — they are facets of the agent domain,\n // not standalone top-level concerns — and the folder-semantic scanner treats each as composition\n // (never a phantom route).\n workflow: { path: (name) => `agents/workflows/${name}.ts`, template: generateWorkflowTemplate },\n eval: { path: (name) => `agents/evals/${name}.ts`, template: generateEvalTemplate },\n sandbox: { path: (name) => `agents/sandbox/${name}.ts`, template: generateSandboxTemplate },\n memory: { path: (name) => `agents/memory/${name}.ts`, template: generateMemoryTemplate },\n route: { path: (name) => `server/routes/${name}.ts`, template: generateRouteTemplate },\n action: { path: (name) => `server/actions/${name}.ts`, template: generateActionTemplate },\n page: { path: (name) => `app/${name}/page.tsx`, template: generatePageTemplate },\n ws: { path: (name) => `server/ws/${name}.ts`, template: generateWsTemplate },\n controller: {\n path: (name) => `server/controllers/${name}.controller.ts`,\n template: generateControllerTemplate,\n },\n agent: { path: (name) => `server/agents/${name}.agent.ts`, template: generateAgentTemplate },\n toolbox: {\n path: (name) => `server/toolboxes/${name}.tools.ts`,\n template: generateToolboxTemplate,\n },\n}\n\nfunction resolveTemplate(\n cwd: string,\n kind: GeneratorType,\n name: string,\n): { filePath: string; content: string } | null {\n const spec = SINGLE_FILE_KINDS[kind]\n if (spec === undefined) return null\n return { filePath: resolve(cwd, spec.path(name)), content: spec.template(name) }\n}\n\nfunction generateActionTestTemplate(name: string): string {\n const camel = toCamelCase(name)\n return [\n `import { describe, it, expect } from 'vitest'`,\n ``,\n `import { ${camel} } from './${name.split('/').pop()}.js'`,\n ``,\n `describe('${name} action', () => {`,\n ` it('should accept a valid input shape', () => {`,\n ` expect(${camel}.input).toBeDefined()`,\n ` expect(typeof ${camel}.handler).toBe('function')`,\n ` })`,\n ``,\n ` it('should reject invalid input via zod schema', () => {`,\n ` const parsed = ${camel}.input.safeParse({ __invalid: true })`,\n ` // Empty object schema accepts {}; tighten this assertion when adding fields.`,\n ` expect(parsed.success).toBe(true)`,\n ` })`,\n `})`,\n ``,\n ].join('\\n')\n}\n\nfunction generatePageTemplate(name: string): string {\n const pascal = toPascalCase(name)\n return [`export default function ${pascal}Page() {`, ` return <h1>${pascal}</h1>`, `}`, ``].join(\n '\\n',\n )\n}\n\nfunction generateWsTemplate(_name: string): string {\n return [\n `import { defineWebSocket } from 'theokit/server'`,\n ``,\n `export default defineWebSocket({`,\n ` onMessage(ws, data) {`,\n ` ws.send(\\`echo: \\${data}\\`)`,\n ` },`,\n `})`,\n ``,\n ].join('\\n')\n}\n\n/** A file to write: where it goes and what it says. */\ninterface Target {\n filePath: string\n content: string\n}\n\n/** Whether `value` names a generator kind. */\nfunction isGeneratorType(value: string): value is GeneratorType {\n return (VALID_TYPES as readonly string[]).includes(value)\n}\n\n/**\n * The refusal for options `generate` cannot act on: not a project, an unknown kind, a name that is\n * not kebab-case, or a reserved basename. `undefined` when the options are acceptable.\n */\nfunction refuseOptions({ cwd, type, name }: GenerateOptions): GenerateResult | undefined {\n if (!existsSync(resolve(cwd, 'theo.config.ts')) && !existsSync(resolve(cwd, 'theo.config.js'))) {\n return {\n status: 'not_a_project',\n message: 'Not a Theo project. cwd has no theo.config.ts or theo.config.js',\n }\n }\n\n if (!isGeneratorType(type)) {\n return {\n status: 'invalid_kind',\n message: `Invalid generator type \"${type}\". Available: ${VALID_TYPES.join(', ')}`,\n }\n }\n\n if (!name || !toKebabCase(name)) {\n return {\n status: 'invalid_name',\n message: `Invalid name \"${name}\". Use kebab-case: lowercase letters, numbers, hyphens. Example: my-route`,\n }\n }\n\n // EC-2-related: reject reserved JS identifier basenames (would shadow\n // built-in prototype machinery in virtual module emit or scan).\n if (hasReservedSegment(name)) {\n return {\n status: 'invalid_name',\n message: `Reserved name \"${name}\" — basename collides with built-in identifier (index/constructor/__proto__/prototype/hasOwnProperty).`,\n }\n }\n return undefined\n}\n\n/** Where a single-file kind goes and what it says, or the refusal that explains why it cannot. */\nasync function resolveTarget(\n cwd: string,\n type: GeneratorType,\n name: string,\n): Promise<Target | GenerateResult> {\n // A schedule's directory comes from the project config, so resolving it is async and may refuse.\n if (type === 'schedule') return resolveScheduleTarget(cwd, name)\n const target = resolveTemplate(cwd, type, name)\n if (target === null) return { status: 'invalid_kind', message: `Unknown type: ${type}` }\n return target\n}\n\n/** Write `target` unless it escapes the project or already exists. */\nfunction writeTarget(\n cwd: string,\n type: GeneratorType,\n name: string,\n { filePath, content }: Target,\n): GenerateResult {\n // EC-4: confirm the resolved filePath stays inside cwd. `toKebabCase` rejects most traversal\n // vectors in the name, but a directory read from config (`agentsDir`) can still point outside.\n const escaped = pathTraversalRefusal(cwd, filePath)\n if (escaped !== undefined) return escaped\n\n if (existsSync(filePath)) {\n return { status: 'already_exists', filePath, kind: type, name }\n }\n\n mkdirSync(dirname(filePath), { recursive: true })\n writeFileSync(filePath, content)\n\n // T5.2 + plan Q4: emit co-located test file alongside actions (only\n // for action type; routes/pages/ws keep prior single-file behavior).\n // Skip if existing test file present (preserve user-customized tests).\n if (type === 'action') {\n const testPath = filePath.replace(/\\.ts$/, '.test.ts')\n if (!existsSync(testPath)) {\n writeFileSync(testPath, generateActionTestTemplate(name))\n }\n }\n\n return { status: 'created', filePath, kind: type, name }\n}\n\n/**\n * Programmatic generate. Returns a structured result instead of throwing —\n * Studio (`theokit_generate` tool) consumes this directly. The CLI wrapper\n * below maps the structured result to console output + exit code semantics.\n */\nexport async function generate(opts: GenerateOptions): Promise<GenerateResult> {\n const refused = refuseOptions(opts)\n if (refused !== undefined) return refused\n const { cwd, name } = opts\n // refuseOptions refused every value that is not a generator kind.\n const kind = opts.type as GeneratorType\n\n // Resource generates multiple files — handle separately\n if (kind === 'resource') {\n return generateResource(cwd, name, opts.fields ?? [])\n }\n\n const resolved = await resolveTarget(cwd, kind, name)\n if ('status' in resolved) return resolved\n return writeTarget(cwd, kind, name, resolved)\n}\n\n/** The statuses the CLI reports by throwing, and the message each throws. */\nconst CLI_REFUSALS: Record<\n Exclude<GenerateStatus, 'created' | 'already_exists'>,\n (result: GenerateResult, name: string) => string\n> = {\n not_a_project: () => 'Not a Theo project. Run this from a project root with theo.config.ts',\n invalid_kind: (result) => result.message ?? 'Invalid kind',\n // `generate` returns invalid_name for a malformed name, a reserved name and a path that\n // resolves outside the project; each message says which, so the CLI must not replace it.\n invalid_name: (result, name) => result.message ?? `Invalid name \"${name}\"`,\n invalid_config: (result) => result.message ?? 'Invalid theo.config.ts',\n agent_not_found: (result) => result.message ?? 'No chat agent found',\n}\n\n/**\n * CLI entry point — preserves the original surface (throws + console.log).\n * Wraps the programmatic `generate` function.\n */\nexport async function generateCommand(\n type: string,\n name: string,\n fields?: string[],\n): Promise<void> {\n const result = await generate({ cwd: process.cwd(), type, name, fields })\n if (result.status === 'already_exists') {\n console.log(`\\n ⚠ ${result.filePath} already exists. Skipping.\\n`)\n } else if (result.status === 'created') {\n console.log(`\\n ✓ Created ${type}: ${result.filePath}\\n`)\n } else {\n throw new Error(CLI_REFUSALS[result.status](result, name))\n }\n}\n","/**\n * The one rule every generator applies before writing: the file stays inside the project. Shared\n * by `generate` and the schedule generator, which checks it before looking for the chat agent so an\n * `agentsDir` outside the project is refused as traversal, never reported as a missing agent there.\n */\nimport { isAbsolute, relative, resolve, sep } from 'node:path'\n\nimport type { GenerateResult } from './generate-types.js'\n\n/**\n * Whether `filePath` lies strictly inside `cwd`. Decided by `path.relative`, never by a string\n * prefix: `/tmp/x/app-outside/...` starts with `/tmp/x/app` and is still outside it. A null byte,\n * an absolute path and a `..` escape are all outside.\n */\nfunction isPathInside(cwd: string, filePath: string): boolean {\n if (filePath.includes('\\x00')) return false\n const fromRoot = relative(resolve(cwd), filePath)\n return fromRoot !== '' && !isAbsolute(fromRoot) && fromRoot.split(sep)[0] !== '..'\n}\n\n/** The refusal for a `filePath` outside the project at `cwd`, or `undefined` when it is inside. */\nexport function pathTraversalRefusal(cwd: string, filePath: string): GenerateResult | undefined {\n if (isPathInside(cwd, filePath)) return undefined\n return {\n status: 'invalid_name',\n message: `Path traversal denied: \"${filePath}\" is outside the project root ${resolve(cwd)}.`,\n }\n}\n","/**\n * The access policy a generated route carries until its author decides one.\n *\n * ADR 0001 made an undeclared `policy` a build error because \"a route that nobody had thought\n * about was indistinguishable from a route deliberately left open\". The generators then wrote\n * `'public'` — including on five methods that read and write a database table — so a generated\n * file was the not-thought-about case wearing the deliberate value, which is the precise state\n * the gate exists to make impossible (usetheokit/theokit#416). `theo build` accepted it, because\n * `assertEveryMethodDeclaresPolicy` asks whether a policy was declared, not which one.\n *\n * A generated route now REFUSES. The author still has to write the answer down — the gate's whole\n * purpose — but the value standing in until they do is the safe one rather than the open one.\n *\n * ## Why an AccessDecision and not `() => false`\n *\n * `evaluateRoutePolicy` maps a bare `false` to \"access denied by route policy\", which tells the\n * reader nothing about where to go. The contract already says a denial \"carries its reason so the\n * caller can say it\", so the refusal names its own file: the first `curl` against a forgotten\n * route prints the path to edit.\n *\n * ## Why a named const rather than a lambda per method\n *\n * Five methods would mean five copies, and deleting four of them is the kind of chore that leaves\n * the fifth behind. One const is one thing to replace, and `grep -rn undecidedPolicy` counts the\n * routes still awaiting a decision — which is the number ADR 0001 wanted to make greppable, and\n * which `grep -c \"policy('public')\"` could not give once generated placeholders were mixed in with\n * real decisions.\n */\n\n/** The identifier the generated methods reference. Exported so callers cannot misspell it. */\nexport const UNDECIDED_POLICY_IDENT = 'undecidedPolicy'\n\n/**\n * Lines declaring the placeholder, to splice into a generated module.\n *\n * @param routePath - the generated file's own path, repo-relative, so the refusal can name it\n * @param what - what the route does, in one clause, for the TODO comment\n */\nexport function undecidedPolicyDeclaration(routePath: string, what: string): string[] {\n return [\n `// TODO(policy): every route declares who may call it (ADR 0001). Generated as a REFUSAL`,\n `// rather than 'public', because the generator cannot know your access model —`,\n `// ${what}.`,\n `// Replace this with 'public' if the route is genuinely open, or with a check such as`,\n `// \\`({ subject, params }) => requireOwner(subject, ownerOf(params.id))\\`, then delete the const.`,\n `const ${UNDECIDED_POLICY_IDENT} = (): AccessDecision => ({`,\n ` allowed: false,`,\n ` reason:`,\n ` '${routePath} still carries the policy the generator wrote, which refuses everyone. ' +`,\n ` 'Declare who may call this route (ADR 0001).',`,\n `})`,\n ]\n}\n","import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\n\nimport {\n UNDECIDED_POLICY_IDENT,\n undecidedPolicyDeclaration,\n} from './generate-policy-placeholder.js'\nimport type { GenerateResult } from './generate-types.js'\n\n// --- Field parsing ---\n\nconst ALLOWED_FIELD_TYPES = new Set(['string', 'text', 'number', 'boolean'])\nconst RESERVED_FIELDS = new Set(['id', 'createdAt', 'created_at'])\n\ninterface ResourceField {\n name: string\n type: 'string' | 'text' | 'number' | 'boolean'\n drizzleColumn: string\n zodType: string\n}\n\nfunction parseResourceFields(args: string[]): ResourceField[] {\n if (args.length === 0) {\n throw new Error(\n 'Resource requires at least one field. Example: theokit generate resource posts title:string',\n )\n }\n return args.map((arg) => {\n const parts = arg.split(':')\n if (parts.length !== 2 || !parts[0] || !parts[1]) {\n throw new Error(`Invalid field \"${arg}\". Use name:type format (e.g. title:string)`)\n }\n const [name, type] = parts\n if (!ALLOWED_FIELD_TYPES.has(type)) {\n throw new Error(\n `Unknown field type \"${type}\" in \"${arg}\". Valid types: ${[...ALLOWED_FIELD_TYPES].join(', ')}`,\n )\n }\n if (RESERVED_FIELDS.has(name)) {\n throw new Error(`Field \"${name}\" is reserved (auto-generated by the framework)`)\n }\n const fieldType = type as ResourceField['type']\n return {\n name,\n type: fieldType,\n drizzleColumn: mapDrizzleColumn(name, fieldType),\n zodType: mapZodType(fieldType),\n }\n })\n}\n\n// --- Column / type mapping ---\n\nfunction mapDrizzleColumn(name: string, type: ResourceField['type']): string {\n switch (type) {\n case 'string':\n case 'text':\n return `text('${name}').notNull()`\n case 'number':\n return `integer('${name}').notNull()`\n case 'boolean':\n return `integer('${name}', { mode: 'boolean' }).notNull().default(false)`\n }\n}\n\nfunction mapZodType(type: ResourceField['type']): string {\n switch (type) {\n case 'string':\n case 'text':\n return 'z.string()'\n case 'number':\n return 'z.number()'\n case 'boolean':\n return 'z.boolean()'\n }\n}\n\n// --- Template generators ---\n\nfunction generateSchemaEntry(resourceName: string, fields: ResourceField[]): string {\n const cols = [\n ` id: integer('id').primaryKey({ autoIncrement: true }),`,\n ...fields.map((f) => ` ${f.name}: ${f.drizzleColumn},`),\n ` createdAt: text('created_at')`,\n ` .notNull()`,\n ` .$defaultFn(() => new Date().toISOString().split('T')[0]),`,\n ]\n return [\n ``,\n `export const ${resourceName} = sqliteTable('${resourceName}', {`,\n ...cols,\n `})`,\n ``,\n ].join('\\n')\n}\n\nfunction generateRouteIndex(resourceName: string, fields: ResourceField[]): string {\n const bodyFields = fields.map((f) => ` ${f.name}: ${f.zodType},`).join('\\n')\n return [\n `import { route, type AccessDecision } from 'theokit/server/define'`,\n `import { z } from 'zod'`,\n `import { db } from '../../db/index.js'`,\n `import { ${resourceName} } from '../../db/schema.js'`,\n ``,\n ...undecidedPolicyDeclaration(\n `server/routes/${resourceName}/index.ts`,\n 'these methods read and write a table',\n ),\n ``,\n `export const GET = route()`,\n ` .policy(${UNDECIDED_POLICY_IDENT})`,\n ` .handler(() => db.select().from(${resourceName}).all())`,\n ` .build()`,\n ``,\n `export const POST = route()`,\n ` .policy(${UNDECIDED_POLICY_IDENT})`,\n ` .body(`,\n ` z.object({`,\n bodyFields,\n ` }),`,\n ` )`,\n ` .status(201)`,\n ` .handler(({ body }) => {`,\n ` const result = db.insert(${resourceName}).values(body).returning().get()`,\n ` return result`,\n ` })`,\n ` .build()`,\n ``,\n ].join('\\n')\n}\n\nfunction generateRouteId(resourceName: string, fields: ResourceField[]): string {\n const updateFields = fields.map((f) => ` ${f.name}: ${f.zodType}.optional(),`).join('\\n')\n return [\n `import { route, type AccessDecision } from 'theokit/server/define'`,\n `import { z } from 'zod'`,\n `import { db } from '../../db/index.js'`,\n `import { ${resourceName} } from '../../db/schema.js'`,\n `import { eq } from 'drizzle-orm'`,\n ``,\n ...undecidedPolicyDeclaration(\n `server/routes/${resourceName}/[id].ts`,\n 'these methods read, update and delete rows of a table',\n ),\n ``,\n `export const GET = route()`,\n ` .policy(${UNDECIDED_POLICY_IDENT})`,\n ` .params(z.object({ id: z.coerce.number() }))`,\n ` .handler(({ params }) => {`,\n ` const item = db.select().from(${resourceName}).where(eq(${resourceName}.id, params.id)).get()`,\n ` if (!item) return new Response(JSON.stringify({ error: 'Not found' }), { status: 404 })`,\n ` return item`,\n ` })`,\n ` .build()`,\n ``,\n `export const PUT = route()`,\n ` .policy(${UNDECIDED_POLICY_IDENT})`,\n ` .params(z.object({ id: z.coerce.number() }))`,\n ` .body(`,\n ` z.object({`,\n updateFields,\n ` }),`,\n ` )`,\n ` .handler(({ params, body }) => {`,\n ` const result = db.update(${resourceName}).set(body).where(eq(${resourceName}.id, params.id)).returning().get()`,\n ` if (!result) return new Response(JSON.stringify({ error: 'Not found' }), { status: 404 })`,\n ` return result`,\n ` })`,\n ` .build()`,\n ``,\n `export const DELETE = route()`,\n ` .policy(${UNDECIDED_POLICY_IDENT})`,\n ` .params(z.object({ id: z.coerce.number() }))`,\n ` .status(204)`,\n ` .handler(({ params }) => {`,\n ` db.delete(${resourceName}).where(eq(${resourceName}.id, params.id)).run()`,\n ` })`,\n ` .build()`,\n ``,\n ].join('\\n')\n}\n\nfunction generateTestTemplate(resourceName: string): string {\n return [\n `import { describe, it, expect } from 'vitest'`,\n ``,\n `describe('${resourceName} API', () => {`,\n ` it('should have a schema defined', async () => {`,\n ` const schema = await import('../server/db/schema.js')`,\n ` expect(schema.${resourceName}).toBeDefined()`,\n ` })`,\n ``,\n ` it('should have route handlers', async () => {`,\n ` const index = await import('../server/routes/${resourceName}/index.js')`,\n ` expect(index.GET).toBeDefined()`,\n ` expect(index.POST).toBeDefined()`,\n ` })`,\n `})`,\n ``,\n ].join('\\n')\n}\n\n// --- Orchestrator ---\n\nexport function generateResource(cwd: string, name: string, fieldArgs: string[]): GenerateResult {\n let fields: ResourceField[]\n try {\n fields = parseResourceFields(fieldArgs)\n } catch (err) {\n return { status: 'invalid_name', message: err instanceof Error ? err.message : String(err) }\n }\n\n const schemaPath = resolve(cwd, 'server/db/schema.ts')\n const routeIndexPath = resolve(cwd, `server/routes/${name}/index.ts`)\n const routeIdPath = resolve(cwd, `server/routes/${name}/[id].ts`)\n const testPath = resolve(cwd, `tests/${name}.test.ts`)\n\n if (!existsSync(schemaPath)) {\n return {\n status: 'not_a_project',\n message:\n 'server/db/schema.ts not found. Run this from a TheoKit project with database setup.',\n }\n }\n\n if (existsSync(routeIndexPath)) {\n return { status: 'already_exists', filePath: routeIndexPath, kind: 'resource', name }\n }\n\n const existingSchema = readFileSync(schemaPath, 'utf-8')\n if (existingSchema.includes(`export const ${name} =`)) {\n return {\n status: 'already_exists',\n filePath: schemaPath,\n kind: 'resource',\n name,\n message: `Table \"${name}\" already exists in schema.ts`,\n }\n }\n\n // 1. Append schema entry\n appendFileSync(schemaPath, generateSchemaEntry(name, fields))\n\n // 2. Create route files\n mkdirSync(dirname(routeIndexPath), { recursive: true })\n writeFileSync(routeIndexPath, generateRouteIndex(name, fields))\n writeFileSync(routeIdPath, generateRouteId(name, fields))\n\n // 3. Create test file\n mkdirSync(dirname(testPath), { recursive: true })\n if (!existsSync(testPath)) {\n writeFileSync(testPath, generateTestTemplate(name))\n }\n\n return { status: 'created', filePath: routeIndexPath, kind: 'resource', name }\n}\n","/**\n * The `theokit generate schedule` generator.\n *\n * A schedule must land where `theokit build` scans (`<agentsDir>/schedules`), so its directory is\n * read from the project config through `loadConfig`, the same reader the build uses. When the\n * config cannot be read the generator refuses by name instead of guessing a directory.\n *\n * A schedule runs the app's `chat` agent, so a project without one is refused too: a schedule\n * importing a module that does not exist would make `theokit build` fail on it.\n *\n * The schedule declares `defineCron('<last segment of the name>')`, so that segment is checked with\n * `defineCron`'s own rule and the scanner's own discovery: a name the build would reject, skip, or\n * find already used by another cron file is refused as `invalid_name` before anything is written.\n */\nimport { basename, dirname, extname, relative, resolve, sep } from 'node:path'\n\nimport { loadConfig } from '../../config/load-config.js'\nimport { cronScanDirs, isDiscoverableCronFile, listCronFiles } from '../../server/cron/cron-scan.js'\nimport { CRON_NAME_RULE, isValidCronName } from '../../server/cron/define-cron.js'\nimport { findAgentFile } from '../../server/scan/agent-scan.js'\n\nimport { pathTraversalRefusal } from './generate-containment.js'\nimport type { GenerateResult } from './generate-types.js'\n\n/** Where the schedule goes and what it says, or the refusal that explains why it cannot be written. */\nexport async function resolveScheduleTarget(\n cwd: string,\n name: string,\n): Promise<{ filePath: string; content: string } | GenerateResult> {\n const malformed = cronNameRefusal(name)\n if (malformed !== undefined) return malformed\n let config: Awaited<ReturnType<typeof loadConfig>>\n try {\n config = await loadConfig(cwd)\n } catch (err) {\n return {\n status: 'invalid_config',\n message: `Cannot read ${resolve(cwd, 'theo.config.ts')}: ${(err as Error).message}`,\n }\n }\n const { agentsDir, serverDir } = config\n const agentsRoot = resolve(cwd, agentsDir)\n const cronDirs = cronScanDirs(cwd, serverDir, agentsDir)\n const filePath = resolve(cronDirs.schedules, `${name}.ts`)\n // Before the agent lookup: a chat agent missing from a directory outside the project is not\n // something to create, and the refusal must name the escape, not the missing file.\n const escaped = pathTraversalRefusal(cwd, filePath)\n if (escaped !== undefined) return escaped\n // The agent scanner's own rule, so the file found is the one the build serves as `chat`.\n const chatPath = findAgentFile(agentsRoot, 'chat')\n if (chatPath === undefined) {\n return {\n status: 'agent_not_found',\n message:\n `No \"chat\" agent in ${agentsRoot} (the build serves it from chat.{ts,tsx,js,jsx} or ` +\n 'chat/index.{ts,tsx,js,jsx}). A schedule runs the chat agent; create it first.',\n }\n }\n // The build scans both homes with one duplicate-name guard (build.ts, emitCronArtifacts).\n const taken = cronNameTaken(name, filePath, [cronDirs.crons, cronDirs.schedules])\n if (taken !== undefined) return taken\n return { filePath, content: generateScheduleTemplate(name, importSpecifier(filePath, chatPath)) }\n}\n\n/** The cron name a schedule called `name` declares: its last `/` segment. */\nexport function cronNameOf(name: string): string {\n return name.split('/').pop() ?? name\n}\n\n/** A refusal when the build would skip the file or `defineCron` would reject its name. */\nfunction cronNameRefusal(name: string): GenerateResult | undefined {\n const cronName = cronNameOf(name)\n if (!isDiscoverableCronFile(`${cronName}.ts`)) {\n return {\n status: 'invalid_name',\n message: `Invalid schedule name \"${name}\": the build does not discover a cron file named \"${cronName}.ts\".`,\n }\n }\n if (!isValidCronName(cronName)) {\n return {\n status: 'invalid_name',\n message: `Invalid schedule name \"${name}\": its cron name \"${cronName}\" is one defineCron rejects. ${CRON_NAME_RULE}`,\n }\n }\n return undefined\n}\n\n/**\n * A refusal when another cron file already carries this schedule's cron name. The generator names\n * a cron after its file, so a file of the same name in either cron home is the clash the build's\n * duplicate-name guard would fail on. The target itself is left to the `already_exists` answer.\n *\n * Compared by FILE name, while the build compares the name each file passes to `defineCron`, which\n * it learns only by importing the file. A hand-written `foo.ts` declaring `defineCron('bar')` is\n * not seen here, so schedule `bar` is written and the build then fails with\n * `DuplicateCronNameError`; and a `bar.ts` declaring another name refuses schedule `bar` although\n * the build would accept it. Importing every cron file to read its name would run user code at\n * generate time, which this generator does not do.\n */\nfunction cronNameTaken(\n name: string,\n filePath: string,\n cronDirs: readonly string[],\n): GenerateResult | undefined {\n const cronName = cronNameOf(name)\n const clash = listCronFiles(cronDirs).find(\n (path) => path !== filePath && basename(path, extname(path)) === cronName,\n )\n if (clash === undefined) return undefined\n return {\n status: 'invalid_name',\n 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/.`,\n }\n}\n\n/**\n * The ESM specifier the schedule at `fromFile` uses to import `toFile` (`../chat.js`). A TypeScript\n * source is imported by its `.js` name, as TypeScript and tsx resolve it; a `.js` or `.jsx` file\n * keeps its own extension.\n */\nexport function importSpecifier(fromFile: string, toFile: string): string {\n const path = relative(dirname(fromFile), toFile)\n .split(sep)\n .join('/')\n .replace(/\\.tsx?$/, '.js')\n return path.startsWith('.') ? path : `./${path}`\n}\n\nfunction generateScheduleTemplate(name: string, chatSpecifier: string): string {\n const base = cronNameOf(name)\n return [\n `import { defineCron } from 'theokit/server/cron'`,\n `import { resolveProvider } from 'theokit/server/agent'`,\n `import { streamAgentTurnInProcess } from '@theokit/agents'`,\n ``,\n `import chat from '${chatSpecifier}'`,\n ``,\n `/**`,\n ` * A scheduled agent run, a first-class TheoKit cron. \\`theokit build\\` discovers it and translates the`,\n ` * schedule to your deploy target's native cron; \\`theokit start\\` runs it in-process.`,\n ` *`,\n ` * Each fire is one model run of your \\`chat\\` agent, so it spends tokens. Nobody is present to approve`,\n ` * a gated tool, so the run uses the \\`auto-reject\\` posture: every approval is answered \"no\" and the`,\n ` * gated tool does not run.`,\n ` *`,\n ` * Schedules are UTC (https://crontab.guru). \\`signal\\` aborts when the scheduler stops.`,\n ` */`,\n `export default defineCron('${base}', {`,\n ` schedule: '0 9 * * *', // every day at 09:00 UTC`,\n ` async handler({ traceId, scheduledAt, signal }) {`,\n ` let outcome = 'ok'`,\n ` try {`,\n ` const agent = chat as { model?: string; plugins?: readonly unknown[] }`,\n ` const { apiKey } = resolveProvider(agent.model, { plugins: agent.plugins })`,\n ` const run = streamAgentTurnInProcess(chat, apiKey, {`,\n ` message: \\`Scheduled run \"${base}\" at \\${scheduledAt.toISOString()}\\`,`,\n ` signal,`,\n ` approvals: {`,\n ` kind: 'auto-reject',`,\n ` reason: 'unattended schedule \"${base}\": nobody is present to approve',`,\n ` },`,\n ` })`,\n ` for await (const chunk of run) {`,\n ` // The SDK can report a failed run as a chunk rather than a rejection.`,\n ` if (chunk.type === 'error') {`,\n ` throw new Error(\\`[${base}] agent run failed: \\${chunk.errorText ?? 'no error text'}\\`)`,\n ` }`,\n ` }`,\n ` } catch (err) {`,\n ` outcome = \\`failed: \\${err instanceof Error ? err.message : String(err)}\\``,\n ` throw err`,\n ` } finally {`,\n ` // One traced line per fire, failed fires included: the scheduler's own error line has no trace id.`,\n ` console.log(\\`[${base}] fire \\${outcome} at \\${scheduledAt.toISOString()} (trace \\${traceId})\\`)`,\n ` }`,\n ` },`,\n `})`,\n ``,\n ].join('\\n')\n}\n","import type { CronDefinition, CronOptions } from './cron-types.js'\nimport { validateCronSchedule } from './cron-validate.js'\n\nconst NAME_RE = /^[a-z0-9][a-z0-9-]{0,63}$/\n\n/** What {@link isValidCronName} requires, in words, for an error message. */\nexport const CRON_NAME_RULE =\n 'Must be 1-64 chars, lowercase alphanumeric + hyphen, starting with [a-z0-9].'\n\n/** Whether `defineCron` accepts `name`. The generator asks this before writing a cron. */\nexport function isValidCronName(name: string): boolean {\n return typeof name === 'string' && NAME_RE.test(name)\n}\n\nfunction validateName(name: string): void {\n if (!isValidCronName(name)) {\n throw new Error(`defineCron: invalid name \"${name}\". ${CRON_NAME_RULE}`)\n }\n}\n\n/**\n * Declare a time-triggered handler. Pure identity helper — no\n * registration side effect; the build-time scanner (T1.3) discovers\n * definitions by walking `server/crons/` and emits a manifest the\n * adapters translate at deploy.\n *\n * @example\n * ```ts\n * // server/crons/morning-summary.ts\n * export default defineCron('morning-summary', {\n * schedule: '0 9 * * *', // 09:00 UTC\n * async handler({ traceId, scheduledAt, signal }) {\n * // ...\n * },\n * })\n * ```\n */\nexport function defineCron(name: string, options: CronOptions): CronDefinition {\n validateName(name)\n validateCronSchedule(options.schedule)\n return {\n name,\n schedule: options.schedule,\n handler: options.handler,\n concurrency: options.concurrency ?? 'forbid',\n }\n}\n","/**\n * Shared types for the `theokit generate` CLI command family.\n *\n * Extracted to break the circular dependency between generate.ts ↔ generate-resource.ts\n * (architecture-remediation plan T1.1, 2026-06-12).\n */\n\nexport const VALID_TYPES = [\n 'route',\n 'action',\n 'page',\n 'ws',\n 'controller',\n 'agent',\n 'toolbox',\n 'workflow',\n 'eval',\n 'sandbox',\n 'schedule',\n 'memory',\n 'resource',\n] as const\nexport type GeneratorType = (typeof VALID_TYPES)[number]\n\nexport interface GenerateOptions {\n cwd: string\n type: string\n name: string\n fields?: string[]\n}\n\nexport type GenerateStatus =\n | 'created'\n | 'already_exists'\n | 'invalid_kind'\n | 'invalid_name'\n | 'not_a_project'\n | 'invalid_config'\n | 'agent_not_found'\n\nexport interface GenerateResult {\n status: GenerateStatus\n filePath?: string\n kind?: GeneratorType\n name?: string\n message?: string\n}\n"],"mappings":"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","names":["existsSync","mkdirSync","writeFileSync","resolve","dirname","resolve","resolve","dirname","relative","resolve","sep","resolve","relative","dirname","sep","basename","resolve","existsSync","mkdirSync","dirname","writeFileSync"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -133,14 +133,14 @@ declare const theoConfigSchema: z.ZodObject<{
|
|
|
133
133
|
permissionsPolicy: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<false>]>>;
|
|
134
134
|
}, z.core.$strip>>;
|
|
135
135
|
disallowed: z.ZodOptional<z.ZodObject<{
|
|
136
|
-
routes: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.
|
|
136
|
+
routes: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodInstanceOf<RegExp>]>>;
|
|
137
137
|
behavior: z.ZodDefault<z.ZodEnum<{
|
|
138
138
|
warn: "warn";
|
|
139
139
|
raise: "raise";
|
|
140
140
|
}>>;
|
|
141
141
|
}, z.core.$strip>>;
|
|
142
142
|
cors: z.ZodOptional<z.ZodObject<{
|
|
143
|
-
origins: z.ZodUnion<readonly [z.ZodLiteral<"*">, z.ZodString, z.
|
|
143
|
+
origins: z.ZodUnion<readonly [z.ZodLiteral<"*">, z.ZodString, z.ZodInstanceOf<RegExp>, z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodInstanceOf<RegExp>]>>, z.ZodFunction<z.ZodTuple<[z.ZodString], null>, z.ZodBoolean>]>;
|
|
144
144
|
methods: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
145
145
|
GET: "GET";
|
|
146
146
|
HEAD: "HEAD";
|