@alexkroman1/aai-cli 13.2.0 → 14.0.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/bin.mjs +1 -1
- package/dist/{_agent-BTwZJu4P.mjs → _agent-BzUeqOdj.mjs} +33 -10
- package/dist/_agent.d.ts +23 -0
- package/dist/{_bundler-C31rlcYz.mjs → _bundler-DolUCMxu.mjs} +9 -8
- package/dist/_cli-common.d.ts +45 -6
- package/dist/{_config-CmJOFsAP.mjs → _config-DYzC6WMD.mjs} +7 -4
- package/dist/_config.d.ts +25 -1
- package/dist/_dev-env.d.ts +1 -1
- package/dist/{_dev-server-DWvkNoIS.mjs → _dev-server-CSMqF8PN.mjs} +28 -30
- package/dist/_dev-vite-config.d.ts +3 -3
- package/dist/_e2e-test-utils.d.ts +15 -7
- package/dist/{_init-BURbdHNM.mjs → _init-CQ8idAwo.mjs} +64 -30
- package/dist/{_output-CKkmWs7i.mjs → _output-DBP9Op_d.mjs} +43 -3
- package/dist/_output.d.ts +24 -7
- package/dist/{_preflight-Co1G9Jww.mjs → _preflight-CcAR577V.mjs} +3 -3
- package/dist/_resource-commands.d.ts +9 -9
- package/dist/{_server-common-6e0QI4mq.mjs → _server-common-vILJp3it.mjs} +3 -3
- package/dist/{_slug-api-CfGQq1Tt.mjs → _slug-api-CGvc2YSQ.mjs} +19 -10
- package/dist/_slug-api.d.ts +19 -0
- package/dist/{_studio-B1waXMuR.mjs → _studio-DnR_BqFp.mjs} +9 -5
- package/dist/_studio-commands.d.ts +13 -13
- package/dist/{_templates-0Zr2z6yA.mjs → _templates-CK4oKoeX.mjs} +3 -3
- package/dist/{_typecheck-gate-BXvUNnfs.mjs → _typecheck-gate-U3jcil8L.mjs} +5 -4
- package/dist/_typecheck-gate.d.ts +3 -1
- package/dist/{_ui-u7T4YooX.mjs → _ui-DCt4qZrk.mjs} +1 -1
- package/dist/{_utils-B8QmtFhK.mjs → _utils-D5JGcjiW.mjs} +44 -2
- package/dist/_utils.d.ts +35 -0
- package/dist/{_vite-env-D9Qj6KPX.mjs → _vite-env-kejF0p2L.mjs} +1 -1
- package/dist/{_workflow-determinism-f9J-5Zlj.mjs → _workflow-determinism-CvpqMpdh.mjs} +19 -27
- package/dist/_workflow-determinism.d.ts +3 -3
- package/dist/build-Mxk8gWvX.mjs +108 -0
- package/dist/build.d.ts +5 -0
- package/dist/cli.mjs +172 -116
- package/dist/{client-bundler-j5c46x0G.mjs → client-bundler-BJgREAh6.mjs} +4 -4
- package/dist/client-bundler.mjs +1 -1
- package/dist/{delete-X_CRgCf6.mjs → delete-BdFnbnVR.mjs} +6 -6
- package/dist/{deploy-BzZ-r4iM.mjs → deploy-uAJ4NukN.mjs} +14 -14
- package/dist/{dev-BDykFvcc.mjs → dev-DApPSaE_.mjs} +5 -5
- package/dist/{eval-0qEOdm0l.mjs → eval-BK47A_K5.mjs} +27 -11
- package/dist/eval.d.ts +2 -0
- package/dist/{init-BtJI306C.mjs → init-DukDxECd.mjs} +13 -21
- package/dist/{login-DeUETobb.mjs → login-DaLrPOFg.mjs} +6 -8
- package/dist/{logs-hWnJ66Bl.mjs → logs-7BRwq0cG.mjs} +22 -15
- package/dist/project-config.mjs +1 -1
- package/dist/scaffold/.env.example +17 -1
- package/dist/scaffold/CLAUDE.md +84 -56
- package/dist/scaffold/package.json +6 -5
- package/dist/scaffold/server.mjs +129 -25
- package/dist/secret-Cjp912yF.mjs +162 -0
- package/dist/secret.d.ts +48 -13
- package/dist/{studio-BxAS_FQQ.mjs → studio-CpHlNHUZ.mjs} +15 -17
- package/dist/templates/briefing-desk/agent.test.ts +7 -3
- package/dist/templates/call-audit/agent.test.ts +25 -9
- package/dist/templates/call-audit/agent.ts +2 -2
- package/dist/templates/call-audit/client.tsx +40 -58
- package/dist/templates/call-audit/workflows/audit.ts +7 -5
- package/dist/templates/call-audit/workflows/ingest.ts +7 -7
- package/dist/templates/call-audit/workflows/media.ts +12 -37
- package/dist/templates/call-audit/workflows/summarize.ts +8 -8
- package/dist/templates/call-audit/workflows/sync-api.ts +3 -3
- package/dist/templates/dispatch-center/agent.test.ts +7 -7
- package/dist/templates/dispatch-center/client.tsx +53 -61
- package/dist/templates/dispatch-center/shared.ts +0 -8
- package/dist/templates/dispatch-center/tools/ops_dashboard.ts +4 -1
- package/dist/templates/embedded-assets/agent.eval.test.ts +4 -2
- package/dist/templates/embedded-assets/agent.test.ts +7 -1
- package/dist/templates/health-assistant/agent.eval.test.ts +4 -2
- package/dist/templates/health-assistant/agent.test.ts +8 -4
- package/dist/templates/infocom-adventure/client.tsx +40 -36
- package/dist/templates/link-digest/agent.test.ts +10 -4
- package/dist/templates/link-digest/agent.ts +2 -2
- package/dist/templates/link-digest/client.tsx +42 -36
- package/dist/templates/link-digest/workflows/digest.ts +13 -16
- package/dist/templates/night-owl/agent.eval.test.ts +4 -2
- package/dist/templates/night-owl/agent.test.ts +6 -1
- package/dist/templates/night-owl/client.tsx +8 -2
- package/dist/templates/pipeline-simple/agent.test.ts +33 -16
- package/dist/templates/pizza-ordering/client.tsx +5 -4
- package/dist/templates/pizza-ordering/shared.ts +3 -8
- package/dist/templates/pizza-ordering/tools/add_pizza.ts +3 -2
- package/dist/templates/pizza-ordering/tools/place_order.ts +3 -2
- package/dist/templates/pizza-ordering/tools/remove_pizza.ts +3 -2
- package/dist/templates/pizza-ordering/tools/update_pizza.ts +3 -2
- package/dist/templates/pizza-ordering/tools/view_order.ts +3 -2
- package/dist/templates/plan-and-execute/agent.test.ts +10 -5
- package/dist/templates/plan-and-execute/client.tsx +2 -2
- package/dist/templates/podcast-digest/agent.test.ts +33 -14
- package/dist/templates/podcast-digest/client.tsx +27 -36
- package/dist/templates/podcast-digest/workflows/digest.ts +21 -19
- package/dist/templates/podcast-digest/workflows/feeds.ts +61 -33
- package/dist/templates/podcast-digest/workflows/slack.ts +7 -7
- package/dist/templates/recap-workflow/agent.eval.test.ts +21 -13
- package/dist/templates/recap-workflow/agent.test.ts +49 -32
- package/dist/templates/recap-workflow/workflows/recap.ts +24 -28
- package/dist/templates/redline/agent.eval.test.ts +5 -8
- package/dist/templates/redline/agent.test.ts +8 -2
- package/dist/templates/redline/client.tsx +47 -48
- package/dist/templates/redline/workflows/redline.ts +17 -17
- package/dist/templates/research-workflow/agent.eval.test.ts +5 -5
- package/dist/templates/research-workflow/agent.test.ts +17 -8
- package/dist/templates/research-workflow/workflows/research.ts +28 -30
- package/dist/templates/retail/agent.test.ts +17 -15
- package/dist/templates/retail/cancel.ts +7 -6
- package/dist/templates/retail/client.tsx +39 -58
- package/dist/templates/retail/payment.ts +4 -3
- package/dist/templates/retail/pending.test.ts +19 -19
- package/dist/templates/retail/registry.test.ts +99 -58
- package/dist/templates/retail/store.test.ts +5 -5
- package/dist/templates/retail/swap.ts +8 -11
- package/dist/templates/simple/agent.test.ts +39 -6
- package/dist/templates/solo-rpg/agent.eval.test.ts +2 -16
- package/dist/templates/solo-rpg/agent.test.ts +12 -12
- package/dist/templates/solo-rpg/client.tsx +3 -3
- package/dist/templates/spoken-summary/agent.test.ts +21 -12
- package/dist/templates/spoken-summary/agent.ts +3 -3
- package/dist/templates/spoken-summary/client.tsx +36 -47
- package/dist/templates/spoken-summary/workflows/summarize.ts +11 -11
- package/dist/templates/spoken-summary/workflows/transcribe.ts +14 -14
- package/dist/templates/support-line/client.tsx +4 -3
- package/dist/templates/support-line/prompts.ts +0 -2
- package/dist/templates/transcription-workflow/agent.test.ts +36 -20
- package/dist/templates/transcription-workflow/agent.ts +2 -2
- package/dist/templates/transcription-workflow/client.tsx +34 -40
- package/dist/templates/transcription-workflow/recover.ts +1 -1
- package/dist/templates/transcription-workflow/run-panel.tsx +34 -14
- package/dist/templates/transcription-workflow/total-latency.tsx +46 -20
- package/dist/templates/transcription-workflow/workflows/batch.ts +15 -15
- package/dist/templates/transcription-workflow/workflows/downsample.ts +1 -1
- package/dist/templates/transcription-workflow/workflows/normalize.ts +15 -19
- package/dist/templates/transcription-workflow/workflows/stream.ts +12 -12
- package/dist/templates/transcription-workflow/workflows/sync-api.ts +3 -3
- package/dist/templates/transcription-workflow/workflows/transcribe.ts +23 -34
- package/dist/templates/travel-concierge/agent.eval.test.ts +2 -2
- package/dist/templates/travel-concierge/agent.test.ts +15 -7
- package/dist/templates/travel-concierge/client.tsx +2 -2
- package/dist/templates/travel-concierge/shared.ts +10 -14
- package/dist/templates/travel-concierge/tools/lookup_booking.ts +4 -3
- package/dist/templates/travel-concierge/tools/search_car_rentals.ts +3 -2
- package/dist/templates/travel-concierge/tools/search_excursions.ts +3 -2
- package/dist/templates/travel-concierge/tools/search_flights.ts +3 -2
- package/dist/templates/travel-concierge/tools/search_hotels.ts +3 -2
- package/dist/test-D032V2cA.mjs +242 -0
- package/dist/test.d.ts +97 -31
- package/dist/typecheck.mjs +2 -2
- package/dist/worker-bundler.mjs +3 -3
- package/dist/{workflow-BBu5oEDw.mjs → workflow-CVioyO3V.mjs} +8 -7
- package/package.json +10 -10
- package/dist/bin.d.mts +0 -2
- package/dist/build-BLlLOrng.mjs +0 -66
- package/dist/secret-B-3T5tvr.mjs +0 -61
- package/dist/test-CiLab-AA.mjs +0 -182
- package/dist/tsdown.config.d.ts +0 -2
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { r as isDevMode, t as getMonorepoRoot } from "./_agent-
|
|
4
|
-
import { REPO_URL, downloadAndMergeTemplate } from "./_templates-
|
|
2
|
+
import { d as readJson, g as writeJson, i as compareCodeUnits, u as isEexist } from "./_utils-D5JGcjiW.mjs";
|
|
3
|
+
import { r as isDevMode, t as getMonorepoRoot } from "./_agent-BzUeqOdj.mjs";
|
|
4
|
+
import { REPO_URL, downloadAndMergeTemplate } from "./_templates-CK4oKoeX.mjs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import fs from "node:fs/promises";
|
|
7
|
-
//#region _init.ts
|
|
7
|
+
//#region src/_init.ts
|
|
8
|
+
/**
|
|
9
|
+
* The README every scaffolded project gets, and the first thing a new author
|
|
10
|
+
* reads. Three things in it are corrections rather than prose:
|
|
11
|
+
*
|
|
12
|
+
* - **`npm run dev`, never a bare `aai dev`.** The CLI is a devDependency, so
|
|
13
|
+
* after `npm install` the binary is in `node_modules/.bin` and NOT on
|
|
14
|
+
* `PATH`. The old quickstart said `aai dev`, which fails with
|
|
15
|
+
* `command not found` for everyone who has not installed the CLI globally.
|
|
16
|
+
* - **The key `aai dev` needs is named, with the two LOCAL ways first.** No
|
|
17
|
+
* step of local development needs a platform account; a twenty-persona DX
|
|
18
|
+
* audit read the account-shaped failure it used to get and concluded the
|
|
19
|
+
* opposite, then stopped using the loop this file documents.
|
|
20
|
+
* - **`aai login` appears where it is actually required.** It was in no
|
|
21
|
+
* user-facing doc at all, while the quickstart's own publish step needs it.
|
|
22
|
+
*/
|
|
8
23
|
function readmeContent(slug) {
|
|
9
24
|
return `# ${slug}
|
|
10
25
|
|
|
@@ -14,8 +29,41 @@ A voice agent built with [aai](${REPO_URL}).
|
|
|
14
29
|
|
|
15
30
|
\`\`\`sh
|
|
16
31
|
npm install # Install dependencies
|
|
17
|
-
|
|
18
|
-
|
|
32
|
+
npm run dev # Run locally on http://localhost:3000 (opens browser)
|
|
33
|
+
\`\`\`
|
|
34
|
+
|
|
35
|
+
The \`aai\` CLI is a devDependency of this project, so it lives in
|
|
36
|
+
\`node_modules/.bin\` rather than on your \`PATH\`. Run it through npm
|
|
37
|
+
(\`npm run dev\`, \`npm test\`, \`npm run build\`) or with \`npx aai <command>\`.
|
|
38
|
+
|
|
39
|
+
### The one key local development needs
|
|
40
|
+
|
|
41
|
+
The default pipeline (speech-to-text → LLM → text-to-speech) runs on a single
|
|
42
|
+
AssemblyAI key. **Any one of these is enough, and the first two need no aai
|
|
43
|
+
account** — nothing about running this agent locally is gated on one:
|
|
44
|
+
|
|
45
|
+
1. Put \`ASSEMBLYAI_API_KEY=<your key>\` in \`.env\` (this project's \`.env.example\`
|
|
46
|
+
documents it, and it is the same file \`aai publish\` uploads as secrets).
|
|
47
|
+
2. Or export it in your shell: \`export ASSEMBLYAI_API_KEY=<your key>\`.
|
|
48
|
+
3. Or run \`npx aai login\`, and \`aai dev\` will use your account's key.
|
|
49
|
+
|
|
50
|
+
Get a key at <https://www.assemblyai.com/dashboard>.
|
|
51
|
+
|
|
52
|
+
## Publishing
|
|
53
|
+
|
|
54
|
+
Publishing (and the studio it syncs to) is the one part that does need an
|
|
55
|
+
account:
|
|
56
|
+
|
|
57
|
+
\`\`\`sh
|
|
58
|
+
npx aai login # Link your account — once per machine
|
|
59
|
+
npm run publish:agent # Publish to production (and sync to the studio)
|
|
60
|
+
\`\`\`
|
|
61
|
+
|
|
62
|
+
You can also run this agent as a plain Node server, with no aai account and
|
|
63
|
+
nothing managed:
|
|
64
|
+
|
|
65
|
+
\`\`\`sh
|
|
66
|
+
npm start # Builds, then serves on http://127.0.0.1:3000
|
|
19
67
|
\`\`\`
|
|
20
68
|
|
|
21
69
|
## Secrets
|
|
@@ -32,9 +80,9 @@ MY_API_KEY=secret-value
|
|
|
32
80
|
**Production** — set secrets on the server:
|
|
33
81
|
|
|
34
82
|
\`\`\`sh
|
|
35
|
-
aai secret put MY_KEY # Set a secret (prompts for value)
|
|
36
|
-
aai secret list # List secret names
|
|
37
|
-
aai secret delete MY_KEY # Remove a secret
|
|
83
|
+
npx aai secret put MY_KEY # Set a secret (prompts for value)
|
|
84
|
+
npx aai secret list # List secret names
|
|
85
|
+
npx aai secret delete MY_KEY # Remove a secret
|
|
38
86
|
\`\`\`
|
|
39
87
|
|
|
40
88
|
`;
|
|
@@ -71,17 +119,6 @@ async function patchPackageJsonForWorkspace(targetDir) {
|
|
|
71
119
|
await pinSharedDeps(targetDir, packagesDir, pkgJson);
|
|
72
120
|
}
|
|
73
121
|
/**
|
|
74
|
-
* Code-unit ordering, never `localeCompare`.
|
|
75
|
-
*
|
|
76
|
-
* With no explicit locale that answers to the runtime's ICU default, so the same
|
|
77
|
-
* project would scaffold a differently-ordered file on a different machine —
|
|
78
|
-
* which is the reason the repo's own generated artifacts sort this way too.
|
|
79
|
-
*/
|
|
80
|
-
function compareNames(a, b) {
|
|
81
|
-
if (a === b) return 0;
|
|
82
|
-
return a < b ? -1 : 1;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
122
|
* Pin every THIRD-PARTY dependency this project shares with a linked workspace
|
|
86
123
|
* package to the copy that workspace package resolved.
|
|
87
124
|
*
|
|
@@ -114,17 +151,14 @@ async function pinSharedDeps(targetDir, packagesDir, pkgJson) {
|
|
|
114
151
|
...pkgJson.devDependencies
|
|
115
152
|
};
|
|
116
153
|
const linked = Object.entries(WORKSPACE_PKG_DIRS).filter(([name]) => name in declared).map(([, dir]) => dir);
|
|
117
|
-
const
|
|
118
|
-
for (const name of Object.keys(declared)) {
|
|
119
|
-
if (name in WORKSPACE_PKG_DIRS) continue;
|
|
154
|
+
const resolved = await Promise.all(Object.keys(declared).filter((name) => !(name in WORKSPACE_PKG_DIRS)).map(async (name) => {
|
|
120
155
|
for (const dir of linked) {
|
|
121
156
|
const manifest = await readJson(path.join(packagesDir, dir, "node_modules", name, "package.json"));
|
|
122
|
-
if (typeof manifest?.version === "string")
|
|
123
|
-
pins.set(name, manifest.version);
|
|
124
|
-
break;
|
|
125
|
-
}
|
|
157
|
+
if (typeof manifest?.version === "string") return [name, manifest.version];
|
|
126
158
|
}
|
|
127
|
-
|
|
159
|
+
return null;
|
|
160
|
+
}));
|
|
161
|
+
const pins = new Map(resolved.filter((entry) => entry !== null));
|
|
128
162
|
if (pins.size === 0) return;
|
|
129
163
|
const file = path.join(targetDir, "pnpm-workspace.yaml");
|
|
130
164
|
let existing;
|
|
@@ -133,8 +167,8 @@ async function pinSharedDeps(targetDir, packagesDir, pkgJson) {
|
|
|
133
167
|
} catch {
|
|
134
168
|
return;
|
|
135
169
|
}
|
|
136
|
-
const block = [...pins].sort(([a], [b]) =>
|
|
137
|
-
await fs.writeFile(file, `${existing.trimEnd()}\n\n# Added by \`aai init\` in DEV MODE only — see pinSharedDeps in\n# packages/aai-cli/_init.ts. The @alexkroman1/* packages above are LINKED to
|
|
170
|
+
const block = [...pins].sort(([a], [b]) => compareCodeUnits(a, b)).map(([name, version]) => ` "${name}": "${version}"`).join("\n");
|
|
171
|
+
await fs.writeFile(file, `${existing.trimEnd()}\n\n# Added by \`aai init\` in DEV MODE only — see pinSharedDeps in\n# packages/aai-cli/src/_init.ts. The @alexkroman1/* packages above are LINKED to
|
|
138
172
|
# this checkout, so anything they and this project both depend on has to be
|
|
139
173
|
# ONE copy: two copies of xstate are two incompatible sets of types, and the
|
|
140
174
|
# typecheck gate refuses the deploy.\noverrides:\n${block}\n`);
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
import { stripVTControlCharacters } from "node:util";
|
|
3
|
+
//#region src/_output.ts
|
|
4
|
+
/**
|
|
5
|
+
* Structured output support for CLI commands.
|
|
6
|
+
*
|
|
7
|
+
* In JSON mode (non-TTY or --json), commands emit exactly one JSON line to
|
|
8
|
+
* stdout. In human mode (TTY, default), commands use @clack/prompts as before.
|
|
9
|
+
*/
|
|
3
10
|
/**
|
|
4
11
|
* Determine output mode from CLI flags and TTY state.
|
|
5
12
|
*
|
|
@@ -11,7 +18,39 @@ function getOutputMode(args, isTTY = Boolean(process.stdout.isTTY)) {
|
|
|
11
18
|
return isTTY ? "human" : "json";
|
|
12
19
|
}
|
|
13
20
|
/**
|
|
14
|
-
*
|
|
21
|
+
* A terminal escape sequence AFTER `JSON.stringify`, which escapes the ESC
|
|
22
|
+
* byte as the six characters `\u001b`. Needed alongside the raw form because a
|
|
23
|
+
* caller stringifies a whole result and hands the line here — see
|
|
24
|
+
* {@link writeLine} — and no builtin reads that spelling.
|
|
25
|
+
*/
|
|
26
|
+
const ANSI_JSON = /\\u001[bB]\[[0-?]*[ -/]*[@-~]/g;
|
|
27
|
+
/**
|
|
28
|
+
* Remove ANSI escape sequences, in raw or JSON-escaped form.
|
|
29
|
+
*
|
|
30
|
+
* A bundler colours its own diagnostics unconditionally, so `aai build`'s
|
|
31
|
+
* failure reached the JSON envelope as per-character SGR pairs — illegible in a
|
|
32
|
+
* CI log and meaningless to a `jq` consumer, which is the audience JSON mode
|
|
33
|
+
* has.
|
|
34
|
+
*
|
|
35
|
+
* The raw half is `node:util`'s, not a local CSI regex. A/B'd on Node 26: the
|
|
36
|
+
* two agree on every SGR pair and cursor move the hand-built pattern was
|
|
37
|
+
* written for, and the builtin ALSO strips OSC-8 hyperlinks (`ESC ] 8 ;; url`)
|
|
38
|
+
* and charset selects (`ESC ( B`), which that pattern left in the envelope — so
|
|
39
|
+
* its claim to cover what a diagnostic arrives wearing was the incomplete part.
|
|
40
|
+
* The JSON-escaped half has no builtin and stays.
|
|
41
|
+
*/
|
|
42
|
+
function stripAnsi(text) {
|
|
43
|
+
return stripVTControlCharacters(text).replace(ANSI_JSON, "");
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Write a machine-readable line to stdout, resolving only once it has been
|
|
47
|
+
* flushed.
|
|
48
|
+
*
|
|
49
|
+
* ANSI escapes are stripped on the way out. This function carries JSON mode's
|
|
50
|
+
* one result line and nothing else — human output goes through `log.*` in
|
|
51
|
+
* `_ui.ts`, which is where colour belongs — so stripping here is what keeps
|
|
52
|
+
* the envelope clean no matter which module built the string inside it, and
|
|
53
|
+
* leaves a TTY's coloured diagnostic intact.
|
|
15
54
|
*
|
|
16
55
|
* Resolves (never rejects) even when the write fails: the common failure is
|
|
17
56
|
* EPIPE — the consumer closed the pipe (`aai … --json | head -1`) — and there
|
|
@@ -19,8 +58,9 @@ function getOutputMode(args, isTTY = Boolean(process.stdout.isTTY)) {
|
|
|
19
58
|
* Stream-level `'error'` events are handled by {@link installStdoutGuard}.
|
|
20
59
|
*/
|
|
21
60
|
function writeLine(line) {
|
|
61
|
+
const clean = stripAnsi(line);
|
|
22
62
|
return new Promise((resolve) => {
|
|
23
|
-
process.stdout.write(
|
|
63
|
+
process.stdout.write(clean, () => resolve());
|
|
24
64
|
});
|
|
25
65
|
}
|
|
26
66
|
/**
|
package/dist/_output.d.ts
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Structured output support for CLI commands.
|
|
3
|
-
*
|
|
4
|
-
* In JSON mode (non-TTY or --json), commands emit exactly one JSON line to
|
|
5
|
-
* stdout. In human mode (TTY, default), commands use @clack/prompts as before.
|
|
6
|
-
*/
|
|
7
1
|
export type OutputMode = "json" | "human";
|
|
8
2
|
export type CommandResult<T> = {
|
|
9
3
|
ok: true;
|
|
@@ -23,7 +17,30 @@ export declare function getOutputMode(args: {
|
|
|
23
17
|
json?: boolean | undefined;
|
|
24
18
|
}, isTTY?: boolean): OutputMode;
|
|
25
19
|
/**
|
|
26
|
-
*
|
|
20
|
+
* Remove ANSI escape sequences, in raw or JSON-escaped form.
|
|
21
|
+
*
|
|
22
|
+
* A bundler colours its own diagnostics unconditionally, so `aai build`'s
|
|
23
|
+
* failure reached the JSON envelope as per-character SGR pairs — illegible in a
|
|
24
|
+
* CI log and meaningless to a `jq` consumer, which is the audience JSON mode
|
|
25
|
+
* has.
|
|
26
|
+
*
|
|
27
|
+
* The raw half is `node:util`'s, not a local CSI regex. A/B'd on Node 26: the
|
|
28
|
+
* two agree on every SGR pair and cursor move the hand-built pattern was
|
|
29
|
+
* written for, and the builtin ALSO strips OSC-8 hyperlinks (`ESC ] 8 ;; url`)
|
|
30
|
+
* and charset selects (`ESC ( B`), which that pattern left in the envelope — so
|
|
31
|
+
* its claim to cover what a diagnostic arrives wearing was the incomplete part.
|
|
32
|
+
* The JSON-escaped half has no builtin and stays.
|
|
33
|
+
*/
|
|
34
|
+
export declare function stripAnsi(text: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Write a machine-readable line to stdout, resolving only once it has been
|
|
37
|
+
* flushed.
|
|
38
|
+
*
|
|
39
|
+
* ANSI escapes are stripped on the way out. This function carries JSON mode's
|
|
40
|
+
* one result line and nothing else — human output goes through `log.*` in
|
|
41
|
+
* `_ui.ts`, which is where colour belongs — so stripping here is what keeps
|
|
42
|
+
* the envelope clean no matter which module built the string inside it, and
|
|
43
|
+
* leaves a TTY's coloured diagnostic intact.
|
|
27
44
|
*
|
|
28
45
|
* Resolves (never rejects) even when the write fails: the common failure is
|
|
29
46
|
* EPIPE — the consumer closed the pipe (`aai … --json | head -1`) — and there
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { plural } from "@alexkroman1/aai/utils";
|
|
2
3
|
import { requiredProviderEnvVars } from "@alexkroman1/aai-runtime";
|
|
3
|
-
//#region _preflight.ts
|
|
4
|
+
//#region src/_preflight.ts
|
|
4
5
|
/**
|
|
5
6
|
* The deploy-time credential preflight — the classic dev/prod failure, caught
|
|
6
7
|
* before the upload instead of at the deployed agent's first session.
|
|
@@ -39,8 +40,7 @@ function missingCredentials(config, env) {
|
|
|
39
40
|
}
|
|
40
41
|
/** One line naming the missing keys and what to do about them. */
|
|
41
42
|
function missingCredentialMessage(missing) {
|
|
42
|
-
|
|
43
|
-
return `Missing credential${plural ? "s" : ""} the agent needs to start: ${missing.join(", ")}. Declare ${plural ? "them" : "it"} in .env and redeploy (already set on the platform with \`aai secret put\`? then this is already handled).`;
|
|
43
|
+
return `Missing ${plural(missing.length, "credential")} the agent needs to start: ${missing.join(", ")}. Declare ${plural(missing.length, "it", "them")} in .env and redeploy (already set on the platform with \`aai secret put\`? then this is already handled).`;
|
|
44
44
|
}
|
|
45
45
|
//#endregion
|
|
46
46
|
export { missingCredentialMessage, missingCredentials };
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
export declare const secret: import("citty").CommandDef<import("citty").ArgsDef>;
|
|
2
2
|
export declare const logs: import("citty").CommandDef<{
|
|
3
|
+
readonly server: {
|
|
4
|
+
readonly type: "string";
|
|
5
|
+
readonly alias: "s";
|
|
6
|
+
readonly description: "Platform server URL";
|
|
7
|
+
};
|
|
8
|
+
readonly json: {
|
|
9
|
+
readonly type: "boolean";
|
|
10
|
+
readonly description: "Output JSON (auto-detected in non-TTY)";
|
|
11
|
+
};
|
|
3
12
|
readonly dir: {
|
|
4
13
|
readonly type: "positional";
|
|
5
14
|
readonly description: "Project directory";
|
|
@@ -10,13 +19,4 @@ export declare const logs: import("citty").CommandDef<{
|
|
|
10
19
|
readonly alias: "f";
|
|
11
20
|
readonly description: "Keep printing new output";
|
|
12
21
|
};
|
|
13
|
-
readonly server: {
|
|
14
|
-
readonly type: "string";
|
|
15
|
-
readonly alias: "s";
|
|
16
|
-
readonly description: "Platform server URL";
|
|
17
|
-
};
|
|
18
|
-
readonly json: {
|
|
19
|
-
readonly type: "boolean";
|
|
20
|
-
readonly description: "Output JSON (auto-detected in non-TTY)";
|
|
21
|
-
};
|
|
22
22
|
}>;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { a as errorCode } from "./_utils-D5JGcjiW.mjs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { parseEnv } from "node:util";
|
|
5
5
|
import fs from "node:fs/promises";
|
|
6
|
-
//#region _server-common.ts
|
|
6
|
+
//#region src/_server-common.ts
|
|
7
7
|
/**
|
|
8
8
|
* Build the `ctx.env` record that agent tools will see at runtime.
|
|
9
9
|
*
|
|
@@ -35,7 +35,7 @@ async function resolveServerEnv(cwd, baseEnv) {
|
|
|
35
35
|
const env = {};
|
|
36
36
|
for (const [key, fileVal] of Object.entries(fileEntries)) {
|
|
37
37
|
const val = source[key] ?? fileVal;
|
|
38
|
-
if (val !== void 0) env[key] = val;
|
|
38
|
+
if (val !== void 0 && val !== "") env[key] = val;
|
|
39
39
|
}
|
|
40
40
|
return env;
|
|
41
41
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { n as getServerInfo } from "./_agent-
|
|
3
|
-
import { l as HINT_NOT_DEPLOYED, s as studioProjectApiUrl, u as apiRequest } from "./_studio-
|
|
4
|
-
//#region _slug-api.ts
|
|
2
|
+
import { n as getServerInfo } from "./_agent-BzUeqOdj.mjs";
|
|
3
|
+
import { l as HINT_NOT_DEPLOYED, s as studioProjectApiUrl, u as apiRequest } from "./_studio-DnR_BqFp.mjs";
|
|
4
|
+
//#region src/_slug-api.ts
|
|
5
5
|
/**
|
|
6
6
|
* Authenticated request against a deployed agent's slug-scoped resource
|
|
7
7
|
* (`${serverUrl}/${slug}${resourcePath}`) — the one shape every per-agent
|
|
@@ -43,12 +43,21 @@ async function secretRequest(cwd, resourcePath, init, server) {
|
|
|
43
43
|
target: studioProject ?? slug
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
/**
|
|
47
|
+
* A slug-scoped request against an ALREADY-resolved target.
|
|
48
|
+
*
|
|
49
|
+
* {@link slugRequest} resolves per call, which is right for a command that
|
|
50
|
+
* issues one request and wrong for one that POLLS. `aai logs --follow` re-entered
|
|
51
|
+
* `getServerInfo` on every tick — a read and zod parse of `.aai/project.json`, a
|
|
52
|
+
* read of the global config, a `new URL` trust check, a slug-shape test, and with
|
|
53
|
+
* `--server` an `approveServer` that takes the CROSS-PROCESS config lock — once a
|
|
54
|
+
* second for the life of the follow, all of it re-deriving an immutable answer.
|
|
55
|
+
* `delete.ts` documents the same cost and answers it the same way, by hand; this
|
|
56
|
+
* is the general version, so the next polling command inherits it.
|
|
57
|
+
*/
|
|
58
|
+
async function slugRequestOn(target, resourcePath, init) {
|
|
59
|
+
const { serverUrl, slug, apiKey } = target;
|
|
60
|
+
return deployedAgentRequest(`${serverUrl}/${slug}${resourcePath}`, init, apiKey);
|
|
52
61
|
}
|
|
53
62
|
//#endregion
|
|
54
|
-
export {
|
|
63
|
+
export { slugRequestOn as n, secretRequest as t };
|
package/dist/_slug-api.d.ts
CHANGED
|
@@ -27,6 +27,25 @@ export declare function secretRequest<T = unknown>(cwd: string, resourcePath: st
|
|
|
27
27
|
data: T;
|
|
28
28
|
target: string;
|
|
29
29
|
}>;
|
|
30
|
+
/** An already-resolved deployed target — what {@link slugRequestOn} needs. */
|
|
31
|
+
export type SlugTarget = {
|
|
32
|
+
serverUrl: string;
|
|
33
|
+
slug: string;
|
|
34
|
+
apiKey: string;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* A slug-scoped request against an ALREADY-resolved target.
|
|
38
|
+
*
|
|
39
|
+
* {@link slugRequest} resolves per call, which is right for a command that
|
|
40
|
+
* issues one request and wrong for one that POLLS. `aai logs --follow` re-entered
|
|
41
|
+
* `getServerInfo` on every tick — a read and zod parse of `.aai/project.json`, a
|
|
42
|
+
* read of the global config, a `new URL` trust check, a slug-shape test, and with
|
|
43
|
+
* `--server` an `approveServer` that takes the CROSS-PROCESS config lock — once a
|
|
44
|
+
* second for the life of the follow, all of it re-deriving an immutable answer.
|
|
45
|
+
* `delete.ts` documents the same cost and answers it the same way, by hand; this
|
|
46
|
+
* is the general version, so the next polling command inherits it.
|
|
47
|
+
*/
|
|
48
|
+
export declare function slugRequestOn<T = unknown>(target: SlugTarget, resourcePath: string, init: SlugRequestInit): Promise<T>;
|
|
30
49
|
export declare function slugRequest<T = unknown>(cwd: string, resourcePath: string, init: SlugRequestInit, server?: string): Promise<{
|
|
31
50
|
data: T;
|
|
32
51
|
slug: string;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as CliError } from "./_output-
|
|
2
|
+
import { t as CliError } from "./_output-DBP9Op_d.mjs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { isRecord, omitUndefined } from "@alexkroman1/aai/utils";
|
|
5
5
|
import { MAX_SLUG_LENGTH, PREVIEW_SLUG_SUFFIX, VALID_SLUG_RE } from "@alexkroman1/aai/internal";
|
|
6
6
|
import { isLocalOnlyFile, snapshotWorkspaceFiles } from "@alexkroman1/aai/workspace-files";
|
|
7
7
|
import { FetchError, ofetch } from "ofetch";
|
|
8
8
|
import { slugifyName } from "@alexkroman1/aai/slugify";
|
|
9
|
-
//#region _api-client.ts
|
|
9
|
+
//#region src/_api-client.ts
|
|
10
10
|
/**
|
|
11
11
|
* Shared HTTP helper for platform API calls (deploy, delete, secrets).
|
|
12
12
|
*
|
|
@@ -143,7 +143,7 @@ function toApiError(err, url, opts) {
|
|
|
143
143
|
return new Error(`${opts.action} failed: could not reach ${url}\n ${hint}`, { cause });
|
|
144
144
|
}
|
|
145
145
|
//#endregion
|
|
146
|
-
//#region _studio.ts
|
|
146
|
+
//#region src/_studio.ts
|
|
147
147
|
/**
|
|
148
148
|
* Internals of the studio-workspace commands (`aai list/pull/push/publish`):
|
|
149
149
|
* the local source-file walk and the thin clients for the platform's
|
|
@@ -221,7 +221,11 @@ function fetchStudioProject(serverUrl, apiKey, project) {
|
|
|
221
221
|
apiKey,
|
|
222
222
|
action: "pull",
|
|
223
223
|
allow404: true
|
|
224
|
-
});
|
|
224
|
+
}).then((res) => res === null ? null : checkedResponse(res, isStudioProject, `the studio project ${project} at ${serverUrl}`));
|
|
225
|
+
}
|
|
226
|
+
/** A pulled workspace, as the wire must actually carry it. */
|
|
227
|
+
function isStudioProject(value) {
|
|
228
|
+
return isRecord(value) && isRecord(value.files) && Object.values(value.files).every((v) => typeof v === "string") && typeof value.sourceHash === "string";
|
|
225
229
|
}
|
|
226
230
|
/** `PUT /studio/projects/:project/source` — the atomic whole-tree push. */
|
|
227
231
|
function pushStudioSource(serverUrl, apiKey, project, body) {
|
|
@@ -231,7 +235,7 @@ function pushStudioSource(serverUrl, apiKey, project, body) {
|
|
|
231
235
|
method: "PUT",
|
|
232
236
|
body,
|
|
233
237
|
hints: { 409: "The studio has newer changes. Run `aai pull` to fetch them, or `aai push --force` to overwrite." }
|
|
234
|
-
});
|
|
238
|
+
}).then((res) => checkedResponse(res, (value) => isRecord(value) && typeof value.sourceHash === "string" && typeof value.created === "boolean", `the studio push route at ${serverUrl}`));
|
|
235
239
|
}
|
|
236
240
|
/** `POST /studio/projects/:project/deploy` — Publish, in the project's sandbox. */
|
|
237
241
|
function publishStudioProject(serverUrl, apiKey, project, opts = {}) {
|
|
@@ -10,6 +10,15 @@ export declare const list: import("citty").CommandDef<{
|
|
|
10
10
|
};
|
|
11
11
|
}>;
|
|
12
12
|
export declare const pull: import("citty").CommandDef<{
|
|
13
|
+
readonly server: {
|
|
14
|
+
readonly type: "string";
|
|
15
|
+
readonly alias: "s";
|
|
16
|
+
readonly description: "Platform server URL";
|
|
17
|
+
};
|
|
18
|
+
readonly json: {
|
|
19
|
+
readonly type: "boolean";
|
|
20
|
+
readonly description: "Output JSON (auto-detected in non-TTY)";
|
|
21
|
+
};
|
|
13
22
|
readonly project: {
|
|
14
23
|
readonly type: "positional";
|
|
15
24
|
readonly description: "Studio project name (see `aai list`)";
|
|
@@ -25,6 +34,8 @@ export declare const pull: import("citty").CommandDef<{
|
|
|
25
34
|
readonly alias: "f";
|
|
26
35
|
readonly description: "Overwrite files in a non-empty directory";
|
|
27
36
|
};
|
|
37
|
+
}>;
|
|
38
|
+
export declare const push: import("citty").CommandDef<{
|
|
28
39
|
readonly server: {
|
|
29
40
|
readonly type: "string";
|
|
30
41
|
readonly alias: "s";
|
|
@@ -34,13 +45,13 @@ export declare const pull: import("citty").CommandDef<{
|
|
|
34
45
|
readonly type: "boolean";
|
|
35
46
|
readonly description: "Output JSON (auto-detected in non-TTY)";
|
|
36
47
|
};
|
|
37
|
-
}>;
|
|
38
|
-
export declare const push: import("citty").CommandDef<{
|
|
39
48
|
readonly force: {
|
|
40
49
|
readonly type: "boolean";
|
|
41
50
|
readonly alias: "f";
|
|
42
51
|
readonly description: "Overwrite studio-side changes instead of failing the fast-forward check";
|
|
43
52
|
};
|
|
53
|
+
}>;
|
|
54
|
+
export declare const publish: import("citty").CommandDef<{
|
|
44
55
|
readonly server: {
|
|
45
56
|
readonly type: "string";
|
|
46
57
|
readonly alias: "s";
|
|
@@ -50,22 +61,11 @@ export declare const push: import("citty").CommandDef<{
|
|
|
50
61
|
readonly type: "boolean";
|
|
51
62
|
readonly description: "Output JSON (auto-detected in non-TTY)";
|
|
52
63
|
};
|
|
53
|
-
}>;
|
|
54
|
-
export declare const publish: import("citty").CommandDef<{
|
|
55
64
|
readonly force: {
|
|
56
65
|
readonly type: "boolean";
|
|
57
66
|
readonly alias: "f";
|
|
58
67
|
readonly description: "Overwrite studio-side changes instead of failing the fast-forward check";
|
|
59
68
|
};
|
|
60
|
-
readonly server: {
|
|
61
|
-
readonly type: "string";
|
|
62
|
-
readonly alias: "s";
|
|
63
|
-
readonly description: "Platform server URL";
|
|
64
|
-
};
|
|
65
|
-
readonly json: {
|
|
66
|
-
readonly type: "boolean";
|
|
67
|
-
readonly description: "Output JSON (auto-detected in non-TTY)";
|
|
68
|
-
};
|
|
69
69
|
readonly skipTypecheck: {
|
|
70
70
|
readonly type: "boolean";
|
|
71
71
|
readonly description: "Skip type checking before publishing";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { t as getMonorepoRoot } from "./_agent-
|
|
2
|
+
import { g as writeJson, s as errorMessage$1 } from "./_utils-D5JGcjiW.mjs";
|
|
3
|
+
import { t as getMonorepoRoot } from "./_agent-BzUeqOdj.mjs";
|
|
4
4
|
import { existsSync } from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { isRecord } from "@alexkroman1/aai/utils";
|
|
@@ -106,7 +106,7 @@ async function layerScaffoldManifest(scaffoldDir, targetDir) {
|
|
|
106
106
|
const [mine, theirs] = await Promise.all([readJsonFile(target), readJsonFile(path.join(scaffoldDir, "package.json"))]);
|
|
107
107
|
if (!(mine && theirs)) return;
|
|
108
108
|
const merged = mergeScaffoldManifest(mine, theirs);
|
|
109
|
-
if (merged) await
|
|
109
|
+
if (merged) await writeJson(target, merged);
|
|
110
110
|
}
|
|
111
111
|
/** Parse a JSON file, or null when it is missing or unparseable. */
|
|
112
112
|
async function readJsonFile(file) {
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as CliError } from "./_output-
|
|
3
|
-
import { n as log } from "./_ui-
|
|
2
|
+
import { t as CliError } from "./_output-DBP9Op_d.mjs";
|
|
3
|
+
import { n as log } from "./_ui-DCt4qZrk.mjs";
|
|
4
4
|
import { typecheckProject } from "./typecheck.mjs";
|
|
5
|
-
//#region _typecheck-gate.ts
|
|
5
|
+
//#region src/_typecheck-gate.ts
|
|
6
6
|
/**
|
|
7
7
|
* The build/deploy typecheck gate: run the project's own `tsc --noEmit`
|
|
8
8
|
* (see `typecheck.ts`) and turn a failure into a structured CliError. The
|
|
9
9
|
* bundlers strip types unchecked, so without this a type-broken agent
|
|
10
10
|
* ships and misbehaves at runtime instead of failing here.
|
|
11
11
|
*/
|
|
12
|
-
async function assertTypechecks(cwd) {
|
|
12
|
+
async function assertTypechecks(cwd, opts = {}) {
|
|
13
|
+
if (opts.skip) return;
|
|
13
14
|
log.step("Type checking…");
|
|
14
15
|
const result = await typecheckProject(cwd);
|
|
15
16
|
if (!result.ok) throw new CliError("typecheck_failed", result.output, "Fix the type errors, or pass --skipTypecheck to build anyway");
|
|
@@ -4,4 +4,6 @@
|
|
|
4
4
|
* bundlers strip types unchecked, so without this a type-broken agent
|
|
5
5
|
* ships and misbehaves at runtime instead of failing here.
|
|
6
6
|
*/
|
|
7
|
-
export declare function assertTypechecks(cwd: string
|
|
7
|
+
export declare function assertTypechecks(cwd: string, opts?: {
|
|
8
|
+
skip?: boolean | undefined;
|
|
9
|
+
}): Promise<void>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { styleText } from "node:util";
|
|
3
3
|
import * as p from "@clack/prompts";
|
|
4
|
-
//#region _ui.ts
|
|
4
|
+
//#region src/_ui.ts
|
|
5
5
|
const noop = () => {};
|
|
6
6
|
let silenced = false;
|
|
7
7
|
/** Log instance that delegates to clack (human mode) or no-ops (JSON mode). */
|
|
@@ -3,7 +3,7 @@ import { readFileSync } from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { errorDetail, errorMessage, errorMessage as errorMessage$1, omitUndefined } from "@alexkroman1/aai/utils";
|
|
5
5
|
import fs from "node:fs/promises";
|
|
6
|
-
//#region _utils.ts
|
|
6
|
+
//#region src/_utils.ts
|
|
7
7
|
/**
|
|
8
8
|
* The file that marks a directory as an agent project — the single source
|
|
9
9
|
* for the entry filename every command checks or scaffolds.
|
|
@@ -115,5 +115,47 @@ async function writeJson(filePath, data, opts = {}) {
|
|
|
115
115
|
throw err;
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Code-unit ordering, never `localeCompare`.
|
|
120
|
+
*
|
|
121
|
+
* With no explicit locale `localeCompare` answers to the runtime's ICU default,
|
|
122
|
+
* so the same project would produce a differently-ordered artifact on a
|
|
123
|
+
* different machine — which is why the repo's own generated files sort this way.
|
|
124
|
+
* The reproducibility this protects is real in both callers: the scaffold's
|
|
125
|
+
* `pnpm-workspace.yaml` overrides, and the unrun-spec report.
|
|
126
|
+
*/
|
|
127
|
+
function compareCodeUnits(a, b) {
|
|
128
|
+
if (a === b) return 0;
|
|
129
|
+
return a < b ? -1 : 1;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Strip trailing slashes, so a `${base}/...` join cannot produce `//path`.
|
|
133
|
+
*
|
|
134
|
+
* Here rather than beside its first caller because BOTH callers need it and
|
|
135
|
+
* `_agent.ts` — where it lived — is a module `workflow.test.ts` mocks whole, so
|
|
136
|
+
* importing a pure helper from there breaks the mock. `workflow.ts`'s local copy
|
|
137
|
+
* took `/\/$/` — ONE slash — against this `/\/+$/`, so the two disagreed on the
|
|
138
|
+
* only input either was written for.
|
|
139
|
+
*/
|
|
140
|
+
function stripTrailingSlash(url) {
|
|
141
|
+
return url.replace(/\/+$/, "");
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* A list as one phrase — the first {@link MAX_NAMED_ITEMS} names, then a count.
|
|
145
|
+
*
|
|
146
|
+
* One definition because a user reads this wording from two different commands:
|
|
147
|
+
* the unrun-spec report (`aai test`) and the "no such project" hint
|
|
148
|
+
* (`aai pull`). Both open-coded the same slice, the same arithmetic and the same
|
|
149
|
+
* `, and N more`, so the cap could drift between two messages one CLI prints.
|
|
150
|
+
*
|
|
151
|
+
* Here rather than in `_ui.ts` because this is a pure formatter and that module
|
|
152
|
+
* is the OUTPUT seam specs mock — `studio.test.ts` replaces it with a bare
|
|
153
|
+
* factory, which drops every export the factory does not list.
|
|
154
|
+
*/
|
|
155
|
+
function formatCappedList(items, max = 10) {
|
|
156
|
+
const named = items.slice(0, max).join(", ");
|
|
157
|
+
const rest = items.length - max;
|
|
158
|
+
return rest > 0 ? `${named}, and ${rest} more` : named;
|
|
159
|
+
}
|
|
118
160
|
//#endregion
|
|
119
|
-
export {
|
|
161
|
+
export { errorCode as a, fileExists as c, readJson as d, readPackageJson as f, writeJson as g, validateAgentExport as h, compareCodeUnits as i, formatCappedList as l, stripTrailingSlash as m, binFromManifest as n, errorDetail as o, resolveCwd as p, binFromPackageJson as r, errorMessage$1 as s, AGENT_ENTRY as t, isEexist as u };
|
package/dist/_utils.d.ts
CHANGED
|
@@ -69,3 +69,38 @@ export declare function readJson(filePath: string): Promise<unknown>;
|
|
|
69
69
|
export declare function writeJson(filePath: string, data: unknown, opts?: {
|
|
70
70
|
mode?: number;
|
|
71
71
|
}): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Code-unit ordering, never `localeCompare`.
|
|
74
|
+
*
|
|
75
|
+
* With no explicit locale `localeCompare` answers to the runtime's ICU default,
|
|
76
|
+
* so the same project would produce a differently-ordered artifact on a
|
|
77
|
+
* different machine — which is why the repo's own generated files sort this way.
|
|
78
|
+
* The reproducibility this protects is real in both callers: the scaffold's
|
|
79
|
+
* `pnpm-workspace.yaml` overrides, and the unrun-spec report.
|
|
80
|
+
*/
|
|
81
|
+
export declare function compareCodeUnits(a: string, b: string): number;
|
|
82
|
+
/**
|
|
83
|
+
* Strip trailing slashes, so a `${base}/...` join cannot produce `//path`.
|
|
84
|
+
*
|
|
85
|
+
* Here rather than beside its first caller because BOTH callers need it and
|
|
86
|
+
* `_agent.ts` — where it lived — is a module `workflow.test.ts` mocks whole, so
|
|
87
|
+
* importing a pure helper from there breaks the mock. `workflow.ts`'s local copy
|
|
88
|
+
* took `/\/$/` — ONE slash — against this `/\/+$/`, so the two disagreed on the
|
|
89
|
+
* only input either was written for.
|
|
90
|
+
*/
|
|
91
|
+
export declare function stripTrailingSlash(url: string): string;
|
|
92
|
+
/** How many names a capped list prints before it starts counting. */
|
|
93
|
+
export declare const MAX_NAMED_ITEMS = 10;
|
|
94
|
+
/**
|
|
95
|
+
* A list as one phrase — the first {@link MAX_NAMED_ITEMS} names, then a count.
|
|
96
|
+
*
|
|
97
|
+
* One definition because a user reads this wording from two different commands:
|
|
98
|
+
* the unrun-spec report (`aai test`) and the "no such project" hint
|
|
99
|
+
* (`aai pull`). Both open-coded the same slice, the same arithmetic and the same
|
|
100
|
+
* `, and N more`, so the cap could drift between two messages one CLI prints.
|
|
101
|
+
*
|
|
102
|
+
* Here rather than in `_ui.ts` because this is a pure formatter and that module
|
|
103
|
+
* is the OUTPUT seam specs mock — `studio.test.ts` replaces it with a bare
|
|
104
|
+
* factory, which drops every export the factory does not list.
|
|
105
|
+
*/
|
|
106
|
+
export declare function formatCappedList(items: readonly string[], max?: number): string;
|