@truealter/sdk 0.5.0 → 0.5.3

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/index.cjs CHANGED
@@ -4,13 +4,14 @@ var p256 = require('@noble/curves/p256');
4
4
  var sha256 = require('@noble/hashes/sha256');
5
5
  var utils = require('@noble/hashes/utils');
6
6
  var crypto$1 = require('crypto');
7
+ var fs = require('fs');
8
+ var os = require('os');
9
+ var path = require('path');
7
10
  var ed25519 = require('@noble/ed25519');
8
11
  var sha512 = require('@noble/hashes/sha512');
12
+ var url = require('url');
9
13
  var child_process = require('child_process');
10
- var os = require('os');
11
- var path = require('path');
12
14
  var process$1 = require('process');
13
- var fs = require('fs');
14
15
 
15
16
  function _interopNamespace(e) {
16
17
  if (e && e.__esModule) return e;
@@ -54,8 +55,11 @@ var __copyProps = (to, from, except, desc) => {
54
55
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
55
56
 
56
57
  // node_modules/tsup/assets/cjs_shims.js
58
+ var getImportMetaUrl, importMetaUrl;
57
59
  var init_cjs_shims = __esm({
58
60
  "node_modules/tsup/assets/cjs_shims.js"() {
61
+ getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
62
+ importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
59
63
  }
60
64
  });
61
65
 
@@ -402,11 +406,11 @@ async function tryWellKnown(host, file, timeoutMs, fetchImpl) {
402
406
  }
403
407
  if (resp.type === "opaqueredirect" || resp.status >= 300 && resp.status < 400) {
404
408
  throw new AlterNetworkError(
405
- `${url} \u2192 redirect rejected (discovery must not follow redirects; validate the server configuration)`
409
+ `${url} -> redirect rejected (discovery must not follow redirects; validate the server configuration)`
406
410
  );
407
411
  }
408
412
  if (resp.status === 404) return null;
409
- if (!resp.ok) throw new AlterNetworkError(`${url} \u2192 HTTP ${resp.status}`);
413
+ if (!resp.ok) throw new AlterNetworkError(`${url} -> HTTP ${resp.status}`);
410
414
  const doc = await resp.json();
411
415
  if (file === "mcp.json") {
412
416
  const remotes = doc.remotes || [];
@@ -440,6 +444,313 @@ function ensureMcpPath(url) {
440
444
  }
441
445
  }
442
446
 
447
+ // src/floor-preflight.ts
448
+ init_cjs_shims();
449
+
450
+ // src/meta.ts
451
+ init_cjs_shims();
452
+ var SDK_NAME = "@truealter/sdk";
453
+ var SDK_VERSION = "0.5.3" ;
454
+
455
+ // src/floor-preflight.ts
456
+ var MIN_VERSION_ENDPOINT = "/v1/clients/min-version";
457
+ var CLIENT_ID = "alter-identity";
458
+ var CLIENT_CHANNEL = "npm";
459
+ var IN_MEMORY_TTL_DEFAULT_MS = 60 * 60 * 1e3;
460
+ var IN_MEMORY_TTL_MIN_MS = 60 * 1e3;
461
+ var IN_MEMORY_TTL_MAX_MS = 24 * 60 * 60 * 1e3;
462
+ var DISK_FRESH_MS = 24 * 60 * 60 * 1e3;
463
+ var DISK_WARN_MS = 7 * 24 * 60 * 60 * 1e3;
464
+ var FETCH_TIMEOUT_MS = 4e3;
465
+ function computeKeyId(publicKeyPem) {
466
+ if (!publicKeyPem) return "00000000";
467
+ const pub = crypto$1.createPublicKey({ key: publicKeyPem, format: "pem" });
468
+ const jwk = pub.export({ format: "jwk" });
469
+ const rawBytes = Buffer.from(jwk.x, "base64url");
470
+ return crypto$1.createHash("sha256").update(rawBytes).digest("hex").slice(0, 8);
471
+ }
472
+ function canonicalJson(obj) {
473
+ return JSON.stringify(sortKeysDeep(obj));
474
+ }
475
+ function sortKeysDeep(value) {
476
+ if (Array.isArray(value)) {
477
+ return value.map(sortKeysDeep);
478
+ }
479
+ if (value !== null && typeof value === "object") {
480
+ const obj = value;
481
+ const sorted = {};
482
+ for (const k of Object.keys(obj).sort()) {
483
+ sorted[k] = sortKeysDeep(obj[k]);
484
+ }
485
+ return sorted;
486
+ }
487
+ return value;
488
+ }
489
+ var KNOWN_FLOOR_PUBLIC_KEYS = {
490
+ "8aa59e05": `-----BEGIN PUBLIC KEY-----
491
+ MCowBQYDK2VwAyEAgqw28dlniOuiTE1f4BxCPSEgMLaPtHsO8wN5RWEwEhE=
492
+ -----END PUBLIC KEY-----`,
493
+ "640f7d9a": `-----BEGIN PUBLIC KEY-----
494
+ MCowBQYDK2VwAyEARzvAWayDwHvZRfOZizGZe+/a7PF082WGhyMS3tx06H4=
495
+ -----END PUBLIC KEY-----`
496
+ };
497
+ var BelowFloorError = class extends Error {
498
+ name = "BelowFloorError";
499
+ code = "client_below_floor";
500
+ client_version;
501
+ min_version;
502
+ upgrade_cmd;
503
+ channel;
504
+ envelope;
505
+ constructor(envelope) {
506
+ super(envelope.error.message);
507
+ this.envelope = envelope;
508
+ this.client_version = envelope.error.client_version;
509
+ this.min_version = envelope.error.min_version;
510
+ this.upgrade_cmd = envelope.error.upgrade_cmd;
511
+ this.channel = envelope.error.channel;
512
+ Object.setPrototypeOf(this, new.target.prototype);
513
+ }
514
+ };
515
+ var memCache = null;
516
+ async function checkMinVersion(opts = {}) {
517
+ const apiBase = opts.apiBase ?? defaultApiBase();
518
+ const clientVersion = opts.clientVersion ?? SDK_VERSION;
519
+ const clientId = opts.clientId ?? CLIENT_ID;
520
+ const channel = opts.channel ?? CLIENT_CHANNEL;
521
+ const knownKeys = opts.knownFloorPublicKeys ?? KNOWN_FLOOR_PUBLIC_KEYS;
522
+ const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
523
+ const now = opts.now ?? Date.now;
524
+ const cachePath = opts.diskCachePath === void 0 ? defaultDiskCachePath() : opts.diskCachePath;
525
+ const mem = readInMemoryCache(now);
526
+ if (mem) {
527
+ return compareAndPermit(mem, {
528
+ clientVersion,
529
+ clientId,
530
+ channel,
531
+ diagnostic: "mem-cache-hit"
532
+ });
533
+ }
534
+ const disk = cachePath ? readDiskCache(cachePath, knownKeys) : null;
535
+ const diskAgeMs = disk ? now() - disk.fetched_at_ms : Number.POSITIVE_INFINITY;
536
+ let fetched = null;
537
+ let fetchError = null;
538
+ if (!disk || diskAgeMs > IN_MEMORY_TTL_DEFAULT_MS) {
539
+ try {
540
+ fetched = await fetchFloorDoc(apiBase, fetchImpl, knownKeys);
541
+ } catch (err) {
542
+ fetchError = err.message ?? "fetch-error";
543
+ }
544
+ }
545
+ if (fetched) {
546
+ populateMemCache(fetched, now());
547
+ if (cachePath) writeDiskCache(cachePath, fetched, now());
548
+ return compareAndPermit(fetched, {
549
+ clientVersion,
550
+ clientId,
551
+ channel,
552
+ diagnostic: "fetched"
553
+ });
554
+ }
555
+ if (disk) {
556
+ populateMemCache(disk.doc, disk.fetched_at_ms);
557
+ if (diskAgeMs > DISK_WARN_MS) {
558
+ return compareAndPermit(disk.doc, {
559
+ clientVersion,
560
+ clientId,
561
+ channel,
562
+ diagnostic: "below-floor-offline-stale-or-permit",
563
+ warn: `floor cache is >7d old and backend unreachable (${fetchError ?? "no refresh attempted"}); permitting if above floor`
564
+ });
565
+ }
566
+ if (diskAgeMs > DISK_FRESH_MS) {
567
+ return compareAndPermit(disk.doc, {
568
+ clientVersion,
569
+ clientId,
570
+ channel,
571
+ diagnostic: "warn-stale-permit",
572
+ warn: `floor cache is ${Math.round(diskAgeMs / (60 * 60 * 1e3))}h old; refresh recommended`
573
+ });
574
+ }
575
+ return compareAndPermit(disk.doc, {
576
+ clientVersion,
577
+ clientId,
578
+ channel,
579
+ diagnostic: "disk-cache-hit"
580
+ });
581
+ }
582
+ return {
583
+ ok: true,
584
+ floor: null,
585
+ diagnostic: "no-cache-no-fetch-permit",
586
+ warn: `floor preflight skipped: backend unreachable (${fetchError ?? "unknown"})`
587
+ };
588
+ }
589
+ function compareAndPermit(doc, ctx) {
590
+ const floor = lookupFloor(doc, ctx.clientId, ctx.channel);
591
+ if (!floor) {
592
+ return { ok: true, floor: null, diagnostic: `${ctx.diagnostic}+no-floor`, warn: ctx.warn };
593
+ }
594
+ if (compareSemver(ctx.clientVersion, floor.min_version) >= 0) {
595
+ return { ok: true, floor, diagnostic: ctx.diagnostic, warn: ctx.warn };
596
+ }
597
+ const envelope = {
598
+ error: {
599
+ code: "client_below_floor",
600
+ message: `Your ${ctx.clientId} is too old. Upgrade required.`,
601
+ client_version: ctx.clientVersion,
602
+ min_version: floor.min_version,
603
+ upgrade_cmd: floor.upgrade_cmd,
604
+ channel: ctx.channel
605
+ }
606
+ };
607
+ throw new BelowFloorError(envelope);
608
+ }
609
+ function lookupFloor(doc, clientId, channel) {
610
+ const entry = doc.floors[clientId];
611
+ if (!entry) return null;
612
+ if (isChannelFloor(entry)) return entry;
613
+ const exact = entry[channel];
614
+ if (exact) return exact;
615
+ const fallback = entry["unknown"];
616
+ if (fallback) return fallback;
617
+ return null;
618
+ }
619
+ function isChannelFloor(v) {
620
+ return typeof v.min_version === "string" && typeof v.upgrade_cmd === "string";
621
+ }
622
+ function compareSemver(a, b) {
623
+ const [aMaj, aMin, aPat, aPre] = parseSemver(a);
624
+ const [bMaj, bMin, bPat, bPre] = parseSemver(b);
625
+ if (aMaj !== bMaj) return aMaj - bMaj;
626
+ if (aMin !== bMin) return aMin - bMin;
627
+ if (aPat !== bPat) return aPat - bPat;
628
+ if (aPre && !bPre) return -1;
629
+ if (!aPre && bPre) return 1;
630
+ if (aPre && bPre) return aPre.localeCompare(bPre);
631
+ return 0;
632
+ }
633
+ function parseSemver(v) {
634
+ const m = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?/.exec(v);
635
+ if (!m) return [0, 0, 0, null];
636
+ return [Number(m[1]), Number(m[2]), Number(m[3]), m[4] ?? null];
637
+ }
638
+ function verifyFloorSignature(doc, keys = KNOWN_FLOOR_PUBLIC_KEYS) {
639
+ const pem = keys[doc.key_id];
640
+ if (!pem) return false;
641
+ if (computeKeyId(pem) !== doc.key_id) return false;
642
+ try {
643
+ const pubKeyObject = crypto$1.createPublicKey({ key: pem, format: "pem" });
644
+ const canonical = canonicalJson({
645
+ floors: doc.floors,
646
+ served_at: doc.served_at
647
+ });
648
+ return crypto$1.verify(
649
+ null,
650
+ Buffer.from(canonical, "utf-8"),
651
+ pubKeyObject,
652
+ Buffer.from(doc.signature, "hex")
653
+ );
654
+ } catch {
655
+ return false;
656
+ }
657
+ }
658
+ async function fetchFloorDoc(apiBase, fetchImpl, knownKeys) {
659
+ const url = `${apiBase.replace(/\/+$/, "")}${MIN_VERSION_ENDPOINT}`;
660
+ let response;
661
+ try {
662
+ response = await fetchImpl(url, {
663
+ headers: {
664
+ accept: "application/json",
665
+ "X-Alter-Client-Id": CLIENT_ID,
666
+ "X-Alter-Client-Version": SDK_VERSION,
667
+ "X-Alter-Client-Channel": CLIENT_CHANNEL,
668
+ "User-Agent": `${SDK_NAME}/${SDK_VERSION}`
669
+ },
670
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
671
+ });
672
+ } catch (err) {
673
+ throw new Error(`network: ${err.message ?? String(err)}`);
674
+ }
675
+ if (!response.ok) throw new Error(`http-${response.status}`);
676
+ const body = await response.json();
677
+ if (!body || !body.floors || !body.signature || !body.key_id) {
678
+ throw new Error("malformed-floor-doc");
679
+ }
680
+ if (!verifyFloorSignature(body, knownKeys)) {
681
+ throw new Error("signature-invalid");
682
+ }
683
+ return body;
684
+ }
685
+ function readInMemoryCache(now) {
686
+ if (!memCache) return null;
687
+ if (now() - memCache.fetched_at_ms > memCache.ttl_ms) {
688
+ memCache = null;
689
+ return null;
690
+ }
691
+ return memCache.doc;
692
+ }
693
+ function populateMemCache(doc, fetched_at_ms) {
694
+ const ttlSec = doc.cache_ttl_seconds ?? 3600;
695
+ const ttlMs = Math.min(
696
+ Math.max(ttlSec * 1e3, IN_MEMORY_TTL_MIN_MS),
697
+ IN_MEMORY_TTL_MAX_MS
698
+ );
699
+ memCache = { doc, fetched_at_ms, ttl_ms: ttlMs };
700
+ }
701
+ function defaultDiskCachePath() {
702
+ const xdg = process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config");
703
+ return path.join(xdg, "alter", "floor-cache.json");
704
+ }
705
+ function readDiskCache(path, knownKeys) {
706
+ if (process.platform !== "win32") {
707
+ let st;
708
+ try {
709
+ st = fs.statSync(path);
710
+ } catch {
711
+ return null;
712
+ }
713
+ const euid = typeof process.geteuid === "function" ? process.geteuid() : st.uid;
714
+ if (st.uid !== euid) return null;
715
+ if ((st.mode & 511) !== 384) return null;
716
+ }
717
+ let raw;
718
+ try {
719
+ raw = fs.readFileSync(path, "utf-8");
720
+ } catch {
721
+ return null;
722
+ }
723
+ let parsed;
724
+ try {
725
+ parsed = JSON.parse(raw);
726
+ } catch {
727
+ return null;
728
+ }
729
+ if (!parsed.doc || typeof parsed.fetched_at_ms !== "number") return null;
730
+ if (!verifyFloorSignature(parsed.doc, knownKeys)) {
731
+ return null;
732
+ }
733
+ return parsed;
734
+ }
735
+ function writeDiskCache(path$1, doc, now_ms) {
736
+ const entry = { doc, fetched_at_ms: now_ms };
737
+ const payload = JSON.stringify(entry);
738
+ try {
739
+ fs.mkdirSync(path.dirname(path$1), { recursive: true, mode: 448 });
740
+ const tmp = `${path$1}.tmp`;
741
+ fs.writeFileSync(tmp, payload, { mode: 384 });
742
+ try {
743
+ fs.chmodSync(tmp, 384);
744
+ } catch {
745
+ }
746
+ fs.renameSync(tmp, path$1);
747
+ } catch {
748
+ }
749
+ }
750
+ function defaultApiBase() {
751
+ return process.env.ALTER_API ?? "https://api.truealter.com";
752
+ }
753
+
443
754
  // src/mcp.ts
444
755
  init_cjs_shims();
445
756
 
@@ -450,11 +761,19 @@ var X402Client = class {
450
761
  maxPerQuery;
451
762
  networks;
452
763
  assets;
764
+ // undefined = allowlist check disabled (backward-compatible default).
765
+ // Non-null = active allowlist; reject any recipient not in the set.
766
+ recipientAllowlist;
453
767
  constructor(opts = {}) {
454
768
  this.signer = opts.signer;
455
769
  this.maxPerQuery = opts.maxPerQuery !== void 0 ? Number(opts.maxPerQuery) : void 0;
456
770
  this.networks = new Set(opts.networks ?? ["base", "base-sepolia"]);
457
771
  this.assets = new Set(opts.assets ?? ["USDC"]);
772
+ if (opts.recipientAllowlist !== void 0) {
773
+ this.recipientAllowlist = opts.recipientAllowlist.length === 0 ? void 0 : new Set(opts.recipientAllowlist.map((a) => a.toLowerCase()));
774
+ } else {
775
+ this.recipientAllowlist = void 0;
776
+ }
458
777
  }
459
778
  /**
460
779
  * Validate the envelope against this client's policy and, if a signer
@@ -480,6 +799,15 @@ var X402Client = class {
480
799
  );
481
800
  }
482
801
  }
802
+ if (this.recipientAllowlist !== void 0) {
803
+ const recipientNorm = (envelope.recipient ?? "").toLowerCase();
804
+ if (!recipientNorm || !this.recipientAllowlist.has(recipientNorm)) {
805
+ throw new AlterError(
806
+ "PAYMENT_REQUIRED",
807
+ `recipient "${envelope.recipient}" is not on the known-recipient allowlist`
808
+ );
809
+ }
810
+ }
483
811
  if (!this.signer) {
484
812
  throw new AlterPaymentRequired(envelope.resource ?? "unknown", envelope);
485
813
  }
@@ -538,6 +866,9 @@ var MCPClient = class {
538
866
  x402;
539
867
  signing;
540
868
  extraHeaders;
869
+ preflightHook;
870
+ preflightPromise = null;
871
+ preflightDone = false;
541
872
  requestCounter = 0;
542
873
  initialised = false;
543
874
  constructor(opts = {}) {
@@ -546,17 +877,43 @@ var MCPClient = class {
546
877
  this.fetchImpl = opts.fetch ?? fetch;
547
878
  this.timeoutMs = opts.timeoutMs ?? 3e4;
548
879
  this.maxRetries = opts.maxRetries ?? 2;
549
- this.clientInfo = opts.clientInfo ?? { name: "@truealter/sdk", version: "0.2.0" };
880
+ this.clientInfo = opts.clientInfo ?? { name: SDK_NAME, version: SDK_VERSION };
550
881
  this.x402 = opts.x402;
551
882
  this.signing = opts.signing;
552
883
  this.extraHeaders = opts.extraHeaders;
884
+ this.preflightHook = opts.preflightHook;
885
+ }
886
+ /**
887
+ * Run the lazy preflight hook (D-MIN-VERSION-FLOOR-1) exactly once.
888
+ * Idempotent and serialised: concurrent callers share the same
889
+ * promise. Throws from the hook propagate to every concurrent caller.
890
+ */
891
+ async runPreflight() {
892
+ if (this.preflightDone) return;
893
+ if (!this.preflightHook) {
894
+ this.preflightDone = true;
895
+ return;
896
+ }
897
+ if (!this.preflightPromise) {
898
+ this.preflightPromise = this.preflightHook().then(
899
+ () => {
900
+ this.preflightDone = true;
901
+ },
902
+ (err) => {
903
+ this.preflightPromise = null;
904
+ throw err;
905
+ }
906
+ );
907
+ }
908
+ await this.preflightPromise;
553
909
  }
554
910
  /**
555
911
  * Send the MCP `initialize` handshake and capture the resulting session
556
- * id. Idempotent safe to call multiple times.
912
+ * id. Idempotent: safe to call multiple times.
557
913
  */
558
914
  async initialize() {
559
915
  if (this.initialised) return null;
916
+ await this.runPreflight();
560
917
  const result = await this.rpc("initialize", {
561
918
  protocolVersion: MCP_PROTOCOL_VERSION,
562
919
  capabilities: {},
@@ -716,7 +1073,10 @@ var MCPClient = class {
716
1073
  ...this.extraHeaders ?? {},
717
1074
  "Content-Type": "application/json",
718
1075
  Accept: "application/json",
719
- "User-Agent": `${this.clientInfo.name}/${this.clientInfo.version}`
1076
+ "User-Agent": `${this.clientInfo.name}/${this.clientInfo.version}`,
1077
+ "X-Alter-Client-Id": "alter-identity",
1078
+ "X-Alter-Client-Version": SDK_VERSION,
1079
+ "X-Alter-Client-Channel": "npm"
720
1080
  };
721
1081
  if (this.apiKey) headers["X-ALTER-API-Key"] = this.apiKey;
722
1082
  if (this.sessionId) headers["Mcp-Session-Id"] = this.sessionId;
@@ -952,9 +1312,46 @@ async function verifyProvenance(envelope, opts = {}) {
952
1312
  kid: header.kid
953
1313
  };
954
1314
  }
1315
+ if (opts.expectedAud !== void 0 && opts.expectedAud !== "") {
1316
+ const tokenAud = payload.aud;
1317
+ const audList = tokenAud === void 0 ? [] : Array.isArray(tokenAud) ? tokenAud : [tokenAud];
1318
+ if (!audList.includes(opts.expectedAud)) {
1319
+ return {
1320
+ valid: false,
1321
+ reason: `aud mismatch: expected "${opts.expectedAud}", got ${JSON.stringify(tokenAud ?? null)}`,
1322
+ payload,
1323
+ kid: header.kid
1324
+ };
1325
+ }
1326
+ }
955
1327
  return { valid: true, payload, kid: header.kid };
956
1328
  }
957
- async function verifyToolSignatures(tools, signatures) {
1329
+ async function verifyToolSignatures(tools, signatures, opts = {}) {
1330
+ const jwksUrl = opts.jwksUrl ?? "https://api.truealter.com/.well-known/alter-keys.json";
1331
+ const fetchImpl = opts.fetch ?? fetch;
1332
+ if (!jwksUrl.startsWith("https://")) {
1333
+ return tools.map((t) => ({
1334
+ tool: t.name,
1335
+ valid: false,
1336
+ reason: `jwksUrl must be https: got ${jwksUrl}`
1337
+ }));
1338
+ }
1339
+ const needsJwks = tools.some((t) => {
1340
+ const sig = signatures[t.name];
1341
+ return sig && sig.signature;
1342
+ });
1343
+ let jwks = null;
1344
+ if (needsJwks) {
1345
+ try {
1346
+ jwks = await fetchJwks(jwksUrl, fetchImpl);
1347
+ } catch (err) {
1348
+ return tools.map((t) => ({
1349
+ tool: t.name,
1350
+ valid: false,
1351
+ reason: `jwks fetch failed: ${err.message}`
1352
+ }));
1353
+ }
1354
+ }
958
1355
  const out = [];
959
1356
  for (const tool of tools) {
960
1357
  const sig = signatures[tool.name];
@@ -962,11 +1359,68 @@ async function verifyToolSignatures(tools, signatures) {
962
1359
  out.push({ tool: tool.name, valid: false, reason: "no signature published" });
963
1360
  continue;
964
1361
  }
965
- const expectedHash = await sha256Hex(canonicalJson(tool.inputSchema));
1362
+ const expectedHash = await sha256Hex(canonicalJson2(tool.inputSchema));
966
1363
  if (expectedHash !== sig.schema_hash) {
967
1364
  out.push({ tool: tool.name, valid: false, reason: "schema hash mismatch" });
968
1365
  continue;
969
1366
  }
1367
+ const jwsToken = sig.signature;
1368
+ if (!jwsToken) {
1369
+ out.push({ tool: tool.name, valid: true, warn_no_signature: true });
1370
+ continue;
1371
+ }
1372
+ const jwksDoc = jwks;
1373
+ let jHeader;
1374
+ let jPayloadRaw;
1375
+ let jSigBytes;
1376
+ try {
1377
+ const parts2 = jwsToken.split(".");
1378
+ if (parts2.length !== 3) throw new Error("JWS must have three segments");
1379
+ jHeader = JSON.parse(new TextDecoder().decode(base64urlDecode(parts2[0])));
1380
+ jPayloadRaw = new TextDecoder().decode(base64urlDecode(parts2[1]));
1381
+ jSigBytes = base64urlDecode(parts2[2]);
1382
+ } catch (err) {
1383
+ out.push({ tool: tool.name, valid: false, reason: `malformed tool JWS: ${err.message}` });
1384
+ continue;
1385
+ }
1386
+ if (jHeader.alg !== "ES256") {
1387
+ out.push({ tool: tool.name, valid: false, reason: `unsupported tool sig alg: ${jHeader.alg}` });
1388
+ continue;
1389
+ }
1390
+ if (jPayloadRaw !== sig.schema_hash) {
1391
+ out.push({ tool: tool.name, valid: false, reason: "tool JWS payload does not match schema_hash" });
1392
+ continue;
1393
+ }
1394
+ const jwk = jwksDoc.keys.find((k) => jHeader.kid ? k.kid === jHeader.kid : true);
1395
+ if (!jwk) {
1396
+ out.push({ tool: tool.name, valid: false, reason: `no JWK for kid=${jHeader.kid}` });
1397
+ continue;
1398
+ }
1399
+ let publicKey;
1400
+ try {
1401
+ publicKey = await importEs256JwkAsPublicKey(jwk);
1402
+ } catch (err) {
1403
+ out.push({ tool: tool.name, valid: false, reason: `jwk import: ${err.message}` });
1404
+ continue;
1405
+ }
1406
+ const parts = jwsToken.split(".");
1407
+ const signedInput = new TextEncoder().encode(`${parts[0]}.${parts[1]}`);
1408
+ let sigValid = false;
1409
+ try {
1410
+ sigValid = await crypto.subtle.verify(
1411
+ { name: "ECDSA", hash: "SHA-256" },
1412
+ publicKey,
1413
+ toArrayBuffer(jSigBytes),
1414
+ toArrayBuffer(signedInput)
1415
+ );
1416
+ } catch (err) {
1417
+ out.push({ tool: tool.name, valid: false, reason: `sig verify error: ${err.message}` });
1418
+ continue;
1419
+ }
1420
+ if (!sigValid) {
1421
+ out.push({ tool: tool.name, valid: false, reason: "tool signature mismatch" });
1422
+ continue;
1423
+ }
970
1424
  out.push({ tool: tool.name, valid: true });
971
1425
  }
972
1426
  return out;
@@ -989,23 +1443,23 @@ async function fetchJwks(url, fetchImpl) {
989
1443
  }
990
1444
  if (resp.type === "opaqueredirect" || resp.status >= 300 && resp.status < 400) {
991
1445
  throw new AlterProvenanceError(
992
- `${url} \u2192 redirect rejected (allowlist enforces initial URL only)`
1446
+ `${url} -> redirect rejected (allowlist enforces initial URL only)`
993
1447
  );
994
1448
  }
995
- if (!resp.ok) throw new AlterNetworkError(`${url} \u2192 HTTP ${resp.status}`);
1449
+ if (!resp.ok) throw new AlterNetworkError(`${url} -> HTTP ${resp.status}`);
996
1450
  const contentLength = resp.headers.get("content-length");
997
1451
  if (contentLength !== null) {
998
1452
  const n = Number.parseInt(contentLength, 10);
999
1453
  if (Number.isFinite(n) && n > JWKS_MAX_BYTES) {
1000
1454
  throw new AlterProvenanceError(
1001
- `${url} \u2192 JWKS too large: ${n} > ${JWKS_MAX_BYTES} bytes`
1455
+ `${url} -> JWKS too large: ${n} > ${JWKS_MAX_BYTES} bytes`
1002
1456
  );
1003
1457
  }
1004
1458
  }
1005
1459
  const body = await resp.text();
1006
1460
  if (body.length > JWKS_MAX_BYTES) {
1007
1461
  throw new AlterProvenanceError(
1008
- `${url} \u2192 JWKS too large: ${body.length} > ${JWKS_MAX_BYTES} bytes`
1462
+ `${url} -> JWKS too large: ${body.length} > ${JWKS_MAX_BYTES} bytes`
1009
1463
  );
1010
1464
  }
1011
1465
  let doc;
@@ -1089,14 +1543,14 @@ async function sha256Hex(input) {
1089
1543
  function toArrayBuffer(view) {
1090
1544
  return view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength);
1091
1545
  }
1092
- function canonicalJson(value) {
1546
+ function canonicalJson2(value) {
1093
1547
  if (value === null || typeof value !== "object") return JSON.stringify(value);
1094
1548
  if (Array.isArray(value)) {
1095
- return `[${value.map(canonicalJson).join(",")}]`;
1549
+ return `[${value.map(canonicalJson2).join(",")}]`;
1096
1550
  }
1097
1551
  const obj = value;
1098
1552
  const keys = Object.keys(obj).sort();
1099
- return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJson(obj[k])}`).join(",")}}`;
1553
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJson2(obj[k])}`).join(",")}}`;
1100
1554
  }
1101
1555
 
1102
1556
  // src/client.ts
@@ -1112,11 +1566,21 @@ var AlterClient = class {
1112
1566
  this.options = options;
1113
1567
  this.x402 = options.x402;
1114
1568
  const endpoint = options.endpoint ?? DEFAULT_ENDPOINT;
1115
- this.mcp = new MCPClient({ ...options, endpoint, x402: options.x402 });
1569
+ const preflightHook = options.unsafe_skipVersionCheck ? void 0 : () => checkMinVersion({
1570
+ apiBase: options.apiBase,
1571
+ knownFloorPublicKeys: options.knownFloorPublicKeys,
1572
+ fetchImpl: options.fetch
1573
+ }).then(() => void 0);
1574
+ this.mcp = new MCPClient({
1575
+ ...options,
1576
+ endpoint,
1577
+ x402: options.x402,
1578
+ preflightHook
1579
+ });
1116
1580
  }
1117
1581
  /**
1118
1582
  * Resolve the MCP endpoint via discovery if requested. Safe to call
1119
- * multiple times the first successful lookup is cached.
1583
+ * multiple times: the first successful lookup is cached.
1120
1584
  */
1121
1585
  async discoverEndpoint() {
1122
1586
  if (this.discovered) return this.discovered;
@@ -1129,7 +1593,7 @@ var AlterClient = class {
1129
1593
  return this.discoveryPromise;
1130
1594
  }
1131
1595
  /**
1132
- * Initialise the MCP session. Optional every method calls
1596
+ * Initialise the MCP session. Optional: every method calls
1133
1597
  * `mcp.initialize()` lazily, but you can call this once at startup if
1134
1598
  * you want fail-fast behaviour.
1135
1599
  */
@@ -1137,11 +1601,11 @@ var AlterClient = class {
1137
1601
  await this.mcp.initialize();
1138
1602
  }
1139
1603
  // ── Free tier ────────────────────────────────────────────────────────
1140
- /** First handshake confirms the connection, returns trust tier and tool counts. */
1604
+ /** First handshake: confirms the connection, returns trust tier and tool counts. */
1141
1605
  async helloAgent() {
1142
1606
  return this.mcp.callTool("hello_agent", {});
1143
1607
  }
1144
- /** Resolve a ~handle (e.g. ~drew) to its canonical form and kind. No auth required. */
1608
+ /** Resolve a ~handle (e.g. ~example) to its canonical form and kind. No auth required. */
1145
1609
  async resolveHandle(args) {
1146
1610
  const payload = typeof args === "string" ? { query: args } : args;
1147
1611
  return this.mcp.callTool("alter_resolve_handle", payload);
@@ -1149,7 +1613,7 @@ var AlterClient = class {
1149
1613
  /** Verify a person is registered with ALTER (handle or id). */
1150
1614
  async verify(handleOrId, claims) {
1151
1615
  const args = handleOrId.includes("@") ? { member_id: "", email: handleOrId } : handleOrId.startsWith("~") ? (
1152
- // ~handle server resolves these via the member_id field
1616
+ // ~handle: server resolves these via the member_id field
1153
1617
  { member_id: handleOrId }
1154
1618
  ) : { member_id: handleOrId };
1155
1619
  if (claims) args.claims = claims;
@@ -1249,7 +1713,7 @@ var AlterClient = class {
1249
1713
  }
1250
1714
  // ── Alter-to-Alter Messaging ─────────────────────────────────────────
1251
1715
  // Wave 1: cross-handle direct messages between authenticated tilde
1252
- // handles. Default closed recipient must have granted the sender via
1716
+ // handles. Default closed: recipient must have granted the sender via
1253
1717
  // alter_message_grant. Spec: docs/technical/Alter-to-Alter Messaging.md.
1254
1718
  /** Send a direct message to another tilde handle. */
1255
1719
  async messageSend(args) {
@@ -1283,7 +1747,7 @@ var AlterClient = class {
1283
1747
  /**
1284
1748
  * Verify the ES256 provenance attestation on a tool response.
1285
1749
  * Accepts either a {@link ProvenanceEnvelope} or the raw `_meta`
1286
- * object the latter is more convenient for ad-hoc verification.
1750
+ * object: the latter is more convenient for ad-hoc verification.
1287
1751
  */
1288
1752
  async verifyProvenance(envelope) {
1289
1753
  if (!envelope) return { valid: false, reason: "no provenance envelope" };
@@ -1323,7 +1787,7 @@ function generateGenericMcpConfig(opts = {}) {
1323
1787
  const entry = {
1324
1788
  url: opts.endpoint ?? DEFAULT_ENDPOINT,
1325
1789
  transport: "streamable-http",
1326
- description: "ALTER Identity \u2014 psychometric identity field for AI agents"
1790
+ description: "ALTER Identity: psychometric identity field for AI agents"
1327
1791
  };
1328
1792
  if (Object.keys(headers).length > 0) entry.headers = headers;
1329
1793
  return { mcpServers: { [serverName]: entry } };
@@ -1351,7 +1815,7 @@ function generateClaudeDesktopConfig(opts = {}) {
1351
1815
  const entry = {
1352
1816
  command: bridgeCommand,
1353
1817
  env: env2,
1354
- description: "ALTER Identity \u2014 psychometric identity field for AI agents"
1818
+ description: "ALTER Identity: psychometric identity field for AI agents"
1355
1819
  };
1356
1820
  if (opts.extraArgs && opts.extraArgs.length > 0) {
1357
1821
  entry.args = [...opts.extraArgs];
@@ -1362,11 +1826,6 @@ function generateClaudeDesktopConfig(opts = {}) {
1362
1826
  // src/wire/index.ts
1363
1827
  init_cjs_shims();
1364
1828
 
1365
- // src/meta.ts
1366
- init_cjs_shims();
1367
- var SDK_NAME = "@truealter/sdk";
1368
- var SDK_VERSION = "0.3.0";
1369
-
1370
1829
  // src/wire/paths.ts
1371
1830
  init_cjs_shims();
1372
1831
  var HOME = os.homedir();
@@ -1499,7 +1958,7 @@ function probeAll() {
1499
1958
  // src/wire/sync.ts
1500
1959
  init_cjs_shims();
1501
1960
  var SYNC_PREFIXES = [
1502
- // iCloud Drive both the new and legacy mounts.
1961
+ // iCloud Drive: both the new and legacy mounts.
1503
1962
  "Library/Mobile Documents/com~apple~CloudDocs",
1504
1963
  "iCloud Drive",
1505
1964
  // OneDrive variants Microsoft ships across editions.
@@ -1512,7 +1971,7 @@ var SYNC_PREFIXES = [
1512
1971
  "Google Drive",
1513
1972
  "GoogleDrive",
1514
1973
  "CloudStorage/GoogleDrive",
1515
- // Box, pCloud, Sync.com, MEGA high-signal names worth refusing.
1974
+ // Box, pCloud, Sync.com, MEGA: high-signal names worth refusing.
1516
1975
  "Box Sync",
1517
1976
  "pCloud Drive",
1518
1977
  "Sync.com",
@@ -1570,10 +2029,10 @@ function atomicJsonMerge(opts) {
1570
2029
  preBytes = fs.readFileSync(path$1, "utf8");
1571
2030
  if (preBytes.trim().length > 0) {
1572
2031
  try {
1573
- parsed = JSON.parse(preBytes);
2032
+ parsed = JSON.parse(preBytes.replace(/^\uFEFF/, ""));
1574
2033
  } catch (err) {
1575
2034
  throw new Error(
1576
- `refusing to wire ${path$1}: existing file is not valid JSON (${err.message}). Hand-fix the file, then re-run \`alter-identity wire\`.`
2035
+ `refusing to wire ${path$1}: existing file is not valid JSON (${err.message}). Hand-fix the file, then re-run \`alter wire\`.`
1577
2036
  );
1578
2037
  }
1579
2038
  if (typeof parsed !== "object" || Array.isArray(parsed) || parsed === null) {
@@ -1626,6 +2085,26 @@ function restoreFromBackup(path, backupPath) {
1626
2085
  // src/wire/index.ts
1627
2086
  var TIMESTAMP = () => String(Math.floor(Date.now() / 1e3));
1628
2087
  var ISO_NOW = () => (/* @__PURE__ */ new Date()).toISOString();
2088
+ function readCfAccessEnv() {
2089
+ const envPath = path.join(os.homedir(), ".config", "alter", "cf-access.env");
2090
+ try {
2091
+ const content = fs.readFileSync(envPath, "utf8");
2092
+ let clientId = "";
2093
+ let clientSecret = "";
2094
+ for (const line of content.split("\n")) {
2095
+ const trimmed = line.trim();
2096
+ if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
2097
+ const eqIdx = trimmed.indexOf("=");
2098
+ const key = trimmed.slice(0, eqIdx).replace(/^export\s+/, "").trim();
2099
+ const val = trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, "");
2100
+ if (key === "CF_ACCESS_CLIENT_ID") clientId = val;
2101
+ if (key === "CF_ACCESS_CLIENT_SECRET") clientSecret = val;
2102
+ }
2103
+ if (clientId && clientSecret) return { clientId, clientSecret };
2104
+ } catch {
2105
+ }
2106
+ return void 0;
2107
+ }
1629
2108
  function clientById(id) {
1630
2109
  const hit = ALL_CLIENTS.find((c) => c.id === id);
1631
2110
  if (!hit) throw new Error(`unknown client id: ${id}`);
@@ -1634,6 +2113,7 @@ function clientById(id) {
1634
2113
  function wire(opts = {}) {
1635
2114
  const endpoint = opts.endpoint ?? DEFAULT_ENDPOINT;
1636
2115
  const apiKey = opts.apiKey;
2116
+ const cfAccess = opts.cfAccess ?? readCfAccessEnv();
1637
2117
  const probes = probeAll();
1638
2118
  const selection = opts.only ?? probes.filter((p) => p.installed).map((p) => p.client.id);
1639
2119
  const ts = TIMESTAMP();
@@ -1652,9 +2132,9 @@ function wire(opts = {}) {
1652
2132
  }
1653
2133
  try {
1654
2134
  if (id === "claude-code") {
1655
- targets.push(wireClaudeCode({ endpoint, apiKey }));
2135
+ targets.push(wireClaudeCode({ endpoint, apiKey, cfAccess }));
1656
2136
  } else {
1657
- targets.push(wireFileTarget({ id, endpoint, apiKey, timestamp: ts }));
2137
+ targets.push(wireFileTarget({ id, endpoint, apiKey, cfAccess, timestamp: ts }));
1658
2138
  }
1659
2139
  } catch (err) {
1660
2140
  const message = err.message;
@@ -1685,10 +2165,15 @@ function wireFileTarget(args) {
1685
2165
  const sync = detectSyncedVolume(client.configPath);
1686
2166
  if (sync) {
1687
2167
  throw new Error(
1688
- `refusing to wire ${client.label}: config path ${sync.resolvedPath} lives under ${sync.matchedPrefix}. Synced volumes propagate credentials across devices \u2014 move the config off the sync root, or run wire on the device you want to target.`
2168
+ `refusing to wire ${client.label}: config path ${sync.resolvedPath} lives under ${sync.matchedPrefix}. Synced volumes propagate credentials across devices: move the config off the sync root, or run wire on the device you want to target.`
1689
2169
  );
1690
2170
  }
1691
- const entry = args.id === "claude-desktop" ? generateClaudeDesktopConfig({ endpoint: args.endpoint, apiKey: args.apiKey }) : generateGenericMcpConfig({ endpoint: args.endpoint, apiKey: args.apiKey });
2171
+ const cfHeaders = {};
2172
+ if (args.cfAccess) {
2173
+ cfHeaders["CF-Access-Client-Id"] = args.cfAccess.clientId;
2174
+ cfHeaders["CF-Access-Client-Secret"] = args.cfAccess.clientSecret;
2175
+ }
2176
+ const entry = args.id === "claude-desktop" ? generateClaudeDesktopConfig({ endpoint: args.endpoint, apiKey: args.apiKey }) : generateGenericMcpConfig({ endpoint: args.endpoint, apiKey: args.apiKey, headers: cfHeaders });
1692
2177
  const rootKey = client.rootKey;
1693
2178
  const serverName = "alter";
1694
2179
  const result = atomicJsonMerge({
@@ -1720,7 +2205,8 @@ function wireFileTarget(args) {
1720
2205
  }
1721
2206
  function wireClaudeCode(args) {
1722
2207
  const cmd = "claude";
1723
- const argList = [
2208
+ const bridgePath = resolveBridgeScript();
2209
+ const argList = bridgePath ? ["mcp", "add", "--scope", "user", "alter", "--", "node", bridgePath] : [
1724
2210
  "mcp",
1725
2211
  "add",
1726
2212
  "--scope",
@@ -1728,16 +2214,15 @@ function wireClaudeCode(args) {
1728
2214
  "--transport",
1729
2215
  "http",
1730
2216
  "alter",
1731
- args.endpoint
2217
+ args.endpoint,
2218
+ ...args.apiKey ? ["--header", `X-ALTER-API-Key:${args.apiKey}`] : []
1732
2219
  ];
1733
- if (args.apiKey) {
1734
- argList.push("--header", `X-ALTER-API-Key:${args.apiKey}`);
1735
- }
1736
2220
  const full = `${cmd} ${argList.join(" ")}`;
1737
2221
  const run = child_process.spawnSync(cmd, argList, {
1738
2222
  encoding: "utf8",
1739
2223
  shell: process.platform === "win32",
1740
- timeout: 1e4
2224
+ timeout: 1e4,
2225
+ env: bridgePath ? { ...process.env, ALTER_PUBLIC_MCP_ENDPOINT: args.endpoint, ...args.apiKey ? { ALTER_API_KEY: args.apiKey } : {} } : void 0
1741
2226
  });
1742
2227
  if (run.error) {
1743
2228
  return {
@@ -1768,6 +2253,16 @@ function wireClaudeCode(args) {
1768
2253
  reason: `claude mcp add exited ${String(run.status)}`
1769
2254
  };
1770
2255
  }
2256
+ function resolveBridgeScript() {
2257
+ const here = path.dirname(url.fileURLToPath(importMetaUrl));
2258
+ const siblingBridge = path.join(here, "..", "dist", "mcp-bridge.js");
2259
+ if (fs.existsSync(siblingBridge)) return siblingBridge;
2260
+ const srcBridge = path.join(here, "..", "mcp-bridge.js");
2261
+ if (fs.existsSync(srcBridge)) return srcBridge;
2262
+ const npmGlobalBridge = path.join(here, "mcp-bridge.js");
2263
+ if (fs.existsSync(npmGlobalBridge)) return npmGlobalBridge;
2264
+ return null;
2265
+ }
1771
2266
  function unwire() {
1772
2267
  const state = readWireState();
1773
2268
  const undone = [];
@@ -1919,19 +2414,19 @@ var TOOL_COSTS = {
1919
2414
  complete_knot: 0,
1920
2415
  check_golden_thread: 0,
1921
2416
  thread_census: 0,
1922
- // L1 ($0.005)
1923
- assess_traits: 5e-3,
1924
- get_trait_snapshot: 5e-3,
1925
- // L2 ($0.01)
1926
- get_full_trait_vector: 0.01,
1927
- get_side_quest_graph: 0.01,
1928
- // L3 ($0.025)
1929
- query_graph_similarity: 0.025,
1930
- // L4 ($0.05)
1931
- compute_belonging: 0.05,
1932
- // L5 ($0.50)
1933
- get_match_recommendations: 0.5,
1934
- generate_match_narrative: 0.5
2417
+ // L1 ($0.01)
2418
+ assess_traits: 0.01,
2419
+ get_trait_snapshot: 0.01,
2420
+ // L2 ($0.10)
2421
+ get_full_trait_vector: 0.1,
2422
+ get_side_quest_graph: 0.1,
2423
+ // L3 ($0.30)
2424
+ query_graph_similarity: 0.3,
2425
+ // L4 ($0.60)
2426
+ compute_belonging: 0.6,
2427
+ // L5 ($1.00)
2428
+ get_match_recommendations: 1,
2429
+ generate_match_narrative: 1
1935
2430
  };
1936
2431
  var TOOL_BLAST_RADIUS = {
1937
2432
  // Low: read-only reference
@@ -1971,13 +2466,110 @@ var TOOL_BLAST_RADIUS = {
1971
2466
  query_graph_similarity: "high"
1972
2467
  };
1973
2468
 
2469
+ // src/pricing.generated.ts
2470
+ init_cjs_shims();
2471
+ var GENERATED_TOOL_TIERS = {
2472
+ // L0 (free)
2473
+ alter_presence_read: 0,
2474
+ alter_resolve_handle: 0,
2475
+ check_assessment_status: 0,
2476
+ create_identity_stub: 0,
2477
+ dispute_attestation: 0,
2478
+ get_competencies: 0,
2479
+ get_earning_summary: 0,
2480
+ get_engagement_level: 0,
2481
+ get_identity_earnings: 0,
2482
+ get_identity_trust_score: 0,
2483
+ get_network_stats: 0,
2484
+ get_privacy_budget: 0,
2485
+ get_profile: 0,
2486
+ initiate_assessment: 0,
2487
+ list_archetypes: 0,
2488
+ query_matches: 0,
2489
+ recommend_tool: 0,
2490
+ search_identities: 0,
2491
+ verify_identity: 0,
2492
+ // L1 ($0.01)
2493
+ assess_traits: 1,
2494
+ attest_domain: 1,
2495
+ get_trait_snapshot: 1,
2496
+ poll_requirement_matches: 1,
2497
+ submit_context: 1,
2498
+ submit_social_links: 1,
2499
+ submit_structured_profile: 1,
2500
+ // L2 ($0.10)
2501
+ attest_claim_provenance: 2,
2502
+ get_full_trait_vector: 2,
2503
+ get_side_quest_graph: 2,
2504
+ submit_batch_context: 2,
2505
+ // L3 ($0.30)
2506
+ alter_alignment: 3,
2507
+ query_graph_similarity: 3,
2508
+ // L4 ($0.60)
2509
+ compute_belonging: 4,
2510
+ // L5 ($1.00)
2511
+ generate_match_narrative: 5,
2512
+ get_match_recommendations: 5,
2513
+ query_field: 5
2514
+ };
2515
+ var GENERATED_TOOL_PRICING = {
2516
+ // L1: $0.01
2517
+ assess_traits: 0.01,
2518
+ attest_domain: 0.01,
2519
+ get_trait_snapshot: 0.01,
2520
+ poll_requirement_matches: 0.01,
2521
+ submit_context: 0.01,
2522
+ submit_social_links: 0.01,
2523
+ submit_structured_profile: 0.01,
2524
+ // L2: $0.10
2525
+ attest_claim_provenance: 0.1,
2526
+ get_full_trait_vector: 0.1,
2527
+ get_side_quest_graph: 0.1,
2528
+ submit_batch_context: 0.1,
2529
+ // L3: $0.30
2530
+ alter_alignment: 0.3,
2531
+ query_graph_similarity: 0.3,
2532
+ // L4: $0.60
2533
+ compute_belonging: 0.6,
2534
+ // L5: $1.00
2535
+ generate_match_narrative: 1,
2536
+ get_match_recommendations: 1,
2537
+ query_field: 1
2538
+ };
2539
+ var TIER_PRICES = {
2540
+ L0: 0,
2541
+ L1: 0.01,
2542
+ L2: 0.1,
2543
+ L3: 0.3,
2544
+ L4: 0.6,
2545
+ L5: 1
2546
+ };
2547
+ var ADVERTISED_TOOL_COUNTS = {
2548
+ /** Free (L0) tools visible to anonymous / agent-class callers. */
2549
+ free: 26,
2550
+ /** Premium (L1-L5) tools requiring x402 payment. */
2551
+ premium: 8,
2552
+ /** Messaging tools (member-self-only; excluded from external advertisement). */
2553
+ messaging: 0,
2554
+ /** Total publicly advertised (free + premium). */
2555
+ total: 34
2556
+ };
2557
+ var REVENUE_SPLIT = {
2558
+ weaver: 0.75,
2559
+ // data subject (Identity Income)
2560
+ facilitator: 0.05,
2561
+ alter: 0.15,
2562
+ treasury: 0.05
2563
+ };
2564
+
1974
2565
  // src/homepage.ts
1975
2566
  init_cjs_shims();
1976
2567
  var HOMEPAGE_LIMITS = {
1977
2568
  whoami_max_chars: 240,
1978
2569
  opener_max_chars: 280,
1979
2570
  pronouns_max_chars: 32,
1980
- attunement_glyph_max_chars: 16
2571
+ attunement_glyph_max_chars: 16,
2572
+ contact_email_max_chars: 254
1981
2573
  };
1982
2574
 
1983
2575
  // src/themes.ts
@@ -1991,6 +2583,7 @@ var THEME_LIMITS = {
1991
2583
  };
1992
2584
  var OSC8_ALLOWED_SCHEMES = ["https:", "mailto:"];
1993
2585
 
2586
+ exports.ADVERTISED_TOOL_COUNTS = ADVERTISED_TOOL_COUNTS;
1994
2587
  exports.ALL_CLIENTS = ALL_CLIENTS;
1995
2588
  exports.AlterAuthError = AlterAuthError;
1996
2589
  exports.AlterClient = AlterClient;
@@ -2003,21 +2596,30 @@ exports.AlterProvenanceError = AlterProvenanceError;
2003
2596
  exports.AlterRateLimited = AlterRateLimited;
2004
2597
  exports.AlterTimeoutError = AlterTimeoutError;
2005
2598
  exports.AlterToolError = AlterToolError;
2599
+ exports.BelowFloorError = BelowFloorError;
2006
2600
  exports.CLAUDE_CODE = CLAUDE_CODE;
2007
2601
  exports.CLAUDE_DESKTOP = CLAUDE_DESKTOP;
2602
+ exports.CLIENT_CHANNEL = CLIENT_CHANNEL;
2603
+ exports.CLIENT_ID = CLIENT_ID;
2008
2604
  exports.CURSOR = CURSOR;
2009
2605
  exports.DEFAULT_DOMAIN = DEFAULT_DOMAIN;
2010
2606
  exports.DEFAULT_ENDPOINT = DEFAULT_ENDPOINT;
2011
2607
  exports.DEFAULT_VERIFY_AT_ALLOWLIST = DEFAULT_VERIFY_AT_ALLOWLIST;
2012
2608
  exports.FREE_TOOL_NAMES = FREE_TOOL_NAMES;
2609
+ exports.GENERATED_TOOL_PRICING = GENERATED_TOOL_PRICING;
2610
+ exports.GENERATED_TOOL_TIERS = GENERATED_TOOL_TIERS;
2013
2611
  exports.HOMEPAGE_LIMITS = HOMEPAGE_LIMITS;
2612
+ exports.KNOWN_FLOOR_PUBLIC_KEYS = KNOWN_FLOOR_PUBLIC_KEYS;
2014
2613
  exports.MCPClient = MCPClient;
2015
2614
  exports.MCP_PROTOCOL_VERSION = MCP_PROTOCOL_VERSION;
2615
+ exports.MIN_VERSION_ENDPOINT = MIN_VERSION_ENDPOINT;
2016
2616
  exports.OSC8_ALLOWED_SCHEMES = OSC8_ALLOWED_SCHEMES;
2017
2617
  exports.PREMIUM_TOOL_NAMES = PREMIUM_TOOL_NAMES;
2618
+ exports.REVENUE_SPLIT = REVENUE_SPLIT;
2018
2619
  exports.SDK_NAME = SDK_NAME;
2019
2620
  exports.SDK_VERSION = SDK_VERSION;
2020
2621
  exports.THEME_LIMITS = THEME_LIMITS;
2622
+ exports.TIER_PRICES = TIER_PRICES;
2021
2623
  exports.TOOL_BLAST_RADIUS = TOOL_BLAST_RADIUS;
2022
2624
  exports.TOOL_COSTS = TOOL_COSTS;
2023
2625
  exports.TOOL_TIERS = TOOL_TIERS;
@@ -2026,8 +2628,12 @@ exports.X402Client = X402Client;
2026
2628
  exports.base64urlDecode = base64urlDecode;
2027
2629
  exports.base64urlEncode = base64urlEncode2;
2028
2630
  exports.canonicalArgsSha256 = canonicalArgsSha256;
2631
+ exports.canonicalJson = canonicalJson;
2029
2632
  exports.canonicalStringify = canonicalStringify;
2633
+ exports.checkMinVersion = checkMinVersion;
2030
2634
  exports.clearDiscoveryCache = clearDiscoveryCache;
2635
+ exports.compareSemver = compareSemver;
2636
+ exports.computeKeyId = computeKeyId;
2031
2637
  exports.decodeDid = decodeDid;
2032
2638
  exports.detectSyncedVolume = detectSyncedVolume;
2033
2639
  exports.discover = discover;
@@ -2051,6 +2657,7 @@ exports.sign = sign;
2051
2657
  exports.signInvocation = signInvocation;
2052
2658
  exports.unwire = unwire;
2053
2659
  exports.verify = verify;
2660
+ exports.verifyFloorSignature = verifyFloorSignature;
2054
2661
  exports.verifyProvenance = verifyProvenance;
2055
2662
  exports.verifyToolSignatures = verifyToolSignatures;
2056
2663
  exports.wire = wire;