@vedmalex/ai-connect 0.12.0 → 0.13.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.
- package/dist/bun/registry-fs.js +331 -2
- package/dist/bun/registry-fs.js.map +3 -3
- package/dist/node/registry-fs.js +331 -2
- package/dist/node/registry-fs.js.map +3 -3
- package/dist/registry/index.js +57 -1
- package/dist/registry/index.js.map +3 -3
- package/dist/types/registry/fs.d.ts +71 -6
- package/dist/types/registry/fs.d.ts.map +1 -1
- package/dist/types/registry/index.d.ts +2 -0
- package/dist/types/registry/index.d.ts.map +1 -1
- package/dist/types/registry/save.d.ts +96 -0
- package/dist/types/registry/save.d.ts.map +1 -0
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../src/registry/fs.ts", "../../src/errors.ts", "../../src/registry/convert-v1.ts", "../../src/registry/merge.ts", "../../src/registry/parse.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * @vedmalex/ai-connect/registry/fs \u2014 node/bun-only filesystem loader (TASK-038 TS-3/TS-5).\n *\n * This is the ONLY file in `src/registry/**` allowed `node:fs/promises`, `node:path`, `node:os`,\n * and `process.env`. `loadConnectionRegistry` resolves the layered on-disk registry (D1 layer\n * precedence), converts legacy v1 documents in place, overlays sibling `connections.secrets.json`\n * files, and merges the result via the pure `mergeRegistries`. `loadV1Pair` is the standalone\n * bs-search transition helper for explicit endpoints/aliases file pairs.\n *\n * No caching, no file watching (TS-6 out-of-scope guard) \u2014 every call re-reads from disk.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nimport { AiConnectError } from \"../errors.js\";\nimport { convertV1Registry } from \"./convert-v1.js\";\nimport { mergeRegistries } from \"./merge.js\";\nimport { detectRegistryVersion, parseRegistry } from \"./parse.js\";\nimport type {\n ConnectionRegistry,\n ConversionNote,\n RegistryAlias,\n RegistryEndpoint,\n RegistryIssue,\n} from \"./types.js\";\n\nexport interface RegistrySource {\n path: string;\n /**\n * `explicit` \u2014 the caller-supplied `opts.path` single file (wins over everything);\n * `env-override` \u2014 `AI_CONNECT_CONNECTIONS`; `user`/`project` \u2014 the XDG/base + project overlays;\n * `secrets` \u2014 a sibling `connections.secrets.json`.\n */\n role: \"explicit\" | \"env-override\" | \"user\" | \"project\" | \"secrets\";\n version: \"v2\" | \"v1-pair\";\n}\n\nexport interface LoadedRegistry {\n /** Merged result across every resolved layer. */\n registry: ConnectionRegistry;\n sources: RegistrySource[];\n issues: RegistryIssue[];\n /** Non-empty only when a v1 layer was converted. */\n notes: ConversionNote[];\n}\n\nexport interface LoadConnectionRegistryOptions {\n /** Default `process.cwd()`; anchors the project overlay layer. */\n cwd?: string;\n /** Explicit single file \u2014 wins over env/user/project (skips the rest of layer resolution). */\n path?: string;\n /** Default `process.env`. Consulted for `AI_CONNECT_CONNECTIONS`, `HOME`, `XDG_CONFIG_HOME`. */\n env?: NodeJS.ProcessEnv;\n /** Default true: overlay a sibling `connections.secrets.json` beside each resolved layer. */\n includeSecrets?: boolean;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isEnoent(error: unknown): boolean {\n return (\n Boolean(error) &&\n typeof error === \"object\" &&\n (error as NodeJS.ErrnoException).code === \"ENOENT\"\n );\n}\n\nfunction stripBom(text: string): string {\n return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;\n}\n\n/** Read + JSON.parse a file; returns `undefined` (never throws) when the file does not exist. */\nasync function readJsonFile(filePath: string): Promise<unknown> {\n let text: string;\n try {\n text = await readFile(filePath, \"utf8\");\n } catch (error) {\n if (isEnoent(error)) {\n return undefined;\n }\n throw error;\n }\n return JSON.parse(stripBom(text)) as unknown;\n}\n\nfunction resolveHomeDir(env: NodeJS.ProcessEnv): string {\n return env.HOME ?? os.homedir();\n}\n\nfunction resolveXdgConfigHome(env: NodeJS.ProcessEnv): string {\n return env.XDG_CONFIG_HOME ?? path.join(resolveHomeDir(env), \".config\");\n}\n\ninterface ResolvedBaseLayer {\n registry: ConnectionRegistry;\n issues: RegistryIssue[];\n notes: ConversionNote[];\n version: \"v2\" | \"v1-pair\";\n}\n\n/**\n * Resolve one base-layer candidate file: v2 documents run through `parseRegistry`; a v1-endpoints\n * document is accepted + auto-converted (its pair-partner `rag-aliases.json` is looked up beside\n * it, per TS-3). Any other detected shape (v1-aliases-only / unknown) is skipped with a\n * document-level issue rather than aborting the whole load (CD-65 graceful recovery) \u2014 other\n * layers may still resolve. Returns `undefined` only when the file does not exist.\n */\nasync function resolveBaseLayer(filePath: string): Promise<ResolvedBaseLayer | undefined> {\n const doc = await readJsonFile(filePath);\n if (doc === undefined) {\n return undefined;\n }\n\n const detected = detectRegistryVersion(doc);\n if (detected === \"v2\") {\n const { registry, issues } = parseRegistry(doc, { source: filePath });\n return { registry, issues, notes: [], version: \"v2\" };\n }\n if (detected === \"v1-endpoints\") {\n const aliasesPath = path.join(path.dirname(filePath), \"rag-aliases.json\");\n const aliasesDoc = await readJsonFile(aliasesPath);\n const { registry, notes } = convertV1Registry(doc, aliasesDoc);\n return { registry, issues: [], notes, version: \"v1-pair\" };\n }\n\n return {\n registry: { version: 2, endpoints: [] },\n issues: [\n {\n level: \"error\",\n code: \"invalid-version\",\n path: filePath,\n message: `\"${filePath}\" is not a recognized v2 document or v1-endpoints file (detected: ${detected}); skipped.`,\n },\n ],\n notes: [],\n version: \"v2\",\n };\n}\n\n/**\n * Resolve a sibling `connections.secrets.json` overlay. Per D5, a secrets file is a \"zero schema\"\n * partial layer (typically id + auth/env only) \u2014 it is intentionally NOT run through\n * `parseRegistry` (which would reject entries missing required fields such as `transport`).\n * Its raw endpoint/alias fragments feed `mergeRegistries` directly; `mergeRegistries`'s shallow\n * per-id spread lets a partial fragment overlay only the fields it declares.\n */\nasync function resolveSecretsLayer(filePath: string): Promise<ConnectionRegistry | undefined> {\n const doc = await readJsonFile(filePath);\n if (!isObject(doc)) {\n return undefined;\n }\n const registry: ConnectionRegistry = {\n version: 2,\n endpoints: Array.isArray(doc.endpoints) ? (doc.endpoints as RegistryEndpoint[]) : [],\n };\n if (Array.isArray(doc.aliases)) {\n registry.aliases = doc.aliases as RegistryAlias[];\n }\n if (isObject(doc.defaults)) {\n registry.defaults = doc.defaults as NonNullable<ConnectionRegistry[\"defaults\"]>;\n }\n return registry;\n}\n\ninterface CandidateLayer {\n filePath: string;\n role: \"explicit\" | \"env-override\" | \"user\" | \"project\";\n}\n\n/**\n * Resolve candidate base-layer paths per the D1 precedence chain: explicit `path` >\n * `AI_CONNECT_CONNECTIONS` env > project overlay > user base \u2014 `path` (when supplied) replaces\n * the entire env/project/user resolution rather than adding a fifth tier on top of it.\n *\n * Relative `opts.path` and `AI_CONNECT_CONNECTIONS` are BOTH resolved against the injected `cwd`\n * (`opts.cwd ?? process.cwd()`), consistent with how the project overlay is anchored \u2014 never\n * silently against a divergent `process.cwd()`.\n */\nfunction resolveCandidateLayers(opts: LoadConnectionRegistryOptions, env: NodeJS.ProcessEnv, cwd: string): CandidateLayer[] {\n if (opts.path) {\n return [{ filePath: path.resolve(cwd, opts.path), role: \"explicit\" }];\n }\n\n const candidates: CandidateLayer[] = [\n { filePath: path.join(resolveXdgConfigHome(env), \"ai-connect\", \"connections.json\"), role: \"user\" },\n { filePath: path.join(cwd, \".config\", \"ai-connect\", \"connections.json\"), role: \"project\" },\n ];\n if (env.AI_CONNECT_CONNECTIONS) {\n candidates.push({ filePath: path.resolve(cwd, env.AI_CONNECT_CONNECTIONS), role: \"env-override\" });\n }\n return candidates;\n}\n\n/**\n * Load + merge the layered connection registry from disk. Layer order (lowest to highest\n * precedence): user base, project overlay, `AI_CONNECT_CONNECTIONS` env override (or, when\n * `opts.path` is given, that single file alone) \u2014 each layer's own sibling\n * `connections.secrets.json` merges directly above it. Throws `AiConnectError(\"validation_error\")`\n * naming every searched path when NO base layer exists on disk. No caching, no watching.\n */\nexport async function loadConnectionRegistry(\n opts: LoadConnectionRegistryOptions = {},\n): Promise<LoadedRegistry> {\n const env = opts.env ?? process.env;\n const cwd = opts.cwd ?? process.cwd();\n const includeSecrets = opts.includeSecrets ?? true;\n\n const candidates = resolveCandidateLayers(opts, env, cwd);\n const searchedPaths: string[] = [];\n const layers: ConnectionRegistry[] = [];\n const sources: RegistrySource[] = [];\n const issues: RegistryIssue[] = [];\n const notes: ConversionNote[] = [];\n let anyBaseLayerFound = false;\n\n for (const candidate of candidates) {\n searchedPaths.push(candidate.filePath);\n const resolved = await resolveBaseLayer(candidate.filePath);\n if (!resolved) {\n continue;\n }\n anyBaseLayerFound = true;\n layers.push(resolved.registry);\n sources.push({ path: candidate.filePath, role: candidate.role, version: resolved.version });\n issues.push(...resolved.issues);\n notes.push(...resolved.notes);\n\n if (includeSecrets) {\n const secretsPath = path.join(path.dirname(candidate.filePath), \"connections.secrets.json\");\n const secretsLayer = await resolveSecretsLayer(secretsPath);\n if (secretsLayer) {\n layers.push(secretsLayer);\n sources.push({ path: secretsPath, role: \"secrets\", version: \"v2\" });\n }\n }\n }\n\n if (!anyBaseLayerFound) {\n throw new AiConnectError(\n \"validation_error\",\n `No connection registry found. Searched: ${searchedPaths.join(\", \")}`,\n { searchedPaths },\n );\n }\n\n const merged = mergeRegistries(layers);\n issues.push(...merged.issues);\n\n return {\n registry: merged.registry,\n sources,\n issues,\n notes,\n };\n}\n\n/**\n * Convert an explicit v1 endpoints file (+ optional aliases file) into a v2 registry. Standalone\n * helper for consumers mid-transition (e.g. bs-search's `rag-endpoints.json` /\n * `rag-aliases.json` pair) that want to name both paths explicitly rather than relying on the\n * layer auto-detect inside {@link loadConnectionRegistry}.\n */\nexport async function loadV1Pair(\n endpointsPath: string,\n aliasesPath?: string,\n): Promise<{ registry: ConnectionRegistry; notes: ConversionNote[] }> {\n const endpointsDoc = await readJsonFile(endpointsPath);\n if (endpointsDoc === undefined) {\n throw new AiConnectError(\n \"validation_error\",\n `v1 endpoints file not found: \"${endpointsPath}\".`,\n { path: endpointsPath },\n );\n }\n const aliasesDoc = aliasesPath ? await readJsonFile(aliasesPath) : undefined;\n return convertV1Registry(endpointsDoc, aliasesDoc);\n}\n", "import type { AbortReason, AiConnectErrorCode } from \"./types.js\";\n\nexport class AiConnectError extends Error {\n readonly code: AiConnectErrorCode;\n readonly details: Record<string, unknown> | undefined;\n\n constructor(\n code: AiConnectErrorCode,\n message: string,\n details?: Record<string, unknown>,\n ) {\n super(message);\n this.name = \"AiConnectError\";\n this.code = code;\n this.details = details;\n }\n\n /**\n * `Error.prototype.message` is non-enumerable, so a bare `JSON.stringify(err)` silently drops\n * it (LOW, review-report-seed.md). Provide an explicit projection so serializing an\n * `AiConnectError` (logs, wide events, error-response bodies) always carries the message.\n */\n toJSON(): { name: string; code: AiConnectErrorCode; message: string; details: Record<string, unknown> | undefined } {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n details: this.details,\n };\n }\n}\n\nexport function isAiConnectError(error: unknown): error is AiConnectError {\n return error instanceof AiConnectError;\n}\n\n/**\n * Map a derived-abort `reason` to the canonical terminal error. `timeout` \u2192 `\"timeout\"`\n * (deadline elapsed), anything else (including `undefined`/`\"caller\"`) \u2192 `\"aborted\"`\n * (caller cancellation). Both codes are hard-terminal: they never rotate/retry/fall back.\n */\nexport function mapAbortError(\n reason: AbortReason | undefined,\n message?: string,\n): AiConnectError {\n return reason === \"timeout\"\n ? new AiConnectError(\"timeout\", message ?? \"Operation timed out.\")\n : new AiConnectError(\"aborted\", message ?? \"Operation aborted.\");\n}\n\nexport function toAiConnectError(\n error: unknown,\n fallbackCode: AiConnectErrorCode = \"temporary_unavailable\",\n): AiConnectError {\n if (error instanceof AiConnectError) {\n return error;\n }\n\n // Duck-typed DOMException/abort recognition (reliable across node/bun/browser where\n // `instanceof DOMException` is not): an aborted fetch throws `AbortError`, a timed-out\n // one throws `TimeoutError`. The authoritative reason still comes from the client's\n // AbortContext.reason; this is the transport-edge fallback.\n if (typeof (error as { name?: unknown })?.name === \"string\") {\n const name = (error as { name: string }).name;\n const message =\n error instanceof Error ? error.message : \"Operation aborted.\";\n if (name === \"AbortError\") {\n return new AiConnectError(\"aborted\", message);\n }\n if (name === \"TimeoutError\") {\n return new AiConnectError(\"timeout\", message);\n }\n }\n\n if (error instanceof Error) {\n return new AiConnectError(fallbackCode, error.message);\n }\n\n return new AiConnectError(fallbackCode, \"Unknown route failure.\");\n}\n", "/**\n * @vedmalex/ai-connect/registry \u2014 convert-v1.ts (TASK-038 TS-3, D7 mapping table).\n *\n * `convertV1Registry` maps the legacy bs-search `rag-endpoints.json` + `rag-aliases.json` pair\n * onto the consolidated v2 {@link ConnectionRegistry}. PURE: no `node:` imports, no `process.*`.\n * Emits {@link ConversionNote}[] (info/warn only \u2014 conversion never errors) in a deterministic\n * document order.\n */\n\nimport type {\n ConnectionRegistry,\n ConversionNote,\n RegistryAlias,\n RegistryEndpoint,\n RegistryIssueCode,\n} from \"./types.js\";\n\nconst BUILTIN_PROVIDERS = new Set([\"openai\", \"anthropic\", \"gemini\"]);\nconst NATIVE_PROVIDERS = new Set([\"anthropic\", \"gemini\"]);\nconst LOCAL_TRANSPORTS = new Set([\"acp\", \"cli\", \"server\"]);\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction note(\n level: \"info\" | \"warn\",\n code: RegistryIssueCode,\n path: string,\n message: string,\n ids?: { endpointId?: string; aliasId?: string },\n): ConversionNote {\n return {\n level,\n code,\n path,\n message,\n ...(ids?.endpointId !== undefined ? { endpointId: ids.endpointId } : {}),\n ...(ids?.aliasId !== undefined ? { aliasId: ids.aliasId } : {}),\n };\n}\n\n/** Count non-empty path segments in a baseUrl (pure string parse, no URL global). */\nfunction pathSegmentCount(url: string): number {\n const withoutScheme = url.replace(/^[a-z][a-z0-9+.-]*:\\/\\//i, \"\");\n const slash = withoutScheme.indexOf(\"/\");\n if (slash < 0) {\n return 0;\n }\n return withoutScheme\n .slice(slash + 1)\n .split(\"/\")\n .filter((segment) => segment.length > 0).length;\n}\n\n/**\n * Convert a v1 endpoints document (+ optional aliases document) into a v2 registry. `deleted:true`\n * entries are tombstones and skipped; missing provider defaults to `opts.defaultProvider`\n * (\"openai\").\n */\nexport function convertV1Registry(\n endpointsDoc: unknown,\n aliasesDoc?: unknown,\n opts?: { defaultProvider?: string },\n): { registry: ConnectionRegistry; notes: ConversionNote[] } {\n const defaultProvider = opts?.defaultProvider ?? \"openai\";\n const notes: ConversionNote[] = [];\n\n const rawEndpoints =\n isObject(endpointsDoc) && Array.isArray(endpointsDoc.endpoints)\n ? endpointsDoc.endpoints\n : [];\n const endpoints: RegistryEndpoint[] = [];\n\n rawEndpoints.forEach((rawUnknown, index) => {\n if (!isObject(rawUnknown)) {\n return;\n }\n const raw = rawUnknown;\n const path = `endpoints[${index}]`;\n const id = typeof raw.id === \"string\" ? raw.id.trim() : `endpoint-${index}`;\n\n if (raw.deleted === true) {\n notes.push(\n note(\"info\", \"tombstone-skipped\", path, `v1 endpoint \"${id}\" is a tombstone (deleted:true); skipped.`, {\n endpointId: id,\n }),\n );\n return;\n }\n\n const transport = ((): \"api\" | \"acp\" | \"cli\" | \"server\" => {\n const t = typeof raw.transport === \"string\" ? raw.transport.trim().toLowerCase() : \"\";\n if (t === \"acp\" || t === \"cli\" || t === \"server\" || t === \"api\") {\n return t;\n }\n if (t === \"http\" || t === \"proxy\") {\n return \"api\";\n }\n return \"api\";\n })();\n\n const url = typeof raw.url === \"string\" ? raw.url.trim() : undefined;\n const isLocal = LOCAL_TRANSPORTS.has(transport);\n\n let provider: string;\n if (typeof raw.provider === \"string\" && raw.provider.trim()) {\n provider = raw.provider.trim();\n } else {\n provider = defaultProvider;\n notes.push(\n note(\"info\", \"provider-defaulted\", `${path}.provider`, `v1 endpoint \"${id}\" has no provider; defaulted to \"${provider}\".`, {\n endpointId: id,\n }),\n );\n }\n\n // Dead fields: api/format are v1 provider-api-format hints with no v2 equivalent.\n if (\"api\" in raw || \"format\" in raw) {\n notes.push(\n note(\"info\", \"dead-field-dropped\", path, `v1 endpoint \"${id}\" drops dead fields (api/format).`, {\n endpointId: id,\n }),\n );\n }\n\n const endpoint: RegistryEndpoint = {\n id,\n provider,\n transport,\n };\n if (typeof raw.label === \"string\") {\n endpoint.label = raw.label;\n }\n if (url) {\n if (isLocal) {\n endpoint.command = url;\n } else {\n endpoint.baseUrl = url;\n }\n }\n\n // Auth: token -> auth.token; tokenEnv -> auth.tokenEnv.\n const auth: NonNullable<RegistryEndpoint[\"auth\"]> = {};\n if (typeof raw.token === \"string\" && raw.token.trim()) {\n auth.token = raw.token.trim();\n }\n if (typeof raw.tokenEnv === \"string\" && raw.tokenEnv.trim()) {\n auth.tokenEnv = raw.tokenEnv.trim();\n }\n if (Object.keys(auth).length > 0) {\n endpoint.auth = auth;\n }\n\n // v1 headers: on api -> headers (WU-2); on acp/cli/server -> reinterpreted as launch env.\n if (isObject(raw.headers)) {\n const headers = raw.headers as Record<string, string>;\n if (isLocal) {\n endpoint.env = { ...headers };\n notes.push(\n note(\"warn\", \"headers-as-env\", `${path}.headers`, `v1 endpoint \"${id}\" headers on a ${transport} transport are reinterpreted as child-process env.`, {\n endpointId: id,\n }),\n );\n } else {\n endpoint.headers = { ...headers };\n }\n }\n\n if (Array.isArray(raw.models)) {\n endpoint.models = raw.models.filter((m): m is string => typeof m === \"string\");\n }\n if (typeof raw.defaultModel === \"string\") {\n endpoint.defaultModel = raw.defaultModel;\n }\n if (typeof raw.maxContextTokens === \"number\") {\n endpoint.contextWindow = raw.maxContextTokens;\n }\n if (typeof raw.maxOutputTokens === \"number\") {\n endpoint.maxOutputTokens = raw.maxOutputTokens;\n }\n if (typeof raw.liveCheck === \"boolean\") {\n endpoint.liveCheck = raw.liveCheck;\n }\n\n // prefilter has no v2 equivalent \u2014 use models[] shortlist + allowlistMode instead.\n if (\"prefilter\" in raw) {\n notes.push(\n note(\"warn\", \"prefilter-dropped\", `${path}.prefilter`, `v1 endpoint \"${id}\" drops \"prefilter\"; use models[] shortlist + allowlistMode instead.`, {\n endpointId: id,\n }),\n );\n }\n\n // suspicious-baseurl-path: a native-provider baseUrl carrying a multi-segment API path.\n if (endpoint.baseUrl && NATIVE_PROVIDERS.has(provider) && pathSegmentCount(endpoint.baseUrl) >= 2) {\n notes.push(\n note(\"warn\", \"suspicious-baseurl-path\", `${path}.baseUrl`, `v1 endpoint \"${id}\" (${provider}) baseUrl \"${endpoint.baseUrl}\" carries an API path suffix; verify the gateway rewrite.`, {\n endpointId: id,\n }),\n );\n }\n\n // custom-provider: a non-builtin provider without cli options needs a custom RouteHandler.\n if (!BUILTIN_PROVIDERS.has(provider) && !isObject(raw.cli)) {\n notes.push(\n note(\"warn\", \"custom-provider\", `${path}.provider`, `v1 endpoint \"${id}\" uses non-builtin provider \"${provider}\" without cli options; it needs a custom RouteHandler.`, {\n endpointId: id,\n }),\n );\n }\n\n endpoints.push(endpoint);\n });\n\n // --- aliases ---\n const rawAliases =\n isObject(aliasesDoc) && Array.isArray(aliasesDoc.aliases) ? aliasesDoc.aliases : [];\n const aliases: RegistryAlias[] = [];\n rawAliases.forEach((rawUnknown) => {\n if (!isObject(rawUnknown)) {\n return;\n }\n const raw = rawUnknown;\n const id =\n typeof raw.alias === \"string\" && raw.alias.trim()\n ? raw.alias.trim()\n : typeof raw.id === \"string\"\n ? raw.id.trim()\n : \"\";\n if (!id || !Array.isArray(raw.chain)) {\n return;\n }\n const alias: RegistryAlias = {\n id,\n chain: (raw.chain as Array<Record<string, unknown>>).map((leg) => ({\n endpointId: String(leg.endpointId),\n ...(typeof leg.model === \"string\" ? { model: leg.model } : {}),\n })),\n };\n if (typeof raw.description === \"string\") {\n alias.description = raw.description;\n }\n if (typeof raw.label === \"string\") {\n alias.label = raw.label;\n }\n aliases.push(alias);\n });\n\n // --- defaults: v1 defaultEndpoint -> defaults.endpoint; defaultAlias -> defaults.alias ---\n const defaults: { alias?: string; endpoint?: string } = {};\n if (isObject(endpointsDoc) && typeof endpointsDoc.defaultEndpoint === \"string\") {\n defaults.endpoint = endpointsDoc.defaultEndpoint.trim();\n }\n if (isObject(aliasesDoc) && typeof aliasesDoc.defaultAlias === \"string\") {\n defaults.alias = aliasesDoc.defaultAlias.trim();\n }\n\n // Document-level advisory notes: v2 chain semantics differ from v1 on timeout/auth exhaustion.\n if (aliases.length > 0) {\n notes.push(\n note(\"warn\", \"timeout-advance-behavior\", \"\", \"v2 advances the failover chain on timeout (double-execution cost; mitigate via fallbackOverrides or generous timeouts on side-effect legs).\"),\n );\n notes.push(\n note(\"warn\", \"auth-advance-behavior\", \"\", \"v2 advances the failover chain after credential rotation exhausts.\"),\n );\n }\n\n const registry: ConnectionRegistry = {\n version: 2,\n ...(defaults.alias !== undefined || defaults.endpoint !== undefined ? { defaults } : {}),\n endpoints,\n ...(aliases.length > 0 ? { aliases } : {}),\n };\n\n return { registry, notes };\n}\n", "/**\n * @vedmalex/ai-connect/registry \u2014 merge.ts (TASK-038 TS-3, D1/D3 merge rule).\n *\n * `mergeRegistries` composes N registry layers in precedence order (later wins). PURE: no\n * `node:` imports, no `process.*`. Merge rule: per-endpoint SHALLOW top-level field merge by id;\n * aliases REPLACE whole by id; `defaults` replaces whole. `provenance` records the winning layer\n * index per `endpoint:<id>` / `alias:<id>`.\n */\n\nimport type {\n ConnectionRegistry,\n RegistryAlias,\n RegistryEndpoint,\n RegistryIssue,\n} from \"./types.js\";\n\n/**\n * Merge registry layers. Precedence is array order \u2014 `layers[layers.length - 1]` wins per field.\n * Endpoint ids keep first-seen order (base layer order preserved, newly introduced ids appended\n * in the order/layer they first appear).\n *\n * The returned `issues` array is RESERVED and currently ALWAYS EMPTY: the pure merge (shallow\n * per-endpoint field merge + alias whole-replace) has no failure mode of its own \u2014 per-layer\n * structural faults are already surfaced by `parseRegistry`, and cross-layer usability faults are\n * surfaced by `compileRegistry`. The field is kept on the return shape so a future merge-time\n * diagnostic (e.g. a cross-layer conflict warning) can populate it without a breaking API change.\n */\nexport function mergeRegistries(layers: ConnectionRegistry[]): {\n registry: ConnectionRegistry;\n issues: RegistryIssue[];\n provenance: Record<string, number>;\n} {\n const issues: RegistryIssue[] = [];\n const provenance: Record<string, number> = {};\n\n // Endpoints: shallow per-id field merge; preserve first-seen order.\n const endpointOrder: string[] = [];\n const endpointById = new Map<string, RegistryEndpoint>();\n\n // Aliases: whole-replace by id; preserve first-seen order.\n const aliasOrder: string[] = [];\n const aliasById = new Map<string, RegistryAlias>();\n\n let mergedDefaults: ConnectionRegistry[\"defaults\"] | undefined;\n let schema: string | undefined;\n\n layers.forEach((layer, layerIndex) => {\n if (layer.$schema && schema === undefined) {\n schema = layer.$schema;\n }\n for (const endpoint of layer.endpoints ?? []) {\n const existing = endpointById.get(endpoint.id);\n if (existing) {\n // SHALLOW field merge: overlay field replaces, absent field inherits.\n endpointById.set(endpoint.id, { ...existing, ...endpoint });\n } else {\n endpointOrder.push(endpoint.id);\n endpointById.set(endpoint.id, { ...endpoint });\n }\n provenance[`endpoint:${endpoint.id}`] = layerIndex;\n }\n for (const alias of layer.aliases ?? []) {\n if (!aliasById.has(alias.id)) {\n aliasOrder.push(alias.id);\n }\n // WHOLE replace by id (no chain splicing).\n aliasById.set(alias.id, { ...alias });\n provenance[`alias:${alias.id}`] = layerIndex;\n }\n if (layer.defaults !== undefined) {\n // `defaults` object replaces whole.\n mergedDefaults = { ...layer.defaults };\n }\n });\n\n const registry: ConnectionRegistry = {\n version: 2,\n ...(schema !== undefined ? { $schema: schema } : {}),\n ...(mergedDefaults !== undefined ? { defaults: mergedDefaults } : {}),\n endpoints: endpointOrder.map((id) => endpointById.get(id)!),\n ...(aliasOrder.length > 0\n ? { aliases: aliasOrder.map((id) => aliasById.get(id)!) }\n : {}),\n };\n\n return { registry, issues, provenance };\n}\n", "/**\n * @vedmalex/ai-connect/registry \u2014 parse.ts (TASK-038 TS-3).\n *\n * `detectRegistryVersion` + `parseRegistry` and every parse-time issue producer. PURE: no\n * `node:` imports, no `process.*`. Document-level shape/version faults THROW an\n * AiConnectError(validation_error); entry-level faults become error-level issues and the entry is\n * excluded from the returned registry (CD-65 graceful-recovery).\n */\n\nimport { AiConnectError } from \"../errors.js\";\nimport type {\n ConnectionRegistry,\n RegistryAlias,\n RegistryEndpoint,\n RegistryIssue,\n RegistryIssueCode,\n RegistryIssueLevel,\n RegistryVersion,\n} from \"./types.js\";\n\nconst BUILTIN_PROVIDERS = new Set([\"openai\", \"anthropic\", \"gemini\"]);\nconst KNOWN_TRANSPORTS = new Set([\"api\", \"acp\", \"cli\", \"server\"]);\nconst TRANSPORT_SYNONYMS: Record<string, \"api\"> = { http: \"api\", proxy: \"api\" };\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isLoopbackBaseUrl(baseUrl: string | undefined): boolean {\n if (!baseUrl) {\n return false;\n }\n // Pure string check (no URL global dependency for exotic command-like inputs): look for a\n // loopback host token in the authority section.\n const lowered = baseUrl.toLowerCase();\n return (\n lowered.includes(\"127.0.0.1\") ||\n lowered.includes(\"localhost\") ||\n lowered.includes(\"[::1]\") ||\n lowered.includes(\"://::1\")\n );\n}\n\ninterface IssueSink {\n push(issue: RegistryIssue): void;\n}\n\nfunction makeIssue(\n level: RegistryIssueLevel,\n code: RegistryIssueCode,\n path: string,\n message: string,\n ids?: { endpointId?: string; aliasId?: string },\n): RegistryIssue {\n return {\n level,\n code,\n path,\n message,\n ...(ids?.endpointId !== undefined ? { endpointId: ids.endpointId } : {}),\n ...(ids?.aliasId !== undefined ? { aliasId: ids.aliasId } : {}),\n };\n}\n\n/**\n * Heuristic document classification. A v2 document is the ONLY shape that carries a `version`\n * key; a v1 endpoints file has `endpoints` (no version); a v1 aliases file has `aliases` (no\n * version, no endpoints). Everything else is `unknown`.\n */\nexport function detectRegistryVersion(doc: unknown): RegistryVersion {\n if (!isObject(doc)) {\n return \"unknown\";\n }\n if (\"version\" in doc) {\n return \"v2\";\n }\n if (Array.isArray(doc.endpoints)) {\n return \"v1-endpoints\";\n }\n if (Array.isArray(doc.aliases)) {\n return \"v1-aliases\";\n }\n return \"unknown\";\n}\n\nfunction normalizeTransportKind(\n raw: unknown,\n): \"api\" | \"acp\" | \"cli\" | \"server\" | undefined {\n if (typeof raw !== \"string\") {\n return undefined;\n }\n const trimmed = raw.trim().toLowerCase();\n if (TRANSPORT_SYNONYMS[trimmed]) {\n return TRANSPORT_SYNONYMS[trimmed];\n }\n if (KNOWN_TRANSPORTS.has(trimmed)) {\n return trimmed as \"api\" | \"acp\" | \"cli\" | \"server\";\n }\n return undefined;\n}\n\n/** Validate a single endpoint; returns the cleaned endpoint or null when it must be excluded. */\nfunction parseEndpoint(\n raw: unknown,\n index: number,\n sink: IssueSink,\n): RegistryEndpoint | null {\n const path = `endpoints[${index}]`;\n if (!isObject(raw) || typeof raw.id !== \"string\" || raw.id.trim().length === 0) {\n sink.push(\n makeIssue(\n \"error\",\n \"invalid-endpoint-shape\",\n path,\n `Endpoint at ${path} must be an object with a non-empty string id.`,\n ),\n );\n return null;\n }\n const id = raw.id.trim();\n const kind = typeof raw.kind === \"string\" && raw.kind.trim() ? raw.kind.trim() : \"text\";\n const transport = normalizeTransportKind(raw.transport);\n if (!transport) {\n sink.push(\n makeIssue(\n \"error\",\n \"unknown-transport\",\n `${path}.transport`,\n `Endpoint \"${id}\" declares unknown transport ${JSON.stringify(raw.transport)}.`,\n { endpointId: id },\n ),\n );\n return null;\n }\n\n const isApi = transport === \"api\";\n const baseUrl = typeof raw.baseUrl === \"string\" ? raw.baseUrl.trim() : undefined;\n const command = typeof raw.command === \"string\" ? raw.command.trim() : undefined;\n const transportId =\n typeof raw.transportId === \"string\" && raw.transportId.trim()\n ? raw.transportId.trim()\n : undefined;\n\n // baseUrl/command placement conflicts.\n if (isApi && command) {\n sink.push(\n makeIssue(\n \"error\",\n \"baseurl-command-conflict\",\n `${path}.command`,\n `Endpoint \"${id}\" is transport \"api\" but declares a command spawn line.`,\n { endpointId: id },\n ),\n );\n return null;\n }\n if (!isApi && baseUrl && transport !== \"server\") {\n sink.push(\n makeIssue(\n \"error\",\n \"baseurl-command-conflict\",\n `${path}.baseUrl`,\n `Endpoint \"${id}\" is transport \"${transport}\" but declares a baseUrl.`,\n { endpointId: id },\n ),\n );\n return null;\n }\n if (isApi && !baseUrl) {\n sink.push(\n makeIssue(\n \"error\",\n \"missing-baseurl\",\n `${path}.baseUrl`,\n `Endpoint \"${id}\" (api) requires a baseUrl.`,\n { endpointId: id },\n ),\n );\n return null;\n }\n if (!isApi && !command && !transportId) {\n sink.push(\n makeIssue(\n \"error\",\n \"missing-command\",\n `${path}.command`,\n `Endpoint \"${id}\" (${transport}) requires a command or a transportId catalog preset.`,\n { endpointId: id },\n ),\n );\n return null;\n }\n\n const headers = isObject(raw.headers)\n ? (raw.headers as Record<string, string>)\n : undefined;\n const env = isObject(raw.env) ? (raw.env as Record<string, string>) : undefined;\n const envPassthrough = Array.isArray(raw.envPassthrough)\n ? (raw.envPassthrough as string[])\n : undefined;\n\n if (headers && !isApi) {\n sink.push(\n makeIssue(\n \"error\",\n \"headers-on-local-endpoint\",\n `${path}.headers`,\n `Endpoint \"${id}\" sets headers on a non-api (\"${transport}\") transport; use env instead.`,\n { endpointId: id },\n ),\n );\n return null;\n }\n if (isApi && (env || envPassthrough)) {\n sink.push(\n makeIssue(\n \"error\",\n \"env-on-api-endpoint\",\n `${path}.env`,\n `Endpoint \"${id}\" sets env/envPassthrough on an api transport; use headers instead.`,\n { endpointId: id },\n ),\n );\n return null;\n }\n\n const auth = isObject(raw.auth) ? raw.auth : undefined;\n const inlineToken =\n auth && typeof auth.token === \"string\" ? auth.token.trim() : undefined;\n if (inlineToken && isApi && !isLoopbackBaseUrl(baseUrl)) {\n sink.push(\n makeIssue(\n \"warn\",\n \"inline-token-non-loopback\",\n `${path}.auth.token`,\n `Endpoint \"${id}\" carries an inline auth.token against a non-loopback baseUrl; prefer tokenEnv.`,\n { endpointId: id },\n ),\n );\n }\n\n const provider =\n typeof raw.provider === \"string\" && raw.provider.trim()\n ? raw.provider.trim()\n : \"openai\";\n const hasCliOptions = isObject(raw.cli);\n if (!BUILTIN_PROVIDERS.has(provider) && !hasCliOptions) {\n sink.push(\n makeIssue(\n \"warn\",\n \"custom-provider\",\n `${path}.provider`,\n `Endpoint \"${id}\" uses non-builtin provider \"${provider}\" without cli options; it needs a custom RouteHandler.`,\n { endpointId: id },\n ),\n );\n }\n\n // Carry the cleaned document through verbatim (opaque fields round-trip). We normalize only the\n // transport synonym and the derived defaults consumers depend on.\n const cleaned: RegistryEndpoint = {\n ...(raw as unknown as RegistryEndpoint),\n id,\n kind,\n provider,\n transport,\n };\n return cleaned;\n}\n\n/** Detect duplicate {endpointId, model} pairs within one chain (R1.6). */\nfunction chainHasDuplicateLeg(\n chain: Array<{ endpointId: string; model?: string }>,\n): { endpointId: string; model: string | undefined; index: number } | null {\n const seen = new Set<string>();\n for (let i = 0; i < chain.length; i++) {\n const leg = chain[i]!;\n // Join with an explicit U+0000 ESCAPE (never a raw byte \u2014 a raw NUL makes this file\n // grep-invisible) so (\"ab\",\"c\") and (\"a\",\"bc\") legs stay distinct in the dedup key.\n const key = `${leg.endpointId}\\u0000${leg.model ?? \"\"}`;\n if (seen.has(key)) {\n return { endpointId: leg.endpointId, model: leg.model, index: i };\n }\n seen.add(key);\n }\n return null;\n}\n\n/**\n * Parse + validate a claimed-v2 document. THROWS AiConnectError(validation_error) only for\n * document-level shape/version faults; entry-level faults become error-level issues.\n */\nexport function parseRegistry(\n doc: unknown,\n _opts?: { source?: string },\n): { registry: ConnectionRegistry; issues: RegistryIssue[] } {\n if (!isObject(doc)) {\n throw new AiConnectError(\n \"validation_error\",\n \"Connection registry must be a JSON object.\",\n );\n }\n if (doc.version !== 2) {\n throw new AiConnectError(\n \"validation_error\",\n `Connection registry version must be 2 (received ${JSON.stringify(doc.version)}).`,\n );\n }\n if (!Array.isArray(doc.endpoints)) {\n throw new AiConnectError(\n \"validation_error\",\n \"Connection registry must declare an `endpoints` array.\",\n );\n }\n\n const issues: RegistryIssue[] = [];\n const sink: IssueSink = { push: (i) => issues.push(i) };\n\n // --- endpoints ---\n const endpoints: RegistryEndpoint[] = [];\n const declaredEndpointIds = new Set<string>();\n const keptEndpointIds = new Set<string>();\n doc.endpoints.forEach((rawEndpoint, index) => {\n const rawId =\n isObject(rawEndpoint) && typeof rawEndpoint.id === \"string\"\n ? rawEndpoint.id.trim()\n : undefined;\n if (rawId) {\n if (declaredEndpointIds.has(rawId)) {\n sink.push(\n makeIssue(\n \"error\",\n \"duplicate-endpoint-id\",\n `endpoints[${index}].id`,\n `Duplicate endpoint id \"${rawId}\"; the later declaration is dropped.`,\n { endpointId: rawId },\n ),\n );\n return;\n }\n declaredEndpointIds.add(rawId);\n }\n const cleaned = parseEndpoint(rawEndpoint, index, sink);\n if (cleaned) {\n endpoints.push(cleaned);\n keptEndpointIds.add(cleaned.id);\n }\n });\n\n // Map id -> kept endpoint for chain validation.\n const endpointById = new Map<string, RegistryEndpoint>();\n for (const endpoint of endpoints) {\n endpointById.set(endpoint.id, endpoint);\n }\n\n // --- aliases ---\n const aliases: RegistryAlias[] = [];\n const declaredAliasIds = new Set<string>();\n const rawAliases = Array.isArray(doc.aliases) ? doc.aliases : [];\n rawAliases.forEach((rawAlias, index) => {\n const path = `aliases[${index}]`;\n if (\n !isObject(rawAlias) ||\n typeof rawAlias.id !== \"string\" ||\n rawAlias.id.trim().length === 0 ||\n !Array.isArray(rawAlias.chain)\n ) {\n sink.push(\n makeIssue(\n \"error\",\n \"invalid-alias-shape\",\n path,\n `Alias at ${path} must be an object with a non-empty id and a chain array.`,\n ),\n );\n return;\n }\n const aliasId = rawAlias.id.trim();\n if (declaredAliasIds.has(aliasId)) {\n sink.push(\n makeIssue(\n \"error\",\n \"duplicate-alias-id\",\n `${path}.id`,\n `Duplicate alias id \"${aliasId}\"; the later declaration is dropped.`,\n { aliasId },\n ),\n );\n return;\n }\n declaredAliasIds.add(aliasId);\n\n const chain = rawAlias.chain as Array<{ endpointId: string; model?: string }>;\n\n // duplicate-chain-leg (R1.6) \u2014 a whole-alias error.\n const dup = chainHasDuplicateLeg(chain);\n if (dup) {\n sink.push(\n makeIssue(\n \"error\",\n \"duplicate-chain-leg\",\n `${path}.chain[${dup.index}]`,\n `Alias \"${aliasId}\" repeats leg {endpointId:\"${dup.endpointId}\", model:${JSON.stringify(dup.model)}}; duplicate legs are rejected (R1.6).`,\n { aliasId },\n ),\n );\n }\n\n chain.forEach((leg, legIndex) => {\n const legPath = `${path}.chain[${legIndex}]`;\n const target = endpointById.get(leg.endpointId);\n if (!declaredEndpointIds.has(leg.endpointId)) {\n sink.push(\n makeIssue(\n \"error\",\n \"chain-endpoint-missing\",\n legPath,\n `Alias \"${aliasId}\" leg ${legIndex} references unknown endpoint \"${leg.endpointId}\".`,\n { aliasId, endpointId: leg.endpointId },\n ),\n );\n return;\n }\n if (!target) {\n // Declared but excluded for a structural fault already reported on the endpoint.\n return;\n }\n if ((target.kind ?? \"text\") !== \"text\") {\n sink.push(\n makeIssue(\n \"error\",\n \"chain-endpoint-non-text\",\n legPath,\n `Alias \"${aliasId}\" leg ${legIndex} references non-text endpoint \"${leg.endpointId}\" (kind \"${target.kind}\").`,\n { aliasId, endpointId: leg.endpointId },\n ),\n );\n return;\n }\n const model = leg.model ?? target.defaultModel;\n if (!model) {\n sink.push(\n makeIssue(\n \"error\",\n \"chain-leg-no-model\",\n legPath,\n `Alias \"${aliasId}\" leg ${legIndex} omits model and endpoint \"${leg.endpointId}\" has no defaultModel.`,\n { aliasId, endpointId: leg.endpointId },\n ),\n );\n return;\n }\n const allowlistMode = target.allowlistMode ?? \"shortlist\";\n if (allowlistMode === \"strict\" && leg.model) {\n const declaredModels = (target.models ?? []).map((entry) =>\n typeof entry === \"string\" ? entry : entry.id,\n );\n if (!declaredModels.includes(leg.model)) {\n sink.push(\n makeIssue(\n \"error\",\n \"chain-model-not-allowed\",\n legPath,\n `Alias \"${aliasId}\" leg ${legIndex} requests model \"${leg.model}\" not in endpoint \"${leg.endpointId}\" models[] under allowlistMode \"strict\".`,\n { aliasId, endpointId: leg.endpointId },\n ),\n );\n }\n }\n });\n\n aliases.push({\n ...(rawAlias as unknown as RegistryAlias),\n id: aliasId,\n chain,\n });\n });\n\n // --- defaults ---\n let defaults: ConnectionRegistry[\"defaults\"];\n if (isObject(doc.defaults)) {\n const rawDefaults = doc.defaults;\n const nextDefaults: { alias?: string; endpoint?: string } = {};\n if (typeof rawDefaults.alias === \"string\" && rawDefaults.alias.trim()) {\n const aliasId = rawDefaults.alias.trim();\n nextDefaults.alias = aliasId;\n const target = aliases.find((a) => a.id === aliasId);\n if (!declaredAliasIds.has(aliasId)) {\n sink.push(\n makeIssue(\n \"error\",\n \"defaults-target-missing\",\n \"defaults.alias\",\n `defaults.alias references unknown alias \"${aliasId}\".`,\n { aliasId },\n ),\n );\n } else if (target && target.enabled === false) {\n sink.push(\n makeIssue(\n \"warn\",\n \"defaults-target-unusable\",\n \"defaults.alias\",\n `defaults.alias \"${aliasId}\" targets a disabled alias.`,\n { aliasId },\n ),\n );\n }\n }\n if (typeof rawDefaults.endpoint === \"string\" && rawDefaults.endpoint.trim()) {\n const endpointId = rawDefaults.endpoint.trim();\n nextDefaults.endpoint = endpointId;\n const target = endpointById.get(endpointId);\n if (!declaredEndpointIds.has(endpointId)) {\n sink.push(\n makeIssue(\n \"error\",\n \"defaults-target-missing\",\n \"defaults.endpoint\",\n `defaults.endpoint references unknown endpoint \"${endpointId}\".`,\n { endpointId },\n ),\n );\n } else if (target && target.enabled === false) {\n sink.push(\n makeIssue(\n \"warn\",\n \"defaults-target-unusable\",\n \"defaults.endpoint\",\n `defaults.endpoint \"${endpointId}\" targets a disabled endpoint.`,\n { endpointId },\n ),\n );\n }\n }\n if (nextDefaults.alias !== undefined || nextDefaults.endpoint !== undefined) {\n defaults = nextDefaults;\n }\n }\n\n const registry: ConnectionRegistry = {\n version: 2,\n ...(doc.$schema && typeof doc.$schema === \"string\" ? { $schema: doc.$schema } : {}),\n ...(defaults ? { defaults } : {}),\n endpoints,\n ...(aliases.length > 0 ? { aliases } : {}),\n };\n\n return { registry, issues };\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
3
|
+
"sources": ["../../src/registry/fs.ts", "../../src/errors.ts", "../../src/registry/convert-v1.ts", "../../src/registry/merge.ts", "../../src/registry/parse.ts", "../../src/registry/save.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * @vedmalex/ai-connect/registry/fs \u2014 node/bun-only filesystem loader + write API\n * (TASK-038 TS-3/TS-5, TASK-040 write API).\n *\n * This is the ONLY file in `src/registry/**` allowed `node:fs/promises`, `node:fs` (constants),\n * `node:path`, `node:os`, and `process.env`/`process.pid`. `loadConnectionRegistry` resolves the\n * layered on-disk registry (D1 layer precedence), converts legacy v1 documents in place, overlays\n * sibling `connections.secrets.json` files, and merges the result via the pure `mergeRegistries`.\n * `loadV1Pair` is the standalone bs-search transition helper for explicit endpoints/aliases file\n * pairs.\n *\n * `saveConnectionRegistry`/`saveConnectionSecrets` are the write-side counterpart (TASK-040):\n * layer/path resolution mirrors the read side, writes are atomic (tmp file + rename) with\n * numbered `<stem>.<n>.json` backups, and `saveConnectionRegistry` unconditionally strips\n * `auth.token`/`auth.fallbackTokens` before every write (defense-in-depth \u2014 see\n * `stripRegistrySecrets` in `save.ts` for the documented boundary).\n *\n * No caching, no file watching (TS-6 out-of-scope guard) \u2014 every call re-reads from disk.\n */\n\nimport { constants as fsConstants } from \"node:fs\";\nimport { copyFile, mkdir, readFile, readdir, rename, unlink, writeFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nimport { AiConnectError } from \"../errors.js\";\nimport { convertV1Registry } from \"./convert-v1.js\";\nimport { mergeRegistries } from \"./merge.js\";\nimport { detectRegistryVersion, parseRegistry } from \"./parse.js\";\nimport { mergeForSave, serializeRegistry, stripRegistrySecrets } from \"./save.js\";\nimport type {\n RegistrySecretsFragment,\n SaveConnectionRegistryOptions,\n SaveConnectionSecretsOptions,\n SaveRegistryResult,\n SaveSecretsResult,\n} from \"./save.js\";\nimport type {\n ConnectionRegistry,\n ConversionNote,\n RegistryAlias,\n RegistryEndpoint,\n RegistryIssue,\n} from \"./types.js\";\n\nexport type {\n RegistrySecretsFragment,\n SaveConnectionRegistryOptions,\n SaveConnectionSecretsOptions,\n SaveRegistryResult,\n SaveSecretsResult,\n} from \"./save.js\";\n\nexport interface RegistrySource {\n path: string;\n /**\n * `explicit` \u2014 the caller-supplied `opts.path` single file (wins over everything);\n * `env-override` \u2014 `AI_CONNECT_CONNECTIONS`; `user`/`project` \u2014 the XDG/base + project overlays;\n * `secrets` \u2014 a sibling `connections.secrets.json`.\n */\n role: \"explicit\" | \"env-override\" | \"user\" | \"project\" | \"secrets\";\n version: \"v2\" | \"v1-pair\";\n}\n\nexport interface LoadedRegistry {\n /** Merged result across every resolved layer. */\n registry: ConnectionRegistry;\n sources: RegistrySource[];\n issues: RegistryIssue[];\n /** Non-empty only when a v1 layer was converted. */\n notes: ConversionNote[];\n}\n\nexport interface LoadConnectionRegistryOptions {\n /** Default `process.cwd()`; anchors the project overlay layer. */\n cwd?: string;\n /** Explicit single file \u2014 wins over env/user/project (skips the rest of layer resolution). */\n path?: string;\n /** Default `process.env`. Consulted for `AI_CONNECT_CONNECTIONS`, `HOME`, `XDG_CONFIG_HOME`. */\n env?: NodeJS.ProcessEnv;\n /** Default true: overlay a sibling `connections.secrets.json` beside each resolved layer. */\n includeSecrets?: boolean;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isEnoent(error: unknown): boolean {\n return (\n Boolean(error) &&\n typeof error === \"object\" &&\n (error as NodeJS.ErrnoException).code === \"ENOENT\"\n );\n}\n\nfunction isEexist(error: unknown): boolean {\n return (\n Boolean(error) &&\n typeof error === \"object\" &&\n (error as NodeJS.ErrnoException).code === \"EEXIST\"\n );\n}\n\nfunction stripBom(text: string): string {\n return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;\n}\n\n/** Read + JSON.parse a file; returns `undefined` (never throws) when the file does not exist. */\nasync function readJsonFile(filePath: string): Promise<unknown> {\n let text: string;\n try {\n text = await readFile(filePath, \"utf8\");\n } catch (error) {\n if (isEnoent(error)) {\n return undefined;\n }\n throw error;\n }\n return JSON.parse(stripBom(text)) as unknown;\n}\n\nfunction resolveHomeDir(env: NodeJS.ProcessEnv): string {\n return env.HOME ?? os.homedir();\n}\n\nfunction resolveXdgConfigHome(env: NodeJS.ProcessEnv): string {\n return env.XDG_CONFIG_HOME ?? path.join(resolveHomeDir(env), \".config\");\n}\n\ninterface ResolvedBaseLayer {\n registry: ConnectionRegistry;\n issues: RegistryIssue[];\n notes: ConversionNote[];\n version: \"v2\" | \"v1-pair\";\n}\n\n/**\n * Resolve one base-layer candidate file: v2 documents run through `parseRegistry`; a v1-endpoints\n * document is accepted + auto-converted (its pair-partner `rag-aliases.json` is looked up beside\n * it, per TS-3). Any other detected shape (v1-aliases-only / unknown) is skipped with a\n * document-level issue rather than aborting the whole load (CD-65 graceful recovery) \u2014 other\n * layers may still resolve. Returns `undefined` only when the file does not exist.\n */\nasync function resolveBaseLayer(filePath: string): Promise<ResolvedBaseLayer | undefined> {\n const doc = await readJsonFile(filePath);\n if (doc === undefined) {\n return undefined;\n }\n\n const detected = detectRegistryVersion(doc);\n if (detected === \"v2\") {\n const { registry, issues } = parseRegistry(doc, { source: filePath });\n return { registry, issues, notes: [], version: \"v2\" };\n }\n if (detected === \"v1-endpoints\") {\n const aliasesPath = path.join(path.dirname(filePath), \"rag-aliases.json\");\n const aliasesDoc = await readJsonFile(aliasesPath);\n const { registry, notes } = convertV1Registry(doc, aliasesDoc);\n return { registry, issues: [], notes, version: \"v1-pair\" };\n }\n\n return {\n registry: { version: 2, endpoints: [] },\n issues: [\n {\n level: \"error\",\n code: \"invalid-version\",\n path: filePath,\n message: `\"${filePath}\" is not a recognized v2 document or v1-endpoints file (detected: ${detected}); skipped.`,\n },\n ],\n notes: [],\n version: \"v2\",\n };\n}\n\n/**\n * Resolve a sibling `connections.secrets.json` overlay. Per D5, a secrets file is a \"zero schema\"\n * partial layer (typically id + auth/env only) \u2014 it is intentionally NOT run through\n * `parseRegistry` (which would reject entries missing required fields such as `transport`).\n * Its raw endpoint/alias fragments feed `mergeRegistries` directly; `mergeRegistries`'s shallow\n * per-id spread lets a partial fragment overlay only the fields it declares.\n */\nasync function resolveSecretsLayer(filePath: string): Promise<ConnectionRegistry | undefined> {\n const doc = await readJsonFile(filePath);\n if (!isObject(doc)) {\n return undefined;\n }\n const registry: ConnectionRegistry = {\n version: 2,\n endpoints: Array.isArray(doc.endpoints) ? (doc.endpoints as RegistryEndpoint[]) : [],\n };\n if (Array.isArray(doc.aliases)) {\n registry.aliases = doc.aliases as RegistryAlias[];\n }\n if (isObject(doc.defaults)) {\n registry.defaults = doc.defaults as NonNullable<ConnectionRegistry[\"defaults\"]>;\n }\n return registry;\n}\n\ninterface CandidateLayer {\n filePath: string;\n role: \"explicit\" | \"env-override\" | \"user\" | \"project\";\n}\n\n/**\n * Resolve candidate base-layer paths per the D1 precedence chain: explicit `path` >\n * `AI_CONNECT_CONNECTIONS` env > project overlay > user base \u2014 `path` (when supplied) replaces\n * the entire env/project/user resolution rather than adding a fifth tier on top of it.\n *\n * Relative `opts.path` and `AI_CONNECT_CONNECTIONS` are BOTH resolved against the injected `cwd`\n * (`opts.cwd ?? process.cwd()`), consistent with how the project overlay is anchored \u2014 never\n * silently against a divergent `process.cwd()`.\n */\nfunction resolveCandidateLayers(opts: LoadConnectionRegistryOptions, env: NodeJS.ProcessEnv, cwd: string): CandidateLayer[] {\n if (opts.path) {\n return [{ filePath: path.resolve(cwd, opts.path), role: \"explicit\" }];\n }\n\n const candidates: CandidateLayer[] = [\n { filePath: path.join(resolveXdgConfigHome(env), \"ai-connect\", \"connections.json\"), role: \"user\" },\n { filePath: path.join(cwd, \".config\", \"ai-connect\", \"connections.json\"), role: \"project\" },\n ];\n if (env.AI_CONNECT_CONNECTIONS) {\n candidates.push({ filePath: path.resolve(cwd, env.AI_CONNECT_CONNECTIONS), role: \"env-override\" });\n }\n return candidates;\n}\n\n/**\n * Load + merge the layered connection registry from disk. Layer order (lowest to highest\n * precedence): user base, project overlay, `AI_CONNECT_CONNECTIONS` env override (or, when\n * `opts.path` is given, that single file alone) \u2014 each layer's own sibling\n * `connections.secrets.json` merges directly above it. Throws `AiConnectError(\"validation_error\")`\n * naming every searched path when NO base layer exists on disk. No caching, no watching.\n */\nexport async function loadConnectionRegistry(\n opts: LoadConnectionRegistryOptions = {},\n): Promise<LoadedRegistry> {\n const env = opts.env ?? process.env;\n const cwd = opts.cwd ?? process.cwd();\n const includeSecrets = opts.includeSecrets ?? true;\n\n const candidates = resolveCandidateLayers(opts, env, cwd);\n const searchedPaths: string[] = [];\n const layers: ConnectionRegistry[] = [];\n const sources: RegistrySource[] = [];\n const issues: RegistryIssue[] = [];\n const notes: ConversionNote[] = [];\n let anyBaseLayerFound = false;\n\n for (const candidate of candidates) {\n searchedPaths.push(candidate.filePath);\n const resolved = await resolveBaseLayer(candidate.filePath);\n if (!resolved) {\n continue;\n }\n anyBaseLayerFound = true;\n layers.push(resolved.registry);\n sources.push({ path: candidate.filePath, role: candidate.role, version: resolved.version });\n issues.push(...resolved.issues);\n notes.push(...resolved.notes);\n\n if (includeSecrets) {\n const secretsPath = path.join(path.dirname(candidate.filePath), \"connections.secrets.json\");\n const secretsLayer = await resolveSecretsLayer(secretsPath);\n if (secretsLayer) {\n layers.push(secretsLayer);\n sources.push({ path: secretsPath, role: \"secrets\", version: \"v2\" });\n }\n }\n }\n\n if (!anyBaseLayerFound) {\n throw new AiConnectError(\n \"validation_error\",\n `No connection registry found. Searched: ${searchedPaths.join(\", \")}`,\n { searchedPaths },\n );\n }\n\n const merged = mergeRegistries(layers);\n issues.push(...merged.issues);\n\n return {\n registry: merged.registry,\n sources,\n issues,\n notes,\n };\n}\n\n/**\n * Convert an explicit v1 endpoints file (+ optional aliases file) into a v2 registry. Standalone\n * helper for consumers mid-transition (e.g. bs-search's `rag-endpoints.json` /\n * `rag-aliases.json` pair) that want to name both paths explicitly rather than relying on the\n * layer auto-detect inside {@link loadConnectionRegistry}.\n */\nexport async function loadV1Pair(\n endpointsPath: string,\n aliasesPath?: string,\n): Promise<{ registry: ConnectionRegistry; notes: ConversionNote[] }> {\n const endpointsDoc = await readJsonFile(endpointsPath);\n if (endpointsDoc === undefined) {\n throw new AiConnectError(\n \"validation_error\",\n `v1 endpoints file not found: \"${endpointsPath}\".`,\n { path: endpointsPath },\n );\n }\n const aliasesDoc = aliasesPath ? await readJsonFile(aliasesPath) : undefined;\n return convertV1Registry(endpointsDoc, aliasesDoc);\n}\n\n// ============================================================================================\n// Write API (TASK-040)\n// ============================================================================================\n\ninterface SaveTargetOpts {\n layer?: \"user\" | \"project\" | undefined;\n path?: string | undefined;\n cwd?: string | undefined;\n env?: NodeJS.ProcessEnv | undefined;\n}\n\n/**\n * Resolve the on-disk write target, symmetric with the read-side layer resolution: `path` (when\n * supplied) wins outright; otherwise `layer:'user'` maps to `$XDG_CONFIG_HOME/ai-connect/<filename>`\n * and `layer:'project'` to `<cwd>/.config/ai-connect/<filename>`. Neither `layer` nor `path` is a\n * `validation_error` \u2014 a save target must always be unambiguous.\n */\nfunction resolveSaveTarget(opts: SaveTargetOpts, filename: string): string {\n const env = opts.env ?? process.env;\n const cwd = opts.cwd ?? process.cwd();\n\n if (opts.path) {\n return path.resolve(cwd, opts.path);\n }\n if (opts.layer === \"user\") {\n return path.join(resolveXdgConfigHome(env), \"ai-connect\", filename);\n }\n if (opts.layer === \"project\") {\n return path.join(cwd, \".config\", \"ai-connect\", filename);\n }\n throw new AiConnectError(\n \"validation_error\",\n \"Registry save target is ambiguous: supply either `layer` ('user' | 'project') or an explicit `path`.\",\n );\n}\n\n/** Read + JSON.parse an existing file for a merge-save; a parse fault is a named, honest error. */\nasync function readExistingJson(filePath: string): Promise<unknown> {\n try {\n return await readJsonFile(filePath);\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new AiConnectError(\n \"validation_error\",\n `Existing file at \"${filePath}\" is not valid JSON (${error.message}); refusing to merge-save over it. The file was not modified.`,\n { path: filePath },\n );\n }\n throw error;\n }\n}\n\nconst BACKUP_MAX_ATTEMPTS = 100;\n\nfunction escapeRegExpLiteral(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Rotate a numbered `<stem>.<n>.json` backup of `targetPath` (which must already exist) beside\n * it. The next index is `max(existing <stem>.<n>.json indices) + 1`; the copy uses\n * `COPYFILE_EXCL` so a concurrent writer that claims the same index loses the race honestly\n * (`EEXIST`) rather than silently clobbering the other backup \u2014 this process retries the next\n * index, bounded, rather than looping forever.\n */\nasync function createNumberedBackup(targetPath: string): Promise<string> {\n const dir = path.dirname(targetPath);\n const ext = path.extname(targetPath);\n const stem = path.basename(targetPath, ext);\n const pattern = new RegExp(`^${escapeRegExpLiteral(stem)}\\\\.(\\\\d+)${escapeRegExpLiteral(ext)}$`);\n\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch (error) {\n if (isEnoent(error)) {\n entries = [];\n } else {\n throw error;\n }\n }\n\n let maxIndex = -1;\n for (const entry of entries) {\n const match = pattern.exec(entry);\n if (match) {\n const parsed = Number.parseInt(match[1]!, 10);\n if (parsed > maxIndex) {\n maxIndex = parsed;\n }\n }\n }\n\n let nextIndex = maxIndex + 1;\n for (let attempt = 0; attempt < BACKUP_MAX_ATTEMPTS; attempt++) {\n const candidate = path.join(dir, `${stem}.${nextIndex}${ext}`);\n try {\n await copyFile(targetPath, candidate, fsConstants.COPYFILE_EXCL);\n return candidate;\n } catch (error) {\n if (isEexist(error)) {\n nextIndex += 1;\n continue;\n }\n throw error;\n }\n }\n\n throw new AiConnectError(\n \"validation_error\",\n `Could not create a numbered backup for \"${targetPath}\" after ${BACKUP_MAX_ATTEMPTS} index-collision retries.`,\n { path: targetPath, attempts: BACKUP_MAX_ATTEMPTS },\n );\n}\n\ninterface AtomicWriteResult {\n /** `true` when the target file did not exist before this call. */\n created: boolean;\n /** `false` when the no-op guard skipped the write (existing content was byte-identical). */\n wrote: boolean;\n backupPath?: string;\n}\n\n/**\n * Shared write pipeline for both `saveConnectionRegistry` and `saveConnectionSecrets`:\n * `mkdir -p` the target directory (always), skip the write entirely when the target already\n * holds byte-identical content (no write, no backup rotation), otherwise rotate a numbered\n * backup (when `opts.backup` and the target exists) and then write via a same-directory tmp\n * file + `rename` \u2014 the target is never observed half-written; a failure mid-write cleans up\n * the tmp file and leaves the previous target intact.\n */\nasync function atomicWriteWithBackup(\n targetPath: string,\n data: string,\n opts: { backup: boolean },\n): Promise<AtomicWriteResult> {\n const dir = path.dirname(targetPath);\n await mkdir(dir, { recursive: true });\n\n let existingText: string | undefined;\n try {\n existingText = await readFile(targetPath, \"utf8\");\n } catch (error) {\n if (!isEnoent(error)) {\n throw error;\n }\n }\n const targetExists = existingText !== undefined;\n\n if (targetExists && existingText === data) {\n return { created: false, wrote: false };\n }\n\n let backupPath: string | undefined;\n if (targetExists && opts.backup) {\n backupPath = await createNumberedBackup(targetPath);\n }\n\n const tmpPath = path.join(\n dir,\n `${path.basename(targetPath)}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`,\n );\n try {\n await writeFile(tmpPath, data, \"utf8\");\n await rename(tmpPath, targetPath);\n } catch (error) {\n await unlink(tmpPath).catch(() => {});\n throw error;\n }\n\n return {\n created: !targetExists,\n wrote: true,\n ...(backupPath !== undefined ? { backupPath } : {}),\n };\n}\n\nfunction isNonEmptyStringId(value: unknown): value is string {\n return typeof value === \"string\" && value.trim().length > 0;\n}\n\n/**\n * Build the existing-layer registry for a v2 merge-save WITHOUT running the document through\n * `parseRegistry` (ISS-280 / plan Rev 3): `parseRegistry` EXCLUDES invalid endpoints\n * (`parseEndpoint` \u2192 `null` on `invalid-endpoint-shape`/`unknown-transport`/\n * `baseurl-command-conflict`/etc.) and rebuilds valid ones from a field whitelist, dropping any\n * field it does not recognize. Running an EXISTING file through that on a merge-save would\n * silently delete a neighbor's broken endpoint and strip unknown fields from their valid ones \u2014\n * a silent-clobber violation of UR-008 (\"other parties' entries survive; silent overwrite is\n * impossible\"). Instead, `endpoints`/`aliases`/`defaults` are taken RAW/VERBATIM from the parsed\n * JSON. The only requirement \u2014 needed because {@link mergeForSave} keys entries by `id` \u2014 is that\n * every `endpoints`/`aliases` entry is an object with a non-empty string `id`; anything else is a\n * named, honest `validation_error` naming the path and index (the file is left untouched), never\n * a silent drop.\n */\nfunction buildRawExistingRegistry(doc: unknown, targetPath: string): ConnectionRegistry {\n if (!isObject(doc)) {\n throw new AiConnectError(\n \"validation_error\",\n `Existing file at \"${targetPath}\" is not a JSON object; refusing to merge-save over it. The file was not modified.`,\n { path: targetPath },\n );\n }\n\n const rawEndpoints = Array.isArray(doc.endpoints) ? doc.endpoints : [];\n rawEndpoints.forEach((entry, index) => {\n if (!isObject(entry) || !isNonEmptyStringId(entry.id)) {\n throw new AiConnectError(\n \"validation_error\",\n `Existing file at \"${targetPath}\" has an invalid entry at endpoints[${index}] (must be an object with a non-empty string id to be merge-keyed); refusing to merge-save over it. The file was not modified.`,\n { path: targetPath, index, field: \"endpoints\" },\n );\n }\n });\n\n const rawAliases = Array.isArray(doc.aliases) ? doc.aliases : [];\n rawAliases.forEach((entry, index) => {\n if (!isObject(entry) || !isNonEmptyStringId(entry.id)) {\n throw new AiConnectError(\n \"validation_error\",\n `Existing file at \"${targetPath}\" has an invalid entry at aliases[${index}] (must be an object with a non-empty string id to be merge-keyed); refusing to merge-save over it. The file was not modified.`,\n { path: targetPath, index, field: \"aliases\" },\n );\n }\n });\n\n return {\n version: 2,\n ...(typeof doc.$schema === \"string\" ? { $schema: doc.$schema } : {}),\n ...(isObject(doc.defaults)\n ? { defaults: doc.defaults as NonNullable<ConnectionRegistry[\"defaults\"]> }\n : {}),\n endpoints: rawEndpoints as RegistryEndpoint[],\n ...(rawAliases.length > 0 ? { aliases: rawAliases as RegistryAlias[] } : {}),\n };\n}\n\n/**\n * Write a v2 connection registry to disk (UR-002/UR-003/UR-008/UR-009/UR-010).\n *\n * Target resolution mirrors {@link loadConnectionRegistry}'s layer addressing: `opts.path` wins\n * outright; otherwise `opts.layer` selects `user` (`$XDG_CONFIG_HOME/ai-connect/connections.json`)\n * or `project` (`<cwd>/.config/ai-connect/connections.json`); neither throws `validation_error`.\n *\n * `opts.mode` (default `'merge'`): `'merge'` reads the existing target (if any). A v2 document's\n * `endpoints`/`aliases`/`defaults` are taken RAW/VERBATIM (see {@link buildRawExistingRegistry}) \u2014\n * deliberately NOT run through `parseRegistry`, which excludes invalid entries and rebuilds valid\n * ones from a field whitelist; doing that on a merge-save would silently delete a neighbor's\n * broken endpoint or strip their unknown fields (a UR-008 violation \u2014 other parties' entries\n * MUST survive verbatim, including ones `parseRegistry` would reject and fields it does not\n * recognize). The only validation applied to the existing v2 document is that each\n * `endpoints`/`aliases` entry is an object with a non-empty string `id` (needed to key the\n * merge); anything else is a named `validation_error` naming the path and index, file untouched.\n * A legacy v1-endpoints document is converted via `convertV1Registry` (its sibling\n * `rag-aliases.json` is looked up beside it, mirroring the read-side base-layer resolution; the\n * result is written back as v2 \u2014 a one-way migration); any other detected shape is a named\n * `validation_error`. The existing layer (raw v2, or converted v1) is then upserted with\n * `registry` via {@link mergeForSave} (later-wins shallow per-id endpoint merge, whole-replace\n * aliases). A malformed existing JSON file is also a named `validation_error`; the file is never\n * touched on any of these read-time faults. `'replace'` skips the read-merge step entirely and\n * writes `registry` as the whole document.\n *\n * `auth.token`/`auth.fallbackTokens` are unconditionally stripped (via {@link stripRegistrySecrets})\n * from the FINAL registry before every write, in both modes \u2014 this is not an opt-out. Round-trip\n * fidelity (reading back what was written reproduces the input in `parseRegistry`'s normalized\n * space) holds MODULO SECRET STRIPPING (UR-004): a registry carrying `auth.token`/\n * `auth.fallbackTokens` will not compare deep-equal to what `saveConnectionRegistry` persists,\n * by design \u2014 see docs/connection-registry.md \"Write API\" for the full boundary (env/auth.params\n * from a merged secrets overlay are NOT covered by this strip).\n *\n * The write itself is atomic (tmp file + `rename`) with a numbered `<stem>.<n>.json` backup\n * rotated first when the target already exists (default `opts.backup !== false`); the target\n * directory is always created (`mkdir -p`); a byte-identical no-op write skips both the write and\n * the backup rotation.\n */\nexport async function saveConnectionRegistry(\n registry: ConnectionRegistry,\n opts: SaveConnectionRegistryOptions = {},\n): Promise<SaveRegistryResult> {\n const env = opts.env ?? process.env;\n const cwd = opts.cwd ?? process.cwd();\n const mode = opts.mode ?? \"merge\";\n const backup = opts.backup ?? true;\n const targetPath = resolveSaveTarget({ layer: opts.layer, path: opts.path, cwd, env }, \"connections.json\");\n\n let finalRegistry: ConnectionRegistry = registry;\n\n if (mode === \"merge\") {\n const existingDoc = await readExistingJson(targetPath);\n if (existingDoc !== undefined) {\n const detected = detectRegistryVersion(existingDoc);\n let existingRegistry: ConnectionRegistry;\n if (detected === \"v2\") {\n existingRegistry = buildRawExistingRegistry(existingDoc, targetPath);\n } else if (detected === \"v1-endpoints\") {\n const aliasesPath = path.join(path.dirname(targetPath), \"rag-aliases.json\");\n const aliasesDoc = await readExistingJson(aliasesPath);\n existingRegistry = convertV1Registry(existingDoc, aliasesDoc).registry;\n } else {\n throw new AiConnectError(\n \"validation_error\",\n `Existing file at \"${targetPath}\" is not a recognized v2 document or v1-endpoints file (detected: ${detected}); refusing to merge-save over it. The file was not modified.`,\n { path: targetPath, detected },\n );\n }\n finalRegistry = mergeForSave(existingRegistry, registry);\n }\n }\n\n const stripped = stripRegistrySecrets(finalRegistry);\n const data = serializeRegistry(stripped.registry);\n const writeResult = await atomicWriteWithBackup(targetPath, data, { backup });\n\n return {\n path: targetPath,\n mode,\n created: writeResult.created,\n ...(writeResult.backupPath !== undefined ? { backupPath: writeResult.backupPath } : {}),\n strippedFields: stripped.strippedFields,\n };\n}\n\nfunction fragmentToPseudoRegistry(fragment: RegistrySecretsFragment): ConnectionRegistry {\n return {\n version: 2,\n endpoints: fragment.endpoints ?? [],\n ...(fragment.aliases !== undefined ? { aliases: fragment.aliases } : {}),\n ...(fragment.defaults !== undefined ? { defaults: fragment.defaults } : {}),\n };\n}\n\n/** Per-id shallow merge of two secrets fragments, reusing `mergeRegistries`'s exact semantics. */\nfunction mergeSecretsFragments(\n existing: RegistrySecretsFragment,\n incoming: RegistrySecretsFragment,\n): RegistrySecretsFragment {\n const merged = mergeRegistries([\n fragmentToPseudoRegistry(existing),\n fragmentToPseudoRegistry(incoming),\n ]).registry;\n\n return {\n ...(merged.endpoints.length > 0 ? { endpoints: merged.endpoints } : {}),\n ...(merged.aliases !== undefined ? { aliases: merged.aliases } : {}),\n ...(merged.defaults !== undefined ? { defaults: merged.defaults } : {}),\n };\n}\n\n/**\n * Deterministic JSON serialization for a secrets fragment: 2-space indent, top-level key order\n * `endpoints`, `aliases`, `defaults` (only present keys \u2014 mirrors {@link serializeRegistry}'s\n * style but WITHOUT `$schema`/`version`, since a secrets file is a zero-schema partial), trailing\n * newline.\n */\nfunction serializeSecretsFragment(fragment: RegistrySecretsFragment): string {\n const ordered: Record<string, unknown> = {};\n if (fragment.endpoints !== undefined) {\n ordered.endpoints = fragment.endpoints;\n }\n if (fragment.aliases !== undefined) {\n ordered.aliases = fragment.aliases;\n }\n if (fragment.defaults !== undefined) {\n ordered.defaults = fragment.defaults;\n }\n return `${JSON.stringify(ordered, null, 2)}\\n`;\n}\n\nfunction readRawSecretsFragment(doc: unknown): RegistrySecretsFragment | undefined {\n if (!isObject(doc)) {\n return undefined;\n }\n const fragment: RegistrySecretsFragment = {};\n if (Array.isArray(doc.endpoints)) {\n fragment.endpoints = doc.endpoints as RegistryEndpoint[];\n }\n if (Array.isArray(doc.aliases)) {\n fragment.aliases = doc.aliases as RegistryAlias[];\n }\n if (isObject(doc.defaults)) {\n fragment.defaults = doc.defaults as NonNullable<ConnectionRegistry[\"defaults\"]>;\n }\n return fragment;\n}\n\n/**\n * Write a `connections.secrets.json` sibling fragment (UR-011) \u2014 the counterpart consumers use to\n * persist `auth.token`/`env` values that `saveConnectionRegistry` deliberately never writes to\n * the main file. `fragment` is a \"zero schema\" partial (`{endpoints?, aliases?, defaults?}`,\n * mirroring the shape `loadConnectionRegistry`'s secrets overlay already reads) \u2014 it is NOT run\n * through `parseRegistry` and is NOT secret-stripped.\n *\n * Target resolution is identical to {@link saveConnectionRegistry} except the filename is\n * `connections.secrets.json`. `opts.mode` (default `'merge'`) per-id shallow-merges `fragment`\n * onto the existing file's fragment (same semantics as `mergeRegistries`: later-wins per-id\n * endpoint field merge, whole-replace aliases by id, `defaults` replaced whole); a malformed\n * existing JSON file is a named `validation_error` and the file is left untouched. `'replace'`\n * writes `fragment` as the whole document. The write itself uses the same atomic tmp+rename with\n * numbered-backup pipeline as `saveConnectionRegistry`.\n */\nexport async function saveConnectionSecrets(\n fragment: RegistrySecretsFragment,\n opts: SaveConnectionSecretsOptions = {},\n): Promise<SaveSecretsResult> {\n const env = opts.env ?? process.env;\n const cwd = opts.cwd ?? process.cwd();\n const mode = opts.mode ?? \"merge\";\n const backup = opts.backup ?? true;\n const targetPath = resolveSaveTarget(\n { layer: opts.layer, path: opts.path, cwd, env },\n \"connections.secrets.json\",\n );\n\n let finalFragment: RegistrySecretsFragment = fragment;\n\n if (mode === \"merge\") {\n const existingDoc = await readExistingJson(targetPath);\n const existingFragment = readRawSecretsFragment(existingDoc);\n if (existingFragment) {\n finalFragment = mergeSecretsFragments(existingFragment, fragment);\n }\n }\n\n const data = serializeSecretsFragment(finalFragment);\n const writeResult = await atomicWriteWithBackup(targetPath, data, { backup });\n\n return {\n path: targetPath,\n mode,\n created: writeResult.created,\n ...(writeResult.backupPath !== undefined ? { backupPath: writeResult.backupPath } : {}),\n };\n}\n", "import type { AbortReason, AiConnectErrorCode } from \"./types.js\";\n\nexport class AiConnectError extends Error {\n readonly code: AiConnectErrorCode;\n readonly details: Record<string, unknown> | undefined;\n\n constructor(\n code: AiConnectErrorCode,\n message: string,\n details?: Record<string, unknown>,\n ) {\n super(message);\n this.name = \"AiConnectError\";\n this.code = code;\n this.details = details;\n }\n\n /**\n * `Error.prototype.message` is non-enumerable, so a bare `JSON.stringify(err)` silently drops\n * it (LOW, review-report-seed.md). Provide an explicit projection so serializing an\n * `AiConnectError` (logs, wide events, error-response bodies) always carries the message.\n */\n toJSON(): { name: string; code: AiConnectErrorCode; message: string; details: Record<string, unknown> | undefined } {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n details: this.details,\n };\n }\n}\n\nexport function isAiConnectError(error: unknown): error is AiConnectError {\n return error instanceof AiConnectError;\n}\n\n/**\n * Map a derived-abort `reason` to the canonical terminal error. `timeout` \u2192 `\"timeout\"`\n * (deadline elapsed), anything else (including `undefined`/`\"caller\"`) \u2192 `\"aborted\"`\n * (caller cancellation). Both codes are hard-terminal: they never rotate/retry/fall back.\n */\nexport function mapAbortError(\n reason: AbortReason | undefined,\n message?: string,\n): AiConnectError {\n return reason === \"timeout\"\n ? new AiConnectError(\"timeout\", message ?? \"Operation timed out.\")\n : new AiConnectError(\"aborted\", message ?? \"Operation aborted.\");\n}\n\nexport function toAiConnectError(\n error: unknown,\n fallbackCode: AiConnectErrorCode = \"temporary_unavailable\",\n): AiConnectError {\n if (error instanceof AiConnectError) {\n return error;\n }\n\n // Duck-typed DOMException/abort recognition (reliable across node/bun/browser where\n // `instanceof DOMException` is not): an aborted fetch throws `AbortError`, a timed-out\n // one throws `TimeoutError`. The authoritative reason still comes from the client's\n // AbortContext.reason; this is the transport-edge fallback.\n if (typeof (error as { name?: unknown })?.name === \"string\") {\n const name = (error as { name: string }).name;\n const message =\n error instanceof Error ? error.message : \"Operation aborted.\";\n if (name === \"AbortError\") {\n return new AiConnectError(\"aborted\", message);\n }\n if (name === \"TimeoutError\") {\n return new AiConnectError(\"timeout\", message);\n }\n }\n\n if (error instanceof Error) {\n return new AiConnectError(fallbackCode, error.message);\n }\n\n return new AiConnectError(fallbackCode, \"Unknown route failure.\");\n}\n", "/**\n * @vedmalex/ai-connect/registry \u2014 convert-v1.ts (TASK-038 TS-3, D7 mapping table).\n *\n * `convertV1Registry` maps the legacy bs-search `rag-endpoints.json` + `rag-aliases.json` pair\n * onto the consolidated v2 {@link ConnectionRegistry}. PURE: no `node:` imports, no `process.*`.\n * Emits {@link ConversionNote}[] (info/warn only \u2014 conversion never errors) in a deterministic\n * document order.\n */\n\nimport type {\n ConnectionRegistry,\n ConversionNote,\n RegistryAlias,\n RegistryEndpoint,\n RegistryIssueCode,\n} from \"./types.js\";\n\nconst BUILTIN_PROVIDERS = new Set([\"openai\", \"anthropic\", \"gemini\"]);\nconst NATIVE_PROVIDERS = new Set([\"anthropic\", \"gemini\"]);\nconst LOCAL_TRANSPORTS = new Set([\"acp\", \"cli\", \"server\"]);\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction note(\n level: \"info\" | \"warn\",\n code: RegistryIssueCode,\n path: string,\n message: string,\n ids?: { endpointId?: string; aliasId?: string },\n): ConversionNote {\n return {\n level,\n code,\n path,\n message,\n ...(ids?.endpointId !== undefined ? { endpointId: ids.endpointId } : {}),\n ...(ids?.aliasId !== undefined ? { aliasId: ids.aliasId } : {}),\n };\n}\n\n/** Count non-empty path segments in a baseUrl (pure string parse, no URL global). */\nfunction pathSegmentCount(url: string): number {\n const withoutScheme = url.replace(/^[a-z][a-z0-9+.-]*:\\/\\//i, \"\");\n const slash = withoutScheme.indexOf(\"/\");\n if (slash < 0) {\n return 0;\n }\n return withoutScheme\n .slice(slash + 1)\n .split(\"/\")\n .filter((segment) => segment.length > 0).length;\n}\n\n/**\n * Convert a v1 endpoints document (+ optional aliases document) into a v2 registry. `deleted:true`\n * entries are tombstones and skipped; missing provider defaults to `opts.defaultProvider`\n * (\"openai\").\n */\nexport function convertV1Registry(\n endpointsDoc: unknown,\n aliasesDoc?: unknown,\n opts?: { defaultProvider?: string },\n): { registry: ConnectionRegistry; notes: ConversionNote[] } {\n const defaultProvider = opts?.defaultProvider ?? \"openai\";\n const notes: ConversionNote[] = [];\n\n const rawEndpoints =\n isObject(endpointsDoc) && Array.isArray(endpointsDoc.endpoints)\n ? endpointsDoc.endpoints\n : [];\n const endpoints: RegistryEndpoint[] = [];\n\n rawEndpoints.forEach((rawUnknown, index) => {\n if (!isObject(rawUnknown)) {\n return;\n }\n const raw = rawUnknown;\n const path = `endpoints[${index}]`;\n const id = typeof raw.id === \"string\" ? raw.id.trim() : `endpoint-${index}`;\n\n if (raw.deleted === true) {\n notes.push(\n note(\"info\", \"tombstone-skipped\", path, `v1 endpoint \"${id}\" is a tombstone (deleted:true); skipped.`, {\n endpointId: id,\n }),\n );\n return;\n }\n\n const transport = ((): \"api\" | \"acp\" | \"cli\" | \"server\" => {\n const t = typeof raw.transport === \"string\" ? raw.transport.trim().toLowerCase() : \"\";\n if (t === \"acp\" || t === \"cli\" || t === \"server\" || t === \"api\") {\n return t;\n }\n if (t === \"http\" || t === \"proxy\") {\n return \"api\";\n }\n return \"api\";\n })();\n\n const url = typeof raw.url === \"string\" ? raw.url.trim() : undefined;\n const isLocal = LOCAL_TRANSPORTS.has(transport);\n\n let provider: string;\n if (typeof raw.provider === \"string\" && raw.provider.trim()) {\n provider = raw.provider.trim();\n } else {\n provider = defaultProvider;\n notes.push(\n note(\"info\", \"provider-defaulted\", `${path}.provider`, `v1 endpoint \"${id}\" has no provider; defaulted to \"${provider}\".`, {\n endpointId: id,\n }),\n );\n }\n\n // Dead fields: api/format are v1 provider-api-format hints with no v2 equivalent.\n if (\"api\" in raw || \"format\" in raw) {\n notes.push(\n note(\"info\", \"dead-field-dropped\", path, `v1 endpoint \"${id}\" drops dead fields (api/format).`, {\n endpointId: id,\n }),\n );\n }\n\n const endpoint: RegistryEndpoint = {\n id,\n provider,\n transport,\n };\n if (typeof raw.label === \"string\") {\n endpoint.label = raw.label;\n }\n if (url) {\n if (isLocal) {\n endpoint.command = url;\n } else {\n endpoint.baseUrl = url;\n }\n }\n\n // Auth: token -> auth.token; tokenEnv -> auth.tokenEnv.\n const auth: NonNullable<RegistryEndpoint[\"auth\"]> = {};\n if (typeof raw.token === \"string\" && raw.token.trim()) {\n auth.token = raw.token.trim();\n }\n if (typeof raw.tokenEnv === \"string\" && raw.tokenEnv.trim()) {\n auth.tokenEnv = raw.tokenEnv.trim();\n }\n if (Object.keys(auth).length > 0) {\n endpoint.auth = auth;\n }\n\n // v1 headers: on api -> headers (WU-2); on acp/cli/server -> reinterpreted as launch env.\n if (isObject(raw.headers)) {\n const headers = raw.headers as Record<string, string>;\n if (isLocal) {\n endpoint.env = { ...headers };\n notes.push(\n note(\"warn\", \"headers-as-env\", `${path}.headers`, `v1 endpoint \"${id}\" headers on a ${transport} transport are reinterpreted as child-process env.`, {\n endpointId: id,\n }),\n );\n } else {\n endpoint.headers = { ...headers };\n }\n }\n\n if (Array.isArray(raw.models)) {\n endpoint.models = raw.models.filter((m): m is string => typeof m === \"string\");\n }\n if (typeof raw.defaultModel === \"string\") {\n endpoint.defaultModel = raw.defaultModel;\n }\n if (typeof raw.maxContextTokens === \"number\") {\n endpoint.contextWindow = raw.maxContextTokens;\n }\n if (typeof raw.maxOutputTokens === \"number\") {\n endpoint.maxOutputTokens = raw.maxOutputTokens;\n }\n if (typeof raw.liveCheck === \"boolean\") {\n endpoint.liveCheck = raw.liveCheck;\n }\n\n // prefilter has no v2 equivalent \u2014 use models[] shortlist + allowlistMode instead.\n if (\"prefilter\" in raw) {\n notes.push(\n note(\"warn\", \"prefilter-dropped\", `${path}.prefilter`, `v1 endpoint \"${id}\" drops \"prefilter\"; use models[] shortlist + allowlistMode instead.`, {\n endpointId: id,\n }),\n );\n }\n\n // suspicious-baseurl-path: a native-provider baseUrl carrying a multi-segment API path.\n if (endpoint.baseUrl && NATIVE_PROVIDERS.has(provider) && pathSegmentCount(endpoint.baseUrl) >= 2) {\n notes.push(\n note(\"warn\", \"suspicious-baseurl-path\", `${path}.baseUrl`, `v1 endpoint \"${id}\" (${provider}) baseUrl \"${endpoint.baseUrl}\" carries an API path suffix; verify the gateway rewrite.`, {\n endpointId: id,\n }),\n );\n }\n\n // custom-provider: a non-builtin provider without cli options needs a custom RouteHandler.\n if (!BUILTIN_PROVIDERS.has(provider) && !isObject(raw.cli)) {\n notes.push(\n note(\"warn\", \"custom-provider\", `${path}.provider`, `v1 endpoint \"${id}\" uses non-builtin provider \"${provider}\" without cli options; it needs a custom RouteHandler.`, {\n endpointId: id,\n }),\n );\n }\n\n endpoints.push(endpoint);\n });\n\n // --- aliases ---\n const rawAliases =\n isObject(aliasesDoc) && Array.isArray(aliasesDoc.aliases) ? aliasesDoc.aliases : [];\n const aliases: RegistryAlias[] = [];\n rawAliases.forEach((rawUnknown) => {\n if (!isObject(rawUnknown)) {\n return;\n }\n const raw = rawUnknown;\n const id =\n typeof raw.alias === \"string\" && raw.alias.trim()\n ? raw.alias.trim()\n : typeof raw.id === \"string\"\n ? raw.id.trim()\n : \"\";\n if (!id || !Array.isArray(raw.chain)) {\n return;\n }\n const alias: RegistryAlias = {\n id,\n chain: (raw.chain as Array<Record<string, unknown>>).map((leg) => ({\n endpointId: String(leg.endpointId),\n ...(typeof leg.model === \"string\" ? { model: leg.model } : {}),\n })),\n };\n if (typeof raw.description === \"string\") {\n alias.description = raw.description;\n }\n if (typeof raw.label === \"string\") {\n alias.label = raw.label;\n }\n aliases.push(alias);\n });\n\n // --- defaults: v1 defaultEndpoint -> defaults.endpoint; defaultAlias -> defaults.alias ---\n const defaults: { alias?: string; endpoint?: string } = {};\n if (isObject(endpointsDoc) && typeof endpointsDoc.defaultEndpoint === \"string\") {\n defaults.endpoint = endpointsDoc.defaultEndpoint.trim();\n }\n if (isObject(aliasesDoc) && typeof aliasesDoc.defaultAlias === \"string\") {\n defaults.alias = aliasesDoc.defaultAlias.trim();\n }\n\n // Document-level advisory notes: v2 chain semantics differ from v1 on timeout/auth exhaustion.\n if (aliases.length > 0) {\n notes.push(\n note(\"warn\", \"timeout-advance-behavior\", \"\", \"v2 advances the failover chain on timeout (double-execution cost; mitigate via fallbackOverrides or generous timeouts on side-effect legs).\"),\n );\n notes.push(\n note(\"warn\", \"auth-advance-behavior\", \"\", \"v2 advances the failover chain after credential rotation exhausts.\"),\n );\n }\n\n const registry: ConnectionRegistry = {\n version: 2,\n ...(defaults.alias !== undefined || defaults.endpoint !== undefined ? { defaults } : {}),\n endpoints,\n ...(aliases.length > 0 ? { aliases } : {}),\n };\n\n return { registry, notes };\n}\n", "/**\n * @vedmalex/ai-connect/registry \u2014 merge.ts (TASK-038 TS-3, D1/D3 merge rule).\n *\n * `mergeRegistries` composes N registry layers in precedence order (later wins). PURE: no\n * `node:` imports, no `process.*`. Merge rule: per-endpoint SHALLOW top-level field merge by id;\n * aliases REPLACE whole by id; `defaults` replaces whole. `provenance` records the winning layer\n * index per `endpoint:<id>` / `alias:<id>`.\n */\n\nimport type {\n ConnectionRegistry,\n RegistryAlias,\n RegistryEndpoint,\n RegistryIssue,\n} from \"./types.js\";\n\n/**\n * Merge registry layers. Precedence is array order \u2014 `layers[layers.length - 1]` wins per field.\n * Endpoint ids keep first-seen order (base layer order preserved, newly introduced ids appended\n * in the order/layer they first appear).\n *\n * The returned `issues` array is RESERVED and currently ALWAYS EMPTY: the pure merge (shallow\n * per-endpoint field merge + alias whole-replace) has no failure mode of its own \u2014 per-layer\n * structural faults are already surfaced by `parseRegistry`, and cross-layer usability faults are\n * surfaced by `compileRegistry`. The field is kept on the return shape so a future merge-time\n * diagnostic (e.g. a cross-layer conflict warning) can populate it without a breaking API change.\n */\nexport function mergeRegistries(layers: ConnectionRegistry[]): {\n registry: ConnectionRegistry;\n issues: RegistryIssue[];\n provenance: Record<string, number>;\n} {\n const issues: RegistryIssue[] = [];\n const provenance: Record<string, number> = {};\n\n // Endpoints: shallow per-id field merge; preserve first-seen order.\n const endpointOrder: string[] = [];\n const endpointById = new Map<string, RegistryEndpoint>();\n\n // Aliases: whole-replace by id; preserve first-seen order.\n const aliasOrder: string[] = [];\n const aliasById = new Map<string, RegistryAlias>();\n\n let mergedDefaults: ConnectionRegistry[\"defaults\"] | undefined;\n let schema: string | undefined;\n\n layers.forEach((layer, layerIndex) => {\n if (layer.$schema && schema === undefined) {\n schema = layer.$schema;\n }\n for (const endpoint of layer.endpoints ?? []) {\n const existing = endpointById.get(endpoint.id);\n if (existing) {\n // SHALLOW field merge: overlay field replaces, absent field inherits.\n endpointById.set(endpoint.id, { ...existing, ...endpoint });\n } else {\n endpointOrder.push(endpoint.id);\n endpointById.set(endpoint.id, { ...endpoint });\n }\n provenance[`endpoint:${endpoint.id}`] = layerIndex;\n }\n for (const alias of layer.aliases ?? []) {\n if (!aliasById.has(alias.id)) {\n aliasOrder.push(alias.id);\n }\n // WHOLE replace by id (no chain splicing).\n aliasById.set(alias.id, { ...alias });\n provenance[`alias:${alias.id}`] = layerIndex;\n }\n if (layer.defaults !== undefined) {\n // `defaults` object replaces whole.\n mergedDefaults = { ...layer.defaults };\n }\n });\n\n const registry: ConnectionRegistry = {\n version: 2,\n ...(schema !== undefined ? { $schema: schema } : {}),\n ...(mergedDefaults !== undefined ? { defaults: mergedDefaults } : {}),\n endpoints: endpointOrder.map((id) => endpointById.get(id)!),\n ...(aliasOrder.length > 0\n ? { aliases: aliasOrder.map((id) => aliasById.get(id)!) }\n : {}),\n };\n\n return { registry, issues, provenance };\n}\n", "/**\n * @vedmalex/ai-connect/registry \u2014 parse.ts (TASK-038 TS-3).\n *\n * `detectRegistryVersion` + `parseRegistry` and every parse-time issue producer. PURE: no\n * `node:` imports, no `process.*`. Document-level shape/version faults THROW an\n * AiConnectError(validation_error); entry-level faults become error-level issues and the entry is\n * excluded from the returned registry (CD-65 graceful-recovery).\n */\n\nimport { AiConnectError } from \"../errors.js\";\nimport type {\n ConnectionRegistry,\n RegistryAlias,\n RegistryEndpoint,\n RegistryIssue,\n RegistryIssueCode,\n RegistryIssueLevel,\n RegistryVersion,\n} from \"./types.js\";\n\nconst BUILTIN_PROVIDERS = new Set([\"openai\", \"anthropic\", \"gemini\"]);\nconst KNOWN_TRANSPORTS = new Set([\"api\", \"acp\", \"cli\", \"server\"]);\nconst TRANSPORT_SYNONYMS: Record<string, \"api\"> = { http: \"api\", proxy: \"api\" };\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isLoopbackBaseUrl(baseUrl: string | undefined): boolean {\n if (!baseUrl) {\n return false;\n }\n // Pure string check (no URL global dependency for exotic command-like inputs): look for a\n // loopback host token in the authority section.\n const lowered = baseUrl.toLowerCase();\n return (\n lowered.includes(\"127.0.0.1\") ||\n lowered.includes(\"localhost\") ||\n lowered.includes(\"[::1]\") ||\n lowered.includes(\"://::1\")\n );\n}\n\ninterface IssueSink {\n push(issue: RegistryIssue): void;\n}\n\nfunction makeIssue(\n level: RegistryIssueLevel,\n code: RegistryIssueCode,\n path: string,\n message: string,\n ids?: { endpointId?: string; aliasId?: string },\n): RegistryIssue {\n return {\n level,\n code,\n path,\n message,\n ...(ids?.endpointId !== undefined ? { endpointId: ids.endpointId } : {}),\n ...(ids?.aliasId !== undefined ? { aliasId: ids.aliasId } : {}),\n };\n}\n\n/**\n * Heuristic document classification. A v2 document is the ONLY shape that carries a `version`\n * key; a v1 endpoints file has `endpoints` (no version); a v1 aliases file has `aliases` (no\n * version, no endpoints). Everything else is `unknown`.\n */\nexport function detectRegistryVersion(doc: unknown): RegistryVersion {\n if (!isObject(doc)) {\n return \"unknown\";\n }\n if (\"version\" in doc) {\n return \"v2\";\n }\n if (Array.isArray(doc.endpoints)) {\n return \"v1-endpoints\";\n }\n if (Array.isArray(doc.aliases)) {\n return \"v1-aliases\";\n }\n return \"unknown\";\n}\n\nfunction normalizeTransportKind(\n raw: unknown,\n): \"api\" | \"acp\" | \"cli\" | \"server\" | undefined {\n if (typeof raw !== \"string\") {\n return undefined;\n }\n const trimmed = raw.trim().toLowerCase();\n if (TRANSPORT_SYNONYMS[trimmed]) {\n return TRANSPORT_SYNONYMS[trimmed];\n }\n if (KNOWN_TRANSPORTS.has(trimmed)) {\n return trimmed as \"api\" | \"acp\" | \"cli\" | \"server\";\n }\n return undefined;\n}\n\n/** Validate a single endpoint; returns the cleaned endpoint or null when it must be excluded. */\nfunction parseEndpoint(\n raw: unknown,\n index: number,\n sink: IssueSink,\n): RegistryEndpoint | null {\n const path = `endpoints[${index}]`;\n if (!isObject(raw) || typeof raw.id !== \"string\" || raw.id.trim().length === 0) {\n sink.push(\n makeIssue(\n \"error\",\n \"invalid-endpoint-shape\",\n path,\n `Endpoint at ${path} must be an object with a non-empty string id.`,\n ),\n );\n return null;\n }\n const id = raw.id.trim();\n const kind = typeof raw.kind === \"string\" && raw.kind.trim() ? raw.kind.trim() : \"text\";\n const transport = normalizeTransportKind(raw.transport);\n if (!transport) {\n sink.push(\n makeIssue(\n \"error\",\n \"unknown-transport\",\n `${path}.transport`,\n `Endpoint \"${id}\" declares unknown transport ${JSON.stringify(raw.transport)}.`,\n { endpointId: id },\n ),\n );\n return null;\n }\n\n const isApi = transport === \"api\";\n const baseUrl = typeof raw.baseUrl === \"string\" ? raw.baseUrl.trim() : undefined;\n const command = typeof raw.command === \"string\" ? raw.command.trim() : undefined;\n const transportId =\n typeof raw.transportId === \"string\" && raw.transportId.trim()\n ? raw.transportId.trim()\n : undefined;\n\n // baseUrl/command placement conflicts.\n if (isApi && command) {\n sink.push(\n makeIssue(\n \"error\",\n \"baseurl-command-conflict\",\n `${path}.command`,\n `Endpoint \"${id}\" is transport \"api\" but declares a command spawn line.`,\n { endpointId: id },\n ),\n );\n return null;\n }\n if (!isApi && baseUrl && transport !== \"server\") {\n sink.push(\n makeIssue(\n \"error\",\n \"baseurl-command-conflict\",\n `${path}.baseUrl`,\n `Endpoint \"${id}\" is transport \"${transport}\" but declares a baseUrl.`,\n { endpointId: id },\n ),\n );\n return null;\n }\n if (isApi && !baseUrl) {\n sink.push(\n makeIssue(\n \"error\",\n \"missing-baseurl\",\n `${path}.baseUrl`,\n `Endpoint \"${id}\" (api) requires a baseUrl.`,\n { endpointId: id },\n ),\n );\n return null;\n }\n if (!isApi && !command && !transportId) {\n sink.push(\n makeIssue(\n \"error\",\n \"missing-command\",\n `${path}.command`,\n `Endpoint \"${id}\" (${transport}) requires a command or a transportId catalog preset.`,\n { endpointId: id },\n ),\n );\n return null;\n }\n\n const headers = isObject(raw.headers)\n ? (raw.headers as Record<string, string>)\n : undefined;\n const env = isObject(raw.env) ? (raw.env as Record<string, string>) : undefined;\n const envPassthrough = Array.isArray(raw.envPassthrough)\n ? (raw.envPassthrough as string[])\n : undefined;\n\n if (headers && !isApi) {\n sink.push(\n makeIssue(\n \"error\",\n \"headers-on-local-endpoint\",\n `${path}.headers`,\n `Endpoint \"${id}\" sets headers on a non-api (\"${transport}\") transport; use env instead.`,\n { endpointId: id },\n ),\n );\n return null;\n }\n if (isApi && (env || envPassthrough)) {\n sink.push(\n makeIssue(\n \"error\",\n \"env-on-api-endpoint\",\n `${path}.env`,\n `Endpoint \"${id}\" sets env/envPassthrough on an api transport; use headers instead.`,\n { endpointId: id },\n ),\n );\n return null;\n }\n\n const auth = isObject(raw.auth) ? raw.auth : undefined;\n const inlineToken =\n auth && typeof auth.token === \"string\" ? auth.token.trim() : undefined;\n if (inlineToken && isApi && !isLoopbackBaseUrl(baseUrl)) {\n sink.push(\n makeIssue(\n \"warn\",\n \"inline-token-non-loopback\",\n `${path}.auth.token`,\n `Endpoint \"${id}\" carries an inline auth.token against a non-loopback baseUrl; prefer tokenEnv.`,\n { endpointId: id },\n ),\n );\n }\n\n const provider =\n typeof raw.provider === \"string\" && raw.provider.trim()\n ? raw.provider.trim()\n : \"openai\";\n const hasCliOptions = isObject(raw.cli);\n if (!BUILTIN_PROVIDERS.has(provider) && !hasCliOptions) {\n sink.push(\n makeIssue(\n \"warn\",\n \"custom-provider\",\n `${path}.provider`,\n `Endpoint \"${id}\" uses non-builtin provider \"${provider}\" without cli options; it needs a custom RouteHandler.`,\n { endpointId: id },\n ),\n );\n }\n\n // Carry the cleaned document through verbatim (opaque fields round-trip). We normalize only the\n // transport synonym and the derived defaults consumers depend on.\n const cleaned: RegistryEndpoint = {\n ...(raw as unknown as RegistryEndpoint),\n id,\n kind,\n provider,\n transport,\n };\n return cleaned;\n}\n\n/** Detect duplicate {endpointId, model} pairs within one chain (R1.6). */\nfunction chainHasDuplicateLeg(\n chain: Array<{ endpointId: string; model?: string }>,\n): { endpointId: string; model: string | undefined; index: number } | null {\n const seen = new Set<string>();\n for (let i = 0; i < chain.length; i++) {\n const leg = chain[i]!;\n // Join with an explicit U+0000 ESCAPE (never a raw byte \u2014 a raw NUL makes this file\n // grep-invisible) so (\"ab\",\"c\") and (\"a\",\"bc\") legs stay distinct in the dedup key.\n const key = `${leg.endpointId}\\u0000${leg.model ?? \"\"}`;\n if (seen.has(key)) {\n return { endpointId: leg.endpointId, model: leg.model, index: i };\n }\n seen.add(key);\n }\n return null;\n}\n\n/**\n * Parse + validate a claimed-v2 document. THROWS AiConnectError(validation_error) only for\n * document-level shape/version faults; entry-level faults become error-level issues.\n */\nexport function parseRegistry(\n doc: unknown,\n _opts?: { source?: string },\n): { registry: ConnectionRegistry; issues: RegistryIssue[] } {\n if (!isObject(doc)) {\n throw new AiConnectError(\n \"validation_error\",\n \"Connection registry must be a JSON object.\",\n );\n }\n if (doc.version !== 2) {\n throw new AiConnectError(\n \"validation_error\",\n `Connection registry version must be 2 (received ${JSON.stringify(doc.version)}).`,\n );\n }\n if (!Array.isArray(doc.endpoints)) {\n throw new AiConnectError(\n \"validation_error\",\n \"Connection registry must declare an `endpoints` array.\",\n );\n }\n\n const issues: RegistryIssue[] = [];\n const sink: IssueSink = { push: (i) => issues.push(i) };\n\n // --- endpoints ---\n const endpoints: RegistryEndpoint[] = [];\n const declaredEndpointIds = new Set<string>();\n const keptEndpointIds = new Set<string>();\n doc.endpoints.forEach((rawEndpoint, index) => {\n const rawId =\n isObject(rawEndpoint) && typeof rawEndpoint.id === \"string\"\n ? rawEndpoint.id.trim()\n : undefined;\n if (rawId) {\n if (declaredEndpointIds.has(rawId)) {\n sink.push(\n makeIssue(\n \"error\",\n \"duplicate-endpoint-id\",\n `endpoints[${index}].id`,\n `Duplicate endpoint id \"${rawId}\"; the later declaration is dropped.`,\n { endpointId: rawId },\n ),\n );\n return;\n }\n declaredEndpointIds.add(rawId);\n }\n const cleaned = parseEndpoint(rawEndpoint, index, sink);\n if (cleaned) {\n endpoints.push(cleaned);\n keptEndpointIds.add(cleaned.id);\n }\n });\n\n // Map id -> kept endpoint for chain validation.\n const endpointById = new Map<string, RegistryEndpoint>();\n for (const endpoint of endpoints) {\n endpointById.set(endpoint.id, endpoint);\n }\n\n // --- aliases ---\n const aliases: RegistryAlias[] = [];\n const declaredAliasIds = new Set<string>();\n const rawAliases = Array.isArray(doc.aliases) ? doc.aliases : [];\n rawAliases.forEach((rawAlias, index) => {\n const path = `aliases[${index}]`;\n if (\n !isObject(rawAlias) ||\n typeof rawAlias.id !== \"string\" ||\n rawAlias.id.trim().length === 0 ||\n !Array.isArray(rawAlias.chain)\n ) {\n sink.push(\n makeIssue(\n \"error\",\n \"invalid-alias-shape\",\n path,\n `Alias at ${path} must be an object with a non-empty id and a chain array.`,\n ),\n );\n return;\n }\n const aliasId = rawAlias.id.trim();\n if (declaredAliasIds.has(aliasId)) {\n sink.push(\n makeIssue(\n \"error\",\n \"duplicate-alias-id\",\n `${path}.id`,\n `Duplicate alias id \"${aliasId}\"; the later declaration is dropped.`,\n { aliasId },\n ),\n );\n return;\n }\n declaredAliasIds.add(aliasId);\n\n const chain = rawAlias.chain as Array<{ endpointId: string; model?: string }>;\n\n // duplicate-chain-leg (R1.6) \u2014 a whole-alias error.\n const dup = chainHasDuplicateLeg(chain);\n if (dup) {\n sink.push(\n makeIssue(\n \"error\",\n \"duplicate-chain-leg\",\n `${path}.chain[${dup.index}]`,\n `Alias \"${aliasId}\" repeats leg {endpointId:\"${dup.endpointId}\", model:${JSON.stringify(dup.model)}}; duplicate legs are rejected (R1.6).`,\n { aliasId },\n ),\n );\n }\n\n chain.forEach((leg, legIndex) => {\n const legPath = `${path}.chain[${legIndex}]`;\n const target = endpointById.get(leg.endpointId);\n if (!declaredEndpointIds.has(leg.endpointId)) {\n sink.push(\n makeIssue(\n \"error\",\n \"chain-endpoint-missing\",\n legPath,\n `Alias \"${aliasId}\" leg ${legIndex} references unknown endpoint \"${leg.endpointId}\".`,\n { aliasId, endpointId: leg.endpointId },\n ),\n );\n return;\n }\n if (!target) {\n // Declared but excluded for a structural fault already reported on the endpoint.\n return;\n }\n if ((target.kind ?? \"text\") !== \"text\") {\n sink.push(\n makeIssue(\n \"error\",\n \"chain-endpoint-non-text\",\n legPath,\n `Alias \"${aliasId}\" leg ${legIndex} references non-text endpoint \"${leg.endpointId}\" (kind \"${target.kind}\").`,\n { aliasId, endpointId: leg.endpointId },\n ),\n );\n return;\n }\n const model = leg.model ?? target.defaultModel;\n if (!model) {\n sink.push(\n makeIssue(\n \"error\",\n \"chain-leg-no-model\",\n legPath,\n `Alias \"${aliasId}\" leg ${legIndex} omits model and endpoint \"${leg.endpointId}\" has no defaultModel.`,\n { aliasId, endpointId: leg.endpointId },\n ),\n );\n return;\n }\n const allowlistMode = target.allowlistMode ?? \"shortlist\";\n if (allowlistMode === \"strict\" && leg.model) {\n const declaredModels = (target.models ?? []).map((entry) =>\n typeof entry === \"string\" ? entry : entry.id,\n );\n if (!declaredModels.includes(leg.model)) {\n sink.push(\n makeIssue(\n \"error\",\n \"chain-model-not-allowed\",\n legPath,\n `Alias \"${aliasId}\" leg ${legIndex} requests model \"${leg.model}\" not in endpoint \"${leg.endpointId}\" models[] under allowlistMode \"strict\".`,\n { aliasId, endpointId: leg.endpointId },\n ),\n );\n }\n }\n });\n\n aliases.push({\n ...(rawAlias as unknown as RegistryAlias),\n id: aliasId,\n chain,\n });\n });\n\n // --- defaults ---\n let defaults: ConnectionRegistry[\"defaults\"];\n if (isObject(doc.defaults)) {\n const rawDefaults = doc.defaults;\n const nextDefaults: { alias?: string; endpoint?: string } = {};\n if (typeof rawDefaults.alias === \"string\" && rawDefaults.alias.trim()) {\n const aliasId = rawDefaults.alias.trim();\n nextDefaults.alias = aliasId;\n const target = aliases.find((a) => a.id === aliasId);\n if (!declaredAliasIds.has(aliasId)) {\n sink.push(\n makeIssue(\n \"error\",\n \"defaults-target-missing\",\n \"defaults.alias\",\n `defaults.alias references unknown alias \"${aliasId}\".`,\n { aliasId },\n ),\n );\n } else if (target && target.enabled === false) {\n sink.push(\n makeIssue(\n \"warn\",\n \"defaults-target-unusable\",\n \"defaults.alias\",\n `defaults.alias \"${aliasId}\" targets a disabled alias.`,\n { aliasId },\n ),\n );\n }\n }\n if (typeof rawDefaults.endpoint === \"string\" && rawDefaults.endpoint.trim()) {\n const endpointId = rawDefaults.endpoint.trim();\n nextDefaults.endpoint = endpointId;\n const target = endpointById.get(endpointId);\n if (!declaredEndpointIds.has(endpointId)) {\n sink.push(\n makeIssue(\n \"error\",\n \"defaults-target-missing\",\n \"defaults.endpoint\",\n `defaults.endpoint references unknown endpoint \"${endpointId}\".`,\n { endpointId },\n ),\n );\n } else if (target && target.enabled === false) {\n sink.push(\n makeIssue(\n \"warn\",\n \"defaults-target-unusable\",\n \"defaults.endpoint\",\n `defaults.endpoint \"${endpointId}\" targets a disabled endpoint.`,\n { endpointId },\n ),\n );\n }\n }\n if (nextDefaults.alias !== undefined || nextDefaults.endpoint !== undefined) {\n defaults = nextDefaults;\n }\n }\n\n const registry: ConnectionRegistry = {\n version: 2,\n ...(doc.$schema && typeof doc.$schema === \"string\" ? { $schema: doc.$schema } : {}),\n ...(defaults ? { defaults } : {}),\n endpoints,\n ...(aliases.length > 0 ? { aliases } : {}),\n };\n\n return { registry, issues };\n}\n", "/**\n * @vedmalex/ai-connect/registry \u2014 save.ts (TASK-040, write-API pure core).\n *\n * `stripRegistrySecrets`, `serializeRegistry`, and `mergeForSave` are the browser-safe building\n * blocks behind the node/bun-only `saveConnectionRegistry`/`saveConnectionSecrets` in `fs.ts`\n * (the ONLY file allowed filesystem access \u2014 see its header). PURE: no `node:` imports, no\n * `process.*` \u2014 same discipline as `merge.ts`/`parse.ts`. `NodeJS.ProcessEnv` below is an\n * ambient TYPE reference only (no runtime `node:` import), consistent with that discipline.\n */\n\nimport { mergeRegistries } from \"./merge.js\";\nimport type { ConnectionRegistry, RegistryAlias, RegistryEndpoint } from \"./types.js\";\n\nexport interface StripRegistrySecretsResult {\n registry: ConnectionRegistry;\n /** JSON-pointer-ish paths of every field removed, e.g. `endpoints[3].auth.token`. */\n strippedFields: string[];\n}\n\n/**\n * Remove `auth.token` and `auth.fallbackTokens` from every endpoint. Does NOT mutate the input\n * registry. `auth` is dropped entirely only when stripping empties it out (no other fields such\n * as `tokenEnv`/`methodId`/`params` remain); otherwise the surviving fields are kept.\n *\n * Boundary (documented in docs/connection-registry.md \"Write API\"): this is defense-in-depth for\n * the main secret class (inline tokens), not a universal classifier \u2014 `env`/`auth.params` values\n * that originated from a secrets-overlay merge are indistinguishable from plain config once\n * merged and are NOT stripped.\n */\nexport function stripRegistrySecrets(registry: ConnectionRegistry): StripRegistrySecretsResult {\n const strippedFields: string[] = [];\n\n const endpoints = registry.endpoints.map((endpoint, index) => {\n const auth = endpoint.auth;\n if (!auth) {\n return endpoint;\n }\n const hasToken = auth.token !== undefined;\n const hasFallbackTokens = auth.fallbackTokens !== undefined;\n if (!hasToken && !hasFallbackTokens) {\n return endpoint;\n }\n\n const { token: _token, fallbackTokens: _fallbackTokens, ...restAuth } = auth;\n if (hasToken) {\n strippedFields.push(`endpoints[${index}].auth.token`);\n }\n if (hasFallbackTokens) {\n strippedFields.push(`endpoints[${index}].auth.fallbackTokens`);\n }\n\n const nextEndpoint: RegistryEndpoint = { ...endpoint };\n if (Object.keys(restAuth).length > 0) {\n nextEndpoint.auth = restAuth;\n } else {\n delete nextEndpoint.auth;\n }\n return nextEndpoint;\n });\n\n return {\n registry: { ...registry, endpoints },\n strippedFields,\n };\n}\n\n/**\n * Deterministic JSON serialization for a registry document: 2-space indent, a stable top-level\n * key order (`$schema`, `version`, `defaults`, `endpoints`, `aliases` \u2014 absent keys omitted), a\n * trailing newline. Arrays (`endpoints`, `aliases`, each alias's `chain`) are written in the\n * order given on the input object \u2014 no reordering (chain-order guarantee, UR-005).\n */\nexport function serializeRegistry(registry: ConnectionRegistry): string {\n const ordered: Record<string, unknown> = {};\n if (registry.$schema !== undefined) {\n ordered.$schema = registry.$schema;\n }\n ordered.version = registry.version;\n if (registry.defaults !== undefined) {\n ordered.defaults = registry.defaults;\n }\n ordered.endpoints = registry.endpoints;\n if (registry.aliases !== undefined) {\n ordered.aliases = registry.aliases;\n }\n return `${JSON.stringify(ordered, null, 2)}\\n`;\n}\n\n/**\n * Thin wrapper over {@link mergeRegistries} for the write path: `incoming` overlays `existing`\n * (later-wins shallow per-id endpoint merge, whole-replace aliases, `defaults` replaced whole \u2014\n * identical semantics to the read-path layer merge).\n */\nexport function mergeForSave(\n existing: ConnectionRegistry,\n incoming: ConnectionRegistry,\n): ConnectionRegistry {\n return mergeRegistries([existing, incoming]).registry;\n}\n\nexport interface SaveConnectionRegistryOptions {\n /** Mutually exclusive with `path`; neither supplied is a `validation_error`. */\n layer?: \"user\" | \"project\";\n /** Explicit target file; wins over `layer` when both are supplied. */\n path?: string;\n /** Anchors the project layer and a relative `path`. Default `process.cwd()`. */\n cwd?: string;\n /** Consulted for `HOME`/`XDG_CONFIG_HOME`. Default `process.env`. */\n env?: NodeJS.ProcessEnv;\n /** `'merge'` (default) upserts onto the existing target; `'replace'` overwrites it whole. */\n mode?: \"merge\" | \"replace\";\n /** Default `true` \u2014 rotate a numbered `<stem>.<n>.json` backup before overwriting. */\n backup?: boolean;\n}\n\nexport interface SaveRegistryResult {\n path: string;\n mode: \"merge\" | \"replace\";\n /** `true` when the target file did not exist before this save. */\n created: boolean;\n /** Set only when a backup was actually written (target existed, `backup` not disabled). */\n backupPath?: string;\n /** Every `auth.token`/`auth.fallbackTokens` path removed before write (see {@link stripRegistrySecrets}). */\n strippedFields: string[];\n}\n\n/**\n * \"Zero schema\" partial layer for `connections.secrets.json` \u2014 mirrors the shape `fs.ts`'s\n * `resolveSecretsLayer` already reads on the load path. NOT run through `parseRegistry` (entries\n * may omit fields such as `transport` that a full v2 endpoint requires); it is merged directly.\n */\nexport interface RegistrySecretsFragment {\n endpoints?: RegistryEndpoint[];\n aliases?: RegistryAlias[];\n defaults?: ConnectionRegistry[\"defaults\"];\n}\n\nexport interface SaveConnectionSecretsOptions {\n /** Mutually exclusive with `path`; neither supplied is a `validation_error`. */\n layer?: \"user\" | \"project\";\n /** Explicit target file; wins over `layer` when both are supplied. */\n path?: string;\n /** Anchors the project layer and a relative `path`. Default `process.cwd()`. */\n cwd?: string;\n /** Consulted for `HOME`/`XDG_CONFIG_HOME`. Default `process.env`. */\n env?: NodeJS.ProcessEnv;\n /** `'merge'` (default) \u2014 per-id shallow upsert onto the existing fragment; `'replace'` overwrites it whole. */\n mode?: \"merge\" | \"replace\";\n /** Default `true` \u2014 rotate a numbered `<stem>.<n>.json` backup before overwriting. */\n backup?: boolean;\n}\n\nexport interface SaveSecretsResult {\n path: string;\n mode: \"merge\" | \"replace\";\n /** `true` when the target file did not exist before this save. */\n created: boolean;\n /** Set only when a backup was actually written (target existed, `backup` not disabled). */\n backupPath?: string;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["path", "BUILTIN_PROVIDERS", "isObject", "path", "isObject"]
|
|
7
7
|
}
|