usemycontext 1.0.0 → 1.0.2
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 +148 -0
- package/dist/client-mXQsH2mZ.d.cts +217 -0
- package/dist/client-mXQsH2mZ.d.ts +217 -0
- package/dist/index.cjs +432 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +418 -0
- package/dist/index.js.map +1 -0
- package/dist/react.cjs +491 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +49 -0
- package/dist/react.d.ts +49 -0
- package/dist/react.js +468 -0
- package/dist/react.js.map +1 -0
- package/package.json +55 -16
package/dist/index.js
ADDED
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
// src/jsonrpc.ts
|
|
2
|
+
var UseMyContextError = class extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "UseMyContextError";
|
|
6
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
var NotConnectedError = class extends UseMyContextError {
|
|
10
|
+
constructor(message = "Not connected. Call connect({ token }) first.") {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = "NotConnectedError";
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
var TokenExpiredError = class extends UseMyContextError {
|
|
16
|
+
constructor(message = "The connection token was rejected (expired or revoked).", wwwAuthenticate) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = "TokenExpiredError";
|
|
19
|
+
this.wwwAuthenticate = wwwAuthenticate;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var ScopeDeniedError = class extends UseMyContextError {
|
|
23
|
+
constructor(message) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.code = -32604;
|
|
26
|
+
this.name = "ScopeDeniedError";
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
var ToolCallError = class extends UseMyContextError {
|
|
30
|
+
constructor(message, code) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = "ToolCallError";
|
|
33
|
+
this.code = code;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var TransportError = class extends UseMyContextError {
|
|
37
|
+
constructor(message) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.name = "TransportError";
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
var nextId = 1;
|
|
43
|
+
function initializeRequest() {
|
|
44
|
+
return {
|
|
45
|
+
jsonrpc: "2.0",
|
|
46
|
+
id: nextId++,
|
|
47
|
+
method: "initialize",
|
|
48
|
+
params: {
|
|
49
|
+
protocolVersion: "2025-06-18",
|
|
50
|
+
capabilities: {},
|
|
51
|
+
clientInfo: { name: "usemycontext-sdk", version: "0.1.0" }
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function toolCallRequest(name, args) {
|
|
56
|
+
return {
|
|
57
|
+
jsonrpc: "2.0",
|
|
58
|
+
id: nextId++,
|
|
59
|
+
method: "tools/call",
|
|
60
|
+
params: { name, arguments: args }
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function asJsonRpcResponse(body) {
|
|
64
|
+
if (body === null || typeof body !== "object") {
|
|
65
|
+
throw new TransportError("Membrane returned a non-object response body.");
|
|
66
|
+
}
|
|
67
|
+
const b = body;
|
|
68
|
+
if (b.jsonrpc !== "2.0") {
|
|
69
|
+
throw new TransportError("Membrane response was not JSON-RPC 2.0.");
|
|
70
|
+
}
|
|
71
|
+
return b;
|
|
72
|
+
}
|
|
73
|
+
function isScopeDenied(code) {
|
|
74
|
+
return code === -32604;
|
|
75
|
+
}
|
|
76
|
+
function resultText(result) {
|
|
77
|
+
for (const block of result.content ?? []) {
|
|
78
|
+
if (block.type === "text" && typeof block.text === "string") return block.text;
|
|
79
|
+
}
|
|
80
|
+
return "";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/machine.ts
|
|
84
|
+
var INITIAL_STATE = "idle";
|
|
85
|
+
function reduce(state, event) {
|
|
86
|
+
switch (event.type) {
|
|
87
|
+
case "CONNECT":
|
|
88
|
+
return state === "connecting" ? state : "connecting";
|
|
89
|
+
case "CONNECT_SUCCESS":
|
|
90
|
+
return state === "connecting" ? "connected" : state;
|
|
91
|
+
case "CONNECT_DECLINED":
|
|
92
|
+
return state === "connecting" ? "declined" : state;
|
|
93
|
+
case "ERROR":
|
|
94
|
+
return state === "connecting" ? "error" : state;
|
|
95
|
+
case "TOKEN_EXPIRED":
|
|
96
|
+
return state === "connecting" || state === "connected" ? "expired" : state;
|
|
97
|
+
case "DISCONNECT":
|
|
98
|
+
return state === "disconnected" ? state : "disconnected";
|
|
99
|
+
default: {
|
|
100
|
+
return state;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function isConnected(state) {
|
|
105
|
+
return state === "connected";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/storage.ts
|
|
109
|
+
var DEFAULT_STORAGE_KEY = "usemycontext.token";
|
|
110
|
+
function memoryStorage() {
|
|
111
|
+
const map = /* @__PURE__ */ new Map();
|
|
112
|
+
return {
|
|
113
|
+
get: (k) => map.has(k) ? map.get(k) : null,
|
|
114
|
+
set: (k, v) => void map.set(k, v),
|
|
115
|
+
remove: (k) => void map.delete(k)
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function webStorage(store) {
|
|
119
|
+
return {
|
|
120
|
+
get: (k) => {
|
|
121
|
+
try {
|
|
122
|
+
return store.getItem(k);
|
|
123
|
+
} catch {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
set: (k, v) => {
|
|
128
|
+
try {
|
|
129
|
+
store.setItem(k, v);
|
|
130
|
+
} catch {
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
remove: (k) => {
|
|
134
|
+
try {
|
|
135
|
+
store.removeItem(k);
|
|
136
|
+
} catch {
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function resolveStorage(opt) {
|
|
142
|
+
if (opt === null) {
|
|
143
|
+
return { get: () => null, set: () => {
|
|
144
|
+
}, remove: () => {
|
|
145
|
+
} };
|
|
146
|
+
}
|
|
147
|
+
if (opt) return opt;
|
|
148
|
+
return memoryStorage();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// src/transport.ts
|
|
152
|
+
var fetchTransport = async ({ url, token, body }) => {
|
|
153
|
+
const res = await fetch(url, {
|
|
154
|
+
method: "POST",
|
|
155
|
+
headers: {
|
|
156
|
+
"content-type": "application/json",
|
|
157
|
+
accept: "application/json",
|
|
158
|
+
authorization: `Bearer ${token}`
|
|
159
|
+
},
|
|
160
|
+
body: JSON.stringify(body)
|
|
161
|
+
});
|
|
162
|
+
let parsed = null;
|
|
163
|
+
try {
|
|
164
|
+
parsed = await res.json();
|
|
165
|
+
} catch {
|
|
166
|
+
parsed = null;
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
status: res.status,
|
|
170
|
+
body: parsed,
|
|
171
|
+
wwwAuthenticate: res.headers.get("www-authenticate")
|
|
172
|
+
};
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// src/client.ts
|
|
176
|
+
var DEFAULT_ENDPOINT = "https://mcp.usemycontext.ai/mcp";
|
|
177
|
+
function useMyContext(options = {}) {
|
|
178
|
+
const endpoint = options.endpoint ?? DEFAULT_ENDPOINT;
|
|
179
|
+
const transport = options.transport ?? fetchTransport;
|
|
180
|
+
const storage = resolveStorage(options.storage);
|
|
181
|
+
const storageKey = options.storageKey ?? DEFAULT_STORAGE_KEY;
|
|
182
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
183
|
+
let state = INITIAL_STATE;
|
|
184
|
+
let token = null;
|
|
185
|
+
const persisted = storage.get(storageKey);
|
|
186
|
+
if (persisted) {
|
|
187
|
+
token = persisted;
|
|
188
|
+
state = "connected";
|
|
189
|
+
}
|
|
190
|
+
function setState(next) {
|
|
191
|
+
if (next === state) return;
|
|
192
|
+
state = next;
|
|
193
|
+
options.onStateChange?.(state);
|
|
194
|
+
for (const l of listeners) l(state);
|
|
195
|
+
}
|
|
196
|
+
function dispatch(event) {
|
|
197
|
+
setState(reduce(state, event));
|
|
198
|
+
}
|
|
199
|
+
async function rpc(body) {
|
|
200
|
+
if (!token) throw new NotConnectedError();
|
|
201
|
+
let res;
|
|
202
|
+
try {
|
|
203
|
+
res = await transport({ url: endpoint, token, body });
|
|
204
|
+
} catch (e) {
|
|
205
|
+
throw new TransportError(
|
|
206
|
+
`Transport failed: ${e instanceof Error ? e.message : String(e)}`
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
if (res.status === 401) {
|
|
210
|
+
dispatch({ type: "TOKEN_EXPIRED" });
|
|
211
|
+
throw new TokenExpiredError(void 0, res.wwwAuthenticate ?? void 0);
|
|
212
|
+
}
|
|
213
|
+
const parsed = asJsonRpcResponse(res.body);
|
|
214
|
+
if (parsed.error) {
|
|
215
|
+
if (isScopeDenied(parsed.error.code)) {
|
|
216
|
+
throw new ScopeDeniedError(parsed.error.message);
|
|
217
|
+
}
|
|
218
|
+
throw new ToolCallError(parsed.error.message, parsed.error.code);
|
|
219
|
+
}
|
|
220
|
+
return parsed.result;
|
|
221
|
+
}
|
|
222
|
+
async function callTool(name, args) {
|
|
223
|
+
if (!isConnected(state)) throw new NotConnectedError();
|
|
224
|
+
const result = await rpc(toolCallRequest(name, args));
|
|
225
|
+
if (result.isError) {
|
|
226
|
+
throw new ToolCallError(resultText(result) || `${name} failed.`);
|
|
227
|
+
}
|
|
228
|
+
return resultText(result);
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
get state() {
|
|
232
|
+
return state;
|
|
233
|
+
},
|
|
234
|
+
get connected() {
|
|
235
|
+
return state === "connected";
|
|
236
|
+
},
|
|
237
|
+
get token() {
|
|
238
|
+
return state === "connected" ? token : null;
|
|
239
|
+
},
|
|
240
|
+
subscribe(listener) {
|
|
241
|
+
listeners.add(listener);
|
|
242
|
+
return () => void listeners.delete(listener);
|
|
243
|
+
},
|
|
244
|
+
async connect({ token: t }) {
|
|
245
|
+
if (typeof t !== "string" || t.length === 0) {
|
|
246
|
+
throw new UseMyContextConnectArgError();
|
|
247
|
+
}
|
|
248
|
+
dispatch({ type: "CONNECT" });
|
|
249
|
+
token = t;
|
|
250
|
+
try {
|
|
251
|
+
const res = await transport({ url: endpoint, token: t, body: initializeRequest() });
|
|
252
|
+
if (res.status === 401) {
|
|
253
|
+
token = null;
|
|
254
|
+
dispatch({ type: "TOKEN_EXPIRED" });
|
|
255
|
+
throw new TokenExpiredError(void 0, res.wwwAuthenticate ?? void 0);
|
|
256
|
+
}
|
|
257
|
+
const parsed = asJsonRpcResponse(res.body);
|
|
258
|
+
if (parsed.error) {
|
|
259
|
+
token = null;
|
|
260
|
+
dispatch({ type: "ERROR" });
|
|
261
|
+
throw new ToolCallError(parsed.error.message, parsed.error.code);
|
|
262
|
+
}
|
|
263
|
+
dispatch({ type: "CONNECT_SUCCESS" });
|
|
264
|
+
if (storage) storage.set(storageKey, t);
|
|
265
|
+
return state;
|
|
266
|
+
} catch (e) {
|
|
267
|
+
token = null;
|
|
268
|
+
if (!(e instanceof TokenExpiredError)) {
|
|
269
|
+
dispatch({ type: "ERROR" });
|
|
270
|
+
if (e instanceof TransportError || e instanceof ToolCallError) throw e;
|
|
271
|
+
throw new TransportError(
|
|
272
|
+
`Connect failed: ${e instanceof Error ? e.message : String(e)}`
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
throw e;
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
disconnect() {
|
|
279
|
+
token = null;
|
|
280
|
+
storage.remove(storageKey);
|
|
281
|
+
dispatch({ type: "DISCONNECT" });
|
|
282
|
+
},
|
|
283
|
+
// --- profile ---------------------------------------------------------
|
|
284
|
+
async profile() {
|
|
285
|
+
const text = await callTool("profile", {});
|
|
286
|
+
const facts = tryParseFacts(text);
|
|
287
|
+
if (facts) return { composite: text, facts };
|
|
288
|
+
return { composite: text };
|
|
289
|
+
},
|
|
290
|
+
// --- list_files / search_files (JSON array of FileMeta) --------------
|
|
291
|
+
async listFiles() {
|
|
292
|
+
return parseFileList(await callTool("list_files", {}));
|
|
293
|
+
},
|
|
294
|
+
async searchFiles(query) {
|
|
295
|
+
if (typeof query !== "string") throw new UseMyContextConnectArgError("search query must be a string");
|
|
296
|
+
return parseFileList(await callTool("search_files", { query }));
|
|
297
|
+
},
|
|
298
|
+
// --- get_file --------------------------------------------------------
|
|
299
|
+
async getFile(fileId) {
|
|
300
|
+
if (typeof fileId !== "string" || !fileId) {
|
|
301
|
+
throw new UseMyContextConnectArgError("fileId must be a non-empty string");
|
|
302
|
+
}
|
|
303
|
+
const text = await callTool("get_file", { fileId });
|
|
304
|
+
return { text, downloadUrl: parseDownloadUrl(text) };
|
|
305
|
+
},
|
|
306
|
+
// --- ask_docs --------------------------------------------------------
|
|
307
|
+
async ask(query, opts = {}) {
|
|
308
|
+
if (typeof query !== "string" || !query) {
|
|
309
|
+
throw new UseMyContextConnectArgError("ask query must be a non-empty string");
|
|
310
|
+
}
|
|
311
|
+
const args = { query };
|
|
312
|
+
if (typeof opts.k === "number") args.k = opts.k;
|
|
313
|
+
if (typeof opts.projectId === "string") args.projectId = opts.projectId;
|
|
314
|
+
const text = await callTool("ask_docs", args);
|
|
315
|
+
return { passages: parseAskPassages(text), text };
|
|
316
|
+
},
|
|
317
|
+
// --- suggest_update (the one write) ----------------------------------
|
|
318
|
+
async suggestUpdate(suggestion) {
|
|
319
|
+
if (typeof suggestion !== "string" || !suggestion.trim()) {
|
|
320
|
+
throw new UseMyContextConnectArgError("suggestion must be a non-empty string");
|
|
321
|
+
}
|
|
322
|
+
const text = await callTool("suggest_update", { suggestion });
|
|
323
|
+
const m = text.match(/\(id:\s*([^)]+)\)/);
|
|
324
|
+
return { id: m ? m[1].trim() : void 0, text };
|
|
325
|
+
},
|
|
326
|
+
// --- shared_context --------------------------------------------------
|
|
327
|
+
async sharedContext(from) {
|
|
328
|
+
const args = {};
|
|
329
|
+
if (typeof from === "string" && from) args.from = from;
|
|
330
|
+
const text = await callTool("shared_context", args);
|
|
331
|
+
return from ? parseSharedRead(text) : parseSharedList(text);
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
var UseMyContextConnectArgError = class extends UseMyContextError {
|
|
336
|
+
constructor(message = "connect() requires a non-empty token string.") {
|
|
337
|
+
super(message);
|
|
338
|
+
this.name = "UseMyContextConnectArgError";
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
function tryParseFacts(text) {
|
|
342
|
+
const trimmed = text.trimStart();
|
|
343
|
+
if (!trimmed.startsWith("{")) return void 0;
|
|
344
|
+
try {
|
|
345
|
+
const obj = JSON.parse(text);
|
|
346
|
+
if (Array.isArray(obj.facts)) return obj.facts.filter((f) => typeof f === "string");
|
|
347
|
+
} catch {
|
|
348
|
+
}
|
|
349
|
+
return void 0;
|
|
350
|
+
}
|
|
351
|
+
function parseFileList(text) {
|
|
352
|
+
try {
|
|
353
|
+
const arr = JSON.parse(text);
|
|
354
|
+
if (!Array.isArray(arr)) return [];
|
|
355
|
+
return arr.map((r) => {
|
|
356
|
+
const o = r ?? {};
|
|
357
|
+
return {
|
|
358
|
+
id: String(o.id ?? ""),
|
|
359
|
+
name: String(o.name ?? ""),
|
|
360
|
+
size: typeof o.size === "number" ? o.size : 0,
|
|
361
|
+
contentType: String(o.contentType ?? ""),
|
|
362
|
+
modified: String(o.modified ?? ""),
|
|
363
|
+
...typeof o.status === "string" ? { status: o.status } : {}
|
|
364
|
+
};
|
|
365
|
+
});
|
|
366
|
+
} catch {
|
|
367
|
+
return [];
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
function parseDownloadUrl(text) {
|
|
371
|
+
const m = text.match(/Download link[^:]*:\s*(\S+)/);
|
|
372
|
+
return m ? m[1] : void 0;
|
|
373
|
+
}
|
|
374
|
+
function parseAskPassages(text) {
|
|
375
|
+
if (/^No relevant passages/i.test(text.trimStart())) return [];
|
|
376
|
+
const blocks = text.split("\n\n");
|
|
377
|
+
const out = [];
|
|
378
|
+
for (const block of blocks) {
|
|
379
|
+
const nl = block.indexOf("\n");
|
|
380
|
+
const citation = nl === -1 ? block : block.slice(0, nl);
|
|
381
|
+
const body = nl === -1 ? "" : block.slice(nl + 1);
|
|
382
|
+
const m = citation.match(/^\[(\d+)\]\s*(.*)$/);
|
|
383
|
+
if (!m) continue;
|
|
384
|
+
const index = Number(m[1]);
|
|
385
|
+
const rest = m[2];
|
|
386
|
+
const parts = rest.split(" \xB7 ");
|
|
387
|
+
const passage = { index, text: body };
|
|
388
|
+
if (parts[0]) passage.file = parts[0].trim();
|
|
389
|
+
if (parts[1]) passage.scope = parts[1].trim();
|
|
390
|
+
const scoreM = rest.match(/score\s+([\d.]+)/);
|
|
391
|
+
if (scoreM) passage.score = Number(scoreM[1]);
|
|
392
|
+
out.push(passage);
|
|
393
|
+
}
|
|
394
|
+
return out;
|
|
395
|
+
}
|
|
396
|
+
function parseSharedList(text) {
|
|
397
|
+
if (/^No one has shared/i.test(text.trimStart())) {
|
|
398
|
+
return { mode: "list", shares: [], composite: null, text };
|
|
399
|
+
}
|
|
400
|
+
const shares = [];
|
|
401
|
+
for (const line of text.split("\n")) {
|
|
402
|
+
const m = line.match(/^-\s*(.*?)\s*\(from:\s*([^)]+)\)\s*$/);
|
|
403
|
+
if (m) shares.push({ ownerLabel: m[1].trim(), grantId: m[2].trim() });
|
|
404
|
+
}
|
|
405
|
+
return { mode: "list", shares, composite: null, text };
|
|
406
|
+
}
|
|
407
|
+
function parseSharedRead(text) {
|
|
408
|
+
if (/not available/i.test(text)) {
|
|
409
|
+
return { mode: "read", shares: [], composite: null, text };
|
|
410
|
+
}
|
|
411
|
+
const m = text.match(/^Shared context from [^:]*:\n\n([\s\S]*)$/);
|
|
412
|
+
const composite = m ? m[1] : text;
|
|
413
|
+
return { mode: "read", shares: [], composite, text };
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
export { DEFAULT_ENDPOINT, DEFAULT_STORAGE_KEY, NotConnectedError, ScopeDeniedError, TokenExpiredError, ToolCallError, TransportError, UseMyContextConnectArgError, UseMyContextError, fetchTransport, memoryStorage, useMyContext, webStorage };
|
|
417
|
+
//# sourceMappingURL=index.js.map
|
|
418
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/jsonrpc.ts","../src/machine.ts","../src/storage.ts","../src/transport.ts","../src/client.ts"],"names":[],"mappings":";AAsCO,IAAM,iBAAA,GAAN,cAAgC,KAAA,CAAM;AAAA,EAC3C,YAAY,OAAA,EAAiB;AAC3B,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,mBAAA;AAEZ,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,GAAA,CAAA,MAAA,CAAW,SAAS,CAAA;AAAA,EAClD;AACF;AAGO,IAAM,iBAAA,GAAN,cAAgC,iBAAA,CAAkB;AAAA,EACvD,WAAA,CAAY,UAAU,+CAAA,EAAiD;AACrE,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,mBAAA;AAAA,EACd;AACF;AAMO,IAAM,iBAAA,GAAN,cAAgC,iBAAA,CAAkB;AAAA,EAGvD,WAAA,CAAY,OAAA,GAAU,yDAAA,EAA2D,eAAA,EAA0B;AACzG,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,mBAAA;AACZ,IAAA,IAAA,CAAK,eAAA,GAAkB,eAAA;AAAA,EACzB;AACF;AAGO,IAAM,gBAAA,GAAN,cAA+B,iBAAA,CAAkB;AAAA,EAEtD,YAAY,OAAA,EAAiB;AAC3B,IAAA,KAAA,CAAM,OAAO,CAAA;AAFf,IAAA,IAAA,CAAS,IAAA,GAAO,MAAA;AAGd,IAAA,IAAA,CAAK,IAAA,GAAO,kBAAA;AAAA,EACd;AACF;AAGO,IAAM,aAAA,GAAN,cAA4B,iBAAA,CAAkB;AAAA,EAGnD,WAAA,CAAY,SAAiB,IAAA,EAAe;AAC1C,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,eAAA;AACZ,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AAAA,EACd;AACF;AAGO,IAAM,cAAA,GAAN,cAA6B,iBAAA,CAAkB;AAAA,EACpD,YAAY,OAAA,EAAiB;AAC3B,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,gBAAA;AAAA,EACd;AACF;AAEA,IAAI,MAAA,GAAS,CAAA;AAGN,SAAS,iBAAA,GAAoC;AAClD,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,KAAA;AAAA,IACT,EAAA,EAAI,MAAA,EAAA;AAAA,IACJ,MAAA,EAAQ,YAAA;AAAA,IACR,MAAA,EAAQ;AAAA,MACN,eAAA,EAAiB,YAAA;AAAA,MACjB,cAAc,EAAC;AAAA,MACf,UAAA,EAAY,EAAE,IAAA,EAAM,kBAAA,EAAoB,SAAS,OAAA;AAAQ;AAC3D,GACF;AACF;AAGO,SAAS,eAAA,CAAgB,MAAc,IAAA,EAA+C;AAC3F,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,KAAA;AAAA,IACT,EAAA,EAAI,MAAA,EAAA;AAAA,IACJ,MAAA,EAAQ,YAAA;AAAA,IACR,MAAA,EAAQ,EAAE,IAAA,EAAM,SAAA,EAAW,IAAA;AAAK,GAClC;AACF;AAOO,SAAS,kBAAkB,IAAA,EAAgC;AAChE,EAAA,IAAI,IAAA,KAAS,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,EAAU;AAC7C,IAAA,MAAM,IAAI,eAAe,+CAA+C,CAAA;AAAA,EAC1E;AACA,EAAA,MAAM,CAAA,GAAI,IAAA;AACV,EAAA,IAAI,CAAA,CAAE,YAAY,KAAA,EAAO;AACvB,IAAA,MAAM,IAAI,eAAe,yCAAyC,CAAA;AAAA,EACpE;AACA,EAAA,OAAO,CAAA;AACT;AAGO,SAAS,cAAc,IAAA,EAAuB;AACnD,EAAA,OAAO,IAAA,KAAS,MAAA;AAClB;AAOO,SAAS,WAAW,MAAA,EAA4B;AACrD,EAAA,KAAA,MAAW,KAAA,IAAS,MAAA,CAAO,OAAA,IAAW,EAAC,EAAG;AACxC,IAAA,IAAI,KAAA,CAAM,SAAS,MAAA,IAAU,OAAO,MAAM,IAAA,KAAS,QAAA,SAAiB,KAAA,CAAM,IAAA;AAAA,EAC5E;AACA,EAAA,OAAO,EAAA;AACT;;;AC9HO,IAAM,aAAA,GAA8B,MAAA;AAMpC,SAAS,MAAA,CAAO,OAAqB,KAAA,EAAmC;AAC7E,EAAA,QAAQ,MAAM,IAAA;AAAM,IAClB,KAAK,SAAA;AAKH,MAAA,OAAO,KAAA,KAAU,eAAe,KAAA,GAAQ,YAAA;AAAA,IAE1C,KAAK,iBAAA;AAGH,MAAA,OAAO,KAAA,KAAU,eAAe,WAAA,GAAc,KAAA;AAAA,IAEhD,KAAK,kBAAA;AAEH,MAAA,OAAO,KAAA,KAAU,eAAe,UAAA,GAAa,KAAA;AAAA,IAE/C,KAAK,OAAA;AAIH,MAAA,OAAO,KAAA,KAAU,eAAe,OAAA,GAAU,KAAA;AAAA,IAE5C,KAAK,eAAA;AAGH,MAAA,OAAO,KAAA,KAAU,YAAA,IAAgB,KAAA,KAAU,WAAA,GAAc,SAAA,GAAY,KAAA;AAAA,IAEvE,KAAK,YAAA;AAGH,MAAA,OAAO,KAAA,KAAU,iBAAiB,KAAA,GAAQ,cAAA;AAAA,IAE5C,SAAS;AAGP,MAAA,OAAO,KAAA;AAAA,IACT;AAAA;AAEJ;AAcO,SAAS,YAAY,KAAA,EAA8B;AACxD,EAAA,OAAO,KAAA,KAAU,WAAA;AACnB;;;AC9EO,IAAM,mBAAA,GAAsB;AAG5B,SAAS,aAAA,GAAgC;AAC9C,EAAA,MAAM,GAAA,uBAAU,GAAA,EAAoB;AACpC,EAAA,OAAO;AAAA,IACL,GAAA,EAAK,CAAC,CAAA,KAAO,GAAA,CAAI,GAAA,CAAI,CAAC,CAAA,GAAK,GAAA,CAAI,GAAA,CAAI,CAAC,CAAA,GAAe,IAAA;AAAA,IACnD,GAAA,EAAK,CAAC,CAAA,EAAG,CAAA,KAAM,KAAK,GAAA,CAAI,GAAA,CAAI,GAAG,CAAC,CAAA;AAAA,IAChC,QAAQ,CAAC,CAAA,KAAM,KAAK,GAAA,CAAI,OAAO,CAAC;AAAA,GAClC;AACF;AAGO,SAAS,WAAW,KAAA,EAAgC;AACzD,EAAA,OAAO;AAAA,IACL,GAAA,EAAK,CAAC,CAAA,KAAM;AACV,MAAA,IAAI;AACF,QAAA,OAAO,KAAA,CAAM,QAAQ,CAAC,CAAA;AAAA,MACxB,CAAA,CAAA,MAAQ;AACN,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,IACF,CAAA;AAAA,IACA,GAAA,EAAK,CAAC,CAAA,EAAG,CAAA,KAAM;AACb,MAAA,IAAI;AACF,QAAA,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAC,CAAA;AAAA,MACpB,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF,CAAA;AAAA,IACA,MAAA,EAAQ,CAAC,CAAA,KAAM;AACb,MAAA,IAAI;AACF,QAAA,KAAA,CAAM,WAAW,CAAC,CAAA;AAAA,MACpB,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AAAA,GACF;AACF;AAUO,SAAS,eAAe,GAAA,EAAwD;AACrF,EAAA,IAAI,QAAQ,IAAA,EAAM;AAChB,IAAA,OAAO,EAAE,GAAA,EAAK,MAAM,IAAA,EAAM,KAAK,MAAM;AAAA,IAAC,CAAA,EAAG,QAAQ,MAAM;AAAA,IAAC,CAAA,EAAE;AAAA,EAC5D;AACA,EAAA,IAAI,KAAK,OAAO,GAAA;AAGhB,EAAA,OAAO,aAAA,EAAc;AACvB;;;AC3DO,IAAM,iBAA4B,OAAO,EAAE,GAAA,EAAK,KAAA,EAAO,MAAK,KAAkC;AACnG,EAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,IAC3B,MAAA,EAAQ,MAAA;AAAA,IACR,OAAA,EAAS;AAAA,MACP,cAAA,EAAgB,kBAAA;AAAA,MAChB,MAAA,EAAQ,kBAAA;AAAA,MACR,aAAA,EAAe,UAAU,KAAK,CAAA;AAAA,KAChC;AAAA,IACA,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,IAAI;AAAA,GAC1B,CAAA;AACD,EAAA,IAAI,MAAA,GAAkB,IAAA;AACtB,EAAA,IAAI;AACF,IAAA,MAAA,GAAS,MAAM,IAAI,IAAA,EAAK;AAAA,EAC1B,CAAA,CAAA,MAAQ;AACN,IAAA,MAAA,GAAS,IAAA;AAAA,EACX;AACA,EAAA,OAAO;AAAA,IACL,QAAQ,GAAA,CAAI,MAAA;AAAA,IACZ,IAAA,EAAM,MAAA;AAAA,IACN,eAAA,EAAiB,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,kBAAkB;AAAA,GACrD;AACF;;;ACeO,IAAM,gBAAA,GAAmB;AAkCzB,SAAS,YAAA,CAAa,OAAA,GAAyB,EAAC,EAAuB;AAC5E,EAAA,MAAM,QAAA,GAAW,QAAQ,QAAA,IAAY,gBAAA;AACrC,EAAA,MAAM,SAAA,GAAuB,QAAQ,SAAA,IAAa,cAAA;AAClD,EAAA,MAAM,OAAA,GAA0B,cAAA,CAAe,OAAA,CAAQ,OAAO,CAAA;AAC9D,EAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,IAAc,mBAAA;AAEzC,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAAmC;AACzD,EAAA,IAAI,KAAA,GAAsB,aAAA;AAC1B,EAAA,IAAI,KAAA,GAAuB,IAAA;AAI3B,EAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,GAAA,CAAI,UAAU,CAAA;AACxC,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,KAAA,GAAQ,SAAA;AACR,IAAA,KAAA,GAAQ,WAAA;AAAA,EACV;AAEA,EAAA,SAAS,SAAS,IAAA,EAA0B;AAC1C,IAAA,IAAI,SAAS,KAAA,EAAO;AACpB,IAAA,KAAA,GAAQ,IAAA;AACR,IAAA,OAAA,CAAQ,gBAAgB,KAAK,CAAA;AAC7B,IAAA,KAAA,MAAW,CAAA,IAAK,SAAA,EAAW,CAAA,CAAE,KAAK,CAAA;AAAA,EACpC;AAEA,EAAA,SAAS,SAAS,KAAA,EAA2C;AAC3D,IAAA,QAAA,CAAS,MAAA,CAAO,KAAA,EAAO,KAAK,CAAC,CAAA;AAAA,EAC/B;AAGA,EAAA,eAAe,IAAI,IAAA,EAAiC;AAClD,IAAA,IAAI,CAAC,KAAA,EAAO,MAAM,IAAI,iBAAA,EAAkB;AACxC,IAAA,IAAI,GAAA;AACJ,IAAA,IAAI;AACF,MAAA,GAAA,GAAM,MAAM,SAAA,CAAU,EAAE,KAAK,QAAA,EAAU,KAAA,EAAO,MAAM,CAAA;AAAA,IACtD,SAAS,CAAA,EAAG;AACV,MAAA,MAAM,IAAI,cAAA;AAAA,QACR,qBAAqB,CAAA,YAAa,KAAA,GAAQ,EAAE,OAAA,GAAU,MAAA,CAAO,CAAC,CAAC,CAAA;AAAA,OACjE;AAAA,IACF;AAGA,IAAA,IAAI,GAAA,CAAI,WAAW,GAAA,EAAK;AACtB,MAAA,QAAA,CAAS,EAAE,IAAA,EAAM,eAAA,EAAiB,CAAA;AAClC,MAAA,MAAM,IAAI,iBAAA,CAAkB,MAAA,EAAW,GAAA,CAAI,mBAAmB,MAAS,CAAA;AAAA,IACzE;AACA,IAAA,MAAM,MAAA,GAAS,iBAAA,CAAkB,GAAA,CAAI,IAAI,CAAA;AACzC,IAAA,IAAI,OAAO,KAAA,EAAO;AAChB,MAAA,IAAI,aAAA,CAAc,MAAA,CAAO,KAAA,CAAM,IAAI,CAAA,EAAG;AACpC,QAAA,MAAM,IAAI,gBAAA,CAAiB,MAAA,CAAO,KAAA,CAAM,OAAO,CAAA;AAAA,MACjD;AACA,MAAA,MAAM,IAAI,aAAA,CAAc,MAAA,CAAO,MAAM,OAAA,EAAS,MAAA,CAAO,MAAM,IAAI,CAAA;AAAA,IACjE;AACA,IAAA,OAAO,MAAA,CAAO,MAAA;AAAA,EAChB;AAGA,EAAA,eAAe,QAAA,CAAS,MAAc,IAAA,EAAgD;AACpF,IAAA,IAAI,CAAC,WAAA,CAAY,KAAK,CAAA,EAAG,MAAM,IAAI,iBAAA,EAAkB;AACrD,IAAA,MAAM,SAAU,MAAM,GAAA,CAAI,eAAA,CAAgB,IAAA,EAAM,IAAI,CAAC,CAAA;AACrD,IAAA,IAAI,OAAO,OAAA,EAAS;AAClB,MAAA,MAAM,IAAI,aAAA,CAAc,UAAA,CAAW,MAAM,CAAA,IAAK,CAAA,EAAG,IAAI,CAAA,QAAA,CAAU,CAAA;AAAA,IACjE;AACA,IAAA,OAAO,WAAW,MAAM,CAAA;AAAA,EAC1B;AAEA,EAAA,OAAO;AAAA,IACL,IAAI,KAAA,GAAQ;AACV,MAAA,OAAO,KAAA;AAAA,IACT,CAAA;AAAA,IACA,IAAI,SAAA,GAAY;AACd,MAAA,OAAO,KAAA,KAAU,WAAA;AAAA,IACnB,CAAA;AAAA,IACA,IAAI,KAAA,GAAQ;AACV,MAAA,OAAO,KAAA,KAAU,cAAc,KAAA,GAAQ,IAAA;AAAA,IACzC,CAAA;AAAA,IAEA,UAAU,QAAA,EAAU;AAClB,MAAA,SAAA,CAAU,IAAI,QAAQ,CAAA;AACtB,MAAA,OAAO,MAAM,KAAK,SAAA,CAAU,MAAA,CAAO,QAAQ,CAAA;AAAA,IAC7C,CAAA;AAAA,IAEA,MAAM,OAAA,CAAQ,EAAE,KAAA,EAAO,GAAE,EAAuC;AAC9D,MAAA,IAAI,OAAO,CAAA,KAAM,QAAA,IAAY,CAAA,CAAE,WAAW,CAAA,EAAG;AAE3C,QAAA,MAAM,IAAI,2BAAA,EAA4B;AAAA,MACxC;AACA,MAAA,QAAA,CAAS,EAAE,IAAA,EAAM,SAAA,EAAW,CAAA;AAC5B,MAAA,KAAA,GAAQ,CAAA;AACR,MAAA,IAAI;AACF,QAAA,MAAM,GAAA,GAAM,MAAM,SAAA,CAAU,EAAE,GAAA,EAAK,QAAA,EAAU,KAAA,EAAO,CAAA,EAAG,IAAA,EAAM,iBAAA,EAAkB,EAAG,CAAA;AAClF,QAAA,IAAI,GAAA,CAAI,WAAW,GAAA,EAAK;AACtB,UAAA,KAAA,GAAQ,IAAA;AACR,UAAA,QAAA,CAAS,EAAE,IAAA,EAAM,eAAA,EAAiB,CAAA;AAClC,UAAA,MAAM,IAAI,iBAAA,CAAkB,KAAA,CAAA,EAAW,GAAA,CAAI,mBAAmB,KAAA,CAAS,CAAA;AAAA,QACzE;AAEA,QAAA,MAAM,MAAA,GAAS,iBAAA,CAAkB,GAAA,CAAI,IAAI,CAAA;AACzC,QAAA,IAAI,OAAO,KAAA,EAAO;AAChB,UAAA,KAAA,GAAQ,IAAA;AACR,UAAA,QAAA,CAAS,EAAE,IAAA,EAAM,OAAA,EAAS,CAAA;AAC1B,UAAA,MAAM,IAAI,aAAA,CAAc,MAAA,CAAO,MAAM,OAAA,EAAS,MAAA,CAAO,MAAM,IAAI,CAAA;AAAA,QACjE;AACA,QAAA,QAAA,CAAS,EAAE,IAAA,EAAM,iBAAA,EAAmB,CAAA;AACpC,QAAA,IAAI,OAAA,EAAS,OAAA,CAAQ,GAAA,CAAI,UAAA,EAAY,CAAC,CAAA;AACtC,QAAA,OAAO,KAAA;AAAA,MACT,SAAS,CAAA,EAAG;AACV,QAAA,KAAA,GAAQ,IAAA;AAGR,QAAA,IAAI,EAAE,aAAa,iBAAA,CAAA,EAAoB;AACrC,UAAA,QAAA,CAAS,EAAE,IAAA,EAAM,OAAA,EAAS,CAAA;AAC1B,UAAA,IAAI,CAAA,YAAa,cAAA,IAAkB,CAAA,YAAa,aAAA,EAAe,MAAM,CAAA;AACrE,UAAA,MAAM,IAAI,cAAA;AAAA,YACR,mBAAmB,CAAA,YAAa,KAAA,GAAQ,EAAE,OAAA,GAAU,MAAA,CAAO,CAAC,CAAC,CAAA;AAAA,WAC/D;AAAA,QACF;AACA,QAAA,MAAM,CAAA;AAAA,MACR;AAAA,IACF,CAAA;AAAA,IAEA,UAAA,GAAmB;AACjB,MAAA,KAAA,GAAQ,IAAA;AACR,MAAA,OAAA,CAAQ,OAAO,UAAU,CAAA;AACzB,MAAA,QAAA,CAAS,EAAE,IAAA,EAAM,YAAA,EAAc,CAAA;AAAA,IACjC,CAAA;AAAA;AAAA,IAGA,MAAM,OAAA,GAAkC;AACtC,MAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,SAAA,EAAW,EAAE,CAAA;AAGzC,MAAA,MAAM,KAAA,GAAQ,cAAc,IAAI,CAAA;AAChC,MAAA,IAAI,KAAA,EAAO,OAAO,EAAE,SAAA,EAAW,MAAM,KAAA,EAAM;AAC3C,MAAA,OAAO,EAAE,WAAW,IAAA,EAAK;AAAA,IAC3B,CAAA;AAAA;AAAA,IAGA,MAAM,SAAA,GAAiC;AACrC,MAAA,OAAO,cAAc,MAAM,QAAA,CAAS,YAAA,EAAc,EAAE,CAAC,CAAA;AAAA,IACvD,CAAA;AAAA,IACA,MAAM,YAAY,KAAA,EAAoC;AACpD,MAAA,IAAI,OAAO,KAAA,KAAU,QAAA,EAAU,MAAM,IAAI,4BAA4B,+BAA+B,CAAA;AACpG,MAAA,OAAO,cAAc,MAAM,QAAA,CAAS,gBAAgB,EAAE,KAAA,EAAO,CAAC,CAAA;AAAA,IAChE,CAAA;AAAA;AAAA,IAGA,MAAM,QAAQ,MAAA,EAAwC;AACpD,MAAA,IAAI,OAAO,MAAA,KAAW,QAAA,IAAY,CAAC,MAAA,EAAQ;AACzC,QAAA,MAAM,IAAI,4BAA4B,mCAAmC,CAAA;AAAA,MAC3E;AACA,MAAA,MAAM,OAAO,MAAM,QAAA,CAAS,UAAA,EAAY,EAAE,QAAQ,CAAA;AAClD,MAAA,OAAO,EAAE,IAAA,EAAM,WAAA,EAAa,gBAAA,CAAiB,IAAI,CAAA,EAAE;AAAA,IACrD,CAAA;AAAA;AAAA,IAGA,MAAM,GAAA,CAAI,KAAA,EAAe,IAAA,GAAmB,EAAC,EAAuB;AAClE,MAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,CAAC,KAAA,EAAO;AACvC,QAAA,MAAM,IAAI,4BAA4B,sCAAsC,CAAA;AAAA,MAC9E;AACA,MAAA,MAAM,IAAA,GAAgC,EAAE,KAAA,EAAM;AAC9C,MAAA,IAAI,OAAO,IAAA,CAAK,CAAA,KAAM,QAAA,EAAU,IAAA,CAAK,IAAI,IAAA,CAAK,CAAA;AAC9C,MAAA,IAAI,OAAO,IAAA,CAAK,SAAA,KAAc,QAAA,EAAU,IAAA,CAAK,YAAY,IAAA,CAAK,SAAA;AAC9D,MAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,UAAA,EAAY,IAAI,CAAA;AAC5C,MAAA,OAAO,EAAE,QAAA,EAAU,gBAAA,CAAiB,IAAI,GAAG,IAAA,EAAK;AAAA,IAClD,CAAA;AAAA;AAAA,IAGA,MAAM,cAAc,UAAA,EAAkD;AACpE,MAAA,IAAI,OAAO,UAAA,KAAe,QAAA,IAAY,CAAC,UAAA,CAAW,MAAK,EAAG;AACxD,QAAA,MAAM,IAAI,4BAA4B,uCAAuC,CAAA;AAAA,MAC/E;AACA,MAAA,MAAM,OAAO,MAAM,QAAA,CAAS,gBAAA,EAAkB,EAAE,YAAY,CAAA;AAC5D,MAAA,MAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,mBAAmB,CAAA;AACxC,MAAA,OAAO,EAAE,IAAI,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,CAAE,IAAA,EAAK,GAAI,MAAA,EAAW,IAAA,EAAK;AAAA,IACjD,CAAA;AAAA;AAAA,IAGA,MAAM,cAAc,IAAA,EAA6C;AAC/D,MAAA,MAAM,OAAgC,EAAC;AACvC,MAAA,IAAI,OAAO,IAAA,KAAS,QAAA,IAAY,IAAA,OAAW,IAAA,GAAO,IAAA;AAClD,MAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,gBAAA,EAAkB,IAAI,CAAA;AAClD,MAAA,OAAO,IAAA,GAAO,eAAA,CAAgB,IAAI,CAAA,GAAI,gBAAgB,IAAI,CAAA;AAAA,IAC5D;AAAA,GACF;AACF;AAGO,IAAM,2BAAA,GAAN,cAA0C,iBAAA,CAAkB;AAAA,EACjE,WAAA,CAAY,UAAU,8CAAA,EAAgD;AACpE,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,6BAAA;AAAA,EACd;AACF;AAKA,SAAS,cAAc,IAAA,EAAoC;AACzD,EAAA,MAAM,OAAA,GAAU,KAAK,SAAA,EAAU;AAC/B,EAAA,IAAI,CAAC,OAAA,CAAQ,UAAA,CAAW,GAAG,GAAG,OAAO,MAAA;AACrC,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA;AAC3B,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,KAAK,CAAA,EAAG,OAAO,GAAA,CAAI,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,KAAmB,OAAO,MAAM,QAAQ,CAAA;AAAA,EACjG,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,OAAO,MAAA;AACT;AAGA,SAAS,cAAc,IAAA,EAA0B;AAC/C,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA;AAC3B,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,SAAU,EAAC;AACjC,IAAA,OAAO,GAAA,CAAI,GAAA,CAAI,CAAC,CAAA,KAAM;AACpB,MAAA,MAAM,CAAA,GAAK,KAAK,EAAC;AACjB,MAAA,OAAO;AAAA,QACL,EAAA,EAAI,MAAA,CAAO,CAAA,CAAE,EAAA,IAAM,EAAE,CAAA;AAAA,QACrB,IAAA,EAAM,MAAA,CAAO,CAAA,CAAE,IAAA,IAAQ,EAAE,CAAA;AAAA,QACzB,MAAM,OAAO,CAAA,CAAE,IAAA,KAAS,QAAA,GAAW,EAAE,IAAA,GAAO,CAAA;AAAA,QAC5C,WAAA,EAAa,MAAA,CAAO,CAAA,CAAE,WAAA,IAAe,EAAE,CAAA;AAAA,QACvC,QAAA,EAAU,MAAA,CAAO,CAAA,CAAE,QAAA,IAAY,EAAE,CAAA;AAAA,QACjC,GAAI,OAAO,CAAA,CAAE,MAAA,KAAW,QAAA,GAAW,EAAE,MAAA,EAAQ,CAAA,CAAE,MAAA,EAA6B,GAAI;AAAC,OACnF;AAAA,IACF,CAAC,CAAA;AAAA,EACH,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,EAAC;AAAA,EACV;AACF;AAGA,SAAS,iBAAiB,IAAA,EAAkC;AAC1D,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,6BAA6B,CAAA;AAClD,EAAA,OAAO,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,GAAI,MAAA;AACpB;AAOA,SAAS,iBAAiB,IAAA,EAA4B;AACpD,EAAA,IAAI,yBAAyB,IAAA,CAAK,IAAA,CAAK,WAAW,CAAA,SAAU,EAAC;AAC7D,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,MAAM,CAAA;AAChC,EAAA,MAAM,MAAoB,EAAC;AAC3B,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,MAAM,EAAA,GAAK,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA;AAC7B,IAAA,MAAM,WAAW,EAAA,KAAO,EAAA,GAAK,QAAQ,KAAA,CAAM,KAAA,CAAM,GAAG,EAAE,CAAA;AACtD,IAAA,MAAM,OAAO,EAAA,KAAO,EAAA,GAAK,KAAK,KAAA,CAAM,KAAA,CAAM,KAAK,CAAC,CAAA;AAChD,IAAA,MAAM,CAAA,GAAI,QAAA,CAAS,KAAA,CAAM,oBAAoB,CAAA;AAC7C,IAAA,IAAI,CAAC,CAAA,EAAG;AACR,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,CAAA,CAAE,CAAC,CAAC,CAAA;AACzB,IAAA,MAAM,IAAA,GAAO,EAAE,CAAC,CAAA;AAEhB,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,QAAK,CAAA;AAC9B,IAAA,MAAM,OAAA,GAAsB,EAAE,KAAA,EAAO,IAAA,EAAM,IAAA,EAAK;AAChD,IAAA,IAAI,KAAA,CAAM,CAAC,CAAA,EAAG,OAAA,CAAQ,OAAO,KAAA,CAAM,CAAC,EAAE,IAAA,EAAK;AAC3C,IAAA,IAAI,KAAA,CAAM,CAAC,CAAA,EAAG,OAAA,CAAQ,QAAQ,KAAA,CAAM,CAAC,EAAE,IAAA,EAAK;AAC5C,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,kBAAkB,CAAA;AAC5C,IAAA,IAAI,QAAQ,OAAA,CAAQ,KAAA,GAAQ,MAAA,CAAO,MAAA,CAAO,CAAC,CAAC,CAAA;AAC5C,IAAA,GAAA,CAAI,KAAK,OAAO,CAAA;AAAA,EAClB;AACA,EAAA,OAAO,GAAA;AACT;AAGA,SAAS,gBAAgB,IAAA,EAAmC;AAC1D,EAAA,IAAI,qBAAA,CAAsB,IAAA,CAAK,IAAA,CAAK,SAAA,EAAW,CAAA,EAAG;AAChD,IAAA,OAAO,EAAE,MAAM,MAAA,EAAQ,MAAA,EAAQ,EAAC,EAAG,SAAA,EAAW,MAAM,IAAA,EAAK;AAAA,EAC3D;AACA,EAAA,MAAM,SAA+B,EAAC;AACtC,EAAA,KAAA,MAAW,IAAA,IAAQ,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA,EAAG;AACnC,IAAA,MAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,sCAAsC,CAAA;AAC3D,IAAA,IAAI,GAAG,MAAA,CAAO,IAAA,CAAK,EAAE,UAAA,EAAY,EAAE,CAAC,CAAA,CAAE,IAAA,EAAK,EAAG,SAAS,CAAA,CAAE,CAAC,CAAA,CAAE,IAAA,IAAQ,CAAA;AAAA,EACtE;AACA,EAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,MAAA,EAAQ,SAAA,EAAW,MAAM,IAAA,EAAK;AACvD;AAGA,SAAS,gBAAgB,IAAA,EAAmC;AAC1D,EAAA,IAAI,gBAAA,CAAiB,IAAA,CAAK,IAAI,CAAA,EAAG;AAC/B,IAAA,OAAO,EAAE,MAAM,MAAA,EAAQ,MAAA,EAAQ,EAAC,EAAG,SAAA,EAAW,MAAM,IAAA,EAAK;AAAA,EAC3D;AACA,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,2CAA2C,CAAA;AAChE,EAAA,MAAM,SAAA,GAAY,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,GAAI,IAAA;AAC7B,EAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,QAAQ,EAAC,EAAG,WAAW,IAAA,EAAK;AACrD","file":"index.js","sourcesContent":["// JSON-RPC 2.0 framing for the membrane MCP wire, plus the SDK error taxonomy.\n//\n// The membrane speaks hand-rolled JSON-RPC over Streamable HTTP. The SDK only\n// ever sends `initialize` and `tools/call`. A tools/call comes back as a RESULT\n// shaped `{ content: [{ type:\"text\", text }], isError }`; a protocol-level\n// failure comes back as a JSON-RPC `error` object. This module owns the framing\n// + the parsing helpers; the wrappers in client.ts decode the text content.\n\n/** A JSON-RPC request envelope. */\nexport interface JsonRpcRequest {\n jsonrpc: \"2.0\";\n id: number;\n method: string;\n params?: unknown;\n}\n\n/** A JSON-RPC error object. */\nexport interface JsonRpcErrorObject {\n code: number;\n message: string;\n data?: unknown;\n}\n\n/** A parsed JSON-RPC response (success xor error). */\nexport interface JsonRpcResponse {\n jsonrpc: \"2.0\";\n id: number | null;\n result?: unknown;\n error?: JsonRpcErrorObject;\n}\n\n/** The MCP tools/call result shape (`content` blocks + an error flag). */\nexport interface ToolResult {\n content: Array<{ type: string; text?: string }>;\n isError?: boolean;\n}\n\n/** Base class for every error the SDK throws, so callers can `catch (e)`. */\nexport class UseMyContextError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"UseMyContextError\";\n // Restore the prototype chain (TS target < ES2015 / transpiled extends).\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when a tool wrapper is called before a successful `connect()`. */\nexport class NotConnectedError extends UseMyContextError {\n constructor(message = \"Not connected. Call connect({ token }) first.\") {\n super(message);\n this.name = \"NotConnectedError\";\n }\n}\n\n/**\n * Thrown when the membrane rejects the token (HTTP 401 + WWW-Authenticate).\n * The client also transitions to `expired` so the host can re-connect.\n */\nexport class TokenExpiredError extends UseMyContextError {\n /** The raw `WWW-Authenticate` header, when the server sent one. */\n readonly wwwAuthenticate?: string;\n constructor(message = \"The connection token was rejected (expired or revoked).\", wwwAuthenticate?: string) {\n super(message);\n this.name = \"TokenExpiredError\";\n this.wwwAuthenticate = wwwAuthenticate;\n }\n}\n\n/** Thrown when a tool call is refused for lack of scope (membrane -32604). */\nexport class ScopeDeniedError extends UseMyContextError {\n readonly code = -32604;\n constructor(message: string) {\n super(message);\n this.name = \"ScopeDeniedError\";\n }\n}\n\n/** Thrown for a JSON-RPC protocol error or a tool-level `isError` result. */\nexport class ToolCallError extends UseMyContextError {\n /** The JSON-RPC error code, when the failure was a protocol error. */\n readonly code?: number;\n constructor(message: string, code?: number) {\n super(message);\n this.name = \"ToolCallError\";\n this.code = code;\n }\n}\n\n/** Thrown for transport/parse failures (network down, non-JSON body, ...). */\nexport class TransportError extends UseMyContextError {\n constructor(message: string) {\n super(message);\n this.name = \"TransportError\";\n }\n}\n\nlet nextId = 1;\n\n/** Build an `initialize` request (the connect handshake). */\nexport function initializeRequest(): JsonRpcRequest {\n return {\n jsonrpc: \"2.0\",\n id: nextId++,\n method: \"initialize\",\n params: {\n protocolVersion: \"2025-06-18\",\n capabilities: {},\n clientInfo: { name: \"usemycontext-sdk\", version: \"0.1.0\" },\n },\n };\n}\n\n/** Build a `tools/call` request for a tool name + arguments. */\nexport function toolCallRequest(name: string, args: Record<string, unknown>): JsonRpcRequest {\n return {\n jsonrpc: \"2.0\",\n id: nextId++,\n method: \"tools/call\",\n params: { name, arguments: args },\n };\n}\n\n/**\n * Narrow an unknown body to a JSON-RPC response. Throws TransportError when the\n * body is not a well-formed JSON-RPC 2.0 envelope (a defensive guard against a\n * misbehaving transport or an HTML error page).\n */\nexport function asJsonRpcResponse(body: unknown): JsonRpcResponse {\n if (body === null || typeof body !== \"object\") {\n throw new TransportError(\"Membrane returned a non-object response body.\");\n }\n const b = body as Record<string, unknown>;\n if (b.jsonrpc !== \"2.0\") {\n throw new TransportError(\"Membrane response was not JSON-RPC 2.0.\");\n }\n return b as unknown as JsonRpcResponse;\n}\n\n/** True iff a JSON-RPC error code denotes a missing-scope denial (-32604). */\nexport function isScopeDenied(code: number): boolean {\n return code === -32604;\n}\n\n/**\n * Pull the single text block out of a tools/call RESULT. The membrane always\n * renders to ONE text content block, so this returns its `text` (or \"\" when a\n * result carried no text block).\n */\nexport function resultText(result: ToolResult): string {\n for (const block of result.content ?? []) {\n if (block.type === \"text\" && typeof block.text === \"string\") return block.text;\n }\n return \"\";\n}\n","// The connect state machine - a PURE reducer (no IO, no timers, no `this`).\n//\n// States (ConnectState):\n// idle -> connecting -> connected\n// \\-> declined (user/host refused; v2 OAuth path)\n// \\-> error (network/transport/parse failure)\n// connected -> expired (membrane 401 mid-session)\n// connected -> disconnected (explicit disconnect)\n// <any> -> disconnected (disconnect is always reachable)\n// <terminal>-> connecting (a fresh connect() retries)\n//\n// The reducer is the single source of truth for legality: an event that does\n// not apply to the current state is a NO-OP (returns the same state), so the\n// client can fire events without guarding, and tests can assert every legal AND\n// illegal transition deterministically.\n\nimport type { ConnectState } from \"./types.js\";\n\n/** The events that drive the machine. */\nexport type ConnectEvent =\n | { type: \"CONNECT\" } // begin a connect attempt\n | { type: \"CONNECT_SUCCESS\" } // membrane accepted the token\n | { type: \"CONNECT_DECLINED\" } // user/host refused (v2 OAuth path)\n | { type: \"TOKEN_EXPIRED\" } // membrane 401 (expired/revoked)\n | { type: \"ERROR\" } // a non-auth failure\n | { type: \"DISCONNECT\" }; // explicit disconnect / token cleared\n\n/** The initial state of a fresh client. */\nexport const INITIAL_STATE: ConnectState = \"idle\";\n\n/**\n * The pure transition function. Returns the NEXT state for `(state, event)`, or\n * the SAME state when the event is illegal in that state (a no-op).\n */\nexport function reduce(state: ConnectState, event: ConnectEvent): ConnectState {\n switch (event.type) {\n case \"CONNECT\":\n // A connect attempt is legal from any NON-in-flight state: a fresh client,\n // a terminal/failed state (retry), or an already-connected client\n // (re-connect with a new token). It is a no-op only while already\n // connecting (a second concurrent connect does nothing).\n return state === \"connecting\" ? state : \"connecting\";\n\n case \"CONNECT_SUCCESS\":\n // Only a connect IN FLIGHT can succeed. Any other state ignores it (a\n // stale/duplicate success after disconnect must not resurrect the client).\n return state === \"connecting\" ? \"connected\" : state;\n\n case \"CONNECT_DECLINED\":\n // Only a connect in flight can be declined.\n return state === \"connecting\" ? \"declined\" : state;\n\n case \"ERROR\":\n // A connect in flight can fail with a non-auth error. (A wrapper-call\n // failure while `connected` does NOT change the connect state - the\n // connection is still valid - so ERROR is a no-op when connected.)\n return state === \"connecting\" ? \"error\" : state;\n\n case \"TOKEN_EXPIRED\":\n // The membrane rejected the token. Legal from `connecting` (the token was\n // bad at connect time) or `connected` (it expired/was revoked mid-session).\n return state === \"connecting\" || state === \"connected\" ? \"expired\" : state;\n\n case \"DISCONNECT\":\n // Disconnect is ALWAYS reachable and idempotent: it moves any state to\n // `disconnected` (a no-op only when already there).\n return state === \"disconnected\" ? state : \"disconnected\";\n\n default: {\n // Exhaustiveness: a new event type must be handled above.\n const _never: never = event;\n return state;\n }\n }\n}\n\n/** Every state the machine can occupy (for reachability assertions). */\nexport const ALL_STATES: readonly ConnectState[] = [\n \"idle\",\n \"connecting\",\n \"connected\",\n \"declined\",\n \"expired\",\n \"error\",\n \"disconnected\",\n] as const;\n\n/** Whether the 7 tool wrappers may be called in this state. */\nexport function isConnected(state: ConnectState): boolean {\n return state === \"connected\";\n}\n","// Token persistence. SAFE BY DEFAULT: persistence is in-memory only, so the\n// token NEVER touches `localStorage` unless the host page opts in explicitly\n// (pass `storage: webStorage(localStorage)`). The v1 token-in credential is the\n// user's long-lived session JWT - writing it to `localStorage` would expose a\n// ~decade-long key to any script on the host page (one XSS = full context\n// theft), which the threat model forbids. So the default is memory; opt IN to\n// cross-reload persistence knowingly, never opt out. Pass `storage: null` to\n// disable persistence entirely (a no-op store).\n\nimport type { StorageAdapter } from \"./types.js\";\n\n/** The default storage key the token is persisted under. */\nexport const DEFAULT_STORAGE_KEY = \"usemycontext.token\";\n\n/** An in-memory storage adapter (SSR-safe; the DEFAULT - token never leaves JS memory). */\nexport function memoryStorage(): StorageAdapter {\n const map = new Map<string, string>();\n return {\n get: (k) => (map.has(k) ? (map.get(k) as string) : null),\n set: (k, v) => void map.set(k, v),\n remove: (k) => void map.delete(k),\n };\n}\n\n/** Wrap a Web Storage (localStorage/sessionStorage) as a StorageAdapter. */\nexport function webStorage(store: Storage): StorageAdapter {\n return {\n get: (k) => {\n try {\n return store.getItem(k);\n } catch {\n return null;\n }\n },\n set: (k, v) => {\n try {\n store.setItem(k, v);\n } catch {\n /* quota / privacy mode - degrade to no-persist, never throw */\n }\n },\n remove: (k) => {\n try {\n store.removeItem(k);\n } catch {\n /* ignore */\n }\n },\n };\n}\n\n/**\n * Resolve the storage adapter to use given the option:\n * - an explicit adapter -> use it (e.g. `webStorage(localStorage)` to opt IN\n * to cross-reload persistence, knowingly accepting host-page token exposure);\n * - `null` -> persistence disabled (a no-op adapter);\n * - `undefined` (the DEFAULT) -> in-memory. The token NEVER touches\n * `localStorage` unless the caller asks for it. Safe by default.\n */\nexport function resolveStorage(opt: StorageAdapter | null | undefined): StorageAdapter {\n if (opt === null) {\n return { get: () => null, set: () => {}, remove: () => {} };\n }\n if (opt) return opt;\n // DEFAULT: in-memory only. We deliberately do NOT auto-select localStorage -\n // persisting the long-lived session JWT to the host page is an opt-in choice.\n return memoryStorage();\n}\n","// The default `fetch`-based transport. Tests inject a mock transport instead, so\n// this module is never exercised by the (zero-network) test suite - it is the\n// production wiring that POSTs one JSON-RPC request to the membrane `/mcp`\n// endpoint with the user's bearer token.\n\nimport type { Transport, TransportResponse } from \"./types.js\";\n\n/** A `fetch`-backed Transport (the default when none is supplied). */\nexport const fetchTransport: Transport = async ({ url, token, body }): Promise<TransportResponse> => {\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n accept: \"application/json\",\n authorization: `Bearer ${token}`,\n },\n body: JSON.stringify(body),\n });\n let parsed: unknown = null;\n try {\n parsed = await res.json();\n } catch {\n parsed = null; // non-JSON / empty body (e.g. a 401 with no body)\n }\n return {\n status: res.status,\n body: parsed,\n wwwAuthenticate: res.headers.get(\"www-authenticate\"),\n };\n};\n","// The core client: `useMyContext()` (a framework-free factory) + the 7 tool\n// wrappers. It holds the user's OWN token and calls the membrane exactly like\n// Claude does - same trust model, new client shape. The service stays\n// structurally blind.\n//\n// v1 IS TOKEN-IN ONLY: `connect({ token })`. The full browser-OAuth-in-the-SDK\n// flow (a hosted popup on connect.usemycontext.ai) is v2, gated on the Phase 2\n// CORS landing - deliberately NOT built here.\n\nimport {\n initializeRequest,\n toolCallRequest,\n asJsonRpcResponse,\n resultText,\n isScopeDenied,\n NotConnectedError,\n TokenExpiredError,\n ScopeDeniedError,\n ToolCallError,\n TransportError,\n UseMyContextError,\n type ToolResult,\n} from \"./jsonrpc.js\";\nimport { reduce, INITIAL_STATE, isConnected } from \"./machine.js\";\nimport { resolveStorage, DEFAULT_STORAGE_KEY } from \"./storage.js\";\nimport { fetchTransport } from \"./transport.js\";\nimport type {\n ClientOptions,\n ConnectArgs,\n ConnectState,\n FileMeta,\n ProfileResult,\n AskOptions,\n AskResult,\n AskPassage,\n GetFileResult,\n SuggestUpdateResult,\n SharedContextResult,\n SharedContextEntry,\n StorageAdapter,\n Transport,\n} from \"./types.js\";\n\n/** The production membrane MCP endpoint. */\nexport const DEFAULT_ENDPOINT = \"https://mcp.usemycontext.ai/mcp\";\n\n/** The shape `useMyContext()` returns - the client handle. */\nexport interface UseMyContextClient {\n /** The current connect state. */\n readonly state: ConnectState;\n /** Convenience: true iff `state === \"connected\"`. */\n readonly connected: boolean;\n /** The held token, when connected (else null). */\n readonly token: string | null;\n\n /** Subscribe to state changes. Returns an unsubscribe fn. */\n subscribe(listener: (state: ConnectState) => void): () => void;\n\n /** v1 TOKEN-IN connect: validate the token against the membrane. */\n connect(args: ConnectArgs): Promise<ConnectState>;\n /** Clear the token + persisted storage; -> `disconnected`. */\n disconnect(): void;\n\n // --- the 7 MCP tool wrappers ---\n profile(): Promise<ProfileResult>;\n listFiles(): Promise<FileMeta[]>;\n searchFiles(query: string): Promise<FileMeta[]>;\n getFile(fileId: string): Promise<GetFileResult>;\n ask(query: string, opts?: AskOptions): Promise<AskResult>;\n suggestUpdate(suggestion: string): Promise<SuggestUpdateResult>;\n sharedContext(from?: string): Promise<SharedContextResult>;\n}\n\n/**\n * Create a UseMyContext client. Framework-free: the React hook wraps this.\n * Restores a persisted token (without revalidating) so a returning host page\n * starts `connected`; a stale token surfaces as `expired` on the first call.\n */\nexport function useMyContext(options: ClientOptions = {}): UseMyContextClient {\n const endpoint = options.endpoint ?? DEFAULT_ENDPOINT;\n const transport: Transport = options.transport ?? fetchTransport;\n const storage: StorageAdapter = resolveStorage(options.storage);\n const storageKey = options.storageKey ?? DEFAULT_STORAGE_KEY;\n\n const listeners = new Set<(state: ConnectState) => void>();\n let state: ConnectState = INITIAL_STATE;\n let token: string | null = null;\n\n // Restore a persisted token: a returning page is `connected` optimistically\n // (the membrane is the authority - a stale token -> `expired` on first call).\n const persisted = storage.get(storageKey);\n if (persisted) {\n token = persisted;\n state = \"connected\";\n }\n\n function setState(next: ConnectState): void {\n if (next === state) return;\n state = next;\n options.onStateChange?.(state);\n for (const l of listeners) l(state);\n }\n\n function dispatch(event: Parameters<typeof reduce>[1]): void {\n setState(reduce(state, event));\n }\n\n /** One JSON-RPC round-trip against the membrane with the held token. */\n async function rpc(body: unknown): Promise<unknown> {\n if (!token) throw new NotConnectedError();\n let res;\n try {\n res = await transport({ url: endpoint, token, body });\n } catch (e) {\n throw new TransportError(\n `Transport failed: ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n // A 401 means the token is bad/expired/revoked. Drive the machine to\n // `expired` and throw - the host should re-connect.\n if (res.status === 401) {\n dispatch({ type: \"TOKEN_EXPIRED\" });\n throw new TokenExpiredError(undefined, res.wwwAuthenticate ?? undefined);\n }\n const parsed = asJsonRpcResponse(res.body);\n if (parsed.error) {\n if (isScopeDenied(parsed.error.code)) {\n throw new ScopeDeniedError(parsed.error.message);\n }\n throw new ToolCallError(parsed.error.message, parsed.error.code);\n }\n return parsed.result;\n }\n\n /** A tools/call that returns the single rendered text block. */\n async function callTool(name: string, args: Record<string, unknown>): Promise<string> {\n if (!isConnected(state)) throw new NotConnectedError();\n const result = (await rpc(toolCallRequest(name, args))) as ToolResult;\n if (result.isError) {\n throw new ToolCallError(resultText(result) || `${name} failed.`);\n }\n return resultText(result);\n }\n\n return {\n get state() {\n return state;\n },\n get connected() {\n return state === \"connected\";\n },\n get token() {\n return state === \"connected\" ? token : null;\n },\n\n subscribe(listener) {\n listeners.add(listener);\n return () => void listeners.delete(listener);\n },\n\n async connect({ token: t }: ConnectArgs): Promise<ConnectState> {\n if (typeof t !== \"string\" || t.length === 0) {\n // A bad arg is a developer error, not a state transition.\n throw new UseMyContextConnectArgError();\n }\n dispatch({ type: \"CONNECT\" });\n token = t;\n try {\n const res = await transport({ url: endpoint, token: t, body: initializeRequest() });\n if (res.status === 401) {\n token = null;\n dispatch({ type: \"TOKEN_EXPIRED\" });\n throw new TokenExpiredError(undefined, res.wwwAuthenticate ?? undefined);\n }\n // Validate the handshake came back as a well-formed JSON-RPC result.\n const parsed = asJsonRpcResponse(res.body);\n if (parsed.error) {\n token = null;\n dispatch({ type: \"ERROR\" });\n throw new ToolCallError(parsed.error.message, parsed.error.code);\n }\n dispatch({ type: \"CONNECT_SUCCESS\" });\n if (storage) storage.set(storageKey, t);\n return state;\n } catch (e) {\n token = null;\n // TokenExpiredError already drove the machine to `expired`; any other\n // failure is a non-auth `error`.\n if (!(e instanceof TokenExpiredError)) {\n dispatch({ type: \"ERROR\" });\n if (e instanceof TransportError || e instanceof ToolCallError) throw e;\n throw new TransportError(\n `Connect failed: ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n throw e;\n }\n },\n\n disconnect(): void {\n token = null;\n storage.remove(storageKey);\n dispatch({ type: \"DISCONNECT\" });\n },\n\n // --- profile ---------------------------------------------------------\n async profile(): Promise<ProfileResult> {\n const text = await callTool(\"profile\", {});\n // The membrane serves the compiled composite as plain text, OR (no compile\n // yet) the JSON of `{ facts: [...] }`. Detect the fallback by parsing.\n const facts = tryParseFacts(text);\n if (facts) return { composite: text, facts };\n return { composite: text };\n },\n\n // --- list_files / search_files (JSON array of FileMeta) --------------\n async listFiles(): Promise<FileMeta[]> {\n return parseFileList(await callTool(\"list_files\", {}));\n },\n async searchFiles(query: string): Promise<FileMeta[]> {\n if (typeof query !== \"string\") throw new UseMyContextConnectArgError(\"search query must be a string\");\n return parseFileList(await callTool(\"search_files\", { query }));\n },\n\n // --- get_file --------------------------------------------------------\n async getFile(fileId: string): Promise<GetFileResult> {\n if (typeof fileId !== \"string\" || !fileId) {\n throw new UseMyContextConnectArgError(\"fileId must be a non-empty string\");\n }\n const text = await callTool(\"get_file\", { fileId });\n return { text, downloadUrl: parseDownloadUrl(text) };\n },\n\n // --- ask_docs --------------------------------------------------------\n async ask(query: string, opts: AskOptions = {}): Promise<AskResult> {\n if (typeof query !== \"string\" || !query) {\n throw new UseMyContextConnectArgError(\"ask query must be a non-empty string\");\n }\n const args: Record<string, unknown> = { query };\n if (typeof opts.k === \"number\") args.k = opts.k;\n if (typeof opts.projectId === \"string\") args.projectId = opts.projectId;\n const text = await callTool(\"ask_docs\", args);\n return { passages: parseAskPassages(text), text };\n },\n\n // --- suggest_update (the one write) ----------------------------------\n async suggestUpdate(suggestion: string): Promise<SuggestUpdateResult> {\n if (typeof suggestion !== \"string\" || !suggestion.trim()) {\n throw new UseMyContextConnectArgError(\"suggestion must be a non-empty string\");\n }\n const text = await callTool(\"suggest_update\", { suggestion });\n const m = text.match(/\\(id:\\s*([^)]+)\\)/);\n return { id: m ? m[1].trim() : undefined, text };\n },\n\n // --- shared_context --------------------------------------------------\n async sharedContext(from?: string): Promise<SharedContextResult> {\n const args: Record<string, unknown> = {};\n if (typeof from === \"string\" && from) args.from = from;\n const text = await callTool(\"shared_context\", args);\n return from ? parseSharedRead(text) : parseSharedList(text);\n },\n };\n}\n\n/** Thrown for a bad SDK call argument (a developer error, not a state event). */\nexport class UseMyContextConnectArgError extends UseMyContextError {\n constructor(message = \"connect() requires a non-empty token string.\") {\n super(message);\n this.name = \"UseMyContextConnectArgError\";\n }\n}\n\n// --- parsers (decode the membrane's RENDERED text back to typed shapes) ---\n\n/** Parse `profile`'s JSON fallback (`{facts:[...]}`); null when it's prose. */\nfunction tryParseFacts(text: string): string[] | undefined {\n const trimmed = text.trimStart();\n if (!trimmed.startsWith(\"{\")) return undefined;\n try {\n const obj = JSON.parse(text) as { facts?: unknown };\n if (Array.isArray(obj.facts)) return obj.facts.filter((f): f is string => typeof f === \"string\");\n } catch {\n /* prose composite that happens to start with \"{\" - treat as composite */\n }\n return undefined;\n}\n\n/** Parse a list/search JSON array into FileMeta[]. */\nfunction parseFileList(text: string): FileMeta[] {\n try {\n const arr = JSON.parse(text) as unknown;\n if (!Array.isArray(arr)) return [];\n return arr.map((r) => {\n const o = (r ?? {}) as Record<string, unknown>;\n return {\n id: String(o.id ?? \"\"),\n name: String(o.name ?? \"\"),\n size: typeof o.size === \"number\" ? o.size : 0,\n contentType: String(o.contentType ?? \"\"),\n modified: String(o.modified ?? \"\"),\n ...(typeof o.status === \"string\" ? { status: o.status as FileMeta[\"status\"] } : {}),\n };\n });\n } catch {\n return [];\n }\n}\n\n/** Pull a `Download link ... : <url>` out of a get_file render. */\nfunction parseDownloadUrl(text: string): string | undefined {\n const m = text.match(/Download link[^:]*:\\s*(\\S+)/);\n return m ? m[1] : undefined;\n}\n\n/**\n * Parse `renderAskDocs` output: blocks separated by a blank line, each a\n * `[n] file · scope · score X.XXX` citation line then the passage text. An empty\n * result is the calm \"No relevant passages...\" sentence -> zero passages.\n */\nfunction parseAskPassages(text: string): AskPassage[] {\n if (/^No relevant passages/i.test(text.trimStart())) return [];\n const blocks = text.split(\"\\n\\n\");\n const out: AskPassage[] = [];\n for (const block of blocks) {\n const nl = block.indexOf(\"\\n\");\n const citation = nl === -1 ? block : block.slice(0, nl);\n const body = nl === -1 ? \"\" : block.slice(nl + 1);\n const m = citation.match(/^\\[(\\d+)\\]\\s*(.*)$/);\n if (!m) continue;\n const index = Number(m[1]);\n const rest = m[2];\n // rest = `file · scope · score N.NNN`\n const parts = rest.split(\" · \");\n const passage: AskPassage = { index, text: body };\n if (parts[0]) passage.file = parts[0].trim();\n if (parts[1]) passage.scope = parts[1].trim();\n const scoreM = rest.match(/score\\s+([\\d.]+)/);\n if (scoreM) passage.score = Number(scoreM[1]);\n out.push(passage);\n }\n return out;\n}\n\n/** Parse `renderSharedContext` LIST output (lines `- who (from: grantId)`). */\nfunction parseSharedList(text: string): SharedContextResult {\n if (/^No one has shared/i.test(text.trimStart())) {\n return { mode: \"list\", shares: [], composite: null, text };\n }\n const shares: SharedContextEntry[] = [];\n for (const line of text.split(\"\\n\")) {\n const m = line.match(/^-\\s*(.*?)\\s*\\(from:\\s*([^)]+)\\)\\s*$/);\n if (m) shares.push({ ownerLabel: m[1].trim(), grantId: m[2].trim() });\n }\n return { mode: \"list\", shares, composite: null, text };\n}\n\n/** Parse `renderSharedContext` READ output (`Shared context from X:\\n\\n<text>`). */\nfunction parseSharedRead(text: string): SharedContextResult {\n if (/not available/i.test(text)) {\n return { mode: \"read\", shares: [], composite: null, text };\n }\n const m = text.match(/^Shared context from [^:]*:\\n\\n([\\s\\S]*)$/);\n const composite = m ? m[1] : text;\n return { mode: \"read\", shares: [], composite, text };\n}\n"]}
|