@ekairos/domain 1.22.81-beta.development.0 → 1.22.83-beta.development.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,12 +5,15 @@ import { dirname, join, relative, resolve } from "node:path";
5
5
  import { PlatformApi } from "@instantdb/platform";
6
6
  import { i } from "@instantdb/core";
7
7
  import { domain } from "../index.js";
8
- const TEMPLATE_NEXT_VERSION = "15.5.7";
9
- const TEMPLATE_REACT_VERSION = "19.2.1";
10
- const TEMPLATE_TYPESCRIPT_VERSION = "^5.9.2";
11
- const TEMPLATE_INSTANT_VERSION = "0.22.126";
12
- const TEMPLATE_INSTANT_REACT_VERSION = "0.22.126";
13
- const TEMPLATE_WORKFLOW_VERSION = "^5.0.0-beta.1";
8
+ export const CREATE_DOMAIN_APP_TEMPLATES = ["empty", "supply-chain", "agent"];
9
+ const TEMPLATE_NEXT_VERSION = "16.2.6";
10
+ const TEMPLATE_REACT_VERSION = "19.2.6";
11
+ const TEMPLATE_TYPESCRIPT_VERSION = "^6.0.3";
12
+ const TEMPLATE_INSTANT_VERSION = "1.0.39";
13
+ const TEMPLATE_INSTANT_REACT_VERSION = "1.0.39";
14
+ const TEMPLATE_WORKFLOW_VERSION = "5.0.0-beta.6";
15
+ const TEMPLATE_VERCEL_OIDC_VERSION = "3.4.1";
16
+ const TEMPLATE_EVENTS_VERSION = "1.22.82-beta.development.0";
14
17
  function trimOrEmpty(value) {
15
18
  return typeof value === "string" ? value.trim() : "";
16
19
  }
@@ -69,8 +72,30 @@ async function readDomainPackageVersion() {
69
72
  const parsed = JSON.parse(raw);
70
73
  return trimOrEmpty(parsed.version) || "latest";
71
74
  }
