@mycadet/cli 0.12.7 → 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/README.md +2 -2
- package/knip.json +1 -1
- package/node_modules/@mycadet/assistant-api/events/cloud-disconnected.ts +21 -0
- package/node_modules/@mycadet/assistant-api/events/host-bash.ts +1 -1
- package/node_modules/@mycadet/assistant-api/events/host-cu.ts +1 -1
- package/node_modules/@mycadet/assistant-api/events/host-file.ts +1 -1
- package/node_modules/@mycadet/assistant-api/events/notification-intent.ts +1 -1
- package/node_modules/@mycadet/assistant-api/events/show-cloud-login.ts +18 -0
- package/node_modules/@mycadet/assistant-api/index.ts +11 -11
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/package.json +2 -2
- package/node_modules/@mycadet/{service-contracts/src/platform-credential.ts → assistant-api/node_modules/@mycadet/service-contracts/src/cloud-credential.ts} +9 -9
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
- package/node_modules/@mycadet/assistant-api/responses/home.ts +1 -1
- package/node_modules/@mycadet/assistant-api/surfaces.ts +4 -4
- package/node_modules/@mycadet/avatar-manifest/src/__tests__/notification-avatar.test.ts +1 -1
- package/node_modules/@mycadet/avatar-manifest/src/notification-avatar.ts +2 -2
- package/node_modules/@mycadet/environments/src/seeds.ts +6 -6
- package/node_modules/@mycadet/environments/src/shell.test.ts +1 -1
- package/node_modules/@mycadet/environments/src/types.ts +6 -6
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/avatar-manifest/src/__tests__/notification-avatar.test.ts +1 -1
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/avatar-manifest/src/notification-avatar.ts +2 -2
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/environments/src/seeds.ts +6 -6
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/environments/src/shell.test.ts +1 -1
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/environments/src/types.ts +6 -6
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/package.json +2 -2
- package/node_modules/@mycadet/{assistant-api/node_modules/@mycadet/service-contracts/src/platform-credential.ts → local-mode/node_modules/@mycadet/service-contracts/src/cloud-credential.ts} +9 -9
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
- package/node_modules/@mycadet/local-mode/src/__tests__/pair.test.ts +5 -5
- package/node_modules/@mycadet/local-mode/src/__tests__/retire.test.ts +1 -1
- package/node_modules/@mycadet/local-mode/src/config.ts +2 -2
- package/node_modules/@mycadet/local-mode/src/index.ts +1 -1
- package/node_modules/@mycadet/local-mode/src/lockfile-contract.test.ts +3 -3
- package/node_modules/@mycadet/local-mode/src/lockfile-contract.ts +10 -5
- package/node_modules/@mycadet/local-mode/src/lockfile.test.ts +15 -15
- package/node_modules/@mycadet/local-mode/src/lockfile.ts +9 -9
- package/node_modules/@mycadet/local-mode/src/pair.ts +2 -2
- package/node_modules/@mycadet/local-mode/src/retire.ts +3 -3
- package/node_modules/@mycadet/local-mode/src/teleport-backup-policy.ts +2 -2
- package/node_modules/@mycadet/local-mode/src/unpair.ts +2 -2
- package/node_modules/@mycadet/service-contracts/package.json +2 -2
- package/node_modules/@mycadet/{local-mode/node_modules/@mycadet/service-contracts/src/platform-credential.ts → service-contracts/src/cloud-credential.ts} +9 -9
- package/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
- package/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
- package/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
- package/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
- package/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
- package/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
- package/package.json +6 -4
- package/src/__tests__/assistant-api-key-resolution.test.ts +6 -6
- package/src/__tests__/{assistant-client-platform-auth.test.ts → assistant-client-cloud-auth.test.ts} +10 -10
- package/src/__tests__/assistant-client-refresh.test.ts +2 -2
- package/src/__tests__/backup.test.ts +86 -91
- package/src/__tests__/client-token.test.ts +1 -1
- package/src/__tests__/client-tui-refresh.test.ts +1 -1
- package/src/__tests__/client-web-config.test.ts +1 -2
- package/src/__tests__/{platform-client.test.ts → cloud-client.test.ts} +59 -59
- package/src/__tests__/{platform-releases.test.ts → cloud-releases.test.ts} +28 -18
- package/src/__tests__/{cloudflare-tunnel.test.ts → cloudflare.test.ts} +1 -1
- package/src/__tests__/connect-import.test.ts +55 -39
- package/src/__tests__/debug.test.ts +38 -38
- package/src/__tests__/ensure-binary.test.ts +182 -0
- package/src/__tests__/guardian-token.test.ts +149 -56
- package/src/__tests__/hatch-provider-secrets.test.ts +2 -2
- package/src/__tests__/helpers/guardian-token.ts +22 -0
- package/src/__tests__/helpers/http.ts +10 -0
- package/src/__tests__/nginx-ingress.test.ts +15 -20
- package/src/__tests__/ngrok-spawn.test.ts +49 -0
- package/src/__tests__/orphan-detection.test.ts +1 -1
- package/src/__tests__/pair.test.ts +149 -85
- package/src/__tests__/{ps-platform-status.test.ts → ps-cloud-status.test.ts} +80 -26
- package/src/__tests__/ps-table.test.ts +79 -0
- package/src/__tests__/{recover.test.ts → restore.test.ts} +29 -28
- package/src/__tests__/retire.test.ts +58 -199
- package/src/__tests__/root-process-lockfile.test.ts +99 -0
- package/src/__tests__/sleep.test.ts +19 -19
- package/src/__tests__/statefulset.test.ts +8 -8
- package/src/__tests__/{tailscale-tunnel.test.ts → tailscale.test.ts} +2 -2
- package/src/__tests__/teleport-backup.test.ts +3 -3
- package/src/__tests__/teleport.test.ts +180 -211
- package/src/__tests__/tunnel.test.ts +490 -270
- package/src/__tests__/uninstall.test.ts +150 -9
- package/src/__tests__/upgrade-local.test.ts +3 -3
- package/src/__tests__/upgrade-replay-env.test.ts +12 -12
- package/src/__tests__/wake.test.ts +260 -43
- package/src/__tests__/workos-pkce.test.ts +2 -2
- package/src/commands/backup.ts +24 -24
- package/src/commands/client.ts +76 -78
- package/src/commands/completions.test.ts +104 -0
- package/src/commands/completions.ts +87 -0
- package/src/commands/debug.ts +20 -20
- package/src/commands/exec.ts +12 -12
- package/src/commands/flags.ts +2 -2
- package/src/commands/hatch.ts +27 -24
- package/src/commands/login.ts +22 -22
- package/src/commands/logs.ts +8 -8
- package/src/commands/pair.ts +140 -37
- package/src/commands/ps.ts +232 -86
- package/src/commands/restart.ts +30 -0
- package/src/commands/restore.ts +140 -22
- package/src/commands/retire.ts +48 -150
- package/src/commands/rollback.ts +20 -26
- package/src/commands/setup.ts +2 -2
- package/src/commands/sleep.ts +2 -2
- package/src/commands/teleport.ts +110 -129
- package/src/commands/terminal.ts +158 -18
- package/src/commands/tunnel.ts +156 -175
- package/src/commands/uninstall.ts +137 -54
- package/src/commands/unpair.ts +3 -3
- package/src/commands/upgrade.ts +30 -54
- package/src/commands/use.ts +1 -1
- package/src/commands/wake.ts +166 -50
- package/src/index.ts +18 -9
- package/src/lib/__tests__/{platform-client-signed-url.test.ts → cloud-client-signed-url.test.ts} +16 -16
- package/src/lib/__tests__/docker.test.ts +168 -20
- package/src/lib/__tests__/job-polling.test.ts +2 -2
- package/src/lib/__tests__/local-gateway-cli.test.ts +42 -0
- package/src/lib/__tests__/local-runtime-client.test.ts +4 -4
- package/src/lib/__tests__/runtime-url.test.ts +1 -1
- package/src/lib/assistant-api-key-resolution.ts +5 -5
- package/src/lib/assistant-client.ts +8 -8
- package/src/lib/assistant-config.ts +58 -12
- package/src/lib/{platform-client.ts → cloud-client.ts} +105 -99
- package/src/lib/cloud-login.ts +23 -24
- package/src/lib/{platform-releases.ts → cloud-releases.ts} +11 -9
- package/src/lib/cloud-values.ts +3 -0
- package/src/lib/color.ts +4 -0
- package/src/lib/config-utils.ts +1 -1
- package/src/lib/detached-process.ts +1 -1
- package/src/lib/docker.ts +300 -111
- package/src/lib/ensure-binary.ts +171 -0
- package/src/lib/environments/__tests__/paths.test.ts +29 -3
- package/src/lib/environments/__tests__/resolve.test.ts +26 -26
- package/src/lib/environments/paths.ts +16 -8
- package/src/lib/environments/resolve.ts +10 -12
- package/src/lib/flag-args.test.ts +1 -1
- package/src/lib/flag-args.ts +1 -1
- package/src/lib/guardian-token.ts +37 -5
- package/src/lib/hatch-local.ts +32 -29
- package/src/lib/hatch-next-steps.ts +5 -9
- package/src/lib/health-check.ts +6 -6
- package/src/lib/ingress-config.ts +8 -35
- package/src/lib/job-polling.ts +2 -2
- package/src/lib/live-voice/connection.ts +12 -12
- package/src/lib/live-voice/tunnel.test.ts +11 -11
- package/src/lib/live-voice/tunnel.ts +15 -15
- package/src/lib/local-runtime-client.ts +20 -20
- package/src/lib/local.ts +46 -34
- package/src/lib/nginx-ingress.ts +40 -15
- package/src/lib/pair.ts +1 -1
- package/src/lib/process.ts +1 -1
- package/src/lib/provider-secrets.ts +3 -3
- package/src/lib/retire-apple-container.ts +1 -1
- package/src/lib/runtime-url.ts +8 -8
- package/src/lib/statefulset.ts +2 -2
- package/src/lib/step-runner.ts +6 -1
- package/src/lib/sync-cloud-assistants.ts +19 -19
- package/src/lib/teleport-backup.ts +4 -4
- package/src/lib/terminal-client.ts +17 -17
- package/src/lib/terminal-session.ts +24 -24
- package/src/lib/{tunnel-edge.ts → tunnel.ts} +199 -11
- package/src/lib/tunnels/cadet.ts +29 -0
- package/src/lib/{cloudflare-tunnel.ts → tunnels/cloudflare.ts} +36 -15
- package/src/lib/tunnels/gateway-notify.ts +61 -0
- package/src/lib/{ngrok.ts → tunnels/ngrok.ts} +350 -89
- package/src/lib/{tailscale-tunnel.ts → tunnels/tailscale.ts} +53 -9
- package/src/lib/upgrade-lifecycle.ts +8 -21
- package/src/lib/upgrade-preflight.ts +2 -2
- package/src/lib/workos-pkce.ts +5 -5
- package/node_modules/@mycadet/assistant-api/events/platform-disconnected.ts +0 -21
- package/node_modules/@mycadet/assistant-api/events/show-platform-login.ts +0 -20
- package/src/commands/recover.ts +0 -131
- package/src/commands/roadmap.ts +0 -459
- package/src/commands/ssh.ts +0 -144
- package/src/commands/workflows.ts +0 -305
- package/src/lib/managed-cloud.ts +0 -3
package/src/commands/tunnel.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isCloudHome } from "../lib/cloud-values.js";
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
|
|
@@ -14,54 +14,50 @@ import {
|
|
|
14
14
|
type AssistantEntry,
|
|
15
15
|
} from "../lib/assistant-config";
|
|
16
16
|
import { parseAssistantTargetArg } from "../lib/assistant-target-args.js";
|
|
17
|
-
import { runCloudflareTunnel } from "../lib/cloudflare-tunnel.js";
|
|
18
17
|
import { relaunchDetached } from "../lib/detached-process.js";
|
|
19
18
|
import {
|
|
20
19
|
getDefaultWorkspaceDir,
|
|
21
20
|
isLocalContainerEntry,
|
|
22
21
|
parseGatewayPortFromEntryUrls,
|
|
23
|
-
saveNgrokDomain,
|
|
24
22
|
} from "../lib/ingress-config.js";
|
|
25
23
|
import {
|
|
26
24
|
ensureTunnelEdge,
|
|
27
|
-
|
|
25
|
+
getNginxVersion,
|
|
28
26
|
type TunnelEdge,
|
|
29
27
|
} from "../lib/nginx-ingress.js";
|
|
30
|
-
import {
|
|
31
|
-
import { shellArg } from "../lib/shell-arg.js";
|
|
28
|
+
import { ensureBinaryInstalled } from "../lib/ensure-binary.js";
|
|
32
29
|
import { STALE_CLI_UPDATE_HINT } from "../lib/stale-cli-hint.js";
|
|
33
|
-
import {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
};
|
|
30
|
+
import {
|
|
31
|
+
ADOPTED_TUNNEL_RE,
|
|
32
|
+
DEFAULT_TUNNEL_PROVIDER,
|
|
33
|
+
PROVIDER_META,
|
|
34
|
+
adoptedTunnelStopNotice,
|
|
35
|
+
cadetTunnelComingSoonError,
|
|
36
|
+
clearReservedDomain,
|
|
37
|
+
hasWebhookIntegrationsConfigured,
|
|
38
|
+
providerSupportsReservedDomain,
|
|
39
|
+
runProviderTunnel,
|
|
40
|
+
} from "../lib/tunnel.js";
|
|
41
|
+
|
|
42
|
+
// The accepted `--provider` values ARE the shared registry, so validation
|
|
43
|
+
// here cannot drift from what the workspace config accepts.
|
|
48
44
|
|
|
49
45
|
interface TunnelArgs {
|
|
50
46
|
assistantName: string | null;
|
|
51
|
-
provider:
|
|
47
|
+
provider: TunnelProviderName;
|
|
52
48
|
domain: string | null;
|
|
53
49
|
clearDomain: boolean;
|
|
54
|
-
|
|
50
|
+
foreground: boolean;
|
|
55
51
|
}
|
|
56
52
|
|
|
57
53
|
const FLAGS_WITH_VALUES = ["--provider", "--domain"] as const;
|
|
58
54
|
|
|
59
55
|
function parseArgs(): TunnelArgs {
|
|
60
56
|
const args = process.argv.slice(3);
|
|
61
|
-
let provider:
|
|
57
|
+
let provider: TunnelProviderName | null = null;
|
|
62
58
|
let domain: string | null = null;
|
|
63
59
|
let clearDomain = false;
|
|
64
|
-
let
|
|
60
|
+
let foreground = false;
|
|
65
61
|
|
|
66
62
|
for (let i = 0; i < args.length; i++) {
|
|
67
63
|
const arg = args[i];
|
|
@@ -76,119 +72,115 @@ function parseArgs(): TunnelArgs {
|
|
|
76
72
|
);
|
|
77
73
|
console.log("");
|
|
78
74
|
console.log(
|
|
79
|
-
"How far that URL reaches depends on the provider
|
|
75
|
+
"How far that URL reaches depends on the provider; omit --provider for",
|
|
80
76
|
);
|
|
81
77
|
console.log(
|
|
82
|
-
"A tailscale tunnel is reachable only from your
|
|
78
|
+
"the default. A tailscale tunnel is reachable only from your",
|
|
83
79
|
);
|
|
84
80
|
console.log(
|
|
85
|
-
"integrations need a public tunnel
|
|
81
|
+
"own tailnet; webhook integrations need a public tunnel. See Providers",
|
|
86
82
|
);
|
|
83
|
+
console.log("below.");
|
|
87
84
|
console.log("");
|
|
88
85
|
console.log(
|
|
89
86
|
"The tunnel always fronts the local nginx edge, which is started automatically.",
|
|
90
87
|
);
|
|
91
88
|
console.log(
|
|
92
|
-
"nginx must be installed
|
|
89
|
+
"nginx must be installed; if it is missing, cadet offers to install it.",
|
|
93
90
|
);
|
|
94
91
|
console.log("");
|
|
95
92
|
console.log("Arguments:");
|
|
96
93
|
console.log(
|
|
97
|
-
" <name> Name of the assistant
|
|
94
|
+
" <name> Name of the assistant, defaults to active or only local",
|
|
98
95
|
);
|
|
99
96
|
console.log("");
|
|
100
97
|
console.log("Options:");
|
|
101
98
|
console.log(
|
|
102
|
-
` --provider <provider>
|
|
103
|
-
);
|
|
104
|
-
console.log(
|
|
105
|
-
" --domain <domain> Reserved ngrok domain to bind (ngrok provider only).",
|
|
106
|
-
);
|
|
107
|
-
console.log(
|
|
108
|
-
" Saved to the workspace config so `cadet wake` restores reuse it.",
|
|
109
|
-
);
|
|
110
|
-
console.log(
|
|
111
|
-
" --clear-domain Clear the saved ngrok domain (ngrok provider only) and tunnel",
|
|
112
|
-
);
|
|
113
|
-
console.log(
|
|
114
|
-
" without one. Cannot be combined with --domain.",
|
|
115
|
-
);
|
|
116
|
-
console.log(
|
|
117
|
-
` -d, --detach Run the tunnel in the background. Waits up to ${TUNNEL_BACKGROUND_START_TIMEOUT_MS / 1000}s for the`,
|
|
118
|
-
);
|
|
119
|
-
console.log(
|
|
120
|
-
" initial established/failed status before returning; logs go to",
|
|
121
|
-
);
|
|
122
|
-
console.log(
|
|
123
|
-
" the CLI log directory. Stop it with `kill <pid>`, except when it",
|
|
99
|
+
` --provider <provider> Tunnel provider: ${TUNNEL_PROVIDERS.join(", ")} — default ${DEFAULT_TUNNEL_PROVIDER}`,
|
|
124
100
|
);
|
|
125
101
|
console.log(
|
|
126
|
-
"
|
|
102
|
+
" --domain <domain> Reserved domain to bind, for providers that support",
|
|
127
103
|
);
|
|
128
104
|
console.log(
|
|
129
|
-
"
|
|
105
|
+
" one. Saved to the workspace config so `cadet wake` restores",
|
|
130
106
|
);
|
|
131
|
-
console.log("");
|
|
132
|
-
console.log("Providers:");
|
|
107
|
+
console.log(" reuse it.");
|
|
133
108
|
console.log(
|
|
134
|
-
"
|
|
109
|
+
" --clear-domain Clear the saved reserved domain (same providers) and",
|
|
135
110
|
);
|
|
136
111
|
console.log(
|
|
137
|
-
"
|
|
112
|
+
" tunnel without one. Cannot be combined with --domain.",
|
|
138
113
|
);
|
|
139
114
|
console.log(
|
|
140
|
-
|
|
115
|
+
` --foreground Stream the tunnel's logs to this terminal. By default the tunnel`,
|
|
141
116
|
);
|
|
142
117
|
console.log(
|
|
143
|
-
"
|
|
118
|
+
" runs in the background: the command returns after printing the public",
|
|
144
119
|
);
|
|
145
120
|
console.log(
|
|
146
|
-
|
|
121
|
+
` URL, and logs stream to tunnel.log in the CLI log directory. A background`,
|
|
147
122
|
);
|
|
148
123
|
console.log(
|
|
149
|
-
"
|
|
124
|
+
" tunnel stops with `kill <pid>`, except when it adopted an already-running",
|
|
150
125
|
);
|
|
151
126
|
console.log(
|
|
152
|
-
"
|
|
127
|
+
" tunnel (started outside this command): killing the supervisor then",
|
|
153
128
|
);
|
|
154
129
|
console.log(
|
|
155
|
-
"
|
|
130
|
+
" leaves that tunnel running.",
|
|
156
131
|
);
|
|
157
132
|
console.log("");
|
|
133
|
+
console.log("Providers:");
|
|
134
|
+
for (const name of TUNNEL_PROVIDERS) {
|
|
135
|
+
const meta = PROVIDER_META[name];
|
|
136
|
+
console.log(` ${name.padEnd(12)}${meta.reach}`);
|
|
137
|
+
console.log(` ${" ".repeat(12)}${meta.install}`);
|
|
138
|
+
for (const note of meta.notes ?? []) {
|
|
139
|
+
console.log(` ${" ".repeat(12)}${note}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
console.log("");
|
|
158
143
|
console.log("Examples:");
|
|
159
|
-
console.log(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
)
|
|
163
|
-
|
|
144
|
+
console.log(` $ cadet tunnel --provider ${DEFAULT_TUNNEL_PROVIDER}`);
|
|
145
|
+
const domainExample =
|
|
146
|
+
PROVIDER_META[DEFAULT_TUNNEL_PROVIDER].domainExample;
|
|
147
|
+
if (domainExample) {
|
|
148
|
+
console.log(
|
|
149
|
+
` $ cadet tunnel --provider ${DEFAULT_TUNNEL_PROVIDER} --domain ${domainExample}`,
|
|
150
|
+
);
|
|
151
|
+
console.log(
|
|
152
|
+
` $ cadet tunnel --provider ${DEFAULT_TUNNEL_PROVIDER} --clear-domain`,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
164
155
|
console.log(" $ cadet tunnel --provider cloudflare");
|
|
165
156
|
console.log(" $ cadet tunnel my-assistant --provider tailscale");
|
|
166
|
-
console.log(" $ cadet tunnel --provider ngrok --detach");
|
|
167
157
|
process.exit(0);
|
|
168
158
|
} else if (arg === "--provider") {
|
|
169
159
|
const next = args[i + 1];
|
|
170
160
|
if (!next || next.startsWith("-")) {
|
|
171
161
|
console.error(
|
|
172
|
-
`Error: --provider requires one of: ${
|
|
162
|
+
`Error: --provider requires one of: ${TUNNEL_PROVIDERS.join(", ")}`,
|
|
173
163
|
);
|
|
174
164
|
process.exit(1);
|
|
175
165
|
}
|
|
176
|
-
if (!
|
|
166
|
+
if (!TUNNEL_PROVIDERS.includes(next as TunnelProviderName)) {
|
|
177
167
|
console.error(
|
|
178
|
-
`Error: unknown tunnel provider '${next}'. Valid providers: ${
|
|
168
|
+
`Error: unknown tunnel provider '${next}'. Valid providers: ${TUNNEL_PROVIDERS.join(", ")}.`,
|
|
179
169
|
);
|
|
180
170
|
console.error(
|
|
181
171
|
`If this provider is documented, ${STALE_CLI_UPDATE_HINT}`,
|
|
182
172
|
);
|
|
183
173
|
process.exit(1);
|
|
184
174
|
}
|
|
185
|
-
provider = next as
|
|
175
|
+
provider = next as TunnelProviderName;
|
|
186
176
|
i++;
|
|
187
177
|
} else if (arg === "--domain") {
|
|
188
178
|
const next = args[i + 1];
|
|
189
179
|
if (!next || next.startsWith("-")) {
|
|
180
|
+
const domainExample =
|
|
181
|
+
PROVIDER_META[DEFAULT_TUNNEL_PROVIDER].domainExample;
|
|
190
182
|
console.error(
|
|
191
|
-
|
|
183
|
+
`Error: --domain requires a value${domainExample ? `, e.g. --domain ${domainExample}` : ""}`,
|
|
192
184
|
);
|
|
193
185
|
process.exit(1);
|
|
194
186
|
}
|
|
@@ -196,8 +188,8 @@ function parseArgs(): TunnelArgs {
|
|
|
196
188
|
i++;
|
|
197
189
|
} else if (arg === "--clear-domain") {
|
|
198
190
|
clearDomain = true;
|
|
199
|
-
} else if (arg === "
|
|
200
|
-
|
|
191
|
+
} else if (arg === "--foreground") {
|
|
192
|
+
foreground = true;
|
|
201
193
|
} else if (arg.startsWith("-")) {
|
|
202
194
|
console.error(`Error: Unknown option '${arg}'.`);
|
|
203
195
|
process.exit(1);
|
|
@@ -210,32 +202,20 @@ function parseArgs(): TunnelArgs {
|
|
|
210
202
|
parseAssistantTargetArg(args, FLAGS_WITH_VALUES) ?? null;
|
|
211
203
|
|
|
212
204
|
if (provider === null) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
);
|
|
216
|
-
console.error("");
|
|
217
|
-
console.error("Run one of:");
|
|
218
|
-
// Carries the assistant through: dropping it would suggest a command that
|
|
219
|
-
// tunnels the active assistant instead of the one that was asked for.
|
|
220
|
-
const target = assistantName ? ` ${shellArg(assistantName)}` : "";
|
|
221
|
-
for (const name of TUNNEL_PROVIDERS) {
|
|
222
|
-
console.error(
|
|
223
|
-
` cadet tunnel${target} --provider ${name.padEnd(13)}${PROVIDER_REACH[name]}`,
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
process.exit(1);
|
|
205
|
+
provider = DEFAULT_TUNNEL_PROVIDER;
|
|
206
|
+
console.log(`Using the default provider: ${provider}.`);
|
|
227
207
|
}
|
|
228
208
|
|
|
229
|
-
if (domain && provider
|
|
209
|
+
if (domain && !providerSupportsReservedDomain(provider)) {
|
|
230
210
|
console.error(
|
|
231
|
-
`Error: --domain is
|
|
211
|
+
`Error: --domain is not supported with --provider ${provider} (reserved domains have no equivalent there).`,
|
|
232
212
|
);
|
|
233
213
|
process.exit(1);
|
|
234
214
|
}
|
|
235
215
|
|
|
236
|
-
if (clearDomain && provider
|
|
216
|
+
if (clearDomain && !providerSupportsReservedDomain(provider)) {
|
|
237
217
|
console.error(
|
|
238
|
-
`Error: --clear-domain is
|
|
218
|
+
`Error: --clear-domain is not supported with --provider ${provider} (reserved domains have no equivalent there).`,
|
|
239
219
|
);
|
|
240
220
|
process.exit(1);
|
|
241
221
|
}
|
|
@@ -247,7 +227,7 @@ function parseArgs(): TunnelArgs {
|
|
|
247
227
|
process.exit(1);
|
|
248
228
|
}
|
|
249
229
|
|
|
250
|
-
return { assistantName, provider, domain, clearDomain,
|
|
230
|
+
return { assistantName, provider, domain, clearDomain, foreground };
|
|
251
231
|
}
|
|
252
232
|
|
|
253
233
|
/** A tunnelable assistant plus the gateway port and workspace the edge fronts. */
|
|
@@ -259,7 +239,7 @@ interface LocalTunnelTarget {
|
|
|
259
239
|
|
|
260
240
|
/**
|
|
261
241
|
* Map an entry to its local tunnel target, or null when it has no locally
|
|
262
|
-
* reachable gateway (e.g.
|
|
242
|
+
* reachable gateway (e.g. cloud-hosted). Entries with `resources` carry
|
|
263
243
|
* their own gateway port and instance workspace. Local container entries
|
|
264
244
|
* (docker, apple-container) run locally without host resources: their gateway
|
|
265
245
|
* port comes from localUrl/runtimeUrl and their ingress state lives in the
|
|
@@ -284,7 +264,7 @@ function toLocalTunnelTarget(entry: AssistantEntry): LocalTunnelTarget | null {
|
|
|
284
264
|
|
|
285
265
|
function describeUntunnelableEntry(entry: AssistantEntry): string {
|
|
286
266
|
const reference = formatAssistantReference(entry);
|
|
287
|
-
return
|
|
267
|
+
return isCloudHome(entry.cloud)
|
|
288
268
|
? `Assistant '${reference}' runs on Cadet Cloud and needs no tunnel.`
|
|
289
269
|
: `Assistant '${reference}' has no locally managed runtime to tunnel.`;
|
|
290
270
|
}
|
|
@@ -292,7 +272,7 @@ function describeUntunnelableEntry(entry: AssistantEntry): string {
|
|
|
292
272
|
/**
|
|
293
273
|
* Resolve the assistant whose gateway and workspace the tunnel edge fronts.
|
|
294
274
|
* Tunnels only make sense for assistants with a local gateway; when the
|
|
295
|
-
* resolved entry has none (e.g. the active assistant is
|
|
275
|
+
* resolved entry has none (e.g. the active assistant is cloud-hosted) and
|
|
296
276
|
* no name was given, fall back to the sole local target, otherwise exit with
|
|
297
277
|
* an error naming the local assistants to pass explicitly.
|
|
298
278
|
*/
|
|
@@ -350,8 +330,8 @@ function guardTailnetOnlyWebhookIngress(workspaceDir: string): boolean {
|
|
|
350
330
|
console.warn(
|
|
351
331
|
"⚠ Webhook integrations are configured, and the tailscale tunnel is reachable only from your own tailnet, " +
|
|
352
332
|
"so it cannot serve their callbacks. The saved ingress base URL stays as it is and the tailnet URL is " +
|
|
353
|
-
"recorded for device pairing only. For a tunnel those integrations can reach too,
|
|
354
|
-
"
|
|
333
|
+
"recorded for device pairing only. For a tunnel those integrations can reach too, run `cadet tunnel --help` " +
|
|
334
|
+
"to see the public providers.",
|
|
355
335
|
);
|
|
356
336
|
return true;
|
|
357
337
|
}
|
|
@@ -359,38 +339,37 @@ function guardTailnetOnlyWebhookIngress(workspaceDir: string): boolean {
|
|
|
359
339
|
// Generous cap so a slow provider handshake (e.g. cloudflared's own ~30s
|
|
360
340
|
// quick-tunnel timeout) still counts as a successful, if slow, start.
|
|
361
341
|
const TUNNEL_BACKGROUND_START_TIMEOUT_MS = 35_000;
|
|
362
|
-
// Matches the "just established" line every provider prints, or
|
|
363
|
-
//
|
|
364
|
-
// that case does not own the tunnel it adopted).
|
|
342
|
+
// Matches the "just established" line every provider prints, or the adoption
|
|
343
|
+
// line printed when a run adopts an already-running agent's tunnel (captured
|
|
344
|
+
// separately since that case does not own the tunnel it adopted).
|
|
365
345
|
const TUNNEL_ESTABLISHED_RE = /Tunnel established: (\S+)/;
|
|
366
|
-
const TUNNEL_ADOPTED_RE = /Found existing ngrok tunnel: (\S+)/;
|
|
367
346
|
|
|
368
347
|
/**
|
|
369
|
-
* Re-invoke `cadet tunnel
|
|
370
|
-
*
|
|
371
|
-
* "Tunnel established"
|
|
372
|
-
*
|
|
373
|
-
*
|
|
348
|
+
* Re-invoke `cadet tunnel --foreground` as a detached background process via
|
|
349
|
+
* `relaunchDetached`, waiting for the child to either print its
|
|
350
|
+
* "Tunnel established" or adopted-tunnel line (readiness) or exit early
|
|
351
|
+
* (failure), so a misconfigured provider still fails loudly in the terminal
|
|
352
|
+
* that ran `cadet tunnel`.
|
|
374
353
|
*
|
|
375
|
-
* The log
|
|
376
|
-
*
|
|
377
|
-
* truncate and read each other's readiness output.
|
|
354
|
+
* The log file is the fixed `tunnel.log` so there is one stable path to tail;
|
|
355
|
+
* concurrent tunnel runs share (and truncate) it.
|
|
378
356
|
*/
|
|
379
357
|
async function spawnDetachedTunnel(): Promise<void> {
|
|
380
358
|
const childArgs: string[] = ["tunnel"];
|
|
381
359
|
for (const arg of process.argv.slice(3)) {
|
|
382
|
-
if (arg === "
|
|
360
|
+
if (arg === "--foreground") {
|
|
383
361
|
continue;
|
|
384
362
|
}
|
|
385
363
|
childArgs.push(arg);
|
|
386
364
|
}
|
|
365
|
+
childArgs.push("--foreground");
|
|
387
366
|
|
|
388
367
|
let publicUrl: string | undefined;
|
|
389
368
|
let adopted = false;
|
|
390
369
|
|
|
391
370
|
const { child, logPath, ready, exitCode } = await relaunchDetached({
|
|
392
371
|
args: childArgs,
|
|
393
|
-
logFile:
|
|
372
|
+
logFile: "tunnel.log",
|
|
394
373
|
timeoutMs: TUNNEL_BACKGROUND_START_TIMEOUT_MS,
|
|
395
374
|
isReady: (logPath) => {
|
|
396
375
|
let logText = "";
|
|
@@ -404,7 +383,7 @@ async function spawnDetachedTunnel(): Promise<void> {
|
|
|
404
383
|
publicUrl = established[1];
|
|
405
384
|
return true;
|
|
406
385
|
}
|
|
407
|
-
const adoptedMatch =
|
|
386
|
+
const adoptedMatch = ADOPTED_TUNNEL_RE.exec(logText);
|
|
408
387
|
if (adoptedMatch) {
|
|
409
388
|
publicUrl = adoptedMatch[1];
|
|
410
389
|
adopted = true;
|
|
@@ -430,11 +409,7 @@ async function spawnDetachedTunnel(): Promise<void> {
|
|
|
430
409
|
}
|
|
431
410
|
console.log(`Running in background (pid ${child.pid}). Logs: ${logPath}`);
|
|
432
411
|
if (adopted) {
|
|
433
|
-
console.log(
|
|
434
|
-
`That ngrok agent was already running outside this command, so \`kill ${child.pid}\` only ` +
|
|
435
|
-
"stops this supervisor, not the ngrok tunnel or its saved ingress URL. Stop the ngrok agent " +
|
|
436
|
-
"directly to end the tunnel.",
|
|
437
|
-
);
|
|
412
|
+
console.log(adoptedTunnelStopNotice(child.pid));
|
|
438
413
|
} else {
|
|
439
414
|
console.log(`Stop with: kill ${child.pid}`);
|
|
440
415
|
}
|
|
@@ -442,72 +417,78 @@ async function spawnDetachedTunnel(): Promise<void> {
|
|
|
442
417
|
|
|
443
418
|
export const tunnel = Object.assign(
|
|
444
419
|
async function tunnel(): Promise<void> {
|
|
445
|
-
|
|
420
|
+
const { assistantName, provider, domain, clearDomain, foreground } =
|
|
421
|
+
parseArgs();
|
|
446
422
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
423
|
+
if (!foreground) {
|
|
424
|
+
await spawnDetachedTunnel();
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
451
427
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
`If this provider is documented, ${STALE_CLI_UPDATE_HINT}`,
|
|
456
|
-
);
|
|
457
|
-
}
|
|
428
|
+
if (PROVIDER_META[provider].comingSoon) {
|
|
429
|
+
throw cadetTunnelComingSoonError(provider);
|
|
430
|
+
}
|
|
458
431
|
|
|
459
|
-
|
|
460
|
-
|
|
432
|
+
const { entry, gatewayPort, workspaceDir } =
|
|
433
|
+
resolveLocalTunnelTarget(assistantName);
|
|
461
434
|
|
|
462
|
-
|
|
463
|
-
|
|
435
|
+
const preserveIngressUrl =
|
|
436
|
+
provider === "tailscale" && guardTailnetOnlyWebhookIngress(workspaceDir);
|
|
464
437
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
438
|
+
if (clearDomain) {
|
|
439
|
+
clearReservedDomain();
|
|
440
|
+
console.log("Cleared the saved reserved domain.");
|
|
441
|
+
}
|
|
469
442
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
443
|
+
const nginxVersion = await ensureBinaryInstalled({
|
|
444
|
+
binary: "nginx",
|
|
445
|
+
purpose: "the tunnel edge",
|
|
446
|
+
packages: {
|
|
447
|
+
brew: ["brew", "install", "nginx"],
|
|
448
|
+
"apt-get": ["sudo", "apt-get", "install", "-y", "nginx"],
|
|
449
|
+
dnf: ["sudo", "dnf", "install", "-y", "nginx"],
|
|
450
|
+
pacman: ["sudo", "pacman", "-S", "--noconfirm", "nginx"],
|
|
451
|
+
zypper: ["sudo", "zypper", "install", "-y", "nginx"],
|
|
452
|
+
choco: ["choco", "install", "-y", "nginx"],
|
|
453
|
+
},
|
|
454
|
+
manualHint:
|
|
455
|
+
"See https://nginx.org/en/docs/install.html for how to install, or point NGINX_BIN at an existing binary.",
|
|
456
|
+
probe: getNginxVersion,
|
|
476
457
|
});
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
458
|
+
if (!nginxVersion) {
|
|
459
|
+
console.error("Error: nginx is required to run the tunnel edge.");
|
|
460
|
+
process.exit(1);
|
|
461
|
+
}
|
|
481
462
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
463
|
+
let edge: TunnelEdge;
|
|
464
|
+
try {
|
|
465
|
+
edge = await ensureTunnelEdge({
|
|
466
|
+
assistantId: entry.assistantId,
|
|
467
|
+
workspaceDir,
|
|
468
|
+
gatewayPort,
|
|
469
|
+
});
|
|
470
|
+
} catch (err) {
|
|
471
|
+
console.error(
|
|
472
|
+
`Error: ${err instanceof Error ? err.message : String(err)}`,
|
|
473
|
+
);
|
|
474
|
+
process.exit(1);
|
|
475
|
+
}
|
|
486
476
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
workspaceDir,
|
|
491
|
-
};
|
|
477
|
+
console.log(
|
|
478
|
+
`${edge.started ? "Started" : "Reusing"} the nginx edge on 127.0.0.1:${edge.port}.`,
|
|
479
|
+
);
|
|
492
480
|
|
|
493
|
-
|
|
494
|
-
|
|
481
|
+
const baseTunnelOpts = {
|
|
482
|
+
port: edge.port,
|
|
483
|
+
assistantId: entry.assistantId,
|
|
484
|
+
workspaceDir,
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
await runProviderTunnel(provider, {
|
|
495
488
|
...baseTunnelOpts,
|
|
496
489
|
...(domain ? { domain } : {}),
|
|
490
|
+
...(preserveIngressUrl ? { preserveIngressUrl } : {}),
|
|
497
491
|
});
|
|
498
|
-
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
if (provider === "cloudflare") {
|
|
502
|
-
await runCloudflareTunnel(baseTunnelOpts);
|
|
503
|
-
return;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
await runTailscaleTunnel({
|
|
507
|
-
...baseTunnelOpts,
|
|
508
|
-
...(preserveIngressUrl ? { preserveIngressUrl } : {}),
|
|
509
|
-
});
|
|
510
|
-
},
|
|
492
|
+
},
|
|
511
493
|
{ description: "Create a tunnel for a locally hosted assistant" },
|
|
512
494
|
);
|
|
513
|
-
|