nibs-cli 4.0.0 → 4.2.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 +70 -8
- package/dist/cli.js +23 -4
- package/dist/commands/import.js +29 -0
- package/dist/commands/new.js +2 -23
- package/dist/commands/rollup-config.js +2 -0
- package/dist/utils.js +54 -0
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -48,11 +48,22 @@ my-novelai-script/
|
|
|
48
48
|
└── package.json # Optional (for your own development dependencies such as prettier formatter)
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
##
|
|
51
|
+
## Usage
|
|
52
52
|
|
|
53
|
-
###
|
|
53
|
+
### Importing an existing project
|
|
54
54
|
|
|
55
|
-
1. Use the
|
|
55
|
+
1. Use the `import` command:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
nibs import my-script.naiscript
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
This will create a `my-script/` directory, importing the `project.yaml` and `src/index.ts` from the `.naiscript` file. You can immediately `cd` into that directory and `nibs build`.
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
### Creating a New Project
|
|
65
|
+
|
|
66
|
+
1. Use the `new` command:
|
|
56
67
|
|
|
57
68
|
```bash
|
|
58
69
|
nibs new my-script
|
|
@@ -113,6 +124,7 @@ config:
|
|
|
113
124
|
| `nibs new <directory>` | Create a new project |
|
|
114
125
|
| `nibs build [directory]` | Build project (default command) |
|
|
115
126
|
| `nibs watch [directory]` | Watch project and rebuild on changes |
|
|
127
|
+
| `nibs import <file>` | Import an existing .naiscript and create a project directory |
|
|
116
128
|
| `nibs help` | Show help information |
|
|
117
129
|
|
|
118
130
|
## Writing Scripts with Imports
|
|
@@ -191,6 +203,60 @@ const utils = {
|
|
|
191
203
|
|
|
192
204
|
You can mix both styles in the same file if needed.
|
|
193
205
|
|
|
206
|
+
## Using npm Packages
|
|
207
|
+
|
|
208
|
+
NIBS can bundle npm packages into your scripts. Since the build system uses Rollup with node module resolution, any package you install in your project directory can be imported and inlined into the final `.naiscript` output.
|
|
209
|
+
|
|
210
|
+
### Setup
|
|
211
|
+
|
|
212
|
+
Initialize a `package.json` in your project directory and install packages:
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
cd my-script
|
|
216
|
+
npm init -y
|
|
217
|
+
npm install lodash-es
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Then import them in your TypeScript files:
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import { debounce } from "lodash-es";
|
|
224
|
+
|
|
225
|
+
const debouncedSave = debounce(async () => {
|
|
226
|
+
await api.v1.storage.set("data", myData);
|
|
227
|
+
}, 500);
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
The build system resolves the import from `node_modules/`, inlines the package code, and produces a single self-contained `.naiscript` file with no external dependencies.
|
|
231
|
+
|
|
232
|
+
### Runtime Constraints
|
|
233
|
+
|
|
234
|
+
**NovelAI scripts run inside a Web Worker sandbox powered by the QuickJS runtime.** This is a very different environment from Node.js or a browser page. Packages you use must be compatible with these constraints:
|
|
235
|
+
|
|
236
|
+
- **No Node.js APIs** - `fs`, `path`, `http`, `child_process`, `Buffer`, and all other Node.js built-in modules are unavailable
|
|
237
|
+
- **No DOM APIs** - `document`, `window`, `localStorage`, and other browser page APIs are unavailable (scripts run in a Web Worker, not a page)
|
|
238
|
+
- **No network access** - `fetch`, `XMLHttpRequest`, and WebSocket are unavailable
|
|
239
|
+
- **QuickJS engine** - The JavaScript engine is QuickJS, not V8. Most ES2023 features are supported, but some newer APIs or V8-specific behaviors may not be available
|
|
240
|
+
|
|
241
|
+
### What Works
|
|
242
|
+
|
|
243
|
+
- **Pure logic packages** - Algorithms, data structures, parsers, math libraries, string manipulation, validation
|
|
244
|
+
- **ESM packages** - Packages that ship ES module builds (check for `"module"` or `"exports"` fields in the package's `package.json`)
|
|
245
|
+
|
|
246
|
+
### What Won't Work
|
|
247
|
+
|
|
248
|
+
- Packages that depend on Node.js built-in modules (e.g., `axios`, `fs-extra`)
|
|
249
|
+
- Packages that access the DOM (e.g., `react`, `jquery`)
|
|
250
|
+
- Packages that use network APIs (e.g., `node-fetch`, `socket.io-client`)
|
|
251
|
+
- Packages that ship only CommonJS builds (no ESM entry point) — these may fail to bundle correctly
|
|
252
|
+
|
|
253
|
+
### Tips
|
|
254
|
+
|
|
255
|
+
- Check a package's dependencies and source before installing — if it imports `fs`, `http`, or `path`, it won't work
|
|
256
|
+
- Prefer packages with `-es` or `esm` variants (e.g., `lodash-es` instead of `lodash`)
|
|
257
|
+
- Keep your bundle small — every dependency gets inlined into the final script
|
|
258
|
+
- Test your built `.naiscript` in NovelAI after adding new packages to catch runtime incompatibilities early
|
|
259
|
+
|
|
194
260
|
## NovelAI API Reference
|
|
195
261
|
|
|
196
262
|
The build system automatically downloads NovelAI type definitions. In supported editors you get full completion and IDE documentation.
|
|
@@ -312,11 +378,7 @@ nibs build
|
|
|
312
378
|
|
|
313
379
|
### Can I use npm packages?
|
|
314
380
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
- Browser-native APIs
|
|
318
|
-
- The NovelAI API (`api.v1.*`)
|
|
319
|
-
- Your own code
|
|
381
|
+
Yes, as of v4.2! Install packages with `npm install` in your project directory and import them normally. The build system bundles them into your `.naiscript` file. However, NovelAI scripts run in a **Web Worker sandbox under the QuickJS runtime**, so packages must be pure JavaScript logic with no Node.js, DOM, or network dependencies. See [Using npm Packages](#using-npm-packages) for details.
|
|
320
382
|
|
|
321
383
|
### How do I add another project?
|
|
322
384
|
|
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,8 @@ const build_1 = require("./commands/build");
|
|
|
9
9
|
const new_1 = require("./commands/new");
|
|
10
10
|
const project_1 = require("./commands/project");
|
|
11
11
|
const watch_1 = require("./commands/watch");
|
|
12
|
-
const
|
|
12
|
+
const utils_1 = require("./utils");
|
|
13
|
+
const import_1 = require("./commands/import");
|
|
13
14
|
// Helpers
|
|
14
15
|
async function ensureTypesFile(projectPath) {
|
|
15
16
|
try {
|
|
@@ -20,14 +21,14 @@ async function ensureTypesFile(projectPath) {
|
|
|
20
21
|
console.log("✓ Using cached NovelAI type definitions");
|
|
21
22
|
}
|
|
22
23
|
else {
|
|
23
|
-
await (0,
|
|
24
|
+
await (0, utils_1.fetchExternalTypes)(projectPath);
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
catch (err) {
|
|
27
|
-
await (0,
|
|
28
|
+
await (0, utils_1.fetchExternalTypes)(projectPath);
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
|
-
commander_1.program.description("NovelAI Script Build System").version("4.
|
|
31
|
+
commander_1.program.description("NovelAI Script Build System").version("4.2.0");
|
|
31
32
|
commander_1.program.command("new [directory]").action((directory = ".") => {
|
|
32
33
|
const projectPath = (0, path_1.resolve)((0, process_1.cwd)(), directory);
|
|
33
34
|
(0, new_1.createNewProject)(projectPath);
|
|
@@ -73,4 +74,22 @@ commander_1.program
|
|
|
73
74
|
console.log(`Watch error: ${err.message}`);
|
|
74
75
|
}
|
|
75
76
|
});
|
|
77
|
+
commander_1.program
|
|
78
|
+
.command("import <naiscript>")
|
|
79
|
+
.description("Import a naiscript file and initialize a new directory with the decompiled script.")
|
|
80
|
+
.action(async (naiscript) => {
|
|
81
|
+
const filePath = (0, path_1.resolve)((0, process_1.cwd)(), naiscript);
|
|
82
|
+
await (0, promises_1.access)(filePath, promises_1.constants.R_OK).catch(() => {
|
|
83
|
+
console.log(`File ${naiscript} is either not readable or doesn't exist.`);
|
|
84
|
+
(0, process_1.exit)(1);
|
|
85
|
+
});
|
|
86
|
+
if (!naiscript.endsWith(".naiscript")) {
|
|
87
|
+
console.log(`File ${naiscript} is not a '.naiscript' file.`);
|
|
88
|
+
(0, process_1.exit)(1);
|
|
89
|
+
}
|
|
90
|
+
await (0, import_1.importNaiscript)(filePath).catch((err) => {
|
|
91
|
+
console.log(`Error importing ${naiscript}: ${err.message}`);
|
|
92
|
+
(0, process_1.exit)(2);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
76
95
|
commander_1.program.parse();
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.importNaiscript = importNaiscript;
|
|
4
|
+
const promises_1 = require("fs/promises");
|
|
5
|
+
const posix_1 = require("path/posix");
|
|
6
|
+
const yaml_1 = require("yaml");
|
|
7
|
+
const prettier_1 = require("prettier");
|
|
8
|
+
const utils_1 = require("../utils");
|
|
9
|
+
const FRONTMATTER = /\/\*---\n([\s\S]+)---\*\/([\s\S]+)/;
|
|
10
|
+
async function importNaiscript(filePath) {
|
|
11
|
+
const naiScriptBuf = await (0, promises_1.readFile)(filePath);
|
|
12
|
+
const naiScript = naiScriptBuf.toString();
|
|
13
|
+
const matches = FRONTMATTER.exec(naiScript);
|
|
14
|
+
if (!matches)
|
|
15
|
+
throw new Error("Unable to parse naiscript.");
|
|
16
|
+
const frontMatter = (0, yaml_1.parseDocument)(matches[1]);
|
|
17
|
+
const script = matches[2];
|
|
18
|
+
const formattedScript = await (0, prettier_1.format)(script, { parser: "typescript" });
|
|
19
|
+
const name = frontMatter.get("name");
|
|
20
|
+
const projectDir = (0, posix_1.join)((0, posix_1.dirname)(filePath), kebab(name));
|
|
21
|
+
await (0, promises_1.mkdir)((0, posix_1.join)(projectDir, "src"), { recursive: true });
|
|
22
|
+
await (0, promises_1.writeFile)((0, posix_1.join)(projectDir, "project.yaml"), frontMatter.toString());
|
|
23
|
+
await (0, promises_1.writeFile)((0, posix_1.join)(projectDir, "src", "index.ts"), formattedScript);
|
|
24
|
+
await (0, utils_1.writeTsConfig)(projectDir);
|
|
25
|
+
}
|
|
26
|
+
// Helpers
|
|
27
|
+
function kebab(a) {
|
|
28
|
+
return a.toLocaleLowerCase().replaceAll(/\W/g, "-").replaceAll(/-+/g, "-");
|
|
29
|
+
}
|
package/dist/commands/new.js
CHANGED
|
@@ -43,32 +43,11 @@ const inquirer_1 = __importDefault(require("inquirer"));
|
|
|
43
43
|
const path_1 = require("path");
|
|
44
44
|
const yaml_1 = __importStar(require("yaml"));
|
|
45
45
|
const project_1 = require("./project");
|
|
46
|
+
const utils_1 = require("../utils");
|
|
46
47
|
// Constants
|
|
47
48
|
const INDEX_TS_TEMPLATE = `(async () => {
|
|
48
49
|
api.v1.log("Hello World!");
|
|
49
50
|
})();`;
|
|
50
|
-
const TSCONFIG = {
|
|
51
|
-
compilerOptions: {
|
|
52
|
-
target: "ES2023",
|
|
53
|
-
module: "ESNext",
|
|
54
|
-
lib: ["ES2023"],
|
|
55
|
-
moduleResolution: "bundler",
|
|
56
|
-
strict: true,
|
|
57
|
-
esModuleInterop: true,
|
|
58
|
-
skipLibCheck: true,
|
|
59
|
-
forceConsistentCasingInFileNames: true,
|
|
60
|
-
allowSyntheticDefaultImports: true,
|
|
61
|
-
noUnusedLocals: true,
|
|
62
|
-
noUnusedParameters: true,
|
|
63
|
-
noImplicitReturns: true,
|
|
64
|
-
noFallthroughCasesInSwitch: true,
|
|
65
|
-
noEmit: true,
|
|
66
|
-
typeRoots: ["./external", "./node_modules/@types"],
|
|
67
|
-
rewriteRelativeImportExtensions: true,
|
|
68
|
-
},
|
|
69
|
-
include: ["src/**/*", "external/**/*"],
|
|
70
|
-
exclude: ["node_modules", "dist", "**/*.test.ts"],
|
|
71
|
-
};
|
|
72
51
|
const COMPAT_VERSION = "naiscript-1.0";
|
|
73
52
|
// Helpers
|
|
74
53
|
async function checkExistingProject(projectPath) {
|
|
@@ -130,7 +109,7 @@ async function createNewProject(projectPath) {
|
|
|
130
109
|
await Promise.all([
|
|
131
110
|
(0, promises_1.writeFile)((0, path_1.join)(projectPath, "project.yaml"), yaml_1.default.stringify(project.meta), "utf-8"),
|
|
132
111
|
(0, promises_1.writeFile)((0, path_1.join)(projectPath, "src", "index.ts"), INDEX_TS_TEMPLATE, "utf-8"),
|
|
133
|
-
(0,
|
|
112
|
+
(0, utils_1.writeTsConfig)(projectPath),
|
|
134
113
|
]);
|
|
135
114
|
console.log(`Project initialized at ${projectPath}`);
|
|
136
115
|
}
|
|
@@ -5,12 +5,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.rollupInputOptions = rollupInputOptions;
|
|
7
7
|
exports.rollupOutputOptions = rollupOutputOptions;
|
|
8
|
+
const plugin_node_resolve_1 = require("@rollup/plugin-node-resolve");
|
|
8
9
|
const plugin_typescript_1 = __importDefault(require("@rollup/plugin-typescript"));
|
|
9
10
|
const path_1 = require("path");
|
|
10
11
|
function rollupInputOptions(project) {
|
|
11
12
|
return {
|
|
12
13
|
input: (0, path_1.join)(project.path, "src", "index.ts"),
|
|
13
14
|
plugins: [
|
|
15
|
+
(0, plugin_node_resolve_1.nodeResolve)(),
|
|
14
16
|
{
|
|
15
17
|
name: "watch-project-yaml",
|
|
16
18
|
buildStart() {
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchExternalTypes = fetchExternalTypes;
|
|
4
|
+
exports.writeTsConfig = writeTsConfig;
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const promises_1 = require("fs/promises");
|
|
7
|
+
const path_1 = require("path");
|
|
8
|
+
const posix_1 = require("path/posix");
|
|
9
|
+
const promises_2 = require("stream/promises");
|
|
10
|
+
const NAI_TYPES_URL = "https://novelai.net/scripting/types/script-types.d.ts";
|
|
11
|
+
async function fetchExternalTypes(projectPath) {
|
|
12
|
+
const outputPath = (0, path_1.join)(projectPath, "external", "script-types.d.ts");
|
|
13
|
+
await (0, promises_1.mkdir)((0, posix_1.dirname)(outputPath), { recursive: true });
|
|
14
|
+
console.log("📥 Fetching NovelAI type definitions...");
|
|
15
|
+
const res = await fetch(NAI_TYPES_URL);
|
|
16
|
+
if (!res.ok) {
|
|
17
|
+
throw new Error(`Failed to fetch types: HTTP ${res.status}, ${res.statusText}`);
|
|
18
|
+
}
|
|
19
|
+
else if (!res.body) {
|
|
20
|
+
throw new Error("Got result, but body is empty");
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
await (0, promises_2.pipeline)(res.body, (0, fs_1.createWriteStream)(outputPath));
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
console.error(err);
|
|
27
|
+
throw err;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const TSCONFIG = {
|
|
31
|
+
compilerOptions: {
|
|
32
|
+
target: "ES2023",
|
|
33
|
+
module: "ESNext",
|
|
34
|
+
lib: ["ES2023"],
|
|
35
|
+
moduleResolution: "bundler",
|
|
36
|
+
strict: true,
|
|
37
|
+
esModuleInterop: true,
|
|
38
|
+
skipLibCheck: true,
|
|
39
|
+
forceConsistentCasingInFileNames: true,
|
|
40
|
+
allowSyntheticDefaultImports: true,
|
|
41
|
+
noUnusedLocals: true,
|
|
42
|
+
noUnusedParameters: true,
|
|
43
|
+
noImplicitReturns: true,
|
|
44
|
+
noFallthroughCasesInSwitch: true,
|
|
45
|
+
noEmit: true,
|
|
46
|
+
typeRoots: ["./external", "./node_modules/@types"],
|
|
47
|
+
rewriteRelativeImportExtensions: true,
|
|
48
|
+
},
|
|
49
|
+
include: ["src/**/*", "external/**/*"],
|
|
50
|
+
exclude: ["node_modules", "dist", "**/*.test.ts"],
|
|
51
|
+
};
|
|
52
|
+
function writeTsConfig(projectPath) {
|
|
53
|
+
return (0, promises_1.writeFile)((0, path_1.join)(projectPath, "tsconfig.json"), JSON.stringify(TSCONFIG, undefined, 2), "utf-8");
|
|
54
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nibs-cli",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"description": "Project build tool for NovelAI scripts - bundles TypeScript files into single scripts",
|
|
5
5
|
"bin": {
|
|
6
6
|
"nibs": "dist/cli.js"
|
|
@@ -21,16 +21,20 @@
|
|
|
21
21
|
"node": ">= 18"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"prettier": "^3.7.4",
|
|
25
24
|
"@types/node": "^25.0.0",
|
|
26
25
|
"@types/rollup": "^0.51.4",
|
|
27
26
|
"typescript": "^5.3.0"
|
|
28
27
|
},
|
|
29
28
|
"dependencies": {
|
|
29
|
+
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
30
30
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
31
31
|
"commander": "^14.0.2",
|
|
32
32
|
"inquirer": "^13.0.2",
|
|
33
|
+
"prettier": "^3.7.4",
|
|
33
34
|
"rollup": "^4.53.3",
|
|
34
35
|
"yaml": "^2.8.2"
|
|
36
|
+
},
|
|
37
|
+
"repository": {
|
|
38
|
+
"url": "https://github.com/LaneRendell/NovelAI_Script_BuildSystem"
|
|
35
39
|
}
|
|
36
40
|
}
|