@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.
@@ -13,24 +13,117 @@
13
13
  // * `childrenUuids` order carries the routing semantics (index 0 of a
14
14
  // `branch` is the matched path), so edge labels are table-driven rather
15
15
  // than inferred from node names.
16
- const ROUTING_ACTIONS = [
16
+ /** Actions that route rather than do work. Shared, so adding one to the
17
+ * platform does not have to be mirrored in each renderer. */
18
+ export const ROUTING_ACTIONS = new Set([
17
19
  "branch",
18
20
  "filter",
19
21
  "switch",
20
22
  "split",
21
23
  "balance",
22
24
  "humanReview",
23
- ];
25
+ ]);
24
26
  const CODE_ACTIONS = ["python", "script"];
25
27
  // ---------------------------------------------------------------------------
28
+ // Graph basics, shared by every renderer
29
+ // ---------------------------------------------------------------------------
30
+ export function isStart(node) {
31
+ if (node === undefined)
32
+ return false;
33
+ return node.kind === "native" && node.actionSlug === "start";
34
+ }
35
+ export function isEnd(node) {
36
+ if (node === undefined)
37
+ return false;
38
+ return node.kind === "native" && node.actionSlug === "end";
39
+ }
40
+ export function indexByUuid(nodes) {
41
+ const byUuid = new Map();
42
+ for (const node of nodes) {
43
+ if (typeof node.uuid === "string")
44
+ byUuid.set(node.uuid, node);
45
+ }
46
+ return byUuid;
47
+ }
48
+ /** Where the graph begins — the `start` node, or the first node if it has none. */
49
+ export function findStart(nodes) {
50
+ const start = nodes.find(isStart);
51
+ return start === undefined ? nodes[0] : start;
52
+ }
53
+ /**
54
+ * Whether a user-supplied `--paid` / `--highlight` token names this node.
55
+ * Slugs repeat within a release, so a slug token marks every node carrying it;
56
+ * pass a uuid to mark exactly one.
57
+ */
58
+ export function matchesNode(node, needles) {
59
+ const slug = node.slug === null ? undefined : node.slug;
60
+ return needles.some((needle) => needle === node.uuid || needle === slug);
61
+ }
62
+ /**
63
+ * A node's outgoing edges, in `childrenUuids` order, labelled by what the
64
+ * routing node means. Shared because this encodes routing *semantics* — index
65
+ * order, and the rule that a fallback pointing at the node's own next step is
66
+ * the same arrow rather than a second one. Two copies would let the two formats
67
+ * draw different graphs from one release.
68
+ */
69
+ /**
70
+ * A fallback pointing at the node's own next step: a failure here does not stop
71
+ * the run. It is not a second arrow — it is the same one — so it shows as a mark
72
+ * on the step rather than an edge. Silently dropping it makes a step that
73
+ * survives a provider outage look like one that dies on it, which is the exact
74
+ * misread this command exists to prevent.
75
+ */
76
+ export function continuesOnFailure(node) {
77
+ const fallback = node.fallbackChildUuid;
78
+ if (typeof fallback !== "string")
79
+ return false;
80
+ return childrenOf(node).includes(fallback);
81
+ }
82
+ /** A node's child slots, in order, including the empty ones. */
83
+ function childrenOf(node) {
84
+ const children = node.childrenUuids;
85
+ return children === null || children === undefined ? [] : children;
86
+ }
87
+ export function outgoingEdges(node, byUuid) {
88
+ const children = childrenOf(node);
89
+ const labels = edgeLabels(node, children.length);
90
+ const edges = [];
91
+ children.forEach((child, index) => {
92
+ if (typeof child === "string" && byUuid.has(child)) {
93
+ edges.push({ to: child, label: labels[index] });
94
+ }
95
+ });
96
+ const fallback = node.fallbackChildUuid;
97
+ if (typeof fallback === "string" &&
98
+ byUuid.has(fallback) &&
99
+ !children.includes(fallback)) {
100
+ edges.push({ to: fallback, label: "on failure", dashed: true });
101
+ }
102
+ return edges;
103
+ }
104
+ /** Structural problems worth saying out loud rather than drawing over. */
105
+ export function graphWarnings(nodes, byUuid, reached) {
106
+ const warnings = [];
107
+ const name = (node) => node.slug === null || node.slug === undefined ? node.uuid : node.slug;
108
+ const orphans = nodes.filter((node) => typeof node.uuid === "string" && !reached.has(node.uuid));
109
+ if (orphans.length > 0) {
110
+ warnings.push(`unreachable from start: ${orphans.map(name).join(", ")} — these nodes never run`);
111
+ }
112
+ const dangling = nodes.filter((node) => childrenOf(node).some((child) => child === null || byUuid.has(child) === false));
113
+ if (dangling.length > 0) {
114
+ warnings.push(`dangling childrenUuids (null or unknown) on: ${dangling.map(name).join(", ")} — the graph stops there`);
115
+ }
116
+ return warnings;
117
+ }
118
+ // ---------------------------------------------------------------------------
26
119
  // Shapes and labels
