@alexkroman1/aai-cli 0.12.3 → 1.0.2

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,40 @@
1
+ #!/usr/bin/env node
2
+ import { t as __exportAll } from "./rolldown-runtime-DacLjcLf.mjs";
3
+ import { log } from "@clack/prompts";
4
+ import { colorize } from "consola/utils";
5
+ //#region _ui.ts
6
+ var _ui_exports = /* @__PURE__ */ __exportAll({
7
+ fmtUrl: () => fmtUrl,
8
+ log: () => log$1,
9
+ parsePort: () => parsePort,
10
+ silenceOutput: () => silenceOutput
11
+ });
12
+ const noop = () => {};
13
+ let _delegate = log;
14
+ /** Log instance that delegates to clack (human mode) or no-ops (JSON mode). */
15
+ const log$1 = new Proxy(log, { get(_target, prop, receiver) {
16
+ return Reflect.get(_delegate, prop, receiver);
17
+ } });
18
+ /** Replace all log methods with no-ops. Call once in JSON mode. */
19
+ function silenceOutput() {
20
+ _delegate = {
21
+ info: noop,
22
+ success: noop,
23
+ error: noop,
24
+ warn: noop,
25
+ step: noop,
26
+ message: noop
27
+ };
28
+ }
29
+ /** Format a URL for display. */
30
+ function fmtUrl(url) {
31
+ return colorize("cyanBright", url);
32
+ }
33
+ /** Parse and validate a port string. Returns the numeric port or throws. */
34
+ function parsePort(raw) {
35
+ const port = Number.parseInt(raw, 10);
36
+ if (Number.isNaN(port) || port < 0 || port > 65535) throw new Error(`Invalid port: ${raw}. Must be a number between 0 and 65535.`);
37
+ return port;
38
+ }
39
+ //#endregion
40
+ export { silenceOutput as a, parsePort as i, fmtUrl as n, log$1 as r, _ui_exports as t };
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs/promises";
3
+ import { consola } from "consola";
4
+ //#region _utils.ts
5
+ /** Resolve the working directory from INIT_CWD or process.cwd(). */
6
+ function resolveCwd() {
7
+ return process.env.INIT_CWD || process.cwd();
8
+ }
9
+ /** Validate that a module's default export is a valid agent definition. Throws if invalid. */
10
+ function validateAgentExport(mod) {
11
+ if (!mod?.name || typeof mod.name !== "string") throw new Error("agent.ts must export default agent({ name: ... })");
12
+ }
13
+ async function fileExists(p) {
14
+ try {
15
+ await fs.access(p);
16
+ return true;
17
+ } catch (error) {
18
+ consola.debug(`File access check failed for ${p}:`, error);
19
+ return false;
20
+ }
21
+ }
22
+ //#endregion
23
+ export { resolveCwd as n, validateAgentExport as r, fileExists as t };
package/dist/cli.mjs CHANGED
@@ -1,8 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import { c as resolveCwd, n as ensureApiKeyInEnv, r as fileExists } from "./_discover-DzsMlg3G.mjs";
3
- import { readFileSync } from "node:fs";
2
+ import { i as getOutputMode, r as fail, t as CliError } from "./_output-BKdAJaM5.mjs";
3
+ import { a as silenceOutput } from "./_ui-r2t6_2eP.mjs";
4
+ import { n as resolveCwd, t as fileExists } from "./_utils-DZo3_J_v.mjs";
5
+ import { existsSync, readFileSync } from "node:fs";
4
6
  import path from "node:path";
5
7
  import { fileURLToPath } from "node:url";
8
+ import { errorMessage } from "@alexkroman1/aai";
6
9
  import { defineCommand, runMain } from "citty";
7
10
  //#region cli.ts
8
11
  /** Shared arg definitions for citty commands. */
@@ -22,6 +25,10 @@ const sharedArgs = {
22
25
  type: "boolean",
23
26
  alias: "y",
24
27
  description: "Accept defaults (no prompts)"
28
+ },
29
+ json: {
30
+ type: "boolean",
31
+ description: "Output JSON (auto-detected in non-TTY)"
25
32
  }
