@alexkroman1/aai-cli 5.1.0 → 5.2.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 (119) hide show
  1. package/dist/{_agent-D39qatZJ.mjs → _agent-D8zBb3M5.mjs} +2 -3
  2. package/dist/_agent.d.ts +39 -0
  3. package/dist/{_api-client-a6cPMebU.mjs → _api-client-MenP4-O7.mjs} +3 -1
  4. package/dist/_api-client.d.ts +37 -0
  5. package/dist/{_bundler-DdmuZzeR.mjs → _bundler-DFS4xxqE.mjs} +3 -13
  6. package/dist/_bundler.d.ts +46 -0
  7. package/dist/{_config-Czjhvaax.mjs → _config-D3F9km8X.mjs} +3 -3
  8. package/dist/_config.d.ts +57 -0
  9. package/dist/_default-html.d.ts +12 -0
  10. package/dist/_deploy.d.ts +28 -0
  11. package/dist/{_dev-server-BCbEUhmv.mjs → _dev-server-BBUWo0sb.mjs} +12 -14
  12. package/dist/_dev-server.d.ts +74 -0
  13. package/dist/{_init-H-lw7czL.mjs → _init-DU-sXH6S.mjs} +47 -65
  14. package/dist/_init.d.ts +7 -0
  15. package/dist/_mock-api.d.ts +22 -0
  16. package/dist/_mock-registry.d.ts +17 -0
  17. package/dist/_output.d.ts +50 -0
  18. package/dist/{_server-common-B75oco06.mjs → _server-common-CnaP_Urf.mjs} +2 -1
  19. package/dist/_server-common.d.ts +17 -0
  20. package/dist/_slug-api-19R5kZ0U.mjs +27 -0
  21. package/dist/_slug-api.d.ts +15 -0
  22. package/dist/_templates.d.ts +21 -0
  23. package/dist/{_typecheck-gate-B1GcKZX-.mjs → _typecheck-gate-9IHWDnl1.mjs} +2 -2
  24. package/dist/_typecheck-gate.d.ts +7 -0
  25. package/dist/{_ui-DXZ9prrM.mjs → _ui-8kOEB-JH.mjs} +6 -3
  26. package/dist/_ui.d.ts +22 -0
  27. package/dist/{_utils-C502jKo8.mjs → _utils-Ch0J4s6a.mjs} +17 -1
  28. package/dist/_utils.d.ts +48 -0
  29. package/dist/_vite-env.d.ts +8 -0
  30. package/dist/build-Xq1xxoSg.mjs +38 -0
  31. package/dist/build.d.ts +16 -0
  32. package/dist/cli.d.ts +1 -0
  33. package/dist/cli.mjs +35 -52
  34. package/dist/{client-bundler-DO3GgO6p.mjs → client-bundler-BWDkUeEP.mjs} +5 -6
  35. package/dist/client-bundler.d.ts +21 -0
  36. package/dist/client-bundler.mjs +1 -1
  37. package/dist/{delete-K24hW1ab.mjs → delete-kSXFLxek.mjs} +4 -4
  38. package/dist/delete.d.ts +18 -0
  39. package/dist/{deploy-D-dnt9L8.mjs → deploy-Ci0X1gp2.mjs} +8 -8
  40. package/dist/deploy.d.ts +15 -0
  41. package/dist/{dev-C8o-p13B.mjs → dev-g7sGt8qX.mjs} +3 -3
  42. package/dist/dev.d.ts +13 -0
  43. package/dist/{init-B53rR3yD.mjs → init-BjeK8crW.mjs} +10 -17
  44. package/dist/init.d.ts +24 -0
  45. package/dist/scaffold/.env.example +11 -0
  46. package/dist/scaffold/CLAUDE.md +919 -0
  47. package/dist/scaffold/global.d.ts +1 -0
  48. package/dist/scaffold/package.json +32 -0
  49. package/dist/scaffold/pnpm-workspace.yaml +13 -0
  50. package/dist/scaffold/tsconfig.json +19 -0
  51. package/dist/scaffold/vite.config.ts +14 -0
  52. package/dist/scaffold/vitest.config.ts +24 -0
  53. package/dist/{secret-C4unwCEw.mjs → secret-DOva9OGk.mjs} +21 -20
  54. package/dist/secret.d.ts +25 -0
  55. package/dist/{storage-tZM1J8qj.mjs → storage-vM6HjDZZ.mjs} +6 -13
  56. package/dist/storage.d.ts +22 -0
  57. package/dist/templates/code-interpreter/agent.ts +11 -0
  58. package/dist/templates/code-interpreter/system-prompt.md +18 -0
  59. package/dist/templates/dispatch-center/agent.test.ts +153 -0
  60. package/dist/templates/dispatch-center/agent.ts +50 -0
  61. package/dist/templates/dispatch-center/client.tsx +378 -0
  62. package/dist/templates/dispatch-center/shared.ts +631 -0
  63. package/dist/templates/dispatch-center/system-prompt.md +39 -0
  64. package/dist/templates/dispatch-center/tools/incident_add_note.ts +27 -0
  65. package/dist/templates/dispatch-center/tools/incident_create.ts +79 -0
  66. package/dist/templates/dispatch-center/tools/incident_escalate.ts +88 -0
  67. package/dist/templates/dispatch-center/tools/incident_get.ts +36 -0
  68. package/dist/templates/dispatch-center/tools/incident_triage.ts +78 -0
  69. package/dist/templates/dispatch-center/tools/incident_update_status.ts +68 -0
  70. package/dist/templates/dispatch-center/tools/ops_dashboard.ts +52 -0
  71. package/dist/templates/dispatch-center/tools/ops_protocols.ts +27 -0
  72. package/dist/templates/dispatch-center/tools/ops_run_scenario.ts +149 -0
  73. package/dist/templates/dispatch-center/tools/resources_dispatch.ts +97 -0
  74. package/dist/templates/dispatch-center/tools/resources_get_available.ts +36 -0
  75. package/dist/templates/dispatch-center/tools/resources_update_status.ts +62 -0
  76. package/dist/templates/embedded-assets/agent.ts +55 -0
  77. package/dist/templates/embedded-assets/knowledge.json +20 -0
  78. package/dist/templates/health-assistant/agent.ts +174 -0
  79. package/dist/templates/health-assistant/system-prompt.md +17 -0
  80. package/dist/templates/infocom-adventure/agent.ts +119 -0
  81. package/dist/templates/infocom-adventure/client.tsx +279 -0
  82. package/dist/templates/infocom-adventure/shared.ts +38 -0
  83. package/dist/templates/infocom-adventure/system-prompt.md +43 -0
  84. package/dist/templates/math-buddy/agent.ts +16 -0
  85. package/dist/templates/math-buddy/system-prompt.md +12 -0
  86. package/dist/templates/night-owl/agent.ts +76 -0
  87. package/dist/templates/night-owl/client.tsx +130 -0
  88. package/dist/templates/night-owl/shared.ts +5 -0
  89. package/dist/templates/personal-finance/agent.ts +11 -0
  90. package/dist/templates/personal-finance/system-prompt.md +17 -0
  91. package/dist/templates/pipeline-simple/agent.test.ts +32 -0
  92. package/dist/templates/pipeline-simple/agent.ts +13 -0
  93. package/dist/templates/pizza-ordering/agent.test.ts +235 -0
  94. package/dist/templates/pizza-ordering/agent.ts +174 -0
  95. package/dist/templates/pizza-ordering/client.tsx +120 -0
  96. package/dist/templates/pizza-ordering/shared.ts +142 -0
  97. package/dist/templates/pizza-ordering/system-prompt.md +17 -0
  98. package/dist/templates/simple/agent.ts +5 -0
  99. package/dist/templates/solo-rpg/agent.test.ts +477 -0
  100. package/dist/templates/solo-rpg/agent.ts +37 -0
  101. package/dist/templates/solo-rpg/client.tsx +809 -0
  102. package/dist/templates/solo-rpg/shared.ts +747 -0
  103. package/dist/templates/solo-rpg/system-prompt.md +115 -0
  104. package/dist/templates/solo-rpg/tools/action_roll.ts +98 -0
  105. package/dist/templates/solo-rpg/tools/burn_momentum.ts +69 -0
  106. package/dist/templates/solo-rpg/tools/check_state.ts +11 -0
  107. package/dist/templates/solo-rpg/tools/load_game.ts +25 -0
  108. package/dist/templates/solo-rpg/tools/oracle.ts +248 -0
  109. package/dist/templates/solo-rpg/tools/save_game.ts +20 -0
  110. package/dist/templates/solo-rpg/tools/setup_character.ts +208 -0
  111. package/dist/templates/solo-rpg/tools/update_state.ts +170 -0
  112. package/dist/templates/web-researcher/agent.ts +11 -0
  113. package/dist/{test-CgWhus_Z.mjs → test-3Gq4pqH_.mjs} +5 -7
  114. package/dist/test.d.ts +39 -0
  115. package/dist/tsdown.config.d.ts +2 -0
  116. package/dist/typecheck.d.ts +26 -0
  117. package/dist/typecheck.mjs +6 -10
  118. package/dist/worker-bundler.d.ts +29 -0
  119. package/package.json +9 -6
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as serverOrigin, i as readProjectConfig, n as ensureApiKey, r as readGlobalConfig, t as approveServer } from "./_config-Czjhvaax.mjs";
2
+ import { a as serverOrigin, i as readProjectConfig, n as ensureApiKey, r as readGlobalConfig, t as approveServer } from "./_config-D3F9km8X.mjs";
3
3
  import { existsSync } from "node:fs";