27
120
  // ---------------------------------------------------------------------------
28
121
  function shapeFor(node) {
29
122
  const action = node.actionSlug ?? "";
30
123
  if (node.kind === "native") {
31
- if (action === "start" || action === "end")
124
+ if (isStart(node) || isEnd(node))
32
125
  return ["([", "])"];
33
- if (ROUTING_ACTIONS.includes(action))
126
+ if (ROUTING_ACTIONS.has(action))
34
127
  return ["{", "}"];
35
128
  if (CODE_ACTIONS.includes(action))
36
129
  return ["[/", "/]"];
@@ -146,7 +239,7 @@ export function labelFor(node) {
146
239
  * is the matched path, index 1 the unmatched one. Getting this backwards
147
240
  * inverts the diagram's meaning, so it is table-driven, never inferred.
148
241
  */
149
- function edgeLabels(node, count) {
242
+ export function edgeLabels(node, count) {
150
243
  const action = node.kind === "native" ? (node.actionSlug ?? "") : "";
151
244
  if (action === "branch")
152
245
  return ["yes", "no"];
@@ -179,9 +272,6 @@ function edgeLabels(node, count) {
179
272
  // ---------------------------------------------------------------------------
180
273
  // Rendering
181
274
  // ---------------------------------------------------------------------------
182
- function matches(node, needles) {
183
- return needles.some((needle) => needle === node.uuid || needle === (node.slug ?? undefined));
184
- }
185
275
  function subNodes(node) {
186
276
  const nested = node.config?.["_nodes"];
187
277
  return Array.isArray(nested) && nested.length > 0
@@ -192,11 +282,7 @@ function renderNodes(nodes, context) {
192
282
  const warnings = [];
193
283
  const highlighted = [];
194
284
  const direction = context.direction;
195
- const byUuid = new Map();
196
- for (const node of nodes) {
197
- if (typeof node.uuid === "string")
198
- byUuid.set(node.uuid, node);
199
- }
285
+ const byUuid = indexByUuid(nodes);
200
286
  // Ids come from a breadth-first walk from `start`, so the diagram reads in
201
287
  // execution order and is stable across runs. Slugs are never used as ids:
202
288
  // they repeat within a single release.
@@ -204,7 +290,7 @@ function renderNodes(nodes, context) {
204
290
  const ids = new Map();
205
291
  const seen = new Set();
206
292
  const queue = [];
207
- const start = nodes.find((node) => node.kind === "native" && node.actionSlug === "start") ?? nodes[0];
293
+ const start = findStart(nodes);
208
294
  if (start !== undefined)
209
295
  queue.push(start.uuid);
210
296
  while (queue.length > 0) {
@@ -236,28 +322,29 @@ function renderNodes(nodes, context) {
236
322
  if (orphans.length > 0) {
237
323
  warnings.push(`unreachable from start: ${orphans.map((node) => node.slug ?? node.uuid).join(", ")} — these nodes never run`);
238
324
  }
325
+ // Edges come from the shared walk, so both formats encode routing the same
326
+ // way — index order, and the rule that a fallback pointing at a node's own
327
+ // next step is a ↷ mark rather than a second arrow. Only the id mapping is
328
+ // Mermaid's own.
239
329
  const edges = [];
240
330
  const dangling = new Set();
241
331
  for (const { id, node } of order) {
242
- const children = node.childrenUuids ?? [];
243
- const labels = edgeLabels(node, children.length);
244
- children.forEach((child, index) => {
245
- const target = child !== null && child !== undefined ? ids.get(child) : undefined;
246
- if (target === undefined) {
247
- dangling.add(node.slug ?? id);
248
- return;
249
- }
250
- edges.push({ from: id, to: target, label: labels[index] });
251
- });
252
- // A fallback pointing at the node's own next step means "a failure here
253
- // does not stop the run" the same arrow, not a second one. Drawing it
254
- // twice clutters the graph, so it becomes a marker on the label instead.
255
- const fallbackUuid = node.fallbackChildUuid;
256
- if (fallbackUuid !== null && fallbackUuid !== undefined) {
257
- const target = ids.get(fallbackUuid);
258
- if (target !== undefined && !children.includes(fallbackUuid)) {
259
- edges.push({ from: id, to: target, label: "on failure", dashed: true });
260
- }
332
+ for (const edge of outgoingEdges(node, byUuid)) {
333
+ const target = ids.get(edge.to);
334
+ if (target === undefined)
335
+ continue;
336
+ edges.push({
337
+ from: id,
338
+ to: target,
339
+ label: edge.label,
340
+ dashed: edge.dashed,
341
+ });
342
+ }
343
+ // `outgoingEdges` drops unusable slots by design; the diagram still has to
344
+ // report them, and it reports the node rather than the slot.
345
+ const hasUnusableSlot = childrenOf(node).some((child) => child === null || byUuid.has(child) === false);
346
+ if (hasUnusableSlot) {
347
+ dangling.add(node.slug === null || node.slug === undefined ? id : node.slug);
261
348
  }
262
349
  }
263
350
  if (dangling.size > 0) {
@@ -266,14 +353,10 @@ function renderNodes(nodes, context) {
266
353
  const lines = [];
267
354
  for (const { id, node } of order) {
268
355
  const [open, close] = shapeFor(node);
269
- const paid = matches(node, context.paid) ? "💳 " : "";
270
- if (matches(node, context.highlight))
356
+ const paid = matchesNode(node, context.paid) ? "💳 " : "";
357
+ if (matchesNode(node, context.highlight))
271
358
  highlighted.push(id);
272
- const children = node.childrenUuids ?? [];
273
- const continuesOnFailure = node.fallbackChildUuid !== null &&
274
- node.fallbackChildUuid !== undefined &&
275
- children.includes(node.fallbackChildUuid);
276
- lines.push(` ${id}${open}"${paid}${labelFor(node)}${continuesOnFailure ? " ↷" : ""}"${close}`);
359
+ lines.push(` ${id}${open}"${paid}${labelFor(node)}${continuesOnFailure(node) ? " ↷" : ""}"${close}`);
277
360
  // A group's steps are drawn inside a sub-graph, under an id namespace of
278
361
  // their own — `--paid` and `--highlight` apply just as they do out here,
279
362
  // since a billing step buried in a loop is exactly the one worth seeing.
@@ -0,0 +1,32 @@
1
+ import { displayWidth } from "./asciiBlocks.js";
2
+ import { type DiagramNode } from "./nodeDiagram.js";
3
+ /**
4
+ * Display names from the integration catalog. A graph stores wire slugs
5
+ * (`apolloio`, `enrichPerson`); the reader knows the product by its name.
6
+ * Optional throughout, so a graph diagrammed offline still renders.
7
+ */
8
+ export type NameBook = {
9
+ /** integration slug → product name, e.g. `apolloio` → `Apollo.io`. */
10
+ integrations?: Record<string, string>;
11
+ /** integration slug → action slug → action name. */
12
+ actions?: Record<string, Record<string, string>>;
13
+ /** native action slug → action name, e.g. `script` → `JavaScript`. */
14
+ native?: Record<string, string>;
15
+ };
16
+ export type AsciiOptions = {
17
+ title?: string;
18
+ /** Catalog names, so steps read as products rather than as slugs. */
19
+ names?: NameBook;
20
+ /** Node slugs or uuids that bill credits — marked with 💳. */
21
+ paid?: string[];
22
+ /** Node slugs or uuids to mark, e.g. the failing node in a trace. */
23
+ highlight?: string[];
24
+ };
25
+ export { displayWidth };
26
+ export type AsciiResult = {
27
+ diagram: string;
28
+ format: "ascii";
29
+ warnings: string[];
30
+ };
31
+ export declare function renderNodeAscii(nodes: DiagramNode[], options?: AsciiOptions): AsciiResult;
32
+ //# sourceMappingURL=nodeDiagramAscii.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nodeDiagramAscii.d.ts","sourceRoot":"","sources":["../../../src/commands/orchestration/nodeDiagramAscii.ts"],"names":[],"mappings":"AA4BA,OAAO,EAEL,YAAY,EAWb,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAEL,KAAK,WAAW,EAQjB,MAAM,kBAAkB,CAAC;AAE1B;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACjD,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAEF,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAkkBF,wBAAgB,eAAe,CAC7B,KAAK,EAAE,WAAW,EAAE,EACpB,OAAO,GAAE,YAAiB,GACzB,WAAW,CAiDb"}