@truealter/sdk 0.2.4 → 0.5.0
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 +50 -36
- package/dist/bin/alter-identity.js +841 -31
- package/dist/bin/mcp-bridge.js +201 -9
- package/dist/index.cjs +785 -24
- package/dist/index.d.cts +677 -41
- package/dist/index.d.ts +677 -41
- package/dist/index.js +712 -28
- package/package.json +4 -3
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var p256 = require('@noble/curves/p256');
|
|
4
|
+
var sha256 = require('@noble/hashes/sha256');
|
|
5
|
+
var utils = require('@noble/hashes/utils');
|
|
6
|
+
var crypto$1 = require('crypto');
|
|
3
7
|
var ed25519 = require('@noble/ed25519');
|
|
4
8
|
var sha512 = require('@noble/hashes/sha512');
|
|
5
|
-
var
|
|
9
|
+
var child_process = require('child_process');
|
|
10
|
+
var os = require('os');
|
|
11
|
+
var path = require('path');
|
|
12
|
+
var process$1 = require('process');
|
|
13
|
+
var fs = require('fs');
|
|
6
14
|
|
|
7
15
|
function _interopNamespace(e) {
|
|
8
16
|
if (e && e.__esModule) return e;
|
|
@@ -24,7 +32,159 @@ function _interopNamespace(e) {
|
|
|
24
32
|
|
|
25
33
|
var ed25519__namespace = /*#__PURE__*/_interopNamespace(ed25519);
|
|
26
34
|
|
|
35
|
+
var __defProp = Object.defineProperty;
|
|
36
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
37
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
38
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
39
|
+
var __esm = (fn, res) => function __init() {
|
|
40
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
41
|
+
};
|
|
42
|
+
var __export = (target, all) => {
|
|
43
|
+
for (var name in all)
|
|
44
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
45
|
+
};
|
|
46
|
+
var __copyProps = (to, from, except, desc) => {
|
|
47
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
48
|
+
for (let key of __getOwnPropNames(from))
|
|
49
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
50
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
51
|
+
}
|
|
52
|
+
return to;
|
|
53
|
+
};
|
|
54
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
55
|
+
|
|
56
|
+
// node_modules/tsup/assets/cjs_shims.js
|
|
57
|
+
var init_cjs_shims = __esm({
|
|
58
|
+
"node_modules/tsup/assets/cjs_shims.js"() {
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// src/signing.ts
|
|
63
|
+
var signing_exports = {};
|
|
64
|
+
__export(signing_exports, {
|
|
65
|
+
canonicalArgsSha256: () => canonicalArgsSha256,
|
|
66
|
+
canonicalStringify: () => canonicalStringify,
|
|
67
|
+
loadPrivateKey: () => loadPrivateKey,
|
|
68
|
+
signInvocation: () => signInvocation
|
|
69
|
+
});
|
|
70
|
+
function canonicalStringify(value) {
|
|
71
|
+
return stringifyInner(value);
|
|
72
|
+
}
|
|
73
|
+
function stringifyInner(value) {
|
|
74
|
+
if (value === null) return "null";
|
|
75
|
+
if (value === void 0) {
|
|
76
|
+
throw new TypeError("canonicalStringify: undefined is not representable in JSON");
|
|
77
|
+
}
|
|
78
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
79
|
+
if (typeof value === "number") {
|
|
80
|
+
if (!Number.isFinite(value)) {
|
|
81
|
+
throw new TypeError("canonicalStringify: non-finite numbers are not representable");
|
|
82
|
+
}
|
|
83
|
+
return JSON.stringify(value);
|
|
84
|
+
}
|
|
85
|
+
if (typeof value === "string") return encodeString(value);
|
|
86
|
+
if (Array.isArray(value)) {
|
|
87
|
+
return "[" + value.map((v) => stringifyInner(v)).join(",") + "]";
|
|
88
|
+
}
|
|
89
|
+
if (typeof value === "object") {
|
|
90
|
+
const obj = value;
|
|
91
|
+
const keys = Object.keys(obj).sort();
|
|
92
|
+
return "{" + keys.map((k) => encodeString(k) + ":" + stringifyInner(obj[k])).join(",") + "}";
|
|
93
|
+
}
|
|
94
|
+
throw new TypeError(`canonicalStringify: unsupported type ${typeof value}`);
|
|
95
|
+
}
|
|
96
|
+
function encodeString(s) {
|
|
97
|
+
return JSON.stringify(s);
|
|
98
|
+
}
|
|
99
|
+
function canonicalArgsSha256(toolArgs) {
|
|
100
|
+
const canonical = canonicalStringify(toolArgs ?? {});
|
|
101
|
+
const bytes = new TextEncoder().encode(canonical);
|
|
102
|
+
const digest = sha256.sha256(bytes);
|
|
103
|
+
return bytesToHex(digest);
|
|
104
|
+
}
|
|
105
|
+
function bytesToHex(bytes) {
|
|
106
|
+
let out = "";
|
|
107
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
108
|
+
out += bytes[i].toString(16).padStart(2, "0");
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
function base64urlEncode(bytes) {
|
|
113
|
+
const raw = typeof bytes === "string" ? new TextEncoder().encode(bytes) : bytes;
|
|
114
|
+
if (typeof Buffer !== "undefined") {
|
|
115
|
+
return Buffer.from(raw).toString("base64url");
|
|
116
|
+
}
|
|
117
|
+
let binary = "";
|
|
118
|
+
for (let i = 0; i < raw.length; i++) binary += String.fromCharCode(raw[i]);
|
|
119
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
120
|
+
}
|
|
121
|
+
function loadPrivateKey(key) {
|
|
122
|
+
if (key instanceof Uint8Array) {
|
|
123
|
+
if (key.length !== 32) {
|
|
124
|
+
throw new TypeError("ES256 raw private key must be 32 bytes.");
|
|
125
|
+
}
|
|
126
|
+
return key;
|
|
127
|
+
}
|
|
128
|
+
if (typeof key === "string" && key.includes("-----BEGIN")) {
|
|
129
|
+
const keyObj = crypto$1.createPrivateKey({ key, format: "pem" });
|
|
130
|
+
const jwk = keyObj.export({ format: "jwk" });
|
|
131
|
+
if (jwk.crv !== "P-256" || !jwk.d) {
|
|
132
|
+
throw new TypeError("PEM is not a P-256 private key.");
|
|
133
|
+
}
|
|
134
|
+
return base64urlDecodeToBytes(jwk.d);
|
|
135
|
+
}
|
|
136
|
+
throw new TypeError("loadPrivateKey: expected Uint8Array(32) or PEM string.");
|
|
137
|
+
}
|
|
138
|
+
function base64urlDecodeToBytes(s) {
|
|
139
|
+
const pad = s.length % 4 === 0 ? "" : "=".repeat(4 - s.length % 4);
|
|
140
|
+
const b64 = (s + pad).replace(/-/g, "+").replace(/_/g, "/");
|
|
141
|
+
if (typeof Buffer !== "undefined") {
|
|
142
|
+
return new Uint8Array(Buffer.from(b64, "base64"));
|
|
143
|
+
}
|
|
144
|
+
const binary = atob(b64);
|
|
145
|
+
const out = new Uint8Array(binary.length);
|
|
146
|
+
for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
|
|
147
|
+
return out;
|
|
148
|
+
}
|
|
149
|
+
function signInvocation(toolName, toolArgs, options) {
|
|
150
|
+
const { kid, privateKey, handle } = options;
|
|
151
|
+
const nonce = options.nonce ?? base64urlEncode(utils.randomBytes(24));
|
|
152
|
+
const iat = options.iatSeconds ?? Math.floor(Date.now() / 1e3);
|
|
153
|
+
const claims = {
|
|
154
|
+
tool: toolName,
|
|
155
|
+
args_sha256: canonicalArgsSha256(toolArgs ?? {}),
|
|
156
|
+
nonce,
|
|
157
|
+
iat,
|
|
158
|
+
iss: handle
|
|
159
|
+
};
|
|
160
|
+
const headerB64 = base64urlEncode(JSON.stringify({ alg: "ES256", kid }));
|
|
161
|
+
const payloadB64 = base64urlEncode(JSON.stringify(claims));
|
|
162
|
+
const signingInput = `${headerB64}.${payloadB64}`;
|
|
163
|
+
const signingBytes = new TextEncoder().encode(signingInput);
|
|
164
|
+
const dBytes = loadPrivateKey(privateKey);
|
|
165
|
+
const digest = sha256.sha256(signingBytes);
|
|
166
|
+
const sig = p256.p256.sign(digest, dBytes, { prehash: false });
|
|
167
|
+
const sigBytes = sig.toCompactRawBytes();
|
|
168
|
+
const sigB64 = base64urlEncode(sigBytes);
|
|
169
|
+
return `${signingInput}.${sigB64}`;
|
|
170
|
+
}
|
|
171
|
+
var init_signing = __esm({
|
|
172
|
+
"src/signing.ts"() {
|
|
173
|
+
init_cjs_shims();
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// src/index.ts
|
|
178
|
+
init_cjs_shims();
|
|
179
|
+
|
|
180
|
+
// src/client.ts
|
|
181
|
+
init_cjs_shims();
|
|
182
|
+
|
|
183
|
+
// src/discovery.ts
|
|
184
|
+
init_cjs_shims();
|
|
185
|
+
|
|
27
186
|
// src/errors.ts
|
|
187
|
+
init_cjs_shims();
|
|
28
188
|
var AlterError = class extends Error {
|
|
29
189
|
code;
|
|
30
190
|
cause;
|
|
@@ -123,7 +283,12 @@ async function discover(domain, opts = {}) {
|
|
|
123
283
|
try {
|
|
124
284
|
const dnsHit = await tryDns(host);
|
|
125
285
|
if (dnsHit) {
|
|
126
|
-
const
|
|
286
|
+
const parsed = validateDiscoveredUrl(dnsHit, "dns");
|
|
287
|
+
const result = {
|
|
288
|
+
url: parsed.toString().replace(/\/$/, ""),
|
|
289
|
+
transport: "streamable-http",
|
|
290
|
+
source: "dns"
|
|
291
|
+
};
|
|
127
292
|
if (cache) _cache.set(host, result);
|
|
128
293
|
return result;
|
|
129
294
|
}
|
|
@@ -164,6 +329,28 @@ function normaliseDomain(input) {
|
|
|
164
329
|
if (!host) throw new AlterDiscoveryError(`Empty domain: "${input}"`);
|
|
165
330
|
return host;
|
|
166
331
|
}
|
|
332
|
+
function validateDiscoveredUrl(url, source) {
|
|
333
|
+
let parsed;
|
|
334
|
+
try {
|
|
335
|
+
parsed = new URL(url);
|
|
336
|
+
} catch {
|
|
337
|
+
throw new AlterDiscoveryError(`${source}: malformed URL ${url}`);
|
|
338
|
+
}
|
|
339
|
+
if (parsed.protocol !== "https:") {
|
|
340
|
+
throw new AlterDiscoveryError(
|
|
341
|
+
`${source}: non-https MCP endpoint rejected (got ${parsed.protocol}//${parsed.hostname})`
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
if (parsed.username || parsed.password) {
|
|
345
|
+
throw new AlterDiscoveryError(
|
|
346
|
+
`${source}: MCP endpoint must not contain userinfo (user:pass@host)`
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
if (!parsed.hostname) {
|
|
350
|
+
throw new AlterDiscoveryError(`${source}: MCP endpoint missing hostname`);
|
|
351
|
+
}
|
|
352
|
+
return parsed;
|
|
353
|
+
}
|
|
167
354
|
async function tryDns(host) {
|
|
168
355
|
let resolveTxt;
|
|
169
356
|
try {
|
|
@@ -224,14 +411,17 @@ async function tryWellKnown(host, file, timeoutMs, fetchImpl) {
|
|
|
224
411
|
if (file === "mcp.json") {
|
|
225
412
|
const remotes = doc.remotes || [];
|
|
226
413
|
const remote = remotes.find((r) => r.transportType === "streamable-http" || r.transportType === "http");
|
|
227
|
-
const
|
|
228
|
-
if (!
|
|
229
|
-
|
|
414
|
+
const rawUrl = remote?.url || doc.url;
|
|
415
|
+
if (!rawUrl) return null;
|
|
416
|
+
const parsed = validateDiscoveredUrl(rawUrl, "mcp.json");
|
|
417
|
+
return { url: parsed.toString().replace(/\/$/, ""), transport: "streamable-http", source: "mcp.json", raw: doc };
|
|
230
418
|
}
|
|
231
419
|
const mcpHost = doc.mcp;
|
|
232
420
|
if (!mcpHost) return null;
|
|
421
|
+
const normalised = ensureMcpPath(mcpHost);
|
|
422
|
+
validateDiscoveredUrl(normalised, "alter.json");
|
|
233
423
|
return {
|
|
234
|
-
url:
|
|
424
|
+
url: normalised,
|
|
235
425
|
transport: "streamable-http",
|
|
236
426
|
source: "alter.json",
|
|
237
427
|
publicKey: doc.pk,
|
|
@@ -250,7 +440,11 @@ function ensureMcpPath(url) {
|
|
|
250
440
|
}
|
|
251
441
|
}
|
|
252
442
|
|
|
443
|
+
// src/mcp.ts
|
|
444
|
+
init_cjs_shims();
|
|
445
|
+
|
|
253
446
|
// src/x402.ts
|
|
447
|
+
init_cjs_shims();
|
|
254
448
|
var X402Client = class {
|
|
255
449
|
signer;
|
|
256
450
|
maxPerQuery;
|
|
@@ -277,11 +471,14 @@ var X402Client = class {
|
|
|
277
471
|
if (!this.assets.has(envelope.asset)) {
|
|
278
472
|
throw new AlterError("PAYMENT_REQUIRED", `asset ${envelope.asset} not permitted by client policy`);
|
|
279
473
|
}
|
|
280
|
-
if (this.maxPerQuery !== void 0
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
474
|
+
if (this.maxPerQuery !== void 0) {
|
|
475
|
+
const amt = Number(envelope.amount);
|
|
476
|
+
if (!Number.isFinite(amt) || amt < 0 || amt > this.maxPerQuery) {
|
|
477
|
+
throw new AlterError(
|
|
478
|
+
"PAYMENT_REQUIRED",
|
|
479
|
+
`quote ${envelope.amount} ${envelope.asset} exceeds maxPerQuery ${this.maxPerQuery}`
|
|
480
|
+
);
|
|
481
|
+
}
|
|
285
482
|
}
|
|
286
483
|
if (!this.signer) {
|
|
287
484
|
throw new AlterPaymentRequired(envelope.resource ?? "unknown", envelope);
|
|
@@ -339,6 +536,8 @@ var MCPClient = class {
|
|
|
339
536
|
maxRetries;
|
|
340
537
|
clientInfo;
|
|
341
538
|
x402;
|
|
539
|
+
signing;
|
|
540
|
+
extraHeaders;
|
|
342
541
|
requestCounter = 0;
|
|
343
542
|
initialised = false;
|
|
344
543
|
constructor(opts = {}) {
|
|
@@ -349,6 +548,8 @@ var MCPClient = class {
|
|
|
349
548
|
this.maxRetries = opts.maxRetries ?? 2;
|
|
350
549
|
this.clientInfo = opts.clientInfo ?? { name: "@truealter/sdk", version: "0.2.0" };
|
|
351
550
|
this.x402 = opts.x402;
|
|
551
|
+
this.signing = opts.signing;
|
|
552
|
+
this.extraHeaders = opts.extraHeaders;
|
|
352
553
|
}
|
|
353
554
|
/**
|
|
354
555
|
* Send the MCP `initialize` handshake and capture the resulting session
|
|
@@ -435,6 +636,7 @@ var MCPClient = class {
|
|
|
435
636
|
method
|
|
436
637
|
};
|
|
437
638
|
if (params !== void 0) payload.params = params;
|
|
639
|
+
const signatureHeader = this.buildSignatureHeader(method, params);
|
|
438
640
|
let attempt = 0;
|
|
439
641
|
let lastErr = null;
|
|
440
642
|
while (attempt <= this.maxRetries) {
|
|
@@ -445,7 +647,7 @@ var MCPClient = class {
|
|
|
445
647
|
try {
|
|
446
648
|
resp = await this.fetchImpl(this.endpoint, {
|
|
447
649
|
method: "POST",
|
|
448
|
-
headers: this.buildHeaders(),
|
|
650
|
+
headers: this.buildHeaders(signatureHeader),
|
|
449
651
|
body: JSON.stringify(payload),
|
|
450
652
|
signal: controller.signal
|
|
451
653
|
});
|
|
@@ -472,7 +674,8 @@ var MCPClient = class {
|
|
|
472
674
|
throw new AlterPaymentRequired(this.guessToolName(payload), envelope);
|
|
473
675
|
}
|
|
474
676
|
if (resp.status === 429) {
|
|
475
|
-
const
|
|
677
|
+
const rawRetryAfter = Number(resp.headers.get("Retry-After") ?? 60);
|
|
678
|
+
const retryAfter = Number.isFinite(rawRetryAfter) && rawRetryAfter >= 0 ? Math.min(rawRetryAfter, 300) : 60;
|
|
476
679
|
if (attempt > this.maxRetries) {
|
|
477
680
|
throw new AlterRateLimited(`HTTP 429 on ${method}`, retryAfter);
|
|
478
681
|
}
|
|
@@ -508,16 +711,36 @@ var MCPClient = class {
|
|
|
508
711
|
}
|
|
509
712
|
throw lastErr ?? new AlterNetworkError(`MCP ${method}: exhausted retries`);
|
|
510
713
|
}
|
|
511
|
-
buildHeaders() {
|
|
714
|
+
buildHeaders(extra) {
|
|
512
715
|
const headers = {
|
|
716
|
+
...this.extraHeaders ?? {},
|
|
513
717
|
"Content-Type": "application/json",
|
|
514
718
|
Accept: "application/json",
|
|
515
719
|
"User-Agent": `${this.clientInfo.name}/${this.clientInfo.version}`
|
|
516
720
|
};
|
|
517
721
|
if (this.apiKey) headers["X-ALTER-API-Key"] = this.apiKey;
|
|
518
722
|
if (this.sessionId) headers["Mcp-Session-Id"] = this.sessionId;
|
|
723
|
+
if (extra) Object.assign(headers, extra);
|
|
519
724
|
return headers;
|
|
520
725
|
}
|
|
726
|
+
/**
|
|
727
|
+
* Produce the `Mcp-Invocation-Signature` header for a `tools/call`
|
|
728
|
+
* payload, when signing is configured. Returns `undefined` when no
|
|
729
|
+
* signing key is attached or the method is not `tools/call`.
|
|
730
|
+
*/
|
|
731
|
+
buildSignatureHeader(method, params) {
|
|
732
|
+
if (!this.signing) return void 0;
|
|
733
|
+
if (method !== "tools/call") return void 0;
|
|
734
|
+
const p = params;
|
|
735
|
+
if (!p?.name) return void 0;
|
|
736
|
+
const { signInvocation: signInvocation2 } = (init_signing(), __toCommonJS(signing_exports));
|
|
737
|
+
const headerValue = signInvocation2(p.name, p.arguments ?? {}, {
|
|
738
|
+
kid: this.signing.kid,
|
|
739
|
+
privateKey: this.signing.privateKey,
|
|
740
|
+
handle: this.signing.handle
|
|
741
|
+
});
|
|
742
|
+
return { "Mcp-Invocation-Signature": headerValue };
|
|
743
|
+
}
|
|
521
744
|
async extractPaymentEnvelope(resp) {
|
|
522
745
|
const headerValue = resp.headers.get("X-402-Payment") ?? resp.headers.get("x-402-payment");
|
|
523
746
|
if (headerValue) {
|
|
@@ -555,6 +778,12 @@ async function safeText(resp) {
|
|
|
555
778
|
return "";
|
|
556
779
|
}
|
|
557
780
|
}
|
|
781
|
+
|
|
782
|
+
// src/provenance.ts
|
|
783
|
+
init_cjs_shims();
|
|
784
|
+
|
|
785
|
+
// src/auth.ts
|
|
786
|
+
init_cjs_shims();
|
|
558
787
|
ed25519__namespace.etc.sha512Sync = (...m) => sha512.sha512(ed25519__namespace.etc.concatBytes(...m));
|
|
559
788
|
function generateKeypair() {
|
|
560
789
|
const privateKey = utils.randomBytes(32);
|
|
@@ -593,14 +822,14 @@ async function verify(publicKeyHex, signatureHex, message) {
|
|
|
593
822
|
}
|
|
594
823
|
function encodeDid(publicKey) {
|
|
595
824
|
const bytes = typeof publicKey === "string" ? utils.hexToBytes(publicKey) : publicKey;
|
|
596
|
-
return `ed25519:${
|
|
825
|
+
return `ed25519:${base64urlEncode2(bytes)}`;
|
|
597
826
|
}
|
|
598
827
|
function decodeDid(did) {
|
|
599
828
|
const ed25519Match = did.match(/^ed25519:(.+)$/);
|
|
600
829
|
if (ed25519Match) return base64urlDecode(ed25519Match[1]);
|
|
601
830
|
throw new Error(`Unrecognised DID encoding: ${did}`);
|
|
602
831
|
}
|
|
603
|
-
function
|
|
832
|
+
function base64urlEncode2(bytes) {
|
|
604
833
|
let b64;
|
|
605
834
|
if (typeof Buffer !== "undefined") {
|
|
606
835
|
b64 = Buffer.from(bytes).toString("base64");
|
|
@@ -631,6 +860,7 @@ var DEFAULT_VERIFY_AT_ALLOWLIST = Object.freeze([
|
|
|
631
860
|
"api.truealter.com",
|
|
632
861
|
"mcp.truealter.com"
|
|
633
862
|
]);
|
|
863
|
+
var ALTER_PLATFORM_ISS = "did:alter:platform";
|
|
634
864
|
async function verifyProvenance(envelope, opts = {}) {
|
|
635
865
|
const token = typeof envelope === "string" ? envelope : envelope.token;
|
|
636
866
|
if (!token) return { valid: false, reason: "empty token" };
|
|
@@ -713,6 +943,15 @@ async function verifyProvenance(envelope, opts = {}) {
|
|
|
713
943
|
if (typeof payload.iat === "number" && payload.iat > now + 300) {
|
|
714
944
|
return { valid: false, reason: "issued in the future", payload, kid: header.kid };
|
|
715
945
|
}
|
|
946
|
+
const expectedIss = opts.expectedIss !== void 0 ? opts.expectedIss : ALTER_PLATFORM_ISS;
|
|
947
|
+
if (expectedIss !== "" && payload.iss !== expectedIss) {
|
|
948
|
+
return {
|
|
949
|
+
valid: false,
|
|
950
|
+
reason: `iss mismatch: expected "${expectedIss}", got "${payload.iss}"`,
|
|
951
|
+
payload,
|
|
952
|
+
kid: header.kid
|
|
953
|
+
};
|
|
954
|
+
}
|
|
716
955
|
return { valid: true, payload, kid: header.kid };
|
|
717
956
|
}
|
|
718
957
|
async function verifyToolSignatures(tools, signatures) {
|
|
@@ -909,10 +1148,10 @@ var AlterClient = class {
|
|
|
909
1148
|
}
|
|
910
1149
|
/** Verify a person is registered with ALTER (handle or id). */
|
|
911
1150
|
async verify(handleOrId, claims) {
|
|
912
|
-
const args = handleOrId.includes("@") ? {
|
|
913
|
-
// ~handle — server resolves these via the
|
|
914
|
-
{
|
|
915
|
-
) : {
|
|
1151
|
+
const args = handleOrId.includes("@") ? { member_id: "", email: handleOrId } : handleOrId.startsWith("~") ? (
|
|
1152
|
+
// ~handle — server resolves these via the member_id field
|
|
1153
|
+
{ member_id: handleOrId }
|
|
1154
|
+
) : { member_id: handleOrId };
|
|
916
1155
|
if (claims) args.claims = claims;
|
|
917
1156
|
return this.mcp.callTool("verify_identity", args);
|
|
918
1157
|
}
|
|
@@ -1069,7 +1308,14 @@ var AlterClient = class {
|
|
|
1069
1308
|
}
|
|
1070
1309
|
};
|
|
1071
1310
|
|
|
1311
|
+
// src/index.ts
|
|
1312
|
+
init_signing();
|
|
1313
|
+
|
|
1314
|
+
// src/adapters/claude-code.ts
|
|
1315
|
+
init_cjs_shims();
|
|
1316
|
+
|
|
1072
1317
|
// src/adapters/generic-mcp.ts
|
|
1318
|
+
init_cjs_shims();
|
|
1073
1319
|
function generateGenericMcpConfig(opts = {}) {
|
|
1074
1320
|
const serverName = opts.serverName ?? "alter";
|
|
1075
1321
|
const headers = { ...opts.headers ?? {} };
|
|
@@ -1089,11 +1335,488 @@ function generateClaudeConfig(opts = {}) {
|
|
|
1089
1335
|
}
|
|
1090
1336
|
|
|
1091
1337
|
// src/adapters/cursor.ts
|
|
1338
|
+
init_cjs_shims();
|
|
1092
1339
|
function generateCursorConfig(opts = {}) {
|
|
1093
1340
|
return generateGenericMcpConfig({ serverName: "alter", ...opts });
|
|
1094
1341
|
}
|
|
1095
1342
|
|
|
1343
|
+
// src/adapters/claude-desktop.ts
|
|
1344
|
+
init_cjs_shims();
|
|
1345
|
+
function generateClaudeDesktopConfig(opts = {}) {
|
|
1346
|
+
const serverName = opts.serverName ?? "alter";
|
|
1347
|
+
const bridgeCommand = opts.bridgeCommand ?? "alter-mcp-bridge";
|
|
1348
|
+
const env2 = {};
|
|
1349
|
+
env2.ALTER_MCP_ENDPOINT = opts.endpoint ?? DEFAULT_ENDPOINT;
|
|
1350
|
+
if (opts.apiKey) env2.ALTER_API_KEY = opts.apiKey;
|
|
1351
|
+
const entry = {
|
|
1352
|
+
command: bridgeCommand,
|
|
1353
|
+
env: env2,
|
|
1354
|
+
description: "ALTER Identity \u2014 psychometric identity field for AI agents"
|
|
1355
|
+
};
|
|
1356
|
+
if (opts.extraArgs && opts.extraArgs.length > 0) {
|
|
1357
|
+
entry.args = [...opts.extraArgs];
|
|
1358
|
+
}
|
|
1359
|
+
return { mcpServers: { [serverName]: entry } };
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
// src/wire/index.ts
|
|
1363
|
+
init_cjs_shims();
|
|
1364
|
+
|
|
1365
|
+
// src/meta.ts
|
|
1366
|
+
init_cjs_shims();
|
|
1367
|
+
var SDK_NAME = "@truealter/sdk";
|
|
1368
|
+
var SDK_VERSION = "0.3.0";
|
|
1369
|
+
|
|
1370
|
+
// src/wire/paths.ts
|
|
1371
|
+
init_cjs_shims();
|
|
1372
|
+
var HOME = os.homedir();
|
|
1373
|
+
var PLAT = os.platform();
|
|
1374
|
+
function appData() {
|
|
1375
|
+
return process$1.env.APPDATA ?? path.join(HOME, "AppData", "Roaming");
|
|
1376
|
+
}
|
|
1377
|
+
function xdgConfig() {
|
|
1378
|
+
return process$1.env.XDG_CONFIG_HOME ?? path.join(HOME, ".config");
|
|
1379
|
+
}
|
|
1380
|
+
function macAppSupport() {
|
|
1381
|
+
return path.join(HOME, "Library", "Application Support");
|
|
1382
|
+
}
|
|
1383
|
+
function claudeDesktopConfigPath() {
|
|
1384
|
+
if (PLAT === "darwin") return path.join(macAppSupport(), "Claude", "claude_desktop_config.json");
|
|
1385
|
+
if (PLAT === "win32") return path.join(appData(), "Claude", "claude_desktop_config.json");
|
|
1386
|
+
return path.join(xdgConfig(), "Claude", "claude_desktop_config.json");
|
|
1387
|
+
}
|
|
1388
|
+
function claudeDesktopDir() {
|
|
1389
|
+
if (PLAT === "darwin") return path.join(macAppSupport(), "Claude");
|
|
1390
|
+
if (PLAT === "win32") return path.join(appData(), "Claude");
|
|
1391
|
+
return path.join(xdgConfig(), "Claude");
|
|
1392
|
+
}
|
|
1393
|
+
function vscodeConfigPath() {
|
|
1394
|
+
if (PLAT === "darwin") return path.join(macAppSupport(), "Code", "User", "mcp.json");
|
|
1395
|
+
if (PLAT === "win32") return path.join(appData(), "Code", "User", "mcp.json");
|
|
1396
|
+
return path.join(xdgConfig(), "Code", "User", "mcp.json");
|
|
1397
|
+
}
|
|
1398
|
+
function vscodeDir() {
|
|
1399
|
+
if (PLAT === "darwin") return path.join(macAppSupport(), "Code", "User");
|
|
1400
|
+
if (PLAT === "win32") return path.join(appData(), "Code", "User");
|
|
1401
|
+
return path.join(xdgConfig(), "Code", "User");
|
|
1402
|
+
}
|
|
1403
|
+
var cursorDir = path.join(HOME, ".cursor");
|
|
1404
|
+
var cursorConfigPath = path.join(cursorDir, "mcp.json");
|
|
1405
|
+
var claudeCodeProbeDir = path.join(HOME, ".claude");
|
|
1406
|
+
var CLAUDE_CODE = {
|
|
1407
|
+
id: "claude-code",
|
|
1408
|
+
label: "Claude Code",
|
|
1409
|
+
configPath: null,
|
|
1410
|
+
probeDir: claudeCodeProbeDir,
|
|
1411
|
+
rootKey: "mcpServers"
|
|
1412
|
+
};
|
|
1413
|
+
var CURSOR = {
|
|
1414
|
+
id: "cursor",
|
|
1415
|
+
label: "Cursor",
|
|
1416
|
+
configPath: cursorConfigPath,
|
|
1417
|
+
probeDir: cursorDir,
|
|
1418
|
+
rootKey: "mcpServers"
|
|
1419
|
+
};
|
|
1420
|
+
var CLAUDE_DESKTOP = {
|
|
1421
|
+
id: "claude-desktop",
|
|
1422
|
+
label: "Claude Desktop",
|
|
1423
|
+
configPath: claudeDesktopConfigPath(),
|
|
1424
|
+
probeDir: claudeDesktopDir(),
|
|
1425
|
+
rootKey: "mcpServers"
|
|
1426
|
+
};
|
|
1427
|
+
var VSCODE = {
|
|
1428
|
+
id: "vscode",
|
|
1429
|
+
label: "VS Code",
|
|
1430
|
+
configPath: vscodeConfigPath(),
|
|
1431
|
+
probeDir: vscodeDir(),
|
|
1432
|
+
// VS Code's user-scoped mcp.json uses `servers`, not `mcpServers`.
|
|
1433
|
+
rootKey: "servers"
|
|
1434
|
+
};
|
|
1435
|
+
var ALL_CLIENTS = [CLAUDE_CODE, CURSOR, CLAUDE_DESKTOP, VSCODE];
|
|
1436
|
+
function alterConfigDir() {
|
|
1437
|
+
return path.join(xdgConfig(), "alter");
|
|
1438
|
+
}
|
|
1439
|
+
function wireStatePath() {
|
|
1440
|
+
return path.join(alterConfigDir(), "wire-state.json");
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
// src/wire/probe.ts
|
|
1444
|
+
init_cjs_shims();
|
|
1445
|
+
function probeClaudeCode() {
|
|
1446
|
+
try {
|
|
1447
|
+
const result = child_process.spawnSync("claude", ["--version"], {
|
|
1448
|
+
encoding: "utf8",
|
|
1449
|
+
shell: process.platform === "win32",
|
|
1450
|
+
timeout: 5e3
|
|
1451
|
+
});
|
|
1452
|
+
if (result.error) {
|
|
1453
|
+
return {
|
|
1454
|
+
client: ALL_CLIENTS.find((c) => c.id === "claude-code"),
|
|
1455
|
+
installed: false,
|
|
1456
|
+
reason: `claude binary not on PATH (${result.error.message})`
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1459
|
+
if (result.status === 0) {
|
|
1460
|
+
return {
|
|
1461
|
+
client: ALL_CLIENTS.find((c) => c.id === "claude-code"),
|
|
1462
|
+
installed: true,
|
|
1463
|
+
version: result.stdout.trim() || void 0,
|
|
1464
|
+
reason: "claude --version returned 0"
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1467
|
+
return {
|
|
1468
|
+
client: ALL_CLIENTS.find((c) => c.id === "claude-code"),
|
|
1469
|
+
installed: false,
|
|
1470
|
+
reason: `claude --version exited ${String(result.status)}`
|
|
1471
|
+
};
|
|
1472
|
+
} catch (err) {
|
|
1473
|
+
return {
|
|
1474
|
+
client: ALL_CLIENTS.find((c) => c.id === "claude-code"),
|
|
1475
|
+
installed: false,
|
|
1476
|
+
reason: err.message
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
function probeByDir(id) {
|
|
1481
|
+
const client = ALL_CLIENTS.find((c) => c.id === id);
|
|
1482
|
+
if (!client) throw new Error(`unknown client id: ${id}`);
|
|
1483
|
+
const installed = fs.existsSync(client.probeDir);
|
|
1484
|
+
return {
|
|
1485
|
+
client,
|
|
1486
|
+
installed,
|
|
1487
|
+
reason: installed ? `found ${client.probeDir}` : `no directory at ${client.probeDir}`
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
function probeAll() {
|
|
1491
|
+
return [
|
|
1492
|
+
probeClaudeCode(),
|
|
1493
|
+
probeByDir("cursor"),
|
|
1494
|
+
probeByDir("claude-desktop"),
|
|
1495
|
+
probeByDir("vscode")
|
|
1496
|
+
];
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
// src/wire/sync.ts
|
|
1500
|
+
init_cjs_shims();
|
|
1501
|
+
var SYNC_PREFIXES = [
|
|
1502
|
+
// iCloud Drive — both the new and legacy mounts.
|
|
1503
|
+
"Library/Mobile Documents/com~apple~CloudDocs",
|
|
1504
|
+
"iCloud Drive",
|
|
1505
|
+
// OneDrive variants Microsoft ships across editions.
|
|
1506
|
+
"OneDrive",
|
|
1507
|
+
"OneDrive - ",
|
|
1508
|
+
// Dropbox standard + enterprise mounts.
|
|
1509
|
+
"Dropbox",
|
|
1510
|
+
"Dropbox (",
|
|
1511
|
+
// Google Drive (ALTER does not integrate with Google; still refuse).
|
|
1512
|
+
"Google Drive",
|
|
1513
|
+
"GoogleDrive",
|
|
1514
|
+
"CloudStorage/GoogleDrive",
|
|
1515
|
+
// Box, pCloud, Sync.com, MEGA — high-signal names worth refusing.
|
|
1516
|
+
"Box Sync",
|
|
1517
|
+
"pCloud Drive",
|
|
1518
|
+
"Sync.com",
|
|
1519
|
+
"MEGAsync"
|
|
1520
|
+
];
|
|
1521
|
+
function detectSyncedVolume(path$1) {
|
|
1522
|
+
const absolute = path.resolve(path$1);
|
|
1523
|
+
const normalised = os.platform() === "win32" ? absolute.replace(/\\/g, "/") : absolute;
|
|
1524
|
+
for (const prefix of SYNC_PREFIXES) {
|
|
1525
|
+
if (normalised.includes(`/${prefix}/`) || normalised.includes(`/${prefix}`)) {
|
|
1526
|
+
return { refused: true, matchedPrefix: prefix, resolvedPath: absolute };
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
return null;
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
// src/wire/state.ts
|
|
1533
|
+
init_cjs_shims();
|
|
1534
|
+
var WIRE_STATE_VERSION = 1;
|
|
1535
|
+
function readWireState() {
|
|
1536
|
+
const path = wireStatePath();
|
|
1537
|
+
if (!fs.existsSync(path)) return null;
|
|
1538
|
+
try {
|
|
1539
|
+
const parsed = JSON.parse(fs.readFileSync(path, "utf8"));
|
|
1540
|
+
if (parsed.version !== WIRE_STATE_VERSION) {
|
|
1541
|
+
throw new Error(
|
|
1542
|
+
`wire-state.json version ${String(parsed.version)} is not supported by this SDK (expected ${WIRE_STATE_VERSION})`
|
|
1543
|
+
);
|
|
1544
|
+
}
|
|
1545
|
+
return parsed;
|
|
1546
|
+
} catch (err) {
|
|
1547
|
+
throw new Error(`failed to parse wire-state.json: ${err.message}`);
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
function writeWireState(state) {
|
|
1551
|
+
const path$1 = wireStatePath();
|
|
1552
|
+
fs.mkdirSync(path.dirname(path$1), { recursive: true, mode: 448 });
|
|
1553
|
+
fs.writeFileSync(path$1, JSON.stringify(state, null, 2) + "\n", { mode: 384 });
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
// src/wire/write.ts
|
|
1557
|
+
init_cjs_shims();
|
|
1558
|
+
function sha2562(bytes) {
|
|
1559
|
+
return crypto$1.createHash("sha256").update(bytes).digest("hex");
|
|
1560
|
+
}
|
|
1561
|
+
function atomicJsonMerge(opts) {
|
|
1562
|
+
const { path: path$1, timestamp, merge, idempotent = true } = opts;
|
|
1563
|
+
const tmpPath = `${path$1}.alter-tmp-${timestamp}`;
|
|
1564
|
+
const backupPath = `${path$1}.alter-backup-${timestamp}`;
|
|
1565
|
+
let existed = false;
|
|
1566
|
+
let preBytes = null;
|
|
1567
|
+
let parsed = {};
|
|
1568
|
+
if (fs.existsSync(path$1)) {
|
|
1569
|
+
existed = true;
|
|
1570
|
+
preBytes = fs.readFileSync(path$1, "utf8");
|
|
1571
|
+
if (preBytes.trim().length > 0) {
|
|
1572
|
+
try {
|
|
1573
|
+
parsed = JSON.parse(preBytes);
|
|
1574
|
+
} catch (err) {
|
|
1575
|
+
throw new Error(
|
|
1576
|
+
`refusing to wire ${path$1}: existing file is not valid JSON (${err.message}). Hand-fix the file, then re-run \`alter-identity wire\`.`
|
|
1577
|
+
);
|
|
1578
|
+
}
|
|
1579
|
+
if (typeof parsed !== "object" || Array.isArray(parsed) || parsed === null) {
|
|
1580
|
+
throw new Error(`refusing to wire ${path$1}: existing JSON root is not an object`);
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
const merged = merge(parsed);
|
|
1585
|
+
const serialised = JSON.stringify(merged, null, 2) + "\n";
|
|
1586
|
+
if (idempotent && preBytes !== null && preBytes === serialised) {
|
|
1587
|
+
return {
|
|
1588
|
+
path: path$1,
|
|
1589
|
+
backupPath: null,
|
|
1590
|
+
preSha256: sha2562(preBytes),
|
|
1591
|
+
postSha256: sha2562(preBytes),
|
|
1592
|
+
noop: true
|
|
1593
|
+
};
|
|
1594
|
+
}
|
|
1595
|
+
fs.mkdirSync(path.dirname(path$1), { recursive: true });
|
|
1596
|
+
fs.writeFileSync(tmpPath, serialised, { mode: 384 });
|
|
1597
|
+
try {
|
|
1598
|
+
if (existed) fs.copyFileSync(path$1, backupPath);
|
|
1599
|
+
fs.renameSync(tmpPath, path$1);
|
|
1600
|
+
} catch (err) {
|
|
1601
|
+
try {
|
|
1602
|
+
fs.unlinkSync(tmpPath);
|
|
1603
|
+
} catch {
|
|
1604
|
+
}
|
|
1605
|
+
throw err;
|
|
1606
|
+
}
|
|
1607
|
+
return {
|
|
1608
|
+
path: path$1,
|
|
1609
|
+
backupPath: existed ? backupPath : null,
|
|
1610
|
+
preSha256: preBytes === null ? null : sha2562(preBytes),
|
|
1611
|
+
postSha256: sha2562(serialised),
|
|
1612
|
+
noop: false
|
|
1613
|
+
};
|
|
1614
|
+
}
|
|
1615
|
+
function restoreFromBackup(path, backupPath) {
|
|
1616
|
+
if (backupPath === null) {
|
|
1617
|
+
if (fs.existsSync(path)) fs.unlinkSync(path);
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
if (!fs.existsSync(backupPath)) {
|
|
1621
|
+
throw new Error(`cannot restore ${path}: backup missing at ${backupPath}`);
|
|
1622
|
+
}
|
|
1623
|
+
fs.renameSync(backupPath, path);
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
// src/wire/index.ts
|
|
1627
|
+
var TIMESTAMP = () => String(Math.floor(Date.now() / 1e3));
|
|
1628
|
+
var ISO_NOW = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
1629
|
+
function clientById(id) {
|
|
1630
|
+
const hit = ALL_CLIENTS.find((c) => c.id === id);
|
|
1631
|
+
if (!hit) throw new Error(`unknown client id: ${id}`);
|
|
1632
|
+
return hit;
|
|
1633
|
+
}
|
|
1634
|
+
function wire(opts = {}) {
|
|
1635
|
+
const endpoint = opts.endpoint ?? DEFAULT_ENDPOINT;
|
|
1636
|
+
const apiKey = opts.apiKey;
|
|
1637
|
+
const probes = probeAll();
|
|
1638
|
+
const selection = opts.only ?? probes.filter((p) => p.installed).map((p) => p.client.id);
|
|
1639
|
+
const ts = TIMESTAMP();
|
|
1640
|
+
const targets = [];
|
|
1641
|
+
for (const id of selection) {
|
|
1642
|
+
const probe = id === "claude-code" ? probeClaudeCode() : probeByDir(id);
|
|
1643
|
+
if (!probe.installed && opts.skipMissing !== false) {
|
|
1644
|
+
targets.push({
|
|
1645
|
+
client: id,
|
|
1646
|
+
method: id === "claude-code" ? "cli" : "file",
|
|
1647
|
+
status: "skipped",
|
|
1648
|
+
...id === "claude-code" ? { command: "" } : { path: clientById(id).configPath ?? "", backupPath: null, rootKey: clientById(id).rootKey, serverName: "alter", preSha256: null, postSha256: "" },
|
|
1649
|
+
reason: probe.reason
|
|
1650
|
+
});
|
|
1651
|
+
continue;
|
|
1652
|
+
}
|
|
1653
|
+
try {
|
|
1654
|
+
if (id === "claude-code") {
|
|
1655
|
+
targets.push(wireClaudeCode({ endpoint, apiKey }));
|
|
1656
|
+
} else {
|
|
1657
|
+
targets.push(wireFileTarget({ id, endpoint, apiKey, timestamp: ts }));
|
|
1658
|
+
}
|
|
1659
|
+
} catch (err) {
|
|
1660
|
+
const message = err.message;
|
|
1661
|
+
targets.push({
|
|
1662
|
+
client: id,
|
|
1663
|
+
method: id === "claude-code" ? "cli" : "file",
|
|
1664
|
+
status: "failed",
|
|
1665
|
+
...id === "claude-code" ? { command: "" } : { path: clientById(id).configPath ?? "", backupPath: null, rootKey: clientById(id).rootKey, serverName: "alter", preSha256: null, postSha256: "" },
|
|
1666
|
+
reason: message
|
|
1667
|
+
});
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
const state = {
|
|
1671
|
+
version: 1,
|
|
1672
|
+
sdkVersion: SDK_VERSION,
|
|
1673
|
+
writtenAt: ISO_NOW(),
|
|
1674
|
+
endpoint,
|
|
1675
|
+
targets
|
|
1676
|
+
};
|
|
1677
|
+
writeWireState(state);
|
|
1678
|
+
return { state, probes };
|
|
1679
|
+
}
|
|
1680
|
+
function wireFileTarget(args) {
|
|
1681
|
+
const client = clientById(args.id);
|
|
1682
|
+
if (!client.configPath) {
|
|
1683
|
+
throw new Error(`client ${client.id} has no file-based config path`);
|
|
1684
|
+
}
|
|
1685
|
+
const sync = detectSyncedVolume(client.configPath);
|
|
1686
|
+
if (sync) {
|
|
1687
|
+
throw new Error(
|
|
1688
|
+
`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.`
|
|
1689
|
+
);
|
|
1690
|
+
}
|
|
1691
|
+
const entry = args.id === "claude-desktop" ? generateClaudeDesktopConfig({ endpoint: args.endpoint, apiKey: args.apiKey }) : generateGenericMcpConfig({ endpoint: args.endpoint, apiKey: args.apiKey });
|
|
1692
|
+
const rootKey = client.rootKey;
|
|
1693
|
+
const serverName = "alter";
|
|
1694
|
+
const result = atomicJsonMerge({
|
|
1695
|
+
path: client.configPath,
|
|
1696
|
+
timestamp: args.timestamp,
|
|
1697
|
+
merge: (existing) => {
|
|
1698
|
+
const bucket = existing[rootKey] ?? {};
|
|
1699
|
+
const source = entry.mcpServers.alter;
|
|
1700
|
+
return {
|
|
1701
|
+
...existing,
|
|
1702
|
+
[rootKey]: {
|
|
1703
|
+
...bucket,
|
|
1704
|
+
[serverName]: source
|
|
1705
|
+
}
|
|
1706
|
+
};
|
|
1707
|
+
}
|
|
1708
|
+
});
|
|
1709
|
+
return {
|
|
1710
|
+
client: args.id,
|
|
1711
|
+
method: "file",
|
|
1712
|
+
status: result.noop ? "already-wired" : "written",
|
|
1713
|
+
path: result.path,
|
|
1714
|
+
backupPath: result.backupPath,
|
|
1715
|
+
rootKey,
|
|
1716
|
+
serverName,
|
|
1717
|
+
preSha256: result.preSha256,
|
|
1718
|
+
postSha256: result.postSha256
|
|
1719
|
+
};
|
|
1720
|
+
}
|
|
1721
|
+
function wireClaudeCode(args) {
|
|
1722
|
+
const cmd = "claude";
|
|
1723
|
+
const argList = [
|
|
1724
|
+
"mcp",
|
|
1725
|
+
"add",
|
|
1726
|
+
"--scope",
|
|
1727
|
+
"user",
|
|
1728
|
+
"--transport",
|
|
1729
|
+
"http",
|
|
1730
|
+
"alter",
|
|
1731
|
+
args.endpoint
|
|
1732
|
+
];
|
|
1733
|
+
if (args.apiKey) {
|
|
1734
|
+
argList.push("--header", `X-ALTER-API-Key:${args.apiKey}`);
|
|
1735
|
+
}
|
|
1736
|
+
const full = `${cmd} ${argList.join(" ")}`;
|
|
1737
|
+
const run = child_process.spawnSync(cmd, argList, {
|
|
1738
|
+
encoding: "utf8",
|
|
1739
|
+
shell: process.platform === "win32",
|
|
1740
|
+
timeout: 1e4
|
|
1741
|
+
});
|
|
1742
|
+
if (run.error) {
|
|
1743
|
+
return {
|
|
1744
|
+
client: "claude-code",
|
|
1745
|
+
method: "cli",
|
|
1746
|
+
status: "failed",
|
|
1747
|
+
command: full,
|
|
1748
|
+
stdout: run.stdout,
|
|
1749
|
+
stderr: run.stderr,
|
|
1750
|
+
reason: run.error.message
|
|
1751
|
+
};
|
|
1752
|
+
}
|
|
1753
|
+
const stderr = (run.stderr ?? "").toLowerCase();
|
|
1754
|
+
const alreadyExists = stderr.includes("already exists") || stderr.includes("already configured");
|
|
1755
|
+
if (run.status === 0) {
|
|
1756
|
+
return { client: "claude-code", method: "cli", status: "written", command: full, stdout: run.stdout, stderr: run.stderr };
|
|
1757
|
+
}
|
|
1758
|
+
if (alreadyExists) {
|
|
1759
|
+
return { client: "claude-code", method: "cli", status: "already-wired", command: full, stdout: run.stdout, stderr: run.stderr };
|
|
1760
|
+
}
|
|
1761
|
+
return {
|
|
1762
|
+
client: "claude-code",
|
|
1763
|
+
method: "cli",
|
|
1764
|
+
status: "failed",
|
|
1765
|
+
command: full,
|
|
1766
|
+
stdout: run.stdout,
|
|
1767
|
+
stderr: run.stderr,
|
|
1768
|
+
reason: `claude mcp add exited ${String(run.status)}`
|
|
1769
|
+
};
|
|
1770
|
+
}
|
|
1771
|
+
function unwire() {
|
|
1772
|
+
const state = readWireState();
|
|
1773
|
+
const undone = [];
|
|
1774
|
+
if (!state || state.targets.length === 0) {
|
|
1775
|
+
return { state, undone };
|
|
1776
|
+
}
|
|
1777
|
+
for (const target of state.targets) {
|
|
1778
|
+
try {
|
|
1779
|
+
if (target.method === "file") {
|
|
1780
|
+
if (target.status === "written") {
|
|
1781
|
+
restoreFromBackup(target.path, target.backupPath);
|
|
1782
|
+
undone.push({ client: target.client, action: target.backupPath ? "restored" : "removed" });
|
|
1783
|
+
} else {
|
|
1784
|
+
undone.push({ client: target.client, action: "skipped", reason: `target status was ${target.status}` });
|
|
1785
|
+
}
|
|
1786
|
+
} else if (target.method === "cli") {
|
|
1787
|
+
if (target.status === "written") {
|
|
1788
|
+
const run = child_process.spawnSync("claude", ["mcp", "remove", "--scope", "user", "alter"], {
|
|
1789
|
+
encoding: "utf8",
|
|
1790
|
+
shell: process.platform === "win32",
|
|
1791
|
+
timeout: 1e4
|
|
1792
|
+
});
|
|
1793
|
+
if (run.error) {
|
|
1794
|
+
undone.push({ client: target.client, action: "failed", reason: run.error.message });
|
|
1795
|
+
} else if (run.status === 0) {
|
|
1796
|
+
undone.push({ client: target.client, action: "cli-removed" });
|
|
1797
|
+
} else {
|
|
1798
|
+
undone.push({ client: target.client, action: "failed", reason: `claude mcp remove exited ${String(run.status)}` });
|
|
1799
|
+
}
|
|
1800
|
+
} else {
|
|
1801
|
+
undone.push({ client: target.client, action: "skipped", reason: `target status was ${target.status}` });
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
} catch (err) {
|
|
1805
|
+
undone.push({ client: target.client, action: "failed", reason: err.message });
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
writeWireState({
|
|
1809
|
+
version: 1,
|
|
1810
|
+
sdkVersion: state.sdkVersion,
|
|
1811
|
+
writtenAt: ISO_NOW(),
|
|
1812
|
+
endpoint: state.endpoint,
|
|
1813
|
+
targets: []
|
|
1814
|
+
});
|
|
1815
|
+
return { state, undone };
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1096
1818
|
// src/types.ts
|
|
1819
|
+
init_cjs_shims();
|
|
1097
1820
|
var FREE_TOOL_NAMES = [
|
|
1098
1821
|
"hello_agent",
|
|
1099
1822
|
"alter_resolve_handle",
|
|
@@ -1248,10 +1971,27 @@ var TOOL_BLAST_RADIUS = {
|
|
|
1248
1971
|
query_graph_similarity: "high"
|
|
1249
1972
|
};
|
|
1250
1973
|
|
|
1251
|
-
// src/
|
|
1252
|
-
|
|
1253
|
-
var
|
|
1974
|
+
// src/homepage.ts
|
|
1975
|
+
init_cjs_shims();
|
|
1976
|
+
var HOMEPAGE_LIMITS = {
|
|
1977
|
+
whoami_max_chars: 240,
|
|
1978
|
+
opener_max_chars: 280,
|
|
1979
|
+
pronouns_max_chars: 32,
|
|
1980
|
+
attunement_glyph_max_chars: 16
|
|
1981
|
+
};
|
|
1982
|
+
|
|
1983
|
+
// src/themes.ts
|
|
1984
|
+
init_cjs_shims();
|
|
1985
|
+
var THEME_LIMITS = {
|
|
1986
|
+
meta_name_pattern: /^[a-z][a-z0-9-]{0,63}$/,
|
|
1987
|
+
meta_description_max_chars: 240,
|
|
1988
|
+
opener_library_max_entries: 32,
|
|
1989
|
+
opener_entry_max_chars: 240,
|
|
1990
|
+
share_note_max_chars: 280
|
|
1991
|
+
};
|
|
1992
|
+
var OSC8_ALLOWED_SCHEMES = ["https:", "mailto:"];
|
|
1254
1993
|
|
|
1994
|
+
exports.ALL_CLIENTS = ALL_CLIENTS;
|
|
1255
1995
|
exports.AlterAuthError = AlterAuthError;
|
|
1256
1996
|
exports.AlterClient = AlterClient;
|
|
1257
1997
|
exports.AlterDiscoveryError = AlterDiscoveryError;
|
|
@@ -1263,34 +2003,55 @@ exports.AlterProvenanceError = AlterProvenanceError;
|
|
|
1263
2003
|
exports.AlterRateLimited = AlterRateLimited;
|
|
1264
2004
|
exports.AlterTimeoutError = AlterTimeoutError;
|
|
1265
2005
|
exports.AlterToolError = AlterToolError;
|
|
2006
|
+
exports.CLAUDE_CODE = CLAUDE_CODE;
|
|
2007
|
+
exports.CLAUDE_DESKTOP = CLAUDE_DESKTOP;
|
|
2008
|
+
exports.CURSOR = CURSOR;
|
|
1266
2009
|
exports.DEFAULT_DOMAIN = DEFAULT_DOMAIN;
|
|
1267
2010
|
exports.DEFAULT_ENDPOINT = DEFAULT_ENDPOINT;
|
|
1268
2011
|
exports.DEFAULT_VERIFY_AT_ALLOWLIST = DEFAULT_VERIFY_AT_ALLOWLIST;
|
|
1269
2012
|
exports.FREE_TOOL_NAMES = FREE_TOOL_NAMES;
|
|
2013
|
+
exports.HOMEPAGE_LIMITS = HOMEPAGE_LIMITS;
|
|
1270
2014
|
exports.MCPClient = MCPClient;
|
|
1271
2015
|
exports.MCP_PROTOCOL_VERSION = MCP_PROTOCOL_VERSION;
|
|
2016
|
+
exports.OSC8_ALLOWED_SCHEMES = OSC8_ALLOWED_SCHEMES;
|
|
1272
2017
|
exports.PREMIUM_TOOL_NAMES = PREMIUM_TOOL_NAMES;
|
|
1273
2018
|
exports.SDK_NAME = SDK_NAME;
|
|
1274
2019
|
exports.SDK_VERSION = SDK_VERSION;
|
|
2020
|
+
exports.THEME_LIMITS = THEME_LIMITS;
|
|
1275
2021
|
exports.TOOL_BLAST_RADIUS = TOOL_BLAST_RADIUS;
|
|
1276
2022
|
exports.TOOL_COSTS = TOOL_COSTS;
|
|
1277
2023
|
exports.TOOL_TIERS = TOOL_TIERS;
|
|
2024
|
+
exports.VSCODE = VSCODE;
|
|
1278
2025
|
exports.X402Client = X402Client;
|
|
1279
2026
|
exports.base64urlDecode = base64urlDecode;
|
|
1280
|
-
exports.base64urlEncode =
|
|
2027
|
+
exports.base64urlEncode = base64urlEncode2;
|
|
2028
|
+
exports.canonicalArgsSha256 = canonicalArgsSha256;
|
|
2029
|
+
exports.canonicalStringify = canonicalStringify;
|
|
1281
2030
|
exports.clearDiscoveryCache = clearDiscoveryCache;
|
|
1282
2031
|
exports.decodeDid = decodeDid;
|
|
2032
|
+
exports.detectSyncedVolume = detectSyncedVolume;
|
|
1283
2033
|
exports.discover = discover;
|
|
1284
2034
|
exports.encodeDid = encodeDid;
|
|
1285
2035
|
exports.fetchPublicKeys = fetchPublicKeys;
|
|
1286
2036
|
exports.generateClaudeConfig = generateClaudeConfig;
|
|
2037
|
+
exports.generateClaudeDesktopConfig = generateClaudeDesktopConfig;
|
|
1287
2038
|
exports.generateCursorConfig = generateCursorConfig;
|
|
1288
2039
|
exports.generateGenericMcpConfig = generateGenericMcpConfig;
|
|
1289
2040
|
exports.generateKeypair = generateKeypair;
|
|
1290
2041
|
exports.keypairFromPrivateKey = keypairFromPrivateKey;
|
|
2042
|
+
exports.loadPrivateKey = loadPrivateKey;
|
|
1291
2043
|
exports.parsePaymentHeader = parsePaymentHeader;
|
|
2044
|
+
exports.probeAll = probeAll;
|
|
2045
|
+
exports.probeByDir = probeByDir;
|
|
2046
|
+
exports.probeClaudeCode = probeClaudeCode;
|
|
2047
|
+
exports.readWireState = readWireState;
|
|
1292
2048
|
exports.resolveVerifyAt = resolveVerifyAt;
|
|
2049
|
+
exports.sha256 = sha2562;
|
|
1293
2050
|
exports.sign = sign;
|
|
2051
|
+
exports.signInvocation = signInvocation;
|
|
2052
|
+
exports.unwire = unwire;
|
|
1294
2053
|
exports.verify = verify;
|
|
1295
2054
|
exports.verifyProvenance = verifyProvenance;
|
|
1296
2055
|
exports.verifyToolSignatures = verifyToolSignatures;
|
|
2056
|
+
exports.wire = wire;
|
|
2057
|
+
exports.writeWireState = writeWireState;
|