4
4
  import path from "node:path";
5
5
  import { fileURLToPath } from "node:url";
@@ -71,8 +71,7 @@ function resolveServerUrl(explicit, configUrl, approvedOrigins = []) {
71
71
  * the project has never been deployed), server URL, and API key.
72
72
  */
73
73
  async function resolveDeployTarget(cwd, explicitServer) {
74
- const config = await readProjectConfig(cwd);
75
- const globalConfig = await readGlobalConfig();
74
+ const [config, globalConfig] = await Promise.all([readProjectConfig(cwd), readGlobalConfig()]);
76
75
  const serverUrl = resolveServerUrl(explicitServer, config?.serverUrl, globalConfig.approvedServers ?? []);
77
76
  if (explicitServer) await approveServer(serverUrl);
78
77
  return {
@@ -0,0 +1,39 @@
1
+ export declare const DEFAULT_SERVER = "https://alexkroman--aai-server-web-server.modal.run";
2
+ export declare const DEFAULT_DEV_SERVER = "http://localhost:8080";
3
+ export declare function getMonorepoRoot(): string | null;
4
+ export declare function isDevMode(): boolean;
5
+ /**
6
+ * Resolve which platform server to talk to.
7
+ *
8
+ * Precedence: an explicit `--server` flag, then dev mode, then the project
9
+ * config, then the shipped default.
10
+ *
11
+ * `configUrl` comes from `.aai/project.json` — a file in the working tree, so
12
+ * a cloned repo controls it. Because callers pair this URL with the user's API
13
+ * key (and, for `aai secret`, with secret values), a config-supplied origin is
14
+ * only honored when it is implicitly trusted or previously approved by the
15
+ * user via `--server`. Otherwise a repo could redirect a credentialed request
16
+ * to a host of its choosing simply by shipping a `project.json`, and
17
+ * `aai deploy` would hand over the developer's key on first run.
18
+ *
19
+ * @param approvedOrigins - Origins from the user-owned global config.
20
+ */
21
+ export declare function resolveServerUrl(explicit?: string, configUrl?: string, approvedOrigins?: readonly string[]): string;
22
+ /**
23
+ * Resolve everything needed to talk to the platform: project config (null if
24
+ * the project has never been deployed), server URL, and API key.
25
+ */
26
+ export declare function resolveDeployTarget(cwd: string, explicitServer?: string): Promise<{
27
+ config: {
28
+ slug: string;
29
+ serverUrl: string;
30
+ } | null;
31
+ serverUrl: string;
32
+ apiKey: string;
33
+ }>;
34
+ /** Like resolveDeployTarget, but requires an existing deployment (project config). */
35
+ export declare function getServerInfo(cwd: string, explicitServer?: string): Promise<{
36
+ serverUrl: string;
37
+ slug: string;
38
+ apiKey: string;
39
+ }>;
@@ -9,6 +9,8 @@ import { FetchError, ofetch } from "ofetch";
9
9
  * 5xx/429) are retried before surfacing an error.
10
10
  */
11
11
  const HINT_INVALID_API_KEY = "Your API key may be invalid. Run `aai` to re-enter your AssemblyAI API key.";
12
+ /** 404 hint for requests scoped to a deployed agent's slug. */
13
+ const HINT_NOT_DEPLOYED = "The agent may not be deployed. Check `.aai/project.json` for the correct slug.";
12
14
  /**
13
15
  * Send an authenticated request to the platform API and return the parsed
14
16
  * JSON response. Throws a descriptive error with status-specific hints on
@@ -44,4 +46,4 @@ function toApiError(err, url, opts) {
44
46
  return new Error(`${opts.action} failed: could not reach ${url}\n ${hint}`, { cause });
45
47
  }
46
48
  //#endregion
47
- export { apiRequest as t };
49
+ export { apiRequest as n, HINT_NOT_DEPLOYED as t };
@@ -0,0 +1,37 @@
1
+ export declare const HINT_INVALID_API_KEY = "Your API key may be invalid. Run `aai` to re-enter your AssemblyAI API key.";
2
+ /** 404 hint for requests scoped to a deployed agent's slug. */
3
+ export declare const HINT_NOT_DEPLOYED = "The agent may not be deployed. Check `.aai/project.json` for the correct slug.";
4
+ export type ApiRequestOptions = {
5
+ apiKey: string;
6
+ /** Verb used in error messages, e.g. "deploy". */
7
+ action: string;
8
+ method?: "GET" | "POST" | "PUT" | "DELETE";
9
+ /**
10
+ * Request body. Plain objects are JSON-serialized by ofetch (with
11
+ * Content-Type set); binary bodies (e.g. a pre-gzipped Buffer) pass
12
+ * through untouched — set Content-Type/Content-Encoding via `headers`.
13
+ */
14
+ body?: unknown;
15
+ /** Extra request headers, merged with the built-in Authorization header. */
16
+ headers?: Record<string, string>;
17
+ /** Extra error hints keyed by HTTP status. The 401 hint is built in. */
18
+ hints?: Record<number, string>;
19
+ /**
20
+ * Transient-failure retry count (default 2). Pass 0 for requests that are
21
+ * not idempotent server-side — a retry of a request that succeeded but lost
22
+ * its response would perform the action twice (e.g. a first deploy with no
23
+ * slug creates a fresh agent per attempt).
24
+ */
25
+ retry?: number;
26
+ /** Delay between retries in ms (default 300). Tests pass 0 so retry-path
27
+ * assertions don't sleep real wall-clock time. */
28
+ retryDelay?: number;
29
+ /** Optional fetch implementation for testing. Defaults to globalThis.fetch. */
30
+ fetch?: typeof globalThis.fetch;
31
+ };
32
+ /**
33
+ * Send an authenticated request to the platform API and return the parsed
34
+ * JSON response. Throws a descriptive error with status-specific hints on
35
+ * failure (the 401 hint is always included; pass more via `hints`).
36
+ */
37
+ export declare function apiRequest<T = unknown>(url: string, opts: ApiRequestOptions): Promise<T>;
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { n as log, u as ok } from "./_ui-DXZ9prrM.mjs";
3
- import { c as validateAgentExport } from "./_utils-C502jKo8.mjs";
4
- import { t as buildClient } from "./client-bundler-DO3GgO6p.mjs";
2
+ import { u as validateAgentExport } from "./_utils-Ch0J4s6a.mjs";
3
+ import { t as buildClient } from "./client-bundler-BWDkUeEP.mjs";
5
4
  import { buildWorker } from "./worker-bundler.mjs";
6
5
  import path from "node:path";
7
6
  import { pathToFileURL } from "node:url";
@@ -79,14 +78,5 @@ function createWorkerEvaluator() {
79
78
  return agentDef;
80
79
  };
81
80
  }
