nexus-agents 2.56.0 → 2.57.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 (101) hide show
  1. package/dist/{adaptive-memory-SUNFPY2W.js → adaptive-memory-TSZEJUJC.js} +3 -3
  2. package/dist/{chunk-4FVISCDB.js → chunk-6QU4DJYW.js} +2 -2
  3. package/dist/{chunk-NYNBDP7M.js → chunk-7Y36JLES.js} +2 -2
  4. package/dist/{chunk-FWSTXEG2.js → chunk-AFSIP6JH.js} +99 -23
  5. package/dist/chunk-AFSIP6JH.js.map +1 -0
  6. package/dist/{chunk-BPMQRYGU.js → chunk-BOZ26RIB.js} +283 -79
  7. package/dist/chunk-BOZ26RIB.js.map +1 -0
  8. package/dist/{chunk-VKZKC4SM.js → chunk-EDGG3RQE.js} +2 -2
  9. package/dist/{chunk-2SPRLBOS.js → chunk-EZXOJZYE.js} +2 -2
  10. package/dist/{chunk-W5AJRK4U.js → chunk-GMOGKX4E.js} +2 -2
  11. package/dist/chunk-H43PABG4.js +402 -0
  12. package/dist/chunk-H43PABG4.js.map +1 -0
  13. package/dist/{chunk-IOU7F5PH.js → chunk-K7EA5OV4.js} +2 -2
  14. package/dist/{chunk-KRIFBGWZ.js → chunk-PTGBJFSD.js} +3 -3
  15. package/dist/{chunk-EKYT4NMD.js → chunk-QGZBCD2A.js} +24 -9
  16. package/dist/chunk-QGZBCD2A.js.map +1 -0
  17. package/dist/{chunk-KGMC6F5D.js → chunk-QNYNQ257.js} +67 -61
  18. package/dist/chunk-QNYNQ257.js.map +1 -0
  19. package/dist/{chunk-UOUJZIKH.js → chunk-R2Y57PM3.js} +38 -15
  20. package/dist/chunk-R2Y57PM3.js.map +1 -0
  21. package/dist/{chunk-RSUCXOQM.js → chunk-SXWZS2V4.js} +26 -53
  22. package/dist/chunk-SXWZS2V4.js.map +1 -0
  23. package/dist/{chunk-PCMWLXT4.js → chunk-TC46TRLR.js} +2 -2
  24. package/dist/{chunk-3IDJSFWT.js → chunk-TFEFN37P.js} +2 -2
  25. package/dist/{chunk-Q5TKQWOI.js → chunk-V5CGWMYL.js} +2 -2
  26. package/dist/{chunk-TCQNNY4J.js → chunk-XEMRMZUN.js} +4 -4
  27. package/dist/{chunk-C3FGEDD7.js → chunk-Y7CD6AZW.js} +22 -406
  28. package/dist/chunk-Y7CD6AZW.js.map +1 -0
  29. package/dist/{chunk-R66AWJJ7.js → chunk-YU4NABXM.js} +2 -2
  30. package/dist/{chunk-EJLWDYK7.js → chunk-ZL3IBCH6.js} +658 -321
  31. package/dist/chunk-ZL3IBCH6.js.map +1 -0
  32. package/dist/{chunk-VLVHPC72.js → chunk-ZUWOFHNC.js} +6 -6
  33. package/dist/{cli-circuit-breaker-7MCMHSDY.js → cli-circuit-breaker-SL73NWX2.js} +4 -4
  34. package/dist/cli.d.ts +3 -1
  35. package/dist/cli.js +425 -341
  36. package/dist/cli.js.map +1 -1
  37. package/dist/{composite-router-LXFOSMSE.js → composite-router-A7URDW4X.js} +2 -2
  38. package/dist/{consensus-vote-G6H532ME.js → consensus-vote-CQ2JP6DC.js} +9 -8
  39. package/dist/{doctor-deep-RKMOZSIS.js → doctor-deep-VN6KMUCG.js} +3 -3
  40. package/dist/{expert-bridge-KHHE42JP.js → expert-bridge-LT7PKUPS.js} +3 -3
  41. package/dist/{factory-KRNR7XHD.js → factory-FA7WDPZW.js} +5 -5
  42. package/dist/{factory-JVN47MFN.js → factory-FZ2KSVYC.js} +4 -4
  43. package/dist/index.d.ts +324 -16
  44. package/dist/index.js +58 -43
  45. package/dist/index.js.map +1 -1
  46. package/dist/{issue-triage-O3C7P66H.js → issue-triage-YYTE6KTC.js} +4 -4
  47. package/dist/{mcp-config-QRERKGR4.js → mcp-config-34XMRM64.js} +3 -3
  48. package/dist/{mobimem-FAOAXV3B.js → mobimem-QDBP37H7.js} +2 -2
  49. package/dist/model-registry.generated.json +17033 -0
  50. package/dist/registry-command-S46JJ2SX.js +580 -0
  51. package/dist/registry-command-S46JJ2SX.js.map +1 -0
  52. package/dist/{repo-security-plan-AZ5HMGFC.js → repo-security-plan-C3LLE3Z7.js} +3 -3
  53. package/dist/{research-helpers-synthesize-E6WQLQKP.js → research-helpers-synthesize-NVQIWLQL.js} +3 -3
  54. package/dist/{routing-memory-MXF45FXT.js → routing-memory-W3YWMLJM.js} +2 -2
  55. package/dist/{session-memory-HL5XDBIL.js → session-memory-DWF5Z2LC.js} +3 -3
  56. package/dist/{setup-command-AV4MODEL.js → setup-command-6EJONTOU.js} +8 -7
  57. package/dist/{setup-config-PWK6WHMG.js → setup-config-JA5IX53Q.js} +3 -3
  58. package/dist/{setup-custom-api-XAWKRDWV.js → setup-custom-api-CSB26HWD.js} +7 -5
  59. package/dist/setup-custom-api-CSB26HWD.js.map +1 -0
  60. package/dist/{weather-report-XZ5CENHE.js → weather-report-YQSLX4MS.js} +2 -2
  61. package/package.json +1 -1
  62. package/dist/chunk-BPMQRYGU.js.map +0 -1
  63. package/dist/chunk-C3FGEDD7.js.map +0 -1
  64. package/dist/chunk-EJLWDYK7.js.map +0 -1
  65. package/dist/chunk-EKYT4NMD.js.map +0 -1
  66. package/dist/chunk-FWSTXEG2.js.map +0 -1
  67. package/dist/chunk-KGMC6F5D.js.map +0 -1
  68. package/dist/chunk-RSUCXOQM.js.map +0 -1
  69. package/dist/chunk-UOUJZIKH.js.map +0 -1
  70. package/dist/setup-custom-api-XAWKRDWV.js.map +0 -1
  71. /package/dist/{adaptive-memory-SUNFPY2W.js.map → adaptive-memory-TSZEJUJC.js.map} +0 -0
  72. /package/dist/{chunk-4FVISCDB.js.map → chunk-6QU4DJYW.js.map} +0 -0
  73. /package/dist/{chunk-NYNBDP7M.js.map → chunk-7Y36JLES.js.map} +0 -0
  74. /package/dist/{chunk-VKZKC4SM.js.map → chunk-EDGG3RQE.js.map} +0 -0
  75. /package/dist/{chunk-2SPRLBOS.js.map → chunk-EZXOJZYE.js.map} +0 -0
  76. /package/dist/{chunk-W5AJRK4U.js.map → chunk-GMOGKX4E.js.map} +0 -0
  77. /package/dist/{chunk-IOU7F5PH.js.map → chunk-K7EA5OV4.js.map} +0 -0
  78. /package/dist/{chunk-KRIFBGWZ.js.map → chunk-PTGBJFSD.js.map} +0 -0
  79. /package/dist/{chunk-PCMWLXT4.js.map → chunk-TC46TRLR.js.map} +0 -0
  80. /package/dist/{chunk-3IDJSFWT.js.map → chunk-TFEFN37P.js.map} +0 -0
  81. /package/dist/{chunk-Q5TKQWOI.js.map → chunk-V5CGWMYL.js.map} +0 -0
  82. /package/dist/{chunk-TCQNNY4J.js.map → chunk-XEMRMZUN.js.map} +0 -0
  83. /package/dist/{chunk-R66AWJJ7.js.map → chunk-YU4NABXM.js.map} +0 -0
  84. /package/dist/{chunk-VLVHPC72.js.map → chunk-ZUWOFHNC.js.map} +0 -0
  85. /package/dist/{cli-circuit-breaker-7MCMHSDY.js.map → cli-circuit-breaker-SL73NWX2.js.map} +0 -0
  86. /package/dist/{composite-router-LXFOSMSE.js.map → composite-router-A7URDW4X.js.map} +0 -0
  87. /package/dist/{consensus-vote-G6H532ME.js.map → consensus-vote-CQ2JP6DC.js.map} +0 -0
  88. /package/dist/{doctor-deep-RKMOZSIS.js.map → doctor-deep-VN6KMUCG.js.map} +0 -0
  89. /package/dist/{expert-bridge-KHHE42JP.js.map → expert-bridge-LT7PKUPS.js.map} +0 -0
  90. /package/dist/{factory-JVN47MFN.js.map → factory-FA7WDPZW.js.map} +0 -0
  91. /package/dist/{factory-KRNR7XHD.js.map → factory-FZ2KSVYC.js.map} +0 -0
  92. /package/dist/{issue-triage-O3C7P66H.js.map → issue-triage-YYTE6KTC.js.map} +0 -0
  93. /package/dist/{mcp-config-QRERKGR4.js.map → mcp-config-34XMRM64.js.map} +0 -0
  94. /package/dist/{mobimem-FAOAXV3B.js.map → mobimem-QDBP37H7.js.map} +0 -0
  95. /package/dist/{repo-security-plan-AZ5HMGFC.js.map → repo-security-plan-C3LLE3Z7.js.map} +0 -0
  96. /package/dist/{research-helpers-synthesize-E6WQLQKP.js.map → research-helpers-synthesize-NVQIWLQL.js.map} +0 -0
  97. /package/dist/{routing-memory-MXF45FXT.js.map → routing-memory-W3YWMLJM.js.map} +0 -0
  98. /package/dist/{session-memory-HL5XDBIL.js.map → session-memory-DWF5Z2LC.js.map} +0 -0
  99. /package/dist/{setup-command-AV4MODEL.js.map → setup-command-6EJONTOU.js.map} +0 -0
  100. /package/dist/{setup-config-PWK6WHMG.js.map → setup-config-JA5IX53Q.js.map} +0 -0
  101. /package/dist/{weather-report-XZ5CENHE.js.map → weather-report-YQSLX4MS.js.map} +0 -0
