@cargo-ai/cli 1.0.70 → 1.0.72

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.
@@ -1 +1 @@
1
- {"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../../../src/commands/orchestration/action.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAcxC,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,GAAG,GAChB,IAAI,CAuRN"}
1
+ {"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../../../src/commands/orchestration/action.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAqBxC,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,GAAG,GAChB,IAAI,CAuSN"}
@@ -1,5 +1,6 @@
1
1
  import { parseBooleanOption } from "../../utils/booleanOption.js";
2
2
  import { applyRecordsLimit, ExitCodes, failWith, handleApiCall, outputJson, parseJson, parsePositiveInt, pollBatchUntilFinished, pollRunUntilFinished, } from "../runHandler.js";
3
+ import { buildCostRows, groupActionsByIntegration, renderCostTable, summarize, } from "./costTable.js";
3
4
  export function registerActionCommands(parent, getApi) {
4
5
  const action = parent
5
6
  .command("action")
@@ -17,6 +18,7 @@ graph needed. Creates a run and consumes credits.
17
18
 
18
19
  To debug a node inside an existing workflow instead, use
19
20
  'cargo-ai orchestration node'.`);
21
+ registerCostTableCommand(action, getApi);
20
22
  const listableKinds = ["connector", "native", "tool", "agent"];
21
23
  action
22
24
  .command("list")
@@ -24,7 +26,8 @@ To debug a node inside an existing workflow instead, use
24
26
  .argument("[query...]", "Keywords to match (space-separated, optional)")
25
27
  .option("--kind <kind>", "Restrict to one kind: connector, native, tool, or agent (enum)")
26
28
  .option("--integration-slug <slug>", "Restrict connector actions to this integration (string)")
27
- .option("--limit <n>", "Maximum results to return (integer, default: 20, max: 50)", "20")
29
+ .option("--category <category>", "Restrict connector actions to integrations in this category (e.g. enrichment, sales, freeform)")
30
+ .option("--limit <n>", "Maximum results to return (integer). Omit to return all results.")
28
31
  .addHelpText("after", `
29
32
  Each result includes an 'action' object you can pass to execute, execute-batch,
30
33
  or get-output-schema. All query terms must match (AND). Matches on the action
@@ -33,7 +36,9 @@ slug or name rank above integration matches, which rank above description.
33
36
  Examples:
34
37
  $ cargo-ai orchestration action list enrich
35
38
  $ cargo-ai orchestration action list --kind tool
36
- $ cargo-ai orchestration action list send --kind connector --integration-slug slack`)
39
+ $ cargo-ai orchestration action list send --kind connector --integration-slug slack
40
+ $ cargo-ai orchestration action list --category enrichment
41
+ $ cargo-ai orchestration action list --limit 10`)
37
42
  .action(async (queryTerms, opts) => {
38
43
  let kind;
39
44
  if (opts.kind !== undefined) {
@@ -42,12 +47,15 @@ Examples:
42
47
  failWith('--kind must be "connector", "native", "tool", or "agent".', { code: ExitCodes.InvalidUsage });
43
48
  }
44
49
  }
45
- const limit = parsePositiveInt(opts.limit, "--limit");
50
+ const limit = opts.limit === undefined
51
+ ? undefined
52
+ : parsePositiveInt(opts.limit, "--limit");
46
53
  const query = queryTerms.length === 0 ? undefined : queryTerms.join(" ");
47
54
  const result = await handleApiCall(() => getApi().orchestration.action.list({
48
55
  query,
49
56
  kind,
50
57
  integrationSlug: opts.integrationSlug,
58
+ category: opts.category,
51
59
  limit,
52
60
  }));
53
61
  outputJson(result);
@@ -152,3 +160,60 @@ Examples:
152
160
  outputJson(result);
153
161
  });
154
162
  }
