@yansirplus/cli 0.5.17 → 0.5.18
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 +12 -6
- package/agent-catalog/agentOS/SKILL.md +22 -0
- package/agent-catalog/agentOS/references/agent/decision-graph.json +530 -0
- package/agent-catalog/agentOS/references/agent/errors.json +497 -0
- package/agent-catalog/agentOS/references/agent/invariant-matrix.json +337 -0
- package/agent-catalog/agentOS/references/agent/primitives.json +989 -0
- package/agent-catalog/agentOS/references/agent/recipes.json +109 -0
- package/agent-catalog/agentOS/references/agent/start-here.md +25 -0
- package/agent-catalog/agentOS/references/package-map.md +72 -0
- package/agent-catalog/agentOS/references/provenance.json +251 -0
- package/agent-catalog/agentOS/references/public-api/cli.md +20 -0
- package/agent-catalog/agentOS/references/public-api/client.md +88 -0
- package/agent-catalog/agentOS/references/public-api/core.md +1817 -0
- package/agent-catalog/agentOS/references/public-api/runtime.md +794 -0
- package/dist/build/agent-authoring/config.d.ts +20 -5
- package/dist/build/agent-authoring/config.js +132 -32
- package/dist/build/agent-authoring/manifest-compiler.d.ts +131 -2
- package/dist/build/agent-authoring/manifest-compiler.js +630 -8
- package/dist/build/agent-authoring/shared.d.ts +2 -0
- package/dist/build/agent-authoring/shared.js +2 -0
- package/dist/build/agent-authoring/static-target.d.ts +6 -3
- package/dist/build/agent-authoring/static-target.js +1807 -286
- package/dist/build/agent-authoring.d.ts +3 -3
- package/dist/build/agent-authoring.js +1 -1
- package/dist/build/build-cli.d.ts +1 -1
- package/dist/build/build-cli.js +1614 -26
- package/dist/check/algorithmic/client-boundary-checks.mjs +3 -34
- package/dist/check/algorithmic/convergence-smoke-checks.mjs +652 -6
- package/dist/check/algorithmic/distribution-checks.mjs +8 -7
- package/dist/check/algorithmic/package-boundary-checks.mjs +3 -2
- package/dist/check/algorithmic/repo-surface-checks.mjs +55 -1
- package/dist/check/algorithmic/static-target-checks.mjs +83 -5
- package/dist/check/algorithmic-checks.mjs +10 -17
- package/dist/check/default-gate.mjs +3 -3
- package/dist/check/effect-scan-gate.mjs +121 -0
- package/dist/check/package-graph.mjs +2 -32
- package/dist/consumer-overlay.mjs +802 -0
- package/dist/lib/public-api-model.mjs +19 -0
- package/dist/lib/repo-source-files.mjs +26 -0
- package/dist/lib/ts-module-loader.mjs +44 -0
- package/dist/lib/workspace-manifest.mjs +77 -0
- package/dist/main.mjs +151 -21
- package/package.json +8 -4
- package/dist/check/check-coverage.mjs +0 -231
- package/dist/generate/generate-agent-docs.mjs +0 -435
- package/dist/generate/generate-carrier-reference.mjs +0 -514
- package/dist/generate/generate-docs.mjs +0 -345
- package/dist/generate/generate-effect-skill-manifests.mjs +0 -193
- package/dist/generate/project-docs-site.mjs +0 -190
- package/dist/lib/boundary-rules.mjs +0 -63
- package/dist/lib/capability-routes.mjs +0 -354
- package/dist/lib/projection-sink.mjs +0 -113
|
@@ -167,6 +167,25 @@ const sourceExportRecordsFromAst = (file, entrypoint, seen) => {
|
|
|
167
167
|
records.push(
|
|
168
168
|
...sourceExportRecordsFromAst(resolveRelativeModule(abs, specifier), entrypoint, seen),
|
|
169
169
|
);
|
|
170
|
+
} else if (
|
|
171
|
+
statement.exportClause !== undefined &&
|
|
172
|
+
ts.isNamedExports(statement.exportClause) &&
|
|
173
|
+
specifier !== null &&
|
|
174
|
+
specifier.startsWith(".")
|
|
175
|
+
) {
|
|
176
|
+
const target = resolveRelativeModule(abs, specifier);
|
|
177
|
+
const targetRecords = sourceExportRecordsFromAst(target, entrypoint, new Set(seen));
|
|
178
|
+
for (const element of statement.exportClause.elements) {
|
|
179
|
+
const importedName = element.propertyName?.text ?? element.name.text;
|
|
180
|
+
const exportedName = element.name.text;
|
|
181
|
+
const targetRecord = targetRecords.find((record) => record.name === importedName);
|
|
182
|
+
if (targetRecord === undefined) continue;
|
|
183
|
+
records.push({
|
|
184
|
+
...targetRecord,
|
|
185
|
+
name: exportedName,
|
|
186
|
+
key: `${entrypoint}:${exportedName}`,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
170
189
|
}
|
|
171
190
|
continue;
|
|
172
191
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
export const repoSourceIgnoredDirectoryNames = Object.freeze(
|
|
5
|
+
new Set(["node_modules", "dist", ".wrangler", ".turbo", ".parallel", ".cst", ".git", ".codex"]),
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
const compare = (left, right) => left.localeCompare(right);
|
|
9
|
+
|
|
10
|
+
const toRepoPath = (file) => file.split(path.sep).join("/");
|
|
11
|
+
|
|
12
|
+
export const walkRepoSourceFiles = (repoRoot, relativePath = ".", options = {}) => {
|
|
13
|
+
const absolutePath = path.join(repoRoot, relativePath);
|
|
14
|
+
if (!fs.existsSync(absolutePath)) return [];
|
|
15
|
+
const stat = fs.statSync(absolutePath);
|
|
16
|
+
if (stat.isFile()) return [toRepoPath(relativePath)];
|
|
17
|
+
const ignored = options.ignored ?? repoSourceIgnoredDirectoryNames;
|
|
18
|
+
const files = [];
|
|
19
|
+
for (const entry of fs.readdirSync(absolutePath, { withFileTypes: true })) {
|
|
20
|
+
if (entry.isDirectory() && ignored.has(entry.name)) continue;
|
|
21
|
+
const child = path.join(relativePath, entry.name);
|
|
22
|
+
if (entry.isDirectory()) files.push(...walkRepoSourceFiles(repoRoot, child, options));
|
|
23
|
+
if (entry.isFile()) files.push(toRepoPath(child));
|
|
24
|
+
}
|
|
25
|
+
return files.sort(compare);
|
|
26
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { mkdir, rm } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
|
|
7
|
+
import { build } from "esbuild";
|
|
8
|
+
|
|
9
|
+
const compare = (left, right) => left.localeCompare(right);
|
|
10
|
+
|
|
11
|
+
export const bundleModuleForNode = async (
|
|
12
|
+
entryPoint,
|
|
13
|
+
{ define = {}, external = [], prefix = "agentos-ts-module-", tempRoot = os.tmpdir() } = {},
|
|
14
|
+
) => {
|
|
15
|
+
const outDir = path.join(tempRoot, `${prefix}${randomUUID()}`);
|
|
16
|
+
const outfile = path.join(outDir, "entry.mjs");
|
|
17
|
+
await mkdir(outDir, { recursive: true });
|
|
18
|
+
await build({
|
|
19
|
+
entryPoints: [entryPoint],
|
|
20
|
+
outfile,
|
|
21
|
+
bundle: true,
|
|
22
|
+
format: "esm",
|
|
23
|
+
platform: "node",
|
|
24
|
+
target: "node22",
|
|
25
|
+
define,
|
|
26
|
+
external: [...new Set(["esbuild", "cloudflare:*", ...external])].sort(compare),
|
|
27
|
+
logLevel: "silent",
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
outfile,
|
|
31
|
+
cleanup: async () => {
|
|
32
|
+
await rm(outDir, { recursive: true, force: true });
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const importBundledModule = async (entryPoint, options = {}) => {
|
|
38
|
+
const bundled = await bundleModuleForNode(entryPoint, options);
|
|
39
|
+
try {
|
|
40
|
+
return await import(`${pathToFileURL(bundled.outfile).href}?agentos=${Date.now()}`);
|
|
41
|
+
} finally {
|
|
42
|
+
await bundled.cleanup();
|
|
43
|
+
}
|
|
44
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import { parse as parseYaml } from "yaml";
|
|
5
|
+
|
|
6
|
+
const compare = (left, right) => left.localeCompare(right);
|
|
7
|
+
const readJsonFile = (file) => JSON.parse(fs.readFileSync(file, "utf8"));
|
|
8
|
+
|
|
9
|
+
const workspaceManifestPath = (repoRoot) => path.join(repoRoot, "pnpm-workspace.yaml");
|
|
10
|
+
|
|
11
|
+
const requireRecord = (value, label) => {
|
|
12
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
13
|
+
throw new Error(`${label} must be an object`);
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const readWorkspaceManifest = (repoRoot) => {
|
|
19
|
+
const file = workspaceManifestPath(repoRoot);
|
|
20
|
+
if (!fs.existsSync(file)) {
|
|
21
|
+
throw new Error("pnpm-workspace.yaml is the workspace manifest SSOT and must exist");
|
|
22
|
+
}
|
|
23
|
+
const manifest = requireRecord(parseYaml(fs.readFileSync(file, "utf8")), "pnpm-workspace.yaml");
|
|
24
|
+
return manifest;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const workspacePackagePatterns = (repoRoot) => {
|
|
28
|
+
const packages = readWorkspaceManifest(repoRoot).packages;
|
|
29
|
+
if (!Array.isArray(packages)) {
|
|
30
|
+
throw new Error("pnpm-workspace.yaml packages must be an array");
|
|
31
|
+
}
|
|
32
|
+
return packages.filter((entry) => typeof entry === "string").sort(compare);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const workspaceCatalog = (repoRoot) => {
|
|
36
|
+
const catalog = readWorkspaceManifest(repoRoot).catalog;
|
|
37
|
+
if (catalog === undefined) return {};
|
|
38
|
+
return requireRecord(catalog, "pnpm-workspace.yaml catalog");
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const workspaceOverrides = (repoRoot) => {
|
|
42
|
+
const overrides = readWorkspaceManifest(repoRoot).overrides;
|
|
43
|
+
if (overrides === undefined) return {};
|
|
44
|
+
return requireRecord(overrides, "pnpm-workspace.yaml overrides");
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const workspacePackagePaths = (repoRoot) => {
|
|
48
|
+
const paths = new Set();
|
|
49
|
+
for (const workspace of workspacePackagePatterns(repoRoot)) {
|
|
50
|
+
if (workspace.endsWith("/*")) {
|
|
51
|
+
const base = workspace.slice(0, -2);
|
|
52
|
+
const baseDir = path.join(repoRoot, base);
|
|
53
|
+
if (!fs.existsSync(baseDir)) continue;
|
|
54
|
+
for (const entry of fs.readdirSync(baseDir, { withFileTypes: true })) {
|
|
55
|
+
if (!entry.isDirectory()) continue;
|
|
56
|
+
const packagePath = `${base}/${entry.name}`;
|
|
57
|
+
if (fs.existsSync(path.join(repoRoot, packagePath, "package.json"))) {
|
|
58
|
+
paths.add(packagePath);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (fs.existsSync(path.join(repoRoot, workspace, "package.json"))) {
|
|
65
|
+
paths.add(workspace);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return [...paths].sort(compare);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const workspacePackageRecords = (repoRoot) =>
|
|
72
|
+
workspacePackagePaths(repoRoot)
|
|
73
|
+
.map((packagePath) => ({
|
|
74
|
+
name: readJsonFile(path.join(repoRoot, packagePath, "package.json")).name,
|
|
75
|
+
path: packagePath,
|
|
76
|
+
}))
|
|
77
|
+
.sort((left, right) => left.path.localeCompare(right.path));
|
package/dist/main.mjs
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
|
-
import {
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
4
6
|
|
|
7
|
+
import { bundleModuleForNode } from "./lib/ts-module-loader.mjs";
|
|
8
|
+
import {
|
|
9
|
+
consumerCheck,
|
|
10
|
+
consumerStatus,
|
|
11
|
+
installConsumer,
|
|
12
|
+
restoreConsumer,
|
|
13
|
+
} from "./consumer-overlay.mjs";
|
|
5
14
|
import {
|
|
6
15
|
algorithmicCheckerAcceptsArgs,
|
|
7
16
|
hasAlgorithmicChecker,
|
|
@@ -13,9 +22,27 @@ import {
|
|
|
13
22
|
runAffectedGates,
|
|
14
23
|
} from "./check/gate-selector.mjs";
|
|
15
24
|
import { runDefaultGate } from "./check/default-gate.mjs";
|
|
25
|
+
import { runEffectScanGate } from "./check/effect-scan-gate.mjs";
|
|
16
26
|
import { listGuards, runGroup, runGuard } from "./runner.mjs";
|
|
17
27
|
|
|
18
|
-
const
|
|
28
|
+
const packageRootFromMain = () => path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
29
|
+
|
|
30
|
+
const repoRootFromMain = () => path.dirname(path.dirname(packageRootFromMain()));
|
|
31
|
+
|
|
32
|
+
const readReleaseVersion = () => {
|
|
33
|
+
const rootPackagePath = path.join(repoRootFromMain(), "package.json");
|
|
34
|
+
const packagePath = existsSync(rootPackagePath)
|
|
35
|
+
? rootPackagePath
|
|
36
|
+
: path.join(packageRootFromMain(), "package.json");
|
|
37
|
+
const packageJson = JSON.parse(readFileSync(packagePath, "utf8"));
|
|
38
|
+
const version = packageJson.agentOsRelease?.version ?? packageJson.version;
|
|
39
|
+
if (typeof version !== "string" || version.length === 0) {
|
|
40
|
+
throw new Error("package.json version must be a non-empty string");
|
|
41
|
+
}
|
|
42
|
+
return version;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const version = readReleaseVersion();
|
|
19
46
|
|
|
20
47
|
const helpText = `agentOS repository CLI ${version}
|
|
21
48
|
|
|
@@ -23,11 +50,21 @@ Usage:
|
|
|
23
50
|
agentos --help
|
|
24
51
|
agentos --version
|
|
25
52
|
agentos build [--cwd <path>] [--config <path>] [--package-scope <scope>]
|
|
53
|
+
agentos info [--cwd <path>] [--config <path>] [--json]
|
|
54
|
+
agentos serve [--cwd <path>] [--config <path>] [--package-scope <scope>] [--host <host>] [--port <port>] [--llm config|test] [--llm-response <text>] [--json]
|
|
55
|
+
agentos dev [--cwd <path>] [--config <path>] [--package-scope <scope>] [--host <host>] [--port <port>] [--llm config|test] [--llm-response <text>] [--json]
|
|
56
|
+
agentos eval [--cwd <path>] [--config <path>] [--package-scope <scope>] [--target local|remote] [--base-url <url>] [--header <name=value>] [--llm config|test] [--llm-response <text>] [--json]
|
|
57
|
+
agentos preflight llm [--cwd <path>] [--config <path>] [--route <binding-ref>] [--json]
|
|
58
|
+
agentos consumer install /path/to/consumer [--from-manifest <path>] [--no-install] [--skip-pack] [--json]
|
|
59
|
+
agentos consumer status /path/to/consumer [--json] [--check-npm] [--registry <url>]
|
|
60
|
+
agentos consumer check /path/to/consumer [--json] [--check-npm] [--registry <url>]
|
|
61
|
+
agentos consumer restore /path/to/consumer [--no-install] [--json]
|
|
26
62
|
agentos check all
|
|
27
63
|
agentos check default
|
|
28
64
|
agentos check structural
|
|
29
65
|
agentos check affected [--base <ref>] [--head <ref>] [--json] [--explain] [--run]
|
|
30
66
|
agentos check docs
|
|
67
|
+
agentos check effect-scan [--repo <path>] [--evidence <path>] [--scanner <command>]
|
|
31
68
|
agentos check effect-manifests
|
|
32
69
|
agentos check release
|
|
33
70
|
agentos check site
|
|
@@ -50,6 +87,12 @@ const fail = (message) => {
|
|
|
50
87
|
if (
|
|
51
88
|
message.startsWith("agentos:") ||
|
|
52
89
|
message.startsWith("agentos build:") ||
|
|
90
|
+
message.startsWith("agentos info:") ||
|
|
91
|
+
message.startsWith("agentos serve:") ||
|
|
92
|
+
message.startsWith("agentos dev:") ||
|
|
93
|
+
message.startsWith("agentos eval:") ||
|
|
94
|
+
message.startsWith("agentos preflight:") ||
|
|
95
|
+
message.startsWith("agentos consumer:") ||
|
|
53
96
|
message.startsWith("agentos check:") ||
|
|
54
97
|
message.startsWith("agentos generate:")
|
|
55
98
|
) {
|
|
@@ -64,25 +107,89 @@ const expectNoExtraArgs = (args, command) => {
|
|
|
64
107
|
}
|
|
65
108
|
};
|
|
66
109
|
|
|
67
|
-
const
|
|
110
|
+
const runBuildRunner = async (command, args) => {
|
|
68
111
|
const runner = fileURLToPath(new URL("./build/build-cli.js", import.meta.url));
|
|
69
|
-
await
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
reject(new Error(`agentos build: failed to start bun: ${error.message}`));
|
|
73
|
-
});
|
|
74
|
-
child.on("exit", (code, signal) => {
|
|
75
|
-
if (signal !== null) {
|
|
76
|
-
reject(new Error(`agentos build: build runner terminated by ${signal}`));
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
if (code !== 0) {
|
|
80
|
-
reject(new Error(`agentos build: build runner failed with exit code ${code ?? 1}`));
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
resolve();
|
|
84
|
-
});
|
|
112
|
+
const bundled = await bundleModuleForNode(runner, {
|
|
113
|
+
prefix: "agentos-build-runner-",
|
|
114
|
+
tempRoot: path.join(packageRootFromMain(), "node_modules", ".cache", "agentos-build"),
|
|
85
115
|
});
|
|
116
|
+
try {
|
|
117
|
+
await new Promise((resolve, reject) => {
|
|
118
|
+
const child = spawn(process.execPath, [bundled.outfile, command, ...args], {
|
|
119
|
+
stdio: "inherit",
|
|
120
|
+
});
|
|
121
|
+
child.on("error", (error) => {
|
|
122
|
+
reject(
|
|
123
|
+
new Error(`agentos ${command}: failed to start node build runner: ${error.message}`),
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
child.on("exit", (code, signal) => {
|
|
127
|
+
if (signal !== null) {
|
|
128
|
+
reject(new Error(`agentos ${command}: build runner terminated by ${signal}`));
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (code !== 0) {
|
|
132
|
+
process.exitCode = code ?? 1;
|
|
133
|
+
}
|
|
134
|
+
resolve();
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
} finally {
|
|
138
|
+
await bundled.cleanup();
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const runBuild = async (args) => runBuildRunner("build", args);
|
|
143
|
+
|
|
144
|
+
const runInfo = async (args) => runBuildRunner("info", args);
|
|
145
|
+
|
|
146
|
+
const runServe = async (args) => runBuildRunner("serve", args);
|
|
147
|
+
|
|
148
|
+
const runDev = async (args) => runBuildRunner("dev", args);
|
|
149
|
+
|
|
150
|
+
const runEval = async (args) => runBuildRunner("eval", args);
|
|
151
|
+
|
|
152
|
+
const runPreflight = async (args) => runBuildRunner("preflight", args);
|
|
153
|
+
|
|
154
|
+
const sourceConsumerProducer = () => {
|
|
155
|
+
const modulePath = path.join(repoRootFromMain(), "tooling/distribution/pack-check.mjs");
|
|
156
|
+
const supportPath = path.join(repoRootFromMain(), "tooling/distribution/support.mjs");
|
|
157
|
+
if (!existsSync(modulePath) || !existsSync(supportPath)) return undefined;
|
|
158
|
+
return {
|
|
159
|
+
sourceRoot: repoRootFromMain(),
|
|
160
|
+
defaultInstallManifestPath: path.join(
|
|
161
|
+
repoRootFromMain(),
|
|
162
|
+
"dist/internal-npm/install-manifest.json",
|
|
163
|
+
),
|
|
164
|
+
produceInstallManifest: async () => {
|
|
165
|
+
const producer = await import(pathToFileURL(modulePath).href);
|
|
166
|
+
producer.packInternal();
|
|
167
|
+
return path.join(repoRootFromMain(), "dist/internal-npm/install-manifest.json");
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const runConsumer = async (args) => {
|
|
173
|
+
const [command, ...rest] = args;
|
|
174
|
+
const commandArgs = rest[0] === "--" ? rest.slice(1) : rest;
|
|
175
|
+
const sourceContext = sourceConsumerProducer() ?? {};
|
|
176
|
+
const context = { packageRoot: packageRootFromMain(), ...sourceContext };
|
|
177
|
+
switch (command) {
|
|
178
|
+
case "install":
|
|
179
|
+
await installConsumer(commandArgs, context);
|
|
180
|
+
return;
|
|
181
|
+
case "status":
|
|
182
|
+
consumerStatus(commandArgs, context);
|
|
183
|
+
return;
|
|
184
|
+
case "check":
|
|
185
|
+
consumerCheck(commandArgs, context);
|
|
186
|
+
return;
|
|
187
|
+
case "restore":
|
|
188
|
+
restoreConsumer(commandArgs, context);
|
|
189
|
+
return;
|
|
190
|
+
default:
|
|
191
|
+
throw new Error("agentos consumer: choose one of install, status, check, restore");
|
|
192
|
+
}
|
|
86
193
|
};
|
|
87
194
|
|
|
88
195
|
const runCheck = async (args) => {
|
|
@@ -134,6 +241,9 @@ const runCheck = async (args) => {
|
|
|
134
241
|
expectNoExtraArgs(rest, "agentos check docs");
|
|
135
242
|
await runGroup("check-docs");
|
|
136
243
|
return;
|
|
244
|
+
case "effect-scan":
|
|
245
|
+
runEffectScanGate(rest, { defaultRepoRoot: repoRootFromMain() });
|
|
246
|
+
return;
|
|
137
247
|
case "effect-manifests":
|
|
138
248
|
expectNoExtraArgs(rest, "agentos check effect-manifests");
|
|
139
249
|
await runGroup("check-effect-manifests");
|
|
@@ -170,7 +280,7 @@ const runCheck = async (args) => {
|
|
|
170
280
|
return;
|
|
171
281
|
}
|
|
172
282
|
throw new Error(
|
|
173
|
-
"agentos check: choose one of all, default, structural, affected, docs, effect-manifests, release, site, guard-coverage, guard, guards, or an algorithmic checker id",
|
|
283
|
+
"agentos check: choose one of all, default, structural, affected, docs, effect-scan, effect-manifests, release, site, guard-coverage, guard, guards, or an algorithmic checker id",
|
|
174
284
|
);
|
|
175
285
|
}
|
|
176
286
|
};
|
|
@@ -215,6 +325,24 @@ const main = async () => {
|
|
|
215
325
|
case "build":
|
|
216
326
|
await runBuild(rest);
|
|
217
327
|
return;
|
|
328
|
+
case "info":
|
|
329
|
+
await runInfo(rest);
|
|
330
|
+
return;
|
|
331
|
+
case "serve":
|
|
332
|
+
await runServe(rest);
|
|
333
|
+
return;
|
|
334
|
+
case "dev":
|
|
335
|
+
await runDev(rest);
|
|
336
|
+
return;
|
|
337
|
+
case "eval":
|
|
338
|
+
await runEval(rest);
|
|
339
|
+
return;
|
|
340
|
+
case "preflight":
|
|
341
|
+
await runPreflight(rest);
|
|
342
|
+
return;
|
|
343
|
+
case "consumer":
|
|
344
|
+
await runConsumer(rest);
|
|
345
|
+
return;
|
|
218
346
|
case "check":
|
|
219
347
|
await runCheck(rest);
|
|
220
348
|
return;
|
|
@@ -222,7 +350,9 @@ const main = async () => {
|
|
|
222
350
|
await runGenerate(rest);
|
|
223
351
|
return;
|
|
224
352
|
default:
|
|
225
|
-
throw new Error(
|
|
353
|
+
throw new Error(
|
|
354
|
+
"agentos: choose one of build, info, serve, dev, eval, preflight, consumer, check, generate",
|
|
355
|
+
);
|
|
226
356
|
}
|
|
227
357
|
};
|
|
228
358
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yansirplus/cli",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.18",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"publishConfig": {
|
|
@@ -19,14 +19,18 @@
|
|
|
19
19
|
},
|
|
20
20
|
"files": [
|
|
21
21
|
"dist",
|
|
22
|
+
"agent-catalog",
|
|
22
23
|
"README.md",
|
|
23
24
|
"PUBLIC_API.md"
|
|
24
25
|
],
|
|
25
26
|
"dependencies": {
|
|
26
|
-
"@yansirplus/core": "0.5.
|
|
27
|
-
"@yansirplus/
|
|
27
|
+
"@yansirplus/core": "0.5.18",
|
|
28
|
+
"@yansirplus/evals": "0.5.18",
|
|
29
|
+
"@yansirplus/runtime": "0.5.18",
|
|
30
|
+
"esbuild": "0.27.3",
|
|
31
|
+
"yaml": "2.9.0"
|
|
28
32
|
},
|
|
29
33
|
"peerDependencies": {
|
|
30
|
-
"effect": "
|
|
34
|
+
"effect": "4.0.0-beta.84"
|
|
31
35
|
}
|
|
32
36
|
}
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { listAlgorithmicCheckers } from "./algorithmic-checks.mjs";
|
|
6
|
-
|
|
7
|
-
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../../..");
|
|
8
|
-
|
|
9
|
-
const readJson = (relativePath) =>
|
|
10
|
-
JSON.parse(fs.readFileSync(path.join(repoRoot, relativePath), "utf8"));
|
|
11
|
-
|
|
12
|
-
const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
13
|
-
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
14
|
-
|
|
15
|
-
const failures = [];
|
|
16
|
-
const fail = (message) => failures.push(message);
|
|
17
|
-
|
|
18
|
-
const coverage = readJson("packages/cli/src/check/check-coverage.source.json");
|
|
19
|
-
const rulesSource = readJson("docs/agent/boundary-rules.source.json");
|
|
20
|
-
const cliPackage = readJson("packages/cli/package.json");
|
|
21
|
-
|
|
22
|
-
if (coverage.schemaVersion !== 1) fail("check coverage schemaVersion must be 1");
|
|
23
|
-
if (!Array.isArray(coverage.entries) || coverage.entries.length === 0) {
|
|
24
|
-
fail("check coverage entries must be non-empty");
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const rules = new Map((rulesSource.rules ?? []).map((rule) => [rule.id, rule]));
|
|
28
|
-
const algorithmicCheckers = new Set(listAlgorithmicCheckers());
|
|
29
|
-
const coveredRuleIds = new Set();
|
|
30
|
-
const genericPackageNegativeWitness =
|
|
31
|
-
"package-owned test fails when the owned contract is violated";
|
|
32
|
-
|
|
33
|
-
const assertProofWitnesses = (label, assertionIndex, assertion) => {
|
|
34
|
-
const witnesses = assertion.packageWitnesses;
|
|
35
|
-
if (witnesses === undefined) return;
|
|
36
|
-
if (!Array.isArray(witnesses) || witnesses.length === 0)
|
|
37
|
-
fail(`${label}: assertions[${assertionIndex}] packageWitnesses must be non-empty when present`);
|
|
38
|
-
|
|
39
|
-
for (const [witnessIndex, witness] of witnesses.entries()) {
|
|
40
|
-
const witnessLabel = `${label}: assertions[${assertionIndex}].packageWitnesses[${witnessIndex}]`;
|
|
41
|
-
if (!isRecord(witness)) {
|
|
42
|
-
fail(`${witnessLabel} must be an object`);
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
if (typeof witness.file !== "string" || !witness.file.startsWith("packages/")) {
|
|
46
|
-
fail(`${witnessLabel}.file must be a package test path`);
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
if (typeof witness.name !== "string" || witness.name.length === 0) {
|
|
50
|
-
fail(`${witnessLabel}.name must be non-empty`);
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const absolutePath = path.join(repoRoot, witness.file);
|
|
55
|
-
if (!fs.existsSync(absolutePath)) {
|
|
56
|
-
fail(`${witnessLabel}.file does not exist`);
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
const source = fs.readFileSync(absolutePath, "utf8");
|
|
60
|
-
const testNamePattern = new RegExp(
|
|
61
|
-
`\\b(?:describe|it|test)(?:\\.effect)?\\(\\s*(["'\`])${escapeRegExp(witness.name)}\\1`,
|
|
62
|
-
"u",
|
|
63
|
-
);
|
|
64
|
-
if (!testNamePattern.test(source)) {
|
|
65
|
-
fail(`${witnessLabel}.name was not found as a test or describe name`);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
for (const [index, entry] of (coverage.entries ?? []).entries()) {
|
|
71
|
-
const label =
|
|
72
|
-
isRecord(entry) && typeof entry.ruleId === "string" ? entry.ruleId : `entry[${index}]`;
|
|
73
|
-
if (!isRecord(entry)) {
|
|
74
|
-
fail(`${label}: coverage entry must be an object`);
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
if (!isRecord(entry.source)) fail(`${label}: missing source`);
|
|
78
|
-
if (!isRecord(entry.target)) fail(`${label}: missing target`);
|
|
79
|
-
if (!Array.isArray(entry.assertions) || entry.assertions.length === 0) {
|
|
80
|
-
fail(`${label}: assertions must be non-empty`);
|
|
81
|
-
} else {
|
|
82
|
-
for (const [assertionIndex, assertion] of entry.assertions.entries()) {
|
|
83
|
-
if (!isRecord(assertion)) {
|
|
84
|
-
fail(`${label}: assertions[${assertionIndex}] must be an object`);
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
if (
|
|
88
|
-
typeof assertion.failureCondition !== "string" ||
|
|
89
|
-
assertion.failureCondition.length === 0
|
|
90
|
-
) {
|
|
91
|
-
fail(`${label}: assertions[${assertionIndex}] missing failureCondition`);
|
|
92
|
-
}
|
|
93
|
-
if (typeof assertion.negativeWitness !== "string" || assertion.negativeWitness.length === 0) {
|
|
94
|
-
fail(`${label}: assertions[${assertionIndex}] missing negativeWitness`);
|
|
95
|
-
}
|
|
96
|
-
if (assertion.negativeWitness === genericPackageNegativeWitness) {
|
|
97
|
-
fail(`${label}: assertions[${assertionIndex}] has generic package negativeWitness`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (typeof entry.ruleId === "string" && entry.ruleId.includes(",")) {
|
|
103
|
-
for (const ruleId of entry.ruleId.split(",")) coveredRuleIds.add(ruleId);
|
|
104
|
-
} else if (typeof entry.ruleId === "string") {
|
|
105
|
-
coveredRuleIds.add(entry.ruleId);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (entry.target?.kind === "algorithmic") {
|
|
109
|
-
const rule = rules.get(entry.target.ruleId);
|
|
110
|
-
if (!isRecord(rule)) {
|
|
111
|
-
fail(`${label}: algorithmic target references unknown rule ${entry.target.ruleId}`);
|
|
112
|
-
} else if (rule.acceptance?.engine !== "algorithmic") {
|
|
113
|
-
fail(`${label}: algorithmic target rule must use algorithmic acceptance`);
|
|
114
|
-
} else if (rule.acceptance.checker !== entry.target.checker) {
|
|
115
|
-
fail(`${label}: coverage checker ${entry.target.checker} does not match rule acceptance`);
|
|
116
|
-
}
|
|
117
|
-
if (!algorithmicCheckers.has(entry.target.checker)) {
|
|
118
|
-
fail(`${label}: missing algorithmic checker ${entry.target.checker}`);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (entry.target?.kind === "proofClass") {
|
|
123
|
-
const rule = rules.get(entry.target.ruleId);
|
|
124
|
-
if (!isRecord(rule)) {
|
|
125
|
-
fail(`${label}: proofClass target references unknown rule ${entry.target.ruleId}`);
|
|
126
|
-
} else if (rule.acceptance?.engine !== "proofClass") {
|
|
127
|
-
fail(`${label}: proofClass target rule must use proofClass acceptance`);
|
|
128
|
-
} else {
|
|
129
|
-
for (const [assertionIndex, assertion] of (entry.assertions ?? []).entries()) {
|
|
130
|
-
if (isRecord(assertion)) {
|
|
131
|
-
assertProofWitnesses(label, assertionIndex, assertion);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (entry.target?.kind === "manifestRule") {
|
|
138
|
-
const rule = rules.get(entry.target.ruleId);
|
|
139
|
-
if (!isRecord(rule)) {
|
|
140
|
-
fail(`${label}: manifestRule target references unknown rule ${entry.target.ruleId}`);
|
|
141
|
-
} else if (rule.acceptance?.engine !== entry.target.engine) {
|
|
142
|
-
fail(`${label}: manifestRule target engine must match rule acceptance`);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (typeof entry.ruleId === "string" && !entry.ruleId.includes(",")) {
|
|
147
|
-
const rule = rules.get(entry.ruleId);
|
|
148
|
-
if (isRecord(rule) && entry.owner !== rule.owner) {
|
|
149
|
-
fail(
|
|
150
|
-
`${label}: coverage owner ${entry.owner} does not match boundary rule owner ${rule.owner}`,
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (entry.target?.kind === "algorithmic-helper") {
|
|
156
|
-
for (const checker of entry.target.checkers ?? []) {
|
|
157
|
-
if (!algorithmicCheckers.has(checker))
|
|
158
|
-
fail(`${label}: helper references unknown checker ${checker}`);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
for (const rule of rulesSource.rules ?? []) {
|
|
164
|
-
if (!coveredRuleIds.has(rule.id)) fail(`${rule.id}: rule lacks check coverage entry`);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const sourceText = fs.readFileSync(
|
|
168
|
-
path.join(repoRoot, "docs/agent/boundary-rules.source.json"),
|
|
169
|
-
"utf8",
|
|
170
|
-
);
|
|
171
|
-
if (sourceText.includes("positiveAcceptance")) {
|
|
172
|
-
fail("docs/agent/boundary-rules.source.json: positiveAcceptance is no longer allowed");
|
|
173
|
-
}
|
|
174
|
-
if (/\s--fix(?:\s|")/u.test(sourceText)) {
|
|
175
|
-
fail("docs/agent/boundary-rules.source.json: check commands must not include --fix");
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const walk = (relativePath) => {
|
|
179
|
-
const absolutePath = path.join(repoRoot, relativePath);
|
|
180
|
-
const files = [];
|
|
181
|
-
for (const entry of fs.readdirSync(absolutePath, { withFileTypes: true })) {
|
|
182
|
-
const child = path.join(relativePath, entry.name);
|
|
183
|
-
if (entry.isDirectory()) files.push(...walk(child));
|
|
184
|
-
if (entry.isFile()) files.push(child.split(path.sep).join("/"));
|
|
185
|
-
}
|
|
186
|
-
return files;
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
const cliMjsFiles = walk("packages/cli/src").filter((file) => file.endsWith(".mjs"));
|
|
190
|
-
const checkMjsFiles = cliMjsFiles.filter((file) => file.startsWith("packages/cli/src/check/"));
|
|
191
|
-
if (cliMjsFiles.length > 19) {
|
|
192
|
-
fail(`packages/cli/src: expected at most 19 .mjs files; observed ${cliMjsFiles.length}`);
|
|
193
|
-
}
|
|
194
|
-
if (checkMjsFiles.length > 12) {
|
|
195
|
-
fail(`packages/cli/src/check: expected at most 12 .mjs files; observed ${checkMjsFiles.length}`);
|
|
196
|
-
}
|
|
197
|
-
for (const file of checkMjsFiles) {
|
|
198
|
-
const content = fs.readFileSync(path.join(repoRoot, file), "utf8");
|
|
199
|
-
const selfTestFlag = "--" + "self-test";
|
|
200
|
-
if (content.includes(selfTestFlag)) fail(`${file}: per-checker self-test flag is not allowed`);
|
|
201
|
-
const harnessPattern = new RegExp(
|
|
202
|
-
["mkd" + "te" + "mp", "tm" + "pdir", "Tem" + "por" + "ary", "te" + "mp"].join("|"),
|
|
203
|
-
"i",
|
|
204
|
-
);
|
|
205
|
-
if (file !== "packages/cli/src/check/algorithmic-checks.mjs" && harnessPattern.test(content)) {
|
|
206
|
-
fail(`${file}: per-checker ad hoc harness is not allowed`);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const cliDependencies = Object.keys(cliPackage.dependencies ?? {});
|
|
211
|
-
for (const dependency of cliDependencies) {
|
|
212
|
-
if (
|
|
213
|
-
dependency === "@effect/cli" ||
|
|
214
|
-
dependency === "@effect/platform-node" ||
|
|
215
|
-
dependency === "@effect/printer" ||
|
|
216
|
-
dependency === "@effect/printer-ansi" ||
|
|
217
|
-
dependency === "@effect/typeclass"
|
|
218
|
-
) {
|
|
219
|
-
fail(`packages/cli/package.json: removed Effect CLI dependency returned: ${dependency}`);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
if (cliPackage.dependencies?.effect === "3.21.2") {
|
|
223
|
-
fail("packages/cli/package.json: Effect v3 CLI dependency returned");
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (failures.length > 0) {
|
|
227
|
-
console.error(failures.join("\n"));
|
|
228
|
-
process.exit(1);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
console.log("guard coverage passed");
|