@alexkroman1/aai-cli 1.8.2 → 1.9.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 (31) hide show
  1. package/dist/_agent-cHGzbDVG.mjs +108 -0
  2. package/dist/_api-client-yJD7xGfd.mjs +47 -0
  3. package/dist/_bundler-BRQFvx00.mjs +62 -0
  4. package/dist/_config-zV70t71V.mjs +125 -0
  5. package/dist/_dev-server-CBrIeDoI.mjs +214 -0
  6. package/dist/{_init-Bh5etNEh.mjs → _init-y7K-dzni.mjs} +17 -43
  7. package/dist/{_output-vvoR6N1x.mjs → _output-fy2bXRNb.mjs} +12 -19
  8. package/dist/{_ui-nOp7hFVy.mjs → _ui-YqQ6Fi8K.mjs} +14 -20
  9. package/dist/_utils-BeU10C7O.mjs +55 -0
  10. package/dist/_vite-env-CmXG4ZAu.mjs +31 -0
  11. package/dist/cli.mjs +41 -40
  12. package/dist/client-bundler-D4VKqCxw.mjs +125 -0
  13. package/dist/client-bundler.mjs +3 -0
  14. package/dist/{delete-BEC2ZXbk.mjs → delete-DE35s3d5.mjs} +7 -8
  15. package/dist/{deploy-D8uMWSDH.mjs → deploy-C0LittrE.mjs} +20 -18
  16. package/dist/{dev-D2EwGHYi.mjs → dev-BvjnXK2x.mjs} +10 -3
  17. package/dist/{init-GWTdzrrm.mjs → init-kYsRaf7e.mjs} +29 -75
  18. package/dist/{secret-DlSN4FdF.mjs → secret-D-90QtDQ.mjs} +14 -16
  19. package/dist/{test-BLYrV5ap.mjs → test-C6jwCnFu.mjs} +30 -5
  20. package/dist/types.mjs +2 -0
  21. package/dist/worker-bundler.mjs +68 -0
  22. package/package.json +25 -14
  23. package/dist/_agent-De8f1JdZ.mjs +0 -38
  24. package/dist/_api-client-Cf9Lv2Rw.mjs +0 -46
  25. package/dist/_bundler-CMMSjh9o.mjs +0 -143
  26. package/dist/_config-B0FXR5GQ.mjs +0 -77
  27. package/dist/_default-html-BasMCgr9.mjs +0 -62
  28. package/dist/_dev-server-DMFyqc9v.mjs +0 -143
  29. package/dist/_utils-DZo3_J_v.mjs +0 -23
  30. package/dist/rolldown-runtime-uZa2dNXj.mjs +0 -15
  31. /package/dist/{_server-common-BVkNI-o4.mjs → _server-common-QpOTRZgi.mjs} +0 -0
@@ -1,72 +1,39 @@
1
1
  #!/usr/bin/env node
2
- import { a as ok } from "./_output-vvoR6N1x.mjs";
3
- import { r as log$1 } from "./_ui-nOp7hFVy.mjs";
4
- import { n as resolveCwd, t as fileExists } from "./_utils-DZo3_J_v.mjs";
5
- import { getMonorepoRoot, isDevMode } from "./_agent-De8f1JdZ.mjs";
2
+ import { i as ok } from "./_output-fy2bXRNb.mjs";
3
+ import { a as unwrapCancel, n as log } from "./_ui-YqQ6Fi8K.mjs";
4
+ import { a as readJson, n as errorMessage, o as resolveCwd, r as fileExists } from "./_utils-BeU10C7O.mjs";
5
+ import { i as isDevMode, n as getMonorepoRoot } from "./_agent-cHGzbDVG.mjs";
6
6
  import path from "node:path";
7
7
  import * as p from "@clack/prompts";
8
8
  import { colorize } from "consola/utils";
9
- import fs from "node:fs/promises";
10
- import { execFile } from "node:child_process";
11
- import { promisify } from "node:util";
9
+ import { execa } from "execa";
12
10
  //#region init.ts
13
- /**
14
- * Format an install error so the user sees what actually went wrong.
15
- * pnpm writes failures to stdout (not stderr), and Node's execFile error
16
- * message is only "Command failed: ..." — so we append both streams.
17
- */
18
- function formatInstallError(err) {
19
- if (!(err instanceof Error)) return String(err);
20
- const parts = [err.message];
21
- const stderr = err.stderr?.trim();
22
- const stdout = err.stdout?.trim();
23
- if (stderr) parts.push(stderr);
24
- if (stdout) parts.push(stdout);
25
- return parts.join("\n");
26
- }
27
- const execFileAsync = promisify(execFile);
28
11
  const DEFAULT_PROJECT_NAME = "my-voice-agent";
