@truealter/sdk 0.5.1 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -4,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,12 +37,6 @@ 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
40
  var __esm = (fn, res) => function __init() {
46
41
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
47
42
  };
@@ -60,8 +55,11 @@ var __copyProps = (to, from, except, desc) => {
60
55
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
61
56
 
62
57
  // node_modules/tsup/assets/cjs_shims.js
58
+ var getImportMetaUrl, importMetaUrl;
63
59
  var init_cjs_shims = __esm({
64
60
  "node_modules/tsup/assets/cjs_shims.js"() {
61
+ getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
62
+ importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
65
63
  }
66
64
  });
67
65
 
@@ -408,11 +406,11 @@ async function tryWellKnown(host, file, timeoutMs, fetchImpl) {
408
406
  }
409
407
  if (resp.type === "opaqueredirect" || resp.status >= 300 && resp.status < 400) {
410
408
  throw new AlterNetworkError(
411
- `${url} \u2192 redirect rejected (discovery must not follow redirects; validate the server configuration)`
409
+ `${url} -> redirect rejected (discovery must not follow redirects; validate the server configuration)`
412
410
  );
413
411
  }
414
412
  if (resp.status === 404) return null;
415
- if (!resp.ok) throw new AlterNetworkError(`${url} \u2192 HTTP ${resp.status}`);
413
+ if (!resp.ok) throw new AlterNetworkError(`${url} -> HTTP ${resp.status}`);
416
414
  const doc = await resp.json();
417
415
  if (file === "mcp.json") {
418
416
  const remotes = doc.remotes || [];
@@ -446,6 +444,313 @@ function ensureMcpPath(url) {
446
444
  }
447
445
  }
448
446
 
447
+ // src/floor-preflight.ts
448
+ init_cjs_shims();
449
+
450
+ // src/meta.ts
451
+ init_cjs_shims();
452
+ var SDK_NAME = "@truealter/sdk";
453
+ var SDK_VERSION = "0.5.3" ;
454
+
455
+ // src/floor-preflight.ts
456
+ var MIN_VERSION_ENDPOINT = "/v1/clients/min-version";
457
+ var CLIENT_ID = "alter-identity";
458
+ var CLIENT_CHANNEL = "npm";
459
+ var IN_MEMORY_TTL_DEFAULT_MS = 60 * 60 * 1e3;
460
+ var IN_MEMORY_TTL_MIN_MS = 60 * 1e3;
461
+ var IN_MEMORY_TTL_MAX_MS = 24 * 60 * 60 * 1e3;
462
+ var DISK_FRESH_MS = 24 * 60 * 60 * 1e3;
463
+ var DISK_WARN_MS = 7 * 24 * 60 * 60 * 1e3;
464
+ var FETCH_TIMEOUT_MS = 4e3;
465
+ function computeKeyId(publicKeyPem) {
466
+ if (!publicKeyPem) return "00000000";
467
+ const pub = crypto$1.createPublicKey({ key: publicKeyPem, format: "pem" });
468
+ const jwk = pub.export({ format: "jwk" });
469
+ const rawBytes = Buffer.from(jwk.x, "base64url");
470
+ return crypto$1.createHash("sha256").update(rawBytes).digest("hex").slice(0, 8);
471
+ }
472
+ function canonicalJson(obj) {
473
+ return JSON.stringify(sortKeysDeep(obj));
474
+ }
475
+ function sortKeysDeep(value) {
476
+ if (Array.isArray(value)) {
477
+ return value.map(sortKeysDeep);
478
+ }
479
+ if (value !== null && typeof value === "object") {
480
+ const obj = value;
481
+ const sorted = {};
482
+ for (const k of Object.keys(obj).sort()) {
483
+ sorted[k] = sortKeysDeep(obj[k]);
484
+ }
485
+ return sorted;
486
+ }
487
+ return value;
488
+ }
489
+ var KNOWN_FLOOR_PUBLIC_KEYS = {
490
+ "8aa59e05": `-----BEGIN PUBLIC KEY-----
491
+ MCowBQYDK2VwAyEAgqw28dlniOuiTE1f4BxCPSEgMLaPtHsO8wN5RWEwEhE=
492
+ -----END PUBLIC KEY-----`,
493
+ "640f7d9a": `-----BEGIN PUBLIC KEY-----
494
+ MCowBQYDK2VwAyEARzvAWayDwHvZRfOZizGZe+/a7PF082WGhyMS3tx06H4=
495
+ -----END PUBLIC KEY-----`
496
+ };
497
+ var BelowFloorError = class extends Error {
498
+ name = "BelowFloorError";
499
+ code = "client_below_floor";
500
+ client_version;
501
+ min_version;
502
+ upgrade_cmd;
503
+ channel;
504
+ envelope;
505
+ constructor(envelope) {
506
+ super(envelope.error.message);
507
+ this.envelope = envelope;
508
+ this.client_version = envelope.error.client_version;
509
+ this.min_version = envelope.error.min_version;
510
+ this.upgrade_cmd = envelope.error.upgrade_cmd;
511
+ this.channel = envelope.error.channel;
512
+ Object.setPrototypeOf(this, new.target.prototype);
513
+ }
514
+ };
515
+ var memCache = null;
516
+ async function checkMinVersion(opts = {}) {
517
+ const apiBase = opts.apiBase ?? defaultApiBase();
518
+ const clientVersion = opts.clientVersion ?? SDK_VERSION;
519
+ const clientId = opts.clientId ?? CLIENT_ID;
520
+ const channel = opts.channel ?? CLIENT_CHANNEL;
521
+ const knownKeys = opts.knownFloorPublicKeys ?? KNOWN_FLOOR_PUBLIC_KEYS;
522
+ const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
523
+ const now = opts.now ?? Date.now;
524
+ const cachePath = opts.diskCachePath === void 0 ? defaultDiskCachePath() : opts.diskCachePath;
525
+ const mem = readInMemoryCache(now);
526
+ if (mem) {
527
+ return compareAndPermit(mem, {
528
+ clientVersion,
529
+ clientId,
530
+ channel,
531
+ diagnostic: "mem-cache-hit"
532
+ });
533
+ }
534
+ const disk = cachePath ? readDiskCache(cachePath, knownKeys) : null;
535
+ const diskAgeMs = disk ? now() - disk.fetched_at_ms : Number.POSITIVE_INFINITY;
536
+ let fetched = null;
537
+ let fetchError = null;
538
+ if (!disk || diskAgeMs > IN_MEMORY_TTL_DEFAULT_MS) {
539
+ try {
540
+ fetched = await fetchFloorDoc(apiBase, fetchImpl, knownKeys);
541
+ } catch (err) {
542
+ fetchError = err.message ?? "fetch-error";
543
+ }
544
+ }
545
+ if (fetched) {
546
+ populateMemCache(fetched, now());
547
+ if (cachePath) writeDiskCache(cachePath, fetched, now());
548
+ return compareAndPermit(fetched, {
549
+ clientVersion,
550
+ clientId,
551
+ channel,
552
+ diagnostic: "fetched"
553
+ });
554
+ }
555
+ if (disk) {
556
+ populateMemCache(disk.doc, disk.fetched_at_ms);
557
+ if (diskAgeMs > DISK_WARN_MS) {
558
+ return compareAndPermit(disk.doc, {
559
+ clientVersion,
560
+ clientId,
561
+ channel,
562
+ diagnostic: "below-floor-offline-stale-or-permit",
563
+ warn: `floor cache is >7d old and backend unreachable (${fetchError ?? "no refresh attempted"}); permitting if above floor`
564
+ });
565
+ }
566
+ if (diskAgeMs > DISK_FRESH_MS) {
567
+ return compareAndPermit(disk.doc, {
568
+ clientVersion,
569
+ clientId,
570
+ channel,
571
+ diagnostic: "warn-stale-permit",
572
+ warn: `floor cache is ${Math.round(diskAgeMs / (60 * 60 * 1e3))}h old; refresh recommended`
573
+ });
574
+ }
575
+ return compareAndPermit(disk.doc, {
576
+ clientVersion,
577
+ clientId,
578
+ channel,
579
+ diagnostic: "disk-cache-hit"
580
+ });
581
+ }
582
+ return {
583
+ ok: true,
584
+ floor: null,
585
+ diagnostic: "no-cache-no-fetch-permit",
586
+ warn: `floor preflight skipped: backend unreachable (${fetchError ?? "unknown"})`
587
+ };
588
+ }
589
+ function compareAndPermit(doc, ctx) {
590
+ const floor = lookupFloor(doc, ctx.clientId, ctx.channel);
591
+ if (!floor) {
592
+ return { ok: true, floor: null, diagnostic: `${ctx.diagnostic}+no-floor`, warn: ctx.warn };
593
+ }
594
+ if (compareSemver(ctx.clientVersion, floor.min_version) >= 0) {
595
+ return { ok: true, floor, diagnostic: ctx.diagnostic, warn: ctx.warn };
596
+ }
597
+ const envelope = {
598
+ error: {
599
+ code: "client_below_floor",
600
+ message: `Your ${ctx.clientId} is too old. Upgrade required.`,
601
+ client_version: ctx.clientVersion,
602
+ min_version: floor.min_version,
603
+ upgrade_cmd: floor.upgrade_cmd,
604
+ channel: ctx.channel
605
+ }
606
+ };
607
+ throw new BelowFloorError(envelope);
608
+ }
609
+ function lookupFloor(doc, clientId, channel) {
610
+ const entry = doc.floors[clientId];
611
+ if (!entry) return null;
612
+ if (isChannelFloor(entry)) return entry;
613
+ const exact = entry[channel];
614
+ if (exact) return exact;
615
+ const fallback = entry["unknown"];
616
+ if (fallback) return fallback;
617
+ return null;
618
+ }
619
+ function isChannelFloor(v) {
620
+ return typeof v.min_version === "string" && typeof v.upgrade_cmd === "string";
621
+ }
622
+ function compareSemver(a, b) {
623
+ const [aMaj, aMin, aPat, aPre] = parseSemver(a);
624
+ const [bMaj, bMin, bPat, bPre] = parseSemver(b);
625
+ if (aMaj !== bMaj) return aMaj - bMaj;
626
+ if (aMin !== bMin) return aMin - bMin;
627
+ if (aPat !== bPat) return aPat - bPat;
628
+ if (aPre && !bPre) return -1;
629
+ if (!aPre && bPre) return 1;
630
+ if (aPre && bPre) return aPre.localeCompare(bPre);
631
+ return 0;
632
+ }
633
+ function parseSemver(v) {
634
+ const m = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?/.exec(v);
635
+ if (!m) return [0, 0, 0, null];
636
+ return [Number(m[1]), Number(m[2]), Number(m[3]), m[4] ?? null];
637
+ }
638
+ function verifyFloorSignature(doc, keys = KNOWN_FLOOR_PUBLIC_KEYS) {
639
+ const pem = keys[doc.key_id];
640
+ if (!pem) return false;
641
+ if (computeKeyId(pem) !== doc.key_id) return false;
642
+ try {
643
+ const pubKeyObject = crypto$1.createPublicKey({ key: pem, format: "pem" });
644
+ const canonical = canonicalJson({
645
+ floors: doc.floors,
646
+ served_at: doc.served_at
647
+ });
648
+ return crypto$1.verify(
649
+ null,
650
+ Buffer.from(canonical, "utf-8"),
651
+ pubKeyObject,
652
+ Buffer.from(doc.signature, "hex")
653
+ );
654
+ } catch {
655
+ return false;
656
+ }
657
+ }
658
+ async function fetchFloorDoc(apiBase, fetchImpl, knownKeys) {
659
+ const url = `${apiBase.replace(/\/+$/, "")}${MIN_VERSION_ENDPOINT}`;
660
+ let response;
661
+ try {
662
+ response = await fetchImpl(url, {
663
+ headers: {
664
+ accept: "application/json",
665
+ "X-Alter-Client-Id": CLIENT_ID,
666
+ "X-Alter-Client-Version": SDK_VERSION,
667
+ "X-Alter-Client-Channel": CLIENT_CHANNEL,
668
+ "User-Agent": `${SDK_NAME}/${SDK_VERSION}`
669
+ },
670
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
671
+ });
672
+ } catch (err) {
673
+ throw new Error(`network: ${err.message ?? String(err)}`);
674
+ }
675
+ if (!response.ok) throw new Error(`http-${response.status}`);
676
+ const body = await response.json();
677
+ if (!body || !body.floors || !body.signature || !body.key_id) {
678
+ throw new Error("malformed-floor-doc");
679
+ }
680
+ if (!verifyFloorSignature(body, knownKeys)) {
681
+ throw new Error("signature-invalid");
682
+ }
683
+ return body;
684
+ }
685
+ function readInMemoryCache(now) {
686
+ if (!memCache) return null;
687
+ if (now() - memCache.fetched_at_ms > memCache.ttl_ms) {
688
+ memCache = null;
689
+ return null;
690
+ }
691
+ return memCache.doc;
692
+ }
693
+ function populateMemCache(doc, fetched_at_ms) {
694
+ const ttlSec = doc.cache_ttl_seconds ?? 3600;
695
+ const ttlMs = Math.min(
696
+ Math.max(ttlSec * 1e3, IN_MEMORY_TTL_MIN_MS),
697
+ IN_MEMORY_TTL_MAX_MS
698
+ );
699
+ memCache = { doc, fetched_at_ms, ttl_ms: ttlMs };
700
+ }
701
+ function defaultDiskCachePath() {
702
+ const xdg = process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config");
703
+ return path.join(xdg, "alter", "floor-cache.json");
704
+ }
705
+ function readDiskCache(path, knownKeys) {
706
+ if (process.platform !== "win32") {
707
+ let st;
708
+ try {
709
+ st = fs.statSync(path);
710
+ } catch {
711
+ return null;
712
+ }
713
+ const euid = typeof process.geteuid === "function" ? process.geteuid() : st.uid;
714
+ if (st.uid !== euid) return null;
715
+ if ((st.mode & 511) !== 384) return null;
716
+ }
717
+ let raw;
718
+ try {
719
+ raw = fs.readFileSync(path, "utf-8");
720
+ } catch {
721
+ return null;
722
+ }
723
+ let parsed;
724
+ try {
725
+ parsed = JSON.parse(raw);
726
+ } catch {
727
+ return null;
728
+ }
729
+ if (!parsed.doc || typeof parsed.fetched_at_ms !== "number") return null;
730
+ if (!verifyFloorSignature(parsed.doc, knownKeys)) {
731
+ return null;
732
+ }
733
+ return parsed;
734
+ }
735
+ function writeDiskCache(path$1, doc, now_ms) {
736
+ const entry = { doc, fetched_at_ms: now_ms };
737
+ const payload = JSON.stringify(entry);
738
+ try {
739
+ fs.mkdirSync(path.dirname(path$1), { recursive: true, mode: 448 });
740
+ const tmp = `${path$1}.tmp`;
741
+ fs.writeFileSync(tmp, payload, { mode: 384 });
742
+ try {
743
+ fs.chmodSync(tmp, 384);
744
+ } catch {
745
+ }
746
+ fs.renameSync(tmp, path$1);
747
+ } catch {
748
+ }
749
+ }
750
+ function defaultApiBase() {
751
+ return process.env.ALTER_API ?? "https://api.truealter.com";
752
+ }
753
+
449
754
  // src/mcp.ts
450
755
  init_cjs_shims();
451
756
 
@@ -561,6 +866,9 @@ var MCPClient = class {
561
866
  x402;
562
867
  signing;
563
868
  extraHeaders;
869
+ preflightHook;
870
+ preflightPromise = null;
871
+ preflightDone = false;
564
872
  requestCounter = 0;
565
873
  initialised = false;
566
874
  constructor(opts = {}) {
@@ -569,17 +877,43 @@ var MCPClient = class {
569
877
  this.fetchImpl = opts.fetch ?? fetch;
570
878
  this.timeoutMs = opts.timeoutMs ?? 3e4;
571
879
  this.maxRetries = opts.maxRetries ?? 2;
572
- this.clientInfo = opts.clientInfo ?? { name: "@truealter/sdk", version: "0.2.0" };
880
+ this.clientInfo = opts.clientInfo ?? { name: SDK_NAME, version: SDK_VERSION };
573
881
  this.x402 = opts.x402;
574
882
  this.signing = opts.signing;
575
883
  this.extraHeaders = opts.extraHeaders;
884
+ this.preflightHook = opts.preflightHook;
885
+ }
886
+ /**
887
+ * Run the lazy preflight hook (D-MIN-VERSION-FLOOR-1) exactly once.
888
+ * Idempotent and serialised: concurrent callers share the same
889
+ * promise. Throws from the hook propagate to every concurrent caller.
890
+ */
891
+ async runPreflight() {
892
+ if (this.preflightDone) return;
893
+ if (!this.preflightHook) {
894
+ this.preflightDone = true;
895
+ return;
896
+ }
897
+ if (!this.preflightPromise) {
898
+ this.preflightPromise = this.preflightHook().then(
899
+ () => {
900
+ this.preflightDone = true;
901
+ },
902
+ (err) => {
903
+ this.preflightPromise = null;
904
+ throw err;
905
+ }
906
+ );
907
+ }
908
+ await this.preflightPromise;
576
909
  }
577
910
  /**
578
911
  * Send the MCP `initialize` handshake and capture the resulting session
579
- * id. Idempotent safe to call multiple times.
912
+ * id. Idempotent: safe to call multiple times.
580
913
  */
581
914
  async initialize() {
582
915
  if (this.initialised) return null;
916
+ await this.runPreflight();
583
917
  const result = await this.rpc("initialize", {
584
918
  protocolVersion: MCP_PROTOCOL_VERSION,
585
919
  capabilities: {},
@@ -739,7 +1073,10 @@ var MCPClient = class {
739
1073
  ...this.extraHeaders ?? {},
740
1074
  "Content-Type": "application/json",
741
1075
  Accept: "application/json",
742
- "User-Agent": `${this.clientInfo.name}/${this.clientInfo.version}`
1076
+ "User-Agent": `${this.clientInfo.name}/${this.clientInfo.version}`,
1077
+ "X-Alter-Client-Id": "alter-identity",
1078
+ "X-Alter-Client-Version": SDK_VERSION,
1079
+ "X-Alter-Client-Channel": "npm"
743
1080
  };
744
1081
  if (this.apiKey) headers["X-ALTER-API-Key"] = this.apiKey;
745
1082
  if (this.sessionId) headers["Mcp-Session-Id"] = this.sessionId;
@@ -1022,7 +1359,7 @@ async function verifyToolSignatures(tools, signatures, opts = {}) {
1022
1359
  out.push({ tool: tool.name, valid: false, reason: "no signature published" });
1023
1360
  continue;
1024
1361
  }
1025
- const expectedHash = await sha256Hex(canonicalJson(tool.inputSchema));
1362
+ const expectedHash = await sha256Hex(canonicalJson2(tool.inputSchema));
1026
1363
  if (expectedHash !== sig.schema_hash) {
1027
1364
  out.push({ tool: tool.name, valid: false, reason: "schema hash mismatch" });
1028
1365
  continue;
@@ -1106,23 +1443,23 @@ async function fetchJwks(url, fetchImpl) {
1106
1443
  }
1107
1444
  if (resp.type === "opaqueredirect" || resp.status >= 300 && resp.status < 400) {
1108
1445
  throw new AlterProvenanceError(
1109
- `${url} \u2192 redirect rejected (allowlist enforces initial URL only)`
1446
+ `${url} -> redirect rejected (allowlist enforces initial URL only)`
1110
1447
  );
1111
1448
  }
1112
- if (!resp.ok) throw new AlterNetworkError(`${url} \u2192 HTTP ${resp.status}`);
1449
+ if (!resp.ok) throw new AlterNetworkError(`${url} -> HTTP ${resp.status}`);
1113
1450
  const contentLength = resp.headers.get("content-length");
1114
1451
  if (contentLength !== null) {
1115
1452
  const n = Number.parseInt(contentLength, 10);
1116
1453
  if (Number.isFinite(n) && n > JWKS_MAX_BYTES) {
1117
1454
  throw new AlterProvenanceError(
1118
- `${url} \u2192 JWKS too large: ${n} > ${JWKS_MAX_BYTES} bytes`
1455
+ `${url} -> JWKS too large: ${n} > ${JWKS_MAX_BYTES} bytes`
1119
1456
  );
1120
1457
  }
1121
1458
  }
1122
1459
  const body = await resp.text();
1123
1460
  if (body.length > JWKS_MAX_BYTES) {
1124
1461
  throw new AlterProvenanceError(
1125
- `${url} \u2192 JWKS too large: ${body.length} > ${JWKS_MAX_BYTES} bytes`
1462
+ `${url} -> JWKS too large: ${body.length} > ${JWKS_MAX_BYTES} bytes`
1126
1463
  );
1127
1464
  }
1128
1465
  let doc;
@@ -1206,14 +1543,14 @@ async function sha256Hex(input) {
1206
1543
  function toArrayBuffer(view) {
1207
1544
  return view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength);
1208
1545
  }
1209
- function canonicalJson(value) {
1546
+ function canonicalJson2(value) {
1210
1547
  if (value === null || typeof value !== "object") return JSON.stringify(value);
1211
1548
  if (Array.isArray(value)) {
1212
- return `[${value.map(canonicalJson).join(",")}]`;
1549
+ return `[${value.map(canonicalJson2).join(",")}]`;
1213
1550
  }
1214
1551
  const obj = value;
1215
1552
  const keys = Object.keys(obj).sort();
1216
- return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJson(obj[k])}`).join(",")}}`;
1553
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJson2(obj[k])}`).join(",")}}`;
1217
1554
  }
1218
1555
 
1219
1556
  // src/client.ts
@@ -1229,11 +1566,21 @@ var AlterClient = class {
1229
1566
  this.options = options;
1230
1567
  this.x402 = options.x402;
1231
1568
  const endpoint = options.endpoint ?? DEFAULT_ENDPOINT;
1232
- this.mcp = new MCPClient({ ...options, endpoint, x402: options.x402 });
1569
+ const preflightHook = options.unsafe_skipVersionCheck ? void 0 : () => checkMinVersion({
1570
+ apiBase: options.apiBase,
1571
+ knownFloorPublicKeys: options.knownFloorPublicKeys,
1572
+ fetchImpl: options.fetch
1573
+ }).then(() => void 0);
1574
+ this.mcp = new MCPClient({
1575
+ ...options,
1576
+ endpoint,
1577
+ x402: options.x402,
1578
+ preflightHook
1579
+ });
1233
1580
  }
1234
1581
  /**
1235
1582
  * Resolve the MCP endpoint via discovery if requested. Safe to call
1236
- * multiple times the first successful lookup is cached.
1583
+ * multiple times: the first successful lookup is cached.
1237
1584
  */
1238
1585
  async discoverEndpoint() {
1239
1586
  if (this.discovered) return this.discovered;
@@ -1246,7 +1593,7 @@ var AlterClient = class {
1246
1593
  return this.discoveryPromise;
1247
1594
  }
1248
1595
  /**
1249
- * Initialise the MCP session. Optional every method calls
1596
+ * Initialise the MCP session. Optional: every method calls
1250
1597
  * `mcp.initialize()` lazily, but you can call this once at startup if
1251
1598
  * you want fail-fast behaviour.
1252
1599
  */
@@ -1254,11 +1601,11 @@ var AlterClient = class {
1254
1601
  await this.mcp.initialize();
1255
1602
  }
1256
1603
  // ── Free tier ────────────────────────────────────────────────────────
1257
- /** First handshake confirms the connection, returns trust tier and tool counts. */
1604
+ /** First handshake: confirms the connection, returns trust tier and tool counts. */
1258
1605
  async helloAgent() {
1259
1606
  return this.mcp.callTool("hello_agent", {});
1260
1607
  }
1261
- /** Resolve a ~handle (e.g. ~drew) to its canonical form and kind. No auth required. */
1608
+ /** Resolve a ~handle (e.g. ~example) to its canonical form and kind. No auth required. */
1262
1609
  async resolveHandle(args) {
1263
1610
  const payload = typeof args === "string" ? { query: args } : args;
1264
1611
  return this.mcp.callTool("alter_resolve_handle", payload);
@@ -1266,7 +1613,7 @@ var AlterClient = class {
1266
1613
  /** Verify a person is registered with ALTER (handle or id). */
1267
1614
  async verify(handleOrId, claims) {
1268
1615
  const args = handleOrId.includes("@") ? { member_id: "", email: handleOrId } : handleOrId.startsWith("~") ? (
1269
- // ~handle server resolves these via the member_id field
1616
+ // ~handle: server resolves these via the member_id field
1270
1617
  { member_id: handleOrId }
1271
1618
  ) : { member_id: handleOrId };
1272
1619
  if (claims) args.claims = claims;
@@ -1366,7 +1713,7 @@ var AlterClient = class {
1366
1713
  }
1367
1714
  // ── Alter-to-Alter Messaging ─────────────────────────────────────────
1368
1715
  // Wave 1: cross-handle direct messages between authenticated tilde
1369
- // handles. Default closed recipient must have granted the sender via
1716
+ // handles. Default closed: recipient must have granted the sender via
1370
1717
  // alter_message_grant. Spec: docs/technical/Alter-to-Alter Messaging.md.
1371
1718
  /** Send a direct message to another tilde handle. */
1372
1719
  async messageSend(args) {
@@ -1400,7 +1747,7 @@ var AlterClient = class {
1400
1747
  /**
1401
1748
  * Verify the ES256 provenance attestation on a tool response.
1402
1749
  * Accepts either a {@link ProvenanceEnvelope} or the raw `_meta`
1403
- * object the latter is more convenient for ad-hoc verification.
1750
+ * object: the latter is more convenient for ad-hoc verification.
1404
1751
  */
1405
1752
  async verifyProvenance(envelope) {
1406
1753
  if (!envelope) return { valid: false, reason: "no provenance envelope" };
@@ -1440,7 +1787,7 @@ function generateGenericMcpConfig(opts = {}) {
1440
1787
  const entry = {
1441
1788
  url: opts.endpoint ?? DEFAULT_ENDPOINT,
1442
1789
  transport: "streamable-http",
1443
- description: "ALTER Identity \u2014 psychometric identity field for AI agents"
1790
+ description: "ALTER Identity: psychometric identity field for AI agents"
1444
1791
  };
1445
1792
  if (Object.keys(headers).length > 0) entry.headers = headers;
1446
1793
  return { mcpServers: { [serverName]: entry } };
@@ -1468,7 +1815,7 @@ function generateClaudeDesktopConfig(opts = {}) {
1468
1815
  const entry = {
1469
1816
  command: bridgeCommand,
1470
1817
  env: env2,
1471
- description: "ALTER Identity \u2014 psychometric identity field for AI agents"
1818
+ description: "ALTER Identity: psychometric identity field for AI agents"
1472
1819
  };
1473
1820
  if (opts.extraArgs && opts.extraArgs.length > 0) {
1474
1821
  entry.args = [...opts.extraArgs];
@@ -1479,11 +1826,6 @@ function generateClaudeDesktopConfig(opts = {}) {
1479
1826
  // src/wire/index.ts
1480
1827
  init_cjs_shims();
1481
1828
 
1482
- // src/meta.ts
1483
- init_cjs_shims();
1484
- var SDK_NAME = "@truealter/sdk";
1485
- var SDK_VERSION = "0.3.0";
1486
-
1487
1829
  // src/wire/paths.ts
1488
1830
  init_cjs_shims();
1489
1831
  var HOME = os.homedir();
@@ -1616,7 +1958,7 @@ function probeAll() {
1616
1958
  // src/wire/sync.ts
1617
1959
  init_cjs_shims();
1618
1960
  var SYNC_PREFIXES = [
1619
- // iCloud Drive both the new and legacy mounts.
1961
+ // iCloud Drive: both the new and legacy mounts.
1620
1962
  "Library/Mobile Documents/com~apple~CloudDocs",
1621
1963
  "iCloud Drive",
1622
1964
  // OneDrive variants Microsoft ships across editions.
@@ -1629,7 +1971,7 @@ var SYNC_PREFIXES = [
1629
1971
  "Google Drive",
1630
1972
  "GoogleDrive",
1631
1973
  "CloudStorage/GoogleDrive",
1632
- // Box, pCloud, Sync.com, MEGA high-signal names worth refusing.
1974
+ // Box, pCloud, Sync.com, MEGA: high-signal names worth refusing.
1633
1975
  "Box Sync",
1634
1976
  "pCloud Drive",
1635
1977
  "Sync.com",
@@ -1687,10 +2029,10 @@ function atomicJsonMerge(opts) {
1687
2029
  preBytes = fs.readFileSync(path$1, "utf8");
1688
2030
  if (preBytes.trim().length > 0) {
1689
2031
  try {
1690
- parsed = JSON.parse(preBytes);
2032
+ parsed = JSON.parse(preBytes.replace(/^\uFEFF/, ""));
1691
2033
  } catch (err) {
1692
2034
  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\`.`
2035
+ `refusing to wire ${path$1}: existing file is not valid JSON (${err.message}). Hand-fix the file, then re-run \`alter wire\`.`
1694
2036
  );
1695
2037
  }
1696
2038
  if (typeof parsed !== "object" || Array.isArray(parsed) || parsed === null) {
@@ -1823,7 +2165,7 @@ function wireFileTarget(args) {
1823
2165
  const sync = detectSyncedVolume(client.configPath);
1824
2166
  if (sync) {
1825
2167
  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.`
2168
+ `refusing to wire ${client.label}: config path ${sync.resolvedPath} lives under ${sync.matchedPrefix}. Synced volumes propagate credentials across devices: move the config off the sync root, or run wire on the device you want to target.`
1827
2169
  );
1828
2170
  }
1829
2171
  const cfHeaders = {};
@@ -1912,14 +2254,13 @@ function wireClaudeCode(args) {
1912
2254
  };
1913
2255
  }
1914
2256
  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;
2257
+ const here = path.dirname(url.fileURLToPath(importMetaUrl));
2258
+ const siblingBridge = path.join(here, "..", "dist", "mcp-bridge.js");
2259
+ if (fs.existsSync(siblingBridge)) return siblingBridge;
2260
+ const srcBridge = path.join(here, "..", "mcp-bridge.js");
2261
+ if (fs.existsSync(srcBridge)) return srcBridge;
2262
+ const npmGlobalBridge = path.join(here, "mcp-bridge.js");
2263
+ if (fs.existsSync(npmGlobalBridge)) return npmGlobalBridge;
1923
2264
  return null;
1924
2265
  }
1925
2266
  function unwire() {
@@ -2073,19 +2414,19 @@ var TOOL_COSTS = {
2073
2414
  complete_knot: 0,
2074
2415
  check_golden_thread: 0,
2075
2416
  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
2417
+ // L1 ($0.01)
2418
+ assess_traits: 0.01,
2419
+ get_trait_snapshot: 0.01,
2420
+ // L2 ($0.10)
2421
+ get_full_trait_vector: 0.1,
2422
+ get_side_quest_graph: 0.1,
2423
+ // L3 ($0.30)
2424
+ query_graph_similarity: 0.3,
2425
+ // L4 ($0.60)
2426
+ compute_belonging: 0.6,
2427
+ // L5 ($1.00)
2428
+ get_match_recommendations: 1,
2429
+ generate_match_narrative: 1
2089
2430
  };
2090
2431
  var TOOL_BLAST_RADIUS = {
2091
2432
  // Low: read-only reference
@@ -2125,13 +2466,110 @@ var TOOL_BLAST_RADIUS = {
2125
2466
  query_graph_similarity: "high"
2126
2467
  };
2127
2468
 
2469
+ // src/pricing.generated.ts
2470
+ init_cjs_shims();
2471
+ var GENERATED_TOOL_TIERS = {
2472
+ // L0 (free)
2473
+ alter_presence_read: 0,
2474
+ alter_resolve_handle: 0,
2475
+ check_assessment_status: 0,
2476
+ create_identity_stub: 0,
2477
+ dispute_attestation: 0,
2478
+ get_competencies: 0,
2479
+ get_earning_summary: 0,
2480
+ get_engagement_level: 0,
2481
+ get_identity_earnings: 0,
2482
+ get_identity_trust_score: 0,
2483
+ get_network_stats: 0,
2484
+ get_privacy_budget: 0,
2485
+ get_profile: 0,
2486
+ initiate_assessment: 0,
2487
+ list_archetypes: 0,
2488
+ query_matches: 0,
2489
+ recommend_tool: 0,
2490
+ search_identities: 0,
2491
+ verify_identity: 0,
2492
+ // L1 ($0.01)
2493
+ assess_traits: 1,
2494
+ attest_domain: 1,
2495
+ get_trait_snapshot: 1,
2496
+ poll_requirement_matches: 1,
2497
+ submit_context: 1,
2498
+ submit_social_links: 1,
2499
+ submit_structured_profile: 1,
2500
+ // L2 ($0.10)
2501
+ attest_claim_provenance: 2,
2502
+ get_full_trait_vector: 2,
2503
+ get_side_quest_graph: 2,
2504
+ submit_batch_context: 2,
2505
+ // L3 ($0.30)
2506
+ alter_alignment: 3,
2507
+ query_graph_similarity: 3,
2508
+ // L4 ($0.60)
2509
+ compute_belonging: 4,
2510
+ // L5 ($1.00)
2511
+ generate_match_narrative: 5,
2512
+ get_match_recommendations: 5,
2513
+ query_field: 5
2514
+ };
2515
+ var GENERATED_TOOL_PRICING = {
2516
+ // L1: $0.01
2517
+ assess_traits: 0.01,
2518
+ attest_domain: 0.01,
2519
+ get_trait_snapshot: 0.01,
2520
+ poll_requirement_matches: 0.01,
2521
+ submit_context: 0.01,
2522
+ submit_social_links: 0.01,
2523
+ submit_structured_profile: 0.01,
2524
+ // L2: $0.10
2525
+ attest_claim_provenance: 0.1,
2526
+ get_full_trait_vector: 0.1,
2527
+ get_side_quest_graph: 0.1,
2528
+ submit_batch_context: 0.1,
2529
+ // L3: $0.30
2530
+ alter_alignment: 0.3,
2531
+ query_graph_similarity: 0.3,
2532
+ // L4: $0.60
2533
+ compute_belonging: 0.6,
2534
+ // L5: $1.00
2535
+ generate_match_narrative: 1,
2536
+ get_match_recommendations: 1,
2537
+ query_field: 1
2538
+ };
2539
+ var TIER_PRICES = {
2540
+ L0: 0,
2541
+ L1: 0.01,
2542
+ L2: 0.1,
2543
+ L3: 0.3,
2544
+ L4: 0.6,
2545
+ L5: 1
2546
+ };
2547
+ var ADVERTISED_TOOL_COUNTS = {
2548
+ /** Free (L0) tools visible to anonymous / agent-class callers. */
2549
+ free: 26,
2550
+ /** Premium (L1-L5) tools requiring x402 payment. */
2551
+ premium: 8,
2552
+ /** Messaging tools (member-self-only; excluded from external advertisement). */
2553
+ messaging: 0,
2554
+ /** Total publicly advertised (free + premium). */
2555
+ total: 34
2556
+ };
2557
+ var REVENUE_SPLIT = {
2558
+ weaver: 0.75,
2559
+ // data subject (Identity Income)
2560
+ facilitator: 0.05,
2561
+ alter: 0.15,
2562
+ treasury: 0.05
2563
+ };
2564
+
2128
2565
  // src/homepage.ts
2129
2566
  init_cjs_shims();
2130
2567
  var HOMEPAGE_LIMITS = {
2131
2568
  whoami_max_chars: 240,
2132
2569
  opener_max_chars: 280,
2133
2570
  pronouns_max_chars: 32,
2134
- attunement_glyph_max_chars: 16
2571
+ attunement_glyph_max_chars: 16,
2572
+ contact_email_max_chars: 254
2135
2573
  };
2136
2574
 
2137
2575
  // src/themes.ts
@@ -2145,6 +2583,7 @@ var THEME_LIMITS = {
2145
2583
  };
2146
2584
  var OSC8_ALLOWED_SCHEMES = ["https:", "mailto:"];
2147
2585
 
2586
+ exports.ADVERTISED_TOOL_COUNTS = ADVERTISED_TOOL_COUNTS;
2148
2587
  exports.ALL_CLIENTS = ALL_CLIENTS;
2149
2588
  exports.AlterAuthError = AlterAuthError;
2150
2589
  exports.AlterClient = AlterClient;
@@ -2157,21 +2596,30 @@ exports.AlterProvenanceError = AlterProvenanceError;
2157
2596
  exports.AlterRateLimited = AlterRateLimited;
2158
2597
  exports.AlterTimeoutError = AlterTimeoutError;
2159
2598
  exports.AlterToolError = AlterToolError;
2599
+ exports.BelowFloorError = BelowFloorError;
2160
2600
  exports.CLAUDE_CODE = CLAUDE_CODE;
2161
2601
  exports.CLAUDE_DESKTOP = CLAUDE_DESKTOP;
2602
+ exports.CLIENT_CHANNEL = CLIENT_CHANNEL;
2603
+ exports.CLIENT_ID = CLIENT_ID;
2162
2604
  exports.CURSOR = CURSOR;
2163
2605
  exports.DEFAULT_DOMAIN = DEFAULT_DOMAIN;
2164
2606
  exports.DEFAULT_ENDPOINT = DEFAULT_ENDPOINT;
2165
2607
  exports.DEFAULT_VERIFY_AT_ALLOWLIST = DEFAULT_VERIFY_AT_ALLOWLIST;
2166
2608
  exports.FREE_TOOL_NAMES = FREE_TOOL_NAMES;
2609
+ exports.GENERATED_TOOL_PRICING = GENERATED_TOOL_PRICING;
2610
+ exports.GENERATED_TOOL_TIERS = GENERATED_TOOL_TIERS;
2167
2611
  exports.HOMEPAGE_LIMITS = HOMEPAGE_LIMITS;
2612
+ exports.KNOWN_FLOOR_PUBLIC_KEYS = KNOWN_FLOOR_PUBLIC_KEYS;
2168
2613
  exports.MCPClient = MCPClient;
2169
2614
  exports.MCP_PROTOCOL_VERSION = MCP_PROTOCOL_VERSION;
2615
+ exports.MIN_VERSION_ENDPOINT = MIN_VERSION_ENDPOINT;
2170
2616
  exports.OSC8_ALLOWED_SCHEMES = OSC8_ALLOWED_SCHEMES;
2171
2617
  exports.PREMIUM_TOOL_NAMES = PREMIUM_TOOL_NAMES;
2618
+ exports.REVENUE_SPLIT = REVENUE_SPLIT;
2172
2619
  exports.SDK_NAME = SDK_NAME;
2173
2620
  exports.SDK_VERSION = SDK_VERSION;
2174
2621
  exports.THEME_LIMITS = THEME_LIMITS;
2622
+ exports.TIER_PRICES = TIER_PRICES;
2175
2623
  exports.TOOL_BLAST_RADIUS = TOOL_BLAST_RADIUS;
2176
2624
  exports.TOOL_COSTS = TOOL_COSTS;
2177
2625
  exports.TOOL_TIERS = TOOL_TIERS;
@@ -2180,8 +2628,12 @@ exports.X402Client = X402Client;
2180
2628
  exports.base64urlDecode = base64urlDecode;
2181
2629
  exports.base64urlEncode = base64urlEncode2;
2182
2630
  exports.canonicalArgsSha256 = canonicalArgsSha256;
2631
+ exports.canonicalJson = canonicalJson;
2183
2632
  exports.canonicalStringify = canonicalStringify;
2633
+ exports.checkMinVersion = checkMinVersion;
2184
2634
  exports.clearDiscoveryCache = clearDiscoveryCache;
2635
+ exports.compareSemver = compareSemver;
2636
+ exports.computeKeyId = computeKeyId;
2185
2637
  exports.decodeDid = decodeDid;
2186
2638
  exports.detectSyncedVolume = detectSyncedVolume;
2187
2639
  exports.discover = discover;
@@ -2205,6 +2657,7 @@ exports.sign = sign;
2205
2657
  exports.signInvocation = signInvocation;
2206
2658
  exports.unwire = unwire;
2207
2659
  exports.verify = verify;
2660
+ exports.verifyFloorSignature = verifyFloorSignature;
2208
2661
  exports.verifyProvenance = verifyProvenance;
2209
2662
  exports.verifyToolSignatures = verifyToolSignatures;
2210
2663
  exports.wire = wire;