@nocville/npc 0.3.1 → 0.4.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 +19 -0
- package/dist/cjs/cli.js +21 -2
- package/dist/cjs/core/base-npc.js +1 -0
- package/dist/cjs/core/config.js +23 -1
- package/dist/cjs/core/user-config.js +2 -0
- package/dist/cjs/doctor.js +19 -0
- package/dist/cjs/npcs/agent-common/session.js +3 -0
- package/dist/cjs/npcs/codex/watcher.js +1 -0
- package/dist/cli.js +21 -2
- package/dist/core/base-npc.js +1 -0
- package/dist/core/config.js +23 -1
- package/dist/core/types.d.ts +2 -0
- package/dist/core/user-config.d.ts +1 -0
- package/dist/core/user-config.js +2 -0
- package/dist/doctor.js +19 -0
- package/dist/npcs/agent-common/session.js +3 -0
- package/dist/npcs/codex/watcher.js +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -226,3 +226,22 @@ class MyNPC extends PollingNPC {
|
|
|
226
226
|
|
|
227
227
|
`nocville-npc --help` lists every command and flag, and `nocville-npc doctor` checks that your
|
|
228
228
|
key and connection work. More at [nocville.com](https://nocville.com).
|
|
229
|
+
|
|
230
|
+
## Encrypted Cloudflare workspaces
|
|
231
|
+
|
|
232
|
+
Download and verify the producer workspace settings in team privacy settings, then:
|
|
233
|
+
|
|
234
|
+
```sh
|
|
235
|
+
nocville-npc init --workspace=/absolute/path/workspace.json
|
|
236
|
+
# Supply the agent key through NOCVILLE_API_KEY or the existing init prompt.
|
|
237
|
+
nocville-npc system-monitor
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
`--workspace`, `NOCVILLE_WORKSPACE`, and the saved workspace path are supported in
|
|
241
|
+
that order. Constructor `workspace` overrides them. The first publication displays
|
|
242
|
+
a signing-device fingerprint for the team owner to approve. Approval is shared by
|
|
243
|
+
the local NPCs using this credential, including coding-agent hooks and watchdogs.
|
|
244
|
+
The workspace pins the HTTPS origin and opaque map ID; changing either requires
|
|
245
|
+
matching verified workspace settings. The `doctor` command uses encrypted enrollment
|
|
246
|
+
and an offline probe for these workspaces. The Node SDK README describes private
|
|
247
|
+
profile storage and the encryption boundary.
|
package/dist/cjs/cli.js
CHANGED
|
@@ -96,6 +96,7 @@ Monitoring NPC types:
|
|
|
96
96
|
web-page-watcher Render a watched web page as a pixel-art TV
|
|
97
97
|
|
|
98
98
|
Common Options:
|
|
99
|
+
--workspace=<file> Verified producer workspace settings from team privacy settings.
|
|
99
100
|
--api-key=<key> Team API key (or NOCVILLE_API_KEY env)
|
|
100
101
|
--api-url=<url> Nocville API URL (or NOCVILLE_API_URL env).
|
|
101
102
|
Default: https://nocville.com. For local dev use
|
|
@@ -512,7 +513,20 @@ async function runInit(flags, config) {
|
|
|
512
513
|
let apiKey = (0, config_js_1.stringFlag)(flags, 'api-key');
|
|
513
514
|
const apiUrl = (0, config_js_1.stringFlag)(flags, 'api-url');
|
|
514
515
|
const map = (0, config_js_1.stringFlag)(flags, 'map');
|
|
515
|
-
|
|
516
|
+
const workspace = config.workspace;
|
|
517
|
+
if (workspace)
|
|
518
|
+
apiKey = apiKey || config.apiKey;
|
|
519
|
+
if (workspace && !apiKey) {
|
|
520
|
+
const { resolveApiKey } = await Promise.resolve().then(() => __importStar(require('./core/api-key.js')));
|
|
521
|
+
const resolved = await resolveApiKey({ flags });
|
|
522
|
+
if (!resolved.ok) {
|
|
523
|
+
console.error(resolved.error);
|
|
524
|
+
process.exitCode = 1;
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
apiKey = resolved.key;
|
|
528
|
+
}
|
|
529
|
+
if (!apiKey && !apiUrl && !map && !workspace) {
|
|
516
530
|
const saved = loadUserConfig();
|
|
517
531
|
if (saved.apiKey && !process.env['NOCVILLE_API_KEY']?.trim()) {
|
|
518
532
|
console.log('Usage: nocville-npc init --api-key=<key> [--api-url=<url>] [--map=<name>]');
|
|
@@ -528,7 +542,12 @@ async function runInit(flags, config) {
|
|
|
528
542
|
}
|
|
529
543
|
apiKey = resolved.key;
|
|
530
544
|
}
|
|
531
|
-
const path = saveUserConfig({
|
|
545
|
+
const path = saveUserConfig({
|
|
546
|
+
apiKey,
|
|
547
|
+
apiUrl: workspace ? config.apiUrl : apiUrl,
|
|
548
|
+
map: workspace ? config.map : map,
|
|
549
|
+
workspace,
|
|
550
|
+
});
|
|
532
551
|
const saved = loadUserConfig();
|
|
533
552
|
console.log(`✓ Saved ${path} (mode 0600)`);
|
|
534
553
|
console.log(` API: ${saved.apiUrl ?? 'https://nocville.com'} key: ${maskKey(saved.apiKey)}`);
|
|
@@ -17,6 +17,7 @@ class BaseNPC {
|
|
|
17
17
|
// We can't access `this.name` here since it's abstract, so use a placeholder
|
|
18
18
|
this.logger = new logger_js_1.Logger('npc', config.debug);
|
|
19
19
|
this.client = new node_1.NocvilleClient({
|
|
20
|
+
workspace: config.workspace,
|
|
20
21
|
apiUrl: config.apiUrl,
|
|
21
22
|
apiKey: config.apiKey,
|
|
22
23
|
serviceId: config.serviceId,
|
package/dist/cjs/core/config.js
CHANGED
|
@@ -6,6 +6,8 @@ exports.stringFlag = stringFlag;
|
|
|
6
6
|
exports.boolFlag = boolFlag;
|
|
7
7
|
exports.listFlag = listFlag;
|
|
8
8
|
exports.numberFlag = numberFlag;
|
|
9
|
+
const node_1 = require("@nocville/node");
|
|
10
|
+
const node_path_1 = require("node:path");
|
|
9
11
|
const user_config_js_1 = require("./user-config.js");
|
|
10
12
|
/**
|
|
11
13
|
* Simple hand-rolled CLI arg parser.
|
|
@@ -56,9 +58,16 @@ function parseArgs(argv) {
|
|
|
56
58
|
*/
|
|
57
59
|
function resolveConfig(flags, overrides) {
|
|
58
60
|
const saved = (0, user_config_js_1.loadUserConfig)();
|
|
61
|
+
const workspacePath = overrides?.workspace ||
|
|
62
|
+
stringFlag(flags, 'workspace') ||
|
|
63
|
+
process.env['NOCVILLE_WORKSPACE'] ||
|
|
64
|
+
saved.workspace;
|
|
65
|
+
const workspace = workspacePath ? (0, node_path_1.resolve)(workspacePath) : undefined;
|
|
66
|
+
const enrollment = workspace ? (0, node_1.loadProducerWorkspace)(workspace) : undefined;
|
|
59
67
|
const apiUrl = overrides?.apiUrl ||
|
|
60
68
|
stringFlag(flags, 'api-url') ||
|
|
61
69
|
process.env['NOCVILLE_API_URL'] ||
|
|
70
|
+
enrollment?.apiUrl ||
|
|
62
71
|
saved.apiUrl ||
|
|
63
72
|
'https://nocville.com';
|
|
64
73
|
const apiKey = overrides?.apiKey ||
|
|
@@ -76,10 +85,23 @@ function resolveConfig(flags, overrides) {
|
|
|
76
85
|
const map = overrides?.map ||
|
|
77
86
|
stringFlag(flags, 'map') ||
|
|
78
87
|
process.env['NOCVILLE_MAP'] ||
|
|
88
|
+
enrollment?.encryption.mapId ||
|
|
79
89
|
saved.map ||
|
|
80
90
|
undefined;
|
|
81
91
|
const name = overrides?.name || stringFlag(flags, 'name') || process.env['NOCVILLE_NAME'] || undefined;
|
|
82
|
-
|
|
92
|
+
if (enrollment && apiUrl.replace(/\/+$/, '') !== enrollment.apiUrl)
|
|
93
|
+
throw new Error('API origin does not match the verified workspace');
|
|
94
|
+
if (enrollment && map && map !== enrollment.encryption.mapId)
|
|
95
|
+
throw new Error('Encrypted producers use the map ID in their workspace settings');
|
|
96
|
+
return {
|
|
97
|
+
apiUrl,
|
|
98
|
+
apiKey,
|
|
99
|
+
serviceId,
|
|
100
|
+
debug,
|
|
101
|
+
map: enrollment?.encryption.mapId ?? map,
|
|
102
|
+
name,
|
|
103
|
+
...(workspace ? { workspace } : {}),
|
|
104
|
+
};
|
|
83
105
|
}
|
|
84
106
|
function stringFlag(flags, key) {
|
|
85
107
|
const val = flags[key];
|
|
@@ -25,6 +25,8 @@ function loadUserConfig(path = userConfigPath()) {
|
|
|
25
25
|
try {
|
|
26
26
|
const raw = JSON.parse((0, node_fs_1.readFileSync)(path, 'utf-8'));
|
|
27
27
|
const out = {};
|
|
28
|
+
if (typeof raw['workspace'] === 'string' && raw['workspace'])
|
|
29
|
+
out.workspace = raw['workspace'];
|
|
28
30
|
if (typeof raw['apiUrl'] === 'string' && raw['apiUrl'])
|
|
29
31
|
out.apiUrl = raw['apiUrl'];
|
|
30
32
|
if (typeof raw['apiKey'] === 'string' && raw['apiKey'])
|
package/dist/cjs/doctor.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.runDoctor = runDoctor;
|
|
4
4
|
exports.hasCodexHookWatcherOverlap = hasCodexHookWatcherOverlap;
|
|
5
|
+
const node_1 = require("@nocville/node");
|
|
5
6
|
const node_crypto_1 = require("node:crypto");
|
|
6
7
|
const node_fs_1 = require("node:fs");
|
|
7
8
|
const liveness_js_1 = require("./npcs/agent-common/liveness.js");
|
|
@@ -43,6 +44,24 @@ async function runDoctor(config) {
|
|
|
43
44
|
if (!hasKey) {
|
|
44
45
|
console.log(' [SKIP] POST /api/v1/status — no --api-key / NOCVILLE_API_KEY set');
|
|
45
46
|
}
|
|
47
|
+
else if (config.workspace || config.apiKey.includes('.team_')) {
|
|
48
|
+
// Exercise the locally encrypted path; never send a plaintext probe to a v2 workspace.
|
|
49
|
+
try {
|
|
50
|
+
const client = new node_1.NocvilleClient({
|
|
51
|
+
workspace: config.workspace,
|
|
52
|
+
apiUrl: config.apiUrl,
|
|
53
|
+
apiKey: config.apiKey,
|
|
54
|
+
serviceId: 'nocville-doctor',
|
|
55
|
+
retries: 0,
|
|
56
|
+
});
|
|
57
|
+
const result = await client.pushStatus({ status: 'unknown', offline: true });
|
|
58
|
+
pushOk = result.success;
|
|
59
|
+
console.log(` [${pushOk ? 'OK' : 'FAIL'}] Encrypted producer: ${result.error ?? 'accepted'}`);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.log(` [FAIL] Encrypted producer: ${error.message}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
46
65
|
else {
|
|
47
66
|
const start = Date.now();
|
|
48
67
|
const t = withTimeout(5000);
|
|
@@ -263,6 +263,7 @@ function buildAgentPayload(adapter, state, config) {
|
|
|
263
263
|
}
|
|
264
264
|
async function defaultPush(config, serviceId, payload, timeoutMs) {
|
|
265
265
|
const client = new node_1.NocvilleClient({
|
|
266
|
+
workspace: config.workspace,
|
|
266
267
|
apiUrl: config.apiUrl,
|
|
267
268
|
apiKey: config.apiKey,
|
|
268
269
|
serviceId,
|
|
@@ -381,6 +382,8 @@ function spawnDetachedAgent(adapter, config, args, cliPath = process.argv[1]) {
|
|
|
381
382
|
if (!cliPath)
|
|
382
383
|
return undefined;
|
|
383
384
|
const env = { ...process.env };
|
|
385
|
+
if (config.workspace)
|
|
386
|
+
env['NOCVILLE_WORKSPACE'] = config.workspace;
|
|
384
387
|
env['NOCVILLE_API_URL'] = config.apiUrl;
|
|
385
388
|
env['NOCVILLE_API_KEY'] = config.apiKey;
|
|
386
389
|
if (config.map)
|
package/dist/cli.js
CHANGED
|
@@ -61,6 +61,7 @@ Monitoring NPC types:
|
|
|
61
61
|
web-page-watcher Render a watched web page as a pixel-art TV
|
|
62
62
|
|
|
63
63
|
Common Options:
|
|
64
|
+
--workspace=<file> Verified producer workspace settings from team privacy settings.
|
|
64
65
|
--api-key=<key> Team API key (or NOCVILLE_API_KEY env)
|
|
65
66
|
--api-url=<url> Nocville API URL (or NOCVILLE_API_URL env).
|
|
66
67
|
Default: https://nocville.com. For local dev use
|
|
@@ -477,7 +478,20 @@ async function runInit(flags, config) {
|
|
|
477
478
|
let apiKey = stringFlag(flags, 'api-key');
|
|
478
479
|
const apiUrl = stringFlag(flags, 'api-url');
|
|
479
480
|
const map = stringFlag(flags, 'map');
|
|
480
|
-
|
|
481
|
+
const workspace = config.workspace;
|
|
482
|
+
if (workspace)
|
|
483
|
+
apiKey = apiKey || config.apiKey;
|
|
484
|
+
if (workspace && !apiKey) {
|
|
485
|
+
const { resolveApiKey } = await import('./core/api-key.js');
|
|
486
|
+
const resolved = await resolveApiKey({ flags });
|
|
487
|
+
if (!resolved.ok) {
|
|
488
|
+
console.error(resolved.error);
|
|
489
|
+
process.exitCode = 1;
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
apiKey = resolved.key;
|
|
493
|
+
}
|
|
494
|
+
if (!apiKey && !apiUrl && !map && !workspace) {
|
|
481
495
|
const saved = loadUserConfig();
|
|
482
496
|
if (saved.apiKey && !process.env['NOCVILLE_API_KEY']?.trim()) {
|
|
483
497
|
console.log('Usage: nocville-npc init --api-key=<key> [--api-url=<url>] [--map=<name>]');
|
|
@@ -493,7 +507,12 @@ async function runInit(flags, config) {
|
|
|
493
507
|
}
|
|
494
508
|
apiKey = resolved.key;
|
|
495
509
|
}
|
|
496
|
-
const path = saveUserConfig({
|
|
510
|
+
const path = saveUserConfig({
|
|
511
|
+
apiKey,
|
|
512
|
+
apiUrl: workspace ? config.apiUrl : apiUrl,
|
|
513
|
+
map: workspace ? config.map : map,
|
|
514
|
+
workspace,
|
|
515
|
+
});
|
|
497
516
|
const saved = loadUserConfig();
|
|
498
517
|
console.log(`✓ Saved ${path} (mode 0600)`);
|
|
499
518
|
console.log(` API: ${saved.apiUrl ?? 'https://nocville.com'} key: ${maskKey(saved.apiKey)}`);
|
package/dist/core/base-npc.js
CHANGED
|
@@ -14,6 +14,7 @@ export class BaseNPC {
|
|
|
14
14
|
// We can't access `this.name` here since it's abstract, so use a placeholder
|
|
15
15
|
this.logger = new Logger('npc', config.debug);
|
|
16
16
|
this.client = new NocvilleClient({
|
|
17
|
+
workspace: config.workspace,
|
|
17
18
|
apiUrl: config.apiUrl,
|
|
18
19
|
apiKey: config.apiKey,
|
|
19
20
|
serviceId: config.serviceId,
|
package/dist/core/config.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { loadProducerWorkspace } from '@nocville/node';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
1
3
|
import { loadUserConfig } from './user-config.js';
|
|
2
4
|
/**
|
|
3
5
|
* Simple hand-rolled CLI arg parser.
|
|
@@ -48,9 +50,16 @@ export function parseArgs(argv) {
|
|
|
48
50
|
*/
|
|
49
51
|
export function resolveConfig(flags, overrides) {
|
|
50
52
|
const saved = loadUserConfig();
|
|
53
|
+
const workspacePath = overrides?.workspace ||
|
|
54
|
+
stringFlag(flags, 'workspace') ||
|
|
55
|
+
process.env['NOCVILLE_WORKSPACE'] ||
|
|
56
|
+
saved.workspace;
|
|
57
|
+
const workspace = workspacePath ? resolve(workspacePath) : undefined;
|
|
58
|
+
const enrollment = workspace ? loadProducerWorkspace(workspace) : undefined;
|
|
51
59
|
const apiUrl = overrides?.apiUrl ||
|
|
52
60
|
stringFlag(flags, 'api-url') ||
|
|
53
61
|
process.env['NOCVILLE_API_URL'] ||
|
|
62
|
+
enrollment?.apiUrl ||
|
|
54
63
|
saved.apiUrl ||
|
|
55
64
|
'https://nocville.com';
|
|
56
65
|
const apiKey = overrides?.apiKey ||
|
|
@@ -68,10 +77,23 @@ export function resolveConfig(flags, overrides) {
|
|
|
68
77
|
const map = overrides?.map ||
|
|
69
78
|
stringFlag(flags, 'map') ||
|
|
70
79
|
process.env['NOCVILLE_MAP'] ||
|
|
80
|
+
enrollment?.encryption.mapId ||
|
|
71
81
|
saved.map ||
|
|
72
82
|
undefined;
|
|
73
83
|
const name = overrides?.name || stringFlag(flags, 'name') || process.env['NOCVILLE_NAME'] || undefined;
|
|
74
|
-
|
|
84
|
+
if (enrollment && apiUrl.replace(/\/+$/, '') !== enrollment.apiUrl)
|
|
85
|
+
throw new Error('API origin does not match the verified workspace');
|
|
86
|
+
if (enrollment && map && map !== enrollment.encryption.mapId)
|
|
87
|
+
throw new Error('Encrypted producers use the map ID in their workspace settings');
|
|
88
|
+
return {
|
|
89
|
+
apiUrl,
|
|
90
|
+
apiKey,
|
|
91
|
+
serviceId,
|
|
92
|
+
debug,
|
|
93
|
+
map: enrollment?.encryption.mapId ?? map,
|
|
94
|
+
name,
|
|
95
|
+
...(workspace ? { workspace } : {}),
|
|
96
|
+
};
|
|
75
97
|
}
|
|
76
98
|
export function stringFlag(flags, key) {
|
|
77
99
|
const val = flags[key];
|
package/dist/core/types.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export type Archetype = 'service' | 'database' | 'trading' | 'ai-agent' | 'pipel
|
|
|
6
6
|
/** A companion declaration is the SDK's CompanionSpec. */
|
|
7
7
|
export type CompanionDecl = CompanionSpec;
|
|
8
8
|
export interface NpcConfig {
|
|
9
|
+
/** Local verified workspace enrollment bundle. */
|
|
10
|
+
workspace?: string;
|
|
9
11
|
apiUrl: string;
|
|
10
12
|
apiKey: string;
|
|
11
13
|
serviceId: string;
|
package/dist/core/user-config.js
CHANGED
|
@@ -18,6 +18,8 @@ export function loadUserConfig(path = userConfigPath()) {
|
|
|
18
18
|
try {
|
|
19
19
|
const raw = JSON.parse(readFileSync(path, 'utf-8'));
|
|
20
20
|
const out = {};
|
|
21
|
+
if (typeof raw['workspace'] === 'string' && raw['workspace'])
|
|
22
|
+
out.workspace = raw['workspace'];
|
|
21
23
|
if (typeof raw['apiUrl'] === 'string' && raw['apiUrl'])
|
|
22
24
|
out.apiUrl = raw['apiUrl'];
|
|
23
25
|
if (typeof raw['apiKey'] === 'string' && raw['apiKey'])
|
package/dist/doctor.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NocvilleClient } from '@nocville/node';
|
|
1
2
|
import { randomUUID } from 'node:crypto';
|
|
2
3
|
import { readdirSync } from 'node:fs';
|
|
3
4
|
import { isAgentAlive } from './npcs/agent-common/liveness.js';
|
|
@@ -39,6 +40,24 @@ export async function runDoctor(config) {
|
|
|
39
40
|
if (!hasKey) {
|
|
40
41
|
console.log(' [SKIP] POST /api/v1/status — no --api-key / NOCVILLE_API_KEY set');
|
|
41
42
|
}
|
|
43
|
+
else if (config.workspace || config.apiKey.includes('.team_')) {
|
|
44
|
+
// Exercise the locally encrypted path; never send a plaintext probe to a v2 workspace.
|
|
45
|
+
try {
|
|
46
|
+
const client = new NocvilleClient({
|
|
47
|
+
workspace: config.workspace,
|
|
48
|
+
apiUrl: config.apiUrl,
|
|
49
|
+
apiKey: config.apiKey,
|
|
50
|
+
serviceId: 'nocville-doctor',
|
|
51
|
+
retries: 0,
|
|
52
|
+
});
|
|
53
|
+
const result = await client.pushStatus({ status: 'unknown', offline: true });
|
|
54
|
+
pushOk = result.success;
|
|
55
|
+
console.log(` [${pushOk ? 'OK' : 'FAIL'}] Encrypted producer: ${result.error ?? 'accepted'}`);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.log(` [FAIL] Encrypted producer: ${error.message}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
42
61
|
else {
|
|
43
62
|
const start = Date.now();
|
|
44
63
|
const t = withTimeout(5000);
|
|
@@ -241,6 +241,7 @@ export function buildAgentPayload(adapter, state, config) {
|
|
|
241
241
|
}
|
|
242
242
|
async function defaultPush(config, serviceId, payload, timeoutMs) {
|
|
243
243
|
const client = new NocvilleClient({
|
|
244
|
+
workspace: config.workspace,
|
|
244
245
|
apiUrl: config.apiUrl,
|
|
245
246
|
apiKey: config.apiKey,
|
|
246
247
|
serviceId,
|
|
@@ -359,6 +360,8 @@ export function spawnDetachedAgent(adapter, config, args, cliPath = process.argv
|
|
|
359
360
|
if (!cliPath)
|
|
360
361
|
return undefined;
|
|
361
362
|
const env = { ...process.env };
|
|
363
|
+
if (config.workspace)
|
|
364
|
+
env['NOCVILLE_WORKSPACE'] = config.workspace;
|
|
362
365
|
env['NOCVILLE_API_URL'] = config.apiUrl;
|
|
363
366
|
env['NOCVILLE_API_KEY'] = config.apiKey;
|
|
364
367
|
if (config.map)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocville/npc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Put your Claude Code and Codex sessions (and your servers) in the Nocville office as live NPCs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"pngjs": "^7.0.0",
|
|
27
|
-
"@nocville/node": "^0.
|
|
27
|
+
"@nocville/node": "^0.4.0"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"puppeteer-core": "*"
|