zk-credits 0.2.10 → 0.2.11
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 +67 -231
- package/dist/cli.js +46 -7
- package/dist/credential-store.js +1 -1
- package/dist/local-x402-agent.js +23 -2
- package/dist/rpc-config.d.ts +1 -1
- package/dist/rpc-config.js +1 -1
- package/dist/setup-config.js +2 -2
- package/dist/sidecar-diagnostics.d.ts +7 -0
- package/dist/sidecar-diagnostics.js +103 -0
- package/dist/sidecar-lifecycle.js +1 -1
- package/dist/sidecar.d.ts +2 -0
- package/dist/sidecar.js +21 -0
- package/dist/zk-credits.js +212 -56
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,248 +1,84 @@
|
|
|
1
|
-
> Release candidate `0.2.9` is prepared for owner publication. Registry commands
|
|
2
|
-
> below apply after publication; the existing `0.2.8` lacks this flow.
|
|
3
|
-
|
|
4
1
|
# zk-credits
|
|
5
2
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Built for the invite-only experimental Base Sepolia pilot. Product calls use
|
|
9
|
-
founder-provisioned test credits and have no product payment step; the circuit
|
|
10
|
-
is not independently audited. External research participants in slots A and C
|
|
11
|
-
receive $25 for a 30-minute session regardless of setup success. That payment
|
|
12
|
-
is not product revenue or willingness-to-pay evidence.
|
|
3
|
+
API credits for Codex, Cline, and x402 agents. The local sidecar proves each
|
|
4
|
+
spend and forwards your request to the gateway.
|
|
13
5
|
|
|
14
|
-
|
|
15
|
-
npm install --global zk-credits@0.2.9
|
|
16
|
-
```
|
|
6
|
+
Requires Node.js 20 or later. Runs on Base Sepolia.
|
|
17
7
|
|
|
18
|
-
##
|
|
8
|
+
## Quick start
|
|
19
9
|
|
|
20
|
-
|
|
21
|
-
|
|
10
|
+
1. [Activate your credits](https://hazecredits.vercel.app) and download your activated credential.
|
|
11
|
+
2. Install the package and configure a Base Sepolia RPC endpoint:
|
|
22
12
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
| `@zk-credits/x402-zk-prepaid` (adapter) | `0.1.0` |
|
|
27
|
-
| `@zk-credits/shared` | `0.1.0` |
|
|
28
|
-
|
|
29
|
-
The pinned Base Sepolia trial version is `0.2.9`. It includes passwordless
|
|
30
|
-
OS credential storage, the hash-pinned proving archive, public Base Sepolia RPC
|
|
31
|
-
by default, a narrow Codex Responses bridge, and the request-aware x402 adapter.
|
|
32
|
-
Setup validates the public chain witness and gateway-known root. No private
|
|
33
|
-
GitHub repository access or recovery password is required for the new flow.
|
|
34
|
-
|
|
35
|
-
Verify the shipped artifacts before your first prove. The package's
|
|
36
|
-
`circuits/manifest.json` fixes the SHA-256 of the frozen
|
|
37
|
-
`private_credit_spend.wasm`, `private_credit_spend.zkey`, and
|
|
38
|
-
`verification_key_private_credit.json`; compute the digest of each delivered
|
|
39
|
-
file and compare it to the manifest. A mismatch is a proof failure, and the
|
|
40
|
-
sidecar refuses to prove rather than sending a payment. The founder compares
|
|
41
|
-
the same digests against the delivered set before inviting you.
|
|
42
|
-
|
|
43
|
-
## What one credit buys
|
|
44
|
-
|
|
45
|
-
One credit buys one successfully committed response from the single service
|
|
46
|
-
class `coding-deepseek-v4-flash-v1` (`deepseek/deepseek-v4-flash`). It is not
|
|
47
|
-
an arbitrary API call, and the gateway enforces the class before it reserves
|
|
48
|
-
the credit:
|
|
49
|
-
|
|
50
|
-
- text messages, tool definitions, and tool calls, with one generated choice;
|
|
51
|
-
- no provider streaming, images, files, audio, web search, model fallback,
|
|
52
|
-
client-selected routing, or unknown cost-affecting fields;
|
|
53
|
-
- input capped at 128,000 UTF-8 byte units (counted conservatively, never below the
|
|
54
|
-
provider's token count), output at 4,000 tokens, request body at 256 KiB,
|
|
55
|
-
encrypted replay at 1 MiB, and upstream timeout at 120 seconds.
|
|
56
|
-
|
|
57
|
-
The requested `model` field is ignored: the gateway always dispatches the
|
|
58
|
-
class model, so an OpenAI-compatible client configured with any model name
|
|
59
|
-
still receives the class. A rejected request consumes no credit.
|
|
60
|
-
|
|
61
|
-
## First run
|
|
62
|
-
|
|
63
|
-
1. Sign in at https://hazecredits.vercel.app and redeem your invite. Save and
|
|
64
|
-
reimport the passwordless recovery capsule, fund your pilot credential, then
|
|
65
|
-
download the activated credential. Anyone possessing these files can spend
|
|
66
|
-
their credits; keep them private.
|
|
67
|
-
2. Import the activated file and validate setup:
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
export ZK_CREDITS_CREDENTIAL_PATH="$HOME/Downloads/zk-credits-credential.json"
|
|
71
|
-
zk-credits setup codex
|
|
13
|
+
```sh
|
|
14
|
+
npm install --global zk-credits
|
|
15
|
+
zk-credits config rpc
|
|
72
16
|
```
|
|
73
17
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
3. Review consent, choose your task, and run `zk-credits codex` or
|
|
81
|
-
`zk-credits x402-agent`. Subsequent launches use OS storage without a password
|
|
82
|
-
or import file. The activated download remains a private recovery backup.
|
|
83
|
-
|
|
84
|
-
The RPC defaults to the launch endpoint baked into the package. If that endpoint is limited,
|
|
85
|
-
run `zk-credits config rpc` and enter your own Base Sepolia endpoint in the hidden
|
|
86
|
-
local prompt. Saved settings stay owner-only; `BASE_RPC_URL` takes precedence.
|
|
87
|
-
Provider keys must not appear in chat or evidence. No gateway admin token is
|
|
88
|
-
needed for operator setup.
|
|
89
|
-
|
|
90
|
-
Existing encrypted exports require `zk-credits setup codex --legacy` (or
|
|
91
|
-
`setup x402 --legacy`) and their original password in a hidden local prompt.
|
|
92
|
-
A missing or altered proving artifact fails closed before payment creation.
|
|
93
|
-
Setup and proving stay local; no supplied witness file is accepted for the
|
|
94
|
-
manifest-pinned V2 deployment.
|
|
95
|
-
|
|
96
|
-
The sidecar binds `127.0.0.1:3210` only. It does not modify `~/.cline` or
|
|
97
|
-
`~/.codex`.
|
|
98
|
-
|
|
99
|
-
## Supported route
|
|
100
|
-
|
|
101
|
-
The pilot serves one bounded upstream spend path: non-streaming
|
|
102
|
-
`POST /v1/chat/completions`, either through this sidecar or through an
|
|
103
|
-
x402-native agent that explicitly registers the project `zk-prepaid` adapter.
|
|
104
|
-
The sidecar also accepts Codex `POST /v1/responses` requests with text input
|
|
105
|
-
and function tools, translates them to that bounded chat request, and converts
|
|
106
|
-
the committed result into Responses SSE events. It rejects unsupported fields
|
|
107
|
-
before starting a proof. Generic x402 clients, public facilitators, Bazaar,
|
|
108
|
-
MCP, and the standard `exact` rail are unsupported; the sidecar never falls
|
|
109
|
-
back to another rail.
|
|
110
|
-
|
|
111
|
-
| Route | Auth | Purpose |
|
|
112
|
-
| --- | --- | --- |
|
|
113
|
-
| `GET /health` | none | Liveness for the local launcher |
|
|
114
|
-
| `GET /v1/models` | local token | Codex model discovery |
|
|
115
|
-
| `GET /metrics` | local token | Aggregate proof metrics (below) |
|
|
116
|
-
| `POST /v1/chat/completions` | local token | The only proving and spend path |
|
|
117
|
-
| `POST /v1/responses` | local token | Codex bridge to the bounded chat spend path |
|
|
118
|
-
|
|
119
|
-
The chat route rejects streaming bodies (`stream`, `stream_options`) and a
|
|
120
|
-
missing `model` with a `4xx` before any proof. The Responses route requires
|
|
121
|
-
`stream: true`, text-only message/function-call input, and the known Codex
|
|
122
|
-
metadata fields; it rejects images, unsupported tools, and unknown controls
|
|
123
|
-
before any proof. Its upstream request is always `stream: false`. Anthropic
|
|
124
|
-
`/v1/messages` and unknown paths return `404 unsupported_openai_path`. The
|
|
125
|
-
sidecar never substitutes a model and never falls back to another rail.
|
|
126
|
-
|
|
127
|
-
### Gateway responses and retries
|
|
128
|
-
|
|
129
|
-
| Response | Meaning | What to do |
|
|
130
|
-
| --- | --- | --- |
|
|
131
|
-
| `402` | No usable authorization, or a stale or invalid one | Re-prove against the fresh challenge |
|
|
132
|
-
| `409 claim_already_committed` | Your exact request already succeeded | Read `encryptedReplay` from the response, or fetch it from `POST /x402/replay`; do not re-prove |
|
|
133
|
-
| `409 claim_commit_ambiguous` | The commit outcome is unknown | Retry the same request; never re-prove a different one |
|
|
134
|
-
| `503 pilot_paused` | The operator paused the pilot | Wait. This is retryable, consumes no credit, and is not a proof failure |
|
|
135
|
-
| `503 provider_spend_cap_exhausted` | The provider-spend cap paused the pilot | Wait for the operator to review. No credit was consumed |
|
|
136
|
-
| `503 claim_store_unavailable` | Transient gateway fault | Retry with backoff |
|
|
137
|
-
|
|
138
|
-
The gateway never asks you to re-prove for its own account. A `503` is
|
|
139
|
-
retryable and free; a proof failure is local and never reaches the gateway.
|
|
140
|
-
|
|
141
|
-
## Proof path
|
|
142
|
-
|
|
143
|
-
- Local artifacts only, hash-pinned against the shipped manifest.
|
|
144
|
-
- One prove at a time per sidecar process, in a terminable child-process
|
|
145
|
-
worker with a 10-second deadline. (snarkjs cannot run inside a Node
|
|
146
|
-
`worker_threads` worker: its `web-worker` polyfill re-enters itself there.)
|
|
147
|
-
- Every proof is self-verified locally with the pinned verification key
|
|
148
|
-
before `PAYMENT-SIGNATURE` can be emitted, and its six public signals must
|
|
149
|
-
exactly match `[root, timestamp, domain, requestSignal, nullifier, share]`.
|
|
150
|
-
- A failed attempt retries with the same slot, request signal, nonce,
|
|
151
|
-
response key, and gateway `issuedAt` while more than ten seconds remain in
|
|
152
|
-
the challenge window.
|
|
153
|
-
- A slot is provisional during proving and recorded in the durable local
|
|
154
|
-
ledger (`$ZK_CREDITS_HOME/base-slots.json`) only after self-verification.
|
|
155
|
-
Proof misses, timeouts, hash failures, and verification failures release
|
|
156
|
-
it.
|
|
157
|
-
|
|
158
|
-
## `GET /metrics`
|
|
159
|
-
|
|
160
|
-
Aggregate only, authenticated with the same local token:
|
|
161
|
-
|
|
162
|
-
```json
|
|
163
|
-
{
|
|
164
|
-
"attempts": 12,
|
|
165
|
-
"successes": 11,
|
|
166
|
-
"failures": 1,
|
|
167
|
-
"retries": 1,
|
|
168
|
-
"failuresByCategory": { "timeout": 1, "artifact_hash_mismatch": 0, "...": 0 },
|
|
169
|
-
"hotProve": { "samples": 11, "p50Ms": 1180.4, "p95Ms": 1902.7 },
|
|
170
|
-
"updatedAt": "2026-09-20T14:00:00.000Z"
|
|
171
|
-
}
|
|
172
|
-
```
|
|
18
|
+
3. Import your credential and connect Codex:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
ZK_CREDITS_CREDENTIAL_PATH=/path/to/activated-credential.json zk-credits setup codex
|
|
22
|
+
zk-credits codex
|
|
23
|
+
```
|
|
173
24
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
any identifying label are never recorded or exposed.
|
|
25
|
+
Setup stores your credential in OS secure storage. Keep your recovery file
|
|
26
|
+
private: anyone with it can spend your credits.
|
|
177
27
|
|
|
178
28
|
## Commands
|
|
179
29
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
zk-credits setup codex
|
|
183
|
-
zk-credits setup x402
|
|
184
|
-
zk-credits
|
|
185
|
-
zk-credits
|
|
186
|
-
zk-credits
|
|
187
|
-
zk-credits serve
|
|
188
|
-
zk-credits
|
|
189
|
-
zk-credits
|
|
190
|
-
zk-credits
|
|
191
|
-
|
|
30
|
+
| Command | Purpose |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| `zk-credits setup codex` | Import a credential and configure Codex |
|
|
33
|
+
| `zk-credits setup x402` | Prepare an x402 agent |
|
|
34
|
+
| `zk-credits codex [args...]` | Start Codex with the local sidecar |
|
|
35
|
+
| `zk-credits cline [args...]` | Start Cline with the local sidecar |
|
|
36
|
+
| `zk-credits x402-agent` | Run the included x402 agent |
|
|
37
|
+
| `zk-credits serve` | Run the sidecar in the foreground |
|
|
38
|
+
| `zk-credits doctor` | Inspect the service, PID, setup, RPC source, and log location |
|
|
39
|
+
| `zk-credits stop` | Stop the local sidecar |
|
|
40
|
+
| `zk-credits status` | Check the Codex configuration and sidecar |
|
|
41
|
+
| `zk-credits config rpc` | Save or change your Base Sepolia RPC endpoint |
|
|
42
|
+
| `zk-credits --help` | Show command and troubleshooting guidance |
|
|
43
|
+
|
|
44
|
+
`doctor` and `stop` are available in version 0.2.11. They work before setup.
|
|
45
|
+
The default port is `3210`; set `ZK_CREDITS_SIDECAR_PORT` to select another port.
|
|
46
|
+
Automatic startup logs are saved to `~/.zk-credits/sidecar.log`, or
|
|
47
|
+
`$ZK_CREDITS_HOME/sidecar.log` when set.
|
|
48
|
+
|
|
49
|
+
## Troubleshooting
|
|
50
|
+
|
|
51
|
+
**Sidecar stopped or setup missing:** Run `zk-credits doctor`. If setup is
|
|
52
|
+
missing, import your activated credential with the setup command above.
|
|
53
|
+
Otherwise run `zk-credits serve` and read its output.
|
|
54
|
+
|
|
55
|
+
**Port occupied or “Server is not running”:** `x402-agent` starts its own local
|
|
56
|
+
server. Run `zk-credits stop` before switching from `serve` to `x402-agent`.
|
|
57
|
+
For older versions, inspect the listener before stopping it:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
lsof -nP -iTCP:3210 -sTCP:LISTEN
|
|
61
|
+
ps -p <PID> -o pid,ppid,etime,command
|
|
62
|
+
kill -TERM <PID>
|
|
192
63
|
```
|
|
193
64
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
attempt one proof. A later valid request receives HTTP 409 with
|
|
198
|
-
`internal_trial_limit_reached`; ordinary `serve` behavior is unchanged without
|
|
199
|
-
the flag.
|
|
200
|
-
|
|
201
|
-
`trial-registered-adapter` is a maintainer-only internal trial command. It
|
|
202
|
-
requires the locally recovered credential and pinned proving bundle, prompts
|
|
203
|
-
for the credential password and gateway admin token without echo when they are
|
|
204
|
-
not already configured, and requires an explicit confirmation before sending
|
|
205
|
-
one billable request. It checks gateway readiness, authenticated admin status,
|
|
206
|
-
Base root agreement, Codex profile/sidecar status, and remaining local slot
|
|
207
|
-
capacity, then reports only the exchange phases and aggregate counter deltas.
|
|
208
|
-
Run it only after recovery is complete and with the same `ZK_CREDITS_HOME`
|
|
209
|
-
used by the sidecar; it registers `zk-prepaid` directly through the official
|
|
210
|
-
x402 client adapter and reuses the durable slot ledger.
|
|
211
|
-
|
|
212
|
-
Codex SDK:
|
|
213
|
-
|
|
214
|
-
```ts
|
|
215
|
-
import { buildCodexSdkOptions, buildCodexThreadOptions } from 'zk-credits/codex';
|
|
216
|
-
```
|
|
65
|
+
Check the PID and command to confirm it is your sidecar. For a foreground
|
|
66
|
+
process, press Ctrl+C. `stop` authenticates the service and lets active requests
|
|
67
|
+
finish before exit.
|
|
217
68
|
|
|
218
|
-
`zk-credits
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
The credential secret is loaded from OS secure storage only into local memory. The sidecar cache
|
|
230
|
-
contains public Base event leaves and never stores the secret, prompt, response,
|
|
231
|
-
proof, account, or wallet.
|
|
232
|
-
|
|
233
|
-
## Validation
|
|
234
|
-
|
|
235
|
-
```bash
|
|
236
|
-
npm test # unit + fixture suites
|
|
237
|
-
npm run build # tsc + bundled CLI
|
|
238
|
-
ZK_CREDITS_ARTIFACT_DIR="$PWD/circuits/artifacts" \
|
|
239
|
-
npx vitest run pinned-artifacts # opt-in real fullProve + self-verify
|
|
240
|
-
```
|
|
69
|
+
**Base witness synchronization failed:** Run `zk-credits config rpc` and enter
|
|
70
|
+
a Base Sepolia endpoint (chain ID `84532`). Stop and restart the sidecar, or
|
|
71
|
+
retry setup if it failed. `BASE_RPC_URL` overrides the saved endpoint.
|
|
72
|
+
A missing funded credential or rejected root can require activation checks;
|
|
73
|
+
changing the RPC may not resolve it.
|
|
74
|
+
|
|
75
|
+
## Clients and privacy
|
|
76
|
+
|
|
77
|
+
Codex and Cline use the local OpenAI-compatible transport. Your own x402 agent
|
|
78
|
+
must register the custom `zk-prepaid` adapter; generic x402 clients do not
|
|
79
|
+
support this scheme automatically. See the [x402 integration guide](https://github.com/mangekyou-labs/haze-api/blob/main/docs/onboarding/base-zk-credits-x402-agent.md).
|
|
241
80
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
crypto fixtures; the default suites stay green with no bundle installed.
|
|
81
|
+
Your credential stays on your device. Proofs are sent to authorize each spend. The gateway and model provider receive request content and
|
|
82
|
+
can observe traffic metadata. This does not hide your IP address.
|
|
245
83
|
|
|
246
|
-
Base Sepolia only. The circuit is
|
|
247
|
-
Development proving material is not suitable for mainnet; the repository's
|
|
248
|
-
release gates require an audited production circuit and ceremony.
|
|
84
|
+
Base Sepolia test credits only. The circuit is not independently audited.
|
package/dist/cli.js
CHANGED
|
@@ -15,6 +15,7 @@ import { createZkPrepaidLifecycleMetrics } from '@zk-credits/x402-zk-prepaid';
|
|
|
15
15
|
import { BaseSlotLedger } from './slot-ledger.js';
|
|
16
16
|
import { nativeCredentialStore, importCredentialPath, readStoredCredential } from './credential-store.js';
|
|
17
17
|
import { applyRpcConfig, checkBaseSepoliaRpc, writeRpcConfig, RPC_GUIDANCE, resolveBaseSepoliaRpc } from './rpc-config.js';
|
|
18
|
+
import { diagnoseSidecar, stopSidecar } from './sidecar-diagnostics.js';
|
|
18
19
|
import { runCliCommand } from './cli-runtime.js';
|
|
19
20
|
import { createNodeSidecarLifecycle } from './node-sidecar-lifecycle.js';
|
|
20
21
|
import { activateSidecarServer } from './server-startup.js';
|
|
@@ -145,6 +146,8 @@ function printHelp() {
|
|
|
145
146
|
zk-credits setup [codex|x402] [--legacy] [--model <model>]
|
|
146
147
|
zk-credits codex [codex arguments...]
|
|
147
148
|
zk-credits status
|
|
149
|
+
zk-credits doctor
|
|
150
|
+
zk-credits stop
|
|
148
151
|
zk-credits serve [--port <port>] [--internal-trial-one-proof]
|
|
149
152
|
zk-credits x402-agent
|
|
150
153
|
zk-credits founder-demo-agent
|
|
@@ -159,11 +162,23 @@ the request-aware x402 adapter and local proof engine. It requires explicit
|
|
|
159
162
|
confirmation before one billable internal exchange; the task and response are
|
|
160
163
|
displayed locally and never added to activation evidence.
|
|
161
164
|
"zk-credits x402-agent" is a small task-running starter for the participant's
|
|
162
|
-
own x402 agent loop. It
|
|
165
|
+
own x402 agent loop. It starts its own metrics server on the selected sidecar port;
|
|
166
|
+
stop an existing sidecar before launching it. It asks for tasks locally and sends each through the same
|
|
163
167
|
request-aware adapter and local proof engine; aggregate metrics stay on the
|
|
164
168
|
loopback-only authenticated endpoint.
|
|
165
169
|
"zk-credits serve --internal-trial-one-proof" limits this sidecar process to
|
|
166
170
|
one authenticated, valid spend request and one local proof attempt.
|
|
171
|
+
"zk-credits doctor" reports health, authenticated PID, setup and RPC configuration
|
|
172
|
+
presence, and log location without starting a server or revealing secrets.
|
|
173
|
+
"zk-credits stop" gracefully drains the authenticated sidecar. For older versions,
|
|
174
|
+
inspect the listener with lsof -nP -iTCP:3210 -sTCP:LISTEN, verify its PID with
|
|
175
|
+
ps -p <PID> -o pid,ppid,etime,command, then kill -TERM <PID>. Foreground: Ctrl+C.
|
|
176
|
+
Use ZK_CREDITS_SIDECAR_PORT for doctor, stop, status and env with a custom port.
|
|
177
|
+
If serve reports missing setup, run:
|
|
178
|
+
ZK_CREDITS_CREDENTIAL_PATH=/path/to/activated-credential.json zk-credits setup
|
|
179
|
+
If the sidecar is stopped after setup, run zk-credits serve.
|
|
180
|
+
For RPC witness synchronization failures, run zk-credits config rpc and then
|
|
181
|
+
zk-credits stop followed by zk-credits serve. BASE_RPC_URL overrides the saved RPC.
|
|
167
182
|
Base Sepolia uses the launch endpoint baked into the package automatically. RPC endpoints
|
|
168
183
|
are rate-limited; run "zk-credits config rpc" to use your own endpoint.
|
|
169
184
|
Provider guide: https://docs.base.org/base-chain/node-operators/node-providers
|
|
@@ -182,7 +197,7 @@ async function loadBaseClientRuntime() {
|
|
|
182
197
|
const witnessPath = process.env.ZK_CREDITS_WITNESS_PATH;
|
|
183
198
|
const artifactDirectory = process.env.ZK_CREDITS_ARTIFACT_DIR;
|
|
184
199
|
if (!artifactDirectory) {
|
|
185
|
-
throw new Error('
|
|
200
|
+
throw new Error('Proving bundle is not configured. Run zk-credits setup first; use ZK_CREDITS_CREDENTIAL_PATH=/path/to/activated-credential.json zk-credits setup for first import. Run zk-credits doctor to inspect setup.');
|
|
186
201
|
}
|
|
187
202
|
const baseRpcUrl = process.env.BASE_RPC_URL;
|
|
188
203
|
const baseContractAddress = process.env.BASE_PRIVATE_CREDIT_BOND_ADDRESS;
|
|
@@ -581,6 +596,12 @@ async function serve(args) {
|
|
|
581
596
|
prepaidClient: prepaid.client,
|
|
582
597
|
internalTrialOneProof,
|
|
583
598
|
metrics: () => ({ ...proofMetrics.snapshot(), exchange: exchangeMetrics.snapshot() }),
|
|
599
|
+
onShutdown: () => {
|
|
600
|
+
void shutdown().catch((error) => {
|
|
601
|
+
console.error(error instanceof Error ? error.message : 'Sidecar shutdown failed');
|
|
602
|
+
process.exitCode = 1;
|
|
603
|
+
});
|
|
604
|
+
},
|
|
584
605
|
});
|
|
585
606
|
let address;
|
|
586
607
|
try {
|
|
@@ -595,20 +616,33 @@ async function serve(args) {
|
|
|
595
616
|
}
|
|
596
617
|
console.log(`ZK Credits sidecar listening on ${address}/v1`);
|
|
597
618
|
console.log('Run eval "$(zk-credits env)" in the client shell.');
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
process.exit(0);
|
|
619
|
+
let shutdownPromise;
|
|
620
|
+
const shutdown = () => {
|
|
621
|
+
shutdownPromise ??= sidecar.close().then(() => { process.exit(0); });
|
|
622
|
+
return shutdownPromise;
|
|
601
623
|
};
|
|
602
624
|
process.once('SIGINT', () => { void shutdown(); });
|
|
603
625
|
process.once('SIGTERM', () => { void shutdown(); });
|
|
604
626
|
}
|
|
605
627
|
async function main() {
|
|
606
628
|
const args = process.argv.slice(2);
|
|
607
|
-
|
|
629
|
+
const localHelp = ['doctor', 'stop', 'status', 'serve', 'setup', 'config'].includes(args[0] ?? '')
|
|
630
|
+
&& args.slice(1).some((arg) => arg === '--help' || arg === '-h');
|
|
631
|
+
if (args[0] === '--help' || args[0] === '-h' || !args[0] || localHelp) {
|
|
608
632
|
printHelp();
|
|
609
633
|
return;
|
|
610
634
|
}
|
|
611
635
|
const sidecarHome = stateDirectory();
|
|
636
|
+
if (args[0] === 'doctor' || args[0] === 'stop') {
|
|
637
|
+
if (args.length !== 1)
|
|
638
|
+
throw new Error(`Usage: zk-credits ${args[0]} (use ZK_CREDITS_SIDECAR_PORT to select a port)`);
|
|
639
|
+
const options = { baseUrl: loopbackBaseUrl(), stateDirectory: sidecarHome, write: (line) => console.log(line) };
|
|
640
|
+
if (args[0] === 'doctor')
|
|
641
|
+
await diagnoseSidecar(options);
|
|
642
|
+
else
|
|
643
|
+
await stopSidecar(options);
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
612
646
|
if (args[0] === 'config' && args[1] === 'rpc' && args.length === 2) {
|
|
613
647
|
console.log(RPC_GUIDANCE);
|
|
614
648
|
const rpc = await checkBaseSepoliaRpc(await readHiddenValue('Base Sepolia RPC URL (hidden): '));
|
|
@@ -717,6 +751,11 @@ async function main() {
|
|
|
717
751
|
process.exitCode = exitCode;
|
|
718
752
|
}
|
|
719
753
|
void main().catch((error) => {
|
|
720
|
-
|
|
754
|
+
if (error?.code === 'EADDRINUSE') {
|
|
755
|
+
console.error('The local port is already occupied. Run zk-credits doctor to identify the listener. Stop the existing sidecar with zk-credits stop (older sidecars: inspect the PID before kill -TERM <PID>), then retry. x402-agent starts its own local metrics server and does not reuse serve.');
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
console.error(error instanceof Error ? error.message : 'zk-credits failed');
|
|
759
|
+
}
|
|
721
760
|
process.exitCode = 1;
|
|
722
761
|
});
|
package/dist/credential-store.js
CHANGED
|
@@ -46,7 +46,7 @@ export async function importStoredCredential(file, store, legacyPassword) {
|
|
|
46
46
|
export async function readStoredCredential(store) {
|
|
47
47
|
const raw = await store.read();
|
|
48
48
|
if (!raw)
|
|
49
|
-
throw new Error('No credential in OS storage. Download your activated credential and run zk-credits setup first.');
|
|
49
|
+
throw new Error('No credential in OS storage. Download your activated credential and run zk-credits setup first: ZK_CREDITS_CREDENTIAL_PATH=/path/to/activated-credential.json zk-credits setup.');
|
|
50
50
|
try {
|
|
51
51
|
const saved = JSON.parse(raw);
|
|
52
52
|
if (saved.version !== 1 || !saved.credential)
|
package/dist/local-x402-agent.js
CHANGED
|
@@ -165490,7 +165490,7 @@ function nativeCredentialStore(stateDirectory) {
|
|
|
165490
165490
|
async function readStoredCredential(store) {
|
|
165491
165491
|
const raw = await store.read();
|
|
165492
165492
|
if (!raw)
|
|
165493
|
-
throw new Error("No credential in OS storage. Download your activated credential and run zk-credits setup first.");
|
|
165493
|
+
throw new Error("No credential in OS storage. Download your activated credential and run zk-credits setup first: ZK_CREDITS_CREDENTIAL_PATH=/path/to/activated-credential.json zk-credits setup.");
|
|
165494
165494
|
try {
|
|
165495
165495
|
const saved = JSON.parse(raw);
|
|
165496
165496
|
if (saved.version !== 1 || !saved.credential)
|
|
@@ -165518,7 +165518,7 @@ function resolveBaseSepoliaRpc(environment, legacyRpc) {
|
|
|
165518
165518
|
throw new Error("Release RPC has not been baked. Set BASE_RPC_URL or build the release package.");
|
|
165519
165519
|
return rpc;
|
|
165520
165520
|
}
|
|
165521
|
-
var RPC_GUIDANCE = "The package uses the release Base Sepolia endpoint by default. To use your own endpoint (chain ID 84532), run zk-credits config rpc. Providers: https://docs.base.org/base-chain/node-operators/node-providers .";
|
|
165521
|
+
var RPC_GUIDANCE = "The package uses the release Base Sepolia endpoint by default. To use your own endpoint (chain ID 84532), run zk-credits config rpc. After changing the RPC, run zk-credits stop then zk-credits serve; retry setup if it has not completed. BASE_RPC_URL overrides the saved setting. Providers: https://docs.base.org/base-chain/node-operators/node-providers .";
|
|
165522
165522
|
function validateRpcUrl(value) {
|
|
165523
165523
|
try {
|
|
165524
165524
|
const url = new URL(value.trim());
|
|
@@ -167311,6 +167311,23 @@ function createSidecarServer(options) {
|
|
|
167311
167311
|
return;
|
|
167312
167312
|
}
|
|
167313
167313
|
const token = extractLocalToken(req);
|
|
167314
|
+
if (req.method === "GET" && pathname === "/runtime" || req.method === "POST" && pathname === "/shutdown") {
|
|
167315
|
+
if (token !== options.localToken) {
|
|
167316
|
+
sendJson(res, 401, { error: "invalid_local_token" });
|
|
167317
|
+
return;
|
|
167318
|
+
}
|
|
167319
|
+
if (pathname === "/runtime") {
|
|
167320
|
+
sendJson(res, 200, { service: "zk-credits-sidecar", pid: process.pid });
|
|
167321
|
+
} else if (options.onShutdown) {
|
|
167322
|
+
res.once("finish", () => {
|
|
167323
|
+
setImmediate(() => options.onShutdown());
|
|
167324
|
+
});
|
|
167325
|
+
sendJson(res, 202, { service: "zk-credits-sidecar", status: "stopping" });
|
|
167326
|
+
} else {
|
|
167327
|
+
sendJson(res, 404, { error: "shutdown_unavailable" });
|
|
167328
|
+
}
|
|
167329
|
+
return;
|
|
167330
|
+
}
|
|
167314
167331
|
if (req.method === "GET" && pathname === "/v1/models") {
|
|
167315
167332
|
if (token !== options.localToken) {
|
|
167316
167333
|
sendJson(res, 401, { error: "invalid_local_token" });
|
|
@@ -167409,6 +167426,10 @@ function createSidecarServer(options) {
|
|
|
167409
167426
|
return;
|
|
167410
167427
|
const activeServer = server;
|
|
167411
167428
|
server = null;
|
|
167429
|
+
if (!activeServer.listening) {
|
|
167430
|
+
await Promise.all(inFlight);
|
|
167431
|
+
return;
|
|
167432
|
+
}
|
|
167412
167433
|
await new Promise((resolve4, reject) => activeServer.close((error) => {
|
|
167413
167434
|
if (error)
|
|
167414
167435
|
reject(error);
|
package/dist/rpc-config.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const DEFAULT_BASE_SEPOLIA_RPC_URL = "__ZK_CREDITS_RELEASE_RPC_URL__";
|
|
2
2
|
export declare function resolveBaseSepoliaRpc(environment: NodeJS.ProcessEnv, legacyRpc?: string): string;
|
|
3
|
-
export declare const RPC_GUIDANCE = "The package uses the release Base Sepolia endpoint by default. To use your own endpoint (chain ID 84532), run zk-credits config rpc. Providers: https://docs.base.org/base-chain/node-operators/node-providers .";
|
|
3
|
+
export declare const RPC_GUIDANCE = "The package uses the release Base Sepolia endpoint by default. To use your own endpoint (chain ID 84532), run zk-credits config rpc. After changing the RPC, run zk-credits stop then zk-credits serve; retry setup if it has not completed. BASE_RPC_URL overrides the saved setting. Providers: https://docs.base.org/base-chain/node-operators/node-providers .";
|
|
4
4
|
export declare function validateRpcUrl(value: string): string;
|
|
5
5
|
export declare function checkBaseSepoliaRpc(value: string, request?: typeof fetch): Promise<string>;
|
|
6
6
|
export declare function readRpcConfig(directory: string): Promise<string | undefined>;
|
package/dist/rpc-config.js
CHANGED
|
@@ -8,7 +8,7 @@ export function resolveBaseSepoliaRpc(environment, legacyRpc) {
|
|
|
8
8
|
throw new Error('Release RPC has not been baked. Set BASE_RPC_URL or build the release package.');
|
|
9
9
|
return rpc;
|
|
10
10
|
}
|
|
11
|
-
export const RPC_GUIDANCE = 'The package uses the release Base Sepolia endpoint by default. To use your own endpoint (chain ID 84532), run zk-credits config rpc. Providers: https://docs.base.org/base-chain/node-operators/node-providers .';
|
|
11
|
+
export const RPC_GUIDANCE = 'The package uses the release Base Sepolia endpoint by default. To use your own endpoint (chain ID 84532), run zk-credits config rpc. After changing the RPC, run zk-credits stop then zk-credits serve; retry setup if it has not completed. BASE_RPC_URL overrides the saved setting. Providers: https://docs.base.org/base-chain/node-operators/node-providers .';
|
|
12
12
|
export function validateRpcUrl(value) {
|
|
13
13
|
try {
|
|
14
14
|
const url = new URL(value.trim());
|
package/dist/setup-config.js
CHANGED
|
@@ -201,7 +201,7 @@ async function resolveWitnessSource(options, saved) {
|
|
|
201
201
|
const explicitPath = environment.ZK_CREDITS_WITNESS_PATH?.trim();
|
|
202
202
|
if (explicitPath) {
|
|
203
203
|
if (options.manifest.deployment) {
|
|
204
|
-
throw new Error('Base Sepolia V2 setup requires a witness synchronized from the pinned public Base events');
|
|
204
|
+
throw new Error('Base Sepolia V2 setup requires a witness synchronized from the pinned public Base events. Unset ZK_CREDITS_WITNESS_PATH, run zk-credits config rpc, and retry zk-credits setup');
|
|
205
205
|
}
|
|
206
206
|
const source = {
|
|
207
207
|
kind: 'file',
|
|
@@ -239,7 +239,7 @@ async function resolveWitnessSource(options, saved) {
|
|
|
239
239
|
}
|
|
240
240
|
}
|
|
241
241
|
if (options.manifest.deployment) {
|
|
242
|
-
throw new Error('Base Sepolia V2 setup requires BASE_RPC_URL to sync the pinned V2 bond from its deployment block');
|
|
242
|
+
throw new Error('Base Sepolia V2 setup requires BASE_RPC_URL to sync the pinned V2 bond from its deployment block. Run zk-credits config rpc, then retry zk-credits setup');
|
|
243
243
|
}
|
|
244
244
|
if (saved?.witness.kind === 'file') {
|
|
245
245
|
try {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface DiagnosticOptions {
|
|
2
|
+
baseUrl: string;
|
|
3
|
+
stateDirectory: string;
|
|
4
|
+
write(line: string): void;
|
|
5
|
+
}
|
|
6
|
+
export declare function diagnoseSidecar(options: DiagnosticOptions): Promise<void>;
|
|
7
|
+
export declare function stopSidecar(options: DiagnosticOptions): Promise<void>;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { readLoopbackToken } from './sidecar-state.js';
|
|
3
|
+
import { sidecarStatePaths } from './sidecar-config.js';
|
|
4
|
+
import { readRpcConfig } from './rpc-config.js';
|
|
5
|
+
import { readSetupConfig } from './setup-config.js';
|
|
6
|
+
function origin(baseUrl) {
|
|
7
|
+
const url = new URL(baseUrl);
|
|
8
|
+
if (url.protocol !== 'http:' || url.hostname !== '127.0.0.1' || !url.port) {
|
|
9
|
+
throw new Error('Diagnostics require a 127.0.0.1 HTTP URL with an explicit port');
|
|
10
|
+
}
|
|
11
|
+
return url.origin;
|
|
12
|
+
}
|
|
13
|
+
async function health(baseUrl) {
|
|
14
|
+
try {
|
|
15
|
+
const response = await fetch(`${origin(baseUrl)}/health`, { signal: AbortSignal.timeout(1_000), redirect: 'error' });
|
|
16
|
+
const body = await response.json().catch(() => null);
|
|
17
|
+
return response.ok && body?.service === 'zk-credits-sidecar' && body.status === 'ok'
|
|
18
|
+
? 'running' : 'unrelated listener';
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
return error.cause?.code === 'ECONNREFUSED' ? 'stopped' : 'unreachable';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function operatorRequest(options, path, method = 'GET') {
|
|
25
|
+
const token = await readLoopbackToken(sidecarStatePaths(options.stateDirectory).tokenPath);
|
|
26
|
+
return fetch(`${origin(options.baseUrl)}${path}`, {
|
|
27
|
+
method, headers: { Authorization: `Bearer ${token}` },
|
|
28
|
+
signal: AbortSignal.timeout(2_000), redirect: 'error',
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
export async function diagnoseSidecar(options) {
|
|
32
|
+
const { write, stateDirectory } = options;
|
|
33
|
+
const baseUrl = origin(options.baseUrl);
|
|
34
|
+
write(`Loopback URL: ${baseUrl}`);
|
|
35
|
+
write(`State directory: ${stateDirectory}`);
|
|
36
|
+
write(`Startup log (auto-launched sidecar): ${sidecarStatePaths(stateDirectory).logPath}`);
|
|
37
|
+
const state = await health(baseUrl);
|
|
38
|
+
write(`Sidecar: ${state}`);
|
|
39
|
+
if (state === 'running') {
|
|
40
|
+
try {
|
|
41
|
+
const response = await operatorRequest(options, '/runtime');
|
|
42
|
+
const body = await response.json();
|
|
43
|
+
if (!response.ok || body.service !== 'zk-credits-sidecar' || !Number.isSafeInteger(body.pid) || Number(body.pid) <= 0)
|
|
44
|
+
throw new Error();
|
|
45
|
+
write(`Sidecar PID: ${body.pid} (authenticated)`);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
write('Sidecar PID: unavailable (older sidecar or missing/mismatched local token).');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
write(`Saved setup: ${await readSetupConfig(join(stateDirectory, 'config.json')) ? 'configured' : 'missing'}`);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
write('Saved setup: invalid or unreadable; rerun zk-credits setup.');
|
|
56
|
+
}
|
|
57
|
+
write('OS credential access: not checked; doctor does not request access.');
|
|
58
|
+
try {
|
|
59
|
+
const configured = await readRpcConfig(stateDirectory);
|
|
60
|
+
write(`RPC source: ${process.env.BASE_RPC_URL?.trim() ? 'BASE_RPC_URL environment override' : configured ? 'saved custom RPC' : 'saved setup or release default'} (URL redacted)`);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
write('RPC config: invalid or unreadable; run zk-credits config rpc.');
|
|
64
|
+
}
|
|
65
|
+
if (state === 'running')
|
|
66
|
+
write('Stop: zk-credits stop');
|
|
67
|
+
else if (state === 'stopped')
|
|
68
|
+
write('Start: zk-credits serve. If setup is missing, run zk-credits setup first.');
|
|
69
|
+
else
|
|
70
|
+
write('Inspect the listener before starting or stopping a process on this port.');
|
|
71
|
+
write('RPC sync failure: run zk-credits config rpc, then stop and restart the sidecar. BASE_RPC_URL overrides saved RPC.');
|
|
72
|
+
write(`Manual inspection: lsof -nP -iTCP:${new URL(baseUrl).port} -sTCP:LISTEN; inspect the PID with ps before kill -TERM <PID>.`);
|
|
73
|
+
}
|
|
74
|
+
export async function stopSidecar(options) {
|
|
75
|
+
const state = await health(origin(options.baseUrl));
|
|
76
|
+
if (state === 'stopped') {
|
|
77
|
+
options.write('Sidecar is already stopped.');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (state !== 'running')
|
|
81
|
+
throw new Error(`Sidecar is ${state}; refusing shutdown. Run zk-credits doctor to inspect the listener.`);
|
|
82
|
+
let response;
|
|
83
|
+
try {
|
|
84
|
+
response = await operatorRequest(options, '/shutdown', 'POST');
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
throw new Error('Could not authenticate or reach the running sidecar. Run zk-credits doctor; inspect its PID before stopping it manually.');
|
|
88
|
+
}
|
|
89
|
+
if (!response.ok) {
|
|
90
|
+
throw new Error('Sidecar refused shutdown (older sidecar or mismatched token). Run zk-credits doctor; use Ctrl+C for foreground serve, or inspect its PID before kill -TERM <PID>.');
|
|
91
|
+
}
|
|
92
|
+
const body = await response.json();
|
|
93
|
+
if (body.service !== 'zk-credits-sidecar' || body.status !== 'stopping')
|
|
94
|
+
throw new Error('Shutdown response was not recognized. Run zk-credits doctor.');
|
|
95
|
+
for (let attempt = 0; attempt < 50; attempt += 1) {
|
|
96
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
97
|
+
if (await health(options.baseUrl) === 'stopped') {
|
|
98
|
+
options.write('Sidecar listener stopped. Active requests may still be draining.');
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
throw new Error('Shutdown requested; the sidecar may still be draining active requests. Run zk-credits doctor to check.');
|
|
103
|
+
}
|
|
@@ -10,5 +10,5 @@ export async function ensureSidecarReady(dependencies, options = {}) {
|
|
|
10
10
|
if (await dependencies.isHealthy())
|
|
11
11
|
return dependencies.readToken();
|
|
12
12
|
}
|
|
13
|
-
throw new Error(`ZK Credits sidecar did not become ready; inspect ${dependencies.logPath}
|
|
13
|
+
throw new Error(`ZK Credits sidecar did not become ready; inspect ${dependencies.logPath}. Run zk-credits doctor, then zk-credits serve in the foreground to see the startup error. Missing setup: run zk-credits setup. RPC witness sync failure: run zk-credits config rpc and retry.`);
|
|
14
14
|
}
|
package/dist/sidecar.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export interface SidecarMetricsSnapshot extends BaseProofMetricsSnapshot {
|
|
|
13
13
|
}
|
|
14
14
|
export interface SidecarOptions {
|
|
15
15
|
localToken: string;
|
|
16
|
+
/** Called after an authenticated shutdown response is sent. */
|
|
17
|
+
onShutdown?: () => void;
|
|
16
18
|
gatewayBaseUrl: string;
|
|
17
19
|
prepaidClient: PrepaidTransport;
|
|
18
20
|
/** Limits this process to one authenticated, valid spend request for the internal trial. */
|
package/dist/sidecar.js
CHANGED
|
@@ -125,6 +125,23 @@ export function createSidecarServer(options) {
|
|
|
125
125
|
return;
|
|
126
126
|
}
|
|
127
127
|
const token = extractLocalToken(req);
|
|
128
|
+
if ((req.method === 'GET' && pathname === '/runtime') || (req.method === 'POST' && pathname === '/shutdown')) {
|
|
129
|
+
if (token !== options.localToken) {
|
|
130
|
+
sendJson(res, 401, { error: 'invalid_local_token' });
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (pathname === '/runtime') {
|
|
134
|
+
sendJson(res, 200, { service: 'zk-credits-sidecar', pid: process.pid });
|
|
135
|
+
}
|
|
136
|
+
else if (options.onShutdown) {
|
|
137
|
+
res.once('finish', () => { setImmediate(() => options.onShutdown()); });
|
|
138
|
+
sendJson(res, 202, { service: 'zk-credits-sidecar', status: 'stopping' });
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
sendJson(res, 404, { error: 'shutdown_unavailable' });
|
|
142
|
+
}
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
128
145
|
if (req.method === 'GET' && pathname === '/v1/models') {
|
|
129
146
|
if (token !== options.localToken) {
|
|
130
147
|
sendJson(res, 401, { error: 'invalid_local_token' });
|
|
@@ -226,6 +243,10 @@ export function createSidecarServer(options) {
|
|
|
226
243
|
return;
|
|
227
244
|
const activeServer = server;
|
|
228
245
|
server = null;
|
|
246
|
+
if (!activeServer.listening) {
|
|
247
|
+
await Promise.all(inFlight);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
229
250
|
await new Promise((resolve, reject) => activeServer.close((error) => {
|
|
230
251
|
if (error)
|
|
231
252
|
reject(error);
|
package/dist/zk-credits.js
CHANGED
|
@@ -17045,7 +17045,7 @@ var require_buffer_list = __commonJS({
|
|
|
17045
17045
|
}
|
|
17046
17046
|
}, {
|
|
17047
17047
|
key: "join",
|
|
17048
|
-
value: function
|
|
17048
|
+
value: function join11(s) {
|
|
17049
17049
|
if (this.length === 0) return "";
|
|
17050
17050
|
var p = this.head;
|
|
17051
17051
|
var ret = "" + p.data;
|
|
@@ -119077,7 +119077,7 @@ var require_lib34 = __commonJS({
|
|
|
119077
119077
|
};
|
|
119078
119078
|
}
|
|
119079
119079
|
// @__NO_SIDE_EFFECTS__
|
|
119080
|
-
function
|
|
119080
|
+
function join11(separator = "") {
|
|
119081
119081
|
astr("join", separator);
|
|
119082
119082
|
return {
|
|
119083
119083
|
encode: (from) => {
|
|
@@ -119293,15 +119293,15 @@ var require_lib34 = __commonJS({
|
|
|
119293
119293
|
convertRadix2,
|
|
119294
119294
|
radix,
|
|
119295
119295
|
radix2,
|
|
119296
|
-
join:
|
|
119296
|
+
join: join11,
|
|
119297
119297
|
padding
|
|
119298
119298
|
};
|
|
119299
|
-
exports.base16 = /* @__PURE__ */ chain(/* @__PURE__ */ radix2(4), /* @__PURE__ */ alphabet("0123456789ABCDEF"), /* @__PURE__ */
|
|
119300
|
-
exports.base32 = /* @__PURE__ */ chain(/* @__PURE__ */ radix2(5), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"), /* @__PURE__ */ padding(5), /* @__PURE__ */
|
|
119301
|
-
exports.base32nopad = /* @__PURE__ */ chain(/* @__PURE__ */ radix2(5), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"), /* @__PURE__ */
|
|
119302
|
-
exports.base32hex = /* @__PURE__ */ chain(/* @__PURE__ */ radix2(5), /* @__PURE__ */ alphabet("0123456789ABCDEFGHIJKLMNOPQRSTUV"), /* @__PURE__ */ padding(5), /* @__PURE__ */
|
|
119303
|
-
exports.base32hexnopad = /* @__PURE__ */ chain(/* @__PURE__ */ radix2(5), /* @__PURE__ */ alphabet("0123456789ABCDEFGHIJKLMNOPQRSTUV"), /* @__PURE__ */
|
|
119304
|
-
exports.base32crockford = /* @__PURE__ */ chain(/* @__PURE__ */ radix2(5), /* @__PURE__ */ alphabet("0123456789ABCDEFGHJKMNPQRSTVWXYZ"), /* @__PURE__ */
|
|
119299
|
+
exports.base16 = /* @__PURE__ */ chain(/* @__PURE__ */ radix2(4), /* @__PURE__ */ alphabet("0123456789ABCDEF"), /* @__PURE__ */ join11(""));
|
|
119300
|
+
exports.base32 = /* @__PURE__ */ chain(/* @__PURE__ */ radix2(5), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"), /* @__PURE__ */ padding(5), /* @__PURE__ */ join11(""));
|
|
119301
|
+
exports.base32nopad = /* @__PURE__ */ chain(/* @__PURE__ */ radix2(5), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"), /* @__PURE__ */ join11(""));
|
|
119302
|
+
exports.base32hex = /* @__PURE__ */ chain(/* @__PURE__ */ radix2(5), /* @__PURE__ */ alphabet("0123456789ABCDEFGHIJKLMNOPQRSTUV"), /* @__PURE__ */ padding(5), /* @__PURE__ */ join11(""));
|
|
119303
|
+
exports.base32hexnopad = /* @__PURE__ */ chain(/* @__PURE__ */ radix2(5), /* @__PURE__ */ alphabet("0123456789ABCDEFGHIJKLMNOPQRSTUV"), /* @__PURE__ */ join11(""));
|
|
119304
|
+
exports.base32crockford = /* @__PURE__ */ chain(/* @__PURE__ */ radix2(5), /* @__PURE__ */ alphabet("0123456789ABCDEFGHJKMNPQRSTVWXYZ"), /* @__PURE__ */ join11(""), /* @__PURE__ */ normalize((s) => s.toUpperCase().replace(/O/g, "0").replace(/[IL]/g, "1")));
|
|
119305
119305
|
var hasBase64Builtin = /* @__PURE__ */ (() => typeof Uint8Array.from([]).toBase64 === "function" && typeof Uint8Array.fromBase64 === "function")();
|
|
119306
119306
|
var decodeBase64Builtin = (s, isUrl) => {
|
|
119307
119307
|
astr("base64", s);
|
|
@@ -119319,8 +119319,8 @@ var require_lib34 = __commonJS({
|
|
|
119319
119319
|
decode(s) {
|
|
119320
119320
|
return decodeBase64Builtin(s, false);
|
|
119321
119321
|
}
|
|
119322
|
-
} : /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), /* @__PURE__ */ padding(6), /* @__PURE__ */
|
|
119323
|
-
exports.base64nopad = /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), /* @__PURE__ */
|
|
119322
|
+
} : /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), /* @__PURE__ */ padding(6), /* @__PURE__ */ join11(""));
|
|
119323
|
+
exports.base64nopad = /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), /* @__PURE__ */ join11(""));
|
|
119324
119324
|
exports.base64url = hasBase64Builtin ? {
|
|
119325
119325
|
encode(b) {
|
|
119326
119326
|
abytes(b);
|
|
@@ -119329,9 +119329,9 @@ var require_lib34 = __commonJS({
|
|
|
119329
119329
|
decode(s) {
|
|
119330
119330
|
return decodeBase64Builtin(s, true);
|
|
119331
119331
|
}
|
|
119332
|
-
} : /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"), /* @__PURE__ */ padding(6), /* @__PURE__ */
|
|
119333
|
-
exports.base64urlnopad = /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"), /* @__PURE__ */
|
|
119334
|
-
var genBase58 = /* @__NO_SIDE_EFFECTS__ */ (abc) => /* @__PURE__ */ chain(/* @__PURE__ */ radix(58), /* @__PURE__ */ alphabet(abc), /* @__PURE__ */
|
|
119332
|
+
} : /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"), /* @__PURE__ */ padding(6), /* @__PURE__ */ join11(""));
|
|
119333
|
+
exports.base64urlnopad = /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"), /* @__PURE__ */ join11(""));
|
|
119334
|
+
var genBase58 = /* @__NO_SIDE_EFFECTS__ */ (abc) => /* @__PURE__ */ chain(/* @__PURE__ */ radix(58), /* @__PURE__ */ alphabet(abc), /* @__PURE__ */ join11(""));
|
|
119335
119335
|
exports.base58 = /* @__PURE__ */ genBase58("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");
|
|
119336
119336
|
exports.base58flickr = /* @__PURE__ */ genBase58("123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ");
|
|
119337
119337
|
exports.base58xrp = /* @__PURE__ */ genBase58("rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz");
|
|
@@ -119363,7 +119363,7 @@ var require_lib34 = __commonJS({
|
|
|
119363
119363
|
var createBase58check = (sha256) => /* @__PURE__ */ chain(checksum(4, (data) => sha256(sha256(data))), exports.base58);
|
|
119364
119364
|
exports.createBase58check = createBase58check;
|
|
119365
119365
|
exports.base58check = exports.createBase58check;
|
|
119366
|
-
var BECH_ALPHABET = /* @__PURE__ */ chain(/* @__PURE__ */ alphabet("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), /* @__PURE__ */
|
|
119366
|
+
var BECH_ALPHABET = /* @__PURE__ */ chain(/* @__PURE__ */ alphabet("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), /* @__PURE__ */ join11(""));
|
|
119367
119367
|
var POLYMOD_GENERATORS = [996825010, 642813549, 513874426, 1027748829, 705979059];
|
|
119368
119368
|
function bech32Polymod(pre) {
|
|
119369
119369
|
const b = pre >> 25;
|
|
@@ -119472,7 +119472,7 @@ var require_lib34 = __commonJS({
|
|
|
119472
119472
|
return Uint8Array.fromHex(s);
|
|
119473
119473
|
}
|
|
119474
119474
|
};
|
|
119475
|
-
exports.hex = hasHexBuiltin ? hexBuiltin : /* @__PURE__ */ chain(/* @__PURE__ */ radix2(4), /* @__PURE__ */ alphabet("0123456789abcdef"), /* @__PURE__ */
|
|
119475
|
+
exports.hex = hasHexBuiltin ? hexBuiltin : /* @__PURE__ */ chain(/* @__PURE__ */ radix2(4), /* @__PURE__ */ alphabet("0123456789abcdef"), /* @__PURE__ */ join11(""), /* @__PURE__ */ normalize((s) => {
|
|
119476
119476
|
if (typeof s !== "string" || s.length % 2 !== 0)
|
|
119477
119477
|
throw new TypeError(`hex.decode: expected string, got ${typeof s} with length ${s.length}`);
|
|
119478
119478
|
return s.toLowerCase();
|
|
@@ -121711,7 +121711,7 @@ var require_Registration = __commonJS({
|
|
|
121711
121711
|
};
|
|
121712
121712
|
}
|
|
121713
121713
|
function verify(options) {
|
|
121714
|
-
const { attestation = "none", credential, origin, rpId, userVerification = "required" } = options;
|
|
121714
|
+
const { attestation = "none", credential, origin: origin2, rpId, userVerification = "required" } = options;
|
|
121715
121715
|
const clientDataJSONBytes = new Uint8Array(credential.clientDataJSON);
|
|
121716
121716
|
const clientDataJSON = Bytes.toString(clientDataJSONBytes);
|
|
121717
121717
|
const clientData = JSON.parse(clientDataJSON);
|
|
@@ -121726,9 +121726,9 @@ var require_Registration = __commonJS({
|
|
|
121726
121726
|
})();
|
|
121727
121727
|
if (!challengeResult)
|
|
121728
121728
|
throw new VerifyError("Challenge mismatch");
|
|
121729
|
-
const origins = Array.isArray(
|
|
121729
|
+
const origins = Array.isArray(origin2) ? origin2 : [origin2];
|
|
121730
121730
|
if (!origins.includes(clientData.origin))
|
|
121731
|
-
throw new VerifyError(`Origin mismatch: expected ${JSON.stringify(
|
|
121731
|
+
throw new VerifyError(`Origin mismatch: expected ${JSON.stringify(origin2)}, got "${clientData.origin}"`);
|
|
121732
121732
|
const attestationObjectBytes = new Uint8Array(credential.attestationObject);
|
|
121733
121733
|
const attestation_ = Cbor.decode(attestationObjectBytes);
|
|
121734
121734
|
const authData = attestation_.authData;
|
|
@@ -121948,11 +121948,11 @@ var require_Authenticator = __commonJS({
|
|
|
121948
121948
|
return (bytes[33] << 24 | bytes[34] << 16 | bytes[35] << 8 | bytes[36]) >>> 0;
|
|
121949
121949
|
}
|
|
121950
121950
|
function getClientDataJSON(options) {
|
|
121951
|
-
const { challenge, crossOrigin = false, extraClientData, origin = window.location.origin, type = "webauthn.get" } = options;
|
|
121951
|
+
const { challenge, crossOrigin = false, extraClientData, origin: origin2 = window.location.origin, type = "webauthn.get" } = options;
|
|
121952
121952
|
return JSON.stringify({
|
|
121953
121953
|
type,
|
|
121954
121954
|
challenge: Base64.fromHex(challenge, { url: true, pad: false }),
|
|
121955
|
-
origin,
|
|
121955
|
+
origin: origin2,
|
|
121956
121956
|
crossOrigin,
|
|
121957
121957
|
...extraClientData
|
|
121958
121958
|
});
|
|
@@ -122056,7 +122056,7 @@ var require_Authentication = __commonJS({
|
|
|
122056
122056
|
};
|
|
122057
122057
|
}
|
|
122058
122058
|
function getSignPayload(options) {
|
|
122059
|
-
const { challenge, crossOrigin, extraClientData, flag, origin, rpId, signCount, userVerification = "required" } = options;
|
|
122059
|
+
const { challenge, crossOrigin, extraClientData, flag, origin: origin2, rpId, signCount, userVerification = "required" } = options;
|
|
122060
122060
|
const authenticatorData = (0, Authenticator_js_1.getAuthenticatorData)({
|
|
122061
122061
|
flag,
|
|
122062
122062
|
rpId,
|
|
@@ -122066,7 +122066,7 @@ var require_Authentication = __commonJS({
|
|
|
122066
122066
|
challenge,
|
|
122067
122067
|
crossOrigin,
|
|
122068
122068
|
extraClientData,
|
|
122069
|
-
origin
|
|
122069
|
+
origin: origin2
|
|
122070
122070
|
});
|
|
122071
122071
|
const clientDataJSONHash = Hash.sha256(Hex.fromString(clientDataJSON));
|
|
122072
122072
|
const challengeIndex = clientDataJSON.indexOf('"challenge"');
|
|
@@ -122180,7 +122180,7 @@ var require_Authentication = __commonJS({
|
|
|
122180
122180
|
};
|
|
122181
122181
|
exports.SignFailedError = SignFailedError;
|
|
122182
122182
|
function verify(options) {
|
|
122183
|
-
const { challenge, metadata, origin, publicKey, rpId, signature } = options;
|
|
122183
|
+
const { challenge, metadata, origin: origin2, publicKey, rpId, signature } = options;
|
|
122184
122184
|
const { authenticatorData, clientDataJSON, userVerificationRequired } = metadata;
|
|
122185
122185
|
const authenticatorDataBytes = Bytes.fromHex(authenticatorData);
|
|
122186
122186
|
if (authenticatorDataBytes.length < 37)
|
|
@@ -122203,8 +122203,8 @@ var require_Authentication = __commonJS({
|
|
|
122203
122203
|
return false;
|
|
122204
122204
|
if (!clientData.challenge || Hex.fromBytes(Base64.toBytes(clientData.challenge)) !== challenge)
|
|
122205
122205
|
return false;
|
|
122206
|
-
if (
|
|
122207
|
-
const origins = Array.isArray(
|
|
122206
|
+
if (origin2 !== void 0) {
|
|
122207
|
+
const origins = Array.isArray(origin2) ? origin2 : [origin2];
|
|
122208
122208
|
if (!origins.includes(clientData.origin))
|
|
122209
122209
|
return false;
|
|
122210
122210
|
}
|
|
@@ -143664,7 +143664,7 @@ var require_Account = __commonJS({
|
|
|
143664
143664
|
return fromRoot(parameters);
|
|
143665
143665
|
}
|
|
143666
143666
|
function fromHeadlessWebAuthn(privateKey, options) {
|
|
143667
|
-
const { access, keyAuthorizationManager, rpId, origin, internal_version } = options;
|
|
143667
|
+
const { access, keyAuthorizationManager, rpId, origin: origin2, internal_version } = options;
|
|
143668
143668
|
const publicKey = P256.getPublicKey({ privateKey });
|
|
143669
143669
|
return from({
|
|
143670
143670
|
...access ? { access, keyAuthorizationManager } : {},
|
|
@@ -143676,7 +143676,7 @@ var require_Account = __commonJS({
|
|
|
143676
143676
|
...options,
|
|
143677
143677
|
challenge: hash,
|
|
143678
143678
|
rpId,
|
|
143679
|
-
origin
|
|
143679
|
+
origin: origin2
|
|
143680
143680
|
});
|
|
143681
143681
|
const signature = P256.sign({
|
|
143682
143682
|
payload,
|
|
@@ -165081,7 +165081,7 @@ var require_cjs2 = __commonJS({
|
|
|
165081
165081
|
// dist/cli.js
|
|
165082
165082
|
import { homedir as homedir2 } from "node:os";
|
|
165083
165083
|
import { readFile as readFile10 } from "node:fs/promises";
|
|
165084
|
-
import { join as
|
|
165084
|
+
import { join as join10 } from "node:path";
|
|
165085
165085
|
import { createInterface } from "node:readline/promises";
|
|
165086
165086
|
|
|
165087
165087
|
// dist/codex-launcher.js
|
|
@@ -166122,7 +166122,7 @@ async function importStoredCredential(file, store, legacyPassword) {
|
|
|
166122
166122
|
async function readStoredCredential(store) {
|
|
166123
166123
|
const raw = await store.read();
|
|
166124
166124
|
if (!raw)
|
|
166125
|
-
throw new Error("No credential in OS storage. Download your activated credential and run zk-credits setup first.");
|
|
166125
|
+
throw new Error("No credential in OS storage. Download your activated credential and run zk-credits setup first: ZK_CREDITS_CREDENTIAL_PATH=/path/to/activated-credential.json zk-credits setup.");
|
|
166126
166126
|
try {
|
|
166127
166127
|
const saved = JSON.parse(raw);
|
|
166128
166128
|
if (saved.version !== 1 || !saved.credential)
|
|
@@ -166160,7 +166160,7 @@ function resolveBaseSepoliaRpc(environment, legacyRpc) {
|
|
|
166160
166160
|
throw new Error("Release RPC has not been baked. Set BASE_RPC_URL or build the release package.");
|
|
166161
166161
|
return rpc;
|
|
166162
166162
|
}
|
|
166163
|
-
var RPC_GUIDANCE = "The package uses the release Base Sepolia endpoint by default. To use your own endpoint (chain ID 84532), run zk-credits config rpc. Providers: https://docs.base.org/base-chain/node-operators/node-providers .";
|
|
166163
|
+
var RPC_GUIDANCE = "The package uses the release Base Sepolia endpoint by default. To use your own endpoint (chain ID 84532), run zk-credits config rpc. After changing the RPC, run zk-credits stop then zk-credits serve; retry setup if it has not completed. BASE_RPC_URL overrides the saved setting. Providers: https://docs.base.org/base-chain/node-operators/node-providers .";
|
|
166164
166164
|
function validateRpcUrl(value) {
|
|
166165
166165
|
try {
|
|
166166
166166
|
const url = new URL(value.trim());
|
|
@@ -167440,7 +167440,7 @@ async function resolveWitnessSource(options, saved) {
|
|
|
167440
167440
|
const explicitPath = environment.ZK_CREDITS_WITNESS_PATH?.trim();
|
|
167441
167441
|
if (explicitPath) {
|
|
167442
167442
|
if (options.manifest.deployment) {
|
|
167443
|
-
throw new Error("Base Sepolia V2 setup requires a witness synchronized from the pinned public Base events");
|
|
167443
|
+
throw new Error("Base Sepolia V2 setup requires a witness synchronized from the pinned public Base events. Unset ZK_CREDITS_WITNESS_PATH, run zk-credits config rpc, and retry zk-credits setup");
|
|
167444
167444
|
}
|
|
167445
167445
|
const source = {
|
|
167446
167446
|
kind: "file",
|
|
@@ -167475,7 +167475,7 @@ async function resolveWitnessSource(options, saved) {
|
|
|
167475
167475
|
}
|
|
167476
167476
|
}
|
|
167477
167477
|
if (options.manifest.deployment) {
|
|
167478
|
-
throw new Error("Base Sepolia V2 setup requires BASE_RPC_URL to sync the pinned V2 bond from its deployment block");
|
|
167478
|
+
throw new Error("Base Sepolia V2 setup requires BASE_RPC_URL to sync the pinned V2 bond from its deployment block. Run zk-credits config rpc, then retry zk-credits setup");
|
|
167479
167479
|
}
|
|
167480
167480
|
if (saved?.witness.kind === "file") {
|
|
167481
167481
|
try {
|
|
@@ -168026,6 +168026,23 @@ function createSidecarServer(options) {
|
|
|
168026
168026
|
return;
|
|
168027
168027
|
}
|
|
168028
168028
|
const token = extractLocalToken(req);
|
|
168029
|
+
if (req.method === "GET" && pathname === "/runtime" || req.method === "POST" && pathname === "/shutdown") {
|
|
168030
|
+
if (token !== options.localToken) {
|
|
168031
|
+
sendJson(res, 401, { error: "invalid_local_token" });
|
|
168032
|
+
return;
|
|
168033
|
+
}
|
|
168034
|
+
if (pathname === "/runtime") {
|
|
168035
|
+
sendJson(res, 200, { service: "zk-credits-sidecar", pid: process.pid });
|
|
168036
|
+
} else if (options.onShutdown) {
|
|
168037
|
+
res.once("finish", () => {
|
|
168038
|
+
setImmediate(() => options.onShutdown());
|
|
168039
|
+
});
|
|
168040
|
+
sendJson(res, 202, { service: "zk-credits-sidecar", status: "stopping" });
|
|
168041
|
+
} else {
|
|
168042
|
+
sendJson(res, 404, { error: "shutdown_unavailable" });
|
|
168043
|
+
}
|
|
168044
|
+
return;
|
|
168045
|
+
}
|
|
168029
168046
|
if (req.method === "GET" && pathname === "/v1/models") {
|
|
168030
168047
|
if (token !== options.localToken) {
|
|
168031
168048
|
sendJson(res, 401, { error: "invalid_local_token" });
|
|
@@ -168124,6 +168141,10 @@ function createSidecarServer(options) {
|
|
|
168124
168141
|
return;
|
|
168125
168142
|
const activeServer = server;
|
|
168126
168143
|
server = null;
|
|
168144
|
+
if (!activeServer.listening) {
|
|
168145
|
+
await Promise.all(inFlight);
|
|
168146
|
+
return;
|
|
168147
|
+
}
|
|
168127
168148
|
await new Promise((resolve5, reject) => activeServer.close((error) => {
|
|
168128
168149
|
if (error)
|
|
168129
168150
|
reject(error);
|
|
@@ -168267,6 +168288,103 @@ async function createLocalX402Agent(options = {}) {
|
|
|
168267
168288
|
// dist/cli.js
|
|
168268
168289
|
import { createZkPrepaidLifecycleMetrics as createZkPrepaidLifecycleMetrics2 } from "@zk-credits/x402-zk-prepaid";
|
|
168269
168290
|
|
|
168291
|
+
// dist/sidecar-diagnostics.js
|
|
168292
|
+
import { join as join9 } from "node:path";
|
|
168293
|
+
function origin(baseUrl) {
|
|
168294
|
+
const url = new URL(baseUrl);
|
|
168295
|
+
if (url.protocol !== "http:" || url.hostname !== "127.0.0.1" || !url.port) {
|
|
168296
|
+
throw new Error("Diagnostics require a 127.0.0.1 HTTP URL with an explicit port");
|
|
168297
|
+
}
|
|
168298
|
+
return url.origin;
|
|
168299
|
+
}
|
|
168300
|
+
async function health(baseUrl) {
|
|
168301
|
+
try {
|
|
168302
|
+
const response = await fetch(`${origin(baseUrl)}/health`, { signal: AbortSignal.timeout(1e3), redirect: "error" });
|
|
168303
|
+
const body = await response.json().catch(() => null);
|
|
168304
|
+
return response.ok && body?.service === "zk-credits-sidecar" && body.status === "ok" ? "running" : "unrelated listener";
|
|
168305
|
+
} catch (error) {
|
|
168306
|
+
return error.cause?.code === "ECONNREFUSED" ? "stopped" : "unreachable";
|
|
168307
|
+
}
|
|
168308
|
+
}
|
|
168309
|
+
async function operatorRequest(options, path, method = "GET") {
|
|
168310
|
+
const token = await readLoopbackToken(sidecarStatePaths(options.stateDirectory).tokenPath);
|
|
168311
|
+
return fetch(`${origin(options.baseUrl)}${path}`, {
|
|
168312
|
+
method,
|
|
168313
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
168314
|
+
signal: AbortSignal.timeout(2e3),
|
|
168315
|
+
redirect: "error"
|
|
168316
|
+
});
|
|
168317
|
+
}
|
|
168318
|
+
async function diagnoseSidecar(options) {
|
|
168319
|
+
const { write, stateDirectory: stateDirectory2 } = options;
|
|
168320
|
+
const baseUrl = origin(options.baseUrl);
|
|
168321
|
+
write(`Loopback URL: ${baseUrl}`);
|
|
168322
|
+
write(`State directory: ${stateDirectory2}`);
|
|
168323
|
+
write(`Startup log (auto-launched sidecar): ${sidecarStatePaths(stateDirectory2).logPath}`);
|
|
168324
|
+
const state = await health(baseUrl);
|
|
168325
|
+
write(`Sidecar: ${state}`);
|
|
168326
|
+
if (state === "running") {
|
|
168327
|
+
try {
|
|
168328
|
+
const response = await operatorRequest(options, "/runtime");
|
|
168329
|
+
const body = await response.json();
|
|
168330
|
+
if (!response.ok || body.service !== "zk-credits-sidecar" || !Number.isSafeInteger(body.pid) || Number(body.pid) <= 0)
|
|
168331
|
+
throw new Error();
|
|
168332
|
+
write(`Sidecar PID: ${body.pid} (authenticated)`);
|
|
168333
|
+
} catch {
|
|
168334
|
+
write("Sidecar PID: unavailable (older sidecar or missing/mismatched local token).");
|
|
168335
|
+
}
|
|
168336
|
+
}
|
|
168337
|
+
try {
|
|
168338
|
+
write(`Saved setup: ${await readSetupConfig(join9(stateDirectory2, "config.json")) ? "configured" : "missing"}`);
|
|
168339
|
+
} catch {
|
|
168340
|
+
write("Saved setup: invalid or unreadable; rerun zk-credits setup.");
|
|
168341
|
+
}
|
|
168342
|
+
write("OS credential access: not checked; doctor does not request access.");
|
|
168343
|
+
try {
|
|
168344
|
+
const configured = await readRpcConfig(stateDirectory2);
|
|
168345
|
+
write(`RPC source: ${process.env.BASE_RPC_URL?.trim() ? "BASE_RPC_URL environment override" : configured ? "saved custom RPC" : "saved setup or release default"} (URL redacted)`);
|
|
168346
|
+
} catch {
|
|
168347
|
+
write("RPC config: invalid or unreadable; run zk-credits config rpc.");
|
|
168348
|
+
}
|
|
168349
|
+
if (state === "running")
|
|
168350
|
+
write("Stop: zk-credits stop");
|
|
168351
|
+
else if (state === "stopped")
|
|
168352
|
+
write("Start: zk-credits serve. If setup is missing, run zk-credits setup first.");
|
|
168353
|
+
else
|
|
168354
|
+
write("Inspect the listener before starting or stopping a process on this port.");
|
|
168355
|
+
write("RPC sync failure: run zk-credits config rpc, then stop and restart the sidecar. BASE_RPC_URL overrides saved RPC.");
|
|
168356
|
+
write(`Manual inspection: lsof -nP -iTCP:${new URL(baseUrl).port} -sTCP:LISTEN; inspect the PID with ps before kill -TERM <PID>.`);
|
|
168357
|
+
}
|
|
168358
|
+
async function stopSidecar(options) {
|
|
168359
|
+
const state = await health(origin(options.baseUrl));
|
|
168360
|
+
if (state === "stopped") {
|
|
168361
|
+
options.write("Sidecar is already stopped.");
|
|
168362
|
+
return;
|
|
168363
|
+
}
|
|
168364
|
+
if (state !== "running")
|
|
168365
|
+
throw new Error(`Sidecar is ${state}; refusing shutdown. Run zk-credits doctor to inspect the listener.`);
|
|
168366
|
+
let response;
|
|
168367
|
+
try {
|
|
168368
|
+
response = await operatorRequest(options, "/shutdown", "POST");
|
|
168369
|
+
} catch {
|
|
168370
|
+
throw new Error("Could not authenticate or reach the running sidecar. Run zk-credits doctor; inspect its PID before stopping it manually.");
|
|
168371
|
+
}
|
|
168372
|
+
if (!response.ok) {
|
|
168373
|
+
throw new Error("Sidecar refused shutdown (older sidecar or mismatched token). Run zk-credits doctor; use Ctrl+C for foreground serve, or inspect its PID before kill -TERM <PID>.");
|
|
168374
|
+
}
|
|
168375
|
+
const body = await response.json();
|
|
168376
|
+
if (body.service !== "zk-credits-sidecar" || body.status !== "stopping")
|
|
168377
|
+
throw new Error("Shutdown response was not recognized. Run zk-credits doctor.");
|
|
168378
|
+
for (let attempt = 0; attempt < 50; attempt += 1) {
|
|
168379
|
+
await new Promise((resolve5) => setTimeout(resolve5, 100));
|
|
168380
|
+
if (await health(options.baseUrl) === "stopped") {
|
|
168381
|
+
options.write("Sidecar listener stopped. Active requests may still be draining.");
|
|
168382
|
+
return;
|
|
168383
|
+
}
|
|
168384
|
+
}
|
|
168385
|
+
throw new Error("Shutdown requested; the sidecar may still be draining active requests. Run zk-credits doctor to check.");
|
|
168386
|
+
}
|
|
168387
|
+
|
|
168270
168388
|
// dist/cli-runtime.js
|
|
168271
168389
|
function setupModel(args) {
|
|
168272
168390
|
const options = args.slice(1).filter((arg) => arg !== "--legacy");
|
|
@@ -168442,7 +168560,7 @@ async function ensureSidecarReady(dependencies, options = {}) {
|
|
|
168442
168560
|
if (await dependencies.isHealthy())
|
|
168443
168561
|
return dependencies.readToken();
|
|
168444
168562
|
}
|
|
168445
|
-
throw new Error(`ZK Credits sidecar did not become ready; inspect ${dependencies.logPath}
|
|
168563
|
+
throw new Error(`ZK Credits sidecar did not become ready; inspect ${dependencies.logPath}. Run zk-credits doctor, then zk-credits serve in the foreground to see the startup error. Missing setup: run zk-credits setup. RPC witness sync failure: run zk-credits config rpc and retry.`);
|
|
168446
168564
|
}
|
|
168447
168565
|
|
|
168448
168566
|
// dist/base-registered-trial.js
|
|
@@ -168605,10 +168723,10 @@ import { BASE_SEPOLIA_NETWORK as BASE_SEPOLIA_NETWORK3 } from "@zk-credits/x402-
|
|
|
168605
168723
|
function isRecord5(value) {
|
|
168606
168724
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
168607
168725
|
}
|
|
168608
|
-
async function requireGatewayKnownRoot(root,
|
|
168726
|
+
async function requireGatewayKnownRoot(root, origin2, fetcher = fetch) {
|
|
168609
168727
|
let response;
|
|
168610
168728
|
try {
|
|
168611
|
-
response = await fetcher(new URL("/v1/root-known",
|
|
168729
|
+
response = await fetcher(new URL("/v1/root-known", origin2), {
|
|
168612
168730
|
method: "POST",
|
|
168613
168731
|
headers: { accept: "application/json", "content-type": "application/json" },
|
|
168614
168732
|
body: JSON.stringify({ root }),
|
|
@@ -168692,10 +168810,10 @@ function evaluateGatewayReadiness(status, body, now = Date.now()) {
|
|
|
168692
168810
|
var DEFAULT_GATEWAY_URL2 = "https://zk-credits-gateway.onrender.com";
|
|
168693
168811
|
var DEFAULT_PORT2 = 3210;
|
|
168694
168812
|
function stateDirectory() {
|
|
168695
|
-
return process.env.ZK_CREDITS_HOME ||
|
|
168813
|
+
return process.env.ZK_CREDITS_HOME || join10(homedir2(), ".zk-credits");
|
|
168696
168814
|
}
|
|
168697
168815
|
function setupConfigPath(directory = stateDirectory()) {
|
|
168698
|
-
return
|
|
168816
|
+
return join10(directory, "config.json");
|
|
168699
168817
|
}
|
|
168700
168818
|
async function applySavedSetupConfig(environment, directory = stateDirectory()) {
|
|
168701
168819
|
await applyRpcConfig(environment, directory);
|
|
@@ -168803,6 +168921,8 @@ function printHelp() {
|
|
|
168803
168921
|
zk-credits setup [codex|x402] [--legacy] [--model <model>]
|
|
168804
168922
|
zk-credits codex [codex arguments...]
|
|
168805
168923
|
zk-credits status
|
|
168924
|
+
zk-credits doctor
|
|
168925
|
+
zk-credits stop
|
|
168806
168926
|
zk-credits serve [--port <port>] [--internal-trial-one-proof]
|
|
168807
168927
|
zk-credits x402-agent
|
|
168808
168928
|
zk-credits founder-demo-agent
|
|
@@ -168817,11 +168937,23 @@ the request-aware x402 adapter and local proof engine. It requires explicit
|
|
|
168817
168937
|
confirmation before one billable internal exchange; the task and response are
|
|
168818
168938
|
displayed locally and never added to activation evidence.
|
|
168819
168939
|
"zk-credits x402-agent" is a small task-running starter for the participant's
|
|
168820
|
-
own x402 agent loop. It
|
|
168940
|
+
own x402 agent loop. It starts its own metrics server on the selected sidecar port;
|
|
168941
|
+
stop an existing sidecar before launching it. It asks for tasks locally and sends each through the same
|
|
168821
168942
|
request-aware adapter and local proof engine; aggregate metrics stay on the
|
|
168822
168943
|
loopback-only authenticated endpoint.
|
|
168823
168944
|
"zk-credits serve --internal-trial-one-proof" limits this sidecar process to
|
|
168824
168945
|
one authenticated, valid spend request and one local proof attempt.
|
|
168946
|
+
"zk-credits doctor" reports health, authenticated PID, setup and RPC configuration
|
|
168947
|
+
presence, and log location without starting a server or revealing secrets.
|
|
168948
|
+
"zk-credits stop" gracefully drains the authenticated sidecar. For older versions,
|
|
168949
|
+
inspect the listener with lsof -nP -iTCP:3210 -sTCP:LISTEN, verify its PID with
|
|
168950
|
+
ps -p <PID> -o pid,ppid,etime,command, then kill -TERM <PID>. Foreground: Ctrl+C.
|
|
168951
|
+
Use ZK_CREDITS_SIDECAR_PORT for doctor, stop, status and env with a custom port.
|
|
168952
|
+
If serve reports missing setup, run:
|
|
168953
|
+
ZK_CREDITS_CREDENTIAL_PATH=/path/to/activated-credential.json zk-credits setup
|
|
168954
|
+
If the sidecar is stopped after setup, run zk-credits serve.
|
|
168955
|
+
For RPC witness synchronization failures, run zk-credits config rpc and then
|
|
168956
|
+
zk-credits stop followed by zk-credits serve. BASE_RPC_URL overrides the saved RPC.
|
|
168825
168957
|
Base Sepolia uses the launch endpoint baked into the package automatically. RPC endpoints
|
|
168826
168958
|
are rate-limited; run "zk-credits config rpc" to use your own endpoint.
|
|
168827
168959
|
Provider guide: https://docs.base.org/base-chain/node-operators/node-providers
|
|
@@ -168839,7 +168971,7 @@ async function loadBaseClientRuntime() {
|
|
|
168839
168971
|
const witnessPath = process.env.ZK_CREDITS_WITNESS_PATH;
|
|
168840
168972
|
const artifactDirectory = process.env.ZK_CREDITS_ARTIFACT_DIR;
|
|
168841
168973
|
if (!artifactDirectory) {
|
|
168842
|
-
throw new Error("
|
|
168974
|
+
throw new Error("Proving bundle is not configured. Run zk-credits setup first; use ZK_CREDITS_CREDENTIAL_PATH=/path/to/activated-credential.json zk-credits setup for first import. Run zk-credits doctor to inspect setup.");
|
|
168843
168975
|
}
|
|
168844
168976
|
const baseRpcUrl = process.env.BASE_RPC_URL;
|
|
168845
168977
|
const baseContractAddress = process.env.BASE_PRIVATE_CREDIT_BOND_ADDRESS;
|
|
@@ -168849,12 +168981,12 @@ async function loadBaseClientRuntime() {
|
|
|
168849
168981
|
contractAddress: baseContractAddress,
|
|
168850
168982
|
...process.env.BASE_DEPLOYMENT_BLOCK && /^\d+$/u.test(process.env.BASE_DEPLOYMENT_BLOCK) ? { deploymentBlock: process.env.BASE_DEPLOYMENT_BLOCK } : {},
|
|
168851
168983
|
...process.env.BASE_CONFIRMATIONS && /^\d+$/u.test(process.env.BASE_CONFIRMATIONS) ? { confirmations: process.env.BASE_CONFIRMATIONS } : {}
|
|
168852
|
-
},
|
|
168984
|
+
}, join10(stateDirectory(), "base-event-sync.json")) : (() => {
|
|
168853
168985
|
throw new Error("Set ZK_CREDITS_WITNESS_PATH or configure BASE_RPC_URL and BASE_PRIVATE_CREDIT_BOND_ADDRESS");
|
|
168854
168986
|
})();
|
|
168855
168987
|
const proofMetrics = createBaseProofMetrics();
|
|
168856
168988
|
const exchangeMetrics = createZkPrepaidLifecycleMetrics2();
|
|
168857
|
-
const slotLedger = await BaseSlotLedger.open({ path:
|
|
168989
|
+
const slotLedger = await BaseSlotLedger.open({ path: join10(stateDirectory(), "base-slots.json") });
|
|
168858
168990
|
const prove = await createPinnedBaseProofGenerator({ artifactDirectory, metrics: proofMetrics });
|
|
168859
168991
|
validateBaseCreditWitness(await witnessProvider.witnessForCredential(credential));
|
|
168860
168992
|
return { credential, witnessProvider, slotLedger, proofMetrics, exchangeMetrics, prove };
|
|
@@ -168885,11 +169017,11 @@ function safeCount(value) {
|
|
|
168885
169017
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
|
|
168886
169018
|
}
|
|
168887
169019
|
function gatewayOrigin() {
|
|
168888
|
-
const
|
|
168889
|
-
if (
|
|
169020
|
+
const origin2 = new URL(process.env.ZK_CREDITS_GATEWAY_URL || DEFAULT_GATEWAY_URL2);
|
|
169021
|
+
if (origin2.protocol !== "https:" || origin2.username || origin2.password || origin2.search || origin2.hash || origin2.pathname !== "/") {
|
|
168890
169022
|
throw new Error("The internal trial requires a plain HTTPS gateway origin");
|
|
168891
169023
|
}
|
|
168892
|
-
return
|
|
169024
|
+
return origin2;
|
|
168893
169025
|
}
|
|
168894
169026
|
async function readGatewayJson(url, headers) {
|
|
168895
169027
|
try {
|
|
@@ -168908,16 +169040,16 @@ async function readGatewayJson(url, headers) {
|
|
|
168908
169040
|
async function requireGatewayKnownRoot2(root) {
|
|
168909
169041
|
await requireGatewayKnownRoot(root, gatewayOrigin());
|
|
168910
169042
|
}
|
|
168911
|
-
async function gatewayReadiness(
|
|
168912
|
-
const response = await readGatewayJson(new URL("/ready",
|
|
169043
|
+
async function gatewayReadiness(origin2) {
|
|
169044
|
+
const response = await readGatewayJson(new URL("/ready", origin2), { accept: "application/json" });
|
|
168913
169045
|
const gate = evaluateGatewayReadiness(response.status, response.body);
|
|
168914
169046
|
return {
|
|
168915
169047
|
status: response.status,
|
|
168916
169048
|
...gate
|
|
168917
169049
|
};
|
|
168918
169050
|
}
|
|
168919
|
-
async function gatewayTrialSnapshot(
|
|
168920
|
-
const response = await readGatewayJson(new URL("/v1/admin/status",
|
|
169051
|
+
async function gatewayTrialSnapshot(origin2, token) {
|
|
169052
|
+
const response = await readGatewayJson(new URL("/v1/admin/status", origin2), {
|
|
168921
169053
|
accept: "application/json",
|
|
168922
169054
|
authorization: `Bearer ${token}`
|
|
168923
169055
|
});
|
|
@@ -169198,7 +169330,13 @@ async function serve(args) {
|
|
|
169198
169330
|
gatewayBaseUrl,
|
|
169199
169331
|
prepaidClient: prepaid.client,
|
|
169200
169332
|
internalTrialOneProof,
|
|
169201
|
-
metrics: () => ({ ...proofMetrics.snapshot(), exchange: exchangeMetrics.snapshot() })
|
|
169333
|
+
metrics: () => ({ ...proofMetrics.snapshot(), exchange: exchangeMetrics.snapshot() }),
|
|
169334
|
+
onShutdown: () => {
|
|
169335
|
+
void shutdown().catch((error) => {
|
|
169336
|
+
console.error(error instanceof Error ? error.message : "Sidecar shutdown failed");
|
|
169337
|
+
process.exitCode = 1;
|
|
169338
|
+
});
|
|
169339
|
+
}
|
|
169202
169340
|
});
|
|
169203
169341
|
let address2;
|
|
169204
169342
|
try {
|
|
@@ -169212,9 +169350,12 @@ async function serve(args) {
|
|
|
169212
169350
|
}
|
|
169213
169351
|
console.log(`ZK Credits sidecar listening on ${address2}/v1`);
|
|
169214
169352
|
console.log('Run eval "$(zk-credits env)" in the client shell.');
|
|
169215
|
-
|
|
169216
|
-
|
|
169217
|
-
|
|
169353
|
+
let shutdownPromise;
|
|
169354
|
+
const shutdown = () => {
|
|
169355
|
+
shutdownPromise ??= sidecar.close().then(() => {
|
|
169356
|
+
process.exit(0);
|
|
169357
|
+
});
|
|
169358
|
+
return shutdownPromise;
|
|
169218
169359
|
};
|
|
169219
169360
|
process.once("SIGINT", () => {
|
|
169220
169361
|
void shutdown();
|
|
@@ -169225,11 +169366,22 @@ async function serve(args) {
|
|
|
169225
169366
|
}
|
|
169226
169367
|
async function main() {
|
|
169227
169368
|
const args = process.argv.slice(2);
|
|
169228
|
-
|
|
169369
|
+
const localHelp = ["doctor", "stop", "status", "serve", "setup", "config"].includes(args[0] ?? "") && args.slice(1).some((arg) => arg === "--help" || arg === "-h");
|
|
169370
|
+
if (args[0] === "--help" || args[0] === "-h" || !args[0] || localHelp) {
|
|
169229
169371
|
printHelp();
|
|
169230
169372
|
return;
|
|
169231
169373
|
}
|
|
169232
169374
|
const sidecarHome = stateDirectory();
|
|
169375
|
+
if (args[0] === "doctor" || args[0] === "stop") {
|
|
169376
|
+
if (args.length !== 1)
|
|
169377
|
+
throw new Error(`Usage: zk-credits ${args[0]} (use ZK_CREDITS_SIDECAR_PORT to select a port)`);
|
|
169378
|
+
const options = { baseUrl: loopbackBaseUrl(), stateDirectory: sidecarHome, write: (line) => console.log(line) };
|
|
169379
|
+
if (args[0] === "doctor")
|
|
169380
|
+
await diagnoseSidecar(options);
|
|
169381
|
+
else
|
|
169382
|
+
await stopSidecar(options);
|
|
169383
|
+
return;
|
|
169384
|
+
}
|
|
169233
169385
|
if (args[0] === "config" && args[1] === "rpc" && args.length === 2) {
|
|
169234
169386
|
console.log(RPC_GUIDANCE);
|
|
169235
169387
|
const rpc = await checkBaseSepoliaRpc(await readHiddenValue("Base Sepolia RPC URL (hidden): "));
|
|
@@ -169297,7 +169449,7 @@ async function main() {
|
|
|
169297
169449
|
configPath: setupConfigPath(sidecarHome),
|
|
169298
169450
|
manifest,
|
|
169299
169451
|
validateWitness: async (source) => {
|
|
169300
|
-
const provider = await witnessProviderForSource2(source,
|
|
169452
|
+
const provider = await witnessProviderForSource2(source, join10(stateDirectory(), "base-event-sync.json"));
|
|
169301
169453
|
const witness = validateBaseCreditWitness(await provider.witnessForCredential(credential));
|
|
169302
169454
|
if (manifest.deployment)
|
|
169303
169455
|
await requireGatewayKnownRoot2(witness.root);
|
|
@@ -169338,6 +169490,10 @@ async function main() {
|
|
|
169338
169490
|
process.exitCode = exitCode;
|
|
169339
169491
|
}
|
|
169340
169492
|
void main().catch((error) => {
|
|
169341
|
-
|
|
169493
|
+
if (error?.code === "EADDRINUSE") {
|
|
169494
|
+
console.error("The local port is already occupied. Run zk-credits doctor to identify the listener. Stop the existing sidecar with zk-credits stop (older sidecars: inspect the PID before kill -TERM <PID>), then retry. x402-agent starts its own local metrics server and does not reuse serve.");
|
|
169495
|
+
} else {
|
|
169496
|
+
console.error(error instanceof Error ? error.message : "zk-credits failed");
|
|
169497
|
+
}
|
|
169342
169498
|
process.exitCode = 1;
|
|
169343
169499
|
});
|