@manifest-network/manifest-mcp-fred 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/http/auth.d.ts +15 -0
  2. package/dist/http/auth.d.ts.map +1 -0
  3. package/dist/http/auth.js +23 -0
  4. package/dist/http/auth.js.map +1 -0
  5. package/dist/http/fred.d.ts +74 -0
  6. package/dist/http/fred.d.ts.map +1 -0
  7. package/dist/http/fred.js +94 -0
  8. package/dist/http/fred.js.map +1 -0
  9. package/dist/http/provider.d.ts +52 -0
  10. package/dist/http/provider.d.ts.map +1 -0
  11. package/dist/http/provider.js +88 -0
  12. package/dist/http/provider.js.map +1 -0
  13. package/dist/index.d.ts +33 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +211 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/manifest.d.ts +44 -0
  18. package/dist/manifest.d.ts.map +1 -0
  19. package/dist/manifest.js +122 -0
  20. package/dist/manifest.js.map +1 -0
  21. package/dist/tools/appStatus.d.ts +21 -0
  22. package/dist/tools/appStatus.d.ts.map +1 -0
  23. package/dist/tools/appStatus.js +73 -0
  24. package/dist/tools/appStatus.js.map +1 -0
  25. package/dist/tools/browseCatalog.d.ts +27 -0
  26. package/dist/tools/browseCatalog.d.ts.map +1 -0
  27. package/dist/tools/browseCatalog.js +76 -0
  28. package/dist/tools/browseCatalog.js.map +1 -0
  29. package/dist/tools/deployApp.d.ts +65 -0
  30. package/dist/tools/deployApp.d.ts.map +1 -0
  31. package/dist/tools/deployApp.js +143 -0
  32. package/dist/tools/deployApp.js.map +1 -0
  33. package/dist/tools/fetchActiveLease.d.ts +12 -0
  34. package/dist/tools/fetchActiveLease.d.ts.map +1 -0
  35. package/dist/tools/fetchActiveLease.js +17 -0
  36. package/dist/tools/fetchActiveLease.js.map +1 -0
  37. package/dist/tools/getLogs.d.ts +11 -0
  38. package/dist/tools/getLogs.d.ts.map +1 -0
  39. package/dist/tools/getLogs.js +32 -0
  40. package/dist/tools/getLogs.js.map +1 -0
  41. package/dist/tools/resolveLeaseProvider.d.ts +7 -0
  42. package/dist/tools/resolveLeaseProvider.d.ts.map +1 -0
  43. package/dist/tools/resolveLeaseProvider.js +20 -0
  44. package/dist/tools/resolveLeaseProvider.js.map +1 -0
  45. package/dist/tools/restartApp.d.ts +10 -0
  46. package/dist/tools/restartApp.d.ts.map +1 -0
  47. package/dist/tools/restartApp.js +14 -0
  48. package/dist/tools/restartApp.js.map +1 -0
  49. package/dist/tools/updateApp.d.ts +10 -0
  50. package/dist/tools/updateApp.d.ts.map +1 -0
  51. package/dist/tools/updateApp.js +47 -0
  52. package/dist/tools/updateApp.js.map +1 -0
  53. package/package.json +59 -0
