@noodleseed/one 0.165.0 → 0.167.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/dist/commands/author-loop.d.ts.map +1 -1
  2. package/dist/commands/author-loop.js +18 -3
  3. package/dist/commands/author-loop.js.map +1 -1
  4. package/dist/commands/deploy-ops.d.ts.map +1 -1
  5. package/dist/commands/deploy-ops.js +21 -2
  6. package/dist/commands/deploy-ops.js.map +1 -1
  7. package/dist/commands/deploy-status-ops.d.ts +2 -0
  8. package/dist/commands/deploy-status-ops.d.ts.map +1 -1
  9. package/dist/commands/deploy-status-ops.js +27 -0
  10. package/dist/commands/deploy-status-ops.js.map +1 -1
  11. package/dist/control-plane-request.d.ts +2 -0
  12. package/dist/control-plane-request.d.ts.map +1 -1
  13. package/dist/control-plane-request.js +18 -0
  14. package/dist/control-plane-request.js.map +1 -1
  15. package/dist/deploy.d.ts +2 -1
  16. package/dist/deploy.d.ts.map +1 -1
  17. package/dist/deploy.js +15 -0
  18. package/dist/deploy.js.map +1 -1
  19. package/dist/dev.d.ts +2 -0
  20. package/dist/dev.d.ts.map +1 -1
  21. package/dist/dev.js +12 -1
  22. package/dist/dev.js.map +1 -1
  23. package/dist/devtools-auth-session.d.ts +5 -0
  24. package/dist/devtools-auth-session.d.ts.map +1 -1
  25. package/dist/devtools-auth-session.js +41 -2
  26. package/dist/devtools-auth-session.js.map +1 -1
  27. package/dist/devtools-auth-types.d.ts +2 -1
  28. package/dist/devtools-auth-types.d.ts.map +1 -1
  29. package/dist/devtools-auth-types.js.map +1 -1
  30. package/dist/devtools-auth-ui.d.ts +1 -1
  31. package/dist/devtools-auth-ui.d.ts.map +1 -1
  32. package/dist/devtools-auth-ui.js +13 -5
  33. package/dist/devtools-auth-ui.js.map +1 -1
  34. package/dist/devtools-harness.d.ts +1 -0
  35. package/dist/devtools-harness.d.ts.map +1 -1
  36. package/dist/devtools-harness.js +12 -7
  37. package/dist/devtools-harness.js.map +1 -1
  38. package/dist/devtools-preview.d.ts +1 -0
  39. package/dist/devtools-preview.d.ts.map +1 -1
  40. package/dist/devtools-preview.js +21 -6
  41. package/dist/devtools-preview.js.map +1 -1
  42. package/dist/devtools-rpc-forwarder.d.ts +1 -0
  43. package/dist/devtools-rpc-forwarder.d.ts.map +1 -1
  44. package/dist/devtools-rpc-forwarder.js +2 -1
  45. package/dist/devtools-rpc-forwarder.js.map +1 -1
  46. package/dist/preview-session.d.ts +1 -0
  47. package/dist/preview-session.d.ts.map +1 -1
  48. package/dist/preview-session.js +1 -0
  49. package/dist/preview-session.js.map +1 -1
  50. package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +3 -3
  51. package/node_modules/@noodle-borg/agent-kit/dist/skill-authoring-auth-ref.js +5 -2
  52. package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
  53. package/node_modules/@noodle-borg/agent-packaging/dist/product-skill-types.d.ts +1 -0
  54. package/node_modules/@noodle-borg/agent-packaging/dist/product-skill-validation.js +4 -0
  55. package/node_modules/@noodle-borg/app-package/dist/schema.js +1 -0
  56. package/node_modules/@noodle-borg/app-package/dist/types.d.ts +1 -0
  57. package/node_modules/@noodle-borg/authoring/dist/server.d.ts +3 -1
  58. package/node_modules/@noodle-borg/authoring/dist/server.js +7 -6
  59. package/node_modules/@noodle-borg/capabilities/dist/index.js +7 -0
  60. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-local-authoring.js +8 -0
  61. package/node_modules/@noodle-borg/compiler/dist/app-package/compile.js +9 -1
  62. package/node_modules/@noodle-borg/compiler/dist/artifact/catalog-emission.js +3 -0
  63. package/node_modules/@noodle-borg/compiler/dist/artifact/types.d.ts +2 -0
  64. package/node_modules/@noodle-borg/compiler/dist/artifact/version.d.ts +2 -1
  65. package/node_modules/@noodle-borg/compiler/dist/artifact/version.js +2 -1
  66. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +5 -0
  67. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +12 -2
  68. package/node_modules/@noodle-borg/module/dist/customer-auth-access.d.ts +7 -0
  69. package/node_modules/@noodle-borg/module/dist/customer-auth-access.js +6 -0
  70. package/node_modules/@noodle-borg/module/dist/index.d.ts +1 -0
  71. package/node_modules/@noodle-borg/module/dist/index.js +1 -0
  72. package/node_modules/@noodle-borg/protocol/dist/handlers/tools.js +3 -0
  73. package/node_modules/@noodle-borg/protocol/dist/index.d.ts +1 -0
  74. package/node_modules/@noodle-borg/protocol/dist/index.js +1 -0
  75. package/node_modules/@noodle-borg/protocol/dist/mapping.d.ts +5 -1
  76. package/node_modules/@noodle-borg/protocol/dist/mapping.js +15 -7
  77. package/node_modules/@noodle-borg/protocol/dist/sdk-server.d.ts +2 -0
  78. package/node_modules/@noodle-borg/protocol/dist/tool-discovery.d.ts +14 -0
  79. package/node_modules/@noodle-borg/protocol/dist/tool-discovery.js +12 -0
  80. package/node_modules/@noodle-borg/protocol/dist/v2/server.js +3 -0
  81. package/node_modules/@noodle-borg/service/dist/build-info.js +4 -2
  82. package/node_modules/@noodle-borg/service/dist/customer-auth-audience-binding.js +16 -5
  83. package/node_modules/@noodle-borg/service/dist/deployment-access-mode.js +24 -0
  84. package/node_modules/@noodle-borg/service/dist/deployment-activation-precondition.js +27 -0
  85. package/node_modules/@noodle-borg/service/dist/deployment-authentication.js +19 -0
  86. package/node_modules/@noodle-borg/service/dist/deployment-execution.js +2 -2
  87. package/node_modules/@noodle-borg/service/dist/deployment-record-version.js +55 -0
  88. package/node_modules/@noodle-borg/service/dist/developer-mcp/inspection.js +15 -1
  89. package/node_modules/@noodle-borg/service/dist/registry-access.js +47 -30
  90. package/node_modules/@noodle-borg/service/dist/registry-compile.js +42 -1
  91. package/node_modules/@noodle-borg/service/dist/registry-deploy-transaction.js +105 -15
  92. package/node_modules/@noodle-borg/service/dist/registry-rollback.js +15 -1
  93. package/node_modules/@noodle-borg/service/dist/registry-state.d.ts +3 -6
  94. package/node_modules/@noodle-borg/service/dist/registry-state.js +15 -7
  95. package/node_modules/@noodle-borg/service/dist/registry-status.js +5 -3
  96. package/node_modules/@noodle-borg/service/dist/registry-targets.js +43 -28
  97. package/node_modules/@noodle-borg/service/dist/registry-types.d.ts +4 -2
  98. package/node_modules/@noodle-borg/service/dist/registry.js +68 -72
  99. package/node_modules/@noodle-borg/service/dist/routes/access.js +7 -0
  100. package/node_modules/@noodle-borg/service/dist/routes/auth-doctor-live.js +3 -2
  101. package/node_modules/@noodle-borg/service/dist/routes/control-plane.js +1 -0
  102. package/node_modules/@noodle-borg/service/dist/routes/deploy-preflight.js +2 -2
  103. package/node_modules/@noodle-borg/service/dist/serve-resource-auth.js +1 -1
  104. package/node_modules/@noodle-borg/service/dist/store/in-memory.d.ts +3 -7
  105. package/node_modules/@noodle-borg/service/dist/store/in-memory.js +13 -4
  106. package/node_modules/@noodle-borg/service/dist/store/json-file.d.ts +3 -7
  107. package/node_modules/@noodle-borg/service/dist/store/json-file.js +15 -6
  108. package/node_modules/@noodle-borg/service/dist/store/records.js +15 -2
  109. package/node_modules/@noodle-borg/service/dist/store.d.ts +23 -8
  110. package/node_modules/@noodle-borg/service/package.json +1 -1
  111. package/node_modules/@noodle-borg/transport-http/dist/handler.d.ts +2 -3
  112. package/node_modules/@noodle-borg/transport-http/dist/handler.js +3 -4
  113. package/node_modules/@noodle-borg/transport-http/dist/identity-authorization.d.ts +3 -2
  114. package/node_modules/@noodle-borg/transport-http/dist/identity-authorization.js +23 -12
  115. package/node_modules/@noodle-borg/transport-http/dist/index.d.ts +1 -0
  116. package/node_modules/@noodle-borg/transport-http/dist/serve-request.js +3 -0
  117. package/node_modules/@noodle-borg/transport-http/dist/target-authentication.d.ts +15 -0
  118. package/node_modules/@noodle-borg/transport-http/dist/target-authentication.js +9 -0
  119. package/node_modules/@noodle-borg/wire-contracts/dist/control-plane.d.ts +23 -0
  120. package/node_modules/@noodle-borg/wire-contracts/dist/control-plane.js +34 -4
  121. package/node_modules/@noodle-borg/wire-contracts/dist/index.d.ts +6 -0
  122. package/node_modules/@noodle-borg/wire-contracts/dist/index.js +4 -1
  123. package/node_modules/@noodle-borg/wire-contracts/dist/service-info.d.ts +41 -0
  124. package/node_modules/@noodle-borg/wire-contracts/dist/service-info.js +26 -0
  125. package/package.json +2 -2
  126. package/node_modules/@noodle-borg/service/dist/routes/access-mode.js +0 -7
