@manifest-network/manifest-agent-core 0.9.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/README.md +39 -0
- package/dist/close-lease.d.ts +33 -0
- package/dist/close-lease.d.ts.map +1 -0
- package/dist/close-lease.js +138 -0
- package/dist/close-lease.js.map +1 -0
- package/dist/deploy-app.d.ts +24 -0
- package/dist/deploy-app.d.ts.map +1 -0
- package/dist/deploy-app.js +446 -0
- package/dist/deploy-app.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +7 -0
- package/dist/internals/classify-deploy-error.d.ts +41 -0
- package/dist/internals/classify-deploy-error.d.ts.map +1 -0
- package/dist/internals/classify-deploy-error.js +79 -0
- package/dist/internals/classify-deploy-error.js.map +1 -0
- package/dist/internals/classify-deploy-response.d.ts +56 -0
- package/dist/internals/classify-deploy-response.d.ts.map +1 -0
- package/dist/internals/classify-deploy-response.js +33 -0
- package/dist/internals/classify-deploy-response.js.map +1 -0
- package/dist/internals/connection.d.ts +76 -0
- package/dist/internals/connection.d.ts.map +1 -0
- package/dist/internals/connection.js +94 -0
- package/dist/internals/connection.js.map +1 -0
- package/dist/internals/evaluate-readiness.d.ts +55 -0
- package/dist/internals/evaluate-readiness.d.ts.map +1 -0
- package/dist/internals/evaluate-readiness.js +131 -0
- package/dist/internals/evaluate-readiness.js.map +1 -0
- package/dist/internals/find-sku-uuid.d.ts +40 -0
- package/dist/internals/find-sku-uuid.d.ts.map +1 -0
- package/dist/internals/find-sku-uuid.js +20 -0
- package/dist/internals/find-sku-uuid.js.map +1 -0
- package/dist/internals/format-success.d.ts +35 -0
- package/dist/internals/format-success.d.ts.map +1 -0
- package/dist/internals/format-success.js +80 -0
- package/dist/internals/format-success.js.map +1 -0
- package/dist/internals/guarded-fetch.d.ts +138 -0
- package/dist/internals/guarded-fetch.d.ts.map +1 -0
- package/dist/internals/guarded-fetch.js +242 -0
- package/dist/internals/guarded-fetch.js.map +1 -0
- package/dist/internals/humanize-denom.d.ts +45 -0
- package/dist/internals/humanize-denom.d.ts.map +1 -0
- package/dist/internals/humanize-denom.js +105 -0
- package/dist/internals/humanize-denom.js.map +1 -0
- package/dist/internals/inspect-image.d.ts +31 -0
- package/dist/internals/inspect-image.d.ts.map +1 -0
- package/dist/internals/inspect-image.js +345 -0
- package/dist/internals/inspect-image.js.map +1 -0
- package/dist/internals/lease-items.d.ts +46 -0
- package/dist/internals/lease-items.d.ts.map +1 -0
- package/dist/internals/lease-items.js +58 -0
- package/dist/internals/lease-items.js.map +1 -0
- package/dist/internals/lease-state.d.ts +32 -0
- package/dist/internals/lease-state.d.ts.map +1 -0
- package/dist/internals/lease-state.js +80 -0
- package/dist/internals/lease-state.js.map +1 -0
- package/dist/internals/render-deployment-plan.d.ts +22 -0
- package/dist/internals/render-deployment-plan.d.ts.map +1 -0
- package/dist/internals/render-deployment-plan.js +135 -0
- package/dist/internals/render-deployment-plan.js.map +1 -0
- package/dist/internals/render-intent-recap.d.ts +43 -0
- package/dist/internals/render-intent-recap.d.ts.map +1 -0
- package/dist/internals/render-intent-recap.js +136 -0
- package/dist/internals/render-intent-recap.js.map +1 -0
- package/dist/internals/render-partial-success-prompt.d.ts +26 -0
- package/dist/internals/render-partial-success-prompt.d.ts.map +1 -0
- package/dist/internals/render-partial-success-prompt.js +53 -0
- package/dist/internals/render-partial-success-prompt.js.map +1 -0
- package/dist/internals/save-manifest.d.ts +105 -0
- package/dist/internals/save-manifest.d.ts.map +1 -0
- package/dist/internals/save-manifest.js +122 -0
- package/dist/internals/save-manifest.js.map +1 -0
- package/dist/internals/secret-denylist.d.ts +42 -0
- package/dist/internals/secret-denylist.d.ts.map +1 -0
- package/dist/internals/secret-denylist.js +59 -0
- package/dist/internals/secret-denylist.js.map +1 -0
- package/dist/internals/spec-normalize.d.ts +84 -0
- package/dist/internals/spec-normalize.d.ts.map +1 -0
- package/dist/internals/spec-normalize.js +169 -0
- package/dist/internals/spec-normalize.js.map +1 -0
- package/dist/internals/verify-domain-state.d.ts +20 -0
- package/dist/internals/verify-domain-state.d.ts.map +1 -0
- package/dist/internals/verify-domain-state.js +63 -0
- package/dist/internals/verify-domain-state.js.map +1 -0
- package/dist/internals/verify-recover.d.ts +120 -0
- package/dist/internals/verify-recover.d.ts.map +1 -0
- package/dist/internals/verify-recover.js +91 -0
- package/dist/internals/verify-recover.js.map +1 -0
- package/dist/manage-domain.d.ts +36 -0
- package/dist/manage-domain.d.ts.map +1 -0
- package/dist/manage-domain.js +230 -0
- package/dist/manage-domain.js.map +1 -0
- package/dist/troubleshoot.d.ts +23 -0
- package/dist/troubleshoot.d.ts.map +1 -0
- package/dist/troubleshoot.js +124 -0
- package/dist/troubleshoot.js.map +1 -0
- package/dist/types.d.ts +294 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +0 -0
- package/package.json +56 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { verifyAndRecover } from "./internals/verify-recover.js";
|
|
2
|
+
import { verifyDomainState } from "./internals/verify-domain-state.js";
|
|
3
|
+
import { ManifestMCPError, ManifestMCPErrorCode, setItemCustomDomain } from "@manifest-network/manifest-mcp-core";
|
|
4
|
+
//#region src/manage-domain.ts
|
|
5
|
+
/**
|
|
6
|
+
* Public entry point: orchestrate setting, clearing, or looking up a
|
|
7
|
+
* lease item's custom domain.
|
|
8
|
+
*
|
|
9
|
+
* Composition (mirrors `deploy-app.ts`'s shape):
|
|
10
|
+
*
|
|
11
|
+
* - `set` / `clear` render a confirmation block, optionally call
|
|
12
|
+
* `onConfirm`, broadcast `setItemCustomDomain` against the agent's
|
|
13
|
+
* bound chain client, then verify the post-broadcast on-chain state
|
|
14
|
+
* via `verifyAndRecover` driving `verify-domain-state` over a direct
|
|
15
|
+
* `billing.v1.lease({ leaseUuid })` single-lease query (tenant-
|
|
16
|
+
* agnostic, no pagination edge cases). Branches are inline closures
|
|
17
|
+
* bound to the per-action context; recovery options are intentionally
|
|
18
|
+
* empty so the verifier surfaces failures via the simple-form
|
|
19
|
+
* `onFailure({ reason })` adapter rather than the rich-form
|
|
20
|
+
* `RecoveryOption[]` prompt (manage-domain has no recovery primitives
|
|
21
|
+
* the orchestrator can dispatch; the user re-runs after a real fix).
|
|
22
|
+
*
|
|
23
|
+
* - `lookup` skips broadcast/verify and resolves the FQDN via the
|
|
24
|
+
* `lease_by_custom_domain` chain query; returns `null` lease when
|
|
25
|
+
* the FQDN isn't claimed.
|
|
26
|
+
*/
|
|
27
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
28
|
+
const FQDN_RE = /^(?=.{1,253}$)(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+[A-Za-z](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$/;
|
|
29
|
+
const SCHEME_PREFIX_RE = /^https?:\/\//i;
|
|
30
|
+
/**
|
|
31
|
+
* Cosmos SDK / gRPC NotFound message patterns. Match against
|
|
32
|
+
* `Error.message` to distinguish chain-keeper NotFound (treated as
|
|
33
|
+
* "unclaimed FQDN" → typed `null` result) from real failures (treated
|
|
34
|
+
* as `QUERY_FAILED` throws). Anchored loose patterns to tolerate
|
|
35
|
+
* different keeper / transport formatting ("not found", "NotFound",
|
|
36
|
+
* "no such record", "does not exist").
|
|
37
|
+
*
|
|
38
|
+
* Per CLAUDE.md: use `String.prototype.match()` over `RegExp.test()`
|
|
39
|
+
* to avoid the CI security hook's false-positive on shell-execution
|
|
40
|
+
* tokens.
|
|
41
|
+
*/
|
|
42
|
+
const NOT_FOUND_RES = [
|
|
43
|
+
/not.?found/i,
|
|
44
|
+
/no.?such/i,
|
|
45
|
+
/does.?not.?exist/i
|
|
46
|
+
];
|
|
47
|
+
/**
|
|
48
|
+
* Set / clear / look up a lease item's custom domain.
|
|
49
|
+
*
|
|
50
|
+
* @throws `ManifestMCPError(INVALID_CONFIG)` for args validation or when
|
|
51
|
+
* `onConfirm` returns `'no'`.
|
|
52
|
+
* @throws `ManifestMCPError` (typically `TX_FAILED`) propagated as-is
|
|
53
|
+
* from the `setItemCustomDomain()` broadcast step in `set` / `clear`
|
|
54
|
+
* paths. Broadcast errors do NOT invoke `onFailure` — that callback
|
|
55
|
+
* is reserved for post-broadcast verification failures.
|
|
56
|
+
* `setItemCustomDomain` already raises a structured `ManifestMCPError`
|
|
57
|
+
* from the core package; wrapping it again at this layer would be
|
|
58
|
+
* redundant. Callers wanting to react to broadcast errors should
|
|
59
|
+
* catch them at the call site.
|
|
60
|
+
* @throws `ManifestMCPError(TX_FAILED)` when post-broadcast verification
|
|
61
|
+
* reaches a `not_found` / `mismatch` outcome (after `onFailure` has
|
|
62
|
+
* been invoked so the caller can react).
|
|
63
|
+
* @throws `ManifestMCPError(QUERY_FAILED)` when a chain query raises a
|
|
64
|
+
* non-NotFound error (RPC / transport / decoding failure). Two paths
|
|
65
|
+
* surface this:
|
|
66
|
+
* - the `lookup` chain query (`lease_by_custom_domain`); the keeper's
|
|
67
|
+
* `NotFound` on an unclaimed FQDN is surfaced as a typed
|
|
68
|
+
* `{ lease: null }` result, not a throw.
|
|
69
|
+
* - the post-broadcast verify chain query (`billing.v1.lease`) in
|
|
70
|
+
* the `set` / `clear` paths (wrapped inside the verifier closure
|
|
71
|
+
* so the failure flows through `onFailure({ reason })` before the
|
|
72
|
+
* throw).
|
|
73
|
+
* Structured `ManifestMCPError`s raised by the chain client are
|
|
74
|
+
* re-thrown as-is (with `onFailure` invoked first).
|
|
75
|
+
*/
|
|
76
|
+
async function manageDomain(args, callbacks, opts) {
|
|
77
|
+
validateArgs(args);
|
|
78
|
+
if (args.action === "lookup") return await lookupDomain(args.fqdn, callbacks, opts);
|
|
79
|
+
const serviceName = args.serviceName;
|
|
80
|
+
const fqdn = args.action === "set" ? args.fqdn.trim() : "";
|
|
81
|
+
const block = renderConfirmationBlock(args);
|
|
82
|
+
if (callbacks.onConfirm) {
|
|
83
|
+
if (await callbacks.onConfirm(block) !== "yes") throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, `User declined to proceed with manage-domain ${args.action}.`);
|
|
84
|
+
}
|
|
85
|
+
callbacks.onProgress?.({ kind: "user_confirmed" });
|
|
86
|
+
const setOpts = args.action === "set" ? serviceName ? { serviceName } : void 0 : {
|
|
87
|
+
clear: true,
|
|
88
|
+
...serviceName ? { serviceName } : {}
|
|
89
|
+
};
|
|
90
|
+
await setItemCustomDomain(opts.clientManager, args.leaseUuid, fqdn, setOpts);
|
|
91
|
+
const verifyResult = await verifyAndRecover({
|
|
92
|
+
verifier: async () => {
|
|
93
|
+
let result;
|
|
94
|
+
try {
|
|
95
|
+
result = await (await opts.clientManager.getQueryClient()).liftedinit.billing.v1.lease({ leaseUuid: args.leaseUuid });
|
|
96
|
+
} catch (err) {
|
|
97
|
+
const reason = `Failed to query lease ${args.leaseUuid} during ${args.action}-verify: ${err instanceof Error ? err.message : String(err)}`;
|
|
98
|
+
if (callbacks.onFailure) await callbacks.onFailure({ reason });
|
|
99
|
+
if (err instanceof ManifestMCPError) throw err;
|
|
100
|
+
throw new ManifestMCPError(ManifestMCPErrorCode.QUERY_FAILED, reason);
|
|
101
|
+
}
|
|
102
|
+
const lease = result?.lease;
|
|
103
|
+
const decoded = verifyDomainState({ leases: lease === null || lease === void 0 ? [] : [lease] }, {
|
|
104
|
+
leaseUuid: args.leaseUuid,
|
|
105
|
+
...serviceName ? { serviceName } : {},
|
|
106
|
+
expected: fqdn
|
|
107
|
+
});
|
|
108
|
+
return {
|
|
109
|
+
outcome: decoded.outcome,
|
|
110
|
+
diagnostic: decoded
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
successValues: ["match"],
|
|
114
|
+
branches: {
|
|
115
|
+
mismatch: {
|
|
116
|
+
branchId: "domain_verification_mismatch",
|
|
117
|
+
journalActionTags: ["domain-verification-mismatch"],
|
|
118
|
+
buildFailureEnvelope: (d) => ({
|
|
119
|
+
outcome: "failed",
|
|
120
|
+
reason: args.action === "set" ? `Chain shows custom_domain="${d.actual ?? ""}" for lease ${args.leaseUuid}; expected "${fqdn}".` : `Chain still shows custom_domain="${d.actual ?? ""}" for lease ${args.leaseUuid}; expected cleared.`
|
|
121
|
+
}),
|
|
122
|
+
buildRecoveryOptions: () => []
|
|
123
|
+
},
|
|
124
|
+
not_found: {
|
|
125
|
+
branchId: "domain_not_found",
|
|
126
|
+
journalActionTags: ["domain-verification-not-found"],
|
|
127
|
+
buildFailureEnvelope: (d) => ({
|
|
128
|
+
outcome: "failed",
|
|
129
|
+
reason: d.reason ?? `Lease ${args.leaseUuid} not found when verifying domain state.`
|
|
130
|
+
}),
|
|
131
|
+
buildRecoveryOptions: () => []
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}, void 0);
|
|
135
|
+
const verified = verifyResult.result === "success";
|
|
136
|
+
const finalCustomDomain = deriveFinalCustomDomain(verifyResult.diagnostic, args.action);
|
|
137
|
+
if (!verified) {
|
|
138
|
+
const reason = verifyResult.failure?.reason ?? `manage-domain ${args.action} verification failed.`;
|
|
139
|
+
if (callbacks.onFailure) await callbacks.onFailure({ reason });
|
|
140
|
+
throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, reason);
|
|
141
|
+
}
|
|
142
|
+
const result = {
|
|
143
|
+
action: args.action,
|
|
144
|
+
leaseUuid: args.leaseUuid,
|
|
145
|
+
verified,
|
|
146
|
+
finalCustomDomain
|
|
147
|
+
};
|
|
148
|
+
callbacks.onComplete?.(result);
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
function validateArgs(args) {
|
|
152
|
+
if (args.action !== "set" && args.action !== "clear" && args.action !== "lookup") throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, `manageDomain: unknown action "${args.action}".`);
|
|
153
|
+
if (args.action === "lookup") {
|
|
154
|
+
if (typeof args.fqdn !== "string" || args.fqdn.trim() === "") throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, "manageDomain lookup: fqdn must be a non-empty string.");
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (typeof args.leaseUuid !== "string" || !args.leaseUuid.match(UUID_RE)) throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, `manageDomain ${args.action}: leaseUuid must be a UUID; got "${args.leaseUuid}".`);
|
|
158
|
+
if (args.action === "set") {
|
|
159
|
+
if (typeof args.fqdn !== "string" || args.fqdn.trim() === "") throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, "manageDomain set: fqdn must be a non-empty string.");
|
|
160
|
+
const candidate = args.fqdn.trim();
|
|
161
|
+
if (candidate.match(SCHEME_PREFIX_RE)) throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, `manageDomain set: fqdn must be a bare hostname (no scheme); got "${args.fqdn}".`);
|
|
162
|
+
if (!candidate.match(FQDN_RE)) throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, `manageDomain set: fqdn "${args.fqdn}" is not a valid RFC 1123 hostname (≤253 chars, ≥2 dot-separated labels of 1-63 alphanumeric/hyphen chars; no leading/trailing hyphens).`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function renderConfirmationBlock(args) {
|
|
166
|
+
const lines = [];
|
|
167
|
+
if (args.action === "set") {
|
|
168
|
+
lines.push(`Set custom domain on lease ${args.leaseUuid}:`);
|
|
169
|
+
lines.push(` FQDN: ${args.fqdn.trim()}`);
|
|
170
|
+
if (args.serviceName) lines.push(` Service: ${args.serviceName}`);
|
|
171
|
+
lines.push("");
|
|
172
|
+
lines.push("Proceed?");
|
|
173
|
+
} else {
|
|
174
|
+
lines.push(`Clear custom domain on lease ${args.leaseUuid}:`);
|
|
175
|
+
if (args.serviceName) lines.push(` Service: ${args.serviceName}`);
|
|
176
|
+
lines.push("");
|
|
177
|
+
lines.push("Proceed?");
|
|
178
|
+
}
|
|
179
|
+
return { text: lines.join("\n") };
|
|
180
|
+
}
|
|
181
|
+
async function lookupDomain(fqdn, callbacks, opts) {
|
|
182
|
+
const customDomain = fqdn.trim();
|
|
183
|
+
let result;
|
|
184
|
+
try {
|
|
185
|
+
result = await (await opts.clientManager.getQueryClient()).liftedinit.billing.v1.leaseByCustomDomain({ customDomain });
|
|
186
|
+
} catch (err) {
|
|
187
|
+
if (isNotFoundError(err)) {
|
|
188
|
+
const notFoundResult = {
|
|
189
|
+
action: "lookup",
|
|
190
|
+
fqdn: customDomain,
|
|
191
|
+
lease: null
|
|
192
|
+
};
|
|
193
|
+
callbacks.onComplete?.(notFoundResult);
|
|
194
|
+
return notFoundResult;
|
|
195
|
+
}
|
|
196
|
+
const reason = `lease_by_custom_domain lookup failed for "${customDomain}": ${err instanceof Error ? err.message : String(err)}`;
|
|
197
|
+
if (callbacks.onFailure) await callbacks.onFailure({ reason });
|
|
198
|
+
if (err instanceof ManifestMCPError) throw err;
|
|
199
|
+
throw new ManifestMCPError(ManifestMCPErrorCode.QUERY_FAILED, reason);
|
|
200
|
+
}
|
|
201
|
+
const uuid = readLeaseUuid(result?.lease);
|
|
202
|
+
const lookupResult = {
|
|
203
|
+
action: "lookup",
|
|
204
|
+
fqdn: customDomain,
|
|
205
|
+
lease: uuid ? { leaseUuid: uuid } : null
|
|
206
|
+
};
|
|
207
|
+
callbacks.onComplete?.(lookupResult);
|
|
208
|
+
return lookupResult;
|
|
209
|
+
}
|
|
210
|
+
function isNotFoundError(err) {
|
|
211
|
+
if (err instanceof ManifestMCPError) return false;
|
|
212
|
+
if (!(err instanceof Error)) return false;
|
|
213
|
+
const msg = err.message;
|
|
214
|
+
return NOT_FOUND_RES.some((re) => msg.match(re) !== null);
|
|
215
|
+
}
|
|
216
|
+
function readLeaseUuid(lease) {
|
|
217
|
+
if (lease === null || typeof lease !== "object") return void 0;
|
|
218
|
+
const r = lease;
|
|
219
|
+
const u = r.uuid ?? r.lease_uuid ?? r.leaseUuid;
|
|
220
|
+
return typeof u === "string" && u.length > 0 ? u : void 0;
|
|
221
|
+
}
|
|
222
|
+
function deriveFinalCustomDomain(diagnostic, action) {
|
|
223
|
+
if (action === "clear") return null;
|
|
224
|
+
const actual = diagnostic.actual ?? "";
|
|
225
|
+
return actual.length > 0 ? actual : null;
|
|
226
|
+
}
|
|
227
|
+
//#endregion
|
|
228
|
+
export { manageDomain };
|
|
229
|
+
|
|
230
|
+
//# sourceMappingURL=manage-domain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manage-domain.js","names":[],"sources":["../src/manage-domain.ts"],"sourcesContent":["/**\n * Public entry point: orchestrate setting, clearing, or looking up a\n * lease item's custom domain.\n *\n * Composition (mirrors `deploy-app.ts`'s shape):\n *\n * - `set` / `clear` render a confirmation block, optionally call\n * `onConfirm`, broadcast `setItemCustomDomain` against the agent's\n * bound chain client, then verify the post-broadcast on-chain state\n * via `verifyAndRecover` driving `verify-domain-state` over a direct\n * `billing.v1.lease({ leaseUuid })` single-lease query (tenant-\n * agnostic, no pagination edge cases). Branches are inline closures\n * bound to the per-action context; recovery options are intentionally\n * empty so the verifier surfaces failures via the simple-form\n * `onFailure({ reason })` adapter rather than the rich-form\n * `RecoveryOption[]` prompt (manage-domain has no recovery primitives\n * the orchestrator can dispatch; the user re-runs after a real fix).\n *\n * - `lookup` skips broadcast/verify and resolves the FQDN via the\n * `lease_by_custom_domain` chain query; returns `null` lease when\n * the FQDN isn't claimed.\n */\n\nimport {\n ManifestMCPError,\n ManifestMCPErrorCode,\n setItemCustomDomain,\n} from '@manifest-network/manifest-mcp-core';\nimport {\n type VerifyDomainOutcome,\n type VerifyDomainResult,\n verifyDomainState,\n} from './internals/verify-domain-state.js';\nimport {\n type VerificationSpec,\n verifyAndRecover,\n} from './internals/verify-recover.js';\nimport type {\n DeploymentPlanBlock,\n ManageDomainArgs,\n ManageDomainCallbacks,\n ManageDomainOptions,\n ManageDomainResult,\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\n// RFC 1123 hostname: each label 1-63 chars, alphanumeric + hyphens, no leading/\n// trailing hyphen; total ≤253 chars; ≥2 labels (FQDN, not single-label host).\n// Rejects scheme prefixes ('http://'), whitespace, trailing dots, and raw\n// unicode (ASCII punycode `xn--...` is accepted — it matches the regex's\n// `[A-Za-z0-9-]` label character class, which is the standard wire form\n// for IDN labels).\n//\n// Client-side typo gate only. The chain's `MsgSetItemCustomDomain` keeper is\n// the authoritative validator (canonical lowercase, reserved-suffix rules,\n// FQDN format). This anchored regex catches the obvious-malformed-input\n// cases pre-broadcast so we don't waste a tx on `\"\"`, `\" \"`, `\"http://x.y\"`,\n// or `\"not a domain\"`. Anything that passes here still goes through the\n// chain's own validation.\nconst FQDN_RE =\n /^(?=.{1,253}$)(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\\.)+[A-Za-z](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$/;\n\nconst SCHEME_PREFIX_RE = /^https?:\\/\\//i;\n\n/**\n * Cosmos SDK / gRPC NotFound message patterns. Match against\n * `Error.message` to distinguish chain-keeper NotFound (treated as\n * \"unclaimed FQDN\" → typed `null` result) from real failures (treated\n * as `QUERY_FAILED` throws). Anchored loose patterns to tolerate\n * different keeper / transport formatting (\"not found\", \"NotFound\",\n * \"no such record\", \"does not exist\").\n *\n * Per CLAUDE.md: use `String.prototype.match()` over `RegExp.test()`\n * to avoid the CI security hook's false-positive on shell-execution\n * tokens.\n */\nconst NOT_FOUND_RES: readonly RegExp[] = [\n /not.?found/i,\n /no.?such/i,\n /does.?not.?exist/i,\n];\n\n/**\n * Set / clear / look up a lease item's custom domain.\n *\n * @throws `ManifestMCPError(INVALID_CONFIG)` for args validation or when\n * `onConfirm` returns `'no'`.\n * @throws `ManifestMCPError` (typically `TX_FAILED`) propagated as-is\n * from the `setItemCustomDomain()` broadcast step in `set` / `clear`\n * paths. Broadcast errors do NOT invoke `onFailure` — that callback\n * is reserved for post-broadcast verification failures.\n * `setItemCustomDomain` already raises a structured `ManifestMCPError`\n * from the core package; wrapping it again at this layer would be\n * redundant. Callers wanting to react to broadcast errors should\n * catch them at the call site.\n * @throws `ManifestMCPError(TX_FAILED)` when post-broadcast verification\n * reaches a `not_found` / `mismatch` outcome (after `onFailure` has\n * been invoked so the caller can react).\n * @throws `ManifestMCPError(QUERY_FAILED)` when a chain query raises a\n * non-NotFound error (RPC / transport / decoding failure). Two paths\n * surface this:\n * - the `lookup` chain query (`lease_by_custom_domain`); the keeper's\n * `NotFound` on an unclaimed FQDN is surfaced as a typed\n * `{ lease: null }` result, not a throw.\n * - the post-broadcast verify chain query (`billing.v1.lease`) in\n * the `set` / `clear` paths (wrapped inside the verifier closure\n * so the failure flows through `onFailure({ reason })` before the\n * throw).\n * Structured `ManifestMCPError`s raised by the chain client are\n * re-thrown as-is (with `onFailure` invoked first).\n */\nexport async function manageDomain(\n args: ManageDomainArgs,\n callbacks: ManageDomainCallbacks,\n opts: ManageDomainOptions,\n): Promise<ManageDomainResult> {\n validateArgs(args);\n\n if (args.action === 'lookup') {\n return await lookupDomain(args.fqdn, callbacks, opts);\n }\n\n const serviceName = args.serviceName;\n // Trim the FQDN silently for `set` (Copilot review PR #60, comment\n // 3276519081): align with the underlying `setItemCustomDomain`\n // primitive (which trims at `packages/core/src/tools/setItemCustomDomain.ts:78-81`)\n // and with `lookupDomain` (which already trims). `validateArgs`\n // continues to reject the empty / whitespace-only case via\n // `args.fqdn.trim() === ''`; this just normalizes the surviving\n // input.\n const fqdn = args.action === 'set' ? args.fqdn.trim() : '';\n\n // --- Confirmation block ---------------------------------------------\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.INVALID_CONFIG,\n `User declined to proceed with manage-domain ${args.action}.`,\n );\n }\n }\n callbacks.onProgress?.({ kind: 'user_confirmed' });\n\n // --- Broadcast ------------------------------------------------------\n const setOpts =\n args.action === 'set'\n ? serviceName\n ? { serviceName }\n : undefined\n : {\n clear: true as const,\n ...(serviceName ? { serviceName } : {}),\n };\n await setItemCustomDomain(opts.clientManager, args.leaseUuid, fqdn, setOpts);\n\n // --- Verify ---------------------------------------------------------\n // Direct single-lease query (Copilot review PR #60, comment 3275999569):\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 //\n // We wrap the single-lease result as `{ leases: [result.lease] }`\n // (or an empty array if the chain returns no match) so\n // `verifyDomainState` stays untouched — its `findLease` walks the\n // same shape, and a `not_found` outcome falls out naturally when the\n // wrapper array is empty.\n const spec: VerificationSpec<\n unknown,\n VerifyDomainOutcome,\n VerifyDomainResult\n > = {\n verifier: async () => {\n // Wrap the chain call in try/catch (Copilot review PR #60,\n // comment 3276419210): 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 ${args.action}-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 const leases = lease === null || lease === undefined ? [] : [lease];\n const decoded = verifyDomainState(\n { leases },\n {\n leaseUuid: args.leaseUuid,\n ...(serviceName ? { serviceName } : {}),\n expected: fqdn,\n },\n );\n return { outcome: decoded.outcome, diagnostic: decoded };\n },\n successValues: ['match'],\n branches: {\n mismatch: {\n branchId: 'domain_verification_mismatch',\n journalActionTags: ['domain-verification-mismatch'],\n buildFailureEnvelope: (d) => ({\n outcome: 'failed',\n reason:\n args.action === 'set'\n ? `Chain shows custom_domain=\"${d.actual ?? ''}\" for lease ${args.leaseUuid}; expected \"${fqdn}\".`\n : `Chain still shows custom_domain=\"${d.actual ?? ''}\" for lease ${args.leaseUuid}; expected cleared.`,\n }),\n buildRecoveryOptions: () => [],\n },\n not_found: {\n branchId: 'domain_not_found',\n journalActionTags: ['domain-verification-not-found'],\n buildFailureEnvelope: (d) => ({\n outcome: 'failed',\n reason:\n d.reason ??\n `Lease ${args.leaseUuid} not found when verifying domain state.`,\n }),\n buildRecoveryOptions: () => [],\n },\n },\n };\n\n const verifyResult = await verifyAndRecover(spec, undefined);\n const verified = verifyResult.result === 'success';\n const finalCustomDomain = deriveFinalCustomDomain(\n verifyResult.diagnostic,\n args.action,\n );\n\n if (!verified) {\n const reason =\n verifyResult.failure?.reason ??\n `manage-domain ${args.action} verification failed.`;\n if (callbacks.onFailure) {\n await callbacks.onFailure({ reason });\n }\n throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, reason);\n }\n\n const result: ManageDomainResult = {\n action: args.action,\n leaseUuid: args.leaseUuid,\n verified,\n finalCustomDomain,\n };\n callbacks.onComplete?.(result);\n return result;\n}\n\n// --- Helpers --------------------------------------------------------\n\nfunction validateArgs(args: ManageDomainArgs): void {\n if (\n args.action !== 'set' &&\n args.action !== 'clear' &&\n args.action !== 'lookup'\n ) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n `manageDomain: unknown action \"${(args as { action?: string }).action}\".`,\n );\n }\n if (args.action === 'lookup') {\n if (typeof args.fqdn !== 'string' || args.fqdn.trim() === '') {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n 'manageDomain lookup: fqdn must be a non-empty string.',\n );\n }\n return;\n }\n if (typeof args.leaseUuid !== 'string' || !args.leaseUuid.match(UUID_RE)) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n `manageDomain ${args.action}: leaseUuid must be a UUID; got \"${args.leaseUuid}\".`,\n );\n }\n if (args.action === 'set') {\n if (typeof args.fqdn !== 'string' || args.fqdn.trim() === '') {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n 'manageDomain set: fqdn must be a non-empty string.',\n );\n }\n // Trim silently (Copilot review PR #60, comment 3276519081):\n // align with `setItemCustomDomain` (which trims at\n // `packages/core/src/tools/setItemCustomDomain.ts:78-81`) and with\n // `lookupDomain` (which already trims). Validation gates below\n // run against the trimmed candidate; the broadcast and confirm\n // block (rendered in the caller) also use the trimmed form.\n const candidate = args.fqdn.trim();\n if (candidate.match(SCHEME_PREFIX_RE)) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n `manageDomain set: fqdn must be a bare hostname (no scheme); got \"${args.fqdn}\".`,\n );\n }\n if (!candidate.match(FQDN_RE)) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n `manageDomain set: fqdn \"${args.fqdn}\" is not a valid RFC 1123 hostname (≤253 chars, ≥2 dot-separated labels of 1-63 alphanumeric/hyphen chars; no leading/trailing hyphens).`,\n );\n }\n }\n}\n\nfunction renderConfirmationBlock(\n args: Exclude<ManageDomainArgs, { action: 'lookup' }>,\n): DeploymentPlanBlock {\n const lines: string[] = [];\n if (args.action === 'set') {\n lines.push(`Set custom domain on lease ${args.leaseUuid}:`);\n // Display the trimmed FQDN — matches the value that will be\n // broadcast (per the silent-trim semantics aligned with\n // setItemCustomDomain) so users don't see whitespace in the\n // confirm prompt that won't appear on-chain.\n lines.push(` FQDN: ${args.fqdn.trim()}`);\n if (args.serviceName) {\n lines.push(` Service: ${args.serviceName}`);\n }\n lines.push('');\n lines.push('Proceed?');\n } else {\n lines.push(`Clear custom domain on lease ${args.leaseUuid}:`);\n if (args.serviceName) {\n lines.push(` Service: ${args.serviceName}`);\n }\n lines.push('');\n lines.push('Proceed?');\n }\n return { text: lines.join('\\n') };\n}\n\nasync function lookupDomain(\n fqdn: string,\n callbacks: ManageDomainCallbacks,\n opts: ManageDomainOptions,\n): Promise<ManageDomainResult> {\n const customDomain = fqdn.trim();\n let result: unknown;\n try {\n // Pull `getQueryClient()` INSIDE the try (Copilot review PR #60,\n // comment 3276719558). `getQueryClient()` can throw\n // `INVALID_CONFIG` (neither rpcUrl nor restUrl set) or\n // `RPC_CONNECTION_FAILED` (connect failure). Catching here routes\n // those init-time failures through the same `onFailure` +\n // QUERY_FAILED / structured-passthrough normalization the chain-\n // query failure mode already gets. The set/clear verifier closure\n // (in `manageDomain` body above) already wraps `getQueryClient()`\n // since commit d9793c1; this brings `lookupDomain` to parity.\n const queryClient = await opts.clientManager.getQueryClient();\n result = await queryClient.liftedinit.billing.v1.leaseByCustomDomain({\n customDomain,\n });\n } catch (err) {\n // Narrowed disambiguation (Copilot review PR #60): the chain keeper\n // raises a NotFound-shaped error when the FQDN is unclaimed (cosmjs/\n // grpc surfaces this as a plain `Error` whose message matches\n // `/not.?found|no.?such|does.?not.?exist/i`). Only that case is\n // collapsed to the typed `{ lease: null }` result. Every other\n // failure mode (RPC transport, decoding, structured\n // `ManifestMCPError`, etc.) flows through `onFailure({ reason })`\n // then a typed throw — matching the lease-package's\n // `lease_by_custom_domain` handler (packages/lease/src/index.ts:442)\n // and `getBalance`'s `catchNotFound` pattern (packages/core/src/\n // tools/getBalance.ts:4). The bare `catch` was masking real failures.\n if (isNotFoundError(err)) {\n const notFoundResult: ManageDomainResult = {\n action: 'lookup',\n fqdn: customDomain,\n lease: null,\n };\n callbacks.onComplete?.(notFoundResult);\n return notFoundResult;\n }\n const reason = `lease_by_custom_domain lookup failed for \"${customDomain}\": ${\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 uuid = readLeaseUuid((result as { lease?: unknown })?.lease);\n const lookupResult: ManageDomainResult = {\n action: 'lookup',\n fqdn: customDomain,\n lease: uuid ? { leaseUuid: uuid } : null,\n };\n // Symmetric `onComplete` fire (Copilot review PR #60, comment\n // 3288656598). Pre-fix, the lookup path returned without invoking\n // `onComplete` — asymmetric vs the set/clear paths in this same\n // function and vs `closeLease` / `troubleshootDeployment`. Was\n // documented as \"intentional\" in PR_DESCRIPTION.md Risks #1 but\n // was really an oversight rationalized post-hoc. Now consistent.\n callbacks.onComplete?.(lookupResult);\n return lookupResult;\n}\n\nfunction isNotFoundError(err: unknown): boolean {\n // Pass-through guard for structured failures: a `ManifestMCPError` is\n // always a real, intentional error — never silently re-classified as\n // \"FQDN unclaimed\" even if its message happens to contain \"not found\".\n if (err instanceof ManifestMCPError) return false;\n if (!(err instanceof Error)) return false;\n const msg = err.message;\n return NOT_FOUND_RES.some((re) => msg.match(re) !== null);\n}\n\nfunction readLeaseUuid(lease: unknown): string | undefined {\n if (lease === null || typeof lease !== 'object') return undefined;\n const r = lease as {\n uuid?: unknown;\n lease_uuid?: unknown;\n leaseUuid?: unknown;\n };\n const u = r.uuid ?? r.lease_uuid ?? r.leaseUuid;\n return typeof u === 'string' && u.length > 0 ? u : undefined;\n}\n\nfunction deriveFinalCustomDomain(\n diagnostic: VerifyDomainResult,\n action: 'set' | 'clear',\n): string | null {\n if (action === 'clear') return null;\n const actual = diagnostic.actual ?? '';\n return actual.length > 0 ? actual : null;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { TroubleshootArgs, TroubleshootCallbacks, TroubleshootOptions, TroubleshootReport } from "./types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/troubleshoot.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Generate a diagnostic markdown report for `args.leaseUuid`.
|
|
6
|
+
*
|
|
7
|
+
* @throws `ManifestMCPError(INVALID_CONFIG)` for args validation.
|
|
8
|
+
* @throws `ManifestMCPError(QUERY_FAILED)` in two cases, both with
|
|
9
|
+
* `onFailure({ reason })` invoked first:
|
|
10
|
+
* - the chain query rejects with a plain `Error` (RPC / transport
|
|
11
|
+
* / decoding failure); or
|
|
12
|
+
* - the chain query succeeds but returns `{ lease: null }` /
|
|
13
|
+
* `undefined` (lease UUID not on-chain — the `billing.v1.lease`
|
|
14
|
+
* no-such-lease response shape).
|
|
15
|
+
* Structured `ManifestMCPError`s raised by the chain client
|
|
16
|
+
* (e.g. `INVALID_CONFIG` from missing rpc/rest url config,
|
|
17
|
+
* `RPC_CONNECTION_FAILED` from connect failure) are re-thrown
|
|
18
|
+
* as-is with their original code, with `onFailure` invoked first.
|
|
19
|
+
*/
|
|
20
|
+
declare function troubleshootDeployment(args: TroubleshootArgs, callbacks: TroubleshootCallbacks, opts: TroubleshootOptions): Promise<TroubleshootReport>;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { troubleshootDeployment };
|
|
23
|
+
//# sourceMappingURL=troubleshoot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"troubleshoot.d.ts","names":[],"sources":["../src/troubleshoot.ts"],"mappings":";;;;;;;;;;;;;;;;;;;iBAgEsB,sBAAA,CACpB,IAAA,EAAM,gBAAA,EACN,SAAA,EAAW,qBAAA,EACX,IAAA,EAAM,mBAAA,GACL,OAAA,CAAQ,kBAAA"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { decode, isTerminal } from "./internals/lease-state.js";
|
|
2
|
+
import { normalizeItem } from "./internals/lease-items.js";
|
|
3
|
+
import { ManifestMCPError, ManifestMCPErrorCode } from "@manifest-network/manifest-mcp-core";
|
|
4
|
+
//#region src/troubleshoot.ts
|
|
5
|
+
/**
|
|
6
|
+
* Public entry point: produce a markdown-formatted diagnostic report
|
|
7
|
+
* for a given lease.
|
|
8
|
+
*
|
|
9
|
+
* Chain-only (no provider HTTP calls — `TroubleshootOptions` has no
|
|
10
|
+
* `walletProvider` for ADR-036 auth). Composes:
|
|
11
|
+
*
|
|
12
|
+
* - `queryClient.liftedinit.billing.v1.lease({ leaseUuid })` for the
|
|
13
|
+
* authoritative chain-side lease record.
|
|
14
|
+
* - `lease-state.decode` + `isTerminal` to translate the integer
|
|
15
|
+
* state into a canonical `LEASE_STATE_*` name and a
|
|
16
|
+
* guidance-routing terminal/non-terminal classification.
|
|
17
|
+
* - `lease-items.normalizeItem` to surface each item's serviceName
|
|
18
|
+
* and customDomain regardless of snake/camelCase payload shape.
|
|
19
|
+
*
|
|
20
|
+
* The returned `markdown` is plain text with markdown formatting — host
|
|
21
|
+
* surfaces can render it in chat directly or embed in a richer
|
|
22
|
+
* diagnostic UI.
|
|
23
|
+
*
|
|
24
|
+
* **Scope:** chain-only. `TroubleshootOptions` carries no `walletProvider`,
|
|
25
|
+
* so provider-side diagnostics (`appStatus` / `getLeaseProvision` /
|
|
26
|
+
* `getAppLogs`) are out of scope for this function. If the report
|
|
27
|
+
* surfaces a recovery-worthy state (e.g. terminal / drift), the caller
|
|
28
|
+
* composes `closeLease()` separately — agent-core's simple-form
|
|
29
|
+
* `onFailure({ reason })` does not carry recovery options, so the
|
|
30
|
+
* orchestration of "report → decide → close" lives at the host surface.
|
|
31
|
+
*/
|
|
32
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
33
|
+
/**
|
|
34
|
+
* Generate a diagnostic markdown report for `args.leaseUuid`.
|
|
35
|
+
*
|
|
36
|
+
* @throws `ManifestMCPError(INVALID_CONFIG)` for args validation.
|
|
37
|
+
* @throws `ManifestMCPError(QUERY_FAILED)` in two cases, both with
|
|
38
|
+
* `onFailure({ reason })` invoked first:
|
|
39
|
+
* - the chain query rejects with a plain `Error` (RPC / transport
|
|
40
|
+
* / decoding failure); or
|
|
41
|
+
* - the chain query succeeds but returns `{ lease: null }` /
|
|
42
|
+
* `undefined` (lease UUID not on-chain — the `billing.v1.lease`
|
|
43
|
+
* no-such-lease response shape).
|
|
44
|
+
* Structured `ManifestMCPError`s raised by the chain client
|
|
45
|
+
* (e.g. `INVALID_CONFIG` from missing rpc/rest url config,
|
|
46
|
+
* `RPC_CONNECTION_FAILED` from connect failure) are re-thrown
|
|
47
|
+
* as-is with their original code, with `onFailure` invoked first.
|
|
48
|
+
*/
|
|
49
|
+
async function troubleshootDeployment(args, callbacks, opts) {
|
|
50
|
+
validateArgs(args);
|
|
51
|
+
let leasePayload;
|
|
52
|
+
try {
|
|
53
|
+
leasePayload = (await (await opts.clientManager.getQueryClient()).liftedinit.billing.v1.lease({ leaseUuid: args.leaseUuid })).lease;
|
|
54
|
+
} catch (err) {
|
|
55
|
+
const reason = `Failed to query lease ${args.leaseUuid}: ${err instanceof Error ? err.message : String(err)}`;
|
|
56
|
+
if (callbacks.onFailure) await callbacks.onFailure({ reason });
|
|
57
|
+
if (err instanceof ManifestMCPError) throw err;
|
|
58
|
+
throw new ManifestMCPError(ManifestMCPErrorCode.QUERY_FAILED, reason);
|
|
59
|
+
}
|
|
60
|
+
if (leasePayload === null || leasePayload === void 0) {
|
|
61
|
+
const reason = `Lease ${args.leaseUuid} not found on chain.`;
|
|
62
|
+
if (callbacks.onFailure) await callbacks.onFailure({ reason });
|
|
63
|
+
throw new ManifestMCPError(ManifestMCPErrorCode.QUERY_FAILED, reason);
|
|
64
|
+
}
|
|
65
|
+
const report = { markdown: renderReport(args.leaseUuid, leasePayload) };
|
|
66
|
+
callbacks.onComplete?.(report);
|
|
67
|
+
return report;
|
|
68
|
+
}
|
|
69
|
+
function validateArgs(args) {
|
|
70
|
+
if (typeof args.leaseUuid !== "string" || !args.leaseUuid.match(UUID_RE)) throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, `troubleshootDeployment: leaseUuid must be a UUID; got "${args.leaseUuid}".`);
|
|
71
|
+
}
|
|
72
|
+
function renderReport(leaseUuid, lease) {
|
|
73
|
+
const l = lease ?? {};
|
|
74
|
+
const rawState = l.state;
|
|
75
|
+
const stateName = decode(typeof rawState === "number" || typeof rawState === "string" ? rawState : void 0);
|
|
76
|
+
const stateLabel = stateName ?? `UNKNOWN(${String(rawState)})`;
|
|
77
|
+
const providerUuid = readString(l.providerUuid) || readString(l.provider_uuid) || "(unknown)";
|
|
78
|
+
const createdAt = readTimestamp(l.createdAt) ?? readTimestamp(l.created_at);
|
|
79
|
+
const closedAt = readTimestamp(l.closedAt) ?? readTimestamp(l.closed_at);
|
|
80
|
+
const items = (Array.isArray(l.items) ? l.items : []).map(normalizeItem);
|
|
81
|
+
const lines = [];
|
|
82
|
+
lines.push(`# Lease diagnostic — ${leaseUuid}`);
|
|
83
|
+
lines.push("");
|
|
84
|
+
lines.push("## Chain state");
|
|
85
|
+
lines.push("");
|
|
86
|
+
lines.push(`- **State:** ${stateLabel}`);
|
|
87
|
+
lines.push(`- **Provider:** ${providerUuid}`);
|
|
88
|
+
if (createdAt) lines.push(`- **Created:** ${createdAt}`);
|
|
89
|
+
if (closedAt) lines.push(`- **Closed:** ${closedAt}`);
|
|
90
|
+
lines.push("");
|
|
91
|
+
lines.push("## Items");
|
|
92
|
+
lines.push("");
|
|
93
|
+
if (items.length === 0) lines.push("_No items found on this lease._");
|
|
94
|
+
else for (const item of items) {
|
|
95
|
+
const svc = item.serviceName.length > 0 ? item.serviceName : "(default)";
|
|
96
|
+
const dom = item.customDomain.length > 0 ? item.customDomain : "(no custom domain)";
|
|
97
|
+
lines.push(`- **${svc}** → ${dom}`);
|
|
98
|
+
}
|
|
99
|
+
lines.push("");
|
|
100
|
+
lines.push("## Guidance");
|
|
101
|
+
lines.push("");
|
|
102
|
+
for (const tip of guidanceFor(stateName)) lines.push(`- ${tip}`);
|
|
103
|
+
return lines.join("\n");
|
|
104
|
+
}
|
|
105
|
+
function guidanceFor(state) {
|
|
106
|
+
if (state === void 0) return ["Lease state could not be decoded. Re-query in a moment, or check the chain client logs for transport errors."];
|
|
107
|
+
if (isTerminal(state)) return [`Lease is in terminal state \`${state}\`. No further provider activity expected.`, "To redeploy, create a new lease via `deployApp`."];
|
|
108
|
+
switch (state) {
|
|
109
|
+
case "LEASE_STATE_PENDING": return ["Lease is awaiting provider acknowledgement.", "If pending persists for more than a few minutes, the provider may be offline; consider closing and redeploying."];
|
|
110
|
+
case "LEASE_STATE_ACTIVE": return ["Lease is active on the provider. App-level status / logs require a provider HTTP call with an ADR-036 auth token (out of scope for this chain-only diagnostic)."];
|
|
111
|
+
default: return [`Lease state is \`${state}\`. Review the chain proto for the expected next transition.`];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function readString(value) {
|
|
115
|
+
return typeof value === "string" ? value : "";
|
|
116
|
+
}
|
|
117
|
+
function readTimestamp(value) {
|
|
118
|
+
if (value instanceof Date) return value.toISOString();
|
|
119
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
export { troubleshootDeployment };
|
|
123
|
+
|
|
124
|
+
//# sourceMappingURL=troubleshoot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"troubleshoot.js","names":["decodeLeaseState"],"sources":["../src/troubleshoot.ts"],"sourcesContent":["/**\n * Public entry point: produce a markdown-formatted diagnostic report\n * for a given lease.\n *\n * Chain-only (no provider HTTP calls — `TroubleshootOptions` has no\n * `walletProvider` for ADR-036 auth). Composes:\n *\n * - `queryClient.liftedinit.billing.v1.lease({ leaseUuid })` for the\n * authoritative chain-side lease record.\n * - `lease-state.decode` + `isTerminal` to translate the integer\n * state into a canonical `LEASE_STATE_*` name and a\n * guidance-routing terminal/non-terminal classification.\n * - `lease-items.normalizeItem` to surface each item's serviceName\n * and customDomain regardless of snake/camelCase payload shape.\n *\n * The returned `markdown` is plain text with markdown formatting — host\n * surfaces can render it in chat directly or embed in a richer\n * diagnostic UI.\n *\n * **Scope:** chain-only. `TroubleshootOptions` carries no `walletProvider`,\n * so provider-side diagnostics (`appStatus` / `getLeaseProvision` /\n * `getAppLogs`) are out of scope for this function. If the report\n * surfaces a recovery-worthy state (e.g. terminal / drift), the caller\n * composes `closeLease()` separately — agent-core's simple-form\n * `onFailure({ reason })` does not carry recovery options, so the\n * orchestration of \"report → decide → close\" lives at the host surface.\n */\n\nimport {\n ManifestMCPError,\n ManifestMCPErrorCode,\n} from '@manifest-network/manifest-mcp-core';\nimport { normalizeItem } from './internals/lease-items.js';\nimport {\n decode as decodeLeaseState,\n isTerminal,\n} from './internals/lease-state.js';\nimport type {\n LeaseStateName,\n TroubleshootArgs,\n TroubleshootCallbacks,\n TroubleshootOptions,\n TroubleshootReport,\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\n/**\n * Generate a diagnostic markdown report for `args.leaseUuid`.\n *\n * @throws `ManifestMCPError(INVALID_CONFIG)` for args validation.\n * @throws `ManifestMCPError(QUERY_FAILED)` in two cases, both with\n * `onFailure({ reason })` invoked first:\n * - the chain query rejects with a plain `Error` (RPC / transport\n * / decoding failure); or\n * - the chain query succeeds but returns `{ lease: null }` /\n * `undefined` (lease UUID not on-chain — the `billing.v1.lease`\n * no-such-lease response shape).\n * Structured `ManifestMCPError`s raised by the chain client\n * (e.g. `INVALID_CONFIG` from missing rpc/rest url config,\n * `RPC_CONNECTION_FAILED` from connect failure) are re-thrown\n * as-is with their original code, with `onFailure` invoked first.\n */\nexport async function troubleshootDeployment(\n args: TroubleshootArgs,\n callbacks: TroubleshootCallbacks,\n opts: TroubleshootOptions,\n): Promise<TroubleshootReport> {\n validateArgs(args);\n\n let leasePayload: unknown;\n try {\n // Pull `getQueryClient()` INSIDE the try (Copilot review PR #60,\n // comment 3276719462). `getQueryClient()` can throw\n // `INVALID_CONFIG` (neither rpcUrl nor restUrl set) or\n // `RPC_CONNECTION_FAILED` (connect failure). Catching here routes\n // those init-time failures through the same `onFailure` +\n // QUERY_FAILED / structured-passthrough normalization the chain-\n // query failure mode already gets — three modes, one disambiguation.\n const queryClient = await opts.clientManager.getQueryClient();\n const result = await queryClient.liftedinit.billing.v1.lease({\n leaseUuid: args.leaseUuid,\n });\n leasePayload = result.lease;\n } catch (err) {\n // Preserve structured `ManifestMCPError`s from the chain client\n // (Copilot review PR #60, comment 3276172289). Wrapping every\n // failure as `QUERY_FAILED` erases upstream error codes — a real\n // `INVALID_CONFIG` from the chain layer should surface to callers\n // with that code, not be collapsed to a less-specific category.\n // Mirrors the disambiguation `manage-domain.ts:lookupDomain`\n // adopted in commit aaa5cc5. Note: chain-NotFound for\n // `billing.v1.lease({ leaseUuid })` returns `{ lease: null }`\n // (handled below), so errors landing here are genuinely transport\n // or structured failures.\n const reason = `Failed to query lease ${args.leaseUuid}: ${err instanceof Error ? err.message : String(err)}`;\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\n if (leasePayload === null || leasePayload === undefined) {\n const reason = `Lease ${args.leaseUuid} not found on chain.`;\n if (callbacks.onFailure) {\n await callbacks.onFailure({ reason });\n }\n throw new ManifestMCPError(ManifestMCPErrorCode.QUERY_FAILED, reason);\n }\n\n const markdown = renderReport(args.leaseUuid, leasePayload);\n const report: TroubleshootReport = { markdown };\n callbacks.onComplete?.(report);\n return report;\n}\n\n// --- Helpers --------------------------------------------------------\n\nfunction validateArgs(args: TroubleshootArgs): void {\n if (typeof args.leaseUuid !== 'string' || !args.leaseUuid.match(UUID_RE)) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n `troubleshootDeployment: leaseUuid must be a UUID; got \"${args.leaseUuid}\".`,\n );\n }\n}\n\ninterface LeaseShape {\n uuid?: unknown;\n state?: unknown;\n providerUuid?: unknown;\n provider_uuid?: unknown;\n createdAt?: unknown;\n created_at?: unknown;\n closedAt?: unknown;\n closed_at?: unknown;\n items?: unknown;\n}\n\nfunction renderReport(leaseUuid: string, lease: unknown): string {\n const l = (lease ?? {}) as LeaseShape;\n const rawState = l.state;\n const stateName = decodeLeaseState(\n typeof rawState === 'number' || typeof rawState === 'string'\n ? rawState\n : undefined,\n );\n const stateLabel = stateName ?? `UNKNOWN(${String(rawState)})`;\n const providerUuid =\n readString(l.providerUuid) || readString(l.provider_uuid) || '(unknown)';\n const createdAt = readTimestamp(l.createdAt) ?? readTimestamp(l.created_at);\n const closedAt = readTimestamp(l.closedAt) ?? readTimestamp(l.closed_at);\n\n const rawItems = Array.isArray(l.items) ? l.items : [];\n const items = rawItems.map(normalizeItem);\n\n const lines: string[] = [];\n lines.push(`# Lease diagnostic — ${leaseUuid}`);\n lines.push('');\n lines.push('## Chain state');\n lines.push('');\n lines.push(`- **State:** ${stateLabel}`);\n lines.push(`- **Provider:** ${providerUuid}`);\n if (createdAt) lines.push(`- **Created:** ${createdAt}`);\n if (closedAt) lines.push(`- **Closed:** ${closedAt}`);\n lines.push('');\n\n lines.push('## Items');\n lines.push('');\n if (items.length === 0) {\n lines.push('_No items found on this lease._');\n } else {\n for (const item of items) {\n const svc = item.serviceName.length > 0 ? item.serviceName : '(default)';\n const dom =\n item.customDomain.length > 0 ? item.customDomain : '(no custom domain)';\n lines.push(`- **${svc}** → ${dom}`);\n }\n }\n lines.push('');\n\n lines.push('## Guidance');\n lines.push('');\n for (const tip of guidanceFor(stateName)) {\n lines.push(`- ${tip}`);\n }\n\n return lines.join('\\n');\n}\n\nfunction guidanceFor(state: LeaseStateName | undefined): string[] {\n if (state === undefined) {\n return [\n 'Lease state could not be decoded. Re-query in a moment, or check the chain client logs for transport errors.',\n ];\n }\n if (isTerminal(state)) {\n return [\n `Lease is in terminal state \\`${state}\\`. No further provider activity expected.`,\n 'To redeploy, create a new lease via `deployApp`.',\n ];\n }\n switch (state) {\n case 'LEASE_STATE_PENDING':\n return [\n 'Lease is awaiting provider acknowledgement.',\n 'If pending persists for more than a few minutes, the provider may be offline; consider closing and redeploying.',\n ];\n case 'LEASE_STATE_ACTIVE':\n return [\n 'Lease is active on the provider. App-level status / logs require a provider HTTP call with an ADR-036 auth token (out of scope for this chain-only diagnostic).',\n ];\n default:\n return [\n `Lease state is \\`${state}\\`. Review the chain proto for the expected next transition.`,\n ];\n }\n}\n\nfunction readString(value: unknown): string {\n return typeof value === 'string' ? value : '';\n}\n\nfunction readTimestamp(value: unknown): string | undefined {\n if (value instanceof Date) return value.toISOString();\n if (typeof value === 'string' && value.length > 0) return value;\n return undefined;\n}\n"],"mappings":"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"}
|