@truealter/sdk 0.5.1 → 0.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -4,11 +4,12 @@ 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 ed25519 = require('@noble/ed25519');
8
- var sha512 = require('@noble/hashes/sha512');
9
7
  var fs = require('fs');
10
- var path = require('path');
11
8
  var os = require('os');
9
+ var path = require('path');
10
+ var ed25519 = require('@noble/ed25519');
11
+ var sha512 = require('@noble/hashes/sha512');
12
+ var url = require('url');
12
13
  var child_process = require('child_process');
13
14
  var process$1 = require('process');
14
15
 
@@ -36,14 +37,13 @@ var __defProp = Object.defineProperty;
36
37
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
37
38
  var __getOwnPropNames = Object.getOwnPropertyNames;
38
39
  var __hasOwnProp = Object.prototype.hasOwnProperty;
39
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
40
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
41
- }) : x)(function(x) {
42
- if (typeof require !== "undefined") return require.apply(this, arguments);
43
- throw Error('Dynamic require of "' + x + '" is not supported');
44
- });
45
- var __esm = (fn, res) => function __init() {
46
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
40
+ var __esm = (fn, res, err) => function __init() {
41
+ if (err) throw err[0];
42
+ try {
43
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
44
+ } catch (e) {
45
+ throw err = [e], e;
46
+ }
47
47
  };
48
48
  var __export = (target, all) => {
49
49
  for (var name in all)
@@ -60,8 +60,11 @@ var __copyProps = (to, from, except, desc) => {
60
60
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
61
61
 
62
62
  // node_modules/tsup/assets/cjs_shims.js
63
+ var getImportMetaUrl, importMetaUrl;
63
64
  var init_cjs_shims = __esm({
64
65
  "node_modules/tsup/assets/cjs_shims.js"() {
66
+ 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;
67
+ importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
65
68
  }
66
69
  });
67
70
 
@@ -408,11 +411,11 @@ async function tryWellKnown(host, file, timeoutMs, fetchImpl) {
408
411
  }
409
412
  if (resp.type === "opaqueredirect" || resp.status >= 300 && resp.status < 400) {
410
413
  throw new AlterNetworkError(
411
- `${url} \u2192 redirect rejected (discovery must not follow redirects; validate the server configuration)`
414
+ `${url} -> redirect rejected (discovery must not follow redirects; validate the server configuration)`
412
415
  );
413
416
  }
414
417
  if (resp.status === 404) return null;
415
- if (!resp.ok) throw new AlterNetworkError(`${url} \u2192 HTTP ${resp.status}`);
418
+ if (!resp.ok) throw new AlterNetworkError(`${url} -> HTTP ${resp.status}`);
416
419
  const doc = await resp.json();
417
420
  if (file === "mcp.json") {
418
421
  const remotes = doc.remotes || [];
@@ -446,6 +449,313 @@ function ensureMcpPath(url) {
446
449
  }
447
450
  }
448
451
 
452
+ // src/floor-preflight.ts
453
+ init_cjs_shims();
454
+
455
+ // src/meta.ts
456
+ init_cjs_shims();
457
+ var SDK_NAME = "@truealter/sdk";
458
+ var SDK_VERSION = "0.5.8" ;
459
+
460
+ // src/floor-preflight.ts
461
+ var MIN_VERSION_ENDPOINT = "/v1/clients/min-version";
462
+ var CLIENT_ID = "alter-identity";
463
+ var CLIENT_CHANNEL = "npm";
464
+ var IN_MEMORY_TTL_DEFAULT_MS = 60 * 60 * 1e3;
465
+ var IN_MEMORY_TTL_MIN_MS = 60 * 1e3;
466
+ var IN_MEMORY_TTL_MAX_MS = 24 * 60 * 60 * 1e3;
467
+ var DISK_FRESH_MS = 24 * 60 * 60 * 1e3;
468
+ var DISK_WARN_MS = 7 * 24 * 60 * 60 * 1e3;
469
+ var FETCH_TIMEOUT_MS = 4e3;
470
+ function computeKeyId(publicKeyPem) {
471
+ if (!publicKeyPem) return "00000000";
472
+ const pub = crypto$1.createPublicKey({ key: publicKeyPem, format: "pem" });
473
+ const jwk = pub.export({ format: "jwk" });
474
+ const rawBytes = Buffer.from(jwk.x, "base64url");
475
+ return crypto$1.createHash("sha256").update(rawBytes).digest("hex").slice(0, 8);
476
+ }
477
+ function canonicalJson(obj) {
478
+ return JSON.stringify(sortKeysDeep(obj));
479
+ }
480
+ function sortKeysDeep(value) {
481
+ if (Array.isArray(value)) {
482
+ return value.map(sortKeysDeep);
483
+ }
484
+ if (value !== null && typeof value === "object") {
485
+ const obj = value;
486
+ const sorted = {};
487
+ for (const k of Object.keys(obj).sort()) {
488
+ sorted[k] = sortKeysDeep(obj[k]);
489
+ }
490
+ return sorted;
491
+ }
492
+ return value;
493
+ }
494
+ var KNOWN_FLOOR_PUBLIC_KEYS = {
495
+ "8aa59e05": `-----BEGIN PUBLIC KEY-----
496
+ MCowBQYDK2VwAyEAgqw28dlniOuiTE1f4BxCPSEgMLaPtHsO8wN5RWEwEhE=
497
+ -----END PUBLIC KEY-----`,
498
+ "640f7d9a": `-----BEGIN PUBLIC KEY-----
499
+ MCowBQYDK2VwAyEARzvAWayDwHvZRfOZizGZe+/a7PF082WGhyMS3tx06H4=
500
+ -----END PUBLIC KEY-----`
501
+ };
502
+ var BelowFloorError = class extends Error {
503
+ name = "BelowFloorError";
504
+ code = "client_below_floor";
505
+ client_version;
506
+ min_version;
507
+ upgrade_cmd;
508
+ channel;
509
+ envelope;
510
+ constructor(envelope) {
511
+ super(envelope.error.message);
512
+ this.envelope = envelope;
513
+ this.client_version = envelope.error.client_version;
514
+ this.min_version = envelope.error.min_version;
515
+ this.upgrade_cmd = envelope.error.upgrade_cmd;
516
+ this.channel = envelope.error.channel;
517
+ Object.setPrototypeOf(this, new.target.prototype);
518
+ }
519
+ };
520
+ var memCache = null;
521
+ async function checkMinVersion(opts = {}) {
522
+ const apiBase = opts.apiBase ?? defaultApiBase();
523
+ const clientVersion = opts.clientVersion ?? SDK_VERSION;
524
+ const clientId = opts.clientId ?? CLIENT_ID;
525
+ const channel = opts.channel ?? CLIENT_CHANNEL;
526
+ const knownKeys = opts.knownFloorPublicKeys ?? KNOWN_FLOOR_PUBLIC_KEYS;
527
+ const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
528
+ const now = opts.now ?? Date.now;
529
+ const cachePath = opts.diskCachePath === void 0 ? defaultDiskCachePath() : opts.diskCachePath;
530
+ const mem = readInMemoryCache(now);
531
+ if (mem) {
532
+ return compareAndPermit(mem, {
533
+ clientVersion,
534
+ clientId,
535
+ channel,
536
+ diagnostic: "mem-cache-hit"
537
+ });
538
+ }
539
+ const disk = cachePath ? readDiskCache(cachePath, knownKeys) : null;
540
+ const diskAgeMs = disk ? now() - disk.fetched_at_ms : Number.POSITIVE_INFINITY;
541
+ let fetched = null;
542
+ let fetchError = null;
543
+ if (!disk || diskAgeMs > IN_MEMORY_TTL_DEFAULT_MS) {
544
+ try {
545
+ fetched = await fetchFloorDoc(apiBase, fetchImpl, knownKeys);
546
+ } catch (err) {
547
+ fetchError = err.message ?? "fetch-error";
548
+ }
549
+ }
550
+ if (fetched) {
551
+ populateMemCache(fetched, now());
552
+ if (cachePath) writeDiskCache(cachePath, fetched, now());
553
+ return compareAndPermit(fetched, {
554
+ clientVersion,
555
+ clientId,
556
+ channel,
557
+ diagnostic: "fetched"
558
+ });
559
+ }
560
+ if (disk) {
561
+ populateMemCache(disk.doc, disk.fetched_at_ms);
562
+ if (diskAgeMs > DISK_WARN_MS) {
563
+ return compareAndPermit(disk.doc, {
564
+ clientVersion,
565
+ clientId,
566
+ channel,
567
+ diagnostic: "below-floor-offline-stale-or-permit",
568
+ warn: `floor cache is >7d old and backend unreachable (${fetchError ?? "no refresh attempted"}); permitting if above floor`
569
+ });
570
+ }
571
+ if (diskAgeMs > DISK_FRESH_MS) {
572
+ return compareAndPermit(disk.doc, {
573
+ clientVersion,
574
+ clientId,
575
+ channel,
576
+ diagnostic: "warn-stale-permit",
577
+ warn: `floor cache is ${Math.round(diskAgeMs / (60 * 60 * 1e3))}h old; refresh recommended`
578
+ });
579
+ }
580
+ return compareAndPermit(disk.doc, {
581
+ clientVersion,
582
+ clientId,
583
+ channel,
584
+ diagnostic: "disk-cache-hit"
585
+ });
586
+ }
587
+ return {
588
+ ok: true,
589
+ floor: null,
590
+ diagnostic: "no-cache-no-fetch-permit",
591
+ warn: `floor preflight skipped: backend unreachable (${fetchError ?? "unknown"})`
592
+ };
593
+ }
594
+ function compareAndPermit(doc, ctx) {
595
+ const floor = lookupFloor(doc, ctx.clientId, ctx.channel);
596
+ if (!floor) {
597
+ return { ok: true, floor: null, diagnostic: `${ctx.diagnostic}+no-floor`, warn: ctx.warn };
598
+ }
599
+ if (compareSemver(ctx.clientVersion, floor.min_version) >= 0) {
600
+ return { ok: true, floor, diagnostic: ctx.diagnostic, warn: ctx.warn };
601
+ }
602
+ const envelope = {
603
+ error: {
604
+ code: "client_below_floor",
605
+ message: `Your ${ctx.clientId} is too old. Upgrade required.`,
606
+ client_version: ctx.clientVersion,
607
+ min_version: floor.min_version,
608
+ upgrade_cmd: floor.upgrade_cmd,
609
+ channel: ctx.channel
610
+ }
611
+ };
612
+ throw new BelowFloorError(envelope);
613
+ }
614
+ function lookupFloor(doc, clientId, channel) {
615
+ const entry = doc.floors[clientId];
616
+ if (!entry) return null;
617
+ if (isChannelFloor(entry)) return entry;
618
+ const exact = entry[channel];
619
+ if (exact) return exact;
620
+ const fallback = entry["unknown"];
621
+ if (fallback) return fallback;
622
+ return null;
623
+ }
624
+ function isChannelFloor(v) {
625
+ return typeof v.min_version === "string" && typeof v.upgrade_cmd === "string";
626
+ }
627
+ function compareSemver(a, b) {
628
+ const [aMaj, aMin, aPat, aPre] = parseSemver(a);
629
+ const [bMaj, bMin, bPat, bPre] = parseSemver(b);
630
+ if (aMaj !== bMaj) return aMaj - bMaj;
631
+ if (aMin !== bMin) return aMin - bMin;
632
+ if (aPat !== bPat) return aPat - bPat;
633
+ if (aPre && !bPre) return -1;
634
+ if (!aPre && bPre) return 1;
635
+ if (aPre && bPre) return aPre.localeCompare(bPre);
636
+ return 0;
637
+ }
638
+ function parseSemver(v) {
639
+ const m = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?/.exec(v);
640
+ if (!m) return [0, 0, 0, null];
641
+ return [Number(m[1]), Number(m[2]), Number(m[3]), m[4] ?? null];
642
+ }
643
+ function verifyFloorSignature(doc, keys = KNOWN_FLOOR_PUBLIC_KEYS) {
644
+ const pem = keys[doc.key_id];
645
+ if (!pem) return false;
646
+ if (computeKeyId(pem) !== doc.key_id) return false;
647
+ try {
648
+ const pubKeyObject = crypto$1.createPublicKey({ key: pem, format: "pem" });
649
+ const canonical = canonicalJson({
650
+ floors: doc.floors,
651
+ served_at: doc.served_at
652
+ });
653
+ return crypto$1.verify(
654
+ null,
655
+ Buffer.from(canonical, "utf-8"),
656
+ pubKeyObject,
657
+ Buffer.from(doc.signature, "hex")
658
+ );
659
+ } catch {
660
+ return false;
661
+ }
662
+ }
663
+ async function fetchFloorDoc(apiBase, fetchImpl, knownKeys) {
664
+ const url = `${apiBase.replace(/\/+$/, "")}${MIN_VERSION_ENDPOINT}`;
665
+ let response;
666
+ try {
667
+ response = await fetchImpl(url, {
668
+ headers: {
669
+ accept: "application/json",
670
+ "X-Alter-Client-Id": CLIENT_ID,
671
+ "X-Alter-Client-Version": SDK_VERSION,
672
+ "X-Alter-Client-Channel": CLIENT_CHANNEL,
673
+ "User-Agent": `${SDK_NAME}/${SDK_VERSION}`
674
+ },
675
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
676
+ });
677
+ } catch (err) {
678
+ throw new Error(`network: ${err.message ?? String(err)}`);
679
+ }
680
+ if (!response.ok) throw new Error(`http-${response.status}`);
681
+ const body = await response.json();
682
+ if (!body || !body.floors || !body.signature || !body.key_id) {
683
+ throw new Error("malformed-floor-doc");
684
+ }
685
+ if (!verifyFloorSignature(body, knownKeys)) {
686
+ throw new Error("signature-invalid");
687
+ }
688
+ return body;
689
+ }
690
+ function readInMemoryCache(now) {
691
+ if (!memCache) return null;
692
+ if (now() - memCache.fetched_at_ms > memCache.ttl_ms) {
693
+ memCache = null;
694
+ return null;
695
+ }
696
+ return memCache.doc;
697
+ }
698
+ function populateMemCache(doc, fetched_at_ms) {
699
+ const ttlSec = doc.cache_ttl_seconds ?? 3600;
700
+ const ttlMs = Math.min(
701
+ Math.max(ttlSec * 1e3, IN_MEMORY_TTL_MIN_MS),
702
+ IN_MEMORY_TTL_MAX_MS
703
+ );
704
+ memCache = { doc, fetched_at_ms, ttl_ms: ttlMs };
705
+ }
706
+ function defaultDiskCachePath() {
707
+ const xdg = process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config");
708
+ return path.join(xdg, "alter", "floor-cache.json");
709
+ }
710
+ function readDiskCache(path, knownKeys) {
711
+ if (process.platform !== "win32") {
712
+ let st;
713
+ try {
714
+ st = fs.statSync(path);
715
+ } catch {
716
+ return null;
717
+ }
718
+ const euid = typeof process.geteuid === "function" ? process.geteuid() : st.uid;
719
+ if (st.uid !== euid) return null;
720
+ if ((st.mode & 511) !== 384) return null;
721
+ }
722
+ let raw;
723
+ try {
724
+ raw = fs.readFileSync(path, "utf-8");
725
+ } catch {
726
+ return null;
727
+ }
728
+ let parsed;
729
+ try {
730
+ parsed = JSON.parse(raw);
731
+ } catch {
732
+ return null;
733
+ }
734
+ if (!parsed.doc || typeof parsed.fetched_at_ms !== "number") return null;
735
+ if (!verifyFloorSignature(parsed.doc, knownKeys)) {
736
+ return null;
737
+ }
738
+ return parsed;
739
+ }
740
+ function writeDiskCache(path$1, doc, now_ms) {
741
+ const entry = { doc, fetched_at_ms: now_ms };
742
+ const payload = JSON.stringify(entry);
743
+ try {
744
+ fs.mkdirSync(path.dirname(path$1), { recursive: true, mode: 448 });
745
+ const tmp = `${path$1}.tmp`;
746
+ fs.writeFileSync(tmp, payload, { mode: 384 });
747
+ try {
748
+ fs.chmodSync(tmp, 384);
749
+ } catch {
750
+ }
751
+ fs.renameSync(tmp, path$1);
752
+ } catch {
753
+ }
754
+ }
755
+ function defaultApiBase() {
756
+ return process.env.ALTER_API ?? "https://api.truealter.com";
757
+ }
758
+
449
759
  // src/mcp.ts
450
760
  init_cjs_shims();
451
761
 
@@ -561,6 +871,9 @@ var MCPClient = class {
561
871
  x402;
562
872
  signing;
563
873
  extraHeaders;
874
+ preflightHook;
875
+ preflightPromise = null;
876
+ preflightDone = false;
564
877
  requestCounter = 0;
565
878
  initialised = false;
566
879
  constructor(opts = {}) {
@@ -569,17 +882,43 @@ var MCPClient = class {
569
882
  this.fetchImpl = opts.fetch ?? fetch;
570
883
  this.timeoutMs = opts.timeoutMs ?? 3e4;
571
884
  this.maxRetries = opts.maxRetries ?? 2;
572
- this.clientInfo = opts.clientInfo ?? { name: "@truealter/sdk", version: "0.2.0" };
885
+ this.clientInfo = opts.clientInfo ?? { name: SDK_NAME, version: SDK_VERSION };
573
886
  this.x402 = opts.x402;
574
887
  this.signing = opts.signing;
575
888
  this.extraHeaders = opts.extraHeaders;
889
+ this.preflightHook = opts.preflightHook;
890
+ }
891
+ /**
892
+ * Run the lazy version-floor preflight hook exactly once.
893
+ * Idempotent and serialised: concurrent callers share the same
894
+ * promise. Throws from the hook propagate to every concurrent caller.
895
+ */
896
+ async runPreflight() {
897
+ if (this.preflightDone) return;
898
+ if (!this.preflightHook) {
899
+ this.preflightDone = true;
900
+ return;
901
+ }
902
+ if (!this.preflightPromise) {
903
+ this.preflightPromise = this.preflightHook().then(
904
+ () => {
905
+ this.preflightDone = true;
906
+ },
907
+ (err) => {
908
+ this.preflightPromise = null;
909
+ throw err;
910
+ }
911
+ );
912
+ }
913
+ await this.preflightPromise;
576
914
  }
577
915
  /**
578
916
  * Send the MCP `initialize` handshake and capture the resulting session
579
- * id. Idempotent safe to call multiple times.
917
+ * id. Idempotent: safe to call multiple times.
580
918
  */
581
919
  async initialize() {
582
920
  if (this.initialised) return null;
921
+ await this.runPreflight();
583
922
  const result = await this.rpc("initialize", {
584
923
  protocolVersion: MCP_PROTOCOL_VERSION,
585
924
  capabilities: {},
@@ -672,7 +1011,14 @@ var MCPClient = class {
672
1011
  method: "POST",
673
1012
  headers: this.buildHeaders(signatureHeader),
674
1013
  body: JSON.stringify(payload),
675
- signal: controller.signal
1014
+ signal: controller.signal,
1015
+ // Prevent fetch from silently following 3xx redirects. When
1016
+ // Cloudflare Access credentials are absent or expired the edge
1017
+ // returns HTTP 302 → CF Access login page (text/html). Without
1018
+ // this option undici follows the redirect, lands on a 200 HTML
1019
+ // body, and resp.json() throws the opaque "invalid JSON body"
1020
+ // error that was surfaced as "MCP <method>: invalid JSON body".
1021
+ redirect: "manual"
676
1022
  });
677
1023
  } catch (err) {
678
1024
  clearTimeout(timer);
@@ -689,6 +1035,19 @@ var MCPClient = class {
689
1035
  clearTimeout(timer);
690
1036
  const sessionHeader = resp.headers.get("Mcp-Session-Id");
691
1037
  if (sessionHeader) this.sessionId = sessionHeader;
1038
+ if (resp.status >= 300 && resp.status < 400) {
1039
+ const location = resp.headers.get("Location") ?? "";
1040
+ const isAuthRedirect = location.includes("cloudflareaccess.com") || location.includes("/cdn-cgi/access/") || !location.startsWith("/") && !location.startsWith(new URL(this.endpoint).origin);
1041
+ if (isAuthRedirect) {
1042
+ throw new AlterAuthError(
1043
+ `MCP ${method}: Cloudflare Access blocked the request (session expired or credentials missing). Run \`alter login\` to re-authenticate.`,
1044
+ 302
1045
+ );
1046
+ }
1047
+ throw new AlterNetworkError(
1048
+ `MCP ${method}: unexpected redirect ${resp.status} to ${location || "(no Location)"}`
1049
+ );
1050
+ }
692
1051
  if (resp.status === 401 || resp.status === 403) {
693
1052
  throw new AlterAuthError(`HTTP ${resp.status} on ${method}`, resp.status);
694
1053
  }
@@ -713,11 +1072,56 @@ var MCPClient = class {
713
1072
  const body2 = await safeText(resp);
714
1073
  throw new AlterError("NETWORK", `HTTP ${resp.status} on ${method}: ${body2.slice(0, 200)}`);
715
1074
  }
1075
+ const contentType = resp.headers.get("Content-Type") ?? "";
1076
+ const isHtml = contentType.includes("text/html");
1077
+ const isSse = contentType.includes("text/event-stream");
1078
+ if (isHtml || isSse) {
1079
+ if (isSse) {
1080
+ const rawText = await safeText(resp);
1081
+ const dataLine = rawText.split("\n").find((l) => l.startsWith("data:"));
1082
+ if (dataLine) {
1083
+ const jsonPart = dataLine.slice("data:".length).trim();
1084
+ try {
1085
+ const parsed = JSON.parse(jsonPart);
1086
+ if (parsed.error) {
1087
+ const code = parsed.error.code;
1088
+ const message = parsed.error.message ?? `MCP ${method} error`;
1089
+ throw new AlterToolError(this.guessToolName(payload), message, code);
1090
+ }
1091
+ return parsed.result;
1092
+ } catch (parseErr) {
1093
+ if (parseErr instanceof AlterError) throw parseErr;
1094
+ throw new AlterInvalidResponse(
1095
+ `MCP ${method}: could not parse SSE data frame as JSON`,
1096
+ parseErr
1097
+ );
1098
+ }
1099
+ }
1100
+ throw new AlterInvalidResponse(
1101
+ `MCP ${method}: received text/event-stream response with no data: frame`
1102
+ );
1103
+ }
1104
+ const excerpt = (await safeText(resp)).slice(0, 300);
1105
+ const looksLikeLoginPage = excerpt.toLowerCase().includes("cloudflareaccess") || excerpt.toLowerCase().includes("access denied") || excerpt.toLowerCase().includes("<title>");
1106
+ if (looksLikeLoginPage) {
1107
+ throw new AlterAuthError(
1108
+ `MCP ${method}: received an HTML login page instead of JSON (Content-Type: ${contentType}). Run \`alter login\` to re-authenticate.`,
1109
+ 200
1110
+ );
1111
+ }
1112
+ throw new AlterInvalidResponse(
1113
+ `MCP ${method}: unexpected Content-Type "${contentType}" (expected application/json). Body excerpt: ${excerpt.slice(0, 120)}`
1114
+ );
1115
+ }
716
1116
  let body;
717
1117
  try {
718
1118
  body = await resp.json();
719
1119
  } catch (err) {
720
- throw new AlterInvalidResponse(`MCP ${method}: invalid JSON body`, err);
1120
+ const hint = contentType ? ` (Content-Type: ${contentType})` : "";
1121
+ throw new AlterInvalidResponse(
1122
+ `MCP ${method}: failed to parse JSON response${hint}. The server may have returned a non-JSON body. Run \`alter login\` if the session is expired.`,
1123
+ err
1124
+ );
721
1125
  }
722
1126
  if (body.error) {
723
1127
  const code = body.error.code;
@@ -738,8 +1142,11 @@ var MCPClient = class {
738
1142
  const headers = {
739
1143
  ...this.extraHeaders ?? {},
740
1144
  "Content-Type": "application/json",
741
- Accept: "application/json",
742
- "User-Agent": `${this.clientInfo.name}/${this.clientInfo.version}`
1145
+ Accept: "application/json, text/event-stream",
1146
+ "User-Agent": `${this.clientInfo.name}/${this.clientInfo.version}`,
1147
+ "X-Alter-Client-Id": "alter-identity",
1148
+ "X-Alter-Client-Version": SDK_VERSION,
1149
+ "X-Alter-Client-Channel": "npm"
743
1150
  };
744
1151
  if (this.apiKey) headers["X-ALTER-API-Key"] = this.apiKey;
745
1152
  if (this.sessionId) headers["Mcp-Session-Id"] = this.sessionId;
@@ -1022,7 +1429,7 @@ async function verifyToolSignatures(tools, signatures, opts = {}) {
1022
1429
  out.push({ tool: tool.name, valid: false, reason: "no signature published" });
1023
1430
  continue;
1024
1431
  }
1025
- const expectedHash = await sha256Hex(canonicalJson(tool.inputSchema));
1432
+ const expectedHash = await sha256Hex(canonicalJson2(tool.inputSchema));
1026
1433
  if (expectedHash !== sig.schema_hash) {
1027
1434
  out.push({ tool: tool.name, valid: false, reason: "schema hash mismatch" });
1028
1435
  continue;
@@ -1106,23 +1513,23 @@ async function fetchJwks(url, fetchImpl) {
1106
1513
  }
1107
1514
  if (resp.type === "opaqueredirect" || resp.status >= 300 && resp.status < 400) {
1108
1515
  throw new AlterProvenanceError(
1109
- `${url} \u2192 redirect rejected (allowlist enforces initial URL only)`
1516
+ `${url} -> redirect rejected (allowlist enforces initial URL only)`
1110
1517
  );
1111
1518
  }
1112
- if (!resp.ok) throw new AlterNetworkError(`${url} \u2192 HTTP ${resp.status}`);
1519
+ if (!resp.ok) throw new AlterNetworkError(`${url} -> HTTP ${resp.status}`);
1113
1520
  const contentLength = resp.headers.get("content-length");
1114
1521
  if (contentLength !== null) {
1115
1522
  const n = Number.parseInt(contentLength, 10);
1116
1523
  if (Number.isFinite(n) && n > JWKS_MAX_BYTES) {
1117
1524
  throw new AlterProvenanceError(
1118
- `${url} \u2192 JWKS too large: ${n} > ${JWKS_MAX_BYTES} bytes`
1525
+ `${url} -> JWKS too large: ${n} > ${JWKS_MAX_BYTES} bytes`
1119
1526
  );
1120
1527
  }
1121
1528
  }
1122
1529
  const body = await resp.text();
1123
1530
  if (body.length > JWKS_MAX_BYTES) {
1124
1531
  throw new AlterProvenanceError(
1125
- `${url} \u2192 JWKS too large: ${body.length} > ${JWKS_MAX_BYTES} bytes`
1532
+ `${url} -> JWKS too large: ${body.length} > ${JWKS_MAX_BYTES} bytes`
1126
1533
  );
1127
1534
  }
1128
1535
  let doc;
@@ -1206,19 +1613,35 @@ async function sha256Hex(input) {
1206
1613
  function toArrayBuffer(view) {
1207
1614
  return view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength);
1208
1615
  }
1209
- function canonicalJson(value) {
1616
+ function canonicalJson2(value) {
1210
1617
  if (value === null || typeof value !== "object") return JSON.stringify(value);
1211
1618
  if (Array.isArray(value)) {
1212
- return `[${value.map(canonicalJson).join(",")}]`;
1619
+ return `[${value.map(canonicalJson2).join(",")}]`;
1213
1620
  }
1214
1621
  const obj = value;
1215
1622
  const keys = Object.keys(obj).sort();
1216
- return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJson(obj[k])}`).join(",")}}`;
1623
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJson2(obj[k])}`).join(",")}}`;
1217
1624
  }
1218
1625
 
1219
1626
  // src/client.ts
1220
1627
  var DEFAULT_ENDPOINT = "https://mcp.truealter.com/api/v1/mcp";
1628
+ var MEMBER_BRIDGE_ENDPOINT = "https://api.truealter.com/api/v1/mcp";
1221
1629
  var DEFAULT_DOMAIN = "truealter.com";
1630
+ var CANONICAL_API_BASE = "https://api.truealter.com";
1631
+ var JWKS_WELL_KNOWN_PATH = "/.well-known/alter-keys.json";
1632
+ function resolveJwksUrl(opts = {}) {
1633
+ if (opts.jwksUrl) return opts.jwksUrl;
1634
+ const base = opts.apiBase ?? (typeof process !== "undefined" ? process.env?.ALTER_API : void 0) ?? originOf(opts.endpoint) ?? CANONICAL_API_BASE;
1635
+ return `${base.replace(/\/+$/, "")}${JWKS_WELL_KNOWN_PATH}`;
1636
+ }
1637
+ function originOf(url) {
1638
+ if (!url) return void 0;
1639
+ try {
1640
+ return new URL(url).origin;
1641
+ } catch {
1642
+ return void 0;
1643
+ }
1644
+ }
1222
1645
  var AlterClient = class {
1223
1646
  mcp;
1224
1647
  x402;
@@ -1229,11 +1652,21 @@ var AlterClient = class {
1229
1652
  this.options = options;
1230
1653
  this.x402 = options.x402;
1231
1654
  const endpoint = options.endpoint ?? DEFAULT_ENDPOINT;
1232
- this.mcp = new MCPClient({ ...options, endpoint, x402: options.x402 });
1655
+ const preflightHook = options.unsafe_skipVersionCheck ? void 0 : () => checkMinVersion({
1656
+ apiBase: options.apiBase,
1657
+ knownFloorPublicKeys: options.knownFloorPublicKeys,
1658
+ fetchImpl: options.fetch
1659
+ }).then(() => void 0);
1660
+ this.mcp = new MCPClient({
1661
+ ...options,
1662
+ endpoint,
1663
+ x402: options.x402,
1664
+ preflightHook
1665
+ });
1233
1666
  }
1234
1667
  /**
1235
1668
  * Resolve the MCP endpoint via discovery if requested. Safe to call
1236
- * multiple times the first successful lookup is cached.
1669
+ * multiple times: the first successful lookup is cached.
1237
1670
  */
1238
1671
  async discoverEndpoint() {
1239
1672
  if (this.discovered) return this.discovered;
@@ -1246,7 +1679,7 @@ var AlterClient = class {
1246
1679
  return this.discoveryPromise;
1247
1680
  }
1248
1681
  /**
1249
- * Initialise the MCP session. Optional every method calls
1682
+ * Initialise the MCP session. Optional: every method calls
1250
1683
  * `mcp.initialize()` lazily, but you can call this once at startup if
1251
1684
  * you want fail-fast behaviour.
1252
1685
  */
@@ -1254,11 +1687,11 @@ var AlterClient = class {
1254
1687
  await this.mcp.initialize();
1255
1688
  }
1256
1689
  // ── Free tier ────────────────────────────────────────────────────────
1257
- /** First handshake confirms the connection, returns trust tier and tool counts. */
1690
+ /** First handshake: confirms the connection, returns trust tier and tool counts. */
1258
1691
  async helloAgent() {
1259
1692
  return this.mcp.callTool("hello_agent", {});
1260
1693
  }
1261
- /** Resolve a ~handle (e.g. ~drew) to its canonical form and kind. No auth required. */
1694
+ /** Resolve a ~handle (e.g. ~example) to its canonical form and kind. No auth required. */
1262
1695
  async resolveHandle(args) {
1263
1696
  const payload = typeof args === "string" ? { query: args } : args;
1264
1697
  return this.mcp.callTool("alter_resolve_handle", payload);
@@ -1266,7 +1699,7 @@ var AlterClient = class {
1266
1699
  /** Verify a person is registered with ALTER (handle or id). */
1267
1700
  async verify(handleOrId, claims) {
1268
1701
  const args = handleOrId.includes("@") ? { member_id: "", email: handleOrId } : handleOrId.startsWith("~") ? (
1269
- // ~handle server resolves these via the member_id field
1702
+ // ~handle: server resolves these via the member_id field
1270
1703
  { member_id: handleOrId }
1271
1704
  ) : { member_id: handleOrId };
1272
1705
  if (claims) args.claims = claims;
@@ -1366,8 +1799,8 @@ var AlterClient = class {
1366
1799
  }
1367
1800
  // ── Alter-to-Alter Messaging ─────────────────────────────────────────
1368
1801
  // Wave 1: cross-handle direct messages between authenticated tilde
1369
- // handles. Default closed recipient must have granted the sender via
1370
- // alter_message_grant. Spec: docs/technical/Alter-to-Alter Messaging.md.
1802
+ // handles. Default closed: recipient must have granted the sender via
1803
+ // alter_message_grant. Spec: the ALTER Alter-to-Alter Messaging spec.
1371
1804
  /** Send a direct message to another tilde handle. */
1372
1805
  async messageSend(args) {
1373
1806
  return this.mcp.callTool("alter_message_send", args);
@@ -1400,12 +1833,16 @@ var AlterClient = class {
1400
1833
  /**
1401
1834
  * Verify the ES256 provenance attestation on a tool response.
1402
1835
  * Accepts either a {@link ProvenanceEnvelope} or the raw `_meta`
1403
- * object the latter is more convenient for ad-hoc verification.
1836
+ * object: the latter is more convenient for ad-hoc verification.
1404
1837
  */
1405
1838
  async verifyProvenance(envelope) {
1406
1839
  if (!envelope) return { valid: false, reason: "no provenance envelope" };
1407
1840
  const inner = envelope.provenance ?? envelope;
1408
1841
  return verifyProvenance(inner, {
1842
+ // Pass an explicit jwksUrl only when the caller pinned one. Leaving it
1843
+ // undefined preserves the envelope `verify_at` (allowlist-gated)
1844
+ // resolution path inside verifyProvenance; the allowlist is the
1845
+ // trust-anchor gate there, not a bare hardcoded host.
1409
1846
  jwksUrl: this.options.jwksUrl,
1410
1847
  verifyAtAllowlist: this.options.verifyAtAllowlist
1411
1848
  });
@@ -1418,9 +1855,21 @@ var AlterClient = class {
1418
1855
  async verifyToolSignatures(tools, signatures) {
1419
1856
  return verifyToolSignatures(tools, signatures);
1420
1857
  }
1421
- /** Fetch the published JWKS for ALTER's signing key (cached 5 min). */
1858
+ /**
1859
+ * Fetch the published JWKS for ALTER's signing key (cached 5 min).
1860
+ *
1861
+ * The JWKS URL is derived from the configured API surface
1862
+ * ({@link resolveJwksUrl}: explicit `jwksUrl` > `apiBase` > `ALTER_API` >
1863
+ * the configured `endpoint` origin > the canonical host) rather than a
1864
+ * single hardcoded host, so the trust anchor tracks the surface the
1865
+ * client is actually pointed at.
1866
+ */
1422
1867
  async fetchPublicKeys() {
1423
- const url = this.options.jwksUrl ?? "https://api.truealter.com/.well-known/alter-keys.json";
1868
+ const url = resolveJwksUrl({
1869
+ jwksUrl: this.options.jwksUrl,
1870
+ apiBase: this.options.apiBase,
1871
+ endpoint: this.options.endpoint
1872
+ });
1424
1873
  return fetchPublicKeys(url);
1425
1874
  }
1426
1875
  };
@@ -1440,7 +1889,7 @@ function generateGenericMcpConfig(opts = {}) {
1440
1889
  const entry = {
1441
1890
  url: opts.endpoint ?? DEFAULT_ENDPOINT,
1442
1891
  transport: "streamable-http",
1443
- description: "ALTER Identity \u2014 psychometric identity field for AI agents"
1892
+ description: "ALTER Identity: psychometric identity field for AI agents"
1444
1893
  };
1445
1894
  if (Object.keys(headers).length > 0) entry.headers = headers;
1446
1895
  return { mcpServers: { [serverName]: entry } };
@@ -1461,17 +1910,20 @@ function generateCursorConfig(opts = {}) {
1461
1910
  init_cjs_shims();
1462
1911
  function generateClaudeDesktopConfig(opts = {}) {
1463
1912
  const serverName = opts.serverName ?? "alter";
1464
- const bridgeCommand = opts.bridgeCommand ?? "alter-mcp-bridge";
1913
+ const bridgeCommand = opts.bridgeCommand ?? "alter";
1465
1914
  const env2 = {};
1466
- env2.ALTER_MCP_ENDPOINT = opts.endpoint ?? DEFAULT_ENDPOINT;
1915
+ env2.ALTER_MCP_ENDPOINT = opts.endpoint ?? MEMBER_BRIDGE_ENDPOINT;
1467
1916
  if (opts.apiKey) env2.ALTER_API_KEY = opts.apiKey;
1468
1917
  const entry = {
1469
1918
  command: bridgeCommand,
1919
+ // The `mcp-bridge` subcommand is always the first arg now that the bridge
1920
+ // is invoked through the `alter` CLI, not a bare bridge binary.
1921
+ args: ["mcp-bridge"],
1470
1922
  env: env2,
1471
- description: "ALTER Identity \u2014 psychometric identity field for AI agents"
1923
+ description: "ALTER Identity: psychometric identity field for AI agents"
1472
1924
  };
1473
1925
  if (opts.extraArgs && opts.extraArgs.length > 0) {
1474
- entry.args = [...opts.extraArgs];
1926
+ entry.args = [...entry.args, ...opts.extraArgs];
1475
1927
  }
1476
1928
  return { mcpServers: { [serverName]: entry } };
1477
1929
  }
@@ -1479,11 +1931,6 @@ function generateClaudeDesktopConfig(opts = {}) {
1479
1931
  // src/wire/index.ts
1480
1932
  init_cjs_shims();
1481
1933
 
1482
- // src/meta.ts
1483
- init_cjs_shims();
1484
- var SDK_NAME = "@truealter/sdk";
1485
- var SDK_VERSION = "0.3.0";
1486
-
1487
1934
  // src/wire/paths.ts
1488
1935
  init_cjs_shims();
1489
1936
  var HOME = os.homedir();
@@ -1616,7 +2063,7 @@ function probeAll() {
1616
2063
  // src/wire/sync.ts
1617
2064
  init_cjs_shims();
1618
2065
  var SYNC_PREFIXES = [
1619
- // iCloud Drive both the new and legacy mounts.
2066
+ // iCloud Drive: both the new and legacy mounts.
1620
2067
  "Library/Mobile Documents/com~apple~CloudDocs",
1621
2068
  "iCloud Drive",
1622
2069
  // OneDrive variants Microsoft ships across editions.
@@ -1629,7 +2076,7 @@ var SYNC_PREFIXES = [
1629
2076
  "Google Drive",
1630
2077
  "GoogleDrive",
1631
2078
  "CloudStorage/GoogleDrive",
1632
- // Box, pCloud, Sync.com, MEGA high-signal names worth refusing.
2079
+ // Box, pCloud, Sync.com, MEGA: high-signal names worth refusing.
1633
2080
  "Box Sync",
1634
2081
  "pCloud Drive",
1635
2082
  "Sync.com",
@@ -1687,10 +2134,10 @@ function atomicJsonMerge(opts) {
1687
2134
  preBytes = fs.readFileSync(path$1, "utf8");
1688
2135
  if (preBytes.trim().length > 0) {
1689
2136
  try {
1690
- parsed = JSON.parse(preBytes);
2137
+ parsed = JSON.parse(preBytes.replace(/^\uFEFF/, ""));
1691
2138
  } catch (err) {
1692
2139
  throw new Error(
1693
- `refusing to wire ${path$1}: existing file is not valid JSON (${err.message}). Hand-fix the file, then re-run \`alter-identity wire\`.`
2140
+ `refusing to wire ${path$1}: existing file is not valid JSON (${err.message}). Hand-fix the file, then re-run \`alter wire\`.`
1694
2141
  );
1695
2142
  }
1696
2143
  if (typeof parsed !== "object" || Array.isArray(parsed) || parsed === null) {
@@ -1772,6 +2219,7 @@ function wire(opts = {}) {
1772
2219
  const endpoint = opts.endpoint ?? DEFAULT_ENDPOINT;
1773
2220
  const apiKey = opts.apiKey;
1774
2221
  const cfAccess = opts.cfAccess ?? readCfAccessEnv();
2222
+ const launcherPath = opts.launcherPath;
1775
2223
  const probes = probeAll();
1776
2224
  const selection = opts.only ?? probes.filter((p) => p.installed).map((p) => p.client.id);
1777
2225
  const ts = TIMESTAMP();
@@ -1790,7 +2238,7 @@ function wire(opts = {}) {
1790
2238
  }
1791
2239
  try {
1792
2240
  if (id === "claude-code") {
1793
- targets.push(wireClaudeCode({ endpoint, apiKey, cfAccess }));
2241
+ targets.push(wireClaudeCode({ endpoint, apiKey, cfAccess, launcherPath }));
1794
2242
  } else {
1795
2243
  targets.push(wireFileTarget({ id, endpoint, apiKey, cfAccess, timestamp: ts }));
1796
2244
  }
@@ -1823,7 +2271,7 @@ function wireFileTarget(args) {
1823
2271
  const sync = detectSyncedVolume(client.configPath);
1824
2272
  if (sync) {
1825
2273
  throw new Error(
1826
- `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.`
2274
+ `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.`
1827
2275
  );
1828
2276
  }
1829
2277
  const cfHeaders = {};
@@ -1861,10 +2309,28 @@ function wireFileTarget(args) {
1861
2309
  postSha256: result.postSha256
1862
2310
  };
1863
2311
  }
1864
- function wireClaudeCode(args) {
1865
- const cmd = "claude";
1866
- const bridgePath = resolveBridgeScript();
1867
- const argList = bridgePath ? ["mcp", "add", "--scope", "user", "alter", "--", "node", bridgePath] : [
2312
+ function redactSecret(text, secret) {
2313
+ if (!secret) return text;
2314
+ return text.split(secret).join("***redacted***");
2315
+ }
2316
+ function buildClaudeCodeAddArgs(args) {
2317
+ if (args.subprocessArgv) {
2318
+ return [
2319
+ "mcp",
2320
+ "add",
2321
+ "--scope",
2322
+ "user",
2323
+ "alter",
2324
+ "--env",
2325
+ `ALTER_MCP_ENDPOINT=${MEMBER_BRIDGE_ENDPOINT}`,
2326
+ "--env",
2327
+ `ALTER_PUBLIC_MCP_ENDPOINT=${MEMBER_BRIDGE_ENDPOINT}`,
2328
+ ...args.apiKey ? ["--env", `ALTER_API_KEY=${args.apiKey}`] : [],
2329
+ "--",
2330
+ ...args.subprocessArgv
2331
+ ];
2332
+ }
2333
+ return [
1868
2334
  "mcp",
1869
2335
  "add",
1870
2336
  "--scope",
@@ -1875,12 +2341,28 @@ function wireClaudeCode(args) {
1875
2341
  args.endpoint,
1876
2342
  ...args.apiKey ? ["--header", `X-ALTER-API-Key:${args.apiKey}`] : []
1877
2343
  ];
1878
- const full = `${cmd} ${argList.join(" ")}`;
2344
+ }
2345
+ function wireClaudeCode(args) {
2346
+ const cmd = "claude";
2347
+ const bridgePath = resolveBridgeScript();
2348
+ const launcher = args.launcherPath && fs.existsSync(args.launcherPath) ? args.launcherPath : null;
2349
+ const subprocessArgv = launcher ? ["node", launcher, "mcp-bridge"] : bridgePath ? ["node", bridgePath] : null;
2350
+ const argList = buildClaudeCodeAddArgs({
2351
+ apiKey: args.apiKey,
2352
+ subprocessArgv,
2353
+ endpoint: args.endpoint
2354
+ });
2355
+ const full = redactSecret(`${cmd} ${argList.join(" ")}`, args.apiKey);
1879
2356
  const run = child_process.spawnSync(cmd, argList, {
1880
2357
  encoding: "utf8",
1881
2358
  shell: process.platform === "win32",
1882
2359
  timeout: 1e4,
1883
- env: bridgePath ? { ...process.env, ALTER_PUBLIC_MCP_ENDPOINT: args.endpoint, ...args.apiKey ? { ALTER_API_KEY: args.apiKey } : {} } : void 0
2360
+ env: subprocessArgv ? {
2361
+ ...process.env,
2362
+ ALTER_MCP_ENDPOINT: MEMBER_BRIDGE_ENDPOINT,
2363
+ ALTER_PUBLIC_MCP_ENDPOINT: MEMBER_BRIDGE_ENDPOINT,
2364
+ ...args.apiKey ? { ALTER_API_KEY: args.apiKey } : {}
2365
+ } : void 0
1884
2366
  });
1885
2367
  if (run.error) {
1886
2368
  return {
@@ -1912,14 +2394,17 @@ function wireClaudeCode(args) {
1912
2394
  };
1913
2395
  }
1914
2396
  function resolveBridgeScript() {
1915
- const { existsSync: existsSync4 } = __require("fs");
1916
- const { join: join3, dirname: dirname3 } = __require("path");
1917
- const siblingBridge = join3(dirname3(__filename), "..", "dist", "mcp-bridge.js");
1918
- if (existsSync4(siblingBridge)) return siblingBridge;
1919
- const srcBridge = join3(dirname3(__filename), "..", "mcp-bridge.js");
1920
- if (existsSync4(srcBridge)) return srcBridge;
1921
- const npmGlobalBridge = join3(dirname3(__filename), "mcp-bridge.js");
1922
- if (existsSync4(npmGlobalBridge)) return npmGlobalBridge;
2397
+ const here = path.dirname(url.fileURLToPath(importMetaUrl));
2398
+ const distBinBridge = path.join(here, "bin", "mcp-bridge.js");
2399
+ if (fs.existsSync(distBinBridge)) return distBinBridge;
2400
+ const nestedDistBinBridge = path.join(here, "..", "dist", "bin", "mcp-bridge.js");
2401
+ if (fs.existsSync(nestedDistBinBridge)) return nestedDistBinBridge;
2402
+ const siblingBridge = path.join(here, "..", "dist", "mcp-bridge.js");
2403
+ if (fs.existsSync(siblingBridge)) return siblingBridge;
2404
+ const srcBridge = path.join(here, "..", "mcp-bridge.js");
2405
+ if (fs.existsSync(srcBridge)) return srcBridge;
2406
+ const npmGlobalBridge = path.join(here, "mcp-bridge.js");
2407
+ if (fs.existsSync(npmGlobalBridge)) return npmGlobalBridge;
1923
2408
  return null;
1924
2409
  }
1925
2410
  function unwire() {
@@ -2073,19 +2558,19 @@ var TOOL_COSTS = {
2073
2558
  complete_knot: 0,
2074
2559
  check_golden_thread: 0,
2075
2560
  thread_census: 0,
2076
- // L1 ($0.005)
2077
- assess_traits: 5e-3,
2078
- get_trait_snapshot: 5e-3,
2079
- // L2 ($0.01)
2080
- get_full_trait_vector: 0.01,
2081
- get_side_quest_graph: 0.01,
2082
- // L3 ($0.025)
2083
- query_graph_similarity: 0.025,
2084
- // L4 ($0.05)
2085
- compute_belonging: 0.05,
2086
- // L5 ($0.50)
2087
- get_match_recommendations: 0.5,
2088
- generate_match_narrative: 0.5
2561
+ // L1 ($0.01)
2562
+ assess_traits: 0.01,
2563
+ get_trait_snapshot: 0.01,
2564
+ // L2 ($0.10)
2565
+ get_full_trait_vector: 0.1,
2566
+ get_side_quest_graph: 0.1,
2567
+ // L3 ($0.30)
2568
+ query_graph_similarity: 0.3,
2569
+ // L4 ($0.60)
2570
+ compute_belonging: 0.6,
2571
+ // L5 ($1.00)
2572
+ get_match_recommendations: 1,
2573
+ generate_match_narrative: 1
2089
2574
  };
2090
2575
  var TOOL_BLAST_RADIUS = {
2091
2576
  // Low: read-only reference
@@ -2125,13 +2610,110 @@ var TOOL_BLAST_RADIUS = {
2125
2610
  query_graph_similarity: "high"
2126
2611
  };
2127
2612
 
2613
+ // src/pricing.generated.ts
2614
+ init_cjs_shims();
2615
+ var GENERATED_TOOL_TIERS = {
2616
+ // L0 (free)
2617
+ alter_presence_read: 0,
2618
+ alter_resolve_handle: 0,
2619
+ check_assessment_status: 0,
2620
+ create_identity_stub: 0,
2621
+ dispute_attestation: 0,
2622
+ get_competencies: 0,
2623
+ get_earning_summary: 0,
2624
+ get_engagement_level: 0,
2625
+ get_identity_earnings: 0,
2626
+ get_identity_trust_score: 0,
2627
+ get_network_stats: 0,
2628
+ get_privacy_budget: 0,
2629
+ get_profile: 0,
2630
+ initiate_assessment: 0,
2631
+ list_archetypes: 0,
2632
+ query_matches: 0,
2633
+ recommend_tool: 0,
2634
+ search_identities: 0,
2635
+ verify_identity: 0,
2636
+ // L1 ($0.01)
2637
+ assess_traits: 1,
2638
+ attest_domain: 1,
2639
+ get_trait_snapshot: 1,
2640
+ poll_requirement_matches: 1,
2641
+ submit_context: 1,
2642
+ submit_social_links: 1,
2643
+ submit_structured_profile: 1,
2644
+ // L2 ($0.10)
2645
+ attest_claim_provenance: 2,
2646
+ get_full_trait_vector: 2,
2647
+ get_side_quest_graph: 2,
2648
+ submit_batch_context: 2,
2649
+ // L3 ($0.30)
2650
+ alter_alignment: 3,
2651
+ query_graph_similarity: 3,
2652
+ // L4 ($0.60)
2653
+ compute_belonging: 4,
2654
+ // L5 ($1.00)
2655
+ generate_match_narrative: 5,
2656
+ get_match_recommendations: 5,
2657
+ query_field: 5
2658
+ };
2659
+ var GENERATED_TOOL_PRICING = {
2660
+ // L1: $0.01
2661
+ assess_traits: 0.01,
2662
+ attest_domain: 0.01,
2663
+ get_trait_snapshot: 0.01,
2664
+ poll_requirement_matches: 0.01,
2665
+ submit_context: 0.01,
2666
+ submit_social_links: 0.01,
2667
+ submit_structured_profile: 0.01,
2668
+ // L2: $0.10
2669
+ attest_claim_provenance: 0.1,
2670
+ get_full_trait_vector: 0.1,
2671
+ get_side_quest_graph: 0.1,
2672
+ submit_batch_context: 0.1,
2673
+ // L3: $0.30
2674
+ alter_alignment: 0.3,
2675
+ query_graph_similarity: 0.3,
2676
+ // L4: $0.60
2677
+ compute_belonging: 0.6,
2678
+ // L5: $1.00
2679
+ generate_match_narrative: 1,
2680
+ get_match_recommendations: 1,
2681
+ query_field: 1
2682
+ };
2683
+ var TIER_PRICES = {
2684
+ L0: 0,
2685
+ L1: 0.01,
2686
+ L2: 0.1,
2687
+ L3: 0.3,
2688
+ L4: 0.6,
2689
+ L5: 1
2690
+ };
2691
+ var ADVERTISED_TOOL_COUNTS = {
2692
+ /** Free (L0) tools visible to anonymous / agent-class callers. */
2693
+ free: 27,
2694
+ /** Premium (L1-L5) tools requiring x402 payment. */
2695
+ premium: 9,
2696
+ /** Messaging tools (member-self-only; excluded from external advertisement). */
2697
+ messaging: 0,
2698
+ /** Total publicly advertised (free + premium). */
2699
+ total: 36
2700
+ };
2701
+ var REVENUE_SPLIT = {
2702
+ weaver: 0.75,
2703
+ // data subject (Identity Income)
2704
+ facilitator: 0.05,
2705
+ alter: 0.15,
2706
+ treasury: 0.05
2707
+ };
2708
+
2128
2709
  // src/homepage.ts
2129
2710
  init_cjs_shims();
2130
2711
  var HOMEPAGE_LIMITS = {
2131
2712
  whoami_max_chars: 240,
2132
2713
  opener_max_chars: 280,
2133
2714
  pronouns_max_chars: 32,
2134
- attunement_glyph_max_chars: 16
2715
+ attunement_glyph_max_chars: 16,
2716
+ contact_email_max_chars: 254
2135
2717
  };
2136
2718
 
2137
2719
  // src/themes.ts
@@ -2145,6 +2727,7 @@ var THEME_LIMITS = {
2145
2727
  };
2146
2728
  var OSC8_ALLOWED_SCHEMES = ["https:", "mailto:"];
2147
2729
 
2730
+ exports.ADVERTISED_TOOL_COUNTS = ADVERTISED_TOOL_COUNTS;
2148
2731
  exports.ALL_CLIENTS = ALL_CLIENTS;
2149
2732
  exports.AlterAuthError = AlterAuthError;
2150
2733
  exports.AlterClient = AlterClient;
@@ -2157,21 +2740,30 @@ exports.AlterProvenanceError = AlterProvenanceError;
2157
2740
  exports.AlterRateLimited = AlterRateLimited;
2158
2741
  exports.AlterTimeoutError = AlterTimeoutError;
2159
2742
  exports.AlterToolError = AlterToolError;
2743
+ exports.BelowFloorError = BelowFloorError;
2160
2744
  exports.CLAUDE_CODE = CLAUDE_CODE;
2161
2745
  exports.CLAUDE_DESKTOP = CLAUDE_DESKTOP;
2746
+ exports.CLIENT_CHANNEL = CLIENT_CHANNEL;
2747
+ exports.CLIENT_ID = CLIENT_ID;
2162
2748
  exports.CURSOR = CURSOR;
2163
2749
  exports.DEFAULT_DOMAIN = DEFAULT_DOMAIN;
2164
2750
  exports.DEFAULT_ENDPOINT = DEFAULT_ENDPOINT;
2165
2751
  exports.DEFAULT_VERIFY_AT_ALLOWLIST = DEFAULT_VERIFY_AT_ALLOWLIST;
2166
2752
  exports.FREE_TOOL_NAMES = FREE_TOOL_NAMES;
2753
+ exports.GENERATED_TOOL_PRICING = GENERATED_TOOL_PRICING;
2754
+ exports.GENERATED_TOOL_TIERS = GENERATED_TOOL_TIERS;
2167
2755
  exports.HOMEPAGE_LIMITS = HOMEPAGE_LIMITS;
2756
+ exports.KNOWN_FLOOR_PUBLIC_KEYS = KNOWN_FLOOR_PUBLIC_KEYS;
2168
2757
  exports.MCPClient = MCPClient;
2169
2758
  exports.MCP_PROTOCOL_VERSION = MCP_PROTOCOL_VERSION;
2759
+ exports.MIN_VERSION_ENDPOINT = MIN_VERSION_ENDPOINT;
2170
2760
  exports.OSC8_ALLOWED_SCHEMES = OSC8_ALLOWED_SCHEMES;
2171
2761
  exports.PREMIUM_TOOL_NAMES = PREMIUM_TOOL_NAMES;
2762
+ exports.REVENUE_SPLIT = REVENUE_SPLIT;
2172
2763
  exports.SDK_NAME = SDK_NAME;
2173
2764
  exports.SDK_VERSION = SDK_VERSION;
2174
2765
  exports.THEME_LIMITS = THEME_LIMITS;
2766
+ exports.TIER_PRICES = TIER_PRICES;
2175
2767
  exports.TOOL_BLAST_RADIUS = TOOL_BLAST_RADIUS;
2176
2768
  exports.TOOL_COSTS = TOOL_COSTS;
2177
2769
  exports.TOOL_TIERS = TOOL_TIERS;
@@ -2180,8 +2772,12 @@ exports.X402Client = X402Client;
2180
2772
  exports.base64urlDecode = base64urlDecode;
2181
2773
  exports.base64urlEncode = base64urlEncode2;
2182
2774
  exports.canonicalArgsSha256 = canonicalArgsSha256;
2775
+ exports.canonicalJson = canonicalJson;
2183
2776
  exports.canonicalStringify = canonicalStringify;
2777
+ exports.checkMinVersion = checkMinVersion;
2184
2778
  exports.clearDiscoveryCache = clearDiscoveryCache;
2779
+ exports.compareSemver = compareSemver;
2780
+ exports.computeKeyId = computeKeyId;
2185
2781
  exports.decodeDid = decodeDid;
2186
2782
  exports.detectSyncedVolume = detectSyncedVolume;
2187
2783
  exports.discover = discover;
@@ -2205,6 +2801,7 @@ exports.sign = sign;
2205
2801
  exports.signInvocation = signInvocation;
2206
2802
  exports.unwire = unwire;
2207
2803
  exports.verify = verify;
2804
+ exports.verifyFloorSignature = verifyFloorSignature;
2208
2805
  exports.verifyProvenance = verifyProvenance;
2209
2806
  exports.verifyToolSignatures = verifyToolSignatures;
2210
2807
  exports.wire = wire;