@theokit/cli 0.1.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/index.cjs ADDED
@@ -0,0 +1,1552 @@
1
+ 'use strict';
2
+
3
+ var commander = require('commander');
4
+ var url = require('url');
5
+ var fs = require('fs');
6
+ var path = require('path');
7
+ var child_process = require('child_process');
8
+ var pc5 = require('picocolors');
9
+ var module$1 = require('module');
10
+ var pathSafety = require('@theokit/sdk/path-safety');
11
+ var crypto = require('crypto');
12
+ var _eval = require('@theokit/sdk/eval');
13
+ var p = require('@clack/prompts');
14
+ var sdk = require('@theokit/sdk');
15
+ var os = require('os');
16
+ var taskStore = require('@theokit/sdk/task-store');
17
+
18
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
19
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
20
+
21
+ function _interopNamespace(e) {
22
+ if (e && e.__esModule) return e;
23
+ var n = Object.create(null);
24
+ if (e) {
25
+ Object.keys(e).forEach(function (k) {
26
+ if (k !== 'default') {
27
+ var d = Object.getOwnPropertyDescriptor(e, k);
28
+ Object.defineProperty(n, k, d.get ? d : {
29
+ enumerable: true,
30
+ get: function () { return e[k]; }
31
+ });
32
+ }
33
+ });
34
+ }
35
+ n.default = e;
36
+ return Object.freeze(n);
37
+ }
38
+
39
+ var pc5__default = /*#__PURE__*/_interopDefault(pc5);
40
+ var p__namespace = /*#__PURE__*/_interopNamespace(p);
41
+
42
+ // src/main.ts
43
+ var FALLBACK_CANDIDATES = [
44
+ "src/index.ts",
45
+ "src/index.tsx",
46
+ "src/index.mjs",
47
+ "src/index.js",
48
+ "index.ts",
49
+ "index.mjs",
50
+ "index.js"
51
+ ];
52
+ function entryNotFoundError(message) {
53
+ const err = new Error(message);
54
+ err.code = "entry_not_found";
55
+ return err;
56
+ }
57
+ function resolveFromExplicit(cwd, explicit) {
58
+ const abs = path.isAbsolute(explicit) ? explicit : path.resolve(cwd, explicit);
59
+ if (!fs.existsSync(abs)) throw entryNotFoundError(`Entry file not found: ${explicit}`);
60
+ return abs;
61
+ }
62
+ function resolveFromPackageJson(cwd) {
63
+ const pkgPath = path.join(cwd, "package.json");
64
+ if (!fs.existsSync(pkgPath)) return void 0;
65
+ try {
66
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
67
+ if (typeof pkg.main === "string" && pkg.main.length > 0) {
68
+ const abs = path.resolve(cwd, pkg.main);
69
+ if (fs.existsSync(abs)) return abs;
70
+ }
71
+ } catch {
72
+ }
73
+ return void 0;
74
+ }
75
+ function resolveFromCandidates(cwd) {
76
+ for (const candidate of FALLBACK_CANDIDATES) {
77
+ const abs = path.join(cwd, candidate);
78
+ if (fs.existsSync(abs)) return abs;
79
+ }
80
+ return void 0;
81
+ }
82
+ function resolveEntry(cwd, explicit) {
83
+ if (explicit !== void 0 && explicit.length > 0) return resolveFromExplicit(cwd, explicit);
84
+ const fromPkg = resolveFromPackageJson(cwd);
85
+ if (fromPkg !== void 0) return fromPkg;
86
+ const fromCandidate = resolveFromCandidates(cwd);
87
+ if (fromCandidate !== void 0) return fromCandidate;
88
+ throw entryNotFoundError(
89
+ `No entry file found. Pass --entry <path> or create one of: ${FALLBACK_CANDIDATES.join(", ")}`
90
+ );
91
+ }
92
+
93
+ // src/commands/acp.ts
94
+ function resolveDefaultExport(mod) {
95
+ if (mod === null || mod === void 0) return void 0;
96
+ const asObj = mod;
97
+ return asObj.default ?? mod;
98
+ }
99
+ function isValidAgentShape(value) {
100
+ if (typeof value === "function") return true;
101
+ if (typeof value !== "object" || value === null) return false;
102
+ const obj = value;
103
+ return typeof obj.agentId === "string" && typeof obj.send === "function";
104
+ }
105
+ function fail(msg, code = 1) {
106
+ process.stderr.write(`theokit acp: ${msg}
107
+ `);
108
+ return code;
109
+ }
110
+ async function loadAgentFromEntry(entryArg) {
111
+ let entryPath;
112
+ try {
113
+ entryPath = resolveEntry(process.cwd(), entryArg);
114
+ } catch (err) {
115
+ return { ok: false, exitCode: fail(err instanceof Error ? err.message : String(err), 2) };
116
+ }
117
+ let mod;
118
+ try {
119
+ mod = await import(url.pathToFileURL(entryPath).href);
120
+ } catch (err) {
121
+ return {
122
+ ok: false,
123
+ exitCode: fail(`failed to import entry: ${err instanceof Error ? err.message : err}`, 1)
124
+ };
125
+ }
126
+ const agent = resolveDefaultExport(mod);
127
+ if (!isValidAgentShape(agent)) {
128
+ return {
129
+ ok: false,
130
+ exitCode: fail(
131
+ `entry ${entryPath} must export an SDKAgent or a factory (sessionId) => SDKAgent. Got: ${typeof agent}`,
132
+ 2
133
+ )
134
+ };
135
+ }
136
+ return { ok: true, agent };
137
+ }
138
+ function parseTrustedTools(raw) {
139
+ if (typeof raw !== "string" || raw.length === 0) return void 0;
140
+ return raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
141
+ }
142
+ function parseTimeout(raw) {
143
+ if (typeof raw !== "string") return { ok: true };
144
+ const n = Number.parseInt(raw, 10);
145
+ if (!Number.isFinite(n) || n <= 0) return { ok: false };
146
+ return { ok: true, value: n };
147
+ }
148
+ function parseFlags(opts) {
149
+ const permission = opts.permission ?? "ask";
150
+ if (permission !== "ask" && permission !== "auto" && permission !== "deny") {
151
+ return { ok: false, exitCode: fail(`invalid --permission: ${permission}`, 2) };
152
+ }
153
+ const timeout = parseTimeout(opts.permissionTimeoutMs);
154
+ if (!timeout.ok) {
155
+ return {
156
+ ok: false,
157
+ exitCode: fail(`invalid --permission-timeout-ms: ${opts.permissionTimeoutMs}`, 2)
158
+ };
159
+ }
160
+ const trustedTools = parseTrustedTools(opts.trustedTools);
161
+ const flags = { permission };
162
+ if (trustedTools !== void 0) flags.trustedTools = trustedTools;
163
+ if (timeout.value !== void 0) flags.permissionTimeoutMs = timeout.value;
164
+ return { ok: true, flags };
165
+ }
166
+ async function importServeAcp() {
167
+ try {
168
+ const mod = await import('@theokit/acp');
169
+ return { ok: true, serveAcp: mod.serveAcp };
170
+ } catch (err) {
171
+ return {
172
+ ok: false,
173
+ exitCode: fail(
174
+ `@theokit/acp not installed. Run: npm i @theokit/acp. (${err instanceof Error ? err.message : err})`,
175
+ 1
176
+ )
177
+ };
178
+ }
179
+ }
180
+ async function runAcp(opts) {
181
+ const agentRes = await loadAgentFromEntry(opts.entry);
182
+ if (!agentRes.ok) return agentRes.exitCode;
183
+ const flagsRes = parseFlags(opts);
184
+ if (!flagsRes.ok) return flagsRes.exitCode;
185
+ const serveRes = await importServeAcp();
186
+ if (!serveRes.ok) return serveRes.exitCode;
187
+ try {
188
+ await serveRes.serveAcp({
189
+ agent: agentRes.agent,
190
+ permissionDefault: flagsRes.flags.permission,
191
+ ...flagsRes.flags.trustedTools !== void 0 ? { trustedTools: flagsRes.flags.trustedTools } : {},
192
+ ...flagsRes.flags.permissionTimeoutMs !== void 0 ? { permissionTimeoutMs: flagsRes.flags.permissionTimeoutMs } : {}
193
+ });
194
+ } catch (err) {
195
+ return fail(err instanceof Error ? err.message : String(err), 1);
196
+ }
197
+ return 0;
198
+ }
199
+ function findDrizzleKit(cwd) {
200
+ const candidates = [
201
+ path.join(cwd, "node_modules", ".bin", "drizzle-kit"),
202
+ path.join(cwd, "node_modules", "drizzle-kit", "bin.cjs")
203
+ ];
204
+ for (const c of candidates) {
205
+ if (fs.existsSync(c)) return c;
206
+ }
207
+ return null;
208
+ }
209
+ function runDrizzleKit(verb, cwd, extraArgs = []) {
210
+ const bin = findDrizzleKit(cwd);
211
+ if (!bin) {
212
+ process.stderr.write(
213
+ "theokit db: drizzle-kit not found in node_modules. Install it: pnpm add -D drizzle-kit\n"
214
+ );
215
+ return 2;
216
+ }
217
+ const r = child_process.spawnSync(bin, [verb, ...extraArgs], { cwd, stdio: "inherit" });
218
+ if (r.status === null) return 1;
219
+ return r.status;
220
+ }
221
+ function runDbGenerate(opts = {}) {
222
+ return runDrizzleKit("generate", opts.cwd ?? process.cwd());
223
+ }
224
+ function runDbMigrate(opts = {}) {
225
+ return runDrizzleKit("migrate", opts.cwd ?? process.cwd());
226
+ }
227
+ function runDbStudio(opts = {}) {
228
+ return runDrizzleKit("studio", opts.cwd ?? process.cwd());
229
+ }
230
+ function runDbPush(opts = {}) {
231
+ return runDrizzleKit("push", opts.cwd ?? process.cwd());
232
+ }
233
+ async function loadOrmConfig(configPath) {
234
+ if (!fs.existsSync(configPath)) {
235
+ throw new Error(
236
+ `theokit db: orm.config not found at ${configPath}. Create one that default-exports { schema } (the Drizzle schema object).`
237
+ );
238
+ }
239
+ const bust = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
240
+ const url$1 = `${url.pathToFileURL(configPath).href}?v=${bust}`;
241
+ const mod = await import(url$1);
242
+ if (!mod.default || typeof mod.default !== "object" || !mod.default.schema) {
243
+ throw new Error(
244
+ `theokit db: orm.config at ${configPath} must default-export { schema }. Got: ${typeof mod.default}`
245
+ );
246
+ }
247
+ return mod.default;
248
+ }
249
+ async function loadSchemaExporter(cwd) {
250
+ const candidates = [
251
+ path.join(cwd, "node_modules", "@theokit", "orm", "dist", "schema-export.js"),
252
+ path.join(cwd, "node_modules", "@theokit", "orm", "schema-export.js")
253
+ ];
254
+ for (const c of candidates) {
255
+ if (fs.existsSync(c)) {
256
+ return await import(url.pathToFileURL(c).href);
257
+ }
258
+ }
259
+ throw new Error("theokit db: @theokit/orm is not installed. Install it: pnpm add @theokit/orm");
260
+ }
261
+ function atomicWrite(filePath, content) {
262
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
263
+ const tmp = `${filePath}.${process.pid}.tmp`;
264
+ fs.writeFileSync(tmp, content, { mode: 420 });
265
+ fs.renameSync(tmp, filePath);
266
+ }
267
+ async function loadConfigOrError(configPath) {
268
+ try {
269
+ return { ok: true, value: await loadOrmConfig(configPath) };
270
+ } catch (err) {
271
+ process.stderr.write(`${err instanceof Error ? err.message : String(err)}
272
+ `);
273
+ return { ok: false, code: 2 };
274
+ }
275
+ }
276
+ async function loadExporterOrError(cwd) {
277
+ try {
278
+ return { ok: true, value: await loadSchemaExporter(cwd) };
279
+ } catch (err) {
280
+ process.stderr.write(`${err instanceof Error ? err.message : String(err)}
281
+ `);
282
+ return { ok: false, code: 2 };
283
+ }
284
+ }
285
+ function exportSchemasOrError(exporter, schema) {
286
+ try {
287
+ return { ok: true, value: exporter.exportSchemas(schema) };
288
+ } catch (err) {
289
+ process.stderr.write(
290
+ `theokit db: schema export failed: ${err instanceof Error ? err.message : String(err)}
291
+ `
292
+ );
293
+ return { ok: false, code: 1 };
294
+ }
295
+ }
296
+ function writeSchemasToOutDir(schemas, outDir) {
297
+ fs.mkdirSync(outDir, { recursive: true });
298
+ for (const f of fs.readdirSync(outDir).filter((n) => n.endsWith(".schema.json"))) {
299
+ fs.unlinkSync(path.join(outDir, f));
300
+ }
301
+ for (const [name, schema] of Object.entries(schemas)) {
302
+ const target = path.join(outDir, `${name}.schema.json`);
303
+ atomicWrite(target, `${JSON.stringify(schema, null, 2)}
304
+ `);
305
+ }
306
+ }
307
+ async function runDbExportSchema(opts = {}) {
308
+ const cwd = opts.cwd ?? process.cwd();
309
+ const configPath = path.resolve(cwd, opts.config ?? "orm.config.ts");
310
+ const outDir = path.resolve(cwd, opts.out ?? ".theokit/schema");
311
+ const configResult = await loadConfigOrError(configPath);
312
+ if (!configResult.ok) return configResult.code;
313
+ const exporterResult = await loadExporterOrError(cwd);
314
+ if (!exporterResult.ok) return exporterResult.code;
315
+ const schemasResult = exportSchemasOrError(exporterResult.value, configResult.value.schema);
316
+ if (!schemasResult.ok) return schemasResult.code;
317
+ writeSchemasToOutDir(schemasResult.value, outDir);
318
+ process.stdout.write(
319
+ `theokit db: wrote ${Object.keys(schemasResult.value).length} schema(s) to ${outDir}
320
+ `
321
+ );
322
+ return 0;
323
+ }
324
+ function readCommittedSchemas(outDir) {
325
+ if (!fs.existsSync(outDir)) return {};
326
+ const out = {};
327
+ for (const f of fs.readdirSync(outDir).filter((n) => n.endsWith(".schema.json"))) {
328
+ const name = f.replace(/\.schema\.json$/, "");
329
+ out[name] = JSON.parse(fs.readFileSync(path.join(outDir, f), "utf-8"));
330
+ }
331
+ return out;
332
+ }
333
+ function computeSchemaDrift(fresh, committed) {
334
+ const freshKeys = new Set(Object.keys(fresh));
335
+ const committedKeys = new Set(Object.keys(committed));
336
+ const added = [...freshKeys].filter((k) => !committedKeys.has(k));
337
+ const removed = [...committedKeys].filter((k) => !freshKeys.has(k));
338
+ const changed = [...freshKeys].filter(
339
+ (k) => committedKeys.has(k) && JSON.stringify(fresh[k]) !== JSON.stringify(committed[k])
340
+ );
341
+ return { added, removed, changed };
342
+ }
343
+ function reportSchemaDrift(drift) {
344
+ const { added, removed, changed } = drift;
345
+ if (added.length === 0 && removed.length === 0 && changed.length === 0) {
346
+ process.stdout.write("theokit db: schema in sync (no drift)\n");
347
+ return 0;
348
+ }
349
+ process.stderr.write("theokit db: schema drift detected\n");
350
+ if (added.length > 0) process.stderr.write(` added: ${added.join(", ")}
351
+ `);
352
+ if (removed.length > 0) process.stderr.write(` removed: ${removed.join(", ")}
353
+ `);
354
+ if (changed.length > 0) process.stderr.write(` changed: ${changed.join(", ")}
355
+ `);
356
+ process.stderr.write("Run `theokit db export-schema` to refresh.\n");
357
+ return 1;
358
+ }
359
+ async function runDbCheckSchemaDrift(opts = {}) {
360
+ const cwd = opts.cwd ?? process.cwd();
361
+ const configPath = path.resolve(cwd, opts.config ?? "orm.config.ts");
362
+ const outDir = path.resolve(cwd, opts.out ?? ".theokit/schema");
363
+ const configResult = await loadConfigOrError(configPath);
364
+ if (!configResult.ok) return configResult.code;
365
+ const exporterResult = await loadExporterOrError(cwd);
366
+ if (!exporterResult.ok) return exporterResult.code;
367
+ const fresh = exporterResult.value.exportSchemas(configResult.value.schema);
368
+ const committed = readCommittedSchemas(outDir);
369
+ return reportSchemaDrift(computeSchemaDrift(fresh, committed));
370
+ }
371
+ function resolveTsxBin() {
372
+ const require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
373
+ return require2.resolve("tsx/cli");
374
+ }
375
+ function startRunner(opts) {
376
+ const tsxBin = resolveTsxBin();
377
+ const args = [];
378
+ if (opts.watch !== false) args.push("--watch");
379
+ const envFile = opts.envFile ?? ".env";
380
+ const envFileAbs = path.join(opts.cwd, envFile);
381
+ if (fs.existsSync(envFileAbs)) {
382
+ args.push("--env-file", envFile);
383
+ }
384
+ args.push(opts.entry);
385
+ const child = child_process.spawn(process.execPath, [tsxBin, ...args], {
386
+ cwd: opts.cwd,
387
+ stdio: "inherit",
388
+ env: process.env
389
+ });
390
+ const exited = new Promise((resolve6) => {
391
+ child.on("exit", (code, signal) => {
392
+ resolve6(code ?? (signal !== null ? 130 : 0));
393
+ });
394
+ });
395
+ let killTimer;
396
+ const forward = (sig) => {
397
+ if (child.killed) return;
398
+ child.kill(sig);
399
+ killTimer = setTimeout(() => {
400
+ if (!child.killed) child.kill("SIGKILL");
401
+ }, 5e3);
402
+ killTimer.unref();
403
+ };
404
+ process.on("SIGINT", () => forward("SIGTERM"));
405
+ process.on("SIGTERM", () => forward("SIGTERM"));
406
+ child.on("exit", () => {
407
+ if (killTimer !== void 0) clearTimeout(killTimer);
408
+ });
409
+ return { child, exited };
410
+ }
411
+
412
+ // src/commands/dev.ts
413
+ async function runDev(opts) {
414
+ let entry;
415
+ try {
416
+ entry = resolveEntry(process.cwd(), opts.entry);
417
+ } catch (err) {
418
+ const message = err instanceof Error ? err.message : String(err);
419
+ const code = err.code ?? "unknown";
420
+ process.stderr.write(`${pc5__default.default.red("error:")} ${message}
421
+ ${pc5__default.default.gray(`(code: ${code})`)}
422
+ `);
423
+ return code === "entry_not_found" ? 2 : 1;
424
+ }
425
+ process.stdout.write(`${pc5__default.default.cyan("[dev]")} watching ${pc5__default.default.bold(entry)} \u2014 Ctrl+C to stop.
426
+ `);
427
+ let handle;
428
+ try {
429
+ handle = startRunner({
430
+ entry,
431
+ cwd: process.cwd(),
432
+ ...opts.env !== void 0 ? { envFile: opts.env } : {}
433
+ });
434
+ } catch (err) {
435
+ process.stderr.write(
436
+ `${pc5__default.default.red("error:")} could not spawn tsx \u2014 ${err instanceof Error ? err.message : String(err)}
437
+ ${pc5__default.default.gray("Hint: try `pnpm install` to repair @theokit/cli.")}
438
+ `
439
+ );
440
+ return 1;
441
+ }
442
+ const exitCode = await handle.exited;
443
+ return exitCode;
444
+ }
445
+ function configError(code, message) {
446
+ const err = new Error(message);
447
+ err.code = code;
448
+ return err;
449
+ }
450
+ async function importConfig(abs) {
451
+ try {
452
+ return await import(url.pathToFileURL(abs).href);
453
+ } catch (cause) {
454
+ throw configError(
455
+ "config_load_failed",
456
+ `Failed to load eval config (${abs}): ${cause instanceof Error ? cause.message : String(cause)}`
457
+ );
458
+ }
459
+ }
460
+ function validateShape(cfg, abs) {
461
+ if (!Array.isArray(cfg.dataset)) throwShapeError(abs, "dataset", "must be an array");
462
+ if (!Array.isArray(cfg.scorers)) throwShapeError(abs, "scorers", "must be an array");
463
+ if (cfg.agent === void 0 || typeof cfg.agent !== "object") {
464
+ throwShapeError(abs, "agent", "must be an Agent.create() options object");
465
+ }
466
+ return cfg;
467
+ }
468
+ async function loadEvalConfig(cwd, configPath) {
469
+ const relPath = configPath ?? "./eval.config.ts";
470
+ const abs = path.isAbsolute(relPath) ? relPath : path.resolve(cwd, relPath);
471
+ if (!fs.existsSync(abs)) throw configError("config_not_found", `Eval config not found: ${abs}`);
472
+ const mod = await importConfig(abs);
473
+ if (mod.default === void 0) {
474
+ throw configError(
475
+ "config_no_default_export",
476
+ `Eval config (${abs}) must have a default export. Example: \`export default { dataset, scorers, agent } satisfies EvalConfig;\``
477
+ );
478
+ }
479
+ return validateShape(mod.default, abs);
480
+ }
481
+ function throwShapeError(abs, field, why) {
482
+ const err = new Error(`Eval config (${abs}) is invalid: field "${field}" ${why}.`);
483
+ err.code = "config_invalid_shape";
484
+ throw err;
485
+ }
486
+
487
+ // src/eval/report.ts
488
+ function formatReport(result) {
489
+ const lines = [];
490
+ lines.push("# Eval Report");
491
+ lines.push("");
492
+ lines.push(`- **Total rows:** ${result.aggregate.totalRows}`);
493
+ lines.push(`- **Mean score:** ${result.aggregate.meanScore.toFixed(3)}`);
494
+ lines.push(`- **Pass ratio (\u22650.5):** ${(result.aggregate.passRatio * 100).toFixed(1)}%`);
495
+ lines.push(`- **Error rows:** ${result.aggregate.errorRows}`);
496
+ lines.push("");
497
+ lines.push("## Per-row results");
498
+ lines.push("");
499
+ lines.push("| # | Input | Output | Mean | Scores | Notes |");
500
+ lines.push("|---|---|---|---:|---|---|");
501
+ for (let i = 0; i < result.rows.length; i += 1) {
502
+ const row = result.rows[i];
503
+ if (row === void 0) continue;
504
+ const input = escapeMd(truncate(row.input, 60));
505
+ const output = row.error !== void 0 ? `*error*` : escapeMd(truncate(row.output, 80));
506
+ const mean = row.meanScore.toFixed(3);
507
+ const scores = row.scores.length > 0 ? row.scores.map(
508
+ (s) => `${s.name}=${s.score.toFixed(2)}${s.reason !== void 0 ? ` (${escapeMd(s.reason)})` : ""}`
509
+ ).join("; ") : "\u2014";
510
+ const notes = row.error !== void 0 ? escapeMd(row.error) : "";
511
+ lines.push(`| ${i + 1} | ${input} | ${output} | ${mean} | ${scores} | ${notes} |`);
512
+ }
513
+ return `${lines.join("\n")}
514
+ `;
515
+ }
516
+ function truncate(s, max) {
517
+ if (s.length <= max) return s;
518
+ return `${s.slice(0, max - 1)}\u2026`;
519
+ }
520
+ function escapeMd(s) {
521
+ return s.replaceAll("|", "\\|").replaceAll("\n", " ");
522
+ }
523
+ async function runEvalSuite(config) {
524
+ if (config.dataset.length === 0) {
525
+ return {
526
+ rows: [],
527
+ aggregate: { meanScore: 0, passRatio: 0, totalRows: 0, errorRows: 0 }
528
+ };
529
+ }
530
+ const sdkScorers = config.scorers.map((s) => ({
531
+ name: s.name,
532
+ score: s.score
533
+ }));
534
+ const run = await _eval.Eval.create({
535
+ // Unique per-process name for telemetry correlation (D213). Use UUID
536
+ // so concurrent CLI invocations on different terminals don't collide.
537
+ name: `theokit-eval-${crypto.randomUUID().slice(0, 8)}`,
538
+ dataset: config.dataset,
539
+ scorers: sdkScorers,
540
+ agent: config.agent,
541
+ ...config.concurrency !== void 0 ? { concurrency: config.concurrency } : {}
542
+ }).run();
543
+ const rows = run.rows.map((r) => ({
544
+ input: r.input,
545
+ output: r.output,
546
+ ...r.expected !== void 0 ? { expected: r.expected } : {},
547
+ scores: r.scores.map((s) => ({
548
+ name: s.name,
549
+ score: s.score,
550
+ ...s.reason !== void 0 ? { reason: s.reason } : {}
551
+ })),
552
+ meanScore: r.meanScore,
553
+ ...r.error !== void 0 ? { error: r.error } : {}
554
+ }));
555
+ return {
556
+ rows,
557
+ aggregate: {
558
+ meanScore: run.aggregate.meanScore,
559
+ passRatio: run.aggregate.passRatio,
560
+ totalRows: run.aggregate.totalRows,
561
+ errorRows: run.aggregate.errorRows
562
+ }
563
+ };
564
+ }
565
+
566
+ // src/commands/eval.ts
567
+ function resolveOutputPath(cwd, output) {
568
+ const target = output ?? "eval-report.md";
569
+ try {
570
+ return pathSafety.safePathJoin(cwd, target);
571
+ } catch (err) {
572
+ if (err instanceof pathSafety.PathTraversalError) {
573
+ const e = new Error(
574
+ `--output path must be inside the current working directory. Got: ${target}`
575
+ );
576
+ e.code = "invalid_output_path";
577
+ throw e;
578
+ }
579
+ throw err;
580
+ }
581
+ }
582
+ function reportError(err, prefix = "error:") {
583
+ const message = err instanceof Error ? err.message : String(err);
584
+ const code = err.code ?? "unknown";
585
+ process.stderr.write(`${pc5__default.default.red(prefix)} ${message}
586
+ ${pc5__default.default.gray(`(code: ${code})`)}
587
+ `);
588
+ return { message, code };
589
+ }
590
+ async function loadAndValidateConfig(cwd, configPath) {
591
+ try {
592
+ return await loadEvalConfig(cwd, configPath);
593
+ } catch (err) {
594
+ const { code } = reportError(err);
595
+ return { failureExit: code.startsWith("config_") ? 2 : 1 };
596
+ }
597
+ }
598
+ function writeReport(outputAbs, report) {
599
+ try {
600
+ void path.dirname;
601
+ fs.writeFileSync(outputAbs, report, "utf8");
602
+ return true;
603
+ } catch (err) {
604
+ process.stderr.write(
605
+ `${pc5__default.default.red("error:")} could not write report to ${outputAbs} \u2014 ${err instanceof Error ? err.message : String(err)}
606
+ `
607
+ );
608
+ return false;
609
+ }
610
+ }
611
+ async function runEval(opts) {
612
+ const cwd = process.cwd();
613
+ let outputAbs;
614
+ try {
615
+ outputAbs = resolveOutputPath(cwd, opts.output);
616
+ } catch (err) {
617
+ reportError(err);
618
+ return 2;
619
+ }
620
+ const configOrErr = await loadAndValidateConfig(cwd, opts.config);
621
+ if ("failureExit" in configOrErr) return configOrErr.failureExit;
622
+ const config = configOrErr;
623
+ process.stdout.write(
624
+ `${pc5__default.default.cyan("[eval]")} running ${config.dataset.length} prompt(s) with ${config.scorers.length} scorer(s)...
625
+ `
626
+ );
627
+ let result;
628
+ try {
629
+ result = await runEvalSuite(config);
630
+ } catch (err) {
631
+ process.stderr.write(
632
+ `${pc5__default.default.red("error:")} eval run failed \u2014 ${err instanceof Error ? err.message : String(err)}
633
+ `
634
+ );
635
+ return 1;
636
+ }
637
+ if (!writeReport(outputAbs, formatReport(result))) return 1;
638
+ process.stdout.write(
639
+ `
640
+ ${pc5__default.default.green("\u2713")} ${result.aggregate.totalRows} rows \xB7 mean score ${result.aggregate.meanScore.toFixed(3)} \xB7 ${(result.aggregate.passRatio * 100).toFixed(1)}% pass \xB7 ${result.aggregate.errorRows} error(s)
641
+ report: ${outputAbs}
642
+ `
643
+ );
644
+ return 0;
645
+ }
646
+
647
+ // src/version.ts
648
+ var SDK_VERSION = "1.5.0";
649
+ var CLI_VERSION = "0.1.1";
650
+
651
+ // src/init/templates.ts
652
+ var TEMPLATES = [
653
+ {
654
+ name: "minimal",
655
+ description: "Smallest possible agent \u2014 one Agent.create + send + stream.",
656
+ hint: "Set ANTHROPIC_API_KEY / OPENAI_API_KEY / OPENROUTER_API_KEY in .env."
657
+ },
658
+ {
659
+ name: "ollama-local",
660
+ description: "100% local agent via Ollama (no remote API key required).",
661
+ hint: "Requires `ollama serve` + `ollama pull llama3.2:3b`."
662
+ },
663
+ {
664
+ name: "telegram-bot",
665
+ description: "Telegram bot via @theokit/gateway + grammy.",
666
+ hint: "Get a bot token from @BotFather, set TELEGRAM_BOT_TOKEN in .env."
667
+ }
668
+ ];
669
+ function findTemplate(name) {
670
+ return TEMPLATES.find((t) => t.name === name);
671
+ }
672
+ var DEFAULT_TEMPLATE = "minimal";
673
+
674
+ // src/init/validate-name.ts
675
+ var NPM_NAME_RE = /^(?:@[a-z0-9-][a-z0-9._-]{0,213}\/)?[a-z0-9][a-z0-9._-]{0,213}$/;
676
+ var RESERVED_NAMES = /* @__PURE__ */ new Set(["node_modules", "favicon.ico", "..", "."]);
677
+ function validateProjectName(name) {
678
+ if (typeof name !== "string" || name.length === 0) {
679
+ return { ok: false, reason: "Project name is required." };
680
+ }
681
+ if (RESERVED_NAMES.has(name)) {
682
+ return { ok: false, reason: `Name "${name}" is reserved.` };
683
+ }
684
+ if (name.length > 214) {
685
+ return { ok: false, reason: "Project name must be \u2264 214 characters." };
686
+ }
687
+ if (!NPM_NAME_RE.test(name)) {
688
+ return {
689
+ ok: false,
690
+ reason: "Invalid project name. Use lowercase letters, numbers, dashes, dots, or underscores. Examples: `my-bot`, `@scope/my-bot`, `chat.bot`."
691
+ };
692
+ }
693
+ return { ok: true };
694
+ }
695
+
696
+ // src/init/scaffold.ts
697
+ var SUBSTITUTE_EXTS = /* @__PURE__ */ new Set([
698
+ ".ts",
699
+ ".tsx",
700
+ ".js",
701
+ ".mjs",
702
+ ".json",
703
+ ".md",
704
+ ".env",
705
+ ".example",
706
+ ".gitignore",
707
+ ".yml",
708
+ ".yaml"
709
+ ]);
710
+ function resolveTemplatesRoot() {
711
+ const here = url.fileURLToPath(new URL(".", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
712
+ let dir = here;
713
+ for (let i = 0; i < 5; i += 1) {
714
+ const candidate = path.join(dir, "templates");
715
+ if (fs.existsSync(candidate) && fs.lstatSync(candidate).isDirectory()) return candidate;
716
+ const parent = path.dirname(dir);
717
+ if (parent === dir) break;
718
+ dir = parent;
719
+ }
720
+ throw new Error(
721
+ `Could not locate bundled templates/ directory (searched up from ${here}). This usually means the published tarball was built without "files": ["templates"] (EC-C regression).`
722
+ );
723
+ }
724
+ function scaffoldError(code, message) {
725
+ const err = new Error(message);
726
+ err.code = code;
727
+ return err;
728
+ }
729
+ function validateNameAndTemplate(opts) {
730
+ const nameCheck = validateProjectName(opts.projectName);
731
+ if (!nameCheck.ok) {
732
+ throw scaffoldError("invalid_project_name", nameCheck.reason ?? "Invalid project name.");
733
+ }
734
+ if (findTemplate(opts.template) === void 0) {
735
+ throw scaffoldError("unknown_template", `Unknown template "${opts.template}".`);
736
+ }
737
+ }
738
+ function validateDest(cwdAbs, destAbs, projectName, force) {
739
+ const rel = path.relative(cwdAbs, destAbs);
740
+ if (rel.startsWith("..") || path.isAbsolute(rel)) {
741
+ throw scaffoldError(
742
+ "invalid_dest",
743
+ `Project name "${projectName}" escapes the working directory.`
744
+ );
745
+ }
746
+ if (!fs.existsSync(destAbs)) return;
747
+ if (fs.lstatSync(destAbs).isSymbolicLink()) {
748
+ throw scaffoldError(
749
+ "dest_is_symlink",
750
+ `Destination "${destAbs}" is a symlink. Refuse to follow (EC-G safety).`
751
+ );
752
+ }
753
+ if (fs.readdirSync(destAbs).length > 0 && !force) {
754
+ throw scaffoldError(
755
+ "dest_not_empty",
756
+ `Destination "${destAbs}" already exists and is not empty. Pass --force to overwrite.`
757
+ );
758
+ }
759
+ }
760
+ function atomicCopy(sourceAbs, tmpAbs, destAbs, projectName) {
761
+ try {
762
+ fs.mkdirSync(tmpAbs, { recursive: true });
763
+ const filesWritten = copyTreeWithSubstitution(sourceAbs, tmpAbs, {
764
+ projectName,
765
+ sdkVersion: SDK_VERSION
766
+ });
767
+ if (fs.existsSync(destAbs)) fs.rmSync(destAbs, { recursive: true, force: true });
768
+ fs.renameSync(tmpAbs, destAbs);
769
+ return filesWritten;
770
+ } catch (err) {
771
+ try {
772
+ fs.rmSync(tmpAbs, { recursive: true, force: true });
773
+ } catch {
774
+ }
775
+ throw err;
776
+ }
777
+ }
778
+ async function scaffold(opts) {
779
+ validateNameAndTemplate(opts);
780
+ const cwdAbs = path.resolve(opts.cwd);
781
+ const destAbs = path.resolve(cwdAbs, opts.projectName);
782
+ validateDest(cwdAbs, destAbs, opts.projectName, opts.force === true);
783
+ const tmpAbs = `${destAbs}.tmp-${crypto.randomBytes(4).toString("hex")}`;
784
+ const sourceAbs = path.join(resolveTemplatesRoot(), opts.template);
785
+ if (!fs.existsSync(sourceAbs)) {
786
+ throw new Error(
787
+ `Template directory not found: ${sourceAbs}. Ensure the published package included templates/ (EC-C).`
788
+ );
789
+ }
790
+ const filesWritten = atomicCopy(sourceAbs, tmpAbs, destAbs, opts.projectName);
791
+ return { destAbs, filesWritten };
792
+ }
793
+ function copyTreeWithSubstitution(srcDir, destDir, vars) {
794
+ const counter = { count: 0 };
795
+ walk(srcDir, destDir, vars, counter);
796
+ return counter.count;
797
+ }
798
+ function walk(src, dst, vars, counter) {
799
+ fs.mkdirSync(dst, { recursive: true });
800
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
801
+ const srcPath = path.join(src, entry.name);
802
+ const dstPath = path.join(dst, entry.name);
803
+ if (entry.isDirectory()) {
804
+ walk(srcPath, dstPath, vars, counter);
805
+ } else if (entry.isFile()) {
806
+ copyFile(srcPath, dstPath, entry.name, vars);
807
+ counter.count += 1;
808
+ }
809
+ }
810
+ }
811
+ function copyFile(srcPath, dstPath, name, vars) {
812
+ const content = fs.readFileSync(srcPath, "utf8");
813
+ const ext = name.includes(".") ? `.${name.split(".").pop() ?? ""}` : "";
814
+ const shouldSubstitute = SUBSTITUTE_EXTS.has(ext) || name === ".gitignore" || name === ".env.example";
815
+ const out = shouldSubstitute ? content.replaceAll("{{projectName}}", vars.projectName).replaceAll("{{sdkVersion}}", vars.sdkVersion) : content;
816
+ fs.writeFileSync(dstPath, out, "utf8");
817
+ }
818
+
819
+ // src/commands/init.ts
820
+ async function resolveProjectName(projectName, skipPrompts) {
821
+ if (projectName !== void 0 && projectName.length > 0) return projectName;
822
+ if (skipPrompts) {
823
+ process.stderr.write(
824
+ `${pc5__default.default.red("error: ")}project name is required in non-interactive mode. Pass it as the positional argument: ${pc5__default.default.cyan(
825
+ "theokit init <project-name>"
826
+ )}
827
+ `
828
+ );
829
+ return { exitCode: 2 };
830
+ }
831
+ const answer = await p__namespace.text({
832
+ message: "Project name?",
833
+ placeholder: "my-bot",
834
+ validate: (v) => v.length === 0 ? "Required." : void 0
835
+ });
836
+ if (p__namespace.isCancel(answer)) return { exitCode: 0 };
837
+ return answer;
838
+ }
839
+ async function resolveTemplate(optsTemplate, skipPrompts) {
840
+ let template = optsTemplate ?? DEFAULT_TEMPLATE;
841
+ if (optsTemplate === void 0 && !skipPrompts) {
842
+ const answer = await p__namespace.select({
843
+ message: "Pick a template:",
844
+ options: TEMPLATES.map((t) => ({ value: t.name, label: t.name, hint: t.description })),
845
+ initialValue: DEFAULT_TEMPLATE
846
+ });
847
+ if (p__namespace.isCancel(answer)) return { exitCode: 0 };
848
+ template = answer;
849
+ }
850
+ if (findTemplate(template) === void 0) {
851
+ process.stderr.write(
852
+ `${pc5__default.default.red("error: ")}unknown template "${template}". Available: ${TEMPLATES.map((t) => t.name).join(", ")}
853
+ `
854
+ );
855
+ return { exitCode: 2 };
856
+ }
857
+ return template;
858
+ }
859
+ var USER_ERROR_CODES = /* @__PURE__ */ new Set([
860
+ "invalid_project_name",
861
+ "dest_not_empty",
862
+ "invalid_dest",
863
+ "unknown_template"
864
+ ]);
865
+ async function runScaffold(name, template, force) {
866
+ try {
867
+ const result = await scaffold({
868
+ projectName: name,
869
+ template,
870
+ cwd: process.cwd(),
871
+ ...force ? { force: true } : {}
872
+ });
873
+ process.stdout.write(
874
+ `
875
+ ${pc5__default.default.green("\u2713")} Scaffolded ${pc5__default.default.bold(name)} (${template}) \u2014 ${result.filesWritten} files written.
876
+
877
+ Next steps:
878
+ ${pc5__default.default.cyan(`cd ${name}`)}
879
+ ${pc5__default.default.cyan("pnpm install")}
880
+ ${pc5__default.default.cyan("cp .env.example .env")} ${pc5__default.default.gray("# edit your keys")}
881
+ ${pc5__default.default.cyan("pnpm dev")}
882
+
883
+ Template hint: ${findTemplate(template)?.hint ?? ""}
884
+ `
885
+ );
886
+ return 0;
887
+ } catch (err) {
888
+ const message = err instanceof Error ? err.message : String(err);
889
+ const code = err.code ?? "unknown";
890
+ process.stderr.write(`${pc5__default.default.red("error:")} ${message}
891
+ ${pc5__default.default.gray(`(code: ${code})`)}
892
+ `);
893
+ return USER_ERROR_CODES.has(code) ? 2 : 1;
894
+ }
895
+ }
896
+ async function runInit(projectName, opts) {
897
+ const isTty = process.stdin.isTTY === true && process.stdout.isTTY === true;
898
+ const skipPrompts = opts.yes === true || !isTty;
899
+ const name = await resolveProjectName(projectName, skipPrompts);
900
+ if (typeof name !== "string") return name.exitCode;
901
+ const template = await resolveTemplate(opts.template, skipPrompts);
902
+ if (typeof template !== "string") return template.exitCode;
903
+ return runScaffold(name, template, opts.force === true);
904
+ }
905
+ var KNOWN_GATEWAYS = [
906
+ { name: "telegram", packageName: "@theokit/gateway-telegram" },
907
+ { name: "discord", packageName: "@theokit/gateway-discord" }
908
+ ];
909
+ function listGatewayAdapters() {
910
+ const require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
911
+ return KNOWN_GATEWAYS.map((g) => {
912
+ let installed = false;
913
+ try {
914
+ require2.resolve(g.packageName);
915
+ installed = true;
916
+ } catch {
917
+ installed = false;
918
+ }
919
+ return { name: g.name, packageName: g.packageName, installed };
920
+ });
921
+ }
922
+ function parseFrontmatter(content) {
923
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
924
+ if (match === null) return {};
925
+ const out = {};
926
+ for (const line of (match[1] ?? "").split("\n")) {
927
+ const [k, ...rest] = line.split(":");
928
+ if (k === void 0 || rest.length === 0) continue;
929
+ const key = k.trim();
930
+ const val = rest.join(":").trim();
931
+ if (key === "name") out.name = val;
932
+ else if (key === "description") out.description = val;
933
+ }
934
+ return out;
935
+ }
936
+ function loadPluginManifest(root, name, source) {
937
+ const manifestPath = path.join(root, name, "PLUGIN.md");
938
+ if (!fs.existsSync(manifestPath)) return void 0;
939
+ try {
940
+ const fm = parseFrontmatter(fs.readFileSync(manifestPath, "utf8"));
941
+ return {
942
+ name: fm.name ?? name,
943
+ source,
944
+ ...fm.description !== void 0 ? { description: fm.description } : {},
945
+ path: manifestPath
946
+ };
947
+ } catch {
948
+ return void 0;
949
+ }
950
+ }
951
+ function walkPluginDir(root, source) {
952
+ if (!fs.existsSync(root) || !fs.lstatSync(root).isDirectory()) return [];
953
+ const out = [];
954
+ for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
955
+ if (!entry.isDirectory()) continue;
956
+ const info = loadPluginManifest(root, entry.name, source);
957
+ if (info !== void 0) out.push(info);
958
+ }
959
+ return out.sort((a, b) => a.name.localeCompare(b.name));
960
+ }
961
+ function listPlugins(cwd) {
962
+ const userPlugins = walkPluginDir(path.join(os.homedir(), ".theokit", "plugins"), "user-global");
963
+ const projectPlugins = walkPluginDir(path.join(cwd, ".theokit", "plugins"), "project");
964
+ const byName = /* @__PURE__ */ new Map();
965
+ for (const p2 of userPlugins) byName.set(p2.name, p2);
966
+ for (const p2 of projectPlugins) byName.set(p2.name, p2);
967
+ return Array.from(byName.values()).sort((a, b) => a.name.localeCompare(b.name));
968
+ }
969
+
970
+ // src/commands/inspect.ts
971
+ function collect(filter, cwd) {
972
+ const all = filter === void 0 || filter === "all";
973
+ const empty = { providers: [], embeddingAdapters: [], gateways: [], plugins: [] };
974
+ const result = empty;
975
+ if (all || filter === "providers") {
976
+ result.providers = sdk.Theokit.inspect.builtinProviders();
977
+ }
978
+ if (all || filter === "adapters") {
979
+ result.embeddingAdapters = sdk.Theokit.inspect.embeddingAdapters();
980
+ }
981
+ if (all || filter === "gateway") {
982
+ result.gateways = listGatewayAdapters();
983
+ }
984
+ if (all || filter === "plugins") {
985
+ result.plugins = listPlugins(cwd);
986
+ }
987
+ return result;
988
+ }
989
+ function formatProviders(providers) {
990
+ if (providers.length === 0) return [];
991
+ const lines = [pc5__default.default.bold(pc5__default.default.cyan("Builtin LLM providers"))];
992
+ for (const p2 of providers) {
993
+ const aliases = p2.aliases !== void 0 && p2.aliases.length > 0 ? ` (aliases: ${p2.aliases.join(", ")})` : "";
994
+ lines.push(
995
+ ` \u2022 ${pc5__default.default.green(p2.name)}${aliases} \u2014 ${p2.apiMode} \xB7 auth: ${p2.authType} \xB7 ${p2.baseUrl}`
996
+ );
997
+ }
998
+ lines.push("");
999
+ return lines;
1000
+ }
1001
+ function formatEmbeddings(adapters) {
1002
+ if (adapters.length === 0) return [];
1003
+ const lines = [pc5__default.default.bold(pc5__default.default.cyan("Memory embedding adapters"))];
1004
+ for (const a of adapters) {
1005
+ lines.push(` \u2022 ${pc5__default.default.green(a.id)} \u2014 ${a.transport} \xB7 default model: ${a.defaultModel}`);
1006
+ }
1007
+ lines.push("");
1008
+ return lines;
1009
+ }
1010
+ function formatGateways(gateways) {
1011
+ if (gateways.length === 0) return [];
1012
+ const lines = [pc5__default.default.bold(pc5__default.default.cyan("Gateway adapters"))];
1013
+ for (const g of gateways) {
1014
+ const status = g.installed ? pc5__default.default.green("installed") : pc5__default.default.gray("not installed");
1015
+ lines.push(` \u2022 ${pc5__default.default.green(g.name)} (${g.packageName}) \u2014 ${status}`);
1016
+ }
1017
+ lines.push("");
1018
+ return lines;
1019
+ }
1020
+ function formatPlugins(r) {
1021
+ if (r.plugins.length > 0) {
1022
+ const lines = [pc5__default.default.bold(pc5__default.default.cyan("User plugins"))];
1023
+ for (const pl of r.plugins) {
1024
+ const desc = pl.description !== void 0 ? ` \u2014 ${pl.description}` : "";
1025
+ lines.push(` \u2022 ${pc5__default.default.green(pl.name)} [${pl.source}]${desc}`);
1026
+ }
1027
+ return lines;
1028
+ }
1029
+ if (r.providers.length > 0 || r.embeddingAdapters.length > 0) {
1030
+ return [pc5__default.default.gray("(no user plugins found in ~/.theokit/plugins/ or ./.theokit/plugins/)")];
1031
+ }
1032
+ return [];
1033
+ }
1034
+ function formatHuman(r) {
1035
+ const lines = [
1036
+ ...formatProviders(r.providers),
1037
+ ...formatEmbeddings(r.embeddingAdapters),
1038
+ ...formatGateways(r.gateways),
1039
+ ...formatPlugins(r)
1040
+ ];
1041
+ return `${lines.join("\n")}
1042
+ `;
1043
+ }
1044
+ async function runInspect(opts) {
1045
+ const VALID_FILTERS = /* @__PURE__ */ new Set(["all", "providers", "adapters", "gateway", "plugins"]);
1046
+ if (opts.filter !== void 0 && !VALID_FILTERS.has(opts.filter)) {
1047
+ process.stderr.write(
1048
+ `${pc5__default.default.red("error:")} invalid --filter "${opts.filter}". Valid: ${Array.from(VALID_FILTERS).join(", ")}
1049
+ `
1050
+ );
1051
+ return 2;
1052
+ }
1053
+ const result = collect(opts.filter, process.cwd());
1054
+ if (opts.json === true) {
1055
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
1056
+ `);
1057
+ } else {
1058
+ process.stdout.write(formatHuman(result));
1059
+ }
1060
+ return 0;
1061
+ }
1062
+ var UPSTREAM_PACKAGE = "google-workspace-mcp@^2.3.0";
1063
+ var DEFAULT_CONFIG_DIR = path.join(os.homedir(), ".google-mcp");
1064
+ var DEFAULT_CREDS_FILENAME = "credentials.json";
1065
+ var PROBE_TIMEOUT_MS = 1e4;
1066
+ function isPathTraversal(p2) {
1067
+ return p2.includes("../") || p2.includes("..\\") || p2 === ".." || p2.startsWith("../") || p2.startsWith("..\\");
1068
+ }
1069
+ function readAndParseJson(resolved) {
1070
+ let raw;
1071
+ try {
1072
+ raw = fs.readFileSync(resolved, "utf8");
1073
+ } catch (err) {
1074
+ return { kind: "malformed", reason: err instanceof Error ? err.message : String(err) };
1075
+ }
1076
+ try {
1077
+ return { kind: "ok", parsed: JSON.parse(raw) };
1078
+ } catch (err) {
1079
+ return { kind: "malformed", reason: err instanceof Error ? err.message : String(err) };
1080
+ }
1081
+ }
1082
+ function validateOAuthShape(parsed, resolved) {
1083
+ if (parsed === null || typeof parsed !== "object") {
1084
+ return { kind: "malformed", path: resolved, reason: "expected JSON object" };
1085
+ }
1086
+ const obj = parsed;
1087
+ if (obj.installed === void 0 && obj.web !== void 0) {
1088
+ return { kind: "wrong_type", path: resolved };
1089
+ }
1090
+ if (typeof obj.installed !== "object" || obj.installed === null) {
1091
+ return {
1092
+ kind: "malformed",
1093
+ path: resolved,
1094
+ reason: "missing `installed` block (expected Desktop OAuth client shape)"
1095
+ };
1096
+ }
1097
+ return { kind: "ok", path: resolved };
1098
+ }
1099
+ function checkCredentialsFile(credentialsPath) {
1100
+ if (isPathTraversal(credentialsPath)) {
1101
+ return { kind: "rejected_path", path: credentialsPath, reason: "path traversal" };
1102
+ }
1103
+ const resolved = path.resolve(credentialsPath);
1104
+ if (!fs.existsSync(resolved)) return { kind: "missing", path: resolved };
1105
+ const parseResult = readAndParseJson(resolved);
1106
+ if (parseResult.kind === "malformed") {
1107
+ return { kind: "malformed", path: resolved, reason: parseResult.reason };
1108
+ }
1109
+ return validateOAuthShape(parseResult.parsed, resolved);
1110
+ }
1111
+ function describeOutcome(o) {
1112
+ switch (o.kind) {
1113
+ case "missing":
1114
+ return `${pc5__default.default.red("error: ")}credentials.json not found at ${o.path}.
1115
+
1116
+ To create it:
1117
+ 1. Go to https://console.cloud.google.com/apis/credentials
1118
+ 2. Create OAuth 2.0 Client ID \u2014 Application type: ${pc5__default.default.bold("Desktop application")}
1119
+ 3. Download the JSON and save it as ${o.path}
1120
+ `;
1121
+ case "malformed":
1122
+ return `${pc5__default.default.red("error: ")}credentials.json could not be parsed: ${o.reason}
1123
+ path: ${o.path}
1124
+ `;
1125
+ case "wrong_type":
1126
+ return `${pc5__default.default.red("error: ")}This looks like a ${pc5__default.default.bold('"Web application"')} OAuth client.
1127
+
1128
+ Re-create as ${pc5__default.default.bold('"Desktop application"')} in Google Cloud Console:
1129
+ https://console.cloud.google.com/apis/credentials
1130
+ (Click "+ Create credentials" \u2192 "OAuth client ID" \u2192 Application type: ${pc5__default.default.bold("Desktop app")}.)
1131
+
1132
+ Then re-download and save to: ${o.path}
1133
+ `;
1134
+ case "rejected_path":
1135
+ return `${pc5__default.default.red("error: ")}credentials path rejected: ${o.reason}
1136
+ path: ${o.path}
1137
+ `;
1138
+ case "ok":
1139
+ return "";
1140
+ }
1141
+ }
1142
+ function resolveCredentialsPath(opts) {
1143
+ if (opts.credentialsPath !== void 0 && opts.credentialsPath.length > 0) {
1144
+ return path.isAbsolute(opts.credentialsPath) ? opts.credentialsPath : path.resolve(opts.credentialsPath);
1145
+ }
1146
+ return path.join(DEFAULT_CONFIG_DIR, DEFAULT_CREDS_FILENAME);
1147
+ }
1148
+ async function runProbeMode() {
1149
+ process.stdout.write(`
1150
+ ${pc5__default.default.cyan("[probe]")} running upstream connectivity check...
1151
+ `);
1152
+ const code = await spawnUpstream(["status"], PROBE_TIMEOUT_MS);
1153
+ if (code !== 0) {
1154
+ process.stderr.write(
1155
+ `${pc5__default.default.yellow("warn: ")}upstream status reported a non-zero exit (${code}). Run \`npx ${UPSTREAM_PACKAGE} accounts list\` to inspect.
1156
+ `
1157
+ );
1158
+ return code;
1159
+ }
1160
+ return 0;
1161
+ }
1162
+ async function runInteractiveSetup() {
1163
+ process.stdout.write(
1164
+ `
1165
+ ${pc5__default.default.cyan("\u2192")} delegating to upstream installer (${UPSTREAM_PACKAGE})...
1166
+ `
1167
+ );
1168
+ const setupCode = await spawnUpstream(["setup"]);
1169
+ if (setupCode !== 0) {
1170
+ process.stderr.write(`${pc5__default.default.red("error: ")}upstream setup exited with code ${setupCode}.
1171
+ `);
1172
+ return setupCode;
1173
+ }
1174
+ process.stdout.write(`
1175
+ ${pc5__default.default.cyan("\u2192")} adding default account...
1176
+ `);
1177
+ const addCode = await spawnUpstream(["accounts", "add", "default"]);
1178
+ if (addCode !== 0) {
1179
+ process.stderr.write(
1180
+ `${pc5__default.default.red("error: ")}upstream \`accounts add\` exited with code ${addCode}.
1181
+ `
1182
+ );
1183
+ return addCode;
1184
+ }
1185
+ return 0;
1186
+ }
1187
+ async function runGworkspaceSetup(opts) {
1188
+ const credentialsPath = resolveCredentialsPath(opts);
1189
+ try {
1190
+ fs.mkdirSync(DEFAULT_CONFIG_DIR, { recursive: true, mode: 448 });
1191
+ } catch {
1192
+ }
1193
+ const outcome = checkCredentialsFile(credentialsPath);
1194
+ if (outcome.kind !== "ok") {
1195
+ process.stderr.write(describeOutcome(outcome));
1196
+ return 2;
1197
+ }
1198
+ try {
1199
+ fs.chmodSync(outcome.path, 384);
1200
+ } catch {
1201
+ }
1202
+ process.stdout.write(
1203
+ `${pc5__default.default.green("\u2713")} credentials validated: ${outcome.path}
1204
+ ${pc5__default.default.dim(" shape: Desktop OAuth client (installed block present)")}
1205
+ `
1206
+ );
1207
+ if (opts.probe === true) return runProbeMode();
1208
+ if (opts.nonInteractive === true) {
1209
+ process.stdout.write(
1210
+ `${pc5__default.default.dim("non-interactive mode: credentials staged. Next step (manual):")}
1211
+ ${pc5__default.default.cyan(`npx ${UPSTREAM_PACKAGE} accounts add default`)}
1212
+ `
1213
+ );
1214
+ return 0;
1215
+ }
1216
+ const interactiveCode = await runInteractiveSetup();
1217
+ if (interactiveCode !== 0) return interactiveCode;
1218
+ if (typeof opts.writable === "string" && opts.writable.length > 0) {
1219
+ process.stdout.write(
1220
+ `
1221
+ ${pc5__default.default.yellow("note:")} you passed --writable=${opts.writable}. The upstream MCP server does not narrow scopes \u2014 all scopes are granted at consent.
1222
+ Write tools are gated at runtime by ${pc5__default.default.bold("googleWorkspace({ writable: true })")} in your code.
1223
+ `
1224
+ );
1225
+ }
1226
+ process.stdout.write(
1227
+ `
1228
+ ${pc5__default.default.green("\u2713")} gworkspace setup complete.
1229
+ ${pc5__default.default.dim(' Next: import { googleWorkspace } from "@theokit/skills-google-workspace".')}
1230
+ `
1231
+ );
1232
+ return 0;
1233
+ }
1234
+ function spawnUpstream(args, timeoutMs) {
1235
+ return new Promise((resolveExit) => {
1236
+ const child = child_process.spawn("npx", ["-y", UPSTREAM_PACKAGE, ...args], {
1237
+ stdio: "inherit",
1238
+ env: process.env
1239
+ });
1240
+ let timer;
1241
+ if (timeoutMs !== void 0) {
1242
+ timer = setTimeout(() => {
1243
+ process.stderr.write(
1244
+ `${pc5__default.default.yellow("warn: ")}upstream did not finish within ${timeoutMs}ms \u2014 killing.
1245
+ `
1246
+ );
1247
+ try {
1248
+ child.kill("SIGTERM");
1249
+ } catch {
1250
+ }
1251
+ }, timeoutMs);
1252
+ }
1253
+ child.on("error", (err) => {
1254
+ if (timer !== void 0) clearTimeout(timer);
1255
+ process.stderr.write(
1256
+ `${pc5__default.default.red("error: ")}failed to spawn npx: ${err instanceof Error ? err.message : String(err)}
1257
+ `
1258
+ );
1259
+ resolveExit(1);
1260
+ });
1261
+ child.on("exit", (code) => {
1262
+ if (timer !== void 0) clearTimeout(timer);
1263
+ resolveExit(code ?? 1);
1264
+ });
1265
+ });
1266
+ }
1267
+
1268
+ // src/commands/setup.ts
1269
+ async function runSetup(domain, opts) {
1270
+ if (domain === "gworkspace") {
1271
+ const gworkspaceOpts = {
1272
+ writable: opts.writable,
1273
+ probe: opts.probe === true,
1274
+ nonInteractive: opts.nonInteractive === true,
1275
+ ...opts.credentialsPath !== void 0 ? { credentialsPath: opts.credentialsPath } : {}
1276
+ };
1277
+ return await runGworkspaceSetup(gworkspaceOpts);
1278
+ }
1279
+ process.stderr.write(
1280
+ `${pc5__default.default.red("error: ")}unknown setup domain '${domain}'. Supported domains: gworkspace.
1281
+ `
1282
+ );
1283
+ return 2;
1284
+ }
1285
+ var TASK_ID_GRAMMAR = /^[a-z0-9][a-z0-9_-]*$/;
1286
+ function isValidTaskId(id) {
1287
+ return TASK_ID_GRAMMAR.test(id);
1288
+ }
1289
+ function resolveStoreDir() {
1290
+ const fromEnv = process.env.THEOKIT_HOME;
1291
+ if (typeof fromEnv === "string" && fromEnv.length > 0) return path.join(fromEnv, "tasks");
1292
+ return path.join(process.cwd(), ".theokit", "tasks");
1293
+ }
1294
+ function openStore() {
1295
+ const dir = resolveStoreDir();
1296
+ try {
1297
+ return new taskStore.JsonFileTaskStore(dir);
1298
+ } catch (err) {
1299
+ if (err.code === "EACCES") {
1300
+ process.stderr.write(`tasks: cannot access store dir ${dir}: permission denied
1301
+ `);
1302
+ throw new Error("permission_denied");
1303
+ }
1304
+ throw err;
1305
+ }
1306
+ }
1307
+ function formatTable(handles) {
1308
+ if (handles.length === 0) return "No tasks found.";
1309
+ const rows = handles.map((h) => ({
1310
+ id: h.id.slice(0, 24).padEnd(24),
1311
+ kind: h.kind.padEnd(8),
1312
+ state: h.state.padEnd(9),
1313
+ age: `${Math.floor((Date.now() - h.submittedAt) / 1e3)}s`.padStart(6)
1314
+ }));
1315
+ const header = "ID KIND STATE AGE";
1316
+ const lines = rows.map((r) => `${r.id} ${r.kind} ${r.state} ${r.age}`);
1317
+ return [header, ...lines].join("\n");
1318
+ }
1319
+ async function listHandles(store, opts) {
1320
+ const filter = {
1321
+ ...opts.state !== void 0 ? { state: opts.state } : {},
1322
+ ...opts.kind !== void 0 ? { kind: opts.kind } : {}
1323
+ };
1324
+ return store.list(filter);
1325
+ }
1326
+ async function runTasksList(opts) {
1327
+ let store;
1328
+ try {
1329
+ store = openStore();
1330
+ } catch {
1331
+ return 2;
1332
+ }
1333
+ const handles = await listHandles(store, opts);
1334
+ if (opts.json === true) {
1335
+ process.stdout.write(`${JSON.stringify(handles, null, 2)}
1336
+ `);
1337
+ } else {
1338
+ process.stdout.write(`${formatTable(handles)}
1339
+ `);
1340
+ }
1341
+ return 0;
1342
+ }
1343
+ function printHandleDetails(handle) {
1344
+ process.stdout.write(`id: ${handle.id}
1345
+ `);
1346
+ process.stdout.write(`kind: ${handle.kind}
1347
+ `);
1348
+ process.stdout.write(`state: ${handle.state}
1349
+ `);
1350
+ process.stdout.write(`submittedAt: ${new Date(handle.submittedAt).toISOString()}
1351
+ `);
1352
+ if (handle.startedAt !== void 0) {
1353
+ process.stdout.write(`startedAt: ${new Date(handle.startedAt).toISOString()}
1354
+ `);
1355
+ }
1356
+ if (handle.finishedAt !== void 0) {
1357
+ process.stdout.write(`finishedAt: ${new Date(handle.finishedAt).toISOString()}
1358
+ `);
1359
+ process.stdout.write(`result: ${JSON.stringify(handle.result)}
1360
+ `);
1361
+ }
1362
+ if (handle.erroredAt !== void 0) {
1363
+ process.stdout.write(`erroredAt: ${new Date(handle.erroredAt).toISOString()}
1364
+ `);
1365
+ process.stdout.write(`error: ${JSON.stringify(handle.error)}
1366
+ `);
1367
+ }
1368
+ if (handle.cancelledAt !== void 0) {
1369
+ process.stdout.write(`cancelledAt: ${new Date(handle.cancelledAt).toISOString()}
1370
+ `);
1371
+ }
1372
+ if (handle.cancelRequested === true) {
1373
+ process.stdout.write(`cancelRequested: true (awaiting owning process)
1374
+ `);
1375
+ }
1376
+ }
1377
+ async function runTasksInspect(id, opts) {
1378
+ if (!isValidTaskId(id)) {
1379
+ process.stderr.write(`tasks: invalid id grammar: ${id}
1380
+ `);
1381
+ return 3;
1382
+ }
1383
+ let store;
1384
+ try {
1385
+ store = openStore();
1386
+ } catch {
1387
+ return 2;
1388
+ }
1389
+ const handle = await store.get(id);
1390
+ if (handle === void 0) {
1391
+ process.stderr.write(`tasks: not found: ${id}
1392
+ `);
1393
+ return 4;
1394
+ }
1395
+ if (opts.json === true) {
1396
+ process.stdout.write(`${JSON.stringify(handle, null, 2)}
1397
+ `);
1398
+ } else {
1399
+ printHandleDetails(handle);
1400
+ }
1401
+ return 0;
1402
+ }
1403
+ async function runTasksCancel(id, _opts) {
1404
+ if (!isValidTaskId(id)) {
1405
+ process.stderr.write(`tasks: invalid id grammar: ${id}
1406
+ `);
1407
+ return 3;
1408
+ }
1409
+ let store;
1410
+ try {
1411
+ store = openStore();
1412
+ } catch {
1413
+ return 2;
1414
+ }
1415
+ const handle = await store.get(id);
1416
+ if (handle === void 0) {
1417
+ process.stderr.write(`tasks: not found: ${id}
1418
+ `);
1419
+ return 4;
1420
+ }
1421
+ if (handle.state === "finished" || handle.state === "error" || handle.state === "cancelled") {
1422
+ process.stdout.write(`task already terminal (state=${handle.state})
1423
+ `);
1424
+ return 0;
1425
+ }
1426
+ if (handle.state === "queued") {
1427
+ const cancelledAt = Date.now();
1428
+ await store.update(id, (h) => ({ ...h, state: "cancelled", cancelledAt }));
1429
+ process.stdout.write(`task ${id} cancelled (was queued)
1430
+ `);
1431
+ return 0;
1432
+ }
1433
+ await store.update(id, (h) => ({ ...h, cancelRequested: true }));
1434
+ process.stdout.write(
1435
+ `cancel requested for task ${id}; the owning process will honor it at the next checkpoint
1436
+ `
1437
+ );
1438
+ return 0;
1439
+ }
1440
+
1441
+ // src/main.ts
1442
+ function registerSubcommands(program, setExit) {
1443
+ program.command("init [project-name]").description("Scaffold a new agent project from a bundled template.").option("-t, --template <name>", "Template name: minimal | ollama-local | telegram-bot").option("-f, --force", "Overwrite a non-empty destination directory").option("--here", "Scaffold into the current directory").option("-y, --yes", "Skip interactive prompts (CI mode)").action(async (projectName, opts) => {
1444
+ setExit(await runInit(projectName, opts));
1445
+ });
1446
+ program.command("dev").description("Run the agent entry point under tsx --watch (hot-reload).").option("--entry <path>", "Entry file (default: src/index.ts or package.main)").option("--env <path>", "Env file to load (default: .env)").action(async (opts) => {
1447
+ setExit(await runDev(opts));
1448
+ });
1449
+ program.command("inspect").description("List builtin providers, embedding adapters, gateways, and plugins.").option("--json", "Emit machine-readable JSON instead of the human tree view").option(
1450
+ "--filter <kind>",
1451
+ "Narrow output to one kind: providers | adapters | gateway | plugins"
1452
+ ).action(async (opts) => {
1453
+ setExit(await runInspect(opts));
1454
+ });
1455
+ program.command("eval").description("Run an eval suite against a real LLM and emit a markdown report.").option("-c, --config <path>", "Eval config file (default: ./eval.config.ts)").option("-o, --output <path>", "Report output path (default: ./eval-report.md)").action(async (opts) => {
1456
+ setExit(await runEval(opts));
1457
+ });
1458
+ program.command("acp").description(
1459
+ "Launch a stdio Agent Client Protocol (ACP) server pointing at the entry file's default-exported agent. Used by Zed/Cursor/Claude Desktop. ADRs D349-D360."
1460
+ ).option("--entry <path>", "Entry file (default: src/index.ts or package.main)").option("--permission <mode>", "Tool permission mode: ask | auto | deny (default: ask)").option("--trusted-tools <list>", "Comma-separated tool names that bypass ask").option("--permission-timeout-ms <ms>", "Permission request timeout in ms (default: 60000)").action(async (opts) => {
1461
+ setExit(await runAcp(opts));
1462
+ });
1463
+ program.command("setup <domain>").description(
1464
+ "Stage credentials + connectivity probe for a third-party integration. Domains: gworkspace (Google Workspace)."
1465
+ ).option(
1466
+ "--writable <products>",
1467
+ "Comma-separated products to grant write access (e.g., 'drive,calendar')"
1468
+ ).option("--probe", "Run upstream connectivity check after staging credentials").option(
1469
+ "--credentials-path <path>",
1470
+ "Override path to credentials.json (default: ~/.google-mcp/credentials.json)"
1471
+ ).option("--non-interactive", "Refuse interactive prompts; suitable for CI").action(async (domain, opts) => {
1472
+ setExit(await runSetup(domain, opts));
1473
+ });
1474
+ const db = program.command("db").description(
1475
+ "Database tooling \u2014 wraps drizzle-kit (generate/migrate/studio/push) and emits polyglot JSON Schema 7 (export-schema/check-schema-drift). Consumes orm.config.ts that default-exports { schema }."
1476
+ );
1477
+ db.command("generate").description("drizzle-kit generate \u2014 generate SQL migrations from your schema diff").action(() => {
1478
+ setExit(runDbGenerate());
1479
+ });
1480
+ db.command("migrate").description("drizzle-kit migrate \u2014 apply pending migrations to the database").action(() => {
1481
+ setExit(runDbMigrate());
1482
+ });
1483
+ db.command("studio").description("drizzle-kit studio \u2014 launch the embedded data browser UI").action(() => {
1484
+ setExit(runDbStudio());
1485
+ });
1486
+ db.command("push").description("drizzle-kit push \u2014 direct schema sync (dangerous in prod; prototypes only)").action(() => {
1487
+ setExit(runDbPush());
1488
+ });
1489
+ db.command("export-schema").description(
1490
+ "Emit JSON Schema 7 per entity to .theokit/schema/{entity}.schema.json (polyglot consumers)."
1491
+ ).option("-o, --out <dir>", "Output directory (default: .theokit/schema)").option("-c, --config <path>", "Path to orm.config (default: orm.config.ts)").action(async (opts) => {
1492
+ setExit(await runDbExportSchema(opts));
1493
+ });
1494
+ db.command("check-schema-drift").description("Re-emit schemas and diff against committed copies. Exit 1 on drift.").option("-o, --out <dir>", "Output directory (default: .theokit/schema)").option("-c, --config <path>", "Path to orm.config (default: orm.config.ts)").action(async (opts) => {
1495
+ setExit(await runDbCheckSchemaDrift(opts));
1496
+ });
1497
+ const tasks = program.command("tasks").description("Observe SDK Task registry (list / inspect / cancel)");
1498
+ tasks.command("list").description("List tasks in the local JsonFileTaskStore").option("--state <state>", "Filter by state (queued|running|finished|error|cancelled)").option("--kind <kind>", "Filter by kind (run|batch|workflow|cron|custom)").option("--json", "Emit machine-readable JSON instead of the table view").action(async (opts) => {
1499
+ setExit(await runTasksList(opts));
1500
+ });
1501
+ tasks.command("inspect <id>").description("Inspect a single task by id").option("--json", "Emit machine-readable JSON").action(async (id, opts) => {
1502
+ setExit(await runTasksInspect(id, opts));
1503
+ });
1504
+ tasks.command("cancel <id>").description("Cancel a task (best-effort cross-process via cancelRequested flag)").option("--reason <reason>", "Cancellation reason recorded in the registry").action(async (id, opts) => {
1505
+ setExit(await runTasksCancel(id));
1506
+ });
1507
+ }
1508
+ function mapCommanderExitCode(code, fallback) {
1509
+ if (code === "commander.help" || code === "commander.helpDisplayed") return 0;
1510
+ if (code === "commander.version") return 0;
1511
+ if (code === "commander.unknownCommand" || code === "commander.unknownOption") return 2;
1512
+ return fallback > 0 ? fallback : 2;
1513
+ }
1514
+ async function main(argv) {
1515
+ const program = new commander.Command();
1516
+ program.name("theokit").description("Developer CLI for @theokit/sdk \u2014 init, dev, inspect, eval.").version(CLI_VERSION, "-v, --version", "Print the CLI version and exit.").addHelpText(
1517
+ "after",
1518
+ `
1519
+ Bundled SDK version: ${SDK_VERSION}
1520
+ Adoption Roadmap #1. See https://github.com/usetheo/theokit-sdk for docs.
1521
+
1522
+ Exit codes: 0=success \xB7 1=unknown error \xB7 2=user error.
1523
+ `
1524
+ );
1525
+ let exitCode = 0;
1526
+ const setExit = (code) => {
1527
+ exitCode = code;
1528
+ };
1529
+ registerSubcommands(program, setExit);
1530
+ let commanderHandledIt = false;
1531
+ program.exitOverride((err) => {
1532
+ commanderHandledIt = true;
1533
+ exitCode = mapCommanderExitCode(err.code, err.exitCode);
1534
+ });
1535
+ try {
1536
+ await program.parseAsync(argv);
1537
+ } catch (err) {
1538
+ if (commanderHandledIt) {
1539
+ return exitCode;
1540
+ }
1541
+ process.stderr.write(`theokit: ${err instanceof Error ? err.message : String(err)}
1542
+ `);
1543
+ return 1;
1544
+ }
1545
+ return exitCode;
1546
+ }
1547
+
1548
+ exports.CLI_VERSION = CLI_VERSION;
1549
+ exports.SDK_VERSION = SDK_VERSION;
1550
+ exports.main = main;
1551
+ //# sourceMappingURL=index.cjs.map
1552
+ //# sourceMappingURL=index.cjs.map