@@ -0,0 +1,580 @@
1
+ import {
2
+ DEFAULT_MODEL_CAPABILITIES,
3
+ ModelCapabilitySchema,
4
+ createLogger,
5
+ getModelCapabilities
6
+ } from "./chunk-R2Y57PM3.js";
7
+ import "./chunk-CLYZ7FWP.js";
8
+ import "./chunk-UP2VWCW5.js";
9
+
10
+ // src/cli/registry-command.ts
11
+ import { createHash } from "crypto";
12
+ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
13
+ import { dirname as dirname2, join as join3 } from "path";
14
+ import { homedir as homedir2 } from "os";
15
+
16
+ // src/config/capability-discovery.ts
17
+ import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
18
+ import { fileURLToPath } from "url";
19
+ import { dirname, join as join2 } from "path";
20
+ import { z } from "zod";
21
+
22
+ // src/config/capability-overlay.ts
23
+ import { existsSync, readFileSync, statSync } from "fs";
24
+ import { homedir } from "os";
25
+ import { join } from "path";
26
+ import { parse as parseYaml } from "yaml";
27
+ var OVERLAY_ENV_VAR = "NEXUS_MODEL_REGISTRY_OVERLAY";
28
+ var OVERLAY_MAX_BYTES = 1 * 1024 * 1024;
29
+ function defaultOverlayPath() {
30
+ return join(homedir(), ".nexus-agents", "models.yaml");
31
+ }
32
+ function resolveOverlayPath(env = process.env) {
33
+ const override = env[OVERLAY_ENV_VAR];
34
+ if (override !== void 0 && override !== "") return override;
35
+ return defaultOverlayPath();
36
+ }
37
+ function loadCapabilityOverlay(pathOrEnv, logger) {
38
+ const log = logger ?? createLogger({ component: "capability-overlay" });
39
+ const path = resolvePath(pathOrEnv);
40
+ if (!existsSync(path)) {
41
+ return { entries: [], rejections: [], path, status: "missing" };
42
+ }
43
+ const sizeStatus = checkSize(path, log);
44
+ if (sizeStatus !== void 0) return sizeStatus;
45
+ const bodyResult = readBody(path, log);
46
+ if ("result" in bodyResult) return bodyResult.result;
47
+ const body = bodyResult.body;
48
+ if (body === "") {
49
+ log.info("Model registry overlay file is empty", { path });
50
+ return { entries: [], rejections: [], path, status: "empty" };
51
+ }
52
+ const parseResult = parseBody(body, path, log);
53
+ if ("result" in parseResult) return parseResult.result;
54
+ const parsed = parseResult.parsed;
55
+ const rawEntries = extractEntries(parsed);
56
+ if (rawEntries === void 0) {
57
+ log.warn("Model registry overlay has unrecognized shape; treated as empty", {
58
+ path
59
+ });
60
+ return {
61
+ entries: [],
62
+ rejections: [{ index: -1, reason: "Expected array or { models: [...] }" }],
63
+ path,
64
+ status: "malformed"
65
+ };
66
+ }
67
+ const { entries, rejections } = validateEntries(rawEntries, log, path);
68
+ return { entries, rejections, path, status: "loaded" };
69
+ }
70
+ function resolvePath(pathOrEnv) {
71
+ if (typeof pathOrEnv === "string") return pathOrEnv;
72
+ if (pathOrEnv !== void 0) return resolveOverlayPath(pathOrEnv);
73
+ return resolveOverlayPath();
74
+ }
75
+ function readBody(path, log) {
76
+ try {
77
+ return { body: readFileSync(path, "utf-8").trim() };
78
+ } catch (err) {
79
+ const message = err instanceof Error ? err.message : String(err);
80
+ log.warn("Model registry overlay file read failed; treated as empty", {
81
+ path,
82
+ errorMessage: message
83
+ });
84
+ return {
85
+ result: {
86
+ entries: [],
87
+ rejections: [{ index: -1, reason: `file read error: ${message}` }],
88
+ path,
89
+ status: "malformed"
90
+ }
91
+ };
92
+ }
93
+ }
94
+ function parseBody(body, path, log) {
95
+ try {
96
+ return { parsed: parseYaml(body) };
97
+ } catch (err) {
98
+ const message = err instanceof Error ? err.message : String(err);
99
+ log.warn("Model registry overlay YAML parse failed; treated as empty", {
100
+ path,
101
+ errorMessage: message
102
+ });
103
+ return {
104
+ result: {
105
+ entries: [],
106
+ rejections: [{ index: -1, reason: `YAML parse error: ${message}` }],
107
+ path,
108
+ status: "malformed"
109
+ }
110
+ };
111
+ }
112
+ }
113
+ function checkSize(path, log) {
114
+ let size;
115
+ try {
116
+ size = statSync(path).size;
117
+ } catch (err) {
118
+ const message = err instanceof Error ? err.message : String(err);
119
+ log.warn("Model registry overlay statSync failed; treated as missing", {
120
+ path,
121
+ errorMessage: message
122
+ });
123
+ return { entries: [], rejections: [], path, status: "missing" };
124
+ }
125
+ if (size <= OVERLAY_MAX_BYTES) return void 0;
126
+ log.warn("Model registry overlay exceeds size cap; refusing to load", {
127
+ path,
128
+ size,
129
+ maxBytes: OVERLAY_MAX_BYTES
130
+ });
131
+ return {
132
+ entries: [],
133
+ rejections: [
134
+ {
135
+ index: -1,
136
+ reason: `file size ${String(size)} exceeds cap ${String(OVERLAY_MAX_BYTES)}`
137
+ }
138
+ ],
139
+ path,
140
+ status: "too-large"
141
+ };
142
+ }
143
+ function extractIdForError(raw) {
144
+ if (raw === null || typeof raw !== "object" || !("id" in raw)) return void 0;
145
+ const idValue = raw.id;
146
+ if (typeof idValue === "string" && idValue !== "") return idValue;
147
+ return void 0;
148
+ }
149
+ function extractEntries(parsed) {
150
+ if (Array.isArray(parsed)) return parsed;
151
+ if (parsed !== null && typeof parsed === "object") {
152
+ const obj = parsed;
153
+ if (Array.isArray(obj.models)) return obj.models;
154
+ }
155
+ return void 0;
156
+ }
157
+ function validateEntries(rawEntries, log, path) {
158
+ const entries = [];
159
+ const rejections = [];
160
+ for (let i = 0; i < rawEntries.length; i++) {
161
+ const raw = rawEntries[i];
162
+ const idCandidate = extractIdForError(raw);
163
+ const parsed = ModelCapabilitySchema.safeParse(raw);
164
+ if (!parsed.success) {
165
+ const rejection = {
166
+ index: i,
167
+ ...idCandidate !== void 0 && idCandidate !== "" ? { id: idCandidate } : {},
168
+ reason: parsed.error.message
169
+ };
170
+ rejections.push(rejection);
171
+ log.warn("Model registry overlay entry rejected by schema", {
172
+ path,
173
+ index: i,
174
+ id: idCandidate,
175
+ errorMessage: parsed.error.message
176
+ });
177
+ continue;
178
+ }
179
+ entries.push(parsed.data);
180
+ }
181
+ return { entries, rejections };
182
+ }
183
+
184
+ // src/config/capability-discovery.ts
185
+ var GeneratedProvenanceSchema = z.object({
186
+ source: z.enum(["models.dev", "litellm"]),
187
+ fetchedAt: z.string(),
188
+ upstreamUrl: z.string()
189
+ });
190
+ var GeneratedModelEntrySchema = z.object({
191
+ id: z.string().min(1),
192
+ displayName: z.string().min(1),
193
+ provider: z.string().min(1),
194
+ contextWindow: z.number().int().positive(),
195
+ pricing: z.object({
196
+ inputPer1M: z.number().nonnegative(),
197
+ outputPer1M: z.number().nonnegative()
198
+ }).optional(),
199
+ maxOutputTokens: z.number().int().positive().optional(),
200
+ deprecated: z.boolean().optional(),
201
+ provenance: GeneratedProvenanceSchema
202
+ });
203
+ var GeneratedRegistrySchema = z.object({
204
+ version: z.literal(1),
205
+ generatedAt: z.string(),
206
+ entryCount: z.number().int().nonnegative(),
207
+ entries: z.array(GeneratedModelEntrySchema)
208
+ });
209
+ var LEGACY_200K_DEFAULT = { contextWindow: 2e5 };
210
+ var FAIL_CLOSED_DEFAULT = { contextWindow: 8192 };
211
+ var CapabilityDiscovery = class {
212
+ canonical;
213
+ generated;
214
+ overlay;
215
+ fallback;
216
+ logger;
217
+ generatedById;
218
+ constructor(config = {}) {
219
+ this.canonical = config.canonical ?? DEFAULT_MODEL_CAPABILITIES;
220
+ this.generated = config.generated ?? null;
221
+ this.overlay = config.overlay ?? [];
222
+ this.fallback = config.conservativeDefault ?? LEGACY_200K_DEFAULT;
223
+ this.logger = config.logger ?? createLogger({ component: "capability-discovery" });
224
+ this.generatedById = /* @__PURE__ */ new Map();
225
+ if (this.generated !== null) {
226
+ for (const entry of this.generated.entries) {
227
+ this.generatedById.set(entry.id, entry);
228
+ }
229
+ }
230
+ }
231
+ /** Synchronously resolve a model id through the four-tier chain. */
232
+ resolve(modelId) {
233
+ const t3 = this.lookupOverlay(modelId);
234
+ if (t3 !== void 0) return this.fromCanonical("t3", t3);
235
+ const t1 = this.lookupCanonical(modelId);
236
+ if (t1 !== void 0) return this.fromCanonical("t1", t1);
237
+ const t2 = this.lookupGenerated(modelId);
238
+ if (t2 !== void 0) return this.fromGenerated("t2", t2);
239
+ return this.fromFallback(modelId);
240
+ }
241
+ /** Exposes the configured fallback for testing / doctor command. */
242
+ getConservativeDefault() {
243
+ return this.fallback;
244
+ }
245
+ /** Count of entries per tier — used by `registry doctor` (#2179). */
246
+ getTierCounts() {
247
+ return {
248
+ t3: this.overlay.length,
249
+ t1: this.canonical.models.length,
250
+ t2: this.generatedById.size,
251
+ t4: 0
252
+ };
253
+ }
254
+ // -------------------------------------------------------------------------
255
+ // Tier lookups
256
+ // -------------------------------------------------------------------------
257
+ lookupOverlay(modelId) {
258
+ return this.overlay.find((m) => m.id === modelId);
259
+ }
260
+ lookupCanonical(modelId) {
261
+ return getModelCapabilities(modelId, this.canonical);
262
+ }
263
+ lookupGenerated(modelId) {
264
+ const direct = this.generatedById.get(modelId);
265
+ if (direct !== void 0) return direct;
266
+ for (const candidate of buildAliasCandidates(modelId)) {
267
+ const hit = this.generatedById.get(candidate);
268
+ if (hit !== void 0) return hit;
269
+ }
270
+ return void 0;
271
+ }
272
+ // -------------------------------------------------------------------------
273
+ // Result shaping
274
+ // -------------------------------------------------------------------------
275
+ fromCanonical(tier, entry) {
276
+ const base = {
277
+ tier,
278
+ id: entry.id,
279
+ displayName: entry.displayName,
280
+ provider: entry.provider,
281
+ contextWindow: entry.contextWindow,
282
+ canonical: entry,
283
+ ...entry.pricing !== void 0 ? { pricing: entry.pricing } : {},
284
+ ...entry.maxOutputTokens !== void 0 ? { maxOutputTokens: entry.maxOutputTokens } : {},
285
+ ...entry.deprecated !== void 0 ? { deprecated: entry.deprecated } : {}
286
+ };
287
+ return base;
288
+ }
289
+ fromGenerated(tier, entry) {
290
+ return {
291
+ tier,
292
+ id: entry.id,
293
+ displayName: entry.displayName,
294
+ provider: entry.provider,
295
+ contextWindow: entry.contextWindow,
296
+ provenance: entry.provenance,
297
+ ...entry.pricing !== void 0 ? { pricing: entry.pricing } : {},
298
+ ...entry.maxOutputTokens !== void 0 ? { maxOutputTokens: entry.maxOutputTokens } : {},
299
+ ...entry.deprecated !== void 0 ? { deprecated: entry.deprecated } : {}
300
+ };
301
+ }
302
+ fromFallback(modelId) {
303
+ this.logger.warn("Model resolved at T4 conservative default", {
304
+ modelId,
305
+ contextWindow: this.fallback.contextWindow
306
+ });
307
+ return {
308
+ tier: "t4",
309
+ id: modelId,
310
+ displayName: modelId,
311
+ provider: "unknown",
312
+ contextWindow: this.fallback.contextWindow,
313
+ ...this.fallback.maxOutputTokens !== void 0 ? { maxOutputTokens: this.fallback.maxOutputTokens } : {}
314
+ };
315
+ }
316
+ };
317
+ var KNOWN_PROVIDER_PREFIXES = [
318
+ "amazon-bedrock",
319
+ "google-vertex",
320
+ "azure-openai",
321
+ "openrouter",
322
+ "anthropic",
323
+ "openai",
324
+ "google",
325
+ "deepseek"
326
+ ];
327
+ function buildAliasCandidates(modelId) {
328
+ const seen = /* @__PURE__ */ new Set([modelId]);
329
+ for (const prefix of KNOWN_PROVIDER_PREFIXES) {
330
+ seen.add(`${prefix}/${modelId}`);
331
+ }
332
+ const slash = modelId.indexOf("/");
333
+ if (slash !== -1) {
334
+ const tail = modelId.slice(slash + 1);
335
+ for (const prefix of KNOWN_PROVIDER_PREFIXES) {
336
+ seen.add(`${prefix}/${tail}`);
337
+ }
338
+ }
339
+ seen.delete(modelId);
340
+ return [...seen];
341
+ }
342
+ function defaultGeneratedRegistryPath() {
343
+ const here = dirname(fileURLToPath(import.meta.url));
344
+ return join2(here, "model-registry.generated.json");
345
+ }
346
+ function loadBundledGeneratedRegistry(path = defaultGeneratedRegistryPath(), logger) {
347
+ const log = logger ?? createLogger({ component: "capability-discovery" });
348
+ if (!existsSync2(path)) {
349
+ log.warn("T2 generated registry file missing; falling back to T1 + T4", { path });
350
+ return null;
351
+ }
352
+ let raw;
353
+ try {
354
+ raw = JSON.parse(readFileSync2(path, "utf-8"));
355
+ } catch (err) {
356
+ const error = err instanceof Error ? err : new Error(String(err));
357
+ log.warn("T2 generated registry JSON parse failed; falling back to T1 + T4", {
358
+ path,
359
+ errorMessage: error.message
360
+ });
361
+ return null;
362
+ }
363
+ const parsed = GeneratedRegistrySchema.safeParse(raw);
364
+ if (!parsed.success) {
365
+ log.warn("T2 generated registry schema validation failed; falling back to T1 + T4", {
366
+ path,
367
+ errorMessage: parsed.error.message
368
+ });
369
+ return null;
370
+ }
371
+ return parsed.data;
372
+ }
373
+ var globalDiscovery;
374
+ function getCapabilityDiscovery() {
375
+ globalDiscovery ??= new CapabilityDiscovery({
376
+ generated: loadBundledGeneratedRegistry(),
377
+ overlay: loadCapabilityOverlay().entries,
378
+ // Fail-closed default (#2177): unknown models get 8 K context and a
379
+ // structured warn instead of the silent 200 K fall-through the legacy
380
+ // getModelContextWindow used to return.
381
+ conservativeDefault: FAIL_CLOSED_DEFAULT
382
+ });
383
+ return globalDiscovery;
384
+ }
385
+
386
+ // src/cli/registry-command.ts
387
+ var VALID_SUBCOMMANDS = ["doctor", "refresh"];
388
+ function isValidRegistrySubcommand(v) {
389
+ return v !== void 0 && VALID_SUBCOMMANDS.includes(v);
390
+ }
391
+ async function registryCommand(subcommand, options = {}) {
392
+ if (subcommand === "doctor") return doctorCommand(options);
393
+ return refreshCommand(options);
394
+ }
395
+ function doctorCommand(options) {
396
+ const report = buildDoctorReport(options);
397
+ if (options.json === true) {
398
+ return { text: JSON.stringify(report, null, 2), exitCode: 0 };
399
+ }
400
+ return { text: formatDoctorReport(report), exitCode: 0 };
401
+ }
402
+ function buildDoctorReport(options) {
403
+ const bundledPath = defaultGeneratedRegistryPath();
404
+ const bundled = loadBundledGeneratedRegistry(bundledPath);
405
+ const overlay = loadCapabilityOverlay(options.overlayPath ?? process.env);
406
+ const discovery = getCapabilityDiscovery();
407
+ const fallback = discovery.getConservativeDefault();
408
+ return {
409
+ tierCounts: discovery.getTierCounts(),
410
+ bundled: {
411
+ path: bundledPath,
412
+ present: bundled !== null,
413
+ entryCount: bundled !== null ? bundled.entryCount : null,
414
+ generatedAt: bundled !== null ? bundled.generatedAt : null
415
+ },
416
+ overlay: {
417
+ path: overlay.path,
418
+ status: overlay.status,
419
+ entryCount: overlay.entries.length,
420
+ rejections: overlay.rejections.map((r) => ({
421
+ index: r.index,
422
+ ...r.id !== void 0 ? { id: r.id } : {},
423
+ reason: r.reason
424
+ })),
425
+ envOverride: process.env[OVERLAY_ENV_VAR]
426
+ },
427
+ conservativeDefault: {
428
+ contextWindow: fallback.contextWindow,
429
+ maxOutputTokens: fallback.maxOutputTokens ?? null
430
+ }
431
+ };
432
+ }
433
+ function formatDoctorReport(r) {
434
+ const lines = [];
435
+ lines.push("nexus-agents registry doctor");
436
+ lines.push("============================");
437
+ lines.push("");
438
+ lines.push("Tier counts (T3 overlay \u2192 T1 canonical \u2192 T2 bundled \u2192 T4 fallback):");
439
+ lines.push(` T3 overlay : ${String(r.tierCounts.t3)}`);
440
+ lines.push(` T1 canonical : ${String(r.tierCounts.t1)}`);
441
+ lines.push(` T2 bundled : ${String(r.tierCounts.t2)}`);
442
+ lines.push("");
443
+ lines.push("T2 bundled registry:");
444
+ lines.push(` path : ${r.bundled.path}`);
445
+ lines.push(` present : ${r.bundled.present ? "yes" : "no (using T1 + T4 only)"}`);
446
+ if (r.bundled.present) {
447
+ lines.push(` entryCount : ${String(r.bundled.entryCount ?? "?")}`);
448
+ lines.push(` generatedAt : ${r.bundled.generatedAt ?? "?"}`);
449
+ }
450
+ lines.push("");
451
+ lines.push("T3 user overlay:");
452
+ lines.push(` path : ${r.overlay.path}`);
453
+ lines.push(` status : ${r.overlay.status}`);
454
+ lines.push(` entryCount : ${String(r.overlay.entryCount)}`);
455
+ lines.push(` env override: ${r.overlay.envOverride ?? "(not set)"}`);
456
+ if (r.overlay.rejections.length > 0) {
457
+ lines.push(" rejections :");
458
+ for (const rej of r.overlay.rejections) {
459
+ const idPart = rej.id !== void 0 ? ` id=${rej.id}` : "";
460
+ lines.push(` - [${String(rej.index)}]${idPart} ${rej.reason}`);
461
+ }
462
+ }
463
+ lines.push("");
464
+ lines.push("T4 conservative default (for unknown ids):");
465
+ lines.push(` contextWindow : ${String(r.conservativeDefault.contextWindow)}`);
466
+ if (r.conservativeDefault.maxOutputTokens !== null) {
467
+ lines.push(` maxOutputTokens: ${String(r.conservativeDefault.maxOutputTokens)}`);
468
+ }
469
+ return lines.join("\n");
470
+ }
471
+ var MAX_REFRESH_BYTES = 5 * 1024 * 1024;
472
+ function missingSourceResult() {
473
+ return {
474
+ text: [
475
+ "registry refresh requires --source=<url>.",
476
+ "",
477
+ "The url should point at a model-registry.generated.json file with a",
478
+ "matching <url>.sha256 sidecar (single line containing the SHA256 hash).",
479
+ "",
480
+ "A signed GitHub release artifact is a planned default but not wired yet;",
481
+ `that is tracked in #2180. Use --source=<your-mirror-url> in the meantime.`
482
+ ].join("\n"),
483
+ exitCode: 2
484
+ };
485
+ }
486
+ async function fetchAndVerify(source, fetchImpl) {
487
+ const body = await fetchWithCap(source, fetchImpl);
488
+ if (!body.ok || body.body === void 0) {
489
+ return { text: `Failed to fetch ${source}: ${body.error ?? "unknown"}`, exitCode: 1 };
490
+ }
491
+ const sha = await fetchWithCap(`${source}.sha256`, fetchImpl);
492
+ if (!sha.ok || sha.body === void 0) {
493
+ return {
494
+ text: `Failed to fetch ${source}.sha256 for integrity check: ${sha.error ?? "unknown"}`,
495
+ exitCode: 1
496
+ };
497
+ }
498
+ const expected = sha.body.trim().split(/\s+/)[0] ?? "";
499
+ if (expected === "") {
500
+ return { text: `SHA256 sidecar at ${source}.sha256 is empty`, exitCode: 1 };
501
+ }
502
+ const actual = createHash("sha256").update(body.body, "utf-8").digest("hex");
503
+ if (actual.toLowerCase() !== expected.toLowerCase()) {
504
+ return {
505
+ text: `SHA256 mismatch: expected ${expected}, got ${actual}. Aborting write.`,
506
+ exitCode: 1
507
+ };
508
+ }
509
+ return { body: body.body, sha256: actual };
510
+ }
511
+ function formatRefreshReport(verb, source, artifact, dest, extraTail = []) {
512
+ return [
513
+ `registry refresh${verb.startsWith("would") ? " --dry-run" : ""}`,
514
+ `source : ${source}`,
515
+ `sha256 : ${artifact.sha256} (verified)`,
516
+ `bytes : ${String(artifact.body.length)}`,
517
+ `${verb}: ${dest}`,
518
+ ...extraTail
519
+ ].join("\n");
520
+ }
521
+ async function refreshCommand(options) {
522
+ const source = options.source;
523
+ if (source === void 0 || source === "") return missingSourceResult();
524
+ const artifact = await fetchAndVerify(source, options.fetchImpl ?? fetch);
525
+ if ("text" in artifact) return artifact;
526
+ const dest = options.destPath ?? join3(homedir2(), ".nexus-agents", "model-registry.generated.json");
527
+ if (options.dryRun === true) {
528
+ return { text: formatRefreshReport("would write to", source, artifact, dest), exitCode: 0 };
529
+ }
530
+ mkdirSync(dirname2(dest), { recursive: true });
531
+ writeFileSync(dest, artifact.body, "utf-8");
532
+ return {
533
+ text: formatRefreshReport("wrote to", source, artifact, dest, [
534
+ "",
535
+ "The refreshed file takes precedence over the bundled registry on the next",
536
+ "run. Restart any running CLI / MCP server for the change to take effect."
537
+ ]),
538
+ exitCode: 0
539
+ };
540
+ }
541
+ async function fetchWithCap(url, fetchImpl) {
542
+ try {
543
+ const response = await fetchImpl(url, { signal: AbortSignal.timeout(3e4) });
544
+ if (!response.ok) {
545
+ return { ok: false, error: `HTTP ${String(response.status)}` };
546
+ }
547
+ const body = await response.text();
548
+ if (body.length > MAX_REFRESH_BYTES) {
549
+ return {
550
+ ok: false,
551
+ error: `payload ${String(body.length)} bytes exceeds cap ${String(MAX_REFRESH_BYTES)}`
552
+ };
553
+ }
554
+ return { ok: true, body };
555
+ } catch (err) {
556
+ const message = err instanceof Error ? err.message : String(err);
557
+ return { ok: false, error: message };
558
+ }
559
+ }
560
+ function formatRegistryUsage() {
561
+ return [
562
+ "Usage:",
563
+ " nexus-agents registry doctor [--json]",
564
+ " Inspect the four-tier capability discovery state.",
565
+ "",
566
+ " nexus-agents registry refresh --source=<url> [--dry-run]",
567
+ " Download a signed model-registry.generated.json from <url>,",
568
+ " SHA256-verify against <url>.sha256, and write to:",
569
+ ` ${join3(homedir2(), ".nexus-agents", "model-registry.generated.json")}`,
570
+ "",
571
+ `Overlay path (T3) is ${defaultOverlayPath()} by default,`,
572
+ `or whatever ${OVERLAY_ENV_VAR} points to. Overlay max size is ${String(OVERLAY_MAX_BYTES)} bytes.`
573
+ ].join("\n");
574
+ }
575
+ export {
576
+ formatRegistryUsage,
577
+ isValidRegistrySubcommand,
578
+ registryCommand
579
+ };
580
+ //# sourceMappingURL=registry-command-S46JJ2SX.js.map