@ganakailabs/cloudeval-cli 0.24.3 → 0.25.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-YO6YXOII.js → App-H46FRLWK.js} +614 -516
- package/dist/{Banner-CSZZHF6L.js → Banner-7X2VHUVH.js} +2 -2
- package/dist/{chunk-UZDPQSE5.js → chunk-LKVKOGVL.js} +1 -1
- package/dist/{chunk-JNHT2L4P.js → chunk-TISPT6EB.js} +1 -1
- package/dist/cli.js +825 -310
- 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-LKVKOGVL.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,33 @@ 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
|
+
"--ignore-dirty",
|
|
457
|
+
"--output",
|
|
458
|
+
"--format",
|
|
459
|
+
"--quiet",
|
|
460
|
+
"--progress",
|
|
461
|
+
"--model",
|
|
462
|
+
"--profile",
|
|
463
|
+
"--help"
|
|
464
|
+
],
|
|
465
|
+
workflows: ["github review", "pr gate", "shift-left review"]
|
|
466
|
+
},
|
|
440
467
|
{
|
|
441
468
|
name: "recipes",
|
|
442
469
|
description: "CloudEval reusable recipes and agent skills",
|
|
@@ -1436,10 +1463,10 @@ var writeFormattedOutput = async (input) => {
|
|
|
1436
1463
|
process.stdout.write(text);
|
|
1437
1464
|
};
|
|
1438
1465
|
var writePrivateOutputFile = async (output, text) => {
|
|
1439
|
-
const
|
|
1440
|
-
await
|
|
1466
|
+
const fs14 = await import("fs/promises");
|
|
1467
|
+
await fs14.writeFile(output, text, { encoding: "utf8", mode: 384 });
|
|
1441
1468
|
if (process.platform !== "win32") {
|
|
1442
|
-
await
|
|
1469
|
+
await fs14.chmod(output, 384);
|
|
1443
1470
|
}
|
|
1444
1471
|
};
|
|
1445
1472
|
|
|
@@ -1787,8 +1814,8 @@ var writeReport = async (report, options, tuiDefault) => {
|
|
|
1787
1814
|
const textFormat = format === "text" || format === "table" ? "summary" : format;
|
|
1788
1815
|
const text = serializeReportOutput(report, { format: textFormat, mode });
|
|
1789
1816
|
if (options.output) {
|
|
1790
|
-
const
|
|
1791
|
-
await
|
|
1817
|
+
const fs14 = await import("fs/promises");
|
|
1818
|
+
await fs14.writeFile(options.output, text, "utf8");
|
|
1792
1819
|
return;
|
|
1793
1820
|
}
|
|
1794
1821
|
process.stdout.write(text);
|
|
@@ -1823,16 +1850,16 @@ var writeDownloadPayload = async (input) => {
|
|
|
1823
1850
|
);
|
|
1824
1851
|
return [];
|
|
1825
1852
|
}
|
|
1826
|
-
const
|
|
1827
|
-
const
|
|
1828
|
-
await
|
|
1853
|
+
const fs14 = await import("fs/promises");
|
|
1854
|
+
const path11 = await import("path");
|
|
1855
|
+
await fs14.mkdir(path11.dirname(input.output), { recursive: true });
|
|
1829
1856
|
const text = formatOutput({
|
|
1830
1857
|
command: input.command,
|
|
1831
1858
|
data: input.payload,
|
|
1832
1859
|
format: input.format,
|
|
1833
1860
|
frontendUrl: input.frontendUrl
|
|
1834
1861
|
});
|
|
1835
|
-
await
|
|
1862
|
+
await fs14.writeFile(input.output, text, "utf8");
|
|
1836
1863
|
return [input.output];
|
|
1837
1864
|
};
|
|
1838
1865
|
var resolveMachineFormat = (requested) => {
|
|
@@ -1981,14 +2008,14 @@ var registerReportsCommand = (program2, deps) => {
|
|
|
1981
2008
|
);
|
|
1982
2009
|
const data = reportTypes.length === 1 ? payload[reportTypes[0] === "architecture" ? "waf" : reportTypes[0]] : payload;
|
|
1983
2010
|
if (options.output && reportTypes.length > 1) {
|
|
1984
|
-
const
|
|
1985
|
-
const
|
|
1986
|
-
const stat = await
|
|
1987
|
-
if (stat?.isDirectory() || !
|
|
1988
|
-
await
|
|
2011
|
+
const fs14 = await import("fs/promises");
|
|
2012
|
+
const path11 = await import("path");
|
|
2013
|
+
const stat = await fs14.stat(options.output).catch(() => void 0);
|
|
2014
|
+
if (stat?.isDirectory() || !path11.extname(options.output)) {
|
|
2015
|
+
await fs14.mkdir(options.output, { recursive: true });
|
|
1989
2016
|
const files = [];
|
|
1990
2017
|
for (const [key, value] of Object.entries(payload)) {
|
|
1991
|
-
const file =
|
|
2018
|
+
const file = path11.join(options.output, `${projectId}-${key}-report.json`);
|
|
1992
2019
|
files.push(
|
|
1993
2020
|
...await writeDownloadPayload({
|
|
1994
2021
|
command: "reports download",
|
|
@@ -2217,9 +2244,546 @@ var registerReportsCommand = (program2, deps) => {
|
|
|
2217
2244
|
);
|
|
2218
2245
|
};
|
|
2219
2246
|
|
|
2247
|
+
// src/reviewCommand.ts
|
|
2248
|
+
import fs2 from "fs/promises";
|
|
2249
|
+
import path2 from "path";
|
|
2250
|
+
import { spawn } from "child_process";
|
|
2251
|
+
|
|
2252
|
+
// src/apiClient.ts
|
|
2253
|
+
var responseErrorMessage = async (response) => {
|
|
2254
|
+
const text = await response.text();
|
|
2255
|
+
if (!text) {
|
|
2256
|
+
return `request failed with status ${response.status}`;
|
|
2257
|
+
}
|
|
2258
|
+
try {
|
|
2259
|
+
const payload = JSON.parse(text);
|
|
2260
|
+
const detail = payload?.detail ?? payload?.message ?? payload?.error;
|
|
2261
|
+
if (typeof detail === "string") {
|
|
2262
|
+
return detail;
|
|
2263
|
+
}
|
|
2264
|
+
if (detail) {
|
|
2265
|
+
return JSON.stringify(detail);
|
|
2266
|
+
}
|
|
2267
|
+
} catch {
|
|
2268
|
+
}
|
|
2269
|
+
return text;
|
|
2270
|
+
};
|
|
2271
|
+
var fetchCloudEvalJson = async ({
|
|
2272
|
+
baseUrl,
|
|
2273
|
+
authToken,
|
|
2274
|
+
path: path11,
|
|
2275
|
+
method = "GET",
|
|
2276
|
+
query = {},
|
|
2277
|
+
body,
|
|
2278
|
+
idempotencyKey
|
|
2279
|
+
}) => {
|
|
2280
|
+
const url = new URL(`${normalizeApiBase(baseUrl)}${path11}`);
|
|
2281
|
+
for (const [key, value] of Object.entries(query)) {
|
|
2282
|
+
if (value !== void 0 && value !== null && value !== "") {
|
|
2283
|
+
url.searchParams.set(key, String(value));
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
const response = await fetch(url, {
|
|
2287
|
+
method,
|
|
2288
|
+
headers: {
|
|
2289
|
+
...getCLIHeaders(authToken),
|
|
2290
|
+
...idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {}
|
|
2291
|
+
},
|
|
2292
|
+
...body === void 0 ? {} : { body: JSON.stringify(body) }
|
|
2293
|
+
});
|
|
2294
|
+
if (!response.ok) {
|
|
2295
|
+
throw new Error(await responseErrorMessage(response));
|
|
2296
|
+
}
|
|
2297
|
+
return await response.json();
|
|
2298
|
+
};
|
|
2299
|
+
|
|
2300
|
+
// src/reviewCommand.ts
|
|
2301
|
+
var DIRTY_REVIEW_MESSAGE = "Reviews pushed commits only. Add --ignore-dirty to review HEAD anyway.";
|
|
2302
|
+
var runGit = async (cwd, args) => {
|
|
2303
|
+
const child = spawn("git", args, {
|
|
2304
|
+
cwd,
|
|
2305
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
2306
|
+
});
|
|
2307
|
+
const stdout = [];
|
|
2308
|
+
child.stdout.on("data", (chunk) => stdout.push(Buffer.from(chunk)));
|
|
2309
|
+
const exitCode = await new Promise(
|
|
2310
|
+
(resolve) => child.on("exit", resolve)
|
|
2311
|
+
);
|
|
2312
|
+
return {
|
|
2313
|
+
ok: exitCode === 0,
|
|
2314
|
+
stdout: Buffer.concat(stdout).toString("utf8").trim()
|
|
2315
|
+
};
|
|
2316
|
+
};
|
|
2317
|
+
var normalizeGithubRepo = (value) => {
|
|
2318
|
+
const text = String(value ?? "").trim();
|
|
2319
|
+
if (!text) return void 0;
|
|
2320
|
+
const withoutGit = text.replace(/\.git$/i, "");
|
|
2321
|
+
const httpsMatch = withoutGit.match(/github\.com[:/]([^/\s]+)\/([^/\s]+)$/i);
|
|
2322
|
+
if (httpsMatch) {
|
|
2323
|
+
return `${httpsMatch[1]}/${httpsMatch[2]}`;
|
|
2324
|
+
}
|
|
2325
|
+
if (/^[^/\s]+\/[^/\s]+$/.test(withoutGit)) {
|
|
2326
|
+
return withoutGit;
|
|
2327
|
+
}
|
|
2328
|
+
return void 0;
|
|
2329
|
+
};
|
|
2330
|
+
var resolveGitMetadata = async (cwd, options) => {
|
|
2331
|
+
const status = await runGit(cwd, ["status", "--porcelain"]);
|
|
2332
|
+
const dirty = status.ok && status.stdout.length > 0;
|
|
2333
|
+
const remote = options.repo ? { ok: true, stdout: options.repo } : await runGit(cwd, ["remote", "get-url", "origin"]);
|
|
2334
|
+
const ref = options.ref ? options.ref : (await runGit(cwd, ["rev-parse", "--abbrev-ref", "HEAD"])).stdout;
|
|
2335
|
+
const sha = options.commitSha ? options.commitSha : (await runGit(cwd, ["rev-parse", "HEAD"])).stdout;
|
|
2336
|
+
return {
|
|
2337
|
+
repo: normalizeGithubRepo(remote.stdout),
|
|
2338
|
+
ref: ref && ref !== "HEAD" ? ref : void 0,
|
|
2339
|
+
commitSha: sha || void 0,
|
|
2340
|
+
dirty
|
|
2341
|
+
};
|
|
2342
|
+
};
|
|
2343
|
+
var asRecord = (value) => value && typeof value === "object" ? value : {};
|
|
2344
|
+
var sourceOf = (project) => asRecord(project.iac_source ?? project.iacSource);
|
|
2345
|
+
var resolveProjectId = async ({
|
|
2346
|
+
baseUrl,
|
|
2347
|
+
token,
|
|
2348
|
+
requestedProjectId,
|
|
2349
|
+
repo,
|
|
2350
|
+
ref,
|
|
2351
|
+
sourceRoot
|
|
2352
|
+
}) => {
|
|
2353
|
+
if (requestedProjectId) {
|
|
2354
|
+
return requestedProjectId;
|
|
2355
|
+
}
|
|
2356
|
+
if (!repo) {
|
|
2357
|
+
throw new Error("Provide --repo or run inside a GitHub-backed Git repository.");
|
|
2358
|
+
}
|
|
2359
|
+
const projects = await fetchCloudEvalJson({
|
|
2360
|
+
baseUrl,
|
|
2361
|
+
authToken: token,
|
|
2362
|
+
path: "/projects"
|
|
2363
|
+
});
|
|
2364
|
+
const matches = projects.filter((project) => {
|
|
2365
|
+
const record = asRecord(project);
|
|
2366
|
+
const source = sourceOf(record);
|
|
2367
|
+
if (source.type !== "github") return false;
|
|
2368
|
+
if (source.repo_full_name !== repo) return false;
|
|
2369
|
+
if (sourceRoot !== void 0 && String(source.source_root ?? "") !== sourceRoot) {
|
|
2370
|
+
return false;
|
|
2371
|
+
}
|
|
2372
|
+
if (ref && source.ref && source.ref !== ref) {
|
|
2373
|
+
return false;
|
|
2374
|
+
}
|
|
2375
|
+
return true;
|
|
2376
|
+
});
|
|
2377
|
+
if (matches.length === 1) {
|
|
2378
|
+
return String(asRecord(matches[0]).id);
|
|
2379
|
+
}
|
|
2380
|
+
if (matches.length > 1) {
|
|
2381
|
+
throw new Error(
|
|
2382
|
+
`Multiple CloudEval projects match ${repo}. Pass --project to choose one.`
|
|
2383
|
+
);
|
|
2384
|
+
}
|
|
2385
|
+
throw new Error(
|
|
2386
|
+
`No CloudEval GitHub project matched ${repo}. Create one in CloudEval or pass --project.`
|
|
2387
|
+
);
|
|
2388
|
+
};
|
|
2389
|
+
var readConfigText = async (cwd, options) => {
|
|
2390
|
+
const configPath = options.config ?? path2.join(cwd, ".cloudeval", "config.yaml");
|
|
2391
|
+
try {
|
|
2392
|
+
return await fs2.readFile(path2.resolve(cwd, configPath), "utf8");
|
|
2393
|
+
} catch {
|
|
2394
|
+
return void 0;
|
|
2395
|
+
}
|
|
2396
|
+
};
|
|
2397
|
+
var parseGateConfig = (configText) => {
|
|
2398
|
+
if (!configText || !/^\s*ci\s*:/m.test(configText) || !/^\s*gates\s*:/m.test(configText)) {
|
|
2399
|
+
return void 0;
|
|
2400
|
+
}
|
|
2401
|
+
const numberValue2 = (key) => {
|
|
2402
|
+
const match = configText.match(new RegExp(`^\\s*${key}\\s*:\\s*([0-9]+(?:\\.[0-9]+)?)`, "m"));
|
|
2403
|
+
return match ? Number(match[1]) : void 0;
|
|
2404
|
+
};
|
|
2405
|
+
const booleanValue2 = (key) => {
|
|
2406
|
+
const match = configText.match(new RegExp(`^\\s*${key}\\s*:\\s*(true|false)`, "im"));
|
|
2407
|
+
return match ? match[1].toLowerCase() === "true" : void 0;
|
|
2408
|
+
};
|
|
2409
|
+
return {
|
|
2410
|
+
overallScoreMin: numberValue2("overall_score_min") ?? 80,
|
|
2411
|
+
failOnHighRisk: booleanValue2("fail_on_high_risk") ?? true,
|
|
2412
|
+
maxMonthlyCost: numberValue2("max_monthly_cost")
|
|
2413
|
+
};
|
|
2414
|
+
};
|
|
2415
|
+
var numberFrom = (...values) => {
|
|
2416
|
+
for (const value of values) {
|
|
2417
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
2418
|
+
return value;
|
|
2419
|
+
}
|
|
2420
|
+
if (typeof value === "string" && value.trim() && Number.isFinite(Number(value))) {
|
|
2421
|
+
return Number(value);
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
return void 0;
|
|
2425
|
+
};
|
|
2426
|
+
var evaluateGate = ({
|
|
2427
|
+
configText,
|
|
2428
|
+
waf,
|
|
2429
|
+
cost
|
|
2430
|
+
}) => {
|
|
2431
|
+
const gateConfig = parseGateConfig(configText);
|
|
2432
|
+
const overallScore = numberFrom(
|
|
2433
|
+
waf?.parsed?.score?.overall,
|
|
2434
|
+
waf?.parsed?.overall_score,
|
|
2435
|
+
waf?.raw?.score
|
|
2436
|
+
);
|
|
2437
|
+
const highRisk = numberFrom(
|
|
2438
|
+
waf?.parsed?.counts?.highRisk,
|
|
2439
|
+
waf?.parsed?.counts?.high_count,
|
|
2440
|
+
waf?.parsed?.highRisk
|
|
2441
|
+
);
|
|
2442
|
+
const monthlyCost = numberFrom(
|
|
2443
|
+
cost?.parsed?.totalSpend?.amount,
|
|
2444
|
+
cost?.parsed?.total_spend?.amount,
|
|
2445
|
+
cost?.raw?.total
|
|
2446
|
+
);
|
|
2447
|
+
if (!gateConfig) {
|
|
2448
|
+
return {
|
|
2449
|
+
status: "warn",
|
|
2450
|
+
reason: "ci.gates is not configured in .cloudeval/config.yaml.",
|
|
2451
|
+
overallScore,
|
|
2452
|
+
highRisk,
|
|
2453
|
+
monthlyCost
|
|
2454
|
+
};
|
|
2455
|
+
}
|
|
2456
|
+
const failures = [];
|
|
2457
|
+
if (overallScore !== void 0 && overallScore < gateConfig.overallScoreMin) {
|
|
2458
|
+
failures.push(
|
|
2459
|
+
`overall score ${overallScore} is below ${gateConfig.overallScoreMin}`
|
|
2460
|
+
);
|
|
2461
|
+
}
|
|
2462
|
+
if (gateConfig.failOnHighRisk && highRisk !== void 0 && highRisk > 0) {
|
|
2463
|
+
failures.push(`${highRisk} high-risk architecture findings`);
|
|
2464
|
+
}
|
|
2465
|
+
if (gateConfig.maxMonthlyCost !== void 0 && monthlyCost !== void 0 && monthlyCost > gateConfig.maxMonthlyCost) {
|
|
2466
|
+
failures.push(`monthly cost ${monthlyCost} exceeds ${gateConfig.maxMonthlyCost}`);
|
|
2467
|
+
}
|
|
2468
|
+
return {
|
|
2469
|
+
status: failures.length ? "fail" : "pass",
|
|
2470
|
+
failures,
|
|
2471
|
+
thresholds: gateConfig,
|
|
2472
|
+
overallScore,
|
|
2473
|
+
highRisk,
|
|
2474
|
+
monthlyCost
|
|
2475
|
+
};
|
|
2476
|
+
};
|
|
2477
|
+
var safeFetch = async (input) => {
|
|
2478
|
+
try {
|
|
2479
|
+
return await fetchCloudEvalJson(input);
|
|
2480
|
+
} catch {
|
|
2481
|
+
return void 0;
|
|
2482
|
+
}
|
|
2483
|
+
};
|
|
2484
|
+
var parsePositiveInteger = (value, flagName, fallback) => {
|
|
2485
|
+
if (value === void 0 || value === "") {
|
|
2486
|
+
return fallback;
|
|
2487
|
+
}
|
|
2488
|
+
const parsed = Number(value);
|
|
2489
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
2490
|
+
throw new Error(`${flagName} must be a positive number of milliseconds.`);
|
|
2491
|
+
}
|
|
2492
|
+
return Math.floor(parsed);
|
|
2493
|
+
};
|
|
2494
|
+
var extractJobId2 = (value) => {
|
|
2495
|
+
const record = asRecord(value);
|
|
2496
|
+
const job = asRecord(record.job);
|
|
2497
|
+
const candidates = [
|
|
2498
|
+
record.job_id,
|
|
2499
|
+
record.jobId,
|
|
2500
|
+
record.id,
|
|
2501
|
+
job.job_id,
|
|
2502
|
+
job.jobId,
|
|
2503
|
+
job.id
|
|
2504
|
+
];
|
|
2505
|
+
return candidates.find(
|
|
2506
|
+
(candidate) => typeof candidate === "string" && candidate.trim().length > 0
|
|
2507
|
+
);
|
|
2508
|
+
};
|
|
2509
|
+
var isTerminalJobStatus2 = (value) => {
|
|
2510
|
+
const status = String(asRecord(value).status ?? "").toUpperCase();
|
|
2511
|
+
return [
|
|
2512
|
+
"COMPLETED",
|
|
2513
|
+
"SUCCEEDED",
|
|
2514
|
+
"SUCCESS",
|
|
2515
|
+
"FAILED",
|
|
2516
|
+
"CANCELLED",
|
|
2517
|
+
"CANCELED",
|
|
2518
|
+
"ERROR"
|
|
2519
|
+
].includes(status);
|
|
2520
|
+
};
|
|
2521
|
+
var isFailedJobStatus = (value) => {
|
|
2522
|
+
const status = String(asRecord(value).status ?? "").toUpperCase();
|
|
2523
|
+
return ["FAILED", "CANCELLED", "CANCELED", "ERROR"].includes(status);
|
|
2524
|
+
};
|
|
2525
|
+
var waitForJob = async ({
|
|
2526
|
+
baseUrl,
|
|
2527
|
+
token,
|
|
2528
|
+
userId,
|
|
2529
|
+
jobId,
|
|
2530
|
+
pollIntervalMs,
|
|
2531
|
+
waitTimeoutMs
|
|
2532
|
+
}) => {
|
|
2533
|
+
const startedAt = Date.now();
|
|
2534
|
+
let lastStatus;
|
|
2535
|
+
for (; ; ) {
|
|
2536
|
+
const query = userId ? `?user_id=${encodeURIComponent(userId)}` : "";
|
|
2537
|
+
lastStatus = await fetchCloudEvalJson({
|
|
2538
|
+
baseUrl,
|
|
2539
|
+
authToken: token,
|
|
2540
|
+
path: `/jobs/${encodeURIComponent(jobId)}${query}`
|
|
2541
|
+
});
|
|
2542
|
+
const status = String(lastStatus.status ?? "unknown");
|
|
2543
|
+
process.stderr.write(`github sync job ${jobId}: ${status}
|
|
2544
|
+
`);
|
|
2545
|
+
if (isTerminalJobStatus2(lastStatus)) {
|
|
2546
|
+
if (isFailedJobStatus(lastStatus)) {
|
|
2547
|
+
throw new Error(`GitHub sync job ${jobId} finished with status ${status}.`);
|
|
2548
|
+
}
|
|
2549
|
+
return lastStatus;
|
|
2550
|
+
}
|
|
2551
|
+
if (Date.now() - startedAt > waitTimeoutMs) {
|
|
2552
|
+
throw new Error(`Timed out waiting for GitHub sync job ${jobId}.`);
|
|
2553
|
+
}
|
|
2554
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
2555
|
+
}
|
|
2556
|
+
};
|
|
2557
|
+
var fetchProjectById = async ({
|
|
2558
|
+
baseUrl,
|
|
2559
|
+
token,
|
|
2560
|
+
projectId
|
|
2561
|
+
}) => {
|
|
2562
|
+
const projects = await safeFetch({
|
|
2563
|
+
baseUrl,
|
|
2564
|
+
authToken: token,
|
|
2565
|
+
path: "/projects"
|
|
2566
|
+
});
|
|
2567
|
+
return projects?.map(asRecord).find((project) => project.id === projectId);
|
|
2568
|
+
};
|
|
2569
|
+
var buildAiSummaryPrompt = (data) => [
|
|
2570
|
+
"Write a concise CloudEval pull request review summary in Markdown.",
|
|
2571
|
+
"Focus on gate status, Well-Architected posture, cost posture, and security/operational risks.",
|
|
2572
|
+
"Keep it under 160 words. Do not invent facts not present below.",
|
|
2573
|
+
"",
|
|
2574
|
+
`Project: ${data.projectId}`,
|
|
2575
|
+
`Repository: ${data.repo ?? "unknown"}`,
|
|
2576
|
+
`Ref: ${data.ref ?? "unknown"}`,
|
|
2577
|
+
`Commit: ${data.commitSha ?? "unknown"}`,
|
|
2578
|
+
`Gate: ${String(data.gate?.status ?? "unknown").toUpperCase()}`,
|
|
2579
|
+
`Well-Architected score: ${data.gate?.overallScore ?? "unknown"}`,
|
|
2580
|
+
`High-risk findings: ${data.gate?.highRisk ?? "unknown"}`,
|
|
2581
|
+
`Monthly cost: ${data.gate?.monthlyCost ?? "unknown"}`,
|
|
2582
|
+
Array.isArray(data.gate?.failures) && data.gate.failures.length ? `Gate failures: ${data.gate.failures.join("; ")}` : "Gate failures: none reported"
|
|
2583
|
+
].join("\n");
|
|
2584
|
+
var generateAiSummary = async ({
|
|
2585
|
+
baseUrl,
|
|
2586
|
+
token,
|
|
2587
|
+
user,
|
|
2588
|
+
project,
|
|
2589
|
+
model,
|
|
2590
|
+
data
|
|
2591
|
+
}) => {
|
|
2592
|
+
const core = await import("./dist-CFLR5FML.js");
|
|
2593
|
+
const threadId = `review-${data.projectId}-${Date.now()}`;
|
|
2594
|
+
let markdown = "";
|
|
2595
|
+
for await (const chunk of core.streamChat({
|
|
2596
|
+
baseUrl,
|
|
2597
|
+
authToken: token,
|
|
2598
|
+
message: buildAiSummaryPrompt(data),
|
|
2599
|
+
threadId,
|
|
2600
|
+
user: {
|
|
2601
|
+
id: String(project?.user_id ?? user?.id ?? "cli-user"),
|
|
2602
|
+
name: String(user?.full_name ?? user?.name ?? user?.email ?? "CloudEval CI")
|
|
2603
|
+
},
|
|
2604
|
+
project: project ? {
|
|
2605
|
+
id: String(project.id ?? data.projectId),
|
|
2606
|
+
name: String(project.name ?? data.projectId),
|
|
2607
|
+
user_id: typeof project.user_id === "string" ? project.user_id : void 0,
|
|
2608
|
+
cloud_provider: typeof project.cloud_provider === "string" ? project.cloud_provider : void 0,
|
|
2609
|
+
type: typeof project.type === "string" ? project.type : void 0,
|
|
2610
|
+
connection_ids: Array.isArray(project.connection_ids) ? project.connection_ids : void 0
|
|
2611
|
+
} : {
|
|
2612
|
+
id: String(data.projectId),
|
|
2613
|
+
name: String(data.projectId)
|
|
2614
|
+
},
|
|
2615
|
+
settings: {
|
|
2616
|
+
mode: "ask",
|
|
2617
|
+
...model ? { model } : {}
|
|
2618
|
+
},
|
|
2619
|
+
completeAfterResponse: true,
|
|
2620
|
+
responseCompletionGraceMs: 250,
|
|
2621
|
+
streamIdleTimeoutMs: 3e4
|
|
2622
|
+
})) {
|
|
2623
|
+
const content = chunk?.content;
|
|
2624
|
+
if (chunk.type === "responding" && typeof content === "string") {
|
|
2625
|
+
markdown += content;
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
return {
|
|
2629
|
+
enabled: true,
|
|
2630
|
+
mode: "ask",
|
|
2631
|
+
...model ? { model } : {},
|
|
2632
|
+
markdown: markdown.trim(),
|
|
2633
|
+
threadId
|
|
2634
|
+
};
|
|
2635
|
+
};
|
|
2636
|
+
var buildMarkdownSummary = (data) => {
|
|
2637
|
+
const gateStatus = String(data.gate?.status ?? "unknown").toUpperCase();
|
|
2638
|
+
const score = data.gate?.overallScore ?? "unknown";
|
|
2639
|
+
const cost = data.gate?.monthlyCost ?? "unknown";
|
|
2640
|
+
const lines = [
|
|
2641
|
+
"### CloudEval review",
|
|
2642
|
+
"",
|
|
2643
|
+
`- **Project:** \`${data.projectId}\``,
|
|
2644
|
+
`- **Repository:** \`${data.repo ?? "unknown"}\``,
|
|
2645
|
+
`- **Ref:** \`${data.ref ?? "unknown"}\``,
|
|
2646
|
+
`- **Commit:** \`${String(data.commitSha ?? "unknown").slice(0, 12)}\``,
|
|
2647
|
+
`- **Gate:** ${gateStatus}`,
|
|
2648
|
+
`- **Well-Architected score:** ${score}`,
|
|
2649
|
+
`- **Monthly cost:** ${cost}`
|
|
2650
|
+
];
|
|
2651
|
+
if (data.aiSummary?.markdown) {
|
|
2652
|
+
lines.push("", "## AI summary", "", data.aiSummary.markdown);
|
|
2653
|
+
}
|
|
2654
|
+
return lines.join("\n");
|
|
2655
|
+
};
|
|
2656
|
+
var registerReviewCommand = (program2, deps) => {
|
|
2657
|
+
const command = addAuthOptions(
|
|
2658
|
+
program2.command("review").description("Review the current GitHub-backed project from a pushed commit"),
|
|
2659
|
+
deps.defaultBaseUrl
|
|
2660
|
+
).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("--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");
|
|
2661
|
+
command.action(async (options, actionCommand) => {
|
|
2662
|
+
try {
|
|
2663
|
+
const cwd = process.cwd();
|
|
2664
|
+
const git = await resolveGitMetadata(cwd, options);
|
|
2665
|
+
if (git.dirty && !options.ignoreDirty) {
|
|
2666
|
+
throw new Error(DIRTY_REVIEW_MESSAGE);
|
|
2667
|
+
}
|
|
2668
|
+
const context = await resolveAuthContext(options, actionCommand, deps);
|
|
2669
|
+
const repo = normalizeGithubRepo(options.repo) ?? git.repo;
|
|
2670
|
+
const ref = options.ref ?? git.ref;
|
|
2671
|
+
const commitSha = options.commitSha ?? git.commitSha;
|
|
2672
|
+
const sourceRoot = options.sourceRoot;
|
|
2673
|
+
const projectId = await resolveProjectId({
|
|
2674
|
+
baseUrl: context.baseUrl,
|
|
2675
|
+
token: context.token,
|
|
2676
|
+
requestedProjectId: options.project,
|
|
2677
|
+
repo,
|
|
2678
|
+
ref,
|
|
2679
|
+
sourceRoot
|
|
2680
|
+
});
|
|
2681
|
+
const sync = await fetchCloudEvalJson({
|
|
2682
|
+
baseUrl: context.baseUrl,
|
|
2683
|
+
authToken: context.token,
|
|
2684
|
+
path: `/projects/${projectId}/github/sync`,
|
|
2685
|
+
method: "POST",
|
|
2686
|
+
body: commitSha ? { commit_sha: commitSha } : {},
|
|
2687
|
+
idempotencyKey: `cloudeval-review-${projectId}-${commitSha ?? "head"}`
|
|
2688
|
+
});
|
|
2689
|
+
const finalStatus = options.wait === false ? void 0 : extractJobId2(sync) ? await waitForJob({
|
|
2690
|
+
baseUrl: context.baseUrl,
|
|
2691
|
+
token: context.token,
|
|
2692
|
+
userId: context.user?.id,
|
|
2693
|
+
jobId: extractJobId2(sync),
|
|
2694
|
+
pollIntervalMs: parsePositiveInteger(
|
|
2695
|
+
options.pollInterval,
|
|
2696
|
+
"--poll-interval",
|
|
2697
|
+
5e3
|
|
2698
|
+
),
|
|
2699
|
+
waitTimeoutMs: parsePositiveInteger(
|
|
2700
|
+
options.waitTimeout,
|
|
2701
|
+
"--wait-timeout",
|
|
2702
|
+
9e5
|
|
2703
|
+
)
|
|
2704
|
+
}) : void 0;
|
|
2705
|
+
const [cost, waf, configText] = await Promise.all([
|
|
2706
|
+
safeFetch({
|
|
2707
|
+
baseUrl: context.baseUrl,
|
|
2708
|
+
authToken: context.token,
|
|
2709
|
+
path: `/cost-reports/${projectId}/full`
|
|
2710
|
+
}),
|
|
2711
|
+
safeFetch({
|
|
2712
|
+
baseUrl: context.baseUrl,
|
|
2713
|
+
authToken: context.token,
|
|
2714
|
+
path: `/well-architected-reports/${projectId}/full`
|
|
2715
|
+
}),
|
|
2716
|
+
readConfigText(cwd, options)
|
|
2717
|
+
]);
|
|
2718
|
+
const project = await fetchProjectById({
|
|
2719
|
+
baseUrl: context.baseUrl,
|
|
2720
|
+
token: context.token,
|
|
2721
|
+
projectId
|
|
2722
|
+
});
|
|
2723
|
+
const data = {
|
|
2724
|
+
projectId,
|
|
2725
|
+
repo,
|
|
2726
|
+
ref,
|
|
2727
|
+
commitSha,
|
|
2728
|
+
sourceRoot,
|
|
2729
|
+
sync: finalStatus ? { ...asRecord(sync), finalStatus } : sync,
|
|
2730
|
+
reports: {
|
|
2731
|
+
cost,
|
|
2732
|
+
waf
|
|
2733
|
+
},
|
|
2734
|
+
gate: evaluateGate({ configText, waf, cost })
|
|
2735
|
+
};
|
|
2736
|
+
if (options.aiSummary !== false) {
|
|
2737
|
+
try {
|
|
2738
|
+
data.aiSummary = await generateAiSummary({
|
|
2739
|
+
baseUrl: context.baseUrl,
|
|
2740
|
+
token: context.token,
|
|
2741
|
+
user: context.user,
|
|
2742
|
+
project,
|
|
2743
|
+
model: options.model,
|
|
2744
|
+
data
|
|
2745
|
+
});
|
|
2746
|
+
} catch (error) {
|
|
2747
|
+
data.aiSummary = {
|
|
2748
|
+
enabled: true,
|
|
2749
|
+
status: "failed",
|
|
2750
|
+
error: error?.message ?? "AI summary failed"
|
|
2751
|
+
};
|
|
2752
|
+
}
|
|
2753
|
+
} else {
|
|
2754
|
+
data.aiSummary = { enabled: false };
|
|
2755
|
+
}
|
|
2756
|
+
const summaryMarkdown = buildMarkdownSummary(data);
|
|
2757
|
+
const filesWritten = [];
|
|
2758
|
+
if (options.output) {
|
|
2759
|
+
const outputDir = path2.resolve(options.output);
|
|
2760
|
+
await fs2.mkdir(outputDir, { recursive: true });
|
|
2761
|
+
const jsonPath = path2.join(outputDir, "review.json");
|
|
2762
|
+
const markdownPath = path2.join(outputDir, "review.md");
|
|
2763
|
+
await fs2.writeFile(jsonPath, JSON.stringify(data, null, 2), "utf8");
|
|
2764
|
+
await fs2.writeFile(markdownPath, summaryMarkdown, "utf8");
|
|
2765
|
+
filesWritten.push(jsonPath, markdownPath);
|
|
2766
|
+
}
|
|
2767
|
+
await writeFormattedOutput({
|
|
2768
|
+
command: "review",
|
|
2769
|
+
data: { ...data, summaryMarkdown },
|
|
2770
|
+
format: options.format,
|
|
2771
|
+
filesWritten
|
|
2772
|
+
});
|
|
2773
|
+
if (data.gate.status === "fail") {
|
|
2774
|
+
process.exit(1);
|
|
2775
|
+
}
|
|
2776
|
+
process.exit(0);
|
|
2777
|
+
} catch (error) {
|
|
2778
|
+
console.error(error?.message ?? "Review failed");
|
|
2779
|
+
process.exit(1);
|
|
2780
|
+
}
|
|
2781
|
+
});
|
|
2782
|
+
};
|
|
2783
|
+
|
|
2220
2784
|
// src/recipesCommand.ts
|
|
2221
2785
|
import { randomUUID } from "crypto";
|
|
2222
|
-
import
|
|
2786
|
+
import fs3 from "fs/promises";
|
|
2223
2787
|
|
|
2224
2788
|
// src/askProgress.ts
|
|
2225
2789
|
var ASK_PROGRESS_MODES = /* @__PURE__ */ new Set(["auto", "stderr", "ndjson", "none"]);
|
|
@@ -3455,7 +4019,7 @@ var writeRecipeList = async (options) => {
|
|
|
3455
4019
|
if (format === "table" || format === "text") {
|
|
3456
4020
|
const text = renderRecipesTable();
|
|
3457
4021
|
if (options.output) {
|
|
3458
|
-
await
|
|
4022
|
+
await fs3.writeFile(options.output, text, "utf8");
|
|
3459
4023
|
return;
|
|
3460
4024
|
}
|
|
3461
4025
|
process.stdout.write(text);
|
|
@@ -3464,7 +4028,7 @@ var writeRecipeList = async (options) => {
|
|
|
3464
4028
|
if (format === "markdown") {
|
|
3465
4029
|
const text = renderRecipesMarkdown();
|
|
3466
4030
|
if (options.output) {
|
|
3467
|
-
await
|
|
4031
|
+
await fs3.writeFile(options.output, text, "utf8");
|
|
3468
4032
|
return;
|
|
3469
4033
|
}
|
|
3470
4034
|
process.stdout.write(text);
|
|
@@ -3482,7 +4046,7 @@ var writeRecipeShow = async (recipe, options) => {
|
|
|
3482
4046
|
if (format === "markdown" || format === "text") {
|
|
3483
4047
|
const text = renderRecipeMarkdown(recipe);
|
|
3484
4048
|
if (options.output) {
|
|
3485
|
-
await
|
|
4049
|
+
await fs3.writeFile(options.output, text, "utf8");
|
|
3486
4050
|
return;
|
|
3487
4051
|
}
|
|
3488
4052
|
process.stdout.write(text);
|
|
@@ -3677,15 +4241,15 @@ var registerRecipesCommand = (program2, deps) => {
|
|
|
3677
4241
|
};
|
|
3678
4242
|
|
|
3679
4243
|
// src/skillsCommand.ts
|
|
3680
|
-
import
|
|
4244
|
+
import fs5 from "fs/promises";
|
|
3681
4245
|
|
|
3682
4246
|
// src/skills/catalog.ts
|
|
3683
|
-
import
|
|
4247
|
+
import fs4 from "fs/promises";
|
|
3684
4248
|
import fsSync from "fs";
|
|
3685
|
-
import
|
|
4249
|
+
import path3 from "path";
|
|
3686
4250
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3687
|
-
var repoRoot =
|
|
3688
|
-
var defaultSkillsPath =
|
|
4251
|
+
var repoRoot = path3.resolve(fileURLToPath2(new URL("../../../../", import.meta.url)));
|
|
4252
|
+
var defaultSkillsPath = path3.join(repoRoot, "skills");
|
|
3689
4253
|
var requiredSkillSections = [
|
|
3690
4254
|
"## WHEN",
|
|
3691
4255
|
"## DO NOT USE FOR",
|
|
@@ -3773,7 +4337,7 @@ var normalizeSkillId = (id) => {
|
|
|
3773
4337
|
return metadataById.has(prefixed) ? prefixed : normalized;
|
|
3774
4338
|
};
|
|
3775
4339
|
var getSkillsPath = () => process.env.CLOUDEVAL_SKILLS_PATH ?? defaultSkillsPath;
|
|
3776
|
-
var skillFilePath = (id) =>
|
|
4340
|
+
var skillFilePath = (id) => path3.join(getSkillsPath(), id, "SKILL.md");
|
|
3777
4341
|
var fileExists = (filePath) => {
|
|
3778
4342
|
try {
|
|
3779
4343
|
return fsSync.statSync(filePath).isFile();
|
|
@@ -3785,7 +4349,7 @@ var readSkillFile = async (id) => {
|
|
|
3785
4349
|
const filePath = skillFilePath(id);
|
|
3786
4350
|
if (fileExists(filePath)) {
|
|
3787
4351
|
return {
|
|
3788
|
-
content: await
|
|
4352
|
+
content: await fs4.readFile(filePath, "utf8"),
|
|
3789
4353
|
path: filePath,
|
|
3790
4354
|
source: "filesystem"
|
|
3791
4355
|
};
|
|
@@ -3982,7 +4546,7 @@ var writeSkillList = async (options) => {
|
|
|
3982
4546
|
if (format === "table" || format === "text") {
|
|
3983
4547
|
const text = renderSkillsTable(skills);
|
|
3984
4548
|
if (options.output) {
|
|
3985
|
-
await
|
|
4549
|
+
await fs5.writeFile(options.output, text, "utf8");
|
|
3986
4550
|
return;
|
|
3987
4551
|
}
|
|
3988
4552
|
process.stdout.write(text);
|
|
@@ -3991,7 +4555,7 @@ var writeSkillList = async (options) => {
|
|
|
3991
4555
|
if (format === "markdown") {
|
|
3992
4556
|
const text = renderSkillsMarkdown(skills);
|
|
3993
4557
|
if (options.output) {
|
|
3994
|
-
await
|
|
4558
|
+
await fs5.writeFile(options.output, text, "utf8");
|
|
3995
4559
|
return;
|
|
3996
4560
|
}
|
|
3997
4561
|
process.stdout.write(text);
|
|
@@ -4014,7 +4578,7 @@ var writeSkillShow = async (id, options) => {
|
|
|
4014
4578
|
const format = options.format ?? "markdown";
|
|
4015
4579
|
if (format === "markdown" || format === "text") {
|
|
4016
4580
|
if (options.output) {
|
|
4017
|
-
await
|
|
4581
|
+
await fs5.writeFile(options.output, skill.content, "utf8");
|
|
4018
4582
|
return;
|
|
4019
4583
|
}
|
|
4020
4584
|
process.stdout.write(skill.content);
|
|
@@ -4069,7 +4633,7 @@ var writeSkillDoctor = async (options) => {
|
|
|
4069
4633
|
""
|
|
4070
4634
|
].filter(Boolean).join("\n");
|
|
4071
4635
|
if (options.output) {
|
|
4072
|
-
await
|
|
4636
|
+
await fs5.writeFile(options.output, `${text}
|
|
4073
4637
|
`, "utf8");
|
|
4074
4638
|
return;
|
|
4075
4639
|
}
|
|
@@ -4101,7 +4665,7 @@ var registerSkillsCommand = (program2) => {
|
|
|
4101
4665
|
const text = `${skillsPath}
|
|
4102
4666
|
`;
|
|
4103
4667
|
if (options.output) {
|
|
4104
|
-
await
|
|
4668
|
+
await fs5.writeFile(options.output, text, "utf8");
|
|
4105
4669
|
return;
|
|
4106
4670
|
}
|
|
4107
4671
|
process.stdout.write(text);
|
|
@@ -4274,10 +4838,10 @@ var registerOpenCommand = (program2, deps) => {
|
|
|
4274
4838
|
};
|
|
4275
4839
|
|
|
4276
4840
|
// src/projectsCommand.ts
|
|
4277
|
-
import
|
|
4278
|
-
import
|
|
4841
|
+
import path4 from "path";
|
|
4842
|
+
import fs6 from "fs/promises";
|
|
4279
4843
|
import os from "os";
|
|
4280
|
-
import { spawn } from "child_process";
|
|
4844
|
+
import { spawn as spawn2 } from "child_process";
|
|
4281
4845
|
|
|
4282
4846
|
// src/projectDiagramImage.ts
|
|
4283
4847
|
var trimTrailingSlash = (value) => value.replace(/\/+$/, "");
|
|
@@ -4434,50 +4998,6 @@ var downloadProjectDiagramImage = async (input) => {
|
|
|
4434
4998
|
};
|
|
4435
4999
|
};
|
|
4436
5000
|
|
|
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
5001
|
// src/graphClient.ts
|
|
4482
5002
|
var normalizeGraphInsightFocus = (focus) => {
|
|
4483
5003
|
const normalized = String(focus ?? "overview").trim().toLowerCase();
|
|
@@ -4637,7 +5157,7 @@ var writeProjectListOutput = async ({
|
|
|
4637
5157
|
});
|
|
4638
5158
|
}
|
|
4639
5159
|
if (options.output) {
|
|
4640
|
-
await
|
|
5160
|
+
await fs6.writeFile(options.output, text, "utf8");
|
|
4641
5161
|
return;
|
|
4642
5162
|
}
|
|
4643
5163
|
process.stdout.write(text);
|
|
@@ -4646,10 +5166,10 @@ var fileBlob = async (filePath) => {
|
|
|
4646
5166
|
if (!filePath) {
|
|
4647
5167
|
return void 0;
|
|
4648
5168
|
}
|
|
4649
|
-
const bytes = await
|
|
5169
|
+
const bytes = await fs6.readFile(filePath);
|
|
4650
5170
|
return {
|
|
4651
5171
|
blob: new Blob([bytes], { type: "application/json" }),
|
|
4652
|
-
name:
|
|
5172
|
+
name: path4.basename(filePath)
|
|
4653
5173
|
};
|
|
4654
5174
|
};
|
|
4655
5175
|
var normalizeWorkspacePath = (value) => value.replace(/\\/g, "/").replace(/^\/+/, "").replace(/^\.\//, "").split("/").filter((part) => part && part !== ".").join("/");
|
|
@@ -4687,7 +5207,7 @@ var generateWorkspaceConfig = (entry, parameters, sourceEntry) => {
|
|
|
4687
5207
|
].filter((line) => line.length > 0).join("\n");
|
|
4688
5208
|
};
|
|
4689
5209
|
var runCommand = (command, args) => new Promise((resolve, reject) => {
|
|
4690
|
-
const child =
|
|
5210
|
+
const child = spawn2(command, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
4691
5211
|
const stdout = [];
|
|
4692
5212
|
const stderr = [];
|
|
4693
5213
|
child.stdout.on("data", (chunk) => stdout.push(Buffer.from(chunk)));
|
|
@@ -4711,9 +5231,9 @@ var runCommand = (command, args) => new Promise((resolve, reject) => {
|
|
|
4711
5231
|
});
|
|
4712
5232
|
var isBicepPath = (filePath) => /\.bicep$/i.test(filePath);
|
|
4713
5233
|
var compiledBicepPathFor = (entry) => {
|
|
4714
|
-
const parsed =
|
|
5234
|
+
const parsed = path4.posix.parse(entry);
|
|
4715
5235
|
return normalizeWorkspacePath(
|
|
4716
|
-
|
|
5236
|
+
path4.posix.join(
|
|
4717
5237
|
".cloudeval/template-cache/compiled",
|
|
4718
5238
|
parsed.dir,
|
|
4719
5239
|
`${parsed.name}.json`
|
|
@@ -4721,18 +5241,18 @@ var compiledBicepPathFor = (entry) => {
|
|
|
4721
5241
|
);
|
|
4722
5242
|
};
|
|
4723
5243
|
var compileBicepEntry = async (root, entry) => {
|
|
4724
|
-
const tempDir = await
|
|
4725
|
-
const outputPath =
|
|
5244
|
+
const tempDir = await fs6.mkdtemp(path4.join(os.tmpdir(), "cloudeval-bicep-"));
|
|
5245
|
+
const outputPath = path4.join(tempDir, "compiled.json");
|
|
4726
5246
|
try {
|
|
4727
5247
|
await runCommand("az", [
|
|
4728
5248
|
"bicep",
|
|
4729
5249
|
"build",
|
|
4730
5250
|
"--file",
|
|
4731
|
-
|
|
5251
|
+
path4.join(root, entry),
|
|
4732
5252
|
"--outfile",
|
|
4733
5253
|
outputPath
|
|
4734
5254
|
]);
|
|
4735
|
-
const bytes = await
|
|
5255
|
+
const bytes = await fs6.readFile(outputPath);
|
|
4736
5256
|
return {
|
|
4737
5257
|
path: compiledBicepPathFor(entry),
|
|
4738
5258
|
blob: new Blob([bytes], { type: "application/json" })
|
|
@@ -4746,16 +5266,16 @@ var compileBicepEntry = async (root, entry) => {
|
|
|
4746
5266
|
}
|
|
4747
5267
|
throw new Error(`Failed to compile Bicep workspace entry '${entry}': ${message}`);
|
|
4748
5268
|
} finally {
|
|
4749
|
-
await
|
|
5269
|
+
await fs6.rm(tempDir, { recursive: true, force: true });
|
|
4750
5270
|
}
|
|
4751
5271
|
};
|
|
4752
5272
|
var collectWorkspacePaths = async (root) => {
|
|
4753
5273
|
const paths = [];
|
|
4754
5274
|
const visit = async (directory) => {
|
|
4755
|
-
const entries = await
|
|
5275
|
+
const entries = await fs6.readdir(directory, { withFileTypes: true });
|
|
4756
5276
|
for (const entry of entries) {
|
|
4757
|
-
const absolute =
|
|
4758
|
-
const relative = normalizeWorkspacePath(
|
|
5277
|
+
const absolute = path4.join(directory, entry.name);
|
|
5278
|
+
const relative = normalizeWorkspacePath(path4.relative(root, absolute));
|
|
4759
5279
|
if (!relative || isIgnoredWorkspacePath(relative)) {
|
|
4760
5280
|
continue;
|
|
4761
5281
|
}
|
|
@@ -4822,8 +5342,8 @@ var resolveWorkspaceParameters = (paths, explicitParameters, config) => {
|
|
|
4822
5342
|
return findFirstPath(paths, ["azuredeploy.parameters.json", "parameters.json"]);
|
|
4823
5343
|
};
|
|
4824
5344
|
var collectWorkspaceFiles = async (workspaceDir, options) => {
|
|
4825
|
-
const root =
|
|
4826
|
-
const stat = await
|
|
5345
|
+
const root = path4.resolve(workspaceDir);
|
|
5346
|
+
const stat = await fs6.stat(root).catch(() => void 0);
|
|
4827
5347
|
if (!stat?.isDirectory()) {
|
|
4828
5348
|
throw new Error(`--workspace-dir '${workspaceDir}' is not a directory.`);
|
|
4829
5349
|
}
|
|
@@ -4831,7 +5351,7 @@ var collectWorkspaceFiles = async (workspaceDir, options) => {
|
|
|
4831
5351
|
const existingConfigPath = paths.find(
|
|
4832
5352
|
(filePath) => filePath.toLowerCase() === ".cloudeval/config.yaml"
|
|
4833
5353
|
);
|
|
4834
|
-
const config = existingConfigPath ? readWorkspaceConfig(await
|
|
5354
|
+
const config = existingConfigPath ? readWorkspaceConfig(await fs6.readFile(path4.join(root, existingConfigPath), "utf8")) : void 0;
|
|
4835
5355
|
const entry = detectWorkspaceEntry(paths, options.workspaceEntry, config);
|
|
4836
5356
|
const parameters = resolveWorkspaceParameters(paths, options.workspaceParameters, config);
|
|
4837
5357
|
const compiledEntry = isBicepPath(entry) ? await compileBicepEntry(root, entry) : void 0;
|
|
@@ -4853,7 +5373,7 @@ var collectWorkspaceFiles = async (workspaceDir, options) => {
|
|
|
4853
5373
|
});
|
|
4854
5374
|
continue;
|
|
4855
5375
|
}
|
|
4856
|
-
const bytes = await
|
|
5376
|
+
const bytes = await fs6.readFile(path4.join(root, relativePath));
|
|
4857
5377
|
files.push({
|
|
4858
5378
|
path: relativePath,
|
|
4859
5379
|
blob: new Blob([bytes], {
|
|
@@ -4915,9 +5435,9 @@ var appendOptionValue = (value, previous = []) => [
|
|
|
4915
5435
|
value
|
|
4916
5436
|
];
|
|
4917
5437
|
var writeDiagramImageHeaders = async (outputPath, headers) => {
|
|
4918
|
-
await
|
|
5438
|
+
await fs6.mkdir(path4.dirname(outputPath), { recursive: true });
|
|
4919
5439
|
const text = Object.entries(headers).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `${key}: ${value}`).join("\n");
|
|
4920
|
-
await
|
|
5440
|
+
await fs6.writeFile(outputPath, `${text}
|
|
4921
5441
|
`, "utf8");
|
|
4922
5442
|
};
|
|
4923
5443
|
var listProjectsForContext = async (core, context) => {
|
|
@@ -5080,10 +5600,10 @@ var configureDiagramExportCommand = (command, deps) => addAuthOptions(command, d
|
|
|
5080
5600
|
publicGraph,
|
|
5081
5601
|
syncVersion: options.syncVersion
|
|
5082
5602
|
});
|
|
5083
|
-
const outputPath =
|
|
5084
|
-
const headersOutputPath = options.headersOutput ?
|
|
5085
|
-
await
|
|
5086
|
-
await
|
|
5603
|
+
const outputPath = path4.resolve(options.output);
|
|
5604
|
+
const headersOutputPath = options.headersOutput ? path4.resolve(options.headersOutput) : void 0;
|
|
5605
|
+
await fs6.mkdir(path4.dirname(outputPath), { recursive: true });
|
|
5606
|
+
await fs6.writeFile(outputPath, result.bytes);
|
|
5087
5607
|
const filesWritten = [outputPath];
|
|
5088
5608
|
if (headersOutputPath) {
|
|
5089
5609
|
await writeDiagramImageHeaders(headersOutputPath, result.headers);
|
|
@@ -5217,7 +5737,7 @@ var registerProjectsCommand = (program2, deps) => {
|
|
|
5217
5737
|
const parameters = await fileBlob(options.parametersFile);
|
|
5218
5738
|
const workspace = options.workspaceDir ? await collectWorkspaceFiles(options.workspaceDir, options) : void 0;
|
|
5219
5739
|
const cloudSync = resolveAzureCloudSyncInput(options);
|
|
5220
|
-
const inferredName = options.name || (options.workspaceDir ?
|
|
5740
|
+
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
5741
|
const result = await core.createQuickProject({
|
|
5222
5742
|
baseUrl: context.baseUrl,
|
|
5223
5743
|
authToken: context.token,
|
|
@@ -5321,8 +5841,8 @@ var writeConnectionsListOutput = async ({
|
|
|
5321
5841
|
if (format === "text") {
|
|
5322
5842
|
const text = renderConnectionsListText(data);
|
|
5323
5843
|
if (options.output) {
|
|
5324
|
-
const
|
|
5325
|
-
await
|
|
5844
|
+
const fs14 = await import("fs/promises");
|
|
5845
|
+
await fs14.writeFile(options.output, text, "utf8");
|
|
5326
5846
|
return;
|
|
5327
5847
|
}
|
|
5328
5848
|
process.stdout.write(text);
|
|
@@ -5656,8 +6176,8 @@ var write = async (command, data, options, frontendUrl) => {
|
|
|
5656
6176
|
const text = renderBillingText(command, data);
|
|
5657
6177
|
if (text) {
|
|
5658
6178
|
if (options.output) {
|
|
5659
|
-
const
|
|
5660
|
-
await
|
|
6179
|
+
const fs14 = await import("fs/promises");
|
|
6180
|
+
await fs14.writeFile(options.output, text, "utf8");
|
|
5661
6181
|
return;
|
|
5662
6182
|
}
|
|
5663
6183
|
process.stdout.write(text);
|
|
@@ -5926,14 +6446,14 @@ var registerBillingCommands = (program2, deps) => {
|
|
|
5926
6446
|
};
|
|
5927
6447
|
|
|
5928
6448
|
// src/mcpCommand.ts
|
|
5929
|
-
import
|
|
5930
|
-
import
|
|
6449
|
+
import fs9 from "fs/promises";
|
|
6450
|
+
import path6 from "path";
|
|
5931
6451
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
5932
6452
|
|
|
5933
6453
|
// src/mcpSetupCommand.ts
|
|
5934
|
-
import
|
|
6454
|
+
import fs7 from "fs/promises";
|
|
5935
6455
|
import os2 from "os";
|
|
5936
|
-
import
|
|
6456
|
+
import path5 from "path";
|
|
5937
6457
|
var MCP_SETUP_CLIENTS = ["codex", "claude", "cursor", "vscode", "generic"];
|
|
5938
6458
|
var CLIENTS = new Set(MCP_SETUP_CLIENTS);
|
|
5939
6459
|
var TOOLSETS = /* @__PURE__ */ new Set([
|
|
@@ -5959,7 +6479,7 @@ var normalizeMcpSetupToolset = (value) => {
|
|
|
5959
6479
|
};
|
|
5960
6480
|
var defaultConfigPath = (client) => {
|
|
5961
6481
|
if (client === "claude") {
|
|
5962
|
-
return
|
|
6482
|
+
return path5.join(
|
|
5963
6483
|
os2.homedir(),
|
|
5964
6484
|
"Library",
|
|
5965
6485
|
"Application Support",
|
|
@@ -5968,10 +6488,10 @@ var defaultConfigPath = (client) => {
|
|
|
5968
6488
|
);
|
|
5969
6489
|
}
|
|
5970
6490
|
if (client === "cursor") {
|
|
5971
|
-
return
|
|
6491
|
+
return path5.join(os2.homedir(), ".cursor", "mcp.json");
|
|
5972
6492
|
}
|
|
5973
6493
|
if (client === "vscode") {
|
|
5974
|
-
return
|
|
6494
|
+
return path5.join(process.cwd(), ".vscode", "mcp.json");
|
|
5975
6495
|
}
|
|
5976
6496
|
return void 0;
|
|
5977
6497
|
};
|
|
@@ -6081,7 +6601,7 @@ var formatMcpClientSetupText = (setup, options = {}) => {
|
|
|
6081
6601
|
};
|
|
6082
6602
|
var readJsonObject = async (filePath) => {
|
|
6083
6603
|
try {
|
|
6084
|
-
const raw = await
|
|
6604
|
+
const raw = await fs7.readFile(filePath, "utf8");
|
|
6085
6605
|
const parsed = JSON.parse(raw);
|
|
6086
6606
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
6087
6607
|
} catch (error) {
|
|
@@ -6106,8 +6626,8 @@ var writeMcpClientConfig = async (setup) => {
|
|
|
6106
6626
|
cloudeval: nextServer
|
|
6107
6627
|
}
|
|
6108
6628
|
};
|
|
6109
|
-
await
|
|
6110
|
-
await
|
|
6629
|
+
await fs7.mkdir(path5.dirname(setup.configPath), { recursive: true });
|
|
6630
|
+
await fs7.writeFile(setup.configPath, `${JSON.stringify(next, null, 2)}
|
|
6111
6631
|
`, {
|
|
6112
6632
|
encoding: "utf8",
|
|
6113
6633
|
mode: 384
|
|
@@ -6116,9 +6636,9 @@ var writeMcpClientConfig = async (setup) => {
|
|
|
6116
6636
|
};
|
|
6117
6637
|
|
|
6118
6638
|
// src/templateValidationClient.ts
|
|
6119
|
-
import
|
|
6639
|
+
import fs8 from "fs/promises";
|
|
6120
6640
|
var readJsonFile = async (filePath) => {
|
|
6121
|
-
const text = await
|
|
6641
|
+
const text = await fs8.readFile(filePath, "utf8");
|
|
6122
6642
|
try {
|
|
6123
6643
|
return JSON.parse(text);
|
|
6124
6644
|
} catch (error) {
|
|
@@ -6169,7 +6689,7 @@ var stringField = (value, field) => {
|
|
|
6169
6689
|
const raw = value?.[field];
|
|
6170
6690
|
return typeof raw === "string" && raw.trim() ? raw : void 0;
|
|
6171
6691
|
};
|
|
6172
|
-
var
|
|
6692
|
+
var extractJobId3 = (value) => {
|
|
6173
6693
|
const record = recordValue(value);
|
|
6174
6694
|
const job = recordValue(record?.job);
|
|
6175
6695
|
const data = recordValue(record?.data);
|
|
@@ -6177,7 +6697,7 @@ var extractJobId2 = (value) => {
|
|
|
6177
6697
|
return stringField(job, "job_id") ?? stringField(job, "jobId") ?? stringField(record, "job_id") ?? stringField(record, "jobId") ?? stringField(dataJob, "job_id") ?? stringField(dataJob, "jobId");
|
|
6178
6698
|
};
|
|
6179
6699
|
var normalizedStatus = (value) => String(recordValue(value)?.status ?? "").trim().toLowerCase();
|
|
6180
|
-
var
|
|
6700
|
+
var isTerminalJobStatus3 = (value) => [
|
|
6181
6701
|
"completed",
|
|
6182
6702
|
"succeeded",
|
|
6183
6703
|
"failed",
|
|
@@ -6326,7 +6846,7 @@ var getTemplateValidationJobResult = async (input) => fetchCloudEvalJson({
|
|
|
6326
6846
|
query: { user_id: input.userId }
|
|
6327
6847
|
});
|
|
6328
6848
|
var waitForTemplateValidationResult = async (input) => {
|
|
6329
|
-
const jobId =
|
|
6849
|
+
const jobId = extractJobId3(input.submitted);
|
|
6330
6850
|
if (!jobId) {
|
|
6331
6851
|
return input.submitted;
|
|
6332
6852
|
}
|
|
@@ -6336,7 +6856,7 @@ var waitForTemplateValidationResult = async (input) => {
|
|
|
6336
6856
|
let status;
|
|
6337
6857
|
for (; ; ) {
|
|
6338
6858
|
status = await getTemplateValidationJobStatus({ ...input, jobId });
|
|
6339
|
-
if (
|
|
6859
|
+
if (isTerminalJobStatus3(status)) {
|
|
6340
6860
|
break;
|
|
6341
6861
|
}
|
|
6342
6862
|
if (Date.now() >= deadline) {
|
|
@@ -6598,9 +7118,15 @@ var createApplicationInsightsClient = async (connectionString) => {
|
|
|
6598
7118
|
const client = new appInsights.TelemetryClient(connectionString, {
|
|
6599
7119
|
useGlobalProviders: false
|
|
6600
7120
|
});
|
|
6601
|
-
client
|
|
7121
|
+
disableDiskRetryCaching(client);
|
|
6602
7122
|
return client;
|
|
6603
7123
|
};
|
|
7124
|
+
var disableDiskRetryCaching = (client) => {
|
|
7125
|
+
try {
|
|
7126
|
+
client.setUseDiskRetryCaching?.(false);
|
|
7127
|
+
} catch {
|
|
7128
|
+
}
|
|
7129
|
+
};
|
|
6604
7130
|
var flushClient = async (client) => {
|
|
6605
7131
|
try {
|
|
6606
7132
|
const flush = client.flush;
|
|
@@ -8501,12 +9027,12 @@ var pickReportDownloadPayload2 = (value, view) => {
|
|
|
8501
9027
|
}
|
|
8502
9028
|
return value;
|
|
8503
9029
|
};
|
|
8504
|
-
var
|
|
9030
|
+
var extractJobId4 = (value) => {
|
|
8505
9031
|
if (!value || typeof value !== "object") return void 0;
|
|
8506
9032
|
const record = value;
|
|
8507
9033
|
return record.job_id ?? record.id ?? record.job?.job_id ?? record.job?.id ?? record.data?.job_id ?? record.data?.job?.job_id;
|
|
8508
9034
|
};
|
|
8509
|
-
var
|
|
9035
|
+
var isTerminalJobStatus4 = (value) => {
|
|
8510
9036
|
if (!value || typeof value !== "object") return true;
|
|
8511
9037
|
const status = String(
|
|
8512
9038
|
value.status ?? ""
|
|
@@ -8522,9 +9048,9 @@ var isTerminalJobStatus3 = (value) => {
|
|
|
8522
9048
|
};
|
|
8523
9049
|
var sleep3 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
8524
9050
|
var writeHeaderFile = async (outputPath, headers) => {
|
|
8525
|
-
await
|
|
9051
|
+
await fs9.mkdir(path6.dirname(outputPath), { recursive: true });
|
|
8526
9052
|
const text = Object.entries(headers).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `${key}: ${value}`).join("\n");
|
|
8527
|
-
await
|
|
9053
|
+
await fs9.writeFile(outputPath, `${text}
|
|
8528
9054
|
`, "utf8");
|
|
8529
9055
|
};
|
|
8530
9056
|
var resolveInvocationConfig = async (serverOptions, args) => {
|
|
@@ -8581,15 +9107,8 @@ var resolveProject2 = async (config, args, auth) => {
|
|
|
8581
9107
|
const requestedProjectId = stringValue(args.projectId) ?? config.defaultProjectId;
|
|
8582
9108
|
const userId = auth.user?.id;
|
|
8583
9109
|
if (!userId) {
|
|
8584
|
-
if (requestedProjectId) {
|
|
8585
|
-
return {
|
|
8586
|
-
id: requestedProjectId,
|
|
8587
|
-
name: "Selected Project",
|
|
8588
|
-
cloud_provider: "azure"
|
|
8589
|
-
};
|
|
8590
|
-
}
|
|
8591
9110
|
throw new Error(
|
|
8592
|
-
"Could not determine the authenticated user.
|
|
9111
|
+
"Could not determine the authenticated user. Run `cloudeval login` and retry."
|
|
8593
9112
|
);
|
|
8594
9113
|
}
|
|
8595
9114
|
const projects = await auth.core.getProjects(
|
|
@@ -8598,12 +9117,15 @@ var resolveProject2 = async (config, args, auth) => {
|
|
|
8598
9117
|
userId
|
|
8599
9118
|
);
|
|
8600
9119
|
if (requestedProjectId) {
|
|
8601
|
-
|
|
8602
|
-
id
|
|
8603
|
-
|
|
8604
|
-
|
|
8605
|
-
|
|
8606
|
-
|
|
9120
|
+
const match = projects.find(
|
|
9121
|
+
(project) => project.id === requestedProjectId
|
|
9122
|
+
);
|
|
9123
|
+
if (!match) {
|
|
9124
|
+
throw new Error(
|
|
9125
|
+
`Project ${requestedProjectId} was not found for authenticated user ${userId}. Run \`cloudeval projects list\` to choose a visible project.`
|
|
9126
|
+
);
|
|
9127
|
+
}
|
|
9128
|
+
return match;
|
|
8607
9129
|
}
|
|
8608
9130
|
const selected = projects.find((project) => project.name === "Playground") ?? projects[0];
|
|
8609
9131
|
if (selected) {
|
|
@@ -8687,7 +9209,7 @@ var assertModelAvailable = async (config, token) => {
|
|
|
8687
9209
|
}
|
|
8688
9210
|
};
|
|
8689
9211
|
var waitForReportJobs2 = async (input) => {
|
|
8690
|
-
const jobIds = input.submitted.map(
|
|
9212
|
+
const jobIds = input.submitted.map(extractJobId4).filter(Boolean);
|
|
8691
9213
|
if (!jobIds.length) {
|
|
8692
9214
|
return input.submitted;
|
|
8693
9215
|
}
|
|
@@ -8701,7 +9223,7 @@ var waitForReportJobs2 = async (input) => {
|
|
|
8701
9223
|
userId: input.userId,
|
|
8702
9224
|
jobId
|
|
8703
9225
|
});
|
|
8704
|
-
if (
|
|
9226
|
+
if (isTerminalJobStatus4(lastStatus)) {
|
|
8705
9227
|
break;
|
|
8706
9228
|
}
|
|
8707
9229
|
await sleep3(input.pollIntervalMs);
|
|
@@ -8770,13 +9292,13 @@ var downloadReports = async (config, args, auth) => {
|
|
|
8770
9292
|
const filesWritten = [];
|
|
8771
9293
|
if (outputPath) {
|
|
8772
9294
|
if (reportTypes.length > 1) {
|
|
8773
|
-
const stat = await
|
|
8774
|
-
const outputIsDirectory = stat?.isDirectory() || !
|
|
9295
|
+
const stat = await fs9.stat(outputPath).catch(() => void 0);
|
|
9296
|
+
const outputIsDirectory = stat?.isDirectory() || !path6.extname(outputPath);
|
|
8775
9297
|
if (outputIsDirectory) {
|
|
8776
|
-
await
|
|
9298
|
+
await fs9.mkdir(outputPath, { recursive: true });
|
|
8777
9299
|
for (const [key, value] of Object.entries(payload)) {
|
|
8778
|
-
const file =
|
|
8779
|
-
await
|
|
9300
|
+
const file = path6.join(outputPath, `${projectId}-${key}-report.json`);
|
|
9301
|
+
await fs9.writeFile(
|
|
8780
9302
|
file,
|
|
8781
9303
|
`${JSON.stringify(value, null, 2)}
|
|
8782
9304
|
`,
|
|
@@ -8785,8 +9307,8 @@ var downloadReports = async (config, args, auth) => {
|
|
|
8785
9307
|
filesWritten.push(file);
|
|
8786
9308
|
}
|
|
8787
9309
|
} else {
|
|
8788
|
-
await
|
|
8789
|
-
await
|
|
9310
|
+
await fs9.mkdir(path6.dirname(outputPath), { recursive: true });
|
|
9311
|
+
await fs9.writeFile(
|
|
8790
9312
|
outputPath,
|
|
8791
9313
|
`${JSON.stringify(data, null, 2)}
|
|
8792
9314
|
`,
|
|
@@ -8795,8 +9317,8 @@ var downloadReports = async (config, args, auth) => {
|
|
|
8795
9317
|
filesWritten.push(outputPath);
|
|
8796
9318
|
}
|
|
8797
9319
|
} else {
|
|
8798
|
-
await
|
|
8799
|
-
await
|
|
9320
|
+
await fs9.mkdir(path6.dirname(outputPath), { recursive: true });
|
|
9321
|
+
await fs9.writeFile(
|
|
8800
9322
|
outputPath,
|
|
8801
9323
|
`${JSON.stringify(data, null, 2)}
|
|
8802
9324
|
`,
|
|
@@ -9106,7 +9628,7 @@ var buildToolHandlers = (serverOptions) => {
|
|
|
9106
9628
|
if (!rawOutputPath) {
|
|
9107
9629
|
throw new Error("outputPath is required.");
|
|
9108
9630
|
}
|
|
9109
|
-
const outputPath =
|
|
9631
|
+
const outputPath = path6.resolve(rawOutputPath);
|
|
9110
9632
|
const publicGraph = booleanValue(args.public) ?? false;
|
|
9111
9633
|
const auth = publicGraph ? void 0 : await resolveAuth(config, { requireUser: true });
|
|
9112
9634
|
const layout = normalizeProjectDiagramImageLayout(stringValue(args.layout));
|
|
@@ -9127,11 +9649,11 @@ var buildToolHandlers = (serverOptions) => {
|
|
|
9127
9649
|
publicGraph,
|
|
9128
9650
|
syncVersion: stringValue(args.syncVersion)
|
|
9129
9651
|
});
|
|
9130
|
-
await
|
|
9131
|
-
await
|
|
9652
|
+
await fs9.mkdir(path6.dirname(outputPath), { recursive: true });
|
|
9653
|
+
await fs9.writeFile(outputPath, result.bytes);
|
|
9132
9654
|
const filesWritten = [outputPath];
|
|
9133
9655
|
const rawHeadersOutputPath = stringValue(args.headersOutputPath);
|
|
9134
|
-
const headersOutputPath = rawHeadersOutputPath ?
|
|
9656
|
+
const headersOutputPath = rawHeadersOutputPath ? path6.resolve(rawHeadersOutputPath) : void 0;
|
|
9135
9657
|
if (headersOutputPath) {
|
|
9136
9658
|
await writeHeaderFile(headersOutputPath, result.headers);
|
|
9137
9659
|
filesWritten.push(headersOutputPath);
|
|
@@ -10003,7 +10525,7 @@ var buildToolHandlers = (serverOptions) => {
|
|
|
10003
10525
|
projectId,
|
|
10004
10526
|
type,
|
|
10005
10527
|
submitted,
|
|
10006
|
-
jobs: submitted.map(
|
|
10528
|
+
jobs: submitted.map(extractJobId4).filter(Boolean),
|
|
10007
10529
|
finalStatuses
|
|
10008
10530
|
},
|
|
10009
10531
|
frontendUrl: reportsFrontendUrl(config, { projectId, type })
|
|
@@ -10856,7 +11378,7 @@ var registerMcpCommand = (program2, deps) => {
|
|
|
10856
11378
|
note
|
|
10857
11379
|
});
|
|
10858
11380
|
if (options.output) {
|
|
10859
|
-
await
|
|
11381
|
+
await fs9.writeFile(options.output, text, "utf8");
|
|
10860
11382
|
} else {
|
|
10861
11383
|
process.stdout.write(text);
|
|
10862
11384
|
}
|
|
@@ -11048,23 +11570,23 @@ Discovery:
|
|
|
11048
11570
|
};
|
|
11049
11571
|
|
|
11050
11572
|
// src/credentialsCommand.ts
|
|
11051
|
-
var
|
|
11573
|
+
var asRecord2 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
11052
11574
|
var arrayFromPayload = (payload, key) => {
|
|
11053
|
-
const record =
|
|
11575
|
+
const record = asRecord2(payload);
|
|
11054
11576
|
const value = record[key] ?? record.data;
|
|
11055
11577
|
return Array.isArray(value) ? value.filter((item) => item && typeof item === "object") : [];
|
|
11056
11578
|
};
|
|
11057
11579
|
var credentialFromPayload = (payload) => {
|
|
11058
|
-
const record =
|
|
11059
|
-
return
|
|
11580
|
+
const record = asRecord2(payload);
|
|
11581
|
+
return asRecord2(record.credential ?? record.data ?? record);
|
|
11060
11582
|
};
|
|
11061
11583
|
var secretFromPayload = (payload) => {
|
|
11062
|
-
const record =
|
|
11584
|
+
const record = asRecord2(payload);
|
|
11063
11585
|
const value = record.access_key ?? record.accessKey ?? record.secret;
|
|
11064
11586
|
return typeof value === "string" ? value : void 0;
|
|
11065
11587
|
};
|
|
11066
11588
|
var projectIdFromPayload = (payload, fallback) => {
|
|
11067
|
-
const record =
|
|
11589
|
+
const record = asRecord2(payload);
|
|
11068
11590
|
const credential = credentialFromPayload(payload);
|
|
11069
11591
|
const value = record.project_id ?? record.projectId ?? credential.project_id ?? credential.projectId ?? (Array.isArray(credential.project_ids) ? credential.project_ids[0] : void 0) ?? fallback;
|
|
11070
11592
|
return typeof value === "string" ? value : void 0;
|
|
@@ -11098,7 +11620,7 @@ var writeCredentialOutput = async (input) => {
|
|
|
11098
11620
|
return;
|
|
11099
11621
|
}
|
|
11100
11622
|
if (input.format === "text" || !input.format) {
|
|
11101
|
-
const record =
|
|
11623
|
+
const record = asRecord2(input.data);
|
|
11102
11624
|
const credentials = arrayFromPayload(input.data, "credentials").length > 0 ? arrayFromPayload(input.data, "credentials") : record.credential ? [credentialFromPayload(input.data)] : arrayFromPayload(input.data, "templates");
|
|
11103
11625
|
if (credentials.length) {
|
|
11104
11626
|
process.stdout.write(formatTextTable(formatCredentialTextRows(credentials)));
|
|
@@ -11222,9 +11744,9 @@ import { randomUUID as randomUUID4 } from "crypto";
|
|
|
11222
11744
|
// src/localHooks.ts
|
|
11223
11745
|
import { exec } from "child_process";
|
|
11224
11746
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
11225
|
-
import
|
|
11747
|
+
import fs10 from "fs/promises";
|
|
11226
11748
|
import os4 from "os";
|
|
11227
|
-
import
|
|
11749
|
+
import path7 from "path";
|
|
11228
11750
|
var normalizeHooks = (config, event) => {
|
|
11229
11751
|
if (config.hooks?.enabled !== true) {
|
|
11230
11752
|
return [];
|
|
@@ -11235,11 +11757,11 @@ var normalizeHooks = (config, event) => {
|
|
|
11235
11757
|
) : [];
|
|
11236
11758
|
};
|
|
11237
11759
|
var writeHookPayload = async (input, hook) => {
|
|
11238
|
-
const filePath =
|
|
11760
|
+
const filePath = path7.join(
|
|
11239
11761
|
os4.tmpdir(),
|
|
11240
11762
|
`cloudeval-hook-${process.pid}-${randomUUID3()}.json`
|
|
11241
11763
|
);
|
|
11242
|
-
await
|
|
11764
|
+
await fs10.writeFile(
|
|
11243
11765
|
filePath,
|
|
11244
11766
|
JSON.stringify(
|
|
11245
11767
|
{
|
|
@@ -11315,7 +11837,7 @@ var runLocalHooks = async (input) => {
|
|
|
11315
11837
|
throw error;
|
|
11316
11838
|
}
|
|
11317
11839
|
} finally {
|
|
11318
|
-
await
|
|
11840
|
+
await fs10.rm(payloadPath, { force: true }).catch(() => void 0);
|
|
11319
11841
|
}
|
|
11320
11842
|
}
|
|
11321
11843
|
return warnings;
|
|
@@ -11627,7 +12149,7 @@ var registerAgentsCommand = (program2, deps) => {
|
|
|
11627
12149
|
|
|
11628
12150
|
// src/validateCommand.ts
|
|
11629
12151
|
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");
|
|
11630
|
-
var
|
|
12152
|
+
var parsePositiveInteger2 = (value, optionName = "--max-results") => {
|
|
11631
12153
|
if (!value) {
|
|
11632
12154
|
return void 0;
|
|
11633
12155
|
}
|
|
@@ -11661,7 +12183,7 @@ var registerValidateCommand = (program2, deps) => {
|
|
|
11661
12183
|
category: options.category,
|
|
11662
12184
|
pillar: options.pillar,
|
|
11663
12185
|
minSeverity: options.minSeverity,
|
|
11664
|
-
maxResults:
|
|
12186
|
+
maxResults: parsePositiveInteger2(options.maxResults),
|
|
11665
12187
|
projectId: options.project,
|
|
11666
12188
|
saveReport: options.saveReport
|
|
11667
12189
|
});
|
|
@@ -11670,11 +12192,11 @@ var registerValidateCommand = (program2, deps) => {
|
|
|
11670
12192
|
authToken: context.token,
|
|
11671
12193
|
userId: context.user.id,
|
|
11672
12194
|
submitted,
|
|
11673
|
-
pollIntervalMs:
|
|
12195
|
+
pollIntervalMs: parsePositiveInteger2(
|
|
11674
12196
|
options.pollInterval,
|
|
11675
12197
|
"--poll-interval"
|
|
11676
12198
|
),
|
|
11677
|
-
waitTimeoutMs:
|
|
12199
|
+
waitTimeoutMs: parsePositiveInteger2(
|
|
11678
12200
|
options.waitTimeout,
|
|
11679
12201
|
"--wait-timeout"
|
|
11680
12202
|
)
|
|
@@ -11734,11 +12256,11 @@ var registerValidateCommand = (program2, deps) => {
|
|
|
11734
12256
|
authToken: context.token,
|
|
11735
12257
|
userId: context.user.id,
|
|
11736
12258
|
submitted,
|
|
11737
|
-
pollIntervalMs:
|
|
12259
|
+
pollIntervalMs: parsePositiveInteger2(
|
|
11738
12260
|
options.pollInterval,
|
|
11739
12261
|
"--poll-interval"
|
|
11740
12262
|
),
|
|
11741
|
-
waitTimeoutMs:
|
|
12263
|
+
waitTimeoutMs: parsePositiveInteger2(
|
|
11742
12264
|
options.waitTimeout,
|
|
11743
12265
|
"--wait-timeout"
|
|
11744
12266
|
)
|
|
@@ -11859,10 +12381,10 @@ var registerConfigCommand = (program2) => {
|
|
|
11859
12381
|
const profile = resolveProfile(options, command);
|
|
11860
12382
|
const current = await loadCliConfig(profile);
|
|
11861
12383
|
const next = writeCliConfigValue(current, key, value);
|
|
11862
|
-
const
|
|
12384
|
+
const path11 = await saveCliConfig(next, profile);
|
|
11863
12385
|
await writeFormattedOutput({
|
|
11864
12386
|
command: "config set",
|
|
11865
|
-
data: { profile, path:
|
|
12387
|
+
data: { profile, path: path11, config: next },
|
|
11866
12388
|
format: options.format,
|
|
11867
12389
|
output: options.output
|
|
11868
12390
|
});
|
|
@@ -11873,10 +12395,10 @@ var registerConfigCommand = (program2) => {
|
|
|
11873
12395
|
const profile = resolveProfile(options, command);
|
|
11874
12396
|
const current = await loadCliConfig(profile);
|
|
11875
12397
|
const next = unsetCliConfigValue(current, key);
|
|
11876
|
-
const
|
|
12398
|
+
const path11 = await saveCliConfig(next, profile);
|
|
11877
12399
|
await writeFormattedOutput({
|
|
11878
12400
|
command: "config unset",
|
|
11879
|
-
data: { profile, path:
|
|
12401
|
+
data: { profile, path: path11, config: next },
|
|
11880
12402
|
format: options.format,
|
|
11881
12403
|
output: options.output
|
|
11882
12404
|
});
|
|
@@ -12173,8 +12695,8 @@ var writeModelsListOutput = async (input) => {
|
|
|
12173
12695
|
defaultModel: input.defaultModel
|
|
12174
12696
|
});
|
|
12175
12697
|
if (input.options.output) {
|
|
12176
|
-
const
|
|
12177
|
-
await
|
|
12698
|
+
const fs14 = await import("fs/promises");
|
|
12699
|
+
await fs14.writeFile(input.options.output, text, "utf8");
|
|
12178
12700
|
return;
|
|
12179
12701
|
}
|
|
12180
12702
|
process.stdout.write(text);
|
|
@@ -12240,10 +12762,10 @@ var registerModelsCommand = (program2, deps) => {
|
|
|
12240
12762
|
const profile = options.profile || getActiveConfigProfile(command);
|
|
12241
12763
|
const config = await loadCliConfig(profile);
|
|
12242
12764
|
const next = { ...config, model };
|
|
12243
|
-
const
|
|
12765
|
+
const path11 = await saveCliConfig(next, profile);
|
|
12244
12766
|
await writeFormattedOutput({
|
|
12245
12767
|
command: "models default set",
|
|
12246
|
-
data: { profile, path:
|
|
12768
|
+
data: { profile, path: path11, model },
|
|
12247
12769
|
format: options.format,
|
|
12248
12770
|
output: options.output
|
|
12249
12771
|
});
|
|
@@ -12286,8 +12808,8 @@ var writeSessionTableOutput = async (command, data, options) => {
|
|
|
12286
12808
|
if (format === "text") {
|
|
12287
12809
|
const text = renderSessionsTable(data);
|
|
12288
12810
|
if (options.output) {
|
|
12289
|
-
const
|
|
12290
|
-
await
|
|
12811
|
+
const fs14 = await import("fs/promises");
|
|
12812
|
+
await fs14.writeFile(options.output, text, "utf8");
|
|
12291
12813
|
return;
|
|
12292
12814
|
}
|
|
12293
12815
|
process.stdout.write(text);
|
|
@@ -12459,12 +12981,12 @@ var registerSetupCommand = (program2, defaultBaseUrl = CLOUD_BASE_URL) => {
|
|
|
12459
12981
|
rl.close();
|
|
12460
12982
|
}
|
|
12461
12983
|
}
|
|
12462
|
-
const
|
|
12984
|
+
const path11 = await saveCliConfig(next, profile);
|
|
12463
12985
|
await writeFormattedOutput({
|
|
12464
12986
|
command: "setup",
|
|
12465
12987
|
data: {
|
|
12466
12988
|
profile,
|
|
12467
|
-
path:
|
|
12989
|
+
path: path11,
|
|
12468
12990
|
config: next,
|
|
12469
12991
|
nextSteps: [
|
|
12470
12992
|
"Run `cloudeval auth status` to inspect authentication.",
|
|
@@ -12479,9 +13001,9 @@ var registerSetupCommand = (program2, defaultBaseUrl = CLOUD_BASE_URL) => {
|
|
|
12479
13001
|
};
|
|
12480
13002
|
|
|
12481
13003
|
// src/updateCommand.ts
|
|
12482
|
-
import { spawn as
|
|
12483
|
-
import
|
|
12484
|
-
import
|
|
13004
|
+
import { spawn as spawn3 } from "child_process";
|
|
13005
|
+
import fs11 from "fs/promises";
|
|
13006
|
+
import path8 from "path";
|
|
12485
13007
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
12486
13008
|
var DEFAULT_LATEST_RELEASE_URL = "https://api.github.com/repos/ganakailabs/cloudeval-cli/releases/latest";
|
|
12487
13009
|
var DEFAULT_INSTALLER_URL = "https://cli.cloudeval.ai/install.sh";
|
|
@@ -12598,7 +13120,7 @@ var runInstaller = async ({
|
|
|
12598
13120
|
installerUrl,
|
|
12599
13121
|
targetTag,
|
|
12600
13122
|
fetchImpl = fetch,
|
|
12601
|
-
spawnImpl =
|
|
13123
|
+
spawnImpl = spawn3,
|
|
12602
13124
|
output = process.stderr,
|
|
12603
13125
|
platform = process.platform,
|
|
12604
13126
|
env = process.env,
|
|
@@ -12620,12 +13142,12 @@ var runInstaller = async ({
|
|
|
12620
13142
|
const installerScript = await response.text();
|
|
12621
13143
|
if (usePowerShellInstaller) {
|
|
12622
13144
|
const configDir = getCloudevalConfigDir();
|
|
12623
|
-
await
|
|
12624
|
-
const scriptPath =
|
|
12625
|
-
await
|
|
13145
|
+
await fs11.mkdir(configDir, { recursive: true, mode: 448 });
|
|
13146
|
+
const scriptPath = path8.join(
|
|
13147
|
+
await fs11.mkdtemp(path8.join(configDir, "installer-")),
|
|
12626
13148
|
"install.ps1"
|
|
12627
13149
|
);
|
|
12628
|
-
await
|
|
13150
|
+
await fs11.writeFile(scriptPath, installerScript, "utf8");
|
|
12629
13151
|
const child2 = spawnImpl(
|
|
12630
13152
|
"pwsh",
|
|
12631
13153
|
[
|
|
@@ -12653,7 +13175,7 @@ var runInstaller = async ({
|
|
|
12653
13175
|
);
|
|
12654
13176
|
});
|
|
12655
13177
|
child2.once("close", (code) => {
|
|
12656
|
-
void
|
|
13178
|
+
void fs11.rm(path8.dirname(scriptPath), { recursive: true, force: true });
|
|
12657
13179
|
if (code === 0) {
|
|
12658
13180
|
resolve();
|
|
12659
13181
|
return;
|
|
@@ -12693,10 +13215,10 @@ var runInstaller = async ({
|
|
|
12693
13215
|
child.stdin.end(installerScript);
|
|
12694
13216
|
});
|
|
12695
13217
|
};
|
|
12696
|
-
var getUpdateCachePath = () =>
|
|
13218
|
+
var getUpdateCachePath = () => path8.join(getCloudevalConfigDir(), UPDATE_CACHE_FILE);
|
|
12697
13219
|
var readCache = async (cachePath) => {
|
|
12698
13220
|
try {
|
|
12699
|
-
const parsed = JSON.parse(await
|
|
13221
|
+
const parsed = JSON.parse(await fs11.readFile(cachePath, "utf8"));
|
|
12700
13222
|
if (parsed && typeof parsed === "object" && typeof parsed.checkedAt === "string" && typeof parsed.latestVersion === "string" && typeof parsed.latestTag === "string") {
|
|
12701
13223
|
return parsed;
|
|
12702
13224
|
}
|
|
@@ -12708,8 +13230,8 @@ var readCache = async (cachePath) => {
|
|
|
12708
13230
|
return void 0;
|
|
12709
13231
|
};
|
|
12710
13232
|
var writeCache = async (cachePath, status) => {
|
|
12711
|
-
await
|
|
12712
|
-
await
|
|
13233
|
+
await fs11.mkdir(path8.dirname(cachePath), { recursive: true, mode: 448 });
|
|
13234
|
+
await fs11.writeFile(
|
|
12713
13235
|
cachePath,
|
|
12714
13236
|
`${JSON.stringify(
|
|
12715
13237
|
{
|
|
@@ -12921,7 +13443,7 @@ var registerUpdateCommand = (program2, registerOptions = {}) => {
|
|
|
12921
13443
|
if (options.format === "text" || !options.format) {
|
|
12922
13444
|
const text = formatUpdateStatusText(result);
|
|
12923
13445
|
if (options.output) {
|
|
12924
|
-
await
|
|
13446
|
+
await fs11.writeFile(options.output, text, "utf8");
|
|
12925
13447
|
return;
|
|
12926
13448
|
}
|
|
12927
13449
|
process.stdout.write(text);
|
|
@@ -12937,13 +13459,13 @@ var registerUpdateCommand = (program2, registerOptions = {}) => {
|
|
|
12937
13459
|
};
|
|
12938
13460
|
|
|
12939
13461
|
// src/uninstallCommand.ts
|
|
12940
|
-
import
|
|
13462
|
+
import fs12 from "fs/promises";
|
|
12941
13463
|
import os5 from "os";
|
|
12942
|
-
import
|
|
13464
|
+
import path9 from "path";
|
|
12943
13465
|
import { createInterface as createInterface3 } from "readline/promises";
|
|
12944
13466
|
var pathExists = async (candidate) => {
|
|
12945
13467
|
try {
|
|
12946
|
-
await
|
|
13468
|
+
await fs12.lstat(candidate);
|
|
12947
13469
|
return true;
|
|
12948
13470
|
} catch (error) {
|
|
12949
13471
|
if (error?.code === "ENOENT") {
|
|
@@ -12952,12 +13474,12 @@ var pathExists = async (candidate) => {
|
|
|
12952
13474
|
throw error;
|
|
12953
13475
|
}
|
|
12954
13476
|
};
|
|
12955
|
-
var installerBinDir = (home) =>
|
|
13477
|
+
var installerBinDir = (home) => path9.join(home, ".local", "bin");
|
|
12956
13478
|
var completionPaths = (home) => [
|
|
12957
|
-
|
|
12958
|
-
|
|
12959
|
-
|
|
12960
|
-
|
|
13479
|
+
path9.join(home, ".local", "share", "bash-completion", "completions", "cloudeval"),
|
|
13480
|
+
path9.join(home, ".zsh", "completions", "_cloudeval"),
|
|
13481
|
+
path9.join(home, ".config", "fish", "completions", "cloudeval.fish"),
|
|
13482
|
+
path9.join(home, ".config", "powershell", "cloudeval-completion.ps1")
|
|
12961
13483
|
];
|
|
12962
13484
|
var installerArtifactTargets = (home, platform) => {
|
|
12963
13485
|
const binDir = installerBinDir(home);
|
|
@@ -12965,37 +13487,37 @@ var installerArtifactTargets = (home, platform) => {
|
|
|
12965
13487
|
const targets = [
|
|
12966
13488
|
{
|
|
12967
13489
|
label: "cloudeval binary",
|
|
12968
|
-
path:
|
|
13490
|
+
path: path9.join(binDir, executableName),
|
|
12969
13491
|
kind: "file",
|
|
12970
13492
|
status: "missing"
|
|
12971
13493
|
},
|
|
12972
13494
|
{
|
|
12973
13495
|
label: "cloudeval binary",
|
|
12974
|
-
path:
|
|
13496
|
+
path: path9.join(binDir, "cloudeval"),
|
|
12975
13497
|
kind: "file",
|
|
12976
13498
|
status: "missing"
|
|
12977
13499
|
},
|
|
12978
13500
|
{
|
|
12979
13501
|
label: "eva alias",
|
|
12980
|
-
path:
|
|
13502
|
+
path: path9.join(binDir, "eva"),
|
|
12981
13503
|
kind: "file",
|
|
12982
13504
|
status: "missing"
|
|
12983
13505
|
},
|
|
12984
13506
|
{
|
|
12985
13507
|
label: "cloud alias",
|
|
12986
|
-
path:
|
|
13508
|
+
path: path9.join(binDir, "cloud"),
|
|
12987
13509
|
kind: "file",
|
|
12988
13510
|
status: "missing"
|
|
12989
13511
|
},
|
|
12990
13512
|
{
|
|
12991
13513
|
label: "Ink runtime asset",
|
|
12992
|
-
path:
|
|
13514
|
+
path: path9.join(binDir, "yoga.wasm"),
|
|
12993
13515
|
kind: "file",
|
|
12994
13516
|
status: "missing"
|
|
12995
13517
|
},
|
|
12996
13518
|
{
|
|
12997
13519
|
label: "license notices",
|
|
12998
|
-
path:
|
|
13520
|
+
path: path9.join(home, ".local", "share", "cloudeval", "licenses"),
|
|
12999
13521
|
kind: "directory",
|
|
13000
13522
|
status: "missing"
|
|
13001
13523
|
},
|
|
@@ -13011,11 +13533,11 @@ var installerArtifactTargets = (home, platform) => {
|
|
|
13011
13533
|
);
|
|
13012
13534
|
};
|
|
13013
13535
|
var shellProfilePaths = (home) => [
|
|
13014
|
-
|
|
13015
|
-
|
|
13016
|
-
|
|
13017
|
-
|
|
13018
|
-
|
|
13536
|
+
path9.join(home, ".bashrc"),
|
|
13537
|
+
path9.join(home, ".bash_profile"),
|
|
13538
|
+
path9.join(home, ".zshrc"),
|
|
13539
|
+
path9.join(home, ".profile"),
|
|
13540
|
+
path9.join(home, ".config", "fish", "config.fish")
|
|
13019
13541
|
];
|
|
13020
13542
|
var removeInstallerPathSnippet = (content, binDir) => {
|
|
13021
13543
|
const exportLine = `export PATH="${binDir}:$PATH"`;
|
|
@@ -13042,7 +13564,7 @@ var removeTarget = async (target, dryRun) => {
|
|
|
13042
13564
|
if (dryRun) {
|
|
13043
13565
|
return { ...target, status: "would_remove" };
|
|
13044
13566
|
}
|
|
13045
|
-
await
|
|
13567
|
+
await fs12.rm(target.path, { recursive: target.kind === "directory", force: true });
|
|
13046
13568
|
return { ...target, status: "removed" };
|
|
13047
13569
|
};
|
|
13048
13570
|
var updateShellProfile = async (profilePath, home, dryRun) => {
|
|
@@ -13054,7 +13576,7 @@ var updateShellProfile = async (profilePath, home, dryRun) => {
|
|
|
13054
13576
|
status: "missing"
|
|
13055
13577
|
};
|
|
13056
13578
|
}
|
|
13057
|
-
const content = await
|
|
13579
|
+
const content = await fs12.readFile(profilePath, "utf8");
|
|
13058
13580
|
const updated = removeInstallerPathSnippet(content, installerBinDir(home));
|
|
13059
13581
|
if (updated === void 0) {
|
|
13060
13582
|
return {
|
|
@@ -13072,7 +13594,7 @@ var updateShellProfile = async (profilePath, home, dryRun) => {
|
|
|
13072
13594
|
status: "would_update"
|
|
13073
13595
|
};
|
|
13074
13596
|
}
|
|
13075
|
-
await
|
|
13597
|
+
await fs12.writeFile(profilePath, updated, "utf8");
|
|
13076
13598
|
return {
|
|
13077
13599
|
label: "shell profile PATH entry",
|
|
13078
13600
|
path: profilePath,
|
|
@@ -13125,7 +13647,7 @@ var handleUninstallCommand = async (options, deps = {}) => {
|
|
|
13125
13647
|
}
|
|
13126
13648
|
const configTarget = {
|
|
13127
13649
|
label: "config",
|
|
13128
|
-
path:
|
|
13650
|
+
path: path9.join(home, ".config", "cloudeval"),
|
|
13129
13651
|
kind: "directory",
|
|
13130
13652
|
status: "kept"
|
|
13131
13653
|
};
|
|
@@ -13187,7 +13709,7 @@ var registerUninstallCommand = (program2) => {
|
|
|
13187
13709
|
if (options.format === "text" || !options.format) {
|
|
13188
13710
|
const text = formatUninstallResultText(result);
|
|
13189
13711
|
if (options.output) {
|
|
13190
|
-
await
|
|
13712
|
+
await fs12.writeFile(options.output, text, "utf8");
|
|
13191
13713
|
return;
|
|
13192
13714
|
}
|
|
13193
13715
|
process.stdout.write(text);
|
|
@@ -13308,6 +13830,7 @@ var resolveLoginOnboardingMode = (options) => {
|
|
|
13308
13830
|
import { jsx } from "react/jsx-runtime";
|
|
13309
13831
|
var DEFAULT_BASE_URL = getDefaultBaseUrl();
|
|
13310
13832
|
var ASK_STREAM_IDLE_TIMEOUT_MS2 = 9e4;
|
|
13833
|
+
var AGENT_STREAM_IDLE_TIMEOUT_MS = 18e4;
|
|
13311
13834
|
var LEGACY_API_KEY_MESSAGE = "API key auth was renamed in beta. Use --access-key or CLOUDEVAL_ACCESS_KEY.";
|
|
13312
13835
|
var STREAM_OUTPUT_NODES3 = /* @__PURE__ */ new Set([
|
|
13313
13836
|
"generate_response",
|
|
@@ -13333,21 +13856,21 @@ var completionScriptPath = (shell) => {
|
|
|
13333
13856
|
const home = os6.homedir();
|
|
13334
13857
|
switch (shell) {
|
|
13335
13858
|
case "bash":
|
|
13336
|
-
return
|
|
13859
|
+
return path10.join(home, ".local", "share", "bash-completion", "completions", "cloudeval");
|
|
13337
13860
|
case "zsh":
|
|
13338
|
-
return
|
|
13861
|
+
return path10.join(home, ".zsh", "completions", "_cloudeval");
|
|
13339
13862
|
case "fish":
|
|
13340
|
-
return
|
|
13863
|
+
return path10.join(home, ".config", "fish", "completions", "cloudeval.fish");
|
|
13341
13864
|
case "powershell":
|
|
13342
|
-
return
|
|
13865
|
+
return path10.join(home, ".config", "powershell", "cloudeval-completion.ps1");
|
|
13343
13866
|
}
|
|
13344
13867
|
};
|
|
13345
13868
|
var ZSH_FPATH_MARKER = "CloudEval CLI completions";
|
|
13346
13869
|
var ensureZshCompletionFpath = async () => {
|
|
13347
|
-
const zshrc =
|
|
13870
|
+
const zshrc = path10.join(os6.homedir(), ".zshrc");
|
|
13348
13871
|
let existing = "";
|
|
13349
13872
|
try {
|
|
13350
|
-
existing = await
|
|
13873
|
+
existing = await fs13.readFile(zshrc, "utf8");
|
|
13351
13874
|
} catch {
|
|
13352
13875
|
existing = "";
|
|
13353
13876
|
}
|
|
@@ -13358,12 +13881,12 @@ var ensureZshCompletionFpath = async () => {
|
|
|
13358
13881
|
# ${ZSH_FPATH_MARKER}
|
|
13359
13882
|
fpath=("$HOME/.zsh/completions" $fpath)
|
|
13360
13883
|
`;
|
|
13361
|
-
await
|
|
13884
|
+
await fs13.appendFile(zshrc, snippet, "utf8");
|
|
13362
13885
|
};
|
|
13363
13886
|
var installCompletionScript = async (shell, binaryName) => {
|
|
13364
13887
|
const scriptPath = completionScriptPath(shell);
|
|
13365
|
-
await
|
|
13366
|
-
await
|
|
13888
|
+
await fs13.mkdir(path10.dirname(scriptPath), { recursive: true });
|
|
13889
|
+
await fs13.writeFile(scriptPath, buildCompletionScript(shell, binaryName), "utf8");
|
|
13367
13890
|
if (shell === "zsh") {
|
|
13368
13891
|
await ensureZshCompletionFpath();
|
|
13369
13892
|
}
|
|
@@ -13371,7 +13894,7 @@ var installCompletionScript = async (shell, binaryName) => {
|
|
|
13371
13894
|
};
|
|
13372
13895
|
var uninstallCompletionScript = async (shell) => {
|
|
13373
13896
|
const scriptPath = completionScriptPath(shell);
|
|
13374
|
-
await
|
|
13897
|
+
await fs13.rm(scriptPath, { force: true });
|
|
13375
13898
|
return scriptPath;
|
|
13376
13899
|
};
|
|
13377
13900
|
var runInteractiveLoginOnboarding = async (baseUrl, token) => {
|
|
@@ -13940,6 +14463,12 @@ registerReportsCommand(program, {
|
|
|
13940
14463
|
resolveBaseUrl,
|
|
13941
14464
|
readStdinValue
|
|
13942
14465
|
});
|
|
14466
|
+
registerReviewCommand(program, {
|
|
14467
|
+
defaultBaseUrl: DEFAULT_BASE_URL,
|
|
14468
|
+
resolveBaseUrl,
|
|
14469
|
+
readStdinValue,
|
|
14470
|
+
isHeadlessEnvironment
|
|
14471
|
+
});
|
|
13943
14472
|
registerRecipesCommand(program, {
|
|
13944
14473
|
defaultBaseUrl: DEFAULT_BASE_URL,
|
|
13945
14474
|
resolveBaseUrl,
|
|
@@ -14096,7 +14625,7 @@ program.command("tui").description("Open the CloudEval Terminal UI").option(
|
|
|
14096
14625
|
const { assertSecureBaseUrl } = await import("./dist-CFLR5FML.js");
|
|
14097
14626
|
const [{ render }, { App }] = await Promise.all([
|
|
14098
14627
|
import("ink"),
|
|
14099
|
-
import("./App-
|
|
14628
|
+
import("./App-H46FRLWK.js")
|
|
14100
14629
|
]);
|
|
14101
14630
|
const baseUrl = await resolveBaseUrl(options, command);
|
|
14102
14631
|
assertSecureBaseUrl(baseUrl);
|
|
@@ -14154,7 +14683,7 @@ program.command("chat").description("Start an interactive chat session").option(
|
|
|
14154
14683
|
const { assertSecureBaseUrl } = await import("./dist-CFLR5FML.js");
|
|
14155
14684
|
const [{ render }, { App }] = await Promise.all([
|
|
14156
14685
|
import("ink"),
|
|
14157
|
-
import("./App-
|
|
14686
|
+
import("./App-H46FRLWK.js")
|
|
14158
14687
|
]);
|
|
14159
14688
|
const baseUrl = await resolveBaseUrl(options, command);
|
|
14160
14689
|
assertSecureBaseUrl(baseUrl);
|
|
@@ -14267,7 +14796,7 @@ program.command("ask").alias("agent").description("Ask a single question or run
|
|
|
14267
14796
|
});
|
|
14268
14797
|
}
|
|
14269
14798
|
try {
|
|
14270
|
-
const
|
|
14799
|
+
const fs14 = await import("fs");
|
|
14271
14800
|
const fsPromises = await import("fs/promises");
|
|
14272
14801
|
const { randomUUID: randomUUID5 } = await import("crypto");
|
|
14273
14802
|
const core = await import("./dist-CFLR5FML.js");
|
|
@@ -14275,8 +14804,6 @@ program.command("ask").alias("agent").description("Ask a single question or run
|
|
|
14275
14804
|
streamChat,
|
|
14276
14805
|
reduceChunk,
|
|
14277
14806
|
getAuthToken,
|
|
14278
|
-
getProjects,
|
|
14279
|
-
ensurePlaygroundProject,
|
|
14280
14807
|
checkUserStatus,
|
|
14281
14808
|
extractEmailFromToken,
|
|
14282
14809
|
initialChatState,
|
|
@@ -14375,68 +14902,39 @@ program.command("ask").alias("agent").description("Ask a single question or run
|
|
|
14375
14902
|
step: "project",
|
|
14376
14903
|
message: selectedProjectId ? `Using project ${selectedProjectId}` : "Resolving project"
|
|
14377
14904
|
});
|
|
14378
|
-
let project;
|
|
14379
14905
|
let authenticatedUserId;
|
|
14380
|
-
|
|
14381
|
-
|
|
14382
|
-
|
|
14383
|
-
|
|
14384
|
-
|
|
14385
|
-
|
|
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
|
-
const playgroundProject = projects.find((p) => p.name === "Playground");
|
|
14411
|
-
if (playgroundProject) {
|
|
14412
|
-
project = playgroundProject;
|
|
14413
|
-
} else if (userStatus.user?.email) {
|
|
14414
|
-
verboseLog("Playground project missing; running shared onboarding repair");
|
|
14415
|
-
project = await ensurePlaygroundProject(baseUrl, token, {
|
|
14416
|
-
id: authenticatedUserId,
|
|
14417
|
-
email: userStatus.user.email,
|
|
14418
|
-
full_name: userStatus.user.full_name,
|
|
14419
|
-
name: userStatus.user.name
|
|
14420
|
-
});
|
|
14421
|
-
} else {
|
|
14422
|
-
project = projects[0] || void 0;
|
|
14423
|
-
}
|
|
14424
|
-
verboseLog("Selected project:", project ? { id: project.id, name: project.name } : "none");
|
|
14425
|
-
}
|
|
14426
|
-
} catch (error) {
|
|
14427
|
-
verboseLog("Failed to fetch projects, using default:", {
|
|
14428
|
-
message: error.message,
|
|
14429
|
-
stack: error.stack
|
|
14430
|
-
});
|
|
14431
|
-
}
|
|
14432
|
-
if (!project) {
|
|
14433
|
-
process.stderr.write(
|
|
14434
|
-
"No project is available for this account. Run `cloudeval chat` to complete onboarding, then retry."
|
|
14435
|
-
);
|
|
14436
|
-
process.stderr.write("\n");
|
|
14437
|
-
await exitCli(1, new Error("no_project_available"));
|
|
14438
|
-
}
|
|
14906
|
+
let authenticatedUser;
|
|
14907
|
+
try {
|
|
14908
|
+
const userStatus = await checkUserStatus(baseUrl, token);
|
|
14909
|
+
getActiveCliTelemetry()?.setUserProperties(userStatus.user || {});
|
|
14910
|
+
authenticatedUserId = userStatus.user?.id;
|
|
14911
|
+
authenticatedUser = userStatus.user;
|
|
14912
|
+
verboseLog("User status:", {
|
|
14913
|
+
hasUser: !!userStatus.user,
|
|
14914
|
+
userId: userStatus.user?.id,
|
|
14915
|
+
onboardingCompleted: userStatus.onboardingCompleted
|
|
14916
|
+
});
|
|
14917
|
+
} catch (error) {
|
|
14918
|
+
verboseLog("Failed to check user status before project resolve:", {
|
|
14919
|
+
message: error.message
|
|
14920
|
+
});
|
|
14921
|
+
}
|
|
14922
|
+
const { resolveAskProject } = await import("./resolveAskProject-NK435I56.js");
|
|
14923
|
+
let project;
|
|
14924
|
+
try {
|
|
14925
|
+
project = await resolveAskProject({
|
|
14926
|
+
baseUrl,
|
|
14927
|
+
token,
|
|
14928
|
+
selectedProjectId,
|
|
14929
|
+
authenticatedUserId,
|
|
14930
|
+
authenticatedUser
|
|
14931
|
+
});
|
|
14932
|
+
} catch (error) {
|
|
14933
|
+
progressWriter.clear();
|
|
14934
|
+
console.error(error?.message ?? "Failed to resolve project");
|
|
14935
|
+
await exitCli(1, error);
|
|
14439
14936
|
}
|
|
14937
|
+
verboseLog("Selected project:", { id: project.id, name: project.name });
|
|
14440
14938
|
let userName = "You";
|
|
14441
14939
|
try {
|
|
14442
14940
|
const email = extractEmailFromToken(token);
|
|
@@ -14463,11 +14961,11 @@ program.command("ask").alias("agent").description("Ask a single question or run
|
|
|
14463
14961
|
console.error(`[${commandName}] Thread ID: ${threadId}`);
|
|
14464
14962
|
}
|
|
14465
14963
|
if (streamTextOutput && options.output) {
|
|
14466
|
-
fileOutputStream =
|
|
14964
|
+
fileOutputStream = fs14.createWriteStream(options.output, { encoding: "utf-8" });
|
|
14467
14965
|
outputStream = fileOutputStream;
|
|
14468
14966
|
}
|
|
14469
14967
|
if (ndjsonOutput && options.output) {
|
|
14470
|
-
ndjsonOutputStream =
|
|
14968
|
+
ndjsonOutputStream = fs14.createWriteStream(options.output, { encoding: "utf-8" });
|
|
14471
14969
|
}
|
|
14472
14970
|
const writeAskDataEvent = (event) => {
|
|
14473
14971
|
const line = `${JSON.stringify(event)}
|
|
@@ -14569,7 +15067,7 @@ program.command("ask").alias("agent").description("Ask a single question or run
|
|
|
14569
15067
|
debug: options.debug,
|
|
14570
15068
|
completeAfterResponse: true,
|
|
14571
15069
|
responseCompletionGraceMs: 5e3,
|
|
14572
|
-
streamIdleTimeoutMs: ASK_STREAM_IDLE_TIMEOUT_MS2,
|
|
15070
|
+
streamIdleTimeoutMs: selectedMode === "agent" ? AGENT_STREAM_IDLE_TIMEOUT_MS : ASK_STREAM_IDLE_TIMEOUT_MS2,
|
|
14573
15071
|
hitlResume
|
|
14574
15072
|
})) {
|
|
14575
15073
|
totalChunkCount++;
|
|
@@ -14748,7 +15246,24 @@ Error: ${errorMsg}
|
|
|
14748
15246
|
throw error;
|
|
14749
15247
|
}
|
|
14750
15248
|
const finalMessage = [...chatState.messages].reverse().find((m) => m.role === "assistant");
|
|
14751
|
-
|
|
15249
|
+
let finalResponse = collapseRepeatedAssistantText3(
|
|
15250
|
+
finalMessage?.content || responseText || ""
|
|
15251
|
+
);
|
|
15252
|
+
if (!finalResponse.trim() && chatState.threadId) {
|
|
15253
|
+
const { fetchLastAssistantContent } = await import("./fetchLastAssistantContent-RH6RMSQO.js");
|
|
15254
|
+
const persisted = await fetchLastAssistantContent({
|
|
15255
|
+
baseUrl,
|
|
15256
|
+
authToken: token,
|
|
15257
|
+
threadId: chatState.threadId,
|
|
15258
|
+
normalizeApiBase: normalizeApiBase2
|
|
15259
|
+
});
|
|
15260
|
+
if (persisted) {
|
|
15261
|
+
finalResponse = collapseRepeatedAssistantText3(persisted);
|
|
15262
|
+
verboseLog("Recovered final response from thread history", {
|
|
15263
|
+
length: finalResponse.length
|
|
15264
|
+
});
|
|
15265
|
+
}
|
|
15266
|
+
}
|
|
14752
15267
|
if (!finalResponse.trim()) {
|
|
14753
15268
|
const noResponseMessage = `No final response returned by CloudEval (last stream status: ${chatState.status ?? "unknown"}). Retry with --verbose or --format ndjson to inspect stream progress.`;
|
|
14754
15269
|
progressWriter.clear();
|
|
@@ -14922,7 +15437,7 @@ Error: ${errorMsg}
|
|
|
14922
15437
|
program.command("banner").description("Preview the startup banner and terminal capabilities").action(async () => {
|
|
14923
15438
|
const { render } = await import("ink");
|
|
14924
15439
|
const BannerPreview = React.lazy(async () => ({
|
|
14925
|
-
default: (await import("./Banner-
|
|
15440
|
+
default: (await import("./Banner-7X2VHUVH.js")).Banner
|
|
14926
15441
|
}));
|
|
14927
15442
|
render(
|
|
14928
15443
|
/* @__PURE__ */ jsx(React.Suspense, { fallback: null, children: /* @__PURE__ */ jsx(BannerPreview, { disable: false }) })
|