@yansirplus/cli 0.5.17 → 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.
Files changed (53) hide show
  1. package/README.md +12 -6
  2. package/agent-catalog/agentOS/SKILL.md +22 -0
  3. package/agent-catalog/agentOS/references/agent/decision-graph.json +530 -0
  4. package/agent-catalog/agentOS/references/agent/errors.json +497 -0
  5. package/agent-catalog/agentOS/references/agent/invariant-matrix.json +337 -0
  6. package/agent-catalog/agentOS/references/agent/primitives.json +989 -0
  7. package/agent-catalog/agentOS/references/agent/recipes.json +109 -0
  8. package/agent-catalog/agentOS/references/agent/start-here.md +25 -0
  9. package/agent-catalog/agentOS/references/package-map.md +73 -0
  10. package/agent-catalog/agentOS/references/provenance.json +251 -0
  11. package/agent-catalog/agentOS/references/public-api/cli.md +20 -0
  12. package/agent-catalog/agentOS/references/public-api/client.md +90 -0
  13. package/agent-catalog/agentOS/references/public-api/core.md +1907 -0
  14. package/agent-catalog/agentOS/references/public-api/runtime.md +843 -0
  15. package/dist/build/agent-authoring/config.d.ts +20 -5
  16. package/dist/build/agent-authoring/config.js +132 -32
  17. package/dist/build/agent-authoring/manifest-compiler.d.ts +131 -2
  18. package/dist/build/agent-authoring/manifest-compiler.js +630 -8
  19. package/dist/build/agent-authoring/shared.d.ts +2 -0
  20. package/dist/build/agent-authoring/shared.js +2 -0
  21. package/dist/build/agent-authoring/static-target.d.ts +6 -3
  22. package/dist/build/agent-authoring/static-target.js +1900 -281
  23. package/dist/build/agent-authoring.d.ts +3 -3
  24. package/dist/build/agent-authoring.js +1 -1
  25. package/dist/build/build-cli.d.ts +1 -1
  26. package/dist/build/build-cli.js +1629 -26
  27. package/dist/check/algorithmic/client-boundary-checks.mjs +3 -34
  28. package/dist/check/algorithmic/convergence-smoke-checks.mjs +652 -6
  29. package/dist/check/algorithmic/distribution-checks.mjs +8 -7
  30. package/dist/check/algorithmic/package-boundary-checks.mjs +3 -2
  31. package/dist/check/algorithmic/repo-surface-checks.mjs +55 -1
  32. package/dist/check/algorithmic/static-target-checks.mjs +83 -5
  33. package/dist/check/algorithmic-checks.mjs +10 -17
  34. package/dist/check/default-gate.mjs +3 -3
  35. package/dist/check/effect-scan-gate.mjs +121 -0
  36. package/dist/check/package-graph.mjs +2 -32
  37. package/dist/consumer-overlay.mjs +1281 -0
  38. package/dist/lib/public-api-model.mjs +19 -0
  39. package/dist/lib/repo-source-files.mjs +26 -0
  40. package/dist/lib/ts-module-loader.mjs +44 -0
  41. package/dist/lib/workspace-manifest.mjs +77 -0
  42. package/dist/main.mjs +171 -21
  43. package/dist/release-status.mjs +515 -0
  44. package/package.json +8 -4
  45. package/dist/check/check-coverage.mjs +0 -231
  46. package/dist/generate/generate-agent-docs.mjs +0 -435
  47. package/dist/generate/generate-carrier-reference.mjs +0 -514
  48. package/dist/generate/generate-docs.mjs +0 -345
  49. package/dist/generate/generate-effect-skill-manifests.mjs +0 -193
  50. package/dist/generate/project-docs-site.mjs +0 -190
  51. package/dist/lib/boundary-rules.mjs +0 -63
  52. package/dist/lib/capability-routes.mjs +0 -354
  53. package/dist/lib/projection-sink.mjs +0 -113