163
+ function registerCostTableCommand(action, getApi) {
164
+ action
165
+ .command("cost-table")
166
+ .description("Price list of every credits-based action in the catalog, sorted by cost (free, reads the catalog)")
167
+ .option("--category <category>", "Filter by integration category (string)")
168
+ .option("--integration <slug>", "Filter by integration slug (string)")
169
+ .option("--raw", "Print the markdown document instead of JSON")
170
+ .addHelpText("after", `
171
+ Free, spends nothing: the catalog is where pricing actually lives, so this is
172
+ the answer to "what does <action> cost" and "what is the cheapest way to do
173
+ <stage>" without pricing anyone copied by hand.
174
+
175
+ Covers connector actions and Cargo's own native actions: sendEmail, modelAsk
176
+ and fileSearch are billed, so a connector-only price list is not a complete one.
177
+ Filtering by --category or --integration drops the natives.
178
+
179
+ Actions absent from the output cost no credits. Actions whose price depends on
180
+ config (an LLM model, a phone-or-email toggle) show a range, with the
181
+ per-config prices in the document's second half.
182
+
183
+ Examples:
184
+ $ cargo-ai orchestration action cost-table
185
+ $ cargo-ai orchestration action cost-table --category enrichment --raw
186
+ $ cargo-ai orchestration action cost-table --raw > credits-cost-table.md`)
187
+ .action(async (opts) => {
188
+ const api = getApi();
189
+ const isFiltered = opts.category !== undefined || opts.integration !== undefined;
190
+ // Fetch all connector and native actions. Omitting limit returns all.
191
+ // When filtering by category or integration, only connectors match —
192
+ // natives have no category and no integrationSlug.
193
+ const kinds = isFiltered
194
+ ? ["connector"]
195
+ : ["connector", "native"];
196
+ const allActions = [];
197
+ for (const kind of kinds) {
198
+ const result = await handleApiCall(() => api.orchestration.action.list({
199
+ kind,
200
+ category: opts.category,
201
+ integrationSlug: opts.integration,
202
+ }));
203
+ allActions.push(...result.results);
204
+ }
205
+ const catalog = groupActionsByIntegration(allActions);
206
+ const rows = buildCostRows(catalog);
207
+ const summary = summarize(catalog);
208
+ if (opts.raw === true) {
209
+ console.log(renderCostTable(rows, summary, {
210
+ generatedAt: new Date().toISOString().slice(0, 10),
211
+ filter: isFiltered
212
+ ? { category: opts.category, integration: opts.integration }
213
+ : undefined,
214
+ }));
215
+ return;
216
+ }
217
+ outputJson({ summary, rows });
218
+ });
219
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Renders every credits-based action as a pricing table.
3
+ *
4
+ * The catalog is the only place pricing lives, and it moves — providers get
5
+ * added, actions get repriced, tiers get split. Anything that copies those
6
+ * numbers by hand goes stale silently, so the table is generated rather than
7
+ * maintained.
8
+ *
9
+ * Sourced from `orchestration action list` (without a limit), which spans
10
+ * connector and native actions in a single call. Natives are a small set but
11
+ * not a free one — `sendEmail` is 0.1, `modelAsk` 0.5, `fileSearch` 0.02/1k
12
+ * tokens — and a table built from connectors alone silently omits them while
13
+ * claiming to be complete.
14
+ */
15
+ /**
16
+ * Shape returned by `orchestration action list` for each matching action.
17
+ */
18
+ export type ListActionMatch = {
19
+ name: string;
20
+ description?: string;
21
+ score: number;
22
+ action: {
23
+ kind: string;
24
+ integrationSlug?: string;
25
+ actionSlug?: string;
26
+ };
27
+ integrationName?: string;
28
+ category?: string;
29
+ credits?: CreditsCost[];
30
+ };
31
+ export type CreditsCost = {
32
+ type?: string;
33
+ cost?: number;
34
+ unit?: string;
35
+ unitsCount?: number;
36
+ fixedCost?: number;
37
+ config?: {
38
+ jsonSchema?: JsonSchema;
39
+ };
40
+ };
41
+ type JsonSchema = {
42
+ type?: string;
43
+ const?: unknown;
44
+ enum?: unknown[];
45
+ minItems?: number;
46
+ maxItems?: number;
47
+ properties?: Record<string, JsonSchema>;
48
+ required?: string[];
49
+ };
50
+ export type CatalogAction = {
51
+ name?: string;
52
+ description?: string;
53
+ credits?: {
54
+ costs?: CreditsCost[];
55
+ };
56
+ };
57
+ export type CatalogIntegration = {
58
+ slug: string;
59
+ name: string;
60
+ category?: string;
61
+ actions?: Record<string, CatalogAction>;
62
+ };
63
+ export type CostVariant = {
64
+ /** Config that selects this price, e.g. `model=gpt-5-nano`. */
65
+ when: string;
66
+ /** Rendered price, e.g. `0.4 + 0.006 / 1k token`. */
67
+ price: string;
68
+ };
69
+ export type CostRow = {
70
+ integrationSlug: string;
71
+ integrationName: string;
72
+ category: string | undefined;
73
+ actionSlug: string;
74
+ name: string | undefined;
75
+ description: string | undefined;
76
+ /** Cheapest and dearest per-unit price, in credits. */
77
+ minCost: number;
78
+ maxCost: number;
79
+ /**
80
+ * What one call at the cheapest config costs — per-unit price plus any flat
81
+ * component. The sort key, so a cheap-looking per-item rate with a base
82
+ * charge doesn't outrank a genuinely cheaper flat action.
83
+ */
84
+ sortCost: number;
85
+ /** Single price, or `min–max` when config picks between several. */
86
+ price: string;
87
+ /** Config fields that move the price; empty when there is only one. */
88
+ variesBy: string[];
89
+ variants: CostVariant[];
90
+ };
91
+ export type CostTableSummary = {
92
+ integrations: number;
93
+ integrationsWithActions: number;
94
+ actions: number;
95
+ creditsBasedActions: number;
96
+ freeActions: number;
97
+ };
98
+ /**
99
+ * Natives have no integration of their own. `native` cannot collide with a real
100
+ * integration slug, and it is what `kind` an action object needs to run one:
101
+ * `{"kind":"native","actionSlug":"sendEmail"}`.
102
+ */
103
+ export declare const NATIVE_PROVIDER_SLUG = "native";
104
+ export declare function asNativeCatalogIntegration(actions: Record<string, CatalogAction> | undefined): CatalogIntegration;
105
+ /**
106
+ * Groups the flat action list returned by `orchestration action list` into the
107
+ * integration-grouped shape that `buildCostRows` and `summarize` expect.
108
+ */
109
+ export declare function groupActionsByIntegration(actions: ListActionMatch[]): CatalogIntegration[];
110
+ export declare function buildCostRows(integrations: CatalogIntegration[]): CostRow[];
111
+ export declare function summarize(integrations: CatalogIntegration[]): CostTableSummary;
112
+ export type RenderCostTableOptions = {
113
+ generatedAt: string;
114
+ filter?: {
115
+ category?: string;
116
+ integration?: string;
117
+ };
118
+ };
119
+ export declare function renderCostTable(rows: CostRow[], summary: CostTableSummary, opts: RenderCostTableOptions): string;
120
+ export {};
121
+ //# sourceMappingURL=costTable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"costTable.d.ts","sourceRoot":"","sources":["../../../src/commands/orchestration/costTable.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,363 @@
1
+ /**
2
+ * Renders every credits-based action as a pricing table.
3
+ *
4
+ * The catalog is the only place pricing lives, and it moves — providers get
5
+ * added, actions get repriced, tiers get split. Anything that copies those
6
+ * numbers by hand goes stale silently, so the table is generated rather than
7
+ * maintained.
8
+ *
9
+ * Sourced from `orchestration action list` (without a limit), which spans
10
+ * connector and native actions in a single call. Natives are a small set but
11
+ * not a free one — `sendEmail` is 0.1, `modelAsk` 0.5, `fileSearch` 0.02/1k
12
+ * tokens — and a table built from connectors alone silently omits them while
13
+ * claiming to be complete.
14
+ */
15
+ const DESCRIPTION_MAX_LENGTH = 120;
16
+ /**
17
+ * Natives have no integration of their own. `native` cannot collide with a real
18
+ * integration slug, and it is what `kind` an action object needs to run one:
19
+ * `{"kind":"native","actionSlug":"sendEmail"}`.
20
+ */
21
+ export const NATIVE_PROVIDER_SLUG = "native";
22
+ export function asNativeCatalogIntegration(actions) {
23
+ return {
24
+ slug: NATIVE_PROVIDER_SLUG,
25
+ name: "Cargo native",
26
+ category: "platform",
27
+ actions: actions ?? {},
28
+ };
29
+ }
30
+ /**
31
+ * Groups the flat action list returned by `orchestration action list` into the
32
+ * integration-grouped shape that `buildCostRows` and `summarize` expect.
33
+ */
34
+ export function groupActionsByIntegration(actions) {
35
+ const integrationMap = new Map();
36
+ for (const match of actions) {
37
+ const actionKind = match.action.kind;
38
+ const actionSlug = match.action.actionSlug;
39
+ if (actionSlug === undefined) {
40
+ continue;
41
+ }
42
+ let slug;
43
+ let name;
44
+ let category;
45
+ if (actionKind === "native") {
46
+ slug = NATIVE_PROVIDER_SLUG;
47
+ name = "Cargo native";
48
+ category = "platform";
49
+ }
50
+ else if (actionKind === "connector") {
51
+ slug =
52
+ match.action.integrationSlug !== undefined
53
+ ? match.action.integrationSlug
54
+ : "";
55
+ name = match.integrationName !== undefined ? match.integrationName : slug;
56
+ category = match.category;
57
+ }
58
+ else {
59
+ continue;
60
+ }
61
+ let integration = integrationMap.get(slug);
62
+ if (integration === undefined) {
63
+ integration = { slug, name, category, actions: {} };
64
+ integrationMap.set(slug, integration);
65
+ }
66
+ if (integration.actions === undefined) {
67
+ integration.actions = {};
68
+ }
69
+ integration.actions[actionSlug] = {
70
+ name: match.name,
71
+ description: match.description,
72
+ credits: match.credits !== undefined ? { costs: match.credits } : undefined,
73
+ };
74
+ }
75
+ return Array.from(integrationMap.values());
76
+ }
77
+ export function buildCostRows(integrations) {
78
+ const rows = [];
79
+ for (const integration of integrations) {
80
+ for (const [actionSlug, action] of Object.entries(integration.actions ?? {})) {
81
+ const costs = action.credits?.costs;
82
+ const [first] = costs ?? [];
83
+ if (costs === undefined || first === undefined) {
84
+ continue;
85
+ }
86
+ const amounts = costs.map((cost) => cost.cost ?? 0);
87
+ const effective = costs.map((cost) => (cost.cost ?? 0) + (cost.fixedCost ?? 0));
88
+ const variants = costs.map((cost) => ({
89
+ when: describeSelector(cost.config?.jsonSchema),
90
+ price: renderPrice(cost),
91
+ }));
92
+ const minCost = Math.min(...amounts);
93
+ const maxCost = Math.max(...amounts);
94
+ rows.push({
95
+ integrationSlug: integration.slug,
96
+ integrationName: integration.name,
97
+ category: integration.category,
98
+ actionSlug,
99
+ name: action.name,
100
+ description: truncate(action.description),
101
+ minCost,
102
+ maxCost,
103
+ sortCost: Math.min(...effective),
104
+ price: costs.length === 1
105
+ ? renderPrice(first)
106
+ : renderRange(costs, minCost, maxCost),
107
+ variesBy: costs.length === 1 ? [] : selectorFields(costs),
108
+ variants: costs.length === 1 ? [] : variants,
109
+ });
110
+ }
111
+ }
112
+ return rows.sort((a, b) => a.sortCost - b.sortCost ||
113
+ a.maxCost - b.maxCost ||
114
+ a.integrationSlug.localeCompare(b.integrationSlug) ||
115
+ a.actionSlug.localeCompare(b.actionSlug));
116
+ }
117
+ export function summarize(integrations) {
118
+ let actions = 0;
119
+ let creditsBasedActions = 0;
120
+ let integrationsWithActions = 0;
121
+ for (const integration of integrations) {
122
+ const entries = Object.values(integration.actions ?? {});
123
+ // The native pseudo-integration is a carrier for platform actions, not a
124
+ // 139th integration. Its actions count; it does not.
125
+ const isNative = integration.slug === NATIVE_PROVIDER_SLUG;
126
+ if (entries.length > 0 && isNative === false) {
127
+ integrationsWithActions += 1;
128
+ }
129
+ actions += entries.length;
130
+ for (const action of entries) {
131
+ if ((action.credits?.costs ?? []).length > 0) {
132
+ creditsBasedActions += 1;
133
+ }
134
+ }
135
+ }
136
+ return {
137
+ integrations: integrations.filter((integration) => {
138
+ return integration.slug !== NATIVE_PROVIDER_SLUG;
139
+ }).length,
140
+ integrationsWithActions,
141
+ actions,
142
+ creditsBasedActions,
143
+ freeActions: actions - creditsBasedActions,
144
+ };
145
+ }
146
+ export function renderCostTable(rows, summary, opts) {
147
+ const lines = [];
148
+ const filter = opts.filter;
149
+ const isFiltered = filter !== undefined &&
150
+ (filter.category !== undefined || filter.integration !== undefined);
151
+ lines.push("# Credits cost table");
152
+ lines.push("");
153
+ if (isFiltered) {
154
+ const filterDesc = filter.category !== undefined && filter.integration !== undefined
155
+ ? `category \`${filter.category}\` and integration \`${filter.integration}\``
156
+ : filter.category !== undefined
157
+ ? `category \`${filter.category}\``
158
+ : `integration \`${filter.integration}\``;
159
+ lines.push(`Filtered by ${filterDesc}: ${String(summary.creditsBasedActions)} credits-based actions ` +
160
+ `out of ${String(summary.actions)} total across ${String(summary.integrationsWithActions)} of ` +
161
+ `${String(summary.integrations)} integrations, sorted by cost. ` +
162
+ `The other ${String(summary.freeActions)} actions in this subset cost no credits. ` +
163
+ "Run without filters for the full catalog, including Cargo's native actions.");
164
+ }
165
+ else {
166
+ lines.push(`Every credits-based action Cargo can run — ${String(summary.creditsBasedActions)} of the ` +
167
+ `${String(summary.actions)} actions exposed by ${String(summary.integrationsWithActions)} of the catalog's ` +
168
+ `${String(summary.integrations)} integrations, plus Cargo's own native actions — sorted by cost. The other ${String(summary.freeActions)} actions ` +
169
+ "(CRM writes, sequencer pushes, Slack sends, …) cost no credits; list them with " +
170
+ "`cargo-ai connection integration get <slug>`.");
171
+ }
172
+ lines.push("");
173
+ if (isFiltered === false) {
174
+ lines.push("Rows whose provider is `native` are Cargo's own platform actions, run as " +
175
+ '`{"kind":"native","actionSlug":"<action>"}` with no integration; every other row ' +
176
+ 'runs as `{"kind":"connector","integrationSlug":"<provider>","actionSlug":"<action>"}`.');
177
+ lines.push("");
178
+ }
179
+ lines.push("**Auto-generated. Do not edit by hand** — regenerate with " +
180
+ "`cargo-ai orchestration action cost-table --raw > cargo-gtm/references/credits-cost-table.md`.");
181
+ lines.push("");
182
+ lines.push(`Generated: ${opts.generatedAt}`);
183
+ lines.push("");
184
+ lines.push("| Cost | Provider | Category | Action | Description |");
185
+ lines.push("|---|---|---|---|---|");
186
+ for (const row of rows) {
187
+ lines.push(`| ${row.price} | \`${row.integrationSlug}\` | ${row.category ?? "—"} | ` +
188
+ `\`${row.actionSlug}\` | ${escapeCell(row.description ?? "")} |`);
189
+ }
190
+ const variable = rows.filter((row) => row.variants.length > 0);
191
+ if (variable.length > 0) {
192
+ lines.push("");
193
+ lines.push("## Actions whose price depends on config");
194
+ lines.push("");
195
+ lines.push("These bill differently depending on how the node is configured, so the range above is not a quote. " +
196
+ "Pick the row that matches the config you are about to run.");
197
+ lines.push("");
198
+ for (const row of variable) {
199
+ lines.push(`### \`${row.integrationSlug}.${row.actionSlug}\` — varies by ${row.variesBy
200
+ .map((field) => `\`${field}\``)
201
+ .join(", ")}`);
202
+ lines.push("");
203
+ lines.push("| Config | Cost |");
204
+ lines.push("|---|---|");
205
+ for (const variant of dedupeVariants(row.variants)) {
206
+ lines.push(`| ${escapeCell(variant.when)} | ${variant.price} |`);
207
+ }
208
+ lines.push("");
209
+ }
210
+ }
211
+ return `${lines.join("\n").trimEnd()}\n`;
212
+ }
213
+ /**
214
+ * One cost entry as a price expression.
215
+ *
216
+ * `fixed` bills per call, `unit` bills per unit consumed, and `package` bills
217
+ * per block of units on top of a flat call cost — an LLM step that charges a
218
+ * base plus a rate per 1k tokens is the common case.
219
+ */
220
+ function renderPrice(cost) {
221
+ const amount = formatAmount(cost.cost ?? 0);
222
+ if (cost.type === "unit") {
223
+ const metered = cost.unit === undefined ? amount : `${amount} / ${cost.unit}`;
224
+ // A per-item rate can sit on top of a flat charge per call — dropping that
225
+ // base is how a 0.175 + 0.025/item search gets quoted as 0.025.
226
+ return cost.fixedCost === undefined || cost.fixedCost === 0
227
+ ? metered
228
+ : `${formatAmount(cost.fixedCost)} + ${metered}`;
229
+ }
230
+ if (cost.type === "package") {
231
+ const block = formatUnits(cost.unitsCount, cost.unit);
232
+ const metered = block === undefined ? amount : `${amount} / ${block}`;
233
+ return cost.fixedCost === undefined || cost.fixedCost === 0
234
+ ? metered
235
+ : `${formatAmount(cost.fixedCost)} + ${metered}`;
236
+ }
237
+ return amount;
238
+ }
239
+ function renderRange(costs, minCost, maxCost) {
240
+ const range = minCost === maxCost
241
+ ? formatAmount(minCost)
242
+ : `${formatAmount(minCost)}–${formatAmount(maxCost)}`;
243
+ const first = costs[0];
244
+ const unit = first !== undefined ? first.unit : undefined;
245
+ const unitsCount = first !== undefined ? first.unitsCount : undefined;
246
+ const packageBlock = formatUnits(unitsCount, unit);
247
+ const metered = costs.some((cost) => cost.type === "package")
248
+ ? packageBlock !== undefined
249
+ ? `${range} / ${packageBlock}`
250
+ : range
251
+ : costs.every((cost) => cost.type === "unit") && unit !== undefined
252
+ ? `${range} / ${unit}`
253
+ : range;
254
+ const hasBase = costs.some((cost) => cost.fixedCost !== undefined && cost.fixedCost !== 0);
255
+ return hasBase ? `${metered} + base` : metered;
256
+ }
257
+ /**
258
+ * The config fields a cost entry keys off, as dotted paths — the answer to
259
+ * "what do I change to land on a different price".
260
+ */
261
+ function selectorPaths(schema, prefix = "") {
262
+ if (schema?.properties === undefined) {
263
+ return [];
264
+ }
265
+ const paths = [];
266
+ for (const [key, property] of Object.entries(schema.properties)) {
267
+ const path = prefix === "" ? key : `${prefix}.${key}`;
268
+ const nested = selectorPaths(property, path);
269
+ if (nested.length > 0) {
270
+ paths.push(...nested);
271
+ }
272
+ else {
273
+ paths.push(path);
274
+ }
275
+ }
276
+ return paths;
277
+ }
278
+ function selectorFields(costs) {
279
+ const fields = new Set();
280
+ for (const cost of costs) {
281
+ for (const path of selectorPaths(cost.config?.jsonSchema)) {
282
+ fields.add(path);
283
+ }
284
+ }
285
+ return [...fields];
286
+ }
287
+ /** A cost entry's selector as `field=value` pairs, e.g. `model=gpt-5-nano`. */
288
+ function describeSelector(schema) {
289
+ const pairs = selectorPairs(schema);
290
+ return pairs.length === 0 ? "any config" : pairs.join(", ");
291
+ }
292
+ function selectorPairs(schema, prefix = "") {
293
+ if (schema?.properties === undefined) {
294
+ return [];
295
+ }
296
+ const pairs = [];
297
+ for (const [key, property] of Object.entries(schema.properties)) {
298
+ const path = prefix === "" ? key : `${prefix}.${key}`;
299
+ const nested = selectorPairs(property, path);
300
+ if (nested.length > 0) {
301
+ pairs.push(...nested);
302
+ continue;
303
+ }
304
+ pairs.push(`${path}${describeConstraint(property)}`);
305
+ }
306
+ return pairs;
307
+ }
308
+ function describeConstraint(schema) {
309
+ if (schema.const !== undefined) {
310
+ return `=${String(schema.const)}`;
311
+ }
312
+ if (schema.enum !== undefined) {
313
+ return `∈ ${schema.enum.map((value) => String(value)).join("|")}`;
314
+ }
315
+ if (schema.maxItems === 0) {
316
+ return " empty";
317
+ }
318
+ if (schema.minItems !== undefined && schema.minItems > 0) {
319
+ return " set";
320
+ }
321
+ return " (any)";
322
+ }
323
+ /**
324
+ * Catalogs sometimes carry the same price twice under selectors that differ
325
+ * only in a field the reader cannot act on; collapsing them keeps the variant
326
+ * table readable.
327
+ */
328
+ function dedupeVariants(variants) {
329
+ const seen = new Set();
330
+ const kept = [];
331
+ for (const variant of variants) {
332
+ const key = `${variant.when}|${variant.price}`;
333
+ if (seen.has(key) === true) {
334
+ continue;
335
+ }
336
+ seen.add(key);
337
+ kept.push(variant);
338
+ }
339
+ return kept;
340
+ }
341
+ function formatUnits(unitsCount, unit) {
342
+ if (unit === undefined) {
343
+ return undefined;
344
+ }
345
+ if (unitsCount === undefined || unitsCount === 1) {
346
+ return unit;
347
+ }
348
+ return unitsCount % 1000 === 0
349
+ ? `${String(unitsCount / 1000)}k ${unit}`
350
+ : `${String(unitsCount)} ${unit}`;
351
+ }
352
+ function formatAmount(value) {
353
+ return String(Number(value.toFixed(6)));
354
+ }
355
+ function truncate(value) {
356
+ if (value === undefined || value.length <= DESCRIPTION_MAX_LENGTH) {
357
+ return value;
358
+ }
359
+ return `${value.slice(0, DESCRIPTION_MAX_LENGTH).trimEnd()}…`;
360
+ }
361
+ function escapeCell(value) {
362
+ return value.replace(/\r?\n/g, " ").replace(/\|/g, "\\|");
363
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/commands/orchestration/run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAgBxC,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,GAAG,IAAI,CA+rB5E"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/commands/orchestration/run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAgBxC,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,GAAG,IAAI,CAuqB5E"}
@@ -1,4 +1,3 @@
1
- import { declarePrompt } from "@cargo-ai/cdk/cli";
2
1
  import { parseBooleanOption } from "../../utils/booleanOption.js";
3
2
  import { loadCompiledWorkflow, resolveCompiledWorkflowFromState, } from "../../utils/loadCompiledWorkflow.js";
4
3
  import { ExitCodes, failWith, handleApiCall, info, outputJson, parseJson, pollRunUntilFinished, } from "../runHandler.js";
@@ -6,7 +5,7 @@ export function registerRunCommands(parent, getApi) {
6
5
  const run = parent
7
6
  .command("run")
8
7
  .description("Manage workflow runs (list, get, create, cancel, count, download)");
9
- const listRuns = run
8
+ run
10
9
  .command("list")
11
10
  .description("List runs")
12
11
  .option("--workflow-uuid <uuid>", "Workflow UUID (string). Omit to list the runs of every play and tool, which the API allows only alongside --created-after inside a 30-day window")
@@ -143,7 +142,7 @@ Examples:
143
142
  await handleApiCall(() => api.orchestration.run.cancel(payload));
144
143
  outputJson({ ok: true });
145
144
  });
146
- const countRuns = run
145
+ run
147
146
  .command("count")
148
147
  .description("Count runs matching the given filters")
149
148
  .option("--workflow-uuid <uuid>", "Workflow UUID (string). Omit to count the runs of every play and tool, which the API allows only alongside --created-after inside a 30-day window")
@@ -190,29 +189,6 @@ Examples:
190
189
  }));
191
190
  outputJson(result);
192
191
  });
193
- // Both of these take the option or go without it, and going without it is
194
- // only legal alongside --created-after. Commander cannot express that, so the
195
- // palette would offer a command that fails on the server's own message. The
196
- // choice is declared here instead, beside the option it chooses between.
197
- for (const command of [listRuns, countRuns]) {
198
- declarePrompt(command, {
199
- message: "which runs?",
200
- choices: [
201
- {
202
- label: "One workflow",
203
- hint: "a single play or tool",
204
- flag: "--workflow-uuid",
205
- value: { name: "workflow-uuid", hint: "550e8400-e29b-41d4-a716-…" },
206
- },
207
- {
208
- label: "Every play and tool",
209
- hint: "needs a start date; the API allows at most a 30-day window",
210
- flag: "--created-after",
211
- value: { name: "created-after", hint: "2026-08-01T00:00:00Z" },
212
- },
213
- ],
214
- });
215
- }
216
192
  run
217
193
  .command("download")
218
194
  .description("Download run results as JSON")
package/build/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from "node:module";
3
- import { BRAND, canRunPalette, fetchCreditsLeft, fetchWorkspaceName, formatWorkspaceSuffix, printSetupIssues, registerCommands as registerCdkCommands, runPalette, SessionExpiredError, } from "@cargo-ai/cdk/cli";
3
+ import { BRAND, canRunPalette, declareSources, fetchCreditsLeft, fetchWorkspaceName, formatWorkspaceSuffix, printSetupIssues, registerCommands as registerCdkCommands, runPalette, SessionExpiredError, } from "@cargo-ai/cdk/cli";
4
4
  import { Command } from "commander";
5
5
  import { AGENT_SKILLS_HINT } from "./agentSkills.js";
6
6
  import { createApi } from "./api.js";
@@ -112,6 +112,57 @@ const getApi = () => {
112
112
  }
113
113
  return createApi({ baseUrl, getAccessToken, workspaceUuid });
114
114
  };
115
+ // Where the palette finds answers for an option that wants an id. Keyed by the
116
+ // flag, because `--model-uuid` asks the same question in all 28 places it
117
+ // appears; these six entries cover the 84 uuid options this CLI declares.
118
+ //
119
+ // Nobody knows a uuid. Every one of these replaces a field you would otherwise
120
+ // have to leave the palette to fill.
121
+ declareSources({
122
+ // A workflow row carries a uuid, two timestamps and a toolUuid — nothing to
123
+ // choose between. The plays and tools that own those workflows carry a name
124
+ // and the workflowUuid, so they are what the menu is built from.
125
+ "--workflow-uuid": [
126
+ {
127
+ argv: ["orchestration", "play", "list"],
128
+ value: "workflowUuid",
129
+ label: "name",
130
+ kind: "play",
131
+ },
132
+ {
133
+ argv: ["orchestration", "tool", "list"],
134
+ value: "workflowUuid",
135
+ label: "name",
136
+ kind: "tool",
137
+ },
138
+ ],
139
+ "--model-uuid": [
140
+ {
141
+ argv: ["storage", "model", "list"],
142
+ value: "uuid",
143
+ label: "name",
144
+ hint: "slug",
145
+ },
146
+ ],
147
+ "--agent-uuid": [
148
+ { argv: ["ai", "agent", "list"], value: "uuid", label: "name" },
149
+ ],
150
+ "--connector-uuid": [
151
+ {
152
+ argv: ["connection", "connector", "list"],
153
+ value: "uuid",
154
+ label: "name",
155
+ hint: "integrationSlug",
156
+ },
157
+ ],
158
+ // Datasets have no name of their own; the slug is what identifies them.
159
+ "--dataset-uuid": [
160
+ { argv: ["storage", "dataset", "list"], value: "uuid", label: "slug" },
161
+ ],
162
+ "--segment-uuid": [
163
+ { argv: ["segmentation", "segment", "list"], value: "uuid", label: "name" },
164
+ ],
165
+ });
115
166
  registerAuthCommands(program, getApi);
116
167
  registerVersionCommand(program, version);
117
168
  registerDoctorCommand(program, version);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cargo-ai/cli",
3
- "version": "1.0.70",
3
+ "version": "1.0.72",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://getcargo.ai",
@@ -66,7 +66,7 @@
66
66
  "dependencies": {
67
67
  "@cargo-ai/api": "^1.0.69",
68
68
  "@cargo-ai/app-sdk": "^1.0.9",
69
- "@cargo-ai/cdk": "^1.0.56",
69
+ "@cargo-ai/cdk": "^1.0.57",
70
70
  "@cargo-ai/types": "^1.0.66",
71
71
  "@cargo-ai/worker-sdk": "^1.0.17",
72
72
  "@modelcontextprotocol/sdk": "1.29.0",