@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.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,12 +15,6 @@ 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
18
  var __esm = (fn, res) => function __init() {
24
19
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
25
20
  };
@@ -369,11 +364,11 @@ async function tryWellKnown(host, file, timeoutMs, fetchImpl) {
369
364
  }
370
365
  if (resp.type === "opaqueredirect" || resp.status >= 300 && resp.status < 400) {
371
366
  throw new AlterNetworkError(
372
- `${url} \u2192 redirect rejected (discovery must not follow redirects; validate the server configuration)`
367
+ `${url} -> redirect rejected (discovery must not follow redirects; validate the server configuration)`
373
368
  );
374
369
  }
375
370
  if (resp.status === 404) return null;
376
- if (!resp.ok) throw new AlterNetworkError(`${url} \u2192 HTTP ${resp.status}`);
371
+ if (!resp.ok) throw new AlterNetworkError(`${url} -> HTTP ${resp.status}`);
377
372
  const doc = await resp.json();
378
373
  if (file === "mcp.json") {
379
374
  const remotes = doc.remotes || [];
@@ -406,6 +401,309 @@ function ensureMcpPath(url) {
406
401
  return url;
407
402
  }
408
403
  }
404
+
405
+ // src/meta.ts
406
+ var SDK_NAME = "@truealter/sdk";
407
+ var SDK_VERSION = "0.5.3" ;
408
+
409
+ // src/floor-preflight.ts
410
+ var MIN_VERSION_ENDPOINT = "/v1/clients/min-version";
411
+ var CLIENT_ID = "alter-identity";
412
+ var CLIENT_CHANNEL = "npm";
413
+ var IN_MEMORY_TTL_DEFAULT_MS = 60 * 60 * 1e3;
414
+ var IN_MEMORY_TTL_MIN_MS = 60 * 1e3;
415
+ var IN_MEMORY_TTL_MAX_MS = 24 * 60 * 60 * 1e3;
416
+ var DISK_FRESH_MS = 24 * 60 * 60 * 1e3;
417
+ var DISK_WARN_MS = 7 * 24 * 60 * 60 * 1e3;
418
+ var FETCH_TIMEOUT_MS = 4e3;
419
+ function computeKeyId(publicKeyPem) {
420
+ if (!publicKeyPem) return "00000000";
421
+ const pub = createPublicKey({ key: publicKeyPem, format: "pem" });
422
+ const jwk = pub.export({ format: "jwk" });
423
+ const rawBytes = Buffer.from(jwk.x, "base64url");
424
+ return createHash("sha256").update(rawBytes).digest("hex").slice(0, 8);
425
+ }
426
+ function canonicalJson(obj) {
427
+ return JSON.stringify(sortKeysDeep(obj));
428
+ }
429
+ function sortKeysDeep(value) {
430
+ if (Array.isArray(value)) {
431
+ return value.map(sortKeysDeep);
432
+ }
433
+ if (value !== null && typeof value === "object") {
434
+ const obj = value;
435
+ const sorted = {};
436
+ for (const k of Object.keys(obj).sort()) {
437
+ sorted[k] = sortKeysDeep(obj[k]);
438
+ }
439
+ return sorted;
440
+ }
441
+ return value;
442
+ }
443
+ var KNOWN_FLOOR_PUBLIC_KEYS = {
444
+ "8aa59e05": `-----BEGIN PUBLIC KEY-----
445
+ MCowBQYDK2VwAyEAgqw28dlniOuiTE1f4BxCPSEgMLaPtHsO8wN5RWEwEhE=
446
+ -----END PUBLIC KEY-----`,
447
+ "640f7d9a": `-----BEGIN PUBLIC KEY-----
448
+ MCowBQYDK2VwAyEARzvAWayDwHvZRfOZizGZe+/a7PF082WGhyMS3tx06H4=
449
+ -----END PUBLIC KEY-----`
450
+ };
451
+ var BelowFloorError = class extends Error {
452
+ name = "BelowFloorError";
453
+ code = "client_below_floor";
454
+ client_version;
455
+ min_version;
456
+ upgrade_cmd;
457
+ channel;
458
+ envelope;
459
+ constructor(envelope) {
460
+ super(envelope.error.message);
461
+ this.envelope = envelope;
462
+ this.client_version = envelope.error.client_version;
463
+ this.min_version = envelope.error.min_version;
464
+ this.upgrade_cmd = envelope.error.upgrade_cmd;
465
+ this.channel = envelope.error.channel;
466
+ Object.setPrototypeOf(this, new.target.prototype);
467
+ }
468
+ };
469
+ var memCache = null;
470
+ async function checkMinVersion(opts = {}) {
471
+ const apiBase = opts.apiBase ?? defaultApiBase();
472
+ const clientVersion = opts.clientVersion ?? SDK_VERSION;
473
+ const clientId = opts.clientId ?? CLIENT_ID;
474
+ const channel = opts.channel ?? CLIENT_CHANNEL;
475
+ const knownKeys = opts.knownFloorPublicKeys ?? KNOWN_FLOOR_PUBLIC_KEYS;
476
+ const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
477
+ const now = opts.now ?? Date.now;
478
+ const cachePath = opts.diskCachePath === void 0 ? defaultDiskCachePath() : opts.diskCachePath;
479
+ const mem = readInMemoryCache(now);
480
+ if (mem) {
481
+ return compareAndPermit(mem, {
482
+ clientVersion,
483
+ clientId,
484
+ channel,
485
+ diagnostic: "mem-cache-hit"
486
+ });
487
+ }
488
+ const disk = cachePath ? readDiskCache(cachePath, knownKeys) : null;
489
+ const diskAgeMs = disk ? now() - disk.fetched_at_ms : Number.POSITIVE_INFINITY;
490
+ let fetched = null;
491
+ let fetchError = null;
492
+ if (!disk || diskAgeMs > IN_MEMORY_TTL_DEFAULT_MS) {
493
+ try {
494
+ fetched = await fetchFloorDoc(apiBase, fetchImpl, knownKeys);
495
+ } catch (err) {
496
+ fetchError = err.message ?? "fetch-error";
497
+ }
498
+ }
499
+ if (fetched) {
500
+ populateMemCache(fetched, now());
501
+ if (cachePath) writeDiskCache(cachePath, fetched, now());
502
+ return compareAndPermit(fetched, {
503
+ clientVersion,
504
+ clientId,
505
+ channel,
506
+ diagnostic: "fetched"
507
+ });
508
+ }
509
+ if (disk) {
510
+ populateMemCache(disk.doc, disk.fetched_at_ms);
511
+ if (diskAgeMs > DISK_WARN_MS) {
512
+ return compareAndPermit(disk.doc, {
513
+ clientVersion,
514
+ clientId,
515
+ channel,
516
+ diagnostic: "below-floor-offline-stale-or-permit",
517
+ warn: `floor cache is >7d old and backend unreachable (${fetchError ?? "no refresh attempted"}); permitting if above floor`
518
+ });
519
+ }
520
+ if (diskAgeMs > DISK_FRESH_MS) {
521
+ return compareAndPermit(disk.doc, {
522
+ clientVersion,
523
+ clientId,
524
+ channel,
525
+ diagnostic: "warn-stale-permit",
526
+ warn: `floor cache is ${Math.round(diskAgeMs / (60 * 60 * 1e3))}h old; refresh recommended`
527
+ });
528
+ }
529
+ return compareAndPermit(disk.doc, {
530
+ clientVersion,
531
+ clientId,
532
+ channel,
533
+ diagnostic: "disk-cache-hit"
534
+ });
535
+ }
536
+ return {
537
+ ok: true,
538
+ floor: null,
539
+ diagnostic: "no-cache-no-fetch-permit",
540
+ warn: `floor preflight skipped: backend unreachable (${fetchError ?? "unknown"})`
541
+ };
542
+ }
543
+ function compareAndPermit(doc, ctx) {
544
+ const floor = lookupFloor(doc, ctx.clientId, ctx.channel);
545
+ if (!floor) {
546
+ return { ok: true, floor: null, diagnostic: `${ctx.diagnostic}+no-floor`, warn: ctx.warn };
547
+ }
548
+ if (compareSemver(ctx.clientVersion, floor.min_version) >= 0) {
549
+ return { ok: true, floor, diagnostic: ctx.diagnostic, warn: ctx.warn };
550
+ }
551
+ const envelope = {
552
+ error: {
553
+ code: "client_below_floor",
554
+ message: `Your ${ctx.clientId} is too old. Upgrade required.`,
555
+ client_version: ctx.clientVersion,
556
+ min_version: floor.min_version,
557
+ upgrade_cmd: floor.upgrade_cmd,
558
+ channel: ctx.channel
559
+ }
560
+ };
561
+ throw new BelowFloorError(envelope);
562
+ }
563
+ function lookupFloor(doc, clientId, channel) {
564
+ const entry = doc.floors[clientId];
565
+ if (!entry) return null;
566
+ if (isChannelFloor(entry)) return entry;
567
+ const exact = entry[channel];
568
+ if (exact) return exact;
569
+ const fallback = entry["unknown"];
570
+ if (fallback) return fallback;
571
+ return null;
572
+ }
573
+ function isChannelFloor(v) {
574
+ return typeof v.min_version === "string" && typeof v.upgrade_cmd === "string";
575
+ }
576
+ function compareSemver(a, b) {
577
+ const [aMaj, aMin, aPat, aPre] = parseSemver(a);
578
+ const [bMaj, bMin, bPat, bPre] = parseSemver(b);
579
+ if (aMaj !== bMaj) return aMaj - bMaj;
580
+ if (aMin !== bMin) return aMin - bMin;
581
+ if (aPat !== bPat) return aPat - bPat;
582
+ if (aPre && !bPre) return -1;
583
+ if (!aPre && bPre) return 1;
584
+ if (aPre && bPre) return aPre.localeCompare(bPre);
585
+ return 0;
586
+ }
587
+ function parseSemver(v) {
588
+ const m = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?/.exec(v);
589
+ if (!m) return [0, 0, 0, null];
590
+ return [Number(m[1]), Number(m[2]), Number(m[3]), m[4] ?? null];
591
+ }
592
+ function verifyFloorSignature(doc, keys = KNOWN_FLOOR_PUBLIC_KEYS) {
593
+ const pem = keys[doc.key_id];
594
+ if (!pem) return false;
595
+ if (computeKeyId(pem) !== doc.key_id) return false;
596
+ try {
597
+ const pubKeyObject = createPublicKey({ key: pem, format: "pem" });
598
+ const canonical = canonicalJson({
599
+ floors: doc.floors,
600
+ served_at: doc.served_at
601
+ });
602
+ return verify$1(
603
+ null,
604
+ Buffer.from(canonical, "utf-8"),
605
+ pubKeyObject,
606
+ Buffer.from(doc.signature, "hex")
607
+ );
608
+ } catch {
609
+ return false;
610
+ }
611
+ }
612
+ async function fetchFloorDoc(apiBase, fetchImpl, knownKeys) {
613
+ const url = `${apiBase.replace(/\/+$/, "")}${MIN_VERSION_ENDPOINT}`;
614
+ let response;
615
+ try {
616
+ response = await fetchImpl(url, {
617
+ headers: {
618
+ accept: "application/json",
619
+ "X-Alter-Client-Id": CLIENT_ID,
620
+ "X-Alter-Client-Version": SDK_VERSION,
621
+ "X-Alter-Client-Channel": CLIENT_CHANNEL,
622
+ "User-Agent": `${SDK_NAME}/${SDK_VERSION}`
623
+ },
624
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
625
+ });
626
+ } catch (err) {
627
+ throw new Error(`network: ${err.message ?? String(err)}`);
628
+ }
629
+ if (!response.ok) throw new Error(`http-${response.status}`);
630
+ const body = await response.json();
631
+ if (!body || !body.floors || !body.signature || !body.key_id) {
632
+ throw new Error("malformed-floor-doc");
633
+ }
634
+ if (!verifyFloorSignature(body, knownKeys)) {
635
+ throw new Error("signature-invalid");
636
+ }
637
+ return body;
638
+ }
639
+ function readInMemoryCache(now) {
640
+ if (!memCache) return null;
641
+ if (now() - memCache.fetched_at_ms > memCache.ttl_ms) {
642
+ memCache = null;
643
+ return null;
644
+ }
645
+ return memCache.doc;
646
+ }
647
+ function populateMemCache(doc, fetched_at_ms) {
648
+ const ttlSec = doc.cache_ttl_seconds ?? 3600;
649
+ const ttlMs = Math.min(
650
+ Math.max(ttlSec * 1e3, IN_MEMORY_TTL_MIN_MS),
651
+ IN_MEMORY_TTL_MAX_MS
652
+ );
653
+ memCache = { doc, fetched_at_ms, ttl_ms: ttlMs };
654
+ }
655
+ function defaultDiskCachePath() {
656
+ const xdg = process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config");
657
+ return join(xdg, "alter", "floor-cache.json");
658
+ }
659
+ function readDiskCache(path, knownKeys) {
660
+ if (process.platform !== "win32") {
661
+ let st;
662
+ try {
663
+ st = statSync(path);
664
+ } catch {
665
+ return null;
666
+ }
667
+ const euid = typeof process.geteuid === "function" ? process.geteuid() : st.uid;
668
+ if (st.uid !== euid) return null;
669
+ if ((st.mode & 511) !== 384) return null;
670
+ }
671
+ let raw;
672
+ try {
673
+ raw = readFileSync(path, "utf-8");
674
+ } catch {
675
+ return null;
676
+ }
677
+ let parsed;
678
+ try {
679
+ parsed = JSON.parse(raw);
680
+ } catch {
681
+ return null;
682
+ }
683
+ if (!parsed.doc || typeof parsed.fetched_at_ms !== "number") return null;
684
+ if (!verifyFloorSignature(parsed.doc, knownKeys)) {
685
+ return null;
686
+ }
687
+ return parsed;
688
+ }
689
+ function writeDiskCache(path, doc, now_ms) {
690
+ const entry = { doc, fetched_at_ms: now_ms };
691
+ const payload = JSON.stringify(entry);
692
+ try {
693
+ mkdirSync(dirname(path), { recursive: true, mode: 448 });
694
+ const tmp = `${path}.tmp`;
695
+ writeFileSync(tmp, payload, { mode: 384 });
696
+ try {
697
+ chmodSync(tmp, 384);
698
+ } catch {
699
+ }
700
+ renameSync(tmp, path);
701
+ } catch {
702
+ }
703
+ }
704
+ function defaultApiBase() {
705
+ return process.env.ALTER_API ?? "https://api.truealter.com";
706
+ }
409
707
  var X402Client = class {
410
708
  signer;
411
709
  maxPerQuery;
@@ -516,6 +814,9 @@ var MCPClient = class {
516
814
  x402;
517
815
  signing;
518
816
  extraHeaders;
817
+ preflightHook;
818
+ preflightPromise = null;
819
+ preflightDone = false;
519
820
  requestCounter = 0;
520
821
  initialised = false;
521
822
  constructor(opts = {}) {
@@ -524,17 +825,43 @@ var MCPClient = class {
524
825
  this.fetchImpl = opts.fetch ?? fetch;
525
826
  this.timeoutMs = opts.timeoutMs ?? 3e4;
526
827
  this.maxRetries = opts.maxRetries ?? 2;
527
- this.clientInfo = opts.clientInfo ?? { name: "@truealter/sdk", version: "0.2.0" };
828
+ this.clientInfo = opts.clientInfo ?? { name: SDK_NAME, version: SDK_VERSION };
528
829
  this.x402 = opts.x402;
529
830
  this.signing = opts.signing;
530
831
  this.extraHeaders = opts.extraHeaders;
832
+ this.preflightHook = opts.preflightHook;
833
+ }
834
+ /**
835
+ * Run the lazy preflight hook (D-MIN-VERSION-FLOOR-1) exactly once.
836
+ * Idempotent and serialised: concurrent callers share the same
837
+ * promise. Throws from the hook propagate to every concurrent caller.
838
+ */
839
+ async runPreflight() {
840
+ if (this.preflightDone) return;
841
+ if (!this.preflightHook) {
842
+ this.preflightDone = true;
843
+ return;
844
+ }
845
+ if (!this.preflightPromise) {
846
+ this.preflightPromise = this.preflightHook().then(
847
+ () => {
848
+ this.preflightDone = true;
849
+ },
850
+ (err) => {
851
+ this.preflightPromise = null;
852
+ throw err;
853
+ }
854
+ );
855
+ }
856
+ await this.preflightPromise;
531
857
  }
532
858
  /**
533
859
  * Send the MCP `initialize` handshake and capture the resulting session
534
- * id. Idempotent safe to call multiple times.
860
+ * id. Idempotent: safe to call multiple times.
535
861
  */
536
862
  async initialize() {
537
863
  if (this.initialised) return null;
864
+ await this.runPreflight();
538
865
  const result = await this.rpc("initialize", {
539
866
  protocolVersion: MCP_PROTOCOL_VERSION,
540
867
  capabilities: {},
@@ -694,7 +1021,10 @@ var MCPClient = class {
694
1021
  ...this.extraHeaders ?? {},
695
1022
  "Content-Type": "application/json",
696
1023
  Accept: "application/json",
697
- "User-Agent": `${this.clientInfo.name}/${this.clientInfo.version}`
1024
+ "User-Agent": `${this.clientInfo.name}/${this.clientInfo.version}`,
1025
+ "X-Alter-Client-Id": "alter-identity",
1026
+ "X-Alter-Client-Version": SDK_VERSION,
1027
+ "X-Alter-Client-Channel": "npm"
698
1028
  };
699
1029
  if (this.apiKey) headers["X-ALTER-API-Key"] = this.apiKey;
700
1030
  if (this.sessionId) headers["Mcp-Session-Id"] = this.sessionId;
@@ -971,7 +1301,7 @@ async function verifyToolSignatures(tools, signatures, opts = {}) {
971
1301
  out.push({ tool: tool.name, valid: false, reason: "no signature published" });
972
1302
  continue;
973
1303
  }
974
- const expectedHash = await sha256Hex(canonicalJson(tool.inputSchema));
1304
+ const expectedHash = await sha256Hex(canonicalJson2(tool.inputSchema));
975
1305
  if (expectedHash !== sig.schema_hash) {
976
1306
  out.push({ tool: tool.name, valid: false, reason: "schema hash mismatch" });
977
1307
  continue;
@@ -1055,23 +1385,23 @@ async function fetchJwks(url, fetchImpl) {
1055
1385
  }
1056
1386
  if (resp.type === "opaqueredirect" || resp.status >= 300 && resp.status < 400) {
1057
1387
  throw new AlterProvenanceError(
1058
- `${url} \u2192 redirect rejected (allowlist enforces initial URL only)`
1388
+ `${url} -> redirect rejected (allowlist enforces initial URL only)`
1059
1389
  );
1060
1390
  }
1061
- if (!resp.ok) throw new AlterNetworkError(`${url} \u2192 HTTP ${resp.status}`);
1391
+ if (!resp.ok) throw new AlterNetworkError(`${url} -> HTTP ${resp.status}`);
1062
1392
  const contentLength = resp.headers.get("content-length");
1063
1393
  if (contentLength !== null) {
1064
1394
  const n = Number.parseInt(contentLength, 10);
1065
1395
  if (Number.isFinite(n) && n > JWKS_MAX_BYTES) {
1066
1396
  throw new AlterProvenanceError(
1067
- `${url} \u2192 JWKS too large: ${n} > ${JWKS_MAX_BYTES} bytes`
1397
+ `${url} -> JWKS too large: ${n} > ${JWKS_MAX_BYTES} bytes`
1068
1398
  );
1069
1399
  }
1070
1400
  }
1071
1401
  const body = await resp.text();
1072
1402
  if (body.length > JWKS_MAX_BYTES) {
1073
1403
  throw new AlterProvenanceError(
1074
- `${url} \u2192 JWKS too large: ${body.length} > ${JWKS_MAX_BYTES} bytes`
1404
+ `${url} -> JWKS too large: ${body.length} > ${JWKS_MAX_BYTES} bytes`
1075
1405
  );
1076
1406
  }
1077
1407
  let doc;
@@ -1155,14 +1485,14 @@ async function sha256Hex(input) {
1155
1485
  function toArrayBuffer(view) {
1156
1486
  return view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength);
1157
1487
  }
1158
- function canonicalJson(value) {
1488
+ function canonicalJson2(value) {
1159
1489
  if (value === null || typeof value !== "object") return JSON.stringify(value);
1160
1490
  if (Array.isArray(value)) {
1161
- return `[${value.map(canonicalJson).join(",")}]`;
1491
+ return `[${value.map(canonicalJson2).join(",")}]`;
1162
1492
  }
1163
1493
  const obj = value;
1164
1494
  const keys = Object.keys(obj).sort();
1165
- return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJson(obj[k])}`).join(",")}}`;
1495
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJson2(obj[k])}`).join(",")}}`;
1166
1496
  }
1167
1497
 
1168
1498
  // src/client.ts
@@ -1178,11 +1508,21 @@ var AlterClient = class {
1178
1508
  this.options = options;
1179
1509
  this.x402 = options.x402;
1180
1510
  const endpoint = options.endpoint ?? DEFAULT_ENDPOINT;
1181
- this.mcp = new MCPClient({ ...options, endpoint, x402: options.x402 });
1511
+ const preflightHook = options.unsafe_skipVersionCheck ? void 0 : () => checkMinVersion({
1512
+ apiBase: options.apiBase,
1513
+ knownFloorPublicKeys: options.knownFloorPublicKeys,
1514
+ fetchImpl: options.fetch
1515
+ }).then(() => void 0);
1516
+ this.mcp = new MCPClient({
1517
+ ...options,
1518
+ endpoint,
1519
+ x402: options.x402,
1520
+ preflightHook
1521
+ });
1182
1522
  }
1183
1523
  /**
1184
1524
  * Resolve the MCP endpoint via discovery if requested. Safe to call
1185
- * multiple times the first successful lookup is cached.
1525
+ * multiple times: the first successful lookup is cached.
1186
1526
  */
1187
1527
  async discoverEndpoint() {
1188
1528
  if (this.discovered) return this.discovered;
@@ -1195,7 +1535,7 @@ var AlterClient = class {
1195
1535
  return this.discoveryPromise;
1196
1536
  }
1197
1537
  /**
1198
- * Initialise the MCP session. Optional every method calls
1538
+ * Initialise the MCP session. Optional: every method calls
1199
1539
  * `mcp.initialize()` lazily, but you can call this once at startup if
1200
1540
  * you want fail-fast behaviour.
1201
1541
  */
@@ -1203,11 +1543,11 @@ var AlterClient = class {
1203
1543
  await this.mcp.initialize();
1204
1544
  }
1205
1545
  // ── Free tier ────────────────────────────────────────────────────────
1206
- /** First handshake confirms the connection, returns trust tier and tool counts. */
1546
+ /** First handshake: confirms the connection, returns trust tier and tool counts. */
1207
1547
  async helloAgent() {
1208
1548
  return this.mcp.callTool("hello_agent", {});
1209
1549
  }
1210
- /** Resolve a ~handle (e.g. ~drew) to its canonical form and kind. No auth required. */
1550
+ /** Resolve a ~handle (e.g. ~example) to its canonical form and kind. No auth required. */
1211
1551
  async resolveHandle(args) {
1212
1552
  const payload = typeof args === "string" ? { query: args } : args;
1213
1553
  return this.mcp.callTool("alter_resolve_handle", payload);
@@ -1215,7 +1555,7 @@ var AlterClient = class {
1215
1555
  /** Verify a person is registered with ALTER (handle or id). */
1216
1556
  async verify(handleOrId, claims) {
1217
1557
  const args = handleOrId.includes("@") ? { member_id: "", email: handleOrId } : handleOrId.startsWith("~") ? (
1218
- // ~handle server resolves these via the member_id field
1558
+ // ~handle: server resolves these via the member_id field
1219
1559
  { member_id: handleOrId }
1220
1560
  ) : { member_id: handleOrId };
1221
1561
  if (claims) args.claims = claims;
@@ -1315,7 +1655,7 @@ var AlterClient = class {
1315
1655
  }
1316
1656
  // ── Alter-to-Alter Messaging ─────────────────────────────────────────
1317
1657
  // Wave 1: cross-handle direct messages between authenticated tilde
1318
- // handles. Default closed recipient must have granted the sender via
1658
+ // handles. Default closed: recipient must have granted the sender via
1319
1659
  // alter_message_grant. Spec: docs/technical/Alter-to-Alter Messaging.md.
1320
1660
  /** Send a direct message to another tilde handle. */
1321
1661
  async messageSend(args) {
@@ -1349,7 +1689,7 @@ var AlterClient = class {
1349
1689
  /**
1350
1690
  * Verify the ES256 provenance attestation on a tool response.
1351
1691
  * Accepts either a {@link ProvenanceEnvelope} or the raw `_meta`
1352
- * object the latter is more convenient for ad-hoc verification.
1692
+ * object: the latter is more convenient for ad-hoc verification.
1353
1693
  */
1354
1694
  async verifyProvenance(envelope) {
1355
1695
  if (!envelope) return { valid: false, reason: "no provenance envelope" };
@@ -1385,7 +1725,7 @@ function generateGenericMcpConfig(opts = {}) {
1385
1725
  const entry = {
1386
1726
  url: opts.endpoint ?? DEFAULT_ENDPOINT,
1387
1727
  transport: "streamable-http",
1388
- description: "ALTER Identity \u2014 psychometric identity field for AI agents"
1728
+ description: "ALTER Identity: psychometric identity field for AI agents"
1389
1729
  };
1390
1730
  if (Object.keys(headers).length > 0) entry.headers = headers;
1391
1731
  return { mcpServers: { [serverName]: entry } };
@@ -1411,17 +1751,13 @@ function generateClaudeDesktopConfig(opts = {}) {
1411
1751
  const entry = {
1412
1752
  command: bridgeCommand,
1413
1753
  env: env2,
1414
- description: "ALTER Identity \u2014 psychometric identity field for AI agents"
1754
+ description: "ALTER Identity: psychometric identity field for AI agents"
1415
1755
  };
1416
1756
  if (opts.extraArgs && opts.extraArgs.length > 0) {
1417
1757
  entry.args = [...opts.extraArgs];
1418
1758
  }
1419
1759
  return { mcpServers: { [serverName]: entry } };
1420
1760
  }
1421
-
1422
- // src/meta.ts
1423
- var SDK_NAME = "@truealter/sdk";
1424
- var SDK_VERSION = "0.3.0";
1425
1761
  var HOME = homedir();
1426
1762
  var PLAT = platform();
1427
1763
  function appData() {
@@ -1546,7 +1882,7 @@ function probeAll() {
1546
1882
  ];
1547
1883
  }
1548
1884
  var SYNC_PREFIXES = [
1549
- // iCloud Drive both the new and legacy mounts.
1885
+ // iCloud Drive: both the new and legacy mounts.
1550
1886
  "Library/Mobile Documents/com~apple~CloudDocs",
1551
1887
  "iCloud Drive",
1552
1888
  // OneDrive variants Microsoft ships across editions.
@@ -1559,7 +1895,7 @@ var SYNC_PREFIXES = [
1559
1895
  "Google Drive",
1560
1896
  "GoogleDrive",
1561
1897
  "CloudStorage/GoogleDrive",
1562
- // Box, pCloud, Sync.com, MEGA high-signal names worth refusing.
1898
+ // Box, pCloud, Sync.com, MEGA: high-signal names worth refusing.
1563
1899
  "Box Sync",
1564
1900
  "pCloud Drive",
1565
1901
  "Sync.com",
@@ -1611,10 +1947,10 @@ function atomicJsonMerge(opts) {
1611
1947
  preBytes = readFileSync(path, "utf8");
1612
1948
  if (preBytes.trim().length > 0) {
1613
1949
  try {
1614
- parsed = JSON.parse(preBytes);
1950
+ parsed = JSON.parse(preBytes.replace(/^\uFEFF/, ""));
1615
1951
  } catch (err) {
1616
1952
  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\`.`
1953
+ `refusing to wire ${path}: existing file is not valid JSON (${err.message}). Hand-fix the file, then re-run \`alter wire\`.`
1618
1954
  );
1619
1955
  }
1620
1956
  if (typeof parsed !== "object" || Array.isArray(parsed) || parsed === null) {
@@ -1747,7 +2083,7 @@ function wireFileTarget(args) {
1747
2083
  const sync = detectSyncedVolume(client.configPath);
1748
2084
  if (sync) {
1749
2085
  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.`
2086
+ `refusing to wire ${client.label}: config path ${sync.resolvedPath} lives under ${sync.matchedPrefix}. Synced volumes propagate credentials across devices: move the config off the sync root, or run wire on the device you want to target.`
1751
2087
  );
1752
2088
  }
1753
2089
  const cfHeaders = {};
@@ -1836,14 +2172,13 @@ function wireClaudeCode(args) {
1836
2172
  };
1837
2173
  }
1838
2174
  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;
2175
+ const here = dirname(fileURLToPath(import.meta.url));
2176
+ const siblingBridge = join(here, "..", "dist", "mcp-bridge.js");
2177
+ if (existsSync(siblingBridge)) return siblingBridge;
2178
+ const srcBridge = join(here, "..", "mcp-bridge.js");
2179
+ if (existsSync(srcBridge)) return srcBridge;
2180
+ const npmGlobalBridge = join(here, "mcp-bridge.js");
2181
+ if (existsSync(npmGlobalBridge)) return npmGlobalBridge;
1847
2182
  return null;
1848
2183
  }
1849
2184
  function unwire() {
@@ -1996,19 +2331,19 @@ var TOOL_COSTS = {
1996
2331
  complete_knot: 0,
1997
2332
  check_golden_thread: 0,
1998
2333
  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
2334
+ // L1 ($0.01)
2335
+ assess_traits: 0.01,
2336
+ get_trait_snapshot: 0.01,
2337
+ // L2 ($0.10)
2338
+ get_full_trait_vector: 0.1,
2339
+ get_side_quest_graph: 0.1,
2340
+ // L3 ($0.30)
2341
+ query_graph_similarity: 0.3,
2342
+ // L4 ($0.60)
2343
+ compute_belonging: 0.6,
2344
+ // L5 ($1.00)
2345
+ get_match_recommendations: 1,
2346
+ generate_match_narrative: 1
2012
2347
  };
2013
2348
  var TOOL_BLAST_RADIUS = {
2014
2349
  // Low: read-only reference
@@ -2048,12 +2383,108 @@ var TOOL_BLAST_RADIUS = {
2048
2383
  query_graph_similarity: "high"
2049
2384
  };
2050
2385
 
2386
+ // src/pricing.generated.ts
2387
+ var GENERATED_TOOL_TIERS = {
2388
+ // L0 (free)
2389
+ alter_presence_read: 0,
2390
+ alter_resolve_handle: 0,
2391
+ check_assessment_status: 0,
2392
+ create_identity_stub: 0,
2393
+ dispute_attestation: 0,
2394
+ get_competencies: 0,
2395
+ get_earning_summary: 0,
2396
+ get_engagement_level: 0,
2397
+ get_identity_earnings: 0,
2398
+ get_identity_trust_score: 0,
2399
+ get_network_stats: 0,
2400
+ get_privacy_budget: 0,
2401
+ get_profile: 0,
2402
+ initiate_assessment: 0,
2403
+ list_archetypes: 0,
2404
+ query_matches: 0,
2405
+ recommend_tool: 0,
2406
+ search_identities: 0,
2407
+ verify_identity: 0,
2408
+ // L1 ($0.01)
2409
+ assess_traits: 1,
2410
+ attest_domain: 1,
2411
+ get_trait_snapshot: 1,
2412
+ poll_requirement_matches: 1,
2413
+ submit_context: 1,
2414
+ submit_social_links: 1,
2415
+ submit_structured_profile: 1,
2416
+ // L2 ($0.10)
2417
+ attest_claim_provenance: 2,
2418
+ get_full_trait_vector: 2,
2419
+ get_side_quest_graph: 2,
2420
+ submit_batch_context: 2,
2421
+ // L3 ($0.30)
2422
+ alter_alignment: 3,
2423
+ query_graph_similarity: 3,
2424
+ // L4 ($0.60)
2425
+ compute_belonging: 4,
2426
+ // L5 ($1.00)
2427
+ generate_match_narrative: 5,
2428
+ get_match_recommendations: 5,
2429
+ query_field: 5
2430
+ };
2431
+ var GENERATED_TOOL_PRICING = {
2432
+ // L1: $0.01
2433
+ assess_traits: 0.01,
2434
+ attest_domain: 0.01,
2435
+ get_trait_snapshot: 0.01,
2436
+ poll_requirement_matches: 0.01,
2437
+ submit_context: 0.01,
2438
+ submit_social_links: 0.01,
2439
+ submit_structured_profile: 0.01,
2440
+ // L2: $0.10
2441
+ attest_claim_provenance: 0.1,
2442
+ get_full_trait_vector: 0.1,
2443
+ get_side_quest_graph: 0.1,
2444
+ submit_batch_context: 0.1,
2445
+ // L3: $0.30
2446
+ alter_alignment: 0.3,
2447
+ query_graph_similarity: 0.3,
2448
+ // L4: $0.60
2449
+ compute_belonging: 0.6,
2450
+ // L5: $1.00
2451
+ generate_match_narrative: 1,
2452
+ get_match_recommendations: 1,
2453
+ query_field: 1
2454
+ };
2455
+ var TIER_PRICES = {
2456
+ L0: 0,
2457
+ L1: 0.01,
2458
+ L2: 0.1,
2459
+ L3: 0.3,
2460
+ L4: 0.6,
2461
+ L5: 1
2462
+ };
2463
+ var ADVERTISED_TOOL_COUNTS = {
2464
+ /** Free (L0) tools visible to anonymous / agent-class callers. */
2465
+ free: 26,
2466
+ /** Premium (L1-L5) tools requiring x402 payment. */
2467
+ premium: 8,
2468
+ /** Messaging tools (member-self-only; excluded from external advertisement). */
2469
+ messaging: 0,
2470
+ /** Total publicly advertised (free + premium). */
2471
+ total: 34
2472
+ };
2473
+ var REVENUE_SPLIT = {
2474
+ weaver: 0.75,
2475
+ // data subject (Identity Income)
2476
+ facilitator: 0.05,
2477
+ alter: 0.15,
2478
+ treasury: 0.05
2479
+ };
2480
+
2051
2481
  // src/homepage.ts
2052
2482
  var HOMEPAGE_LIMITS = {
2053
2483
  whoami_max_chars: 240,
2054
2484
  opener_max_chars: 280,
2055
2485
  pronouns_max_chars: 32,
2056
- attunement_glyph_max_chars: 16
2486
+ attunement_glyph_max_chars: 16,
2487
+ contact_email_max_chars: 254
2057
2488
  };
2058
2489
 
2059
2490
  // src/themes.ts
@@ -2066,4 +2497,4 @@ var THEME_LIMITS = {
2066
2497
  };
2067
2498
  var OSC8_ALLOWED_SCHEMES = ["https:", "mailto:"];
2068
2499
 
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 };
2500
+ export { ADVERTISED_TOOL_COUNTS, ALL_CLIENTS, AlterAuthError, AlterClient, AlterDiscoveryError, AlterError, AlterInvalidResponse, AlterNetworkError, AlterPaymentRequired, AlterProvenanceError, AlterRateLimited, AlterTimeoutError, AlterToolError, BelowFloorError, CLAUDE_CODE, CLAUDE_DESKTOP, CLIENT_CHANNEL, CLIENT_ID, CURSOR, DEFAULT_DOMAIN, DEFAULT_ENDPOINT, DEFAULT_VERIFY_AT_ALLOWLIST, FREE_TOOL_NAMES, GENERATED_TOOL_PRICING, GENERATED_TOOL_TIERS, HOMEPAGE_LIMITS, KNOWN_FLOOR_PUBLIC_KEYS, MCPClient, MCP_PROTOCOL_VERSION, MIN_VERSION_ENDPOINT, OSC8_ALLOWED_SCHEMES, PREMIUM_TOOL_NAMES, REVENUE_SPLIT, SDK_NAME, SDK_VERSION, THEME_LIMITS, TIER_PRICES, TOOL_BLAST_RADIUS, TOOL_COSTS, TOOL_TIERS, VSCODE, X402Client, base64urlDecode, base64urlEncode2 as base64urlEncode, canonicalArgsSha256, canonicalJson, canonicalStringify, checkMinVersion, clearDiscoveryCache, compareSemver, computeKeyId, decodeDid, detectSyncedVolume, discover, encodeDid, fetchPublicKeys, generateClaudeConfig, generateClaudeDesktopConfig, generateCursorConfig, generateGenericMcpConfig, generateKeypair, keypairFromPrivateKey, loadPrivateKey, parsePaymentHeader, probeAll, probeByDir, probeClaudeCode, readWireState, resolveVerifyAt, sha2562 as sha256, sign, signInvocation, unwire, verify, verifyFloorSignature, verifyProvenance, verifyToolSignatures, wire, writeWireState };