29
12
  /** Prompt for project name or return default when --yes is set. */
30
13
  async function promptProjectName(yes) {
31
14
  if (yes) return DEFAULT_PROJECT_NAME;
32
- const result = await p.text({
15
+ return unwrapCancel(await p.text({
33
16
  message: "What is your project named?",
34
17
  placeholder: DEFAULT_PROJECT_NAME,
35
18
  defaultValue: DEFAULT_PROJECT_NAME
36
- });
37
- if (p.isCancel(result)) {
38
- p.cancel("Setup cancelled");
39
- process.exit(0);
40
- }
41
- return result || DEFAULT_PROJECT_NAME;
19
+ })) || DEFAULT_PROJECT_NAME;
42
20
  }
43
21
  /** Enable corepack so pnpm is available (scaffold declares packageManager: pnpm). */
44
22
  async function ensurePnpm() {
45
- try {
46
- await execFileAsync("corepack", ["enable"]);
47
- } catch {}
23
+ await execa("corepack", ["enable"], { reject: false });
48
24
  }
49
25
  /** Check if the project has any dependencies to install. */
50
26
  async function hasDeps(cwd) {
51
27
  if (await fileExists(path.join(cwd, "node_modules"))) return false;
52
- let pkgJson;
53
- try {
54
- pkgJson = JSON.parse(await fs.readFile(path.join(cwd, "package.json"), "utf-8"));
55
- } catch {
56
- pkgJson = {};
57
- }
28
+ const pkgJson = await readJson(path.join(cwd, "package.json")) ?? {};
58
29
  const deps = Object.keys(pkgJson.dependencies ?? {});
59
30
  const devDeps = Object.keys(pkgJson.devDependencies ?? {});
60
31
  return deps.length > 0 || devDeps.length > 0;
61
32
  }
62
33
  /** Check whether the safe-chain binary is on PATH. */
63
34
  async function hasSafeChain() {
64
- try {
65
- await execFileAsync("safe-chain", ["--version"]);
66
- return true;
67
- } catch {
68
- return false;
69
- }
35
+ const { failed } = await execa("safe-chain", ["--version"], { reject: false });
36
+ return !failed;
70
37
  }
71
38
  /** Build the command + args for running pnpm, routing through safe-chain when available. */
