@cargo-ai/cli 1.0.55 → 1.0.57

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.
@@ -0,0 +1,528 @@
1
+ // Render a workflow node graph as a centred ASCII flow, for terminals.
2
+ //
3
+ // Mermaid is the right answer when the output lands somewhere that renders it —
4
+ // a PR, the docs, a published page. It is the wrong answer in a terminal, where
5
+ // it is just source code the reader has to compile in their head. This module
6
+ // draws the same graph as text a person can read where they are standing.
7
+ //
8
+ // Why not shell out to one of the mermaid-to-ASCII renderers: they re-parse the
9
+ // diagram and auto-route edges on a character grid, which collides as soon as a
10
+ // graph has crossings. Measured on the 29-node `Find email` waterfall, they
11
+ // produce rails drawn straight through node boxes, doubled edge labels
12
+ // (`├─noo►`), and junction characters overwriting label text (`Email┴valid?`) —
13
+ // a picture that reads as edges the workflow does not have. We never have to
14
+ // infer layout, because we hold the graph: this renderer lays a spine down the
15
+ // centre and hangs rails off it, so an edge it draws is an edge that exists.
16
+ //
17
+ // The shape it draws, and why:
18
+ //
19
+ // * A **spine** runs down the centre. One child continues it; the others peel
20
+ // off as rails.
21
+ // * A branch whose paths reconverge is a **detour**: the rail leaves at `├──┐`
22
+ // and rejoins at `├──┘`, and the spine keeps its identity across the split.
23
+ // * A branch whose paths never reconverge is a **fork**: `┌──┴──┐`, columns
24
+ // side by side, no spine survives.
25
+ //
26
+ // Which child holds the spine is the whole readability question, and it is not
27
+ // the same answer in both cases — see `chooseSpine`.
28
+ import { displayWidth, emptyBlock, frame, hstack, indent, padColumn, rail, spineBlock, teeAt, textBlock, vstack, } from "./asciiBlocks.js";
29
+ import { continuesOnFailure, findStart, graphWarnings, indexByUuid, isEnd, matchesNode, outgoingEdges, ROUTING_ACTIONS, } from "./nodeDiagram.js";
30
+ export { displayWidth };
31
+ /**
32
+ * Where the incoming line meets the split. A detour lands on the column that
33
+ * keeps the spine; a fork lands between the columns, snapped onto one when it
34
+ * falls beside it — `┴` and `┬` in adjacent cells read as a smudge rather than
35
+ * a junction.
36
+ */
37
+ function columnAnchor(spines, spineIndex, midpoint) {
38
+ if (spineIndex !== undefined) {
39
+ const at = spines[spineIndex];
40
+ return at === undefined ? 0 : at;
41
+ }
42
+ const nearest = spines.find((at) => Math.abs(at - midpoint) <= 1);
43
+ return nearest === undefined ? midpoint : nearest;
44
+ }
45
+ /** Whether a split column is still carrying the flow. */
46
+ function isColumnOpen(columns, index) {
47
+ if (index === undefined)
48
+ return false;
49
+ const column = columns[index];
50
+ return column === undefined ? false : column.open;
51
+ }
52
+ /** A node's outgoing edges, or none when it has no entry in the graph. */
53
+ function edgesFrom(graph, uuid) {
54
+ const edges = graph.out.get(uuid);
55
+ return edges === undefined ? [] : edges;
56
+ }
57
+ /** What to call a node when nothing better is available. */
58
+ function fallbackLabel(node) {
59
+ return node.slug === null || node.slug === undefined ? node.uuid : node.slug;
60
+ }
61
+ function buildGraph(nodes) {
62
+ const byUuid = indexByUuid(nodes);
63
+ const out = new Map();
64
+ for (const node of nodes)
65
+ out.set(node.uuid, outgoingEdges(node, byUuid));
66
+ return { byUuid, out, reach: new Map() };
67
+ }
68
+ /** Everything reachable from `uuid`, itself excluded. Cycle-safe, memoised. */
69
+ function reachable(graph, uuid) {
70
+ const cached = graph.reach.get(uuid);
71
+ if (cached !== undefined)
72
+ return cached;
73
+ const seen = new Set();
74
+ const stack = edgesFrom(graph, uuid).map((edge) => edge.to);
75
+ while (stack.length > 0) {
76
+ const next = stack.pop();
77
+ if (next === undefined || seen.has(next))
78
+ continue;
79
+ seen.add(next);
80
+ for (const edge of edgesFrom(graph, next))
81
+ stack.push(edge.to);
82
+ }
83
+ // A descendant pointing back adds the node to its own set. `chooseSpine`
84
+ // weighs arms by these sizes, so one extra entry is enough to hand the spine
85
+ // to an arm that should have stayed half of a fork.
86
+ seen.delete(uuid);
87
+ graph.reach.set(uuid, seen);
88
+ return seen;
89
+ }
90
+ /**
91
+ * Where a branch's paths come back together: the first node reachable from
92
+ * every child, in one child's own reach order so the choice is deterministic.
93
+ *
94
+ * A merge on an `end` node does not count. Every path in a workflow ends there,
95
+ * so treating it as a reconvergence would wrap the entire graph in one split
96
+ * and nest every later branch inside it.
97
+ */
98
+ function mergePoint(graph, children) {
99
+ const [first, ...rest] = children;
100
+ if (first === undefined || rest.length === 0)
101
+ return undefined;
102
+ // `reachable` hands back its memoised set, so the child itself is compared
103
+ // separately rather than added to it. Adding would poison the cache, and
104
+ // `chooseSpine` weighs branches by exactly those set sizes — an entry that
105
+ // grew by one flips the `best >= second * 2 + 1` test and picks the wrong
106
+ // arm to carry the spine.
107
+ const others = rest.map((child) => ({
108
+ child,
109
+ reaches: reachable(graph, child),
110
+ }));
111
+ const candidates = [first, ...reachable(graph, first)];
112
+ for (const candidate of candidates) {
113
+ if (isEnd(graph.byUuid.get(candidate)))
114
+ continue;
115
+ if (others.every(({ child, reaches }) => child === candidate || reaches.has(candidate))) {
116
+ return candidate;
117
+ }
118
+ }
119
+ return undefined;
120
+ }
121
+ /**
122
+ * Which child keeps the spine — the one judgement call in the layout, and the
123
+ * answer differs by branch kind:
124
+ *
125
+ * * **Detour** (the paths reconverge): the spine is the *straight-through*
126
+ * path, so source order wins and child 0 keeps it. A "not found → look it
127
+ * up another way → carry on" branch then reads as a short excursion off a
128
+ * continuous main line, which is what it is.
129
+ *
130
+ * * **Fork** (they never reconverge): source order is meaningless, because
131
+ * the two sides are not a main line and an excursion. The path with more
132
+ * work left in it is the main line. In a provider waterfall the "yes" exit
133
+ * is two nodes and the "no" carries the remaining twenty, so weighting by
134
+ * what is left keeps the waterfall vertical instead of stair-stepping it
135
+ * sideways once per stage.
136
+ *
137
+ * The fork rule only applies when one side is decisively bigger. Two ends of
138
+ * comparable weight are a genuine fork and get drawn symmetrically.
139
+ */
140
+ function chooseSpine(graph, edges, merged) {
141
+ if (merged)
142
+ return 0;
143
+ const weights = edges.map((edge) => reachable(graph, edge.to).size + 1);
144
+ let best = 0;
145
+ let second = 0;
146
+ let bestIndex = 0;
147
+ weights.forEach((weight, index) => {
148
+ if (weight > best) {
149
+ second = best;
150
+ best = weight;
151
+ bestIndex = index;
152
+ }
153
+ else if (weight > second) {
154
+ second = weight;
155
+ }
156
+ });
157
+ return best >= second * SPINE_DOMINANCE + 1 ? bestIndex : undefined;
158
+ }
159
+ // ---------------------------------------------------------------------------
160
+ // Layout
161
+ // ---------------------------------------------------------------------------
162
+ /** Past this the drawing wraps in an ordinary terminal and stops being one. */
163
+ const TERMINAL_WIDTH = 100;
164
+ /**
165
+ * How much bigger one arm of a non-reconverging branch must be before it is
166
+ * treated as the main line rather than one of two equal ends.
167
+ */
168
+ const SPINE_DOMINANCE = 2;
169
+ const GAP = 5;
170
+ /**
171
+ * `enrichPerson` → `Enrich person`. Only ever a fallback: the catalog's own name
172
+ * is better, and this is what a graph rendered without one falls back to.
173
+ * Splitting on case is safe for the slugs the platform mints; it deliberately
174
+ * does not touch integration slugs, where guessing turns `apolloio` into
175
+ * something that is neither the slug nor the product.
176
+ */
177
+ function prettyAction(slug) {
178
+ const words = slug
179
+ .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
180
+ .replace(/[_-]+/g, " ")
181
+ .trim()
182
+ .toLowerCase();
183
+ const first = words[0];
184
+ return first === undefined ? slug : first.toUpperCase() + words.slice(1);
185
+ }
186
+ function marks(node, context) {
187
+ let suffix = "";
188
+ if (continuesOnFailure(node))
189
+ suffix += " ↷";
190
+ if (matchesNode(node, context.paid))
191
+ suffix += " 💳";
192
+ if (matchesNode(node, context.highlight))
193
+ suffix += " ←";
194
+ return suffix;
195
+ }
196
+ /**
197
+ * The two label lines: what system the step runs on, then what it does. That
198
+ * ordering is deliberate — scanning a flow, the reader wants the provider first
199
+ * ("Apollo", "Slack") and the specifics second.
200
+ */
201
+ function nodeLines(node, context) {
202
+ const suffix = marks(node, context);
203
+ const action = node.actionSlug === null || node.actionSlug === undefined
204
+ ? ""
205
+ : node.actionSlug;
206
+ if (node.kind === "native" && (action === "start" || action === "end")) {
207
+ return [`${fallbackLabel(node)}${suffix}`];
208
+ }
209
+ const name = node.name === null || node.name === undefined
210
+ ? undefined
211
+ : node.name.trim();
212
+ const book = context.names;
213
+ // The action's own name, as the platform publishes it. Falling back to the
214
+ // slug split into words keeps an offline drawing readable without this file
215
+ // inventing a name that disagrees with the product.
216
+ const published = book === undefined || book.native === undefined
217
+ ? undefined
218
+ : book.native[action];
219
+ const nativeName = published === undefined ? prettyAction(action) : published;
220
+ // A routing step's second line would read "Branch" under every one of them,
221
+ // so it gets one line. The labelled rails leaving it already say it routes.
222
+ if (node.kind === "native" && ROUTING_ACTIONS.has(action)) {
223
+ const headline = name !== undefined && name.length > 0 ? name : nativeName;
224
+ return [`${headline}${suffix}`];
225
+ }
226
+ if (node.kind === "connector") {
227
+ const slug = node.integrationSlug === null || node.integrationSlug === undefined
228
+ ? "connector"
229
+ : node.integrationSlug;
230
+ const named = book === undefined || book.integrations === undefined
231
+ ? undefined
232
+ : book.integrations[slug];
233
+ const provider = named === undefined ? slug : named;
234
+ const perAction = book === undefined || book.actions === undefined
235
+ ? undefined
236
+ : book.actions[slug];
237
+ const actionName = perAction === undefined ? undefined : perAction[action];
238
+ const what = name !== undefined && name.length > 0
239
+ ? name
240
+ : actionName === undefined
241
+ ? prettyAction(action)
242
+ : actionName;
243
+ return provider === what
244
+ ? [`${provider}${suffix}`]
245
+ : [`${provider}${suffix}`, what];
246
+ }
247
+ const provider = node.kind === "tool"
248
+ ? "Tool"
249
+ : node.kind === "agent"
250
+ ? "Agent"
251
+ : nativeName;
252
+ const headline = name !== undefined && name.length > 0 ? name : node.slug;
253
+ if (headline === undefined ||
254
+ headline === null ||
255
+ headline === provider ||
256
+ headline === action) {
257
+ return [`${provider}${suffix}`];
258
+ }
259
+ return [`${provider}${suffix}`, headline];
260
+ }
261
+ function referenceBlock(node, context) {
262
+ // The step's most specific line — for a two-line step that is what it does,
263
+ // not the product it runs on, since the product repeats across steps.
264
+ const lines = nodeLines(node, context);
265
+ const specific = lines[lines.length - 1];
266
+ const label = specific ?? fallbackLabel(node);
267
+ return textBlock([`↑ ${label}`]);
268
+ }
269
+ /**
270
+ * A group's own graph, drawn inside a box rather than left to the reader's
271
+ * imagination. The steps live in `config._nodes` and form a complete graph of
272
+ * their own — start node included — so the same walk draws them one level down.
273
+ * Marks and names carry in; `drawn` does not, because an inner step repeating an
274
+ * outer slug is a different step.
275
+ */
276
+ function subGraph(node, context) {
277
+ const config = node.config;
278
+ const nested = config === null || config === undefined ? undefined : config["_nodes"];
279
+ // No `_nodes` key at all means the graph came from a source that does not
280
+ // carry loop bodies; an empty one means the loop was left empty, which is a
281
+ // defect the drawing should not hide behind a tidy box.
282
+ if (!Array.isArray(nested))
283
+ return undefined;
284
+ const label = fallbackLabel(node);
285
+ if (nested.length === 0) {
286
+ context.notes.push(`${label}: the loop has no steps`);
287
+ return undefined;
288
+ }
289
+ const nodes = nested;
290
+ const start = findStart(nodes);
291
+ if (start === undefined)
292
+ return undefined;
293
+ const graph = buildGraph(nodes);
294
+ const inner = { ...context, graph, drawn: new Set() };
295
+ const { block } = layout(start.uuid, new Set(), inner);
296
+ for (const warning of graphWarnings(nodes, graph.byUuid, inner.drawn)) {
297
+ context.notes.push(`${label}: ${warning}`);
298
+ }
299
+ return frame(block, "per item");
300
+ }
301
+ /**
302
+ * Draw the flow rooted at `uuid`, stopping when it reaches anything in `stop`.
303
+ * The returned block's spine is live at the bottom only when `open` is true —
304
+ * a caller merging rails back together needs to know whether there is anything
305
+ * left to merge.
306
+ */
307
+ function layout(uuid, stop, context) {
308
+ if (stop.has(uuid))
309
+ return { block: emptyBlock(), open: true };
310
+ const node = context.graph.byUuid.get(uuid);
311
+ if (node === undefined)
312
+ return { block: emptyBlock(), open: false };
313
+ // An `end` is allowed to appear more than once: five paths finishing at `end`
314
+ // read better as five `end`s than as five pointers to one. Every other node
315
+ // is referenced on a second arrival, leaf or not — drawing a named step like
316
+ // "Send to #best-leads" twice reads as two separate sends.
317
+ const edges = edgesFrom(context.graph, uuid);
318
+ if (context.drawn.has(uuid) && !isEnd(node)) {
319
+ return { block: referenceBlock(node, context), open: false };
320
+ }
321
+ context.drawn.add(uuid);
322
+ const nested = subGraph(node, context);
323
+ const head = nested === undefined
324
+ ? textBlock(nodeLines(node, context))
325
+ : vstack([textBlock(nodeLines(node, context)), spineBlock(), nested]);
326
+ if (edges.length === 0)
327
+ return { block: head, open: false };
328
+ if (edges.length === 1) {
329
+ const edge = edges[0];
330
+ if (edge === undefined)
331
+ return { block: head, open: false };
332
+ const tail = layout(edge.to, stop, context);
333
+ // A lone edge usually has no label and draws as a bare `│`. When it does
334
+ // carry one — a branch whose other arm is dangling, or a step whose only
335
+ // outgoing edge is its fallback — the label goes above the stem, the way
336
+ // every multi-edge column draws it, so it reads as an edge label rather
337
+ // than as the name of the step below it.
338
+ const stem = spineBlock(edge.dashed === true ? "┆" : "│");
339
+ const connector = edge.label === undefined ? stem : vstack([textBlock([edge.label]), stem]);
340
+ if (tail.block.lines.length === 0) {
341
+ return { block: vstack([head, connector]), open: true };
342
+ }
343
+ return {
344
+ block: vstack([head, connector, tail.block]),
345
+ open: tail.open,
346
+ };
347
+ }
348
+ return branch(head, edges, stop, context);
349
+ }
350
+ function branch(head, edges, stop, context) {
351
+ // An elided routing node contributes no rows, and the step above it already
352
+ // drew the `│` leading in — adding another leaves a two-row gap above every
353
+ // split in a graph whose branches are unnamed.
354
+ const lead = head.lines.length === 0 ? emptyBlock() : spineBlock();
355
+ const merge = mergePoint(context.graph, edges.map((edge) => edge.to));
356
+ const spineIndex = chooseSpine(context.graph, edges, merge !== undefined);
357
+ const innerStop = new Set(stop);
358
+ if (merge !== undefined)
359
+ innerStop.add(merge);
360
+ // Columns are label-over-body so `hstack` sizes each one around whichever is
361
+ // wider, and an edge label can never collide with the node beneath it.
362
+ const columns = edges.map((edge) => {
363
+ const drawn = layout(edge.to, innerStop, context);
364
+ const char = edge.dashed === true ? "┆" : "│";
365
+ return {
366
+ block: vstack([
367
+ textBlock([edge.label === undefined ? char : edge.label]),
368
+ spineBlock(char),
369
+ drawn.block,
370
+ ]),
371
+ open: drawn.open,
372
+ char,
373
+ };
374
+ });
375
+ const height = Math.max(...columns.map((column) => column.block.lines.length));
376
+ const padded = columns.map((column) => padColumn(column.block,
377
+ // A column still carrying the flow is drawn to the bottom of the split,
378
+ // so its spine is unbroken all the way to whatever joins it below. One
379
+ // that has ended stops where it ended — the difference is how the reader
380
+ // tells a path that continues from a path that terminates.
381
+ column.open ? height : column.block.lines.length, column.char));
382
+ // The spine column is drawn leftmost, whichever child holds it, so rails
383
+ // always peel off to the right. Ordering columns by edge index instead pushes
384
+ // the spine one indent further right at every nesting level — five stages of
385
+ // a waterfall then stair-step across 80 columns rather than staying in one
386
+ // vertical line. Labels ride their own rail, so nothing is mislabelled by the
387
+ // reordering; only the left-to-right order of the arms changes.
388
+ const order = spineIndex === undefined
389
+ ? padded.map((_, index) => index)
390
+ : [
391
+ spineIndex,
392
+ ...padded
393
+ .map((_, index) => index)
394
+ .filter((index) => index !== spineIndex),
395
+ ];
396
+ const laid = hstack(order.map((index) => {
397
+ const column = padded[index];
398
+ return column === undefined ? emptyBlock() : column;
399
+ }), GAP);
400
+ const body = laid.block;
401
+ const width = body.width;
402
+ const spines = new Array(padded.length).fill(0);
403
+ order.forEach((original, position) => {
404
+ const at = laid.spines[position];
405
+ spines[original] = at === undefined ? 0 : at;
406
+ });
407
+ const isDetour = spineIndex !== undefined;
408
+ const first = spines[0];
409
+ const last = spines[spines.length - 1];
410
+ const midpoint = Math.round(((first === undefined ? 0 : first) + (last === undefined ? 0 : last)) / 2);
411
+ // Snap the incoming line onto a column when it lands beside one: `┴` and `┬`
412
+ // printed in adjacent cells read as a smudge, not a junction.
413
+ const anchor = columnAnchor(spines, spineIndex, midpoint);
414
+ const others = spines.filter((_, index) => index !== spineIndex);
415
+ // A detour keeps its spine and hangs the rails off it; a fork has no spine to
416
+ // keep, so the incoming line lands between the columns instead.
417
+ const splitRail = isDetour
418
+ ? rail(width, spines.map((at, index) => ({
419
+ at,
420
+ char: index === spineIndex
421
+ ? teeAt(anchor, others, true)
422
+ : at > anchor
423
+ ? "┐"
424
+ : "┌",
425
+ })))
426
+ : rail(width, [
427
+ ...spines.map((at, index) => ({
428
+ at,
429
+ char: at === anchor
430
+ ? "┼"
431
+ : index === 0
432
+ ? "┌"
433
+ : index === spines.length - 1
434
+ ? "┐"
435
+ : "┬",
436
+ })),
437
+ ...(spines.includes(anchor) ? [] : [{ at: anchor, char: "┴" }]),
438
+ ]);
439
+ const top = vstack([head, lead]);
440
+ const aligned = indent({ lines: [splitRail, ...body.lines], width, spine: anchor }, Math.max(0, top.spine - anchor));
441
+ const whole = vstack([top, aligned]);
442
+ const mergeAt = aligned.spine;
443
+ // Every column still carrying the flow is joined back to one spine, whether
444
+ // this branch owns the merge point or an ancestor does. A column left hanging
445
+ // reads as a path that stops, which is a different claim about the workflow.
446
+ const shift = aligned.width - width;
447
+ const arms = spines
448
+ .map((at, index) => ({
449
+ at: at + shift,
450
+ open: isColumnOpen(columns, index),
451
+ }))
452
+ .filter(({ at, open }) => open && at !== mergeAt)
453
+ .map(({ at }) => at);
454
+ const joined = arms.length === 0
455
+ ? []
456
+ : [
457
+ {
458
+ lines: [
459
+ rail(aligned.width, [
460
+ { at: mergeAt, char: teeAt(mergeAt, arms, false) },
461
+ ...arms.map((at) => ({
462
+ at,
463
+ char: at > mergeAt ? "┘" : "└",
464
+ })),
465
+ ]),
466
+ ],
467
+ width: aligned.width,
468
+ spine: mergeAt,
469
+ },
470
+ ];
471
+ if (merge === undefined) {
472
+ // For a detour, the spine column's status determines the flow. For a fork,
473
+ // any open column means the flow continues — including the column at the
474
+ // anchor, which `arms` filters out because it needs no rail to reach it.
475
+ const spineOpen = isDetour
476
+ ? isColumnOpen(columns, spineIndex)
477
+ : columns.some((column) => column.open);
478
+ return { block: vstack([whole, ...joined]), open: spineOpen };
479
+ }
480
+ const after = layout(merge, stop, context);
481
+ return {
482
+ block: vstack([whole, ...joined, spineBlock(), after.block]),
483
+ open: after.open,
484
+ };
485
+ }
486
+ // ---------------------------------------------------------------------------
487
+ // Entry point
488
+ // ---------------------------------------------------------------------------
489
+ export function renderNodeAscii(nodes, options = {}) {
490
+ const graph = buildGraph(nodes);
491
+ const warnings = [];
492
+ const start = findStart(nodes);
493
+ if (start === undefined) {
494
+ return { diagram: "", format: "ascii", warnings: ["no nodes to draw"] };
495
+ }
496
+ const context = {
497
+ graph,
498
+ names: options.names,
499
+ paid: options.paid === undefined ? [] : options.paid,
500
+ highlight: options.highlight === undefined ? [] : options.highlight,
501
+ drawn: new Set(),
502
+ notes: warnings,
503
+ };
504
+ const { block } = layout(start.uuid, new Set(), context);
505
+ warnings.push(...graphWarnings(nodes, graph.byUuid, context.drawn));
506
+ // Rails stack rightward, so a graph that branches at every step of a long
507
+ // chain gets wide no matter how the columns are packed — each rail has to
508
+ // clear the one belonging to the step below it. Say so rather than letting it
509
+ // wrap in the reader's terminal and look like a rendering bug.
510
+ const widest = Math.max(0, ...block.lines.map((line) => displayWidth(line)));
511
+ if (widest > TERMINAL_WIDTH) {
512
+ warnings.push(`the drawing is ${String(widest)} columns wide and will wrap in most terminals — try --format mermaid, or diagram a sub-graph`);
513
+ }
514
+ const body = block.lines.map((line) => line.replace(/\s+$/, "")).join("\n");
515
+ const title = options.title === undefined ? undefined : options.title.trim();
516
+ if (title === undefined || title.length === 0) {
517
+ return { diagram: body, format: "ascii", warnings };
518
+ }
519
+ // Centre the title on the spine, not on the drawing's bounding box — the
520
+ // spine is what the eye follows down, and a wide rail off to one side would
521
+ // otherwise drag the title away from it.
522
+ const left = Math.max(0, block.spine - Math.floor(displayWidth(title) / 2));
523
+ return {
524
+ diagram: `${" ".repeat(left)}${title}\n\n${body}`,
525
+ format: "ascii",
526
+ warnings,
527
+ };
528
+ }
@@ -1,2 +1,2 @@
1
- export { clearCredentials, type StoredCredentials as Credentials, getConfigDir, getCredentialsPath, loadCredentials, type RefreshableSession, saveCredentials, } from "@cargo-ai/cdk/cli";
1
+ export { clearCredentials, type StoredCredentials as Credentials, CredentialsNotClearedError, getConfigDir, getCredentialsPath, loadCredentials, type RefreshableSession, saveCredentials, } from "@cargo-ai/cdk/cli";
2
2
  //# sourceMappingURL=credentials.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,gBAAgB,EAChB,KAAK,iBAAiB,IAAI,WAAW,EACrC,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,KAAK,kBAAkB,EACvB,eAAe,GAChB,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,gBAAgB,EAChB,KAAK,iBAAiB,IAAI,WAAW,EACrC,0BAA0B,EAC1B,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,KAAK,kBAAkB,EACvB,eAAe,GAChB,MAAM,mBAAmB,CAAC"}
@@ -1,4 +1,4 @@
1
1
  // The credentials file is shared with `cargo-cdk`, so its format and access
2
2
  // live in @cargo-ai/cdk/cli — one login serves both binaries and neither can
3
3
  // drift from the other's idea of what is on disk.
4
- export { clearCredentials, getConfigDir, getCredentialsPath, loadCredentials, saveCredentials, } from "@cargo-ai/cdk/cli";
4
+ export { clearCredentials, CredentialsNotClearedError, getConfigDir, getCredentialsPath, loadCredentials, saveCredentials, } from "@cargo-ai/cdk/cli";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cargo-ai/cli",
3
- "version": "1.0.55",
3
+ "version": "1.0.57",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://getcargo.ai",
@@ -61,7 +61,7 @@
61
61
  "dependencies": {
62
62
  "@cargo-ai/api": "^1.0.60",
63
63
  "@cargo-ai/app-sdk": "^1.0.6",
64
- "@cargo-ai/cdk": "^1.0.45",
64
+ "@cargo-ai/cdk": "^1.0.46",
65
65
  "@cargo-ai/types": "^1.0.57",
66
66
  "@cargo-ai/worker-sdk": "^1.0.12",
67
67
  "@modelcontextprotocol/sdk": "1.29.0",