libfx 0.0.13 → 0.0.14-dev.1561.g1a070af32cdf
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 +454 -486
- package/browser.js +18 -4
- package/chatgpt-sign-in.js +141 -0
- package/chatgpt.js +69 -0
- package/custom-provider.js +123 -0
- package/durable/local.mjs +190 -0
- package/durable/vercel.mjs +268 -0
- package/durable/world.mjs +1 -0
- package/durable.js +2752 -0
- package/fetch-cleanup.js +12 -0
- package/fx-core.wasm +0 -0
- package/fx-harness.js +220 -0
- package/fx-sdk.js +1302 -116
- package/fx-term.wasm +0 -0
- package/grok-sign-in.js +145 -0
- package/grok.js +108 -0
- package/libfx.darwin-arm64.node +0 -0
- package/libfx.darwin-x64.node +0 -0
- package/libfx.linux-arm64.node +0 -0
- package/libfx.linux-x64.node +0 -0
- package/mcp.js +14 -2
- package/node.cjs +4508 -213
- package/node.js +195 -47
- package/oauth.js +64 -0
- package/package.json +66 -13
- package/subscription.js +253 -0
- package/tool-observation.js +185 -0
- package/types/browser.d.ts +146 -0
- package/types/chatgpt.d.ts +58 -0
- package/types/durable-local.d.ts +18 -0
- package/types/durable-vercel.d.ts +22 -0
- package/types/durable-world.d.ts +95 -0
- package/types/grok.d.ts +61 -0
- package/types/libfx.d.cts +1061 -0
- package/types/mcp.d.ts +46 -0
- package/types/node.d.cts +165 -0
- package/types/node.d.ts +3 -0
- package/types/skills-node.d.ts +25 -0
- package/types/skills.d.ts +28 -0
- package/types/wasm.d.ts +106 -0
package/node.js
CHANGED
|
@@ -2,14 +2,20 @@ import { access, readFile } from "node:fs/promises";
|
|
|
2
2
|
import { closeSync } from "node:fs";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { Socket } from "node:net";
|
|
5
|
-
import { homedir } from "node:os";
|
|
5
|
+
import { homedir, tmpdir } from "node:os";
|
|
6
6
|
import { isAbsolute, resolve } from "node:path";
|
|
7
7
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
8
8
|
import { CoreOutput } from "./core-output.js";
|
|
9
9
|
import { loadModule, withModuleFailure } from "./wasm-module.js";
|
|
10
|
+
import { fetchCleanupAction, cleanupTimeoutMs, cleanupByteLimit } from "./fetch-cleanup.js";
|
|
11
|
+
import { createDurableAgentFactory, memory, wakeHandler } from "./durable.js";
|
|
12
|
+
import { fxHarness } from "./fx-harness.js";
|
|
10
13
|
import {
|
|
11
|
-
|
|
14
|
+
createFxEngine as createWasmAgent,
|
|
12
15
|
createFxTerminal as createWasmTerminal,
|
|
16
|
+
createMemoryPersistence,
|
|
17
|
+
FxFencedError,
|
|
18
|
+
FxJournalVersionError,
|
|
13
19
|
encodeXtermKeyEvent,
|
|
14
20
|
fxSdkApiVersion,
|
|
15
21
|
listModels,
|
|
@@ -18,8 +24,8 @@ import {
|
|
|
18
24
|
xtermAdapter,
|
|
19
25
|
} from "./fx-sdk.js";
|
|
20
26
|
|
|
21
|
-
export { encodeXtermKeyEvent, fxSdkApiVersion, listModels, supportsJspi, xtermAdapter };
|
|
22
|
-
export const libfxApiVersion =
|
|
27
|
+
export { createMemoryPersistence, encodeXtermKeyEvent, FxFencedError, FxJournalVersionError, fxSdkApiVersion, listModels, memory, supportsJspi, wakeHandler, xtermAdapter };
|
|
28
|
+
export const libfxApiVersion = 3;
|
|
23
29
|
const nativeCoreApiVersion = 4;
|
|
24
30
|
|
|
25
31
|
const fetchOperationStale = 0;
|
|
@@ -346,14 +352,22 @@ export async function getBackendInfo(value = {}) {
|
|
|
346
352
|
}
|
|
347
353
|
|
|
348
354
|
function createNativeCoreRuntime(addon, options) {
|
|
349
|
-
const { apiKey, model, effort, fast, ultrafast, gatewayChatUrl } = options;
|
|
355
|
+
const { apiKey, providerName: provider, customProvider, model, effort, fast, ultrafast, gatewayChatUrl } = options;
|
|
350
356
|
if (ultrafast !== undefined && addon.supportsUltrafast !== true) {
|
|
351
357
|
const error = new Error("native addon does not support the ultrafast option");
|
|
352
358
|
error.code = "LIBFX_NATIVE_CAPABILITY_UNAVAILABLE";
|
|
353
359
|
throw error;
|
|
354
360
|
}
|
|
361
|
+
const capability = customProvider ? "supportsConfiguredProvider" : { codex: "supportsCodex", grok: "supportsGrok" }[provider];
|
|
362
|
+
if (capability && addon[capability] !== true) {
|
|
363
|
+
const error = new Error(`native addon does not support the ${provider} provider`);
|
|
364
|
+
error.code = "LIBFX_NATIVE_CAPABILITY_UNAVAILABLE";
|
|
365
|
+
throw error;
|
|
366
|
+
}
|
|
355
367
|
const core = addon.createCore({
|
|
356
|
-
apiKey,
|
|
368
|
+
...(apiKey === undefined ? {} : { apiKey }),
|
|
369
|
+
...(provider === undefined ? {} : { provider }),
|
|
370
|
+
...(customProvider === undefined ? {} : { configuredProviders: customProvider.json }),
|
|
357
371
|
home: options.home ?? homedir(),
|
|
358
372
|
workspaceRoot: options.workspaceRoot ?? process.cwd(),
|
|
359
373
|
...(model === undefined ? {} : { model }),
|
|
@@ -383,76 +397,154 @@ function createNativeCoreRuntime(addon, options) {
|
|
|
383
397
|
let settled = false;
|
|
384
398
|
let fetchState = null;
|
|
385
399
|
const exited = new Promise((resolve) => { exitedResolve = resolve; });
|
|
400
|
+
function refreshFetch(state) {
|
|
401
|
+
if (settled) return;
|
|
402
|
+
if (typeof addon.coreFetchDisposition !== "function") {
|
|
403
|
+
state.active = addon.coreFetchActive(core, state.handle);
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
// One native lock observes consumption and retirement together.
|
|
407
|
+
const disposition = addon.coreFetchDisposition(core, state.handle);
|
|
408
|
+
if (disposition !== 0 && disposition !== 1 && disposition !== 2) {
|
|
409
|
+
throw new Error(`invalid native fetch disposition ${disposition}`);
|
|
410
|
+
}
|
|
411
|
+
state.consumed ||= disposition === 2;
|
|
412
|
+
state.active = disposition === 1;
|
|
413
|
+
}
|
|
414
|
+
function cleanupAction(state) {
|
|
415
|
+
return fetchCleanupAction({ ...state, alive: !settled,
|
|
416
|
+
age: state.drainStarted === null ? 0 : performance.now() - state.drainStarted });
|
|
417
|
+
}
|
|
418
|
+
function beginDrain(state) {
|
|
419
|
+
if (state.drainStarted !== null) return;
|
|
420
|
+
state.drainStarted = performance.now();
|
|
421
|
+
state.timer = setTimeout(() => state.controller.abort(), cleanupTimeoutMs);
|
|
422
|
+
}
|
|
386
423
|
const abortHostEffects = () => {
|
|
387
|
-
fetchState
|
|
388
|
-
|
|
424
|
+
if (fetchState) {
|
|
425
|
+
fetchState.canceled = true;
|
|
426
|
+
fetchState.controller.abort();
|
|
427
|
+
}
|
|
428
|
+
if (!settled) { try { addon.abortCoreFetch(core); } catch {} }
|
|
389
429
|
};
|
|
390
430
|
const finish = (code, error) => {
|
|
391
431
|
if (settled) return;
|
|
432
|
+
const state = fetchState;
|
|
433
|
+
if (state) {
|
|
434
|
+
refreshFetch(state);
|
|
435
|
+
state.closing = true;
|
|
436
|
+
state.failed ||= code !== 0 || error !== undefined;
|
|
437
|
+
}
|
|
392
438
|
settled = true;
|
|
393
439
|
outputError = error;
|
|
394
440
|
output.close();
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
441
|
+
const destroy = () => {
|
|
442
|
+
try { addon.destroyCore(core); } catch {}
|
|
443
|
+
readySocket.destroy();
|
|
444
|
+
void readyClosed.then(() => exitedResolve(code));
|
|
445
|
+
};
|
|
446
|
+
if (state && cleanupAction(state) === "drain") {
|
|
447
|
+
beginDrain(state);
|
|
448
|
+
void state.done.then(destroy);
|
|
449
|
+
} else {
|
|
450
|
+
if (state && cleanupAction(state) !== "done") state.controller.abort();
|
|
451
|
+
destroy();
|
|
452
|
+
}
|
|
399
453
|
};
|
|
400
454
|
const pumpFetch = async (request, body) => {
|
|
401
455
|
const controller = new AbortController();
|
|
402
|
-
|
|
456
|
+
let complete, reader;
|
|
457
|
+
const state = { handle: request.handle, controller, consumed: false, active: true,
|
|
458
|
+
canceled: false, failed: false, closing: false, eof: false, discarded: 0,
|
|
459
|
+
drainStarted: null, timer: null, done: new Promise(resolve => { complete = resolve; }) };
|
|
403
460
|
fetchState = state;
|
|
461
|
+
let onAbort;
|
|
462
|
+
const aborted = new Promise((_, reject) => {
|
|
463
|
+
onAbort = () => reject(new DOMException("Aborted", "AbortError"));
|
|
464
|
+
controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
465
|
+
});
|
|
466
|
+
void aborted.catch(() => {});
|
|
467
|
+
const wait = task => Promise.race([task, aborted]);
|
|
404
468
|
try {
|
|
405
|
-
const
|
|
469
|
+
const responseTask = Promise.resolve().then(() => (options.fetch ?? globalThis.fetch)(request.url, {
|
|
406
470
|
method: request.method,
|
|
407
471
|
headers: new Headers(JSON.parse(request.headers).map(({ name, value }) => [name, value])),
|
|
408
472
|
body: body.length ? body : undefined,
|
|
409
473
|
signal: controller.signal,
|
|
474
|
+
})).then(response => {
|
|
475
|
+
if (controller.signal.aborted) {
|
|
476
|
+
void response.body?.cancel().catch(() => {});
|
|
477
|
+
throw new DOMException("Aborted", "AbortError");
|
|
478
|
+
}
|
|
479
|
+
return response;
|
|
410
480
|
});
|
|
411
|
-
const
|
|
412
|
-
if (
|
|
413
|
-
|
|
414
|
-
if (
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
481
|
+
const response = await wait(responseTask);
|
|
482
|
+
if (settled && !state.consumed) throw new DOMException("Aborted", "AbortError");
|
|
483
|
+
const started = settled ? fetchOperationStale : addon.startCoreFetchResponse(core, state.handle, response.status);
|
|
484
|
+
if (started !== fetchOperationApplied && started !== fetchOperationStale) throw new Error(`invalid native fetch start result ${started}`);
|
|
485
|
+
state.active = started === fetchOperationApplied;
|
|
486
|
+
reader = response.body?.getReader();
|
|
487
|
+
while (reader) {
|
|
488
|
+
refreshFetch(state);
|
|
489
|
+
const action = cleanupAction(state);
|
|
490
|
+
if (action === "abort") { controller.abort(); throw new DOMException("Aborted", "AbortError"); }
|
|
491
|
+
if (action === "drain") beginDrain(state);
|
|
492
|
+
const { done, value } = await wait(reader.read());
|
|
493
|
+
if (done) break;
|
|
494
|
+
const buffer = Buffer.from(value);
|
|
495
|
+
options.onTransportChunk?.(buffer.length);
|
|
496
|
+
let offset = 0;
|
|
497
|
+
while (offset < buffer.length) {
|
|
498
|
+
refreshFetch(state);
|
|
499
|
+
const next = cleanupAction(state);
|
|
500
|
+
if (next === "drain") {
|
|
501
|
+
beginDrain(state);
|
|
502
|
+
state.discarded += buffer.length - offset;
|
|
503
|
+
if (state.discarded >= cleanupByteLimit) controller.abort();
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
if (next !== "forward") { controller.abort(); throw new DOMException("Aborted", "AbortError"); }
|
|
507
|
+
const end = Math.min(offset + 64 * 1024, buffer.length);
|
|
508
|
+
const pushed = addon.pushCoreFetchResponse(core, state.handle, buffer.subarray(offset, end));
|
|
509
|
+
if (pushed === fetchOperationApplied) { offset = end; continue; }
|
|
510
|
+
if (pushed === fetchOperationStale) {
|
|
511
|
+
refreshFetch(state);
|
|
512
|
+
if (cleanupAction(state) === "drain") continue;
|
|
513
|
+
controller.abort(); throw new DOMException("Aborted", "AbortError");
|
|
429
514
|
}
|
|
515
|
+
if (pushed !== fetchOperationBackpressure) throw new Error(`invalid native fetch push result ${pushed}`);
|
|
516
|
+
await wait(new Promise(resolve => setTimeout(resolve, 2)));
|
|
430
517
|
}
|
|
431
518
|
}
|
|
432
|
-
|
|
433
|
-
if (
|
|
434
|
-
|
|
519
|
+
state.eof = true;
|
|
520
|
+
if (!settled && state.active) {
|
|
521
|
+
const finished = addon.finishCoreFetch(core, state.handle);
|
|
522
|
+
if (finished !== fetchOperationApplied && finished !== fetchOperationStale) throw new Error(`invalid native fetch finish result ${finished}`);
|
|
435
523
|
}
|
|
436
524
|
} catch (error) {
|
|
437
|
-
if (
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
} catch {}
|
|
525
|
+
if (!settled && !controller.signal.aborted) {
|
|
526
|
+
state.failed = true;
|
|
527
|
+
try { if (addon.coreFetchActive(core, state.handle)) addon.failCoreFetch(core, state.handle); } catch {}
|
|
441
528
|
}
|
|
442
529
|
} finally {
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
530
|
+
clearTimeout(state.timer);
|
|
531
|
+
controller.signal.removeEventListener("abort", onAbort);
|
|
532
|
+
if (reader) {
|
|
533
|
+
if (!state.eof) { try { void reader.cancel().catch(() => {}); } catch {} }
|
|
534
|
+
try { reader.releaseLock(); } catch {}
|
|
446
535
|
}
|
|
536
|
+
complete();
|
|
537
|
+
if (fetchState === state) { fetchState = null; queueMicrotask(drainReady); }
|
|
447
538
|
}
|
|
448
539
|
};
|
|
449
540
|
function drainReady() {
|
|
450
541
|
if (settled) return;
|
|
451
542
|
try {
|
|
452
543
|
if (fetchState) {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
544
|
+
refreshFetch(fetchState);
|
|
545
|
+
const action = cleanupAction(fetchState);
|
|
546
|
+
if (action === "drain") beginDrain(fetchState);
|
|
547
|
+
if (action === "abort") fetchState.controller.abort();
|
|
456
548
|
} else {
|
|
457
549
|
// The core hands over JSON metadata and the raw request body separately.
|
|
458
550
|
const fetchRequest = addon.takeCoreFetch(core);
|
|
@@ -546,7 +638,8 @@ async function createWithFallback(surface, nativeMethod, wasmFactory, defaultWas
|
|
|
546
638
|
} catch (error) {
|
|
547
639
|
nativeError = error;
|
|
548
640
|
if (backend === "native" || error?.code === "LIBFX_MODEL_UNSUPPORTED_FAST" ||
|
|
549
|
-
error?.code === "LIBFX_MODEL_UNSUPPORTED_EFFORT"
|
|
641
|
+
error?.code === "LIBFX_MODEL_UNSUPPORTED_EFFORT" ||
|
|
642
|
+
typeof error?.code === "string" && error.code.startsWith("LIBFX_MODEL_METADATA_")) throw error;
|
|
550
643
|
}
|
|
551
644
|
}
|
|
552
645
|
if (backend === "native") {
|
|
@@ -564,7 +657,12 @@ async function createWithFallback(surface, nativeMethod, wasmFactory, defaultWas
|
|
|
564
657
|
return wasmFactory({ ...runtimeOptions, wasm: await wasmInput(wasmSource) });
|
|
565
658
|
}
|
|
566
659
|
|
|
567
|
-
|
|
660
|
+
/**
|
|
661
|
+
* One fx session on the native core, or on WebAssembly when the native
|
|
662
|
+
* addon is unavailable, with no durability of its own. `createFxAgent`
|
|
663
|
+
* runs durable sessions on these.
|
|
664
|
+
*/
|
|
665
|
+
export async function createFxEngine(options = {}) {
|
|
568
666
|
return createWithFallback(
|
|
569
667
|
"agent",
|
|
570
668
|
"createCore",
|
|
@@ -574,6 +672,56 @@ export async function createFxAgent(options = {}) {
|
|
|
574
672
|
);
|
|
575
673
|
}
|
|
576
674
|
|
|
675
|
+
// The durability a session gets when the caller names none: Vercel's World
|
|
676
|
+
// on Vercel, otherwise files in the temporary directory. Each is its own
|
|
677
|
+
// module, loaded only here: a bundler copies the file the URL names and
|
|
678
|
+
// leaves the import itself to Node.
|
|
679
|
+
// Agents created without a durability share one for each place sessions
|
|
680
|
+
// live, as agents passed one durability do, so they can send each other's
|
|
681
|
+
// prompts on.
|
|
682
|
+
const environments = new Map();
|
|
683
|
+
function environmentDurability() {
|
|
684
|
+
const dir = process.env.VERCEL ? null : process.env.FX_SESSIONS_DIR || resolve(tmpdir(), "libfx", "sessions");
|
|
685
|
+
const key = dir ?? "vercel";
|
|
686
|
+
if (!environments.has(key)) {
|
|
687
|
+
environments.set(key, chooseEnvironmentDurability(dir).catch((error) => {
|
|
688
|
+
environments.delete(key);
|
|
689
|
+
throw error;
|
|
690
|
+
}));
|
|
691
|
+
}
|
|
692
|
+
return environments.get(key);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
async function chooseEnvironmentDurability(dir) {
|
|
696
|
+
const load = (asset) => import(/* webpackIgnore: true */ /* turbopackIgnore: true */ (asset.protocol === "" ? bundledAssetUrl(asset) : asset).href);
|
|
697
|
+
if (dir === null) {
|
|
698
|
+
const { vercel } = await load(new URL("./durable/vercel.mjs", import.meta.url));
|
|
699
|
+
return vercel();
|
|
700
|
+
}
|
|
701
|
+
const { local } = await load(new URL("./durable/local.mjs", import.meta.url));
|
|
702
|
+
return local({ dir });
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* An agent whose sessions survive crashes, timeouts and redeployments. It
|
|
707
|
+
* does no I/O until a session is used.
|
|
708
|
+
*/
|
|
709
|
+
export const createFxAgent = createDurableAgentFactory({
|
|
710
|
+
name: "createFxAgent",
|
|
711
|
+
label: "fx agent",
|
|
712
|
+
defaultDurability: environmentDurability,
|
|
713
|
+
// One durability per environment, so agents created without one share it.
|
|
714
|
+
defaultShared: true,
|
|
715
|
+
harness: fxHarness({
|
|
716
|
+
createEngine: createFxEngine,
|
|
717
|
+
// An explicit key, then the durability's own credential, such as a
|
|
718
|
+
// Vercel deployment's OIDC token, then the one in the environment.
|
|
719
|
+
defaultApiKey: async (durability) => process.env.AI_GATEWAY_API_KEY ||
|
|
720
|
+
(await Promise.resolve(durability?.gatewayKey?.()).catch(() => undefined)) ||
|
|
721
|
+
process.env.VERCEL_OIDC_TOKEN || undefined,
|
|
722
|
+
}),
|
|
723
|
+
});
|
|
724
|
+
|
|
577
725
|
export function createFxTerminal(options = {}) {
|
|
578
726
|
return createWithFallback("terminal", "createFxTerminal", createWasmTerminal, defaultTermWasm, options);
|
|
579
727
|
}
|
package/oauth.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// Pieces the subscription sign-ins share: PKCE secrets, reading a pasted
|
|
2
|
+
// redirect address, and waiting between device code polls.
|
|
3
|
+
|
|
4
|
+
const maxAddressBytes = 16 * 1024;
|
|
5
|
+
const encoder = new TextEncoder();
|
|
6
|
+
|
|
7
|
+
export function signInError(code, message, fields = {}) {
|
|
8
|
+
const error = new Error(message);
|
|
9
|
+
error.code = code;
|
|
10
|
+
return Object.assign(error, fields);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const base64url = (bytes) => btoa(String.fromCharCode(...bytes)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
14
|
+
|
|
15
|
+
/** A random URL-safe secret, for a PKCE verifier or an OAuth state. */
|
|
16
|
+
export const randomSecret = () => base64url(crypto.getRandomValues(new Uint8Array(32)));
|
|
17
|
+
|
|
18
|
+
/** The S256 challenge for a PKCE verifier. */
|
|
19
|
+
export async function pkceChallenge(verifier) {
|
|
20
|
+
return base64url(new Uint8Array(await crypto.subtle.digest("SHA-256", encoder.encode(verifier))));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const hostFetch = (fetch) => fetch ?? globalThis.fetch?.bind(globalThis);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Reads the sign-in result from a pasted address: the whole address, its query,
|
|
27
|
+
* or either wrapped in quotes, backticks or angle brackets. `prefix` names the
|
|
28
|
+
* error codes, `issuer` the sign-in page, and `redirectUri` the address the
|
|
29
|
+
* user should have copied.
|
|
30
|
+
*/
|
|
31
|
+
export function readSignInAddress(text, { prefix, issuer, redirectUri }) {
|
|
32
|
+
if (typeof text !== "string" || encoder.encode(text).length > maxAddressBytes) {
|
|
33
|
+
throw signInError(`${prefix}_SIGN_IN_INVALID_ADDRESS`, `Paste the address of the page ${issuer} opened after you signed in.`);
|
|
34
|
+
}
|
|
35
|
+
const trimmed = text.trim().replace(/^[`'"<\s]+|[`'">\s]+$/g, "");
|
|
36
|
+
let params;
|
|
37
|
+
try {
|
|
38
|
+
params = new URL(trimmed).searchParams;
|
|
39
|
+
} catch {
|
|
40
|
+
params = new URLSearchParams(trimmed.includes("?") ? trimmed.slice(trimmed.indexOf("?") + 1) : trimmed);
|
|
41
|
+
}
|
|
42
|
+
const error = params.get("error");
|
|
43
|
+
if (error) {
|
|
44
|
+
throw signInError(`${prefix}_SIGN_IN_DENIED`, `${issuer} did not sign you in: ${params.get("error_description") || error}`, { reason: error });
|
|
45
|
+
}
|
|
46
|
+
const code = params.get("code");
|
|
47
|
+
if (!code) {
|
|
48
|
+
throw signInError(
|
|
49
|
+
`${prefix}_SIGN_IN_INVALID_ADDRESS`,
|
|
50
|
+
`That address has no sign-in code. Copy the whole address of the page ${issuer} opened after you signed in; it starts with ${redirectUri}?code=`,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
return { code, state: params.get("state") };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Resolves after `ms`, or rejects when `signal` aborts first. */
|
|
57
|
+
export function wait(ms, signal) {
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
if (signal?.aborted) return reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
60
|
+
const timer = setTimeout(() => { signal?.removeEventListener("abort", onAbort); resolve(); }, ms);
|
|
61
|
+
const onAbort = () => { clearTimeout(timer); reject(signal.reason ?? new DOMException("Aborted", "AbortError")); };
|
|
62
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
63
|
+
});
|
|
64
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "libfx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14-dev.1561.g1a070af32cdf",
|
|
4
4
|
"description": "Embed fx agents and terminals in JavaScript hosts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -18,21 +18,74 @@
|
|
|
18
18
|
"exports": {
|
|
19
19
|
".": {
|
|
20
20
|
"node": {
|
|
21
|
-
"import":
|
|
22
|
-
|
|
21
|
+
"import": {
|
|
22
|
+
"types": "./types/node.d.ts",
|
|
23
|
+
"default": "./node.js"
|
|
24
|
+
},
|
|
25
|
+
"require": {
|
|
26
|
+
"types": "./types/node.d.cts",
|
|
27
|
+
"default": "./node.cjs"
|
|
28
|
+
}
|
|
23
29
|
},
|
|
24
|
-
"browser":
|
|
25
|
-
|
|
30
|
+
"browser": {
|
|
31
|
+
"types": "./types/browser.d.ts",
|
|
32
|
+
"default": "./browser.js"
|
|
33
|
+
},
|
|
34
|
+
"default": {
|
|
35
|
+
"types": "./types/browser.d.ts",
|
|
36
|
+
"default": "./browser.js"
|
|
37
|
+
}
|
|
26
38
|
},
|
|
27
39
|
"./node": {
|
|
28
|
-
"import":
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
40
|
+
"import": {
|
|
41
|
+
"types": "./types/node.d.ts",
|
|
42
|
+
"default": "./node.js"
|
|
43
|
+
},
|
|
44
|
+
"require": {
|
|
45
|
+
"types": "./types/node.d.cts",
|
|
46
|
+
"default": "./node.cjs"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"./browser": {
|
|
50
|
+
"types": "./types/browser.d.ts",
|
|
51
|
+
"default": "./browser.js"
|
|
52
|
+
},
|
|
53
|
+
"./wasm": {
|
|
54
|
+
"types": "./types/wasm.d.ts",
|
|
55
|
+
"default": "./fx-sdk.js"
|
|
56
|
+
},
|
|
57
|
+
"./durable-local": {
|
|
58
|
+
"types": "./types/durable-local.d.ts",
|
|
59
|
+
"default": "./durable/local.mjs"
|
|
60
|
+
},
|
|
61
|
+
"./durable-vercel": {
|
|
62
|
+
"types": "./types/durable-vercel.d.ts",
|
|
63
|
+
"default": "./durable/vercel.mjs"
|
|
64
|
+
},
|
|
65
|
+
"./durable-world": {
|
|
66
|
+
"types": "./types/durable-world.d.ts",
|
|
67
|
+
"default": "./durable/world.mjs"
|
|
68
|
+
},
|
|
69
|
+
"./chatgpt": {
|
|
70
|
+
"types": "./types/chatgpt.d.ts",
|
|
71
|
+
"default": "./chatgpt-sign-in.js"
|
|
72
|
+
},
|
|
73
|
+
"./grok": {
|
|
74
|
+
"types": "./types/grok.d.ts",
|
|
75
|
+
"default": "./grok-sign-in.js"
|
|
76
|
+
},
|
|
77
|
+
"./mcp": {
|
|
78
|
+
"types": "./types/mcp.d.ts",
|
|
79
|
+
"default": "./mcp.js"
|
|
80
|
+
},
|
|
81
|
+
"./skills": {
|
|
82
|
+
"types": "./types/skills.d.ts",
|
|
83
|
+
"default": "./skills.js"
|
|
84
|
+
},
|
|
85
|
+
"./skills/node": {
|
|
86
|
+
"types": "./types/skills-node.d.ts",
|
|
87
|
+
"default": "./skills-node.js"
|
|
88
|
+
}
|
|
36
89
|
},
|
|
37
90
|
"engines": {
|
|
38
91
|
"node": ">=20"
|