@manifest-network/manifest-agent-core 0.13.0 → 0.14.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 (30) hide show
  1. package/dist/deploy-app.d.ts.map +1 -1
  2. package/dist/deploy-app.js +83 -18
  3. package/dist/deploy-app.js.map +1 -1
  4. package/dist/index.d.ts +2 -2
  5. package/dist/internals/build-fred-input.d.ts +9 -1
  6. package/dist/internals/build-fred-input.d.ts.map +1 -1
  7. package/dist/internals/build-fred-input.js +16 -3
  8. package/dist/internals/build-fred-input.js.map +1 -1
  9. package/dist/internals/evaluate-readiness-from-fred.d.ts.map +1 -1
  10. package/dist/internals/evaluate-readiness-from-fred.js +12 -3
  11. package/dist/internals/evaluate-readiness-from-fred.js.map +1 -1
  12. package/dist/internals/evaluate-readiness.d.ts +14 -0
  13. package/dist/internals/evaluate-readiness.d.ts.map +1 -1
  14. package/dist/internals/evaluate-readiness.js +21 -8
  15. package/dist/internals/evaluate-readiness.js.map +1 -1
  16. package/dist/internals/render-deployment-plan.d.ts +6 -0
  17. package/dist/internals/render-deployment-plan.d.ts.map +1 -1
  18. package/dist/internals/render-deployment-plan.js +8 -5
  19. package/dist/internals/render-deployment-plan.js.map +1 -1
  20. package/dist/internals/spec-normalize.d.ts +4 -3
  21. package/dist/internals/spec-normalize.d.ts.map +1 -1
  22. package/dist/internals/spec-normalize.js +4 -3
  23. package/dist/internals/spec-normalize.js.map +1 -1
  24. package/dist/types.d.ts +43 -2
  25. package/dist/types.d.ts.map +1 -1
  26. package/package.json +3 -3
  27. package/dist/internals/find-sku-uuid.d.ts +0 -40
  28. package/dist/internals/find-sku-uuid.d.ts.map +0 -1
  29. package/dist/internals/find-sku-uuid.js +0 -20
  30. package/dist/internals/find-sku-uuid.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"spec-normalize.js","names":[],"sources":["../../src/internals/spec-normalize.ts"],"sourcesContent":["import type {\n DeploySpec,\n ServiceDef,\n SingleServiceSpec,\n SpecSummary,\n StackSpec,\n} from '../types.js';\n\n/**\n * Spec normalization + summarization helpers. Exports `isStack`,\n * `firstImage`, `normalizeServices`, `summarizeSpec`, and `validateSpec`\n * (the latter surfaces pre-broadcast shape violations).\n *\n * Two spec shapes are supported (frozen in ENG-128's `types.ts`):\n * - **services-map (StackSpec)** — `{ services: { <name>: ServiceDef }, customDomain?, serviceName? }`\n * - **legacy single-service (SingleServiceSpec)** — `{ image, port?, env?, customDomain? }`\n *\n * `normalizeServices` collapses the two shapes into a single iterable form\n * so callers (Plan summary, manifest builder, etc.) walk one structure\n * regardless of which form the user passed.\n *\n * Validation: `validateSpec` throws a plain `TypeError` on shape violations\n * — agent-core has no workspace dep on `@manifest-network/manifest-mcp-core`\n * in PR 1/2 (per parent's REV 1), so `ManifestMCPError` isn't available\n * here. PR 3's high-level `deployApp` re-wraps `TypeError` into\n * `ManifestMCPError(INVALID_CONFIG)` at the public-API boundary.\n */\n\n/**\n * True when `spec` uses the services-map shape (StackSpec). Mirrors\n * `_spec.cjs#isStack`: `services` is a non-null, non-array object.\n */\nexport function isStackSpec(\n spec: DeploySpec | null | undefined,\n): spec is StackSpec {\n if (spec === null || spec === undefined || typeof spec !== 'object')\n return false;\n const services = (spec as { services?: unknown }).services;\n return (\n services !== null &&\n typeof services === 'object' &&\n !Array.isArray(services)\n );\n}\n\n/**\n * Return the canonical first image string for a spec. For legacy single-\n * service: `spec.image`. For stack: the first non-empty `image` in\n * `Object.values(spec.services)`. Returns `null` when neither shape\n * carries an image (or `spec` is malformed).\n */\nexport function firstImage(spec: DeploySpec | null | undefined): string | null {\n if (spec === null || spec === undefined || typeof spec !== 'object')\n return null;\n const single = spec as Partial<SingleServiceSpec>;\n if (typeof single.image === 'string' && single.image.length > 0) {\n return single.image;\n }\n if (isStackSpec(spec)) {\n for (const svc of Object.values(spec.services)) {\n if (svc !== null && typeof svc === 'object') {\n const image = (svc as Partial<ServiceDef>).image;\n if (typeof image === 'string' && image.length > 0) return image;\n }\n }\n }\n return null;\n}\n\n/**\n * Walk a spec as `[{name, raw}]` where:\n * - `name === null` for legacy single-service (only one entry, raw is the spec itself).\n * - `name === <key>` for each services-map entry; `raw` is the per-service ServiceDef.\n *\n * Stable iteration order matches `Object.entries` (insertion order in v8/modern engines).\n */\nexport interface NormalizedService {\n /** `null` for legacy single-service; the services-map key for stack leases. */\n name: string | null;\n /** The per-service object exactly as the spec stores it. No field projection. */\n raw: ServiceDef | SingleServiceSpec;\n}\n\nexport function normalizeServices(\n spec: DeploySpec | null | undefined,\n): NormalizedService[] {\n if (isStackSpec(spec)) {\n return Object.entries(spec.services).map(([name, raw]) => ({\n name,\n raw: (raw ?? {}) as ServiceDef,\n }));\n }\n return [\n {\n name: null,\n raw: (spec ?? {}) as SingleServiceSpec,\n },\n ];\n}\n\n/**\n * Produce the frozen `SpecSummary` shape for inclusion in the `Plan`\n * (camelCase fields: `serviceCount`, etc.).\n *\n * Port count rules:\n * - SingleServiceSpec `port: number` → +1 port.\n * - SingleServiceSpec `port: number[]` → +length ports.\n * - ServiceDef `ports: number[]` (per type) → +length ports.\n * - ServiceDef `ports` shaped as a Record (older codepath) → +key count.\n *\n * Env key uniqueness is computed across services (one `env_keys` set\n * spans the whole spec); `envCount` is the size of that set; `envKeys`\n * is sorted ascending.\n */\nexport function summarizeSpec(spec: DeploySpec): SpecSummary {\n const format: 'single' | 'stack' = isStackSpec(spec) ? 'stack' : 'single';\n const services = normalizeServices(spec);\n\n let portCount = 0;\n const envKeys = new Set<string>();\n const images: string[] = [];\n\n for (const { raw: svc } of services) {\n if (svc !== null && typeof svc === 'object') {\n const svcRecord = svc as unknown as Record<string, unknown>;\n const image = svcRecord.image;\n if (typeof image === 'string' && image.length > 0) images.push(image);\n\n const port = svcRecord.port;\n if (typeof port === 'number') portCount += 1;\n else if (Array.isArray(port)) portCount += port.length;\n\n const ports = svcRecord.ports;\n if (Array.isArray(ports)) {\n portCount += ports.length;\n } else if (ports !== null && typeof ports === 'object') {\n portCount += Object.keys(ports).length;\n }\n\n const env = svcRecord.env;\n if (env !== null && typeof env === 'object' && !Array.isArray(env)) {\n for (const k of Object.keys(env)) envKeys.add(k);\n }\n }\n }\n\n return {\n format,\n serviceCount: services.length,\n portCount,\n envCount: envKeys.size,\n envKeys: Array.from(envKeys).sort(),\n images,\n };\n}\n\n/**\n * Validate a `DeploySpec` shape pre-broadcast. Throws `TypeError` on the\n * first violation. The frozen type union (`SingleServiceSpec | StackSpec`)\n * already enforces most structural rules at compile time; this runtime\n * check defends against `unknown`-cast callers and `JSON.parse`-decoded\n * inputs.\n *\n * Rules (mirror fred's `deployApp.ts` input validation):\n * - `spec` must be a non-null object.\n * - Stack: `services` must have ≥1 entry; each entry's `image` must be a\n * non-empty string.\n * - Single: `image` must be a non-empty string.\n * - Mutually exclusive `image` AND `services` not allowed.\n *\n * The high-level `deployApp` in PR 3 layers domain checks on top\n * (`customDomain` shape, `serviceName` membership, etc.).\n */\nexport function validateSpec(spec: DeploySpec | null | undefined): void {\n if (spec === null || spec === undefined || typeof spec !== 'object') {\n throw new TypeError('validateSpec: spec must be a non-null object');\n }\n const record = spec as unknown as Record<string, unknown>;\n\n // Mutual-exclusion gate uses KEY presence (not value validity). This\n // closes the bypass where a caller supplies a malformed `image` value\n // (empty string, number, null) alongside a valid `services` map: the\n // value-based check would silently treat `image` as \"absent\" and accept\n // the spec, but the caller's intent was ambiguous (which shape did they\n // mean?). Rejecting on key-presence forces the caller to delete one key\n // before submission and removes the ambiguity.\n const hasImageKey = 'image' in record;\n const hasServicesKey = 'services' in record;\n if (hasImageKey && hasServicesKey) {\n throw new TypeError(\n 'validateSpec: spec has both `image` and `services` keys; these are mutually exclusive (regardless of value validity)',\n );\n }\n\n // Downstream value-validity check (after the mutual-exclusion gate has\n // ruled out the ambiguous case). An `image` key with a non-string or\n // empty-string value still fails here when `services` is absent.\n const hasImage = typeof record.image === 'string' && record.image.length > 0;\n const hasServices = isStackSpec(spec);\n if (!hasImage && !hasServices) {\n throw new TypeError(\n 'validateSpec: spec must declare either `image` (SingleServiceSpec) or `services` (StackSpec)',\n );\n }\n\n // Copilot review fix (PR #58 r3266786899): `customDomain` shape at\n // the boundary. The orchestrator's `buildFredDeployInput`\n // (`deploy-app.ts:701`) uses a `if (customDomain)` truthiness check,\n // which silently drops `''`, `null`, `false`, `0`, `NaN` from the\n // emitted `fredInput`. A user spec like `{ ..., customDomain: '' }`\n // passes validation today, fred receives `fredInput` WITHOUT the\n // domain, deploy proceeds — the user's requested domain silently\n // not claimed, no error signal.\n //\n // Boundary check: when `customDomain` is present, it must be a\n // non-empty string. `undefined` (key absent) is fine; that's the\n // \"no domain requested\" case. Fires before the stack-customDomain\n // serviceName check (r3249684707) so the user gets a clear\n // customDomain-shape error rather than a misleading\n // requires-serviceName one.\n // Copilot review fix (PR #58 r3267373001): reject whitespace-only\n // strings AND strings with surrounding whitespace (option (i) from\n // the team-lead's brief — strict; let the caller send a clean,\n // already-trimmed value rather than silently trim for them). The\n // prior `cd.length === 0` predicate accepted `' '`, `'\\t\\n'`,\n // and `' app.example.com '`; fred would either accept the\n // surrounding whitespace as part of the domain (correctness bug)\n // or trim-and-reject (worse UX than agent-core's clear error).\n if ('customDomain' in record) {\n const cd = record.customDomain;\n if (cd !== undefined) {\n const isCleanNonEmptyString =\n typeof cd === 'string' && cd.length > 0 && cd.trim() === cd;\n if (!isCleanNonEmptyString) {\n const got =\n typeof cd === 'string'\n ? cd.trim().length === 0\n ? `\"${cd}\"`\n : `\"${cd}\" (has surrounding whitespace)`\n : cd === null\n ? 'null'\n : typeof cd;\n throw new TypeError(\n `validateSpec: \\`customDomain\\` must be a non-empty trimmed string or absent (got ${got}).`,\n );\n }\n }\n }\n\n if (hasServices) {\n const entries = Object.entries(spec.services);\n if (entries.length === 0) {\n throw new TypeError(\n 'validateSpec: stack spec `services` must have at least one entry',\n );\n }\n for (const [name, svc] of entries) {\n if (svc === null || typeof svc !== 'object') {\n throw new TypeError(\n `validateSpec: stack service \"${name}\" must be a non-null object`,\n );\n }\n const image = (svc as Partial<ServiceDef>).image;\n if (typeof image !== 'string' || image.length === 0) {\n throw new TypeError(\n `validateSpec: stack service \"${name}\" must declare a non-empty \\`image\\` string`,\n );\n }\n }\n\n // Copilot review fix (PR #58 r3249684707): a stack spec with a\n // `customDomain` MUST declare which service receives the domain\n // via `serviceName`, and that value must be a key in `services`.\n // Without this guard, `customDomainServiceOf` in `deploy-app.ts`\n // returns `undefined`, planning proceeds with no target, renderers\n // misrepresent the claim, and fred rejects the set-domain tx\n // ONLY after `create-lease` commits — leaving the user with an\n // orphan lease + a failed domain claim. Catching this at\n // validate-time is fail-fast at the boundary.\n //\n // Single-service specs are unaffected: their `customDomain` is\n // claimed against the implicit single lease item — no\n // serviceName disambiguation needed.\n const stackDomain = (spec as Partial<StackSpec>).customDomain;\n if (typeof stackDomain === 'string' && stackDomain.length > 0) {\n const stackServiceName = (spec as Partial<StackSpec>).serviceName;\n if (\n typeof stackServiceName !== 'string' ||\n stackServiceName.length === 0\n ) {\n throw new TypeError(\n 'validateSpec: stack spec with `customDomain` requires `serviceName` identifying which service receives the domain.',\n );\n }\n // Copilot review fix (PR #58 r3250331968): use an own-key check.\n // The `in` operator walks the prototype chain, so `serviceName:\n // 'constructor'` (or `'toString'`, `'hasOwnProperty'`, etc.)\n // would falsely pass against a `services` map that doesn't\n // declare those names. Mirrors fred's own choice at\n // `packages/fred/src/tools/deployApp.ts:254` for cross-package\n // symmetry. `Object.keys().includes()` (not `Object.hasOwn`,\n // which is ES2022 and our `tsdown.config.ts` targets ES2020).\n if (!Object.keys(spec.services).includes(stackServiceName)) {\n throw new TypeError(\n `validateSpec: stack spec \\`serviceName\\` \"${stackServiceName}\" must be a key in \\`services\\` (got services: [${Object.keys(spec.services).join(', ')}]).`,\n );\n }\n }\n } else {\n // Single-service spec port requirement.\n //\n // Copilot review fix (PR #58 r3249097051): fred's image-mode rejects\n // portless inputs with `port is required when using image`\n // (`packages/fred/src/tools/deployApp.ts:202` +\n // `packages/fred/src/tools/buildManifestPreview.ts:181`). Without\n // an agent-core boundary check the orchestrator silently passed\n // `port: undefined` through `buildManifestPreviewInput` /\n // `buildFredDeployInput`, surfacing fred's error mid-orchestration\n // (after readiness check + plan render). Failing fast at validate\n // time produces a clearer message and avoids partial work.\n //\n // The escape hatch for genuinely internal-only services is the\n // stack spec — service-level `ports` is optional, so a stack with\n // `{ services: { mysvc: { image, env } } }` deploys without ports.\n //\n // Copilot review fix (PR #58 r3249294877): tighten the predicate to\n // a finite positive integer in the TCP port range. The prior\n // `typeof p === 'number'` check accepted `0`, `NaN`, `Infinity`,\n // negative numbers, non-integers, and out-of-range ports —\n // partially defeating the fail-fast intent. Fred catches `port: 0`\n // via `!input.port`, but the other shapes either flow through to a\n // less helpful error or get coerced silently. The shared predicate\n // `isValidPortNumber` (below) is the single source of truth.\n const port = (spec as Partial<SingleServiceSpec>).port;\n const hasValidPort =\n isValidPortNumber(port) ||\n (Array.isArray(port) && port.length > 0 && port.every(isValidPortNumber));\n if (!hasValidPort) {\n throw new TypeError(\n 'validateSpec: single-service specs require at least one port (port must be a finite positive integer in the TCP range (1-65535), or a non-empty array of such); got ' +\n `port=${JSON.stringify(port)}. For internal-only services, use a stack spec instead.`,\n );\n }\n }\n}\n\n/**\n * Predicate: `p` is a finite positive integer in the TCP port range\n * (1-65535). Used by `validateSpec` to gate single-service `port`\n * shapes against the broad `typeof === 'number'` bypass.\n *\n * Co-located in this module because it's exclusive to the port-\n * validation boundary; if a future caller needs the same check,\n * promote it to a shared utility then.\n */\nfunction isValidPortNumber(p: unknown): p is number {\n return typeof p === 'number' && Number.isInteger(p) && p > 0 && p <= 65535;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"spec-normalize.js","names":[],"sources":["../../src/internals/spec-normalize.ts"],"sourcesContent":["import type {\n DeploySpec,\n ServiceDef,\n SingleServiceSpec,\n SpecSummary,\n StackSpec,\n} from '../types.js';\n\n/**\n * Spec normalization + summarization helpers. Exports `isStack`,\n * `firstImage`, `normalizeServices`, `summarizeSpec`, and `validateSpec`\n * (the latter surfaces pre-broadcast shape violations).\n *\n * Two spec shapes are supported (defined in `types.ts`; `size?` added in\n * ENG-275):\n * - **services-map (StackSpec)** — `{ services: { <name>: ServiceDef }, customDomain?, serviceName?, size? }`\n * - **legacy single-service (SingleServiceSpec)** — `{ image, port?, env?, customDomain?, size? }`\n *\n * `normalizeServices` collapses the two shapes into a single iterable form\n * so callers (Plan summary, manifest builder, etc.) walk one structure\n * regardless of which form the user passed.\n *\n * Validation: `validateSpec` throws a plain `TypeError` on shape violations\n * — agent-core has no workspace dep on `@manifest-network/manifest-mcp-core`\n * in PR 1/2 (per parent's REV 1), so `ManifestMCPError` isn't available\n * here. PR 3's high-level `deployApp` re-wraps `TypeError` into\n * `ManifestMCPError(INVALID_CONFIG)` at the public-API boundary.\n */\n\n/**\n * True when `spec` uses the services-map shape (StackSpec). Mirrors\n * `_spec.cjs#isStack`: `services` is a non-null, non-array object.\n */\nexport function isStackSpec(\n spec: DeploySpec | null | undefined,\n): spec is StackSpec {\n if (spec === null || spec === undefined || typeof spec !== 'object')\n return false;\n const services = (spec as { services?: unknown }).services;\n return (\n services !== null &&\n typeof services === 'object' &&\n !Array.isArray(services)\n );\n}\n\n/**\n * Return the canonical first image string for a spec. For legacy single-\n * service: `spec.image`. For stack: the first non-empty `image` in\n * `Object.values(spec.services)`. Returns `null` when neither shape\n * carries an image (or `spec` is malformed).\n */\nexport function firstImage(spec: DeploySpec | null | undefined): string | null {\n if (spec === null || spec === undefined || typeof spec !== 'object')\n return null;\n const single = spec as Partial<SingleServiceSpec>;\n if (typeof single.image === 'string' && single.image.length > 0) {\n return single.image;\n }\n if (isStackSpec(spec)) {\n for (const svc of Object.values(spec.services)) {\n if (svc !== null && typeof svc === 'object') {\n const image = (svc as Partial<ServiceDef>).image;\n if (typeof image === 'string' && image.length > 0) return image;\n }\n }\n }\n return null;\n}\n\n/**\n * Walk a spec as `[{name, raw}]` where:\n * - `name === null` for legacy single-service (only one entry, raw is the spec itself).\n * - `name === <key>` for each services-map entry; `raw` is the per-service ServiceDef.\n *\n * Stable iteration order matches `Object.entries` (insertion order in v8/modern engines).\n */\nexport interface NormalizedService {\n /** `null` for legacy single-service; the services-map key for stack leases. */\n name: string | null;\n /** The per-service object exactly as the spec stores it. No field projection. */\n raw: ServiceDef | SingleServiceSpec;\n}\n\nexport function normalizeServices(\n spec: DeploySpec | null | undefined,\n): NormalizedService[] {\n if (isStackSpec(spec)) {\n return Object.entries(spec.services).map(([name, raw]) => ({\n name,\n raw: (raw ?? {}) as ServiceDef,\n }));\n }\n return [\n {\n name: null,\n raw: (spec ?? {}) as SingleServiceSpec,\n },\n ];\n}\n\n/**\n * Produce the frozen `SpecSummary` shape for inclusion in the `Plan`\n * (camelCase fields: `serviceCount`, etc.).\n *\n * Port count rules:\n * - SingleServiceSpec `port: number` → +1 port.\n * - SingleServiceSpec `port: number[]` → +length ports.\n * - ServiceDef `ports: number[]` (per type) → +length ports.\n * - ServiceDef `ports` shaped as a Record (older codepath) → +key count.\n *\n * Env key uniqueness is computed across services (one `env_keys` set\n * spans the whole spec); `envCount` is the size of that set; `envKeys`\n * is sorted ascending.\n */\nexport function summarizeSpec(spec: DeploySpec): SpecSummary {\n const format: 'single' | 'stack' = isStackSpec(spec) ? 'stack' : 'single';\n const services = normalizeServices(spec);\n\n let portCount = 0;\n const envKeys = new Set<string>();\n const images: string[] = [];\n\n for (const { raw: svc } of services) {\n if (svc !== null && typeof svc === 'object') {\n const svcRecord = svc as unknown as Record<string, unknown>;\n const image = svcRecord.image;\n if (typeof image === 'string' && image.length > 0) images.push(image);\n\n const port = svcRecord.port;\n if (typeof port === 'number') portCount += 1;\n else if (Array.isArray(port)) portCount += port.length;\n\n const ports = svcRecord.ports;\n if (Array.isArray(ports)) {\n portCount += ports.length;\n } else if (ports !== null && typeof ports === 'object') {\n portCount += Object.keys(ports).length;\n }\n\n const env = svcRecord.env;\n if (env !== null && typeof env === 'object' && !Array.isArray(env)) {\n for (const k of Object.keys(env)) envKeys.add(k);\n }\n }\n }\n\n return {\n format,\n serviceCount: services.length,\n portCount,\n envCount: envKeys.size,\n envKeys: Array.from(envKeys).sort(),\n images,\n };\n}\n\n/**\n * Validate a `DeploySpec` shape pre-broadcast. Throws `TypeError` on the\n * first violation. The frozen type union (`SingleServiceSpec | StackSpec`)\n * already enforces most structural rules at compile time; this runtime\n * check defends against `unknown`-cast callers and `JSON.parse`-decoded\n * inputs.\n *\n * Rules (mirror fred's `deployApp.ts` input validation):\n * - `spec` must be a non-null object.\n * - Stack: `services` must have ≥1 entry; each entry's `image` must be a\n * non-empty string.\n * - Single: `image` must be a non-empty string.\n * - Mutually exclusive `image` AND `services` not allowed.\n *\n * The high-level `deployApp` in PR 3 layers domain checks on top\n * (`customDomain` shape, `serviceName` membership, etc.).\n */\nexport function validateSpec(spec: DeploySpec | null | undefined): void {\n if (spec === null || spec === undefined || typeof spec !== 'object') {\n throw new TypeError('validateSpec: spec must be a non-null object');\n }\n const record = spec as unknown as Record<string, unknown>;\n\n // Mutual-exclusion gate uses KEY presence (not value validity). This\n // closes the bypass where a caller supplies a malformed `image` value\n // (empty string, number, null) alongside a valid `services` map: the\n // value-based check would silently treat `image` as \"absent\" and accept\n // the spec, but the caller's intent was ambiguous (which shape did they\n // mean?). Rejecting on key-presence forces the caller to delete one key\n // before submission and removes the ambiguity.\n const hasImageKey = 'image' in record;\n const hasServicesKey = 'services' in record;\n if (hasImageKey && hasServicesKey) {\n throw new TypeError(\n 'validateSpec: spec has both `image` and `services` keys; these are mutually exclusive (regardless of value validity)',\n );\n }\n\n // Downstream value-validity check (after the mutual-exclusion gate has\n // ruled out the ambiguous case). An `image` key with a non-string or\n // empty-string value still fails here when `services` is absent.\n const hasImage = typeof record.image === 'string' && record.image.length > 0;\n const hasServices = isStackSpec(spec);\n if (!hasImage && !hasServices) {\n throw new TypeError(\n 'validateSpec: spec must declare either `image` (SingleServiceSpec) or `services` (StackSpec)',\n );\n }\n\n // Copilot review fix (PR #58 r3266786899): `customDomain` shape at\n // the boundary. The orchestrator's `buildFredDeployInput`\n // (`deploy-app.ts:701`) uses a `if (customDomain)` truthiness check,\n // which silently drops `''`, `null`, `false`, `0`, `NaN` from the\n // emitted `fredInput`. A user spec like `{ ..., customDomain: '' }`\n // passes validation today, fred receives `fredInput` WITHOUT the\n // domain, deploy proceeds — the user's requested domain silently\n // not claimed, no error signal.\n //\n // Boundary check: when `customDomain` is present, it must be a\n // non-empty string. `undefined` (key absent) is fine; that's the\n // \"no domain requested\" case. Fires before the stack-customDomain\n // serviceName check (r3249684707) so the user gets a clear\n // customDomain-shape error rather than a misleading\n // requires-serviceName one.\n // Copilot review fix (PR #58 r3267373001): reject whitespace-only\n // strings AND strings with surrounding whitespace (option (i) from\n // the team-lead's brief — strict; let the caller send a clean,\n // already-trimmed value rather than silently trim for them). The\n // prior `cd.length === 0` predicate accepted `' '`, `'\\t\\n'`,\n // and `' app.example.com '`; fred would either accept the\n // surrounding whitespace as part of the domain (correctness bug)\n // or trim-and-reject (worse UX than agent-core's clear error).\n if ('customDomain' in record) {\n const cd = record.customDomain;\n if (cd !== undefined) {\n const isCleanNonEmptyString =\n typeof cd === 'string' && cd.length > 0 && cd.trim() === cd;\n if (!isCleanNonEmptyString) {\n const got =\n typeof cd === 'string'\n ? cd.trim().length === 0\n ? `\"${cd}\"`\n : `\"${cd}\" (has surrounding whitespace)`\n : cd === null\n ? 'null'\n : typeof cd;\n throw new TypeError(\n `validateSpec: \\`customDomain\\` must be a non-empty trimmed string or absent (got ${got}).`,\n );\n }\n }\n }\n\n if (hasServices) {\n const entries = Object.entries(spec.services);\n if (entries.length === 0) {\n throw new TypeError(\n 'validateSpec: stack spec `services` must have at least one entry',\n );\n }\n for (const [name, svc] of entries) {\n if (svc === null || typeof svc !== 'object') {\n throw new TypeError(\n `validateSpec: stack service \"${name}\" must be a non-null object`,\n );\n }\n const image = (svc as Partial<ServiceDef>).image;\n if (typeof image !== 'string' || image.length === 0) {\n throw new TypeError(\n `validateSpec: stack service \"${name}\" must declare a non-empty \\`image\\` string`,\n );\n }\n }\n\n // Copilot review fix (PR #58 r3249684707): a stack spec with a\n // `customDomain` MUST declare which service receives the domain\n // via `serviceName`, and that value must be a key in `services`.\n // Without this guard, `customDomainServiceOf` in `deploy-app.ts`\n // returns `undefined`, planning proceeds with no target, renderers\n // misrepresent the claim, and fred rejects the set-domain tx\n // ONLY after `create-lease` commits — leaving the user with an\n // orphan lease + a failed domain claim. Catching this at\n // validate-time is fail-fast at the boundary.\n //\n // Single-service specs are unaffected: their `customDomain` is\n // claimed against the implicit single lease item — no\n // serviceName disambiguation needed.\n const stackDomain = (spec as Partial<StackSpec>).customDomain;\n if (typeof stackDomain === 'string' && stackDomain.length > 0) {\n const stackServiceName = (spec as Partial<StackSpec>).serviceName;\n if (\n typeof stackServiceName !== 'string' ||\n stackServiceName.length === 0\n ) {\n throw new TypeError(\n 'validateSpec: stack spec with `customDomain` requires `serviceName` identifying which service receives the domain.',\n );\n }\n // Copilot review fix (PR #58 r3250331968): use an own-key check.\n // The `in` operator walks the prototype chain, so `serviceName:\n // 'constructor'` (or `'toString'`, `'hasOwnProperty'`, etc.)\n // would falsely pass against a `services` map that doesn't\n // declare those names. Mirrors fred's own choice at\n // `packages/fred/src/tools/deployApp.ts:254` for cross-package\n // symmetry. `Object.keys().includes()` (not `Object.hasOwn`,\n // which is ES2022 and our `tsdown.config.ts` targets ES2020).\n if (!Object.keys(spec.services).includes(stackServiceName)) {\n throw new TypeError(\n `validateSpec: stack spec \\`serviceName\\` \"${stackServiceName}\" must be a key in \\`services\\` (got services: [${Object.keys(spec.services).join(', ')}]).`,\n );\n }\n }\n } else {\n // Single-service spec port requirement.\n //\n // Copilot review fix (PR #58 r3249097051): fred's image-mode rejects\n // portless inputs with `port is required when using image`\n // (`packages/fred/src/tools/deployApp.ts:202` +\n // `packages/fred/src/tools/buildManifestPreview.ts:181`). Without\n // an agent-core boundary check the orchestrator silently passed\n // `port: undefined` through `buildManifestPreviewInput` /\n // `buildFredDeployInput`, surfacing fred's error mid-orchestration\n // (after readiness check + plan render). Failing fast at validate\n // time produces a clearer message and avoids partial work.\n //\n // The escape hatch for genuinely internal-only services is the\n // stack spec — service-level `ports` is optional, so a stack with\n // `{ services: { mysvc: { image, env } } }` deploys without ports.\n //\n // Copilot review fix (PR #58 r3249294877): tighten the predicate to\n // a finite positive integer in the TCP port range. The prior\n // `typeof p === 'number'` check accepted `0`, `NaN`, `Infinity`,\n // negative numbers, non-integers, and out-of-range ports —\n // partially defeating the fail-fast intent. Fred catches `port: 0`\n // via `!input.port`, but the other shapes either flow through to a\n // less helpful error or get coerced silently. The shared predicate\n // `isValidPortNumber` (below) is the single source of truth.\n const port = (spec as Partial<SingleServiceSpec>).port;\n const hasValidPort =\n isValidPortNumber(port) ||\n (Array.isArray(port) && port.length > 0 && port.every(isValidPortNumber));\n if (!hasValidPort) {\n throw new TypeError(\n 'validateSpec: single-service specs require at least one port (port must be a finite positive integer in the TCP range (1-65535), or a non-empty array of such); got ' +\n `port=${JSON.stringify(port)}. For internal-only services, use a stack spec instead.`,\n );\n }\n }\n}\n\n/**\n * Predicate: `p` is a finite positive integer in the TCP port range\n * (1-65535). Used by `validateSpec` to gate single-service `port`\n * shapes against the broad `typeof === 'number'` bypass.\n *\n * Co-located in this module because it's exclusive to the port-\n * validation boundary; if a future caller needs the same check,\n * promote it to a shared utility then.\n */\nfunction isValidPortNumber(p: unknown): p is number {\n return typeof p === 'number' && Number.isInteger(p) && p > 0 && p <= 65535;\n}\n"],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { CosmosClientManager, WalletProvider } from "@manifest-network/manifest-mcp-core";
1
+ import { CosmosClientManager, SkuCandidate, WalletProvider } from "@manifest-network/manifest-mcp-core";
2
2
 
3
3
  //#region src/types.d.ts
4
4
  interface Coin {
@@ -120,11 +120,40 @@ interface SingleServiceSpec {
120
120
  port?: number | number[];
121
121
  env?: Record<string, string>;
122
122
  customDomain?: string;
123
+ /**
124
+ * Compute-tier / SKU name selecting the lease item's resources (e.g.
125
+ * `'small'`, `'medium'`). Resolved to an on-chain SKU UUID by
126
+ * `findSkuUuid`, then threaded into SKU selection, fee estimation,
127
+ * readiness, fred's deploy input, and the persisted manifest. Optional;
128
+ * `requestedSize` (`deploy-app.ts`) falls back to `'small'` when absent
129
+ * or empty. ENG-275 promoted this from an undocumented cast-read
130
+ * escape hatch to a first-class typed field so contract-following
131
+ * callers can select a non-default SKU.
132
+ */
133
+ size?: string;
134
+ /**
135
+ * Optional SKU disambiguator: `provider_uuid` narrows a duplicate
136
+ * `size` name to one provider; `sku_uuid` pins a specific SKU by uuid
137
+ * (bypasses the name). Threaded into `resolveSku`. ENG-296.
138
+ */
139
+ providerUuid?: string;
140
+ /** See {@link SingleServiceSpec.providerUuid}. Pins a specific SKU by uuid (bypasses the name). ENG-296. */
141
+ skuUuid?: string;
123
142
  }
124
143
  interface StackSpec {
125
144
  services: Record<string, ServiceDef>;
126
145
  customDomain?: string;
127
146
  serviceName?: string;
147
+ /** SKU tier for the lease items; see {@link SingleServiceSpec.size}. */
148
+ size?: string;
149
+ /**
150
+ * Optional SKU disambiguator: `provider_uuid` narrows a duplicate
151
+ * `size` name to one provider; `sku_uuid` pins a specific SKU by uuid
152
+ * (bypasses the name). Threaded into `resolveSku`. ENG-296.
153
+ */
154
+ providerUuid?: string;
155
+ /** See {@link StackSpec.providerUuid}. Pins a specific SKU by uuid (bypasses the name). ENG-296. */
156
+ skuUuid?: string;
128
157
  }
129
158
  type DeploySpec = SingleServiceSpec | StackSpec;
130
159
  interface SpecSummary {
@@ -215,6 +244,9 @@ type ProgressEvent = {
215
244
  kind: 'partial_success_prompt_rendered';
216
245
  prompt: string;
217
246
  leaseUuid: string;
247
+ } | {
248
+ kind: 'sku_ambiguous';
249
+ candidates: SkuCandidate[];
218
250
  };
219
251
  type FailureEnvelope = {
220
252
  outcome: 'partially_succeeded';
@@ -240,6 +272,15 @@ interface DeployAppCallbacks {
240
272
  onProgress?: (event: ProgressEvent) => void;
241
273
  onComplete?: (result: DeployResult) => void;
242
274
  onFailure?: (failure: FailureEnvelope, options: RecoveryOption[]) => Promise<RecoveryChoice>;
275
+ /**
276
+ * Resolve an ambiguous SKU name. Invoked when a requested `size` matches
277
+ * more than one active SKU and no provider_uuid/sku_uuid was supplied.
278
+ * Returns the user's pick; when absent, agent-core re-throws SKU_AMBIGUOUS.
279
+ */
280
+ onResolveSku?: (candidates: SkuCandidate[]) => Promise<{
281
+ skuUuid: string;
282
+ providerUuid: string;
283
+ }>;
243
284
  }
244
285
  type ManageDomainArgs = {
245
286
  action: 'set';
@@ -309,5 +350,5 @@ interface CloseLeaseCallbacks {
309
350
  }) => Promise<void>;
310
351
  }
311
352
  //#endregion
312
- export { AgentCoreRuntime, CloseLeaseArgs, CloseLeaseCallbacks, CloseLeaseOptions, CloseLeaseResult, Coin, type CosmosClientManager, DenomLookup, DenomMap, DeployAppCallbacks, DeployAppOptions, DeployResult, DeploySpec, DeploymentPlanBlock, FailureEnvelope, FeeEstimate, LeaseStateName, ManageDomainArgs, ManageDomainCallbacks, ManageDomainOptions, ManageDomainResult, Plan, PlanEdit, PlanFees, ProgressEvent, Readiness, ReadinessAction, RecoveryChoice, RecoveryOption, RecoveryOptionId, ServiceDef, SingleServiceSpec, SpecSummary, StackSpec, TroubleshootArgs, TroubleshootCallbacks, TroubleshootOptions, TroubleshootReport, type WalletProvider };
353
+ export { AgentCoreRuntime, CloseLeaseArgs, CloseLeaseCallbacks, CloseLeaseOptions, CloseLeaseResult, Coin, type CosmosClientManager, DenomLookup, DenomMap, DeployAppCallbacks, DeployAppOptions, DeployResult, DeploySpec, DeploymentPlanBlock, FailureEnvelope, FeeEstimate, LeaseStateName, ManageDomainArgs, ManageDomainCallbacks, ManageDomainOptions, ManageDomainResult, Plan, PlanEdit, PlanFees, ProgressEvent, Readiness, ReadinessAction, RecoveryChoice, RecoveryOption, RecoveryOptionId, ServiceDef, SingleServiceSpec, type SkuCandidate, SpecSummary, StackSpec, TroubleshootArgs, TroubleshootCallbacks, TroubleshootOptions, TroubleshootReport, type WalletProvider };
313
354
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@manifest-network/manifest-agent-core",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "TypeScript orchestration surface for Manifest agent flows (deploy / manage-domain / troubleshoot / close-lease). Type contract only — see ENG-127.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -43,8 +43,8 @@
43
43
  "dist"
44
44
  ],
45
45
  "dependencies": {
46
- "@manifest-network/manifest-mcp-core": "^0.13.0",
47
- "@manifest-network/manifest-mcp-fred": "^0.13.0"
46
+ "@manifest-network/manifest-mcp-core": "^0.14.0",
47
+ "@manifest-network/manifest-mcp-fred": "^0.14.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/node": "22.19.19",
@@ -1,40 +0,0 @@
1
- import { CosmosClientManager } from "@manifest-network/manifest-mcp-core";
2
-
3
- //#region src/internals/find-sku-uuid.d.ts
4
- /**
5
- * Resolve a SKU-tier name (e.g. `'docker-micro'`, `'small'`) to its on-chain
6
- * UUID + the UUID of its publishing provider. Mirrors fred's internal
7
- * `findSkuUuid` helper at `packages/fred/src/tools/deployApp.ts:L62`.
8
- *
9
- * Used by `deploy-app.ts`'s pre-broadcast fee-estimation step (the
10
- * `cosmosEstimateFee('billing', 'create-lease', ...)` call needs the SKU
11
- * UUID as part of the item-arg string format `sku-uuid:quantity[:service-name]`).
12
- *
13
- * **Why duplicated (not imported from fred):** fred's `findSkuUuid` is an
14
- * internal helper, not re-exported from `packages/fred/src/index.ts`.
15
- * Per team-lead-2's Path 2 verdict (vs Path 1 export-from-fred), the
16
- * helper is duplicated here because:
17
- *
18
- * 1. Stateless query helper — zero cross-instance drift risk
19
- * (unlike `AuthTimestampTracker` which has cross-call state).
20
- * 2. fred's barrel deliberately keeps this helper internal; making it
21
- * public would be an architectural decision, not an oversight fix.
22
- * 3. The duplicated logic uses only already-exported core symbols
23
- * (`createPagination`, `MAX_PAGE_LIMIT`, `ManifestMCPError`).
24
- * 4. Drift bounded by the shared `@manifest-network/manifestjs@2.4.1`
25
- * proto pin — both fred and agent-core import the same SKU types.
26
- *
27
- * Throws `ManifestMCPError(QUERY_FAILED)` when no active SKU matches
28
- * the requested `size`. Error message includes the available SKU names
29
- * for caller-side debugging.
30
- */
31
- interface SkuResolution {
32
- /** On-chain SKU UUID. Used in `create-lease` item-arg construction. */
33
- readonly skuUuid: string;
34
- /** Publishing provider's UUID. Required by some downstream chain queries. */
35
- readonly providerUuid: string;
36
- }
37
- declare function findSkuUuid(clientManager: CosmosClientManager, size: string): Promise<SkuResolution>;
38
- //#endregion
39
- export { SkuResolution, findSkuUuid };
40
- //# sourceMappingURL=find-sku-uuid.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"find-sku-uuid.d.ts","names":[],"sources":["../../src/internals/find-sku-uuid.ts"],"mappings":";;;;;AAmCA;;;;AAIuB;AAGvB;;;;;;;;;;;;;;AAGwB;;;;;;UAVP,aAAA;;WAEN,OAAA;;WAEA,YAAY;AAAA;AAAA,iBAGD,WAAA,CACpB,aAAA,EAAe,mBAAA,EACf,IAAA,WACC,OAAA,CAAQ,aAAA"}
@@ -1,20 +0,0 @@
1
- import { MAX_PAGE_LIMIT, ManifestMCPError, ManifestMCPErrorCode, createPagination } from "@manifest-network/manifest-mcp-core";
2
- //#region src/internals/find-sku-uuid.ts
3
- async function findSkuUuid(clientManager, size) {
4
- const queryClient = await clientManager.getQueryClient();
5
- const pagination = createPagination(MAX_PAGE_LIMIT);
6
- const result = await queryClient.liftedinit.sku.v1.sKUs({
7
- activeOnly: true,
8
- pagination
9
- });
10
- for (const sku of result.skus) if (sku.name === size) return {
11
- skuUuid: sku.uuid,
12
- providerUuid: sku.providerUuid
13
- };
14
- const available = result.skus.map((s) => s.name);
15
- throw new ManifestMCPError(ManifestMCPErrorCode.QUERY_FAILED, `SKU tier "${size}" not found. Available: ${available.join(", ")}`);
16
- }
17
- //#endregion
18
- export { findSkuUuid };
19
-
20
- //# sourceMappingURL=find-sku-uuid.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"find-sku-uuid.js","names":[],"sources":["../../src/internals/find-sku-uuid.ts"],"sourcesContent":["import {\n type CosmosClientManager,\n createPagination,\n MAX_PAGE_LIMIT,\n ManifestMCPError,\n ManifestMCPErrorCode,\n} from '@manifest-network/manifest-mcp-core';\n\n/**\n * Resolve a SKU-tier name (e.g. `'docker-micro'`, `'small'`) to its on-chain\n * UUID + the UUID of its publishing provider. Mirrors fred's internal\n * `findSkuUuid` helper at `packages/fred/src/tools/deployApp.ts:L62`.\n *\n * Used by `deploy-app.ts`'s pre-broadcast fee-estimation step (the\n * `cosmosEstimateFee('billing', 'create-lease', ...)` call needs the SKU\n * UUID as part of the item-arg string format `sku-uuid:quantity[:service-name]`).\n *\n * **Why duplicated (not imported from fred):** fred's `findSkuUuid` is an\n * internal helper, not re-exported from `packages/fred/src/index.ts`.\n * Per team-lead-2's Path 2 verdict (vs Path 1 export-from-fred), the\n * helper is duplicated here because:\n *\n * 1. Stateless query helper — zero cross-instance drift risk\n * (unlike `AuthTimestampTracker` which has cross-call state).\n * 2. fred's barrel deliberately keeps this helper internal; making it\n * public would be an architectural decision, not an oversight fix.\n * 3. The duplicated logic uses only already-exported core symbols\n * (`createPagination`, `MAX_PAGE_LIMIT`, `ManifestMCPError`).\n * 4. Drift bounded by the shared `@manifest-network/manifestjs@2.4.1`\n * proto pin — both fred and agent-core import the same SKU types.\n *\n * Throws `ManifestMCPError(QUERY_FAILED)` when no active SKU matches\n * the requested `size`. Error message includes the available SKU names\n * for caller-side debugging.\n */\nexport interface SkuResolution {\n /** On-chain SKU UUID. Used in `create-lease` item-arg construction. */\n readonly skuUuid: string;\n /** Publishing provider's UUID. Required by some downstream chain queries. */\n readonly providerUuid: string;\n}\n\nexport async function findSkuUuid(\n clientManager: CosmosClientManager,\n size: string,\n): Promise<SkuResolution> {\n const queryClient = await clientManager.getQueryClient();\n const pagination = createPagination(MAX_PAGE_LIMIT);\n const result = await queryClient.liftedinit.sku.v1.sKUs({\n activeOnly: true,\n pagination,\n });\n\n for (const sku of result.skus) {\n if (sku.name === size) {\n return { skuUuid: sku.uuid, providerUuid: sku.providerUuid };\n }\n }\n\n const available = result.skus.map((s) => s.name);\n throw new ManifestMCPError(\n ManifestMCPErrorCode.QUERY_FAILED,\n `SKU tier \"${size}\" not found. Available: ${available.join(', ')}`,\n );\n}\n"],"mappings":";;AA0CA,eAAsB,YACpB,eACA,MACwB;CACxB,MAAM,cAAc,MAAM,cAAc,eAAe;CACvD,MAAM,aAAa,iBAAiB,cAAc;CAClD,MAAM,SAAS,MAAM,YAAY,WAAW,IAAI,GAAG,KAAK;EACtD,YAAY;EACZ;CACF,CAAC;CAED,KAAK,MAAM,OAAO,OAAO,MACvB,IAAI,IAAI,SAAS,MACf,OAAO;EAAE,SAAS,IAAI;EAAM,cAAc,IAAI;CAAa;CAI/D,MAAM,YAAY,OAAO,KAAK,KAAK,MAAM,EAAE,IAAI;CAC/C,MAAM,IAAI,iBACR,qBAAqB,cACrB,aAAa,KAAK,0BAA0B,UAAU,KAAK,IAAI,GACjE;AACF"}