loadout-ai 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/LICENSE +21 -0
  2. package/MASTER_PLAN.md +1711 -0
  3. package/README.md +453 -0
  4. package/SECURITY.md +18 -0
  5. package/SIMPLE_PLAN.md +44 -0
  6. package/catalog/discovered.json +10469 -0
  7. package/catalog/packages.json +1150 -0
  8. package/dashboard/app.js +607 -0
  9. package/dashboard/index.html +249 -0
  10. package/dashboard/styles.css +384 -0
  11. package/dist/src/cli.js +2735 -0
  12. package/dist/src/core/active-policy.js +233 -0
  13. package/dist/src/core/active-set.js +382 -0
  14. package/dist/src/core/adapters.js +254 -0
  15. package/dist/src/core/adopt.js +92 -0
  16. package/dist/src/core/agent-health-score.js +475 -0
  17. package/dist/src/core/agent-inspection.js +152 -0
  18. package/dist/src/core/agent-versions.js +131 -0
  19. package/dist/src/core/api.js +90 -0
  20. package/dist/src/core/atomic-file.js +22 -0
  21. package/dist/src/core/audit.js +174 -0
  22. package/dist/src/core/benchmark-campaign.js +496 -0
  23. package/dist/src/core/benchmark-evidence.js +458 -0
  24. package/dist/src/core/benchmark-fixtures.js +1157 -0
  25. package/dist/src/core/benchmark-runner.js +301 -0
  26. package/dist/src/core/benchmark-trust.js +795 -0
  27. package/dist/src/core/canary.js +77 -0
  28. package/dist/src/core/candidate-intelligence.js +661 -0
  29. package/dist/src/core/catalog-coverage.js +85 -0
  30. package/dist/src/core/catalog-install.js +186 -0
  31. package/dist/src/core/catalog-release.js +286 -0
  32. package/dist/src/core/catalog.js +327 -0
  33. package/dist/src/core/codex-mcp.js +122 -0
  34. package/dist/src/core/community.js +86 -0
  35. package/dist/src/core/compatibility-intelligence.js +465 -0
  36. package/dist/src/core/completion.js +160 -0
  37. package/dist/src/core/components.js +138 -0
  38. package/dist/src/core/conversion.js +56 -0
  39. package/dist/src/core/credentials.js +377 -0
  40. package/dist/src/core/demo.js +136 -0
  41. package/dist/src/core/diff.js +83 -0
  42. package/dist/src/core/discovery-connector.js +165 -0
  43. package/dist/src/core/doctor.js +95 -0
  44. package/dist/src/core/ecosystem-import.js +1072 -0
  45. package/dist/src/core/evaluate.js +72 -0
  46. package/dist/src/core/file-lock.js +140 -0
  47. package/dist/src/core/freshness-alerts.js +230 -0
  48. package/dist/src/core/github-discovery.js +97 -0
  49. package/dist/src/core/github.js +148 -0
  50. package/dist/src/core/head-to-head.js +437 -0
  51. package/dist/src/core/health-score-evidence.js +169 -0
  52. package/dist/src/core/health.js +129 -0
  53. package/dist/src/core/improve.js +223 -0
  54. package/dist/src/core/install.js +233 -0
  55. package/dist/src/core/intelligence-feed-build.js +59 -0
  56. package/dist/src/core/intelligence-feed.js +353 -0
  57. package/dist/src/core/loadout-badge.js +55 -0
  58. package/dist/src/core/loadout-card.js +87 -0
  59. package/dist/src/core/manifest.js +307 -0
  60. package/dist/src/core/mcp-recipes.js +419 -0
  61. package/dist/src/core/mcp-registry-discovery.js +455 -0
  62. package/dist/src/core/mcp.js +287 -0
  63. package/dist/src/core/model-config.js +102 -0
  64. package/dist/src/core/observations.js +87 -0
  65. package/dist/src/core/outcomes.js +164 -0
  66. package/dist/src/core/package.js +225 -0
  67. package/dist/src/core/paths.js +214 -0
  68. package/dist/src/core/portable.js +130 -0
  69. package/dist/src/core/private-discovery.js +45 -0
  70. package/dist/src/core/profiles.js +255 -0
  71. package/dist/src/core/provenance.js +278 -0
  72. package/dist/src/core/ranking.js +104 -0
  73. package/dist/src/core/recommend.js +181 -0
  74. package/dist/src/core/registry-api.js +97 -0
  75. package/dist/src/core/registry.js +316 -0
  76. package/dist/src/core/release-claims.js +175 -0
  77. package/dist/src/core/remove.js +122 -0
  78. package/dist/src/core/review-queue.js +200 -0
  79. package/dist/src/core/runtime-tool-recipe.js +315 -0
  80. package/dist/src/core/runtime-tools.js +453 -0
  81. package/dist/src/core/safety.js +234 -0
  82. package/dist/src/core/sandbox.js +78 -0
  83. package/dist/src/core/scheduler.js +264 -0
  84. package/dist/src/core/share-report.js +85 -0
  85. package/dist/src/core/signing.js +115 -0
  86. package/dist/src/core/skill-compare.js +239 -0
  87. package/dist/src/core/skill-inventory.js +193 -0
  88. package/dist/src/core/skill-security.js +340 -0
  89. package/dist/src/core/skills-sh-discovery.js +336 -0
  90. package/dist/src/core/skills.js +208 -0
  91. package/dist/src/core/snapshot.js +210 -0
  92. package/dist/src/core/source.js +305 -0
  93. package/dist/src/core/state.js +300 -0
  94. package/dist/src/core/sync.js +348 -0
  95. package/dist/src/core/terminal.js +53 -0
  96. package/dist/src/core/transaction.js +211 -0
  97. package/dist/src/core/update-watch.js +39 -0
  98. package/dist/src/core/update.js +216 -0
  99. package/dist/src/core/upgrade.js +207 -0
  100. package/dist/src/dashboard.js +418 -0
  101. package/dist/src/shared/schemas.js +336 -0
  102. package/dist/src/shared/types.js +1 -0
  103. package/docs/ACTIVE_SET.md +53 -0
  104. package/docs/ACTIVE_SET_POLICY.md +45 -0
  105. package/docs/CANDIDATE_INTELLIGENCE.md +139 -0
  106. package/docs/CATALOG.md +78 -0
  107. package/docs/CATALOG_POLICY.md +41 -0
  108. package/docs/COMMUNITY_DISCOVERY.md +14 -0
  109. package/docs/COMPATIBILITY_POLICY.md +22 -0
  110. package/docs/CONVERSION_AND_SANDBOX.md +27 -0
  111. package/docs/CREDENTIAL_AND_UPDATE_POLICY.md +31 -0
  112. package/docs/DISCOVERED.md +280 -0
  113. package/docs/EVALUATION_PROTOCOL.md +40 -0
  114. package/docs/EVALUATION_PROTOCOL_V1.md +300 -0
  115. package/docs/FEATURE_TEST_MATRIX.md +802 -0
  116. package/docs/GITHUB_AUTHORIZATION.md +38 -0
  117. package/docs/HEAD_TO_HEAD_EVALUATION.md +79 -0
  118. package/docs/PROVENANCE_AND_COMPARISON.md +72 -0
  119. package/docs/PROVIDER_CONFIGURATION.md +45 -0
  120. package/docs/RELEASE_REVIEW.md +117 -0
  121. package/docs/SAFE_UPDATE_DEMO.md +25 -0
  122. package/docs/SCHEMA_DECISIONS.md +25 -0
  123. package/docs/TEAM_POLICY.md +18 -0
  124. package/docs/TESTING.md +190 -0
  125. package/package.json +82 -0
