@manifest-network/manifest-agent-core 0.14.0 → 0.15.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 (62) hide show
  1. package/dist/close-lease.d.ts.map +1 -1
  2. package/dist/close-lease.js +15 -3
  3. package/dist/close-lease.js.map +1 -1
  4. package/dist/deploy-app.d.ts +2 -2
  5. package/dist/deploy-app.d.ts.map +1 -1
  6. package/dist/deploy-app.js +82 -35
  7. package/dist/deploy-app.js.map +1 -1
  8. package/dist/deploy-app.test-d.d.ts +1 -0
  9. package/dist/deploy-app.test-d.js +11 -0
  10. package/dist/deploy-app.test-d.js.map +1 -0
  11. package/dist/guarded-fetch.d.ts +2 -0
  12. package/dist/guarded-fetch.js +2 -0
  13. package/dist/index.d.ts +2 -3
  14. package/dist/index.js +1 -2
  15. package/dist/internals/cancellation.d.ts +57 -0
  16. package/dist/internals/cancellation.d.ts.map +1 -0
  17. package/dist/internals/cancellation.js +79 -0
  18. package/dist/internals/cancellation.js.map +1 -0
  19. package/dist/internals/inspect-image.js +1 -1
  20. package/dist/internals/inspect-image.js.map +1 -1
  21. package/dist/internals/render-intent-recap.d.ts +13 -11
  22. package/dist/internals/render-intent-recap.d.ts.map +1 -1
  23. package/dist/internals/render-intent-recap.js +5 -4
  24. package/dist/internals/render-intent-recap.js.map +1 -1
  25. package/dist/internals/spec-normalize.d.ts +34 -28
  26. package/dist/internals/spec-normalize.d.ts.map +1 -1
  27. package/dist/internals/spec-normalize.js +28 -22
  28. package/dist/internals/spec-normalize.js.map +1 -1
  29. package/dist/manage-domain.d.ts.map +1 -1
  30. package/dist/manage-domain.js +34 -8
  31. package/dist/manage-domain.js.map +1 -1
  32. package/dist/node_modules/@vitest/pretty-format/dist/index.js +888 -0
  33. package/dist/node_modules/@vitest/pretty-format/dist/index.js.map +1 -0
  34. package/dist/node_modules/@vitest/runner/dist/chunk-artifact.js +1500 -0
  35. package/dist/node_modules/@vitest/runner/dist/chunk-artifact.js.map +1 -0
  36. package/dist/node_modules/@vitest/runner/dist/index.js +1 -0
  37. package/dist/node_modules/@vitest/runner/dist/utils.js +1 -0
  38. package/dist/node_modules/@vitest/utils/dist/chunk-pathe.M-eThtNZ.js +82 -0
  39. package/dist/node_modules/@vitest/utils/dist/chunk-pathe.M-eThtNZ.js.map +1 -0
  40. package/dist/node_modules/@vitest/utils/dist/display.js +558 -0
  41. package/dist/node_modules/@vitest/utils/dist/display.js.map +1 -0
  42. package/dist/node_modules/@vitest/utils/dist/helpers.js +68 -0
  43. package/dist/node_modules/@vitest/utils/dist/helpers.js.map +1 -0
  44. package/dist/node_modules/@vitest/utils/dist/source-map.js +95 -0
  45. package/dist/node_modules/@vitest/utils/dist/source-map.js.map +1 -0
  46. package/dist/node_modules/@vitest/utils/dist/timers.js +20 -0
  47. package/dist/node_modules/@vitest/utils/dist/timers.js.map +1 -0
  48. package/dist/node_modules/tinyrainbow/dist/index.js +86 -0
  49. package/dist/node_modules/tinyrainbow/dist/index.js.map +1 -0
  50. package/dist/node_modules/vite/dist/node/module-runner.js +22 -0
  51. package/dist/node_modules/vite/dist/node/module-runner.js.map +1 -0
  52. package/dist/node_modules/vitest/dist/index.js +6 -0
  53. package/dist/troubleshoot.d.ts.map +1 -1
  54. package/dist/troubleshoot.js +11 -1
  55. package/dist/troubleshoot.js.map +1 -1
  56. package/dist/types.d.ts +30 -51
  57. package/dist/types.d.ts.map +1 -1
  58. package/package.json +12 -6
  59. package/dist/internals/build-fred-input.d.ts +0 -46
  60. package/dist/internals/build-fred-input.d.ts.map +0 -1
  61. package/dist/internals/build-fred-input.js +0 -160
  62. package/dist/internals/build-fred-input.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"close-lease.d.ts","names":[],"sources":["../src/close-lease.ts"],"mappings":";;;;AAoF2B;;;;;;;;;;;;;;;;;;;;;;;;;;iBAJL,UAAA,CACpB,IAAA,EAAM,cAAA,EACN,SAAA,EAAW,mBAAA,EACX,IAAA,EAAM,iBAAA,GACL,OAAA,CAAQ,gBAAA"}
1
+ {"version":3,"file":"close-lease.d.ts","names":[],"sources":["../src/close-lease.ts"],"mappings":";;;;AAuF2B;;;;;;;;;;;;;;;;;;;;;;;;;;iBAJL,UAAA,CACpB,IAAA,EAAM,cAAA,EACN,SAAA,EAAW,mBAAA,EACX,IAAA,EAAM,iBAAA,GACL,OAAA,CAAQ,gBAAA"}
@@ -1,6 +1,7 @@
1
+ import { makeCancellationScope } from "./internals/cancellation.js";
1
2
  import { decode, isTerminal } from "./internals/lease-state.js";
2
3
  import { verifyAndRecover } from "./internals/verify-recover.js";
3
- import { ManifestMCPError, ManifestMCPErrorCode, stopApp } from "@manifest-network/manifest-mcp-core";
4
+ import { ManifestMCPError, ManifestMCPErrorCode, noopLogger, parseLeaseUuid, stopApp } from "@manifest-network/manifest-mcp-core";
4
5
  //#region src/close-lease.ts
5
6
  /**
6
7
  * Public entry point: orchestrate closing an existing lease via the
@@ -52,12 +53,23 @@ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
52
53
  */
