@mycadet/cli 0.12.9 → 0.12.11
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/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +8 -8
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/ingress.ts +1 -1
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +8 -8
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/ingress.ts +1 -1
- package/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +8 -8
- package/node_modules/@mycadet/service-contracts/src/ingress.ts +1 -1
- package/package.json +4 -3
- package/src/__tests__/client-web-config.test.ts +0 -1
- package/src/__tests__/cloud-login-poll.test.ts +69 -0
- package/src/__tests__/devices.test.ts +62 -0
- package/src/__tests__/ensure-binary.test.ts +52 -0
- package/src/__tests__/hatch-next-steps.test.ts +38 -0
- package/src/__tests__/multi-local.test.ts +3 -3
- package/src/__tests__/nginx-ingress.test.ts +58 -26
- package/src/__tests__/pair.test.ts +149 -78
- package/src/__tests__/ps-cloud-status.test.ts +6 -4
- package/src/__tests__/setup.test.ts +14 -4
- package/src/__tests__/teleport.test.ts +0 -15
- package/src/__tests__/tunnel-startup-summary.test.ts +110 -0
- package/src/__tests__/tunnel-stop.test.ts +226 -0
- package/src/__tests__/tunnel.test.ts +265 -70
- package/src/__tests__/uninstall.test.ts +140 -8
- package/src/commands/client.ts +4 -6
- package/src/commands/devices.ts +66 -30
- package/src/commands/hatch.ts +14 -53
- package/src/commands/pair.ts +147 -38
- package/src/commands/ps.ts +10 -20
- package/src/commands/teleport.ts +2 -21
- package/src/commands/tunnel.ts +67 -28
- package/src/commands/uninstall.ts +106 -71
- package/src/commands/upgrade.ts +6 -1
- package/src/lib/__tests__/local-gateway-cli.test.ts +42 -0
- package/src/lib/assistant-config.ts +20 -5
- package/src/lib/cloud-client.ts +0 -3
- package/src/lib/cloud-login-poll.ts +75 -0
- package/src/lib/cloud-login.ts +3 -2
- package/src/lib/color.ts +4 -0
- package/src/lib/docker.ts +46 -70
- package/src/lib/ensure-binary.ts +65 -6
- 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/hatch-local.ts +16 -59
- package/src/lib/hatch-next-steps.ts +18 -9
- package/src/lib/live-voice/tunnel.ts +2 -2
- package/src/lib/local.ts +42 -9
- package/src/lib/nginx-ingress.ts +124 -24
- package/src/lib/step-runner.ts +6 -1
- package/src/lib/tunnel.ts +86 -34
- package/src/lib/tunnels/cadet.ts +12 -1
- package/src/lib/tunnels/cloudflare.ts +36 -1
- package/src/lib/tunnels/ngrok.ts +16 -2
- package/src/lib/tunnels/process-lifecycle.ts +26 -0
- package/src/lib/tunnels/tailscale.ts +79 -1
- package/src/lib/xdg-log.ts +11 -0
|
@@ -53,6 +53,10 @@ const runCloudflareTunnelMock = mock<
|
|
|
53
53
|
mock.module("../lib/tunnels/cloudflare.js", () => ({
|
|
54
54
|
...realCloudflareTunnel,
|
|
55
55
|
runCloudflareTunnel: runCloudflareTunnelMock,
|
|
56
|
+
CLOUDFLARE_PROVIDER: {
|
|
57
|
+
...realCloudflareTunnel.CLOUDFLARE_PROVIDER,
|
|
58
|
+
run: runCloudflareTunnelMock,
|
|
59
|
+
},
|
|
56
60
|
}));
|
|
57
61
|
|
|
58
62
|
const runNgrokTunnelMock = mock<typeof ngrok.runNgrokTunnel>(async () =>
|
|
@@ -61,6 +65,7 @@ const runNgrokTunnelMock = mock<typeof ngrok.runNgrokTunnel>(async () =>
|
|
|
61
65
|
mock.module("../lib/tunnels/ngrok", () => ({
|
|
62
66
|
...realNgrok,
|
|
63
67
|
runNgrokTunnel: runNgrokTunnelMock,
|
|
68
|
+
NGROK_PROVIDER: { ...realNgrok.NGROK_PROVIDER, run: runNgrokTunnelMock },
|
|
64
69
|
}));
|
|
65
70
|
|
|
66
71
|
const runTailscaleTunnelMock = mock<typeof tailscaleTunnel.runTailscaleTunnel>(
|
|
@@ -69,6 +74,10 @@ const runTailscaleTunnelMock = mock<typeof tailscaleTunnel.runTailscaleTunnel>(
|
|
|
69
74
|
mock.module("../lib/tunnels/tailscale.js", () => ({
|
|
70
75
|
...realTailscaleTunnel,
|
|
71
76
|
runTailscaleTunnel: runTailscaleTunnelMock,
|
|
77
|
+
TAILSCALE_PROVIDER: {
|
|
78
|
+
...realTailscaleTunnel.TAILSCALE_PROVIDER,
|
|
79
|
+
run: runTailscaleTunnelMock,
|
|
80
|
+
},
|
|
72
81
|
}));
|
|
73
82
|
|
|
74
83
|
const notifyGatewayOfPublicUrlMock = mock<
|
|
@@ -245,7 +254,14 @@ async function runTunnelCapturingLogs(): Promise<string> {
|
|
|
245
254
|
|
|
246
255
|
describe("tunnel edge targeting", () => {
|
|
247
256
|
beforeEach(() => {
|
|
248
|
-
process.argv = [
|
|
257
|
+
process.argv = [
|
|
258
|
+
"bun",
|
|
259
|
+
"cadet",
|
|
260
|
+
"tunnel",
|
|
261
|
+
"--provider",
|
|
262
|
+
"tailscale",
|
|
263
|
+
"--foreground",
|
|
264
|
+
];
|
|
249
265
|
writeLockfile(makeLocalEntry());
|
|
250
266
|
globalThis.fetch = withPreconnect(async () => {
|
|
251
267
|
throw new Error("gateway unavailable");
|
|
@@ -311,7 +327,14 @@ describe("tunnel edge targeting", () => {
|
|
|
311
327
|
const entry = makeLocalEntry();
|
|
312
328
|
entry.runtimeUrl = "https://stale-tunnel.ngrok-free.dev";
|
|
313
329
|
writeLockfile(entry);
|
|
314
|
-
process.argv = [
|
|
330
|
+
process.argv = [
|
|
331
|
+
"bun",
|
|
332
|
+
"cadet",
|
|
333
|
+
"tunnel",
|
|
334
|
+
"--provider",
|
|
335
|
+
"ngrok",
|
|
336
|
+
"--foreground",
|
|
337
|
+
];
|
|
315
338
|
|
|
316
339
|
const logs = await runTunnelCapturingLogs();
|
|
317
340
|
|
|
@@ -332,7 +355,14 @@ describe("tunnel edge targeting", () => {
|
|
|
332
355
|
|
|
333
356
|
test("the tunnel level notifies the gateway of the provider's public URL", async () => {
|
|
334
357
|
writeLockfile(makeLocalEntry());
|
|
335
|
-
process.argv = [
|
|
358
|
+
process.argv = [
|
|
359
|
+
"bun",
|
|
360
|
+
"cadet",
|
|
361
|
+
"tunnel",
|
|
362
|
+
"--provider",
|
|
363
|
+
"ngrok",
|
|
364
|
+
"--foreground",
|
|
365
|
+
];
|
|
336
366
|
|
|
337
367
|
await runTunnelCapturingLogs();
|
|
338
368
|
|
|
@@ -348,7 +378,14 @@ describe("tunnel edge targeting", () => {
|
|
|
348
378
|
const cloud = makeCloudEntry();
|
|
349
379
|
const local = makeLocalEntry();
|
|
350
380
|
writeLockfile([cloud, local], cloud.assistantId);
|
|
351
|
-
process.argv = [
|
|
381
|
+
process.argv = [
|
|
382
|
+
"bun",
|
|
383
|
+
"cadet",
|
|
384
|
+
"tunnel",
|
|
385
|
+
"--provider",
|
|
386
|
+
"ngrok",
|
|
387
|
+
"--foreground",
|
|
388
|
+
];
|
|
352
389
|
|
|
353
390
|
const logs = await runTunnelCapturingLogs();
|
|
354
391
|
|
|
@@ -380,6 +417,7 @@ describe("tunnel edge targeting", () => {
|
|
|
380
417
|
"docker-1",
|
|
381
418
|
"--provider",
|
|
382
419
|
"ngrok",
|
|
420
|
+
"--foreground",
|
|
383
421
|
];
|
|
384
422
|
|
|
385
423
|
await runTunnelCapturingLogs();
|
|
@@ -400,7 +438,14 @@ describe("tunnel edge targeting", () => {
|
|
|
400
438
|
const workspaceDir = useTempDefaultWorkspaceDir();
|
|
401
439
|
const docker = makeDockerEntry();
|
|
402
440
|
writeLockfile(docker, docker.assistantId);
|
|
403
|
-
process.argv = [
|
|
441
|
+
process.argv = [
|
|
442
|
+
"bun",
|
|
443
|
+
"cadet",
|
|
444
|
+
"tunnel",
|
|
445
|
+
"--provider",
|
|
446
|
+
"ngrok",
|
|
447
|
+
"--foreground",
|
|
448
|
+
];
|
|
404
449
|
|
|
405
450
|
await runTunnelCapturingLogs();
|
|
406
451
|
|
|
@@ -420,7 +465,14 @@ describe("tunnel edge targeting", () => {
|
|
|
420
465
|
const workspaceDir = useTempDefaultWorkspaceDir();
|
|
421
466
|
const cloud = makeCloudEntry();
|
|
422
467
|
writeLockfile([cloud, makeDockerEntry()], cloud.assistantId);
|
|
423
|
-
process.argv = [
|
|
468
|
+
process.argv = [
|
|
469
|
+
"bun",
|
|
470
|
+
"cadet",
|
|
471
|
+
"tunnel",
|
|
472
|
+
"--provider",
|
|
473
|
+
"ngrok",
|
|
474
|
+
"--foreground",
|
|
475
|
+
];
|
|
424
476
|
|
|
425
477
|
const logs = await runTunnelCapturingLogs();
|
|
426
478
|
|
|
@@ -437,7 +489,14 @@ describe("tunnel edge targeting", () => {
|
|
|
437
489
|
|
|
438
490
|
test("an active cloud assistant with no local entries exits with an error", async () => {
|
|
439
491
|
writeLockfile(makeCloudEntry());
|
|
440
|
-
process.argv = [
|
|
492
|
+
process.argv = [
|
|
493
|
+
"bun",
|
|
494
|
+
"cadet",
|
|
495
|
+
"tunnel",
|
|
496
|
+
"--provider",
|
|
497
|
+
"ngrok",
|
|
498
|
+
"--foreground",
|
|
499
|
+
];
|
|
441
500
|
|
|
442
501
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
443
502
|
|
|
@@ -456,7 +515,14 @@ describe("tunnel edge targeting", () => {
|
|
|
456
515
|
[cloud, makeLocalEntry("assistant-a"), makeLocalEntry("assistant-b")],
|
|
457
516
|
cloud.assistantId,
|
|
458
517
|
);
|
|
459
|
-
process.argv = [
|
|
518
|
+
process.argv = [
|
|
519
|
+
"bun",
|
|
520
|
+
"cadet",
|
|
521
|
+
"tunnel",
|
|
522
|
+
"--provider",
|
|
523
|
+
"ngrok",
|
|
524
|
+
"--foreground",
|
|
525
|
+
];
|
|
460
526
|
|
|
461
527
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
462
528
|
|
|
@@ -474,7 +540,15 @@ describe("tunnel edge targeting", () => {
|
|
|
474
540
|
test("a positional cloud assistant name errors without auto-fallback", async () => {
|
|
475
541
|
const local = makeLocalEntry();
|
|
476
542
|
writeLockfile([makeCloudEntry(), local], local.assistantId);
|
|
477
|
-
process.argv = [
|
|
543
|
+
process.argv = [
|
|
544
|
+
"bun",
|
|
545
|
+
"cadet",
|
|
546
|
+
"tunnel",
|
|
547
|
+
"cloud-1",
|
|
548
|
+
"--provider",
|
|
549
|
+
"ngrok",
|
|
550
|
+
"--foreground",
|
|
551
|
+
];
|
|
478
552
|
|
|
479
553
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
480
554
|
|
|
@@ -493,7 +567,15 @@ describe("tunnel edge targeting", () => {
|
|
|
493
567
|
const local = makeLocalEntry();
|
|
494
568
|
local.name = "Ada";
|
|
495
569
|
writeLockfile([makeCloudEntry(), local], "cloud-1");
|
|
496
|
-
process.argv = [
|
|
570
|
+
process.argv = [
|
|
571
|
+
"bun",
|
|
572
|
+
"cadet",
|
|
573
|
+
"tunnel",
|
|
574
|
+
"Ada",
|
|
575
|
+
"--provider",
|
|
576
|
+
"ngrok",
|
|
577
|
+
"--foreground",
|
|
578
|
+
];
|
|
497
579
|
|
|
498
580
|
await runTunnelCapturingLogs();
|
|
499
581
|
|
|
@@ -517,6 +599,7 @@ describe("tunnel edge targeting", () => {
|
|
|
517
599
|
"Lovelace",
|
|
518
600
|
"--provider",
|
|
519
601
|
"ngrok",
|
|
602
|
+
"--foreground",
|
|
520
603
|
];
|
|
521
604
|
|
|
522
605
|
await runTunnelCapturingLogs();
|
|
@@ -533,7 +616,15 @@ describe("tunnel edge targeting", () => {
|
|
|
533
616
|
const workspaceDir = useTempDefaultWorkspaceDir();
|
|
534
617
|
const local = makeLocalEntry();
|
|
535
618
|
writeLockfile([local, makeAppleContainerEntry()], local.assistantId);
|
|
536
|
-
process.argv = [
|
|
619
|
+
process.argv = [
|
|
620
|
+
"bun",
|
|
621
|
+
"cadet",
|
|
622
|
+
"tunnel",
|
|
623
|
+
"apple-1",
|
|
624
|
+
"--provider",
|
|
625
|
+
"ngrok",
|
|
626
|
+
"--foreground",
|
|
627
|
+
];
|
|
537
628
|
|
|
538
629
|
await runTunnelCapturingLogs();
|
|
539
630
|
|
|
@@ -553,7 +644,14 @@ describe("tunnel edge targeting", () => {
|
|
|
553
644
|
const workspaceDir = useTempDefaultWorkspaceDir();
|
|
554
645
|
const cloud = makeCloudEntry();
|
|
555
646
|
writeLockfile([cloud, makeAppleContainerEntry()], cloud.assistantId);
|
|
556
|
-
process.argv = [
|
|
647
|
+
process.argv = [
|
|
648
|
+
"bun",
|
|
649
|
+
"cadet",
|
|
650
|
+
"tunnel",
|
|
651
|
+
"--provider",
|
|
652
|
+
"ngrok",
|
|
653
|
+
"--foreground",
|
|
654
|
+
];
|
|
557
655
|
|
|
558
656
|
const logs = await runTunnelCapturingLogs();
|
|
559
657
|
|
|
@@ -585,6 +683,7 @@ describe("tunnel edge targeting", () => {
|
|
|
585
683
|
"assistant-b",
|
|
586
684
|
"--provider",
|
|
587
685
|
"ngrok",
|
|
686
|
+
"--foreground",
|
|
588
687
|
];
|
|
589
688
|
|
|
590
689
|
await runTunnelCapturingLogs();
|
|
@@ -599,7 +698,15 @@ describe("tunnel edge targeting", () => {
|
|
|
599
698
|
cloud.name = "Cloudy";
|
|
600
699
|
const local = makeLocalEntry();
|
|
601
700
|
writeLockfile([cloud, local], local.assistantId);
|
|
602
|
-
process.argv = [
|
|
701
|
+
process.argv = [
|
|
702
|
+
"bun",
|
|
703
|
+
"cadet",
|
|
704
|
+
"tunnel",
|
|
705
|
+
"Cloudy",
|
|
706
|
+
"--provider",
|
|
707
|
+
"ngrok",
|
|
708
|
+
"--foreground",
|
|
709
|
+
];
|
|
603
710
|
|
|
604
711
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
605
712
|
|
|
@@ -620,7 +727,15 @@ describe("tunnel edge targeting", () => {
|
|
|
620
727
|
const second = makeLocalEntry("assistant-b");
|
|
621
728
|
second.name = "Ada";
|
|
622
729
|
writeLockfile([first, second], "assistant-a");
|
|
623
|
-
process.argv = [
|
|
730
|
+
process.argv = [
|
|
731
|
+
"bun",
|
|
732
|
+
"cadet",
|
|
733
|
+
"tunnel",
|
|
734
|
+
"Ada",
|
|
735
|
+
"--provider",
|
|
736
|
+
"ngrok",
|
|
737
|
+
"--foreground",
|
|
738
|
+
];
|
|
624
739
|
|
|
625
740
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
626
741
|
|
|
@@ -633,7 +748,15 @@ describe("tunnel edge targeting", () => {
|
|
|
633
748
|
});
|
|
634
749
|
|
|
635
750
|
test("an unknown positional name errors", async () => {
|
|
636
|
-
process.argv = [
|
|
751
|
+
process.argv = [
|
|
752
|
+
"bun",
|
|
753
|
+
"cadet",
|
|
754
|
+
"tunnel",
|
|
755
|
+
"nope",
|
|
756
|
+
"--provider",
|
|
757
|
+
"ngrok",
|
|
758
|
+
"--foreground",
|
|
759
|
+
];
|
|
637
760
|
|
|
638
761
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
639
762
|
|
|
@@ -646,7 +769,14 @@ describe("tunnel edge targeting", () => {
|
|
|
646
769
|
test("targets the edge port for cloudflare", async () => {
|
|
647
770
|
const entry = makeLocalEntry();
|
|
648
771
|
writeLockfile(entry);
|
|
649
|
-
process.argv = [
|
|
772
|
+
process.argv = [
|
|
773
|
+
"bun",
|
|
774
|
+
"cadet",
|
|
775
|
+
"tunnel",
|
|
776
|
+
"--provider",
|
|
777
|
+
"cloudflare",
|
|
778
|
+
"--foreground",
|
|
779
|
+
];
|
|
650
780
|
|
|
651
781
|
await runTunnelCapturingLogs();
|
|
652
782
|
|
|
@@ -661,7 +791,14 @@ describe("tunnel edge targeting", () => {
|
|
|
661
791
|
test("targets the edge port for tailscale and notes a reused webhooks-only edge", async () => {
|
|
662
792
|
const entry = makeLocalEntry();
|
|
663
793
|
writeLockfile(entry);
|
|
664
|
-
process.argv = [
|
|
794
|
+
process.argv = [
|
|
795
|
+
"bun",
|
|
796
|
+
"cadet",
|
|
797
|
+
"tunnel",
|
|
798
|
+
"--provider",
|
|
799
|
+
"tailscale",
|
|
800
|
+
"--foreground",
|
|
801
|
+
];
|
|
665
802
|
ensureTunnelEdgeMock.mockResolvedValue({
|
|
666
803
|
port: EDGE_PORT,
|
|
667
804
|
started: false,
|
|
@@ -681,7 +818,7 @@ describe("tunnel edge targeting", () => {
|
|
|
681
818
|
test("a bare invocation tunnels the default provider", async () => {
|
|
682
819
|
const entry = makeLocalEntry();
|
|
683
820
|
writeLockfile(entry);
|
|
684
|
-
process.argv = ["bun", "cadet", "tunnel"];
|
|
821
|
+
process.argv = ["bun", "cadet", "tunnel", "--foreground"];
|
|
685
822
|
|
|
686
823
|
const logs = await runTunnelCapturingLogs();
|
|
687
824
|
|
|
@@ -700,7 +837,7 @@ describe("tunnel edge targeting", () => {
|
|
|
700
837
|
const entry = makeLocalEntry();
|
|
701
838
|
entry.name = "Ada";
|
|
702
839
|
writeLockfile(entry);
|
|
703
|
-
process.argv = ["bun", "cadet", "tunnel", "Ada"];
|
|
840
|
+
process.argv = ["bun", "cadet", "tunnel", "Ada", "--foreground"];
|
|
704
841
|
|
|
705
842
|
const logs = await runTunnelCapturingLogs();
|
|
706
843
|
|
|
@@ -720,7 +857,14 @@ describe("tunnel edge targeting", () => {
|
|
|
720
857
|
const workspaceDir = writeEntryWorkspaceConfig(entry, {
|
|
721
858
|
telegram: { botUsername: "example_bot" },
|
|
722
859
|
});
|
|
723
|
-
process.argv = [
|
|
860
|
+
process.argv = [
|
|
861
|
+
"bun",
|
|
862
|
+
"cadet",
|
|
863
|
+
"tunnel",
|
|
864
|
+
"--provider",
|
|
865
|
+
"tailscale",
|
|
866
|
+
"--foreground",
|
|
867
|
+
];
|
|
724
868
|
|
|
725
869
|
const warnings: string[] = [];
|
|
726
870
|
const warnSpy = spyOn(console, "warn").mockImplementation(
|
|
@@ -751,7 +895,14 @@ describe("tunnel edge targeting", () => {
|
|
|
751
895
|
const entry = makeLocalEntry();
|
|
752
896
|
writeLockfile(entry);
|
|
753
897
|
const workspaceDir = writeEntryWorkspaceConfig(entry, {});
|
|
754
|
-
process.argv = [
|
|
898
|
+
process.argv = [
|
|
899
|
+
"bun",
|
|
900
|
+
"cadet",
|
|
901
|
+
"tunnel",
|
|
902
|
+
"--provider",
|
|
903
|
+
"tailscale",
|
|
904
|
+
"--foreground",
|
|
905
|
+
];
|
|
755
906
|
|
|
756
907
|
await runTunnelCapturingLogs();
|
|
757
908
|
|
|
@@ -768,7 +919,14 @@ describe("tunnel edge targeting", () => {
|
|
|
768
919
|
const workspaceDir = writeEntryWorkspaceConfig(entry, {
|
|
769
920
|
telegram: { botUsername: "example_bot" },
|
|
770
921
|
});
|
|
771
|
-
process.argv = [
|
|
922
|
+
process.argv = [
|
|
923
|
+
"bun",
|
|
924
|
+
"cadet",
|
|
925
|
+
"tunnel",
|
|
926
|
+
"--provider",
|
|
927
|
+
"ngrok",
|
|
928
|
+
"--foreground",
|
|
929
|
+
];
|
|
772
930
|
|
|
773
931
|
await runTunnelCapturingLogs();
|
|
774
932
|
|
|
@@ -780,7 +938,14 @@ describe("tunnel edge targeting", () => {
|
|
|
780
938
|
});
|
|
781
939
|
|
|
782
940
|
test("missing nginx aborts before any provider spawn", async () => {
|
|
783
|
-
process.argv = [
|
|
941
|
+
process.argv = [
|
|
942
|
+
"bun",
|
|
943
|
+
"cadet",
|
|
944
|
+
"tunnel",
|
|
945
|
+
"--provider",
|
|
946
|
+
"ngrok",
|
|
947
|
+
"--foreground",
|
|
948
|
+
];
|
|
784
949
|
ensureTunnelEdgeMock.mockRejectedValue(
|
|
785
950
|
new Error(
|
|
786
951
|
"nginx is not installed, so the tunnel edge cannot start. " +
|
|
@@ -801,7 +966,14 @@ describe("tunnel edge targeting", () => {
|
|
|
801
966
|
});
|
|
802
967
|
|
|
803
968
|
test("rejects an unknown --provider with a stale-CLI hint", async () => {
|
|
804
|
-
process.argv = [
|
|
969
|
+
process.argv = [
|
|
970
|
+
"bun",
|
|
971
|
+
"cadet",
|
|
972
|
+
"tunnel",
|
|
973
|
+
"--provider",
|
|
974
|
+
"bogus",
|
|
975
|
+
"--foreground",
|
|
976
|
+
];
|
|
805
977
|
|
|
806
978
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
807
979
|
|
|
@@ -818,7 +990,14 @@ describe("tunnel edge targeting", () => {
|
|
|
818
990
|
try {
|
|
819
991
|
for (const provider of TUNNEL_PROVIDERS) {
|
|
820
992
|
writeLockfile(makeLocalEntry());
|
|
821
|
-
process.argv = [
|
|
993
|
+
process.argv = [
|
|
994
|
+
"bun",
|
|
995
|
+
"cadet",
|
|
996
|
+
"tunnel",
|
|
997
|
+
"--provider",
|
|
998
|
+
provider,
|
|
999
|
+
"--foreground",
|
|
1000
|
+
];
|
|
822
1001
|
|
|
823
1002
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
824
1003
|
|
|
@@ -841,6 +1020,7 @@ describe("tunnel edge targeting", () => {
|
|
|
841
1020
|
"ngrok",
|
|
842
1021
|
"--domain",
|
|
843
1022
|
"foo.ngrok.app",
|
|
1023
|
+
"--foreground",
|
|
844
1024
|
];
|
|
845
1025
|
|
|
846
1026
|
await runTunnelCapturingLogs();
|
|
@@ -862,6 +1042,7 @@ describe("tunnel edge targeting", () => {
|
|
|
862
1042
|
"cloudflare",
|
|
863
1043
|
"--domain",
|
|
864
1044
|
"foo.ngrok.app",
|
|
1045
|
+
"--foreground",
|
|
865
1046
|
];
|
|
866
1047
|
|
|
867
1048
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
@@ -887,6 +1068,7 @@ describe("tunnel edge targeting", () => {
|
|
|
887
1068
|
"--provider",
|
|
888
1069
|
"ngrok",
|
|
889
1070
|
"--clear-domain",
|
|
1071
|
+
"--foreground",
|
|
890
1072
|
];
|
|
891
1073
|
|
|
892
1074
|
const logs = await runTunnelCapturingLogs();
|
|
@@ -912,6 +1094,7 @@ describe("tunnel edge targeting", () => {
|
|
|
912
1094
|
"--domain",
|
|
913
1095
|
"foo.ngrok.app",
|
|
914
1096
|
"--clear-domain",
|
|
1097
|
+
"--foreground",
|
|
915
1098
|
];
|
|
916
1099
|
|
|
917
1100
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
@@ -929,6 +1112,7 @@ describe("tunnel edge targeting", () => {
|
|
|
929
1112
|
"--provider",
|
|
930
1113
|
"cloudflare",
|
|
931
1114
|
"--clear-domain",
|
|
1115
|
+
"--foreground",
|
|
932
1116
|
];
|
|
933
1117
|
|
|
934
1118
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
@@ -949,6 +1133,7 @@ describe("tunnel edge targeting", () => {
|
|
|
949
1133
|
"--provider",
|
|
950
1134
|
"ngrok",
|
|
951
1135
|
"--domain",
|
|
1136
|
+
"--foreground",
|
|
952
1137
|
];
|
|
953
1138
|
|
|
954
1139
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
@@ -961,7 +1146,14 @@ describe("tunnel edge targeting", () => {
|
|
|
961
1146
|
test("a not-yet-implemented provider error carries the stale-CLI hint", async () => {
|
|
962
1147
|
// `cadet` stays valid-but-unimplemented, so it hits this path rather
|
|
963
1148
|
// than the unknown-provider one.
|
|
964
|
-
process.argv = [
|
|
1149
|
+
process.argv = [
|
|
1150
|
+
"bun",
|
|
1151
|
+
"cadet",
|
|
1152
|
+
"tunnel",
|
|
1153
|
+
"--provider",
|
|
1154
|
+
"cadet",
|
|
1155
|
+
"--foreground",
|
|
1156
|
+
];
|
|
965
1157
|
|
|
966
1158
|
let err: Error | undefined;
|
|
967
1159
|
try {
|
|
@@ -1490,7 +1682,7 @@ describe("ngrok --domain spawn args", () => {
|
|
|
1490
1682
|
});
|
|
1491
1683
|
});
|
|
1492
1684
|
|
|
1493
|
-
describe("tunnel
|
|
1685
|
+
describe("tunnel background mode", () => {
|
|
1494
1686
|
const originalXdgConfigHome = process.env.XDG_CONFIG_HOME;
|
|
1495
1687
|
|
|
1496
1688
|
function makeFakeChild(pid: number): ChildProcess {
|
|
@@ -1512,13 +1704,23 @@ describe("tunnel --detach", () => {
|
|
|
1512
1704
|
): ChildProcess => makeFakeChild(9999),
|
|
1513
1705
|
);
|
|
1514
1706
|
|
|
1515
|
-
// spawnDetachedTunnel() scopes the log filename to its own (parent) pid,
|
|
1516
|
-
// which in-process is this test runner's pid.
|
|
1517
1707
|
function detachedLogPath(): string {
|
|
1518
|
-
return join(getLogDir(),
|
|
1708
|
+
return join(getLogDir(), "tunnel.log");
|
|
1519
1709
|
}
|
|
1520
1710
|
|
|
1521
1711
|
beforeAll(() => {
|
|
1712
|
+
// The edge-targeting describe's afterAll restores the REAL provider
|
|
1713
|
+
// modules; re-establish the fakes the foreground test here needs.
|
|
1714
|
+
mock.module("../lib/tunnels/ngrok", () => ({
|
|
1715
|
+
...realNgrok,
|
|
1716
|
+
runNgrokTunnel: runNgrokTunnelMock,
|
|
1717
|
+
NGROK_PROVIDER: { ...realNgrok.NGROK_PROVIDER, run: runNgrokTunnelMock },
|
|
1718
|
+
}));
|
|
1719
|
+
mock.module("../lib/nginx-ingress.js", () => ({
|
|
1720
|
+
...realNginxIngress,
|
|
1721
|
+
ensureTunnelEdge: ensureTunnelEdgeMock,
|
|
1722
|
+
getNginxVersion: getNginxVersionMock,
|
|
1723
|
+
}));
|
|
1522
1724
|
mock.module("node:child_process", () => ({
|
|
1523
1725
|
...realChildProcess,
|
|
1524
1726
|
spawn: spawnMock,
|
|
@@ -1527,10 +1729,22 @@ describe("tunnel --detach", () => {
|
|
|
1527
1729
|
|
|
1528
1730
|
afterAll(() => {
|
|
1529
1731
|
mock.module("node:child_process", () => realChildProcess);
|
|
1732
|
+
mock.module("../lib/tunnels/ngrok", () => realNgrok);
|
|
1733
|
+
mock.module("../lib/nginx-ingress.js", () => realNginxIngress);
|
|
1530
1734
|
});
|
|
1531
1735
|
|
|
1532
1736
|
beforeEach(() => {
|
|
1533
1737
|
spawnMock.mockClear();
|
|
1738
|
+
runNgrokTunnelMock.mockReset();
|
|
1739
|
+
runNgrokTunnelMock.mockResolvedValue(
|
|
1740
|
+
fakeTunnelResult("https://test.example.ngrok.dev"),
|
|
1741
|
+
);
|
|
1742
|
+
ensureTunnelEdgeMock.mockReset();
|
|
1743
|
+
ensureTunnelEdgeMock.mockResolvedValue({
|
|
1744
|
+
port: EDGE_PORT,
|
|
1745
|
+
started: true,
|
|
1746
|
+
includesWebApp: true,
|
|
1747
|
+
});
|
|
1534
1748
|
const configHome = mkdtempSync(join(tmpdir(), "cadet-tunnel-xdg-"));
|
|
1535
1749
|
tempDirs.push(configHome);
|
|
1536
1750
|
process.env.XDG_CONFIG_HOME = configHome;
|
|
@@ -1548,15 +1762,8 @@ describe("tunnel --detach", () => {
|
|
|
1548
1762
|
}
|
|
1549
1763
|
});
|
|
1550
1764
|
|
|
1551
|
-
test("
|
|
1552
|
-
process.argv = [
|
|
1553
|
-
"bun",
|
|
1554
|
-
"cadet",
|
|
1555
|
-
"tunnel",
|
|
1556
|
-
"--provider",
|
|
1557
|
-
"ngrok",
|
|
1558
|
-
"--detach",
|
|
1559
|
-
];
|
|
1765
|
+
test("runs detached by default: re-invokes itself as a detached child and reports readiness", async () => {
|
|
1766
|
+
process.argv = ["bun", "cadet", "tunnel", "--provider", "ngrok"];
|
|
1560
1767
|
|
|
1561
1768
|
// Simulate the detached child writing its readiness line to the log file
|
|
1562
1769
|
// the moment it is spawned.
|
|
@@ -1575,13 +1782,15 @@ describe("tunnel --detach", () => {
|
|
|
1575
1782
|
expect(args).toContain("tunnel");
|
|
1576
1783
|
expect(args).toContain("--provider");
|
|
1577
1784
|
expect(args).toContain("ngrok");
|
|
1578
|
-
|
|
1579
|
-
|
|
1785
|
+
// The child runs the foreground path (it IS the background daemon); the
|
|
1786
|
+
// parent must not hand the default through or the child would daemonize
|
|
1787
|
+
// again.
|
|
1788
|
+
expect(args).toContain("--foreground");
|
|
1580
1789
|
expect(opts?.detached).toBe(true);
|
|
1581
1790
|
|
|
1582
1791
|
expect(logs).toContain("Tunnel established: https://foo.ngrok.app");
|
|
1583
1792
|
expect(logs).toContain("Running in background (pid 9999)");
|
|
1584
|
-
expect(logs).toContain("Stop with:
|
|
1793
|
+
expect(logs).toContain("Stop with: cadet tunnel stop");
|
|
1585
1794
|
|
|
1586
1795
|
// The parent hands the actual tunnel workflow off to the child: it never
|
|
1587
1796
|
// runs the provider or edge setup itself.
|
|
@@ -1589,32 +1798,25 @@ describe("tunnel --detach", () => {
|
|
|
1589
1798
|
expect(ensureTunnelEdgeMock).not.toHaveBeenCalled();
|
|
1590
1799
|
});
|
|
1591
1800
|
|
|
1592
|
-
test("
|
|
1593
|
-
process.argv = ["bun", "cadet", "tunnel", "--provider", "tailscale", "-d"];
|
|
1594
|
-
spawnMock.mockImplementationOnce(() => {
|
|
1595
|
-
appendFileSync(
|
|
1596
|
-
detachedLogPath(),
|
|
1597
|
-
"Tunnel established: https://tailnet.example.ts.net\n",
|
|
1598
|
-
);
|
|
1599
|
-
return makeFakeChild(1234);
|
|
1600
|
-
});
|
|
1601
|
-
|
|
1602
|
-
await runTunnelCapturingLogs();
|
|
1603
|
-
|
|
1604
|
-
const [, args] = spawnMock.mock.calls[0];
|
|
1605
|
-
expect(args).not.toContain("-d");
|
|
1606
|
-
expect(args).not.toContain("--detach");
|
|
1607
|
-
});
|
|
1608
|
-
|
|
1609
|
-
test("reports an early child exit instead of hanging", async () => {
|
|
1801
|
+
test("--foreground runs the tunnel in this process instead of a detached child", async () => {
|
|
1610
1802
|
process.argv = [
|
|
1611
1803
|
"bun",
|
|
1612
1804
|
"cadet",
|
|
1613
1805
|
"tunnel",
|
|
1614
1806
|
"--provider",
|
|
1615
1807
|
"ngrok",
|
|
1616
|
-
"--
|
|
1808
|
+
"--foreground",
|
|
1617
1809
|
];
|
|
1810
|
+
|
|
1811
|
+
await runTunnelCapturingLogs();
|
|
1812
|
+
|
|
1813
|
+
expect(spawnMock).not.toHaveBeenCalled();
|
|
1814
|
+
expect(ensureTunnelEdgeMock).toHaveBeenCalled();
|
|
1815
|
+
expect(runNgrokTunnelMock).toHaveBeenCalled();
|
|
1816
|
+
});
|
|
1817
|
+
|
|
1818
|
+
test("reports an early child exit instead of hanging", async () => {
|
|
1819
|
+
process.argv = ["bun", "cadet", "tunnel", "--provider", "ngrok"];
|
|
1618
1820
|
spawnMock.mockImplementationOnce(() => {
|
|
1619
1821
|
const child = makeFakeChild(4242);
|
|
1620
1822
|
// Listeners attach synchronously right after spawn() returns; defer the
|
|
@@ -1626,19 +1828,12 @@ describe("tunnel --detach", () => {
|
|
|
1626
1828
|
const { exited, errors } = await runTunnelExpectingExit1();
|
|
1627
1829
|
|
|
1628
1830
|
expect(exited).toBe(true);
|
|
1629
|
-
expect(errors).toContain("exited during startup
|
|
1831
|
+
expect(errors).toContain("Error (1): tunnel process exited during startup");
|
|
1630
1832
|
expect(runNgrokTunnelMock).not.toHaveBeenCalled();
|
|
1631
1833
|
});
|
|
1632
1834
|
|
|
1633
1835
|
test("an adopted ngrok tunnel gets an accurate stop message, not a false kill claim", async () => {
|
|
1634
|
-
process.argv = [
|
|
1635
|
-
"bun",
|
|
1636
|
-
"cadet",
|
|
1637
|
-
"tunnel",
|
|
1638
|
-
"--provider",
|
|
1639
|
-
"ngrok",
|
|
1640
|
-
"--detach",
|
|
1641
|
-
];
|
|
1836
|
+
process.argv = ["bun", "cadet", "tunnel", "--provider", "ngrok"];
|
|
1642
1837
|
spawnMock.mockImplementationOnce(() => {
|
|
1643
1838
|
appendFileSync(
|
|
1644
1839
|
detachedLogPath(),
|