26
33
  };
27
34
  const cliDir = path.dirname(fileURLToPath(import.meta.url));
@@ -33,30 +40,44 @@ function findPkgJson(dir) {
33
40
  }
34
41
  }
35
42
  const VERSION = JSON.parse(findPkgJson(cliDir)).version;
36
- async function ensureAgent(cwd, yes) {
37
- if (!await fileExists(path.join(cwd, "agent.ts"))) {
38
- const { runInitCommand } = await import("./init-CNuntiij.mjs");
39
- return runInitCommand({ yes }, { quiet: true });
43
+ /** Shared command setup: resolve cwd, optionally require agent.ts. */
44
+ async function setup(opts) {
45
+ const cwd = resolveCwd();
46
+ if (opts?.agent) {
47
+ if (!await fileExists(path.join(cwd, "agent.ts"))) throw new Error("No agent.ts found in the current directory. Run `aai init` first.");
40
48
  }
41
49
  return cwd;
42
50
  }
43
- /** Shared command setup: resolve cwd, optionally scaffold agent and check API key. */
44
- async function setup(args, opts) {
45
- let cwd = resolveCwd();
46
- if (opts?.agent) cwd = await ensureAgent(cwd, args?.yes);
47
- if (opts?.apiKey) await ensureApiKeyInEnv();
48
- return cwd;
49
- }
50
51
  /** Catch command errors and display a clean message instead of a raw stack trace. */
51
- async function handleErrors(fn) {
52
+ async function handleErrors(mode, fn) {
52
53
  try {
53
54
  await fn();
54
55
  } catch (err) {
55
- const { log } = await import("./_ui-DWGXImbO.mjs");
56
- log.error(err instanceof Error ? err.message : String(err));
56
+ const code = err instanceof CliError ? err.code : "command_failed";
57
+ const hint = err instanceof CliError ? err.hint : void 0;
58
+ if (mode === "json") {
59
+ const result = fail(code, errorMessage(err), hint);
60
+ process.stdout.write(`${JSON.stringify(result)}\n`);
61
+ process.exit(1);
62
+ }
63
+ const { log } = await import("./_ui-r2t6_2eP.mjs").then((n) => n.t);
64
+ log.error(errorMessage(err));
57
65
  process.exit(1);
58
66
  }
59
67
  }
68
+ /**
69
+ * Run a command body with standard error handling, output mode resolution, and withOutput wrapping.
70
+ * `setYes` controls whether json mode sets `args.yes = true` (default true for most commands).
71
+ */
72
+ async function runCommand(args, fn, opts = {}) {
73
+ const mode = getOutputMode(args);
74
+ if (mode === "json") {
75
+ silenceOutput();
76
+ if (opts.setYes !== false) args.yes = true;
77
+ }
78
+ const { withOutput } = await import("./_output-BKdAJaM5.mjs").then((n) => n.n);
79
+ await handleErrors(mode, () => withOutput(mode, () => fn(mode), () => {}));
80
+ }
60
81
  const init = defineCommand({
61
82
  meta: {
62
83
  name: "init",
@@ -68,18 +89,19 @@ const init = defineCommand({
68
89
  description: "Project directory",
69
90
  required: false
70
91
  },
71
- template: {
72
- type: "string",
73
- alias: "t",
74
- description: "Template to use"
75
- },
76
92
  force: {
77
93
  type: "boolean",
78
94
  alias: "f",
79
95
  description: "Overwrite existing files"
80
96
  },
97
+ template: {
98
+ type: "string",
99
+ alias: "t",
100
+ description: "Template to use (e.g. pizza-ordering)"
101
+ },
81
102
  server: sharedArgs.server,
82
103
  yes: sharedArgs.yes,
104
+ json: sharedArgs.json,
83
105
  skipApi: {
84
106
  type: "boolean",
85
107
  description: "Skip API key check"
@@ -90,17 +112,17 @@ const init = defineCommand({
90
112
  }
91
113
  },
92
114
  async run({ args }) {
93
- await handleErrors(async () => {
94
- const { runInitCommand } = await import("./init-CNuntiij.mjs");
95
- await runInitCommand({
115
+ await runCommand(args, async (mode) => {
116
+ const { executeInit } = await import("./init-DJQVk0Gw.mjs");
117
+ return executeInit({
96
118
  dir: args.dir,
97
- template: args.template,
98
119
  force: args.force,
120
+ template: args.template,
99
121
  yes: args.yes,
100
122
  skipApi: args.skipApi,
101
123
  skipDeploy: args.skipDeploy,
102
124
  server: args.server
103
- });
125
+ }, mode === "json" ? { silent: true } : void 0);
104
126
  });
105
127
  }
106
128
  });
@@ -112,16 +134,14 @@ const dev = defineCommand({
112
134
  args: {
113
135
  port: sharedArgs.port,
114
136
  server: sharedArgs.server,
115
- yes: sharedArgs.yes
137
+ yes: sharedArgs.yes,
138
+ json: sharedArgs.json
116
139
  },
117
140
  async run({ args }) {
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({
141
+ await runCommand(args, async () => {
142
+ const cwd = await setup({ agent: true });
143
+ const { executeDev } = await import("./dev-Wmola4F6.mjs");
144
+ return executeDev({
125
145
  cwd,
126
146
  port: args.port
127
147
  });
@@ -133,12 +153,13 @@ const test = defineCommand({
133
153
  name: "test",
134
154
  description: "Run agent tests"
135
155
  },
136
- async run() {
137
- await handleErrors(async () => {
156
+ args: { json: sharedArgs.json },
157
+ async run({ args }) {
158
+ await runCommand(args, async () => {
138
159
  const cwd = await setup();
139
- const { runTestCommand } = await import("./test-yqHGZPF3.mjs");
140
- await runTestCommand(cwd);
141
- });
160
+ const { executeTest } = await import("./test-DhQ4aznR.mjs");
161
+ return executeTest(cwd);
162
+ }, { setYes: false });
142
163
  }
143
164
  });
144
165
  const build = defineCommand({
@@ -149,20 +170,21 @@ const build = defineCommand({
149
170
  args: {
150
171
  server: sharedArgs.server,
151
172
  yes: sharedArgs.yes,
173
+ json: sharedArgs.json,
152
174
  skipTests: {
153
175
  type: "boolean",
154
176
  description: "Skip running tests before build"
155
177
  }
156
178
  },
157
179
  async run({ args }) {
158
- await handleErrors(async () => {
159
- const cwd = await setup(args, { agent: true });
180
+ await runCommand(args, async () => {
181
+ const cwd = await setup({ agent: true });
160
182
  if (!args.skipTests) {
161
- const { runVitest } = await import("./test-yqHGZPF3.mjs");
183
+ const { runVitest } = await import("./test-DhQ4aznR.mjs");
162
184
  runVitest(cwd);
163
185
  }
164
- const { runBuildCommand } = await import("./_bundler-DQtFVeww.mjs");
165
- await runBuildCommand(cwd);
186
+ const { executeBuild } = await import("./_bundler-DgRDzBzD.mjs");
187
+ return executeBuild(cwd);
166
188
  });
167
189
  }
168
190
  });
@@ -173,13 +195,14 @@ const deploy = defineCommand({
173
195
  },
174
196
  args: {
175
197
  server: sharedArgs.server,
176
- yes: sharedArgs.yes
198
+ yes: sharedArgs.yes,
199
+ json: sharedArgs.json
177
200
  },
178
201
  async run({ args }) {
179
- await handleErrors(async () => {
180
- const cwd = await setup(args, { agent: true });
181
- const { runDeployCommand } = await import("./deploy-CLs8gLHV.mjs");
182
- await runDeployCommand({
202
+ await runCommand(args, async () => {
203
+ const cwd = await setup({ agent: true });
204
+ const { executeDeploy } = await import("./deploy-CmY2AtML.mjs");
205
+ return executeDeploy({
183
206
  cwd,
184
207
  ...args.server ? { server: args.server } : {}
185
208
  });
@@ -191,16 +214,19 @@ const del = defineCommand({
191
214
  name: "delete",
192
215
  description: "Remove a deployed agent"
193
216
  },
194
- args: { server: sharedArgs.server },
217
+ args: {
218
+ server: sharedArgs.server,
219
+ json: sharedArgs.json
220
+ },
195
221
  async run({ args }) {
196
- await handleErrors(async () => {
222
+ await runCommand(args, async () => {
197
223
  const cwd = await setup();
198
- const { runDeleteCommand } = await import("./delete-CalIL6Pg.mjs");
199
- await runDeleteCommand({
224
+ const { executeDelete } = await import("./delete-CaiAsY1S.mjs");
225
+ return executeDelete({
200
226
  cwd,
201
227
  ...args.server ? { server: args.server } : {}
202
228
  });
203
- });
229
+ }, { setYes: false });
204
230
  }
205
231
  });
206
232
  const secret = defineCommand({
@@ -220,14 +246,21 @@ const secret = defineCommand({
220
246
  description: "Secret name",
221
247
  required: true
222
248
  },
223
- server: sharedArgs.server
249
+ server: sharedArgs.server,
250
+ json: sharedArgs.json
224
251
  },
225
252
  async run({ args }) {
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
- });
253
+ await runCommand(args, async (mode) => {
254
+ const cwd = await setup();
255
+ const { executeSecretPut, readStdin } = await import("./secret-lSaf6Uax.mjs");
256
+ const value = mode === "json" ? await readStdin() : void 0;
257
+ if (mode === "json" && !value) {
258
+ const result = fail("no_input", "No value provided", "Pipe secret value to stdin");
259
+ process.stdout.write(`${JSON.stringify(result)}\n`);
260
+ process.exit(1);
261
+ }
262
+ return executeSecretPut(cwd, args.name, value, args.server);
263
+ }, { setYes: false });
231
264
  }
232
265
  }),
233
266
  delete: defineCommand({
@@ -241,14 +274,15 @@ const secret = defineCommand({
241
274
  description: "Secret name",
242
275
  required: true
243
276
  },
244
- server: sharedArgs.server
277
+ server: sharedArgs.server,
278
+ json: sharedArgs.json
245
279
  },
246
280
  async run({ args }) {
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
- });
281
+ await runCommand(args, async () => {
282
+ const cwd = await setup();
283
+ const { executeSecretDelete } = await import("./secret-lSaf6Uax.mjs");
284
+ return executeSecretDelete(cwd, args.name, args.server);
285
+ }, { setYes: false });
252
286
  }
253
287
  }),
254
288
  list: defineCommand({
@@ -256,13 +290,16 @@ const secret = defineCommand({
256
290
  name: "list",
257
291
  description: "List all secrets"
258
292
  },
259
- args: { server: sharedArgs.server },
293
+ args: {
294
+ server: sharedArgs.server,
295
+ json: sharedArgs.json
296
+ },
260
297
  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
- });
298
+ await runCommand(args, async () => {
299
+ const cwd = await setup();
300
+ const { executeSecretList } = await import("./secret-lSaf6Uax.mjs");
301
+ return executeSecretList(cwd, args.server);
302
+ }, { setYes: false });
266
303
  }
267
304
  })
268
305
  }
@@ -291,8 +328,12 @@ if (process.env.VITEST !== "true") {
291
328
  "-h",
292
329
  "-V"
293
330
  ]);
294
- if (!sub || sub.startsWith("-") && !helpFlags.has(sub)) process.argv.splice(2, 0, "init");
295
- runMain(mainCommand);
331
+ if (!sub || sub.startsWith("-") && !helpFlags.has(sub)) {
332
+ const defaultCmd = existsSync(path.join(resolveCwd(), "agent.ts")) ? "deploy" : "init";
333
+ process.argv.splice(2, 0, defaultCmd);
334
+ }
335
+ const cmd = process.argv[2];
336
+ (helpFlags.has(cmd ?? "") || cmd === "test" || cmd === "build" ? Promise.resolve() : import("./_config-Dv-T6uRj.mjs").then((n) => n.t).then((m) => m.ensureApiKey())).then(() => runMain(mainCommand));
296
337
  }
297
338
  //#endregion
298
339
  export { mainCommand };
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env node
2
+ import { a as ok } from "./_output-BKdAJaM5.mjs";
3
+ import { r as log } from "./_ui-r2t6_2eP.mjs";
4
+ import { getServerInfo } from "./_agent-CzbSa09n.mjs";
5
+ import { t as apiRequestOrThrow } from "./_api-client-CFfjWfNa.mjs";
6
+ //#region _delete.ts
7
+ async function runDelete(opts) {
8
+ await apiRequestOrThrow(`${opts.url}/${opts.slug}`, {
9
+ method: "DELETE",
10
+ apiKey: opts.apiKey,
11
+ action: "delete"
12
+ }, {
13
+ hints: { 404: "The agent may not be deployed. Check `.aai/project.json` for the correct slug." },
14
+ fetch: opts.fetch
15
+ });
16
+ }
17
+ //#endregion
18
+ //#region delete.ts
19
+ /** Execute delete and return structured result. */
20
+ async function executeDelete(opts) {
21
+ const { cwd } = opts;
22
+ const { serverUrl, slug, apiKey } = await getServerInfo(cwd, opts.server);
23
+ log.step(`Deleting ${slug}`);
24
+ await runDelete({
25
+ url: serverUrl,
26
+ slug,
27
+ apiKey
28
+ });
29
+ log.success(`Deleted ${serverUrl}/${slug}`);
30
+ return ok({ slug });
31
+ }
32
+ //#endregion
33
+ export { executeDelete };
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+ import { a as ok } from "./_output-BKdAJaM5.mjs";
3
+ import { n as fmtUrl, r as log } from "./_ui-r2t6_2eP.mjs";
4
+ import { i as writeProjectConfig, n as ensureApiKey, r as readProjectConfig } from "./_config-Dv-T6uRj.mjs";
5
+ import { resolveServerUrl } from "./_agent-CzbSa09n.mjs";
6
+ import { buildAgentBundle } from "./_bundler-DgRDzBzD.mjs";
7
+ import { t as resolveServerEnv } from "./_server-common-Pdb-KUSK.mjs";
8
+ import { t as apiRequestOrThrow } from "./_api-client-CFfjWfNa.mjs";
9
+ //#region _deploy.ts
10
+ async function runDeploy(opts) {
11
+ const body = JSON.stringify({
12
+ ...opts.slug ? { slug: opts.slug } : {},
13
+ env: opts.env,
14
+ worker: opts.bundle.worker,
15
+ clientFiles: opts.bundle.clientFiles,
16
+ agentConfig: opts.bundle.agentConfig
17
+ });
18
+ return { slug: (await (await apiRequestOrThrow(`${opts.url}/deploy`, {
19
+ method: "POST",
20
+ body,
21
+ apiKey: opts.apiKey,
22
+ action: "deploy"
23
+ }, {
24
+ hints: { 413: "Your bundle is too large. Try reducing dependencies or splitting your agent." },
25
+ fetch: opts.fetch
26
+ })).json()).slug };
27
+ }
28
+ //#endregion
29
+ //#region deploy.ts
30
+ async function executeDeploy(opts) {
31
+ const { cwd } = opts;
32
+ const projectConfig = await readProjectConfig(cwd);
33
+ const serverUrl = resolveServerUrl(opts.server, projectConfig?.serverUrl);
34
+ const bundle = await buildAgentBundle(cwd);
35
+ const slug = projectConfig?.slug;
36
+ const apiKey = await ensureApiKey();
37
+ const env = await resolveServerEnv(cwd);
38
+ log.step(`Deploying${slug ? ` ${slug}` : ""}…`);
39
+ const deployed = await runDeploy({
40
+ url: serverUrl,
41
+ bundle,
42
+ env: {
43
+ ...env,
44
+ ASSEMBLYAI_API_KEY: apiKey
45
+ },
46
+ ...slug ? { slug } : {},
47
+ apiKey
48
+ });
49
+ await writeProjectConfig(cwd, {
50
+ slug: deployed.slug,
51
+ serverUrl
52
+ });
53
+ const agentUrl = `${serverUrl}/${deployed.slug}`;
54
+ log.success(`Deployed ${fmtUrl(agentUrl)}`);
55
+ return ok({
56
+ slug: deployed.slug,
57
+ url: agentUrl
58
+ });
59
+ }
60
+ //#endregion
61
+ export { executeDeploy };
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+ import { a as ok } from "./_output-BKdAJaM5.mjs";
3
+ import { i as parsePort, n as fmtUrl, r as log } from "./_ui-r2t6_2eP.mjs";
4
+ import path from "node:path";
5
+ import { colorize } from "consola/utils";
6
+ //#region dev.ts
7
+ /**
8
+ * Start the dev server and return the result.
9
+ * The process stays alive after this returns — caller handles signals.
10
+ */
11
+ async function executeDev(opts) {
12
+ const port = parsePort(opts.port);
13
+ const agentName = path.basename(path.resolve(opts.cwd));
14
+ const { startDevServer } = await import("./_dev-server-Cag4LlkI.mjs");
15
+ const cleanup = await startDevServer({
16
+ cwd: opts.cwd,
17
+ port
18
+ });
19
+ const url = `http://localhost:${port}`;
20
+ log.success(`${colorize("bold", agentName)} running at ${fmtUrl(url)}`);
21
+ log.info("Press Ctrl-C to stop");
22
+ const onSignal = () => {
23
+ cleanup().finally(() => process.exit(0));
24
+ };
25
+ process.on("SIGINT", onSignal);
26
+ process.on("SIGTERM", onSignal);
27
+ return ok({ url });
28
+ }
29
+ //#endregion
30
+ export { executeDev };
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env node
2
+ import { a as ok } from "./_output-BKdAJaM5.mjs";
3
+ import { r as log$1 } from "./_ui-r2t6_2eP.mjs";
4
+ import { n as resolveCwd, t as fileExists } from "./_utils-DZo3_J_v.mjs";
5
+ import { getMonorepoRoot, isDevMode } from "./_agent-CzbSa09n.mjs";
6
+ import path from "node:path";
7
+ import { errorMessage } from "@alexkroman1/aai";
8
+ import * as p from "@clack/prompts";
9
+ import { colorize } from "consola/utils";
10
+ import fs from "node:fs/promises";
11
+ import { execFile } from "node:child_process";
12
+ import { promisify } from "node:util";
13
+ //#region init.ts
14
+ const execFileAsync = promisify(execFile);
15
+ const DEFAULT_PROJECT_NAME = "my-voice-agent";
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
+ /** Enable corepack so pnpm is available (scaffold declares packageManager: pnpm). */
31
+ async function ensurePnpm() {
32
+ try {
33
+ await execFileAsync("corepack", ["enable"]);
34
+ } catch {}
35
+ }
36
+ /** Check if the project has any dependencies to install. */
37
+ async function hasDeps(cwd) {
38
+ if (await fileExists(path.join(cwd, "node_modules"))) return false;
39
+ let pkgJson;
40
+ try {
41
+ pkgJson = JSON.parse(await fs.readFile(path.join(cwd, "package.json"), "utf-8"));
42
+ } catch {
43
+ pkgJson = {};
44
+ }
45
+ const deps = Object.keys(pkgJson.dependencies ?? {});
46
+ const devDeps = Object.keys(pkgJson.devDependencies ?? {});
47
+ return deps.length > 0 || devDeps.length > 0;
48
+ }
49
+ /** Run pnpm install and warn on failure. */
50
+ async function runPnpmInstall(cwd) {
51
+ await execFileAsync("pnpm", isDevMode() ? ["install"] : ["install", "--ignore-workspace"], { cwd });
52
+ }
53
+ /** Install deps with pnpm (scaffold declares packageManager: pnpm). */
54
+ async function installDeps(cwd, silent) {
55
+ if (!await hasDeps(cwd)) return;
56
+ await ensurePnpm();
57
+ if (silent) {
58
+ try {
59
+ await runPnpmInstall(cwd);
60
+ } catch (err) {
61
+ log$1.warn(`pnpm install failed: ${errorMessage(err)}`);
62
+ log$1.warn("Run `corepack enable && pnpm install` manually in the project directory.");
63
+ }
64
+ return;
65
+ }
66
+ const s = p.spinner();
67
+ s.start("Installing dependencies with pnpm");
68
+ try {
69
+ await runPnpmInstall(cwd);
70
+ s.stop("Dependencies installed");
71
+ } catch (err) {
72
+ s.stop("Dependency install failed");
73
+ log$1.warn(`pnpm install failed: ${errorMessage(err)}`);
74
+ log$1.warn("Run `corepack enable && pnpm install` manually in the project directory.");
75
+ }
76
+ }
77
+ /** Resolve target directory relative to the user's current directory. */
78
+ function resolveTargetDir(dir) {
79
+ return path.resolve(resolveCwd(), dir);
80
+ }
81
+ /** Resolve the deploy server — in dev mode, default to localhost. */
82
+ function resolveDeployServer(explicit, monorepoRoot) {
83
+ return explicit ?? (monorepoRoot ? "http://localhost:8080" : void 0);
84
+ }
85
+ /** Run deploy after init and return deploy metadata if successful. */
86
+ async function tryDeploy(cwd, server, monorepoRoot) {
87
+ const resolvedServer = resolveDeployServer(server, monorepoRoot);
88
+ const { executeDeploy } = await import("./deploy-CmY2AtML.mjs");
89
+ const result = await executeDeploy({
90
+ cwd,
91
+ ...resolvedServer ? { server: resolvedServer } : {}
92
+ });
93
+ return result.ok ? {
94
+ slug: result.data.slug,
95
+ url: result.data.url
96
+ } : null;
97
+ }
98
+ /** Scaffold the project, optionally showing a spinner. */
99
+ async function scaffoldProject(dir, cwd, template, silent) {
100
+ const { runInit } = await import("./_init-DhX1a2Zb.mjs");
101
+ if (silent) {
102
+ await runInit({
103
+ targetDir: cwd,
104
+ template
105
+ });
106
+ return;
107
+ }
108
+ const s = p.spinner();
109
+ s.start(`Creating ${dir}`);
110
+ await runInit({
111
+ targetDir: cwd,
112
+ template
113
+ });
114
+ s.stop("Project created");
115
+ }
116
+ /** Print post-init instructions. */
117
+ function printPostInitInfo(cwd, monorepoRoot) {
118
+ log$1.success(`Created ${cwd}`);
119
+ if (monorepoRoot) log$1.info("Dev mode: project linked to workspace packages");
120
+ log$1.info(`Next: cd ${cwd} && aai dev`);
121
+ }
122
+ async function executeInit(opts, extra) {
123
+ const suppressUi = extra?.quiet ?? extra?.silent;
124
+ if (!suppressUi) p.intro(colorize("cyanBright", "Create a new voice agent"));
125
+ const dir = opts.dir ?? await promptProjectName(opts.yes);
126
+ const monorepoRoot = getMonorepoRoot();
127
+ const cwd = resolveTargetDir(dir);
128
+ 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.`);
129
+ const template = opts.template ?? "simple";
130
+ await scaffoldProject(dir, cwd, template, suppressUi);
131
+ await installDeps(cwd, suppressUi);
132
+ let deployed = false;
133
+ let slug;
134
+ let url;
135
+ if (!(opts.skipDeploy || extra?.quiet)) {
136
+ const deployInfo = await tryDeploy(cwd, opts.server, monorepoRoot);
137
+ if (deployInfo) {
138
+ deployed = true;
139
+ slug = deployInfo.slug;
140
+ url = deployInfo.url;
141
+ }
142
+ }
143
+ if (!suppressUi) printPostInitInfo(cwd, monorepoRoot);
144
+ const data = {
145
+ dir: cwd,
146
+ template,
147
+ deployed
148
+ };
149
+ if (slug) data.slug = slug;
150
+ if (url) data.url = url;
151
+ return ok(data);
152
+ }
153
+ //#endregion
154
+ export { executeInit };