53
54
  async function closeLease(args, callbacks, opts) {
54
55
  validateArgs(args);
56
+ const cx = makeCancellationScope({
57
+ opts,
58
+ onProgress: callbacks.onProgress,
59
+ opLabel: "Lease close",
60
+ broadcasts: true
61
+ });
62
+ cx.throwIfCancelled();
55
63
  const block = renderConfirmationBlock(args);
56
64
  if (callbacks.onConfirm) {
57
- if (await callbacks.onConfirm(block) !== "yes") throw new ManifestMCPError(ManifestMCPErrorCode.OPERATION_CANCELLED, "User declined to proceed with close-lease.");
65
+ if (await cx.race(callbacks.onConfirm(block)) !== "yes") throw new ManifestMCPError(ManifestMCPErrorCode.OPERATION_CANCELLED, "User declined to proceed with close-lease.");
58
66
  }
59
67
  callbacks.onProgress?.({ kind: "user_confirmed" });
60
- await stopApp(opts.clientManager, args.leaseUuid);
68
+ cx.throwIfCancelled();
69
+ await stopApp({
70
+ chain: opts.clientManager,
71
+ logger: noopLogger
72
+ }, { leaseUuid: parseLeaseUuid(args.leaseUuid) });
61
73
  const verifyResult = await verifyAndRecover({
62
74
  verifier: async () => {
63
75
  let result;
@@ -1 +1 @@
1
- {"version":3,"file":"close-lease.js","names":["decodeLeaseState"],"sources":["../src/close-lease.ts"],"sourcesContent":["/**\n * Public entry point: orchestrate closing an existing lease via the\n * `close-lease` billing tx.\n *\n * Composition (mirrors `deploy-app.ts` / `manage-domain.ts`):\n *\n * 1. Validate args.\n * 2. Render a confirmation block + optionally consult `onConfirm`.\n * 3. Broadcast `stopApp` (which submits `MsgCloseLease`).\n * 4. Verify the post-broadcast on-chain state via `verifyAndRecover`\n * driving a direct `billing.v1.lease({ leaseUuid })` query +\n * `lease-state.decode` + `isTerminal`. Terminal states (CLOSED /\n * REJECTED / EXPIRED / INSUFFICIENT_FUNDS) count as success;\n * PENDING / ACTIVE map to the `pending_drift` branch; a chain\n * response with no lease (`{ lease: null }`) maps to the catch-all\n * `unclassified` branch.\n * 5. On verify-failure, invoke the simple-form `onFailure({ reason })`\n * then throw `ManifestMCPError(TX_FAILED)`. On success, emit\n * `onComplete` with the typed `CloseLeaseResult`.\n */\n\nimport {\n ManifestMCPError,\n ManifestMCPErrorCode,\n stopApp,\n} from '@manifest-network/manifest-mcp-core';\nimport {\n decode as decodeLeaseState,\n isTerminal,\n} from './internals/lease-state.js';\nimport {\n type VerificationSpec,\n verifyAndRecover,\n} from './internals/verify-recover.js';\nimport type {\n CloseLeaseArgs,\n CloseLeaseCallbacks,\n CloseLeaseOptions,\n CloseLeaseResult,\n DeploymentPlanBlock,\n LeaseStateName,\n} from './types.js';\n\nconst UUID_RE =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\ntype CloseOutcome = 'terminal' | 'pending' | 'not_found';\n\ninterface CloseDiag {\n stateName?: LeaseStateName;\n reason?: string;\n}\n\n/**\n * Close a lease and verify it reached a terminal on-chain state.\n *\n * @throws `ManifestMCPError(INVALID_CONFIG)` for args validation.\n * @throws `ManifestMCPError(OPERATION_CANCELLED)` when `onConfirm` returns\n * `'no'` (deliberate user cancellation — ENG-272).\n * @throws `ManifestMCPError` (typically `TX_FAILED`) propagated as-is\n * from the `stopApp()` broadcast step. Broadcast errors do NOT invoke\n * `onFailure` — that callback is reserved for post-broadcast\n * verification failures. `stopApp` already raises a structured\n * `ManifestMCPError` from the core package; wrapping it again at this\n * layer would be redundant. Callers wanting to react to broadcast\n * errors should catch them at the call site.\n * @throws `ManifestMCPError(TX_FAILED)` when post-broadcast verification\n * reaches one of two failure modes (both with `onFailure({ reason })`\n * invoked first):\n * - the lease is still non-terminal (`pending_drift` branch — state\n * decoded as PENDING / ACTIVE / similar non-terminal); or\n * - the chain returns `{ lease: null }` post-close, so the lease is\n * not visible on-chain (`unclassified` branch).\n * @throws `ManifestMCPError(QUERY_FAILED)` when the post-broadcast verify\n * chain query (`billing.v1.lease`) raises a non-NotFound error\n * (RPC / transport / decoding failure). Wrapped inside the verifier\n * closure so the failure flows through `onFailure({ reason })` before\n * the throw. Structured `ManifestMCPError`s raised by the chain client\n * are re-thrown as-is (with `onFailure` invoked first).\n */\nexport async function closeLease(\n args: CloseLeaseArgs,\n callbacks: CloseLeaseCallbacks,\n opts: CloseLeaseOptions,\n): Promise<CloseLeaseResult> {\n validateArgs(args);\n\n const block = renderConfirmationBlock(args);\n if (callbacks.onConfirm) {\n const yesNo = await callbacks.onConfirm(block);\n if (yesNo !== 'yes') {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.OPERATION_CANCELLED,\n 'User declined to proceed with close-lease.',\n );\n }\n }\n callbacks.onProgress?.({ kind: 'user_confirmed' });\n\n await stopApp(opts.clientManager, args.leaseUuid);\n\n // Direct single-lease query (Copilot review PR #60, comment 3275999624):\n // the previous `leasesByTenant` + page-1-only pagination would\n // false-`not_found` for tenants with >100 leases. `billing.v1.lease`\n // is the same query shape `troubleshoot.ts` already uses; it's\n // tenant-agnostic and bounded to a single lease.\n const spec: VerificationSpec<unknown, CloseOutcome, CloseDiag> = {\n verifier: async () => {\n // Wrap the chain call in try/catch (Copilot review PR #60,\n // comment 3276419264): if `billing.v1.lease` rejects (RPC down,\n // transport, structured `ManifestMCPError`), the error would\n // otherwise propagate OUT of `verifyAndRecover` and bypass the\n // post-verify `onFailure({ reason })` callback below. Mirror\n // the disambiguation pattern from `lookupDomain` (commit aaa5cc5)\n // and `troubleshootDeployment` (commit f1a4737): invoke\n // `onFailure` first, then re-throw `ManifestMCPError` as-is or\n // wrap plain errors as `QUERY_FAILED`.\n let result: unknown;\n try {\n const queryClient = await opts.clientManager.getQueryClient();\n result = await queryClient.liftedinit.billing.v1.lease({\n leaseUuid: args.leaseUuid,\n });\n } catch (err) {\n const reason = `Failed to query lease ${args.leaseUuid} during close-verify: ${\n err instanceof Error ? err.message : String(err)\n }`;\n if (callbacks.onFailure) {\n await callbacks.onFailure({ reason });\n }\n if (err instanceof ManifestMCPError) {\n throw err;\n }\n throw new ManifestMCPError(ManifestMCPErrorCode.QUERY_FAILED, reason);\n }\n const lease = (result as { lease?: unknown })?.lease;\n if (lease === null || lease === undefined) {\n return {\n outcome: 'not_found' as const,\n diagnostic: {\n reason: `lease ${args.leaseUuid} not visible on chain after close`,\n },\n };\n }\n const rawState = (lease as { state?: unknown }).state;\n const stateName = decodeLeaseState(\n typeof rawState === 'number' || typeof rawState === 'string'\n ? rawState\n : undefined,\n );\n if (stateName === undefined) {\n return {\n outcome: 'pending' as const,\n diagnostic: {\n reason: `lease ${args.leaseUuid} state could not be decoded (raw=${String(rawState)})`,\n },\n };\n }\n return {\n outcome: (isTerminal(stateName) ? 'terminal' : 'pending') as\n | 'terminal'\n | 'pending',\n diagnostic: { stateName },\n };\n },\n successValues: ['terminal'],\n branches: {\n pending: {\n branchId: 'pending_drift',\n journalActionTags: ['close-lease-verify-pending'],\n buildFailureEnvelope: (d) => ({\n outcome: 'failed',\n reason:\n d.reason ??\n `close_lease tx accepted but state is still ${d.stateName ?? 'unknown'}.`,\n }),\n buildRecoveryOptions: () => [],\n },\n not_found: {\n branchId: 'unclassified',\n journalActionTags: ['close-lease-verify-not-found'],\n buildFailureEnvelope: (d) => ({\n outcome: 'failed',\n reason:\n d.reason ??\n `Lease ${args.leaseUuid} not visible on chain after close.`,\n }),\n buildRecoveryOptions: () => [],\n },\n },\n };\n\n const verifyResult = await verifyAndRecover(spec, undefined);\n\n if (verifyResult.result !== 'success') {\n const reason =\n verifyResult.failure?.reason ?? 'close-lease verification failed.';\n if (callbacks.onFailure) {\n await callbacks.onFailure({ reason });\n }\n throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, reason);\n }\n\n // Invariant: when `verifyAndRecover` returns success, the matched\n // outcome was `'terminal'`, and the verifier's `terminal` branch\n // ALWAYS sets `diagnostic.stateName` (see the spec above). A missing\n // `stateName` on the success path means the verifier invariant is\n // broken — likely a future refactor regression. The previous\n // implementation fell back to `'LEASE_STATE_CLOSED'` silently, which\n // would lie to the caller (Copilot review PR #60, comment 3276719603).\n // Fail loudly with a typed error instead. `TX_FAILED` is the closest\n // available code in `ManifestMCPErrorCode` (no `INTERNAL_ERROR`\n // variant); the message names the invariant explicitly.\n if (!verifyResult.diagnostic.stateName) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.TX_FAILED,\n `close-lease verifier invariant violated: success outcome reached without diagnostic.stateName for lease ${args.leaseUuid}`,\n );\n }\n const finalState: LeaseStateName = verifyResult.diagnostic.stateName;\n const result: CloseLeaseResult = {\n leaseUuid: args.leaseUuid,\n finalState,\n };\n callbacks.onComplete?.(result);\n return result;\n}\n\n// --- Helpers --------------------------------------------------------\n\nfunction validateArgs(args: CloseLeaseArgs): void {\n if (typeof args.leaseUuid !== 'string' || !args.leaseUuid.match(UUID_RE)) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n `closeLease: leaseUuid must be a UUID; got \"${args.leaseUuid}\".`,\n );\n }\n}\n\nfunction renderConfirmationBlock(args: CloseLeaseArgs): DeploymentPlanBlock {\n // Image is not tracked in `CloseLeaseArgs` and `stopApp` doesn't return it;\n // surface the gap explicitly so reviewers/users see the missing context\n // rather than silently omitting an image field they'd expect.\n const text = [\n `Close lease ${args.leaseUuid}.`,\n ' Image: (image not recorded)',\n ' This is permanent — the lease cannot be reopened.',\n '',\n 'Proceed?',\n ].join('\\n');\n return { text };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA2CA,MAAM,UACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCF,eAAsB,WACpB,MACA,WACA,MAC2B;CAC3B,aAAa,IAAI;CAEjB,MAAM,QAAQ,wBAAwB,IAAI;CAC1C,IAAI,UAAU;MAER,MADgB,UAAU,UAAU,KAAK,MAC/B,OACZ,MAAM,IAAI,iBACR,qBAAqB,qBACrB,4CACF;CAAA;CAGJ,UAAU,aAAa,EAAE,MAAM,iBAAiB,CAAC;CAEjD,MAAM,QAAQ,KAAK,eAAe,KAAK,SAAS;CA6FhD,MAAM,eAAe,MAAM,iBAAiB;EArF1C,UAAU,YAAY;GAUpB,IAAI;GACJ,IAAI;IAEF,SAAS,OAAM,MADW,KAAK,cAAc,eAAe,GACjC,WAAW,QAAQ,GAAG,MAAM,EACrD,WAAW,KAAK,UAClB,CAAC;GACH,SAAS,KAAK;IACZ,MAAM,SAAS,yBAAyB,KAAK,UAAU,wBACrD,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;IAEjD,IAAI,UAAU,WACZ,MAAM,UAAU,UAAU,EAAE,OAAO,CAAC;IAEtC,IAAI,eAAe,kBACjB,MAAM;IAER,MAAM,IAAI,iBAAiB,qBAAqB,cAAc,MAAM;GACtE;GACA,MAAM,QAAS,QAAgC;GAC/C,IAAI,UAAU,QAAQ,UAAU,KAAA,GAC9B,OAAO;IACL,SAAS;IACT,YAAY,EACV,QAAQ,SAAS,KAAK,UAAU,mCAClC;GACF;GAEF,MAAM,WAAY,MAA8B;GAChD,MAAM,YAAYA,OAChB,OAAO,aAAa,YAAY,OAAO,aAAa,WAChD,WACA,KAAA,CACN;GACA,IAAI,cAAc,KAAA,GAChB,OAAO;IACL,SAAS;IACT,YAAY,EACV,QAAQ,SAAS,KAAK,UAAU,mCAAmC,OAAO,QAAQ,EAAE,GACtF;GACF;GAEF,OAAO;IACL,SAAU,WAAW,SAAS,IAAI,aAAa;IAG/C,YAAY,EAAE,UAAU;GAC1B;EACF;EACA,eAAe,CAAC,UAAU;EAC1B,UAAU;GACR,SAAS;IACP,UAAU;IACV,mBAAmB,CAAC,4BAA4B;IAChD,uBAAuB,OAAO;KAC5B,SAAS;KACT,QACE,EAAE,UACF,8CAA8C,EAAE,aAAa,UAAU;IAC3E;IACA,4BAA4B,CAAC;GAC/B;GACA,WAAW;IACT,UAAU;IACV,mBAAmB,CAAC,8BAA8B;IAClD,uBAAuB,OAAO;KAC5B,SAAS;KACT,QACE,EAAE,UACF,SAAS,KAAK,UAAU;IAC5B;IACA,4BAA4B,CAAC;GAC/B;EACF;CAG6C,GAAG,KAAA,CAAS;CAE3D,IAAI,aAAa,WAAW,WAAW;EACrC,MAAM,SACJ,aAAa,SAAS,UAAU;EAClC,IAAI,UAAU,WACZ,MAAM,UAAU,UAAU,EAAE,OAAO,CAAC;EAEtC,MAAM,IAAI,iBAAiB,qBAAqB,WAAW,MAAM;CACnE;CAYA,IAAI,CAAC,aAAa,WAAW,WAC3B,MAAM,IAAI,iBACR,qBAAqB,WACrB,2GAA2G,KAAK,WAClH;CAEF,MAAM,aAA6B,aAAa,WAAW;CAC3D,MAAM,SAA2B;EAC/B,WAAW,KAAK;EAChB;CACF;CACA,UAAU,aAAa,MAAM;CAC7B,OAAO;AACT;AAIA,SAAS,aAAa,MAA4B;CAChD,IAAI,OAAO,KAAK,cAAc,YAAY,CAAC,KAAK,UAAU,MAAM,OAAO,GACrE,MAAM,IAAI,iBACR,qBAAqB,gBACrB,8CAA8C,KAAK,UAAU,GAC/D;AAEJ;AAEA,SAAS,wBAAwB,MAA2C;CAW1E,OAAO,EAAE,MAPI;EACX,eAAe,KAAK,UAAU;EAC9B;EACA;EACA;EACA;CACF,EAAE,KAAK,IACK,EAAE;AAChB"}
1
+ {"version":3,"file":"close-lease.js","names":["decodeLeaseState"],"sources":["../src/close-lease.ts"],"sourcesContent":["/**\n * Public entry point: orchestrate closing an existing lease via the\n * `close-lease` billing tx.\n *\n * Composition (mirrors `deploy-app.ts` / `manage-domain.ts`):\n *\n * 1. Validate args.\n * 2. Render a confirmation block + optionally consult `onConfirm`.\n * 3. Broadcast `stopApp` (which submits `MsgCloseLease`).\n * 4. Verify the post-broadcast on-chain state via `verifyAndRecover`\n * driving a direct `billing.v1.lease({ leaseUuid })` query +\n * `lease-state.decode` + `isTerminal`. Terminal states (CLOSED /\n * REJECTED / EXPIRED / INSUFFICIENT_FUNDS) count as success;\n * PENDING / ACTIVE map to the `pending_drift` branch; a chain\n * response with no lease (`{ lease: null }`) maps to the catch-all\n * `unclassified` branch.\n * 5. On verify-failure, invoke the simple-form `onFailure({ reason })`\n * then throw `ManifestMCPError(TX_FAILED)`. On success, emit\n * `onComplete` with the typed `CloseLeaseResult`.\n */\n\nimport {\n ManifestMCPError,\n ManifestMCPErrorCode,\n noopLogger,\n parseLeaseUuid,\n stopApp,\n} from '@manifest-network/manifest-mcp-core';\nimport { makeCancellationScope } from './internals/cancellation.js';\nimport {\n decode as decodeLeaseState,\n isTerminal,\n} from './internals/lease-state.js';\nimport {\n type VerificationSpec,\n verifyAndRecover,\n} from './internals/verify-recover.js';\nimport type {\n CloseLeaseArgs,\n CloseLeaseCallbacks,\n CloseLeaseOptions,\n CloseLeaseResult,\n DeploymentPlanBlock,\n LeaseStateName,\n} from './types.js';\n\nconst UUID_RE =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\ntype CloseOutcome = 'terminal' | 'pending' | 'not_found';\n\ninterface CloseDiag {\n stateName?: LeaseStateName;\n reason?: string;\n}\n\n/**\n * Close a lease and verify it reached a terminal on-chain state.\n *\n * @throws `ManifestMCPError(INVALID_CONFIG)` for args validation.\n * @throws `ManifestMCPError(OPERATION_CANCELLED)` when `onConfirm` returns\n * `'no'` (deliberate user cancellation — ENG-272).\n * @throws `ManifestMCPError` (typically `TX_FAILED`) propagated as-is\n * from the `stopApp()` broadcast step. Broadcast errors do NOT invoke\n * `onFailure` — that callback is reserved for post-broadcast\n * verification failures. `stopApp` already raises a structured\n * `ManifestMCPError` from the core package; wrapping it again at this\n * layer would be redundant. Callers wanting to react to broadcast\n * errors should catch them at the call site.\n * @throws `ManifestMCPError(TX_FAILED)` when post-broadcast verification\n * reaches one of two failure modes (both with `onFailure({ reason })`\n * invoked first):\n * - the lease is still non-terminal (`pending_drift` branch — state\n * decoded as PENDING / ACTIVE / similar non-terminal); or\n * - the chain returns `{ lease: null }` post-close, so the lease is\n * not visible on-chain (`unclassified` branch).\n * @throws `ManifestMCPError(QUERY_FAILED)` when the post-broadcast verify\n * chain query (`billing.v1.lease`) raises a non-NotFound error\n * (RPC / transport / decoding failure). Wrapped inside the verifier\n * closure so the failure flows through `onFailure({ reason })` before\n * the throw. Structured `ManifestMCPError`s raised by the chain client\n * are re-thrown as-is (with `onFailure` invoked first).\n */\nexport async function closeLease(\n args: CloseLeaseArgs,\n callbacks: CloseLeaseCallbacks,\n opts: CloseLeaseOptions,\n): Promise<CloseLeaseResult> {\n validateArgs(args);\n\n const cx = makeCancellationScope({\n opts,\n onProgress: callbacks.onProgress,\n opLabel: 'Lease close',\n broadcasts: true,\n });\n cx.throwIfCancelled();\n\n const block = renderConfirmationBlock(args);\n if (callbacks.onConfirm) {\n const yesNo = await cx.race(callbacks.onConfirm(block));\n if (yesNo !== 'yes') {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.OPERATION_CANCELLED,\n 'User declined to proceed with close-lease.',\n );\n }\n }\n callbacks.onProgress?.({ kind: 'user_confirmed' });\n\n cx.throwIfCancelled();\n\n // txCtx has no signer (ManageDomain/CloseLease flows carry no walletProvider);\n // the sender resolves from ctx.chain (the CosmosClientManager wallet). See OI-SENDER.\n await stopApp(\n { chain: opts.clientManager, logger: noopLogger },\n { leaseUuid: parseLeaseUuid(args.leaseUuid) },\n );\n\n // Direct single-lease query (Copilot review PR #60, comment 3275999624):\n // the previous `leasesByTenant` + page-1-only pagination would\n // false-`not_found` for tenants with >100 leases. `billing.v1.lease`\n // is the same query shape `troubleshoot.ts` already uses; it's\n // tenant-agnostic and bounded to a single lease.\n const spec: VerificationSpec<unknown, CloseOutcome, CloseDiag> = {\n verifier: async () => {\n // Wrap the chain call in try/catch (Copilot review PR #60,\n // comment 3276419264): if `billing.v1.lease` rejects (RPC down,\n // transport, structured `ManifestMCPError`), the error would\n // otherwise propagate OUT of `verifyAndRecover` and bypass the\n // post-verify `onFailure({ reason })` callback below. Mirror\n // the disambiguation pattern from `lookupDomain` (commit aaa5cc5)\n // and `troubleshootDeployment` (commit f1a4737): invoke\n // `onFailure` first, then re-throw `ManifestMCPError` as-is or\n // wrap plain errors as `QUERY_FAILED`.\n let result: unknown;\n try {\n const queryClient = await opts.clientManager.getQueryClient();\n result = await queryClient.liftedinit.billing.v1.lease({\n leaseUuid: args.leaseUuid,\n });\n } catch (err) {\n const reason = `Failed to query lease ${args.leaseUuid} during close-verify: ${\n err instanceof Error ? err.message : String(err)\n }`;\n if (callbacks.onFailure) {\n await callbacks.onFailure({ reason });\n }\n if (err instanceof ManifestMCPError) {\n throw err;\n }\n throw new ManifestMCPError(ManifestMCPErrorCode.QUERY_FAILED, reason);\n }\n const lease = (result as { lease?: unknown })?.lease;\n if (lease === null || lease === undefined) {\n return {\n outcome: 'not_found' as const,\n diagnostic: {\n reason: `lease ${args.leaseUuid} not visible on chain after close`,\n },\n };\n }\n const rawState = (lease as { state?: unknown }).state;\n const stateName = decodeLeaseState(\n typeof rawState === 'number' || typeof rawState === 'string'\n ? rawState\n : undefined,\n );\n if (stateName === undefined) {\n return {\n outcome: 'pending' as const,\n diagnostic: {\n reason: `lease ${args.leaseUuid} state could not be decoded (raw=${String(rawState)})`,\n },\n };\n }\n return {\n outcome: (isTerminal(stateName) ? 'terminal' : 'pending') as\n | 'terminal'\n | 'pending',\n diagnostic: { stateName },\n };\n },\n successValues: ['terminal'],\n branches: {\n pending: {\n branchId: 'pending_drift',\n journalActionTags: ['close-lease-verify-pending'],\n buildFailureEnvelope: (d) => ({\n outcome: 'failed',\n reason:\n d.reason ??\n `close_lease tx accepted but state is still ${d.stateName ?? 'unknown'}.`,\n }),\n buildRecoveryOptions: () => [],\n },\n not_found: {\n branchId: 'unclassified',\n journalActionTags: ['close-lease-verify-not-found'],\n buildFailureEnvelope: (d) => ({\n outcome: 'failed',\n reason:\n d.reason ??\n `Lease ${args.leaseUuid} not visible on chain after close.`,\n }),\n buildRecoveryOptions: () => [],\n },\n },\n };\n\n const verifyResult = await verifyAndRecover(spec, undefined);\n\n if (verifyResult.result !== 'success') {\n const reason =\n verifyResult.failure?.reason ?? 'close-lease verification failed.';\n if (callbacks.onFailure) {\n await callbacks.onFailure({ reason });\n }\n throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, reason);\n }\n\n // Invariant: when `verifyAndRecover` returns success, the matched\n // outcome was `'terminal'`, and the verifier's `terminal` branch\n // ALWAYS sets `diagnostic.stateName` (see the spec above). A missing\n // `stateName` on the success path means the verifier invariant is\n // broken — likely a future refactor regression. The previous\n // implementation fell back to `'LEASE_STATE_CLOSED'` silently, which\n // would lie to the caller (Copilot review PR #60, comment 3276719603).\n // Fail loudly with a typed error instead. `TX_FAILED` is the closest\n // available code in `ManifestMCPErrorCode` (no `INTERNAL_ERROR`\n // variant); the message names the invariant explicitly.\n if (!verifyResult.diagnostic.stateName) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.TX_FAILED,\n `close-lease verifier invariant violated: success outcome reached without diagnostic.stateName for lease ${args.leaseUuid}`,\n );\n }\n const finalState: LeaseStateName = verifyResult.diagnostic.stateName;\n const result: CloseLeaseResult = {\n leaseUuid: args.leaseUuid,\n finalState,\n };\n callbacks.onComplete?.(result);\n return result;\n}\n\n// --- Helpers --------------------------------------------------------\n\nfunction validateArgs(args: CloseLeaseArgs): void {\n if (typeof args.leaseUuid !== 'string' || !args.leaseUuid.match(UUID_RE)) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n `closeLease: leaseUuid must be a UUID; got \"${args.leaseUuid}\".`,\n );\n }\n}\n\nfunction renderConfirmationBlock(args: CloseLeaseArgs): DeploymentPlanBlock {\n // Image is not tracked in `CloseLeaseArgs` and `stopApp` doesn't return it;\n // surface the gap explicitly so reviewers/users see the missing context\n // rather than silently omitting an image field they'd expect.\n const text = [\n `Close lease ${args.leaseUuid}.`,\n ' Image: (image not recorded)',\n ' This is permanent — the lease cannot be reopened.',\n '',\n 'Proceed?',\n ].join('\\n');\n return { text };\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { DeployAppCallbacks, DeployAppOptions, DeployResult, DeploySpec } from "./types.js";
1
+ import { AppDeploySpec, DeployAppCallbacks, DeployAppOptions, DeployResult } from "./types.js";
2
2
 
3
3
  //#region src/deploy-app.d.ts
4
4
  /**
@@ -19,7 +19,7 @@ import { DeployAppCallbacks, DeployAppOptions, DeployResult, DeploySpec } from "
19
19
  * branch) throw directly as `ManifestMCPError(TX_FAILED)` without
20
20
  * invoking `onFailure`.
21
21
  */
22
- declare function deployApp(spec: DeploySpec, callbacks: DeployAppCallbacks, opts: DeployAppOptions): Promise<DeployResult>;
22
+ declare function deployApp(spec: AppDeploySpec, callbacks: DeployAppCallbacks, opts: DeployAppOptions): Promise<DeployResult>;
23
23
  //#endregion
24
24
  export { deployApp };
25
25
  //# sourceMappingURL=deploy-app.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"deploy-app.d.ts","names":[],"sources":["../src/deploy-app.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;iBAkIsB,SAAA,CACpB,IAAA,EAAM,UAAA,EACN,SAAA,EAAW,kBAAA,EACX,IAAA,EAAM,gBAAA,GACL,OAAA,CAAQ,YAAA"}
1
+ {"version":3,"file":"deploy-app.d.ts","names":[],"sources":["../src/deploy-app.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;iBAqIsB,SAAA,CACpB,IAAA,EAAM,aAAA,EACN,SAAA,EAAW,kBAAA,EACX,IAAA,EAAM,gBAAA,GACL,OAAA,CAAQ,YAAA"}
@@ -1,20 +1,20 @@
1
+ import { makeCancellationScope } from "./internals/cancellation.js";
1
2
  import { decode } from "./internals/lease-state.js";
2
- import { isStackSpec, summarizeSpec, validateSpec } from "./internals/spec-normalize.js";
3
- import { buildFredDeployInput, buildManifestPreviewInput } from "./internals/build-fred-input.js";
4
3
  import { classifyDeployError } from "./internals/classify-deploy-error.js";
5
4
  import { extractRunningEndpoints, formatEndpointAsUrl, normalizeFredUrl } from "./internals/connection.js";
6
5
  import { classifyDeployResponse } from "./internals/classify-deploy-response.js";
7
6
  import { EMPTY_DENOM_MAP, loadChainDenomMap } from "./internals/humanize-denom.js";
8
7
  import { evaluateReadinessFromFredResponse } from "./internals/evaluate-readiness-from-fred.js";
9
8
  import { renderDeploymentPlan } from "./internals/render-deployment-plan.js";
9
+ import { isStackSpec, summarizeSpec, validateSpec } from "./internals/spec-normalize.js";
10
10
  import { renderIntentRecap } from "./internals/render-intent-recap.js";
11
11
  import { renderPartialSuccessPrompt } from "./internals/render-partial-success-prompt.js";
12
- import { ManifestMCPError, ManifestMCPErrorCode, cosmosEstimateFee, resolveSku, setItemCustomDomain, stopApp } from "@manifest-network/manifest-mcp-core";
12
+ import { ManifestMCPError, ManifestMCPErrorCode, asLeaseUuid, asProviderUuid, cosmosEstimateFee, noopLogger, parseFqdn, parseLeaseUuid, resolveSku, setItemCustomDomain, stopApp } from "@manifest-network/manifest-mcp-core";
13
13
  import { AuthTimestampTracker, buildManifestPreview, checkDeploymentReadiness, createAuthToken, createLeaseDataSignMessage, createSignMessage, deployApp as deployApp$1, fetchActiveLease, pollLeaseUntilReady, resolveProviderUrl, uploadLeaseData, waitForAppReady } from "@manifest-network/manifest-mcp-fred";
14
14
  //#region src/deploy-app.ts
15
15
  /**
16
16
  * Public entry point: orchestrate a Manifest-Network app deployment from
17
- * a typed `DeploySpec` through the plan/confirm/broadcast/save flow.
17
+ * a typed `AppDeploySpec` through the plan/confirm/broadcast/save flow.
18
18
  *
19
19
  * Architect's α-locked composition (post-PR-3 sub-plan Q1):
20
20
  *
@@ -75,6 +75,13 @@ async function deployApp(spec, callbacks, opts) {
75
75
  throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, err instanceof Error ? err.message : `Invalid spec: ${String(err)}`);
76
76
  }
77
77
  if (typeof opts.walletProvider.signArbitrary !== "function") throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, "opts.walletProvider must implement signArbitrary for ADR-036 auth tokens.");
78
+ const { signal, throwIfCancelled, race } = makeCancellationScope({
79
+ opts,
80
+ onProgress: callbacks.onProgress,
81
+ opLabel: "Deployment",
82
+ broadcasts: true
83
+ });
84
+ throwIfCancelled();
78
85
  const walletAddress = await opts.walletProvider.getAddress();
79
86
  const clientAddress = await opts.clientManager.getAddress();
80
87
  if (walletAddress !== clientAddress) throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, `opts.walletProvider and opts.clientManager are bound to different addresses (walletProvider=${walletAddress}, clientManager=${clientAddress}); they must reference the same wallet to avoid creating an orphaned lease on the clientManager wallet when ADR-036 auth (signed by walletProvider) fails.`);
@@ -83,12 +90,17 @@ async function deployApp(spec, callbacks, opts) {
83
90
  const chainId = opts.clientManager.getConfig().chainId;
84
91
  const activeChain = /mainnet|main/i.test(chainId) ? "mainnet" : "testnet";
85
92
  const queryClient = await opts.clientManager.getQueryClient();
93
+ const readCtx = {
94
+ query: queryClient,
95
+ chain: opts.clientManager,
96
+ logger: noopLogger
97
+ };
86
98
  const resolvePin = async (s) => {
87
99
  const providerUuid = requestedProviderUuid(s);
88
100
  const skuUuid = requestedSkuUuid(s);
89
101
  try {
90
102
  return {
91
- pin: await resolveSku(queryClient, {
103
+ pin: await resolveSku(readCtx, {
92
104
  size: requestedSize(s),
93
105
  ...providerUuid !== void 0 ? { providerUuid } : {},
94
106
  ...skuUuid !== void 0 ? { skuUuid } : {}
@@ -102,9 +114,9 @@ async function deployApp(spec, callbacks, opts) {
102
114
  kind: "sku_ambiguous",
103
115
  candidates
104
116
  });
105
- const pick = await callbacks.onResolveSku(candidates);
117
+ const pick = await race(callbacks.onResolveSku(candidates));
106
118
  return {
107
- pin: await resolveSku(queryClient, {
119
+ pin: await resolveSku(readCtx, {
108
120
  size: requestedSize(s),
109
121
  skuUuid: pick.skuUuid,
110
122
  providerUuid: pick.providerUuid
@@ -116,7 +128,7 @@ async function deployApp(spec, callbacks, opts) {
116
128
  }
117
129
  };
118
130
  let { pin: pinned, elicited: pinElicited } = await resolvePin(spec);
119
- let readiness = evaluateReadinessFromFredResponse(await checkDeploymentReadiness(queryClient, tenantAddress, {
131
+ let readiness = evaluateReadinessFromFredResponse(await checkDeploymentReadiness(readCtx, tenantAddress, {
120
132
  image: primaryImage(spec),
121
133
  size: pinned.name,
122
134
  providerUuid: pinned.providerUuid,
@@ -130,7 +142,7 @@ async function deployApp(spec, callbacks, opts) {
130
142
  `${readiness.reasons.join("; ")}`;
131
143
  throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, `Readiness check failed: ${readiness.reasons.join("; ")}`);
132
144
  }
133
- let preview = await buildManifestPreview(buildManifestPreviewInput(spec, requestedSize(spec)));
145
+ let preview = await buildManifestPreview(spec);
134
146
  let summary = summarizeSpec(spec);
135
147
  let fees = await estimateFees(opts, spec, preview.meta_hash_hex, pinned.skuUuid);
136
148
  let plan = {
@@ -158,7 +170,7 @@ async function deployApp(spec, callbacks, opts) {
158
170
  block
159
171
  });
160
172
  if (callbacks.onPlan) {
161
- const verdict = await callbacks.onPlan(plan);
173
+ const verdict = await race(callbacks.onPlan(plan));
162
174
  if (verdict === "cancel") throw new ManifestMCPError(ManifestMCPErrorCode.OPERATION_CANCELLED, "User cancelled deployment at plan step.");
163
175
  if (verdict !== "confirm") {
164
176
  confirmedSpec = applyPlanEdit(confirmedSpec, verdict);
@@ -173,7 +185,7 @@ async function deployApp(spec, callbacks, opts) {
173
185
  skuUuid: pinned.skuUuid,
174
186
  providerUuid: pinned.providerUuid
175
187
  };
176
- readiness = evaluateReadinessFromFredResponse(await checkDeploymentReadiness(queryClient, tenantAddress, {
188
+ readiness = evaluateReadinessFromFredResponse(await checkDeploymentReadiness(readCtx, tenantAddress, {
177
189
  image: primaryImage(confirmedSpec),
178
190
  size: pinned.name,
179
191
  providerUuid: pinned.providerUuid,
@@ -184,7 +196,7 @@ async function deployApp(spec, callbacks, opts) {
184
196
  readiness
185
197
  });
186
198
  if (readiness.status === "block") throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, `Post-edit readiness check failed: ${readiness.reasons.join("; ")}`);
187
- preview = await buildManifestPreview(buildManifestPreviewInput(confirmedSpec, requestedSize(confirmedSpec)));
199
+ preview = await buildManifestPreview(confirmedSpec);
188
200
  summary = summarizeSpec(confirmedSpec);
189
201
  fees = await estimateFees(opts, confirmedSpec, preview.meta_hash_hex, pinned.skuUuid);
190
202
  plan = {
@@ -213,7 +225,7 @@ async function deployApp(spec, callbacks, opts) {
213
225
  activeChain
214
226
  }) };
215
227
  if (callbacks.onConfirm) {
216
- if (await callbacks.onConfirm(recapBlock) !== "yes") throw new ManifestMCPError(ManifestMCPErrorCode.OPERATION_CANCELLED, "User declined to proceed at intent-recap step.");
228
+ if (await race(callbacks.onConfirm(recapBlock)) !== "yes") throw new ManifestMCPError(ManifestMCPErrorCode.OPERATION_CANCELLED, "User declined to proceed at intent-recap step.");
217
229
  }
218
230
  callbacks.onProgress?.({ kind: "user_confirmed" });
219
231
  const signArbitrary = opts.walletProvider.signArbitrary.bind(opts.walletProvider);
@@ -228,14 +240,26 @@ async function deployApp(spec, callbacks, opts) {
228
240
  const { pub_key, signature } = await signArbitrary(address, createLeaseDataSignMessage(leaseUuid, metaHashHex, ts));
229
241
  return createAuthToken(address, leaseUuid, ts, pub_key.value, signature, metaHashHex);
230
242
  };
243
+ throwIfCancelled();
231
244
  callbacks.onProgress?.({ kind: "deploy_app_broadcast" });
232
- const fredInput = buildFredDeployInput(confirmedSpec, pinned.name, {
245
+ const fredInput = {
246
+ ...confirmedSpec,
247
+ size: pinned.name,
233
248
  skuUuid: pinned.skuUuid,
234
249
  providerUuid: pinned.providerUuid
235
- });
250
+ };
236
251
  let fredResult;
237
252
  try {
238
- fredResult = await deployApp$1(opts.clientManager, getAuthToken, getLeaseDataAuthToken, fredInput, opts.fetchFn);
253
+ fredResult = await deployApp$1({
254
+ query: queryClient,
255
+ chain: opts.clientManager,
256
+ fetch: opts.fetchFn ?? globalThis.fetch,
257
+ logger: noopLogger,
258
+ providerAuth: {
259
+ providerToken: (i) => getAuthToken(i.address, i.leaseUuid),
260
+ leaseDataToken: (i) => getLeaseDataAuthToken(i.address, i.leaseUuid, i.metaHashHex)
261
+ }
262
+ }, fredInput, signal ? { abortSignal: signal } : {});
239
263
  } catch (err) {
240
264
  const recoveryCtx = {
241
265
  manifestJson: preview.manifest_json,
@@ -267,7 +291,19 @@ async function deployApp(spec, callbacks, opts) {
267
291
  let attempt = 0;
268
292
  let pollResult;
269
293
  try {
270
- pollResult = await waitForAppReady(queryClient, tenantAddress, leaseUuid, getAuthToken, {
294
+ pollResult = await waitForAppReady({
295
+ query: queryClient,
296
+ chain: opts.clientManager,
297
+ fetch: opts.fetchFn ?? globalThis.fetch,
298
+ logger: noopLogger,
299
+ providerAuth: {
300
+ providerToken: (i) => getAuthToken(i.address, i.leaseUuid),
301
+ leaseDataToken: (i) => getLeaseDataAuthToken(i.address, i.leaseUuid, i.metaHashHex)
302
+ }
303
+ }, {
304
+ address: tenantAddress,
305
+ leaseUuid
306
+ }, {
271
307
  timeoutMs: opts.waitForReadyTimeoutMs ?? 48e4,
272
308
  onProgress: (status) => {
273
309
  attempt += 1;
@@ -280,7 +316,7 @@ async function deployApp(spec, callbacks, opts) {
280
316
  ...stateName !== void 0 ? { state: stateName } : {}
281
317
  });
282
318
  }
283
- }, opts.fetchFn);
319
+ });
284
320
  } catch (err) {
285
321
  const reason = err instanceof Error ? `wait_for_app_ready failed for lease ${leaseUuid}: ${err.message}` : `wait_for_app_ready failed for lease ${leaseUuid}: ${String(err)}`;
286
322
  throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, reason);
@@ -298,8 +334,8 @@ async function deployApp(spec, callbacks, opts) {
298
334
  }
299
335
  fredResult = {
300
336
  ...fredResult,
301
- lease_uuid: pollResult.lease_uuid,
302
- provider_uuid: pollResult.provider_uuid,
337
+ lease_uuid: asLeaseUuid(pollResult.lease_uuid),
338
+ provider_uuid: asProviderUuid(pollResult.provider_uuid),
303
339
  provider_url: pollResult.provider_url
304
340
  };
305
341
  liveState = pollResult.status.state;
@@ -353,15 +389,13 @@ function primaryImage(spec) {
353
389
  return spec.image ?? "";
354
390
  }
355
391
  function requestedSize(spec) {
356
- const recorded = spec.size;
357
- return typeof recorded === "string" && recorded.length > 0 ? recorded : "small";
392
+ return spec.size;
358
393
  }
359
394
  /**
360
395
  * SKU disambiguator intent helpers. `providerUuid` / `skuUuid` are
361
- * first-class optional fields on both `DeploySpec` variants (ENG-296,
362
- * mirroring ENG-275's typed `size`). Returns `undefined` for absent /
363
- * empty values so `resolveSku` only narrows when a real disambiguator is
364
- * supplied.
396
+ * first-class optional fields on `AppDeploySpec` (ENG-296, mirroring
397
+ * ENG-275's typed `size`). Returns `undefined` for absent / empty values
398
+ * so `resolveSku` only narrows when a real disambiguator is supplied.
365
399
  */
366
400
  function requestedProviderUuid(spec) {
367
401
  const v = spec.providerUuid;
@@ -426,11 +460,10 @@ function applyPlanEdit(spec, edit) {
426
460
  }
427
461
  };
428
462
  }
429
- const single = spec;
430
463
  return {
431
- ...single,
464
+ ...spec,
432
465
  env: {
433
- ...single.env ?? {},
466
+ ...spec.env ?? {},
434
467
  ...edit.env
435
468
  }
436
469
  };
@@ -478,7 +511,10 @@ async function dispatchRecovery(choice, envelope, spec, opts, callbacks, ctx) {
478
511
  case "salvage_without_domain": throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, `salvage_without_domain: lease ${leaseUuid} retained without domain; caller should re-run troubleshootDeployment.`);
479
512
  case "cancel_lease":
480
513
  case "close_lease":
481
- await stopApp(opts.clientManager, leaseUuid);
514
+ await stopApp({
515
+ chain: opts.clientManager,
516
+ logger: noopLogger
517
+ }, { leaseUuid: parseLeaseUuid(leaseUuid) });
482
518
  throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, `${choice.id}: lease ${leaseUuid} closed.`);
483
519
  }
484
520
  throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, `Unknown recovery option: ${choice.id}`);
@@ -537,19 +573,30 @@ async function retrySetDomainAndComplete(leaseUuid, spec, opts, callbacks, ctx)
537
573
  const domain = customDomainOf(spec);
538
574
  if (!domain) throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, "retry_set_domain requires a customDomain in spec.");
539
575
  const serviceName = customDomainServiceOf(spec);
540
- const setItemOpts = serviceName ? { serviceName } : void 0;
541
576
  try {
542
- await setItemCustomDomain(opts.clientManager, leaseUuid, domain, setItemOpts);
577
+ await setItemCustomDomain({
578
+ chain: opts.clientManager,
579
+ logger: noopLogger
580
+ }, {
581
+ leaseUuid: parseLeaseUuid(leaseUuid),
582
+ customDomain: parseFqdn(domain),
583
+ serviceName
584
+ });
543
585
  } catch (err) {
544
586
  const reason = err instanceof Error ? `retry_set_domain set-item-custom-domain failed for lease ${leaseUuid}: ${err.message}` : `retry_set_domain set-item-custom-domain failed for lease ${leaseUuid}: ${String(err)}`;
545
587
  throw new ManifestMCPError(err instanceof ManifestMCPError ? err.code : ManifestMCPErrorCode.TX_FAILED, reason);
546
588
  }
547
- const queryClient = await opts.clientManager.getQueryClient();
589
+ const readCtx = {
590
+ query: await opts.clientManager.getQueryClient(),
591
+ chain: opts.clientManager,
592
+ fetch: opts.fetchFn ?? globalThis.fetch,
593
+ logger: noopLogger
594
+ };
548
595
  let lease;
549
596
  let providerApiUrl;
550
597
  try {
551
- lease = await fetchActiveLease(queryClient, leaseUuid, "cannot complete retry_set_domain");
552
- providerApiUrl = await resolveProviderUrl(queryClient, lease.providerUuid);
598
+ lease = await fetchActiveLease(readCtx, leaseUuid, "cannot complete retry_set_domain");
599
+ providerApiUrl = await resolveProviderUrl(readCtx, lease.providerUuid);
553
600
  } catch (err) {
554
601
  const reason = err instanceof Error ? `retry_set_domain failed to resolve provider for lease ${leaseUuid}: ${err.message}` : `retry_set_domain failed to resolve provider for lease ${leaseUuid}: ${String(err)}`;
555
602
  throw new ManifestMCPError(err instanceof ManifestMCPError ? err.code : ManifestMCPErrorCode.TX_FAILED, reason);