@byoky/sdk 0.2.0 → 0.4.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/LICENSE +21 -0
- package/dist/index.cjs +290 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -4
- package/dist/index.d.ts +28 -4
- package/dist/index.js +293 -9
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +223 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +28 -0
- package/dist/server.d.ts +28 -0
- package/dist/server.js +202 -0
- package/dist/server.js.map +1 -0
- package/package.json +29 -10
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 byoky contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.cjs
CHANGED
|
@@ -21,8 +21,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
Byoky: () => Byoky,
|
|
24
|
-
ByokyError: () =>
|
|
25
|
-
ByokyErrorCode: () =>
|
|
24
|
+
ByokyError: () => import_core4.ByokyError,
|
|
25
|
+
ByokyErrorCode: () => import_core4.ByokyErrorCode,
|
|
26
26
|
createProxyFetch: () => createProxyFetch,
|
|
27
27
|
getStoreUrl: () => getStoreUrl,
|
|
28
28
|
isExtensionInstalled: () => isExtensionInstalled
|
|
@@ -30,7 +30,7 @@ __export(index_exports, {
|
|
|
30
30
|
module.exports = __toCommonJS(index_exports);
|
|
31
31
|
|
|
32
32
|
// src/byoky.ts
|
|
33
|
-
var
|
|
33
|
+
var import_core3 = require("@byoky/core");
|
|
34
34
|
|
|
35
35
|
// src/detect.ts
|
|
36
36
|
var import_core = require("@byoky/core");
|
|
@@ -161,6 +161,185 @@ async function readBody(body) {
|
|
|
161
161
|
return void 0;
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
// src/relay-client.ts
|
|
165
|
+
var import_core2 = require("@byoky/core");
|
|
166
|
+
function createRelayClient(wsUrl, sessionKey, providers) {
|
|
167
|
+
let status = "connecting";
|
|
168
|
+
const closeCallbacks = /* @__PURE__ */ new Set();
|
|
169
|
+
const inFlight = /* @__PURE__ */ new Map();
|
|
170
|
+
let ws;
|
|
171
|
+
let pingInterval;
|
|
172
|
+
try {
|
|
173
|
+
const parsed = new URL(wsUrl);
|
|
174
|
+
const isSecure = parsed.protocol === "wss:";
|
|
175
|
+
const isLocalWs = parsed.protocol === "ws:" && (parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1");
|
|
176
|
+
if (!isSecure && !isLocalWs) {
|
|
177
|
+
status = "disconnected";
|
|
178
|
+
return {
|
|
179
|
+
get status() {
|
|
180
|
+
return status;
|
|
181
|
+
},
|
|
182
|
+
close() {
|
|
183
|
+
},
|
|
184
|
+
onClose(cb) {
|
|
185
|
+
cb("Insecure WebSocket URL rejected \u2014 use wss:// for non-localhost connections");
|
|
186
|
+
return () => {
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
} catch {
|
|
192
|
+
status = "disconnected";
|
|
193
|
+
return {
|
|
194
|
+
get status() {
|
|
195
|
+
return status;
|
|
196
|
+
},
|
|
197
|
+
close() {
|
|
198
|
+
},
|
|
199
|
+
onClose(cb) {
|
|
200
|
+
cb("Invalid WebSocket URL");
|
|
201
|
+
return () => {
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
ws = new WebSocket(wsUrl);
|
|
208
|
+
} catch {
|
|
209
|
+
status = "disconnected";
|
|
210
|
+
return {
|
|
211
|
+
get status() {
|
|
212
|
+
return status;
|
|
213
|
+
},
|
|
214
|
+
close() {
|
|
215
|
+
},
|
|
216
|
+
onClose(cb) {
|
|
217
|
+
cb("Failed to create WebSocket");
|
|
218
|
+
return () => {
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
ws.onopen = () => {
|
|
224
|
+
status = "connected";
|
|
225
|
+
const relayId = `relay_${crypto.randomUUID().replace(/-/g, "")}`;
|
|
226
|
+
ws.send(JSON.stringify({
|
|
227
|
+
type: "relay:hello",
|
|
228
|
+
sessionId: relayId,
|
|
229
|
+
providers
|
|
230
|
+
}));
|
|
231
|
+
pingInterval = setInterval(() => {
|
|
232
|
+
if (ws.readyState === import_core2.WS_READY_STATE.OPEN) {
|
|
233
|
+
ws.send(JSON.stringify({ type: "relay:ping", ts: Date.now() }));
|
|
234
|
+
}
|
|
235
|
+
}, 3e4);
|
|
236
|
+
};
|
|
237
|
+
ws.onmessage = (event) => {
|
|
238
|
+
const msg = (0, import_core2.parseRelayMessage)(event.data);
|
|
239
|
+
if (!msg) return;
|
|
240
|
+
switch (msg.type) {
|
|
241
|
+
case "relay:request":
|
|
242
|
+
handleRequest(msg);
|
|
243
|
+
break;
|
|
244
|
+
case "relay:ping":
|
|
245
|
+
ws.send(JSON.stringify({ type: "relay:pong", ts: msg.ts }));
|
|
246
|
+
break;
|
|
247
|
+
case "relay:pong":
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
ws.onclose = (event) => {
|
|
252
|
+
cleanup(event.reason || "WebSocket closed");
|
|
253
|
+
};
|
|
254
|
+
ws.onerror = () => {
|
|
255
|
+
cleanup("WebSocket error");
|
|
256
|
+
};
|
|
257
|
+
async function handleRequest(req) {
|
|
258
|
+
const { requestId, providerId, url, method, headers, body } = req;
|
|
259
|
+
const controller = new AbortController();
|
|
260
|
+
inFlight.set(requestId, controller);
|
|
261
|
+
try {
|
|
262
|
+
const proxyFetch = createProxyFetch(providerId, sessionKey);
|
|
263
|
+
const response = await proxyFetch(url, {
|
|
264
|
+
method,
|
|
265
|
+
headers,
|
|
266
|
+
body,
|
|
267
|
+
signal: controller.signal
|
|
268
|
+
});
|
|
269
|
+
const responseHeaders = {};
|
|
270
|
+
response.headers.forEach((value, key) => {
|
|
271
|
+
responseHeaders[key] = value;
|
|
272
|
+
});
|
|
273
|
+
send({
|
|
274
|
+
type: "relay:response:meta",
|
|
275
|
+
requestId,
|
|
276
|
+
status: response.status,
|
|
277
|
+
statusText: response.statusText,
|
|
278
|
+
headers: responseHeaders
|
|
279
|
+
});
|
|
280
|
+
if (response.body) {
|
|
281
|
+
const reader = response.body.getReader();
|
|
282
|
+
const decoder = new TextDecoder();
|
|
283
|
+
while (true) {
|
|
284
|
+
const { done, value } = await reader.read();
|
|
285
|
+
if (done) break;
|
|
286
|
+
if (controller.signal.aborted) break;
|
|
287
|
+
send({
|
|
288
|
+
type: "relay:response:chunk",
|
|
289
|
+
requestId,
|
|
290
|
+
chunk: decoder.decode(value, { stream: true })
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
send({ type: "relay:response:done", requestId });
|
|
295
|
+
} catch (err) {
|
|
296
|
+
send({
|
|
297
|
+
type: "relay:response:error",
|
|
298
|
+
requestId,
|
|
299
|
+
error: {
|
|
300
|
+
code: err instanceof import_core2.ByokyError ? err.code : "PROXY_ERROR",
|
|
301
|
+
message: err instanceof Error ? err.message : "Unknown error"
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
} finally {
|
|
305
|
+
inFlight.delete(requestId);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function send(msg) {
|
|
309
|
+
if (ws.readyState === import_core2.WS_READY_STATE.OPEN) {
|
|
310
|
+
ws.send(JSON.stringify(msg));
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
function cleanup(reason) {
|
|
314
|
+
if (status === "disconnected") return;
|
|
315
|
+
status = "disconnected";
|
|
316
|
+
if (pingInterval) clearInterval(pingInterval);
|
|
317
|
+
for (const controller of inFlight.values()) {
|
|
318
|
+
controller.abort();
|
|
319
|
+
}
|
|
320
|
+
inFlight.clear();
|
|
321
|
+
for (const cb of closeCallbacks) cb(reason);
|
|
322
|
+
closeCallbacks.clear();
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
get status() {
|
|
326
|
+
return status;
|
|
327
|
+
},
|
|
328
|
+
close() {
|
|
329
|
+
if (ws.readyState === import_core2.WS_READY_STATE.OPEN || ws.readyState === import_core2.WS_READY_STATE.CONNECTING) {
|
|
330
|
+
ws.close(1e3, "Client closed");
|
|
331
|
+
}
|
|
332
|
+
cleanup("Client closed");
|
|
333
|
+
},
|
|
334
|
+
onClose(callback) {
|
|
335
|
+
closeCallbacks.add(callback);
|
|
336
|
+
return () => {
|
|
337
|
+
closeCallbacks.delete(callback);
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
164
343
|
// src/byoky.ts
|
|
165
344
|
var Byoky = class {
|
|
166
345
|
timeout;
|
|
@@ -168,18 +347,61 @@ var Byoky = class {
|
|
|
168
347
|
this.timeout = options.timeout ?? 6e4;
|
|
169
348
|
}
|
|
170
349
|
async connect(request = {}) {
|
|
350
|
+
if (typeof window === "undefined") {
|
|
351
|
+
throw new import_core3.ByokyError(
|
|
352
|
+
import_core3.ByokyErrorCode.UNKNOWN,
|
|
353
|
+
"Byoky SDK requires a browser environment. On the server, use your API key directly."
|
|
354
|
+
);
|
|
355
|
+
}
|
|
171
356
|
if (!isExtensionInstalled()) {
|
|
172
357
|
const storeUrl = getStoreUrl();
|
|
173
358
|
if (storeUrl) {
|
|
174
359
|
window.open(storeUrl, "_blank");
|
|
175
360
|
}
|
|
176
|
-
throw
|
|
361
|
+
throw import_core3.ByokyError.walletNotInstalled();
|
|
177
362
|
}
|
|
178
363
|
const response = await this.sendConnectRequest(request);
|
|
364
|
+
return this.buildSession(response);
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Reconnect to an existing session using previously stored response data.
|
|
368
|
+
* Returns null if the session is no longer valid.
|
|
369
|
+
*/
|
|
370
|
+
async reconnect(savedResponse) {
|
|
371
|
+
if (typeof window === "undefined") return null;
|
|
372
|
+
if (!isExtensionInstalled()) return null;
|
|
373
|
+
const connected = await this.querySessionStatus(savedResponse.sessionKey);
|
|
374
|
+
if (!connected) return null;
|
|
375
|
+
return this.buildSession(savedResponse);
|
|
376
|
+
}
|
|
377
|
+
buildSession(response) {
|
|
378
|
+
const sessionKey = response.sessionKey;
|
|
379
|
+
const disconnectCallbacks = /* @__PURE__ */ new Set();
|
|
380
|
+
function handleRevocation(event) {
|
|
381
|
+
const msg = event.detail;
|
|
382
|
+
if (msg?.type === "BYOKY_SESSION_REVOKED" && msg.payload?.sessionKey === sessionKey) {
|
|
383
|
+
for (const cb of disconnectCallbacks) cb();
|
|
384
|
+
disconnectCallbacks.clear();
|
|
385
|
+
document.removeEventListener("byoky-message", handleRevocation);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
document.addEventListener("byoky-message", handleRevocation);
|
|
179
389
|
return {
|
|
180
390
|
...response,
|
|
181
|
-
createFetch: (providerId) => createProxyFetch(providerId,
|
|
182
|
-
|
|
391
|
+
createFetch: (providerId) => createProxyFetch(providerId, sessionKey),
|
|
392
|
+
createRelay: (wsUrl) => createRelayClient(wsUrl, sessionKey, response.providers),
|
|
393
|
+
disconnect: () => {
|
|
394
|
+
document.removeEventListener("byoky-message", handleRevocation);
|
|
395
|
+
this.sendDisconnect(sessionKey);
|
|
396
|
+
},
|
|
397
|
+
isConnected: () => this.querySessionStatus(sessionKey),
|
|
398
|
+
getUsage: () => this.querySessionUsage(sessionKey),
|
|
399
|
+
onDisconnect: (callback) => {
|
|
400
|
+
disconnectCallbacks.add(callback);
|
|
401
|
+
return () => {
|
|
402
|
+
disconnectCallbacks.delete(callback);
|
|
403
|
+
};
|
|
404
|
+
}
|
|
183
405
|
};
|
|
184
406
|
}
|
|
185
407
|
sendConnectRequest(request) {
|
|
@@ -188,7 +410,7 @@ var Byoky = class {
|
|
|
188
410
|
const timeoutId = setTimeout(() => {
|
|
189
411
|
cleanup();
|
|
190
412
|
reject(
|
|
191
|
-
new
|
|
413
|
+
new import_core3.ByokyError(import_core3.ByokyErrorCode.UNKNOWN, "Connection request timed out")
|
|
192
414
|
);
|
|
193
415
|
}, this.timeout);
|
|
194
416
|
function handleEvent(event) {
|
|
@@ -200,7 +422,7 @@ var Byoky = class {
|
|
|
200
422
|
resolve(msg.payload);
|
|
201
423
|
} else if (msg.type === "BYOKY_ERROR") {
|
|
202
424
|
const { code, message } = msg.payload;
|
|
203
|
-
reject(new
|
|
425
|
+
reject(new import_core3.ByokyError(code, message));
|
|
204
426
|
}
|
|
205
427
|
}
|
|
206
428
|
function cleanup() {
|
|
@@ -219,16 +441,74 @@ var Byoky = class {
|
|
|
219
441
|
);
|
|
220
442
|
});
|
|
221
443
|
}
|
|
222
|
-
|
|
444
|
+
sendDisconnect(sessionKey) {
|
|
223
445
|
window.postMessage(
|
|
224
446
|
{ type: "BYOKY_DISCONNECT", payload: { sessionKey } },
|
|
225
447
|
"*"
|
|
226
448
|
);
|
|
227
449
|
}
|
|
450
|
+
querySessionStatus(sessionKey) {
|
|
451
|
+
return new Promise((resolve) => {
|
|
452
|
+
const requestId = crypto.randomUUID();
|
|
453
|
+
const timeout = setTimeout(() => {
|
|
454
|
+
cleanup();
|
|
455
|
+
resolve(false);
|
|
456
|
+
}, 5e3);
|
|
457
|
+
function handleEvent(event) {
|
|
458
|
+
const msg = event.detail;
|
|
459
|
+
if (msg?.requestId !== requestId) return;
|
|
460
|
+
if (msg.type === "BYOKY_SESSION_STATUS_RESPONSE") {
|
|
461
|
+
cleanup();
|
|
462
|
+
resolve(!!msg.payload?.connected);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
function cleanup() {
|
|
466
|
+
clearTimeout(timeout);
|
|
467
|
+
document.removeEventListener("byoky-message", handleEvent);
|
|
468
|
+
}
|
|
469
|
+
document.addEventListener("byoky-message", handleEvent);
|
|
470
|
+
window.postMessage({
|
|
471
|
+
type: "BYOKY_SESSION_STATUS",
|
|
472
|
+
requestId,
|
|
473
|
+
payload: { sessionKey }
|
|
474
|
+
}, "*");
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
querySessionUsage(sessionKey) {
|
|
478
|
+
return new Promise((resolve, reject) => {
|
|
479
|
+
const requestId = crypto.randomUUID();
|
|
480
|
+
const timeout = setTimeout(() => {
|
|
481
|
+
cleanup();
|
|
482
|
+
reject(new Error("Usage query timed out"));
|
|
483
|
+
}, 5e3);
|
|
484
|
+
function handleEvent(event) {
|
|
485
|
+
const msg = event.detail;
|
|
486
|
+
if (msg?.requestId !== requestId) return;
|
|
487
|
+
if (msg.type === "BYOKY_SESSION_USAGE_RESPONSE") {
|
|
488
|
+
cleanup();
|
|
489
|
+
if (msg.payload) {
|
|
490
|
+
resolve(msg.payload);
|
|
491
|
+
} else {
|
|
492
|
+
reject(new Error("Session not found"));
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
function cleanup() {
|
|
497
|
+
clearTimeout(timeout);
|
|
498
|
+
document.removeEventListener("byoky-message", handleEvent);
|
|
499
|
+
}
|
|
500
|
+
document.addEventListener("byoky-message", handleEvent);
|
|
501
|
+
window.postMessage({
|
|
502
|
+
type: "BYOKY_SESSION_USAGE",
|
|
503
|
+
requestId,
|
|
504
|
+
payload: { sessionKey }
|
|
505
|
+
}, "*");
|
|
506
|
+
});
|
|
507
|
+
}
|
|
228
508
|
};
|
|
229
509
|
|
|
230
510
|
// src/index.ts
|
|
231
|
-
var
|
|
511
|
+
var import_core4 = require("@byoky/core");
|
|
232
512
|
// Annotate the CommonJS export names for ESM import in node:
|
|
233
513
|
0 && (module.exports = {
|
|
234
514
|
Byoky,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/byoky.ts","../src/detect.ts","../src/proxy-fetch.ts"],"sourcesContent":["export { Byoky } from './byoky.js';\nexport type { ByokySession, ByokyOptions } from './byoky.js';\nexport { isExtensionInstalled, getStoreUrl } from './detect.js';\nexport { createProxyFetch } from './proxy-fetch.js';\nexport {\n type ConnectRequest,\n type ConnectResponse,\n type ProviderRequirement,\n ByokyError,\n ByokyErrorCode,\n} from '@byoky/core';\n","import type { ConnectRequest, ConnectResponse } from '@byoky/core';\nimport { ByokyError, ByokyErrorCode, isByokyMessage } from '@byoky/core';\nimport { isExtensionInstalled, getStoreUrl } from './detect.js';\nimport { createProxyFetch } from './proxy-fetch.js';\n\nexport interface ByokySession extends ConnectResponse {\n createFetch(providerId: string): typeof fetch;\n disconnect(): void;\n}\n\nexport interface ByokyOptions {\n timeout?: number;\n}\n\nexport class Byoky {\n private timeout: number;\n\n constructor(options: ByokyOptions = {}) {\n this.timeout = options.timeout ?? 60_000;\n }\n\n async connect(request: ConnectRequest = {}): Promise<ByokySession> {\n if (!isExtensionInstalled()) {\n const storeUrl = getStoreUrl();\n if (storeUrl) {\n window.open(storeUrl, '_blank');\n }\n throw ByokyError.walletNotInstalled();\n }\n\n const response = await this.sendConnectRequest(request);\n\n return {\n ...response,\n createFetch: (providerId: string) =>\n createProxyFetch(providerId, response.sessionKey),\n disconnect: () => this.disconnect(response.sessionKey),\n };\n }\n\n private sendConnectRequest(\n request: ConnectRequest,\n ): Promise<ConnectResponse> {\n return new Promise<ConnectResponse>((resolve, reject) => {\n const requestId = crypto.randomUUID();\n\n const timeoutId = setTimeout(() => {\n cleanup();\n reject(\n new ByokyError(ByokyErrorCode.UNKNOWN, 'Connection request timed out'),\n );\n }, this.timeout);\n\n function handleEvent(event: Event) {\n const msg = (event as CustomEvent).detail;\n if (typeof msg?.type !== 'string' || !msg.type.startsWith('BYOKY_')) return;\n if (msg.requestId !== requestId) return;\n\n cleanup();\n\n if (msg.type === 'BYOKY_CONNECT_RESPONSE') {\n resolve(msg.payload as ConnectResponse);\n } else if (msg.type === 'BYOKY_ERROR') {\n const { code, message } = msg.payload as {\n code: string;\n message: string;\n };\n reject(new ByokyError(code as ByokyErrorCode, message));\n }\n }\n\n function cleanup() {\n clearTimeout(timeoutId);\n document.removeEventListener('byoky-message', handleEvent);\n }\n\n document.addEventListener('byoky-message', handleEvent);\n\n window.postMessage(\n {\n type: 'BYOKY_CONNECT_REQUEST',\n id: requestId,\n requestId,\n payload: request,\n },\n '*',\n );\n });\n }\n\n private disconnect(sessionKey: string): void {\n window.postMessage(\n { type: 'BYOKY_DISCONNECT', payload: { sessionKey } },\n '*',\n );\n }\n}\n","import { BYOKY_PROVIDER_KEY } from '@byoky/core';\n\nexport function isExtensionInstalled(): boolean {\n return typeof window !== 'undefined' && BYOKY_PROVIDER_KEY in window;\n}\n\nexport function getStoreUrl(): string | null {\n if (typeof navigator === 'undefined') return null;\n\n const ua = navigator.userAgent.toLowerCase();\n\n if (ua.includes('chrome') && !ua.includes('edg')) {\n return 'https://chrome.google.com/webstore/detail/byoky/TODO_EXTENSION_ID';\n }\n if (ua.includes('firefox')) {\n return 'https://addons.mozilla.org/en-US/firefox/addon/byoky/';\n }\n if (ua.includes('safari') && !ua.includes('chrome')) {\n return 'https://apps.apple.com/app/byoky/TODO_APP_ID';\n }\n return null;\n}\n","export function createProxyFetch(\n providerId: string,\n sessionKey: string,\n): typeof fetch {\n return async (\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> => {\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.toString()\n : input.url;\n\n const method = init?.method ?? 'GET';\n const headers = init?.headers\n ? Object.fromEntries(new Headers(init.headers).entries())\n : {};\n const body = init?.body ? await readBody(init.body) : undefined;\n\n const requestId = crypto.randomUUID();\n\n return new Promise<Response>((resolve, reject) => {\n const { readable, writable } = new TransformStream<Uint8Array>();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n let resolved = false;\n\n const timeout = setTimeout(() => {\n cleanup();\n reject(new Error('Proxy request timed out'));\n }, 120_000);\n\n function handleEvent(event: Event) {\n const data = (event as CustomEvent).detail ?? (event as MessageEvent).data;\n if (data?.requestId !== requestId) return;\n\n switch (data.type) {\n case 'BYOKY_PROXY_RESPONSE_META':\n if (!resolved) {\n resolved = true;\n clearTimeout(timeout);\n resolve(\n new Response(readable, {\n status: data.status,\n statusText: data.statusText,\n headers: new Headers(data.headers),\n }),\n );\n }\n break;\n\n case 'BYOKY_PROXY_RESPONSE_CHUNK':\n writer.write(encoder.encode(data.chunk)).catch(() => {});\n break;\n\n case 'BYOKY_PROXY_RESPONSE_DONE':\n writer.close().catch(() => {});\n cleanup();\n break;\n\n case 'BYOKY_PROXY_RESPONSE_ERROR': {\n const errResponse = new Response(\n JSON.stringify({ error: data.error }),\n {\n status: data.status || 500,\n headers: { 'content-type': 'application/json' },\n },\n );\n if (!resolved) {\n resolved = true;\n clearTimeout(timeout);\n resolve(errResponse);\n }\n writer.close().catch(() => {});\n cleanup();\n break;\n }\n }\n }\n\n function cleanup() {\n clearTimeout(timeout);\n document.removeEventListener('byoky-message', handleEvent);\n }\n\n document.addEventListener('byoky-message', handleEvent);\n\n window.postMessage(\n {\n type: 'BYOKY_PROXY_REQUEST',\n requestId,\n sessionKey,\n providerId,\n url,\n method,\n headers,\n body,\n },\n '*',\n );\n });\n };\n}\n\nasync function readBody(body: BodyInit): Promise<string | undefined> {\n if (typeof body === 'string') return body;\n if (body instanceof ArrayBuffer) return new TextDecoder().decode(body);\n if (body instanceof Blob) return body.text();\n if (body instanceof URLSearchParams) return body.toString();\n if (body instanceof ReadableStream) {\n const reader = body.getReader();\n const chunks: Uint8Array[] = [];\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n }\n const total = chunks.reduce((acc, c) => acc + c.length, 0);\n const combined = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n return new TextDecoder().decode(combined);\n }\n return undefined;\n}\n"],"mappings":"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","names":["import_core","import_core"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/byoky.ts","../src/detect.ts","../src/proxy-fetch.ts","../src/relay-client.ts"],"sourcesContent":["export { Byoky } from './byoky.js';\nexport type { ByokySession, ByokyOptions } from './byoky.js';\nexport { isExtensionInstalled, getStoreUrl } from './detect.js';\nexport { createProxyFetch } from './proxy-fetch.js';\nexport type { RelayConnection } from './relay-client.js';\nexport {\n type ConnectRequest,\n type ConnectResponse,\n type ProviderRequirement,\n type SessionUsage,\n ByokyError,\n ByokyErrorCode,\n} from '@byoky/core';\n","import type { ConnectRequest, ConnectResponse, SessionUsage } from '@byoky/core';\nimport { ByokyError, ByokyErrorCode, isByokyMessage } from '@byoky/core';\nimport { isExtensionInstalled, getStoreUrl } from './detect.js';\nimport { createProxyFetch } from './proxy-fetch.js';\nimport { createRelayClient, type RelayConnection } from './relay-client.js';\n\nexport interface ByokySession extends ConnectResponse {\n /** Create a fetch function that proxies requests through the wallet for the given provider. */\n createFetch(providerId: string): typeof fetch;\n /** Open a relay channel so a backend server can make LLM calls through this session. */\n createRelay(wsUrl: string): RelayConnection;\n /** Disconnect this session from the wallet. */\n disconnect(): void;\n /** Check if this session is still connected and valid. */\n isConnected(): Promise<boolean>;\n /** Get token usage stats for this session only. */\n getUsage(): Promise<SessionUsage>;\n /** Register a callback for when the wallet revokes this session. */\n onDisconnect(callback: () => void): () => void;\n}\n\nexport interface ByokyOptions {\n timeout?: number;\n}\n\nexport class Byoky {\n private timeout: number;\n\n constructor(options: ByokyOptions = {}) {\n this.timeout = options.timeout ?? 60_000;\n }\n\n async connect(request: ConnectRequest = {}): Promise<ByokySession> {\n if (typeof window === 'undefined') {\n throw new ByokyError(\n ByokyErrorCode.UNKNOWN,\n 'Byoky SDK requires a browser environment. On the server, use your API key directly.',\n );\n }\n\n if (!isExtensionInstalled()) {\n const storeUrl = getStoreUrl();\n if (storeUrl) {\n window.open(storeUrl, '_blank');\n }\n throw ByokyError.walletNotInstalled();\n }\n\n const response = await this.sendConnectRequest(request);\n return this.buildSession(response);\n }\n\n /**\n * Reconnect to an existing session using previously stored response data.\n * Returns null if the session is no longer valid.\n */\n async reconnect(savedResponse: ConnectResponse): Promise<ByokySession | null> {\n if (typeof window === 'undefined') return null;\n if (!isExtensionInstalled()) return null;\n\n const connected = await this.querySessionStatus(savedResponse.sessionKey);\n if (!connected) return null;\n\n return this.buildSession(savedResponse);\n }\n\n private buildSession(response: ConnectResponse): ByokySession {\n const sessionKey = response.sessionKey;\n const disconnectCallbacks = new Set<() => void>();\n\n function handleRevocation(event: Event) {\n const msg = (event as CustomEvent).detail;\n if (msg?.type === 'BYOKY_SESSION_REVOKED' && msg.payload?.sessionKey === sessionKey) {\n for (const cb of disconnectCallbacks) cb();\n disconnectCallbacks.clear();\n document.removeEventListener('byoky-message', handleRevocation);\n }\n }\n document.addEventListener('byoky-message', handleRevocation);\n\n return {\n ...response,\n createFetch: (providerId: string) =>\n createProxyFetch(providerId, sessionKey),\n createRelay: (wsUrl: string) =>\n createRelayClient(wsUrl, sessionKey, response.providers),\n disconnect: () => {\n document.removeEventListener('byoky-message', handleRevocation);\n this.sendDisconnect(sessionKey);\n },\n isConnected: () => this.querySessionStatus(sessionKey),\n getUsage: () => this.querySessionUsage(sessionKey),\n onDisconnect: (callback: () => void) => {\n disconnectCallbacks.add(callback);\n return () => { disconnectCallbacks.delete(callback); };\n },\n };\n }\n\n private sendConnectRequest(\n request: ConnectRequest,\n ): Promise<ConnectResponse> {\n return new Promise<ConnectResponse>((resolve, reject) => {\n const requestId = crypto.randomUUID();\n\n const timeoutId = setTimeout(() => {\n cleanup();\n reject(\n new ByokyError(ByokyErrorCode.UNKNOWN, 'Connection request timed out'),\n );\n }, this.timeout);\n\n function handleEvent(event: Event) {\n const msg = (event as CustomEvent).detail;\n if (typeof msg?.type !== 'string' || !msg.type.startsWith('BYOKY_')) return;\n if (msg.requestId !== requestId) return;\n\n cleanup();\n\n if (msg.type === 'BYOKY_CONNECT_RESPONSE') {\n resolve(msg.payload as ConnectResponse);\n } else if (msg.type === 'BYOKY_ERROR') {\n const { code, message } = msg.payload as {\n code: string;\n message: string;\n };\n reject(new ByokyError(code as ByokyErrorCode, message));\n }\n }\n\n function cleanup() {\n clearTimeout(timeoutId);\n document.removeEventListener('byoky-message', handleEvent);\n }\n\n document.addEventListener('byoky-message', handleEvent);\n\n window.postMessage(\n {\n type: 'BYOKY_CONNECT_REQUEST',\n id: requestId,\n requestId,\n payload: request,\n },\n '*',\n );\n });\n }\n\n private sendDisconnect(sessionKey: string): void {\n window.postMessage(\n { type: 'BYOKY_DISCONNECT', payload: { sessionKey } },\n '*',\n );\n }\n\n private querySessionStatus(sessionKey: string): Promise<boolean> {\n return new Promise((resolve) => {\n const requestId = crypto.randomUUID();\n const timeout = setTimeout(() => { cleanup(); resolve(false); }, 5000);\n\n function handleEvent(event: Event) {\n const msg = (event as CustomEvent).detail;\n if (msg?.requestId !== requestId) return;\n if (msg.type === 'BYOKY_SESSION_STATUS_RESPONSE') {\n cleanup();\n resolve(!!msg.payload?.connected);\n }\n }\n\n function cleanup() {\n clearTimeout(timeout);\n document.removeEventListener('byoky-message', handleEvent);\n }\n\n document.addEventListener('byoky-message', handleEvent);\n window.postMessage({\n type: 'BYOKY_SESSION_STATUS',\n requestId,\n payload: { sessionKey },\n }, '*');\n });\n }\n\n private querySessionUsage(sessionKey: string): Promise<SessionUsage> {\n return new Promise((resolve, reject) => {\n const requestId = crypto.randomUUID();\n const timeout = setTimeout(() => {\n cleanup();\n reject(new Error('Usage query timed out'));\n }, 5000);\n\n function handleEvent(event: Event) {\n const msg = (event as CustomEvent).detail;\n if (msg?.requestId !== requestId) return;\n if (msg.type === 'BYOKY_SESSION_USAGE_RESPONSE') {\n cleanup();\n if (msg.payload) {\n resolve(msg.payload as SessionUsage);\n } else {\n reject(new Error('Session not found'));\n }\n }\n }\n\n function cleanup() {\n clearTimeout(timeout);\n document.removeEventListener('byoky-message', handleEvent);\n }\n\n document.addEventListener('byoky-message', handleEvent);\n window.postMessage({\n type: 'BYOKY_SESSION_USAGE',\n requestId,\n payload: { sessionKey },\n }, '*');\n });\n }\n}\n","import { BYOKY_PROVIDER_KEY } from '@byoky/core';\n\nexport function isExtensionInstalled(): boolean {\n return typeof window !== 'undefined' && BYOKY_PROVIDER_KEY in window;\n}\n\nexport function getStoreUrl(): string | null {\n if (typeof navigator === 'undefined') return null;\n\n const ua = navigator.userAgent.toLowerCase();\n\n if (ua.includes('chrome') && !ua.includes('edg')) {\n return 'https://chrome.google.com/webstore/detail/byoky/TODO_EXTENSION_ID';\n }\n if (ua.includes('firefox')) {\n return 'https://addons.mozilla.org/en-US/firefox/addon/byoky/';\n }\n if (ua.includes('safari') && !ua.includes('chrome')) {\n return 'https://apps.apple.com/app/byoky/TODO_APP_ID';\n }\n return null;\n}\n","export function createProxyFetch(\n providerId: string,\n sessionKey: string,\n): typeof fetch {\n return async (\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> => {\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.toString()\n : input.url;\n\n const method = init?.method ?? 'GET';\n const headers = init?.headers\n ? Object.fromEntries(new Headers(init.headers).entries())\n : {};\n const body = init?.body ? await readBody(init.body) : undefined;\n\n const requestId = crypto.randomUUID();\n\n return new Promise<Response>((resolve, reject) => {\n const { readable, writable } = new TransformStream<Uint8Array>();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n let resolved = false;\n\n const timeout = setTimeout(() => {\n cleanup();\n reject(new Error('Proxy request timed out'));\n }, 120_000);\n\n function handleEvent(event: Event) {\n const data = (event as CustomEvent).detail ?? (event as MessageEvent).data;\n if (data?.requestId !== requestId) return;\n\n switch (data.type) {\n case 'BYOKY_PROXY_RESPONSE_META':\n if (!resolved) {\n resolved = true;\n clearTimeout(timeout);\n resolve(\n new Response(readable, {\n status: data.status,\n statusText: data.statusText,\n headers: new Headers(data.headers),\n }),\n );\n }\n break;\n\n case 'BYOKY_PROXY_RESPONSE_CHUNK':\n writer.write(encoder.encode(data.chunk)).catch(() => {});\n break;\n\n case 'BYOKY_PROXY_RESPONSE_DONE':\n writer.close().catch(() => {});\n cleanup();\n break;\n\n case 'BYOKY_PROXY_RESPONSE_ERROR': {\n const errResponse = new Response(\n JSON.stringify({ error: data.error }),\n {\n status: data.status || 500,\n headers: { 'content-type': 'application/json' },\n },\n );\n if (!resolved) {\n resolved = true;\n clearTimeout(timeout);\n resolve(errResponse);\n }\n writer.close().catch(() => {});\n cleanup();\n break;\n }\n }\n }\n\n function cleanup() {\n clearTimeout(timeout);\n document.removeEventListener('byoky-message', handleEvent);\n }\n\n document.addEventListener('byoky-message', handleEvent);\n\n window.postMessage(\n {\n type: 'BYOKY_PROXY_REQUEST',\n requestId,\n sessionKey,\n providerId,\n url,\n method,\n headers,\n body,\n },\n '*',\n );\n });\n };\n}\n\nasync function readBody(body: BodyInit): Promise<string | undefined> {\n if (typeof body === 'string') return body;\n if (body instanceof ArrayBuffer) return new TextDecoder().decode(body);\n if (body instanceof Blob) return body.text();\n if (body instanceof URLSearchParams) return body.toString();\n if (body instanceof ReadableStream) {\n const reader = body.getReader();\n const chunks: Uint8Array[] = [];\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n }\n const total = chunks.reduce((acc, c) => acc + c.length, 0);\n const combined = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n return new TextDecoder().decode(combined);\n }\n return undefined;\n}\n","import type {\n AuthMethod,\n RelayMessage,\n RelayRequest,\n} from '@byoky/core';\nimport {\n ByokyError,\n parseRelayMessage,\n WS_READY_STATE,\n} from '@byoky/core';\nimport { createProxyFetch } from './proxy-fetch.js';\n\nexport interface RelayConnection {\n readonly status: 'connecting' | 'connected' | 'disconnected';\n close(): void;\n onClose(callback: (reason?: string) => void): () => void;\n}\n\nexport function createRelayClient(\n wsUrl: string,\n sessionKey: string,\n providers: Record<string, { available: boolean; authMethod: AuthMethod }>,\n): RelayConnection {\n let status: 'connecting' | 'connected' | 'disconnected' = 'connecting';\n const closeCallbacks = new Set<(reason?: string) => void>();\n const inFlight = new Map<string, AbortController>();\n\n let ws: WebSocket;\n let pingInterval: ReturnType<typeof setInterval> | undefined;\n\n try {\n const parsed = new URL(wsUrl);\n const isSecure = parsed.protocol === 'wss:';\n const isLocalWs = parsed.protocol === 'ws:' &&\n (parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1');\n if (!isSecure && !isLocalWs) {\n status = 'disconnected';\n return {\n get status() { return status; },\n close() {},\n onClose(cb) { cb('Insecure WebSocket URL rejected — use wss:// for non-localhost connections'); return () => {}; },\n };\n }\n } catch {\n status = 'disconnected';\n return {\n get status() { return status; },\n close() {},\n onClose(cb) { cb('Invalid WebSocket URL'); return () => {}; },\n };\n }\n\n try {\n ws = new WebSocket(wsUrl);\n } catch {\n status = 'disconnected';\n return {\n get status() { return status; },\n close() {},\n onClose(cb) { cb('Failed to create WebSocket'); return () => {}; },\n };\n }\n\n ws.onopen = () => {\n status = 'connected';\n\n // Send hello with a relay-specific ID (never expose the real session key to the relay server)\n const relayId = `relay_${crypto.randomUUID().replace(/-/g, '')}`;\n ws.send(JSON.stringify({\n type: 'relay:hello',\n sessionId: relayId,\n providers,\n }));\n\n // Keepalive ping every 30s\n pingInterval = setInterval(() => {\n if (ws.readyState === WS_READY_STATE.OPEN) {\n ws.send(JSON.stringify({ type: 'relay:ping', ts: Date.now() }));\n }\n }, 30_000);\n };\n\n ws.onmessage = (event) => {\n const msg = parseRelayMessage(event.data);\n if (!msg) return;\n\n switch (msg.type) {\n case 'relay:request':\n handleRequest(msg);\n break;\n case 'relay:ping':\n ws.send(JSON.stringify({ type: 'relay:pong', ts: msg.ts }));\n break;\n case 'relay:pong':\n // Keepalive response, nothing to do\n break;\n }\n };\n\n ws.onclose = (event) => {\n cleanup(event.reason || 'WebSocket closed');\n };\n\n ws.onerror = () => {\n cleanup('WebSocket error');\n };\n\n async function handleRequest(req: RelayRequest) {\n const { requestId, providerId, url, method, headers, body } = req;\n const controller = new AbortController();\n inFlight.set(requestId, controller);\n\n try {\n const proxyFetch = createProxyFetch(providerId, sessionKey);\n const response = await proxyFetch(url, {\n method,\n headers,\n body,\n signal: controller.signal,\n });\n\n // Send response metadata\n const responseHeaders: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n responseHeaders[key] = value;\n });\n\n send({\n type: 'relay:response:meta',\n requestId,\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n });\n\n // Stream the body\n if (response.body) {\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (controller.signal.aborted) break;\n\n send({\n type: 'relay:response:chunk',\n requestId,\n chunk: decoder.decode(value, { stream: true }),\n });\n }\n }\n\n send({ type: 'relay:response:done', requestId });\n } catch (err) {\n send({\n type: 'relay:response:error',\n requestId,\n error: {\n code: err instanceof ByokyError ? err.code : 'PROXY_ERROR',\n message: err instanceof Error ? err.message : 'Unknown error',\n },\n });\n } finally {\n inFlight.delete(requestId);\n }\n }\n\n function send(msg: RelayMessage) {\n if (ws.readyState === WS_READY_STATE.OPEN) {\n ws.send(JSON.stringify(msg));\n }\n }\n\n function cleanup(reason?: string) {\n if (status === 'disconnected') return;\n status = 'disconnected';\n\n if (pingInterval) clearInterval(pingInterval);\n\n // Abort all in-flight requests\n for (const controller of inFlight.values()) {\n controller.abort();\n }\n inFlight.clear();\n\n for (const cb of closeCallbacks) cb(reason);\n closeCallbacks.clear();\n }\n\n return {\n get status() { return status; },\n close() {\n if (ws.readyState === WS_READY_STATE.OPEN || ws.readyState === WS_READY_STATE.CONNECTING) {\n ws.close(1000, 'Client closed');\n }\n cleanup('Client closed');\n },\n onClose(callback: (reason?: string) => void) {\n closeCallbacks.add(callback);\n return () => { closeCallbacks.delete(callback); };\n },\n };\n}\n"],"mappings":"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","names":["import_core","import_core","import_core"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
|
-
import { ConnectRequest, ConnectResponse } from '@byoky/core';
|
|
2
|
-
export { ByokyError, ByokyErrorCode, ConnectRequest, ConnectResponse, ProviderRequirement } from '@byoky/core';
|
|
1
|
+
import { ConnectRequest, ConnectResponse, SessionUsage } from '@byoky/core';
|
|
2
|
+
export { ByokyError, ByokyErrorCode, ConnectRequest, ConnectResponse, ProviderRequirement, SessionUsage } from '@byoky/core';
|
|
3
|
+
|
|
4
|
+
interface RelayConnection {
|
|
5
|
+
readonly status: 'connecting' | 'connected' | 'disconnected';
|
|
6
|
+
close(): void;
|
|
7
|
+
onClose(callback: (reason?: string) => void): () => void;
|
|
8
|
+
}
|
|
3
9
|
|
|
4
10
|
interface ByokySession extends ConnectResponse {
|
|
11
|
+
/** Create a fetch function that proxies requests through the wallet for the given provider. */
|
|
5
12
|
createFetch(providerId: string): typeof fetch;
|
|
13
|
+
/** Open a relay channel so a backend server can make LLM calls through this session. */
|
|
14
|
+
createRelay(wsUrl: string): RelayConnection;
|
|
15
|
+
/** Disconnect this session from the wallet. */
|
|
6
16
|
disconnect(): void;
|
|
17
|
+
/** Check if this session is still connected and valid. */
|
|
18
|
+
isConnected(): Promise<boolean>;
|
|
19
|
+
/** Get token usage stats for this session only. */
|
|
20
|
+
getUsage(): Promise<SessionUsage>;
|
|
21
|
+
/** Register a callback for when the wallet revokes this session. */
|
|
22
|
+
onDisconnect(callback: () => void): () => void;
|
|
7
23
|
}
|
|
8
24
|
interface ByokyOptions {
|
|
9
25
|
timeout?: number;
|
|
@@ -12,8 +28,16 @@ declare class Byoky {
|
|
|
12
28
|
private timeout;
|
|
13
29
|
constructor(options?: ByokyOptions);
|
|
14
30
|
connect(request?: ConnectRequest): Promise<ByokySession>;
|
|
31
|
+
/**
|
|
32
|
+
* Reconnect to an existing session using previously stored response data.
|
|
33
|
+
* Returns null if the session is no longer valid.
|
|
34
|
+
*/
|
|
35
|
+
reconnect(savedResponse: ConnectResponse): Promise<ByokySession | null>;
|
|
36
|
+
private buildSession;
|
|
15
37
|
private sendConnectRequest;
|
|
16
|
-
private
|
|
38
|
+
private sendDisconnect;
|
|
39
|
+
private querySessionStatus;
|
|
40
|
+
private querySessionUsage;
|
|
17
41
|
}
|
|
18
42
|
|
|
19
43
|
declare function isExtensionInstalled(): boolean;
|
|
@@ -21,4 +45,4 @@ declare function getStoreUrl(): string | null;
|
|
|
21
45
|
|
|
22
46
|
declare function createProxyFetch(providerId: string, sessionKey: string): typeof fetch;
|
|
23
47
|
|
|
24
|
-
export { Byoky, type ByokyOptions, type ByokySession, createProxyFetch, getStoreUrl, isExtensionInstalled };
|
|
48
|
+
export { Byoky, type ByokyOptions, type ByokySession, type RelayConnection, createProxyFetch, getStoreUrl, isExtensionInstalled };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
|
-
import { ConnectRequest, ConnectResponse } from '@byoky/core';
|
|
2
|
-
export { ByokyError, ByokyErrorCode, ConnectRequest, ConnectResponse, ProviderRequirement } from '@byoky/core';
|
|
1
|
+
import { ConnectRequest, ConnectResponse, SessionUsage } from '@byoky/core';
|
|
2
|
+
export { ByokyError, ByokyErrorCode, ConnectRequest, ConnectResponse, ProviderRequirement, SessionUsage } from '@byoky/core';
|
|
3
|
+
|
|
4
|
+
interface RelayConnection {
|
|
5
|
+
readonly status: 'connecting' | 'connected' | 'disconnected';
|
|
6
|
+
close(): void;
|
|
7
|
+
onClose(callback: (reason?: string) => void): () => void;
|
|
8
|
+
}
|
|
3
9
|
|
|
4
10
|
interface ByokySession extends ConnectResponse {
|
|
11
|
+
/** Create a fetch function that proxies requests through the wallet for the given provider. */
|
|
5
12
|
createFetch(providerId: string): typeof fetch;
|
|
13
|
+
/** Open a relay channel so a backend server can make LLM calls through this session. */
|
|
14
|
+
createRelay(wsUrl: string): RelayConnection;
|
|
15
|
+
/** Disconnect this session from the wallet. */
|
|
6
16
|
disconnect(): void;
|
|
17
|
+
/** Check if this session is still connected and valid. */
|
|
18
|
+
isConnected(): Promise<boolean>;
|
|
19
|
+
/** Get token usage stats for this session only. */
|
|
20
|
+
getUsage(): Promise<SessionUsage>;
|
|
21
|
+
/** Register a callback for when the wallet revokes this session. */
|
|
22
|
+
onDisconnect(callback: () => void): () => void;
|
|
7
23
|
}
|
|
8
24
|
interface ByokyOptions {
|
|
9
25
|
timeout?: number;
|
|
@@ -12,8 +28,16 @@ declare class Byoky {
|
|
|
12
28
|
private timeout;
|
|
13
29
|
constructor(options?: ByokyOptions);
|
|
14
30
|
connect(request?: ConnectRequest): Promise<ByokySession>;
|
|
31
|
+
/**
|
|
32
|
+
* Reconnect to an existing session using previously stored response data.
|
|
33
|
+
* Returns null if the session is no longer valid.
|
|
34
|
+
*/
|
|
35
|
+
reconnect(savedResponse: ConnectResponse): Promise<ByokySession | null>;
|
|
36
|
+
private buildSession;
|
|
15
37
|
private sendConnectRequest;
|
|
16
|
-
private
|
|
38
|
+
private sendDisconnect;
|
|
39
|
+
private querySessionStatus;
|
|
40
|
+
private querySessionUsage;
|
|
17
41
|
}
|
|
18
42
|
|
|
19
43
|
declare function isExtensionInstalled(): boolean;
|
|
@@ -21,4 +45,4 @@ declare function getStoreUrl(): string | null;
|
|
|
21
45
|
|
|
22
46
|
declare function createProxyFetch(providerId: string, sessionKey: string): typeof fetch;
|
|
23
47
|
|
|
24
|
-
export { Byoky, type ByokyOptions, type ByokySession, createProxyFetch, getStoreUrl, isExtensionInstalled };
|
|
48
|
+
export { Byoky, type ByokyOptions, type ByokySession, type RelayConnection, createProxyFetch, getStoreUrl, isExtensionInstalled };
|