lightnode-sdk 0.7.19 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -3
- package/dist/add.d.ts +10 -0
- package/dist/add.js +101 -15
- package/dist/cli.js +157 -68
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -484,11 +484,15 @@ npx lightnode add judge-web3 # evaluator UI, wallet-signed
|
|
|
484
484
|
npx lightnode add wagmi-setup # wallet wiring: lib/wagmi + providers + connect button
|
|
485
485
|
```
|
|
486
486
|
|
|
487
|
-
The `*-web3` scaffolders
|
|
488
|
-
|
|
487
|
+
The `*-web3` scaffolders are one command end to end: run in an empty folder and
|
|
488
|
+
they scaffold a Next.js app, write the page with a wired Connect button, bundle
|
|
489
|
+
the wagmi config + providers + connect button, wrap your layout with
|
|
490
|
+
`<Providers>`, and `npm install` the deps. Run inside an existing Next.js app
|
|
491
|
+
and they skip the scaffold and just add what's missing. Opt out of the
|
|
492
|
+
automation with `--no-scaffold` and `--no-install`.
|
|
489
493
|
|
|
490
494
|
All `add` commands accept `--template auto|nextjs-api|hono|node`,
|
|
491
|
-
`--net testnet|mainnet`, and `--
|
|
495
|
+
`--net testnet|mainnet`, `--force`, `--no-install`, and `--no-scaffold`.
|
|
492
496
|
|
|
493
497
|
> If `add <name>` reports an unknown target, your `npx` cache is serving an
|
|
494
498
|
> older CLI. Force the current release: `npx lightnode-sdk@latest add <name>`.
|
package/dist/add.d.ts
CHANGED
|
@@ -109,6 +109,16 @@ export declare function addWagmiSetup(opts?: AddOpts): {
|
|
|
109
109
|
template: Template;
|
|
110
110
|
network: Network;
|
|
111
111
|
};
|
|
112
|
+
export interface LayoutPatch {
|
|
113
|
+
path: string;
|
|
114
|
+
patched: boolean;
|
|
115
|
+
reason?: string;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Patch the project's root layout to import and wrap children in <Providers>.
|
|
119
|
+
* Returns what happened so the CLI can report it; never throws.
|
|
120
|
+
*/
|
|
121
|
+
export declare function patchLayoutWithProviders(cwd?: string): LayoutPatch;
|
|
112
122
|
export declare function addJudge(opts?: AddOpts): {
|
|
113
123
|
written: WrittenFile[];
|
|
114
124
|
install: string;
|
package/dist/add.js
CHANGED
|
@@ -321,6 +321,17 @@ function depsNeeded(template) {
|
|
|
321
321
|
// installed too, otherwise `tsx ...` fails with "command not found".
|
|
322
322
|
return ["lightnode-sdk", "viem", "ws", "tsx"];
|
|
323
323
|
}
|
|
324
|
+
/** Full `npm install` line for a template's next-steps, including the dev type
|
|
325
|
+
* packages an editor needs. The node/script template uses Node builtins
|
|
326
|
+
* (`node:process`, `node:readline`) and imports `ws`, so without @types/node
|
|
327
|
+
* and @types/ws a freshly-scaffolded file is a wall of red squiggles in any
|
|
328
|
+
* TypeScript-aware editor even though `tsx` runs it fine. */
|
|
329
|
+
function installLine(template) {
|
|
330
|
+
const runtime = `npm install ${depsNeeded(template).join(" ")}`;
|
|
331
|
+
if (template === "node")
|
|
332
|
+
return `${runtime} && npm install -D @types/node @types/ws`;
|
|
333
|
+
return runtime;
|
|
334
|
+
}
|
|
324
335
|
/**
|
|
325
336
|
* Implementation called by `lightnode add inference [...]`.
|
|
326
337
|
* Returns the list of files written + the install command the user should run.
|
|
@@ -347,7 +358,7 @@ export function addInference(opts = {}) {
|
|
|
347
358
|
written.push(writeFile(path.join(cwd, ".env.example"), ENV_EXAMPLE(network), force));
|
|
348
359
|
return {
|
|
349
360
|
written,
|
|
350
|
-
install:
|
|
361
|
+
install: installLine(template),
|
|
351
362
|
template,
|
|
352
363
|
network,
|
|
353
364
|
};
|
|
@@ -953,6 +964,7 @@ const NEXTJS_CHAT_WEB3_PAGE = `// app/chat-web3/page.tsx
|
|
|
953
964
|
import { useEffect, useState } from "react";
|
|
954
965
|
import { useAccount, useWalletClient, usePublicClient } from "wagmi";
|
|
955
966
|
import { siweSignIn, GatewayClient, runInference, estimateJobFee, NETWORKS } from "lightnode-sdk";
|
|
967
|
+
import { ConnectButton } from "@/components/connect-button";
|
|
956
968
|
|
|
957
969
|
type Turn = {
|
|
958
970
|
role: "user" | "assistant";
|
|
@@ -1063,16 +1075,19 @@ export default function ChatWeb3() {
|
|
|
1063
1075
|
|
|
1064
1076
|
return (
|
|
1065
1077
|
<main style={{ maxWidth: 720, margin: "32px auto", padding: 16, fontFamily: "system-ui" }}>
|
|
1066
|
-
<
|
|
1078
|
+
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 }}>
|
|
1079
|
+
<h1>Chat (user-pays)</h1>
|
|
1080
|
+
<ConnectButton />
|
|
1081
|
+
</div>
|
|
1067
1082
|
<p style={{ color: "#666" }}>
|
|
1068
1083
|
Each turn signs one createSession transaction from your wallet on{" "}
|
|
1069
1084
|
<code>{network ?? "(connect a wallet)"}</code>. Fee:{" "}
|
|
1070
1085
|
<code>{feeLcai != null ? \`\${feeLcai} LCAI\` : "(fetching)"}</code> per turn plus a small gas amount.
|
|
1071
1086
|
</p>
|
|
1072
1087
|
{!address && (
|
|
1073
|
-
<div style={{ border: "1px solid #ddd", borderRadius: 8, padding: 12, margin: "12px 0" }}>
|
|
1074
|
-
Connect a wallet to start chatting
|
|
1075
|
-
|
|
1088
|
+
<div style={{ border: "1px solid #ddd", borderRadius: 8, padding: 12, margin: "12px 0", display: "flex", alignItems: "center", gap: 12 }}>
|
|
1089
|
+
<span>Connect a wallet to start chatting.</span>
|
|
1090
|
+
<ConnectButton />
|
|
1076
1091
|
</div>
|
|
1077
1092
|
)}
|
|
1078
1093
|
|
|
@@ -1155,6 +1170,7 @@ const NEXTJS_INFERENCE_WEB3_PAGE = `// app/inference-web3/page.tsx
|
|
|
1155
1170
|
import { useEffect, useState } from "react";
|
|
1156
1171
|
import { useAccount, useWalletClient, usePublicClient } from "wagmi";
|
|
1157
1172
|
import { siweSignIn, GatewayClient, runInference, estimateJobFee, NETWORKS } from "lightnode-sdk";
|
|
1173
|
+
import { ConnectButton } from "@/components/connect-button";
|
|
1158
1174
|
|
|
1159
1175
|
type Result = {
|
|
1160
1176
|
answer: string;
|
|
@@ -1243,15 +1259,19 @@ export default function InferenceWeb3() {
|
|
|
1243
1259
|
|
|
1244
1260
|
return (
|
|
1245
1261
|
<main style={{ maxWidth: 720, margin: "32px auto", padding: 16, fontFamily: "system-ui" }}>
|
|
1246
|
-
<
|
|
1262
|
+
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 }}>
|
|
1263
|
+
<h1>Inference (user-pays)</h1>
|
|
1264
|
+
<ConnectButton />
|
|
1265
|
+
</div>
|
|
1247
1266
|
<p style={{ color: "#666" }}>
|
|
1248
1267
|
Signs one encrypted inference from your wallet on{" "}
|
|
1249
1268
|
<code>{network ?? "(connect a wallet)"}</code>. Fee:{" "}
|
|
1250
1269
|
<code>{feeLcai != null ? \`\${feeLcai} LCAI\` : "(fetching)"}</code> per call plus a small gas amount.
|
|
1251
1270
|
</p>
|
|
1252
1271
|
{!address && (
|
|
1253
|
-
<div style={{ border: "1px solid #ddd", borderRadius: 8, padding: 12, margin: "12px 0" }}>
|
|
1254
|
-
Connect a wallet to run inference
|
|
1272
|
+
<div style={{ border: "1px solid #ddd", borderRadius: 8, padding: 12, margin: "12px 0", display: "flex", alignItems: "center", gap: 12 }}>
|
|
1273
|
+
<span>Connect a wallet to run inference.</span>
|
|
1274
|
+
<ConnectButton />
|
|
1255
1275
|
</div>
|
|
1256
1276
|
)}
|
|
1257
1277
|
|
|
@@ -1310,6 +1330,7 @@ const NEXTJS_JUDGE_WEB3_PAGE = `// app/judge-web3/page.tsx
|
|
|
1310
1330
|
import { useEffect, useState } from "react";
|
|
1311
1331
|
import { useAccount, useWalletClient, usePublicClient } from "wagmi";
|
|
1312
1332
|
import { siweSignIn, GatewayClient, runInference, estimateJobFee, NETWORKS } from "lightnode-sdk";
|
|
1333
|
+
import { ConnectButton } from "@/components/connect-button";
|
|
1313
1334
|
|
|
1314
1335
|
type Verdict = {
|
|
1315
1336
|
passed: boolean;
|
|
@@ -1423,15 +1444,19 @@ Reply with STRICT JSON only, matching: { "passed": boolean, "confidence": 0-1, "
|
|
|
1423
1444
|
|
|
1424
1445
|
return (
|
|
1425
1446
|
<main style={{ maxWidth: 720, margin: "32px auto", padding: 16, fontFamily: "system-ui" }}>
|
|
1426
|
-
<
|
|
1447
|
+
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 }}>
|
|
1448
|
+
<h1>AI Judge (user-pays)</h1>
|
|
1449
|
+
<ConnectButton />
|
|
1450
|
+
</div>
|
|
1427
1451
|
<p style={{ color: "#666" }}>
|
|
1428
1452
|
Each submission signs one inference from your wallet on{" "}
|
|
1429
1453
|
<code>{network ?? "(connect a wallet)"}</code>. Cost:{" "}
|
|
1430
1454
|
<code>{feeLcai != null ? \`\${feeLcai} LCAI\` : "(fetching)"}</code> plus gas. Verdict comes back with on-chain proof.
|
|
1431
1455
|
</p>
|
|
1432
1456
|
{!address && (
|
|
1433
|
-
<div style={{ border: "1px solid #ddd", borderRadius: 8, padding: 12, margin: "12px 0" }}>
|
|
1434
|
-
Connect a wallet to submit
|
|
1457
|
+
<div style={{ border: "1px solid #ddd", borderRadius: 8, padding: 12, margin: "12px 0", display: "flex", alignItems: "center", gap: 12 }}>
|
|
1458
|
+
<span>Connect a wallet to submit.</span>
|
|
1459
|
+
<ConnectButton />
|
|
1435
1460
|
</div>
|
|
1436
1461
|
)}
|
|
1437
1462
|
|
|
@@ -1708,7 +1733,7 @@ export function addAgent(opts = {}) {
|
|
|
1708
1733
|
written.push(writeFile(path.join(cwd, "agent.ts"), NODE_AGENT_SCRIPT, force));
|
|
1709
1734
|
}
|
|
1710
1735
|
written.push(writeFile(path.join(cwd, ".env.example"), ENV_EXAMPLE(network), force));
|
|
1711
|
-
return { written, install:
|
|
1736
|
+
return { written, install: installLine(template), template, network };
|
|
1712
1737
|
}
|
|
1713
1738
|
export function addChat(opts = {}) {
|
|
1714
1739
|
const cwd = opts.cwd ?? process.cwd();
|
|
@@ -1732,7 +1757,7 @@ export function addChat(opts = {}) {
|
|
|
1732
1757
|
written.push(writeFile(path.join(cwd, "chat-repl.ts"), NODE_CHAT_REPL, force));
|
|
1733
1758
|
}
|
|
1734
1759
|
written.push(writeFile(path.join(cwd, ".env.example"), ENV_EXAMPLE(network), force));
|
|
1735
|
-
return { written, install:
|
|
1760
|
+
return { written, install: installLine(template), template, network };
|
|
1736
1761
|
}
|
|
1737
1762
|
/**
|
|
1738
1763
|
* `lightnode add chat-web3` - the user-pays counterpart to addChat.
|
|
@@ -1972,6 +1997,67 @@ export function addWagmiSetup(opts = {}) {
|
|
|
1972
1997
|
network,
|
|
1973
1998
|
};
|
|
1974
1999
|
}
|
|
2000
|
+
function findLayoutFile(cwd) {
|
|
2001
|
+
const candidates = ["app/layout.tsx", "app/layout.jsx", "src/app/layout.tsx", "src/app/layout.jsx"];
|
|
2002
|
+
for (const rel of candidates) {
|
|
2003
|
+
const abs = path.join(cwd, rel);
|
|
2004
|
+
if (fs.existsSync(abs))
|
|
2005
|
+
return abs;
|
|
2006
|
+
}
|
|
2007
|
+
return null;
|
|
2008
|
+
}
|
|
2009
|
+
function withProvidersImport(source) {
|
|
2010
|
+
if (/from\s+["']\.\/providers["']/.test(source))
|
|
2011
|
+
return source;
|
|
2012
|
+
const importLine = `import { Providers } from "./providers";`;
|
|
2013
|
+
const lines = source.split("\n");
|
|
2014
|
+
let lastImport = -1;
|
|
2015
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2016
|
+
if (/^\s*import\s/.test(lines[i]))
|
|
2017
|
+
lastImport = i;
|
|
2018
|
+
}
|
|
2019
|
+
if (lastImport === -1)
|
|
2020
|
+
return `${importLine}\n${source}`;
|
|
2021
|
+
return [...lines.slice(0, lastImport + 1), importLine, ...lines.slice(lastImport + 1)].join("\n");
|
|
2022
|
+
}
|
|
2023
|
+
function withWrappedChildren(source) {
|
|
2024
|
+
if (/<Providers>/.test(source))
|
|
2025
|
+
return source;
|
|
2026
|
+
if (!source.includes("{children}"))
|
|
2027
|
+
return null;
|
|
2028
|
+
return source.replace("{children}", "<Providers>{children}</Providers>");
|
|
2029
|
+
}
|
|
2030
|
+
/**
|
|
2031
|
+
* Patch the project's root layout to import and wrap children in <Providers>.
|
|
2032
|
+
* Returns what happened so the CLI can report it; never throws.
|
|
2033
|
+
*/
|
|
2034
|
+
export function patchLayoutWithProviders(cwd = process.cwd()) {
|
|
2035
|
+
const abs = findLayoutFile(cwd);
|
|
2036
|
+
if (!abs)
|
|
2037
|
+
return { path: "app/layout.tsx", patched: false, reason: "no layout file found" };
|
|
2038
|
+
const rel = path.relative(cwd, abs) || abs;
|
|
2039
|
+
let source;
|
|
2040
|
+
try {
|
|
2041
|
+
source = fs.readFileSync(abs, "utf8");
|
|
2042
|
+
}
|
|
2043
|
+
catch (e) {
|
|
2044
|
+
return { path: rel, patched: false, reason: `could not read layout (${e.message})` };
|
|
2045
|
+
}
|
|
2046
|
+
if (/<Providers>/.test(source) && /from\s+["']\.\/providers["']/.test(source)) {
|
|
2047
|
+
return { path: rel, patched: false, reason: "already wrapped with <Providers>" };
|
|
2048
|
+
}
|
|
2049
|
+
const wrapped = withWrappedChildren(withProvidersImport(source));
|
|
2050
|
+
if (wrapped === null) {
|
|
2051
|
+
return { path: rel, patched: false, reason: "no {children} found - wrap with <Providers> manually" };
|
|
2052
|
+
}
|
|
2053
|
+
try {
|
|
2054
|
+
fs.writeFileSync(abs, wrapped);
|
|
2055
|
+
}
|
|
2056
|
+
catch (e) {
|
|
2057
|
+
return { path: rel, patched: false, reason: `could not write layout (${e.message})` };
|
|
2058
|
+
}
|
|
2059
|
+
return { path: rel, patched: true };
|
|
2060
|
+
}
|
|
1975
2061
|
const NEXTJS_JUDGE_ROUTE = `// app/api/judge/route.ts
|
|
1976
2062
|
// Generated by 'lightnode add judge'. See https://lightnode.app/build
|
|
1977
2063
|
// The LightChallenge-style evaluator: post evidence + criteria, get a
|
|
@@ -2114,7 +2200,7 @@ export function addJudge(opts = {}) {
|
|
|
2114
2200
|
written.push(writeFile(path.join(cwd, "judge.ts"), NODE_JUDGE_SCRIPT, force));
|
|
2115
2201
|
}
|
|
2116
2202
|
written.push(writeFile(path.join(cwd, ".env.example"), ENV_EXAMPLE(network), force));
|
|
2117
|
-
return { written, install:
|
|
2203
|
+
return { written, install: installLine(template), template, network };
|
|
2118
2204
|
}
|
|
2119
2205
|
export function addNftMint(opts = {}) {
|
|
2120
2206
|
const cwd = opts.cwd ?? process.cwd();
|
|
@@ -2132,7 +2218,7 @@ export function addNftMint(opts = {}) {
|
|
|
2132
2218
|
written.push(writeFile(path.join(cwd, ".env.example"), ENV_EXAMPLE(network), force));
|
|
2133
2219
|
return {
|
|
2134
2220
|
written,
|
|
2135
|
-
install:
|
|
2221
|
+
install: installLine(template),
|
|
2136
2222
|
template,
|
|
2137
2223
|
network,
|
|
2138
2224
|
};
|
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { LightNode, modelStatsCsv, workerStatsCsv, workerJobsCsv, runInferenceWithKey, runInferenceBatch, Agent, isStalledWorker, workerPreflight, workerWatch, WorkerOperator, isWorkerOpError, BRIDGE_ROUTE, DAO, DAO_ADDRESSES } from "./index.js";
|
|
3
|
-
import { addInference, addInferenceWeb3, addJudgeWeb3, addAnalyticsDashboard, addNftMint, addChat, addChatWeb3, addAgent, addJudge, addWagmiSetup } from "./add.js";
|
|
2
|
+
import { LightNode, modelStatsCsv, workerStatsCsv, workerJobsCsv, runInferenceWithKey, runInferenceBatch, Agent, isStalledWorker, workerPreflight, workerWatch, WorkerOperator, isWorkerOpError, BRIDGE_ROUTE, DAO, DAO_ADDRESSES, SDK_VERSION } from "./index.js";
|
|
3
|
+
import { addInference, addInferenceWeb3, addJudgeWeb3, addAnalyticsDashboard, addNftMint, addChat, addChatWeb3, addAgent, addJudge, addWagmiSetup, patchLayoutWithProviders } from "./add.js";
|
|
4
4
|
import { createPublicClient, createWalletClient, http, parseEther } from "viem";
|
|
5
5
|
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts";
|
|
6
|
+
import { existsSync, readdirSync, renameSync, rmSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { spawnSync } from "node:child_process";
|
|
6
9
|
function flag(name) {
|
|
7
10
|
const i = process.argv.indexOf(name);
|
|
8
11
|
return i >= 0 ? process.argv[i + 1] : undefined;
|
|
@@ -17,6 +20,71 @@ function die(msg) {
|
|
|
17
20
|
}
|
|
18
21
|
const lcai = (wei) => (wei ? Number(BigInt(wei)) / 1e18 : 0);
|
|
19
22
|
const rate = (r) => (r == null ? "-" : `${Math.round(r * 100)}%`);
|
|
23
|
+
// `add` targets that are always a Next.js client page. In a bare folder these
|
|
24
|
+
// get a real Next.js app scaffolded first so the generated page can render.
|
|
25
|
+
const NEXT_PAGE_TARGETS = new Set(["chat-web3", "inference-web3", "judge-web3", "wagmi-setup"]);
|
|
26
|
+
function printWritten(files) {
|
|
27
|
+
for (const f of files) {
|
|
28
|
+
if (f.skipped)
|
|
29
|
+
console.log(` ⤴ ${f.path} (skipped - ${f.reason})`);
|
|
30
|
+
else
|
|
31
|
+
console.log(` ✓ ${f.path}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Scaffold a Next.js app into cwd via create-next-app. Returns true on success.
|
|
36
|
+
*
|
|
37
|
+
* We scaffold into a fixed-name subfolder and move the files up rather than
|
|
38
|
+
* passing ".", because create-next-app derives the project name from the
|
|
39
|
+
* target and rejects names npm won't allow (capital letters, leading dots,
|
|
40
|
+
* etc.) - which would make this fail in any folder like "MyApp". The subfolder
|
|
41
|
+
* name is one we control, so that validation always passes.
|
|
42
|
+
*/
|
|
43
|
+
function scaffoldNextApp(cwd, target) {
|
|
44
|
+
console.log(`\nNo Next.js app here yet - scaffolding one with create-next-app...\n`);
|
|
45
|
+
const stageName = "lightnode-next-app-stage";
|
|
46
|
+
const args = [
|
|
47
|
+
"--yes", "create-next-app@latest", stageName,
|
|
48
|
+
"--ts", "--app", "--no-src-dir", "--eslint", "--tailwind",
|
|
49
|
+
"--use-npm", "--no-turbopack", "--import-alias", "@/*",
|
|
50
|
+
];
|
|
51
|
+
const r = spawnSync("npx", args, { cwd, stdio: "inherit" });
|
|
52
|
+
if (r.status !== 0) {
|
|
53
|
+
console.error(`\ncreate-next-app did not complete (exit ${r.status ?? "?"}).`);
|
|
54
|
+
console.error(`If this folder already had files, scaffold manually then re-run:`);
|
|
55
|
+
console.error(` npx create-next-app@latest . && npx lightnode-sdk@latest add ${target}`);
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
return relocateScaffold(join(cwd, stageName), cwd, target);
|
|
59
|
+
}
|
|
60
|
+
/** Move every entry from the staged scaffold dir up into cwd, then remove it. */
|
|
61
|
+
function relocateScaffold(from, cwd, target) {
|
|
62
|
+
try {
|
|
63
|
+
for (const entry of readdirSync(from)) {
|
|
64
|
+
const dest = join(cwd, entry);
|
|
65
|
+
if (existsSync(dest))
|
|
66
|
+
continue; // never clobber files the user already had
|
|
67
|
+
renameSync(join(from, entry), dest);
|
|
68
|
+
}
|
|
69
|
+
rmSync(from, { recursive: true, force: true });
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
console.error(`\nScaffolded into ${from} but could not move it up (${e.message}).`);
|
|
74
|
+
console.error(`Move its contents into this folder, then re-run: npx lightnode-sdk@latest add ${target}`);
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Run an `npm install ...` line in cwd. The line is an internal constant
|
|
79
|
+
* (never user input), so running it through a shell is safe here. */
|
|
80
|
+
function installDeps(installLine, cwd) {
|
|
81
|
+
console.log(`\nInstalling dependencies: ${installLine}\n`);
|
|
82
|
+
const r = spawnSync(installLine, { cwd, stdio: "inherit", shell: true });
|
|
83
|
+
if (r.status === 0)
|
|
84
|
+
return true;
|
|
85
|
+
console.error(`\nDependency install failed (exit ${r.status ?? "?"}). Run it yourself:\n ${installLine}`);
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
20
88
|
const HELP = `lightnode <command> [--net mainnet|testnet]
|
|
21
89
|
|
|
22
90
|
Run one inference (needs PRIVATE_KEY in env):
|
|
@@ -77,7 +145,12 @@ Scaffold templates into the current project (run inside a Next.js app):
|
|
|
77
145
|
add wagmi-setup wallet wiring: lib/wagmi + providers + connect button
|
|
78
146
|
(all add commands: [--template auto|nextjs-api|hono|node] [--net testnet|mainnet] [--force])
|
|
79
147
|
|
|
80
|
-
To scaffold a new project instead, run: npm create lightnode-app my-app
|
|
148
|
+
To scaffold a new project instead, run: npm create lightnode-app my-app
|
|
149
|
+
|
|
150
|
+
Diagnostics:
|
|
151
|
+
version print this CLI's version (also: --version, -v)
|
|
152
|
+
(a missing 'add' target usually means an old install -
|
|
153
|
+
update with: npm install -g lightnode-sdk@latest)`;
|
|
81
154
|
function pickKey() {
|
|
82
155
|
const k = flag("--key") ?? process.env.PRIVATE_KEY;
|
|
83
156
|
if (!k || !k.startsWith("0x") || k.length !== 66) {
|
|
@@ -120,6 +193,14 @@ async function workerJobIds(n, address) {
|
|
|
120
193
|
return jobs.map((j) => Number(j.id)).filter((x) => Number.isFinite(x));
|
|
121
194
|
}
|
|
122
195
|
async function main() {
|
|
196
|
+
// Answer `version` / `--version` / `-v` before anything else so a user who
|
|
197
|
+
// suspects they're on a stale binary can confirm it without a network call
|
|
198
|
+
// or a funded key. This is the first thing to check when an `add` target
|
|
199
|
+
// "doesn't exist" - an old global install is the common cause.
|
|
200
|
+
if (cmd === "version" || process.argv.includes("--version") || process.argv.includes("-v")) {
|
|
201
|
+
console.log(SDK_VERSION);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
123
204
|
const ln = new LightNode(net);
|
|
124
205
|
switch (cmd) {
|
|
125
206
|
case "chat": {
|
|
@@ -474,16 +555,34 @@ async function main() {
|
|
|
474
555
|
const lines = [
|
|
475
556
|
`usage: lightnode add <${known.join("|")}> [--template auto|nextjs-api|hono|node] [--net testnet|mainnet] [--force]`,
|
|
476
557
|
];
|
|
477
|
-
//
|
|
478
|
-
//
|
|
479
|
-
//
|
|
558
|
+
// A target that's missing here but valid in a newer release means the
|
|
559
|
+
// user is running an OLD lightnode-sdk. The usual cause is an outdated
|
|
560
|
+
// GLOBAL install (`npm i -g lightnode-sdk`) on PATH, which npx prefers
|
|
561
|
+
// over the registry - so `npx lightnode-sdk add ...` keeps hitting the
|
|
562
|
+
// stale binary. We can't know the latest version offline, but we can
|
|
563
|
+
// show what THIS binary is and the two commands that fix it. Listing
|
|
564
|
+
// the global update first because that's the one most people miss.
|
|
480
565
|
if (sub) {
|
|
481
566
|
lines.push("");
|
|
482
|
-
lines.push(`unknown add target "${sub}"
|
|
483
|
-
lines.push(`
|
|
567
|
+
lines.push(`unknown add target "${sub}" - this CLI is lightnode-sdk v${SDK_VERSION}, which`);
|
|
568
|
+
lines.push(`does not have it. You're likely on an older install. Update, then retry:`);
|
|
569
|
+
lines.push(` npm install -g lightnode-sdk@latest # if 'lightnode' is on your PATH`);
|
|
570
|
+
lines.push(` npx lightnode-sdk@latest add ${sub} # or force the latest for one run`);
|
|
484
571
|
}
|
|
485
572
|
die(lines.join("\n"));
|
|
486
573
|
}
|
|
574
|
+
// ---- one-command setup: flags + optional scaffold ----
|
|
575
|
+
const noInstall = process.argv.includes("--no-install");
|
|
576
|
+
const noScaffold = process.argv.includes("--no-scaffold");
|
|
577
|
+
const cwd = process.cwd();
|
|
578
|
+
const isWeb3Page = sub === "chat-web3" || sub === "inference-web3" || sub === "judge-web3";
|
|
579
|
+
// A Next.js client page needs a Next.js app to live in. In a bare folder,
|
|
580
|
+
// scaffold one first so the generated page renders instead of throwing
|
|
581
|
+
// "Cannot find module 'react'". Opt out with --no-scaffold.
|
|
582
|
+
if (NEXT_PAGE_TARGETS.has(sub ?? "") && !existsSync(join(cwd, "package.json")) && !noScaffold) {
|
|
583
|
+
scaffoldNextApp(cwd, sub ?? "");
|
|
584
|
+
}
|
|
585
|
+
// ---- write the requested files ----
|
|
487
586
|
const result = sub === "analytics-dashboard" ? addAnalyticsDashboard({ template, network, force })
|
|
488
587
|
: sub === "nft-mint-with-inference" ? addNftMint({ template, network, force })
|
|
489
588
|
: sub === "chat-web3" ? addChatWeb3({ template, network, force })
|
|
@@ -495,61 +594,53 @@ async function main() {
|
|
|
495
594
|
: sub === "judge" ? addJudge({ template, network, force })
|
|
496
595
|
: addInference({ template, network, force });
|
|
497
596
|
console.log(`▶ add ${sub} (${result.template} template, default network ${result.network})`);
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
597
|
+
printWritten(result.written);
|
|
598
|
+
// ---- web3 pages: bundle the wagmi wiring + wrap the root layout so the
|
|
599
|
+
// page's <ConnectButton /> and wagmi hooks have a provider to resolve. ----
|
|
600
|
+
let layout = null;
|
|
601
|
+
if (isWeb3Page) {
|
|
602
|
+
const wagmi = addWagmiSetup({ template: result.template, network, force });
|
|
603
|
+
printWritten(wagmi.written);
|
|
604
|
+
}
|
|
605
|
+
if (isWeb3Page || sub === "wagmi-setup") {
|
|
606
|
+
layout = patchLayoutWithProviders(cwd);
|
|
607
|
+
if (layout.patched)
|
|
608
|
+
console.log(` ✓ ${layout.path} (wrapped children with <Providers>)`);
|
|
501
609
|
else
|
|
502
|
-
console.log(`
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
610
|
+
console.log(` ⤴ ${layout.path} (${layout.reason})`);
|
|
611
|
+
}
|
|
612
|
+
// ---- install dependencies (opt out with --no-install) ----
|
|
613
|
+
const installed = noInstall ? false : installDeps(result.install, cwd);
|
|
614
|
+
// ---- next steps ----
|
|
615
|
+
const layoutNeedsManual = layout != null && !layout.patched && !/already/.test(layout.reason ?? "");
|
|
616
|
+
if (isWeb3Page) {
|
|
617
|
+
const route = sub === "chat-web3" ? "/chat-web3" : sub === "inference-web3" ? "/inference-web3" : "/judge-web3";
|
|
618
|
+
const chainId = result.network === "mainnet" ? "9200" : "8200";
|
|
619
|
+
console.log(`\n${installed ? "✓ Done - deps installed, wagmi + layout wired. Just run it:" : "Files written. Next:"}`);
|
|
620
|
+
if (!installed)
|
|
621
|
+
console.log(` ${result.install}`);
|
|
622
|
+
console.log(` npm run dev`);
|
|
623
|
+
console.log(` open http://localhost:3000${route} and click Connect wallet (chainId ${chainId})`);
|
|
624
|
+
console.log(` ${result.network === "mainnet" ? "llama3-8b costs 0.02 LCAI per call" : "testnet is free"}`);
|
|
625
|
+
if (layoutNeedsManual) {
|
|
626
|
+
console.log(`\nHeads up: couldn't auto-wire the layout (${layout?.reason}).`);
|
|
627
|
+
console.log(`Wrap {children} with <Providers> in app/layout.tsx (import from "./providers").`);
|
|
518
628
|
}
|
|
519
|
-
console.log(`\
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
console.log(
|
|
527
|
-
console.log(` 4. You can now use any wagmi hook (useAccount, useWalletClient, ...).`);
|
|
528
|
-
console.log(` Wallets on chains other than 9200/8200 will be prompted to switch.`);
|
|
629
|
+
console.log(`\n No server-side route - deploy static (Vercel/Netlify/Cloudflare free tier all work).`);
|
|
630
|
+
}
|
|
631
|
+
else if (sub === "wagmi-setup") {
|
|
632
|
+
console.log(`\n${installed ? "✓ Done - deps installed and layout wired." : "Files written. Run: " + result.install}`);
|
|
633
|
+
console.log(`\nUse it: import { ConnectButton } from "@/components/connect-button"; drop <ConnectButton /> anywhere.`);
|
|
634
|
+
console.log(`Any wagmi hook (useAccount, useWalletClient, ...) now works app-wide. Off-network wallets get a switch prompt.`);
|
|
635
|
+
if (layoutNeedsManual) {
|
|
636
|
+
console.log(`\nHeads up: couldn't auto-wire the layout (${layout?.reason}). Wrap {children} with <Providers> in app/layout.tsx.`);
|
|
529
637
|
}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
if (needsWagmi) {
|
|
537
|
-
console.log(` 2. Get wagmi wired up with one command:`);
|
|
538
|
-
console.log(` npx lightnode add wagmi-setup`);
|
|
539
|
-
console.log(` (drops lib/wagmi.ts + app/providers.tsx + components/connect-button.tsx)`);
|
|
540
|
-
console.log(` 3. Wrap your layout with <Providers> and drop <ConnectButton /> on the page.`);
|
|
541
|
-
console.log(` 4. npm run dev, open ${route}, connect on chainId ${result.network === "mainnet" ? "9200" : "8200"}.`);
|
|
542
|
-
console.log(` Mainnet llama3-8b is 0.02 LCAI per call; testnet is free from https://lightfaucet.ai`);
|
|
543
|
-
}
|
|
544
|
-
else {
|
|
545
|
-
console.log(` 2. npm run dev, open ${route}`);
|
|
546
|
-
console.log(` 3. Connect a wallet on LightChain ${result.network === "mainnet" ? "mainnet (chainId 9200)" : "testnet (chainId 8200)"}.`);
|
|
547
|
-
console.log(` Mainnet llama3-8b is 0.02 LCAI per call; testnet is free from https://lightfaucet.ai`);
|
|
548
|
-
}
|
|
549
|
-
console.log(`\n Note: ${sub} has NO server-side route, so it scales infinitely on`);
|
|
550
|
-
console.log(` static hosting (Vercel/Netlify/Cloudflare Pages free tier all work).`);
|
|
551
|
-
}
|
|
552
|
-
else if (sub === "nft-mint-with-inference" || sub === "inference" || sub === "chat" || sub === "agent" || sub === "judge") {
|
|
638
|
+
}
|
|
639
|
+
else {
|
|
640
|
+
// server-paid + read-only targets keep the detailed guidance.
|
|
641
|
+
console.log(`\nNext steps:`);
|
|
642
|
+
console.log(` 1. ${installed ? "(done) " : ""}${result.install}`);
|
|
643
|
+
if (sub === "nft-mint-with-inference" || sub === "inference" || sub === "chat" || sub === "agent" || sub === "judge") {
|
|
553
644
|
console.log(` 2. cp .env.example .env (and put a funded ${result.network} PRIVATE_KEY in it)`);
|
|
554
645
|
if (sub === "agent" && result.template === "nextjs-api") {
|
|
555
646
|
console.log(` 3. Set CRON_SECRET in your Vercel env vars + edit AGENT_TASK in .env`);
|
|
@@ -607,8 +698,6 @@ async function main() {
|
|
|
607
698
|
}
|
|
608
699
|
}
|
|
609
700
|
// Hosting note: the Docker setup we shipped is the recommended path.
|
|
610
|
-
// The managed platforms (Vercel etc.) are the fallback if a builder is
|
|
611
|
-
// already committed to one.
|
|
612
701
|
if (result.template === "nextjs-api"
|
|
613
702
|
&& (sub === "inference" || sub === "chat" || sub === "judge")) {
|
|
614
703
|
console.log(`\n Hosting: a mainnet inference takes 60-90s. The Dockerfile + docker-compose.yml`);
|
|
@@ -617,13 +706,13 @@ async function main() {
|
|
|
617
706
|
console.log(` Don't use Vercel Hobby (10s cap, every call times out). Vercel Pro works at`);
|
|
618
707
|
console.log(` 60s if you'd rather stay on Vercel. See LIGHTNODE-HOSTING.md for the full table.`);
|
|
619
708
|
}
|
|
620
|
-
if (result.network === "testnet") {
|
|
621
|
-
console.log(`\nNo wallet yet? Make one: npx lightnode wallet new then fund it free below.`);
|
|
622
|
-
}
|
|
623
|
-
console.log(`\nFree testnet LCAI: https://lightfaucet.ai`);
|
|
624
|
-
console.log(`Builder docs: https://lightnode.app/build`);
|
|
625
|
-
console.log(`New to all this? See GETTING-STARTED.md in the lightnode repo.`);
|
|
626
709
|
}
|
|
710
|
+
if (result.network === "testnet") {
|
|
711
|
+
console.log(`\nNo wallet yet? Make one: npx lightnode wallet new then fund it free below.`);
|
|
712
|
+
}
|
|
713
|
+
console.log(`\nFree testnet LCAI: https://lightfaucet.ai`);
|
|
714
|
+
console.log(`Builder docs: https://lightnode.app/build`);
|
|
715
|
+
console.log(`New to all this? See GETTING-STARTED.md in the lightnode repo.`);
|
|
627
716
|
break;
|
|
628
717
|
}
|
|
629
718
|
case "batch": {
|
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.
|
|
137
|
+
export declare const SDK_VERSION = "0.7.20";
|
|
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.
|
|
216
|
+
export const SDK_VERSION = "0.7.20";
|
|
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lightnode-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
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",
|