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 CHANGED
@@ -1,141 +1,33 @@
1
1
  # mycomesh-consumer
2
2
 
3
- Native Node.js Consumer for the MycoMesh Settlement V8 Relay network. It runs
4
- on the user's machine with no Docker, Python runtime, Compose file, or public
5
- Gateway dependency.
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
- ## Install
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
- The default remains the public V8-compatible network for existing installs.
19
- The pinned V10 fixed-budget committee is a controlled testnet and must be
20
- selected explicitly:
21
-
22
- ```sh
23
- mycomesh-consumer --v10-controlled-test
24
- ```
25
-
26
- This option uses the manifest and private testnet CA shipped in the package,
27
- pins both test Relays, and requires the wallet to fund an active fixed budget
28
- channel. It is not a production network and does not enable token rewards or
29
- independent arbitration.
30
-
31
- For a checkout:
32
-
33
- ```sh
34
- npm install --global ./packages/mycomesh-cli
35
- ```
36
-
37
- The command starts the Consumer API on `127.0.0.1:8110`, prints and optionally
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
- import { isApiInvocation } from "../src/consumer.mjs";
4
-
5
- const argv = process.argv.slice(2);
6
- const { main } = isApiInvocation(argv)
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.1.52",
4
- "description": "Native Node.js MycoMesh Consumer and OpenAI-compatible request CLI.",
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/cli.mjs",
21
- "src/consumer.mjs",
22
- "src/consumer-runtime.mjs",
23
- "src/consumer-reserved.mjs",
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
+ }