lightnode-sdk 0.8.0 → 0.8.1

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/add.d.ts CHANGED
@@ -119,6 +119,20 @@ export interface LayoutPatch {
119
119
  * Returns what happened so the CLI can report it; never throws.
120
120
  */
121
121
  export declare function patchLayoutWithProviders(cwd?: string): LayoutPatch;
122
+ export declare const SCAFFOLD_GLOBALS_CSS = "@import \"tailwindcss\";\n\n/* dark mode via .dark class (we default the app to dark) */\n@custom-variant dark (&:is(.dark, .dark *));\n\n/* design tokens (light) - ported from lcai-chat-v2 */\n:root {\n font-family: var(--font-inter), ui-sans-serif, system-ui, sans-serif;\n\n --background: #ffffff;\n --primary: #6767e9;\n --primary-600: #5a4fd8;\n --foreground: #09090b;\n --card: #ffffff;\n --card-foreground: #09090b;\n --popover: #ffffff;\n --popover-foreground: hsl(240 10% 3.9%);\n --primary-foreground: #fafafa;\n --secondary: hsl(240 4.8% 95.9%);\n --secondary-foreground: hsl(240 5.9% 10%);\n --muted: hsl(240 4.8% 95.9%);\n --muted-foreground: hsl(240 3.8% 46.1%);\n --accent: hsl(240 4.8% 95.9%);\n --accent-foreground: hsl(240 5.9% 10%);\n --destructive: #ef4d6a;\n --destructive-foreground: hsl(0 0% 98%);\n --success: #15bd77;\n --warning: #eaa53d;\n --border: hsl(240 5.9% 90%);\n --input: hsl(240 5.9% 90%);\n --ring: hsl(240 10% 3.9%);\n --radius: 0.625rem;\n\n --surface-base-subtle: rgba(34, 35, 42, 0.02);\n --surface-base-faint: rgba(14, 18, 27, 0.04);\n --surface-base-light: rgba(204, 206, 239, 0.16);\n --surface-elevation-light: #ffffff;\n\n --content-primary: #0f0f14;\n --content-default: #373842;\n --content-soft: #656678;\n --content-extraLight: #9798b6;\n\n --border-soft: rgba(14, 18, 27, 0.08);\n --border-light: rgba(14, 18, 27, 0.06);\n}\n\n/* design tokens (dark) */\n.dark {\n --background: #070710;\n --foreground: hsl(0 0% 98%);\n --card: #0f0f14;\n --card-foreground: hsl(0 0% 98%);\n --popover: #0f0f14;\n --popover-foreground: hsl(0 0% 98%);\n --primary: #7064e9;\n --primary-600: #8c71f6;\n --primary-foreground: hsl(0 0% 98%);\n --secondary: hsl(240 3.7% 15.9%);\n --secondary-foreground: hsl(0 0% 98%);\n --muted: hsl(240 3.7% 15.9%);\n --muted-foreground: hsl(240 5% 64.9%);\n --accent: hsl(240 3.7% 15.9%);\n --accent-foreground: hsl(0 0% 98%);\n --destructive: #fb5a76;\n --destructive-foreground: hsl(0 0% 98%);\n --success: #22d68a;\n --warning: #f5be5c;\n --border: hsl(240 3.7% 15.9%);\n --input: hsl(240 3.7% 15.9%);\n --ring: hsl(240 4.9% 83.9%);\n\n --surface-base-subtle: rgba(204, 206, 239, 0.02);\n --surface-base-faint: rgba(204, 206, 239, 0.04);\n --surface-base-light: rgba(204, 206, 239, 0.08);\n --surface-elevation-light: #0f0f14;\n\n --content-primary: #cccef0;\n --content-default: #9798b6;\n --content-soft: rgba(154, 156, 207, 0.8);\n --content-extraLight: #9798b6;\n\n --border-soft: rgba(204, 206, 239, 0.12);\n --border-light: rgba(204, 206, 239, 0.08);\n}\n\n/* theme mapping (Tailwind v4 @theme) */\n@theme inline {\n --radius-md: calc(var(--radius) - 2px);\n --radius-sm: calc(var(--radius) - 4px);\n --radius-lg: var(--radius);\n\n --color-background: var(--background);\n --color-foreground: var(--foreground);\n --color-card: var(--card);\n --color-card-foreground: var(--card-foreground);\n --color-popover: var(--popover);\n --color-popover-foreground: var(--popover-foreground);\n --color-primary: var(--primary);\n --color-primary-600: var(--primary-600);\n --color-primary-foreground: var(--primary-foreground);\n --color-secondary: var(--secondary);\n --color-secondary-foreground: var(--secondary-foreground);\n --color-muted: var(--muted);\n --color-muted-foreground: var(--muted-foreground);\n --color-accent: var(--accent);\n --color-accent-foreground: var(--accent-foreground);\n --color-destructive: var(--destructive);\n --color-destructive-foreground: var(--destructive-foreground);\n --color-success: var(--success);\n --color-warning: var(--warning);\n --color-border: var(--border);\n --color-input: var(--input);\n --color-ring: var(--ring);\n\n --color-surface-base-subtle: var(--surface-base-subtle);\n --color-surface-base-faint: var(--surface-base-faint);\n --color-surface-base-light: var(--surface-base-light);\n --color-surface-elevation-light: var(--surface-elevation-light);\n --color-surface-base-brand-default: #693ee0;\n --color-surface-base-brand-strong: #8c71f6;\n\n --color-content-primary: var(--content-primary);\n --color-content-default: var(--content-default);\n --color-content-soft: var(--content-soft);\n --color-content-extraLight: var(--content-extraLight);\n\n --color-bdr-soft: var(--border-soft);\n --color-bdr-light: var(--border-light);\n\n --color-gradient-primary: linear-gradient(270deg, #7064e9 0%, #dd00ac 100%);\n}\n\n@layer base {\n * {\n border-color: var(--border);\n }\n body {\n background-color: var(--background);\n color: var(--foreground);\n overflow-x: hidden;\n }\n html {\n overflow-x: hidden;\n }\n button {\n cursor: pointer;\n }\n button:disabled {\n cursor: not-allowed;\n }\n /* visible keyboard focus across interactive elements */\n a:focus-visible,\n button:focus-visible,\n input:focus-visible,\n select:focus-visible,\n textarea:focus-visible {\n outline: 2px solid var(--primary);\n outline-offset: 2px;\n border-radius: 6px;\n }\n}\n\n/* respect reduced-motion: kill non-essential animation */\n@media (prefers-reduced-motion: reduce) {\n *,\n ::before,\n ::after {\n animation-duration: 0.001ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.001ms !important;\n scroll-behavior: auto !important;\n }\n}\n\n/* ambient app background (gradient mesh behind everything) */\nbody::before {\n content: \"\";\n position: fixed;\n inset: 0;\n z-index: -1;\n pointer-events: none;\n background:\n radial-gradient(60% 50% at 50% -6%, rgba(221, 0, 172, 0.10), transparent 60%),\n radial-gradient(55% 45% at 12% -8%, rgba(112, 100, 233, 0.14), transparent 60%),\n radial-gradient(50% 40% at 88% -2%, rgba(112, 100, 233, 0.12), transparent 60%),\n radial-gradient(45% 45% at 50% 115%, rgba(112, 100, 233, 0.07), transparent 60%);\n}\n.dark body::before {\n background:\n radial-gradient(60% 50% at 50% -6%, rgba(221, 0, 172, 0.12), transparent 60%),\n radial-gradient(55% 45% at 12% -8%, rgba(112, 100, 233, 0.18), transparent 60%),\n radial-gradient(50% 40% at 88% -2%, rgba(112, 100, 233, 0.14), transparent 60%),\n radial-gradient(45% 45% at 50% 118%, rgba(112, 100, 233, 0.10), transparent 60%);\n}\n\n/* minimal scrollbar */\n::-webkit-scrollbar {\n width: 6px;\n height: 6px;\n}\n::-webkit-scrollbar-track {\n background: transparent;\n}\n::-webkit-scrollbar-thumb {\n background: var(--border);\n border-radius: 3px;\n}\n* {\n scrollbar-width: thin;\n scrollbar-color: var(--border) transparent;\n}\n";
123
+ export interface ScaffoldWiring {
124
+ written: WrittenFile[];
125
+ /** The route now also served at `/` (e.g. "/chat-web3"), or null if nothing was wired. */
126
+ homepageRoute: string | null;
127
+ /** Whether the `dark` class was added to <html> (dark kept as the default). */
128
+ darkDefault: boolean;
129
+ }
130
+ /**
131
+ * Wire a freshly scaffolded Next.js app so the generated -web3 page is the
132
+ * homepage and the LightChain theme + dark default are in place. No-op for any
133
+ * target without a known route folder.
134
+ */
135
+ export declare function wireFreshScaffold(target: string, opts?: AddOpts): ScaffoldWiring;
122
136
  export declare function addJudge(opts?: AddOpts): {
123
137
  written: WrittenFile[];
124
138
  install: string;
package/dist/add.js CHANGED
@@ -2058,6 +2058,311 @@ export function patchLayoutWithProviders(cwd = process.cwd()) {
2058
2058
  }
2059
2059
  return { path: rel, patched: true };
2060
2060
  }
2061
+ // ---------------------------------------------------------------------------
2062
+ // Fresh-scaffold wiring: when `add <x>-web3` scaffolds a brand-new Next.js app
2063
+ // (bare folder), the create-next-app starter page sits at `/` and the generated
2064
+ // page sits at `/<x>-web3`, so `npm run dev` + localhost:3000 lands on the
2065
+ // starter, not the chat. This makes the generated page the homepage and ships
2066
+ // the LightChain theme so the first render is the real thing.
2067
+ //
2068
+ // Only ever invoked on a scaffold WE just created, so overwriting page.tsx and
2069
+ // globals.css is safe - there is no user content to clobber. In an existing app
2070
+ // none of this runs; the page keeps its dedicated /<x>-web3 route untouched.
2071
+ // ---------------------------------------------------------------------------
2072
+ // The LightChain chat theme (Tailwind v4 tokens, light + dark), ported from the
2073
+ // lightnode app's globals.css. Shipped as the scaffold's app/globals.css so
2074
+ // installs default to the real look instead of the create-next-app starter.
2075
+ export const SCAFFOLD_GLOBALS_CSS = `@import "tailwindcss";
2076
+
2077
+ /* dark mode via .dark class (we default the app to dark) */
2078
+ @custom-variant dark (&:is(.dark, .dark *));
2079
+
2080
+ /* design tokens (light) - ported from lcai-chat-v2 */
2081
+ :root {
2082
+ font-family: var(--font-inter), ui-sans-serif, system-ui, sans-serif;
2083
+
2084
+ --background: #ffffff;
2085
+ --primary: #6767e9;
2086
+ --primary-600: #5a4fd8;
2087
+ --foreground: #09090b;
2088
+ --card: #ffffff;
2089
+ --card-foreground: #09090b;
2090
+ --popover: #ffffff;
2091
+ --popover-foreground: hsl(240 10% 3.9%);
2092
+ --primary-foreground: #fafafa;
2093
+ --secondary: hsl(240 4.8% 95.9%);
2094
+ --secondary-foreground: hsl(240 5.9% 10%);
2095
+ --muted: hsl(240 4.8% 95.9%);
2096
+ --muted-foreground: hsl(240 3.8% 46.1%);
2097
+ --accent: hsl(240 4.8% 95.9%);
2098
+ --accent-foreground: hsl(240 5.9% 10%);
2099
+ --destructive: #ef4d6a;
2100
+ --destructive-foreground: hsl(0 0% 98%);
2101
+ --success: #15bd77;
2102
+ --warning: #eaa53d;
2103
+ --border: hsl(240 5.9% 90%);
2104
+ --input: hsl(240 5.9% 90%);
2105
+ --ring: hsl(240 10% 3.9%);
2106
+ --radius: 0.625rem;
2107
+
2108
+ --surface-base-subtle: rgba(34, 35, 42, 0.02);
2109
+ --surface-base-faint: rgba(14, 18, 27, 0.04);
2110
+ --surface-base-light: rgba(204, 206, 239, 0.16);
2111
+ --surface-elevation-light: #ffffff;
2112
+
2113
+ --content-primary: #0f0f14;
2114
+ --content-default: #373842;
2115
+ --content-soft: #656678;
2116
+ --content-extraLight: #9798b6;
2117
+
2118
+ --border-soft: rgba(14, 18, 27, 0.08);
2119
+ --border-light: rgba(14, 18, 27, 0.06);
2120
+ }
2121
+
2122
+ /* design tokens (dark) */
2123
+ .dark {
2124
+ --background: #070710;
2125
+ --foreground: hsl(0 0% 98%);
2126
+ --card: #0f0f14;
2127
+ --card-foreground: hsl(0 0% 98%);
2128
+ --popover: #0f0f14;
2129
+ --popover-foreground: hsl(0 0% 98%);
2130
+ --primary: #7064e9;
2131
+ --primary-600: #8c71f6;
2132
+ --primary-foreground: hsl(0 0% 98%);
2133
+ --secondary: hsl(240 3.7% 15.9%);
2134
+ --secondary-foreground: hsl(0 0% 98%);
2135
+ --muted: hsl(240 3.7% 15.9%);
2136
+ --muted-foreground: hsl(240 5% 64.9%);
2137
+ --accent: hsl(240 3.7% 15.9%);
2138
+ --accent-foreground: hsl(0 0% 98%);
2139
+ --destructive: #fb5a76;
2140
+ --destructive-foreground: hsl(0 0% 98%);
2141
+ --success: #22d68a;
2142
+ --warning: #f5be5c;
2143
+ --border: hsl(240 3.7% 15.9%);
2144
+ --input: hsl(240 3.7% 15.9%);
2145
+ --ring: hsl(240 4.9% 83.9%);
2146
+
2147
+ --surface-base-subtle: rgba(204, 206, 239, 0.02);
2148
+ --surface-base-faint: rgba(204, 206, 239, 0.04);
2149
+ --surface-base-light: rgba(204, 206, 239, 0.08);
2150
+ --surface-elevation-light: #0f0f14;
2151
+
2152
+ --content-primary: #cccef0;
2153
+ --content-default: #9798b6;
2154
+ --content-soft: rgba(154, 156, 207, 0.8);
2155
+ --content-extraLight: #9798b6;
2156
+
2157
+ --border-soft: rgba(204, 206, 239, 0.12);
2158
+ --border-light: rgba(204, 206, 239, 0.08);
2159
+ }
2160
+
2161
+ /* theme mapping (Tailwind v4 @theme) */
2162
+ @theme inline {
2163
+ --radius-md: calc(var(--radius) - 2px);
2164
+ --radius-sm: calc(var(--radius) - 4px);
2165
+ --radius-lg: var(--radius);
2166
+
2167
+ --color-background: var(--background);
2168
+ --color-foreground: var(--foreground);
2169
+ --color-card: var(--card);
2170
+ --color-card-foreground: var(--card-foreground);
2171
+ --color-popover: var(--popover);
2172
+ --color-popover-foreground: var(--popover-foreground);
2173
+ --color-primary: var(--primary);
2174
+ --color-primary-600: var(--primary-600);
2175
+ --color-primary-foreground: var(--primary-foreground);
2176
+ --color-secondary: var(--secondary);
2177
+ --color-secondary-foreground: var(--secondary-foreground);
2178
+ --color-muted: var(--muted);
2179
+ --color-muted-foreground: var(--muted-foreground);
2180
+ --color-accent: var(--accent);
2181
+ --color-accent-foreground: var(--accent-foreground);
2182
+ --color-destructive: var(--destructive);
2183
+ --color-destructive-foreground: var(--destructive-foreground);
2184
+ --color-success: var(--success);
2185
+ --color-warning: var(--warning);
2186
+ --color-border: var(--border);
2187
+ --color-input: var(--input);
2188
+ --color-ring: var(--ring);
2189
+
2190
+ --color-surface-base-subtle: var(--surface-base-subtle);
2191
+ --color-surface-base-faint: var(--surface-base-faint);
2192
+ --color-surface-base-light: var(--surface-base-light);
2193
+ --color-surface-elevation-light: var(--surface-elevation-light);
2194
+ --color-surface-base-brand-default: #693ee0;
2195
+ --color-surface-base-brand-strong: #8c71f6;
2196
+
2197
+ --color-content-primary: var(--content-primary);
2198
+ --color-content-default: var(--content-default);
2199
+ --color-content-soft: var(--content-soft);
2200
+ --color-content-extraLight: var(--content-extraLight);
2201
+
2202
+ --color-bdr-soft: var(--border-soft);
2203
+ --color-bdr-light: var(--border-light);
2204
+
2205
+ --color-gradient-primary: linear-gradient(270deg, #7064e9 0%, #dd00ac 100%);
2206
+ }
2207
+
2208
+ @layer base {
2209
+ * {
2210
+ border-color: var(--border);
2211
+ }
2212
+ body {
2213
+ background-color: var(--background);
2214
+ color: var(--foreground);
2215
+ overflow-x: hidden;
2216
+ }
2217
+ html {
2218
+ overflow-x: hidden;
2219
+ }
2220
+ button {
2221
+ cursor: pointer;
2222
+ }
2223
+ button:disabled {
2224
+ cursor: not-allowed;
2225
+ }
2226
+ /* visible keyboard focus across interactive elements */
2227
+ a:focus-visible,
2228
+ button:focus-visible,
2229
+ input:focus-visible,
2230
+ select:focus-visible,
2231
+ textarea:focus-visible {
2232
+ outline: 2px solid var(--primary);
2233
+ outline-offset: 2px;
2234
+ border-radius: 6px;
2235
+ }
2236
+ }
2237
+
2238
+ /* respect reduced-motion: kill non-essential animation */
2239
+ @media (prefers-reduced-motion: reduce) {
2240
+ *,
2241
+ ::before,
2242
+ ::after {
2243
+ animation-duration: 0.001ms !important;
2244
+ animation-iteration-count: 1 !important;
2245
+ transition-duration: 0.001ms !important;
2246
+ scroll-behavior: auto !important;
2247
+ }
2248
+ }
2249
+
2250
+ /* ambient app background (gradient mesh behind everything) */
2251
+ body::before {
2252
+ content: "";
2253
+ position: fixed;
2254
+ inset: 0;
2255
+ z-index: -1;
2256
+ pointer-events: none;
2257
+ background:
2258
+ radial-gradient(60% 50% at 50% -6%, rgba(221, 0, 172, 0.10), transparent 60%),
2259
+ radial-gradient(55% 45% at 12% -8%, rgba(112, 100, 233, 0.14), transparent 60%),
2260
+ radial-gradient(50% 40% at 88% -2%, rgba(112, 100, 233, 0.12), transparent 60%),
2261
+ radial-gradient(45% 45% at 50% 115%, rgba(112, 100, 233, 0.07), transparent 60%);
2262
+ }
2263
+ .dark body::before {
2264
+ background:
2265
+ radial-gradient(60% 50% at 50% -6%, rgba(221, 0, 172, 0.12), transparent 60%),
2266
+ radial-gradient(55% 45% at 12% -8%, rgba(112, 100, 233, 0.18), transparent 60%),
2267
+ radial-gradient(50% 40% at 88% -2%, rgba(112, 100, 233, 0.14), transparent 60%),
2268
+ radial-gradient(45% 45% at 50% 118%, rgba(112, 100, 233, 0.10), transparent 60%);
2269
+ }
2270
+
2271
+ /* minimal scrollbar */
2272
+ ::-webkit-scrollbar {
2273
+ width: 6px;
2274
+ height: 6px;
2275
+ }
2276
+ ::-webkit-scrollbar-track {
2277
+ background: transparent;
2278
+ }
2279
+ ::-webkit-scrollbar-thumb {
2280
+ background: var(--border);
2281
+ border-radius: 3px;
2282
+ }
2283
+ * {
2284
+ scrollbar-width: thin;
2285
+ scrollbar-color: var(--border) transparent;
2286
+ }
2287
+ `;
2288
+ /** Map an `add` target to the route folder its page lives in. */
2289
+ const WEB3_ROUTE_DIR = {
2290
+ "chat-web3": "chat-web3",
2291
+ "inference-web3": "inference-web3",
2292
+ "judge-web3": "judge-web3",
2293
+ };
2294
+ /** The app/page.tsx we drop in to make the generated page the homepage. It
2295
+ * re-exports the real page so its documented /<target> route still works. */
2296
+ function homepageReexport(dir, target) {
2297
+ return `// app/page.tsx
2298
+ // Generated by 'lightnode add ${target}'. Makes the ${dir} page the homepage so
2299
+ // 'npm run dev' + http://localhost:3000 lands on it directly. The page itself
2300
+ // still lives at app/${dir}/page.tsx and is also served at /${dir}.
2301
+ export { default } from "./${dir}/page";
2302
+ `;
2303
+ }
2304
+ /** Add the \`dark\` class to the layout's <html> element so dark stays the
2305
+ * default theme. Returns the patched source, or null if <html> was not found
2306
+ * or already carries a \`dark\` class. Handles className as a template literal,
2307
+ * a string literal, or absent. */
2308
+ function withDarkHtml(source) {
2309
+ const match = source.match(/<html\b[^>]*>/);
2310
+ if (!match)
2311
+ return null;
2312
+ const tag = match[0];
2313
+ // Already dark? Cover both className={`...dark...`} and className="...dark...".
2314
+ if (/className=\{`[^`]*\bdark\b/.test(tag) || /className=(["'])[^"']*\bdark\b/.test(tag))
2315
+ return null;
2316
+ if (/className=\{`/.test(tag)) {
2317
+ return source.replace(tag, tag.replace(/className=\{`/, "className={`dark "));
2318
+ }
2319
+ if (/className=(["'])/.test(tag)) {
2320
+ return source.replace(tag, tag.replace(/className=(["'])/, 'className=$1dark '));
2321
+ }
2322
+ // No className on <html>: add one.
2323
+ return source.replace(tag, tag.replace(/<html\b/, '<html className="dark"'));
2324
+ }
2325
+ function setDarkDefaultOnLayout(cwd) {
2326
+ const abs = findLayoutFile(cwd);
2327
+ if (!abs)
2328
+ return false;
2329
+ let source;
2330
+ try {
2331
+ source = fs.readFileSync(abs, "utf8");
2332
+ }
2333
+ catch {
2334
+ return false;
2335
+ }
2336
+ const patched = withDarkHtml(source);
2337
+ if (patched === null)
2338
+ return false;
2339
+ try {
2340
+ fs.writeFileSync(abs, patched);
2341
+ }
2342
+ catch {
2343
+ return false;
2344
+ }
2345
+ return true;
2346
+ }
2347
+ /**
2348
+ * Wire a freshly scaffolded Next.js app so the generated -web3 page is the
2349
+ * homepage and the LightChain theme + dark default are in place. No-op for any
2350
+ * target without a known route folder.
2351
+ */
2352
+ export function wireFreshScaffold(target, opts = {}) {
2353
+ const cwd = opts.cwd ?? process.cwd();
2354
+ const dir = WEB3_ROUTE_DIR[target];
2355
+ const written = [];
2356
+ if (!dir)
2357
+ return { written, homepageRoute: null, darkDefault: false };
2358
+ // 1. Ship the LightChain theme, replacing the create-next-app starter globals.
2359
+ written.push(writeFile(path.join(cwd, "app/globals.css"), SCAFFOLD_GLOBALS_CSS, true));
2360
+ // 2. Make the generated page the homepage (replaces the starter page.tsx).
2361
+ written.push(writeFile(path.join(cwd, "app/page.tsx"), homepageReexport(dir, target), true));
2362
+ // 3. Keep dark as the default theme (matches lightnode.app).
2363
+ const darkDefault = setDarkDefaultOnLayout(cwd);
2364
+ return { written, homepageRoute: `/${dir}`, darkDefault };
2365
+ }
2061
2366
  const NEXTJS_JUDGE_ROUTE = `// app/api/judge/route.ts
2062
2367
  // Generated by 'lightnode add judge'. See https://lightnode.app/build
2063
2368
  // The LightChallenge-style evaluator: post evidence + criteria, get a
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
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";
3
+ import { addInference, addInferenceWeb3, addJudgeWeb3, addAnalyticsDashboard, addNftMint, addChat, addChatWeb3, addAgent, addJudge, addWagmiSetup, patchLayoutWithProviders, wireFreshScaffold } from "./add.js";
4
4
  import { createPublicClient, createWalletClient, http, parseEther } from "viem";
5
5
  import { privateKeyToAccount, generatePrivateKey } from "viem/accounts";
6
6
  import { existsSync, readdirSync, renameSync, rmSync } from "node:fs";
@@ -579,9 +579,9 @@ async function main() {
579
579
  // A Next.js client page needs a Next.js app to live in. In a bare folder,
580
580
  // scaffold one first so the generated page renders instead of throwing
581
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
- }
582
+ const didScaffold = NEXT_PAGE_TARGETS.has(sub ?? "") && !existsSync(join(cwd, "package.json")) && !noScaffold
583
+ ? scaffoldNextApp(cwd, sub ?? "")
584
+ : false;
585
585
  // ---- write the requested files ----
586
586
  const result = sub === "analytics-dashboard" ? addAnalyticsDashboard({ template, network, force })
587
587
  : sub === "nft-mint-with-inference" ? addNftMint({ template, network, force })
@@ -609,6 +609,18 @@ async function main() {
609
609
  else
610
610
  console.log(` ⤴ ${layout.path} (${layout.reason})`);
611
611
  }
612
+ // ---- fresh scaffold only: make the generated page the homepage and ship
613
+ // the LightChain theme so localhost:3000 lands on the chat, not the
614
+ // create-next-app starter. Skipped in an existing app (nothing to clobber).
615
+ let wiring = null;
616
+ if (didScaffold && isWeb3Page) {
617
+ wiring = wireFreshScaffold(sub, { cwd });
618
+ printWritten(wiring.written);
619
+ if (wiring.homepageRoute)
620
+ console.log(` ✓ app/page.tsx (chat is now the homepage at /)`);
621
+ if (wiring.darkDefault)
622
+ console.log(` ✓ app/layout.tsx (dark theme default)`);
623
+ }
612
624
  // ---- install dependencies (opt out with --no-install) ----
613
625
  const installed = noInstall ? false : installDeps(result.install, cwd);
614
626
  // ---- next steps ----
@@ -620,7 +632,10 @@ async function main() {
620
632
  if (!installed)
621
633
  console.log(` ${result.install}`);
622
634
  console.log(` npm run dev`);
623
- console.log(` open http://localhost:3000${route} and click Connect wallet (chainId ${chainId})`);
635
+ const openPath = wiring ? "" : route; // fresh scaffold serves the page at /
636
+ console.log(` open http://localhost:3000${openPath} and click Connect wallet (chainId ${chainId})`);
637
+ if (wiring)
638
+ console.log(` (also reachable at ${route})`);
624
639
  console.log(` ${result.network === "mainnet" ? "llama3-8b costs 0.02 LCAI per call" : "testnet is free"}`);
625
640
  if (layoutNeedsManual) {
626
641
  console.log(`\nHeads up: couldn't auto-wire the layout (${layout?.reason}).`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lightnode-sdk",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
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",