@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.js CHANGED
@@ -1,14 +1,15 @@
1
1
  import { p256 } from '@noble/curves/p256';
2
2
  import { sha256 } from '@noble/hashes/sha256';
3
3
  import { randomBytes, bytesToHex as bytesToHex$1, hexToBytes } from '@noble/hashes/utils';
4
- import { createPrivateKey, createHash } from 'crypto';
4
+ import { createPrivateKey, createPublicKey, createHash, verify as verify$1 } from 'crypto';
5
+ import { statSync, readFileSync, mkdirSync, writeFileSync, chmodSync, renameSync, existsSync, copyFileSync, unlinkSync } from 'fs';
6
+ import { homedir, platform } from 'os';
7
+ import { join, dirname, resolve } from 'path';
5
8
  import * as ed25519 from '@noble/ed25519';
6
9
  import { sha512 } from '@noble/hashes/sha512';
10
+ import { fileURLToPath } from 'url';
7
11
  import { spawnSync } from 'child_process';
8
- import { homedir, platform } from 'os';
9
- import { join, resolve, dirname } from 'path';
10
12
  import { env } from 'process';
11
- import { existsSync, readFileSync, mkdirSync, writeFileSync, copyFileSync, renameSync, unlinkSync } from 'fs';
12
13
 
13
14
  var __defProp = Object.defineProperty;
14
15
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -363,11 +364,11 @@ async function tryWellKnown(host, file, timeoutMs, fetchImpl) {
363
364
  }
364
365
  if (resp.type === "opaqueredirect" || resp.status >= 300 && resp.status < 400) {
365
366
  throw new AlterNetworkError(
366
- `${url} \u2192 redirect rejected (discovery must not follow redirects; validate the server configuration)`
367
+ `${url} -> redirect rejected (discovery must not follow redirects; validate the server configuration)`
367
368
  );
368
369
  }
369
370
  if (resp.status === 404) return null;
370
- if (!resp.ok) throw new AlterNetworkError(`${url} \u2192 HTTP ${resp.status}`);
371
+ if (!resp.ok) throw new AlterNetworkError(`${url} -> HTTP ${resp.status}`);
371
372
  const doc = await resp.json();
372
373
  if (file === "mcp.json") {
373
374
  const remotes = doc.remotes || [];
@@ -401,17 +402,326 @@ function ensureMcpPath(url) {
401
402
  }
402
403
  }
403
404
 
