@alexkroman1/aai-cli 0.12.3 → 1.0.3

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.
@@ -1,218 +0,0 @@
1
- #!/usr/bin/env node
2
- import { existsSync } from "node:fs";
3
- import path from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import fs from "node:fs/promises";
6
- import os from "node:os";
7
- import ci from "ci-info";
8
- import { consola } from "consola";
9
- import { z } from "zod";
10
- import { createInterface } from "node:readline";
11
- //#region \0rolldown/runtime.js
12
- var __defProp = Object.defineProperty;
13
- var __exportAll = (all, no_symbols) => {
14
- let target = {};
15
- for (var name in all) __defProp(target, name, {
16
- get: all[name],
17
- enumerable: true
18
- });
19
- if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
20
- return target;
21
- };
22
- //#endregion
23
- //#region _prompts.ts
24
- /**
25
- * Prompt the user for a password (masked input).
26
- * Throws in CI or non-TTY environments instead of hanging.
27
- */
28
- async function askPassword(message) {
29
- if (ci.isCI || !process.stdin.isTTY) throw new Error(`Interactive prompt requires a terminal. Set ${message} as an environment variable in CI.`);
30
- const rl = createInterface({
31
- input: process.stdin,
32
- output: process.stdout
33
- });
34
- process.stdout.write(`${message}: `);
35
- const stdin = process.stdin;
36
- const wasRaw = stdin.isRaw;
37
- stdin.setRawMode(true);
38
- try {
39
- return await readMasked(stdin);
40
- } finally {
41
- stdin.setRawMode(wasRaw ?? false);
42
- rl.close();
43
- }
44
- }
45
- function readMasked(stdin) {
46
- return new Promise((resolve) => {
47
- let buf = "";
48
- const onData = (ch) => {
49
- const c = ch.toString();
50
- if (c === "\n" || c === "\r") {
51
- stdin.removeListener("data", onData);
52
- process.stdout.write("\n");
53
- resolve(buf);
54
- } else if (c === "") {
55
- stdin.removeListener("data", onData);
56
- process.exit(0);
57
- } else if (c === "" || c === "\b") buf = buf.slice(0, -1);
58
- else buf += c;
59
- };
60
- stdin.on("data", onData);
61
- });
62
- }
63
- //#endregion
64
- //#region _discover.ts
65
- var _discover_exports = /* @__PURE__ */ __exportAll({
66
- DEFAULT_DEV_SERVER: () => DEFAULT_DEV_SERVER,
67
- DEFAULT_SERVER: () => DEFAULT_SERVER,
68
- ensureApiKeyInEnv: () => ensureApiKeyInEnv,
69
- fileExists: () => fileExists,
70
- getApiKey: () => getApiKey,
71
- getConfigDir: () => getConfigDir,
72
- getServerInfo: () => getServerInfo,
73
- isDevMode: () => isDevMode,
74
- loadAgent: () => loadAgent,
75
- readProjectConfig: () => readProjectConfig,
76
- resolveCwd: () => resolveCwd,
77
- resolveServerUrl: () => resolveServerUrl,
78
- writeProjectConfig: () => writeProjectConfig
79
- });
80
- const AuthConfigSchema = z.object({ assemblyai_api_key: z.string().optional() });
81
- const ProjectConfigSchema = z.object({
82
- slug: z.string(),
83
- serverUrl: z.string(),
84
- sessionId: z.string().optional()
85
- });
86
- /** Resolve the working directory from INIT_CWD or process.cwd(). */
87
- function resolveCwd() {
88
- return process.env.INIT_CWD || process.cwd();
89
- }
90
- function getConfigDir() {
91
- if (process.platform === "win32") return path.join(process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming"), "aai");
92
- return path.join(process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config"), "aai");
93
- }
94
- const CONFIG_DIR = getConfigDir();
95
- const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
96
- async function readAuthConfig() {
97
- try {
98
- return AuthConfigSchema.parse(JSON.parse(await fs.readFile(CONFIG_FILE, "utf-8")));
99
- } catch (error) {
100
- consola.debug(`Failed to read auth config from ${CONFIG_FILE}:`, error);
101
- return {};
102
- }
103
- }
104
- async function writeAuthConfig(config) {
105
- await fs.mkdir(CONFIG_DIR, { recursive: true });
106
- await fs.writeFile(CONFIG_FILE, `${JSON.stringify(config, null, 2)}\n`);
107
- if (process.platform !== "win32") await fs.chmod(CONFIG_FILE, 384);
108
- }
109
- /**
110
- * Retrieves the AssemblyAI API key from `process.env`, `~/.config/aai/config.json`,
111
- * or by interactively prompting the user (persisting it to config).
112
- *
113
- * Does NOT mutate `process.env`. Callers that need the key available in the
114
- * environment for child processes should use {@link ensureApiKeyInEnv} instead.
115
- */
116
- async function getApiKey() {
117
- if (process.env.ASSEMBLYAI_API_KEY) return process.env.ASSEMBLYAI_API_KEY;
118
- const config = await readAuthConfig();
119
- if (config.assemblyai_api_key) return config.assemblyai_api_key;
120
- if (ci.isCI || !process.stdin.isTTY) throw new Error("No ASSEMBLYAI_API_KEY found. Set the ASSEMBLYAI_API_KEY environment variable in CI or non-interactive environments.");
121
- const { log } = await import("./_ui-DWGXImbO.mjs");
122
- log.info("Get your API key at https://www.assemblyai.com/dashboard/signup");
123
- log.info("Or set the ASSEMBLYAI_API_KEY environment variable to skip this prompt.");
124
- let key;
125
- while (!key) key = await askPassword("ASSEMBLYAI_API_KEY");
126
- config.assemblyai_api_key = key;
127
- await writeAuthConfig(config);
128
- return key;
129
- }
130
- /**
131
- * Resolves the API key via {@link getApiKey} and sets it on `process.env.ASSEMBLYAI_API_KEY`
132
- * so that child processes and downstream code can read it from the environment.
133
- */
134
- async function ensureApiKeyInEnv() {
135
- const key = await getApiKey();
136
- process.env.ASSEMBLYAI_API_KEY = key;
137
- return key;
138
- }
139
- /**
140
- * Reads `.aai/project.json` from an agent directory.
141
- * Returns null if the file doesn't exist.
142
- */
143
- async function readProjectConfig(agentDir) {
144
- try {
145
- return ProjectConfigSchema.parse(JSON.parse(await fs.readFile(path.join(agentDir, ".aai", "project.json"), "utf-8")));
146
- } catch (error) {
147
- consola.debug(`Failed to read project config from ${path.join(agentDir, ".aai", "project.json")}:`, error);
148
- return null;
149
- }
150
- }
151
- /**
152
- * Writes `.aai/project.json` to an agent directory.
153
- */
154
- async function writeProjectConfig(agentDir, data) {
155
- const aaiDir = path.join(agentDir, ".aai");
156
- await fs.mkdir(aaiDir, { recursive: true });
157
- await fs.writeFile(path.join(aaiDir, "project.json"), `${JSON.stringify(data, null, 2)}\n`);
158
- }
159
- /**
160
- * Read project config (throws if missing), resolve API key and server URL.
161
- * Shared by secret commands.
162
- */
163
- async function getServerInfo(cwd, explicitServer, explicitApiKey) {
164
- const config = await readProjectConfig(cwd);
165
- if (!config) throw new Error("No .aai/project.json found — run `aai deploy` first");
166
- const apiKey = explicitApiKey ?? await getApiKey();
167
- return {
168
- serverUrl: resolveServerUrl(explicitServer, config.serverUrl),
169
- slug: config.slug,
170
- apiKey
171
- };
172
- }
173
- /** Default production server URL for agent deployments. */
174
- const DEFAULT_SERVER = "https://aai-agent.fly.dev";
175
- /** Default local dev server URL. */
176
- const DEFAULT_DEV_SERVER = "http://localhost:8787";
177
- /** Check if the CLI is running from the monorepo (dev mode). */
178
- function isDevMode() {
179
- const cliDir = path.dirname(fileURLToPath(import.meta.url));
180
- const root1 = path.resolve(cliDir, "../..");
181
- const root2 = path.resolve(cliDir, "../../..");
182
- return existsSync(path.join(root1, "pnpm-workspace.yaml")) || existsSync(path.join(root2, "pnpm-workspace.yaml"));
183
- }
184
- /** Resolve the server URL from an explicit value, project config, or default. */
185
- function resolveServerUrl(explicit, configUrl) {
186
- if (explicit) return explicit;
187
- if (isDevMode()) return DEFAULT_DEV_SERVER;
188
- return configUrl ?? "https://aai-agent.fly.dev";
189
- }
190
- async function fileExists(p) {
191
- try {
192
- await fs.access(p);
193
- return true;
194
- } catch (error) {
195
- consola.debug(`File access check failed for ${p}:`, error);
196
- return false;
197
- }
198
- }
199
- /**
200
- * Loads agent metadata from a directory by checking for `agent.ts` and
201
- * resolving the client entry point.
202
- *
203
- * Env vars for deployed agents are managed on the server via
204
- * `aai secret put`. For local dev, `.env` is loaded by `resolveServerEnv`.
205
- */
206
- async function loadAgent(dir) {
207
- if (!await fileExists(path.join(dir, "agent.ts"))) return null;
208
- const slug = (await readProjectConfig(dir))?.slug ?? "";
209
- const clientEntry = await fileExists(path.join(dir, "client.tsx")) ? path.join(dir, "client.tsx") : "";
210
- return {
211
- slug,
212
- dir,
213
- entryPoint: path.join(dir, "agent.ts"),
214
- clientEntry
215
- };
216
- }
217
- //#endregion
218
- export { getServerInfo as a, resolveCwd as c, askPassword as d, getApiKey as i, resolveServerUrl as l, ensureApiKeyInEnv as n, isDevMode as o, fileExists as r, readProjectConfig as s, _discover_exports as t, writeProjectConfig as u };
@@ -1,56 +0,0 @@
1
- #!/usr/bin/env node
2
- import { t as downloadAndMergeTemplate } from "./_templates-BDkbj3TM.mjs";
3
- import path from "node:path";
4
- import fs from "node:fs/promises";
5
- //#region _init.ts
6
- async function runInit(opts) {
7
- const { targetDir, template } = opts;
8
- await downloadAndMergeTemplate(template, targetDir);
9
- try {
10
- await fs.copyFile(path.join(targetDir, ".env.example"), path.join(targetDir, ".env"));
11
- } catch {}
12
- const readmePath = path.join(targetDir, "README.md");
13
- const readme = `# ${path.basename(path.resolve(targetDir))}
14
-
15
- A voice agent built with [aai](https://github.com/anthropics/aai).
16
-
17
- ## Getting started
18
-
19
- \`\`\`sh
20
- npm install # Install dependencies
21
- aai dev # Run locally (opens browser)
22
- aai deploy # Deploy to production
23
- \`\`\`
24
-
25
- ## Secrets
26
-
27
- Access secrets in your agent via \`ctx.env.MY_KEY\`.
28
-
29
- **Local development** — add secrets to \`.env\` (auto-loaded by \`aai dev\`):
30
-
31
- \`\`\`sh
32
- ALPHA_VANTAGE_KEY=sk-abc123
33
- MY_API_KEY=secret-value
34
- \`\`\`
35
-
36
- **Production** — set secrets on the server:
37
-
38
- \`\`\`sh
39
- aai secret put MY_KEY # Set a secret (prompts for value)
40
- aai secret list # List secret names
41
- aai secret delete MY_KEY # Remove a secret
42
- \`\`\`
43
-
44
- ## Learn more
45
-
46
- See \`CLAUDE.md\` for the full agent API reference.
47
- `;
48
- try {
49
- await fs.writeFile(readmePath, readme, { flag: "wx" });
50
- } catch (err) {
51
- if (!(err instanceof Error && "code" in err && err.code === "EEXIST")) throw err;
52
- }
53
- return targetDir;
54
- }
55
- //#endregion
56
- export { runInit };
@@ -1,72 +0,0 @@
1
- #!/usr/bin/env node
2
- import { o as isDevMode } from "./_discover-DzsMlg3G.mjs";
3
- import { existsSync } from "node:fs";
4
- import path from "node:path";
5
- import { fileURLToPath } from "node:url";
6
- import fs 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
- /** List available templates with descriptions. */
27
- async function listTemplates() {
28
- const root = await resolveTemplatesDir();
29
- const dir = path.join(root, "templates");
30
- const names = (await fs.readdir(dir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name).sort((a, b) => a.localeCompare(b));
31
- let descriptions = {};
32
- try {
33
- descriptions = JSON.parse(await fs.readFile(path.join(root, "templates.json"), "utf-8"));
34
- } catch {}
35
- return names.map((name) => ({
36
- name,
37
- description: descriptions[name] ?? ""
38
- }));
39
- }
40
- /**
41
- * Download a template into targetDir, merging scaffold files underneath.
42
- */
43
- async function downloadAndMergeTemplate(template, targetDir) {
44
- const root = await resolveTemplatesDir();
45
- const templatesDir = path.join(root, "templates");
46
- const names = (await fs.readdir(templatesDir, { withFileTypes: true })).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)) {
54
- const entries = await fs.readdir(scaffoldDir, {
55
- recursive: true,
56
- withFileTypes: true
57
- });
58
- for (const entry of entries) {
59
- if (!entry.isFile()) continue;
60
- const rel = path.relative(scaffoldDir, path.join(entry.parentPath, entry.name));
61
- const destPath = path.join(targetDir, rel);
62
- await fs.mkdir(path.dirname(destPath), { recursive: true });
63
- try {
64
- await fs.copyFile(path.join(scaffoldDir, rel), destPath, fs.constants.COPYFILE_EXCL);
65
- } catch (err) {
66
- if (!(err instanceof Error && "code" in err && err.code === "EEXIST")) throw err;
67
- }
68
- }
69
- }
70
- }
71
- //#endregion
72
- export { listTemplates as n, downloadAndMergeTemplate as t };
@@ -1,30 +0,0 @@
1
- #!/usr/bin/env node
2
- import * as p from "@clack/prompts";
3
- import { colorize } from "consola/utils";
4
- //#region _ui.ts
5
- /**
6
- * Unified CLI output using @clack/prompts style (◐ ◇ │).
7
- *
8
- * All commands should use these helpers instead of consola directly
9
- * so the output is visually consistent.
10
- */
11
- const log = {
12
- step: (msg) => p.log.step(msg),
13
- success: (msg) => p.log.success(msg),
14
- info: (msg) => p.log.info(msg),
15
- warn: (msg) => p.log.warn(msg),
16
- error: (msg) => p.log.error(msg),
17
- message: (msg) => p.log.message(msg)
18
- };
19
- /** Format a URL for display. */
20
- function fmtUrl(url) {
21
- return colorize("cyanBright", url);
22
- }
23
- /** Parse and validate a port string. Returns the numeric port or throws. */
24
- function parsePort(raw) {
25
- const port = Number.parseInt(raw, 10);
26
- if (Number.isNaN(port) || port < 0 || port > 65535) throw new Error(`Invalid port: ${raw}. Must be a number between 0 and 65535.`);
27
- return port;
28
- }
29
- //#endregion
30
- export { fmtUrl, log, parsePort };
@@ -1,34 +0,0 @@
1
- #!/usr/bin/env node
2
- import { a as getServerInfo } from "./_discover-DzsMlg3G.mjs";
3
- import { log } from "./_ui-DWGXImbO.mjs";
4
- import { n as apiError, r as apiRequest, t as HINT_INVALID_API_KEY } from "./_api-client-H4MFOr8j.mjs";
5
- //#region _delete.ts
6
- async function runDelete(opts) {
7
- const fetchFn = opts.fetch ?? globalThis.fetch.bind(globalThis);
8
- const resp = await apiRequest(`${opts.url}/${opts.slug}`, {
9
- method: "DELETE",
10
- apiKey: opts.apiKey,
11
- action: "delete"
12
- }, fetchFn);
13
- if (resp.ok) return;
14
- const text = await resp.text();
15
- let hint;
16
- if (resp.status === 401) hint = HINT_INVALID_API_KEY;
17
- else if (resp.status === 404) hint = "The agent may not be deployed. Check `.aai/project.json` for the correct slug.";
18
- throw apiError("delete", resp.status, text, hint);
19
- }
20
- //#endregion
21
- //#region delete.ts
22
- async function runDeleteCommand(opts) {
23
- const { cwd } = opts;
24
- const { serverUrl, slug, apiKey } = await getServerInfo(cwd, opts.server);
25
- log.step(`Deleting ${slug}`);
26
- await runDelete({
27
- url: serverUrl,
28
- slug,
29
- apiKey
30
- });
31
- log.success(`Deleted ${serverUrl}/${slug}`);
32
- }
33
- //#endregion
34
- export { runDeleteCommand };
@@ -1,53 +0,0 @@
1
- #!/usr/bin/env node
2
- import { i as getApiKey, l as resolveServerUrl, s as readProjectConfig, u as writeProjectConfig } from "./_discover-DzsMlg3G.mjs";
3
- import { buildAgentBundle } from "./_bundler-DQtFVeww.mjs";
4
- import { fmtUrl, log } from "./_ui-DWGXImbO.mjs";
5
- import { n as apiError, r as apiRequest, t as HINT_INVALID_API_KEY } from "./_api-client-H4MFOr8j.mjs";
6
- //#region _deploy.ts
7
- async function runDeploy(opts) {
8
- const fetchFn = opts.fetch ?? globalThis.fetch.bind(globalThis);
9
- const body = JSON.stringify({
10
- ...opts.slug ? { slug: opts.slug } : {},
11
- env: opts.env,
12
- worker: opts.bundle.worker,
13
- clientFiles: opts.bundle.clientFiles
14
- });
15
- const resp = await apiRequest(`${opts.url}/deploy`, {
16
- method: "POST",
17
- body,
18
- apiKey: opts.apiKey,
19
- action: "deploy"
20
- }, fetchFn);
21
- if (resp.ok) return { slug: (await resp.json()).slug };
22
- const text = await resp.text();
23
- let hint;
24
- if (resp.status === 401) hint = HINT_INVALID_API_KEY;
25
- else if (resp.status === 413) hint = "Your bundle is too large. Try reducing dependencies or splitting your agent.";
26
- throw new Error(apiError("deploy", resp.status, text, hint).message);
27
- }
28
- //#endregion
29
- //#region deploy.ts
30
- async function runDeployCommand(opts) {
31
- const { cwd } = opts;
32
- const apiKey = await getApiKey();
33
- const projectConfig = await readProjectConfig(cwd);
34
- const serverUrl = resolveServerUrl(opts.server, projectConfig?.serverUrl);
35
- const bundle = await buildAgentBundle(cwd);
36
- const slug = projectConfig?.slug;
37
- log.step(`Deploying${slug ? ` ${slug}` : ""}…`);
38
- const deployed = await runDeploy({
39
- url: serverUrl,
40
- bundle,
41
- env: { ASSEMBLYAI_API_KEY: apiKey },
42
- ...slug ? { slug } : {},
43
- apiKey
44
- });
45
- await writeProjectConfig(cwd, {
46
- slug: deployed.slug,
47
- serverUrl
48
- });
49
- const agentUrl = `${serverUrl}/${deployed.slug}`;
50
- log.success(`Deployed ${fmtUrl(agentUrl)}`);
51
- }
52
- //#endregion
53
- export { runDeployCommand };
@@ -1,18 +0,0 @@
1
- #!/usr/bin/env node
2
- import { fmtUrl, log, parsePort } from "./_ui-DWGXImbO.mjs";
3
- import path from "node:path";
4
- import { createServer } from "vite";
5
- import { colorize } from "consola/utils";
6
- //#region dev.ts
7
- async function runDevCommand(opts) {
8
- const port = parsePort(opts.port);
9
- const agentName = path.basename(path.resolve(opts.cwd));
10
- await (await createServer({
11
- root: opts.cwd,
12
- server: { port }
13
- })).listen();
14
- log.success(`${colorize("bold", agentName)} running at ${fmtUrl(`http://localhost:${port}`)}`);
15
- log.info("Press Ctrl-C to stop");
16
- }
17
- //#endregion
18
- export { runDevCommand };
@@ -1,114 +0,0 @@
1
- #!/usr/bin/env node
2
- import { c as resolveCwd, n as ensureApiKeyInEnv, r as fileExists } from "./_discover-DzsMlg3G.mjs";
3
- import { n as listTemplates } from "./_templates-BDkbj3TM.mjs";
4
- import { log } from "./_ui-DWGXImbO.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
- /** Prompt for project name or return default when --yes is set. */
17
- async function promptProjectName(yes) {
18
- if (yes) return DEFAULT_PROJECT_NAME;
19
- const result = await p.text({
20
- message: "What is your project named?",
21
- placeholder: DEFAULT_PROJECT_NAME,
22
- defaultValue: DEFAULT_PROJECT_NAME
23
- });
24
- if (p.isCancel(result)) {
25
- p.cancel("Setup cancelled");
26
- process.exit(0);
27
- }
28
- return result || DEFAULT_PROJECT_NAME;
29
- }
30
- /** Prompt for template selection or return default when --yes is set. */
31
- async function promptTemplate(yes) {
32
- if (yes) return DEFAULT_TEMPLATE;
33
- const templates = await listTemplates();
34
- const result = await p.select({
35
- message: "Which template would you like to use?",
36
- options: templates.map((t) => ({
37
- value: t.name,
38
- label: t.name,
39
- hint: t.description
40
- })),
41
- initialValue: DEFAULT_TEMPLATE
42
- });
43
- if (p.isCancel(result)) {
44
- p.cancel("Setup cancelled");
45
- process.exit(0);
46
- }
47
- return result;
48
- }
49
- /** Enable corepack so pnpm is available (scaffold declares packageManager: pnpm). */
50
- async function ensurePnpm() {
51
- try {
52
- await execFileAsync("corepack", ["enable"]);
53
- } catch {}
54
- }
55
- /** Install deps with pnpm (scaffold declares packageManager: pnpm). */
56
- async function installDeps(cwd) {
57
- if (await fileExists(path.join(cwd, "node_modules"))) return;
58
- let pkgJson;
59
- try {
60
- pkgJson = JSON.parse(await fs.readFile(path.join(cwd, "package.json"), "utf-8"));
61
- } catch {
62
- pkgJson = {};
63
- }
64
- const deps = Object.keys(pkgJson.dependencies ?? {});
65
- const devDeps = Object.keys(pkgJson.devDependencies ?? {});
66
- if (deps.length === 0 && devDeps.length === 0) return;
67
- await ensurePnpm();
68
- const s = p.spinner();
69
- s.start("Installing dependencies with pnpm");
70
- try {
71
- await execFileAsync("pnpm", ["install", "--ignore-workspace"], { cwd });
72
- s.stop("Dependencies installed");
73
- } catch (err) {
74
- const msg = errorMessage(err);
75
- s.stop("Dependency install failed");
76
- log.warn(`pnpm install failed: ${msg}`);
77
- log.warn("Run `corepack enable && pnpm install` manually in the project directory.");
78
- }
79
- }
80
- /** Format the dev command for the "Next steps" note. */
81
- function devCommand() {
82
- return "aai dev";
83
- }
84
- async function runInitCommand(opts, extra) {
85
- if (!extra?.quiet) p.intro(colorize("cyanBright", "Create a new voice agent"));
86
- if (!opts.skipApi) await ensureApiKeyInEnv();
87
- const dir = opts.dir ?? await promptProjectName(opts.yes);
88
- const cwd = path.resolve(resolveCwd(), dir);
89
- if (!opts.force && await fileExists(path.join(cwd, "agent.ts"))) throw new Error(`agent.ts already exists in this directory. Use ${colorize("cyanBright", "--force")} to overwrite.`);
90
- const template = opts.template ?? await promptTemplate(opts.yes);
91
- const s = p.spinner();
92
- s.start(`Creating ${dir} from ${template} template`);
93
- const { runInit } = await import("./_init-CbMs9S2O.mjs");
94
- await runInit({
95
- targetDir: cwd,
96
- template
97
- });
98
- s.stop("Project created");
99
- await installDeps(cwd);
100
- if (!(opts.skipDeploy || extra?.quiet)) {
101
- const { runDeployCommand } = await import("./deploy-CLs8gLHV.mjs");
102
- await runDeployCommand({
103
- cwd,
104
- ...opts.server ? { server: opts.server } : {}
105
- });
106
- }
107
- if (!extra?.quiet) {
108
- log.success(`Created ${dir}`);
109
- log.info(`Next: cd ${dir} && ${devCommand()}`);
110
- }
111
- return cwd;
112
- }
113
- //#endregion
114
- export { runInitCommand };
@@ -1,46 +0,0 @@
1
- #!/usr/bin/env node
2
- import { a as getServerInfo, d as askPassword } from "./_discover-DzsMlg3G.mjs";
3
- import { log } from "./_ui-DWGXImbO.mjs";
4
- import { n as apiError, r as apiRequest, t as HINT_INVALID_API_KEY } from "./_api-client-H4MFOr8j.mjs";
5
- //#region secret.ts
6
- async function secretRequest(cwd, pathSuffix, init, server) {
7
- const { serverUrl, slug, apiKey } = await getServerInfo(cwd, server);
8
- const resp = await apiRequest(`${serverUrl}/${slug}/secret${pathSuffix}`, {
9
- ...init,
10
- apiKey,
11
- action: "secret"
12
- });
13
- if (!resp.ok) {
14
- const text = await resp.text();
15
- const hint = resp.status === 401 ? HINT_INVALID_API_KEY : void 0;
16
- throw apiError("secret", resp.status, text, hint);
17
- }
18
- return {
19
- resp,
20
- slug
21
- };
22
- }
23
- async function runSecretPut(cwd, name, server) {
24
- const value = await askPassword(`Enter value for ${name}`);
25
- if (!value) throw new Error("No value provided");
26
- const { slug } = await secretRequest(cwd, "", {
27
- method: "PUT",
28
- body: JSON.stringify({ [name]: value })
29
- }, server);
30
- log.success(`Set ${name} for ${slug}`);
31
- }
32
- async function runSecretDelete(cwd, name, server) {
33
- const { slug } = await secretRequest(cwd, `/${name}`, { method: "DELETE" }, server);
34
- log.success(`Deleted ${name} from ${slug}`);
35
- }
36
- async function runSecretList(cwd, server) {
37
- const { resp } = await secretRequest(cwd, "", void 0, server);
38
- const { vars } = await resp.json();
39
- if (vars.length === 0) log.info("No secrets set. Use `aai secret put <name>` to add one.");
40
- else {
41
- log.message(`${vars.length} secret${vars.length === 1 ? "" : "s"}:`);
42
- for (const name of vars) log.message(` ${name}`);
43
- }
44
- }
45
- //#endregion
46
- export { runSecretDelete, runSecretList, runSecretPut };
@@ -1,44 +0,0 @@
1
- #!/usr/bin/env node
2
- import { log } from "./_ui-DWGXImbO.mjs";
3
- import { existsSync } from "node:fs";
4
- import path from "node:path";
5
- import { execFileSync } from "node:child_process";
6
- //#region test.ts
7
- /**
8
- * `aai test` — run agent tests via vitest.
9
- */
10
- /**
11
- * Run vitest in the given project directory.
12
- *
13
- * Returns `true` if tests passed, `false` if no test files exist.
14
- * Throws on test failure.
15
- */
16
- function runVitest(cwd) {
17
- if (!(existsSync(path.join(cwd, "agent.test.ts")) || existsSync(path.join(cwd, "agent.test.js")))) return false;
18
- execFileSync("npx", [
19
- "vitest",
20
- "run",
21
- "--root",
22
- ".",
23
- existsSync(path.join(cwd, "agent.test.ts")) ? "agent.test.ts" : "agent.test.js"
24
- ], {
25
- cwd,
26
- stdio: "inherit",
27
- env: {
28
- ...process.env,
29
- NODE_OPTIONS: "--experimental-strip-types"
30
- }
31
- });
32
- return true;
33
- }
34
- /** Run agent tests. Used by `aai test`. */
35
- async function runTestCommand(cwd) {
36
- log.step("Running agent tests");
37
- if (!runVitest(cwd)) {
38
- log.info("No test file found. Create agent.test.ts to add tests.");
39
- return;
40
- }
41
- log.success("Tests passed");
42
- }
43
- //#endregion
44
- export { runTestCommand, runVitest };