@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.js CHANGED
@@ -1,12 +1,13 @@
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';
7
- import { existsSync, readFileSync, mkdirSync, writeFileSync, copyFileSync, renameSync, unlinkSync } from 'fs';
8
- import { join, resolve, dirname } from 'path';
9
- import { homedir, platform } from 'os';
10
+ import { fileURLToPath } from 'url';
10
11
  import { spawnSync } from 'child_process';
11
12
  import { env } from 'process';
12
13
 
@@ -14,14 +15,13 @@ var __defProp = Object.defineProperty;
14
15
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
15
16
  var __getOwnPropNames = Object.getOwnPropertyNames;
16
17
  var __hasOwnProp = Object.prototype.hasOwnProperty;
17
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
18
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
19
- }) : x)(function(x) {
20
- if (typeof require !== "undefined") return require.apply(this, arguments);
21
- throw Error('Dynamic require of "' + x + '" is not supported');
22
- });
23
- var __esm = (fn, res) => function __init() {
24
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
18
+ var __esm = (fn, res, err) => function __init() {
19
+ if (err) throw err[0];
20
+ try {
21
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
22
+ } catch (e) {
23
+ throw err = [e], e;
24
+ }
25
25
  };
26
26
  var __export = (target, all) => {
27
27
  for (var name in all)
@@ -369,11 +369,11 @@ async function tryWellKnown(host, file, timeoutMs, fetchImpl) {
369
369
  }
370
370
  if (resp.type === "opaqueredirect" || resp.status >= 300 && resp.status < 400) {
371
371
  throw new AlterNetworkError(
372
- `${url} \u2192 redirect rejected (discovery must not follow redirects; validate the server configuration)`
372
+ `${url} -> redirect rejected (discovery must not follow redirects; validate the server configuration)`
373
373
  );
374
374
  }
375
375
  if (resp.status === 404) return null;
376
- if (!resp.ok) throw new AlterNetworkError(`${url} \u2192 HTTP ${resp.status}`);
376
+ if (!resp.ok) throw new AlterNetworkError(`${url} -> HTTP ${resp.status}`);
377
377
  const doc = await resp.json();
378
378
  if (file === "mcp.json") {
379
379
  const remotes = doc.remotes || [];
@@ -406,6 +406,309 @@ function ensureMcpPath(url) {
406
406
  return url;
407
407
  }
408
408
  }
409
+
410
+ // src/meta.ts
411
+ var SDK_NAME = "@truealter/sdk";
412
+ var SDK_VERSION = "0.5.8" ;
413
+
414
+ // src/floor-preflight.ts
415
+ var MIN_VERSION_ENDPOINT = "/v1/clients/min-version";
416
+ var CLIENT_ID = "alter-identity";
417
+ var CLIENT_CHANNEL = "npm";
418
+ var IN_MEMORY_TTL_DEFAULT_MS = 60 * 60 * 1e3;
419
+ var IN_MEMORY_TTL_MIN_MS = 60 * 1e3;
420
+ var IN_MEMORY_TTL_MAX_MS = 24 * 60 * 60 * 1e3;
421
+ var DISK_FRESH_MS = 24 * 60 * 60 * 1e3;
422
+ var DISK_WARN_MS = 7 * 24 * 60 * 60 * 1e3;
423
+ var FETCH_TIMEOUT_MS = 4e3;
424
+ function computeKeyId(publicKeyPem) {
425
+ if (!publicKeyPem) return "00000000";
426
+ const pub = createPublicKey({ key: publicKeyPem, format: "pem" });
427
+ const jwk = pub.export({ format: "jwk" });
428
+ const rawBytes = Buffer.from(jwk.x, "base64url");
429
+ return createHash("sha256").update(rawBytes).digest("hex").slice(0, 8);
430
+ }
431
+ function canonicalJson(obj) {
432
+ return JSON.stringify(sortKeysDeep(obj));
433
+ }
434
+ function sortKeysDeep(value) {
435
+ if (Array.isArray(value)) {
436
+ return value.map(sortKeysDeep);
437
+ }
438
+ if (value !== null && typeof value === "object") {
439
+ const obj = value;
440
+ const sorted = {};
441
+ for (const k of Object.keys(obj).sort()) {
442
+ sorted[k] = sortKeysDeep(obj[k]);
443
+ }
444
+ return sorted;
445
+ }
446
+ return value;
447
+ }
448
+ var KNOWN_FLOOR_PUBLIC_KEYS = {
449
+ "8aa59e05": `-----BEGIN PUBLIC KEY-----
450
+ MCowBQYDK2VwAyEAgqw28dlniOuiTE1f4BxCPSEgMLaPtHsO8wN5RWEwEhE=
451
+ -----END PUBLIC KEY-----`,
452
+ "640f7d9a": `-----BEGIN PUBLIC KEY-----
453
+ MCowBQYDK2VwAyEARzvAWayDwHvZRfOZizGZe+/a7PF082WGhyMS3tx06H4=
454
+ -----END PUBLIC KEY-----`
455
+ };
456
+ var BelowFloorError = class extends Error {
457
+ name = "BelowFloorError";
458
+ code = "client_below_floor";
459
+ client_version;
460
+ min_version;
461
+ upgrade_cmd;
462
+ channel;
463
+ envelope;
464
+ constructor(envelope) {
465
+ super(envelope.error.message);
466
+ this.envelope = envelope;
467
+ this.client_version = envelope.error.client_version;
468
+ this.min_version = envelope.error.min_version;
469
+ this.upgrade_cmd = envelope.error.upgrade_cmd;
470
+ this.channel = envelope.error.channel;
471
+ Object.setPrototypeOf(this, new.target.prototype);
472
+ }
473
+ };
474
+ var memCache = null;
475
+ async function checkMinVersion(opts = {}) {
476
+ const apiBase = opts.apiBase ?? defaultApiBase();
477
+ const clientVersion = opts.clientVersion ?? SDK_VERSION;
478
+ const clientId = opts.clientId ?? CLIENT_ID;
479
+ const channel = opts.channel ?? CLIENT_CHANNEL;
480
+ const knownKeys = opts.knownFloorPublicKeys ?? KNOWN_FLOOR_PUBLIC_KEYS;
481
+ const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
482
+ const now = opts.now ?? Date.now;
483
+ const cachePath = opts.diskCachePath === void 0 ? defaultDiskCachePath() : opts.diskCachePath;
484
+ const mem = readInMemoryCache(now);
485
+ if (mem) {
486
+ return compareAndPermit(mem, {
487
+ clientVersion,
488
+ clientId,
489
+ channel,
490
+ diagnostic: "mem-cache-hit"
491
+ });
492
+ }
493
+ const disk = cachePath ? readDiskCache(cachePath, knownKeys) : null;
494
+ const diskAgeMs = disk ? now() - disk.fetched_at_ms : Number.POSITIVE_INFINITY;
495
+ let fetched = null;
496
+ let fetchError = null;
497
+ if (!disk || diskAgeMs > IN_MEMORY_TTL_DEFAULT_MS) {
498
+ try {
499
+ fetched = await fetchFloorDoc(apiBase, fetchImpl, knownKeys);
500
+ } catch (err) {
501
+ fetchError = err.message ?? "fetch-error";
502
+ }
503
+ }
504
+ if (fetched) {
505
+ populateMemCache(fetched, now());
506
+ if (cachePath) writeDiskCache(cachePath, fetched, now());
507
+ return compareAndPermit(fetched, {
508
+ clientVersion,
509
+ clientId,
510
+ channel,
511
+ diagnostic: "fetched"
512
+ });
513
+ }
514
+ if (disk) {
515
+ populateMemCache(disk.doc, disk.fetched_at_ms);
516
+ if (diskAgeMs > DISK_WARN_MS) {
517
+ return compareAndPermit(disk.doc, {
518
+ clientVersion,
519
+ clientId,
520
+ channel,
521
+ diagnostic: "below-floor-offline-stale-or-permit",
522
+ warn: `floor cache is >7d old and backend unreachable (${fetchError ?? "no refresh attempted"}); permitting if above floor`
523
+ });
524
+ }
525
+ if (diskAgeMs > DISK_FRESH_MS) {
526
+ return compareAndPermit(disk.doc, {
527
+ clientVersion,
528
+ clientId,
529
+ channel,
530
+ diagnostic: "warn-stale-permit",
531
+ warn: `floor cache is ${Math.round(diskAgeMs / (60 * 60 * 1e3))}h old; refresh recommended`
532
+ });
533
+ }
534
+ return compareAndPermit(disk.doc, {
535
+ clientVersion,
536
+ clientId,
537
+ channel,
538
+ diagnostic: "disk-cache-hit"
539
+ });
540
+ }
541
+ return {
542
+ ok: true,
543
+ floor: null,
544
+ diagnostic: "no-cache-no-fetch-permit",
545
+ warn: `floor preflight skipped: backend unreachable (${fetchError ?? "unknown"})`
546
+ };
547
+ }
548
+ function compareAndPermit(doc, ctx) {
549
+ const floor = lookupFloor(doc, ctx.clientId, ctx.channel);
550
+ if (!floor) {
551
+ return { ok: true, floor: null, diagnostic: `${ctx.diagnostic}+no-floor`, warn: ctx.warn };
552
+ }
553
+ if (compareSemver(ctx.clientVersion, floor.min_version) >= 0) {
554
+ return { ok: true, floor, diagnostic: ctx.diagnostic, warn: ctx.warn };
555
+ }
556
+ const envelope = {
557
+ error: {
558
+ code: "client_below_floor",
559
+ message: `Your ${ctx.clientId} is too old. Upgrade required.`,
560
+ client_version: ctx.clientVersion,
561
+ min_version: floor.min_version,
562
+ upgrade_cmd: floor.upgrade_cmd,
563
+ channel: ctx.channel
564
+ }
565
+ };
566
+ throw new BelowFloorError(envelope);
567
+ }
568
+ function lookupFloor(doc, clientId, channel) {
569
+ const entry = doc.floors[clientId];
570
+ if (!entry) return null;
571
+ if (isChannelFloor(entry)) return entry;
572
+ const exact = entry[channel];
573
+ if (exact) return exact;
574
+ const fallback = entry["unknown"];
575
+ if (fallback) return fallback;
576
+ return null;
577
+ }
578
+ function isChannelFloor(v) {
579
+ return typeof v.min_version === "string" && typeof v.upgrade_cmd === "string";
580
+ }
581
+ function compareSemver(a, b) {
582
+ const [aMaj, aMin, aPat, aPre] = parseSemver(a);
583
+ const [bMaj, bMin, bPat, bPre] = parseSemver(b);
584
+ if (aMaj !== bMaj) return aMaj - bMaj;
585
+ if (aMin !== bMin) return aMin - bMin;
586
+ if (aPat !== bPat) return aPat - bPat;
587
+ if (aPre && !bPre) return -1;
588
+ if (!aPre && bPre) return 1;
589
+ if (aPre && bPre) return aPre.localeCompare(bPre);
590
+ return 0;
591
+ }
592
+ function parseSemver(v) {
593
+ const m = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?/.exec(v);
594
+ if (!m) return [0, 0, 0, null];
595
+ return [Number(m[1]), Number(m[2]), Number(m[3]), m[4] ?? null];
596
+ }
597
+ function verifyFloorSignature(doc, keys = KNOWN_FLOOR_PUBLIC_KEYS) {
598
+ const pem = keys[doc.key_id];
599
+ if (!pem) return false;
600
+ if (computeKeyId(pem) !== doc.key_id) return false;
601
+ try {
602
+ const pubKeyObject = createPublicKey({ key: pem, format: "pem" });
603
+ const canonical = canonicalJson({
604
+ floors: doc.floors,
605
+ served_at: doc.served_at
606
+ });
607
+ return verify$1(
608
+ null,
609
+ Buffer.from(canonical, "utf-8"),
610
+ pubKeyObject,
611
+ Buffer.from(doc.signature, "hex")
612
+ );
613
+ } catch {
614
+ return false;
615
+ }
616
+ }
617
+ async function fetchFloorDoc(apiBase, fetchImpl, knownKeys) {
618
+ const url = `${apiBase.replace(/\/+$/, "")}${MIN_VERSION_ENDPOINT}`;
619
+ let response;
620
+ try {
621
+ response = await fetchImpl(url, {
622
+ headers: {
623
+ accept: "application/json",
624
+ "X-Alter-Client-Id": CLIENT_ID,
625
+ "X-Alter-Client-Version": SDK_VERSION,
626
+ "X-Alter-Client-Channel": CLIENT_CHANNEL,
627
+ "User-Agent": `${SDK_NAME}/${SDK_VERSION}`
628
+ },
629
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
630
+ });
631
+ } catch (err) {
632
+ throw new Error(`network: ${err.message ?? String(err)}`);
633
+ }
634
+ if (!response.ok) throw new Error(`http-${response.status}`);
635
+ const body = await response.json();
636
+ if (!body || !body.floors || !body.signature || !body.key_id) {
637
+ throw new Error("malformed-floor-doc");
638
+ }
639
+ if (!verifyFloorSignature(body, knownKeys)) {
640
+ throw new Error("signature-invalid");
641
+ }
642
+ return body;
643
+ }
644
+ function readInMemoryCache(now) {
645
+ if (!memCache) return null;
646
+ if (now() - memCache.fetched_at_ms > memCache.ttl_ms) {
647
+ memCache = null;
648
+ return null;
649
+ }
650
+ return memCache.doc;
651
+ }
652
+ function populateMemCache(doc, fetched_at_ms) {
653
+ const ttlSec = doc.cache_ttl_seconds ?? 3600;
654
+ const ttlMs = Math.min(
655
+ Math.max(ttlSec * 1e3, IN_MEMORY_TTL_MIN_MS),
656
+ IN_MEMORY_TTL_MAX_MS
657
+ );
658
+ memCache = { doc, fetched_at_ms, ttl_ms: ttlMs };
659
+ }
660
+ function defaultDiskCachePath() {
661
+ const xdg = process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config");
662
+ return join(xdg, "alter", "floor-cache.json");
663
+ }
664
+ function readDiskCache(path, knownKeys) {
665
+ if (process.platform !== "win32") {
666
+ let st;
667
+ try {
668
+ st = statSync(path);
669
+ } catch {
670
+ return null;
671
+ }
672
+ const euid = typeof process.geteuid === "function" ? process.geteuid() : st.uid;
673
+ if (st.uid !== euid) return null;
674
+ if ((st.mode & 511) !== 384) return null;
675
+ }
676
+ let raw;
677
+ try {
678
+ raw = readFileSync(path, "utf-8");
679
+ } catch {
680
+ return null;
681
+ }
682
+ let parsed;
683
+ try {
684
+ parsed = JSON.parse(raw);
685
+ } catch {
686
+ return null;
687
+ }
688
+ if (!parsed.doc || typeof parsed.fetched_at_ms !== "number") return null;
689
+ if (!verifyFloorSignature(parsed.doc, knownKeys)) {
690
+ return null;
691
+ }
692
+ return parsed;
693
+ }
694
+ function writeDiskCache(path, doc, now_ms) {
695
+ const entry = { doc, fetched_at_ms: now_ms };
696
+ const payload = JSON.stringify(entry);
697
+ try {
698
+ mkdirSync(dirname(path), { recursive: true, mode: 448 });
699
+ const tmp = `${path}.tmp`;
700
+ writeFileSync(tmp, payload, { mode: 384 });
701
+ try {
702
+ chmodSync(tmp, 384);
703
+ } catch {
704
+ }
705
+ renameSync(tmp, path);
706
+ } catch {
707
+ }
708
+ }
709
+ function defaultApiBase() {
710
+ return process.env.ALTER_API ?? "https://api.truealter.com";
711
+ }
409
712
  var X402Client = class {
410
713
  signer;
411
714
  maxPerQuery;
@@ -516,6 +819,9 @@ var MCPClient = class {
516
819
  x402;
517
820
  signing;
518
821
  extraHeaders;
822
+ preflightHook;
823
+ preflightPromise = null;
824
+ preflightDone = false;
519
825
  requestCounter = 0;
520
826
  initialised = false;
521
827
  constructor(opts = {}) {
@@ -524,17 +830,43 @@ var MCPClient = class {
524
830
  this.fetchImpl = opts.fetch ?? fetch;
525
831
  this.timeoutMs = opts.timeoutMs ?? 3e4;
526
832
  this.maxRetries = opts.maxRetries ?? 2;
527
- this.clientInfo = opts.clientInfo ?? { name: "@truealter/sdk", version: "0.2.0" };
833
+ this.clientInfo = opts.clientInfo ?? { name: SDK_NAME, version: SDK_VERSION };
528
834
  this.x402 = opts.x402;
529
835
  this.signing = opts.signing;
530
836
  this.extraHeaders = opts.extraHeaders;
837
+ this.preflightHook = opts.preflightHook;
838
+ }
839
+ /**
840
+ * Run the lazy version-floor preflight hook exactly once.
841
+ * Idempotent and serialised: concurrent callers share the same
842
+ * promise. Throws from the hook propagate to every concurrent caller.
843
+ */
844
+ async runPreflight() {
845
+ if (this.preflightDone) return;
846
+ if (!this.preflightHook) {
847
+ this.preflightDone = true;
848
+ return;
849
+ }
850
+ if (!this.preflightPromise) {
851
+ this.preflightPromise = this.preflightHook().then(
852
+ () => {
853
+ this.preflightDone = true;
854
+ },
855
+ (err) => {
856
+ this.preflightPromise = null;
857
+ throw err;
858
+ }
859
+ );
860
+ }
861
+ await this.preflightPromise;
531
862
  }
532
863
  /**
533
864
  * Send the MCP `initialize` handshake and capture the resulting session
534
- * id. Idempotent safe to call multiple times.
865
+ * id. Idempotent: safe to call multiple times.
535
866
  */
536
867
  async initialize() {
537
868
  if (this.initialised) return null;
869
+ await this.runPreflight();
538
870
  const result = await this.rpc("initialize", {
539
871
  protocolVersion: MCP_PROTOCOL_VERSION,
540
872
  capabilities: {},
@@ -627,7 +959,14 @@ var MCPClient = class {
627
959
  method: "POST",
628
960
  headers: this.buildHeaders(signatureHeader),
629
961
  body: JSON.stringify(payload),
630
- signal: controller.signal
962
+ signal: controller.signal,
963
+ // Prevent fetch from silently following 3xx redirects. When
964
+ // Cloudflare Access credentials are absent or expired the edge
965
+ // returns HTTP 302 → CF Access login page (text/html). Without
966
+ // this option undici follows the redirect, lands on a 200 HTML
967
+ // body, and resp.json() throws the opaque "invalid JSON body"
968
+ // error that was surfaced as "MCP <method>: invalid JSON body".
969
+ redirect: "manual"
631
970
  });
632
971
  } catch (err) {
633
972
  clearTimeout(timer);
@@ -644,6 +983,19 @@ var MCPClient = class {
644
983
  clearTimeout(timer);
645
984
  const sessionHeader = resp.headers.get("Mcp-Session-Id");
646
985
  if (sessionHeader) this.sessionId = sessionHeader;
986
+ if (resp.status >= 300 && resp.status < 400) {
987
+ const location = resp.headers.get("Location") ?? "";
988
+ const isAuthRedirect = location.includes("cloudflareaccess.com") || location.includes("/cdn-cgi/access/") || !location.startsWith("/") && !location.startsWith(new URL(this.endpoint).origin);
989
+ if (isAuthRedirect) {
990
+ throw new AlterAuthError(
991
+ `MCP ${method}: Cloudflare Access blocked the request (session expired or credentials missing). Run \`alter login\` to re-authenticate.`,
992
+ 302
993
+ );
994
+ }
995
+ throw new AlterNetworkError(
996
+ `MCP ${method}: unexpected redirect ${resp.status} to ${location || "(no Location)"}`
997
+ );
998
+ }
647
999
  if (resp.status === 401 || resp.status === 403) {
648
1000
  throw new AlterAuthError(`HTTP ${resp.status} on ${method}`, resp.status);
649
1001
  }
@@ -668,11 +1020,56 @@ var MCPClient = class {
668
1020
  const body2 = await safeText(resp);
669
1021
  throw new AlterError("NETWORK", `HTTP ${resp.status} on ${method}: ${body2.slice(0, 200)}`);
670
1022
  }
1023
+ const contentType = resp.headers.get("Content-Type") ?? "";
1024
+ const isHtml = contentType.includes("text/html");
1025
+ const isSse = contentType.includes("text/event-stream");
1026
+ if (isHtml || isSse) {
1027
+ if (isSse) {
1028
+ const rawText = await safeText(resp);
1029
+ const dataLine = rawText.split("\n").find((l) => l.startsWith("data:"));
1030
+ if (dataLine) {
1031
+ const jsonPart = dataLine.slice("data:".length).trim();
1032
+ try {
1033
+ const parsed = JSON.parse(jsonPart);
1034
+ if (parsed.error) {
1035
+ const code = parsed.error.code;
1036
+ const message = parsed.error.message ?? `MCP ${method} error`;
1037
+ throw new AlterToolError(this.guessToolName(payload), message, code);
1038
+ }
1039
+ return parsed.result;
1040
+ } catch (parseErr) {
1041
+ if (parseErr instanceof AlterError) throw parseErr;
1042
+ throw new AlterInvalidResponse(
1043
+ `MCP ${method}: could not parse SSE data frame as JSON`,
1044
+ parseErr
1045
+ );
1046
+ }
1047
+ }
1048
+ throw new AlterInvalidResponse(
1049
+ `MCP ${method}: received text/event-stream response with no data: frame`
1050
+ );
1051
+ }
1052
+ const excerpt = (await safeText(resp)).slice(0, 300);
1053
+ const looksLikeLoginPage = excerpt.toLowerCase().includes("cloudflareaccess") || excerpt.toLowerCase().includes("access denied") || excerpt.toLowerCase().includes("<title>");
1054
+ if (looksLikeLoginPage) {
1055
+ throw new AlterAuthError(
1056
+ `MCP ${method}: received an HTML login page instead of JSON (Content-Type: ${contentType}). Run \`alter login\` to re-authenticate.`,
1057
+ 200
1058
+ );
1059
+ }
1060
+ throw new AlterInvalidResponse(
1061
+ `MCP ${method}: unexpected Content-Type "${contentType}" (expected application/json). Body excerpt: ${excerpt.slice(0, 120)}`
1062
+ );
1063
+ }
671
1064
  let body;
672
1065
  try {
673
1066
  body = await resp.json();
674
1067
  } catch (err) {
675
- throw new AlterInvalidResponse(`MCP ${method}: invalid JSON body`, err);
1068
+ const hint = contentType ? ` (Content-Type: ${contentType})` : "";
1069
+ throw new AlterInvalidResponse(
1070
+ `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.`,
1071
+ err
1072
+ );
676
1073
  }
677
1074
  if (body.error) {
678
1075
  const code = body.error.code;
@@ -693,8 +1090,11 @@ var MCPClient = class {
693
1090
  const headers = {
694
1091
  ...this.extraHeaders ?? {},
695
1092
  "Content-Type": "application/json",
696
- Accept: "application/json",
697
- "User-Agent": `${this.clientInfo.name}/${this.clientInfo.version}`
1093
+ Accept: "application/json, text/event-stream",
1094
+ "User-Agent": `${this.clientInfo.name}/${this.clientInfo.version}`,
1095
+ "X-Alter-Client-Id": "alter-identity",
1096
+ "X-Alter-Client-Version": SDK_VERSION,
1097
+ "X-Alter-Client-Channel": "npm"
698
1098
  };
699
1099
  if (this.apiKey) headers["X-ALTER-API-Key"] = this.apiKey;
700
1100
  if (this.sessionId) headers["Mcp-Session-Id"] = this.sessionId;
@@ -971,7 +1371,7 @@ async function verifyToolSignatures(tools, signatures, opts = {}) {
971
1371
  out.push({ tool: tool.name, valid: false, reason: "no signature published" });
972
1372
  continue;
973
1373
  }
974
- const expectedHash = await sha256Hex(canonicalJson(tool.inputSchema));
1374
+ const expectedHash = await sha256Hex(canonicalJson2(tool.inputSchema));
975
1375
  if (expectedHash !== sig.schema_hash) {
976
1376
  out.push({ tool: tool.name, valid: false, reason: "schema hash mismatch" });
977
1377
  continue;
@@ -1055,23 +1455,23 @@ async function fetchJwks(url, fetchImpl) {
1055
1455
  }
1056
1456
  if (resp.type === "opaqueredirect" || resp.status >= 300 && resp.status < 400) {
1057
1457
  throw new AlterProvenanceError(
1058
- `${url} \u2192 redirect rejected (allowlist enforces initial URL only)`
1458
+ `${url} -> redirect rejected (allowlist enforces initial URL only)`
1059
1459
  );
1060
1460
  }
1061
- if (!resp.ok) throw new AlterNetworkError(`${url} \u2192 HTTP ${resp.status}`);
1461
+ if (!resp.ok) throw new AlterNetworkError(`${url} -> HTTP ${resp.status}`);
1062
1462
  const contentLength = resp.headers.get("content-length");
1063
1463
  if (contentLength !== null) {
1064
1464
  const n = Number.parseInt(contentLength, 10);
1065
1465
  if (Number.isFinite(n) && n > JWKS_MAX_BYTES) {
1066
1466
  throw new AlterProvenanceError(
1067
- `${url} \u2192 JWKS too large: ${n} > ${JWKS_MAX_BYTES} bytes`
1467
+ `${url} -> JWKS too large: ${n} > ${JWKS_MAX_BYTES} bytes`
1068
1468
  );
1069
1469
  }
1070
1470
  }
1071
1471
  const body = await resp.text();
1072
1472
  if (body.length > JWKS_MAX_BYTES) {
1073
1473
  throw new AlterProvenanceError(
1074
- `${url} \u2192 JWKS too large: ${body.length} > ${JWKS_MAX_BYTES} bytes`
1474
+ `${url} -> JWKS too large: ${body.length} > ${JWKS_MAX_BYTES} bytes`
1075
1475
  );
1076
1476
  }
1077
1477
  let doc;
@@ -1155,19 +1555,35 @@ async function sha256Hex(input) {
1155
1555
  function toArrayBuffer(view) {
1156
1556
  return view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength);
1157
1557
  }
1158
- function canonicalJson(value) {
1558
+ function canonicalJson2(value) {
1159
1559
  if (value === null || typeof value !== "object") return JSON.stringify(value);
1160
1560
  if (Array.isArray(value)) {
1161
- return `[${value.map(canonicalJson).join(",")}]`;
1561
+ return `[${value.map(canonicalJson2).join(",")}]`;
1162
1562
  }
1163
1563
  const obj = value;
1164
1564
  const keys = Object.keys(obj).sort();
1165
- return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJson(obj[k])}`).join(",")}}`;
1565
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJson2(obj[k])}`).join(",")}}`;
1166
1566
  }
1167
1567
 
1168
1568
  // src/client.ts
1169
1569
  var DEFAULT_ENDPOINT = "https://mcp.truealter.com/api/v1/mcp";
1570
+ var MEMBER_BRIDGE_ENDPOINT = "https://api.truealter.com/api/v1/mcp";
1170
1571
  var DEFAULT_DOMAIN = "truealter.com";
1572
+ var CANONICAL_API_BASE = "https://api.truealter.com";
1573
+ var JWKS_WELL_KNOWN_PATH = "/.well-known/alter-keys.json";
1574
+ function resolveJwksUrl(opts = {}) {
1575
+ if (opts.jwksUrl) return opts.jwksUrl;
1576
+ const base = opts.apiBase ?? (typeof process !== "undefined" ? process.env?.ALTER_API : void 0) ?? originOf(opts.endpoint) ?? CANONICAL_API_BASE;
1577
+ return `${base.replace(/\/+$/, "")}${JWKS_WELL_KNOWN_PATH}`;
1578
+ }
1579
+ function originOf(url) {
1580
+ if (!url) return void 0;
1581
+ try {
1582
+ return new URL(url).origin;
1583
+ } catch {
1584
+ return void 0;
1585
+ }
1586
+ }
1171
1587
  var AlterClient = class {
1172
1588
  mcp;
1173
1589
  x402;
@@ -1178,11 +1594,21 @@ var AlterClient = class {
1178
1594
  this.options = options;
1179
1595
  this.x402 = options.x402;
1180
1596
  const endpoint = options.endpoint ?? DEFAULT_ENDPOINT;
1181
- this.mcp = new MCPClient({ ...options, endpoint, x402: options.x402 });
1597
+ const preflightHook = options.unsafe_skipVersionCheck ? void 0 : () => checkMinVersion({
1598
+ apiBase: options.apiBase,
1599
+ knownFloorPublicKeys: options.knownFloorPublicKeys,
1600
+ fetchImpl: options.fetch
1601
+ }).then(() => void 0);
1602
+ this.mcp = new MCPClient({
1603
+ ...options,
1604
+ endpoint,
1605
+ x402: options.x402,
1606
+ preflightHook
1607
+ });
1182
1608
  }
1183
1609
  /**
1184
1610
  * Resolve the MCP endpoint via discovery if requested. Safe to call
1185
- * multiple times the first successful lookup is cached.
1611
+ * multiple times: the first successful lookup is cached.
1186
1612
  */
1187
1613
  async discoverEndpoint() {
1188
1614
  if (this.discovered) return this.discovered;
@@ -1195,7 +1621,7 @@ var AlterClient = class {
1195
1621
  return this.discoveryPromise;
1196
1622
  }
1197
1623
  /**
1198
- * Initialise the MCP session. Optional every method calls
1624
+ * Initialise the MCP session. Optional: every method calls
1199
1625
  * `mcp.initialize()` lazily, but you can call this once at startup if
1200
1626
  * you want fail-fast behaviour.
1201
1627
  */
@@ -1203,11 +1629,11 @@ var AlterClient = class {
1203
1629
  await this.mcp.initialize();
1204
1630
  }
1205
1631
  // ── Free tier ────────────────────────────────────────────────────────
1206
- /** First handshake confirms the connection, returns trust tier and tool counts. */
1632
+ /** First handshake: confirms the connection, returns trust tier and tool counts. */
1207
1633
  async helloAgent() {
1208
1634
  return this.mcp.callTool("hello_agent", {});
1209
1635
  }
1210
- /** Resolve a ~handle (e.g. ~drew) to its canonical form and kind. No auth required. */
1636
+ /** Resolve a ~handle (e.g. ~example) to its canonical form and kind. No auth required. */
1211
1637
  async resolveHandle(args) {
1212
1638
  const payload = typeof args === "string" ? { query: args } : args;
1213
1639
  return this.mcp.callTool("alter_resolve_handle", payload);
@@ -1215,7 +1641,7 @@ var AlterClient = class {
1215
1641
  /** Verify a person is registered with ALTER (handle or id). */
1216
1642
  async verify(handleOrId, claims) {
1217
1643
  const args = handleOrId.includes("@") ? { member_id: "", email: handleOrId } : handleOrId.startsWith("~") ? (
1218
- // ~handle server resolves these via the member_id field
1644
+ // ~handle: server resolves these via the member_id field
1219
1645
  { member_id: handleOrId }
1220
1646
  ) : { member_id: handleOrId };
1221
1647
  if (claims) args.claims = claims;
@@ -1315,8 +1741,8 @@ var AlterClient = class {
1315
1741
  }
1316
1742
  // ── Alter-to-Alter Messaging ─────────────────────────────────────────
1317
1743
  // Wave 1: cross-handle direct messages between authenticated tilde
1318
- // handles. Default closed recipient must have granted the sender via
1319
- // alter_message_grant. Spec: docs/technical/Alter-to-Alter Messaging.md.
1744
+ // handles. Default closed: recipient must have granted the sender via
1745
+ // alter_message_grant. Spec: the ALTER Alter-to-Alter Messaging spec.
1320
1746
  /** Send a direct message to another tilde handle. */
1321
1747
  async messageSend(args) {
1322
1748
  return this.mcp.callTool("alter_message_send", args);
@@ -1349,12 +1775,16 @@ var AlterClient = class {
1349
1775
  /**
1350
1776
  * Verify the ES256 provenance attestation on a tool response.
1351
1777
  * Accepts either a {@link ProvenanceEnvelope} or the raw `_meta`
1352
- * object the latter is more convenient for ad-hoc verification.
1778
+ * object: the latter is more convenient for ad-hoc verification.
1353
1779
  */
1354
1780
  async verifyProvenance(envelope) {
1355
1781
  if (!envelope) return { valid: false, reason: "no provenance envelope" };
1356
1782
  const inner = envelope.provenance ?? envelope;
1357
1783
  return verifyProvenance(inner, {
1784
+ // Pass an explicit jwksUrl only when the caller pinned one. Leaving it
1785
+ // undefined preserves the envelope `verify_at` (allowlist-gated)
1786
+ // resolution path inside verifyProvenance; the allowlist is the
1787
+ // trust-anchor gate there, not a bare hardcoded host.
1358
1788
  jwksUrl: this.options.jwksUrl,
1359
1789
  verifyAtAllowlist: this.options.verifyAtAllowlist
1360
1790
  });
@@ -1367,9 +1797,21 @@ var AlterClient = class {
1367
1797
  async verifyToolSignatures(tools, signatures) {
1368
1798
  return verifyToolSignatures(tools, signatures);
1369
1799
  }
1370
- /** Fetch the published JWKS for ALTER's signing key (cached 5 min). */
1800
+ /**
1801
+ * Fetch the published JWKS for ALTER's signing key (cached 5 min).
1802
+ *
1803
+ * The JWKS URL is derived from the configured API surface
1804
+ * ({@link resolveJwksUrl}: explicit `jwksUrl` > `apiBase` > `ALTER_API` >
1805
+ * the configured `endpoint` origin > the canonical host) rather than a
1806
+ * single hardcoded host, so the trust anchor tracks the surface the
1807
+ * client is actually pointed at.
1808
+ */
1371
1809
  async fetchPublicKeys() {
1372
- const url = this.options.jwksUrl ?? "https://api.truealter.com/.well-known/alter-keys.json";
1810
+ const url = resolveJwksUrl({
1811
+ jwksUrl: this.options.jwksUrl,
1812
+ apiBase: this.options.apiBase,
1813
+ endpoint: this.options.endpoint
1814
+ });
1373
1815
  return fetchPublicKeys(url);
1374
1816
  }
1375
1817
  };
@@ -1385,7 +1827,7 @@ function generateGenericMcpConfig(opts = {}) {
1385
1827
  const entry = {
1386
1828
  url: opts.endpoint ?? DEFAULT_ENDPOINT,
1387
1829
  transport: "streamable-http",
1388
- description: "ALTER Identity \u2014 psychometric identity field for AI agents"
1830
+ description: "ALTER Identity: psychometric identity field for AI agents"
1389
1831
  };
1390
1832
  if (Object.keys(headers).length > 0) entry.headers = headers;
1391
1833
  return { mcpServers: { [serverName]: entry } };
@@ -1404,24 +1846,23 @@ function generateCursorConfig(opts = {}) {
1404
1846
  // src/adapters/claude-desktop.ts
1405
1847
  function generateClaudeDesktopConfig(opts = {}) {
1406
1848
  const serverName = opts.serverName ?? "alter";
1407
- const bridgeCommand = opts.bridgeCommand ?? "alter-mcp-bridge";
1849
+ const bridgeCommand = opts.bridgeCommand ?? "alter";
1408
1850
  const env2 = {};
1409
- env2.ALTER_MCP_ENDPOINT = opts.endpoint ?? DEFAULT_ENDPOINT;
1851
+ env2.ALTER_MCP_ENDPOINT = opts.endpoint ?? MEMBER_BRIDGE_ENDPOINT;
1410
1852
  if (opts.apiKey) env2.ALTER_API_KEY = opts.apiKey;
1411
1853
  const entry = {
1412
1854
  command: bridgeCommand,
1855
+ // The `mcp-bridge` subcommand is always the first arg now that the bridge
1856
+ // is invoked through the `alter` CLI, not a bare bridge binary.
1857
+ args: ["mcp-bridge"],
1413
1858
  env: env2,
1414
- description: "ALTER Identity \u2014 psychometric identity field for AI agents"
1859
+ description: "ALTER Identity: psychometric identity field for AI agents"
1415
1860
  };
1416
1861
  if (opts.extraArgs && opts.extraArgs.length > 0) {
1417
- entry.args = [...opts.extraArgs];
1862
+ entry.args = [...entry.args, ...opts.extraArgs];
1418
1863
  }
1419
1864
  return { mcpServers: { [serverName]: entry } };
1420
1865
  }
1421
-
1422
- // src/meta.ts
1423
- var SDK_NAME = "@truealter/sdk";
1424
- var SDK_VERSION = "0.3.0";
1425
1866
  var HOME = homedir();
1426
1867
  var PLAT = platform();
1427
1868
  function appData() {
@@ -1546,7 +1987,7 @@ function probeAll() {
1546
1987
  ];
1547
1988
  }
1548
1989
  var SYNC_PREFIXES = [
1549
- // iCloud Drive both the new and legacy mounts.
1990
+ // iCloud Drive: both the new and legacy mounts.
1550
1991
  "Library/Mobile Documents/com~apple~CloudDocs",
1551
1992
  "iCloud Drive",
1552
1993
  // OneDrive variants Microsoft ships across editions.
@@ -1559,7 +2000,7 @@ var SYNC_PREFIXES = [
1559
2000
  "Google Drive",
1560
2001
  "GoogleDrive",
1561
2002
  "CloudStorage/GoogleDrive",
1562
- // Box, pCloud, Sync.com, MEGA high-signal names worth refusing.
2003
+ // Box, pCloud, Sync.com, MEGA: high-signal names worth refusing.
1563
2004
  "Box Sync",
1564
2005
  "pCloud Drive",
1565
2006
  "Sync.com",
@@ -1611,10 +2052,10 @@ function atomicJsonMerge(opts) {
1611
2052
  preBytes = readFileSync(path, "utf8");
1612
2053
  if (preBytes.trim().length > 0) {
1613
2054
  try {
1614
- parsed = JSON.parse(preBytes);
2055
+ parsed = JSON.parse(preBytes.replace(/^\uFEFF/, ""));
1615
2056
  } catch (err) {
1616
2057
  throw new Error(
1617
- `refusing to wire ${path}: existing file is not valid JSON (${err.message}). Hand-fix the file, then re-run \`alter-identity wire\`.`
2058
+ `refusing to wire ${path}: existing file is not valid JSON (${err.message}). Hand-fix the file, then re-run \`alter wire\`.`
1618
2059
  );
1619
2060
  }
1620
2061
  if (typeof parsed !== "object" || Array.isArray(parsed) || parsed === null) {
@@ -1696,6 +2137,7 @@ function wire(opts = {}) {
1696
2137
  const endpoint = opts.endpoint ?? DEFAULT_ENDPOINT;
1697
2138
  const apiKey = opts.apiKey;
1698
2139
  const cfAccess = opts.cfAccess ?? readCfAccessEnv();
2140
+ const launcherPath = opts.launcherPath;
1699
2141
  const probes = probeAll();
1700
2142
  const selection = opts.only ?? probes.filter((p) => p.installed).map((p) => p.client.id);
1701
2143
  const ts = TIMESTAMP();
@@ -1714,7 +2156,7 @@ function wire(opts = {}) {
1714
2156
  }
1715
2157
  try {
1716
2158
  if (id === "claude-code") {
1717
- targets.push(wireClaudeCode({ endpoint, apiKey, cfAccess }));
2159
+ targets.push(wireClaudeCode({ endpoint, apiKey, cfAccess, launcherPath }));
1718
2160
  } else {
1719
2161
  targets.push(wireFileTarget({ id, endpoint, apiKey, cfAccess, timestamp: ts }));
1720
2162
  }
@@ -1747,7 +2189,7 @@ function wireFileTarget(args) {
1747
2189
  const sync = detectSyncedVolume(client.configPath);
1748
2190
  if (sync) {
1749
2191
  throw new Error(
1750
- `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.`
2192
+ `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.`
1751
2193
  );
1752
2194
  }
1753
2195
  const cfHeaders = {};
@@ -1785,10 +2227,28 @@ function wireFileTarget(args) {
1785
2227
  postSha256: result.postSha256
1786
2228
  };
1787
2229
  }
1788
- function wireClaudeCode(args) {
1789
- const cmd = "claude";
1790
- const bridgePath = resolveBridgeScript();
1791
- const argList = bridgePath ? ["mcp", "add", "--scope", "user", "alter", "--", "node", bridgePath] : [
2230
+ function redactSecret(text, secret) {
2231
+ if (!secret) return text;
2232
+ return text.split(secret).join("***redacted***");
2233
+ }
2234
+ function buildClaudeCodeAddArgs(args) {
2235
+ if (args.subprocessArgv) {
2236
+ return [
2237
+ "mcp",
2238
+ "add",
2239
+ "--scope",
2240
+ "user",
2241
+ "alter",
2242
+ "--env",
2243
+ `ALTER_MCP_ENDPOINT=${MEMBER_BRIDGE_ENDPOINT}`,
2244
+ "--env",
2245
+ `ALTER_PUBLIC_MCP_ENDPOINT=${MEMBER_BRIDGE_ENDPOINT}`,
2246
+ ...args.apiKey ? ["--env", `ALTER_API_KEY=${args.apiKey}`] : [],
2247
+ "--",
2248
+ ...args.subprocessArgv
2249
+ ];
2250
+ }
2251
+ return [
1792
2252
  "mcp",
1793
2253
  "add",
1794
2254
  "--scope",
@@ -1799,12 +2259,28 @@ function wireClaudeCode(args) {
1799
2259
  args.endpoint,
1800
2260
  ...args.apiKey ? ["--header", `X-ALTER-API-Key:${args.apiKey}`] : []
1801
2261
  ];
1802
- const full = `${cmd} ${argList.join(" ")}`;
2262
+ }
2263
+ function wireClaudeCode(args) {
2264
+ const cmd = "claude";
2265
+ const bridgePath = resolveBridgeScript();
2266
+ const launcher = args.launcherPath && existsSync(args.launcherPath) ? args.launcherPath : null;
2267
+ const subprocessArgv = launcher ? ["node", launcher, "mcp-bridge"] : bridgePath ? ["node", bridgePath] : null;
2268
+ const argList = buildClaudeCodeAddArgs({
2269
+ apiKey: args.apiKey,
2270
+ subprocessArgv,
2271
+ endpoint: args.endpoint
2272
+ });
2273
+ const full = redactSecret(`${cmd} ${argList.join(" ")}`, args.apiKey);
1803
2274
  const run = spawnSync(cmd, argList, {
1804
2275
  encoding: "utf8",
1805
2276
  shell: process.platform === "win32",
1806
2277
  timeout: 1e4,
1807
- env: bridgePath ? { ...process.env, ALTER_PUBLIC_MCP_ENDPOINT: args.endpoint, ...args.apiKey ? { ALTER_API_KEY: args.apiKey } : {} } : void 0
2278
+ env: subprocessArgv ? {
2279
+ ...process.env,
2280
+ ALTER_MCP_ENDPOINT: MEMBER_BRIDGE_ENDPOINT,
2281
+ ALTER_PUBLIC_MCP_ENDPOINT: MEMBER_BRIDGE_ENDPOINT,
2282
+ ...args.apiKey ? { ALTER_API_KEY: args.apiKey } : {}
2283
+ } : void 0
1808
2284
  });
1809
2285
  if (run.error) {
1810
2286
  return {
@@ -1836,14 +2312,17 @@ function wireClaudeCode(args) {
1836
2312
  };
1837
2313
  }
1838
2314
  function resolveBridgeScript() {
1839
- const { existsSync: existsSync4 } = __require("fs");
1840
- const { join: join3, dirname: dirname3 } = __require("path");
1841
- const siblingBridge = join3(dirname3(__filename), "..", "dist", "mcp-bridge.js");
1842
- if (existsSync4(siblingBridge)) return siblingBridge;
1843
- const srcBridge = join3(dirname3(__filename), "..", "mcp-bridge.js");
1844
- if (existsSync4(srcBridge)) return srcBridge;
1845
- const npmGlobalBridge = join3(dirname3(__filename), "mcp-bridge.js");
1846
- if (existsSync4(npmGlobalBridge)) return npmGlobalBridge;
2315
+ const here = dirname(fileURLToPath(import.meta.url));
2316
+ const distBinBridge = join(here, "bin", "mcp-bridge.js");
2317
+ if (existsSync(distBinBridge)) return distBinBridge;
2318
+ const nestedDistBinBridge = join(here, "..", "dist", "bin", "mcp-bridge.js");
2319
+ if (existsSync(nestedDistBinBridge)) return nestedDistBinBridge;
2320
+ const siblingBridge = join(here, "..", "dist", "mcp-bridge.js");
2321
+ if (existsSync(siblingBridge)) return siblingBridge;
2322
+ const srcBridge = join(here, "..", "mcp-bridge.js");
2323
+ if (existsSync(srcBridge)) return srcBridge;
2324
+ const npmGlobalBridge = join(here, "mcp-bridge.js");
2325
+ if (existsSync(npmGlobalBridge)) return npmGlobalBridge;
1847
2326
  return null;
1848
2327
  }
1849
2328
  function unwire() {
@@ -1996,19 +2475,19 @@ var TOOL_COSTS = {
1996
2475
  complete_knot: 0,
1997
2476
  check_golden_thread: 0,
1998
2477
  thread_census: 0,
1999
- // L1 ($0.005)
2000
- assess_traits: 5e-3,
2001
- get_trait_snapshot: 5e-3,
2002
- // L2 ($0.01)
2003
- get_full_trait_vector: 0.01,
2004
- get_side_quest_graph: 0.01,
2005
- // L3 ($0.025)
2006
- query_graph_similarity: 0.025,
2007
- // L4 ($0.05)
2008
- compute_belonging: 0.05,
2009
- // L5 ($0.50)
2010
- get_match_recommendations: 0.5,
2011
- generate_match_narrative: 0.5
2478
+ // L1 ($0.01)
2479
+ assess_traits: 0.01,
2480
+ get_trait_snapshot: 0.01,
2481
+ // L2 ($0.10)
2482
+ get_full_trait_vector: 0.1,
2483
+ get_side_quest_graph: 0.1,
2484
+ // L3 ($0.30)
2485
+ query_graph_similarity: 0.3,
2486
+ // L4 ($0.60)
2487
+ compute_belonging: 0.6,
2488
+ // L5 ($1.00)
2489
+ get_match_recommendations: 1,
2490
+ generate_match_narrative: 1
2012
2491
  };
2013
2492
  var TOOL_BLAST_RADIUS = {
2014
2493
  // Low: read-only reference
@@ -2048,12 +2527,108 @@ var TOOL_BLAST_RADIUS = {
2048
2527
  query_graph_similarity: "high"
2049
2528
  };
2050
2529
 
2530
+ // src/pricing.generated.ts
2531
+ var GENERATED_TOOL_TIERS = {
2532
+ // L0 (free)
2533
+ alter_presence_read: 0,
2534
+ alter_resolve_handle: 0,
2535
+ check_assessment_status: 0,
2536
+ create_identity_stub: 0,
2537
+ dispute_attestation: 0,
2538
+ get_competencies: 0,
2539
+ get_earning_summary: 0,
2540
+ get_engagement_level: 0,
2541
+ get_identity_earnings: 0,
2542
+ get_identity_trust_score: 0,
2543
+ get_network_stats: 0,
2544
+ get_privacy_budget: 0,
2545
+ get_profile: 0,
2546
+ initiate_assessment: 0,
2547
+ list_archetypes: 0,
2548
+ query_matches: 0,
2549
+ recommend_tool: 0,
2550
+ search_identities: 0,
2551
+ verify_identity: 0,
2552
+ // L1 ($0.01)
2553
+ assess_traits: 1,
2554
+ attest_domain: 1,
2555
+ get_trait_snapshot: 1,
2556
+ poll_requirement_matches: 1,
2557
+ submit_context: 1,
2558
+ submit_social_links: 1,
2559
+ submit_structured_profile: 1,
2560
+ // L2 ($0.10)
2561
+ attest_claim_provenance: 2,
2562
+ get_full_trait_vector: 2,
2563
+ get_side_quest_graph: 2,
2564
+ submit_batch_context: 2,
2565
+ // L3 ($0.30)
2566
+ alter_alignment: 3,
2567
+ query_graph_similarity: 3,
2568
+ // L4 ($0.60)
2569
+ compute_belonging: 4,
2570
+ // L5 ($1.00)
2571
+ generate_match_narrative: 5,
2572
+ get_match_recommendations: 5,
2573
+ query_field: 5
2574
+ };
2575
+ var GENERATED_TOOL_PRICING = {
2576
+ // L1: $0.01
2577
+ assess_traits: 0.01,
2578
+ attest_domain: 0.01,
2579
+ get_trait_snapshot: 0.01,
2580
+ poll_requirement_matches: 0.01,
2581
+ submit_context: 0.01,
2582
+ submit_social_links: 0.01,
2583
+ submit_structured_profile: 0.01,
2584
+ // L2: $0.10
2585
+ attest_claim_provenance: 0.1,
2586
+ get_full_trait_vector: 0.1,
2587
+ get_side_quest_graph: 0.1,
2588
+ submit_batch_context: 0.1,
2589
+ // L3: $0.30
2590
+ alter_alignment: 0.3,
2591
+ query_graph_similarity: 0.3,
2592
+ // L4: $0.60
2593
+ compute_belonging: 0.6,
2594
+ // L5: $1.00
2595
+ generate_match_narrative: 1,
2596
+ get_match_recommendations: 1,
2597
+ query_field: 1
2598
+ };
2599
+ var TIER_PRICES = {
2600
+ L0: 0,
2601
+ L1: 0.01,
2602
+ L2: 0.1,
2603
+ L3: 0.3,
2604
+ L4: 0.6,
2605
+ L5: 1
2606
+ };
2607
+ var ADVERTISED_TOOL_COUNTS = {
2608
+ /** Free (L0) tools visible to anonymous / agent-class callers. */
2609
+ free: 27,
2610
+ /** Premium (L1-L5) tools requiring x402 payment. */
2611
+ premium: 9,
2612
+ /** Messaging tools (member-self-only; excluded from external advertisement). */
2613
+ messaging: 0,
2614
+ /** Total publicly advertised (free + premium). */
2615
+ total: 36
2616
+ };
2617
+ var REVENUE_SPLIT = {
2618
+ weaver: 0.75,
2619
+ // data subject (Identity Income)
2620
+ facilitator: 0.05,
2621
+ alter: 0.15,
2622
+ treasury: 0.05
2623
+ };
2624
+
2051
2625
  // src/homepage.ts
2052
2626
  var HOMEPAGE_LIMITS = {
2053
2627
  whoami_max_chars: 240,
2054
2628
  opener_max_chars: 280,
2055
2629
  pronouns_max_chars: 32,
2056
- attunement_glyph_max_chars: 16
2630
+ attunement_glyph_max_chars: 16,
2631
+ contact_email_max_chars: 254
2057
2632
  };
2058
2633
 
2059
2634
  // src/themes.ts
@@ -2066,4 +2641,4 @@ var THEME_LIMITS = {
2066
2641
  };
2067
2642
  var OSC8_ALLOWED_SCHEMES = ["https:", "mailto:"];
2068
2643
 
2069
- 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 };
2644
+ 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 };