Skip to content

Commit 441e888

Browse files
authored
feat: add gatway support to rli (#101)
## Description <!-- Provide a brief description of your changes --> **Note:** PR titles should follow [Conventional Commits](https://www.conventionalcommits.org/) format (e.g., `feat(devbox): add support for custom env vars` or `fix(snapshot): resolve pagination issue`) as they are used for automatic release notes generation. ## Type of Change <!-- Mark the relevant option with an 'x' --> - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Documentation update - [ ] Code refactoring - [ ] Performance improvement - [ ] Test updates ## Related Issues <!-- Link to related issues using #issue-number --> Closes # ## Changes Made <!-- Describe the changes in detail --> ## Testing <!-- Describe how you tested your changes --> - [ ] I have tested locally - [ ] I have added/updated tests - [ ] All existing tests pass ## Checklist - [ ] My code follows the code style of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have updated the documentation accordingly - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published ## Screenshots (if applicable) <!-- Add screenshots to help explain your changes --> ## Additional Notes <!-- Any additional information that reviewers should know -->
1 parent 69f4032 commit 441e888

20 files changed

Lines changed: 2867 additions & 12 deletions

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,16 @@ rli secret update <name> # Update a secret value (value from std
152152
rli secret delete <name> # Delete a secret
153153
```
154154

155+
### Gateway-config Commands (alias: `gwc`)
156+
157+
```bash
158+
rli gateway-config list # List gateway configurations
159+
rli gateway-config create # Create a new gateway configuration
160+
rli gateway-config get <id> # Get gateway configuration details
161+
rli gateway-config update <id> # Update a gateway configuration
162+
rli gateway-config delete <id> # Delete a gateway configuration
163+
```
164+
155165
### Mcp Commands
156166

157167
```bash

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
},
6969
"dependencies": {
7070
"@modelcontextprotocol/sdk": "^1.19.1",
71-
"@runloop/api-client": "1.3.1",
71+
"@runloop/api-client": "1.6.0",
7272
"@types/express": "^5.0.3",
7373
"chalk": "^5.3.0",
7474
"commander": "^14.0.1",

pnpm-lock.yaml

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/commands/devbox/create.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ interface CreateOptions {
2323
root?: boolean;
2424
user?: string;
2525
networkPolicy?: string;
26+
gateways?: string[];
2627
output?: string;
2728
}
2829

@@ -71,6 +72,45 @@ function parseCodeMounts(codeMounts: string[]): unknown[] {
7172
});
7273
}
7374

