@cimplify/cli 0.12.0 → 0.12.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/dist/{chunk-EL34CORK.mjs → chunk-7P2OGMZ4.mjs} +1 -1
- package/dist/{chunk-W64GWXPF.mjs → chunk-PVHB2MHV.mjs} +1 -1
- package/dist/{dev-3TPDHU67.mjs → dev-5MB2BJ6L.mjs} +476 -29
- package/dist/dispatcher.mjs +7 -7
- package/dist/{doctor-JQYXARYQ.mjs → doctor-6Z6YR37N.mjs} +2 -2
- package/dist/{explain-2I4MWMPY.mjs → explain-2FLJFJP3.mjs} +1 -1
- package/dist/{introspect-TB2QFQMF.mjs → introspect-YBDCOO3I.mjs} +2 -2
- package/dist/{update-T5O6YCIC.mjs → update-VIDP5OKO.mjs} +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { gitDetectRoot, gitCurrentBranch, gitCurrentSha, gitStatusPorcelain } from './chunk-K5464A3L.mjs';
|
|
3
3
|
import { parseEnvFile } from './chunk-DBZ3UOQ2.mjs';
|
|
4
|
-
import { package_default } from './chunk-
|
|
4
|
+
import { package_default } from './chunk-PVHB2MHV.mjs';
|
|
5
5
|
import { parseArgs } from './chunk-C4M3DXKC.mjs';
|
|
6
6
|
import { readAuthOrNull, readProjectLinkOrNull, readProjectState } from './chunk-UBAI443T.mjs';
|
|
7
7
|
import { bold, dim, yellow, green, info, result, red } from './chunk-E2T2SBP5.mjs';
|
|
@@ -6,8 +6,8 @@ import { ApiClient } from './chunk-F4S5KETY.mjs';
|
|
|
6
6
|
import { readAuth, readProjectLink } from './chunk-UBAI443T.mjs';
|
|
7
7
|
import { info, CliError, CLI_ERROR_CODE, dim } from './chunk-E2T2SBP5.mjs';
|
|
8
8
|
import { spawn } from 'child_process';
|
|
9
|
-
import { promises } from 'fs';
|
|
10
|
-
import
|
|
9
|
+
import { promises, watch } from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
11
|
import http from 'http';
|
|
12
12
|
|
|
13
13
|
// src/tunnel/v2/codec.ts
|
|
@@ -367,6 +367,410 @@ function sendableCloseCode(code) {
|
|
|
367
367
|
return legal ? code : void 0;
|
|
368
368
|
}
|
|
369
369
|
|
|
370
|
+
// src/tunnel/v2/control-protocol.ts
|
|
371
|
+
var CONTROL_KIND = "control";
|
|
372
|
+
var LENGTH_PREFIX_BYTES = 4;
|
|
373
|
+
var MAX_MESSAGE_BYTES = 32 * 1024 * 1024;
|
|
374
|
+
var CONTROL_OP = {
|
|
375
|
+
READ: "read",
|
|
376
|
+
WRITE: "write",
|
|
377
|
+
DELETE: "delete",
|
|
378
|
+
LIST: "list",
|
|
379
|
+
LIST_TREE: "list_tree",
|
|
380
|
+
STAT: "stat",
|
|
381
|
+
MKDIR: "mkdir",
|
|
382
|
+
RENAME: "rename",
|
|
383
|
+
WATCH_START: "watch_start",
|
|
384
|
+
WATCH_STOP: "watch_stop",
|
|
385
|
+
EXEC: "exec"
|
|
386
|
+
};
|
|
387
|
+
var CONTROL_ERROR = {
|
|
388
|
+
NOT_FOUND: "not_found",
|
|
389
|
+
INVALID_PATH: "invalid_path",
|
|
390
|
+
PERMISSION_DENIED: "permission_denied",
|
|
391
|
+
IO: "io",
|
|
392
|
+
UNSUPPORTED: "unsupported"};
|
|
393
|
+
var WATCH_EVENT = {
|
|
394
|
+
ADD: "add",
|
|
395
|
+
CHANGE: "change",
|
|
396
|
+
REMOVE: "remove",
|
|
397
|
+
RESYNC: "resync"
|
|
398
|
+
};
|
|
399
|
+
var ENCODING = {
|
|
400
|
+
UTF8: "utf8",
|
|
401
|
+
BASE64: "base64"
|
|
402
|
+
};
|
|
403
|
+
var ControlProtocolError = class extends Error {
|
|
404
|
+
};
|
|
405
|
+
var textEncoder3 = new TextEncoder();
|
|
406
|
+
var textDecoder2 = new TextDecoder();
|
|
407
|
+
function encodeMessage(message) {
|
|
408
|
+
const json = textEncoder3.encode(JSON.stringify(message));
|
|
409
|
+
if (json.length > MAX_MESSAGE_BYTES) {
|
|
410
|
+
throw new ControlProtocolError(`control message ${json.length} exceeds ${MAX_MESSAGE_BYTES}`);
|
|
411
|
+
}
|
|
412
|
+
const framed = new Uint8Array(LENGTH_PREFIX_BYTES + json.length);
|
|
413
|
+
new DataView(framed.buffer).setUint32(0, json.length);
|
|
414
|
+
framed.set(json, LENGTH_PREFIX_BYTES);
|
|
415
|
+
return framed;
|
|
416
|
+
}
|
|
417
|
+
function isRequest(message) {
|
|
418
|
+
return typeof message.op === "string";
|
|
419
|
+
}
|
|
420
|
+
var MessageFramer = class {
|
|
421
|
+
// Annotated Uint8Array (ArrayBufferLike) so concat/subarray/slice results
|
|
422
|
+
// assign back cleanly under TS 5.7's Uint8Array<ArrayBuffer> generic.
|
|
423
|
+
buffer = new Uint8Array(0);
|
|
424
|
+
push(bytes) {
|
|
425
|
+
this.buffer = concat(this.buffer, bytes);
|
|
426
|
+
const messages = [];
|
|
427
|
+
for (; ; ) {
|
|
428
|
+
if (this.buffer.length < LENGTH_PREFIX_BYTES) break;
|
|
429
|
+
const length = new DataView(
|
|
430
|
+
this.buffer.buffer,
|
|
431
|
+
this.buffer.byteOffset,
|
|
432
|
+
LENGTH_PREFIX_BYTES
|
|
433
|
+
).getUint32(0);
|
|
434
|
+
if (length > MAX_MESSAGE_BYTES) {
|
|
435
|
+
throw new ControlProtocolError(`framed length ${length} exceeds ${MAX_MESSAGE_BYTES}`);
|
|
436
|
+
}
|
|
437
|
+
if (this.buffer.length < LENGTH_PREFIX_BYTES + length) break;
|
|
438
|
+
const jsonBytes = this.buffer.subarray(LENGTH_PREFIX_BYTES, LENGTH_PREFIX_BYTES + length);
|
|
439
|
+
messages.push(JSON.parse(textDecoder2.decode(jsonBytes)));
|
|
440
|
+
this.buffer = this.buffer.subarray(LENGTH_PREFIX_BYTES + length);
|
|
441
|
+
}
|
|
442
|
+
if (this.buffer.byteOffset > 0) this.buffer = this.buffer.slice();
|
|
443
|
+
return messages;
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
function concat(a, b) {
|
|
447
|
+
if (a.length === 0) return b;
|
|
448
|
+
if (b.length === 0) return a;
|
|
449
|
+
const out = new Uint8Array(a.length + b.length);
|
|
450
|
+
out.set(a, 0);
|
|
451
|
+
out.set(b, a.length);
|
|
452
|
+
return out;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// src/tunnel/v2/control-agent.ts
|
|
456
|
+
var DEFAULT_IGNORED = /* @__PURE__ */ new Set([
|
|
457
|
+
"node_modules",
|
|
458
|
+
".git",
|
|
459
|
+
".next",
|
|
460
|
+
"dist",
|
|
461
|
+
"build",
|
|
462
|
+
".turbo",
|
|
463
|
+
".cache",
|
|
464
|
+
".cimplify"
|
|
465
|
+
]);
|
|
466
|
+
var ControlFsError = class extends Error {
|
|
467
|
+
constructor(code, message) {
|
|
468
|
+
super(message);
|
|
469
|
+
this.code = code;
|
|
470
|
+
}
|
|
471
|
+
code;
|
|
472
|
+
};
|
|
473
|
+
var ControlKind = class {
|
|
474
|
+
constructor(deps) {
|
|
475
|
+
this.deps = deps;
|
|
476
|
+
}
|
|
477
|
+
deps;
|
|
478
|
+
kind = CONTROL_KIND;
|
|
479
|
+
handle(incoming) {
|
|
480
|
+
void new ControlSession(incoming.stream, this.deps).run();
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
var ControlSession = class {
|
|
484
|
+
constructor(stream, deps) {
|
|
485
|
+
this.stream = stream;
|
|
486
|
+
this.deps = deps;
|
|
487
|
+
}
|
|
488
|
+
stream;
|
|
489
|
+
deps;
|
|
490
|
+
framer = new MessageFramer();
|
|
491
|
+
watch = null;
|
|
492
|
+
epoch = 0;
|
|
493
|
+
seq = 0;
|
|
494
|
+
// Serialize all writes so message bytes never interleave and event order
|
|
495
|
+
// (which carries seq) is preserved.
|
|
496
|
+
sending = Promise.resolve();
|
|
497
|
+
async run() {
|
|
498
|
+
try {
|
|
499
|
+
for (; ; ) {
|
|
500
|
+
const event = await this.stream.recv();
|
|
501
|
+
if (event === null) return;
|
|
502
|
+
if (event.type === "data") {
|
|
503
|
+
for (const message of this.framer.push(event.payload)) {
|
|
504
|
+
if (isRequest(message)) void this.dispatch(message);
|
|
505
|
+
}
|
|
506
|
+
} else if (event.type === "end") {
|
|
507
|
+
continue;
|
|
508
|
+
} else {
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
} finally {
|
|
513
|
+
this.watch?.stop();
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
send(message) {
|
|
517
|
+
const bytes = encodeMessage(message);
|
|
518
|
+
this.sending = this.sending.then(() => this.stream.data(bytes).catch(() => {
|
|
519
|
+
}));
|
|
520
|
+
}
|
|
521
|
+
async dispatch(request) {
|
|
522
|
+
try {
|
|
523
|
+
const data = await this.execute(request);
|
|
524
|
+
this.send({ id: request.id, ok: true, data });
|
|
525
|
+
} catch (error) {
|
|
526
|
+
const code = error instanceof ControlFsError ? error.code : CONTROL_ERROR.IO;
|
|
527
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
528
|
+
this.send({ id: request.id, ok: false, error: { code, message } });
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
async execute(request) {
|
|
532
|
+
const { fs: backend } = this.deps;
|
|
533
|
+
switch (request.op) {
|
|
534
|
+
case CONTROL_OP.READ:
|
|
535
|
+
return backend.read(str(request, FIELD.PATH));
|
|
536
|
+
case CONTROL_OP.WRITE:
|
|
537
|
+
await backend.write(str(request, FIELD.PATH), str(request, FIELD.CONTENT), enc(request));
|
|
538
|
+
return {};
|
|
539
|
+
case CONTROL_OP.DELETE:
|
|
540
|
+
await backend.delete(str(request, FIELD.PATH), request.recursive === true);
|
|
541
|
+
return {};
|
|
542
|
+
case CONTROL_OP.LIST:
|
|
543
|
+
return { entries: await backend.list(str(request, FIELD.PATH)) };
|
|
544
|
+
case CONTROL_OP.LIST_TREE:
|
|
545
|
+
return { paths: await backend.listTree() };
|
|
546
|
+
case CONTROL_OP.STAT:
|
|
547
|
+
return await backend.stat(str(request, FIELD.PATH));
|
|
548
|
+
case CONTROL_OP.MKDIR:
|
|
549
|
+
await backend.mkdir(str(request, FIELD.PATH));
|
|
550
|
+
return {};
|
|
551
|
+
case CONTROL_OP.RENAME:
|
|
552
|
+
await backend.rename(str(request, FIELD.FROM), str(request, FIELD.TO));
|
|
553
|
+
return {};
|
|
554
|
+
case CONTROL_OP.WATCH_START:
|
|
555
|
+
return this.startWatch(request);
|
|
556
|
+
case CONTROL_OP.WATCH_STOP:
|
|
557
|
+
this.watch?.stop();
|
|
558
|
+
this.watch = null;
|
|
559
|
+
return {};
|
|
560
|
+
case CONTROL_OP.EXEC:
|
|
561
|
+
return this.deps.exec.run(execCommand(request));
|
|
562
|
+
default:
|
|
563
|
+
throw new ControlFsError(CONTROL_ERROR.UNSUPPORTED, `unknown op ${request.op}`);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
startWatch(request) {
|
|
567
|
+
this.watch?.stop();
|
|
568
|
+
this.epoch += 1;
|
|
569
|
+
this.seq = 0;
|
|
570
|
+
const epoch = this.epoch;
|
|
571
|
+
const paths = Array.isArray(request.paths) ? request.paths : [WORKSPACE_ROOT];
|
|
572
|
+
this.watch = this.deps.watcher.start(
|
|
573
|
+
paths,
|
|
574
|
+
(type, changed) => {
|
|
575
|
+
if (this.epoch !== epoch) return;
|
|
576
|
+
this.seq += 1;
|
|
577
|
+
this.send({ event: type, paths: changed, seq: this.seq, epoch });
|
|
578
|
+
},
|
|
579
|
+
() => {
|
|
580
|
+
if (this.epoch !== epoch) return;
|
|
581
|
+
this.send({ event: WATCH_EVENT.RESYNC, epoch });
|
|
582
|
+
}
|
|
583
|
+
);
|
|
584
|
+
return { epoch };
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
var WORKSPACE_ROOT = ".";
|
|
588
|
+
var FIELD = {
|
|
589
|
+
PATH: "path",
|
|
590
|
+
CONTENT: "content",
|
|
591
|
+
FROM: "from",
|
|
592
|
+
TO: "to"
|
|
593
|
+
};
|
|
594
|
+
function str(request, field) {
|
|
595
|
+
const value = request[field];
|
|
596
|
+
if (typeof value !== "string") {
|
|
597
|
+
throw new ControlFsError(CONTROL_ERROR.INVALID_PATH, `missing string field '${field}'`);
|
|
598
|
+
}
|
|
599
|
+
return value;
|
|
600
|
+
}
|
|
601
|
+
function enc(request) {
|
|
602
|
+
return request.encoding === ENCODING.BASE64 ? ENCODING.BASE64 : ENCODING.UTF8;
|
|
603
|
+
}
|
|
604
|
+
function execCommand(request) {
|
|
605
|
+
const command = request.command;
|
|
606
|
+
if (typeof command === "string") return command;
|
|
607
|
+
if (Array.isArray(command) && command.every((part) => typeof part === "string")) {
|
|
608
|
+
return command;
|
|
609
|
+
}
|
|
610
|
+
throw new ControlFsError(CONTROL_ERROR.INVALID_PATH, "exec requires a command");
|
|
611
|
+
}
|
|
612
|
+
function resolveInRoot(root, rel) {
|
|
613
|
+
const resolved = path.resolve(root, rel);
|
|
614
|
+
const rootWithSep = root.endsWith(path.sep) ? root : root + path.sep;
|
|
615
|
+
if (resolved !== root && !resolved.startsWith(rootWithSep)) {
|
|
616
|
+
throw new ControlFsError(CONTROL_ERROR.INVALID_PATH, `path escapes workspace: ${rel}`);
|
|
617
|
+
}
|
|
618
|
+
return resolved;
|
|
619
|
+
}
|
|
620
|
+
function isIgnored(rel) {
|
|
621
|
+
return rel.split(/[\\/]/).some((segment) => DEFAULT_IGNORED.has(segment));
|
|
622
|
+
}
|
|
623
|
+
var NodeControlFs = class {
|
|
624
|
+
constructor(root) {
|
|
625
|
+
this.root = root;
|
|
626
|
+
}
|
|
627
|
+
root;
|
|
628
|
+
async read(rel) {
|
|
629
|
+
const abs = resolveInRoot(this.root, rel);
|
|
630
|
+
const bytes = await promises.readFile(abs).catch(rethrow(rel));
|
|
631
|
+
const text = tryDecodeUtf8(bytes);
|
|
632
|
+
return text !== null ? { content: text, encoding: ENCODING.UTF8 } : { content: Buffer.from(bytes).toString("base64"), encoding: ENCODING.BASE64 };
|
|
633
|
+
}
|
|
634
|
+
async write(rel, content, encoding) {
|
|
635
|
+
const abs = resolveInRoot(this.root, rel);
|
|
636
|
+
await promises.mkdir(path.dirname(abs), { recursive: true });
|
|
637
|
+
const bytes = encoding === ENCODING.BASE64 ? Buffer.from(content, "base64") : Buffer.from(content, "utf8");
|
|
638
|
+
await promises.writeFile(abs, bytes).catch(rethrow(rel));
|
|
639
|
+
}
|
|
640
|
+
async delete(rel, recursive) {
|
|
641
|
+
const abs = resolveInRoot(this.root, rel);
|
|
642
|
+
await promises.rm(abs, { recursive, force: true });
|
|
643
|
+
}
|
|
644
|
+
async list(rel) {
|
|
645
|
+
const abs = resolveInRoot(this.root, rel);
|
|
646
|
+
const dirents = await promises.readdir(abs, { withFileTypes: true }).catch(rethrow(rel));
|
|
647
|
+
return dirents.filter((d) => !DEFAULT_IGNORED.has(d.name)).map((d) => ({ name: d.name, dir: d.isDirectory() }));
|
|
648
|
+
}
|
|
649
|
+
async listTree() {
|
|
650
|
+
const out = [];
|
|
651
|
+
const walk = async (dir) => {
|
|
652
|
+
const dirents = await promises.readdir(path.join(this.root, dir), { withFileTypes: true });
|
|
653
|
+
for (const dirent of dirents) {
|
|
654
|
+
if (DEFAULT_IGNORED.has(dirent.name)) continue;
|
|
655
|
+
const rel = dir ? `${dir}/${dirent.name}` : dirent.name;
|
|
656
|
+
if (dirent.isDirectory()) await walk(rel);
|
|
657
|
+
else out.push(rel);
|
|
658
|
+
}
|
|
659
|
+
};
|
|
660
|
+
await walk("");
|
|
661
|
+
return out;
|
|
662
|
+
}
|
|
663
|
+
async stat(rel) {
|
|
664
|
+
const abs = resolveInRoot(this.root, rel);
|
|
665
|
+
const stat = await promises.stat(abs).catch(rethrow(rel));
|
|
666
|
+
return {
|
|
667
|
+
kind: stat.isDirectory() ? "dir" : "file",
|
|
668
|
+
size: stat.size,
|
|
669
|
+
mtimeMs: Math.floor(stat.mtimeMs)
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
async mkdir(rel) {
|
|
673
|
+
await promises.mkdir(resolveInRoot(this.root, rel), { recursive: true });
|
|
674
|
+
}
|
|
675
|
+
async rename(from, to) {
|
|
676
|
+
const absFrom = resolveInRoot(this.root, from);
|
|
677
|
+
const absTo = resolveInRoot(this.root, to);
|
|
678
|
+
await promises.mkdir(path.dirname(absTo), { recursive: true });
|
|
679
|
+
await promises.rename(absFrom, absTo).catch(rethrow(from));
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
var WATCH_DEBOUNCE_MS = 60;
|
|
683
|
+
var NodeControlWatcher = class {
|
|
684
|
+
constructor(root) {
|
|
685
|
+
this.root = root;
|
|
686
|
+
}
|
|
687
|
+
root;
|
|
688
|
+
start(_relPaths, onEvent, onResync) {
|
|
689
|
+
let watcher;
|
|
690
|
+
const pending = /* @__PURE__ */ new Map();
|
|
691
|
+
const flush = (rel) => {
|
|
692
|
+
pending.delete(rel);
|
|
693
|
+
promises.stat(path.join(this.root, rel)).then(
|
|
694
|
+
(stat) => onEvent(stat.isDirectory() ? WATCH_EVENT.ADD : WATCH_EVENT.CHANGE, [rel]),
|
|
695
|
+
() => onEvent(WATCH_EVENT.REMOVE, [rel])
|
|
696
|
+
);
|
|
697
|
+
};
|
|
698
|
+
try {
|
|
699
|
+
watcher = watch(this.root, { recursive: true }, (_type, filename) => {
|
|
700
|
+
if (filename === null) {
|
|
701
|
+
onResync();
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
const rel = filename.toString().split(path.sep).join("/");
|
|
705
|
+
if (isIgnored(rel)) return;
|
|
706
|
+
const existing = pending.get(rel);
|
|
707
|
+
if (existing) clearTimeout(existing);
|
|
708
|
+
pending.set(
|
|
709
|
+
rel,
|
|
710
|
+
setTimeout(() => flush(rel), WATCH_DEBOUNCE_MS)
|
|
711
|
+
);
|
|
712
|
+
});
|
|
713
|
+
} catch (error) {
|
|
714
|
+
onResync();
|
|
715
|
+
return { stop: () => {
|
|
716
|
+
} };
|
|
717
|
+
}
|
|
718
|
+
watcher.on("error", () => onResync());
|
|
719
|
+
return {
|
|
720
|
+
stop: () => {
|
|
721
|
+
for (const timer of pending.values()) clearTimeout(timer);
|
|
722
|
+
pending.clear();
|
|
723
|
+
watcher.close();
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
var EXEC_MAX_BUFFER = 8 * 1024 * 1024;
|
|
729
|
+
var NodeControlExec = class {
|
|
730
|
+
constructor(root) {
|
|
731
|
+
this.root = root;
|
|
732
|
+
}
|
|
733
|
+
root;
|
|
734
|
+
run(command) {
|
|
735
|
+
const [file, args, shell] = Array.isArray(command) ? [command[0], command.slice(1), false] : [command, [], true];
|
|
736
|
+
return new Promise((resolve) => {
|
|
737
|
+
const child = spawn(file, args, { cwd: this.root, shell, env: process.env });
|
|
738
|
+
let stdout = "";
|
|
739
|
+
let stderr = "";
|
|
740
|
+
let truncated = false;
|
|
741
|
+
const cap = (existing, chunk) => {
|
|
742
|
+
if (existing.length >= EXEC_MAX_BUFFER) {
|
|
743
|
+
truncated = true;
|
|
744
|
+
return existing;
|
|
745
|
+
}
|
|
746
|
+
return existing + chunk.toString("utf8");
|
|
747
|
+
};
|
|
748
|
+
child.stdout?.on("data", (chunk) => stdout = cap(stdout, chunk));
|
|
749
|
+
child.stderr?.on("data", (chunk) => stderr = cap(stderr, chunk));
|
|
750
|
+
child.on("error", (error) => resolve({ stdout, stderr: error.message, exitCode: 127 }));
|
|
751
|
+
child.on("close", (code) => {
|
|
752
|
+
if (truncated) stderr += "\n[output truncated]";
|
|
753
|
+
resolve({ stdout, stderr, exitCode: code ?? 0 });
|
|
754
|
+
});
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
function tryDecodeUtf8(bytes) {
|
|
759
|
+
try {
|
|
760
|
+
const text = new TextDecoder("utf8", { fatal: true }).decode(bytes);
|
|
761
|
+
return text;
|
|
762
|
+
} catch {
|
|
763
|
+
return null;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
function rethrow(rel) {
|
|
767
|
+
return (error) => {
|
|
768
|
+
if (error.code === "ENOENT") throw new ControlFsError(CONTROL_ERROR.NOT_FOUND, `not found: ${rel}`);
|
|
769
|
+
if (error.code === "EACCES") throw new ControlFsError(CONTROL_ERROR.PERMISSION_DENIED, rel);
|
|
770
|
+
throw new ControlFsError(CONTROL_ERROR.IO, error.message);
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
|
|
370
774
|
// src/tunnel/protocol.ts
|
|
371
775
|
var MAX_PAYLOAD = 262144;
|
|
372
776
|
var HEADER_LEN2 = 9;
|
|
@@ -419,13 +823,13 @@ function decodeFrame(bytes) {
|
|
|
419
823
|
}
|
|
420
824
|
return { streamId, frameType: rawType, payload: bytes.subarray(HEADER_LEN2) };
|
|
421
825
|
}
|
|
422
|
-
var
|
|
423
|
-
var
|
|
826
|
+
var textEncoder4 = new TextEncoder();
|
|
827
|
+
var textDecoder3 = new TextDecoder();
|
|
424
828
|
function encodeJsonPayload(value) {
|
|
425
|
-
return
|
|
829
|
+
return textEncoder4.encode(JSON.stringify(value));
|
|
426
830
|
}
|
|
427
831
|
function decodeJsonPayload(payload) {
|
|
428
|
-
return JSON.parse(
|
|
832
|
+
return JSON.parse(textDecoder3.decode(payload));
|
|
429
833
|
}
|
|
430
834
|
function* chunkPayload(bytes) {
|
|
431
835
|
if (bytes.length === 0) {
|
|
@@ -454,8 +858,8 @@ var HOP_BY_HOP_HEADERS2 = /* @__PURE__ */ new Set([
|
|
|
454
858
|
"transfer-encoding",
|
|
455
859
|
"upgrade"
|
|
456
860
|
]);
|
|
457
|
-
var
|
|
458
|
-
var
|
|
861
|
+
var textEncoder5 = new TextEncoder();
|
|
862
|
+
var textDecoder4 = new TextDecoder();
|
|
459
863
|
function defaultSocketFactory(url, protocols) {
|
|
460
864
|
const ctor = globalThis.WebSocket;
|
|
461
865
|
if (!ctor) {
|
|
@@ -705,7 +1109,7 @@ var HttpProxyStream = class {
|
|
|
705
1109
|
status: 502,
|
|
706
1110
|
headers: [HEADER_CONTENT_TYPE_TEXT]
|
|
707
1111
|
});
|
|
708
|
-
this.ctx.send(FRAME_TYPE.BODY,
|
|
1112
|
+
this.ctx.send(FRAME_TYPE.BODY, textEncoder5.encode(`dev server unreachable: ${err.message}`));
|
|
709
1113
|
this.ctx.send(FRAME_TYPE.END, EMPTY_PAYLOAD);
|
|
710
1114
|
this.ctx.finish();
|
|
711
1115
|
}
|
|
@@ -736,7 +1140,7 @@ var WsProxyStream = class {
|
|
|
736
1140
|
status: 502,
|
|
737
1141
|
headers: [HEADER_CONTENT_TYPE_TEXT]
|
|
738
1142
|
});
|
|
739
|
-
this.ctx.send(FRAME_TYPE.BODY,
|
|
1143
|
+
this.ctx.send(FRAME_TYPE.BODY, textEncoder5.encode("dev server refused the websocket"));
|
|
740
1144
|
this.ctx.send(FRAME_TYPE.END, EMPTY_PAYLOAD);
|
|
741
1145
|
this.ctx.finish();
|
|
742
1146
|
});
|
|
@@ -748,7 +1152,7 @@ var WsProxyStream = class {
|
|
|
748
1152
|
handleFrame(frame) {
|
|
749
1153
|
switch (frame.frameType) {
|
|
750
1154
|
case FRAME_TYPE.WS_TEXT:
|
|
751
|
-
this.local.send(
|
|
1155
|
+
this.local.send(textDecoder4.decode(frame.payload));
|
|
752
1156
|
break;
|
|
753
1157
|
case FRAME_TYPE.WS_BIN:
|
|
754
1158
|
this.local.send(frame.payload);
|
|
@@ -776,7 +1180,7 @@ var WsProxyStream = class {
|
|
|
776
1180
|
let payload;
|
|
777
1181
|
let frameType;
|
|
778
1182
|
if (typeof data === "string") {
|
|
779
|
-
payload =
|
|
1183
|
+
payload = textEncoder5.encode(data);
|
|
780
1184
|
frameType = FRAME_TYPE.WS_TEXT;
|
|
781
1185
|
} else if (data instanceof ArrayBuffer) {
|
|
782
1186
|
payload = new Uint8Array(data);
|
|
@@ -902,8 +1306,11 @@ var StreamHandleV2 = class {
|
|
|
902
1306
|
}
|
|
903
1307
|
};
|
|
904
1308
|
var CONNECTION_STREAM_ID = 0;
|
|
905
|
-
var
|
|
906
|
-
var
|
|
1309
|
+
var LIVENESS_PING_INTERVAL_MS = 15e3;
|
|
1310
|
+
var LIVENESS_TIMEOUT_MS = 45e3;
|
|
1311
|
+
var FROZEN_TICK_GAP_MS = 6e4;
|
|
1312
|
+
var textEncoder6 = new TextEncoder();
|
|
1313
|
+
var textDecoder5 = new TextDecoder();
|
|
907
1314
|
var SessionV2 = class {
|
|
908
1315
|
constructor(transport, options) {
|
|
909
1316
|
this.transport = transport;
|
|
@@ -911,6 +1318,10 @@ var SessionV2 = class {
|
|
|
911
1318
|
this.nextStreamId = options.role === "service" ? 1 : 2;
|
|
912
1319
|
this.connectionSend = new SendWindow(0);
|
|
913
1320
|
this.connectionRecv = new RecvWindow(options.local.connection_window);
|
|
1321
|
+
this.lastInboundAt = Date.now();
|
|
1322
|
+
this.lastTickAt = Date.now();
|
|
1323
|
+
this.livenessTimer = setInterval(() => this.onLivenessTick(), LIVENESS_PING_INTERVAL_MS);
|
|
1324
|
+
this.livenessTimer.unref?.();
|
|
914
1325
|
transport.onMessage((bytes) => this.onMessage(bytes));
|
|
915
1326
|
transport.onClose((reason) => this.teardown(reason));
|
|
916
1327
|
}
|
|
@@ -925,9 +1336,36 @@ var SessionV2 = class {
|
|
|
925
1336
|
draining = false;
|
|
926
1337
|
closed = false;
|
|
927
1338
|
sending = Promise.resolve();
|
|
1339
|
+
lastInboundAt;
|
|
1340
|
+
lastTickAt;
|
|
1341
|
+
livenessTimer;
|
|
1342
|
+
onLivenessTick() {
|
|
1343
|
+
if (this.closed) return;
|
|
1344
|
+
const now = Date.now();
|
|
1345
|
+
const tickGap = now - this.lastTickAt;
|
|
1346
|
+
this.lastTickAt = now;
|
|
1347
|
+
if (tickGap > FROZEN_TICK_GAP_MS) {
|
|
1348
|
+
this.abort(`process froze ${Math.round(tickGap / 1e3)}s (suspend/resume); redialing`);
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
if (now - this.lastInboundAt > LIVENESS_TIMEOUT_MS) {
|
|
1352
|
+
this.abort(`no inbound frames for ${Math.round((now - this.lastInboundAt) / 1e3)}s`);
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
void this.enqueueSend({
|
|
1356
|
+
streamId: CONNECTION_STREAM_ID,
|
|
1357
|
+
frameType: FRAME_TYPE_V2.PING,
|
|
1358
|
+
payload: new Uint8Array(0)
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
/** Presumed-dead teardown: no farewell frames — the socket is a zombie. */
|
|
1362
|
+
abort(reason) {
|
|
1363
|
+
this.teardown(reason);
|
|
1364
|
+
this.transport.close(1001, reason.slice(0, 100));
|
|
1365
|
+
}
|
|
928
1366
|
/** Send our HELLO; the peer's must arrive as its first frame. */
|
|
929
1367
|
async start() {
|
|
930
|
-
const payload =
|
|
1368
|
+
const payload = textEncoder6.encode(JSON.stringify(this.options.local));
|
|
931
1369
|
await this.transport.send(
|
|
932
1370
|
encodeFrameV2({ streamId: CONNECTION_STREAM_ID, frameType: FRAME_TYPE_V2.HELLO, payload })
|
|
933
1371
|
);
|
|
@@ -981,6 +1419,7 @@ var SessionV2 = class {
|
|
|
981
1419
|
this.fail(`undecodable frame: ${error.message}`);
|
|
982
1420
|
return;
|
|
983
1421
|
}
|
|
1422
|
+
this.lastInboundAt = Date.now();
|
|
984
1423
|
if (!this.peer) {
|
|
985
1424
|
this.onHandshakeFrame(frame);
|
|
986
1425
|
return;
|
|
@@ -1153,7 +1592,7 @@ var SessionV2 = class {
|
|
|
1153
1592
|
}
|
|
1154
1593
|
}
|
|
1155
1594
|
sendHead(streamId, frameType, value) {
|
|
1156
|
-
const payload =
|
|
1595
|
+
const payload = textEncoder6.encode(JSON.stringify(value ?? null));
|
|
1157
1596
|
if (payload.length > MAX_HEAD_BYTES) {
|
|
1158
1597
|
return Promise.reject(new SessionV2Error("head exceeds MAX_HEAD_BYTES"));
|
|
1159
1598
|
}
|
|
@@ -1268,6 +1707,7 @@ var SessionV2 = class {
|
|
|
1268
1707
|
teardown(reason) {
|
|
1269
1708
|
if (this.closed) return;
|
|
1270
1709
|
this.closed = true;
|
|
1710
|
+
clearInterval(this.livenessTimer);
|
|
1271
1711
|
for (const streamId of this.streams.keys()) {
|
|
1272
1712
|
this.dropStream(streamId);
|
|
1273
1713
|
}
|
|
@@ -1277,7 +1717,7 @@ var SessionV2 = class {
|
|
|
1277
1717
|
function parseJson(payload) {
|
|
1278
1718
|
if (payload.length === 0) return null;
|
|
1279
1719
|
try {
|
|
1280
|
-
return JSON.parse(
|
|
1720
|
+
return JSON.parse(textDecoder5.decode(payload));
|
|
1281
1721
|
} catch {
|
|
1282
1722
|
return null;
|
|
1283
1723
|
}
|
|
@@ -1373,7 +1813,13 @@ var NegotiatingAgentClient = class {
|
|
|
1373
1813
|
const hello = defaultHello();
|
|
1374
1814
|
const transport = new WsTransportV2(socket);
|
|
1375
1815
|
const createLocalSocket = this.options.createLocalSocket ?? defaultSocketFactory;
|
|
1376
|
-
const dispatcher = new AgentDispatcher().register(new HttpProxyKind(makeAgentContext(this.options.localPort, hello, createLocalSocket))).register(new WsSpliceKind(makeAgentContext(this.options.localPort, hello, createLocalSocket)))
|
|
1816
|
+
const dispatcher = new AgentDispatcher().register(new HttpProxyKind(makeAgentContext(this.options.localPort, hello, createLocalSocket))).register(new WsSpliceKind(makeAgentContext(this.options.localPort, hello, createLocalSocket))).register(
|
|
1817
|
+
new ControlKind({
|
|
1818
|
+
fs: new NodeControlFs(this.options.workspaceRoot),
|
|
1819
|
+
watcher: new NodeControlWatcher(this.options.workspaceRoot),
|
|
1820
|
+
exec: new NodeControlExec(this.options.workspaceRoot)
|
|
1821
|
+
})
|
|
1822
|
+
);
|
|
1377
1823
|
const session = new SessionV2(transport, {
|
|
1378
1824
|
role: "agent",
|
|
1379
1825
|
local: hello,
|
|
@@ -1412,7 +1858,7 @@ var TUNNEL_CONFIG_DIR = ".cimplify";
|
|
|
1412
1858
|
var TUNNEL_CONFIG_FILE = "tunnel.json";
|
|
1413
1859
|
var DEFAULT_DEV_SERVER_PORT = 3e3;
|
|
1414
1860
|
function tunnelConfigPath(cwd) {
|
|
1415
|
-
return
|
|
1861
|
+
return path.join(cwd, TUNNEL_CONFIG_DIR, TUNNEL_CONFIG_FILE);
|
|
1416
1862
|
}
|
|
1417
1863
|
async function readTunnelConfig(cwd, env = process.env) {
|
|
1418
1864
|
const url = env[TUNNEL_URL_ENV];
|
|
@@ -1461,17 +1907,18 @@ var CONFIG_POLL_MS = 2e3;
|
|
|
1461
1907
|
var RECONNECT_MIN_MS = 1e3;
|
|
1462
1908
|
var RECONNECT_MAX_MS = 3e4;
|
|
1463
1909
|
var CLOSE_CODE_REPLACED = 4e3;
|
|
1464
|
-
function
|
|
1465
|
-
return new NegotiatingAgentClient({
|
|
1910
|
+
function makeDefaultCreateClient(workspaceRoot) {
|
|
1911
|
+
return (config, events) => new NegotiatingAgentClient({
|
|
1466
1912
|
url: config.url,
|
|
1467
1913
|
token: config.token,
|
|
1468
1914
|
localPort: config.localPort,
|
|
1915
|
+
workspaceRoot,
|
|
1469
1916
|
events
|
|
1470
1917
|
});
|
|
1471
1918
|
}
|
|
1472
1919
|
function startTunnelRuntime(cwd, options = {}) {
|
|
1473
1920
|
const pollIntervalMs = options.pollIntervalMs ?? CONFIG_POLL_MS;
|
|
1474
|
-
const createClient = options.createClient ??
|
|
1921
|
+
const createClient = options.createClient ?? makeDefaultCreateClient(cwd);
|
|
1475
1922
|
const onStatus = options.onStatus ?? ((message) => info(dim(message)));
|
|
1476
1923
|
let stopped = false;
|
|
1477
1924
|
let generation = 0;
|
|
@@ -1609,11 +2056,11 @@ function envEndpoint(businessId, projectId) {
|
|
|
1609
2056
|
return `/v1/businesses/${encodeURIComponent(businessId)}/projects/${encodeURIComponent(projectId)}/env-vars`;
|
|
1610
2057
|
}
|
|
1611
2058
|
async function detectPackageManager(cwd) {
|
|
1612
|
-
if (await pathExists(
|
|
1613
|
-
if (await pathExists(
|
|
1614
|
-
if (await pathExists(
|
|
1615
|
-
if (await pathExists(
|
|
1616
|
-
if (await pathExists(
|
|
2059
|
+
if (await pathExists(path.join(cwd, LOCKFILE_BUN))) return PM_BUN;
|
|
2060
|
+
if (await pathExists(path.join(cwd, LOCKFILE_BUN_BINARY))) return PM_BUN;
|
|
2061
|
+
if (await pathExists(path.join(cwd, BUNFIG))) return PM_BUN;
|
|
2062
|
+
if (await pathExists(path.join(cwd, LOCKFILE_PNPM))) return PM_PNPM;
|
|
2063
|
+
if (await pathExists(path.join(cwd, LOCKFILE_YARN))) return PM_YARN;
|
|
1617
2064
|
return PM_NPM;
|
|
1618
2065
|
}
|
|
1619
2066
|
async function pathExists(target) {
|
|
@@ -1626,7 +2073,7 @@ async function pathExists(target) {
|
|
|
1626
2073
|
}
|
|
1627
2074
|
}
|
|
1628
2075
|
async function readPackageJson(cwd) {
|
|
1629
|
-
const pkgPath =
|
|
2076
|
+
const pkgPath = path.join(cwd, PACKAGE_JSON);
|
|
1630
2077
|
let raw;
|
|
1631
2078
|
try {
|
|
1632
2079
|
raw = await promises.readFile(pkgPath, "utf8");
|
|
@@ -1657,7 +2104,7 @@ async function applyRemoteEnv(client, businessId, projectId, apiBaseUrl, cwd) {
|
|
|
1657
2104
|
});
|
|
1658
2105
|
const incoming = records.filter((r) => r.value !== null && !r.is_secret).map((r) => ({ key: r.key, value: r.value }));
|
|
1659
2106
|
incoming.push({ key: PUBLIC_API_URL_KEY, value: apiBaseUrl });
|
|
1660
|
-
const envPath =
|
|
2107
|
+
const envPath = path.join(cwd, ENV_LOCAL_FILE);
|
|
1661
2108
|
let existing = [];
|
|
1662
2109
|
try {
|
|
1663
2110
|
const raw = await promises.readFile(envPath, "utf8");
|
package/dist/dispatcher.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { package_default } from './chunk-
|
|
2
|
+
import { package_default } from './chunk-PVHB2MHV.mjs';
|
|
3
3
|
import { TEMPLATES } from './chunk-W3WLADJP.mjs';
|
|
4
4
|
|
|
5
5
|
// src/dispatcher.ts
|
|
@@ -142,18 +142,18 @@ var COMMANDS = {
|
|
|
142
142
|
domains: () => import('./domains-H3K2WW55.mjs'),
|
|
143
143
|
logs: () => import('./logs-2ECJQ3MU.mjs'),
|
|
144
144
|
status: () => import('./status-IA7PJ4TV.mjs'),
|
|
145
|
-
dev: () => import('./dev-
|
|
145
|
+
dev: () => import('./dev-5MB2BJ6L.mjs'),
|
|
146
146
|
mock: () => import('./mock-TSUKCLWU.mjs'),
|
|
147
|
-
introspect: () => import('./introspect-
|
|
147
|
+
introspect: () => import('./introspect-YBDCOO3I.mjs'),
|
|
148
148
|
inspect: () => import('./inspect-CGYX4DDF.mjs'),
|
|
149
|
-
doctor: () => import('./doctor-
|
|
150
|
-
explain: () => import('./explain-
|
|
149
|
+
doctor: () => import('./doctor-6Z6YR37N.mjs'),
|
|
150
|
+
explain: () => import('./explain-2FLJFJP3.mjs'),
|
|
151
151
|
assets: () => import('./assets-4D7VXRUE.mjs'),
|
|
152
152
|
repo: () => import('./repo-WSUAHXWO.mjs'),
|
|
153
153
|
list: () => import('./list-27TS2L47.mjs'),
|
|
154
154
|
add: () => import('./add-5ODVFWVR.mjs'),
|
|
155
|
-
update: () => import('./update-
|
|
156
|
-
upgrade: () => import('./update-
|
|
155
|
+
update: () => import('./update-VIDP5OKO.mjs'),
|
|
156
|
+
upgrade: () => import('./update-VIDP5OKO.mjs'),
|
|
157
157
|
"auth-step-up": () => import('./auth-step-up-NOSM4PUR.mjs')
|
|
158
158
|
};
|
|
159
159
|
var COMMAND_PREFIXES = {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { gatherIntrospection } from './chunk-
|
|
2
|
+
import { gatherIntrospection } from './chunk-7P2OGMZ4.mjs';
|
|
3
3
|
import './chunk-K5464A3L.mjs';
|
|
4
4
|
import './chunk-DBZ3UOQ2.mjs';
|
|
5
|
-
import './chunk-
|
|
5
|
+
import './chunk-PVHB2MHV.mjs';
|
|
6
6
|
import { parseArgs, flagBool } from './chunk-C4M3DXKC.mjs';
|
|
7
7
|
import { SDK_ENV_API_URL, DEFAULT_API_BASE_URL, ApiClient, ENV_API_BASE_URL } from './chunk-F4S5KETY.mjs';
|
|
8
8
|
import { readAuthOrNull } from './chunk-UBAI443T.mjs';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { package_default } from './chunk-
|
|
2
|
+
import { package_default } from './chunk-PVHB2MHV.mjs';
|
|
3
3
|
import { parseArgs } from './chunk-C4M3DXKC.mjs';
|
|
4
4
|
import { bold, dim, info, result, CliError, CLI_ERROR_CODE } from './chunk-E2T2SBP5.mjs';
|
|
5
5
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export { run as default, extractMockSeed, gatherIntrospection, renderIntrospection } from './chunk-
|
|
2
|
+
export { run as default, extractMockSeed, gatherIntrospection, renderIntrospection } from './chunk-7P2OGMZ4.mjs';
|
|
3
3
|
import './chunk-K5464A3L.mjs';
|
|
4
4
|
import './chunk-DBZ3UOQ2.mjs';
|
|
5
|
-
import './chunk-
|
|
5
|
+
import './chunk-PVHB2MHV.mjs';
|
|
6
6
|
import './chunk-C4M3DXKC.mjs';
|
|
7
7
|
import './chunk-UBAI443T.mjs';
|
|
8
8
|
import './chunk-E2T2SBP5.mjs';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { package_default } from './chunk-
|
|
2
|
+
import { package_default } from './chunk-PVHB2MHV.mjs';
|
|
3
3
|
import { promptYesNo } from './chunk-ITAFAORS.mjs';
|
|
4
4
|
import { parseArgs, flagBool, flagString } from './chunk-C4M3DXKC.mjs';
|
|
5
5
|
import { success, bold, info, dim, result, failure, CliError, CLI_ERROR_CODE, step, isJsonMode } from './chunk-E2T2SBP5.mjs';
|