@ganakailabs/cloudeval-cli 0.19.3 → 0.19.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,10 +1,30 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  buildFrontendUrl,
4
+ deleteSession,
5
+ exportSessions,
6
+ getActiveConfigProfile,
7
+ getCliConfigPath,
8
+ getCloudevalConfigDir,
4
9
  getFirstNameForDisplay,
10
+ getSession,
11
+ listCliConfigProfiles,
12
+ listSessions,
13
+ loadCliConfig,
14
+ normalizeCliMode,
15
+ normalizeConfigProfile,
5
16
  openExternalUrl,
6
- resolveFrontendBaseUrl
7
- } from "./chunk-TA4WC462.js";
17
+ pruneSessions,
18
+ readCliConfigValue,
19
+ recordSessionTurn,
20
+ renameSession,
21
+ resolveFrontendBaseUrl,
22
+ resolveSessionReference,
23
+ saveCliConfig,
24
+ searchSessions,
25
+ unsetCliConfigValue,
26
+ writeCliConfigValue
27
+ } from "./chunk-RCRNSEQS.js";
8
28
  import {
9
29
  getBundledAgentProfile,
10
30
  getBundledAgentProfiles,
@@ -13,10 +33,10 @@ import {
13
33
  normalizeApiBase,
14
34
  redactSensitiveSecrets,
15
35
  redactSensitiveText
16
- } from "./chunk-4QIKW5TJ.js";
36
+ } from "./chunk-2D4BE3OS.js";
17
37
  import {
18
38
  CLI_VERSION
19
- } from "./chunk-SCKO2ZTZ.js";
39
+ } from "./chunk-HUE4D6RO.js";
20
40
 
21
41
  // src/runtime/prepareInk.ts
22
42
  import fs from "fs";
@@ -116,8 +136,8 @@ ensureInkRuntimeEnvironment();
116
136
  import React from "react";
117
137
  import { Command } from "commander";
118
138
  import { promises as fs12 } from "fs";
119
- import os5 from "os";
120
- import path10 from "path";
139
+ import os4 from "os";
140
+ import path9 from "path";
121
141
 
122
142
  // src/shellCompletion.ts
123
143
  var normalizeCompletionShell = (shell) => {
@@ -452,6 +472,22 @@ var cliCommands = [
452
472
  ],
453
473
  workflows: ["recipes list", "recipes show", "recipes run"]
454
474
  },
