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