@alexkroman1/aai-cli 14.0.0 → 15.1.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 (45) hide show
  1. package/dist/_artifacts-BJOYGQPp.mjs +21 -0
  2. package/dist/_artifacts.d.ts +16 -0
  3. package/dist/_build-target.d.ts +172 -0
  4. package/dist/{_bundler-DolUCMxu.mjs → _bundler-DM0d0M7m.mjs} +1 -1
  5. package/dist/{_dev-server-CSMqF8PN.mjs → _dev-server-BzWB6-4y.mjs} +9 -6
  6. package/dist/_e2e-test-utils.d.ts +1 -1
  7. package/dist/{_init-CQ8idAwo.mjs → _init-Bsi3DZNJ.mjs} +1 -1
  8. package/dist/_server-common-De0haHr9.mjs +70 -0
  9. package/dist/_server-common.d.ts +20 -1
  10. package/dist/{_templates-CK4oKoeX.mjs → _templates-CIlJ3Vay.mjs} +1 -1
  11. package/dist/_templates.d.ts +1 -1
  12. package/dist/_vercel-output.d.ts +63 -0
  13. package/dist/build-BhEaxBPu.mjs +481 -0
  14. package/dist/build.d.ts +18 -10
  15. package/dist/cli.mjs +51 -23
  16. package/dist/{client-bundler-BJgREAh6.mjs → client-bundler-6mTLs6ny.mjs} +4 -4
  17. package/dist/client-bundler.d.ts +1 -1
  18. package/dist/client-bundler.mjs +1 -1
  19. package/dist/{deploy-uAJ4NukN.mjs → deploy-CGqPU5U-.mjs} +2 -2
  20. package/dist/{dev-DApPSaE_.mjs → dev-Bx9gYBHM.mjs} +1 -1
  21. package/dist/{eval-BK47A_K5.mjs → eval-B3I7FqN9.mjs} +1 -1
  22. package/dist/{init-DukDxECd.mjs → init-CQbj3ycf.mjs} +49 -39
  23. package/dist/init.d.ts +19 -10
  24. package/dist/scaffold/CLAUDE.md +73 -22
  25. package/dist/scaffold/package.json +6 -6
  26. package/dist/start.d.ts +112 -0
  27. package/dist/start.mjs +156 -0
  28. package/dist/{studio-CpHlNHUZ.mjs → studio-C_zuRC_z.mjs} +2 -2
  29. package/dist/templates/briefing-desk/agent.eval.test.ts +156 -0
  30. package/dist/templates/code-interpreter/agent.test.ts +103 -0
  31. package/dist/templates/link-digest/client.tsx +55 -3
  32. package/dist/templates/math-buddy/agent.test.ts +126 -0
  33. package/dist/templates/personal-finance/agent.test.ts +127 -0
  34. package/dist/templates/support-line/agent.ts +8 -0
  35. package/dist/templates/travel-concierge/routing.ts +64 -55
  36. package/dist/templates/travel-concierge/tools/cancel_action.ts +3 -1
  37. package/dist/templates/travel-concierge/tools/complete_or_escalate.ts +3 -1
  38. package/dist/templates/travel-concierge/tools/confirm_action.ts +3 -1
  39. package/dist/templates/web-researcher/agent.test.ts +130 -0
  40. package/dist/worker-bundler.d.ts +1 -1
  41. package/dist/worker-bundler.mjs +7 -7
  42. package/package.json +9 -4
  43. package/dist/_server-common-vILJp3it.mjs +0 -43
  44. package/dist/build-Mxk8gWvX.mjs +0 -108
  45. package/dist/scaffold/server.mjs +0 -308
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ import path from "node:path";
3
+ //#region src/_artifacts.ts
4
+ /**
5
+ * The `.aai/` layout — where `aai build` leaves what `aai start` and the
6
+ * deployment targets read back.
7
+ *
8
+ * A LEAF module, and that is the whole reason it exists. These two paths were
9
+ * declared where they were first needed (`build.ts`, `start.ts`), which was
10
+ * fine while the readers were the writers. `_vercel-output.ts` needs both and
11
+ * is imported BY `build.ts`, so taking them from there is an import cycle —
12
+ * one Biome rejects, and one that would fail at runtime rather than at build
13
+ * time, since a `const` read across a cycle is a `ReferenceError` decided by
14
+ * import order. Both original homes re-export, so no published subpath moved.
15
+ */
16
+ /** Where `aai build` leaves the worker bundle, relative to the project root. */
17
+ const WORKER_ARTIFACT_REL = path.join(".aai", "worker.mjs");
18
+ /** Where `aai build` leaves the built browser client, relative to the root. */
19
+ const CLIENT_ARTIFACT_REL = path.join(".aai", "client");
20
+ //#endregion
21
+ export { WORKER_ARTIFACT_REL as n, CLIENT_ARTIFACT_REL as t };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The `.aai/` layout — where `aai build` leaves what `aai start` and the
3
+ * deployment targets read back.
4
+ *
5
+ * A LEAF module, and that is the whole reason it exists. These two paths were
6
+ * declared where they were first needed (`build.ts`, `start.ts`), which was
7
+ * fine while the readers were the writers. `_vercel-output.ts` needs both and
8
+ * is imported BY `build.ts`, so taking them from there is an import cycle —
9
+ * one Biome rejects, and one that would fail at runtime rather than at build
10
+ * time, since a `const` read across a cycle is a `ReferenceError` decided by
11
+ * import order. Both original homes re-export, so no published subpath moved.
12
+ */
13
+ /** Where `aai build` leaves the worker bundle, relative to the project root. */
14
+ export declare const WORKER_ARTIFACT_REL: string;
15
+ /** Where `aai build` leaves the built browser client, relative to the root. */
16
+ export declare const CLIENT_ARTIFACT_REL: string;
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Deployment TARGETS — what `aai build` emits beside the worker so a host can
3
+ * run this project without the project holding anything host-specific.
4
+ *
5
+ * ## Why a target rather than a file in the scaffold
6
+ *
7
+ * Every host wants a different entry shape: Vercel wants a module whose default
8
+ * export is an `http.Server` it binds itself, a container wants a long-lived
9
+ * process, another platform wants something else again. Committing one of those
10
+ * to the scaffold makes every project assert a fact that is load-bearing on
11
+ * exactly one host — the same objection that removed `server.mjs`.
12
+ *
13
+ * Nitro is the worked precedent: one codebase, a preset per provider, and the
14
+ * preset EMITS the host's expected shape into the build directory. `node-server`
15
+ * is its default and Vercel/Netlify/Cloudflare are detected from the CI
16
+ * environment with no configuration. Next does the same narrower thing with
17
+ * `output: "standalone"` — it generates a `server.js` rather than asking anyone
18
+ * to write one. Either way the user's repository contains no host file, which is
19
+ * the property to preserve here.
20
+ *
21
+ * ## Auto-detection, and why it is safe
22
+ *
23
+ * A target is chosen with `--target`, or detected from the environment when the
24
+ * flag is absent. Detection reads the variables the hosts set on their own build
25
+ * containers ({@link TARGET_ENV_MARKERS}), so it only ever fires where the build
26
+ * is genuinely running on that host — a laptop sets none of them and gets
27
+ * {@link DEFAULT_BUILD_TARGET}, which emits nothing extra and is what every
28
+ * existing project already does.
29
+ */
30
+ /** The targets `aai build --target` accepts. */
31
+ export declare const BUILD_TARGETS: readonly ["node", "vercel"];
32
+ export type BuildTarget = (typeof BUILD_TARGETS)[number];
33
+ /**
34
+ * What a build with no `--target` and no host environment produces: the worker
35
+ * and the client, and nothing else.
36
+ *
37
+ * `node` rather than a `"none"` sentinel because it NAMES the deployment it
38
+ * serves — a long-lived process running `aai start` — which is what every
39
+ * container platform wants and what the scaffold's own `start` script runs.
40
+ */
41
+ export declare const DEFAULT_BUILD_TARGET: BuildTarget;
42
+ /**
43
+ * The environment variable each host sets on its own build container.
44
+ *
45
+ * `VERCEL` is set for every Vercel build and deployment. Detection is per host
46
+ * rather than a single "am I in CI" test, because a GitHub Action building a
47
+ * container image is CI too and wants the default.
48
+ */
49
+ export declare const TARGET_ENV_MARKERS: Readonly<Record<string, BuildTarget>>;
50
+ export declare function isBuildTarget(value: string): value is BuildTarget;
51
+ /**
52
+ * Resolve the target for this build: an explicit flag wins, then the
53
+ * environment, then {@link DEFAULT_BUILD_TARGET}.
54
+ *
55
+ * An unrecognised `--target` is REFUSED naming what is accepted, rather than
56
+ * falling back to the default — a typo'd target that silently built the default
57
+ * would deploy a project missing the entry its host needs, and the failure would
58
+ * arrive as a 404 from the platform rather than as an error from the build.
59
+ */
60
+ export declare function resolveBuildTarget(explicit: string | undefined, env?: Record<string, string | undefined>): BuildTarget;
61
+ /**
62
+ * Where a PREBUILT Vercel deployment lives, relative to the project root.
63
+ *
64
+ * The Build Output API rather than an `api/` entry plus a `vercel.json`, and
65
+ * the reason is ORDERING. Vercel reads `vercel.json` and decides what to build
66
+ * BEFORE it runs the build command, so a `vercel.json` that the build WRITES
67
+ * configures the NEXT deployment and not this one — a clean clone deploys with
68
+ * no rewrite and no function at all. The `api/` shape only ever appeared to
69
+ * work because a previous local `aai build --target vercel` had left both
70
+ * files in the working tree, which is a property of one laptop rather than of
71
+ * the repository. `.vercel/output/` is read AFTER the build command; it is the
72
+ * only place a build can describe its own deployment.
73
+ *
74
+ * Two more things fall out of owning the directory, both of which the `api/`
75
+ * shape got wrong and could not fix. The function bundle is ASSEMBLED here
76
+ * rather than traced, so `.aai/worker.mjs` — loaded through a dynamic
77
+ * `import(pathToFileURL(...))` that no static tracer can follow — and
78
+ * `.env.example` — the file that DECLARES which variables become `ctx.env` —
79
+ * are present because they were copied in. And the built client is served by
80
+ * the CDN out of `static/` instead of through the function.
81
+ *
82
+ * Nitro's vercel preset is the worked precedent and lands in exactly here:
83
+ * `output.dir = {{rootDir}}/.vercel/output`, `serverDir` a `.func` under it.
84
+ *
85
+ * @see https://vercel.com/docs/build-output-api/v3
86
+ */
87
+ export declare const VERCEL_OUTPUT_DIR: string;
88
+ /**
89
+ * The one function every request that is not a static file reaches.
90
+ *
91
+ * The Build Output API derives a function's ROUTE from its path, so the
92
+ * directory name IS a URL and must not collide with one the static output
93
+ * claims. **`index.func` collides**, which a deployment is the only way to
94
+ * find out: it is served at `/index`, and Vercel's directory index resolves
95
+ * `/` to the extensionless `/index` — so the function won `/`, every other
96
+ * asset came off the CDN correctly, and the home page 500'd on a deployment
97
+ * whose static output was perfect. Measured on a real preview:
98
+ * `/favicon.ico`, `/index.html` and both hashed `/assets/*` returned 200
99
+ * while `/` and `/index` did not.
100
+ *
101
+ * `__server` is Nitro's answer to the same problem (`__server.func`) and the
102
+ * reason is this one: a double-underscore prefix is not a path any bundler
103
+ * emits, so no static file can ever take the name.
104
+ */
105
+ export declare const VERCEL_FUNCTION_DIR: string;
106
+ /**
107
+ * The route {@link VERCEL_FUNCTION_DIR} is served at — its directory name
108
+ * without `.func`, which is how the Build Output API names a function.
109
+ */
110
+ export declare const VERCEL_FUNCTION_ROUTE = "/__server";
111
+ /** Static assets the Vercel CDN serves directly, never reaching the function. */
112
+ export declare const VERCEL_STATIC_DIR: string;
113
+ /** `nodejs<major>.x` for the Node running this build, clamped to what Vercel offers. */
114
+ export declare function vercelNodeRuntime(version?: string): string;
115
+ /**
116
+ * The Vercel function entry, emitted into {@link VERCEL_FUNCTION_DIR}.
117
+ *
118
+ * ## Why a `(req, res)` handler and not `export default server`
119
+ *
120
+ * `export default <http.Server>` is what Vercel's own `@vercel/node` BUILDER
121
+ * accepts, and it is the shape the previous `api/index.mjs` used. The Build
122
+ * Output API has no builder in the path: `launcherType: "Nodejs"` invokes the
123
+ * module's default export as a request handler, so the server never gets
124
+ * bound and there is nothing to raise an `upgrade` event on it.
125
+ *
126
+ * ## How a WebSocket survives that
127
+ *
128
+ * Vercel hands a Node function the raw upgrade through its PER-REQUEST
129
+ * context — `globalThis[Symbol.for("@vercel/request-context")].get()
130
+ * .upgradeWebSocket()` returns the `{ req, socket, head }` triple — rather
131
+ * than as an event. Nitro reaches it through `crossws/adapters/vercel`; here
132
+ * the adapter is three lines, because {@link AgentServer.node} is a real
133
+ * `http.Server` that already has both an `upgrade` and a `request` listener
134
+ * registered. Re-emitting onto it is the whole translation, and it means the
135
+ * deployed path through `server.ts` is the same one `aai dev` and `aai start`
136
+ * take — no second WebSocket entry point to keep in step.
137
+ *
138
+ * The `204` afterwards is what the launcher needs to consider the invocation
139
+ * finished; the socket the agent is now talking on is not this `res`.
140
+ *
141
+ * ## `import.meta.dirname`, not `process.cwd()`
142
+ *
143
+ * The function's working directory belongs to the platform, but `.aai/` and
144
+ * `.env.example` were copied in BESIDE this file. Resolving from the module
145
+ * keeps that a fact about the bundle rather than about how Vercel happens to
146
+ * invoke it.
147
+ */
148
+ export declare const VERCEL_ENTRY_SOURCE = "// Generated by `aai build --target vercel` \u2014 do not edit, and do not commit.\n// Vercel invokes this handler per request and delivers a WebSocket upgrade\n// through its request context. See @alexkroman1/aai-cli/start.\nimport { createProjectServer } from \"@alexkroman1/aai-cli/start\";\n\nconst server = (await createProjectServer({ cwd: import.meta.dirname })).node;\n\nconst REQUEST_CONTEXT = Symbol.for(\"@vercel/request-context\");\n\nexport default function handler(req, res) {\n if (req.method === \"GET\" && req.headers.upgrade?.toLowerCase() === \"websocket\") {\n const upgrade = globalThis[REQUEST_CONTEXT]?.get?.()?.upgradeWebSocket?.();\n if (upgrade) {\n server.emit(\"upgrade\", upgrade.req, upgrade.socket, upgrade.head);\n if (!res.headersSent && !res.writableEnded) {\n res.statusCode = 204;\n res.end();\n }\n return;\n }\n }\n server.emit(\"request\", req, res);\n}\n";
149
+ /**
150
+ * `.vc-config.json` — how the platform runs {@link VERCEL_ENTRY_SOURCE}.
151
+ *
152
+ * `supportsResponseStreaming` is not optional here: an agent streams TTS audio
153
+ * and SSE workflow events, and without it the platform buffers a response to
154
+ * completion, which for a stream that ends when the call does means it never
155
+ * arrives. `shouldAddHelpers` stays off — the entry speaks `node:http`, and
156
+ * the helpers exist to bolt Express-shaped sugar onto a handler that does not.
157
+ */
158
+ export declare function vercelFunctionConfigSource(runtime?: string): string;
159
+ /**
160
+ * `config.json` — the routing table, and the reason static assets stop paying
161
+ * for a function invocation.
162
+ *
163
+ * `{ "handle": "filesystem" }` serves anything present in
164
+ * {@link VERCEL_STATIC_DIR} from the CDN and only then falls through, so the
165
+ * client bundle, its assets and the worklets are edge-served while
166
+ * `/client-config`, `/websocket`, `/workflows/*` and the webhook route reach
167
+ * the agent. The `api/` shape routed EVERY request through the function, which
168
+ * this file's earlier revision noted as deliberate and "not what makes a
169
+ * deployment work or not" — true of correctness, false of cost, and free here
170
+ * because the Build Output API already separates the two directories.
171
+ */
172
+ export declare const VERCEL_BUILD_CONFIG_SOURCE: string;
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { h as validateAgentExport } from "./_utils-D5JGcjiW.mjs";
3
- import { t as buildClient } from "./client-bundler-BJgREAh6.mjs";
3
+ import { t as buildClient } from "./client-bundler-6mTLs6ny.mjs";
4
4
  import { buildWorker } from "./worker-bundler.mjs";