72
- function createScaffoldSchema() {
73
- return createSupplyChainScaffoldSchema();
75
+ export function normalizeCreateAppTemplate(value) {
76
+ const normalized = trimOrEmpty(value);
77
+ if (!normalized)
78
+ return null;
79
+ if (CREATE_DOMAIN_APP_TEMPLATES.includes(normalized)) {
80
+ return normalized;
81
+ }
82
+ throw new Error(`Unsupported template: ${normalized}. Supported templates: ${CREATE_DOMAIN_APP_TEMPLATES.join(", ")}.`);
83
+ }
84
+ function resolveCreateAppTemplate(params) {
85
+ if (params.template) {
86
+ if (params.demo && params.template !== "supply-chain") {
87
+ throw new Error("--demo only supports --template=supply-chain.");
88
+ }
89
+ return params.template;
90
+ }
91
+ return params.demo ? "supply-chain" : "empty";
92
+ }
93
+ function createScaffoldSchema(template) {
94
+ if (template === "supply-chain")
95
+ return createSupplyChainScaffoldSchema();
96
+ if (template === "agent")
97
+ return createAgentScaffoldSchema();
98
+ return createEmptyScaffoldSchema();
74
99
  }
75
100
  function createEmptyScaffoldSchema() {
76
101
  const scaffoldDomain = domain("app").withSchema({
@@ -178,8 +203,189 @@ function createSupplyChainScaffoldSchema() {
178
203
  .withSchema({ entities: {}, links: {}, rooms: {} });
179
204
  return scaffoldDomain.toInstantSchema();
180
205
  }
181
- function createScaffoldPerms() {
182
- return createSupplyChainScaffoldPerms();
206
+ function createAgentScaffoldSchema() {
207
+ return domain("events")
208
+ .withSchema({
209
+ entities: {
210
+ event_contexts: i.entity({
211
+ createdAt: i.date(),
212
+ updatedAt: i.date().optional(),
213
+ key: i.string().optional().indexed().unique(),
214
+ name: i.string().optional(),
215
+ status: i.string().optional().indexed(),
216
+ content: i.any().optional(),
217
+ reactor: i.json().optional(),
218
+ }),
219
+ event_items: i.entity({
220
+ channel: i.string().indexed(),
221
+ createdAt: i.date().indexed(),
222
+ type: i.string().optional().indexed(),
223
+ content: i.any().optional(),
224
+ status: i.string().optional().indexed(),
225
+ }),
226
+ event_executions: i.entity({
227
+ createdAt: i.date(),
228
+ updatedAt: i.date().optional(),
229
+ status: i.string().optional().indexed(),
230
+ workflowRunId: i.string().optional().indexed(),
231
+ activeStreamId: i.string().optional().indexed(),
232
+ activeStreamClientId: i.string().optional().indexed(),
233
+ lastStreamId: i.string().optional().indexed(),
234
+ lastStreamClientId: i.string().optional().indexed(),
235
+ }),
236
+ event_steps: i.entity({
237
+ createdAt: i.date().indexed(),
238
+ updatedAt: i.date().optional(),
239
+ status: i.string().optional().indexed(),
240
+ iteration: i.number().indexed(),
241
+ errorText: i.string().optional(),
242
+ streamId: i.string().optional().indexed(),
243
+ streamClientId: i.string().optional().indexed(),
244
+ streamStartedAt: i.date().optional().indexed(),
245
+ streamFinishedAt: i.date().optional().indexed(),
246
+ streamAbortReason: i.string().optional(),
247
+ }),
248
+ event_parts: i.entity({
249
+ key: i.string().unique().indexed(),
250
+ stepId: i.string().indexed(),
251
+ idx: i.number().indexed(),
252
+ type: i.string().optional().indexed(),
253
+ part: i.json().optional(),
254
+ metadata: i.json().optional(),
255
+ updatedAt: i.date().optional(),
256
+ }),
257
+ event_trace_events: i.entity({
258
+ key: i.string().unique().indexed(),
259
+ workflowRunId: i.string().indexed(),
260
+ seq: i.number().indexed(),
261
+ eventId: i.string().indexed(),
262
+ eventKind: i.string().indexed(),
263
+ eventAt: i.date().optional(),
264
+ ingestedAt: i.date().optional(),
265
+ orgId: i.string().optional().indexed(),
266
+ projectId: i.string().optional().indexed(),
267
+ contextKey: i.string().optional().indexed(),
268
+ contextId: i.string().optional().indexed(),
269
+ executionId: i.string().optional().indexed(),
270
+ stepId: i.string().optional().indexed(),
271
+ contextEventId: i.string().optional().indexed(),
272
+ toolCallId: i.string().optional().indexed(),
273
+ partKey: i.string().optional().indexed(),
274
+ partIdx: i.number().optional().indexed(),
275
+ spanId: i.string().optional().indexed(),
276
+ parentSpanId: i.string().optional().indexed(),
277
+ isDeleted: i.boolean().optional(),
278
+ aiProvider: i.string().optional().indexed(),
279
+ aiModel: i.string().optional().indexed(),
280
+ promptTokens: i.number().optional(),
281
+ promptTokensCached: i.number().optional(),
282
+ promptTokensUncached: i.number().optional(),
283
+ completionTokens: i.number().optional(),
284
+ totalTokens: i.number().optional(),
285
+ latencyMs: i.number().optional(),
286
+ cacheCostUsd: i.number().optional(),
287
+ computeCostUsd: i.number().optional(),
288
+ costUsd: i.number().optional(),
289
+ payload: i.any().optional(),
290
+ }),
291
+ event_trace_runs: i.entity({
292
+ workflowRunId: i.string().unique().indexed(),
293
+ orgId: i.string().optional().indexed(),
294
+ projectId: i.string().optional().indexed(),
295
+ firstEventAt: i.date().optional().indexed(),
296
+ lastEventAt: i.date().optional().indexed(),
297
+ lastIngestedAt: i.date().optional().indexed(),
298
+ eventsCount: i.number().optional(),
299
+ status: i.string().optional().indexed(),
300
+ payload: i.any().optional(),
301
+ }),
302
+ event_trace_spans: i.entity({
303
+ spanId: i.string().unique().indexed(),
304
+ parentSpanId: i.string().optional().indexed(),
305
+ workflowRunId: i.string().indexed(),
306
+ executionId: i.string().optional().indexed(),
307
+ stepId: i.string().optional().indexed(),
308
+ kind: i.string().optional().indexed(),
309
+ name: i.string().optional().indexed(),
310
+ status: i.string().optional().indexed(),
311
+ startedAt: i.date().optional().indexed(),
312
+ endedAt: i.date().optional().indexed(),
313
+ durationMs: i.number().optional(),
314
+ payload: i.any().optional(),
315
+ }),
316
+ document_documents: i.entity({
317
+ name: i.string().optional().indexed(),
318
+ mimeType: i.string().optional(),
319
+ size: i.number().optional(),
320
+ ownerId: i.string().optional().indexed(),
321
+ orgId: i.string().optional().indexed(),
322
+ createdAt: i.date().optional().indexed(),
323
+ processedAt: i.date().optional().indexed(),
324
+ updatedAt: i.date().optional(),
325
+ lastJobId: i.string().optional(),
326
+ content: i.json().optional(),
327
+ }),
328
+ },
329
+ links: {
330
+ contextItemsContext: {
331
+ forward: { on: "event_items", has: "one", label: "context" },
332
+ reverse: { on: "event_contexts", has: "many", label: "items" },
333
+ },
334
+ contextExecutionsContext: {
335
+ forward: { on: "event_executions", has: "one", label: "context" },
336
+ reverse: { on: "event_contexts", has: "many", label: "executions" },
337
+ },
338
+ contextCurrentExecution: {
339
+ forward: { on: "event_contexts", has: "one", label: "currentExecution" },
340
+ reverse: { on: "event_executions", has: "one", label: "currentOf" },
341
+ },
342
+ contextExecutionsTrigger: {
343
+ forward: { on: "event_executions", has: "one", label: "trigger" },
344
+ reverse: { on: "event_items", has: "many", label: "executionsAsTrigger" },
345
+ },
346
+ contextExecutionsReaction: {
347
+ forward: { on: "event_executions", has: "one", label: "reaction" },
348
+ reverse: { on: "event_items", has: "many", label: "executionsAsReaction" },
349
+ },
350
+ contextStepsExecution: {
351
+ forward: { on: "event_steps", has: "one", label: "execution" },
352
+ reverse: { on: "event_executions", has: "many", label: "steps" },
353
+ },
354
+ contextExecutionItems: {
355
+ forward: { on: "event_items", has: "one", label: "execution" },
356
+ reverse: { on: "event_executions", has: "many", label: "items" },
357
+ },
358
+ contextPartsStep: {
359
+ forward: { on: "event_parts", has: "one", label: "step" },
360
+ reverse: { on: "event_steps", has: "many", label: "parts" },
361
+ },
362
+ contextStepStream: {
363
+ forward: { on: "event_steps", has: "one", label: "stream" },
364
+ reverse: { on: "$streams", has: "many", label: "step" },
365
+ },
366
+ contextExecutionActiveStream: {
367
+ forward: { on: "event_executions", has: "one", label: "activeStream" },
368
+ reverse: { on: "$streams", has: "many", label: "activeOf" },
369
+ },
370
+ contextExecutionLastStream: {
371
+ forward: { on: "event_executions", has: "one", label: "lastStream" },
372
+ reverse: { on: "$streams", has: "many", label: "lastOf" },
373
+ },
374
+ documentFile: {
375
+ forward: { on: "document_documents", has: "one", label: "file" },
376
+ reverse: { on: "$files", has: "one", label: "document" },
377
+ },
378
+ },
379
+ rooms: {},
380
+ })
381
+ .toInstantSchema();
382
+ }
383
+ function createScaffoldPerms(template) {
384
+ if (template === "supply-chain")
385
+ return createSupplyChainScaffoldPerms();
386
+ if (template === "agent")
387
+ return createAgentScaffoldPerms();
388
+ return createEmptyScaffoldPerms();
183
389
  }
184
390
  function createEmptyScaffoldPerms() {
185
391
  return {
@@ -209,6 +415,30 @@ function createSupplyChainScaffoldPerms() {
209
415
  qualityControl_inspection: entityRules,
210
416
  };
211
417
  }
418
+ function createAgentScaffoldPerms() {
419
+ const entityRules = {
420
+ allow: {
421
+ view: "true",
422
+ create: "true",
423
+ update: "true",
424
+ delete: "false",
425
+ },
426
+ };
427
+ return {
428
+ attrs: {
429
+ allow: { create: "true" },
430
+ },
431
+ event_contexts: entityRules,
432
+ event_items: entityRules,
433
+ event_executions: entityRules,
434
+ event_steps: entityRules,
435
+ event_parts: entityRules,
436
+ event_trace_events: entityRules,
437
+ event_trace_runs: entityRules,
438
+ event_trace_spans: entityRules,
439
+ document_documents: entityRules,
440
+ };
441
+ }
212
442
  function installCommandFor(packageManager) {
213
443
  if (packageManager === "pnpm")
214
444
  return "pnpm install";
@@ -556,8 +786,8 @@ async function provisionInstantApp(params) {
556
786
  const created = await api.createApp({
557
787
  title: `ekairos-${trimOrEmpty(params.directory.split(/[\\/]/).pop()) || "app"}`,
558
788
  orgId: trimOrEmpty(params.orgId) || undefined,
559
- schema: createScaffoldSchema(),
560
- perms: createScaffoldPerms(),
789
+ schema: createScaffoldSchema(params.template),
790
+ perms: createScaffoldPerms(params.template),
561
791
  });
562
792
  const appId = trimOrEmpty(created?.app?.id);
563
793
  const adminToken = trimOrEmpty(created?.app?.adminToken);
@@ -635,20 +865,27 @@ async function writeScaffoldFiles(targetDir, files) {
635
865
  await writeFile(absolutePath, content, "utf8");
636
866
  }
637
867
  }
638
- function resolveDomainDependencyVersion(version, targetDir, workspacePath) {
868
+ function resolveWorkspacePackageDependencyVersion(packageName, version, targetDir, workspacePath) {
639
869
  const workspaceRoot = trimOrEmpty(workspacePath);
640
870
  if (!workspaceRoot) {
641
871
  return version;
642
872
  }
643
- const packageRoot = resolve(workspaceRoot, "packages/domain");
873
+ const packageRoot = resolve(workspaceRoot, "packages", packageName.split("/").pop() || packageName);
644
874
  const relativePath = toPosix(relative(targetDir, packageRoot));
645
875
  if (!relativePath)
646
876
  return "file:.";
647
877
  const prefixed = relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
648
878
  return `file:${prefixed}`;
649
879
  }
880
+ function resolveDomainDependencyVersion(version, targetDir, workspacePath) {
881
+ return resolveWorkspacePackageDependencyVersion("@ekairos/domain", version, targetDir, workspacePath);
882
+ }
650
883
  function buildNextTemplateFiles(params) {
651
- const domainDependency = resolveDomainDependencyVersion(params.domainVersion, params.targetDir, params.workspacePath);
884
+ const workspaceRoot = trimOrEmpty(params.workspacePath);
885
+ const domainDependency = params.template === "agent" && !workspaceRoot
886
+ ? TEMPLATE_EVENTS_VERSION
887
+ : resolveDomainDependencyVersion(params.domainVersion, params.targetDir, params.workspacePath);
888
+ const eventsDependency = resolveWorkspacePackageDependencyVersion("@ekairos/events", TEMPLATE_EVENTS_VERSION, params.targetDir, params.workspacePath);
652
889
  const packageJson = {
653
890
  name: trimOrEmpty(params.targetDir.split(/[\\/]/).pop()) || "ekairos-app",
654
891
  private: true,
@@ -662,6 +899,7 @@ function buildNextTemplateFiles(params) {
662
899
  },
663
900
  dependencies: {
664
901
  "@ekairos/domain": domainDependency,
902
+ "@vercel/oidc": TEMPLATE_VERCEL_OIDC_VERSION,
665
903
  "@instantdb/admin": TEMPLATE_INSTANT_VERSION,
666
904
  "@instantdb/core": TEMPLATE_INSTANT_VERSION,
667
905
  "@instantdb/react": TEMPLATE_INSTANT_REACT_VERSION,
@@ -669,6 +907,7 @@ function buildNextTemplateFiles(params) {
669
907
  react: TEMPLATE_REACT_VERSION,
670
908
  "react-dom": TEMPLATE_REACT_VERSION,
671
909
  workflow: TEMPLATE_WORKFLOW_VERSION,
910
+ zod: "^4.3.6",
672
911
  },
673
912
  devDependencies: {
674
913
  "@types/node": "^24.5.0",
@@ -682,7 +921,21 @@ function buildNextTemplateFiles(params) {
682
921
  ? "yarn@1"
683
922
  : undefined,
684
923
  };
685
- if (!params.demo) {
924
+ if (params.template === "agent") {
925
+ ;
926
+ packageJson.dependencies["@ekairos/events"] = eventsDependency;
927
+ if (params.packageManager === "pnpm") {
928
+ ;
929
+ packageJson.pnpm = {
930
+ overrides: {
931
+ "@ekairos/domain": domainDependency,
932
+ "@instantdb/admin": TEMPLATE_INSTANT_VERSION,
933
+ "@instantdb/core": TEMPLATE_INSTANT_VERSION,
934
+ },
935
+ };
936
+ }
937
+ }
938
+ if (params.template === "empty") {
686
939
  return {
687
940
  ".gitignore": [".next", "node_modules", ".env.local", ".workflow-data"].join("\n"),
688
941
  ".env.example": [
@@ -750,14 +1003,16 @@ function buildNextTemplateFiles(params) {
750
1003
  ' "moduleResolution": "bundler",',
751
1004
  ' "resolveJsonModule": true,',
752
1005
  ' "isolatedModules": true,',
753
- ' "jsx": "preserve",',
1006
+ ' "jsx": "react-jsx",',
754
1007
  ' "incremental": true,',
755
- ' "baseUrl": ".",',
756
1008
  ' "paths": {',
757
1009
  ' "@/*": ["./src/*"]',
758
- " }",
1010
+ " },",
1011
+ ' "plugins": [',
1012
+ ' { "name": "next" }',
1013
+ " ]",
759
1014
  " },",
760
- ' "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],',
1015
+ ' "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", ".next/dev/types/**/*.ts"],',
761
1016
  ' "exclude": ["node_modules"]',
762
1017
  "}",
763
1018
  ].join("\n"),
@@ -919,6 +1174,398 @@ function buildNextTemplateFiles(params) {
919
1174
  ].join("\n"),
920
1175
  };
921
1176
  }
1177
+ if (params.template === "agent") {
1178
+ return {
1179
+ ".gitignore": [".next", "node_modules", ".env.local", ".workflow-data"].join("\n"),
1180
+ ".env.example": [
1181
+ "NEXT_PUBLIC_INSTANT_APP_ID=",
1182
+ "INSTANT_ADMIN_TOKEN=",
1183
+ "",
1184
+ "# Optional: use this only while provisioning new apps with the CLI.",
1185
+ "INSTANT_PERSONAL_ACCESS_TOKEN=",
1186
+ ].join("\n"),
1187
+ "DOMAIN.md": [
1188
+ "# Ekairos Agent Template",
1189
+ "",
1190
+ "This app is the minimal loop for Ekairos event contexts:",
1191
+ "",
1192
+ "1. create the app",
1193
+ "2. start an agent context through `/api/agent/react`",
1194
+ "3. open the Instant app in Workbench v2",
1195
+ "4. iterate on `src/agent.ts`",
1196
+ "",
1197
+ "The scaffold uses the canonical `@ekairos/events` schema so Workbench v2 can inspect `event_contexts`.",
1198
+ "It includes an Ekairos registry alias so domain UI components can be installed with shadcn and use `@ekairos/events` directly.",
1199
+ ].join("\n"),
1200
+ "components.json": [
1201
+ "{",
1202
+ ' "$schema": "https://ui.shadcn.com/schema.json",',
1203
+ ' "style": "default",',
1204
+ ' "rsc": false,',
1205
+ ' "tsx": true,',
1206
+ ' "tailwind": {',
1207
+ ' "config": "",',
1208
+ ' "css": "src/app/globals.css",',
1209
+ ' "baseColor": "zinc",',
1210
+ ' "cssVariables": true,',
1211
+ ' "prefix": ""',
1212
+ " },",
1213
+ ' "iconLibrary": "lucide",',
1214
+ ' "aliases": {',
1215
+ ' "components": "@/components",',
1216
+ ' "utils": "@/lib/utils",',
1217
+ ' "ui": "@/components/ui",',
1218
+ ' "lib": "@/lib",',
1219
+ ' "hooks": "@/hooks"',
1220
+ " },",
1221
+ ' "registries": {',
1222
+ ' "@ekairos": "https://registry.ekairos.dev/r/{name}.json"',
1223
+ " }",
1224
+ "}",
1225
+ ].join("\n"),
1226
+ "instant.schema.ts": [
1227
+ 'import { eventsDomain } from "@ekairos/events/schema";',
1228
+ "",
1229
+ "const schema = eventsDomain.toInstantSchema();",
1230
+ "",
1231
+ "export default schema;",
1232
+ ].join("\n"),
1233
+ "next-env.d.ts": [
1234
+ '/// <reference types="next" />',
1235
+ '/// <reference types="next/image-types/global" />',
1236
+ "",
1237
+ "// This file is managed by Next.js.",
1238
+ ].join("\n"),
1239
+ "next.config.ts": [
1240
+ 'import type { NextConfig } from "next";',
1241
+ 'import { withWorkflow } from "workflow/next";',
1242
+ "",
1243
+ "const nextConfig: NextConfig = {",
1244
+ " transpilePackages: [\"@ekairos/domain\", \"@ekairos/events\"],",
1245
+ "};",
1246
+ "",
1247
+ "export default withWorkflow(nextConfig) as NextConfig;",
1248
+ ].join("\n"),
1249
+ "package.json": `${JSON.stringify(packageJson, null, 2)}\n`,
1250
+ "tsconfig.json": [
1251
+ "{",
1252
+ ' "compilerOptions": {',
1253
+ ' "target": "ES2022",',
1254
+ ' "lib": ["dom", "dom.iterable", "es2022"],',
1255
+ ' "allowJs": false,',
1256
+ ' "skipLibCheck": true,',
1257
+ ' "strict": true,',
1258
+ ' "noEmit": true,',
1259
+ ' "esModuleInterop": true,',
1260
+ ' "module": "esnext",',
1261
+ ' "moduleResolution": "bundler",',
1262
+ ' "resolveJsonModule": true,',
1263
+ ' "isolatedModules": true,',
1264
+ ' "jsx": "react-jsx",',
1265
+ ' "incremental": true,',
1266
+ ' "paths": {',
1267
+ ' "@/*": ["./src/*"]',
1268
+ " },",
1269
+ ' "plugins": [',
1270
+ ' { "name": "next" }',
1271
+ " ]",
1272
+ " },",
1273
+ ' "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", ".next/dev/types/**/*.ts"],',
1274
+ ' "exclude": ["node_modules"]',
1275
+ "}",
1276
+ ].join("\n"),
1277
+ "src/app/globals.css": [
1278
+ ":root {",
1279
+ " color-scheme: light;",
1280
+ " --background: #f7f8f6;",
1281
+ " --foreground: #171a18;",
1282
+ " --muted: #626a63;",
1283
+ " --border: #d9ded7;",
1284
+ " --surface: #ffffff;",
1285
+ " --accent: #26715f;",
1286
+ "}",
1287
+ "* { box-sizing: border-box; }",
1288
+ "html, body { margin: 0; min-height: 100%; }",
1289
+ "body { min-height: 100dvh; background: var(--background); color: var(--foreground); font-family: \"Segoe UI\", sans-serif; }",
1290
+ "button, textarea, input { font: inherit; }",
1291
+ "main { width: min(980px, calc(100% - 32px)); margin: 0 auto; padding: 44px 0; }",
1292
+ ".shell { display: grid; gap: 22px; }",
1293
+ ".eyebrow { color: var(--accent); font-size: 12px; font-weight: 800; letter-spacing: 0.14em; text-transform: uppercase; }",
1294
+ "h1 { max-width: 760px; margin: 0; font-size: clamp(2.1rem, 5vw, 4.4rem); line-height: 1; }",
1295
+ "p { max-width: 68ch; margin: 0; color: var(--muted); line-height: 1.65; }",
1296
+ ".panel { display: grid; gap: 16px; border: 1px solid var(--border); background: var(--surface); padding: 20px; }",
1297
+ ".grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); border: 1px solid var(--border); }",
1298
+ ".grid div { display: grid; gap: 6px; padding: 14px; border-right: 1px solid var(--border); }",
1299
+ ".grid div:last-child { border-right: 0; }",
1300
+ ".grid span { color: var(--muted); font-size: 11px; font-weight: 800; letter-spacing: 0.12em; text-transform: uppercase; }",
1301
+ ".grid strong { font-size: 1rem; overflow-wrap: anywhere; }",
1302
+ "textarea { width: 100%; min-height: 120px; resize: vertical; border: 1px solid var(--border); padding: 12px; }",
1303
+ "button { width: fit-content; border: 0; background: var(--accent); color: white; padding: 10px 14px; font-weight: 700; cursor: pointer; }",
1304
+ "button:disabled { cursor: wait; opacity: 0.65; }",
1305
+ "pre { overflow: auto; margin: 0; border: 1px solid var(--border); background: #f2f4f1; padding: 14px; }",
1306
+ "code { font-family: \"Cascadia Code\", monospace; }",
1307
+ "@media (max-width: 720px) { .grid { grid-template-columns: 1fr; } .grid div { border-right: 0; border-bottom: 1px solid var(--border); } }",
1308
+ ].join("\n"),
1309
+ "src/app/layout.tsx": [
1310
+ 'import "./globals.css";',
1311
+ 'import type { ReactNode } from "react";',
1312
+ "",
1313
+ "export const metadata = {",
1314
+ ' title: "Ekairos Agent",',
1315
+ ' description: "Scaffolded Ekairos agent context app",',
1316
+ "};",
1317
+ "",
1318
+ "export default function RootLayout({ children }: { children: ReactNode }) {",
1319
+ " return (",
1320
+ ' <html lang="en">',
1321
+ " <body>{children}</body>",
1322
+ " </html>",
1323
+ " );",
1324
+ "}",
1325
+ ].join("\n"),
1326
+ "src/app/page.tsx": [
1327
+ 'import AgentWorkbench from "./agent-workbench";',
1328
+ "",
1329
+ 'export const dynamic = "force-dynamic";',
1330
+ "",
1331
+ "export default function HomePage() {",
1332
+ " return (",
1333
+ " <main>",
1334
+ ' <section className="shell">',
1335
+ ' <div className="eyebrow">Ekairos Agent Template</div>',
1336
+ " <h1>Start a context, inspect it in Workbench v2, iterate.</h1>",
1337
+ " <p>",
1338
+ " This template writes canonical @ekairos/events rows and returns the context id immediately.",
1339
+ " </p>",
1340
+ " <AgentWorkbench />",
1341
+ " </section>",
1342
+ " </main>",
1343
+ " );",
1344
+ "}",
1345
+ ].join("\n"),
1346
+ "src/app/agent-workbench.tsx": [
1347
+ '"use client";',
1348
+ "",
1349
+ "import { useMemo, useState } from \"react\";",
1350
+ "",
1351
+ "type AgentResult = {",
1352
+ " ok: boolean;",
1353
+ " appId?: string;",
1354
+ " contextId?: string;",
1355
+ " contextKey?: string | null;",
1356
+ " triggerEventId?: string;",
1357
+ " error?: string;",
1358
+ "};",
1359
+ "",
1360
+ "export default function AgentWorkbench() {",
1361
+ " const [prompt, setPrompt] = useState(\"Create a deterministic template context.\");",
1362
+ " const [result, setResult] = useState<AgentResult | null>(null);",
1363
+ " const [pending, setPending] = useState(false);",
1364
+ "",
1365
+ " const workbenchPath = useMemo(() => {",
1366
+ " if (!result?.appId || !result?.contextId) return \"\";",
1367
+ " return `/app/${result.appId}/contexts/${result.contextId}`;",
1368
+ " }, [result]);",
1369
+ "",
1370
+ " async function startContext() {",
1371
+ " setPending(true);",
1372
+ " setResult(null);",
1373
+ " try {",
1374
+ " const response = await fetch(\"/api/agent/react\", {",
1375
+ " method: \"POST\",",
1376
+ " headers: { \"content-type\": \"application/json\" },",
1377
+ " body: JSON.stringify({ prompt }),",
1378
+ " });",
1379
+ " const data = (await response.json().catch(() => ({}))) as AgentResult;",
1380
+ " setResult(response.ok ? data : { ok: false, error: data.error || response.statusText });",
1381
+ " } catch (error) {",
1382
+ " setResult({ ok: false, error: error instanceof Error ? error.message : String(error) });",
1383
+ " } finally {",
1384
+ " setPending(false);",
1385
+ " }",
1386
+ " }",
1387
+ "",
1388
+ " return (",
1389
+ ' <section className="panel">',
1390
+ ' <div className="grid">',
1391
+ " <div><span>Schema</span><strong>@ekairos/events</strong></div>",
1392
+ " <div><span>Route</span><strong>/api/agent/react</strong></div>",
1393
+ " <div><span>Mode</span><strong>scripted reactor</strong></div>",
1394
+ " </div>",
1395
+ " <textarea value={prompt} onChange={(event) => setPrompt(event.target.value)} />",
1396
+ " <button type=\"button\" disabled={pending} onClick={startContext}>",
1397
+ " {pending ? \"Starting context...\" : \"Start context\"}",
1398
+ " </button>",
1399
+ " {workbenchPath ? <p>Workbench v2 path: <code>{workbenchPath}</code></p> : null}",
1400
+ " {result ? <pre>{JSON.stringify(result, null, 2)}</pre> : null}",
1401
+ " </section>",
1402
+ " );",
1403
+ "}",
1404
+ ].join("\n"),
1405
+ "src/app/api/agent/react/route.ts": [
1406
+ 'import { NextRequest, NextResponse } from "next/server";',
1407
+ 'import { agentContext, createTriggerEvent } from "@/agent";',
1408
+ 'import { createRuntime } from "@/runtime";',
1409
+ "",
1410
+ "export const maxDuration = 60;",
1411
+ "",
1412
+ "function readTrimmed(value: unknown) {",
1413
+ " return typeof value === \"string\" ? value.trim() : \"\";",
1414
+ "}",
1415
+ "",
1416
+ "export async function POST(req: NextRequest) {",
1417
+ " try {",
1418
+ " const body = await req.json().catch(() => ({}));",
1419
+ " const prompt = readTrimmed((body as any).prompt) || \"Create a deterministic template context.\";",
1420
+ " const contextKey = readTrimmed((body as any).contextKey) || `agent-template:${Date.now()}`;",
1421
+ " const triggerEvent = createTriggerEvent(prompt);",
1422
+ " const reaction = await agentContext.react(triggerEvent, {",
1423
+ " runtime: createRuntime({ actorId: \"template-user\" }),",
1424
+ " context: { key: contextKey },",
1425
+ " durable: false,",
1426
+ " });",
1427
+ " if (reaction.run) await reaction.run;",
1428
+ " return NextResponse.json({",
1429
+ " ok: true,",
1430
+ " appId: process.env.NEXT_PUBLIC_INSTANT_APP_ID || null,",
1431
+ " contextId: String(reaction.context.id || \"\"),",
1432
+ " contextKey: reaction.context.key ?? contextKey,",
1433
+ " triggerEventId: triggerEvent.id,",
1434
+ " });",
1435
+ " } catch (error) {",
1436
+ " return NextResponse.json({ ok: false, error: error instanceof Error ? error.message : String(error) }, { status: 500 });",
1437
+ " }",
1438
+ "}",
1439
+ ].join("\n"),
1440
+ "src/agent.ts": [
1441
+ "import {",
1442
+ " createContext,",
1443
+ " createScriptedReactor,",
1444
+ " defineAction,",
1445
+ " eventsDomain,",
1446
+ " type ContextItem,",
1447
+ "} from \"@ekairos/events\";",
1448
+ "import { z } from \"zod\";",
1449
+ "",
1450
+ "export type AgentEnv = {",
1451
+ " actorId?: string;",
1452
+ "};",
1453
+ "",
1454
+ "const createMessageInput = z.object({",
1455
+ " message: z.string(),",
1456
+ " marker: z.string().optional(),",
1457
+ "});",
1458
+ "",
1459
+ "const createMessageOutput = z.object({",
1460
+ " message: z.string(),",
1461
+ " marker: z.string(),",
1462
+ "});",
1463
+ "",
1464
+ "async function createMessage(input: z.infer<typeof createMessageInput>) {",
1465
+ " \"use step\";",
1466
+ " return {",
1467
+ " message: input.message,",
1468
+ " marker: input.marker ?? \"AGENT_TEMPLATE_OK\",",
1469
+ " };",
1470
+ "}",
1471
+ "",
1472
+ "function randomId() {",
1473
+ " return globalThis.crypto?.randomUUID?.() ?? `evt_${Date.now()}_${Math.random().toString(16).slice(2)}`;",
1474
+ "}",
1475
+ "",
1476
+ "export function createTriggerEvent(text: string): ContextItem {",
1477
+ " return {",
1478
+ " id: randomId(),",
1479
+ " type: \"user_message_created\",",
1480
+ " channel: \"web\",",
1481
+ " createdAt: new Date().toISOString(),",
1482
+ " content: { parts: [{ type: \"text\", text }] },",
1483
+ " } as any;",
1484
+ "}",
1485
+ "",
1486
+ "export const agentContext = createContext<AgentEnv>(\"agent.template\")",
1487
+ " .context((stored, env) => ({ ...(stored.content ?? {}), actorId: env.actorId ?? null }))",
1488
+ " .narrative(() => \"You are the minimal Ekairos agent template. Create one deterministic message.\")",
1489
+ " .actions(() => ({",
1490
+ " createMessage: defineAction<any, any, Record<string, never>, AgentEnv, typeof eventsDomain>({",
1491
+ " description: \"Create the deterministic template message.\",",
1492
+ " input: createMessageInput as any,",
1493
+ " output: createMessageOutput as any,",
1494
+ " execute: async ({ input }) => await createMessage(input),",
1495
+ " }),",
1496
+ " }))",
1497
+ " .reactor(",
1498
+ " createScriptedReactor({",
1499
+ " steps: [",
1500
+ " {",
1501
+ " assistantEvent: {",
1502
+ " content: {",
1503
+ " parts: [",
1504
+ " { type: \"text\", text: \"AGENT_TEMPLATE_OK context ready.\" },",
1505
+ " {",
1506
+ " type: \"tool-createMessage\",",
1507
+ " toolCallId: \"agent-template-create-message\",",
1508
+ " input: { message: \"AGENT_TEMPLATE_OK context ready.\", marker: \"AGENT_TEMPLATE_OK\" },",
1509
+ " },",
1510
+ " ],",
1511
+ " },",
1512
+ " },",
1513
+ " actionRequests: [",
1514
+ " {",
1515
+ " actionRef: \"agent-template-create-message\",",
1516
+ " actionName: \"createMessage\",",
1517
+ " input: { message: \"AGENT_TEMPLATE_OK context ready.\", marker: \"AGENT_TEMPLATE_OK\" },",
1518
+ " },",
1519
+ " ],",
1520
+ " messagesForModel: [],",
1521
+ " llm: { provider: \"scripted\", model: \"agent-template\" },",
1522
+ " },",
1523
+ " ],",
1524
+ " repeatLast: true,",
1525
+ " }),",
1526
+ " )",
1527
+ " .shouldContinue(() => false)",
1528
+ " .build();",
1529
+ ].join("\n"),
1530
+ "src/runtime.ts": [
1531
+ 'import { init } from "@instantdb/admin";',
1532
+ 'import { EkairosRuntime } from "@ekairos/domain/runtime-handle";',
1533
+ 'import { eventsDomain } from "@ekairos/events/schema";',
1534
+ "",
1535
+ "export type AgentRuntimeEnv = {",
1536
+ " actorId?: string | null;",
1537
+ " adminToken?: string;",
1538
+ " appId?: string;",
1539
+ "};",
1540
+ "",
1541
+ "function resolveRuntimeEnv(env: AgentRuntimeEnv = {}): Required<Pick<AgentRuntimeEnv, \"appId\" | \"adminToken\">> & AgentRuntimeEnv {",
1542
+ " const appId = String(env.appId ?? process.env.NEXT_PUBLIC_INSTANT_APP_ID ?? \"\").trim();",
1543
+ " const adminToken = String(env.adminToken ?? process.env.INSTANT_ADMIN_TOKEN ?? \"\").trim();",
1544
+ " if (!appId || !adminToken) {",
1545
+ " throw new Error(\"Missing NEXT_PUBLIC_INSTANT_APP_ID or INSTANT_ADMIN_TOKEN. Copy .env.example to .env.local and fill both values.\");",
1546
+ " }",
1547
+ " return { ...env, appId, adminToken };",
1548
+ "}",
1549
+ "",
1550
+ "export class AgentRuntime extends EkairosRuntime<AgentRuntimeEnv, typeof eventsDomain, any> {",
1551
+ " protected getDomain() { return eventsDomain; }",
1552
+ " protected async resolveDb(env: AgentRuntimeEnv) {",
1553
+ " const resolved = resolveRuntimeEnv(env);",
1554
+ " return init({",
1555
+ " appId: resolved.appId,",
1556
+ " adminToken: resolved.adminToken,",
1557
+ " schema: eventsDomain.toInstantSchema(),",
1558
+ " useDateObjects: true,",
1559
+ " } as any) as any;",
1560
+ " }",
1561
+ "}",
1562
+ "",
1563
+ "export function createRuntime(env: AgentRuntimeEnv = {}) {",
1564
+ " return new AgentRuntime(resolveRuntimeEnv(env));",
1565
+ "}",
1566
+ ].join("\n"),
1567
+ };
1568
+ }
922
1569
  return {
923
1570
  ".gitignore": [".next", "node_modules", ".env.local", ".workflow-data"].join("\n"),
924
1571
  ".env.example": [
@@ -988,14 +1635,16 @@ function buildNextTemplateFiles(params) {
988
1635
  ' "moduleResolution": "bundler",',
989
1636
  ' "resolveJsonModule": true,',
990
1637
  ' "isolatedModules": true,',
991
- ' "jsx": "preserve",',
1638
+ ' "jsx": "react-jsx",',
992
1639
  ' "incremental": true,',
993
- ' "baseUrl": ".",',
994
1640
  ' "paths": {',
995
1641
  ' "@/*": ["./src/*"]',
996
- " }",
1642
+ " },",
1643
+ ' "plugins": [',
1644
+ ' { "name": "next" }',
1645
+ " ]",
997
1646
  " },",
998
- ' "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],',
1647
+ ' "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", ".next/dev/types/**/*.ts"],',
999
1648
  ' "exclude": ["node_modules"]',
1000
1649
  "}",
1001
1650
  ].join("\n"),
@@ -1804,6 +2453,7 @@ function buildNextTemplateFiles(params) {
1804
2453
  "src/domain.ts": [
1805
2454
  "import { defineAction, domain } from \"@ekairos/domain\";",
1806
2455
  "import { i } from \"@instantdb/core\";",
2456
+ "import { z } from \"zod\";",
1807
2457
  "",
1808
2458
  "export const supplierNetworkDomain = domain(\"supplierNetwork\").withSchema({",
1809
2459
  " entities: {",
@@ -1906,13 +2556,20 @@ function buildNextTemplateFiles(params) {
1906
2556
  " .includes(qualityControlDomain)",
1907
2557
  " .withSchema({ entities: {}, links: {}, rooms: {} });",
1908
2558
  "",
1909
- "export const launchOrderAction = defineAction<",
1910
- " Record<string, unknown>,",
1911
- " { reference?: string; supplierName?: string; sku?: string },",
1912
- " { supplierId: string; orderId: string; stockItemId: string; shipmentId: string; inspectionId: string },",
1913
- " any",
1914
- ">({",
2559
+ "export const launchOrderAction = defineAction({",
1915
2560
  " name: \"supplyChain.order.launch\",",
2561
+ " input: z.object({",
2562
+ " reference: z.string().optional(),",
2563
+ " supplierName: z.string().optional(),",
2564
+ " sku: z.string().optional(),",
2565
+ " }),",
2566
+ " output: z.object({",
2567
+ " supplierId: z.string(),",
2568
+ " orderId: z.string(),",
2569
+ " stockItemId: z.string(),",
2570
+ " shipmentId: z.string(),",
2571
+ " inspectionId: z.string(),",
2572
+ " }),",
1916
2573
  " async execute({ runtime, input }): Promise<{",
1917
2574
  " supplierId: string;",
1918
2575
  " orderId: string;",
@@ -1920,8 +2577,6 @@ function buildNextTemplateFiles(params) {
1920
2577
  " shipmentId: string;",
1921
2578
  " inspectionId: string;",
1922
2579
  " }> {",
1923
- " \"use step\";",
1924
- " const scoped = await runtime.use(appDomain);",
1925
2580
  " const now = Date.now();",
1926
2581
  " const supplierId = globalThis.crypto.randomUUID();",
1927
2582
  " const orderId = globalThis.crypto.randomUUID();",
@@ -1929,65 +2584,60 @@ function buildNextTemplateFiles(params) {
1929
2584
  " const shipmentId = globalThis.crypto.randomUUID();",
1930
2585
  " const inspectionId = globalThis.crypto.randomUUID();",
1931
2586
  "",
1932
- " await scoped.db.transact([",
1933
- " scoped.db.tx.supplierNetwork_supplier[supplierId].update({",
2587
+ " await runtime.db.transact([",
2588
+ " runtime.db.tx.supplierNetwork_supplier[supplierId].update({",
1934
2589
  " name: String(input?.supplierName ?? \"\").trim() || \"Marula Components\",",
1935
2590
  " region: \"Pacific North\",",
1936
2591
  " risk: \"watch\",",
1937
2592
  " score: 82,",
1938
2593
  " createdAt: now,",
1939
2594
  " }),",
1940
- " scoped.db.tx.procurement_order[orderId].update({",
2595
+ " runtime.db.tx.procurement_order[orderId].update({",
1941
2596
  " reference: String(input?.reference ?? \"\").trim() || \"PO-7842\",",
1942
2597
  " status: \"released\",",
1943
2598
  " spend: 184700,",
1944
2599
  " createdAt: now + 1,",
1945
2600
  " }),",
1946
- " scoped.db.tx.procurement_order[orderId].link({ supplier: supplierId }),",
1947
- " scoped.db.tx.inventory_stockItem[stockItemId].update({",
2601
+ " runtime.db.tx.procurement_order[orderId].link({ supplier: supplierId }),",
2602
+ " runtime.db.tx.inventory_stockItem[stockItemId].update({",
1948
2603
  " sku: String(input?.sku ?? \"\").trim() || \"DRV-2048\",",
1949
2604
  " warehouse: \"Reno DC\",",
1950
2605
  " available: 320,",
1951
2606
  " safetyStock: 140,",
1952
2607
  " createdAt: now + 2,",
1953
2608
  " }),",
1954
- " scoped.db.tx.inventory_stockItem[stockItemId].link({ order: orderId }),",
1955
- " scoped.db.tx.transportation_shipment[shipmentId].update({",
2609
+ " runtime.db.tx.inventory_stockItem[stockItemId].link({ order: orderId }),",
2610
+ " runtime.db.tx.transportation_shipment[shipmentId].update({",
1956
2611
  " carrier: \"Northstar Freight\",",
1957
2612
  " lane: \"Reno -> Austin\",",
1958
2613
  " status: \"in-transit\",",
1959
2614
  " etaHours: 38,",
1960
2615
  " createdAt: now + 3,",
1961
2616
  " }),",
1962
- " scoped.db.tx.transportation_shipment[shipmentId].link({ order: orderId }),",
1963
- " scoped.db.tx.qualityControl_inspection[inspectionId].update({",
2617
+ " runtime.db.tx.transportation_shipment[shipmentId].link({ order: orderId }),",
2618
+ " runtime.db.tx.qualityControl_inspection[inspectionId].update({",
1964
2619
  " result: \"pending\",",
1965
2620
  " severity: \"medium\",",
1966
2621
  " note: \"Inspect seal integrity on arrival.\",",
1967
2622
  " createdAt: now + 4,",
1968
2623
  " }),",
1969
- " scoped.db.tx.qualityControl_inspection[inspectionId].link({ shipment: shipmentId }),",
2624
+ " runtime.db.tx.qualityControl_inspection[inspectionId].link({ shipment: shipmentId }),",
1970
2625
  " ]);",
1971
2626
  "",
1972
2627
  " return { supplierId, orderId, stockItemId, shipmentId, inspectionId };",
1973
2628
  " },",
1974
2629
  "});",
1975
2630
  "",
1976
- "export const expediteShipmentAction = defineAction<",
1977
- " Record<string, unknown>,",
1978
- " { shipmentId?: string },",
1979
- " { shipmentId: string },",
1980
- " any",
1981
- ">({",
2631
+ "export const expediteShipmentAction = defineAction({",
1982
2632
  " name: \"supplyChain.shipment.expedite\",",
2633
+ " input: z.object({ shipmentId: z.string().optional() }),",
2634
+ " output: z.object({ shipmentId: z.string() }),",
1983
2635
  " async execute({ runtime, input }): Promise<{ shipmentId: string }> {",
1984
- " \"use step\";",
1985
- " const scoped = await runtime.use(appDomain);",
1986
2636
  " const shipmentId = String(input?.shipmentId ?? \"\").trim();",
1987
2637
  " if (!shipmentId) throw new Error(\"shipmentId is required\");",
1988
2638
  "",
1989
- " await scoped.db.transact([",
1990
- " scoped.db.tx.transportation_shipment[shipmentId].update({",
2639
+ " await runtime.db.transact([",
2640
+ " runtime.db.tx.transportation_shipment[shipmentId].update({",
1991
2641
  " status: \"expedited\",",
1992
2642
  " etaHours: 16,",
1993
2643
  " }),",
@@ -2061,6 +2711,7 @@ function buildNextTemplateFiles(params) {
2061
2711
  "});",
2062
2712
  ].join("\n"),
2063
2713
  "src/workflows/demo.workflow.ts": [
2714
+ "import type { ActiveDomain } from \"@ekairos/domain\";",
2064
2715
  "import appDomain from \"../domain\";",
2065
2716
  "import { createRuntime } from \"../runtime\";",
2066
2717
  "",
@@ -2074,7 +2725,7 @@ function buildNextTemplateFiles(params) {
2074
2725
  "export async function runDemoWorkflow(input: DemoWorkflowInput) {",
2075
2726
  " \"use workflow\";",
2076
2727
  " const runtime = createRuntime();",
2077
- " const scoped = await runtime.use(appDomain);",
2728
+ " const scoped = (await runtime.use(appDomain)) as ActiveDomain<typeof appDomain>;",
2078
2729
  " const created = await scoped.actions.launchOrder({",
2079
2730
  " reference: input.reference,",
2080
2731
  " sku: input.sku,",
@@ -2096,6 +2747,7 @@ export async function createDomainApp(params) {
2096
2747
  if (params.framework !== "next") {
2097
2748
  throw new Error("Only --next is supported right now.");
2098
2749
  }
2750
+ const template = resolveCreateAppTemplate(params);
2099
2751
  if (params.smoke && !params.install) {
2100
2752
  throw new Error("--smoke requires dependencies. Remove --no-install or run smoke after installing.");
2101
2753
  }
@@ -2156,6 +2808,7 @@ export async function createDomainApp(params) {
2156
2808
  });
2157
2809
  provisioned = await provisionInstantApp({
2158
2810
  directory: targetDir,
2811
+ template,
2159
2812
  instantToken: trimOrEmpty(params.instantToken),
2160
2813
  orgId: params.orgId,
2161
2814
  });
@@ -2181,6 +2834,7 @@ export async function createDomainApp(params) {
2181
2834
  });
2182
2835
  const files = buildNextTemplateFiles({
2183
2836
  targetDir,
2837
+ template,
2184
2838
  domainVersion,
2185
2839
  packageManager,
2186
2840
  workspacePath: params.workspacePath,
@@ -2229,7 +2883,7 @@ export async function createDomainApp(params) {
2229
2883
  }
2230
2884
  const smoke = params.smoke
2231
2885
  ? await runSmoke({
2232
- demo: Boolean(params.demo),
2886
+ demo: template === "supply-chain",
2233
2887
  targetDir,
2234
2888
  packageManager,
2235
2889
  keepServer: Boolean(params.keepServer),
@@ -2239,22 +2893,39 @@ export async function createDomainApp(params) {
2239
2893
  : null;
2240
2894
  const cliCommand = packageBinCommandFor(packageManager, "domain");
2241
2895
  const reviewUrl = smoke?.baseUrl ?? "http://localhost:3000";
2242
- const nextSteps = [
2243
- `cd ${targetDir}`,
2244
- smoke?.keepServer
2245
- ? `Open ${reviewUrl} for review`
2246
- : params.install
2247
- ? runScriptCommandFor(packageManager, "dev")
2248
- : `${installCommandFor(packageManager)} && ${runScriptCommandFor(packageManager, "dev")}`,
2249
- `Open ${reviewUrl} and launch a purchase order from the control tower UI`,
2250
- `${cliCommand} inspect --baseUrl=${reviewUrl} --admin --pretty`,
2251
- `${cliCommand} "supplyChain.order.launch" "{ reference: 'PO-7842', supplierName: 'Marula Components', sku: 'DRV-2048' }" --baseUrl=${reviewUrl} --admin --pretty`,
2252
- `${cliCommand} query "{ procurement_order: { supplier: {}, stockItems: {}, shipments: { inspections: {} } } }" --baseUrl=${reviewUrl} --admin --pretty`,
2253
- ];
2896
+ const startStep = smoke?.keepServer
2897
+ ? `Open ${reviewUrl} for review`
2898
+ : params.install
2899
+ ? runScriptCommandFor(packageManager, "dev")
2900
+ : `${installCommandFor(packageManager)} && ${runScriptCommandFor(packageManager, "dev")}`;
2901
+ const nextSteps = template === "supply-chain"
2902
+ ? [
2903
+ `cd ${targetDir}`,
2904
+ startStep,
2905
+ `Open ${reviewUrl} and launch a purchase order from the control tower UI`,
2906
+ `${cliCommand} inspect --baseUrl=${reviewUrl} --admin --pretty`,
2907
+ `${cliCommand} "supplyChain.order.launch" "{ reference: 'PO-7842', supplierName: 'Marula Components', sku: 'DRV-2048' }" --baseUrl=${reviewUrl} --admin --pretty`,
2908
+ `${cliCommand} query "{ procurement_order: { supplier: {}, stockItems: {}, shipments: { inspections: {} } } }" --baseUrl=${reviewUrl} --admin --pretty`,
2909
+ ]
2910
+ : template === "agent"
2911
+ ? [
2912
+ `cd ${targetDir}`,
2913
+ startStep,
2914
+ `Open ${reviewUrl} and start a context through /api/agent/react`,
2915
+ `Inspect the returned context id in Workbench v2 for app ${appId || "<instant-app-id>"}`,
2916
+ `Iterate on src/agent.ts`,
2917
+ ]
2918
+ : [
2919
+ `cd ${targetDir}`,
2920
+ startStep,
2921
+ `Open ${reviewUrl} and add your first domain in src/domain.ts`,
2922
+ `${cliCommand} inspect --baseUrl=${reviewUrl} --admin --pretty`,
2923
+ ];
2254
2924
  const result = {
2255
2925
  ok: true,
2256
2926
  directory: targetDir,
2257
2927
  framework: params.framework,
2928
+ template,
2258
2929
  installed: params.install,
2259
2930
  packageManager,
2260
2931
  provisioned: Boolean(provisioned),