82
- async function executeBuild(cwd) {
83
- const bundle = await buildAgentBundle(cwd, { minify: true });
84
- const agentDef = await evalWorkerBundle(bundle.worker);
85
- log.success("Build complete");
86
- return ok({
87
- name: agentDef.name,
88
- workerBytes: bundle.worker.length
89
- });
90
- }
91
81
  //#endregion
92
- export { buildAgentBundle, createWorkerEvaluator, executeBuild };
82
+ export { createWorkerEvaluator as n, evalWorkerBundle as r, buildAgentBundle as t };
@@ -0,0 +1,46 @@
1
+ import type { AgentDef } from "@alexkroman1/aai";
2
+ import { type BuildWorkerOptions } from "./worker-bundler.ts";
3
+ /** Output from the bundler: worker ESM + client files. */
4
+ export type DirectoryBundleOutput = {
5
+ /** ESM bundle of agent.ts (tool execute functions + hook handlers). */
6
+ worker: string;
7
+ /** Static client files from Vite build. Empty if no client.tsx. */
8
+ clientFiles: Record<string, string>;
9
+ };
10
+ /**
11
+ * Bundle an agent directory: build agent.ts into worker ESM + client files.
12
+ *
13
+ * agent.ts is the single entry point: `export default agent({...})`. The
14
+ * worker self-describes (it exports `__aaiConfig` — see `worker-bundler.ts`),
15
+ * so nothing here evaluates the bundle: the server extracts the config inside
16
+ * a guest sandbox at deploy time.
17
+ */
18
+ export declare function buildAgentBundle(cwd: string, opts?: BuildWorkerOptions): Promise<DirectoryBundleOutput>;
19
+ /**
20
+ * Import the worker ESM from a uniquely named temp file and return the
21
+ * AgentDef default export. A real `file:` URL, not a `data:` URL: deploy
22
+ * bundles ship the SDK runtime, whose CJS interop calls
23
+ * `createRequire(import.meta.url)` — which rejects anything that isn't a
24
+ * file URL or absolute path. (The guest harness imports bundles the same
25
+ * way, for the same reason.) The file is removed after import; the module
26
+ * lives on in memory.
27
+ *
28
+ * Each call imports a unique URL, and Node's ESM registry never evicts — so
29
+ * every call retains one bundle for the process lifetime. That is fine for
30
+ * one-shot commands (`aai build`); long-lived callers must go through
31
+ * `createWorkerEvaluator` to at least dedupe identical builds. Evaluating in
32
+ * a discardable context is not an option: tool `execute` functions from the
33
+ * returned AgentDef are called in-process by the dev runtime, which rules
34
+ * out worker threads, and `node:vm` ESM evaluation is still flagged
35
+ * experimental.
36
+ */
37
+ export declare function evalWorkerBundle(code: string): Promise<AgentDef>;
38
+ /**
39
+ * Memoizing wrapper around `evalWorkerBundle` for long-lived callers (the
40
+ * dev server): byte-identical worker code returns the previously evaluated
41
+ * AgentDef without touching the ESM registry. No-op saves and formatter
42
+ * churn are the common watcher events, so this caps the registry leak (see
43
+ * `evalWorkerBundle`) to genuinely-new bundles — the residual one-module-per-
44
+ * distinct-build leak is accepted for the reasons documented there.
45
+ */
46
+ export declare function createWorkerEvaluator(): (code: string) => Promise<AgentDef>;
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { a as unwrapCancel, n as log, o as CliError } from "./_ui-DXZ9prrM.mjs";
3
- import { l as writeJson, o as readJson, r as errorMessage } from "./_utils-C502jKo8.mjs";
2
+ import { a as unwrapCancel, n as log, o as CliError } from "./_ui-8kOEB-JH.mjs";
3
+ import { a as errorMessage, c as readJson, d as writeJson } from "./_utils-Ch0J4s6a.mjs";
4
4
  import path from "node:path";
