@alexkroman1/aai-cli 13.3.0 → 15.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/dist/{_agent-Clbq3a4h.mjs → _agent-BzUeqOdj.mjs} +32 -9
  2. package/dist/_agent.d.ts +23 -0
  3. package/dist/_artifacts-BJOYGQPp.mjs +21 -0
  4. package/dist/_artifacts.d.ts +16 -0
  5. package/dist/_build-target.d.ts +172 -0
  6. package/dist/{_bundler-DEkjO1d8.mjs → _bundler-DM0d0M7m.mjs} +8 -7
  7. package/dist/_cli-common.d.ts +45 -6
  8. package/dist/{_config-ZY2euhdh.mjs → _config-DYzC6WMD.mjs} +2 -2
  9. package/dist/_dev-env.d.ts +1 -1
  10. package/dist/{_dev-server-C13xuGB1.mjs → _dev-server-BzWB6-4y.mjs} +25 -24
  11. package/dist/_dev-vite-config.d.ts +3 -3
  12. package/dist/_e2e-test-utils.d.ts +1 -1
  13. package/dist/{_init-Ci4QGjp8.mjs → _init-Bsi3DZNJ.mjs} +9 -23
  14. package/dist/{_output-DbJt9gsl.mjs → _output-DBP9Op_d.mjs} +22 -13
  15. package/dist/_output.d.ts +10 -9
  16. package/dist/{_preflight-B2laWPdU.mjs → _preflight-CcAR577V.mjs} +2 -2
  17. package/dist/_resource-commands.d.ts +9 -9
  18. package/dist/_server-common-De0haHr9.mjs +70 -0
  19. package/dist/_server-common.d.ts +20 -1
  20. package/dist/{_slug-api-DlKpj91X.mjs → _slug-api-CGvc2YSQ.mjs} +18 -9
  21. package/dist/_slug-api.d.ts +19 -0
  22. package/dist/{_studio-DTi3EdXQ.mjs → _studio-DnR_BqFp.mjs} +7 -3
  23. package/dist/_studio-commands.d.ts +13 -13
  24. package/dist/{_templates-V4l5sZJy.mjs → _templates-CIlJ3Vay.mjs} +4 -4
  25. package/dist/_templates.d.ts +1 -1
  26. package/dist/{_typecheck-gate-sV4N5F93.mjs → _typecheck-gate-U3jcil8L.mjs} +3 -2
  27. package/dist/_typecheck-gate.d.ts +3 -1
  28. package/dist/{_utils-Dhd058D9.mjs → _utils-D5JGcjiW.mjs} +43 -1
  29. package/dist/_utils.d.ts +35 -0
  30. package/dist/_vercel-output.d.ts +63 -0
  31. package/dist/{_workflow-determinism-C9l8uC2R.mjs → _workflow-determinism-CvpqMpdh.mjs} +18 -26
  32. package/dist/_workflow-determinism.d.ts +3 -3
  33. package/dist/build-BhEaxBPu.mjs +481 -0
  34. package/dist/build.d.ts +23 -10
  35. package/dist/cli.d.ts +1 -2
  36. package/dist/cli.mjs +155 -138
  37. package/dist/{client-bundler-CT-_26c5.mjs → client-bundler-6mTLs6ny.mjs} +5 -5
  38. package/dist/client-bundler.d.ts +1 -1
  39. package/dist/client-bundler.mjs +1 -1
  40. package/dist/{delete-RqlQZ6Y_.mjs → delete-BdFnbnVR.mjs} +4 -4
  41. package/dist/{deploy-B7f3CK7v.mjs → deploy-CGqPU5U-.mjs} +11 -11
  42. package/dist/{dev-DamXieW_.mjs → dev-Bx9gYBHM.mjs} +3 -3
  43. package/dist/{eval-h3DTVfRw.mjs → eval-B3I7FqN9.mjs} +3 -3
  44. package/dist/{init-D-vFrV8h.mjs → init-CFyusRbq.mjs} +48 -20
  45. package/dist/init.d.ts +14 -0
  46. package/dist/{login-CDiemNmC.mjs → login-DaLrPOFg.mjs} +4 -6
  47. package/dist/{logs-DGwPNCzB.mjs → logs-7BRwq0cG.mjs} +21 -14
  48. package/dist/project-config.mjs +1 -1
  49. package/dist/scaffold/CLAUDE.md +155 -76
  50. package/dist/scaffold/package.json +6 -6
  51. package/dist/{secret-V1w7y9-j.mjs → secret-Cjp912yF.mjs} +5 -5
  52. package/dist/secret.d.ts +8 -6
  53. package/dist/start.d.ts +112 -0
  54. package/dist/start.mjs +156 -0
  55. package/dist/{studio-BpMCfUNx.mjs → studio-C_zuRC_z.mjs} +13 -15
  56. package/dist/templates/briefing-desk/agent.eval.test.ts +156 -0
  57. package/dist/templates/call-audit/agent.test.ts +11 -6
  58. package/dist/templates/call-audit/agent.ts +2 -2
  59. package/dist/templates/call-audit/client.tsx +27 -42
  60. package/dist/templates/call-audit/workflows/audit.ts +7 -5
  61. package/dist/templates/call-audit/workflows/ingest.ts +7 -7
  62. package/dist/templates/call-audit/workflows/media.ts +12 -37
  63. package/dist/templates/call-audit/workflows/summarize.ts +8 -8
  64. package/dist/templates/call-audit/workflows/sync-api.ts +3 -3
  65. package/dist/templates/code-interpreter/agent.test.ts +103 -0
  66. package/dist/templates/dispatch-center/agent.test.ts +7 -7
  67. package/dist/templates/dispatch-center/client.tsx +53 -61
  68. package/dist/templates/dispatch-center/shared.ts +0 -8
  69. package/dist/templates/dispatch-center/tools/ops_dashboard.ts +4 -1
  70. package/dist/templates/embedded-assets/agent.eval.test.ts +4 -2
  71. package/dist/templates/health-assistant/agent.eval.test.ts +4 -2
  72. package/dist/templates/infocom-adventure/client.tsx +40 -36
  73. package/dist/templates/link-digest/agent.test.ts +3 -3
  74. package/dist/templates/link-digest/agent.ts +2 -2
  75. package/dist/templates/link-digest/client.tsx +81 -19
  76. package/dist/templates/link-digest/workflows/digest.ts +13 -16
  77. package/dist/templates/math-buddy/agent.test.ts +126 -0
  78. package/dist/templates/night-owl/agent.eval.test.ts +4 -2
  79. package/dist/templates/night-owl/client.tsx +8 -2
  80. package/dist/templates/personal-finance/agent.test.ts +127 -0
  81. package/dist/templates/pipeline-simple/agent.test.ts +2 -2
  82. package/dist/templates/pizza-ordering/client.tsx +5 -4
  83. package/dist/templates/pizza-ordering/shared.ts +3 -8
  84. package/dist/templates/pizza-ordering/tools/add_pizza.ts +3 -2
  85. package/dist/templates/pizza-ordering/tools/place_order.ts +3 -2
  86. package/dist/templates/pizza-ordering/tools/remove_pizza.ts +3 -2
  87. package/dist/templates/pizza-ordering/tools/update_pizza.ts +3 -2
  88. package/dist/templates/pizza-ordering/tools/view_order.ts +3 -2
  89. package/dist/templates/plan-and-execute/agent.test.ts +10 -5
  90. package/dist/templates/plan-and-execute/client.tsx +2 -2
  91. package/dist/templates/podcast-digest/agent.test.ts +20 -11
  92. package/dist/templates/podcast-digest/client.tsx +16 -22
  93. package/dist/templates/podcast-digest/workflows/digest.ts +21 -19
  94. package/dist/templates/podcast-digest/workflows/feeds.ts +61 -33
  95. package/dist/templates/podcast-digest/workflows/slack.ts +7 -7
  96. package/dist/templates/recap-workflow/agent.eval.test.ts +21 -13
  97. package/dist/templates/recap-workflow/agent.test.ts +27 -23
  98. package/dist/templates/recap-workflow/workflows/recap.ts +24 -28
  99. package/dist/templates/redline/agent.eval.test.ts +5 -8
  100. package/dist/templates/redline/agent.test.ts +1 -1
  101. package/dist/templates/redline/client.tsx +36 -34
  102. package/dist/templates/redline/workflows/redline.ts +17 -17
  103. package/dist/templates/research-workflow/agent.eval.test.ts +5 -5
  104. package/dist/templates/research-workflow/agent.test.ts +10 -7
  105. package/dist/templates/research-workflow/workflows/research.ts +28 -30
  106. package/dist/templates/retail/agent.test.ts +17 -15
  107. package/dist/templates/retail/cancel.ts +7 -6
  108. package/dist/templates/retail/client.tsx +39 -58
  109. package/dist/templates/retail/payment.ts +4 -3
  110. package/dist/templates/retail/pending.test.ts +19 -19
  111. package/dist/templates/retail/store.test.ts +5 -5
  112. package/dist/templates/retail/swap.ts +8 -11
  113. package/dist/templates/solo-rpg/agent.eval.test.ts +2 -16
  114. package/dist/templates/solo-rpg/agent.test.ts +12 -12
  115. package/dist/templates/solo-rpg/client.tsx +3 -3
  116. package/dist/templates/spoken-summary/agent.test.ts +9 -9
  117. package/dist/templates/spoken-summary/agent.ts +3 -3
  118. package/dist/templates/spoken-summary/client.tsx +23 -30
  119. package/dist/templates/spoken-summary/workflows/summarize.ts +11 -11
  120. package/dist/templates/spoken-summary/workflows/transcribe.ts +14 -14
  121. package/dist/templates/support-line/agent.ts +8 -0
  122. package/dist/templates/support-line/client.tsx +4 -3
  123. package/dist/templates/support-line/prompts.ts +0 -2
  124. package/dist/templates/transcription-workflow/agent.test.ts +20 -12
  125. package/dist/templates/transcription-workflow/agent.ts +2 -2
  126. package/dist/templates/transcription-workflow/client.tsx +15 -14
  127. package/dist/templates/transcription-workflow/recover.ts +1 -1
  128. package/dist/templates/transcription-workflow/run-panel.tsx +34 -14
  129. package/dist/templates/transcription-workflow/total-latency.tsx +46 -20
  130. package/dist/templates/transcription-workflow/workflows/batch.ts +15 -15
  131. package/dist/templates/transcription-workflow/workflows/downsample.ts +1 -1
  132. package/dist/templates/transcription-workflow/workflows/normalize.ts +15 -19
  133. package/dist/templates/transcription-workflow/workflows/stream.ts +12 -12
  134. package/dist/templates/transcription-workflow/workflows/sync-api.ts +3 -3
  135. package/dist/templates/transcription-workflow/workflows/transcribe.ts +23 -34
  136. package/dist/templates/travel-concierge/agent.eval.test.ts +2 -2
  137. package/dist/templates/travel-concierge/agent.test.ts +15 -7
  138. package/dist/templates/travel-concierge/client.tsx +2 -2
  139. package/dist/templates/travel-concierge/routing.ts +64 -55
  140. package/dist/templates/travel-concierge/shared.ts +10 -14
  141. package/dist/templates/travel-concierge/tools/cancel_action.ts +3 -1
  142. package/dist/templates/travel-concierge/tools/complete_or_escalate.ts +3 -1
  143. package/dist/templates/travel-concierge/tools/confirm_action.ts +3 -1
  144. package/dist/templates/travel-concierge/tools/lookup_booking.ts +4 -3
  145. package/dist/templates/travel-concierge/tools/search_car_rentals.ts +3 -2
  146. package/dist/templates/travel-concierge/tools/search_excursions.ts +3 -2
  147. package/dist/templates/travel-concierge/tools/search_flights.ts +3 -2
  148. package/dist/templates/travel-concierge/tools/search_hotels.ts +3 -2
  149. package/dist/templates/web-researcher/agent.test.ts +130 -0
  150. package/dist/{test-guenJUxi.mjs → test-D032V2cA.mjs} +4 -17
  151. package/dist/typecheck.mjs +1 -1
  152. package/dist/worker-bundler.d.ts +1 -1
  153. package/dist/worker-bundler.mjs +8 -8
  154. package/dist/{workflow-BZyKLkhV.mjs → workflow-CVioyO3V.mjs} +6 -5
  155. package/package.json +9 -4
  156. package/dist/_server-common-BV4_CrP3.mjs +0 -43
  157. package/dist/build-CWyFGe-W.mjs +0 -69
  158. package/dist/scaffold/server.mjs +0 -204
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { n as binFromManifest, o as errorMessage, u as readPackageJson } from "./_utils-Dhd058D9.mjs";
2
+ import { f as readPackageJson, n as binFromManifest, s as errorMessage } from "./_utils-D5JGcjiW.mjs";
3
3
  import { existsSync } from "node:fs";
