@alexkroman1/aai-cli 0.10.2 → 0.10.4

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.
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env node
2
+ import { l as resolveCwd, n as ensureApiKeyInEnv, r as fileExists } from "./_discover-DiRl7b_K.mjs";
3
+ import { n as listTemplates } from "./_templates-s5MYfWS9.mjs";
4
+ import { consola } from "./_ui-Cu-v_Bzz.mjs";
5
+ import path from "node:path";
6
+ import fs from "node:fs/promises";
7
+ import { errorMessage } from "@alexkroman1/aai/utils";
8
+ import * as p from "@clack/prompts";
9
+ import { colorize } from "consola/utils";
10
+ import { execFile } from "node:child_process";
11
+ import { promisify } from "node:util";
12
+ //#region init.ts
13
+ const execFileAsync = promisify(execFile);
14
+ const DEFAULT_PROJECT_NAME = "my-voice-agent";
15
+ const DEFAULT_TEMPLATE = "simple";
16
+ /** Detect the package manager from the environment. */
17
+ function detectPackageManager() {
18
+ const ua = process.env.npm_config_user_agent ?? "";
19
+ if (ua.startsWith("pnpm")) return "pnpm";
20
+ if (ua.startsWith("yarn")) return "yarn";
21
+ if (ua.startsWith("bun")) return "bun";
22
+ return "npm";
23
+ }
24
+ /** Prompt for project name or return default when --yes is set. */
25
+ async function promptProjectName(yes) {
26
+ if (yes) return DEFAULT_PROJECT_NAME;
27
+ const result = await p.text({
28
+ message: "What is your project named?",
29
+ placeholder: DEFAULT_PROJECT_NAME,
30
+ defaultValue: DEFAULT_PROJECT_NAME
31
+ });
32
+ if (p.isCancel(result)) {
33
+ p.cancel("Setup cancelled.");
34
+ process.exit(0);
35
+ }
36
+ return result || DEFAULT_PROJECT_NAME;
37
+ }
38
+ /** Prompt for template selection or return default when --yes is set. */
39
+ async function promptTemplate(yes) {
40
+ if (yes) return DEFAULT_TEMPLATE;
41
+ const templates = await listTemplates();
42
+ const result = await p.select({
43
+ message: "Which template would you like to use?",
44
+ options: templates.map((name) => ({
45
+ value: name,
46
+ label: name
47
+ })),
48
+ initialValue: DEFAULT_TEMPLATE
49
+ });
50
+ if (p.isCancel(result)) {
51
+ p.cancel("Setup cancelled.");
52
+ process.exit(0);
53
+ }
54
+ return result;
55
+ }
56
+ /** Install deps via the detected package manager. */
57
+ async function installDeps(cwd, pm) {
58
+ if (await fileExists(path.join(cwd, "node_modules"))) return;
59
+ let pkgJson;
60
+ try {
61
+ pkgJson = JSON.parse(await fs.readFile(path.join(cwd, "package.json"), "utf-8"));
62
+ } catch {
63
+ pkgJson = {};
64
+ }
65
+ const deps = Object.keys(pkgJson.dependencies ?? {});
66
+ const devDeps = Object.keys(pkgJson.devDependencies ?? {});
67
+ if (deps.length === 0 && devDeps.length === 0) return;
68
+ const s = p.spinner();
69
+ s.start(`Installing dependencies with ${pm}`);
70
+ try {
71
+ await execFileAsync(pm, ["install"], { cwd });
72
+ s.stop("Dependencies installed");
73
+ } catch (err) {
74
+ const msg = errorMessage(err);
75
+ s.stop("Dependency install failed");
76
+ consola.warn(`${pm} install failed: ${msg}`);
77
+ consola.warn(`Run \`${pm} install\` manually in the project directory.`);
78
+ }
79
+ }
80
+ /** Format the run command for the detected package manager. */
81
+ function devCommand(pm) {
82
+ return pm === "npm" ? `${pm} run dev` : `${pm} dev`;
83
+ }
84
+ async function runInitCommand(opts, extra) {
85
+ const pm = detectPackageManager();
86
+ if (!extra?.quiet) p.intro(colorize("blueBright", "Create a new voice agent"));
87
+ if (!opts.skipApi) await ensureApiKeyInEnv();
88
+ const dir = opts.dir ?? await promptProjectName(opts.yes);
89
+ const cwd = path.resolve(resolveCwd(), dir);
90
+ if (!opts.force && await fileExists(path.join(cwd, "agent.ts"))) throw new Error(`agent.ts already exists in this directory. Use ${colorize("blueBright", "--force")} to overwrite.`);
91
+ const template = opts.template ?? await promptTemplate(opts.yes);
92
+ const s = p.spinner();
93
+ s.start(`Creating ${dir} from ${template} template`);
94
+ const { runInit } = await import("./_init-CVkKf8t_.mjs");
95
+ await runInit({
96
+ targetDir: cwd,
97
+ template
98
+ });
99
+ s.stop("Project created");
100
+ await installDeps(cwd, pm);
101
+ if (!(opts.skipDeploy || extra?.quiet)) {
102
+ const { runDeployCommand } = await import("./deploy-C2s4C_ff.mjs");
103
+ await runDeployCommand({ cwd });
104
+ }
105
+ if (!extra?.quiet) {
106
+ p.note(`cd ${dir}\n${devCommand(pm)}`, "Next steps");
107
+ p.outro("Happy building!");
108
+ }
109
+ return cwd;
110
+ }
111
+ //#endregion
112
+ export { runInitCommand };
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+ import { f as askPassword, o as getServerInfo } from "./_discover-DiRl7b_K.mjs";
3
+ import { consola } from "./_ui-Cu-v_Bzz.mjs";
4
+ //#region secret.ts
5
+ async function apiFetch(cwd, pathSuffix, init) {
6
+ const { serverUrl, slug, apiKey } = await getServerInfo(cwd);
7
+ const resp = await fetch(`${serverUrl}/${slug}/secret${pathSuffix}`, {
8
+ ...init,
9
+ headers: {
10
+ Authorization: `Bearer ${apiKey}`,
11
+ ...init?.headers
12
+ }
13
+ });
14
+ if (!resp.ok) {
15
+ const text = await resp.text();
16
+ throw new Error(`Secret operation failed: ${text}`);
17
+ }
18
+ return {
19
+ resp,
20
+ slug
21
+ };
22
+ }
23
+ async function runSecretPut(cwd, name) {
24
+ const value = await askPassword(`Enter value for ${name}`);
25
+ if (!value) throw new Error("No value provided");
26
+ const { slug } = await apiFetch(cwd, "", {
27
+ method: "PUT",
28
+ headers: { "Content-Type": "application/json" },
29
+ body: JSON.stringify({ [name]: value })
30
+ });
31
+ consola.success(`Set ${name} for ${slug}`);
32
+ }
33
+ async function runSecretDelete(cwd, name) {
34
+ const { slug } = await apiFetch(cwd, `/${name}`, { method: "DELETE" });
35
+ consola.success(`Deleted ${name} from ${slug}`);
36
+ }
37
+ async function runSecretList(cwd) {
38
+ const { resp } = await apiFetch(cwd, "");
39
+ const { vars } = await resp.json();
40
+ if (vars.length === 0) consola.info("Secrets: none set");
41
+ else for (const name of vars) consola.log(` ${name}`);
42
+ }
43
+ //#endregion
44
+ export { runSecretDelete, runSecretList, runSecretPut };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as runCommand, o as step } from "./_ui-C9IvR7Fh.mjs";
2
+ import { consola } from "./_ui-Cu-v_Bzz.mjs";
3
3
  import { existsSync } from "node:fs";
