@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.
- package/LICENSE +21 -0
- package/README.md +178 -0
- package/dist/chain.d.ts +71 -0
- package/dist/chain.js +144 -0
- package/dist/chain.js.map +1 -0
- package/dist/config.d.ts +28 -0
- package/dist/config.js +36 -0
- package/dist/config.js.map +1 -0
- package/dist/deploy.d.ts +111 -0
- package/dist/deploy.js +216 -0
- package/dist/deploy.js.map +1 -0
- package/dist/docs.d.ts +5 -0
- package/dist/docs.js +164 -0
- package/dist/docs.js.map +1 -0
- package/dist/enterprise.d.ts +29 -0
- package/dist/enterprise.js +45 -0
- package/dist/enterprise.js.map +1 -0
- package/dist/fluxapi.d.ts +71 -0
- package/dist/fluxapi.js +165 -0
- package/dist/fluxapi.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/keys.d.ts +44 -0
- package/dist/keys.js +79 -0
- package/dist/keys.js.map +1 -0
- package/dist/pricing.d.ts +93 -0
- package/dist/pricing.js +192 -0
- package/dist/pricing.js.map +1 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.js +877 -0
- package/dist/server.js.map +1 -0
- package/dist/spec.d.ts +144 -0
- package/dist/spec.js +312 -0
- package/dist/spec.js.map +1 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 InFlux Technologies Limited
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Flux Cloud MCP server
|
|
2
|
+
|
|
3
|
+
An [MCP](https://modelcontextprotocol.io) server that lets any AI agent use
|
|
4
|
+
[Flux Cloud](https://runonflux.com), the decentralized cloud: quote an app in
|
|
5
|
+
US dollars, deploy it, pay for it, watch it come up, read its logs, update it,
|
|
6
|
+
and cancel it. Works with Claude Code, Claude Desktop, Cursor, Windsurf,
|
|
7
|
+
OpenCode and every other MCP host.
|
|
8
|
+
|
|
9
|
+
There is no account. Two keys are the whole identity:
|
|
10
|
+
|
|
11
|
+
| Key | Address | Role |
|
|
12
|
+
| -------------------------- | --------------------- | -------------------------------------------------------------------- |
|
|
13
|
+
| `FLUX_ID_PRIVATE_KEY` | Flux ID, `1...` | Owns apps. Signs specifications and API sessions. Never holds funds. |
|
|
14
|
+
| `FLUX_PAYMENT_PRIVATE_KEY` | Flux address, `t1...` | Holds FLUX and pays deployment fees on-chain. |
|
|
15
|
+
|
|
16
|
+
The server generates a pair for you (`flux_generate_keys`); fund the payment
|
|
17
|
+
address with FLUX and deploy.
|
|
18
|
+
|
|
19
|
+
## Pricing policy
|
|
20
|
+
|
|
21
|
+
Every price this server shows is the **Flux Cloud USD price**, the same one
|
|
22
|
+
[home.runonflux.io](https://home.runonflux.io) charges, converted to FLUX at the
|
|
23
|
+
live market rate with the 5 percent pay-in-FLUX discount. Quotes come from the
|
|
24
|
+
network itself (`/apps/calculatefiatandfluxprice`), so they include the $0.99
|
|
25
|
+
minimum, hardware and term discounts, and update credits.
|
|
26
|
+
|
|
27
|
+
The blockchain's own acceptance threshold is several times lower. It is never
|
|
28
|
+
shown as a price; it is only checked as a guard before a payment is broadcast.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install -g @runonflux/flux-cloud-mcp # or: yarn global add @runonflux/flux-cloud-mcp
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Or run it without installing: `npx @runonflux/flux-cloud-mcp`.
|
|
37
|
+
|
|
38
|
+
### Claude Code
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
claude mcp add flux-cloud -s user \
|
|
42
|
+
-e FLUX_ID_PRIVATE_KEY=<wif> -e FLUX_PAYMENT_PRIVATE_KEY=<wif> \
|
|
43
|
+
-- npx -y @runonflux/flux-cloud-mcp
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Claude Desktop, Cursor, Windsurf (JSON config)
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"mcpServers": {
|
|
51
|
+
"flux-cloud": {
|
|
52
|
+
"command": "npx",
|
|
53
|
+
"args": ["-y", "@runonflux/flux-cloud-mcp"],
|
|
54
|
+
"env": {
|
|
55
|
+
"FLUX_ID_PRIVATE_KEY": "<wif of the Flux ID>",
|
|
56
|
+
"FLUX_PAYMENT_PRIVATE_KEY": "<wif of the payment address>"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Keys are optional. Without them every read-only tool works, and
|
|
64
|
+
`flux_generate_keys` will create a pair to put into the config.
|
|
65
|
+
|
|
66
|
+
### OpenCode
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"mcp": {
|
|
71
|
+
"flux-cloud": {
|
|
72
|
+
"type": "local",
|
|
73
|
+
"command": ["npx", "-y", "@runonflux/flux-cloud-mcp"],
|
|
74
|
+
"environment": {
|
|
75
|
+
"FLUX_ID_PRIVATE_KEY": "<wif>",
|
|
76
|
+
"FLUX_PAYMENT_PRIVATE_KEY": "<wif>"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Tools
|
|
84
|
+
|
|
85
|
+
| Tool | What it does | Spends FLUX |
|
|
86
|
+
| ----------------------- | ------------------------------------------------------------------------ | -------------- |
|
|
87
|
+
| `flux_get_identity` | Flux ID, payment address, balance in FLUX and USD | no |
|
|
88
|
+
| `flux_generate_keys` | New Flux ID + payment key pair with setup instructions | no |
|
|
89
|
+
| `flux_get_pricing` | USD rate card, FLUX/USD rate, discounts, reference sizes | no |
|
|
90
|
+
| `flux_build_spec` | Simple description (image, ports, cpu/ram/hdd, months) to a full v8 spec | no |
|
|
91
|
+
| `flux_validate_spec` | Local rules plus verification on a FluxOS node | no |
|
|
92
|
+
| `flux_quote_app` | USD price and FLUX to pay, for a registration or an update | no |
|
|
93
|
+
| `flux_deploy_app` | Plan (default) or, with `confirm=true`, sign, broadcast and pay | with `confirm` |
|
|
94
|
+
| `flux_wait_for_app` | Poll until accepted and instances run; returns URLs | no |
|
|
95
|
+
| `flux_get_app` | Any app's spec, expiry, instances, URLs | no |
|
|
96
|
+
| `flux_list_my_apps` | Apps owned by the Flux ID, with instance counts and days left | no |
|
|
97
|
+
| `flux_get_app_logs` | Container logs from a running instance (owner only) | no |
|
|
98
|
+
| `flux_get_app_stats` | Live CPU/memory/network of an instance (owner only) | no |
|
|
99
|
+
| `flux_control_app` | Restart, redeploy or remove instances, per node or globally | no |
|
|
100
|
+
| `flux_cancel_app` | End an app early by shortening its term | with `confirm` |
|
|
101
|
+
| `flux_get_network_info` | Node counts, height, FLUX/USD, deployment address | no |
|
|
102
|
+
|
|
103
|
+
Resources `flux://guide/overview`, `flux://guide/spec-format`,
|
|
104
|
+
`flux://guide/pricing` and `flux://guide/gotchas` give the agent the domain
|
|
105
|
+
knowledge; the `deploy_on_flux` prompt walks it through a deployment.
|
|
106
|
+
|
|
107
|
+
## A typical session
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
> deploy nginx on flux, 3 instances, one month
|
|
111
|
+
|
|
112
|
+
flux_get_identity -> Flux ID 1Ab..., payer t1Cd..., 996.98 FLUX ($48.74)
|
|
113
|
+
flux_build_spec -> v8 spec, port 39978 -> 80, r:/data, valid
|
|
114
|
+
flux_quote_app -> $0.99 for 1 month, pay 19.24 FLUX (5% FLUX discount)
|
|
115
|
+
user agrees
|
|
116
|
+
flux_deploy_app confirm=true
|
|
117
|
+
-> message hash 8d2f..., paid 19.24 FLUX, txid 0c41...
|
|
118
|
+
flux_wait_for_app -> accepted at height 2941560, 3/3 instances running
|
|
119
|
+
https://myapp.app.runonflux.io
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## How a deployment works
|
|
123
|
+
|
|
124
|
+
1. The spec is verified by a FluxOS node exactly as it will be at
|
|
125
|
+
registration (image reachable, architecture, ports, name free).
|
|
126
|
+
2. The network quotes the USD price; the server checks the payer's balance.
|
|
127
|
+
3. With `confirm=true` the Flux ID signs the spec, one node broadcasts it to
|
|
128
|
+
the network and returns a 64-character message hash.
|
|
129
|
+
4. The payment address sends the quoted FLUX to the network deployment
|
|
130
|
+
address with the hash in an OP_RETURN output. Right before signing, the
|
|
131
|
+
amount is re-checked against a freshly fetched price table, because an
|
|
132
|
+
underpaid message is dropped silently and the FLUX is not refunded.
|
|
133
|
+
5. Nodes pair the confirmed payment with the message and publish the app.
|
|
134
|
+
Instances then spawn and pull the image.
|
|
135
|
+
|
|
136
|
+
Updates use the same tool: an existing app of the same owner gets a
|
|
137
|
+
`fluxappupdate` message and the network credits the unused part of the old
|
|
138
|
+
term. Private (enterprise) apps are supported through the `enterprise`
|
|
139
|
+
argument: components are encrypted for the network and only ArcaneOS nodes
|
|
140
|
+
can run them.
|
|
141
|
+
|
|
142
|
+
## Configuration
|
|
143
|
+
|
|
144
|
+
| Variable | Default | Purpose |
|
|
145
|
+
| -------------------------- | -------------------------------------------------- | --------------------------------------------- |
|
|
146
|
+
| `FLUX_ID_PRIVATE_KEY` | | WIF of the Flux ID (owner) |
|
|
147
|
+
| `FLUX_PAYMENT_PRIVATE_KEY` | | WIF of the paying address |
|
|
148
|
+
| `FLUX_API_URL` | `https://api.runonflux.io` | Load-balanced FluxOS API for reads and quotes |
|
|
149
|
+
| `FLUX_NODE_URL` | auto | Pin one FluxOS node for registrations |
|
|
150
|
+
| `FLUX_EXPLORER_URLS` | `explorer.runonflux.io,flux-explorer.sspwallet.io` | Insight explorers for UTXOs and broadcast |
|
|
151
|
+
| `FLUX_STATS_URL` | `https://stats.runonflux.io` | USD rate card |
|
|
152
|
+
| `FLUX_RATES_URL` | `https://viprates.runonflux.io` | FLUX market rate |
|
|
153
|
+
|
|
154
|
+
## Security notes
|
|
155
|
+
|
|
156
|
+
- Private keys stay in the server process. No tool ever returns a configured
|
|
157
|
+
key; `flux_generate_keys` returns only the keys it just created.
|
|
158
|
+
- Nothing is spent unless a tool is called with `confirm=true`. Hosts that
|
|
159
|
+
ask before tool calls will show the USD amount in the arguments' context.
|
|
160
|
+
- FluxOS sessions are self-issued signed phrases valid for a few hours; no
|
|
161
|
+
password or token is stored anywhere.
|
|
162
|
+
- Use a dedicated payment address holding only what you intend to spend.
|
|
163
|
+
|
|
164
|
+
## Development
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
yarn install
|
|
168
|
+
yarn type-check && yarn lint && yarn format:check && yarn build && yarn test
|
|
169
|
+
yarn test:live # differential test of the USD estimator against the network
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The USD estimator, the specification formatter and the consensus guard mirror
|
|
173
|
+
the FluxOS code (`appSpecHelpers.js`, `appUtilities.js`, `messageVerifier.js`)
|
|
174
|
+
and the live test checks the estimator against the network on every run.
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
MIT
|
package/dist/chain.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flux blockchain access over the public Insight explorer API, plus
|
|
3
|
+
* construction of the deployment payment.
|
|
4
|
+
*
|
|
5
|
+
* A registration payment is an ordinary transparent transaction with two
|
|
6
|
+
* outputs that matter: `amount` FLUX to the network deployment address, and an
|
|
7
|
+
* OP_RETURN carrying the 64-character message hash. Nodes index exactly that
|
|
8
|
+
* pair; nothing else about the payer is consulted, which is why the payment
|
|
9
|
+
* key and the owner Flux ID are independent.
|
|
10
|
+
*/
|
|
11
|
+
export declare const DUST_LIMIT = 546;
|
|
12
|
+
export declare const SATOSHIS = 100000000;
|
|
13
|
+
export interface Utxo {
|
|
14
|
+
txid: string;
|
|
15
|
+
vout: number;
|
|
16
|
+
satoshis: number;
|
|
17
|
+
confirmations: number;
|
|
18
|
+
coinbase: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface Balance {
|
|
21
|
+
total: number;
|
|
22
|
+
spendable: number;
|
|
23
|
+
utxos: Utxo[];
|
|
24
|
+
spendableUtxos: Utxo[];
|
|
25
|
+
}
|
|
26
|
+
export declare function toFlux(satoshis: number): number;
|
|
27
|
+
export declare class Explorer {
|
|
28
|
+
private readonly urls;
|
|
29
|
+
private readonly timeoutMs;
|
|
30
|
+
constructor(urls: string[], timeoutMs?: number);
|
|
31
|
+
private request;
|
|
32
|
+
height(): Promise<number>;
|
|
33
|
+
utxos(address: string): Promise<Utxo[]>;
|
|
34
|
+
balance(address: string): Promise<Balance>;
|
|
35
|
+
broadcast(rawtx: string): Promise<string>;
|
|
36
|
+
transaction(txid: string): Promise<{
|
|
37
|
+
confirmations?: number;
|
|
38
|
+
blockheight?: number;
|
|
39
|
+
}>;
|
|
40
|
+
}
|
|
41
|
+
/** Coinbase outputs are unspendable until mature; unconfirmed inputs are avoided. */
|
|
42
|
+
export declare function selectSpendable(utxos: Utxo[]): Utxo[];
|
|
43
|
+
/** 148 bytes per p2pkh input, 34 per standard output, 10 overhead, plus the OP_RETURN. */
|
|
44
|
+
export declare function estimateSize(inputCount: number, outputCount: number, opReturnBytes: number): number;
|
|
45
|
+
export interface InputSelection {
|
|
46
|
+
inputs: Utxo[];
|
|
47
|
+
total: number;
|
|
48
|
+
fee: number;
|
|
49
|
+
change: number;
|
|
50
|
+
}
|
|
51
|
+
/** Greedily pick the fewest inputs covering amount + fee, recomputing the fee as inputs are added. */
|
|
52
|
+
export declare function selectInputs(utxos: Utxo[], amountSat: number, opReturnBytes: number): InputSelection;
|
|
53
|
+
export interface PaymentParams {
|
|
54
|
+
wif: string;
|
|
55
|
+
to: string;
|
|
56
|
+
amountSat: number;
|
|
57
|
+
message: string;
|
|
58
|
+
utxos: Utxo[];
|
|
59
|
+
height: number;
|
|
60
|
+
maxFeeSat?: number;
|
|
61
|
+
}
|
|
62
|
+
export interface SignedPayment {
|
|
63
|
+
hex: string;
|
|
64
|
+
txid: string;
|
|
65
|
+
fee: number;
|
|
66
|
+
change: number;
|
|
67
|
+
inputs: number;
|
|
68
|
+
sizeBytes: number;
|
|
69
|
+
}
|
|
70
|
+
/** Build and sign the deployment payment. */
|
|
71
|
+
export declare function buildPayment({ wif, to, amountSat, message, utxos, height, maxFeeSat, }: PaymentParams): SignedPayment;
|
package/dist/chain.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flux blockchain access over the public Insight explorer API, plus
|
|
3
|
+
* construction of the deployment payment.
|
|
4
|
+
*
|
|
5
|
+
* A registration payment is an ordinary transparent transaction with two
|
|
6
|
+
* outputs that matter: `amount` FLUX to the network deployment address, and an
|
|
7
|
+
* OP_RETURN carrying the 64-character message hash. Nodes index exactly that
|
|
8
|
+
* pair; nothing else about the payer is consulted, which is why the payment
|
|
9
|
+
* key and the owner Flux ID are independent.
|
|
10
|
+
*/
|
|
11
|
+
import utxolib from '@runonflux/utxo-lib';
|
|
12
|
+
const NETWORK = utxolib.networks.flux;
|
|
13
|
+
const TX_VERSION = 4; // sapling
|
|
14
|
+
const TX_VERSION_GROUP_ID = 0x892f2085;
|
|
15
|
+
const TX_EXPIRY_DELTA = 30; // blocks
|
|
16
|
+
export const DUST_LIMIT = 546;
|
|
17
|
+
const FEE_PER_BYTE = 1;
|
|
18
|
+
const COINBASE_MATURITY = 100;
|
|
19
|
+
export const SATOSHIS = 1e8;
|
|
20
|
+
export function toFlux(satoshis) {
|
|
21
|
+
return Number((satoshis / SATOSHIS).toFixed(8));
|
|
22
|
+
}
|
|
23
|
+
export class Explorer {
|
|
24
|
+
urls;
|
|
25
|
+
timeoutMs;
|
|
26
|
+
constructor(urls, timeoutMs = 30000) {
|
|
27
|
+
this.urls = urls;
|
|
28
|
+
this.timeoutMs = timeoutMs;
|
|
29
|
+
}
|
|
30
|
+
async request(method, pathname, body) {
|
|
31
|
+
let lastError;
|
|
32
|
+
for (const base of this.urls) {
|
|
33
|
+
try {
|
|
34
|
+
const response = await fetch(`${base}${pathname}`, {
|
|
35
|
+
method,
|
|
36
|
+
headers: body === undefined ? {} : { 'Content-Type': 'application/json' },
|
|
37
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
38
|
+
signal: AbortSignal.timeout(this.timeoutMs),
|
|
39
|
+
});
|
|
40
|
+
const text = await response.text();
|
|
41
|
+
if (!response.ok)
|
|
42
|
+
throw new Error(`HTTP ${response.status} ${text.slice(0, 200)}`);
|
|
43
|
+
return JSON.parse(text);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
lastError = new Error(`${base}${pathname}: ${error.message}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
throw lastError ?? new Error('No explorer configured');
|
|
50
|
+
}
|
|
51
|
+
async height() {
|
|
52
|
+
const status = (await this.request('GET', '/api/sync'));
|
|
53
|
+
const height = Number(status.blockChainHeight ?? status.height);
|
|
54
|
+
if (!Number.isFinite(height))
|
|
55
|
+
throw new Error('Explorer returned no block height');
|
|
56
|
+
return height;
|
|
57
|
+
}
|
|
58
|
+
async utxos(address) {
|
|
59
|
+
const list = (await this.request('GET', `/api/addr/${address}/utxo`));
|
|
60
|
+
return (Array.isArray(list) ? list : []).map((u) => ({
|
|
61
|
+
txid: String(u.txid),
|
|
62
|
+
vout: Number(u.vout),
|
|
63
|
+
satoshis: Number(u.satoshis),
|
|
64
|
+
confirmations: Number(u.confirmations ?? 0),
|
|
65
|
+
coinbase: Boolean(u.coinbase),
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
async balance(address) {
|
|
69
|
+
const utxos = await this.utxos(address);
|
|
70
|
+
const spendable = selectSpendable(utxos);
|
|
71
|
+
const sum = (list) => list.reduce((acc, u) => acc + u.satoshis, 0);
|
|
72
|
+
return { total: sum(utxos), spendable: sum(spendable), utxos, spendableUtxos: spendable };
|
|
73
|
+
}
|
|
74
|
+
async broadcast(rawtx) {
|
|
75
|
+
const result = (await this.request('POST', '/api/tx/send', { rawtx }));
|
|
76
|
+
const txid = typeof result?.txid === 'object' ? result.txid?.result : (result?.txid ?? result?.result);
|
|
77
|
+
if (!txid)
|
|
78
|
+
throw new Error(`Broadcast returned no txid: ${JSON.stringify(result)}`);
|
|
79
|
+
return String(txid);
|
|
80
|
+
}
|
|
81
|
+
async transaction(txid) {
|
|
82
|
+
return (await this.request('GET', `/api/tx/${txid}`));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/** Coinbase outputs are unspendable until mature; unconfirmed inputs are avoided. */
|
|
86
|
+
export function selectSpendable(utxos) {
|
|
87
|
+
return utxos.filter((u) => {
|
|
88
|
+
if (u.coinbase && u.confirmations < COINBASE_MATURITY)
|
|
89
|
+
return false;
|
|
90
|
+
return u.confirmations >= 1;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/** 148 bytes per p2pkh input, 34 per standard output, 10 overhead, plus the OP_RETURN. */
|
|
94
|
+
export function estimateSize(inputCount, outputCount, opReturnBytes) {
|
|
95
|
+
const opReturn = opReturnBytes ? 9 + opReturnBytes + 2 : 0;
|
|
96
|
+
return inputCount * 148 + outputCount * 34 + 10 + opReturn;
|
|
97
|
+
}
|
|
98
|
+
/** Greedily pick the fewest inputs covering amount + fee, recomputing the fee as inputs are added. */
|
|
99
|
+
export function selectInputs(utxos, amountSat, opReturnBytes) {
|
|
100
|
+
const sorted = [...utxos].sort((a, b) => b.satoshis - a.satoshis);
|
|
101
|
+
const chosen = [];
|
|
102
|
+
let total = 0;
|
|
103
|
+
for (const utxo of sorted) {
|
|
104
|
+
chosen.push(utxo);
|
|
105
|
+
total += utxo.satoshis;
|
|
106
|
+
const withChange = estimateSize(chosen.length, 2, opReturnBytes) * FEE_PER_BYTE;
|
|
107
|
+
const withoutChange = estimateSize(chosen.length, 1, opReturnBytes) * FEE_PER_BYTE;
|
|
108
|
+
if (total >= amountSat + withChange) {
|
|
109
|
+
return { inputs: chosen, total, fee: withChange, change: total - amountSat - withChange };
|
|
110
|
+
}
|
|
111
|
+
if (total >= amountSat + withoutChange && total - amountSat - withoutChange < DUST_LIMIT) {
|
|
112
|
+
return { inputs: chosen, total, fee: total - amountSat, change: 0 };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const needed = amountSat + estimateSize(Math.max(chosen.length, 1), 2, opReturnBytes) * FEE_PER_BYTE;
|
|
116
|
+
throw new Error(`Insufficient funds: have ${toFlux(total)} FLUX spendable, need about ${toFlux(needed)} FLUX`);
|
|
117
|
+
}
|
|
118
|
+
/** Build and sign the deployment payment. */
|
|
119
|
+
export function buildPayment({ wif, to, amountSat, message, utxos, height, maxFeeSat = 1000000, }) {
|
|
120
|
+
const keyPair = utxolib.ECPair.fromWIF(wif, NETWORK);
|
|
121
|
+
const changeAddress = keyPair.getAddress();
|
|
122
|
+
const opReturnBytes = Buffer.byteLength(message, 'utf8');
|
|
123
|
+
if (opReturnBytes > 80)
|
|
124
|
+
throw new Error('OP_RETURN payload exceeds 80 bytes');
|
|
125
|
+
const { inputs, fee, change } = selectInputs(utxos, amountSat, opReturnBytes);
|
|
126
|
+
if (fee > maxFeeSat)
|
|
127
|
+
throw new Error(`Refusing to sign: fee ${fee} sat exceeds cap ${maxFeeSat} sat`);
|
|
128
|
+
const builder = new utxolib.TransactionBuilder(NETWORK, fee);
|
|
129
|
+
builder.setVersion(TX_VERSION);
|
|
130
|
+
builder.setVersionGroupId(TX_VERSION_GROUP_ID);
|
|
131
|
+
builder.setExpiryHeight(height + TX_EXPIRY_DELTA);
|
|
132
|
+
inputs.forEach((utxo) => builder.addInput(utxo.txid, utxo.vout));
|
|
133
|
+
builder.addOutput(to, amountSat);
|
|
134
|
+
if (change >= DUST_LIMIT)
|
|
135
|
+
builder.addOutput(changeAddress, change);
|
|
136
|
+
builder.addOutput(utxolib.script.nullData.output.encode(Buffer.from(message, 'utf8')), 0);
|
|
137
|
+
inputs.forEach((utxo, index) => {
|
|
138
|
+
builder.sign(index, keyPair, undefined, utxolib.Transaction.SIGHASH_ALL, utxo.satoshis);
|
|
139
|
+
});
|
|
140
|
+
const tx = builder.build();
|
|
141
|
+
const hex = tx.toHex();
|
|
142
|
+
return { hex, txid: tx.getId(), fee, change, inputs: inputs.length, sizeBytes: hex.length / 2 };
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=chain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chain.js","sourceRoot":"","sources":["../src/chain.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime configuration, read from the environment the MCP host passes in.
|
|
3
|
+
*
|
|
4
|
+
* Two independent keys are used, exactly as the Flux network models them:
|
|
5
|
+
*
|
|
6
|
+
* FLUX_ID_PRIVATE_KEY WIF of the Flux ID (ZelID). It is the app owner:
|
|
7
|
+
* it signs registration messages and API sessions.
|
|
8
|
+
* It never holds funds.
|
|
9
|
+
* FLUX_PAYMENT_PRIVATE_KEY WIF of a Flux transparent address (t1...). It
|
|
10
|
+
* holds FLUX and pays deployment fees on-chain.
|
|
11
|
+
*
|
|
12
|
+
* Both are optional: without them every read-only tool still works, and the
|
|
13
|
+
* server can generate a fresh pair for the user.
|
|
14
|
+
*/
|
|
15
|
+
export interface Config {
|
|
16
|
+
ownerWif: string | undefined;
|
|
17
|
+
payerWif: string | undefined;
|
|
18
|
+
/** Load-balanced FluxOS API, fine for reads and price quotes. */
|
|
19
|
+
apiUrl: string;
|
|
20
|
+
/** A specific FluxOS node to use for sessions and registrations, if pinned. */
|
|
21
|
+
nodeUrl: string | undefined;
|
|
22
|
+
explorerUrls: string[];
|
|
23
|
+
statsUrl: string;
|
|
24
|
+
ratesUrl: string;
|
|
25
|
+
/** Deployment address override, only for testing. */
|
|
26
|
+
requestTimeoutMs: number;
|
|
27
|
+
}
|
|
28
|
+
export declare function loadConfig(env?: NodeJS.ProcessEnv): Config;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime configuration, read from the environment the MCP host passes in.
|
|
3
|
+
*
|
|
4
|
+
* Two independent keys are used, exactly as the Flux network models them:
|
|
5
|
+
*
|
|
6
|
+
* FLUX_ID_PRIVATE_KEY WIF of the Flux ID (ZelID). It is the app owner:
|
|
7
|
+
* it signs registration messages and API sessions.
|
|
8
|
+
* It never holds funds.
|
|
9
|
+
* FLUX_PAYMENT_PRIVATE_KEY WIF of a Flux transparent address (t1...). It
|
|
10
|
+
* holds FLUX and pays deployment fees on-chain.
|
|
11
|
+
*
|
|
12
|
+
* Both are optional: without them every read-only tool still works, and the
|
|
13
|
+
* server can generate a fresh pair for the user.
|
|
14
|
+
*/
|
|
15
|
+
const DEFAULT_EXPLORERS = ['https://explorer.runonflux.io', 'https://flux-explorer.sspwallet.io'];
|
|
16
|
+
function optional(value) {
|
|
17
|
+
const trimmed = value?.trim();
|
|
18
|
+
return trimmed ? trimmed : undefined;
|
|
19
|
+
}
|
|
20
|
+
export function loadConfig(env = process.env) {
|
|
21
|
+
const explorers = optional(env.FLUX_EXPLORER_URLS)
|
|
22
|
+
?.split(',')
|
|
23
|
+
.map((url) => url.trim().replace(/\/$/, ''))
|
|
24
|
+
.filter(Boolean);
|
|
25
|
+
return {
|
|
26
|
+
ownerWif: optional(env.FLUX_ID_PRIVATE_KEY),
|
|
27
|
+
payerWif: optional(env.FLUX_PAYMENT_PRIVATE_KEY),
|
|
28
|
+
apiUrl: (optional(env.FLUX_API_URL) ?? 'https://api.runonflux.io').replace(/\/$/, ''),
|
|
29
|
+
nodeUrl: optional(env.FLUX_NODE_URL)?.replace(/\/$/, ''),
|
|
30
|
+
explorerUrls: explorers && explorers.length ? explorers : DEFAULT_EXPLORERS,
|
|
31
|
+
statsUrl: (optional(env.FLUX_STATS_URL) ?? 'https://stats.runonflux.io').replace(/\/$/, ''),
|
|
32
|
+
ratesUrl: (optional(env.FLUX_RATES_URL) ?? 'https://viprates.runonflux.io').replace(/\/$/, ''),
|
|
33
|
+
requestTimeoutMs: Number(optional(env.FLUX_REQUEST_TIMEOUT_MS) ?? 60000),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAgBH,MAAM,iBAAiB,GAAG,CAAC,+BAA+B,EAAE,oCAAoC,CAAC,CAAC;AAElG,SAAS,QAAQ,CAAC,KAAyB;IACzC,MAAM,OAAO,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC;IAC9B,OAAO,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC7D,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,kBAAkB,CAAC;QAChD,EAAE,KAAK,CAAC,GAAG,CAAC;SACX,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;SAC3C,MAAM,CAAC,OAAO,CAAC,CAAC;IACnB,OAAO;QACL,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,mBAAmB,CAAC;QAC3C,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,wBAAwB,CAAC;QAChD,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,0BAA0B,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QACrF,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QACxD,YAAY,EAAE,SAAS,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,iBAAiB;QAC3E,QAAQ,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,4BAA4B,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QAC3F,QAAQ,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,+BAA+B,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QAC9F,gBAAgB,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,uBAAuB,CAAC,IAAI,KAAK,CAAC;KACzE,CAAC;AACJ,CAAC"}
|
package/dist/deploy.d.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deploying an app is two halves that must agree:
|
|
3
|
+
*
|
|
4
|
+
* 1. A signed specification message, handed to one FluxOS node and gossiped
|
|
5
|
+
* to the network. The node answers with a 64-character hash and keeps the
|
|
6
|
+
* message in a temporary store for one hour.
|
|
7
|
+
* 2. An on-chain payment to the network deployment address carrying that
|
|
8
|
+
* hash in an OP_RETURN. When nodes index the transaction they pair it with
|
|
9
|
+
* the stored message, check the amount, and publish the app globally.
|
|
10
|
+
*
|
|
11
|
+
* If the payment never lands the message just expires and nothing is spent.
|
|
12
|
+
* The amount paid is the Flux Cloud USD quote converted to FLUX. Consensus
|
|
13
|
+
* would accept far less, but that minimum is checked here only as a guard:
|
|
14
|
+
* underpaying it burns the FLUX with no refund, since the message is dropped
|
|
15
|
+
* silently and the same hash cannot be paid twice.
|
|
16
|
+
*/
|
|
17
|
+
import type { Config } from './config.js';
|
|
18
|
+
import { Explorer } from './chain.js';
|
|
19
|
+
import { FluxClient } from './fluxapi.js';
|
|
20
|
+
import { type Identity } from './keys.js';
|
|
21
|
+
import { type DeploymentInformation, type Quote } from './pricing.js';
|
|
22
|
+
import { type AppComponent, type AppSpec, type PublishedAppSpec, type SpecInput } from './spec.js';
|
|
23
|
+
export interface Wallet {
|
|
24
|
+
owner: Identity;
|
|
25
|
+
payer: Identity;
|
|
26
|
+
}
|
|
27
|
+
export declare function walletFromConfig(config: Config): Wallet;
|
|
28
|
+
export declare function api(config: Config): FluxClient;
|
|
29
|
+
export declare function explorer(config: Config): Explorer;
|
|
30
|
+
export declare function publishedSpec(client: FluxClient, name: string): Promise<PublishedAppSpec | null>;
|
|
31
|
+
export interface Location {
|
|
32
|
+
ip: string;
|
|
33
|
+
name: string;
|
|
34
|
+
broadcastedAt?: string;
|
|
35
|
+
expireAt?: string;
|
|
36
|
+
hash?: string;
|
|
37
|
+
runningSince?: string;
|
|
38
|
+
osUptime?: number;
|
|
39
|
+
staticIp?: boolean;
|
|
40
|
+
}
|
|
41
|
+
export declare function appLocations(client: FluxClient, name: string): Promise<Location[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Pick a node that can take the registration: enough peers, ArcaneOS when
|
|
44
|
+
* the spec is enterprise, and honouring a pinned FLUX_NODE_URL.
|
|
45
|
+
*/
|
|
46
|
+
export declare function selectNode(config: Config, { arcane, log }?: {
|
|
47
|
+
arcane?: boolean;
|
|
48
|
+
log?: (m: string) => void;
|
|
49
|
+
}): Promise<FluxClient>;
|
|
50
|
+
export interface EnterpriseInput {
|
|
51
|
+
compose: AppComponent[];
|
|
52
|
+
contacts: string[];
|
|
53
|
+
}
|
|
54
|
+
export interface Plan {
|
|
55
|
+
action: 'register' | 'update';
|
|
56
|
+
spec: AppSpec;
|
|
57
|
+
/** The node's own formatting of the spec: this is what gets signed. */
|
|
58
|
+
formatted: AppSpec;
|
|
59
|
+
previous: PublishedAppSpec | null;
|
|
60
|
+
quote: Quote;
|
|
61
|
+
consensusMinimumFlux: number;
|
|
62
|
+
deployment: DeploymentInformation;
|
|
63
|
+
height: number;
|
|
64
|
+
payer: string;
|
|
65
|
+
balanceFlux: number;
|
|
66
|
+
requiredFlux: number;
|
|
67
|
+
funded: boolean;
|
|
68
|
+
warnings: string[];
|
|
69
|
+
node: FluxClient;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Everything needed to decide whether a deployment can proceed, without
|
|
73
|
+
* signing, spending or broadcasting anything.
|
|
74
|
+
*/
|
|
75
|
+
export declare function plan(config: Config, input: SpecInput & {
|
|
76
|
+
name: string;
|
|
77
|
+
}, options?: {
|
|
78
|
+
enterprise?: EnterpriseInput | undefined;
|
|
79
|
+
log?: (m: string) => void;
|
|
80
|
+
}): Promise<Plan>;
|
|
81
|
+
export interface DeployResult {
|
|
82
|
+
action: 'register' | 'update';
|
|
83
|
+
name: string;
|
|
84
|
+
messageHash: string;
|
|
85
|
+
messageExpiresAt: string;
|
|
86
|
+
txid: string;
|
|
87
|
+
paidFlux: number;
|
|
88
|
+
paidUsd: number;
|
|
89
|
+
feeFlux: number;
|
|
90
|
+
deploymentAddress: string;
|
|
91
|
+
}
|
|
92
|
+
/** Sign, broadcast the message, then pay for it. Spends FLUX. */
|
|
93
|
+
export declare function execute(config: Config, prepared: Plan, log?: (m: string) => void): Promise<DeployResult>;
|
|
94
|
+
export interface WaitResult {
|
|
95
|
+
accepted: PublishedAppSpec | null;
|
|
96
|
+
paymentConfirmations: number;
|
|
97
|
+
instances: Location[];
|
|
98
|
+
wantedInstances: number;
|
|
99
|
+
done: boolean;
|
|
100
|
+
timedOut: boolean;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Poll until the message is accepted and instances run, within `timeoutMs`.
|
|
104
|
+
* Returns partial progress on timeout so a caller can simply call again.
|
|
105
|
+
*/
|
|
106
|
+
export declare function waitForApp(config: Config, name: string, options?: {
|
|
107
|
+
txid?: string | undefined;
|
|
108
|
+
previousHash?: string | undefined;
|
|
109
|
+
timeoutMs?: number;
|
|
110
|
+
pollMs?: number;
|
|
111
|
+
}): Promise<WaitResult>;
|