@alexkroman1/aai-cli 5.5.1 → 5.6.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.
Files changed (35) hide show
  1. package/README.md +16 -7
  2. package/dist/{_agent-DMyOab9_.mjs → _agent-BB1ZaZsX.mjs} +3 -3
  3. package/dist/_agent.d.ts +3 -1
  4. package/dist/{_api-client-MenP4-O7.mjs → _api-client-B6_uvuLs.mjs} +1 -0
  5. package/dist/_api-client.d.ts +6 -0
  6. package/dist/_cli-common.d.ts +38 -0
  7. package/dist/{_config-5AEqhh-O.mjs → _config-PIC0nzRu.mjs} +29 -3
  8. package/dist/_config.d.ts +9 -1
  9. package/dist/_deploy.d.ts +7 -0
  10. package/dist/{_dev-server-vV05Fnki.mjs → _dev-server-CGptLIAm.mjs} +2 -2
  11. package/dist/{_init-BZ9t_Kz-.mjs → _init-BPFM4uBH.mjs} +3 -3
  12. package/dist/{_slug-api-DaqQJHk8.mjs → _slug-api-dcaQ_Uho.mjs} +2 -2
  13. package/dist/_studio-commands.d.ts +73 -0
  14. package/dist/_studio.d.ts +55 -0
  15. package/dist/{_templates-Bv8CR800.mjs → _templates-jmjgdgcd.mjs} +19 -8
  16. package/dist/_templates.d.ts +9 -0
  17. package/dist/{_typecheck-gate-9IHWDnl1.mjs → _typecheck-gate-DvE8S3aQ.mjs} +1 -1
  18. package/dist/{build-D_PgQOD4.mjs → build-BXwDB78d.mjs} +1 -1
  19. package/dist/cli.mjs +163 -34
  20. package/dist/delete-BaHgd9cN.mjs +51 -0
  21. package/dist/delete.d.ts +9 -2
  22. package/dist/{deploy-1eaXcfUw.mjs → deploy-D-O-Q_mW.mjs} +7 -5
  23. package/dist/deploy.d.ts +2 -0
  24. package/dist/{dev-gVNdGFYY.mjs → dev-C5P8amSg.mjs} +1 -1
  25. package/dist/{init-DoU4_txp.mjs → init-BGlOhIOl.mjs} +15 -14
  26. package/dist/login-B6IhXski.mjs +117 -0
  27. package/dist/login.d.ts +33 -20
  28. package/dist/scaffold/package.json +4 -4
  29. package/dist/{secret-CGAIAbUx.mjs → secret-_bHo4rqt.mjs} +1 -1
  30. package/dist/{storage-CnhOayhm.mjs → storage-MWiVx_mV.mjs} +1 -1
  31. package/dist/studio-CsRn2J1a.mjs +284 -0
  32. package/dist/studio.d.ts +52 -0
  33. package/package.json +4 -4
  34. package/dist/delete-DXilFBb1.mjs +0 -29
  35. package/dist/login-C59ZHzuO.mjs +0 -109
package/dist/cli.mjs CHANGED
@@ -5,7 +5,7 @@ import { existsSync, readFileSync } from "node:fs";
5
5
  import path from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { defineCommand, runMain } from "citty";
8
- //#region cli.ts
8
+ //#region _cli-common.ts
9
9
  /** Shared arg definitions for citty commands. */