475
+ {
476
+ name: "skills",
477
+ description: "List, inspect, and validate CloudEval agent skills",
478
+ domain: "skills",
479
+ options: [
480
+ "list",
481
+ "show",
482
+ "doctor",
483
+ "path",
484
+ "--format",
485
+ "--output",
486
+ "--profile",
487
+ "--help"
488
+ ],
489
+ workflows: ["skills list", "skills show", "skills doctor", "skills path"]
490
+ },
455
491
  {
456
492
  name: "projects",
457
493
  description: "Project utilities",
@@ -1568,7 +1604,7 @@ var resolveReportProjectId = async ({
1568
1604
  if (!token) {
1569
1605
  throw new Error("No project specified. Use --project <id> for report access.");
1570
1606
  }
1571
- const resolvedWorkspace = workspace ?? await import("./dist-I4IPYCRC.js").then((core) => ({
1607
+ const resolvedWorkspace = workspace ?? await import("./dist-TBAQ5KOK.js").then((core) => ({
1572
1608
  checkUserStatus: core.checkUserStatus,
1573
1609
  getProjects: core.getProjects
1574
1610
  }));
@@ -1597,7 +1633,7 @@ var warnIfAccessKeyFromCliOption = (options, command) => {
1597
1633
  };
1598
1634
  var resolveAuthContext = async (options, command, deps) => {
1599
1635
  const baseUrl = await deps.resolveBaseUrl(options, command);
1600
- const core = await import("./dist-I4IPYCRC.js");
1636
+ const core = await import("./dist-TBAQ5KOK.js");
1601
1637
  core.assertSecureBaseUrl(baseUrl);
1602
1638
  warnIfAccessKeyFromCliOption(options, command);
1603
1639
  let accessKey = options.accessKey;
@@ -1683,7 +1719,7 @@ var resolveToken = async (options, baseUrl, deps, command) => {
1683
1719
  if (options.accessKey) {
1684
1720
  return options.accessKey;
1685
1721
  }
1686
- const { getAuthToken } = await import("./dist-I4IPYCRC.js");
1722
+ const { getAuthToken } = await import("./dist-TBAQ5KOK.js");
1687
1723
  try {
1688
1724
  return await getAuthToken({
1689
1725
  accessKey: options.accessKey,
@@ -1694,7 +1730,7 @@ var resolveToken = async (options, baseUrl, deps, command) => {
1694
1730
  if (!canLogin) {
1695
1731
  throw error;
1696
1732
  }
1697
- const { login } = await import("./dist-I4IPYCRC.js");
1733
+ const { login } = await import("./dist-TBAQ5KOK.js");
1698
1734
  process.stderr.write("Authentication required. Starting login flow...\n");
1699
1735
  const token = await login(baseUrl, {
1700
1736
  headless: Boolean(process.env.SSH_TTY || process.env.CLOUDEVAL_HEADLESS_LOGIN)
@@ -1730,7 +1766,7 @@ var writeReport = async (report, options, tuiDefault) => {
1730
1766
  const [{ default: React2 }, { render }, { ReportDashboard }] = await Promise.all([
1731
1767
  import("react"),
1732
1768
  import("ink"),
1733
- import("./ReportDashboard-6JFJJVRW.js")
1769
+ import("./ReportDashboard-T63UGA7M.js")
1734
1770
  ]);
1735
1771
  render(React2.createElement(ReportDashboard, {
1736
1772
  report,
@@ -1778,8 +1814,8 @@ var writeDownloadPayload = async (input) => {
1778
1814
  return [];
1779
1815
  }
1780
1816
  const fs13 = await import("fs/promises");
1781
- const path11 = await import("path");
1782
- await fs13.mkdir(path11.dirname(input.output), { recursive: true });
1817
+ const path10 = await import("path");
1818
+ await fs13.mkdir(path10.dirname(input.output), { recursive: true });
1783
1819
  const text = formatOutput({
1784
1820
  command: input.command,
1785
1821
  data: input.payload,
@@ -1860,7 +1896,7 @@ var registerReportsCommand = (program2, deps) => {
1860
1896
  token,
1861
1897
  requestedProjectId: options.project
1862
1898
  });
1863
- const core = await import("./dist-I4IPYCRC.js");
1899
+ const core = await import("./dist-TBAQ5KOK.js");
1864
1900
  const status = token ? await core.checkUserStatus(baseUrl, token) : void 0;
1865
1901
  const reports2 = await core.listReports({
1866
1902
  baseUrl,
@@ -1882,7 +1918,7 @@ var registerReportsCommand = (program2, deps) => {
1882
1918
  try {
1883
1919
  const baseUrl = await deps.resolveBaseUrl(options, command);
1884
1920
  const token = await resolveToken(options, baseUrl, deps, command);
1885
- const core = await import("./dist-I4IPYCRC.js");
1921
+ const core = await import("./dist-TBAQ5KOK.js");
1886
1922
  const status = token ? await core.checkUserStatus(baseUrl, token) : void 0;
1887
1923
  const projectId = await resolveReportProjectId({
1888
1924
  baseUrl,
@@ -1936,13 +1972,13 @@ var registerReportsCommand = (program2, deps) => {
1936
1972
  const data = reportTypes.length === 1 ? payload[reportTypes[0] === "architecture" ? "waf" : reportTypes[0]] : payload;
1937
1973
  if (options.output && reportTypes.length > 1) {
1938
1974
  const fs13 = await import("fs/promises");
1939
- const path11 = await import("path");
1975
+ const path10 = await import("path");
1940
1976
  const stat = await fs13.stat(options.output).catch(() => void 0);
1941
- if (stat?.isDirectory() || !path11.extname(options.output)) {
1977
+ if (stat?.isDirectory() || !path10.extname(options.output)) {
1942
1978
  await fs13.mkdir(options.output, { recursive: true });
1943
1979
  const files = [];
1944
1980
  for (const [key, value] of Object.entries(payload)) {
1945
- const file = path11.join(options.output, `${projectId}-${key}-report.json`);
1981
+ const file = path10.join(options.output, `${projectId}-${key}-report.json`);
1946
1982
  files.push(
1947
1983
  ...await writeDownloadPayload({
1948
1984
  command: "reports download",
@@ -1985,7 +2021,7 @@ var registerReportsCommand = (program2, deps) => {
1985
2021
  try {
1986
2022
  const baseUrl = await deps.resolveBaseUrl(options, command);
1987
2023
  const token = await resolveToken(options, baseUrl, deps, command);
1988
- const core = await import("./dist-I4IPYCRC.js");
2024
+ const core = await import("./dist-TBAQ5KOK.js");
1989
2025
  const status = token ? await core.checkUserStatus(baseUrl, token) : void 0;
1990
2026
  const projectId = await resolveReportProjectId({
1991
2027
  baseUrl,
@@ -2055,7 +2091,7 @@ var registerReportsCommand = (program2, deps) => {
2055
2091
  token,
2056
2092
  requestedProjectId: options.project
2057
2093
  });
2058
- const core = await import("./dist-I4IPYCRC.js");
2094
+ const core = await import("./dist-TBAQ5KOK.js");
2059
2095
  const status = token ? await core.checkUserStatus(baseUrl, token) : void 0;
2060
2096
  const report = await core.getWafReport({
2061
2097
  baseUrl,
@@ -2094,7 +2130,7 @@ var registerReportsCommand = (program2, deps) => {
2094
2130
  token,
2095
2131
  requestedProjectId: options.project
2096
2132
  });
2097
- const core = await import("./dist-I4IPYCRC.js");
2133
+ const core = await import("./dist-TBAQ5KOK.js");
2098
2134
  const status = token ? await core.checkUserStatus(baseUrl, token) : void 0;
2099
2135
  const report = await core.getReport({
2100
2136
  baseUrl,
@@ -2122,7 +2158,7 @@ var registerReportsCommand = (program2, deps) => {
2122
2158
  token,
2123
2159
  requestedProjectId: options.project
2124
2160
  });
2125
- const core = await import("./dist-I4IPYCRC.js");
2161
+ const core = await import("./dist-TBAQ5KOK.js");
2126
2162
  const status = token ? await core.checkUserStatus(baseUrl, token) : void 0;
2127
2163
  const report = await core.getCostReport({
2128
2164
  baseUrl,
@@ -2151,7 +2187,7 @@ var registerReportsCommand = (program2, deps) => {
2151
2187
  token,
2152
2188
  requestedProjectId: options.project
2153
2189
  });
2154
- const core = await import("./dist-I4IPYCRC.js");
2190
+ const core = await import("./dist-TBAQ5KOK.js");
2155
2191
  const status = token ? await core.checkUserStatus(baseUrl, token) : void 0;
2156
2192
  const report = await core.getWafReport({
2157
2193
  baseUrl,
@@ -2173,7 +2209,7 @@ var registerReportsCommand = (program2, deps) => {
2173
2209
 
2174
2210
  // src/recipesCommand.ts
2175
2211
  import { randomUUID } from "crypto";
2176
- import fs4 from "fs/promises";
2212
+ import fs2 from "fs/promises";
2177
2213
 
2178
2214
  // src/askProgress.ts
2179
2215
  var ASK_PROGRESS_MODES = /* @__PURE__ */ new Set(["auto", "stderr", "ndjson", "none"]);
@@ -3346,725 +3382,6 @@ var renderRecipeMarkdown = (recipe) => {
3346
3382
  ].join("\n");
3347
3383
  };
3348
3384
 
3349
- // src/cliConfig.ts
3350
- import fs2 from "fs/promises";
3351
- import os from "os";
3352
- import path2 from "path";
3353
- var CONFIG_PROFILE_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/;
3354
- var SETTINGS_FILE = "settings.json";
3355
- var normalizeConfigProfile = (profile) => {
3356
- const normalized = (profile || process.env.CLOUDEVAL_PROFILE || "default").trim() || "default";
3357
- if (!CONFIG_PROFILE_PATTERN.test(normalized)) {
3358
- throw new Error(
3359
- `Invalid profile '${normalized}'. Use letters, numbers, dashes, or underscores.`
3360
- );
3361
- }
3362
- return normalized;
3363
- };
3364
- var getActiveConfigProfile = (command) => {
3365
- const opts = typeof command?.optsWithGlobals === "function" ? command.optsWithGlobals() : command?.opts();
3366
- return normalizeConfigProfile(opts?.profile);
3367
- };
3368
- var getCloudevalConfigDir = () => path2.join(os.homedir(), ".config", "cloudeval");
3369
- var getCliConfigPath = (profile) => {
3370
- const normalized = normalizeConfigProfile(profile);
3371
- if (normalized === "default") {
3372
- return path2.join(getCloudevalConfigDir(), SETTINGS_FILE);
3373
- }
3374
- return path2.join(getCloudevalConfigDir(), "profiles", normalized, SETTINGS_FILE);
3375
- };
3376
- var ensureConfigParent = async (filePath) => {
3377
- await fs2.mkdir(path2.dirname(filePath), { recursive: true, mode: 448 });
3378
- };
3379
- var loadCliConfig = async (profile) => {
3380
- const filePath = getCliConfigPath(profile);
3381
- try {
3382
- const raw = await fs2.readFile(filePath, "utf8");
3383
- const parsed = JSON.parse(raw);
3384
- return parsed && typeof parsed === "object" ? parsed : {};
3385
- } catch (error) {
3386
- if (error?.code === "ENOENT") {
3387
- return {};
3388
- }
3389
- throw error;
3390
- }
3391
- };
3392
- var saveCliConfig = async (config, profile) => {
3393
- const filePath = getCliConfigPath(profile);
3394
- await ensureConfigParent(filePath);
3395
- const tempPath = `${filePath}.${process.pid}.tmp`;
3396
- await fs2.writeFile(tempPath, `${JSON.stringify(config, null, 2)}
3397
- `, {
3398
- encoding: "utf8",
3399
- mode: 384
3400
- });
3401
- await fs2.rename(tempPath, filePath);
3402
- return filePath;
3403
- };
3404
- var listCliConfigProfiles = async () => {
3405
- const profiles = /* @__PURE__ */ new Set(["default"]);
3406
- const profilesDir = path2.join(getCloudevalConfigDir(), "profiles");
3407
- try {
3408
- const entries = await fs2.readdir(profilesDir, { withFileTypes: true });
3409
- for (const entry of entries) {
3410
- if (entry.isDirectory() && CONFIG_PROFILE_PATTERN.test(entry.name)) {
3411
- profiles.add(entry.name);
3412
- }
3413
- }
3414
- } catch (error) {
3415
- if (error?.code !== "ENOENT") {
3416
- throw error;
3417
- }
3418
- }
3419
- return [...profiles].sort();
3420
- };
3421
- var keyAliases = {
3422
- project: "defaultProjectId",
3423
- projectId: "defaultProjectId",
3424
- defaultProject: "defaultProjectId",
3425
- defaultProjectId: "defaultProjectId",
3426
- model: "model",
3427
- mode: "mode",
3428
- chatMode: "mode",
3429
- defaultMode: "mode",
3430
- baseUrl: "baseUrl",
3431
- frontendUrl: "frontendUrl",
3432
- outputFormat: "outputFormat",
3433
- format: "outputFormat"
3434
- };
3435
- var normalizeCliMode = (value) => {
3436
- const normalized = value?.trim().toLowerCase();
3437
- if (!normalized) {
3438
- return void 0;
3439
- }
3440
- if (normalized === "ask" || normalized === "agent") {
3441
- return normalized;
3442
- }
3443
- throw new Error("mode must be one of: ask, agent");
3444
- };
3445
- var normalizeConfigKey = (key) => {
3446
- const normalized = key.trim();
3447
- const mapped = keyAliases[normalized];
3448
- if (!mapped) {
3449
- throw new Error(
3450
- `Unsupported config key '${key}'. Supported keys: baseUrl, frontendUrl, defaultProjectId, model, mode, outputFormat.`
3451
- );
3452
- }
3453
- return mapped;
3454
- };
3455
- var readCliConfigValue = (config, key) => {
3456
- const normalized = normalizeConfigKey(key);
3457
- const value = config[normalized];
3458
- return typeof value === "string" ? value : void 0;
3459
- };
3460
- var writeCliConfigValue = (config, key, value) => {
3461
- const normalized = normalizeConfigKey(key);
3462
- const normalizedValue = normalized === "mode" ? normalizeCliMode(value) : value;
3463
- return {
3464
- ...config,
3465
- [normalized]: normalizedValue
3466
- };
3467
- };
3468
- var unsetCliConfigValue = (config, key) => {
3469
- const normalized = normalizeConfigKey(key);
3470
- const next = { ...config };
3471
- delete next[normalized];
3472
- return next;
3473
- };
3474
-
3475
- // src/sessionsStore.ts
3476
- import fsSync from "fs";
3477
- import fs3 from "fs/promises";
3478
- import path3 from "path";
3479
- import { fileURLToPath as fileURLToPath2 } from "url";
3480
- var SQLJS_WASM_ENV_VAR = "CLOUDEVAL_SQLJS_WASM";
3481
- var legacySessionsDir = (profile) => {
3482
- const normalized = normalizeConfigProfile(profile);
3483
- if (normalized === "default") {
3484
- return path3.join(getCloudevalConfigDir(), "sessions");
3485
- }
3486
- return path3.join(getCloudevalConfigDir(), "profiles", normalized, "sessions");
3487
- };
3488
- var sessionsDatabasePath = (profile) => {
3489
- const normalized = normalizeConfigProfile(profile);
3490
- if (normalized === "default") {
3491
- return path3.join(getCloudevalConfigDir(), "sessions.sqlite");
3492
- }
3493
- return path3.join(getCloudevalConfigDir(), "profiles", normalized, "sessions.sqlite");
3494
- };
3495
- var legacySessionPath = (threadId, profile) => path3.join(legacySessionsDir(profile), `${sanitizeThreadId(threadId)}.json`);
3496
- var sanitizeThreadId = (threadId) => threadId.replace(/[^a-zA-Z0-9_.-]/g, "_");
3497
- var nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
3498
- var titleFromQuestion = (question) => {
3499
- const singleLine = question.replace(/[^\p{L}\p{N}\s'-]/gu, " ").replace(/\s+/g, " ").trim().replace(/^(can you|could you|please|help me|show me|tell me|what is|what are|how do i)\s+/i, "").replace(/^(review|investigate|triage|summarize|explain|analyze)\s+the\s+/i, "$1 ");
3500
- if (!singleLine) {
3501
- return "Untitled CloudEval session";
3502
- }
3503
- const words = singleLine.split(/\s+/).slice(0, 7);
3504
- const joined = words.join(" ");
3505
- const title = joined.charAt(0).toUpperCase() + joined.slice(1);
3506
- return title.length > 80 ? `${title.slice(0, 77)}...` : title;
3507
- };
3508
- var sanitizeTitle = (title) => {
3509
- const cleaned = title.replace(/[\u0000-\u001f\u007f]/g, " ").replace(/[\u200b-\u200f\u202a-\u202e]/g, "").replace(/\s+/g, " ").trim();
3510
- if (!cleaned) {
3511
- throw new Error("Session title cannot be empty.");
3512
- }
3513
- return cleaned.length > 100 ? cleaned.slice(0, 100) : cleaned;
3514
- };
3515
- var hasFile2 = (candidate) => {
3516
- try {
3517
- return fsSync.statSync(candidate).isFile();
3518
- } catch {
3519
- return false;
3520
- }
3521
- };
3522
- var findSqlJsWasmInNodeModules = (dir) => {
3523
- const nodeModulesDir = path3.join(dir, "node_modules");
3524
- const direct = path3.join(nodeModulesDir, "sql.js", "dist", "sql-wasm.wasm");
3525
- if (hasFile2(direct)) {
3526
- return direct;
3527
- }
3528
- const pnpmRoot = path3.join(nodeModulesDir, ".pnpm");
3529
- if (!fsSync.existsSync(pnpmRoot)) {
3530
- return void 0;
3531
- }
3532
- for (const entry of fsSync.readdirSync(pnpmRoot)) {
3533
- if (!entry.startsWith("sql.js@")) {
3534
- continue;
3535
- }
3536
- const candidate = path3.join(
3537
- pnpmRoot,
3538
- entry,
3539
- "node_modules",
3540
- "sql.js",
3541
- "dist",
3542
- "sql-wasm.wasm"
3543
- );
3544
- if (hasFile2(candidate)) {
3545
- return candidate;
3546
- }
3547
- }
3548
- return void 0;
3549
- };
3550
- var searchSqlJsWasmFrom = (start) => {
3551
- let current = path3.resolve(start);
3552
- while (true) {
3553
- const localCandidates = [
3554
- path3.join(current, "sql-wasm.wasm"),
3555
- path3.join(current, "dist", "sql-wasm.wasm")
3556
- ];
3557
- for (const candidate of localCandidates) {
3558
- if (hasFile2(candidate)) {
3559
- return candidate;
3560
- }
3561
- }
3562
- const nodeModulesMatch = findSqlJsWasmInNodeModules(current);
3563
- if (nodeModulesMatch) {
3564
- return nodeModulesMatch;
3565
- }
3566
- const parent = path3.dirname(current);
3567
- if (parent === current) {
3568
- return void 0;
3569
- }
3570
- current = parent;
3571
- }
3572
- };
3573
- var resolveSqlJsWasmPath = () => {
3574
- if (process.env[SQLJS_WASM_ENV_VAR]) {
3575
- return process.env[SQLJS_WASM_ENV_VAR];
3576
- }
3577
- const moduleDir = path3.dirname(fileURLToPath2(import.meta.url));
3578
- const seen = /* @__PURE__ */ new Set();
3579
- const roots = [process.cwd(), moduleDir, path3.dirname(process.execPath)];
3580
- for (const root of roots) {
3581
- const resolvedRoot = path3.resolve(root);
3582
- if (seen.has(resolvedRoot)) {
3583
- continue;
3584
- }
3585
- seen.add(resolvedRoot);
3586
- const match = searchSqlJsWasmFrom(resolvedRoot);
3587
- if (match) {
3588
- return match;
3589
- }
3590
- }
3591
- return void 0;
3592
- };
3593
- var isBunRuntime = () => typeof process.versions.bun === "string";
3594
- var dynamicImport = new Function("specifier", "return import(specifier)");
3595
- var openBunDatabase = async (dbPath) => {
3596
- if (!isBunRuntime()) {
3597
- return null;
3598
- }
3599
- try {
3600
- const { Database } = await dynamicImport("bun:sqlite");
3601
- await fs3.mkdir(path3.dirname(dbPath), { recursive: true, mode: 448 });
3602
- const db = new Database(dbPath, { create: true });
3603
- db.exec("PRAGMA foreign_keys = ON");
3604
- return {
3605
- rows(sql, params = []) {
3606
- const statement = db.query(sql);
3607
- return statement.all(...params);
3608
- },
3609
- run(sql, params = []) {
3610
- const statement = db.query(sql);
3611
- statement.run(...params);
3612
- },
3613
- async persist() {
3614
- },
3615
- close() {
3616
- db.close();
3617
- }
3618
- };
3619
- } catch {
3620
- return null;
3621
- }
3622
- };
3623
- var sqlJsFactoryPromise = null;
3624
- var loadSqlJsFactory = async () => {
3625
- if (!sqlJsFactoryPromise) {
3626
- sqlJsFactoryPromise = (async () => {
3627
- const initSqlJs = (await import("sql.js")).default;
3628
- const wasmPath = resolveSqlJsWasmPath();
3629
- return initSqlJs({
3630
- locateFile: (file) => {
3631
- if (file === "sql-wasm.wasm" && wasmPath) {
3632
- return wasmPath;
3633
- }
3634
- return file;
3635
- }
3636
- });
3637
- })();
3638
- }
3639
- return sqlJsFactoryPromise;
3640
- };
3641
- var openSqlJsDatabase = async (dbPath) => {
3642
- await fs3.mkdir(path3.dirname(dbPath), { recursive: true, mode: 448 });
3643
- const SQL = await loadSqlJsFactory();
3644
- let bytes;
3645
- try {
3646
- bytes = await fs3.readFile(dbPath);
3647
- } catch (error) {
3648
- if (error?.code !== "ENOENT") {
3649
- throw error;
3650
- }
3651
- }
3652
- const db = bytes ? new SQL.Database(bytes) : new SQL.Database();
3653
- db.run("PRAGMA foreign_keys = ON");
3654
- let dirty = false;
3655
- return {
3656
- rows(sql, params = []) {
3657
- const statement = db.prepare(sql, params);
3658
- const rows = [];
3659
- try {
3660
- while (statement.step()) {
3661
- rows.push(statement.getAsObject());
3662
- }
3663
- } finally {
3664
- statement.free();
3665
- }
3666
- return rows;
3667
- },
3668
- run(sql, params = []) {
3669
- db.run(sql, params);
3670
- dirty = true;
3671
- },
3672
- async persist() {
3673
- if (!dirty) {
3674
- return;
3675
- }
3676
- const tempPath = `${dbPath}.${process.pid}.tmp`;
3677
- await fs3.writeFile(tempPath, Buffer.from(db.export()), { mode: 384 });
3678
- await fs3.rename(tempPath, dbPath);
3679
- dirty = false;
3680
- },
3681
- close() {
3682
- db.close();
3683
- }
3684
- };
3685
- };
3686
- var openSessionDatabase = async (profile) => {
3687
- const dbPath = sessionsDatabasePath(profile);
3688
- return await openBunDatabase(dbPath) ?? openSqlJsDatabase(dbPath);
3689
- };
3690
- var ensureSchema = (db) => {
3691
- db.run(`
3692
- CREATE TABLE IF NOT EXISTS sessions (
3693
- thread_id TEXT PRIMARY KEY,
3694
- title TEXT NOT NULL,
3695
- project_id TEXT,
3696
- project_name TEXT,
3697
- model TEXT,
3698
- profile TEXT NOT NULL,
3699
- created_at TEXT NOT NULL,
3700
- updated_at TEXT NOT NULL,
3701
- message_count INTEGER NOT NULL DEFAULT 0
3702
- )
3703
- `);
3704
- db.run(`
3705
- CREATE TABLE IF NOT EXISTS session_messages (
3706
- session_thread_id TEXT NOT NULL,
3707
- message_index INTEGER NOT NULL,
3708
- role TEXT NOT NULL CHECK (role IN ('user', 'assistant')),
3709
- content TEXT NOT NULL,
3710
- created_at TEXT NOT NULL,
3711
- PRIMARY KEY (session_thread_id, message_index),
3712
- FOREIGN KEY (session_thread_id) REFERENCES sessions(thread_id) ON DELETE CASCADE
3713
- )
3714
- `);
3715
- db.run("CREATE INDEX IF NOT EXISTS idx_sessions_updated_at ON sessions(updated_at DESC)");
3716
- db.run(
3717
- "CREATE INDEX IF NOT EXISTS idx_session_messages_thread ON session_messages(session_thread_id, message_index)"
3718
- );
3719
- };
3720
- var optionalString = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
3721
- var messageFromRow = (row) => {
3722
- const role = row.role;
3723
- const content = row.content;
3724
- const createdAt = row.created_at;
3725
- if (role !== "user" && role !== "assistant" || typeof content !== "string") {
3726
- return null;
3727
- }
3728
- return {
3729
- role,
3730
- content,
3731
- createdAt: typeof createdAt === "string" ? createdAt : nowIso()
3732
- };
3733
- };
3734
- var sessionFromRow = (db, row) => {
3735
- const threadId = row.thread_id;
3736
- const title = row.title;
3737
- const createdAt = row.created_at;
3738
- const updatedAt = row.updated_at;
3739
- if (typeof threadId !== "string" || typeof title !== "string" || typeof createdAt !== "string" || typeof updatedAt !== "string") {
3740
- return null;
3741
- }
3742
- const messages = db.rows(
3743
- `SELECT role, content, created_at
3744
- FROM session_messages
3745
- WHERE session_thread_id = ?
3746
- ORDER BY message_index ASC`,
3747
- [threadId]
3748
- ).map(messageFromRow).filter((message) => Boolean(message));
3749
- return {
3750
- threadId,
3751
- title,
3752
- projectId: optionalString(row.project_id),
3753
- projectName: optionalString(row.project_name),
3754
- model: optionalString(row.model),
3755
- profile: optionalString(row.profile),
3756
- createdAt,
3757
- updatedAt,
3758
- messageCount: Number(row.message_count) || messages.length,
3759
- messages
3760
- };
3761
- };
3762
- var getSessionFromDb = (db, threadId) => {
3763
- const row = db.rows("SELECT * FROM sessions WHERE thread_id = ?", [threadId])[0];
3764
- return row ? sessionFromRow(db, row) : null;
3765
- };
3766
- var runTransaction = (db, fn) => {
3767
- db.run("BEGIN IMMEDIATE");
3768
- try {
3769
- const result = fn();
3770
- db.run("COMMIT");
3771
- return result;
3772
- } catch (error) {
3773
- try {
3774
- db.run("ROLLBACK");
3775
- } catch {
3776
- }
3777
- throw error;
3778
- }
3779
- };
3780
- var upsertSession = (db, session) => {
3781
- runTransaction(db, () => {
3782
- db.run(
3783
- `INSERT INTO sessions (
3784
- thread_id,
3785
- title,
3786
- project_id,
3787
- project_name,
3788
- model,
3789
- profile,
3790
- created_at,
3791
- updated_at,
3792
- message_count
3793
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
3794
- ON CONFLICT(thread_id) DO UPDATE SET
3795
- title = excluded.title,
3796
- project_id = excluded.project_id,
3797
- project_name = excluded.project_name,
3798
- model = excluded.model,
3799
- profile = excluded.profile,
3800
- created_at = excluded.created_at,
3801
- updated_at = excluded.updated_at,
3802
- message_count = excluded.message_count`,
3803
- [
3804
- session.threadId,
3805
- session.title,
3806
- session.projectId ?? null,
3807
- session.projectName ?? null,
3808
- session.model ?? null,
3809
- session.profile ?? "default",
3810
- session.createdAt,
3811
- session.updatedAt,
3812
- session.messages.length
3813
- ]
3814
- );
3815
- db.run("DELETE FROM session_messages WHERE session_thread_id = ?", [session.threadId]);
3816
- session.messages.forEach((message, index) => {
3817
- db.run(
3818
- `INSERT INTO session_messages (
3819
- session_thread_id,
3820
- message_index,
3821
- role,
3822
- content,
3823
- created_at
3824
- ) VALUES (?, ?, ?, ?, ?)`,
3825
- [session.threadId, index, message.role, message.content, message.createdAt]
3826
- );
3827
- });
3828
- });
3829
- };
3830
- var readLegacySessionFile = async (filePath) => {
3831
- try {
3832
- const raw = await fs3.readFile(filePath, "utf8");
3833
- const parsed = JSON.parse(raw);
3834
- return parsed && typeof parsed === "object" ? normalizeLegacySession(parsed) : null;
3835
- } catch (error) {
3836
- if (error?.code === "ENOENT") {
3837
- return null;
3838
- }
3839
- throw error;
3840
- }
3841
- };
3842
- var normalizeLegacySession = (value) => {
3843
- if (!value || typeof value !== "object" || typeof value.threadId !== "string") {
3844
- return null;
3845
- }
3846
- const timestamp = nowIso();
3847
- const messages = Array.isArray(value.messages) ? value.messages.map((message) => {
3848
- if (!message || message.role !== "user" && message.role !== "assistant" || typeof message.content !== "string") {
3849
- return null;
3850
- }
3851
- return {
3852
- role: message.role,
3853
- content: message.content,
3854
- createdAt: typeof message.createdAt === "string" ? message.createdAt : timestamp
3855
- };
3856
- }).filter(
3857
- (message) => Boolean(message)
3858
- ) : [];
3859
- return {
3860
- threadId: value.threadId,
3861
- title: typeof value.title === "string" && value.title.trim() ? sanitizeTitle(value.title) : "Untitled CloudEval session",
3862
- projectId: optionalString(value.projectId),
3863
- projectName: optionalString(value.projectName),
3864
- model: optionalString(value.model),
3865
- profile: optionalString(value.profile),
3866
- createdAt: typeof value.createdAt === "string" ? value.createdAt : timestamp,
3867
- updatedAt: typeof value.updatedAt === "string" ? value.updatedAt : timestamp,
3868
- messageCount: messages.length,
3869
- messages
3870
- };
3871
- };
3872
- var readLegacySessions = async (profile) => {
3873
- try {
3874
- const dir = legacySessionsDir(profile);
3875
- const entries = await fs3.readdir(dir, { withFileTypes: true });
3876
- const sessions = await Promise.all(
3877
- entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => readLegacySessionFile(path3.join(dir, entry.name)))
3878
- );
3879
- return sessions.filter((session) => Boolean(session));
3880
- } catch (error) {
3881
- if (error?.code === "ENOENT") {
3882
- return [];
3883
- }
3884
- throw error;
3885
- }
3886
- };
3887
- var migrateLegacyJsonSessions = async (db, profile) => {
3888
- const normalizedProfile = normalizeConfigProfile(profile);
3889
- const legacySessions = await readLegacySessions(normalizedProfile);
3890
- for (const session of legacySessions) {
3891
- if (getSessionFromDb(db, session.threadId)) {
3892
- continue;
3893
- }
3894
- upsertSession(db, {
3895
- ...session,
3896
- profile: session.profile ?? normalizedProfile,
3897
- messageCount: session.messages.length
3898
- });
3899
- }
3900
- };
3901
- var withSessionDatabase = async (profile, fn) => {
3902
- const normalizedProfile = normalizeConfigProfile(profile);
3903
- const db = await openSessionDatabase(normalizedProfile);
3904
- try {
3905
- ensureSchema(db);
3906
- await migrateLegacyJsonSessions(db, normalizedProfile);
3907
- const result = await fn(db, normalizedProfile);
3908
- await db.persist();
3909
- return result;
3910
- } finally {
3911
- db.close();
3912
- }
3913
- };
3914
- var recordSessionTurn = async ({
3915
- threadId,
3916
- question,
3917
- response,
3918
- project,
3919
- model,
3920
- profile
3921
- }) => {
3922
- if (!threadId || !question.trim() && !response.trim()) {
3923
- return;
3924
- }
3925
- await withSessionDatabase(profile, (db, normalizedProfile) => {
3926
- const existing = getSessionFromDb(db, threadId);
3927
- const timestamp = nowIso();
3928
- const messages = [
3929
- ...existing?.messages ?? [],
3930
- ...question.trim() ? [{ role: "user", content: question.trim(), createdAt: timestamp }] : [],
3931
- ...response.trim() ? [{ role: "assistant", content: response.trim(), createdAt: timestamp }] : []
3932
- ];
3933
- upsertSession(db, {
3934
- threadId,
3935
- title: existing?.title ?? titleFromQuestion(question),
3936
- projectId: project?.id ?? existing?.projectId,
3937
- projectName: project?.name ?? existing?.projectName,
3938
- model: model ?? existing?.model,
3939
- profile: normalizedProfile,
3940
- createdAt: existing?.createdAt ?? timestamp,
3941
- updatedAt: timestamp,
3942
- messageCount: messages.length,
3943
- messages
3944
- });
3945
- });
3946
- };
3947
- var listSessions = async (limit = 20, profile) => withSessionDatabase(profile, (db) => {
3948
- const rows = db.rows(
3949
- `SELECT *
3950
- FROM sessions
3951
- ORDER BY updated_at DESC
3952
- LIMIT ?`,
3953
- [Math.max(1, limit)]
3954
- );
3955
- return rows.map((row) => sessionFromRow(db, row)).filter((session) => Boolean(session));
3956
- });
3957
- var getSession = async (threadId, profile) => withSessionDatabase(profile, (db) => getSessionFromDb(db, threadId));
3958
- var renameSession = async (threadId, title, profile) => {
3959
- return withSessionDatabase(profile, (db) => {
3960
- const session = getSessionFromDb(db, threadId);
3961
- if (!session) {
3962
- return null;
3963
- }
3964
- const updated = {
3965
- ...session,
3966
- title: sanitizeTitle(title),
3967
- updatedAt: nowIso()
3968
- };
3969
- upsertSession(db, updated);
3970
- return updated;
3971
- });
3972
- };
3973
- var searchTerms = (query) => query.toLowerCase().split(/[^a-z0-9]+/i).map((term) => term.trim()).filter((term) => term.length > 1);
3974
- var countTerm = (text, term) => {
3975
- if (!text || !term) return 0;
3976
- const escaped = term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3977
- return text.match(new RegExp(escaped, "gi"))?.length ?? 0;
3978
- };
3979
- var previewFor = (session, terms) => {
3980
- const matched = session.messages.map((message) => ({
3981
- message,
3982
- score: terms.reduce((total, term) => total + countTerm(message.content, term), 0)
3983
- })).filter((entry) => entry.score > 0).sort((a, b) => b.score - a.score)[0]?.message;
3984
- const value = matched?.content ?? session.messages.at(-1)?.content ?? session.title;
3985
- const singleLine = value.replace(/\s+/g, " ").trim();
3986
- return singleLine.length > 180 ? `${singleLine.slice(0, 177)}...` : singleLine;
3987
- };
3988
- var scoreSession = (session, terms) => {
3989
- const title = session.title.toLowerCase();
3990
- const project = `${session.projectId ?? ""} ${session.projectName ?? ""}`.toLowerCase();
3991
- const messages = session.messages.map((message) => message.content).join("\n").toLowerCase();
3992
- return terms.reduce(
3993
- (score, term) => score + countTerm(title, term) * 8 + countTerm(project, term) * 4 + countTerm(messages, term),
3994
- 0
3995
- );
3996
- };
3997
- var toSearchResult = (session, score, terms) => ({
3998
- threadId: session.threadId,
3999
- title: session.title,
4000
- projectId: session.projectId,
4001
- projectName: session.projectName,
4002
- model: session.model,
4003
- profile: session.profile,
4004
- createdAt: session.createdAt,
4005
- updatedAt: session.updatedAt,
4006
- messageCount: session.messageCount,
4007
- score,
4008
- preview: previewFor(session, terms)
4009
- });
4010
- var searchSessions = async (query, options = {}) => {
4011
- const terms = searchTerms(query);
4012
- const sessions = await exportSessions(options.profile);
4013
- if (!terms.length) {
4014
- return sessions.slice(0, Math.max(1, options.limit ?? 20)).map((session) => toSearchResult(session, 0, []));
4015
- }
4016
- return sessions.map((session) => ({
4017
- session,
4018
- score: scoreSession(session, terms)
4019
- })).filter((entry) => entry.score > 0).sort((a, b) => b.score - a.score || b.session.updatedAt.localeCompare(a.session.updatedAt)).slice(0, Math.max(1, options.limit ?? 20)).map((entry) => toSearchResult(entry.session, entry.score, terms));
4020
- };
4021
- var resolveSessionReference = async (reference, profile) => {
4022
- const trimmed = reference.trim();
4023
- if (!trimmed) {
4024
- return null;
4025
- }
4026
- const exact = await getSession(trimmed, profile);
4027
- if (exact) {
4028
- return exact;
4029
- }
4030
- const sessions = await exportSessions(profile);
4031
- const lower = trimmed.toLowerCase();
4032
- return sessions.find((session) => session.title.toLowerCase() === lower) ?? sessions.find((session) => session.threadId.startsWith(trimmed)) ?? sessions.find((session) => session.title.toLowerCase().includes(lower)) ?? null;
4033
- };
4034
- var deleteSession = async (threadId, profile) => {
4035
- const deleted = await withSessionDatabase(profile, (db) => {
4036
- const existing = getSessionFromDb(db, threadId);
4037
- if (!existing) {
4038
- return false;
4039
- }
4040
- db.run("DELETE FROM sessions WHERE thread_id = ?", [threadId]);
4041
- return true;
4042
- });
4043
- try {
4044
- await fs3.unlink(legacySessionPath(threadId, profile));
4045
- } catch (error) {
4046
- if (error?.code !== "ENOENT") {
4047
- throw error;
4048
- }
4049
- }
4050
- return deleted;
4051
- };
4052
- var exportSessions = async (profile) => listSessions(Number.MAX_SAFE_INTEGER, profile);
4053
- var pruneSessions = async (olderThanDays, profile) => {
4054
- const cutoff = Date.now() - Math.max(1, olderThanDays) * 24 * 60 * 60 * 1e3;
4055
- const sessions = await exportSessions(profile);
4056
- let deleted = 0;
4057
- for (const session of sessions) {
4058
- const updatedAt = Date.parse(session.updatedAt);
4059
- if (Number.isFinite(updatedAt) && updatedAt < cutoff) {
4060
- if (await deleteSession(session.threadId, profile)) {
4061
- deleted++;
4062
- }
4063
- }
4064
- }
4065
- return deleted;
4066
- };
4067
-
4068
3385
  // src/recipesCommand.ts
4069
3386
  var STREAM_OUTPUT_NODES = /* @__PURE__ */ new Set([
4070
3387
  "generate_response",
@@ -4128,7 +3445,7 @@ var writeRecipeList = async (options) => {
4128
3445
  if (format === "table" || format === "text") {
4129
3446
  const text = renderRecipesTable();
4130
3447
  if (options.output) {
4131
- await fs4.writeFile(options.output, text, "utf8");
3448
+ await fs2.writeFile(options.output, text, "utf8");
4132
3449
  return;
4133
3450
  }
4134
3451
  process.stdout.write(text);
@@ -4137,7 +3454,7 @@ var writeRecipeList = async (options) => {
4137
3454
  if (format === "markdown") {
4138
3455
  const text = renderRecipesMarkdown();
4139
3456
  if (options.output) {
4140
- await fs4.writeFile(options.output, text, "utf8");
3457
+ await fs2.writeFile(options.output, text, "utf8");
4141
3458
  return;
4142
3459
  }
4143
3460
  process.stdout.write(text);
@@ -4155,7 +3472,7 @@ var writeRecipeShow = async (recipe, options) => {
4155
3472
  if (format === "markdown" || format === "text") {
4156
3473
  const text = renderRecipeMarkdown(recipe);
4157
3474
  if (options.output) {
4158
- await fs4.writeFile(options.output, text, "utf8");
3475
+ await fs2.writeFile(options.output, text, "utf8");
4159
3476
  return;
4160
3477
  }
4161
3478
  process.stdout.write(text);
@@ -4212,7 +3529,7 @@ var runChatRecipe = async (recipe, prompt2, options, command, deps) => {
4212
3529
  });
4213
3530
  progressWriter.write({ type: "auth", step: "auth", message: "Resolving authentication" });
4214
3531
  const context = await resolveAuthContext(options, command, deps);
4215
- const core = await import("./dist-I4IPYCRC.js");
3532
+ const core = await import("./dist-TBAQ5KOK.js");
4216
3533
  progressWriter.write({
4217
3534
  type: "request",
4218
3535
  step: "project",
@@ -4264,88 +3581,528 @@ var runChatRecipe = async (recipe, prompt2, options, command, deps) => {
4264
3581
  throw new Error(chunk.message || chunk.description || "CloudEval recipe failed.");
4265
3582
  }
4266
3583
  }
4267
- const finalMessage = [...chatState.messages].reverse().find((message) => message.role === "assistant");
4268
- const finalResponse = collapseRepeatedAssistantText(finalMessage?.content || responseText || "");
4269
- if (!finalResponse.trim()) {
4270
- throw new Error("No final response returned by CloudEval for this recipe.");
3584
+ const finalMessage = [...chatState.messages].reverse().find((message) => message.role === "assistant");
3585
+ const finalResponse = collapseRepeatedAssistantText(finalMessage?.content || responseText || "");
3586
+ if (!finalResponse.trim()) {
3587
+ throw new Error("No final response returned by CloudEval for this recipe.");
3588
+ }
3589
+ const frontendUrl = buildFrontendUrl({
3590
+ baseUrl: resolveFrontendBaseUrl({
3591
+ frontendUrl: selectedFrontendUrl,
3592
+ apiBaseUrl: context.baseUrl
3593
+ }),
3594
+ target: "chat",
3595
+ threadId: chatState.threadId
3596
+ });
3597
+ await recordSessionTurn({
3598
+ threadId: chatState.threadId,
3599
+ question: prompt2,
3600
+ response: finalResponse,
3601
+ project: { id: project.id, name: project.name },
3602
+ model: selectedModel,
3603
+ profile: selectedProfile
3604
+ }).catch(() => void 0);
3605
+ progressWriter.clear();
3606
+ return {
3607
+ recipeId: recipe.id,
3608
+ title: recipe.title,
3609
+ mode: recipe.mode,
3610
+ prompt: prompt2,
3611
+ response: finalResponse,
3612
+ threadId: chatState.threadId,
3613
+ project: { id: project.id, name: project.name },
3614
+ commands: renderRecipeCommands(recipe, recipeContext(options)),
3615
+ safety: recipe.safety,
3616
+ frontendUrl
3617
+ };
3618
+ };
3619
+ var buildGuideResult = (recipe, context) => ({
3620
+ recipeId: recipe.id,
3621
+ title: recipe.title,
3622
+ mode: recipe.mode,
3623
+ prompt: renderRecipePrompt(recipe, context),
3624
+ commands: renderRecipeCommands(recipe, context),
3625
+ inputs: context,
3626
+ safety: recipe.safety,
3627
+ expectedOutput: recipe.expectedOutput,
3628
+ note: "This recipe requires explicit user-run commands for side effects; no project, file, billing, or MCP config mutation was performed."
3629
+ });
3630
+ var registerRecipesCommand = (program2, deps) => {
3631
+ const command = program2.command("recipes").description("CloudEval reusable recipes and agent skills");
3632
+ command.command("list").description("List CloudEval recipes").option("--format <format>", "Output format: table, text, json, ndjson, markdown", "table").option("--output <file>", "Output file").action((options) => writeRecipeList(options));
3633
+ command.command("show").description("Show a CloudEval recipe").argument("<id>", "Recipe id").option("--format <format>", "Output format: text, json, ndjson, markdown", "markdown").option("--output <file>", "Output file").action(async (id, options) => {
3634
+ const recipe = getRecipe(id);
3635
+ if (!recipe) {
3636
+ console.error(`Unknown recipe '${id}'. Run 'cloudeval recipes list' to see available recipes.`);
3637
+ process.exit(1);
3638
+ }
3639
+ await writeRecipeShow(recipe, options);
3640
+ });
3641
+ addAuthOptions(
3642
+ command.command("run").description("Run a CloudEval recipe or print explicit commands for side-effecting recipes").argument("<id>", "Recipe id"),
3643
+ deps.defaultBaseUrl
3644
+ ).option("--project <id>", "Project ID to use").option("--connection-id <id>", "Connection id for connection recipes").option("--credential-id <id>", "Credential id for credential recipes").option("--range <range>", "Usage/report range such as 7d, 30d, 90d, all", "30d").option("--template-file <path>", "Local JSON template file").option("--template-url <url>", "Template URL").option("--parameters-file <path>", "Local parameters file").option("--parameters-url <url>", "Parameters URL").option("--provider <provider>", "Cloud provider accepted by projects create").option("--name <name>", "Project or recipe display name").option("--output-path <path>", "Side-effect output path for guide recipes").option("--output-dir <path>", "Side-effect output directory for guide recipes").option("--client <name>", "MCP client name for setup recipes").option("--layout <layout>", "Visualization layout such as architecture or dependency").option("--model <name>", "Model name").option("--thread <id>", "Thread id to reuse").option("--format <format>", "Output format: text, json, ndjson, markdown", "text").option("--output <file>", "Output file").option("--progress <mode>", "Progress events: auto, stderr, ndjson, none", "auto").option("--quiet", "Suppress progress and warning messages", false).option("--frontend-url <url>", "Frontend base URL").action(async (id, options, actionCommand) => {
3645
+ const recipe = getRecipe(id);
3646
+ if (!recipe) {
3647
+ console.error(`Unknown recipe '${id}'. Run 'cloudeval recipes list' to see available recipes.`);
3648
+ process.exit(1);
3649
+ }
3650
+ try {
3651
+ const context = recipeContext(options);
3652
+ const prompt2 = renderRecipePrompt(recipe, context);
3653
+ const result = recipe.mode === "guide" ? buildGuideResult(recipe, context) : await runChatRecipe(recipe, prompt2, options, actionCommand, deps);
3654
+ const frontendUrl = "frontendUrl" in result ? result.frontendUrl : void 0;
3655
+ await writeFormattedOutput({
3656
+ command: "recipes run",
3657
+ data: result,
3658
+ format: options.format === "table" ? "text" : options.format,
3659
+ output: options.output,
3660
+ frontendUrl
3661
+ });
3662
+ } catch (error) {
3663
+ console.error(`Failed to run recipe: ${error?.message ?? "Unknown error"}`);
3664
+ process.exit(1);
3665
+ }
3666
+ });
3667
+ };
3668
+
3669
+ // src/skillsCommand.ts
3670
+ import fs4 from "fs/promises";
3671
+
3672
+ // src/skills/catalog.ts
3673
+ import fs3 from "fs/promises";
3674
+ import fsSync from "fs";
3675
+ import path2 from "path";
3676
+ import { fileURLToPath as fileURLToPath2 } from "url";
3677
+ var repoRoot = path2.resolve(fileURLToPath2(new URL("../../../../", import.meta.url)));
3678
+ var defaultSkillsPath = path2.join(repoRoot, "skills");
3679
+ var requiredSkillSections = [
3680
+ "## WHEN",
3681
+ "## DO NOT USE FOR",
3682
+ "## Required CloudEval Context",
3683
+ "## CLI Commands",
3684
+ "## MCP Tools",
3685
+ "## Safety Requirements",
3686
+ "## Expected Output / Proof",
3687
+ "## Failure Handling"
3688
+ ];
3689
+ var bundledSkillMetadata = [
3690
+ {
3691
+ id: "cloudeval",
3692
+ title: "CloudEval Skill Router",
3693
+ description: "Routes CloudEval CLI and MCP work across projects, reports, billing, credentials, connections, diagnostics, recipes, and agent workflows."
3694
+ },
3695
+ {
3696
+ id: "cloudeval-agent-ops",
3697
+ title: "CloudEval Agent Ops",
3698
+ description: "Runs CloudEval ask, agent, chat/TUI, model selection, recipes, and local session recovery."
3699
+ },
3700
+ {
3701
+ id: "cloudeval-billing",
3702
+ title: "CloudEval Billing",
3703
+ description: "Inspects CloudEval credits, plans, usage, ledger, invoices, notifications, top-ups, and checkout links."
3704
+ },
3705
+ {
3706
+ id: "cloudeval-connections",
3707
+ title: "CloudEval Connections",
3708
+ description: "Inspects CloudEval cloud/template connections, connection health, and connection frontend links."
3709
+ },
3710
+ {
3711
+ id: "cloudeval-cost",
3712
+ title: "CloudEval Cost",
3713
+ description: "Triages CloudEval cost reports, billing usage, savings opportunities, anomalies, and credit impact."
3714
+ },
3715
+ {
3716
+ id: "cloudeval-credentials",
3717
+ title: "CloudEval Credentials",
3718
+ description: "Manages CloudEval scoped access-key templates, creation, inspection, rotation, and revocation."
3719
+ },
3720
+ {
3721
+ id: "cloudeval-graph-intelligence",
3722
+ title: "CloudEval Graph Intelligence",
3723
+ description: "Inspects project graphs, graph drift, sync history, dependency impact, critical paths, and graph-derived risk signals."
3724
+ },
3725
+ {
3726
+ id: "cloudeval-mcp-diagnostics",
3727
+ title: "CloudEval MCP Diagnostics",
3728
+ description: "Sets up and diagnoses CloudEval MCP, config, auth, doctor, status, update, completion, banner, and deeplinks."
3729
+ },
3730
+ {
3731
+ id: "cloudeval-projects",
3732
+ title: "CloudEval Projects",
3733
+ description: "Lists, inspects, creates, opens, and health-checks CloudEval projects and template project workflows."
3734
+ },
3735
+ {
3736
+ id: "cloudeval-reports",
3737
+ title: "CloudEval Reports",
3738
+ description: "Lists, shows, generates, downloads, and summarizes CloudEval cost and Well-Architected reports."
3739
+ },
3740
+ {
3741
+ id: "cloudeval-template-validation",
3742
+ title: "CloudEval Template Validation",
3743
+ description: "Validates, parses, tests, and release-gates CloudEval-supported cloud template files."
3744
+ },
3745
+ {
3746
+ id: "cloudeval-visualizations",
3747
+ title: "CloudEval Visualizations",
3748
+ description: "Exports CloudEval architecture or dependency diagrams and prepares visual evidence for reviews."
3749
+ },
3750
+ {
3751
+ id: "cloudeval-waf",
3752
+ title: "CloudEval WAF",
3753
+ description: "Triages CloudEval Well-Architected findings, failed rules, pillar risk, and remediation plans."
3754
+ }
3755
+ ];
3756
+ var metadataById = new Map(bundledSkillMetadata.map((skill) => [skill.id, skill]));
3757
+ var normalizeSkillId = (id) => {
3758
+ const normalized = id.trim().toLowerCase();
3759
+ if (metadataById.has(normalized)) {
3760
+ return normalized;
3761
+ }
3762
+ const prefixed = `cloudeval-${normalized}`;
3763
+ return metadataById.has(prefixed) ? prefixed : normalized;
3764
+ };
3765
+ var getSkillsPath = () => process.env.CLOUDEVAL_SKILLS_PATH ?? defaultSkillsPath;
3766
+ var skillFilePath = (id) => path2.join(getSkillsPath(), id, "SKILL.md");
3767
+ var fileExists = (filePath) => {
3768
+ try {
3769
+ return fsSync.statSync(filePath).isFile();
3770
+ } catch {
3771
+ return false;
3772
+ }
3773
+ };
3774
+ var readSkillFile = async (id) => {
3775
+ const filePath = skillFilePath(id);
3776
+ if (fileExists(filePath)) {
3777
+ return {
3778
+ content: await fs3.readFile(filePath, "utf8"),
3779
+ path: filePath,
3780
+ source: "filesystem"
3781
+ };
3782
+ }
3783
+ return {
3784
+ content: synthesizeSkillContent(metadataById.get(id) ?? {
3785
+ id,
3786
+ title: id,
3787
+ description: "CloudEval skill metadata."
3788
+ }),
3789
+ source: "embedded"
3790
+ };
3791
+ };
3792
+ var recipesForSkill = (id) => recipes.filter((recipe) => recipe.skill === id);
3793
+ var unique = (values) => [...new Set(values)].filter(Boolean).sort();
3794
+ var summarizeSkill = async (metadata) => {
3795
+ const file = await readSkillFile(metadata.id);
3796
+ const skillRecipes = recipesForSkill(metadata.id);
3797
+ return {
3798
+ ...metadata,
3799
+ path: file.path,
3800
+ source: file.source,
3801
+ recipes: skillRecipes.map((recipe) => recipe.id),
3802
+ mcpTools: unique(skillRecipes.flatMap((recipe) => recipe.mcpTools))
3803
+ };
3804
+ };
3805
+ var listSkills = async () => Promise.all(bundledSkillMetadata.map(summarizeSkill));
3806
+ var getSkill = async (id) => {
3807
+ const normalized = normalizeSkillId(id);
3808
+ const metadata = metadataById.get(normalized);
3809
+ if (!metadata) {
3810
+ return void 0;
3811
+ }
3812
+ const [summary, file] = await Promise.all([
3813
+ summarizeSkill(metadata),
3814
+ readSkillFile(metadata.id)
3815
+ ]);
3816
+ return {
3817
+ ...summary,
3818
+ content: file.content
3819
+ };
3820
+ };
3821
+ var synthesizeSkillContent = (metadata) => `---
3822
+ name: ${metadata.id}
3823
+ description: ${metadata.description}
3824
+ ---
3825
+
3826
+ # ${metadata.title}
3827
+
3828
+ ## WHEN
3829
+ - Use this bundled CloudEval skill when source SKILL.md files are unavailable at runtime.
3830
+
3831
+ ## DO NOT USE FOR
3832
+ - Unsupported CloudEval capabilities or private backend internals.
3833
+
3834
+ ## Required CloudEval Context
3835
+ - Run \`cloudeval capabilities --format json\` and \`cloudeval recipes list\` to discover available commands.
3836
+
3837
+ ## CLI Commands
3838
+ - \`cloudeval skills show ${metadata.id}\`
3839
+ - \`cloudeval recipes list\`
3840
+
3841
+ ## MCP Tools
3842
+ - \`capabilities_get\`
3843
+ - \`recipes_list\`
3844
+
3845
+ ## Safety Requirements
3846
+ - Redact secrets, account identifiers, session identifiers, tenant identifiers, billing data, and raw report payloads by default.
3847
+
3848
+ ## Expected Output / Proof
3849
+ - State the command or MCP tool used and separate confirmed evidence from missing data.
3850
+
3851
+ ## Failure Handling
3852
+ - If the source skill file is needed, run from a CloudEval CLI checkout or inspect the public repository.
3853
+ `;
3854
+ var frontMatterNamePattern = (id) => new RegExp(`^---[\\s\\S]*?^name:\\s*${id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*$[\\s\\S]*?^---`, "m");
3855
+ var sensitivePatterns = [
3856
+ /sk-or-v1-[a-z0-9]{16,}/i,
3857
+ /sk-[a-z0-9]{20,}/i,
3858
+ /ghp_[a-z0-9]{20,}/i,
3859
+ /xox[baprs]-[a-z0-9-]{20,}/i,
3860
+ /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/
3861
+ ];
3862
+ var unsupportedClaims = [
3863
+ /\bterraform\b/i,
3864
+ /\bpull request\b/i,
3865
+ /\bgithub pr\b/i,
3866
+ /\bpr integration\b/i
3867
+ ];
3868
+ var validateSkillText = (id, text, file) => {
3869
+ const checks = [];
3870
+ checks.push({
3871
+ id: `${id}:frontmatter-name`,
3872
+ status: frontMatterNamePattern(id).test(text) ? "pass" : "fail",
3873
+ message: `Skill ${id} declares matching frontmatter name.`,
3874
+ file
3875
+ });
3876
+ for (const section of requiredSkillSections) {
3877
+ checks.push({
3878
+ id: `${id}:section:${section.replace(/^##\s+/, "").toLowerCase().replace(/[^a-z0-9]+/g, "-")}`,
3879
+ status: text.includes(section) ? "pass" : "fail",
3880
+ message: `Skill ${id} includes ${section}.`,
3881
+ file
3882
+ });
3883
+ }
3884
+ for (const pattern of sensitivePatterns) {
3885
+ checks.push({
3886
+ id: `${id}:no-sensitive-pattern:${pattern.source.slice(0, 24)}`,
3887
+ status: pattern.test(text) ? "fail" : "pass",
3888
+ message: `Skill ${id} does not contain obvious secret patterns.`,
3889
+ file
3890
+ });
3891
+ }
3892
+ for (const pattern of unsupportedClaims) {
3893
+ checks.push({
3894
+ id: `${id}:no-unsupported-claim:${pattern.source}`,
3895
+ status: pattern.test(text) ? "fail" : "pass",
3896
+ message: `Skill ${id} does not claim unsupported workflows.`,
3897
+ file
3898
+ });
3899
+ }
3900
+ return checks;
3901
+ };
3902
+ var validateSkills = async () => {
3903
+ const skills = await listSkills();
3904
+ const knownIds = new Set(skills.map((skill) => skill.id));
3905
+ const checks = [];
3906
+ for (const recipe of recipes) {
3907
+ checks.push({
3908
+ id: `recipe:${recipe.id}:skill-exists`,
3909
+ status: knownIds.has(recipe.skill) ? "pass" : "fail",
3910
+ message: `Recipe ${recipe.id} references implemented skill ${recipe.skill}.`
3911
+ });
3912
+ }
3913
+ for (const skill of skills) {
3914
+ const file = await readSkillFile(skill.id);
3915
+ checks.push({
3916
+ id: `${skill.id}:source`,
3917
+ status: file.source === "filesystem" ? "pass" : "fail",
3918
+ message: `Skill ${skill.id} has a public SKILL.md file.`,
3919
+ file: file.path
3920
+ });
3921
+ checks.push(...validateSkillText(skill.id, file.content, file.path));
4271
3922
  }
4272
- const frontendUrl = buildFrontendUrl({
4273
- baseUrl: resolveFrontendBaseUrl({
4274
- frontendUrl: selectedFrontendUrl,
4275
- apiBaseUrl: context.baseUrl
4276
- }),
4277
- target: "chat",
4278
- threadId: chatState.threadId
4279
- });
4280
- await recordSessionTurn({
4281
- threadId: chatState.threadId,
4282
- question: prompt2,
4283
- response: finalResponse,
4284
- project: { id: project.id, name: project.name },
4285
- model: selectedModel,
4286
- profile: selectedProfile
4287
- }).catch(() => void 0);
4288
- progressWriter.clear();
4289
3923
  return {
4290
- recipeId: recipe.id,
4291
- title: recipe.title,
4292
- mode: recipe.mode,
4293
- prompt: prompt2,
4294
- response: finalResponse,
4295
- threadId: chatState.threadId,
4296
- project: { id: project.id, name: project.name },
4297
- commands: renderRecipeCommands(recipe, recipeContext(options)),
4298
- safety: recipe.safety,
4299
- frontendUrl
3924
+ ok: checks.every((check) => check.status === "pass"),
3925
+ skillsPath: getSkillsPath(),
3926
+ skills,
3927
+ checks
4300
3928
  };
4301
3929
  };
4302
- var buildGuideResult = (recipe, context) => ({
4303
- recipeId: recipe.id,
4304
- title: recipe.title,
4305
- mode: recipe.mode,
4306
- prompt: renderRecipePrompt(recipe, context),
4307
- commands: renderRecipeCommands(recipe, context),
4308
- inputs: context,
4309
- safety: recipe.safety,
4310
- expectedOutput: recipe.expectedOutput,
4311
- note: "This recipe requires explicit user-run commands for side effects; no project, file, billing, or MCP config mutation was performed."
3930
+ var skillsResourceData = async () => ({
3931
+ skillsPath: getSkillsPath(),
3932
+ skills: await listSkills(),
3933
+ requiredSections: [...requiredSkillSections]
4312
3934
  });
4313
- var registerRecipesCommand = (program2, deps) => {
4314
- const command = program2.command("recipes").description("CloudEval reusable recipes and agent skills");
4315
- command.command("list").description("List CloudEval recipes").option("--format <format>", "Output format: table, text, json, ndjson, markdown", "table").option("--output <file>", "Output file").action((options) => writeRecipeList(options));
4316
- command.command("show").description("Show a CloudEval recipe").argument("<id>", "Recipe id").option("--format <format>", "Output format: text, json, ndjson, markdown", "markdown").option("--output <file>", "Output file").action(async (id, options) => {
4317
- const recipe = getRecipe(id);
4318
- if (!recipe) {
4319
- console.error(`Unknown recipe '${id}'. Run 'cloudeval recipes list' to see available recipes.`);
4320
- process.exit(1);
3935
+
3936
+ // src/skillsCommand.ts
3937
+ var renderSkillsTable = (skills) => formatTextTable(
3938
+ skills.map((skill) => ({
3939
+ id: skill.id,
3940
+ title: skill.title,
3941
+ recipes: skill.recipes.length,
3942
+ tools: skill.mcpTools.length,
3943
+ source: skill.source
3944
+ })),
3945
+ [
3946
+ { key: "id", header: "ID", width: 36 },
3947
+ { key: "title", header: "Title", maxWidth: 40 },
3948
+ { key: "recipes", header: "Recipes", align: "right" },
3949
+ { key: "tools", header: "MCP Tools", align: "right" },
3950
+ { key: "source", header: "Source", maxWidth: 12 }
3951
+ ],
3952
+ { emptyMessage: "No CloudEval skills found." }
3953
+ );
3954
+ var renderSkillsMarkdown = (skills) => [
3955
+ "# CloudEval Skills",
3956
+ "",
3957
+ ...skills.map((skill) => [
3958
+ `## ${skill.title}`,
3959
+ "",
3960
+ `- ID: ${skill.id}`,
3961
+ `- Source: ${skill.source}`,
3962
+ `- Recipes: ${skill.recipes.length ? skill.recipes.join(", ") : "none"}`,
3963
+ `- MCP tools: ${skill.mcpTools.length ? skill.mcpTools.join(", ") : "none"}`,
3964
+ "",
3965
+ skill.description,
3966
+ ""
3967
+ ].join("\n"))
3968
+ ].join("\n");
3969
+ var writeSkillList = async (options) => {
3970
+ const skills = await listSkills();
3971
+ const format = options.format ?? "table";
3972
+ if (format === "table" || format === "text") {
3973
+ const text = renderSkillsTable(skills);
3974
+ if (options.output) {
3975
+ await fs4.writeFile(options.output, text, "utf8");
3976
+ return;
4321
3977
  }
4322
- await writeRecipeShow(recipe, options);
3978
+ process.stdout.write(text);
3979
+ return;
3980
+ }
3981
+ if (format === "markdown") {
3982
+ const text = renderSkillsMarkdown(skills);
3983
+ if (options.output) {
3984
+ await fs4.writeFile(options.output, text, "utf8");
3985
+ return;
3986
+ }
3987
+ process.stdout.write(text);
3988
+ return;
3989
+ }
3990
+ await writeFormattedOutput({
3991
+ command: "skills list",
3992
+ data: { skills },
3993
+ format,
3994
+ output: options.output
4323
3995
  });
4324
- addAuthOptions(
4325
- command.command("run").description("Run a CloudEval recipe or print explicit commands for side-effecting recipes").argument("<id>", "Recipe id"),
4326
- deps.defaultBaseUrl
4327
- ).option("--project <id>", "Project ID to use").option("--connection-id <id>", "Connection id for connection recipes").option("--credential-id <id>", "Credential id for credential recipes").option("--range <range>", "Usage/report range such as 7d, 30d, 90d, all", "30d").option("--template-file <path>", "Local JSON template file").option("--template-url <url>", "Template URL").option("--parameters-file <path>", "Local parameters file").option("--parameters-url <url>", "Parameters URL").option("--provider <provider>", "Cloud provider accepted by projects create").option("--name <name>", "Project or recipe display name").option("--output-path <path>", "Side-effect output path for guide recipes").option("--output-dir <path>", "Side-effect output directory for guide recipes").option("--client <name>", "MCP client name for setup recipes").option("--layout <layout>", "Visualization layout such as architecture or dependency").option("--model <name>", "Model name").option("--thread <id>", "Thread id to reuse").option("--format <format>", "Output format: text, json, ndjson, markdown", "text").option("--output <file>", "Output file").option("--progress <mode>", "Progress events: auto, stderr, ndjson, none", "auto").option("--quiet", "Suppress progress and warning messages", false).option("--frontend-url <url>", "Frontend base URL").action(async (id, options, actionCommand) => {
4328
- const recipe = getRecipe(id);
4329
- if (!recipe) {
4330
- console.error(`Unknown recipe '${id}'. Run 'cloudeval recipes list' to see available recipes.`);
4331
- process.exit(1);
3996
+ };
3997
+ var writeSkillShow = async (id, options) => {
3998
+ const skill = await getSkill(id);
3999
+ if (!skill) {
4000
+ process.stderr.write(`Unknown skill '${id}'. Run 'cloudeval skills list' to see available skills.
4001
+ `);
4002
+ process.exit(1);
4003
+ }
4004
+ const format = options.format ?? "markdown";
4005
+ if (format === "markdown" || format === "text") {
4006
+ if (options.output) {
4007
+ await fs4.writeFile(options.output, skill.content, "utf8");
4008
+ return;
4332
4009
  }
4333
- try {
4334
- const context = recipeContext(options);
4335
- const prompt2 = renderRecipePrompt(recipe, context);
4336
- const result = recipe.mode === "guide" ? buildGuideResult(recipe, context) : await runChatRecipe(recipe, prompt2, options, actionCommand, deps);
4337
- const frontendUrl = "frontendUrl" in result ? result.frontendUrl : void 0;
4338
- await writeFormattedOutput({
4339
- command: "recipes run",
4340
- data: result,
4341
- format: options.format === "table" ? "text" : options.format,
4342
- output: options.output,
4343
- frontendUrl
4344
- });
4345
- } catch (error) {
4346
- console.error(`Failed to run recipe: ${error?.message ?? "Unknown error"}`);
4347
- process.exit(1);
4010
+ process.stdout.write(skill.content);
4011
+ if (!skill.content.endsWith("\n")) {
4012
+ process.stdout.write("\n");
4013
+ }
4014
+ return;
4015
+ }
4016
+ await writeFormattedOutput({
4017
+ command: "skills show",
4018
+ data: skill,
4019
+ format: format === "table" ? "text" : format,
4020
+ output: options.output
4021
+ });
4022
+ };
4023
+ var writeSkillDoctor = async (options) => {
4024
+ const result = await validateSkills();
4025
+ const format = options.format ?? "table";
4026
+ if (format === "table" || format === "text") {
4027
+ const failed = result.checks.filter((check) => check.status === "fail");
4028
+ const text = [
4029
+ formatTextTable(
4030
+ [
4031
+ {
4032
+ ok: result.ok ? "yes" : "no",
4033
+ skills: result.skills.length,
4034
+ checks: result.checks.length,
4035
+ failures: failed.length,
4036
+ path: result.skillsPath
4037
+ }
4038
+ ],
4039
+ [
4040
+ { key: "ok", header: "OK", width: 4 },
4041
+ { key: "skills", header: "Skills", align: "right" },
4042
+ { key: "checks", header: "Checks", align: "right" },
4043
+ { key: "failures", header: "Failures", align: "right" },
4044
+ { key: "path", header: "Path", maxWidth: 72 }
4045
+ ]
4046
+ ).trimEnd(),
4047
+ failed.length ? "\nFailures\n" + formatTextTable(
4048
+ failed.map((check) => ({
4049
+ check: check.id,
4050
+ message: check.message,
4051
+ file: check.file ?? ""
4052
+ })),
4053
+ [
4054
+ { key: "check", header: "Check", maxWidth: 44 },
4055
+ { key: "message", header: "Message", maxWidth: 72 },
4056
+ { key: "file", header: "File", maxWidth: 64 }
4057
+ ]
4058
+ ).trimEnd() : "",
4059
+ ""
4060
+ ].filter(Boolean).join("\n");
4061
+ if (options.output) {
4062
+ await fs4.writeFile(options.output, `${text}
4063
+ `, "utf8");
4064
+ return;
4065
+ }
4066
+ process.stdout.write(`${text}
4067
+ `);
4068
+ if (!result.ok) {
4069
+ process.exitCode = 1;
4070
+ }
4071
+ return;
4072
+ }
4073
+ await writeFormattedOutput({
4074
+ command: "skills doctor",
4075
+ data: result,
4076
+ format,
4077
+ output: options.output
4078
+ });
4079
+ if (!result.ok) {
4080
+ process.exitCode = 1;
4081
+ }
4082
+ };
4083
+ var registerSkillsCommand = (program2) => {
4084
+ const command = program2.command("skills").description("List, inspect, and validate CloudEval agent skills");
4085
+ command.command("list").description("List CloudEval skills").option("--format <format>", "Output format: table, text, json, ndjson, markdown", "table").option("--output <file>", "Output file").action((options) => writeSkillList(options));
4086
+ command.command("show").description("Show a CloudEval skill").argument("<id>", "Skill id").option("--format <format>", "Output format: text, json, ndjson, markdown", "markdown").option("--output <file>", "Output file").action((id, options) => writeSkillShow(id, options));
4087
+ command.command("doctor").description("Validate CloudEval skill files and recipe references").option("--format <format>", "Output format: table, text, json, ndjson, markdown", "table").option("--output <file>", "Output file").action((options) => writeSkillDoctor(options));
4088
+ command.command("path").description("Print the CloudEval skills directory").option("--format <format>", "Output format: text, json, ndjson, markdown", "text").option("--output <file>", "Output file").action(async (options) => {
4089
+ const skillsPath = getSkillsPath();
4090
+ if (!options.format || options.format === "text" || options.format === "table") {
4091
+ const text = `${skillsPath}
4092
+ `;
4093
+ if (options.output) {
4094
+ await fs4.writeFile(options.output, text, "utf8");
4095
+ return;
4096
+ }
4097
+ process.stdout.write(text);
4098
+ return;
4348
4099
  }
4100
+ await writeFormattedOutput({
4101
+ command: "skills path",
4102
+ data: { skillsPath },
4103
+ format: options.format,
4104
+ output: options.output
4105
+ });
4349
4106
  });
4350
4107
  };
4351
4108
 
@@ -4507,7 +4264,7 @@ var registerOpenCommand = (program2, deps) => {
4507
4264
  };
4508
4265
 
4509
4266
  // src/projectsCommand.ts
4510
- import path4 from "path";
4267
+ import path3 from "path";
4511
4268
  import fs5 from "fs/promises";
4512
4269
 
4513
4270
  // src/projectDiagramImage.ts
@@ -4687,12 +4444,12 @@ var responseErrorMessage = async (response) => {
4687
4444
  var fetchCloudEvalJson = async ({
4688
4445
  baseUrl,
4689
4446
  authToken,
4690
- path: path11,
4447
+ path: path10,
4691
4448
  method = "GET",
4692
4449
  query = {},
4693
4450
  body
4694
4451
  }) => {
4695
- const url = new URL(`${normalizeApiBase(baseUrl)}${path11}`);
4452
+ const url = new URL(`${normalizeApiBase(baseUrl)}${path10}`);
4696
4453
  for (const [key, value] of Object.entries(query)) {
4697
4454
  if (value !== void 0 && value !== null && value !== "") {
4698
4455
  url.searchParams.set(key, String(value));
@@ -4880,11 +4637,11 @@ var fileBlob = async (filePath) => {
4880
4637
  const bytes = await fs5.readFile(filePath);
4881
4638
  return {
4882
4639
  blob: new Blob([bytes], { type: "application/json" }),
4883
- name: path4.basename(filePath)
4640
+ name: path3.basename(filePath)
4884
4641
  };
4885
4642
  };
4886
4643
  var writeDiagramImageHeaders = async (outputPath, headers) => {
4887
- await fs5.mkdir(path4.dirname(outputPath), { recursive: true });
4644
+ await fs5.mkdir(path3.dirname(outputPath), { recursive: true });
4888
4645
  const text = Object.entries(headers).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `${key}: ${value}`).join("\n");
4889
4646
  await fs5.writeFile(outputPath, `${text}
4890
4647
  `, "utf8");
@@ -5021,7 +4778,7 @@ var configureDiagramExportCommand = (command, deps) => addAuthOptions(command, d
5021
4778
  const context = requireAuthUser(
5022
4779
  await resolveAuthContext(options, actionCommand, deps)
5023
4780
  );
5024
- const core = await import("./dist-I4IPYCRC.js");
4781
+ const core = await import("./dist-TBAQ5KOK.js");
5025
4782
  const projects = await core.getProjects(
5026
4783
  context.baseUrl,
5027
4784
  context.token,
@@ -5049,9 +4806,9 @@ var configureDiagramExportCommand = (command, deps) => addAuthOptions(command, d
5049
4806
  publicGraph,
5050
4807
  syncVersion: options.syncVersion
5051
4808
  });
5052
- const outputPath = path4.resolve(options.output);
5053
- const headersOutputPath = options.headersOutput ? path4.resolve(options.headersOutput) : void 0;
5054
- await fs5.mkdir(path4.dirname(outputPath), { recursive: true });
4809
+ const outputPath = path3.resolve(options.output);
4810
+ const headersOutputPath = options.headersOutput ? path3.resolve(options.headersOutput) : void 0;
4811
+ await fs5.mkdir(path3.dirname(outputPath), { recursive: true });
5055
4812
  await fs5.writeFile(outputPath, result.bytes);
5056
4813
  const filesWritten = [outputPath];
5057
4814
  if (headersOutputPath) {
@@ -5098,7 +4855,7 @@ var registerProjectsCommand = (program2, deps) => {
5098
4855
  addCommon(addAuthOptions(projects.command("list").description("List projects"), deps.defaultBaseUrl)).action(async (options, command) => {
5099
4856
  try {
5100
4857
  const context = await resolveAuthContext(options, command, deps);
5101
- const core = await import("./dist-I4IPYCRC.js");
4858
+ const core = await import("./dist-TBAQ5KOK.js");
5102
4859
  const data = await listProjectsForContext(core, context);
5103
4860
  const url = buildFrontendUrl({ baseUrl: frontendBase(context, options), target: "projects" });
5104
4861
  await writeProjectListOutput({ data, options, frontendUrl: url });
@@ -5116,7 +4873,7 @@ var registerProjectsCommand = (program2, deps) => {
5116
4873
  ).action(async (id, options, command) => {
5117
4874
  try {
5118
4875
  const context = await resolveAuthContext(options, command, deps);
5119
- const core = await import("./dist-I4IPYCRC.js");
4876
+ const core = await import("./dist-TBAQ5KOK.js");
5120
4877
  const list = await listProjectsForContext(core, context);
5121
4878
  const data = list.find((project) => project.id === id);
5122
4879
  if (!data) {
@@ -5180,10 +4937,10 @@ var registerProjectsCommand = (program2, deps) => {
5180
4937
  addCommon(addAuthOptions(projects.command("create").description("Create a quick template project"), deps.defaultBaseUrl)).option("--template-url <url>", "Template URL").option("--template-file <path>", "Local JSON template file").option("--parameters-file <path>", "Local JSON parameters file").option("--parameters-url <url>", "Parameters file URL").option("--name <name>", "Project name").option("--description <text>", "Project description").option("--provider <provider>", "Cloud provider: azure, aws, gcp").action(async (options, command) => {
5181
4938
  try {
5182
4939
  const context = requireAuthUser(await resolveAuthContext(options, command, deps));
5183
- const core = await import("./dist-I4IPYCRC.js");
4940
+ const core = await import("./dist-TBAQ5KOK.js");
5184
4941
  const template = await fileBlob(options.templateFile);
5185
4942
  const parameters = await fileBlob(options.parametersFile);
5186
- const inferredName = options.name || (options.templateFile ? path4.basename(options.templateFile, path4.extname(options.templateFile)) : void 0);
4943
+ const inferredName = options.name || (options.templateFile ? path3.basename(options.templateFile, path3.extname(options.templateFile)) : void 0);
5187
4944
  const result = await core.createQuickProject({
5188
4945
  baseUrl: context.baseUrl,
5189
4946
  authToken: context.token,
@@ -5303,7 +5060,7 @@ var registerConnectionsCommand = (program2, deps) => {
5303
5060
  addCommon2(addAuthOptions(connections.command("list").description("List connections"), deps.defaultBaseUrl)).action(async (options, command) => {
5304
5061
  try {
5305
5062
  const context = requireAuthUser(await resolveAuthContext(options, command, deps));
5306
- const core = await import("./dist-I4IPYCRC.js");
5063
+ const core = await import("./dist-TBAQ5KOK.js");
5307
5064
  const data = await core.listConnections({
5308
5065
  baseUrl: context.baseUrl,
5309
5066
  authToken: context.token,
@@ -5328,7 +5085,7 @@ var registerConnectionsCommand = (program2, deps) => {
5328
5085
  ).action(async (id, options, command) => {
5329
5086
  try {
5330
5087
  const context = requireAuthUser(await resolveAuthContext(options, command, deps));
5331
- const core = await import("./dist-I4IPYCRC.js");
5088
+ const core = await import("./dist-TBAQ5KOK.js");
5332
5089
  const data = await core.getConnection({
5333
5090
  baseUrl: context.baseUrl,
5334
5091
  authToken: context.token,
@@ -5666,7 +5423,7 @@ var checkoutReturnUrl = (context, options) => options.returnTo || billingUrl(con
5666
5423
  var runTopUpCheckout = async (commandName, packId, options, command, deps) => {
5667
5424
  try {
5668
5425
  const context = requireAuthUser(await resolveAuthContext(options, command, deps));
5669
- const core = await import("./dist-I4IPYCRC.js");
5426
+ const core = await import("./dist-TBAQ5KOK.js");
5670
5427
  const returnTo = checkoutReturnUrl(context, options);
5671
5428
  const session = await core.createTopUpCheckoutSession({
5672
5429
  baseUrl: context.baseUrl,
@@ -5706,14 +5463,27 @@ var registerBillingCommands = (program2, deps) => {
5706
5463
  ).action(async (options, command) => {
5707
5464
  try {
5708
5465
  const context = requireAuthUser(await resolveAuthContext(options, command, deps));
5709
- const core = await import("./dist-I4IPYCRC.js");
5710
- const entitlement = await core.getBillingEntitlement({
5711
- baseUrl: context.baseUrl,
5712
- authToken: context.token
5466
+ const core = await import("./dist-TBAQ5KOK.js");
5467
+ const range = rangeToDates("30d");
5468
+ const [entitlement, usageSummary] = await Promise.all([
5469
+ core.getBillingEntitlement({
5470
+ baseUrl: context.baseUrl,
5471
+ authToken: context.token
5472
+ }),
5473
+ core.getBillingUsageSummary({
5474
+ baseUrl: context.baseUrl,
5475
+ authToken: context.token,
5476
+ startAt: range.startAt,
5477
+ endAt: range.endAt,
5478
+ granularity: "day"
5479
+ }).catch(() => null)
5480
+ ]);
5481
+ const usageCreditsUsed = core.getBillingUsageCreditsUsed(usageSummary);
5482
+ const status = core.getCreditStatus(entitlement, {
5483
+ reportedUsedCredits: usageCreditsUsed
5713
5484
  });
5714
- const status = core.getCreditStatus(entitlement);
5715
5485
  const url = billingUrl(context, { ...options, tab: "usage" });
5716
- await write("credits", { status, entitlement }, options, url);
5486
+ await write("credits", { status, entitlement, usageCreditsUsed }, options, url);
5717
5487
  await maybeOpen3(url, options);
5718
5488
  } catch (error) {
5719
5489
  failBillingCommand("credits", "Failed to show credits", error, options);
@@ -5723,15 +5493,31 @@ var registerBillingCommands = (program2, deps) => {
5723
5493
  addCommon3(addAuthOptions(billing.command("summary").description("Show billing summary"), deps.defaultBaseUrl)).action(async (options, command) => {
5724
5494
  try {
5725
5495
  const context = requireAuthUser(await resolveAuthContext(options, command, deps));
5726
- const core = await import("./dist-I4IPYCRC.js");
5727
- const [entitlement, subscriptionStatus] = await Promise.all([
5496
+ const core = await import("./dist-TBAQ5KOK.js");
5497
+ const range = rangeToDates("30d");
5498
+ const [entitlement, subscriptionStatus, usageSummary] = await Promise.all([
5728
5499
  core.getBillingEntitlement({ baseUrl: context.baseUrl, authToken: context.token }),
5729
- core.getSubscriptionStatus({ baseUrl: context.baseUrl, authToken: context.token })
5500
+ core.getSubscriptionStatus({ baseUrl: context.baseUrl, authToken: context.token }),
5501
+ core.getBillingUsageSummary({
5502
+ baseUrl: context.baseUrl,
5503
+ authToken: context.token,
5504
+ startAt: range.startAt,
5505
+ endAt: range.endAt,
5506
+ granularity: "day"
5507
+ }).catch(() => null)
5730
5508
  ]);
5509
+ const usageCreditsUsed = core.getBillingUsageCreditsUsed(usageSummary);
5731
5510
  const url = billingUrl(context, { ...options, tab: "plans" });
5732
5511
  await write(
5733
5512
  "billing summary",
5734
- { creditStatus: core.getCreditStatus(entitlement), entitlement, subscriptionStatus },
5513
+ {
5514
+ creditStatus: core.getCreditStatus(entitlement, {
5515
+ reportedUsedCredits: usageCreditsUsed
5516
+ }),
5517
+ entitlement,
5518
+ subscriptionStatus,
5519
+ usageCreditsUsed
5520
+ },
5735
5521
  options,
5736
5522
  url
5737
5523
  );
@@ -5743,7 +5529,7 @@ var registerBillingCommands = (program2, deps) => {
5743
5529
  addCommon3(addAuthOptions(billing.command("plans").description("Show billing plans"), deps.defaultBaseUrl)).action(async (options, command) => {
5744
5530
  try {
5745
5531
  const context = await resolveAuthContext(options, command, deps);
5746
- const core = await import("./dist-I4IPYCRC.js");
5532
+ const core = await import("./dist-TBAQ5KOK.js");
5747
5533
  const data = await core.getBillingConfig({ baseUrl: context.baseUrl, authToken: context.token });
5748
5534
  const url = billingUrl(context, { ...options, tab: "plans" });
5749
5535
  await write("billing plans", data, options, url);
@@ -5755,7 +5541,7 @@ var registerBillingCommands = (program2, deps) => {
5755
5541
  addCommon3(addAuthOptions(billing.command("usage").description("Show billing usage summary"), deps.defaultBaseUrl)).option("--range <range>", "Usage range: 7d, 30d, 90d, all", "30d").option("--start-at <iso>", "Start timestamp").option("--end-at <iso>", "End timestamp").option("--granularity <value>", "Granularity: hour, day, month", "day").option("--action-type <type>", "Action type filter").option("--model <name>", "Model filter").option("--outcome <outcome>", "Outcome filter").option("--charge-status <status>", "Charge status filter").action(async (options, command) => {
5756
5542
  try {
5757
5543
  const context = requireAuthUser(await resolveAuthContext(options, command, deps));
5758
- const core = await import("./dist-I4IPYCRC.js");
5544
+ const core = await import("./dist-TBAQ5KOK.js");
5759
5545
  const range = rangeToDates(options.range);
5760
5546
  const data = await core.getBillingUsageSummary({
5761
5547
  baseUrl: context.baseUrl,
@@ -5778,7 +5564,7 @@ var registerBillingCommands = (program2, deps) => {
5778
5564
  addCommon3(addAuthOptions(billing.command("ledger").description("Show billing ledger"), deps.defaultBaseUrl)).option("--range <range>", "Usage range: 7d, 30d, 90d, all", "30d").option("--start-at <iso>", "Start timestamp").option("--end-at <iso>", "End timestamp").option("--action-type <type>", "Action type filter").option("--model <name>", "Model filter").option("--outcome <outcome>", "Outcome filter").option("--charge-status <status>", "Charge status filter").option("--limit <n>", "Page size", "25").option("--cursor <cursor>", "Pagination cursor").action(async (options, command) => {
5779
5565
  try {
5780
5566
  const context = requireAuthUser(await resolveAuthContext(options, command, deps));
5781
- const core = await import("./dist-I4IPYCRC.js");
5567
+ const core = await import("./dist-TBAQ5KOK.js");
5782
5568
  const range = rangeToDates(options.range);
5783
5569
  const data = await core.getBillingUsageLedger({
5784
5570
  baseUrl: context.baseUrl,
@@ -5806,7 +5592,7 @@ var registerBillingCommands = (program2, deps) => {
5806
5592
  addCommon3(addAuthOptions(billing.command(name).description(`Show billing ${name}`), deps.defaultBaseUrl)).option("--limit <n>", "Result limit", "25").action(async (options, command) => {
5807
5593
  try {
5808
5594
  const context = requireAuthUser(await resolveAuthContext(options, command, deps));
5809
- const core = await import("./dist-I4IPYCRC.js");
5595
+ const core = await import("./dist-TBAQ5KOK.js");
5810
5596
  const data = await core[getter]({
5811
5597
  baseUrl: context.baseUrl,
5812
5598
  authToken: context.token,
@@ -5824,7 +5610,7 @@ var registerBillingCommands = (program2, deps) => {
5824
5610
  addCommon3(addAuthOptions(topups, deps.defaultBaseUrl)).option("--limit <n>", "Result limit", "25").action(async (options, command) => {
5825
5611
  try {
5826
5612
  const context = requireAuthUser(await resolveAuthContext(options, command, deps));
5827
- const core = await import("./dist-I4IPYCRC.js");
5613
+ const core = await import("./dist-TBAQ5KOK.js");
5828
5614
  const data = await core.getTopUpPacks({
5829
5615
  baseUrl: context.baseUrl,
5830
5616
  authToken: context.token
@@ -5860,13 +5646,13 @@ var registerBillingCommands = (program2, deps) => {
5860
5646
 
5861
5647
  // src/mcpCommand.ts
5862
5648
  import fs8 from "fs/promises";
5863
- import path6 from "path";
5649
+ import path5 from "path";
5864
5650
  import { randomUUID as randomUUID2 } from "crypto";
5865
5651
 
5866
5652
  // src/mcpSetupCommand.ts
5867
5653
  import fs6 from "fs/promises";
5868
- import os2 from "os";
5869
- import path5 from "path";
5654
+ import os from "os";
5655
+ import path4 from "path";
5870
5656
  var MCP_SETUP_CLIENTS = ["codex", "claude", "cursor", "vscode", "generic"];
5871
5657
  var CLIENTS = new Set(MCP_SETUP_CLIENTS);
5872
5658
  var TOOLSETS = /* @__PURE__ */ new Set([
@@ -5892,8 +5678,8 @@ var normalizeMcpSetupToolset = (value) => {
5892
5678
  };
5893
5679
  var defaultConfigPath = (client) => {
5894
5680
  if (client === "claude") {
5895
- return path5.join(
5896
- os2.homedir(),
5681
+ return path4.join(
5682
+ os.homedir(),
5897
5683
  "Library",
5898
5684
  "Application Support",
5899
5685
  "Claude",
@@ -5901,10 +5687,10 @@ var defaultConfigPath = (client) => {
5901
5687
  );
5902
5688
  }
5903
5689
  if (client === "cursor") {
5904
- return path5.join(os2.homedir(), ".cursor", "mcp.json");
5690
+ return path4.join(os.homedir(), ".cursor", "mcp.json");
5905
5691
  }
5906
5692
  if (client === "vscode") {
5907
- return path5.join(process.cwd(), ".vscode", "mcp.json");
5693
+ return path4.join(process.cwd(), ".vscode", "mcp.json");
5908
5694
  }
5909
5695
  return void 0;
5910
5696
  };
@@ -6039,7 +5825,7 @@ var writeMcpClientConfig = async (setup) => {
6039
5825
  cloudeval: nextServer
6040
5826
  }
6041
5827
  };
6042
- await fs6.mkdir(path5.dirname(setup.configPath), { recursive: true });
5828
+ await fs6.mkdir(path4.dirname(setup.configPath), { recursive: true });
6043
5829
  await fs6.writeFile(setup.configPath, `${JSON.stringify(next, null, 2)}
6044
5830
  `, {
6045
5831
  encoding: "utf8",
@@ -8005,6 +7791,13 @@ var mcpResourceDefinitions = [
8005
7791
  title: "CloudEval Recipes",
8006
7792
  description: "CloudEval reusable recipes and agent skill metadata.",
8007
7793
  mimeType: "application/json"
7794
+ },
7795
+ {
7796
+ uri: "cloudeval://skills",
7797
+ name: "skills",
7798
+ title: "CloudEval Skills",
7799
+ description: "Public CloudEval SKILL.md catalog for agent reasoning.",
7800
+ mimeType: "application/json"
8008
7801
  }
8009
7802
  ];
8010
7803
  var mcpPromptDefinitions = recipes.map((recipe) => ({
@@ -8022,7 +7815,8 @@ var MCP_RESOURCE_TOOL_REQUIREMENTS = {
8022
7815
  "cloudeval://projects": ["projects_list"],
8023
7816
  "cloudeval://billing/summary": ["billing_summary"],
8024
7817
  "cloudeval://reports/latest": ["reports_list"],
8025
- "cloudeval://recipes": ["recipes_list"]
7818
+ "cloudeval://recipes": ["recipes_list"],
7819
+ "cloudeval://skills": ["capabilities_get"]
8026
7820
  };
8027
7821
  var promptRequirementTools = (tools) => tools.filter((tool) => tool !== "ask" && tool !== "recipes_run");
8028
7822
  var MCP_PROMPT_TOOL_REQUIREMENTS = Object.fromEntries(
@@ -8180,7 +7974,7 @@ var isTerminalJobStatus3 = (value) => {
8180
7974
  };
8181
7975
  var sleep3 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
8182
7976
  var writeHeaderFile = async (outputPath, headers) => {
8183
- await fs8.mkdir(path6.dirname(outputPath), { recursive: true });
7977
+ await fs8.mkdir(path5.dirname(outputPath), { recursive: true });
8184
7978
  const text = Object.entries(headers).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `${key}: ${value}`).join("\n");
8185
7979
  await fs8.writeFile(outputPath, `${text}
8186
7980
  `, "utf8");
@@ -8214,7 +8008,7 @@ var reportsFrontendUrl = (config, input) => buildFrontendUrl({
8214
8008
  reportVerbosity: input.reportVerbosity
8215
8009
  });
8216
8010
  var resolveAuth = async (config, options = {}) => {
8217
- const core = await import("./dist-I4IPYCRC.js");
8011
+ const core = await import("./dist-TBAQ5KOK.js");
8218
8012
  core.assertSecureBaseUrl(config.baseUrl);
8219
8013
  let token;
8220
8014
  try {
@@ -8314,7 +8108,7 @@ var assertModelAvailable = async (config, token) => {
8314
8108
  if (!config.model) {
8315
8109
  return;
8316
8110
  }
8317
- const core = await import("./dist-I4IPYCRC.js");
8111
+ const core = await import("./dist-TBAQ5KOK.js");
8318
8112
  try {
8319
8113
  const response = await fetch(
8320
8114
  `${core.normalizeApiBase(config.baseUrl)}/models`,
@@ -8429,11 +8223,11 @@ var downloadReports = async (config, args, auth) => {
8429
8223
  if (outputPath) {
8430
8224
  if (reportTypes.length > 1) {
8431
8225
  const stat = await fs8.stat(outputPath).catch(() => void 0);
8432
- const outputIsDirectory = stat?.isDirectory() || !path6.extname(outputPath);
8226
+ const outputIsDirectory = stat?.isDirectory() || !path5.extname(outputPath);
8433
8227
  if (outputIsDirectory) {
8434
8228
  await fs8.mkdir(outputPath, { recursive: true });
8435
8229
  for (const [key, value] of Object.entries(payload)) {
8436
- const file = path6.join(outputPath, `${projectId}-${key}-report.json`);
8230
+ const file = path5.join(outputPath, `${projectId}-${key}-report.json`);
8437
8231
  await fs8.writeFile(
8438
8232
  file,
8439
8233
  `${JSON.stringify(value, null, 2)}
@@ -8443,7 +8237,7 @@ var downloadReports = async (config, args, auth) => {
8443
8237
  filesWritten.push(file);
8444
8238
  }
8445
8239
  } else {
8446
- await fs8.mkdir(path6.dirname(outputPath), { recursive: true });
8240
+ await fs8.mkdir(path5.dirname(outputPath), { recursive: true });
8447
8241
  await fs8.writeFile(
8448
8242
  outputPath,
8449
8243
  `${JSON.stringify(data, null, 2)}
@@ -8453,7 +8247,7 @@ var downloadReports = async (config, args, auth) => {
8453
8247
  filesWritten.push(outputPath);
8454
8248
  }
8455
8249
  } else {
8456
- await fs8.mkdir(path6.dirname(outputPath), { recursive: true });
8250
+ await fs8.mkdir(path5.dirname(outputPath), { recursive: true });
8457
8251
  await fs8.writeFile(
8458
8252
  outputPath,
8459
8253
  `${JSON.stringify(data, null, 2)}
@@ -8571,7 +8365,7 @@ var buildToolHandlers = (serverOptions) => {
8571
8365
  });
8572
8366
  handlers.set("agent_profiles_list", async (args) => {
8573
8367
  const config = await resolveInvocationConfig(serverOptions, args);
8574
- const core = await import("./dist-I4IPYCRC.js");
8368
+ const core = await import("./dist-TBAQ5KOK.js");
8575
8369
  const data = await listProfilesForDiscovery(core, config.baseUrl);
8576
8370
  return withEnvelope({
8577
8371
  command: "agents list",
@@ -8584,7 +8378,7 @@ var buildToolHandlers = (serverOptions) => {
8584
8378
  throw new Error("profileId is required.");
8585
8379
  }
8586
8380
  const config = await resolveInvocationConfig(serverOptions, args);
8587
- const core = await import("./dist-I4IPYCRC.js");
8381
+ const core = await import("./dist-TBAQ5KOK.js");
8588
8382
  const data = await getProfileForDiscovery(core, config.baseUrl, profileId);
8589
8383
  return withEnvelope({
8590
8384
  command: "agents show",
@@ -8764,7 +8558,7 @@ var buildToolHandlers = (serverOptions) => {
8764
8558
  if (!rawOutputPath) {
8765
8559
  throw new Error("outputPath is required.");
8766
8560
  }
8767
- const outputPath = path6.resolve(rawOutputPath);
8561
+ const outputPath = path5.resolve(rawOutputPath);
8768
8562
  const publicGraph = booleanValue(args.public) ?? false;
8769
8563
  const auth = publicGraph ? void 0 : await resolveAuth(config, { requireUser: true });
8770
8564
  const layout = normalizeProjectDiagramImageLayout(stringValue(args.layout));
@@ -8785,11 +8579,11 @@ var buildToolHandlers = (serverOptions) => {
8785
8579
  publicGraph,
8786
8580
  syncVersion: stringValue(args.syncVersion)
8787
8581
  });
8788
- await fs8.mkdir(path6.dirname(outputPath), { recursive: true });
8582
+ await fs8.mkdir(path5.dirname(outputPath), { recursive: true });
8789
8583
  await fs8.writeFile(outputPath, result.bytes);
8790
8584
  const filesWritten = [outputPath];
8791
8585
  const rawHeadersOutputPath = stringValue(args.headersOutputPath);
8792
- const headersOutputPath = rawHeadersOutputPath ? path6.resolve(rawHeadersOutputPath) : void 0;
8586
+ const headersOutputPath = rawHeadersOutputPath ? path5.resolve(rawHeadersOutputPath) : void 0;
8793
8587
  if (headersOutputPath) {
8794
8588
  await writeHeaderFile(headersOutputPath, result.headers);
8795
8589
  filesWritten.push(headersOutputPath);
@@ -9221,7 +9015,7 @@ var buildToolHandlers = (serverOptions) => {
9221
9015
  });
9222
9016
  handlers.set("auth_status", async (args) => {
9223
9017
  const config = await resolveInvocationConfig(serverOptions, args);
9224
- const core = await import("./dist-I4IPYCRC.js");
9018
+ const core = await import("./dist-TBAQ5KOK.js");
9225
9019
  const data = await core.getAuthStatus(config.baseUrl, { validate: true });
9226
9020
  return withEnvelope({
9227
9021
  command: "auth status",
@@ -9230,7 +9024,7 @@ var buildToolHandlers = (serverOptions) => {
9230
9024
  });
9231
9025
  handlers.set("status", async (args) => {
9232
9026
  const config = await resolveInvocationConfig(serverOptions, args);
9233
- const core = await import("./dist-I4IPYCRC.js");
9027
+ const core = await import("./dist-TBAQ5KOK.js");
9234
9028
  const auth = await core.getAuthStatus(config.baseUrl, { validate: true });
9235
9029
  return withEnvelope({
9236
9030
  command: "status",
@@ -9261,7 +9055,7 @@ var buildToolHandlers = (serverOptions) => {
9261
9055
  }
9262
9056
  ];
9263
9057
  try {
9264
- const core = await import("./dist-I4IPYCRC.js");
9058
+ const core = await import("./dist-TBAQ5KOK.js");
9265
9059
  core.assertSecureBaseUrl(config.baseUrl);
9266
9060
  checks.push({
9267
9061
  id: "base-url-secure",
@@ -9675,7 +9469,8 @@ var buildToolHandlers = (serverOptions) => {
9675
9469
  handlers.set("billing_summary", async (args) => {
9676
9470
  const config = await resolveInvocationConfig(serverOptions, args);
9677
9471
  const auth = await resolveAuth(config, { requireUser: true });
9678
- const [entitlement, subscriptionStatus] = await Promise.all([
9472
+ const range = rangeToDates2("30d");
9473
+ const [entitlement, subscriptionStatus, usageSummary] = await Promise.all([
9679
9474
  auth.core.getBillingEntitlement({
9680
9475
  baseUrl: config.baseUrl,
9681
9476
  authToken: auth.token
@@ -9683,19 +9478,30 @@ var buildToolHandlers = (serverOptions) => {
9683
9478
  auth.core.getSubscriptionStatus({
9684
9479
  baseUrl: config.baseUrl,
9685
9480
  authToken: auth.token
9686
- })
9481
+ }),
9482
+ auth.core.getBillingUsageSummary({
9483
+ baseUrl: config.baseUrl,
9484
+ authToken: auth.token,
9485
+ startAt: range.startAt,
9486
+ endAt: range.endAt,
9487
+ granularity: "day"
9488
+ }).catch(() => null)
9687
9489
  ]);
9688
9490
  const frontendUrl = buildFrontendUrl({
9689
9491
  baseUrl: frontendBase3(config),
9690
9492
  target: "billing",
9691
9493
  tab: "plans"
9692
9494
  });
9495
+ const usageCreditsUsed = auth.core.getBillingUsageCreditsUsed(usageSummary);
9693
9496
  return withEnvelope({
9694
9497
  command: "billing summary",
9695
9498
  data: {
9696
- creditStatus: auth.core.getCreditStatus(entitlement),
9499
+ creditStatus: auth.core.getCreditStatus(entitlement, {
9500
+ reportedUsedCredits: usageCreditsUsed
9501
+ }),
9697
9502
  entitlement,
9698
- subscriptionStatus
9503
+ subscriptionStatus,
9504
+ usageCreditsUsed
9699
9505
  },
9700
9506
  frontendUrl
9701
9507
  });
@@ -9769,7 +9575,7 @@ var buildToolHandlers = (serverOptions) => {
9769
9575
  } catch {
9770
9576
  token = config.accessKey;
9771
9577
  }
9772
- const core = await import("./dist-I4IPYCRC.js");
9578
+ const core = await import("./dist-TBAQ5KOK.js");
9773
9579
  const data = await core.getBillingConfig({
9774
9580
  baseUrl: config.baseUrl,
9775
9581
  authToken: token
@@ -9943,6 +9749,19 @@ var readMcpResource = async (uri, handlers, availableToolNames, toolset) => {
9943
9749
  const envelope = await handlers.get("capabilities_get")?.({});
9944
9750
  return { contents: [resourceText(uri, envelope?.data ?? {})] };
9945
9751
  }
9752
+ if (uri === "cloudeval://skills") {
9753
+ return {
9754
+ contents: [
9755
+ resourceText(
9756
+ uri,
9757
+ formatSuccessEnvelope({
9758
+ command: "skills list",
9759
+ data: await skillsResourceData()
9760
+ })
9761
+ )
9762
+ ]
9763
+ };
9764
+ }
9946
9765
  const toolName = uri === "cloudeval://projects" ? "projects_list" : uri === "cloudeval://billing/summary" ? "billing_summary" : uri === "cloudeval://recipes" ? "recipes_list" : "reports_list";
9947
9766
  const handler = handlers.get(toolName);
9948
9767
  if (!handler) {
@@ -10590,7 +10409,7 @@ var registerCapabilitiesCommand = (program2, deps) => {
10590
10409
  warnIfAccessKeyFromCliOption(options, command);
10591
10410
  let data = capabilities;
10592
10411
  if (options.live) {
10593
- const core = await import("./dist-I4IPYCRC.js");
10412
+ const core = await import("./dist-TBAQ5KOK.js");
10594
10413
  const baseUrl = await deps.resolveBaseUrl(options, command);
10595
10414
  const accessKey = options.accessKeyStdin ? await deps.readStdinValue() : options.accessKey;
10596
10415
  const token = await core.getAuthToken({ accessKey, baseUrl });
@@ -10622,6 +10441,8 @@ Mode-specific commands:
10622
10441
  cloudeval recipes list Discover reusable CloudEval workflows
10623
10442
  cloudeval recipes show <id> Show skill-style commands, safety, and outputs
10624
10443
  cloudeval recipes run <id> Run ask/agent recipes or print explicit commands for side-effecting recipes
10444
+ cloudeval skills list Discover portable CloudEval SKILL.md instructions
10445
+ cloudeval skills doctor Validate skill files and recipe references
10625
10446
 
10626
10447
  Progress:
10627
10448
  ask/agent show a live stderr loader and reasoning progress bar in interactive terminals, then write the final answer to stdout. In non-TTY logs this falls back to append-only stderr events. Use --progress none or --quiet to suppress progress, or --format ndjson --progress ndjson to stream progress on stdout.
@@ -10647,6 +10468,7 @@ Stable JSON envelope:
10647
10468
  Discovery:
10648
10469
  cloudeval capabilities --format json
10649
10470
  cloudeval recipes list
10471
+ cloudeval skills list
10650
10472
  cloudeval mcp serve
10651
10473
  cloudeval doctor --format json
10652
10474
  cloudeval config show --format json
@@ -10725,7 +10547,7 @@ var writeCredentialOutput = async (input) => {
10725
10547
  };
10726
10548
  var resolveCoreAuth = async (options, command, deps) => {
10727
10549
  const context = await resolveAuthContext(options, command, deps);
10728
- const core = await import("./dist-I4IPYCRC.js");
10550
+ const core = await import("./dist-TBAQ5KOK.js");
10729
10551
  return { ...context, core };
10730
10552
  };
10731
10553
  var parseCapabilities = (value) => value?.split(",").map((item) => item.trim()).filter(Boolean);
@@ -10833,8 +10655,8 @@ import { randomUUID as randomUUID4 } from "crypto";
10833
10655
  import { exec } from "child_process";
10834
10656
  import { randomUUID as randomUUID3 } from "crypto";
10835
10657
  import fs9 from "fs/promises";
10836
- import os3 from "os";
10837
- import path7 from "path";
10658
+ import os2 from "os";
10659
+ import path6 from "path";
10838
10660
  var normalizeHooks = (config, event) => {
10839
10661
  if (config.hooks?.enabled !== true) {
10840
10662
  return [];
@@ -10845,8 +10667,8 @@ var normalizeHooks = (config, event) => {
10845
10667
  ) : [];
10846
10668
  };
10847
10669
  var writeHookPayload = async (input, hook) => {
10848
- const filePath = path7.join(
10849
- os3.tmpdir(),
10670
+ const filePath = path6.join(
10671
+ os2.tmpdir(),
10850
10672
  `cloudeval-hook-${process.pid}-${randomUUID3()}.json`
10851
10673
  );
10852
10674
  await fs9.writeFile(
@@ -11042,7 +10864,7 @@ var registerAgentsCommand = (program2, deps) => {
11042
10864
  const agents = program2.command("agents").description("CloudEval Agent Profile utilities");
11043
10865
  addAgentOutputOptions(agents.command("list").description("List Agent Profiles"), deps).action(async (options, command) => {
11044
10866
  const baseUrl = await deps.resolveBaseUrl(options, command);
11045
- const core = await import("./dist-I4IPYCRC.js");
10867
+ const core = await import("./dist-TBAQ5KOK.js");
11046
10868
  core.assertSecureBaseUrl(baseUrl);
11047
10869
  const data = await listProfilesForDiscovery2(core, baseUrl);
11048
10870
  await writeProfiles({
@@ -11058,7 +10880,7 @@ var registerAgentsCommand = (program2, deps) => {
11058
10880
  deps
11059
10881
  ).action(async (profileId, options, command) => {
11060
10882
  const baseUrl = await deps.resolveBaseUrl(options, command);
11061
- const core = await import("./dist-I4IPYCRC.js");
10883
+ const core = await import("./dist-TBAQ5KOK.js");
11062
10884
  core.assertSecureBaseUrl(baseUrl);
11063
10885
  const data = await getProfileForDiscovery2(core, baseUrl, profileId);
11064
10886
  await writeProfiles({
@@ -11077,7 +10899,7 @@ var registerAgentsCommand = (program2, deps) => {
11077
10899
  const cliProfile = getActiveConfigProfile(command);
11078
10900
  const cliConfig = await loadCliConfig(cliProfile);
11079
10901
  const auth = await resolveAuthContext(options, command, deps);
11080
- const core = await import("./dist-I4IPYCRC.js");
10902
+ const core = await import("./dist-TBAQ5KOK.js");
11081
10903
  const profileResponse = await core.getAgentProfile({
11082
10904
  baseUrl: auth.baseUrl,
11083
10905
  authToken: auth.token,
@@ -11469,10 +11291,10 @@ var registerConfigCommand = (program2) => {
11469
11291
  const profile = resolveProfile(options, command);
11470
11292
  const current = await loadCliConfig(profile);
11471
11293
  const next = writeCliConfigValue(current, key, value);
11472
- const path11 = await saveCliConfig(next, profile);
11294
+ const path10 = await saveCliConfig(next, profile);
11473
11295
  await writeFormattedOutput({
11474
11296
  command: "config set",
11475
- data: { profile, path: path11, config: next },
11297
+ data: { profile, path: path10, config: next },
11476
11298
  format: options.format,
11477
11299
  output: options.output
11478
11300
  });
@@ -11483,10 +11305,10 @@ var registerConfigCommand = (program2) => {
11483
11305
  const profile = resolveProfile(options, command);
11484
11306
  const current = await loadCliConfig(profile);
11485
11307
  const next = unsetCliConfigValue(current, key);
11486
- const path11 = await saveCliConfig(next, profile);
11308
+ const path10 = await saveCliConfig(next, profile);
11487
11309
  await writeFormattedOutput({
11488
11310
  command: "config unset",
11489
- data: { profile, path: path11, config: next },
11311
+ data: { profile, path: path10, config: next },
11490
11312
  format: options.format,
11491
11313
  output: options.output
11492
11314
  });
@@ -11575,7 +11397,7 @@ var registerDiagnosticsCommands = (program2, deps) => {
11575
11397
  const baseUrl = await deps.resolveBaseUrl(options, command);
11576
11398
  const profile = getActiveConfigProfile(command);
11577
11399
  const config = await loadCliConfig(profile);
11578
- const core = await import("./dist-I4IPYCRC.js");
11400
+ const core = await import("./dist-TBAQ5KOK.js");
11579
11401
  const auth = await core.getAuthStatus(baseUrl, { validate: true });
11580
11402
  if (options.format === "text" || !options.format) {
11581
11403
  process.stdout.write(
@@ -11625,7 +11447,7 @@ var registerDiagnosticsCommands = (program2, deps) => {
11625
11447
  detail: getCliConfigPath(profile)
11626
11448
  });
11627
11449
  try {
11628
- const core = await import("./dist-I4IPYCRC.js");
11450
+ const core = await import("./dist-TBAQ5KOK.js");
11629
11451
  core.assertSecureBaseUrl(baseUrl);
11630
11452
  checks.push({
11631
11453
  id: "base-url-secure",
@@ -11725,7 +11547,7 @@ var resolveToken2 = async (options, deps, baseUrl, command) => {
11725
11547
  return options.accessKey;
11726
11548
  }
11727
11549
  try {
11728
- const core = await import("./dist-I4IPYCRC.js");
11550
+ const core = await import("./dist-TBAQ5KOK.js");
11729
11551
  return await core.getAuthToken({
11730
11552
  baseUrl
11731
11553
  });
@@ -11809,7 +11631,7 @@ var registerModelsCommand = (program2, deps) => {
11809
11631
  let source = "fallback";
11810
11632
  let modelList = fallbackModels;
11811
11633
  try {
11812
- const core = await import("./dist-I4IPYCRC.js");
11634
+ const core = await import("./dist-TBAQ5KOK.js");
11813
11635
  const response = await fetch(`${core.normalizeApiBase(baseUrl)}/models`, {
11814
11636
  headers: {
11815
11637
  Accept: "application/json",
@@ -11850,10 +11672,10 @@ var registerModelsCommand = (program2, deps) => {
11850
11672
  const profile = options.profile || getActiveConfigProfile(command);
11851
11673
  const config = await loadCliConfig(profile);
11852
11674
  const next = { ...config, model };
11853
- const path11 = await saveCliConfig(next, profile);
11675
+ const path10 = await saveCliConfig(next, profile);
11854
11676
  await writeFormattedOutput({
11855
11677
  command: "models default set",
11856
- data: { profile, path: path11, model },
11678
+ data: { profile, path: path10, model },
11857
11679
  format: options.format,
11858
11680
  output: options.output
11859
11681
  });
@@ -12069,12 +11891,12 @@ var registerSetupCommand = (program2, defaultBaseUrl = CLOUD_BASE_URL) => {
12069
11891
  rl.close();
12070
11892
  }
12071
11893
  }
12072
- const path11 = await saveCliConfig(next, profile);
11894
+ const path10 = await saveCliConfig(next, profile);
12073
11895
  await writeFormattedOutput({
12074
11896
  command: "setup",
12075
11897
  data: {
12076
11898
  profile,
12077
- path: path11,
11899
+ path: path10,
12078
11900
  config: next,
12079
11901
  nextSteps: [
12080
11902
  "Run `cloudeval auth status` to inspect authentication.",
@@ -12091,7 +11913,7 @@ var registerSetupCommand = (program2, defaultBaseUrl = CLOUD_BASE_URL) => {
12091
11913
  // src/updateCommand.ts
12092
11914
  import { spawn } from "child_process";
12093
11915
  import fs10 from "fs/promises";
12094
- import path8 from "path";
11916
+ import path7 from "path";
12095
11917
  import { createInterface as createInterface2 } from "readline/promises";
12096
11918
  var DEFAULT_LATEST_RELEASE_URL = "https://api.github.com/repos/ganakailabs/cloudeval-cli/releases/latest";
12097
11919
  var DEFAULT_INSTALLER_URL = "https://cli.cloudeval.ai/install.sh";
@@ -12256,7 +12078,7 @@ var runInstaller = async ({
12256
12078
  child.stdin.end(installerScript);
12257
12079
  });
12258
12080
  };
12259
- var getUpdateCachePath = () => path8.join(getCloudevalConfigDir(), UPDATE_CACHE_FILE);
12081
+ var getUpdateCachePath = () => path7.join(getCloudevalConfigDir(), UPDATE_CACHE_FILE);
12260
12082
  var readCache = async (cachePath) => {
12261
12083
  try {
12262
12084
  const parsed = JSON.parse(await fs10.readFile(cachePath, "utf8"));
@@ -12271,7 +12093,7 @@ var readCache = async (cachePath) => {
12271
12093
  return void 0;
12272
12094
  };
12273
12095
  var writeCache = async (cachePath, status) => {
12274
- await fs10.mkdir(path8.dirname(cachePath), { recursive: true, mode: 448 });
12096
+ await fs10.mkdir(path7.dirname(cachePath), { recursive: true, mode: 448 });
12275
12097
  await fs10.writeFile(
12276
12098
  cachePath,
12277
12099
  `${JSON.stringify(
@@ -12495,8 +12317,8 @@ var registerUpdateCommand = (program2) => {
12495
12317
 
12496
12318
  // src/uninstallCommand.ts
12497
12319
  import fs11 from "fs/promises";
12498
- import os4 from "os";
12499
- import path9 from "path";
12320
+ import os3 from "os";
12321
+ import path8 from "path";
12500
12322
  import { createInterface as createInterface3 } from "readline/promises";
12501
12323
  var pathExists = async (candidate) => {
12502
12324
  try {
@@ -12509,12 +12331,12 @@ var pathExists = async (candidate) => {
12509
12331
  throw error;
12510
12332
  }
12511
12333
  };
12512
- var installerBinDir = (home) => path9.join(home, ".local", "bin");
12334
+ var installerBinDir = (home) => path8.join(home, ".local", "bin");
12513
12335
  var completionPaths = (home) => [
12514
- path9.join(home, ".local", "share", "bash-completion", "completions", "cloudeval"),
12515
- path9.join(home, ".zsh", "completions", "_cloudeval"),
12516
- path9.join(home, ".config", "fish", "completions", "cloudeval.fish"),
12517
- path9.join(home, ".config", "powershell", "cloudeval-completion.ps1")
12336
+ path8.join(home, ".local", "share", "bash-completion", "completions", "cloudeval"),
12337
+ path8.join(home, ".zsh", "completions", "_cloudeval"),
12338
+ path8.join(home, ".config", "fish", "completions", "cloudeval.fish"),
12339
+ path8.join(home, ".config", "powershell", "cloudeval-completion.ps1")
12518
12340
  ];
12519
12341
  var installerArtifactTargets = (home, platform) => {
12520
12342
  const binDir = installerBinDir(home);
@@ -12522,37 +12344,37 @@ var installerArtifactTargets = (home, platform) => {
12522
12344
  const targets = [
12523
12345
  {
12524
12346
  label: "cloudeval binary",
12525
- path: path9.join(binDir, executableName),
12347
+ path: path8.join(binDir, executableName),
12526
12348
  kind: "file",
12527
12349
  status: "missing"
12528
12350
  },
12529
12351
  {
12530
12352
  label: "cloudeval binary",
12531
- path: path9.join(binDir, "cloudeval"),
12353
+ path: path8.join(binDir, "cloudeval"),
12532
12354
  kind: "file",
12533
12355
  status: "missing"
12534
12356
  },
12535
12357
  {
12536
12358
  label: "eva alias",
12537
- path: path9.join(binDir, "eva"),
12359
+ path: path8.join(binDir, "eva"),
12538
12360
  kind: "file",
12539
12361
  status: "missing"
12540
12362
  },
12541
12363
  {
12542
12364
  label: "cloud alias",
12543
- path: path9.join(binDir, "cloud"),
12365
+ path: path8.join(binDir, "cloud"),
12544
12366
  kind: "file",
12545
12367
  status: "missing"
12546
12368
  },
12547
12369
  {
12548
12370
  label: "Ink runtime asset",
12549
- path: path9.join(binDir, "yoga.wasm"),
12371
+ path: path8.join(binDir, "yoga.wasm"),
12550
12372
  kind: "file",
12551
12373
  status: "missing"
12552
12374
  },
12553
12375
  {
12554
12376
  label: "license notices",
12555
- path: path9.join(home, ".local", "share", "cloudeval", "licenses"),
12377
+ path: path8.join(home, ".local", "share", "cloudeval", "licenses"),
12556
12378
  kind: "directory",
12557
12379
  status: "missing"
12558
12380
  },
@@ -12568,11 +12390,11 @@ var installerArtifactTargets = (home, platform) => {
12568
12390
  );
12569
12391
  };
12570
12392
  var shellProfilePaths = (home) => [
12571
- path9.join(home, ".bashrc"),
12572
- path9.join(home, ".bash_profile"),
12573
- path9.join(home, ".zshrc"),
12574
- path9.join(home, ".profile"),
12575
- path9.join(home, ".config", "fish", "config.fish")
12393
+ path8.join(home, ".bashrc"),
12394
+ path8.join(home, ".bash_profile"),
12395
+ path8.join(home, ".zshrc"),
12396
+ path8.join(home, ".profile"),
12397
+ path8.join(home, ".config", "fish", "config.fish")
12576
12398
  ];
12577
12399
  var removeInstallerPathSnippet = (content, binDir) => {
12578
12400
  const exportLine = `export PATH="${binDir}:$PATH"`;
@@ -12652,7 +12474,7 @@ var confirmUninstall = async ({
12652
12474
  }
12653
12475
  };
12654
12476
  var handleUninstallCommand = async (options, deps = {}) => {
12655
- const home = deps.home ?? os4.homedir();
12477
+ const home = deps.home ?? os3.homedir();
12656
12478
  const platform = deps.platform ?? process.platform;
12657
12479
  const dryRun = Boolean(options.dryRun);
12658
12480
  const removeConfig = Boolean(options.removeConfig);
@@ -12682,7 +12504,7 @@ var handleUninstallCommand = async (options, deps = {}) => {
12682
12504
  }
12683
12505
  const configTarget = {
12684
12506
  label: "config",
12685
- path: path9.join(home, ".config", "cloudeval"),
12507
+ path: path8.join(home, ".config", "cloudeval"),
12686
12508
  kind: "directory",
12687
12509
  status: "kept"
12688
12510
  };
@@ -12887,21 +12709,21 @@ var assertNoLegacyApiKeyUsage = () => {
12887
12709
  };
12888
12710
  assertNoLegacyApiKeyUsage();
12889
12711
  var completionScriptPath = (shell) => {
12890
- const home = os5.homedir();
12712
+ const home = os4.homedir();
12891
12713
  switch (shell) {
12892
12714
  case "bash":
12893
- return path10.join(home, ".local", "share", "bash-completion", "completions", "cloudeval");
12715
+ return path9.join(home, ".local", "share", "bash-completion", "completions", "cloudeval");
12894
12716
  case "zsh":
12895
- return path10.join(home, ".zsh", "completions", "_cloudeval");
12717
+ return path9.join(home, ".zsh", "completions", "_cloudeval");
12896
12718
  case "fish":
12897
- return path10.join(home, ".config", "fish", "completions", "cloudeval.fish");
12719
+ return path9.join(home, ".config", "fish", "completions", "cloudeval.fish");
12898
12720
  case "powershell":
12899
- return path10.join(home, ".config", "powershell", "cloudeval-completion.ps1");
12721
+ return path9.join(home, ".config", "powershell", "cloudeval-completion.ps1");
12900
12722
  }
12901
12723
  };
12902
12724
  var ZSH_FPATH_MARKER = "CloudEval CLI completions";
12903
12725
  var ensureZshCompletionFpath = async () => {
12904
- const zshrc = path10.join(os5.homedir(), ".zshrc");
12726
+ const zshrc = path9.join(os4.homedir(), ".zshrc");
12905
12727
  let existing = "";
12906
12728
  try {
12907
12729
  existing = await fs12.readFile(zshrc, "utf8");
@@ -12919,7 +12741,7 @@ fpath=("$HOME/.zsh/completions" $fpath)
12919
12741
  };
12920
12742
  var installCompletionScript = async (shell, binaryName) => {
12921
12743
  const scriptPath = completionScriptPath(shell);
12922
- await fs12.mkdir(path10.dirname(scriptPath), { recursive: true });
12744
+ await fs12.mkdir(path9.dirname(scriptPath), { recursive: true });
12923
12745
  await fs12.writeFile(scriptPath, buildCompletionScript(shell, binaryName), "utf8");
12924
12746
  if (shell === "zsh") {
12925
12747
  await ensureZshCompletionFpath();
@@ -12934,7 +12756,7 @@ var uninstallCompletionScript = async (shell) => {
12934
12756
  var runInteractiveLoginOnboarding = async (baseUrl, token) => {
12935
12757
  const [{ render }, { Onboarding }] = await Promise.all([
12936
12758
  import("ink"),
12937
- import("./Onboarding-HVCYIPTL.js")
12759
+ import("./Onboarding-3NZCPBCO.js")
12938
12760
  ]);
12939
12761
  await new Promise((resolve) => {
12940
12762
  let app;
@@ -13168,7 +12990,7 @@ var resolveBaseUrl = async (options, command) => {
13168
12990
  });
13169
12991
  }
13170
12992
  try {
13171
- const { getAuthStatus } = await import("./dist-I4IPYCRC.js");
12993
+ const { getAuthStatus } = await import("./dist-TBAQ5KOK.js");
13172
12994
  const status = await getAuthStatus();
13173
12995
  const storedBaseUrl = status.baseUrl;
13174
12996
  if (storedBaseUrl && shouldUseStoredBaseUrl(storedBaseUrl)) {
@@ -13239,7 +13061,7 @@ program.command("login").description("Authenticate with Cloudeval").option(
13239
13061
  checkUserStatus,
13240
13062
  ensurePlaygroundProject,
13241
13063
  login
13242
- } = await import("./dist-I4IPYCRC.js");
13064
+ } = await import("./dist-TBAQ5KOK.js");
13243
13065
  assertSecureBaseUrl(options.baseUrl);
13244
13066
  const headlessEnvironment = isHeadlessEnvironment();
13245
13067
  const headlessLogin = options.headless || headlessEnvironment;
@@ -13303,7 +13125,7 @@ program.command("logout").description("Log out and clear stored authentication s
13303
13125
  DEFAULT_BASE_URL
13304
13126
  ).option("--all-devices", "Revoke sessions on all devices", false).action(async (options) => {
13305
13127
  try {
13306
- const { assertSecureBaseUrl, logout } = await import("./dist-I4IPYCRC.js");
13128
+ const { assertSecureBaseUrl, logout } = await import("./dist-TBAQ5KOK.js");
13307
13129
  assertSecureBaseUrl(options.baseUrl);
13308
13130
  const result = await logout({
13309
13131
  baseUrl: options.baseUrl,
@@ -13327,7 +13149,7 @@ authCommand.command("status").description("Show current authentication status").
13327
13149
  DEFAULT_BASE_URL
13328
13150
  ).option("--format <format>", "Output format: text, json, ndjson, markdown", "text").option("--show-sensitive-ids", "Show full account/session identifiers in command output", false).option("-v, --verbose", "Enable verbose logging and show full non-token identifiers", false).action(async (options, command) => {
13329
13151
  try {
13330
- const { assertSecureBaseUrl, getAuthStatus } = await import("./dist-I4IPYCRC.js");
13152
+ const { assertSecureBaseUrl, getAuthStatus } = await import("./dist-TBAQ5KOK.js");
13331
13153
  const effectiveBaseUrl = await resolveBaseUrl(options, command);
13332
13154
  assertSecureBaseUrl(effectiveBaseUrl);
13333
13155
  const status = await getAuthStatus(effectiveBaseUrl, { validate: true });
@@ -13379,6 +13201,7 @@ registerRecipesCommand(program, {
13379
13201
  readStdinValue,
13380
13202
  isHeadlessEnvironment
13381
13203
  });
13204
+ registerSkillsCommand(program);
13382
13205
  registerOpenCommand(program, {
13383
13206
  defaultBaseUrl: DEFAULT_BASE_URL,
13384
13207
  resolveBaseUrl
@@ -13508,14 +13331,15 @@ program.command("tui").description("Open the CloudEval Terminal UI").option(
13508
13331
  "--access-key <key>",
13509
13332
  "Access key for automation",
13510
13333
  process.env.CLOUDEVAL_ACCESS_KEY
13511
- ).option("--access-key-stdin", "Read access key from stdin (recommended for automation)", false).option("--model <name>", "Model name").option("--debug", "Log raw chunks", false).option("--health-check", "Enable health check (disabled by default)").option("--no-banner", "Disable ASCII banner").option("--animate", "Enable TUI animations").option("--no-anim", "Disable TUI animations").option("-v, --verbose", "Enable verbose logging", false).action(async (options, command) => {
13512
- const { assertSecureBaseUrl } = await import("./dist-I4IPYCRC.js");
13334
+ ).option("--access-key-stdin", "Read access key from stdin (recommended for automation)", false).option("--model <name>", "Model name").option("--debug", "Log raw chunks", false).option("--health-check", "Enable health check (disabled by default)").option("--no-banner", "Disable ASCII banner").option("--animate", "Enable TUI animations (default)").option("--no-anim", "Disable TUI animations").option("-v, --verbose", "Enable verbose logging", false).action(async (options, command) => {
13335
+ const { assertSecureBaseUrl } = await import("./dist-TBAQ5KOK.js");
13513
13336
  const [{ render }, { App }] = await Promise.all([
13514
13337
  import("ink"),
13515
- import("./App-PS547LKZ.js")
13338
+ import("./App-AQAMCM4E.js")
13516
13339
  ]);
13517
13340
  const baseUrl = await resolveBaseUrl(options, command);
13518
13341
  assertSecureBaseUrl(baseUrl);
13342
+ const selectedProfile = getActiveConfigProfile(command);
13519
13343
  const cliConfig = await resolveCliConfig(command);
13520
13344
  const initialMode = normalizeCliMode(options.mode ?? cliConfig.mode) ?? "ask";
13521
13345
  let accessKey = options.accessKey;
@@ -13540,6 +13364,7 @@ program.command("tui").description("Open the CloudEval Terminal UI").option(
13540
13364
  initialMode,
13541
13365
  initialTab: options.tab,
13542
13366
  initialProjectId: options.project ?? cliConfig.defaultProjectId,
13367
+ configProfile: selectedProfile,
13543
13368
  frontendUrl: options.frontendUrl ?? cliConfig.frontendUrl,
13544
13369
  debug: options.debug,
13545
13370
  disableBanner: options.banner === false,
@@ -13558,11 +13383,11 @@ program.command("chat").description("Start an interactive chat session").option(
13558
13383
  "--access-key <key>",
13559
13384
  "Access key for automation",
13560
13385
  process.env.CLOUDEVAL_ACCESS_KEY
13561
- ).option("--access-key-stdin", "Read access key from stdin (recommended for automation)", false).option("--conversation <id>", "Conversation/thread id to resume").option("--continue", "Resume the most recent local chat session", false).option("--resume <id-or-title>", "Resume a local chat session by thread id or title").option("--model <name>", "Model name").option("--mode <mode>", "Initial chat mode: ask, agent").option("--debug", "Log raw chunks", false).option("--health-check", "Enable health check (disabled by default)").option("--no-banner", "Disable ASCII banner").option("--animate", "Enable TUI animations").option("--no-anim", "Disable TUI animations").option("-v, --verbose", "Enable verbose logging", false).action(async (options, command) => {
13562
- const { assertSecureBaseUrl } = await import("./dist-I4IPYCRC.js");
13386
+ ).option("--access-key-stdin", "Read access key from stdin (recommended for automation)", false).option("--conversation <id>", "Conversation/thread id to resume").option("--continue", "Resume the most recent local chat session", false).option("--resume <id-or-title>", "Resume a local chat session by thread id or title").option("--model <name>", "Model name").option("--mode <mode>", "Initial chat mode: ask, agent").option("--debug", "Log raw chunks", false).option("--health-check", "Enable health check (disabled by default)").option("--no-banner", "Disable ASCII banner").option("--animate", "Enable TUI animations (default)").option("--no-anim", "Disable TUI animations").option("-v, --verbose", "Enable verbose logging", false).action(async (options, command) => {
13387
+ const { assertSecureBaseUrl } = await import("./dist-TBAQ5KOK.js");
13563
13388
  const [{ render }, { App }] = await Promise.all([
13564
13389
  import("ink"),
13565
- import("./App-PS547LKZ.js")
13390
+ import("./App-AQAMCM4E.js")
13566
13391
  ]);
13567
13392
  const baseUrl = await resolveBaseUrl(options, command);
13568
13393
  assertSecureBaseUrl(baseUrl);
@@ -13608,6 +13433,7 @@ program.command("chat").description("Start an interactive chat session").option(
13608
13433
  model: options.model ?? cliConfig.model,
13609
13434
  initialMode,
13610
13435
  initialProjectId: cliConfig.defaultProjectId,
13436
+ configProfile: selectedProfile,
13611
13437
  frontendUrl: cliConfig.frontendUrl,
13612
13438
  debug: options.debug,
13613
13439
  disableBanner: options.banner === false,
@@ -13630,7 +13456,7 @@ program.command("ask").alias("agent").description("Ask a single question or run
13630
13456
  const question = Array.isArray(questionParts) ? questionParts.join(" ") : String(questionParts);
13631
13457
  const commandName = command.parent?.args?.[0] === "agent" ? "agent" : "ask";
13632
13458
  const selectedMode = commandName === "agent" ? "agent" : "ask";
13633
- const { assertSecureBaseUrl } = await import("./dist-I4IPYCRC.js");
13459
+ const { assertSecureBaseUrl } = await import("./dist-TBAQ5KOK.js");
13634
13460
  const baseUrl = await resolveBaseUrl(options, command);
13635
13461
  assertSecureBaseUrl(baseUrl);
13636
13462
  const selectedProfile = getActiveConfigProfile(command);
@@ -13671,7 +13497,7 @@ program.command("ask").alias("agent").description("Ask a single question or run
13671
13497
  const fs13 = await import("fs");
13672
13498
  const fsPromises = await import("fs/promises");
13673
13499
  const { randomUUID: randomUUID5 } = await import("crypto");
13674
- const core = await import("./dist-I4IPYCRC.js");
13500
+ const core = await import("./dist-TBAQ5KOK.js");
13675
13501
  const {
13676
13502
  streamChat,
13677
13503
  reduceChunk,
@@ -13681,7 +13507,8 @@ program.command("ask").alias("agent").description("Ask a single question or run
13681
13507
  checkUserStatus,
13682
13508
  extractEmailFromToken,
13683
13509
  initialChatState,
13684
- normalizeApiBase: normalizeApiBase2
13510
+ normalizeApiBase: normalizeApiBase2,
13511
+ isExpiredDeviceTokenStreamError
13685
13512
  } = core;
13686
13513
  const progressWriter = createAskProgressWriter({
13687
13514
  mode: progressMode,
@@ -13728,7 +13555,7 @@ program.command("ask").alias("agent").description("Ask a single question or run
13728
13555
  console.error("Authentication required. Starting login process...\n");
13729
13556
  }
13730
13557
  try {
13731
- const { login } = await import("./dist-I4IPYCRC.js");
13558
+ const { login } = await import("./dist-TBAQ5KOK.js");
13732
13559
  verboseLog("Calling interactive login", { baseUrl });
13733
13560
  token = await login(baseUrl, {
13734
13561
  headless: isHeadlessEnvironment()
@@ -13941,6 +13768,7 @@ program.command("ask").alias("agent").description("Ask a single question or run
13941
13768
  try {
13942
13769
  let totalChunkCount = 0;
13943
13770
  let hitlResume;
13771
+ let retriedAfterAuthRefresh = false;
13944
13772
  while (true) {
13945
13773
  let pendingHitlRequest;
13946
13774
  progressWriter.write({
@@ -13950,89 +13778,109 @@ program.command("ask").alias("agent").description("Ask a single question or run
13950
13778
  threadId,
13951
13779
  projectId: project.id
13952
13780
  });
13953
- for await (const chunk of streamChat({
13954
- baseUrl,
13955
- authToken: token,
13956
- message: hitlResume ? "" : question,
13957
- threadId,
13958
- user: { id: project.user_id ?? authenticatedUserId ?? "cli-user", name: userName },
13959
- project,
13960
- settings: streamSettings,
13961
- debug: options.debug,
13962
- completeAfterResponse: true,
13963
- responseCompletionGraceMs: 5e3,
13964
- streamIdleTimeoutMs: ASK_STREAM_IDLE_TIMEOUT_MS2,
13965
- hitlResume
13966
- })) {
13967
- totalChunkCount++;
13968
- if (options.verbose && totalChunkCount % 10 === 0) {
13969
- verboseLog(`Received ${totalChunkCount} chunks`);
13970
- }
13971
- if (options.debug || options.verbose) {
13972
- verboseLog("Chunk received:", {
13973
- type: chunk.type,
13974
- node: chunk.node,
13975
- hasContent: !!chunk.content,
13976
- contentLength: chunk.content?.length || 0
13977
- });
13978
- }
13979
- chatState = reduceChunk(chatState, chunk);
13980
- writeChunkProgressEvent(progressEventFromChunk(chunk, { verbose: options.verbose }));
13981
- if (chunk.type === "hitl_request") {
13982
- pendingHitlRequest = chunk;
13983
- if (ndjsonOutput) {
13984
- writeAskDataEvent({
13985
- type: "hitl_request",
13986
- threadId,
13987
- checkpointId: chunk.checkpoint_id,
13988
- pendingIntentId: chunk.pending_intent_id,
13989
- questions: chunk.questions ?? [],
13990
- frontendUrl
13991
- });
13992
- }
13993
- break;
13994
- }
13995
- const latestMessage = [...chatState.messages].reverse().find((m) => m.role === "assistant");
13996
- if (ndjsonOutput && chunk.type === "responding" && chunk.content && (!chunk.node || STREAM_OUTPUT_NODES3.has(chunk.node))) {
13997
- writeAskDataEvent({
13998
- type: "chunk",
13999
- content: chunk.content,
14000
- node: chunk.node,
14001
- threadId
14002
- });
14003
- }
14004
- if (streamTextOutput && chunk.type === "responding" && chunk.content && (!chunk.node || STREAM_OUTPUT_NODES3.has(chunk.node))) {
14005
- if (latestMessage?.content) {
14006
- responseText = latestMessage.content;
14007
- const delta = responseText.slice(emittedTextLength);
14008
- if (delta) {
14009
- progressWriter.clear();
14010
- if (!responseText.slice(0, emittedTextLength).endsWith(delta)) {
14011
- outputStream.write(delta);
13781
+ while (true) {
13782
+ try {
13783
+ for await (const chunk of streamChat({
13784
+ baseUrl,
13785
+ authToken: token,
13786
+ message: hitlResume ? "" : question,
13787
+ threadId,
13788
+ user: { id: project.user_id ?? authenticatedUserId ?? "cli-user", name: userName },
13789
+ project,
13790
+ settings: streamSettings,
13791
+ debug: options.debug,
13792
+ completeAfterResponse: true,
13793
+ responseCompletionGraceMs: 5e3,
13794
+ streamIdleTimeoutMs: ASK_STREAM_IDLE_TIMEOUT_MS2,
13795
+ hitlResume
13796
+ })) {
13797
+ totalChunkCount++;
13798
+ if (options.verbose && totalChunkCount % 10 === 0) {
13799
+ verboseLog(`Received ${totalChunkCount} chunks`);
13800
+ }
13801
+ if (options.debug || options.verbose) {
13802
+ verboseLog("Chunk received:", {
13803
+ type: chunk.type,
13804
+ node: chunk.node,
13805
+ hasContent: !!chunk.content,
13806
+ contentLength: chunk.content?.length || 0
13807
+ });
13808
+ }
13809
+ chatState = reduceChunk(chatState, chunk);
13810
+ writeChunkProgressEvent(progressEventFromChunk(chunk, { verbose: options.verbose }));
13811
+ if (chunk.type === "hitl_request") {
13812
+ pendingHitlRequest = chunk;
13813
+ if (ndjsonOutput) {
13814
+ writeAskDataEvent({
13815
+ type: "hitl_request",
13816
+ threadId,
13817
+ checkpointId: chunk.checkpoint_id,
13818
+ pendingIntentId: chunk.pending_intent_id,
13819
+ questions: chunk.questions ?? [],
13820
+ frontendUrl
13821
+ });
14012
13822
  }
14013
- emittedTextLength = responseText.length;
13823
+ break;
14014
13824
  }
14015
- }
14016
- }
14017
- if (chunk.type === "error") {
14018
- const errorMsg = chunk.message || chunk.description || "Unknown error";
14019
- verboseLog("Error chunk received:", {
14020
- message: errorMsg,
14021
- node: chunk.node,
14022
- status: chunk.status,
14023
- stack: chunk.stacktrace
14024
- });
14025
- progressWriter.clear();
14026
- if (jsonOutput) {
14027
- responseText = `Error: ${errorMsg}`;
14028
- } else if (ndjsonOutput) {
14029
- writeAskDataEvent({ type: "error", error: { message: errorMsg }, threadId });
14030
- } else {
14031
- outputStream.write(`
13825
+ const latestMessage = [...chatState.messages].reverse().find((m) => m.role === "assistant");
13826
+ if (ndjsonOutput && chunk.type === "responding" && chunk.content && (!chunk.node || STREAM_OUTPUT_NODES3.has(chunk.node))) {
13827
+ writeAskDataEvent({
13828
+ type: "chunk",
13829
+ content: chunk.content,
13830
+ node: chunk.node,
13831
+ threadId
13832
+ });
13833
+ }
13834
+ if (streamTextOutput && chunk.type === "responding" && chunk.content && (!chunk.node || STREAM_OUTPUT_NODES3.has(chunk.node))) {
13835
+ if (latestMessage?.content) {
13836
+ responseText = latestMessage.content;
13837
+ const delta = responseText.slice(emittedTextLength);
13838
+ if (delta) {
13839
+ progressWriter.clear();
13840
+ if (!responseText.slice(0, emittedTextLength).endsWith(delta)) {
13841
+ outputStream.write(delta);
13842
+ }
13843
+ emittedTextLength = responseText.length;
13844
+ }
13845
+ }
13846
+ }
13847
+ if (chunk.type === "error") {
13848
+ const errorMsg = chunk.message || chunk.description || "Unknown error";
13849
+ verboseLog("Error chunk received:", {
13850
+ message: errorMsg,
13851
+ node: chunk.node,
13852
+ status: chunk.status,
13853
+ stack: chunk.stacktrace
13854
+ });
13855
+ progressWriter.clear();
13856
+ if (jsonOutput) {
13857
+ responseText = `Error: ${errorMsg}`;
13858
+ } else if (ndjsonOutput) {
13859
+ writeAskDataEvent({ type: "error", error: { message: errorMsg }, threadId });
13860
+ } else {
13861
+ outputStream.write(`
14032
13862
  Error: ${errorMsg}
14033
13863
  `);
13864
+ }
13865
+ break;
13866
+ }
14034
13867
  }
14035
13868
  break;
13869
+ } catch (error) {
13870
+ if (!providedAccessKey && !retriedAfterAuthRefresh && isExpiredDeviceTokenStreamError(error)) {
13871
+ retriedAfterAuthRefresh = true;
13872
+ verboseLog("Stored device token expired during stream; refreshing and retrying once");
13873
+ progressWriter.write({
13874
+ type: "auth",
13875
+ step: "auth",
13876
+ message: "Refreshing expired session",
13877
+ threadId,
13878
+ projectId: project.id
13879
+ });
13880
+ token = await getAuthToken({ baseUrl, forceRefresh: true });
13881
+ continue;
13882
+ }
13883
+ throw error;
14036
13884
  }
14037
13885
  }
14038
13886
  if (!pendingHitlRequest) {
@@ -14296,7 +14144,7 @@ Error: ${errorMsg}
14296
14144
  program.command("banner").description("Preview the startup banner and terminal capabilities").action(async () => {
14297
14145
  const { render } = await import("ink");
14298
14146
  const BannerPreview = React.lazy(async () => ({
14299
- default: (await import("./Banner-XZFAVVR2.js")).Banner
14147
+ default: (await import("./Banner-64DOS5YB.js")).Banner
14300
14148
  }));
14301
14149
  render(
14302
14150
  /* @__PURE__ */ jsx(React.Suspense, { fallback: null, children: /* @__PURE__ */ jsx(BannerPreview, { disable: false }) })