5
5
  import * as p from "@clack/prompts";
6
6
  import envPaths from "env-paths";
@@ -116,7 +116,7 @@ async function ensureApiKey(configDir) {
116
116
  return envKey;
117
117
  }
118
118
  if (!process.stdin.isTTY) throw new CliError("no_api_key", "No API key configured and no TTY to prompt for one.", "Set the ASSEMBLYAI_API_KEY environment variable, or run `aai` interactively once to save a key.");
119
- const apiKey = unwrapCancel(await p.password({ message: "Enter your AssemblyAI API key" }));
119
+ const apiKey = unwrapCancel(await p.password({ message: "Enter your AssemblyAI API key" }), "Setup cancelled");
120
120
  await trySaveApiKey(dir, config, apiKey);
121
121
  return apiKey;
122
122
  }
@@ -0,0 +1,57 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * `.aai/project.json` lives in the working tree, so everything in it is
4
+ * untrusted input — a cloned repo can supply any value.
5
+ *
6
+ * `serverUrl` is deliberately NOT validated here. A failed field makes
7
+ * `readProjectConfig` return null for the whole file, which discards the
8
+ * `slug` too — and a deploy with no slug generates a fresh one, silently
9
+ * creating a duplicate agent and overwriting the config. The URL is instead
10
+ * validated where it is used, by `resolveServerUrl`, which rejects anything
11
+ * that isn't an approved http(s) origin.
12
+ */
13
+ declare const ProjectConfigSchema: z.ZodObject<{
14
+ slug: z.ZodString;
15
+ serverUrl: z.ZodString;
16
+ }, z.core.$strip>;
17
+ /**
18
+ * Resolve the global config directory (the platform-conventional env-paths
19
+ * location).
20
+ *
21
+ * `AAI_CONFIG_DIR` overrides everything — it exists so tests (and unusual
22
+ * setups) can redirect ALL global-config reads and writes away from the
23
+ * user's real config. The test suite's `approveServer` calls used to
24
+ * permanently pollute `~/.config/aai/config.json` with approved origins.
25
+ */
26
+ export declare function getConfigDir(): string;
27
+ export type ProjectConfig = z.infer<typeof ProjectConfigSchema>;
28
+ export declare function readProjectConfig(agentDir: string): Promise<ProjectConfig | null>;
29
+ export declare function writeProjectConfig(agentDir: string, data: ProjectConfig): Promise<void>;
30
+ export type GlobalConfig = {
31
+ apiKey?: string;
32
+ /**
33
+ * Origins the user has explicitly pointed the CLI at with `--server`.
34
+ *
35
+ * Lives in the user-owned global config, never in the repo: it is what makes
36
+ * a `serverUrl` from `.aai/project.json` trustworthy enough to receive an
37
+ * API key. See `resolveServerUrl`.
38
+ */
39
+ approvedServers?: string[];
40
+ };
41
+ /**
42
+ * Origin of `url`, or `null` when it is not an absolute http(s) URL.
43
+ *
44
+ * Non-HTTP schemes are rejected rather than returned: `new URL()` yields the
45
+ * opaque origin `"null"` for them, which would otherwise flow on as if it
46
+ * were a real origin.
47
+ */
48
+ export declare function serverOrigin(url: string): string | null;
49
+ /**
50
+ * Record `url`'s origin as user-approved, so later commands in this project
51
+ * may send credentials there without re-passing `--server`.
52
+ */
53
+ export declare function approveServer(url: string, configDir?: string): Promise<void>;
54
+ export declare function readGlobalConfig(configDir?: string): Promise<GlobalConfig>;
55
+ export declare function writeGlobalConfig(configDir: string, data: GlobalConfig): Promise<void>;
56
+ export declare function ensureApiKey(configDir?: string): Promise<string>;
57
+ export {};
@@ -0,0 +1,12 @@
1
+ import type { Plugin } from "vite";
2
+ export declare const DEFAULT_HTML = "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, viewport-fit=cover\" />\n <title>aai</title>\n <link rel=\"icon\" href=\"./favicon.ico\" />\n <style>html, body { background: #FBF8F2; margin: 0; }</style>\n </head>\n <body>\n <main id=\"app\"></main>\n <script type=\"module\" src=\"./client.tsx\"></script>\n </body>\n</html>";
3
+ /**
4
+ * Vite plugin that serves a fallback index.html in dev mode when one doesn't
5
+ * exist on disk. No-op if index.html exists (user override).
6
+ */
7
+ export declare function fallbackHtmlPlugin(root: string): Plugin;
8
+ /**
9
+ * Write a temporary index.html for Vite build (HTML must be on disk for build).
10
+ * Returns a cleanup function to remove it. No-op if index.html already exists.
11
+ */
12
+ export declare function writeTempHtml(root: string): () => void;
@@ -0,0 +1,28 @@
1
+ import type { DirectoryBundleOutput } from "./_bundler.ts";
2
+ export type DeployOpts = {
3
+ url: string;
4
+ bundle: DirectoryBundleOutput;
5
+ /** Env var values from .env to send to the server. */
6
+ env: Record<string, string>;
7
+ /** Existing slug for redeployment. Omit for first deploy — server generates one. */
8
+ slug?: string;
9
+ apiKey: string;
10
+ /**
11
+ * Ask the server to WARN (in `warnings`) instead of rejecting when the
12
+ * agent's providers are missing credentials — `aai deploy
13
+ * --allow-missing-secrets`, for setting them post-deploy with
14
+ * `aai secret put` (the studio's publish flow relies on this: its Secrets
15
+ * panel needs a deployed slug to attach secrets to).
16
+ */
17
+ allowMissingSecrets?: boolean;
18
+ /** Retry delay override for tests (0 = no real sleeps on retry paths). */
19
+ retryDelay?: number;
20
+ /** Optional fetch implementation for testing. Defaults to globalThis.fetch. */
21
+ fetch?: typeof globalThis.fetch;
22
+ };
23
+ export type DeployResult = {
24
+ slug: string;
25
+ /** Server-side deploy warnings (e.g. the missing-credential preflight). */
26
+ warnings?: string[];
27
+ };
28
+ export declare function runDeploy(opts: DeployOpts): Promise<DeployResult>;
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import { n as log } from "./_ui-DXZ9prrM.mjs";
3
- import { r as errorMessage, t as errorCode } from "./_utils-C502jKo8.mjs";
4
- import { n as fallbackHtmlPlugin } from "./client-bundler-DO3GgO6p.mjs";
2
+ import { n as log } from "./_ui-8kOEB-JH.mjs";
3
+ import { a as errorMessage, r as errorCode } from "./_utils-Ch0J4s6a.mjs";
4
+ import { n as fallbackHtmlPlugin } from "./client-bundler-BWDkUeEP.mjs";
5
5
  import { buildWorker } from "./worker-bundler.mjs";
