@yansirplus/cli 0.5.18 → 0.5.19
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/agent-catalog/agentOS/SKILL.md +2 -2
- package/agent-catalog/agentOS/references/package-map.md +57 -56
- package/agent-catalog/agentOS/references/provenance.json +27 -27
- package/agent-catalog/agentOS/references/public-api/client.md +2 -0
- package/agent-catalog/agentOS/references/public-api/core.md +90 -0
- package/agent-catalog/agentOS/references/public-api/runtime.md +49 -0
- package/dist/build/agent-authoring/static-target.js +120 -22
- package/dist/build/build-cli.js +15 -0
- package/dist/consumer-overlay.mjs +545 -66
- package/dist/main.mjs +21 -1
- package/dist/release-status.mjs +515 -0
- package/package.json +4 -4
|
@@ -4,6 +4,8 @@ import fs from "node:fs";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
|
|
7
|
+
import { workspacePackagePaths } from "./lib/workspace-manifest.mjs";
|
|
8
|
+
|
|
7
9
|
export const installManifestProtocol = "agentos-install-manifest@1";
|
|
8
10
|
export const localConsumerMarkerName = ".agentos-local.json";
|
|
9
11
|
|
|
@@ -112,6 +114,57 @@ export const consumerManifestFiles = (consumerRoot) =>
|
|
|
112
114
|
(name) => path.join(consumerRoot, name),
|
|
113
115
|
);
|
|
114
116
|
|
|
117
|
+
const packageNameForRoot = (consumerRoot) => {
|
|
118
|
+
const file = path.join(consumerRoot, "package.json");
|
|
119
|
+
if (!fs.existsSync(file)) return undefined;
|
|
120
|
+
const name = readJson(file).name;
|
|
121
|
+
return typeof name === "string" && name.length > 0 ? name : undefined;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const consumerWorkspaceLayout = (consumerRoot) => {
|
|
125
|
+
const manifest = path.join(consumerRoot, "pnpm-workspace.yaml");
|
|
126
|
+
const root = {
|
|
127
|
+
kind: "root",
|
|
128
|
+
relativePath: ".",
|
|
129
|
+
consumerRoot,
|
|
130
|
+
packageName: packageNameForRoot(consumerRoot),
|
|
131
|
+
};
|
|
132
|
+
if (!fs.existsSync(manifest)) {
|
|
133
|
+
return { status: "not_workspace", roots: [root] };
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
const roots = [
|
|
137
|
+
root,
|
|
138
|
+
...workspacePackagePaths(consumerRoot).map((relativePath) => {
|
|
139
|
+
const packageRoot = path.join(consumerRoot, relativePath);
|
|
140
|
+
return {
|
|
141
|
+
kind: "workspace-package",
|
|
142
|
+
relativePath,
|
|
143
|
+
consumerRoot: packageRoot,
|
|
144
|
+
packageName: packageNameForRoot(packageRoot),
|
|
145
|
+
};
|
|
146
|
+
}),
|
|
147
|
+
];
|
|
148
|
+
return {
|
|
149
|
+
status: "workspace",
|
|
150
|
+
manifestPath: path.relative(consumerRoot, manifest).split(path.sep).join("/"),
|
|
151
|
+
roots,
|
|
152
|
+
};
|
|
153
|
+
} catch (error) {
|
|
154
|
+
return {
|
|
155
|
+
status: "invalid",
|
|
156
|
+
manifestPath: path.relative(consumerRoot, manifest).split(path.sep).join("/"),
|
|
157
|
+
error: error instanceof Error ? error.message : String(error),
|
|
158
|
+
roots: [root],
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const consumerWorkspaceManifestFiles = (layout) =>
|
|
164
|
+
[...new Set(layout.roots.flatMap((root) => consumerManifestFiles(root.consumerRoot)))].sort(
|
|
165
|
+
(left, right) => left.localeCompare(right),
|
|
166
|
+
);
|
|
167
|
+
|
|
115
168
|
export const snapshotFiles = (files) =>
|
|
116
169
|
new Map(files.map((file) => [file, fs.existsSync(file) ? fs.readFileSync(file) : undefined]));
|
|
117
170
|
|
|
@@ -289,6 +342,9 @@ const fileSpecPath = (spec) => {
|
|
|
289
342
|
return spec.slice("file:".length);
|
|
290
343
|
};
|
|
291
344
|
|
|
345
|
+
const optionalFileSpecPath = (spec) =>
|
|
346
|
+
typeof spec === "string" && spec.startsWith("file:") ? spec.slice("file:".length) : undefined;
|
|
347
|
+
|
|
292
348
|
export const tarballPackageEntries = (manifest) =>
|
|
293
349
|
Object.entries(manifest.tarballs)
|
|
294
350
|
.map(([packageName, entry]) => {
|
|
@@ -321,6 +377,326 @@ const markerArtifact = (manifestPath, manifest) => ({
|
|
|
321
377
|
},
|
|
322
378
|
});
|
|
323
379
|
|
|
380
|
+
const sourcePackageScope = "@agent-os";
|
|
381
|
+
|
|
382
|
+
const releaseNpmScope = (sourceRoot) => {
|
|
383
|
+
if (typeof sourceRoot !== "string") return undefined;
|
|
384
|
+
const manifestPath = path.join(sourceRoot, "package.json");
|
|
385
|
+
if (!fs.existsSync(manifestPath)) return undefined;
|
|
386
|
+
const manifest = readJson(manifestPath);
|
|
387
|
+
return typeof manifest.agentOsRelease?.npmScope === "string"
|
|
388
|
+
? manifest.agentOsRelease.npmScope
|
|
389
|
+
: undefined;
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
const publicPackageName = (sourceName, npmScope) => {
|
|
393
|
+
if (typeof npmScope !== "string" || !sourceName.startsWith(`${sourcePackageScope}/`)) {
|
|
394
|
+
return sourceName;
|
|
395
|
+
}
|
|
396
|
+
return `${npmScope}/${sourceName.slice(sourcePackageScope.length + 1)}`;
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
const sourceManifestPathForPackage = (packageName, sourceRoot) => {
|
|
400
|
+
if (typeof sourceRoot !== "string") return undefined;
|
|
401
|
+
const surfacePath = path.join(sourceRoot, "docs", "surface.json");
|
|
402
|
+
if (!fs.existsSync(surfacePath)) return undefined;
|
|
403
|
+
const npmScope = releaseNpmScope(sourceRoot);
|
|
404
|
+
const surface = readJson(surfacePath);
|
|
405
|
+
for (const pkg of surface.packages ?? []) {
|
|
406
|
+
if (pkg?.published !== true || typeof pkg.name !== "string" || typeof pkg.path !== "string") {
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
if (packageName !== pkg.name && packageName !== publicPackageName(pkg.name, npmScope)) {
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
const manifestPath = path.join(sourceRoot, pkg.path, "package.json");
|
|
413
|
+
return fs.existsSync(manifestPath) ? manifestPath : undefined;
|
|
414
|
+
}
|
|
415
|
+
return undefined;
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
const collectExportTargetStrings = (value, output = []) => {
|
|
419
|
+
if (typeof value === "string") {
|
|
420
|
+
output.push(value);
|
|
421
|
+
return output;
|
|
422
|
+
}
|
|
423
|
+
if (value === null || typeof value !== "object") return output;
|
|
424
|
+
for (const child of Object.values(value)) collectExportTargetStrings(child, output);
|
|
425
|
+
return output;
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
const exportTargetKind = (target) => {
|
|
429
|
+
if (target.startsWith("./src/") && target.endsWith(".ts")) return "source-ts";
|
|
430
|
+
if (target.startsWith("./src/") && target.endsWith(".mjs")) return "source-mjs";
|
|
431
|
+
if (target.startsWith("./dist/") && target.endsWith(".d.ts")) return "dist-dts";
|
|
432
|
+
if (target.startsWith("./dist/") && target.endsWith(".js")) return "dist-js";
|
|
433
|
+
if (target.startsWith("./dist/") && target.endsWith(".mjs")) return "dist-mjs";
|
|
434
|
+
if (target.startsWith("./") && !target.includes("..") && target.endsWith(".json")) {
|
|
435
|
+
return "json-asset";
|
|
436
|
+
}
|
|
437
|
+
if (target.startsWith("./")) return "relative-other";
|
|
438
|
+
return "specifier-other";
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
const exportEntryKind = (targetKinds) => {
|
|
442
|
+
if (targetKinds.length === 0) return "empty";
|
|
443
|
+
if (targetKinds.every((kind) => kind.startsWith("source-"))) return "source-module";
|
|
444
|
+
if (targetKinds.every((kind) => kind.startsWith("dist-"))) return "dist-module";
|
|
445
|
+
if (targetKinds.every((kind) => kind === "json-asset")) return "json-asset";
|
|
446
|
+
return "mixed";
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
const exportSetForManifest = (manifest) => {
|
|
450
|
+
const exportsValue = manifest.exports ?? manifest.main;
|
|
451
|
+
const entries =
|
|
452
|
+
typeof exportsValue === "string"
|
|
453
|
+
? [[".", exportsValue]]
|
|
454
|
+
: exportsValue !== null && typeof exportsValue === "object"
|
|
455
|
+
? Object.entries(exportsValue)
|
|
456
|
+
: [];
|
|
457
|
+
return Object.fromEntries(
|
|
458
|
+
entries
|
|
459
|
+
.map(([subpath, value]) => {
|
|
460
|
+
const targets = collectExportTargetStrings(value).sort((left, right) =>
|
|
461
|
+
left.localeCompare(right),
|
|
462
|
+
);
|
|
463
|
+
const targetKinds = [...new Set(targets.map(exportTargetKind))].sort((left, right) =>
|
|
464
|
+
left.localeCompare(right),
|
|
465
|
+
);
|
|
466
|
+
return [
|
|
467
|
+
subpath,
|
|
468
|
+
{
|
|
469
|
+
targetKinds,
|
|
470
|
+
entryKind: exportEntryKind(targetKinds),
|
|
471
|
+
},
|
|
472
|
+
];
|
|
473
|
+
})
|
|
474
|
+
.sort(([left], [right]) => left.localeCompare(right)),
|
|
475
|
+
);
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
const readPackageJsonProjection = (manifestPath) => {
|
|
479
|
+
if (typeof manifestPath !== "string") return { status: "unavailable" };
|
|
480
|
+
if (!fs.existsSync(manifestPath)) return { status: "missing", manifestPath };
|
|
481
|
+
try {
|
|
482
|
+
const manifest = readJson(manifestPath);
|
|
483
|
+
return {
|
|
484
|
+
status: "available",
|
|
485
|
+
manifestPath,
|
|
486
|
+
packageName: manifest.name,
|
|
487
|
+
exports: exportSetForManifest(manifest),
|
|
488
|
+
};
|
|
489
|
+
} catch (error) {
|
|
490
|
+
return {
|
|
491
|
+
status: "failed",
|
|
492
|
+
manifestPath,
|
|
493
|
+
error: error instanceof Error ? error.message : String(error),
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
const readTarballPackageJsonProjection = (tarball) => {
|
|
499
|
+
if (typeof tarball !== "string" || tarball.length === 0) {
|
|
500
|
+
return { status: "unavailable" };
|
|
501
|
+
}
|
|
502
|
+
if (!fs.existsSync(tarball)) return { status: "missing", tarball };
|
|
503
|
+
const tmp = fs.mkdtempSync(path.join(fs.realpathSync("/tmp"), "agentos-export-tarball-"));
|
|
504
|
+
try {
|
|
505
|
+
const result = spawnSync("tar", ["-xzf", tarball, "-C", tmp, "package/package.json"], {
|
|
506
|
+
encoding: "utf8",
|
|
507
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
508
|
+
});
|
|
509
|
+
if (result.status !== 0) {
|
|
510
|
+
return {
|
|
511
|
+
status: "failed",
|
|
512
|
+
tarball,
|
|
513
|
+
error: result.stderr.trim() || result.stdout.trim() || `tar exited ${result.status}`,
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
const projection = readPackageJsonProjection(path.join(tmp, "package", "package.json"));
|
|
517
|
+
return { ...projection, manifestPath: "package/package.json", tarball };
|
|
518
|
+
} finally {
|
|
519
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
const sortedExportKeys = (exports) =>
|
|
524
|
+
Object.keys(exports ?? {}).sort((left, right) => left.localeCompare(right));
|
|
525
|
+
|
|
526
|
+
const compareSubpathSets = (leftExports, rightExports) => {
|
|
527
|
+
const left = new Set(sortedExportKeys(leftExports));
|
|
528
|
+
const right = new Set(sortedExportKeys(rightExports));
|
|
529
|
+
return {
|
|
530
|
+
missing: [...left].filter((subpath) => !right.has(subpath)),
|
|
531
|
+
extra: [...right].filter((subpath) => !left.has(subpath)),
|
|
532
|
+
};
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
const expectedPackedKindForSource = (sourceKind) => {
|
|
536
|
+
if (sourceKind === "source-module") return "dist-module";
|
|
537
|
+
return sourceKind;
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
const compareSourcePackedExports = (source, packed) => {
|
|
541
|
+
const subpaths = compareSubpathSets(source.exports, packed.exports);
|
|
542
|
+
const targetKindDrift = [];
|
|
543
|
+
for (const subpath of sortedExportKeys(source.exports)) {
|
|
544
|
+
if (packed.exports?.[subpath] === undefined) continue;
|
|
545
|
+
const expected = expectedPackedKindForSource(source.exports[subpath].entryKind);
|
|
546
|
+
const actual = packed.exports[subpath].entryKind;
|
|
547
|
+
if (actual !== expected) {
|
|
548
|
+
targetKindDrift.push({
|
|
549
|
+
subpath,
|
|
550
|
+
sourceKind: source.exports[subpath].entryKind,
|
|
551
|
+
expectedPackedKind: expected,
|
|
552
|
+
actualPackedKind: actual,
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return {
|
|
557
|
+
status:
|
|
558
|
+
subpaths.missing.length === 0 && subpaths.extra.length === 0 && targetKindDrift.length === 0
|
|
559
|
+
? "pass"
|
|
560
|
+
: "fail",
|
|
561
|
+
subpaths,
|
|
562
|
+
targetKindDrift,
|
|
563
|
+
};
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
const compareEquivalentExports = (left, right) => {
|
|
567
|
+
const subpaths = compareSubpathSets(left.exports, right.exports);
|
|
568
|
+
const targetKindDrift = [];
|
|
569
|
+
for (const subpath of sortedExportKeys(left.exports)) {
|
|
570
|
+
if (right.exports?.[subpath] === undefined) continue;
|
|
571
|
+
const leftKind = left.exports[subpath].entryKind;
|
|
572
|
+
const rightKind = right.exports[subpath].entryKind;
|
|
573
|
+
if (leftKind !== rightKind) {
|
|
574
|
+
targetKindDrift.push({ subpath, leftKind, rightKind });
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
return {
|
|
578
|
+
status:
|
|
579
|
+
subpaths.missing.length === 0 && subpaths.extra.length === 0 && targetKindDrift.length === 0
|
|
580
|
+
? "pass"
|
|
581
|
+
: "fail",
|
|
582
|
+
subpaths,
|
|
583
|
+
targetKindDrift,
|
|
584
|
+
};
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
const exportFailure = (code, packageName, comparison, detail = {}) => ({
|
|
588
|
+
code,
|
|
589
|
+
packageName,
|
|
590
|
+
comparison,
|
|
591
|
+
...detail,
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
const exportEquivalenceForPackage = (entry, options = {}) => {
|
|
595
|
+
const source = readPackageJsonProjection(
|
|
596
|
+
sourceManifestPathForPackage(entry.packageName, options.sourceRoot),
|
|
597
|
+
);
|
|
598
|
+
const packed = readTarballPackageJsonProjection(entry.tarball);
|
|
599
|
+
const installed = readPackageJsonProjection(entry.installedManifestPath);
|
|
600
|
+
const failures = [];
|
|
601
|
+
const comparisons = {};
|
|
602
|
+
if (packed.status !== "available") {
|
|
603
|
+
failures.push(
|
|
604
|
+
exportFailure("export_packed_manifest_unavailable", entry.packageName, "packed", {
|
|
605
|
+
status: packed.status,
|
|
606
|
+
error: packed.error,
|
|
607
|
+
}),
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
if (entry.installedManifestPath !== undefined && installed.status !== "available") {
|
|
611
|
+
failures.push(
|
|
612
|
+
exportFailure("export_installed_manifest_unavailable", entry.packageName, "installed", {
|
|
613
|
+
status: installed.status,
|
|
614
|
+
error: installed.error,
|
|
615
|
+
}),
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
if (source.status === "available" && packed.status === "available") {
|
|
619
|
+
const comparison = compareSourcePackedExports(source, packed);
|
|
620
|
+
comparisons.sourcePacked = comparison;
|
|
621
|
+
if (comparison.subpaths.missing.length > 0 || comparison.subpaths.extra.length > 0) {
|
|
622
|
+
failures.push(
|
|
623
|
+
exportFailure("export_source_packed_subpath_drift", entry.packageName, "source_packed", {
|
|
624
|
+
missingSubpaths: comparison.subpaths.missing,
|
|
625
|
+
extraSubpaths: comparison.subpaths.extra,
|
|
626
|
+
}),
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
if (comparison.targetKindDrift.length > 0) {
|
|
630
|
+
failures.push(
|
|
631
|
+
exportFailure(
|
|
632
|
+
"export_source_packed_target_kind_drift",
|
|
633
|
+
entry.packageName,
|
|
634
|
+
"source_packed",
|
|
635
|
+
{ targetKindDrift: comparison.targetKindDrift },
|
|
636
|
+
),
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
if (packed.status === "available" && installed.status === "available") {
|
|
641
|
+
const comparison = compareEquivalentExports(packed, installed);
|
|
642
|
+
comparisons.packedInstalled = comparison;
|
|
643
|
+
if (comparison.subpaths.missing.length > 0 || comparison.subpaths.extra.length > 0) {
|
|
644
|
+
failures.push(
|
|
645
|
+
exportFailure(
|
|
646
|
+
"export_packed_installed_subpath_drift",
|
|
647
|
+
entry.packageName,
|
|
648
|
+
"packed_installed",
|
|
649
|
+
{
|
|
650
|
+
missingSubpaths: comparison.subpaths.missing,
|
|
651
|
+
extraSubpaths: comparison.subpaths.extra,
|
|
652
|
+
},
|
|
653
|
+
),
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
if (comparison.targetKindDrift.length > 0) {
|
|
657
|
+
failures.push(
|
|
658
|
+
exportFailure(
|
|
659
|
+
"export_packed_installed_target_kind_drift",
|
|
660
|
+
entry.packageName,
|
|
661
|
+
"packed_installed",
|
|
662
|
+
{ targetKindDrift: comparison.targetKindDrift },
|
|
663
|
+
),
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return {
|
|
668
|
+
packageName: entry.packageName,
|
|
669
|
+
source,
|
|
670
|
+
packed,
|
|
671
|
+
...(entry.installedManifestPath === undefined ? {} : { installed }),
|
|
672
|
+
comparisons,
|
|
673
|
+
status: failures.length === 0 ? "verified" : "failed",
|
|
674
|
+
failures,
|
|
675
|
+
};
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
export const exportEquivalenceProjection = (entries, options = {}) => {
|
|
679
|
+
const packages = entries
|
|
680
|
+
.map((entry) => exportEquivalenceForPackage(entry, options))
|
|
681
|
+
.sort((left, right) => left.packageName.localeCompare(right.packageName));
|
|
682
|
+
const failures = packages.flatMap((pkg) => pkg.failures);
|
|
683
|
+
return {
|
|
684
|
+
status: packages.length === 0 ? "not_checked" : failures.length === 0 ? "verified" : "failed",
|
|
685
|
+
packagesChecked: packages.length,
|
|
686
|
+
packages,
|
|
687
|
+
failures,
|
|
688
|
+
};
|
|
689
|
+
};
|
|
690
|
+
|
|
691
|
+
export const exportEquivalenceForInstallManifest = (manifest, options = {}) =>
|
|
692
|
+
exportEquivalenceProjection(
|
|
693
|
+
Object.entries(manifest.tarballs ?? {}).map(([packageName, entry]) => ({
|
|
694
|
+
packageName,
|
|
695
|
+
tarball: optionalFileSpecPath(entry?.spec),
|
|
696
|
+
})),
|
|
697
|
+
options,
|
|
698
|
+
);
|
|
699
|
+
|
|
324
700
|
const packageOverlayRows = (consumerRoot, marker) => {
|
|
325
701
|
const nodeModules = path.join(consumerRoot, "node_modules");
|
|
326
702
|
return Object.entries(marker.packages ?? {})
|
|
@@ -336,6 +712,7 @@ const packageOverlayRows = (consumerRoot, marker) => {
|
|
|
336
712
|
const tarballExists = tarball.length > 0 && fs.existsSync(tarball);
|
|
337
713
|
const expectedSha = typeof record.sha256 === "string" ? record.sha256 : undefined;
|
|
338
714
|
const actualSha = tarballExists ? sha256File(tarball) : undefined;
|
|
715
|
+
const requiresSha = marker.artifact?.kind === "install-manifest-overlay";
|
|
339
716
|
return {
|
|
340
717
|
packageName,
|
|
341
718
|
target: record.target,
|
|
@@ -343,9 +720,13 @@ const packageOverlayRows = (consumerRoot, marker) => {
|
|
|
343
720
|
targetStatus,
|
|
344
721
|
tarball,
|
|
345
722
|
tarballStatus: tarballExists
|
|
346
|
-
? expectedSha === undefined
|
|
347
|
-
?
|
|
348
|
-
|
|
723
|
+
? expectedSha === undefined
|
|
724
|
+
? requiresSha
|
|
725
|
+
? "sha_missing"
|
|
726
|
+
: "verified"
|
|
727
|
+
: expectedSha === actualSha
|
|
728
|
+
? "verified"
|
|
729
|
+
: "sha_mismatch"
|
|
349
730
|
: "missing",
|
|
350
731
|
sha256: expectedSha,
|
|
351
732
|
};
|
|
@@ -457,6 +838,38 @@ const consumerGateIssue = (code, severity, dimension, message, detail = {}) => (
|
|
|
457
838
|
const consumerOverlayGate = (status) => {
|
|
458
839
|
const hardFailures = [];
|
|
459
840
|
const signals = [];
|
|
841
|
+
if (status.workspaceOverlay?.status === "invalid") {
|
|
842
|
+
hardFailures.push(
|
|
843
|
+
consumerGateIssue(
|
|
844
|
+
"workspace_layout_invalid",
|
|
845
|
+
"hard",
|
|
846
|
+
"workspace_layout",
|
|
847
|
+
"consumer workspace manifest could not be projected",
|
|
848
|
+
{
|
|
849
|
+
manifestPath: status.workspaceOverlay.manifestPath,
|
|
850
|
+
error: status.workspaceOverlay.error,
|
|
851
|
+
},
|
|
852
|
+
),
|
|
853
|
+
);
|
|
854
|
+
}
|
|
855
|
+
for (const root of status.workspaceOverlay?.roots ?? []) {
|
|
856
|
+
if (root.relativePath === ".") continue;
|
|
857
|
+
if (root.gate?.status !== "pass") {
|
|
858
|
+
hardFailures.push(
|
|
859
|
+
consumerGateIssue(
|
|
860
|
+
"workspace_consumer_root_failed",
|
|
861
|
+
"hard",
|
|
862
|
+
"workspace_resolver",
|
|
863
|
+
`workspace consumer root ${root.relativePath} does not have a passing local overlay`,
|
|
864
|
+
{
|
|
865
|
+
relativePath: root.relativePath,
|
|
866
|
+
packageName: root.packageName,
|
|
867
|
+
gate: root.gate,
|
|
868
|
+
},
|
|
869
|
+
),
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
460
873
|
if (status.localOverlay.status === "missing") {
|
|
461
874
|
hardFailures.push(
|
|
462
875
|
consumerGateIssue(
|
|
@@ -468,13 +881,25 @@ const consumerOverlayGate = (status) => {
|
|
|
468
881
|
),
|
|
469
882
|
);
|
|
470
883
|
}
|
|
471
|
-
|
|
884
|
+
for (const failure of status.packageIntegrity.failures ?? []) {
|
|
472
885
|
hardFailures.push(
|
|
473
886
|
consumerGateIssue(
|
|
474
|
-
|
|
887
|
+
failure.code,
|
|
475
888
|
"hard",
|
|
476
889
|
"package_integrity",
|
|
477
|
-
|
|
890
|
+
failure.message ?? `local consumer overlay package integrity failed: ${failure.code}`,
|
|
891
|
+
failure,
|
|
892
|
+
),
|
|
893
|
+
);
|
|
894
|
+
}
|
|
895
|
+
for (const failure of status.exportEquivalence?.failures ?? []) {
|
|
896
|
+
hardFailures.push(
|
|
897
|
+
consumerGateIssue(
|
|
898
|
+
failure.code,
|
|
899
|
+
"hard",
|
|
900
|
+
"export_equivalence",
|
|
901
|
+
`local consumer overlay export equivalence failed: ${failure.code}`,
|
|
902
|
+
failure,
|
|
478
903
|
),
|
|
479
904
|
);
|
|
480
905
|
}
|
|
@@ -513,41 +938,6 @@ const consumerOverlayGate = (status) => {
|
|
|
513
938
|
),
|
|
514
939
|
);
|
|
515
940
|
}
|
|
516
|
-
for (const pkg of status.localOverlay.packages ?? []) {
|
|
517
|
-
if (pkg.targetStatus === "missing") {
|
|
518
|
-
hardFailures.push(
|
|
519
|
-
consumerGateIssue(
|
|
520
|
-
"local_overlay_package_missing",
|
|
521
|
-
"hard",
|
|
522
|
-
"package_integrity",
|
|
523
|
-
`${pkg.packageName} is missing from the consumer overlay`,
|
|
524
|
-
{ packageName: pkg.packageName },
|
|
525
|
-
),
|
|
526
|
-
);
|
|
527
|
-
}
|
|
528
|
-
if (pkg.targetStatus === "symlink") {
|
|
529
|
-
hardFailures.push(
|
|
530
|
-
consumerGateIssue(
|
|
531
|
-
"local_overlay_package_symlink",
|
|
532
|
-
"hard",
|
|
533
|
-
"package_integrity",
|
|
534
|
-
`${pkg.packageName} is a symlink, not packed package content`,
|
|
535
|
-
{ packageName: pkg.packageName },
|
|
536
|
-
),
|
|
537
|
-
);
|
|
538
|
-
}
|
|
539
|
-
if (pkg.tarballStatus !== "verified") {
|
|
540
|
-
hardFailures.push(
|
|
541
|
-
consumerGateIssue(
|
|
542
|
-
"local_overlay_tarball_not_verified",
|
|
543
|
-
"hard",
|
|
544
|
-
"package_integrity",
|
|
545
|
-
`${pkg.packageName} tarball status is ${pkg.tarballStatus}`,
|
|
546
|
-
{ packageName: pkg.packageName, tarballStatus: pkg.tarballStatus },
|
|
547
|
-
),
|
|
548
|
-
);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
941
|
if (status.npmLatest.status === "not_checked") {
|
|
552
942
|
signals.push(
|
|
553
943
|
consumerGateIssue(
|
|
@@ -585,7 +975,7 @@ const withConsumerGate = (status) => ({
|
|
|
585
975
|
gate: consumerOverlayGate(status),
|
|
586
976
|
});
|
|
587
977
|
|
|
588
|
-
|
|
978
|
+
const consumerStatusDataForRoot = (consumerRoot, options = {}) => {
|
|
589
979
|
const metadata = packageMetadata(options.packageRoot);
|
|
590
980
|
const markerPath = localConsumerMarkerPath(consumerRoot);
|
|
591
981
|
const currentSource =
|
|
@@ -606,6 +996,19 @@ export const consumerStatusData = (consumerRoot, options = {}) => {
|
|
|
606
996
|
}
|
|
607
997
|
const marker = readJson(markerPath);
|
|
608
998
|
const packages = packageOverlayRows(consumerRoot, marker);
|
|
999
|
+
const exportEquivalence = exportEquivalenceProjection(
|
|
1000
|
+
packages.map((pkg) => ({
|
|
1001
|
+
packageName: pkg.packageName,
|
|
1002
|
+
tarball: pkg.tarball,
|
|
1003
|
+
installedManifestPath: path.join(
|
|
1004
|
+
consumerRoot,
|
|
1005
|
+
"node_modules",
|
|
1006
|
+
...pkg.packageName.split("/"),
|
|
1007
|
+
"package.json",
|
|
1008
|
+
),
|
|
1009
|
+
})),
|
|
1010
|
+
{ sourceRoot: options.sourceRoot },
|
|
1011
|
+
);
|
|
609
1012
|
const sourceStatus = overlaySourceStatus(marker, currentSource);
|
|
610
1013
|
const packageIntegrity = packageIntegrityFor(marker, packages);
|
|
611
1014
|
const sourceFreshness = sourceFreshnessFor(marker, currentSource);
|
|
@@ -623,6 +1026,7 @@ export const consumerStatusData = (consumerRoot, options = {}) => {
|
|
|
623
1026
|
packages,
|
|
624
1027
|
},
|
|
625
1028
|
packageIntegrity,
|
|
1029
|
+
exportEquivalence,
|
|
626
1030
|
sourceFreshness,
|
|
627
1031
|
source: {
|
|
628
1032
|
...(currentSource === undefined ? {} : { current: currentSource }),
|
|
@@ -643,12 +1047,62 @@ export const consumerStatusData = (consumerRoot, options = {}) => {
|
|
|
643
1047
|
});
|
|
644
1048
|
};
|
|
645
1049
|
|
|
1050
|
+
const workspaceRootStatusSummary = (root, status) => ({
|
|
1051
|
+
kind: root.kind,
|
|
1052
|
+
relativePath: root.relativePath,
|
|
1053
|
+
consumerRoot: root.consumerRoot,
|
|
1054
|
+
...(root.packageName === undefined ? {} : { packageName: root.packageName }),
|
|
1055
|
+
truthMode: status.truthMode,
|
|
1056
|
+
localOverlay: status.localOverlay,
|
|
1057
|
+
packageIntegrity: status.packageIntegrity,
|
|
1058
|
+
exportEquivalence: status.exportEquivalence,
|
|
1059
|
+
sourceFreshness: status.sourceFreshness,
|
|
1060
|
+
packageVersion: status.packageVersion,
|
|
1061
|
+
gate: status.gate,
|
|
1062
|
+
});
|
|
1063
|
+
|
|
1064
|
+
export const consumerStatusData = (consumerRoot, options = {}) => {
|
|
1065
|
+
const status = consumerStatusDataForRoot(consumerRoot, options);
|
|
1066
|
+
if (options.workspace === false) return status;
|
|
1067
|
+
const layout = consumerWorkspaceLayout(consumerRoot);
|
|
1068
|
+
if (layout.status === "not_workspace") return status;
|
|
1069
|
+
const roots =
|
|
1070
|
+
layout.status === "invalid"
|
|
1071
|
+
? [workspaceRootStatusSummary(layout.roots[0], status)]
|
|
1072
|
+
: layout.roots.map((root) =>
|
|
1073
|
+
workspaceRootStatusSummary(
|
|
1074
|
+
root,
|
|
1075
|
+
root.relativePath === "."
|
|
1076
|
+
? status
|
|
1077
|
+
: consumerStatusDataForRoot(root.consumerRoot, options),
|
|
1078
|
+
),
|
|
1079
|
+
);
|
|
1080
|
+
const workspaceStatus =
|
|
1081
|
+
layout.status === "invalid"
|
|
1082
|
+
? "invalid"
|
|
1083
|
+
: roots.every((root) => root.gate.status === "pass")
|
|
1084
|
+
? "verified"
|
|
1085
|
+
: "failed";
|
|
1086
|
+
return withConsumerGate({
|
|
1087
|
+
...status,
|
|
1088
|
+
workspaceOverlay: {
|
|
1089
|
+
status: workspaceStatus,
|
|
1090
|
+
manifestPath: layout.manifestPath,
|
|
1091
|
+
roots,
|
|
1092
|
+
...(layout.error === undefined ? {} : { error: layout.error }),
|
|
1093
|
+
},
|
|
1094
|
+
});
|
|
1095
|
+
};
|
|
1096
|
+
|
|
646
1097
|
const printConsumerStatus = (status) => {
|
|
647
1098
|
console.log(`consumer: ${status.consumerRoot}`);
|
|
648
1099
|
console.log(`marker: ${status.markerPath}`);
|
|
649
1100
|
console.log(`truth mode: ${status.truthMode}`);
|
|
650
1101
|
console.log(`local overlay: ${status.localOverlay.status}`);
|
|
651
1102
|
console.log(`package integrity: ${status.packageIntegrity.status}`);
|
|
1103
|
+
if (status.exportEquivalence !== undefined) {
|
|
1104
|
+
console.log(`export equivalence: ${status.exportEquivalence.status}`);
|
|
1105
|
+
}
|
|
652
1106
|
if (status.sourceFreshness !== undefined) {
|
|
653
1107
|
console.log(`source freshness: ${status.sourceFreshness.status}`);
|
|
654
1108
|
}
|
|
@@ -659,6 +1113,14 @@ const printConsumerStatus = (status) => {
|
|
|
659
1113
|
`package version: overlay=${status.packageVersion.overlay ?? "none"} release=${status.packageVersion.release} status=${status.packageVersion.status ?? "none"}`,
|
|
660
1114
|
);
|
|
661
1115
|
console.log(`npm latest: ${status.npmLatest.status}`);
|
|
1116
|
+
if (status.workspaceOverlay !== undefined) {
|
|
1117
|
+
console.log(`workspace overlay: ${status.workspaceOverlay.status}`);
|
|
1118
|
+
for (const root of status.workspaceOverlay.roots ?? []) {
|
|
1119
|
+
console.log(
|
|
1120
|
+
`workspace ${root.relativePath}: gate=${root.gate.status} overlay=${root.localOverlay.status}`,
|
|
1121
|
+
);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
662
1124
|
console.log(`gate: ${status.gate.status}`);
|
|
663
1125
|
for (const pkg of status.localOverlay.packages ?? []) {
|
|
664
1126
|
console.log(
|
|
@@ -695,40 +1157,50 @@ export const installConsumer = async (rawArgs, context = {}) => {
|
|
|
695
1157
|
const args = parseArgs(rawArgs);
|
|
696
1158
|
const consumerRoot = resolveConsumerRoot(positionalArgs(args)[0]);
|
|
697
1159
|
const manifestPath = await installManifestPathForArgs(args, context);
|
|
698
|
-
const
|
|
1160
|
+
const workspaceLayout = consumerWorkspaceLayout(consumerRoot);
|
|
1161
|
+
if (workspaceLayout.status === "invalid") {
|
|
1162
|
+
fail(`${consumerRoot}: ${workspaceLayout.error}`);
|
|
1163
|
+
}
|
|
1164
|
+
const snapshot = snapshotFiles(consumerWorkspaceManifestFiles(workspaceLayout));
|
|
699
1165
|
const { manifest } = readInstallManifest(manifestPath);
|
|
700
1166
|
const entries = tarballPackageEntries(manifest);
|
|
701
|
-
|
|
702
|
-
const packages = {};
|
|
703
|
-
for (const entry of entries) {
|
|
704
|
-
const target = packageTargetDir(nodeModules, entry.packageName);
|
|
705
|
-
unpackTarballInto(entry.tarball, target);
|
|
706
|
-
packages[entry.packageName] = {
|
|
707
|
-
target: path.relative(consumerRoot, target).split(path.sep).join("/"),
|
|
708
|
-
tarball: entry.tarball,
|
|
709
|
-
sha256: entry.sha256,
|
|
710
|
-
};
|
|
711
|
-
}
|
|
1167
|
+
nodeModulesRoot(consumerRoot, { install: !boolArg(args, "no-install") });
|
|
712
1168
|
const source =
|
|
713
1169
|
typeof context.sourceRoot === "string"
|
|
714
1170
|
? sourceIdentityFor(context.sourceRoot)
|
|
715
1171
|
: (manifest.source ?? undefined);
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
1172
|
+
const installedAt = new Date().toISOString();
|
|
1173
|
+
for (const root of workspaceLayout.roots) {
|
|
1174
|
+
const nodeModules = path.join(root.consumerRoot, "node_modules");
|
|
1175
|
+
const packages = {};
|
|
1176
|
+
for (const entry of entries) {
|
|
1177
|
+
const target = packageTargetDir(nodeModules, entry.packageName);
|
|
1178
|
+
unpackTarballInto(entry.tarball, target);
|
|
1179
|
+
packages[entry.packageName] = {
|
|
1180
|
+
target: path.relative(root.consumerRoot, target).split(path.sep).join("/"),
|
|
1181
|
+
tarball: entry.tarball,
|
|
1182
|
+
sha256: entry.sha256,
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
writeJson(localConsumerMarkerPath(root.consumerRoot), {
|
|
1186
|
+
schemaVersion: 1,
|
|
1187
|
+
generatedBy: "agentos consumer install",
|
|
1188
|
+
installedAt,
|
|
1189
|
+
consumerRoot: root.consumerRoot,
|
|
1190
|
+
...(source === undefined ? {} : { source }),
|
|
1191
|
+
packageVersion: manifest.version,
|
|
1192
|
+
artifact: markerArtifact(manifestPath, manifest),
|
|
1193
|
+
packages,
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
726
1196
|
assertSnapshotUnchanged(snapshot, "agentos consumer install");
|
|
727
1197
|
const status = consumerStatusData(consumerRoot, { sourceRoot: context.sourceRoot });
|
|
728
1198
|
if (boolArg(args, "json")) {
|
|
729
1199
|
console.log(JSON.stringify(status, null, 2));
|
|
730
1200
|
} else {
|
|
731
|
-
console.log(
|
|
1201
|
+
console.log(
|
|
1202
|
+
`installed ${entries.length} local agentOS packages into ${workspaceLayout.roots.length} consumer root(s)`,
|
|
1203
|
+
);
|
|
732
1204
|
console.log(
|
|
733
1205
|
`wrote ${path.relative(consumerRoot, localConsumerMarkerPath(consumerRoot)).split(path.sep).join("/")}`,
|
|
734
1206
|
);
|
|
@@ -790,7 +1262,14 @@ export const restoreConsumer = (rawArgs) => {
|
|
|
790
1262
|
}
|
|
791
1263
|
fs.rmSync(markerPath, { force: true });
|
|
792
1264
|
if (!boolArg(args, "no-install")) {
|
|
793
|
-
|
|
1265
|
+
const installCommand = consumerInstallCommand(consumerRoot);
|
|
1266
|
+
if (installCommand === null) {
|
|
1267
|
+
fail(`${consumerRoot}: no package manager/lockfile was detected for consumer restore`);
|
|
1268
|
+
}
|
|
1269
|
+
run(installCommand.cmd, installCommand.args, {
|
|
1270
|
+
cwd: consumerRoot,
|
|
1271
|
+
env: installCommand.env,
|
|
1272
|
+
});
|
|
794
1273
|
}
|
|
795
1274
|
assertSnapshotUnchanged(snapshot, "agentos consumer restore");
|
|
796
1275
|
const result = { schemaVersion: 1, restoredPackages: packageNames };
|