@mycadet/cli 0.12.8 → 0.12.9-dev.202610131452.9acab64
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/node_modules/@mycadet/assistant-api/events/show-cloud-login.ts +2 -2
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/package.json +1 -1
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/package.json +1 -1
- package/node_modules/@mycadet/service-contracts/package.json +1 -1
- package/package.json +4 -3
- package/src/__tests__/client-web-config.test.ts +0 -1
- package/src/__tests__/cloud-releases.test.ts +23 -13
- package/src/__tests__/ensure-binary.test.ts +182 -0
- package/src/__tests__/nginx-ingress.test.ts +6 -11
- package/src/__tests__/ngrok-spawn.test.ts +49 -0
- package/src/__tests__/pair.test.ts +149 -78
- package/src/__tests__/ps-cloud-status.test.ts +4 -3
- package/src/__tests__/ps-table.test.ts +79 -0
- package/src/__tests__/tailscale.test.ts +1 -1
- package/src/__tests__/teleport.test.ts +1 -16
- package/src/__tests__/tunnel.test.ts +274 -82
- package/src/__tests__/uninstall.test.ts +141 -9
- package/src/__tests__/wake.test.ts +2 -2
- package/src/commands/backup.ts +3 -3
- package/src/commands/client.ts +4 -6
- package/src/commands/completions.test.ts +104 -0
- package/src/commands/completions.ts +87 -0
- package/src/commands/exec.ts +7 -7
- package/src/commands/flags.ts +2 -2
- package/src/commands/hatch.ts +5 -2
- package/src/commands/logs.ts +7 -7
- package/src/commands/pair.ts +140 -37
- package/src/commands/ps.ts +63 -26
- package/src/commands/restore.ts +2 -2
- package/src/commands/retire.ts +2 -2
- package/src/commands/rollback.ts +2 -2
- package/src/commands/sleep.ts +2 -2
- package/src/commands/teleport.ts +18 -37
- package/src/commands/terminal.ts +1 -1
- package/src/commands/tunnel.ts +101 -78
- package/src/commands/uninstall.ts +108 -73
- package/src/commands/upgrade.ts +8 -8
- package/src/commands/use.ts +1 -1
- package/src/commands/wake.ts +6 -6
- package/src/index.ts +15 -0
- package/src/lib/__tests__/docker.test.ts +168 -20
- package/src/lib/__tests__/job-polling.test.ts +1 -1
- package/src/lib/__tests__/local-gateway-cli.test.ts +42 -0
- package/src/lib/cloud-client.ts +0 -3
- package/src/lib/cloud-login.ts +0 -1
- package/src/lib/cloud-releases.ts +6 -4
- package/src/lib/color.ts +4 -0
- package/src/lib/docker.ts +190 -102
- package/src/lib/ensure-binary.ts +171 -0
- package/src/lib/environments/__tests__/paths.test.ts +27 -1
- package/src/lib/environments/paths.ts +16 -8
- package/src/lib/environments/resolve.ts +2 -4
- package/src/lib/flag-args.test.ts +1 -1
- package/src/lib/flag-args.ts +1 -1
- package/src/lib/guardian-token.ts +1 -1
- package/src/lib/hatch-local.ts +15 -8
- package/src/lib/hatch-next-steps.ts +5 -9
- package/src/lib/job-polling.ts +1 -1
- package/src/lib/local.ts +37 -4
- package/src/lib/nginx-ingress.ts +3 -5
- package/src/lib/pair.ts +1 -1
- package/src/lib/step-runner.ts +6 -1
- package/src/lib/sync-cloud-assistants.ts +2 -2
- package/src/lib/tunnels/cloudflare.ts +21 -11
- package/src/lib/tunnels/ngrok.ts +198 -108
- package/src/lib/tunnels/tailscale.ts +34 -4
- package/src/lib/upgrade-lifecycle.ts +5 -4
|
@@ -84,9 +84,13 @@ const EDGE_PORT = 18080;
|
|
|
84
84
|
const ensureTunnelEdgeMock = mock<typeof nginxIngress.ensureTunnelEdge>(
|
|
85
85
|
async () => ({ port: EDGE_PORT, started: true, includesWebApp: true }),
|
|
86
86
|
);
|
|
87
|
+
const getNginxVersionMock = mock<typeof nginxIngress.getNginxVersion>(
|
|
88
|
+
() => "nginx version (test)",
|
|
89
|
+
);
|
|
87
90
|
mock.module("../lib/nginx-ingress.js", () => ({
|
|
88
91
|
...realNginxIngress,
|
|
89
92
|
ensureTunnelEdge: ensureTunnelEdgeMock,
|
|
93
|
+
getNginxVersion: getNginxVersionMock,
|
|
90
94
|
}));
|
|
91
95
|
|
|
92
96
|
const { tunnel } = await import("../commands/tunnel.js");
|
|
@@ -241,7 +245,14 @@ async function runTunnelCapturingLogs(): Promise<string> {
|
|
|
241
245
|
|
|
242
246
|
describe("tunnel edge targeting", () => {
|
|
243
247
|
beforeEach(() => {
|
|
244
|
-
process.argv = [
|
|
248
|
+
process.argv = [
|
|
249
|
+
"bun",
|
|
250
|
+
"cadet",
|
|
251
|
+
"tunnel",
|
|
252
|
+
"--provider",
|
|
253
|
+
"tailscale",
|
|
254
|
+
"--foreground",
|
|
255
|
+
];
|
|
245
256
|
writeLockfile(makeLocalEntry());
|
|
246
257
|
globalThis.fetch = withPreconnect(async () => {
|
|
247
258
|
throw new Error("gateway unavailable");
|
|
@@ -307,7 +318,14 @@ describe("tunnel edge targeting", () => {
|
|
|
307
318
|
const entry = makeLocalEntry();
|
|
308
319
|
entry.runtimeUrl = "https://stale-tunnel.ngrok-free.dev";
|
|
309
320
|
writeLockfile(entry);
|
|
310
|
-
process.argv = [
|
|
321
|
+
process.argv = [
|
|
322
|
+
"bun",
|
|
323
|
+
"cadet",
|
|
324
|
+
"tunnel",
|
|
325
|
+
"--provider",
|
|
326
|
+
"ngrok",
|
|
327
|
+
"--foreground",
|
|
328
|
+
];
|
|
311
329
|
|
|
312
330
|
const logs = await runTunnelCapturingLogs();
|
|
313
331
|
|
|
@@ -324,12 +342,18 @@ describe("tunnel edge targeting", () => {
|
|
|
324
342
|
});
|
|
325
343
|
expect(runCloudflareTunnelMock).not.toHaveBeenCalled();
|
|
326
344
|
expect(logs).toContain(`Started the nginx edge on 127.0.0.1:${EDGE_PORT}`);
|
|
327
|
-
expect(logs).toContain("serves remote web + webhooks");
|
|
328
345
|
});
|
|
329
346
|
|
|
330
347
|
test("the tunnel level notifies the gateway of the provider's public URL", async () => {
|
|
331
348
|
writeLockfile(makeLocalEntry());
|
|
332
|
-
process.argv = [
|
|
349
|
+
process.argv = [
|
|
350
|
+
"bun",
|
|
351
|
+
"cadet",
|
|
352
|
+
"tunnel",
|
|
353
|
+
"--provider",
|
|
354
|
+
"ngrok",
|
|
355
|
+
"--foreground",
|
|
356
|
+
];
|
|
333
357
|
|
|
334
358
|
await runTunnelCapturingLogs();
|
|
335
359
|
|
|
@@ -345,7 +369,14 @@ describe("tunnel edge targeting", () => {
|
|
|
345
369
|
const cloud = makeCloudEntry();
|
|
346
370
|
const local = makeLocalEntry();
|
|
347
371
|
writeLockfile([cloud, local], cloud.assistantId);
|
|
348
|
-
process.argv = [
|
|
372
|
+
process.argv = [
|
|
373
|
+
"bun",
|
|
374
|
+
"cadet",
|
|
375
|
+
"tunnel",
|
|
376
|
+
"--provider",
|
|
377
|
+
"ngrok",
|
|
378
|
+
"--foreground",
|
|
379
|
+
];
|
|
349
380
|
|
|
350
381
|
const logs = await runTunnelCapturingLogs();
|
|
351
382
|
|
|
@@ -377,6 +408,7 @@ describe("tunnel edge targeting", () => {
|
|
|
377
408
|
"docker-1",
|
|
378
409
|
"--provider",
|
|
379
410
|
"ngrok",
|
|
411
|
+
"--foreground",
|
|
380
412
|
];
|
|
381
413
|
|
|
382
414
|
await runTunnelCapturingLogs();
|
|
@@ -397,7 +429,14 @@ describe("tunnel edge targeting", () => {
|
|
|
397
429
|
const workspaceDir = useTempDefaultWorkspaceDir();
|
|
398
430
|
const docker = makeDockerEntry();
|
|
399
431
|
writeLockfile(docker, docker.assistantId);
|
|
400
|
-
process.argv = [
|
|
432
|
+
process.argv = [
|
|
433
|
+
"bun",
|
|
434
|
+
"cadet",
|
|
435
|
+
"tunnel",
|
|
436
|
+
"--provider",
|
|
437
|
+
"ngrok",
|
|
438
|
+
"--foreground",
|
|
439
|
+
];
|
|
401
440
|
|
|
402
441
|
await runTunnelCapturingLogs();
|
|
403
442
|
|
|
@@ -417,7 +456,14 @@ describe("tunnel edge targeting", () => {
|
|
|
417
456
|
const workspaceDir = useTempDefaultWorkspaceDir();
|
|
418
457
|
const cloud = makeCloudEntry();
|
|
419
458
|
writeLockfile([cloud, makeDockerEntry()], cloud.assistantId);
|
|
420
|
-
process.argv = [
|
|
459
|
+
process.argv = [
|
|
460
|
+
"bun",
|
|
461
|
+
"cadet",
|
|
462
|
+
"tunnel",
|
|
463
|
+
"--provider",
|
|
464
|
+
"ngrok",
|
|
465
|
+
"--foreground",
|
|
466
|
+
];
|
|
421
467
|
|
|
422
468
|
const logs = await runTunnelCapturingLogs();
|
|
423
469
|
|
|
@@ -434,7 +480,14 @@ describe("tunnel edge targeting", () => {
|
|
|
434
480
|
|
|
435
481
|
test("an active cloud assistant with no local entries exits with an error", async () => {
|
|
436
482
|
writeLockfile(makeCloudEntry());
|
|
437
|
-
process.argv = [
|
|
483
|
+
process.argv = [
|
|
484
|
+
"bun",
|
|
485
|
+
"cadet",
|
|
486
|
+
"tunnel",
|
|
487
|
+
"--provider",
|
|
488
|
+
"ngrok",
|
|
489
|
+
"--foreground",
|
|
490
|
+
];
|
|
438
491
|
|
|
439
492
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
440
493
|
|
|
@@ -453,7 +506,14 @@ describe("tunnel edge targeting", () => {
|
|
|
453
506
|
[cloud, makeLocalEntry("assistant-a"), makeLocalEntry("assistant-b")],
|
|
454
507
|
cloud.assistantId,
|
|
455
508
|
);
|
|
456
|
-
process.argv = [
|
|
509
|
+
process.argv = [
|
|
510
|
+
"bun",
|
|
511
|
+
"cadet",
|
|
512
|
+
"tunnel",
|
|
513
|
+
"--provider",
|
|
514
|
+
"ngrok",
|
|
515
|
+
"--foreground",
|
|
516
|
+
];
|
|
457
517
|
|
|
458
518
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
459
519
|
|
|
@@ -471,7 +531,15 @@ describe("tunnel edge targeting", () => {
|
|
|
471
531
|
test("a positional cloud assistant name errors without auto-fallback", async () => {
|
|
472
532
|
const local = makeLocalEntry();
|
|
473
533
|
writeLockfile([makeCloudEntry(), local], local.assistantId);
|
|
474
|
-
process.argv = [
|
|
534
|
+
process.argv = [
|
|
535
|
+
"bun",
|
|
536
|
+
"cadet",
|
|
537
|
+
"tunnel",
|
|
538
|
+
"cloud-1",
|
|
539
|
+
"--provider",
|
|
540
|
+
"ngrok",
|
|
541
|
+
"--foreground",
|
|
542
|
+
];
|
|
475
543
|
|
|
476
544
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
477
545
|
|
|
@@ -490,7 +558,15 @@ describe("tunnel edge targeting", () => {
|
|
|
490
558
|
const local = makeLocalEntry();
|
|
491
559
|
local.name = "Ada";
|
|
492
560
|
writeLockfile([makeCloudEntry(), local], "cloud-1");
|
|
493
|
-
process.argv = [
|
|
561
|
+
process.argv = [
|
|
562
|
+
"bun",
|
|
563
|
+
"cadet",
|
|
564
|
+
"tunnel",
|
|
565
|
+
"Ada",
|
|
566
|
+
"--provider",
|
|
567
|
+
"ngrok",
|
|
568
|
+
"--foreground",
|
|
569
|
+
];
|
|
494
570
|
|
|
495
571
|
await runTunnelCapturingLogs();
|
|
496
572
|
|
|
@@ -514,6 +590,7 @@ describe("tunnel edge targeting", () => {
|
|
|
514
590
|
"Lovelace",
|
|
515
591
|
"--provider",
|
|
516
592
|
"ngrok",
|
|
593
|
+
"--foreground",
|
|
517
594
|
];
|
|
518
595
|
|
|
519
596
|
await runTunnelCapturingLogs();
|
|
@@ -530,7 +607,15 @@ describe("tunnel edge targeting", () => {
|
|
|
530
607
|
const workspaceDir = useTempDefaultWorkspaceDir();
|
|
531
608
|
const local = makeLocalEntry();
|
|
532
609
|
writeLockfile([local, makeAppleContainerEntry()], local.assistantId);
|
|
533
|
-
process.argv = [
|
|
610
|
+
process.argv = [
|
|
611
|
+
"bun",
|
|
612
|
+
"cadet",
|
|
613
|
+
"tunnel",
|
|
614
|
+
"apple-1",
|
|
615
|
+
"--provider",
|
|
616
|
+
"ngrok",
|
|
617
|
+
"--foreground",
|
|
618
|
+
];
|
|
534
619
|
|
|
535
620
|
await runTunnelCapturingLogs();
|
|
536
621
|
|
|
@@ -550,7 +635,14 @@ describe("tunnel edge targeting", () => {
|
|
|
550
635
|
const workspaceDir = useTempDefaultWorkspaceDir();
|
|
551
636
|
const cloud = makeCloudEntry();
|
|
552
637
|
writeLockfile([cloud, makeAppleContainerEntry()], cloud.assistantId);
|
|
553
|
-
process.argv = [
|
|
638
|
+
process.argv = [
|
|
639
|
+
"bun",
|
|
640
|
+
"cadet",
|
|
641
|
+
"tunnel",
|
|
642
|
+
"--provider",
|
|
643
|
+
"ngrok",
|
|
644
|
+
"--foreground",
|
|
645
|
+
];
|
|
554
646
|
|
|
555
647
|
const logs = await runTunnelCapturingLogs();
|
|
556
648
|
|
|
@@ -582,6 +674,7 @@ describe("tunnel edge targeting", () => {
|
|
|
582
674
|
"assistant-b",
|
|
583
675
|
"--provider",
|
|
584
676
|
"ngrok",
|
|
677
|
+
"--foreground",
|
|
585
678
|
];
|
|
586
679
|
|
|
587
680
|
await runTunnelCapturingLogs();
|
|
@@ -596,7 +689,15 @@ describe("tunnel edge targeting", () => {
|
|
|
596
689
|
cloud.name = "Cloudy";
|
|
597
690
|
const local = makeLocalEntry();
|
|
598
691
|
writeLockfile([cloud, local], local.assistantId);
|
|
599
|
-
process.argv = [
|
|
692
|
+
process.argv = [
|
|
693
|
+
"bun",
|
|
694
|
+
"cadet",
|
|
695
|
+
"tunnel",
|
|
696
|
+
"Cloudy",
|
|
697
|
+
"--provider",
|
|
698
|
+
"ngrok",
|
|
699
|
+
"--foreground",
|
|
700
|
+
];
|
|
600
701
|
|
|
601
702
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
602
703
|
|
|
@@ -617,7 +718,15 @@ describe("tunnel edge targeting", () => {
|
|
|
617
718
|
const second = makeLocalEntry("assistant-b");
|
|
618
719
|
second.name = "Ada";
|
|
619
720
|
writeLockfile([first, second], "assistant-a");
|
|
620
|
-
process.argv = [
|
|
721
|
+
process.argv = [
|
|
722
|
+
"bun",
|
|
723
|
+
"cadet",
|
|
724
|
+
"tunnel",
|
|
725
|
+
"Ada",
|
|
726
|
+
"--provider",
|
|
727
|
+
"ngrok",
|
|
728
|
+
"--foreground",
|
|
729
|
+
];
|
|
621
730
|
|
|
622
731
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
623
732
|
|
|
@@ -630,7 +739,15 @@ describe("tunnel edge targeting", () => {
|
|
|
630
739
|
});
|
|
631
740
|
|
|
632
741
|
test("an unknown positional name errors", async () => {
|
|
633
|
-
process.argv = [
|
|
742
|
+
process.argv = [
|
|
743
|
+
"bun",
|
|
744
|
+
"cadet",
|
|
745
|
+
"tunnel",
|
|
746
|
+
"nope",
|
|
747
|
+
"--provider",
|
|
748
|
+
"ngrok",
|
|
749
|
+
"--foreground",
|
|
750
|
+
];
|
|
634
751
|
|
|
635
752
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
636
753
|
|
|
@@ -643,7 +760,14 @@ describe("tunnel edge targeting", () => {
|
|
|
643
760
|
test("targets the edge port for cloudflare", async () => {
|
|
644
761
|
const entry = makeLocalEntry();
|
|
645
762
|
writeLockfile(entry);
|
|
646
|
-
process.argv = [
|
|
763
|
+
process.argv = [
|
|
764
|
+
"bun",
|
|
765
|
+
"cadet",
|
|
766
|
+
"tunnel",
|
|
767
|
+
"--provider",
|
|
768
|
+
"cloudflare",
|
|
769
|
+
"--foreground",
|
|
770
|
+
];
|
|
647
771
|
|
|
648
772
|
await runTunnelCapturingLogs();
|
|
649
773
|
|
|
@@ -658,7 +782,14 @@ describe("tunnel edge targeting", () => {
|
|
|
658
782
|
test("targets the edge port for tailscale and notes a reused webhooks-only edge", async () => {
|
|
659
783
|
const entry = makeLocalEntry();
|
|
660
784
|
writeLockfile(entry);
|
|
661
|
-
process.argv = [
|
|
785
|
+
process.argv = [
|
|
786
|
+
"bun",
|
|
787
|
+
"cadet",
|
|
788
|
+
"tunnel",
|
|
789
|
+
"--provider",
|
|
790
|
+
"tailscale",
|
|
791
|
+
"--foreground",
|
|
792
|
+
];
|
|
662
793
|
ensureTunnelEdgeMock.mockResolvedValue({
|
|
663
794
|
port: EDGE_PORT,
|
|
664
795
|
started: false,
|
|
@@ -673,13 +804,12 @@ describe("tunnel edge targeting", () => {
|
|
|
673
804
|
workspaceDir: join(entry.resources!.instanceDir, "workspace"),
|
|
674
805
|
});
|
|
675
806
|
expect(logs).toContain(`Reusing the nginx edge on 127.0.0.1:${EDGE_PORT}`);
|
|
676
|
-
expect(logs).toContain("serves webhooks only");
|
|
677
807
|
});
|
|
678
808
|
|
|
679
809
|
test("a bare invocation tunnels the default provider", async () => {
|
|
680
810
|
const entry = makeLocalEntry();
|
|
681
811
|
writeLockfile(entry);
|
|
682
|
-
process.argv = ["bun", "cadet", "tunnel"];
|
|
812
|
+
process.argv = ["bun", "cadet", "tunnel", "--foreground"];
|
|
683
813
|
|
|
684
814
|
const logs = await runTunnelCapturingLogs();
|
|
685
815
|
|
|
@@ -698,7 +828,7 @@ describe("tunnel edge targeting", () => {
|
|
|
698
828
|
const entry = makeLocalEntry();
|
|
699
829
|
entry.name = "Ada";
|
|
700
830
|
writeLockfile(entry);
|
|
701
|
-
process.argv = ["bun", "cadet", "tunnel", "Ada"];
|
|
831
|
+
process.argv = ["bun", "cadet", "tunnel", "Ada", "--foreground"];
|
|
702
832
|
|
|
703
833
|
const logs = await runTunnelCapturingLogs();
|
|
704
834
|
|
|
@@ -718,7 +848,14 @@ describe("tunnel edge targeting", () => {
|
|
|
718
848
|
const workspaceDir = writeEntryWorkspaceConfig(entry, {
|
|
719
849
|
telegram: { botUsername: "example_bot" },
|
|
720
850
|
});
|
|
721
|
-
process.argv = [
|
|
851
|
+
process.argv = [
|
|
852
|
+
"bun",
|
|
853
|
+
"cadet",
|
|
854
|
+
"tunnel",
|
|
855
|
+
"--provider",
|
|
856
|
+
"tailscale",
|
|
857
|
+
"--foreground",
|
|
858
|
+
];
|
|
722
859
|
|
|
723
860
|
const warnings: string[] = [];
|
|
724
861
|
const warnSpy = spyOn(console, "warn").mockImplementation(
|
|
@@ -749,7 +886,14 @@ describe("tunnel edge targeting", () => {
|
|
|
749
886
|
const entry = makeLocalEntry();
|
|
750
887
|
writeLockfile(entry);
|
|
751
888
|
const workspaceDir = writeEntryWorkspaceConfig(entry, {});
|
|
752
|
-
process.argv = [
|
|
889
|
+
process.argv = [
|
|
890
|
+
"bun",
|
|
891
|
+
"cadet",
|
|
892
|
+
"tunnel",
|
|
893
|
+
"--provider",
|
|
894
|
+
"tailscale",
|
|
895
|
+
"--foreground",
|
|
896
|
+
];
|
|
753
897
|
|
|
754
898
|
await runTunnelCapturingLogs();
|
|
755
899
|
|
|
@@ -766,7 +910,14 @@ describe("tunnel edge targeting", () => {
|
|
|
766
910
|
const workspaceDir = writeEntryWorkspaceConfig(entry, {
|
|
767
911
|
telegram: { botUsername: "example_bot" },
|
|
768
912
|
});
|
|
769
|
-
process.argv = [
|
|
913
|
+
process.argv = [
|
|
914
|
+
"bun",
|
|
915
|
+
"cadet",
|
|
916
|
+
"tunnel",
|
|
917
|
+
"--provider",
|
|
918
|
+
"ngrok",
|
|
919
|
+
"--foreground",
|
|
920
|
+
];
|
|
770
921
|
|
|
771
922
|
await runTunnelCapturingLogs();
|
|
772
923
|
|
|
@@ -778,11 +929,18 @@ describe("tunnel edge targeting", () => {
|
|
|
778
929
|
});
|
|
779
930
|
|
|
780
931
|
test("missing nginx aborts before any provider spawn", async () => {
|
|
781
|
-
process.argv = [
|
|
932
|
+
process.argv = [
|
|
933
|
+
"bun",
|
|
934
|
+
"cadet",
|
|
935
|
+
"tunnel",
|
|
936
|
+
"--provider",
|
|
937
|
+
"ngrok",
|
|
938
|
+
"--foreground",
|
|
939
|
+
];
|
|
782
940
|
ensureTunnelEdgeMock.mockRejectedValue(
|
|
783
941
|
new Error(
|
|
784
942
|
"nginx is not installed, so the tunnel edge cannot start. " +
|
|
785
|
-
"
|
|
943
|
+
"See https://nginx.org/en/docs/install.html for how to install, " +
|
|
786
944
|
"or point NGINX_BIN at an existing binary.",
|
|
787
945
|
),
|
|
788
946
|
);
|
|
@@ -790,14 +948,23 @@ describe("tunnel edge targeting", () => {
|
|
|
790
948
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
791
949
|
|
|
792
950
|
expect(exited).toBe(true);
|
|
793
|
-
expect(errors).toContain(
|
|
951
|
+
expect(errors).toContain(
|
|
952
|
+
"See https://nginx.org/en/docs/install.html for how to install",
|
|
953
|
+
);
|
|
794
954
|
expect(runNgrokTunnelMock).not.toHaveBeenCalled();
|
|
795
955
|
expect(runCloudflareTunnelMock).not.toHaveBeenCalled();
|
|
796
956
|
expect(runTailscaleTunnelMock).not.toHaveBeenCalled();
|
|
797
957
|
});
|
|
798
958
|
|
|
799
959
|
test("rejects an unknown --provider with a stale-CLI hint", async () => {
|
|
800
|
-
process.argv = [
|
|
960
|
+
process.argv = [
|
|
961
|
+
"bun",
|
|
962
|
+
"cadet",
|
|
963
|
+
"tunnel",
|
|
964
|
+
"--provider",
|
|
965
|
+
"bogus",
|
|
966
|
+
"--foreground",
|
|
967
|
+
];
|
|
801
968
|
|
|
802
969
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
803
970
|
|
|
@@ -814,7 +981,14 @@ describe("tunnel edge targeting", () => {
|
|
|
814
981
|
try {
|
|
815
982
|
for (const provider of TUNNEL_PROVIDERS) {
|
|
816
983
|
writeLockfile(makeLocalEntry());
|
|
817
|
-
process.argv = [
|
|
984
|
+
process.argv = [
|
|
985
|
+
"bun",
|
|
986
|
+
"cadet",
|
|
987
|
+
"tunnel",
|
|
988
|
+
"--provider",
|
|
989
|
+
provider,
|
|
990
|
+
"--foreground",
|
|
991
|
+
];
|
|
818
992
|
|
|
819
993
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
820
994
|
|
|
@@ -837,6 +1011,7 @@ describe("tunnel edge targeting", () => {
|
|
|
837
1011
|
"ngrok",
|
|
838
1012
|
"--domain",
|
|
839
1013
|
"foo.ngrok.app",
|
|
1014
|
+
"--foreground",
|
|
840
1015
|
];
|
|
841
1016
|
|
|
842
1017
|
await runTunnelCapturingLogs();
|
|
@@ -858,6 +1033,7 @@ describe("tunnel edge targeting", () => {
|
|
|
858
1033
|
"cloudflare",
|
|
859
1034
|
"--domain",
|
|
860
1035
|
"foo.ngrok.app",
|
|
1036
|
+
"--foreground",
|
|
861
1037
|
];
|
|
862
1038
|
|
|
863
1039
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
@@ -883,6 +1059,7 @@ describe("tunnel edge targeting", () => {
|
|
|
883
1059
|
"--provider",
|
|
884
1060
|
"ngrok",
|
|
885
1061
|
"--clear-domain",
|
|
1062
|
+
"--foreground",
|
|
886
1063
|
];
|
|
887
1064
|
|
|
888
1065
|
const logs = await runTunnelCapturingLogs();
|
|
@@ -908,6 +1085,7 @@ describe("tunnel edge targeting", () => {
|
|
|
908
1085
|
"--domain",
|
|
909
1086
|
"foo.ngrok.app",
|
|
910
1087
|
"--clear-domain",
|
|
1088
|
+
"--foreground",
|
|
911
1089
|
];
|
|
912
1090
|
|
|
913
1091
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
@@ -925,6 +1103,7 @@ describe("tunnel edge targeting", () => {
|
|
|
925
1103
|
"--provider",
|
|
926
1104
|
"cloudflare",
|
|
927
1105
|
"--clear-domain",
|
|
1106
|
+
"--foreground",
|
|
928
1107
|
];
|
|
929
1108
|
|
|
930
1109
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
@@ -945,6 +1124,7 @@ describe("tunnel edge targeting", () => {
|
|
|
945
1124
|
"--provider",
|
|
946
1125
|
"ngrok",
|
|
947
1126
|
"--domain",
|
|
1127
|
+
"--foreground",
|
|
948
1128
|
];
|
|
949
1129
|
|
|
950
1130
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
@@ -957,7 +1137,14 @@ describe("tunnel edge targeting", () => {
|
|
|
957
1137
|
test("a not-yet-implemented provider error carries the stale-CLI hint", async () => {
|
|
958
1138
|
// `cadet` stays valid-but-unimplemented, so it hits this path rather
|
|
959
1139
|
// than the unknown-provider one.
|
|
960
|
-
process.argv = [
|
|
1140
|
+
process.argv = [
|
|
1141
|
+
"bun",
|
|
1142
|
+
"cadet",
|
|
1143
|
+
"tunnel",
|
|
1144
|
+
"--provider",
|
|
1145
|
+
"cadet",
|
|
1146
|
+
"--foreground",
|
|
1147
|
+
];
|
|
961
1148
|
|
|
962
1149
|
let err: Error | undefined;
|
|
963
1150
|
try {
|
|
@@ -1139,13 +1326,14 @@ describe("ngrok --domain spawn args", () => {
|
|
|
1139
1326
|
expect(spawnMock).toHaveBeenCalledTimes(1);
|
|
1140
1327
|
const [cmd, args] = spawnMock.mock.calls[0];
|
|
1141
1328
|
expect(cmd).toBe("ngrok");
|
|
1329
|
+
// Plain `ngrok http` so the user's default config — where their
|
|
1330
|
+
// authtoken lives — is what the agent loads.
|
|
1142
1331
|
expect(args).toEqual([
|
|
1143
|
-
"
|
|
1144
|
-
"
|
|
1145
|
-
`--config=${realNgrok.ngrokConfigPath()}`,
|
|
1332
|
+
"http",
|
|
1333
|
+
"127.0.0.1:7831",
|
|
1146
1334
|
"--log=stdout",
|
|
1335
|
+
"--domain=foo.ngrok.app",
|
|
1147
1336
|
]);
|
|
1148
|
-
// The reserved domain rides the XDG config the agent loads, not a flag.
|
|
1149
1337
|
expect(realNgrok.loadNgrokDomain()).toBe("foo.ngrok.app");
|
|
1150
1338
|
|
|
1151
1339
|
const config = IngressTestConfigSchema.parse(
|
|
@@ -1430,11 +1618,13 @@ describe("ngrok --domain spawn args", () => {
|
|
|
1430
1618
|
expect(spawnMock).toHaveBeenCalledTimes(1);
|
|
1431
1619
|
const [cmd, args] = spawnMock.mock.calls[0];
|
|
1432
1620
|
expect(cmd).toBe("ngrok");
|
|
1621
|
+
// Plain `ngrok http` so the user's default config — where their
|
|
1622
|
+
// authtoken lives — is what the agent loads.
|
|
1433
1623
|
expect(args).toEqual([
|
|
1434
|
-
"
|
|
1435
|
-
"
|
|
1436
|
-
`--config=${realNgrok.ngrokConfigPath()}`,
|
|
1624
|
+
"http",
|
|
1625
|
+
"127.0.0.1:7830",
|
|
1437
1626
|
"--log=stdout",
|
|
1627
|
+
"--domain=foo.ngrok.app",
|
|
1438
1628
|
]);
|
|
1439
1629
|
|
|
1440
1630
|
const config = IngressTestConfigSchema.parse(
|
|
@@ -1469,10 +1659,10 @@ describe("ngrok --domain spawn args", () => {
|
|
|
1469
1659
|
expect(spawnMock).toHaveBeenCalledTimes(1);
|
|
1470
1660
|
const [, args] = spawnMock.mock.calls[0];
|
|
1471
1661
|
expect(args).toEqual([
|
|
1472
|
-
"
|
|
1473
|
-
"
|
|
1474
|
-
`--config=${realNgrok.ngrokConfigPath()}`,
|
|
1662
|
+
"http",
|
|
1663
|
+
"127.0.0.1:7831",
|
|
1475
1664
|
"--log=stdout",
|
|
1665
|
+
"--domain=foo.ngrok.app",
|
|
1476
1666
|
]);
|
|
1477
1667
|
|
|
1478
1668
|
const config = IngressTestConfigSchema.parse(
|
|
@@ -1483,7 +1673,7 @@ describe("ngrok --domain spawn args", () => {
|
|
|
1483
1673
|
});
|
|
1484
1674
|
});
|
|
1485
1675
|
|
|
1486
|
-
describe("tunnel
|
|
1676
|
+
describe("tunnel background mode", () => {
|
|
1487
1677
|
const originalXdgConfigHome = process.env.XDG_CONFIG_HOME;
|
|
1488
1678
|
|
|
1489
1679
|
function makeFakeChild(pid: number): ChildProcess {
|
|
@@ -1505,13 +1695,22 @@ describe("tunnel --detach", () => {
|
|
|
1505
1695
|
): ChildProcess => makeFakeChild(9999),
|
|
1506
1696
|
);
|
|
1507
1697
|
|
|
1508
|
-
// spawnDetachedTunnel() scopes the log filename to its own (parent) pid,
|
|
1509
|
-
// which in-process is this test runner's pid.
|
|
1510
1698
|
function detachedLogPath(): string {
|
|
1511
|
-
return join(getLogDir(),
|
|
1699
|
+
return join(getLogDir(), "tunnel.log");
|
|
1512
1700
|
}
|
|
1513
1701
|
|
|
1514
1702
|
beforeAll(() => {
|
|
1703
|
+
// The edge-targeting describe's afterAll restores the REAL provider
|
|
1704
|
+
// modules; re-establish the fakes the foreground test here needs.
|
|
1705
|
+
mock.module("../lib/tunnels/ngrok", () => ({
|
|
1706
|
+
...realNgrok,
|
|
1707
|
+
runNgrokTunnel: runNgrokTunnelMock,
|
|
1708
|
+
}));
|
|
1709
|
+
mock.module("../lib/nginx-ingress.js", () => ({
|
|
1710
|
+
...realNginxIngress,
|
|
1711
|
+
ensureTunnelEdge: ensureTunnelEdgeMock,
|
|
1712
|
+
getNginxVersion: getNginxVersionMock,
|
|
1713
|
+
}));
|
|
1515
1714
|
mock.module("node:child_process", () => ({
|
|
1516
1715
|
...realChildProcess,
|
|
1517
1716
|
spawn: spawnMock,
|
|
@@ -1520,10 +1719,22 @@ describe("tunnel --detach", () => {
|
|
|
1520
1719
|
|
|
1521
1720
|
afterAll(() => {
|
|
1522
1721
|
mock.module("node:child_process", () => realChildProcess);
|
|
1722
|
+
mock.module("../lib/tunnels/ngrok", () => realNgrok);
|
|
1723
|
+
mock.module("../lib/nginx-ingress.js", () => realNginxIngress);
|
|
1523
1724
|
});
|
|
1524
1725
|
|
|
1525
1726
|
beforeEach(() => {
|
|
1526
1727
|
spawnMock.mockClear();
|
|
1728
|
+
runNgrokTunnelMock.mockReset();
|
|
1729
|
+
runNgrokTunnelMock.mockResolvedValue(
|
|
1730
|
+
fakeTunnelResult("https://test.example.ngrok.dev"),
|
|
1731
|
+
);
|
|
1732
|
+
ensureTunnelEdgeMock.mockReset();
|
|
1733
|
+
ensureTunnelEdgeMock.mockResolvedValue({
|
|
1734
|
+
port: EDGE_PORT,
|
|
1735
|
+
started: true,
|
|
1736
|
+
includesWebApp: true,
|
|
1737
|
+
});
|
|
1527
1738
|
const configHome = mkdtempSync(join(tmpdir(), "cadet-tunnel-xdg-"));
|
|
1528
1739
|
tempDirs.push(configHome);
|
|
1529
1740
|
process.env.XDG_CONFIG_HOME = configHome;
|
|
@@ -1541,15 +1752,8 @@ describe("tunnel --detach", () => {
|
|
|
1541
1752
|
}
|
|
1542
1753
|
});
|
|
1543
1754
|
|
|
1544
|
-
test("
|
|
1545
|
-
process.argv = [
|
|
1546
|
-
"bun",
|
|
1547
|
-
"cadet",
|
|
1548
|
-
"tunnel",
|
|
1549
|
-
"--provider",
|
|
1550
|
-
"ngrok",
|
|
1551
|
-
"--detach",
|
|
1552
|
-
];
|
|
1755
|
+
test("runs detached by default: re-invokes itself as a detached child and reports readiness", async () => {
|
|
1756
|
+
process.argv = ["bun", "cadet", "tunnel", "--provider", "ngrok"];
|
|
1553
1757
|
|
|
1554
1758
|
// Simulate the detached child writing its readiness line to the log file
|
|
1555
1759
|
// the moment it is spawned.
|
|
@@ -1568,8 +1772,10 @@ describe("tunnel --detach", () => {
|
|
|
1568
1772
|
expect(args).toContain("tunnel");
|
|
1569
1773
|
expect(args).toContain("--provider");
|
|
1570
1774
|
expect(args).toContain("ngrok");
|
|
1571
|
-
|
|
1572
|
-
|
|
1775
|
+
// The child runs the foreground path (it IS the background daemon); the
|
|
1776
|
+
// parent must not hand the default through or the child would daemonize
|
|
1777
|
+
// again.
|
|
1778
|
+
expect(args).toContain("--foreground");
|
|
1573
1779
|
expect(opts?.detached).toBe(true);
|
|
1574
1780
|
|
|
1575
1781
|
expect(logs).toContain("Tunnel established: https://foo.ngrok.app");
|
|
@@ -1582,32 +1788,25 @@ describe("tunnel --detach", () => {
|
|
|
1582
1788
|
expect(ensureTunnelEdgeMock).not.toHaveBeenCalled();
|
|
1583
1789
|
});
|
|
1584
1790
|
|
|
1585
|
-
test("
|
|
1586
|
-
process.argv = ["bun", "cadet", "tunnel", "--provider", "tailscale", "-d"];
|
|
1587
|
-
spawnMock.mockImplementationOnce(() => {
|
|
1588
|
-
appendFileSync(
|
|
1589
|
-
detachedLogPath(),
|
|
1590
|
-
"Tunnel established: https://tailnet.example.ts.net\n",
|
|
1591
|
-
);
|
|
1592
|
-
return makeFakeChild(1234);
|
|
1593
|
-
});
|
|
1594
|
-
|
|
1595
|
-
await runTunnelCapturingLogs();
|
|
1596
|
-
|
|
1597
|
-
const [, args] = spawnMock.mock.calls[0];
|
|
1598
|
-
expect(args).not.toContain("-d");
|
|
1599
|
-
expect(args).not.toContain("--detach");
|
|
1600
|
-
});
|
|
1601
|
-
|
|
1602
|
-
test("reports an early child exit instead of hanging", async () => {
|
|
1791
|
+
test("--foreground runs the tunnel in this process instead of a detached child", async () => {
|
|
1603
1792
|
process.argv = [
|
|
1604
1793
|
"bun",
|
|
1605
1794
|
"cadet",
|
|
1606
1795
|
"tunnel",
|
|
1607
1796
|
"--provider",
|
|
1608
1797
|
"ngrok",
|
|
1609
|
-
"--
|
|
1798
|
+
"--foreground",
|
|
1610
1799
|
];
|
|
1800
|
+
|
|
1801
|
+
await runTunnelCapturingLogs();
|
|
1802
|
+
|
|
1803
|
+
expect(spawnMock).not.toHaveBeenCalled();
|
|
1804
|
+
expect(ensureTunnelEdgeMock).toHaveBeenCalled();
|
|
1805
|
+
expect(runNgrokTunnelMock).toHaveBeenCalled();
|
|
1806
|
+
});
|
|
1807
|
+
|
|
1808
|
+
test("reports an early child exit instead of hanging", async () => {
|
|
1809
|
+
process.argv = ["bun", "cadet", "tunnel", "--provider", "ngrok"];
|
|
1611
1810
|
spawnMock.mockImplementationOnce(() => {
|
|
1612
1811
|
const child = makeFakeChild(4242);
|
|
1613
1812
|
// Listeners attach synchronously right after spawn() returns; defer the
|
|
@@ -1624,14 +1823,7 @@ describe("tunnel --detach", () => {
|
|
|
1624
1823
|
});
|
|
1625
1824
|
|
|
1626
1825
|
test("an adopted ngrok tunnel gets an accurate stop message, not a false kill claim", async () => {
|
|
1627
|
-
process.argv = [
|
|
1628
|
-
"bun",
|
|
1629
|
-
"cadet",
|
|
1630
|
-
"tunnel",
|
|
1631
|
-
"--provider",
|
|
1632
|
-
"ngrok",
|
|
1633
|
-
"--detach",
|
|
1634
|
-
];
|
|
1826
|
+
process.argv = ["bun", "cadet", "tunnel", "--provider", "ngrok"];
|
|
1635
1827
|
spawnMock.mockImplementationOnce(() => {
|
|
1636
1828
|
appendFileSync(
|
|
1637
1829
|
detachedLogPath(),
|