5
5
  import path from "node:path";
6
6
  import { pathToFileURL } from "node:url";
@@ -1,21 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import { i as outputSilenced, r as notify } from "./_ui-DCt4qZrk.mjs";
3
3
  import { a as errorCode, s as errorMessage$1 } from "./_utils-D5JGcjiW.mjs";
4
- import { n as fallbackHtmlPlugin } from "./client-bundler-BJgREAh6.mjs";
4
+ import { n as fallbackHtmlPlugin } from "./client-bundler-6mTLs6ny.mjs";
5
5
  import { t as DEDUPED_PEERS } from "./_vite-env-kejF0p2L.mjs";
6
6
  import { buildWorker } from "./worker-bundler.mjs";
7
7
  import { typecheckProject } from "./typecheck.mjs";
8
8
  import { n as ensureApiKey } from "./_config-DYzC6WMD.mjs";
9
- import { n as createWorkerEvaluator } from "./_bundler-DolUCMxu.mjs";
10
- import { t as resolveServerEnv } from "./_server-common-vILJp3it.mjs";
9
+ import { n as resolveServerEnv } from "./_server-common-De0haHr9.mjs";
10
+ import { n as createWorkerEvaluator } from "./_bundler-DM0d0M7m.mjs";
11
11
  import { existsSync, statSync } from "node:fs";
