@runonflux/flux-cloud-mcp 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.
@@ -0,0 +1,165 @@
1
+ /**
2
+ * FluxOS HTTP client.
3
+ *
4
+ * Every FluxOS response is `{ status: 'success' | 'error', data }`; `unwrap`
5
+ * turns the error shape into a thrown Error with the node's message.
6
+ *
7
+ * POST bodies are sent as text/plain, never application/json. FluxOS installs
8
+ * `express.json()` globally, but the handlers this server needs
9
+ * (/apps/appregister, /apps/appupdate, /apps/calculatefiatandfluxprice,
10
+ * /apps/verifyapp*specifications, /apps/getpublickey) read the raw request
11
+ * stream themselves. With a JSON content type the middleware has already
12
+ * consumed the stream, the handler's 'end' listener never fires and the
13
+ * request hangs until a gateway 504. The payload is still JSON text.
14
+ */
15
+ export class FluxApiError extends Error {
16
+ endpoint;
17
+ constructor(message, endpoint) {
18
+ super(message);
19
+ this.endpoint = endpoint;
20
+ this.name = 'FluxApiError';
21
+ }
22
+ }
23
+ export function unwrap(payload, endpoint) {
24
+ const response = payload;
25
+ if (response && response.status === 'success')
26
+ return response.data;
27
+ if (response && response.status === 'error') {
28
+ const data = response.data;
29
+ const message = typeof data === 'string' ? data : (data?.message ?? data?.name ?? 'unknown FluxOS error');
30
+ throw new FluxApiError(`${endpoint}: ${message}`, endpoint);
31
+ }
32
+ throw new FluxApiError(`${endpoint}: unexpected response ${JSON.stringify(payload).slice(0, 300)}`, endpoint);
33
+ }
34
+ export function sessionHeader(session) {
35
+ return JSON.stringify(session);
36
+ }
37
+ export class FluxClient {
38
+ baseUrl;
39
+ defaultTimeoutMs;
40
+ constructor(baseUrl, defaultTimeoutMs = 60000) {
41
+ this.baseUrl = baseUrl;
42
+ this.defaultTimeoutMs = defaultTimeoutMs;
43
+ }
44
+ async raw(method, pathname, options = {}) {
45
+ const headers = {};
46
+ let body;
47
+ if (options.body !== undefined) {
48
+ headers['Content-Type'] = 'text/plain';
49
+ body = typeof options.body === 'string' ? options.body : JSON.stringify(options.body);
50
+ }
51
+ if (options.session)
52
+ headers.zelidauth = sessionHeader(options.session);
53
+ const url = `${this.baseUrl}${pathname}`;
54
+ let response;
55
+ try {
56
+ response = await fetch(url, {
57
+ method,
58
+ headers,
59
+ ...(body === undefined ? {} : { body }),
60
+ signal: AbortSignal.timeout(options.timeoutMs ?? this.defaultTimeoutMs),
61
+ });
62
+ }
63
+ catch (error) {
64
+ throw new FluxApiError(`${url}: ${error.message}`, pathname);
65
+ }
66
+ const text = await response.text();
67
+ if (!response.ok) {
68
+ throw new FluxApiError(`${url}: HTTP ${response.status} ${text.slice(0, 200)}`, pathname);
69
+ }
70
+ try {
71
+ return JSON.parse(text);
72
+ }
73
+ catch {
74
+ throw new FluxApiError(`${url}: non-JSON response ${text.slice(0, 200)}`, pathname);
75
+ }
76
+ }
77
+ async get(pathname, options = {}) {
78
+ return unwrap(await this.raw('GET', pathname, options), pathname);
79
+ }
80
+ async post(pathname, body, options = {}) {
81
+ return unwrap(await this.raw('POST', pathname, { ...options, body }), pathname);
82
+ }
83
+ }
84
+ /** FluxOS refuses registrations on nodes with fewer peers than this. */
85
+ export const MIN_OUTGOING_PEERS = 8;
86
+ export const MIN_INCOMING_PEERS = 4;
87
+ export async function listNodeEndpoints(api) {
88
+ const nodes = await api.get('/daemon/viewdeterministiczelnodelist', {
89
+ timeoutMs: 120000,
90
+ });
91
+ return nodes
92
+ .filter((node) => node.ip && node.network === 'ipv4')
93
+ .map((node) => {
94
+ const [ip, port] = node.ip.split(':');
95
+ return { endpoint: `http://${ip}:${port ?? '16127'}`, ip: ip ?? '', tier: node.tier };
96
+ });
97
+ }
98
+ export async function nodeHealth(node) {
99
+ const [outgoing, incoming, info] = await Promise.all([
100
+ node.get('/flux/connectedpeersinfo', { timeoutMs: 12000 }),
101
+ node.get('/flux/incomingconnectionsinfo', { timeoutMs: 12000 }),
102
+ node
103
+ .get('/flux/info', { timeoutMs: 12000 })
104
+ .catch(() => undefined),
105
+ ]);
106
+ return {
107
+ outgoing: outgoing.length,
108
+ incoming: incoming.length,
109
+ ok: outgoing.length >= MIN_OUTGOING_PEERS && incoming.length >= MIN_INCOMING_PEERS,
110
+ arcaneVersion: info?.flux?.arcaneVersion,
111
+ };
112
+ }
113
+ function shuffle(list) {
114
+ const copy = [...list];
115
+ for (let i = copy.length - 1; i > 0; i -= 1) {
116
+ const j = Math.floor(Math.random() * (i + 1));
117
+ const a = copy[i];
118
+ copy[i] = copy[j];
119
+ copy[j] = a;
120
+ }
121
+ return copy;
122
+ }
123
+ /**
124
+ * Probe random nodes until `count` of them meet the peer thresholds. With
125
+ * `arcane` only ArcaneOS nodes qualify: they alone hold the key that
126
+ * decrypts enterprise specifications, so only they can validate or accept one.
127
+ */
128
+ export async function findHealthyNodes(api, count, options = {}) {
129
+ const { arcane = false, batchSize = 12, maxProbes = 240, log = () => { } } = options;
130
+ const pool = shuffle(await listNodeEndpoints(api));
131
+ const healthy = [];
132
+ let probed = 0;
133
+ while (healthy.length < count && probed < Math.min(maxProbes, pool.length)) {
134
+ const batch = pool.slice(probed, probed + batchSize);
135
+ probed += batch.length;
136
+ const results = await Promise.all(batch.map(async (candidate) => {
137
+ const client = new FluxClient(candidate.endpoint);
138
+ try {
139
+ const health = await nodeHealth(client);
140
+ if (!health.ok)
141
+ return undefined;
142
+ if (arcane && !health.arcaneVersion)
143
+ return undefined;
144
+ return { ...candidate, client, health };
145
+ }
146
+ catch {
147
+ return undefined;
148
+ }
149
+ }));
150
+ for (const result of results)
151
+ if (result)
152
+ healthy.push(result);
153
+ log(`probed ${probed} nodes, ${healthy.length} usable`);
154
+ }
155
+ if (!healthy.length) {
156
+ throw new Error(`No ${arcane ? 'ArcaneOS ' : ''}FluxOS node with enough peers found after probing ${probed} candidates`);
157
+ }
158
+ return healthy.slice(0, count);
159
+ }
160
+ /** `http://ip:port` for an app instance reported by /apps/location. */
161
+ export function instanceEndpoint(ip) {
162
+ const [host, port] = ip.split(':');
163
+ return `http://${host}:${port ?? '16127'}`;
164
+ }
165
+ //# sourceMappingURL=fluxapi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fluxapi.js","sourceRoot":"","sources":["../src/fluxapi.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { loadConfig } from './config.js';
4
+ import { createServer } from './server.js';
5
+ async function main() {
6
+ const config = loadConfig();
7
+ const server = createServer(config);
8
+ const transport = new StdioServerTransport();
9
+ await server.connect(transport);
10
+ process.stderr.write(`flux-cloud-mcp ready (api ${config.apiUrl}, keys ${config.ownerWif && config.payerWif ? 'configured' : 'missing'})\n`);
11
+ }
12
+ main().catch((error) => {
13
+ process.stderr.write(`flux-cloud-mcp failed to start: ${error.message}\n`);
14
+ process.exit(1);
15
+ });
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,6BAA6B,MAAM,CAAC,MAAM,UAAU,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,KAAK,CACvH,CAAC;AACJ,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC9B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mCAAoC,KAAe,CAAC,OAAO,IAAI,CAAC,CAAC;IACtF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
package/dist/keys.d.ts ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Key material and signatures.
3
+ *
4
+ * Flux ID (ZelID) a Bitcoin-style P2PKH address ("1..."), derived from a
5
+ * secp256k1 key. Owns apps, signs messages and sessions.
6
+ * Payment address a Flux transparent address ("t1...") from a second key.
7
+ * Holds FLUX and pays deployment fees.
8
+ *
9
+ * Both use the same WIF encoding (version byte 0x80), so one WIF can be read
10
+ * as either identity; which role it plays is decided by the caller.
11
+ */
12
+ export interface Identity {
13
+ wif: string;
14
+ publicKey: string;
15
+ /** Flux ID / ZelID: the app owner address ("1..."). */
16
+ zelid: string;
17
+ /** Flux transparent address ("t1..."), the one that can hold FLUX. */
18
+ fluxAddress: string;
19
+ }
20
+ export declare function identityFromWif(wif: string): Identity;
21
+ export declare function generateIdentity(): Identity;
22
+ /**
23
+ * Standard Bitcoin signed-message signature, base64. This is what FluxOS
24
+ * `signatureVerifier.verifySignature` checks for a "1..." owner.
25
+ */
26
+ export declare function signMessage(message: string, wif: string): string;
27
+ export declare function verifyMessage(message: string, zelid: string, signature: string): boolean;
28
+ export interface Session {
29
+ zelid: string;
30
+ signature: string;
31
+ loginPhrase: string;
32
+ }
33
+ /**
34
+ * A self-issued FluxOS session.
35
+ *
36
+ * FluxOS accepts a login phrase it never issued as long as it starts with a
37
+ * 13-digit millisecond timestamp less than 16 hours old, is 40-70 characters
38
+ * long, and is signed by the ZelID (verificationHelperUtils.verifyUserSession).
39
+ * That means one signed phrase authenticates against every node on the
40
+ * network, with no login round-trip and no node affinity.
41
+ */
42
+ export declare function createSession(ownerWif: string): Session;
43
+ /** The same session for the whole process, re-issued well before it ages out. */
44
+ export declare function currentSession(ownerWif: string): Session;
package/dist/keys.js ADDED
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Key material and signatures.
3
+ *
4
+ * Flux ID (ZelID) a Bitcoin-style P2PKH address ("1..."), derived from a
5
+ * secp256k1 key. Owns apps, signs messages and sessions.
6
+ * Payment address a Flux transparent address ("t1...") from a second key.
7
+ * Holds FLUX and pays deployment fees.
8
+ *
9
+ * Both use the same WIF encoding (version byte 0x80), so one WIF can be read
10
+ * as either identity; which role it plays is decided by the caller.
11
+ */
12
+ import { randomBytes } from 'node:crypto';
13
+ import utxolib from '@runonflux/utxo-lib';
14
+ import bitcoinMessage from 'bitcoinjs-message';
15
+ function keyPairFromWif(wif) {
16
+ try {
17
+ return utxolib.ECPair.fromWIF(wif, utxolib.networks.flux);
18
+ }
19
+ catch (error) {
20
+ throw new Error(`Not a valid WIF private key: ${error.message}`, { cause: error });
21
+ }
22
+ }
23
+ export function identityFromWif(wif) {
24
+ const flux = keyPairFromWif(wif);
25
+ const bitcoin = utxolib.ECPair.fromWIF(wif, utxolib.networks.bitcoin);
26
+ return {
27
+ wif,
28
+ publicKey: flux.getPublicKeyBuffer().toString('hex'),
29
+ zelid: bitcoin.getAddress(),
30
+ fluxAddress: flux.getAddress(),
31
+ };
32
+ }
33
+ export function generateIdentity() {
34
+ const pair = utxolib.ECPair.makeRandom({ network: utxolib.networks.flux, compressed: true });
35
+ return identityFromWif(pair.toWIF());
36
+ }
37
+ /**
38
+ * Standard Bitcoin signed-message signature, base64. This is what FluxOS
39
+ * `signatureVerifier.verifySignature` checks for a "1..." owner.
40
+ */
41
+ export function signMessage(message, wif) {
42
+ const pair = keyPairFromWif(wif);
43
+ return bitcoinMessage.sign(message, pair.d.toBuffer(32), pair.compressed).toString('base64');
44
+ }
45
+ export function verifyMessage(message, zelid, signature) {
46
+ try {
47
+ return bitcoinMessage.verify(message, zelid, signature);
48
+ }
49
+ catch {
50
+ return false;
51
+ }
52
+ }
53
+ /**
54
+ * A self-issued FluxOS session.
55
+ *
56
+ * FluxOS accepts a login phrase it never issued as long as it starts with a
57
+ * 13-digit millisecond timestamp less than 16 hours old, is 40-70 characters
58
+ * long, and is signed by the ZelID (verificationHelperUtils.verifyUserSession).
59
+ * That means one signed phrase authenticates against every node on the
60
+ * network, with no login round-trip and no node affinity.
61
+ */
62
+ export function createSession(ownerWif) {
63
+ const identity = identityFromWif(ownerWif);
64
+ const loginPhrase = `${Date.now()}${randomBytes(16).toString('hex')}`;
65
+ return { zelid: identity.zelid, signature: signMessage(loginPhrase, ownerWif), loginPhrase };
66
+ }
67
+ const SESSION_TTL_MS = 8 * 60 * 60 * 1000;
68
+ let cachedSession;
69
+ /** The same session for the whole process, re-issued well before it ages out. */
70
+ export function currentSession(ownerWif) {
71
+ if (cachedSession &&
72
+ cachedSession.wif === ownerWif &&
73
+ Date.now() - cachedSession.issuedAt < SESSION_TTL_MS) {
74
+ return cachedSession.session;
75
+ }
76
+ cachedSession = { session: createSession(ownerWif), wif: ownerWif, issuedAt: Date.now() };
77
+ return cachedSession.session;
78
+ }
79
+ //# sourceMappingURL=keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Pricing.
3
+ *
4
+ * Flux Cloud has two price tables and this server deliberately quotes only one:
5
+ *
6
+ * USD table what Flux Cloud (home.runonflux.io) charges. Per-resource USD
7
+ * rates, a $0.99 minimum, hardware and term discounts, converted
8
+ * to FLUX at market rate with a 5% discount for paying in FLUX.
9
+ * Served by every node at POST /apps/calculatefiatandfluxprice.
10
+ * THIS IS THE PRICE.
11
+ *
12
+ * chain table what consensus enforces as the bare minimum for a message to
13
+ * be accepted (messageVerifier.js). It has no USD floor and is
14
+ * several times lower. It is used here only as a safety guard
15
+ * before broadcasting a payment, never shown as a quote.
16
+ */
17
+ import type { FluxClient } from './fluxapi.js';
18
+ import type { AppSpec } from './spec.js';
19
+ export interface UsdRates {
20
+ cpu: number;
21
+ ram: number;
22
+ hdd: number;
23
+ minPrice: number;
24
+ port: number;
25
+ scope: number;
26
+ staticip: number;
27
+ fluxmultiplier: number;
28
+ multiplier: number;
29
+ minUSDPrice: number;
30
+ }
31
+ /** Bundled fallback, identical to ZelBack/config/default.js `usdprice` (Sept 2026). */
32
+ export declare const DEFAULT_USD_RATES: UsdRates;
33
+ export interface ChainPriceInterval {
34
+ height: number;
35
+ cpu: number;
36
+ ram: number;
37
+ hdd: number;
38
+ minPrice: number;
39
+ port: number;
40
+ scope: number;
41
+ staticip: number;
42
+ }
43
+ export interface DeploymentInformation {
44
+ price: ChainPriceInterval[];
45
+ address: string;
46
+ minimumInstances?: number;
47
+ maximumInstances?: number;
48
+ minBlocksAllowance?: number;
49
+ maxBlocksAllowance?: number;
50
+ blocksLasting?: number;
51
+ }
52
+ export interface Quote {
53
+ /** The price, in US dollars, for the whole term. */
54
+ usd: number;
55
+ /** FLUX to send for the whole term, at market rate, with the pay-in-FLUX discount applied. */
56
+ flux: number;
57
+ /** Discount in percent for paying with FLUX rather than card. */
58
+ fluxDiscountPercent: number | string;
59
+ /** Term length in blocks and in network months. */
60
+ expireBlocks: number;
61
+ months: number;
62
+ usdPerMonth: number;
63
+ /** FLUX/USD market rate implied by the quote. */
64
+ fluxUsdRate: number | null;
65
+ source: 'flux-cloud' | 'local-estimate';
66
+ }
67
+ /** Live USD quote from the network, for a registration or an update of an existing app. */
68
+ export declare function quoteFromNetwork(api: FluxClient, spec: AppSpec): Promise<Quote>;
69
+ export declare function fetchUsdRates(statsUrl: string): Promise<UsdRates>;
70
+ /** FLUX price in USD, the way FluxOS derives it: BTC/USD from the rates service times FLUX/BTC. */
71
+ export declare function fetchFluxUsdRate(ratesUrl: string): Promise<number>;
72
+ /**
73
+ * Monthly USD price of a specification, mirroring appPricePerMonth() with the
74
+ * USD table. Per-instance price is a third of the resource total (one
75
+ * instance of three), then multiplied out to the instance count.
76
+ */
77
+ export declare function usdPerMonth(spec: AppSpec, rates?: UsdRates): number;
78
+ /**
79
+ * Local mirror of the Flux Cloud USD price for a NEW registration, used for
80
+ * instant what-if estimates and to sanity check the network's answer. It does
81
+ * not know about update credits or marketplace multipliers, so the network
82
+ * quote stays authoritative.
83
+ */
84
+ export declare function estimateUsd(spec: AppSpec, rates?: UsdRates): number;
85
+ export declare function estimateQuote(spec: AppSpec, rates: UsdRates, fluxUsdRate: number): Quote;
86
+ export declare function chainInterval(table: ChainPriceInterval[], height: number): ChainPriceInterval;
87
+ /**
88
+ * The minimum FLUX consensus will accept for this message at `height`
89
+ * (messageVerifier.js). For updates the network additionally credits the
90
+ * unused part of the previous term, so the true minimum is lower still; using
91
+ * the registration formula errs on the safe side.
92
+ */
93
+ export declare function consensusMinimumFlux(spec: AppSpec, table: ChainPriceInterval[], height: number): number;
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Pricing.
3
+ *
4
+ * Flux Cloud has two price tables and this server deliberately quotes only one:
5
+ *
6
+ * USD table what Flux Cloud (home.runonflux.io) charges. Per-resource USD
7
+ * rates, a $0.99 minimum, hardware and term discounts, converted
8
+ * to FLUX at market rate with a 5% discount for paying in FLUX.
9
+ * Served by every node at POST /apps/calculatefiatandfluxprice.
10
+ * THIS IS THE PRICE.
11
+ *
12
+ * chain table what consensus enforces as the bare minimum for a message to
13
+ * be accepted (messageVerifier.js). It has no USD floor and is
14
+ * several times lower. It is used here only as a safety guard
15
+ * before broadcasting a payment, never shown as a quote.
16
+ */
17
+ import { BLOCKS_PER_MONTH, isPortEnterprise, totalResources } from './spec.js';
18
+ /** Bundled fallback, identical to ZelBack/config/default.js `usdprice` (Sept 2026). */
19
+ export const DEFAULT_USD_RATES = {
20
+ cpu: 0.15,
21
+ ram: 0.05,
22
+ hdd: 0.02,
23
+ minPrice: 0.01,
24
+ port: 2,
25
+ scope: 4,
26
+ staticip: 2,
27
+ fluxmultiplier: 0.95,
28
+ multiplier: 1,
29
+ minUSDPrice: 0.99,
30
+ };
31
+ /** Live USD quote from the network, for a registration or an update of an existing app. */
32
+ export async function quoteFromNetwork(api, spec) {
33
+ const price = await api.post('/apps/calculatefiatandfluxprice', spec, { timeoutMs: 60000 });
34
+ const months = spec.expire / BLOCKS_PER_MONTH;
35
+ const usd = Number(price.usd);
36
+ const flux = Number(price.flux);
37
+ const discount = typeof price.fluxDiscount === 'number' ? price.fluxDiscount : 0;
38
+ const rate = flux > 0 && usd > 0 ? (usd / flux) * (1 - discount / 100) : null;
39
+ return {
40
+ usd,
41
+ flux,
42
+ fluxDiscountPercent: price.fluxDiscount,
43
+ expireBlocks: spec.expire,
44
+ months: round2(months),
45
+ usdPerMonth: months > 0 ? round2(usd / months) : usd,
46
+ fluxUsdRate: rate === null ? null : Number(rate.toFixed(4)),
47
+ source: 'flux-cloud',
48
+ };
49
+ }
50
+ export async function fetchUsdRates(statsUrl) {
51
+ try {
52
+ const response = await fetch(`${statsUrl}/apps/getappspecsusdprice`, {
53
+ signal: AbortSignal.timeout(10000),
54
+ });
55
+ const payload = (await response.json());
56
+ if (payload.status === 'success' && payload.data && payload.data.cpu)
57
+ return payload.data;
58
+ }
59
+ catch {
60
+ // fall through to the bundled table
61
+ }
62
+ return DEFAULT_USD_RATES;
63
+ }
64
+ /** FLUX price in USD, the way FluxOS derives it: BTC/USD from the rates service times FLUX/BTC. */
65
+ export async function fetchFluxUsdRate(ratesUrl) {
66
+ const response = await fetch(`${ratesUrl}/rates`, { signal: AbortSignal.timeout(10000) });
67
+ const payload = (await response.json());
68
+ const usd = payload[0]?.find((rate) => rate.code === 'USD');
69
+ const fluxInBtc = payload[1]?.FLUX;
70
+ if (!usd || fluxInBtc === undefined)
71
+ throw new Error('Rates service returned no FLUX/USD rate');
72
+ return usd.rate * fluxInBtc;
73
+ }
74
+ function round2(value) {
75
+ return Math.round(value * 100) / 100;
76
+ }
77
+ function ceil2(value) {
78
+ return Math.ceil(value * 100) / 100;
79
+ }
80
+ /**
81
+ * Monthly USD price of a specification, mirroring appPricePerMonth() with the
82
+ * USD table. Per-instance price is a third of the resource total (one
83
+ * instance of three), then multiplied out to the instance count.
84
+ */
85
+ export function usdPerMonth(spec, rates = DEFAULT_USD_RATES) {
86
+ const res = totalResources(spec);
87
+ const enterprisePorts = spec.compose.flatMap((c) => c.ports.filter(isPortEnterprise)).length;
88
+ let total = res.cpu * rates.cpu * 10 + (res.ram * rates.ram) / 100 + res.hdd * rates.hdd;
89
+ if (spec.nodes.length || spec.enterprise)
90
+ total += rates.scope;
91
+ if (spec.staticip)
92
+ total += rates.staticip;
93
+ total += enterprisePorts * rates.port;
94
+ let price = ceil2(total / 3);
95
+ const extra = spec.instances - 1;
96
+ if (extra > 0) {
97
+ if (price < 0.5 && extra > 2)
98
+ price += extra * 0.5;
99
+ else
100
+ price = (Math.ceil(price * extra * 100) + Math.ceil(price * 100)) / 100;
101
+ }
102
+ if (price < rates.minUSDPrice)
103
+ price = Number(rates.minUSDPrice.toFixed(2));
104
+ return price;
105
+ }
106
+ /**
107
+ * Local mirror of the Flux Cloud USD price for a NEW registration, used for
108
+ * instant what-if estimates and to sanity check the network's answer. It does
109
+ * not know about update credits or marketplace multipliers, so the network
110
+ * quote stays authoritative.
111
+ */
112
+ export function estimateUsd(spec, rates = DEFAULT_USD_RATES) {
113
+ const months = spec.expire / BLOCKS_PER_MONTH;
114
+ let price = Number((usdPerMonth(spec, rates) * months).toFixed(2));
115
+ const res = totalResources(spec);
116
+ if (spec.instances < 4) {
117
+ if (res.cpu < 3 && res.ram < 6000 && res.hdd < 150)
118
+ price *= 0.8;
119
+ else if (res.cpu < 7 && res.ram < 29000 && res.hdd < 370)
120
+ price *= 0.9;
121
+ }
122
+ if (spec.compose.some((c) => c.containerData.includes('g:')))
123
+ price *= 0.8;
124
+ price = Number((price * rates.multiplier).toFixed(2));
125
+ if (price < rates.minUSDPrice)
126
+ price = rates.minUSDPrice;
127
+ if (months >= 9)
128
+ price *= 0.88;
129
+ else if (months >= 6)
130
+ price *= 0.94;
131
+ else if (months >= 3)
132
+ price *= 0.97;
133
+ price = Number(price.toFixed(2));
134
+ if (price < rates.minUSDPrice)
135
+ price = rates.minUSDPrice;
136
+ return price;
137
+ }
138
+ export function estimateQuote(spec, rates, fluxUsdRate) {
139
+ const usd = estimateUsd(spec, rates);
140
+ const months = spec.expire / BLOCKS_PER_MONTH;
141
+ return {
142
+ usd,
143
+ flux: round2((usd / fluxUsdRate) * rates.fluxmultiplier),
144
+ fluxDiscountPercent: round2(100 - rates.fluxmultiplier * 100),
145
+ expireBlocks: spec.expire,
146
+ months: round2(months),
147
+ usdPerMonth: months > 0 ? round2(usd / months) : usd,
148
+ fluxUsdRate: Number(fluxUsdRate.toFixed(4)),
149
+ source: 'local-estimate',
150
+ };
151
+ }
152
+ // ---------------------------------------------------------------------------
153
+ // Consensus floor. Guard only; never quoted.
154
+ // ---------------------------------------------------------------------------
155
+ export function chainInterval(table, height) {
156
+ const applicable = table.filter((entry) => entry.height < height);
157
+ const last = applicable[applicable.length - 1];
158
+ if (!last)
159
+ throw new Error(`No chain price interval applies at height ${height}`);
160
+ return last;
161
+ }
162
+ function chainPerMonth(spec, interval) {
163
+ const res = totalResources(spec);
164
+ const enterprisePorts = spec.compose.flatMap((c) => c.ports.filter(isPortEnterprise)).length;
165
+ let total = res.cpu * interval.cpu * 10 + (res.ram * interval.ram) / 100 + res.hdd * interval.hdd;
166
+ if (spec.nodes.length || spec.enterprise)
167
+ total += interval.scope;
168
+ if (spec.staticip)
169
+ total += interval.staticip;
170
+ total += enterprisePorts * interval.port;
171
+ let price = ceil2(total / 3);
172
+ const extra = spec.instances - 1;
173
+ if (extra > 0) {
174
+ if (price < 0.5 && extra > 2)
175
+ price += extra * 0.5;
176
+ else
177
+ price = (Math.ceil(price * extra * 100) + Math.ceil(price * 100)) / 100;
178
+ }
179
+ return price;
180
+ }
181
+ /**
182
+ * The minimum FLUX consensus will accept for this message at `height`
183
+ * (messageVerifier.js). For updates the network additionally credits the
184
+ * unused part of the previous term, so the true minimum is lower still; using
185
+ * the registration formula errs on the safe side.
186
+ */
187
+ export function consensusMinimumFlux(spec, table, height) {
188
+ const interval = chainInterval(table, height);
189
+ const required = ceil2(chainPerMonth(spec, interval) * (spec.expire / BLOCKS_PER_MONTH));
190
+ return Math.max(required, interval.minPrice);
191
+ }
192
+ //# sourceMappingURL=pricing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.js","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The Flux Cloud MCP server: tools, resources and prompts.
3
+ *
4
+ * Every price shown to an agent is the Flux Cloud USD price. The consensus
5
+ * minimum is never surfaced as a quote.
6
+ */
7
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
8
+ import type { Config } from './config.js';
9
+ import { walletFromConfig } from './deploy.js';
10
+ export declare const SERVER_NAME = "flux-cloud";
11
+ export declare const SERVER_VERSION = "0.1.0";
12
+ export declare function createServer(config: Config): McpServer;
13
+ export { walletFromConfig };