@@ -0,0 +1,315 @@
1
+ import { posix, win32 } from "node:path";
2
+ import { z } from "zod";
3
+ const agentIds = [
4
+ "claude-code",
5
+ "codex",
6
+ "cursor",
7
+ "gemini-cli",
8
+ "opencode",
9
+ "hermes",
10
+ "windsurf",
11
+ "cline",
12
+ "github-copilot",
13
+ "roo-code",
14
+ "kiro-cli",
15
+ "junie",
16
+ ];
17
+ const inheritedEnvironmentNames = [
18
+ "PATH",
19
+ "SystemRoot",
20
+ "WINDIR",
21
+ "TMPDIR",
22
+ "TMP",
23
+ "TEMP",
24
+ ];
25
+ const fixedEnvironmentNames = [
26
+ "HOME",
27
+ "USERPROFILE",
28
+ "UV_TOOL_DIR",
29
+ "UV_TOOL_BIN_DIR",
30
+ "UV_CACHE_DIR",
31
+ ];
32
+ const recipePlatformSchema = z.enum(["darwin", "linux", "win32"]);
33
+ const agentIdSchema = z.enum(agentIds);
34
+ const digestSchema = z.string().regex(/^[a-f0-9]{64}$/);
35
+ const commitSchema = z.string().regex(/^[a-f0-9]{40}$/);
36
+ const safeIdSchema = z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/);
37
+ const nonControlTextSchema = z
38
+ .string()
39
+ .min(1)
40
+ .refine((value) => !/[\0\r\n]/u.test(value), "control characters are forbidden");
41
+ const pathSegmentSchema = z
42
+ .string()
43
+ .min(1)
44
+ .refine((value) => value !== "." &&
45
+ value !== ".." &&
46
+ !value.includes("/") &&
47
+ !value.includes("\\") &&
48
+ !value.includes(":") &&
49
+ !/[. ]$/u.test(value) &&
50
+ !/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/iu.test(value) &&
51
+ !/[\0\r\n]/u.test(value), "paths must be safe relative segments");
52
+ const environmentValueSchema = z
53
+ .object({
54
+ root: z.enum(["userHome", "runtimeRoot"]),
55
+ path: z.array(pathSegmentSchema).max(8),
56
+ })
57
+ .strict();
58
+ const artifactSchema = z
59
+ .object({
60
+ id: safeIdSchema,
61
+ kind: z.enum(["python-wheel"]),
62
+ packageName: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/),
63
+ url: z.url().refine((url) => url.startsWith("https://"), "HTTPS required"),
64
+ sha256: digestSchema,
65
+ })
66
+ .strict();
67
+ const commandExecutableSchema = nonControlTextSchema.refine((value) => value === "{runtimeBinary}" ||
68
+ (/^[A-Za-z0-9][A-Za-z0-9._-]*$/u.test(value) &&
69
+ !new Set([
70
+ "sh",
71
+ "bash",
72
+ "zsh",
73
+ "fish",
74
+ "cmd",
75
+ "cmd.exe",
76
+ "powershell",
77
+ "powershell.exe",
78
+ "pwsh",
79
+ ]).has(value.toLowerCase())), "commands must name a non-shell executable directly");
80
+ const hostToolSchema = z
81
+ .object({
82
+ executable: commandExecutableSchema.refine((value) => value !== "{runtimeBinary}", "host tools must name an external executable"),
83
+ versionPolicy: z.literal("unversioned-host-dependency"),
84
+ purpose: nonControlTextSchema,
85
+ })
86
+ .strict();
87
+ const commandArgumentSchema = nonControlTextSchema.refine((value) => !value.includes("{") ||
88
+ value === "{artifactRequirement}" ||
89
+ value === "{version}", "unknown command template variable");
90
+ const commandSchema = z
91
+ .object({
92
+ executable: commandExecutableSchema,
93
+ args: z.array(commandArgumentSchema).max(64),
94
+ purpose: nonControlTextSchema,
95
+ })
96
+ .strict();
97
+ const registrationSchema = z
98
+ .object({
99
+ args: z.array(commandArgumentSchema).max(32),
100
+ purpose: nonControlTextSchema,
101
+ })
102
+ .strict();
103
+ const targetSchema = z
104
+ .object({
105
+ path: z.array(pathSegmentSchema).min(1).max(16),
106
+ })
107
+ .strict();
108
+ const healthCheckSchema = commandSchema
109
+ .extend({
110
+ stdoutIncludes: z.enum(["{version}"]),
111
+ })
112
+ .strict();
113
+ const generatedFilesSchema = z
114
+ .object({
115
+ requiredRelativePaths: z.array(pathSegmentSchema).min(1),
116
+ rejectSymlinks: z.literal(true),
117
+ textRewrites: z.array(z
118
+ .object({
119
+ fileExtension: z.string().regex(/^\.[A-Za-z0-9]+$/),
120
+ from: nonControlTextSchema,
121
+ to: nonControlTextSchema.refine((value) => !/[{}]/u.test(value.replaceAll("{artifactRequirement}", "")), "unknown rewrite template variable"),
122
+ mustEliminate: z.boolean(),
123
+ })
124
+ .strict()),
125
+ })
126
+ .strict();
127
+ export const runtimeToolRecipeSchema = z
128
+ .object({
129
+ schemaVersion: z.literal(1),
130
+ kind: z.literal("loadout.reviewed-runtime-recipe"),
131
+ id: safeIdSchema,
132
+ displayName: nonControlTextSchema,
133
+ version: z.string().regex(/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/),
134
+ source: z
135
+ .url()
136
+ .refine((url) => url.startsWith("https://"), "HTTPS required"),
137
+ sourceRepository: z
138
+ .url()
139
+ .refine((url) => url.startsWith("https://"), "HTTPS required")
140
+ .refine((url) => !url.endsWith("/"), "omit trailing repository slash"),
141
+ reviewedCommit: commitSchema,
142
+ artifactUrl: z
143
+ .url()
144
+ .refine((url) => url.startsWith("https://"), "HTTPS required"),
145
+ artifactSha256: digestSchema,
146
+ artifacts: z.array(artifactSchema).min(1).max(64),
147
+ excludeNewer: z.iso.datetime({ offset: true }),
148
+ license: z.string().regex(/^[A-Za-z0-9-.+]+$/),
149
+ trust: z
150
+ .object({
151
+ reviewType: z.literal("manual-source-and-artifact-review"),
152
+ reviewedAt: z.iso.datetime({ offset: true }),
153
+ reviewer: nonControlTextSchema,
154
+ provenance: z.literal("direct-upstream-and-package-index"),
155
+ })
156
+ .strict(),
157
+ dependencies: z
158
+ .object({
159
+ resolution: z.enum(["fully-locked", "cutoff-bounded"]),
160
+ excludeNewer: z.iso.datetime({ offset: true }).optional(),
161
+ lockArtifactSha256: digestSchema.optional(),
162
+ hostTools: z.array(hostToolSchema).min(1),
163
+ disclosure: nonControlTextSchema,
164
+ })
165
+ .strict(),
166
+ permissions: z.array(nonControlTextSchema).min(1),
167
+ operatingSystems: z.array(recipePlatformSchema).min(1),
168
+ environment: z
169
+ .object({
170
+ inherit: z.array(z.enum(inheritedEnvironmentNames)),
171
+ fixed: z.record(z.enum(fixedEnvironmentNames), environmentValueSchema),
172
+ })
173
+ .strict(),
174
+ runtime: z
175
+ .object({
176
+ root: z.tuple([z.literal("runtime"), z.literal("{recipeId}")]),
177
+ binaryPaths: z
178
+ .object({
179
+ darwin: z.array(pathSegmentSchema).min(1),
180
+ linux: z.array(pathSegmentSchema).min(1),
181
+ win32: z.array(pathSegmentSchema).min(1),
182
+ })
183
+ .strict(),
184
+ })
185
+ .strict(),
186
+ commands: z
187
+ .object({
188
+ install: z.array(commandSchema).min(1),
189
+ register: z.partialRecord(agentIdSchema, registrationSchema),
190
+ })
191
+ .strict(),
192
+ healthChecks: z.array(healthCheckSchema).min(1),
193
+ targets: z.partialRecord(agentIdSchema, targetSchema),
194
+ timeouts: z
195
+ .object({
196
+ commandMs: z.number().int().min(1_000).max(900_000),
197
+ })
198
+ .strict(),
199
+ snapshotRoots: z
200
+ .array(z.enum(["{runtimeRoot}", "{agentTargets}"]))
201
+ .refine((values) => values.includes("{runtimeRoot}") && values.includes("{agentTargets}"), "runtime and agent targets must both be snapshotted"),
202
+ removal: z
203
+ .object({
204
+ strategy: z.literal("restore-preinstall-snapshot"),
205
+ runtimeRoot: z.literal("restore-preinstall-state"),
206
+ })
207
+ .strict(),
208
+ generatedFiles: generatedFilesSchema,
209
+ guarantees: z.array(nonControlTextSchema).min(1),
210
+ })
211
+ .strict()
212
+ .superRefine((recipe, context) => {
213
+ const artifact = recipe.artifacts[0];
214
+ if (artifact.url !== recipe.artifactUrl)
215
+ context.addIssue({
216
+ code: "custom",
217
+ path: ["artifactUrl"],
218
+ message: "top-level artifact URL must match artifacts[0]",
219
+ });
220
+ if (artifact.sha256 !== recipe.artifactSha256)
221
+ context.addIssue({
222
+ code: "custom",
223
+ path: ["artifactSha256"],
224
+ message: "top-level artifact digest must match artifacts[0]",
225
+ });
226
+ if (!recipe.source.includes(recipe.reviewedCommit))
227
+ context.addIssue({
228
+ code: "custom",
229
+ path: ["source"],
230
+ message: "reviewed source URL must include the exact commit",
231
+ });
232
+ if (!recipe.source.startsWith(`${recipe.sourceRepository}/`))
233
+ context.addIssue({
234
+ code: "custom",
235
+ path: ["sourceRepository"],
236
+ message: "reviewed source must belong to the declared repository",
237
+ });
238
+ if (new Set(recipe.artifacts.map((item) => item.id)).size !==
239
+ recipe.artifacts.length)
240
+ context.addIssue({
241
+ code: "custom",
242
+ path: ["artifacts"],
243
+ message: "artifact ids must be unique",
244
+ });
245
+ if (!recipe.commands.install.some((command) => command.args.includes("{artifactRequirement}")))
246
+ context.addIssue({
247
+ code: "custom",
248
+ path: ["commands", "install"],
249
+ message: "an install command must consume the pinned artifact",
250
+ });
251
+ const hostExecutables = new Set(recipe.dependencies.hostTools.map((tool) => tool.executable));
252
+ for (const [index, command] of recipe.commands.install.entries()) {
253
+ if (command.executable !== "{runtimeBinary}" &&
254
+ !hostExecutables.has(command.executable))
255
+ context.addIssue({
256
+ code: "custom",
257
+ path: ["commands", "install", index, "executable"],
258
+ message: "install executable must be a declared host dependency",
259
+ });
260
+ }
261
+ if (recipe.dependencies.resolution === "cutoff-bounded" &&
262
+ recipe.dependencies.excludeNewer !== recipe.excludeNewer)
263
+ context.addIssue({
264
+ code: "custom",
265
+ path: ["dependencies", "excludeNewer"],
266
+ message: "dependency cutoff must match the installer cutoff",
267
+ });
268
+ if (recipe.dependencies.resolution === "fully-locked" &&
269
+ !recipe.dependencies.lockArtifactSha256)
270
+ context.addIssue({
271
+ code: "custom",
272
+ path: ["dependencies", "lockArtifactSha256"],
273
+ message: "fully locked dependencies require a lock artifact digest",
274
+ });
275
+ if (recipe.dependencies.lockArtifactSha256 &&
276
+ !recipe.artifacts.some((artifact) => artifact.sha256 === recipe.dependencies.lockArtifactSha256))
277
+ context.addIssue({
278
+ code: "custom",
279
+ path: ["dependencies", "lockArtifactSha256"],
280
+ message: "dependency lock digest must identify a declared artifact",
281
+ });
282
+ for (const agent of Object.keys(recipe.commands.register)) {
283
+ if (!(agent in recipe.targets))
284
+ context.addIssue({
285
+ code: "custom",
286
+ path: ["targets", agent],
287
+ message: "every registration command requires a target",
288
+ });
289
+ }
290
+ for (const agent of Object.keys(recipe.targets)) {
291
+ if (!(agent in recipe.commands.register))
292
+ context.addIssue({
293
+ code: "custom",
294
+ path: ["commands", "register", agent],
295
+ message: "every target requires a registration command",
296
+ });
297
+ }
298
+ });
299
+ export function parseRuntimeToolRecipe(value) {
300
+ return runtimeToolRecipeSchema.parse(value);
301
+ }
302
+ export function runtimeArtifactRequirement(recipe) {
303
+ const artifact = recipe.artifacts[0];
304
+ return `${artifact.packageName} @ ${artifact.url}#sha256=${artifact.sha256}`;
305
+ }
306
+ export function renderRuntimeRecipeValue(value, recipe) {
307
+ return value
308
+ .replaceAll("{artifactRequirement}", runtimeArtifactRequirement(recipe))
309
+ .replaceAll("{version}", recipe.version);
310
+ }
311
+ /** Resolve reviewed path segments without ever accepting a recipe-owned root. */
312
+ export function resolveRuntimeRecipePath(root, segments, platform) {
313
+ const pathApi = platform === "win32" ? win32 : posix;
314
+ return pathApi.join(root, ...segments);
315
+ }