12
12
  import path from "node:path";
13
13
  import { omitUndefined, plural } from "@alexkroman1/aai/utils";
14
14
  import { createCoalescingRunner, requestPath, sleep } from "@alexkroman1/aai/internal";
15
- import { agentConfigWarnings } from "@alexkroman1/aai/manifest";
16
15
  import { DEFAULT_LISTEN_HOST, WORKFLOW_API_PREFIX, createRuntime, createRuntimeServer, ensureSessionStateSchema, ensureWorkflowJournalSchema, requiredProviderEnvVars, withHostCredentialFallback } from "@alexkroman1/aai-runtime";
17
- import { WORKFLOW_DATA_DIR_ENV, consoleLogger, createMemoryJournal, handleWorkflowRequest, isPathInside, publishStepEnv } from "@alexkroman1/aai-runtime/internal";
18
16
  import { defaultClientDir } from "@alexkroman1/aai-ui/client-dir";
17
+ import { agentConfigWarnings } from "@alexkroman1/aai/manifest";
18
+ import { WORKFLOW_DATA_DIR_ENV, consoleLogger, createMemoryJournal, handleWorkflowRequest, isPathInside, publishStepEnv } from "@alexkroman1/aai-runtime/internal";
19
19
  import { watch } from "chokidar";
20
20
  import getPort, { portNumbers } from "get-port";
