@alexkroman1/aai-cli 5.14.0 → 6.1.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-C8Jk-ZcP.mjs → _agent-vGW5SBnX.mjs} +23 -5
- package/dist/_agent.d.ts +18 -0
- package/dist/{_api-client-BX8Qv51n.mjs → _api-client-LgLksMhN.mjs} +45 -8
- package/dist/_api-client.d.ts +39 -0
- package/dist/{_bundler-BuIyDdvs.mjs → _bundler-BapCUUJN.mjs} +42 -14
- package/dist/_bundler.d.ts +43 -5
- package/dist/_cli-common.d.ts +67 -3
- package/dist/{_config-CzwAWOWr.mjs → _config-D_s09e7g.mjs} +36 -18
- package/dist/_deploy.d.ts +2 -5
- package/dist/_dev-restart.d.ts +6 -1
- package/dist/{_dev-server-Ch7K8e8i.mjs → _dev-server-DQ1t9sbw.mjs} +94 -38
- package/dist/_dev-server.d.ts +30 -2
- package/dist/_e2e-test-utils.d.ts +80 -0
- package/dist/_fault-mode.d.ts +175 -0
- package/dist/{_init-CbkrE2wq.mjs → _init-CpISqoR8.mjs} +3 -3
- package/dist/{_output-CC300DzW.mjs → _output-CKkmWs7i.mjs} +8 -1
- package/dist/_output.d.ts +9 -2
- package/dist/{_server-common-61fL__39.mjs → _server-common-6e0QI4mq.mjs} +1 -1
- package/dist/{_slug-api-D30zPmsn.mjs → _slug-api-D2heILuC.mjs} +2 -2
- package/dist/{_studio-D2ciIhbi.mjs → _studio-zAJycZ8j.mjs} +3 -3
- package/dist/{_templates-B1kt3BGt.mjs → _templates-iDGjGiPf.mjs} +4 -6
- package/dist/{_typecheck-gate-4v9UBZHh.mjs → _typecheck-gate-BXvUNnfs.mjs} +1 -1
- package/dist/{_utils-0DHbcqUb.mjs → _utils-B8QmtFhK.mjs} +21 -7
- package/dist/_utils.d.ts +18 -2
- package/dist/{_vite-env-Dg_QlVv0.mjs → _vite-env-BNveawd1.mjs} +31 -1
- package/dist/_vite-env.d.ts +30 -0
- package/dist/build-BO1Ni6oJ.mjs +62 -0
- package/dist/build.d.ts +19 -0
- package/dist/cli-workflow.d.ts +10 -0
- package/dist/cli.d.ts +1 -2
- package/dist/cli.mjs +406 -224
- package/dist/{client-bundler-C9qXL8A2.mjs → client-bundler-B8Q9F8gJ.mjs} +3 -22
- package/dist/client-bundler.mjs +1 -1
- package/dist/{delete-DCRX3d96.mjs → delete-4I0uNR4a.mjs} +6 -7
- package/dist/delete.d.ts +2 -8
- package/dist/{deploy-DjsEV-40.mjs → deploy-DJ3HRcia.mjs} +13 -14
- package/dist/{dev-CvPH5dbF.mjs → dev-C6SWaAZP.mjs} +3 -3
- package/dist/{eject-LgBRfnbH.mjs → eject-Zrzk5KV3.mjs} +41 -15
- package/dist/eject.d.ts +30 -3
- package/dist/{init-BppxMR8r.mjs → init-BTRGiPQr.mjs} +61 -26
- package/dist/init.d.ts +11 -0
- package/dist/{login-BQQaxJhL.mjs → login-k0Z3VQu9.mjs} +5 -5
- package/dist/project-config.mjs +1 -1
- package/dist/scaffold/CLAUDE.md +498 -91
- package/dist/scaffold/package.json +11 -8
- package/dist/scaffold/pnpm-workspace.yaml +8 -0
- package/dist/scaffold/server.mjs +53 -57
- package/dist/{secret-Ds9dvqAj.mjs → secret-DzSQx1Ds.mjs} +6 -3
- package/dist/{storage-DbrQSHFk.mjs → storage-C-Do0mIA.mjs} +18 -6
- package/dist/{studio-GSFd4s5O.mjs → studio-BGVmCA2G.mjs} +10 -10
- package/dist/templates/code-interpreter/agent.ts +0 -2
- package/dist/templates/dispatch-center/agent.test.ts +17 -19
- package/dist/templates/dispatch-center/agent.ts +2 -29
- package/dist/templates/dispatch-center/client.tsx +33 -16
- package/dist/templates/dispatch-center/shared.ts +53 -19
- package/dist/templates/dispatch-center/tools/incident_add_note.ts +12 -14
- package/dist/templates/dispatch-center/tools/incident_create.ts +41 -44
- package/dist/templates/dispatch-center/tools/incident_escalate.ts +59 -61
- package/dist/templates/dispatch-center/tools/incident_get.ts +3 -4
- package/dist/templates/dispatch-center/tools/incident_triage.ts +43 -45
- package/dist/templates/dispatch-center/tools/incident_update_status.ts +39 -41
- package/dist/templates/dispatch-center/tools/ops_dashboard.ts +2 -5
- package/dist/templates/dispatch-center/tools/ops_protocols.ts +1 -1
- package/dist/templates/dispatch-center/tools/ops_run_scenario.ts +20 -23
- package/dist/templates/dispatch-center/tools/resources_dispatch.ts +52 -54
- package/dist/templates/dispatch-center/tools/resources_get_available.ts +2 -4
- package/dist/templates/dispatch-center/tools/resources_update_status.ts +39 -42
- package/dist/templates/embedded-assets/agent.test.ts +83 -0
- package/dist/templates/embedded-assets/agent.ts +1 -48
- package/dist/templates/embedded-assets/shared.ts +17 -0
- package/dist/templates/embedded-assets/system-prompt.md +13 -0
- package/dist/templates/embedded-assets/tools/list_topics.ts +9 -0
- package/dist/templates/embedded-assets/tools/search_knowledge.ts +26 -0
- package/dist/templates/health-assistant/agent.test.ts +169 -0
- package/dist/templates/health-assistant/agent.ts +1 -166
- package/dist/templates/health-assistant/fda.ts +100 -0
- package/dist/templates/health-assistant/tools/check_drug_interaction.ts +59 -0
- package/dist/templates/health-assistant/tools/medication_lookup.ts +31 -0
- package/dist/templates/infocom-adventure/agent.test.ts +205 -0
- package/dist/templates/infocom-adventure/agent.ts +3 -106
- package/dist/templates/infocom-adventure/client.tsx +28 -12
- package/dist/templates/infocom-adventure/shared.ts +28 -11
- package/dist/templates/infocom-adventure/tools/game_state_drop.ts +13 -0
- package/dist/templates/infocom-adventure/tools/game_state_flag.ts +13 -0
- package/dist/templates/infocom-adventure/tools/game_state_get.ts +16 -0
- package/dist/templates/infocom-adventure/tools/game_state_history.ts +14 -0
- package/dist/templates/infocom-adventure/tools/game_state_move.ts +14 -0
- package/dist/templates/infocom-adventure/tools/game_state_restart.ts +13 -0
- package/dist/templates/infocom-adventure/tools/game_state_score.ts +13 -0
- package/dist/templates/infocom-adventure/tools/game_state_take.ts +13 -0
- package/dist/templates/link-digest/agent.test.ts +223 -0
- package/dist/templates/link-digest/agent.ts +79 -0
- package/dist/templates/link-digest/client.tsx +146 -0
- package/dist/templates/link-digest/workflows/digest.ts +248 -0
- package/dist/templates/math-buddy/agent.ts +0 -2
- package/dist/templates/night-owl/agent.test.ts +80 -0
- package/dist/templates/night-owl/agent.ts +1 -68
- package/dist/templates/night-owl/system-prompt.md +10 -0
- package/dist/templates/night-owl/tools/recommend.ts +64 -0
- package/dist/templates/personal-finance/agent.ts +0 -2
- package/dist/templates/pizza-ordering/agent.test.ts +52 -47
- package/dist/templates/pizza-ordering/agent.ts +9 -158
- package/dist/templates/pizza-ordering/shared.ts +35 -12
- package/dist/templates/pizza-ordering/tools/add_pizza.ts +31 -0
- package/dist/templates/pizza-ordering/tools/place_order.ts +22 -0
- package/dist/templates/pizza-ordering/tools/remove_pizza.ts +22 -0
- package/dist/templates/pizza-ordering/tools/set_customer_name.ts +13 -0
- package/dist/templates/pizza-ordering/tools/update_pizza.ts +28 -0
- package/dist/templates/pizza-ordering/tools/view_order.ts +21 -0
- package/dist/templates/plan-and-execute/agent.test.ts +411 -0
- package/dist/templates/plan-and-execute/agent.ts +23 -0
- package/dist/templates/plan-and-execute/client.tsx +115 -0
- package/dist/templates/plan-and-execute/graph.ts +178 -0
- package/dist/templates/plan-and-execute/prompts.ts +110 -0
- package/dist/templates/plan-and-execute/shared.ts +161 -0
- package/dist/templates/plan-and-execute/system-prompt.md +18 -0
- package/dist/templates/plan-and-execute/tools/plan_status.ts +20 -0
- package/dist/templates/plan-and-execute/tools/revise_plan.ts +63 -0
- package/dist/templates/plan-and-execute/tools/start_plan.ts +51 -0
- package/dist/templates/plan-and-execute/tools/work_next_step.ts +101 -0
- package/dist/templates/recap-workflow/agent.test.ts +762 -0
- package/dist/templates/recap-workflow/agent.ts +95 -0
- package/dist/templates/recap-workflow/shared.ts +43 -0
- package/dist/templates/recap-workflow/tools/cancel_recap.ts +23 -0
- package/dist/templates/recap-workflow/tools/keep_transcript.ts +31 -0
- package/dist/templates/recap-workflow/tools/recap_progress.ts +27 -0
- package/dist/templates/recap-workflow/tools/recap_status.ts +46 -0
- package/dist/templates/recap-workflow/tools/request_recap.ts +43 -0
- package/dist/templates/recap-workflow/workflows/recap.ts +544 -0
- package/dist/templates/recap-workflow/workflows/tokens.ts +26 -0
- package/dist/templates/redline/agent.test.ts +222 -0
- package/dist/templates/redline/agent.ts +89 -0
- package/dist/templates/redline/client.tsx +184 -0
- package/dist/templates/redline/workflows/prompts.ts +68 -0
- package/dist/templates/redline/workflows/redline.ts +258 -0
- package/dist/templates/research-workflow/agent.test.ts +525 -0
- package/dist/templates/research-workflow/agent.ts +94 -0
- package/dist/templates/research-workflow/shared.ts +35 -0
- package/dist/templates/research-workflow/tools/file_it_now.ts +22 -0
- package/dist/templates/research-workflow/tools/request_research.ts +33 -0
- package/dist/templates/research-workflow/tools/research_progress.ts +26 -0
- package/dist/templates/research-workflow/tools/research_status.ts +40 -0
- package/dist/templates/research-workflow/workflows/prompts.ts +143 -0
- package/dist/templates/research-workflow/workflows/research.ts +558 -0
- package/dist/templates/retail/agent.test.ts +236 -172
- package/dist/templates/retail/agent.ts +5 -42
- package/dist/templates/retail/client.tsx +33 -11
- package/dist/templates/retail/registry.test.ts +31 -16
- package/dist/templates/retail/resolve.ts +28 -64
- package/dist/templates/retail/shared.test.ts +4 -3
- package/dist/templates/retail/shared.ts +24 -7
- package/dist/templates/retail/store.test.ts +12 -10
- package/dist/templates/retail/store.ts +69 -30
- package/dist/templates/retail/tools/cancel_pending_order.ts +4 -9
- package/dist/templates/retail/tools/exchange_delivered_order_items.ts +22 -11
- package/dist/templates/retail/tools/find_user_id_by_email.ts +5 -9
- package/dist/templates/retail/tools/find_user_id_by_name_zip.ts +5 -7
- package/dist/templates/retail/tools/get_item_details.ts +5 -7
- package/dist/templates/retail/tools/get_order_details.ts +5 -7
- package/dist/templates/retail/tools/get_product_details.ts +4 -7
- package/dist/templates/retail/tools/get_user_details.ts +4 -7
- package/dist/templates/retail/tools/list_all_product_types.ts +5 -7
- package/dist/templates/retail/tools/modify_pending_order_address.ts +4 -9
- package/dist/templates/retail/tools/modify_pending_order_items.ts +4 -16
- package/dist/templates/retail/tools/modify_pending_order_payment.ts +3 -9
- package/dist/templates/retail/tools/modify_user_address.ts +4 -9
- package/dist/templates/retail/tools/return_delivered_order_items.ts +3 -9
- package/dist/templates/retail/tools/transfer_to_human_agents.ts +1 -1
- package/dist/templates/solo-rpg/agent.test.ts +152 -29
- package/dist/templates/solo-rpg/agent.ts +3 -21
- package/dist/templates/solo-rpg/client.tsx +19 -6
- package/dist/templates/solo-rpg/shared.ts +19 -11
- package/dist/templates/solo-rpg/tools/action_roll.ts +2 -4
- package/dist/templates/solo-rpg/tools/burn_momentum.ts +2 -4
- package/dist/templates/solo-rpg/tools/check_state.ts +6 -6
- package/dist/templates/solo-rpg/tools/load_game.ts +1 -1
- package/dist/templates/solo-rpg/tools/oracle.ts +18 -11
- package/dist/templates/solo-rpg/tools/save_game.ts +10 -7
- package/dist/templates/solo-rpg/tools/setup_character.ts +1 -1
- package/dist/templates/solo-rpg/tools/update_state.ts +3 -4
- package/dist/templates/support-line/agent.test.ts +343 -0
- package/dist/templates/support-line/agent.ts +27 -0
- package/dist/templates/support-line/client.tsx +122 -0
- package/dist/templates/support-line/graph.ts +224 -0
- package/dist/templates/support-line/knowledge.json +65 -0
- package/dist/templates/support-line/prompts.ts +95 -0
- package/dist/templates/support-line/shared.ts +240 -0
- package/dist/templates/support-line/system-prompt.md +17 -0
- package/dist/templates/support-line/tools/answer_question.ts +72 -0
- package/dist/templates/support-line/tools/list_topics.ts +22 -0
- package/dist/templates/support-line/tools/log_ticket.ts +30 -0
- package/dist/templates/transcription-workflow/agent.test.ts +563 -0
- package/dist/templates/transcription-workflow/agent.ts +92 -0
- package/dist/templates/transcription-workflow/client.tsx +234 -0
- package/dist/templates/transcription-workflow/workflows/transcribe.ts +416 -0
- package/dist/templates/transcription-workflow/workflows/wav.ts +314 -0
- package/dist/templates/travel-concierge/agent.test.ts +338 -0
- package/dist/templates/travel-concierge/agent.ts +29 -0
- package/dist/templates/travel-concierge/client.tsx +161 -0
- package/dist/templates/travel-concierge/routing.ts +138 -0
- package/dist/templates/travel-concierge/shared.ts +547 -0
- package/dist/templates/travel-concierge/system-prompt.md +24 -0
- package/dist/templates/travel-concierge/tools/book_car_rental.ts +20 -0
- package/dist/templates/travel-concierge/tools/book_excursion.ts +18 -0
- package/dist/templates/travel-concierge/tools/book_hotel.ts +20 -0
- package/dist/templates/travel-concierge/tools/cancel_action.ts +7 -0
- package/dist/templates/travel-concierge/tools/cancel_ticket.ts +16 -0
- package/dist/templates/travel-concierge/tools/complete_or_escalate.ts +7 -0
- package/dist/templates/travel-concierge/tools/confirm_action.ts +7 -0
- package/dist/templates/travel-concierge/tools/lookup_booking.ts +35 -0
- package/dist/templates/travel-concierge/tools/search_car_rentals.ts +33 -0
- package/dist/templates/travel-concierge/tools/search_excursions.ts +37 -0
- package/dist/templates/travel-concierge/tools/search_flights.ts +43 -0
- package/dist/templates/travel-concierge/tools/search_hotels.ts +36 -0
- package/dist/templates/travel-concierge/tools/to_car_rental_assistant.ts +11 -0
- package/dist/templates/travel-concierge/tools/to_excursion_assistant.ts +11 -0
- package/dist/templates/travel-concierge/tools/to_flight_assistant.ts +11 -0
- package/dist/templates/travel-concierge/tools/to_hotel_assistant.ts +11 -0
- package/dist/templates/travel-concierge/tools/update_ticket.ts +22 -0
- package/dist/templates/web-researcher/agent.ts +0 -2
- package/dist/templates/web-researcher/system-prompt.md +10 -0
- package/dist/{test-2LWBcXvY.mjs → test-B6ZBxSpk.mjs} +2 -2
- package/dist/typecheck.mjs +17 -15
- package/dist/worker-bundler-CIdDhZ0c.mjs +398 -0
- package/dist/worker-bundler.d.ts +14 -0
- package/dist/worker-bundler.mjs +1 -108
- package/dist/workflow-B-O97AqI.mjs +125 -0
- package/dist/workflow-bundler.d.ts +133 -0
- package/dist/workflow.d.ts +67 -0
- package/package.json +13 -9
- package/dist/build-DQJ1ZSm9.mjs +0 -39
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as outputSilenced,
|
|
3
|
-
import {
|
|
4
|
-
import { n as fallbackHtmlPlugin } from "./client-bundler-
|
|
5
|
-
import {
|
|
6
|
-
import { n as
|
|
7
|
-
import { n as
|
|
8
|
-
import {
|
|
2
|
+
import { i as outputSilenced, r as notify } from "./_ui-u7T4YooX.mjs";
|
|
3
|
+
import { i as errorCode, o as errorMessage$1 } from "./_utils-B8QmtFhK.mjs";
|
|
4
|
+
import { n as fallbackHtmlPlugin } from "./client-bundler-B8Q9F8gJ.mjs";
|
|
5
|
+
import { t as DEDUPED_PEERS } from "./_vite-env-BNveawd1.mjs";
|
|
6
|
+
import { n as buildWorkflows, t as buildWorker } from "./worker-bundler-CIdDhZ0c.mjs";
|
|
7
|
+
import { n as ensureApiKey } from "./_config-D_s09e7g.mjs";
|
|
8
|
+
import { n as createWorkerEvaluator } from "./_bundler-BapCUUJN.mjs";
|
|
9
|
+
import { t as resolveServerEnv } from "./_server-common-6e0QI4mq.mjs";
|
|
9
10
|
import { existsSync } from "node:fs";
|
|
10
11
|
import path from "node:path";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
12
|
+
import { omitUndefined } from "@alexkroman1/aai/utils";
|
|
13
|
+
import { createCoalescingRunner, sleep } from "@alexkroman1/aai/internal";
|
|
14
|
+
import { WORKFLOW_API_PREFIX, configureWorkflowWorld, consoleLogger, createRuntime, createServer, createWorkflowSurface, handleWorkflowRequest, publishStepEnv, requiredProviderEnvVars, startWorkflowWorldIfDeclared, withHostCredentialFallback } from "@alexkroman1/aai/runtime";
|
|
13
15
|
import { defaultClientDir } from "@alexkroman1/aai-ui/client-dir";
|
|
14
16
|
import { watch } from "chokidar";
|
|
15
17
|
import getPort, { portNumbers } from "get-port";
|
|
@@ -94,36 +96,41 @@ const LISTEN_RETRY_DELAY_MS = 250;
|
|
|
94
96
|
* the built server (or {@link RestartSupervisor.close} on startup failure).
|
|
95
97
|
*/
|
|
96
98
|
function createRestartSupervisor(ops) {
|
|
97
|
-
const wait = ops.sleep ??
|
|
98
|
-
let
|
|
99
|
-
let
|
|
99
|
+
const wait = ops.sleep ?? sleep;
|
|
100
|
+
let booting = true;
|
|
101
|
+
let queuedDuringBoot = false;
|
|
100
102
|
let closed = false;
|
|
101
103
|
let current;
|
|
102
104
|
let cleanupPromise;
|
|
105
|
+
/**
|
|
106
|
+
* At most one rebuild in flight; every change landing during one collapses
|
|
107
|
+
* into a SINGLE trailing rebuild started after it settles.
|
|
108
|
+
*
|
|
109
|
+
* That is exactly the repo's `createCoalescingRunner` contract, and this
|
|
110
|
+
* module used to re-derive it with a `restarting` flag, a `pendingRestart`
|
|
111
|
+
* flag and a `do/while` loop. Coalescing is the right policy for the same
|
|
112
|
+
* reason it is there: a rebuild reads the files as they are WHEN IT RUNS, so
|
|
113
|
+
* N queued rebuilds would do the trailing one's work N times, while dropping
|
|
114
|
+
* the trigger outright would leave the newest save unserved (or, when the
|
|
115
|
+
* in-flight rebuild died on a mid-edit syntax error, leave the server down).
|
|
116
|
+
*/
|
|
117
|
+
const rebuilds = createCoalescingRunner(() => restartOnce());
|
|
103
118
|
function request() {
|
|
104
|
-
if (
|
|
105
|
-
|
|
119
|
+
if (booting) {
|
|
120
|
+
queuedDuringBoot = true;
|
|
106
121
|
return;
|
|
107
122
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
ops.notify("error", `Restart failed: ${errorMessage(err)}`);
|
|
111
|
-
}).finally(() => {
|
|
112
|
-
restarting = false;
|
|
123
|
+
rebuilds.trigger().catch((err) => {
|
|
124
|
+
ops.notify("error", `Restart failed: ${errorMessage$1(err)}`);
|
|
113
125
|
});
|
|
114
126
|
}
|
|
115
|
-
async function restart() {
|
|
116
|
-
do {
|
|
117
|
-
pendingRestart = false;
|
|
118
|
-
await restartOnce();
|
|
119
|
-
} while (pendingRestart && !closed);
|
|
120
|
-
}
|
|
121
127
|
async function restartOnce() {
|
|
128
|
+
if (closed) return;
|
|
122
129
|
let newServer;
|
|
123
130
|
try {
|
|
124
131
|
newServer = await ops.build();
|
|
125
132
|
} catch (err) {
|
|
126
|
-
ops.notify("error", `Restart failed: ${errorMessage(err)} (previous server still running)`);
|
|
133
|
+
ops.notify("error", `Restart failed: ${errorMessage$1(err)} (previous server still running)`);
|
|
127
134
|
return;
|
|
128
135
|
}
|
|
129
136
|
if (closed) {
|
|
@@ -138,7 +145,7 @@ function createRestartSupervisor(ops) {
|
|
|
138
145
|
try {
|
|
139
146
|
await listenWithRetry(newServer);
|
|
140
147
|
} catch (err) {
|
|
141
|
-
ops.notify("error", `Restart failed: ${errorMessage(err)} — dev server is down; save a file to retry.`);
|
|
148
|
+
ops.notify("error", `Restart failed: ${errorMessage$1(err)} — dev server is down; save a file to retry.`);
|
|
142
149
|
await closeQuietly(newServer);
|
|
143
150
|
return;
|
|
144
151
|
}
|
|
@@ -174,8 +181,8 @@ function createRestartSupervisor(ops) {
|
|
|
174
181
|
request,
|
|
175
182
|
adopt(server) {
|
|
176
183
|
current = server;
|
|
177
|
-
|
|
178
|
-
if (
|
|
184
|
+
booting = false;
|
|
185
|
+
if (queuedDuringBoot) request();
|
|
179
186
|
},
|
|
180
187
|
current: () => current,
|
|
181
188
|
close() {
|
|
@@ -229,7 +236,7 @@ async function resolveAgentEnv(root, agentDef) {
|
|
|
229
236
|
const required = requiredProviderEnvVars(agentDef);
|
|
230
237
|
const hasShellKey = Boolean(process.env.ASSEMBLYAI_API_KEY);
|
|
231
238
|
if (required.includes("ASSEMBLYAI_API_KEY") && !env.ASSEMBLYAI_API_KEY && !hasShellKey) env.ASSEMBLYAI_API_KEY = await ensureApiKey();
|
|
232
|
-
for (const warning of agentEnvWarnings(agentDef, env))
|
|
239
|
+
for (const warning of agentEnvWarnings(agentDef, env)) notify("warn", warning);
|
|
233
240
|
return env;
|
|
234
241
|
}
|
|
235
242
|
/**
|
|
@@ -244,9 +251,19 @@ async function resolveAgentEnv(root, agentDef) {
|
|
|
244
251
|
* errors in the agent's code propagate — the restart loop reports them and
|
|
245
252
|
* keeps the old server. Evaluation goes through the memoizing evaluator so
|
|
246
253
|
* a no-op save doesn't leak another module into the ESM registry.
|
|
254
|
+
*
|
|
255
|
+
* The project's `workflows/` directory is compiled by the same pass deploy
|
|
256
|
+
* runs (`buildWorkflows`) and rides the bundle as two string exports, which is
|
|
257
|
+
* how `aai dev` serves a workflow at all: nothing here hands the bundle to a
|
|
258
|
+
* guest, so the CLI is both ends of that contract locally. A project with no
|
|
259
|
+
* `workflows/` directory pays nothing — `buildWorkflows` resolves `undefined`
|
|
260
|
+
* without starting a builder.
|
|
247
261
|
*/
|
|
248
|
-
async function
|
|
249
|
-
return evaluate(await buildWorker(cwd, {
|
|
262
|
+
async function loadWorker(cwd, evaluate) {
|
|
263
|
+
return evaluate(await buildWorker(cwd, {
|
|
264
|
+
runtime: false,
|
|
265
|
+
workflows: await buildWorkflows(cwd)
|
|
266
|
+
}));
|
|
250
267
|
}
|
|
251
268
|
/**
|
|
252
269
|
* True for paths that should never trigger a restart: anything inside
|
|
@@ -310,11 +327,11 @@ function watchDirectory(dir, onChange) {
|
|
|
310
327
|
});
|
|
311
328
|
watcher.on("error", (err) => {
|
|
312
329
|
const hint = errorCode(err) === "ENOSPC" ? " The inotify watch limit was reached — raise the fs.inotify max_user_watches sysctl." : "";
|
|
313
|
-
notify("error", `File watcher error: ${errorMessage(err)}.${hint} Auto-restart on file changes may have stopped; restart \`aai dev\` after fixing.`);
|
|
330
|
+
notify("error", `File watcher error: ${errorMessage$1(err)}.${hint} Auto-restart on file changes may have stopped; restart \`aai dev\` after fixing.`);
|
|
314
331
|
});
|
|
315
332
|
watcher.on("all", () => {
|
|
316
333
|
debouncedChange().catch((err) => {
|
|
317
|
-
notify("error", `Watch handler failed: ${errorMessage(err)}`);
|
|
334
|
+
notify("error", `Watch handler failed: ${errorMessage$1(err)}`);
|
|
318
335
|
});
|
|
319
336
|
});
|
|
320
337
|
return watcher;
|
|
@@ -324,25 +341,48 @@ function watchDirectory(dir, onChange) {
|
|
|
324
341
|
* is unit-testable: `/websocket` MUST proxy with `ws: true` or `aai dev`
|
|
325
342
|
* with a `client.tsx` serves a page whose WebSocket never connects.
|
|
326
343
|
*
|
|
344
|
+
* **This table is the whole agent API as the browser can see it**, which is
|
|
345
|
+
* the thing to hold in mind before adding a route to `createServer`. Vite owns
|
|
346
|
+
* the port the user is told to open and answers everything not listed here
|
|
347
|
+
* itself — with a bare 404 carrying none of the agent server's headers, so the
|
|
348
|
+
* failure looks like a missing route rather than a missing proxy entry.
|
|
349
|
+
*
|
|
350
|
+
* `/workflows` is why that matters beyond voice. A WORKFLOW APP
|
|
351
|
+
* (`workflowApp()`) has no session and no socket: `page()` mounts a
|
|
352
|
+
* form and every single thing it does — listing workflows, starting a run,
|
|
353
|
+
* polling it, streaming its events — is a same-origin `fetch` under that
|
|
354
|
+
* prefix. Unproxied, the two workflow-app templates were dead on arrival under
|
|
355
|
+
* `aai dev` (`404 POST /workflows/runs` the instant the form is submitted)
|
|
356
|
+
* while the backend served the API correctly one port over. A string key
|
|
357
|
+
* prefix-matches, so this one entry covers `/runs`, `/runs/:id` and the
|
|
358
|
+
* `/runs/:id/events` SSE stream.
|
|
359
|
+
*
|
|
327
360
|
* `strictPort` because the reported URL is `http://localhost:<port>` —
|
|
328
361
|
* without it, Vite silently binds port+N when the port is busy and the
|
|
329
362
|
* printed/JSON-returned URL points at whatever else was listening.
|
|
363
|
+
*
|
|
364
|
+
* `AAI_DEV_HOST` reaches BOTH servers. Binding only the backend left Vite —
|
|
365
|
+
* the port the user is told to open — on loopback, i.e. failing exactly the
|
|
366
|
+
* case that variable exists for (`aai dev` in a container, reached from the host).
|
|
330
367
|
*/
|
|
331
368
|
function viteDevConfig(cwd, vitePort, backendPort) {
|
|
332
369
|
const target = `http://localhost:${backendPort}`;
|
|
333
370
|
return {
|
|
334
371
|
root: cwd,
|
|
335
372
|
plugins: [fallbackHtmlPlugin(cwd)],
|
|
373
|
+
resolve: { dedupe: DEDUPED_PEERS },
|
|
336
374
|
server: {
|
|
337
375
|
port: vitePort,
|
|
338
376
|
strictPort: true,
|
|
377
|
+
...omitUndefined({ host: devBindHost() }),
|
|
339
378
|
proxy: {
|
|
340
379
|
"/health": target,
|
|
341
380
|
"/client-config": target,
|
|
342
381
|
"/websocket": {
|
|
343
382
|
target,
|
|
344
383
|
ws: true
|
|
345
|
-
}
|
|
384
|
+
},
|
|
385
|
+
[WORKFLOW_API_PREFIX]: target
|
|
346
386
|
}
|
|
347
387
|
}
|
|
348
388
|
};
|
|
@@ -360,22 +400,38 @@ async function startDevServer(opts) {
|
|
|
360
400
|
const clientDirOpt = hasClient ? {} : { clientDir: defaultClientDir() };
|
|
361
401
|
const evaluateWorker = createWorkerEvaluator();
|
|
362
402
|
const devLogger = createDevLogger(outputSilenced());
|
|
403
|
+
let workflowWorldStarted = false;
|
|
363
404
|
/** Full build sequence, shared by initial startup and every restart. */
|
|
364
405
|
async function buildServer() {
|
|
365
|
-
const
|
|
406
|
+
const worker = await loadWorker(cwd, evaluateWorker);
|
|
407
|
+
const agentDef = worker.agent;
|
|
366
408
|
const env = await resolveAgentEnv(cwd, agentDef);
|
|
409
|
+
const world = configureWorkflowWorld({
|
|
410
|
+
databaseUrl: env.DATABASE_URL,
|
|
411
|
+
port: backendPort,
|
|
412
|
+
dataDir: path.join(cwd, ".workflow-data")
|
|
413
|
+
});
|
|
414
|
+
publishStepEnv(env);
|
|
415
|
+
const workflows = await createWorkflowSurface(worker.workflowCode, worker.stepCode);
|
|
416
|
+
if (workflows && !workflowWorldStarted) {
|
|
417
|
+
workflowWorldStarted = true;
|
|
418
|
+
await startWorkflowWorldIfDeclared(true, world);
|
|
419
|
+
}
|
|
367
420
|
const providerEnv = withHostCredentialFallback(env);
|
|
368
421
|
return createServer({
|
|
369
422
|
runtime: createRuntime({
|
|
370
423
|
agent: agentDef,
|
|
371
424
|
env,
|
|
372
425
|
providerEnv,
|
|
373
|
-
logger: devLogger
|
|
426
|
+
logger: devLogger,
|
|
427
|
+
publicUrl: process.env.PUBLIC_URL?.trim() || `http://localhost:${backendPort}`
|
|
374
428
|
}),
|
|
375
429
|
name: agentDef.name,
|
|
376
430
|
env: hostModeEnv(providerEnv),
|
|
377
431
|
hostBaseAgent: agentDef,
|
|
378
432
|
greeting: agentDef.greeting,
|
|
433
|
+
...omitUndefined({ page: agentDef.page }),
|
|
434
|
+
request: (req, res, url, method) => handleWorkflowRequest(workflows, req, res, url, method),
|
|
379
435
|
...clientDirOpt
|
|
380
436
|
});
|
|
381
437
|
}
|
|
@@ -402,7 +458,7 @@ async function startDevServer(opts) {
|
|
|
402
458
|
viteServer = await createViteServer(viteDevConfig(cwd, vitePort, backendPort));
|
|
403
459
|
await viteServer.listen();
|
|
404
460
|
viteServer.httpServer?.on("error", (err) => {
|
|
405
|
-
notify("error", `Vite dev server error: ${errorMessage(err)}`);
|
|
461
|
+
notify("error", `Vite dev server error: ${errorMessage$1(err)}`);
|
|
406
462
|
});
|
|
407
463
|
}
|
|
408
464
|
supervisor.adopt(initialServer);
|
package/dist/_dev-server.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import type { AgentDef } from "@alexkroman1/aai";
|
|
10
10
|
import { type Logger } from "@alexkroman1/aai/runtime";
|
|
11
11
|
import { type FSWatcher } from "chokidar";
|
|
12
|
+
import { type EvaluatedWorker } from "./_bundler.ts";
|
|
12
13
|
/**
|
|
13
14
|
* Warnings about the agent's credentials, computed against the `.env`-derived
|
|
14
15
|
* env and the shell. Pure so it is directly testable; `resolveAgentEnv` logs
|
|
@@ -22,7 +23,7 @@ import { type FSWatcher } from "chokidar";
|
|
|
22
23
|
* it at all: custom keys never fall back to the shell, so a shell export
|
|
23
24
|
* can't mask one that would be missing both here and after deploy.
|
|
24
25
|
*/
|
|
25
|
-
export declare function agentEnvWarnings(agentDef: Pick<AgentDef, "stt" | "llm" | "tts" | "s2s" | "requiredEnv">, env: Record<string, string>, shellEnv?: Record<string, string | undefined>): string[];
|
|
26
|
+
export declare function agentEnvWarnings(agentDef: Pick<AgentDef, "stt" | "llm" | "tts" | "s2s" | "requiredEnv" | "page">, env: Record<string, string>, shellEnv?: Record<string, string | undefined>): string[];
|
|
26
27
|
/**
|
|
27
28
|
* Load the agent definition by bundling agent.ts (and all its local imports)
|
|
28
29
|
* into a single ESM file, then importing that. A raw `import(agent.ts?t=...)`
|
|
@@ -35,8 +36,15 @@ export declare function agentEnvWarnings(agentDef: Pick<AgentDef, "stt" | "llm"
|
|
|
35
36
|
* errors in the agent's code propagate — the restart loop reports them and
|
|
36
37
|
* keeps the old server. Evaluation goes through the memoizing evaluator so
|
|
37
38
|
* a no-op save doesn't leak another module into the ESM registry.
|
|
39
|
+
*
|
|
40
|
+
* The project's `workflows/` directory is compiled by the same pass deploy
|
|
41
|
+
* runs (`buildWorkflows`) and rides the bundle as two string exports, which is
|
|
42
|
+
* how `aai dev` serves a workflow at all: nothing here hands the bundle to a
|
|
43
|
+
* guest, so the CLI is both ends of that contract locally. A project with no
|
|
44
|
+
* `workflows/` directory pays nothing — `buildWorkflows` resolves `undefined`
|
|
45
|
+
* without starting a builder.
|
|
38
46
|
*/
|
|
39
|
-
export declare function
|
|
47
|
+
export declare function loadWorker(cwd: string, evaluate: (code: string) => Promise<EvaluatedWorker>): Promise<EvaluatedWorker>;
|
|
40
48
|
/**
|
|
41
49
|
* True for paths that should never trigger a restart: anything inside
|
|
42
50
|
* `node_modules/` and any dot-entry (`.git/`, `.aai/`, `.DS_Store`, …).
|
|
@@ -78,9 +86,29 @@ export type DevServerOptions = {
|
|
|
78
86
|
* is unit-testable: `/websocket` MUST proxy with `ws: true` or `aai dev`
|
|
79
87
|
* with a `client.tsx` serves a page whose WebSocket never connects.
|
|
80
88
|
*
|
|
89
|
+
* **This table is the whole agent API as the browser can see it**, which is
|
|
90
|
+
* the thing to hold in mind before adding a route to `createServer`. Vite owns
|
|
91
|
+
* the port the user is told to open and answers everything not listed here
|
|
92
|
+
* itself — with a bare 404 carrying none of the agent server's headers, so the
|
|
93
|
+
* failure looks like a missing route rather than a missing proxy entry.
|
|
94
|
+
*
|
|
95
|
+
* `/workflows` is why that matters beyond voice. A WORKFLOW APP
|
|
96
|
+
* (`workflowApp()`) has no session and no socket: `page()` mounts a
|
|
97
|
+
* form and every single thing it does — listing workflows, starting a run,
|
|
98
|
+
* polling it, streaming its events — is a same-origin `fetch` under that
|
|
99
|
+
* prefix. Unproxied, the two workflow-app templates were dead on arrival under
|
|
100
|
+
* `aai dev` (`404 POST /workflows/runs` the instant the form is submitted)
|
|
101
|
+
* while the backend served the API correctly one port over. A string key
|
|
102
|
+
* prefix-matches, so this one entry covers `/runs`, `/runs/:id` and the
|
|
103
|
+
* `/runs/:id/events` SSE stream.
|
|
104
|
+
*
|
|
81
105
|
* `strictPort` because the reported URL is `http://localhost:<port>` —
|
|
82
106
|
* without it, Vite silently binds port+N when the port is busy and the
|
|
83
107
|
* printed/JSON-returned URL points at whatever else was listening.
|
|
108
|
+
*
|
|
109
|
+
* `AAI_DEV_HOST` reaches BOTH servers. Binding only the backend left Vite —
|
|
110
|
+
* the port the user is told to open — on loopback, i.e. failing exactly the
|
|
111
|
+
* case that variable exists for (`aai dev` in a container, reached from the host).
|
|
84
112
|
*/
|
|
85
113
|
export declare function viteDevConfig(cwd: string, vitePort: number, backendPort: number): import("vite").InlineConfig;
|
|
86
114
|
/**
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for the e2e test suites (e2e*.test.ts): CLI build, mock
|
|
3
|
+
* registry setup, dependency installation, and process/server utilities.
|
|
4
|
+
* Each e2e suite performs its own setup/teardown using these helpers.
|
|
5
|
+
*/
|
|
6
|
+
import type { ChildProcess } from "node:child_process";
|
|
7
|
+
import type { MockRegistry } from "./_mock-registry.ts";
|
|
8
|
+
export declare const dir: string;
|
|
9
|
+
export declare const packagesDir: string;
|
|
10
|
+
export declare const pm: "pnpm" | "npm" | "yarn";
|
|
11
|
+
export declare function aaiEnv(): NodeJS.ProcessEnv;
|
|
12
|
+
export declare function aai(aaiBin: string, args: string[], cwd: string, timeoutMs?: number): void;
|
|
13
|
+
/**
|
|
14
|
+
* Build the CLI and return the path to the built binary.
|
|
15
|
+
*
|
|
16
|
+
* Runs the package's own `build` script rather than bare `tsdown`, because
|
|
17
|
+
* the build is two steps now: tsdown, then `bundle-templates.mjs` copying the
|
|
18
|
+
* templates into `dist/`. Calling the bundler directly produced a `dist/`
|
|
19
|
+
* that no published tarball ever looks like — one with no templates in it.
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildCli(): string;
|
|
22
|
+
/**
|
|
23
|
+
* Copy a built `dist/` somewhere with no pnpm-workspace.yaml above it, so the
|
|
24
|
+
* CLI resolves templates the way an npm-installed copy does.
|
|
25
|
+
*
|
|
26
|
+
* `getMonorepoRoot()` keys off the *module's* location, so a build run from
|
|
27
|
+
* `packages/aai-cli/dist` always finds the workspace root and takes the
|
|
28
|
+
* monorepo branch — no test running in-tree can reach the bundled branch,
|
|
29
|
+
* which is the only one real users hit.
|
|
30
|
+
*/
|
|
31
|
+
export declare function detachedCli(aaiBin: string, into: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Start a mock npm registry and publish workspace packages to it.
|
|
34
|
+
* Packages are built + published inside startMockRegistry, so consumers
|
|
35
|
+
* (npm/pnpm/yarn install) resolve them exactly as they would from the real registry.
|
|
36
|
+
*/
|
|
37
|
+
export declare function startRegistry(): Promise<MockRegistry>;
|
|
38
|
+
/** Poll a health endpoint, capturing child stderr for diagnostics on timeout. */
|
|
39
|
+
export declare function waitForHealth(url: string, child?: ChildProcess, timeoutMs?: number): Promise<void>;
|
|
40
|
+
/** Wait for a child process to exit (for clean teardown). */
|
|
41
|
+
export declare function waitForExit(child: ChildProcess, timeoutMs?: number): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Install dependencies using the mock registry.
|
|
44
|
+
*
|
|
45
|
+
* **`ignore-scripts` is set HERE and nowhere else, and that placement is
|
|
46
|
+
* load-bearing.** It exists to stop a linked package's `postinstall` running
|
|
47
|
+
* during THIS install — but `npm_config_ignore_scripts` is read by every npm
|
|
48
|
+
* invocation, so while it sat in `aaiEnv()` it silently suppressed every
|
|
49
|
+
* lifecycle script in every spawned command. That took out `npm start`'s
|
|
50
|
+
* `prestart`, i.e. the build the self-hosted entrypoint cannot boot without: the
|
|
51
|
+
* child printed `> start` with no `> prestart` above it and died on the missing
|
|
52
|
+
* artifact, naming a project file rather than the env var that skipped the step.
|
|
53
|
+
*
|
|
54
|
+
* The trap generalizes past this repo — an install-time protection that is
|
|
55
|
+
* really a global one — and the second half is worse than the failure: had the
|
|
56
|
+
* artifact happened to exist from an earlier build, the test would have PASSED
|
|
57
|
+
* while never running the script it exists to exercise.
|
|
58
|
+
*/
|
|
59
|
+
/**
|
|
60
|
+
* Whether a failed install may be excused as the mock registry's npmjs
|
|
61
|
+
* passthrough failing rather than as a real dependency-resolution break.
|
|
62
|
+
*
|
|
63
|
+
* **`AAI_REQUIRE_REGISTRY` turns every excuse off**, and CI sets it — the same
|
|
64
|
+
* shape as `AAI_REQUIRE_PG`, for the same reason. A skip predicate over ERROR TEXT
|
|
65
|
+
* is a guess, and this tier is where a broken `exports` map surfaces: with three
|
|
66
|
+
* of four tests able to self-skip, a genuine break whose message happens to
|
|
67
|
+
* contain a matched substring would report green in the one job that could have
|
|
68
|
+
* caught it. Locally the excuse stays, because a developer behind a proxy has no
|
|
69
|
+
* way to make the passthrough work and a hard failure there is just noise.
|
|
70
|
+
*
|
|
71
|
+
* The predicate itself is narrowed to TRANSPORT-level failures. `fetch failed`
|
|
72
|
+
* and `network` are gone: both are substrings a real 404 can carry, which made
|
|
73
|
+
* them the two patterns most likely to excuse the thing this suite exists to
|
|
74
|
+
* find. verdaccio maps a failed upstream fetch to a plain 404, so
|
|
75
|
+
* `ERR_PNPM_FETCH_*` on a THIRD-PARTY package still counts — but never one naming
|
|
76
|
+
* our own scope, since those live in verdaccio's local storage and failing to
|
|
77
|
+
* resolve one means the published packages are actually broken.
|
|
78
|
+
*/
|
|
79
|
+
export declare function isRegistryProxyFailure(err: unknown): boolean;
|
|
80
|
+
export declare function installDeps(registry: MockRegistry, projectDir: string): void;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fault mode: run a test's dev server as a child that gets HARD-KILLED and
|
|
3
|
+
* restarted at declared points, so a suite can be run against a process that
|
|
4
|
+
* keeps dying under it.
|
|
5
|
+
*
|
|
6
|
+
* `AAI_FAULT_PROFILE=<name>` turns it on for every test that boots its server
|
|
7
|
+
* through {@link startSupervisedDevServer}; unset, the helper is an ordinary
|
|
8
|
+
* spawn and nothing about a suite changes. That is the whole shape of the
|
|
9
|
+
* feature: one env var covers the suite, and the off path has no supervisor in
|
|
10
|
+
* it at all.
|
|
11
|
+
*
|
|
12
|
+
* ## Why the kill is a SIGKILL, and why that is the only faithful option
|
|
13
|
+
*
|
|
14
|
+
* A graceful stop lets graphile-worker's runner release the queue locks it
|
|
15
|
+
* holds. That is exactly the difference that decides whether an in-flight step
|
|
16
|
+
* is redelivered, so a fault mode built on SIGTERM would exercise the recovery
|
|
17
|
+
* path that already works and never the one that does not. Measured: one hard
|
|
18
|
+
* kill of the process (or of its Postgres) strands every locked step until
|
|
19
|
+
* graphile-worker's own `interval '4 hours'` reclaim, with the run sitting
|
|
20
|
+
* `running` and a page showing "Working…" forever.
|
|
21
|
+
*
|
|
22
|
+
* ## Why there is no seed and no PRNG
|
|
23
|
+
*
|
|
24
|
+
* "Consistent fault mode" is the requirement, and the cheapest way to be
|
|
25
|
+
* consistent is to have nothing to reproduce: a profile is an ORDERED LIST of
|
|
26
|
+
* fault points keyed on logical events, so the Nth kill lands after the same
|
|
27
|
+
* observed event on every machine at every speed. Wall-clock kills are what
|
|
28
|
+
* `tmp/transcribe-load/chaos.mjs` does, and they are why its runs cannot be
|
|
29
|
+
* compared to each other.
|
|
30
|
+
*
|
|
31
|
+
* Randomized exploration is a different job with a different tool: this repo
|
|
32
|
+
* drives every randomized suite with fast-check (see "Property tests run on
|
|
33
|
+
* fast-check" in the root guide) precisely so nobody hand-rolls a seventh PRNG.
|
|
34
|
+
* A fault mode that grew a seed would be that seventh. If the fault SCHEDULE
|
|
35
|
+
* should be explored rather than declared, that belongs in a fast-check
|
|
36
|
+
* property over this module's plan type, not in an env var here.
|
|
37
|
+
*
|
|
38
|
+
* ## Triggers are log lines, and the plan is CHECKED
|
|
39
|
+
*
|
|
40
|
+
* A fault point names a pattern the server writes and which occurrence to fire
|
|
41
|
+
* on. Keying on log output couples this to strings the server happens to print,
|
|
42
|
+
* which is a real cost and is accepted for one reason: the alternative is a
|
|
43
|
+
* fault hook in production code, and a test-only mechanism should not be able to
|
|
44
|
+
* fire in production at all.
|
|
45
|
+
*
|
|
46
|
+
* The cost is paid down by {@link SupervisedServer.assertPlanConsumed}, which
|
|
47
|
+
* THROWS when a declared fault point never fired. Without it a renamed log line
|
|
48
|
+
* turns the entire mode into a no-op and the suite passes "under faults" having
|
|
49
|
+
* injected none — the failure this repo keeps paying for (a gate that reports
|
|
50
|
+
* success while checking nothing). Call it before asserting anything else.
|
|
51
|
+
*/
|
|
52
|
+
/**
|
|
53
|
+
* One kill, described by what the server DID rather than by when — the two
|
|
54
|
+
* kinds being a line it printed and a boot it completed.
|
|
55
|
+
*
|
|
56
|
+
* `downMs` holds the process down before the restart on either. The default is
|
|
57
|
+
* 0, i.e. restart immediately, because the fault under test is the DEATH rather
|
|
58
|
+
* than the downtime.
|
|
59
|
+
*
|
|
60
|
+
* Both kinds exist because a log trigger cannot reach the boot. `aai dev`
|
|
61
|
+
* announces itself through `log.success`, which JSON mode SILENCES — and JSON
|
|
62
|
+
* mode is what the e2e suite runs (and what a pipe auto-selects), so a
|
|
63
|
+
* boot-level profile keyed on a startup line matched nothing and the mode
|
|
64
|
+
* injected no faults at all. Workflow lines are unaffected: the agent server's
|
|
65
|
+
* logger writes straight to stderr rather than through `log`, so
|
|
66
|
+
* `"Workflow run started"` survives JSON mode. `afterHealthy` needs no log at
|
|
67
|
+
* all, which makes it the right kind for anything about the process lifecycle.
|
|
68
|
+
*/
|
|
69
|
+
export type FaultPoint = {
|
|
70
|
+
/** A substring of the log line to fire after. */
|
|
71
|
+
after: string;
|
|
72
|
+
/** Which occurrence of `after` to fire on. 1-based; defaults to the first. */
|
|
73
|
+
nth?: number;
|
|
74
|
+
/** Milliseconds to stay down. Defaults to 0. */
|
|
75
|
+
downMs?: number;
|
|
76
|
+
afterHealthy?: never;
|
|
77
|
+
} | {
|
|
78
|
+
/**
|
|
79
|
+
* Fire once the server has answered `/health` this many times — the first
|
|
80
|
+
* boot is 1, the generation after one restart is 2.
|
|
81
|
+
*/
|
|
82
|
+
afterHealthy: number;
|
|
83
|
+
downMs?: number;
|
|
84
|
+
after?: never;
|
|
85
|
+
nth?: never;
|
|
86
|
+
};
|
|
87
|
+
export type FaultProfile = {
|
|
88
|
+
description: string;
|
|
89
|
+
points: readonly FaultPoint[];
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* The declared profiles.
|
|
93
|
+
*
|
|
94
|
+
* Named scenarios rather than knobs, so a suite run says what it exercised and
|
|
95
|
+
* a failure names something a reader can picture. Add one here rather than
|
|
96
|
+
* assembling a plan at a call site — a plan that lives in one test cannot be
|
|
97
|
+
* run across the suite, which is the point of the mode.
|
|
98
|
+
*/
|
|
99
|
+
export declare const FAULT_PROFILES: Readonly<Record<string, FaultProfile>>;
|
|
100
|
+
/**
|
|
101
|
+
* The profile `AAI_FAULT_PROFILE` names, or undefined when the mode is off.
|
|
102
|
+
*
|
|
103
|
+
* An unknown name THROWS naming the declared set rather than degrading to off:
|
|
104
|
+
* a typo would otherwise produce a green run that injected nothing, which is
|
|
105
|
+
* indistinguishable from a healthy one and is the failure this whole module is
|
|
106
|
+
* careful about.
|
|
107
|
+
*/
|
|
108
|
+
export declare function resolveFaultProfile(name?: string | undefined): FaultProfile | undefined;
|
|
109
|
+
export type SupervisedServer = {
|
|
110
|
+
/** Origin the server answers on, stable across restarts (the port is fixed). */
|
|
111
|
+
url: string;
|
|
112
|
+
/**
|
|
113
|
+
* How many times the process has been killed and is BACK — incremented once
|
|
114
|
+
* the replacement answers `/health`, so polling this to `n` means the nth
|
|
115
|
+
* replacement is serving.
|
|
116
|
+
*/
|
|
117
|
+
restarts: () => number;
|
|
118
|
+
/**
|
|
119
|
+
* Resolve once every declared fault point has fired AND the surviving
|
|
120
|
+
* generation answers `/health`.
|
|
121
|
+
*
|
|
122
|
+
* This is the point a test should assert from: it is the only moment at which
|
|
123
|
+
* "the faults happened and the server is back" is true, and without it a
|
|
124
|
+
* request races the restart window — so every test would have to be written
|
|
125
|
+
* retry-aware to survive a profile it does not know about. Resolves
|
|
126
|
+
* immediately when the mode is off.
|
|
127
|
+
*
|
|
128
|
+
* Throws on timeout, naming the points that never fired — the same shortfall
|
|
129
|
+
* {@link assertPlanConsumed} reports, surfaced at the moment a test waited for
|
|
130
|
+
* it rather than at teardown.
|
|
131
|
+
*/
|
|
132
|
+
awaitSettled: (timeoutMs?: number) => Promise<void>;
|
|
133
|
+
/**
|
|
134
|
+
* Throw unless every declared fault point fired.
|
|
135
|
+
*
|
|
136
|
+
* For a test whose SUBJECT is the profile. A test merely running under one
|
|
137
|
+
* should call {@link awaitSettled} instead — a profile whose triggers that
|
|
138
|
+
* test never produces (a step-level trigger in a test that runs no workflow)
|
|
139
|
+
* would otherwise fail it for something that is not a bug.
|
|
140
|
+
*/
|
|
141
|
+
assertPlanConsumed: () => void;
|
|
142
|
+
/** Every line the server wrote, across all generations. */
|
|
143
|
+
lines: () => readonly string[];
|
|
144
|
+
stop: () => Promise<void>;
|
|
145
|
+
};
|
|
146
|
+
export type SupervisedDevServerOptions = {
|
|
147
|
+
/** Absolute path to the CLI entry (`bin.mjs`). */
|
|
148
|
+
aaiBin: string;
|
|
149
|
+
/** The project directory to run in. */
|
|
150
|
+
cwd: string;
|
|
151
|
+
/** A fixed port: the URL has to survive a restart. */
|
|
152
|
+
port: number;
|
|
153
|
+
env: NodeJS.ProcessEnv;
|
|
154
|
+
/** Extra `aai dev` arguments. */
|
|
155
|
+
args?: readonly string[];
|
|
156
|
+
/** Defaults to `resolveFaultProfile()`. Pass one to drive the mode from a test. */
|
|
157
|
+
profile?: FaultProfile | undefined;
|
|
158
|
+
/**
|
|
159
|
+
* How long a restarted server has to answer `/health`. Defaults to
|
|
160
|
+
* {@link HEALTH_TIMEOUT_MS}.
|
|
161
|
+
*
|
|
162
|
+
* A test seam, and one worth having: the ONLY way to exercise "the server did
|
|
163
|
+
* not survive a kill this mode inflicted" is to let that wait expire, and at
|
|
164
|
+
* the shipped 60s that is a minute of wall clock per assertion.
|
|
165
|
+
*/
|
|
166
|
+
healthTimeoutMs?: number | undefined;
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* Boot `aai dev` as a child and, under a fault profile, keep killing it.
|
|
170
|
+
*
|
|
171
|
+
* With no profile this is a plain spawn plus a health wait — the same thing the
|
|
172
|
+
* e2e suite did inline — so converting a test to it changes nothing until the
|
|
173
|
+
* env var is set.
|
|
174
|
+
*/
|
|
175
|
+
export declare function startSupervisedDevServer(opts: SupervisedDevServerOptions): Promise<SupervisedServer>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { c as
|
|
3
|
-
import { r as isDevMode, t as getMonorepoRoot } from "./_agent-
|
|
4
|
-
import { REPO_URL, downloadAndMergeTemplate } from "./_templates-
|
|
2
|
+
import { c as isEexist, l as readJson, p as writeJson } from "./_utils-B8QmtFhK.mjs";
|
|
3
|
+
import { r as isDevMode, t as getMonorepoRoot } from "./_agent-vGW5SBnX.mjs";
|
|
4
|
+
import { REPO_URL, downloadAndMergeTemplate } from "./_templates-iDGjGiPf.mjs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import fs from "node:fs/promises";
|
|
7
7
|
//#region _init.ts
|
|
@@ -45,7 +45,14 @@ function ok(data) {
|
|
|
45
45
|
data
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* Create an error result.
|
|
50
|
+
*
|
|
51
|
+
* `CommandResult<never>`, not `CommandResult<T>`: a failure carries no `data`,
|
|
52
|
+
* so the parameter was inferred as `unknown` at all ten call sites and named
|
|
53
|
+
* a type this value can never hold. `never` widens into any `CommandResult<T>`
|
|
54
|
+
* the caller declares, so `return fail(...)` still type-checks everywhere.
|
|
55
|
+
*/
|
|
49
56
|
function fail(code, error, hint) {
|
|
50
57
|
return hint ? {
|
|
51
58
|
ok: false,
|
package/dist/_output.d.ts
CHANGED
|
@@ -40,8 +40,15 @@ export declare function writeLine(line: string): Promise<void>;
|
|
|
40
40
|
export declare function installStdoutGuard(stream?: NodeJS.WriteStream): void;
|
|
41
41
|
/** Create an ok result. */
|
|
42
42
|
export declare function ok<T>(data: T): CommandResult<T>;
|
|
43
|
-
/**
|
|
44
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Create an error result.
|
|
45
|
+
*
|
|
46
|
+
* `CommandResult<never>`, not `CommandResult<T>`: a failure carries no `data`,
|
|
47
|
+
* so the parameter was inferred as `unknown` at all ten call sites and named
|
|
48
|
+
* a type this value can never hold. `never` widens into any `CommandResult<T>`
|
|
49
|
+
* the caller declares, so `return fail(...)` still type-checks everywhere.
|
|
50
|
+
*/
|
|
51
|
+
export declare function fail(code: string, error: string, hint?: string): CommandResult<never>;
|
|
45
52
|
/** Typed CLI error that carries a structured error code and optional hint. */
|
|
46
53
|
export declare class CliError extends Error {
|
|
47
54
|
readonly code: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { n as getServerInfo } from "./_agent-
|
|
3
|
-
import { n as apiRequest, t as HINT_NOT_DEPLOYED } from "./_api-client-
|
|
2
|
+
import { n as getServerInfo } from "./_agent-vGW5SBnX.mjs";
|
|
3
|
+
import { n as apiRequest, t as HINT_NOT_DEPLOYED } from "./_api-client-LgLksMhN.mjs";
|
|
4
4
|
//#region _slug-api.ts
|
|
5
5
|
/**
|
|
6
6
|
* Authenticated request against a deployed agent's slug-scoped resource
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { n as apiRequest } from "./_api-client-
|
|
2
|
+
import { a as isStringArray, i as checkedResponse, n as apiRequest } from "./_api-client-LgLksMhN.mjs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { MAX_SLUG_LENGTH, PREVIEW_SLUG_SUFFIX, VALID_SLUG_RE } from "@alexkroman1/aai/utils";
|
|
4
|
+
import { MAX_SLUG_LENGTH, PREVIEW_SLUG_SUFFIX, VALID_SLUG_RE, isRecord } from "@alexkroman1/aai/utils";
|
|
5
5
|
import { slugifyName } from "@alexkroman1/aai/slugify";
|
|
6
6
|
import { isLocalOnlyFile, snapshotWorkspaceFiles } from "@alexkroman1/aai/workspace-files";
|
|
7
7
|
//#region _studio.ts
|
|
@@ -61,7 +61,7 @@ function listStudioProjects(serverUrl, apiKey) {
|
|
|
61
61
|
return apiRequest(`${serverUrl}/studio/projects`, {
|
|
62
62
|
apiKey,
|
|
63
63
|
action: "list"
|
|
64
|
-
}).then((res) => res.projects);
|
|
64
|
+
}).then((res) => checkedResponse(res, (value) => isRecord(value) && isStringArray(value.projects), `the studio project list at ${serverUrl}`).projects);
|
|
65
65
|
}
|
|
66
66
|
/** Fetch a project, or null when it doesn't exist (the push existence probe). */
|
|
67
67
|
function fetchStudioProject(serverUrl, apiKey, project) {
|