@alexkroman1/aai-cli 1.9.2 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+ import path from "node:path";
3
+ import fs from "node:fs/promises";
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
+ /**
10
+ * Extract a message from an unknown error. Local copy of `errorMessage` from
11
+ * `@alexkroman1/aai` — importing the root barrel pulls zod into every CLI
12
+ * invocation (including `aai --help`), so the one-liner lives here instead.
13
+ */
14
+ function errorMessage(err) {
15
+ return err instanceof Error ? err.message : String(err);
16
+ }
17
+ /**
18
+ * Extract a stack (falling back to the message) from an unknown error. Local
19
+ * copy of `errorDetail` from `@alexkroman1/aai` for the same reason as
20
+ * {@link errorMessage} above.
21
+ */
22
+ function errorDetail(err) {
23
+ return err instanceof Error ? err.stack ?? err.message : String(err);
24
+ }
25
+ /** The `code` of a Node errno-style error (`"ENOENT"`, `"EPIPE"`, …), or undefined. */
26
+ function errorCode(err) {
27
+ return err instanceof Error && "code" in err && typeof err.code === "string" ? err.code : void 0;
28
+ }
29
+ /** True when `err` is a filesystem EEXIST error (target already exists). */
30
+ function isEexist(err) {
31
+ return errorCode(err) === "EEXIST";
32
+ }
33
+ /** Validate that a module's default export is a valid agent definition. Throws if invalid. */
34
+ function validateAgentExport(mod) {
35
+ if (!mod?.name || typeof mod.name !== "string") throw new Error("agent.ts must export default agent({ name: ... })");
36
+ }
37
+ async function fileExists(p) {
38
+ try {
39
+ await fs.access(p);
40
+ return true;
41
+ } catch {
42
+ return false;
43
+ }
44
+ }
45
+ /**
46
+ * Read and parse a JSON file. Returns null only when the file does not exist
47
+ * (ENOENT — the "optional file" case). A file that exists but cannot be read
48
+ * (EACCES, …) or parsed throws instead: treating a corrupted file as absent
49
+ * hides real problems — e.g. a corrupted `.aai/project.json` would silently
50
+ * deploy under a NEW slug, orphaning the live deployment.
51
+ */
52
+ async function readJson(filePath) {
53
+ let raw;
54
+ try {
55
+ raw = await fs.readFile(filePath, "utf-8");
56
+ } catch (err) {
57
+ if (errorCode(err) === "ENOENT") return null;
58
+ throw err;
59
+ }
60
+ try {
61
+ return JSON.parse(raw);
62
+ } catch (err) {
63
+ throw new Error(`Invalid JSON in ${filePath}: ${errorMessage(err)}`, { cause: err });
64
+ }
65
+ }
66
+ /**
67
+ * Write `data` as pretty-printed JSON (+ trailing newline), creating parent dirs.
68
+ *
69
+ * Writes to a temp file in the same directory, then renames it into place.
70
+ * A plain `writeFile` can be observed (or left, on crash) half-written; a
71
+ * torn config.json fails `JSON.parse`, reads back as `{}`, and the next
72
+ * read-modify-write silently wipes fields like `approvedServers`. Rename on
73
+ * the same filesystem is atomic, so readers only ever see a complete file.
74
+ * Two concurrent CLI processes can still lose each other's *updates*
75
+ * (last rename wins) — acceptable for these small user-config files.
76
+ *
77
+ * `mode` restricts the file's permissions (the rename carries the temp
78
+ * file's mode to the destination, so an existing world-readable file is
79
+ * tightened on the next write). The parent directory is created 0o700 in
80
+ * that case so a fresh config dir never goes through a readable window.
81
+ */
82
+ async function writeJson(filePath, data, opts = {}) {
83
+ await fs.mkdir(path.dirname(filePath), {
84
+ recursive: true,
85
+ ...opts.mode !== void 0 ? { mode: 448 } : {}
86
+ });
87
+ const tmpPath = `${filePath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
88
+ await fs.writeFile(tmpPath, `${JSON.stringify(data, null, 2)}\n`, { ...opts.mode !== void 0 ? { mode: opts.mode } : {} });
89
+ try {
90
+ await fs.rename(tmpPath, filePath);
91
+ } catch (err) {
92
+ await fs.rm(tmpPath, { force: true }).catch(() => void 0);
93
+ throw err;
94
+ }
95
+ }
96
+ //#endregion
97
+ export { isEexist as a, validateAgentExport as c, fileExists as i, writeJson as l, errorDetail as n, readJson as o, errorMessage as r, resolveCwd as s, errorCode as t };
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+ //#region _vite-env.ts
3
+ /**
4
+ * Run a Vite build without letting it mutate the calling process's env.
5
+ *
6
+ * Vite's `build()` sets `process.env.NODE_ENV = "production"` when NODE_ENV is
7
+ * unset — a global, permanent side effect on whatever process invoked it. That
8
+ * is fine for a one-shot `aai build`, but both long-lived callers are broken by
9
+ * it:
10
+ *
11
+ * - `aai dev` rebuilds on every file change, so the first rebuild would flip
12
+ * the dev server into production mode.
13
+ * - The platform studio builds inside the server process, where flipping
14
+ * NODE_ENV makes the sandbox demand gVisor ("gVisor (runsc) is required in
15
+ * production but not found on PATH") and refuse every subsequent deploy on a
16
+ * dev machine.
17
+ *
18
+ * Snapshot and restore rather than pinning a value: callers that legitimately
19
+ * run with NODE_ENV=production must keep it.
20
+ *
21
+ * The snapshot is refcounted, not per-call: both bundle paths run the worker
22
+ * and client builds concurrently (`Promise.all`), and independent snapshots
23
+ * interleave — the second entrant would snapshot the "production" the first
24
+ * build's Vite just set, and "restore" it after the first exiter deleted it,
25
+ * flipping the process permanently anyway. So the first entrant snapshots,
26
+ * later entrants just join, and only the last exiter restores. This keeps the
27
+ * builds parallel (a mutex serializing them would cost real deploy time).
28
+ */
29
+ let activeBuilds = 0;
30
+ let savedNodeEnv;
31
+ /**
32
+ * `env` is injectable for tests ONLY, so specs can exercise the
33
+ * snapshot/refcount logic on a plain object instead of mutating (and
34
+ * repeatedly deleting) the real `process.env.NODE_ENV` mid-suite.
35
+ */
36
+ async function withPreservedNodeEnv(fn, env = process.env) {
37
+ if (activeBuilds === 0) savedNodeEnv = env.NODE_ENV;
38
+ activeBuilds++;
39
+ try {
40
+ return await fn();
41
+ } finally {
42
+ activeBuilds--;
43
+ if (activeBuilds === 0) if (savedNodeEnv === void 0) delete env.NODE_ENV;
44
+ else env.NODE_ENV = savedNodeEnv;
45
+ }
46
+ }
47
+ //#endregion
48
+ export { withPreservedNodeEnv as t };
package/dist/cli.mjs CHANGED
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { a as withOutput, n as fail, o as writeLine, r as getOutputMode, t as CliError } from "./_output-fy2bXRNb.mjs";
3
- import { i as silenceOutput, n as log } from "./_ui-YqQ6Fi8K.mjs";
4
- import { n as errorMessage, o as resolveCwd, r as fileExists } from "./_utils-BeU10C7O.mjs";
2
+ import { c as getOutputMode, d as writeLine, i as silenceOutput, l as installStdoutGuard, n as log, o as CliError, s as fail } from "./_ui-CKEIHAtB.mjs";
3
+ import { i as fileExists, r as errorMessage, s as resolveCwd } from "./_utils-ECl2je7-.mjs";
5
4
  import { existsSync, readFileSync } from "node:fs";
6
5
  import path from "node:path";
7
6
  import { fileURLToPath } from "node:url";
@@ -31,14 +30,20 @@ const sharedArgs = {
31
30
  }
32
31
  };
33
32
  const cliDir = path.dirname(fileURLToPath(import.meta.url));
34
- function findPkgJson(dir) {
35
- try {
36
- return readFileSync(path.join(dir, "package.json"), "utf-8");
37
- } catch {
38
- return readFileSync(path.join(dir, "..", "package.json"), "utf-8");
39
- }
33
+ /**
34
+ * Read this CLI's own version from its package.json (source layout keeps it
35
+ * next to cli.ts; dist layout one level up). A missing or corrupt file must
36
+ * not brick every command over a cosmetic string — warn and fall back.
37
+ */
38
+ function readCliVersion(dir) {
39
+ for (const candidate of [path.join(dir, "package.json"), path.join(dir, "..", "package.json")]) try {
40
+ const parsed = JSON.parse(readFileSync(candidate, "utf-8"));
41
+ if (typeof parsed.version === "string") return parsed.version;
42
+ } catch {}
43
+ process.stderr.write("warning: could not read aai's package.json — reporting version unknown\n");
44
+ return "unknown";
40
45
  }
41
- const VERSION = JSON.parse(findPkgJson(cliDir)).version;
46
+ const VERSION = readCliVersion(cliDir);
42
47
  /** Shared command setup: resolve cwd, optionally require agent.ts. */
43
48
  async function setup(opts) {
44
49
  const cwd = resolveCwd();
@@ -47,28 +52,19 @@ async function setup(opts) {
47
52
  }
48
53
  return cwd;
49
54
  }
50
- /** Catch command errors and display a clean message instead of a raw stack trace. */
51
- async function handleErrors(mode, fn) {
52
- try {
53
- await fn();
54
- } catch (err) {
55
- const code = err instanceof CliError ? err.code : "command_failed";
56
- const hint = err instanceof CliError ? err.hint : void 0;
57
- if (mode === "json") {
58
- const result = fail(code, errorMessage(err), hint);
59
- await writeLine(`${JSON.stringify(result)}\n`);
60
- process.exit(1);
61
- }
62
- log.error(errorMessage(err));
63
- process.exit(1);
64
- }
65
- }
66
55
  /**
67
- * Run a command body with standard error handling, output mode resolution, and withOutput wrapping.
56
+ * Run a command body with standard output-mode resolution, error handling,
57
+ * and result emission.
68
58
  *
69
59
  * - `setYes`: json mode sets `args.yes = true` (only meaningful for commands with a `yes` arg).
70
- * - `apiKey`: prompt for / resolve the AssemblyAI API key before the command body runs
71
- * (default true — pass false for commands that never talk to the platform).
60
+ * - API key acquisition is owned by `resolveDeployTarget`/`getServerInfo`
61
+ * inside the commands that talk to the platform — after the server-trust
62
+ * check, so an untrusted `serverUrl` is refused without prompting for a
63
+ * key, and commands with no platform traffic never prompt at all.
64
+ *
65
+ * A thrown error and a returned `fail(...)` converge here on one emitter:
66
+ * human mode logs the message, JSON mode writes exactly one result line,
67
+ * and both exit 1.
72
68
  */
73
69
  async function runCommand(args, fn, opts = {}) {
74
70
  const mode = getOutputMode(args);
@@ -76,13 +72,17 @@ async function runCommand(args, fn, opts = {}) {
76
72
  silenceOutput();
77
73
  if (opts.setYes) args.yes = true;
78
74
  }
79
- await handleErrors(mode, () => withOutput(mode, async () => {
80
- if (opts.apiKey !== false) {
81
- const { ensureApiKey } = await import("./_config-zV70t71V.mjs").then((n) => n.t);
82
- await ensureApiKey();
83
- }
84
- return fn(mode);
85
- }));
75
+ let result;
76
+ try {
77
+ result = await fn(mode);
78
+ } catch (err) {
79
+ const code = err instanceof CliError ? err.code : "command_failed";
80
+ const hint = err instanceof CliError ? err.hint : void 0;
81
+ if (mode === "human") log.error(errorMessage(err));
82
+ result = fail(code, errorMessage(err), hint);
83
+ }
84
+ if (mode === "json") await writeLine(`${JSON.stringify(result)}\n`);
85
+ if (!result.ok) process.exit(1);
86
86
  }
87
87
  const init = defineCommand({
88
88
  meta: {
@@ -110,7 +110,7 @@ const init = defineCommand({
110
110
  json: sharedArgs.json,
111
111
  skipApi: {
112
112
  type: "boolean",
113
- description: "Skip API key check"
113
+ description: "Deprecated no-op: the API key is now only requested when deploying"
114
114
  },
115
115
  skipDeploy: {
116
116
  type: "boolean",
@@ -119,7 +119,7 @@ const init = defineCommand({
119
119
  },
120
120
  async run({ args }) {
121
121
  await runCommand(args, async (mode) => {
122
- const { executeInit } = await import("./init-BDhs6-jB.mjs");
122
+ const { executeInit } = await import("./init-CFwZf8q1.mjs");
123
123
  return executeInit({
124
124
  dir: args.dir,
125
125
  force: args.force,
@@ -128,10 +128,7 @@ const init = defineCommand({
128
128
  skipDeploy: args.skipDeploy,
129
129
  server: args.server
130
130
  }, mode === "json" ? { silent: true } : void 0);
131
- }, {
132
- setYes: true,
133
- apiKey: !args.skipApi
134
- });
131
+ }, { setYes: true });
135
132
  }
136
133
  });
137
134
  const dev = defineCommand({
@@ -146,7 +143,7 @@ const dev = defineCommand({
146
143
  async run({ args }) {
147
144
  await runCommand(args, async () => {
148
145
  const cwd = await setup({ agent: true });
149
- const { executeDev } = await import("./dev-DGzDKzGZ.mjs");
146
+ const { executeDev } = await import("./dev-CtE0d-Gs.mjs");
150
147
  return executeDev({
151
148
  cwd,
152
149
  port: args.port
@@ -163,9 +160,9 @@ const test = defineCommand({
163
160
  async run({ args }) {
164
161
  await runCommand(args, async () => {
165
162
  const cwd = await setup();
166
- const { executeTest } = await import("./test-C6jwCnFu.mjs");
163
+ const { executeTest } = await import("./test-DXPY9Bqq.mjs");
167
164
  return executeTest(cwd);
168
- }, { apiKey: false });
165
+ });
169
166
  }
170
167
  });
171
168
  const build = defineCommand({
@@ -184,12 +181,20 @@ const build = defineCommand({
184
181
  await runCommand(args, async () => {
185
182
  const cwd = await setup({ agent: true });
186
183
  if (!args.skipTests) {
187
- const { runVitest } = await import("./test-C6jwCnFu.mjs");
188
- runVitest(cwd);
184
+ const { classifyVitestError, runVitest } = await import("./test-DXPY9Bqq.mjs");
185
+ const toCliError = (err) => {
186
+ const { code, message } = classifyVitestError(err);
187
+ return new CliError(code, message, "Re-run with --skipTests to build without tests", { cause: err });
188
+ };
189
+ try {
190
+ runVitest(cwd);
191
+ } catch (err) {
192
+ throw toCliError(err);
193
+ }
189
194
  }
190
- const { executeBuild } = await import("./_bundler-CpLVjRzc.mjs");
195
+ const { executeBuild } = await import("./_bundler-CKz9Q15i.mjs");
191
196
  return executeBuild(cwd);
192
- }, { apiKey: false });
197
+ });
193
198
  }
194
199
  });
195
200
  const deploy = defineCommand({
@@ -204,7 +209,7 @@ const deploy = defineCommand({
204
209
  async run({ args }) {
205
210
  await runCommand(args, async () => {
206
211
  const cwd = await setup({ agent: true });
207
- const { executeDeploy } = await import("./deploy-DOy6wOCd.mjs");
212
+ const { executeDeploy } = await import("./deploy-Ds1spTPC.mjs");
208
213
  return executeDeploy({
209
214
  cwd,
210
215
  ...args.server ? { server: args.server } : {}
@@ -224,7 +229,7 @@ const del = defineCommand({
224
229
  async run({ args }) {
225
230
  await runCommand(args, async () => {
226
231
  const cwd = await setup();
227
- const { executeDelete } = await import("./delete-DE35s3d5.mjs");
232
+ const { executeDelete } = await import("./delete-xZ8PzM_4.mjs");
228
233
  return executeDelete({
229
234
  cwd,
230
235
  ...args.server ? { server: args.server } : {}
@@ -255,7 +260,7 @@ const secret = defineCommand({
255
260
  async run({ args }) {
256
261
  await runCommand(args, async (mode) => {
257
262
  const cwd = await setup();
258
- const { executeSecretPut, readStdin } = await import("./secret-D-90QtDQ.mjs");
263
+ const { executeSecretPut, readStdin } = await import("./secret-B4LRmUhF.mjs");
259
264
  const value = mode === "json" ? await readStdin() : void 0;
260
265
  if (mode === "json" && !value) throw new CliError("no_input", "No value provided", "Pipe secret value to stdin");
261
266
  return executeSecretPut(cwd, args.name, value, args.server);
@@ -279,7 +284,7 @@ const secret = defineCommand({
279
284
  async run({ args }) {
280
285
  await runCommand(args, async () => {
281
286
  const cwd = await setup();
282
- const { executeSecretDelete } = await import("./secret-D-90QtDQ.mjs");
287
+ const { executeSecretDelete } = await import("./secret-B4LRmUhF.mjs");
283
288
  return executeSecretDelete(cwd, args.name, args.server);
284
289
  });
285
290
  }
@@ -296,7 +301,7 @@ const secret = defineCommand({
296
301
  async run({ args }) {
297
302
  await runCommand(args, async () => {
298
303
  const cwd = await setup();
299
- const { executeSecretList } = await import("./secret-D-90QtDQ.mjs");
304
+ const { executeSecretList } = await import("./secret-B4LRmUhF.mjs");
300
305
  return executeSecretList(cwd, args.server);
301
306
  });
302
307
  }
@@ -320,18 +325,22 @@ const mainCommand = defineCommand({
320
325
  }
321
326
  });
322
327
  if (process.env.VITEST !== "true") {
323
- const sub = process.argv[2];
324
- const helpFlags = /* @__PURE__ */ new Set([
325
- "--help",
326
- "--version",
327
- "-h",
328
- "-V"
329
- ]);
330
- if (!sub || sub.startsWith("-") && !helpFlags.has(sub)) {
331
- const defaultCmd = existsSync(path.join(resolveCwd(), "agent.ts")) ? "deploy" : "init";
332
- process.argv.splice(2, 0, defaultCmd);
333
- }
334
- runMain(mainCommand).catch((err) => {
328
+ installStdoutGuard();
329
+ const runDefault = async () => {
330
+ if (process.argv.length > 2) return;
331
+ if (!existsSync(path.join(resolveCwd(), "agent.ts"))) {
332
+ process.argv.splice(2, 0, "init");
333
+ return;
334
+ }
335
+ if (process.stdin.isTTY && process.stdout.isTTY) {
336
+ if (await (await import("@clack/prompts")).confirm({ message: "Deploy this agent to production?" }) !== true) {
337
+ log.info("Cancelled. Run `aai --help` to see all commands.");
338
+ process.exit(0);
339
+ }
340
+ }
341
+ process.argv.splice(2, 0, "deploy");
342
+ };
343
+ runDefault().then(() => runMain(mainCommand)).catch((err) => {
335
344
  log.error(errorMessage(err));
336
345
  process.exitCode = 1;
337
346
  });
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { r as fileExists } from "./_utils-BeU10C7O.mjs";
3
- import { t as withPreservedNodeEnv } from "./_vite-env-CmXG4ZAu.mjs";
2
+ import { i as fileExists, r as errorMessage } from "./_utils-ECl2je7-.mjs";
3
+ import { t as withPreservedNodeEnv } from "./_vite-env-DNb9R8xq.mjs";
4
4
  import { existsSync, unlinkSync, writeFileSync } from "node:fs";
5
5
  import path from "node:path";
6
6
  import fs from "node:fs/promises";
@@ -41,7 +41,7 @@ function fallbackHtmlPlugin(root) {
41
41
  server.transformIndexHtml("/", DEFAULT_HTML, req.originalUrl).then((html) => {
42
42
  res.setHeader("Content-Type", "text/html");
43
43
  res.end(html);
44
- }, next);
44
+ }).catch(next);
45
45
  return;
46
46
  }
47
47
  next();
@@ -56,29 +56,21 @@ function fallbackHtmlPlugin(root) {
56
56
  function writeTempHtml(root) {
57
57
  const htmlPath = path.join(root, "index.html");
58
58
  if (existsSync(htmlPath)) return () => {};
59
- writeFileSync(htmlPath, DEFAULT_HTML);
60
- return () => {
59
+ const cleanup = () => {
61
60
  try {
62
61
  unlinkSync(htmlPath);
63
62
  } catch {}
64
63
  };
64
+ try {
65
+ writeFileSync(htmlPath, DEFAULT_HTML);
66
+ } catch (err) {
67
+ cleanup();
68
+ throw new Error(`Failed to write a temporary index.html at ${htmlPath} for the client build — is the project directory writable? (${errorMessage(err)})`, { cause: err });
69
+ }
70
+ return cleanup;
65
71
  }
66
72
  //#endregion
67
73
  //#region client-bundler.ts
68
- /**
69
- * Client SPA bundling — the one implementation of "turn a `client.tsx` into
70
- * deployable `clientFiles`".
71
- *
72
- * Public (no `_` prefix) because the platform's browser studio reuses it: a
73
- * studio workspace is materialized to a directory and built through this same
74
- * function, so a UI published from the browser is byte-identical to one
75
- * deployed with `aai deploy`. Keep it that way — a second client bundler is
76
- * how the two paths would silently drift.
77
- *
78
- * The studio differs from a CLI project in two ways, hence the options:
79
- * it has no `vite.config.ts` to supply React/Tailwind plugins, and its files
80
- * are untrusted so any config the workspace *does* contain must be ignored.
81
- */
82
74
  const DEFAULT_OUT_DIR = ".aai/client";
83
75
  /**
84
76
  * Packages resolved from the build root rather than from whichever
@@ -109,8 +101,9 @@ async function buildClient(cwd, opts = {}) {
109
101
  if (!await fileExists(path.join(cwd, "client.tsx"))) return {};
110
102
  const outDir = opts.outDir ?? DEFAULT_OUT_DIR;
111
103
  const clientDir = path.join(cwd, outDir);
112
- const cleanupHtml = writeTempHtml(cwd);
104
+ let cleanupHtml = () => {};
113
105
  try {
106
+ cleanupHtml = writeTempHtml(cwd);
114
107
  await withPreservedNodeEnv(() => build({
115
108
  root: cwd,
116
109
  base: "./",
@@ -131,10 +124,15 @@ async function buildClient(cwd, opts = {}) {
131
124
  /** Read a built client directory into an in-memory deploy payload. */
132
125
  async function readClientDir(clientDir) {
133
126
  const files = {};
134
- const entries = await fs.readdir(clientDir, {
135
- recursive: true,
136
- withFileTypes: true
137
- });
127
+ let entries;
128
+ try {
129
+ entries = await fs.readdir(clientDir, {
130
+ recursive: true,
131
+ withFileTypes: true
132
+ });
133
+ } catch (err) {
134
+ throw new Error(`Client build produced no output at ${clientDir}: ${errorMessage(err)}`, { cause: err });
135
+ }
138
136
  await Promise.all(entries.filter((entry) => entry.isFile()).map(async (entry) => {
139
137
  const abs = path.join(entry.parentPath, entry.name);
140
138
  const rel = path.relative(clientDir, abs).split(path.sep).join("/");
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import { t as buildClient } from "./client-bundler-Bki_Rned.mjs";
2
+ import { t as buildClient } from "./client-bundler-VdcgFcos.mjs";
3
3
  export { buildClient };
@@ -1,9 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { i as ok } from "./_output-fy2bXRNb.mjs";
3
- import { n as log } from "./_ui-YqQ6Fi8K.mjs";
4
- import { r as getServerInfo } from "./_agent-cHGzbDVG.mjs";
5
- import { t as apiRequest } from "./_api-client-yJD7xGfd.mjs";
6
- //#region _delete.ts
2
+ import { n as log, u as ok } from "./_ui-CKEIHAtB.mjs";
3
+ import { n as getServerInfo } from "./_agent-Ba5Ykp05.mjs";
4
+ import { t as apiRequest } from "./_api-client-a6cPMebU.mjs";
5
+ //#region delete.ts
7
6
  async function runDelete(opts) {
8
7
  await apiRequest(`${opts.url}/${opts.slug}`, {
9
8
  method: "DELETE",
@@ -13,8 +12,6 @@ async function runDelete(opts) {
13
12
  ...opts.fetch ? { fetch: opts.fetch } : {}
14
13
  });
15
14
  }
16
- //#endregion
17
- //#region delete.ts
18
15
  /** Execute delete and return structured result. */
19
16
  async function executeDelete(opts) {
20
17
  const { cwd } = opts;
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import { s as writeProjectConfig } from "./_config-zV70t71V.mjs";
3
- import { i as ok } from "./_output-fy2bXRNb.mjs";
4
- import { n as log, t as fmtUrl } from "./_ui-YqQ6Fi8K.mjs";
5
- import { a as resolveDeployTarget } from "./_agent-cHGzbDVG.mjs";
6
- import { buildAgentBundle } from "./_bundler-CpLVjRzc.mjs";
7
- import { t as resolveServerEnv } from "./_server-common-QpOTRZgi.mjs";
8
- import { t as apiRequest } from "./_api-client-yJD7xGfd.mjs";
2
+ import { n as log, t as fmtUrl, u as ok } from "./_ui-CKEIHAtB.mjs";
3
+ import { r as errorMessage } from "./_utils-ECl2je7-.mjs";
4
+ import { buildAgentBundle } from "./_bundler-CKz9Q15i.mjs";
5
+ import { o as writeProjectConfig } from "./_config-BMjZt5hG.mjs";
6
+ import { t as resolveServerEnv } from "./_server-common-Bv-4Rskq.mjs";
7
+ import { i as resolveDeployTarget } from "./_agent-Ba5Ykp05.mjs";
8
+ import { t as apiRequest } from "./_api-client-a6cPMebU.mjs";
9
9
  import { gzipSync } from "node:zlib";
10
10
  //#region _deploy.ts
11
11
  async function runDeploy(opts) {
@@ -26,6 +26,8 @@ async function runDeploy(opts) {
26
26
  apiKey: opts.apiKey,
27
27
  action: "deploy",
28
28
  hints: { 413: "Your bundle is too large. Try reducing dependencies or splitting your agent." },
29
+ ...opts.slug ? {} : { retry: 0 },
30
+ ...opts.retryDelay !== void 0 ? { retryDelay: opts.retryDelay } : {},
29
31
  ...opts.fetch ? { fetch: opts.fetch } : {}
30
32
  })).slug };
31
33
  }
@@ -42,17 +44,25 @@ async function executeDeploy(opts) {
42
44
  url: serverUrl,
43
45
  bundle,
44
46
  env: {
45
- ...env,
46
- ASSEMBLYAI_API_KEY: apiKey
47
+ ASSEMBLYAI_API_KEY: apiKey,
48
+ ...env
47
49
  },
48
50
  ...slug ? { slug } : {},
49
51
  apiKey
50
52
  });
51
- await writeProjectConfig(cwd, {
52
- slug: deployed.slug,
53
- serverUrl
54
- });
55
53
  const agentUrl = `${serverUrl}/${deployed.slug}`;
54
+ try {
55
+ await writeProjectConfig(cwd, {
56
+ slug: deployed.slug,
57
+ serverUrl
58
+ });
59
+ } catch (err) {
60
+ log.warn(`Deployed as ${deployed.slug}, but couldn't save .aai/project.json: ${errorMessage(err)}\n Write it manually so future deploys reuse this slug:
61
+ ${JSON.stringify({
62
+ slug: deployed.slug,
63
+ serverUrl
64
+ })}`);
65
+ }
56
66
  log.success(`Deployed ${fmtUrl(agentUrl)}`);
57
67
  return ok({
58
68
  slug: deployed.slug,
@@ -1,9 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { i as ok } from "./_output-fy2bXRNb.mjs";
3
- import { n as log, r as parsePort, t as fmtUrl } from "./_ui-YqQ6Fi8K.mjs";
4
- import { t as errorDetail } from "./_utils-BeU10C7O.mjs";
2
+ import { n as log, r as parsePort, t as fmtUrl, u as ok } from "./_ui-CKEIHAtB.mjs";
3
+ import { n as errorDetail } from "./_utils-ECl2je7-.mjs";
5
4
  import path from "node:path";
6
- import { colorize } from "consola/utils";
5
+ import pc from "picocolors";
7
6
  //#region dev.ts
8
7
  /**
9
8
  * Start the dev server and return the result.
@@ -12,19 +11,27 @@ import { colorize } from "consola/utils";
12
11
  async function executeDev(opts) {
13
12
  const port = parsePort(opts.port);
14
13
  const agentName = path.basename(path.resolve(opts.cwd));
15
- const { startDevServer } = await import("./_dev-server-8pf2tqvC.mjs");
16
- const cleanup = await startDevServer({
14
+ const { startDevServer } = await import("./_dev-server-DZl-xNwH.mjs");
15
+ let cleanup;
16
+ let shuttingDown = false;
17
+ const onSignal = () => {
18
+ if (shuttingDown) return;
19
+ shuttingDown = true;
20
+ if (!cleanup) process.exit(130);
21
+ cleanup().then(() => process.exit(0), (err) => {
22
+ log.error(`Shutdown failed: ${errorDetail(err)}`);
23
+ process.exit(1);
24
+ });
25
+ };
26
+ process.on("SIGINT", onSignal);
27
+ process.on("SIGTERM", onSignal);
28
+ cleanup = await startDevServer({
17
29
  cwd: opts.cwd,
18
30
  port
19
31
  });
20
32
  const url = `http://localhost:${port}`;
21
- log.success(`${colorize("bold", agentName)} running at ${fmtUrl(url)}`);
33
+ log.success(`${pc.bold(agentName)} running at ${fmtUrl(url)}`);
22
34
  log.info("Press Ctrl-C to stop");
23
- const onSignal = () => {
24
- cleanup().finally(() => process.exit(0));
25
- };
26
- process.on("SIGINT", onSignal);
27
- process.on("SIGTERM", onSignal);
28
35
  process.on("unhandledRejection", (err) => {
29
36
  log.error(`Unhandled rejection: ${errorDetail(err)}`);
30
37
  });