@mikeargento/bitgraph-mcp 0.6.0 → 0.7.1
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/dist/__tests__/carrier.test.js +2 -2
- package/dist/__tests__/carrier.test.js.map +1 -1
- package/dist/__tests__/format.test.js +1 -1
- package/dist/__tests__/format.test.js.map +1 -1
- package/dist/__tests__/proof-beside.test.js +1 -1
- package/dist/__tests__/proof-beside.test.js.map +1 -1
- package/dist/__tests__/tools.test.js +1 -1
- package/dist/__tests__/tools.test.js.map +1 -1
- package/dist/format.d.ts +11 -2
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +3 -4
- package/dist/format.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/instructions.d.ts +2 -0
- package/dist/instructions.d.ts.map +1 -0
- package/dist/instructions.js +9 -0
- package/dist/instructions.js.map +1 -0
- package/dist/server.d.ts +3 -38
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +15 -106
- package/dist/server.js.map +1 -1
- package/package.json +4 -3
- package/src/__tests__/carrier.test.ts +2 -2
- package/src/__tests__/format.test.ts +2 -2
- package/src/__tests__/proof-beside.test.ts +1 -1
- package/src/__tests__/tools.test.ts +1 -1
- package/src/format.ts +3 -5
- package/src/index.ts +1 -1
- package/src/instructions.ts +11 -0
- package/src/server.ts +25 -186
- package/dist/__tests__/encoding.test.d.ts +0 -2
- package/dist/__tests__/encoding.test.d.ts.map +0 -1
- package/dist/__tests__/encoding.test.js +0 -51
- package/dist/__tests__/encoding.test.js.map +0 -1
- package/dist/__tests__/scan.test.d.ts +0 -2
- package/dist/__tests__/scan.test.d.ts.map +0 -1
- package/dist/__tests__/scan.test.js +0 -73
- package/dist/__tests__/scan.test.js.map +0 -1
- package/dist/api.d.ts +0 -42
- package/dist/api.d.ts.map +0 -1
- package/dist/api.js +0 -121
- package/dist/api.js.map +0 -1
- package/dist/carrier-io.d.ts +0 -52
- package/dist/carrier-io.d.ts.map +0 -1
- package/dist/carrier-io.js +0 -91
- package/dist/carrier-io.js.map +0 -1
- package/dist/encoding.d.ts +0 -19
- package/dist/encoding.d.ts.map +0 -1
- package/dist/encoding.js +0 -64
- package/dist/encoding.js.map +0 -1
- package/dist/scan.d.ts +0 -60
- package/dist/scan.d.ts.map +0 -1
- package/dist/scan.js +0 -201
- package/dist/scan.js.map +0 -1
- package/dist/task.d.ts +0 -55
- package/dist/task.d.ts.map +0 -1
- package/dist/task.js +0 -182
- package/dist/task.js.map +0 -1
- package/dist/types.d.ts +0 -108
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -3
- package/dist/types.js.map +0 -1
- package/src/__tests__/encoding.test.ts +0 -64
- package/src/__tests__/scan.test.ts +0 -80
- package/src/api.ts +0 -170
- package/src/carrier-io.ts +0 -120
- package/src/encoding.ts +0 -72
- package/src/scan.ts +0 -220
- package/src/task.ts +0 -187
- package/src/types.ts +0 -96
package/src/server.ts
CHANGED
|
@@ -19,24 +19,17 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
19
19
|
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
20
20
|
import type { ServerNotification, ServerRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
21
21
|
import { z } from "zod";
|
|
22
|
-
import {
|
|
23
|
-
import { FuseError, MAX_SET_MEMBERS, builderFor, fuse, fuseSet, fusedNamesFor, type FuseSetMember, type FuseSetProgress } from "@mikeargento/bitgraph";
|
|
22
|
+
import { FuseError, MAX_SET_MEMBERS, type FuseSetProgress } from "@mikeargento/bitgraph";
|
|
24
23
|
import {
|
|
25
|
-
ApiError,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
fromUrlSafeB64,
|
|
35
|
-
looksLikeDigest,
|
|
36
|
-
mapConcurrent,
|
|
37
|
-
sha256FileB64,
|
|
38
|
-
toUrlSafeB64,
|
|
39
|
-
} from "./encoding.js";
|
|
24
|
+
ApiError, batchCheck, configFromEnv, getProofDetail, indexSetMembers, search, type ApiConfig,
|
|
25
|
+
fromUrlSafeB64, looksLikeDigest, mapConcurrent, sha256FileB64, toUrlSafeB64,
|
|
26
|
+
expandPaths, scanFile, type ScannedFile,
|
|
27
|
+
type CarrierWindowView,
|
|
28
|
+
classifyPath, fuseFilePipeline, fuseSetPipeline, MAX_LOADED_BYTES,
|
|
29
|
+
type CarrierRow, type FuseFileFn, type FuseSetFn, type FusedSummary, type SetSummary,
|
|
30
|
+
SLOT_TTL_SECONDS, beginTask, decodeTaskToken, sealTask, writeProofBeside,
|
|
31
|
+
type BitGraphProof, type ProofDetailResponse,
|
|
32
|
+
} from "@mikeargento/bitgraph-sdk";
|
|
40
33
|
import {
|
|
41
34
|
capJson,
|
|
42
35
|
positionOf,
|
|
@@ -48,13 +41,11 @@ import {
|
|
|
48
41
|
type RecordOutcome,
|
|
49
42
|
type SetOutcome,
|
|
50
43
|
} from "./format.js";
|
|
51
|
-
import {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
import { SLOT_TTL_SECONDS, TASK_INSTRUCTIONS, beginTask, decodeTaskToken, sealTask, writeProofBeside } from "./task.js";
|
|
55
|
-
import type { BitGraphProof, ProofDetailResponse } from "./types.js";
|
|
44
|
+
import { TASK_INSTRUCTIONS } from "./instructions.js";
|
|
45
|
+
|
|
46
|
+
export type { FuseFileFn, FuseSetFn, FusedSummary, SetSummary } from "@mikeargento/bitgraph-sdk";
|
|
56
47
|
|
|
57
|
-
export const SERVER_VERSION = "0.
|
|
48
|
+
export const SERVER_VERSION = "0.7.1";
|
|
58
49
|
|
|
59
50
|
const SCAN_CONCURRENCY = 4;
|
|
60
51
|
/** Paths per call; a directory counts once and expands to its files. */
|
|
@@ -64,7 +55,6 @@ export const MAX_MEMBERS = 100_000;
|
|
|
64
55
|
/** Files one check may cover after directories expand. */
|
|
65
56
|
const MAX_CHECK_FILES = 10_000;
|
|
66
57
|
/** A file whose length changed while it was read is fused from its bytes instead; above this it is left out rather than held in memory. */
|
|
67
|
-
const MAX_LOADED_BYTES = 256 * 1024 * 1024;
|
|
68
58
|
/** A single file up to this size is fused on its own, in memory, with its Frame; a larger one is a set of one, never held. */
|
|
69
59
|
const MAX_SOLO_BYTES = 256 * 1024 * 1024;
|
|
70
60
|
/** Rows the structured result lists in full; every fused row shares the set's position. */
|
|
@@ -73,42 +63,6 @@ export const ROW_CAP = 500;
|
|
|
73
63
|
export const SET_INDEX_CHUNK = 2500;
|
|
74
64
|
|
|
75
65
|
/** What one set yields, in the shape the tool reports; tests inject a stand-in. */
|
|
76
|
-
export interface SetSummary {
|
|
77
|
-
set: "set/1" | "set/2";
|
|
78
|
-
proof: BitGraphProof;
|
|
79
|
-
/** Standard base64: the committed artifact's digest. */
|
|
80
|
-
artifactDigestB64: string;
|
|
81
|
-
count: number;
|
|
82
|
-
manifestEchoed: boolean;
|
|
83
|
-
recovered: boolean;
|
|
84
|
-
/** In the order the files were given. */
|
|
85
|
-
members: Array<{
|
|
86
|
-
index: number;
|
|
87
|
-
/** The row's ordinal in the committed artifact. */
|
|
88
|
-
manifestIndex: number;
|
|
89
|
-
placement: string;
|
|
90
|
-
originDigestB64: string;
|
|
91
|
-
artifactDigestB64: string;
|
|
92
|
-
/** set/2 only: the member's evidence, for the site's index. */
|
|
93
|
-
memberProof?: unknown;
|
|
94
|
-
}>;
|
|
95
|
-
}
|
|
96
|
-
export type FuseSetFn = (
|
|
97
|
-
files: readonly ScannedFile[],
|
|
98
|
-
config: ApiConfig,
|
|
99
|
-
opts: { set: "set/1" | "set/2"; onProgress?: (p: FuseSetProgress) => void }
|
|
100
|
-
) => Promise<SetSummary>;
|
|
101
|
-
|
|
102
|
-
/** What fusing one file on its own yields. */
|
|
103
|
-
export interface FusedSummary {
|
|
104
|
-
proof: BitGraphProof;
|
|
105
|
-
frame: unknown;
|
|
106
|
-
placement: string;
|
|
107
|
-
artifactDigestB64: string;
|
|
108
|
-
originDigestB64: string;
|
|
109
|
-
}
|
|
110
|
-
export type FuseFileFn = (file: ScannedFile, config: ApiConfig) => Promise<FusedSummary>;
|
|
111
|
-
|
|
112
66
|
export interface ServerDeps {
|
|
113
67
|
/** The set pipeline; tests inject a stand-in. Default: the core package's fuseSet() against the configured site. */
|
|
114
68
|
fuseSet?: FuseSetFn;
|
|
@@ -116,121 +70,6 @@ export interface ServerDeps {
|
|
|
116
70
|
fuseFile?: FuseFileFn;
|
|
117
71
|
}
|
|
118
72
|
|
|
119
|
-
/**
|
|
120
|
-
* A single file, the way a single drop on the site goes: the bytes in hand,
|
|
121
|
-
* the placement chosen from them, one slot, the fused bytes built in memory
|
|
122
|
-
* and hashed, committed under that exact slot, verified against the bytes,
|
|
123
|
-
* and a Frame returned. The fused bytes are not kept.
|
|
124
|
-
*/
|
|
125
|
-
async function fuseFileDefault(file: ScannedFile, config: ApiConfig): Promise<FusedSummary> {
|
|
126
|
-
const bytes = new Uint8Array(await readFile(file.path));
|
|
127
|
-
const placement = file.placement;
|
|
128
|
-
const { fusedName } = fusedNamesFor(file.name, placement);
|
|
129
|
-
const r = await fuse(builderFor(placement, bytes), {
|
|
130
|
-
placement,
|
|
131
|
-
original: bytes,
|
|
132
|
-
fusedFile: fusedName,
|
|
133
|
-
keepFused: false,
|
|
134
|
-
transport: { baseUrl: config.baseUrl, ...(config.apiKey ? { apiKey: config.apiKey } : {}) },
|
|
135
|
-
});
|
|
136
|
-
return {
|
|
137
|
-
proof: r.proof as unknown as BitGraphProof,
|
|
138
|
-
frame: r.frame,
|
|
139
|
-
placement,
|
|
140
|
-
artifactDigestB64: r.artifactDigestB64,
|
|
141
|
-
originDigestB64: r.originDigestB64 ?? file.digestB64,
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* The default pipeline, the one the site's drop runs: one slot for the set,
|
|
147
|
-
* every member a hashed member whose fused digest is finished from the
|
|
148
|
-
* scan's open hasher with its placement's suffix for that slot, the set's
|
|
149
|
-
* manifest (or, for a set/2, its root document) committed under the same
|
|
150
|
-
* slot, and the returned proof verified against the committed artifact with
|
|
151
|
-
* every member bound to it by digest. A file whose length changed during the
|
|
152
|
-
* scan is a loaded member: read again when it is its turn, checked against
|
|
153
|
-
* the scan's digest, fused in memory, hashed and released.
|
|
154
|
-
*/
|
|
155
|
-
async function fuseSetDefault(
|
|
156
|
-
files: readonly ScannedFile[],
|
|
157
|
-
config: ApiConfig,
|
|
158
|
-
opts: { set: "set/1" | "set/2"; onProgress?: (p: FuseSetProgress) => void }
|
|
159
|
-
): Promise<SetSummary> {
|
|
160
|
-
const members: FuseSetMember[] = files.map((f) =>
|
|
161
|
-
f.state !== null
|
|
162
|
-
? { originDigest: f.originDigest, placement: f.placement, name: f.name, fusedDigest: ({ commitment }) => fusedDigestFor(f, commitment) }
|
|
163
|
-
: { load: async () => new Uint8Array(await readFile(f.path)), originDigest: f.originDigest, placement: f.placement, name: f.name }
|
|
164
|
-
);
|
|
165
|
-
const r = await fuseSet(members, {
|
|
166
|
-
set: opts.set,
|
|
167
|
-
keepFused: false,
|
|
168
|
-
...(opts.onProgress !== undefined ? { onProgress: opts.onProgress } : {}),
|
|
169
|
-
transport: { baseUrl: config.baseUrl, ...(config.apiKey ? { apiKey: config.apiKey } : {}) },
|
|
170
|
-
});
|
|
171
|
-
return {
|
|
172
|
-
set: r.set,
|
|
173
|
-
proof: r.proof as unknown as BitGraphProof,
|
|
174
|
-
artifactDigestB64: r.artifactDigestB64,
|
|
175
|
-
count: r.members.length,
|
|
176
|
-
manifestEchoed: r.manifestEchoed,
|
|
177
|
-
recovered: r.recovered,
|
|
178
|
-
members: r.members.map((m) => ({
|
|
179
|
-
index: m.index,
|
|
180
|
-
manifestIndex: m.manifestIndex,
|
|
181
|
-
placement: m.placement,
|
|
182
|
-
originDigestB64: m.originDigestB64,
|
|
183
|
-
artifactDigestB64: m.artifactDigestB64,
|
|
184
|
-
...(m.memberProof !== undefined ? { memberProof: m.memberProof } : {}),
|
|
185
|
-
})),
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* A path is either plain bytes to scan or a BitGraphed file (bitgraph-carrier/1),
|
|
191
|
-
* decided by one 8-byte tail read. A BitGraphed file is judged OFFLINE from the
|
|
192
|
-
* proof it carries, its lookups use the digest of the committed bytes inside,
|
|
193
|
-
* and it is never minted: the envelope is not the recorded thing, the bytes
|
|
194
|
-
* inside are. That is the same structural guarantee the site's drop box gives.
|
|
195
|
-
*/
|
|
196
|
-
interface CarrierRow {
|
|
197
|
-
kind: "carrier";
|
|
198
|
-
path: string;
|
|
199
|
-
status: "ok" | "corrupt" | "too-large";
|
|
200
|
-
/** Standard base64 SHA-256 of the committed bytes inside (status "ok"). */
|
|
201
|
-
innerDigestB64: string | null;
|
|
202
|
-
view: CarrierWindowView | null;
|
|
203
|
-
/** The carried bitgraph/1 proof, for offline answers when the ledger has no row. */
|
|
204
|
-
proof: unknown | null;
|
|
205
|
-
c2pa: boolean;
|
|
206
|
-
reason: string | null;
|
|
207
|
-
}
|
|
208
|
-
type ClassifiedPath = { kind: "plain"; file: ScannedFile } | CarrierRow;
|
|
209
|
-
|
|
210
|
-
async function classifyPath(p: string): Promise<ClassifiedPath> {
|
|
211
|
-
const info = await stat(p);
|
|
212
|
-
if (info.isFile() && (await sniffCarrierTail(p, info.size))) {
|
|
213
|
-
if (info.size > MAX_LOADED_BYTES) {
|
|
214
|
-
return {
|
|
215
|
-
kind: "carrier", path: p, status: "too-large", innerDigestB64: null, view: null, proof: null, c2pa: false,
|
|
216
|
-
reason: `a BitGraphed file larger than this tool loads (${Math.round(MAX_LOADED_BYTES / (1024 * 1024))} MiB); judge it offline with: npx @mikeargento/bitgraph-audit "${p}"`,
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
const r = await readCarrierFile(p);
|
|
220
|
-
if (r.status === "corrupt" || r.innerDigestB64 === null) {
|
|
221
|
-
return {
|
|
222
|
-
kind: "carrier", path: p, status: "corrupt", innerDigestB64: null, view: null, proof: null, c2pa: false,
|
|
223
|
-
reason: `a carrier block was found at the end of the file but is unreadable: ${r.corruptReason ?? "unspecified"}. A corrupted block, not a forgery; nothing was minted for it.`,
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
return {
|
|
227
|
-
kind: "carrier", path: p, status: "ok", innerDigestB64: r.innerDigestB64, view: r.view,
|
|
228
|
-
proof: r.result?.payload?.proof ?? null, c2pa: r.inner !== null && sniffC2paBytes(r.inner), reason: null,
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
return { kind: "plain", file: await scanFile(p) };
|
|
232
|
-
}
|
|
233
|
-
|
|
234
73
|
/** Hash the given paths (bounded concurrency). Throws before any network call. */
|
|
235
74
|
async function hashPaths(paths: readonly string[]): Promise<string[]> {
|
|
236
75
|
const failures: string[] = [];
|
|
@@ -370,8 +209,8 @@ async function flushIndex(config: ApiConfig, report: Report): Promise<{ written:
|
|
|
370
209
|
}
|
|
371
210
|
|
|
372
211
|
export function buildServer(deps: ServerDeps = {}): McpServer {
|
|
373
|
-
const
|
|
374
|
-
const
|
|
212
|
+
const runFuseSet = deps.fuseSet ?? fuseSetPipeline;
|
|
213
|
+
const runFuseFile = deps.fuseFile ?? fuseFilePipeline;
|
|
375
214
|
const server = new McpServer(
|
|
376
215
|
{
|
|
377
216
|
name: "bitgraph-mcp-server",
|
|
@@ -379,7 +218,7 @@ export function buildServer(deps: ServerDeps = {}): McpServer {
|
|
|
379
218
|
},
|
|
380
219
|
{
|
|
381
220
|
instructions:
|
|
382
|
-
"BitGraph gives a file's bytes a causal position in a public sequence bracketed by Ethereum anchors. bitgraph_record makes ONE BitGraph of everything in a call, files and folders alike: a single file is fused on its own; two or more become one set under one
|
|
221
|
+
"BitGraph gives a file's bytes a causal position in a public sequence bracketed by Ethereum anchors. bitgraph_record makes ONE BitGraph of everything in a call, files and folders alike: a single file is fused on its own; two or more become one set under one position, every file's new fused bytes listed by digest in the committed artifact. " +
|
|
383
222
|
"Files are read on this machine and never uploaded or modified; the new bytes are virtual and never written. Recordings are permanent: only make BitGraphs of files the user asked for, and never generate content just to record it. bitgraph_check and bitgraph_get_proof are read-only. " +
|
|
384
223
|
"A BitGraphed file (one that carries its own proof, bitgraph-carrier/1) is recognized by its structure: bitgraph_check judges it offline from the proof inside and states the window, and bitgraph_record never re-mints it, because the envelope is not the recorded thing, the bytes inside are. " +
|
|
385
224
|
"To do work INSIDE a BitGraph, call bitgraph_open BEFORE starting: it returns a position and its commitment; put the commitment string into the task, seal the task with bitgraph_commit within 120 seconds, then record the outputs with bitgraph_record. The task then could not have existed before the position's floor block, and the outputs sit after it.",
|
|
@@ -391,9 +230,9 @@ export function buildServer(deps: ServerDeps = {}): McpServer {
|
|
|
391
230
|
{
|
|
392
231
|
title: "Make a BitGraph",
|
|
393
232
|
description:
|
|
394
|
-
"Make a BitGraph of files or folders. Everything in one call becomes ONE BitGraph, the way a drop on the site works: a single file is fused on its own
|
|
395
|
-
"On this machine each file is read once for its SHA-256 (the origin) and a hasher state; an unused
|
|
396
|
-
"Files are never modified and never uploaded: only digests, the committed artifact and
|
|
233
|
+
"Make a BitGraph of files or folders. Everything in one call becomes ONE BitGraph, the way a drop on the site works: a single file is fused on its own position; two or more files become a set under a single position on BitGraph (bitgraph.ing). " +
|
|
234
|
+
"On this machine each file is read once for its SHA-256 (the origin) and a hasher state; an unused position is allocated before any new file exists; every file's new fused bytes (the original plus a registered placement carrying the position commitment: a 48-byte trailer for JPEG, PNG, GIF, TIFF and TIFF-based raws, BMP, WebP, WAV and AVI, a small tar container with the original first for everything else) are hashed from that state without being written or held; and for a set the canonical list of those digests (above 2,000 files, a Merkle root over it) is committed under the same position. " +
|
|
235
|
+
"Files are never modified and never uploaded: only digests, the committed artifact and position records leave the machine. " +
|
|
397
236
|
"Give file paths, directory paths, or both (absolute paths preferred): a directory is every regular file under it, recursively, with hidden entries and symbolic links left out. " +
|
|
398
237
|
"Files already on record are NOT made again by default; they come back as 'on record' with their earliest position. A file can also hold a BitGraph its holder keeps, which no lookup sees. Pass again=true to make a new BitGraph regardless. " +
|
|
399
238
|
"A BitGraphed file (bitgraph-carrier/1, a file that carries its own proof) is never minted, with or without again: its carried proof is judged offline and reported, because the envelope is not the recorded thing, the bytes inside are. " +
|
|
@@ -501,14 +340,14 @@ export function buildServer(deps: ServerDeps = {}): McpServer {
|
|
|
501
340
|
// One file, as a single drop on the site goes: its own slot, its own Frame.
|
|
502
341
|
report(0, 1, "fusing");
|
|
503
342
|
try {
|
|
504
|
-
solo = { ...(await
|
|
343
|
+
solo = { ...(await runFuseFile(one, config)), file: one };
|
|
505
344
|
} catch (err) {
|
|
506
345
|
failure = setFailureText(err);
|
|
507
346
|
}
|
|
508
347
|
} else if (toMint.length > 0) {
|
|
509
348
|
const kind: "set/1" | "set/2" = toMint.length > MAX_SET_MEMBERS ? "set/2" : "set/1";
|
|
510
349
|
try {
|
|
511
|
-
made = await
|
|
350
|
+
made = await runFuseSet(toMint, config, {
|
|
512
351
|
set: kind,
|
|
513
352
|
onProgress: (p) => report(p.done, p.total, `${PHASES[p.phase]} ${p.done} of ${p.total}`),
|
|
514
353
|
});
|
|
@@ -687,7 +526,7 @@ export function buildServer(deps: ServerDeps = {}): McpServer {
|
|
|
687
526
|
{
|
|
688
527
|
title: "Open a position before the work",
|
|
689
528
|
description:
|
|
690
|
-
"Ask for a BitGraph position BEFORE starting a task, so the task can be done inside the BitGraph. Returns a fuse_token, the position, its floor block, and
|
|
529
|
+
"Ask for a BitGraph position BEFORE starting a task, so the task can be done inside the BitGraph. Returns a fuse_token, the position, its floor block, and its position commitment as a string. " +
|
|
691
530
|
"Put the commitment string into the task before running it (the prompt or request you send, a seed, a line in the document, text that must appear in the output), then call bitgraph_commit with the fuse_token and the task file's path within " + SLOT_TTL_SECONDS + " seconds. " +
|
|
692
531
|
"What that gives a stranger: the task could not have existed before the floor block (the commitment did not exist before the position did), and the outputs, recorded afterwards with bitgraph_record, sit at later positions. " +
|
|
693
532
|
"Nothing about the task is sent here; only its digest is, at commit. Positions are permanent: open one only when a task is about to run.",
|
|
@@ -720,7 +559,7 @@ export function buildServer(deps: ServerDeps = {}): McpServer {
|
|
|
720
559
|
title: "Seal the task under its position",
|
|
721
560
|
description:
|
|
722
561
|
"Step two of doing work inside a BitGraph: seal the task bytes that carry the commitment under the position bitgraph_open returned. Give the fuse_token and the path of the task file (the exact request, prompt or document that contains the commitment string), or its SHA-256 (base64) when the bytes are elsewhere. " +
|
|
723
|
-
"With a path, the file is read on this machine, the commitment string is looked for inside it BEFORE anything is sent (a task that does not carry it is refused and the
|
|
562
|
+
"With a path, the file is read on this machine, the commitment string is looked for inside it BEFORE anything is sent (a task that does not carry it is refused and the position stays held), and only the digest travels. " +
|
|
724
563
|
"Returns the proof: keep it beside the task bytes. Then record the outputs with bitgraph_record. Must be called within " + SLOT_TTL_SECONDS + " seconds of bitgraph_open.",
|
|
725
564
|
inputSchema: {
|
|
726
565
|
fuse_token: z.string().min(1).max(8000).describe("The fuse_token bitgraph_open returned."),
|
|
@@ -758,7 +597,7 @@ export function buildServer(deps: ServerDeps = {}): McpServer {
|
|
|
758
597
|
const structured = { outcome: "sealed", slot_counter: state.slot.counter, counter, epoch: epoch ? toUrlSafeB64(epoch) : null, floor_block: floor, artifact_digest: toUrlSafeB64(sealed.artifactDigestB64), commitment_offsets: sealed.offsets, proof_path: proofPath, instructions: proofNote.trim(), proof: sealed.proof };
|
|
759
598
|
if (response_format === "json") return ok(JSON.stringify(structured, null, 2), structured);
|
|
760
599
|
return ok(
|
|
761
|
-
`Sealed the task at position ${counter ?? "?"} (
|
|
600
|
+
`Sealed the task at position ${counter ?? "?"} (reserved at ${state.slot.counter}).${floor !== null ? ` Not before block ${floor}.` : ""} ` +
|
|
762
601
|
(sealed.offsets !== null ? `The commitment string was found in the sealed bytes at offset ${sealed.offsets[0]}. ` : "The bytes were not read here; a verifier looks for the commitment string in them. ") +
|
|
763
602
|
proofNote +
|
|
764
603
|
"Record the outputs with bitgraph_record when they exist." +
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"encoding.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/encoding.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2024-2026 Argento Computing Inc. Licensed under the MIT License. See LICENSE.
|
|
2
|
-
import { test } from "node:test";
|
|
3
|
-
import { strict as assert } from "node:assert";
|
|
4
|
-
import { createHash } from "node:crypto";
|
|
5
|
-
import { writeFile, mkdtemp } from "node:fs/promises";
|
|
6
|
-
import { tmpdir } from "node:os";
|
|
7
|
-
import { join } from "node:path";
|
|
8
|
-
import { fromUrlSafeB64, looksLikeDigest, mapConcurrent, sha256FileB64, toUrlSafeB64, } from "../encoding.js";
|
|
9
|
-
// A real 32-byte digest with + and / to exercise both substitutions.
|
|
10
|
-
const STANDARD = createHash("sha256").update("bitgraph").digest("base64");
|
|
11
|
-
test("url-safe round trip preserves the digest", () => {
|
|
12
|
-
const safe = toUrlSafeB64(STANDARD);
|
|
13
|
-
assert.ok(!safe.includes("+") && !safe.includes("/") && !safe.includes("="));
|
|
14
|
-
assert.equal(fromUrlSafeB64(safe), STANDARD);
|
|
15
|
-
});
|
|
16
|
-
test("fromUrlSafeB64 is a no-op on standard base64", () => {
|
|
17
|
-
assert.equal(fromUrlSafeB64(STANDARD), STANDARD);
|
|
18
|
-
});
|
|
19
|
-
test("looksLikeDigest accepts both forms, rejects junk", () => {
|
|
20
|
-
assert.ok(looksLikeDigest(STANDARD));
|
|
21
|
-
assert.ok(looksLikeDigest(toUrlSafeB64(STANDARD)));
|
|
22
|
-
assert.ok(!looksLikeDigest("not-a-digest"));
|
|
23
|
-
assert.ok(!looksLikeDigest(""));
|
|
24
|
-
assert.ok(!looksLikeDigest(Buffer.alloc(16).toString("base64"))); // 16 bytes, not 32
|
|
25
|
-
});
|
|
26
|
-
test("sha256FileB64 matches crypto over the same bytes", async () => {
|
|
27
|
-
const dir = await mkdtemp(join(tmpdir(), "bitgraph-mcp-"));
|
|
28
|
-
const file = join(dir, "sample.bin");
|
|
29
|
-
const bytes = Buffer.from("BitGraph proves causal ordering.\n");
|
|
30
|
-
await writeFile(file, bytes);
|
|
31
|
-
const expected = createHash("sha256").update(bytes).digest("base64");
|
|
32
|
-
assert.equal(await sha256FileB64(file), expected);
|
|
33
|
-
});
|
|
34
|
-
test("sha256FileB64 rejects directories and missing files", async () => {
|
|
35
|
-
await assert.rejects(() => sha256FileB64(tmpdir()), /Not a regular file/);
|
|
36
|
-
await assert.rejects(() => sha256FileB64(join(tmpdir(), "definitely-missing-9x7")));
|
|
37
|
-
});
|
|
38
|
-
test("mapConcurrent preserves order and honors the limit", async () => {
|
|
39
|
-
let inFlight = 0;
|
|
40
|
-
let peak = 0;
|
|
41
|
-
const out = await mapConcurrent([1, 2, 3, 4, 5, 6, 7, 8], 3, async (n) => {
|
|
42
|
-
inFlight += 1;
|
|
43
|
-
peak = Math.max(peak, inFlight);
|
|
44
|
-
await new Promise((r) => setTimeout(r, 5));
|
|
45
|
-
inFlight -= 1;
|
|
46
|
-
return n * 2;
|
|
47
|
-
});
|
|
48
|
-
assert.deepEqual(out, [2, 4, 6, 8, 10, 12, 14, 16]);
|
|
49
|
-
assert.ok(peak <= 3, `peak concurrency ${peak} exceeded 3`);
|
|
50
|
-
});
|
|
51
|
-
//# sourceMappingURL=encoding.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"encoding.test.js","sourceRoot":"","sources":["../../src/__tests__/encoding.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scan.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/scan.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2024-2026 Argento Computing Inc. Licensed under the MIT License. See LICENSE.
|
|
2
|
-
/**
|
|
3
|
-
* The scan's two promises: the digest is the file's own, and a state
|
|
4
|
-
* finished with the placement's suffix for a slot is the hash of the
|
|
5
|
-
* placement's own build for that slot. Both are pinned across sizes on both
|
|
6
|
-
* sides of every boundary that matters (the 64-byte sniff, the tar block,
|
|
7
|
-
* the 64 KB stream chunk) for both placements the scan makes.
|
|
8
|
-
*/
|
|
9
|
-
import { test, before } from "node:test";
|
|
10
|
-
import { strict as assert } from "node:assert";
|
|
11
|
-
import { createHash, randomBytes } from "node:crypto";
|
|
12
|
-
import { mkdtemp, mkdir, symlink, writeFile } from "node:fs/promises";
|
|
13
|
-
import { tmpdir } from "node:os";
|
|
14
|
-
import { join } from "node:path";
|
|
15
|
-
import { placementForBytes } from "@mikeargento/bitgraph";
|
|
16
|
-
import { getPlacement } from "@mikeargento/bitgraph-verify";
|
|
17
|
-
import { expandPaths, fusedDigestFor, scanFile } from "../scan.js";
|
|
18
|
-
let root = "";
|
|
19
|
-
before(async () => {
|
|
20
|
-
root = await mkdtemp(join(tmpdir(), "bitgraph-mcp-scan-"));
|
|
21
|
-
});
|
|
22
|
-
const PNG = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
23
|
-
const SIZES = [0, 1, 8, 63, 64, 65, 511, 512, 513, 1024, 65_535, 65_536, 65_537, 200_000];
|
|
24
|
-
test("a scan's digest is the file's own and its state finishes to the placement's own build", async () => {
|
|
25
|
-
let checked = 0;
|
|
26
|
-
for (const size of SIZES) {
|
|
27
|
-
for (const kind of ["png", "txt"]) {
|
|
28
|
-
const lead = kind === "png" ? PNG : Buffer.from("txt:");
|
|
29
|
-
const bytes = Buffer.concat([lead, randomBytes(Math.max(0, size - lead.length))]).subarray(0, size);
|
|
30
|
-
const path = join(root, `${kind}-${size}.bin`);
|
|
31
|
-
await writeFile(path, bytes);
|
|
32
|
-
const s = await scanFile(path);
|
|
33
|
-
assert.equal(s.size, size);
|
|
34
|
-
assert.equal(s.digestB64, createHash("sha256").update(bytes).digest("base64"), `${kind} ${size}: digest`);
|
|
35
|
-
assert.equal(s.placement, placementForBytes(new Uint8Array(bytes)), `${kind} ${size}: the core's own placement choice`);
|
|
36
|
-
assert.ok(s.state !== null, "a file that did not change keeps its state");
|
|
37
|
-
const placement = getPlacement(s.placement);
|
|
38
|
-
if (placement === undefined)
|
|
39
|
-
throw new Error("placement missing");
|
|
40
|
-
// Two slots, both finished from the same saved state: the copy leaves the state intact.
|
|
41
|
-
for (const commitment of [randomBytes(32), randomBytes(32)]) {
|
|
42
|
-
const built = placement.build({ original: new Uint8Array(bytes), originDigest: s.originDigest, commitment: new Uint8Array(commitment) });
|
|
43
|
-
const expected = createHash("sha256").update(built).digest();
|
|
44
|
-
assert.deepEqual(Buffer.from(fusedDigestFor(s, new Uint8Array(commitment))), expected, `${kind} ${size}: state finished == sha256(build)`);
|
|
45
|
-
checked += 1;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
assert.equal(checked, SIZES.length * 4);
|
|
50
|
-
});
|
|
51
|
-
test("expandPaths: files, directories, hidden entries, links, duplicates, limits, missing paths", async () => {
|
|
52
|
-
const lone = join(root, "lone.txt");
|
|
53
|
-
await writeFile(lone, "lone");
|
|
54
|
-
const dir = join(root, "tree");
|
|
55
|
-
await mkdir(join(dir, "sub", ".secret"), { recursive: true });
|
|
56
|
-
await writeFile(join(dir, "a.txt"), "a");
|
|
57
|
-
await writeFile(join(dir, ".DS_Store"), "junk");
|
|
58
|
-
await writeFile(join(dir, "sub", "b.txt"), "b");
|
|
59
|
-
await writeFile(join(dir, "sub", ".secret", "c.txt"), "c");
|
|
60
|
-
await symlink(join(dir, "a.txt"), join(dir, "link.txt"));
|
|
61
|
-
const e = await expandPaths([lone, dir], 100);
|
|
62
|
-
assert.deepEqual(e.files, [lone, join(dir, "a.txt"), join(dir, "sub", "b.txt")]);
|
|
63
|
-
assert.equal(e.directories, 1);
|
|
64
|
-
const twice = await expandPaths([lone, lone, dir, join(dir, "a.txt")], 100);
|
|
65
|
-
assert.deepEqual(twice.files, [lone, join(dir, "a.txt"), join(dir, "sub", "b.txt")], "the same file twice is one file");
|
|
66
|
-
await assert.rejects(expandPaths([dir], 1), /more than 1 files/);
|
|
67
|
-
await assert.rejects(expandPaths([lone, join(root, "missing.txt")], 100), (err) => {
|
|
68
|
-
assert.ok(err.message.includes("nothing was BitGraphed"));
|
|
69
|
-
assert.ok(err.message.includes(join(root, "missing.txt")));
|
|
70
|
-
return true;
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
//# sourceMappingURL=scan.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scan.test.js","sourceRoot":"","sources":["../../src/__tests__/scan.test.ts"],"names":[],"mappings":"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"}
|
package/dist/api.d.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import type { BatchCheckResponse, ProofDetailResponse, SearchResponse, SetIndexResponse } from "./types.js";
|
|
2
|
-
export interface ApiConfig {
|
|
3
|
-
baseUrl: string;
|
|
4
|
-
apiKey?: string;
|
|
5
|
-
}
|
|
6
|
-
/** Read configuration from the environment at call time (testable via env). */
|
|
7
|
-
export declare function configFromEnv(): ApiConfig;
|
|
8
|
-
/** Matches the batch endpoint's MAX_DIGESTS. */
|
|
9
|
-
export declare const BATCH_CHECK_LIMIT = 500;
|
|
10
|
-
export declare class ApiError extends Error {
|
|
11
|
-
readonly status: number;
|
|
12
|
-
readonly retryAfterSec: number | null;
|
|
13
|
-
constructor(status: number, message: string, retryAfterSec?: number | null);
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Look up which digests are on record. Input digests must be URL-safe base64;
|
|
17
|
-
* the response is keyed by the exact strings sent. Pages of up to 500, a few
|
|
18
|
-
* in flight at once.
|
|
19
|
-
*/
|
|
20
|
-
export declare function batchCheck(config: ApiConfig, urlSafeDigests: readonly string[]): Promise<BatchCheckResponse>;
|
|
21
|
-
/** One request's worth of a set/2's member evidence, for the site to index. */
|
|
22
|
-
export interface SetIndexRequest {
|
|
23
|
-
/** The set proof's artifact digest, URL-safe base64. */
|
|
24
|
-
setDigest: string;
|
|
25
|
-
/** The set proof's epoch id, URL-safe base64. */
|
|
26
|
-
epoch: string;
|
|
27
|
-
/** The set proof's commit counter. */
|
|
28
|
-
counter: string;
|
|
29
|
-
members: unknown[];
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Index members of a set/2 from their evidence. The site reads the set proof
|
|
33
|
-
* from its own position, binds the root document, and checks every member's
|
|
34
|
-
* path before writing a key; a row that does not bind is rejected, never
|
|
35
|
-
* written.
|
|
36
|
-
*/
|
|
37
|
-
export declare function indexSetMembers(config: ApiConfig, body: SetIndexRequest): Promise<SetIndexResponse>;
|
|
38
|
-
/** Full detail for one digest: proof, all causal positions, anchor window. */
|
|
39
|
-
export declare function getProofDetail(config: ApiConfig, urlSafeDigest: string, counter?: string, urlSafeEpoch?: string): Promise<ProofDetailResponse>;
|
|
40
|
-
/** Resolve a BitGraph number (or digest string) to a digest via /api/search. */
|
|
41
|
-
export declare function search(config: ApiConfig, q: string): Promise<SearchResponse>;
|
|
42
|
-
//# sourceMappingURL=api.d.ts.map
|
package/dist/api.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE5G,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,+EAA+E;AAC/E,wBAAgB,aAAa,IAAI,SAAS,CAIzC;AAED,gDAAgD;AAChD,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAOrC,qBAAa,QAAS,SAAQ,KAAK;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;gBAE1B,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,GAAE,MAAM,GAAG,IAAW;CAKjF;AA0DD;;;;GAIG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,SAAS,EACjB,cAAc,EAAE,SAAS,MAAM,EAAE,GAChC,OAAO,CAAC,kBAAkB,CAAC,CAW7B;AAED,+EAA+E;AAC/E,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,EAAE,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAEzG;AAED,8EAA8E;AAC9E,wBAAsB,cAAc,CAClC,MAAM,EAAE,SAAS,EACjB,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,mBAAmB,CAAC,CAU9B;AAED,gFAAgF;AAChF,wBAAsB,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAMlF"}
|
package/dist/api.js
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2024-2026 Argento Computing Inc. Licensed under the MIT License. See LICENSE.
|
|
2
|
-
/**
|
|
3
|
-
* @mikeargento/bitgraph-mcp: HTTP client for the hosted BitGraph API.
|
|
4
|
-
*
|
|
5
|
-
* The reads: the batch lookup, the proof detail, the number search. Making a
|
|
6
|
-
* BitGraph goes through the core package's own transport against the same
|
|
7
|
-
* site (its /api/fuse routes), which is what maintains the per-position
|
|
8
|
-
* by-digest index that makes every member of a set discoverable afterward;
|
|
9
|
-
* the one write here is a set/2's member evidence, indexed after the commit.
|
|
10
|
-
*/
|
|
11
|
-
import { mapConcurrent } from "./encoding.js";
|
|
12
|
-
/** Read configuration from the environment at call time (testable via env). */
|
|
13
|
-
export function configFromEnv() {
|
|
14
|
-
const baseUrl = (process.env["BITGRAPH_API_URL"] ?? "https://bitgraph.ing").replace(/\/+$/, "");
|
|
15
|
-
const apiKey = process.env["BITGRAPH_API_KEY"];
|
|
16
|
-
return apiKey ? { baseUrl, apiKey } : { baseUrl };
|
|
17
|
-
}
|
|
18
|
-
/** Matches the batch endpoint's MAX_DIGESTS. */
|
|
19
|
-
export const BATCH_CHECK_LIMIT = 500;
|
|
20
|
-
/** Batch lookups in flight at once: a large folder is many pages. */
|
|
21
|
-
const BATCH_CHECK_CONCURRENCY = 4;
|
|
22
|
-
const CHECK_TIMEOUT_MS = 30_000;
|
|
23
|
-
const SET_INDEX_TIMEOUT_MS = 60_000;
|
|
24
|
-
export class ApiError extends Error {
|
|
25
|
-
status;
|
|
26
|
-
retryAfterSec;
|
|
27
|
-
constructor(status, message, retryAfterSec = null) {
|
|
28
|
-
super(message);
|
|
29
|
-
this.status = status;
|
|
30
|
-
this.retryAfterSec = retryAfterSec;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
async function parseError(res) {
|
|
34
|
-
let message = res.statusText || `HTTP ${res.status}`;
|
|
35
|
-
try {
|
|
36
|
-
const body = (await res.json());
|
|
37
|
-
if (typeof body.error === "string" && body.error.length > 0)
|
|
38
|
-
message = body.error;
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
/* non-JSON error body */
|
|
42
|
-
}
|
|
43
|
-
const retryAfterRaw = res.headers.get("retry-after");
|
|
44
|
-
const retryAfterSec = retryAfterRaw !== null ? Number.parseInt(retryAfterRaw, 10) : null;
|
|
45
|
-
return new ApiError(res.status, message, Number.isFinite(retryAfterSec) ? retryAfterSec : null);
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Redirects are refused everywhere: an Authorization header must never follow
|
|
49
|
-
* one to a different host, and the canonical endpoint serves without them.
|
|
50
|
-
*/
|
|
51
|
-
function redirectHint(err) {
|
|
52
|
-
if (err instanceof TypeError && /redirect/i.test(err.message)) {
|
|
53
|
-
throw new Error("The BitGraph endpoint redirected. Set BITGRAPH_API_URL to the canonical https://bitgraph.ing (apex, no www).");
|
|
54
|
-
}
|
|
55
|
-
throw err;
|
|
56
|
-
}
|
|
57
|
-
async function getJson(config, path, timeoutMs) {
|
|
58
|
-
const res = await fetch(`${config.baseUrl}${path}`, {
|
|
59
|
-
method: "GET",
|
|
60
|
-
redirect: "error",
|
|
61
|
-
signal: AbortSignal.timeout(timeoutMs),
|
|
62
|
-
}).catch(redirectHint);
|
|
63
|
-
if (!res.ok)
|
|
64
|
-
throw await parseError(res);
|
|
65
|
-
return (await res.json());
|
|
66
|
-
}
|
|
67
|
-
async function postJson(config, path, body, timeoutMs, authenticated) {
|
|
68
|
-
const headers = { "Content-Type": "application/json" };
|
|
69
|
-
if (authenticated && config.apiKey)
|
|
70
|
-
headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
71
|
-
const res = await fetch(`${config.baseUrl}${path}`, {
|
|
72
|
-
method: "POST",
|
|
73
|
-
headers,
|
|
74
|
-
body: JSON.stringify(body),
|
|
75
|
-
redirect: "error",
|
|
76
|
-
signal: AbortSignal.timeout(timeoutMs),
|
|
77
|
-
}).catch(redirectHint);
|
|
78
|
-
if (!res.ok)
|
|
79
|
-
throw await parseError(res);
|
|
80
|
-
return (await res.json());
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Look up which digests are on record. Input digests must be URL-safe base64;
|
|
84
|
-
* the response is keyed by the exact strings sent. Pages of up to 500, a few
|
|
85
|
-
* in flight at once.
|
|
86
|
-
*/
|
|
87
|
-
export async function batchCheck(config, urlSafeDigests) {
|
|
88
|
-
const chunks = [];
|
|
89
|
-
for (let offset = 0; offset < urlSafeDigests.length; offset += BATCH_CHECK_LIMIT) {
|
|
90
|
-
chunks.push(urlSafeDigests.slice(offset, offset + BATCH_CHECK_LIMIT));
|
|
91
|
-
}
|
|
92
|
-
const pages = await mapConcurrent(chunks, BATCH_CHECK_CONCURRENCY, (chunk) => postJson(config, "/api/proofs/batch", { digests: chunk }, CHECK_TIMEOUT_MS, false));
|
|
93
|
-
const merged = { results: {} };
|
|
94
|
-
for (const page of pages)
|
|
95
|
-
Object.assign(merged.results, page.results);
|
|
96
|
-
return merged;
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Index members of a set/2 from their evidence. The site reads the set proof
|
|
100
|
-
* from its own position, binds the root document, and checks every member's
|
|
101
|
-
* path before writing a key; a row that does not bind is rejected, never
|
|
102
|
-
* written.
|
|
103
|
-
*/
|
|
104
|
-
export async function indexSetMembers(config, body) {
|
|
105
|
-
return postJson(config, "/api/fuse/set-index", body, SET_INDEX_TIMEOUT_MS, false);
|
|
106
|
-
}
|
|
107
|
-
/** Full detail for one digest: proof, all causal positions, anchor window. */
|
|
108
|
-
export async function getProofDetail(config, urlSafeDigest, counter, urlSafeEpoch) {
|
|
109
|
-
const params = new URLSearchParams();
|
|
110
|
-
if (counter !== undefined)
|
|
111
|
-
params.set("counter", counter);
|
|
112
|
-
if (urlSafeEpoch !== undefined)
|
|
113
|
-
params.set("epoch", urlSafeEpoch);
|
|
114
|
-
const query = params.size > 0 ? `?${params.toString()}` : "";
|
|
115
|
-
return getJson(config, `/api/proofs/digest/${encodeURIComponent(urlSafeDigest)}${query}`, CHECK_TIMEOUT_MS);
|
|
116
|
-
}
|
|
117
|
-
/** Resolve a BitGraph number (or digest string) to a digest via /api/search. */
|
|
118
|
-
export async function search(config, q) {
|
|
119
|
-
return getJson(config, `/api/search?q=${encodeURIComponent(q)}`, CHECK_TIMEOUT_MS);
|
|
120
|
-
}
|
|
121
|
-
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|