72
39
  async function resolvePnpmCommand(checkSafeChain = hasSafeChain) {
@@ -83,30 +50,22 @@ async function resolvePnpmCommand(checkSafeChain = hasSafeChain) {
83
50
  async function runPnpmInstall(cwd) {
84
51
  const { cmd, args } = await resolvePnpmCommand();
85
52
  const pnpmArgs = isDevMode() ? ["install"] : ["install", "--ignore-workspace"];
86
- await execFileAsync(cmd, [...args, ...pnpmArgs], { cwd });
53
+ await execa(cmd, [...args, ...pnpmArgs], { cwd });
87
54
  }
88
55
  /** Install deps with pnpm. Returns true on success (or no deps to install). */
89
56
  async function installDeps(cwd, silent) {
90
57
  if (!await hasDeps(cwd)) return true;
91
58
  await ensurePnpm();
92
- if (silent) try {
93
- await runPnpmInstall(cwd);
94
- return true;
95
- } catch (err) {
96
- log$1.warn(`pnpm install failed: ${formatInstallError(err)}`);
97
- log$1.warn("Run `corepack enable && pnpm install` manually in the project directory.");
98
- return false;
99
- }
100
- const s = p.spinner();
101
- s.start("Installing dependencies with pnpm");
59
+ const s = silent ? void 0 : p.spinner();
60
+ s?.start("Installing dependencies with pnpm");
102
61
  try {
103
62
  await runPnpmInstall(cwd);
104
- s.stop("Dependencies installed");
63
+ s?.stop("Dependencies installed");
105
64
  return true;
106
65
  } catch (err) {
107
- s.stop("Dependency install failed");
108
- log$1.warn(`pnpm install failed: ${formatInstallError(err)}`);
109
- log$1.warn("Run `corepack enable && pnpm install` manually in the project directory.");
66
+ s?.stop("Dependency install failed");
67
+ log.warn(`pnpm install failed: ${errorMessage(err)}`);
68
+ log.warn("Run `corepack enable && pnpm install` manually in the project directory.");
110
69
  return false;
111
70
  }
112
71
  }
@@ -114,17 +73,12 @@ async function installDeps(cwd, silent) {
114
73
  function resolveTargetDir(dir) {
115
74
  return path.resolve(resolveCwd(), dir);
116
75
  }
117
- /** Resolve the deploy server — in dev mode, default to localhost. */
118
- function resolveDeployServer(explicit, monorepoRoot) {
119
- return explicit ?? (monorepoRoot ? "http://localhost:8080" : void 0);
120
- }
121
76
  /** Run deploy after init and return deploy metadata if successful. */
122
- async function tryDeploy(cwd, server, monorepoRoot) {
123
- const resolvedServer = resolveDeployServer(server, monorepoRoot);
124
- const { executeDeploy } = await import("./deploy-D8uMWSDH.mjs");
77
+ async function tryDeploy(cwd, server) {
78
+ const { executeDeploy } = await import("./deploy-C0LittrE.mjs");
125
79
  const result = await executeDeploy({
126
80
  cwd,
127
- ...resolvedServer ? { server: resolvedServer } : {}
81
+ ...server ? { server } : {}
128
82
  });
129
83
  return result.ok ? {
130
84
  slug: result.data.slug,
@@ -133,7 +87,7 @@ async function tryDeploy(cwd, server, monorepoRoot) {
133
87
  }
134
88
  /** Scaffold the project, optionally showing a spinner. */
135
89
  async function scaffoldProject(dir, cwd, template, silent) {
136
- const { runInit } = await import("./_init-Bh5etNEh.mjs");
90
+ const { runInit } = await import("./_init-y7K-dzni.mjs");
137
91
  if (silent) {
138
92
  await runInit({
139
93
  targetDir: cwd,
@@ -151,12 +105,12 @@ async function scaffoldProject(dir, cwd, template, silent) {
151
105
  }
152
106
  /** Print post-init instructions. */
153
107
  function printPostInitInfo(cwd, monorepoRoot) {
154
- log$1.success(`Created ${cwd}`);
155
- if (monorepoRoot) log$1.info("Dev mode: project linked to workspace packages");
156
- log$1.info(`Next: cd ${cwd} && aai dev`);
108
+ log.success(`Created ${cwd}`);
109
+ if (monorepoRoot) log.info("Dev mode: project linked to workspace packages");
110
+ log.info(`Next: cd ${cwd} && aai dev`);
157
111
  }
158
112
  async function executeInit(opts, extra) {
159
- const suppressUi = extra?.quiet ?? extra?.silent;
113
+ const suppressUi = extra?.silent;
160
114
  if (!suppressUi) p.intro(colorize("cyanBright", "Create a new voice agent"));
161
115
  const dir = opts.dir ?? await promptProjectName(opts.yes);
162
116
  const monorepoRoot = getMonorepoRoot();
@@ -168,9 +122,9 @@ async function executeInit(opts, extra) {
168
122
  let deployed = false;
169
123
  let slug;
170
124
  let url;
171
- if (!installed) log$1.warn("Skipping deploy because dependencies were not installed.");
172
- else if (!(opts.skipDeploy || extra?.quiet)) {
173
- const deployInfo = await tryDeploy(cwd, opts.server, monorepoRoot);
125
+ if (!installed) log.warn("Skipping deploy because dependencies were not installed.");
126
+ else if (!opts.skipDeploy) {
127
+ const deployInfo = await tryDeploy(cwd, opts.server);
174
128
  if (deployInfo) {
175
129
  deployed = true;
176
130
  slug = deployInfo.slug;
@@ -1,14 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import { a as ok, r as fail } from "./_output-vvoR6N1x.mjs";
3
- import { r as log$1 } from "./_ui-nOp7hFVy.mjs";
4
- import { getServerInfo } from "./_agent-De8f1JdZ.mjs";
5
- import { t as apiRequestOrThrow } from "./_api-client-Cf9Lv2Rw.mjs";
2
+ import { i as ok, n as fail } from "./_output-fy2bXRNb.mjs";
3
+ import { n as log } from "./_ui-YqQ6Fi8K.mjs";
4
+ import { r as getServerInfo } from "./_agent-cHGzbDVG.mjs";
5
+ import { t as apiRequest } from "./_api-client-yJD7xGfd.mjs";
6
6
  import * as p from "@clack/prompts";
7
+ import { text } from "node:stream/consumers";
7
8
  //#region secret.ts
8
9
  async function secretRequest(cwd, pathSuffix, init, server) {
9
10
  const { serverUrl, slug, apiKey } = await getServerInfo(cwd, server);
10
11
  return {
11
- resp: await apiRequestOrThrow(`${serverUrl}/${slug}/secret${pathSuffix}`, {
12
+ data: await apiRequest(`${serverUrl}/${slug}/secret${pathSuffix}`, {
12
13
  ...init,
13
14
  apiKey,
14
15
  action: "secret"
@@ -18,9 +19,7 @@ async function secretRequest(cwd, pathSuffix, init, server) {
18
19
  }
19
20
  /** Read secret value from stdin (for non-TTY / piped input). */
20
21
  async function readStdin() {
21
- const chunks = [];
22
- for await (const chunk of process.stdin) chunks.push(chunk);
23
- return Buffer.concat(chunks).toString("utf-8").trim();
22
+ return (await text(process.stdin)).trim();
24
23
  }
25
24
  /**
26
25
  * Execute secret put. If `value` is provided, use it directly (non-TTY path).
@@ -36,23 +35,22 @@ async function executeSecretPut(cwd, name, value, server) {
36
35
  }
37
36
  const { slug } = await secretRequest(cwd, "", {
38
37
  method: "PUT",
39
- body: JSON.stringify({ [name]: secretValue })
38
+ body: { [name]: secretValue }
40
39
  }, server);
41
- log$1.success(`Set ${name} for ${slug}`);
40
+ log.success(`Set ${name} for ${slug}`);
42
41
  return ok({ name });
43
42
  }
44
43
  async function executeSecretDelete(cwd, name, server) {
45
44
  const { slug } = await secretRequest(cwd, `/${name}`, { method: "DELETE" }, server);
46
- log$1.success(`Deleted ${name} from ${slug}`);
45
+ log.success(`Deleted ${name} from ${slug}`);
47
46
  return ok({ name });
48
47
  }
49
48
  async function executeSecretList(cwd, server) {
50
- const { resp } = await secretRequest(cwd, "", void 0, server);
51
- const { vars } = await resp.json();
52
- if (vars.length === 0) log$1.info("No secrets set. Use `aai secret put <name>` to add one.");
49
+ const { data: { vars } } = await secretRequest(cwd, "", void 0, server);
50
+ if (vars.length === 0) log.info("No secrets set. Use `aai secret put <name>` to add one.");
53
51
  else {
54
- log$1.message(`${vars.length} secret${vars.length === 1 ? "" : "s"}:`);
55
- for (const v of vars) log$1.message(` ${v}`);
52
+ log.message(`${vars.length} secret${vars.length === 1 ? "" : "s"}:`);
53
+ for (const v of vars) log.message(` ${v}`);
56
54
  }
57
55
  return ok({ secrets: vars });
58
56
  }
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { a as ok, r as fail } from "./_output-vvoR6N1x.mjs";
3
- import { r as log } from "./_ui-nOp7hFVy.mjs";
4
- import { existsSync } from "node:fs";
2
+ import { i as ok, n as fail } from "./_output-fy2bXRNb.mjs";
3
+ import { n as log } from "./_ui-YqQ6Fi8K.mjs";
4
+ import { createRequire } from "node:module";
5
+ import { existsSync, readFileSync } from "node:fs";
5
6
  import path from "node:path";
6
7
  import { execFileSync } from "node:child_process";
7
8
  //#region test.ts
@@ -9,6 +10,29 @@ import { execFileSync } from "node:child_process";
9
10
  * `aai test` — run agent tests via vitest.
10
11
  */
11
12
  /**
13
+ * Resolve the agent project's own vitest binary so tests run without the
14
+ * npx resolution overhead (and its potential network fetch of vitest).
15
+ *
16
+ * Resolves `vitest/package.json` from the agent directory, derives the bin
17
+ * script, and runs it with the current Node executable. Falls back to
18
+ * `npx vitest` only when no local install is resolvable.
19
+ */
20
+ function resolveVitestCommand(cwd, resolve = createRequire(path.join(cwd, "package.json")).resolve) {
21
+ try {
22
+ const pkgPath = resolve("vitest/package.json");
23
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
24
+ const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.vitest;
25
+ if (bin) return {
26
+ cmd: process.execPath,
27
+ args: [path.join(path.dirname(pkgPath), bin)]
28
+ };
29
+ } catch {}
30
+ return {
31
+ cmd: "npx",
32
+ args: ["vitest"]
33
+ };
34
+ }
35
+ /**
12
36
  * Run vitest in the given project directory.
13
37
  *
14
38
  * Returns `true` if tests passed, `false` if no test files exist.
@@ -19,8 +43,9 @@ function runVitest(cwd) {
19
43
  if (existsSync(path.join(cwd, "agent.test.ts"))) testFile = "agent.test.ts";
20
44
  else if (existsSync(path.join(cwd, "agent.test.js"))) testFile = "agent.test.js";
21
45
  if (!testFile) return false;
22
- execFileSync("npx", [
23
- "vitest",
46
+ const { cmd, args } = resolveVitestCommand(cwd);
47
+ execFileSync(cmd, [
48
+ ...args,
24
49
  "run",
25
50
  "--root",
26
51
  ".",
package/dist/types.mjs ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env node
2
+ import { t as withPreservedNodeEnv } from "./_vite-env-CmXG4ZAu.mjs";
3
+ import path from "node:path";
4
+ import { build } from "vite";
5
+ //#region worker-bundler.ts
6
+ /**
7
+ * Worker bundling — the one implementation of "turn an `agent.ts` into the
8
+ * ESM the guest sandbox loads".
9
+ *
10
+ * Public (no `_` prefix) for the same reason as `client-bundler.ts`: the
11
+ * platform's browser studio builds its workspaces through this function, so a
12
+ * worker published from the browser comes out of the same Vite/Rollup pass as
13
+ * one from `aai deploy`.
14
+ *
15
+ * What the studio supplies via options, because a workspace is not a project:
16
+ *
17
+ * - **`root` + `entry`.** The studio builds a generated entry that re-exports
18
+ * the agent *and* its extracted config, so the guest can report the config
19
+ * back without the host ever evaluating agent code.
20
+ * - **`configFile: false`.** Workspace files are untrusted and a Vite config
21
+ * is executable host code.
22
+ * - **`plugins`.** The studio adds its import allowlist; without it, Vite
23
+ * would happily resolve any package in the server's `node_modules`.
24
+ */
25
+ /**
26
+ * Transform `.md` imports into raw string exports so templates that do
27
+ * `import systemPrompt from "./system-prompt.md"` bundle correctly.
28
+ */
29
+ const rawMdPlugin = {
30
+ name: "raw-md",
31
+ transform(code, id) {
32
+ if (id.endsWith(".md")) return `export default ${JSON.stringify(code)}`;
33
+ }
34
+ };
35
+ /**
36
+ * Bundle agent.ts into a single ESM string for the sandbox worker.
37
+ *
38
+ * Zod is bundled in — zod 4's `Function()` usage is wrapped in try/catch
39
+ * and gracefully degrades in restricted environments like Deno.
40
+ */
41
+ async function buildWorker(cwd, opts = {}) {
42
+ const entry = opts.entry ?? "agent.ts";
43
+ const agentEntry = path.isAbsolute(entry) ? entry : path.join(cwd, entry);
44
+ const result = await withPreservedNodeEnv(() => build({
45
+ root: cwd,
46
+ logLevel: "silent",
47
+ ...opts.configFile === false && { configFile: false },
48
+ plugins: [rawMdPlugin, ...opts.plugins ?? []],
49
+ build: {
50
+ lib: {
51
+ entry: agentEntry,
52
+ formats: ["es"],
53
+ fileName: "worker"
54
+ },
55
+ target: "node20",
56
+ minify: opts.minify ? "esbuild" : false,
57
+ write: false,
58
+ rollupOptions: { output: { entryFileNames: "[name].js" } }
59
+ }
60
+ }));
61
+ const output = Array.isArray(result) ? result[0] : result;
62
+ if (!output) throw new Error("Vite produced no output for agent.ts");
63
+ const chunk = output.output.find((o) => o.type === "chunk" && o.isEntry);
64
+ if (!chunk) throw new Error("Vite produced no entry chunk for agent.ts");
65
+ return chunk.code;
66
+ }
67
+ //#endregion
68
+ export { buildWorker };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alexkroman1/aai-cli",
3
- "version": "1.8.2",
3
+ "version": "1.9.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "aai": "dist/cli.mjs"
@@ -9,33 +9,44 @@
9
9
  "./types": {
10
10
  "@dev/source": "./types.ts",
11
11
  "import": "./dist/types.mjs"
12
+ },
13
+ "./client-bundler": {
14
+ "@dev/source": "./client-bundler.ts",
15
+ "import": "./dist/client-bundler.mjs"
16
+ },
17
+ "./worker-bundler": {
18
+ "@dev/source": "./worker-bundler.ts",
19
+ "import": "./dist/worker-bundler.mjs"
12
20
  }
13
21
  },
14
22
  "files": [
15
23
  "dist"
16
24
  ],
17
25
  "dependencies": {
18
- "@clack/prompts": "^1.2.0",
26
+ "@clack/prompts": "^1.7.0",
27
+ "chokidar": "^5.0.0",
19
28
  "citty": "^0.2.2",
20
29
  "consola": "^3.4.2",
21
30
  "dotenv": "^17.4.2",
22
- "giget": "^3.2.0",
31
+ "execa": "^9.6.1",
32
+ "get-port": "^7.2.0",
33
+ "giget": "^3.3.0",
34
+ "ofetch": "^1.5.1",
23
35
  "p-debounce": "^5.1.0",
24
- "vite": "^8.0.8",
25
- "zod": "^4.3.6",
26
- "@alexkroman1/aai": "1.8.2",
27
- "@alexkroman1/aai-ui": "1.8.2"
36
+ "vite": "^8.1.5",
37
+ "zod": "^4.4.3",
38
+ "@alexkroman1/aai": "1.9.0",
39
+ "@alexkroman1/aai-ui": "1.9.0"
28
40
  },
29
41
  "devDependencies": {
30
- "get-port": "^7.2.0",
31
- "playwright": "^1.59.1",
42
+ "playwright": "^1.61.1",
32
43
  "tree-kill": "^1.2.2",
33
- "tsdown": "^0.21.7",
34
- "verdaccio": "^6.4.0",
35
- "vitest": "^4.1.3"
44
+ "tsdown": "^0.22.13",
45
+ "verdaccio": "^6.8.0",
46
+ "vitest": "^4.1.10"
36
47
  },
37
48
  "peerDependencies": {
38
- "vitest": "^4.1.3"
49
+ "vitest": "^4.1.10"
39
50
  },
40
51
  "peerDependenciesMeta": {
41
52
  "vitest": {
@@ -56,7 +67,7 @@
56
67
  "build": "tsdown",
57
68
  "typecheck": "tsc --noEmit",
58
69
  "lint": "biome check .",
59
- "test:e2e": "VITEST_PROFILE=e2e VITEST_INCLUDE=e2e.test.ts vitest run -c ../../vitest.slow.config.ts",
70
+ "test:e2e": "VITEST_PROFILE=e2e VITEST_INCLUDE=e2e*.test.ts vitest run -c ../../vitest.slow.config.ts",
60
71
  "check:e2e": "pnpm run test:e2e"
61
72
  }
62
73
  }
@@ -1,38 +0,0 @@
1
- #!/usr/bin/env node
2
- import { n as ensureApiKey, r as readProjectConfig } from "./_config-B0FXR5GQ.mjs";
3
- import { existsSync } from "node:fs";
4
- import path from "node:path";
5
- import { fileURLToPath } from "node:url";
6
- const DEFAULT_DEV_SERVER = "http://localhost:8080";
7
- let _cachedMonorepoRoot;
8
- function getMonorepoRoot() {
9
- if (_cachedMonorepoRoot !== void 0) return _cachedMonorepoRoot;
10
- const cliDir = path.dirname(fileURLToPath(import.meta.url));
11
- const root1 = path.resolve(cliDir, "../..");
12
- const root2 = path.resolve(cliDir, "../../..");
13
- if (existsSync(path.join(root1, "pnpm-workspace.yaml"))) _cachedMonorepoRoot = root1;
14
- else if (existsSync(path.join(root2, "pnpm-workspace.yaml"))) _cachedMonorepoRoot = root2;
15
- else _cachedMonorepoRoot = null;
16
- return _cachedMonorepoRoot;
17
- }
18
- function isDevMode() {
19
- if (process.env.AAI_NO_DEV === "1") return false;
20
- return getMonorepoRoot() !== null;
21
- }
22
- function resolveServerUrl(explicit, configUrl) {
23
- if (explicit) return explicit;
24
- if (isDevMode()) return DEFAULT_DEV_SERVER;
25
- return configUrl ?? "https://aai-agent.fly.dev";
26
- }
27
- async function getServerInfo(cwd, explicitServer, explicitApiKey) {
28
- const config = await readProjectConfig(cwd);
29
- if (!config) throw new Error("No .aai/project.json found — run `aai deploy` first");
30
- const apiKey = explicitApiKey ?? await ensureApiKey();
31
- return {
32
- serverUrl: resolveServerUrl(explicitServer, config.serverUrl),
33
- slug: config.slug,
34
- apiKey
35
- };
36
- }
37
- //#endregion
38
- export { DEFAULT_DEV_SERVER, getMonorepoRoot, getServerInfo, isDevMode, resolveServerUrl };
@@ -1,46 +0,0 @@
1
- #!/usr/bin/env node
2
- //#region _api-client.ts
3
- /**
4
- * Shared HTTP helpers for platform API calls (deploy, delete, secrets).
5
- */
6
- const HINT_INVALID_API_KEY = "Your API key may be invalid. Run `aai` to re-enter your AssemblyAI API key.";
7
- /**
8
- * Send an authenticated request to the platform API.
9
- *
10
- * Adds the `Authorization` header and, on network failure, throws with a
11
- * contextual hint (localhost → "is the dev server running?", remote →
12
- * "check your network connection").
13
- */
14
- async function apiRequest(url, init, fetchFn = globalThis.fetch.bind(globalThis)) {
15
- const { apiKey, action, ...rest } = init;
16
- const headers = {
17
- Authorization: `Bearer ${apiKey}`,
18
- ...rest.body ? { "Content-Type": "application/json" } : {},
19
- ...rest.headers
20
- };
21
- try {
22
- return await fetchFn(url, {
23
- ...rest,
24
- headers
25
- });
26
- } catch (err) {
27
- throw new Error(`${action} failed: could not reach ${url}\n Check your network connection and verify the server URL is correct.`, { cause: err });
28
- }
29
- }
30
- /** Format a non-ok API response into a descriptive error. */
31
- function apiError(action, status, body, hint) {
32
- return /* @__PURE__ */ new Error(`${action} failed (HTTP ${status}): ${body}${hint ? `\n ${hint}` : ""}`);
33
- }
34
- /**
35
- * Like `apiRequest`, but throws on non-ok responses with status-specific hints.
36
- * The 401 hint is always included. Pass additional hints via `opts.hints`.
37
- */
38
- async function apiRequestOrThrow(url, init, opts) {
39
- const resp = await apiRequest(url, init, opts?.fetch);
40
- if (resp.ok) return resp;
41
- const text = await resp.text();
42
- const hint = resp.status === 401 ? HINT_INVALID_API_KEY : opts?.hints?.[resp.status];
43
- throw apiError(init.action, resp.status, text, hint);
44
- }
45
- //#endregion
46
- export { apiRequestOrThrow as t };
@@ -1,143 +0,0 @@
1
- #!/usr/bin/env node
2
- import { a as ok } from "./_output-vvoR6N1x.mjs";
3
- import { r as validateAgentExport, t as fileExists } from "./_utils-DZo3_J_v.mjs";
4
- import { n as writeTempHtml } from "./_default-html-BasMCgr9.mjs";
5
- import path from "node:path";
6
- import { pathToFileURL } from "node:url";
7
- import fs from "node:fs/promises";
8
- import { agentToolsToSchemas, toAgentConfig } from "@alexkroman1/aai/manifest";
9
- import { build } from "vite";
10
- //#region _bundler.ts
11
- /** Shared Vite build base config for agent bundles. */
12
- function agentViteBuildBase(entry) {
13
- return {
14
- logLevel: "silent",
15
- build: {
16
- lib: {
17
- entry,
18
- formats: ["es"]
19
- },
20
- target: "node20",
21
- minify: false,
22
- rollupOptions: { output: { entryFileNames: "[name].js" } }
23
- }
24
- };
25
- }
26
- /**
27
- * Bundle an agent directory: build agent.ts into worker ESM + extract config.
28
- *
29
- * - agent.ts is the single entry point: `export default agent({...})`
30
- * - A single Vite build produces the worker ESM (all deps bundled in).
31
- * The AgentDef is extracted from that bundle via dynamic import, avoiding a
32
- * second build pass.
33
- */
34
- async function buildAgentBundle(cwd) {
35
- const { log } = await import("./_ui-nOp7hFVy.mjs").then((n) => n.t);
36
- const [worker, clientFiles] = await Promise.all([buildWorker(cwd), buildClient(cwd)]);
37
- const agentDef = await evalWorkerBundle(worker, cwd);
38
- log.step(`Bundling ${agentDef.name}`);
39
- const config = toAgentConfig(agentDef);
40
- const toolSchemas = agentToolsToSchemas(agentDef.tools ?? {});
41
- return {
42
- worker,
43
- clientFiles,
44
- agentConfig: {
45
- ...config,
46
- toolSchemas
47
- }
48
- };
49
- }
50
- /**
51
- * Write the worker ESM to a temp file and dynamic-import it, returning
52
- * the AgentDef default export. All dependencies are bundled in, so the
53
- * file can be evaluated from any directory.
54
- */
55
- async function evalWorkerBundle(code, cwd) {
56
- const evalDir = path.join(cwd, ".aai", "eval");
57
- await fs.mkdir(evalDir, { recursive: true });
58
- const tmpPath = path.join(evalDir, `agent-${Date.now()}-${Math.random().toString(36).slice(2)}.mjs`);
59
- try {
60
- await fs.writeFile(tmpPath, code);
61
- const mod = await import(pathToFileURL(tmpPath).href);
62
- const agentDef = mod.default ?? mod;
63
- validateAgentExport(agentDef);
64
- return agentDef;
65
- } finally {
66
- await fs.rm(tmpPath).catch(() => {});
67
- }
68
- }
69
- /**
70
- * Bundle agent.ts into a single ESM string for the sandbox worker.
71
- *
72
- * Zod is bundled in — zod 4's `Function()` usage is wrapped in try/catch
73
- * and gracefully degrades in restricted environments like Deno.
74
- */
75
- async function buildWorker(cwd) {
76
- const base = agentViteBuildBase(path.join(cwd, "agent.ts"));
77
- const result = await build({
78
- ...base,
79
- plugins: [{
80
- name: "raw-md",
81
- transform(code, id) {
82
- if (id.endsWith(".md")) return `export default ${JSON.stringify(code)}`;
83
- }
84
- }],
85
- build: {
86
- ...base.build,
87
- lib: {
88
- ...base.build.lib,
89
- fileName: "worker"
90
- },
91
- write: false,
92
- rollupOptions: { output: { entryFileNames: "[name].js" } }
93
- }
94
- });
95
- const output = Array.isArray(result) ? result[0] : result;
96
- if (!output) throw new Error("Vite produced no output for agent.ts");
97
- const chunk = output.output.find((o) => o.type === "chunk" && o.isEntry);
98
- if (!chunk) throw new Error("Vite produced no entry chunk for agent.ts");
99
- return chunk.code;
100
- }
101
- /**
102
- * Build the client SPA using Vite if client.tsx exists.
103
- * Returns a map of relative file paths to string contents for deploy.
104
- */
105
- async function buildClient(cwd) {
106
- if (!await fileExists(path.join(cwd, "client.tsx"))) return {};
107
- const clientDir = path.join(cwd, ".aai", "client");
108
- const cleanupHtml = writeTempHtml(cwd);
109
- try {
110
- await build({
111
- root: cwd,
112
- base: "./",
113
- logLevel: "silent",
114
- build: {
115
- outDir: ".aai/client",
116
- emptyOutDir: true
117
- }
118
- });
119
- } finally {
120
- cleanupHtml();
121
- }
122
- const files = {};
123
- async function walk(dir, prefix) {
124
- for (const entry of await fs.readdir(dir, { withFileTypes: true })) {
125
- const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
126
- if (entry.isDirectory()) await walk(path.join(dir, entry.name), rel);
127
- else files[rel] = await fs.readFile(path.join(dir, entry.name), "utf-8");
128
- }
129
- }
130
- await walk(clientDir, "");
131
- return files;
132
- }
133
- async function executeBuild(cwd) {
134
- const { log } = await import("./_ui-nOp7hFVy.mjs").then((n) => n.t);
135
- const bundle = await buildAgentBundle(cwd);
136
- log.success("Build complete");
137
- return ok({
138
- name: bundle.agentConfig.name,
139
- workerBytes: bundle.worker.length
140
- });
141
- }
142
- //#endregion
143
- export { buildAgentBundle, executeBuild };