@runloop/rl-cli 1.9.0 → 1.10.0

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.
@@ -3,46 +3,56 @@
3
3
  */
4
4
  import { getClient } from "../../utils/client.js";
5
5
  import { output, outputError } from "../../utils/output.js";
6
+ import { validateGatewayConfig } from "../../utils/gatewayConfigValidation.js";
6
7
  export async function updateGatewayConfig(options) {
7
8
  try {
8
9
  const client = getClient();
10
+ // Validate that at most one auth type is specified
11
+ if (options.bearerAuth && options.headerAuth) {
12
+ outputError("Cannot specify both --bearer-auth and --header-auth. Choose one.");
13
+ return;
14
+ }
15
+ // Determine auth type if specified
16
+ const authType = options.bearerAuth
17
+ ? "bearer"
18
+ : options.headerAuth
19
+ ? "header"
20
+ : undefined;
21
+ // Validate provided fields using shared validation
22
+ const validation = validateGatewayConfig({
23
+ name: options.name,
24
+ endpoint: options.endpoint,
25
+ authType,
26
+ authKey: options.headerAuth,
27
+ }, { requireName: false, requireEndpoint: false });
28
+ if (!validation.valid) {
29
+ outputError(validation.errors.join("\n"));
30
+ return;
31
+ }
32
+ const { sanitized } = validation;
9
33
  // Build update params - only include fields that are provided
10
34
  const updateParams = {};
11
- if (options.name) {
12
- updateParams.name = options.name;
35
+ if (sanitized.name) {
36
+ updateParams.name = sanitized.name;
13
37
  }
14
- if (options.endpoint) {
15
- updateParams.endpoint = options.endpoint;
38
+ if (sanitized.endpoint) {
39
+ updateParams.endpoint = sanitized.endpoint;
16
40
  }
17
41
  if (options.description !== undefined) {
18
- updateParams.description = options.description;
42
+ updateParams.description = options.description.trim() || undefined;
19
43
  }
20
44
  // Handle auth mechanism update
21
- if (options.authType) {
22
- const authType = options.authType.toLowerCase();
23
- if (authType !== "bearer" && authType !== "header") {
24
- outputError("Invalid auth type. Must be 'bearer' or 'header'");
25
- return;
26
- }
27
- const authMechanism = {
28
- type: authType,
45
+ if (sanitized.authType === "bearer") {
46
+ updateParams.auth_mechanism = { type: "bearer" };
47
+ }
48
+ else if (sanitized.authType === "header" && sanitized.authKey) {
49
+ updateParams.auth_mechanism = {
50
+ type: "header",
51
+ key: sanitized.authKey,
29
52
  };
30
- if (authType === "header") {
31
- if (!options.authKey) {
32
- outputError("--auth-key is required when auth-type is 'header'");
33
- return;
34
- }
35
- authMechanism.key = options.authKey;
36
- }
37
- updateParams.auth_mechanism = authMechanism;
38
- }
39
- else if (options.authKey) {
40
- // If only auth key is provided without auth type, we need the type
41
- outputError("--auth-type is required when updating --auth-key");
42
- return;
43
53
  }
44
54
  if (Object.keys(updateParams).length === 0) {
45
- outputError("No update options provided. Use --name, --endpoint, --auth-type, --auth-key, or --description");
55
+ outputError("No update options provided. Use --name, --endpoint, --bearer-auth, --header-auth, or --description");
46
56
  return;
47
57
  }
48
58
  const config = await client.gatewayConfigs.update(options.id, updateParams);
@@ -139,8 +139,10 @@ export const DevboxActionsMenu = ({ devbox, onBack, breadcrumbItems = [
139
139
  },
140
140
  ];
141
141
  // Filter operations based on devbox status
142
+ const hasTunnel = !!(devbox?.tunnel && devbox.tunnel.tunnel_key);
142
143
  const operations = devbox
143
- ? allOperations.filter((op) => {
144
+ ? allOperations
145
+ .filter((op) => {
144
146
  const status = devbox.status;
145
147
  // When suspended: logs and resume
146
148
  if (status === "suspended") {
@@ -158,6 +160,20 @@ export const DevboxActionsMenu = ({ devbox, onBack, breadcrumbItems = [
158
160
  }
159
161
  // Default for transitional states (provisioning, initializing)
160
162
  return op.key === "logs" || op.key === "delete";
163
+ })
164
+ .map((op) => {
165
+ // Dynamic tunnel label based on whether tunnel is active
166
+ if (op.key === "tunnel") {
167
+ return hasTunnel
168
+ ? {
169
+ ...op,
170
+ label: "Tunnel (Active)",
171
+ color: colors.success,
172
+ icon: figures.tick,
173
+ }
174
+ : op;
175
+ }
176
+ return op;
161
177
  })
162
178
  : allOperations;
163
179
  // Auto-execute operations that don't need input (except delete which needs confirmation)