21
21
  import pDebounce from "p-debounce";
@@ -638,7 +638,10 @@ async function startDevServer(opts) {
638
638
  env: hostModeEnv(providerEnv),
639
639
  hostBaseAgent: agentDef,
640
640
  greeting: agentDef.greeting,
641
- ...omitUndefined({ page: agentDef.page }),
641
+ ...omitUndefined({
642
+ page: agentDef.page,
643
+ telephony: agentDef.telephony
644
+ }),
642
645
  request: (req, res, url, method) => handleWorkflowRequest(req, res, url, method, {
643
646
  deliver: () => runtime.deliverWorkflow,
644
647
  logger: devLogger
@@ -61,7 +61,7 @@ export declare function waitForHealth(url: string, child?: ChildProcess, timeout
61
61
  export declare function waitForExit(child: ChildProcess, timeoutMs?: number): Promise<void>;
62
62
  /**
63
63
  * Run a scaffolded project the way a self-hoster does — `npm start`, which runs
64
- * the project's own `prestart` (`aai build`) and then `server.mjs`.
64
+ * the project's own `prestart` (`aai build`) and then `aai start`.
65
65
  *
66
66
  * Extracted because there are two legs now and the spawn is the fiddly half: the
67
67
  * port has to be read off stdout, and every way this can fail — a build error, a
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { d as readJson, g as writeJson, i as compareCodeUnits, u as isEexist } from "./_utils-D5JGcjiW.mjs";
3
3
  import { r as isDevMode, t as getMonorepoRoot } from "./_agent-BzUeqOdj.mjs";
4
- import { REPO_URL, downloadAndMergeTemplate } from "./_templates-CK4oKoeX.mjs";
4
+ import { REPO_URL, downloadAndMergeTemplate } from "./_templates-CIlJ3Vay.mjs";
5
5
  import path from "node:path";
6
6
  import fs from "node:fs/promises";
7
7
  //#region src/_init.ts
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
2
+ import { a as errorCode } from "./_utils-D5JGcjiW.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
+ * One dotenv file's entries, or `{}` when it is not there.
9
+ *
10
+ * An ABSENT file is normal — `.env` is gitignored and a container usually ships
11
+ * neither — while an UNREADABLE one is not: the agent would boot with no
12
+ * secrets and fail later as an opaque provider auth error, so that one throws.
13
+ */
14
+ async function readDotenv(file) {
15
+ try {
16
+ return parseEnv(await fs.readFile(file, "utf-8"));
17
+ } catch (err) {
18
+ if (errorCode(err) !== "ENOENT") throw err;
19
+ return {};
20
+ }
21
+ }
22
+ /**
23
+ * The dotenv files a DEPLOYMENT reads, in precedence order.
24
+ *
25
+ * `.env.example` counts as a DECLARATION here and nowhere else, which is what
26
+ * lets a container run with no `.env` at all: that file is committed, it names
27
+ * the secrets the agent needs, and `docker run -e MY_API_KEY=...` supplies the
28
+ * values — the shell always winning over a file entry, and a declared-but-empty
29
+ * value being dropped rather than kept (see below). Without it a deployment
30
+ * that ships no `.env` — which is every correctly-built image — resolves an
31
+ * empty agent env and fails later as an opaque provider auth error.
32
+ *
33
+ * `aai dev` deliberately reads `.env` alone. There the developer HAS the file,
34
+ * and treating the example as a declaration would surface a key they have not
35
+ * filled in yet as one they have.
36
+ */
37
+ const DEPLOY_ENV_FILES = [".env.example", ".env"];
38
+ /**
39
+ * Build the `ctx.env` record that agent tools will see at runtime.
40
+ *
41
+ * Only variables explicitly declared in `.env` are included — matching
42
+ * the platform sandbox behavior where `ctx.env`
43
+ * contains only secrets set via `aai secret put`. This prevents agents
44
+ * from accidentally depending on shell-level vars (PATH, HOME, etc.) that
45
+ * won't exist in production.
46
+ *
47
+ * Values are resolved by merging the `.env` file with the current
48
+ * environment — existing shell exports take precedence over `.env`
49
+ * defaults, without mutating `process.env`.
50
+ *
51
+ * @param cwd - Project directory containing `.env` (optional).
52
+ * @param baseEnv - Override the environment to read values from (tests only).
53
+ * @param files - Which dotenv files to read, later entries winning over
54
+ * earlier. Defaults to `.env` alone; {@link DEPLOY_ENV_FILES} is what a
55
+ * DEPLOYMENT reads, and its own doc says why the two differ.
56
+ */
57
+ async function resolveServerEnv(cwd, baseEnv, files = [".env"]) {
58
+ const fileEntries = {};
59
+ const root = cwd;
60
+ if (root !== void 0) for (const file of files) Object.assign(fileEntries, await readDotenv(path.join(root, file)));
61
+ const source = baseEnv ?? process.env;
62
+ const env = {};
63
+ for (const [key, fileVal] of Object.entries(fileEntries)) {
64
+ const val = source[key] ?? fileVal;
65
+ if (val !== void 0 && val !== "") env[key] = val;
66
+ }
67
+ return env;
68
+ }
69
+ //#endregion
70
+ export { resolveServerEnv as n, DEPLOY_ENV_FILES as t };
@@ -1,3 +1,19 @@
1
+ /**
2
+ * The dotenv files a DEPLOYMENT reads, in precedence order.
3
+ *
4
+ * `.env.example` counts as a DECLARATION here and nowhere else, which is what
5
+ * lets a container run with no `.env` at all: that file is committed, it names
6
+ * the secrets the agent needs, and `docker run -e MY_API_KEY=...` supplies the
7
+ * values — the shell always winning over a file entry, and a declared-but-empty
8
+ * value being dropped rather than kept (see below). Without it a deployment
9
+ * that ships no `.env` — which is every correctly-built image — resolves an
10
+ * empty agent env and fails later as an opaque provider auth error.
11
+ *
12
+ * `aai dev` deliberately reads `.env` alone. There the developer HAS the file,
13
+ * and treating the example as a declaration would surface a key they have not
14
+ * filled in yet as one they have.
15
+ */
16
+ export declare const DEPLOY_ENV_FILES: readonly [".env.example", ".env"];
1
17
  /**
2
18
  * Build the `ctx.env` record that agent tools will see at runtime.
3
19
  *
@@ -13,5 +29,8 @@
13
29
  *
14
30
  * @param cwd - Project directory containing `.env` (optional).
15
31
  * @param baseEnv - Override the environment to read values from (tests only).
32
+ * @param files - Which dotenv files to read, later entries winning over
33
+ * earlier. Defaults to `.env` alone; {@link DEPLOY_ENV_FILES} is what a
34
+ * DEPLOYMENT reads, and its own doc says why the two differ.
16
35
  */
17
- export declare function resolveServerEnv(cwd?: string, baseEnv?: Record<string, string | undefined>): Promise<Record<string, string>>;
36
+ export declare function resolveServerEnv(cwd?: string, baseEnv?: Record<string, string | undefined>, files?: readonly string[]): Promise<Record<string, string>>;
@@ -119,7 +119,7 @@ async function readJsonFile(file) {
119
119
  }
120
120
  /**
121
121
  * Directory holding the base scaffold — the files every project gets
122
- * underneath its template (package.json, tsconfig, `server.mjs`, …).
122
+ * underneath its template (package.json, tsconfig, `.gitignore`, …).
123
123
  *
124
124
  * The scaffold is the single definition of the self-hosted entrypoint, so a
125
125
  * caller needing one of its files resolves it here rather than carrying a
@@ -37,7 +37,7 @@ type Manifest = Record<string, unknown>;
37
37
  export declare function mergeScaffoldManifest(existing: Manifest, scaffold: Manifest): Manifest | null;
38
38
  /**
39
39
  * Directory holding the base scaffold — the files every project gets
40
- * underneath its template (package.json, tsconfig, `server.mjs`, …).
40
+ * underneath its template (package.json, tsconfig, `.gitignore`, …).
41
41
  *
42
42
  * The scaffold is the single definition of the self-hosted entrypoint, so a
43
43
  * caller needing one of its files resolves it here rather than carrying a
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Assemble a PREBUILT Vercel deployment — `.vercel/output/` — from what
3
+ * `aai build` just produced.
4
+ *
5
+ * The argument for the Build Output API over an `api/` entry is in
6
+ * `_build-target.ts` at {@link VERCEL_OUTPUT_DIR}; what this module adds is the
7
+ * consequence of it. A `.func` directory is a directory WE fill, so every file
8
+ * the server reads at runtime is present because it was copied in, and nothing
9
+ * depends on a static tracer following a path it structurally cannot:
10
+ *
11
+ * - **`.aai/worker.mjs`** is loaded through `import(pathToFileURL(...))`
12
+ * (`start.ts`), which `@vercel/nft` cannot resolve. Under the `api/` shape
13
+ * the build was green and the function 500'd on its first request with
14
+ * "No built agent at .aai/worker.mjs".
15
+ * - **`.env.example`** is not documentation here. `resolveServerEnv` treats it
16
+ * as the DECLARATION of which variables become `ctx.env` (see
17
+ * `DEPLOY_ENV_FILES`), so a function without it hands every tool an empty
18
+ * env while the Vercel project has the values set — a failure that looks
19
+ * like a credential problem and is a packaging one.
20
+ * - **The client** is copied to `static/`, where the CDN serves it, and also
21
+ * beside the worker, so `resolveClientDir` finds a real directory rather
22
+ * than reaching into a `node_modules` that the bundle replaced.
23
+ *
24
+ * The entry is BUNDLED rather than shipped with a `node_modules`. Same call as
25
+ * the worker (`ssr: { noExternal: true }`, `root: cwd`), so it resolves the
26
+ * project's own installed SDK and pulls the runtime, `ws` and `pg` in with it
27
+ * — which is already the arrangement a deployed worker runs under, and it is
28
+ * what makes the function independent of whether the host's install left a
29
+ * usable, hoisted `node_modules` behind.
30
+ */
31
+ /** Options for {@link emitVercelOutput}. */
32
+ export interface EmitVercelOutputOptions {
33
+ /**
34
+ * Produce the function's `index.mjs`. Defaults to {@link bundleEntry}.
35
+ *
36
+ * Injectable so the ASSEMBLY — which file lands where, and which does not —
37
+ * can be asserted without a ~15s rolldown pass over the whole runtime. The
38
+ * bundle has its own contract and its own (scenario-tier) test; what this
39
+ * seam separates is a question about a directory from a question about a
40
+ * bundler.
41
+ */
42
+ bundle?: (cwd: string) => Promise<string>;
43
+ }
44
+ /**
45
+ * Write `.vercel/output/` for this project.
46
+ *
47
+ * The directory is REMOVED first. It is not addressed by content, so a
48
+ * function or a static asset left by an earlier build with a different shape
49
+ * would be deployed alongside this one — and `vercel deploy --prebuilt`
50
+ * uploads whatever is there.
51
+ */
52
+ export declare function emitVercelOutput(cwd: string, options?: EmitVercelOutputOptions): Promise<void>;
53
+ /**
54
+ * Bundle {@link VERCEL_ENTRY_SOURCE} and everything it imports into one ESM
55
+ * file.
56
+ *
57
+ * The entry is written INTO the project rather than a temp directory, because
58
+ * that is what makes `@alexkroman1/aai-cli/start` resolve against the user's
59
+ * install — the deployed server is then the version their lockfile pins, which
60
+ * is the same guarantee `aai publish` gives. Removed in a `finally`: a build
61
+ * that throws must not leave a file that looks authored.
62
+ */
63
+ export declare function bundleEntry(cwd: string): Promise<string>;