@acid-cat/cli 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +55 -2
- package/conformance/fixture.acidcat.json +152 -0
- package/dist/catalog.d.ts +11 -0
- package/dist/catalog.js +111 -0
- package/dist/conform/check.d.ts +28 -0
- package/dist/conform/check.js +51 -0
- package/dist/conform/cli.d.ts +2 -0
- package/dist/conform/cli.js +112 -0
- package/dist/conform/http.d.ts +5 -0
- package/dist/conform/http.js +97 -0
- package/dist/conform/index.d.ts +29 -0
- package/dist/conform/index.js +37 -0
- package/dist/conform/mcp.d.ts +11 -0
- package/dist/conform/mcp.js +127 -0
- package/dist/conform/properties.d.ts +14 -0
- package/dist/conform/properties.js +98 -0
- package/dist/conform/serve.d.ts +10 -0
- package/dist/conform/serve.js +62 -0
- package/dist/conform/webmcp.d.ts +51 -0
- package/dist/conform/webmcp.js +179 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/json/catalog.schema.json.js +117 -0
- package/dist/json/fixture.acidcat.json.js +105 -0
- package/dist/json/package.json.js +4 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +98 -0
- package/package.json +58 -3
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
//#region ../../schemas/catalog.schema.json
|
|
2
|
+
var catalog_schema_default = {
|
|
3
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
4
|
+
$id: "https://acid-cat.dev/schemas/0.1/catalog.schema.json",
|
|
5
|
+
title: "AcidCat catalog",
|
|
6
|
+
description: "One document that states what an application can do and which targets see it. See spec/catalog.md.",
|
|
7
|
+
type: "object",
|
|
8
|
+
required: [
|
|
9
|
+
"acidcat",
|
|
10
|
+
"targets",
|
|
11
|
+
"capabilities"
|
|
12
|
+
],
|
|
13
|
+
properties: {
|
|
14
|
+
"acidcat": {
|
|
15
|
+
"description": "The version of the AcidCat specification this catalog follows.",
|
|
16
|
+
"const": "0.1"
|
|
17
|
+
},
|
|
18
|
+
"title": { "type": "string" },
|
|
19
|
+
"description": { "type": "string" },
|
|
20
|
+
"targets": {
|
|
21
|
+
"description": "The targets, keyed by name.",
|
|
22
|
+
"type": "object",
|
|
23
|
+
"propertyNames": { "$ref": "#/$defs/targetName" },
|
|
24
|
+
"additionalProperties": { "$ref": "#/$defs/target" }
|
|
25
|
+
},
|
|
26
|
+
"$defs": {
|
|
27
|
+
"description": "Shared types, keyed by name. Capability schemas reference them as #/$defs/<Name>.",
|
|
28
|
+
"type": "object",
|
|
29
|
+
"propertyNames": { "$ref": "#/$defs/typeName" },
|
|
30
|
+
"additionalProperties": { "$ref": "#/$defs/schema" }
|
|
31
|
+
},
|
|
32
|
+
"capabilities": {
|
|
33
|
+
"description": "The capabilities, keyed by id.",
|
|
34
|
+
"type": "object",
|
|
35
|
+
"propertyNames": { "$ref": "#/$defs/id" },
|
|
36
|
+
"additionalProperties": { "$ref": "#/$defs/capability" }
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
$defs: {
|
|
40
|
+
"id": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"maxLength": 128,
|
|
43
|
+
"pattern": "^[a-z][a-z0-9]*(-[a-z0-9]+)*(\\.[a-z][a-z0-9]*(-[a-z0-9]+)*)+$"
|
|
44
|
+
},
|
|
45
|
+
"targetName": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"pattern": "^[a-z][a-z0-9-]*(:[a-z][a-z0-9-]*)?$"
|
|
48
|
+
},
|
|
49
|
+
"bindingName": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"pattern": "^[a-z][a-z0-9-]*(:[a-z][a-z0-9-]*)?$"
|
|
52
|
+
},
|
|
53
|
+
"typeName": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"pattern": "^[A-Za-z][A-Za-z0-9_]*$"
|
|
56
|
+
},
|
|
57
|
+
"target": {
|
|
58
|
+
"type": "object",
|
|
59
|
+
"required": ["bindings"],
|
|
60
|
+
"properties": {
|
|
61
|
+
"bindings": {
|
|
62
|
+
"type": "array",
|
|
63
|
+
"items": { "$ref": "#/$defs/bindingName" },
|
|
64
|
+
"minItems": 1,
|
|
65
|
+
"uniqueItems": true
|
|
66
|
+
},
|
|
67
|
+
"description": { "type": "string" },
|
|
68
|
+
"options": { "type": "object" }
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"schema": {
|
|
72
|
+
"description": "A JSON Schema 2020-12 schema. Its $refs point into the catalog's $defs.",
|
|
73
|
+
"type": ["object", "boolean"]
|
|
74
|
+
},
|
|
75
|
+
"objectSchema": {
|
|
76
|
+
"description": "An input schema: it states that the input is an object, or it references a type.",
|
|
77
|
+
"type": "object",
|
|
78
|
+
"anyOf": [{
|
|
79
|
+
"required": ["type"],
|
|
80
|
+
"properties": { "type": { "const": "object" } }
|
|
81
|
+
}, {
|
|
82
|
+
"required": ["$ref"],
|
|
83
|
+
"properties": { "$ref": { "type": "string" } }
|
|
84
|
+
}]
|
|
85
|
+
},
|
|
86
|
+
"capability": {
|
|
87
|
+
"type": "object",
|
|
88
|
+
"required": ["description"],
|
|
89
|
+
"properties": {
|
|
90
|
+
"description": {
|
|
91
|
+
"type": "string",
|
|
92
|
+
"minLength": 1,
|
|
93
|
+
"pattern": "\\S"
|
|
94
|
+
},
|
|
95
|
+
"audience": {
|
|
96
|
+
"type": "array",
|
|
97
|
+
"items": { "$ref": "#/$defs/targetName" },
|
|
98
|
+
"uniqueItems": true
|
|
99
|
+
},
|
|
100
|
+
"input": { "$ref": "#/$defs/objectSchema" },
|
|
101
|
+
"output": { "$ref": "#/$defs/schema" },
|
|
102
|
+
"failures": {
|
|
103
|
+
"type": "object",
|
|
104
|
+
"propertyNames": {
|
|
105
|
+
"type": "string",
|
|
106
|
+
"minLength": 1
|
|
107
|
+
},
|
|
108
|
+
"additionalProperties": { "$ref": "#/$defs/schema" }
|
|
109
|
+
},
|
|
110
|
+
"readOnly": { "type": "boolean" },
|
|
111
|
+
"state": { "$ref": "#/$defs/schema" }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
//#endregion
|
|
117
|
+
export { catalog_schema_default as default };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
//#region conformance/fixture.acidcat.json
|
|
2
|
+
var fixture_acidcat_default = {
|
|
3
|
+
acidcat: "0.1",
|
|
4
|
+
title: "AcidCat conformance fixture",
|
|
5
|
+
targets: {
|
|
6
|
+
"api": { "bindings": ["http"] },
|
|
7
|
+
"app": { "bindings": ["typescript", "webmcp"] },
|
|
8
|
+
"cli": { "bindings": ["cli"] },
|
|
9
|
+
"mcp": { "bindings": ["mcp"] }
|
|
10
|
+
},
|
|
11
|
+
capabilities: {
|
|
12
|
+
"fixture.crash": {
|
|
13
|
+
"description": "Always fail with a defect.",
|
|
14
|
+
"audience": [
|
|
15
|
+
"app",
|
|
16
|
+
"mcp",
|
|
17
|
+
"cli",
|
|
18
|
+
"api"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"fixture.echo": {
|
|
22
|
+
"description": "Return the input text.",
|
|
23
|
+
"audience": [
|
|
24
|
+
"app",
|
|
25
|
+
"mcp",
|
|
26
|
+
"cli",
|
|
27
|
+
"api"
|
|
28
|
+
],
|
|
29
|
+
"input": {
|
|
30
|
+
"properties": { "text": { "type": "string" } },
|
|
31
|
+
"required": ["text"],
|
|
32
|
+
"type": "object"
|
|
33
|
+
},
|
|
34
|
+
"output": {
|
|
35
|
+
"properties": { "text": { "type": "string" } },
|
|
36
|
+
"required": ["text"],
|
|
37
|
+
"type": "object"
|
|
38
|
+
},
|
|
39
|
+
"readOnly": true
|
|
40
|
+
},
|
|
41
|
+
"fixture.hidden": {
|
|
42
|
+
"description": "Only the app target can use this capability.",
|
|
43
|
+
"audience": ["app"]
|
|
44
|
+
},
|
|
45
|
+
"fixture.refuse": {
|
|
46
|
+
"description": "Always return the failure Refused.",
|
|
47
|
+
"audience": [
|
|
48
|
+
"app",
|
|
49
|
+
"mcp",
|
|
50
|
+
"cli",
|
|
51
|
+
"api"
|
|
52
|
+
],
|
|
53
|
+
"failures": { "Refused": {
|
|
54
|
+
"properties": {
|
|
55
|
+
"_tag": {
|
|
56
|
+
"const": "Refused",
|
|
57
|
+
"type": "string"
|
|
58
|
+
},
|
|
59
|
+
"reason": { "type": "string" }
|
|
60
|
+
},
|
|
61
|
+
"required": ["_tag", "reason"],
|
|
62
|
+
"type": "object"
|
|
63
|
+
} }
|
|
64
|
+
},
|
|
65
|
+
"fixture.slow": {
|
|
66
|
+
"description": "Wait for some milliseconds, then return null.",
|
|
67
|
+
"audience": [
|
|
68
|
+
"app",
|
|
69
|
+
"mcp",
|
|
70
|
+
"cli",
|
|
71
|
+
"api"
|
|
72
|
+
],
|
|
73
|
+
"input": {
|
|
74
|
+
"properties": { "milliseconds": { "type": "number" } },
|
|
75
|
+
"required": ["milliseconds"],
|
|
76
|
+
"type": "object"
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"fixture.switch.set": {
|
|
80
|
+
"description": "Turn the switch on or off. The switch controls the availability of fixture.toggle.",
|
|
81
|
+
"audience": [
|
|
82
|
+
"app",
|
|
83
|
+
"mcp",
|
|
84
|
+
"cli",
|
|
85
|
+
"api"
|
|
86
|
+
],
|
|
87
|
+
"input": {
|
|
88
|
+
"properties": { "on": { "type": "boolean" } },
|
|
89
|
+
"required": ["on"],
|
|
90
|
+
"type": "object"
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"fixture.toggle": {
|
|
94
|
+
"description": "Available only while the switch is on.",
|
|
95
|
+
"audience": [
|
|
96
|
+
"app",
|
|
97
|
+
"mcp",
|
|
98
|
+
"cli",
|
|
99
|
+
"api"
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
//#endregion
|
|
105
|
+
export { fixture_acidcat_default as default };
|
package/dist/main.d.ts
ADDED
package/dist/main.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { check, viewOf } from "./catalog.js";
|
|
3
|
+
import { conform } from "./conform/index.js";
|
|
4
|
+
import { version } from "./json/package.json.js";
|
|
5
|
+
import { canonicalText } from "@acid-cat/catalog";
|
|
6
|
+
import { Console, Effect, FileSystem, Option } from "effect";
|
|
7
|
+
import { NodeRuntime, NodeServices } from "@effect/platform-node";
|
|
8
|
+
import { Argument, Command, Flag } from "effect/cli";
|
|
9
|
+
//#region src/main.ts
|
|
10
|
+
/**
|
|
11
|
+
* `acid-cat`: check, format and view AcidCat catalogs.
|
|
12
|
+
*
|
|
13
|
+
* Exit codes: 0 when the command succeeds, 1 when a catalog does not conform or a file is not found.
|
|
14
|
+
*/
|
|
15
|
+
const readCatalog = Effect.fn("readCatalog")(function* (file) {
|
|
16
|
+
const text = yield* (yield* FileSystem.FileSystem).readFileString(file);
|
|
17
|
+
return {
|
|
18
|
+
text,
|
|
19
|
+
document: JSON.parse(text)
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
const fail = Effect.sync(() => {
|
|
23
|
+
process.exitCode = 1;
|
|
24
|
+
});
|
|
25
|
+
const files = Argument.String("catalog").pipe(Argument.withDescription("A catalog file, such as acidcat.catalog.json"), Argument.variadic({ min: 1 }));
|
|
26
|
+
const checkCommand = Command.make("check", {
|
|
27
|
+
files,
|
|
28
|
+
strict: Flag.Boolean("strict").pipe(Flag.withDescription("Fail on warnings too"), Flag.withDefault(false))
|
|
29
|
+
}, Effect.fn(function* ({ files, strict }) {
|
|
30
|
+
let failed = false;
|
|
31
|
+
for (const file of files) {
|
|
32
|
+
const { text, document } = yield* readCatalog(file);
|
|
33
|
+
const report = check(document, text);
|
|
34
|
+
for (const error of report.errors) yield* Console.error(`${file}: error: ${error}`);
|
|
35
|
+
for (const warning of report.warnings) yield* Console.error(`${file}: warning: ${warning}`);
|
|
36
|
+
failed ||= report.errors.length > 0 || strict && report.warnings.length > 0;
|
|
37
|
+
}
|
|
38
|
+
if (failed) return yield* fail;
|
|
39
|
+
yield* Console.log(files.length === 1 ? "The catalog conforms." : "Every catalog conforms.");
|
|
40
|
+
})).pipe(Command.withDescription("Check that catalogs conform to the specification."));
|
|
41
|
+
const formatCommand = Command.make("format", {
|
|
42
|
+
files,
|
|
43
|
+
write: Flag.Boolean("write").pipe(Flag.withDescription("Rewrite each file instead of printing it"), Flag.withDefault(false))
|
|
44
|
+
}, Effect.fn(function* ({ files, write }) {
|
|
45
|
+
const fs = yield* FileSystem.FileSystem;
|
|
46
|
+
for (const file of files) {
|
|
47
|
+
const { document } = yield* readCatalog(file);
|
|
48
|
+
const text = canonicalText(document);
|
|
49
|
+
if (write) yield* fs.writeFileString(file, text);
|
|
50
|
+
else yield* Effect.sync(() => process.stdout.write(text));
|
|
51
|
+
}
|
|
52
|
+
})).pipe(Command.withDescription("Write catalogs in canonical form."));
|
|
53
|
+
const viewCommand = Command.make("view", {
|
|
54
|
+
file: Argument.String("catalog").pipe(Argument.withDescription("A catalog file")),
|
|
55
|
+
target: Argument.String("target").pipe(Argument.withDescription("The name of a target in the catalog"))
|
|
56
|
+
}, Effect.fn(function* ({ file, target }) {
|
|
57
|
+
const { document } = yield* readCatalog(file);
|
|
58
|
+
const catalog = document;
|
|
59
|
+
if (!(target in catalog.targets)) {
|
|
60
|
+
yield* Console.error(`${file}: error: there is no target ${target}`);
|
|
61
|
+
return yield* fail;
|
|
62
|
+
}
|
|
63
|
+
yield* Effect.sync(() => process.stdout.write(canonicalText(viewOf(catalog, target))));
|
|
64
|
+
})).pipe(Command.withDescription("Print the view of one target."));
|
|
65
|
+
const url = (name, description) => Flag.String(name).pipe(Flag.withDescription(description), Flag.optional);
|
|
66
|
+
const conformCommand = Command.make("conform", {
|
|
67
|
+
mcp: url("mcp", "The URL of the MCP server"),
|
|
68
|
+
http: url("http", "The base URL of the HTTP API, before /capabilities"),
|
|
69
|
+
cli: url("cli", "The command that starts the command line, for example \"node fixture-cli.ts\""),
|
|
70
|
+
webmcp: url("webmcp", "The URL of a web page that serves the app target. It runs in Chromium, through Playwright"),
|
|
71
|
+
maximumBytes: Flag.Int("max-bytes").pipe(Flag.withDescription("The body limit of the remote bindings"), Flag.withDefault(1048576))
|
|
72
|
+
}, Effect.fn(function* (options) {
|
|
73
|
+
const value = (option) => Option.isSome(option) ? option.value : void 0;
|
|
74
|
+
const command = value(options.cli)?.split(/\s+/).filter((word) => word !== "");
|
|
75
|
+
const results = yield* Effect.promise(() => conform({
|
|
76
|
+
mcp: value(options.mcp),
|
|
77
|
+
http: value(options.http),
|
|
78
|
+
cli: command,
|
|
79
|
+
webmcp: value(options.webmcp),
|
|
80
|
+
maximumBytes: options.maximumBytes
|
|
81
|
+
}));
|
|
82
|
+
if (results.length === 0) {
|
|
83
|
+
yield* Console.error("Give at least one binding: --mcp, --http, --cli or --webmcp.");
|
|
84
|
+
return yield* fail;
|
|
85
|
+
}
|
|
86
|
+
for (const result of results) yield* Console.log(`${result.passed ? "✓" : "✗"} ${result.binding}: ${result.name} (${result.rule})${result.detail ? `\n ${result.detail}` : ""}`);
|
|
87
|
+
const failed = results.filter((result) => !result.passed).length;
|
|
88
|
+
yield* Console.log(`\n${results.length - failed} passed, ${failed} failed.`);
|
|
89
|
+
if (failed > 0) return yield* fail;
|
|
90
|
+
})).pipe(Command.withDescription("Test a running implementation of the conformance fixture through its bindings."));
|
|
91
|
+
Command.make("acid-cat").pipe(Command.withDescription("Check, format and view AcidCat catalogs, and test implementations."), Command.withSubcommands([
|
|
92
|
+
checkCommand,
|
|
93
|
+
formatCommand,
|
|
94
|
+
viewCommand,
|
|
95
|
+
conformCommand
|
|
96
|
+
])).pipe(Command.run({ version }), Effect.provide(NodeServices.layer), NodeRuntime.runMain);
|
|
97
|
+
//#endregion
|
|
98
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,61 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@acid-cat/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "The AcidCat command line: check, format and view catalogs, and test implementations.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"acidcat",
|
|
7
|
+
"cli",
|
|
8
|
+
"conformance"
|
|
9
|
+
],
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/Hebilicious/acidcat.git",
|
|
14
|
+
"directory": "packages/cli"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/Hebilicious/acidcat/tree/main/packages/cli#readme",
|
|
17
|
+
"bugs": "https://github.com/Hebilicious/acidcat/issues",
|
|
18
|
+
"type": "module",
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=24"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"conformance/fixture.acidcat.json"
|
|
25
|
+
],
|
|
26
|
+
"bin": {
|
|
27
|
+
"acid-cat": "./dist/main.js"
|
|
28
|
+
},
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"default": "./dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./conformance/fixture.acidcat.json": "./conformance/fixture.acidcat.json"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@acid-cat/catalog": "0.2.0",
|
|
41
|
+
"@acid-cat/spec-ts": "0.2.0",
|
|
42
|
+
"@effect/platform-node": "4.0.4",
|
|
43
|
+
"@effect/platform-node-shared": "4.0.4",
|
|
44
|
+
"@modelcontextprotocol/client": "2.3.1",
|
|
45
|
+
"effect": "4.0.4",
|
|
46
|
+
"fast-check": "4.10.2"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"playwright": "^1.64.0"
|
|
50
|
+
},
|
|
51
|
+
"peerDependenciesMeta": {
|
|
52
|
+
"playwright": {
|
|
53
|
+
"optional": true
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/node": "26.6.5",
|
|
58
|
+
"playwright": "1.64.0",
|
|
59
|
+
"typescript": "7.0.2"
|
|
60
|
+
}
|
|
6
61
|
}
|