@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
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { workspaceCatalog } from "../../lib/workspace-manifest.mjs";
|
|
2
|
+
|
|
1
3
|
export const createDistributionChecks = ({
|
|
2
4
|
fs,
|
|
3
5
|
path,
|
|
@@ -29,6 +31,8 @@ export const createDistributionChecks = ({
|
|
|
29
31
|
/\b(?:node-gyp|prebuild(?:ify|-install)?|cmake-js|node-pre-gyp)\b/u;
|
|
30
32
|
const distributionNativeFilePattern = /(?:^|\/)(?:binding\.gyp|CMakeLists\.txt)$|\.node$/u;
|
|
31
33
|
const distributionSourcePattern = /\.(?:ts|tsx|mts|cts|js|jsx|mjs|cjs|d\.ts)$/u;
|
|
34
|
+
const sourceEffectPeerSpecifier = () =>
|
|
35
|
+
workspaceCatalog(repoRoot).effect === undefined ? undefined : "catalog:";
|
|
32
36
|
|
|
33
37
|
const distributionFinding = ({
|
|
34
38
|
kind,
|
|
@@ -620,7 +624,7 @@ export const createDistributionChecks = ({
|
|
|
620
624
|
distributionUnitFinding({
|
|
621
625
|
kind: "package-unit-effect-peer-invariant",
|
|
622
626
|
unit,
|
|
623
|
-
message: `effect peer range must
|
|
627
|
+
message: `effect peer range must use catalog single source ${expectedEffectRange}`,
|
|
624
628
|
specifier: "effect",
|
|
625
629
|
target: peer.range,
|
|
626
630
|
}),
|
|
@@ -667,7 +671,7 @@ export const createDistributionChecks = ({
|
|
|
667
671
|
? distributionRoots.targetProfiles.map((profile) => profile.id)
|
|
668
672
|
: [],
|
|
669
673
|
);
|
|
670
|
-
const expectedEffectRange =
|
|
674
|
+
const expectedEffectRange = sourceEffectPeerSpecifier();
|
|
671
675
|
return [
|
|
672
676
|
...packageUnitsRegistryFindings({
|
|
673
677
|
registry: packageUnits,
|
|
@@ -856,10 +860,7 @@ export const createDistributionChecks = ({
|
|
|
856
860
|
edges: graph.edges,
|
|
857
861
|
});
|
|
858
862
|
}),
|
|
859
|
-
...distributionEffectPeerFindings(
|
|
860
|
-
recordsWithManifests,
|
|
861
|
-
readJson("package.json").catalog?.effect,
|
|
862
|
-
),
|
|
863
|
+
...distributionEffectPeerFindings(recordsWithManifests, sourceEffectPeerSpecifier()),
|
|
863
864
|
].sort(
|
|
864
865
|
(left, right) =>
|
|
865
866
|
compare(left.severity, right.severity) ||
|
|
@@ -890,7 +891,7 @@ export const createDistributionChecks = ({
|
|
|
890
891
|
});
|
|
891
892
|
const effectPeerFindings = distributionEffectPeerFindings(
|
|
892
893
|
recordsWithManifests,
|
|
893
|
-
|
|
894
|
+
sourceEffectPeerSpecifier(),
|
|
894
895
|
);
|
|
895
896
|
const failures = [
|
|
896
897
|
...distributionArchitectureFailures(),
|
|
@@ -825,7 +825,8 @@ export const createPackageBoundaryChecks = ({
|
|
|
825
825
|
);
|
|
826
826
|
fs.rmSync(outDir, { recursive: true, force: true });
|
|
827
827
|
const args = [
|
|
828
|
-
"
|
|
828
|
+
"exec",
|
|
829
|
+
"esbuild",
|
|
829
830
|
entryPath,
|
|
830
831
|
"--outdir",
|
|
831
832
|
outDir,
|
|
@@ -835,7 +836,7 @@ export const createPackageBoundaryChecks = ({
|
|
|
835
836
|
if (profile.ambient === "cloudflare-worker") args.push("--external", "cloudflare:*");
|
|
836
837
|
if (profile.ambient !== "node") args.push("--external", "node:*");
|
|
837
838
|
try {
|
|
838
|
-
execFileSync("
|
|
839
|
+
execFileSync("pnpm", args, {
|
|
839
840
|
cwd: repoRoot,
|
|
840
841
|
encoding: "utf8",
|
|
841
842
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -59,6 +59,27 @@ export const createRepoSurfaceChecks = ({
|
|
|
59
59
|
|
|
60
60
|
const checkPublicApi = () => {
|
|
61
61
|
const failures = [];
|
|
62
|
+
const runtimeRoot = "packages/runtime/src/index.ts";
|
|
63
|
+
const runtimeRootSource = read(runtimeRoot);
|
|
64
|
+
const runtimeRootAst = ts.createSourceFile(
|
|
65
|
+
path.join(repoRoot, runtimeRoot),
|
|
66
|
+
runtimeRootSource,
|
|
67
|
+
ts.ScriptTarget.Latest,
|
|
68
|
+
true,
|
|
69
|
+
);
|
|
70
|
+
for (const statement of runtimeRootAst.statements) {
|
|
71
|
+
if (
|
|
72
|
+
ts.isExportDeclaration(statement) &&
|
|
73
|
+
(statement.exportClause === undefined || ts.isNamespaceExport(statement.exportClause))
|
|
74
|
+
) {
|
|
75
|
+
const { line, character } = runtimeRootAst.getLineAndCharacterOfPosition(
|
|
76
|
+
statement.getStart(runtimeRootAst),
|
|
77
|
+
);
|
|
78
|
+
failures.push(
|
|
79
|
+
`${runtimeRoot}:${line + 1}:${character + 1}: runtime root barrel must use explicit named exports; export-star syntax is forbidden`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
62
83
|
for (const target of targetPackages()) {
|
|
63
84
|
if (target.apiSource === undefined) {
|
|
64
85
|
failures.push(
|
|
@@ -78,7 +99,7 @@ export const createRepoSurfaceChecks = ({
|
|
|
78
99
|
failures.push(...validateSourceTsdocRecords(target, records));
|
|
79
100
|
const expected = `${sourceTsdocApiMarkdown(target, records).replace(/\s+$/u, "")}\n`;
|
|
80
101
|
if (fs.readFileSync(manifest, "utf8") !== expected) {
|
|
81
|
-
failures.push(`${target.apiSource} is stale; run
|
|
102
|
+
failures.push(`${target.apiSource} is stale; run pnpm run docs:generate`);
|
|
82
103
|
}
|
|
83
104
|
} else if (mode !== "manual") {
|
|
84
105
|
failures.push(`${target.name}: unsupported apiSourceMode ${mode}`);
|
|
@@ -254,6 +275,38 @@ export const createRepoSurfaceChecks = ({
|
|
|
254
275
|
failIfAny("repo tooling surface", failures);
|
|
255
276
|
};
|
|
256
277
|
|
|
278
|
+
const checkEvalResultArtifactBoundary = () => {
|
|
279
|
+
const failures = [];
|
|
280
|
+
if (!read(".gitignore").split(/\r?\n/u).includes(".agentos/eval-results/")) {
|
|
281
|
+
failures.push(".gitignore: missing .agentos/eval-results/ ignore entry");
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const writerPath = "packages/cli/src/build/build-cli.ts";
|
|
285
|
+
const writer = read(writerPath);
|
|
286
|
+
if (!writer.includes('path.join(cwd, ".agentos", "eval-results")')) {
|
|
287
|
+
failures.push(`${writerPath}: eval runner must own the eval-results output path`);
|
|
288
|
+
}
|
|
289
|
+
if (!writer.includes("writeEvalReportArtifact")) {
|
|
290
|
+
failures.push(`${writerPath}: eval runner must write a derived eval report artifact`);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const forbiddenReaders = [
|
|
294
|
+
"packages/cli/src/build/agent-authoring",
|
|
295
|
+
"packages/cli/src/generate",
|
|
296
|
+
"packages/runtime/src",
|
|
297
|
+
"packages/core/src",
|
|
298
|
+
"packages/evals/src",
|
|
299
|
+
"agent-catalog/agentOS",
|
|
300
|
+
];
|
|
301
|
+
for (const file of forbiddenReaders.flatMap((root) => walk(root))) {
|
|
302
|
+
if (!/\.(?:mjs|js|ts|tsx|md|json)$/u.test(file)) continue;
|
|
303
|
+
if (read(file).includes("eval-results")) {
|
|
304
|
+
failures.push(`${file}: eval-results must remain write-only derived output`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
failIfAny("eval result artifact boundary", failures);
|
|
308
|
+
};
|
|
309
|
+
|
|
257
310
|
return {
|
|
258
311
|
manifestEntries,
|
|
259
312
|
manifestNames,
|
|
@@ -262,6 +315,7 @@ export const createRepoSurfaceChecks = ({
|
|
|
262
315
|
ruleConstraints,
|
|
263
316
|
checkPublicApi,
|
|
264
317
|
checkEventNamespaces,
|
|
318
|
+
checkEvalResultArtifactBoundary,
|
|
265
319
|
checkRepoToolingSurface,
|
|
266
320
|
};
|
|
267
321
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const createStaticTargetChecks = ({ read, failIfAny }) => {
|
|
1
|
+
export const createStaticTargetChecks = ({ read, readJson, failIfAny }) => {
|
|
2
2
|
const sliceBetweenMarkers = (source, startMarker, endMarker) => {
|
|
3
3
|
const start = source.indexOf(startMarker);
|
|
4
4
|
if (start === -1) return "";
|
|
@@ -12,6 +12,7 @@ export const createStaticTargetChecks = ({ read, failIfAny }) => {
|
|
|
12
12
|
const workspaceAgentSourcePath = "packages/core/src/workspace-agent.ts";
|
|
13
13
|
const source = read(sourcePath);
|
|
14
14
|
const workspaceAgentSource = read(workspaceAgentSourcePath);
|
|
15
|
+
const surface = readJson("docs/surface.json");
|
|
15
16
|
const renderWorkspaceStaticTargetSource = sliceBetweenMarkers(
|
|
16
17
|
source,
|
|
17
18
|
"const renderWorkspaceStaticTarget =",
|
|
@@ -260,15 +261,17 @@ export const createStaticTargetChecks = ({ read, failIfAny }) => {
|
|
|
260
261
|
'import semanticDeclarations from "./manifest.json";',
|
|
261
262
|
'import deploymentProvenance from "./deployment.json";',
|
|
262
263
|
"createAgentDurableObject",
|
|
263
|
-
"
|
|
264
|
+
"resolveRuntimeInstallGraph",
|
|
265
|
+
"workspaceOperations",
|
|
264
266
|
"OpenAiCompatibleLlmTransportLive",
|
|
267
|
+
"preflightOpenAiCompatibleProviderMaterial",
|
|
265
268
|
"defineWorkspaceAgentMount",
|
|
266
|
-
"bindWorkspaceToolsForRuntime",
|
|
267
269
|
"makeCloudflareWorkspaceEnv",
|
|
268
270
|
"getSandbox",
|
|
269
271
|
"generatedCustomTools",
|
|
270
272
|
"llmTransport: () => OpenAiCompatibleLlmTransportLive",
|
|
271
|
-
"
|
|
273
|
+
"generatedCapabilityInstallGraphFor",
|
|
274
|
+
"extensions: (env) => generatedCapabilityInstallGraphFor(env).extensions",
|
|
272
275
|
"override submit(spec: AgentSubmitSpec): Promise<SubmitResult>",
|
|
273
276
|
"submitRunInput(input: SubmitRunInput): Promise<SubmitResult>",
|
|
274
277
|
"readWorkspaceFile(",
|
|
@@ -281,16 +284,57 @@ export const createStaticTargetChecks = ({ read, failIfAny }) => {
|
|
|
281
284
|
}
|
|
282
285
|
}
|
|
283
286
|
|
|
287
|
+
const requiredSkillSupportMarkers = [
|
|
288
|
+
"GENERATED_LOAD_SKILL_TOOL_NAME",
|
|
289
|
+
"const renderSkillSupport =",
|
|
290
|
+
"const generatedLoadSkillTool = defineProductTool",
|
|
291
|
+
"const generatedSkillsSystemAdvert =",
|
|
292
|
+
"const generatedFrameworkToolsFor =",
|
|
293
|
+
"const renderSubmitSpecFromRunInput =",
|
|
294
|
+
"generatedSystemPrompt(input.system, dynamicCapabilityProjection)",
|
|
295
|
+
];
|
|
296
|
+
for (const marker of requiredSkillSupportMarkers) {
|
|
297
|
+
if (!source.includes(marker)) {
|
|
298
|
+
failures.push(
|
|
299
|
+
`${sourcePath}: generated-static-target-linking: generated skills support missing ${marker}`,
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
const assertProfileSkillProjection = ({ targetSource, profile }) => {
|
|
304
|
+
for (const marker of [
|
|
305
|
+
"const hasSkills = normalized.skills.length > 0;",
|
|
306
|
+
'...(hasSkills ? ["defineProductTool"] : [])',
|
|
307
|
+
'renderNamedImport(hasSkills ? ["Effect", "Schema"] : ["Effect"], modules.effect)',
|
|
308
|
+
"${renderSkillSupport(normalized.skills)}",
|
|
309
|
+
"${renderSubmitSpecFromRunInput(hasSkills)}",
|
|
310
|
+
"...generatedFrameworkTools",
|
|
311
|
+
]) {
|
|
312
|
+
if (!targetSource.includes(marker)) {
|
|
313
|
+
failures.push(
|
|
314
|
+
`${sourcePath}: generated-static-target-linking: ${profile} missing generated skills projection marker ${marker}`,
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
assertProfileSkillProjection({
|
|
320
|
+
targetSource: renderWorkspaceStaticTargetSource,
|
|
321
|
+
profile: "workspace@1",
|
|
322
|
+
});
|
|
323
|
+
assertProfileSkillProjection({
|
|
324
|
+
targetSource: renderChatStaticTargetSource,
|
|
325
|
+
profile: "chat@1",
|
|
326
|
+
});
|
|
327
|
+
|
|
284
328
|
const requiredModuleKinds = [
|
|
285
329
|
'"semantic-json"',
|
|
286
330
|
'"target-runtime"',
|
|
287
331
|
'"target-scope-helper"',
|
|
288
332
|
'"target-worker"',
|
|
289
333
|
'"target-config"',
|
|
334
|
+
'"capability-runtime"',
|
|
290
335
|
'"provider-runtime"',
|
|
291
336
|
'"workspace-host"',
|
|
292
337
|
'"authored-tool"',
|
|
293
|
-
'"workspace-binding"',
|
|
294
338
|
'"execution-domain-runtime"',
|
|
295
339
|
'"platform-runtime"',
|
|
296
340
|
'"client-core"',
|
|
@@ -305,6 +349,40 @@ export const createStaticTargetChecks = ({ read, failIfAny }) => {
|
|
|
305
349
|
}
|
|
306
350
|
}
|
|
307
351
|
|
|
352
|
+
const packagesBySlug = new Map((surface.packages ?? []).map((pkg) => [pkg.slug, pkg]));
|
|
353
|
+
const generatedImportAudience = new Set(["default-direct", "generated-only"]);
|
|
354
|
+
const generatedPackageSpecifiers = [
|
|
355
|
+
...source.matchAll(/publicPackageSpecifier\(scope,\s*"([^"]+)"\)/gu),
|
|
356
|
+
].map((match) => match[1]);
|
|
357
|
+
if (generatedPackageSpecifiers.length === 0) {
|
|
358
|
+
failures.push(
|
|
359
|
+
`${sourcePath}: generated-static-target-linking: generated package specifier map is empty`,
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
for (const specifier of generatedPackageSpecifiers) {
|
|
363
|
+
const [slug, ...subpathParts] = specifier.split("/");
|
|
364
|
+
const pkg = packagesBySlug.get(slug);
|
|
365
|
+
const subpath = subpathParts.length === 0 ? "." : `./${subpathParts.join("/")}`;
|
|
366
|
+
if (pkg === undefined) {
|
|
367
|
+
failures.push(
|
|
368
|
+
`${sourcePath}: generated-static-target-linking: ${specifier} has no docs/surface.json package`,
|
|
369
|
+
);
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
const entrypoint = (pkg.entrypoints ?? []).find((entry) => entry.subpath === subpath);
|
|
373
|
+
if (entrypoint === undefined) {
|
|
374
|
+
failures.push(
|
|
375
|
+
`${sourcePath}: generated-static-target-linking: ${pkg.name}${subpath === "." ? "" : subpath.slice(1)} is missing docs/surface.json entrypoint metadata`,
|
|
376
|
+
);
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
if (!entrypoint.audiences.some((audience) => generatedImportAudience.has(audience))) {
|
|
380
|
+
failures.push(
|
|
381
|
+
`${sourcePath}: generated-static-target-linking: generated target must not import ${pkg.name}${subpath === "." ? "" : subpath.slice(1)} with audiences ${entrypoint.audiences.join(", ")}`,
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
308
386
|
const durableObjectConfigSections = [
|
|
309
387
|
renderWorkspaceStaticTargetSource,
|
|
310
388
|
renderChatStaticTargetSource,
|
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
tsconfigReferenceEdges,
|
|
17
17
|
workspacePackageRecords as graphWorkspacePackageRecords,
|
|
18
18
|
} from "./package-graph.mjs";
|
|
19
|
+
import { workspacePackagePatterns } from "../lib/workspace-manifest.mjs";
|
|
20
|
+
import { walkRepoSourceFiles } from "../lib/repo-source-files.mjs";
|
|
19
21
|
import { collectAgentDocsModel } from "../lib/agent-docs-model.mjs";
|
|
20
22
|
import { createOwnerChecks } from "./algorithmic/owner-checks.mjs";
|
|
21
23
|
import { createArchitectureChecks } from "./algorithmic/architecture-checks.mjs";
|
|
@@ -47,21 +49,7 @@ const read = (relativePath) => fs.readFileSync(path.join(repoRoot, relativePath)
|
|
|
47
49
|
const readJson = (relativePath) => JSON.parse(read(relativePath));
|
|
48
50
|
const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
49
51
|
|
|
50
|
-
const walk = (relativePath, options = {}) =>
|
|
51
|
-
const absolutePath = path.join(repoRoot, relativePath);
|
|
52
|
-
if (!fs.existsSync(absolutePath)) return [];
|
|
53
|
-
const stat = fs.statSync(absolutePath);
|
|
54
|
-
if (stat.isFile()) return [relativePath];
|
|
55
|
-
const ignored = options.ignored ?? new Set(["node_modules", "dist", ".wrangler", ".turbo"]);
|
|
56
|
-
const files = [];
|
|
57
|
-
for (const entry of fs.readdirSync(absolutePath, { withFileTypes: true })) {
|
|
58
|
-
if (entry.isDirectory() && ignored.has(entry.name)) continue;
|
|
59
|
-
const child = path.join(relativePath, entry.name);
|
|
60
|
-
if (entry.isDirectory()) files.push(...walk(child, options));
|
|
61
|
-
if (entry.isFile()) files.push(child.split(path.sep).join("/"));
|
|
62
|
-
}
|
|
63
|
-
return files.sort(compare);
|
|
64
|
-
};
|
|
52
|
+
const walk = (relativePath, options = {}) => walkRepoSourceFiles(repoRoot, relativePath, options);
|
|
65
53
|
|
|
66
54
|
const failIfAny = (label, failures) => {
|
|
67
55
|
if (failures.length === 0) {
|
|
@@ -94,6 +82,7 @@ const manifestNames = repoSurfaceChecks.manifestNames;
|
|
|
94
82
|
const ruleConstraints = repoSurfaceChecks.ruleConstraints;
|
|
95
83
|
const checkPublicApi = repoSurfaceChecks.checkPublicApi;
|
|
96
84
|
const checkEventNamespaces = repoSurfaceChecks.checkEventNamespaces;
|
|
85
|
+
const checkEvalResultArtifactBoundary = repoSurfaceChecks.checkEvalResultArtifactBoundary;
|
|
97
86
|
const checkRepoToolingSurface = repoSurfaceChecks.checkRepoToolingSurface;
|
|
98
87
|
|
|
99
88
|
const clientBoundaryChecks = createClientBoundaryChecks({
|
|
@@ -340,7 +329,7 @@ const checkModuleGraphOracle = packageBoundaryChecks.checkModuleGraphOracle;
|
|
|
340
329
|
const checkSubpathNoLeak = packageBoundaryChecks.checkSubpathNoLeak;
|
|
341
330
|
const checkProfileVerification = packageBoundaryChecks.checkProfileVerification;
|
|
342
331
|
|
|
343
|
-
const staticTargetChecks = createStaticTargetChecks({ read, failIfAny });
|
|
332
|
+
const staticTargetChecks = createStaticTargetChecks({ read, readJson, failIfAny });
|
|
344
333
|
const checkGeneratedStaticTargetLinking = staticTargetChecks.checkGeneratedStaticTargetLinking;
|
|
345
334
|
|
|
346
335
|
const projectionBoundaryChecks = createProjectionBoundaryChecks({
|
|
@@ -379,6 +368,7 @@ const convergenceSmokeChecks = createConvergenceSmokeChecks({
|
|
|
379
368
|
checkGeneratedStaticTargetLinking,
|
|
380
369
|
checkSpikeHygiene,
|
|
381
370
|
moduleBucketRegistry,
|
|
371
|
+
workspacePackagePatterns: () => workspacePackagePatterns(repoRoot),
|
|
382
372
|
workspacePackageRecords,
|
|
383
373
|
consumerFacingSpecifierFailures,
|
|
384
374
|
packageUnitPublicSpecifiers,
|
|
@@ -400,12 +390,14 @@ const checkDocsSiteBuild = convergenceSmokeChecks.checkDocsSiteBuild;
|
|
|
400
390
|
const checkCliSurface = convergenceSmokeChecks.checkCliSurface;
|
|
401
391
|
const checkConsumerImports = convergenceSmokeChecks.checkConsumerImports;
|
|
402
392
|
const checkDogfoodSmoke = convergenceSmokeChecks.checkDogfoodSmoke;
|
|
393
|
+
const checkBlueprintRecipes = convergenceSmokeChecks.checkBlueprintRecipes;
|
|
403
394
|
|
|
404
|
-
const checkBoundaryProjection = () => runCommand("vp check", { cwd: repoRoot });
|
|
395
|
+
const checkBoundaryProjection = () => runCommand("vp fmt --check", { cwd: repoRoot });
|
|
405
396
|
|
|
406
397
|
const checkerById = new Map([
|
|
407
398
|
["architecture-sources", checkArchitectureSources],
|
|
408
399
|
["backend-neutrality", checkBackendNeutrality],
|
|
400
|
+
["blueprint-recipes", checkBlueprintRecipes],
|
|
409
401
|
["boundaries", checkBoundaryProjection],
|
|
410
402
|
["cli-surface", checkCliSurface],
|
|
411
403
|
["client-boundaries", checkClientBoundaries],
|
|
@@ -419,6 +411,7 @@ const checkerById = new Map([
|
|
|
419
411
|
["docs-link-integrity", checkDocsLinkIntegrity],
|
|
420
412
|
["docs-site-build", checkDocsSiteBuild],
|
|
421
413
|
["event-namespaces", checkEventNamespaces],
|
|
414
|
+
["eval-result-artifact-boundary", checkEvalResultArtifactBoundary],
|
|
422
415
|
["limit-registry", checkLimitRegistry],
|
|
423
416
|
["generated-static-target-linking", checkGeneratedStaticTargetLinking],
|
|
424
417
|
["gate-tier-governance", checkGateTierGovernance],
|
|
@@ -32,9 +32,9 @@ const runStage = (label, command, args) =>
|
|
|
32
32
|
export const runDefaultGate = async () => {
|
|
33
33
|
const startedAt = Date.now();
|
|
34
34
|
const stages = await Promise.all([
|
|
35
|
-
runStage("structural", "
|
|
36
|
-
runStage("typecheck", "
|
|
37
|
-
runStage("test", "
|
|
35
|
+
runStage("structural", "pnpm", ["run", "check:structural"]),
|
|
36
|
+
runStage("typecheck", "pnpm", ["run", "typecheck"]),
|
|
37
|
+
runStage("test", "pnpm", ["run", "test"]),
|
|
38
38
|
]);
|
|
39
39
|
for (const stage of stages) {
|
|
40
40
|
console.log(`${stage.label} duration: ${stage.durationMs}ms`);
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
7
|
+
|
|
8
|
+
const parseArgs = (rawArgs, defaultRepoRoot) => {
|
|
9
|
+
const args = {
|
|
10
|
+
repo: defaultRepoRoot,
|
|
11
|
+
evidence: path.join(os.tmpdir(), "agentos-effect-scan"),
|
|
12
|
+
scanner: "effect-skill-scan",
|
|
13
|
+
};
|
|
14
|
+
for (let index = 0; index < rawArgs.length; index += 1) {
|
|
15
|
+
const arg = rawArgs[index];
|
|
16
|
+
switch (arg) {
|
|
17
|
+
case "--repo":
|
|
18
|
+
if (rawArgs[index + 1] === undefined) throw new Error("--repo requires a path");
|
|
19
|
+
args.repo = rawArgs[index + 1];
|
|
20
|
+
index += 1;
|
|
21
|
+
break;
|
|
22
|
+
case "--evidence":
|
|
23
|
+
if (rawArgs[index + 1] === undefined) throw new Error("--evidence requires a path");
|
|
24
|
+
args.evidence = rawArgs[index + 1];
|
|
25
|
+
index += 1;
|
|
26
|
+
break;
|
|
27
|
+
case "--scanner":
|
|
28
|
+
if (rawArgs[index + 1] === undefined) throw new Error("--scanner requires a command");
|
|
29
|
+
args.scanner = rawArgs[index + 1];
|
|
30
|
+
index += 1;
|
|
31
|
+
break;
|
|
32
|
+
default:
|
|
33
|
+
throw new Error(`unexpected argument ${arg}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
repo: path.resolve(args.repo),
|
|
38
|
+
evidence: path.resolve(args.evidence),
|
|
39
|
+
scanner: args.scanner,
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const validateEffectScanGateJson = (value) => {
|
|
44
|
+
const failures = [];
|
|
45
|
+
if (!isRecord(value)) return ["effect scan gate-json must be an object"];
|
|
46
|
+
if (value.schemaVersion !== 1) failures.push("effect scan gate-json schemaVersion must be 1");
|
|
47
|
+
if (typeof value.ok !== "boolean") failures.push("effect scan gate-json ok must be boolean");
|
|
48
|
+
if (!isRecord(value.tiers)) {
|
|
49
|
+
failures.push("effect scan gate-json tiers object is required");
|
|
50
|
+
return failures;
|
|
51
|
+
}
|
|
52
|
+
if (!Array.isArray(value.tiers.block)) {
|
|
53
|
+
failures.push("effect scan gate-json tiers.block array is required");
|
|
54
|
+
}
|
|
55
|
+
if (!Array.isArray(value.tiers.report)) {
|
|
56
|
+
failures.push("effect scan gate-json tiers.report array is required");
|
|
57
|
+
}
|
|
58
|
+
if (!isRecord(value.tiers.review)) {
|
|
59
|
+
failures.push("effect scan gate-json tiers.review object is required");
|
|
60
|
+
}
|
|
61
|
+
return failures;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const effectScanBlockCount = (value) =>
|
|
65
|
+
isRecord(value) && isRecord(value.tiers) && Array.isArray(value.tiers.block)
|
|
66
|
+
? value.tiers.block.length
|
|
67
|
+
: 0;
|
|
68
|
+
|
|
69
|
+
export const effectScanGateFailures = (value, processExitCode) => {
|
|
70
|
+
const validationFailures = validateEffectScanGateJson(value);
|
|
71
|
+
if (validationFailures.length > 0) return validationFailures;
|
|
72
|
+
const failures = [];
|
|
73
|
+
const blockCount = effectScanBlockCount(value);
|
|
74
|
+
if (value.ok !== true) {
|
|
75
|
+
failures.push("effect scan gate-json ok is false");
|
|
76
|
+
}
|
|
77
|
+
if (blockCount > 0) {
|
|
78
|
+
failures.push(`effect scan gate-json contains ${blockCount} scanner-owned block finding(s)`);
|
|
79
|
+
}
|
|
80
|
+
if (processExitCode !== 0 && value.ok === true && blockCount === 0) {
|
|
81
|
+
failures.push(
|
|
82
|
+
`effect scan process exited ${processExitCode} while gate-json reported no block findings`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
return failures;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const parseGateJson = (stdout) => {
|
|
89
|
+
try {
|
|
90
|
+
return JSON.parse(stdout);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`effect scan did not emit parseable gate-json: ${error instanceof Error ? error.message : String(error)}`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const runEffectScanGate = (rawArgs, options) => {
|
|
99
|
+
const args = parseArgs(rawArgs, options.defaultRepoRoot);
|
|
100
|
+
fs.rmSync(args.evidence, { recursive: true, force: true });
|
|
101
|
+
fs.mkdirSync(args.evidence, { recursive: true });
|
|
102
|
+
const result = spawnSync(
|
|
103
|
+
args.scanner,
|
|
104
|
+
[args.repo, "--strict", "--output", "gate-json", "--evidence", args.evidence],
|
|
105
|
+
{
|
|
106
|
+
cwd: args.repo,
|
|
107
|
+
encoding: "utf8",
|
|
108
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
if (result.error !== undefined) {
|
|
112
|
+
throw new Error(`effect scan failed to start: ${result.error.message}`);
|
|
113
|
+
}
|
|
114
|
+
const projection = parseGateJson(result.stdout);
|
|
115
|
+
process.stdout.write(`${JSON.stringify(projection, null, 2)}\n`);
|
|
116
|
+
if (result.stderr.length > 0) process.stderr.write(result.stderr);
|
|
117
|
+
const failures = effectScanGateFailures(projection, result.status ?? 1);
|
|
118
|
+
if (failures.length > 0) {
|
|
119
|
+
throw new Error(`agentos check effect-scan: ${failures.join("; ")}`);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import ts from "typescript";
|
|
4
|
+
import { workspacePackageRecords as workspaceManifestPackageRecords } from "../lib/workspace-manifest.mjs";
|
|
4
5
|
|
|
5
6
|
const compare = (left, right) => left.localeCompare(right);
|
|
6
7
|
const readJsonFile = (file) => JSON.parse(fs.readFileSync(file, "utf8"));
|
|
@@ -23,38 +24,7 @@ export const walkFiles = (repoRoot, relativePath, options = {}) => {
|
|
|
23
24
|
return files.sort(compare);
|
|
24
25
|
};
|
|
25
26
|
|
|
26
|
-
export const workspacePackageRecords = (repoRoot) =>
|
|
27
|
-
const rootPackage = readJsonFile(path.join(repoRoot, "package.json"));
|
|
28
|
-
const workspaces = Array.isArray(rootPackage.workspaces)
|
|
29
|
-
? rootPackage.workspaces
|
|
30
|
-
: Array.isArray(rootPackage.workspaces?.packages)
|
|
31
|
-
? rootPackage.workspaces.packages
|
|
32
|
-
: [];
|
|
33
|
-
const records = [];
|
|
34
|
-
|
|
35
|
-
for (const workspace of workspaces) {
|
|
36
|
-
if (typeof workspace !== "string") continue;
|
|
37
|
-
if (workspace.endsWith("/*")) {
|
|
38
|
-
const base = workspace.slice(0, -2);
|
|
39
|
-
const baseDir = path.join(repoRoot, base);
|
|
40
|
-
if (!fs.existsSync(baseDir)) continue;
|
|
41
|
-
for (const entry of fs.readdirSync(baseDir, { withFileTypes: true })) {
|
|
42
|
-
if (!entry.isDirectory()) continue;
|
|
43
|
-
const packagePath = `${base}/${entry.name}`;
|
|
44
|
-
const packageJsonPath = path.join(repoRoot, packagePath, "package.json");
|
|
45
|
-
if (!fs.existsSync(packageJsonPath)) continue;
|
|
46
|
-
records.push({ name: readJsonFile(packageJsonPath).name, path: packagePath });
|
|
47
|
-
}
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const packageJsonPath = path.join(repoRoot, workspace, "package.json");
|
|
52
|
-
if (!fs.existsSync(packageJsonPath)) continue;
|
|
53
|
-
records.push({ name: readJsonFile(packageJsonPath).name, path: workspace });
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return records.sort((left, right) => left.path.localeCompare(right.path));
|
|
57
|
-
};
|
|
27
|
+
export const workspacePackageRecords = (repoRoot) => workspaceManifestPackageRecords(repoRoot);
|
|
58
28
|
|
|
59
29
|
const scriptKindForFile = (fileName) => {
|
|
60
30
|
if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
|