10
10
  const sharedArgs = {
11
11
  server: {
@@ -23,21 +23,6 @@ const sharedArgs = {
23
23
  description: "Output JSON (auto-detected in non-TTY)"
24
24
  }
25
25
  };
26
- const cliDir = path.dirname(fileURLToPath(import.meta.url));
27
- /**
28
- * Read this CLI's own version from its package.json (source layout keeps it
29
- * next to cli.ts; dist layout one level up). A missing or corrupt file must
30
- * not brick every command over a cosmetic string — warn and fall back.
31
- */
32
- function readCliVersion(dir) {
33
- for (const candidate of [path.join(dir, "package.json"), path.join(dir, "..", "package.json")]) try {
34
- const parsed = JSON.parse(readFileSync(candidate, "utf-8"));
35
- if (typeof parsed.version === "string") return parsed.version;
36
- } catch {}
37
- process.stderr.write("warning: could not read aai's package.json — reporting version unknown\n");
38
- return "unknown";
39
- }
40
- const VERSION = readCliVersion(cliDir);
41
26
  /** Shared command setup: resolve cwd, optionally require agent.ts. */
42
27
  async function setup(opts) {
43
28
  const cwd = resolveCwd();
@@ -77,6 +62,140 @@ async function runCommand(args, fn) {
77
62
  if (mode === "json") await writeLine(`${JSON.stringify(result)}\n`);
78
63
  if (!result.ok) process.exit(1);
79
64
  }
65
+ //#endregion
66
+ //#region _studio-commands.ts
67
+ const list = defineCommand({
68
+ meta: {
69
+ name: "list",
70
+ description: "List your studio projects"
71
+ },
72
+ args: {
73
+ server: sharedArgs.server,
74
+ json: sharedArgs.json
75
+ },
76
+ async run({ args }) {
77
+ await runCommand(args, async () => {
78
+ const cwd = resolveCwd();
79
+ const { executeList } = await import("./studio-CsRn2J1a.mjs");
80
+ return executeList({
81
+ cwd,
82
+ server: args.server
83
+ });
84
+ });
85
+ }
86
+ });
87
+ const pull = defineCommand({
88
+ meta: {
89
+ name: "pull",
90
+ description: "Pull a studio project into a local directory"
91
+ },
92
+ args: {
93
+ project: {
94
+ type: "positional",
95
+ description: "Studio project name (see `aai list`)",
96
+ required: true
97
+ },
98
+ dir: {
99
+ type: "positional",
100
+ description: "Target directory (default: the project name)",
101
+ required: false
102
+ },
103
+ force: {
104
+ type: "boolean",
105
+ alias: "f",
106
+ description: "Overwrite files in a non-empty directory"
107
+ },
108
+ server: sharedArgs.server,
109
+ json: sharedArgs.json
110
+ },
111
+ async run({ args }) {
112
+ await runCommand(args, async () => {
113
+ const cwd = resolveCwd();
114
+ const { executePull } = await import("./studio-CsRn2J1a.mjs");
115
+ return executePull({
116
+ cwd,
117
+ project: args.project,
118
+ dir: args.dir,
119
+ force: args.force,
120
+ server: args.server
121
+ });
122
+ });
123
+ }
124
+ });
125
+ const push = defineCommand({
126
+ meta: {
127
+ name: "push",
128
+ description: "Sync this project's source to its studio workspace"
129
+ },
130
+ args: {
131
+ force: {
132
+ type: "boolean",
133
+ alias: "f",
134
+ description: "Overwrite studio-side changes instead of failing the fast-forward check"
135
+ },
136
+ server: sharedArgs.server,
137
+ json: sharedArgs.json
138
+ },
139
+ async run({ args }) {
140
+ await runCommand(args, async () => {
141
+ const cwd = await setup({ agent: true });
142
+ const { executePush } = await import("./studio-CsRn2J1a.mjs");
143
+ return executePush({
144
+ cwd,
145
+ server: args.server,
146
+ force: args.force
147
+ });
148
+ });
149
+ }
150
+ });
151
+ const publish = defineCommand({
152
+ meta: {
153
+ name: "publish",
154
+ description: "Push to the studio and deploy to production (the studio's Publish button)"
155
+ },
156
+ args: {
157
+ force: {
158
+ type: "boolean",
159
+ alias: "f",
160
+ description: "Overwrite studio-side changes instead of failing the fast-forward check"
161
+ },
162
+ server: sharedArgs.server,
163
+ json: sharedArgs.json,
164
+ skipTypecheck: {
165
+ type: "boolean",
166
+ description: "Skip type checking before publishing"
167
+ }
168
+ },
169
+ async run({ args }) {
170
+ await runCommand(args, async () => {
171
+ const cwd = await setup({ agent: true });
172
+ const { executePublish } = await import("./studio-CsRn2J1a.mjs");
173
+ return executePublish({
174
+ cwd,
175
+ server: args.server,
176
+ force: args.force,
177
+ skipTypecheck: args.skipTypecheck
178
+ });
179
+ });
180
+ }
181
+ });
182
+ //#endregion
183
+ //#region cli.ts
184
+ const cliDir = path.dirname(fileURLToPath(import.meta.url));
185
+ /**
186
+ * Read this CLI's own version from its package.json (source layout keeps it
187
+ * next to cli.ts; dist layout one level up). A missing or corrupt file must
188
+ * not brick every command over a cosmetic string — warn and fall back.
189
+ */
190
+ function readCliVersion(dir) {
191
+ for (const candidate of [path.join(dir, "package.json"), path.join(dir, "..", "package.json")]) try {
192
+ const parsed = JSON.parse(readFileSync(candidate, "utf-8"));
193
+ if (typeof parsed.version === "string") return parsed.version;
194
+ } catch {}
195
+ process.stderr.write("warning: could not read aai's package.json — reporting version unknown\n");
196
+ return "unknown";
197
+ }
198
+ const VERSION = readCliVersion(cliDir);
80
199
  const init = defineCommand({
81
200
  meta: {
82
201
  name: "init",
@@ -108,7 +227,7 @@ const init = defineCommand({
108
227
  },
109
228
  async run({ args }) {
110
229
  await runCommand(args, async (mode) => {
111
- const { executeInit } = await import("./init-DoU4_txp.mjs");
230
+ const { executeInit } = await import("./init-BGlOhIOl.mjs");
112
231
  return executeInit({
113
232
  dir: args.dir,
114
233
  force: args.force,
@@ -137,7 +256,7 @@ const dev = defineCommand({
137
256
  async run({ args }) {
138
257
  await runCommand(args, async () => {
139
258
  const cwd = await setup({ agent: true });
140
- const { executeDev } = await import("./dev-gVNdGFYY.mjs");
259
+ const { executeDev } = await import("./dev-C5P8amSg.mjs");
141
260
  return executeDev({
142
261
  cwd,
143
262
  port: args.port
@@ -178,7 +297,7 @@ const build = defineCommand({
178
297
  async run({ args }) {
179
298
  await runCommand(args, async () => {
180
299
  const cwd = await setup({ agent: true });
181
- const { executeBuild } = await import("./build-D_PgQOD4.mjs");
300
+ const { executeBuild } = await import("./build-BXwDB78d.mjs");
182
301
  return executeBuild({
183
302
  cwd,
184
303
  skipTests: args.skipTests,
@@ -190,7 +309,8 @@ const build = defineCommand({
190
309
  const deploy = defineCommand({
191
310
  meta: {
192
311
  name: "deploy",
193
- description: "Bundle and deploy to production"
312
+ description: "(internal) used by studio Publish",
313
+ hidden: true
194
314
  },
195
315
  args: {
196
316
  server: sharedArgs.server,
@@ -199,6 +319,10 @@ const deploy = defineCommand({
199
319
  type: "boolean",
200
320
  description: "Deploy even when the agent's providers are missing credentials (the server warns instead of rejecting; set them afterwards with `aai secret put`)"
201
321
  },
322
+ allowPreviewSlug: {
323
+ type: "boolean",
324
+ description: "Permit a `-preview`-suffixed slug (reserved for studio auto-previews; studio-internal — a slug you claim this way is subject to the preview reaper)"
325
+ },
202
326
  skipTypecheck: {
203
327
  type: "boolean",
204
328
  description: "Skip type checking before deploy"
@@ -207,11 +331,12 @@ const deploy = defineCommand({
207
331
  async run({ args }) {
208
332
  await runCommand(args, async () => {
209
333
  const cwd = await setup({ agent: true });
210
- const { executeDeploy } = await import("./deploy-1eaXcfUw.mjs");
334
+ const { executeDeploy } = await import("./deploy-D-O-Q_mW.mjs");
211
335
  return executeDeploy({
212
336
  cwd,
213
337
  server: args.server,
214
338
  allowMissingSecrets: args.allowMissingSecrets,
339
+ allowPreviewSlug: args.allowPreviewSlug,
215
340
  skipTypecheck: args.skipTypecheck
216
341
  });
217
342
  });
@@ -220,7 +345,7 @@ const deploy = defineCommand({
220
345
  const del = defineCommand({
221
346
  meta: {
222
347
  name: "delete",
223
- description: "Remove a deployed agent"
348
+ description: "Delete the studio project and its deployed agents"
224
349
  },
225
350
  args: {
226
351
  server: sharedArgs.server,
@@ -229,7 +354,7 @@ const del = defineCommand({
229
354
  async run({ args }) {
230
355
  await runCommand(args, async () => {
231
356
  const cwd = await setup();
232
- const { executeDelete } = await import("./delete-DXilFBb1.mjs");
357
+ const { executeDelete } = await import("./delete-BaHgd9cN.mjs");
233
358
  return executeDelete({
234
359
  cwd,
235
360
  server: args.server
@@ -260,7 +385,7 @@ const secret = defineCommand({
260
385
  async run({ args }) {
261
386
  await runCommand(args, async (mode) => {
262
387
  const cwd = await setup();
263
- const { executeSecretPut, NO_INPUT, readStdin } = await import("./secret-CGAIAbUx.mjs");
388
+ const { executeSecretPut, NO_INPUT, readStdin } = await import("./secret-_bHo4rqt.mjs");
264
389
  const value = mode === "json" ? await readStdin() : void 0;
265
390
  if (mode === "json" && !value) throw new CliError(...NO_INPUT);
266
391
  return executeSecretPut(cwd, args.name, value, args.server);
@@ -284,7 +409,7 @@ const secret = defineCommand({
284
409
  async run({ args }) {
285
410
  await runCommand(args, async () => {
286
411
  const cwd = await setup();
287
- const { executeSecretDelete } = await import("./secret-CGAIAbUx.mjs");
412
+ const { executeSecretDelete } = await import("./secret-_bHo4rqt.mjs");
288
413
  return executeSecretDelete(cwd, args.name, args.server);
289
414
  });
290
415
  }
@@ -301,7 +426,7 @@ const secret = defineCommand({
301
426
  async run({ args }) {
302
427
  await runCommand(args, async () => {
303
428
  const cwd = await setup();
304
- const { executeSecretList } = await import("./secret-CGAIAbUx.mjs");
429
+ const { executeSecretList } = await import("./secret-_bHo4rqt.mjs");
305
430
  return executeSecretList(cwd, args.server);
306
431
  });
307
432
  }
@@ -336,7 +461,7 @@ const storage = defineCommand({
336
461
  },
337
462
  async run({ args }) {
338
463
  await runCommand(args, async () => {
339
- const { executeStorageStatus } = await import("./storage-CnhOayhm.mjs");
464
+ const { executeStorageStatus } = await import("./storage-MWiVx_mV.mjs");
340
465
  return executeStorageStatus(resolveStorageCwd(args.dir), args.server);
341
466
  });
342
467
  }
@@ -353,7 +478,7 @@ const storage = defineCommand({
353
478
  },
354
479
  async run({ args }) {
355
480
  await runCommand(args, async () => {
356
- const { executeStorageEnable } = await import("./storage-CnhOayhm.mjs");
481
+ const { executeStorageEnable } = await import("./storage-MWiVx_mV.mjs");
357
482
  return executeStorageEnable(resolveStorageCwd(args.dir), args.server);
358
483
  });
359
484
  }
@@ -375,7 +500,7 @@ const storage = defineCommand({
375
500
  },
376
501
  async run({ args }) {
377
502
  await runCommand(args, async () => {
378
- const { executeStorageDisable } = await import("./storage-CnhOayhm.mjs");
503
+ const { executeStorageDisable } = await import("./storage-MWiVx_mV.mjs");
379
504
  return executeStorageDisable(resolveStorageCwd(args.dir), {
380
505
  server: args.server,
381
506
  force: args.force
@@ -388,7 +513,7 @@ const storage = defineCommand({
388
513
  const login = defineCommand({
389
514
  meta: {
390
515
  name: "login",
391
- description: "Sign in with your email and save your API key"
516
+ description: "Link your signed-in browser account and save your API key"
392
517
  },
393
518
  args: {
394
519
  server: sharedArgs.server,
@@ -396,7 +521,7 @@ const login = defineCommand({
396
521
  },
397
522
  async run({ args }) {
398
523
  await runCommand(args, async () => {
399
- const { executeLogin } = await import("./login-C59ZHzuO.mjs");
524
+ const { executeLogin } = await import("./login-B6IhXski.mjs");
400
525
  return executeLogin({ server: args.server });
401
526
  });
402
527
  }
@@ -409,7 +534,7 @@ const templates = defineCommand({
409
534
  args: { json: sharedArgs.json },
410
535
  async run({ args }) {
411
536
  await runCommand(args, async (mode) => {
412
- const { listTemplates } = await import("./_templates-Bv8CR800.mjs");
537
+ const { listTemplates } = await import("./_templates-jmjgdgcd.mjs");
413
538
  const names = await listTemplates();
414
539
  if (mode === "human") {
415
540
  for (const name of names) log.message(name);
@@ -433,6 +558,10 @@ const mainCommand = defineCommand({
433
558
  dev,
434
559
  test,
435
560
  build,
561
+ list,
562
+ pull,
563
+ push,
564
+ publish,
436
565
  deploy,
437
566
  delete: del,
438
567
  login,
@@ -450,12 +579,12 @@ if (process.env.VITEST !== "true") {
450
579
  return;
451
580
  }
452
581
  if (process.stdin.isTTY && process.stdout.isTTY) {
453
- if (await (await import("@clack/prompts")).confirm({ message: "Deploy this agent to production?" }) !== true) {
582
+ if (await (await import("@clack/prompts")).confirm({ message: "Publish this agent to production?" }) !== true) {
454
583
  log.info("Cancelled. Run `aai --help` to see all commands.");
455
584
  process.exit(0);
456
585
  }
457
586
  }
458
- process.argv.splice(2, 0, "deploy");
587
+ process.argv.splice(2, 0, "publish");
459
588
  };
460
589
  runDefault().then(() => runMain(mainCommand)).catch((err) => {
461
590
  log.error(errorMessage(err));
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+ import { n as log, u as ok } from "./_ui-8kOEB-JH.mjs";
3
+ import { i as resolveDeployTarget, n as getServerInfo } from "./_agent-BB1ZaZsX.mjs";
4
+ import { n as apiRequest, t as HINT_NOT_DEPLOYED } from "./_api-client-B6_uvuLs.mjs";
5
+ //#region delete.ts
6
+ async function runDelete(opts) {
7
+ await apiRequest(`${opts.url}/${opts.slug}`, {
8
+ method: "DELETE",
9
+ apiKey: opts.apiKey,
10
+ action: "delete",
11
+ hints: { 404: HINT_NOT_DEPLOYED },
12
+ ...opts.fetch ? { fetch: opts.fetch } : {}
13
+ });
14
+ }
15
+ /**
16
+ * Delete THE PROJECT. A studio-linked directory deletes its studio project
17
+ * (`DELETE /studio/projects/:project`), which cascades server-side to the
18
+ * workspace, chat, and the project's deployed + preview agents — the exact
19
+ * delete the studio's own Delete button runs. A directory that only knows a
20
+ * slug (no studio link) deletes that deployed agent directly.
21
+ */
22
+ async function executeDelete(opts) {
23
+ const { cwd } = opts;
24
+ const { config, serverUrl, apiKey } = await resolveDeployTarget(cwd, opts.server);
25
+ if (config?.studioProject) {
26
+ const project = config.studioProject;
27
+ log.step(`Deleting studio project ${project} (and its deployed agents)`);
28
+ await apiRequest(`${serverUrl}/studio/projects/${encodeURIComponent(project)}`, {
29
+ method: "DELETE",
30
+ apiKey,
31
+ action: "delete",
32
+ hints: { 404: "Run `aai list` to see your projects." }
33
+ });
34
+ log.success(`Deleted ${project}`);
35
+ return ok({
36
+ project,
37
+ ...config.slug ? { slug: config.slug } : {}
38
+ });
39
+ }
40
+ const { slug } = await getServerInfo(cwd, opts.server);
41
+ log.step(`Deleting ${slug}`);
42
+ await runDelete({
43
+ url: serverUrl,
44
+ slug,
45
+ apiKey
46
+ });
47
+ log.success(`Deleted ${serverUrl}/${slug}`);
48
+ return ok({ slug });
49
+ }
50
+ //#endregion
51
+ export { executeDelete };
package/dist/delete.d.ts CHANGED
@@ -8,9 +8,16 @@ export type DeleteOpts = {
8
8
  };
9
9
  export declare function runDelete(opts: DeleteOpts): Promise<void>;
10
10
  type DeleteData = {
11
- slug: string;
11
+ slug?: string;
12
+ project?: string;
12
13
  };
13
- /** Execute delete and return structured result. */
14
+ /**
15
+ * Delete THE PROJECT. A studio-linked directory deletes its studio project
16
+ * (`DELETE /studio/projects/:project`), which cascades server-side to the
17
+ * workspace, chat, and the project's deployed + preview agents — the exact
18
+ * delete the studio's own Delete button runs. A directory that only knows a
19
+ * slug (no studio link) deletes that deployed agent directly.
20
+ */
14
21
  export declare function executeDelete(opts: {
15
22
  cwd: string;
16
23
  server?: string | undefined;
@@ -2,17 +2,18 @@
2
2
  import { n as log, t as fmtUrl, u as ok } from "./_ui-8kOEB-JH.mjs";
3
3
  import { a as errorMessage } from "./_utils-Ch0J4s6a.mjs";
4
4
  import { t as buildAgentBundle } from "./_bundler-Cjaxa2wi.mjs";
5
- import { c as writeProjectConfig } from "./_config-5AEqhh-O.mjs";
5
+ import { s as updateProjectConfig } from "./_config-PIC0nzRu.mjs";
6
6
  import { t as resolveServerEnv } from "./_server-common-CnaP_Urf.mjs";
7
- import { i as resolveDeployTarget } from "./_agent-DMyOab9_.mjs";
8
- import { t as assertTypechecks } from "./_typecheck-gate-9IHWDnl1.mjs";
9
- import { n as apiRequest } from "./_api-client-MenP4-O7.mjs";
7
+ import { i as resolveDeployTarget } from "./_agent-BB1ZaZsX.mjs";
8
+ import { assertTypechecks } from "./_typecheck-gate-DvE8S3aQ.mjs";
9
+ import { n as apiRequest } from "./_api-client-B6_uvuLs.mjs";
10
10
  import { gzipSync } from "node:zlib";
11
11
  //#region _deploy.ts
12
12
  async function runDeploy(opts) {
13
13
  const body = gzipSync(JSON.stringify({
14
14
  ...opts.slug ? { slug: opts.slug } : {},
15
15
  ...opts.allowMissingSecrets ? { credentialPolicy: "warn" } : {},
16
+ ...opts.allowPreviewSlug ? { allowPreviewSlug: true } : {},
16
17
  env: opts.env,
17
18
  worker: opts.bundle.worker,
18
19
  clientFiles: opts.bundle.clientFiles
@@ -55,11 +56,12 @@ async function executeDeploy(opts) {
55
56
  },
56
57
  ...slug ? { slug } : {},
57
58
  ...opts.allowMissingSecrets ? { allowMissingSecrets: true } : {},
59
+ ...opts.allowPreviewSlug ? { allowPreviewSlug: true } : {},
58
60
  apiKey
59
61
  });
60
62
  const agentUrl = `${serverUrl}/${deployed.slug}`;
61
63
  try {
62
- await writeProjectConfig(cwd, {
64
+ await updateProjectConfig(cwd, {
63
65
  slug: deployed.slug,
64
66
  serverUrl
65
67
  });
package/dist/deploy.d.ts CHANGED
@@ -9,6 +9,8 @@ export declare function executeDeploy(opts: {
9
9
  server?: string | undefined;
10
10
  /** See DeployOpts.allowMissingSecrets (`--allow-missing-secrets`). */
11
11
  allowMissingSecrets?: boolean | undefined;
12
+ /** See DeployOpts.allowPreviewSlug (`--allow-preview-slug`; studio-internal). */
13
+ allowPreviewSlug?: boolean | undefined;
12
14
  /** `--skipTypecheck`: deploy without the tsc gate. */
13
15
  skipTypecheck?: boolean | undefined;
14
16
  }): Promise<CommandResult<DeployData>>;
@@ -11,7 +11,7 @@ import { styleText } from "node:util";
11
11
  async function executeDev(opts) {
12
12
  const port = parsePort(opts.port);
13
13
  const agentName = path.basename(path.resolve(opts.cwd));
14
- const { startDevServer } = await import("./_dev-server-vV05Fnki.mjs");
14
+ const { startDevServer } = await import("./_dev-server-CGptLIAm.mjs");
15
15
  let cleanup;
16
16
  let shuttingDown = false;
17
17
  const onSignal = () => {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { a as unwrapCancel, n as log, u as ok } from "./_ui-8kOEB-JH.mjs";
3
3
  import { a as errorMessage, c as readJson, l as resolveCwd, o as fileExists, t as AGENT_ENTRY } from "./_utils-Ch0J4s6a.mjs";
4
- import { r as isDevMode, t as getMonorepoRoot } from "./_agent-DMyOab9_.mjs";
4
+ import { r as isDevMode, t as getMonorepoRoot } from "./_agent-BB1ZaZsX.mjs";
5
5
  import path from "node:path";
6
6
  import { styleText } from "node:util";
7
7
  import * as p from "@clack/prompts";
@@ -72,27 +72,28 @@ async function installDeps(cwd, silent) {
72
72
  function resolveTargetDir(dir) {
73
73
  return path.resolve(resolveCwd(), dir);
74
74
  }
75
- /** Run deploy after init and return deploy metadata if successful. */
76
- async function tryDeploy(cwd, server) {
77
- const { executeDeploy } = await import("./deploy-1eaXcfUw.mjs");
75
+ /** Publish after init and return deploy metadata if successful. */
76
+ async function tryPublish(cwd, server) {
77
+ const { executePublish } = await import("./studio-CsRn2J1a.mjs");
78
78
  try {
79
- const result = await executeDeploy({
79
+ const result = await executePublish({
80
80
  cwd,
81
81
  ...server ? { server } : {}
82
82
  });
83
83
  return result.ok ? {
84
84
  slug: result.data.slug,
85
- url: result.data.url
85
+ url: result.data.url,
86
+ studioUrl: result.data.studioUrl
86
87
  } : null;
87
88
  } catch (err) {
88
- log.warn(`Deploy failed: ${errorMessage(err)}`);
89
- log.warn("Your project was still created — run `aai deploy` in it to retry.");
89
+ log.warn(`Publish failed: ${errorMessage(err)}`);
90
+ log.warn("Your project was still created — run `aai publish` in it to retry.");
90
91
  return null;
91
92
  }
92
93
  }
93
94
  /** Scaffold the project, optionally showing a spinner. */
94
95
  async function scaffoldProject(dir, cwd, template, silent) {
95
- const { runInit } = await import("./_init-BZ9t_Kz-.mjs");
96
+ const { runInit } = await import("./_init-BPFM4uBH.mjs");
96
97
  const s = silent ? void 0 : p.spinner();
97
98
  s?.start(`Creating ${dir}`);
98
99
  await runInit({
@@ -120,13 +121,13 @@ async function executeInit(opts, extra) {
120
121
  let deployed = false;
121
122
  let slug;
122
123
  let url;
123
- if (!installed) log.warn("Skipping deploy because dependencies were not installed.");
124
+ if (!installed) log.warn("Skipping publish because dependencies were not installed.");
124
125
  else if (!opts.skipDeploy) {
125
- const deployInfo = await tryDeploy(cwd, opts.server);
126
- if (deployInfo) {
126
+ const published = await tryPublish(cwd, opts.server);
127
+ if (published) {
127
128
  deployed = true;
128
- slug = deployInfo.slug;
129
- url = deployInfo.url;
129
+ slug = published.slug;
130
+ url = published.url;
130
131
  }
131
132
  }
132
133
  if (!suppressUi) printPostInitInfo(cwd, monorepoRoot);
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+ import { n as log, o as CliError, u as ok } from "./_ui-8kOEB-JH.mjs";
3
+ import { c as writeGlobalConfig, i as readGlobalConfig, r as getConfigDir, t as approveServer } from "./_config-PIC0nzRu.mjs";
4
+ import { a as resolveServerUrl } from "./_agent-BB1ZaZsX.mjs";
5
+ import { spawn } from "node:child_process";
6
+ import { setTimeout } from "node:timers/promises";
7
+ import { randomBytes } from "node:crypto";
8
+ //#region login.ts
9
+ /**
10
+ * `aai login` — link the CLI to an account that is ALREADY signed in to the
11
+ * browser studio, ending with the account's AssemblyAI API key stored in
12
+ * the global config (the same slot `ensureApiKey` reads), so every other
13
+ * command is untouched by how the key was acquired.
14
+ *
15
+ * The CLI deliberately performs no sign-in of its own — it cannot create an
16
+ * account, and it never sees a session token. Device-link flow:
17
+ * 1. `GET /studio/auth` — fail fast when the server has no browser login
18
+ * configured (nobody could ever approve the link).
19
+ * 2. Mint an unguessable one-shot code (32 random bytes, base64url) and
20
+ * open the browser at `<server>/?cli-link=<code>`. The studio — where
21
+ * the user signs in with GitHub (or the local-dev login) if they aren't
22
+ * already — shows a "link the CLI to this account?" approval displaying
23
+ * the same short confirmation code this terminal printed, so a phished
24
+ * approval link has a visible mismatch (no terminal to match against).
25
+ * 3. Poll `POST /studio/cli-link/exchange` with the code. Approval grants
26
+ * the code ONE exchange for the account's stored API key, which is
27
+ * saved locally. An account with no stored key can't approve — the
28
+ * studio's own onboarding gate runs first — so the CLI never sets keys.
29
+ */
30
+ const LINK_POLL_INTERVAL_MS = 2e3;
31
+ const LINK_TIMEOUT_MS = 3e5;
32
+ async function jsonBody(res, what) {
33
+ const body = await res.json().catch(() => null);
34
+ if (!res.ok) throw new CliError("login_failed", `${what} failed: ${body?.error ?? body?.msg ?? `HTTP ${res.status}`}`);
35
+ if (body === null) throw new CliError("login_failed", `${what} returned an invalid response`);
36
+ return body;
37
+ }
38
+ function requireTty() {
39
+ if (!process.stdin.isTTY) throw new CliError("login_interactive", "`aai login` is interactive and needs a TTY.", "Non-interactive setups can set the ASSEMBLYAI_API_KEY environment variable instead.");
40
+ }
41
+ function openerFor(platform) {
42
+ if (platform === "darwin") return ["open", []];
43
+ if (platform === "win32") return ["cmd", [
44
+ "/c",
45
+ "start",
46
+ ""
47
+ ]];
48
+ return ["xdg-open", []];
49
+ }
50
+ /**
51
+ * Human-matchable confirmation derived from the link code — printed in the
52
+ * terminal AND shown on the browser approval gate (aai-studio-client's
53
+ * cli-link.ts derives the same value; keep the two in lockstep). Not a
54
+ * secret: both ends already hold the full code. It exists so someone who
55
+ * lands on an approval page they didn't cause has a concrete mismatch to
56
+ * notice ("what terminal?") instead of a bare Approve button.
57
+ */
58
+ function linkConfirmationCode(code) {
59
+ const head = code.slice(0, 8).toUpperCase();
60
+ return `${head.slice(0, 4)}-${head.slice(4)}`;
61
+ }
62
+ /** Best-effort: the link URL is always printed, so a failure is fine. */
63
+ function defaultOpenBrowser(url) {
64
+ const [cmd, args] = openerFor(process.platform);
65
+ try {
66
+ const child = spawn(cmd, [...args, url], {
67
+ stdio: "ignore",
68
+ detached: true
69
+ });
70
+ child.on("error", () => {});
71
+ child.unref();
72
+ } catch {}
73
+ }
74
+ async function executeLogin(opts, deps = {}) {
75
+ const fetchFn = deps.fetchFn ?? globalThis.fetch;
76
+ requireTty();
77
+ const globalConfig = await readGlobalConfig();
78
+ const serverUrl = resolveServerUrl(opts.server, void 0, globalConfig.approvedServers ?? []);
79
+ if (opts.server) await approveServer(serverUrl);
80
+ if ((await jsonBody(await fetchFn(`${serverUrl}/studio/auth`), "Reading the server's login configuration")).mode === "none") throw new CliError("login_unavailable", "This server has no browser login configured, so there is no account to link.", "Set the ASSEMBLYAI_API_KEY environment variable, or run any platform command to be prompted for a key.");
81
+ const code = randomBytes(32).toString("base64url");
82
+ const linkUrl = `${serverUrl}/?cli-link=${code}`;
83
+ log.info(`Opening the browser to link your account…\n ${linkUrl}`);
84
+ log.info(`Confirmation code: ${linkConfirmationCode(code)}`);
85
+ log.info("Approve the link in the browser (sign in there first if you need to) — the approval page shows the same code.");
86
+ (deps.openBrowser ?? defaultOpenBrowser)(linkUrl);
87
+ const pollInterval = deps.pollIntervalMs ?? LINK_POLL_INTERVAL_MS;
88
+ const deadline = Date.now() + (deps.timeoutMs ?? LINK_TIMEOUT_MS);
89
+ let granted;
90
+ for (;;) {
91
+ const res = await fetchFn(`${serverUrl}/studio/cli-link/exchange`, {
92
+ method: "POST",
93
+ headers: { "Content-Type": "application/json" },
94
+ body: JSON.stringify({ code })
95
+ });
96
+ if (res.status !== 404) {
97
+ granted = await jsonBody(res, "Linking your account");
98
+ break;
99
+ }
100
+ if (Date.now() >= deadline) throw new CliError("login_timeout", "Timed out waiting for the link to be approved in the browser.", "Run `aai login` again and approve the link within five minutes.");
101
+ await setTimeout(pollInterval);
102
+ }
103
+ if (!granted.apiKey) throw new CliError("login_failed", "Linking your account did not return an API key.");
104
+ const dir = getConfigDir();
105
+ await writeGlobalConfig(dir, {
106
+ ...await readGlobalConfig(dir),
107
+ apiKey: granted.apiKey
108
+ });
109
+ const email = granted.email ?? "your account";
110
+ log.success(`Linked ${email} — your API key is saved for future commands.`);
111
+ return ok({
112
+ email,
113
+ server: serverUrl
114
+ });
115
+ }
116
+ //#endregion
117
+ export { executeLogin };