zk-credits 0.2.7 → 0.2.8
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 +14 -16
- package/circuits/base-sepolia-v2.tar.gz +0 -0
- package/dist/artifact-release.d.ts +2 -1
- package/dist/artifact-release.js +63 -18
- package/dist/setup-config.d.ts +1 -1
- package/dist/zk-credits.js +59 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ receive $25 for a 30-minute session regardless of setup success. That payment
|
|
|
9
9
|
is not product revenue or willingness-to-pay evidence.
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npm install --global zk-credits@0.2.
|
|
12
|
+
npm install --global zk-credits@0.2.8
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## Pinned pilot versions
|
|
@@ -19,17 +19,18 @@ not mix versions between the sidecar, the adapter package, and the gateway:
|
|
|
19
19
|
|
|
20
20
|
| Package | Version |
|
|
21
21
|
| --- | --- |
|
|
22
|
-
| `zk-credits` (this sidecar) | `0.2.
|
|
22
|
+
| `zk-credits` (this sidecar) | `0.2.8` |
|
|
23
23
|
| `@zk-credits/x402-zk-prepaid` (adapter) | `0.1.0` |
|
|
24
24
|
| `@zk-credits/shared` | `0.1.0` |
|
|
25
25
|
|
|
26
|
-
The
|
|
26
|
+
The slot A Base Sepolia trial version is `0.2.8`. It uses the non-streaming
|
|
27
27
|
`POST /v1/chat/completions` path below, includes the narrow loopback Responses
|
|
28
|
-
bridge for supported Codex text and function-tool requests, and
|
|
29
|
-
pinned
|
|
28
|
+
bridge for supported Codex text and function-tool requests, and includes the
|
|
29
|
+
pinned proving bundle in the package. It also
|
|
30
30
|
synchronizes V2 Base events and checks the gateway known root before local
|
|
31
|
-
setup completes. The
|
|
32
|
-
|
|
31
|
+
setup completes. The packaged archive is checked against the immutable release
|
|
32
|
+
digest pinned by `circuits/manifest.json`. Version 0.2.8 must be published by
|
|
33
|
+
the owner before inviting a slot A participant.
|
|
33
34
|
|
|
34
35
|
Verify the shipped artifacts before your first prove. The package's
|
|
35
36
|
`circuits/manifest.json` fixes the SHA-256 of the frozen
|
|
@@ -60,16 +61,13 @@ still receives the class. A rejected request consumes no credit.
|
|
|
60
61
|
## First run
|
|
61
62
|
|
|
62
63
|
1. Redeem the founder-prepared invite and download the encrypted V2 credential
|
|
63
|
-
export.
|
|
64
|
-
`mangekyou-labs/zk-credits-base-sepolia-v2-bundle` before the session.
|
|
64
|
+
export. Slot A needs no access to the proving-bundle repository.
|
|
65
65
|
2. Give Codex the single setup instruction in the
|
|
66
66
|
[first-use guide](../../docs/onboarding/base-zk-credits-codex-first-use.md).
|
|
67
|
-
Codex
|
|
68
|
-
this pinned release, downloads and verifies the bundle, and runs setup.
|
|
67
|
+
Codex installs this pinned release, verifies its packaged bundle, and runs setup.
|
|
69
68
|
The human signs in if needed, enters the recovery password at the hidden
|
|
70
69
|
prompt, gives consent, and chooses their own task. No product payment is
|
|
71
|
-
involved. The sidecar
|
|
72
|
-
or stores the GitHub token.
|
|
70
|
+
involved. The sidecar needs no GitHub token to install proving material.
|
|
73
71
|
3. The setup command sets the credential, gateway, and Base Sepolia RPC
|
|
74
72
|
endpoint:
|
|
75
73
|
|
|
@@ -80,10 +78,10 @@ still receives the class. A rejected request consumes no credit.
|
|
|
80
78
|
```
|
|
81
79
|
|
|
82
80
|
`zk-credits setup codex` searches the standard local folders first. If it
|
|
83
|
-
finds no valid bundle, it
|
|
84
|
-
|
|
81
|
+
finds no valid bundle, it reads the packaged archive, verifies its pinned
|
|
82
|
+
archive digest and all three file digests,
|
|
85
83
|
then atomically installs the bundle under `~/.zk-credits/artifacts` with
|
|
86
|
-
owner-only permissions.
|
|
84
|
+
owner-only permissions. Any mismatch stops setup.
|
|
87
85
|
It syncs public Base events from the pinned V2 bond and deployment block,
|
|
88
86
|
validates that the resulting witness resolves the activated credential,
|
|
89
87
|
and asks the gateway's public `POST /v1/root-known` endpoint whether the
|
|
Binary file
|
|
@@ -5,6 +5,7 @@ export interface AcquirePinnedBundleOptions {
|
|
|
5
5
|
manifest: CircuitManifest;
|
|
6
6
|
targetDirectory: string;
|
|
7
7
|
runGithubCommand?: GithubCommand;
|
|
8
|
+
bundledArchivePath?: string;
|
|
8
9
|
}
|
|
9
|
-
/**
|
|
10
|
+
/** Installs the pinned package archive, or fetches the release for legacy/test callers. */
|
|
10
11
|
export declare function acquirePinnedArtifactBundle(options: AcquirePinnedBundleOptions): Promise<string>;
|
package/dist/artifact-release.js
CHANGED
|
@@ -5,7 +5,8 @@ import { gunzipSync } from 'node:zlib';
|
|
|
5
5
|
import { promisify } from 'node:util';
|
|
6
6
|
import { chmod, lstat, mkdir, open, readFile, rename, rm, } from 'node:fs/promises';
|
|
7
7
|
import { dirname, join, resolve } from 'node:path';
|
|
8
|
-
import {
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { ArtifactBundleError, resolvePinnedArtifactBundle } from './artifact-bundle.js';
|
|
9
10
|
const execFile = promisify(nodeExecFile);
|
|
10
11
|
const MAX_COMPRESSED_BYTES = 20 * 1024 * 1024;
|
|
11
12
|
const MAX_EXPANDED_BYTES = 32 * 1024 * 1024;
|
|
@@ -126,14 +127,15 @@ function pinnedAsset(release, raw) {
|
|
|
126
127
|
}
|
|
127
128
|
const record = value;
|
|
128
129
|
const assets = Array.isArray(record.assets) ? record.assets : [];
|
|
129
|
-
const
|
|
130
|
+
const matchingAssets = assets.filter((entry) => entry && typeof entry === 'object' && !Array.isArray(entry)
|
|
130
131
|
&& entry.name === release.assetName);
|
|
132
|
+
const asset = matchingAssets[0];
|
|
131
133
|
if (record.id !== release.releaseId
|
|
132
134
|
|| record.tag_name !== release.tag
|
|
133
135
|
|| record.draft !== false
|
|
134
136
|
|| record.immutable !== true
|
|
135
|
-
||
|
|
136
|
-
||
|
|
137
|
+
|| matchingAssets.length !== 1
|
|
138
|
+
|| asset.digest !== 'sha256:' + release.sha256) {
|
|
137
139
|
throw new Error('Pinned proving release identity or immutability check failed');
|
|
138
140
|
}
|
|
139
141
|
}
|
|
@@ -185,13 +187,15 @@ async function replaceDirectoryAtomically(stage, target) {
|
|
|
185
187
|
await rm(stage, { recursive: true, force: true });
|
|
186
188
|
}
|
|
187
189
|
}
|
|
188
|
-
/**
|
|
190
|
+
/** Installs the pinned package archive, or fetches the release for legacy/test callers. */
|
|
189
191
|
export async function acquirePinnedArtifactBundle(options) {
|
|
190
192
|
const release = options.manifest.release;
|
|
191
193
|
if (!release)
|
|
192
194
|
throw new Error('This sidecar manifest has no pinned artifact release');
|
|
193
195
|
const names = new Set(options.manifest.artifacts.map((artifact) => artifact.file));
|
|
194
196
|
const command = options.runGithubCommand ?? defaultGithubCommand;
|
|
197
|
+
const bundledArchivePath = options.bundledArchivePath
|
|
198
|
+
?? (!options.runGithubCommand ? fileURLToPath(new URL('../circuits/base-sepolia-v2.tar.gz', import.meta.url)) : undefined);
|
|
195
199
|
const parent = dirname(resolve(options.targetDirectory));
|
|
196
200
|
const tempDirectory = join(parent, '.base-sepolia-download-' + randomUUID());
|
|
197
201
|
const stage = join(parent, '.base-sepolia-stage-' + randomUUID());
|
|
@@ -199,16 +203,49 @@ export async function acquirePinnedArtifactBundle(options) {
|
|
|
199
203
|
await mkdir(parent, { recursive: true, mode: 0o700 });
|
|
200
204
|
await mkdir(tempDirectory, { recursive: false, mode: 0o700 });
|
|
201
205
|
await mkdir(stage, { recursive: false, mode: 0o700 });
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
206
|
+
if (!bundledArchivePath) {
|
|
207
|
+
try {
|
|
208
|
+
await command(['auth', 'status']);
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
throw new Error('GitHub CLI authentication is required; run gh auth login locally before downloading the pinned proving bundle.');
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
const repositoryJson = await command(['api', `repos/${release.repository}`]);
|
|
215
|
+
const repository = JSON.parse(repositoryJson);
|
|
216
|
+
if (repository.full_name !== release.repository)
|
|
217
|
+
throw new Error('Repository identity mismatch');
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
throw new Error(`Pinned proving bundle requires read access to ${release.repository}; ask the founder to grant access to your signed-in GitHub account.`);
|
|
221
|
+
}
|
|
222
|
+
let releaseJson;
|
|
223
|
+
try {
|
|
224
|
+
releaseJson = await command(['api', `repos/${release.repository}/releases/${release.releaseId}`]);
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
throw new Error('Pinned proving release metadata could not be read; ask the founder to verify the published release with an authorized account. A 404 does not distinguish a missing release from insufficient access.');
|
|
228
|
+
}
|
|
229
|
+
pinnedAsset(release, releaseJson);
|
|
230
|
+
try {
|
|
231
|
+
await command([
|
|
232
|
+
'release', 'download', release.tag,
|
|
233
|
+
'--repo', release.repository,
|
|
234
|
+
'--dir', tempDirectory,
|
|
235
|
+
'--pattern', release.assetName,
|
|
236
|
+
]);
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
throw new Error('Pinned proving bundle download failed; verify gh access and the published release asset.');
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
const archivePath = bundledArchivePath ?? join(tempDirectory, release.assetName);
|
|
243
|
+
const archiveInfo = await lstat(archivePath).catch((error) => {
|
|
244
|
+
if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {
|
|
245
|
+
throw new Error('Pinned proving bundle archive is missing or too large');
|
|
246
|
+
}
|
|
247
|
+
throw error;
|
|
248
|
+
});
|
|
212
249
|
if (archiveInfo.isSymbolicLink() || !archiveInfo.isFile() || archiveInfo.size > MAX_COMPRESSED_BYTES) {
|
|
213
250
|
throw new Error('Pinned proving bundle archive is missing or too large');
|
|
214
251
|
}
|
|
@@ -226,15 +263,23 @@ export async function acquirePinnedArtifactBundle(options) {
|
|
|
226
263
|
for (const [name, bytes] of files)
|
|
227
264
|
await writeBundleFile(stage, name, bytes);
|
|
228
265
|
await chmod(stage, 0o700);
|
|
229
|
-
|
|
266
|
+
try {
|
|
267
|
+
await resolvePinnedArtifactBundle({ artifactDirectory: stage, manifest: options.manifest });
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
if (error instanceof ArtifactBundleError && error.category === 'artifact_hash_mismatch') {
|
|
271
|
+
throw new Error(`Pinned proving bundle file SHA-256 check failed: ${error.message}`, { cause: error });
|
|
272
|
+
}
|
|
273
|
+
throw error;
|
|
274
|
+
}
|
|
230
275
|
await replaceDirectoryAtomically(stage, resolve(options.targetDirectory));
|
|
231
276
|
await resolvePinnedArtifactBundle({ artifactDirectory: resolve(options.targetDirectory), manifest: options.manifest });
|
|
232
277
|
return resolve(options.targetDirectory);
|
|
233
278
|
}
|
|
234
279
|
catch (error) {
|
|
235
|
-
if (error instanceof Error && /^(Pinned proving|This sidecar manifest|Local proving bundle)/u.test(error.message))
|
|
280
|
+
if (error instanceof Error && /^(Pinned proving|This sidecar manifest|Local proving bundle|GitHub CLI authentication)/u.test(error.message))
|
|
236
281
|
throw error;
|
|
237
|
-
throw new Error('Pinned proving bundle acquisition failed; verify
|
|
282
|
+
throw new Error('Pinned proving bundle acquisition failed; verify the packaged archive and manifest pins.');
|
|
238
283
|
}
|
|
239
284
|
finally {
|
|
240
285
|
await rm(tempDirectory, { recursive: true, force: true });
|
package/dist/setup-config.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export interface ResolveSetupInputsOptions {
|
|
|
24
24
|
validateWitness(source: SetupWitnessSource): Promise<void>;
|
|
25
25
|
/** Called only when more than one valid local candidate exists. */
|
|
26
26
|
chooseCandidate?(kind: 'proving bundle' | 'witness file', candidates: readonly string[]): Promise<string>;
|
|
27
|
-
/** Test seam for clean-install acquisition; production
|
|
27
|
+
/** Test seam for clean-install acquisition; production reads the packaged pinned archive. */
|
|
28
28
|
acquireArtifactBundle?(targetDirectory: string, manifest: CircuitManifest): Promise<void>;
|
|
29
29
|
workingDirectory?: string;
|
|
30
30
|
}
|
package/dist/zk-credits.js
CHANGED
|
@@ -82027,6 +82027,7 @@ import { gunzipSync } from "node:zlib";
|
|
|
82027
82027
|
import { promisify as promisify2 } from "node:util";
|
|
82028
82028
|
import { chmod as chmod3, lstat, mkdir as mkdir5, open, readFile as readFile4, rename as rename3, rm } from "node:fs/promises";
|
|
82029
82029
|
import { dirname as dirname4, join as join4, resolve as resolve2 } from "node:path";
|
|
82030
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
82030
82031
|
var execFile = promisify2(nodeExecFile);
|
|
82031
82032
|
var MAX_COMPRESSED_BYTES = 20 * 1024 * 1024;
|
|
82032
82033
|
var MAX_EXPANDED_BYTES = 32 * 1024 * 1024;
|
|
@@ -82145,8 +82146,9 @@ function pinnedAsset(release, raw) {
|
|
|
82145
82146
|
}
|
|
82146
82147
|
const record = value;
|
|
82147
82148
|
const assets = Array.isArray(record.assets) ? record.assets : [];
|
|
82148
|
-
const
|
|
82149
|
-
|
|
82149
|
+
const matchingAssets = assets.filter((entry) => entry && typeof entry === "object" && !Array.isArray(entry) && entry.name === release.assetName);
|
|
82150
|
+
const asset = matchingAssets[0];
|
|
82151
|
+
if (record.id !== release.releaseId || record.tag_name !== release.tag || record.draft !== false || record.immutable !== true || matchingAssets.length !== 1 || asset.digest !== "sha256:" + release.sha256) {
|
|
82150
82152
|
throw new Error("Pinned proving release identity or immutability check failed");
|
|
82151
82153
|
}
|
|
82152
82154
|
}
|
|
@@ -82198,6 +82200,7 @@ async function acquirePinnedArtifactBundle(options) {
|
|
|
82198
82200
|
throw new Error("This sidecar manifest has no pinned artifact release");
|
|
82199
82201
|
const names = new Set(options.manifest.artifacts.map((artifact) => artifact.file));
|
|
82200
82202
|
const command = options.runGithubCommand ?? defaultGithubCommand;
|
|
82203
|
+
const bundledArchivePath = options.bundledArchivePath ?? (!options.runGithubCommand ? fileURLToPath2(new URL("../circuits/base-sepolia-v2.tar.gz", import.meta.url)) : void 0);
|
|
82201
82204
|
const parent = dirname4(resolve2(options.targetDirectory));
|
|
82202
82205
|
const tempDirectory = join4(parent, ".base-sepolia-download-" + randomUUID());
|
|
82203
82206
|
const stage = join4(parent, ".base-sepolia-stage-" + randomUUID());
|
|
@@ -82205,21 +82208,50 @@ async function acquirePinnedArtifactBundle(options) {
|
|
|
82205
82208
|
await mkdir5(parent, { recursive: true, mode: 448 });
|
|
82206
82209
|
await mkdir5(tempDirectory, { recursive: false, mode: 448 });
|
|
82207
82210
|
await mkdir5(stage, { recursive: false, mode: 448 });
|
|
82208
|
-
|
|
82209
|
-
|
|
82210
|
-
|
|
82211
|
-
|
|
82212
|
-
|
|
82213
|
-
|
|
82214
|
-
|
|
82215
|
-
|
|
82216
|
-
|
|
82217
|
-
|
|
82218
|
-
|
|
82219
|
-
|
|
82220
|
-
|
|
82221
|
-
|
|
82222
|
-
|
|
82211
|
+
if (!bundledArchivePath) {
|
|
82212
|
+
try {
|
|
82213
|
+
await command(["auth", "status"]);
|
|
82214
|
+
} catch {
|
|
82215
|
+
throw new Error("GitHub CLI authentication is required; run gh auth login locally before downloading the pinned proving bundle.");
|
|
82216
|
+
}
|
|
82217
|
+
try {
|
|
82218
|
+
const repositoryJson = await command(["api", `repos/${release.repository}`]);
|
|
82219
|
+
const repository = JSON.parse(repositoryJson);
|
|
82220
|
+
if (repository.full_name !== release.repository)
|
|
82221
|
+
throw new Error("Repository identity mismatch");
|
|
82222
|
+
} catch {
|
|
82223
|
+
throw new Error(`Pinned proving bundle requires read access to ${release.repository}; ask the founder to grant access to your signed-in GitHub account.`);
|
|
82224
|
+
}
|
|
82225
|
+
let releaseJson;
|
|
82226
|
+
try {
|
|
82227
|
+
releaseJson = await command(["api", `repos/${release.repository}/releases/${release.releaseId}`]);
|
|
82228
|
+
} catch {
|
|
82229
|
+
throw new Error("Pinned proving release metadata could not be read; ask the founder to verify the published release with an authorized account. A 404 does not distinguish a missing release from insufficient access.");
|
|
82230
|
+
}
|
|
82231
|
+
pinnedAsset(release, releaseJson);
|
|
82232
|
+
try {
|
|
82233
|
+
await command([
|
|
82234
|
+
"release",
|
|
82235
|
+
"download",
|
|
82236
|
+
release.tag,
|
|
82237
|
+
"--repo",
|
|
82238
|
+
release.repository,
|
|
82239
|
+
"--dir",
|
|
82240
|
+
tempDirectory,
|
|
82241
|
+
"--pattern",
|
|
82242
|
+
release.assetName
|
|
82243
|
+
]);
|
|
82244
|
+
} catch {
|
|
82245
|
+
throw new Error("Pinned proving bundle download failed; verify gh access and the published release asset.");
|
|
82246
|
+
}
|
|
82247
|
+
}
|
|
82248
|
+
const archivePath = bundledArchivePath ?? join4(tempDirectory, release.assetName);
|
|
82249
|
+
const archiveInfo = await lstat(archivePath).catch((error) => {
|
|
82250
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
82251
|
+
throw new Error("Pinned proving bundle archive is missing or too large");
|
|
82252
|
+
}
|
|
82253
|
+
throw error;
|
|
82254
|
+
});
|
|
82223
82255
|
if (archiveInfo.isSymbolicLink() || !archiveInfo.isFile() || archiveInfo.size > MAX_COMPRESSED_BYTES) {
|
|
82224
82256
|
throw new Error("Pinned proving bundle archive is missing or too large");
|
|
82225
82257
|
}
|
|
@@ -82236,14 +82268,21 @@ async function acquirePinnedArtifactBundle(options) {
|
|
|
82236
82268
|
for (const [name, bytes] of files)
|
|
82237
82269
|
await writeBundleFile(stage, name, bytes);
|
|
82238
82270
|
await chmod3(stage, 448);
|
|
82239
|
-
|
|
82271
|
+
try {
|
|
82272
|
+
await resolvePinnedArtifactBundle({ artifactDirectory: stage, manifest: options.manifest });
|
|
82273
|
+
} catch (error) {
|
|
82274
|
+
if (error instanceof ArtifactBundleError && error.category === "artifact_hash_mismatch") {
|
|
82275
|
+
throw new Error(`Pinned proving bundle file SHA-256 check failed: ${error.message}`, { cause: error });
|
|
82276
|
+
}
|
|
82277
|
+
throw error;
|
|
82278
|
+
}
|
|
82240
82279
|
await replaceDirectoryAtomically(stage, resolve2(options.targetDirectory));
|
|
82241
82280
|
await resolvePinnedArtifactBundle({ artifactDirectory: resolve2(options.targetDirectory), manifest: options.manifest });
|
|
82242
82281
|
return resolve2(options.targetDirectory);
|
|
82243
82282
|
} catch (error) {
|
|
82244
|
-
if (error instanceof Error && /^(Pinned proving|This sidecar manifest|Local proving bundle)/u.test(error.message))
|
|
82283
|
+
if (error instanceof Error && /^(Pinned proving|This sidecar manifest|Local proving bundle|GitHub CLI authentication)/u.test(error.message))
|
|
82245
82284
|
throw error;
|
|
82246
|
-
throw new Error("Pinned proving bundle acquisition failed; verify
|
|
82285
|
+
throw new Error("Pinned proving bundle acquisition failed; verify the packaged archive and manifest pins.");
|
|
82247
82286
|
} finally {
|
|
82248
82287
|
await rm(tempDirectory, { recursive: true, force: true });
|
|
82249
82288
|
await rm(stage, { recursive: true, force: true });
|
package/package.json
CHANGED