75+
// Parse gateways from ENV_PREFIX=gateway,secret format
76+
function parseGateways(
77+
gateways: string[],
78+
): Record<string, { gateway: string; secret: string }> {
79+
const result: Record<string, { gateway: string; secret: string }> = {};
80+
for (const gateway of gateways) {
81+
const eqIndex = gateway.indexOf("=");
82+
if (eqIndex === -1) {
83+
throw new Error(
84+
`Invalid gateway format: ${gateway}. Expected ENV_PREFIX=gateway_id_or_name,secret_id_or_name`,
85+
);
86+
}
87+
const envPrefix = gateway.substring(0, eqIndex);
88+
const valueStr = gateway.substring(eqIndex + 1);
89+
90+
// Split by comma to get gateway and secret
91+
const commaIndex = valueStr.indexOf(",");
92+
if (commaIndex === -1) {
93+
throw new Error(
94+
`Invalid gateway format: ${gateway}. Expected ENV_PREFIX=gateway_id_or_name,secret_id_or_name`,
95+
);
96+
}
97+
const gatewayIdOrName = valueStr.substring(0, commaIndex);
98+
const secretIdOrName = valueStr.substring(commaIndex + 1);
99+
100+
if (!envPrefix || !gatewayIdOrName || !secretIdOrName) {
101+
throw new Error(
102+
`Invalid gateway format: ${gateway}. Expected ENV_PREFIX=gateway_id_or_name,secret_id_or_name`,
103+
);
104+
}
105+
106+
result[envPrefix] = {
107+
gateway: gatewayIdOrName,
108+
secret: secretIdOrName,
109+
};
110+
}
111+
return result;
112+
}
113+
74114
export async function createDevbox(options: CreateOptions = {}) {
75115
try {
76116
const client = getClient();
@@ -173,6 +213,11 @@ export async function createDevbox(options: CreateOptions = {}) {
173213
createRequest.secrets = parseSecrets(options.secrets);
174214
}
175215

216+
// Handle gateways
217+
if (options.gateways && options.gateways.length > 0) {
218+
createRequest.gateways = parseGateways(options.gateways);
219+
}
220+
176221
if (Object.keys(launchParameters).length > 0) {
177222
createRequest.launch_parameters = launchParameters;
178223
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/**
2+
* Create gateway config command
3+
*/
4+
5+
import { getClient } from "../../utils/client.js";
6+
import { output, outputError } from "../../utils/output.js";
7+
8+
interface CreateOptions {
9+
name: string;
10+
endpoint: string;
11+
authType: string;
12+
authKey?: string;
13+
description?: string;
14+
output?: string;
15+
}
16+
17+
export async function createGatewayConfig(options: CreateOptions) {
18+
try {
19+
const client = getClient();
20+
21+
// Validate auth type
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+
28+
// Validate auth key is provided for header type
29+
if (authType === "header" && !options.authKey) {
30+
outputError("--auth-key is required when auth-type is 'header'");
31+
return;
32+
}
33+
34+
// Build auth mechanism
35+
const authMechanism: { type: string; key?: string } = {
36+
type: authType,
37+
};
38+
if (authType === "header" && options.authKey) {
39+
authMechanism.key = options.authKey;
40+
}
41+
42+
const config = await client.gatewayConfigs.create({
43+
name: options.name,
44+
endpoint: options.endpoint,
45+
auth_mechanism: authMechanism,
46+
description: options.description,
47+
});
48+
49+
// Default: just output the ID for easy scripting
50+
if (!options.output || options.output === "text") {
51+
console.log(config.id);
52+
} else {
53+
output(config, { format: options.output, defaultFormat: "json" });
54+
}
55+
} catch (error) {
56+
outputError("Failed to create gateway config", error);
57+
}
58+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* Delete gateway config command
3+
*/
4+
5+
import { getClient } from "../../utils/client.js";
6+
import { output, outputError } from "../../utils/output.js";
7+
8+
interface DeleteOptions {
9+
output?: string;
10+
}
11+
12+
export async function deleteGatewayConfig(
13+
id: string,
14+
options: DeleteOptions = {},
15+
) {
16+
try {
17+
const client = getClient();
18+
19+
await client.gatewayConfigs.delete(id);
20+
21+
// Default: just output the ID for easy scripting
22+
if (!options.output || options.output === "text") {
23+
console.log(id);
24+
} else {
25+
output(
26+
{ id, status: "deleted" },
27+
{ format: options.output, defaultFormat: "json" },
28+
);
29+
}
30+
} catch (error) {
31+
outputError("Failed to delete gateway config", error);
32+
}
33+
}

src/commands/gateway-config/get.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* Get gateway config command
3+
*/
4+
5+
import { getClient } from "../../utils/client.js";
6+
import { output, outputError } from "../../utils/output.js";
7+
8+
interface GetOptions {
9+
id: string;
10+
output?: string;
11+
}
12+
13+
export async function getGatewayConfig(options: GetOptions) {
14+
try {
15+
const client = getClient();
16+
17+
const config = await client.gatewayConfigs.retrieve(options.id);
18+
19+
output(config, { format: options.output, defaultFormat: "json" });
20+
} catch (error) {
21+
outputError("Failed to get gateway config", error);
22+
}
23+
}

0 commit comments

Comments
 (0)