@manifest-network/manifest-agent-core 0.13.1 → 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.
- package/dist/close-lease.d.ts.map +1 -1
- package/dist/close-lease.js +15 -3
- package/dist/close-lease.js.map +1 -1
- package/dist/deploy-app.d.ts +2 -2
- package/dist/deploy-app.d.ts.map +1 -1
- package/dist/deploy-app.js +155 -43
- package/dist/deploy-app.js.map +1 -1
- package/dist/deploy-app.test-d.d.ts +1 -0
- package/dist/deploy-app.test-d.js +11 -0
- package/dist/deploy-app.test-d.js.map +1 -0
- package/dist/guarded-fetch.d.ts +2 -0
- package/dist/guarded-fetch.js +2 -0
- package/dist/index.d.ts +2 -3
- package/dist/index.js +1 -2
- package/dist/internals/cancellation.d.ts +57 -0
- package/dist/internals/cancellation.d.ts.map +1 -0
- package/dist/internals/cancellation.js +79 -0
- package/dist/internals/cancellation.js.map +1 -0
- package/dist/internals/evaluate-readiness-from-fred.d.ts.map +1 -1
- package/dist/internals/evaluate-readiness-from-fred.js +12 -3
- package/dist/internals/evaluate-readiness-from-fred.js.map +1 -1
- package/dist/internals/evaluate-readiness.d.ts +14 -0
- package/dist/internals/evaluate-readiness.d.ts.map +1 -1
- package/dist/internals/evaluate-readiness.js +21 -8
- package/dist/internals/evaluate-readiness.js.map +1 -1
- package/dist/internals/inspect-image.js +1 -1
- package/dist/internals/inspect-image.js.map +1 -1
- package/dist/internals/render-deployment-plan.d.ts +6 -0
- package/dist/internals/render-deployment-plan.d.ts.map +1 -1
- package/dist/internals/render-deployment-plan.js +8 -5
- package/dist/internals/render-deployment-plan.js.map +1 -1
- package/dist/internals/render-intent-recap.d.ts +13 -11
- package/dist/internals/render-intent-recap.d.ts.map +1 -1
- package/dist/internals/render-intent-recap.js +5 -4
- package/dist/internals/render-intent-recap.js.map +1 -1
- package/dist/internals/spec-normalize.d.ts +34 -27
- package/dist/internals/spec-normalize.d.ts.map +1 -1
- package/dist/internals/spec-normalize.js +28 -21
- package/dist/internals/spec-normalize.js.map +1 -1
- package/dist/manage-domain.d.ts.map +1 -1
- package/dist/manage-domain.js +34 -8
- package/dist/manage-domain.js.map +1 -1
- package/dist/node_modules/@vitest/pretty-format/dist/index.js +888 -0
- package/dist/node_modules/@vitest/pretty-format/dist/index.js.map +1 -0
- package/dist/node_modules/@vitest/runner/dist/chunk-artifact.js +1500 -0
- package/dist/node_modules/@vitest/runner/dist/chunk-artifact.js.map +1 -0
- package/dist/node_modules/@vitest/runner/dist/index.js +1 -0
- package/dist/node_modules/@vitest/runner/dist/utils.js +1 -0
- package/dist/node_modules/@vitest/utils/dist/chunk-pathe.M-eThtNZ.js +82 -0
- package/dist/node_modules/@vitest/utils/dist/chunk-pathe.M-eThtNZ.js.map +1 -0
- package/dist/node_modules/@vitest/utils/dist/display.js +558 -0
- package/dist/node_modules/@vitest/utils/dist/display.js.map +1 -0
- package/dist/node_modules/@vitest/utils/dist/helpers.js +68 -0
- package/dist/node_modules/@vitest/utils/dist/helpers.js.map +1 -0
- package/dist/node_modules/@vitest/utils/dist/source-map.js +95 -0
- package/dist/node_modules/@vitest/utils/dist/source-map.js.map +1 -0
- package/dist/node_modules/@vitest/utils/dist/timers.js +20 -0
- package/dist/node_modules/@vitest/utils/dist/timers.js.map +1 -0
- package/dist/node_modules/tinyrainbow/dist/index.js +86 -0
- package/dist/node_modules/tinyrainbow/dist/index.js.map +1 -0
- package/dist/node_modules/vite/dist/node/module-runner.js +22 -0
- package/dist/node_modules/vite/dist/node/module-runner.js.map +1 -0
- package/dist/node_modules/vitest/dist/index.js +6 -0
- package/dist/troubleshoot.d.ts.map +1 -1
- package/dist/troubleshoot.js +11 -1
- package/dist/troubleshoot.js.map +1 -1
- package/dist/types.d.ts +42 -22
- package/dist/types.d.ts.map +1 -1
- package/package.json +12 -6
- package/dist/internals/build-fred-input.d.ts +0 -38
- package/dist/internals/build-fred-input.d.ts.map +0 -1
- package/dist/internals/build-fred-input.js +0 -147
- package/dist/internals/build-fred-input.js.map +0 -1
- package/dist/internals/find-sku-uuid.d.ts +0 -40
- package/dist/internals/find-sku-uuid.d.ts.map +0 -1
- package/dist/internals/find-sku-uuid.js +0 -20
- package/dist/internals/find-sku-uuid.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"close-lease.d.ts","names":[],"sources":["../src/close-lease.ts"],"mappings":";;;;
|
|
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"}
|
package/dist/close-lease.js
CHANGED
|
@@ -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
|
-
|
|
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;
|
package/dist/close-lease.js.map
CHANGED
|
@@ -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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"}
|
package/dist/deploy-app.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DeployAppCallbacks, DeployAppOptions, DeployResult
|
|
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:
|
|
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
|
package/dist/deploy-app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy-app.d.ts","names":[],"sources":["../src/deploy-app.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;
|
|
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"}
|
package/dist/deploy-app.js
CHANGED
|
@@ -1,21 +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
|
-
import { findSkuUuid } from "./internals/find-sku-uuid.js";
|
|
10
8
|
import { renderDeploymentPlan } from "./internals/render-deployment-plan.js";
|
|
9
|
+
import { isStackSpec, summarizeSpec, validateSpec } from "./internals/spec-normalize.js";
|
|
11
10
|
import { renderIntentRecap } from "./internals/render-intent-recap.js";
|
|
12
11
|
import { renderPartialSuccessPrompt } from "./internals/render-partial-success-prompt.js";
|
|
13
|
-
import { ManifestMCPError, ManifestMCPErrorCode, cosmosEstimateFee, 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";
|
|
14
13
|
import { AuthTimestampTracker, buildManifestPreview, checkDeploymentReadiness, createAuthToken, createLeaseDataSignMessage, createSignMessage, deployApp as deployApp$1, fetchActiveLease, pollLeaseUntilReady, resolveProviderUrl, uploadLeaseData, waitForAppReady } from "@manifest-network/manifest-mcp-fred";
|
|
15
14
|
//#region src/deploy-app.ts
|
|
16
15
|
/**
|
|
17
16
|
* Public entry point: orchestrate a Manifest-Network app deployment from
|
|
18
|
-
* a typed `
|
|
17
|
+
* a typed `AppDeploySpec` through the plan/confirm/broadcast/save flow.
|
|
19
18
|
*
|
|
20
19
|
* Architect's α-locked composition (post-PR-3 sub-plan Q1):
|
|
21
20
|
*
|
|
@@ -76,6 +75,13 @@ async function deployApp(spec, callbacks, opts) {
|
|
|
76
75
|
throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, err instanceof Error ? err.message : `Invalid spec: ${String(err)}`);
|
|
77
76
|
}
|
|
78
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();
|
|
79
85
|
const walletAddress = await opts.walletProvider.getAddress();
|
|
80
86
|
const clientAddress = await opts.clientManager.getAddress();
|
|
81
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.`);
|
|
@@ -84,9 +90,49 @@ async function deployApp(spec, callbacks, opts) {
|
|
|
84
90
|
const chainId = opts.clientManager.getConfig().chainId;
|
|
85
91
|
const activeChain = /mainnet|main/i.test(chainId) ? "mainnet" : "testnet";
|
|
86
92
|
const queryClient = await opts.clientManager.getQueryClient();
|
|
87
|
-
|
|
93
|
+
const readCtx = {
|
|
94
|
+
query: queryClient,
|
|
95
|
+
chain: opts.clientManager,
|
|
96
|
+
logger: noopLogger
|
|
97
|
+
};
|
|
98
|
+
const resolvePin = async (s) => {
|
|
99
|
+
const providerUuid = requestedProviderUuid(s);
|
|
100
|
+
const skuUuid = requestedSkuUuid(s);
|
|
101
|
+
try {
|
|
102
|
+
return {
|
|
103
|
+
pin: await resolveSku(readCtx, {
|
|
104
|
+
size: requestedSize(s),
|
|
105
|
+
...providerUuid !== void 0 ? { providerUuid } : {},
|
|
106
|
+
...skuUuid !== void 0 ? { skuUuid } : {}
|
|
107
|
+
}),
|
|
108
|
+
elicited: false
|
|
109
|
+
};
|
|
110
|
+
} catch (err) {
|
|
111
|
+
if (err instanceof ManifestMCPError && err.code === ManifestMCPErrorCode.SKU_AMBIGUOUS && callbacks.onResolveSku) {
|
|
112
|
+
const candidates = err.details?.candidates ?? [];
|
|
113
|
+
callbacks.onProgress?.({
|
|
114
|
+
kind: "sku_ambiguous",
|
|
115
|
+
candidates
|
|
116
|
+
});
|
|
117
|
+
const pick = await race(callbacks.onResolveSku(candidates));
|
|
118
|
+
return {
|
|
119
|
+
pin: await resolveSku(readCtx, {
|
|
120
|
+
size: requestedSize(s),
|
|
121
|
+
skuUuid: pick.skuUuid,
|
|
122
|
+
providerUuid: pick.providerUuid
|
|
123
|
+
}),
|
|
124
|
+
elicited: true
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
throw err;
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
let { pin: pinned, elicited: pinElicited } = await resolvePin(spec);
|
|
131
|
+
let readiness = evaluateReadinessFromFredResponse(await checkDeploymentReadiness(readCtx, tenantAddress, {
|
|
88
132
|
image: primaryImage(spec),
|
|
89
|
-
size:
|
|
133
|
+
size: pinned.name,
|
|
134
|
+
providerUuid: pinned.providerUuid,
|
|
135
|
+
skuUuid: pinned.skuUuid
|
|
90
136
|
}), opts.clientManager.getConfig().gasPrice ?? "1umfx", denomMap, tenantAddress);
|
|
91
137
|
callbacks.onProgress?.({
|
|
92
138
|
kind: "readiness_evaluated",
|
|
@@ -96,30 +142,35 @@ async function deployApp(spec, callbacks, opts) {
|
|
|
96
142
|
`${readiness.reasons.join("; ")}`;
|
|
97
143
|
throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, `Readiness check failed: ${readiness.reasons.join("; ")}`);
|
|
98
144
|
}
|
|
99
|
-
let preview = await buildManifestPreview(
|
|
145
|
+
let preview = await buildManifestPreview(spec);
|
|
100
146
|
let summary = summarizeSpec(spec);
|
|
101
|
-
let fees = await estimateFees(opts, spec, preview.meta_hash_hex);
|
|
147
|
+
let fees = await estimateFees(opts, spec, preview.meta_hash_hex, pinned.skuUuid);
|
|
102
148
|
let plan = {
|
|
103
149
|
summary,
|
|
104
150
|
readiness,
|
|
105
151
|
fees
|
|
106
152
|
};
|
|
107
|
-
let confirmedSpec =
|
|
153
|
+
let confirmedSpec = pinElicited ? {
|
|
154
|
+
...spec,
|
|
155
|
+
skuUuid: pinned.skuUuid,
|
|
156
|
+
providerUuid: pinned.providerUuid
|
|
157
|
+
} : spec;
|
|
108
158
|
const block = renderDeploymentPlan({
|
|
109
159
|
plan,
|
|
110
160
|
denomMap,
|
|
111
161
|
image: primaryImage(spec),
|
|
112
|
-
size:
|
|
162
|
+
size: pinned.name,
|
|
113
163
|
metaHash: preview.meta_hash_hex,
|
|
114
164
|
customDomain: customDomainOf(spec),
|
|
115
|
-
customDomainService: customDomainServiceOf(spec)
|
|
165
|
+
customDomainService: customDomainServiceOf(spec),
|
|
166
|
+
providerUuid: pinned.providerUuid
|
|
116
167
|
});
|
|
117
168
|
callbacks.onProgress?.({
|
|
118
169
|
kind: "deployment_plan_rendered",
|
|
119
170
|
block
|
|
120
171
|
});
|
|
121
172
|
if (callbacks.onPlan) {
|
|
122
|
-
const verdict = await callbacks.onPlan(plan);
|
|
173
|
+
const verdict = await race(callbacks.onPlan(plan));
|
|
123
174
|
if (verdict === "cancel") throw new ManifestMCPError(ManifestMCPErrorCode.OPERATION_CANCELLED, "User cancelled deployment at plan step.");
|
|
124
175
|
if (verdict !== "confirm") {
|
|
125
176
|
confirmedSpec = applyPlanEdit(confirmedSpec, verdict);
|
|
@@ -128,18 +179,26 @@ async function deployApp(spec, callbacks, opts) {
|
|
|
128
179
|
} catch (err) {
|
|
129
180
|
throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, err instanceof Error ? `Post-edit spec failed validation: ${err.message}` : `Post-edit spec failed validation: ${String(err)}`);
|
|
130
181
|
}
|
|
131
|
-
|
|
182
|
+
({pin: pinned, elicited: pinElicited} = await resolvePin(confirmedSpec));
|
|
183
|
+
if (pinElicited) confirmedSpec = {
|
|
184
|
+
...confirmedSpec,
|
|
185
|
+
skuUuid: pinned.skuUuid,
|
|
186
|
+
providerUuid: pinned.providerUuid
|
|
187
|
+
};
|
|
188
|
+
readiness = evaluateReadinessFromFredResponse(await checkDeploymentReadiness(readCtx, tenantAddress, {
|
|
132
189
|
image: primaryImage(confirmedSpec),
|
|
133
|
-
size:
|
|
190
|
+
size: pinned.name,
|
|
191
|
+
providerUuid: pinned.providerUuid,
|
|
192
|
+
skuUuid: pinned.skuUuid
|
|
134
193
|
}), opts.clientManager.getConfig().gasPrice ?? "1umfx", denomMap, tenantAddress);
|
|
135
194
|
callbacks.onProgress?.({
|
|
136
195
|
kind: "readiness_evaluated",
|
|
137
196
|
readiness
|
|
138
197
|
});
|
|
139
198
|
if (readiness.status === "block") throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, `Post-edit readiness check failed: ${readiness.reasons.join("; ")}`);
|
|
140
|
-
preview = await buildManifestPreview(
|
|
199
|
+
preview = await buildManifestPreview(confirmedSpec);
|
|
141
200
|
summary = summarizeSpec(confirmedSpec);
|
|
142
|
-
fees = await estimateFees(opts, confirmedSpec, preview.meta_hash_hex);
|
|
201
|
+
fees = await estimateFees(opts, confirmedSpec, preview.meta_hash_hex, pinned.skuUuid);
|
|
143
202
|
plan = {
|
|
144
203
|
summary,
|
|
145
204
|
readiness,
|
|
@@ -149,10 +208,11 @@ async function deployApp(spec, callbacks, opts) {
|
|
|
149
208
|
plan,
|
|
150
209
|
denomMap,
|
|
151
210
|
image: primaryImage(confirmedSpec),
|
|
152
|
-
size:
|
|
211
|
+
size: pinned.name,
|
|
153
212
|
metaHash: preview.meta_hash_hex,
|
|
154
213
|
customDomain: customDomainOf(confirmedSpec),
|
|
155
|
-
customDomainService: customDomainServiceOf(confirmedSpec)
|
|
214
|
+
customDomainService: customDomainServiceOf(confirmedSpec),
|
|
215
|
+
providerUuid: pinned.providerUuid
|
|
156
216
|
});
|
|
157
217
|
callbacks.onProgress?.({
|
|
158
218
|
kind: "deployment_plan_rendered",
|
|
@@ -165,7 +225,7 @@ async function deployApp(spec, callbacks, opts) {
|
|
|
165
225
|
activeChain
|
|
166
226
|
}) };
|
|
167
227
|
if (callbacks.onConfirm) {
|
|
168
|
-
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.");
|
|
169
229
|
}
|
|
170
230
|
callbacks.onProgress?.({ kind: "user_confirmed" });
|
|
171
231
|
const signArbitrary = opts.walletProvider.signArbitrary.bind(opts.walletProvider);
|
|
@@ -180,11 +240,26 @@ async function deployApp(spec, callbacks, opts) {
|
|
|
180
240
|
const { pub_key, signature } = await signArbitrary(address, createLeaseDataSignMessage(leaseUuid, metaHashHex, ts));
|
|
181
241
|
return createAuthToken(address, leaseUuid, ts, pub_key.value, signature, metaHashHex);
|
|
182
242
|
};
|
|
243
|
+
throwIfCancelled();
|
|
183
244
|
callbacks.onProgress?.({ kind: "deploy_app_broadcast" });
|
|
184
|
-
const fredInput =
|
|
245
|
+
const fredInput = {
|
|
246
|
+
...confirmedSpec,
|
|
247
|
+
size: pinned.name,
|
|
248
|
+
skuUuid: pinned.skuUuid,
|
|
249
|
+
providerUuid: pinned.providerUuid
|
|
250
|
+
};
|
|
185
251
|
let fredResult;
|
|
186
252
|
try {
|
|
187
|
-
fredResult = await deployApp$1(
|
|
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 } : {});
|
|
188
263
|
} catch (err) {
|
|
189
264
|
const recoveryCtx = {
|
|
190
265
|
manifestJson: preview.manifest_json,
|
|
@@ -193,7 +268,8 @@ async function deployApp(spec, callbacks, opts) {
|
|
|
193
268
|
getLeaseDataAuthToken,
|
|
194
269
|
tenantAddress,
|
|
195
270
|
chainId,
|
|
196
|
-
denomMap
|
|
271
|
+
denomMap,
|
|
272
|
+
skuName: pinned.name
|
|
197
273
|
};
|
|
198
274
|
return await handleBroadcastFailure(err, confirmedSpec, callbacks, opts, recoveryCtx);
|
|
199
275
|
}
|
|
@@ -215,7 +291,19 @@ async function deployApp(spec, callbacks, opts) {
|
|
|
215
291
|
let attempt = 0;
|
|
216
292
|
let pollResult;
|
|
217
293
|
try {
|
|
218
|
-
pollResult = await waitForAppReady(
|
|
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
|
+
}, {
|
|
219
307
|
timeoutMs: opts.waitForReadyTimeoutMs ?? 48e4,
|
|
220
308
|
onProgress: (status) => {
|
|
221
309
|
attempt += 1;
|
|
@@ -228,7 +316,7 @@ async function deployApp(spec, callbacks, opts) {
|
|
|
228
316
|
...stateName !== void 0 ? { state: stateName } : {}
|
|
229
317
|
});
|
|
230
318
|
}
|
|
231
|
-
}
|
|
319
|
+
});
|
|
232
320
|
} catch (err) {
|
|
233
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)}`;
|
|
234
322
|
throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, reason);
|
|
@@ -246,8 +334,8 @@ async function deployApp(spec, callbacks, opts) {
|
|
|
246
334
|
}
|
|
247
335
|
fredResult = {
|
|
248
336
|
...fredResult,
|
|
249
|
-
lease_uuid: pollResult.lease_uuid,
|
|
250
|
-
provider_uuid: pollResult.provider_uuid,
|
|
337
|
+
lease_uuid: asLeaseUuid(pollResult.lease_uuid),
|
|
338
|
+
provider_uuid: asProviderUuid(pollResult.provider_uuid),
|
|
251
339
|
provider_url: pollResult.provider_url
|
|
252
340
|
};
|
|
253
341
|
liveState = pollResult.status.state;
|
|
@@ -260,7 +348,7 @@ async function deployApp(spec, callbacks, opts) {
|
|
|
260
348
|
const persistedPath = await tryPersistManifest({
|
|
261
349
|
leaseUuid: fredResult.lease_uuid,
|
|
262
350
|
image: primaryImage(confirmedSpec),
|
|
263
|
-
size:
|
|
351
|
+
size: pinned.name,
|
|
264
352
|
metaHash: preview.meta_hash_hex,
|
|
265
353
|
chainId,
|
|
266
354
|
manifestJson: preview.manifest_json,
|
|
@@ -301,8 +389,21 @@ function primaryImage(spec) {
|
|
|
301
389
|
return spec.image ?? "";
|
|
302
390
|
}
|
|
303
391
|
function requestedSize(spec) {
|
|
304
|
-
|
|
305
|
-
|
|
392
|
+
return spec.size;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* SKU disambiguator intent helpers. `providerUuid` / `skuUuid` are
|
|
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.
|
|
399
|
+
*/
|
|
400
|
+
function requestedProviderUuid(spec) {
|
|
401
|
+
const v = spec.providerUuid;
|
|
402
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
403
|
+
}
|
|
404
|
+
function requestedSkuUuid(spec) {
|
|
405
|
+
const v = spec.skuUuid;
|
|
406
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
306
407
|
}
|
|
307
408
|
function customDomainOf(spec) {
|
|
308
409
|
return spec.customDomain;
|
|
@@ -310,9 +411,7 @@ function customDomainOf(spec) {
|
|
|
310
411
|
function customDomainServiceOf(spec) {
|
|
311
412
|
if (isStackSpec(spec)) return spec.serviceName;
|
|
312
413
|
}
|
|
313
|
-
async function estimateFees(opts, spec, metaHashHex) {
|
|
314
|
-
const size = requestedSize(spec);
|
|
315
|
-
const { skuUuid } = await findSkuUuid(opts.clientManager, size);
|
|
414
|
+
async function estimateFees(opts, spec, metaHashHex, skuUuid) {
|
|
316
415
|
const itemArgs = isStackSpec(spec) ? Object.keys(spec.services).map((name) => `${skuUuid}:1:${name}`) : [`${skuUuid}:1`];
|
|
317
416
|
let createLeaseEstimate;
|
|
318
417
|
try {
|
|
@@ -361,11 +460,10 @@ function applyPlanEdit(spec, edit) {
|
|
|
361
460
|
}
|
|
362
461
|
};
|
|
363
462
|
}
|
|
364
|
-
const single = spec;
|
|
365
463
|
return {
|
|
366
|
-
...
|
|
464
|
+
...spec,
|
|
367
465
|
env: {
|
|
368
|
-
...
|
|
466
|
+
...spec.env ?? {},
|
|
369
467
|
...edit.env
|
|
370
468
|
}
|
|
371
469
|
};
|
|
@@ -413,7 +511,10 @@ async function dispatchRecovery(choice, envelope, spec, opts, callbacks, ctx) {
|
|
|
413
511
|
case "salvage_without_domain": throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, `salvage_without_domain: lease ${leaseUuid} retained without domain; caller should re-run troubleshootDeployment.`);
|
|
414
512
|
case "cancel_lease":
|
|
415
513
|
case "close_lease":
|
|
416
|
-
await stopApp(
|
|
514
|
+
await stopApp({
|
|
515
|
+
chain: opts.clientManager,
|
|
516
|
+
logger: noopLogger
|
|
517
|
+
}, { leaseUuid: parseLeaseUuid(leaseUuid) });
|
|
417
518
|
throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, `${choice.id}: lease ${leaseUuid} closed.`);
|
|
418
519
|
}
|
|
419
520
|
throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, `Unknown recovery option: ${choice.id}`);
|
|
@@ -472,19 +573,30 @@ async function retrySetDomainAndComplete(leaseUuid, spec, opts, callbacks, ctx)
|
|
|
472
573
|
const domain = customDomainOf(spec);
|
|
473
574
|
if (!domain) throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, "retry_set_domain requires a customDomain in spec.");
|
|
474
575
|
const serviceName = customDomainServiceOf(spec);
|
|
475
|
-
const setItemOpts = serviceName ? { serviceName } : void 0;
|
|
476
576
|
try {
|
|
477
|
-
await setItemCustomDomain(
|
|
577
|
+
await setItemCustomDomain({
|
|
578
|
+
chain: opts.clientManager,
|
|
579
|
+
logger: noopLogger
|
|
580
|
+
}, {
|
|
581
|
+
leaseUuid: parseLeaseUuid(leaseUuid),
|
|
582
|
+
customDomain: parseFqdn(domain),
|
|
583
|
+
serviceName
|
|
584
|
+
});
|
|
478
585
|
} catch (err) {
|
|
479
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)}`;
|
|
480
587
|
throw new ManifestMCPError(err instanceof ManifestMCPError ? err.code : ManifestMCPErrorCode.TX_FAILED, reason);
|
|
481
588
|
}
|
|
482
|
-
const
|
|
589
|
+
const readCtx = {
|
|
590
|
+
query: await opts.clientManager.getQueryClient(),
|
|
591
|
+
chain: opts.clientManager,
|
|
592
|
+
fetch: opts.fetchFn ?? globalThis.fetch,
|
|
593
|
+
logger: noopLogger
|
|
594
|
+
};
|
|
483
595
|
let lease;
|
|
484
596
|
let providerApiUrl;
|
|
485
597
|
try {
|
|
486
|
-
lease = await fetchActiveLease(
|
|
487
|
-
providerApiUrl = await resolveProviderUrl(
|
|
598
|
+
lease = await fetchActiveLease(readCtx, leaseUuid, "cannot complete retry_set_domain");
|
|
599
|
+
providerApiUrl = await resolveProviderUrl(readCtx, lease.providerUuid);
|
|
488
600
|
} catch (err) {
|
|
489
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)}`;
|
|
490
602
|
throw new ManifestMCPError(err instanceof ManifestMCPError ? err.code : ManifestMCPErrorCode.TX_FAILED, reason);
|
|
@@ -537,7 +649,7 @@ async function retrySetDomainAndComplete(leaseUuid, spec, opts, callbacks, ctx)
|
|
|
537
649
|
const persistedPath = await tryPersistManifest({
|
|
538
650
|
leaseUuid,
|
|
539
651
|
image: primaryImage(spec),
|
|
540
|
-
size:
|
|
652
|
+
size: ctx.skuName,
|
|
541
653
|
metaHash: ctx.metaHash,
|
|
542
654
|
chainId: ctx.chainId,
|
|
543
655
|
manifestJson: ctx.manifestJson,
|