@ganakailabs/cloudeval-cli 0.24.4 → 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-NGGAERHH.js → App-H46FRLWK.js} +614 -516
- package/dist/{Banner-6POGUWTT.js → Banner-7X2VHUVH.js} +2 -2
- package/dist/{chunk-YEKKVQCN.js → chunk-LKVKOGVL.js} +1 -1
- package/dist/{chunk-ALXP3LJK.js → chunk-TISPT6EB.js} +1 -1
- package/dist/cli.js +818 -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-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) {
|
|
@@ -8507,12 +9027,12 @@ var pickReportDownloadPayload2 = (value, view) => {
|
|
|
8507
9027
|
}
|
|
8508
9028
|
return value;
|
|
8509
9029
|
};
|
|
8510
|
-
var
|
|
9030
|
+
var extractJobId4 = (value) => {
|
|
8511
9031
|
if (!value || typeof value !== "object") return void 0;
|
|
8512
9032
|
const record = value;
|
|
8513
9033
|
return record.job_id ?? record.id ?? record.job?.job_id ?? record.job?.id ?? record.data?.job_id ?? record.data?.job?.job_id;
|
|
8514
9034
|
};
|
|
8515
|
-
var
|
|
9035
|
+
var isTerminalJobStatus4 = (value) => {
|
|
8516
9036
|
if (!value || typeof value !== "object") return true;
|
|
8517
9037
|
const status = String(
|
|
8518
9038
|
value.status ?? ""
|
|
@@ -8528,9 +9048,9 @@ var isTerminalJobStatus3 = (value) => {
|
|
|
8528
9048
|
};
|
|
8529
9049
|
var sleep3 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
8530
9050
|
var writeHeaderFile = async (outputPath, headers) => {
|
|
8531
|
-
await
|
|
9051
|
+
await fs9.mkdir(path6.dirname(outputPath), { recursive: true });
|
|
8532
9052
|
const text = Object.entries(headers).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `${key}: ${value}`).join("\n");
|
|
8533
|
-
await
|
|
9053
|
+
await fs9.writeFile(outputPath, `${text}
|
|
8534
9054
|
`, "utf8");
|
|
8535
9055
|
};
|
|
8536
9056
|
var resolveInvocationConfig = async (serverOptions, args) => {
|
|
@@ -8587,15 +9107,8 @@ var resolveProject2 = async (config, args, auth) => {
|
|
|
8587
9107
|
const requestedProjectId = stringValue(args.projectId) ?? config.defaultProjectId;
|
|
8588
9108
|
const userId = auth.user?.id;
|
|
8589
9109
|
if (!userId) {
|
|
8590
|
-
if (requestedProjectId) {
|
|
8591
|
-
return {
|
|
8592
|
-
id: requestedProjectId,
|
|
8593
|
-
name: "Selected Project",
|
|
8594
|
-
cloud_provider: "azure"
|
|
8595
|
-
};
|
|
8596
|
-
}
|
|
8597
9110
|
throw new Error(
|
|
8598
|
-
"Could not determine the authenticated user.
|
|
9111
|
+
"Could not determine the authenticated user. Run `cloudeval login` and retry."
|
|
8599
9112
|
);
|
|
8600
9113
|
}
|
|
8601
9114
|
const projects = await auth.core.getProjects(
|
|
@@ -8604,12 +9117,15 @@ var resolveProject2 = async (config, args, auth) => {
|
|
|
8604
9117
|
userId
|
|
8605
9118
|
);
|
|
8606
9119
|
if (requestedProjectId) {
|
|
8607
|
-
|
|
8608
|
-
id
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
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;
|
|
8613
9129
|
}
|
|
8614
9130
|
const selected = projects.find((project) => project.name === "Playground") ?? projects[0];
|
|
8615
9131
|
if (selected) {
|
|
@@ -8693,7 +9209,7 @@ var assertModelAvailable = async (config, token) => {
|
|
|
8693
9209
|
}
|
|
8694
9210
|
};
|
|
8695
9211
|
var waitForReportJobs2 = async (input) => {
|
|
8696
|
-
const jobIds = input.submitted.map(
|
|
9212
|
+
const jobIds = input.submitted.map(extractJobId4).filter(Boolean);
|
|
8697
9213
|
if (!jobIds.length) {
|
|
8698
9214
|
return input.submitted;
|
|
8699
9215
|
}
|
|
@@ -8707,7 +9223,7 @@ var waitForReportJobs2 = async (input) => {
|
|
|
8707
9223
|
userId: input.userId,
|
|
8708
9224
|
jobId
|
|
8709
9225
|
});
|
|
8710
|
-
if (
|
|
9226
|
+
if (isTerminalJobStatus4(lastStatus)) {
|
|
8711
9227
|
break;
|
|
8712
9228
|
}
|
|
8713
9229
|
await sleep3(input.pollIntervalMs);
|
|
@@ -8776,13 +9292,13 @@ var downloadReports = async (config, args, auth) => {
|
|
|
8776
9292
|
const filesWritten = [];
|
|
8777
9293
|
if (outputPath) {
|
|
8778
9294
|
if (reportTypes.length > 1) {
|
|
8779
|
-
const stat = await
|
|
8780
|
-
const outputIsDirectory = stat?.isDirectory() || !
|
|
9295
|
+
const stat = await fs9.stat(outputPath).catch(() => void 0);
|
|
9296
|
+
const outputIsDirectory = stat?.isDirectory() || !path6.extname(outputPath);
|
|
8781
9297
|
if (outputIsDirectory) {
|
|
8782
|
-
await
|
|
9298
|
+
await fs9.mkdir(outputPath, { recursive: true });
|
|
8783
9299
|
for (const [key, value] of Object.entries(payload)) {
|
|
8784
|
-
const file =
|
|
8785
|
-
await
|
|
9300
|
+
const file = path6.join(outputPath, `${projectId}-${key}-report.json`);
|
|
9301
|
+
await fs9.writeFile(
|
|
8786
9302
|
file,
|
|
8787
9303
|
`${JSON.stringify(value, null, 2)}
|
|
8788
9304
|
`,
|
|
@@ -8791,8 +9307,8 @@ var downloadReports = async (config, args, auth) => {
|
|
|
8791
9307
|
filesWritten.push(file);
|
|
8792
9308
|
}
|
|
8793
9309
|
} else {
|
|
8794
|
-
await
|
|
8795
|
-
await
|
|
9310
|
+
await fs9.mkdir(path6.dirname(outputPath), { recursive: true });
|
|
9311
|
+
await fs9.writeFile(
|
|
8796
9312
|
outputPath,
|
|
8797
9313
|
`${JSON.stringify(data, null, 2)}
|
|
8798
9314
|
`,
|
|
@@ -8801,8 +9317,8 @@ var downloadReports = async (config, args, auth) => {
|
|
|
8801
9317
|
filesWritten.push(outputPath);
|
|
8802
9318
|
}
|
|
8803
9319
|
} else {
|
|
8804
|
-
await
|
|
8805
|
-
await
|
|
9320
|
+
await fs9.mkdir(path6.dirname(outputPath), { recursive: true });
|
|
9321
|
+
await fs9.writeFile(
|
|
8806
9322
|
outputPath,
|
|
8807
9323
|
`${JSON.stringify(data, null, 2)}
|
|
8808
9324
|
`,
|
|
@@ -9112,7 +9628,7 @@ var buildToolHandlers = (serverOptions) => {
|
|
|
9112
9628
|
if (!rawOutputPath) {
|
|
9113
9629
|
throw new Error("outputPath is required.");
|
|
9114
9630
|
}
|
|
9115
|
-
const outputPath =
|
|
9631
|
+
const outputPath = path6.resolve(rawOutputPath);
|
|
9116
9632
|
const publicGraph = booleanValue(args.public) ?? false;
|
|
9117
9633
|
const auth = publicGraph ? void 0 : await resolveAuth(config, { requireUser: true });
|
|
9118
9634
|
const layout = normalizeProjectDiagramImageLayout(stringValue(args.layout));
|
|
@@ -9133,11 +9649,11 @@ var buildToolHandlers = (serverOptions) => {
|
|
|
9133
9649
|
publicGraph,
|
|
9134
9650
|
syncVersion: stringValue(args.syncVersion)
|
|
9135
9651
|
});
|
|
9136
|
-
await
|
|
9137
|
-
await
|
|
9652
|
+
await fs9.mkdir(path6.dirname(outputPath), { recursive: true });
|
|
9653
|
+
await fs9.writeFile(outputPath, result.bytes);
|
|
9138
9654
|
const filesWritten = [outputPath];
|
|
9139
9655
|
const rawHeadersOutputPath = stringValue(args.headersOutputPath);
|
|
9140
|
-
const headersOutputPath = rawHeadersOutputPath ?
|
|
9656
|
+
const headersOutputPath = rawHeadersOutputPath ? path6.resolve(rawHeadersOutputPath) : void 0;
|
|
9141
9657
|
if (headersOutputPath) {
|
|
9142
9658
|
await writeHeaderFile(headersOutputPath, result.headers);
|
|
9143
9659
|
filesWritten.push(headersOutputPath);
|
|
@@ -10009,7 +10525,7 @@ var buildToolHandlers = (serverOptions) => {
|
|
|
10009
10525
|
projectId,
|
|
10010
10526
|
type,
|
|
10011
10527
|
submitted,
|
|
10012
|
-
jobs: submitted.map(
|
|
10528
|
+
jobs: submitted.map(extractJobId4).filter(Boolean),
|
|
10013
10529
|
finalStatuses
|
|
10014
10530
|
},
|
|
10015
10531
|
frontendUrl: reportsFrontendUrl(config, { projectId, type })
|
|
@@ -10862,7 +11378,7 @@ var registerMcpCommand = (program2, deps) => {
|
|
|
10862
11378
|
note
|
|
10863
11379
|
});
|
|
10864
11380
|
if (options.output) {
|
|
10865
|
-
await
|
|
11381
|
+
await fs9.writeFile(options.output, text, "utf8");
|
|
10866
11382
|
} else {
|
|
10867
11383
|
process.stdout.write(text);
|
|
10868
11384
|
}
|
|
@@ -11054,23 +11570,23 @@ Discovery:
|
|
|
11054
11570
|
};
|
|
11055
11571
|
|
|
11056
11572
|
// src/credentialsCommand.ts
|
|
11057
|
-
var
|
|
11573
|
+
var asRecord2 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
11058
11574
|
var arrayFromPayload = (payload, key) => {
|
|
11059
|
-
const record =
|
|
11575
|
+
const record = asRecord2(payload);
|
|
11060
11576
|
const value = record[key] ?? record.data;
|
|
11061
11577
|
return Array.isArray(value) ? value.filter((item) => item && typeof item === "object") : [];
|
|
11062
11578
|
};
|
|
11063
11579
|
var credentialFromPayload = (payload) => {
|
|
11064
|
-
const record =
|
|
11065
|
-
return
|
|
11580
|
+
const record = asRecord2(payload);
|
|
11581
|
+
return asRecord2(record.credential ?? record.data ?? record);
|
|
11066
11582
|
};
|
|
11067
11583
|
var secretFromPayload = (payload) => {
|
|
11068
|
-
const record =
|
|
11584
|
+
const record = asRecord2(payload);
|
|
11069
11585
|
const value = record.access_key ?? record.accessKey ?? record.secret;
|
|
11070
11586
|
return typeof value === "string" ? value : void 0;
|
|
11071
11587
|
};
|
|
11072
11588
|
var projectIdFromPayload = (payload, fallback) => {
|
|
11073
|
-
const record =
|
|
11589
|
+
const record = asRecord2(payload);
|
|
11074
11590
|
const credential = credentialFromPayload(payload);
|
|
11075
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;
|
|
11076
11592
|
return typeof value === "string" ? value : void 0;
|
|
@@ -11104,7 +11620,7 @@ var writeCredentialOutput = async (input) => {
|
|
|
11104
11620
|
return;
|
|
11105
11621
|
}
|
|
11106
11622
|
if (input.format === "text" || !input.format) {
|
|
11107
|
-
const record =
|
|
11623
|
+
const record = asRecord2(input.data);
|
|
11108
11624
|
const credentials = arrayFromPayload(input.data, "credentials").length > 0 ? arrayFromPayload(input.data, "credentials") : record.credential ? [credentialFromPayload(input.data)] : arrayFromPayload(input.data, "templates");
|
|
11109
11625
|
if (credentials.length) {
|
|
11110
11626
|
process.stdout.write(formatTextTable(formatCredentialTextRows(credentials)));
|
|
@@ -11228,9 +11744,9 @@ import { randomUUID as randomUUID4 } from "crypto";
|
|
|
11228
11744
|
// src/localHooks.ts
|
|
11229
11745
|
import { exec } from "child_process";
|
|
11230
11746
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
11231
|
-
import
|
|
11747
|
+
import fs10 from "fs/promises";
|
|
11232
11748
|
import os4 from "os";
|
|
11233
|
-
import
|
|
11749
|
+
import path7 from "path";
|
|
11234
11750
|
var normalizeHooks = (config, event) => {
|
|
11235
11751
|
if (config.hooks?.enabled !== true) {
|
|
11236
11752
|
return [];
|
|
@@ -11241,11 +11757,11 @@ var normalizeHooks = (config, event) => {
|
|
|
11241
11757
|
) : [];
|
|
11242
11758
|
};
|
|
11243
11759
|
var writeHookPayload = async (input, hook) => {
|
|
11244
|
-
const filePath =
|
|
11760
|
+
const filePath = path7.join(
|
|
11245
11761
|
os4.tmpdir(),
|
|
11246
11762
|
`cloudeval-hook-${process.pid}-${randomUUID3()}.json`
|
|
11247
11763
|
);
|
|
11248
|
-
await
|
|
11764
|
+
await fs10.writeFile(
|
|
11249
11765
|
filePath,
|
|
11250
11766
|
JSON.stringify(
|
|
11251
11767
|
{
|
|
@@ -11321,7 +11837,7 @@ var runLocalHooks = async (input) => {
|
|
|
11321
11837
|
throw error;
|
|
11322
11838
|
}
|
|
11323
11839
|
} finally {
|
|
11324
|
-
await
|
|
11840
|
+
await fs10.rm(payloadPath, { force: true }).catch(() => void 0);
|
|
11325
11841
|
}
|
|
11326
11842
|
}
|
|
11327
11843
|
return warnings;
|
|
@@ -11633,7 +12149,7 @@ var registerAgentsCommand = (program2, deps) => {
|
|
|
11633
12149
|
|
|
11634
12150
|
// src/validateCommand.ts
|
|
11635
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");
|
|
11636
|
-
var
|
|
12152
|
+
var parsePositiveInteger2 = (value, optionName = "--max-results") => {
|
|
11637
12153
|
if (!value) {
|
|
11638
12154
|
return void 0;
|
|
11639
12155
|
}
|
|
@@ -11667,7 +12183,7 @@ var registerValidateCommand = (program2, deps) => {
|
|
|
11667
12183
|
category: options.category,
|
|
11668
12184
|
pillar: options.pillar,
|
|
11669
12185
|
minSeverity: options.minSeverity,
|
|
11670
|
-
maxResults:
|
|
12186
|
+
maxResults: parsePositiveInteger2(options.maxResults),
|
|
11671
12187
|
projectId: options.project,
|
|
11672
12188
|
saveReport: options.saveReport
|
|
11673
12189
|
});
|
|
@@ -11676,11 +12192,11 @@ var registerValidateCommand = (program2, deps) => {
|
|
|
11676
12192
|
authToken: context.token,
|
|
11677
12193
|
userId: context.user.id,
|
|
11678
12194
|
submitted,
|
|
11679
|
-
pollIntervalMs:
|
|
12195
|
+
pollIntervalMs: parsePositiveInteger2(
|
|
11680
12196
|
options.pollInterval,
|
|
11681
12197
|
"--poll-interval"
|
|
11682
12198
|
),
|
|
11683
|
-
waitTimeoutMs:
|
|
12199
|
+
waitTimeoutMs: parsePositiveInteger2(
|
|
11684
12200
|
options.waitTimeout,
|
|
11685
12201
|
"--wait-timeout"
|
|
11686
12202
|
)
|
|
@@ -11740,11 +12256,11 @@ var registerValidateCommand = (program2, deps) => {
|
|
|
11740
12256
|
authToken: context.token,
|
|
11741
12257
|
userId: context.user.id,
|
|
11742
12258
|
submitted,
|
|
11743
|
-
pollIntervalMs:
|
|
12259
|
+
pollIntervalMs: parsePositiveInteger2(
|
|
11744
12260
|
options.pollInterval,
|
|
11745
12261
|
"--poll-interval"
|
|
11746
12262
|
),
|
|
11747
|
-
waitTimeoutMs:
|
|
12263
|
+
waitTimeoutMs: parsePositiveInteger2(
|
|
11748
12264
|
options.waitTimeout,
|
|
11749
12265
|
"--wait-timeout"
|
|
11750
12266
|
)
|
|
@@ -11865,10 +12381,10 @@ var registerConfigCommand = (program2) => {
|
|
|
11865
12381
|
const profile = resolveProfile(options, command);
|
|
11866
12382
|
const current = await loadCliConfig(profile);
|
|
11867
12383
|
const next = writeCliConfigValue(current, key, value);
|
|
11868
|
-
const
|
|
12384
|
+
const path11 = await saveCliConfig(next, profile);
|
|
11869
12385
|
await writeFormattedOutput({
|
|
11870
12386
|
command: "config set",
|
|
11871
|
-
data: { profile, path:
|
|
12387
|
+
data: { profile, path: path11, config: next },
|
|
11872
12388
|
format: options.format,
|
|
11873
12389
|
output: options.output
|
|
11874
12390
|
});
|
|
@@ -11879,10 +12395,10 @@ var registerConfigCommand = (program2) => {
|
|
|
11879
12395
|
const profile = resolveProfile(options, command);
|
|
11880
12396
|
const current = await loadCliConfig(profile);
|
|
11881
12397
|
const next = unsetCliConfigValue(current, key);
|
|
11882
|
-
const
|
|
12398
|
+
const path11 = await saveCliConfig(next, profile);
|
|
11883
12399
|
await writeFormattedOutput({
|
|
11884
12400
|
command: "config unset",
|
|
11885
|
-
data: { profile, path:
|
|
12401
|
+
data: { profile, path: path11, config: next },
|
|
11886
12402
|
format: options.format,
|
|
11887
12403
|
output: options.output
|
|
11888
12404
|
});
|
|
@@ -12179,8 +12695,8 @@ var writeModelsListOutput = async (input) => {
|
|
|
12179
12695
|
defaultModel: input.defaultModel
|
|
12180
12696
|
});
|
|
12181
12697
|
if (input.options.output) {
|
|
12182
|
-
const
|
|
12183
|
-
await
|
|
12698
|
+
const fs14 = await import("fs/promises");
|
|
12699
|
+
await fs14.writeFile(input.options.output, text, "utf8");
|
|
12184
12700
|
return;
|
|
12185
12701
|
}
|
|
12186
12702
|
process.stdout.write(text);
|
|
@@ -12246,10 +12762,10 @@ var registerModelsCommand = (program2, deps) => {
|
|
|
12246
12762
|
const profile = options.profile || getActiveConfigProfile(command);
|
|
12247
12763
|
const config = await loadCliConfig(profile);
|
|
12248
12764
|
const next = { ...config, model };
|
|
12249
|
-
const
|
|
12765
|
+
const path11 = await saveCliConfig(next, profile);
|
|
12250
12766
|
await writeFormattedOutput({
|
|
12251
12767
|
command: "models default set",
|
|
12252
|
-
data: { profile, path:
|
|
12768
|
+
data: { profile, path: path11, model },
|
|
12253
12769
|
format: options.format,
|
|
12254
12770
|
output: options.output
|
|
12255
12771
|
});
|
|
@@ -12292,8 +12808,8 @@ var writeSessionTableOutput = async (command, data, options) => {
|
|
|
12292
12808
|
if (format === "text") {
|
|
12293
12809
|
const text = renderSessionsTable(data);
|
|
12294
12810
|
if (options.output) {
|
|
12295
|
-
const
|
|
12296
|
-
await
|
|
12811
|
+
const fs14 = await import("fs/promises");
|
|
12812
|
+
await fs14.writeFile(options.output, text, "utf8");
|
|
12297
12813
|
return;
|
|
12298
12814
|
}
|
|
12299
12815
|
process.stdout.write(text);
|
|
@@ -12465,12 +12981,12 @@ var registerSetupCommand = (program2, defaultBaseUrl = CLOUD_BASE_URL) => {
|
|
|
12465
12981
|
rl.close();
|
|
12466
12982
|
}
|
|
12467
12983
|
}
|
|
12468
|
-
const
|
|
12984
|
+
const path11 = await saveCliConfig(next, profile);
|
|
12469
12985
|
await writeFormattedOutput({
|
|
12470
12986
|
command: "setup",
|
|
12471
12987
|
data: {
|
|
12472
12988
|
profile,
|
|
12473
|
-
path:
|
|
12989
|
+
path: path11,
|
|
12474
12990
|
config: next,
|
|
12475
12991
|
nextSteps: [
|
|
12476
12992
|
"Run `cloudeval auth status` to inspect authentication.",
|
|
@@ -12485,9 +13001,9 @@ var registerSetupCommand = (program2, defaultBaseUrl = CLOUD_BASE_URL) => {
|
|
|
12485
13001
|
};
|
|
12486
13002
|
|
|
12487
13003
|
// src/updateCommand.ts
|
|
12488
|
-
import { spawn as
|
|
12489
|
-
import
|
|
12490
|
-
import
|
|
13004
|
+
import { spawn as spawn3 } from "child_process";
|
|
13005
|
+
import fs11 from "fs/promises";
|
|
13006
|
+
import path8 from "path";
|
|
12491
13007
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
12492
13008
|
var DEFAULT_LATEST_RELEASE_URL = "https://api.github.com/repos/ganakailabs/cloudeval-cli/releases/latest";
|
|
12493
13009
|
var DEFAULT_INSTALLER_URL = "https://cli.cloudeval.ai/install.sh";
|
|
@@ -12604,7 +13120,7 @@ var runInstaller = async ({
|
|
|
12604
13120
|
installerUrl,
|
|
12605
13121
|
targetTag,
|
|
12606
13122
|
fetchImpl = fetch,
|
|
12607
|
-
spawnImpl =
|
|
13123
|
+
spawnImpl = spawn3,
|
|
12608
13124
|
output = process.stderr,
|
|
12609
13125
|
platform = process.platform,
|
|
12610
13126
|
env = process.env,
|
|
@@ -12626,12 +13142,12 @@ var runInstaller = async ({
|
|
|
12626
13142
|
const installerScript = await response.text();
|
|
12627
13143
|
if (usePowerShellInstaller) {
|
|
12628
13144
|
const configDir = getCloudevalConfigDir();
|
|
12629
|
-
await
|
|
12630
|
-
const scriptPath =
|
|
12631
|
-
await
|
|
13145
|
+
await fs11.mkdir(configDir, { recursive: true, mode: 448 });
|
|
13146
|
+
const scriptPath = path8.join(
|
|
13147
|
+
await fs11.mkdtemp(path8.join(configDir, "installer-")),
|
|
12632
13148
|
"install.ps1"
|
|
12633
13149
|
);
|
|
12634
|
-
await
|
|
13150
|
+
await fs11.writeFile(scriptPath, installerScript, "utf8");
|
|
12635
13151
|
const child2 = spawnImpl(
|
|
12636
13152
|
"pwsh",
|
|
12637
13153
|
[
|
|
@@ -12659,7 +13175,7 @@ var runInstaller = async ({
|
|
|
12659
13175
|
);
|
|
12660
13176
|
});
|
|
12661
13177
|
child2.once("close", (code) => {
|
|
12662
|
-
void
|
|
13178
|
+
void fs11.rm(path8.dirname(scriptPath), { recursive: true, force: true });
|
|
12663
13179
|
if (code === 0) {
|
|
12664
13180
|
resolve();
|
|
12665
13181
|
return;
|
|
@@ -12699,10 +13215,10 @@ var runInstaller = async ({
|
|
|
12699
13215
|
child.stdin.end(installerScript);
|
|
12700
13216
|
});
|
|
12701
13217
|
};
|
|
12702
|
-
var getUpdateCachePath = () =>
|
|
13218
|
+
var getUpdateCachePath = () => path8.join(getCloudevalConfigDir(), UPDATE_CACHE_FILE);
|
|
12703
13219
|
var readCache = async (cachePath) => {
|
|
12704
13220
|
try {
|
|
12705
|
-
const parsed = JSON.parse(await
|
|
13221
|
+
const parsed = JSON.parse(await fs11.readFile(cachePath, "utf8"));
|
|
12706
13222
|
if (parsed && typeof parsed === "object" && typeof parsed.checkedAt === "string" && typeof parsed.latestVersion === "string" && typeof parsed.latestTag === "string") {
|
|
12707
13223
|
return parsed;
|
|
12708
13224
|
}
|
|
@@ -12714,8 +13230,8 @@ var readCache = async (cachePath) => {
|
|
|
12714
13230
|
return void 0;
|
|
12715
13231
|
};
|
|
12716
13232
|
var writeCache = async (cachePath, status) => {
|
|
12717
|
-
await
|
|
12718
|
-
await
|
|
13233
|
+
await fs11.mkdir(path8.dirname(cachePath), { recursive: true, mode: 448 });
|
|
13234
|
+
await fs11.writeFile(
|
|
12719
13235
|
cachePath,
|
|
12720
13236
|
`${JSON.stringify(
|
|
12721
13237
|
{
|
|
@@ -12927,7 +13443,7 @@ var registerUpdateCommand = (program2, registerOptions = {}) => {
|
|
|
12927
13443
|
if (options.format === "text" || !options.format) {
|
|
12928
13444
|
const text = formatUpdateStatusText(result);
|
|
12929
13445
|
if (options.output) {
|
|
12930
|
-
await
|
|
13446
|
+
await fs11.writeFile(options.output, text, "utf8");
|
|
12931
13447
|
return;
|
|
12932
13448
|
}
|
|
12933
13449
|
process.stdout.write(text);
|
|
@@ -12943,13 +13459,13 @@ var registerUpdateCommand = (program2, registerOptions = {}) => {
|
|
|
12943
13459
|
};
|
|
12944
13460
|
|
|
12945
13461
|
// src/uninstallCommand.ts
|
|
12946
|
-
import
|
|
13462
|
+
import fs12 from "fs/promises";
|
|
12947
13463
|
import os5 from "os";
|
|
12948
|
-
import
|
|
13464
|
+
import path9 from "path";
|
|
12949
13465
|
import { createInterface as createInterface3 } from "readline/promises";
|
|
12950
13466
|
var pathExists = async (candidate) => {
|
|
12951
13467
|
try {
|
|
12952
|
-
await
|
|
13468
|
+
await fs12.lstat(candidate);
|
|
12953
13469
|
return true;
|
|
12954
13470
|
} catch (error) {
|
|
12955
13471
|
if (error?.code === "ENOENT") {
|
|
@@ -12958,12 +13474,12 @@ var pathExists = async (candidate) => {
|
|
|
12958
13474
|
throw error;
|
|
12959
13475
|
}
|
|
12960
13476
|
};
|
|
12961
|
-
var installerBinDir = (home) =>
|
|
13477
|
+
var installerBinDir = (home) => path9.join(home, ".local", "bin");
|
|
12962
13478
|
var completionPaths = (home) => [
|
|
12963
|
-
|
|
12964
|
-
|
|
12965
|
-
|
|
12966
|
-
|
|
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")
|
|
12967
13483
|
];
|
|
12968
13484
|
var installerArtifactTargets = (home, platform) => {
|
|
12969
13485
|
const binDir = installerBinDir(home);
|
|
@@ -12971,37 +13487,37 @@ var installerArtifactTargets = (home, platform) => {
|
|
|
12971
13487
|
const targets = [
|
|
12972
13488
|
{
|
|
12973
13489
|
label: "cloudeval binary",
|
|
12974
|
-
path:
|
|
13490
|
+
path: path9.join(binDir, executableName),
|
|
12975
13491
|
kind: "file",
|
|
12976
13492
|
status: "missing"
|
|
12977
13493
|
},
|
|
12978
13494
|
{
|
|
12979
13495
|
label: "cloudeval binary",
|
|
12980
|
-
path:
|
|
13496
|
+
path: path9.join(binDir, "cloudeval"),
|
|
12981
13497
|
kind: "file",
|
|
12982
13498
|
status: "missing"
|
|
12983
13499
|
},
|
|
12984
13500
|
{
|
|
12985
13501
|
label: "eva alias",
|
|
12986
|
-
path:
|
|
13502
|
+
path: path9.join(binDir, "eva"),
|
|
12987
13503
|
kind: "file",
|
|
12988
13504
|
status: "missing"
|
|
12989
13505
|
},
|
|
12990
13506
|
{
|
|
12991
13507
|
label: "cloud alias",
|
|
12992
|
-
path:
|
|
13508
|
+
path: path9.join(binDir, "cloud"),
|
|
12993
13509
|
kind: "file",
|
|
12994
13510
|
status: "missing"
|
|
12995
13511
|
},
|
|
12996
13512
|
{
|
|
12997
13513
|
label: "Ink runtime asset",
|
|
12998
|
-
path:
|
|
13514
|
+
path: path9.join(binDir, "yoga.wasm"),
|
|
12999
13515
|
kind: "file",
|
|
13000
13516
|
status: "missing"
|
|
13001
13517
|
},
|
|
13002
13518
|
{
|
|
13003
13519
|
label: "license notices",
|
|
13004
|
-
path:
|
|
13520
|
+
path: path9.join(home, ".local", "share", "cloudeval", "licenses"),
|
|
13005
13521
|
kind: "directory",
|
|
13006
13522
|
status: "missing"
|
|
13007
13523
|
},
|
|
@@ -13017,11 +13533,11 @@ var installerArtifactTargets = (home, platform) => {
|
|
|
13017
13533
|
);
|
|
13018
13534
|
};
|
|
13019
13535
|
var shellProfilePaths = (home) => [
|
|
13020
|
-
|
|
13021
|
-
|
|
13022
|
-
|
|
13023
|
-
|
|
13024
|
-
|
|
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")
|
|
13025
13541
|
];
|
|
13026
13542
|
var removeInstallerPathSnippet = (content, binDir) => {
|
|
13027
13543
|
const exportLine = `export PATH="${binDir}:$PATH"`;
|
|
@@ -13048,7 +13564,7 @@ var removeTarget = async (target, dryRun) => {
|
|
|
13048
13564
|
if (dryRun) {
|
|
13049
13565
|
return { ...target, status: "would_remove" };
|
|
13050
13566
|
}
|
|
13051
|
-
await
|
|
13567
|
+
await fs12.rm(target.path, { recursive: target.kind === "directory", force: true });
|
|
13052
13568
|
return { ...target, status: "removed" };
|
|
13053
13569
|
};
|
|
13054
13570
|
var updateShellProfile = async (profilePath, home, dryRun) => {
|
|
@@ -13060,7 +13576,7 @@ var updateShellProfile = async (profilePath, home, dryRun) => {
|
|
|
13060
13576
|
status: "missing"
|
|
13061
13577
|
};
|
|
13062
13578
|
}
|
|
13063
|
-
const content = await
|
|
13579
|
+
const content = await fs12.readFile(profilePath, "utf8");
|
|
13064
13580
|
const updated = removeInstallerPathSnippet(content, installerBinDir(home));
|
|
13065
13581
|
if (updated === void 0) {
|
|
13066
13582
|
return {
|
|
@@ -13078,7 +13594,7 @@ var updateShellProfile = async (profilePath, home, dryRun) => {
|
|
|
13078
13594
|
status: "would_update"
|
|
13079
13595
|
};
|
|
13080
13596
|
}
|
|
13081
|
-
await
|
|
13597
|
+
await fs12.writeFile(profilePath, updated, "utf8");
|
|
13082
13598
|
return {
|
|
13083
13599
|
label: "shell profile PATH entry",
|
|
13084
13600
|
path: profilePath,
|
|
@@ -13131,7 +13647,7 @@ var handleUninstallCommand = async (options, deps = {}) => {
|
|
|
13131
13647
|
}
|
|
13132
13648
|
const configTarget = {
|
|
13133
13649
|
label: "config",
|
|
13134
|
-
path:
|
|
13650
|
+
path: path9.join(home, ".config", "cloudeval"),
|
|
13135
13651
|
kind: "directory",
|
|
13136
13652
|
status: "kept"
|
|
13137
13653
|
};
|
|
@@ -13193,7 +13709,7 @@ var registerUninstallCommand = (program2) => {
|
|
|
13193
13709
|
if (options.format === "text" || !options.format) {
|
|
13194
13710
|
const text = formatUninstallResultText(result);
|
|
13195
13711
|
if (options.output) {
|
|
13196
|
-
await
|
|
13712
|
+
await fs12.writeFile(options.output, text, "utf8");
|
|
13197
13713
|
return;
|
|
13198
13714
|
}
|
|
13199
13715
|
process.stdout.write(text);
|
|
@@ -13314,6 +13830,7 @@ var resolveLoginOnboardingMode = (options) => {
|
|
|
13314
13830
|
import { jsx } from "react/jsx-runtime";
|
|
13315
13831
|
var DEFAULT_BASE_URL = getDefaultBaseUrl();
|
|
13316
13832
|
var ASK_STREAM_IDLE_TIMEOUT_MS2 = 9e4;
|
|
13833
|
+
var AGENT_STREAM_IDLE_TIMEOUT_MS = 18e4;
|
|
13317
13834
|
var LEGACY_API_KEY_MESSAGE = "API key auth was renamed in beta. Use --access-key or CLOUDEVAL_ACCESS_KEY.";
|
|
13318
13835
|
var STREAM_OUTPUT_NODES3 = /* @__PURE__ */ new Set([
|
|
13319
13836
|
"generate_response",
|
|
@@ -13339,21 +13856,21 @@ var completionScriptPath = (shell) => {
|
|
|
13339
13856
|
const home = os6.homedir();
|
|
13340
13857
|
switch (shell) {
|
|
13341
13858
|
case "bash":
|
|
13342
|
-
return
|
|
13859
|
+
return path10.join(home, ".local", "share", "bash-completion", "completions", "cloudeval");
|
|
13343
13860
|
case "zsh":
|
|
13344
|
-
return
|
|
13861
|
+
return path10.join(home, ".zsh", "completions", "_cloudeval");
|
|
13345
13862
|
case "fish":
|
|
13346
|
-
return
|
|
13863
|
+
return path10.join(home, ".config", "fish", "completions", "cloudeval.fish");
|
|
13347
13864
|
case "powershell":
|
|
13348
|
-
return
|
|
13865
|
+
return path10.join(home, ".config", "powershell", "cloudeval-completion.ps1");
|
|
13349
13866
|
}
|
|
13350
13867
|
};
|
|
13351
13868
|
var ZSH_FPATH_MARKER = "CloudEval CLI completions";
|
|
13352
13869
|
var ensureZshCompletionFpath = async () => {
|
|
13353
|
-
const zshrc =
|
|
13870
|
+
const zshrc = path10.join(os6.homedir(), ".zshrc");
|
|
13354
13871
|
let existing = "";
|
|
13355
13872
|
try {
|
|
13356
|
-
existing = await
|
|
13873
|
+
existing = await fs13.readFile(zshrc, "utf8");
|
|
13357
13874
|
} catch {
|
|
13358
13875
|
existing = "";
|
|
13359
13876
|
}
|
|
@@ -13364,12 +13881,12 @@ var ensureZshCompletionFpath = async () => {
|
|
|
13364
13881
|
# ${ZSH_FPATH_MARKER}
|
|
13365
13882
|
fpath=("$HOME/.zsh/completions" $fpath)
|
|
13366
13883
|
`;
|
|
13367
|
-
await
|
|
13884
|
+
await fs13.appendFile(zshrc, snippet, "utf8");
|
|
13368
13885
|
};
|
|
13369
13886
|
var installCompletionScript = async (shell, binaryName) => {
|
|
13370
13887
|
const scriptPath = completionScriptPath(shell);
|
|
13371
|
-
await
|
|
13372
|
-
await
|
|
13888
|
+
await fs13.mkdir(path10.dirname(scriptPath), { recursive: true });
|
|
13889
|
+
await fs13.writeFile(scriptPath, buildCompletionScript(shell, binaryName), "utf8");
|
|
13373
13890
|
if (shell === "zsh") {
|
|
13374
13891
|
await ensureZshCompletionFpath();
|
|
13375
13892
|
}
|
|
@@ -13377,7 +13894,7 @@ var installCompletionScript = async (shell, binaryName) => {
|
|
|
13377
13894
|
};
|
|
13378
13895
|
var uninstallCompletionScript = async (shell) => {
|
|
13379
13896
|
const scriptPath = completionScriptPath(shell);
|
|
13380
|
-
await
|
|
13897
|
+
await fs13.rm(scriptPath, { force: true });
|
|
13381
13898
|
return scriptPath;
|
|
13382
13899
|
};
|
|
13383
13900
|
var runInteractiveLoginOnboarding = async (baseUrl, token) => {
|
|
@@ -13946,6 +14463,12 @@ registerReportsCommand(program, {
|
|
|
13946
14463
|
resolveBaseUrl,
|
|
13947
14464
|
readStdinValue
|
|
13948
14465
|
});
|
|
14466
|
+
registerReviewCommand(program, {
|
|
14467
|
+
defaultBaseUrl: DEFAULT_BASE_URL,
|
|
14468
|
+
resolveBaseUrl,
|
|
14469
|
+
readStdinValue,
|
|
14470
|
+
isHeadlessEnvironment
|
|
14471
|
+
});
|
|
13949
14472
|
registerRecipesCommand(program, {
|
|
13950
14473
|
defaultBaseUrl: DEFAULT_BASE_URL,
|
|
13951
14474
|
resolveBaseUrl,
|
|
@@ -14102,7 +14625,7 @@ program.command("tui").description("Open the CloudEval Terminal UI").option(
|
|
|
14102
14625
|
const { assertSecureBaseUrl } = await import("./dist-CFLR5FML.js");
|
|
14103
14626
|
const [{ render }, { App }] = await Promise.all([
|
|
14104
14627
|
import("ink"),
|
|
14105
|
-
import("./App-
|
|
14628
|
+
import("./App-H46FRLWK.js")
|
|
14106
14629
|
]);
|
|
14107
14630
|
const baseUrl = await resolveBaseUrl(options, command);
|
|
14108
14631
|
assertSecureBaseUrl(baseUrl);
|
|
@@ -14160,7 +14683,7 @@ program.command("chat").description("Start an interactive chat session").option(
|
|
|
14160
14683
|
const { assertSecureBaseUrl } = await import("./dist-CFLR5FML.js");
|
|
14161
14684
|
const [{ render }, { App }] = await Promise.all([
|
|
14162
14685
|
import("ink"),
|
|
14163
|
-
import("./App-
|
|
14686
|
+
import("./App-H46FRLWK.js")
|
|
14164
14687
|
]);
|
|
14165
14688
|
const baseUrl = await resolveBaseUrl(options, command);
|
|
14166
14689
|
assertSecureBaseUrl(baseUrl);
|
|
@@ -14273,7 +14796,7 @@ program.command("ask").alias("agent").description("Ask a single question or run
|
|
|
14273
14796
|
});
|
|
14274
14797
|
}
|
|
14275
14798
|
try {
|
|
14276
|
-
const
|
|
14799
|
+
const fs14 = await import("fs");
|
|
14277
14800
|
const fsPromises = await import("fs/promises");
|
|
14278
14801
|
const { randomUUID: randomUUID5 } = await import("crypto");
|
|
14279
14802
|
const core = await import("./dist-CFLR5FML.js");
|
|
@@ -14281,8 +14804,6 @@ program.command("ask").alias("agent").description("Ask a single question or run
|
|
|
14281
14804
|
streamChat,
|
|
14282
14805
|
reduceChunk,
|
|
14283
14806
|
getAuthToken,
|
|
14284
|
-
getProjects,
|
|
14285
|
-
ensurePlaygroundProject,
|
|
14286
14807
|
checkUserStatus,
|
|
14287
14808
|
extractEmailFromToken,
|
|
14288
14809
|
initialChatState,
|
|
@@ -14381,68 +14902,39 @@ program.command("ask").alias("agent").description("Ask a single question or run
|
|
|
14381
14902
|
step: "project",
|
|
14382
14903
|
message: selectedProjectId ? `Using project ${selectedProjectId}` : "Resolving project"
|
|
14383
14904
|
});
|
|
14384
|
-
let project;
|
|
14385
14905
|
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
|
-
}
|
|
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);
|
|
14445
14936
|
}
|
|
14937
|
+
verboseLog("Selected project:", { id: project.id, name: project.name });
|
|
14446
14938
|
let userName = "You";
|
|
14447
14939
|
try {
|
|
14448
14940
|
const email = extractEmailFromToken(token);
|
|
@@ -14469,11 +14961,11 @@ program.command("ask").alias("agent").description("Ask a single question or run
|
|
|
14469
14961
|
console.error(`[${commandName}] Thread ID: ${threadId}`);
|
|
14470
14962
|
}
|
|
14471
14963
|
if (streamTextOutput && options.output) {
|
|
14472
|
-
fileOutputStream =
|
|
14964
|
+
fileOutputStream = fs14.createWriteStream(options.output, { encoding: "utf-8" });
|
|
14473
14965
|
outputStream = fileOutputStream;
|
|
14474
14966
|
}
|
|
14475
14967
|
if (ndjsonOutput && options.output) {
|
|
14476
|
-
ndjsonOutputStream =
|
|
14968
|
+
ndjsonOutputStream = fs14.createWriteStream(options.output, { encoding: "utf-8" });
|
|
14477
14969
|
}
|
|
14478
14970
|
const writeAskDataEvent = (event) => {
|
|
14479
14971
|
const line = `${JSON.stringify(event)}
|
|
@@ -14575,7 +15067,7 @@ program.command("ask").alias("agent").description("Ask a single question or run
|
|
|
14575
15067
|
debug: options.debug,
|
|
14576
15068
|
completeAfterResponse: true,
|
|
14577
15069
|
responseCompletionGraceMs: 5e3,
|
|
14578
|
-
streamIdleTimeoutMs: ASK_STREAM_IDLE_TIMEOUT_MS2,
|
|
15070
|
+
streamIdleTimeoutMs: selectedMode === "agent" ? AGENT_STREAM_IDLE_TIMEOUT_MS : ASK_STREAM_IDLE_TIMEOUT_MS2,
|
|
14579
15071
|
hitlResume
|
|
14580
15072
|
})) {
|
|
14581
15073
|
totalChunkCount++;
|
|
@@ -14754,7 +15246,24 @@ Error: ${errorMsg}
|
|
|
14754
15246
|
throw error;
|
|
14755
15247
|
}
|
|
14756
15248
|
const finalMessage = [...chatState.messages].reverse().find((m) => m.role === "assistant");
|
|
14757
|
-
|
|
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
|
+
}
|
|
14758
15267
|
if (!finalResponse.trim()) {
|
|
14759
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.`;
|
|
14760
15269
|
progressWriter.clear();
|
|
@@ -14928,7 +15437,7 @@ Error: ${errorMsg}
|
|
|
14928
15437
|
program.command("banner").description("Preview the startup banner and terminal capabilities").action(async () => {
|
|
14929
15438
|
const { render } = await import("ink");
|
|
14930
15439
|
const BannerPreview = React.lazy(async () => ({
|
|
14931
|
-
default: (await import("./Banner-
|
|
15440
|
+
default: (await import("./Banner-7X2VHUVH.js")).Banner
|
|
14932
15441
|
}));
|
|
14933
15442
|
render(
|
|
14934
15443
|
/* @__PURE__ */ jsx(React.Suspense, { fallback: null, children: /* @__PURE__ */ jsx(BannerPreview, { disable: false }) })
|