4
4
  import path from "node:path";
5
5
  import { execSync } from "node:child_process";
@@ -15,7 +15,7 @@ import { execSync } from "node:child_process";
15
15
  */
16
16
  function runVitest(cwd) {
17
17
  if (!(existsSync(path.join(cwd, "agent.test.ts")) || existsSync(path.join(cwd, "agent.test.js")))) return false;
18
- execSync("npx vitest run", {
18
+ execSync(`npx vitest run --root . ${existsSync(path.join(cwd, "agent.test.ts")) ? "agent.test.ts" : "agent.test.js"}`, {
19
19
  cwd,
20
20
  stdio: "inherit",
21
21
  env: {
@@ -27,14 +27,12 @@ function runVitest(cwd) {
27
27
  }
28
28
  /** Run agent tests. Used by `aai test`. */
29
29
  async function runTestCommand(cwd) {
30
- await runCommand(async ({ log }) => {
31
- log(step("Test", "running agent tests"));
32
- if (!runVitest(cwd)) {
33
- log("No test files found (agent.test.ts). Skipping.");
34
- return;
35
- }
36
- log(step("Test", "ok"));
37
- });
30
+ consola.start("Running agent tests");
31
+ if (!runVitest(cwd)) {
32
+ consola.info("No test files found (agent.test.ts). Skipping.");
33
+ return;
34
+ }
35
+ consola.success("Tests passed");
38
36
  }
39
37
  //#endregion
40
38
  export { runTestCommand, runVitest };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alexkroman1/aai-cli",
3
- "version": "0.10.2",
3
+ "version": "0.10.4",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "aai": "dist/cli.mjs"
@@ -10,24 +10,20 @@
10
10
  ],
11
11
  "dependencies": {
12
12
  "@ai-sdk/openai-compatible": "^2.0.37",
13
- "@chonkiejs/core": "^0.0.8",
14
- "consola": "^3.4.2",
15
- "giget": "^2.0.0",
16
- "@hono/node-server": "^1.19.11",
17
- "@preact/preset-vite": "^2.10.5",
18
- "@tailwindcss/vite": "^4.2.2",
13
+ "@clack/prompts": "^1.1.0",
19
14
  "ai": "^6.0.140",
20
15
  "citty": "^0.2.1",
21
- "hono": "^4.12.9",
16
+ "consola": "^3.4.2",
17
+ "giget": "^2.0.0",
22
18
  "human-id": "^4.1.3",
23
- "p-limit": "^7.3.0",
24
19
  "vite": "^8.0.3",
25
20
  "zod": "^4.3.6",
26
- "@alexkroman1/aai": "0.10.2"
21
+ "@alexkroman1/aai": "0.10.4"
27
22
  },
28
23
  "devDependencies": {
29
24
  "playwright": "^1.58.2",
30
- "tsdown": "^0.21.5"
25
+ "tsdown": "^0.21.5",
26
+ "vitest": "^4.1.1"
31
27
  },
32
28
  "engines": {
33
29
  "node": ">=22.6"
@@ -41,8 +37,7 @@
41
37
  "build": "tsdown",
42
38
  "typecheck": "tsc --noEmit",
43
39
  "lint": "biome check .",
44
- "test:integration": "vitest run pack-build.test.ts -c vitest.slow.config.ts",
45
40
  "test:e2e": "vitest run e2e.test.ts -c vitest.slow.config.ts",
46
- "check:e2e": "vitest run e2e.test.ts -c vitest.slow.config.ts"
41
+ "check:e2e": "pnpm run test:e2e"
47
42
  }
48
43
  }
@@ -1,176 +0,0 @@
1
- #!/usr/bin/env node
2
- import { s as loadAgent } from "./_discover-D7HCLa_N.mjs";
3
- import { a as runCommand, o as step } from "./_ui-C9IvR7Fh.mjs";
4
- import path from "node:path";
5
- import fs from "node:fs/promises";
6
- import { errorMessage } from "@alexkroman1/aai/utils";
7
- import preact from "@preact/preset-vite";
8
- import tailwindcss from "@tailwindcss/vite";
9
- import { build, createServer } from "vite";
10
- //#region _bundler.ts
11
- /**
12
- * Error thrown when bundling fails.
13
- *
14
- * @param message Human-readable error message (typically formatted build output).
15
- */
16
- var BundleError = class extends Error {
17
- constructor(message, options) {
18
- super(message, options);
19
- this.name = "BundleError";
20
- }
21
- };
22
- /** File extensions that are safe to read as UTF-8 text. */
23
- const TEXT_EXTENSIONS = new Set([
24
- ".html",
25
- ".htm",
26
- ".css",
27
- ".js",
28
- ".mjs",
29
- ".cjs",
30
- ".ts",
31
- ".mts",
32
- ".json",
33
- ".map",
34
- ".svg",
35
- ".xml",
36
- ".txt",
37
- ".md"
38
- ]);
39
- /** Read all files in a directory as a map of relative paths to contents. */
40
- async function readDirFiles(dir) {
41
- let entries;
42
- try {
43
- entries = await fs.readdir(dir, {
44
- recursive: true,
45
- withFileTypes: true
46
- });
47
- } catch (err) {
48
- if (err instanceof Error && "code" in err && err.code === "ENOENT") return {};
49
- throw err;
50
- }
51
- const files = {};
52
- await Promise.all(entries.filter((e) => e.isFile()).map(async (e) => {
53
- const full = path.join(e.parentPath, e.name);
54
- const ext = path.extname(e.name).toLowerCase();
55
- const rel = path.relative(dir, full);
56
- if (TEXT_EXTENSIONS.has(ext)) files[rel] = await fs.readFile(full, "utf-8");
57
- else files[rel] = `base64:${(await fs.readFile(full)).toString("base64")}`;
58
- }));
59
- return files;
60
- }
61
- /**
62
- * Bundles an agent project into deployable artifacts using Vite.
63
- *
64
- * Writes all output to `.aai/` on disk:
65
- * - `.aai/build/worker.js` — the platform worker bundle
66
- * - `.aai/client/` — standard Vite multi-file output (index.html + assets/)
67
- *
68
- * Both `aai dev` and `aai deploy` use this function identically.
69
- */
70
- async function bundleAgent(agent, opts) {
71
- const aaiDir = path.join(agent.dir, ".aai");
72
- const buildDir = path.join(aaiDir, "build");
73
- const clientDir = path.join(aaiDir, "client");
74
- try {
75
- await build({
76
- configFile: false,
77
- root: agent.dir,
78
- logLevel: "warn",
79
- build: {
80
- lib: {
81
- entry: path.join(agent.dir, "agent.ts"),
82
- formats: ["es"],
83
- fileName: () => "worker.js"
84
- },
85
- outDir: buildDir,
86
- emptyOutDir: true,
87
- minify: true,
88
- target: "es2022"
89
- }
90
- });
91
- } catch (err) {
92
- throw new BundleError(errorMessage(err), { cause: err });
93
- }
94
- if (!(opts?.skipClient ?? !agent.clientEntry)) try {
95
- await build({
96
- root: agent.dir,
97
- base: "./",
98
- logLevel: "warn",
99
- plugins: [preact(), tailwindcss()],
100
- resolve: { dedupe: ["preact", "@preact/signals"] },
101
- build: {
102
- outDir: clientDir,
103
- emptyOutDir: true,
104
- minify: true,
105
- target: "es2022"
106
- }
107
- });
108
- } catch (err) {
109
- throw new BundleError(errorMessage(err), { cause: err });
110
- }
111
- const worker = await fs.readFile(path.join(buildDir, "worker.js"), "utf-8");
112
- return {
113
- worker,
114
- clientFiles: await readDirFiles(clientDir),
115
- clientDir,
116
- workerBytes: Buffer.byteLength(worker)
117
- };
118
- }
119
- /**
120
- * Create a Vite dev server for client HMR during development.
121
- *
122
- * The dev server serves client files with hot module replacement enabled and
123
- * proxies backend requests (`/health`, `/websocket`) to the agent server.
124
- */
125
- async function createClientDevServer(agentDir, backendPort, port) {
126
- const target = `http://localhost:${backendPort}`;
127
- return await createServer({
128
- configFile: false,
129
- root: agentDir,
130
- plugins: [preact(), tailwindcss()],
131
- resolve: { dedupe: ["preact", "@preact/signals"] },
132
- server: {
133
- port,
134
- strictPort: true,
135
- proxy: {
136
- "/health": target,
137
- "/websocket": {
138
- target,
139
- ws: true
140
- }
141
- }
142
- }
143
- });
144
- }
145
- //#endregion
146
- //#region _build.ts
147
- /**
148
- * Discover the agent entry and bundle both worker and client.
149
- *
150
- * Shared by `aai build`, `aai dev`, and `aai deploy`.
151
- */
152
- async function buildAgentBundle(cwd, log) {
153
- const agent = await loadAgent(cwd);
154
- if (!agent) throw new Error("No agent found — run `aai init` first");
155
- log(step("Bundle", agent.slug));
156
- let bundle;
157
- try {
158
- bundle = await bundleAgent(agent);
159
- } catch (err) {
160
- if (err instanceof BundleError) throw new Error(`Bundle failed: ${err.message}`, { cause: err });
161
- throw err;
162
- }
163
- const kb = (bundle.workerBytes / 1024).toFixed(1);
164
- const clientCount = Object.keys(bundle.clientFiles).length;
165
- log(`worker: ${kb} KB, client: ${clientCount} file(s)`);
166
- return bundle;
167
- }
168
- /** Bundle the agent and report success. Used by `aai build`. */
169
- async function runBuildCommand(cwd) {
170
- await runCommand(async ({ log }) => {
171
- await buildAgentBundle(cwd, log);
172
- log(step("Build", "ok"));
173
- });
174
- }
175
- //#endregion
176
- export { buildAgentBundle, runBuildCommand, createClientDevServer as t };
@@ -1,108 +0,0 @@
1
- #!/usr/bin/env node
2
- import { o as isDevMode } from "./_discover-D7HCLa_N.mjs";
3
- import { existsSync } from "node:fs";
4
- import path from "node:path";
5
- import { fileURLToPath } from "node:url";
6
- import fs$1 from "node:fs/promises";
7
- import { downloadTemplate } from "giget";
8
- //#region _templates.ts
9
- const GIGET_SOURCE = "github:alexkroman/agent/packages/aai-templates";
10
- const GIGET_REF = process.env.AAI_TEMPLATES_REF ?? "main";
11
- /** Resolve the local aai-templates package directory (dev mode only). */
12
- function resolveLocalTemplatesDir() {
13
- const cliDir = path.dirname(fileURLToPath(import.meta.url));
14
- const fromSrc = path.resolve(cliDir, "../aai-templates");
15
- const fromDist = path.resolve(cliDir, "../../aai-templates");
16
- if (existsSync(fromSrc)) return fromSrc;
17
- if (existsSync(fromDist)) return fromDist;
18
- throw new Error("Cannot find local aai-templates package");
19
- }
20
- /** Resolve the templates directory — local in dev, giget download in prod. */
21
- async function resolveTemplatesDir() {
22
- if (isDevMode()) return resolveLocalTemplatesDir();
23
- const { dir } = await downloadTemplate(`${GIGET_SOURCE}#${GIGET_REF}`, { force: true });
24
- return dir;
25
- }
26
- /**
27
- * Download a template into targetDir, merging _shared files underneath.
28
- */
29
- async function downloadAndMergeTemplate(template, targetDir) {
30
- const templatesDir = await resolveTemplatesDir();
31
- const names = (await fs$1.readdir(templatesDir, { withFileTypes: true })).filter((e) => e.isDirectory() && !e.name.startsWith("_") && e.name !== "node_modules").map((e) => e.name);
32
- if (!names.includes(template)) throw new Error(`unknown template '${template}' -- available: ${names.join(", ")}`);
33
- await fs$1.cp(path.join(templatesDir, template), targetDir, {
34
- recursive: true,
35
- force: true
36
- });
37
- const sharedDir = path.join(templatesDir, "_shared");
38
- if (existsSync(sharedDir)) {
39
- const entries = await fs$1.readdir(sharedDir, {
40
- recursive: true,
41
- withFileTypes: true
42
- });
43
- for (const entry of entries) {
44
- if (!entry.isFile()) continue;
45
- const rel = path.relative(sharedDir, path.join(entry.parentPath, entry.name));
46
- const destPath = path.join(targetDir, rel);
47
- await fs$1.mkdir(path.dirname(destPath), { recursive: true });
48
- try {
49
- await fs$1.copyFile(path.join(sharedDir, rel), destPath, fs$1.constants.COPYFILE_EXCL);
50
- } catch (err) {
51
- if (!(err instanceof Error && "code" in err && err.code === "EEXIST")) throw err;
52
- }
53
- }
54
- }
55
- }
56
- //#endregion
57
- //#region _init.ts
58
- async function runInit(opts) {
59
- const { targetDir, template } = opts;
60
- await downloadAndMergeTemplate(template, targetDir);
61
- try {
62
- await fs$1.copyFile(path.join(targetDir, ".env.example"), path.join(targetDir, ".env"));
63
- } catch {}
64
- const readmePath = path.join(targetDir, "README.md");
65
- const readme = `# ${path.basename(path.resolve(targetDir))}
66
-
67
- A voice agent built with [aai](https://github.com/anthropics/aai).
68
-
69
- ## Getting started
70
-
71
- \`\`\`sh
72
- npm install # Install dependencies
73
- npm run dev # Run locally (opens browser)
74
- npm run deploy # Deploy to production
75
- \`\`\`
76
-
77
- ## Secrets
78
-
79
- Access secrets in your agent via \`ctx.env.MY_KEY\`.
80
-
81
- **Local development** — add secrets to \`.env\` (auto-loaded by \`aai dev\`):
82
-
83
- \`\`\`sh
84
- ALPHA_VANTAGE_KEY=sk-abc123
85
- MY_API_KEY=secret-value
86
- \`\`\`
87
-
88
- **Production** — set secrets on the server:
89
-
90
- \`\`\`sh
91
- aai secret put MY_KEY # Set a secret (prompts for value)
92
- aai secret list # List secret names
93
- aai secret delete MY_KEY # Remove a secret
94
- \`\`\`
95
-
96
- ## Learn more
97
-
98
- See \`CLAUDE.md\` for the full agent API reference.
99
- `;
100
- try {
101
- await fs$1.writeFile(readmePath, readme, { flag: "wx" });
102
- } catch (err) {
103
- if (!(err instanceof Error && "code" in err && err.code === "EEXIST")) throw err;
104
- }
105
- return targetDir;
106
- }
107
- //#endregion
108
- export { runInit };
@@ -1,49 +0,0 @@
1
- #!/usr/bin/env node
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import { consola } from "consola";
6
- import { z } from "zod";
7
- import { execFileSync } from "node:child_process";
8
- //#region _link.ts
9
- const WORKSPACE_PKGS = ["aai", "aai-ui"];
10
- function getPackagesDir() {
11
- const cliDir = path.dirname(fileURLToPath(import.meta.url));
12
- const parent = path.resolve(cliDir, "..");
13
- return fs.existsSync(path.join(parent, "aai", "package.json")) ? parent : path.resolve(parent, "..");
14
- }
15
- function rewriteWorkspaceDeps(cwd, rewrite, verb) {
16
- const packagesDir = getPackagesDir();
17
- const pkgJsonPath = path.join(cwd, "package.json");
18
- const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
19
- const deps = pkgJson.dependencies ?? {};
20
- const changed = [];
21
- for (const pkgDir of WORKSPACE_PKGS) {
22
- const localPath = path.join(packagesDir, pkgDir);
23
- const name = z.object({ name: z.string() }).parse(JSON.parse(fs.readFileSync(path.join(localPath, "package.json"), "utf-8"))).name;
24
- if (!deps[name]) continue;
25
- const newVersion = rewrite(deps[name], localPath);
26
- if (newVersion !== null) {
27
- deps[name] = newVersion;
28
- changed.push(name);
29
- }
30
- }
31
- if (changed.length === 0) {
32
- consola.info(`No packages to ${verb}.`);
33
- return;
34
- }
35
- fs.writeFileSync(pkgJsonPath, `${JSON.stringify(pkgJson, null, 2)}\n`);
36
- consola.start(`${verb}: ${changed.join(", ")} → installing...`);
37
- execFileSync("npm", ["install"], {
38
- cwd,
39
- stdio: "inherit"
40
- });
41
- }
42
- function runLinkCommand(cwd) {
43
- rewriteWorkspaceDeps(cwd, (_cur, localPath) => `file:${localPath}`, "Linked");
44
- }
45
- function runUnlinkCommand(cwd) {
46
- rewriteWorkspaceDeps(cwd, (cur) => cur.startsWith("file:") ? "*" : null, "Unlinked");
47
- }
48
- //#endregion
49
- export { runLinkCommand, runUnlinkCommand };
@@ -1,105 +0,0 @@
1
- #!/usr/bin/env node
2
- import { i as getApiKey } from "./_discover-D7HCLa_N.mjs";
3
- import path from "node:path";
4
- import fs from "node:fs/promises";
5
- //#region _server-common.ts
6
- /**
7
- * Return the variable names declared in a `.env` file.
8
- *
9
- * Only used to determine *which* keys the developer intended as agent
10
- * secrets — actual values are resolved from `process.env` (so shell
11
- * overrides still win).
12
- */
13
- function envFileKeys(content) {
14
- const keys = [];
15
- for (const raw of content.split("\n")) {
16
- const line = raw.trim();
17
- if (!line || line.startsWith("#")) continue;
18
- const eq = line.indexOf("=");
19
- if (eq === -1) continue;
20
- const key = line.slice(0, eq).trim();
21
- if (key) keys.push(key);
22
- }
23
- return keys;
24
- }
25
- /** Load an AgentDef by dynamically importing agent.ts via Node's native TS support. */
26
- async function loadAgentDef(cwd) {
27
- const agentDef = (await import(path.resolve(cwd, "agent.ts"))).default;
28
- if (!agentDef || typeof agentDef !== "object" || !agentDef.name) throw new Error("agent.ts must export a default agent definition (from defineAgent())");
29
- const missing = [];
30
- if (typeof agentDef.name !== "string") missing.push("name (string)");
31
- if (typeof agentDef.instructions !== "string") missing.push("instructions (string)");
32
- if (typeof agentDef.greeting !== "string") missing.push("greeting (string)");
33
- if (typeof agentDef.maxSteps !== "number" && typeof agentDef.maxSteps !== "function") missing.push("maxSteps (number or function)");
34
- if (!agentDef.tools || typeof agentDef.tools !== "object" || Array.isArray(agentDef.tools)) missing.push("tools (object)");
35
- if (missing.length > 0) throw new Error(`Invalid agent definition: missing or invalid fields: ${missing.join(", ")}. Use defineAgent() to create a valid agent definition.`);
36
- return agentDef;
37
- }
38
- /**
39
- * Build the `ctx.env` record that agent tools will see at runtime.
40
- *
41
- * Only variables explicitly declared in `.env` (plus `ASSEMBLYAI_API_KEY`)
42
- * are included — matching 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 from `process.env` after loading `.env` via Node's
48
- * built-in `process.loadEnvFile()`, so shell exports override `.env`.
49
- *
50
- * @param cwd - Project directory containing `.env` (optional).
51
- * @param baseEnv - Override the environment to read values from (tests only).
52
- */
53
- async function resolveServerEnv(cwd, baseEnv) {
54
- let declaredKeys = [];
55
- if (cwd) {
56
- const envPath = path.join(cwd, ".env");
57
- try {
58
- declaredKeys = envFileKeys(await fs.readFile(envPath, "utf-8"));
59
- process.loadEnvFile(envPath);
60
- } catch {}
61
- }
62
- const source = baseEnv ?? process.env;
63
- const env = {};
64
- for (const key of declaredKeys) {
65
- const val = source[key];
66
- if (val !== void 0) env[key] = val;
67
- }
68
- if (!env.ASSEMBLYAI_API_KEY) env.ASSEMBLYAI_API_KEY = source.ASSEMBLYAI_API_KEY ?? await getApiKey();
69
- return env;
70
- }
71
- /**
72
- * Create and start an agent server with static file serving.
73
- *
74
- * NOTE: This dynamically imports `@alexkroman1/aai/server` which has peer
75
- * dependencies on `hono` and `@hono/node-server`. Those packages are listed
76
- * as direct dependencies of aai-cli (in package.json) solely to satisfy
77
- * those peer deps — they are not imported directly by aai-cli code.
78
- */
79
- async function bootServer(agentDef, clientDir, env, port) {
80
- const { createServer } = await import("@alexkroman1/aai/server");
81
- const server = createServer({
82
- agent: agentDef,
83
- clientDir,
84
- env
85
- });
86
- await server.listen(port);
87
- return server;
88
- }
89
- /**
90
- * Boot the agent server without client serving.
91
- *
92
- * Used in dev mode where Vite handles client files with HMR,
93
- * and only the backend (health, WebSocket) runs on this server.
94
- */
95
- async function bootBackendServer(agentDef, env, port) {
96
- const { createServer } = await import("@alexkroman1/aai/server");
97
- const server = createServer({
98
- agent: agentDef,
99
- env
100
- });
101
- await server.listen(port);
102
- return server;
103
- }
104
- //#endregion
105
- export { resolveServerEnv as a, loadAgentDef as i, bootServer as n, envFileKeys as r, bootBackendServer as t };