@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.
- package/dist/http/auth.d.ts +15 -0
- package/dist/http/auth.d.ts.map +1 -0
- package/dist/http/auth.js +23 -0
- package/dist/http/auth.js.map +1 -0
- package/dist/http/fred.d.ts +74 -0
- package/dist/http/fred.d.ts.map +1 -0
- package/dist/http/fred.js +94 -0
- package/dist/http/fred.js.map +1 -0
- package/dist/http/provider.d.ts +52 -0
- package/dist/http/provider.d.ts.map +1 -0
- package/dist/http/provider.js +88 -0
- package/dist/http/provider.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +211 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.d.ts +44 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +122 -0
- package/dist/manifest.js.map +1 -0
- package/dist/tools/appStatus.d.ts +21 -0
- package/dist/tools/appStatus.d.ts.map +1 -0
- package/dist/tools/appStatus.js +73 -0
- package/dist/tools/appStatus.js.map +1 -0
- package/dist/tools/browseCatalog.d.ts +27 -0
- package/dist/tools/browseCatalog.d.ts.map +1 -0
- package/dist/tools/browseCatalog.js +76 -0
- package/dist/tools/browseCatalog.js.map +1 -0
- package/dist/tools/deployApp.d.ts +65 -0
- package/dist/tools/deployApp.d.ts.map +1 -0
- package/dist/tools/deployApp.js +143 -0
- package/dist/tools/deployApp.js.map +1 -0
- package/dist/tools/fetchActiveLease.d.ts +12 -0
- package/dist/tools/fetchActiveLease.d.ts.map +1 -0
- package/dist/tools/fetchActiveLease.js +17 -0
- package/dist/tools/fetchActiveLease.js.map +1 -0
- package/dist/tools/getLogs.d.ts +11 -0
- package/dist/tools/getLogs.d.ts.map +1 -0
- package/dist/tools/getLogs.js +32 -0
- package/dist/tools/getLogs.js.map +1 -0
- package/dist/tools/resolveLeaseProvider.d.ts +7 -0
- package/dist/tools/resolveLeaseProvider.d.ts.map +1 -0
- package/dist/tools/resolveLeaseProvider.js +20 -0
- package/dist/tools/resolveLeaseProvider.js.map +1 -0
- package/dist/tools/restartApp.d.ts +10 -0
- package/dist/tools/restartApp.d.ts.map +1 -0
- package/dist/tools/restartApp.js +14 -0
- package/dist/tools/restartApp.js.map +1 -0
- package/dist/tools/updateApp.d.ts +10 -0
- package/dist/tools/updateApp.d.ts.map +1 -0
- package/dist/tools/updateApp.js +47 -0
- package/dist/tools/updateApp.js.map +1 -0
- package/package.json +59 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { createAuthToken, createLeaseDataSignMessage, createSignMessage } from "./http/auth.js";
|
|
2
|
+
import { ProviderApiError, checkedFetch, getLeaseConnectionInfo, getProviderHealth, uploadLeaseData, validateProviderUrl } from "./http/provider.js";
|
|
3
|
+
import { MAX_TAIL, getLeaseInfo, getLeaseLogs, getLeaseProvision, getLeaseReleases, getLeaseStatus, pollLeaseUntilReady, restartLease, updateLease } from "./http/fred.js";
|
|
4
|
+
import { resolveProviderUrl } from "./tools/resolveLeaseProvider.js";
|
|
5
|
+
import { appStatus } from "./tools/appStatus.js";
|
|
6
|
+
import { browseCatalog, mapWithConcurrency } from "./tools/browseCatalog.js";
|
|
7
|
+
import { buildManifest, buildStackManifest, deriveAppNameFromImage, getServiceNames, isStackManifest, mergeManifest, normalizePorts, parseStackManifest, validateServiceName } from "./manifest.js";
|
|
8
|
+
import { deployApp } from "./tools/deployApp.js";
|
|
9
|
+
import { fetchActiveLease } from "./tools/fetchActiveLease.js";
|
|
10
|
+
import { getAppLogs } from "./tools/getLogs.js";
|
|
11
|
+
import { restartApp } from "./tools/restartApp.js";
|
|
12
|
+
import { updateApp } from "./tools/updateApp.js";
|
|
13
|
+
import { CosmosClientManager, INFRASTRUCTURE_ERROR_CODES, ManifestMCPError, ManifestMCPError as ManifestMCPError$1, ManifestMCPErrorCode, ManifestMCPErrorCode as ManifestMCPErrorCode$1, VERSION, bigIntReplacer, createMnemonicServer, createValidatedConfig, jsonResponse, withErrorHandling } from "@manifest-network/manifest-mcp-core";
|
|
14
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
//#region src/index.ts
|
|
17
|
+
var FredMCPServer = class {
|
|
18
|
+
constructor(options) {
|
|
19
|
+
const config = createValidatedConfig(options.config);
|
|
20
|
+
this.walletProvider = options.walletProvider;
|
|
21
|
+
this.clientManager = CosmosClientManager.getInstance(config, this.walletProvider);
|
|
22
|
+
this.mcpServer = new McpServer({
|
|
23
|
+
name: "@manifest-network/manifest-mcp-fred",
|
|
24
|
+
version: VERSION
|
|
25
|
+
}, { capabilities: { tools: {} } });
|
|
26
|
+
this.registerTools();
|
|
27
|
+
}
|
|
28
|
+
requireSignArbitrary() {
|
|
29
|
+
if (!this.walletProvider.signArbitrary) throw new ManifestMCPError$1(ManifestMCPErrorCode$1.INVALID_CONFIG, "Wallet does not support signArbitrary (ADR-036). Required for provider authentication. Use a wallet provider that implements signArbitrary.");
|
|
30
|
+
return this.walletProvider.signArbitrary.bind(this.walletProvider);
|
|
31
|
+
}
|
|
32
|
+
async getProviderAuthToken(address, leaseUuid) {
|
|
33
|
+
const signArbitrary = this.requireSignArbitrary();
|
|
34
|
+
const timestamp = Math.floor(Date.now() / 1e3);
|
|
35
|
+
const { pub_key, signature } = await signArbitrary(address, createSignMessage(address, leaseUuid, timestamp));
|
|
36
|
+
return createAuthToken(address, leaseUuid, timestamp, pub_key.value, signature);
|
|
37
|
+
}
|
|
38
|
+
async getLeaseDataAuthToken(address, leaseUuid, metaHashHex) {
|
|
39
|
+
const signArbitrary = this.requireSignArbitrary();
|
|
40
|
+
const timestamp = Math.floor(Date.now() / 1e3);
|
|
41
|
+
const { pub_key, signature } = await signArbitrary(address, createLeaseDataSignMessage(leaseUuid, metaHashHex, timestamp));
|
|
42
|
+
return createAuthToken(address, leaseUuid, timestamp, pub_key.value, signature, metaHashHex);
|
|
43
|
+
}
|
|
44
|
+
registerTools() {
|
|
45
|
+
this.mcpServer.registerTool("browse_catalog", { description: "Browse available cloud providers and service tiers with live health checks. Use this before deploy_app to see which providers are online and what SKU sizes (e.g. docker-micro, docker-small) are available with pricing." }, withErrorHandling("browse_catalog", async () => {
|
|
46
|
+
await this.clientManager.acquireRateLimit();
|
|
47
|
+
return jsonResponse(await browseCatalog(await this.clientManager.getQueryClient()), bigIntReplacer);
|
|
48
|
+
}));
|
|
49
|
+
this.mcpServer.registerTool("app_status", {
|
|
50
|
+
description: "Get detailed status and connection info for a deployed app. Use this after deploy_app to check if an app is running and get its URL.",
|
|
51
|
+
inputSchema: { lease_uuid: z.string().uuid().describe("The lease UUID of the app to check") }
|
|
52
|
+
}, withErrorHandling("app_status", async (args) => {
|
|
53
|
+
const leaseUuid = args.lease_uuid;
|
|
54
|
+
const address = await this.walletProvider.getAddress();
|
|
55
|
+
await this.clientManager.acquireRateLimit();
|
|
56
|
+
return jsonResponse(await appStatus(await this.clientManager.getQueryClient(), address, leaseUuid, (addr, uuid) => this.getProviderAuthToken(addr, uuid)), bigIntReplacer);
|
|
57
|
+
}));
|
|
58
|
+
this.mcpServer.registerTool("get_logs", {
|
|
59
|
+
description: "Get recent container logs for a deployed app. Use this to debug apps that are failing or to verify an app started correctly after deploy_app.",
|
|
60
|
+
inputSchema: {
|
|
61
|
+
lease_uuid: z.string().uuid().describe("The lease UUID of the app to get logs for"),
|
|
62
|
+
tail: z.number().int().min(1).max(MAX_TAIL).optional().describe("Number of recent log lines to retrieve")
|
|
63
|
+
}
|
|
64
|
+
}, withErrorHandling("get_logs", async (args) => {
|
|
65
|
+
const leaseUuid = args.lease_uuid;
|
|
66
|
+
const tail = args.tail;
|
|
67
|
+
const address = await this.walletProvider.getAddress();
|
|
68
|
+
await this.clientManager.acquireRateLimit();
|
|
69
|
+
return jsonResponse(await getAppLogs(await this.clientManager.getQueryClient(), address, leaseUuid, (addr, uuid) => this.getProviderAuthToken(addr, uuid), tail), bigIntReplacer);
|
|
70
|
+
}));
|
|
71
|
+
this.mcpServer.registerTool("deploy_app", {
|
|
72
|
+
description: "Deploy a new containerized application. Requires funded credits (use fund_credit if needed). Creates a lease on-chain, uploads the container manifest to a provider, and polls until ready. Use browse_catalog first to see available SKU sizes.",
|
|
73
|
+
inputSchema: {
|
|
74
|
+
image: z.string().optional().describe("Docker image to deploy. Required unless services is provided."),
|
|
75
|
+
port: z.number().int().min(1).max(65535).optional().describe("Container port to expose. Required unless services is provided."),
|
|
76
|
+
size: z.string().describe("SKU tier name (e.g. \"docker-micro\", \"docker-small\")"),
|
|
77
|
+
env: z.record(z.string(), z.string()).optional().describe("Environment variables as key-value pairs"),
|
|
78
|
+
command: z.array(z.string()).optional().describe("Override container command (entrypoint)"),
|
|
79
|
+
args: z.array(z.string()).optional().describe("Arguments to the container command"),
|
|
80
|
+
user: z.string().optional().describe("User to run the container as (e.g. \"1000:1000\")"),
|
|
81
|
+
tmpfs: z.array(z.string()).optional().describe("tmpfs mounts (e.g. [\"/tmp:size=64M\"])"),
|
|
82
|
+
health_check: z.object({
|
|
83
|
+
test: z.array(z.string()),
|
|
84
|
+
interval: z.string().optional(),
|
|
85
|
+
timeout: z.string().optional(),
|
|
86
|
+
retries: z.number().int().optional(),
|
|
87
|
+
start_period: z.string().optional()
|
|
88
|
+
}).optional().describe("Container health check configuration"),
|
|
89
|
+
stop_grace_period: z.string().optional().describe("Grace period before force-killing (e.g. \"30s\")"),
|
|
90
|
+
init: z.boolean().optional().describe("Run an init process inside the container"),
|
|
91
|
+
expose: z.array(z.string()).optional().describe("Expose ports without publishing (e.g. [\"8080/tcp\"])"),
|
|
92
|
+
labels: z.record(z.string(), z.string()).optional().describe("Container labels as key-value pairs"),
|
|
93
|
+
storage: z.string().optional().describe("Storage SKU name for persistent disk (adds a second lease item)"),
|
|
94
|
+
depends_on: z.record(z.string(), z.object({ condition: z.string() })).optional().describe("Service dependencies"),
|
|
95
|
+
services: z.record(z.string(), z.object({
|
|
96
|
+
image: z.string(),
|
|
97
|
+
ports: z.record(z.string(), z.object({})).optional(),
|
|
98
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
99
|
+
command: z.array(z.string()).optional(),
|
|
100
|
+
args: z.array(z.string()).optional(),
|
|
101
|
+
user: z.string().optional(),
|
|
102
|
+
tmpfs: z.array(z.string()).optional(),
|
|
103
|
+
health_check: z.object({
|
|
104
|
+
test: z.array(z.string()),
|
|
105
|
+
interval: z.string().optional(),
|
|
106
|
+
timeout: z.string().optional(),
|
|
107
|
+
retries: z.number().int().optional(),
|
|
108
|
+
start_period: z.string().optional()
|
|
109
|
+
}).optional(),
|
|
110
|
+
stop_grace_period: z.string().optional(),
|
|
111
|
+
depends_on: z.record(z.string(), z.object({ condition: z.string() })).optional(),
|
|
112
|
+
expose: z.array(z.string()).optional(),
|
|
113
|
+
labels: z.record(z.string(), z.string()).optional()
|
|
114
|
+
})).optional().describe("Multi-service stack. Mutually exclusive with image/port. Keys are service names (RFC 1123 DNS labels).")
|
|
115
|
+
}
|
|
116
|
+
}, withErrorHandling("deploy_app", async (args) => {
|
|
117
|
+
return jsonResponse(await deployApp(this.clientManager, (addr, uuid) => this.getProviderAuthToken(addr, uuid), (addr, uuid, metaHashHex) => this.getLeaseDataAuthToken(addr, uuid, metaHashHex), {
|
|
118
|
+
image: args.image,
|
|
119
|
+
port: args.port,
|
|
120
|
+
size: args.size,
|
|
121
|
+
env: args.env,
|
|
122
|
+
command: args.command,
|
|
123
|
+
args: args.args,
|
|
124
|
+
user: args.user,
|
|
125
|
+
tmpfs: args.tmpfs,
|
|
126
|
+
health_check: args.health_check,
|
|
127
|
+
stop_grace_period: args.stop_grace_period,
|
|
128
|
+
init: args.init,
|
|
129
|
+
expose: args.expose,
|
|
130
|
+
labels: args.labels,
|
|
131
|
+
storage: args.storage,
|
|
132
|
+
depends_on: args.depends_on,
|
|
133
|
+
services: args.services
|
|
134
|
+
}), bigIntReplacer);
|
|
135
|
+
}));
|
|
136
|
+
this.mcpServer.registerTool("restart_app", {
|
|
137
|
+
description: "Restart a running app via the provider without closing its lease. Use this to apply configuration changes or recover from a crash.",
|
|
138
|
+
inputSchema: { lease_uuid: z.string().uuid().describe("The lease UUID of the app to restart") }
|
|
139
|
+
}, withErrorHandling("restart_app", async (args) => {
|
|
140
|
+
const leaseUuid = args.lease_uuid;
|
|
141
|
+
const address = await this.walletProvider.getAddress();
|
|
142
|
+
await this.clientManager.acquireRateLimit();
|
|
143
|
+
return jsonResponse(await restartApp(await this.clientManager.getQueryClient(), address, leaseUuid, (addr, uuid) => this.getProviderAuthToken(addr, uuid)), bigIntReplacer);
|
|
144
|
+
}));
|
|
145
|
+
this.mcpServer.registerTool("update_app", {
|
|
146
|
+
description: "Update a deployed app with a new container manifest. Use this to change the Docker image, ports, or environment variables of a running app without closing the lease.",
|
|
147
|
+
inputSchema: {
|
|
148
|
+
lease_uuid: z.string().uuid().describe("The lease UUID of the app to update"),
|
|
149
|
+
manifest: z.string().describe("The full manifest JSON string to deploy"),
|
|
150
|
+
existing_manifest: z.string().optional().describe("The current manifest JSON. When provided, the new manifest is merged over the existing one (env, ports, labels merged; other fields carried forward if not in new).")
|
|
151
|
+
}
|
|
152
|
+
}, withErrorHandling("update_app", async (args) => {
|
|
153
|
+
const manifest = args.manifest;
|
|
154
|
+
try {
|
|
155
|
+
const parsed = JSON.parse(manifest);
|
|
156
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("must be a JSON object");
|
|
157
|
+
} catch (err) {
|
|
158
|
+
throw new ManifestMCPError$1(ManifestMCPErrorCode$1.INVALID_CONFIG, `Invalid manifest: ${err instanceof Error ? err.message : String(err)}`);
|
|
159
|
+
}
|
|
160
|
+
const leaseUuid = args.lease_uuid;
|
|
161
|
+
const address = await this.walletProvider.getAddress();
|
|
162
|
+
await this.clientManager.acquireRateLimit();
|
|
163
|
+
return jsonResponse(await updateApp(await this.clientManager.getQueryClient(), address, leaseUuid, (addr, uuid) => this.getProviderAuthToken(addr, uuid), manifest, args.existing_manifest), bigIntReplacer);
|
|
164
|
+
}));
|
|
165
|
+
this.mcpServer.registerTool("app_diagnostics", {
|
|
166
|
+
description: "Get provision diagnostics for a deployed app. Use this to debug apps stuck in provisioning or that failed to start. Returns provision status, failure count, and last error message.",
|
|
167
|
+
inputSchema: { lease_uuid: z.string().uuid().describe("The lease UUID of the app to diagnose") }
|
|
168
|
+
}, withErrorHandling("app_diagnostics", async (args) => {
|
|
169
|
+
const leaseUuid = args.lease_uuid;
|
|
170
|
+
const address = await this.walletProvider.getAddress();
|
|
171
|
+
await this.clientManager.acquireRateLimit();
|
|
172
|
+
const queryClient = await this.clientManager.getQueryClient();
|
|
173
|
+
const provision = await getLeaseProvision(await resolveProviderUrl(queryClient, (await fetchActiveLease(queryClient, leaseUuid, "cannot be diagnosed")).providerUuid), leaseUuid, await this.getProviderAuthToken(address, leaseUuid));
|
|
174
|
+
return jsonResponse({
|
|
175
|
+
lease_uuid: leaseUuid,
|
|
176
|
+
provision_status: provision.status,
|
|
177
|
+
fail_count: provision.fail_count,
|
|
178
|
+
last_error: provision.last_error
|
|
179
|
+
}, bigIntReplacer);
|
|
180
|
+
}));
|
|
181
|
+
this.mcpServer.registerTool("app_releases", {
|
|
182
|
+
description: "Get release/version history for a deployed app. Use this to see what versions have been deployed, when they were created, and their status.",
|
|
183
|
+
inputSchema: { lease_uuid: z.string().uuid().describe("The lease UUID of the app to get release history for") }
|
|
184
|
+
}, withErrorHandling("app_releases", async (args) => {
|
|
185
|
+
const leaseUuid = args.lease_uuid;
|
|
186
|
+
const address = await this.walletProvider.getAddress();
|
|
187
|
+
await this.clientManager.acquireRateLimit();
|
|
188
|
+
const queryClient = await this.clientManager.getQueryClient();
|
|
189
|
+
return jsonResponse({
|
|
190
|
+
lease_uuid: leaseUuid,
|
|
191
|
+
releases: (await getLeaseReleases(await resolveProviderUrl(queryClient, (await fetchActiveLease(queryClient, leaseUuid, "releases are not available")).providerUuid), leaseUuid, await this.getProviderAuthToken(address, leaseUuid))).releases
|
|
192
|
+
}, bigIntReplacer);
|
|
193
|
+
}));
|
|
194
|
+
}
|
|
195
|
+
getServer() {
|
|
196
|
+
return this.mcpServer.server;
|
|
197
|
+
}
|
|
198
|
+
getClientManager() {
|
|
199
|
+
return this.clientManager;
|
|
200
|
+
}
|
|
201
|
+
disconnect() {
|
|
202
|
+
this.clientManager.disconnect();
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
function createMnemonicFredServer(config) {
|
|
206
|
+
return createMnemonicServer(config, FredMCPServer);
|
|
207
|
+
}
|
|
208
|
+
//#endregion
|
|
209
|
+
export { FredMCPServer, INFRASTRUCTURE_ERROR_CODES, MAX_TAIL, ManifestMCPError, ManifestMCPErrorCode, ProviderApiError, appStatus, browseCatalog, buildManifest, buildStackManifest, checkedFetch, createAuthToken, createLeaseDataSignMessage, createMnemonicFredServer, createSignMessage, deployApp, deriveAppNameFromImage, fetchActiveLease, getAppLogs, getLeaseConnectionInfo, getLeaseInfo, getLeaseLogs, getLeaseProvision, getLeaseReleases, getLeaseStatus, getProviderHealth, getServiceNames, isStackManifest, mapWithConcurrency, mergeManifest, normalizePorts, parseStackManifest, pollLeaseUntilReady, resolveProviderUrl, restartApp, restartLease, updateApp, updateLease, uploadLeaseData, validateProviderUrl, validateServiceName };
|
|
210
|
+
|
|
211
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["ManifestMCPError","ManifestMCPErrorCode"],"sources":["../src/index.ts"],"sourcesContent":["import type { WalletProvider } from '@manifest-network/manifest-mcp-core';\nimport {\n bigIntReplacer,\n CosmosClientManager,\n createMnemonicServer,\n createValidatedConfig,\n jsonResponse,\n ManifestMCPError,\n ManifestMCPErrorCode,\n type ManifestMCPServerOptions,\n type MnemonicServerConfig,\n VERSION,\n withErrorHandling,\n} from '@manifest-network/manifest-mcp-core';\nimport type { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport {\n createAuthToken,\n createLeaseDataSignMessage,\n createSignMessage,\n} from './http/auth.js';\nimport { getLeaseProvision, getLeaseReleases, MAX_TAIL } from './http/fred.js';\nimport { appStatus } from './tools/appStatus.js';\nimport { browseCatalog } from './tools/browseCatalog.js';\nimport { deployApp } from './tools/deployApp.js';\nimport { fetchActiveLease } from './tools/fetchActiveLease.js';\nimport { getAppLogs } from './tools/getLogs.js';\nimport { resolveProviderUrl } from './tools/resolveLeaseProvider.js';\nimport { restartApp } from './tools/restartApp.js';\nimport { updateApp } from './tools/updateApp.js';\n\nexport type { ManifestMCPServerOptions } from '@manifest-network/manifest-mcp-core';\nexport {\n INFRASTRUCTURE_ERROR_CODES,\n ManifestMCPError,\n ManifestMCPErrorCode,\n} from '@manifest-network/manifest-mcp-core';\nexport {\n type AuthTokenPayload,\n createAuthToken,\n createLeaseDataSignMessage,\n createSignMessage,\n} from './http/auth.js';\nexport {\n type FredActionResponse,\n type FredInstanceInfo,\n type FredLeaseInfo,\n type FredLeaseLogs,\n type FredLeaseProvision,\n type FredLeaseRelease,\n type FredLeaseReleases,\n type FredLeaseStatus,\n type FredServiceStatus,\n getLeaseInfo,\n getLeaseLogs,\n getLeaseProvision,\n getLeaseReleases,\n getLeaseStatus,\n MAX_TAIL,\n type PollOptions,\n pollLeaseUntilReady,\n restartLease,\n updateLease,\n} from './http/fred.js';\nexport {\n type ConnectionDetails,\n checkedFetch,\n getLeaseConnectionInfo,\n getProviderHealth,\n type InstanceInfo,\n type LeaseConnectionResponse,\n ProviderApiError,\n type ProviderHealthResponse,\n type ServiceConnectionDetails,\n uploadLeaseData,\n validateProviderUrl,\n} from './http/provider.js';\nexport {\n type BuildManifestOptions,\n buildManifest,\n buildStackManifest,\n deriveAppNameFromImage,\n getServiceNames,\n isStackManifest,\n mergeManifest,\n normalizePorts,\n parseStackManifest,\n validateServiceName,\n} from './manifest.js';\nexport { appStatus } from './tools/appStatus.js';\nexport { browseCatalog, mapWithConcurrency } from './tools/browseCatalog.js';\nexport {\n type DeployAppInput,\n type DeployAppResult,\n deployApp,\n type ServiceConfig,\n} from './tools/deployApp.js';\nexport { fetchActiveLease } from './tools/fetchActiveLease.js';\nexport { getAppLogs } from './tools/getLogs.js';\nexport { resolveProviderUrl } from './tools/resolveLeaseProvider.js';\nexport { restartApp } from './tools/restartApp.js';\nexport { updateApp } from './tools/updateApp.js';\n\nexport class FredMCPServer {\n private mcpServer: McpServer;\n private clientManager: CosmosClientManager;\n private walletProvider: WalletProvider;\n\n constructor(options: ManifestMCPServerOptions) {\n const config = createValidatedConfig(options.config);\n this.walletProvider = options.walletProvider;\n this.clientManager = CosmosClientManager.getInstance(\n config,\n this.walletProvider,\n );\n\n this.mcpServer = new McpServer(\n {\n name: '@manifest-network/manifest-mcp-fred',\n version: VERSION,\n },\n {\n capabilities: {\n tools: {},\n },\n },\n );\n\n this.registerTools();\n }\n\n private requireSignArbitrary(): NonNullable<WalletProvider['signArbitrary']> {\n if (!this.walletProvider.signArbitrary) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n 'Wallet does not support signArbitrary (ADR-036). Required for provider authentication. Use a wallet provider that implements signArbitrary.',\n );\n }\n return this.walletProvider.signArbitrary.bind(this.walletProvider);\n }\n\n private async getProviderAuthToken(\n address: string,\n leaseUuid: string,\n ): Promise<string> {\n const signArbitrary = this.requireSignArbitrary();\n const timestamp = Math.floor(Date.now() / 1000);\n const message = createSignMessage(address, leaseUuid, timestamp);\n const { pub_key, signature } = await signArbitrary(address, message);\n return createAuthToken(\n address,\n leaseUuid,\n timestamp,\n pub_key.value,\n signature,\n );\n }\n\n private async getLeaseDataAuthToken(\n address: string,\n leaseUuid: string,\n metaHashHex: string,\n ): Promise<string> {\n const signArbitrary = this.requireSignArbitrary();\n const timestamp = Math.floor(Date.now() / 1000);\n const message = createLeaseDataSignMessage(\n leaseUuid,\n metaHashHex,\n timestamp,\n );\n const { pub_key, signature } = await signArbitrary(address, message);\n return createAuthToken(\n address,\n leaseUuid,\n timestamp,\n pub_key.value,\n signature,\n metaHashHex,\n );\n }\n\n private registerTools(): void {\n // -- browse_catalog --\n this.mcpServer.registerTool(\n 'browse_catalog',\n {\n description:\n 'Browse available cloud providers and service tiers with live health checks. Use this before deploy_app to see which providers are online and what SKU sizes (e.g. docker-micro, docker-small) are available with pricing.',\n },\n withErrorHandling('browse_catalog', async () => {\n await this.clientManager.acquireRateLimit();\n const queryClient = await this.clientManager.getQueryClient();\n const result = await browseCatalog(queryClient);\n return jsonResponse(result, bigIntReplacer);\n }),\n );\n\n // -- app_status --\n this.mcpServer.registerTool(\n 'app_status',\n {\n description:\n 'Get detailed status and connection info for a deployed app. Use this after deploy_app to check if an app is running and get its URL.',\n inputSchema: {\n lease_uuid: z\n .string()\n .uuid()\n .describe('The lease UUID of the app to check'),\n },\n },\n withErrorHandling('app_status', async (args) => {\n const leaseUuid = args.lease_uuid;\n const address = await this.walletProvider.getAddress();\n await this.clientManager.acquireRateLimit();\n const queryClient = await this.clientManager.getQueryClient();\n const result = await appStatus(\n queryClient,\n address,\n leaseUuid,\n (addr, uuid) => this.getProviderAuthToken(addr, uuid),\n );\n return jsonResponse(result, bigIntReplacer);\n }),\n );\n\n // -- get_logs --\n this.mcpServer.registerTool(\n 'get_logs',\n {\n description:\n 'Get recent container logs for a deployed app. Use this to debug apps that are failing or to verify an app started correctly after deploy_app.',\n inputSchema: {\n lease_uuid: z\n .string()\n .uuid()\n .describe('The lease UUID of the app to get logs for'),\n tail: z\n .number()\n .int()\n .min(1)\n .max(MAX_TAIL)\n .optional()\n .describe('Number of recent log lines to retrieve'),\n },\n },\n withErrorHandling('get_logs', async (args) => {\n const leaseUuid = args.lease_uuid;\n const tail = args.tail;\n const address = await this.walletProvider.getAddress();\n await this.clientManager.acquireRateLimit();\n const queryClient = await this.clientManager.getQueryClient();\n const result = await getAppLogs(\n queryClient,\n address,\n leaseUuid,\n (addr, uuid) => this.getProviderAuthToken(addr, uuid),\n tail,\n );\n return jsonResponse(result, bigIntReplacer);\n }),\n );\n\n // -- deploy_app --\n this.mcpServer.registerTool(\n 'deploy_app',\n {\n description:\n 'Deploy a new containerized application. Requires funded credits (use fund_credit if needed). Creates a lease on-chain, uploads the container manifest to a provider, and polls until ready. Use browse_catalog first to see available SKU sizes.',\n inputSchema: {\n image: z\n .string()\n .optional()\n .describe(\n 'Docker image to deploy. Required unless services is provided.',\n ),\n port: z\n .number()\n .int()\n .min(1)\n .max(65535)\n .optional()\n .describe(\n 'Container port to expose. Required unless services is provided.',\n ),\n size: z\n .string()\n .describe('SKU tier name (e.g. \"docker-micro\", \"docker-small\")'),\n env: z\n .record(z.string(), z.string())\n .optional()\n .describe('Environment variables as key-value pairs'),\n command: z\n .array(z.string())\n .optional()\n .describe('Override container command (entrypoint)'),\n args: z\n .array(z.string())\n .optional()\n .describe('Arguments to the container command'),\n user: z\n .string()\n .optional()\n .describe('User to run the container as (e.g. \"1000:1000\")'),\n tmpfs: z\n .array(z.string())\n .optional()\n .describe('tmpfs mounts (e.g. [\"/tmp:size=64M\"])'),\n health_check: z\n .object({\n test: z.array(z.string()),\n interval: z.string().optional(),\n timeout: z.string().optional(),\n retries: z.number().int().optional(),\n start_period: z.string().optional(),\n })\n .optional()\n .describe('Container health check configuration'),\n stop_grace_period: z\n .string()\n .optional()\n .describe('Grace period before force-killing (e.g. \"30s\")'),\n init: z\n .boolean()\n .optional()\n .describe('Run an init process inside the container'),\n expose: z\n .array(z.string())\n .optional()\n .describe('Expose ports without publishing (e.g. [\"8080/tcp\"])'),\n labels: z\n .record(z.string(), z.string())\n .optional()\n .describe('Container labels as key-value pairs'),\n storage: z\n .string()\n .optional()\n .describe(\n 'Storage SKU name for persistent disk (adds a second lease item)',\n ),\n depends_on: z\n .record(z.string(), z.object({ condition: z.string() }))\n .optional()\n .describe('Service dependencies'),\n services: z\n .record(\n z.string(),\n z.object({\n image: z.string(),\n ports: z.record(z.string(), z.object({})).optional(),\n env: z.record(z.string(), z.string()).optional(),\n command: z.array(z.string()).optional(),\n args: z.array(z.string()).optional(),\n user: z.string().optional(),\n tmpfs: z.array(z.string()).optional(),\n health_check: z\n .object({\n test: z.array(z.string()),\n interval: z.string().optional(),\n timeout: z.string().optional(),\n retries: z.number().int().optional(),\n start_period: z.string().optional(),\n })\n .optional(),\n stop_grace_period: z.string().optional(),\n depends_on: z\n .record(z.string(), z.object({ condition: z.string() }))\n .optional(),\n expose: z.array(z.string()).optional(),\n labels: z.record(z.string(), z.string()).optional(),\n }),\n )\n .optional()\n .describe(\n 'Multi-service stack. Mutually exclusive with image/port. Keys are service names (RFC 1123 DNS labels).',\n ),\n },\n },\n withErrorHandling('deploy_app', async (args) => {\n const result = await deployApp(\n this.clientManager,\n (addr, uuid) => this.getProviderAuthToken(addr, uuid),\n (addr, uuid, metaHashHex) =>\n this.getLeaseDataAuthToken(addr, uuid, metaHashHex),\n {\n image: args.image,\n port: args.port,\n size: args.size,\n env: args.env,\n command: args.command,\n args: args.args,\n user: args.user,\n tmpfs: args.tmpfs,\n health_check: args.health_check,\n stop_grace_period: args.stop_grace_period,\n init: args.init,\n expose: args.expose,\n labels: args.labels,\n storage: args.storage,\n depends_on: args.depends_on,\n services: args.services,\n },\n );\n return jsonResponse(result, bigIntReplacer);\n }),\n );\n\n // -- restart_app --\n this.mcpServer.registerTool(\n 'restart_app',\n {\n description:\n 'Restart a running app via the provider without closing its lease. Use this to apply configuration changes or recover from a crash.',\n inputSchema: {\n lease_uuid: z\n .string()\n .uuid()\n .describe('The lease UUID of the app to restart'),\n },\n },\n withErrorHandling('restart_app', async (args) => {\n const leaseUuid = args.lease_uuid;\n const address = await this.walletProvider.getAddress();\n await this.clientManager.acquireRateLimit();\n const queryClient = await this.clientManager.getQueryClient();\n const result = await restartApp(\n queryClient,\n address,\n leaseUuid,\n (addr, uuid) => this.getProviderAuthToken(addr, uuid),\n );\n return jsonResponse(result, bigIntReplacer);\n }),\n );\n\n // -- update_app --\n this.mcpServer.registerTool(\n 'update_app',\n {\n description:\n 'Update a deployed app with a new container manifest. Use this to change the Docker image, ports, or environment variables of a running app without closing the lease.',\n inputSchema: {\n lease_uuid: z\n .string()\n .uuid()\n .describe('The lease UUID of the app to update'),\n manifest: z\n .string()\n .describe('The full manifest JSON string to deploy'),\n existing_manifest: z\n .string()\n .optional()\n .describe(\n 'The current manifest JSON. When provided, the new manifest is merged over the existing one (env, ports, labels merged; other fields carried forward if not in new).',\n ),\n },\n },\n withErrorHandling('update_app', async (args) => {\n const manifest = args.manifest;\n\n try {\n const parsed = JSON.parse(manifest);\n if (\n parsed === null ||\n typeof parsed !== 'object' ||\n Array.isArray(parsed)\n ) {\n throw new Error('must be a JSON object');\n }\n } catch (err) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n `Invalid manifest: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n const leaseUuid = args.lease_uuid;\n const address = await this.walletProvider.getAddress();\n await this.clientManager.acquireRateLimit();\n const queryClient = await this.clientManager.getQueryClient();\n\n const result = await updateApp(\n queryClient,\n address,\n leaseUuid,\n (addr, uuid) => this.getProviderAuthToken(addr, uuid),\n manifest,\n args.existing_manifest,\n );\n return jsonResponse(result, bigIntReplacer);\n }),\n );\n\n // -- app_diagnostics --\n this.mcpServer.registerTool(\n 'app_diagnostics',\n {\n description:\n 'Get provision diagnostics for a deployed app. Use this to debug apps stuck in provisioning or that failed to start. Returns provision status, failure count, and last error message.',\n inputSchema: {\n lease_uuid: z\n .string()\n .uuid()\n .describe('The lease UUID of the app to diagnose'),\n },\n },\n withErrorHandling('app_diagnostics', async (args) => {\n const leaseUuid = args.lease_uuid;\n const address = await this.walletProvider.getAddress();\n await this.clientManager.acquireRateLimit();\n const queryClient = await this.clientManager.getQueryClient();\n\n const lease = await fetchActiveLease(\n queryClient,\n leaseUuid,\n 'cannot be diagnosed',\n );\n const providerUrl = await resolveProviderUrl(\n queryClient,\n lease.providerUuid,\n );\n const authToken = await this.getProviderAuthToken(address, leaseUuid);\n const provision = await getLeaseProvision(\n providerUrl,\n leaseUuid,\n authToken,\n );\n\n return jsonResponse(\n {\n lease_uuid: leaseUuid,\n provision_status: provision.status,\n fail_count: provision.fail_count,\n last_error: provision.last_error,\n },\n bigIntReplacer,\n );\n }),\n );\n\n // -- app_releases --\n this.mcpServer.registerTool(\n 'app_releases',\n {\n description:\n 'Get release/version history for a deployed app. Use this to see what versions have been deployed, when they were created, and their status.',\n inputSchema: {\n lease_uuid: z\n .string()\n .uuid()\n .describe('The lease UUID of the app to get release history for'),\n },\n },\n withErrorHandling('app_releases', async (args) => {\n const leaseUuid = args.lease_uuid;\n const address = await this.walletProvider.getAddress();\n await this.clientManager.acquireRateLimit();\n const queryClient = await this.clientManager.getQueryClient();\n\n const lease = await fetchActiveLease(\n queryClient,\n leaseUuid,\n 'releases are not available',\n );\n const providerUrl = await resolveProviderUrl(\n queryClient,\n lease.providerUuid,\n );\n const authToken = await this.getProviderAuthToken(address, leaseUuid);\n const result = await getLeaseReleases(\n providerUrl,\n leaseUuid,\n authToken,\n );\n\n return jsonResponse(\n {\n lease_uuid: leaseUuid,\n releases: result.releases,\n },\n bigIntReplacer,\n );\n }),\n );\n }\n\n getServer(): Server {\n return this.mcpServer.server;\n }\n\n getClientManager(): CosmosClientManager {\n return this.clientManager;\n }\n\n disconnect(): void {\n this.clientManager.disconnect();\n }\n}\n\nexport function createMnemonicFredServer(\n config: MnemonicServerConfig,\n): Promise<FredMCPServer> {\n return createMnemonicServer(config, FredMCPServer);\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAwGA,IAAa,gBAAb,MAA2B;CAKzB,YAAY,SAAmC;EAC7C,MAAM,SAAS,sBAAsB,QAAQ,OAAO;AACpD,OAAK,iBAAiB,QAAQ;AAC9B,OAAK,gBAAgB,oBAAoB,YACvC,QACA,KAAK,eACN;AAED,OAAK,YAAY,IAAI,UACnB;GACE,MAAM;GACN,SAAS;GACV,EACD,EACE,cAAc,EACZ,OAAO,EAAE,EACV,EACF,CACF;AAED,OAAK,eAAe;;CAGtB,uBAA6E;AAC3E,MAAI,CAAC,KAAK,eAAe,cACvB,OAAM,IAAIA,mBACRC,uBAAqB,gBACrB,8IACD;AAEH,SAAO,KAAK,eAAe,cAAc,KAAK,KAAK,eAAe;;CAGpE,MAAc,qBACZ,SACA,WACiB;EACjB,MAAM,gBAAgB,KAAK,sBAAsB;EACjD,MAAM,YAAY,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;EAE/C,MAAM,EAAE,SAAS,cAAc,MAAM,cAAc,SADnC,kBAAkB,SAAS,WAAW,UAAU,CACI;AACpE,SAAO,gBACL,SACA,WACA,WACA,QAAQ,OACR,UACD;;CAGH,MAAc,sBACZ,SACA,WACA,aACiB;EACjB,MAAM,gBAAgB,KAAK,sBAAsB;EACjD,MAAM,YAAY,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;EAM/C,MAAM,EAAE,SAAS,cAAc,MAAM,cAAc,SALnC,2BACd,WACA,aACA,UACD,CACmE;AACpE,SAAO,gBACL,SACA,WACA,WACA,QAAQ,OACR,WACA,YACD;;CAGH,gBAA8B;AAE5B,OAAK,UAAU,aACb,kBACA,EACE,aACE,6NACH,EACD,kBAAkB,kBAAkB,YAAY;AAC9C,SAAM,KAAK,cAAc,kBAAkB;AAG3C,UAAO,aADQ,MAAM,cADD,MAAM,KAAK,cAAc,gBAAgB,CACd,EACnB,eAAe;IAC3C,CACH;AAGD,OAAK,UAAU,aACb,cACA;GACE,aACE;GACF,aAAa,EACX,YAAY,EACT,QAAQ,CACR,MAAM,CACN,SAAS,qCAAqC,EAClD;GACF,EACD,kBAAkB,cAAc,OAAO,SAAS;GAC9C,MAAM,YAAY,KAAK;GACvB,MAAM,UAAU,MAAM,KAAK,eAAe,YAAY;AACtD,SAAM,KAAK,cAAc,kBAAkB;AAQ3C,UAAO,aANQ,MAAM,UADD,MAAM,KAAK,cAAc,gBAAgB,EAG3D,SACA,YACC,MAAM,SAAS,KAAK,qBAAqB,MAAM,KAAK,CACtD,EAC2B,eAAe;IAC3C,CACH;AAGD,OAAK,UAAU,aACb,YACA;GACE,aACE;GACF,aAAa;IACX,YAAY,EACT,QAAQ,CACR,MAAM,CACN,SAAS,4CAA4C;IACxD,MAAM,EACH,QAAQ,CACR,KAAK,CACL,IAAI,EAAE,CACN,IAAI,SAAS,CACb,UAAU,CACV,SAAS,yCAAyC;IACtD;GACF,EACD,kBAAkB,YAAY,OAAO,SAAS;GAC5C,MAAM,YAAY,KAAK;GACvB,MAAM,OAAO,KAAK;GAClB,MAAM,UAAU,MAAM,KAAK,eAAe,YAAY;AACtD,SAAM,KAAK,cAAc,kBAAkB;AAS3C,UAAO,aAPQ,MAAM,WADD,MAAM,KAAK,cAAc,gBAAgB,EAG3D,SACA,YACC,MAAM,SAAS,KAAK,qBAAqB,MAAM,KAAK,EACrD,KACD,EAC2B,eAAe;IAC3C,CACH;AAGD,OAAK,UAAU,aACb,cACA;GACE,aACE;GACF,aAAa;IACX,OAAO,EACJ,QAAQ,CACR,UAAU,CACV,SACC,gEACD;IACH,MAAM,EACH,QAAQ,CACR,KAAK,CACL,IAAI,EAAE,CACN,IAAI,MAAM,CACV,UAAU,CACV,SACC,kEACD;IACH,MAAM,EACH,QAAQ,CACR,SAAS,0DAAsD;IAClE,KAAK,EACF,OAAO,EAAE,QAAQ,EAAE,EAAE,QAAQ,CAAC,CAC9B,UAAU,CACV,SAAS,2CAA2C;IACvD,SAAS,EACN,MAAM,EAAE,QAAQ,CAAC,CACjB,UAAU,CACV,SAAS,0CAA0C;IACtD,MAAM,EACH,MAAM,EAAE,QAAQ,CAAC,CACjB,UAAU,CACV,SAAS,qCAAqC;IACjD,MAAM,EACH,QAAQ,CACR,UAAU,CACV,SAAS,oDAAkD;IAC9D,OAAO,EACJ,MAAM,EAAE,QAAQ,CAAC,CACjB,UAAU,CACV,SAAS,0CAAwC;IACpD,cAAc,EACX,OAAO;KACN,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC;KACzB,UAAU,EAAE,QAAQ,CAAC,UAAU;KAC/B,SAAS,EAAE,QAAQ,CAAC,UAAU;KAC9B,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU;KACpC,cAAc,EAAE,QAAQ,CAAC,UAAU;KACpC,CAAC,CACD,UAAU,CACV,SAAS,uCAAuC;IACnD,mBAAmB,EAChB,QAAQ,CACR,UAAU,CACV,SAAS,mDAAiD;IAC7D,MAAM,EACH,SAAS,CACT,UAAU,CACV,SAAS,2CAA2C;IACvD,QAAQ,EACL,MAAM,EAAE,QAAQ,CAAC,CACjB,UAAU,CACV,SAAS,wDAAsD;IAClE,QAAQ,EACL,OAAO,EAAE,QAAQ,EAAE,EAAE,QAAQ,CAAC,CAC9B,UAAU,CACV,SAAS,sCAAsC;IAClD,SAAS,EACN,QAAQ,CACR,UAAU,CACV,SACC,kEACD;IACH,YAAY,EACT,OAAO,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,CAAC,CACvD,UAAU,CACV,SAAS,uBAAuB;IACnC,UAAU,EACP,OACC,EAAE,QAAQ,EACV,EAAE,OAAO;KACP,OAAO,EAAE,QAAQ;KACjB,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,UAAU;KACpD,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,QAAQ,CAAC,CAAC,UAAU;KAChD,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,UAAU;KACvC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,UAAU;KACpC,MAAM,EAAE,QAAQ,CAAC,UAAU;KAC3B,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,UAAU;KACrC,cAAc,EACX,OAAO;MACN,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC;MACzB,UAAU,EAAE,QAAQ,CAAC,UAAU;MAC/B,SAAS,EAAE,QAAQ,CAAC,UAAU;MAC9B,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU;MACpC,cAAc,EAAE,QAAQ,CAAC,UAAU;MACpC,CAAC,CACD,UAAU;KACb,mBAAmB,EAAE,QAAQ,CAAC,UAAU;KACxC,YAAY,EACT,OAAO,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,CAAC,CACvD,UAAU;KACb,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,UAAU;KACtC,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,QAAQ,CAAC,CAAC,UAAU;KACpD,CAAC,CACH,CACA,UAAU,CACV,SACC,yGACD;IACJ;GACF,EACD,kBAAkB,cAAc,OAAO,SAAS;AAyB9C,UAAO,aAxBQ,MAAM,UACnB,KAAK,gBACJ,MAAM,SAAS,KAAK,qBAAqB,MAAM,KAAK,GACpD,MAAM,MAAM,gBACX,KAAK,sBAAsB,MAAM,MAAM,YAAY,EACrD;IACE,OAAO,KAAK;IACZ,MAAM,KAAK;IACX,MAAM,KAAK;IACX,KAAK,KAAK;IACV,SAAS,KAAK;IACd,MAAM,KAAK;IACX,MAAM,KAAK;IACX,OAAO,KAAK;IACZ,cAAc,KAAK;IACnB,mBAAmB,KAAK;IACxB,MAAM,KAAK;IACX,QAAQ,KAAK;IACb,QAAQ,KAAK;IACb,SAAS,KAAK;IACd,YAAY,KAAK;IACjB,UAAU,KAAK;IAChB,CACF,EAC2B,eAAe;IAC3C,CACH;AAGD,OAAK,UAAU,aACb,eACA;GACE,aACE;GACF,aAAa,EACX,YAAY,EACT,QAAQ,CACR,MAAM,CACN,SAAS,uCAAuC,EACpD;GACF,EACD,kBAAkB,eAAe,OAAO,SAAS;GAC/C,MAAM,YAAY,KAAK;GACvB,MAAM,UAAU,MAAM,KAAK,eAAe,YAAY;AACtD,SAAM,KAAK,cAAc,kBAAkB;AAQ3C,UAAO,aANQ,MAAM,WADD,MAAM,KAAK,cAAc,gBAAgB,EAG3D,SACA,YACC,MAAM,SAAS,KAAK,qBAAqB,MAAM,KAAK,CACtD,EAC2B,eAAe;IAC3C,CACH;AAGD,OAAK,UAAU,aACb,cACA;GACE,aACE;GACF,aAAa;IACX,YAAY,EACT,QAAQ,CACR,MAAM,CACN,SAAS,sCAAsC;IAClD,UAAU,EACP,QAAQ,CACR,SAAS,0CAA0C;IACtD,mBAAmB,EAChB,QAAQ,CACR,UAAU,CACV,SACC,sKACD;IACJ;GACF,EACD,kBAAkB,cAAc,OAAO,SAAS;GAC9C,MAAM,WAAW,KAAK;AAEtB,OAAI;IACF,MAAM,SAAS,KAAK,MAAM,SAAS;AACnC,QACE,WAAW,QACX,OAAO,WAAW,YAClB,MAAM,QAAQ,OAAO,CAErB,OAAM,IAAI,MAAM,wBAAwB;YAEnC,KAAK;AACZ,UAAM,IAAID,mBACRC,uBAAqB,gBACrB,qBAAqB,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GACtE;;GAGH,MAAM,YAAY,KAAK;GACvB,MAAM,UAAU,MAAM,KAAK,eAAe,YAAY;AACtD,SAAM,KAAK,cAAc,kBAAkB;AAW3C,UAAO,aARQ,MAAM,UAFD,MAAM,KAAK,cAAc,gBAAgB,EAI3D,SACA,YACC,MAAM,SAAS,KAAK,qBAAqB,MAAM,KAAK,EACrD,UACA,KAAK,kBACN,EAC2B,eAAe;IAC3C,CACH;AAGD,OAAK,UAAU,aACb,mBACA;GACE,aACE;GACF,aAAa,EACX,YAAY,EACT,QAAQ,CACR,MAAM,CACN,SAAS,wCAAwC,EACrD;GACF,EACD,kBAAkB,mBAAmB,OAAO,SAAS;GACnD,MAAM,YAAY,KAAK;GACvB,MAAM,UAAU,MAAM,KAAK,eAAe,YAAY;AACtD,SAAM,KAAK,cAAc,kBAAkB;GAC3C,MAAM,cAAc,MAAM,KAAK,cAAc,gBAAgB;GAY7D,MAAM,YAAY,MAAM,kBALJ,MAAM,mBACxB,cANY,MAAM,iBAClB,aACA,WACA,sBACD,EAGO,aACP,EAIC,WAHgB,MAAM,KAAK,qBAAqB,SAAS,UAAU,CAKpE;AAED,UAAO,aACL;IACE,YAAY;IACZ,kBAAkB,UAAU;IAC5B,YAAY,UAAU;IACtB,YAAY,UAAU;IACvB,EACD,eACD;IACD,CACH;AAGD,OAAK,UAAU,aACb,gBACA;GACE,aACE;GACF,aAAa,EACX,YAAY,EACT,QAAQ,CACR,MAAM,CACN,SAAS,uDAAuD,EACpE;GACF,EACD,kBAAkB,gBAAgB,OAAO,SAAS;GAChD,MAAM,YAAY,KAAK;GACvB,MAAM,UAAU,MAAM,KAAK,eAAe,YAAY;AACtD,SAAM,KAAK,cAAc,kBAAkB;GAC3C,MAAM,cAAc,MAAM,KAAK,cAAc,gBAAgB;AAkB7D,UAAO,aACL;IACE,YAAY;IACZ,WATW,MAAM,iBALD,MAAM,mBACxB,cANY,MAAM,iBAClB,aACA,WACA,6BACD,EAGO,aACP,EAIC,WAHgB,MAAM,KAAK,qBAAqB,SAAS,UAAU,CAKpE,EAKoB;IAClB,EACD,eACD;IACD,CACH;;CAGH,YAAoB;AAClB,SAAO,KAAK,UAAU;;CAGxB,mBAAwC;AACtC,SAAO,KAAK;;CAGd,aAAmB;AACjB,OAAK,cAAc,YAAY;;;AAInC,SAAgB,yBACd,QACwB;AACxB,QAAO,qBAAqB,QAAQ,cAAc"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//#region src/manifest.d.ts
|
|
2
|
+
interface BuildManifestOptions {
|
|
3
|
+
image: string;
|
|
4
|
+
ports: Record<string, Record<string, never>>;
|
|
5
|
+
env?: Record<string, string>;
|
|
6
|
+
command?: string[];
|
|
7
|
+
args?: string[];
|
|
8
|
+
user?: string;
|
|
9
|
+
tmpfs?: string[];
|
|
10
|
+
health_check?: {
|
|
11
|
+
test: string[];
|
|
12
|
+
interval?: string;
|
|
13
|
+
timeout?: string;
|
|
14
|
+
retries?: number;
|
|
15
|
+
start_period?: string;
|
|
16
|
+
};
|
|
17
|
+
stop_grace_period?: string;
|
|
18
|
+
init?: boolean;
|
|
19
|
+
expose?: string[];
|
|
20
|
+
labels?: Record<string, string>;
|
|
21
|
+
depends_on?: Record<string, {
|
|
22
|
+
condition: string;
|
|
23
|
+
}>;
|
|
24
|
+
}
|
|
25
|
+
declare function deriveAppNameFromImage(image: string): string;
|
|
26
|
+
declare function validateServiceName(name: string): boolean;
|
|
27
|
+
declare function buildManifest(opts: BuildManifestOptions): Record<string, unknown>;
|
|
28
|
+
declare function normalizePorts(port: string): Record<string, Record<string, never>>;
|
|
29
|
+
declare function buildStackManifest(opts: {
|
|
30
|
+
services: Record<string, BuildManifestOptions>;
|
|
31
|
+
}): {
|
|
32
|
+
services: Record<string, unknown>;
|
|
33
|
+
};
|
|
34
|
+
declare function mergeManifest(newManifest: Record<string, unknown>, oldManifestJson: string): Record<string, unknown>;
|
|
35
|
+
declare function isStackManifest(manifest: unknown): manifest is {
|
|
36
|
+
services: Record<string, Record<string, unknown>>;
|
|
37
|
+
};
|
|
38
|
+
declare function parseStackManifest(json: string): {
|
|
39
|
+
services: Record<string, Record<string, unknown>>;
|
|
40
|
+
};
|
|
41
|
+
declare function getServiceNames(manifest: unknown): string[];
|
|
42
|
+
//#endregion
|
|
43
|
+
export { BuildManifestOptions, buildManifest, buildStackManifest, deriveAppNameFromImage, getServiceNames, isStackManifest, mergeManifest, normalizePorts, parseStackManifest, validateServiceName };
|
|
44
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","names":[],"sources":["../src/manifest.ts"],"mappings":";UAAiB,oBAAA;EACf,KAAA;EACA,KAAA,EAAO,MAAA,SAAe,MAAA;EACtB,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,UAAA,GAAa,MAAA;IAAiB,SAAA;EAAA;AAAA;AAAA,iBAWhB,sBAAA,CAAuB,KAAA;AAAA,iBAkCvB,mBAAA,CAAoB,IAAA;AAAA,iBAIpB,aAAA,CACd,IAAA,EAAM,oBAAA,GACL,MAAA;AAAA,iBAsBa,cAAA,CACd,IAAA,WACC,MAAA,SAAe,MAAA;AAAA,iBA+BF,kBAAA,CAAmB,IAAA;EACjC,QAAA,EAAU,MAAA,SAAe,oBAAA;AAAA;EACrB,QAAA,EAAU,MAAA;AAAA;AAAA,iBAoBA,aAAA,CACd,WAAA,EAAa,MAAA,mBACb,eAAA,WACC,MAAA;AAAA,iBA6Da,eAAA,CACd,QAAA,YACC,QAAA;EAAc,QAAA,EAAU,MAAA,SAAe,MAAA;AAAA;AAAA,iBA2B1B,kBAAA,CAAmB,IAAA;EACjC,QAAA,EAAU,MAAA,SAAe,MAAA;AAAA;AAAA,iBAuBX,eAAA,CAAgB,QAAA"}
|
package/dist/manifest.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { DNS_LABEL_RE, ManifestMCPError, ManifestMCPErrorCode } from "@manifest-network/manifest-mcp-core";
|
|
2
|
+
//#region src/manifest.ts
|
|
3
|
+
const MAX_NAME_LENGTH = 32;
|
|
4
|
+
function deriveAppNameFromImage(image) {
|
|
5
|
+
const lastSlash = image.lastIndexOf("/");
|
|
6
|
+
let name = lastSlash >= 0 ? image.slice(lastSlash + 1) : image;
|
|
7
|
+
const atIdx = name.indexOf("@");
|
|
8
|
+
if (atIdx >= 0) name = name.slice(0, atIdx);
|
|
9
|
+
const colonIdx = name.indexOf(":");
|
|
10
|
+
if (colonIdx >= 0) name = name.slice(0, colonIdx);
|
|
11
|
+
name = name.toLowerCase().replace(/[^a-z0-9]/g, "-");
|
|
12
|
+
name = name.replace(/-{2,}/g, "-");
|
|
13
|
+
name = name.replace(/^-+|-+$/g, "");
|
|
14
|
+
if (name.length > MAX_NAME_LENGTH) name = name.slice(0, MAX_NAME_LENGTH).replace(/-+$/, "");
|
|
15
|
+
return name;
|
|
16
|
+
}
|
|
17
|
+
function validateServiceName(name) {
|
|
18
|
+
return DNS_LABEL_RE.test(name);
|
|
19
|
+
}
|
|
20
|
+
function buildManifest(opts) {
|
|
21
|
+
const manifest = {
|
|
22
|
+
image: opts.image,
|
|
23
|
+
ports: opts.ports
|
|
24
|
+
};
|
|
25
|
+
if (opts.env) manifest.env = opts.env;
|
|
26
|
+
if (opts.command) manifest.command = opts.command;
|
|
27
|
+
if (opts.args) manifest.args = opts.args;
|
|
28
|
+
if (opts.user) manifest.user = opts.user;
|
|
29
|
+
if (opts.tmpfs) manifest.tmpfs = opts.tmpfs;
|
|
30
|
+
if (opts.health_check) manifest.health_check = opts.health_check;
|
|
31
|
+
if (opts.stop_grace_period) manifest.stop_grace_period = opts.stop_grace_period;
|
|
32
|
+
if (opts.init !== void 0) manifest.init = opts.init;
|
|
33
|
+
if (opts.expose) manifest.expose = opts.expose;
|
|
34
|
+
if (opts.labels) manifest.labels = opts.labels;
|
|
35
|
+
if (opts.depends_on) manifest.depends_on = opts.depends_on;
|
|
36
|
+
return manifest;
|
|
37
|
+
}
|
|
38
|
+
const VALID_PROTOCOLS = new Set(["tcp", "udp"]);
|
|
39
|
+
function normalizePorts(port) {
|
|
40
|
+
const result = {};
|
|
41
|
+
for (const raw of port.split(",")) {
|
|
42
|
+
const trimmed = raw.trim();
|
|
43
|
+
if (!trimmed) continue;
|
|
44
|
+
const slashIdx = trimmed.indexOf("/");
|
|
45
|
+
const portStr = slashIdx >= 0 ? trimmed.slice(0, slashIdx) : trimmed;
|
|
46
|
+
const protocol = slashIdx >= 0 ? trimmed.slice(slashIdx + 1) : "tcp";
|
|
47
|
+
const portNum = parseInt(portStr, 10);
|
|
48
|
+
if (Number.isNaN(portNum) || portNum < 1 || portNum > 65535 || String(portNum) !== portStr) throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, `Invalid port: "${portStr}". Port must be a number between 1 and 65535.`);
|
|
49
|
+
if (!VALID_PROTOCOLS.has(protocol)) throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, `Invalid protocol: "${protocol}". Must be "tcp" or "udp".`);
|
|
50
|
+
result[`${portNum}/${protocol}`] = {};
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
function buildStackManifest(opts) {
|
|
55
|
+
const stack = {};
|
|
56
|
+
for (const [name, serviceOpts] of Object.entries(opts.services)) stack[name] = buildManifest(serviceOpts);
|
|
57
|
+
return { services: stack };
|
|
58
|
+
}
|
|
59
|
+
const CARRY_FORWARD_KEYS = [
|
|
60
|
+
"user",
|
|
61
|
+
"tmpfs",
|
|
62
|
+
"command",
|
|
63
|
+
"args",
|
|
64
|
+
"health_check",
|
|
65
|
+
"stop_grace_period",
|
|
66
|
+
"init",
|
|
67
|
+
"expose",
|
|
68
|
+
"depends_on"
|
|
69
|
+
];
|
|
70
|
+
function mergeManifest(newManifest, oldManifestJson) {
|
|
71
|
+
let old;
|
|
72
|
+
try {
|
|
73
|
+
const parsed = JSON.parse(oldManifestJson);
|
|
74
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, "existing_manifest must be a JSON object");
|
|
75
|
+
old = parsed;
|
|
76
|
+
} catch (err) {
|
|
77
|
+
if (err instanceof SyntaxError) throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, `existing_manifest contains invalid JSON: ${err.message}`);
|
|
78
|
+
throw err;
|
|
79
|
+
}
|
|
80
|
+
const merged = { ...newManifest };
|
|
81
|
+
if (old.env || merged.env) merged.env = {
|
|
82
|
+
...old.env,
|
|
83
|
+
...merged.env
|
|
84
|
+
};
|
|
85
|
+
if (old.ports || merged.ports) merged.ports = {
|
|
86
|
+
...old.ports,
|
|
87
|
+
...merged.ports
|
|
88
|
+
};
|
|
89
|
+
if (old.labels || merged.labels) merged.labels = {
|
|
90
|
+
...old.labels,
|
|
91
|
+
...merged.labels
|
|
92
|
+
};
|
|
93
|
+
for (const key of CARRY_FORWARD_KEYS) if (!(key in merged) && key in old) merged[key] = old[key];
|
|
94
|
+
return merged;
|
|
95
|
+
}
|
|
96
|
+
function isStackManifest(manifest) {
|
|
97
|
+
if (manifest === null || typeof manifest !== "object" || Array.isArray(manifest)) return false;
|
|
98
|
+
const services = manifest.services;
|
|
99
|
+
if (services === null || typeof services !== "object" || Array.isArray(services)) return false;
|
|
100
|
+
const entries = Object.values(services);
|
|
101
|
+
if (entries.length === 0) return false;
|
|
102
|
+
return entries.every((v) => v !== null && typeof v === "object" && !Array.isArray(v) && "image" in v);
|
|
103
|
+
}
|
|
104
|
+
function parseStackManifest(json) {
|
|
105
|
+
let parsed;
|
|
106
|
+
try {
|
|
107
|
+
parsed = JSON.parse(json);
|
|
108
|
+
} catch (err) {
|
|
109
|
+
if (err instanceof SyntaxError) throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, `Stack manifest contains invalid JSON: ${err.message}`);
|
|
110
|
+
throw err;
|
|
111
|
+
}
|
|
112
|
+
if (!isStackManifest(parsed)) throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_CONFIG, "Not a valid stack manifest: expected { services: { ... } } where each service has an \"image\" key");
|
|
113
|
+
return parsed;
|
|
114
|
+
}
|
|
115
|
+
function getServiceNames(manifest) {
|
|
116
|
+
if (!isStackManifest(manifest)) return [];
|
|
117
|
+
return Object.keys(manifest.services);
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
export { buildManifest, buildStackManifest, deriveAppNameFromImage, getServiceNames, isStackManifest, mergeManifest, normalizePorts, parseStackManifest, validateServiceName };
|
|
121
|
+
|
|
122
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","names":[],"sources":["../src/manifest.ts"],"sourcesContent":["export interface BuildManifestOptions {\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 init?: boolean;\n expose?: string[];\n labels?: Record<string, string>;\n depends_on?: Record<string, { condition: string }>;\n}\n\nimport {\n DNS_LABEL_RE,\n ManifestMCPError,\n ManifestMCPErrorCode,\n} from '@manifest-network/manifest-mcp-core';\n\nconst MAX_NAME_LENGTH = 32;\n\nexport function deriveAppNameFromImage(image: string): string {\n // Strip registry prefix (everything before the last /)\n const lastSlash = image.lastIndexOf('/');\n let name = lastSlash >= 0 ? image.slice(lastSlash + 1) : image;\n\n // Strip digest (@sha256:...)\n const atIdx = name.indexOf('@');\n if (atIdx >= 0) {\n name = name.slice(0, atIdx);\n }\n\n // Strip tag unconditionally\n const colonIdx = name.indexOf(':');\n if (colonIdx >= 0) {\n name = name.slice(0, colonIdx);\n }\n\n // Normalize: lowercase, replace non-alphanumeric with hyphens\n name = name.toLowerCase().replace(/[^a-z0-9]/g, '-');\n\n // Collapse consecutive hyphens\n name = name.replace(/-{2,}/g, '-');\n\n // Trim leading/trailing hyphens\n name = name.replace(/^-+|-+$/g, '');\n\n // Truncate\n if (name.length > MAX_NAME_LENGTH) {\n name = name.slice(0, MAX_NAME_LENGTH).replace(/-+$/, '');\n }\n\n return name;\n}\n\nexport function validateServiceName(name: string): boolean {\n return DNS_LABEL_RE.test(name);\n}\n\nexport function buildManifest(\n opts: BuildManifestOptions,\n): Record<string, unknown> {\n const manifest: Record<string, unknown> = {\n image: opts.image,\n ports: opts.ports,\n };\n if (opts.env) manifest.env = opts.env;\n if (opts.command) manifest.command = opts.command;\n if (opts.args) manifest.args = opts.args;\n if (opts.user) manifest.user = opts.user;\n if (opts.tmpfs) manifest.tmpfs = opts.tmpfs;\n if (opts.health_check) manifest.health_check = opts.health_check;\n if (opts.stop_grace_period)\n manifest.stop_grace_period = opts.stop_grace_period;\n if (opts.init !== undefined) manifest.init = opts.init;\n if (opts.expose) manifest.expose = opts.expose;\n if (opts.labels) manifest.labels = opts.labels;\n if (opts.depends_on) manifest.depends_on = opts.depends_on;\n return manifest;\n}\n\nconst VALID_PROTOCOLS = new Set(['tcp', 'udp']);\n\nexport function normalizePorts(\n port: string,\n): Record<string, Record<string, never>> {\n const result: Record<string, Record<string, never>> = {};\n for (const raw of port.split(',')) {\n const trimmed = raw.trim();\n if (!trimmed) continue;\n const slashIdx = trimmed.indexOf('/');\n const portStr = slashIdx >= 0 ? trimmed.slice(0, slashIdx) : trimmed;\n const protocol = slashIdx >= 0 ? trimmed.slice(slashIdx + 1) : 'tcp';\n const portNum = parseInt(portStr, 10);\n if (\n Number.isNaN(portNum) ||\n portNum < 1 ||\n portNum > 65535 ||\n String(portNum) !== portStr\n ) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n `Invalid port: \"${portStr}\". Port must be a number between 1 and 65535.`,\n );\n }\n if (!VALID_PROTOCOLS.has(protocol)) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n `Invalid protocol: \"${protocol}\". Must be \"tcp\" or \"udp\".`,\n );\n }\n result[`${portNum}/${protocol}`] = {};\n }\n return result;\n}\n\nexport function buildStackManifest(opts: {\n services: Record<string, BuildManifestOptions>;\n}): { services: Record<string, unknown> } {\n const stack: Record<string, unknown> = {};\n for (const [name, serviceOpts] of Object.entries(opts.services)) {\n stack[name] = buildManifest(serviceOpts);\n }\n return { services: stack };\n}\n\nconst CARRY_FORWARD_KEYS = [\n 'user',\n 'tmpfs',\n 'command',\n 'args',\n 'health_check',\n 'stop_grace_period',\n 'init',\n 'expose',\n 'depends_on',\n] as const;\n\nexport function mergeManifest(\n newManifest: Record<string, unknown>,\n oldManifestJson: string,\n): Record<string, unknown> {\n let old: Record<string, unknown>;\n try {\n const parsed = JSON.parse(oldManifestJson);\n if (\n parsed === null ||\n typeof parsed !== 'object' ||\n Array.isArray(parsed)\n ) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n 'existing_manifest must be a JSON object',\n );\n }\n old = parsed as Record<string, unknown>;\n } catch (err) {\n if (err instanceof SyntaxError) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n `existing_manifest contains invalid JSON: ${err.message}`,\n );\n }\n throw err;\n }\n\n const merged: Record<string, unknown> = { ...newManifest };\n\n // env: old defaults, new overrides\n if (old.env || merged.env) {\n merged.env = {\n ...(old.env as Record<string, string> | undefined),\n ...(merged.env as Record<string, string> | undefined),\n };\n }\n\n // ports: union\n if (old.ports || merged.ports) {\n merged.ports = {\n ...(old.ports as Record<string, unknown> | undefined),\n ...(merged.ports as Record<string, unknown> | undefined),\n };\n }\n\n // labels: old defaults, new overrides\n if (old.labels || merged.labels) {\n merged.labels = {\n ...(old.labels as Record<string, string> | undefined),\n ...(merged.labels as Record<string, string> | undefined),\n };\n }\n\n // Carry forward from old if not present in new\n for (const key of CARRY_FORWARD_KEYS) {\n if (!(key in merged) && key in old) {\n merged[key] = old[key];\n }\n }\n\n return merged;\n}\n\nexport function isStackManifest(\n manifest: unknown,\n): manifest is { services: Record<string, Record<string, unknown>> } {\n if (\n manifest === null ||\n typeof manifest !== 'object' ||\n Array.isArray(manifest)\n ) {\n return false;\n }\n const services = (manifest as Record<string, unknown>).services;\n if (\n services === null ||\n typeof services !== 'object' ||\n Array.isArray(services)\n ) {\n return false;\n }\n const entries = Object.values(services as Record<string, unknown>);\n if (entries.length === 0) return false;\n return entries.every(\n (v) =>\n v !== null &&\n typeof v === 'object' &&\n !Array.isArray(v) &&\n 'image' in (v as Record<string, unknown>),\n );\n}\n\nexport function parseStackManifest(json: string): {\n services: Record<string, Record<string, unknown>>;\n} {\n let parsed: unknown;\n try {\n parsed = JSON.parse(json);\n } catch (err) {\n if (err instanceof SyntaxError) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n `Stack manifest contains invalid JSON: ${err.message}`,\n );\n }\n throw err;\n }\n if (!isStackManifest(parsed)) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_CONFIG,\n 'Not a valid stack manifest: expected { services: { ... } } where each service has an \"image\" key',\n );\n }\n return parsed;\n}\n\nexport function getServiceNames(manifest: unknown): string[] {\n if (!isStackManifest(manifest)) return [];\n return Object.keys(manifest.services);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { FredLeaseStatus } from "../http/fred.js";
|
|
2
|
+
import { ConnectionDetails } from "../http/provider.js";
|
|
3
|
+
import { LeaseState, ManifestQueryClient } from "@manifest-network/manifest-mcp-core";
|
|
4
|
+
|
|
5
|
+
//#region src/tools/appStatus.d.ts
|
|
6
|
+
declare function appStatus(queryClient: ManifestQueryClient, address: string, leaseUuid: string, getAuthToken: (address: string, leaseUuid: string) => Promise<string>, fetchFn?: typeof globalThis.fetch): Promise<{
|
|
7
|
+
connectionError?: string | undefined;
|
|
8
|
+
providerError?: string | undefined;
|
|
9
|
+
fredStatus?: FredLeaseStatus | undefined;
|
|
10
|
+
chainState: {
|
|
11
|
+
state: LeaseState;
|
|
12
|
+
providerUuid: string;
|
|
13
|
+
createdAt: string;
|
|
14
|
+
closedAt: string | undefined;
|
|
15
|
+
};
|
|
16
|
+
connection?: ConnectionDetails | undefined;
|
|
17
|
+
lease_uuid: string;
|
|
18
|
+
}>;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { appStatus };
|
|
21
|
+
//# sourceMappingURL=appStatus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"appStatus.d.ts","names":[],"sources":["../../src/tools/appStatus.ts"],"mappings":";;;;;iBAgBsB,SAAA,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,73 @@
|
|
|
1
|
+
import { getLeaseConnectionInfo } from "../http/provider.js";
|
|
2
|
+
import { getLeaseStatus } from "../http/fred.js";
|
|
3
|
+
import { resolveProviderUrl } from "./resolveLeaseProvider.js";
|
|
4
|
+
import { INFRASTRUCTURE_ERROR_CODES, LeaseState, ManifestMCPError, ManifestMCPErrorCode, logger, sanitizeForLogging } from "@manifest-network/manifest-mcp-core";
|
|
5
|
+
//#region src/tools/appStatus.ts
|
|
6
|
+
async function appStatus(queryClient, address, leaseUuid, getAuthToken, fetchFn) {
|
|
7
|
+
const leaseResult = await queryClient.liftedinit.billing.v1.lease({ leaseUuid });
|
|
8
|
+
if (!leaseResult.lease) throw new ManifestMCPError(ManifestMCPErrorCode.QUERY_FAILED, `Lease "${leaseUuid}" not found on chain`);
|
|
9
|
+
const lease = leaseResult.lease;
|
|
10
|
+
const chainState = {
|
|
11
|
+
state: lease.state,
|
|
12
|
+
providerUuid: lease.providerUuid,
|
|
13
|
+
createdAt: lease.createdAt?.toISOString(),
|
|
14
|
+
closedAt: lease.closedAt?.toISOString()
|
|
15
|
+
};
|
|
16
|
+
let fredStatus = null;
|
|
17
|
+
let connection = null;
|
|
18
|
+
let providerError;
|
|
19
|
+
let connectionError;
|
|
20
|
+
if (lease.state === LeaseState.LEASE_STATE_PENDING || lease.state === LeaseState.LEASE_STATE_ACTIVE) {
|
|
21
|
+
let providerUrl;
|
|
22
|
+
try {
|
|
23
|
+
providerUrl = await resolveProviderUrl(queryClient, lease.providerUuid);
|
|
24
|
+
} catch (err) {
|
|
25
|
+
if (err instanceof ManifestMCPError && INFRASTRUCTURE_ERROR_CODES.has(err.code)) throw err;
|
|
26
|
+
const rawMsg = `Could not resolve provider: ${err instanceof Error ? err.message : String(err)}`;
|
|
27
|
+
logger.error(`[app_status] ${rawMsg}`);
|
|
28
|
+
return {
|
|
29
|
+
lease_uuid: leaseUuid,
|
|
30
|
+
chainState,
|
|
31
|
+
providerError: sanitizeForLogging(rawMsg)
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
let authToken;
|
|
35
|
+
try {
|
|
36
|
+
authToken = await getAuthToken(address, leaseUuid);
|
|
37
|
+
} catch (err) {
|
|
38
|
+
if (err instanceof ManifestMCPError && INFRASTRUCTURE_ERROR_CODES.has(err.code)) throw err;
|
|
39
|
+
const rawMsg = `Auth token error: ${err instanceof Error ? err.message : String(err)}`;
|
|
40
|
+
logger.error(`[app_status] ${rawMsg}`);
|
|
41
|
+
return {
|
|
42
|
+
lease_uuid: leaseUuid,
|
|
43
|
+
chainState,
|
|
44
|
+
providerError: sanitizeForLogging(rawMsg)
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const [statusResult, connResult] = await Promise.allSettled([getLeaseStatus(providerUrl, leaseUuid, authToken, fetchFn), getLeaseConnectionInfo(providerUrl, leaseUuid, authToken, fetchFn)]);
|
|
48
|
+
if (statusResult.status === "fulfilled") fredStatus = statusResult.value;
|
|
49
|
+
else {
|
|
50
|
+
const rawMsg = statusResult.reason instanceof Error ? statusResult.reason.message : String(statusResult.reason);
|
|
51
|
+
logger.error(`[app_status] Failed to get lease status for ${leaseUuid}: ${rawMsg}`);
|
|
52
|
+
providerError = sanitizeForLogging(rawMsg);
|
|
53
|
+
}
|
|
54
|
+
if (connResult.status === "fulfilled") connection = connResult.value.connection;
|
|
55
|
+
else {
|
|
56
|
+
const rawMsg = connResult.reason instanceof Error ? connResult.reason.message : String(connResult.reason);
|
|
57
|
+
logger.error(`[app_status] Failed to get connection info for ${leaseUuid}: ${rawMsg}`);
|
|
58
|
+
connectionError = sanitizeForLogging(rawMsg);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
lease_uuid: leaseUuid,
|
|
63
|
+
...connection && { connection },
|
|
64
|
+
chainState,
|
|
65
|
+
...fredStatus && { fredStatus },
|
|
66
|
+
...providerError && { providerError },
|
|
67
|
+
...connectionError && { connectionError }
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//#endregion
|
|
71
|
+
export { appStatus };
|
|
72
|
+
|
|
73
|
+
//# sourceMappingURL=appStatus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"appStatus.js","names":[],"sources":["../../src/tools/appStatus.ts"],"sourcesContent":["import {\n INFRASTRUCTURE_ERROR_CODES,\n LeaseState,\n logger,\n ManifestMCPError,\n ManifestMCPErrorCode,\n type ManifestQueryClient,\n sanitizeForLogging,\n} from '@manifest-network/manifest-mcp-core';\nimport { type FredLeaseStatus, getLeaseStatus } from '../http/fred.js';\nimport {\n type ConnectionDetails,\n getLeaseConnectionInfo,\n} from '../http/provider.js';\nimport { resolveProviderUrl } from './resolveLeaseProvider.js';\n\nexport async function appStatus(\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 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 const chainState = {\n state: lease.state,\n providerUuid: lease.providerUuid,\n createdAt: lease.createdAt?.toISOString(),\n closedAt: lease.closedAt?.toISOString(),\n };\n\n let fredStatus: FredLeaseStatus | null = null;\n let connection: ConnectionDetails | null = null;\n let providerError: string | undefined;\n let connectionError: string | undefined;\n\n if (\n lease.state === LeaseState.LEASE_STATE_PENDING ||\n lease.state === LeaseState.LEASE_STATE_ACTIVE\n ) {\n let providerUrl: string;\n try {\n providerUrl = await resolveProviderUrl(queryClient, lease.providerUuid);\n } catch (err) {\n if (\n err instanceof ManifestMCPError &&\n INFRASTRUCTURE_ERROR_CODES.has(err.code)\n )\n throw err;\n const rawMsg = `Could not resolve provider: ${err instanceof Error ? err.message : String(err)}`;\n logger.error(`[app_status] ${rawMsg}`);\n return {\n lease_uuid: leaseUuid,\n chainState,\n providerError: sanitizeForLogging(rawMsg) as string,\n };\n }\n\n let authToken: string;\n try {\n authToken = await getAuthToken(address, leaseUuid);\n } catch (err) {\n if (\n err instanceof ManifestMCPError &&\n INFRASTRUCTURE_ERROR_CODES.has(err.code)\n )\n throw err;\n const rawMsg = `Auth token error: ${err instanceof Error ? err.message : String(err)}`;\n logger.error(`[app_status] ${rawMsg}`);\n return {\n lease_uuid: leaseUuid,\n chainState,\n providerError: sanitizeForLogging(rawMsg) as string,\n };\n }\n\n const [statusResult, connResult] = await Promise.allSettled([\n getLeaseStatus(providerUrl, leaseUuid, authToken, fetchFn),\n getLeaseConnectionInfo(providerUrl, leaseUuid, authToken, fetchFn),\n ]);\n\n if (statusResult.status === 'fulfilled') {\n fredStatus = statusResult.value;\n } else {\n const rawMsg =\n statusResult.reason instanceof Error\n ? statusResult.reason.message\n : String(statusResult.reason);\n logger.error(\n `[app_status] Failed to get lease status for ${leaseUuid}: ${rawMsg}`,\n );\n providerError = sanitizeForLogging(rawMsg) as string;\n }\n\n if (connResult.status === 'fulfilled') {\n connection = connResult.value.connection;\n } else {\n const rawMsg =\n connResult.reason instanceof Error\n ? connResult.reason.message\n : String(connResult.reason);\n logger.error(\n `[app_status] Failed to get connection info for ${leaseUuid}: ${rawMsg}`,\n );\n connectionError = sanitizeForLogging(rawMsg) as string;\n }\n }\n\n return {\n lease_uuid: leaseUuid,\n ...(connection && { connection }),\n chainState,\n ...(fredStatus && { fredStatus }),\n ...(providerError && { providerError }),\n ...(connectionError && { connectionError }),\n };\n}\n"],"mappings":";;;;;AAgBA,eAAsB,UACpB,aACA,SACA,WACA,cACA,SACA;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;CAC1B,MAAM,aAAa;EACjB,OAAO,MAAM;EACb,cAAc,MAAM;EACpB,WAAW,MAAM,WAAW,aAAa;EACzC,UAAU,MAAM,UAAU,aAAa;EACxC;CAED,IAAI,aAAqC;CACzC,IAAI,aAAuC;CAC3C,IAAI;CACJ,IAAI;AAEJ,KACE,MAAM,UAAU,WAAW,uBAC3B,MAAM,UAAU,WAAW,oBAC3B;EACA,IAAI;AACJ,MAAI;AACF,iBAAc,MAAM,mBAAmB,aAAa,MAAM,aAAa;WAChE,KAAK;AACZ,OACE,eAAe,oBACf,2BAA2B,IAAI,IAAI,KAAK,CAExC,OAAM;GACR,MAAM,SAAS,+BAA+B,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC9F,UAAO,MAAM,gBAAgB,SAAS;AACtC,UAAO;IACL,YAAY;IACZ;IACA,eAAe,mBAAmB,OAAO;IAC1C;;EAGH,IAAI;AACJ,MAAI;AACF,eAAY,MAAM,aAAa,SAAS,UAAU;WAC3C,KAAK;AACZ,OACE,eAAe,oBACf,2BAA2B,IAAI,IAAI,KAAK,CAExC,OAAM;GACR,MAAM,SAAS,qBAAqB,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AACpF,UAAO,MAAM,gBAAgB,SAAS;AACtC,UAAO;IACL,YAAY;IACZ;IACA,eAAe,mBAAmB,OAAO;IAC1C;;EAGH,MAAM,CAAC,cAAc,cAAc,MAAM,QAAQ,WAAW,CAC1D,eAAe,aAAa,WAAW,WAAW,QAAQ,EAC1D,uBAAuB,aAAa,WAAW,WAAW,QAAQ,CACnE,CAAC;AAEF,MAAI,aAAa,WAAW,YAC1B,cAAa,aAAa;OACrB;GACL,MAAM,SACJ,aAAa,kBAAkB,QAC3B,aAAa,OAAO,UACpB,OAAO,aAAa,OAAO;AACjC,UAAO,MACL,+CAA+C,UAAU,IAAI,SAC9D;AACD,mBAAgB,mBAAmB,OAAO;;AAG5C,MAAI,WAAW,WAAW,YACxB,cAAa,WAAW,MAAM;OACzB;GACL,MAAM,SACJ,WAAW,kBAAkB,QACzB,WAAW,OAAO,UAClB,OAAO,WAAW,OAAO;AAC/B,UAAO,MACL,kDAAkD,UAAU,IAAI,SACjE;AACD,qBAAkB,mBAAmB,OAAO;;;AAIhD,QAAO;EACL,YAAY;EACZ,GAAI,cAAc,EAAE,YAAY;EAChC;EACA,GAAI,cAAc,EAAE,YAAY;EAChC,GAAI,iBAAiB,EAAE,eAAe;EACtC,GAAI,mBAAmB,EAAE,iBAAiB;EAC3C"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ManifestQueryClient } from "@manifest-network/manifest-mcp-core";
|
|
2
|
+
|
|
3
|
+
//#region src/tools/browseCatalog.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Run an array of async functions with a concurrency limit.
|
|
6
|
+
* Returns results in the same order as the input.
|
|
7
|
+
*/
|
|
8
|
+
declare function mapWithConcurrency<T, R>(items: T[], limit: number, fn: (item: T) => Promise<R>): Promise<R[]>;
|
|
9
|
+
declare function browseCatalog(queryClient: ManifestQueryClient, fetchFn?: typeof globalThis.fetch): Promise<{
|
|
10
|
+
providers: {
|
|
11
|
+
healthError?: string | undefined;
|
|
12
|
+
uuid: string;
|
|
13
|
+
address: string;
|
|
14
|
+
apiUrl: string;
|
|
15
|
+
active: boolean;
|
|
16
|
+
healthy: boolean;
|
|
17
|
+
providerUuid: string | undefined;
|
|
18
|
+
}[];
|
|
19
|
+
tiers: Record<string, {
|
|
20
|
+
provider: string;
|
|
21
|
+
price: string | null;
|
|
22
|
+
unit: string | null;
|
|
23
|
+
}[]>;
|
|
24
|
+
}>;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { browseCatalog, mapWithConcurrency };
|
|
27
|
+
//# sourceMappingURL=browseCatalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browseCatalog.d.ts","names":[],"sources":["../../src/tools/browseCatalog.ts"],"mappings":";;;;;AAgBA;;iBAAsB,kBAAA,MAAA,CACpB,KAAA,EAAO,CAAA,IACP,KAAA,UACA,EAAA,GAAK,IAAA,EAAM,CAAA,KAAM,OAAA,CAAQ,CAAA,IACxB,OAAA,CAAQ,CAAA;AAAA,iBAiBW,aAAA,CACpB,WAAA,EAAa,mBAAA,EACb,OAAA,UAAiB,UAAA,CAAW,KAAA,GAAK,OAAA"}
|