@cnrai/pave 0.3.32 → 0.3.34
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/MARKETPLACE.md +406 -0
- package/README.md +218 -21
- package/build-binary.js +591 -0
- package/build-npm.js +537 -0
- package/build.js +230 -0
- package/check-binary.js +26 -0
- package/deploy.sh +95 -0
- package/index.js +5775 -0
- package/lib/agent-registry.js +1037 -0
- package/lib/args-parser.js +837 -0
- package/lib/blessed-widget-patched.js +93 -0
- package/lib/cli-markdown.js +590 -0
- package/lib/compaction.js +153 -0
- package/lib/duration.js +94 -0
- package/lib/hash.js +22 -0
- package/lib/marketplace.js +866 -0
- package/lib/memory-config.js +166 -0
- package/lib/skill-manager.js +891 -0
- package/lib/soul.js +31 -0
- package/lib/tool-output-formatter.js +180 -0
- package/package.json +35 -33
- package/start-pave.sh +149 -0
- package/status.js +271 -0
- package/test/abort-stream.test.js +445 -0
- package/test/agent-auto-compaction.test.js +552 -0
- package/test/agent-comm-abort.test.js +95 -0
- package/test/agent-comm.test.js +598 -0
- package/test/agent-inbox.test.js +576 -0
- package/test/agent-init.test.js +264 -0
- package/test/agent-interrupt.test.js +314 -0
- package/test/agent-lifecycle.test.js +520 -0
- package/test/agent-log-files.test.js +349 -0
- package/test/agent-mode.manual-test.js +392 -0
- package/test/agent-parsing.test.js +228 -0
- package/test/agent-post-stream-idle.test.js +762 -0
- package/test/agent-registry.test.js +359 -0
- package/test/agent-rm.test.js +442 -0
- package/test/agent-spawn.test.js +933 -0
- package/test/agent-status-api.test.js +624 -0
- package/test/agent-update.test.js +435 -0
- package/test/args-parser.test.js +391 -0
- package/test/auto-compaction-chat.manual-test.js +227 -0
- package/test/auto-compaction.test.js +941 -0
- package/test/build-config.test.js +120 -0
- package/test/build-npm.test.js +388 -0
- package/test/chat-command.test.js +137 -0
- package/test/chat-leading-lines.test.js +159 -0
- package/test/config-flag.test.js +272 -0
- package/test/cursor-drift.test.js +135 -0
- package/test/debug-require.js +23 -0
- package/test/dir-migration.test.js +323 -0
- package/test/duration.test.js +229 -0
- package/test/ghostty-term.test.js +202 -0
- package/test/http500-backoff.test.js +854 -0
- package/test/integration.test.js +86 -0
- package/test/memory-guard-env.test.js +220 -0
- package/test/pr233-fixes.test.js +259 -0
- package/test/run-agent-init.js +297 -0
- package/test/run-all.js +64 -0
- package/test/run-config-flag.js +159 -0
- package/test/run-cursor-drift.js +82 -0
- package/test/run-session-path.js +154 -0
- package/test/run-tests.js +643 -0
- package/test/sandbox-redirect.test.js +202 -0
- package/test/session-path.test.js +132 -0
- package/test/shebang-strip.test.js +241 -0
- package/test/soul-reinject.test.js +1027 -0
- package/test/soul-reread.test.js +281 -0
- package/test/tool-output-formatter.test.js +486 -0
- package/test/tool-output-gating.test.js +143 -0
- package/test/tool-states.test.js +167 -0
- package/test/tools-flag.test.js +65 -0
- package/test/tui-attach.test.js +1255 -0
- package/test/tui-compaction.test.js +354 -0
- package/test/tui-wrap.test.js +568 -0
- package/test-binary.js +52 -0
- package/test-binary2.js +36 -0
- package/LICENSE +0 -21
- package/pave.js +0 -2
- package/sandbox/SandboxRunner.js +0 -1
- package/sandbox/pave-run.js +0 -2
- package/sandbox/permission.js +0 -1
- package/sandbox/utils/yaml.js +0 -1
package/build-npm.js
ADDED
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Build script for npm-publishable PAVE package
|
|
4
|
+
*
|
|
5
|
+
* Creates an obfuscated, Node 16-compatible npm package at dist/npm/
|
|
6
|
+
* that can be published as @cnrai/pave.
|
|
7
|
+
*
|
|
8
|
+
* Pipeline: esbuild bundle � javascript-obfuscator � npm package
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* node build-npm.js # Build only
|
|
12
|
+
* node build-npm.js --publish # Build and publish (requires npm login)
|
|
13
|
+
* node build-npm.js --dry-run # Build and show what would be published
|
|
14
|
+
*
|
|
15
|
+
* Output:
|
|
16
|
+
* dist/npm/
|
|
17
|
+
* package.json - npm package metadata
|
|
18
|
+
* pave.js - Obfuscated main bundle with shebang
|
|
19
|
+
* sandbox/ - Obfuscated sandbox runner files
|
|
20
|
+
* pave-run.js
|
|
21
|
+
* SandboxRunner.js
|
|
22
|
+
* permission.js
|
|
23
|
+
* utils/yaml.js
|
|
24
|
+
* README.md - Usage instructions
|
|
25
|
+
* LICENSE - MIT License
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const { execSync, execFileSync } = require('child_process');
|
|
29
|
+
const esbuild = require('esbuild');
|
|
30
|
+
const fs = require('fs');
|
|
31
|
+
const path = require('path');
|
|
32
|
+
|
|
33
|
+
// Directories
|
|
34
|
+
const ROOT_DIR = __dirname;
|
|
35
|
+
const OPENCODE_LITE_DIR = path.join(ROOT_DIR, '..', 'opencode-lite');
|
|
36
|
+
const DIST_DIR = path.join(ROOT_DIR, 'dist', 'npm');
|
|
37
|
+
const SANDBOX_DEST_DIR = path.join(DIST_DIR, 'sandbox');
|
|
38
|
+
|
|
39
|
+
// Get version from package.json
|
|
40
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(ROOT_DIR, 'package.json'), 'utf8'));
|
|
41
|
+
const VERSION = packageJson.version || '0.0.0';
|
|
42
|
+
|
|
43
|
+
// Check for flags (mutually exclusive)
|
|
44
|
+
const hasPublishFlag = process.argv.includes('--publish');
|
|
45
|
+
const hasDryRunFlag = process.argv.includes('--dry-run');
|
|
46
|
+
|
|
47
|
+
if (hasPublishFlag && hasDryRunFlag) {
|
|
48
|
+
console.error('Error: --publish and --dry-run cannot be used together. Please specify only one.');
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const shouldPublish = hasPublishFlag;
|
|
53
|
+
const isDryRun = hasDryRunFlag;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Obfuscate a JavaScript file using javascript-obfuscator.
|
|
57
|
+
* Uses execFileSync with argument array to safely handle paths with spaces.
|
|
58
|
+
* @param {string} inputPath - Path to the source file
|
|
59
|
+
* @param {string} outputPath - Path for the obfuscated output
|
|
60
|
+
* @returns {string} Path to the obfuscated file
|
|
61
|
+
*/
|
|
62
|
+
function obfuscateFile(inputPath, outputPath) {
|
|
63
|
+
// Resolve npx path for execFileSync
|
|
64
|
+
const npxPath = process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
65
|
+
|
|
66
|
+
// Same obfuscation settings as build-binary.js for consistency
|
|
67
|
+
// Use --no-install to prevent npx from fetching packages from the registry
|
|
68
|
+
const args = [
|
|
69
|
+
'--no-install',
|
|
70
|
+
'javascript-obfuscator',
|
|
71
|
+
inputPath,
|
|
72
|
+
'--output', outputPath,
|
|
73
|
+
'--compact', 'true',
|
|
74
|
+
'--control-flow-flattening', 'false',
|
|
75
|
+
'--dead-code-injection', 'false',
|
|
76
|
+
'--identifier-names-generator', 'mangled',
|
|
77
|
+
'--rename-globals', 'false',
|
|
78
|
+
'--self-defending', 'false',
|
|
79
|
+
'--string-array', 'true',
|
|
80
|
+
'--string-array-encoding', 'base64',
|
|
81
|
+
'--string-array-threshold', '1.0',
|
|
82
|
+
'--seed', '42',
|
|
83
|
+
'--transform-object-keys', 'false',
|
|
84
|
+
'--unicode-escape-sequence', 'false',
|
|
85
|
+
'--target', 'node',
|
|
86
|
+
'--options-preset', 'low-obfuscation',
|
|
87
|
+
'--reserved-names', 'authenticated,valid,token,copilot,github,OpenCode,blessed,require,module,exports,process,console,Buffer,__dirname,__filename,program,tput,screen,cursor,options,element,children,parent,box,text,list,input,key,mouse,emit,on,off,render,destroy,focus,append,remove,hide,show,toggle,scroll,setContent,getContent,pushLine,setLine,insertLine,deleteLine,width,height,left,right,top,bottom,rows,cols',
|
|
88
|
+
'--reserved-strings', 'authenticated,valid,token,GitHub Copilot,Saved token is valid,OpenCode',
|
|
89
|
+
'--split-strings', 'false',
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
execFileSync(npxPath, args, { stdio: 'pipe', cwd: ROOT_DIR });
|
|
94
|
+
} catch (err) {
|
|
95
|
+
let message = 'javascript-obfuscator failed for ' + inputPath + ':\n' + String(err && err.message ? err.message : err);
|
|
96
|
+
if (err && err.stdout) {
|
|
97
|
+
message += '\nstdout:\n' + err.stdout.toString();
|
|
98
|
+
}
|
|
99
|
+
if (err && err.stderr) {
|
|
100
|
+
message += '\nstderr:\n' + err.stderr.toString();
|
|
101
|
+
}
|
|
102
|
+
throw new Error(message);
|
|
103
|
+
}
|
|
104
|
+
return outputPath;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function build() {
|
|
108
|
+
console.log('='.repeat(60));
|
|
109
|
+
console.log('Building PAVE npm package v' + VERSION);
|
|
110
|
+
console.log('='.repeat(60));
|
|
111
|
+
|
|
112
|
+
// Clean and recreate dist/npm directory
|
|
113
|
+
if (fs.existsSync(DIST_DIR)) {
|
|
114
|
+
fs.rmSync(DIST_DIR, { recursive: true, force: true });
|
|
115
|
+
}
|
|
116
|
+
fs.mkdirSync(DIST_DIR, { recursive: true });
|
|
117
|
+
fs.mkdirSync(SANDBOX_DEST_DIR, { recursive: true });
|
|
118
|
+
fs.mkdirSync(path.join(SANDBOX_DEST_DIR, 'utils'), { recursive: true });
|
|
119
|
+
|
|
120
|
+
// Ensure javascript-obfuscator is available (must be pre-installed as devDependency)
|
|
121
|
+
console.log('\n[0/5] Checking javascript-obfuscator...');
|
|
122
|
+
const npxCheck = process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
123
|
+
try {
|
|
124
|
+
execFileSync(npxCheck, ['--no-install', 'javascript-obfuscator', '--version'], { stdio: 'pipe', cwd: ROOT_DIR });
|
|
125
|
+
console.log(' javascript-obfuscator is available');
|
|
126
|
+
} catch (e) {
|
|
127
|
+
console.error(' Error: javascript-obfuscator is not installed.');
|
|
128
|
+
console.error(' Run: npm install --save-dev javascript-obfuscator');
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Step 1: Bundle with esbuild
|
|
133
|
+
console.log('\n[1/5] Bundling with esbuild (target: node16)...');
|
|
134
|
+
|
|
135
|
+
const bundlePath = path.join(DIST_DIR, 'pave-bundle.js');
|
|
136
|
+
|
|
137
|
+
await esbuild.build({
|
|
138
|
+
entryPoints: [path.join(ROOT_DIR, 'index.js')],
|
|
139
|
+
bundle: true,
|
|
140
|
+
platform: 'node',
|
|
141
|
+
target: 'node16',
|
|
142
|
+
outfile: bundlePath,
|
|
143
|
+
define: {
|
|
144
|
+
PAVE_VERSION: JSON.stringify(VERSION),
|
|
145
|
+
},
|
|
146
|
+
nodePaths: [path.join(ROOT_DIR, 'node_modules')],
|
|
147
|
+
external: [
|
|
148
|
+
'blessed',
|
|
149
|
+
'abort-controller',
|
|
150
|
+
'term.js',
|
|
151
|
+
'pty.js',
|
|
152
|
+
],
|
|
153
|
+
minify: true,
|
|
154
|
+
keepNames: true,
|
|
155
|
+
sourcemap: false,
|
|
156
|
+
format: 'cjs',
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
const bundleStats = fs.statSync(bundlePath);
|
|
160
|
+
console.log(' Bundle size: ' + (bundleStats.size / 1024).toFixed(1) + ' KB');
|
|
161
|
+
|
|
162
|
+
// Step 2: Obfuscate main bundle
|
|
163
|
+
console.log('\n[2/5] Obfuscating main bundle...');
|
|
164
|
+
|
|
165
|
+
const obfuscatedPath = path.join(DIST_DIR, 'pave.js');
|
|
166
|
+
obfuscateFile(bundlePath, obfuscatedPath);
|
|
167
|
+
|
|
168
|
+
// Add shebang, iSH auto-detection, and make executable
|
|
169
|
+
let content = fs.readFileSync(obfuscatedPath, 'utf-8');
|
|
170
|
+
content = content.replace(/^(#!.*\n)+/, '');
|
|
171
|
+
|
|
172
|
+
// iSH auto-detection prefix: re-execs with optimized flags if on iSH
|
|
173
|
+
// Runs before any heavy imports. Checks process.execArgv to avoid infinite loop.
|
|
174
|
+
const ishPrefix = [
|
|
175
|
+
'(function(){',
|
|
176
|
+
'if(process.execArgv.indexOf("--jitless")!==-1)return;',
|
|
177
|
+
'try{require("fs").readFileSync("/proc/ish/version","utf-8")}catch(e){return}',
|
|
178
|
+
'var f=["--jitless","--max-old-space-size=128","--optimize-for-size",',
|
|
179
|
+
'"--no-lazy","--expose-gc","--gc-interval=25","--max-semi-space-size=8",',
|
|
180
|
+
'"--memory-reducer","--no-compilation-cache","--no-turbo-inlining",',
|
|
181
|
+
'"--no-use-osr","--no-opt","--stack-size=512"];',
|
|
182
|
+
'var r=require("child_process").spawnSync(process.execPath,',
|
|
183
|
+
'f.concat([__filename]).concat(process.argv.slice(2)),',
|
|
184
|
+
'{stdio:"inherit"});',
|
|
185
|
+
'process.exit(r.status||0);',
|
|
186
|
+
'})();',
|
|
187
|
+
].join('');
|
|
188
|
+
|
|
189
|
+
content = '#!/usr/bin/env node\n' + ishPrefix + '\n' + content;
|
|
190
|
+
fs.writeFileSync(obfuscatedPath, content);
|
|
191
|
+
fs.chmodSync(obfuscatedPath, 0o755);
|
|
192
|
+
|
|
193
|
+
// Remove intermediate bundle
|
|
194
|
+
fs.unlinkSync(bundlePath);
|
|
195
|
+
|
|
196
|
+
const obfStats = fs.statSync(obfuscatedPath);
|
|
197
|
+
console.log(' Obfuscated size: ' + (obfStats.size / 1024).toFixed(1) + ' KB');
|
|
198
|
+
|
|
199
|
+
// Verify obfuscation of main bundle
|
|
200
|
+
const obfContent = fs.readFileSync(obfuscatedPath, 'utf-8');
|
|
201
|
+
const sensitiveStrings = ['newGlobal', 'os.system', 'SandboxRunner', 'drainJobQueue'];
|
|
202
|
+
const found = sensitiveStrings.filter((s) => { return obfContent.includes(s); });
|
|
203
|
+
if (found.length > 0) {
|
|
204
|
+
console.error(' Error: Some sensitive strings still visible in pave.js after obfuscation: ' + found.join(', '));
|
|
205
|
+
process.exit(1);
|
|
206
|
+
} else {
|
|
207
|
+
console.log(' Sensitive strings successfully obfuscated');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Step 3: Copy and obfuscate sandbox files
|
|
211
|
+
console.log('\n[3/5] Copying and obfuscating sandbox files...');
|
|
212
|
+
|
|
213
|
+
const sandboxFiles = [
|
|
214
|
+
{ src: 'bin/pave-run.js', dest: 'pave-run.js' },
|
|
215
|
+
{ src: 'src/sandbox/SandboxRunner.js', dest: 'SandboxRunner.js' },
|
|
216
|
+
{ src: 'src/tools/permission.js', dest: 'permission.js' },
|
|
217
|
+
{ src: 'src/utils/yaml.js', dest: 'utils/yaml.js' },
|
|
218
|
+
];
|
|
219
|
+
|
|
220
|
+
for (let i = 0; i < sandboxFiles.length; i++) {
|
|
221
|
+
const file = sandboxFiles[i];
|
|
222
|
+
const srcPath = path.join(OPENCODE_LITE_DIR, file.src);
|
|
223
|
+
const destPath = path.join(SANDBOX_DEST_DIR, file.dest);
|
|
224
|
+
|
|
225
|
+
if (!fs.existsSync(srcPath)) {
|
|
226
|
+
console.error(' Error: Required sandbox file is missing: ' + srcPath);
|
|
227
|
+
process.exit(1);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
let fileContent = fs.readFileSync(srcPath, 'utf-8');
|
|
231
|
+
|
|
232
|
+
// Fix require paths for the bundled structure (same as build.js)
|
|
233
|
+
if (file.dest === 'pave-run.js') {
|
|
234
|
+
fileContent = fileContent.replace(
|
|
235
|
+
/var sandboxPath = path\.join\(__dirname.*SandboxRunner\.js'\);/,
|
|
236
|
+
"var sandboxPath = path.join(__dirname, 'SandboxRunner.js');",
|
|
237
|
+
);
|
|
238
|
+
fileContent = fileContent.replace(
|
|
239
|
+
/var permissionPath = path\.join\(__dirname.*permission\.js'\);/,
|
|
240
|
+
"var permissionPath = path.join(__dirname, 'permission.js');",
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (file.dest === 'SandboxRunner.js') {
|
|
245
|
+
fileContent = fileContent.replace(
|
|
246
|
+
/} = require\(['"]\.\.\/tools\/permission['"]\);/,
|
|
247
|
+
"} = require('./permission');",
|
|
248
|
+
);
|
|
249
|
+
fileContent = fileContent.replace(
|
|
250
|
+
/require\(['"]\.\.\/tools\/permission['"]\)/g,
|
|
251
|
+
"require('./permission')",
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (file.dest === 'permission.js') {
|
|
256
|
+
fileContent = fileContent.replace(
|
|
257
|
+
/require\(['"]\.\.\/utils\/yaml['"]\)/g,
|
|
258
|
+
"require('./utils/yaml')",
|
|
259
|
+
);
|
|
260
|
+
// Strip developer-specific absolute paths from LEGACY_ENV_FILE_PATHS
|
|
261
|
+
// to avoid leaking local machine info into the npm package
|
|
262
|
+
fileContent = fileContent.replace(
|
|
263
|
+
/,\s*['"]\/Users\/[^'"]*['"]/g,
|
|
264
|
+
'',
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Write patched file to a temp location, then obfuscate to dest
|
|
269
|
+
const tempPath = destPath.replace(/\.js$/, '.input.js');
|
|
270
|
+
fs.writeFileSync(tempPath, fileContent);
|
|
271
|
+
|
|
272
|
+
try {
|
|
273
|
+
obfuscateFile(tempPath, destPath);
|
|
274
|
+
fs.unlinkSync(tempPath);
|
|
275
|
+
console.log(' Obfuscated: sandbox/' + file.dest);
|
|
276
|
+
} catch (err) {
|
|
277
|
+
// yaml.js is a generic YAML parsing utility with no PAVE-specific logic,
|
|
278
|
+
// so it's safe to ship unobfuscated if obfuscation fails
|
|
279
|
+
if (file.dest === 'utils/yaml.js') {
|
|
280
|
+
console.warn(' Warning: Obfuscation skipped for sandbox/utils/yaml.js; using patched source.');
|
|
281
|
+
fs.renameSync(tempPath, destPath);
|
|
282
|
+
} else {
|
|
283
|
+
console.error(' Error: Failed to obfuscate sandbox/' + file.dest + ': ' + err.message);
|
|
284
|
+
try { fs.unlinkSync(tempPath); } catch (e) { /* cleanup best-effort */ }
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Verify sandbox outputs for sensitive strings (except yaml.js which may be unobfuscated)
|
|
291
|
+
const sandboxSensitive = ['newGlobal', 'os.system', 'drainJobQueue', 'SandboxRunner'];
|
|
292
|
+
const whitelistedSandbox = ['utils/yaml.js'];
|
|
293
|
+
// pave-run.js legitimately references SandboxRunner as a require path/variable name
|
|
294
|
+
const perFileWhitelist = { 'pave-run.js': ['SandboxRunner'] };
|
|
295
|
+
for (let k = 0; k < sandboxFiles.length; k++) {
|
|
296
|
+
const sf = sandboxFiles[k];
|
|
297
|
+
if (whitelistedSandbox.indexOf(sf.dest) > -1) continue;
|
|
298
|
+
const sfPath = path.join(SANDBOX_DEST_DIR, sf.dest);
|
|
299
|
+
if (!fs.existsSync(sfPath)) continue;
|
|
300
|
+
const sfContent = fs.readFileSync(sfPath, 'utf-8');
|
|
301
|
+
const fileWhitelist = perFileWhitelist[sf.dest] || [];
|
|
302
|
+
const sfFound = sandboxSensitive.filter((s) => {
|
|
303
|
+
return fileWhitelist.indexOf(s) === -1 && sfContent.includes(s);
|
|
304
|
+
});
|
|
305
|
+
if (sfFound.length > 0) {
|
|
306
|
+
console.error(' Error: Sensitive strings in sandbox/' + sf.dest + ': ' + sfFound.join(', '));
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
console.log(' Sandbox files verified: no sensitive strings detected');
|
|
311
|
+
|
|
312
|
+
// Check for developer-specific paths that should not be in published package
|
|
313
|
+
const devPathPatterns = [/\/Users\/[^/\\]+\//g, /\/home\/[^/\\]+\//g, /C:\\\\Users\\\\[^\\]+\\\\/g];
|
|
314
|
+
for (let m = 0; m < sandboxFiles.length; m++) {
|
|
315
|
+
const dpFile = sandboxFiles[m];
|
|
316
|
+
const dpPath = path.join(SANDBOX_DEST_DIR, dpFile.dest);
|
|
317
|
+
if (!fs.existsSync(dpPath)) continue;
|
|
318
|
+
const dpContent = fs.readFileSync(dpPath, 'utf-8');
|
|
319
|
+
for (let n = 0; n < devPathPatterns.length; n++) {
|
|
320
|
+
const devMatch = dpContent.match(devPathPatterns[n]);
|
|
321
|
+
if (devMatch) {
|
|
322
|
+
console.error(' Error: Developer-specific path found in sandbox/' + dpFile.dest + ': ' + devMatch[0]);
|
|
323
|
+
process.exit(1);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
console.log(' Sandbox files verified: no developer-specific paths detected');
|
|
328
|
+
|
|
329
|
+
// Step 4: Generate package.json, README, LICENSE
|
|
330
|
+
console.log('\n[4/5] Generating package metadata...');
|
|
331
|
+
|
|
332
|
+
const npmPackage = {
|
|
333
|
+
name: '@cnrai/pave',
|
|
334
|
+
version: VERSION,
|
|
335
|
+
description: 'PAVE - Personal AI Virtual Environment. AI agent framework for the terminal.',
|
|
336
|
+
main: 'pave.js',
|
|
337
|
+
bin: {
|
|
338
|
+
pave: './pave.js',
|
|
339
|
+
},
|
|
340
|
+
scripts: {
|
|
341
|
+
start: 'node pave.js',
|
|
342
|
+
},
|
|
343
|
+
engines: {
|
|
344
|
+
node: '>=16.0.0',
|
|
345
|
+
},
|
|
346
|
+
os: ['darwin', 'linux', 'win32'],
|
|
347
|
+
dependencies: {
|
|
348
|
+
blessed: '^0.1.81',
|
|
349
|
+
'js-yaml': '^4.1.0',
|
|
350
|
+
},
|
|
351
|
+
files: [
|
|
352
|
+
'pave.js',
|
|
353
|
+
'sandbox/',
|
|
354
|
+
'README.md',
|
|
355
|
+
'LICENSE',
|
|
356
|
+
],
|
|
357
|
+
keywords: [
|
|
358
|
+
'ai', 'terminal', 'tui', 'agent', 'cli',
|
|
359
|
+
'ish', 'ios', 'node16', 'memory-efficient',
|
|
360
|
+
],
|
|
361
|
+
repository: {
|
|
362
|
+
type: 'git',
|
|
363
|
+
url: 'git+https://github.com/cnrai/openpave.git',
|
|
364
|
+
},
|
|
365
|
+
homepage: 'https://github.com/cnrai/openpave',
|
|
366
|
+
bugs: {
|
|
367
|
+
url: 'https://github.com/cnrai/openpave/issues',
|
|
368
|
+
},
|
|
369
|
+
author: 'CNRAI',
|
|
370
|
+
license: 'MIT',
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
fs.writeFileSync(
|
|
374
|
+
path.join(DIST_DIR, 'package.json'),
|
|
375
|
+
JSON.stringify(npmPackage, null, 2) + '\n',
|
|
376
|
+
);
|
|
377
|
+
console.log(' Created package.json');
|
|
378
|
+
|
|
379
|
+
// README
|
|
380
|
+
const readme = [
|
|
381
|
+
'# @cnrai/pave',
|
|
382
|
+
'',
|
|
383
|
+
'PAVE - Personal AI Virtual Environment. AI agent framework for the terminal.',
|
|
384
|
+
'',
|
|
385
|
+
'## Installation',
|
|
386
|
+
'',
|
|
387
|
+
'```bash',
|
|
388
|
+
'npm install -g @cnrai/pave',
|
|
389
|
+
'```',
|
|
390
|
+
'',
|
|
391
|
+
'## Usage',
|
|
392
|
+
'',
|
|
393
|
+
'```bash',
|
|
394
|
+
'pave # Launch TUI',
|
|
395
|
+
'pave --help # Show help',
|
|
396
|
+
'pave --version # Show version',
|
|
397
|
+
'pave chat "question" # Quick chat',
|
|
398
|
+
'```',
|
|
399
|
+
'',
|
|
400
|
+
'### iSH (iOS)',
|
|
401
|
+
'',
|
|
402
|
+
'```bash',
|
|
403
|
+
'npm install -g @cnrai/pave',
|
|
404
|
+
'pave # Auto-detects iSH and applies optimized Node flags',
|
|
405
|
+
'```',
|
|
406
|
+
'',
|
|
407
|
+
'## Requirements',
|
|
408
|
+
'',
|
|
409
|
+
'- Node.js >= 16.0.0',
|
|
410
|
+
'- A GitHub Copilot subscription (for the AI backend)',
|
|
411
|
+
'',
|
|
412
|
+
'## Links',
|
|
413
|
+
'',
|
|
414
|
+
'- Source: https://github.com/cnrai/openpave',
|
|
415
|
+
'- Issues: https://github.com/cnrai/openpave/issues',
|
|
416
|
+
'- Homebrew (native binary): `brew tap cnrai/tap && brew install pave`',
|
|
417
|
+
'',
|
|
418
|
+
'## License',
|
|
419
|
+
'',
|
|
420
|
+
'MIT - Copyright (c) 2025 CNRAI',
|
|
421
|
+
'',
|
|
422
|
+
].join('\n');
|
|
423
|
+
|
|
424
|
+
fs.writeFileSync(path.join(DIST_DIR, 'README.md'), readme);
|
|
425
|
+
console.log(' Created README.md');
|
|
426
|
+
|
|
427
|
+
// LICENSE
|
|
428
|
+
const license = [
|
|
429
|
+
'MIT License',
|
|
430
|
+
'',
|
|
431
|
+
'Copyright (c) 2025 CNRAI',
|
|
432
|
+
'',
|
|
433
|
+
'Permission is hereby granted, free of charge, to any person obtaining a copy',
|
|
434
|
+
'of this software and associated documentation files (the "Software"), to deal',
|
|
435
|
+
'in the Software without restriction, including without limitation the rights',
|
|
436
|
+
'to use, copy, modify, merge, publish, distribute, sublicense, and/or sell',
|
|
437
|
+
'copies of the Software, and to permit persons to whom the Software is',
|
|
438
|
+
'furnished to do so, subject to the following conditions:',
|
|
439
|
+
'',
|
|
440
|
+
'The above copyright notice and this permission notice shall be included in all',
|
|
441
|
+
'copies or substantial portions of the Software.',
|
|
442
|
+
'',
|
|
443
|
+
'THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR',
|
|
444
|
+
'IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,',
|
|
445
|
+
'FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE',
|
|
446
|
+
'AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER',
|
|
447
|
+
'LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,',
|
|
448
|
+
'OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE',
|
|
449
|
+
'SOFTWARE.',
|
|
450
|
+
'',
|
|
451
|
+
].join('\n');
|
|
452
|
+
|
|
453
|
+
fs.writeFileSync(path.join(DIST_DIR, 'LICENSE'), license);
|
|
454
|
+
console.log(' Created LICENSE');
|
|
455
|
+
|
|
456
|
+
// Step 5: Summary and optional publish
|
|
457
|
+
console.log('\n[5/5] Package summary...');
|
|
458
|
+
|
|
459
|
+
// Calculate total size
|
|
460
|
+
let totalSize = 0;
|
|
461
|
+
function addSize(filePath) {
|
|
462
|
+
if (fs.existsSync(filePath)) {
|
|
463
|
+
totalSize += fs.statSync(filePath).size;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
addSize(path.join(DIST_DIR, 'pave.js'));
|
|
467
|
+
addSize(path.join(DIST_DIR, 'package.json'));
|
|
468
|
+
addSize(path.join(DIST_DIR, 'README.md'));
|
|
469
|
+
addSize(path.join(DIST_DIR, 'LICENSE'));
|
|
470
|
+
|
|
471
|
+
// Add sandbox files
|
|
472
|
+
const sandboxDir = path.join(DIST_DIR, 'sandbox');
|
|
473
|
+
if (fs.existsSync(sandboxDir)) {
|
|
474
|
+
const walkDir = function (dir) {
|
|
475
|
+
const files = fs.readdirSync(dir);
|
|
476
|
+
for (let j = 0; j < files.length; j++) {
|
|
477
|
+
const fp = path.join(dir, files[j]);
|
|
478
|
+
const stat = fs.statSync(fp);
|
|
479
|
+
if (stat.isDirectory()) {
|
|
480
|
+
walkDir(fp);
|
|
481
|
+
} else {
|
|
482
|
+
totalSize += stat.size;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
walkDir(sandboxDir);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
console.log('\n' + '='.repeat(60));
|
|
490
|
+
console.log('npm package built successfully!');
|
|
491
|
+
console.log('='.repeat(60));
|
|
492
|
+
console.log('\n Package: @cnrai/pave@' + VERSION);
|
|
493
|
+
console.log(' Location: ' + DIST_DIR);
|
|
494
|
+
console.log(' Size: ' + (totalSize / 1024).toFixed(1) + ' KB (before npm install)');
|
|
495
|
+
console.log('\n Contents:');
|
|
496
|
+
console.log(' pave.js - Obfuscated main bundle');
|
|
497
|
+
console.log(' sandbox/ - Obfuscated sandbox files');
|
|
498
|
+
console.log(' package.json - npm metadata');
|
|
499
|
+
console.log(' README.md - Usage instructions');
|
|
500
|
+
console.log(' LICENSE - MIT License');
|
|
501
|
+
|
|
502
|
+
if (shouldPublish) {
|
|
503
|
+
console.log('\n Publishing to npm...');
|
|
504
|
+
try {
|
|
505
|
+
execSync('npm publish --access public', { cwd: DIST_DIR, stdio: 'inherit' });
|
|
506
|
+
console.log('\n Published @cnrai/pave@' + VERSION + ' to npm!');
|
|
507
|
+
} catch (err) {
|
|
508
|
+
console.error('\n Publish failed: ' + err.message);
|
|
509
|
+
console.error(' Make sure you are logged in: npm login');
|
|
510
|
+
console.error(' And @cnrai org exists: npm org create cnrai');
|
|
511
|
+
process.exit(1);
|
|
512
|
+
}
|
|
513
|
+
} else if (isDryRun) {
|
|
514
|
+
console.log('\n Dry run (showing what would be published):');
|
|
515
|
+
try {
|
|
516
|
+
execSync('npm pack --dry-run', { cwd: DIST_DIR, stdio: 'inherit' });
|
|
517
|
+
} catch (err) {
|
|
518
|
+
// Only ignore the error if the npm command itself is not available.
|
|
519
|
+
if (err && (err.code === 'ENOENT' || err.status === 127)) {
|
|
520
|
+
console.log(' (npm pack not available, skipping dry run)');
|
|
521
|
+
} else {
|
|
522
|
+
console.error(' npm pack --dry-run failed:', err && err.message ? err.message : err);
|
|
523
|
+
process.exit(1);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
} else {
|
|
527
|
+
console.log('\n To publish:');
|
|
528
|
+
console.log(' cd ' + DIST_DIR);
|
|
529
|
+
console.log(' npm publish --access public');
|
|
530
|
+
console.log('\n Or run: node build-npm.js --publish');
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
build().catch((err) => {
|
|
535
|
+
console.error('Build failed:', err);
|
|
536
|
+
process.exit(1);
|
|
537
|
+
});
|