Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
],
"type": "module",
"scripts": {
"build": "tsc && cp -r ../policy-engine/profiles dist/profiles",
"build": "tsc && chmod +x dist/index.js && cp -r ../policy-engine/profiles dist/profiles",
"dev": "tsx watch src/index.ts",
"lint": "oxlint .",
"format": "oxfmt .",
Expand Down
29 changes: 20 additions & 9 deletions cli/src/commands/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Command } from "commander";
import chalk from "chalk";
import { existsSync } from "fs";
import { Stepper, banner, section, kvLine, checkLine } from "../stepper.js";
import { ensureCredentials, CREDENTIALS_FILE, resolveSecret, getSecret } from "../config.js";
import { loadConfig, promptAndSaveCredentials, CREDENTIALS_FILE, resolveSecret, getSecret } from "../config.js";

const DEFAULT_SANDBOX_IMAGE =
"azureclaw-sandbox:dev";
Expand All @@ -19,10 +19,12 @@ export function devCommand(): Command {

cmd
.description(
"Run a sandbox locally via Docker for development. Same policies, same model routing, on your laptop."
"Run a sandbox locally via Docker for development. Same policies, same model routing, on your laptop.\n" +
"Requires an existing Azure OpenAI resource with at least one model deployment (e.g. gpt-4.1).\n" +
"On first run, you will be prompted for your endpoint, model deployment name, and resource-level API key."
)
.option("--name <name>", "Sandbox name", "dev-agent")
.option("--model <model>", "AI model", "gpt-4.1")
.option("--model <model>", "Existing model deployment name in your Azure OpenAI resource", "gpt-4.1")
.option(
"--policy <preset>",
"Policy preset: minimal, developer, web, azure",
Expand Down Expand Up @@ -76,6 +78,21 @@ export function devCommand(): Command {
repoRoot = path.dirname(repoRoot);
}

// ── Credentials (first — prompt before potentially long build) ──
stepper.step("Checking credentials...");
let creds = loadConfig();
if (!creds) {
// Stop spinner so inquirer interactive prompts display correctly
stepper.stop();
console.log(chalk.yellow("\n No Azure OpenAI credentials found. Let's set them up."));
console.log(chalk.yellow(" You need an existing Azure OpenAI resource with a deployed model (e.g. gpt-4.1).\n"));
creds = await promptAndSaveCredentials();
stepper.done("Credentials configured");
} else {
stepper.done("Credentials loaded");
}
const model = options.model !== "gpt-4.1" ? options.model : creds.model;

// ── Image resolution ─────────────────────────────────────────
stepper.step("Resolving sandbox image...");
let imageExists = false;
Expand Down Expand Up @@ -189,12 +206,6 @@ export function devCommand(): Command {
stepper.done("Sandbox image found");
}

// ── Load or prompt for credentials ──────────────────────────
stepper.step("Loading credentials...");
const creds = await ensureCredentials();
stepper.done("Credentials ready");
const model = options.model !== "gpt-4.1" ? options.model : creds.model;

// ── Discover deployed models from Azure endpoint ─────────────
let discoveredDeployments = "";
// Discover deployed models via Azure CLI (ARM management API — only reliable way).
Expand Down
4 changes: 2 additions & 2 deletions cli/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,13 @@ export async function promptAndSaveCredentials(options?: {
{
type: "input",
name: "model",
message: "Model deployment name:",
message: "Model deployment name (must already exist in your Azure OpenAI resource):",
default: existing?.model || "gpt-4.1",
},
{
type: "password",
name: "apiKey",
message: "API key:",
message: "Azure OpenAI resource API key (resource-level key, not a per-model key):",
mask: "•",
validate: (input: string) => {
if (!input || input.length < 10) return "API key is required";
Expand Down
Loading