lightnode-sdk 0.7.10 → 0.7.12

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/dist/auth.d.ts CHANGED
@@ -76,6 +76,7 @@ export interface SiweSession {
76
76
  */
77
77
  export declare function siweChallenge(network: NetworkId | NetworkConfig, address: `0x${string}`, opts?: {
78
78
  signal?: AbortSignal;
79
+ baseUrl?: string;
79
80
  }): Promise<SiweChallenge>;
80
81
  /**
81
82
  * Verify a signed SIWE message and mint a JWT bearer.
@@ -85,6 +86,7 @@ export declare function siweVerify(network: NetworkId | NetworkConfig, args: {
85
86
  signature: `0x${string}`;
86
87
  }, opts?: {
87
88
  signal?: AbortSignal;
89
+ baseUrl?: string;
88
90
  }): Promise<SiweVerifyResult>;
89
91
  /**
90
92
  * End-to-end SIWE sign-in: challenge -> sign -> verify -> JWT.
@@ -103,4 +105,5 @@ export declare function siweVerify(network: NetworkId | NetworkConfig, args: {
103
105
  export declare function siweSignIn(walletClient: SiweWalletClient, network: NetworkId | NetworkConfig, opts?: {
104
106
  address?: `0x${string}`;
105
107
  signal?: AbortSignal;
108
+ baseUrl?: string;
106
109
  }): Promise<SiweSession>;
package/dist/auth.js CHANGED
@@ -21,6 +21,26 @@
21
21
  * can refresh proactively.
22
22
  */
23
23
  import { NETWORKS } from "./networks.js";
24
+ // Same logic as gateway.ts: in a browser-like runtime the consumer-api's
25
+ // CORS allowlist excludes most origins, so we route the SIWE handshake
26
+ // through lightnode.app's public pass-through proxy. The proxy preserves
27
+ // the upstream `/api/auth/{challenge,verify}` shape, so callers do not
28
+ // have to special-case it.
29
+ const PROXY_HOSTS = {
30
+ mainnet: "https://lightnode.app/api/gw/mainnet",
31
+ testnet: "https://lightnode.app/api/gw/testnet",
32
+ };
33
+ function looksLikeBrowserFetch() {
34
+ if (typeof window !== "undefined" && typeof document !== "undefined")
35
+ return true;
36
+ const wc = globalThis.process?.versions?.webcontainer;
37
+ return Boolean(wc);
38
+ }
39
+ function defaultSiweBase(cfg) {
40
+ if (looksLikeBrowserFetch())
41
+ return PROXY_HOSTS[cfg.id];
42
+ return cfg.consumerApi ?? "";
43
+ }
24
44
  const REQUEST_TIMEOUT_MS = 15000;
25
45
  function resolveNetwork(network) {
26
46
  if (typeof network === "string") {
@@ -68,10 +88,15 @@ async function httpJson(url, init) {
68
88
  */
69
89
  export async function siweChallenge(network, address, opts = {}) {
70
90
  const cfg = resolveNetwork(network);
71
- if (!cfg.consumerApi) {
72
- throw new Error(`siweChallenge: network "${cfg.id}" has no consumerApi configured`);
91
+ // baseUrl override lets a browser caller route through a different
92
+ // proxy. By default the SDK auto-routes browser traffic through
93
+ // lightnode.app's pass-through proxy (the consumer-api's CORS allowlist
94
+ // excludes most origins). Server-side, we hit the consumer-api direct.
95
+ const base = (opts.baseUrl ?? defaultSiweBase(cfg)).replace(/\/+$/, "");
96
+ if (!base) {
97
+ throw new Error(`siweChallenge: network "${cfg.id}" has no consumerApi configured (and no baseUrl override)`);
73
98
  }
74
- const url = `${cfg.consumerApi.replace(/\/+$/, "")}/api/auth/challenge?address=${address}`;
99
+ const url = `${base}/api/auth/challenge?address=${address}`;
75
100
  return httpJson(url, { method: "GET", signal: opts.signal });
76
101
  }
77
102
  /**
@@ -79,10 +104,11 @@ export async function siweChallenge(network, address, opts = {}) {
79
104
  */
80
105
  export async function siweVerify(network, args, opts = {}) {
81
106
  const cfg = resolveNetwork(network);
82
- if (!cfg.consumerApi) {
83
- throw new Error(`siweVerify: network "${cfg.id}" has no consumerApi configured`);
107
+ const base = (opts.baseUrl ?? defaultSiweBase(cfg)).replace(/\/+$/, "");
108
+ if (!base) {
109
+ throw new Error(`siweVerify: network "${cfg.id}" has no consumerApi configured (and no baseUrl override)`);
84
110
  }
85
- const url = `${cfg.consumerApi.replace(/\/+$/, "")}/api/auth/verify`;
111
+ const url = `${base}/api/auth/verify`;
86
112
  const out = await httpJson(url, {
87
113
  method: "POST",
88
114
  body: { message: args.message, signature: args.signature },
@@ -125,14 +151,14 @@ export async function siweSignIn(walletClient, network, opts = {}) {
125
151
  if (!address) {
126
152
  throw new Error("siweSignIn: walletClient has no account; pass `address` explicitly");
127
153
  }
128
- const { message } = await siweChallenge(cfg, address, { signal: opts.signal });
154
+ const { message } = await siweChallenge(cfg, address, { signal: opts.signal, baseUrl: opts.baseUrl });
129
155
  // viem's signMessage requires `account` even when one is set on the
130
156
  // client; passing it explicitly works with both wagmi and bare viem.
131
157
  const signature = await walletClient.signMessage({
132
158
  account: walletClient.account?.address ?? address,
133
159
  message,
134
160
  });
135
- const verified = await siweVerify(cfg, { message, signature }, { signal: opts.signal });
161
+ const verified = await siweVerify(cfg, { message, signature }, { signal: opts.signal, baseUrl: opts.baseUrl });
136
162
  const token = verified.token;
137
163
  return {
138
164
  token,
package/dist/gateway.d.ts CHANGED
@@ -35,6 +35,19 @@ export interface SelectSessionResult {
35
35
  disputerEncryptionKey?: string;
36
36
  nonce: number;
37
37
  expiry: number;
38
+ /**
39
+ * Opaque correlation token added by the dispatcher in May 2026
40
+ * (lcai-chat-v2 commit 33c70841, web-search epic / Story 16). The client
41
+ * MUST echo it back to `prepareSession` so a later capability-aware
42
+ * select cannot overwrite our pending slot. Optional for forward-compat
43
+ * with older dispatchers that predate the token.
44
+ *
45
+ * Without this, any concurrent activity for the same wallet produces a
46
+ * 409 selection_mismatch on prepare.
47
+ */
48
+ selectionId?: string;
49
+ /** Worker capabilities reported by the dispatcher (web-search etc.). */
50
+ workerCapabilities?: string[];
38
51
  }
39
52
  export interface PrepareSessionResult {
40
53
  worker: `0x${string}`;
@@ -95,6 +108,13 @@ export declare class GatewayClient {
95
108
  modelId: `0x${string}`;
96
109
  encWorkerKey: string;
97
110
  encDisputerKey: string;
111
+ /**
112
+ * Correlation token from {@link SelectSessionResult.selectionId}. Required
113
+ * by the May 2026 dispatcher to avoid 409 selection_mismatch when a newer
114
+ * select for the same wallet has overwritten the pending slot.
115
+ */
116
+ selectionId?: string;
117
+ requiredCapabilities?: string[];
98
118
  }): Promise<PrepareSessionResult>;
99
119
  /** Protected: upload an encrypted prompt blob; returns the EIP-4844 blob hash. */
100
120
  uploadBlob(base64Data: string): Promise<UploadBlobResult>;
package/dist/index.d.ts CHANGED
@@ -134,7 +134,7 @@ export declare class LightNode {
134
134
  * (especially in registry-proxy environments like StackBlitz where lockfiles
135
135
  * may pin an older minor than the local install command suggests).
136
136
  */
137
- export declare const SDK_VERSION = "0.7.10";
137
+ export declare const SDK_VERSION = "0.7.12";
138
138
  export { NETWORKS, WORKER_REGISTRY, REGISTRY_TOPICS, aggregateModelStats, aggregateWorkerStats, networkAnalytics, modelStatsCsv, workerStatsCsv, workerJobsCsv, fromWei, resolveJobTransactions, siweSignIn, siweChallenge, siweVerify, fetchWorkerModels, computeModelId as modelId, estimateJobFee, JOB_REGISTRY_CONSUMER_ABI, consumerGatewayUrl, consumerGatewayHost, GatewayClient, GatewayHttpError, prepareSession, submitPrompt, decryptResponse, generateEcdhKeyPair, crypto, runInference, runInferenceWithKey, runInferenceStream, Conversation, chat, runInferenceBatch, Agent, parseAgentOutput, workerPreflight, workerWatch, Bridge, BRIDGE_ROUTE, HYPERLANE_ROUTER_ABI, ERC20_ABI, addressToBytes32, quoteBridgeFee, bridgeableBalance, bridgeAllowance, approveBridge, bridgeTransfer, DAO, DAO_ADDRESSES, ProposalState, PROPOSAL_STATE_LABEL, VoteSupport, GOVERNOR_ABI, VOTES_ABI, OnchainModelRegistry, AIVM_MODEL_REGISTRY_ABI, BENCHMARK_REGISTRY_ABI, ModelStatus, MODEL_STATUS_LABEL, StalledWorkerError, OnChainRevertError, RelayTokenTimeoutError, GatewayAuthError, isStalledWorker, WorkerOperator, WORKER_REGISTRY_ABI, JOB_REGISTRY_OPERATOR_ABI, AI_CONFIG_ABI, JOB_STATE, decodeWorkerError, WorkerOpError, isWorkerOpError, };
139
139
  export type { BearerSource, GatewayClientOptions, SelectSessionResult, PrepareSessionResult, UploadBlobResult, SessionTokenResult } from "./gateway.js";
140
140
  export type { SessionPreparation, RunInferenceArgs, RunInferenceResult, RunInferenceWithKeyArgs, RunInferenceStreamResult } from "./inference.js";
package/dist/index.js CHANGED
@@ -213,7 +213,7 @@ export class LightNode {
213
213
  * (especially in registry-proxy environments like StackBlitz where lockfiles
214
214
  * may pin an older minor than the local install command suggests).
215
215
  */
216
- export const SDK_VERSION = "0.7.10";
216
+ export const SDK_VERSION = "0.7.12";
217
217
  export { NETWORKS, WORKER_REGISTRY, REGISTRY_TOPICS, aggregateModelStats, aggregateWorkerStats, networkAnalytics, modelStatsCsv, workerStatsCsv, workerJobsCsv, fromWei,
218
218
  // v0.7.3 per-job transaction-hash resolver (lifts the upstream
219
219
  // subgraph's "block-only" Job entity to a deep-linkable Job + tx pair).
package/dist/inference.js CHANGED
@@ -107,10 +107,18 @@ export async function prepareSession(gateway, modelTag) {
107
107
  const encDisputer = selected.disputerEncryptionKey
108
108
  ? await encryptSessionKey(sessionKey, await importPublicKey(decodePublicKey(selected.disputerEncryptionKey)))
109
109
  : new Uint8Array(0);
110
+ // ROOT-CAUSE FIX (lcai-chat-v2 commit 33c70841, May 2026): echo the
111
+ // dispatcher's selectionId from selectSession back into prepareSession.
112
+ // Without this, the dispatcher's pending-slot tracker matches against
113
+ // the LATEST select for our wallet, so any concurrent activity (other
114
+ // tab, other dApp signed into the same wallet) produces 409
115
+ // selection_mismatch. Threading it makes the prepare bind to the
116
+ // selection we actually got.
110
117
  const prepared = await gateway.prepareSession({
111
118
  modelId: id,
112
119
  encWorkerKey: bytesToBase64(encWorker),
113
120
  encDisputerKey: bytesToBase64(encDisputer),
121
+ ...(selected.selectionId ? { selectionId: selected.selectionId } : {}),
114
122
  });
115
123
  return {
116
124
  sessionKey,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lightnode-sdk",
3
- "version": "0.7.10",
3
+ "version": "0.7.12",
4
4
  "description": "Read-only TypeScript client for LightChain AI: workers, jobs, models, on-chain registration, and per-model network analytics. Independent, community-built (not an official LightChain package).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",