@ganakailabs/cloudeval-cli 0.24.4 → 0.26.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.
package/dist/cli.js CHANGED
@@ -36,7 +36,7 @@ import {
36
36
  } from "./chunk-KBHRBGSX.js";
37
37
  import {
38
38
  CLI_VERSION
39
- } from "./chunk-YEKKVQCN.js";
39
+ } from "./chunk-IAXSLM2G.js";
40
40
 
41
41
  // src/runtime/prepareInk.ts
42
42
  import fs from "fs";
@@ -135,9 +135,9 @@ ensureInkRuntimeEnvironment();
135
135
  // src/cli.tsx
136
136
  import React from "react";
137
137
  import { Command } from "commander";
138
- import { promises as fs12 } from "fs";
138
+ import { promises as fs13 } from "fs";
139
139
  import os6 from "os";
140
- import path9 from "path";
140
+ import path10 from "path";
141
141
 
142
142
  // src/shellCompletion.ts
143
143
  var normalizeCompletionShell = (shell) => {
@@ -437,6 +437,35 @@ var cliCommands = [
437
437
  "reports rules"
438
438
  ]
439
439
  },
440
+ {
441
+ name: "review",
442
+ description: "Sync a GitHub-backed CloudEval project from the pushed commit and evaluate report gates",
443
+ domain: "reports",
444
+ options: [
445
+ ...authOptions,
446
+ "--project",
447
+ "--repo",
448
+ "--ref",
449
+ "--commit-sha",
450
+ "--source-root",
451
+ "--config",
452
+ "--no-wait",
453
+ "--wait-timeout",
454
+ "--poll-interval",
455
+ "--no-ai-summary",
456
+ "--ai-summary-mode",
457
+ "--ai-summary-profile",
458
+ "--ignore-dirty",
459
+ "--output",
460
+ "--format",
461
+ "--quiet",
462
+ "--progress",
463
+ "--model",
464
+ "--profile",
465
+ "--help"
466
+ ],
467
+ workflows: ["github review", "pr gate", "shift-left review"]
468
+ },
440
469
  {
441
470
  name: "recipes",
442
471
  description: "CloudEval reusable recipes and agent skills",
@@ -1436,10 +1465,10 @@ var writeFormattedOutput = async (input) => {
1436
1465
  process.stdout.write(text);
1437
1466
  };
1438
1467
  var writePrivateOutputFile = async (output, text) => {
1439
- const fs13 = await import("fs/promises");
1440
- await fs13.writeFile(output, text, { encoding: "utf8", mode: 384 });
1468
+ const fs14 = await import("fs/promises");
1469
+ await fs14.writeFile(output, text, { encoding: "utf8", mode: 384 });
1441
1470
  if (process.platform !== "win32") {
1442
- await fs13.chmod(output, 384);
1471
+ await fs14.chmod(output, 384);
1443
1472
  }
1444
1473
  };
1445
1474
 
@@ -1787,8 +1816,8 @@ var writeReport = async (report, options, tuiDefault) => {
1787
1816
  const textFormat = format === "text" || format === "table" ? "summary" : format;
1788
1817
  const text = serializeReportOutput(report, { format: textFormat, mode });
1789
1818
  if (options.output) {
1790
- const fs13 = await import("fs/promises");
1791
- await fs13.writeFile(options.output, text, "utf8");
1819
+ const fs14 = await import("fs/promises");
1820
+ await fs14.writeFile(options.output, text, "utf8");
1792
1821
  return;
1793
1822
  }
1794
1823
  process.stdout.write(text);
@@ -1823,16 +1852,16 @@ var writeDownloadPayload = async (input) => {
1823
1852
  );
1824
1853
  return [];
1825
1854
  }
1826
- const fs13 = await import("fs/promises");
1827
- const path10 = await import("path");
1828
- await fs13.mkdir(path10.dirname(input.output), { recursive: true });
1855
+ const fs14 = await import("fs/promises");
1856
+ const path11 = await import("path");
1857
+ await fs14.mkdir(path11.dirname(input.output), { recursive: true });
1829
1858
  const text = formatOutput({
1830
1859
  command: input.command,
1831
1860
  data: input.payload,
1832
1861
  format: input.format,
1833
1862
  frontendUrl: input.frontendUrl
1834
1863
  });
1835
- await fs13.writeFile(input.output, text, "utf8");
1864
+ await fs14.writeFile(input.output, text, "utf8");
1836
1865
  return [input.output];
1837
1866
  };
