@alexkroman1/aai-cli 0.12.1 → 0.12.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.
@@ -78,7 +78,7 @@ async function bundleAgent(agent, opts) {
78
78
  } catch (err) {
79
79
  throw new BundleError(errorMessage(err), { cause: err });
80
80
  }
81
- if (!(opts?.skipClient ?? !agent.clientEntry)) try {
81
+ if (!opts?.skipClient && agent.clientEntry) try {
82
82
  await build({
83
83
  root: agent.dir,
84
84
  base: "./",
@@ -102,7 +102,7 @@ async function bundleAgent(agent, opts) {
102
102
  };
103
103
  }
104
104
  async function buildAgentBundle(cwd) {
105
- const { loadAgent } = await import("./_discover-a8yIuqEp.mjs").then((n) => n.t);
105
+ const { loadAgent } = await import("./_discover-DzsMlg3G.mjs").then((n) => n.t);
106
106
  const { log } = await import("./_ui-DWGXImbO.mjs");
107
107
  const agent = await loadAgent(cwd);
108
108
  if (!agent) throw new Error("No agent found — run `aai init` first");
@@ -3,8 +3,9 @@ import { existsSync } from "node:fs";
3
3
  import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import fs from "node:fs/promises";
6
+ import os from "node:os";
7
+ import ci from "ci-info";
6
8
  import { consola } from "consola";
7
- import { humanId } from "human-id";
8
9
  import { z } from "zod";
9
10
  import { createInterface } from "node:readline";
10
11
  //#region \0rolldown/runtime.js
@@ -22,9 +23,10 @@ var __exportAll = (all, no_symbols) => {
22
23
  //#region _prompts.ts
23
24
  /**
24
25
  * Prompt the user for a password (masked input).
25
- * Returns the entered string.
26
+ * Throws in CI or non-TTY environments instead of hanging.
26
27
  */
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.`);
28
30
  const rl = createInterface({
29
31
  input: process.stdin,
30
32
  output: process.stdout
@@ -32,11 +34,11 @@ async function askPassword(message) {
32
34
  process.stdout.write(`${message}: `);
33
35
  const stdin = process.stdin;
34
36
  const wasRaw = stdin.isRaw;
35
- if (stdin.isTTY) stdin.setRawMode(true);
37
+ stdin.setRawMode(true);
36
38
  try {
37
39
  return await readMasked(stdin);
38
40
  } finally {
39
- if (stdin.isTTY) stdin.setRawMode(wasRaw ?? false);
41
+ stdin.setRawMode(wasRaw ?? false);
40
42
  rl.close();
41
43
  }
42
44
  }
@@ -65,8 +67,8 @@ var _discover_exports = /* @__PURE__ */ __exportAll({
65
67
  DEFAULT_SERVER: () => DEFAULT_SERVER,
66
68
  ensureApiKeyInEnv: () => ensureApiKeyInEnv,
67
69
  fileExists: () => fileExists,
68
- generateSlug: () => generateSlug,
69
70
  getApiKey: () => getApiKey,
71
+ getConfigDir: () => getConfigDir,
70
72
  getServerInfo: () => getServerInfo,
71
73
  isDevMode: () => isDevMode,
72
74
  loadAgent: () => loadAgent,
@@ -85,16 +87,11 @@ const ProjectConfigSchema = z.object({
85
87
  function resolveCwd() {
86
88
  return process.env.INIT_CWD || process.cwd();
87
89
  }
88
- /**
89
- * Generates a human-readable slug using human-id.
90
- */
91
- function generateSlug() {
92
- return humanId({
93
- separator: "-",
94
- capitalize: false
95
- });
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");
96
93
  }
97
- const CONFIG_DIR = path.join(process.env.HOME ?? process.env.USERPROFILE ?? ".", ".config", "aai");
94
+ const CONFIG_DIR = getConfigDir();
98
95
  const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
99
96
  async function readAuthConfig() {
100
97
  try {
@@ -120,6 +117,7 @@ async function getApiKey() {
120
117
  if (process.env.ASSEMBLYAI_API_KEY) return process.env.ASSEMBLYAI_API_KEY;
121
118
  const config = await readAuthConfig();
122
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.");
123
121
  const { log } = await import("./_ui-DWGXImbO.mjs");
124
122
  log.info("Get your API key at https://www.assemblyai.com/dashboard/signup");
125
123
  log.info("Or set the ASSEMBLYAI_API_KEY environment variable to skip this prompt.");
@@ -207,7 +205,7 @@ async function fileExists(p) {
207
205
  */
208
206
  async function loadAgent(dir) {
209
207
  if (!await fileExists(path.join(dir, "agent.ts"))) return null;
210
- const slug = (await readProjectConfig(dir))?.slug ?? generateSlug();
208
+ const slug = (await readProjectConfig(dir))?.slug ?? "";
211
209
  const clientEntry = await fileExists(path.join(dir, "client.tsx")) ? path.join(dir, "client.tsx") : "";
212
210
  return {
213
211
  slug,
@@ -217,4 +215,4 @@ async function loadAgent(dir) {
217
215
  };
218
216
  }
219
217
  //#endregion
220
- export { getApiKey as a, readProjectConfig as c, writeProjectConfig as d, askPassword as f, generateSlug as i, resolveCwd as l, ensureApiKeyInEnv as n, getServerInfo as o, fileExists as r, isDevMode as s, _discover_exports as t, resolveServerUrl as u };
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,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as downloadAndMergeTemplate } from "./_templates-CtZBILce.mjs";
2
+ import { t as downloadAndMergeTemplate } from "./_templates-BDkbj3TM.mjs";
3
3
  import path from "node:path";
4
4
  import fs from "node:fs/promises";
5
5
  //#region _init.ts
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { s as isDevMode } from "./_discover-a8yIuqEp.mjs";
2
+ import { o as isDevMode } from "./_discover-DzsMlg3G.mjs";
3
3
  import { existsSync } from "node:fs";
4
4
  import path from "node:path";
5
5
  import { fileURLToPath } from "node:url";
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { l as resolveCwd, n as ensureApiKeyInEnv, r as fileExists } from "./_discover-a8yIuqEp.mjs";
2
+ import { c as resolveCwd, n as ensureApiKeyInEnv, r as fileExists } from "./_discover-DzsMlg3G.mjs";
3
3
  import { readFileSync } from "node:fs";
4
4
  import path from "node:path";
5
5
  import { fileURLToPath } from "node:url";
@@ -35,7 +35,7 @@ function findPkgJson(dir) {
35
35
  const VERSION = JSON.parse(findPkgJson(cliDir)).version;
36
36
  async function ensureAgent(cwd, yes) {
37
37
  if (!await fileExists(path.join(cwd, "agent.ts"))) {
38
- const { runInitCommand } = await import("./init-DTfpWUv8.mjs");
38
+ const { runInitCommand } = await import("./init-CNuntiij.mjs");
39
39
  return runInitCommand({ yes }, { quiet: true });
40
40
  }
41
41
  return cwd;
@@ -47,6 +47,16 @@ async function setup(args, opts) {
47
47
  if (opts?.apiKey) await ensureApiKeyInEnv();
48
48
  return cwd;
49
49
  }
50
+ /** Catch command errors and display a clean message instead of a raw stack trace. */
51
+ async function handleErrors(fn) {
52
+ try {
53
+ await fn();
54
+ } catch (err) {
55
+ const { log } = await import("./_ui-DWGXImbO.mjs");
56
+ log.error(err instanceof Error ? err.message : String(err));
57
+ process.exit(1);
58
+ }
59
+ }
50
60
  const init = defineCommand({
51
61
  meta: {
52
62
  name: "init",
@@ -68,6 +78,7 @@ const init = defineCommand({
68
78
  alias: "f",
69
79
  description: "Overwrite existing files"
70
80
  },
81
+ server: sharedArgs.server,
71
82
  yes: sharedArgs.yes,
72
83
  skipApi: {
73
84
  type: "boolean",
@@ -79,14 +90,17 @@ const init = defineCommand({
79
90
  }
80
91
  },
81
92
  async run({ args }) {
82
- const { runInitCommand } = await import("./init-DTfpWUv8.mjs");
83
- await runInitCommand({
84
- dir: args.dir,
85
- template: args.template,
86
- force: args.force,
87
- yes: args.yes,
88
- skipApi: args.skipApi,
89
- skipDeploy: args.skipDeploy
93
+ await handleErrors(async () => {
94
+ const { runInitCommand } = await import("./init-CNuntiij.mjs");
95
+ await runInitCommand({
96
+ dir: args.dir,
97
+ template: args.template,
98
+ force: args.force,
99
+ yes: args.yes,
100
+ skipApi: args.skipApi,
101
+ skipDeploy: args.skipDeploy,
102
+ server: args.server
103
+ });
90
104
  });
91
105
  }
92
106
  });
@@ -97,17 +111,20 @@ const dev = defineCommand({
97
111
  },
98
112
  args: {
99
113
  port: sharedArgs.port,
114
+ server: sharedArgs.server,
100
115
  yes: sharedArgs.yes
101
116
  },
102
117
  async run({ args }) {
103
- const cwd = await setup(args, {
104
- agent: true,
105
- apiKey: true
106
- });
107
- const { runDevCommand } = await import("./dev-DEZKrw8v.mjs");
108
- await runDevCommand({
109
- cwd,
110
- port: args.port
118
+ await handleErrors(async () => {
119
+ const cwd = await setup(args, {
120
+ agent: true,
121
+ apiKey: true
122
+ });
123
+ const { runDevCommand } = await import("./dev-DEZKrw8v.mjs");
124
+ await runDevCommand({
125
+ cwd,
126
+ port: args.port
127
+ });
111
128
  });
112
129
  }
113
130
  });
@@ -117,9 +134,11 @@ const test = defineCommand({
117
134
  description: "Run agent tests"
118
135
  },
119
136
  async run() {
120
- const cwd = await setup();
121
- const { runTestCommand } = await import("./test-CYoqKJP0.mjs");
122
- await runTestCommand(cwd);
137
+ await handleErrors(async () => {
138
+ const cwd = await setup();
139
+ const { runTestCommand } = await import("./test-yqHGZPF3.mjs");
140
+ await runTestCommand(cwd);
141
+ });
123
142
  }
124
143
  });
125
144
  const build = defineCommand({
@@ -128,6 +147,7 @@ const build = defineCommand({
128
147
  description: "Bundle agent without deploying"
129
148
  },
130
149
  args: {
150
+ server: sharedArgs.server,
131
151
  yes: sharedArgs.yes,
132
152
  skipTests: {
133
153
  type: "boolean",
@@ -135,13 +155,15 @@ const build = defineCommand({
135
155
  }
136
156
  },
137
157
  async run({ args }) {
138
- const cwd = await setup(args, { agent: true });
139
- if (!args.skipTests) {
140
- const { runVitest } = await import("./test-CYoqKJP0.mjs");
141
- runVitest(cwd);
142
- }
143
- const { runBuildCommand } = await import("./_bundler-2yKukgnU.mjs");
144
- await runBuildCommand(cwd);
158
+ await handleErrors(async () => {
159
+ const cwd = await setup(args, { agent: true });
160
+ if (!args.skipTests) {
161
+ const { runVitest } = await import("./test-yqHGZPF3.mjs");
162
+ runVitest(cwd);
163
+ }
164
+ const { runBuildCommand } = await import("./_bundler-DQtFVeww.mjs");
165
+ await runBuildCommand(cwd);
166
+ });
145
167
  }
146
168
  });
147
169
  const deploy = defineCommand({
@@ -151,26 +173,17 @@ const deploy = defineCommand({
151
173
  },
152
174
  args: {
153
175
  server: sharedArgs.server,
154
- dryRun: {
155
- type: "boolean",
156
- description: "Validate and bundle without deploying"
157
- },
158
176
  yes: sharedArgs.yes
159
177
  },
160
178
  async run({ args }) {
161
- const cwd = await setup(args, { agent: true });
162
- const { runDeployCommand } = await import("./deploy-CnscqVZv.mjs");
163
- try {
179
+ await handleErrors(async () => {
180
+ const cwd = await setup(args, { agent: true });
181
+ const { runDeployCommand } = await import("./deploy-CLs8gLHV.mjs");
164
182
  await runDeployCommand({
165
183
  cwd,
166
- ...args.server ? { server: args.server } : {},
167
- ...args.dryRun ? { dryRun: args.dryRun } : {}
184
+ ...args.server ? { server: args.server } : {}
168
185
  });
169
- } catch (err) {
170
- const { log } = await import("./_ui-DWGXImbO.mjs");
171
- log.error(err instanceof Error ? err.message : String(err));
172
- process.exit(1);
173
- }
186
+ });
174
187
  }
175
188
  });
176
189
  const del = defineCommand({
@@ -180,11 +193,13 @@ const del = defineCommand({
180
193
  },
181
194
  args: { server: sharedArgs.server },
182
195
  async run({ args }) {
183
- const cwd = await setup();
184
- const { runDeleteCommand } = await import("./delete-BvRel3Tw.mjs");
185
- await runDeleteCommand({
186
- cwd,
187
- ...args.server ? { server: args.server } : {}
196
+ await handleErrors(async () => {
197
+ const cwd = await setup();
198
+ const { runDeleteCommand } = await import("./delete-CalIL6Pg.mjs");
199
+ await runDeleteCommand({
200
+ cwd,
201
+ ...args.server ? { server: args.server } : {}
202
+ });
188
203
  });
189
204
  }
190
205
  });
@@ -199,15 +214,20 @@ const secret = defineCommand({
199
214
  name: "put",
200
215
  description: "Create or update a secret"
201
216
  },
202
- args: { name: {
203
- type: "positional",
204
- description: "Secret name",
205
- required: true
206
- } },
217
+ args: {
218
+ name: {
219
+ type: "positional",
220
+ description: "Secret name",
221
+ required: true
222
+ },
223
+ server: sharedArgs.server
224
+ },
207
225
  async run({ args }) {
208
- const cwd = await setup(void 0, { apiKey: true });
209
- const { runSecretPut } = await import("./secret-DLosn47q.mjs");
210
- await runSecretPut(cwd, args.name);
226
+ await handleErrors(async () => {
227
+ const cwd = await setup(void 0, { apiKey: true });
228
+ const { runSecretPut } = await import("./secret-xmLQ96Yz.mjs");
229
+ await runSecretPut(cwd, args.name, args.server);
230
+ });
211
231
  }
212
232
  }),
213
233
  delete: defineCommand({
@@ -215,15 +235,20 @@ const secret = defineCommand({
215
235
  name: "delete",
216
236
  description: "Delete a secret"
217
237
  },
218
- args: { name: {
219
- type: "positional",
220
- description: "Secret name",
221
- required: true
222
- } },
238
+ args: {
239
+ name: {
240
+ type: "positional",
241
+ description: "Secret name",
242
+ required: true
243
+ },
244
+ server: sharedArgs.server
245
+ },
223
246
  async run({ args }) {
224
- const cwd = await setup(void 0, { apiKey: true });
225
- const { runSecretDelete } = await import("./secret-DLosn47q.mjs");
226
- await runSecretDelete(cwd, args.name);
247
+ await handleErrors(async () => {
248
+ const cwd = await setup(void 0, { apiKey: true });
249
+ const { runSecretDelete } = await import("./secret-xmLQ96Yz.mjs");
250
+ await runSecretDelete(cwd, args.name, args.server);
251
+ });
227
252
  }
228
253
  }),
229
254
  list: defineCommand({
@@ -231,10 +256,13 @@ const secret = defineCommand({
231
256
  name: "list",
232
257
  description: "List all secrets"
233
258
  },
234
- async run() {
235
- const cwd = await setup(void 0, { apiKey: true });
236
- const { runSecretList } = await import("./secret-DLosn47q.mjs");
237
- await runSecretList(cwd);
259
+ args: { server: sharedArgs.server },
260
+ async run({ args }) {
261
+ await handleErrors(async () => {
262
+ const cwd = await setup(void 0, { apiKey: true });
263
+ const { runSecretList } = await import("./secret-xmLQ96Yz.mjs");
264
+ await runSecretList(cwd, args.server);
265
+ });
238
266
  }
239
267
  })
240
268
  }
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { o as getServerInfo } from "./_discover-a8yIuqEp.mjs";
2
+ import { a as getServerInfo } from "./_discover-DzsMlg3G.mjs";
3
3
  import { log } from "./_ui-DWGXImbO.mjs";
4
4
  import { n as apiError, r as apiRequest, t as HINT_INVALID_API_KEY } from "./_api-client-H4MFOr8j.mjs";
5
5
  //#region _delete.ts
@@ -0,0 +1,53 @@
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,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { l as resolveCwd, n as ensureApiKeyInEnv, r as fileExists } from "./_discover-a8yIuqEp.mjs";
3
- import { n as listTemplates } from "./_templates-CtZBILce.mjs";
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
4
  import { log } from "./_ui-DWGXImbO.mjs";
5
5
  import path from "node:path";
6
6
  import fs from "node:fs/promises";
@@ -13,14 +13,6 @@ import { promisify } from "node:util";
13
13
  const execFileAsync = promisify(execFile);
14
14
  const DEFAULT_PROJECT_NAME = "my-voice-agent";
15
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
16
  /** Prompt for project name or return default when --yes is set. */
25
17
  async function promptProjectName(yes) {
26
18
  if (yes) return DEFAULT_PROJECT_NAME;
@@ -54,8 +46,14 @@ async function promptTemplate(yes) {
54
46
  }
55
47
  return result;
56
48
  }
57
- /** Install deps via the detected package manager. */
58
- async function installDeps(cwd, pm) {
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) {
59
57
  if (await fileExists(path.join(cwd, "node_modules"))) return;
60
58
  let pkgJson;
61
59
  try {
@@ -66,16 +64,17 @@ async function installDeps(cwd, pm) {
66
64
  const deps = Object.keys(pkgJson.dependencies ?? {});
67
65
  const devDeps = Object.keys(pkgJson.devDependencies ?? {});
68
66
  if (deps.length === 0 && devDeps.length === 0) return;
67
+ await ensurePnpm();
69
68
  const s = p.spinner();
70
- s.start(`Installing dependencies with ${pm}`);
69
+ s.start("Installing dependencies with pnpm");
71
70
  try {
72
- await execFileAsync(pm, ["install"], { cwd });
71
+ await execFileAsync("pnpm", ["install", "--ignore-workspace"], { cwd });
73
72
  s.stop("Dependencies installed");
74
73
  } catch (err) {
75
74
  const msg = errorMessage(err);
76
75
  s.stop("Dependency install failed");
77
- log.warn(`${pm} install failed: ${msg}`);
78
- log.warn(`Run \`${pm} install\` manually in the project directory.`);
76
+ log.warn(`pnpm install failed: ${msg}`);
77
+ log.warn("Run `corepack enable && pnpm install` manually in the project directory.");
79
78
  }
80
79
  }
81
80
  /** Format the dev command for the "Next steps" note. */
@@ -83,7 +82,6 @@ function devCommand() {
83
82
  return "aai dev";
84
83
  }
85
84
  async function runInitCommand(opts, extra) {
86
- const pm = detectPackageManager();
87
85
  if (!extra?.quiet) p.intro(colorize("cyanBright", "Create a new voice agent"));
88
86
  if (!opts.skipApi) await ensureApiKeyInEnv();
89
87
  const dir = opts.dir ?? await promptProjectName(opts.yes);
@@ -92,16 +90,19 @@ async function runInitCommand(opts, extra) {
92
90
  const template = opts.template ?? await promptTemplate(opts.yes);
93
91
  const s = p.spinner();
94
92
  s.start(`Creating ${dir} from ${template} template`);
95
- const { runInit } = await import("./_init-VzVTpJFZ.mjs");
93
+ const { runInit } = await import("./_init-CbMs9S2O.mjs");
96
94
  await runInit({
97
95
  targetDir: cwd,
98
96
  template
99
97
  });
100
98
  s.stop("Project created");
101
- await installDeps(cwd, pm);
99
+ await installDeps(cwd);
102
100
  if (!(opts.skipDeploy || extra?.quiet)) {
103
- const { runDeployCommand } = await import("./deploy-CnscqVZv.mjs");
104
- await runDeployCommand({ cwd });
101
+ const { runDeployCommand } = await import("./deploy-CLs8gLHV.mjs");
102
+ await runDeployCommand({
103
+ cwd,
104
+ ...opts.server ? { server: opts.server } : {}
105
+ });
105
106
  }
106
107
  if (!extra?.quiet) {
107
108
  log.success(`Created ${dir}`);
@@ -0,0 +1,46 @@
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 };
@@ -2,7 +2,7 @@
2
2
  import { log } from "./_ui-DWGXImbO.mjs";
3
3
  import { existsSync } from "node:fs";
4
4
  import path from "node:path";
5
- import { execSync } from "node:child_process";
5
+ import { execFileSync } from "node:child_process";
6
6
  //#region test.ts
7
7
  /**
8
8
  * `aai test` — run agent tests via vitest.
@@ -15,7 +15,13 @@ 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 --root . ${existsSync(path.join(cwd, "agent.test.ts")) ? "agent.test.ts" : "agent.test.js"}`, {
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
+ ], {
19
25
  cwd,
20
26
  stdio: "inherit",
21
27
  env: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alexkroman1/aai-cli",
3
- "version": "0.12.1",
3
+ "version": "0.12.3",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "aai": "dist/cli.mjs"
@@ -9,19 +9,22 @@
9
9
  "dist"
10
10
  ],
11
11
  "dependencies": {
12
- "@clack/prompts": "^1.1.0",
12
+ "@clack/prompts": "^1.2.0",
13
+ "ci-info": "^4.4.0",
13
14
  "citty": "^0.2.1",
14
15
  "consola": "^3.4.2",
15
16
  "giget": "^2.0.0",
16
- "human-id": "^4.1.3",
17
17
  "vite": "^8.0.3",
18
18
  "zod": "^4.3.6",
19
- "@alexkroman1/aai": "0.12.1"
19
+ "@alexkroman1/aai": "0.12.3"
20
20
  },
21
21
  "devDependencies": {
22
- "playwright": "^1.58.2",
23
- "tsdown": "^0.21.5",
24
- "vitest": "^4.1.1"
22
+ "get-port": "^7.2.0",
23
+ "playwright": "^1.59.0",
24
+ "tree-kill": "^1.2.2",
25
+ "tsdown": "^0.21.7",
26
+ "verdaccio": "^6.3.2",
27
+ "vitest": "^4.1.2"
25
28
  },
26
29
  "engines": {
27
30
  "node": ">=22.6"
@@ -32,6 +35,8 @@
32
35
  "directory": "packages/aai-cli"
33
36
  },
34
37
  "scripts": {
38
+ "test": "vitest run",
39
+ "test:coverage": "vitest run --coverage",
35
40
  "build": "tsdown",
36
41
  "typecheck": "tsc --noEmit",
37
42
  "lint": "biome check .",
@@ -1,92 +0,0 @@
1
- #!/usr/bin/env node
2
- import { a as getApiKey, c as readProjectConfig, d as writeProjectConfig, i as generateSlug, u as resolveServerUrl } from "./_discover-a8yIuqEp.mjs";
3
- import { buildAgentBundle } from "./_bundler-2yKukgnU.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
- const MAX_SLUG_RETRIES = 20;
8
- async function attempt(fetchFn, url, slug, body, apiKey) {
9
- const resp = await apiRequest(`${url}/${slug}/deploy`, {
10
- method: "POST",
11
- body,
12
- apiKey,
13
- action: "deploy"
14
- }, fetchFn);
15
- if (resp.ok) return {
16
- ok: true,
17
- slug
18
- };
19
- const text = await resp.text();
20
- if (resp.status === 403 && text.includes("owned by another")) return {
21
- ok: false,
22
- retry: true
23
- };
24
- let hint;
25
- if (resp.status === 401) hint = HINT_INVALID_API_KEY;
26
- else if (resp.status === 403 && text.includes("Slug")) hint = "This slug is already taken. Set a different slug in .aai/project.json.";
27
- else if (resp.status === 413) hint = "Your bundle is too large. Try reducing dependencies or splitting your agent.";
28
- return {
29
- ok: false,
30
- retry: false,
31
- error: apiError("deploy", resp.status, text, hint).message
32
- };
33
- }
34
- async function runDeploy(opts) {
35
- const fetchFn = opts.fetch ?? globalThis.fetch.bind(globalThis);
36
- const body = JSON.stringify({
37
- env: opts.env,
38
- worker: opts.bundle.worker,
39
- clientFiles: opts.bundle.clientFiles
40
- });
41
- let slug = opts.slug;
42
- for (let i = 0; i < MAX_SLUG_RETRIES; i++) {
43
- const result = await attempt(fetchFn, opts.url, slug, body, opts.apiKey);
44
- if (result.ok) return { slug: result.slug };
45
- if (!result.retry) throw new Error(result.error);
46
- slug = generateSlug();
47
- }
48
- throw new Error(`Could not find an available slug after ${MAX_SLUG_RETRIES} attempts. Set one manually in .aai/project.json.`);
49
- }
50
- //#endregion
51
- //#region deploy.ts
52
- async function deployBundle(opts) {
53
- const { bundle, serverUrl, apiKey, cwd } = opts;
54
- let { slug } = opts;
55
- log.step(`Deploying ${slug}`);
56
- slug = (await runDeploy({
57
- url: serverUrl,
58
- bundle,
59
- env: { ASSEMBLYAI_API_KEY: apiKey },
60
- slug,
61
- apiKey
62
- })).slug;
63
- await writeProjectConfig(cwd, {
64
- slug,
65
- serverUrl
66
- });
67
- const agentUrl = `${serverUrl}/${slug}`;
68
- log.success(`Deployed ${fmtUrl(agentUrl)}`);
69
- return agentUrl;
70
- }
71
- async function runDeployCommand(opts) {
72
- const { cwd } = opts;
73
- const dryRun = opts.dryRun ?? false;
74
- const apiKey = dryRun ? "" : await getApiKey();
75
- const projectConfig = await readProjectConfig(cwd);
76
- const serverUrl = resolveServerUrl(opts.server, projectConfig?.serverUrl);
77
- const bundle = await buildAgentBundle(cwd);
78
- const slug = projectConfig?.slug ?? bundle.slug;
79
- if (dryRun) {
80
- log.info(`Dry run complete — would deploy as ${slug}`);
81
- return;
82
- }
83
- await deployBundle({
84
- bundle,
85
- serverUrl,
86
- apiKey,
87
- slug,
88
- cwd
89
- });
90
- }
91
- //#endregion
92
- export { runDeployCommand };
@@ -1,47 +0,0 @@
1
- #!/usr/bin/env node
2
- import { f as askPassword, o as getServerInfo } from "./_discover-a8yIuqEp.mjs";
3
- import { log } from "./_ui-DWGXImbO.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
- log.success(`Set ${name} for ${slug}`);
32
- }
33
- async function runSecretDelete(cwd, name) {
34
- const { slug } = await apiFetch(cwd, `/${name}`, { method: "DELETE" });
35
- log.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) log.info("No secrets set. Use `aai secret put <name>` to add one.");
41
- else {
42
- log.message(`${vars.length} secret${vars.length === 1 ? "" : "s"}:`);
43
- for (const name of vars) log.message(` ${name}`);
44
- }
45
- }
46
- //#endregion
47
- export { runSecretDelete, runSecretList, runSecretPut };