6
- import { createWorkerEvaluator } from "./_bundler-DdmuZzeR.mjs";
7
- import { n as ensureApiKey } from "./_config-Czjhvaax.mjs";
8
- import { t as resolveServerEnv } from "./_server-common-B75oco06.mjs";
6
+ import { n as createWorkerEvaluator } from "./_bundler-DFS4xxqE.mjs";
7
+ import { n as ensureApiKey } from "./_config-D3F9km8X.mjs";
8
+ import { t as resolveServerEnv } from "./_server-common-CnaP_Urf.mjs";
9
9
  import { createRequire } from "node:module";
10
10
  import { existsSync } from "node:fs";
11
11
  import path from "node:path";
12
12
  import { setTimeout } from "node:timers/promises";
13
- import { requiredProviderEnvVars, withHostCredentialFallback } from "@alexkroman1/aai/runtime";
13
+ import { createRuntime, createServer, requiredProviderEnvVars, withHostCredentialFallback } from "@alexkroman1/aai/runtime";
14
14
  import { watch } from "chokidar";
15
15
  import getPort, { portNumbers } from "get-port";
16
16
  import pDebounce from "p-debounce";
@@ -183,7 +183,6 @@ function viteDevConfig(cwd, vitePort, backendPort) {
183
183
  */
184
184
  async function startDevServer(opts) {
185
185
  const { cwd, port } = opts;
186
- const { createRuntime, createServer } = await import("@alexkroman1/aai/runtime");
187
186
  const hasClient = existsSync(path.join(cwd, "client.tsx"));
188
187
  const backendPort = hasClient ? await getPort({ port: portNumbers(port + 1, port + 100) }) : port;
189
188
  const vitePort = port;
@@ -194,13 +193,12 @@ async function startDevServer(opts) {
194
193
  const agentDef = await loadAgentDef(cwd, evaluateWorker);
195
194
  const env = await resolveAgentEnv(cwd, agentDef);
196
195
  const providerEnv = withHostCredentialFallback(env);
197
- const runtime = createRuntime({
198
- agent: agentDef,
199
- env,
200
- providerEnv
201
- });
202
196
  return createServer({
203
- runtime,
197
+ runtime: createRuntime({
198
+ agent: agentDef,
199
+ env,
200
+ providerEnv
201
+ }),
204
202
  name: agentDef.name,
205
203
  env: hostModeEnv(providerEnv),
206
204
  hostBaseAgent: agentDef,
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Dev server for directory-based agents.
3
+ *
4
+ * Imports agent.ts directly for the full agent definition,
5
+ * builds a runtime, and starts an HTTP+WebSocket server. Watches for
6
+ * file changes and restarts automatically. Optionally runs Vite for
7
+ * client SPA HMR.
8
+ */
9
+ import type { AgentDef } from "@alexkroman1/aai";
10
+ import { type FSWatcher } from "chokidar";
11
+ /**
12
+ * Warnings about the agent's credentials, computed against the `.env`-derived
13
+ * env and the shell. Pure so it is directly testable; `resolveAgentEnv` logs
14
+ * each entry. Three cases, in increasing subtlety:
15
+ *
16
+ * - a provider key found nowhere → the first session will fail auth;
17
+ * - a provider key found only in the shell → works here (the
18
+ * `withHostCredentialFallback` ergonomic) but is invisible to `aai deploy`,
19
+ * which uploads `.env` — the classic "works locally, dead on deploy";
20
+ * - a declared `requiredEnv` key absent from `.env` → `ctx.env` won't contain
21
+ * it at all: custom keys never fall back to the shell, so a shell export
22
+ * can't mask one that would be missing both here and after deploy.
23
+ */
24
+ export declare function agentEnvWarnings(agentDef: Pick<AgentDef, "stt" | "llm" | "tts" | "s2s" | "requiredEnv">, env: Record<string, string>, shellEnv?: Record<string, string | undefined>): string[];
25
+ /**
26
+ * Load the agent definition by bundling agent.ts (and all its local imports)
27
+ * into a single ESM file, then importing that. A raw `import(agent.ts?t=...)`
28
+ * only cache-busts agent.ts itself — transitive imports (./tools.ts, etc.)
29
+ * stay in Node's ESM registry, so edits to them are ignored on reload.
30
+ * Bundling picks them up.
31
+ *
32
+ * The bundle comes from the same Vite pass deploy runs (`buildWorker`), so
33
+ * dev and deploy can't drift; a warm rebuild is well under 100ms. Compile
34
+ * errors in the agent's code propagate — the restart loop reports them and
35
+ * keeps the old server. Evaluation goes through the memoizing evaluator so
36
+ * a no-op save doesn't leak another module into the ESM registry.
37
+ */
38
+ export declare function loadAgentDef(cwd: string, evaluate: (code: string) => Promise<AgentDef>): Promise<AgentDef>;
39
+ /**
40
+ * True for paths that should never trigger a restart: anything inside
41
+ * `node_modules/` and any dot-entry (`.git/`, `.aai/`, `.DS_Store`, …).
42
+ * `.git/` especially matters — commits and status checks churn the index
43
+ * and would otherwise cause spurious full backend restarts.
44
+ *
45
+ * Exception: `.env` / `.env.*` files stay watched — env edits should
46
+ * restart the server with the new values.
47
+ */
48
+ export declare function isIgnoredPath(dir: string, filePath: string): boolean;
49
+ /**
50
+ * Watch the agent directory for changes and call `onChange` when detected.
51
+ * Debounces to avoid rapid restarts. Uses chokidar for reliable recursive
52
+ * watching across platforms (raw `fs.watch` misses events on Linux).
53
+ */
54
+ export declare function watchDirectory(dir: string, onChange: () => void): FSWatcher;
55
+ export type DevServerOptions = {
56
+ cwd: string;
57
+ port: number;
58
+ };
59
+ /**
60
+ * Vite dev-server config for the client SPA. Extracted so the proxy wiring
61
+ * is unit-testable: `/websocket` MUST proxy with `ws: true` or `aai dev`
62
+ * with a `client.tsx` serves a page whose WebSocket never connects.
63
+ *
64
+ * `strictPort` because the reported URL is `http://localhost:<port>` —
65
+ * without it, Vite silently binds port+N when the port is busy and the
66
+ * printed/JSON-returned URL points at whatever else was listening.
67
+ */
68
+ export declare function viteDevConfig(cwd: string, vitePort: number, backendPort: number): import("vite").InlineConfig;
69
+ /**
70
+ * Start the dev server for a directory-based agent.
71
+ *
72
+ * Returns a cleanup function to shut down the server and watchers.
73
+ */
74
+ export declare function startDevServer(opts: DevServerOptions): Promise<() => Promise<void>>;
@@ -1,85 +1,67 @@
1
1
  #!/usr/bin/env node
2
- import { a as isEexist, l as writeJson, o as readJson, r as errorMessage } from "./_utils-C502jKo8.mjs";
3
- import { r as isDevMode, t as getMonorepoRoot } from "./_agent-D39qatZJ.mjs";
2
+ import { a as errorMessage, c as readJson, d as writeJson, s as isEexist } from "./_utils-Ch0J4s6a.mjs";
3
+ import { r as isDevMode, t as getMonorepoRoot } from "./_agent-D8zBb3M5.mjs";
4
4
  import { existsSync } from "node:fs";
5
5
  import path from "node:path";
6
6
  import fs from "node:fs/promises";
7
- import os from "node:os";
8
- import { downloadTemplate } from "giget";
9
- //#region _templates.ts
10
- const GIGET_SOURCE = "github:alexkroman/agent/packages/aai-templates";
11
- const GIGET_REF = process.env.AAI_TEMPLATES_REF ?? "main";
12
- const VALID_REF_RE = /^[\w./-]+$/;
13
- const noCleanup = async () => void 0;
14
- /** Resolve the templates directory local in dev, giget download in prod. */
15
- async function resolveTemplatesDir() {
16
- if (process.env.AAI_TEMPLATES_DIR) return {
17
- root: process.env.AAI_TEMPLATES_DIR,
18
- cleanup: noCleanup
19
- };
7
+ const REPO_URL = `https://github.com/alexkroman/agent`;
8
+ /**
9
+ * Templates as shipped inside the published tarball, copied into `dist/` by
10
+ * `bundle-templates.mjs` at build time — so this resolves to `dist/` for a
11
+ * published CLI and to the (template-less) package root when running source
12
+ * in the monorepo, where the branch above wins.
13
+ *
14
+ * They used to be fetched at `init` time with giget from
15
+ * `github:alexkroman/agent/packages/aai-templates#main`. That required a
16
+ * network for every `init`, and pinned templates to `main` regardless of the
17
+ * CLI version the user had installed, so a template written against a newer
18
+ * SDK could land in a project resolving an older one. Bundling pins the two
19
+ * together by construction. It also puts the templates inside the studio's
20
+ * guest sandbox, which has the CLI in its baked toolchain but no way to fetch
21
+ * anything from GitHub.
22
+ */
23
+ function bundledTemplatesDir() {
24
+ return import.meta.dirname;
25
+ }
26
+ /** Resolve the templates root — env override, then monorepo, then bundled. */
27
+ function resolveTemplatesDir() {
28
+ const override = process.env.AAI_TEMPLATES_DIR;
29
+ if (override) return override;
20
30
  const monorepoRoot = getMonorepoRoot();
21
- if (monorepoRoot) return {
22
- root: path.join(monorepoRoot, "packages", "aai-templates"),
23
- cleanup: noCleanup
24
- };
25
- if (!VALID_REF_RE.test(GIGET_REF)) throw new Error(`Invalid AAI_TEMPLATES_REF: ${JSON.stringify(GIGET_REF)} is not a git ref.`);
26
- const extractDir = await fs.mkdtemp(path.join(os.tmpdir(), "aai-templates-"));
27
- const cleanup = async () => {
28
- await fs.rm(extractDir, {
29
- recursive: true,
30
- force: true
31
- }).catch(() => void 0);
32
- };
33
- try {
34
- const { dir } = await downloadTemplate(`${GIGET_SOURCE}#${GIGET_REF}`, {
35
- dir: extractDir,
36
- force: true,
37
- forceClean: true
38
- });
39
- return {
40
- root: dir,
41
- cleanup
42
- };
43
- } catch (err) {
44
- await cleanup();
45
- throw new Error(`Failed to download templates from ${GIGET_SOURCE}#${GIGET_REF}: ${errorMessage(err)}`, { cause: err });
46
- }
31
+ if (monorepoRoot) return path.join(monorepoRoot, "packages", "aai-templates");
32
+ return bundledTemplatesDir();
47
33
  }
48
34
  /**
49
- * Download a template into targetDir, merging scaffold files underneath.
35
+ * Copy a template into targetDir, merging scaffold files underneath.
50
36
  */
51
37
  async function downloadAndMergeTemplate(template, targetDir) {
52
- const { root, cleanup } = await resolveTemplatesDir();
38
+ const root = resolveTemplatesDir();
39
+ const templatesDir = path.join(root, "templates");
40
+ let available;
53
41
  try {
54
- const templatesDir = path.join(root, "templates");
55
- let available;
56
- try {
57
- available = await fs.readdir(templatesDir, { withFileTypes: true });
58
- } catch (err) {
59
- throw new Error(`Templates directory is missing or unreadable at ${templatesDir} (corrupt or incomplete template download?): ${errorMessage(err)}`, { cause: err });
60
- }
61
- const names = available.filter((e) => e.isDirectory()).map((e) => e.name);
62
- if (!names.includes(template)) throw new Error(`Unknown template "${template}". Available templates: ${names.join(", ")}`);
63
- await fs.cp(path.join(templatesDir, template), targetDir, {
64
- recursive: true,
65
- force: true
66
- });
67
- const scaffoldDir = path.join(root, "scaffold");
68
- if (existsSync(scaffoldDir)) await fs.cp(scaffoldDir, targetDir, {
69
- recursive: true,
70
- force: false,
71
- errorOnExist: false
72
- });
73
- } finally {
74
- await cleanup();
42
+ available = await fs.readdir(templatesDir, { withFileTypes: true });
43
+ } catch (err) {
44
+ throw new Error(`Templates directory is missing or unreadable at ${templatesDir} (incomplete @alexkroman1/aai-cli install?): ${errorMessage(err)}`, { cause: err });
75
45
  }
46
+ const names = available.filter((e) => e.isDirectory()).map((e) => e.name);
47
+ if (!names.includes(template)) throw new Error(`Unknown template "${template}". Available templates: ${names.join(", ")}`);
48
+ await fs.cp(path.join(templatesDir, template), targetDir, {
49
+ recursive: true,
50
+ force: true
51
+ });
52
+ const scaffoldDir = path.join(root, "scaffold");
53
+ if (existsSync(scaffoldDir)) await fs.cp(scaffoldDir, targetDir, {
54
+ recursive: true,
55
+ force: false,
56
+ errorOnExist: false
57
+ });
76
58
  }
77
59
  //#endregion
78
60
  //#region _init.ts
79
61
  function readmeContent(slug) {
80
62
  return `# ${slug}
81
63
 
82
- A voice agent built with [aai](https://github.com/anthropics/aai).
64
+ A voice agent built with [aai](${REPO_URL}).
83
65
 
84
66
  ## Getting started
85
67
 
@@ -0,0 +1,7 @@
1
+ export type InitOptions = {
2
+ targetDir: string;
3
+ template: string;
4
+ };
5
+ /** Rewrite workspace deps to link: paths so pnpm links to local source. */
6
+ export declare function patchPackageJsonForWorkspace(targetDir: string): Promise<void>;
7
+ export declare function runInit(opts: InitOptions): Promise<void>;
@@ -0,0 +1,22 @@
1
+ export interface RecordedRequest {
2
+ method: string;
3
+ path: string;
4
+ headers: Record<string, string | string[] | undefined>;
5
+ /** Decoded body text (inflated first when sent with Content-Encoding: gzip). */
6
+ body: string;
7
+ }
8
+ export interface MockApi {
9
+ /** Base URL of the mock server (http://localhost:<port>) */
10
+ url: string;
11
+ /** All recorded requests */
12
+ requests: RecordedRequest[];
13
+ /** Secrets currently stored */
14
+ secrets: Record<string, string>;
15
+ /** Override response for a specific method+path pattern */
16
+ override(method: string, pathPattern: string, status: number, body?: string): void;
17
+ /** Clear recorded requests */
18
+ clear(): void;
19
+ /** Stop the server */
20
+ stop(): Promise<void>;
21
+ }
22
+ export declare function startMockApi(): Promise<MockApi>;
@@ -0,0 +1,17 @@
1
+ export interface MockRegistry {
2
+ /** Local registry URL (http://localhost:<port>) */
3
+ registryUrl: string;
4
+ /** The unique version string used for published workspace packages */
5
+ testVersion: string;
6
+ /** Environment variables to set for child processes using this registry */
7
+ env: Record<string, string>;
8
+ /** Stop the registry and clean up */
9
+ stop: () => Promise<void>;
10
+ }
11
+ /**
12
+ * Start a mock npm registry, build and publish workspace packages to it.
13
+ *
14
+ * @param packagesDir - Path to the `packages/` directory in the monorepo
15
+ * @param packageNames - Directory names under `packages/` to publish (e.g. ["aai", "aai-ui", "aai-cli"])
16
+ */
17
+ export declare function startMockRegistry(packagesDir: string, packageNames: string[]): Promise<MockRegistry>;