Skip to content

Commit e1455c6

Browse files
authored
feat: ✨ CLI: Add aws credentials onboarding (#7443)
1 parent 2a4fbac commit e1455c6

File tree

4 files changed

+157
-8
lines changed

4 files changed

+157
-8
lines changed

.vscode/launch.json

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,37 @@
159159
"!**/node_modules/**"
160160
],
161161
"preLaunchTask": "cli:build-and-watch-logs",
162-
"cwd": "${workspaceFolder}/extensions/cli"
162+
"cwd": "${workspaceFolder}/manual-testing-sandbox"
163+
},
164+
{
165+
"name": "Debug CLI - AWS Bedrock",
166+
"type": "node",
167+
"request": "launch",
168+
"program": "${workspaceFolder}/extensions/cli/dist/cn.js",
169+
"args": [],
170+
"runtimeArgs": ["--enable-source-maps"],
171+
"env": {
172+
"NODE_ENV": "development",
173+
"CONTINUE_USE_BEDROCK": "1"
174+
// "CONTINUE_API_BASE": "http://localhost:3001/",
175+
// "WORKOS_CLIENT_ID": "client_01J0FW6XCPMJMQ3CG51RB4HBZQ",
176+
// "HUB_URL": "http://localhost:3000"
177+
},
178+
"console": "integratedTerminal",
179+
"internalConsoleOptions": "neverOpen",
180+
"skipFiles": ["<node_internals>/**"],
181+
"sourceMaps": true,
182+
"outFiles": ["${workspaceFolder}/extensions/cli/dist/**/*.js"],
183+
"sourceMapPathOverrides": {
184+
"../src/*": "${workspaceFolder}/extensions/cli/src/*",
185+
"../../core/*": "${workspaceFolder}/core/*"
186+
},
187+
"resolveSourceMapLocations": [
188+
"${workspaceFolder}/**",
189+
"!**/node_modules/**"
190+
],
191+
"preLaunchTask": "cli:build-and-watch-logs",
192+
"cwd": "${workspaceFolder}/manual-testing-sandbox"
163193
},
164194
{
165195
"name": "Debug CLI Tests",

extensions/cli/spec/onboarding.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ When a user first runs `cn` in interactive mode, they will be taken through "onb
77
**The onboarding flow runs when the user hasn't completed onboarding before, regardless of whether they have a valid config.yaml file.**
88

99
1. If the --config flag is provided, load this config
10-
2. Otherwise, we will present the user with two options:
10+
2. If the CONTINUE_USE_BEDROCK environment variable is set to "1", automatically use AWS Bedrock configuration and skip interactive prompts
11+
3. Present the user with available options:
1112

1213
- Log in with Continue: log them in, which will automatically create their assistant and then we can load the first assistant from the first org
1314
- Enter your Anthropic API key: let them enter the key, and then either create a ~/.continue/config.yaml with the following contents OR update the existing config.yaml to add the model
@@ -23,7 +24,26 @@ When a user first runs `cn` in interactive mode, they will be taken through "onb
2324
ANTHROPIC_API_KEY: <THEIR_ANTHROPIC_API_KEY>
2425
```
2526
26-
When something in the onboarding flow is done automatically, we should tell the user what happened.
27+
When CONTINUE_USE_BEDROCK=1 is detected, it will use AWS Bedrock configuration. The user must have AWS credentials configured through the standard AWS credential chain (AWS CLI, environment variables, IAM roles, etc.).
28+
29+
When something in the onboarding flow is done automatically, we should tell the user what happened. For example, when CONTINUE_USE_BEDROCK=1 is detected, the CLI displays: "✓ Using AWS Bedrock (CONTINUE_USE_BEDROCK detected)"
30+
31+
### AWS Bedrock Environment Variable
32+
33+
Users can bypass the interactive onboarding menu by setting the `CONTINUE_USE_BEDROCK` environment variable to "1":
34+
35+
```bash
36+
export CONTINUE_USE_BEDROCK=1
37+
cn <command>
38+
```
39+
40+
This will:
41+
42+
- Skip the interactive onboarding prompts
43+
- Automatically configure the CLI to use AWS Bedrock
44+
- Require that AWS credentials are already configured through the standard AWS credential chain
45+
- Display a confirmation message to the user
46+
- Mark onboarding as completed
2747

2848
## Normal flow
2949

extensions/cli/src/onboarding.test.ts

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as fs from "fs";
22
import * as os from "os";
33
import * as path from "path";
44

5-
import { describe, expect, test, beforeEach, afterEach } from "vitest";
5+
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
66

77
import type { AuthConfig } from "./auth/workos.js";
88
import { runNormalFlow } from "./onboarding.js";
@@ -160,3 +160,93 @@ name: "Incomplete Config"
160160
}
161161
});
162162
});
163+
164+
// Separate describe block with its own mocking for BEDROCK tests
165+
describe("CONTINUE_USE_BEDROCK environment variable", () => {
166+
const mockConsoleLog = vi.fn();
167+
let mockAuthConfig: AuthConfig;
168+
const originalEnv = process.env.CONTINUE_USE_BEDROCK;
169+
170+
// Mock initialize for these tests only
171+
const mockInitialize = vi.fn().mockResolvedValue({
172+
config: { name: "test-config", models: [], rules: [] },
173+
llmApi: {},
174+
model: { name: "test-model" },
175+
mcpService: {},
176+
apiClient: {},
177+
});
178+
179+
beforeEach(() => {
180+
mockConsoleLog.mockClear();
181+
mockInitialize.mockClear();
182+
183+
// Spy on console.log for these tests
184+
vi.spyOn(console, "log").mockImplementation(mockConsoleLog);
185+
186+
// Mock the config module
187+
vi.doMock("./config.js", () => ({ initialize: mockInitialize }));
188+
189+
mockAuthConfig = {
190+
userId: "test-user",
191+
userEmail: "[email protected]",
192+
accessToken: "test-token",
193+
refreshToken: "test-refresh",
194+
expiresAt: Date.now() + 3600000,
195+
organizationId: "test-org",
196+
};
197+
});
198+
199+
afterEach(() => {
200+
if (originalEnv) {
201+
process.env.CONTINUE_USE_BEDROCK = originalEnv;
202+
} else {
203+
delete process.env.CONTINUE_USE_BEDROCK;
204+
}
205+
vi.restoreAllMocks();
206+
vi.doUnmock("./config.js");
207+
});
208+
209+
test("should bypass interactive options when CONTINUE_USE_BEDROCK=1", async () => {
210+
process.env.CONTINUE_USE_BEDROCK = "1";
211+
212+
// Re-import to get the mocked version
213+
vi.resetModules();
214+
const { runOnboardingFlow } = await import("./onboarding.js");
215+
216+
const result = await runOnboardingFlow(undefined, mockAuthConfig);
217+
218+
expect(result.wasOnboarded).toBe(true);
219+
expect(mockConsoleLog).toHaveBeenCalledWith(
220+
expect.stringContaining(
221+
"✓ Using AWS Bedrock (CONTINUE_USE_BEDROCK detected)",
222+
),
223+
);
224+
});
225+
226+
test("should not bypass when CONTINUE_USE_BEDROCK is not '1'", async () => {
227+
process.env.CONTINUE_USE_BEDROCK = "0";
228+
229+
// Re-import to get the mocked version
230+
vi.resetModules();
231+
const { runOnboardingFlow } = await import("./onboarding.js");
232+
233+
// Mock non-interactive environment to avoid hanging
234+
const originalIsTTY = process.stdin.isTTY;
235+
process.stdin.isTTY = false;
236+
237+
try {
238+
await runOnboardingFlow(undefined, mockAuthConfig);
239+
240+
// Verify the Bedrock message was NOT called by checking all calls
241+
const allCalls = mockConsoleLog.mock.calls.flat();
242+
const hasBedrockMessage = allCalls.some((call) =>
243+
String(call).includes(
244+
"✓ Using AWS Bedrock (CONTINUE_USE_BEDROCK detected)",
245+
),
246+
);
247+
expect(hasBedrockMessage).toBe(false);
248+
} finally {
249+
process.stdin.isTTY = originalIsTTY;
250+
}
251+
});
252+
});

extensions/cli/src/onboarding.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export async function createOrUpdateConfig(apiKey: string): Promise<void> {
5959
fs.writeFileSync(CONFIG_PATH, updatedContent);
6060
}
6161

62-
async function runOnboardingFlow(
62+
export async function runOnboardingFlow(
6363
configPath: string | undefined,
6464
authConfig: AuthConfig,
6565
): Promise<OnboardingResult> {
@@ -69,7 +69,16 @@ async function runOnboardingFlow(
6969
return { ...result, wasOnboarded: false };
7070
}
7171

72-
// Step 2: Check if we're in a test/CI environment - if so, skip interactive prompts
72+
// Step 2: Check for CONTINUE_USE_BEDROCK environment variable first (before test env check)
73+
if (process.env.CONTINUE_USE_BEDROCK === "1") {
74+
console.log(
75+
chalk.blue("✓ Using AWS Bedrock (CONTINUE_USE_BEDROCK detected)"),
76+
);
77+
const result = await initialize(authConfig, CONFIG_PATH);
78+
return { ...result, wasOnboarded: true };
79+
}
80+
81+
// Step 3: Check if we're in a test/CI environment - if so, skip interactive prompts
7382
const isTestEnv =
7483
process.env.NODE_ENV === "test" ||
7584
process.env.CI === "true" ||
@@ -92,7 +101,7 @@ async function runOnboardingFlow(
92101
return { ...result, wasOnboarded: false };
93102
}
94103

95-
// Step 3: Present user with two options
104+
// Step 4: Present user with two options
96105
console.log(chalk.yellow("How do you want to get started?"));
97106
console.log(chalk.white("1. ⏩ Log in with Continue"));
98107
console.log(chalk.white("2. 🔑 Enter your Anthropic API key"));
@@ -129,7 +138,7 @@ async function runOnboardingFlow(
129138
const result = await initialize(authConfig, CONFIG_PATH);
130139
return { ...result, wasOnboarded: true };
131140
} else {
132-
throw new Error("Invalid choice. Please select 1 or 2.");
141+
throw new Error(`Invalid choice. Please select "1" or "2"`);
133142
}
134143
}
135144

0 commit comments

Comments
 (0)