@@ -3,8 +3,10 @@ import { mkdir, readdir, readFile, rename, rm, writeFile } from 'node:fs/promise
3
3
  import { join } from 'node:path';
4
4
  import { normalizeServerVersion } from '@noodle-borg/module';
5
5
  import { assertSameAppPackageSnapshot, sanitizeDeployRecordAppPackageSnapshot, } from '../app-package-snapshot.js';
6
- import { assertCustomerAuthRestorePrecondition, assertUniqueActiveCustomerAuthAudienceBindings, findActiveCustomerAuthAudienceConflict, } from '../customer-auth-audience-binding.js';
6
+ import { assertCustomerAuthRestorePrecondition, assertUniqueActiveCustomerAuthAudienceBindings, findActiveCustomerAuthAudienceConflict, requiresCustomerAuthProjection, } from '../customer-auth-audience-binding.js';
7
+ import { matchesDeploymentActivation } from '../deployment-activation-precondition.js';
7
8
  import { assertDeploymentActivationUnlocked, assertDeploymentAppendUnlocked, } from '../deployment-lock.js';
9
+ import { assertDeploymentAppendPolicy, assertDeploymentAppendVersion, } from '../deployment-record-version.js';
8
10
  import { defaultActiveRecord, sameDeploymentScope, sameTenantRecord, } from '../deployment-versioning.js';
9
11
  import { appArchivedAt, deploymentSummary, matchesDeploymentFilter, paginateAppSummaries, planAppArchive, planAppRestore, planArchivedAppSweep, preserveDeploymentOwnerState, resolveActiveAccessUpdate, resolveProductionEnvironment, summarizeApps, summarizeEnvs, validateDeploymentListFilter, withoutArchiveStamp, } from './records.js';
10
12
  import { DEPLOYMENT_ID_PATTERN, validateSlug, validateTenantRef } from './validate.js';
@@ -22,10 +24,10 @@ export class JsonFileArtifactStore {
22
24
  this.#dir = join(dataDir, 'deployments');
23
25
  this.#environmentMetadataDir = join(dataDir, 'environment-metadata');
24
26
  }
