@acid-cat/cli 0.6.1 → 0.8.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/README.md +1 -1
- package/conformance/fixture.acidcat.json +24 -0
- package/dist/conform/cli.js +77 -1
- package/dist/conform/index.d.ts +1 -1
- package/dist/conform/serve.d.ts +6 -4
- package/dist/conform/serve.js +4 -3
- package/dist/index.d.ts +1 -1
- package/dist/json/fixture.acidcat.json.js +16 -0
- package/dist/json/package.json.js +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ The exit code is 0 if there are no errors. It is 1 if there are errors. With `--
|
|
|
35
35
|
`conform` tests a running implementation of the [conformance fixture](conformance/fixture.pkl) through its bindings (see [Conformance](../../spec/conformance.md#acid-cat-conform)):
|
|
36
36
|
|
|
37
37
|
- **MCP:** the official MCP client, with revision `2026-07-28`, for tools and outcomes; raw requests for rejections.
|
|
38
|
-
- **HTTP** and **CLI:** each outcome, each kind of call failure, availability, and rejections. For the CLI, an interrupt (`SIGINT`) during a call that holds gives exit code 130.
|
|
38
|
+
- **HTTP** and **CLI:** each outcome, each kind of call failure, availability, and rejections. For the CLI, also each kind of flag, including a repeated flag that gives one item of an array for each occurrence, or the whole array as JSON text, and the usage line of each command. An interrupt (`SIGINT`) during a call that holds gives exit code 130.
|
|
39
39
|
- **WebMCP:** the checks act as the WebMCP host. `--webmcp <url>` opens a page that serves the `app` target in Chromium, through [Playwright](https://playwright.dev), with a test `modelContext`. Install Playwright to use it: `pnpm add -D playwright`, then `pnpm exec playwright install chromium`. In code, `webmcp` can also be a function that installs the implementation on a host in the same process.
|
|
40
40
|
- **Properties:** with fast-check, through each remote binding: concurrent calls each get exactly their own outcome, and a cancellation changes only its own call.
|
|
41
41
|
|
|
@@ -93,6 +93,18 @@
|
|
|
93
93
|
"ratio": {
|
|
94
94
|
"type": "number"
|
|
95
95
|
},
|
|
96
|
+
"steps": {
|
|
97
|
+
"items": {
|
|
98
|
+
"type": "integer"
|
|
99
|
+
},
|
|
100
|
+
"type": "array"
|
|
101
|
+
},
|
|
102
|
+
"tags": {
|
|
103
|
+
"items": {
|
|
104
|
+
"type": "string"
|
|
105
|
+
},
|
|
106
|
+
"type": "array"
|
|
107
|
+
},
|
|
96
108
|
"verbose": {
|
|
97
109
|
"type": "boolean"
|
|
98
110
|
}
|
|
@@ -123,6 +135,18 @@
|
|
|
123
135
|
"ratio": {
|
|
124
136
|
"type": "number"
|
|
125
137
|
},
|
|
138
|
+
"steps": {
|
|
139
|
+
"items": {
|
|
140
|
+
"type": "integer"
|
|
141
|
+
},
|
|
142
|
+
"type": "array"
|
|
143
|
+
},
|
|
144
|
+
"tags": {
|
|
145
|
+
"items": {
|
|
146
|
+
"type": "string"
|
|
147
|
+
},
|
|
148
|
+
"type": "array"
|
|
149
|
+
},
|
|
126
150
|
"verbose": {
|
|
127
151
|
"type": "boolean"
|
|
128
152
|
}
|
package/dist/conform/cli.js
CHANGED
|
@@ -5,6 +5,7 @@ import { existsSync } from "node:fs";
|
|
|
5
5
|
import { mkdtemp, rm } from "node:fs/promises";
|
|
6
6
|
import { tmpdir } from "node:os";
|
|
7
7
|
import { join } from "node:path";
|
|
8
|
+
import { isDeepStrictEqual } from "node:util";
|
|
8
9
|
//#region src/conform/cli.ts
|
|
9
10
|
/** Conformance checks for the CLI binding (spec/bindings/cli.md). Each check runs the command line as a process. */
|
|
10
11
|
const RULE = "spec/bindings/cli.md";
|
|
@@ -12,7 +13,7 @@ const RULE = "spec/bindings/cli.md";
|
|
|
12
13
|
const USAGES = {
|
|
13
14
|
"fixture.crash": "fixture crash",
|
|
14
15
|
"fixture.echo": "fixture echo --text <string>",
|
|
15
|
-
"fixture.flags": "fixture flags --mode <fast|slow> [--count <integer>] [--extra <json>] [--label <string>] [--ratio <number>] [--verbose]",
|
|
16
|
+
"fixture.flags": "fixture flags --mode <fast|slow> [--count <integer>] [--extra <json>] [--label <string>] [--ratio <number>] [--steps <integer>...] [--tags <string>...] [--verbose]",
|
|
16
17
|
"fixture.held": "fixture held",
|
|
17
18
|
"fixture.hold": "fixture hold --key <string> [--marker <string>]",
|
|
18
19
|
"fixture.refuse": "fixture refuse",
|
|
@@ -109,6 +110,32 @@ async function checkCli(command, options) {
|
|
|
109
110
|
],
|
|
110
111
|
3
|
|
111
112
|
],
|
|
113
|
+
[
|
|
114
|
+
"an item of a repeated flag that is not valid has exit code 3",
|
|
115
|
+
[
|
|
116
|
+
"fixture",
|
|
117
|
+
"flags",
|
|
118
|
+
"--mode",
|
|
119
|
+
"fast",
|
|
120
|
+
"--steps",
|
|
121
|
+
"1",
|
|
122
|
+
"--steps",
|
|
123
|
+
"soon"
|
|
124
|
+
],
|
|
125
|
+
3
|
|
126
|
+
],
|
|
127
|
+
[
|
|
128
|
+
"an item of a repeated flag as JSON text that is not valid has exit code 3",
|
|
129
|
+
[
|
|
130
|
+
"fixture",
|
|
131
|
+
"flags",
|
|
132
|
+
"--mode",
|
|
133
|
+
"fast",
|
|
134
|
+
"--steps",
|
|
135
|
+
"[1,\"soon\"]"
|
|
136
|
+
],
|
|
137
|
+
3
|
|
138
|
+
],
|
|
112
139
|
[
|
|
113
140
|
"invalid input through call has exit code 3",
|
|
114
141
|
[
|
|
@@ -148,6 +175,55 @@ async function checkCli(command, options) {
|
|
|
148
175
|
});
|
|
149
176
|
}
|
|
150
177
|
});
|
|
178
|
+
await checks.run("each flag gives the value of its property, and a repeated flag gives one item for each occurrence", `${RULE}#commands`, async (signal) => {
|
|
179
|
+
const scalars = [
|
|
180
|
+
"--mode",
|
|
181
|
+
"slow",
|
|
182
|
+
"--count",
|
|
183
|
+
"3",
|
|
184
|
+
"--ratio=0.5",
|
|
185
|
+
"--label",
|
|
186
|
+
"x",
|
|
187
|
+
"--verbose",
|
|
188
|
+
"--extra",
|
|
189
|
+
"{\"a\":1}"
|
|
190
|
+
];
|
|
191
|
+
const repeated = [
|
|
192
|
+
"--steps",
|
|
193
|
+
"2",
|
|
194
|
+
"--tags",
|
|
195
|
+
"[\"a\"]",
|
|
196
|
+
"--steps=1",
|
|
197
|
+
"--tags",
|
|
198
|
+
"b"
|
|
199
|
+
];
|
|
200
|
+
const result = await run(signal, "fixture", "flags", ...scalars, ...repeated);
|
|
201
|
+
expectEqual(result.code, 0, "exit code");
|
|
202
|
+
const expected = {
|
|
203
|
+
mode: "slow",
|
|
204
|
+
count: 3,
|
|
205
|
+
ratio: .5,
|
|
206
|
+
label: "x",
|
|
207
|
+
verbose: true,
|
|
208
|
+
extra: { a: 1 },
|
|
209
|
+
steps: [2, 1],
|
|
210
|
+
tags: ["[\"a\"]", "b"]
|
|
211
|
+
};
|
|
212
|
+
const output = parseJson(result.stdout);
|
|
213
|
+
expect(isDeepStrictEqual(output, expected), `standard output: expected ${JSON.stringify(expected)}, got ${JSON.stringify(output)}`);
|
|
214
|
+
});
|
|
215
|
+
await checks.run("a repeated flag that occurs once with JSON text of an array gives that array", `${RULE}#commands`, async (signal) => {
|
|
216
|
+
const result = await run(signal, "fixture", "flags", "--steps", "[2,1]", "--mode", "fast", "--verbose", "--tags=[\"a\",\"b\"]");
|
|
217
|
+
expectEqual(result.code, 0, "exit code");
|
|
218
|
+
const expected = {
|
|
219
|
+
mode: "fast",
|
|
220
|
+
verbose: true,
|
|
221
|
+
steps: [2, 1],
|
|
222
|
+
tags: ["a", "b"]
|
|
223
|
+
};
|
|
224
|
+
const output = parseJson(result.stdout);
|
|
225
|
+
expect(isDeepStrictEqual(output, expected), `standard output: expected ${JSON.stringify(expected)}, got ${JSON.stringify(output)}`);
|
|
226
|
+
});
|
|
151
227
|
await checks.run("call gives the value of a capability", `${RULE}#commands`, async (signal) => {
|
|
152
228
|
const result = await run(signal, "call", "fixture.echo", "{\"text\":\"hello\"}");
|
|
153
229
|
expectEqual(result.code, 0, "exit code");
|
package/dist/conform/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { WebMcpInstall } from "./webmcp.js";
|
|
|
3
3
|
export type { Check } from "./check.js";
|
|
4
4
|
export { DEADLINE, fixture } from "./check.js";
|
|
5
5
|
export { serve } from "./serve.js";
|
|
6
|
-
export type { Served, WebHandler } from "./serve.js";
|
|
6
|
+
export type { Routes, Served, WebHandler } from "./serve.js";
|
|
7
7
|
export { createTestHost } from "./webmcp.js";
|
|
8
8
|
export type { TestHost, WebMcpInstall } from "./webmcp.js";
|
|
9
9
|
export interface ConformOptions {
|
package/dist/conform/serve.d.ts
CHANGED
|
@@ -3,8 +3,10 @@ export interface Served {
|
|
|
3
3
|
readonly url: string;
|
|
4
4
|
close(): Promise<void>;
|
|
5
5
|
}
|
|
6
|
-
export type WebHandler = (request: Request) => Promise<Response
|
|
7
|
-
/**
|
|
8
|
-
export
|
|
6
|
+
export type WebHandler = (request: Request) => Promise<Response> | Response;
|
|
7
|
+
/** Handlers by path prefix, for example `{ "/mcp": mcp, "/api": http }`. The longest prefix that matches gets the request. */
|
|
8
|
+
export interface Routes {
|
|
9
9
|
readonly [prefix: string]: WebHandler;
|
|
10
|
-
}
|
|
10
|
+
}
|
|
11
|
+
/** Serve one handler for all paths, or handlers by path prefix, on `127.0.0.1` and a free port. */
|
|
12
|
+
export declare function serve(handler: WebHandler | Routes): Promise<Served>;
|
package/dist/conform/serve.js
CHANGED
|
@@ -4,8 +4,9 @@ import { createServer } from "node:http";
|
|
|
4
4
|
* Serve a web request handler, `(request: Request) => Promise<Response>`, on a local port. Implementations use it
|
|
5
5
|
* to run `acid-cat conform` against their bindings in a test.
|
|
6
6
|
*/
|
|
7
|
-
/** Serve handlers by path prefix,
|
|
8
|
-
function serve(
|
|
7
|
+
/** Serve one handler for all paths, or handlers by path prefix, on `127.0.0.1` and a free port. */
|
|
8
|
+
function serve(handler) {
|
|
9
|
+
const routes = typeof handler === "function" ? { "/": handler } : handler;
|
|
9
10
|
const prefixes = Object.keys(routes).sort((a, b) => b.length - a.length);
|
|
10
11
|
const server = createServer((incoming, outgoing) => {
|
|
11
12
|
answer(incoming, outgoing, prefixes, routes);
|
|
@@ -22,7 +23,7 @@ function serve(routes) {
|
|
|
22
23
|
}
|
|
23
24
|
async function answer(incoming, outgoing, prefixes, routes) {
|
|
24
25
|
const url = new URL(incoming.url ?? "/", `http://${incoming.headers.host ?? "127.0.0.1"}`);
|
|
25
|
-
const prefix = prefixes.find((candidate) => url.pathname === candidate || url.pathname.startsWith(`${candidate}/`));
|
|
26
|
+
const prefix = prefixes.find((candidate) => candidate === "/" || url.pathname === candidate || url.pathname.startsWith(`${candidate}/`));
|
|
26
27
|
const handler = prefix === void 0 ? void 0 : routes[prefix];
|
|
27
28
|
if (handler === void 0) {
|
|
28
29
|
outgoing.writeHead(404).end();
|
package/dist/index.d.ts
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
export { bundle, check, viewOf } from "./catalog.js";
|
|
3
3
|
export type { Catalog, Report } from "./catalog.js";
|
|
4
4
|
export { conform, fixture, serve } from "./conform/index.js";
|
|
5
|
-
export type { Check, ConformOptions, Served, WebHandler } from "./conform/index.js";
|
|
5
|
+
export type { Check, ConformOptions, Routes, Served, WebHandler } from "./conform/index.js";
|
|
@@ -52,6 +52,14 @@ var fixture_acidcat_default = {
|
|
|
52
52
|
"type": "string"
|
|
53
53
|
},
|
|
54
54
|
"ratio": { "type": "number" },
|
|
55
|
+
"steps": {
|
|
56
|
+
"items": { "type": "integer" },
|
|
57
|
+
"type": "array"
|
|
58
|
+
},
|
|
59
|
+
"tags": {
|
|
60
|
+
"items": { "type": "string" },
|
|
61
|
+
"type": "array"
|
|
62
|
+
},
|
|
55
63
|
"verbose": { "type": "boolean" }
|
|
56
64
|
},
|
|
57
65
|
"required": ["mode"],
|
|
@@ -67,6 +75,14 @@ var fixture_acidcat_default = {
|
|
|
67
75
|
"type": "string"
|
|
68
76
|
},
|
|
69
77
|
"ratio": { "type": "number" },
|
|
78
|
+
"steps": {
|
|
79
|
+
"items": { "type": "integer" },
|
|
80
|
+
"type": "array"
|
|
81
|
+
},
|
|
82
|
+
"tags": {
|
|
83
|
+
"items": { "type": "string" },
|
|
84
|
+
"type": "array"
|
|
85
|
+
},
|
|
70
86
|
"verbose": { "type": "boolean" }
|
|
71
87
|
},
|
|
72
88
|
"required": ["mode"],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@acid-cat/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "The AcidCat command line: check, format and view catalogs, and test implementations.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"acidcat",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"access": "public"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@acid-cat/catalog": "0.
|
|
41
|
-
"@acid-cat/spec-ts": "0.
|
|
40
|
+
"@acid-cat/catalog": "0.8.0",
|
|
41
|
+
"@acid-cat/spec-ts": "0.8.0",
|
|
42
42
|
"@effect/platform-node": "4.0.6",
|
|
43
43
|
"@effect/platform-node-shared": "4.0.6",
|
|
44
44
|
"@modelcontextprotocol/client": "2.3.1",
|