@wildo-ai/wildo-module-client 1.1.5 → 1.1.6
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/esm/services/_http-client.d.ts +32 -3
- package/dist/esm/services/_http-client.d.ts.map +1 -1
- package/dist/esm/services/_http-client.js +100 -6
- package/dist/esm/services/_http-client.js.map +1 -1
- package/dist/esm/services/module-adopter.service.d.ts +1 -1
- package/dist/esm/services/module-adopter.service.d.ts.map +1 -1
- package/dist/esm/services/module-adopter.service.js +7 -8
- package/dist/esm/services/module-adopter.service.js.map +1 -1
- package/dist/esm/services/module-packager.service.d.ts.map +1 -1
- package/dist/esm/services/module-packager.service.js +16 -19
- package/dist/esm/services/module-packager.service.js.map +1 -1
- package/dist/esm/services/provider-sync-runner.d.ts +2 -3
- package/dist/esm/services/provider-sync-runner.d.ts.map +1 -1
- package/dist/esm/services/provider-sync-runner.js +14 -2
- package/dist/esm/services/provider-sync-runner.js.map +1 -1
- package/dist/esm/services/registry-companion-client.d.ts +10 -47
- package/dist/esm/services/registry-companion-client.d.ts.map +1 -1
- package/dist/esm/services/registry-companion-client.js +9 -97
- package/dist/esm/services/registry-companion-client.js.map +1 -1
- package/dist/esm/services/registry-lockfile.service.d.ts +7 -3
- package/dist/esm/services/registry-lockfile.service.d.ts.map +1 -1
- package/dist/esm/services/registry-lockfile.service.js +8 -3
- package/dist/esm/services/registry-lockfile.service.js.map +1 -1
- package/dist/esm/services/registry-server-client.d.ts +35 -9
- package/dist/esm/services/registry-server-client.d.ts.map +1 -1
- package/dist/esm/services/registry-server-client.js +115 -58
- package/dist/esm/services/registry-server-client.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/__tests__/module-adopter.service.test.ts +6 -4
- package/src/__tests__/module-packager.service.test.ts +36 -34
- package/src/__tests__/provider-sync-runner.test.ts +12 -0
- package/src/__tests__/registry-lockfile.service.test.ts +3 -3
- package/src/__tests__/registry-server-client.test.ts +129 -0
- package/src/__tests__/registry-transfer-timeouts.test.ts +94 -0
- package/src/services/_http-client.ts +100 -6
- package/src/services/module-adopter.service.ts +6 -9
- package/src/services/module-packager.service.ts +19 -16
- package/src/services/provider-sync-runner.ts +16 -5
- package/src/services/registry-companion-client.ts +10 -166
- package/src/services/registry-lockfile.service.ts +9 -3
- package/src/services/registry-server-client.ts +147 -57
- package/dist/esm/__tests__/lockfile-repair.service.test.d.ts +0 -0
- package/dist/esm/__tests__/lockfile-repair.service.test.d.ts.map +0 -0
- package/dist/esm/__tests__/lockfile-repair.service.test.js.map +0 -0
- package/dist/esm/__tests__/module-adopter.service.test.d.ts +0 -0
- package/dist/esm/__tests__/module-adopter.service.test.d.ts.map +0 -0
- package/dist/esm/__tests__/module-adopter.service.test.js.map +0 -0
- package/dist/esm/__tests__/module-packager.service.test.d.ts +0 -0
- package/dist/esm/__tests__/module-packager.service.test.d.ts.map +0 -0
- package/dist/esm/__tests__/module-packager.service.test.js.map +0 -0
- package/dist/esm/__tests__/provider-materialization-hook.test.d.ts +0 -0
- package/dist/esm/__tests__/provider-materialization-hook.test.d.ts.map +0 -0
- package/dist/esm/__tests__/provider-materialization-hook.test.js.map +0 -0
- package/dist/esm/__tests__/provider-sync-runner.test.d.ts +0 -0
- package/dist/esm/__tests__/provider-sync-runner.test.d.ts.map +0 -0
- package/dist/esm/__tests__/provider-sync-runner.test.js.map +0 -0
- package/dist/esm/__tests__/registry-lockfile.service.test.d.ts +0 -0
- package/dist/esm/__tests__/registry-lockfile.service.test.d.ts.map +0 -0
- package/dist/esm/__tests__/registry-lockfile.service.test.js.map +0 -0
- package/dist/esm/__tests__/registry-server-client.test.d.ts +0 -0
- package/dist/esm/__tests__/registry-server-client.test.d.ts.map +0 -0
- package/dist/esm/__tests__/registry-server-client.test.js.map +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider-sync-runner.js","sourceRoot":"","sources":["../../../../src/services/provider-sync-runner.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `runProviderSync` — Phase 2 real shell-out to\n * `wildo config sync --domain config`.\n *\n * Module-registry install/update/remove/adopt flows mutate app\n * source. When that source changes provider implementations,\n * provider companion exports, or provider scopes, the\n * provider-runtime delivery layer must regenerate its outputs\n * (KD-18). The canonical command is owned by the provider\n * workstream — module-registry MUST delegate, not fork.\n *\n * Phase 0.5 / 1 shipped a no-op `runProviderMaterializationHook`\n * that recorded intent. Phase 2 replaces it with a real\n * `child_process.spawn`-based runner that:\n * - resolves the `wildo` CLI binary from the saas root's\n * `node_modules/.bin/wildo` (the workspace's own version);\n * - shells `wildo config sync --domain config` in the saas root;\n * - times out at the operator-configurable budget;\n * - classifies stderr-tail patterns into the\n * `RegistryProviderSyncFailedError.failureKind` discriminator\n * (`companion-discovery` / `runtime-import-reachability` /\n * `invalid-scope` / `unknown`) so callers report repairable\n * failures with first-class UX rather than generic install\n * noise (`module-registry-analysis.md` / Provider Migration\n * WIP Seams §1).\n *\n * Hard contract (KD-18): the runner NEVER writes provider runtime\n * artifacts itself. It only invokes the provider workstream's\n * canonical command. Tests assert no provider artifact is touched\n * by this code path.\n */\n\nimport { spawn } from 'node:child_process';\nimport { promises as fs } from 'node:fs';\nimport * as path from 'node:path';\nimport {\n PROVIDER_LOCK_SECTION_PATH,\n PROVIDER_RUNTIME_ARTIFACT_RELATIVE_PATHS,\n PROVIDER_RUNTIME_GENERATED_MODULE_RELATIVE_PATH,\n RegistryProviderSyncFailedError,\n} from '@wildo-ai/platform-config-lib';\nimport type { RegistryClientLogger } from './types';\n\nconst DEFAULT_TIMEOUT_MS = 120_000;\n\nexport interface ProviderSyncRunnerInput {\n /** Absolute path to the SaaS application root. */\n saasRoot: string;\n /** Reason for the call — drives diagnostic copy. */\n reason: 'install' | 'update' | 'remove' | 'adopt' | 'smoke';\n /** Optional logger. Defaults to no-op. */\n log?: RegistryClientLogger;\n /**\n * Override the wildo CLI binary path. Defaults to\n * `<saasRoot>/node_modules/.bin/wildo`. Phase 2 tests override\n * this to point at a stub script.\n */\n wildoBinPath?: string;\n /**\n * Maximum wall-time before SIGTERM. Default 120s — provider sync\n * can be heavy on first run (artifact generation + cleanup),\n * but every additional second is operator-visible.\n */\n timeoutMs?: number;\n /**\n * If true, do NOT actually shell out — log the intent and\n * resolve as `mode: 'dry-run'`. Used by the Phase 0.5 smoke\n * command which intentionally cannot rely on a working\n * `wildo` binary in the smoke sandbox.\n */\n dryRun?: boolean;\n}\n\nexport interface ProviderSyncRunnerResult {\n invoked: true;\n /**\n * `'shell-out'` when the runner spawned `wildo config sync`.\n * `'dry-run'` when `dryRun: true` was passed (smoke flow).\n */\n mode: 'shell-out' | 'dry-run';\n /** Exit code reported by the wildo binary (0 on success). */\n exitCode: number | null;\n /** Wall-clock duration (ms). */\n durationMs: number;\n /** Reason that was passed in. */\n reason: ProviderSyncRunnerInput['reason'];\n /** Provider-output paths the canonical sync would refresh. */\n providerJsonRelativePaths: readonly string[];\n /** Source module the Node-runtime consumes. */\n providerSourceRelativePath: string;\n /** Provider lock section the materializer owns end-to-end. */\n providerLockSectionPath: typeof PROVIDER_LOCK_SECTION_PATH;\n}\n\n/**\n * Provider artifact paths sourced from the canonical\n * `PROVIDER_RUNTIME_ARTIFACT_RELATIVE_PATHS` constant in\n * `platform-config-lib`. Refactored on 2026-05-06 to import the\n * single source of truth instead of duplicating the literal paths\n * here — the artifacts moved from `.wildo-saas/providers/` to\n * `.wildo-saas/generated/providers/` and any duplication would\n * silently drift.\n */\nconst PROVIDER_JSON_RELATIVE_PATHS = Object.values(\n PROVIDER_RUNTIME_ARTIFACT_RELATIVE_PATHS,\n) as readonly string[];\n\nconst PROVIDER_SOURCE_RELATIVE_PATH = PROVIDER_RUNTIME_GENERATED_MODULE_RELATIVE_PATH;\n\n/**\n * Classify a `wildo config sync` failure based on its stderr tail.\n * The matchers mirror the failure modes the provider workstream\n * surfaces today; new modes get a new branch + a new\n * discriminator value (out of `RegistryProviderSyncFailedError`).\n */\nfunction classifyStderr(\n stderr: string,\n): 'companion-discovery' | 'runtime-import-reachability' | 'invalid-scope' | 'unknown' {\n const tail = stderr.slice(-2000);\n // Companion-export discovery failure — the provider sync walks\n // each owner package's `./companion` export to discover provider\n // contributions. A package whose export contract is broken\n // surfaces a \"companion export\" / \"DiscoveredProviderCompanionContribution\"\n // error.\n if (\n /companion[- ]export|DiscoveredProviderCompanionContribution|companion-discovery/i.test(tail)\n ) {\n return 'companion-discovery';\n }\n // Runtime-import reachability — the materializer can't resolve a\n // contributed `runtimeImport` against the owning runtime package\n // graph. Typical messages mention `runtimeImport`, `Cannot find\n // module`, or `MODULE_NOT_FOUND`.\n if (\n /runtimeImport|MODULE_NOT_FOUND|Cannot find module|cannot resolve/i.test(tail)\n ) {\n return 'runtime-import-reachability';\n }\n // Scope validation — `defineSaaSProviders({ scopes })` rejects\n // malformed scope inputs ahead of materialization.\n if (\n /defineSaaSProviders|invalid[- ]scope|providers\\.scopes/i.test(tail)\n ) {\n return 'invalid-scope';\n }\n return 'unknown';\n}\n\nasync function resolveWildoBin(input: ProviderSyncRunnerInput): Promise<string> {\n if (input.wildoBinPath) return input.wildoBinPath;\n const candidate = path.join(input.saasRoot, 'node_modules', '.bin', 'wildo');\n try {\n await fs.access(candidate);\n return candidate;\n } catch {\n // Fall back to PATH-resolved `wildo`. spawn will raise ENOENT\n // if it's not on PATH either, surfaced as\n // `RegistryProviderSyncFailedError(failureKind: 'unknown')`.\n return 'wildo';\n }\n}\n\n/**\n * Grace window between SIGTERM and SIGKILL. A well-behaved\n * `wildo config sync` should react to SIGTERM in well under a\n * second — the grace exists for tools that need to flush a buffer\n * or release a file lock. After the window we escalate to SIGKILL\n * so a hung child cannot pin the parent forever.\n */\nconst SIGKILL_GRACE_MS = 5_000;\n\nfunction runWildoConfigSync(\n binPath: string,\n cwd: string,\n timeoutMs: number,\n): Promise<{ exitCode: number | null; stderr: string; durationMs: number; signaled: boolean }> {\n return new Promise((resolve) => {\n const startedAt = Date.now();\n const child = spawn(binPath, ['config', 'sync', '--domain', 'config'], {\n cwd,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n let stderr = '';\n let signaled = false;\n let killTimer: NodeJS.Timeout | undefined;\n child.stderr?.on('data', (b: Buffer) => {\n // Cap captured stderr so a runaway sync can't OOM the parent.\n stderr += b.toString();\n if (stderr.length > 16_384) stderr = stderr.slice(-16_384);\n });\n const termTimer = setTimeout(() => {\n // Soft-kill first; record so the caller can synthesize a\n // diagnostic message that distinguishes a TERM-induced exit\n // from a normal one.\n signaled = true;\n stderr += `\\n[provider-sync] timeout ${timeoutMs}ms exceeded — sent SIGTERM.`;\n child.kill('SIGTERM');\n // Escalate to SIGKILL if the child doesn't react within the\n // grace window. Without this a child stuck in a syscall (or\n // deliberately ignoring SIGTERM) would leave the parent\n // blocked on the spawn promise forever.\n killTimer = setTimeout(() => {\n if (child.exitCode === null && child.signalCode === null) {\n stderr += `\\n[provider-sync] SIGTERM grace ${SIGKILL_GRACE_MS}ms exceeded — escalating to SIGKILL.`;\n try {\n child.kill('SIGKILL');\n } catch {\n /* swallow — best-effort escalation */\n }\n }\n }, SIGKILL_GRACE_MS);\n }, timeoutMs);\n child.on('exit', (code) => {\n clearTimeout(termTimer);\n if (killTimer) clearTimeout(killTimer);\n resolve({\n exitCode: code,\n stderr,\n durationMs: Date.now() - startedAt,\n signaled,\n });\n });\n child.on('error', (err) => {\n clearTimeout(termTimer);\n if (killTimer) clearTimeout(killTimer);\n resolve({\n exitCode: null,\n stderr: stderr + `\\n[spawn error] ${err.message}`,\n durationMs: Date.now() - startedAt,\n signaled,\n });\n });\n });\n}\n\n/**\n * Run the canonical provider-sync command. Throws\n * `RegistryProviderSyncFailedError` (with classified `failureKind`)\n * when the spawned process exits non-zero or fails to start.\n */\nexport async function runProviderSync(\n input: ProviderSyncRunnerInput,\n): Promise<ProviderSyncRunnerResult> {\n const log = input.log;\n const timeoutMs = input.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const baseResult = {\n invoked: true as const,\n reason: input.reason,\n providerJsonRelativePaths: PROVIDER_JSON_RELATIVE_PATHS,\n providerSourceRelativePath: PROVIDER_SOURCE_RELATIVE_PATH,\n providerLockSectionPath: PROVIDER_LOCK_SECTION_PATH,\n };\n\n if (input.dryRun) {\n log?.info?.(\n `[registry] post-${input.reason} provider sync (dry-run): would shell ` +\n `\\`wildo config sync --domain config\\` in ${input.saasRoot}.`,\n );\n return { ...baseResult, mode: 'dry-run', exitCode: 0, durationMs: 0 };\n }\n\n const binPath = await resolveWildoBin(input);\n log?.info?.(\n `[registry] post-${input.reason} provider sync: spawning ` +\n `${binPath} config sync --domain config (cwd=${input.saasRoot}, timeout=${timeoutMs}ms)`,\n );\n const { exitCode, stderr, durationMs, signaled } = await runWildoConfigSync(\n binPath,\n input.saasRoot,\n timeoutMs,\n );\n\n if (exitCode === 0) {\n log?.success?.(`[registry] provider sync succeeded in ${durationMs}ms.`);\n return { ...baseResult, mode: 'shell-out', exitCode, durationMs };\n }\n\n // H5: distinguish a timeout-signaled exit from a generic non-zero\n // exit. `classifyStderr` runs regex against the appended\n // `[provider-sync] timeout ... SIGTERM/SIGKILL` lines but those\n // tails match no failure-mode regex — so prior to this branch\n // operators saw `failureKind: 'unknown'` for a hard timeout. The\n // typed error now carries the discriminator + a hint to either\n // raise the timeout or investigate the wedge.\n const failureKind = signaled ? ('timeout' as const) : classifyStderr(stderr);\n log?.error?.(\n `[registry] provider sync failed (${failureKind}, exit=${exitCode}, ${durationMs}ms).`,\n );\n const messageSuffix = failureKind === 'timeout'\n ? `Wall-clock exceeded ${timeoutMs}ms; child was sent SIGTERM (then SIGKILL after grace). ` +\n 'Either raise `timeoutMs` for legitimately heavy syncs or investigate why the sync wedged.'\n : 'Run `wildo config sync --domain config` manually to inspect.';\n throw new RegistryProviderSyncFailedError(\n `Post-${input.reason} provider sync failed (${failureKind}). ${messageSuffix}`,\n failureKind,\n stderr.slice(-1024),\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"provider-sync-runner.js","sourceRoot":"","sources":["../../../../src/services/provider-sync-runner.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `runProviderSync` — Phase 2 real shell-out to\n * `wildo config sync --domain config`.\n *\n * Module-registry install/update/remove/adopt flows mutate app\n * source. When that source changes provider implementations,\n * provider companion exports, or provider scopes, the\n * provider-runtime delivery layer must regenerate its outputs\n * (KD-18). The canonical command is owned by the provider\n * workstream — module-registry MUST delegate, not fork.\n *\n * Phase 0.5 / 1 shipped a no-op `runProviderMaterializationHook`\n * that recorded intent. Phase 2 replaces it with a real\n * `child_process.spawn`-based runner that:\n * - resolves the `wildo` CLI binary from the saas root's\n * `node_modules/.bin/wildo` (the workspace's own version);\n * - shells `wildo config sync --domain config` in the saas root;\n * - times out at the operator-configurable budget;\n * - classifies stderr-tail patterns into the\n * `RegistryProviderSyncFailedError.failureKind` discriminator\n * (`companion-discovery` / `runtime-import-reachability` /\n * `invalid-scope` / `unknown`) so callers report repairable\n * failures with first-class UX rather than generic install\n * noise (`module-registry-analysis.md` / Provider Migration\n * WIP Seams §1).\n *\n * Hard contract (KD-18): the runner NEVER writes provider runtime\n * artifacts itself. It only invokes the provider workstream's\n * canonical command. Tests assert no provider artifact is touched\n * by this code path.\n */\n\nimport { spawn } from 'node:child_process';\nimport { promises as fs } from 'node:fs';\nimport * as path from 'node:path';\nimport {\n PROVIDER_LOCK_SECTION_PATH,\n PROVIDER_RUNTIME_ARTIFACT_RELATIVE_PATHS,\n PROVIDER_RUNTIME_GENERATED_MODULE_RELATIVE_PATH,\n RegistryProviderSyncFailedError,\n} from '@wildo-ai/platform-config-lib';\nimport type { RegistryClientLogger } from './types';\n\n/**\n * How long the post-mutation `wildo config sync` may run before it is treated as wedged and killed.\n *\n * Five minutes, not two: the budget exists to stop a WEDGED sync, and a wall clock cannot tell wedged\n * from slow. Measured 2026-09-27 on a scaffolded application (#1765): the same sync took 14.6 s on a\n * quiet machine, 91 s while the framework's watchers were recompiling, and exceeded the old 120 s during\n * an install under that load — so a healthy sync was killed part-way and the install reported a provider\n * sync failure it did not have. The kill stays: a genuinely stuck sync still ends, five minutes on.\n */\nconst DEFAULT_TIMEOUT_MS = 300_000;\n\nexport interface ProviderSyncRunnerInput {\n /** Absolute path to the SaaS application root. */\n saasRoot: string;\n /** Reason for the call — drives diagnostic copy. */\n reason: 'install' | 'update' | 'remove' | 'adopt' | 'smoke';\n /** Optional logger. Defaults to no-op. */\n log?: RegistryClientLogger;\n /**\n * Override the wildo CLI binary path. Defaults to\n * `<saasRoot>/node_modules/.bin/wildo`. Phase 2 tests override\n * this to point at a stub script.\n */\n wildoBinPath?: string;\n /**\n * Maximum wall-time before SIGTERM. Default {@link DEFAULT_TIMEOUT_MS} (five minutes): it only ever\n * elapses on a wedged sync, and a healthy one under load has been measured past two minutes.\n */\n timeoutMs?: number;\n /**\n * If true, do NOT actually shell out — log the intent and\n * resolve as `mode: 'dry-run'`. Used by the Phase 0.5 smoke\n * command which intentionally cannot rely on a working\n * `wildo` binary in the smoke sandbox.\n */\n dryRun?: boolean;\n}\n\nexport interface ProviderSyncRunnerResult {\n invoked: true;\n /**\n * `'shell-out'` when the runner spawned `wildo config sync`.\n * `'dry-run'` when `dryRun: true` was passed (smoke flow).\n */\n mode: 'shell-out' | 'dry-run';\n /** Exit code reported by the wildo binary (0 on success). */\n exitCode: number | null;\n /** Wall-clock duration (ms). */\n durationMs: number;\n /** Reason that was passed in. */\n reason: ProviderSyncRunnerInput['reason'];\n /** Provider-output paths the canonical sync would refresh. */\n providerJsonRelativePaths: readonly string[];\n /** Source module the Node-runtime consumes. */\n providerSourceRelativePath: string;\n /** Provider lock section the materializer owns end-to-end. */\n providerLockSectionPath: typeof PROVIDER_LOCK_SECTION_PATH;\n}\n\n/**\n * Provider artifact paths sourced from the canonical\n * `PROVIDER_RUNTIME_ARTIFACT_RELATIVE_PATHS` constant in\n * `platform-config-lib`. Refactored on 2026-05-06 to import the\n * single source of truth instead of duplicating the literal paths\n * here — the artifacts moved from `.wildo-saas/providers/` to\n * `.wildo-saas/generated/providers/` and any duplication would\n * silently drift.\n */\nconst PROVIDER_JSON_RELATIVE_PATHS = Object.values(\n PROVIDER_RUNTIME_ARTIFACT_RELATIVE_PATHS,\n) as readonly string[];\n\nconst PROVIDER_SOURCE_RELATIVE_PATH = PROVIDER_RUNTIME_GENERATED_MODULE_RELATIVE_PATH;\n\n/**\n * Classify a `wildo config sync` failure based on its stderr tail.\n * The matchers mirror the failure modes the provider workstream\n * surfaces today; new modes get a new branch + a new\n * discriminator value (out of `RegistryProviderSyncFailedError`).\n */\nfunction classifyStderr(\n stderr: string,\n): 'companion-discovery' | 'runtime-import-reachability' | 'invalid-scope' | 'unknown' {\n const tail = stderr.slice(-2000);\n // Companion-export discovery failure — the provider sync walks\n // each owner package's `./companion` export to discover provider\n // contributions. A package whose export contract is broken\n // surfaces a \"companion export\" / \"DiscoveredProviderCompanionContribution\"\n // error.\n if (\n /companion[- ]export|DiscoveredProviderCompanionContribution|companion-discovery/i.test(tail)\n ) {\n return 'companion-discovery';\n }\n // Runtime-import reachability — the materializer can't resolve a\n // contributed `runtimeImport` against the owning runtime package\n // graph. Typical messages mention `runtimeImport`, `Cannot find\n // module`, or `MODULE_NOT_FOUND`.\n if (\n /runtimeImport|MODULE_NOT_FOUND|Cannot find module|cannot resolve/i.test(tail)\n ) {\n return 'runtime-import-reachability';\n }\n // Scope validation — `defineSaaSProviders({ scopes })` rejects\n // malformed scope inputs ahead of materialization.\n if (\n /defineSaaSProviders|invalid[- ]scope|providers\\.scopes/i.test(tail)\n ) {\n return 'invalid-scope';\n }\n return 'unknown';\n}\n\nasync function resolveWildoBin(input: ProviderSyncRunnerInput): Promise<string> {\n if (input.wildoBinPath) return input.wildoBinPath;\n const candidate = path.join(input.saasRoot, 'node_modules', '.bin', 'wildo');\n try {\n await fs.access(candidate);\n return candidate;\n } catch {\n // Fall back to PATH-resolved `wildo`. spawn will raise ENOENT\n // if it's not on PATH either, surfaced as\n // `RegistryProviderSyncFailedError(failureKind: 'unknown')`.\n return 'wildo';\n }\n}\n\n/**\n * Grace window between SIGTERM and SIGKILL. A well-behaved\n * `wildo config sync` should react to SIGTERM in well under a\n * second — the grace exists for tools that need to flush a buffer\n * or release a file lock. After the window we escalate to SIGKILL\n * so a hung child cannot pin the parent forever.\n */\nconst SIGKILL_GRACE_MS = 5_000;\n\nfunction runWildoConfigSync(\n binPath: string,\n cwd: string,\n timeoutMs: number,\n): Promise<{ exitCode: number | null; stderr: string; durationMs: number; signaled: boolean }> {\n return new Promise((resolve) => {\n const startedAt = Date.now();\n const child = spawn(binPath, ['config', 'sync', '--domain', 'config'], {\n cwd,\n // stdout is IGNORED, not piped: nothing here reads it, and an unread pipe fills at the OS buffer\n // (~64 KB) and blocks the child mid-write — a finished sync then looked like a wedged one and\n // was killed at the timeout. stderr stays piped because the diagnostic reads it (bounded below).\n stdio: ['ignore', 'ignore', 'pipe'],\n });\n let stderr = '';\n let signaled = false;\n let killTimer: NodeJS.Timeout | undefined;\n child.stderr?.on('data', (b: Buffer) => {\n // Cap captured stderr so a runaway sync can't OOM the parent.\n stderr += b.toString();\n if (stderr.length > 16_384) stderr = stderr.slice(-16_384);\n });\n const termTimer = setTimeout(() => {\n // Soft-kill first; record so the caller can synthesize a\n // diagnostic message that distinguishes a TERM-induced exit\n // from a normal one.\n signaled = true;\n stderr += `\\n[provider-sync] timeout ${timeoutMs}ms exceeded — sent SIGTERM.`;\n child.kill('SIGTERM');\n // Escalate to SIGKILL if the child doesn't react within the\n // grace window. Without this a child stuck in a syscall (or\n // deliberately ignoring SIGTERM) would leave the parent\n // blocked on the spawn promise forever.\n killTimer = setTimeout(() => {\n if (child.exitCode === null && child.signalCode === null) {\n stderr += `\\n[provider-sync] SIGTERM grace ${SIGKILL_GRACE_MS}ms exceeded — escalating to SIGKILL.`;\n try {\n child.kill('SIGKILL');\n } catch {\n /* swallow — best-effort escalation */\n }\n }\n }, SIGKILL_GRACE_MS);\n }, timeoutMs);\n child.on('exit', (code) => {\n clearTimeout(termTimer);\n if (killTimer) clearTimeout(killTimer);\n resolve({\n exitCode: code,\n stderr,\n durationMs: Date.now() - startedAt,\n signaled,\n });\n });\n child.on('error', (err) => {\n clearTimeout(termTimer);\n if (killTimer) clearTimeout(killTimer);\n resolve({\n exitCode: null,\n stderr: stderr + `\\n[spawn error] ${err.message}`,\n durationMs: Date.now() - startedAt,\n signaled,\n });\n });\n });\n}\n\n/**\n * Run the canonical provider-sync command. Throws\n * `RegistryProviderSyncFailedError` (with classified `failureKind`)\n * when the spawned process exits non-zero or fails to start.\n */\nexport async function runProviderSync(\n input: ProviderSyncRunnerInput,\n): Promise<ProviderSyncRunnerResult> {\n const log = input.log;\n const timeoutMs = input.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const baseResult = {\n invoked: true as const,\n reason: input.reason,\n providerJsonRelativePaths: PROVIDER_JSON_RELATIVE_PATHS,\n providerSourceRelativePath: PROVIDER_SOURCE_RELATIVE_PATH,\n providerLockSectionPath: PROVIDER_LOCK_SECTION_PATH,\n };\n\n if (input.dryRun) {\n log?.info?.(\n `[registry] post-${input.reason} provider sync (dry-run): would shell ` +\n `\\`wildo config sync --domain config\\` in ${input.saasRoot}.`,\n );\n return { ...baseResult, mode: 'dry-run', exitCode: 0, durationMs: 0 };\n }\n\n const binPath = await resolveWildoBin(input);\n log?.info?.(\n `[registry] post-${input.reason} provider sync: spawning ` +\n `${binPath} config sync --domain config (cwd=${input.saasRoot}, timeout=${timeoutMs}ms)`,\n );\n const { exitCode, stderr, durationMs, signaled } = await runWildoConfigSync(\n binPath,\n input.saasRoot,\n timeoutMs,\n );\n\n if (exitCode === 0) {\n log?.success?.(`[registry] provider sync succeeded in ${durationMs}ms.`);\n return { ...baseResult, mode: 'shell-out', exitCode, durationMs };\n }\n\n // H5: distinguish a timeout-signaled exit from a generic non-zero\n // exit. `classifyStderr` runs regex against the appended\n // `[provider-sync] timeout ... SIGTERM/SIGKILL` lines but those\n // tails match no failure-mode regex — so prior to this branch\n // operators saw `failureKind: 'unknown'` for a hard timeout. The\n // typed error now carries the discriminator + a hint to either\n // raise the timeout or investigate the wedge.\n const failureKind = signaled ? ('timeout' as const) : classifyStderr(stderr);\n log?.error?.(\n `[registry] provider sync failed (${failureKind}, exit=${exitCode}, ${durationMs}ms).`,\n );\n const messageSuffix = failureKind === 'timeout'\n ? `Wall-clock exceeded ${timeoutMs}ms; child was sent SIGTERM (then SIGKILL after grace). ` +\n 'Either raise `timeoutMs` for legitimately heavy syncs or investigate why the sync wedged.'\n : 'Run `wildo config sync --domain config` manually to inspect.';\n throw new RegistryProviderSyncFailedError(\n `Post-${input.reason} provider sync failed (${failureKind}). ${messageSuffix}`,\n failureKind,\n stderr.slice(-1024),\n );\n}\n"]}
|
|
@@ -1,61 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `RegistryCompanionClient` —
|
|
3
|
-
*
|
|
2
|
+
* `RegistryCompanionClient` — liveness probe for the local dev companion, used by
|
|
3
|
+
* `wildo registry ping` beside the registry-server probe.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* companion controller (under
|
|
13
|
-
* `platform/factory/wildo-dev-companion/src/companion-api/controllers/`)
|
|
14
|
-
* implements them with the actual ts-morph + filesystem work in
|
|
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.
|
|
5
|
+
* That is all it is. It used to carry four mutation methods (install / update / remove / publish)
|
|
6
|
+
* against `/api/companion/registry/*` routes that only ever answered a "Phase 5/6 deliverable"
|
|
7
|
+
* not-implemented envelope and had no caller. Installation shipped CLI-direct instead
|
|
8
|
+
* (`wildo registry add` / `update` / `remove` / `publish` do the work themselves), so the routes,
|
|
9
|
+
* these methods and their wire schemas were deleted on 2026-09-25 rather than left describing a
|
|
10
|
+
* design that was never built. A future companion-owned mutation lane starts from the CLI commands,
|
|
11
|
+
* not from this file.
|
|
20
12
|
*/
|
|
21
|
-
import { type RegistryInstallRequest, type RegistryPublishRequest, type RegistryRemoveRequest, type RegistryRouteResult, type RegistryUpdateRequest } from '@wildo-ai/platform-config-lib';
|
|
22
13
|
export interface RegistryCompanionClientOptions {
|
|
23
14
|
baseUrl: string;
|
|
24
15
|
timeoutMs?: number;
|
|
25
16
|
}
|
|
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
|
-
}
|
|
44
17
|
export declare const RegistryCompanionClient: {
|
|
45
18
|
/**
|
|
46
19
|
* Liveness probe against `/health` (the same endpoint the
|
|
47
20
|
* dev-supervisor monitors). Used by `wildo registry ping`.
|
|
48
21
|
*/
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Ask the companion to install a module: download bytes, copy
|
|
52
|
-
* source, mutate `wildo.saas.config.ts > moduleRegistries` +
|
|
53
|
-
* the three `modules-registry.*.ts` files, refresh
|
|
54
|
-
* `sections.modules`, run provider sync.
|
|
55
|
-
*/
|
|
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>;
|
|
22
|
+
ping(options: RegistryCompanionClientOptions): Promise<boolean>;
|
|
60
23
|
};
|
|
61
24
|
//# 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
|
|
1
|
+
{"version":3,"file":"registry-companion-client.d.ts","sourceRoot":"","sources":["../../../../src/services/registry-companion-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,uBAAuB;IAClC;;;OAGG;kBACiB,8BAA8B,GAAG,OAAO,CAAC,OAAO,CAAC;CAatE,CAAC"}
|
|
@@ -1,65 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `RegistryCompanionClient` —
|
|
3
|
-
*
|
|
2
|
+
* `RegistryCompanionClient` — liveness probe for the local dev companion, used by
|
|
3
|
+
* `wildo registry ping` beside the registry-server probe.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* companion controller (under
|
|
13
|
-
* `platform/factory/wildo-dev-companion/src/companion-api/controllers/`)
|
|
14
|
-
* implements them with the actual ts-morph + filesystem work in
|
|
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.
|
|
5
|
+
* That is all it is. It used to carry four mutation methods (install / update / remove / publish)
|
|
6
|
+
* against `/api/companion/registry/*` routes that only ever answered a "Phase 5/6 deliverable"
|
|
7
|
+
* not-implemented envelope and had no caller. Installation shipped CLI-direct instead
|
|
8
|
+
* (`wildo registry add` / `update` / `remove` / `publish` do the work themselves), so the routes,
|
|
9
|
+
* these methods and their wire schemas were deleted on 2026-09-25 rather than left describing a
|
|
10
|
+
* design that was never built. A future companion-owned mutation lane starts from the CLI commands,
|
|
11
|
+
* not from this file.
|
|
20
12
|
*/
|
|
21
|
-
import { REGISTRY_COMPANION_ROUTES, RegistryMalformedPayloadError, RegistryRouteResultSchema, RegistryUnexpectedStatusError, } from '@wildo-ai/platform-config-lib';
|
|
22
13
|
import { fetchWithTimeout, joinUrl } from './_http-client.js';
|
|
23
|
-
import { formatZodIssuesAsSingleLine } from './_zod-issue-formatting.js';
|
|
24
14
|
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';
|
|
35
|
-
async function postJson(options, routePath, body) {
|
|
36
|
-
const url = joinUrl(options.baseUrl, routePath);
|
|
37
|
-
const res = await fetchWithTimeout(url, {
|
|
38
|
-
method: 'POST',
|
|
39
|
-
headers: { 'content-type': 'application/json', [COMPANION_TOKEN_HEADER]: options.companionToken },
|
|
40
|
-
body: JSON.stringify(body),
|
|
41
|
-
timeoutMs: options.timeoutMs,
|
|
42
|
-
diagnosticLabel: DIAGNOSTIC_LABEL,
|
|
43
|
-
});
|
|
44
|
-
if (!res.ok) {
|
|
45
|
-
// 3rd-pass M5: typed `RegistryUnexpectedStatusError` instead of
|
|
46
|
-
// bare `new Error(...)` so callers can `isRegistryError(err)` and
|
|
47
|
-
// discriminate by status code without re-parsing message text.
|
|
48
|
-
// The companion deliberately reuses the same error vocabulary
|
|
49
|
-
// as `RegistryServerClient` since the wire shape (status + URL +
|
|
50
|
-
// action) is identical even though the lane is different.
|
|
51
|
-
throw new RegistryUnexpectedStatusError(options.baseUrl, res.status, res.statusText, `companion ${routePath}`);
|
|
52
|
-
}
|
|
53
|
-
// 6th-pass M3: typed JSON-parse — a misconfigured proxy returning
|
|
54
|
-
// HTML at 2xx would otherwise throw a bare `SyntaxError` that
|
|
55
|
-
// bypasses the typed-error contract.
|
|
56
|
-
try {
|
|
57
|
-
return (await res.json());
|
|
58
|
-
}
|
|
59
|
-
catch (err) {
|
|
60
|
-
throw new RegistryMalformedPayloadError(options.baseUrl, `companion ${routePath}`, `response is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
15
|
export const RegistryCompanionClient = {
|
|
64
16
|
/**
|
|
65
17
|
* Liveness probe against `/health` (the same endpoint the
|
|
@@ -79,45 +31,5 @@ export const RegistryCompanionClient = {
|
|
|
79
31
|
return false;
|
|
80
32
|
}
|
|
81
33
|
},
|
|
82
|
-
/**
|
|
83
|
-
* Ask the companion to install a module: download bytes, copy
|
|
84
|
-
* source, mutate `wildo.saas.config.ts > moduleRegistries` +
|
|
85
|
-
* the three `modules-registry.*.ts` files, refresh
|
|
86
|
-
* `sections.modules`, run provider sync.
|
|
87
|
-
*/
|
|
88
|
-
async install(options, request) {
|
|
89
|
-
const raw = await postJson(options, REGISTRY_COMPANION_ROUTES.install, request);
|
|
90
|
-
return parseCompanionResult(options.baseUrl, REGISTRY_COMPANION_ROUTES.install, raw);
|
|
91
|
-
},
|
|
92
|
-
async update(options, request) {
|
|
93
|
-
const raw = await postJson(options, REGISTRY_COMPANION_ROUTES.update, request);
|
|
94
|
-
return parseCompanionResult(options.baseUrl, REGISTRY_COMPANION_ROUTES.update, raw);
|
|
95
|
-
},
|
|
96
|
-
async remove(options, request) {
|
|
97
|
-
const raw = await postJson(options, REGISTRY_COMPANION_ROUTES.remove, request);
|
|
98
|
-
return parseCompanionResult(options.baseUrl, REGISTRY_COMPANION_ROUTES.remove, raw);
|
|
99
|
-
},
|
|
100
|
-
async publish(options, request) {
|
|
101
|
-
const raw = await postJson(options, REGISTRY_COMPANION_ROUTES.publish, request);
|
|
102
|
-
return parseCompanionResult(options.baseUrl, REGISTRY_COMPANION_ROUTES.publish, raw);
|
|
103
|
-
},
|
|
104
34
|
};
|
|
105
|
-
/**
|
|
106
|
-
* M5: thin wrapper around `RegistryRouteResultSchema.parse(raw)` that
|
|
107
|
-
* converts a Zod parse failure into the typed
|
|
108
|
-
* `RegistryMalformedPayloadError` instead of letting a bare `ZodError`
|
|
109
|
-
* bubble up. Keeps the typed-handler contract aligned with
|
|
110
|
-
* `RegistryServerClient`.
|
|
111
|
-
*/
|
|
112
|
-
function parseCompanionResult(baseUrl, routePath, raw) {
|
|
113
|
-
const parsed = RegistryRouteResultSchema.safeParse(raw);
|
|
114
|
-
if (!parsed.success) {
|
|
115
|
-
// 7th-pass C-L2: format the issues array for operator
|
|
116
|
-
// readability instead of using Zod 4's JSON-stringified
|
|
117
|
-
// `error.message`.
|
|
118
|
-
const detail = formatZodIssuesAsSingleLine(parsed.error);
|
|
119
|
-
throw new RegistryMalformedPayloadError(baseUrl, `companion ${routePath}`, detail);
|
|
120
|
-
}
|
|
121
|
-
return parsed.data;
|
|
122
|
-
}
|
|
123
35
|
//# sourceMappingURL=registry-companion-client.js.map
|
|
@@ -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 * 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"]}
|
|
1
|
+
{"version":3,"file":"registry-companion-client.js","sourceRoot":"","sources":["../../../../src/services/registry-companion-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAE3D,MAAM,gBAAgB,GAAG,oBAAoB,CAAC;AAO9C,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC;;;OAGG;IACH,KAAK,CAAC,IAAI,CAAC,OAAuC;QAChD,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAChD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,GAAG,EAAE;gBACtC,MAAM,EAAE,KAAK;gBACb,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,eAAe,EAAE,gBAAgB;aAClC,CAAC,CAAC;YACH,OAAO,GAAG,CAAC,EAAE,CAAC;QAChB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF,CAAC","sourcesContent":["/**\n * `RegistryCompanionClient` — liveness probe for the local dev companion, used by\n * `wildo registry ping` beside the registry-server probe.\n *\n * That is all it is. It used to carry four mutation methods (install / update / remove / publish)\n * against `/api/companion/registry/*` routes that only ever answered a \"Phase 5/6 deliverable\"\n * not-implemented envelope and had no caller. Installation shipped CLI-direct instead\n * (`wildo registry add` / `update` / `remove` / `publish` do the work themselves), so the routes,\n * these methods and their wire schemas were deleted on 2026-09-25 rather than left describing a\n * design that was never built. A future companion-owned mutation lane starts from the CLI commands,\n * not from this file.\n */\n\nimport { fetchWithTimeout, joinUrl } from './_http-client';\n\nconst DIAGNOSTIC_LABEL = 'Companion registry';\n\nexport interface RegistryCompanionClientOptions {\n baseUrl: string;\n timeoutMs?: number;\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"]}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* those are produced by the provider materialization workflow.
|
|
20
20
|
* - Never mutates `sections.providers.runtimeScopes`.
|
|
21
21
|
*/
|
|
22
|
-
import { type RegistryModulesLockSection, type
|
|
22
|
+
import { type RegistryModulesLockSection, type RegistryModulesLockEntryInput } from '@wildo-ai/platform-config-lib';
|
|
23
23
|
export declare const RegistryLockfileService: {
|
|
24
24
|
/**
|
|
25
25
|
* Read the current `sections.modules` payload (or an empty section
|
|
@@ -35,8 +35,12 @@ export declare const RegistryLockfileService: {
|
|
|
35
35
|
* before commit.
|
|
36
36
|
*/
|
|
37
37
|
readonly mutate: (saasRoot: string, mutator: (current: RegistryModulesLockSection) => RegistryModulesLockSection | void | Promise<RegistryModulesLockSection | void>) => Promise<RegistryModulesLockSection>;
|
|
38
|
-
/**
|
|
39
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Insert or replace one module's entry. The entry is parsed here, so its defaults (`distribution`,
|
|
40
|
+
* `files`) apply in one place and a malformed entry is refused naming the module, before the lockfile
|
|
41
|
+
* is touched (`mutate` then re-validates the whole section).
|
|
42
|
+
*/
|
|
43
|
+
readonly upsertEntry: (saasRoot: string, input: RegistryModulesLockEntryInput) => Promise<void>;
|
|
40
44
|
/**
|
|
41
45
|
* Find every other lockfile entry whose `files[]` claims any of the
|
|
42
46
|
* given paths. Used by `update`/`remove` to skip deletes that would
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry-lockfile.service.d.ts","sourceRoot":"","sources":["../../../../src/services/registry-lockfile.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAIL,KAAK,0BAA0B,
|
|
1
|
+
{"version":3,"file":"registry-lockfile.service.d.ts","sourceRoot":"","sources":["../../../../src/services/registry-lockfile.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAIL,KAAK,0BAA0B,EAE/B,KAAK,6BAA6B,EACnC,MAAM,+BAA+B,CAAC;AAevC,eAAO,MAAM,uBAAuB;IAClC;;;;OAIG;8BACkB,MAAM,KAAG,OAAO,CAAC,0BAA0B,CAAC;IAOjE;;;;;;OAMG;gCAES,MAAM,WACP,CACP,OAAO,EAAE,0BAA0B,KAEjC,0BAA0B,GAC1B,IAAI,GACJ,OAAO,CAAC,0BAA0B,GAAG,IAAI,CAAC,KAC7C,OAAO,CAAC,0BAA0B,CAAC;IAYtC;;;;OAIG;qCACyB,MAAM,SAAS,6BAA6B,KAAG,OAAO,CAAC,IAAI,CAAC;IAQxF;;;;;;;;;OASG;oCACwB;QACzB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;QACzB,eAAe,EAAE,MAAM,CAAC;KACzB,KAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAgB1B,CAAC"}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* those are produced by the provider materialization workflow.
|
|
20
20
|
* - Never mutates `sections.providers.runtimeScopes`.
|
|
21
21
|
*/
|
|
22
|
-
import { RegistryModulesLockSectionSchema, withLockfileSectionMutate, readWildoSaasLock, } from '@wildo-ai/platform-config-lib';
|
|
22
|
+
import { RegistryModulesLockSectionSchema, withLockfileSectionMutate, readWildoSaasLock, RegistryModulesLockEntrySchema, } from '@wildo-ai/platform-config-lib';
|
|
23
23
|
// L6 (third-pass review): the package's barrel-discipline rule says
|
|
24
24
|
// "Never re-export (except in the root `index.ts` of each package)."
|
|
25
25
|
// The previous `export { WILDO_SAAS_LOCK_RELATIVE_PATH }` here AND in
|
|
@@ -59,8 +59,13 @@ export const RegistryLockfileService = {
|
|
|
59
59
|
initialSection: emptySection,
|
|
60
60
|
}, mutator);
|
|
61
61
|
},
|
|
62
|
-
/**
|
|
63
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Insert or replace one module's entry. The entry is parsed here, so its defaults (`distribution`,
|
|
64
|
+
* `files`) apply in one place and a malformed entry is refused naming the module, before the lockfile
|
|
65
|
+
* is touched (`mutate` then re-validates the whole section).
|
|
66
|
+
*/
|
|
67
|
+
async upsertEntry(saasRoot, input) {
|
|
68
|
+
const entry = RegistryModulesLockEntrySchema.parse(input);
|
|
64
69
|
await this.mutate(saasRoot, (current) => ({
|
|
65
70
|
...current,
|
|
66
71
|
installed: { ...current.installed, [entry.moduleId]: entry },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry-lockfile.service.js","sourceRoot":"","sources":["../../../../src/services/registry-lockfile.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EACL,gCAAgC,EAChC,yBAAyB,EACzB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"registry-lockfile.service.js","sourceRoot":"","sources":["../../../../src/services/registry-lockfile.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EACL,gCAAgC,EAChC,yBAAyB,EACzB,iBAAiB,EAEjB,8BAA8B,GAE/B,MAAM,+BAA+B,CAAC;AACvC,oEAAoE;AACpE,qEAAqE;AACrE,sEAAsE;AACtE,8DAA8D;AAC9D,kEAAkE;AAClE,qEAAqE;AACrE,mCAAmC;AAEnC,MAAM,WAAW,GAAG,SAAkB,CAAC;AAEvC,SAAS,YAAY;IACnB,OAAO,gCAAgC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC;;;;OAIG;IACH,KAAK,CAAC,IAAI,CAAC,QAAgB;QACzB,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;QACvD,MAAM,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;QACtC,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,YAAY,EAAE,CAAC;QAC7C,OAAO,gCAAgC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrD,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,MAAM,CACV,QAAgB,EAChB,OAK8C;QAE9C,OAAO,yBAAyB,CAC9B;YACE,QAAQ;YACR,UAAU,EAAE,WAAW;YACvB,aAAa,EAAE,gCAAgC;YAC/C,cAAc,EAAE,YAAY;SAC7B,EACD,OAAO,CACR,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAW,CAAC,QAAgB,EAAE,KAAoC;QACtE,MAAM,KAAK,GAAG,8BAA8B,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC1D,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YACxC,GAAG,OAAO;YACV,SAAS,EAAE,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE;SAC7D,CAAC,CAAC,CAAC;IACN,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,cAAc,CAAC,IAIpB;QACC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB,CAAC;QAC9C,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK;YAAE,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACjD,KAAK,MAAM,aAAa,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3D,IAAI,aAAa,KAAK,IAAI,CAAC,eAAe;gBAAE,SAAS;YACrD,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;YACpD,IAAI,CAAC,UAAU;gBAAE,SAAS;YAC1B,KAAK,MAAM,SAAS,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC;gBACzC,IAAI,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC7B,SAAS,CAAC,GAAG,CAAC,SAAS,CAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;gBAChD,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;CACO,CAAC","sourcesContent":["/**\n * `RegistryLockfileService` — section-scoped reader/writer for the\n * `sections.modules` slice of `wildo-saas.lock.json` (KD-4).\n *\n * Phase 1 graduates the locking + atomic-write primitives into\n * `@wildo-ai/platform-config-lib` (validated by the Phase 0.B\n * contention spike). This service is a thin wrapper around\n * `withLockfileSectionMutate` that pre-binds the `modules` section\n * key + schema, so module-registry callers cannot accidentally\n * touch sibling sections.\n *\n * Hard contracts (KD-4 / KD-15 / KD-18):\n * - Mutates ONLY `sections.modules`. The shared library preserves\n * `sections.providers`, `sections.i18n`, and every unknown\n * `sections.*` payload verbatim — no defensive logic needed\n * here.\n * - Never writes `.wildo-saas/generated/providers/*.runtime-providers.generated.json`\n * or `src/generated/wildo-provider-runtime.generated.ts` —\n * those are produced by the provider materialization workflow.\n * - Never mutates `sections.providers.runtimeScopes`.\n */\n\nimport {\n RegistryModulesLockSectionSchema,\n withLockfileSectionMutate,\n readWildoSaasLock,\n type RegistryModulesLockSection,\n RegistryModulesLockEntrySchema,\n type RegistryModulesLockEntryInput,\n} from '@wildo-ai/platform-config-lib';\n// L6 (third-pass review): the package's barrel-discipline rule says\n// \"Never re-export (except in the root `index.ts` of each package).\"\n// The previous `export { WILDO_SAAS_LOCK_RELATIVE_PATH }` here AND in\n// `lockfile-repair.service.ts` surfaced the symbol twice from\n// `wildo-module-client`'s root. Consumers that need it can import\n// directly from `@wildo-ai/platform-config-lib`, OR rely on the root\n// `index.ts` to re-export it once.\n\nconst SECTION_KEY = 'modules' as const;\n\nfunction emptySection(): RegistryModulesLockSection {\n return RegistryModulesLockSectionSchema.parse({});\n}\n\nexport const RegistryLockfileService = {\n /**\n * Read the current `sections.modules` payload (or an empty section\n * when missing). Unsynchronized — callers needing read-after-write\n * coherence should read INSIDE `mutate`.\n */\n async read(saasRoot: string): Promise<RegistryModulesLockSection> {\n const envelope = await readWildoSaasLock({ saasRoot });\n const raw = envelope.sections.modules;\n if (raw === undefined) return emptySection();\n return RegistryModulesLockSectionSchema.parse(raw);\n },\n\n /**\n * Run `mutator` against the current `sections.modules` payload\n * under an OS-level lock + atomic-write commit. The mutator can\n * either mutate-in-place or return a new value; either way the\n * result is re-validated against `RegistryModulesLockSectionSchema`\n * before commit.\n */\n async mutate(\n saasRoot: string,\n mutator: (\n current: RegistryModulesLockSection,\n ) =>\n | RegistryModulesLockSection\n | void\n | Promise<RegistryModulesLockSection | void>,\n ): Promise<RegistryModulesLockSection> {\n return withLockfileSectionMutate(\n {\n saasRoot,\n sectionKey: SECTION_KEY,\n sectionSchema: RegistryModulesLockSectionSchema,\n initialSection: emptySection,\n },\n mutator,\n );\n },\n\n /**\n * Insert or replace one module's entry. The entry is parsed here, so its defaults (`distribution`,\n * `files`) apply in one place and a malformed entry is refused naming the module, before the lockfile\n * is touched (`mutate` then re-validates the whole section).\n */\n async upsertEntry(saasRoot: string, input: RegistryModulesLockEntryInput): Promise<void> {\n const entry = RegistryModulesLockEntrySchema.parse(input);\n await this.mutate(saasRoot, (current) => ({\n ...current,\n installed: { ...current.installed, [entry.moduleId]: entry },\n }));\n },\n\n /**\n * Find every other lockfile entry whose `files[]` claims any of the\n * given paths. Used by `update`/`remove` to skip deletes that would\n * silently pull files out from under modules that own them too\n * (cross-module file overlap).\n *\n * Returns a `Map<relPath, owners[]>` where `owners` excludes\n * `excludeModuleId`. Empty array → uniquely owned by the excluded\n * module, safe to delete.\n */\n async findFileOwners(args: {\n saasRoot: string;\n paths: readonly string[];\n excludeModuleId: string;\n }): Promise<Map<string, string[]>> {\n const section = await this.read(args.saasRoot);\n const claimedBy = new Map<string, string[]>();\n for (const p of args.paths) claimedBy.set(p, []);\n for (const otherModuleId of Object.keys(section.installed)) {\n if (otherModuleId === args.excludeModuleId) continue;\n const otherEntry = section.installed[otherModuleId];\n if (!otherEntry) continue;\n for (const otherFile of otherEntry.files) {\n if (claimedBy.has(otherFile)) {\n claimedBy.get(otherFile)!.push(otherModuleId);\n }\n }\n }\n return claimedBy;\n },\n} as const;\n"]}
|
|
@@ -12,9 +12,10 @@
|
|
|
12
12
|
* Auth (KD-11): if `authToken` is supplied, every request adds
|
|
13
13
|
* `Authorization: Bearer <token>`. Public read paths work without
|
|
14
14
|
* a token; restricted paths return 401/403, surfaced as
|
|
15
|
-
* `RegistryAuthRequiredError`
|
|
15
|
+
* `RegistryAuthRequiredError` (no key sent), `RegistryCredentialRejectedError` (a key sent and refused)
|
|
16
|
+
* or `RegistryAuthForbiddenError`; a 429 is `RegistryRateLimitedError`.
|
|
16
17
|
*/
|
|
17
|
-
import { type ModuleManifest, type ModuleVersionRecord } from '@wildo-ai/platform-config-lib';
|
|
18
|
+
import { type ModuleManifest, type ModuleSearchFilter, type ModuleSearchHit, type RegistryYankNotice, type ModuleVersionRecord } from '@wildo-ai/platform-config-lib';
|
|
18
19
|
export interface RegistryServerClientOptions {
|
|
19
20
|
/** Registry origin (e.g. `https://registry.wildo.ai`). */
|
|
20
21
|
baseUrl: string;
|
|
@@ -27,6 +28,11 @@ export interface RegistryResolveResult {
|
|
|
27
28
|
/** Resolved exact version (matches `Sha256DigestSchema` for digest). */
|
|
28
29
|
version: string;
|
|
29
30
|
artifactDigest: string;
|
|
31
|
+
/**
|
|
32
|
+
* Present when the resolved release is yanked. Only an EXACT-version request can resolve to one
|
|
33
|
+
* (#1758) — ranges and tags never do — so the caller is pinned to it and should warn, not refuse.
|
|
34
|
+
*/
|
|
35
|
+
yanked?: RegistryYankNotice;
|
|
30
36
|
}
|
|
31
37
|
export interface RegistryInfoResult {
|
|
32
38
|
moduleId: string;
|
|
@@ -35,11 +41,8 @@ export interface RegistryInfoResult {
|
|
|
35
41
|
/** dist-tag → version map (e.g. `latest → 1.2.3`). */
|
|
36
42
|
tags: Readonly<Record<string, string>>;
|
|
37
43
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
description?: string;
|
|
41
|
-
latestVersion: string;
|
|
42
|
-
}
|
|
44
|
+
/** One search hit: the module, its latest version, and (#2014) its kind and provider facts. */
|
|
45
|
+
export type RegistrySearchHit = ModuleSearchHit;
|
|
43
46
|
export declare const RegistryServerClient: {
|
|
44
47
|
/**
|
|
45
48
|
* Liveness probe against `/health`. Returns boolean — used by
|
|
@@ -68,12 +71,13 @@ export declare const RegistryServerClient: {
|
|
|
68
71
|
moduleId: string;
|
|
69
72
|
}) => Promise<RegistryInfoResult>;
|
|
70
73
|
/**
|
|
71
|
-
* Search the registry's metadata index
|
|
72
|
-
*
|
|
74
|
+
* Search the registry's metadata index: a module-id substring, and (#2014) the manifest filters —
|
|
75
|
+
* kind, engine capability and provider maturity. The server refuses an unknown filter value.
|
|
73
76
|
*/
|
|
74
77
|
readonly search: (options: RegistryServerClientOptions, args: {
|
|
75
78
|
domain: string;
|
|
76
79
|
query: string;
|
|
80
|
+
filter?: ModuleSearchFilter;
|
|
77
81
|
}) => Promise<readonly RegistrySearchHit[]>;
|
|
78
82
|
/**
|
|
79
83
|
* Stream-download a published artifact's bytes and verify the
|
|
@@ -89,6 +93,28 @@ export declare const RegistryServerClient: {
|
|
|
89
93
|
}) => Promise<{
|
|
90
94
|
bytes: Uint8Array;
|
|
91
95
|
integrity: string;
|
|
96
|
+
yanked?: RegistryYankNotice;
|
|
97
|
+
}>;
|
|
98
|
+
/**
|
|
99
|
+
* Yank a release (#1758): ranges and `latest` stop selecting it, and `latest` moves to the highest
|
|
100
|
+
* live release; an exact pin still installs it, with a warning. Needs a key holding the version UPDATE
|
|
101
|
+
* role in the domain's owning organization. Returns where `latest` points afterwards.
|
|
102
|
+
*/
|
|
103
|
+
readonly yank: (options: RegistryServerClientOptions, args: {
|
|
104
|
+
domain: string;
|
|
105
|
+
moduleId: string;
|
|
106
|
+
version: string;
|
|
107
|
+
reason?: string;
|
|
108
|
+
}) => Promise<{
|
|
109
|
+
latest: string | null;
|
|
110
|
+
}>;
|
|
111
|
+
/** Restore a yanked release (#1758); `latest` returns to it when it outranks the current target. */
|
|
112
|
+
readonly unyank: (options: RegistryServerClientOptions, args: {
|
|
113
|
+
domain: string;
|
|
114
|
+
moduleId: string;
|
|
115
|
+
version: string;
|
|
116
|
+
}) => Promise<{
|
|
117
|
+
latest: string | null;
|
|
92
118
|
}>;
|
|
93
119
|
};
|
|
94
120
|
//# sourceMappingURL=registry-server-client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry-server-client.d.ts","sourceRoot":"","sources":["../../../../src/services/registry-server-client.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"registry-server-client.d.ts","sourceRoot":"","sources":["../../../../src/services/registry-server-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,EAwBL,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACzB,MAAM,+BAA+B,CAAC;AAsBvC,MAAM,WAAW,2BAA2B;IAC1C,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA6DD,MAAM,WAAW,qBAAqB;IACpC,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,cAAc,CAAC;IACzB,QAAQ,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACzC,sDAAsD;IACtD,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACxC;AAED,+FAA+F;AAC/F,MAAM,MAAM,iBAAiB,GAAG,eAAe,CAAC;AAEhD,eAAO,MAAM,oBAAoB;IAC/B;;;;;;OAMG;6BACiB,2BAA2B,KAAG,OAAO,CAAC,OAAO,CAAC;IAalE;;;;OAIG;gCAEQ,2BAA2B,QAC9B;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KACxD,OAAO,CAAC,qBAAqB,CAAC;IAkDjC;;;OAGG;6BAEQ,2BAA2B,QAC9B;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KACzC,OAAO,CAAC,kBAAkB,CAAC;IAyB9B;;;OAGG;+BAEQ,2BAA2B,QAC9B;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,kBAAkB,CAAA;KAAE,KACnE,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC;IA2BxC;;;;;OAKG;iCAEQ,2BAA2B,QAC9B;QACJ,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,cAAc,EAAE,MAAM,CAAC;KACxB,KACA,OAAO,CAAC;QAAE,KAAK,EAAE,UAAU,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,kBAAkB,CAAA;KAAE,CAAC;IAiDjF;;;;OAIG;6BAEQ,2BAA2B,QAC9B;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,KAC3E,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAIrC,oGAAoG;+BAEzF,2BAA2B,QAC9B;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAC1D,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CAG7B,CAAC"}
|