@wildo-ai/wildo-module-client 1.1.3 → 1.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/esm/__tests__/lockfile-repair.service.test.d.ts +0 -0
  2. package/dist/esm/__tests__/lockfile-repair.service.test.d.ts.map +0 -0
  3. package/dist/esm/__tests__/lockfile-repair.service.test.js.map +0 -0
  4. package/dist/esm/__tests__/module-adopter.service.test.d.ts +0 -0
  5. package/dist/esm/__tests__/module-adopter.service.test.d.ts.map +0 -0
  6. package/dist/esm/__tests__/module-adopter.service.test.js.map +0 -0
  7. package/dist/esm/__tests__/module-packager.service.test.d.ts +0 -0
  8. package/dist/esm/__tests__/module-packager.service.test.d.ts.map +0 -0
  9. package/dist/esm/__tests__/module-packager.service.test.js.map +0 -0
  10. package/dist/esm/__tests__/provider-materialization-hook.test.d.ts +0 -0
  11. package/dist/esm/__tests__/provider-materialization-hook.test.d.ts.map +0 -0
  12. package/dist/esm/__tests__/provider-materialization-hook.test.js.map +0 -0
  13. package/dist/esm/__tests__/provider-sync-runner.test.d.ts +0 -0
  14. package/dist/esm/__tests__/provider-sync-runner.test.d.ts.map +0 -0
  15. package/dist/esm/__tests__/provider-sync-runner.test.js.map +0 -0
  16. package/dist/esm/__tests__/registry-lockfile.service.test.d.ts +0 -0
  17. package/dist/esm/__tests__/registry-lockfile.service.test.d.ts.map +0 -0
  18. package/dist/esm/__tests__/registry-lockfile.service.test.js.map +0 -0
  19. package/dist/esm/__tests__/registry-server-client.test.d.ts +0 -0
  20. package/dist/esm/__tests__/registry-server-client.test.d.ts.map +0 -0
  21. package/dist/esm/__tests__/registry-server-client.test.js.map +0 -0
  22. package/dist/esm/services/lockfile-repair.service.d.ts +8 -23
  23. package/dist/esm/services/lockfile-repair.service.d.ts.map +1 -1
  24. package/dist/esm/services/lockfile-repair.service.js +10 -28
  25. package/dist/esm/services/lockfile-repair.service.js.map +1 -1
  26. package/dist/esm/services/registry-companion-client.d.ts +26 -4
  27. package/dist/esm/services/registry-companion-client.d.ts.map +1 -1
  28. package/dist/esm/services/registry-companion-client.js +15 -1
  29. package/dist/esm/services/registry-companion-client.js.map +1 -1
  30. package/dist/tsconfig.build.tsbuildinfo +1 -1
  31. package/dist/tsconfig.tsbuildinfo +1 -1
  32. package/package.json +2 -2
  33. package/src/__tests__/lockfile-repair.service.test.ts +2 -2
  34. package/src/services/lockfile-repair.service.ts +9 -34
  35. package/src/services/registry-companion-client.ts +40 -6
@@ -1,21 +1,12 @@
1
1
  /**
2
- * `LockfileRepairService` — Phase 2 read-only lockfile diagnostics.
2
+ * `LockfileRepairService` — read-only lockfile diagnostics.
3
3
  *
4
- * Backs `wildo registry repair-lockfile` (CLI command lands in a
5
- * later step). The service does NOT mutate the lockfile by
6
- * default — its job is to surface a structured diagnostic the
7
- * operator can act on:
8
- * - validate the canonical envelope shape;
9
- * - validate `sections.modules` against
10
- * `RegistryModulesLockSectionSchema`;
11
- * - report which `installed[*].files` are missing on disk
12
- * (drift between the lockfile and the source tree);
13
- * - report which `installed[*].artifactDigest` strings are not
14
- * `Sha256DigestSchema`-shaped (residue from pre-tightening
15
- * publishes).
16
- *
17
- * Mutating repair is a Phase 7 deliverable — surfacing the gaps is
18
- * what unblocks the operator now.
4
+ * Backs `wildo registry repair-lockfile`. Strict envelope and module-section
5
+ * validation runs before file inspection. Malformed records, including invalid
6
+ * artifact digests, require restoration from a trusted record; they are never
7
+ * admitted into the automatic repair path. Valid records can report missing
8
+ * source files, which the CLI can re-fetch at their recorded exact version.
9
+ * This inspector does not mutate files.
19
10
  *
20
11
  * `i18n` validation is intentionally NOT performed here (KD-15 +
21
12
  * Review-pass-2 / MEDIUM-5): repair is the wrong place to
@@ -39,11 +30,6 @@ export type LockfileRepairFinding = {
39
30
  kind: 'module-file-missing';
40
31
  moduleId: string;
41
32
  filePath: string;
42
- } | {
43
- severity: 'warn';
44
- kind: 'module-digest-not-sha256';
45
- moduleId: string;
46
- observedDigest: string;
47
33
  };
48
34
  export interface LockfileRepairReport {
49
35
  /** Absolute path the report covers. */
@@ -60,8 +46,7 @@ export declare const LockfileRepairService: {
60
46
  */
61
47
  readonly inspect: (saasRoot: string) => Promise<LockfileRepairReport>;
62
48
  /**
63
- * Per-entry diagnostic helper. Exposed so Phase 7 mutating-
64
- * repair flows can reuse the same predicates.
49
+ * Missing-file diagnostics for an entry already validated by the strict section schema.
65
50
  */
66
51
  readonly collectEntryFindings: (saasRoot: string, moduleId: string, entry: RegistryModulesLockEntry, findings: LockfileRepairFinding[]) => Promise<void>;
67
52
  };