404
- // src/x402.ts
405
+ // src/meta.ts
406
+ var SDK_NAME = "@truealter/sdk";
407
+ var SDK_VERSION = "0.5.3" ;
408
+
409
+ // src/floor-preflight.ts
410
+ var MIN_VERSION_ENDPOINT = "/v1/clients/min-version";
411
+ var CLIENT_ID = "alter-identity";
412
+ var CLIENT_CHANNEL = "npm";
413
+ var IN_MEMORY_TTL_DEFAULT_MS = 60 * 60 * 1e3;
414
+ var IN_MEMORY_TTL_MIN_MS = 60 * 1e3;
415
+ var IN_MEMORY_TTL_MAX_MS = 24 * 60 * 60 * 1e3;
416
+ var DISK_FRESH_MS = 24 * 60 * 60 * 1e3;
417
+ var DISK_WARN_MS = 7 * 24 * 60 * 60 * 1e3;
418
+ var FETCH_TIMEOUT_MS = 4e3;
419
+ function computeKeyId(publicKeyPem) {
420
+ if (!publicKeyPem) return "00000000";
421
+ const pub = createPublicKey({ key: publicKeyPem, format: "pem" });
422
+ const jwk = pub.export({ format: "jwk" });
423
+ const rawBytes = Buffer.from(jwk.x, "base64url");
424
+ return createHash("sha256").update(rawBytes).digest("hex").slice(0, 8);
425
+ }
426
+ function canonicalJson(obj) {
427
+ return JSON.stringify(sortKeysDeep(obj));
428
+ }
429
+ function sortKeysDeep(value) {
430
+ if (Array.isArray(value)) {
431
+ return value.map(sortKeysDeep);
432
+ }
433
+ if (value !== null && typeof value === "object") {
434
+ const obj = value;
435
+ const sorted = {};
436
+ for (const k of Object.keys(obj).sort()) {
437
+ sorted[k] = sortKeysDeep(obj[k]);
438
+ }
439
+ return sorted;
440
+ }
441
+ return value;
442
+ }
443
+ var KNOWN_FLOOR_PUBLIC_KEYS = {
444
+ "8aa59e05": `-----BEGIN PUBLIC KEY-----
445
+ MCowBQYDK2VwAyEAgqw28dlniOuiTE1f4BxCPSEgMLaPtHsO8wN5RWEwEhE=
446
+ -----END PUBLIC KEY-----`,
447
+ "640f7d9a": `-----BEGIN PUBLIC KEY-----
448
+ MCowBQYDK2VwAyEARzvAWayDwHvZRfOZizGZe+/a7PF082WGhyMS3tx06H4=
449
+ -----END PUBLIC KEY-----`
450
+ };
451
+ var BelowFloorError = class extends Error {
452
+ name = "BelowFloorError";
453
+ code = "client_below_floor";
454
+ client_version;
455
+ min_version;
456
+ upgrade_cmd;
457
+ channel;
458
+ envelope;
459
+ constructor(envelope) {
460
+ super(envelope.error.message);
461
+ this.envelope = envelope;
462
+ this.client_version = envelope.error.client_version;
463
+ this.min_version = envelope.error.min_version;
464
+ this.upgrade_cmd = envelope.error.upgrade_cmd;
465
+ this.channel = envelope.error.channel;
466
+ Object.setPrototypeOf(this, new.target.prototype);
467
+ }
468
+ };
469
+ var memCache = null;
470
+ async function checkMinVersion(opts = {}) {
471
+ const apiBase = opts.apiBase ?? defaultApiBase();
472
+ const clientVersion = opts.clientVersion ?? SDK_VERSION;
473
+ const clientId = opts.clientId ?? CLIENT_ID;
474
+ const channel = opts.channel ?? CLIENT_CHANNEL;
475
+ const knownKeys = opts.knownFloorPublicKeys ?? KNOWN_FLOOR_PUBLIC_KEYS;
476
+ const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
477
+ const now = opts.now ?? Date.now;
478
+ const cachePath = opts.diskCachePath === void 0 ? defaultDiskCachePath() : opts.diskCachePath;
479
+ const mem = readInMemoryCache(now);
480
+ if (mem) {
481
+ return compareAndPermit(mem, {
482
+ clientVersion,
483
+ clientId,
484
+ channel,
485
+ diagnostic: "mem-cache-hit"
486
+ });
487
+ }
488
+ const disk = cachePath ? readDiskCache(cachePath, knownKeys) : null;
489
+ const diskAgeMs = disk ? now() - disk.fetched_at_ms : Number.POSITIVE_INFINITY;
490
+ let fetched = null;
491
+ let fetchError = null;
492
+ if (!disk || diskAgeMs > IN_MEMORY_TTL_DEFAULT_MS) {
493
+ try {
494
+ fetched = await fetchFloorDoc(apiBase, fetchImpl, knownKeys);
495
+ } catch (err) {
496
+ fetchError = err.message ?? "fetch-error";
497
+ }
498
+ }
499
+ if (fetched) {
500
+ populateMemCache(fetched, now());
501
+ if (cachePath) writeDiskCache(cachePath, fetched, now());
502
+ return compareAndPermit(fetched, {
503
+ clientVersion,
504
+ clientId,
505
+ channel,
506
+ diagnostic: "fetched"
507
+ });
508
+ }
509
+ if (disk) {
510
+ populateMemCache(disk.doc, disk.fetched_at_ms);
511
+ if (diskAgeMs > DISK_WARN_MS) {
512
+ return compareAndPermit(disk.doc, {
513
+ clientVersion,
514
+ clientId,
515
+ channel,
516
+ diagnostic: "below-floor-offline-stale-or-permit",
517
+ warn: `floor cache is >7d old and backend unreachable (${fetchError ?? "no refresh attempted"}); permitting if above floor`
518
+ });
519
+ }
520
+ if (diskAgeMs > DISK_FRESH_MS) {
521
+ return compareAndPermit(disk.doc, {
522
+ clientVersion,
523
+ clientId,
524
+ channel,
525
+ diagnostic: "warn-stale-permit",
526
+ warn: `floor cache is ${Math.round(diskAgeMs / (60 * 60 * 1e3))}h old; refresh recommended`
527
+ });
528
+ }
529
+ return compareAndPermit(disk.doc, {
530
+ clientVersion,
531
+ clientId,
532
+ channel,
533
+ diagnostic: "disk-cache-hit"
534
+ });
535
+ }
536
+ return {
537
+ ok: true,
538
+ floor: null,
539
+ diagnostic: "no-cache-no-fetch-permit",
540
+ warn: `floor preflight skipped: backend unreachable (${fetchError ?? "unknown"})`
541
+ };
542
+ }
543
+ function compareAndPermit(doc, ctx) {
544
+ const floor = lookupFloor(doc, ctx.clientId, ctx.channel);
545
+ if (!floor) {
546
+ return { ok: true, floor: null, diagnostic: `${ctx.diagnostic}+no-floor`, warn: ctx.warn };
547
+ }
548
+ if (compareSemver(ctx.clientVersion, floor.min_version) >= 0) {
549
+ return { ok: true, floor, diagnostic: ctx.diagnostic, warn: ctx.warn };
550
+ }
551
+ const envelope = {
552
+ error: {
553
+ code: "client_below_floor",
554
+ message: `Your ${ctx.clientId} is too old. Upgrade required.`,
555
+ client_version: ctx.clientVersion,
556
+ min_version: floor.min_version,
557
+ upgrade_cmd: floor.upgrade_cmd,
558
+ channel: ctx.channel
559
+ }
560
+ };
561
+ throw new BelowFloorError(envelope);
562
+ }
563
+ function lookupFloor(doc, clientId, channel) {
564
+ const entry = doc.floors[clientId];
565
+ if (!entry) return null;
566
+ if (isChannelFloor(entry)) return entry;
567
+ const exact = entry[channel];
568
+ if (exact) return exact;
569
+ const fallback = entry["unknown"];
570
+ if (fallback) return fallback;
571
+ return null;
572
+ }
573
+ function isChannelFloor(v) {
574
+ return typeof v.min_version === "string" && typeof v.upgrade_cmd === "string";
575
+ }
576
+ function compareSemver(a, b) {
577
+ const [aMaj, aMin, aPat, aPre] = parseSemver(a);
578
+ const [bMaj, bMin, bPat, bPre] = parseSemver(b);
579
+ if (aMaj !== bMaj) return aMaj - bMaj;
580
+ if (aMin !== bMin) return aMin - bMin;
581
+ if (aPat !== bPat) return aPat - bPat;
582
+ if (aPre && !bPre) return -1;
583
+ if (!aPre && bPre) return 1;
584
+ if (aPre && bPre) return aPre.localeCompare(bPre);
585
+ return 0;
586
+ }
587
+ function parseSemver(v) {
588
+ const m = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?/.exec(v);
589
+ if (!m) return [0, 0, 0, null];
590
+ return [Number(m[1]), Number(m[2]), Number(m[3]), m[4] ?? null];
591
+ }
592
+ function verifyFloorSignature(doc, keys = KNOWN_FLOOR_PUBLIC_KEYS) {
593
+ const pem = keys[doc.key_id];
594
+ if (!pem) return false;
595
+ if (computeKeyId(pem) !== doc.key_id) return false;
596
+ try {
597
+ const pubKeyObject = createPublicKey({ key: pem, format: "pem" });
598
+ const canonical = canonicalJson({
599
+ floors: doc.floors,
600
+ served_at: doc.served_at
601
+ });
602
+ return verify$1(
603
+ null,
604
+ Buffer.from(canonical, "utf-8"),
605
+ pubKeyObject,
606
+ Buffer.from(doc.signature, "hex")
607
+ );
608
+ } catch {
609
+ return false;
610
+ }
611
+ }
612
+ async function fetchFloorDoc(apiBase, fetchImpl, knownKeys) {
613
+ const url = `${apiBase.replace(/\/+$/, "")}${MIN_VERSION_ENDPOINT}`;
614
+ let response;
615
+ try {
616
+ response = await fetchImpl(url, {
617
+ headers: {
618
+ accept: "application/json",
619
+ "X-Alter-Client-Id": CLIENT_ID,
620
+ "X-Alter-Client-Version": SDK_VERSION,
621
+ "X-Alter-Client-Channel": CLIENT_CHANNEL,
622
+ "User-Agent": `${SDK_NAME}/${SDK_VERSION}`
623
+ },
624
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
625
+ });
626
+ } catch (err) {
627
+ throw new Error(`network: ${err.message ?? String(err)}`);
628
+ }
629
+ if (!response.ok) throw new Error(`http-${response.status}`);
630
+ const body = await response.json();
631
+ if (!body || !body.floors || !body.signature || !body.key_id) {
632
+ throw new Error("malformed-floor-doc");
633
+ }
634
+ if (!verifyFloorSignature(body, knownKeys)) {
635
+ throw new Error("signature-invalid");
636
+ }
637
+ return body;
638
+ }
639
+ function readInMemoryCache(now) {
640
+ if (!memCache) return null;
641
+ if (now() - memCache.fetched_at_ms > memCache.ttl_ms) {
642
+ memCache = null;
643
+ return null;
644
+ }
645
+ return memCache.doc;
646
+ }
647
+ function populateMemCache(doc, fetched_at_ms) {
648
+ const ttlSec = doc.cache_ttl_seconds ?? 3600;
649
+ const ttlMs = Math.min(
650
+ Math.max(ttlSec * 1e3, IN_MEMORY_TTL_MIN_MS),
651
+ IN_MEMORY_TTL_MAX_MS
652
+ );
653
+ memCache = { doc, fetched_at_ms, ttl_ms: ttlMs };
654
+ }
655
+ function defaultDiskCachePath() {
656
+ const xdg = process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config");
657
+ return join(xdg, "alter", "floor-cache.json");
658
+ }
659
+ function readDiskCache(path, knownKeys) {
660
+ if (process.platform !== "win32") {
661
+ let st;
662
+ try {
663
+ st = statSync(path);
664
+ } catch {
665
+ return null;
666
+ }
667
+ const euid = typeof process.geteuid === "function" ? process.geteuid() : st.uid;
668
+ if (st.uid !== euid) return null;
669
+ if ((st.mode & 511) !== 384) return null;
670
+ }
671
+ let raw;
672
+ try {
673
+ raw = readFileSync(path, "utf-8");
674
+ } catch {
675
+ return null;
676
+ }
677
+ let parsed;
678
+ try {
679
+ parsed = JSON.parse(raw);
680
+ } catch {
681
+ return null;
682
+ }
683
+ if (!parsed.doc || typeof parsed.fetched_at_ms !== "number") return null;
684
+ if (!verifyFloorSignature(parsed.doc, knownKeys)) {
685
+ return null;
686
+ }
687
+ return parsed;
688
+ }
689
+ function writeDiskCache(path, doc, now_ms) {
690
+ const entry = { doc, fetched_at_ms: now_ms };
691
+ const payload = JSON.stringify(entry);
692
+ try {
693
+ mkdirSync(dirname(path), { recursive: true, mode: 448 });
694
+ const tmp = `${path}.tmp`;
695
+ writeFileSync(tmp, payload, { mode: 384 });
696
+ try {
697
+ chmodSync(tmp, 384);
698
+ } catch {
699
+ }
700
+ renameSync(tmp, path);
701
+ } catch {
702
+ }
703
+ }
704
+ function defaultApiBase() {
705
+ return process.env.ALTER_API ?? "https://api.truealter.com";
706
+ }
405
707
  var X402Client = class {
406
708
  signer;
407
709
  maxPerQuery;
408
710
  networks;
409
711
  assets;
712
+ // undefined = allowlist check disabled (backward-compatible default).
713
+ // Non-null = active allowlist; reject any recipient not in the set.
714
+ recipientAllowlist;
410
715
  constructor(opts = {}) {
411
716
  this.signer = opts.signer;
412
717
  this.maxPerQuery = opts.maxPerQuery !== void 0 ? Number(opts.maxPerQuery) : void 0;
413
718
  this.networks = new Set(opts.networks ?? ["base", "base-sepolia"]);
414
719
  this.assets = new Set(opts.assets ?? ["USDC"]);
720
+ if (opts.recipientAllowlist !== void 0) {
721
+ this.recipientAllowlist = opts.recipientAllowlist.length === 0 ? void 0 : new Set(opts.recipientAllowlist.map((a) => a.toLowerCase()));
722
+ } else {
723
+ this.recipientAllowlist = void 0;
724
+ }
415
725
  }
416
726
  /**
417
727
  * Validate the envelope against this client's policy and, if a signer
@@ -437,6 +747,15 @@ var X402Client = class {
437
747
  );
438
748
  }
439
749
  }
750
+ if (this.recipientAllowlist !== void 0) {
751
+ const recipientNorm = (envelope.recipient ?? "").toLowerCase();
752
+ if (!recipientNorm || !this.recipientAllowlist.has(recipientNorm)) {
753
+ throw new AlterError(
754
+ "PAYMENT_REQUIRED",
755
+ `recipient "${envelope.recipient}" is not on the known-recipient allowlist`
756
+ );
757
+ }
758
+ }
440
759
  if (!this.signer) {
441
760
  throw new AlterPaymentRequired(envelope.resource ?? "unknown", envelope);
442
761
  }
@@ -495,6 +814,9 @@ var MCPClient = class {
495
814
  x402;
496
815
  signing;
497
816
  extraHeaders;
817
+ preflightHook;
818
+ preflightPromise = null;
819
+ preflightDone = false;
498
820
  requestCounter = 0;
499
821
  initialised = false;
500
822
  constructor(opts = {}) {
@@ -503,17 +825,43 @@ var MCPClient = class {
503
825
  this.fetchImpl = opts.fetch ?? fetch;
504
826
  this.timeoutMs = opts.timeoutMs ?? 3e4;
505
827
  this.maxRetries = opts.maxRetries ?? 2;
506
- this.clientInfo = opts.clientInfo ?? { name: "@truealter/sdk", version: "0.2.0" };
828
+ this.clientInfo = opts.clientInfo ?? { name: SDK_NAME, version: SDK_VERSION };
507
829
  this.x402 = opts.x402;
508
830
  this.signing = opts.signing;
509
831
  this.extraHeaders = opts.extraHeaders;
832
+ this.preflightHook = opts.preflightHook;
833
+ }
834
+ /**
835
+ * Run the lazy preflight hook (D-MIN-VERSION-FLOOR-1) exactly once.
836
+ * Idempotent and serialised: concurrent callers share the same
837
+ * promise. Throws from the hook propagate to every concurrent caller.
838
+ */
839
+ async runPreflight() {
840
+ if (this.preflightDone) return;
841
+ if (!this.preflightHook) {
842
+ this.preflightDone = true;
843
+ return;
844
+ }
845
+ if (!this.preflightPromise) {
846
+ this.preflightPromise = this.preflightHook().then(
847
+ () => {
848
+ this.preflightDone = true;
849
+ },
850
+ (err) => {
851
+ this.preflightPromise = null;
852
+ throw err;
853
+ }
854
+ );
855
+ }
856
+ await this.preflightPromise;
510
857
  }
511
858
  /**
512
859
  * Send the MCP `initialize` handshake and capture the resulting session
513
- * id. Idempotent safe to call multiple times.
860
+ * id. Idempotent: safe to call multiple times.
514
861
  */
515
862
  async initialize() {
516
863
  if (this.initialised) return null;
864
+ await this.runPreflight();
517
865
  const result = await this.rpc("initialize", {
518
866
  protocolVersion: MCP_PROTOCOL_VERSION,
519
867
  capabilities: {},
@@ -673,7 +1021,10 @@ var MCPClient = class {
673
1021
  ...this.extraHeaders ?? {},
674
1022
  "Content-Type": "application/json",
675
1023
  Accept: "application/json",
676
- "User-Agent": `${this.clientInfo.name}/${this.clientInfo.version}`
1024
+ "User-Agent": `${this.clientInfo.name}/${this.clientInfo.version}`,
1025
+ "X-Alter-Client-Id": "alter-identity",
1026
+ "X-Alter-Client-Version": SDK_VERSION,
1027
+ "X-Alter-Client-Channel": "npm"
677
1028
  };
678
1029
  if (this.apiKey) headers["X-ALTER-API-Key"] = this.apiKey;
679
1030
  if (this.sessionId) headers["Mcp-Session-Id"] = this.sessionId;
@@ -903,9 +1254,46 @@ async function verifyProvenance(envelope, opts = {}) {
903
1254
  kid: header.kid
904
1255
  };
905
1256
  }
1257
+ if (opts.expectedAud !== void 0 && opts.expectedAud !== "") {
1258
+ const tokenAud = payload.aud;
1259
+ const audList = tokenAud === void 0 ? [] : Array.isArray(tokenAud) ? tokenAud : [tokenAud];
1260
+ if (!audList.includes(opts.expectedAud)) {
1261
+ return {
1262
+ valid: false,
1263
+ reason: `aud mismatch: expected "${opts.expectedAud}", got ${JSON.stringify(tokenAud ?? null)}`,
1264
+ payload,
1265
+ kid: header.kid
1266
+ };
1267
+ }
1268
+ }
906
1269
  return { valid: true, payload, kid: header.kid };
907
1270
  }
908
- async function verifyToolSignatures(tools, signatures) {
1271
+ async function verifyToolSignatures(tools, signatures, opts = {}) {
1272
+ const jwksUrl = opts.jwksUrl ?? "https://api.truealter.com/.well-known/alter-keys.json";
1273
+ const fetchImpl = opts.fetch ?? fetch;
1274
+ if (!jwksUrl.startsWith("https://")) {
1275
+ return tools.map((t) => ({
1276
+ tool: t.name,
1277
+ valid: false,
1278
+ reason: `jwksUrl must be https: got ${jwksUrl}`
1279
+ }));
1280
+ }
1281
+ const needsJwks = tools.some((t) => {
1282
+ const sig = signatures[t.name];
1283
+ return sig && sig.signature;
1284
+ });
1285
+ let jwks = null;
1286
+ if (needsJwks) {
1287
+ try {
1288
+ jwks = await fetchJwks(jwksUrl, fetchImpl);
1289
+ } catch (err) {
1290
+ return tools.map((t) => ({
1291
+ tool: t.name,
1292
+ valid: false,
1293
+ reason: `jwks fetch failed: ${err.message}`
1294
+ }));
1295
+ }
1296
+ }
909
1297
  const out = [];
910
1298
  for (const tool of tools) {
911
1299
  const sig = signatures[tool.name];
@@ -913,11 +1301,68 @@ async function verifyToolSignatures(tools, signatures) {
913
1301
  out.push({ tool: tool.name, valid: false, reason: "no signature published" });
914
1302
  continue;
915
1303
  }
916
- const expectedHash = await sha256Hex(canonicalJson(tool.inputSchema));
1304
+ const expectedHash = await sha256Hex(canonicalJson2(tool.inputSchema));
917
1305
  if (expectedHash !== sig.schema_hash) {
918
1306
  out.push({ tool: tool.name, valid: false, reason: "schema hash mismatch" });
919
1307
  continue;
920
1308
  }
1309
+ const jwsToken = sig.signature;
1310
+ if (!jwsToken) {
1311
+ out.push({ tool: tool.name, valid: true, warn_no_signature: true });
1312
+ continue;
1313
+ }
1314
+ const jwksDoc = jwks;
1315
+ let jHeader;
1316
+ let jPayloadRaw;
1317
+ let jSigBytes;
1318
+ try {
1319
+ const parts2 = jwsToken.split(".");
1320
+ if (parts2.length !== 3) throw new Error("JWS must have three segments");
1321
+ jHeader = JSON.parse(new TextDecoder().decode(base64urlDecode(parts2[0])));
1322
+ jPayloadRaw = new TextDecoder().decode(base64urlDecode(parts2[1]));
1323
+ jSigBytes = base64urlDecode(parts2[2]);
1324
+ } catch (err) {
1325
+ out.push({ tool: tool.name, valid: false, reason: `malformed tool JWS: ${err.message}` });
1326
+ continue;
1327
+ }
1328
+ if (jHeader.alg !== "ES256") {
1329
+ out.push({ tool: tool.name, valid: false, reason: `unsupported tool sig alg: ${jHeader.alg}` });
1330
+ continue;
1331
+ }
1332
+ if (jPayloadRaw !== sig.schema_hash) {
1333
+ out.push({ tool: tool.name, valid: false, reason: "tool JWS payload does not match schema_hash" });
1334
+ continue;
1335
+ }
1336
+ const jwk = jwksDoc.keys.find((k) => jHeader.kid ? k.kid === jHeader.kid : true);
1337
+ if (!jwk) {
1338
+ out.push({ tool: tool.name, valid: false, reason: `no JWK for kid=${jHeader.kid}` });
1339
+ continue;
1340
+ }
1341
+ let publicKey;
1342
+ try {
1343
+ publicKey = await importEs256JwkAsPublicKey(jwk);
1344
+ } catch (err) {
1345
+ out.push({ tool: tool.name, valid: false, reason: `jwk import: ${err.message}` });
1346
+ continue;
1347
+ }
1348
+ const parts = jwsToken.split(".");
1349
+ const signedInput = new TextEncoder().encode(`${parts[0]}.${parts[1]}`);
1350
+ let sigValid = false;
1351
+ try {
1352
+ sigValid = await crypto.subtle.verify(
1353
+ { name: "ECDSA", hash: "SHA-256" },
1354
+ publicKey,
1355
+ toArrayBuffer(jSigBytes),
1356
+ toArrayBuffer(signedInput)
1357
+ );
1358
+ } catch (err) {
1359
+ out.push({ tool: tool.name, valid: false, reason: `sig verify error: ${err.message}` });
1360
+ continue;
1361
+ }
1362
+ if (!sigValid) {
1363
+ out.push({ tool: tool.name, valid: false, reason: "tool signature mismatch" });
1364
+ continue;
1365
+ }
921
1366
  out.push({ tool: tool.name, valid: true });
922
1367
  }
923
1368
  return out;
@@ -940,23 +1385,23 @@ async function fetchJwks(url, fetchImpl) {
940
1385
  }
941
1386
  if (resp.type === "opaqueredirect" || resp.status >= 300 && resp.status < 400) {
942
1387
  throw new AlterProvenanceError(
943
- `${url} \u2192 redirect rejected (allowlist enforces initial URL only)`
1388
+ `${url} -> redirect rejected (allowlist enforces initial URL only)`
944
1389
  );
945
1390
  }
946
- if (!resp.ok) throw new AlterNetworkError(`${url} \u2192 HTTP ${resp.status}`);
1391
+ if (!resp.ok) throw new AlterNetworkError(`${url} -> HTTP ${resp.status}`);
947
1392
  const contentLength = resp.headers.get("content-length");
948
1393
  if (contentLength !== null) {
949
1394
  const n = Number.parseInt(contentLength, 10);
950
1395
  if (Number.isFinite(n) && n > JWKS_MAX_BYTES) {
951
1396
  throw new AlterProvenanceError(
952
- `${url} \u2192 JWKS too large: ${n} > ${JWKS_MAX_BYTES} bytes`
1397
+ `${url} -> JWKS too large: ${n} > ${JWKS_MAX_BYTES} bytes`
953
1398
  );
954
1399
  }
955
1400
  }
956
1401
  const body = await resp.text();
957
1402
  if (body.length > JWKS_MAX_BYTES) {
958
1403
  throw new AlterProvenanceError(
959
- `${url} \u2192 JWKS too large: ${body.length} > ${JWKS_MAX_BYTES} bytes`
1404
+ `${url} -> JWKS too large: ${body.length} > ${JWKS_MAX_BYTES} bytes`
960
1405
  );
961
1406
  }
962
1407
  let doc;
@@ -1040,14 +1485,14 @@ async function sha256Hex(input) {
1040
1485
  function toArrayBuffer(view) {
1041
1486
  return view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength);
1042
1487
  }
1043
- function canonicalJson(value) {
1488
+ function canonicalJson2(value) {
1044
1489
  if (value === null || typeof value !== "object") return JSON.stringify(value);
1045
1490
  if (Array.isArray(value)) {
1046
- return `[${value.map(canonicalJson).join(",")}]`;
1491
+ return `[${value.map(canonicalJson2).join(",")}]`;
1047
1492
  }
1048
1493
  const obj = value;
1049
1494
  const keys = Object.keys(obj).sort();
1050
- return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJson(obj[k])}`).join(",")}}`;
1495
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJson2(obj[k])}`).join(",")}}`;
1051
1496
  }
1052
1497
 
1053
1498
  // src/client.ts
@@ -1063,11 +1508,21 @@ var AlterClient = class {
1063
1508
  this.options = options;
1064
1509
  this.x402 = options.x402;
1065
1510
  const endpoint = options.endpoint ?? DEFAULT_ENDPOINT;
1066
- this.mcp = new MCPClient({ ...options, endpoint, x402: options.x402 });
1511
+ const preflightHook = options.unsafe_skipVersionCheck ? void 0 : () => checkMinVersion({
1512
+ apiBase: options.apiBase,
1513
+ knownFloorPublicKeys: options.knownFloorPublicKeys,
1514
+ fetchImpl: options.fetch
1515
+ }).then(() => void 0);
1516
+ this.mcp = new MCPClient({
1517
+ ...options,
1518
+ endpoint,
1519
+ x402: options.x402,
1520
+ preflightHook
1521
+ });
1067
1522
  }
1068
1523
  /**
1069
1524
  * Resolve the MCP endpoint via discovery if requested. Safe to call
1070
- * multiple times the first successful lookup is cached.
1525
+ * multiple times: the first successful lookup is cached.
1071
1526
  */
1072
1527
  async discoverEndpoint() {
1073
1528
  if (this.discovered) return this.discovered;
@@ -1080,7 +1535,7 @@ var AlterClient = class {
1080
1535
  return this.discoveryPromise;
1081
1536
  }
1082
1537
  /**
1083
- * Initialise the MCP session. Optional every method calls
1538
+ * Initialise the MCP session. Optional: every method calls
1084
1539
  * `mcp.initialize()` lazily, but you can call this once at startup if
1085
1540
  * you want fail-fast behaviour.
1086
1541
  */
@@ -1088,11 +1543,11 @@ var AlterClient = class {
1088
1543
  await this.mcp.initialize();
1089
1544
  }
1090
1545
  // ── Free tier ────────────────────────────────────────────────────────
1091
- /** First handshake confirms the connection, returns trust tier and tool counts. */
1546
+ /** First handshake: confirms the connection, returns trust tier and tool counts. */
1092
1547
  async helloAgent() {
1093
1548
  return this.mcp.callTool("hello_agent", {});
1094
1549
  }
1095
- /** Resolve a ~handle (e.g. ~drew) to its canonical form and kind. No auth required. */
1550
+ /** Resolve a ~handle (e.g. ~example) to its canonical form and kind. No auth required. */
1096
1551
  async resolveHandle(args) {
1097
1552
  const payload = typeof args === "string" ? { query: args } : args;
1098
1553
  return this.mcp.callTool("alter_resolve_handle", payload);
@@ -1100,7 +1555,7 @@ var AlterClient = class {
1100
1555
  /** Verify a person is registered with ALTER (handle or id). */
1101
1556
  async verify(handleOrId, claims) {
1102
1557
  const args = handleOrId.includes("@") ? { member_id: "", email: handleOrId } : handleOrId.startsWith("~") ? (
1103
- // ~handle server resolves these via the member_id field
1558
+ // ~handle: server resolves these via the member_id field
1104
1559
  { member_id: handleOrId }
1105
1560
  ) : { member_id: handleOrId };
1106
1561
  if (claims) args.claims = claims;
@@ -1200,7 +1655,7 @@ var AlterClient = class {
1200
1655
  }
1201
1656
  // ── Alter-to-Alter Messaging ─────────────────────────────────────────
1202
1657
  // Wave 1: cross-handle direct messages between authenticated tilde
1203
- // handles. Default closed recipient must have granted the sender via
1658
+ // handles. Default closed: recipient must have granted the sender via
1204
1659
  // alter_message_grant. Spec: docs/technical/Alter-to-Alter Messaging.md.
1205
1660
  /** Send a direct message to another tilde handle. */
1206
1661
  async messageSend(args) {
@@ -1234,7 +1689,7 @@ var AlterClient = class {
1234
1689
  /**
1235
1690
  * Verify the ES256 provenance attestation on a tool response.
1236
1691
  * Accepts either a {@link ProvenanceEnvelope} or the raw `_meta`
1237
- * object the latter is more convenient for ad-hoc verification.
1692
+ * object: the latter is more convenient for ad-hoc verification.
1238
1693
  */
1239
1694
  async verifyProvenance(envelope) {
1240
1695
  if (!envelope) return { valid: false, reason: "no provenance envelope" };
@@ -1270,7 +1725,7 @@ function generateGenericMcpConfig(opts = {}) {
1270
1725
  const entry = {
1271
1726
  url: opts.endpoint ?? DEFAULT_ENDPOINT,
1272
1727
  transport: "streamable-http",
1273
- description: "ALTER Identity \u2014 psychometric identity field for AI agents"
1728
+ description: "ALTER Identity: psychometric identity field for AI agents"
1274
1729
  };
1275
1730
  if (Object.keys(headers).length > 0) entry.headers = headers;
1276
1731
  return { mcpServers: { [serverName]: entry } };
@@ -1296,17 +1751,13 @@ function generateClaudeDesktopConfig(opts = {}) {
1296
1751
  const entry = {
1297
1752
  command: bridgeCommand,
1298
1753
  env: env2,
1299
- description: "ALTER Identity \u2014 psychometric identity field for AI agents"
1754
+ description: "ALTER Identity: psychometric identity field for AI agents"
1300
1755
  };
1301
1756
  if (opts.extraArgs && opts.extraArgs.length > 0) {
1302
1757
  entry.args = [...opts.extraArgs];
1303
1758
  }
1304
1759
  return { mcpServers: { [serverName]: entry } };
1305
1760
  }
1306
-
1307
- // src/meta.ts
1308
- var SDK_NAME = "@truealter/sdk";
1309
- var SDK_VERSION = "0.3.0";
1310
1761
  var HOME = homedir();
1311
1762
  var PLAT = platform();
1312
1763
  function appData() {
@@ -1431,7 +1882,7 @@ function probeAll() {
1431
1882
  ];
1432
1883
  }
1433
1884
  var SYNC_PREFIXES = [
1434
- // iCloud Drive both the new and legacy mounts.
1885
+ // iCloud Drive: both the new and legacy mounts.
1435
1886
  "Library/Mobile Documents/com~apple~CloudDocs",
1436
1887
  "iCloud Drive",
1437
1888
  // OneDrive variants Microsoft ships across editions.
@@ -1444,7 +1895,7 @@ var SYNC_PREFIXES = [
1444
1895
  "Google Drive",
1445
1896
  "GoogleDrive",
1446
1897
  "CloudStorage/GoogleDrive",
1447
- // Box, pCloud, Sync.com, MEGA high-signal names worth refusing.
1898
+ // Box, pCloud, Sync.com, MEGA: high-signal names worth refusing.
1448
1899
  "Box Sync",
1449
1900
  "pCloud Drive",
1450
1901
  "Sync.com",
@@ -1496,10 +1947,10 @@ function atomicJsonMerge(opts) {
1496
1947
  preBytes = readFileSync(path, "utf8");
1497
1948
  if (preBytes.trim().length > 0) {
1498
1949
  try {
1499
- parsed = JSON.parse(preBytes);
1950
+ parsed = JSON.parse(preBytes.replace(/^\uFEFF/, ""));
1500
1951
  } catch (err) {
1501
1952
  throw new Error(
1502
- `refusing to wire ${path}: existing file is not valid JSON (${err.message}). Hand-fix the file, then re-run \`alter-identity wire\`.`
1953
+ `refusing to wire ${path}: existing file is not valid JSON (${err.message}). Hand-fix the file, then re-run \`alter wire\`.`
1503
1954
  );
1504
1955
  }
1505
1956
  if (typeof parsed !== "object" || Array.isArray(parsed) || parsed === null) {
@@ -1552,6 +2003,26 @@ function restoreFromBackup(path, backupPath) {
1552
2003
  // src/wire/index.ts
1553
2004
  var TIMESTAMP = () => String(Math.floor(Date.now() / 1e3));
1554
2005
  var ISO_NOW = () => (/* @__PURE__ */ new Date()).toISOString();
2006
+ function readCfAccessEnv() {
2007
+ const envPath = join(homedir(), ".config", "alter", "cf-access.env");
2008
+ try {
2009
+ const content = readFileSync(envPath, "utf8");
2010
+ let clientId = "";
2011
+ let clientSecret = "";
2012
+ for (const line of content.split("\n")) {
2013
+ const trimmed = line.trim();
2014
+ if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
2015
+ const eqIdx = trimmed.indexOf("=");
2016
+ const key = trimmed.slice(0, eqIdx).replace(/^export\s+/, "").trim();
2017
+ const val = trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, "");
2018
+ if (key === "CF_ACCESS_CLIENT_ID") clientId = val;
2019
+ if (key === "CF_ACCESS_CLIENT_SECRET") clientSecret = val;
2020
+ }
2021
+ if (clientId && clientSecret) return { clientId, clientSecret };
2022
+ } catch {
2023
+ }
2024
+ return void 0;
2025
+ }
1555
2026
  function clientById(id) {
1556
2027
  const hit = ALL_CLIENTS.find((c) => c.id === id);
1557
2028
  if (!hit) throw new Error(`unknown client id: ${id}`);
@@ -1560,6 +2031,7 @@ function clientById(id) {
1560
2031
  function wire(opts = {}) {
1561
2032
  const endpoint = opts.endpoint ?? DEFAULT_ENDPOINT;
1562
2033
  const apiKey = opts.apiKey;
2034
+ const cfAccess = opts.cfAccess ?? readCfAccessEnv();
1563
2035
  const probes = probeAll();
1564
2036
  const selection = opts.only ?? probes.filter((p) => p.installed).map((p) => p.client.id);
1565
2037
  const ts = TIMESTAMP();
@@ -1578,9 +2050,9 @@ function wire(opts = {}) {
1578
2050
  }
1579
2051
  try {
1580
2052
  if (id === "claude-code") {
1581
- targets.push(wireClaudeCode({ endpoint, apiKey }));
2053
+ targets.push(wireClaudeCode({ endpoint, apiKey, cfAccess }));
1582
2054
  } else {
1583
- targets.push(wireFileTarget({ id, endpoint, apiKey, timestamp: ts }));
2055
+ targets.push(wireFileTarget({ id, endpoint, apiKey, cfAccess, timestamp: ts }));
1584
2056
  }
1585
2057
  } catch (err) {
1586
2058
  const message = err.message;
@@ -1611,10 +2083,15 @@ function wireFileTarget(args) {
1611
2083
  const sync = detectSyncedVolume(client.configPath);
1612
2084
  if (sync) {
1613
2085
  throw new Error(
1614
- `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.`
2086
+ `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.`
1615
2087
  );
1616
2088
  }
1617
- const entry = args.id === "claude-desktop" ? generateClaudeDesktopConfig({ endpoint: args.endpoint, apiKey: args.apiKey }) : generateGenericMcpConfig({ endpoint: args.endpoint, apiKey: args.apiKey });
2089
+ const cfHeaders = {};
2090
+ if (args.cfAccess) {
2091
+ cfHeaders["CF-Access-Client-Id"] = args.cfAccess.clientId;
2092
+ cfHeaders["CF-Access-Client-Secret"] = args.cfAccess.clientSecret;
2093
+ }
2094
+ const entry = args.id === "claude-desktop" ? generateClaudeDesktopConfig({ endpoint: args.endpoint, apiKey: args.apiKey }) : generateGenericMcpConfig({ endpoint: args.endpoint, apiKey: args.apiKey, headers: cfHeaders });
1618
2095
  const rootKey = client.rootKey;
1619
2096
  const serverName = "alter";
1620
2097
  const result = atomicJsonMerge({
@@ -1646,7 +2123,8 @@ function wireFileTarget(args) {
1646
2123
  }
1647
2124
  function wireClaudeCode(args) {
1648
2125
  const cmd = "claude";
1649
- const argList = [
2126
+ const bridgePath = resolveBridgeScript();
2127
+ const argList = bridgePath ? ["mcp", "add", "--scope", "user", "alter", "--", "node", bridgePath] : [
1650
2128
  "mcp",
1651
2129
  "add",
1652
2130
  "--scope",
@@ -1654,16 +2132,15 @@ function wireClaudeCode(args) {
1654
2132
  "--transport",
1655
2133
  "http",
1656
2134
  "alter",
1657
- args.endpoint
2135
+ args.endpoint,
2136
+ ...args.apiKey ? ["--header", `X-ALTER-API-Key:${args.apiKey}`] : []
1658
2137
  ];
1659
- if (args.apiKey) {
1660
- argList.push("--header", `X-ALTER-API-Key:${args.apiKey}`);
1661
- }
1662
2138
  const full = `${cmd} ${argList.join(" ")}`;
1663
2139
  const run = spawnSync(cmd, argList, {
1664
2140
  encoding: "utf8",
1665
2141
  shell: process.platform === "win32",
1666
- timeout: 1e4
2142
+ timeout: 1e4,
2143
+ env: bridgePath ? { ...process.env, ALTER_PUBLIC_MCP_ENDPOINT: args.endpoint, ...args.apiKey ? { ALTER_API_KEY: args.apiKey } : {} } : void 0
1667
2144
  });
1668
2145
  if (run.error) {
1669
2146
  return {
@@ -1694,6 +2171,16 @@ function wireClaudeCode(args) {
1694
2171
  reason: `claude mcp add exited ${String(run.status)}`
1695
2172
  };
1696
2173
  }
2174
+ function resolveBridgeScript() {
2175
+ const here = dirname(fileURLToPath(import.meta.url));
2176
+ const siblingBridge = join(here, "..", "dist", "mcp-bridge.js");
2177
+ if (existsSync(siblingBridge)) return siblingBridge;
2178
+ const srcBridge = join(here, "..", "mcp-bridge.js");
2179
+ if (existsSync(srcBridge)) return srcBridge;
2180
+ const npmGlobalBridge = join(here, "mcp-bridge.js");
2181
+ if (existsSync(npmGlobalBridge)) return npmGlobalBridge;
2182
+ return null;
2183
+ }
1697
2184
  function unwire() {
1698
2185
  const state = readWireState();
1699
2186
  const undone = [];
@@ -1844,19 +2331,19 @@ var TOOL_COSTS = {
1844
2331
  complete_knot: 0,
1845
2332
  check_golden_thread: 0,
1846
2333
  thread_census: 0,
1847
- // L1 ($0.005)
1848
- assess_traits: 5e-3,
1849
- get_trait_snapshot: 5e-3,
1850
- // L2 ($0.01)
1851
- get_full_trait_vector: 0.01,
1852
- get_side_quest_graph: 0.01,
1853
- // L3 ($0.025)
1854
- query_graph_similarity: 0.025,
1855
- // L4 ($0.05)
1856
- compute_belonging: 0.05,
1857
- // L5 ($0.50)
1858
- get_match_recommendations: 0.5,
1859
- generate_match_narrative: 0.5
2334
+ // L1 ($0.01)
2335
+ assess_traits: 0.01,
2336
+ get_trait_snapshot: 0.01,
2337
+ // L2 ($0.10)
2338
+ get_full_trait_vector: 0.1,
2339
+ get_side_quest_graph: 0.1,
2340
+ // L3 ($0.30)
2341
+ query_graph_similarity: 0.3,
2342
+ // L4 ($0.60)
2343
+ compute_belonging: 0.6,
2344
+ // L5 ($1.00)
2345
+ get_match_recommendations: 1,
2346
+ generate_match_narrative: 1
1860
2347
  };
1861
2348
  var TOOL_BLAST_RADIUS = {
1862
2349
  // Low: read-only reference
@@ -1896,12 +2383,108 @@ var TOOL_BLAST_RADIUS = {
1896
2383
  query_graph_similarity: "high"
1897
2384
  };
1898
2385
 
2386
+ // src/pricing.generated.ts
2387
+ var GENERATED_TOOL_TIERS = {
2388
+ // L0 (free)
2389
+ alter_presence_read: 0,
2390
+ alter_resolve_handle: 0,
2391
+ check_assessment_status: 0,
2392
+ create_identity_stub: 0,
2393
+ dispute_attestation: 0,
2394
+ get_competencies: 0,
2395
+ get_earning_summary: 0,
2396
+ get_engagement_level: 0,
2397
+ get_identity_earnings: 0,
2398
+ get_identity_trust_score: 0,
2399
+ get_network_stats: 0,
2400
+ get_privacy_budget: 0,
2401
+ get_profile: 0,
2402
+ initiate_assessment: 0,
2403
+ list_archetypes: 0,
2404
+ query_matches: 0,
2405
+ recommend_tool: 0,
2406
+ search_identities: 0,
2407
+ verify_identity: 0,
2408
+ // L1 ($0.01)
2409
+ assess_traits: 1,
2410
+ attest_domain: 1,
2411
+ get_trait_snapshot: 1,
2412
+ poll_requirement_matches: 1,
2413
+ submit_context: 1,
2414
+ submit_social_links: 1,
2415
+ submit_structured_profile: 1,
2416
+ // L2 ($0.10)
2417
+ attest_claim_provenance: 2,
2418
+ get_full_trait_vector: 2,
2419
+ get_side_quest_graph: 2,
2420
+ submit_batch_context: 2,
2421
+ // L3 ($0.30)
2422
+ alter_alignment: 3,
2423
+ query_graph_similarity: 3,
2424
+ // L4 ($0.60)
2425
+ compute_belonging: 4,
2426
+ // L5 ($1.00)
2427
+ generate_match_narrative: 5,
2428
+ get_match_recommendations: 5,
2429
+ query_field: 5
2430
+ };
2431
+ var GENERATED_TOOL_PRICING = {
2432
+ // L1: $0.01
2433
+ assess_traits: 0.01,
2434
+ attest_domain: 0.01,
2435
+ get_trait_snapshot: 0.01,
2436
+ poll_requirement_matches: 0.01,
2437
+ submit_context: 0.01,
2438
+ submit_social_links: 0.01,
2439
+ submit_structured_profile: 0.01,
2440
+ // L2: $0.10
2441
+ attest_claim_provenance: 0.1,
2442
+ get_full_trait_vector: 0.1,
2443
+ get_side_quest_graph: 0.1,
2444
+ submit_batch_context: 0.1,
2445
+ // L3: $0.30
2446
+ alter_alignment: 0.3,
2447
+ query_graph_similarity: 0.3,
2448
+ // L4: $0.60
2449
+ compute_belonging: 0.6,
2450
+ // L5: $1.00
2451
+ generate_match_narrative: 1,
2452
+ get_match_recommendations: 1,
2453
+ query_field: 1
2454
+ };
2455
+ var TIER_PRICES = {
2456
+ L0: 0,
2457
+ L1: 0.01,
2458
+ L2: 0.1,
2459
+ L3: 0.3,
2460
+ L4: 0.6,
2461
+ L5: 1
2462
+ };
2463
+ var ADVERTISED_TOOL_COUNTS = {
2464
+ /** Free (L0) tools visible to anonymous / agent-class callers. */
2465
+ free: 26,
2466
+ /** Premium (L1-L5) tools requiring x402 payment. */
2467
+ premium: 8,
2468
+ /** Messaging tools (member-self-only; excluded from external advertisement). */
2469
+ messaging: 0,
2470
+ /** Total publicly advertised (free + premium). */
2471
+ total: 34
2472
+ };
2473
+ var REVENUE_SPLIT = {
2474
+ weaver: 0.75,
2475
+ // data subject (Identity Income)
2476
+ facilitator: 0.05,
2477
+ alter: 0.15,
2478
+ treasury: 0.05
2479
+ };
2480
+
1899
2481
  // src/homepage.ts
1900
2482
  var HOMEPAGE_LIMITS = {
1901
2483
  whoami_max_chars: 240,
1902
2484
  opener_max_chars: 280,
1903
2485
  pronouns_max_chars: 32,
1904
- attunement_glyph_max_chars: 16
2486
+ attunement_glyph_max_chars: 16,
2487
+ contact_email_max_chars: 254
1905
2488
  };
1906
2489
 
1907
2490
  // src/themes.ts
@@ -1914,4 +2497,4 @@ var THEME_LIMITS = {
1914
2497
  };
1915
2498
  var OSC8_ALLOWED_SCHEMES = ["https:", "mailto:"];
1916
2499
 
1917
- export { ALL_CLIENTS, AlterAuthError, AlterClient, AlterDiscoveryError, AlterError, AlterInvalidResponse, AlterNetworkError, AlterPaymentRequired, AlterProvenanceError, AlterRateLimited, AlterTimeoutError, AlterToolError, CLAUDE_CODE, CLAUDE_DESKTOP, CURSOR, DEFAULT_DOMAIN, DEFAULT_ENDPOINT, DEFAULT_VERIFY_AT_ALLOWLIST, FREE_TOOL_NAMES, HOMEPAGE_LIMITS, MCPClient, MCP_PROTOCOL_VERSION, OSC8_ALLOWED_SCHEMES, PREMIUM_TOOL_NAMES, SDK_NAME, SDK_VERSION, THEME_LIMITS, TOOL_BLAST_RADIUS, TOOL_COSTS, TOOL_TIERS, VSCODE, X402Client, base64urlDecode, base64urlEncode2 as base64urlEncode, canonicalArgsSha256, canonicalStringify, clearDiscoveryCache, decodeDid, detectSyncedVolume, discover, encodeDid, fetchPublicKeys, generateClaudeConfig, generateClaudeDesktopConfig, generateCursorConfig, generateGenericMcpConfig, generateKeypair, keypairFromPrivateKey, loadPrivateKey, parsePaymentHeader, probeAll, probeByDir, probeClaudeCode, readWireState, resolveVerifyAt, sha2562 as sha256, sign, signInvocation, unwire, verify, verifyProvenance, verifyToolSignatures, wire, writeWireState };
2500
+ export { ADVERTISED_TOOL_COUNTS, ALL_CLIENTS, AlterAuthError, AlterClient, AlterDiscoveryError, AlterError, AlterInvalidResponse, AlterNetworkError, AlterPaymentRequired, AlterProvenanceError, AlterRateLimited, AlterTimeoutError, AlterToolError, BelowFloorError, CLAUDE_CODE, CLAUDE_DESKTOP, CLIENT_CHANNEL, CLIENT_ID, CURSOR, DEFAULT_DOMAIN, DEFAULT_ENDPOINT, DEFAULT_VERIFY_AT_ALLOWLIST, FREE_TOOL_NAMES, GENERATED_TOOL_PRICING, GENERATED_TOOL_TIERS, HOMEPAGE_LIMITS, KNOWN_FLOOR_PUBLIC_KEYS, MCPClient, MCP_PROTOCOL_VERSION, MIN_VERSION_ENDPOINT, OSC8_ALLOWED_SCHEMES, PREMIUM_TOOL_NAMES, REVENUE_SPLIT, SDK_NAME, SDK_VERSION, THEME_LIMITS, TIER_PRICES, TOOL_BLAST_RADIUS, TOOL_COSTS, TOOL_TIERS, VSCODE, X402Client, base64urlDecode, base64urlEncode2 as base64urlEncode, canonicalArgsSha256, canonicalJson, canonicalStringify, checkMinVersion, clearDiscoveryCache, compareSemver, computeKeyId, decodeDid, detectSyncedVolume, discover, encodeDid, fetchPublicKeys, generateClaudeConfig, generateClaudeDesktopConfig, generateCursorConfig, generateGenericMcpConfig, generateKeypair, keypairFromPrivateKey, loadPrivateKey, parsePaymentHeader, probeAll, probeByDir, probeClaudeCode, readWireState, resolveVerifyAt, sha2562 as sha256, sign, signInvocation, unwire, verify, verifyFloorSignature, verifyProvenance, verifyToolSignatures, wire, writeWireState };