1838
1867
  var resolveMachineFormat = (requested) => {
@@ -1981,14 +2010,14 @@ var registerReportsCommand = (program2, deps) => {
1981
2010
  );
1982
2011
  const data = reportTypes.length === 1 ? payload[reportTypes[0] === "architecture" ? "waf" : reportTypes[0]] : payload;
1983
2012
  if (options.output && reportTypes.length > 1) {
1984
- const fs13 = await import("fs/promises");
1985
- const path10 = await import("path");
1986
- const stat = await fs13.stat(options.output).catch(() => void 0);
1987
- if (stat?.isDirectory() || !path10.extname(options.output)) {
1988
- await fs13.mkdir(options.output, { recursive: true });
2013
+ const fs14 = await import("fs/promises");
2014
+ const path11 = await import("path");
2015
+ const stat = await fs14.stat(options.output).catch(() => void 0);
2016
+ if (stat?.isDirectory() || !path11.extname(options.output)) {
2017
+ await fs14.mkdir(options.output, { recursive: true });
1989
2018
  const files = [];
1990
2019
  for (const [key, value] of Object.entries(payload)) {
1991
- const file = path10.join(options.output, `${projectId}-${key}-report.json`);
2020
+ const file = path11.join(options.output, `${projectId}-${key}-report.json`);
1992
2021
  files.push(
1993
2022
  ...await writeDownloadPayload({
1994
2023
  command: "reports download",
@@ -2217,9 +2246,762 @@ var registerReportsCommand = (program2, deps) => {
2217
2246
  );
2218
2247
  };
2219
2248
 
2249
+ // src/reviewCommand.ts
2250
+ import fs2 from "fs/promises";
2251
+ import path2 from "path";
2252
+ import { spawn } from "child_process";
2253
+
2254
+ // src/apiClient.ts
2255
+ var responseErrorMessage = async (response) => {
2256
+ const text = await response.text();
2257
+ if (!text) {
2258
+ return `request failed with status ${response.status}`;
2259
+ }
2260
+ try {
2261
+ const payload = JSON.parse(text);
2262
+ const detail = payload?.detail ?? payload?.message ?? payload?.error;
2263
+ if (typeof detail === "string") {
2264
+ return detail;
2265
+ }
2266
+ if (detail) {
2267
+ return JSON.stringify(detail);
2268
+ }
2269
+ } catch {
2270
+ }
2271
+ return text;
2272
+ };
2273
+ var fetchCloudEvalJson = async ({
2274
+ baseUrl,
2275
+ authToken,
2276
+ path: path11,
2277
+ method = "GET",
2278
+ query = {},
2279
+ body,
2280
+ idempotencyKey
2281
+ }) => {
2282
+ const url = new URL(`${normalizeApiBase(baseUrl)}${path11}`);
2283
+ for (const [key, value] of Object.entries(query)) {
2284
+ if (value !== void 0 && value !== null && value !== "") {
2285
+ url.searchParams.set(key, String(value));
2286
+ }
2287
+ }
2288
+ const response = await fetch(url, {
2289
+ method,
2290
+ headers: {
2291
+ ...getCLIHeaders(authToken),
2292
+ ...idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {}
2293
+ },
2294
+ ...body === void 0 ? {} : { body: JSON.stringify(body) }
2295
+ });
2296
+ if (!response.ok) {
2297
+ throw new Error(await responseErrorMessage(response));
2298
+ }
2299
+ return await response.json();
2300
+ };
2301
+
2302
+ // src/reviewCommand.ts
2303
+ var DIRTY_REVIEW_MESSAGE = "Reviews pushed commits only. Add --ignore-dirty to review HEAD anyway.";
2304
+ var runGit = async (cwd, args) => {
2305
+ const child = spawn("git", args, {
2306
+ cwd,
2307
+ stdio: ["ignore", "pipe", "pipe"]
2308
+ });
2309
+ const stdout = [];
2310
+ child.stdout.on("data", (chunk) => stdout.push(Buffer.from(chunk)));
2311
+ const exitCode = await new Promise(
2312
+ (resolve) => child.on("exit", resolve)
2313
+ );
2314
+ return {
2315
+ ok: exitCode === 0,
2316
+ stdout: Buffer.concat(stdout).toString("utf8").trim()
2317
+ };
2318
+ };
2319
+ var normalizeGithubRepo = (value) => {
2320
+ const text = String(value ?? "").trim();
2321
+ if (!text) return void 0;
2322
+ const withoutGit = text.replace(/\.git$/i, "");
2323
+ const httpsMatch = withoutGit.match(/github\.com[:/]([^/\s]+)\/([^/\s]+)$/i);
2324
+ if (httpsMatch) {
2325
+ return `${httpsMatch[1]}/${httpsMatch[2]}`;
2326
+ }
2327
+ if (/^[^/\s]+\/[^/\s]+$/.test(withoutGit)) {
2328
+ return withoutGit;
2329
+ }
2330
+ return void 0;
2331
+ };
2332
+ var resolveGitMetadata = async (cwd, options) => {
2333
+ const status = await runGit(cwd, ["status", "--porcelain"]);
2334
+ const dirty = status.ok && status.stdout.length > 0;
2335
+ const remote = options.repo ? { ok: true, stdout: options.repo } : await runGit(cwd, ["remote", "get-url", "origin"]);
2336
+ const ref = options.ref ? options.ref : (await runGit(cwd, ["rev-parse", "--abbrev-ref", "HEAD"])).stdout;
2337
+ const sha = options.commitSha ? options.commitSha : (await runGit(cwd, ["rev-parse", "HEAD"])).stdout;
2338
+ return {
2339
+ repo: normalizeGithubRepo(remote.stdout),
2340
+ ref: ref && ref !== "HEAD" ? ref : void 0,
2341
+ commitSha: sha || void 0,
2342
+ dirty
2343
+ };
2344
+ };
2345
+ var asRecord = (value) => value && typeof value === "object" ? value : {};
2346
+ var sourceOf = (project) => asRecord(project.iac_source ?? project.iacSource);
2347
+ var resolveProjectId = async ({
2348
+ baseUrl,
2349
+ token,
2350
+ requestedProjectId,
2351
+ repo,
2352
+ ref,
2353
+ sourceRoot
2354
+ }) => {
2355
+ if (requestedProjectId) {
2356
+ return requestedProjectId;
2357
+ }
2358
+ if (!repo) {
2359
+ throw new Error("Provide --repo or run inside a GitHub-backed Git repository.");
2360
+ }
2361
+ const projects = await fetchCloudEvalJson({
2362
+ baseUrl,
2363
+ authToken: token,
2364
+ path: "/projects"
2365
+ });
2366
+ const matches = projects.filter((project) => {
2367
+ const record = asRecord(project);
2368
+ const source = sourceOf(record);
2369
+ if (source.type !== "github") return false;
2370
+ if (source.repo_full_name !== repo) return false;
2371
+ if (sourceRoot !== void 0 && String(source.source_root ?? "") !== sourceRoot) {
2372
+ return false;
2373
+ }
2374
+ if (ref && source.ref && source.ref !== ref) {
2375
+ return false;
2376
+ }
2377
+ return true;
2378
+ });
2379
+ if (matches.length === 1) {
2380
+ return String(asRecord(matches[0]).id);
2381
+ }
2382
+ if (matches.length > 1) {
2383
+ throw new Error(
2384
+ `Multiple CloudEval projects match ${repo}. Pass --project to choose one.`
2385
+ );
2386
+ }
2387
+ throw new Error(
2388
+ `No CloudEval GitHub project matched ${repo}. Create one in CloudEval or pass --project.`
2389
+ );
2390
+ };
2391
+ var readConfigText = async (cwd, options) => {
2392
+ const configPath = options.config ?? path2.join(cwd, ".cloudeval", "config.yaml");
2393
+ try {
2394
+ return await fs2.readFile(path2.resolve(cwd, configPath), "utf8");
2395
+ } catch {
2396
+ return void 0;
2397
+ }
2398
+ };
2399
+ var parseGateConfig = (configText) => {
2400
+ if (!configText || !/^\s*ci\s*:/m.test(configText) || !/^\s*gates\s*:/m.test(configText)) {
2401
+ return void 0;
2402
+ }
2403
+ const stringValue2 = (key) => {
2404
+ const match = configText.match(new RegExp(`^\\s*${key}\\s*:\\s*([^\\s#]+)`, "m"));
2405
+ return match ? match[1].trim() : void 0;
2406
+ };
2407
+ const numberValue2 = (key) => {
2408
+ const match = configText.match(new RegExp(`^\\s*${key}\\s*:\\s*([0-9]+(?:\\.[0-9]+)?)`, "m"));
2409
+ return match ? Number(match[1]) : void 0;
2410
+ };
2411
+ const booleanValue2 = (key) => {
2412
+ const match = configText.match(new RegExp(`^\\s*${key}\\s*:\\s*(true|false)`, "im"));
2413
+ return match ? match[1].toLowerCase() === "true" : void 0;
2414
+ };
2415
+ const pillarScoreMins = {};
2416
+ const pillarBlock = configText.match(/^(\s*)pillars\s*:\s*$(?<body>(?:\n\s+[-\w]+\s*:\s*[0-9]+(?:\.[0-9]+)?\s*)+)/m);
2417
+ const pillarBody = pillarBlock?.groups?.body ?? "";
2418
+ for (const line of pillarBody.split("\n")) {
2419
+ const match = line.match(/^\s+([-\w]+)\s*:\s*([0-9]+(?:\.[0-9]+)?)/);
2420
+ if (match) {
2421
+ pillarScoreMins[match[1].replace(/-/g, "_").toLowerCase()] = Number(match[2]);
2422
+ }
2423
+ }
2424
+ for (const key of [
2425
+ "security",
2426
+ "reliability",
2427
+ "operational_excellence",
2428
+ "performance_efficiency",
2429
+ "cost_optimization"
2430
+ ]) {
2431
+ const value = numberValue2(`${key}_score_min`);
2432
+ if (value !== void 0) {
2433
+ pillarScoreMins[key] = value;
2434
+ }
2435
+ }
2436
+ const enforcement = stringValue2("enforcement")?.toLowerCase();
2437
+ return {
2438
+ enforcement: enforcement === "warn" ? "warn" : "required",
2439
+ overallScoreMin: numberValue2("overall_score_min") ?? 80,
2440
+ pillarScoreMin: numberValue2("pillar_score_min"),
2441
+ pillarScoreMins,
2442
+ failOnHighRisk: booleanValue2("fail_on_high_risk") ?? true,
2443
+ failOnValidationErrors: booleanValue2("fail_on_validation_errors") ?? true,
2444
+ maxMonthlyCost: numberValue2("max_monthly_cost")
2445
+ };
2446
+ };
2447
+ var numberFrom = (...values) => {
2448
+ for (const value of values) {
2449
+ if (typeof value === "number" && Number.isFinite(value)) {
2450
+ return value;
2451
+ }
2452
+ if (typeof value === "string" && value.trim() && Number.isFinite(Number(value))) {
2453
+ return Number(value);
2454
+ }
2455
+ }
2456
+ return void 0;
2457
+ };
2458
+ var normalizeKey = (value) => String(value ?? "").trim().replace(/[\s-]+/g, "_").toLowerCase();
2459
+ var firstRecord = (...values) => {
2460
+ for (const value of values) {
2461
+ if (value && typeof value === "object" && !Array.isArray(value)) {
2462
+ return value;
2463
+ }
2464
+ }
2465
+ return void 0;
2466
+ };
2467
+ var extractPillars = (waf) => {
2468
+ const fromArray = waf?.parsed?.score?.pillars;
2469
+ if (Array.isArray(fromArray)) {
2470
+ return fromArray.map((pillar) => {
2471
+ const record = asRecord(pillar);
2472
+ const id = normalizeKey(record.id ?? record.name ?? record.label);
2473
+ const score = numberFrom(record.score, record.value);
2474
+ if (!id || score === void 0) return void 0;
2475
+ return {
2476
+ id,
2477
+ label: String(record.label ?? record.name ?? id),
2478
+ score,
2479
+ passed: numberFrom(record.passed, record.passed_rules),
2480
+ warned: numberFrom(record.warned, record.warning_rules),
2481
+ failed: numberFrom(record.failed, record.failed_rules)
2482
+ };
2483
+ }).filter((pillar) => pillar !== void 0);
2484
+ }
2485
+ const scores = firstRecord(
2486
+ waf?.parsed?.pillar_scores,
2487
+ waf?.parsed?.score?.pillar_scores,
2488
+ waf?.raw?.pillar_scores
2489
+ );
2490
+ if (!scores) return [];
2491
+ return Object.entries(scores).map(([id, score]) => ({
2492
+ id: normalizeKey(id),
2493
+ label: id,
2494
+ score: numberFrom(score)
2495
+ })).filter((pillar) => pillar.score !== void 0);
2496
+ };
2497
+ var extractValidation = ({
2498
+ waf,
2499
+ preload,
2500
+ project
2501
+ }) => {
2502
+ const psRuleSummary = firstRecord(
2503
+ waf?.parsed?.validation_summary,
2504
+ waf?.raw?.validation_summary,
2505
+ waf?.validation_summary
2506
+ );
2507
+ const rules = Array.isArray(waf?.parsed?.rules) ? waf?.parsed?.rules : [];
2508
+ const failedRules = rules.filter(
2509
+ (rule) => ["fail", "failed", "error"].includes(String(rule?.status ?? rule?.outcome ?? "").toLowerCase())
2510
+ );
2511
+ const unitSummary = firstRecord(
2512
+ preload?.latest_payloads?.unit_tests?.summary,
2513
+ preload?.reports?.unit_tests?.metrics,
2514
+ project?.status?.unit_tests
2515
+ );
2516
+ return {
2517
+ psRule: {
2518
+ total: numberFrom(psRuleSummary?.total_rules, psRuleSummary?.totalRules, rules.length),
2519
+ passed: numberFrom(psRuleSummary?.passed_rules, psRuleSummary?.passedRules),
2520
+ failed: numberFrom(psRuleSummary?.failed_rules, psRuleSummary?.failedRules, failedRules.length),
2521
+ errors: numberFrom(psRuleSummary?.error_rules, psRuleSummary?.errorRules)
2522
+ },
2523
+ unitTests: {
2524
+ status: unitSummary?.status,
2525
+ total: numberFrom(unitSummary?.total_tests, unitSummary?.totalTests),
2526
+ passed: numberFrom(unitSummary?.passed_tests, unitSummary?.passedTests),
2527
+ failed: numberFrom(unitSummary?.failed_tests, unitSummary?.failedTests),
2528
+ skipped: numberFrom(unitSummary?.skipped_tests, unitSummary?.skippedTests)
2529
+ }
2530
+ };
2531
+ };
2532
+ var evaluateGate = ({
2533
+ configText,
2534
+ waf,
2535
+ cost,
2536
+ preload,
2537
+ project
2538
+ }) => {
2539
+ const gateConfig = parseGateConfig(configText);
2540
+ const overallScore = numberFrom(
2541
+ waf?.parsed?.score?.overall,
2542
+ waf?.parsed?.overall_score,
2543
+ waf?.raw?.score
2544
+ );
2545
+ const highRisk = numberFrom(
2546
+ waf?.parsed?.counts?.highRisk,
2547
+ waf?.parsed?.counts?.high_count,
2548
+ waf?.parsed?.highRisk
2549
+ );
2550
+ const monthlyCost = numberFrom(
2551
+ cost?.parsed?.totalSpend?.amount,
2552
+ cost?.parsed?.total_spend?.amount,
2553
+ cost?.raw?.total
2554
+ );
2555
+ const currency = String(
2556
+ cost?.parsed?.totalSpend?.currency ?? cost?.parsed?.total_spend?.currency ?? cost?.raw?.currency ?? ""
2557
+ ) || void 0;
2558
+ const savings = numberFrom(
2559
+ cost?.parsed?.estimatedSavings?.amount,
2560
+ cost?.parsed?.estimated_savings?.amount
2561
+ );
2562
+ const pillars = extractPillars(waf).map((pillar) => {
2563
+ const threshold = gateConfig?.pillarScoreMins[pillar.id] ?? gateConfig?.pillarScoreMin;
2564
+ return {
2565
+ ...pillar,
2566
+ threshold,
2567
+ status: threshold !== void 0 && pillar.score < threshold ? "fail" : "pass"
2568
+ };
2569
+ });
2570
+ const validation = extractValidation({ waf, preload, project });
2571
+ const wellArchitected = {
2572
+ overall: {
2573
+ score: overallScore,
2574
+ threshold: gateConfig?.overallScoreMin,
2575
+ status: gateConfig && overallScore !== void 0 && overallScore < gateConfig.overallScoreMin ? "fail" : "pass"
2576
+ },
2577
+ pillars,
2578
+ risks: {
2579
+ high: highRisk,
2580
+ medium: numberFrom(waf?.parsed?.counts?.mediumRisk, waf?.parsed?.counts?.medium_count),
2581
+ critical: numberFrom(waf?.parsed?.counts?.criticalRisk, waf?.parsed?.counts?.critical_count)
2582
+ },
2583
+ topFindings: Array.isArray(waf?.parsed?.rules) ? waf.parsed.rules.slice(0, 5) : []
2584
+ };
2585
+ const costSummary = {
2586
+ monthly: {
2587
+ amount: monthlyCost,
2588
+ currency,
2589
+ threshold: gateConfig?.maxMonthlyCost,
2590
+ status: gateConfig?.maxMonthlyCost !== void 0 && monthlyCost !== void 0 && monthlyCost > gateConfig.maxMonthlyCost ? "fail" : "pass"
2591
+ },
2592
+ estimatedSavings: {
2593
+ amount: savings,
2594
+ currency
2595
+ },
2596
+ topServices: Array.isArray(cost?.parsed?.serviceGroups) ? cost.parsed.serviceGroups.slice(0, 5) : [],
2597
+ recommendations: Array.isArray(cost?.parsed?.recommendations) ? cost.parsed.recommendations.slice(0, 5) : []
2598
+ };
2599
+ if (!gateConfig) {
2600
+ return {
2601
+ status: "warn",
2602
+ reason: "ci.gates is not configured in .cloudeval/config.yaml.",
2603
+ enforcement: "warn",
2604
+ overallScore,
2605
+ highRisk,
2606
+ monthlyCost,
2607
+ wellArchitected,
2608
+ cost: costSummary,
2609
+ validation
2610
+ };
2611
+ }
2612
+ const failures = [];
2613
+ if (overallScore !== void 0 && overallScore < gateConfig.overallScoreMin) {
2614
+ failures.push(
2615
+ `overall score ${overallScore} is below ${gateConfig.overallScoreMin}`
2616
+ );
2617
+ }
2618
+ if (gateConfig.failOnHighRisk && highRisk !== void 0 && highRisk > 0) {
2619
+ failures.push(`${highRisk} high-risk architecture findings`);
2620
+ }
2621
+ for (const pillar of pillars) {
2622
+ if (pillar.status === "fail") {
2623
+ failures.push(`${pillar.label} score ${pillar.score} is below ${pillar.threshold}`);
2624
+ }
2625
+ }
2626
+ const failedPsRules = validation.psRule.failed ?? 0;
2627
+ const failedUnitTests = validation.unitTests.failed ?? 0;
2628
+ if (gateConfig.failOnValidationErrors && (failedPsRules > 0 || failedUnitTests > 0)) {
2629
+ failures.push(
2630
+ `validation has ${failedPsRules} failed PSRule checks and ${failedUnitTests} failed unit tests`
2631
+ );
2632
+ }
2633
+ if (gateConfig.maxMonthlyCost !== void 0 && monthlyCost !== void 0 && monthlyCost > gateConfig.maxMonthlyCost) {
2634
+ failures.push(`monthly cost ${monthlyCost} exceeds ${gateConfig.maxMonthlyCost}`);
2635
+ }
2636
+ const wouldFail = failures.length > 0;
2637
+ return {
2638
+ status: wouldFail && gateConfig.enforcement === "required" ? "fail" : wouldFail ? "warn" : "pass",
2639
+ enforcement: gateConfig.enforcement,
2640
+ wouldFail,
2641
+ failures,
2642
+ thresholds: gateConfig,
2643
+ overallScore,
2644
+ highRisk,
2645
+ monthlyCost,
2646
+ wellArchitected,
2647
+ cost: costSummary,
2648
+ validation
2649
+ };
2650
+ };
2651
+ var safeFetch = async (input) => {
2652
+ try {
2653
+ return await fetchCloudEvalJson(input);
2654
+ } catch {
2655
+ return void 0;
2656
+ }
2657
+ };
2658
+ var parsePositiveInteger = (value, flagName, fallback) => {
2659
+ if (value === void 0 || value === "") {
2660
+ return fallback;
2661
+ }
2662
+ const parsed = Number(value);
2663
+ if (!Number.isFinite(parsed) || parsed <= 0) {
2664
+ throw new Error(`${flagName} must be a positive number of milliseconds.`);
2665
+ }
2666
+ return Math.floor(parsed);
2667
+ };
2668
+ var extractJobId2 = (value) => {
2669
+ const record = asRecord(value);
2670
+ const job = asRecord(record.job);
2671
+ const candidates = [
2672
+ record.job_id,
2673
+ record.jobId,
2674
+ record.id,
2675
+ job.job_id,
2676
+ job.jobId,
2677
+ job.id
2678
+ ];
2679
+ return candidates.find(
2680
+ (candidate) => typeof candidate === "string" && candidate.trim().length > 0
2681
+ );
2682
+ };
2683
+ var isTerminalJobStatus2 = (value) => {
2684
+ const status = String(asRecord(value).status ?? "").toUpperCase();
2685
+ return [
2686
+ "COMPLETED",
2687
+ "SUCCEEDED",
2688
+ "SUCCESS",
2689
+ "FAILED",
2690
+ "CANCELLED",
2691
+ "CANCELED",
2692
+ "ERROR"
2693
+ ].includes(status);
2694
+ };
2695
+ var isFailedJobStatus = (value) => {
2696
+ const status = String(asRecord(value).status ?? "").toUpperCase();
2697
+ return ["FAILED", "CANCELLED", "CANCELED", "ERROR"].includes(status);
2698
+ };
2699
+ var waitForJob = async ({
2700
+ baseUrl,
2701
+ token,
2702
+ userId,
2703
+ jobId,
2704
+ pollIntervalMs,
2705
+ waitTimeoutMs
2706
+ }) => {
2707
+ const startedAt = Date.now();
2708
+ let lastStatus;
2709
+ for (; ; ) {
2710
+ const query = userId ? `?user_id=${encodeURIComponent(userId)}` : "";
2711
+ lastStatus = await fetchCloudEvalJson({
2712
+ baseUrl,
2713
+ authToken: token,
2714
+ path: `/jobs/${encodeURIComponent(jobId)}${query}`
2715
+ });
2716
+ const status = String(lastStatus.status ?? "unknown");
2717
+ process.stderr.write(`github sync job ${jobId}: ${status}
2718
+ `);
2719
+ if (isTerminalJobStatus2(lastStatus)) {
2720
+ if (isFailedJobStatus(lastStatus)) {
2721
+ throw new Error(`GitHub sync job ${jobId} finished with status ${status}.`);
2722
+ }
2723
+ return lastStatus;
2724
+ }
2725
+ if (Date.now() - startedAt > waitTimeoutMs) {
2726
+ throw new Error(`Timed out waiting for GitHub sync job ${jobId}.`);
2727
+ }
2728
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
2729
+ }
2730
+ };
2731
+ var fetchProjectById = async ({
2732
+ baseUrl,
2733
+ token,
2734
+ projectId
2735
+ }) => {
2736
+ const projects = await safeFetch({
2737
+ baseUrl,
2738
+ authToken: token,
2739
+ path: "/projects"
2740
+ });
2741
+ return projects?.map(asRecord).find((project) => project.id === projectId);
2742
+ };
2743
+ var buildAiSummaryPrompt = (data) => [
2744
+ "Write a concise CloudEval pull request review summary in Markdown.",
2745
+ "Focus on gate status, Well-Architected posture, cost posture, and security/operational risks.",
2746
+ "Keep it under 160 words. Do not invent facts not present below.",
2747
+ "",
2748
+ `Project: ${data.projectId}`,
2749
+ `Repository: ${data.repo ?? "unknown"}`,
2750
+ `Ref: ${data.ref ?? "unknown"}`,
2751
+ `Commit: ${data.commitSha ?? "unknown"}`,
2752
+ `Gate: ${String(data.gate?.status ?? "unknown").toUpperCase()}`,
2753
+ `Well-Architected score: ${data.gate?.overallScore ?? "unknown"}`,
2754
+ `High-risk findings: ${data.gate?.highRisk ?? "unknown"}`,
2755
+ `Monthly cost: ${data.gate?.monthlyCost ?? "unknown"}`,
2756
+ `Validation: PSRule failed ${data.gate?.validation?.psRule?.failed ?? "unknown"}, unit tests failed ${data.gate?.validation?.unitTests?.failed ?? "unknown"}`,
2757
+ Array.isArray(data.gate?.failures) && data.gate.failures.length ? `Gate failures: ${data.gate.failures.join("; ")}` : "Gate failures: none reported"
2758
+ ].join("\n");
2759
+ var generateAiSummary = async ({
2760
+ baseUrl,
2761
+ token,
2762
+ user,
2763
+ project,
2764
+ model,
2765
+ mode,
2766
+ agentProfileId,
2767
+ data
2768
+ }) => {
2769
+ const core = await import("./dist-CFLR5FML.js");
2770
+ const threadId = `review-${data.projectId}-${Date.now()}`;
2771
+ let markdown = "";
2772
+ for await (const chunk of core.streamChat({
2773
+ baseUrl,
2774
+ authToken: token,
2775
+ message: buildAiSummaryPrompt(data),
2776
+ threadId,
2777
+ user: {
2778
+ id: String(project?.user_id ?? user?.id ?? "cli-user"),
2779
+ name: String(user?.full_name ?? user?.name ?? user?.email ?? "CloudEval CI")
2780
+ },
2781
+ project: project ? {
2782
+ id: String(project.id ?? data.projectId),
2783
+ name: String(project.name ?? data.projectId),
2784
+ user_id: typeof project.user_id === "string" ? project.user_id : void 0,
2785
+ cloud_provider: typeof project.cloud_provider === "string" ? project.cloud_provider : void 0,
2786
+ type: typeof project.type === "string" ? project.type : void 0,
2787
+ connection_ids: Array.isArray(project.connection_ids) ? project.connection_ids : void 0
2788
+ } : {
2789
+ id: String(data.projectId),
2790
+ name: String(data.projectId)
2791
+ },
2792
+ settings: {
2793
+ mode,
2794
+ ...model ? { model } : {}
2795
+ },
2796
+ ...mode === "agent" ? { agentProfileId: agentProfileId ?? "architecture" } : {},
2797
+ completeAfterResponse: true,
2798
+ responseCompletionGraceMs: 250,
2799
+ streamIdleTimeoutMs: 3e4
2800
+ })) {
2801
+ const content = chunk?.content;
2802
+ if (chunk.type === "responding" && typeof content === "string") {
2803
+ markdown += content;
2804
+ }
2805
+ }
2806
+ return {
2807
+ enabled: true,
2808
+ mode,
2809
+ ...mode === "agent" ? { agentProfileId: agentProfileId ?? "architecture" } : {},
2810
+ ...model ? { model } : {},
2811
+ markdown: markdown.trim(),
2812
+ threadId
2813
+ };
2814
+ };
2815
+ var buildMarkdownSummary = (data) => {
2816
+ const gateStatus = String(data.gate?.status ?? "unknown").toUpperCase();
2817
+ const score = data.gate?.overallScore ?? "unknown";
2818
+ const cost = data.gate?.cost?.monthly;
2819
+ const validation = data.gate?.validation;
2820
+ const pillarLines = Array.isArray(data.gate?.wellArchitected?.pillars) ? data.gate.wellArchitected.pillars.map(
2821
+ (pillar) => ` - ${pillar.label}: ${pillar.score}${pillar.threshold !== void 0 ? ` (min ${pillar.threshold})` : ""} - ${String(pillar.status ?? "unknown").toUpperCase()}`
2822
+ ) : [];
2823
+ const lines = [
2824
+ "### CloudEval review",
2825
+ "",
2826
+ `- **Project:** \`${data.projectId}\``,
2827
+ `- **Repository:** \`${data.repo ?? "unknown"}\``,
2828
+ `- **Ref:** \`${data.ref ?? "unknown"}\``,
2829
+ `- **Commit:** \`${String(data.commitSha ?? "unknown").slice(0, 12)}\``,
2830
+ `- **Gate:** ${gateStatus}`,
2831
+ `- **Well-Architected score:** ${score}`,
2832
+ `- **Cost:** ${cost?.amount ?? "unknown"} ${cost?.currency ?? ""}`.trim(),
2833
+ `- **Validation:** PSRule ${validation?.psRule?.failed ?? "unknown"} failed, unit tests ${validation?.unitTests?.failed ?? "unknown"} failed`
2834
+ ];
2835
+ if (Array.isArray(data.gate?.failures) && data.gate.failures.length) {
2836
+ lines.push("", "#### Gate failures", "", ...data.gate.failures.map((failure) => `- ${failure}`));
2837
+ }
2838
+ if (pillarLines.length) {
2839
+ lines.push("", "#### Well-Architected drilldown", "", ...pillarLines);
2840
+ }
2841
+ if (cost?.amount !== void 0 || cost?.threshold !== void 0) {
2842
+ lines.push(
2843
+ "",
2844
+ "#### Cost drilldown",
2845
+ "",
2846
+ `- Cost: ${cost?.amount ?? "unknown"} ${cost?.currency ?? ""}${cost?.threshold !== void 0 ? ` (max ${cost.threshold})` : ""}`.trim()
2847
+ );
2848
+ }
2849
+ if (validation) {
2850
+ lines.push(
2851
+ "",
2852
+ "#### Validation drilldown",
2853
+ "",
2854
+ `- Validation: PSRule ${validation.psRule?.failed ?? "unknown"} failed, unit tests ${validation.unitTests?.failed ?? "unknown"} failed`
2855
+ );
2856
+ }
2857
+ if (data.aiSummary?.markdown) {
2858
+ lines.push("", "## AI summary", "", data.aiSummary.markdown);
2859
+ }
2860
+ return lines.join("\n");
2861
+ };
2862
+ var registerReviewCommand = (program2, deps) => {
2863
+ const command = addAuthOptions(
2864
+ program2.command("review").description("Review the current GitHub-backed project from a pushed commit"),
2865
+ deps.defaultBaseUrl
2866
+ ).option("--project <id>", "CloudEval project id. If omitted, resolve by GitHub repo metadata.").option("--repo <owner/repo>", "GitHub repository. Defaults to git origin.").option("--ref <name>", "Git branch/ref. Defaults to current branch.").option("--commit-sha <sha>", "Commit SHA to sync/review. Defaults to local HEAD.").option("--source-root <path>", "GitHub source root used by the CloudEval project.").option("--config <path>", "Path to .cloudeval/config.yaml for gate thresholds.").option("--no-wait", "Submit GitHub sync and return without waiting for analysis.").option("--wait-timeout <ms>", "Maximum time to wait for GitHub sync.", "900000").option("--poll-interval <ms>", "Polling interval while waiting for GitHub sync.", "5000").option("--no-ai-summary", "Skip the AI-written review summary.").option("--ai-summary-mode <mode>", "AI summary mode: ask or agent.", "ask").option("--ai-summary-profile <profile-id>", "Agent Profile id when --ai-summary-mode agent is used.", "architecture").option("--ignore-dirty", "Review HEAD even if the local working tree has uncommitted changes.", false).option("--output <dir>", "Write review.json and review.md into a directory.").option("--quiet", "Accepted for CI parity; review output stays machine-readable.", false).option("--progress <mode>", "Accepted for CI parity; review does not stream progress.", "none").option("--model <model>", "Accepted for CI parity with ask/agent modes.").option("--format <format>", "Output format: text, json, ndjson, markdown", "text");
2867
+ command.action(async (options, actionCommand) => {
2868
+ try {
2869
+ const cwd = process.cwd();
2870
+ const git = await resolveGitMetadata(cwd, options);
2871
+ if (git.dirty && !options.ignoreDirty) {
2872
+ throw new Error(DIRTY_REVIEW_MESSAGE);
2873
+ }
2874
+ const context = await resolveAuthContext(options, actionCommand, deps);
2875
+ const repo = normalizeGithubRepo(options.repo) ?? git.repo;
2876
+ const ref = options.ref ?? git.ref;
2877
+ const commitSha = options.commitSha ?? git.commitSha;
2878
+ const sourceRoot = options.sourceRoot;
2879
+ const projectId = await resolveProjectId({
2880
+ baseUrl: context.baseUrl,
2881
+ token: context.token,
2882
+ requestedProjectId: options.project,
2883
+ repo,
2884
+ ref,
2885
+ sourceRoot
2886
+ });
2887
+ const sync = await fetchCloudEvalJson({
2888
+ baseUrl: context.baseUrl,
2889
+ authToken: context.token,
2890
+ path: `/projects/${projectId}/github/sync`,
2891
+ method: "POST",
2892
+ body: commitSha ? { commit_sha: commitSha } : {},
2893
+ idempotencyKey: `cloudeval-review-${projectId}-${commitSha ?? "head"}`
2894
+ });
2895
+ const finalStatus = options.wait === false ? void 0 : extractJobId2(sync) ? await waitForJob({
2896
+ baseUrl: context.baseUrl,
2897
+ token: context.token,
2898
+ userId: context.user?.id,
2899
+ jobId: extractJobId2(sync),
2900
+ pollIntervalMs: parsePositiveInteger(
2901
+ options.pollInterval,
2902
+ "--poll-interval",
2903
+ 5e3
2904
+ ),
2905
+ waitTimeoutMs: parsePositiveInteger(
2906
+ options.waitTimeout,
2907
+ "--wait-timeout",
2908
+ 9e5
2909
+ )
2910
+ }) : void 0;
2911
+ const [cost, waf, configText] = await Promise.all([
2912
+ safeFetch({
2913
+ baseUrl: context.baseUrl,
2914
+ authToken: context.token,
2915
+ path: `/cost-reports/${projectId}/full`
2916
+ }),
2917
+ safeFetch({
2918
+ baseUrl: context.baseUrl,
2919
+ authToken: context.token,
2920
+ path: `/well-architected-reports/${projectId}/full`
2921
+ }),
2922
+ readConfigText(cwd, options)
2923
+ ]);
2924
+ const project = await fetchProjectById({
2925
+ baseUrl: context.baseUrl,
2926
+ token: context.token,
2927
+ projectId
2928
+ });
2929
+ const preload = context.user?.id ? await safeFetch({
2930
+ baseUrl: context.baseUrl,
2931
+ authToken: context.token,
2932
+ path: `/reports/preload/${encodeURIComponent(projectId)}?user_id=${encodeURIComponent(context.user.id)}&include_payload=true`
2933
+ }) : void 0;
2934
+ const data = {
2935
+ projectId,
2936
+ repo,
2937
+ ref,
2938
+ commitSha,
2939
+ sourceRoot,
2940
+ sync: finalStatus ? { ...asRecord(sync), finalStatus } : sync,
2941
+ reports: {
2942
+ cost,
2943
+ waf,
2944
+ preload
2945
+ },
2946
+ gate: evaluateGate({ configText, waf, cost, preload, project })
2947
+ };
2948
+ if (options.aiSummary !== false) {
2949
+ try {
2950
+ const aiSummaryMode = String(options.aiSummaryMode ?? "ask").toLowerCase();
2951
+ if (!["ask", "agent"].includes(aiSummaryMode)) {
2952
+ throw new Error("--ai-summary-mode must be ask or agent.");
2953
+ }
2954
+ data.aiSummary = await generateAiSummary({
2955
+ baseUrl: context.baseUrl,
2956
+ token: context.token,
2957
+ user: context.user,
2958
+ project,
2959
+ model: options.model,
2960
+ mode: aiSummaryMode,
2961
+ agentProfileId: options.aiSummaryProfile,
2962
+ data
2963
+ });
2964
+ } catch (error) {
2965
+ data.aiSummary = {
2966
+ enabled: true,
2967
+ status: "failed",
2968
+ error: error?.message ?? "AI summary failed"
2969
+ };
2970
+ }
2971
+ } else {
2972
+ data.aiSummary = { enabled: false };
2973
+ }
2974
+ const summaryMarkdown = buildMarkdownSummary(data);
2975
+ const filesWritten = [];
2976
+ if (options.output) {
2977
+ const outputDir = path2.resolve(options.output);
2978
+ await fs2.mkdir(outputDir, { recursive: true });
2979
+ const jsonPath = path2.join(outputDir, "review.json");
2980
+ const markdownPath = path2.join(outputDir, "review.md");
2981
+ await fs2.writeFile(jsonPath, JSON.stringify(data, null, 2), "utf8");
2982
+ await fs2.writeFile(markdownPath, summaryMarkdown, "utf8");
2983
+ filesWritten.push(jsonPath, markdownPath);
2984
+ }
2985
+ await writeFormattedOutput({
2986
+ command: "review",
2987
+ data: { ...data, summaryMarkdown },
2988
+ format: options.format,
2989
+ filesWritten
2990
+ });
2991
+ if (data.gate.status === "fail") {
2992
+ process.exit(1);
2993
+ }
2994
+ process.exit(0);
2995
+ } catch (error) {
2996
+ console.error(error?.message ?? "Review failed");
2997
+ process.exit(1);
2998
+ }
2999
+ });
3000
+ };
3001
+
2220
3002
  // src/recipesCommand.ts
2221
3003
  import { randomUUID } from "crypto";
2222
- import fs2 from "fs/promises";
3004
+ import fs3 from "fs/promises";
2223
3005
 
2224
3006
  // src/askProgress.ts
2225
3007
  var ASK_PROGRESS_MODES = /* @__PURE__ */ new Set(["auto", "stderr", "ndjson", "none"]);
@@ -3455,7 +4237,7 @@ var writeRecipeList = async (options) => {
3455
4237
  if (format === "table" || format === "text") {
3456
4238
  const text = renderRecipesTable();
3457
4239
  if (options.output) {
3458
- await fs2.writeFile(options.output, text, "utf8");
4240
+ await fs3.writeFile(options.output, text, "utf8");
3459
4241
  return;
3460
4242
  }
3461
4243
  process.stdout.write(text);
@@ -3464,7 +4246,7 @@ var writeRecipeList = async (options) => {
3464
4246
  if (format === "markdown") {
3465
4247
  const text = renderRecipesMarkdown();
3466
4248
  if (options.output) {
3467
- await fs2.writeFile(options.output, text, "utf8");
4249
+ await fs3.writeFile(options.output, text, "utf8");
3468
4250
  return;
3469
4251
  }
3470
4252
  process.stdout.write(text);
@@ -3482,7 +4264,7 @@ var writeRecipeShow = async (recipe, options) => {
3482
4264
  if (format === "markdown" || format === "text") {
3483
4265
  const text = renderRecipeMarkdown(recipe);
3484
4266
  if (options.output) {
3485
- await fs2.writeFile(options.output, text, "utf8");
4267
+ await fs3.writeFile(options.output, text, "utf8");
3486
4268
  return;
3487
4269
  }
3488
4270
  process.stdout.write(text);
@@ -3677,15 +4459,15 @@ var registerRecipesCommand = (program2, deps) => {
3677
4459
  };
3678
4460
 
3679
4461
  // src/skillsCommand.ts
3680
- import fs4 from "fs/promises";
4462
+ import fs5 from "fs/promises";
3681
4463
 
3682
4464
  // src/skills/catalog.ts
3683
- import fs3 from "fs/promises";
4465
+ import fs4 from "fs/promises";
3684
4466
  import fsSync from "fs";
3685
- import path2 from "path";
4467
+ import path3 from "path";
3686
4468
  import { fileURLToPath as fileURLToPath2 } from "url";
3687
- var repoRoot = path2.resolve(fileURLToPath2(new URL("../../../../", import.meta.url)));
3688
- var defaultSkillsPath = path2.join(repoRoot, "skills");
4469
+ var repoRoot = path3.resolve(fileURLToPath2(new URL("../../../../", import.meta.url)));
4470
+ var defaultSkillsPath = path3.join(repoRoot, "skills");
3689
4471
  var requiredSkillSections = [
3690
4472
  "## WHEN",
3691
4473
  "## DO NOT USE FOR",
@@ -3773,7 +4555,7 @@ var normalizeSkillId = (id) => {
3773
4555
  return metadataById.has(prefixed) ? prefixed : normalized;
3774
4556
  };
3775
4557
  var getSkillsPath = () => process.env.CLOUDEVAL_SKILLS_PATH ?? defaultSkillsPath;
3776
- var skillFilePath = (id) => path2.join(getSkillsPath(), id, "SKILL.md");
4558
+ var skillFilePath = (id) => path3.join(getSkillsPath(), id, "SKILL.md");
3777
4559
  var fileExists = (filePath) => {
3778
4560
  try {
3779
4561
  return fsSync.statSync(filePath).isFile();
@@ -3785,7 +4567,7 @@ var readSkillFile = async (id) => {
3785
4567
  const filePath = skillFilePath(id);
3786
4568
  if (fileExists(filePath)) {
3787
4569
  return {
3788
- content: await fs3.readFile(filePath, "utf8"),
4570
+ content: await fs4.readFile(filePath, "utf8"),
3789
4571
  path: filePath,
3790
4572
  source: "filesystem"
3791
4573
  };
@@ -3982,7 +4764,7 @@ var writeSkillList = async (options) => {
3982
4764
  if (format === "table" || format === "text") {
3983
4765
  const text = renderSkillsTable(skills);
3984
4766
  if (options.output) {
3985
- await fs4.writeFile(options.output, text, "utf8");
4767
+ await fs5.writeFile(options.output, text, "utf8");
3986
4768
  return;
3987
4769
  }
3988
4770
  process.stdout.write(text);
@@ -3991,7 +4773,7 @@ var writeSkillList = async (options) => {
3991
4773
  if (format === "markdown") {
3992
4774
  const text = renderSkillsMarkdown(skills);
3993
4775
  if (options.output) {
3994
- await fs4.writeFile(options.output, text, "utf8");
4776
+ await fs5.writeFile(options.output, text, "utf8");
3995
4777
  return;
3996
4778
  }
3997
4779
  process.stdout.write(text);
@@ -4014,7 +4796,7 @@ var writeSkillShow = async (id, options) => {
4014
4796
  const format = options.format ?? "markdown";
4015
4797
  if (format === "markdown" || format === "text") {
4016
4798
  if (options.output) {
4017
- await fs4.writeFile(options.output, skill.content, "utf8");
4799
+ await fs5.writeFile(options.output, skill.content, "utf8");
4018
4800
  return;
4019
4801
  }
4020
4802
  process.stdout.write(skill.content);
@@ -4069,7 +4851,7 @@ var writeSkillDoctor = async (options) => {
4069
4851
  ""
4070
4852
  ].filter(Boolean).join("\n");
4071
4853
  if (options.output) {
4072
- await fs4.writeFile(options.output, `${text}
4854
+ await fs5.writeFile(options.output, `${text}
4073
4855
  `, "utf8");
4074
4856
  return;
4075
4857
  }
@@ -4101,7 +4883,7 @@ var registerSkillsCommand = (program2) => {
4101
4883
  const text = `${skillsPath}
4102
4884
  `;
4103
4885
  if (options.output) {
4104
- await fs4.writeFile(options.output, text, "utf8");
4886
+ await fs5.writeFile(options.output, text, "utf8");
4105
4887
  return;
4106
4888
  }
4107
4889
  process.stdout.write(text);
@@ -4274,10 +5056,10 @@ var registerOpenCommand = (program2, deps) => {
4274
5056
  };
4275
5057
 
4276
5058
  // src/projectsCommand.ts
4277
- import path3 from "path";
4278
- import fs5 from "fs/promises";
5059
+ import path4 from "path";
5060
+ import fs6 from "fs/promises";
4279
5061
  import os from "os";
4280
- import { spawn } from "child_process";
5062
+ import { spawn as spawn2 } from "child_process";
4281
5063
 
4282
5064
  // src/projectDiagramImage.ts
4283
5065
  var trimTrailingSlash = (value) => value.replace(/\/+$/, "");
@@ -4434,50 +5216,6 @@ var downloadProjectDiagramImage = async (input) => {
4434
5216
  };
4435
5217
  };
4436
5218
 
4437
- // src/apiClient.ts
4438
- var responseErrorMessage = async (response) => {
4439
- const text = await response.text();
4440
- if (!text) {
4441
- return `request failed with status ${response.status}`;
4442
- }
4443
- try {
4444
- const payload = JSON.parse(text);
4445
- const detail = payload?.detail ?? payload?.message ?? payload?.error;
4446
- if (typeof detail === "string") {
4447
- return detail;
4448
- }
4449
- if (detail) {
4450
- return JSON.stringify(detail);
4451
- }
4452
- } catch {
4453
- }
4454
- return text;
4455
- };
4456
- var fetchCloudEvalJson = async ({
4457
- baseUrl,
4458
- authToken,
4459
- path: path10,
4460
- method = "GET",
4461
- query = {},
4462
- body
4463
- }) => {
4464
- const url = new URL(`${normalizeApiBase(baseUrl)}${path10}`);
4465
- for (const [key, value] of Object.entries(query)) {
4466
- if (value !== void 0 && value !== null && value !== "") {
4467
- url.searchParams.set(key, String(value));
4468
- }
4469
- }
4470
- const response = await fetch(url, {
4471
- method,
4472
- headers: getCLIHeaders(authToken),
4473
- ...body === void 0 ? {} : { body: JSON.stringify(body) }
4474
- });
4475
- if (!response.ok) {
4476
- throw new Error(await responseErrorMessage(response));
4477
- }
4478
- return await response.json();
4479
- };
4480
-
4481
5219
  // src/graphClient.ts
4482
5220
  var normalizeGraphInsightFocus = (focus) => {
4483
5221
  const normalized = String(focus ?? "overview").trim().toLowerCase();
@@ -4637,7 +5375,7 @@ var writeProjectListOutput = async ({
4637
5375
  });
4638
5376
  }
4639
5377
  if (options.output) {
4640
- await fs5.writeFile(options.output, text, "utf8");
5378
+ await fs6.writeFile(options.output, text, "utf8");
4641
5379
  return;
4642
5380
  }
4643
5381
  process.stdout.write(text);
@@ -4646,10 +5384,10 @@ var fileBlob = async (filePath) => {
4646
5384
  if (!filePath) {
4647
5385
  return void 0;
4648
5386
  }
4649
- const bytes = await fs5.readFile(filePath);
5387
+ const bytes = await fs6.readFile(filePath);
4650
5388
  return {
4651
5389
  blob: new Blob([bytes], { type: "application/json" }),
4652
- name: path3.basename(filePath)
5390
+ name: path4.basename(filePath)
4653
5391
  };
4654
5392
  };
4655
5393
  var normalizeWorkspacePath = (value) => value.replace(/\\/g, "/").replace(/^\/+/, "").replace(/^\.\//, "").split("/").filter((part) => part && part !== ".").join("/");
@@ -4687,7 +5425,7 @@ var generateWorkspaceConfig = (entry, parameters, sourceEntry) => {
4687
5425
  ].filter((line) => line.length > 0).join("\n");
4688
5426
  };
4689
5427
  var runCommand = (command, args) => new Promise((resolve, reject) => {
4690
- const child = spawn(command, args, { stdio: ["ignore", "pipe", "pipe"] });
5428
+ const child = spawn2(command, args, { stdio: ["ignore", "pipe", "pipe"] });
4691
5429
  const stdout = [];
4692
5430
  const stderr = [];
4693
5431
  child.stdout.on("data", (chunk) => stdout.push(Buffer.from(chunk)));
@@ -4711,9 +5449,9 @@ var runCommand = (command, args) => new Promise((resolve, reject) => {
4711
5449
  });
4712
5450
  var isBicepPath = (filePath) => /\.bicep$/i.test(filePath);
4713
5451
  var compiledBicepPathFor = (entry) => {
4714
- const parsed = path3.posix.parse(entry);
5452
+ const parsed = path4.posix.parse(entry);
4715
5453
  return normalizeWorkspacePath(
4716
- path3.posix.join(
5454
+ path4.posix.join(
4717
5455
  ".cloudeval/template-cache/compiled",
4718
5456
  parsed.dir,
4719
5457
  `${parsed.name}.json`
@@ -4721,18 +5459,18 @@ var compiledBicepPathFor = (entry) => {
4721
5459
  );
4722
5460
  };
4723
5461
  var compileBicepEntry = async (root, entry) => {
4724
- const tempDir = await fs5.mkdtemp(path3.join(os.tmpdir(), "cloudeval-bicep-"));
4725
- const outputPath = path3.join(tempDir, "compiled.json");
5462
+ const tempDir = await fs6.mkdtemp(path4.join(os.tmpdir(), "cloudeval-bicep-"));
5463
+ const outputPath = path4.join(tempDir, "compiled.json");
4726
5464
  try {
4727
5465
  await runCommand("az", [
4728
5466
  "bicep",
4729
5467
  "build",
4730
5468
  "--file",
4731
- path3.join(root, entry),
5469
+ path4.join(root, entry),
4732
5470
  "--outfile",
4733
5471
  outputPath
4734
5472
  ]);
4735
- const bytes = await fs5.readFile(outputPath);
5473
+ const bytes = await fs6.readFile(outputPath);
4736
5474
  return {
4737
5475
  path: compiledBicepPathFor(entry),
4738
5476
  blob: new Blob([bytes], { type: "application/json" })
@@ -4746,16 +5484,16 @@ var compileBicepEntry = async (root, entry) => {
4746
5484
  }
4747
5485
  throw new Error(`Failed to compile Bicep workspace entry '${entry}': ${message}`);
4748
5486
  } finally {
4749
- await fs5.rm(tempDir, { recursive: true, force: true });
5487
+ await fs6.rm(tempDir, { recursive: true, force: true });
4750
5488
  }
4751
5489
  };
4752
5490
  var collectWorkspacePaths = async (root) => {
4753
5491
  const paths = [];
4754
5492
  const visit = async (directory) => {
4755
- const entries = await fs5.readdir(directory, { withFileTypes: true });
5493
+ const entries = await fs6.readdir(directory, { withFileTypes: true });
4756
5494
  for (const entry of entries) {
4757
- const absolute = path3.join(directory, entry.name);
4758
- const relative = normalizeWorkspacePath(path3.relative(root, absolute));
5495
+ const absolute = path4.join(directory, entry.name);
5496
+ const relative = normalizeWorkspacePath(path4.relative(root, absolute));
4759
5497
  if (!relative || isIgnoredWorkspacePath(relative)) {
4760
5498
  continue;
4761
5499
  }
@@ -4822,8 +5560,8 @@ var resolveWorkspaceParameters = (paths, explicitParameters, config) => {
4822
5560
  return findFirstPath(paths, ["azuredeploy.parameters.json", "parameters.json"]);
4823
5561
  };
4824
5562
  var collectWorkspaceFiles = async (workspaceDir, options) => {
4825
- const root = path3.resolve(workspaceDir);
4826
- const stat = await fs5.stat(root).catch(() => void 0);
5563
+ const root = path4.resolve(workspaceDir);
5564
+ const stat = await fs6.stat(root).catch(() => void 0);
4827
5565
  if (!stat?.isDirectory()) {
4828
5566
  throw new Error(`--workspace-dir '${workspaceDir}' is not a directory.`);
4829
5567
  }
@@ -4831,7 +5569,7 @@ var collectWorkspaceFiles = async (workspaceDir, options) => {
4831
5569
  const existingConfigPath = paths.find(
4832
5570
  (filePath) => filePath.toLowerCase() === ".cloudeval/config.yaml"
4833
5571
  );
4834
- const config = existingConfigPath ? readWorkspaceConfig(await fs5.readFile(path3.join(root, existingConfigPath), "utf8")) : void 0;
5572
+ const config = existingConfigPath ? readWorkspaceConfig(await fs6.readFile(path4.join(root, existingConfigPath), "utf8")) : void 0;
4835
5573
  const entry = detectWorkspaceEntry(paths, options.workspaceEntry, config);
4836
5574
  const parameters = resolveWorkspaceParameters(paths, options.workspaceParameters, config);
4837
5575
  const compiledEntry = isBicepPath(entry) ? await compileBicepEntry(root, entry) : void 0;
@@ -4853,7 +5591,7 @@ var collectWorkspaceFiles = async (workspaceDir, options) => {
4853
5591
  });
4854
5592
  continue;
4855
5593
  }
4856
- const bytes = await fs5.readFile(path3.join(root, relativePath));
5594
+ const bytes = await fs6.readFile(path4.join(root, relativePath));
4857
5595
  files.push({
4858
5596
  path: relativePath,
4859
5597
  blob: new Blob([bytes], {
@@ -4915,9 +5653,9 @@ var appendOptionValue = (value, previous = []) => [
4915
5653
  value
4916
5654
  ];
4917
5655
  var writeDiagramImageHeaders = async (outputPath, headers) => {
4918
- await fs5.mkdir(path3.dirname(outputPath), { recursive: true });
5656
+ await fs6.mkdir(path4.dirname(outputPath), { recursive: true });
4919
5657
  const text = Object.entries(headers).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `${key}: ${value}`).join("\n");
4920
- await fs5.writeFile(outputPath, `${text}
5658
+ await fs6.writeFile(outputPath, `${text}
4921
5659
  `, "utf8");
4922
5660
  };
4923
5661
  var listProjectsForContext = async (core, context) => {
@@ -5080,10 +5818,10 @@ var configureDiagramExportCommand = (command, deps) => addAuthOptions(command, d
5080
5818
  publicGraph,
5081
5819
  syncVersion: options.syncVersion
5082
5820
  });
5083
- const outputPath = path3.resolve(options.output);
5084
- const headersOutputPath = options.headersOutput ? path3.resolve(options.headersOutput) : void 0;
5085
- await fs5.mkdir(path3.dirname(outputPath), { recursive: true });
5086
- await fs5.writeFile(outputPath, result.bytes);
5821
+ const outputPath = path4.resolve(options.output);
5822
+ const headersOutputPath = options.headersOutput ? path4.resolve(options.headersOutput) : void 0;
5823
+ await fs6.mkdir(path4.dirname(outputPath), { recursive: true });
5824
+ await fs6.writeFile(outputPath, result.bytes);
5087
5825
  const filesWritten = [outputPath];
5088
5826
  if (headersOutputPath) {
5089
5827
  await writeDiagramImageHeaders(headersOutputPath, result.headers);
@@ -5217,7 +5955,7 @@ var registerProjectsCommand = (program2, deps) => {
5217
5955
  const parameters = await fileBlob(options.parametersFile);
5218
5956
  const workspace = options.workspaceDir ? await collectWorkspaceFiles(options.workspaceDir, options) : void 0;
5219
5957
  const cloudSync = resolveAzureCloudSyncInput(options);
5220
- const inferredName = options.name || (options.workspaceDir ? path3.basename(path3.resolve(options.workspaceDir)) : void 0) || (cloudSync ? "Cloud sync" : void 0) || (options.templateFile ? path3.basename(options.templateFile, path3.extname(options.templateFile)) : void 0);
5958
+ const inferredName = options.name || (options.workspaceDir ? path4.basename(path4.resolve(options.workspaceDir)) : void 0) || (cloudSync ? "Cloud sync" : void 0) || (options.templateFile ? path4.basename(options.templateFile, path4.extname(options.templateFile)) : void 0);
5221
5959
  const result = await core.createQuickProject({
5222
5960
  baseUrl: context.baseUrl,
5223
5961
  authToken: context.token,
@@ -5321,8 +6059,8 @@ var writeConnectionsListOutput = async ({
5321
6059
  if (format === "text") {
5322
6060
  const text = renderConnectionsListText(data);
5323
6061
  if (options.output) {
5324
- const fs13 = await import("fs/promises");
5325
- await fs13.writeFile(options.output, text, "utf8");
6062
+ const fs14 = await import("fs/promises");
6063
+ await fs14.writeFile(options.output, text, "utf8");
5326
6064
  return;
5327
6065
  }
5328
6066
  process.stdout.write(text);
@@ -5656,8 +6394,8 @@ var write = async (command, data, options, frontendUrl) => {
5656
6394
  const text = renderBillingText(command, data);
5657
6395
  if (text) {
5658
6396
  if (options.output) {
5659
- const fs13 = await import("fs/promises");
5660
- await fs13.writeFile(options.output, text, "utf8");
6397
+ const fs14 = await import("fs/promises");
6398
+ await fs14.writeFile(options.output, text, "utf8");
5661
6399
  return;
5662
6400
  }
5663
6401
  process.stdout.write(text);
@@ -5926,14 +6664,14 @@ var registerBillingCommands = (program2, deps) => {
5926
6664
  };
5927
6665
 
5928
6666
  // src/mcpCommand.ts
5929
- import fs8 from "fs/promises";
5930
- import path5 from "path";
6667
+ import fs9 from "fs/promises";
6668
+ import path6 from "path";
5931
6669
  import { randomUUID as randomUUID2 } from "crypto";
5932
6670
 
5933
6671
  // src/mcpSetupCommand.ts
5934
- import fs6 from "fs/promises";
6672
+ import fs7 from "fs/promises";
5935
6673
  import os2 from "os";
5936
- import path4 from "path";
6674
+ import path5 from "path";
5937
6675
  var MCP_SETUP_CLIENTS = ["codex", "claude", "cursor", "vscode", "generic"];
5938
6676
  var CLIENTS = new Set(MCP_SETUP_CLIENTS);
5939
6677
  var TOOLSETS = /* @__PURE__ */ new Set([
@@ -5959,7 +6697,7 @@ var normalizeMcpSetupToolset = (value) => {
5959
6697
  };
5960
6698
  var defaultConfigPath = (client) => {
5961
6699
  if (client === "claude") {
5962
- return path4.join(
6700
+ return path5.join(
5963
6701
  os2.homedir(),
5964
6702
  "Library",
5965
6703
  "Application Support",
@@ -5968,10 +6706,10 @@ var defaultConfigPath = (client) => {
5968
6706
  );
5969
6707
  }
5970
6708
  if (client === "cursor") {
5971
- return path4.join(os2.homedir(), ".cursor", "mcp.json");
6709
+ return path5.join(os2.homedir(), ".cursor", "mcp.json");
5972
6710
  }
5973
6711
  if (client === "vscode") {
5974
- return path4.join(process.cwd(), ".vscode", "mcp.json");
6712
+ return path5.join(process.cwd(), ".vscode", "mcp.json");
5975
6713
  }
5976
6714
  return void 0;
5977
6715
  };
@@ -6081,7 +6819,7 @@ var formatMcpClientSetupText = (setup, options = {}) => {
6081
6819
  };
6082
6820
  var readJsonObject = async (filePath) => {
6083
6821
  try {
6084
- const raw = await fs6.readFile(filePath, "utf8");
6822
+ const raw = await fs7.readFile(filePath, "utf8");
6085
6823
  const parsed = JSON.parse(raw);
6086
6824
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
6087
6825
  } catch (error) {
@@ -6106,8 +6844,8 @@ var writeMcpClientConfig = async (setup) => {
6106
6844
  cloudeval: nextServer
6107
6845
  }
6108
6846
  };
6109
- await fs6.mkdir(path4.dirname(setup.configPath), { recursive: true });
6110
- await fs6.writeFile(setup.configPath, `${JSON.stringify(next, null, 2)}
6847
+ await fs7.mkdir(path5.dirname(setup.configPath), { recursive: true });
6848
+ await fs7.writeFile(setup.configPath, `${JSON.stringify(next, null, 2)}
6111
6849
  `, {
6112
6850
  encoding: "utf8",
6113
6851
  mode: 384
@@ -6116,9 +6854,9 @@ var writeMcpClientConfig = async (setup) => {
6116
6854
  };
6117
6855
 
6118
6856
  // src/templateValidationClient.ts
6119
- import fs7 from "fs/promises";
6857
+ import fs8 from "fs/promises";
6120
6858
  var readJsonFile = async (filePath) => {
6121
- const text = await fs7.readFile(filePath, "utf8");
6859
+ const text = await fs8.readFile(filePath, "utf8");
6122
6860
  try {
6123
6861
  return JSON.parse(text);
6124
6862
  } catch (error) {
@@ -6169,7 +6907,7 @@ var stringField = (value, field) => {
6169
6907
  const raw = value?.[field];
6170
6908
  return typeof raw === "string" && raw.trim() ? raw : void 0;
6171
6909
  };
6172
- var extractJobId2 = (value) => {
6910
+ var extractJobId3 = (value) => {
6173
6911
  const record = recordValue(value);
6174
6912
  const job = recordValue(record?.job);
6175
6913
  const data = recordValue(record?.data);
@@ -6177,7 +6915,7 @@ var extractJobId2 = (value) => {
6177
6915
  return stringField(job, "job_id") ?? stringField(job, "jobId") ?? stringField(record, "job_id") ?? stringField(record, "jobId") ?? stringField(dataJob, "job_id") ?? stringField(dataJob, "jobId");
6178
6916
  };
6179
6917
  var normalizedStatus = (value) => String(recordValue(value)?.status ?? "").trim().toLowerCase();
6180
- var isTerminalJobStatus2 = (value) => [
6918
+ var isTerminalJobStatus3 = (value) => [
6181
6919
  "completed",
6182
6920
  "succeeded",
6183
6921
  "failed",
@@ -6326,7 +7064,7 @@ var getTemplateValidationJobResult = async (input) => fetchCloudEvalJson({
6326
7064
  query: { user_id: input.userId }
6327
7065
  });
6328
7066
  var waitForTemplateValidationResult = async (input) => {
6329
- const jobId = extractJobId2(input.submitted);
7067
+ const jobId = extractJobId3(input.submitted);
6330
7068
  if (!jobId) {
6331
7069
  return input.submitted;
6332
7070
  }
@@ -6336,7 +7074,7 @@ var waitForTemplateValidationResult = async (input) => {
6336
7074
  let status;
6337
7075
  for (; ; ) {
6338
7076
  status = await getTemplateValidationJobStatus({ ...input, jobId });
6339
- if (isTerminalJobStatus2(status)) {
7077
+ if (isTerminalJobStatus3(status)) {
6340
7078
  break;
6341
7079
  }
6342
7080
  if (Date.now() >= deadline) {
@@ -8507,12 +9245,12 @@ var pickReportDownloadPayload2 = (value, view) => {
8507
9245
  }
8508
9246
  return value;
8509
9247
  };
8510
- var extractJobId3 = (value) => {
9248
+ var extractJobId4 = (value) => {
8511
9249
  if (!value || typeof value !== "object") return void 0;
8512
9250
  const record = value;
8513
9251
  return record.job_id ?? record.id ?? record.job?.job_id ?? record.job?.id ?? record.data?.job_id ?? record.data?.job?.job_id;
8514
9252
  };
8515
- var isTerminalJobStatus3 = (value) => {
9253
+ var isTerminalJobStatus4 = (value) => {
8516
9254
  if (!value || typeof value !== "object") return true;
8517
9255
  const status = String(
8518
9256
  value.status ?? ""
@@ -8528,9 +9266,9 @@ var isTerminalJobStatus3 = (value) => {
8528
9266
  };
8529
9267
  var sleep3 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
8530
9268
  var writeHeaderFile = async (outputPath, headers) => {
8531
- await fs8.mkdir(path5.dirname(outputPath), { recursive: true });
9269
+ await fs9.mkdir(path6.dirname(outputPath), { recursive: true });
8532
9270
  const text = Object.entries(headers).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `${key}: ${value}`).join("\n");
8533
- await fs8.writeFile(outputPath, `${text}
9271
+ await fs9.writeFile(outputPath, `${text}
8534
9272
  `, "utf8");
8535
9273
  };
8536
9274
  var resolveInvocationConfig = async (serverOptions, args) => {
@@ -8587,15 +9325,8 @@ var resolveProject2 = async (config, args, auth) => {
8587
9325
  const requestedProjectId = stringValue(args.projectId) ?? config.defaultProjectId;
8588
9326
  const userId = auth.user?.id;
8589
9327
  if (!userId) {
8590
- if (requestedProjectId) {
8591
- return {
8592
- id: requestedProjectId,
8593
- name: "Selected Project",
8594
- cloud_provider: "azure"
8595
- };
8596
- }
8597
9328
  throw new Error(
8598
- "Could not determine the authenticated user. Provide projectId."
9329
+ "Could not determine the authenticated user. Run `cloudeval login` and retry."
8599
9330
  );
8600
9331
  }
8601
9332
  const projects = await auth.core.getProjects(
@@ -8604,12 +9335,15 @@ var resolveProject2 = async (config, args, auth) => {
8604
9335
  userId
8605
9336
  );
8606
9337
  if (requestedProjectId) {
8607
- return projects.find((project) => project.id === requestedProjectId) ?? {
8608
- id: requestedProjectId,
8609
- name: "Selected Project",
8610
- user_id: userId,
8611
- cloud_provider: "azure"
8612
- };
9338
+ const match = projects.find(
9339
+ (project) => project.id === requestedProjectId
9340
+ );
9341
+ if (!match) {
9342
+ throw new Error(
9343
+ `Project ${requestedProjectId} was not found for authenticated user ${userId}. Run \`cloudeval projects list\` to choose a visible project.`
9344
+ );
9345
+ }
9346
+ return match;
8613
9347
  }
8614
9348
  const selected = projects.find((project) => project.name === "Playground") ?? projects[0];
8615
9349
  if (selected) {
@@ -8693,7 +9427,7 @@ var assertModelAvailable = async (config, token) => {
8693
9427
  }
8694
9428
  };
8695
9429
  var waitForReportJobs2 = async (input) => {
8696
- const jobIds = input.submitted.map(extractJobId3).filter(Boolean);
9430
+ const jobIds = input.submitted.map(extractJobId4).filter(Boolean);
8697
9431
  if (!jobIds.length) {
8698
9432
  return input.submitted;
8699
9433
  }
@@ -8707,7 +9441,7 @@ var waitForReportJobs2 = async (input) => {
8707
9441
  userId: input.userId,
8708
9442
  jobId
8709
9443
  });
8710
- if (isTerminalJobStatus3(lastStatus)) {
9444
+ if (isTerminalJobStatus4(lastStatus)) {
8711
9445
  break;
8712
9446
  }
8713
9447
  await sleep3(input.pollIntervalMs);
@@ -8776,13 +9510,13 @@ var downloadReports = async (config, args, auth) => {
8776
9510
  const filesWritten = [];
8777
9511
  if (outputPath) {
8778
9512
  if (reportTypes.length > 1) {
8779
- const stat = await fs8.stat(outputPath).catch(() => void 0);
8780
- const outputIsDirectory = stat?.isDirectory() || !path5.extname(outputPath);
9513
+ const stat = await fs9.stat(outputPath).catch(() => void 0);
9514
+ const outputIsDirectory = stat?.isDirectory() || !path6.extname(outputPath);
8781
9515
  if (outputIsDirectory) {
8782
- await fs8.mkdir(outputPath, { recursive: true });
9516
+ await fs9.mkdir(outputPath, { recursive: true });
8783
9517
  for (const [key, value] of Object.entries(payload)) {
8784
- const file = path5.join(outputPath, `${projectId}-${key}-report.json`);
8785
- await fs8.writeFile(
9518
+ const file = path6.join(outputPath, `${projectId}-${key}-report.json`);
9519
+ await fs9.writeFile(
8786
9520
  file,
8787
9521
  `${JSON.stringify(value, null, 2)}
8788
9522
  `,
@@ -8791,8 +9525,8 @@ var downloadReports = async (config, args, auth) => {
8791
9525
  filesWritten.push(file);
8792
9526
  }
8793
9527
  } else {
8794
- await fs8.mkdir(path5.dirname(outputPath), { recursive: true });
8795
- await fs8.writeFile(
9528
+ await fs9.mkdir(path6.dirname(outputPath), { recursive: true });
9529
+ await fs9.writeFile(
8796
9530
  outputPath,
8797
9531
  `${JSON.stringify(data, null, 2)}
8798
9532
  `,
@@ -8801,8 +9535,8 @@ var downloadReports = async (config, args, auth) => {
8801
9535
  filesWritten.push(outputPath);
8802
9536
  }
8803
9537
  } else {
8804
- await fs8.mkdir(path5.dirname(outputPath), { recursive: true });
8805
- await fs8.writeFile(
9538
+ await fs9.mkdir(path6.dirname(outputPath), { recursive: true });
9539
+ await fs9.writeFile(
8806
9540
  outputPath,
8807
9541
  `${JSON.stringify(data, null, 2)}
8808
9542
  `,
@@ -9112,7 +9846,7 @@ var buildToolHandlers = (serverOptions) => {
9112
9846
  if (!rawOutputPath) {
9113
9847
  throw new Error("outputPath is required.");
9114
9848
  }
9115
- const outputPath = path5.resolve(rawOutputPath);
9849
+ const outputPath = path6.resolve(rawOutputPath);
9116
9850
  const publicGraph = booleanValue(args.public) ?? false;
9117
9851
  const auth = publicGraph ? void 0 : await resolveAuth(config, { requireUser: true });
9118
9852
  const layout = normalizeProjectDiagramImageLayout(stringValue(args.layout));
@@ -9133,11 +9867,11 @@ var buildToolHandlers = (serverOptions) => {
9133
9867
  publicGraph,
9134
9868
  syncVersion: stringValue(args.syncVersion)
9135
9869
  });
9136
- await fs8.mkdir(path5.dirname(outputPath), { recursive: true });
9137
- await fs8.writeFile(outputPath, result.bytes);
9870
+ await fs9.mkdir(path6.dirname(outputPath), { recursive: true });
9871
+ await fs9.writeFile(outputPath, result.bytes);
9138
9872
  const filesWritten = [outputPath];
9139
9873
  const rawHeadersOutputPath = stringValue(args.headersOutputPath);
9140
- const headersOutputPath = rawHeadersOutputPath ? path5.resolve(rawHeadersOutputPath) : void 0;
9874
+ const headersOutputPath = rawHeadersOutputPath ? path6.resolve(rawHeadersOutputPath) : void 0;
9141
9875
  if (headersOutputPath) {
9142
9876
  await writeHeaderFile(headersOutputPath, result.headers);
9143
9877
  filesWritten.push(headersOutputPath);
@@ -10009,7 +10743,7 @@ var buildToolHandlers = (serverOptions) => {
10009
10743
  projectId,
10010
10744
  type,
10011
10745
  submitted,
10012
- jobs: submitted.map(extractJobId3).filter(Boolean),
10746
+ jobs: submitted.map(extractJobId4).filter(Boolean),
10013
10747
  finalStatuses
10014
10748
  },
10015
10749
  frontendUrl: reportsFrontendUrl(config, { projectId, type })
@@ -10862,7 +11596,7 @@ var registerMcpCommand = (program2, deps) => {
10862
11596
  note
10863
11597
  });
10864
11598
  if (options.output) {
10865
- await fs8.writeFile(options.output, text, "utf8");
11599
+ await fs9.writeFile(options.output, text, "utf8");
10866
11600
  } else {
10867
11601
  process.stdout.write(text);
10868
11602
  }
@@ -11054,23 +11788,23 @@ Discovery:
11054
11788
  };
11055
11789
 
11056
11790
  // src/credentialsCommand.ts
11057
- var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
11791
+ var asRecord2 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
11058
11792
  var arrayFromPayload = (payload, key) => {
11059
- const record = asRecord(payload);
11793
+ const record = asRecord2(payload);
11060
11794
  const value = record[key] ?? record.data;
11061
11795
  return Array.isArray(value) ? value.filter((item) => item && typeof item === "object") : [];
11062
11796
  };
11063
11797
  var credentialFromPayload = (payload) => {
11064
- const record = asRecord(payload);
11065
- return asRecord(record.credential ?? record.data ?? record);
11798
+ const record = asRecord2(payload);
11799
+ return asRecord2(record.credential ?? record.data ?? record);
11066
11800
  };
11067
11801
  var secretFromPayload = (payload) => {
11068
- const record = asRecord(payload);
11802
+ const record = asRecord2(payload);
11069
11803
  const value = record.access_key ?? record.accessKey ?? record.secret;
11070
11804
  return typeof value === "string" ? value : void 0;
11071
11805
  };
11072
11806
  var projectIdFromPayload = (payload, fallback) => {
11073
- const record = asRecord(payload);
11807
+ const record = asRecord2(payload);
11074
11808
  const credential = credentialFromPayload(payload);
11075
11809
  const value = record.project_id ?? record.projectId ?? credential.project_id ?? credential.projectId ?? (Array.isArray(credential.project_ids) ? credential.project_ids[0] : void 0) ?? fallback;
11076
11810
  return typeof value === "string" ? value : void 0;
@@ -11104,7 +11838,7 @@ var writeCredentialOutput = async (input) => {
11104
11838
  return;
11105
11839
  }
11106
11840
  if (input.format === "text" || !input.format) {
11107
- const record = asRecord(input.data);
11841
+ const record = asRecord2(input.data);
11108
11842
  const credentials = arrayFromPayload(input.data, "credentials").length > 0 ? arrayFromPayload(input.data, "credentials") : record.credential ? [credentialFromPayload(input.data)] : arrayFromPayload(input.data, "templates");
11109
11843
  if (credentials.length) {
11110
11844
  process.stdout.write(formatTextTable(formatCredentialTextRows(credentials)));
@@ -11228,9 +11962,9 @@ import { randomUUID as randomUUID4 } from "crypto";
11228
11962
  // src/localHooks.ts
11229
11963
  import { exec } from "child_process";
11230
11964
  import { randomUUID as randomUUID3 } from "crypto";
11231
- import fs9 from "fs/promises";
11965
+ import fs10 from "fs/promises";
11232
11966
  import os4 from "os";
11233
- import path6 from "path";
11967
+ import path7 from "path";
11234
11968
  var normalizeHooks = (config, event) => {
11235
11969
  if (config.hooks?.enabled !== true) {
11236
11970
  return [];
@@ -11241,11 +11975,11 @@ var normalizeHooks = (config, event) => {
11241
11975
  ) : [];
11242
11976
  };
11243
11977
  var writeHookPayload = async (input, hook) => {
11244
- const filePath = path6.join(
11978
+ const filePath = path7.join(
11245
11979
  os4.tmpdir(),
11246
11980
  `cloudeval-hook-${process.pid}-${randomUUID3()}.json`
11247
11981
  );
11248
- await fs9.writeFile(
11982
+ await fs10.writeFile(
11249
11983
  filePath,
11250
11984
  JSON.stringify(
11251
11985
  {
@@ -11321,7 +12055,7 @@ var runLocalHooks = async (input) => {
11321
12055
  throw error;
11322
12056
  }
11323
12057
  } finally {
11324
- await fs9.rm(payloadPath, { force: true }).catch(() => void 0);
12058
+ await fs10.rm(payloadPath, { force: true }).catch(() => void 0);
11325
12059
  }
11326
12060
  }
11327
12061
  return warnings;
@@ -11633,7 +12367,7 @@ var registerAgentsCommand = (program2, deps) => {
11633
12367
 
11634
12368
  // src/validateCommand.ts
11635
12369
  var addCommon5 = (command, deps) => addAuthOptions(command, deps.defaultBaseUrl).requiredOption("--template-file <path>", "Cloud template JSON file").option("--parameters-file <path>", "Optional parameters JSON file").option("--format <format>", "Output format: text, json, ndjson, markdown", "text").option("--output <file>", "Output file");
11636
- var parsePositiveInteger = (value, optionName = "--max-results") => {
12370
+ var parsePositiveInteger2 = (value, optionName = "--max-results") => {
11637
12371
  if (!value) {
11638
12372
  return void 0;
11639
12373
  }
@@ -11667,7 +12401,7 @@ var registerValidateCommand = (program2, deps) => {
11667
12401
  category: options.category,
11668
12402
  pillar: options.pillar,
11669
12403
  minSeverity: options.minSeverity,
11670
- maxResults: parsePositiveInteger(options.maxResults),
12404
+ maxResults: parsePositiveInteger2(options.maxResults),
11671
12405
  projectId: options.project,
11672
12406
  saveReport: options.saveReport
11673
12407
  });
@@ -11676,11 +12410,11 @@ var registerValidateCommand = (program2, deps) => {
11676
12410
  authToken: context.token,
11677
12411
  userId: context.user.id,
11678
12412
  submitted,
11679
- pollIntervalMs: parsePositiveInteger(
12413
+ pollIntervalMs: parsePositiveInteger2(
11680
12414
  options.pollInterval,
11681
12415
  "--poll-interval"
11682
12416
  ),
11683
- waitTimeoutMs: parsePositiveInteger(
12417
+ waitTimeoutMs: parsePositiveInteger2(
11684
12418
  options.waitTimeout,
11685
12419
  "--wait-timeout"
11686
12420
  )
@@ -11740,11 +12474,11 @@ var registerValidateCommand = (program2, deps) => {
11740
12474
  authToken: context.token,
11741
12475
  userId: context.user.id,
11742
12476
  submitted,
11743
- pollIntervalMs: parsePositiveInteger(
12477
+ pollIntervalMs: parsePositiveInteger2(
11744
12478
  options.pollInterval,
11745
12479
  "--poll-interval"
11746
12480
  ),
11747
- waitTimeoutMs: parsePositiveInteger(
12481
+ waitTimeoutMs: parsePositiveInteger2(
11748
12482
  options.waitTimeout,
11749
12483
  "--wait-timeout"
11750
12484
  )
@@ -11865,10 +12599,10 @@ var registerConfigCommand = (program2) => {
11865
12599
  const profile = resolveProfile(options, command);
11866
12600
  const current = await loadCliConfig(profile);
11867
12601
  const next = writeCliConfigValue(current, key, value);
11868
- const path10 = await saveCliConfig(next, profile);
12602
+ const path11 = await saveCliConfig(next, profile);
11869
12603
  await writeFormattedOutput({
11870
12604
  command: "config set",
11871
- data: { profile, path: path10, config: next },
12605
+ data: { profile, path: path11, config: next },
11872
12606
  format: options.format,
11873
12607
  output: options.output
11874
12608
  });
@@ -11879,10 +12613,10 @@ var registerConfigCommand = (program2) => {
11879
12613
  const profile = resolveProfile(options, command);
11880
12614
  const current = await loadCliConfig(profile);
11881
12615
  const next = unsetCliConfigValue(current, key);
11882
- const path10 = await saveCliConfig(next, profile);
12616
+ const path11 = await saveCliConfig(next, profile);
11883
12617
  await writeFormattedOutput({
11884
12618
  command: "config unset",
11885
- data: { profile, path: path10, config: next },
12619
+ data: { profile, path: path11, config: next },
11886
12620
  format: options.format,
11887
12621
  output: options.output
11888
12622
  });
@@ -12179,8 +12913,8 @@ var writeModelsListOutput = async (input) => {
12179
12913
  defaultModel: input.defaultModel
12180
12914
  });
12181
12915
  if (input.options.output) {
12182
- const fs13 = await import("fs/promises");
12183
- await fs13.writeFile(input.options.output, text, "utf8");
12916
+ const fs14 = await import("fs/promises");
12917
+ await fs14.writeFile(input.options.output, text, "utf8");
12184
12918
  return;
12185
12919
  }
12186
12920
  process.stdout.write(text);
@@ -12246,10 +12980,10 @@ var registerModelsCommand = (program2, deps) => {
12246
12980
  const profile = options.profile || getActiveConfigProfile(command);
12247
12981
  const config = await loadCliConfig(profile);
12248
12982
  const next = { ...config, model };
12249
- const path10 = await saveCliConfig(next, profile);
12983
+ const path11 = await saveCliConfig(next, profile);
12250
12984
  await writeFormattedOutput({
12251
12985
  command: "models default set",
12252
- data: { profile, path: path10, model },
12986
+ data: { profile, path: path11, model },
12253
12987
  format: options.format,
12254
12988
  output: options.output
12255
12989
  });
@@ -12292,8 +13026,8 @@ var writeSessionTableOutput = async (command, data, options) => {
12292
13026
  if (format === "text") {
12293
13027
  const text = renderSessionsTable(data);
12294
13028
  if (options.output) {
12295
- const fs13 = await import("fs/promises");
12296
- await fs13.writeFile(options.output, text, "utf8");
13029
+ const fs14 = await import("fs/promises");
13030
+ await fs14.writeFile(options.output, text, "utf8");
12297
13031
  return;
12298
13032
  }
12299
13033
  process.stdout.write(text);
@@ -12465,12 +13199,12 @@ var registerSetupCommand = (program2, defaultBaseUrl = CLOUD_BASE_URL) => {
12465
13199
  rl.close();
12466
13200
  }
12467
13201
  }
12468
- const path10 = await saveCliConfig(next, profile);
13202
+ const path11 = await saveCliConfig(next, profile);
12469
13203
  await writeFormattedOutput({
12470
13204
  command: "setup",
12471
13205
  data: {
12472
13206
  profile,
12473
- path: path10,
13207
+ path: path11,
12474
13208
  config: next,
12475
13209
  nextSteps: [
12476
13210
  "Run `cloudeval auth status` to inspect authentication.",
@@ -12485,9 +13219,9 @@ var registerSetupCommand = (program2, defaultBaseUrl = CLOUD_BASE_URL) => {
12485
13219
  };
12486
13220
 
12487
13221
  // src/updateCommand.ts
12488
- import { spawn as spawn2 } from "child_process";
12489
- import fs10 from "fs/promises";
12490
- import path7 from "path";
13222
+ import { spawn as spawn3 } from "child_process";
13223
+ import fs11 from "fs/promises";
13224
+ import path8 from "path";
12491
13225
  import { createInterface as createInterface2 } from "readline/promises";
12492
13226
  var DEFAULT_LATEST_RELEASE_URL = "https://api.github.com/repos/ganakailabs/cloudeval-cli/releases/latest";
12493
13227
  var DEFAULT_INSTALLER_URL = "https://cli.cloudeval.ai/install.sh";
@@ -12604,7 +13338,7 @@ var runInstaller = async ({
12604
13338
  installerUrl,
12605
13339
  targetTag,
12606
13340
  fetchImpl = fetch,
12607
- spawnImpl = spawn2,
13341
+ spawnImpl = spawn3,
12608
13342
  output = process.stderr,
12609
13343
  platform = process.platform,
12610
13344
  env = process.env,
@@ -12626,12 +13360,12 @@ var runInstaller = async ({
12626
13360
  const installerScript = await response.text();
12627
13361
  if (usePowerShellInstaller) {
12628
13362
  const configDir = getCloudevalConfigDir();
12629
- await fs10.mkdir(configDir, { recursive: true, mode: 448 });
12630
- const scriptPath = path7.join(
12631
- await fs10.mkdtemp(path7.join(configDir, "installer-")),
13363
+ await fs11.mkdir(configDir, { recursive: true, mode: 448 });
13364
+ const scriptPath = path8.join(
13365
+ await fs11.mkdtemp(path8.join(configDir, "installer-")),
12632
13366
  "install.ps1"
12633
13367
  );
12634
- await fs10.writeFile(scriptPath, installerScript, "utf8");
13368
+ await fs11.writeFile(scriptPath, installerScript, "utf8");
12635
13369
  const child2 = spawnImpl(
12636
13370
  "pwsh",
12637
13371
  [
@@ -12659,7 +13393,7 @@ var runInstaller = async ({
12659
13393
  );
12660
13394
  });
12661
13395
  child2.once("close", (code) => {
12662
- void fs10.rm(path7.dirname(scriptPath), { recursive: true, force: true });
13396
+ void fs11.rm(path8.dirname(scriptPath), { recursive: true, force: true });
12663
13397
  if (code === 0) {
12664
13398
  resolve();
12665
13399
  return;
@@ -12699,10 +13433,10 @@ var runInstaller = async ({
12699
13433
  child.stdin.end(installerScript);
12700
13434
  });
12701
13435
  };
12702
- var getUpdateCachePath = () => path7.join(getCloudevalConfigDir(), UPDATE_CACHE_FILE);
13436
+ var getUpdateCachePath = () => path8.join(getCloudevalConfigDir(), UPDATE_CACHE_FILE);
12703
13437
  var readCache = async (cachePath) => {
12704
13438
  try {
12705
- const parsed = JSON.parse(await fs10.readFile(cachePath, "utf8"));
13439
+ const parsed = JSON.parse(await fs11.readFile(cachePath, "utf8"));
12706
13440
  if (parsed && typeof parsed === "object" && typeof parsed.checkedAt === "string" && typeof parsed.latestVersion === "string" && typeof parsed.latestTag === "string") {
12707
13441
  return parsed;
12708
13442
  }
@@ -12714,8 +13448,8 @@ var readCache = async (cachePath) => {
12714
13448
  return void 0;
12715
13449
  };
12716
13450
  var writeCache = async (cachePath, status) => {
12717
- await fs10.mkdir(path7.dirname(cachePath), { recursive: true, mode: 448 });
12718
- await fs10.writeFile(
13451
+ await fs11.mkdir(path8.dirname(cachePath), { recursive: true, mode: 448 });
13452
+ await fs11.writeFile(
12719
13453
  cachePath,
12720
13454
  `${JSON.stringify(
12721
13455
  {
@@ -12927,7 +13661,7 @@ var registerUpdateCommand = (program2, registerOptions = {}) => {
12927
13661
  if (options.format === "text" || !options.format) {
12928
13662
  const text = formatUpdateStatusText(result);
12929
13663
  if (options.output) {
12930
- await fs10.writeFile(options.output, text, "utf8");
13664
+ await fs11.writeFile(options.output, text, "utf8");
12931
13665
  return;
12932
13666
  }
12933
13667
  process.stdout.write(text);
@@ -12943,13 +13677,13 @@ var registerUpdateCommand = (program2, registerOptions = {}) => {
12943
13677
  };
12944
13678
 
12945
13679
  // src/uninstallCommand.ts
12946
- import fs11 from "fs/promises";
13680
+ import fs12 from "fs/promises";
12947
13681
  import os5 from "os";
12948
- import path8 from "path";
13682
+ import path9 from "path";
12949
13683
  import { createInterface as createInterface3 } from "readline/promises";
12950
13684
  var pathExists = async (candidate) => {
12951
13685
  try {
12952
- await fs11.lstat(candidate);
13686
+ await fs12.lstat(candidate);
12953
13687
  return true;
12954
13688
  } catch (error) {
12955
13689
  if (error?.code === "ENOENT") {
@@ -12958,12 +13692,12 @@ var pathExists = async (candidate) => {
12958
13692
  throw error;
12959
13693
  }
12960
13694
  };
12961
- var installerBinDir = (home) => path8.join(home, ".local", "bin");
13695
+ var installerBinDir = (home) => path9.join(home, ".local", "bin");
12962
13696
  var completionPaths = (home) => [
12963
- path8.join(home, ".local", "share", "bash-completion", "completions", "cloudeval"),
12964
- path8.join(home, ".zsh", "completions", "_cloudeval"),
12965
- path8.join(home, ".config", "fish", "completions", "cloudeval.fish"),
12966
- path8.join(home, ".config", "powershell", "cloudeval-completion.ps1")
13697
+ path9.join(home, ".local", "share", "bash-completion", "completions", "cloudeval"),
13698
+ path9.join(home, ".zsh", "completions", "_cloudeval"),
13699
+ path9.join(home, ".config", "fish", "completions", "cloudeval.fish"),
13700
+ path9.join(home, ".config", "powershell", "cloudeval-completion.ps1")
12967
13701
  ];
12968
13702
  var installerArtifactTargets = (home, platform) => {
12969
13703
  const binDir = installerBinDir(home);
@@ -12971,37 +13705,37 @@ var installerArtifactTargets = (home, platform) => {
12971
13705
  const targets = [
12972
13706
  {
12973
13707
  label: "cloudeval binary",
12974
- path: path8.join(binDir, executableName),
13708
+ path: path9.join(binDir, executableName),
12975
13709
  kind: "file",
12976
13710
  status: "missing"
12977
13711
  },
12978
13712
  {
12979
13713
  label: "cloudeval binary",
12980
- path: path8.join(binDir, "cloudeval"),
13714
+ path: path9.join(binDir, "cloudeval"),
12981
13715
  kind: "file",
12982
13716
  status: "missing"
12983
13717
  },
12984
13718
  {
12985
13719
  label: "eva alias",
12986
- path: path8.join(binDir, "eva"),
13720
+ path: path9.join(binDir, "eva"),
12987
13721
  kind: "file",
12988
13722
  status: "missing"
12989
13723
  },
12990
13724
  {
12991
13725
  label: "cloud alias",
12992
- path: path8.join(binDir, "cloud"),
13726
+ path: path9.join(binDir, "cloud"),
12993
13727
  kind: "file",
12994
13728
  status: "missing"
12995
13729
  },
12996
13730
  {
12997
13731
  label: "Ink runtime asset",
12998
- path: path8.join(binDir, "yoga.wasm"),
13732
+ path: path9.join(binDir, "yoga.wasm"),
12999
13733
  kind: "file",
13000
13734
  status: "missing"
13001
13735
  },
13002
13736
  {
13003
13737
  label: "license notices",
13004
- path: path8.join(home, ".local", "share", "cloudeval", "licenses"),
13738
+ path: path9.join(home, ".local", "share", "cloudeval", "licenses"),
13005
13739
  kind: "directory",
13006
13740
  status: "missing"
13007
13741
  },
@@ -13017,11 +13751,11 @@ var installerArtifactTargets = (home, platform) => {
13017
13751
  );
13018
13752
  };
13019
13753
  var shellProfilePaths = (home) => [
13020
- path8.join(home, ".bashrc"),
13021
- path8.join(home, ".bash_profile"),
13022
- path8.join(home, ".zshrc"),
13023
- path8.join(home, ".profile"),
13024
- path8.join(home, ".config", "fish", "config.fish")
13754
+ path9.join(home, ".bashrc"),
13755
+ path9.join(home, ".bash_profile"),
13756
+ path9.join(home, ".zshrc"),
13757
+ path9.join(home, ".profile"),
13758
+ path9.join(home, ".config", "fish", "config.fish")
13025
13759
  ];
13026
13760
  var removeInstallerPathSnippet = (content, binDir) => {
13027
13761
  const exportLine = `export PATH="${binDir}:$PATH"`;
@@ -13048,7 +13782,7 @@ var removeTarget = async (target, dryRun) => {
13048
13782
  if (dryRun) {
13049
13783
  return { ...target, status: "would_remove" };
13050
13784
  }
13051
- await fs11.rm(target.path, { recursive: target.kind === "directory", force: true });
13785
+ await fs12.rm(target.path, { recursive: target.kind === "directory", force: true });
13052
13786
  return { ...target, status: "removed" };
13053
13787
  };
13054
13788
  var updateShellProfile = async (profilePath, home, dryRun) => {
@@ -13060,7 +13794,7 @@ var updateShellProfile = async (profilePath, home, dryRun) => {
13060
13794
  status: "missing"
13061
13795
  };
13062
13796
  }
13063
- const content = await fs11.readFile(profilePath, "utf8");
13797
+ const content = await fs12.readFile(profilePath, "utf8");
13064
13798
  const updated = removeInstallerPathSnippet(content, installerBinDir(home));
13065
13799
  if (updated === void 0) {
13066
13800
  return {
@@ -13078,7 +13812,7 @@ var updateShellProfile = async (profilePath, home, dryRun) => {
13078
13812
  status: "would_update"
13079
13813
  };
13080
13814
  }
13081
- await fs11.writeFile(profilePath, updated, "utf8");
13815
+ await fs12.writeFile(profilePath, updated, "utf8");
13082
13816
  return {
13083
13817
  label: "shell profile PATH entry",
13084
13818
  path: profilePath,
@@ -13131,7 +13865,7 @@ var handleUninstallCommand = async (options, deps = {}) => {
13131
13865
  }
13132
13866
  const configTarget = {
13133
13867
  label: "config",
13134
- path: path8.join(home, ".config", "cloudeval"),
13868
+ path: path9.join(home, ".config", "cloudeval"),
13135
13869
  kind: "directory",
13136
13870
  status: "kept"
13137
13871
  };
@@ -13193,7 +13927,7 @@ var registerUninstallCommand = (program2) => {
13193
13927
  if (options.format === "text" || !options.format) {
13194
13928
  const text = formatUninstallResultText(result);
13195
13929
  if (options.output) {
13196
- await fs11.writeFile(options.output, text, "utf8");
13930
+ await fs12.writeFile(options.output, text, "utf8");
13197
13931
  return;
13198
13932
  }
13199
13933
  process.stdout.write(text);
@@ -13314,6 +14048,7 @@ var resolveLoginOnboardingMode = (options) => {
13314
14048
  import { jsx } from "react/jsx-runtime";
13315
14049
  var DEFAULT_BASE_URL = getDefaultBaseUrl();
13316
14050
  var ASK_STREAM_IDLE_TIMEOUT_MS2 = 9e4;
14051
+ var AGENT_STREAM_IDLE_TIMEOUT_MS = 18e4;
13317
14052
  var LEGACY_API_KEY_MESSAGE = "API key auth was renamed in beta. Use --access-key or CLOUDEVAL_ACCESS_KEY.";
13318
14053
  var STREAM_OUTPUT_NODES3 = /* @__PURE__ */ new Set([
13319
14054
  "generate_response",
@@ -13339,21 +14074,21 @@ var completionScriptPath = (shell) => {
13339
14074
  const home = os6.homedir();
13340
14075
  switch (shell) {
13341
14076
  case "bash":
13342
- return path9.join(home, ".local", "share", "bash-completion", "completions", "cloudeval");
14077
+ return path10.join(home, ".local", "share", "bash-completion", "completions", "cloudeval");
13343
14078
  case "zsh":
13344
- return path9.join(home, ".zsh", "completions", "_cloudeval");
14079
+ return path10.join(home, ".zsh", "completions", "_cloudeval");
13345
14080
  case "fish":
13346
- return path9.join(home, ".config", "fish", "completions", "cloudeval.fish");
14081
+ return path10.join(home, ".config", "fish", "completions", "cloudeval.fish");
13347
14082
  case "powershell":
13348
- return path9.join(home, ".config", "powershell", "cloudeval-completion.ps1");
14083
+ return path10.join(home, ".config", "powershell", "cloudeval-completion.ps1");
13349
14084
  }
13350
14085
  };
13351
14086
  var ZSH_FPATH_MARKER = "CloudEval CLI completions";
13352
14087
  var ensureZshCompletionFpath = async () => {
13353
- const zshrc = path9.join(os6.homedir(), ".zshrc");
14088
+ const zshrc = path10.join(os6.homedir(), ".zshrc");
13354
14089
  let existing = "";
13355
14090
  try {
13356
- existing = await fs12.readFile(zshrc, "utf8");
14091
+ existing = await fs13.readFile(zshrc, "utf8");
13357
14092
  } catch {
13358
14093
  existing = "";
13359
14094
  }
@@ -13364,12 +14099,12 @@ var ensureZshCompletionFpath = async () => {
13364
14099
  # ${ZSH_FPATH_MARKER}
13365
14100
  fpath=("$HOME/.zsh/completions" $fpath)
13366
14101
  `;
13367
- await fs12.appendFile(zshrc, snippet, "utf8");
14102
+ await fs13.appendFile(zshrc, snippet, "utf8");
13368
14103
  };
13369
14104
  var installCompletionScript = async (shell, binaryName) => {
13370
14105
  const scriptPath = completionScriptPath(shell);
13371
- await fs12.mkdir(path9.dirname(scriptPath), { recursive: true });
13372
- await fs12.writeFile(scriptPath, buildCompletionScript(shell, binaryName), "utf8");
14106
+ await fs13.mkdir(path10.dirname(scriptPath), { recursive: true });
14107
+ await fs13.writeFile(scriptPath, buildCompletionScript(shell, binaryName), "utf8");
13373
14108
  if (shell === "zsh") {
13374
14109
  await ensureZshCompletionFpath();
13375
14110
  }
@@ -13377,7 +14112,7 @@ var installCompletionScript = async (shell, binaryName) => {
13377
14112
  };
13378
14113
  var uninstallCompletionScript = async (shell) => {
13379
14114
  const scriptPath = completionScriptPath(shell);
13380
- await fs12.rm(scriptPath, { force: true });
14115
+ await fs13.rm(scriptPath, { force: true });
13381
14116
  return scriptPath;
13382
14117
  };
13383
14118
  var runInteractiveLoginOnboarding = async (baseUrl, token) => {
@@ -13946,6 +14681,12 @@ registerReportsCommand(program, {
13946
14681
  resolveBaseUrl,
13947
14682
  readStdinValue
13948
14683
  });
14684
+ registerReviewCommand(program, {
14685
+ defaultBaseUrl: DEFAULT_BASE_URL,
14686
+ resolveBaseUrl,
14687
+ readStdinValue,
14688
+ isHeadlessEnvironment
14689
+ });
13949
14690
  registerRecipesCommand(program, {
13950
14691
  defaultBaseUrl: DEFAULT_BASE_URL,
13951
14692
  resolveBaseUrl,
@@ -14102,7 +14843,7 @@ program.command("tui").description("Open the CloudEval Terminal UI").option(
14102
14843
  const { assertSecureBaseUrl } = await import("./dist-CFLR5FML.js");
14103
14844
  const [{ render }, { App }] = await Promise.all([
14104
14845
  import("ink"),
14105
- import("./App-NGGAERHH.js")
14846
+ import("./App-XNVJF2ON.js")
14106
14847
  ]);
14107
14848
  const baseUrl = await resolveBaseUrl(options, command);
14108
14849
  assertSecureBaseUrl(baseUrl);
@@ -14160,7 +14901,7 @@ program.command("chat").description("Start an interactive chat session").option(
14160
14901
  const { assertSecureBaseUrl } = await import("./dist-CFLR5FML.js");
14161
14902
  const [{ render }, { App }] = await Promise.all([
14162
14903
  import("ink"),
14163
- import("./App-NGGAERHH.js")
14904
+ import("./App-XNVJF2ON.js")
14164
14905
  ]);
14165
14906
  const baseUrl = await resolveBaseUrl(options, command);
14166
14907
  assertSecureBaseUrl(baseUrl);
@@ -14273,7 +15014,7 @@ program.command("ask").alias("agent").description("Ask a single question or run
14273
15014
  });
14274
15015
  }
14275
15016
  try {
14276
- const fs13 = await import("fs");
15017
+ const fs14 = await import("fs");
14277
15018
  const fsPromises = await import("fs/promises");
14278
15019
  const { randomUUID: randomUUID5 } = await import("crypto");
14279
15020
  const core = await import("./dist-CFLR5FML.js");
@@ -14281,8 +15022,6 @@ program.command("ask").alias("agent").description("Ask a single question or run
14281
15022
  streamChat,
14282
15023
  reduceChunk,
14283
15024
  getAuthToken,
14284
- getProjects,
14285
- ensurePlaygroundProject,
14286
15025
  checkUserStatus,
14287
15026
  extractEmailFromToken,
14288
15027
  initialChatState,
@@ -14381,68 +15120,39 @@ program.command("ask").alias("agent").description("Ask a single question or run
14381
15120
  step: "project",
14382
15121
  message: selectedProjectId ? `Using project ${selectedProjectId}` : "Resolving project"
14383
15122
  });
14384
- let project;
14385
15123
  let authenticatedUserId;
14386
- if (selectedProjectId) {
14387
- verboseLog("Using provided project ID:", selectedProjectId);
14388
- try {
14389
- const userStatus = await checkUserStatus(baseUrl, token);
14390
- getActiveCliTelemetry()?.setUserProperties(userStatus.user || {});
14391
- authenticatedUserId = userStatus.user?.id;
14392
- } catch {
14393
- }
14394
- project = {
14395
- id: selectedProjectId,
14396
- name: "Selected Project",
14397
- user_id: authenticatedUserId,
14398
- cloud_provider: "azure"
14399
- };
14400
- } else {
14401
- verboseLog("No project ID provided, attempting to fetch user projects");
14402
- try {
14403
- verboseLog("Checking user status", { baseUrl });
14404
- const userStatus = await checkUserStatus(baseUrl, token);
14405
- getActiveCliTelemetry()?.setUserProperties(userStatus.user || {});
14406
- authenticatedUserId = userStatus.user?.id;
14407
- verboseLog("User status:", {
14408
- hasUser: !!userStatus.user,
14409
- userId: userStatus.user?.id,
14410
- onboardingCompleted: userStatus.onboardingCompleted
14411
- });
14412
- if (authenticatedUserId) {
14413
- verboseLog("Fetching projects for user", { userId: authenticatedUserId });
14414
- const projects = await getProjects(baseUrl, token, authenticatedUserId);
14415
- verboseLog("Projects fetched:", { count: projects.length, names: projects.map((p) => p.name) });
14416
- const playgroundProject = projects.find((p) => p.name === "Playground");
14417
- if (playgroundProject) {
14418
- project = playgroundProject;
14419
- } else if (userStatus.user?.email) {
14420
- verboseLog("Playground project missing; running shared onboarding repair");
14421
- project = await ensurePlaygroundProject(baseUrl, token, {
14422
- id: authenticatedUserId,
14423
- email: userStatus.user.email,
14424
- full_name: userStatus.user.full_name,
14425
- name: userStatus.user.name
14426
- });
14427
- } else {
14428
- project = projects[0] || void 0;
14429
- }
14430
- verboseLog("Selected project:", project ? { id: project.id, name: project.name } : "none");
14431
- }
14432
- } catch (error) {
14433
- verboseLog("Failed to fetch projects, using default:", {
14434
- message: error.message,
14435
- stack: error.stack
14436
- });
14437
- }
14438
- if (!project) {
14439
- process.stderr.write(
14440
- "No project is available for this account. Run `cloudeval chat` to complete onboarding, then retry."
14441
- );
14442
- process.stderr.write("\n");
14443
- await exitCli(1, new Error("no_project_available"));
14444
- }
15124
+ let authenticatedUser;
15125
+ try {
15126
+ const userStatus = await checkUserStatus(baseUrl, token);
15127
+ getActiveCliTelemetry()?.setUserProperties(userStatus.user || {});
15128
+ authenticatedUserId = userStatus.user?.id;
15129
+ authenticatedUser = userStatus.user;
15130
+ verboseLog("User status:", {
15131
+ hasUser: !!userStatus.user,
15132
+ userId: userStatus.user?.id,
15133
+ onboardingCompleted: userStatus.onboardingCompleted
15134
+ });
15135
+ } catch (error) {
15136
+ verboseLog("Failed to check user status before project resolve:", {
15137
+ message: error.message
15138
+ });
15139
+ }
15140
+ const { resolveAskProject } = await import("./resolveAskProject-NK435I56.js");
15141
+ let project;
15142
+ try {
15143
+ project = await resolveAskProject({
15144
+ baseUrl,
15145
+ token,
15146
+ selectedProjectId,
15147
+ authenticatedUserId,
15148
+ authenticatedUser
15149
+ });
15150
+ } catch (error) {
15151
+ progressWriter.clear();
15152
+ console.error(error?.message ?? "Failed to resolve project");
15153
+ await exitCli(1, error);
14445
15154
  }
15155
+ verboseLog("Selected project:", { id: project.id, name: project.name });
14446
15156
  let userName = "You";
14447
15157
  try {
14448
15158
  const email = extractEmailFromToken(token);
@@ -14469,11 +15179,11 @@ program.command("ask").alias("agent").description("Ask a single question or run
14469
15179
  console.error(`[${commandName}] Thread ID: ${threadId}`);
14470
15180
  }
14471
15181
  if (streamTextOutput && options.output) {
14472
- fileOutputStream = fs13.createWriteStream(options.output, { encoding: "utf-8" });
15182
+ fileOutputStream = fs14.createWriteStream(options.output, { encoding: "utf-8" });
14473
15183
  outputStream = fileOutputStream;
14474
15184
  }
14475
15185
  if (ndjsonOutput && options.output) {
14476
- ndjsonOutputStream = fs13.createWriteStream(options.output, { encoding: "utf-8" });
15186
+ ndjsonOutputStream = fs14.createWriteStream(options.output, { encoding: "utf-8" });
14477
15187
  }
14478
15188
  const writeAskDataEvent = (event) => {
14479
15189
  const line = `${JSON.stringify(event)}
@@ -14575,7 +15285,7 @@ program.command("ask").alias("agent").description("Ask a single question or run
14575
15285
  debug: options.debug,
14576
15286
  completeAfterResponse: true,
14577
15287
  responseCompletionGraceMs: 5e3,
14578
- streamIdleTimeoutMs: ASK_STREAM_IDLE_TIMEOUT_MS2,
15288
+ streamIdleTimeoutMs: selectedMode === "agent" ? AGENT_STREAM_IDLE_TIMEOUT_MS : ASK_STREAM_IDLE_TIMEOUT_MS2,
14579
15289
  hitlResume
14580
15290
  })) {
14581
15291
  totalChunkCount++;
@@ -14754,7 +15464,24 @@ Error: ${errorMsg}
14754
15464
  throw error;
14755
15465
  }
14756
15466
  const finalMessage = [...chatState.messages].reverse().find((m) => m.role === "assistant");
14757
- const finalResponse = collapseRepeatedAssistantText3(finalMessage?.content || responseText || "");
15467
+ let finalResponse = collapseRepeatedAssistantText3(
15468
+ finalMessage?.content || responseText || ""
15469
+ );
15470
+ if (!finalResponse.trim() && chatState.threadId) {
15471
+ const { fetchLastAssistantContent } = await import("./fetchLastAssistantContent-RH6RMSQO.js");
15472
+ const persisted = await fetchLastAssistantContent({
15473
+ baseUrl,
15474
+ authToken: token,
15475
+ threadId: chatState.threadId,
15476
+ normalizeApiBase: normalizeApiBase2
15477
+ });
15478
+ if (persisted) {
15479
+ finalResponse = collapseRepeatedAssistantText3(persisted);
15480
+ verboseLog("Recovered final response from thread history", {
15481
+ length: finalResponse.length
15482
+ });
15483
+ }
15484
+ }
14758
15485
  if (!finalResponse.trim()) {
14759
15486
  const noResponseMessage = `No final response returned by CloudEval (last stream status: ${chatState.status ?? "unknown"}). Retry with --verbose or --format ndjson to inspect stream progress.`;
14760
15487
  progressWriter.clear();
@@ -14928,7 +15655,7 @@ Error: ${errorMsg}
14928
15655
  program.command("banner").description("Preview the startup banner and terminal capabilities").action(async () => {
14929
15656
  const { render } = await import("ink");
14930
15657
  const BannerPreview = React.lazy(async () => ({
14931
- default: (await import("./Banner-6POGUWTT.js")).Banner
15658
+ default: (await import("./Banner-2FNO54OA.js")).Banner
14932
15659
  }));
14933
15660
  render(
14934
15661
  /* @__PURE__ */ jsx(React.Suspense, { fallback: null, children: /* @__PURE__ */ jsx(BannerPreview, { disable: false }) })