25
- append(record) {
26
- return this.#serializeLifecycle(() => this.#appendUnlocked(record));
27
+ append(record, precondition) {
28
+ return this.#serializeLifecycle(() => this.#appendUnlocked(record, precondition));
27
29
  }
28
- async #appendUnlocked(record) {
30
+ async #appendUnlocked(record, precondition) {
29
31
  // Defence in depth: `deploymentId` becomes a filename, so reject anything outside the minted shape
30
32
  // (`mintDeploymentId` only ever produces `[a-z0-9-]`). Guards this public class against a caller that
31
33
  // passes an untrusted id (no path traversal, no escaping the data dir).
@@ -34,6 +36,8 @@ export class JsonFileArtifactStore {
34
36
  throw new Error(`invalid deploymentId for persistence: "${record.deploymentId}"`);
35
37
  }
36
38
  const records = await this.loadAll();
39
+ assertDeploymentAppendVersion(records, record);
40
+ assertDeploymentAppendPolicy(records, record, precondition);
37
41
  const existing = records.find((candidate) => candidate.deploymentId === record.deploymentId);
38
42
  if (existing !== undefined)
39
43
  assertSameAppPackageSnapshot(existing, record);
@@ -127,7 +131,7 @@ export class JsonFileArtifactStore {
127
131
  const target = await this.get(deploymentId);
128
132
  if (target === undefined || !sameTenantRecord(target, safe))
129
133
  return undefined;
130
- if (precondition !== undefined && target.accessMode !== precondition.expectedAccessMode) {
134
+ if (!matchesDeploymentActivation(target, precondition)) {
131
135
  return undefined;
132
136
  }
133
137
  const records = await this.loadAll();
@@ -135,11 +139,16 @@ export class JsonFileArtifactStore {
135
139
  const previousActive = records
136
140
  .filter((record) => record.active && sameDeploymentScope(record, target))
137
141
  .sort((a, b) => b.deploymentVersion - a.deploymentVersion)[0];
142
+ if (precondition === undefined && previousActive !== undefined) {
143
+ assertDeploymentAppendVersion([previousActive], { ...target, active: true });
144
+ }
145
+ assertDeploymentAppendPolicy(records, { ...target, active: true }, precondition?.expectedActivePolicy);
138
146
  const alreadyActive = previousActive?.deploymentId === target.deploymentId;
139
147
  const activated = {
140
148
  ...target,
141
149
  active: true,
142
- ...(target.accessMode === 'customers' && precondition?.serverAuth !== undefined
150
+ ...(requiresCustomerAuthProjection(target, precondition?.serverAuth) &&
151
+ precondition?.serverAuth !== undefined
143
152
  ? { serverAuth: precondition.serverAuth }
144
153
  : {}),
145
154
  };
@@ -5,6 +5,7 @@
5
5
  * own persistence and feed their own raw records/anchors through these pure functions so every
6
6
  * backend groups, ranks, and sorts identically.
7
7
  */
8
+ import { requiresCustomerAuthProjection } from '../customer-auth-audience-binding.js';
8
9
  import { sameTenantRecord } from '../deployment-versioning.js';
9
10
  import { deploymentOwnerSubject } from '../registry-helpers.js';
10
11
  import { validateSlug } from './validate.js';
@@ -139,17 +140,29 @@ export function resolveActiveAccessUpdate(record, ref, input) {
139
140
  record.archivedAt !== undefined ||
140
141
  !sameTenantRecord(record, ref) ||
141
142
  record.accessMode !== input.expectedAccessMode ||
143
+ record.schemaVersion !== (input.expectedSchemaVersion ?? 1) ||
144
+ (input.expectedManifest !== undefined && record.manifest !== input.expectedManifest) ||
142
145
  deploymentOwnerSubject(record) !== input.expectedOwnerSubject)
143
146
  return undefined;
144
147
  if ((record.accessMode ?? 'owner-only') === input.accessMode &&
148
+ (input.schemaVersion === undefined || record.schemaVersion === input.schemaVersion) &&
145
149
  (input.ownerSubject === undefined || deploymentOwnerSubject(record) === input.ownerSubject) &&
146
- input.accessMode !== 'customers')
150
+ !requiresCustomerAuthProjection({
151
+ ...record,
152
+ accessMode: input.accessMode,
153
+ schemaVersion: input.schemaVersion ?? record.schemaVersion,
154
+ }, input.serverAuth))
147
155
  return record;
148
156
  return {
149
157
  ...record,
150
158
  accessMode: input.accessMode,
159
+ schemaVersion: input.schemaVersion ?? record.schemaVersion,
151
160
  ...(input.ownerSubject !== undefined ? { ownerSubject: input.ownerSubject } : {}),
152
- ...(input.accessMode === 'customers' && input.serverAuth !== undefined
161
+ ...(requiresCustomerAuthProjection({
162
+ ...record,
163
+ accessMode: input.accessMode,
164
+ schemaVersion: input.schemaVersion ?? record.schemaVersion,
165
+ }, input.serverAuth) && input.serverAuth !== undefined
153
166
  ? { serverAuth: input.serverAuth }
154
167
  : {}),
155
168
  };
@@ -3,7 +3,7 @@ import type { OrganizationStore } from '@noodle-borg/control-plane/portable';
3
3
  import type { OrgMembershipSource } from '@noodle-borg/module';
4
4
  import type { SealedSecret } from '@noodle-borg/runtime';
5
5
  import type { AccessMode } from '@noodle-borg/transport-http';
6
- import type { DeploymentSource } from '@noodle-borg/wire-contracts';
6
+ import type { DeploymentAuthentication, DeploymentSource } from '@noodle-borg/wire-contracts';
7
7
  import type { AppPackageSnapshotV1 } from './app-package-snapshot.js';
8
8
  import type { ProductionEnvironmentChange } from './store/environment-production.js';
9
9
  export type { ActiveMcpSubdomainClaim, ChangeMcpSubdomainInput, CreateOrgWithOwnerInput, McpSubdomainMutationResult, McpSubdomainSetting, OrgDomainRecord, OrgInvitationRecord, OrgMemberRecord, OrgOpenAIAppsChallengeRecord, OrgRecord, OrgRole, PersonalWorkspaceProvisionInput, PersonalWorkspaceProvisionResult, SignupAllowlistKind, SignupAllowlistRecord, WelcomeEmailRecord, } from '@noodle-borg/control-plane/portable';
@@ -33,7 +33,7 @@ export type SecretEnvelope = {
33
33
  * of the source strings, so the inputs + identity are a faithful, reproducible record.
34
34
  */
35
35
  export interface DeployRecord {
36
- /** Record format version, for forward migration of the persisted shape. Current writers emit `1`. */
36
+ /** Record format version; customer mixed adoption writes 2, which remains sticky on new records. */
37
37
  readonly schemaVersion: number;
38
38
  readonly deploymentId: string;
39
39
  readonly orgSlug: string;
@@ -241,6 +241,7 @@ export interface DeploymentStatus {
241
241
  readonly createdByEmail?: string;
242
242
  readonly ownerSubject?: string;
243
243
  readonly accessMode: AccessMode;
244
+ readonly authentication?: DeploymentAuthentication;
244
245
  readonly deploymentLock?: DeploymentLockMetadata;
245
246
  };
246
247
  readonly health: {
@@ -257,11 +258,29 @@ export interface DeploymentActivationResult {
257
258
  readonly alreadyActive: boolean;
258
259
  }
259
260
  /** One atomic access/owner compare-and-set over the active deployment's observed state. */
261
+ export interface DeploymentPolicyPrecondition {
262
+ readonly active: Pick<DeployRecord, 'deploymentId' | 'schemaVersion' | 'accessMode' | 'ownerSubject' | 'manifest' | 'serverAuth'> | null;
263
+ }
264
+ /** The source and authority validated before atomically activating this exact target revision. */
265
+ export interface DeploymentActivationPrecondition {
266
+ readonly expectedAccessMode: AccessMode | undefined;
267
+ /** Older callers are restricted to schema-1 targets. */
268
+ readonly expectedSchemaVersion?: number;
269
+ readonly expectedRevision?: Pick<DeployRecord, 'manifest' | 'connectors' | 'hostedAssets' | 'serverAuth' | 'serverVersion' | 'ownerSubject' | 'createdBySubject' | 'orgMembershipSources' | 'archivedAt'>;
270
+ readonly expectedActivePolicy?: DeploymentPolicyPrecondition;
271
+ /** Compiler-authoritative projection, applied only after revision validation. */
272
+ readonly serverAuth?: TenantAuthConfig;
273
+ }
260
274
  export interface ActiveAccessUpdateInput {
275
+ /** Internal policy adoption; callers cannot author a deployment schema version. */
276
+ readonly schemaVersion?: 2;
261
277
  readonly accessMode: AccessMode;
262
278
  /** Desired owner mutation. Omission preserves the current effective owner; clearing is unsupported. */
263
279
  readonly ownerSubject?: string;
264
280
  readonly expectedAccessMode: AccessMode | undefined;
281
+ /** Older callers omit this and are restricted to version-1 records. */
282
+ readonly expectedSchemaVersion?: number;
283
+ readonly expectedManifest?: string;
265
284
  readonly expectedOwnerSubject: string | undefined;
266
285
  /** Compiler-authoritative projection persisted atomically when enabling customer access. */
267
286
  readonly serverAuth?: TenantAuthConfig;
@@ -283,7 +302,7 @@ export interface ControlPlaneStore extends OrganizationStore {
283
302
  * an earlier deployment without changing the tenant-facing URL.
284
303
  */
285
304
  export interface ArtifactStore {
286
- append(record: DeployRecord): Promise<void>;
305
+ append(record: DeployRecord, precondition?: DeploymentPolicyPrecondition): Promise<void>;
287
306
  loadAll(): Promise<readonly DeployRecord[]>;
288
307
  /**
289
308
  * Fetch one server's record by id, or `undefined` if absent. A point read used by lazy
@@ -301,11 +320,7 @@ export interface ArtifactStore {
301
320
  * bindings. Implementations may optimize this security lookup; registries fall back to `loadAll`.
302
321
  */
303
322
  findActiveCustomerAuthAudienceConflict?(ref: TenantRef, auth: TenantAuthConfig): Promise<TenantRef | undefined>;
304
- activateDeployment(ref: TenantRef, deploymentId: string, precondition?: {
305
- readonly expectedAccessMode: AccessMode | undefined;
306
- /** Compiler-authoritative projection persisted atomically when activating customer access. */
307
- readonly serverAuth?: TenantAuthConfig;
308
- }, options?: {
323
+ activateDeployment(ref: TenantRef, deploymentId: string, precondition?: DeploymentActivationPrecondition, options?: {
309
324
  readonly automationId?: string;
310
325
  }): Promise<DeploymentActivationResult | undefined>;
311
326
  /**
@@ -39,7 +39,7 @@
39
39
  "dependencies": {
40
40
  "@modelcontextprotocol/sdk": "^1.29.0",
41
41
  "@noodle-borg/admission-limits": "0.0.0",
42
- "@noodle-borg/agent-kit": "0.101.0",
42
+ "@noodle-borg/agent-kit": "0.102.0",
43
43
  "@noodle-borg/app-package": "0.0.0",
44
44
  "@noodle-borg/assistant-gateway": "0.0.0",
45
45
  "@noodle-borg/auth": "0.0.0",
@@ -5,6 +5,7 @@ import { type TlsPosture } from './front-door.js';
5
5
  import { type Logger } from './logging.js';
6
6
  import { type PublicTenantRouting } from './routing.js';
7
7
  import type { HostedToolAuthorizationObserver } from './service-principal-authorization.js';
8
+ import { type TargetAuthentication } from './target-authentication.js';
8
9
  import type { HostedToolDispatchHook } from './tool-dispatch.js';
9
10
  export type { AccessMode, AdmissionCategory, AdmissionContext, AdmissionDecision, AdmissionGate, DataPlaneAuthorizationResult, DataPlaneIdentityAuthorizer, OwnerTokenVerifier, TenantRouteRef, } from '@noodle-borg/module';
10
11
  export type { PublicTenantRouting } from './routing.js';
@@ -78,9 +79,7 @@ export interface ServedTarget {
78
79
  readonly app?: string;
79
80
  readonly environment?: string;
80
81
  readonly orgMembershipSources?: readonly OrgMembershipSource[];
81
- readonly verifyToken?: OwnerTokenVerifier;
82
- readonly authServerIssuer?: string;
83
- readonly authServerIssuers?: readonly string[];
82
+ readonly authentication?: TargetAuthentication;
84
83
  readonly intentCaptureMode?: IntentCaptureMode;
85
84
  }
86
85
  export type ServerLookup = (deploymentId: string) => Promise<ServedTarget | undefined>;
@@ -4,6 +4,7 @@ import { noopLogger } from './logging.js';
4
4
  import { guard, logRequest, rpcError, sendJson } from './responses.js';
5
5
  import { resolveRoute } from './routing.js';
6
6
  import { serveRequest } from './serve-request.js';
7
+ import { resolveTargetAuthentication } from './target-authentication.js';
7
8
  const DEFAULT_ENDPOINT = '/mcp';
8
9
  const DEFAULT_MAX_BODY = 1 << 20;
9
10
  /** Build the single-artifact raw-Node security front door. */
@@ -33,7 +34,7 @@ export function createMcpHttpHandler(target, options = {}) {
33
34
  app: undefined,
34
35
  environment: undefined,
35
36
  orgMembershipSources: undefined,
36
- verifyOwnerToken: options.verifyOwnerToken,
37
+ authentication: resolveTargetAuthentication({}, options.verifyOwnerToken),
37
38
  authorizeDataPlaneIdentity: options.authorizeDataPlaneIdentity,
38
39
  admissionGate: options.admissionGate,
39
40
  captureRequestEvent: options.captureRequestEvent,
@@ -101,9 +102,7 @@ export function createMcpRouter(lookup, options = {}) {
101
102
  ownerSubject: resolvedTarget.ownerSubject,
102
103
  org: resolvedTarget.org,
103
104
  orgMembershipSources: resolvedTarget.orgMembershipSources,
104
- verifyOwnerToken: resolvedTarget.accessMode === 'customers'
105
- ? resolvedTarget.verifyToken
106
- : (resolvedTarget.verifyToken ?? options.verifyOwnerToken),
105
+ authentication: resolveTargetAuthentication(resolvedTarget, options.verifyOwnerToken),
107
106
  authorizeDataPlaneIdentity: options.authorizeDataPlaneIdentity,
108
107
  admissionGate: options.admissionGate,
109
108
  captureRequestEvent: options.captureRequestEvent,
@@ -1,12 +1,13 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
- import type { AccessMode, DataPlaneIdentityAuthorizer, OrgMembershipSource, OwnerTokenVerifier } from '@noodle-borg/module';
2
+ import type { AccessMode, DataPlaneIdentityAuthorizer, OrgMembershipSource } from '@noodle-borg/module';
3
3
  import { type ProtocolRequestContext } from '@noodle-borg/protocol';
4
+ import type { TargetAuthentication } from './target-authentication.js';
4
5
  export interface IdentityAuthorizationOptions {
5
6
  readonly accessMode: AccessMode | undefined;
6
7
  readonly ownerSubject: string | undefined;
7
8
  readonly org: string | undefined;
8
9
  readonly orgMembershipSources: readonly OrgMembershipSource[] | undefined;
9
- readonly verifyOwnerToken: OwnerTokenVerifier | undefined;
10
+ readonly authentication: TargetAuthentication;
10
11
  readonly authorizeDataPlaneIdentity: DataPlaneIdentityAuthorizer | undefined;
11
12
  /** Whether to trust forwarded-proto when reconstructing the public resource/PRM URLs. */
12
13
  readonly trustProxy: boolean;
@@ -9,13 +9,13 @@ import { header } from './request-capture.js';
9
9
  export async function authorizeIdentityMode(req, res, auth) {
10
10
  const challenge = protectedResourceMetadataUrl(req, auth);
11
11
  const token = bearerToken(header(req, 'authorization'));
12
- if (token === null || auth.verifyOwnerToken === undefined) {
12
+ if (token === null || auth.authentication.verifyToken === undefined) {
13
13
  sendUnauthorized(res, challenge);
14
14
  return { allow: false, reason: token === null ? 'missing_token' : 'verifier_unavailable' };
15
15
  }
16
- const verification = await auth.verifyOwnerToken(token, canonicalResourceUrl(req, auth));
16
+ const verification = await auth.authentication.verifyToken(token, canonicalResourceUrl(req, auth));
17
17
  if (verification === null) {
18
- sendUnauthorized(res, challenge);
18
+ sendUnauthorized(res, challenge, 'invalid_token');
19
19
  return { allow: false, reason: 'token_rejected' };
20
20
  }
21
21
  const { caller: identity } = verification;
@@ -76,27 +76,38 @@ export async function authorizeIdentityMode(req, res, auth) {
76
76
  */
77
77
  export async function authorizePublicServiceMode(req, auth) {
78
78
  const token = bearerToken(header(req, 'authorization'));
79
- if (token === null || auth.verifyOwnerToken === undefined)
79
+ if (token === null || auth.authentication.verifyToken === undefined)
80
80
  return { allow: true };
81
- const verification = await auth.verifyOwnerToken(token, canonicalResourceUrl(req, auth));
81
+ const verification = await auth.authentication.verifyToken(token, canonicalResourceUrl(req, auth));
82
82
  return verification?.caller.identityKind === 'service'
83
83
  ? { allow: true, caller: verification.caller }
84
84
  : { allow: true };
85
85
  }
86
86
  export async function authorizeMixedMode(req, res, auth) {
87
- const token = bearerToken(header(req, 'authorization'));
88
- if (token === null)
87
+ const authorization = header(req, 'authorization');
88
+ if (authorization === undefined)
89
89
  return { allow: true };
90
+ const token = bearerToken(authorization);
90
91
  const challenge = protectedResourceMetadataUrl(req, auth);
91
- if (auth.verifyOwnerToken === undefined) {
92
+ if (token === null) {
93
+ sendUnauthorized(res, challenge, 'invalid_token');
94
+ return { allow: false, reason: 'token_rejected' };
95
+ }
96
+ if (auth.authentication.verifyToken === undefined) {
92
97
  sendUnauthorized(res, challenge);
93
98
  return { allow: false, reason: 'verifier_unavailable' };
94
99
  }
95
- const verification = await auth.verifyOwnerToken(token, canonicalResourceUrl(req, auth));
100
+ const verification = await auth.authentication.verifyToken(token, canonicalResourceUrl(req, auth));
96
101
  if (verification === null) {
97
- sendUnauthorized(res, challenge);
102
+ sendUnauthorized(res, challenge, 'invalid_token');
98
103
  return { allow: false, reason: 'token_rejected' };
99
104
  }
105
+ if (auth.authentication.kind === 'customer' &&
106
+ verification.caller.identityKind !== 'customer' &&
107
+ verification.caller.identityKind !== 'service') {
108
+ sendUnauthorized(res, challenge, 'invalid_token');
109
+ return { allow: false, reason: 'identity_not_customer' };
110
+ }
100
111
  return {
101
112
  allow: true,
102
113
  caller: verification.caller,
@@ -126,13 +137,13 @@ export function bearerToken(headerValue) {
126
137
  * `resourceMetadataUrl` is included so an MCP client can discover the authorization server (RFC 9728 /
127
138
  * the MCP authorization spec).
128
139
  */
129
- function sendUnauthorized(res, resourceMetadataUrl) {
140
+ function sendUnauthorized(res, resourceMetadataUrl, error) {
130
141
  const challenge = resourceMetadataUrl !== undefined
131
142
  ? `Bearer realm="noodle", resource_metadata="${resourceMetadataUrl}"`
132
143
  : 'Bearer realm="noodle"';
133
144
  res.writeHead(401, {
134
145
  'content-type': 'application/json; charset=utf-8',
135
- 'www-authenticate': challenge,
146
+ 'www-authenticate': error === undefined ? challenge : `${challenge}, error="${error}"`,
136
147
  });
137
148
  res.end(JSON.stringify(rpcError(JSON_RPC.INVALID_REQUEST, 'unauthorized')));
138
149
  }
@@ -8,6 +8,7 @@ export { sendJson } from './responses.js';
8
8
  export { type RunningServer, type ServeOptions, serveHttp } from './serve.js';
9
9
  export type { HostedToolAuthorizationObservation, HostedToolAuthorizationObserver, ServicePrincipalToolAuthorizationObservation, } from './service-principal-authorization.js';
10
10
  export { preflightServicePrincipalToolCall, sendServicePrincipalToolDenial, } from './service-principal-authorization.js';
11
+ export type { TargetAuthentication } from './target-authentication.js';
11
12
  export type { HostedToolDispatchContext, HostedToolDispatchHook, } from './tool-dispatch.js';
12
13
  export { toWebRequest, writeWebResponse } from './web-bridge.js';
13
14
  //# sourceMappingURL=index.d.ts.map
@@ -49,6 +49,9 @@ export async function serveRequest(req, res, target, auth, routeId, maxBody, all
49
49
  // and the SDK transport is never constructed for it. A missing accessMode is reserved for loopback/local
50
50
  // dev targets and remains open.
51
51
  let protocolContext = {
52
+ ...(auth.accessMode === 'mixed' && auth.authentication.kind === 'customer'
53
+ ? { toolAuthentication: 'mixed-customer' }
54
+ : {}),
52
55
  ...(auth.oauthClientCredentialsReady ? { oauthClientCredentialsReady: true } : {}),
53
56
  };
54
57
  let subject;
@@ -0,0 +1,15 @@
1
+ import type { AccessMode, OwnerTokenVerifier } from '@noodle-borg/module';
2
+ /** Resolved identity authority. A missing customer verifier must never select platform defaults. */
3
+ export type TargetAuthentication = {
4
+ readonly kind: 'platform';
5
+ readonly verifyToken?: OwnerTokenVerifier;
6
+ } | {
7
+ readonly kind: 'customer';
8
+ readonly verifyToken?: OwnerTokenVerifier;
9
+ readonly authorizationServers?: readonly string[];
10
+ };
11
+ export declare function resolveTargetAuthentication(target: {
12
+ readonly accessMode?: AccessMode;
13
+ readonly authentication?: TargetAuthentication;
14
+ }, platformVerifier?: OwnerTokenVerifier): TargetAuthentication;
15
+ //# sourceMappingURL=target-authentication.d.ts.map
@@ -0,0 +1,9 @@
1
+ export function resolveTargetAuthentication(target, platformVerifier) {
2
+ if (target.authentication?.kind === 'customer')
3
+ return target.authentication;
4
+ if (target.accessMode === 'customers')
5
+ return { kind: 'customer' };
6
+ const verifyToken = target.authentication?.verifyToken ?? platformVerifier;
7
+ return { kind: 'platform', ...(verifyToken === undefined ? {} : { verifyToken }) };
8
+ }
9
+ //# sourceMappingURL=target-authentication.js.map
@@ -1,5 +1,12 @@
1
1
  /** Strict service-output contracts for control-plane reads and mutations (ADR 0128). */
2
2
  import { z } from 'zod';
3
+ /** Effective authority enforcing caller authentication for one deployment. */
4
+ export declare const deploymentAuthenticationSchema: z.ZodEnum<{
5
+ customer: "customer";
6
+ none: "none";
7
+ platform: "platform";
8
+ }>;
9
+ export type DeploymentAuthentication = z.infer<typeof deploymentAuthenticationSchema>;
3
10
  /** Exact OAuth subject bound by an operator to an owner-only deployment. */
4
11
  export declare const deploymentOwnerSubjectSchema: z.ZodString;
5
12
  export declare const accessUpdateRequestSchema: z.ZodObject<{
@@ -32,6 +39,11 @@ export declare const accessUpdateResponseSchema: z.ZodObject<{
32
39
  mixed: "mixed";
33
40
  customers: "customers";
34
41
  }>;
42
+ authentication: z.ZodOptional<z.ZodEnum<{
43
+ customer: "customer";
44
+ none: "none";
45
+ platform: "platform";
46
+ }>>;
35
47
  ownerSubject: z.ZodOptional<z.ZodString>;
36
48
  }, z.core.$strict>;
37
49
  previousAccessMode: z.ZodEnum<{
@@ -45,6 +57,7 @@ export declare const accessUpdateResponseSchema: z.ZodObject<{
45
57
  previousOwnerSubject: z.ZodOptional<z.ZodString>;
46
58
  accessChanged: z.ZodBoolean;
47
59
  ownerChanged: z.ZodBoolean;
60
+ policyChanged: z.ZodOptional<z.ZodBoolean>;
48
61
  changed: z.ZodBoolean;
49
62
  }, z.core.$strict>;
50
63
  export type AccessUpdateResponse = z.output<typeof accessUpdateResponseSchema>;
@@ -71,6 +84,11 @@ export declare const accessUpdateClientResponseSchema: z.ZodPipe<z.ZodObject<{
71
84
  mixed: "mixed";
72
85
  customers: "customers";
73
86
  }>;
87
+ authentication: z.ZodOptional<z.ZodEnum<{
88
+ customer: "customer";
89
+ none: "none";
90
+ platform: "platform";
91
+ }>>;
74
92
  ownerSubject: z.ZodOptional<z.ZodString>;
75
93
  }, z.core.$strip>;
76
94
  previousAccessMode: z.ZodEnum<{
@@ -84,6 +102,7 @@ export declare const accessUpdateClientResponseSchema: z.ZodPipe<z.ZodObject<{
84
102
  previousOwnerSubject: z.ZodOptional<z.ZodString>;
85
103
  accessChanged: z.ZodOptional<z.ZodBoolean>;
86
104
  ownerChanged: z.ZodOptional<z.ZodBoolean>;
105
+ policyChanged: z.ZodOptional<z.ZodBoolean>;
87
106
  changed: z.ZodBoolean;
88
107
  }, z.core.$strip>, z.ZodTransform<{
89
108
  accessChanged: boolean;
@@ -97,12 +116,14 @@ export declare const accessUpdateClientResponseSchema: z.ZodPipe<z.ZodObject<{
97
116
  deploymentId: string;
98
117
  accessMode: "owner-only" | "org-members" | "authenticated" | "public" | "mixed" | "customers";
99
118
  serverVersion?: string | undefined;
119
+ authentication?: "customer" | "none" | "platform" | undefined;
100
120
  ownerSubject?: string | undefined;
101
121
  };
102
122
  previousAccessMode: "owner-only" | "org-members" | "authenticated" | "public" | "mixed" | "customers";
103
123
  changed: boolean;
104
124
  previousOwnerSubject?: string | undefined;
105
125
  ownerChanged?: boolean | undefined;
126
+ policyChanged?: boolean | undefined;
106
127
  }, {
107
128
  ok: true;
108
129
  target: {
@@ -114,6 +135,7 @@ export declare const accessUpdateClientResponseSchema: z.ZodPipe<z.ZodObject<{
114
135
  deploymentId: string;
115
136
  accessMode: "owner-only" | "org-members" | "authenticated" | "public" | "mixed" | "customers";
116
137
  serverVersion?: string | undefined;
138
+ authentication?: "customer" | "none" | "platform" | undefined;
117
139
  ownerSubject?: string | undefined;
118
140
  };
119
141
  previousAccessMode: "owner-only" | "org-members" | "authenticated" | "public" | "mixed" | "customers";
@@ -121,6 +143,7 @@ export declare const accessUpdateClientResponseSchema: z.ZodPipe<z.ZodObject<{
121
143
  previousOwnerSubject?: string | undefined;
122
144
  accessChanged?: boolean | undefined;
123
145
  ownerChanged?: boolean | undefined;
146
+ policyChanged?: boolean | undefined;
124
147
  }>>;
125
148
  export type AccessUpdateClientResponse = z.output<typeof accessUpdateClientResponseSchema>;
126
149
  export declare const WhoamiIdentityResponseSchema: z.ZodObject<{
@@ -1,6 +1,8 @@
1
1
  /** Strict service-output contracts for control-plane reads and mutations (ADR 0128). */
2
2
  import { z } from 'zod';
3
3
  import { accessModeSchema } from './access-mode.js';
4
+ /** Effective authority enforcing caller authentication for one deployment. */
5
+ export const deploymentAuthenticationSchema = z.enum(['none', 'platform', 'customer']);
4
6
  function containsC0OrDel(value) {
5
7
  for (let index = 0; index < value.length; index += 1) {
6
8
  const codeUnit = value.charCodeAt(index);
@@ -46,6 +48,7 @@ export const accessUpdateResponseSchema = z
46
48
  deploymentId: z.string().min(1),
47
49
  serverVersion: z.string().min(1).optional(),
48
50
  accessMode: accessModeSchema,
51
+ authentication: deploymentAuthenticationSchema.optional(),
49
52
  ownerSubject: deploymentOwnerSubjectSchema.optional(),
50
53
  })
51
54
  .strict(),
@@ -53,6 +56,7 @@ export const accessUpdateResponseSchema = z
53
56
  previousOwnerSubject: deploymentOwnerSubjectSchema.optional(),
54
57
  accessChanged: z.boolean(),
55
58
  ownerChanged: z.boolean(),
59
+ policyChanged: z.boolean().optional(),
56
60
  changed: z.boolean(),
57
61
  })
58
62
  .strict()
@@ -65,11 +69,19 @@ export const accessUpdateResponseSchema = z
65
69
  message: 'owner-only access requires a current owner subject',
66
70
  });
67
71
  }
68
- if (value.changed !== (value.accessChanged || value.ownerChanged)) {
72
+ if ((value.deployment.authentication === undefined) !== (value.policyChanged === undefined)) {
73
+ context.addIssue({
74
+ code: 'custom',
75
+ path: ['policyChanged'],
76
+ message: 'authentication and policyChanged must be present together',
77
+ });
78
+ }
79
+ if (value.changed !==
80
+ (value.accessChanged || value.ownerChanged || (value.policyChanged ?? false))) {
69
81
  context.addIssue({
70
82
  code: 'custom',
71
83
  path: ['changed'],
72
- message: 'changed must equal accessChanged || ownerChanged',
84
+ message: 'changed must equal accessChanged || ownerChanged || policyChanged',
73
85
  });
74
86
  }
75
87
  });
@@ -90,12 +102,14 @@ export const accessUpdateClientResponseSchema = z
90
102
  deploymentId: z.string().min(1),
91
103
  serverVersion: z.string().min(1).optional(),
92
104
  accessMode: accessModeSchema,
105
+ authentication: deploymentAuthenticationSchema.optional(),
93
106
  ownerSubject: deploymentOwnerSubjectSchema.optional(),
94
107
  }),
95
108
  previousAccessMode: accessModeSchema,
96
109
  previousOwnerSubject: deploymentOwnerSubjectSchema.optional(),
97
110
  accessChanged: z.boolean().optional(),
98
111
  ownerChanged: z.boolean().optional(),
112
+ policyChanged: z.boolean().optional(),
99
113
  changed: z.boolean(),
100
114
  })
101
115
  .superRefine((value, context) => {
@@ -107,6 +121,20 @@ export const accessUpdateClientResponseSchema = z
107
121
  message: 'accessChanged and ownerChanged must be present together',
108
122
  });
109
123
  }
124
+ if ((value.deployment.authentication === undefined) !== (value.policyChanged === undefined)) {
125
+ context.addIssue({
126
+ code: 'custom',
127
+ path: ['policyChanged'],
128
+ message: 'authentication and policyChanged must be present together',
129
+ });
130
+ }
131
+ if (value.policyChanged !== undefined && !modernComponents) {
132
+ context.addIssue({
133
+ code: 'custom',
134
+ path: ['policyChanged'],
135
+ message: 'policyChanged requires accessChanged and ownerChanged',
136
+ });
137
+ }
110
138
  if (modernComponents &&
111
139
  value.deployment.accessMode === 'owner-only' &&
112
140
  value.deployment.ownerSubject === undefined) {
@@ -116,11 +144,13 @@ export const accessUpdateClientResponseSchema = z
116
144
  message: 'owner-only access requires a current owner subject',
117
145
  });
118
146
  }
119
- if (modernComponents && value.changed !== (value.accessChanged || value.ownerChanged)) {
147
+ if (modernComponents &&
148
+ value.changed !==
149
+ (value.accessChanged || value.ownerChanged || (value.policyChanged ?? false))) {
120
150
  context.addIssue({
121
151
  code: 'custom',
122
152
  path: ['changed'],
123
- message: 'changed must equal accessChanged || ownerChanged',
153
+ message: 'changed must equal accessChanged || ownerChanged || policyChanged',
124
154
  });
125
155
  }
126
156
  })
@@ -124,6 +124,11 @@ export declare const deploySuccessResponseSchema: z.ZodObject<{
124
124
  mixed: "mixed";
125
125
  customers: "customers";
126
126
  }>;
127
+ authentication: z.ZodOptional<z.ZodEnum<{
128
+ customer: "customer";
129
+ none: "none";
130
+ platform: "platform";
131
+ }>>;
127
132
  ownerSubject: z.ZodOptional<z.ZodString>;
128
133
  url: z.ZodString;
129
134
  defaultUrl: z.ZodString;
@@ -385,4 +390,5 @@ export * from './application-connections.js';
385
390
  export * from './application-onboarding.js';
386
391
  export * from './billing-catalog.js';
387
392
  export * from './operation-coordination.js';
393
+ export { MIXED_CUSTOMER_AUTH_FEATURE_VERSION, type ServiceInfoClientResponse, type ServiceInfoResponse, serviceInfoClientResponseSchema, serviceInfoResponseSchema, } from './service-info.js';
388
394
  //# sourceMappingURL=index.d.ts.map
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import { z } from 'zod';
11
11
  import { accessModeSchema } from './access-mode.js';
12
- import { deploymentOwnerSubjectSchema } from './control-plane.js';
12
+ import { deploymentAuthenticationSchema, deploymentOwnerSubjectSchema } from './control-plane.js';
13
13
  export * from './access-mode.js';
14
14
  export * from './app-purge-reconciliation.js';
15
15
  export * from './application-channels.js';
@@ -92,6 +92,8 @@ export const deploySuccessResponseSchema = z.object({
92
92
  deploymentId: z.string().min(1),
93
93
  serverVersion: z.string().min(1),
94
94
  accessMode: accessModeSchema,
95
+ /** Effective authority reported by capable services; absence means unknown on older services. */
96
+ authentication: deploymentAuthenticationSchema.optional(),
95
97
  ownerSubject: deploymentOwnerSubjectSchema.optional(),
96
98
  url: z.string().min(1),
97
99
  defaultUrl: z.string().min(1),
@@ -227,4 +229,5 @@ export * from './application-connections.js';
227
229
  export * from './application-onboarding.js';
228
230
  export * from './billing-catalog.js';
229
231
  export * from './operation-coordination.js';
232
+ export { MIXED_CUSTOMER_AUTH_FEATURE_VERSION, serviceInfoClientResponseSchema, serviceInfoResponseSchema, } from './service-info.js';
230
233
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,41 @@
1
+ import { z } from 'zod';
2
+ export declare const MIXED_CUSTOMER_AUTH_FEATURE_VERSION = 1;
3
+ /** Strict producer contract for the enabled R1 service; no configured tenant data belongs here. */
4
+ export declare const serviceInfoResponseSchema: z.ZodObject<{
5
+ developerPlugin: z.ZodOptional<z.ZodObject<{
6
+ mcpCapabilityVersion: z.ZodString;
7
+ }, z.core.$strict>>;
8
+ features: z.ZodObject<{
9
+ mixedCustomerAuth: z.ZodLiteral<1>;
10
+ }, z.core.$strict>;
11
+ ok: z.ZodLiteral<true>;
12
+ status: z.ZodLiteral<"ok">;
13
+ version: z.ZodString;
14
+ gitSha: z.ZodString;
15
+ buildTime: z.ZodString;
16
+ systemRelease: z.ZodOptional<z.ZodString>;
17
+ manifestChecksum: z.ZodOptional<z.ZodString>;
18
+ packageVersions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
19
+ compatiblePackageVersions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
20
+ }, z.core.$strict>;
21
+ /** Older services may omit features; future fields are stripped at each structured metadata layer. */
22
+ export declare const serviceInfoClientResponseSchema: z.ZodObject<{
23
+ developerPlugin: z.ZodOptional<z.ZodObject<{
24
+ mcpCapabilityVersion: z.ZodString;
25
+ }, z.core.$strip>>;
26
+ features: z.ZodOptional<z.ZodObject<{
27
+ mixedCustomerAuth: z.ZodOptional<z.ZodNumber>;
28
+ }, z.core.$strip>>;
29
+ ok: z.ZodLiteral<true>;
30
+ status: z.ZodLiteral<"ok">;
31
+ version: z.ZodString;
32
+ gitSha: z.ZodString;
33
+ buildTime: z.ZodString;
34
+ systemRelease: z.ZodOptional<z.ZodString>;
35
+ manifestChecksum: z.ZodOptional<z.ZodString>;
36
+ packageVersions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
37
+ compatiblePackageVersions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
38
+ }, z.core.$strip>;
39
+ export type ServiceInfoResponse = z.infer<typeof serviceInfoResponseSchema>;
40
+ export type ServiceInfoClientResponse = z.infer<typeof serviceInfoClientResponseSchema>;
41
+ //# sourceMappingURL=service-info.d.ts.map