@@ -0,0 +1,1281 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import crypto from "node:crypto";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ import { workspacePackagePaths } from "./lib/workspace-manifest.mjs";
8
+
9
+ export const installManifestProtocol = "agentos-install-manifest@1";
10
+ export const localConsumerMarkerName = ".agentos-local.json";
11
+
12
+ const packageRootFromModule = () => path.dirname(path.dirname(fileURLToPath(import.meta.url)));
13
+
14
+ const fail = (message) => {
15
+ throw new Error(message);
16
+ };
17
+
18
+ const readJson = (file) => JSON.parse(fs.readFileSync(file, "utf8"));
19
+
20
+ const writeJson = (file, value) => {
21
+ fs.mkdirSync(path.dirname(file), { recursive: true });
22
+ fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`);
23
+ };
24
+
25
+ const sha256File = (file) =>
26
+ crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex");
27
+
28
+ const run = (cmd, args, options = {}) => {
29
+ const result = spawnSync(cmd, args, {
30
+ cwd: options.cwd ?? process.cwd(),
31
+ env: options.env ?? process.env,
32
+ encoding: "utf8",
33
+ stdio: options.capture === true ? ["ignore", "pipe", "pipe"] : "inherit",
34
+ });
35
+ if (result.status !== 0) {
36
+ const detail = options.capture === true ? `\n${result.stdout ?? ""}${result.stderr ?? ""}` : "";
37
+ fail(`${cmd} ${args.join(" ")} failed with exit ${result.status}${detail}`);
38
+ }
39
+ return result;
40
+ };
41
+
42
+ const parseArgs = (args) => {
43
+ const parsed = { _: [] };
44
+ const booleanKeys = new Set(["skip-pack", "no-install", "json", "check-npm"]);
45
+ for (let index = 0; index < args.length; index += 1) {
46
+ const arg = args[index];
47
+ if (!arg.startsWith("--")) {
48
+ parsed._.push(arg);
49
+ continue;
50
+ }
51
+ const eq = arg.indexOf("=");
52
+ if (eq >= 0) {
53
+ parsed[arg.slice(2, eq)] = arg.slice(eq + 1);
54
+ continue;
55
+ }
56
+ const key = arg.slice(2);
57
+ if (booleanKeys.has(key)) {
58
+ parsed[key] = true;
59
+ continue;
60
+ }
61
+ const next = args[index + 1];
62
+ if (next !== undefined && !next.startsWith("--")) {
63
+ parsed[key] = next;
64
+ index += 1;
65
+ } else {
66
+ parsed[key] = true;
67
+ }
68
+ }
69
+ return parsed;
70
+ };
71
+
72
+ const positionalArgs = (args) => args._ ?? [];
73
+
74
+ const boolArg = (args, name) => args[name] === true || args[name] === "true";
75
+
76
+ const packageMetadata = (packageRoot = packageRootFromModule()) => {
77
+ const manifest = readJson(path.join(packageRoot, "package.json"));
78
+ const version =
79
+ typeof manifest.agentOsRelease?.version === "string"
80
+ ? manifest.agentOsRelease.version
81
+ : manifest.version;
82
+ if (typeof version !== "string" || version.length === 0) {
83
+ fail(`${path.join(packageRoot, "package.json")}: package version must be a non-empty string`);
84
+ }
85
+ const scope = typeof manifest.name === "string" ? manifest.name.split("/")[0] : undefined;
86
+ return {
87
+ packageRoot,
88
+ packageName: manifest.name,
89
+ packageVersion: version,
90
+ packageScope: typeof scope === "string" && scope.startsWith("@") ? scope : undefined,
91
+ };
92
+ };
93
+
94
+ const gitValue = (cwd, args, fallback) => {
95
+ const result = spawnSync("git", args, {
96
+ cwd,
97
+ encoding: "utf8",
98
+ stdio: ["ignore", "pipe", "ignore"],
99
+ });
100
+ if (result.status !== 0) return fallback;
101
+ const value = result.stdout.trim();
102
+ return value.length === 0 ? fallback : value;
103
+ };
104
+
105
+ export const sourceIdentityFor = (sourceRoot) => ({
106
+ repoRoot: sourceRoot,
107
+ branch: gitValue(sourceRoot, ["branch", "--show-current"], "unknown"),
108
+ head: gitValue(sourceRoot, ["rev-parse", "HEAD"], "unknown"),
109
+ dirty: gitValue(sourceRoot, ["status", "--short"], "").length > 0,
110
+ });
111
+
112
+ export const consumerManifestFiles = (consumerRoot) =>
113
+ ["package.json", "package-lock.json", "npm-shrinkwrap.json", "pnpm-lock.yaml", "yarn.lock"].map(
114
+ (name) => path.join(consumerRoot, name),
115
+ );
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
+
168
+ export const snapshotFiles = (files) =>
169
+ new Map(files.map((file) => [file, fs.existsSync(file) ? fs.readFileSync(file) : undefined]));
170
+
171
+ export const assertSnapshotUnchanged = (snapshot, context) => {
172
+ const changed = [];
173
+ for (const [file, before] of snapshot.entries()) {
174
+ const after = fs.existsSync(file) ? fs.readFileSync(file) : undefined;
175
+ if (
176
+ before === undefined
177
+ ? after !== undefined
178
+ : after === undefined || !Buffer.from(before).equals(after)
179
+ ) {
180
+ changed.push(path.basename(file));
181
+ }
182
+ }
183
+ if (changed.length > 0) {
184
+ fail(`${context} changed consumer manifest/lock files:\n${changed.join("\n")}`);
185
+ }
186
+ };
187
+
188
+ export const resolveConsumerRoot = (value) => {
189
+ if (typeof value !== "string" || value.length === 0) {
190
+ fail("consumer path is required");
191
+ }
192
+ const consumerRoot = path.resolve(process.cwd(), value);
193
+ if (!fs.existsSync(path.join(consumerRoot, "package.json"))) {
194
+ fail(`${consumerRoot}: missing package.json`);
195
+ }
196
+ return consumerRoot;
197
+ };
198
+
199
+ export const localConsumerMarkerPath = (consumerRoot) =>
200
+ path.join(consumerRoot, "node_modules", localConsumerMarkerName);
201
+
202
+ const consumerPackageManagerName = (consumerRoot) => {
203
+ const packageJson = readJson(path.join(consumerRoot, "package.json"));
204
+ const packageManager =
205
+ typeof packageJson.packageManager === "string" ? packageJson.packageManager : "";
206
+ if (packageManager.startsWith("pnpm@")) return "pnpm";
207
+ if (packageManager.startsWith("npm@")) return "npm";
208
+ if (packageManager.startsWith("bun@")) return "bun";
209
+ if (packageManager.startsWith("yarn@")) return "yarn";
210
+ if (fs.existsSync(path.join(consumerRoot, "pnpm-lock.yaml"))) return "pnpm";
211
+ if (fs.existsSync(path.join(consumerRoot, "package-lock.json"))) return "npm";
212
+ if (fs.existsSync(path.join(consumerRoot, "bun.lock"))) return "bun";
213
+ if (fs.existsSync(path.join(consumerRoot, "bun.lockb"))) return "bun";
214
+ if (fs.existsSync(path.join(consumerRoot, "yarn.lock"))) return "yarn";
215
+ return null;
216
+ };
217
+
218
+ export const consumerInstallCommand = (consumerRoot) => {
219
+ const manager = consumerPackageManagerName(consumerRoot);
220
+ switch (manager) {
221
+ case "pnpm":
222
+ return {
223
+ manager,
224
+ cmd: "pnpm",
225
+ args: ["install", "--frozen-lockfile", "--ignore-scripts"],
226
+ env: { ...process.env, CI: "true", COREPACK_ENABLE_DOWNLOAD_PROMPT: "0" },
227
+ };
228
+ case "npm":
229
+ return fs.existsSync(path.join(consumerRoot, "package-lock.json"))
230
+ ? {
231
+ manager,
232
+ cmd: "npm",
233
+ args: ["ci", "--ignore-scripts", "--no-audit", "--no-fund"],
234
+ env: process.env,
235
+ }
236
+ : {
237
+ manager,
238
+ cmd: "npm",
239
+ args: [
240
+ "install",
241
+ "--package-lock=false",
242
+ "--ignore-scripts",
243
+ "--no-audit",
244
+ "--no-fund",
245
+ ],
246
+ env: process.env,
247
+ };
248
+ case "bun":
249
+ return {
250
+ manager,
251
+ cmd: "bun",
252
+ args: ["install", "--frozen-lockfile", "--ignore-scripts"],
253
+ env: { ...process.env, CI: "true" },
254
+ };
255
+ case "yarn":
256
+ return {
257
+ manager,
258
+ cmd: "yarn",
259
+ args: ["install", "--immutable", "--ignore-scripts"],
260
+ env: { ...process.env, CI: "true" },
261
+ };
262
+ default:
263
+ return null;
264
+ }
265
+ };
266
+
267
+ export const nodeModulesRoot = (consumerRoot, options = {}) => {
268
+ const root = path.join(consumerRoot, "node_modules");
269
+ if (fs.existsSync(root)) return root;
270
+ if (options.install !== true) {
271
+ fail(
272
+ `${consumerRoot}: missing node_modules; run the consumer package manager install first, or rerun install without --no-install to let agentOS run a frozen install`,
273
+ );
274
+ }
275
+ const installCommand = consumerInstallCommand(consumerRoot);
276
+ if (installCommand === null) {
277
+ fail(
278
+ `${consumerRoot}: missing node_modules and no package manager/lockfile was detected; run the consumer install first`,
279
+ );
280
+ }
281
+ console.log(
282
+ `node_modules missing; running ${installCommand.cmd} ${installCommand.args.join(" ")} in ${consumerRoot}`,
283
+ );
284
+ run(installCommand.cmd, installCommand.args, {
285
+ cwd: consumerRoot,
286
+ capture: true,
287
+ env: installCommand.env,
288
+ });
289
+ if (!fs.existsSync(root)) {
290
+ fail(`${consumerRoot}: package manager install completed but node_modules is still missing`);
291
+ }
292
+ return root;
293
+ };
294
+
295
+ export const packageTargetDir = (nodeModules, packageName) =>
296
+ path.join(nodeModules, ...packageName.split("/"));
297
+
298
+ export const unpackTarballInto = (tarball, target) => {
299
+ const tmp = fs.mkdtempSync(path.join(fs.realpathSync("/tmp"), "agentos-consumer-package-"));
300
+ try {
301
+ run("tar", ["-xzf", tarball, "-C", tmp], { capture: true });
302
+ const packageDir = path.join(tmp, "package");
303
+ if (!fs.existsSync(packageDir)) {
304
+ fail(`${tarball}: tarball did not contain package/`);
305
+ }
306
+ fs.rmSync(target, { recursive: true, force: true });
307
+ fs.mkdirSync(path.dirname(target), { recursive: true });
308
+ fs.renameSync(packageDir, target);
309
+ } finally {
310
+ fs.rmSync(tmp, { recursive: true, force: true });
311
+ }
312
+ };
313
+
314
+ export const readInstallManifest = (manifestPath) => {
315
+ if (typeof manifestPath !== "string" || manifestPath.length === 0) {
316
+ fail("install manifest path is required");
317
+ }
318
+ const absolutePath = path.resolve(process.cwd(), manifestPath);
319
+ if (!fs.existsSync(absolutePath)) {
320
+ fail(`${absolutePath}: install manifest is missing`);
321
+ }
322
+ const manifest = readJson(absolutePath);
323
+ if (manifest === null || typeof manifest !== "object" || Array.isArray(manifest)) {
324
+ fail(`${absolutePath}: install manifest must be an object`);
325
+ }
326
+ if (manifest.protocol !== installManifestProtocol) {
327
+ fail(`${absolutePath}: install manifest protocol must be ${installManifestProtocol}`);
328
+ }
329
+ if (typeof manifest.version !== "string" || manifest.version.length === 0) {
330
+ fail(`${absolutePath}: install manifest version must be a non-empty string`);
331
+ }
332
+ if (manifest.tarballs === null || typeof manifest.tarballs !== "object") {
333
+ fail(`${absolutePath}: install manifest tarballs must be an object`);
334
+ }
335
+ return { path: absolutePath, manifest };
336
+ };
337
+
338
+ const fileSpecPath = (spec) => {
339
+ if (typeof spec !== "string" || !spec.startsWith("file:")) {
340
+ fail(`expected file: tarball spec; actual ${String(spec)}`);
341
+ }
342
+ return spec.slice("file:".length);
343
+ };
344
+
345
+ const optionalFileSpecPath = (spec) =>
346
+ typeof spec === "string" && spec.startsWith("file:") ? spec.slice("file:".length) : undefined;
347
+
348
+ export const tarballPackageEntries = (manifest) =>
349
+ Object.entries(manifest.tarballs)
350
+ .map(([packageName, entry]) => {
351
+ if (entry === null || typeof entry !== "object") {
352
+ fail(`${packageName}: invalid tarball manifest entry`);
353
+ }
354
+ const tarball = fileSpecPath(entry.spec);
355
+ if (!fs.existsSync(tarball)) {
356
+ fail(`${packageName}: tarball does not exist: ${tarball}`);
357
+ }
358
+ if (typeof entry.sha256 !== "string" || entry.sha256.length !== 64) {
359
+ fail(`${packageName}: tarball manifest entry sha256 must be hex64`);
360
+ }
361
+ return {
362
+ packageName,
363
+ tarball,
364
+ sha256: entry.sha256,
365
+ };
366
+ })
367
+ .sort((left, right) => left.packageName.localeCompare(right.packageName));
368
+
369
+ const markerArtifact = (manifestPath, manifest) => ({
370
+ kind: "install-manifest-overlay",
371
+ installManifest: {
372
+ path: manifestPath,
373
+ sha256: sha256File(manifestPath),
374
+ protocol: manifest.protocol,
375
+ version: manifest.version,
376
+ generatedBy: manifest.generatedBy,
377
+ },
378
+ });
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
+
700
+ const packageOverlayRows = (consumerRoot, marker) => {
701
+ const nodeModules = path.join(consumerRoot, "node_modules");
702
+ return Object.entries(marker.packages ?? {})
703
+ .map(([packageName, record]) => {
704
+ const target = packageTargetDir(nodeModules, packageName);
705
+ const targetExists = fs.existsSync(target);
706
+ const targetStatus = !targetExists
707
+ ? "missing"
708
+ : fs.lstatSync(target).isSymbolicLink()
709
+ ? "symlink"
710
+ : "installed";
711
+ const tarball = typeof record.tarball === "string" ? record.tarball : "";
712
+ const tarballExists = tarball.length > 0 && fs.existsSync(tarball);
713
+ const expectedSha = typeof record.sha256 === "string" ? record.sha256 : undefined;
714
+ const actualSha = tarballExists ? sha256File(tarball) : undefined;
715
+ const requiresSha = marker.artifact?.kind === "install-manifest-overlay";
716
+ return {
717
+ packageName,
718
+ target: record.target,
719
+ installed: targetStatus === "installed",
720
+ targetStatus,
721
+ tarball,
722
+ tarballStatus: tarballExists
723
+ ? expectedSha === undefined
724
+ ? requiresSha
725
+ ? "sha_missing"
726
+ : "verified"
727
+ : expectedSha === actualSha
728
+ ? "verified"
729
+ : "sha_mismatch"
730
+ : "missing",
731
+ sha256: expectedSha,
732
+ };
733
+ })
734
+ .sort((left, right) => left.packageName.localeCompare(right.packageName));
735
+ };
736
+
737
+ const overlaySourceStatus = (marker, currentSource) => {
738
+ if (marker.source === undefined) return "not_recorded";
739
+ if (currentSource === undefined) return "not_checked";
740
+ if (marker.source?.repoRoot !== currentSource.repoRoot) return "foreign_source";
741
+ if (marker.source?.head !== currentSource.head) return "stale_source";
742
+ if (marker.source?.dirty !== currentSource.dirty) return "dirty_state_changed";
743
+ return "current_source";
744
+ };
745
+
746
+ const truthModeFor = (marker) => {
747
+ if (marker === undefined) return "npm_release";
748
+ if (marker.artifact?.kind === "install-manifest-overlay") return "local_overlay";
749
+ return "legacy_local_overlay";
750
+ };
751
+
752
+ const packageIntegrityFor = (marker, packages) => {
753
+ if (marker === undefined) {
754
+ return {
755
+ status: "not_checked",
756
+ reason: "local overlay marker is missing; consumer is using package-manager truth",
757
+ };
758
+ }
759
+ const failures = [];
760
+ if (packages.length === 0) {
761
+ failures.push({ code: "local_overlay_packages_missing", message: "marker lists no packages" });
762
+ }
763
+ for (const pkg of packages) {
764
+ if (pkg.targetStatus === "missing") {
765
+ failures.push({
766
+ code: "local_overlay_package_missing",
767
+ packageName: pkg.packageName,
768
+ targetStatus: pkg.targetStatus,
769
+ });
770
+ }
771
+ if (pkg.targetStatus === "symlink") {
772
+ failures.push({
773
+ code: "local_overlay_package_symlink",
774
+ packageName: pkg.packageName,
775
+ targetStatus: pkg.targetStatus,
776
+ });
777
+ }
778
+ if (pkg.tarballStatus !== "verified") {
779
+ failures.push({
780
+ code: "local_overlay_tarball_not_verified",
781
+ packageName: pkg.packageName,
782
+ tarballStatus: pkg.tarballStatus,
783
+ });
784
+ }
785
+ }
786
+ return {
787
+ status: failures.length === 0 ? "verified" : "failed",
788
+ packagesChecked: packages.length,
789
+ failures,
790
+ };
791
+ };
792
+
793
+ const sourceFreshnessFor = (marker, currentSource) => {
794
+ const status = overlaySourceStatus(marker ?? {}, currentSource);
795
+ return {
796
+ status,
797
+ checked: currentSource !== undefined,
798
+ gate: ["current_source", "not_checked"].includes(status) ? "pass" : "fail",
799
+ ...(status === "not_checked"
800
+ ? { reason: "source checkout identity is unavailable in this invocation" }
801
+ : {}),
802
+ };
803
+ };
804
+
805
+ const packageVersionStatus = (marker, packageVersion) =>
806
+ marker.packageVersion === packageVersion ? "release_version_match" : "release_version_mismatch";
807
+
808
+ const npmLatestNotChecked = () => ({
809
+ status: "not_checked",
810
+ reason: "pass --check-npm to compare against the registry",
811
+ });
812
+
813
+ const npmLatestFor = (packageNames, registry) => {
814
+ const packages = {};
815
+ for (const packageName of packageNames) {
816
+ const args = ["view", packageName, "version", "--json"];
817
+ if (typeof registry === "string" && registry.length > 0) args.push("--registry", registry);
818
+ const result = spawnSync("npm", args, {
819
+ encoding: "utf8",
820
+ stdio: ["ignore", "pipe", "pipe"],
821
+ });
822
+ packages[packageName] =
823
+ result.status === 0
824
+ ? { status: "resolved", version: JSON.parse(result.stdout.trim()) }
825
+ : { status: "unresolved", detail: result.stderr.trim() || result.stdout.trim() };
826
+ }
827
+ return { status: "checked", packages };
828
+ };
829
+
830
+ const consumerGateIssue = (code, severity, dimension, message, detail = {}) => ({
831
+ code,
832
+ severity,
833
+ dimension,
834
+ message,
835
+ ...detail,
836
+ });
837
+
838
+ const consumerOverlayGate = (status) => {
839
+ const hardFailures = [];
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
+ }
873
+ if (status.localOverlay.status === "missing") {
874
+ hardFailures.push(
875
+ consumerGateIssue(
876
+ "local_overlay_missing",
877
+ "hard",
878
+ "truth_mode",
879
+ "local consumer overlay marker is missing",
880
+ { markerPath: status.markerPath },
881
+ ),
882
+ );
883
+ }
884
+ for (const failure of status.packageIntegrity.failures ?? []) {
885
+ hardFailures.push(
886
+ consumerGateIssue(
887
+ failure.code,
888
+ "hard",
889
+ "package_integrity",
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,
903
+ ),
904
+ );
905
+ }
906
+ if (status.sourceFreshness?.gate === "fail") {
907
+ hardFailures.push(
908
+ consumerGateIssue(
909
+ "local_overlay_source_not_current",
910
+ "hard",
911
+ "source_freshness",
912
+ `local consumer overlay source freshness is ${status.sourceFreshness.status}`,
913
+ { sourceStatus: status.sourceFreshness.status },
914
+ ),
915
+ );
916
+ }
917
+ if (status.sourceFreshness?.status === "not_checked") {
918
+ signals.push(
919
+ consumerGateIssue(
920
+ "local_overlay_source_not_checked",
921
+ "signal",
922
+ "source_freshness",
923
+ "local overlay source was not checked by this packaged CLI invocation",
924
+ ),
925
+ );
926
+ }
927
+ if (
928
+ status.packageVersion.status !== undefined &&
929
+ status.packageVersion.status !== "release_version_match"
930
+ ) {
931
+ hardFailures.push(
932
+ consumerGateIssue(
933
+ "local_overlay_release_version_mismatch",
934
+ "hard",
935
+ "release_identity",
936
+ `local consumer overlay version is ${status.packageVersion.status}`,
937
+ { packageVersionStatus: status.packageVersion.status },
938
+ ),
939
+ );
940
+ }
941
+ if (status.npmLatest.status === "not_checked") {
942
+ signals.push(
943
+ consumerGateIssue(
944
+ "npm_latest_not_checked",
945
+ "signal",
946
+ "registry_observation",
947
+ "npm latest was not checked; pass --check-npm to include registry observation",
948
+ ),
949
+ );
950
+ }
951
+ if (status.npmLatest.status === "checked") {
952
+ for (const [packageName, row] of Object.entries(status.npmLatest.packages ?? {})) {
953
+ if (row.status !== "resolved") {
954
+ signals.push(
955
+ consumerGateIssue(
956
+ "npm_latest_unresolved",
957
+ "signal",
958
+ "registry_observation",
959
+ `${packageName} npm latest could not be resolved`,
960
+ { packageName, status: row.status },
961
+ ),
962
+ );
963
+ }
964
+ }
965
+ }
966
+ return {
967
+ status: hardFailures.length === 0 ? "pass" : "fail",
968
+ hardFailures,
969
+ signals,
970
+ };
971
+ };
972
+
973
+ const withConsumerGate = (status) => ({
974
+ ...status,
975
+ gate: consumerOverlayGate(status),
976
+ });
977
+
978
+ const consumerStatusDataForRoot = (consumerRoot, options = {}) => {
979
+ const metadata = packageMetadata(options.packageRoot);
980
+ const markerPath = localConsumerMarkerPath(consumerRoot);
981
+ const currentSource =
982
+ typeof options.sourceRoot === "string" ? sourceIdentityFor(options.sourceRoot) : undefined;
983
+ if (!fs.existsSync(markerPath)) {
984
+ return withConsumerGate({
985
+ schemaVersion: 1,
986
+ consumerRoot,
987
+ markerPath: path.relative(consumerRoot, markerPath).split(path.sep).join("/"),
988
+ truthMode: truthModeFor(undefined),
989
+ localOverlay: { status: "missing" },
990
+ packageIntegrity: packageIntegrityFor(undefined, []),
991
+ sourceFreshness: { status: "not_applicable", checked: false, gate: "pass" },
992
+ packageVersion: { release: metadata.packageVersion },
993
+ npmLatest:
994
+ options.checkNpm === true ? npmLatestFor([], options.registry) : npmLatestNotChecked(),
995
+ });
996
+ }
997
+ const marker = readJson(markerPath);
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
+ );
1012
+ const sourceStatus = overlaySourceStatus(marker, currentSource);
1013
+ const packageIntegrity = packageIntegrityFor(marker, packages);
1014
+ const sourceFreshness = sourceFreshnessFor(marker, currentSource);
1015
+ return withConsumerGate({
1016
+ schemaVersion: 1,
1017
+ consumerRoot,
1018
+ markerPath: path.relative(consumerRoot, markerPath).split(path.sep).join("/"),
1019
+ truthMode: truthModeFor(marker),
1020
+ localOverlay: {
1021
+ status: packages.every((pkg) => pkg.installed) ? "installed" : "partial",
1022
+ sourceStatus,
1023
+ generatedBy: marker.generatedBy,
1024
+ installedAt: marker.installedAt,
1025
+ artifact: marker.artifact ?? { kind: "legacy-local-overlay" },
1026
+ packages,
1027
+ },
1028
+ packageIntegrity,
1029
+ exportEquivalence,
1030
+ sourceFreshness,
1031
+ source: {
1032
+ ...(currentSource === undefined ? {} : { current: currentSource }),
1033
+ overlay: marker.source,
1034
+ },
1035
+ packageVersion: {
1036
+ release: metadata.packageVersion,
1037
+ overlay: marker.packageVersion,
1038
+ status: packageVersionStatus(marker, metadata.packageVersion),
1039
+ },
1040
+ npmLatest:
1041
+ options.checkNpm === true
1042
+ ? npmLatestFor(
1043
+ packages.map((pkg) => pkg.packageName),
1044
+ options.registry,
1045
+ )
1046
+ : npmLatestNotChecked(),
1047
+ });
1048
+ };
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
+
1097
+ const printConsumerStatus = (status) => {
1098
+ console.log(`consumer: ${status.consumerRoot}`);
1099
+ console.log(`marker: ${status.markerPath}`);
1100
+ console.log(`truth mode: ${status.truthMode}`);
1101
+ console.log(`local overlay: ${status.localOverlay.status}`);
1102
+ console.log(`package integrity: ${status.packageIntegrity.status}`);
1103
+ if (status.exportEquivalence !== undefined) {
1104
+ console.log(`export equivalence: ${status.exportEquivalence.status}`);
1105
+ }
1106
+ if (status.sourceFreshness !== undefined) {
1107
+ console.log(`source freshness: ${status.sourceFreshness.status}`);
1108
+ }
1109
+ if (status.localOverlay.sourceStatus !== undefined) {
1110
+ console.log(`source status: ${status.localOverlay.sourceStatus}`);
1111
+ }
1112
+ console.log(
1113
+ `package version: overlay=${status.packageVersion.overlay ?? "none"} release=${status.packageVersion.release} status=${status.packageVersion.status ?? "none"}`,
1114
+ );
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
+ }
1124
+ console.log(`gate: ${status.gate.status}`);
1125
+ for (const pkg of status.localOverlay.packages ?? []) {
1126
+ console.log(
1127
+ `package ${pkg.packageName}: target=${pkg.targetStatus} tarball=${pkg.tarballStatus} sha256=${pkg.sha256}`,
1128
+ );
1129
+ }
1130
+ for (const failure of status.gate.hardFailures) {
1131
+ console.log(`failure ${failure.code}: ${failure.message}`);
1132
+ }
1133
+ for (const signal of status.gate.signals) {
1134
+ console.log(`signal ${signal.code}: ${signal.message}`);
1135
+ }
1136
+ };
1137
+
1138
+ const installManifestPathForArgs = async (args, context) => {
1139
+ if (typeof args["from-manifest"] === "string") {
1140
+ return path.resolve(process.cwd(), args["from-manifest"]);
1141
+ }
1142
+ if (boolArg(args, "skip-pack")) {
1143
+ if (typeof context.defaultInstallManifestPath !== "string") {
1144
+ fail(
1145
+ "agentos consumer install --skip-pack requires --from-manifest outside a source checkout",
1146
+ );
1147
+ }
1148
+ return context.defaultInstallManifestPath;
1149
+ }
1150
+ if (typeof context.produceInstallManifest !== "function") {
1151
+ fail("agentos consumer install requires --from-manifest outside an agentOS source checkout");
1152
+ }
1153
+ return await context.produceInstallManifest();
1154
+ };
1155
+
1156
+ export const installConsumer = async (rawArgs, context = {}) => {
1157
+ const args = parseArgs(rawArgs);
1158
+ const consumerRoot = resolveConsumerRoot(positionalArgs(args)[0]);
1159
+ const manifestPath = await installManifestPathForArgs(args, context);
1160
+ const workspaceLayout = consumerWorkspaceLayout(consumerRoot);
1161
+ if (workspaceLayout.status === "invalid") {
1162
+ fail(`${consumerRoot}: ${workspaceLayout.error}`);
1163
+ }
1164
+ const snapshot = snapshotFiles(consumerWorkspaceManifestFiles(workspaceLayout));
1165
+ const { manifest } = readInstallManifest(manifestPath);
1166
+ const entries = tarballPackageEntries(manifest);
1167
+ nodeModulesRoot(consumerRoot, { install: !boolArg(args, "no-install") });
1168
+ const source =
1169
+ typeof context.sourceRoot === "string"
1170
+ ? sourceIdentityFor(context.sourceRoot)
1171
+ : (manifest.source ?? undefined);
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
+ }
1196
+ assertSnapshotUnchanged(snapshot, "agentos consumer install");
1197
+ const status = consumerStatusData(consumerRoot, { sourceRoot: context.sourceRoot });
1198
+ if (boolArg(args, "json")) {
1199
+ console.log(JSON.stringify(status, null, 2));
1200
+ } else {
1201
+ console.log(
1202
+ `installed ${entries.length} local agentOS packages into ${workspaceLayout.roots.length} consumer root(s)`,
1203
+ );
1204
+ console.log(
1205
+ `wrote ${path.relative(consumerRoot, localConsumerMarkerPath(consumerRoot)).split(path.sep).join("/")}`,
1206
+ );
1207
+ printConsumerStatus(status);
1208
+ }
1209
+ };
1210
+
1211
+ export const consumerStatus = (rawArgs, context = {}) => {
1212
+ const args = parseArgs(rawArgs);
1213
+ const consumerRoot = resolveConsumerRoot(positionalArgs(args)[0]);
1214
+ const status = consumerStatusData(consumerRoot, {
1215
+ packageRoot: context.packageRoot,
1216
+ sourceRoot: context.sourceRoot,
1217
+ checkNpm: boolArg(args, "check-npm"),
1218
+ registry: args.registry,
1219
+ });
1220
+ if (boolArg(args, "json")) {
1221
+ console.log(JSON.stringify(status, null, 2));
1222
+ return;
1223
+ }
1224
+ printConsumerStatus(status);
1225
+ };
1226
+
1227
+ export const consumerCheck = (rawArgs, context = {}) => {
1228
+ const args = parseArgs(rawArgs);
1229
+ const consumerRoot = resolveConsumerRoot(positionalArgs(args)[0]);
1230
+ const status = consumerStatusData(consumerRoot, {
1231
+ packageRoot: context.packageRoot,
1232
+ sourceRoot: context.sourceRoot,
1233
+ checkNpm: boolArg(args, "check-npm"),
1234
+ registry: args.registry,
1235
+ });
1236
+ if (boolArg(args, "json")) {
1237
+ console.log(JSON.stringify(status, null, 2));
1238
+ } else {
1239
+ printConsumerStatus(status);
1240
+ }
1241
+ if (status.gate.status !== "pass") {
1242
+ process.exitCode = 1;
1243
+ }
1244
+ };
1245
+
1246
+ export const restoreConsumer = (rawArgs) => {
1247
+ const args = parseArgs(rawArgs);
1248
+ const consumerRoot = resolveConsumerRoot(positionalArgs(args)[0]);
1249
+ const nodeModules = nodeModulesRoot(consumerRoot);
1250
+ const markerPath = localConsumerMarkerPath(consumerRoot);
1251
+ if (!fs.existsSync(markerPath)) {
1252
+ fail(`${markerPath}: no local agentOS overlay marker`);
1253
+ }
1254
+ const marker = readJson(markerPath);
1255
+ const packageNames = Object.keys(marker.packages ?? {}).sort((left, right) =>
1256
+ left.localeCompare(right),
1257
+ );
1258
+ if (packageNames.length === 0) fail(`${markerPath}: marker does not list packages`);
1259
+ const snapshot = snapshotFiles(consumerManifestFiles(consumerRoot));
1260
+ for (const packageName of packageNames) {
1261
+ fs.rmSync(packageTargetDir(nodeModules, packageName), { recursive: true, force: true });
1262
+ }
1263
+ fs.rmSync(markerPath, { force: true });
1264
+ if (!boolArg(args, "no-install")) {
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
+ });
1273
+ }
1274
+ assertSnapshotUnchanged(snapshot, "agentos consumer restore");
1275
+ const result = { schemaVersion: 1, restoredPackages: packageNames };
1276
+ if (boolArg(args, "json")) {
1277
+ console.log(JSON.stringify(result, null, 2));
1278
+ } else {
1279
+ console.log(`restored ${packageNames.length} local agentOS package overlays`);
1280
+ }
1281
+ };