diff --git a/cli/package.json b/cli/package.json index c8e4eba1a..254160908 100644 --- a/cli/package.json +++ b/cli/package.json @@ -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 .", diff --git a/cli/src/commands/dev.ts b/cli/src/commands/dev.ts index 275acf7f7..50e99460d 100644 --- a/cli/src/commands/dev.ts +++ b/cli/src/commands/dev.ts @@ -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"; @@ -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 ", "Sandbox name", "dev-agent") - .option("--model ", "AI model", "gpt-4.1") + .option("--model ", "Existing model deployment name in your Azure OpenAI resource", "gpt-4.1") .option( "--policy ", "Policy preset: minimal, developer, web, azure", @@ -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; @@ -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). diff --git a/cli/src/config.ts b/cli/src/config.ts index 42a59bbd8..5cb7f2992 100644 --- a/cli/src/config.ts +++ b/cli/src/config.ts @@ -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";