nibs-cli 4.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Charles Nicholson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,355 @@
1
+ # NovelAI Script Build System
2
+
3
+ A CLI tool for building NovelAI scripts from TypeScript files. This tool uses Rollup to bundle multiple TypeScript files into single scripts that can be copied and pasted into the NovelAI script editor.
4
+
5
+ ## Features
6
+
7
+ - **Standalone CLI Tool**: Install globally and use anywhere
8
+ - **Modular Development**: Write scripts across multiple TypeScript files with imports/exports
9
+ - **Automatic Type Definitions**: Fetches the latest NovelAI API types automatically
10
+ - **Type Safety**: Full TypeScript support with IntelliSense and type checking
11
+ - **Watch Mode**: Automatic rebuilds when files change during development
12
+ - **Single File Output**: Each project bundles into one script with no external dependencies
13
+
14
+ ## Quick Start
15
+
16
+ ### Prerequisites
17
+
18
+ - [Node.js](https://nodejs.org/) (v18 or higher)
19
+ - npm (comes with Node.js)
20
+
21
+ ### Installation
22
+
23
+ ```bash
24
+ npm install -g nibs-cli
25
+ ```
26
+
27
+ ### Your First Build
28
+
29
+ ```bash
30
+ nibs build
31
+ ```
32
+
33
+ This builds the current project. Output appears in `dist/`.
34
+
35
+ ## Project Structure
36
+
37
+ ```text
38
+ my-novelai-script/
39
+ ├── src/ # Your TypeScript files
40
+ │ ├── index.ts # Main entry point
41
+ │ └── utils.ts # Utility modules
42
+ ├── project.yaml # Project configuration
43
+ ├── tsconfig.json # TypeScript configuration
44
+ ├── dist/ # Build output (generated)
45
+ │ └── my-script.naiscript # Built script ready for NovelAI
46
+ ├── external/ # Auto-downloaded type definitions
47
+ │ └── script-types.d.ts
48
+ └── package.json # Optional (for your own development dependencies such as prettier formatter)
49
+ ```
50
+
51
+ ## Creating a New Project
52
+
53
+ ### Quick Start (No Configuration)
54
+
55
+ 1. Use the new command:
56
+
57
+ ```bash
58
+ nibs new my-script
59
+ ```
60
+
61
+ This will create a new project in `my-script/` directory with the basic structure.
62
+
63
+ 2. Edit your TypeScript files:
64
+
65
+ ```bash
66
+ # Edit the main script file
67
+ my-script/src/index.ts
68
+ ```
69
+
70
+ 3. Build:
71
+ ```bash
72
+ cd my-script
73
+ nibs build
74
+ ```
75
+
76
+ The build system will auto-discover all `.ts` files in the `src/` folder and bundle them.
77
+
78
+ ### Configuration (project.yaml)
79
+
80
+ Bundling incorporates metadata from a `project.yaml` file in your project folder. This unified configuration matches the YAML frontmatter of `.naiscript` files:
81
+
82
+ ```yaml
83
+ compatibilityVersion: naiscript-1.0
84
+ id: xxxxxxxx-4xxx-xxxx-xxxx-xxxxxxxxxxxxxxxx
85
+ name: my-awesome-script
86
+ version: 1.0.0
87
+ author: Your Name <your.email@example.com>
88
+ description: My amazing NovelAI script
89
+ memoryLimit: 8
90
+ createdAt: 1234567890123
91
+ updatedAt: 1234567894564
92
+ config:
93
+ - # Custom configuration items here
94
+ ```
95
+
96
+ **Fields:**
97
+
98
+ - `compatibilityVersion` - NAIScript compatibility version (always "naiscript-1.0")
99
+ - `id` - Unique ID required to update your script. Don't change.
100
+ - `name` - Output filename (creates `dist/{kebab-name}.naiscript`)
101
+ - `version` - Script version (appears in header)
102
+ - `author` - Your name/email (appears in header)
103
+ - `description` - Script description (appears in header)
104
+ - `memoryLimit` - How many megabytes of in-browser local-storage memory your script can use for storage. Maximum 128.
105
+ - `createdAt` - Timestamp of when your script was created
106
+ - `updatedAt` - Timestamp of when script was updated. Automatically updated on builds
107
+ - `config` - Array of custom configuration items (replaces config.yaml)
108
+
109
+ ## CLI Commands
110
+
111
+ | Command | Description |
112
+ |---------|-------------|
113
+ | `nibs new <directory>` | Create a new project |
114
+ | `nibs build [directory]` | Build project (default command) |
115
+ | `nibs watch [directory]` | Watch project and rebuild on changes |
116
+ | `nibs help` | Show help information |
117
+
118
+ ## Writing Scripts with Imports
119
+
120
+ You can split your code across multiple files using imports/exports. The build system removes all module syntax in the final output.
121
+
122
+ ### Named Imports (Traditional Style)
123
+
124
+ **src/utils.ts**
125
+
126
+ ```typescript
127
+ export interface Config {
128
+ enabled: boolean;
129
+ debugMode: boolean;
130
+ }
131
+
132
+ export async function saveConfig(config: Config) {
133
+ await api.v1.storage.set("config", config);
134
+ }
135
+
136
+ export function log(message: string) {
137
+ api.v1.log(`[MyScript] ${message}`);
138
+ }
139
+ ```
140
+
141
+ **src/index.ts**
142
+
143
+ ```typescript
144
+ import type { Config } from "./utils";
145
+ import { saveConfig, log } from "./utils";
146
+
147
+ const config: Config = {
148
+ enabled: true,
149
+ debugMode: false,
150
+ };
151
+
152
+ async function init() {
153
+ log("Starting...");
154
+ await saveConfig(config);
155
+ }
156
+
157
+ init();
158
+ ```
159
+
160
+ ### Namespace Imports
161
+
162
+ You can also use namespace imports (`import * as name`) to keep track of where functions come from:
163
+
164
+ **src/index.ts**
165
+
166
+ ```typescript
167
+ import type { Config } from "./utils";
168
+ import * as utils from "./utils";
169
+
170
+ const config: Config = {
171
+ enabled: true,
172
+ debugMode: false,
173
+ };
174
+
175
+ async function init() {
176
+ utils.log("Starting...");
177
+ await utils.saveConfig(config);
178
+ }
179
+
180
+ init();
181
+ ```
182
+
183
+ The build system automatically generates a namespace wrapper object using Rollup:
184
+
185
+ ```typescript
186
+ const utils = {
187
+ saveConfig,
188
+ log,
189
+ };
190
+ ```
191
+
192
+ You can mix both styles in the same file if needed.
193
+
194
+ ## NovelAI API Reference
195
+
196
+ The build system automatically downloads NovelAI type definitions. In supported editors you get full completion and IDE documentation.
197
+
198
+ **Official Documentation**: [NovelAI Scripting Docs](https://docs.novelai.net/scripting/introduction.html)
199
+
200
+ ## Example Projects
201
+
202
+ Two example projects are included to demonstrate different use cases:
203
+
204
+ ### example-script (Full-Featured)
205
+
206
+ Located in `examples/example-script/`, this demonstrates:
207
+
208
+ - **Modular code organization** with imports/exports across files
209
+ - **UI
210
+ extensions** - Toolbar buttons with callbacks
211
+ - **Generation hooks** - Intercept and modify generation
212
+ - **Persistent storage** - Track data across script loads
213
+ - **Lorebook API** - Query and filter entries
214
+ - **Type safety** - TypeScript interfaces
215
+
216
+ ### word-counter (Simple)
217
+
218
+ Located in `examples/word-counter/`, this demonstrates:
219
+
220
+ - **Single-file script** - Simple project structure
221
+ - **Document API** - Reading story text
222
+ - **Editor API** - Getting text selection
223
+ - **UI toasts** - Displaying information to users
224
+ - **Toolbar buttons** - Adding custom UI
225
+
226
+ ## Tips and Best Practices
227
+
228
+ ### Use Watch Mode During Development
229
+
230
+ ```bash
231
+ nibs watch
232
+ ```
233
+
234
+ Auto-rebuilds when you save. Keep it running while you work!
235
+
236
+ ### Debugging
237
+
238
+ ```typescript
239
+ // Use api.v1.log, not console.log
240
+ api.v1.log("Debug:", someVariable);
241
+ api.v1.error("Error:", error);
242
+ ```
243
+
244
+ Output appears in browser console (F12).
245
+
246
+ ### Code Organization
247
+
248
+ **Simple script:**
249
+
250
+ ```text
251
+ src/
252
+ └── index.ts
253
+ ```
254
+
255
+ **Medium script:**
256
+
257
+ ```text
258
+ src/
259
+ ├── utils.ts
260
+ └── index.ts
261
+ ```
262
+
263
+ **Complex script:**
264
+
265
+ ```text
266
+ src/
267
+ ├── types.ts
268
+ ├── config.ts
269
+ ├── storage.ts
270
+ ├── hooks.ts
271
+ ├── ui.ts
272
+ └── index.ts
273
+ ```
274
+
275
+ ## Troubleshooting
276
+
277
+ ### "Cannot find module" errors
278
+
279
+ 1. Run `npm install`
280
+ 2. Restart your TypeScript language server
281
+ 3. Run `nibs build` to download type definitions
282
+
283
+ ### Type definitions not found
284
+
285
+ ```bash
286
+ rm -rf external/
287
+ nibs build
288
+ ```
289
+
290
+ ### Script doesn't work in NovelAI
291
+
292
+ 1. Check browser console (F12) for errors
293
+ 2. Verify script is enabled in NovelAI
294
+ 3. Check that your TypeScript files compile correctly
295
+ 4. Verify that your scripts don't use unsupported browser APIs
296
+
297
+ ### Build errors
298
+
299
+ 1. Check for TypeScript syntax errors in your source files
300
+ 2. Make sure all imported files exist and have correct extensions
301
+ 3. Verify that you're using the latest version of the build system:
302
+ ```bash
303
+ npm install -g nibs-cli
304
+ ```
305
+ 4. For Rollup-related issues, try a clean build:
306
+ ```bash
307
+ rm -rf dist/
308
+ nibs build
309
+ ```
310
+
311
+ ## FAQ
312
+
313
+ ### Can I use npm packages?
314
+
315
+ No. NovelAI scripts run in the browser without npm access. Use only:
316
+
317
+ - Browser-native APIs
318
+ - The NovelAI API (`api.v1.*`)
319
+ - Your own code
320
+
321
+ ### How do I add another project?
322
+
323
+ Use the CLI command `nibs new <directory>`. Or you may manually create a new folder with a `src/` subdirectory containing your TypeScript files.
324
+
325
+ ### Legacy Projects (v3.x and earlier)
326
+
327
+ If you're upgrading from the older npm-run-script based version:
328
+
329
+ 1. **Install the CLI tool**:
330
+ ```bash
331
+ npm install -g nibs-cli
332
+ ```
333
+
334
+ 2. **Move your projects** out of the repository's `projects/` folder and into your own source control repository
335
+
336
+ 3. **Update your workflow**:
337
+ - Old: `npm run build -- my-project`
338
+ - New: `cd my-project && nibs build`
339
+
340
+ The old multi-project-folder structure is no longer supported. Each script should now be managed in its own directory.
341
+
342
+ ## License
343
+
344
+ MIT License - feel free to use this CLI tool for your NovelAI script projects!
345
+
346
+ ## Resources
347
+
348
+ - **[NovelAI Scripting Documentation](https://docs.novelai.net/scripting/introduction.html)**
349
+ - **[NovelAI API Reference](https://docs.novelai.net/en/scripting/api-reference)**
350
+ - **[TypeScript Documentation](https://www.typescriptlang.org/docs/)**
351
+ - **[NovelAI Discord](https://discord.gg/novelai)**
352
+
353
+ ---
354
+
355
+ **Ready to start?** Run `nibs new my-script` and start building your NovelAI scripts!
package/dist/cli.js ADDED
@@ -0,0 +1,76 @@
1
+ #! /usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const promises_1 = require("fs/promises");
6
+ const path_1 = require("path");
7
+ const process_1 = require("process");
8
+ const build_1 = require("./commands/build");
9
+ const new_1 = require("./commands/new");
10
+ const project_1 = require("./commands/project");
11
+ const watch_1 = require("./commands/watch");
12
+ const fetch_1 = require("./utils/fetch");
13
+ // Helpers
14
+ async function ensureTypesFile(projectPath) {
15
+ try {
16
+ const stats = await (0, promises_1.stat)((0, path_1.join)(projectPath, "external", "script-types.d.ts"));
17
+ const age = Date.now() - stats.mtimeMs;
18
+ const hoursOld = age / (1000 * 60 * 60);
19
+ if (hoursOld < 24) {
20
+ console.log("✓ Using cached NovelAI type definitions");
21
+ }
22
+ else {
23
+ await (0, fetch_1.fetchExternalTypes)(projectPath);
24
+ }
25
+ }
26
+ catch (err) {
27
+ await (0, fetch_1.fetchExternalTypes)(projectPath);
28
+ }
29
+ }
30
+ commander_1.program.description("NovelAI Script Build System").version("4.0.0");
31
+ commander_1.program.command("new [directory]").action((directory = ".") => {
32
+ const projectPath = (0, path_1.resolve)((0, process_1.cwd)(), directory);
33
+ (0, new_1.createNewProject)(projectPath);
34
+ });
35
+ commander_1.program
36
+ .command("build [directory]", { isDefault: true })
37
+ .description("Build project")
38
+ .action(async (directory = ".") => {
39
+ const projectPath = (0, path_1.resolve)((0, process_1.cwd)(), directory);
40
+ await ensureTypesFile(projectPath);
41
+ try {
42
+ const project = await (0, project_1.loadProject)(projectPath);
43
+ await (0, build_1.buildProject)(project);
44
+ console.log(`\n✅ Build complete!`);
45
+ // Show output file size
46
+ const outputPath = (0, path_1.join)((0, path_1.join)(projectPath, "dist"), `${project.name}.naiscript`);
47
+ const stats = await (0, promises_1.stat)(outputPath);
48
+ const sizeKB = (stats.size / 1024).toFixed(2);
49
+ console.log(`✅ Built: dist/${project.name}.naiscript (${sizeKB} KB)`);
50
+ process.exit(0);
51
+ }
52
+ catch (err) {
53
+ console.log("Build error:", err);
54
+ process.exit(1);
55
+ }
56
+ });
57
+ commander_1.program
58
+ .command("watch [directory]")
59
+ .description("Automatically watch and rebuild project on changes.")
60
+ .action(async (directory = ".") => {
61
+ const projectPath = (0, path_1.resolve)((0, process_1.cwd)(), directory);
62
+ await ensureTypesFile(projectPath);
63
+ try {
64
+ const project = await (0, project_1.loadProject)(projectPath);
65
+ const watcher = (0, watch_1.watchProject)(project);
66
+ ["SIGINT", "SIGTERM", "SIGQUIT"].forEach((signal) => process.on(signal, () => {
67
+ console.log(" Cleaning up, closing watcher");
68
+ watcher.close();
69
+ process.exit(0);
70
+ }));
71
+ }
72
+ catch (err) {
73
+ console.log(`Watch error: ${err.message}`);
74
+ }
75
+ });
76
+ commander_1.program.parse();
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildProject = buildProject;
4
+ const promises_1 = require("fs/promises");
5
+ const path_1 = require("path");
6
+ const rollup_1 = require("rollup");
7
+ const project_1 = require("./project");
8
+ const rollup_config_1 = require("./rollup-config");
9
+ async function buildProject(project) {
10
+ const { name, path, meta } = project;
11
+ console.log(`\n🔨 Building project: ${name}`);
12
+ meta.set("updatedAt", (0, project_1.currentEpochS)());
13
+ await (0, rollup_1.rollup)((0, rollup_config_1.rollupInputOptions)(project)).then((bundle) => bundle.write((0, rollup_config_1.rollupOutputOptions)(project)).then(() => bundle.close()));
14
+ // Write new project.yaml file
15
+ await (0, promises_1.writeFile)((0, path_1.join)(path, "project.yaml"), meta.toString()).catch(console.error);
16
+ return true;
17
+ }