@alexkroman1/aai-cli 5.9.0 → 5.10.1
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 +32 -0
- package/dist/{_agent-BPEXSdWX.mjs → _agent-DS2PUJcl.mjs} +1 -1
- package/dist/{_bundler-Cjaxa2wi.mjs → _bundler-DC17suWN.mjs} +2 -2
- package/dist/{_config-BJm2o795.mjs → _config-DMyolIk9.mjs} +92 -10
- package/dist/_config.d.ts +12 -0
- package/dist/_dev-env.d.ts +38 -0
- package/dist/{_dev-server-Cnf4jNzE.mjs → _dev-server-B3ivqyEd.mjs} +72 -54
- package/dist/_dev-server.d.ts +2 -2
- package/dist/{_init-DVNeyGSl.mjs → _init-CYLvYU-B.mjs} +3 -3
- package/dist/{_server-common-CnaP_Urf.mjs → _server-common-DX8Bfrf5.mjs} +1 -1
- package/dist/{_slug-api-CQw1YGR5.mjs → _slug-api-fRNNR8tz.mjs} +1 -1
- package/dist/{_templates-DDx08LIG.mjs → _templates-BWJOiWOO.mjs} +2 -2
- package/dist/{_typecheck-gate-BT4iPz7A.mjs → _typecheck-gate-DB-PY0A3.mjs} +1 -1
- package/dist/{_ui-RZmPgrF6.mjs → _ui-DfwfDbT-.mjs} +26 -1
- package/dist/_ui.d.ts +19 -0
- package/dist/{_utils-Ch0J4s6a.mjs → _utils-8KKw-bzi.mjs} +9 -2
- package/dist/_utils.d.ts +9 -2
- package/dist/{build-WdGtFsSx.mjs → build-CACFbdQ4.mjs} +4 -4
- package/dist/cli.mjs +20 -20
- package/dist/{client-bundler-yiWoXrgb.mjs → client-bundler-DONm-khu.mjs} +1 -1
- package/dist/client-bundler.mjs +1 -1
- package/dist/{delete-Dr1Jn65f.mjs → delete-DEZ7u3u4.mjs} +5 -4
- package/dist/delete.d.ts +5 -0
- package/dist/{deploy-Bij1jES6.mjs → deploy-Ch0d_jje.mjs} +7 -7
- package/dist/{dev-HBCgChsA.mjs → dev-CdeRcYiQ.mjs} +6 -6
- package/dist/{init-BYGp3Usr.mjs → init-GQsNWkLJ.mjs} +7 -7
- package/dist/{login-CXazkkcR.mjs → login-BeFUiU6M.mjs} +6 -7
- package/dist/scaffold/CLAUDE.md +35 -12
- package/dist/scaffold/package.json +3 -3
- package/dist/{secret-vhGe-r2a.mjs → secret-4_dYyrpA.mjs} +2 -2
- package/dist/{storage-CMPsAmch.mjs → storage-BTfErOOW.mjs} +2 -2
- package/dist/{studio-B6zDNPPr.mjs → studio-LNvXtWak.mjs} +6 -6
- package/dist/templates/dispatch-center/agent.ts +4 -6
- package/dist/templates/retail/agent.ts +4 -6
- package/dist/templates/retail/registry.test.ts +5 -3
- package/dist/templates/retail/resolve.test.ts +6 -4
- package/dist/templates/retail/seed.test.ts +21 -13
- package/dist/templates/retail/shared.test.ts +9 -5
- package/dist/templates/solo-rpg/agent.test.ts +7 -5
- package/dist/templates/solo-rpg/shared.ts +2 -2
- package/dist/{test-CkXvfcpq.mjs → test-C-V98oC-.mjs} +3 -4
- package/dist/typecheck.mjs +65 -14
- package/package.json +5 -4
|
@@ -90,18 +90,25 @@ describe("seed shape", () => {
|
|
|
90
90
|
expect(Object.keys(seed.orders)).toHaveLength(22);
|
|
91
91
|
});
|
|
92
92
|
|
|
93
|
+
// These four sweep the whole corpus, so they assert SOFTLY: one hand-edited
|
|
94
|
+
// order usually breaks several records at once, and a hard failure reports
|
|
95
|
+
// the first and hides how far the damage runs.
|
|
93
96
|
test("every order belongs to a seeded user and is listed on that user", () => {
|
|
94
97
|
for (const order of Object.values(seed.orders)) {
|
|
95
98
|
const user = seed.users[order.user_id];
|
|
96
|
-
expect(user, `order ${order.order_id} has no seeded user`).toBeDefined();
|
|
97
|
-
expect
|
|
99
|
+
expect.soft(user, `order ${order.order_id} has no seeded user`).toBeDefined();
|
|
100
|
+
expect
|
|
101
|
+
.soft(user?.orders ?? [], `${order.order_id} is not listed on its user`)
|
|
102
|
+
.toContain(order.order_id);
|
|
98
103
|
}
|
|
99
104
|
});
|
|
100
105
|
|
|
101
106
|
test("every user's listed orders are all seeded", () => {
|
|
102
107
|
for (const user of Object.values(seed.users)) {
|
|
103
108
|
for (const orderId of user.orders) {
|
|
104
|
-
expect
|
|
109
|
+
expect
|
|
110
|
+
.soft(seed.orders[orderId], `${user.user_id} lists unseeded ${orderId}`)
|
|
111
|
+
.toBeDefined();
|
|
105
112
|
}
|
|
106
113
|
}
|
|
107
114
|
});
|
|
@@ -110,11 +117,10 @@ describe("seed shape", () => {
|
|
|
110
117
|
for (const order of Object.values(seed.orders)) {
|
|
111
118
|
for (const item of order.items) {
|
|
112
119
|
const variant = seed.products[item.product_id]?.variants[item.item_id];
|
|
113
|
-
expect
|
|
114
|
-
variant,
|
|
115
|
-
|
|
116
|
-
).
|
|
117
|
-
expect(variant?.price).toBe(item.price);
|
|
120
|
+
expect
|
|
121
|
+
.soft(variant, `${order.order_id}: ${item.item_id} missing from its product`)
|
|
122
|
+
.toBeDefined();
|
|
123
|
+
expect.soft(variant?.price, `${order.order_id}: ${item.item_id} price`).toBe(item.price);
|
|
118
124
|
}
|
|
119
125
|
}
|
|
120
126
|
});
|
|
@@ -123,10 +129,12 @@ describe("seed shape", () => {
|
|
|
123
129
|
for (const order of Object.values(seed.orders)) {
|
|
124
130
|
const user = seed.users[order.user_id];
|
|
125
131
|
for (const payment of order.payment_history) {
|
|
126
|
-
expect
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
132
|
+
expect
|
|
133
|
+
.soft(
|
|
134
|
+
user?.payment_methods[payment.payment_method_id],
|
|
135
|
+
`${order.order_id}: unknown method ${payment.payment_method_id}`,
|
|
136
|
+
)
|
|
137
|
+
.toBeDefined();
|
|
130
138
|
}
|
|
131
139
|
}
|
|
132
140
|
});
|
|
@@ -148,7 +156,7 @@ describe("seed coverage — each datum backs a specific test in agent.test.ts",
|
|
|
148
156
|
test("every status the tools branch on is present", () => {
|
|
149
157
|
const statuses = new Set(Object.values(seed.orders).map((o) => o.status));
|
|
150
158
|
for (const s of ["pending", "processed", "delivered", "cancelled"]) {
|
|
151
|
-
expect(statuses, `no ${s} order seeded`).toContain(s);
|
|
159
|
+
expect.soft(statuses, `no ${s} order seeded`).toContain(s);
|
|
152
160
|
}
|
|
153
161
|
});
|
|
154
162
|
|
|
@@ -57,7 +57,9 @@ describe("storeView", () => {
|
|
|
57
57
|
"#W4316152",
|
|
58
58
|
"#W1840144",
|
|
59
59
|
]) {
|
|
60
|
-
|
|
60
|
+
// Soft: a widened projection leaks several fields at once, and the full
|
|
61
|
+
// list is what says how much of the other customers' records got out.
|
|
62
|
+
expect.soft(json, `projection leaked ${leaked}`).not.toContain(leaked);
|
|
61
63
|
}
|
|
62
64
|
});
|
|
63
65
|
|
|
@@ -84,15 +86,17 @@ describe("DEMO_PERSONAS", () => {
|
|
|
84
86
|
const users = Object.values((structuredClone(seedJson) as unknown as Store).users);
|
|
85
87
|
for (const persona of DEMO_PERSONAS) {
|
|
86
88
|
const user = users.find((u) => u.email === persona.email);
|
|
87
|
-
expect(user, `no seeded customer has email ${persona.email}`).toBeDefined();
|
|
88
|
-
expect
|
|
89
|
-
|
|
89
|
+
expect.soft(user, `no seeded customer has email ${persona.email}`).toBeDefined();
|
|
90
|
+
expect
|
|
91
|
+
.soft(`${user?.name.first_name} ${user?.name.last_name}`, persona.email)
|
|
92
|
+
.toBe(persona.name);
|
|
93
|
+
expect.soft(user?.address.zip, persona.email).toBe(persona.zip);
|
|
90
94
|
}
|
|
91
95
|
});
|
|
92
96
|
|
|
93
97
|
test("every persona carries a hint, so a user can pick a path deliberately", () => {
|
|
94
98
|
for (const persona of DEMO_PERSONAS) {
|
|
95
|
-
expect(persona.hint.length, persona.name).toBeGreaterThan(20);
|
|
99
|
+
expect.soft(persona.hint.length, persona.name).toBeGreaterThan(20);
|
|
96
100
|
}
|
|
97
101
|
});
|
|
98
102
|
});
|
|
@@ -167,11 +167,13 @@ describe("applyConsequences MISS matrix", () => {
|
|
|
167
167
|
] as const) {
|
|
168
168
|
const state = playingState();
|
|
169
169
|
const { deltas, clockEvents } = applyConsequences(state, miss, position, "standard", null);
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
expect(state.
|
|
173
|
-
expect(deltas.
|
|
174
|
-
expect(
|
|
170
|
+
// Each row starts from a fresh state, so they are independent — assert
|
|
171
|
+
// softly and a rebalanced table reports the whole matrix in one run.
|
|
172
|
+
expect.soft(state.health, position).toBe(5 - dmg);
|
|
173
|
+
expect.soft(deltas.health, position).toBe(-dmg);
|
|
174
|
+
expect.soft(state.clocks[0]?.filled, position).toBe(ticks);
|
|
175
|
+
expect.soft(deltas.clockTicks, position).toBe(ticks);
|
|
176
|
+
expect.soft(clockEvents, position).toHaveLength(0); // 4-segment clock not full yet
|
|
175
177
|
}
|
|
176
178
|
});
|
|
177
179
|
|
|
@@ -341,8 +341,8 @@ export function saveGameState(ctx: ToolContext, state: GameState): void {
|
|
|
341
341
|
|
|
342
342
|
// ── Persistent save slots (ctx.db) ───────────────────────────────────────────
|
|
343
343
|
// save_game / load_game are genuine cross-session persistence, so they use
|
|
344
|
-
// the app's SQL database. Requires storage: `aai storage enable` (or
|
|
345
|
-
//
|
|
344
|
+
// the app's SQL database. Requires storage: `aai storage enable` (or
|
|
345
|
+
// Settings → Database in the studio); under `aai dev`, set DATABASE_URL in .env.
|
|
346
346
|
//
|
|
347
347
|
// Slots are keyed by name alone — the whole point of a save is loading it in
|
|
348
348
|
// a LATER session, whose sessionId differs, so the key can't embed one. The
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { a as ok, n as fail } from "./_output-CC300DzW.mjs";
|
|
3
|
-
import { n as log } from "./_ui-
|
|
4
|
-
import { a as errorMessage, n as binFromPackageJson, r as errorCode } from "./_utils-
|
|
3
|
+
import { n as log } from "./_ui-DfwfDbT-.mjs";
|
|
4
|
+
import { a as errorMessage, n as binFromPackageJson, r as errorCode } from "./_utils-8KKw-bzi.mjs";
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
6
|
import { existsSync } from "node:fs";
|
|
7
7
|
import path from "node:path";
|
|
@@ -51,8 +51,7 @@ function runVitest(cwd) {
|
|
|
51
51
|
testFile
|
|
52
52
|
], {
|
|
53
53
|
cwd,
|
|
54
|
-
stdio: "inherit"
|
|
55
|
-
env: { NODE_OPTIONS: "--experimental-strip-types" }
|
|
54
|
+
stdio: "inherit"
|
|
56
55
|
});
|
|
57
56
|
return true;
|
|
58
57
|
}
|
package/dist/typecheck.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as errorMessage, n as binFromPackageJson } from "./_utils-
|
|
3
|
-
import { existsSync } from "node:fs";
|
|
2
|
+
import { a as errorMessage, n as binFromPackageJson } from "./_utils-8KKw-bzi.mjs";
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
6
|
//#region typecheck.ts
|
|
@@ -56,13 +56,69 @@ function findTypescriptPackage(cwd) {
|
|
|
56
56
|
dir = parent;
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
-
/**
|
|
60
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Resolve the project's TypeScript compiler entry (its own `tsc` bin), plus
|
|
61
|
+
* its major version — see {@link tscArgs} for what the version decides.
|
|
62
|
+
*/
|
|
63
|
+
function resolveTsc(cwd) {
|
|
61
64
|
const dir = findTypescriptPackage(cwd);
|
|
62
65
|
if (dir === void 0) throw new Error("no typescript package in the project's node_modules");
|
|
63
|
-
const
|
|
66
|
+
const manifest = path.join(dir, "package.json");
|
|
67
|
+
const bin = binFromPackageJson(manifest, "tsc");
|
|
64
68
|
if (!bin) throw new Error("installed typescript package declares no tsc bin");
|
|
65
|
-
return
|
|
69
|
+
return {
|
|
70
|
+
entry: bin,
|
|
71
|
+
major: readMajor(manifest)
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The resolved compiler's major version, or 0 when it can't be read.
|
|
76
|
+
*
|
|
77
|
+
* 0 is the safe answer: {@link tscArgs} only ADDS flags above a version
|
|
78
|
+
* floor, so an unreadable manifest degrades to the flag set every TypeScript
|
|
79
|
+
* accepts rather than failing a build over a cosmetic read.
|
|
80
|
+
*/
|
|
81
|
+
function readMajor(manifest) {
|
|
82
|
+
try {
|
|
83
|
+
const { version } = JSON.parse(readFileSync(manifest, "utf-8"));
|
|
84
|
+
return typeof version === "string" ? Number.parseInt(version, 10) || 0 : 0;
|
|
85
|
+
} catch {
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The `tsc` argv for one project check.
|
|
91
|
+
*
|
|
92
|
+
* `--singleThreaded` is the interesting one, and it is a SPEEDUP here rather
|
|
93
|
+
* than a throttle. TypeScript 7 parallelizes parse/check/emit by default,
|
|
94
|
+
* which pays off on a repo-sized program and costs on a single agent project —
|
|
95
|
+
* and this function only ever checks one of those. Measured on the templates
|
|
96
|
+
* project (14 agents, the closest in-repo analogue of a studio workspace):
|
|
97
|
+
*
|
|
98
|
+
* - pinned to 1 core: 2.4–2.9s parallel vs 1.2–1.4s single — ~2x faster
|
|
99
|
+
* - on 4 cores: 1.21–1.24s parallel vs 1.01–1.04s single
|
|
100
|
+
*
|
|
101
|
+
* The 1-core number is the one that matters: a guest sandbox RESERVES one CPU
|
|
102
|
+
* (`SANDBOX_CPU`) and this same check runs after every settled write burst in
|
|
103
|
+
* the studio (`aai-guest/studio-write-diagnostics.ts`), where the whole design
|
|
104
|
+
* rests on it finishing in well under a second. Parallelism inside a one-core
|
|
105
|
+
* reservation is oversubscription: the threads exist, contend, and the wall
|
|
106
|
+
* clock doubles.
|
|
107
|
+
*
|
|
108
|
+
* Gated on major >= 7 because the flag is TS 7's, and an unknown compiler
|
|
109
|
+
* option is a HARD error (TS5023) — a project pinning an older TypeScript
|
|
110
|
+
* would fail its build on a flag it never asked for. `engines`/scaffold pin
|
|
111
|
+
* `^7`, so in practice this floor is always met; it exists so a project that
|
|
112
|
+
* pins otherwise degrades instead of breaking.
|
|
113
|
+
*/
|
|
114
|
+
function tscArgs(entry, major) {
|
|
115
|
+
return [
|
|
116
|
+
entry,
|
|
117
|
+
"--noEmit",
|
|
118
|
+
"--pretty",
|
|
119
|
+
"false",
|
|
120
|
+
...major >= 7 ? ["--singleThreaded"] : []
|
|
121
|
+
];
|
|
66
122
|
}
|
|
67
123
|
/**
|
|
68
124
|
* Typecheck the project at `cwd` with its own tsconfig + compiler.
|
|
@@ -76,9 +132,9 @@ async function typecheckProject(cwd) {
|
|
|
76
132
|
ok: true,
|
|
77
133
|
skipped: true
|
|
78
134
|
};
|
|
79
|
-
let
|
|
135
|
+
let tsc;
|
|
80
136
|
try {
|
|
81
|
-
|
|
137
|
+
tsc = resolveTsc(cwd);
|
|
82
138
|
} catch (err) {
|
|
83
139
|
return {
|
|
84
140
|
ok: false,
|
|
@@ -86,12 +142,7 @@ async function typecheckProject(cwd) {
|
|
|
86
142
|
};
|
|
87
143
|
}
|
|
88
144
|
const result = await new Promise((resolve, reject) => {
|
|
89
|
-
const child = spawn(process.execPath,
|
|
90
|
-
tscEntry,
|
|
91
|
-
"--noEmit",
|
|
92
|
-
"--pretty",
|
|
93
|
-
"false"
|
|
94
|
-
], {
|
|
145
|
+
const child = spawn(process.execPath, tscArgs(tsc.entry, tsc.major), {
|
|
95
146
|
cwd,
|
|
96
147
|
timeout: TYPECHECK_TIMEOUT_MS,
|
|
97
148
|
stdio: [
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alexkroman1/aai-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.10.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
|
-
"aai": "
|
|
6
|
+
"aai": "bin.mjs"
|
|
7
7
|
},
|
|
8
8
|
"exports": {
|
|
9
9
|
"./client-bundler": {
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
|
+
"bin.mjs",
|
|
26
27
|
"dist"
|
|
27
28
|
],
|
|
28
29
|
"dependencies": {
|
|
@@ -37,8 +38,8 @@
|
|
|
37
38
|
"p-timeout": "^7.0.1",
|
|
38
39
|
"vite": "^8.1.5",
|
|
39
40
|
"zod": "^4.4.3",
|
|
40
|
-
"@alexkroman1/aai": "5.
|
|
41
|
-
"@alexkroman1/aai-ui": "5.
|
|
41
|
+
"@alexkroman1/aai": "5.10.1",
|
|
42
|
+
"@alexkroman1/aai-ui": "5.10.1"
|
|
42
43
|
},
|
|
43
44
|
"devDependencies": {
|
|
44
45
|
"playwright": "^1.61.1",
|