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 +14 -0
- package/dist/add.js +305 -0
- package/dist/cli.js +20 -5
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
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.
|
|
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",
|