@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/{App-NGGAERHH.js → App-XNVJF2ON.js} +614 -516
- package/dist/{Banner-6POGUWTT.js → Banner-2FNO54OA.js} +2 -2
- package/dist/{chunk-YEKKVQCN.js → chunk-IAXSLM2G.js} +1 -1
- package/dist/{chunk-ALXP3LJK.js → chunk-U5TI24XX.js} +1 -1
- package/dist/cli.js +1036 -309
- package/dist/fetchLastAssistantContent-RH6RMSQO.js +27 -0
- package/dist/resolveAskProject-NK435I56.js +49 -0
- package/package.json +1 -1
- package/sbom.spdx.json +1 -1
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-
|
|
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
|
|
138
|
+
import { promises as fs13 } from "fs";
|
|
139
139
|
import os6 from "os";
|
|
140
|
-
import
|
|
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
|
|
1440
|
-
await
|
|
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
|
|
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
|
|
1791
|
-
await
|
|
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
|
|
1827
|
-
const
|
|
1828
|
-
await
|
|
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
|
|
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
|
|
1985
|
-
const
|
|
1986
|
-
const stat = await
|
|
1987
|
-
if (stat?.isDirectory() || !
|
|
1988
|
-
await
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
4462
|
+
import fs5 from "fs/promises";
|
|
3681
4463
|
|
|
3682
4464
|
// src/skills/catalog.ts
|
|
3683
|
-
import
|
|
4465
|
+
import fs4 from "fs/promises";
|
|
3684
4466
|
import fsSync from "fs";
|
|
3685
|
-
import
|
|
4467
|
+
import path3 from "path";
|
|
3686
4468
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3687
|
-
var repoRoot =
|
|
3688
|
-
var defaultSkillsPath =
|
|
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) =>
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
4278
|
-
import
|
|
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
|
|
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
|
|
5387
|
+
const bytes = await fs6.readFile(filePath);
|
|
4650
5388
|
return {
|
|
4651
5389
|
blob: new Blob([bytes], { type: "application/json" }),
|
|
4652
|
-
name:
|
|
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 =
|
|
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 =
|
|
5452
|
+
const parsed = path4.posix.parse(entry);
|
|
4715
5453
|
return normalizeWorkspacePath(
|
|
4716
|
-
|
|
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
|
|
4725
|
-
const outputPath =
|
|
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
|
-
|
|
5469
|
+
path4.join(root, entry),
|
|
4732
5470
|
"--outfile",
|
|
4733
5471
|
outputPath
|
|
4734
5472
|
]);
|
|
4735
|
-
const bytes = await
|
|
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
|
|
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
|
|
5493
|
+
const entries = await fs6.readdir(directory, { withFileTypes: true });
|
|
4756
5494
|
for (const entry of entries) {
|
|
4757
|
-
const absolute =
|
|
4758
|
-
const relative = normalizeWorkspacePath(
|
|
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 =
|
|
4826
|
-
const stat = await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
5084
|
-
const headersOutputPath = options.headersOutput ?
|
|
5085
|
-
await
|
|
5086
|
-
await
|
|
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 ?
|
|
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
|
|
5325
|
-
await
|
|
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
|
|
5660
|
-
await
|
|
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
|
|
5930
|
-
import
|
|
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
|
|
6672
|
+
import fs7 from "fs/promises";
|
|
5935
6673
|
import os2 from "os";
|
|
5936
|
-
import
|
|
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
|
|
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
|
|
6709
|
+
return path5.join(os2.homedir(), ".cursor", "mcp.json");
|
|
5972
6710
|
}
|
|
5973
6711
|
if (client === "vscode") {
|
|
5974
|
-
return
|
|
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
|
|
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
|
|
6110
|
-
await
|
|
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
|
|
6857
|
+
import fs8 from "fs/promises";
|
|
6120
6858
|
var readJsonFile = async (filePath) => {
|
|
6121
|
-
const text = await
|
|
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
|
|
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
|
|
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 =
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
8608
|
-
id
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
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(
|
|
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 (
|
|
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
|
|
8780
|
-
const outputIsDirectory = stat?.isDirectory() || !
|
|
9513
|
+
const stat = await fs9.stat(outputPath).catch(() => void 0);
|
|
9514
|
+
const outputIsDirectory = stat?.isDirectory() || !path6.extname(outputPath);
|
|
8781
9515
|
if (outputIsDirectory) {
|
|
8782
|
-
await
|
|
9516
|
+
await fs9.mkdir(outputPath, { recursive: true });
|
|
8783
9517
|
for (const [key, value] of Object.entries(payload)) {
|
|
8784
|
-
const file =
|
|
8785
|
-
await
|
|
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
|
|
8795
|
-
await
|
|
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
|
|
8805
|
-
await
|
|
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 =
|
|
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
|
|
9137
|
-
await
|
|
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 ?
|
|
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(
|
|
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
|
|
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
|
|
11791
|
+
var asRecord2 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
11058
11792
|
var arrayFromPayload = (payload, key) => {
|
|
11059
|
-
const record =
|
|
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 =
|
|
11065
|
-
return
|
|
11798
|
+
const record = asRecord2(payload);
|
|
11799
|
+
return asRecord2(record.credential ?? record.data ?? record);
|
|
11066
11800
|
};
|
|
11067
11801
|
var secretFromPayload = (payload) => {
|
|
11068
|
-
const record =
|
|
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 =
|
|
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 =
|
|
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
|
|
11965
|
+
import fs10 from "fs/promises";
|
|
11232
11966
|
import os4 from "os";
|
|
11233
|
-
import
|
|
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 =
|
|
11978
|
+
const filePath = path7.join(
|
|
11245
11979
|
os4.tmpdir(),
|
|
11246
11980
|
`cloudeval-hook-${process.pid}-${randomUUID3()}.json`
|
|
11247
11981
|
);
|
|
11248
|
-
await
|
|
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
|
|
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
|
|
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:
|
|
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:
|
|
12413
|
+
pollIntervalMs: parsePositiveInteger2(
|
|
11680
12414
|
options.pollInterval,
|
|
11681
12415
|
"--poll-interval"
|
|
11682
12416
|
),
|
|
11683
|
-
waitTimeoutMs:
|
|
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:
|
|
12477
|
+
pollIntervalMs: parsePositiveInteger2(
|
|
11744
12478
|
options.pollInterval,
|
|
11745
12479
|
"--poll-interval"
|
|
11746
12480
|
),
|
|
11747
|
-
waitTimeoutMs:
|
|
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
|
|
12602
|
+
const path11 = await saveCliConfig(next, profile);
|
|
11869
12603
|
await writeFormattedOutput({
|
|
11870
12604
|
command: "config set",
|
|
11871
|
-
data: { profile, path:
|
|
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
|
|
12616
|
+
const path11 = await saveCliConfig(next, profile);
|
|
11883
12617
|
await writeFormattedOutput({
|
|
11884
12618
|
command: "config unset",
|
|
11885
|
-
data: { profile, path:
|
|
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
|
|
12183
|
-
await
|
|
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
|
|
12983
|
+
const path11 = await saveCliConfig(next, profile);
|
|
12250
12984
|
await writeFormattedOutput({
|
|
12251
12985
|
command: "models default set",
|
|
12252
|
-
data: { profile, path:
|
|
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
|
|
12296
|
-
await
|
|
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
|
|
13202
|
+
const path11 = await saveCliConfig(next, profile);
|
|
12469
13203
|
await writeFormattedOutput({
|
|
12470
13204
|
command: "setup",
|
|
12471
13205
|
data: {
|
|
12472
13206
|
profile,
|
|
12473
|
-
path:
|
|
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
|
|
12489
|
-
import
|
|
12490
|
-
import
|
|
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 =
|
|
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
|
|
12630
|
-
const scriptPath =
|
|
12631
|
-
await
|
|
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
|
|
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
|
|
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 = () =>
|
|
13436
|
+
var getUpdateCachePath = () => path8.join(getCloudevalConfigDir(), UPDATE_CACHE_FILE);
|
|
12703
13437
|
var readCache = async (cachePath) => {
|
|
12704
13438
|
try {
|
|
12705
|
-
const parsed = JSON.parse(await
|
|
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
|
|
12718
|
-
await
|
|
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
|
|
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
|
|
13680
|
+
import fs12 from "fs/promises";
|
|
12947
13681
|
import os5 from "os";
|
|
12948
|
-
import
|
|
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
|
|
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) =>
|
|
13695
|
+
var installerBinDir = (home) => path9.join(home, ".local", "bin");
|
|
12962
13696
|
var completionPaths = (home) => [
|
|
12963
|
-
|
|
12964
|
-
|
|
12965
|
-
|
|
12966
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
13021
|
-
|
|
13022
|
-
|
|
13023
|
-
|
|
13024
|
-
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
14077
|
+
return path10.join(home, ".local", "share", "bash-completion", "completions", "cloudeval");
|
|
13343
14078
|
case "zsh":
|
|
13344
|
-
return
|
|
14079
|
+
return path10.join(home, ".zsh", "completions", "_cloudeval");
|
|
13345
14080
|
case "fish":
|
|
13346
|
-
return
|
|
14081
|
+
return path10.join(home, ".config", "fish", "completions", "cloudeval.fish");
|
|
13347
14082
|
case "powershell":
|
|
13348
|
-
return
|
|
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 =
|
|
14088
|
+
const zshrc = path10.join(os6.homedir(), ".zshrc");
|
|
13354
14089
|
let existing = "";
|
|
13355
14090
|
try {
|
|
13356
|
-
existing = await
|
|
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
|
|
14102
|
+
await fs13.appendFile(zshrc, snippet, "utf8");
|
|
13368
14103
|
};
|
|
13369
14104
|
var installCompletionScript = async (shell, binaryName) => {
|
|
13370
14105
|
const scriptPath = completionScriptPath(shell);
|
|
13371
|
-
await
|
|
13372
|
-
await
|
|
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
|
|
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-
|
|
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-
|
|
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
|
|
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
|
-
|
|
14387
|
-
|
|
14388
|
-
|
|
14389
|
-
|
|
14390
|
-
|
|
14391
|
-
|
|
14392
|
-
|
|
14393
|
-
|
|
14394
|
-
|
|
14395
|
-
|
|
14396
|
-
|
|
14397
|
-
|
|
14398
|
-
|
|
14399
|
-
|
|
14400
|
-
|
|
14401
|
-
|
|
14402
|
-
|
|
14403
|
-
|
|
14404
|
-
|
|
14405
|
-
|
|
14406
|
-
|
|
14407
|
-
|
|
14408
|
-
|
|
14409
|
-
|
|
14410
|
-
|
|
14411
|
-
|
|
14412
|
-
|
|
14413
|
-
|
|
14414
|
-
|
|
14415
|
-
|
|
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 =
|
|
15182
|
+
fileOutputStream = fs14.createWriteStream(options.output, { encoding: "utf-8" });
|
|
14473
15183
|
outputStream = fileOutputStream;
|
|
14474
15184
|
}
|
|
14475
15185
|
if (ndjsonOutput && options.output) {
|
|
14476
|
-
ndjsonOutputStream =
|
|
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
|
-
|
|
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-
|
|
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 }) })
|