mycomesh-consumer 0.1.52 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -135
- package/bin/mycomesh-consumer.mjs +5 -8
- package/networks/mycomesh-v11-sepolia.json +27 -0
- package/package.json +8 -21
- package/src/chain.mjs +52 -0
- package/src/cli.mjs +131 -643
- package/src/consumer.mjs +144 -473
- package/src/disputes.mjs +54 -0
- package/src/eip712.mjs +151 -0
- package/src/protocol.mjs +90 -0
- package/src/secure-envelope.mjs +324 -0
- package/src/sse.mjs +101 -0
- package/bin/mycomesh.mjs +0 -10
- package/networks/v10-controlled-test.json +0 -119
- package/networks/v10-dynamic-20260926.ca.crt +0 -25
- package/networks/v10-dynamic-20260926.json +0 -154
- package/src/consumer-discovery.mjs +0 -355
- package/src/consumer-history.mjs +0 -287
- package/src/consumer-request-journal.mjs +0 -189
- package/src/consumer-reserved.mjs +0 -112
- package/src/consumer-runtime.mjs +0 -4176
- package/src/consumer-sessions.mjs +0 -205
- package/src/release.mjs +0 -12
- /package/networks/{v10-controlled-test.ca.crt → mycomesh-testnet-ca.crt} +0 -0
package/README.md
CHANGED
|
@@ -1,141 +1,33 @@
|
|
|
1
1
|
# mycomesh-consumer
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
on the
|
|
5
|
-
|
|
3
|
+
The MycoMesh V11 Consumer. One deposit custodied by the settlement contract
|
|
4
|
+
pays any Provider on the network; every request is signed by a local payment
|
|
5
|
+
key and sealed end to end, so Relays forward ciphertext only.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Requirements: Node.js 20 or newer. The package contains only the small
|
|
10
|
-
cryptographic dependencies needed for V8 EIP-712 payment-key authorizations;
|
|
11
|
-
Codex is an independent client.
|
|
7
|
+
Requires Node.js 20 or newer. No Docker or Python.
|
|
12
8
|
|
|
13
9
|
```sh
|
|
14
10
|
npm install --global mycomesh-consumer
|
|
15
|
-
mycomesh-consumer
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
opens the local credentials page, and stays available for any
|
|
39
|
-
OpenAI-compatible client. It does not start or configure Codex.
|
|
40
|
-
|
|
41
|
-
The page starts with a wallet signature check. The selected wallet must match
|
|
42
|
-
the local payment key's active on-chain grant before the API key/export and
|
|
43
|
-
inference are enabled. It exposes only the local API URL, payment key/export,
|
|
44
|
-
prepaid balance, key operations, and local consumption history. It has no
|
|
45
|
-
conversation or request-session UI. The payment key is generated once and stored at
|
|
46
|
-
`~/.mycomesh/consumer/payment-key` with mode `0600`; set
|
|
47
|
-
`MYCOMESH_CONSUMER_DATA_DIR` to choose another directory.
|
|
48
|
-
|
|
49
|
-
## Connect a client
|
|
50
|
-
|
|
51
|
-
The default command is already service-only. Sign in on the local page first,
|
|
52
|
-
then load the export block into a shell and use Codex or another compatible
|
|
53
|
-
client:
|
|
54
|
-
|
|
55
|
-
```sh
|
|
56
|
-
mycomesh-consumer --no-browser
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
Open the printed setup URL, sign in, and use **Copy export** on the local page.
|
|
60
|
-
Paste the export into the client terminal, then run `codex`. An unauthenticated
|
|
61
|
-
request to `/credentials` is rejected even after the wallet is unlocked.
|
|
62
|
-
|
|
63
|
-
The optional convenience wrapper is explicit:
|
|
64
|
-
|
|
65
|
-
```sh
|
|
66
|
-
mycomesh-consumer --codex
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
Use `--no-browser` on a headless machine. Stop the process without deleting
|
|
70
|
-
the key or history with `mycomesh-consumer --stop`. A full reset is explicit:
|
|
71
|
-
|
|
72
|
-
```sh
|
|
73
|
-
MYCOMESH_CONFIRM_RESET=RESET mycomesh-consumer --reset-local
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
The local API is OpenAI-compatible:
|
|
77
|
-
|
|
78
|
-
```sh
|
|
79
|
-
export OPENAI_BASE_URL=http://127.0.0.1:8110/v1
|
|
80
|
-
export OPENAI_API_KEY='myco_sk_...'
|
|
81
|
-
mycomesh responses --input 'hello' --model gpt-5.5
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
The browser's export block is the canonical way to obtain both values after
|
|
85
|
-
wallet verification. `--codex` waits for that verification before launching
|
|
86
|
-
Codex.
|
|
87
|
-
|
|
88
|
-
## Temporary sharing
|
|
89
|
-
|
|
90
|
-
The local page can create one time-limited HTTPS API URL and an independent
|
|
91
|
-
`myco_share_...` key for testing an OpenAI-compatible client outside the
|
|
92
|
-
Consumer machine. Install `cloudflared`, choose a lifetime, and click **Start
|
|
93
|
-
sharing**. The public endpoint exposes only models, Responses, Chat
|
|
94
|
-
Completions, and Codex search routes; it does not expose the dashboard or the
|
|
95
|
-
payment key. Stopping the share, reaching its expiry, or restarting the
|
|
96
|
-
Consumer invalidates the temporary key.
|
|
97
|
-
|
|
98
|
-
## Relay scheduling and V8 payments
|
|
99
|
-
|
|
100
|
-
The Consumer checks each configured Relay's selected protocol health (V8 by
|
|
101
|
-
default, or V10 when explicitly selected) and automatically tries the next
|
|
102
|
-
Relay after a health, timeout, or retryable HTTP failure. A single
|
|
103
|
-
request ID is preserved across failover. Each attempt carries a fresh V8
|
|
104
|
-
EIP-712 payment authorization signed by the persisted key; the Relay resolves
|
|
105
|
-
the key address to its on-chain grant and settles the signed receipt. A wallet
|
|
106
|
-
signature unlocks each Consumer process once; inference has no per-request
|
|
107
|
-
wallet signature or Consumer-managed conversation state.
|
|
108
|
-
|
|
109
|
-
Configure multiple Relay origins and an optional outbound proxy:
|
|
110
|
-
|
|
111
|
-
```sh
|
|
112
|
-
export MYCOMESH_V8_RELAY_URLS='https://relay-a.example,https://relay-b.example'
|
|
113
|
-
mycomesh-consumer --proxy http://127.0.0.1:10792
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
The default Relay is `https://bridge.mycomesh.xyz`. The V8 deployment and RPC
|
|
117
|
-
defaults are embedded in the package; `MYCOMESH_CONSUMER_NETWORK_CONFIG` and
|
|
118
|
-
`MYCOMESH_CONSUMER_SETTLEMENT_RPC_URLS` can override them for another network.
|
|
119
|
-
For the controlled V10 path, use `--v10-controlled-test` or provide an
|
|
120
|
-
equivalent manifest with `--network-config` and `--controlled-test`.
|
|
121
|
-
|
|
122
|
-
## Stateless request CLI
|
|
123
|
-
|
|
124
|
-
The package also keeps the stateless API commands:
|
|
125
|
-
|
|
126
|
-
```sh
|
|
127
|
-
mycomesh health
|
|
128
|
-
mycomesh models
|
|
129
|
-
mycomesh responses --input 'Summarize this' --max-output-tokens 500
|
|
130
|
-
mycomesh chat --message 'Explain this function'
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
Use `--json @file`, `--json -`, or piped JSON for complete OpenAI-shaped
|
|
134
|
-
requests. `--stream` returns buffered-compatible SSE events from the Relay.
|
|
135
|
-
|
|
136
|
-
## Development
|
|
137
|
-
|
|
138
|
-
```sh
|
|
139
|
-
cd packages/mycomesh-cli
|
|
140
|
-
npm test
|
|
141
|
-
```
|
|
11
|
+
mycomesh-consumer init
|
|
12
|
+
mycomesh-consumer setup --owner-key-file owner.key --faucet 100000000 --deposit 100000000
|
|
13
|
+
mycomesh-consumer request "What is MycoMesh?"
|
|
14
|
+
mycomesh-consumer serve # http://127.0.0.1:8110/v1 (responses, chat/completions, models; streaming)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
- `init` creates the payment key in `~/.mycomesh/v11` (it never leaves the machine).
|
|
18
|
+
- `setup` uses the owner account (which needs Sepolia ETH for gas) to deposit
|
|
19
|
+
tUSDC and authorize the payment key up to `--max-per-request` units per request.
|
|
20
|
+
`--faucet UNITS` first mints testnet tUSDC to the owner.
|
|
21
|
+
- `balance --owner-key-file owner.key` shows the deposit.
|
|
22
|
+
- `request "..." [--model gpt-5.5] [--provider SIGNER]` prints the verified answer,
|
|
23
|
+
the fee and the settlement key.
|
|
24
|
+
- `serve` runs a local OpenAI-compatible endpoint for Codex and other clients;
|
|
25
|
+
set `MYCOMESH_CONSUMER_API_KEY` to require a bearer token.
|
|
26
|
+
- `dispute <settlement-key|last> --owner-key-file owner.key --statement "..."`
|
|
27
|
+
reveals a recorded request and response to a randomly drawn Provider-AI jury
|
|
28
|
+
within the 24-hour dispute window. The 1 tUSDC reporter bond is returned and a
|
|
29
|
+
bounty paid when fraud is confirmed.
|
|
30
|
+
|
|
31
|
+
Each response is decrypted locally and checked against the Provider-signed
|
|
32
|
+
receipt before it is shown. The bundled network manifest is
|
|
33
|
+
`networks/mycomesh-v11-sepolia.json`; use `--network FILE` for another.
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { main } from "../src/cli.mjs";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
? await import("../src/cli.mjs")
|
|
8
|
-
: await import("../src/consumer.mjs");
|
|
9
|
-
|
|
10
|
-
process.exitCode = await main(argv);
|
|
4
|
+
main().then((code) => { if (typeof code === "number") process.exitCode = code; }).catch((error) => {
|
|
5
|
+
process.stderr.write(`mycomesh-consumer: ${error.message}\n`);
|
|
6
|
+
process.exitCode = 1;
|
|
7
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "mycomesh.v11.network.v1",
|
|
3
|
+
"network_id": "mycomesh-v11-sepolia",
|
|
4
|
+
"chain_id": 11155111,
|
|
5
|
+
"settlement": "0xd46b7efb1e650e83cdeb4a36fdfef33499e54842",
|
|
6
|
+
"stablecoin": "0xbce27efad4191277167fe76819581a97959048cf",
|
|
7
|
+
"registry": "0x0ff3210e8e8abf5f5bfa73a39e60db7416b6c8c7",
|
|
8
|
+
"deployment_block": 11809185,
|
|
9
|
+
"rpc_urls": [
|
|
10
|
+
"https://ethereum-sepolia-rpc.publicnode.com",
|
|
11
|
+
"https://rpc.sepolia.ethpandaops.io",
|
|
12
|
+
"https://sepolia.gateway.tenderly.co"
|
|
13
|
+
],
|
|
14
|
+
"tls_ca_file": "mycomesh-testnet-ca.crt",
|
|
15
|
+
"relays": [
|
|
16
|
+
{
|
|
17
|
+
"url": "https://136.0.3.126:10443",
|
|
18
|
+
"signer": "0x58fb2f217f3065d67cdde9d5231aa9388cf72297",
|
|
19
|
+
"link": "136.0.3.126:10991"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"url": "https://166.88.96.60:10443",
|
|
23
|
+
"signer": "0x2211e0288ef46a7b4ea03a5022f4a96f146d8c9d",
|
|
24
|
+
"link": "166.88.96.60:10991"
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mycomesh-consumer",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "MycoMesh V11 Consumer: a custodied deposit, relay-blind encrypted requests to any Provider, and a local OpenAI-compatible endpoint.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/Charleslzp/mycomesh"
|
|
@@ -11,37 +11,24 @@
|
|
|
11
11
|
},
|
|
12
12
|
"type": "module",
|
|
13
13
|
"bin": {
|
|
14
|
-
"mycomesh": "bin/mycomesh.mjs",
|
|
15
14
|
"mycomesh-consumer": "bin/mycomesh-consumer.mjs"
|
|
16
15
|
},
|
|
17
16
|
"files": [
|
|
18
|
-
"bin/mycomesh.mjs",
|
|
19
17
|
"bin/mycomesh-consumer.mjs",
|
|
20
|
-
"src
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"src/consumer-request-journal.mjs",
|
|
25
|
-
"src/consumer-discovery.mjs",
|
|
26
|
-
"src/release.mjs",
|
|
27
|
-
"src/consumer-sessions.mjs",
|
|
28
|
-
"src/consumer-history.mjs",
|
|
29
|
-
"README.md",
|
|
30
|
-
"networks/v10-controlled-test.json",
|
|
31
|
-
"networks/v10-controlled-test.ca.crt",
|
|
32
|
-
"networks/v10-dynamic-20260926.json",
|
|
33
|
-
"networks/v10-dynamic-20260926.ca.crt"
|
|
18
|
+
"src/*.mjs",
|
|
19
|
+
"networks/mycomesh-v11-sepolia.json",
|
|
20
|
+
"networks/mycomesh-testnet-ca.crt",
|
|
21
|
+
"README.md"
|
|
34
22
|
],
|
|
35
23
|
"engines": {
|
|
36
24
|
"node": ">=20"
|
|
37
25
|
},
|
|
38
26
|
"dependencies": {
|
|
39
27
|
"@noble/curves": "1.9.1",
|
|
40
|
-
"@noble/hashes": "1.8.0"
|
|
41
|
-
"undici": "6.28.0"
|
|
28
|
+
"@noble/hashes": "1.8.0"
|
|
42
29
|
},
|
|
43
30
|
"scripts": {
|
|
44
|
-
"test": "node --test"
|
|
31
|
+
"test": "node --test test/*.test.mjs"
|
|
45
32
|
},
|
|
46
33
|
"license": "UNLICENSED"
|
|
47
34
|
}
|
package/src/chain.mjs
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Minimal JSON-RPC for Consumer setup and balance reads, with endpoint failover.
|
|
2
|
+
import { addressOf, encodeCall, signLegacyTransaction } from "./eip712.mjs";
|
|
3
|
+
|
|
4
|
+
export async function rpcCall(urls, method, params, { timeoutMs = 15000 } = {}) {
|
|
5
|
+
let last;
|
|
6
|
+
for (const url of [].concat(urls)) {
|
|
7
|
+
try {
|
|
8
|
+
const response = await fetch(url, {
|
|
9
|
+
method: "POST", headers: { "content-type": "application/json" },
|
|
10
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }), signal: AbortSignal.timeout(timeoutMs),
|
|
11
|
+
});
|
|
12
|
+
const body = await response.json();
|
|
13
|
+
if (body.error) throw Object.assign(new Error(`${method}: ${JSON.stringify(body.error).slice(0, 300)}`), { answered: true });
|
|
14
|
+
return body.result;
|
|
15
|
+
} catch (error) {
|
|
16
|
+
if (error.answered) throw error;
|
|
17
|
+
last = error;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
throw new Error(`${method}: no RPC endpoint answered (${last?.message || "unknown"})`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const quantity = (value) => BigInt(value);
|
|
24
|
+
|
|
25
|
+
export async function ethCallWord(urls, to, data) {
|
|
26
|
+
return BigInt(await rpcCall(urls, "eth_call", [{ to, data }, "latest"]));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function availableBalance(network, owner) {
|
|
30
|
+
return ethCallWord(network.rpc_urls, network.settlement, encodeCall("availableBalance(address)", [["address", owner]]));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function sendTransaction(urls, privateKey, { to, data, gasLimit }) {
|
|
34
|
+
const from = addressOf(privateKey);
|
|
35
|
+
const [chainId, nonce, gasPrice] = await Promise.all([
|
|
36
|
+
rpcCall(urls, "eth_chainId", []), rpcCall(urls, "eth_getTransactionCount", [from, "pending"]), rpcCall(urls, "eth_gasPrice", []),
|
|
37
|
+
]);
|
|
38
|
+
const limit = gasLimit ?? (quantity(await rpcCall(urls, "eth_estimateGas", [{ from, to, data }])) * 12n / 10n + 10000n);
|
|
39
|
+
const raw = signLegacyTransaction(privateKey, {
|
|
40
|
+
nonce: quantity(nonce), gasPrice: quantity(gasPrice) * 12n / 10n, gasLimit: limit, to, data, chainId: quantity(chainId),
|
|
41
|
+
});
|
|
42
|
+
const hash = await rpcCall(urls, "eth_sendRawTransaction", [raw]);
|
|
43
|
+
for (let i = 0; i < 180; i += 1) {
|
|
44
|
+
const receipt = await rpcCall(urls, "eth_getTransactionReceipt", [hash]);
|
|
45
|
+
if (receipt) {
|
|
46
|
+
if (receipt.status !== "0x1") throw new Error(`transaction ${hash} reverted`);
|
|
47
|
+
return receipt;
|
|
48
|
+
}
|
|
49
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
50
|
+
}
|
|
51
|
+
throw new Error(`transaction ${hash} was not mined in time`);
|
|
52
|
+
}
|