@naarang/ccc 2.2.0 → 3.0.0-beta.2
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 +261 -261
- package/bin/ccc.cjs +82 -82
- package/dist/index.js +116 -101
- package/dist/scripts/build-binaries.ts +539 -533
- package/dist/scripts/build-dist.ts +36 -36
- package/dist/scripts/install.ps1 +208 -208
- package/dist/scripts/postinstall.cjs +84 -84
- package/package.json +88 -86
- package/scripts/build-binaries.ts +539 -533
- package/scripts/build-dist.ts +36 -36
- package/scripts/install.ps1 +208 -208
- package/scripts/postinstall.cjs +84 -84
|
@@ -1,533 +1,539 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Build script for creating cross-platform binaries with embedded version
|
|
3
|
-
* and native library support (bun-pty)
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { $ } from 'bun';
|
|
7
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from 'fs';
|
|
8
|
-
import { join } from 'path';
|
|
9
|
-
|
|
10
|
-
// Read version from package.json
|
|
11
|
-
const packageJson = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8'));
|
|
12
|
-
const VERSION = packageJson.version;
|
|
13
|
-
|
|
14
|
-
console.log(`Building CCC v${VERSION}`);
|
|
15
|
-
|
|
16
|
-
// Ensure binaries directory exists
|
|
17
|
-
const binariesDir = join(import.meta.dir, '..', 'binaries');
|
|
18
|
-
if (!existsSync(binariesDir)) {
|
|
19
|
-
mkdirSync(binariesDir, { recursive: true });
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Define targets with their native library paths
|
|
23
|
-
const targets = [
|
|
24
|
-
{
|
|
25
|
-
name: 'linux-x64',
|
|
26
|
-
target: 'bun-linux-x64',
|
|
27
|
-
outfile: 'ccc-linux-x64',
|
|
28
|
-
ptyLib: 'librust_pty.so',
|
|
29
|
-
ngrokLib: 'ngrok.linux-x64-gnu.node',
|
|
30
|
-
ngrokPkg: '@ngrok/ngrok-linux-x64-gnu',
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
name: 'linux-arm64',
|
|
34
|
-
target: 'bun-linux-arm64',
|
|
35
|
-
outfile: 'ccc-linux-arm64',
|
|
36
|
-
ptyLib: 'librust_pty_arm64.so',
|
|
37
|
-
ngrokLib: 'ngrok.linux-arm64-gnu.node',
|
|
38
|
-
ngrokPkg: '@ngrok/ngrok-linux-arm64-gnu',
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
name: 'darwin-x64',
|
|
42
|
-
target: 'bun-darwin-x64',
|
|
43
|
-
outfile: 'ccc-darwin-x64',
|
|
44
|
-
ptyLib: 'librust_pty.dylib',
|
|
45
|
-
ngrokLib: 'ngrok.darwin-x64.node',
|
|
46
|
-
ngrokPkg: '@ngrok/ngrok-darwin-x64',
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
name: 'darwin-arm64',
|
|
50
|
-
target: 'bun-darwin-arm64',
|
|
51
|
-
outfile: 'ccc-darwin-arm64',
|
|
52
|
-
ptyLib: 'librust_pty_arm64.dylib',
|
|
53
|
-
ngrokLib: 'ngrok.darwin-arm64.node',
|
|
54
|
-
ngrokPkg: '@ngrok/ngrok-darwin-arm64',
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
name: 'windows-x64',
|
|
58
|
-
target: 'bun-windows-x64',
|
|
59
|
-
outfile: 'ccc-windows-x64.exe',
|
|
60
|
-
ptyLib: 'rust_pty.dll',
|
|
61
|
-
ngrokLib: 'ngrok.win32-x64-msvc.node',
|
|
62
|
-
ngrokPkg: '@ngrok/ngrok-win32-x64-msvc',
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
import
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
private
|
|
301
|
-
private
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
if (this._closing) return;
|
|
342
|
-
|
|
343
|
-
this.
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
this.
|
|
350
|
-
lib.symbols.
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
this.
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
} else {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
import
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
cleanupLoaders();
|
|
532
|
-
|
|
533
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Build script for creating cross-platform binaries with embedded version
|
|
3
|
+
* and native library support (bun-pty)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { $ } from 'bun';
|
|
7
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from 'fs';
|
|
8
|
+
import { join } from 'path';
|
|
9
|
+
|
|
10
|
+
// Read version from package.json
|
|
11
|
+
const packageJson = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8'));
|
|
12
|
+
const VERSION = packageJson.version;
|
|
13
|
+
|
|
14
|
+
console.log(`Building CCC v${VERSION}`);
|
|
15
|
+
|
|
16
|
+
// Ensure binaries directory exists
|
|
17
|
+
const binariesDir = join(import.meta.dir, '..', 'binaries');
|
|
18
|
+
if (!existsSync(binariesDir)) {
|
|
19
|
+
mkdirSync(binariesDir, { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Define targets with their native library paths
|
|
23
|
+
const targets = [
|
|
24
|
+
{
|
|
25
|
+
name: 'linux-x64',
|
|
26
|
+
target: 'bun-linux-x64',
|
|
27
|
+
outfile: 'ccc-linux-x64',
|
|
28
|
+
ptyLib: 'librust_pty.so',
|
|
29
|
+
ngrokLib: 'ngrok.linux-x64-gnu.node',
|
|
30
|
+
ngrokPkg: '@ngrok/ngrok-linux-x64-gnu',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'linux-arm64',
|
|
34
|
+
target: 'bun-linux-arm64',
|
|
35
|
+
outfile: 'ccc-linux-arm64',
|
|
36
|
+
ptyLib: 'librust_pty_arm64.so',
|
|
37
|
+
ngrokLib: 'ngrok.linux-arm64-gnu.node',
|
|
38
|
+
ngrokPkg: '@ngrok/ngrok-linux-arm64-gnu',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'darwin-x64',
|
|
42
|
+
target: 'bun-darwin-x64',
|
|
43
|
+
outfile: 'ccc-darwin-x64',
|
|
44
|
+
ptyLib: 'librust_pty.dylib',
|
|
45
|
+
ngrokLib: 'ngrok.darwin-x64.node',
|
|
46
|
+
ngrokPkg: '@ngrok/ngrok-darwin-x64',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'darwin-arm64',
|
|
50
|
+
target: 'bun-darwin-arm64',
|
|
51
|
+
outfile: 'ccc-darwin-arm64',
|
|
52
|
+
ptyLib: 'librust_pty_arm64.dylib',
|
|
53
|
+
ngrokLib: 'ngrok.darwin-arm64.node',
|
|
54
|
+
ngrokPkg: '@ngrok/ngrok-darwin-arm64',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: 'windows-x64',
|
|
58
|
+
target: 'bun-windows-x64',
|
|
59
|
+
outfile: 'ccc-windows-x64.exe',
|
|
60
|
+
ptyLib: 'rust_pty.dll',
|
|
61
|
+
ngrokLib: 'ngrok.win32-x64-msvc.node',
|
|
62
|
+
ngrokPkg: '@ngrok/ngrok-win32-x64-msvc',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'windows-arm64',
|
|
66
|
+
target: 'bun-windows-arm64',
|
|
67
|
+
outfile: 'ccc-windows-arm64.exe',
|
|
68
|
+
ptyLib: 'rust_pty_arm64.dll',
|
|
69
|
+
ngrokLib: 'ngrok.win32-arm64-msvc.node',
|
|
70
|
+
ngrokPkg: '@ngrok/ngrok-win32-arm64-msvc',
|
|
71
|
+
},
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
// Parse command line arguments
|
|
75
|
+
const args = process.argv.slice(2);
|
|
76
|
+
const targetArg = args.find((arg) => !arg.startsWith('-'));
|
|
77
|
+
const selectedTargets = targetArg ? targets.filter((t) => t.name === targetArg) : targets;
|
|
78
|
+
|
|
79
|
+
if (targetArg && selectedTargets.length === 0) {
|
|
80
|
+
console.error(`Unknown target: ${targetArg}`);
|
|
81
|
+
console.error(`Available targets: ${targets.map((t) => t.name).join(', ')}`);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Paths to the generated loader files
|
|
86
|
+
const loaderDir = join(import.meta.dir, '..', 'src', 'lib');
|
|
87
|
+
const ptyLoaderPath = join(loaderDir, 'bun-pty-loader.generated.ts');
|
|
88
|
+
const ngrokLoaderPath = join(loaderDir, 'ngrok-loader.generated.ts');
|
|
89
|
+
const nodeModulesDir = join(import.meta.dir, '..', 'node_modules');
|
|
90
|
+
|
|
91
|
+
// Ensure the lib directory exists
|
|
92
|
+
if (!existsSync(loaderDir)) {
|
|
93
|
+
mkdirSync(loaderDir, { recursive: true });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Check if a ngrok platform package is installed
|
|
98
|
+
*/
|
|
99
|
+
function isNgrokPackageInstalled(ngrokPkg: string, ngrokLib: string): boolean {
|
|
100
|
+
// For scoped packages like @ngrok/ngrok-darwin-x64, check for the .node file
|
|
101
|
+
const nodeFilePath = join(nodeModulesDir, ngrokPkg, ngrokLib);
|
|
102
|
+
return existsSync(nodeFilePath);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Try to install a ngrok platform package
|
|
107
|
+
*/
|
|
108
|
+
async function installNgrokPackage(ngrokPkg: string, ngrokLib: string): Promise<boolean> {
|
|
109
|
+
try {
|
|
110
|
+
console.log(` Installing ${ngrokPkg}...`);
|
|
111
|
+
|
|
112
|
+
// Download the package tarball directly from npm registry and extract it
|
|
113
|
+
const pkgDir = join(nodeModulesDir, ngrokPkg);
|
|
114
|
+
const scopeDir = join(nodeModulesDir, '@ngrok');
|
|
115
|
+
|
|
116
|
+
// Ensure @ngrok scope directory exists
|
|
117
|
+
if (!existsSync(scopeDir)) {
|
|
118
|
+
mkdirSync(scopeDir, { recursive: true });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Use npm pack to download and extract the package
|
|
122
|
+
await $`npm pack ${ngrokPkg}@1.6.0 --pack-destination ${scopeDir}`;
|
|
123
|
+
|
|
124
|
+
// Find the tarball and extract it
|
|
125
|
+
const tarball = join(scopeDir, `ngrok-${ngrokPkg.split('/')[1]}-1.6.0.tgz`);
|
|
126
|
+
await $`tar -xzf ${tarball} -C ${scopeDir}`;
|
|
127
|
+
|
|
128
|
+
// npm pack extracts to 'package' directory, rename it
|
|
129
|
+
const extractedDir = join(scopeDir, 'package');
|
|
130
|
+
if (existsSync(extractedDir)) {
|
|
131
|
+
// Remove existing if any
|
|
132
|
+
if (existsSync(pkgDir)) {
|
|
133
|
+
await $`rm -rf ${pkgDir}`;
|
|
134
|
+
}
|
|
135
|
+
await $`mv ${extractedDir} ${pkgDir}`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Clean up tarball
|
|
139
|
+
await $`rm -f ${tarball}`;
|
|
140
|
+
|
|
141
|
+
// Verify the file exists
|
|
142
|
+
const nodeFilePath = join(pkgDir, ngrokLib);
|
|
143
|
+
if (existsSync(nodeFilePath)) {
|
|
144
|
+
console.log(` Successfully installed ${ngrokPkg}`);
|
|
145
|
+
return true;
|
|
146
|
+
} else {
|
|
147
|
+
console.error(` Package installed but ${ngrokLib} not found`);
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
} catch (error) {
|
|
151
|
+
console.error(` Failed to install ${ngrokPkg}:`, error);
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Generate a platform-specific bun-pty loader that embeds the native library
|
|
158
|
+
* This inlines the bun-pty implementation to ensure the library is extracted
|
|
159
|
+
* before dlopen is called (bun-pty calls dlopen at module load time)
|
|
160
|
+
*/
|
|
161
|
+
function generatePtyLoader(ptyLib: string): void {
|
|
162
|
+
// Get the absolute path to the native library for reliable embedding
|
|
163
|
+
const nativeLibAbsPath = join(nodeModulesDir, 'bun-pty', 'rust-pty', 'target', 'release', ptyLib).replace(/\\/g, '/');
|
|
164
|
+
|
|
165
|
+
const loaderContent = `/**
|
|
166
|
+
* Auto-generated bun-pty implementation with embedded native library
|
|
167
|
+
* DO NOT EDIT - This file is generated by build-binaries.ts
|
|
168
|
+
*
|
|
169
|
+
* This is a copy of bun-pty's implementation that uses an embedded native library.
|
|
170
|
+
* We can't just set BUN_PTY_LIB and import bun-pty because bun-pty calls dlopen
|
|
171
|
+
* at module load time, before our env var setup code runs.
|
|
172
|
+
*/
|
|
173
|
+
|
|
174
|
+
import { dlopen, FFIType, ptr } from 'bun:ffi';
|
|
175
|
+
import { Buffer } from 'buffer';
|
|
176
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync, statSync } from 'fs';
|
|
177
|
+
import { join } from 'path';
|
|
178
|
+
import { homedir } from 'os';
|
|
179
|
+
|
|
180
|
+
// Embed the native library file - Bun will include this in the binary
|
|
181
|
+
// Using absolute path for reliable resolution during compilation
|
|
182
|
+
// @ts-ignore - Bun's file embedding syntax
|
|
183
|
+
import embeddedLibPath from '${nativeLibAbsPath}' with { type: 'file' };
|
|
184
|
+
|
|
185
|
+
// Extract embedded library to user cache directory (persistent, won't be auto-deleted)
|
|
186
|
+
const libName = '${ptyLib}';
|
|
187
|
+
const cacheDir = join(homedir(), '.cache', 'ccc', 'lib');
|
|
188
|
+
const extractedLibPath = join(cacheDir, libName);
|
|
189
|
+
|
|
190
|
+
// Extract library if not present or if embedded version is different size
|
|
191
|
+
try {
|
|
192
|
+
let needsExtract = true;
|
|
193
|
+
|
|
194
|
+
if (existsSync(extractedLibPath)) {
|
|
195
|
+
// Check if sizes match (quick check for version changes)
|
|
196
|
+
const embeddedSize = statSync(embeddedLibPath).size;
|
|
197
|
+
const extractedSize = statSync(extractedLibPath).size;
|
|
198
|
+
needsExtract = embeddedSize !== extractedSize;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (needsExtract) {
|
|
202
|
+
if (!existsSync(cacheDir)) {
|
|
203
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (!existsSync(embeddedLibPath)) {
|
|
207
|
+
throw new Error(\`Embedded library not found at: \${embeddedLibPath}\`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const libData = readFileSync(embeddedLibPath);
|
|
211
|
+
writeFileSync(extractedLibPath, libData);
|
|
212
|
+
|
|
213
|
+
// Make executable on Unix
|
|
214
|
+
try {
|
|
215
|
+
chmodSync(extractedLibPath, 0o755);
|
|
216
|
+
} catch {
|
|
217
|
+
// Ignore chmod errors on Windows
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
} catch (extractError) {
|
|
221
|
+
throw new Error(\`Failed to extract PTY library: \${extractError instanceof Error ? extractError.message : String(extractError)}\`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Load the native library
|
|
225
|
+
const lib = dlopen(extractedLibPath, {
|
|
226
|
+
bun_pty_spawn: {
|
|
227
|
+
args: [FFIType.cstring, FFIType.cstring, FFIType.cstring, FFIType.i32, FFIType.i32],
|
|
228
|
+
returns: FFIType.i32,
|
|
229
|
+
},
|
|
230
|
+
bun_pty_write: {
|
|
231
|
+
args: [FFIType.i32, FFIType.pointer, FFIType.i32],
|
|
232
|
+
returns: FFIType.i32,
|
|
233
|
+
},
|
|
234
|
+
bun_pty_read: {
|
|
235
|
+
args: [FFIType.i32, FFIType.pointer, FFIType.i32],
|
|
236
|
+
returns: FFIType.i32,
|
|
237
|
+
},
|
|
238
|
+
bun_pty_resize: {
|
|
239
|
+
args: [FFIType.i32, FFIType.i32, FFIType.i32],
|
|
240
|
+
returns: FFIType.i32,
|
|
241
|
+
},
|
|
242
|
+
bun_pty_kill: { args: [FFIType.i32], returns: FFIType.i32 },
|
|
243
|
+
bun_pty_get_pid: { args: [FFIType.i32], returns: FFIType.i32 },
|
|
244
|
+
bun_pty_get_exit_code: { args: [FFIType.i32], returns: FFIType.i32 },
|
|
245
|
+
bun_pty_close: { args: [FFIType.i32], returns: FFIType.void },
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// EventEmitter for PTY events
|
|
249
|
+
class EventEmitter<T> {
|
|
250
|
+
private listeners: ((data: T) => void)[] = [];
|
|
251
|
+
|
|
252
|
+
event = (listener: (data: T) => void) => {
|
|
253
|
+
this.listeners.push(listener);
|
|
254
|
+
return {
|
|
255
|
+
dispose: () => {
|
|
256
|
+
const i = this.listeners.indexOf(listener);
|
|
257
|
+
if (i !== -1) this.listeners.splice(i, 1);
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
fire(data: T) {
|
|
263
|
+
for (const listener of this.listeners) {
|
|
264
|
+
listener(data);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Shell quote helper
|
|
270
|
+
function shQuote(s: string): string {
|
|
271
|
+
if (s.length === 0) return "''";
|
|
272
|
+
return \`'\${s.replace(/'/g, \`'\\\\''\\'\`)}'\`;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const DEFAULT_COLS = 80;
|
|
276
|
+
const DEFAULT_ROWS = 24;
|
|
277
|
+
const DEFAULT_FILE = process.platform === 'win32' ? 'powershell.exe' : 'sh';
|
|
278
|
+
|
|
279
|
+
export interface IPtyOptions {
|
|
280
|
+
name?: string;
|
|
281
|
+
cols?: number;
|
|
282
|
+
rows?: number;
|
|
283
|
+
cwd?: string;
|
|
284
|
+
env?: Record<string, string>;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export interface IPty {
|
|
288
|
+
pid: number;
|
|
289
|
+
cols: number;
|
|
290
|
+
rows: number;
|
|
291
|
+
process: string;
|
|
292
|
+
onData: (listener: (data: string) => void) => { dispose: () => void };
|
|
293
|
+
onExit: (listener: (data: { exitCode: number; signal?: string }) => void) => { dispose: () => void };
|
|
294
|
+
write(data: string): void;
|
|
295
|
+
resize(cols: number, rows: number): void;
|
|
296
|
+
kill(signal?: string): void;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export class Terminal implements IPty {
|
|
300
|
+
private handle = -1;
|
|
301
|
+
private _pid = -1;
|
|
302
|
+
private _cols = DEFAULT_COLS;
|
|
303
|
+
private _rows = DEFAULT_ROWS;
|
|
304
|
+
private _readLoop = false;
|
|
305
|
+
private _closing = false;
|
|
306
|
+
private _onData = new EventEmitter<string>();
|
|
307
|
+
private _onExit = new EventEmitter<{ exitCode: number; signal?: string }>();
|
|
308
|
+
|
|
309
|
+
constructor(file = DEFAULT_FILE, args: string[] = [], opts: IPtyOptions = {}) {
|
|
310
|
+
this._cols = opts.cols ?? DEFAULT_COLS;
|
|
311
|
+
this._rows = opts.rows ?? DEFAULT_ROWS;
|
|
312
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
313
|
+
const cmdline = [file, ...args.map(shQuote)].join(' ');
|
|
314
|
+
let envStr = '';
|
|
315
|
+
if (opts.env) {
|
|
316
|
+
const envPairs = Object.entries(opts.env).map(([k, v]) => \`\${k}=\${v}\`);
|
|
317
|
+
envStr = envPairs.join('\\x00') + '\\x00';
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
this.handle = lib.symbols.bun_pty_spawn(
|
|
321
|
+
Buffer.from(\`\${cmdline}\\x00\`, 'utf8'),
|
|
322
|
+
Buffer.from(\`\${cwd}\\x00\`, 'utf8'),
|
|
323
|
+
Buffer.from(\`\${envStr}\\x00\`, 'utf8'),
|
|
324
|
+
this._cols,
|
|
325
|
+
this._rows
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
if (this.handle < 0) throw new Error('PTY spawn failed');
|
|
329
|
+
this._pid = lib.symbols.bun_pty_get_pid(this.handle);
|
|
330
|
+
this._startReadLoop();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
get pid() { return this._pid; }
|
|
334
|
+
get cols() { return this._cols; }
|
|
335
|
+
get rows() { return this._rows; }
|
|
336
|
+
get process() { return 'shell'; }
|
|
337
|
+
get onData() { return this._onData.event; }
|
|
338
|
+
get onExit() { return this._onExit.event; }
|
|
339
|
+
|
|
340
|
+
write(data: string) {
|
|
341
|
+
if (this._closing) return;
|
|
342
|
+
const buf = Buffer.from(data, 'utf8');
|
|
343
|
+
lib.symbols.bun_pty_write(this.handle, ptr(buf), buf.length);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
resize(cols: number, rows: number) {
|
|
347
|
+
if (this._closing) return;
|
|
348
|
+
this._cols = cols;
|
|
349
|
+
this._rows = rows;
|
|
350
|
+
lib.symbols.bun_pty_resize(this.handle, cols, rows);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
kill(signal = 'SIGTERM') {
|
|
354
|
+
if (this._closing) return;
|
|
355
|
+
this._closing = true;
|
|
356
|
+
lib.symbols.bun_pty_kill(this.handle);
|
|
357
|
+
lib.symbols.bun_pty_close(this.handle);
|
|
358
|
+
this._onExit.fire({ exitCode: 0, signal });
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
private async _startReadLoop() {
|
|
362
|
+
if (this._readLoop) return;
|
|
363
|
+
this._readLoop = true;
|
|
364
|
+
const buf = Buffer.allocUnsafe(4096);
|
|
365
|
+
|
|
366
|
+
while (this._readLoop && !this._closing) {
|
|
367
|
+
const n = lib.symbols.bun_pty_read(this.handle, ptr(buf), buf.length);
|
|
368
|
+
if (n > 0) {
|
|
369
|
+
this._onData.fire(buf.subarray(0, n).toString('utf8'));
|
|
370
|
+
} else if (n === -2) {
|
|
371
|
+
const exitCode = lib.symbols.bun_pty_get_exit_code(this.handle);
|
|
372
|
+
this._onExit.fire({ exitCode });
|
|
373
|
+
break;
|
|
374
|
+
} else if (n < 0) {
|
|
375
|
+
break;
|
|
376
|
+
} else {
|
|
377
|
+
await new Promise((r) => setTimeout(r, 8));
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
export function spawn(file: string, args: string[], options: IPtyOptions): IPty {
|
|
384
|
+
return new Terminal(file, args, options);
|
|
385
|
+
}
|
|
386
|
+
`;
|
|
387
|
+
|
|
388
|
+
writeFileSync(ptyLoaderPath, loaderContent);
|
|
389
|
+
console.log(` Generated bun-pty loader for ${ptyLib}`);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Generate a platform-specific ngrok loader that embeds the native NAPI module
|
|
394
|
+
* This extracts the .node file at runtime and loads it using process.dlopen
|
|
395
|
+
*/
|
|
396
|
+
function generateNgrokLoader(ngrokLib: string, ngrokPkg: string): void {
|
|
397
|
+
// Use absolute path to the ngrok native module for reliable resolution
|
|
398
|
+
const ngrokNodePath = join(nodeModulesDir, ngrokPkg, ngrokLib).replace(/\\/g, '/');
|
|
399
|
+
|
|
400
|
+
const loaderContent = `/**
|
|
401
|
+
* Auto-generated ngrok loader with embedded native NAPI module
|
|
402
|
+
* DO NOT EDIT - This file is generated by build-binaries.ts
|
|
403
|
+
*
|
|
404
|
+
* This embeds the platform-specific ngrok .node file and extracts it at runtime.
|
|
405
|
+
*/
|
|
406
|
+
|
|
407
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from 'fs';
|
|
408
|
+
import { join } from 'path';
|
|
409
|
+
import { tmpdir } from 'os';
|
|
410
|
+
|
|
411
|
+
// Embed the native ngrok .node file - Bun will include this in the binary
|
|
412
|
+
// @ts-ignore - Bun's file embedding syntax
|
|
413
|
+
import embeddedNgrokPath from '${ngrokNodePath}' with { type: 'file' };
|
|
414
|
+
|
|
415
|
+
// Extract embedded library to temp directory
|
|
416
|
+
const libName = '${ngrokLib}';
|
|
417
|
+
const tempDir = join(tmpdir(), 'ccc-ngrok-lib');
|
|
418
|
+
const extractedLibPath = join(tempDir, libName);
|
|
419
|
+
|
|
420
|
+
if (!existsSync(extractedLibPath)) {
|
|
421
|
+
if (!existsSync(tempDir)) {
|
|
422
|
+
mkdirSync(tempDir, { recursive: true });
|
|
423
|
+
}
|
|
424
|
+
const libData = readFileSync(embeddedNgrokPath);
|
|
425
|
+
writeFileSync(extractedLibPath, libData);
|
|
426
|
+
// Make executable on Unix systems
|
|
427
|
+
try {
|
|
428
|
+
chmodSync(extractedLibPath, 0o755);
|
|
429
|
+
} catch {
|
|
430
|
+
// Ignore chmod errors on Windows
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Load the native module using process.dlopen (NAPI compatible)
|
|
435
|
+
const nativeModule: { exports: Record<string, any> } = { exports: {} };
|
|
436
|
+
process.dlopen(nativeModule, extractedLibPath);
|
|
437
|
+
|
|
438
|
+
// Re-export all ngrok functions from the native binding
|
|
439
|
+
const nativeBinding = nativeModule.exports;
|
|
440
|
+
|
|
441
|
+
export const {
|
|
442
|
+
connect,
|
|
443
|
+
forward,
|
|
444
|
+
disconnect,
|
|
445
|
+
kill,
|
|
446
|
+
Listener,
|
|
447
|
+
listeners,
|
|
448
|
+
getListener,
|
|
449
|
+
getListenerByUrl,
|
|
450
|
+
HttpListenerBuilder,
|
|
451
|
+
TcpListenerBuilder,
|
|
452
|
+
TlsListenerBuilder,
|
|
453
|
+
LabeledListenerBuilder,
|
|
454
|
+
loggingCallback,
|
|
455
|
+
authtoken,
|
|
456
|
+
SessionBuilder,
|
|
457
|
+
Session,
|
|
458
|
+
UpdateRequest,
|
|
459
|
+
} = nativeBinding;
|
|
460
|
+
|
|
461
|
+
// Export default for compatibility
|
|
462
|
+
export default nativeBinding;
|
|
463
|
+
`;
|
|
464
|
+
|
|
465
|
+
writeFileSync(ngrokLoaderPath, loaderContent);
|
|
466
|
+
console.log(` Generated ngrok loader for ${ngrokLib}`);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Clean up the generated loader files
|
|
471
|
+
*/
|
|
472
|
+
function cleanupLoaders(): void {
|
|
473
|
+
if (existsSync(ptyLoaderPath)) {
|
|
474
|
+
unlinkSync(ptyLoaderPath);
|
|
475
|
+
}
|
|
476
|
+
if (existsSync(ngrokLoaderPath)) {
|
|
477
|
+
unlinkSync(ngrokLoaderPath);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Build each target
|
|
482
|
+
for (const { name, target, outfile, ptyLib, ngrokLib, ngrokPkg } of selectedTargets) {
|
|
483
|
+
console.log(`\nBuilding ${name}...`);
|
|
484
|
+
|
|
485
|
+
const outfilePath = join(binariesDir, outfile);
|
|
486
|
+
|
|
487
|
+
try {
|
|
488
|
+
// Ensure ngrok platform package is installed (needed for cross-compilation)
|
|
489
|
+
if (!isNgrokPackageInstalled(ngrokPkg, ngrokLib)) {
|
|
490
|
+
const installed = await installNgrokPackage(ngrokPkg, ngrokLib);
|
|
491
|
+
if (!installed) {
|
|
492
|
+
throw new Error(`Failed to install ${ngrokPkg}`);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Verify the .node file exists
|
|
497
|
+
const ngrokNodePath = join(nodeModulesDir, ngrokPkg, ngrokLib);
|
|
498
|
+
const pkgDir = join(nodeModulesDir, ngrokPkg);
|
|
499
|
+
|
|
500
|
+
// Debug: list package contents
|
|
501
|
+
console.log(` Checking ${pkgDir}...`);
|
|
502
|
+
try {
|
|
503
|
+
const { readdirSync } = await import('fs');
|
|
504
|
+
const files = readdirSync(pkgDir);
|
|
505
|
+
console.log(` Package contents: ${files.join(', ')}`);
|
|
506
|
+
} catch (e) {
|
|
507
|
+
console.log(` Could not read directory: ${e}`);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (!existsSync(ngrokNodePath)) {
|
|
511
|
+
throw new Error(`ngrok .node file not found at ${ngrokNodePath}`);
|
|
512
|
+
}
|
|
513
|
+
console.log(` Verified ngrok .node file exists at ${ngrokNodePath}`);
|
|
514
|
+
|
|
515
|
+
// Verify bun-pty native library exists
|
|
516
|
+
const ptyLibPath = join(nodeModulesDir, 'bun-pty', 'rust-pty', 'target', 'release', ptyLib);
|
|
517
|
+
if (!existsSync(ptyLibPath)) {
|
|
518
|
+
throw new Error(`bun-pty native library not found at ${ptyLibPath}`);
|
|
519
|
+
}
|
|
520
|
+
console.log(` Verified bun-pty native library exists at ${ptyLibPath}`);
|
|
521
|
+
|
|
522
|
+
// Generate platform-specific loaders
|
|
523
|
+
generatePtyLoader(ptyLib);
|
|
524
|
+
generateNgrokLoader(ngrokLib, ngrokPkg);
|
|
525
|
+
|
|
526
|
+
// Build the binary (bun-pty and ngrok are now bundled via the generated loaders)
|
|
527
|
+
await $`bun build --compile --minify --bytecode --target=${target} --define BUILD_VERSION='"${VERSION}"' src/index.ts --outfile ${outfilePath}`;
|
|
528
|
+
console.log(` ✓ Built ${outfile}`);
|
|
529
|
+
} catch (error) {
|
|
530
|
+
console.error(` ✗ Failed to build ${name}:`, error);
|
|
531
|
+
cleanupLoaders();
|
|
532
|
+
process.exit(1);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Clean up generated files
|
|
537
|
+
cleanupLoaders();
|
|
538
|
+
|
|
539
|
+
console.log('\nBuild complete!');
|