@abraca/mcp 1.0.2 → 1.0.10
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/abracadabra-mcp.cjs +1124 -42
- package/dist/abracadabra-mcp.cjs.map +1 -1
- package/dist/abracadabra-mcp.esm.js +1126 -42
- package/dist/abracadabra-mcp.esm.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/package.json +2 -1
- package/src/crypto.ts +68 -0
- package/src/index.ts +40 -13
- package/src/server.ts +31 -19
- package/src/tools/tree.ts +92 -17
|
@@ -3,8 +3,12 @@ import { ZodOptional, z } from "zod";
|
|
|
3
3
|
import process$1 from "node:process";
|
|
4
4
|
import * as Y from "yjs";
|
|
5
5
|
import { AbracadabraClient, AbracadabraProvider, awarenessStatesToArray } from "@abraca/dabra";
|
|
6
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
6
7
|
import * as fs from "node:fs";
|
|
8
|
+
import { existsSync } from "node:fs";
|
|
9
|
+
import { homedir } from "node:os";
|
|
7
10
|
import * as path from "node:path";
|
|
11
|
+
import { dirname, join } from "node:path";
|
|
8
12
|
|
|
9
13
|
//#region \0rolldown/runtime.js
|
|
10
14
|
var __create = Object.create;
|
|
@@ -18430,6 +18434,992 @@ function waitForSync(provider, timeoutMs = 15e3) {
|
|
|
18430
18434
|
});
|
|
18431
18435
|
}
|
|
18432
18436
|
|
|
18437
|
+
//#endregion
|
|
18438
|
+
//#region node_modules/@noble/ed25519/index.js
|
|
18439
|
+
/*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) */
|
|
18440
|
+
/**
|
|
18441
|
+
* 4KB JS implementation of ed25519 EdDSA signatures.
|
|
18442
|
+
* Compliant with RFC8032, FIPS 186-5 & ZIP215.
|
|
18443
|
+
* @module
|
|
18444
|
+
* @example
|
|
18445
|
+
* ```js
|
|
18446
|
+
import * as ed from '@noble/ed25519';
|
|
18447
|
+
(async () => {
|
|
18448
|
+
const privKey = ed.utils.randomPrivateKey();
|
|
18449
|
+
const message = Uint8Array.from([0xab, 0xbc, 0xcd, 0xde]);
|
|
18450
|
+
const pubKey = await ed.getPublicKeyAsync(privKey); // Sync methods are also present
|
|
18451
|
+
const signature = await ed.signAsync(message, privKey);
|
|
18452
|
+
const isValid = await ed.verifyAsync(signature, message, pubKey);
|
|
18453
|
+
})();
|
|
18454
|
+
```
|
|
18455
|
+
*/
|
|
18456
|
+
/**
|
|
18457
|
+
* Curve params. ed25519 is twisted edwards curve. Equation is −x² + y² = -a + dx²y².
|
|
18458
|
+
* * P = `2n**255n - 19n` // field over which calculations are done
|
|
18459
|
+
* * N = `2n**252n + 27742317777372353535851937790883648493n` // group order, amount of curve points
|
|
18460
|
+
* * h = 8 // cofactor
|
|
18461
|
+
* * a = `Fp.create(BigInt(-1))` // equation param
|
|
18462
|
+
* * d = -121665/121666 a.k.a. `Fp.neg(121665 * Fp.inv(121666))` // equation param
|
|
18463
|
+
* * Gx, Gy are coordinates of Generator / base point
|
|
18464
|
+
*/
|
|
18465
|
+
const ed25519_CURVE = {
|
|
18466
|
+
p: 57896044618658097711785492504343953926634992332820282019728792003956564819949n,
|
|
18467
|
+
n: 7237005577332262213973186563042994240857116359379907606001950938285454250989n,
|
|
18468
|
+
h: 8n,
|
|
18469
|
+
a: 57896044618658097711785492504343953926634992332820282019728792003956564819948n,
|
|
18470
|
+
d: 37095705934669439343138083508754565189542113879843219016388785533085940283555n,
|
|
18471
|
+
Gx: 15112221349535400772501151409588531511454012693041857206046113283949847762202n,
|
|
18472
|
+
Gy: 46316835694926478169428394003475163141307993866256225615783033603165251855960n
|
|
18473
|
+
};
|
|
18474
|
+
const { p: P, n: N, Gx, Gy, a: _a, d: _d } = ed25519_CURVE;
|
|
18475
|
+
const h = 8n;
|
|
18476
|
+
const L = 32;
|
|
18477
|
+
const L2 = 64;
|
|
18478
|
+
const err = (m = "") => {
|
|
18479
|
+
throw new Error(m);
|
|
18480
|
+
};
|
|
18481
|
+
const isBig = (n) => typeof n === "bigint";
|
|
18482
|
+
const isStr = (s) => typeof s === "string";
|
|
18483
|
+
const isBytes$1 = (a) => a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
|
|
18484
|
+
/** assert is Uint8Array (of specific length) */
|
|
18485
|
+
const abytes$1 = (a, l) => !isBytes$1(a) || typeof l === "number" && l > 0 && a.length !== l ? err("Uint8Array expected") : a;
|
|
18486
|
+
/** create Uint8Array */
|
|
18487
|
+
const u8n = (len) => new Uint8Array(len);
|
|
18488
|
+
const u8fr = (buf) => Uint8Array.from(buf);
|
|
18489
|
+
const padh = (n, pad) => n.toString(16).padStart(pad, "0");
|
|
18490
|
+
const bytesToHex = (b) => Array.from(abytes$1(b)).map((e) => padh(e, 2)).join("");
|
|
18491
|
+
const C = {
|
|
18492
|
+
_0: 48,
|
|
18493
|
+
_9: 57,
|
|
18494
|
+
A: 65,
|
|
18495
|
+
F: 70,
|
|
18496
|
+
a: 97,
|
|
18497
|
+
f: 102
|
|
18498
|
+
};
|
|
18499
|
+
const _ch = (ch) => {
|
|
18500
|
+
if (ch >= C._0 && ch <= C._9) return ch - C._0;
|
|
18501
|
+
if (ch >= C.A && ch <= C.F) return ch - (C.A - 10);
|
|
18502
|
+
if (ch >= C.a && ch <= C.f) return ch - (C.a - 10);
|
|
18503
|
+
};
|
|
18504
|
+
const hexToBytes = (hex) => {
|
|
18505
|
+
const e = "hex invalid";
|
|
18506
|
+
if (!isStr(hex)) return err(e);
|
|
18507
|
+
const hl = hex.length;
|
|
18508
|
+
const al = hl / 2;
|
|
18509
|
+
if (hl % 2) return err(e);
|
|
18510
|
+
const array = u8n(al);
|
|
18511
|
+
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
|
|
18512
|
+
const n1 = _ch(hex.charCodeAt(hi));
|
|
18513
|
+
const n2 = _ch(hex.charCodeAt(hi + 1));
|
|
18514
|
+
if (n1 === void 0 || n2 === void 0) return err(e);
|
|
18515
|
+
array[ai] = n1 * 16 + n2;
|
|
18516
|
+
}
|
|
18517
|
+
return array;
|
|
18518
|
+
};
|
|
18519
|
+
/** normalize hex or ui8a to ui8a */
|
|
18520
|
+
const toU8 = (a, len) => abytes$1(isStr(a) ? hexToBytes(a) : u8fr(abytes$1(a)), len);
|
|
18521
|
+
const cr = () => globalThis?.crypto;
|
|
18522
|
+
const subtle = () => cr()?.subtle ?? err("crypto.subtle must be defined");
|
|
18523
|
+
const concatBytes = (...arrs) => {
|
|
18524
|
+
const r = u8n(arrs.reduce((sum, a) => sum + abytes$1(a).length, 0));
|
|
18525
|
+
let pad = 0;
|
|
18526
|
+
arrs.forEach((a) => {
|
|
18527
|
+
r.set(a, pad);
|
|
18528
|
+
pad += a.length;
|
|
18529
|
+
});
|
|
18530
|
+
return r;
|
|
18531
|
+
};
|
|
18532
|
+
/** WebCrypto OS-level CSPRNG (random number generator). Will throw when not available. */
|
|
18533
|
+
const randomBytes = (len = L) => {
|
|
18534
|
+
return cr().getRandomValues(u8n(len));
|
|
18535
|
+
};
|
|
18536
|
+
const big = BigInt;
|
|
18537
|
+
const arange = (n, min, max, msg = "bad number: out of range") => isBig(n) && min <= n && n < max ? n : err(msg);
|
|
18538
|
+
/** modular division */
|
|
18539
|
+
const M = (a, b = P) => {
|
|
18540
|
+
const r = a % b;
|
|
18541
|
+
return r >= 0n ? r : b + r;
|
|
18542
|
+
};
|
|
18543
|
+
const modN = (a) => M(a, N);
|
|
18544
|
+
/** Modular inversion using eucledian GCD (non-CT). No negative exponent for now. */
|
|
18545
|
+
const invert = (num, md) => {
|
|
18546
|
+
if (num === 0n || md <= 0n) err("no inverse n=" + num + " mod=" + md);
|
|
18547
|
+
let a = M(num, md), b = md, x = 0n, y = 1n, u = 1n, v = 0n;
|
|
18548
|
+
while (a !== 0n) {
|
|
18549
|
+
const q = b / a, r = b % a;
|
|
18550
|
+
const m = x - u * q, n = y - v * q;
|
|
18551
|
+
b = a, a = r, x = u, y = v, u = m, v = n;
|
|
18552
|
+
}
|
|
18553
|
+
return b === 1n ? M(x, md) : err("no inverse");
|
|
18554
|
+
};
|
|
18555
|
+
const callHash = (name) => {
|
|
18556
|
+
const fn = etc[name];
|
|
18557
|
+
if (typeof fn !== "function") err("hashes." + name + " not set");
|
|
18558
|
+
return fn;
|
|
18559
|
+
};
|
|
18560
|
+
const apoint = (p) => p instanceof Point ? p : err("Point expected");
|
|
18561
|
+
const B256 = 2n ** 256n;
|
|
18562
|
+
/** Point in XYZT extended coordinates. */
|
|
18563
|
+
var Point = class Point {
|
|
18564
|
+
static BASE;
|
|
18565
|
+
static ZERO;
|
|
18566
|
+
ex;
|
|
18567
|
+
ey;
|
|
18568
|
+
ez;
|
|
18569
|
+
et;
|
|
18570
|
+
constructor(ex, ey, ez, et) {
|
|
18571
|
+
const max = B256;
|
|
18572
|
+
this.ex = arange(ex, 0n, max);
|
|
18573
|
+
this.ey = arange(ey, 0n, max);
|
|
18574
|
+
this.ez = arange(ez, 1n, max);
|
|
18575
|
+
this.et = arange(et, 0n, max);
|
|
18576
|
+
Object.freeze(this);
|
|
18577
|
+
}
|
|
18578
|
+
static fromAffine(p) {
|
|
18579
|
+
return new Point(p.x, p.y, 1n, M(p.x * p.y));
|
|
18580
|
+
}
|
|
18581
|
+
/** RFC8032 5.1.3: Uint8Array to Point. */
|
|
18582
|
+
static fromBytes(hex, zip215 = false) {
|
|
18583
|
+
const d = _d;
|
|
18584
|
+
const normed = u8fr(abytes$1(hex, L));
|
|
18585
|
+
const lastByte = hex[31];
|
|
18586
|
+
normed[31] = lastByte & -129;
|
|
18587
|
+
const y = bytesToNumLE(normed);
|
|
18588
|
+
arange(y, 0n, zip215 ? B256 : P);
|
|
18589
|
+
const y2 = M(y * y);
|
|
18590
|
+
let { isValid, value: x } = uvRatio(M(y2 - 1n), M(d * y2 + 1n));
|
|
18591
|
+
if (!isValid) err("bad point: y not sqrt");
|
|
18592
|
+
const isXOdd = (x & 1n) === 1n;
|
|
18593
|
+
const isLastByteOdd = (lastByte & 128) !== 0;
|
|
18594
|
+
if (!zip215 && x === 0n && isLastByteOdd) err("bad point: x==0, isLastByteOdd");
|
|
18595
|
+
if (isLastByteOdd !== isXOdd) x = M(-x);
|
|
18596
|
+
return new Point(x, y, 1n, M(x * y));
|
|
18597
|
+
}
|
|
18598
|
+
/** Checks if the point is valid and on-curve. */
|
|
18599
|
+
assertValidity() {
|
|
18600
|
+
const a = _a;
|
|
18601
|
+
const d = _d;
|
|
18602
|
+
const p = this;
|
|
18603
|
+
if (p.is0()) throw new Error("bad point: ZERO");
|
|
18604
|
+
const { ex: X, ey: Y, ez: Z, et: T } = p;
|
|
18605
|
+
const X2 = M(X * X);
|
|
18606
|
+
const Y2 = M(Y * Y);
|
|
18607
|
+
const Z2 = M(Z * Z);
|
|
18608
|
+
const Z4 = M(Z2 * Z2);
|
|
18609
|
+
if (M(Z2 * M(M(X2 * a) + Y2)) !== M(Z4 + M(d * M(X2 * Y2)))) throw new Error("bad point: equation left != right (1)");
|
|
18610
|
+
if (M(X * Y) !== M(Z * T)) throw new Error("bad point: equation left != right (2)");
|
|
18611
|
+
return this;
|
|
18612
|
+
}
|
|
18613
|
+
/** Equality check: compare points P&Q. */
|
|
18614
|
+
equals(other) {
|
|
18615
|
+
const { ex: X1, ey: Y1, ez: Z1 } = this;
|
|
18616
|
+
const { ex: X2, ey: Y2, ez: Z2 } = apoint(other);
|
|
18617
|
+
const X1Z2 = M(X1 * Z2);
|
|
18618
|
+
const X2Z1 = M(X2 * Z1);
|
|
18619
|
+
const Y1Z2 = M(Y1 * Z2);
|
|
18620
|
+
const Y2Z1 = M(Y2 * Z1);
|
|
18621
|
+
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
|
|
18622
|
+
}
|
|
18623
|
+
is0() {
|
|
18624
|
+
return this.equals(I);
|
|
18625
|
+
}
|
|
18626
|
+
/** Flip point over y coordinate. */
|
|
18627
|
+
negate() {
|
|
18628
|
+
return new Point(M(-this.ex), this.ey, this.ez, M(-this.et));
|
|
18629
|
+
}
|
|
18630
|
+
/** Point doubling. Complete formula. Cost: `4M + 4S + 1*a + 6add + 1*2`. */
|
|
18631
|
+
double() {
|
|
18632
|
+
const { ex: X1, ey: Y1, ez: Z1 } = this;
|
|
18633
|
+
const a = _a;
|
|
18634
|
+
const A = M(X1 * X1);
|
|
18635
|
+
const B = M(Y1 * Y1);
|
|
18636
|
+
const C = M(2n * M(Z1 * Z1));
|
|
18637
|
+
const D = M(a * A);
|
|
18638
|
+
const x1y1 = X1 + Y1;
|
|
18639
|
+
const E = M(M(x1y1 * x1y1) - A - B);
|
|
18640
|
+
const G = D + B;
|
|
18641
|
+
const F = G - C;
|
|
18642
|
+
const H = D - B;
|
|
18643
|
+
const X3 = M(E * F);
|
|
18644
|
+
const Y3 = M(G * H);
|
|
18645
|
+
const T3 = M(E * H);
|
|
18646
|
+
return new Point(X3, Y3, M(F * G), T3);
|
|
18647
|
+
}
|
|
18648
|
+
/** Point addition. Complete formula. Cost: `8M + 1*k + 8add + 1*2`. */
|
|
18649
|
+
add(other) {
|
|
18650
|
+
const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;
|
|
18651
|
+
const { ex: X2, ey: Y2, ez: Z2, et: T2 } = apoint(other);
|
|
18652
|
+
const a = _a;
|
|
18653
|
+
const d = _d;
|
|
18654
|
+
const A = M(X1 * X2);
|
|
18655
|
+
const B = M(Y1 * Y2);
|
|
18656
|
+
const C = M(T1 * d * T2);
|
|
18657
|
+
const D = M(Z1 * Z2);
|
|
18658
|
+
const E = M((X1 + Y1) * (X2 + Y2) - A - B);
|
|
18659
|
+
const F = M(D - C);
|
|
18660
|
+
const G = M(D + C);
|
|
18661
|
+
const H = M(B - a * A);
|
|
18662
|
+
const X3 = M(E * F);
|
|
18663
|
+
const Y3 = M(G * H);
|
|
18664
|
+
const T3 = M(E * H);
|
|
18665
|
+
return new Point(X3, Y3, M(F * G), T3);
|
|
18666
|
+
}
|
|
18667
|
+
/**
|
|
18668
|
+
* Point-by-scalar multiplication. Scalar must be in range 1 <= n < CURVE.n.
|
|
18669
|
+
* Uses {@link wNAF} for base point.
|
|
18670
|
+
* Uses fake point to mitigate side-channel leakage.
|
|
18671
|
+
* @param n scalar by which point is multiplied
|
|
18672
|
+
* @param safe safe mode guards against timing attacks; unsafe mode is faster
|
|
18673
|
+
*/
|
|
18674
|
+
multiply(n, safe = true) {
|
|
18675
|
+
if (!safe && (n === 0n || this.is0())) return I;
|
|
18676
|
+
arange(n, 1n, N);
|
|
18677
|
+
if (n === 1n) return this;
|
|
18678
|
+
if (this.equals(G)) return wNAF(n).p;
|
|
18679
|
+
let p = I;
|
|
18680
|
+
let f = G;
|
|
18681
|
+
for (let d = this; n > 0n; d = d.double(), n >>= 1n) if (n & 1n) p = p.add(d);
|
|
18682
|
+
else if (safe) f = f.add(d);
|
|
18683
|
+
return p;
|
|
18684
|
+
}
|
|
18685
|
+
/** Convert point to 2d xy affine point. (X, Y, Z) ∋ (x=X/Z, y=Y/Z) */
|
|
18686
|
+
toAffine() {
|
|
18687
|
+
const { ex: x, ey: y, ez: z } = this;
|
|
18688
|
+
if (this.equals(I)) return {
|
|
18689
|
+
x: 0n,
|
|
18690
|
+
y: 1n
|
|
18691
|
+
};
|
|
18692
|
+
const iz = invert(z, P);
|
|
18693
|
+
if (M(z * iz) !== 1n) err("invalid inverse");
|
|
18694
|
+
return {
|
|
18695
|
+
x: M(x * iz),
|
|
18696
|
+
y: M(y * iz)
|
|
18697
|
+
};
|
|
18698
|
+
}
|
|
18699
|
+
toBytes() {
|
|
18700
|
+
const { x, y } = this.assertValidity().toAffine();
|
|
18701
|
+
const b = numTo32bLE(y);
|
|
18702
|
+
b[31] |= x & 1n ? 128 : 0;
|
|
18703
|
+
return b;
|
|
18704
|
+
}
|
|
18705
|
+
toHex() {
|
|
18706
|
+
return bytesToHex(this.toBytes());
|
|
18707
|
+
}
|
|
18708
|
+
clearCofactor() {
|
|
18709
|
+
return this.multiply(big(h), false);
|
|
18710
|
+
}
|
|
18711
|
+
isSmallOrder() {
|
|
18712
|
+
return this.clearCofactor().is0();
|
|
18713
|
+
}
|
|
18714
|
+
isTorsionFree() {
|
|
18715
|
+
let p = this.multiply(N / 2n, false).double();
|
|
18716
|
+
if (N % 2n) p = p.add(this);
|
|
18717
|
+
return p.is0();
|
|
18718
|
+
}
|
|
18719
|
+
static fromHex(hex, zip215) {
|
|
18720
|
+
return Point.fromBytes(toU8(hex), zip215);
|
|
18721
|
+
}
|
|
18722
|
+
get x() {
|
|
18723
|
+
return this.toAffine().x;
|
|
18724
|
+
}
|
|
18725
|
+
get y() {
|
|
18726
|
+
return this.toAffine().y;
|
|
18727
|
+
}
|
|
18728
|
+
toRawBytes() {
|
|
18729
|
+
return this.toBytes();
|
|
18730
|
+
}
|
|
18731
|
+
};
|
|
18732
|
+
/** Generator / base point */
|
|
18733
|
+
const G = new Point(Gx, Gy, 1n, M(Gx * Gy));
|
|
18734
|
+
/** Identity / zero point */
|
|
18735
|
+
const I = new Point(0n, 1n, 1n, 0n);
|
|
18736
|
+
Point.BASE = G;
|
|
18737
|
+
Point.ZERO = I;
|
|
18738
|
+
const numTo32bLE = (num) => hexToBytes(padh(arange(num, 0n, B256), L2)).reverse();
|
|
18739
|
+
const bytesToNumLE = (b) => big("0x" + bytesToHex(u8fr(abytes$1(b)).reverse()));
|
|
18740
|
+
const pow2 = (x, power) => {
|
|
18741
|
+
let r = x;
|
|
18742
|
+
while (power-- > 0n) {
|
|
18743
|
+
r *= r;
|
|
18744
|
+
r %= P;
|
|
18745
|
+
}
|
|
18746
|
+
return r;
|
|
18747
|
+
};
|
|
18748
|
+
const pow_2_252_3 = (x) => {
|
|
18749
|
+
const b2 = x * x % P * x % P;
|
|
18750
|
+
const b5 = pow2(pow2(b2, 2n) * b2 % P, 1n) * x % P;
|
|
18751
|
+
const b10 = pow2(b5, 5n) * b5 % P;
|
|
18752
|
+
const b20 = pow2(b10, 10n) * b10 % P;
|
|
18753
|
+
const b40 = pow2(b20, 20n) * b20 % P;
|
|
18754
|
+
const b80 = pow2(b40, 40n) * b40 % P;
|
|
18755
|
+
return {
|
|
18756
|
+
pow_p_5_8: pow2(pow2(pow2(pow2(b80, 80n) * b80 % P, 80n) * b80 % P, 10n) * b10 % P, 2n) * x % P,
|
|
18757
|
+
b2
|
|
18758
|
+
};
|
|
18759
|
+
};
|
|
18760
|
+
const RM1 = 19681161376707505956807079304988542015446066515923890162744021073123829784752n;
|
|
18761
|
+
const uvRatio = (u, v) => {
|
|
18762
|
+
const v3 = M(v * v * v);
|
|
18763
|
+
const pow = pow_2_252_3(u * M(v3 * v3 * v)).pow_p_5_8;
|
|
18764
|
+
let x = M(u * v3 * pow);
|
|
18765
|
+
const vx2 = M(v * x * x);
|
|
18766
|
+
const root1 = x;
|
|
18767
|
+
const root2 = M(x * RM1);
|
|
18768
|
+
const useRoot1 = vx2 === u;
|
|
18769
|
+
const useRoot2 = vx2 === M(-u);
|
|
18770
|
+
const noRoot = vx2 === M(-u * RM1);
|
|
18771
|
+
if (useRoot1) x = root1;
|
|
18772
|
+
if (useRoot2 || noRoot) x = root2;
|
|
18773
|
+
if ((M(x) & 1n) === 1n) x = M(-x);
|
|
18774
|
+
return {
|
|
18775
|
+
isValid: useRoot1 || useRoot2,
|
|
18776
|
+
value: x
|
|
18777
|
+
};
|
|
18778
|
+
};
|
|
18779
|
+
const modL_LE = (hash) => modN(bytesToNumLE(hash));
|
|
18780
|
+
const sha512a = (...m) => etc.sha512Async(...m);
|
|
18781
|
+
const sha512s = (...m) => callHash("sha512Sync")(...m);
|
|
18782
|
+
const hash2extK = (hashed) => {
|
|
18783
|
+
const head = hashed.slice(0, L);
|
|
18784
|
+
head[0] &= 248;
|
|
18785
|
+
head[31] &= 127;
|
|
18786
|
+
head[31] |= 64;
|
|
18787
|
+
const prefix = hashed.slice(L, L2);
|
|
18788
|
+
const scalar = modL_LE(head);
|
|
18789
|
+
const point = G.multiply(scalar);
|
|
18790
|
+
return {
|
|
18791
|
+
head,
|
|
18792
|
+
prefix,
|
|
18793
|
+
scalar,
|
|
18794
|
+
point,
|
|
18795
|
+
pointBytes: point.toBytes()
|
|
18796
|
+
};
|
|
18797
|
+
};
|
|
18798
|
+
const getExtendedPublicKeyAsync = (priv) => sha512a(toU8(priv, L)).then(hash2extK);
|
|
18799
|
+
const getExtendedPublicKey = (priv) => hash2extK(sha512s(toU8(priv, L)));
|
|
18800
|
+
/** Creates 32-byte ed25519 public key from 32-byte private key. To use, set `etc.sha512Sync` first. */
|
|
18801
|
+
const getPublicKey = (priv) => getExtendedPublicKey(priv).pointBytes;
|
|
18802
|
+
const hashFinishS = (res) => res.finish(sha512s(res.hashable));
|
|
18803
|
+
const _sign = (e, rBytes, msg) => {
|
|
18804
|
+
const { pointBytes: P, scalar: s } = e;
|
|
18805
|
+
const r = modL_LE(rBytes);
|
|
18806
|
+
const R = G.multiply(r).toBytes();
|
|
18807
|
+
const hashable = concatBytes(R, P, msg);
|
|
18808
|
+
const finish = (hashed) => {
|
|
18809
|
+
return abytes$1(concatBytes(R, numTo32bLE(modN(r + modL_LE(hashed) * s))), L2);
|
|
18810
|
+
};
|
|
18811
|
+
return {
|
|
18812
|
+
hashable,
|
|
18813
|
+
finish
|
|
18814
|
+
};
|
|
18815
|
+
};
|
|
18816
|
+
/**
|
|
18817
|
+
* Signs message (NOT message hash) using private key. To use, set `hashes.sha512` first.
|
|
18818
|
+
* Follows RFC8032 5.1.6.
|
|
18819
|
+
*/
|
|
18820
|
+
const sign = (msg, privKey) => {
|
|
18821
|
+
const m = toU8(msg);
|
|
18822
|
+
const e = getExtendedPublicKey(privKey);
|
|
18823
|
+
return hashFinishS(_sign(e, sha512s(e.prefix, m), m));
|
|
18824
|
+
};
|
|
18825
|
+
/** Math, hex, byte helpers. Not in `utils` because utils share API with noble-curves. */
|
|
18826
|
+
const etc = {
|
|
18827
|
+
sha512Async: async (...messages) => {
|
|
18828
|
+
const s = subtle();
|
|
18829
|
+
const m = concatBytes(...messages);
|
|
18830
|
+
return u8n(await s.digest("SHA-512", m.buffer));
|
|
18831
|
+
},
|
|
18832
|
+
sha512Sync: void 0,
|
|
18833
|
+
bytesToHex,
|
|
18834
|
+
hexToBytes,
|
|
18835
|
+
concatBytes,
|
|
18836
|
+
mod: M,
|
|
18837
|
+
invert,
|
|
18838
|
+
randomBytes
|
|
18839
|
+
};
|
|
18840
|
+
/** ed25519-specific key utilities. */
|
|
18841
|
+
const utils = {
|
|
18842
|
+
getExtendedPublicKeyAsync,
|
|
18843
|
+
getExtendedPublicKey,
|
|
18844
|
+
randomPrivateKey: () => randomBytes(L),
|
|
18845
|
+
precompute: (w = 8, p = G) => {
|
|
18846
|
+
p.multiply(3n);
|
|
18847
|
+
return p;
|
|
18848
|
+
}
|
|
18849
|
+
};
|
|
18850
|
+
const W = 8;
|
|
18851
|
+
const pwindows = Math.ceil(256 / W) + 1;
|
|
18852
|
+
const pwindowSize = 2 ** (W - 1);
|
|
18853
|
+
const precompute = () => {
|
|
18854
|
+
const points = [];
|
|
18855
|
+
let p = G;
|
|
18856
|
+
let b = p;
|
|
18857
|
+
for (let w = 0; w < pwindows; w++) {
|
|
18858
|
+
b = p;
|
|
18859
|
+
points.push(b);
|
|
18860
|
+
for (let i = 1; i < pwindowSize; i++) {
|
|
18861
|
+
b = b.add(p);
|
|
18862
|
+
points.push(b);
|
|
18863
|
+
}
|
|
18864
|
+
p = b.double();
|
|
18865
|
+
}
|
|
18866
|
+
return points;
|
|
18867
|
+
};
|
|
18868
|
+
let Gpows = void 0;
|
|
18869
|
+
const ctneg = (cnd, p) => {
|
|
18870
|
+
const n = p.negate();
|
|
18871
|
+
return cnd ? n : p;
|
|
18872
|
+
};
|
|
18873
|
+
/**
|
|
18874
|
+
* Precomputes give 12x faster getPublicKey(), 10x sign(), 2x verify() by
|
|
18875
|
+
* caching multiples of G (base point). Cache is stored in 32MB of RAM.
|
|
18876
|
+
* Any time `G.multiply` is done, precomputes are used.
|
|
18877
|
+
* Not used for getSharedSecret, which instead multiplies random pubkey `P.multiply`.
|
|
18878
|
+
*
|
|
18879
|
+
* w-ary non-adjacent form (wNAF) precomputation method is 10% slower than windowed method,
|
|
18880
|
+
* but takes 2x less RAM. RAM reduction is possible by utilizing `.subtract`.
|
|
18881
|
+
*
|
|
18882
|
+
* !! Precomputes can be disabled by commenting-out call of the wNAF() inside Point#multiply().
|
|
18883
|
+
*/
|
|
18884
|
+
const wNAF = (n) => {
|
|
18885
|
+
const comp = Gpows || (Gpows = precompute());
|
|
18886
|
+
let p = I;
|
|
18887
|
+
let f = G;
|
|
18888
|
+
const pow_2_w = 2 ** W;
|
|
18889
|
+
const maxNum = pow_2_w;
|
|
18890
|
+
const mask = big(pow_2_w - 1);
|
|
18891
|
+
const shiftBy = big(W);
|
|
18892
|
+
for (let w = 0; w < pwindows; w++) {
|
|
18893
|
+
let wbits = Number(n & mask);
|
|
18894
|
+
n >>= shiftBy;
|
|
18895
|
+
if (wbits > pwindowSize) {
|
|
18896
|
+
wbits -= maxNum;
|
|
18897
|
+
n += 1n;
|
|
18898
|
+
}
|
|
18899
|
+
const off = w * pwindowSize;
|
|
18900
|
+
const offF = off;
|
|
18901
|
+
const offP = off + Math.abs(wbits) - 1;
|
|
18902
|
+
const isEven = w % 2 !== 0;
|
|
18903
|
+
const isNeg = wbits < 0;
|
|
18904
|
+
if (wbits === 0) f = f.add(ctneg(isEven, comp[offF]));
|
|
18905
|
+
else p = p.add(ctneg(isNeg, comp[offP]));
|
|
18906
|
+
}
|
|
18907
|
+
return {
|
|
18908
|
+
p,
|
|
18909
|
+
f
|
|
18910
|
+
};
|
|
18911
|
+
};
|
|
18912
|
+
|
|
18913
|
+
//#endregion
|
|
18914
|
+
//#region node_modules/@noble/hashes/esm/utils.js
|
|
18915
|
+
/** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */
|
|
18916
|
+
function isBytes(a) {
|
|
18917
|
+
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
|
|
18918
|
+
}
|
|
18919
|
+
/** Asserts something is Uint8Array. */
|
|
18920
|
+
function abytes(b, ...lengths) {
|
|
18921
|
+
if (!isBytes(b)) throw new Error("Uint8Array expected");
|
|
18922
|
+
if (lengths.length > 0 && !lengths.includes(b.length)) throw new Error("Uint8Array expected of length " + lengths + ", got length=" + b.length);
|
|
18923
|
+
}
|
|
18924
|
+
/** Asserts a hash instance has not been destroyed / finished */
|
|
18925
|
+
function aexists(instance, checkFinished = true) {
|
|
18926
|
+
if (instance.destroyed) throw new Error("Hash instance has been destroyed");
|
|
18927
|
+
if (checkFinished && instance.finished) throw new Error("Hash#digest() has already been called");
|
|
18928
|
+
}
|
|
18929
|
+
/** Asserts output is properly-sized byte array */
|
|
18930
|
+
function aoutput(out, instance) {
|
|
18931
|
+
abytes(out);
|
|
18932
|
+
const min = instance.outputLen;
|
|
18933
|
+
if (out.length < min) throw new Error("digestInto() expects output buffer of length at least " + min);
|
|
18934
|
+
}
|
|
18935
|
+
/** Zeroize a byte array. Warning: JS provides no guarantees. */
|
|
18936
|
+
function clean(...arrays) {
|
|
18937
|
+
for (let i = 0; i < arrays.length; i++) arrays[i].fill(0);
|
|
18938
|
+
}
|
|
18939
|
+
/** Create DataView of an array for easy byte-level manipulation. */
|
|
18940
|
+
function createView(arr) {
|
|
18941
|
+
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
18942
|
+
}
|
|
18943
|
+
/** Is current platform little-endian? Most are. Big-Endian platform: IBM */
|
|
18944
|
+
const isLE = new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68;
|
|
18945
|
+
const hasHexBuiltin = typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function";
|
|
18946
|
+
/**
|
|
18947
|
+
* Converts string to bytes using UTF8 encoding.
|
|
18948
|
+
* @example utf8ToBytes('abc') // Uint8Array.from([97, 98, 99])
|
|
18949
|
+
*/
|
|
18950
|
+
function utf8ToBytes(str) {
|
|
18951
|
+
if (typeof str !== "string") throw new Error("string expected");
|
|
18952
|
+
return new Uint8Array(new TextEncoder().encode(str));
|
|
18953
|
+
}
|
|
18954
|
+
/**
|
|
18955
|
+
* Normalizes (non-hex) string or Uint8Array to Uint8Array.
|
|
18956
|
+
* Warning: when Uint8Array is passed, it would NOT get copied.
|
|
18957
|
+
* Keep in mind for future mutable operations.
|
|
18958
|
+
*/
|
|
18959
|
+
function toBytes(data) {
|
|
18960
|
+
if (typeof data === "string") data = utf8ToBytes(data);
|
|
18961
|
+
abytes(data);
|
|
18962
|
+
return data;
|
|
18963
|
+
}
|
|
18964
|
+
/** For runtime check if class implements interface */
|
|
18965
|
+
var Hash = class {};
|
|
18966
|
+
/** Wraps hash function, creating an interface on top of it */
|
|
18967
|
+
function createHasher(hashCons) {
|
|
18968
|
+
const hashC = (msg) => hashCons().update(toBytes(msg)).digest();
|
|
18969
|
+
const tmp = hashCons();
|
|
18970
|
+
hashC.outputLen = tmp.outputLen;
|
|
18971
|
+
hashC.blockLen = tmp.blockLen;
|
|
18972
|
+
hashC.create = () => hashCons();
|
|
18973
|
+
return hashC;
|
|
18974
|
+
}
|
|
18975
|
+
|
|
18976
|
+
//#endregion
|
|
18977
|
+
//#region node_modules/@noble/hashes/esm/_md.js
|
|
18978
|
+
/**
|
|
18979
|
+
* Internal Merkle-Damgard hash utils.
|
|
18980
|
+
* @module
|
|
18981
|
+
*/
|
|
18982
|
+
/** Polyfill for Safari 14. https://caniuse.com/mdn-javascript_builtins_dataview_setbiguint64 */
|
|
18983
|
+
function setBigUint64(view, byteOffset, value, isLE) {
|
|
18984
|
+
if (typeof view.setBigUint64 === "function") return view.setBigUint64(byteOffset, value, isLE);
|
|
18985
|
+
const _32n = BigInt(32);
|
|
18986
|
+
const _u32_max = BigInt(4294967295);
|
|
18987
|
+
const wh = Number(value >> _32n & _u32_max);
|
|
18988
|
+
const wl = Number(value & _u32_max);
|
|
18989
|
+
const h = isLE ? 4 : 0;
|
|
18990
|
+
const l = isLE ? 0 : 4;
|
|
18991
|
+
view.setUint32(byteOffset + h, wh, isLE);
|
|
18992
|
+
view.setUint32(byteOffset + l, wl, isLE);
|
|
18993
|
+
}
|
|
18994
|
+
/**
|
|
18995
|
+
* Merkle-Damgard hash construction base class.
|
|
18996
|
+
* Could be used to create MD5, RIPEMD, SHA1, SHA2.
|
|
18997
|
+
*/
|
|
18998
|
+
var HashMD = class extends Hash {
|
|
18999
|
+
constructor(blockLen, outputLen, padOffset, isLE) {
|
|
19000
|
+
super();
|
|
19001
|
+
this.finished = false;
|
|
19002
|
+
this.length = 0;
|
|
19003
|
+
this.pos = 0;
|
|
19004
|
+
this.destroyed = false;
|
|
19005
|
+
this.blockLen = blockLen;
|
|
19006
|
+
this.outputLen = outputLen;
|
|
19007
|
+
this.padOffset = padOffset;
|
|
19008
|
+
this.isLE = isLE;
|
|
19009
|
+
this.buffer = new Uint8Array(blockLen);
|
|
19010
|
+
this.view = createView(this.buffer);
|
|
19011
|
+
}
|
|
19012
|
+
update(data) {
|
|
19013
|
+
aexists(this);
|
|
19014
|
+
data = toBytes(data);
|
|
19015
|
+
abytes(data);
|
|
19016
|
+
const { view, buffer, blockLen } = this;
|
|
19017
|
+
const len = data.length;
|
|
19018
|
+
for (let pos = 0; pos < len;) {
|
|
19019
|
+
const take = Math.min(blockLen - this.pos, len - pos);
|
|
19020
|
+
if (take === blockLen) {
|
|
19021
|
+
const dataView = createView(data);
|
|
19022
|
+
for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos);
|
|
19023
|
+
continue;
|
|
19024
|
+
}
|
|
19025
|
+
buffer.set(data.subarray(pos, pos + take), this.pos);
|
|
19026
|
+
this.pos += take;
|
|
19027
|
+
pos += take;
|
|
19028
|
+
if (this.pos === blockLen) {
|
|
19029
|
+
this.process(view, 0);
|
|
19030
|
+
this.pos = 0;
|
|
19031
|
+
}
|
|
19032
|
+
}
|
|
19033
|
+
this.length += data.length;
|
|
19034
|
+
this.roundClean();
|
|
19035
|
+
return this;
|
|
19036
|
+
}
|
|
19037
|
+
digestInto(out) {
|
|
19038
|
+
aexists(this);
|
|
19039
|
+
aoutput(out, this);
|
|
19040
|
+
this.finished = true;
|
|
19041
|
+
const { buffer, view, blockLen, isLE } = this;
|
|
19042
|
+
let { pos } = this;
|
|
19043
|
+
buffer[pos++] = 128;
|
|
19044
|
+
clean(this.buffer.subarray(pos));
|
|
19045
|
+
if (this.padOffset > blockLen - pos) {
|
|
19046
|
+
this.process(view, 0);
|
|
19047
|
+
pos = 0;
|
|
19048
|
+
}
|
|
19049
|
+
for (let i = pos; i < blockLen; i++) buffer[i] = 0;
|
|
19050
|
+
setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE);
|
|
19051
|
+
this.process(view, 0);
|
|
19052
|
+
const oview = createView(out);
|
|
19053
|
+
const len = this.outputLen;
|
|
19054
|
+
if (len % 4) throw new Error("_sha2: outputLen should be aligned to 32bit");
|
|
19055
|
+
const outLen = len / 4;
|
|
19056
|
+
const state = this.get();
|
|
19057
|
+
if (outLen > state.length) throw new Error("_sha2: outputLen bigger than state");
|
|
19058
|
+
for (let i = 0; i < outLen; i++) oview.setUint32(4 * i, state[i], isLE);
|
|
19059
|
+
}
|
|
19060
|
+
digest() {
|
|
19061
|
+
const { buffer, outputLen } = this;
|
|
19062
|
+
this.digestInto(buffer);
|
|
19063
|
+
const res = buffer.slice(0, outputLen);
|
|
19064
|
+
this.destroy();
|
|
19065
|
+
return res;
|
|
19066
|
+
}
|
|
19067
|
+
_cloneInto(to) {
|
|
19068
|
+
to || (to = new this.constructor());
|
|
19069
|
+
to.set(...this.get());
|
|
19070
|
+
const { blockLen, buffer, length, finished, destroyed, pos } = this;
|
|
19071
|
+
to.destroyed = destroyed;
|
|
19072
|
+
to.finished = finished;
|
|
19073
|
+
to.length = length;
|
|
19074
|
+
to.pos = pos;
|
|
19075
|
+
if (length % blockLen) to.buffer.set(buffer);
|
|
19076
|
+
return to;
|
|
19077
|
+
}
|
|
19078
|
+
clone() {
|
|
19079
|
+
return this._cloneInto();
|
|
19080
|
+
}
|
|
19081
|
+
};
|
|
19082
|
+
/** Initial SHA512 state. Bits 0..64 of frac part of sqrt of primes 2..19 */
|
|
19083
|
+
const SHA512_IV = /* @__PURE__ */ Uint32Array.from([
|
|
19084
|
+
1779033703,
|
|
19085
|
+
4089235720,
|
|
19086
|
+
3144134277,
|
|
19087
|
+
2227873595,
|
|
19088
|
+
1013904242,
|
|
19089
|
+
4271175723,
|
|
19090
|
+
2773480762,
|
|
19091
|
+
1595750129,
|
|
19092
|
+
1359893119,
|
|
19093
|
+
2917565137,
|
|
19094
|
+
2600822924,
|
|
19095
|
+
725511199,
|
|
19096
|
+
528734635,
|
|
19097
|
+
4215389547,
|
|
19098
|
+
1541459225,
|
|
19099
|
+
327033209
|
|
19100
|
+
]);
|
|
19101
|
+
|
|
19102
|
+
//#endregion
|
|
19103
|
+
//#region node_modules/@noble/hashes/esm/_u64.js
|
|
19104
|
+
/**
|
|
19105
|
+
* Internal helpers for u64. BigUint64Array is too slow as per 2025, so we implement it using Uint32Array.
|
|
19106
|
+
* @todo re-check https://issues.chromium.org/issues/42212588
|
|
19107
|
+
* @module
|
|
19108
|
+
*/
|
|
19109
|
+
const U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1);
|
|
19110
|
+
const _32n = /* @__PURE__ */ BigInt(32);
|
|
19111
|
+
function fromBig(n, le = false) {
|
|
19112
|
+
if (le) return {
|
|
19113
|
+
h: Number(n & U32_MASK64),
|
|
19114
|
+
l: Number(n >> _32n & U32_MASK64)
|
|
19115
|
+
};
|
|
19116
|
+
return {
|
|
19117
|
+
h: Number(n >> _32n & U32_MASK64) | 0,
|
|
19118
|
+
l: Number(n & U32_MASK64) | 0
|
|
19119
|
+
};
|
|
19120
|
+
}
|
|
19121
|
+
function split(lst, le = false) {
|
|
19122
|
+
const len = lst.length;
|
|
19123
|
+
let Ah = new Uint32Array(len);
|
|
19124
|
+
let Al = new Uint32Array(len);
|
|
19125
|
+
for (let i = 0; i < len; i++) {
|
|
19126
|
+
const { h, l } = fromBig(lst[i], le);
|
|
19127
|
+
[Ah[i], Al[i]] = [h, l];
|
|
19128
|
+
}
|
|
19129
|
+
return [Ah, Al];
|
|
19130
|
+
}
|
|
19131
|
+
const shrSH = (h, _l, s) => h >>> s;
|
|
19132
|
+
const shrSL = (h, l, s) => h << 32 - s | l >>> s;
|
|
19133
|
+
const rotrSH = (h, l, s) => h >>> s | l << 32 - s;
|
|
19134
|
+
const rotrSL = (h, l, s) => h << 32 - s | l >>> s;
|
|
19135
|
+
const rotrBH = (h, l, s) => h << 64 - s | l >>> s - 32;
|
|
19136
|
+
const rotrBL = (h, l, s) => h >>> s - 32 | l << 64 - s;
|
|
19137
|
+
function add(Ah, Al, Bh, Bl) {
|
|
19138
|
+
const l = (Al >>> 0) + (Bl >>> 0);
|
|
19139
|
+
return {
|
|
19140
|
+
h: Ah + Bh + (l / 2 ** 32 | 0) | 0,
|
|
19141
|
+
l: l | 0
|
|
19142
|
+
};
|
|
19143
|
+
}
|
|
19144
|
+
const add3L = (Al, Bl, Cl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0);
|
|
19145
|
+
const add3H = (low, Ah, Bh, Ch) => Ah + Bh + Ch + (low / 2 ** 32 | 0) | 0;
|
|
19146
|
+
const add4L = (Al, Bl, Cl, Dl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0);
|
|
19147
|
+
const add4H = (low, Ah, Bh, Ch, Dh) => Ah + Bh + Ch + Dh + (low / 2 ** 32 | 0) | 0;
|
|
19148
|
+
const add5L = (Al, Bl, Cl, Dl, El) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0) + (El >>> 0);
|
|
19149
|
+
const add5H = (low, Ah, Bh, Ch, Dh, Eh) => Ah + Bh + Ch + Dh + Eh + (low / 2 ** 32 | 0) | 0;
|
|
19150
|
+
|
|
19151
|
+
//#endregion
|
|
19152
|
+
//#region node_modules/@noble/hashes/esm/sha2.js
|
|
19153
|
+
/**
|
|
19154
|
+
* SHA2 hash function. A.k.a. sha256, sha384, sha512, sha512_224, sha512_256.
|
|
19155
|
+
* SHA256 is the fastest hash implementable in JS, even faster than Blake3.
|
|
19156
|
+
* Check out [RFC 4634](https://datatracker.ietf.org/doc/html/rfc4634) and
|
|
19157
|
+
* [FIPS 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf).
|
|
19158
|
+
* @module
|
|
19159
|
+
*/
|
|
19160
|
+
const K512 = split([
|
|
19161
|
+
"0x428a2f98d728ae22",
|
|
19162
|
+
"0x7137449123ef65cd",
|
|
19163
|
+
"0xb5c0fbcfec4d3b2f",
|
|
19164
|
+
"0xe9b5dba58189dbbc",
|
|
19165
|
+
"0x3956c25bf348b538",
|
|
19166
|
+
"0x59f111f1b605d019",
|
|
19167
|
+
"0x923f82a4af194f9b",
|
|
19168
|
+
"0xab1c5ed5da6d8118",
|
|
19169
|
+
"0xd807aa98a3030242",
|
|
19170
|
+
"0x12835b0145706fbe",
|
|
19171
|
+
"0x243185be4ee4b28c",
|
|
19172
|
+
"0x550c7dc3d5ffb4e2",
|
|
19173
|
+
"0x72be5d74f27b896f",
|
|
19174
|
+
"0x80deb1fe3b1696b1",
|
|
19175
|
+
"0x9bdc06a725c71235",
|
|
19176
|
+
"0xc19bf174cf692694",
|
|
19177
|
+
"0xe49b69c19ef14ad2",
|
|
19178
|
+
"0xefbe4786384f25e3",
|
|
19179
|
+
"0x0fc19dc68b8cd5b5",
|
|
19180
|
+
"0x240ca1cc77ac9c65",
|
|
19181
|
+
"0x2de92c6f592b0275",
|
|
19182
|
+
"0x4a7484aa6ea6e483",
|
|
19183
|
+
"0x5cb0a9dcbd41fbd4",
|
|
19184
|
+
"0x76f988da831153b5",
|
|
19185
|
+
"0x983e5152ee66dfab",
|
|
19186
|
+
"0xa831c66d2db43210",
|
|
19187
|
+
"0xb00327c898fb213f",
|
|
19188
|
+
"0xbf597fc7beef0ee4",
|
|
19189
|
+
"0xc6e00bf33da88fc2",
|
|
19190
|
+
"0xd5a79147930aa725",
|
|
19191
|
+
"0x06ca6351e003826f",
|
|
19192
|
+
"0x142929670a0e6e70",
|
|
19193
|
+
"0x27b70a8546d22ffc",
|
|
19194
|
+
"0x2e1b21385c26c926",
|
|
19195
|
+
"0x4d2c6dfc5ac42aed",
|
|
19196
|
+
"0x53380d139d95b3df",
|
|
19197
|
+
"0x650a73548baf63de",
|
|
19198
|
+
"0x766a0abb3c77b2a8",
|
|
19199
|
+
"0x81c2c92e47edaee6",
|
|
19200
|
+
"0x92722c851482353b",
|
|
19201
|
+
"0xa2bfe8a14cf10364",
|
|
19202
|
+
"0xa81a664bbc423001",
|
|
19203
|
+
"0xc24b8b70d0f89791",
|
|
19204
|
+
"0xc76c51a30654be30",
|
|
19205
|
+
"0xd192e819d6ef5218",
|
|
19206
|
+
"0xd69906245565a910",
|
|
19207
|
+
"0xf40e35855771202a",
|
|
19208
|
+
"0x106aa07032bbd1b8",
|
|
19209
|
+
"0x19a4c116b8d2d0c8",
|
|
19210
|
+
"0x1e376c085141ab53",
|
|
19211
|
+
"0x2748774cdf8eeb99",
|
|
19212
|
+
"0x34b0bcb5e19b48a8",
|
|
19213
|
+
"0x391c0cb3c5c95a63",
|
|
19214
|
+
"0x4ed8aa4ae3418acb",
|
|
19215
|
+
"0x5b9cca4f7763e373",
|
|
19216
|
+
"0x682e6ff3d6b2b8a3",
|
|
19217
|
+
"0x748f82ee5defb2fc",
|
|
19218
|
+
"0x78a5636f43172f60",
|
|
19219
|
+
"0x84c87814a1f0ab72",
|
|
19220
|
+
"0x8cc702081a6439ec",
|
|
19221
|
+
"0x90befffa23631e28",
|
|
19222
|
+
"0xa4506cebde82bde9",
|
|
19223
|
+
"0xbef9a3f7b2c67915",
|
|
19224
|
+
"0xc67178f2e372532b",
|
|
19225
|
+
"0xca273eceea26619c",
|
|
19226
|
+
"0xd186b8c721c0c207",
|
|
19227
|
+
"0xeada7dd6cde0eb1e",
|
|
19228
|
+
"0xf57d4f7fee6ed178",
|
|
19229
|
+
"0x06f067aa72176fba",
|
|
19230
|
+
"0x0a637dc5a2c898a6",
|
|
19231
|
+
"0x113f9804bef90dae",
|
|
19232
|
+
"0x1b710b35131c471b",
|
|
19233
|
+
"0x28db77f523047d84",
|
|
19234
|
+
"0x32caab7b40c72493",
|
|
19235
|
+
"0x3c9ebe0a15c9bebc",
|
|
19236
|
+
"0x431d67c49c100d4c",
|
|
19237
|
+
"0x4cc5d4becb3e42b6",
|
|
19238
|
+
"0x597f299cfc657e2a",
|
|
19239
|
+
"0x5fcb6fab3ad6faec",
|
|
19240
|
+
"0x6c44198c4a475817"
|
|
19241
|
+
].map((n) => BigInt(n)));
|
|
19242
|
+
const SHA512_Kh = K512[0];
|
|
19243
|
+
const SHA512_Kl = K512[1];
|
|
19244
|
+
const SHA512_W_H = /* @__PURE__ */ new Uint32Array(80);
|
|
19245
|
+
const SHA512_W_L = /* @__PURE__ */ new Uint32Array(80);
|
|
19246
|
+
var SHA512 = class extends HashMD {
|
|
19247
|
+
constructor(outputLen = 64) {
|
|
19248
|
+
super(128, outputLen, 16, false);
|
|
19249
|
+
this.Ah = SHA512_IV[0] | 0;
|
|
19250
|
+
this.Al = SHA512_IV[1] | 0;
|
|
19251
|
+
this.Bh = SHA512_IV[2] | 0;
|
|
19252
|
+
this.Bl = SHA512_IV[3] | 0;
|
|
19253
|
+
this.Ch = SHA512_IV[4] | 0;
|
|
19254
|
+
this.Cl = SHA512_IV[5] | 0;
|
|
19255
|
+
this.Dh = SHA512_IV[6] | 0;
|
|
19256
|
+
this.Dl = SHA512_IV[7] | 0;
|
|
19257
|
+
this.Eh = SHA512_IV[8] | 0;
|
|
19258
|
+
this.El = SHA512_IV[9] | 0;
|
|
19259
|
+
this.Fh = SHA512_IV[10] | 0;
|
|
19260
|
+
this.Fl = SHA512_IV[11] | 0;
|
|
19261
|
+
this.Gh = SHA512_IV[12] | 0;
|
|
19262
|
+
this.Gl = SHA512_IV[13] | 0;
|
|
19263
|
+
this.Hh = SHA512_IV[14] | 0;
|
|
19264
|
+
this.Hl = SHA512_IV[15] | 0;
|
|
19265
|
+
}
|
|
19266
|
+
get() {
|
|
19267
|
+
const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;
|
|
19268
|
+
return [
|
|
19269
|
+
Ah,
|
|
19270
|
+
Al,
|
|
19271
|
+
Bh,
|
|
19272
|
+
Bl,
|
|
19273
|
+
Ch,
|
|
19274
|
+
Cl,
|
|
19275
|
+
Dh,
|
|
19276
|
+
Dl,
|
|
19277
|
+
Eh,
|
|
19278
|
+
El,
|
|
19279
|
+
Fh,
|
|
19280
|
+
Fl,
|
|
19281
|
+
Gh,
|
|
19282
|
+
Gl,
|
|
19283
|
+
Hh,
|
|
19284
|
+
Hl
|
|
19285
|
+
];
|
|
19286
|
+
}
|
|
19287
|
+
set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl) {
|
|
19288
|
+
this.Ah = Ah | 0;
|
|
19289
|
+
this.Al = Al | 0;
|
|
19290
|
+
this.Bh = Bh | 0;
|
|
19291
|
+
this.Bl = Bl | 0;
|
|
19292
|
+
this.Ch = Ch | 0;
|
|
19293
|
+
this.Cl = Cl | 0;
|
|
19294
|
+
this.Dh = Dh | 0;
|
|
19295
|
+
this.Dl = Dl | 0;
|
|
19296
|
+
this.Eh = Eh | 0;
|
|
19297
|
+
this.El = El | 0;
|
|
19298
|
+
this.Fh = Fh | 0;
|
|
19299
|
+
this.Fl = Fl | 0;
|
|
19300
|
+
this.Gh = Gh | 0;
|
|
19301
|
+
this.Gl = Gl | 0;
|
|
19302
|
+
this.Hh = Hh | 0;
|
|
19303
|
+
this.Hl = Hl | 0;
|
|
19304
|
+
}
|
|
19305
|
+
process(view, offset) {
|
|
19306
|
+
for (let i = 0; i < 16; i++, offset += 4) {
|
|
19307
|
+
SHA512_W_H[i] = view.getUint32(offset);
|
|
19308
|
+
SHA512_W_L[i] = view.getUint32(offset += 4);
|
|
19309
|
+
}
|
|
19310
|
+
for (let i = 16; i < 80; i++) {
|
|
19311
|
+
const W15h = SHA512_W_H[i - 15] | 0;
|
|
19312
|
+
const W15l = SHA512_W_L[i - 15] | 0;
|
|
19313
|
+
const s0h = rotrSH(W15h, W15l, 1) ^ rotrSH(W15h, W15l, 8) ^ shrSH(W15h, W15l, 7);
|
|
19314
|
+
const s0l = rotrSL(W15h, W15l, 1) ^ rotrSL(W15h, W15l, 8) ^ shrSL(W15h, W15l, 7);
|
|
19315
|
+
const W2h = SHA512_W_H[i - 2] | 0;
|
|
19316
|
+
const W2l = SHA512_W_L[i - 2] | 0;
|
|
19317
|
+
const s1h = rotrSH(W2h, W2l, 19) ^ rotrBH(W2h, W2l, 61) ^ shrSH(W2h, W2l, 6);
|
|
19318
|
+
const s1l = rotrSL(W2h, W2l, 19) ^ rotrBL(W2h, W2l, 61) ^ shrSL(W2h, W2l, 6);
|
|
19319
|
+
const SUMl = add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]);
|
|
19320
|
+
SHA512_W_H[i] = add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]) | 0;
|
|
19321
|
+
SHA512_W_L[i] = SUMl | 0;
|
|
19322
|
+
}
|
|
19323
|
+
let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;
|
|
19324
|
+
for (let i = 0; i < 80; i++) {
|
|
19325
|
+
const sigma1h = rotrSH(Eh, El, 14) ^ rotrSH(Eh, El, 18) ^ rotrBH(Eh, El, 41);
|
|
19326
|
+
const sigma1l = rotrSL(Eh, El, 14) ^ rotrSL(Eh, El, 18) ^ rotrBL(Eh, El, 41);
|
|
19327
|
+
const CHIh = Eh & Fh ^ ~Eh & Gh;
|
|
19328
|
+
const CHIl = El & Fl ^ ~El & Gl;
|
|
19329
|
+
const T1ll = add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]);
|
|
19330
|
+
const T1h = add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]);
|
|
19331
|
+
const T1l = T1ll | 0;
|
|
19332
|
+
const sigma0h = rotrSH(Ah, Al, 28) ^ rotrBH(Ah, Al, 34) ^ rotrBH(Ah, Al, 39);
|
|
19333
|
+
const sigma0l = rotrSL(Ah, Al, 28) ^ rotrBL(Ah, Al, 34) ^ rotrBL(Ah, Al, 39);
|
|
19334
|
+
const MAJh = Ah & Bh ^ Ah & Ch ^ Bh & Ch;
|
|
19335
|
+
const MAJl = Al & Bl ^ Al & Cl ^ Bl & Cl;
|
|
19336
|
+
Hh = Gh | 0;
|
|
19337
|
+
Hl = Gl | 0;
|
|
19338
|
+
Gh = Fh | 0;
|
|
19339
|
+
Gl = Fl | 0;
|
|
19340
|
+
Fh = Eh | 0;
|
|
19341
|
+
Fl = El | 0;
|
|
19342
|
+
({h: Eh, l: El} = add(Dh | 0, Dl | 0, T1h | 0, T1l | 0));
|
|
19343
|
+
Dh = Ch | 0;
|
|
19344
|
+
Dl = Cl | 0;
|
|
19345
|
+
Ch = Bh | 0;
|
|
19346
|
+
Cl = Bl | 0;
|
|
19347
|
+
Bh = Ah | 0;
|
|
19348
|
+
Bl = Al | 0;
|
|
19349
|
+
const All = add3L(T1l, sigma0l, MAJl);
|
|
19350
|
+
Ah = add3H(All, T1h, sigma0h, MAJh);
|
|
19351
|
+
Al = All | 0;
|
|
19352
|
+
}
|
|
19353
|
+
({h: Ah, l: Al} = add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0));
|
|
19354
|
+
({h: Bh, l: Bl} = add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0));
|
|
19355
|
+
({h: Ch, l: Cl} = add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0));
|
|
19356
|
+
({h: Dh, l: Dl} = add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0));
|
|
19357
|
+
({h: Eh, l: El} = add(this.Eh | 0, this.El | 0, Eh | 0, El | 0));
|
|
19358
|
+
({h: Fh, l: Fl} = add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0));
|
|
19359
|
+
({h: Gh, l: Gl} = add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0));
|
|
19360
|
+
({h: Hh, l: Hl} = add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0));
|
|
19361
|
+
this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl);
|
|
19362
|
+
}
|
|
19363
|
+
roundClean() {
|
|
19364
|
+
clean(SHA512_W_H, SHA512_W_L);
|
|
19365
|
+
}
|
|
19366
|
+
destroy() {
|
|
19367
|
+
clean(this.buffer);
|
|
19368
|
+
this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
19369
|
+
}
|
|
19370
|
+
};
|
|
19371
|
+
/** SHA2-512 hash function from RFC 4634. */
|
|
19372
|
+
const sha512 = /* @__PURE__ */ createHasher(() => new SHA512());
|
|
19373
|
+
|
|
19374
|
+
//#endregion
|
|
19375
|
+
//#region packages/mcp/src/crypto.ts
|
|
19376
|
+
/**
|
|
19377
|
+
* Ed25519 key generation, persistence, and challenge signing for MCP agent auth.
|
|
19378
|
+
*/
|
|
19379
|
+
etc.sha512Sync = (...msgs) => sha512(etc.concatBytes(...msgs));
|
|
19380
|
+
const DEFAULT_KEY_PATH = join(homedir(), ".abracadabra", "agent.key");
|
|
19381
|
+
function toBase64url(bytes) {
|
|
19382
|
+
return Buffer.from(bytes).toString("base64url");
|
|
19383
|
+
}
|
|
19384
|
+
function fromBase64url(b64) {
|
|
19385
|
+
return new Uint8Array(Buffer.from(b64, "base64url"));
|
|
19386
|
+
}
|
|
19387
|
+
/**
|
|
19388
|
+
* Load an existing Ed25519 keypair from disk, or generate and persist a new one.
|
|
19389
|
+
* The file stores the raw 32-byte private key seed.
|
|
19390
|
+
*/
|
|
19391
|
+
async function loadOrCreateKeypair(keyPath) {
|
|
19392
|
+
const path = keyPath || DEFAULT_KEY_PATH;
|
|
19393
|
+
if (existsSync(path)) {
|
|
19394
|
+
const seed = await readFile(path);
|
|
19395
|
+
if (seed.length !== 32) throw new Error(`Invalid key file at ${path}: expected 32 bytes, got ${seed.length}`);
|
|
19396
|
+
const privateKey = new Uint8Array(seed);
|
|
19397
|
+
return {
|
|
19398
|
+
privateKey,
|
|
19399
|
+
publicKeyB64: toBase64url(getPublicKey(privateKey))
|
|
19400
|
+
};
|
|
19401
|
+
}
|
|
19402
|
+
const privateKey = utils.randomPrivateKey();
|
|
19403
|
+
const publicKey = getPublicKey(privateKey);
|
|
19404
|
+
const dir = dirname(path);
|
|
19405
|
+
if (!existsSync(dir)) await mkdir(dir, {
|
|
19406
|
+
recursive: true,
|
|
19407
|
+
mode: 448
|
|
19408
|
+
});
|
|
19409
|
+
await writeFile(path, Buffer.from(privateKey), { mode: 384 });
|
|
19410
|
+
console.error(`[abracadabra-mcp] Generated new agent keypair at ${path}`);
|
|
19411
|
+
console.error(`[abracadabra-mcp] Public key: ${toBase64url(publicKey)}`);
|
|
19412
|
+
return {
|
|
19413
|
+
privateKey,
|
|
19414
|
+
publicKeyB64: toBase64url(publicKey)
|
|
19415
|
+
};
|
|
19416
|
+
}
|
|
19417
|
+
/** Sign a base64url challenge with the agent's private key; returns base64url signature. */
|
|
19418
|
+
function signChallenge(challengeB64, privateKey) {
|
|
19419
|
+
const challenge = fromBase64url(challengeB64);
|
|
19420
|
+
return toBase64url(sign(challenge, privateKey));
|
|
19421
|
+
}
|
|
19422
|
+
|
|
18433
19423
|
//#endregion
|
|
18434
19424
|
//#region packages/mcp/src/server.ts
|
|
18435
19425
|
/**
|
|
@@ -18483,20 +19473,28 @@ var AbracadabraMCPServer = class {
|
|
|
18483
19473
|
get userId() {
|
|
18484
19474
|
return this._userId;
|
|
18485
19475
|
}
|
|
18486
|
-
/** Start the server:
|
|
19476
|
+
/** Start the server: authenticate with Ed25519 key, discover spaces/root doc, connect provider. */
|
|
18487
19477
|
async connect() {
|
|
18488
|
-
await this.
|
|
18489
|
-
|
|
18490
|
-
|
|
18491
|
-
});
|
|
18492
|
-
console.error(`[abracadabra-mcp] Logged in as ${this.config.username}`);
|
|
19478
|
+
const keypair = await loadOrCreateKeypair(this.config.keyFile);
|
|
19479
|
+
this._userId = keypair.publicKeyB64;
|
|
19480
|
+
const signFn = (challenge) => Promise.resolve(signChallenge(challenge, keypair.privateKey));
|
|
18493
19481
|
try {
|
|
18494
|
-
|
|
18495
|
-
|
|
18496
|
-
|
|
18497
|
-
|
|
18498
|
-
|
|
19482
|
+
await this.client.loginWithKey(keypair.publicKeyB64, signFn);
|
|
19483
|
+
} catch (err) {
|
|
19484
|
+
const status = err?.status ?? err?.response?.status;
|
|
19485
|
+
if (status === 404 || status === 422) {
|
|
19486
|
+
console.error("[abracadabra-mcp] Key not registered, creating new account...");
|
|
19487
|
+
await this.client.registerWithKey({
|
|
19488
|
+
publicKey: keypair.publicKeyB64,
|
|
19489
|
+
username: this.agentName.replace(/\s+/g, "-").toLowerCase(),
|
|
19490
|
+
displayName: this.agentName,
|
|
19491
|
+
deviceName: "MCP Agent",
|
|
19492
|
+
inviteCode: this.config.inviteCode
|
|
19493
|
+
});
|
|
19494
|
+
await this.client.loginWithKey(keypair.publicKeyB64, signFn);
|
|
19495
|
+
} else throw err;
|
|
18499
19496
|
}
|
|
19497
|
+
console.error(`[abracadabra-mcp] Authenticated as ${this.agentName} (${keypair.publicKeyB64.slice(0, 12)}...)`);
|
|
18500
19498
|
this._serverInfo = await this.client.serverInfo();
|
|
18501
19499
|
let initialDocId = this._serverInfo.index_doc_id ?? null;
|
|
18502
19500
|
try {
|
|
@@ -18757,6 +19755,15 @@ var AbracadabraMCPServer = class {
|
|
|
18757
19755
|
|
|
18758
19756
|
//#endregion
|
|
18759
19757
|
//#region packages/mcp/src/tools/tree.ts
|
|
19758
|
+
/**
|
|
19759
|
+
* Normalize a document ID so the hub/root doc ID is treated as the tree root (null).
|
|
19760
|
+
* This lets callers pass the hub doc_id from list_spaces as parentId/rootId
|
|
19761
|
+
* and get the expected root-level results instead of an empty set.
|
|
19762
|
+
*/
|
|
19763
|
+
function normalizeRootId(id, server) {
|
|
19764
|
+
if (id == null) return null;
|
|
19765
|
+
return id === server.rootDocId ? null : id;
|
|
19766
|
+
}
|
|
18760
19767
|
function readEntries$1(treeMap) {
|
|
18761
19768
|
const entries = [];
|
|
18762
19769
|
treeMap.forEach((value, id) => {
|
|
@@ -18778,7 +19785,10 @@ function childrenOf$1(entries, parentId) {
|
|
|
18778
19785
|
}
|
|
18779
19786
|
function descendantsOf(entries, id) {
|
|
18780
19787
|
const result = [];
|
|
19788
|
+
const visited = /* @__PURE__ */ new Set();
|
|
18781
19789
|
function collect(pid) {
|
|
19790
|
+
if (visited.has(pid)) return;
|
|
19791
|
+
visited.add(pid);
|
|
18782
19792
|
for (const child of childrenOf$1(entries, pid)) {
|
|
18783
19793
|
result.push(child);
|
|
18784
19794
|
collect(child.id);
|
|
@@ -18787,32 +19797,36 @@ function descendantsOf(entries, id) {
|
|
|
18787
19797
|
collect(id);
|
|
18788
19798
|
return result;
|
|
18789
19799
|
}
|
|
18790
|
-
function buildTree$1(entries, rootId, maxDepth, currentDepth = 0) {
|
|
19800
|
+
function buildTree$1(entries, rootId, maxDepth, currentDepth = 0, visited = /* @__PURE__ */ new Set()) {
|
|
18791
19801
|
if (maxDepth >= 0 && currentDepth >= maxDepth) return [];
|
|
18792
|
-
return childrenOf$1(entries, rootId).map((entry) =>
|
|
18793
|
-
|
|
18794
|
-
|
|
18795
|
-
|
|
18796
|
-
|
|
18797
|
-
|
|
18798
|
-
|
|
18799
|
-
|
|
19802
|
+
return childrenOf$1(entries, rootId).filter((e) => !visited.has(e.id)).map((entry) => {
|
|
19803
|
+
const next = new Set(visited);
|
|
19804
|
+
next.add(entry.id);
|
|
19805
|
+
return {
|
|
19806
|
+
id: entry.id,
|
|
19807
|
+
label: entry.label,
|
|
19808
|
+
type: entry.type,
|
|
19809
|
+
meta: entry.meta,
|
|
19810
|
+
order: entry.order,
|
|
19811
|
+
children: buildTree$1(entries, entry.id, maxDepth, currentDepth + 1, next)
|
|
19812
|
+
};
|
|
19813
|
+
});
|
|
18800
19814
|
}
|
|
18801
19815
|
function registerTreeTools(mcp, server) {
|
|
18802
|
-
mcp.tool("list_documents", "List direct children of a document (defaults to root). Returns id, label, type, meta, order.", { parentId: z.string().optional().describe("Parent document ID. Omit for root-level documents.") }, async ({ parentId }) => {
|
|
19816
|
+
mcp.tool("list_documents", "List direct children of a document (defaults to root). Returns id, label, type, meta, order. NOTE: Only returns ONE level. Use find_document to search by name across the full tree, or get_document_tree to see the complete hierarchy.", { parentId: z.string().optional().describe("Parent document ID. Omit for root-level documents.") }, async ({ parentId }) => {
|
|
18803
19817
|
const treeMap = server.getTreeMap();
|
|
18804
19818
|
if (!treeMap) return { content: [{
|
|
18805
19819
|
type: "text",
|
|
18806
19820
|
text: "Not connected"
|
|
18807
19821
|
}] };
|
|
18808
|
-
const targetId = parentId
|
|
19822
|
+
const targetId = normalizeRootId(parentId, server);
|
|
18809
19823
|
const children = childrenOf$1(readEntries$1(treeMap), targetId);
|
|
18810
19824
|
return { content: [{
|
|
18811
19825
|
type: "text",
|
|
18812
19826
|
text: JSON.stringify(children, null, 2)
|
|
18813
19827
|
}] };
|
|
18814
19828
|
});
|
|
18815
|
-
mcp.tool("get_document_tree", "Get the full document tree as nested JSON.
|
|
19829
|
+
mcp.tool("get_document_tree", "Get the full document tree as nested JSON. Call this FIRST when exploring a space or looking for documents — it shows the complete hierarchy including all nested documents. Use rootId to scope to a subtree.", {
|
|
18816
19830
|
rootId: z.string().optional().describe("Root document ID to start from. Omit for the entire tree."),
|
|
18817
19831
|
depth: z.number().optional().describe("Maximum depth to traverse. Default 3. Use -1 for unlimited.")
|
|
18818
19832
|
}, async ({ rootId, depth }) => {
|
|
@@ -18821,7 +19835,7 @@ function registerTreeTools(mcp, server) {
|
|
|
18821
19835
|
type: "text",
|
|
18822
19836
|
text: "Not connected"
|
|
18823
19837
|
}] };
|
|
18824
|
-
const targetId = rootId
|
|
19838
|
+
const targetId = normalizeRootId(rootId, server);
|
|
18825
19839
|
const maxDepth = depth ?? 3;
|
|
18826
19840
|
const tree = buildTree$1(readEntries$1(treeMap), targetId, maxDepth);
|
|
18827
19841
|
return { content: [{
|
|
@@ -18829,10 +19843,52 @@ function registerTreeTools(mcp, server) {
|
|
|
18829
19843
|
text: JSON.stringify(tree, null, 2)
|
|
18830
19844
|
}] };
|
|
18831
19845
|
});
|
|
19846
|
+
mcp.tool("find_document", "Search for documents by name across the entire tree. Returns matching documents with their full ancestor path. Use this when you're looking for a specific document and don't know where it is in the hierarchy.", {
|
|
19847
|
+
query: z.string().describe("Search query — matched case-insensitively against document labels (substring match)."),
|
|
19848
|
+
rootId: z.string().optional().describe("Restrict search to descendants of this document. Omit to search the entire tree.")
|
|
19849
|
+
}, async ({ query, rootId }) => {
|
|
19850
|
+
const treeMap = server.getTreeMap();
|
|
19851
|
+
if (!treeMap) return { content: [{
|
|
19852
|
+
type: "text",
|
|
19853
|
+
text: "Not connected"
|
|
19854
|
+
}] };
|
|
19855
|
+
const entries = readEntries$1(treeMap);
|
|
19856
|
+
const lowerQuery = query.toLowerCase();
|
|
19857
|
+
const normalizedRoot = normalizeRootId(rootId, server);
|
|
19858
|
+
const matches = (normalizedRoot ? descendantsOf(entries, normalizedRoot) : entries).filter((e) => e.label.toLowerCase().includes(lowerQuery));
|
|
19859
|
+
const byId = new Map(entries.map((e) => [e.id, e]));
|
|
19860
|
+
const results = matches.map((entry) => {
|
|
19861
|
+
const path = [];
|
|
19862
|
+
let current = entry.parentId;
|
|
19863
|
+
const visited = /* @__PURE__ */ new Set();
|
|
19864
|
+
while (current && !visited.has(current)) {
|
|
19865
|
+
visited.add(current);
|
|
19866
|
+
const parent = byId.get(current);
|
|
19867
|
+
if (!parent) break;
|
|
19868
|
+
path.unshift(parent.label);
|
|
19869
|
+
current = parent.parentId;
|
|
19870
|
+
}
|
|
19871
|
+
return {
|
|
19872
|
+
id: entry.id,
|
|
19873
|
+
label: entry.label,
|
|
19874
|
+
type: entry.type,
|
|
19875
|
+
meta: entry.meta,
|
|
19876
|
+
path
|
|
19877
|
+
};
|
|
19878
|
+
});
|
|
19879
|
+
if (results.length === 0) return { content: [{
|
|
19880
|
+
type: "text",
|
|
19881
|
+
text: `No documents found matching "${query}". Try get_document_tree to see the full hierarchy.`
|
|
19882
|
+
}] };
|
|
19883
|
+
return { content: [{
|
|
19884
|
+
type: "text",
|
|
19885
|
+
text: JSON.stringify(results, null, 2)
|
|
19886
|
+
}] };
|
|
19887
|
+
});
|
|
18832
19888
|
mcp.tool("create_document", "Create a new document in the tree. Returns the new document ID.", {
|
|
18833
19889
|
parentId: z.string().optional().describe("Parent document ID. Omit for top-level pages. Use a document ID for nested/child pages."),
|
|
18834
19890
|
label: z.string().describe("Display name / title for the document."),
|
|
18835
|
-
type: z.string().optional().describe("Page type: \"doc\", \"kanban\", \"calendar\", \"table\", \"outline\", \"gallery\", \"slides\", \"timeline\", \"whiteboard\", \"map\", \"
|
|
19891
|
+
type: z.string().optional().describe("Page type: \"doc\", \"kanban\", \"calendar\", \"table\", \"outline\", \"gallery\", \"slides\", \"timeline\", \"whiteboard\", \"map\", \"dashboard\", \"mindmap\", \"graph\". Omit to inherit parent view."),
|
|
18836
19892
|
meta: z.record(z.unknown()).optional().describe("Initial metadata (PageMeta fields: color as hex string, icon as Lucide kebab-case name like \"star\"/\"code-2\"/\"users\" — never emoji, dateStart, dateEnd, priority 0-4, tags array, etc). Omit icon entirely to use page type default.")
|
|
18837
19893
|
}, async ({ parentId, label, type, meta }) => {
|
|
18838
19894
|
const treeMap = server.getTreeMap();
|
|
@@ -18842,11 +19898,12 @@ function registerTreeTools(mcp, server) {
|
|
|
18842
19898
|
text: "Not connected"
|
|
18843
19899
|
}] };
|
|
18844
19900
|
const id = crypto.randomUUID();
|
|
19901
|
+
const normalizedParent = normalizeRootId(parentId, server);
|
|
18845
19902
|
const now = Date.now();
|
|
18846
19903
|
rootDoc.transact(() => {
|
|
18847
19904
|
treeMap.set(id, {
|
|
18848
19905
|
label,
|
|
18849
|
-
parentId:
|
|
19906
|
+
parentId: normalizedParent,
|
|
18850
19907
|
order: now,
|
|
18851
19908
|
type,
|
|
18852
19909
|
meta,
|
|
@@ -18905,7 +19962,7 @@ function registerTreeTools(mcp, server) {
|
|
|
18905
19962
|
}] };
|
|
18906
19963
|
treeMap.set(id, {
|
|
18907
19964
|
...entry,
|
|
18908
|
-
parentId: newParentId
|
|
19965
|
+
parentId: normalizeRootId(newParentId, server),
|
|
18909
19966
|
order: order ?? Date.now(),
|
|
18910
19967
|
updatedAt: Date.now()
|
|
18911
19968
|
});
|
|
@@ -18946,7 +20003,7 @@ function registerTreeTools(mcp, server) {
|
|
|
18946
20003
|
});
|
|
18947
20004
|
mcp.tool("change_document_type", "Change the page type view of a document (data is preserved).", {
|
|
18948
20005
|
id: z.string().describe("Document ID."),
|
|
18949
|
-
type: z.string().describe("New page type (e.g. \"doc\", \"kanban\", \"table\", \"calendar\", \"outline\", \"gallery\", \"slides\", \"timeline\", \"whiteboard\", \"map\", \"
|
|
20006
|
+
type: z.string().describe("New page type (e.g. \"doc\", \"kanban\", \"table\", \"calendar\", \"outline\", \"gallery\", \"slides\", \"timeline\", \"whiteboard\", \"map\", \"dashboard\", \"mindmap\", \"graph\").")
|
|
18950
20007
|
}, async ({ id, type }) => {
|
|
18951
20008
|
const treeMap = server.getTreeMap();
|
|
18952
20009
|
if (!treeMap) return { content: [{
|
|
@@ -20932,36 +21989,63 @@ function registerServerInfoResource(mcp, server) {
|
|
|
20932
21989
|
* Abracadabra MCP Server — entry point.
|
|
20933
21990
|
*
|
|
20934
21991
|
* Environment variables:
|
|
20935
|
-
* ABRA_URL
|
|
20936
|
-
*
|
|
20937
|
-
*
|
|
20938
|
-
*
|
|
20939
|
-
*
|
|
21992
|
+
* ABRA_URL (required) — Server URL (e.g. http://localhost:1234)
|
|
21993
|
+
* ABRA_AGENT_NAME — Display name (default: "AI Assistant")
|
|
21994
|
+
* ABRA_AGENT_COLOR — HSL color for presence (default: "hsl(270, 80%, 60%)")
|
|
21995
|
+
* ABRA_INVITE_CODE — Invite code for first-run registration (grants role)
|
|
21996
|
+
* ABRA_KEY_FILE — Path to Ed25519 key file (default: ~/.abracadabra/agent.key)
|
|
20940
21997
|
*/
|
|
20941
21998
|
async function main() {
|
|
20942
21999
|
const url = process.env.ABRA_URL;
|
|
20943
|
-
|
|
20944
|
-
|
|
20945
|
-
if (!url || !username || !password) {
|
|
20946
|
-
console.error("Missing required environment variables: ABRA_URL, ABRA_USERNAME, ABRA_PASSWORD");
|
|
22000
|
+
if (!url) {
|
|
22001
|
+
console.error("Missing required environment variable: ABRA_URL");
|
|
20947
22002
|
process.exit(1);
|
|
20948
22003
|
}
|
|
20949
22004
|
const server = new AbracadabraMCPServer({
|
|
20950
22005
|
url,
|
|
20951
|
-
username,
|
|
20952
|
-
password,
|
|
20953
22006
|
agentName: process.env.ABRA_AGENT_NAME,
|
|
20954
|
-
agentColor: process.env.ABRA_AGENT_COLOR
|
|
22007
|
+
agentColor: process.env.ABRA_AGENT_COLOR,
|
|
22008
|
+
inviteCode: process.env.ABRA_INVITE_CODE,
|
|
22009
|
+
keyFile: process.env.ABRA_KEY_FILE
|
|
20955
22010
|
});
|
|
20956
22011
|
const mcp = new McpServer({
|
|
20957
22012
|
name: "abracadabra",
|
|
20958
22013
|
version: "1.0.0"
|
|
20959
22014
|
}, {
|
|
20960
22015
|
capabilities: { experimental: { "claude/channel": {} } },
|
|
20961
|
-
instructions: `
|
|
22016
|
+
instructions: `Abracadabra is a CRDT collaboration platform where everything is a document in a tree.
|
|
22017
|
+
|
|
22018
|
+
## Quick Start Workflow
|
|
22019
|
+
1. list_spaces → note the active space's doc_id (this is the hub/root document)
|
|
22020
|
+
2. get_document_tree(rootId: hubDocId) → see the FULL hierarchy before doing anything
|
|
22021
|
+
3. find_document(query) → search for a document by name if you know what you're looking for
|
|
22022
|
+
4. read_document / write_document / create_document → operate on specific documents
|
|
22023
|
+
|
|
22024
|
+
## Key Concepts
|
|
22025
|
+
- Documents form a tree. A kanban board's columns are child documents; cards are grandchildren.
|
|
22026
|
+
- A document's label IS its display name everywhere. Children ARE the content (not just the body text).
|
|
22027
|
+
- Page types (doc, kanban, table, calendar, timeline, outline, etc.) are views over the SAME tree — switching types preserves data.
|
|
22028
|
+
- An empty markdown body does NOT mean empty content — always check the children array.
|
|
22029
|
+
|
|
22030
|
+
## Finding Documents
|
|
22031
|
+
- list_documents only shows ONE level of children. If you don't find what you need, use find_document to search the entire tree by name, or get_document_tree to see the full hierarchy.
|
|
22032
|
+
- NEVER conclude a document doesn't exist after only checking root-level documents.
|
|
22033
|
+
|
|
22034
|
+
## Rules
|
|
22035
|
+
- Always call list_spaces first to get the hub doc ID before creating documents.
|
|
22036
|
+
- Always call get_document_tree to understand existing structure before creating documents.
|
|
22037
|
+
- Use Lucide icon names in kebab-case (e.g. "star", "code-2") for meta.icon — NEVER emoji.
|
|
22038
|
+
- Never rename or write content to the hub document itself.
|
|
22039
|
+
- Use universal meta keys (color, icon, dateStart) — never prefix with page type names.
|
|
22040
|
+
|
|
22041
|
+
## Channel Events
|
|
22042
|
+
Events from the abracadabra channel arrive as <channel source="abracadabra" ...>. They may be:
|
|
20962
22043
|
- ai:task events from human users (includes sender, doc_id context)
|
|
20963
22044
|
- chat messages from the platform chat system (includes channel, sender, sender_id)
|
|
20964
|
-
When you receive a channel event, read it, do the work using your tools, and reply using the reply tool (for document responses) or send_chat_message (for chat responses)
|
|
22045
|
+
When you receive a channel event, read it, do the work using your tools, and reply using the reply tool (for document responses) or send_chat_message (for chat responses).
|
|
22046
|
+
|
|
22047
|
+
## Full Reference
|
|
22048
|
+
Read the resource at abracadabra://agent-guide for the complete guide covering page type schemas, metadata reference, awareness/presence, content structure, and detailed examples.`
|
|
20965
22049
|
});
|
|
20966
22050
|
registerTreeTools(mcp, server);
|
|
20967
22051
|
registerContentTools(mcp, server);
|