@@ -0,0 +1,76 @@
1
+ import { ProviderApiError, getProviderHealth } from "../http/provider.js";
2
+ import { INFRASTRUCTURE_ERROR_CODES, MAX_PAGE_LIMIT, ManifestMCPError, createPagination } from "@manifest-network/manifest-mcp-core";
3
+ //#region src/tools/browseCatalog.ts
4
+ /** Maximum concurrent outgoing health check requests to provider APIs */
5
+ const MAX_CONCURRENT_HEALTH_CHECKS = 5;
6
+ /**
7
+ * Run an array of async functions with a concurrency limit.
8
+ * Returns results in the same order as the input.
9
+ */
10
+ async function mapWithConcurrency(items, limit, fn) {
11
+ const results = new Array(items.length);
12
+ let nextIndex = 0;
13
+ async function worker() {
14
+ while (nextIndex < items.length) {
15
+ const idx = nextIndex++;
16
+ results[idx] = await fn(items[idx]);
17
+ }
18
+ }
19
+ const workerCount = Math.min(Math.max(limit, 1), items.length);
20
+ const workers = Array.from({ length: workerCount }, () => worker());
21
+ await Promise.all(workers);
22
+ return results;
23
+ }
24
+ async function browseCatalog(queryClient, fetchFn) {
25
+ const sku = queryClient.liftedinit.sku.v1;
26
+ const pagination = createPagination(MAX_PAGE_LIMIT);
27
+ const [providersResult, skusResult] = await Promise.all([sku.providers({
28
+ activeOnly: true,
29
+ pagination
30
+ }), sku.sKUs({
31
+ activeOnly: true,
32
+ pagination
33
+ })]);
34
+ const providers = await mapWithConcurrency(providersResult.providers, MAX_CONCURRENT_HEALTH_CHECKS, async (p) => {
35
+ let healthy = false;
36
+ let providerUuid;
37
+ let healthError;
38
+ try {
39
+ const health = await getProviderHealth(p.apiUrl, void 0, fetchFn);
40
+ healthy = health.status === "ok" || health.status === "healthy";
41
+ providerUuid = health.provider_uuid;
42
+ } catch (err) {
43
+ if (err instanceof ManifestMCPError && INFRASTRUCTURE_ERROR_CODES.has(err.code)) throw err;
44
+ if (err instanceof ProviderApiError) healthError = `HTTP ${err.status}: ${err.message}`;
45
+ else healthError = `Health check failed: ${err instanceof Error ? err.message : String(err)}`;
46
+ }
47
+ return {
48
+ uuid: p.uuid,
49
+ address: p.address,
50
+ apiUrl: p.apiUrl,
51
+ active: p.active,
52
+ healthy,
53
+ providerUuid,
54
+ ...healthError && { healthError }
55
+ };
56
+ });
57
+ const providerByUuid = new Map(providersResult.providers.map((p) => [p.uuid, p]));
58
+ const tiers = {};
59
+ for (const s of skusResult.skus) {
60
+ const entry = {
61
+ provider: providerByUuid.get(s.providerUuid)?.apiUrl ?? s.providerUuid,
62
+ price: s.basePrice?.amount ?? null,
63
+ unit: s.basePrice?.denom ?? null
64
+ };
65
+ if (!tiers[s.name]) tiers[s.name] = [];
66
+ tiers[s.name].push(entry);
67
+ }
68
+ return {
69
+ providers,
70
+ tiers
71
+ };
72
+ }
73
+ //#endregion
74
+ export { browseCatalog, mapWithConcurrency };
75
+
76
+ //# sourceMappingURL=browseCatalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browseCatalog.js","names":[],"sources":["../../src/tools/browseCatalog.ts"],"sourcesContent":["import type { ManifestQueryClient } from '@manifest-network/manifest-mcp-core';\nimport {\n createPagination,\n INFRASTRUCTURE_ERROR_CODES,\n MAX_PAGE_LIMIT,\n ManifestMCPError,\n} from '@manifest-network/manifest-mcp-core';\nimport { getProviderHealth, ProviderApiError } from '../http/provider.js';\n\n/** Maximum concurrent outgoing health check requests to provider APIs */\nconst MAX_CONCURRENT_HEALTH_CHECKS = 5;\n\n/**\n * Run an array of async functions with a concurrency limit.\n * Returns results in the same order as the input.\n */\nexport async function mapWithConcurrency<T, R>(\n items: T[],\n limit: number,\n fn: (item: T) => Promise<R>,\n): Promise<R[]> {\n const results: R[] = new Array(items.length);\n let nextIndex = 0;\n\n async function worker(): Promise<void> {\n while (nextIndex < items.length) {\n const idx = nextIndex++;\n results[idx] = await fn(items[idx]);\n }\n }\n\n const workerCount = Math.min(Math.max(limit, 1), items.length);\n const workers = Array.from({ length: workerCount }, () => worker());\n await Promise.all(workers);\n return results;\n}\n\nexport async function browseCatalog(\n queryClient: ManifestQueryClient,\n fetchFn?: typeof globalThis.fetch,\n) {\n const sku = queryClient.liftedinit.sku.v1;\n\n const pagination = createPagination(MAX_PAGE_LIMIT);\n\n const [providersResult, skusResult] = await Promise.all([\n sku.providers({ activeOnly: true, pagination }),\n sku.sKUs({ activeOnly: true, pagination }),\n ]);\n\n const providers = await mapWithConcurrency(\n providersResult.providers,\n MAX_CONCURRENT_HEALTH_CHECKS,\n async (p) => {\n let healthy = false;\n let providerUuid: string | undefined;\n let healthError: string | undefined;\n try {\n const health = await getProviderHealth(p.apiUrl, undefined, fetchFn);\n healthy = health.status === 'ok' || health.status === 'healthy';\n providerUuid = health.provider_uuid;\n } catch (err) {\n if (\n err instanceof ManifestMCPError &&\n INFRASTRUCTURE_ERROR_CODES.has(err.code)\n )\n throw err;\n if (err instanceof ProviderApiError) {\n healthError = `HTTP ${err.status}: ${err.message}`;\n } else {\n healthError = `Health check failed: ${err instanceof Error ? err.message : String(err)}`;\n }\n }\n return {\n uuid: p.uuid,\n address: p.address,\n apiUrl: p.apiUrl,\n active: p.active,\n healthy,\n providerUuid,\n ...(healthError && { healthError }),\n };\n },\n );\n\n const providerByUuid = new Map(\n providersResult.providers.map((p) => [p.uuid, p]),\n );\n\n const tiers: Record<\n string,\n Array<{ provider: string; price: string | null; unit: string | null }>\n > = {};\n for (const s of skusResult.skus) {\n const provider = providerByUuid.get(s.providerUuid);\n const entry = {\n provider: provider?.apiUrl ?? s.providerUuid,\n price: s.basePrice?.amount ?? null,\n unit: s.basePrice?.denom ?? null,\n };\n if (!tiers[s.name]) {\n tiers[s.name] = [];\n }\n tiers[s.name].push(entry);\n }\n\n return { providers, tiers };\n}\n"],"mappings":";;;;AAUA,MAAM,+BAA+B;;;;;AAMrC,eAAsB,mBACpB,OACA,OACA,IACc;CACd,MAAM,UAAe,IAAI,MAAM,MAAM,OAAO;CAC5C,IAAI,YAAY;CAEhB,eAAe,SAAwB;AACrC,SAAO,YAAY,MAAM,QAAQ;GAC/B,MAAM,MAAM;AACZ,WAAQ,OAAO,MAAM,GAAG,MAAM,KAAK;;;CAIvC,MAAM,cAAc,KAAK,IAAI,KAAK,IAAI,OAAO,EAAE,EAAE,MAAM,OAAO;CAC9D,MAAM,UAAU,MAAM,KAAK,EAAE,QAAQ,aAAa,QAAQ,QAAQ,CAAC;AACnE,OAAM,QAAQ,IAAI,QAAQ;AAC1B,QAAO;;AAGT,eAAsB,cACpB,aACA,SACA;CACA,MAAM,MAAM,YAAY,WAAW,IAAI;CAEvC,MAAM,aAAa,iBAAiB,eAAe;CAEnD,MAAM,CAAC,iBAAiB,cAAc,MAAM,QAAQ,IAAI,CACtD,IAAI,UAAU;EAAE,YAAY;EAAM;EAAY,CAAC,EAC/C,IAAI,KAAK;EAAE,YAAY;EAAM;EAAY,CAAC,CAC3C,CAAC;CAEF,MAAM,YAAY,MAAM,mBACtB,gBAAgB,WAChB,8BACA,OAAO,MAAM;EACX,IAAI,UAAU;EACd,IAAI;EACJ,IAAI;AACJ,MAAI;GACF,MAAM,SAAS,MAAM,kBAAkB,EAAE,QAAQ,KAAA,GAAW,QAAQ;AACpE,aAAU,OAAO,WAAW,QAAQ,OAAO,WAAW;AACtD,kBAAe,OAAO;WACf,KAAK;AACZ,OACE,eAAe,oBACf,2BAA2B,IAAI,IAAI,KAAK,CAExC,OAAM;AACR,OAAI,eAAe,iBACjB,eAAc,QAAQ,IAAI,OAAO,IAAI,IAAI;OAEzC,eAAc,wBAAwB,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;;AAG1F,SAAO;GACL,MAAM,EAAE;GACR,SAAS,EAAE;GACX,QAAQ,EAAE;GACV,QAAQ,EAAE;GACV;GACA;GACA,GAAI,eAAe,EAAE,aAAa;GACnC;GAEJ;CAED,MAAM,iBAAiB,IAAI,IACzB,gBAAgB,UAAU,KAAK,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAClD;CAED,MAAM,QAGF,EAAE;AACN,MAAK,MAAM,KAAK,WAAW,MAAM;EAE/B,MAAM,QAAQ;GACZ,UAFe,eAAe,IAAI,EAAE,aAAa,EAE7B,UAAU,EAAE;GAChC,OAAO,EAAE,WAAW,UAAU;GAC9B,MAAM,EAAE,WAAW,SAAS;GAC7B;AACD,MAAI,CAAC,MAAM,EAAE,MACX,OAAM,EAAE,QAAQ,EAAE;AAEpB,QAAM,EAAE,MAAM,KAAK,MAAM;;AAG3B,QAAO;EAAE;EAAW;EAAO"}
@@ -0,0 +1,65 @@
1
+ import { ConnectionDetails } from "../http/provider.js";
2
+ import { CosmosClientManager, LeaseState } from "@manifest-network/manifest-mcp-core";
3
+
4
+ //#region src/tools/deployApp.d.ts
5
+ interface ServiceConfig {
6
+ image: string;
7
+ ports?: Record<string, Record<string, never>>;
8
+ env?: Record<string, string>;
9
+ command?: string[];
10
+ args?: string[];
11
+ user?: string;
12
+ tmpfs?: string[];
13
+ health_check?: {
14
+ test: string[];
15
+ interval?: string;
16
+ timeout?: string;
17
+ retries?: number;
18
+ start_period?: string;
19
+ };
20
+ stop_grace_period?: string;
21
+ depends_on?: Record<string, {
22
+ condition: string;
23
+ }>;
24
+ expose?: string[];
25
+ labels?: Record<string, string>;
26
+ }
27
+ interface DeployAppInput {
28
+ image?: string;
29
+ port?: number;
30
+ size: string;
31
+ env?: Record<string, string>;
32
+ command?: string[];
33
+ args?: string[];
34
+ user?: string;
35
+ tmpfs?: string[];
36
+ health_check?: {
37
+ test: string[];
38
+ interval?: string;
39
+ timeout?: string;
40
+ retries?: number;
41
+ start_period?: string;
42
+ };
43
+ stop_grace_period?: string;
44
+ init?: boolean;
45
+ expose?: string[];
46
+ labels?: Record<string, string>;
47
+ storage?: string;
48
+ depends_on?: Record<string, {
49
+ condition: string;
50
+ }>;
51
+ services?: Record<string, ServiceConfig>;
52
+ }
53
+ interface DeployAppResult {
54
+ readonly lease_uuid: string;
55
+ readonly provider_uuid: string;
56
+ readonly provider_url: string;
57
+ readonly state: LeaseState;
58
+ readonly url?: string;
59
+ readonly connection?: ConnectionDetails;
60
+ readonly connectionError?: string;
61
+ }
62
+ declare function deployApp(clientManager: CosmosClientManager, getAuthToken: (address: string, leaseUuid: string) => Promise<string>, getLeaseDataAuthToken: (address: string, leaseUuid: string, metaHashHex: string) => Promise<string>, input: DeployAppInput, fetchFn?: typeof globalThis.fetch): Promise<DeployAppResult>;
63
+ //#endregion
64
+ export { DeployAppInput, DeployAppResult, ServiceConfig, deployApp };
65
+ //# sourceMappingURL=deployApp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deployApp.d.ts","names":[],"sources":["../../src/tools/deployApp.ts"],"mappings":";;;;UA6FiB,aAAA;EACf,KAAA;EACA,KAAA,GAAQ,MAAA,SAAe,MAAA;EACvB,GAAA,GAAM,MAAA;EACN,OAAA;EACA,IAAA;EACA,IAAA;EACA,KAAA;EACA,YAAA;IACE,IAAA;IACA,QAAA;IACA,OAAA;IACA,OAAA;IACA,YAAA;EAAA;EAEF,iBAAA;EACA,UAAA,GAAa,MAAA;IAAiB,SAAA;EAAA;EAC9B,MAAA;EACA,MAAA,GAAS,MAAA;AAAA;AAAA,UAGM,cAAA;EACf,KAAA;EACA,IAAA;EACA,IAAA;EACA,GAAA,GAAM,MAAA;EACN,OAAA;EACA,IAAA;EACA,IAAA;EACA,KAAA;EACA,YAAA;IACE,IAAA;IACA,QAAA;IACA,OAAA;IACA,OAAA;IACA,YAAA;EAAA;EAEF,iBAAA;EACA,IAAA;EACA,MAAA;EACA,MAAA,GAAS,MAAA;EACT,OAAA;EACA,UAAA,GAAa,MAAA;IAAiB,SAAA;EAAA;EAC9B,QAAA,GAAW,MAAA,SAAe,aAAA;AAAA;AAAA,UAGX,eAAA;EAAA,SACN,UAAA;EAAA,SACA,aAAA;EAAA,SACA,YAAA;EAAA,SACA,KAAA,EAAO,UAAA;EAAA,SACP,GAAA;EAAA,SACA,UAAA,GAAa,iBAAA;EAAA,SACb,eAAA;AAAA;AAAA,iBAGW,SAAA,CACpB,aAAA,EAAe,mBAAA,EACf,YAAA,GAAe,OAAA,UAAiB,SAAA,aAAsB,OAAA,UACtD,qBAAA,GACE,OAAA,UACA,SAAA,UACA,WAAA,aACG,OAAA,UACL,KAAA,EAAO,cAAA,EACP,OAAA,UAAiB,UAAA,CAAW,KAAA,GAC3B,OAAA,CAAQ,eAAA"}
@@ -0,0 +1,143 @@
1
+ import { getLeaseConnectionInfo, uploadLeaseData } from "../http/provider.js";
2
+ import { pollLeaseUntilReady } from "../http/fred.js";
3
+ import { resolveProviderUrl } from "./resolveLeaseProvider.js";
4
+ import { buildManifest, buildStackManifest, validateServiceName } from "../manifest.js";
5
+ import { MAX_PAGE_LIMIT, ManifestMCPError, ManifestMCPErrorCode, cosmosTx, createPagination, logger, requireUuid, sanitizeForLogging } from "@manifest-network/manifest-mcp-core";
6
+ //#region src/tools/deployApp.ts
7
+ async function sha256(data) {
8
+ const encoded = new TextEncoder().encode(data);
9
+ const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
10
+ const bytes = new Uint8Array(hashBuffer);
11
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
12
+ }
13
+ function extractLeaseUuid(txResult) {
14
+ if (!txResult.events) throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, "No events in transaction result; cannot extract lease UUID");
15
+ for (const event of txResult.events) {
16
+ if (!event.type.includes("lease") && !event.type.includes("Lease")) continue;
17
+ for (const attr of event.attributes) if (attr.key === "lease_uuid" || attr.key === "uuid") {
18
+ const raw = attr.value.replace(/^"|"$/g, "");
19
+ requireUuid({ lease_uuid: raw }, "lease_uuid", ManifestMCPErrorCode.TX_FAILED);
20
+ return raw;
21
+ }
22
+ }
23
+ throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, "Could not find lease UUID in transaction events", { events: txResult.events });
24
+ }
25
+ async function findSkuUuid(queryClient, size) {
26
+ const pagination = createPagination(MAX_PAGE_LIMIT);
27
+ const result = await queryClient.liftedinit.sku.v1.sKUs({
28
+ activeOnly: true,
29
+ pagination
30
+ });
31
+ for (const sku of result.skus) if (sku.name === size) return {
32
+ skuUuid: sku.uuid,
33
+ providerUuid: sku.providerUuid
34
+ };
35
+ const available = result.skus.map((s) => s.name);
36
+ throw new ManifestMCPError(ManifestMCPErrorCode.QUERY_FAILED, `SKU tier "${size}" not found. Available: ${available.join(", ")}`);
37
+ }
38
+ async function deployApp(clientManager, getAuthToken, getLeaseDataAuthToken, input, fetchFn) {
39
+ if (input.image && input.services) throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, "image and services are mutually exclusive");
40
+ if (!input.image && !input.services) throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, "either image or services is required");
41
+ if (input.image && !input.port) throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, "port is required when using image");
42
+ const address = await clientManager.getAddress();
43
+ await clientManager.acquireRateLimit();
44
+ const queryClient = await clientManager.getQueryClient();
45
+ let manifestJson;
46
+ if (input.services) {
47
+ for (const name of Object.keys(input.services)) if (!validateServiceName(name)) throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, `Invalid service name: "${name}". Must be 1-63 chars, lowercase alphanumeric + hyphens, no leading/trailing hyphens.`);
48
+ const services = {};
49
+ for (const [name, svc] of Object.entries(input.services)) services[name] = {
50
+ image: svc.image,
51
+ ports: svc.ports ?? {},
52
+ env: svc.env,
53
+ command: svc.command,
54
+ args: svc.args,
55
+ user: svc.user,
56
+ tmpfs: svc.tmpfs,
57
+ health_check: svc.health_check,
58
+ stop_grace_period: svc.stop_grace_period,
59
+ depends_on: svc.depends_on,
60
+ expose: svc.expose,
61
+ labels: svc.labels
62
+ };
63
+ manifestJson = JSON.stringify(buildStackManifest({ services }));
64
+ } else {
65
+ const image = input.image;
66
+ manifestJson = JSON.stringify(buildManifest({
67
+ image,
68
+ ports: { [`${input.port}/tcp`]: {} },
69
+ env: input.env,
70
+ command: input.command,
71
+ args: input.args,
72
+ user: input.user,
73
+ tmpfs: input.tmpfs,
74
+ health_check: input.health_check,
75
+ stop_grace_period: input.stop_grace_period,
76
+ init: input.init,
77
+ expose: input.expose,
78
+ labels: input.labels,
79
+ depends_on: input.depends_on
80
+ }));
81
+ }
82
+ const metaHashHex = await sha256(manifestJson);
83
+ const { skuUuid, providerUuid } = await findSkuUuid(queryClient, input.size);
84
+ let leaseItems;
85
+ if (input.services) leaseItems = Object.keys(input.services).map((name) => `${skuUuid}:1:${name}`);
86
+ else leaseItems = [`${skuUuid}:1`];
87
+ if (input.storage) {
88
+ const { skuUuid: storageSkuUuid } = await findSkuUuid(queryClient, input.storage);
89
+ leaseItems.push(`${storageSkuUuid}:1`);
90
+ }
91
+ const providerUrl = await resolveProviderUrl(queryClient, providerUuid);
92
+ const leaseUuid = extractLeaseUuid(await cosmosTx(clientManager, "billing", "create-lease", [
93
+ "--meta-hash",
94
+ metaHashHex,
95
+ ...leaseItems
96
+ ], true));
97
+ let status;
98
+ try {
99
+ const leaseDataToken = await getLeaseDataAuthToken(address, leaseUuid, metaHashHex);
100
+ await uploadLeaseData(providerUrl, leaseUuid, new TextEncoder().encode(manifestJson), leaseDataToken, fetchFn);
101
+ status = await pollLeaseUntilReady(providerUrl, leaseUuid, () => getAuthToken(address, leaseUuid), void 0, fetchFn);
102
+ } catch (err) {
103
+ const code = err instanceof ManifestMCPError ? err.code : ManifestMCPErrorCode.QUERY_FAILED;
104
+ const details = err instanceof ManifestMCPError ? {
105
+ ...err.details,
106
+ lease_uuid: leaseUuid,
107
+ provider_uuid: providerUuid,
108
+ provider_url: providerUrl
109
+ } : {
110
+ lease_uuid: leaseUuid,
111
+ provider_uuid: providerUuid,
112
+ provider_url: providerUrl
113
+ };
114
+ throw new ManifestMCPError(code, `Deploy partially succeeded: lease ${leaseUuid} was created but subsequent steps failed. Close this lease with close_lease if needed. Error: ${err instanceof Error ? err.message : String(err)}`, details);
115
+ }
116
+ let connection;
117
+ let url;
118
+ let connectionError;
119
+ try {
120
+ connection = (await getLeaseConnectionInfo(providerUrl, leaseUuid, await getAuthToken(address, leaseUuid), fetchFn)).connection;
121
+ if (connection.host && connection.ports) {
122
+ const firstPort = Object.values(connection.ports)[0];
123
+ if (typeof firstPort === "number" || typeof firstPort === "string") url = `${connection.host}:${firstPort}`;
124
+ }
125
+ } catch (err) {
126
+ const rawMsg = err instanceof Error ? err.message : String(err);
127
+ logger.error(`[deploy_app] Failed to fetch connection info for lease ${leaseUuid}: ${rawMsg}`);
128
+ connectionError = sanitizeForLogging(rawMsg);
129
+ }
130
+ return {
131
+ lease_uuid: leaseUuid,
132
+ provider_uuid: providerUuid,
133
+ provider_url: providerUrl,
134
+ state: status.state,
135
+ ...url && { url },
136
+ ...connection && { connection },
137
+ ...connectionError && { connectionError }
138
+ };
139
+ }
140
+ //#endregion
141
+ export { deployApp };
142
+
143
+ //# sourceMappingURL=deployApp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deployApp.js","names":[],"sources":["../../src/tools/deployApp.ts"],"sourcesContent":["import type {\n CosmosClientManager,\n CosmosTxResult,\n LeaseState,\n ManifestQueryClient,\n} from '@manifest-network/manifest-mcp-core';\nimport {\n cosmosTx,\n createPagination,\n logger,\n MAX_PAGE_LIMIT,\n ManifestMCPError,\n ManifestMCPErrorCode,\n requireUuid,\n sanitizeForLogging,\n} from '@manifest-network/manifest-mcp-core';\nimport type { FredLeaseStatus } from '../http/fred.js';\nimport { pollLeaseUntilReady } from '../http/fred.js';\nimport {\n type ConnectionDetails,\n getLeaseConnectionInfo,\n uploadLeaseData,\n} from '../http/provider.js';\nimport {\n type BuildManifestOptions,\n buildManifest,\n buildStackManifest,\n validateServiceName,\n} from '../manifest.js';\nimport { resolveProviderUrl } from './resolveLeaseProvider.js';\n\nasync function sha256(data: string): Promise<string> {\n const encoded = new TextEncoder().encode(data);\n const hashBuffer = await crypto.subtle.digest('SHA-256', encoded);\n const bytes = new Uint8Array(hashBuffer);\n return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n}\n\nfunction extractLeaseUuid(txResult: CosmosTxResult): string {\n if (!txResult.events) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.TX_FAILED,\n 'No events in transaction result; cannot extract lease UUID',\n );\n }\n\n for (const event of txResult.events) {\n if (!event.type.includes('lease') && !event.type.includes('Lease'))\n continue;\n for (const attr of event.attributes) {\n if (attr.key === 'lease_uuid' || attr.key === 'uuid') {\n const raw = attr.value.replace(/^\"|\"$/g, '');\n // Validate the extracted value is a proper UUID\n requireUuid(\n { lease_uuid: raw },\n 'lease_uuid',\n ManifestMCPErrorCode.TX_FAILED,\n );\n return raw;\n }\n }\n }\n\n throw new ManifestMCPError(\n ManifestMCPErrorCode.TX_FAILED,\n 'Could not find lease UUID in transaction events',\n { events: txResult.events as unknown as Record<string, unknown>[] },\n );\n}\n\nasync function findSkuUuid(\n queryClient: ManifestQueryClient,\n size: string,\n): Promise<{ skuUuid: string; providerUuid: string }> {\n const pagination = createPagination(MAX_PAGE_LIMIT);\n const result = await queryClient.liftedinit.sku.v1.sKUs({\n activeOnly: true,\n pagination,\n });\n\n for (const sku of result.skus) {\n if (sku.name === size) {\n return { skuUuid: sku.uuid, providerUuid: sku.providerUuid };\n }\n }\n\n const available = result.skus.map((s) => s.name);\n throw new ManifestMCPError(\n ManifestMCPErrorCode.QUERY_FAILED,\n `SKU tier \"${size}\" not found. Available: ${available.join(', ')}`,\n );\n}\n\nexport interface ServiceConfig {\n image: string;\n ports?: Record<string, Record<string, never>>;\n env?: Record<string, string>;\n command?: string[];\n args?: string[];\n user?: string;\n tmpfs?: string[];\n health_check?: {\n test: string[];\n interval?: string;\n timeout?: string;\n retries?: number;\n start_period?: string;\n };\n stop_grace_period?: string;\n depends_on?: Record<string, { condition: string }>;\n expose?: string[];\n labels?: Record<string, string>;\n}\n\nexport interface DeployAppInput {\n image?: string;\n port?: number;\n size: string;\n env?: Record<string, string>;\n command?: string[];\n args?: string[];\n user?: string;\n tmpfs?: string[];\n health_check?: {\n test: string[];\n interval?: string;\n timeout?: string;\n retries?: number;\n start_period?: string;\n };\n stop_grace_period?: string;\n init?: boolean;\n expose?: string[];\n labels?: Record<string, string>;\n storage?: string;\n depends_on?: Record<string, { condition: string }>;\n services?: Record<string, ServiceConfig>;\n}\n\nexport interface DeployAppResult {\n readonly lease_uuid: string;\n readonly provider_uuid: string;\n readonly provider_url: string;\n readonly state: LeaseState;\n readonly url?: string;\n readonly connection?: ConnectionDetails;\n readonly connectionError?: string;\n}\n\nexport async function deployApp(\n clientManager: CosmosClientManager,\n getAuthToken: (address: string, leaseUuid: string) => Promise<string>,\n getLeaseDataAuthToken: (\n address: string,\n leaseUuid: string,\n metaHashHex: string,\n ) => Promise<string>,\n input: DeployAppInput,\n fetchFn?: typeof globalThis.fetch,\n): Promise<DeployAppResult> {\n // Validate mutually exclusive inputs\n if (input.image && input.services) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n 'image and services are mutually exclusive',\n );\n }\n if (!input.image && !input.services) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n 'either image or services is required',\n );\n }\n if (input.image && !input.port) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n 'port is required when using image',\n );\n }\n\n const address = await clientManager.getAddress();\n await clientManager.acquireRateLimit();\n const queryClient = await clientManager.getQueryClient();\n\n // 1. Build manifest\n let manifestJson: string;\n if (input.services) {\n for (const name of Object.keys(input.services)) {\n if (!validateServiceName(name)) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n `Invalid service name: \"${name}\". Must be 1-63 chars, lowercase alphanumeric + hyphens, no leading/trailing hyphens.`,\n );\n }\n }\n\n const services: Record<string, BuildManifestOptions> = {};\n for (const [name, svc] of Object.entries(input.services)) {\n services[name] = {\n image: svc.image,\n ports: svc.ports ?? {},\n env: svc.env,\n command: svc.command,\n args: svc.args,\n user: svc.user,\n tmpfs: svc.tmpfs,\n health_check: svc.health_check,\n stop_grace_period: svc.stop_grace_period,\n depends_on: svc.depends_on,\n expose: svc.expose,\n labels: svc.labels,\n };\n }\n manifestJson = JSON.stringify(buildStackManifest({ services }));\n } else {\n // image is guaranteed defined here: the guard above ensures !image && !services is false,\n // and the if-branch handles the services case. TypeScript can't narrow across if/else.\n const image = input.image as string;\n manifestJson = JSON.stringify(\n buildManifest({\n image,\n ports: { [`${input.port}/tcp`]: {} },\n env: input.env,\n command: input.command,\n args: input.args,\n user: input.user,\n tmpfs: input.tmpfs,\n health_check: input.health_check,\n stop_grace_period: input.stop_grace_period,\n init: input.init,\n expose: input.expose,\n labels: input.labels,\n depends_on: input.depends_on,\n }),\n );\n }\n\n // 2. SHA-256 hash of manifest\n const metaHashHex = await sha256(manifestJson);\n\n // 3. Find matching SKU(s)\n const { skuUuid, providerUuid } = await findSkuUuid(queryClient, input.size);\n\n let leaseItems: string[];\n if (input.services) {\n const serviceNames = Object.keys(input.services);\n leaseItems = serviceNames.map((name) => `${skuUuid}:1:${name}`);\n } else {\n leaseItems = [`${skuUuid}:1`];\n }\n\n if (input.storage) {\n const { skuUuid: storageSkuUuid } = await findSkuUuid(\n queryClient,\n input.storage,\n );\n leaseItems.push(`${storageSkuUuid}:1`);\n }\n\n // 4. Get provider URL\n const providerUrl = await resolveProviderUrl(queryClient, providerUuid);\n\n // 5. Create lease\n const txResult = await cosmosTx(\n clientManager,\n 'billing',\n 'create-lease',\n ['--meta-hash', metaHashHex, ...leaseItems],\n true,\n );\n\n // 6. Extract lease UUID\n const leaseUuid = extractLeaseUuid(txResult);\n\n let status: FredLeaseStatus;\n try {\n // 7. Upload manifest with lease-data auth token\n const leaseDataToken = await getLeaseDataAuthToken(\n address,\n leaseUuid,\n metaHashHex,\n );\n await uploadLeaseData(\n providerUrl,\n leaseUuid,\n new TextEncoder().encode(manifestJson),\n leaseDataToken,\n fetchFn,\n );\n\n // 8. Poll until ready\n status = await pollLeaseUntilReady(\n providerUrl,\n leaseUuid,\n () => getAuthToken(address, leaseUuid),\n undefined,\n fetchFn,\n );\n } catch (err) {\n const code =\n err instanceof ManifestMCPError\n ? err.code\n : ManifestMCPErrorCode.QUERY_FAILED;\n const details =\n err instanceof ManifestMCPError\n ? {\n ...err.details,\n lease_uuid: leaseUuid,\n provider_uuid: providerUuid,\n provider_url: providerUrl,\n }\n : {\n lease_uuid: leaseUuid,\n provider_uuid: providerUuid,\n provider_url: providerUrl,\n };\n throw new ManifestMCPError(\n code,\n `Deploy partially succeeded: lease ${leaseUuid} was created but subsequent steps failed. ` +\n `Close this lease with close_lease if needed. Error: ${err instanceof Error ? err.message : String(err)}`,\n details,\n );\n }\n\n // 9. Get connection info (best-effort)\n let connection: ConnectionDetails | undefined;\n let url: string | undefined;\n let connectionError: string | undefined;\n try {\n const authToken = await getAuthToken(address, leaseUuid);\n const connResp = await getLeaseConnectionInfo(\n providerUrl,\n leaseUuid,\n authToken,\n fetchFn,\n );\n connection = connResp.connection;\n if (connection.host && connection.ports) {\n const firstPort = Object.values(connection.ports)[0];\n if (typeof firstPort === 'number' || typeof firstPort === 'string') {\n url = `${connection.host}:${firstPort}`;\n }\n }\n } catch (err) {\n const rawMsg = err instanceof Error ? err.message : String(err);\n // Log raw message to stderr for debugging; sanitize only the user-facing return value\n logger.error(\n `[deploy_app] Failed to fetch connection info for lease ${leaseUuid}: ${rawMsg}`,\n );\n connectionError = sanitizeForLogging(rawMsg) as string;\n }\n\n return {\n lease_uuid: leaseUuid,\n provider_uuid: providerUuid,\n provider_url: providerUrl,\n state: status.state,\n ...(url && { url }),\n ...(connection && { connection }),\n ...(connectionError && { connectionError }),\n };\n}\n"],"mappings":";;;;;;AA+BA,eAAe,OAAO,MAA+B;CACnD,MAAM,UAAU,IAAI,aAAa,CAAC,OAAO,KAAK;CAC9C,MAAM,aAAa,MAAM,OAAO,OAAO,OAAO,WAAW,QAAQ;CACjE,MAAM,QAAQ,IAAI,WAAW,WAAW;AACxC,QAAO,MAAM,KAAK,QAAQ,MAAM,EAAE,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,KAAK,GAAG;;AAG3E,SAAS,iBAAiB,UAAkC;AAC1D,KAAI,CAAC,SAAS,OACZ,OAAM,IAAI,iBACR,qBAAqB,WACrB,6DACD;AAGH,MAAK,MAAM,SAAS,SAAS,QAAQ;AACnC,MAAI,CAAC,MAAM,KAAK,SAAS,QAAQ,IAAI,CAAC,MAAM,KAAK,SAAS,QAAQ,CAChE;AACF,OAAK,MAAM,QAAQ,MAAM,WACvB,KAAI,KAAK,QAAQ,gBAAgB,KAAK,QAAQ,QAAQ;GACpD,MAAM,MAAM,KAAK,MAAM,QAAQ,UAAU,GAAG;AAE5C,eACE,EAAE,YAAY,KAAK,EACnB,cACA,qBAAqB,UACtB;AACD,UAAO;;;AAKb,OAAM,IAAI,iBACR,qBAAqB,WACrB,mDACA,EAAE,QAAQ,SAAS,QAAgD,CACpE;;AAGH,eAAe,YACb,aACA,MACoD;CACpD,MAAM,aAAa,iBAAiB,eAAe;CACnD,MAAM,SAAS,MAAM,YAAY,WAAW,IAAI,GAAG,KAAK;EACtD,YAAY;EACZ;EACD,CAAC;AAEF,MAAK,MAAM,OAAO,OAAO,KACvB,KAAI,IAAI,SAAS,KACf,QAAO;EAAE,SAAS,IAAI;EAAM,cAAc,IAAI;EAAc;CAIhE,MAAM,YAAY,OAAO,KAAK,KAAK,MAAM,EAAE,KAAK;AAChD,OAAM,IAAI,iBACR,qBAAqB,cACrB,aAAa,KAAK,0BAA0B,UAAU,KAAK,KAAK,GACjE;;AA2DH,eAAsB,UACpB,eACA,cACA,uBAKA,OACA,SAC0B;AAE1B,KAAI,MAAM,SAAS,MAAM,SACvB,OAAM,IAAI,iBACR,qBAAqB,gBACrB,4CACD;AAEH,KAAI,CAAC,MAAM,SAAS,CAAC,MAAM,SACzB,OAAM,IAAI,iBACR,qBAAqB,gBACrB,uCACD;AAEH,KAAI,MAAM,SAAS,CAAC,MAAM,KACxB,OAAM,IAAI,iBACR,qBAAqB,gBACrB,oCACD;CAGH,MAAM,UAAU,MAAM,cAAc,YAAY;AAChD,OAAM,cAAc,kBAAkB;CACtC,MAAM,cAAc,MAAM,cAAc,gBAAgB;CAGxD,IAAI;AACJ,KAAI,MAAM,UAAU;AAClB,OAAK,MAAM,QAAQ,OAAO,KAAK,MAAM,SAAS,CAC5C,KAAI,CAAC,oBAAoB,KAAK,CAC5B,OAAM,IAAI,iBACR,qBAAqB,gBACrB,0BAA0B,KAAK,uFAChC;EAIL,MAAM,WAAiD,EAAE;AACzD,OAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,QAAQ,MAAM,SAAS,CACtD,UAAS,QAAQ;GACf,OAAO,IAAI;GACX,OAAO,IAAI,SAAS,EAAE;GACtB,KAAK,IAAI;GACT,SAAS,IAAI;GACb,MAAM,IAAI;GACV,MAAM,IAAI;GACV,OAAO,IAAI;GACX,cAAc,IAAI;GAClB,mBAAmB,IAAI;GACvB,YAAY,IAAI;GAChB,QAAQ,IAAI;GACZ,QAAQ,IAAI;GACb;AAEH,iBAAe,KAAK,UAAU,mBAAmB,EAAE,UAAU,CAAC,CAAC;QAC1D;EAGL,MAAM,QAAQ,MAAM;AACpB,iBAAe,KAAK,UAClB,cAAc;GACZ;GACA,OAAO,GAAG,GAAG,MAAM,KAAK,QAAQ,EAAE,EAAE;GACpC,KAAK,MAAM;GACX,SAAS,MAAM;GACf,MAAM,MAAM;GACZ,MAAM,MAAM;GACZ,OAAO,MAAM;GACb,cAAc,MAAM;GACpB,mBAAmB,MAAM;GACzB,MAAM,MAAM;GACZ,QAAQ,MAAM;GACd,QAAQ,MAAM;GACd,YAAY,MAAM;GACnB,CAAC,CACH;;CAIH,MAAM,cAAc,MAAM,OAAO,aAAa;CAG9C,MAAM,EAAE,SAAS,iBAAiB,MAAM,YAAY,aAAa,MAAM,KAAK;CAE5E,IAAI;AACJ,KAAI,MAAM,SAER,cADqB,OAAO,KAAK,MAAM,SAAS,CACtB,KAAK,SAAS,GAAG,QAAQ,KAAK,OAAO;KAE/D,cAAa,CAAC,GAAG,QAAQ,IAAI;AAG/B,KAAI,MAAM,SAAS;EACjB,MAAM,EAAE,SAAS,mBAAmB,MAAM,YACxC,aACA,MAAM,QACP;AACD,aAAW,KAAK,GAAG,eAAe,IAAI;;CAIxC,MAAM,cAAc,MAAM,mBAAmB,aAAa,aAAa;CAYvE,MAAM,YAAY,iBATD,MAAM,SACrB,eACA,WACA,gBACA;EAAC;EAAe;EAAa,GAAG;EAAW,EAC3C,KACD,CAG2C;CAE5C,IAAI;AACJ,KAAI;EAEF,MAAM,iBAAiB,MAAM,sBAC3B,SACA,WACA,YACD;AACD,QAAM,gBACJ,aACA,WACA,IAAI,aAAa,CAAC,OAAO,aAAa,EACtC,gBACA,QACD;AAGD,WAAS,MAAM,oBACb,aACA,iBACM,aAAa,SAAS,UAAU,EACtC,KAAA,GACA,QACD;UACM,KAAK;EACZ,MAAM,OACJ,eAAe,mBACX,IAAI,OACJ,qBAAqB;EAC3B,MAAM,UACJ,eAAe,mBACX;GACE,GAAG,IAAI;GACP,YAAY;GACZ,eAAe;GACf,cAAc;GACf,GACD;GACE,YAAY;GACZ,eAAe;GACf,cAAc;GACf;AACP,QAAM,IAAI,iBACR,MACA,qCAAqC,UAAU,gGACU,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,IACzG,QACD;;CAIH,IAAI;CACJ,IAAI;CACJ,IAAI;AACJ,KAAI;AAQF,gBANiB,MAAM,uBACrB,aACA,WAHgB,MAAM,aAAa,SAAS,UAAU,EAKtD,QACD,EACqB;AACtB,MAAI,WAAW,QAAQ,WAAW,OAAO;GACvC,MAAM,YAAY,OAAO,OAAO,WAAW,MAAM,CAAC;AAClD,OAAI,OAAO,cAAc,YAAY,OAAO,cAAc,SACxD,OAAM,GAAG,WAAW,KAAK,GAAG;;UAGzB,KAAK;EACZ,MAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAE/D,SAAO,MACL,0DAA0D,UAAU,IAAI,SACzE;AACD,oBAAkB,mBAAmB,OAAO;;AAG9C,QAAO;EACL,YAAY;EACZ,eAAe;EACf,cAAc;EACd,OAAO,OAAO;EACd,GAAI,OAAO,EAAE,KAAK;EAClB,GAAI,cAAc,EAAE,YAAY;EAChC,GAAI,mBAAmB,EAAE,iBAAiB;EAC3C"}
@@ -0,0 +1,12 @@
1
+ import * as _manifest_network_manifest_mcp_core0 from "@manifest-network/manifest-mcp-core";
2
+ import { ManifestQueryClient } from "@manifest-network/manifest-mcp-core";
3
+
4
+ //#region src/tools/fetchActiveLease.d.ts
5
+ /**
6
+ * Fetches a lease by UUID, validates it exists and is in active/pending state.
7
+ * Throws ManifestMCPError if the lease is not found or not in an active state.
8
+ */
9
+ declare function fetchActiveLease(queryClient: ManifestQueryClient, leaseUuid: string, action: string): Promise<_manifest_network_manifest_mcp_core0.Lease>;
10
+ //#endregion
11
+ export { fetchActiveLease };
12
+ //# sourceMappingURL=fetchActiveLease.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetchActiveLease.d.ts","names":[],"sources":["../../src/tools/fetchActiveLease.ts"],"mappings":";;;;;;;AAYA;iBAAsB,gBAAA,CACpB,WAAA,EAAa,mBAAA,EACb,SAAA,UACA,MAAA,WAAc,OAAA,CAFkB,oCAAA,CAElB,KAAA"}
@@ -0,0 +1,17 @@
1
+ import { LeaseState, ManifestMCPError, ManifestMCPErrorCode, leaseStateToJSON } from "@manifest-network/manifest-mcp-core";
2
+ //#region src/tools/fetchActiveLease.ts
3
+ /**
4
+ * Fetches a lease by UUID, validates it exists and is in active/pending state.
5
+ * Throws ManifestMCPError if the lease is not found or not in an active state.
6
+ */
7
+ async function fetchActiveLease(queryClient, leaseUuid, action) {
8
+ const leaseResult = await queryClient.liftedinit.billing.v1.lease({ leaseUuid });
9
+ if (!leaseResult.lease) throw new ManifestMCPError(ManifestMCPErrorCode.QUERY_FAILED, `Lease "${leaseUuid}" not found on chain`);
10
+ const lease = leaseResult.lease;
11
+ if (lease.state !== LeaseState.LEASE_STATE_ACTIVE && lease.state !== LeaseState.LEASE_STATE_PENDING) throw new ManifestMCPError(ManifestMCPErrorCode.QUERY_FAILED, `Lease "${leaseUuid}" is not active (state: ${leaseStateToJSON(lease.state)}); ${action}`);
12
+ return lease;
13
+ }
14
+ //#endregion
15
+ export { fetchActiveLease };
16
+
17
+ //# sourceMappingURL=fetchActiveLease.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetchActiveLease.js","names":[],"sources":["../../src/tools/fetchActiveLease.ts"],"sourcesContent":["import {\n LeaseState,\n leaseStateToJSON,\n ManifestMCPError,\n ManifestMCPErrorCode,\n type ManifestQueryClient,\n} from '@manifest-network/manifest-mcp-core';\n\n/**\n * Fetches a lease by UUID, validates it exists and is in active/pending state.\n * Throws ManifestMCPError if the lease is not found or not in an active state.\n */\nexport async function fetchActiveLease(\n queryClient: ManifestQueryClient,\n leaseUuid: string,\n action: string,\n) {\n const leaseResult = await queryClient.liftedinit.billing.v1.lease({\n leaseUuid,\n });\n\n if (!leaseResult.lease) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.QUERY_FAILED,\n `Lease \"${leaseUuid}\" not found on chain`,\n );\n }\n\n const lease = leaseResult.lease;\n\n if (\n lease.state !== LeaseState.LEASE_STATE_ACTIVE &&\n lease.state !== LeaseState.LEASE_STATE_PENDING\n ) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.QUERY_FAILED,\n `Lease \"${leaseUuid}\" is not active (state: ${leaseStateToJSON(lease.state)}); ${action}`,\n );\n }\n\n return lease;\n}\n"],"mappings":";;;;;;AAYA,eAAsB,iBACpB,aACA,WACA,QACA;CACA,MAAM,cAAc,MAAM,YAAY,WAAW,QAAQ,GAAG,MAAM,EAChE,WACD,CAAC;AAEF,KAAI,CAAC,YAAY,MACf,OAAM,IAAI,iBACR,qBAAqB,cACrB,UAAU,UAAU,sBACrB;CAGH,MAAM,QAAQ,YAAY;AAE1B,KACE,MAAM,UAAU,WAAW,sBAC3B,MAAM,UAAU,WAAW,oBAE3B,OAAM,IAAI,iBACR,qBAAqB,cACrB,UAAU,UAAU,0BAA0B,iBAAiB,MAAM,MAAM,CAAC,KAAK,SAClF;AAGH,QAAO"}
@@ -0,0 +1,11 @@
1
+ import { ManifestQueryClient } from "@manifest-network/manifest-mcp-core";
2
+
3
+ //#region src/tools/getLogs.d.ts
4
+ declare function getAppLogs(queryClient: ManifestQueryClient, address: string, leaseUuid: string, getAuthToken: (address: string, leaseUuid: string) => Promise<string>, tail?: number, fetchFn?: typeof globalThis.fetch): Promise<{
5
+ lease_uuid: string;
6
+ logs: Record<string, string>;
7
+ truncated: boolean;
8
+ }>;
9
+ //#endregion
10
+ export { getAppLogs };
11
+ //# sourceMappingURL=getLogs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getLogs.d.ts","names":[],"sources":["../../src/tools/getLogs.ts"],"mappings":";;;iBAOsB,UAAA,CACpB,WAAA,EAAa,mBAAA,EACb,OAAA,UACA,SAAA,UACA,YAAA,GAAe,OAAA,UAAiB,SAAA,aAAsB,OAAA,UACtD,IAAA,WACA,OAAA,UAAiB,UAAA,CAAW,KAAA,GAAK,OAAA"}
@@ -0,0 +1,32 @@
1
+ import { getLeaseLogs } from "../http/fred.js";
2
+ import { resolveProviderUrl } from "./resolveLeaseProvider.js";
3
+ import { fetchActiveLease } from "./fetchActiveLease.js";
4
+ //#region src/tools/getLogs.ts
5
+ const MAX_LOG_CHARS = 4e3;
6
+ async function getAppLogs(queryClient, address, leaseUuid, getAuthToken, tail, fetchFn) {
7
+ const result = await getLeaseLogs(await resolveProviderUrl(queryClient, (await fetchActiveLease(queryClient, leaseUuid, "logs are not available")).providerUuid), leaseUuid, await getAuthToken(address, leaseUuid), tail, fetchFn);
8
+ let truncated = false;
9
+ const logs = {};
10
+ let totalChars = 0;
11
+ for (const [service, log] of Object.entries(result.logs)) {
12
+ if (totalChars >= MAX_LOG_CHARS) {
13
+ truncated = true;
14
+ break;
15
+ }
16
+ const remaining = MAX_LOG_CHARS - totalChars;
17
+ if (log.length > remaining) {
18
+ logs[service] = log.slice(-remaining);
19
+ truncated = true;
20
+ } else logs[service] = log;
21
+ totalChars += logs[service].length;
22
+ }
23
+ return {
24
+ lease_uuid: leaseUuid,
25
+ logs,
26
+ truncated
27
+ };
28
+ }
29
+ //#endregion
30
+ export { getAppLogs };
31
+
32
+ //# sourceMappingURL=getLogs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getLogs.js","names":[],"sources":["../../src/tools/getLogs.ts"],"sourcesContent":["import type { ManifestQueryClient } from '@manifest-network/manifest-mcp-core';\nimport { getLeaseLogs } from '../http/fred.js';\nimport { fetchActiveLease } from './fetchActiveLease.js';\nimport { resolveProviderUrl } from './resolveLeaseProvider.js';\n\nconst MAX_LOG_CHARS = 4000;\n\nexport async function getAppLogs(\n queryClient: ManifestQueryClient,\n address: string,\n leaseUuid: string,\n getAuthToken: (address: string, leaseUuid: string) => Promise<string>,\n tail?: number,\n fetchFn?: typeof globalThis.fetch,\n) {\n const lease = await fetchActiveLease(\n queryClient,\n leaseUuid,\n 'logs are not available',\n );\n\n const providerUrl = await resolveProviderUrl(queryClient, lease.providerUuid);\n const authToken = await getAuthToken(address, leaseUuid);\n const result = await getLeaseLogs(\n providerUrl,\n leaseUuid,\n authToken,\n tail,\n fetchFn,\n );\n\n let truncated = false;\n const logs: Record<string, string> = {};\n let totalChars = 0;\n\n for (const [service, log] of Object.entries(result.logs)) {\n if (totalChars >= MAX_LOG_CHARS) {\n truncated = true;\n break;\n }\n const remaining = MAX_LOG_CHARS - totalChars;\n if (log.length > remaining) {\n logs[service] = log.slice(-remaining);\n truncated = true;\n } else {\n logs[service] = log;\n }\n totalChars += logs[service].length;\n }\n\n return {\n lease_uuid: leaseUuid,\n logs,\n truncated,\n };\n}\n"],"mappings":";;;;AAKA,MAAM,gBAAgB;AAEtB,eAAsB,WACpB,aACA,SACA,WACA,cACA,MACA,SACA;CASA,MAAM,SAAS,MAAM,aAFD,MAAM,mBAAmB,cAN/B,MAAM,iBAClB,aACA,WACA,yBACD,EAE+D,aAAa,EAI3E,WAHgB,MAAM,aAAa,SAAS,UAAU,EAKtD,MACA,QACD;CAED,IAAI,YAAY;CAChB,MAAM,OAA+B,EAAE;CACvC,IAAI,aAAa;AAEjB,MAAK,MAAM,CAAC,SAAS,QAAQ,OAAO,QAAQ,OAAO,KAAK,EAAE;AACxD,MAAI,cAAc,eAAe;AAC/B,eAAY;AACZ;;EAEF,MAAM,YAAY,gBAAgB;AAClC,MAAI,IAAI,SAAS,WAAW;AAC1B,QAAK,WAAW,IAAI,MAAM,CAAC,UAAU;AACrC,eAAY;QAEZ,MAAK,WAAW;AAElB,gBAAc,KAAK,SAAS;;AAG9B,QAAO;EACL,YAAY;EACZ;EACA;EACD"}
@@ -0,0 +1,7 @@
1
+ import { ManifestQueryClient } from "@manifest-network/manifest-mcp-core";
2
+
3
+ //#region src/tools/resolveLeaseProvider.d.ts
4
+ declare function resolveProviderUrl(queryClient: ManifestQueryClient, providerUuid: string): Promise<string>;
5
+ //#endregion
6
+ export { resolveProviderUrl };
7
+ //# sourceMappingURL=resolveLeaseProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveLeaseProvider.d.ts","names":[],"sources":["../../src/tools/resolveLeaseProvider.ts"],"mappings":";;;iBAOsB,kBAAA,CACpB,WAAA,EAAa,mBAAA,EACb,YAAA,WACC,OAAA"}
@@ -0,0 +1,20 @@
1
+ import { ProviderApiError, validateProviderUrl } from "../http/provider.js";
2
+ import { ManifestMCPError, ManifestMCPErrorCode } from "@manifest-network/manifest-mcp-core";
3
+ //#region src/tools/resolveLeaseProvider.ts
4
+ async function resolveProviderUrl(queryClient, providerUuid) {
5
+ if (!providerUuid) throw new ManifestMCPError(ManifestMCPErrorCode.QUERY_FAILED, "Provider UUID is empty; the lease may not have an assigned provider");
6
+ try {
7
+ const providerResult = await queryClient.liftedinit.sku.v1.provider({ uuid: providerUuid });
8
+ if (!providerResult.provider?.apiUrl) throw new ManifestMCPError(ManifestMCPErrorCode.QUERY_FAILED, `Provider "${providerUuid}" has no API URL`);
9
+ return validateProviderUrl(providerResult.provider.apiUrl);
10
+ } catch (error) {
11
+ if (error instanceof ManifestMCPError) throw error;
12
+ if (error instanceof ProviderApiError) throw error;
13
+ const message = error instanceof Error ? error.message : String(error);
14
+ throw new ManifestMCPError(ManifestMCPErrorCode.QUERY_FAILED, `Failed to resolve provider "${providerUuid}" via SKU module: ${message}`);
15
+ }
16
+ }
17
+ //#endregion
18
+ export { resolveProviderUrl };
19
+
20
+ //# sourceMappingURL=resolveLeaseProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveLeaseProvider.js","names":[],"sources":["../../src/tools/resolveLeaseProvider.ts"],"sourcesContent":["import type { ManifestQueryClient } from '@manifest-network/manifest-mcp-core';\nimport {\n ManifestMCPError,\n ManifestMCPErrorCode,\n} from '@manifest-network/manifest-mcp-core';\nimport { ProviderApiError, validateProviderUrl } from '../http/provider.js';\n\nexport async function resolveProviderUrl(\n queryClient: ManifestQueryClient,\n providerUuid: string,\n): Promise<string> {\n if (!providerUuid) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.QUERY_FAILED,\n 'Provider UUID is empty; the lease may not have an assigned provider',\n );\n }\n\n try {\n const providerResult = await queryClient.liftedinit.sku.v1.provider({\n uuid: providerUuid,\n });\n\n if (!providerResult.provider?.apiUrl) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.QUERY_FAILED,\n `Provider \"${providerUuid}\" has no API URL`,\n );\n }\n\n return validateProviderUrl(providerResult.provider.apiUrl);\n } catch (error) {\n if (error instanceof ManifestMCPError) throw error;\n if (error instanceof ProviderApiError) throw error;\n const message = error instanceof Error ? error.message : String(error);\n throw new ManifestMCPError(\n ManifestMCPErrorCode.QUERY_FAILED,\n `Failed to resolve provider \"${providerUuid}\" via SKU module: ${message}`,\n );\n }\n}\n"],"mappings":";;;AAOA,eAAsB,mBACpB,aACA,cACiB;AACjB,KAAI,CAAC,aACH,OAAM,IAAI,iBACR,qBAAqB,cACrB,sEACD;AAGH,KAAI;EACF,MAAM,iBAAiB,MAAM,YAAY,WAAW,IAAI,GAAG,SAAS,EAClE,MAAM,cACP,CAAC;AAEF,MAAI,CAAC,eAAe,UAAU,OAC5B,OAAM,IAAI,iBACR,qBAAqB,cACrB,aAAa,aAAa,kBAC3B;AAGH,SAAO,oBAAoB,eAAe,SAAS,OAAO;UACnD,OAAO;AACd,MAAI,iBAAiB,iBAAkB,OAAM;AAC7C,MAAI,iBAAiB,iBAAkB,OAAM;EAC7C,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACtE,QAAM,IAAI,iBACR,qBAAqB,cACrB,+BAA+B,aAAa,oBAAoB,UACjE"}
@@ -0,0 +1,10 @@
1
+ import { ManifestQueryClient } from "@manifest-network/manifest-mcp-core";
2
+
3
+ //#region src/tools/restartApp.d.ts
4
+ declare function restartApp(queryClient: ManifestQueryClient, address: string, leaseUuid: string, getAuthToken: (address: string, leaseUuid: string) => Promise<string>, fetchFn?: typeof globalThis.fetch): Promise<{
5
+ lease_uuid: string;
6
+ status: string;
7
+ }>;
8
+ //#endregion
9
+ export { restartApp };
10
+ //# sourceMappingURL=restartApp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"restartApp.d.ts","names":[],"sources":["../../src/tools/restartApp.ts"],"mappings":";;;iBAKsB,UAAA,CACpB,WAAA,EAAa,mBAAA,EACb,OAAA,UACA,SAAA,UACA,YAAA,GAAe,OAAA,UAAiB,SAAA,aAAsB,OAAA,UACtD,OAAA,UAAiB,UAAA,CAAW,KAAA,GAAK,OAAA"}
@@ -0,0 +1,14 @@
1
+ import { restartLease } from "../http/fred.js";
2
+ import { resolveProviderUrl } from "./resolveLeaseProvider.js";
3
+ import { fetchActiveLease } from "./fetchActiveLease.js";
4
+ //#region src/tools/restartApp.ts
5
+ async function restartApp(queryClient, address, leaseUuid, getAuthToken, fetchFn) {
6
+ return {
7
+ lease_uuid: leaseUuid,
8
+ status: (await restartLease(await resolveProviderUrl(queryClient, (await fetchActiveLease(queryClient, leaseUuid, "cannot be restarted")).providerUuid), leaseUuid, await getAuthToken(address, leaseUuid), fetchFn)).status
9
+ };
10
+ }
11
+ //#endregion
12
+ export { restartApp };
13
+
14
+ //# sourceMappingURL=restartApp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"restartApp.js","names":[],"sources":["../../src/tools/restartApp.ts"],"sourcesContent":["import type { ManifestQueryClient } from '@manifest-network/manifest-mcp-core';\nimport { restartLease } from '../http/fred.js';\nimport { fetchActiveLease } from './fetchActiveLease.js';\nimport { resolveProviderUrl } from './resolveLeaseProvider.js';\n\nexport async function restartApp(\n queryClient: ManifestQueryClient,\n address: string,\n leaseUuid: string,\n getAuthToken: (address: string, leaseUuid: string) => Promise<string>,\n fetchFn?: typeof globalThis.fetch,\n) {\n const lease = await fetchActiveLease(\n queryClient,\n leaseUuid,\n 'cannot be restarted',\n );\n\n const providerUrl = await resolveProviderUrl(queryClient, lease.providerUuid);\n const authToken = await getAuthToken(address, leaseUuid);\n const result = await restartLease(providerUrl, leaseUuid, authToken, fetchFn);\n\n return {\n lease_uuid: leaseUuid,\n status: result.status,\n };\n}\n"],"mappings":";;;;AAKA,eAAsB,WACpB,aACA,SACA,WACA,cACA,SACA;AAWA,QAAO;EACL,YAAY;EACZ,SAJa,MAAM,aAFD,MAAM,mBAAmB,cAN/B,MAAM,iBAClB,aACA,WACA,sBACD,EAE+D,aAAa,EAE9B,WAD7B,MAAM,aAAa,SAAS,UAAU,EACa,QAAQ,EAI5D;EAChB"}
@@ -0,0 +1,10 @@
1
+ import { ManifestQueryClient } from "@manifest-network/manifest-mcp-core";
2
+
3
+ //#region src/tools/updateApp.d.ts
4
+ declare function updateApp(queryClient: ManifestQueryClient, address: string, leaseUuid: string, getAuthToken: (address: string, leaseUuid: string) => Promise<string>, manifest: string, existingManifest?: string, fetchFn?: typeof globalThis.fetch): Promise<{
5
+ lease_uuid: string;
6
+ status: string;
7
+ }>;
8
+ //#endregion
9
+ export { updateApp };
10
+ //# sourceMappingURL=updateApp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"updateApp.d.ts","names":[],"sources":["../../src/tools/updateApp.ts"],"mappings":";;;iBAcsB,SAAA,CACpB,WAAA,EAAa,mBAAA,EACb,OAAA,UACA,SAAA,UACA,YAAA,GAAe,OAAA,UAAiB,SAAA,aAAsB,OAAA,UACtD,QAAA,UACA,gBAAA,WACA,OAAA,UAAiB,UAAA,CAAW,KAAA,GAAK,OAAA"}
@@ -0,0 +1,47 @@
1
+ import { updateLease } from "../http/fred.js";
2
+ import { resolveProviderUrl } from "./resolveLeaseProvider.js";
3
+ import { isStackManifest, mergeManifest, validateServiceName } from "../manifest.js";
4
+ import { fetchActiveLease } from "./fetchActiveLease.js";
5
+ import { ManifestMCPError, ManifestMCPErrorCode } from "@manifest-network/manifest-mcp-core";
6
+ //#region src/tools/updateApp.ts
7
+ async function updateApp(queryClient, address, leaseUuid, getAuthToken, manifest, existingManifest, fetchFn) {
8
+ const lease = await fetchActiveLease(queryClient, leaseUuid, "cannot be updated");
9
+ let finalManifest = manifest;
10
+ if (existingManifest) {
11
+ let parsed;
12
+ try {
13
+ parsed = JSON.parse(manifest);
14
+ } catch (err) {
15
+ throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, `Invalid manifest JSON: ${err instanceof Error ? err.message : String(err)}`);
16
+ }
17
+ if (isStackManifest(parsed)) {
18
+ for (const name of Object.keys(parsed.services)) if (!validateServiceName(name)) throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, `Invalid service name: "${name}". Must be 1-63 chars, lowercase alphanumeric + hyphens, no leading/trailing hyphens.`);
19
+ let oldParsed;
20
+ try {
21
+ oldParsed = JSON.parse(existingManifest);
22
+ } catch (err) {
23
+ throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, `Invalid existing_manifest: ${err instanceof Error ? err.message : String(err)}`);
24
+ }
25
+ if (!isStackManifest(oldParsed)) throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, "Cannot merge: new manifest is a stack but existing_manifest is not. Provide a stack-format existing_manifest or omit existing_manifest for full replacement.");
26
+ const mergedStack = {};
27
+ for (const [svc, svcManifest] of Object.entries(parsed.services)) mergedStack[svc] = mergeManifest(svcManifest, oldParsed.services[svc] ? JSON.stringify(oldParsed.services[svc]) : "{}");
28
+ finalManifest = JSON.stringify({ services: mergedStack });
29
+ } else try {
30
+ const merged = mergeManifest(parsed, existingManifest);
31
+ finalManifest = JSON.stringify(merged);
32
+ } catch (err) {
33
+ if (err instanceof ManifestMCPError) throw err;
34
+ throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, `Invalid existing_manifest: ${err instanceof Error ? err.message : String(err)}`);
35
+ }
36
+ }
37
+ const providerUrl = await resolveProviderUrl(queryClient, lease.providerUuid);
38
+ const authToken = await getAuthToken(address, leaseUuid);
39
+ return {
40
+ lease_uuid: leaseUuid,
41
+ status: (await updateLease(providerUrl, leaseUuid, new TextEncoder().encode(finalManifest), authToken, fetchFn)).status
42
+ };
43
+ }
44
+ //#endregion
45
+ export { updateApp };
46
+
47
+ //# sourceMappingURL=updateApp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"updateApp.js","names":[],"sources":["../../src/tools/updateApp.ts"],"sourcesContent":["import type { ManifestQueryClient } from '@manifest-network/manifest-mcp-core';\nimport {\n ManifestMCPError,\n ManifestMCPErrorCode,\n} from '@manifest-network/manifest-mcp-core';\nimport { updateLease } from '../http/fred.js';\nimport {\n isStackManifest,\n mergeManifest,\n validateServiceName,\n} from '../manifest.js';\nimport { fetchActiveLease } from './fetchActiveLease.js';\nimport { resolveProviderUrl } from './resolveLeaseProvider.js';\n\nexport async function updateApp(\n queryClient: ManifestQueryClient,\n address: string,\n leaseUuid: string,\n getAuthToken: (address: string, leaseUuid: string) => Promise<string>,\n manifest: string,\n existingManifest?: string,\n fetchFn?: typeof globalThis.fetch,\n) {\n const lease = await fetchActiveLease(\n queryClient,\n leaseUuid,\n 'cannot be updated',\n );\n\n let finalManifest = manifest;\n if (existingManifest) {\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(manifest) as Record<string, unknown>;\n } catch (err) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n `Invalid manifest JSON: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n if (isStackManifest(parsed)) {\n for (const name of Object.keys(parsed.services)) {\n if (!validateServiceName(name)) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n `Invalid service name: \"${name}\". Must be 1-63 chars, lowercase alphanumeric + hyphens, no leading/trailing hyphens.`,\n );\n }\n }\n // Per-service merge: merge each service independently\n let oldParsed: unknown;\n try {\n oldParsed = JSON.parse(existingManifest);\n } catch (err) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n `Invalid existing_manifest: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n if (!isStackManifest(oldParsed)) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n 'Cannot merge: new manifest is a stack but existing_manifest is not. Provide a stack-format existing_manifest or omit existing_manifest for full replacement.',\n );\n }\n const mergedStack: Record<string, unknown> = {};\n for (const [svc, svcManifest] of Object.entries(parsed.services)) {\n const oldSvcJson = oldParsed.services[svc]\n ? JSON.stringify(oldParsed.services[svc])\n : '{}';\n mergedStack[svc] = mergeManifest(\n svcManifest as Record<string, unknown>,\n oldSvcJson,\n );\n }\n finalManifest = JSON.stringify({ services: mergedStack });\n } else {\n try {\n const merged = mergeManifest(parsed, existingManifest);\n finalManifest = JSON.stringify(merged);\n } catch (err) {\n if (err instanceof ManifestMCPError) throw err;\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n `Invalid existing_manifest: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n }\n\n const providerUrl = await resolveProviderUrl(queryClient, lease.providerUuid);\n const authToken = await getAuthToken(address, leaseUuid);\n const result = await updateLease(\n providerUrl,\n leaseUuid,\n new TextEncoder().encode(finalManifest),\n authToken,\n fetchFn,\n );\n\n return {\n lease_uuid: leaseUuid,\n status: result.status,\n };\n}\n"],"mappings":";;;;;;AAcA,eAAsB,UACpB,aACA,SACA,WACA,cACA,UACA,kBACA,SACA;CACA,MAAM,QAAQ,MAAM,iBAClB,aACA,WACA,oBACD;CAED,IAAI,gBAAgB;AACpB,KAAI,kBAAkB;EACpB,IAAI;AACJ,MAAI;AACF,YAAS,KAAK,MAAM,SAAS;WACtB,KAAK;AACZ,SAAM,IAAI,iBACR,qBAAqB,gBACrB,0BAA0B,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAC3E;;AAEH,MAAI,gBAAgB,OAAO,EAAE;AAC3B,QAAK,MAAM,QAAQ,OAAO,KAAK,OAAO,SAAS,CAC7C,KAAI,CAAC,oBAAoB,KAAK,CAC5B,OAAM,IAAI,iBACR,qBAAqB,gBACrB,0BAA0B,KAAK,uFAChC;GAIL,IAAI;AACJ,OAAI;AACF,gBAAY,KAAK,MAAM,iBAAiB;YACjC,KAAK;AACZ,UAAM,IAAI,iBACR,qBAAqB,gBACrB,8BAA8B,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAC/E;;AAEH,OAAI,CAAC,gBAAgB,UAAU,CAC7B,OAAM,IAAI,iBACR,qBAAqB,gBACrB,+JACD;GAEH,MAAM,cAAuC,EAAE;AAC/C,QAAK,MAAM,CAAC,KAAK,gBAAgB,OAAO,QAAQ,OAAO,SAAS,CAI9D,aAAY,OAAO,cACjB,aAJiB,UAAU,SAAS,OAClC,KAAK,UAAU,UAAU,SAAS,KAAK,GACvC,KAIH;AAEH,mBAAgB,KAAK,UAAU,EAAE,UAAU,aAAa,CAAC;QAEzD,KAAI;GACF,MAAM,SAAS,cAAc,QAAQ,iBAAiB;AACtD,mBAAgB,KAAK,UAAU,OAAO;WAC/B,KAAK;AACZ,OAAI,eAAe,iBAAkB,OAAM;AAC3C,SAAM,IAAI,iBACR,qBAAqB,gBACrB,8BAA8B,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAC/E;;;CAKP,MAAM,cAAc,MAAM,mBAAmB,aAAa,MAAM,aAAa;CAC7E,MAAM,YAAY,MAAM,aAAa,SAAS,UAAU;AASxD,QAAO;EACL,YAAY;EACZ,SAVa,MAAM,YACnB,aACA,WACA,IAAI,aAAa,CAAC,OAAO,cAAc,EACvC,WACA,QACD,EAIgB;EAChB"}