4
4
  import path from "node:path";
5
5
  import { spawn } from "node:child_process";
@@ -34,4 +34,4 @@ export type BuildWorkerOptions = {
34
34
  * @internal — build hook for aai-server/the studio; not a supported public
35
35
  * API and not covered by semver.
36
36
  */
37
- export declare function buildWorker(cwd: string, opts?: BuildWorkerOptions): Promise<string>;
37
+ export declare function buildWorker(cwd: string, options?: BuildWorkerOptions): Promise<string>;
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { i as errorCode } from "./_utils-Dhd058D9.mjs";
2
+ import { a as errorCode } from "./_utils-D5JGcjiW.mjs";
3
3
  import { n as withPreservedNodeEnv } from "./_vite-env-kejF0p2L.mjs";
4
4
  import path from "node:path";
5
5
  import fs from "node:fs/promises";
@@ -92,8 +92,8 @@ export const __aaiConfig = {
92
92
  ...toAgentConfig(__aaiAgent),
93
93
  toolSchemas: agentToolsToSchemas(__aaiAgent.tools ?? {}),
94
94
  };
95
- ${runtime ? `export const __aaiCreateRuntime = (opts: Record<string, unknown>) =>
96
- createRuntime({ ...opts, agent: __aaiAgent });
95
+ ${runtime ? `export const __aaiCreateRuntime = (options: Record<string, unknown>) =>
96
+ createRuntime({ ...options, agent: __aaiAgent });
97
97
  ` : ""}`;
98
98
  }
99
99
  /**
@@ -105,21 +105,21 @@ ${runtime ? `export const __aaiCreateRuntime = (opts: Record<string, unknown>) =
105
105
  * @internal — build hook for aai-server/the studio; not a supported public
106
106
  * API and not covered by semver.
107
107
  */
108
- async function buildWorker(cwd, opts = {}) {
108
+ async function buildWorker(cwd, options = {}) {
109
109
  const wrapperPath = path.join(cwd, WRAPPER_ENTRY_REL);
110
110
  const [, toolFiles, systemPromptFile] = await Promise.all([
111
111
  fs.mkdir(path.dirname(wrapperPath), { recursive: true }),
112
112
  discoverToolFiles(cwd),
113
113
  hasSystemPromptFile(cwd)
114
114
  ]);
115
- await fs.writeFile(wrapperPath, wrapperEntrySource(opts.runtime !== false, toolFiles, systemPromptFile), "utf-8");
116
- const plugins = opts.plugins ?? [];
115
+ await fs.writeFile(wrapperPath, wrapperEntrySource(options.runtime !== false, toolFiles, systemPromptFile), "utf-8");
116
+ const plugins = options.plugins ?? [];
117
117
  let result;
118
118
  try {
119
119
  result = await withPreservedNodeEnv(() => build({
120
120
  root: cwd,
121
121
  logLevel: "silent",
122
- ...opts.configFile === false && { configFile: false },
122
+ ...options.configFile === false && { configFile: false },
123
123
  ...plugins.length > 0 && { plugins },
124
124
  ssr: { noExternal: true },
125
125
  build: {
@@ -130,7 +130,7 @@ async function buildWorker(cwd, opts = {}) {
130
130
  fileName: "worker"
131
131
  },
132
132
  target: "node20",
133
- minify: opts.minify ? "oxc" : false,
133
+ minify: options.minify ? "oxc" : false,
134
134
  write: false,
135
135
  rollupOptions: { output: {
136
136
  entryFileNames: "[name].js",
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { a as ok, n as fail, t as CliError } from "./_output-DbJt9gsl.mjs";
2
+ import { a as ok, n as fail, t as CliError } from "./_output-DBP9Op_d.mjs";
3
3
  import { n as log } from "./_ui-DCt4qZrk.mjs";
4
- import { i as readProjectConfig } from "./_config-ZY2euhdh.mjs";
5
- import { n as getServerInfo } from "./_agent-Clbq3a4h.mjs";
4
+ import { m as stripTrailingSlash } from "./_utils-D5JGcjiW.mjs";
5
+ import { i as readProjectConfig } from "./_config-DYzC6WMD.mjs";
6
+ import { n as getServerInfo } from "./_agent-BzUeqOdj.mjs";
6
7
  import { errorMessage, omitUndefined } from "@alexkroman1/aai/utils";
7
8
  import { createWorkflowApiClient } from "@alexkroman1/aai/workflow-api";
8
9
  //#region src/workflow.ts
@@ -38,7 +39,7 @@ const DEFAULT_RUN_LIMIT = 20;
38
39
  /**
39
40
  * The base URL {@link createWorkflowApiClient} is given for `--agent`.
40
41
  *
41
- * A dev server has no slug: `createServer` mounts the workflow API at
42
+ * A dev server has no slug: `createRuntimeServer` mounts the workflow API at
42
43
  * `WORKFLOW_API_PREFIX` on the ORIGIN, where the platform serves it under
43
44
  * `/:slug`. So targeting one is not a matter of a different origin — it is the
44
45
  * absence of the path segment, which nothing in this command could express.
@@ -61,7 +62,7 @@ function agentBaseUrl(raw) {
61
62
  const url = URL.parse(raw);
62
63
  if (url === null) throw new CliError("bad_agent_url", `--agent is not a URL: ${JSON.stringify(raw)}`, HINT_URL);
63
64
  if (url.protocol !== "http:" && url.protocol !== "https:") throw new CliError("bad_agent_url", `--agent must be http or https, not ${url.protocol.replace(":", "")}`, HINT_URL);
64
- return raw.replace(/\/+$/, "");
65
+ return stripTrailingSlash(raw);
65
66
  }
66
67
  /**
67
68
  * A client for the agent's workflow API, plus the bearer when one was given.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alexkroman1/aai-cli",
3
- "version": "13.3.0",
3
+ "version": "15.0.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -38,6 +38,11 @@
38
38
  "@dev/source": "./src/project-config.ts",
39
39
  "types": "./dist/project-config.d.ts",
40
40
  "import": "./dist/project-config.mjs"
41
+ },
42
+ "./start": {
43
+ "@dev/source": "./src/start.ts",
44
+ "types": "./dist/start.d.ts",
45
+ "import": "./dist/start.mjs"
41
46
  }
42
47
  },
43
48
  "dependencies": {
@@ -52,9 +57,9 @@
52
57
  "p-timeout": "^7.0.1",
53
58
  "vite": "^8.2.2",
54
59
  "zod": "^4.5.4",
55
- "@alexkroman1/aai": "13.3.0",
56
- "@alexkroman1/aai-runtime": "13.3.0",
57
- "@alexkroman1/aai-ui": "13.3.0"
60
+ "@alexkroman1/aai": "15.0.0",
61
+ "@alexkroman1/aai-runtime": "15.0.0",
62
+ "@alexkroman1/aai-ui": "15.0.0"
58
63
  },
59
64
  "devDependencies": {
60
65
  "playwright": "^1.62.1",
@@ -1,43 +0,0 @@
1
- #!/usr/bin/env node
2
- import { i as errorCode } from "./_utils-Dhd058D9.mjs";
3
- import path from "node:path";
4
- import { parseEnv } from "node:util";
5
- import fs from "node:fs/promises";
6
- //#region src/_server-common.ts
7
- /**
8
- * Build the `ctx.env` record that agent tools will see at runtime.
9
- *
10
- * Only variables explicitly declared in `.env` are included — matching
11
- * the platform sandbox behavior where `ctx.env`
12
- * contains only secrets set via `aai secret put`. This prevents agents
13
- * from accidentally depending on shell-level vars (PATH, HOME, etc.) that
14
- * won't exist in production.
15
- *
16
- * Values are resolved by merging the `.env` file with the current
17
- * environment — existing shell exports take precedence over `.env`
18
- * defaults, without mutating `process.env`.
19
- *
20
- * @param cwd - Project directory containing `.env` (optional).
21
- * @param baseEnv - Override the environment to read values from (tests only).
22
- */
23
- async function resolveServerEnv(cwd, baseEnv) {
24
- let fileEntries = {};
25
- if (cwd) {
26
- let content = null;
27
- try {
28
- content = await fs.readFile(path.join(cwd, ".env"), "utf-8");
29
- } catch (err) {
30
- if (errorCode(err) !== "ENOENT") throw err;
31
- }
32
- if (content !== null) fileEntries = parseEnv(content);
33
- }
34
- const source = baseEnv ?? process.env;
35
- const env = {};
36
- for (const [key, fileVal] of Object.entries(fileEntries)) {
37
- const val = source[key] ?? fileVal;
38
- if (val !== void 0 && val !== "") env[key] = val;
39
- }
40
- return env;
41
- }
42
- //#endregion
43
- export { resolveServerEnv as t };
@@ -1,69 +0,0 @@
1
- #!/usr/bin/env node
2
- import { a as ok, t as CliError } from "./_output-DbJt9gsl.mjs";
3
- import { n as log, r as notify } from "./_ui-DCt4qZrk.mjs";
4
- import { r as evalWorkerBundle, t as buildAgentBundle } from "./_bundler-DEkjO1d8.mjs";
5
- import { assertTypechecks } from "./_typecheck-gate-sV4N5F93.mjs";
6
- import { n as scanWorkflowDeterminism, t as determinismWarnings } from "./_workflow-determinism-C9l8uC2R.mjs";
7
- import { TEST_FILES, classifyVitestError, runVitest } from "./test-guenJUxi.mjs";
8
- import path from "node:path";
9
- import fs from "node:fs/promises";
10
- import { agentConfigWarnings } from "@alexkroman1/aai/manifest";
11
- //#region src/build.ts
12
- /**
13
- * `aai build` — bundle the agent without deploying, behind the same gates
14
- * deploy runs (tests, then typecheck), so the command previews the deploy
15
- * artifact and its failures alike.
16
- *
17
- * It also LEAVES that artifact on disk, at {@link WORKER_ARTIFACT_REL}, which is
18
- * what makes self-hosting work: `server.mjs` loads the built worker rather than
19
- * `agent.ts`, because a tool is discovered by the bundler enumerating `tools/`
20
- * and no un-bundled loader can see that directory. See the scaffold's own
21
- * `server.mjs` and "Self-hosting runs the built worker" in
22
- * `packages/aai-templates/CLAUDE.md`.
23
- */
24
- /**
25
- * Where the built worker lands, relative to the project root — under `.aai/`,
26
- * beside the built client, so one gitignore rule covers every build output.
27
- *
28
- * `server.mjs` hardcodes this path: it is a plain `.mjs` file in a user's
29
- * project and may not import from the CLI (which a deployed project does not
30
- * install at all). The pair is covered end-to-end by the `npm start` leg of
31
- * `e2e.test.ts` — the only tier that runs both halves as a user does.
32
- */
33
- const WORKER_ARTIFACT_REL = path.join(".aai", "worker.mjs");
34
- /**
35
- * Map a {@link runVitest} failure to a CliError — distinguishing a real test
36
- * failure (test_failed) from the runner not spawning (spawn_failed) instead
37
- * of a generic command_failed.
38
- */
39
- function testGateError(err) {
40
- const { code, message } = classifyVitestError(err);
41
- return new CliError(code, message, "Re-run with --skipTests to build without tests", { cause: err });
42
- }
43
- async function executeBuild(opts) {
44
- const { cwd } = opts;
45
- if (!opts.skipTests) try {
46
- runVitest(cwd, {
47
- candidates: TEST_FILES,
48
- all: true
49
- });
50
- } catch (err) {
51
- throw testGateError(err);
52
- }
53
- if (!opts.skipTypecheck) await assertTypechecks(cwd);
54
- const bundle = await buildAgentBundle(cwd, { minify: true });
55
- const agentDef = await evalWorkerBundle(bundle.worker);
56
- for (const warning of agentConfigWarnings(agentDef)) notify("warn", warning);
57
- for (const warning of determinismWarnings(await scanWorkflowDeterminism(cwd))) notify("warn", warning);
58
- const worker = path.join(cwd, WORKER_ARTIFACT_REL);
59
- await fs.mkdir(path.dirname(worker), { recursive: true });
60
- await fs.writeFile(worker, bundle.worker, "utf-8");
61
- log.success("Build complete");
62
- return ok({
63
- name: agentDef.name,
64
- workerBytes: bundle.worker.length,
65
- worker
66
- });
67
- }
68
- //#endregion
69
- export { executeBuild };
@@ -1,204 +0,0 @@
1
- // Self-hosted entrypoint — this is what `npm start` runs.
2
- //
3
- // It serves the agent over HTTP + WebSocket from your own Node process: no
4
- // platform account, no managed anything. `aai dev` is the development
5
- // counterpart (file watching, Vite, a browser that opens itself); this file is
6
- // the deployment.
7
- //
8
- // npm start # http://127.0.0.1:3000
9
- // PORT=8080 HOST=0.0.0.0 npm start # bind every interface, e.g. in a container
10
- //
11
- // Anything that can run Node can host it: copy the project, install
12
- // dependencies, provide the secrets, run `npm start`. Deleting this file costs
13
- // nothing — `aai dev`, `aai publish` and the managed platform never read it.
14
-
15
- import { existsSync } from "node:fs";
16
- import { readFile } from "node:fs/promises";
17
- import path from "node:path";
18
- import { fileURLToPath, pathToFileURL } from "node:url";
19
- import { parseEnv } from "node:util";
20
- import {
21
- createAgentServer,
22
- ensureSessionStateSchema,
23
- ensureWorkflowJournalSchema,
24
- withHostCredentialFallback,
25
- } from "@alexkroman1/aai-runtime";
26
- import { defaultClientDir } from "@alexkroman1/aai-ui/client-dir";
27
-
28
- const root = path.dirname(fileURLToPath(import.meta.url));
29
-
30
- /**
31
- * The built agent — `.aai/worker.mjs`, produced by `aai build`, which the
32
- * `prestart` script runs for you. It is the SAME artifact `aai publish` uploads
33
- * and the managed platform runs, so a self-hosted agent and a deployed one
34
- * cannot behave differently.
35
- *
36
- * Loading the build rather than `agent.ts` directly is what makes `tools/`
37
- * work. A tool is registered by EXISTING — its file name is the name the model
38
- * calls, and nothing anywhere lists them — and the only place a directory can
39
- * be turned into modules is where the bundle is assembled: a deployed agent is
40
- * handed one ESM string and has no filesystem to scan. So the bundler
41
- * enumerates `tools/` and emits the imports, and every loader that skips it
42
- * would serve an agent with no tools at all.
43
- *
44
- * It also settles two things the bundler already resolves, and this file used
45
- * to re-implement with `node:module` hooks: `import prompt from
46
- * "./system-prompt.md?raw"` (a Vite convention — Node looks for a file
47
- * literally named `system-prompt.md?raw`) and `import data from "./data.json"`
48
- * with no import attribute (TypeScript's `resolveJsonModule` allows it, Node
49
- * requires `with { type: "json" }`). Both are inlined into the bundle, so
50
- * there is nothing left to teach Node.
51
- */
52
- const workerPath = path.join(root, ".aai", "worker.mjs");
53
- if (!existsSync(workerPath)) {
54
- console.error(
55
- `No built agent at ${path.relative(root, workerPath)}.\n` +
56
- "Run `npm run build` (or `aai build`) first — `npm start` normally does it for you.",
57
- );
58
- process.exit(1);
59
- }
60
- // A file: URL rather than a relative specifier, so the path is correct on
61
- // Windows, where a bare POSIX-looking path is not a valid module specifier.
62
- const worker = await import(pathToFileURL(workerPath).href);
63
- const agent = worker.default;
64
-
65
- /**
66
- * Parse a dotenv-syntax file into a record; `{}` when it does not exist.
67
- *
68
- * @param {string} file - Path relative to the project root.
69
- * @returns {Promise<Record<string, string | undefined>>}
70
- */
71
- async function readEnvFile(file) {
72
- try {
73
- return parseEnv(await readFile(path.join(root, file), "utf-8"));
74
- } catch (err) {
75
- // Absent is normal — `.env` is gitignored, and a container usually has
76
- // neither file. Unreadable is not: the agent would boot with no
77
- // credentials and fail later as an opaque provider auth error.
78
- if (err.code !== "ENOENT") throw err;
79
- return {};
80
- }
81
- }
82
-
83
- /**
84
- * Build `ctx.env` — what this agent's own tool code reads.
85
- *
86
- * The rule is the one `aai dev` follows: only DECLARED keys are exposed, and a
87
- * real environment variable wins over the file's value. Nothing else from
88
- * process.env comes along, so the agent cannot come to depend on a variable
89
- * (PATH, HOME, …) that will not exist wherever you deploy it.
90
- *
91
- * `.env.example` counts as a declaration too, which is what lets a container
92
- * run with no `.env` at all: the committed file names the secrets the agent
93
- * needs, and `docker run -e MY_API_KEY=…` supplies the values. Declare
94
- * `DATABASE_URL` the same way to give your tools `ctx.db`.
95
- */
96
- async function resolveAgentEnv() {
97
- const declared = { ...(await readEnvFile(".env.example")), ...(await readEnvFile(".env")) };
98
- /** @type {Record<string, string>} */
99
- const env = {};
100
- for (const [key, fileValue] of Object.entries(declared)) {
101
- const value = process.env[key] ?? fileValue;
102
- // An empty value is worse than a missing one: a provider would try to
103
- // authenticate with "" rather than report the credential as absent. The
104
- // example file is full of them by design (`BRAVE_API_KEY=`). A key the
105
- // parser saw with no value at all is the same case.
106
- if (value !== undefined && value !== "") env[key] = value;
107
- }
108
- return env;
109
- }
110
-
111
- /**
112
- * Static assets served at `/`: this project's own UI once `client.tsx` has been
113
- * built (the same `aai build` leaves it in `.aai/client`), otherwise the
114
- * prebuilt default client that ships inside @alexkroman1/aai-ui.
115
- */
116
- function resolveClientDir() {
117
- const built = path.join(root, ".aai", "client");
118
- if (existsSync(path.join(built, "index.html"))) return built;
119
- if (existsSync(path.join(root, "client.tsx"))) {
120
- console.warn("client.tsx is not built — serving the default UI. Run `npm run build` first.");
121
- }
122
- return defaultClientDir();
123
- }
124
-
125
- const env = await resolveAgentEnv();
126
-
127
- /**
128
- * Where THIS server is reachable from outside — `PUBLIC_URL`, e.g.
129
- * `https://agent.example.com`. Set it whenever a durable workflow has to hand a
130
- * URL to somebody else: `ctx.workflows.publicWebhookUrl(token)` is built from it,
131
- * and without it that call throws rather than minting a `http://localhost:3000`
132
- * URL that a payment provider will try, days later, and fail.
133
- *
134
- * Nothing else reads it, so it is not a startup requirement: an agent with no
135
- * webhooks needs no value here. It is deliberately NOT derived from `PORT`/`HOST`
136
- * — those describe the socket this process binds, which behind a reverse proxy is
137
- * not what the outside world dials.
138
- */
139
- const publicUrl = process.env.PUBLIC_URL?.trim();
140
-
141
- /**
142
- * Create the session-state tables, when this agent has a database.
143
- *
144
- * A `DATABASE_URL` puts session state in Postgres, and those tables come with
145
- * whoever OWNS the database — which for a self-hosted agent is you, with no
146
- * migration step anywhere to hang them off. Without this the server starts,
147
- * reports `sessionState: postgres, durable: true`, and then every session dies
148
- * at start with a fatal error the browser shows as "Session failed to start",
149
- * the real reason (`relation "aai_session_events" does not exist`) appearing
150
- * only in this process's log.
151
- *
152
- * Best-effort: if a real migration already created them and this role may not
153
- * CREATE, it warns and the server starts anyway.
154
- */
155
- if (env.DATABASE_URL) {
156
- await ensureSessionStateSchema({ url: env.DATABASE_URL, logger: console });
157
- // And the durable-run journal's, which is a separate set of tables owned by
158
- // the same deployment. Without it a project with a `DATABASE_URL` boots
159
- // claiming durable runs and fails on the first one.
160
- await ensureWorkflowJournalSchema({ url: env.DATABASE_URL, logger: console });
161
- }
162
-
163
- const server = createAgentServer({
164
- agent,
165
- env,
166
- // Provider credentials may ALSO arrive straight from the environment without
167
- // being declared, and without becoming ctx.env — the ordinary way to hand
168
- // ASSEMBLYAI_API_KEY to a container. Anything in `env` still wins.
169
- providerEnv: withHostCredentialFallback(env),
170
- clientDir: resolveClientDir(),
171
- ...(publicUrl ? { publicUrl } : {}),
172
- // Durable workflows need nothing passed here. A `DATABASE_URL` in `env` puts
173
- // the runs in Postgres and they survive a restart; without one they live in a
174
- // per-process directory and do not, which is the same trade `aai dev` makes.
175
- //
176
- // Two options used to sit here — the compiled workflow surface, carried on the
177
- // bundle as `__aaiWorkflowCode`/`__aaiStepCode` because a `"use workflow"` body
178
- // had to go through a compiler at BUILD time. The engine reads the agent's own
179
- // `workflows` declaration instead, so there is no artifact to hand over.
180
- });
181
-
182
- // Loopback by default: this server has no request authentication of its own,
183
- // so exposing it is a deliberate act. Set HOST=0.0.0.0 to bind every interface
184
- // behind your own proxy or auth. An empty HOST means unset, not "everything".
185
- const host = process.env.HOST?.trim() || undefined;
186
- await server.listen(Number(process.env.PORT ?? 3000), host);
187
- console.log(`${agent.name} listening on http://${host ?? "127.0.0.1"}:${server.port}`);
188
-
189
- for (const signal of ["SIGINT", "SIGTERM"]) {
190
- // A SYNCHRONOUS listener. An `async` one hands its promise to `process`,
191
- // which discards what a listener returns — so a `close()` that rejects would
192
- // surface as an unhandled rejection, i.e. a crash with a stack trace on
193
- // Ctrl-C, instead of the non-zero exit a failed shutdown should be.
194
- process.once(signal, () => {
195
- // close() shuts the runtime down too — no separate runtime.shutdown().
196
- server.close().then(
197
- () => process.exit(0),
198
- (error) => {
199
- console.error(`shutdown failed: ${error?.message ?? error}`);
200
- process.exit(1);
201
- },
202
- );
203
- });
204
- }