@alexkroman1/aai-cli 9.1.0 → 10.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/dist/{_bundler-CDuYl5Gb.mjs → _bundler-B31MqYaD.mjs} +7 -33
- package/dist/_bundler.d.ts +3 -41
- package/dist/{_dev-server-BAR8Ya43.mjs → _dev-server-Bo5dSBzQ.mjs} +140 -44
- package/dist/_dev-server.d.ts +1 -8
- package/dist/_dev-vite-config.d.ts +21 -0
- package/dist/{_init-BNotRgV1.mjs → _init-BURbdHNM.mjs} +1 -1
- package/dist/{_slug-api--ZBmIGH-.mjs → _slug-api-CfGQq1Tt.mjs} +1 -1
- package/dist/{_templates-4WcKOjS5.mjs → _templates-0Zr2z6yA.mjs} +37 -2
- package/dist/_templates.d.ts +29 -0
- package/dist/{build-DvDdSwAS.mjs → build-DKC2e9o_.mjs} +1 -2
- package/dist/cli.mjs +46 -22
- package/dist/{delete-8pjH8n8n.mjs → delete-X_CRgCf6.mjs} +1 -1
- package/dist/{deploy-CjU3e-3P.mjs → deploy-CchW5pmw.mjs} +2 -2
- package/dist/{dev-dFzdvwa7.mjs → dev-BfVl1uBA.mjs} +1 -1
- package/dist/{init-DK7GpIF9.mjs → init-BRfoc2EK.mjs} +2 -2
- package/dist/{logs-D2Yhqe2w.mjs → logs-hWnJ66Bl.mjs} +2 -2
- package/dist/scaffold/CLAUDE.md +162 -54
- package/dist/scaffold/package.json +4 -6
- package/dist/scaffold/server.mjs +12 -23
- package/dist/{secret-CwgDLpSy.mjs → secret-B-3T5tvr.mjs} +2 -2
- package/dist/{studio-D3-HWIfk.mjs → studio-wVWNLREn.mjs} +4 -3
- package/dist/templates/call-audit/agent.eval.test.ts +1 -1
- package/dist/templates/call-audit/agent.test.ts +80 -8
- package/dist/templates/call-audit/client.tsx +87 -3
- package/dist/templates/call-audit/workflows/audit.ts +52 -32
- package/dist/templates/call-audit/workflows/ingest.ts +15 -15
- package/dist/templates/call-audit/workflows/media.ts +1 -1
- package/dist/templates/call-audit/workflows/summarize.ts +0 -4
- package/dist/templates/link-digest/agent.eval.test.ts +4 -2
- package/dist/templates/link-digest/agent.test.ts +25 -5
- package/dist/templates/link-digest/client.tsx +65 -7
- package/dist/templates/link-digest/workflows/digest.ts +39 -29
- package/dist/templates/podcast-digest/agent.eval.test.ts +5 -5
- package/dist/templates/podcast-digest/agent.test.ts +108 -2
- package/dist/templates/podcast-digest/client.tsx +89 -8
- package/dist/templates/podcast-digest/workflows/digest.ts +67 -48
- package/dist/templates/podcast-digest/workflows/feeds.ts +1 -4
- package/dist/templates/podcast-digest/workflows/slack.ts +5 -6
- package/dist/templates/recap-workflow/agent.eval.test.ts +72 -8
- package/dist/templates/recap-workflow/agent.test.ts +323 -83
- package/dist/templates/recap-workflow/shared.ts +4 -3
- package/dist/templates/recap-workflow/workflows/recap.ts +322 -86
- package/dist/templates/recap-workflow/workflows/tokens.ts +21 -0
- package/dist/templates/redline/agent.eval.test.ts +1 -1
- package/dist/templates/redline/agent.test.ts +1 -1
- package/dist/templates/redline/client.tsx +83 -3
- package/dist/templates/redline/workflows/redline.ts +26 -23
- package/dist/templates/research-workflow/agent.eval.test.ts +9 -8
- package/dist/templates/research-workflow/agent.test.ts +32 -6
- package/dist/templates/research-workflow/agent.ts +4 -4
- package/dist/templates/research-workflow/shared.ts +4 -3
- package/dist/templates/research-workflow/workflows/prompts.ts +1 -1
- package/dist/templates/research-workflow/workflows/research.ts +35 -38
- package/dist/templates/spoken-summary/agent.eval.test.ts +3 -3
- package/dist/templates/spoken-summary/agent.test.ts +10 -8
- package/dist/templates/spoken-summary/client.tsx +72 -3
- package/dist/templates/spoken-summary/workflows/summarize.ts +25 -21
- package/dist/templates/spoken-summary/workflows/transcribe.ts +15 -18
- package/dist/templates/transcription-workflow/agent.eval.test.ts +1 -1
- package/dist/templates/transcription-workflow/agent.test.ts +720 -16
- package/dist/templates/transcription-workflow/agent.ts +6 -4
- package/dist/templates/transcription-workflow/client.tsx +106 -308
- package/dist/templates/transcription-workflow/recover.test.ts +141 -0
- package/dist/templates/transcription-workflow/recover.ts +130 -0
- package/dist/templates/transcription-workflow/run-panel.tsx +203 -0
- package/dist/templates/transcription-workflow/total-latency.tsx +144 -0
- package/dist/templates/transcription-workflow/workflows/batch.ts +21 -24
- package/dist/templates/transcription-workflow/workflows/downsample.ts +273 -0
- package/dist/templates/transcription-workflow/workflows/normalize.ts +17 -40
- package/dist/templates/transcription-workflow/workflows/stream.ts +311 -38
- package/dist/templates/transcription-workflow/workflows/transcribe.ts +69 -32
- package/dist/templates/transcription-workflow/workflows/wav.ts +15 -4
- package/dist/worker-bundler.d.ts +0 -14
- package/dist/worker-bundler.mjs +149 -1
- package/dist/{workflow-BflATMmU.mjs → workflow-BBu5oEDw.mjs} +84 -10
- package/dist/workflow.d.ts +1 -0
- package/package.json +4 -6
- package/dist/_workflow-scan.d.ts +0 -108
- package/dist/worker-bundler-CCVEDjm1.mjs +0 -650
- package/dist/workflow-bundler.d.ts +0 -138
- package/dist/{_studio-CKrsixd0.mjs → _studio-B1waXMuR.mjs} +1 -1
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { f as validateAgentExport } from "./_utils-B8QmtFhK.mjs";
|
|
3
3
|
import { t as buildClient } from "./client-bundler-CWnG42cU.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { buildWorker } from "./worker-bundler.mjs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { pathToFileURL } from "node:url";
|
|
7
|
-
import { omitUndefined } from "@alexkroman1/aai/utils";
|
|
8
7
|
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
9
8
|
import { tmpdir } from "node:os";
|
|
10
9
|
import { hash } from "node:crypto";
|
|
@@ -18,14 +17,10 @@ import { hash } from "node:crypto";
|
|
|
18
17
|
* config and evaluates nothing, so every read of one happens here.
|
|
19
18
|
*/
|
|
20
19
|
async function buildAgentBundle(cwd, opts = {}) {
|
|
21
|
-
const [
|
|
20
|
+
const [clientFiles, worker] = await Promise.all([buildClient(cwd), buildWorker(cwd, opts)]);
|
|
22
21
|
return {
|
|
23
|
-
worker
|
|
24
|
-
|
|
25
|
-
workflows
|
|
26
|
-
}),
|
|
27
|
-
clientFiles,
|
|
28
|
-
...omitUndefined({ workflows })
|
|
22
|
+
worker,
|
|
23
|
+
clientFiles
|
|
29
24
|
};
|
|
30
25
|
}
|
|
31
26
|
/**
|
|
@@ -47,27 +42,10 @@ async function buildAgentBundle(cwd, opts = {}) {
|
|
|
47
42
|
* experimental.
|
|
48
43
|
*/
|
|
49
44
|
async function evalWorkerBundle(code) {
|
|
50
|
-
return (await evalWorkerWithWorkflows(code)).agent;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* {@link evalWorkerBundle}, keeping the workflow exports the AgentDef cannot
|
|
54
|
-
* carry.
|
|
55
|
-
*
|
|
56
|
-
* A separate function rather than a wider return from `evalWorkerBundle`,
|
|
57
|
-
* because every other caller wants the agent and nothing else — and the two
|
|
58
|
-
* strings are ~76 KB that a deploy path has no use for.
|
|
59
|
-
*/
|
|
60
|
-
async function evalWorkerWithWorkflows(code) {
|
|
61
45
|
const mod = await importWorkerModule(code);
|
|
62
46
|
const agentDef = mod.default ?? mod;
|
|
63
47
|
validateAgentExport(agentDef);
|
|
64
|
-
|
|
65
|
-
const stepCode = mod.__aaiStepCode;
|
|
66
|
-
return {
|
|
67
|
-
agent: agentDef,
|
|
68
|
-
workflowCode: typeof workflowCode === "string" ? workflowCode : void 0,
|
|
69
|
-
stepCode: typeof stepCode === "string" ? stepCode : void 0
|
|
70
|
-
};
|
|
48
|
+
return agentDef;
|
|
71
49
|
}
|
|
72
50
|
/** Import a built worker from a temp file. See {@link evalWorkerBundle}. */
|
|
73
51
|
async function importWorkerModule(code) {
|
|
@@ -100,16 +78,12 @@ async function evalWorkerConfig(code) {
|
|
|
100
78
|
return (await importWorkerModule(code)).__aaiConfig;
|
|
101
79
|
}
|
|
102
80
|
/**
|
|
103
|
-
* Memoizing wrapper around
|
|
81
|
+
* Memoizing wrapper around {@link evalWorkerBundle} for long-lived callers
|
|
104
82
|
* (the dev server): byte-identical worker code returns the previously
|
|
105
83
|
* evaluated result without touching the ESM registry. No-op saves and formatter
|
|
106
84
|
* churn are the common watcher events, so this caps the registry leak (see
|
|
107
85
|
* `evalWorkerBundle`) to genuinely-new bundles — the residual one-module-per-
|
|
108
86
|
* distinct-build leak is accepted for the reasons documented there.
|
|
109
|
-
*
|
|
110
|
-
* It evaluates the WORKFLOW-carrying variant because its one caller is
|
|
111
|
-
* `aai dev`, which has no guest to re-read the bundle: dropping the two strings
|
|
112
|
-
* here is what left the dev server unable to serve a workflow at all.
|
|
113
87
|
*/
|
|
114
88
|
function createWorkerEvaluator() {
|
|
115
89
|
let lastHash;
|
|
@@ -117,7 +91,7 @@ function createWorkerEvaluator() {
|
|
|
117
91
|
return async (code) => {
|
|
118
92
|
const codeHash = hash("sha256", code);
|
|
119
93
|
if (lastWorker && codeHash === lastHash) return lastWorker;
|
|
120
|
-
const worker = await
|
|
94
|
+
const worker = await evalWorkerBundle(code);
|
|
121
95
|
lastHash = codeHash;
|
|
122
96
|
lastWorker = worker;
|
|
123
97
|
return worker;
|
package/dist/_bundler.d.ts
CHANGED
|
@@ -1,21 +1,11 @@
|
|
|
1
1
|
import type { AgentDef } from "@alexkroman1/aai";
|
|
2
2
|
import { type BuildWorkerOptions } from "./worker-bundler.ts";
|
|
3
|
-
|
|
4
|
-
/** Output from the bundler: worker ESM + client files + workflow artifacts. */
|
|
3
|
+
/** Output from the bundler: worker ESM + client files. */
|
|
5
4
|
export type DirectoryBundleOutput = {
|
|
6
5
|
/** ESM bundle of agent.ts (tool execute functions + hook handlers). */
|
|
7
6
|
worker: string;
|
|
8
7
|
/** Static client files from Vite build. Empty if no client.tsx. */
|
|
9
8
|
clientFiles: Record<string, string>;
|
|
10
|
-
/**
|
|
11
|
-
* The project's durable workflows, or undefined when it declares none.
|
|
12
|
-
*
|
|
13
|
-
* Built here rather than in the guest because the transform is per TENANT: the
|
|
14
|
-
* guest image is baked once and serves many agents, so there is no
|
|
15
|
-
* `workflows/` directory in existence when it is built. See
|
|
16
|
-
* `workflow-bundler.ts`.
|
|
17
|
-
*/
|
|
18
|
-
workflows?: WorkflowBundleOutput;
|
|
19
9
|
};
|
|
20
10
|
/**
|
|
21
11
|
* Bundle an agent directory: build agent.ts into worker ESM + client files.
|
|
@@ -45,30 +35,6 @@ export declare function buildAgentBundle(cwd: string, opts?: BuildWorkerOptions)
|
|
|
45
35
|
* experimental.
|
|
46
36
|
*/
|
|
47
37
|
export declare function evalWorkerBundle(code: string): Promise<AgentDef>;
|
|
48
|
-
/**
|
|
49
|
-
* The agent plus the compiled workflow surface a worker carries.
|
|
50
|
-
*
|
|
51
|
-
* `evalWorkerBundle` returns only the `AgentDef`, which is all a deploy needs —
|
|
52
|
-
* the guest re-reads the bundle itself. `aai dev` never hands the bundle to a
|
|
53
|
-
* guest, so this is the only path by which the workflow code reaches a running
|
|
54
|
-
* server locally, and dropping it is why `aai dev` served no workflows at first.
|
|
55
|
-
*/
|
|
56
|
-
export type EvaluatedWorker = {
|
|
57
|
-
agent: AgentDef;
|
|
58
|
-
/** `__aaiWorkflowCode` — the flow bundle, for `workflowEntrypoint`. */
|
|
59
|
-
workflowCode: string | undefined;
|
|
60
|
-
/** `__aaiStepCode` — evaluated to register the project's step functions. */
|
|
61
|
-
stepCode: string | undefined;
|
|
62
|
-
};
|
|
63
|
-
/**
|
|
64
|
-
* {@link evalWorkerBundle}, keeping the workflow exports the AgentDef cannot
|
|
65
|
-
* carry.
|
|
66
|
-
*
|
|
67
|
-
* A separate function rather than a wider return from `evalWorkerBundle`,
|
|
68
|
-
* because every other caller wants the agent and nothing else — and the two
|
|
69
|
-
* strings are ~76 KB that a deploy path has no use for.
|
|
70
|
-
*/
|
|
71
|
-
export declare function evalWorkerWithWorkflows(code: string): Promise<EvaluatedWorker>;
|
|
72
38
|
/**
|
|
73
39
|
* The config a built worker describes about itself — its `__aaiConfig`
|
|
74
40
|
* export, generated by `buildWorker`'s wrapper entry (`toAgentConfig(def)`
|
|
@@ -84,15 +50,11 @@ export declare function evalWorkerWithWorkflows(code: string): Promise<Evaluated
|
|
|
84
50
|
*/
|
|
85
51
|
export declare function evalWorkerConfig(code: string): Promise<unknown>;
|
|
86
52
|
/**
|
|
87
|
-
* Memoizing wrapper around
|
|
53
|
+
* Memoizing wrapper around {@link evalWorkerBundle} for long-lived callers
|
|
88
54
|
* (the dev server): byte-identical worker code returns the previously
|
|
89
55
|
* evaluated result without touching the ESM registry. No-op saves and formatter
|
|
90
56
|
* churn are the common watcher events, so this caps the registry leak (see
|
|
91
57
|
* `evalWorkerBundle`) to genuinely-new bundles — the residual one-module-per-
|
|
92
58
|
* distinct-build leak is accepted for the reasons documented there.
|
|
93
|
-
*
|
|
94
|
-
* It evaluates the WORKFLOW-carrying variant because its one caller is
|
|
95
|
-
* `aai dev`, which has no guest to re-read the bundle: dropping the two strings
|
|
96
|
-
* here is what left the dev server unable to serve a workflow at all.
|
|
97
59
|
*/
|
|
98
|
-
export declare function createWorkerEvaluator(): (code: string) => Promise<
|
|
60
|
+
export declare function createWorkerEvaluator(): (code: string) => Promise<AgentDef>;
|
|
@@ -3,18 +3,18 @@ import { i as outputSilenced, r as notify } from "./_ui-u7T4YooX.mjs";
|
|
|
3
3
|
import { i as errorCode, o as errorMessage$1 } from "./_utils-B8QmtFhK.mjs";
|
|
4
4
|
import { n as fallbackHtmlPlugin } from "./client-bundler-CWnG42cU.mjs";
|
|
5
5
|
import { t as DEDUPED_PEERS } from "./_vite-env-BNveawd1.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { buildWorker } from "./worker-bundler.mjs";
|
|
7
7
|
import { typecheckProject } from "./typecheck.mjs";
|
|
8
8
|
import { n as ensureApiKey } from "./_config-CmJOFsAP.mjs";
|
|
9
|
-
import { n as createWorkerEvaluator } from "./_bundler-
|
|
9
|
+
import { n as createWorkerEvaluator } from "./_bundler-B31MqYaD.mjs";
|
|
10
10
|
import { t as resolveServerEnv } from "./_server-common-6e0QI4mq.mjs";
|
|
11
|
-
import { existsSync } from "node:fs";
|
|
11
|
+
import { existsSync, statSync } from "node:fs";
|
|
12
12
|
import path from "node:path";
|
|
13
13
|
import { omitUndefined } from "@alexkroman1/aai/utils";
|
|
14
|
-
import { createCoalescingRunner, sleep } from "@alexkroman1/aai/internal";
|
|
14
|
+
import { createCoalescingRunner, requestPath, sleep } from "@alexkroman1/aai/internal";
|
|
15
15
|
import { agentConfigWarnings } from "@alexkroman1/aai/manifest";
|
|
16
|
-
import { WORKFLOW_API_PREFIX, createRuntime, createServer, ensureSessionStateSchema, requiredProviderEnvVars, withHostCredentialFallback } from "@alexkroman1/aai-runtime";
|
|
17
|
-
import {
|
|
16
|
+
import { DEFAULT_LISTEN_HOST, WORKFLOW_API_PREFIX, createRuntime, createServer, ensureSessionStateSchema, ensureWorkflowJournalSchema, requiredProviderEnvVars, withHostCredentialFallback } from "@alexkroman1/aai-runtime";
|
|
17
|
+
import { WORKFLOW_DATA_DIR_ENV, consoleLogger, createMemoryJournal, handleWorkflowRequest, publishStepEnv } from "@alexkroman1/aai-runtime/internal";
|
|
18
18
|
import { defaultClientDir } from "@alexkroman1/aai-ui/client-dir";
|
|
19
19
|
import { watch } from "chokidar";
|
|
20
20
|
import getPort, { portNumbers } from "get-port";
|
|
@@ -286,6 +286,77 @@ function createDevTypecheck(cwd, report = notify) {
|
|
|
286
286
|
* file past the length cap.
|
|
287
287
|
*/
|
|
288
288
|
/**
|
|
289
|
+
* The request under {@link WORKFLOW_API_PREFIX} that VITE must answer, not the
|
|
290
|
+
* proxy — or `undefined` for every request the workflow API owns.
|
|
291
|
+
*
|
|
292
|
+
* `/workflows` is a proxy prefix key AND the directory the SDK tells authors to
|
|
293
|
+
* put workflow bodies in, so the two claim the same URL space. A string key
|
|
294
|
+
* prefix-matches, which is what makes one entry cover `/runs/:id/events` — and
|
|
295
|
+
* it also swallowed `transcription-workflow`'s `client.tsx:173`, a value import
|
|
296
|
+
* of `./workflows/stitch.ts`. Vite rewrites that specifier to the absolute
|
|
297
|
+
* `/workflows/stitch.ts` during import analysis, the proxy claimed it, and the
|
|
298
|
+
* agent server answered the `404 {"error":"Not found"}` its workflow router
|
|
299
|
+
* gives any unmatched path under the prefix. The browser refuses a module
|
|
300
|
+
* served as `application/json`, so the page rendered BLANK — and this lands on
|
|
301
|
+
* the naming convention every workflow template follows, not on one template's
|
|
302
|
+
* bad luck.
|
|
303
|
+
*
|
|
304
|
+
* ## Why the filesystem decides, and not a route table
|
|
305
|
+
*
|
|
306
|
+
* The obvious fix is to narrow the proxy to the API's real shape: the fourteen
|
|
307
|
+
* routes are enumerable (`/runs`, `/runs/:id`, `…/events`, `…/stream`,
|
|
308
|
+
* `…/wake`, `/uploads`, `…/parts`, `…/info`). That restates `aai-runtime`'s
|
|
309
|
+
* router here, in a package that cannot see it — and a route added there but
|
|
310
|
+
* missing from the copy 404s under `aai dev` while working deployed, which is
|
|
311
|
+
* the exact silent failure this proxy table exists to prevent and the reason
|
|
312
|
+
* the entry is keyed off the SDK's own constant rather than a literal. The
|
|
313
|
+
* drift runs the wrong way.
|
|
314
|
+
*
|
|
315
|
+
* Enumerating what VITE owns inverts it: a new API route has no file behind it
|
|
316
|
+
* and is proxied with nothing to update. The only thing that can shadow the API
|
|
317
|
+
* is a real file at a real route's exact path — `workflows/runs` with no
|
|
318
|
+
* extension, or a `workflows/runs/` directory — where an unreachable API is the
|
|
319
|
+
* lesser of the two failures and the author can see the file that caused it.
|
|
320
|
+
* Note `workflows/runs.ts` is NOT such a file: `/workflows/runs.ts` is neither
|
|
321
|
+
* `/workflows/runs` nor under `/workflows/runs/`.
|
|
322
|
+
*
|
|
323
|
+
* Moving the API off `/workflows` was the third option and is the expensive one
|
|
324
|
+
* — it is a wire change reaching the SDK client, the platform's broker and
|
|
325
|
+
* every deployed agent, to buy what a file check buys locally.
|
|
326
|
+
*
|
|
327
|
+
* ## Only an EXACT file counts
|
|
328
|
+
*
|
|
329
|
+
* No extension resolution, deliberately: `/workflows/runs` must not find
|
|
330
|
+
* `workflows/runs.ts`, and a workflow body named `runs.ts` is entirely
|
|
331
|
+
* plausible. Nothing is lost by it — Vite resolves specifiers SERVER-side and
|
|
332
|
+
* rewrites them to paths that exist, so an extensionless `./workflows/stitch`
|
|
333
|
+
* reaches the browser as `/workflows/stitch.ts` (verified: both the explicit
|
|
334
|
+
* and the bare import in one module come back with `.ts` appended). The browser
|
|
335
|
+
* only ever asks for files Vite already found.
|
|
336
|
+
*
|
|
337
|
+
* A path escaping the root is refused rather than served — a raw client can
|
|
338
|
+
* send `..` where a browser would normalize it — and a malformed percent-escape
|
|
339
|
+
* is left to the API, which is where a path we cannot resolve belongs.
|
|
340
|
+
*/
|
|
341
|
+
function workflowPathServedByVite(root, rawUrl) {
|
|
342
|
+
if (rawUrl === void 0) return void 0;
|
|
343
|
+
let decoded;
|
|
344
|
+
try {
|
|
345
|
+
decoded = decodeURIComponent(requestPath(rawUrl));
|
|
346
|
+
} catch {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
const base = path.resolve(root);
|
|
350
|
+
const resolved = path.resolve(base, `.${decoded}`);
|
|
351
|
+
if (resolved !== base && !resolved.startsWith(base + path.sep)) return void 0;
|
|
352
|
+
try {
|
|
353
|
+
if (!statSync(resolved).isFile()) return void 0;
|
|
354
|
+
} catch {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
return rawUrl;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
289
360
|
* Vite dev-server config for the client SPA. Extracted so the proxy wiring
|
|
290
361
|
* is unit-testable: `/websocket` MUST proxy with `ws: true` or `aai dev`
|
|
291
362
|
* with a `client.tsx` serves a page whose WebSocket never connects.
|
|
@@ -314,6 +385,27 @@ function createDevTypecheck(cwd, report = notify) {
|
|
|
314
385
|
* the port the user is told to open — on loopback, i.e. failing exactly the
|
|
315
386
|
* case that variable exists for (`aai dev` in a container, reached from the host).
|
|
316
387
|
*
|
|
388
|
+
* ## With no `AAI_DEV_HOST` the bind host is the BACKEND's, not Vite's default
|
|
389
|
+
*
|
|
390
|
+
* Vite's default `server.host` is the hostname `localhost`, so Node binds
|
|
391
|
+
* whatever `getaddrinfo` returns first — `::1` on macOS, usually `127.0.0.1` on
|
|
392
|
+
* Linux. Measured here: `vite.httpServer.address()` reported
|
|
393
|
+
* `{ address: "::1", family: "IPv6" }` and `http://127.0.0.1:<port>` was
|
|
394
|
+
* ECONNREFUSED while `http://localhost:<port>` worked. So the same command
|
|
395
|
+
* produced different reachability per machine, and `aai dev` reports
|
|
396
|
+
* `http://localhost:<port>` either way — a caller that resolves IPv4-only, or
|
|
397
|
+
* dials the literal, gets a connection refused against a server that is up.
|
|
398
|
+
*
|
|
399
|
+
* The two halves of `aai dev` also disagreed: `createServer` binds
|
|
400
|
+
* {@link DEFAULT_LISTEN_HOST} explicitly, Vite took its own default, and only a
|
|
401
|
+
* set `AAI_DEV_HOST` brought them back together. Taking the same constant is
|
|
402
|
+
* what makes them agree by construction rather than by two matching literals.
|
|
403
|
+
*
|
|
404
|
+
* IPv4 loopback is the SAFE side of that choice rather than a coin flip:
|
|
405
|
+
* browsers, curl and undici all try every address `localhost` resolves to, so a
|
|
406
|
+
* `127.0.0.1` bind stays reachable as `localhost`, while a `::1` bind is not
|
|
407
|
+
* reachable at all from a client holding the IPv4 literal.
|
|
408
|
+
*
|
|
317
409
|
* ## The target is an IP LITERAL, and that is a fix rather than a style choice
|
|
318
410
|
*
|
|
319
411
|
* `127.0.0.1`, never `localhost`. Vite opens a FRESH upstream connection for
|
|
@@ -359,7 +451,7 @@ function viteDevConfig(cwd, vitePort, backendPort) {
|
|
|
359
451
|
server: {
|
|
360
452
|
port: vitePort,
|
|
361
453
|
strictPort: true,
|
|
362
|
-
|
|
454
|
+
host: devBindHost() ?? DEFAULT_LISTEN_HOST,
|
|
363
455
|
proxy: {
|
|
364
456
|
"/health": target,
|
|
365
457
|
"/client-config": target,
|
|
@@ -367,7 +459,10 @@ function viteDevConfig(cwd, vitePort, backendPort) {
|
|
|
367
459
|
target,
|
|
368
460
|
ws: true
|
|
369
461
|
},
|
|
370
|
-
[WORKFLOW_API_PREFIX]:
|
|
462
|
+
[WORKFLOW_API_PREFIX]: {
|
|
463
|
+
target,
|
|
464
|
+
bypass: (req) => workflowPathServedByVite(cwd, req.url)
|
|
465
|
+
}
|
|
371
466
|
}
|
|
372
467
|
}
|
|
373
468
|
};
|
|
@@ -430,20 +525,9 @@ async function resolveAgentEnv(root, agentDef) {
|
|
|
430
525
|
* keeps the old server. Evaluation goes through the memoizing evaluator so
|
|
431
526
|
* a no-op save doesn't leak another module into the ESM registry.
|
|
432
527
|
*
|
|
433
|
-
* The project's `workflows/` directory is compiled by the same pass deploy
|
|
434
|
-
* runs (`buildWorkflows`) and rides the bundle as two string exports, which is
|
|
435
|
-
* how `aai dev` serves a workflow at all: nothing here hands the bundle to a
|
|
436
|
-
* guest, so the CLI is both ends of that contract locally. A project with no
|
|
437
|
-
* `workflows/` directory pays nothing — `buildWorkflows` resolves `undefined`
|
|
438
|
-
* without starting a builder.
|
|
439
528
|
*/
|
|
440
529
|
async function loadWorker(cwd, evaluate) {
|
|
441
|
-
|
|
442
|
-
for (const warning of workflows?.warnings ?? []) notify("warn", warning);
|
|
443
|
-
return evaluate(await buildWorker(cwd, {
|
|
444
|
-
runtime: false,
|
|
445
|
-
workflows
|
|
446
|
-
}));
|
|
530
|
+
return evaluate(await buildWorker(cwd, { runtime: false }));
|
|
447
531
|
}
|
|
448
532
|
/**
|
|
449
533
|
* True for paths that should never trigger a restart: anything inside
|
|
@@ -496,25 +580,38 @@ function watchDirectory(dir, onChange) {
|
|
|
496
580
|
*/
|
|
497
581
|
async function startDevServer(opts) {
|
|
498
582
|
const { cwd, port } = opts;
|
|
583
|
+
if (!process.env[WORKFLOW_DATA_DIR_ENV]?.trim()) process.env[WORKFLOW_DATA_DIR_ENV] = path.join(cwd, ".workflow-data");
|
|
499
584
|
const hasClient = existsSync(path.join(cwd, "client.tsx"));
|
|
500
585
|
const backendPort = hasClient ? await getPort({ port: portNumbers(port + 1, port + 100) }) : port;
|
|
501
586
|
const vitePort = port;
|
|
502
587
|
const clientDirOpt = hasClient ? {} : { clientDir: defaultClientDir() };
|
|
503
588
|
const evaluateWorker = createWorkerEvaluator();
|
|
504
589
|
const devLogger = createDevLogger(outputSilenced());
|
|
505
|
-
let workflowWorldStarted = false;
|
|
506
590
|
/**
|
|
507
591
|
* Whether the session-state tables have been ensured this process.
|
|
508
592
|
*
|
|
509
|
-
* Once
|
|
510
|
-
*
|
|
511
|
-
*
|
|
593
|
+
* Once per dev server rather than per restart: a rebuild replaces the routes,
|
|
594
|
+
* not the storage behind them, and re-running the DDL on every file save would
|
|
595
|
+
* be two round trips per keystroke burst.
|
|
512
596
|
*/
|
|
513
597
|
let sessionSchemaEnsured = false;
|
|
598
|
+
/**
|
|
599
|
+
* Where a databaseless project's durable runs live for this whole `aai dev`.
|
|
600
|
+
*
|
|
601
|
+
* STORAGE per PROCESS, CODE per BUILD — the split the deleted DevKit world
|
|
602
|
+
* made on our behalf. Every save rebuilds the runtime (that is what reloads a
|
|
603
|
+
* workflow BODY) and a rebuild handed no journal builds a fresh one inside the
|
|
604
|
+
* engine, so a run started before a save was gone after it and
|
|
605
|
+
* `GET /workflows/runs/:id` 404'd for a run the page was still polling.
|
|
606
|
+
* Deliberately NOT the whole client: reusing build 1's would freeze every body
|
|
607
|
+
* at build 1. `RuntimeOptions.journal` carries the rest, including what this
|
|
608
|
+
* does NOT restore (a `ctx.sleep` parked across the save still needs a
|
|
609
|
+
* delivery) and why it cannot demote a postgres or platform journal.
|
|
610
|
+
*/
|
|
611
|
+
const journal = createMemoryJournal();
|
|
514
612
|
/** Full build sequence, shared by initial startup and every restart. */
|
|
515
613
|
async function buildServer() {
|
|
516
|
-
const
|
|
517
|
-
const agentDef = worker.agent;
|
|
614
|
+
const agentDef = await loadWorker(cwd, evaluateWorker);
|
|
518
615
|
const env = await resolveAgentEnv(cwd, agentDef);
|
|
519
616
|
if (env.DATABASE_URL && !sessionSchemaEnsured) {
|
|
520
617
|
sessionSchemaEnsured = true;
|
|
@@ -522,33 +619,32 @@ async function startDevServer(opts) {
|
|
|
522
619
|
url: env.DATABASE_URL,
|
|
523
620
|
logger: devLogger
|
|
524
621
|
});
|
|
622
|
+
await ensureWorkflowJournalSchema({
|
|
623
|
+
url: env.DATABASE_URL,
|
|
624
|
+
logger: devLogger
|
|
625
|
+
});
|
|
525
626
|
}
|
|
526
|
-
const world = configureWorkflowWorld({
|
|
527
|
-
databaseUrl: env.DATABASE_URL,
|
|
528
|
-
port: backendPort,
|
|
529
|
-
dataDir: path.join(cwd, ".workflow-data")
|
|
530
|
-
});
|
|
531
627
|
publishStepEnv(env);
|
|
532
|
-
const workflows = await createWorkflowSurface(worker.workflowCode, worker.stepCode);
|
|
533
|
-
if (workflows && !workflowWorldStarted) {
|
|
534
|
-
workflowWorldStarted = true;
|
|
535
|
-
await startWorkflowWorldIfDeclared(true, world);
|
|
536
|
-
}
|
|
537
628
|
const providerEnv = withHostCredentialFallback(env);
|
|
629
|
+
const runtime = createRuntime({
|
|
630
|
+
agent: agentDef,
|
|
631
|
+
env,
|
|
632
|
+
providerEnv,
|
|
633
|
+
logger: devLogger,
|
|
634
|
+
journal,
|
|
635
|
+
publicUrl: process.env.PUBLIC_URL?.trim() || `http://localhost:${backendPort}`
|
|
636
|
+
});
|
|
538
637
|
return createServer({
|
|
539
|
-
runtime
|
|
540
|
-
agent: agentDef,
|
|
541
|
-
env,
|
|
542
|
-
providerEnv,
|
|
543
|
-
logger: devLogger,
|
|
544
|
-
publicUrl: process.env.PUBLIC_URL?.trim() || `http://localhost:${backendPort}`
|
|
545
|
-
}),
|
|
638
|
+
runtime,
|
|
546
639
|
name: agentDef.name,
|
|
547
640
|
env: hostModeEnv(providerEnv),
|
|
548
641
|
hostBaseAgent: agentDef,
|
|
549
642
|
greeting: agentDef.greeting,
|
|
550
643
|
...omitUndefined({ page: agentDef.page }),
|
|
551
|
-
request: (req, res, url, method) => handleWorkflowRequest(
|
|
644
|
+
request: (req, res, url, method) => handleWorkflowRequest(req, res, url, method, {
|
|
645
|
+
deliver: () => runtime.deliverWorkflow,
|
|
646
|
+
logger: devLogger
|
|
647
|
+
}),
|
|
552
648
|
...clientDirOpt
|
|
553
649
|
});
|
|
554
650
|
}
|
package/dist/_dev-server.d.ts
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { AgentDef } from "@alexkroman1/aai";
|
|
10
10
|
import { type FSWatcher } from "chokidar";
|
|
11
|
-
import { type EvaluatedWorker } from "./_bundler.ts";
|
|
12
11
|
/**
|
|
13
12
|
* Warnings about the agent's credentials, computed against the `.env`-derived
|
|
14
13
|
* env and the shell. Pure so it is directly testable; `resolveAgentEnv` logs
|
|
@@ -36,14 +35,8 @@ export declare function agentEnvWarnings(agentDef: Pick<AgentDef, "stt" | "llm"
|
|
|
36
35
|
* keeps the old server. Evaluation goes through the memoizing evaluator so
|
|
37
36
|
* a no-op save doesn't leak another module into the ESM registry.
|
|
38
37
|
*
|
|
39
|
-
* The project's `workflows/` directory is compiled by the same pass deploy
|
|
40
|
-
* runs (`buildWorkflows`) and rides the bundle as two string exports, which is
|
|
41
|
-
* how `aai dev` serves a workflow at all: nothing here hands the bundle to a
|
|
42
|
-
* guest, so the CLI is both ends of that contract locally. A project with no
|
|
43
|
-
* `workflows/` directory pays nothing — `buildWorkflows` resolves `undefined`
|
|
44
|
-
* without starting a builder.
|
|
45
38
|
*/
|
|
46
|
-
export declare function loadWorker(cwd: string, evaluate: (code: string) => Promise<
|
|
39
|
+
export declare function loadWorker(cwd: string, evaluate: (code: string) => Promise<AgentDef>): Promise<AgentDef>;
|
|
47
40
|
/**
|
|
48
41
|
* True for paths that should never trigger a restart: anything inside
|
|
49
42
|
* `node_modules/` and any dot-entry (`.git/`, `.aai/`, `.DS_Store`, …).
|
|
@@ -35,6 +35,27 @@
|
|
|
35
35
|
* the port the user is told to open — on loopback, i.e. failing exactly the
|
|
36
36
|
* case that variable exists for (`aai dev` in a container, reached from the host).
|
|
37
37
|
*
|
|
38
|
+
* ## With no `AAI_DEV_HOST` the bind host is the BACKEND's, not Vite's default
|
|
39
|
+
*
|
|
40
|
+
* Vite's default `server.host` is the hostname `localhost`, so Node binds
|
|
41
|
+
* whatever `getaddrinfo` returns first — `::1` on macOS, usually `127.0.0.1` on
|
|
42
|
+
* Linux. Measured here: `vite.httpServer.address()` reported
|
|
43
|
+
* `{ address: "::1", family: "IPv6" }` and `http://127.0.0.1:<port>` was
|
|
44
|
+
* ECONNREFUSED while `http://localhost:<port>` worked. So the same command
|
|
45
|
+
* produced different reachability per machine, and `aai dev` reports
|
|
46
|
+
* `http://localhost:<port>` either way — a caller that resolves IPv4-only, or
|
|
47
|
+
* dials the literal, gets a connection refused against a server that is up.
|
|
48
|
+
*
|
|
49
|
+
* The two halves of `aai dev` also disagreed: `createServer` binds
|
|
50
|
+
* {@link DEFAULT_LISTEN_HOST} explicitly, Vite took its own default, and only a
|
|
51
|
+
* set `AAI_DEV_HOST` brought them back together. Taking the same constant is
|
|
52
|
+
* what makes them agree by construction rather than by two matching literals.
|
|
53
|
+
*
|
|
54
|
+
* IPv4 loopback is the SAFE side of that choice rather than a coin flip:
|
|
55
|
+
* browsers, curl and undici all try every address `localhost` resolves to, so a
|
|
56
|
+
* `127.0.0.1` bind stays reachable as `localhost`, while a `::1` bind is not
|
|
57
|
+
* reachable at all from a client holding the IPv4 literal.
|
|
58
|
+
*
|
|
38
59
|
* ## The target is an IP LITERAL, and that is a fix rather than a style choice
|
|
39
60
|
*
|
|
40
61
|
* `127.0.0.1`, never `localhost`. Vite opens a FRESH upstream connection for
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { c as isEexist, l as readJson, p as writeJson } from "./_utils-B8QmtFhK.mjs";
|
|
3
3
|
import { r as isDevMode, t as getMonorepoRoot } from "./_agent-BTwZJu4P.mjs";
|
|
4
|
-
import { REPO_URL, downloadAndMergeTemplate } from "./_templates-
|
|
4
|
+
import { REPO_URL, downloadAndMergeTemplate } from "./_templates-0Zr2z6yA.mjs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import fs from "node:fs/promises";
|
|
7
7
|
//#region _init.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { n as getServerInfo } from "./_agent-BTwZJu4P.mjs";
|
|
3
|
-
import { l as HINT_NOT_DEPLOYED, s as studioProjectApiUrl, u as apiRequest } from "./_studio-
|
|
3
|
+
import { l as HINT_NOT_DEPLOYED, s as studioProjectApiUrl, u as apiRequest } from "./_studio-B1waXMuR.mjs";
|
|
4
4
|
//#region _slug-api.ts
|
|
5
5
|
/**
|
|
6
6
|
* Authenticated request against a deployed agent's slug-scoped resource
|
|
@@ -5,6 +5,7 @@ import { existsSync } from "node:fs";
|
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { isRecord } from "@alexkroman1/aai/utils";
|
|
7
7
|
import fs from "node:fs/promises";
|
|
8
|
+
import { IGNORED_WORKSPACE_DIRS, isLocalOnlyFile } from "@alexkroman1/aai/workspace-files";
|
|
8
9
|
const REPO_URL = `https://github.com/alexkroman/agent`;
|
|
9
10
|
/**
|
|
10
11
|
* Templates as shipped inside the published tarball, copied into `dist/` by
|
|
@@ -144,11 +145,44 @@ async function layerScaffold(targetDir) {
|
|
|
144
145
|
await fs.cp(dir, targetDir, {
|
|
145
146
|
recursive: true,
|
|
146
147
|
force: false,
|
|
147
|
-
errorOnExist: false
|
|
148
|
+
errorOnExist: false,
|
|
149
|
+
filter: templateCopyFilter
|
|
148
150
|
});
|
|
149
151
|
await layerScaffoldManifest(dir, targetDir);
|
|
150
152
|
}
|
|
151
153
|
/**
|
|
154
|
+
* `fs.cp` filter for every copy OUT of a template or the scaffold — the runtime
|
|
155
|
+
* ones here and the build-time one in `bundle-templates.mjs`.
|
|
156
|
+
*
|
|
157
|
+
* A template directory is also a runnable project, so a developer who runs
|
|
158
|
+
* `aai dev`, `aai build` or `aai publish` inside one leaves build output and
|
|
159
|
+
* machine state in it: `.aai/` (which holds `project.json` — a SLUG and a
|
|
160
|
+
* `serverUrl` — plus a built client), `.workflow-data/`, `node_modules/`, a
|
|
161
|
+
* `.env`. None of it is git-tracked, and an unfiltered `fs.cp` copied all of it
|
|
162
|
+
* anyway, to both destinations:
|
|
163
|
+
*
|
|
164
|
+
* - into every scaffolded project, so `aai init foo --template bar` produced a
|
|
165
|
+
* directory already LINKED to `bar`'s last local deploy. `aai init` publishes
|
|
166
|
+
* by default, so the first publish either targeted a slug the user never
|
|
167
|
+
* chose or — for the `http://localhost:8080` a dev checkout leaves behind —
|
|
168
|
+
* failed outright with "Refusing to send your API key to …", the project
|
|
169
|
+
* staying mis-linked for every later `push`/`publish`/`secret`.
|
|
170
|
+
* - into `packages/aai-cli/dist/templates`, i.e. into the PUBLISHED tarball
|
|
171
|
+
* (`files: ["bin.mjs", "dist"]`). Measured on a real build: 26 stray
|
|
172
|
+
* `.aai/project.json` files and 9.4 MB of one developer's `.aai/client`
|
|
173
|
+
* bundles out of a 12 MB `templates/`.
|
|
174
|
+
*
|
|
175
|
+
* The vocabulary is the SDK's, not a fourth list: {@link IGNORED_WORKSPACE_DIRS}
|
|
176
|
+
* is already "never walk this", and {@link isLocalOnlyFile} already means "this
|
|
177
|
+
* exists only on a developer's machine" — including a `.env` that must not ship
|
|
178
|
+
* to npm, and deliberately EXCLUDING `.env.example`, which the scaffold ships as
|
|
179
|
+
* source and a scaffolded project cannot do without.
|
|
180
|
+
*/
|
|
181
|
+
function templateCopyFilter(src) {
|
|
182
|
+
const name = path.basename(src);
|
|
183
|
+
return !(IGNORED_WORKSPACE_DIRS.has(name) || isLocalOnlyFile(name));
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
152
186
|
* Copy a template into targetDir, merging scaffold files underneath.
|
|
153
187
|
*/
|
|
154
188
|
async function downloadAndMergeTemplate(template, targetDir) {
|
|
@@ -158,7 +192,8 @@ async function downloadAndMergeTemplate(template, targetDir) {
|
|
|
158
192
|
if (!names.includes(template)) throw new Error(`Unknown template "${template}". Available templates: ${names.join(", ")}`);
|
|
159
193
|
await fs.cp(path.join(templatesDir, template), targetDir, {
|
|
160
194
|
recursive: true,
|
|
161
|
-
force: true
|
|
195
|
+
force: true,
|
|
196
|
+
filter: templateCopyFilter
|
|
162
197
|
});
|
|
163
198
|
await layerScaffold(targetDir);
|
|
164
199
|
}
|
package/dist/_templates.d.ts
CHANGED
|
@@ -56,6 +56,35 @@ export declare function scaffoldDir(): string;
|
|
|
56
56
|
* {@link layerScaffoldManifest}.
|
|
57
57
|
*/
|
|
58
58
|
export declare function layerScaffold(targetDir: string): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* `fs.cp` filter for every copy OUT of a template or the scaffold — the runtime
|
|
61
|
+
* ones here and the build-time one in `bundle-templates.mjs`.
|
|
62
|
+
*
|
|
63
|
+
* A template directory is also a runnable project, so a developer who runs
|
|
64
|
+
* `aai dev`, `aai build` or `aai publish` inside one leaves build output and
|
|
65
|
+
* machine state in it: `.aai/` (which holds `project.json` — a SLUG and a
|
|
66
|
+
* `serverUrl` — plus a built client), `.workflow-data/`, `node_modules/`, a
|
|
67
|
+
* `.env`. None of it is git-tracked, and an unfiltered `fs.cp` copied all of it
|
|
68
|
+
* anyway, to both destinations:
|
|
69
|
+
*
|
|
70
|
+
* - into every scaffolded project, so `aai init foo --template bar` produced a
|
|
71
|
+
* directory already LINKED to `bar`'s last local deploy. `aai init` publishes
|
|
72
|
+
* by default, so the first publish either targeted a slug the user never
|
|
73
|
+
* chose or — for the `http://localhost:8080` a dev checkout leaves behind —
|
|
74
|
+
* failed outright with "Refusing to send your API key to …", the project
|
|
75
|
+
* staying mis-linked for every later `push`/`publish`/`secret`.
|
|
76
|
+
* - into `packages/aai-cli/dist/templates`, i.e. into the PUBLISHED tarball
|
|
77
|
+
* (`files: ["bin.mjs", "dist"]`). Measured on a real build: 26 stray
|
|
78
|
+
* `.aai/project.json` files and 9.4 MB of one developer's `.aai/client`
|
|
79
|
+
* bundles out of a 12 MB `templates/`.
|
|
80
|
+
*
|
|
81
|
+
* The vocabulary is the SDK's, not a fourth list: {@link IGNORED_WORKSPACE_DIRS}
|
|
82
|
+
* is already "never walk this", and {@link isLocalOnlyFile} already means "this
|
|
83
|
+
* exists only on a developer's machine" — including a `.env` that must not ship
|
|
84
|
+
* to npm, and deliberately EXCLUDING `.env.example`, which the scaffold ships as
|
|
85
|
+
* source and a scaffolded project cannot do without.
|
|
86
|
+
*/
|
|
87
|
+
export declare function templateCopyFilter(src: string): boolean;
|
|
59
88
|
/**
|
|
60
89
|
* Copy a template into targetDir, merging scaffold files underneath.
|
|
61
90
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { a as ok, t as CliError } from "./_output-CKkmWs7i.mjs";
|
|
3
3
|
import { n as log, r as notify } from "./_ui-u7T4YooX.mjs";
|
|
4
|
-
import { r as evalWorkerBundle, t as buildAgentBundle } from "./_bundler-
|
|
4
|
+
import { r as evalWorkerBundle, t as buildAgentBundle } from "./_bundler-B31MqYaD.mjs";
|
|
5
5
|
import { assertTypechecks } from "./_typecheck-gate-BXvUNnfs.mjs";
|
|
6
6
|
import { classifyVitestError, runVitest } from "./test-CvwgeVSQ.mjs";
|
|
7
7
|
import path from "node:path";
|
|
@@ -48,7 +48,6 @@ async function executeBuild(opts) {
|
|
|
48
48
|
}
|
|
49
49
|
if (!opts.skipTypecheck) await assertTypechecks(cwd);
|
|
50
50
|
const bundle = await buildAgentBundle(cwd, { minify: true });
|
|
51
|
-
for (const warning of bundle.workflows?.warnings ?? []) notify("warn", warning);
|
|
52
51
|
const agentDef = await evalWorkerBundle(bundle.worker);
|
|
53
52
|
for (const warning of agentConfigWarnings(agentDef)) notify("warn", warning);
|
|
54
53
|
const worker = path.join(cwd, WORKER_ARTIFACT_REL);
|