@@ -1 +1 @@
1
- {"version":3,"file":"lockfile-repair.service.d.ts","sourceRoot":"","sources":["../../../../src/services/lockfile-repair.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAMH,OAAO,EAIL,KAAK,wBAAwB,EAE9B,MAAM,+BAA+B,CAAC;AAcvC,MAAM,MAAM,qBAAqB,GAC7B;IACE,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,oBAAoB,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GACD;IACE,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,2BAA2B,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GACD;IACE,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,qBAAqB,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,GACD;IACE,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,0BAA0B,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEN,MAAM,WAAW,oBAAoB;IACnC,uCAAuC;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,SAAS,qBAAqB,EAAE,CAAC;CAC5C;AAWD,eAAO,MAAM,qBAAqB;IAChC,iDAAiD;iCAC/B,MAAM,KAAG,MAAM;IAIjC;;OAEG;iCACqB,MAAM,KAAG,OAAO,CAAC,oBAAoB,CAAC;IA8E9D;;;OAGG;8CAES,MAAM,YACN,MAAM,SACT,wBAAwB,YACrB,qBAAqB,EAAE,KAChC,OAAO,CAAC,IAAI,CAAC;CAqBR,CAAC"}
1
+ {"version":3,"file":"lockfile-repair.service.d.ts","sourceRoot":"","sources":["../../../../src/services/lockfile-repair.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAMH,OAAO,EAGL,KAAK,wBAAwB,EAE9B,MAAM,+BAA+B,CAAC;AAcvC,MAAM,MAAM,qBAAqB,GAC7B;IACE,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,oBAAoB,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GACD;IACE,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,2BAA2B,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GACD;IACE,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,qBAAqB,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEN,MAAM,WAAW,oBAAoB;IACnC,uCAAuC;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,SAAS,qBAAqB,EAAE,CAAC;CAC5C;AAWD,eAAO,MAAM,qBAAqB;IAChC,iDAAiD;iCAC/B,MAAM,KAAG,MAAM;IAIjC;;OAEG;iCACqB,MAAM,KAAG,OAAO,CAAC,oBAAoB,CAAC;IA8E9D;;OAEG;8CAES,MAAM,YACN,MAAM,SACT,wBAAwB,YACrB,qBAAqB,EAAE,KAChC,OAAO,CAAC,IAAI,CAAC;CAaR,CAAC"}
@@ -1,21 +1,12 @@
1
1
  /**
2
- * `LockfileRepairService` — Phase 2 read-only lockfile diagnostics.
2
+ * `LockfileRepairService` — read-only lockfile diagnostics.
3
3
  *
4
- * Backs `wildo registry repair-lockfile` (CLI command lands in a
5
- * later step). The service does NOT mutate the lockfile by
6
- * default — its job is to surface a structured diagnostic the
7
- * operator can act on:
8
- * - validate the canonical envelope shape;
9
- * - validate `sections.modules` against
10
- * `RegistryModulesLockSectionSchema`;
11
- * - report which `installed[*].files` are missing on disk
12
- * (drift between the lockfile and the source tree);
13
- * - report which `installed[*].artifactDigest` strings are not
14
- * `Sha256DigestSchema`-shaped (residue from pre-tightening
15
- * publishes).
16
- *
17
- * Mutating repair is a Phase 7 deliverable — surfacing the gaps is
18
- * what unblocks the operator now.
4
+ * Backs `wildo registry repair-lockfile`. Strict envelope and module-section
5
+ * validation runs before file inspection. Malformed records, including invalid
6
+ * artifact digests, require restoration from a trusted record; they are never
7
+ * admitted into the automatic repair path. Valid records can report missing
8
+ * source files, which the CLI can re-fetch at their recorded exact version.
9
+ * This inspector does not mutate files.
19
10
  *
20
11
  * `i18n` validation is intentionally NOT performed here (KD-15 +
21
12
  * Review-pass-2 / MEDIUM-5): repair is the wrong place to
@@ -27,7 +18,7 @@ import { promises as fs } from 'node:fs';
27
18
  import * as path from 'node:path';
28
19
  import { z } from 'zod';
29
20
  import { formatZodIssuesAsSingleLine } from './_zod-issue-formatting.js';
30
- import { RegistryModulesLockSectionSchema, Sha256DigestSchema, getWildoSaasLockPath, } from '@wildo-ai/platform-config-lib';
21
+ import { RegistryModulesLockSectionSchema, getWildoSaasLockPath, } from '@wildo-ai/platform-config-lib';
31
22
  /**
32
23
  * Permissive envelope schema used by repair only — it accepts any
33
24
  * `sections.*` payload without delegating to per-section schemas
@@ -116,7 +107,7 @@ export const LockfileRepairService = {
116
107
  findings.push({
117
108
  severity: 'error',
118
109
  kind: 'modules-section-malformed',
119
- message: '`sections.modules` failed schema validation.',
110
+ message: '`sections.modules` failed schema validation. Restore the affected record from a trusted lockfile revision; automatic repair does not infer missing or invalid metadata.',
120
111
  detail: formatZodIssuesAsSingleLine(parsed.error),
121
112
  });
122
113
  return { lockfilePath, ok: false, findings };
@@ -129,18 +120,9 @@ export const LockfileRepairService = {
129
120
  return { lockfilePath, ok, findings };
130
121
  },
131
122
  /**
132
- * Per-entry diagnostic helper. Exposed so Phase 7 mutating-
133
- * repair flows can reuse the same predicates.
123
+ * Missing-file diagnostics for an entry already validated by the strict section schema.
134
124
  */
135
125
  async collectEntryFindings(saasRoot, moduleId, entry, findings) {
136
- if (!Sha256DigestSchema.safeParse(entry.artifactDigest).success) {
137
- findings.push({
138
- severity: 'warn',
139
- kind: 'module-digest-not-sha256',
140
- moduleId,
141
- observedDigest: entry.artifactDigest,
142
- });
143
- }
144
126
  for (const filePath of entry.files) {
145
127
  const abs = path.resolve(saasRoot, filePath);
146
128
  if (!(await fileExists(abs))) {
@@ -1 +1 @@
1
- {"version":3,"file":"lockfile-repair.service.js","sourceRoot":"","sources":["../../../../src/services/lockfile-repair.service.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `LockfileRepairService` — Phase 2 read-only lockfile diagnostics.\n *\n * Backs `wildo registry repair-lockfile` (CLI command lands in a\n * later step). The service does NOT mutate the lockfile by\n * default — its job is to surface a structured diagnostic the\n * operator can act on:\n * - validate the canonical envelope shape;\n * - validate `sections.modules` against\n * `RegistryModulesLockSectionSchema`;\n * - report which `installed[*].files` are missing on disk\n * (drift between the lockfile and the source tree);\n * - report which `installed[*].artifactDigest` strings are not\n * `Sha256DigestSchema`-shaped (residue from pre-tightening\n * publishes).\n *\n * Mutating repair is a Phase 7 deliverable — surfacing the gaps is\n * what unblocks the operator now.\n *\n * `i18n` validation is intentionally NOT performed here (KD-15 +\n * Review-pass-2 / MEDIUM-5): repair is the wrong place to\n * second-guess companion-owned section semantics. The shared\n * lockfile library STILL validates i18n on the companion-owned\n * read path; this service only owns module diagnostics.\n */\n\nimport { promises as fs } from 'node:fs';\nimport * as path from 'node:path';\nimport { z } from 'zod';\nimport { formatZodIssuesAsSingleLine } from './_zod-issue-formatting';\nimport {\n RegistryModulesLockSectionSchema,\n Sha256DigestSchema,\n getWildoSaasLockPath,\n type RegistryModulesLockEntry,\n type RegistryModulesLockSection,\n} from '@wildo-ai/platform-config-lib';\n\n/**\n * Permissive envelope schema used by repair only — it accepts any\n * `sections.*` payload without delegating to per-section schemas\n * so the repair flow can isolate \"envelope is malformed\" from\n * \"envelope is fine but modules section is malformed\".\n */\nconst RelaxedEnvelopeSchema = z.strictObject({\n schemaVersion: z.literal(2),\n generatedAt: z.iso.datetime({ offset: true }),\n sections: z.record(z.string(), z.unknown()),\n});\n\nexport type LockfileRepairFinding =\n | {\n severity: 'error';\n kind: 'envelope-malformed';\n message: string;\n detail?: string;\n }\n | {\n severity: 'error';\n kind: 'modules-section-malformed';\n message: string;\n detail?: string;\n }\n | {\n severity: 'warn';\n kind: 'module-file-missing';\n moduleId: string;\n filePath: string;\n }\n | {\n severity: 'warn';\n kind: 'module-digest-not-sha256';\n moduleId: string;\n observedDigest: string;\n };\n\nexport interface LockfileRepairReport {\n /** Absolute path the report covers. */\n lockfilePath: string;\n /** True when no errors were produced (warnings allowed). */\n ok: boolean;\n findings: readonly LockfileRepairFinding[];\n}\n\nasync function fileExists(absPath: string): Promise<boolean> {\n try {\n await fs.access(absPath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport const LockfileRepairService = {\n /** Compute the lockfile path for a saas root. */\n pathFor(saasRoot: string): string {\n return getWildoSaasLockPath(saasRoot);\n },\n\n /**\n * Read the envelope + run every diagnostic. Read-only.\n */\n async inspect(saasRoot: string): Promise<LockfileRepairReport> {\n const lockfilePath = getWildoSaasLockPath(saasRoot);\n const findings: LockfileRepairFinding[] = [];\n\n // Read raw bytes ourselves so we can produce per-section\n // diagnostics: a malformed `modules` section under an\n // otherwise-valid envelope should report `modules-section-malformed`,\n // NOT `envelope-malformed` (the latter would tell the operator\n // to re-author the whole file when only one section is bad).\n let raw: string;\n try {\n raw = await fs.readFile(lockfilePath, 'utf8');\n } catch (err: unknown) {\n const errno = (err as NodeJS.ErrnoException)?.code;\n if (errno === 'ENOENT') {\n // Missing lockfile is \"ok\" from a repair POV.\n return { lockfilePath, ok: true, findings: [] };\n }\n findings.push({\n severity: 'error',\n kind: 'envelope-malformed',\n message: 'Failed to read lockfile bytes.',\n detail: err instanceof Error ? err.message : String(err),\n });\n return { lockfilePath, ok: false, findings };\n }\n\n let parsedJson: unknown;\n try {\n parsedJson = JSON.parse(raw);\n } catch (err) {\n findings.push({\n severity: 'error',\n kind: 'envelope-malformed',\n message: 'Lockfile is not valid JSON.',\n detail: err instanceof Error ? err.message : String(err),\n });\n return { lockfilePath, ok: false, findings };\n }\n\n const envelopeParsed = RelaxedEnvelopeSchema.safeParse(parsedJson);\n if (!envelopeParsed.success) {\n findings.push({\n severity: 'error',\n kind: 'envelope-malformed',\n message: 'Top-level envelope failed to parse.',\n detail: formatZodIssuesAsSingleLine(envelopeParsed.error),\n });\n return { lockfilePath, ok: false, findings };\n }\n const envelope = envelopeParsed.data;\n\n const modulesPayload = envelope.sections.modules;\n if (modulesPayload === undefined) {\n // No modules section present — nothing to inspect.\n return { lockfilePath, ok: true, findings };\n }\n\n const parsed = RegistryModulesLockSectionSchema.safeParse(modulesPayload);\n if (!parsed.success) {\n findings.push({\n severity: 'error',\n kind: 'modules-section-malformed',\n message: '`sections.modules` failed schema validation.',\n detail: formatZodIssuesAsSingleLine(parsed.error),\n });\n return { lockfilePath, ok: false, findings };\n }\n const modules: RegistryModulesLockSection = parsed.data;\n\n for (const [moduleId, entry] of Object.entries(modules.installed)) {\n await this.collectEntryFindings(saasRoot, moduleId, entry, findings);\n }\n\n const ok = findings.every((f) => f.severity !== 'error');\n return { lockfilePath, ok, findings };\n },\n\n /**\n * Per-entry diagnostic helper. Exposed so Phase 7 mutating-\n * repair flows can reuse the same predicates.\n */\n async collectEntryFindings(\n saasRoot: string,\n moduleId: string,\n entry: RegistryModulesLockEntry,\n findings: LockfileRepairFinding[],\n ): Promise<void> {\n if (!Sha256DigestSchema.safeParse(entry.artifactDigest).success) {\n findings.push({\n severity: 'warn',\n kind: 'module-digest-not-sha256',\n moduleId,\n observedDigest: entry.artifactDigest,\n });\n }\n for (const filePath of entry.files) {\n const abs = path.resolve(saasRoot, filePath);\n if (!(await fileExists(abs))) {\n findings.push({\n severity: 'warn',\n kind: 'module-file-missing',\n moduleId,\n filePath,\n });\n }\n }\n },\n} as const;\n\n// L6 (third-pass review): re-export removed — barrel-discipline rule\n// says \"Never re-export except in the root `index.ts`.\" Consumers\n// import the symbol directly from `@wildo-ai/platform-config-lib`.\n"]}
1
+ {"version":3,"file":"lockfile-repair.service.js","sourceRoot":"","sources":["../../../../src/services/lockfile-repair.service.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `LockfileRepairService` — read-only lockfile diagnostics.\n *\n * Backs `wildo registry repair-lockfile`. Strict envelope and module-section\n * validation runs before file inspection. Malformed records, including invalid\n * artifact digests, require restoration from a trusted record; they are never\n * admitted into the automatic repair path. Valid records can report missing\n * source files, which the CLI can re-fetch at their recorded exact version.\n * This inspector does not mutate files.\n *\n * `i18n` validation is intentionally NOT performed here (KD-15 +\n * Review-pass-2 / MEDIUM-5): repair is the wrong place to\n * second-guess companion-owned section semantics. The shared\n * lockfile library STILL validates i18n on the companion-owned\n * read path; this service only owns module diagnostics.\n */\n\nimport { promises as fs } from 'node:fs';\nimport * as path from 'node:path';\nimport { z } from 'zod';\nimport { formatZodIssuesAsSingleLine } from './_zod-issue-formatting';\nimport {\n RegistryModulesLockSectionSchema,\n getWildoSaasLockPath,\n type RegistryModulesLockEntry,\n type RegistryModulesLockSection,\n} from '@wildo-ai/platform-config-lib';\n\n/**\n * Permissive envelope schema used by repair only — it accepts any\n * `sections.*` payload without delegating to per-section schemas\n * so the repair flow can isolate \"envelope is malformed\" from\n * \"envelope is fine but modules section is malformed\".\n */\nconst RelaxedEnvelopeSchema = z.strictObject({\n schemaVersion: z.literal(2),\n generatedAt: z.iso.datetime({ offset: true }),\n sections: z.record(z.string(), z.unknown()),\n});\n\nexport type LockfileRepairFinding =\n | {\n severity: 'error';\n kind: 'envelope-malformed';\n message: string;\n detail?: string;\n }\n | {\n severity: 'error';\n kind: 'modules-section-malformed';\n message: string;\n detail?: string;\n }\n | {\n severity: 'warn';\n kind: 'module-file-missing';\n moduleId: string;\n filePath: string;\n };\n\nexport interface LockfileRepairReport {\n /** Absolute path the report covers. */\n lockfilePath: string;\n /** True when no errors were produced (warnings allowed). */\n ok: boolean;\n findings: readonly LockfileRepairFinding[];\n}\n\nasync function fileExists(absPath: string): Promise<boolean> {\n try {\n await fs.access(absPath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport const LockfileRepairService = {\n /** Compute the lockfile path for a saas root. */\n pathFor(saasRoot: string): string {\n return getWildoSaasLockPath(saasRoot);\n },\n\n /**\n * Read the envelope + run every diagnostic. Read-only.\n */\n async inspect(saasRoot: string): Promise<LockfileRepairReport> {\n const lockfilePath = getWildoSaasLockPath(saasRoot);\n const findings: LockfileRepairFinding[] = [];\n\n // Read raw bytes ourselves so we can produce per-section\n // diagnostics: a malformed `modules` section under an\n // otherwise-valid envelope should report `modules-section-malformed`,\n // NOT `envelope-malformed` (the latter would tell the operator\n // to re-author the whole file when only one section is bad).\n let raw: string;\n try {\n raw = await fs.readFile(lockfilePath, 'utf8');\n } catch (err: unknown) {\n const errno = (err as NodeJS.ErrnoException)?.code;\n if (errno === 'ENOENT') {\n // Missing lockfile is \"ok\" from a repair POV.\n return { lockfilePath, ok: true, findings: [] };\n }\n findings.push({\n severity: 'error',\n kind: 'envelope-malformed',\n message: 'Failed to read lockfile bytes.',\n detail: err instanceof Error ? err.message : String(err),\n });\n return { lockfilePath, ok: false, findings };\n }\n\n let parsedJson: unknown;\n try {\n parsedJson = JSON.parse(raw);\n } catch (err) {\n findings.push({\n severity: 'error',\n kind: 'envelope-malformed',\n message: 'Lockfile is not valid JSON.',\n detail: err instanceof Error ? err.message : String(err),\n });\n return { lockfilePath, ok: false, findings };\n }\n\n const envelopeParsed = RelaxedEnvelopeSchema.safeParse(parsedJson);\n if (!envelopeParsed.success) {\n findings.push({\n severity: 'error',\n kind: 'envelope-malformed',\n message: 'Top-level envelope failed to parse.',\n detail: formatZodIssuesAsSingleLine(envelopeParsed.error),\n });\n return { lockfilePath, ok: false, findings };\n }\n const envelope = envelopeParsed.data;\n\n const modulesPayload = envelope.sections.modules;\n if (modulesPayload === undefined) {\n // No modules section present — nothing to inspect.\n return { lockfilePath, ok: true, findings };\n }\n\n const parsed = RegistryModulesLockSectionSchema.safeParse(modulesPayload);\n if (!parsed.success) {\n findings.push({\n severity: 'error',\n kind: 'modules-section-malformed',\n message: '`sections.modules` failed schema validation. Restore the affected record from a trusted lockfile revision; automatic repair does not infer missing or invalid metadata.',\n detail: formatZodIssuesAsSingleLine(parsed.error),\n });\n return { lockfilePath, ok: false, findings };\n }\n const modules: RegistryModulesLockSection = parsed.data;\n\n for (const [moduleId, entry] of Object.entries(modules.installed)) {\n await this.collectEntryFindings(saasRoot, moduleId, entry, findings);\n }\n\n const ok = findings.every((f) => f.severity !== 'error');\n return { lockfilePath, ok, findings };\n },\n\n /**\n * Missing-file diagnostics for an entry already validated by the strict section schema.\n */\n async collectEntryFindings(\n saasRoot: string,\n moduleId: string,\n entry: RegistryModulesLockEntry,\n findings: LockfileRepairFinding[],\n ): Promise<void> {\n for (const filePath of entry.files) {\n const abs = path.resolve(saasRoot, filePath);\n if (!(await fileExists(abs))) {\n findings.push({\n severity: 'warn',\n kind: 'module-file-missing',\n moduleId,\n filePath,\n });\n }\n }\n },\n} as const;\n\n// L6 (third-pass review): re-export removed — barrel-discipline rule\n// says \"Never re-export except in the root `index.ts`.\" Consumers\n// import the symbol directly from `@wildo-ai/platform-config-lib`.\n"]}
@@ -13,12 +13,34 @@
13
13
  * `platform/factory/wildo-dev-companion/src/companion-api/controllers/`)
14
14
  * implements them with the actual ts-morph + filesystem work in
15
15
  * Phase 5+.
16
+ *
17
+ * The four mutations are GATED: the companion refuses any non-`GET` route that does not present the
18
+ * machine-local token, so they take `RegistryCompanionMutationOptions` and `ping` — a `GET` — does
19
+ * not. See that type for why the credential is required rather than optional.
16
20
  */
17
21
  import { type RegistryInstallRequest, type RegistryPublishRequest, type RegistryRemoveRequest, type RegistryRouteResult, type RegistryUpdateRequest } from '@wildo-ai/platform-config-lib';
18
22
  export interface RegistryCompanionClientOptions {
19
23
  baseUrl: string;
20
24
  timeoutMs?: number;
21
25
  }
26
+ /**
27
+ * What a MUTATING companion registry call needs, over and above an address.
28
+ *
29
+ * `companionToken` is REQUIRED, and that is the whole point of this second type. Every non-`GET`
30
+ * route under `/api/companion/**` is refused without it, and the four methods below have no caller
31
+ * yet (the routes answer a not-implemented envelope until Phase 5) — so making it required now costs
32
+ * nothing and means the caller Phase 5 writes cannot be written without the credential. An optional
33
+ * field would have been a rule to remember; a required one is a compile error.
34
+ *
35
+ * The value is the content of `<appRoot>/.wildo-saas/state/companion.token` (mode 0600), which the
36
+ * companion mints on every boot. This package has no notion of an application root, so reading it is
37
+ * deliberately the caller's job: `wildo-cli` already owns a hardened no-follow reader
38
+ * (`readCompanionTokenNoFollow`), and a second copy of a permission-checked read is precisely where
39
+ * the checks quietly diverge.
40
+ */
41
+ export interface RegistryCompanionMutationOptions extends RegistryCompanionClientOptions {
42
+ companionToken: string;
43
+ }
22
44
  export declare const RegistryCompanionClient: {
23
45
  /**
24
46
  * Liveness probe against `/health` (the same endpoint the
@@ -31,9 +53,9 @@ export declare const RegistryCompanionClient: {
31
53
  * the three `modules-registry.*.ts` files, refresh
32
54
  * `sections.modules`, run provider sync.
33
55
  */
34
- readonly install: (options: RegistryCompanionClientOptions, request: RegistryInstallRequest) => Promise<RegistryRouteResult>;
35
- readonly update: (options: RegistryCompanionClientOptions, request: RegistryUpdateRequest) => Promise<RegistryRouteResult>;
36
- readonly remove: (options: RegistryCompanionClientOptions, request: RegistryRemoveRequest) => Promise<RegistryRouteResult>;
37
- readonly publish: (options: RegistryCompanionClientOptions, request: RegistryPublishRequest) => Promise<RegistryRouteResult>;
56
+ readonly install: (options: RegistryCompanionMutationOptions, request: RegistryInstallRequest) => Promise<RegistryRouteResult>;
57
+ readonly update: (options: RegistryCompanionMutationOptions, request: RegistryUpdateRequest) => Promise<RegistryRouteResult>;
58
+ readonly remove: (options: RegistryCompanionMutationOptions, request: RegistryRemoveRequest) => Promise<RegistryRouteResult>;
59
+ readonly publish: (options: RegistryCompanionMutationOptions, request: RegistryPublishRequest) => Promise<RegistryRouteResult>;
38
60
  };
39
61
  //# sourceMappingURL=registry-companion-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry-companion-client.d.ts","sourceRoot":"","sources":["../../../../src/services/registry-companion-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAKL,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC3B,MAAM,+BAA+B,CAAC;AAMvC,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA2CD,eAAO,MAAM,uBAAuB;IAClC;;;OAGG;6BACiB,8BAA8B,KAAG,OAAO,CAAC,OAAO,CAAC;IAcrE;;;;;OAKG;gCAEQ,8BAA8B,WAC9B,sBAAsB,KAC9B,OAAO,CAAC,mBAAmB,CAAC;+BAMpB,8BAA8B,WAC9B,qBAAqB,KAC7B,OAAO,CAAC,mBAAmB,CAAC;+BAMpB,8BAA8B,WAC9B,qBAAqB,KAC7B,OAAO,CAAC,mBAAmB,CAAC;gCAMpB,8BAA8B,WAC9B,sBAAsB,KAC9B,OAAO,CAAC,mBAAmB,CAAC;CAIvB,CAAC"}
1
+ {"version":3,"file":"registry-companion-client.d.ts","sourceRoot":"","sources":["../../../../src/services/registry-companion-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAKL,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC3B,MAAM,+BAA+B,CAAC;AAiBvC,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,gCAAiC,SAAQ,8BAA8B;IACtF,cAAc,EAAE,MAAM,CAAC;CACxB;AA2CD,eAAO,MAAM,uBAAuB;IAClC;;;OAGG;6BACiB,8BAA8B,KAAG,OAAO,CAAC,OAAO,CAAC;IAcrE;;;;;OAKG;gCAEQ,gCAAgC,WAChC,sBAAsB,KAC9B,OAAO,CAAC,mBAAmB,CAAC;+BAMpB,gCAAgC,WAChC,qBAAqB,KAC7B,OAAO,CAAC,mBAAmB,CAAC;+BAMpB,gCAAgC,WAChC,qBAAqB,KAC7B,OAAO,CAAC,mBAAmB,CAAC;gCAMpB,gCAAgC,WAChC,sBAAsB,KAC9B,OAAO,CAAC,mBAAmB,CAAC;CAIvB,CAAC"}
@@ -13,16 +13,30 @@
13
13
  * `platform/factory/wildo-dev-companion/src/companion-api/controllers/`)
14
14
  * implements them with the actual ts-morph + filesystem work in
15
15
  * Phase 5+.
16
+ *
17
+ * The four mutations are GATED: the companion refuses any non-`GET` route that does not present the
18
+ * machine-local token, so they take `RegistryCompanionMutationOptions` and `ping` — a `GET` — does
19
+ * not. See that type for why the credential is required rather than optional.
16
20
  */
17
21
  import { REGISTRY_COMPANION_ROUTES, RegistryMalformedPayloadError, RegistryRouteResultSchema, RegistryUnexpectedStatusError, } from '@wildo-ai/platform-config-lib';
18
22
  import { fetchWithTimeout, joinUrl } from './_http-client.js';
19
23
  import { formatZodIssuesAsSingleLine } from './_zod-issue-formatting.js';
20
24
  const DIAGNOSTIC_LABEL = 'Companion registry';
25
+ /**
26
+ * The header the companion's P-29b token floor reads on a mutating route.
27
+ *
28
+ * A wire contract between two processes that share no package, so it is named at both ends rather
29
+ * than at neither. Its server twin is `COMPANION_TOKEN_HEADER` in the companion's
30
+ * `core/auth/companion-auth-floor.companion.service.ts`; the CLI's is in
31
+ * `wildo-cli/src/services/companion/companion-local-token.utils.ts`. Move one, move all three.
32
+ */
33
+ // semantic-literal-boundary: private companion HTTP protocol, translated only at this client edge.
34
+ const COMPANION_TOKEN_HEADER = 'x-wildo-companion-token';
21
35
  async function postJson(options, routePath, body) {
22
36
  const url = joinUrl(options.baseUrl, routePath);
23
37
  const res = await fetchWithTimeout(url, {
24
38
  method: 'POST',
25
- headers: { 'content-type': 'application/json' },
39
+ headers: { 'content-type': 'application/json', [COMPANION_TOKEN_HEADER]: options.companionToken },
26
40
  body: JSON.stringify(body),
27
41
  timeoutMs: options.timeoutMs,
28
42
  diagnosticLabel: DIAGNOSTIC_LABEL,
@@ -1 +1 @@
1
- {"version":3,"file":"registry-companion-client.js","sourceRoot":"","sources":["../../../../src/services/registry-companion-client.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `RegistryCompanionClient` — HTTP client for the local dev\n * companion's registry routes.\n *\n * Distinct from `RegistryServerClient` (which talks to the central\n * `platform-module-registry` server). The companion client targets\n * `/api/companion/registry/*` on `http://localhost:<companion-port>`\n * for AST/source mutations the CLI delegates to the companion\n * (KD-6).\n *\n * Phase 2 ships the four mutation routes as method calls; the\n * companion controller (under\n * `platform/factory/wildo-dev-companion/src/companion-api/controllers/`)\n * implements them with the actual ts-morph + filesystem work in\n * Phase 5+.\n */\n\nimport {\n REGISTRY_COMPANION_ROUTES,\n RegistryMalformedPayloadError,\n RegistryRouteResultSchema,\n RegistryUnexpectedStatusError,\n type RegistryInstallRequest,\n type RegistryPublishRequest,\n type RegistryRemoveRequest,\n type RegistryRouteResult,\n type RegistryUpdateRequest,\n} from '@wildo-ai/platform-config-lib';\nimport { fetchWithTimeout, joinUrl } from './_http-client';\nimport { formatZodIssuesAsSingleLine } from './_zod-issue-formatting';\n\nconst DIAGNOSTIC_LABEL = 'Companion registry';\n\nexport interface RegistryCompanionClientOptions {\n baseUrl: string;\n timeoutMs?: number;\n}\n\nasync function postJson<R>(\n options: RegistryCompanionClientOptions,\n routePath: string,\n body: unknown,\n): Promise<R> {\n const url = joinUrl(options.baseUrl, routePath);\n const res = await fetchWithTimeout(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n timeoutMs: options.timeoutMs,\n diagnosticLabel: DIAGNOSTIC_LABEL,\n });\n if (!res.ok) {\n // 3rd-pass M5: typed `RegistryUnexpectedStatusError` instead of\n // bare `new Error(...)` so callers can `isRegistryError(err)` and\n // discriminate by status code without re-parsing message text.\n // The companion deliberately reuses the same error vocabulary\n // as `RegistryServerClient` since the wire shape (status + URL +\n // action) is identical even though the lane is different.\n throw new RegistryUnexpectedStatusError(\n options.baseUrl,\n res.status,\n res.statusText,\n `companion ${routePath}`,\n );\n }\n // 6th-pass M3: typed JSON-parse — a misconfigured proxy returning\n // HTML at 2xx would otherwise throw a bare `SyntaxError` that\n // bypasses the typed-error contract.\n try {\n return (await res.json()) as R;\n } catch (err) {\n throw new RegistryMalformedPayloadError(\n options.baseUrl,\n `companion ${routePath}`,\n `response is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n}\n\nexport const RegistryCompanionClient = {\n /**\n * Liveness probe against `/health` (the same endpoint the\n * dev-supervisor monitors). Used by `wildo registry ping`.\n */\n async ping(options: RegistryCompanionClientOptions): Promise<boolean> {\n const url = joinUrl(options.baseUrl, '/health');\n try {\n const res = await fetchWithTimeout(url, {\n method: 'GET',\n timeoutMs: options.timeoutMs,\n diagnosticLabel: DIAGNOSTIC_LABEL,\n });\n return res.ok;\n } catch {\n return false;\n }\n },\n\n /**\n * Ask the companion to install a module: download bytes, copy\n * source, mutate `wildo.saas.config.ts > moduleRegistries` +\n * the three `modules-registry.*.ts` files, refresh\n * `sections.modules`, run provider sync.\n */\n async install(\n options: RegistryCompanionClientOptions,\n request: RegistryInstallRequest,\n ): Promise<RegistryRouteResult> {\n const raw = await postJson<unknown>(options, REGISTRY_COMPANION_ROUTES.install, request);\n return parseCompanionResult(options.baseUrl, REGISTRY_COMPANION_ROUTES.install, raw);\n },\n\n async update(\n options: RegistryCompanionClientOptions,\n request: RegistryUpdateRequest,\n ): Promise<RegistryRouteResult> {\n const raw = await postJson<unknown>(options, REGISTRY_COMPANION_ROUTES.update, request);\n return parseCompanionResult(options.baseUrl, REGISTRY_COMPANION_ROUTES.update, raw);\n },\n\n async remove(\n options: RegistryCompanionClientOptions,\n request: RegistryRemoveRequest,\n ): Promise<RegistryRouteResult> {\n const raw = await postJson<unknown>(options, REGISTRY_COMPANION_ROUTES.remove, request);\n return parseCompanionResult(options.baseUrl, REGISTRY_COMPANION_ROUTES.remove, raw);\n },\n\n async publish(\n options: RegistryCompanionClientOptions,\n request: RegistryPublishRequest,\n ): Promise<RegistryRouteResult> {\n const raw = await postJson<unknown>(options, REGISTRY_COMPANION_ROUTES.publish, request);\n return parseCompanionResult(options.baseUrl, REGISTRY_COMPANION_ROUTES.publish, raw);\n },\n} as const;\n\n/**\n * M5: thin wrapper around `RegistryRouteResultSchema.parse(raw)` that\n * converts a Zod parse failure into the typed\n * `RegistryMalformedPayloadError` instead of letting a bare `ZodError`\n * bubble up. Keeps the typed-handler contract aligned with\n * `RegistryServerClient`.\n */\nfunction parseCompanionResult(\n baseUrl: string,\n routePath: string,\n raw: unknown,\n): RegistryRouteResult {\n const parsed = RegistryRouteResultSchema.safeParse(raw);\n if (!parsed.success) {\n // 7th-pass C-L2: format the issues array for operator\n // readability instead of using Zod 4's JSON-stringified\n // `error.message`.\n const detail = formatZodIssuesAsSingleLine(parsed.error);\n throw new RegistryMalformedPayloadError(\n baseUrl,\n `companion ${routePath}`,\n detail,\n );\n }\n return parsed.data;\n}\n"]}
1
+ {"version":3,"file":"registry-companion-client.js","sourceRoot":"","sources":["../../../../src/services/registry-companion-client.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `RegistryCompanionClient` — HTTP client for the local dev\n * companion's registry routes.\n *\n * Distinct from `RegistryServerClient` (which talks to the central\n * `platform-module-registry` server). The companion client targets\n * `/api/companion/registry/*` on `http://localhost:<companion-port>`\n * for AST/source mutations the CLI delegates to the companion\n * (KD-6).\n *\n * Phase 2 ships the four mutation routes as method calls; the\n * companion controller (under\n * `platform/factory/wildo-dev-companion/src/companion-api/controllers/`)\n * implements them with the actual ts-morph + filesystem work in\n * Phase 5+.\n *\n * The four mutations are GATED: the companion refuses any non-`GET` route that does not present the\n * machine-local token, so they take `RegistryCompanionMutationOptions` and `ping` — a `GET` — does\n * not. See that type for why the credential is required rather than optional.\n */\n\nimport {\n REGISTRY_COMPANION_ROUTES,\n RegistryMalformedPayloadError,\n RegistryRouteResultSchema,\n RegistryUnexpectedStatusError,\n type RegistryInstallRequest,\n type RegistryPublishRequest,\n type RegistryRemoveRequest,\n type RegistryRouteResult,\n type RegistryUpdateRequest,\n} from '@wildo-ai/platform-config-lib';\nimport { fetchWithTimeout, joinUrl } from './_http-client';\nimport { formatZodIssuesAsSingleLine } from './_zod-issue-formatting';\n\nconst DIAGNOSTIC_LABEL = 'Companion registry';\n\n/**\n * The header the companion's P-29b token floor reads on a mutating route.\n *\n * A wire contract between two processes that share no package, so it is named at both ends rather\n * than at neither. Its server twin is `COMPANION_TOKEN_HEADER` in the companion's\n * `core/auth/companion-auth-floor.companion.service.ts`; the CLI's is in\n * `wildo-cli/src/services/companion/companion-local-token.utils.ts`. Move one, move all three.\n */\n// semantic-literal-boundary: private companion HTTP protocol, translated only at this client edge.\nconst COMPANION_TOKEN_HEADER = 'x-wildo-companion-token';\n\nexport interface RegistryCompanionClientOptions {\n baseUrl: string;\n timeoutMs?: number;\n}\n\n/**\n * What a MUTATING companion registry call needs, over and above an address.\n *\n * `companionToken` is REQUIRED, and that is the whole point of this second type. Every non-`GET`\n * route under `/api/companion/**` is refused without it, and the four methods below have no caller\n * yet (the routes answer a not-implemented envelope until Phase 5) — so making it required now costs\n * nothing and means the caller Phase 5 writes cannot be written without the credential. An optional\n * field would have been a rule to remember; a required one is a compile error.\n *\n * The value is the content of `<appRoot>/.wildo-saas/state/companion.token` (mode 0600), which the\n * companion mints on every boot. This package has no notion of an application root, so reading it is\n * deliberately the caller's job: `wildo-cli` already owns a hardened no-follow reader\n * (`readCompanionTokenNoFollow`), and a second copy of a permission-checked read is precisely where\n * the checks quietly diverge.\n */\nexport interface RegistryCompanionMutationOptions extends RegistryCompanionClientOptions {\n companionToken: string;\n}\n\nasync function postJson<R>(\n options: RegistryCompanionMutationOptions,\n routePath: string,\n body: unknown,\n): Promise<R> {\n const url = joinUrl(options.baseUrl, routePath);\n const res = await fetchWithTimeout(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json', [COMPANION_TOKEN_HEADER]: options.companionToken },\n body: JSON.stringify(body),\n timeoutMs: options.timeoutMs,\n diagnosticLabel: DIAGNOSTIC_LABEL,\n });\n if (!res.ok) {\n // 3rd-pass M5: typed `RegistryUnexpectedStatusError` instead of\n // bare `new Error(...)` so callers can `isRegistryError(err)` and\n // discriminate by status code without re-parsing message text.\n // The companion deliberately reuses the same error vocabulary\n // as `RegistryServerClient` since the wire shape (status + URL +\n // action) is identical even though the lane is different.\n throw new RegistryUnexpectedStatusError(\n options.baseUrl,\n res.status,\n res.statusText,\n `companion ${routePath}`,\n );\n }\n // 6th-pass M3: typed JSON-parse — a misconfigured proxy returning\n // HTML at 2xx would otherwise throw a bare `SyntaxError` that\n // bypasses the typed-error contract.\n try {\n return (await res.json()) as R;\n } catch (err) {\n throw new RegistryMalformedPayloadError(\n options.baseUrl,\n `companion ${routePath}`,\n `response is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n}\n\nexport const RegistryCompanionClient = {\n /**\n * Liveness probe against `/health` (the same endpoint the\n * dev-supervisor monitors). Used by `wildo registry ping`.\n */\n async ping(options: RegistryCompanionClientOptions): Promise<boolean> {\n const url = joinUrl(options.baseUrl, '/health');\n try {\n const res = await fetchWithTimeout(url, {\n method: 'GET',\n timeoutMs: options.timeoutMs,\n diagnosticLabel: DIAGNOSTIC_LABEL,\n });\n return res.ok;\n } catch {\n return false;\n }\n },\n\n /**\n * Ask the companion to install a module: download bytes, copy\n * source, mutate `wildo.saas.config.ts > moduleRegistries` +\n * the three `modules-registry.*.ts` files, refresh\n * `sections.modules`, run provider sync.\n */\n async install(\n options: RegistryCompanionMutationOptions,\n request: RegistryInstallRequest,\n ): Promise<RegistryRouteResult> {\n const raw = await postJson<unknown>(options, REGISTRY_COMPANION_ROUTES.install, request);\n return parseCompanionResult(options.baseUrl, REGISTRY_COMPANION_ROUTES.install, raw);\n },\n\n async update(\n options: RegistryCompanionMutationOptions,\n request: RegistryUpdateRequest,\n ): Promise<RegistryRouteResult> {\n const raw = await postJson<unknown>(options, REGISTRY_COMPANION_ROUTES.update, request);\n return parseCompanionResult(options.baseUrl, REGISTRY_COMPANION_ROUTES.update, raw);\n },\n\n async remove(\n options: RegistryCompanionMutationOptions,\n request: RegistryRemoveRequest,\n ): Promise<RegistryRouteResult> {\n const raw = await postJson<unknown>(options, REGISTRY_COMPANION_ROUTES.remove, request);\n return parseCompanionResult(options.baseUrl, REGISTRY_COMPANION_ROUTES.remove, raw);\n },\n\n async publish(\n options: RegistryCompanionMutationOptions,\n request: RegistryPublishRequest,\n ): Promise<RegistryRouteResult> {\n const raw = await postJson<unknown>(options, REGISTRY_COMPANION_ROUTES.publish, request);\n return parseCompanionResult(options.baseUrl, REGISTRY_COMPANION_ROUTES.publish, raw);\n },\n} as const;\n\n/**\n * M5: thin wrapper around `RegistryRouteResultSchema.parse(raw)` that\n * converts a Zod parse failure into the typed\n * `RegistryMalformedPayloadError` instead of letting a bare `ZodError`\n * bubble up. Keeps the typed-handler contract aligned with\n * `RegistryServerClient`.\n */\nfunction parseCompanionResult(\n baseUrl: string,\n routePath: string,\n raw: unknown,\n): RegistryRouteResult {\n const parsed = RegistryRouteResultSchema.safeParse(raw);\n if (!parsed.success) {\n // 7th-pass C-L2: format the issues array for operator\n // readability instead of using Zod 4's JSON-stringified\n // `error.message`.\n const detail = formatZodIssuesAsSingleLine(parsed.error);\n throw new RegistryMalformedPayloadError(\n baseUrl,\n `companion ${routePath}`,\n detail,\n );\n }\n return parsed.data;\n}\n"]}