@alexkroman1/aai-cli 5.4.0 → 5.5.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.
- package/dist/{_agent-D8zBb3M5.mjs → _agent-DMyOab9_.mjs} +2 -2
- package/dist/{_config-D3F9km8X.mjs → _config-5AEqhh-O.mjs} +2 -2
- package/dist/{_dev-server-cigzayJe.mjs → _dev-server-vV05Fnki.mjs} +1 -1
- package/dist/{_init-_G8l4lh2.mjs → _init-BZ9t_Kz-.mjs} +2 -2
- package/dist/{_slug-api-19R5kZ0U.mjs → _slug-api-DaqQJHk8.mjs} +1 -1
- package/dist/{_templates-DW9CvuDN.mjs → _templates-Bv8CR800.mjs} +1 -1
- package/dist/cli.mjs +28 -11
- package/dist/{delete-kSXFLxek.mjs → delete-DXilFBb1.mjs} +1 -1
- package/dist/{deploy-C0VLnnJe.mjs → deploy-1eaXcfUw.mjs} +2 -2
- package/dist/{dev-2oUsvgRX.mjs → dev-gVNdGFYY.mjs} +1 -1
- package/dist/{init-BkW9BuZF.mjs → init-DoU4_txp.mjs} +3 -3
- package/dist/login-C59ZHzuO.mjs +109 -0
- package/dist/login.d.ts +34 -0
- package/dist/scaffold/CLAUDE.md +69 -53
- package/dist/scaffold/package.json +3 -3
- package/dist/{secret-DOva9OGk.mjs → secret-CGAIAbUx.mjs} +1 -1
- package/dist/{storage-vM6HjDZZ.mjs → storage-CnhOayhm.mjs} +1 -1
- package/dist/templates/code-interpreter/agent.ts +1 -2
- package/dist/templates/dispatch-center/agent.ts +5 -5
- package/dist/templates/dispatch-center/client.tsx +7 -2
- package/dist/templates/embedded-assets/agent.ts +1 -2
- package/dist/templates/health-assistant/agent.ts +1 -2
- package/dist/templates/infocom-adventure/agent.ts +4 -6
- package/dist/templates/infocom-adventure/client.tsx +5 -5
- package/dist/templates/math-buddy/agent.ts +6 -5
- package/dist/templates/night-owl/agent.ts +1 -2
- package/dist/templates/personal-finance/agent.ts +1 -2
- package/dist/templates/pipeline-simple/agent.test.ts +13 -13
- package/dist/templates/pipeline-simple/agent.ts +5 -6
- package/dist/templates/pizza-ordering/agent.ts +1 -2
- package/dist/templates/retail/address.ts +29 -0
- package/dist/templates/retail/agent.test.ts +1030 -0
- package/dist/templates/retail/agent.ts +71 -0
- package/dist/templates/retail/authenticate.ts +34 -0
- package/dist/templates/retail/client.tsx +459 -0
- package/dist/templates/retail/refund.ts +19 -0
- package/dist/templates/retail/registry.test.ts +182 -0
- package/dist/templates/retail/resolve.test.ts +158 -0
- package/dist/templates/retail/resolve.ts +158 -0
- package/dist/templates/retail/seed.json +4260 -0
- package/dist/templates/retail/seed.test.ts +224 -0
- package/dist/templates/retail/shared.test.ts +192 -0
- package/dist/templates/retail/shared.ts +430 -0
- package/dist/templates/retail/store.test.ts +256 -0
- package/dist/templates/retail/store.ts +243 -0
- package/dist/templates/retail/swap.test.ts +186 -0
- package/dist/templates/retail/swap.ts +138 -0
- package/dist/templates/retail/system-prompt.md +111 -0
- package/dist/templates/retail/tools/cancel_pending_order.ts +82 -0
- package/dist/templates/retail/tools/exchange_delivered_order_items.ts +83 -0
- package/dist/templates/retail/tools/find_user_id_by_email.ts +32 -0
- package/dist/templates/retail/tools/find_user_id_by_name_zip.ts +38 -0
- package/dist/templates/retail/tools/get_item_details.ts +29 -0
- package/dist/templates/retail/tools/get_order_details.ts +41 -0
- package/dist/templates/retail/tools/get_product_details.ts +33 -0
- package/dist/templates/retail/tools/get_user_details.ts +44 -0
- package/dist/templates/retail/tools/list_all_product_types.ts +24 -0
- package/dist/templates/retail/tools/modify_pending_order_address.ts +46 -0
- package/dist/templates/retail/tools/modify_pending_order_items.ts +102 -0
- package/dist/templates/retail/tools/modify_pending_order_payment.ts +103 -0
- package/dist/templates/retail/tools/modify_user_address.ts +38 -0
- package/dist/templates/retail/tools/return_delivered_order_items.ts +101 -0
- package/dist/templates/retail/tools/transfer_to_human_agents.ts +22 -0
- package/dist/templates/simple/agent.test.ts +1 -1
- package/dist/templates/simple/agent.ts +2 -1
- package/dist/templates/solo-rpg/agent.ts +1 -2
- package/dist/templates/web-researcher/agent.ts +1 -2
- package/package.json +3 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as readProjectConfig, i as readGlobalConfig, n as ensureApiKey, o as serverOrigin, t as approveServer } from "./_config-5AEqhh-O.mjs";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -92,4 +92,4 @@ async function getServerInfo(cwd, explicitServer) {
|
|
|
92
92
|
};
|
|
93
93
|
}
|
|
94
94
|
//#endregion
|
|
95
|
-
export { resolveDeployTarget as i, getServerInfo as n, isDevMode as r, getMonorepoRoot as t };
|
|
95
|
+
export { resolveServerUrl as a, resolveDeployTarget as i, getServerInfo as n, isDevMode as r, getMonorepoRoot as t };
|
|
@@ -115,10 +115,10 @@ async function ensureApiKey(configDir) {
|
|
|
115
115
|
await trySaveApiKey(dir, config, envKey);
|
|
116
116
|
return envKey;
|
|
117
117
|
}
|
|
118
|
-
if (!process.stdin.isTTY) throw new CliError("no_api_key", "No API key configured and no TTY to prompt for one.", "Set the ASSEMBLYAI_API_KEY environment variable, or run `aai` interactively once to save a key.");
|
|
118
|
+
if (!process.stdin.isTTY) throw new CliError("no_api_key", "No API key configured and no TTY to prompt for one.", "Set the ASSEMBLYAI_API_KEY environment variable, or run `aai login` interactively once to save a key.");
|
|
119
119
|
const apiKey = unwrapCancel(await p.password({ message: "Enter your AssemblyAI API key" }), "Setup cancelled");
|
|
120
120
|
await trySaveApiKey(dir, config, apiKey);
|
|
121
121
|
return apiKey;
|
|
122
122
|
}
|
|
123
123
|
//#endregion
|
|
124
|
-
export {
|
|
124
|
+
export { readProjectConfig as a, writeProjectConfig as c, readGlobalConfig as i, ensureApiKey as n, serverOrigin as o, getConfigDir as r, writeGlobalConfig as s, approveServer as t };
|
|
@@ -4,7 +4,7 @@ import { a as errorMessage, r as errorCode } from "./_utils-Ch0J4s6a.mjs";
|
|
|
4
4
|
import { n as fallbackHtmlPlugin } from "./client-bundler-yiWoXrgb.mjs";
|
|
5
5
|
import { buildWorker } from "./worker-bundler.mjs";
|
|
6
6
|
import { n as createWorkerEvaluator } from "./_bundler-Cjaxa2wi.mjs";
|
|
7
|
-
import { n as ensureApiKey } from "./_config-
|
|
7
|
+
import { n as ensureApiKey } from "./_config-5AEqhh-O.mjs";
|
|
8
8
|
import { t as resolveServerEnv } from "./_server-common-CnaP_Urf.mjs";
|
|
9
9
|
import { createRequire } from "node:module";
|
|
10
10
|
import { existsSync } from "node:fs";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { c as readJson, d as writeJson, s as isEexist } from "./_utils-Ch0J4s6a.mjs";
|
|
3
|
-
import { r as isDevMode, t as getMonorepoRoot } from "./_agent-
|
|
4
|
-
import { REPO_URL, downloadAndMergeTemplate } from "./_templates-
|
|
3
|
+
import { r as isDevMode, t as getMonorepoRoot } from "./_agent-DMyOab9_.mjs";
|
|
4
|
+
import { REPO_URL, downloadAndMergeTemplate } from "./_templates-Bv8CR800.mjs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import fs from "node:fs/promises";
|
|
7
7
|
//#region _init.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { a as errorMessage } from "./_utils-Ch0J4s6a.mjs";
|
|
3
|
-
import { t as getMonorepoRoot } from "./_agent-
|
|
3
|
+
import { t as getMonorepoRoot } from "./_agent-DMyOab9_.mjs";
|
|
4
4
|
import { existsSync } from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import fs from "node:fs/promises";
|
package/dist/cli.mjs
CHANGED
|
@@ -108,7 +108,7 @@ const init = defineCommand({
|
|
|
108
108
|
},
|
|
109
109
|
async run({ args }) {
|
|
110
110
|
await runCommand(args, async (mode) => {
|
|
111
|
-
const { executeInit } = await import("./init-
|
|
111
|
+
const { executeInit } = await import("./init-DoU4_txp.mjs");
|
|
112
112
|
return executeInit({
|
|
113
113
|
dir: args.dir,
|
|
114
114
|
force: args.force,
|
|
@@ -137,7 +137,7 @@ const dev = defineCommand({
|
|
|
137
137
|
async run({ args }) {
|
|
138
138
|
await runCommand(args, async () => {
|
|
139
139
|
const cwd = await setup({ agent: true });
|
|
140
|
-
const { executeDev } = await import("./dev-
|
|
140
|
+
const { executeDev } = await import("./dev-gVNdGFYY.mjs");
|
|
141
141
|
return executeDev({
|
|
142
142
|
cwd,
|
|
143
143
|
port: args.port
|
|
@@ -207,7 +207,7 @@ const deploy = defineCommand({
|
|
|
207
207
|
async run({ args }) {
|
|
208
208
|
await runCommand(args, async () => {
|
|
209
209
|
const cwd = await setup({ agent: true });
|
|
210
|
-
const { executeDeploy } = await import("./deploy-
|
|
210
|
+
const { executeDeploy } = await import("./deploy-1eaXcfUw.mjs");
|
|
211
211
|
return executeDeploy({
|
|
212
212
|
cwd,
|
|
213
213
|
server: args.server,
|
|
@@ -229,7 +229,7 @@ const del = defineCommand({
|
|
|
229
229
|
async run({ args }) {
|
|
230
230
|
await runCommand(args, async () => {
|
|
231
231
|
const cwd = await setup();
|
|
232
|
-
const { executeDelete } = await import("./delete-
|
|
232
|
+
const { executeDelete } = await import("./delete-DXilFBb1.mjs");
|
|
233
233
|
return executeDelete({
|
|
234
234
|
cwd,
|
|
235
235
|
server: args.server
|
|
@@ -260,7 +260,7 @@ const secret = defineCommand({
|
|
|
260
260
|
async run({ args }) {
|
|
261
261
|
await runCommand(args, async (mode) => {
|
|
262
262
|
const cwd = await setup();
|
|
263
|
-
const { executeSecretPut, NO_INPUT, readStdin } = await import("./secret-
|
|
263
|
+
const { executeSecretPut, NO_INPUT, readStdin } = await import("./secret-CGAIAbUx.mjs");
|
|
264
264
|
const value = mode === "json" ? await readStdin() : void 0;
|
|
265
265
|
if (mode === "json" && !value) throw new CliError(...NO_INPUT);
|
|
266
266
|
return executeSecretPut(cwd, args.name, value, args.server);
|
|
@@ -284,7 +284,7 @@ const secret = defineCommand({
|
|
|
284
284
|
async run({ args }) {
|
|
285
285
|
await runCommand(args, async () => {
|
|
286
286
|
const cwd = await setup();
|
|
287
|
-
const { executeSecretDelete } = await import("./secret-
|
|
287
|
+
const { executeSecretDelete } = await import("./secret-CGAIAbUx.mjs");
|
|
288
288
|
return executeSecretDelete(cwd, args.name, args.server);
|
|
289
289
|
});
|
|
290
290
|
}
|
|
@@ -301,7 +301,7 @@ const secret = defineCommand({
|
|
|
301
301
|
async run({ args }) {
|
|
302
302
|
await runCommand(args, async () => {
|
|
303
303
|
const cwd = await setup();
|
|
304
|
-
const { executeSecretList } = await import("./secret-
|
|
304
|
+
const { executeSecretList } = await import("./secret-CGAIAbUx.mjs");
|
|
305
305
|
return executeSecretList(cwd, args.server);
|
|
306
306
|
});
|
|
307
307
|
}
|
|
@@ -336,7 +336,7 @@ const storage = defineCommand({
|
|
|
336
336
|
},
|
|
337
337
|
async run({ args }) {
|
|
338
338
|
await runCommand(args, async () => {
|
|
339
|
-
const { executeStorageStatus } = await import("./storage-
|
|
339
|
+
const { executeStorageStatus } = await import("./storage-CnhOayhm.mjs");
|
|
340
340
|
return executeStorageStatus(resolveStorageCwd(args.dir), args.server);
|
|
341
341
|
});
|
|
342
342
|
}
|
|
@@ -353,7 +353,7 @@ const storage = defineCommand({
|
|
|
353
353
|
},
|
|
354
354
|
async run({ args }) {
|
|
355
355
|
await runCommand(args, async () => {
|
|
356
|
-
const { executeStorageEnable } = await import("./storage-
|
|
356
|
+
const { executeStorageEnable } = await import("./storage-CnhOayhm.mjs");
|
|
357
357
|
return executeStorageEnable(resolveStorageCwd(args.dir), args.server);
|
|
358
358
|
});
|
|
359
359
|
}
|
|
@@ -375,7 +375,7 @@ const storage = defineCommand({
|
|
|
375
375
|
},
|
|
376
376
|
async run({ args }) {
|
|
377
377
|
await runCommand(args, async () => {
|
|
378
|
-
const { executeStorageDisable } = await import("./storage-
|
|
378
|
+
const { executeStorageDisable } = await import("./storage-CnhOayhm.mjs");
|
|
379
379
|
return executeStorageDisable(resolveStorageCwd(args.dir), {
|
|
380
380
|
server: args.server,
|
|
381
381
|
force: args.force
|
|
@@ -385,6 +385,22 @@ const storage = defineCommand({
|
|
|
385
385
|
})
|
|
386
386
|
}
|
|
387
387
|
});
|
|
388
|
+
const login = defineCommand({
|
|
389
|
+
meta: {
|
|
390
|
+
name: "login",
|
|
391
|
+
description: "Sign in with your email and save your API key"
|
|
392
|
+
},
|
|
393
|
+
args: {
|
|
394
|
+
server: sharedArgs.server,
|
|
395
|
+
json: sharedArgs.json
|
|
396
|
+
},
|
|
397
|
+
async run({ args }) {
|
|
398
|
+
await runCommand(args, async () => {
|
|
399
|
+
const { executeLogin } = await import("./login-C59ZHzuO.mjs");
|
|
400
|
+
return executeLogin({ server: args.server });
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
});
|
|
388
404
|
const templates = defineCommand({
|
|
389
405
|
meta: {
|
|
390
406
|
name: "templates",
|
|
@@ -393,7 +409,7 @@ const templates = defineCommand({
|
|
|
393
409
|
args: { json: sharedArgs.json },
|
|
394
410
|
async run({ args }) {
|
|
395
411
|
await runCommand(args, async (mode) => {
|
|
396
|
-
const { listTemplates } = await import("./_templates-
|
|
412
|
+
const { listTemplates } = await import("./_templates-Bv8CR800.mjs");
|
|
397
413
|
const names = await listTemplates();
|
|
398
414
|
if (mode === "human") {
|
|
399
415
|
for (const name of names) log.message(name);
|
|
@@ -419,6 +435,7 @@ const mainCommand = defineCommand({
|
|
|
419
435
|
build,
|
|
420
436
|
deploy,
|
|
421
437
|
delete: del,
|
|
438
|
+
login,
|
|
422
439
|
secret,
|
|
423
440
|
storage,
|
|
424
441
|
templates
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { n as log, u as ok } from "./_ui-8kOEB-JH.mjs";
|
|
3
|
-
import { n as getServerInfo } from "./_agent-
|
|
3
|
+
import { n as getServerInfo } from "./_agent-DMyOab9_.mjs";
|
|
4
4
|
import { n as apiRequest, t as HINT_NOT_DEPLOYED } from "./_api-client-MenP4-O7.mjs";
|
|
5
5
|
//#region delete.ts
|
|
6
6
|
async function runDelete(opts) {
|
|
@@ -2,9 +2,9 @@
|
|
|
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 {
|
|
5
|
+
import { c as writeProjectConfig } from "./_config-5AEqhh-O.mjs";
|
|
6
6
|
import { t as resolveServerEnv } from "./_server-common-CnaP_Urf.mjs";
|
|
7
|
-
import { i as resolveDeployTarget } from "./_agent-
|
|
7
|
+
import { i as resolveDeployTarget } from "./_agent-DMyOab9_.mjs";
|
|
8
8
|
import { t as assertTypechecks } from "./_typecheck-gate-9IHWDnl1.mjs";
|
|
9
9
|
import { n as apiRequest } from "./_api-client-MenP4-O7.mjs";
|
|
10
10
|
import { gzipSync } from "node:zlib";
|
|
@@ -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-
|
|
14
|
+
const { startDevServer } = await import("./_dev-server-vV05Fnki.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-
|
|
4
|
+
import { r as isDevMode, t as getMonorepoRoot } from "./_agent-DMyOab9_.mjs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { styleText } from "node:util";
|
|
7
7
|
import * as p from "@clack/prompts";
|
|
@@ -74,7 +74,7 @@ function resolveTargetDir(dir) {
|
|
|
74
74
|
}
|
|
75
75
|
/** Run deploy after init and return deploy metadata if successful. */
|
|
76
76
|
async function tryDeploy(cwd, server) {
|
|
77
|
-
const { executeDeploy } = await import("./deploy-
|
|
77
|
+
const { executeDeploy } = await import("./deploy-1eaXcfUw.mjs");
|
|
78
78
|
try {
|
|
79
79
|
const result = await executeDeploy({
|
|
80
80
|
cwd,
|
|
@@ -92,7 +92,7 @@ async function tryDeploy(cwd, server) {
|
|
|
92
92
|
}
|
|
93
93
|
/** Scaffold the project, optionally showing a spinner. */
|
|
94
94
|
async function scaffoldProject(dir, cwd, template, silent) {
|
|
95
|
-
const { runInit } = await import("./_init-
|
|
95
|
+
const { runInit } = await import("./_init-BZ9t_Kz-.mjs");
|
|
96
96
|
const s = silent ? void 0 : p.spinner();
|
|
97
97
|
s?.start(`Creating ${dir}`);
|
|
98
98
|
await runInit({
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { a as unwrapCancel, n as log, o as CliError, u as ok } from "./_ui-8kOEB-JH.mjs";
|
|
3
|
+
import { i as readGlobalConfig, r as getConfigDir, s as writeGlobalConfig, t as approveServer } from "./_config-5AEqhh-O.mjs";
|
|
4
|
+
import { a as resolveServerUrl } from "./_agent-DMyOab9_.mjs";
|
|
5
|
+
import * as p from "@clack/prompts";
|
|
6
|
+
//#region login.ts
|
|
7
|
+
/**
|
|
8
|
+
* `aai login` — email sign-in against the platform, ending with the
|
|
9
|
+
* account's AssemblyAI API key stored in the global config (the same slot
|
|
10
|
+
* `ensureApiKey` reads), so every other command is untouched by how the
|
|
11
|
+
* key was acquired.
|
|
12
|
+
*
|
|
13
|
+
* Flow (the CLI mirror of the browser studio's two gates):
|
|
14
|
+
* 1. `GET /studio/auth` names the login mode.
|
|
15
|
+
* - `supabase`: Supabase email OTP — `POST /auth/v1/otp` emails a
|
|
16
|
+
* one-time code, the user types it here, `POST /auth/v1/verify`
|
|
17
|
+
* returns the session. The magic LINK in the same email targets the
|
|
18
|
+
* browser; the CLI uses the code because a terminal has no redirect
|
|
19
|
+
* to land on. (The Supabase email template must include the
|
|
20
|
+
* `{{ .Token }}` code for this to work.)
|
|
21
|
+
* - `dev`: mint the same self-describing dev token the studio's local
|
|
22
|
+
* login mints — nothing is emailed anywhere.
|
|
23
|
+
* 2. `GET /studio/account` — when no key is on file yet, prompt for one
|
|
24
|
+
* and `PUT /studio/account/key` (the same mandatory onboarding step
|
|
25
|
+
* the browser shows after sign-in).
|
|
26
|
+
* 3. `GET /studio/account/key` — fetch the key and save it locally.
|
|
27
|
+
* Unlike the browser, the CLI needs the RAW key: `aai dev` runs the
|
|
28
|
+
* provider pipeline in-process on it.
|
|
29
|
+
*/
|
|
30
|
+
async function jsonBody(res, what) {
|
|
31
|
+
const body = await res.json().catch(() => null);
|
|
32
|
+
if (!res.ok) throw new CliError("login_failed", `${what} failed: ${body?.error ?? body?.msg ?? `HTTP ${res.status}`}`);
|
|
33
|
+
if (body === null) throw new CliError("login_failed", `${what} returned an invalid response`);
|
|
34
|
+
return body;
|
|
35
|
+
}
|
|
36
|
+
function requireTty() {
|
|
37
|
+
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.");
|
|
38
|
+
}
|
|
39
|
+
/** The same self-describing token the studio's local-dev login mints. */
|
|
40
|
+
function mintDevToken(email) {
|
|
41
|
+
return `dev.${Buffer.from(JSON.stringify({
|
|
42
|
+
id: `dev:${email}`,
|
|
43
|
+
email
|
|
44
|
+
})).toString("base64url").replace(/=+$/, "")}.dev`;
|
|
45
|
+
}
|
|
46
|
+
/** Supabase email OTP: send the code, prompt for it, verify to a session. */
|
|
47
|
+
async function supabaseSession(auth, email, fetchFn) {
|
|
48
|
+
const base = auth.supabaseUrl.replace(/\/+$/, "");
|
|
49
|
+
const headers = {
|
|
50
|
+
apikey: auth.supabasePublishableKey,
|
|
51
|
+
"Content-Type": "application/json"
|
|
52
|
+
};
|
|
53
|
+
await jsonBody(await fetchFn(`${base}/auth/v1/otp`, {
|
|
54
|
+
method: "POST",
|
|
55
|
+
headers,
|
|
56
|
+
body: JSON.stringify({
|
|
57
|
+
email,
|
|
58
|
+
create_user: true
|
|
59
|
+
})
|
|
60
|
+
}), "Sending the sign-in code");
|
|
61
|
+
log.info(`Sent a sign-in code to ${email}.`);
|
|
62
|
+
const code = unwrapCancel(await p.text({ message: "Enter the code from your email" }), "Login cancelled").trim();
|
|
63
|
+
const session = await jsonBody(await fetchFn(`${base}/auth/v1/verify`, {
|
|
64
|
+
method: "POST",
|
|
65
|
+
headers,
|
|
66
|
+
body: JSON.stringify({
|
|
67
|
+
type: "email",
|
|
68
|
+
email,
|
|
69
|
+
token: code
|
|
70
|
+
})
|
|
71
|
+
}), "Verifying the code");
|
|
72
|
+
if (!session.access_token) throw new CliError("login_failed", "Verifying the code did not return a session.");
|
|
73
|
+
return session.access_token;
|
|
74
|
+
}
|
|
75
|
+
async function executeLogin(opts, deps = {}) {
|
|
76
|
+
const fetchFn = deps.fetchFn ?? globalThis.fetch;
|
|
77
|
+
requireTty();
|
|
78
|
+
const globalConfig = await readGlobalConfig();
|
|
79
|
+
const serverUrl = resolveServerUrl(opts.server, void 0, globalConfig.approvedServers ?? []);
|
|
80
|
+
if (opts.server) await approveServer(serverUrl);
|
|
81
|
+
const auth = await jsonBody(await fetchFn(`${serverUrl}/studio/auth`), "Reading the server's login configuration");
|
|
82
|
+
if (auth.mode === "none") throw new CliError("login_unavailable", "This server has no browser/email login configured.", "Set the ASSEMBLYAI_API_KEY environment variable, or run any platform command to be prompted for a key.");
|
|
83
|
+
const email = unwrapCancel(await p.text({ message: "Email address" }), "Login cancelled").trim();
|
|
84
|
+
const bearer = { Authorization: `Bearer ${auth.mode === "dev" ? mintDevToken(email) : await supabaseSession(auth, email, fetchFn)}` };
|
|
85
|
+
if (!(await jsonBody(await fetchFn(`${serverUrl}/studio/account`, { headers: bearer }), "Loading your account")).hasKey) {
|
|
86
|
+
const newKey = unwrapCancel(await p.password({ message: "Enter your AssemblyAI API key (stored with your account)" }), "Login cancelled").trim();
|
|
87
|
+
await jsonBody(await fetchFn(`${serverUrl}/studio/account/key`, {
|
|
88
|
+
method: "PUT",
|
|
89
|
+
headers: {
|
|
90
|
+
...bearer,
|
|
91
|
+
"Content-Type": "application/json"
|
|
92
|
+
},
|
|
93
|
+
body: JSON.stringify({ apiKey: newKey })
|
|
94
|
+
}), "Saving your API key");
|
|
95
|
+
}
|
|
96
|
+
const { apiKey } = await jsonBody(await fetchFn(`${serverUrl}/studio/account/key`, { headers: bearer }), "Fetching your API key");
|
|
97
|
+
const dir = getConfigDir();
|
|
98
|
+
await writeGlobalConfig(dir, {
|
|
99
|
+
...await readGlobalConfig(dir),
|
|
100
|
+
apiKey
|
|
101
|
+
});
|
|
102
|
+
log.success(`Signed in as ${email} — your API key is saved for future commands.`);
|
|
103
|
+
return ok({
|
|
104
|
+
email,
|
|
105
|
+
server: serverUrl
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
export { executeLogin };
|
package/dist/login.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `aai login` — email sign-in against the platform, ending with the
|
|
3
|
+
* account's AssemblyAI API key stored in the global config (the same slot
|
|
4
|
+
* `ensureApiKey` reads), so every other command is untouched by how the
|
|
5
|
+
* key was acquired.
|
|
6
|
+
*
|
|
7
|
+
* Flow (the CLI mirror of the browser studio's two gates):
|
|
8
|
+
* 1. `GET /studio/auth` names the login mode.
|
|
9
|
+
* - `supabase`: Supabase email OTP — `POST /auth/v1/otp` emails a
|
|
10
|
+
* one-time code, the user types it here, `POST /auth/v1/verify`
|
|
11
|
+
* returns the session. The magic LINK in the same email targets the
|
|
12
|
+
* browser; the CLI uses the code because a terminal has no redirect
|
|
13
|
+
* to land on. (The Supabase email template must include the
|
|
14
|
+
* `{{ .Token }}` code for this to work.)
|
|
15
|
+
* - `dev`: mint the same self-describing dev token the studio's local
|
|
16
|
+
* login mints — nothing is emailed anywhere.
|
|
17
|
+
* 2. `GET /studio/account` — when no key is on file yet, prompt for one
|
|
18
|
+
* and `PUT /studio/account/key` (the same mandatory onboarding step
|
|
19
|
+
* the browser shows after sign-in).
|
|
20
|
+
* 3. `GET /studio/account/key` — fetch the key and save it locally.
|
|
21
|
+
* Unlike the browser, the CLI needs the RAW key: `aai dev` runs the
|
|
22
|
+
* provider pipeline in-process on it.
|
|
23
|
+
*/
|
|
24
|
+
import { type CommandResult } from "./_output.ts";
|
|
25
|
+
export type LoginDeps = {
|
|
26
|
+
/** Test seam — never set outside tests. */
|
|
27
|
+
fetchFn?: typeof globalThis.fetch;
|
|
28
|
+
};
|
|
29
|
+
export declare function executeLogin(opts: {
|
|
30
|
+
server?: string | undefined;
|
|
31
|
+
}, deps?: LoginDeps): Promise<CommandResult<{
|
|
32
|
+
email: string;
|
|
33
|
+
server: string;
|
|
34
|
+
}>>;
|
package/dist/scaffold/CLAUDE.md
CHANGED
|
@@ -27,7 +27,9 @@ The fast loop: edit → `pnpm dev` (browser, talk to it) →
|
|
|
27
27
|
`node_modules/@alexkroman1/aai-cli/dist/templates/`. Read them directly;
|
|
28
28
|
`aai init --template <name>` scaffolds a fresh project from one. Closest
|
|
29
29
|
matches: `simple`, `pipeline-simple`, `web-researcher`, `solo-rpg`,
|
|
30
|
-
`pizza-ordering
|
|
30
|
+
`pizza-ordering`, `retail` (the most complex — 15 tools over a
|
|
31
|
+
relational store, with a `syncState`-driven UI). When reading SDK
|
|
32
|
+
types under
|
|
31
33
|
`node_modules/@alexkroman1/aai*/dist/`, note the built entry points
|
|
32
34
|
re-export with source specifiers (`"./sdk/constants.ts"`,
|
|
33
35
|
`"./components/button.tsx"`) — rewrite `.ts`/`.tsx` to `.d.ts` to find
|
|
@@ -77,6 +79,13 @@ export default agent({
|
|
|
77
79
|
systemPrompt?: string; // default: general voice assistant
|
|
78
80
|
// (`system` is an accepted alias)
|
|
79
81
|
greeting?: string; // default: "Hey there..."
|
|
82
|
+
voice?: string; // TTS voice for the default pipeline, e.g. "michael"
|
|
83
|
+
// (shorthand for tts: assemblyAITts({ voice });
|
|
84
|
+
// invalid with an explicit `tts` or with `s2s`)
|
|
85
|
+
stt?: SttProvider; // pipeline stage overrides — set any subset;
|
|
86
|
+
llm?: LlmProvider | string; // unset stages default to AssemblyAI
|
|
87
|
+
tts?: TtsProvider; // (llm also takes a model-id string)
|
|
88
|
+
s2s?: S2sProvider; // explicit opt-in to speech-to-speech mode
|
|
80
89
|
sttPrompt?: string; // STT guidance for jargon/acronyms
|
|
81
90
|
builtinTools?: BuiltinTool[]; // see built-in tools table
|
|
82
91
|
tools?: Record<string, ToolDef>;
|
|
@@ -97,55 +106,55 @@ export default agent({
|
|
|
97
106
|
});
|
|
98
107
|
```
|
|
99
108
|
|
|
100
|
-
>
|
|
101
|
-
>
|
|
102
|
-
> string: `"creator/model"` routes
|
|
103
|
-
> (`AI_GATEWAY_API_KEY`), a bare id through
|
|
104
|
-
> (`ASSEMBLYAI_API_KEY`).
|
|
109
|
+
> Unless `s2s` is set, the agent runs in **Pipeline mode** — see the section
|
|
110
|
+
> below. Declare any subset of `stt`/`llm`/`tts`; unset stages default to
|
|
111
|
+
> AssemblyAI. `llm` also accepts a model-id string: `"creator/model"` routes
|
|
112
|
+
> through the Vercel AI Gateway (`AI_GATEWAY_API_KEY`), a bare id through
|
|
113
|
+
> the AssemblyAI LLM Gateway (`ASSEMBLYAI_API_KEY`).
|
|
105
114
|
|
|
106
115
|
Minimal agent — a cascaded pipeline, which is what you should build unless
|
|
107
116
|
the user asks for the speech-to-speech API:
|
|
108
117
|
|
|
109
118
|
```ts
|
|
110
|
-
import { agent
|
|
119
|
+
import { agent } from "@alexkroman1/aai";
|
|
111
120
|
|
|
112
121
|
export default agent({
|
|
113
122
|
name: "My Agent",
|
|
114
|
-
...assemblyAIPipeline(),
|
|
115
123
|
});
|
|
116
124
|
```
|
|
117
125
|
|
|
118
|
-
|
|
119
|
-
the one key a published agent is guaranteed to have.
|
|
120
|
-
|
|
126
|
+
No provider fields means the default all-AssemblyAI pipeline: all three
|
|
127
|
+
stages bill to the one key a published agent is guaranteed to have. Pick
|
|
128
|
+
its voice with the `voice` field — this is the normal way to choose a
|
|
129
|
+
voice:
|
|
121
130
|
|
|
122
131
|
```ts
|
|
123
|
-
import { agent
|
|
124
|
-
import { assemblyAITts } from "@alexkroman1/aai/tts";
|
|
132
|
+
import { agent } from "@alexkroman1/aai";
|
|
125
133
|
|
|
126
134
|
export default agent({
|
|
127
135
|
name: "My Agent",
|
|
128
|
-
|
|
129
|
-
tts: assemblyAITts({ voice: "paul" }),
|
|
136
|
+
voice: "paul",
|
|
130
137
|
});
|
|
131
138
|
```
|
|
132
139
|
|
|
133
|
-
|
|
134
|
-
as a plain
|
|
140
|
+
Swap a single stage by declaring just that field — everything you leave
|
|
141
|
+
unset stays on the default. `llm` accepts the gateway model id as a plain
|
|
142
|
+
string:
|
|
135
143
|
|
|
136
144
|
```ts
|
|
137
|
-
import { agent
|
|
145
|
+
import { agent } from "@alexkroman1/aai";
|
|
138
146
|
|
|
139
147
|
export default agent({
|
|
140
148
|
name: "My Agent",
|
|
141
|
-
...assemblyAIPipeline(),
|
|
142
149
|
llm: "claude-sonnet-4-6",
|
|
143
150
|
});
|
|
144
151
|
```
|
|
145
152
|
|
|
146
|
-
`
|
|
147
|
-
|
|
148
|
-
|
|
153
|
+
`assemblyAIPipeline()` (from `@alexkroman1/aai`) is the explicit spelling of
|
|
154
|
+
the same default — spread it (`...assemblyAIPipeline({ region: "eu" })`) when
|
|
155
|
+
you want the three stages visible in the config or EU data residency across
|
|
156
|
+
STT and the LLM gateway. Speech-to-speech (S2S) mode is an explicit opt-in
|
|
157
|
+
via the `s2s` field — see below.
|
|
149
158
|
|
|
150
159
|
System prompt from file:
|
|
151
160
|
|
|
@@ -165,8 +174,8 @@ explicit the modern spelling is `with { type: "json" }`, but plain is fine.
|
|
|
165
174
|
## Pipeline mode
|
|
166
175
|
|
|
167
176
|
Pipeline mode is the default: omitting `stt`/`llm`/`tts` (and `s2s`) gives
|
|
168
|
-
you the all-AssemblyAI pipeline,
|
|
169
|
-
|
|
177
|
+
you the all-AssemblyAI pipeline, and any stage you do declare replaces just
|
|
178
|
+
that stage — the rest keep the default.
|
|
170
179
|
|
|
171
180
|
**S2S mode is an explicit opt-in.** Setting `s2s: assemblyAIS2s()` (imported
|
|
172
181
|
from `@alexkroman1/aai`, next to `agent()`) selects AssemblyAI's
|
|
@@ -194,14 +203,16 @@ providers when:
|
|
|
194
203
|
- you want a specific LLM (Anthropic, OpenAI, Gemini, Mistral, xAI, Groq,
|
|
195
204
|
hundreds of models via OpenRouter, or 25+ models via the AssemblyAI
|
|
196
205
|
LLM Gateway)
|
|
197
|
-
- you want a specific STT model or TTS
|
|
206
|
+
- you want a specific STT model, or a non-AssemblyAI TTS provider (for the
|
|
207
|
+
default pipeline's voice, use the `voice` field instead)
|
|
198
208
|
- you need to swap providers without changing agent code
|
|
199
209
|
|
|
200
|
-
**The rule:**
|
|
201
|
-
`
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
210
|
+
**The rule:** declare only the stages you're changing — any subset of
|
|
211
|
+
`stt`, `llm`, `tts`; each unset stage runs on the AssemblyAI default.
|
|
212
|
+
Combining `s2s` with any pipeline provider or pipeline-only tuning field is
|
|
213
|
+
a compile error naming the rule, as is `voice` alongside an explicit `tts`
|
|
214
|
+
descriptor (the descriptor owns its own voice). A raw config that skips
|
|
215
|
+
`agent()` is still checked at parse time.
|
|
205
216
|
|
|
206
217
|
```ts
|
|
207
218
|
import { agent } from "@alexkroman1/aai";
|
|
@@ -297,37 +308,31 @@ hundreds of models addressed as `"creator/model"`, e.g.
|
|
|
297
308
|
`assemblyAILlm` routes through the [AssemblyAI LLM
|
|
298
309
|
Gateway](https://www.assemblyai.com/docs/llm-gateway) — an
|
|
299
310
|
OpenAI-compatible endpoint fronting 25+ models (Claude, GPT, Gemini,
|
|
300
|
-
etc.) with the same API key as AssemblyAI STT
|
|
311
|
+
etc.) with the same API key as AssemblyAI STT. A bare model-id string on
|
|
312
|
+
`llm` is shorthand for it, and unset stages keep the AssemblyAI default:
|
|
301
313
|
|
|
302
314
|
```ts
|
|
303
315
|
import { agent } from "@alexkroman1/aai";
|
|
304
|
-
import { assemblyAILlm } from "@alexkroman1/aai/llm";
|
|
305
|
-
import { assemblyAIStt } from "@alexkroman1/aai/stt";
|
|
306
|
-
import { cartesia } from "@alexkroman1/aai/tts";
|
|
307
316
|
|
|
308
317
|
export default agent({
|
|
309
318
|
name: "My Agent",
|
|
310
|
-
|
|
311
|
-
llm: assemblyAILlm({ model: "claude-sonnet-4-6" }),
|
|
312
|
-
tts: cartesia(),
|
|
319
|
+
llm: "claude-sonnet-4-6",
|
|
313
320
|
});
|
|
314
321
|
```
|
|
315
322
|
|
|
316
|
-
|
|
323
|
+
`assemblyAILlm({ model, region: "eu" })` is the explicit form; `region`
|
|
324
|
+
selects EU data residency.
|
|
317
325
|
|
|
318
|
-
|
|
326
|
+
Mixing providers works the same way — declare the stages you're changing:
|
|
319
327
|
|
|
320
328
|
```ts
|
|
321
329
|
import { agent } from "@alexkroman1/aai";
|
|
322
|
-
import {
|
|
323
|
-
import { assemblyAIStt } from "@alexkroman1/aai/stt";
|
|
324
|
-
import { assemblyAITts } from "@alexkroman1/aai/tts";
|
|
330
|
+
import { cartesia } from "@alexkroman1/aai/tts";
|
|
325
331
|
|
|
326
332
|
export default agent({
|
|
327
333
|
name: "My Agent",
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
tts: assemblyAITts({ voice: "jane" }),
|
|
334
|
+
llm: "claude-sonnet-4-6",
|
|
335
|
+
tts: cartesia(),
|
|
331
336
|
});
|
|
332
337
|
```
|
|
333
338
|
|
|
@@ -343,7 +348,9 @@ Bare calls (`assemblyAITts()`, `cartesia()`, `rime()`) use the defaults.
|
|
|
343
348
|
Override with `{ voice, model, language }`.
|
|
344
349
|
|
|
345
350
|
**AssemblyAI TTS** shares `ASSEMBLYAI_API_KEY` with AssemblyAI STT and the
|
|
346
|
-
LLM Gateway, so an all-AssemblyAI pipeline needs exactly one secret.
|
|
351
|
+
LLM Gateway, so an all-AssemblyAI pipeline needs exactly one secret. On the
|
|
352
|
+
default pipeline, `agent({ voice: "michael" })` is the shorthand for
|
|
353
|
+
`tts: assemblyAITts({ voice: "michael" })` — same catalog, same rules. Each
|
|
347
354
|
voice speaks one language, and this is the whole catalog — **a voice not on
|
|
348
355
|
this list is rejected after the socket opens, which leaves the agent
|
|
349
356
|
connected, "ready", and permanently silent**, so pick one from here rather
|
|
@@ -721,7 +728,7 @@ import "@alexkroman1/aai-ui/styles.css";
|
|
|
721
728
|
import { client, useSession } from "@alexkroman1/aai-ui";
|
|
722
729
|
|
|
723
730
|
function MyApp() {
|
|
724
|
-
const { messages, userTranscript, started, running, start, toggle,
|
|
731
|
+
const { messages, userTranscript, started, running, start, toggle, end } =
|
|
725
732
|
useSession();
|
|
726
733
|
return (
|
|
727
734
|
<div>
|
|
@@ -732,7 +739,7 @@ function MyApp() {
|
|
|
732
739
|
) : (
|
|
733
740
|
<>
|
|
734
741
|
<button onClick={toggle}>{running ? "Stop" : "Resume"}</button>
|
|
735
|
-
<button onClick={
|
|
742
|
+
<button onClick={end}>End</button>
|
|
736
743
|
</>
|
|
737
744
|
)}
|
|
738
745
|
</div>
|
|
@@ -774,8 +781,16 @@ to put it in.
|
|
|
774
781
|
| `started` | `boolean` | Whether session started |
|
|
775
782
|
| `running` | `boolean` | Whether session active |
|
|
776
783
|
|
|
777
|
-
Methods: `start()`, `toggle()`, `
|
|
778
|
-
`resetState()`.
|
|
784
|
+
Methods: `start()`, `toggle()`, `end()`, `reset()`, `cancel()`,
|
|
785
|
+
`disconnect()`, `resetState()`.
|
|
786
|
+
|
|
787
|
+
- `end()` hangs up: it flips `started` back to `false` (a start-screen UI
|
|
788
|
+
shows its Start control again) and the next `start()` is a brand-new
|
|
789
|
+
session — fresh per-session tool state, greeting included. Use it for
|
|
790
|
+
End/Hang up/New game buttons.
|
|
791
|
+
- `reset()` clears the conversation but keeps the call live (`started`
|
|
792
|
+
stays `true`) — the control stays on Stop/Resume, and per-session tool
|
|
793
|
+
state survives. Use it for a "clear chat" control, not for ending.
|
|
779
794
|
|
|
780
795
|
## UI hooks
|
|
781
796
|
|
|
@@ -949,9 +964,10 @@ Common mistakes when working in aai projects:
|
|
|
949
964
|
- **Filter large API responses before returning them from tools.** Return
|
|
950
965
|
values are injected into LLM context. Truncate, summarize, or extract
|
|
951
966
|
only what the model needs.
|
|
952
|
-
- **
|
|
953
|
-
|
|
954
|
-
|
|
967
|
+
- **Declare only the pipeline stages you're changing.** Unset stages of
|
|
968
|
+
`stt` / `llm` / `tts` default to AssemblyAI (omit all three for the full
|
|
969
|
+
default pipeline; `voice` picks its TTS voice). S2S needs an explicit
|
|
970
|
+
`s2s: assemblyAIS2s()` and takes no pipeline fields.
|
|
955
971
|
- **Never hardcode secrets.** Use `ctx.env.MY_KEY`. `.env` for local dev,
|
|
956
972
|
`aai secret put` for production.
|
|
957
973
|
- **Don't use `useEffect` + `toolCalls` to derive state.** Use
|