@cargo-ai/cli 1.0.54 → 1.0.56
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/build/agentSkills.d.ts +34 -0
- package/build/agentSkills.d.ts.map +1 -0
- package/build/agentSkills.js +37 -0
- package/build/commands/auth/email.d.ts.map +1 -1
- package/build/commands/auth/email.js +2 -0
- package/build/commands/auth/index.d.ts.map +1 -1
- package/build/commands/auth/index.js +3 -0
- package/build/commands/orchestration/asciiBlocks.d.ts +42 -0
- package/build/commands/orchestration/asciiBlocks.d.ts.map +1 -0
- package/build/commands/orchestration/asciiBlocks.js +170 -0
- package/build/commands/orchestration/node.d.ts.map +1 -1
- package/build/commands/orchestration/node.js +131 -18
- package/build/commands/orchestration/nodeDiagram.d.ts +42 -0
- package/build/commands/orchestration/nodeDiagram.d.ts.map +1 -1
- package/build/commands/orchestration/nodeDiagram.js +123 -40
- package/build/commands/orchestration/nodeDiagramAscii.d.ts +32 -0
- package/build/commands/orchestration/nodeDiagramAscii.d.ts.map +1 -0
- package/build/commands/orchestration/nodeDiagramAscii.js +528 -0
- package/build/index.js +4 -6
- package/package.json +2 -2
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where this CLI tells a coding agent that the skill bundles exist.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS IS ITS OWN MODULE. The pointer used to live as a const inside
|
|
5
|
+
* `index.ts`, reachable only from `--help` and from the footer of an
|
|
6
|
+
* unknown-subcommand error. Both are paths an agent takes while it is still
|
|
7
|
+
* *exploring*. The moment someone has decided to use Cargo, and the only moment
|
|
8
|
+
* we know for certain a human or agent is watching, is the line right after
|
|
9
|
+
* sign-in succeeds, and that line said nothing.
|
|
10
|
+
*
|
|
11
|
+
* That gap is measurable and it is large: `@cargo-ai/cli` runs at roughly 1,900
|
|
12
|
+
* npm downloads a week while `getcargohq/cargo-skills` gains on the order of 35
|
|
13
|
+
* installs a week. People are already running this binary and almost none of
|
|
14
|
+
* them install the skills that make it usable by an agent.
|
|
15
|
+
*
|
|
16
|
+
* Keep the strings here rather than at each call site, so a change to how the
|
|
17
|
+
* bundles are named or installed cannot land in `--help` and miss sign-in.
|
|
18
|
+
*/
|
|
19
|
+
/** One line, for a footer where vertical space is somebody else's error. */
|
|
20
|
+
export declare const AGENT_SKILLS_HINT = "Coding agents: install the Cargo skills with `npx skills add getcargohq/cargo-skills`";
|
|
21
|
+
/**
|
|
22
|
+
* Printed after a successful sign-in.
|
|
23
|
+
*
|
|
24
|
+
* Deliberately two lines and no more. A sign-in banner that scrolls is a banner
|
|
25
|
+
* people configure away, and this fires at most a handful of times per machine
|
|
26
|
+
* because signing in is rare, so it does not need a suppression flag to be
|
|
27
|
+
* polite.
|
|
28
|
+
*
|
|
29
|
+
* No skill count. The help epilogue carried "sixteen skills" while the bundle
|
|
30
|
+
* held seventeen: a hand-maintained number in prose goes stale the next time
|
|
31
|
+
* somebody adds one, and nothing in CI was ever going to catch it here.
|
|
32
|
+
*/
|
|
33
|
+
export declare const AGENT_SKILLS_NEXT_STEP: string;
|
|
34
|
+
//# sourceMappingURL=agentSkills.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentSkills.d.ts","sourceRoot":"","sources":["../src/agentSkills.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,4EAA4E;AAC5E,eAAO,MAAM,iBAAiB,0FAC2D,CAAC;AAE1F;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,QAIvB,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where this CLI tells a coding agent that the skill bundles exist.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS IS ITS OWN MODULE. The pointer used to live as a const inside
|
|
5
|
+
* `index.ts`, reachable only from `--help` and from the footer of an
|
|
6
|
+
* unknown-subcommand error. Both are paths an agent takes while it is still
|
|
7
|
+
* *exploring*. The moment someone has decided to use Cargo, and the only moment
|
|
8
|
+
* we know for certain a human or agent is watching, is the line right after
|
|
9
|
+
* sign-in succeeds, and that line said nothing.
|
|
10
|
+
*
|
|
11
|
+
* That gap is measurable and it is large: `@cargo-ai/cli` runs at roughly 1,900
|
|
12
|
+
* npm downloads a week while `getcargohq/cargo-skills` gains on the order of 35
|
|
13
|
+
* installs a week. People are already running this binary and almost none of
|
|
14
|
+
* them install the skills that make it usable by an agent.
|
|
15
|
+
*
|
|
16
|
+
* Keep the strings here rather than at each call site, so a change to how the
|
|
17
|
+
* bundles are named or installed cannot land in `--help` and miss sign-in.
|
|
18
|
+
*/
|
|
19
|
+
/** One line, for a footer where vertical space is somebody else's error. */
|
|
20
|
+
export const AGENT_SKILLS_HINT = "Coding agents: install the Cargo skills with `npx skills add getcargohq/cargo-skills`";
|
|
21
|
+
/**
|
|
22
|
+
* Printed after a successful sign-in.
|
|
23
|
+
*
|
|
24
|
+
* Deliberately two lines and no more. A sign-in banner that scrolls is a banner
|
|
25
|
+
* people configure away, and this fires at most a handful of times per machine
|
|
26
|
+
* because signing in is rare, so it does not need a suppression flag to be
|
|
27
|
+
* polite.
|
|
28
|
+
*
|
|
29
|
+
* No skill count. The help epilogue carried "sixteen skills" while the bundle
|
|
30
|
+
* held seventeen: a hand-maintained number in prose goes stale the next time
|
|
31
|
+
* somebody adds one, and nothing in CI was ever going to catch it here.
|
|
32
|
+
*/
|
|
33
|
+
export const AGENT_SKILLS_NEXT_STEP = [
|
|
34
|
+
"Next: teach your coding agent this CLI.",
|
|
35
|
+
" npx skills add getcargohq/cargo-skills every command, as agent skills",
|
|
36
|
+
" npx skills add getcargohq/gtm-skills standalone one-job GTM skills",
|
|
37
|
+
].join("\n");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/email.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/email.ts"],"names":[],"mappings":"AAcA,6EAA6E;AAC7E,eAAO,MAAM,UAAU,MAAM,CAAC;AAE9B,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CA8C1E"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AGENT_SKILLS_NEXT_STEP } from "../../agentSkills.js";
|
|
1
2
|
import { getCredentialsPath } from "../../credentials.js";
|
|
2
3
|
import { askQuestion, openPromptInput } from "../../utils/prompt.js";
|
|
3
4
|
import { ExitCodes, failWith, info, outputJson, startSpinner, success, } from "../runHandler.js";
|
|
@@ -38,6 +39,7 @@ export async function runEmailLogin(opts) {
|
|
|
38
39
|
workspaceName: opts.workspaceName,
|
|
39
40
|
});
|
|
40
41
|
success(`Signed in as ${email}. Workspace: ${workspace.name}.`);
|
|
42
|
+
info(AGENT_SKILLS_NEXT_STEP);
|
|
41
43
|
outputJson({
|
|
42
44
|
ok: true,
|
|
43
45
|
status: "signedIn",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,OAAO,EAAU,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,OAAO,EAAU,MAAM,WAAW,CAAC;AAGjD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAExC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAwBxD,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,GAAG,GAChB,IAAI,CAkGN;AAmFD,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpE;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,WAAW,GAAG,SAAS,EACpC,IAAI,EAAE,aAAa,GAClB,cAAc,CAchB"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { revokeSession } from "@cargo-ai/cdk/cli";
|
|
2
2
|
import { Option } from "commander";
|
|
3
|
+
import { AGENT_SKILLS_NEXT_STEP } from "../../agentSkills.js";
|
|
3
4
|
import { getConfig } from "../../config.js";
|
|
4
5
|
import { clearCredentials, getCredentialsPath, loadCredentials, saveCredentials, } from "../../credentials.js";
|
|
5
6
|
import { ExitCodes, failWith, handleApiCall, info, outputJson, success, } from "../runHandler.js";
|
|
@@ -89,6 +90,7 @@ async function runLogin(opts) {
|
|
|
89
90
|
baseUrl: opts.baseUrl,
|
|
90
91
|
});
|
|
91
92
|
success("Signed in with API token.");
|
|
93
|
+
info(AGENT_SKILLS_NEXT_STEP);
|
|
92
94
|
outputJson({
|
|
93
95
|
ok: true,
|
|
94
96
|
status: "signedIn",
|
|
@@ -107,6 +109,7 @@ async function runLogin(opts) {
|
|
|
107
109
|
workspaceName: opts.workspaceName,
|
|
108
110
|
});
|
|
109
111
|
success(`Signed in. Default workspace: ${workspace.name}.`);
|
|
112
|
+
info(AGENT_SKILLS_NEXT_STEP);
|
|
110
113
|
outputJson({
|
|
111
114
|
ok: true,
|
|
112
115
|
status: "signedIn",
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export declare function displayWidth(text: string): number;
|
|
2
|
+
/**
|
|
3
|
+
* A rectangle of text plus the column its spine sits on. `spine` is where an
|
|
4
|
+
* incoming `│` attaches at the top and where an outgoing one leaves at the
|
|
5
|
+
* bottom, so stacking is a matter of lining up that column.
|
|
6
|
+
*/
|
|
7
|
+
export type Block = {
|
|
8
|
+
lines: string[];
|
|
9
|
+
width: number;
|
|
10
|
+
spine: number;
|
|
11
|
+
};
|
|
12
|
+
export declare function padTo(line: string, width: number): string;
|
|
13
|
+
export declare function indent(block: Block, by: number): Block;
|
|
14
|
+
/** Text lines centred on a shared spine. No lines is a legitimate answer — an
|
|
15
|
+
* unnamed routing node draws as its split and nothing else. */
|
|
16
|
+
export declare function textBlock(texts: string[]): Block;
|
|
17
|
+
/** One row carrying nothing but the spine. */
|
|
18
|
+
export declare function spineBlock(char?: string): Block;
|
|
19
|
+
export declare function emptyBlock(): Block;
|
|
20
|
+
/** Stack blocks vertically, aligning their spines. */
|
|
21
|
+
export declare function vstack(blocks: Block[]): Block;
|
|
22
|
+
/** Place blocks side by side, returning where each one's spine landed. */
|
|
23
|
+
export declare function hstack(blocks: Block[], gap: number): {
|
|
24
|
+
block: Block;
|
|
25
|
+
spines: number[];
|
|
26
|
+
};
|
|
27
|
+
/** Extend a column downwards so every column in a split ends level. */
|
|
28
|
+
export declare function padColumn(block: Block, toHeight: number, char: string): Block;
|
|
29
|
+
/**
|
|
30
|
+
* The glyph where a rail meets the spine. A tee has to point at the rails it
|
|
31
|
+
* actually carries: drawing `├` while the rail leaves to the left produces a
|
|
32
|
+
* junction with an arm attached to nothing and a rail attached to nothing.
|
|
33
|
+
*/
|
|
34
|
+
export declare function teeAt(spine: number, others: number[], down: boolean): string;
|
|
35
|
+
/** Draw a horizontal rail across `width`, writing one glyph per marked column. */
|
|
36
|
+
export declare function rail(width: number, marks: {
|
|
37
|
+
at: number;
|
|
38
|
+
char: string;
|
|
39
|
+
}[]): string;
|
|
40
|
+
/** Wrap a block in a captioned box, for a sub-flow that runs inside a step. */
|
|
41
|
+
export declare function frame(block: Block, caption: string): Block;
|
|
42
|
+
//# sourceMappingURL=asciiBlocks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asciiBlocks.d.ts","sourceRoot":"","sources":["../../../src/commands/orchestration/asciiBlocks.ts"],"names":[],"mappings":"AAyBA,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKjD;AAMD;;;;GAIG;AACH,MAAM,MAAM,KAAK,GAAG;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAIF,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,GAAG,KAAK,CAQtD;AAED;+DAC+D;AAC/D,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,KAAK,CAShD;AAED,8CAA8C;AAC9C,wBAAgB,UAAU,CAAC,IAAI,SAAM,GAAG,KAAK,CAE5C;AAED,wBAAgB,UAAU,IAAI,KAAK,CAElC;AAED,sDAAsD;AACtD,wBAAgB,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAsB7C;AAED,0EAA0E;AAC1E,wBAAgB,MAAM,CACpB,MAAM,EAAE,KAAK,EAAE,EACf,GAAG,EAAE,MAAM,GACV;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CA6BpC;AAED,uEAAuE;AACvE,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,KAAK,CAQ7E;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAO5E;AAED,kFAAkF;AAClF,wBAAgB,IAAI,CAClB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,GACpC,MAAM,CAWR;AAED,+EAA+E;AAC/E,wBAAgB,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,CAU1D"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// Text blocks: the layout algebra the ASCII diagram is assembled from.
|
|
2
|
+
//
|
|
3
|
+
// A Block is a rectangle of text plus the column its spine sits on — the column
|
|
4
|
+
// an incoming `│` attaches to at the top and an outgoing one leaves from at the
|
|
5
|
+
// bottom. Stacking is then a matter of lining that column up, which is what lets
|
|
6
|
+
// the renderer compose a drawing without ever tracking absolute coordinates.
|
|
7
|
+
//
|
|
8
|
+
// Nothing here knows what a workflow is. The invariant every producer maintains
|
|
9
|
+
// is that each line of a Block is exactly `block.width` cells wide, which is why
|
|
10
|
+
// so little of this has to measure anything.
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Character width
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// A marker glyph occupies two terminal cells; measuring it as one silently
|
|
15
|
+
// shears every line to its right. `Emoji_Presentation` is the property that
|
|
16
|
+
// means "wide by default" — `Extended_Pictographic` is too broad, matching `◀`,
|
|
17
|
+
// which a terminal draws in a single cell.
|
|
18
|
+
const WIDE = /\p{Emoji_Presentation}/u;
|
|
19
|
+
// Most lines are plain text; the per-character scan is only needed once a line
|
|
20
|
+
// actually carries a marker. One test for the whole string skips it.
|
|
21
|
+
const PLAIN = /^[\x20-\x7E]*$/;
|
|
22
|
+
export function displayWidth(text) {
|
|
23
|
+
if (PLAIN.test(text))
|
|
24
|
+
return text.length;
|
|
25
|
+
let total = 0;
|
|
26
|
+
for (const char of text)
|
|
27
|
+
total += WIDE.test(char) ? 2 : 1;
|
|
28
|
+
return total;
|
|
29
|
+
}
|
|
30
|
+
const blank = (width) => (width > 0 ? " ".repeat(width) : "");
|
|
31
|
+
export function padTo(line, width) {
|
|
32
|
+
const short = width - displayWidth(line);
|
|
33
|
+
return short > 0 ? line + " ".repeat(short) : line;
|
|
34
|
+
}
|
|
35
|
+
export function indent(block, by) {
|
|
36
|
+
if (by <= 0)
|
|
37
|
+
return block;
|
|
38
|
+
const prefix = " ".repeat(by);
|
|
39
|
+
return {
|
|
40
|
+
lines: block.lines.map((line) => prefix + line),
|
|
41
|
+
width: block.width + by,
|
|
42
|
+
spine: block.spine + by,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** Text lines centred on a shared spine. No lines is a legitimate answer — an
|
|
46
|
+
* unnamed routing node draws as its split and nothing else. */
|
|
47
|
+
export function textBlock(texts) {
|
|
48
|
+
if (texts.length === 0)
|
|
49
|
+
return emptyBlock();
|
|
50
|
+
const measured = texts.map((text) => ({ text, own: displayWidth(text) }));
|
|
51
|
+
const width = Math.max(...measured.map((entry) => entry.own));
|
|
52
|
+
const lines = measured.map(({ text, own }) => {
|
|
53
|
+
const left = Math.floor((width - own) / 2);
|
|
54
|
+
return " ".repeat(left) + text + blank(width - own - left);
|
|
55
|
+
});
|
|
56
|
+
return { lines, width, spine: Math.floor((width - 1) / 2) };
|
|
57
|
+
}
|
|
58
|
+
/** One row carrying nothing but the spine. */
|
|
59
|
+
export function spineBlock(char = "│") {
|
|
60
|
+
return { lines: [char], width: 1, spine: 0 };
|
|
61
|
+
}
|
|
62
|
+
export function emptyBlock() {
|
|
63
|
+
return { lines: [], width: 0, spine: 0 };
|
|
64
|
+
}
|
|
65
|
+
/** Stack blocks vertically, aligning their spines. */
|
|
66
|
+
export function vstack(blocks) {
|
|
67
|
+
const real = blocks.filter((block) => block.lines.length > 0);
|
|
68
|
+
if (real.length === 0)
|
|
69
|
+
return emptyBlock();
|
|
70
|
+
if (real.length === 1) {
|
|
71
|
+
const only = real[0];
|
|
72
|
+
if (only === undefined)
|
|
73
|
+
return emptyBlock();
|
|
74
|
+
return only;
|
|
75
|
+
}
|
|
76
|
+
const spine = Math.max(...real.map((block) => block.spine));
|
|
77
|
+
const shifted = real.map((block) => indent(block, spine - block.spine));
|
|
78
|
+
const width = Math.max(...shifted.map((block) => block.width));
|
|
79
|
+
return {
|
|
80
|
+
lines: shifted.flatMap((block) => {
|
|
81
|
+
const short = blank(width - block.width);
|
|
82
|
+
return short.length === 0
|
|
83
|
+
? block.lines
|
|
84
|
+
: block.lines.map((line) => line + short);
|
|
85
|
+
}),
|
|
86
|
+
width,
|
|
87
|
+
spine,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/** Place blocks side by side, returning where each one's spine landed. */
|
|
91
|
+
export function hstack(blocks, gap) {
|
|
92
|
+
const height = Math.max(...blocks.map((block) => block.lines.length));
|
|
93
|
+
const spines = [];
|
|
94
|
+
let offset = 0;
|
|
95
|
+
const rows = Array.from({ length: height }, () => "");
|
|
96
|
+
blocks.forEach((block, index) => {
|
|
97
|
+
if (index > 0)
|
|
98
|
+
offset += gap;
|
|
99
|
+
spines.push(offset + block.spine);
|
|
100
|
+
for (let row = 0; row < height; row += 1) {
|
|
101
|
+
// Every Block keeps `displayWidth(line) === block.width` on every line, so
|
|
102
|
+
// the line needs no measuring here — only the canvas does.
|
|
103
|
+
const canvas = rows[row];
|
|
104
|
+
const line = block.lines[row];
|
|
105
|
+
rows[row] =
|
|
106
|
+
padTo(canvas === undefined ? "" : canvas, offset) +
|
|
107
|
+
(line === undefined ? blank(block.width) : line);
|
|
108
|
+
}
|
|
109
|
+
offset += block.width;
|
|
110
|
+
});
|
|
111
|
+
return {
|
|
112
|
+
block: {
|
|
113
|
+
lines: rows.map((row) => padTo(row, offset)),
|
|
114
|
+
width: offset,
|
|
115
|
+
spine: 0,
|
|
116
|
+
},
|
|
117
|
+
spines,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/** Extend a column downwards so every column in a split ends level. */
|
|
121
|
+
export function padColumn(block, toHeight, char) {
|
|
122
|
+
const missing = toHeight - block.lines.length;
|
|
123
|
+
if (missing <= 0)
|
|
124
|
+
return block;
|
|
125
|
+
const filler = padTo(" ".repeat(block.spine) + char, block.width);
|
|
126
|
+
return {
|
|
127
|
+
...block,
|
|
128
|
+
lines: [...block.lines, ...Array.from({ length: missing }, () => filler)],
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* The glyph where a rail meets the spine. A tee has to point at the rails it
|
|
133
|
+
* actually carries: drawing `├` while the rail leaves to the left produces a
|
|
134
|
+
* junction with an arm attached to nothing and a rail attached to nothing.
|
|
135
|
+
*/
|
|
136
|
+
export function teeAt(spine, others, down) {
|
|
137
|
+
const left = others.some((at) => at < spine);
|
|
138
|
+
const right = others.some((at) => at > spine);
|
|
139
|
+
if (left && right)
|
|
140
|
+
return "┼";
|
|
141
|
+
if (left)
|
|
142
|
+
return "┤";
|
|
143
|
+
if (right)
|
|
144
|
+
return "├";
|
|
145
|
+
return down ? "┬" : "┴";
|
|
146
|
+
}
|
|
147
|
+
/** Draw a horizontal rail across `width`, writing one glyph per marked column. */
|
|
148
|
+
export function rail(width, marks) {
|
|
149
|
+
const sorted = [...marks].sort((a, b) => a.at - b.at);
|
|
150
|
+
const first = sorted[0];
|
|
151
|
+
const last = sorted[sorted.length - 1];
|
|
152
|
+
if (first === undefined || last === undefined)
|
|
153
|
+
return " ".repeat(width);
|
|
154
|
+
const cells = Array.from({ length: width }, (_, column) => column > first.at && column < last.at ? "─" : " ");
|
|
155
|
+
for (const mark of sorted)
|
|
156
|
+
cells[mark.at] = mark.char;
|
|
157
|
+
return cells.join("");
|
|
158
|
+
}
|
|
159
|
+
/** Wrap a block in a captioned box, for a sub-flow that runs inside a step. */
|
|
160
|
+
export function frame(block, caption) {
|
|
161
|
+
const inner = Math.max(block.width, displayWidth(caption) + 2);
|
|
162
|
+
const width = inner + 4;
|
|
163
|
+
const label = ` ${caption} `;
|
|
164
|
+
const lines = [
|
|
165
|
+
`┌─${label}${"─".repeat(Math.max(0, width - 3 - displayWidth(label)))}┐`,
|
|
166
|
+
...block.lines.map((line) => `│ ${padTo(line, inner)} │`),
|
|
167
|
+
`└${"─".repeat(width - 2)}┘`,
|
|
168
|
+
];
|
|
169
|
+
return { lines, width, spine: block.spine + 2 };
|
|
170
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../src/commands/orchestration/node.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../src/commands/orchestration/node.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAgBxC,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,GAAG,IAAI,CAoK7E"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { ExitCodes, failWith, handleApiCall, outputJson, parseJson, } from "../runHandler.js";
|
|
3
3
|
import { extractNodes, renderNodeDiagram, toMermaidBlock, } from "./nodeDiagram.js";
|
|
4
|
+
import { renderNodeAscii } from "./nodeDiagramAscii.js";
|
|
4
5
|
export function registerNodeCommands(parent, getApi) {
|
|
5
6
|
const node = parent
|
|
6
7
|
.command("node")
|
|
@@ -105,7 +106,7 @@ Examples:
|
|
|
105
106
|
function registerDiagramCommand(node, getApi) {
|
|
106
107
|
node
|
|
107
108
|
.command("diagram")
|
|
108
|
-
.description("Draw a node graph
|
|
109
|
+
.description("Draw a node graph — ASCII for a terminal, Mermaid to paste elsewhere (free, runs nothing, no credits)")
|
|
109
110
|
.option("--nodes <json>", "Node definitions (JSON array, or '-' to read from stdin)")
|
|
110
111
|
.option("--file <path>", "Read a JSON payload or node array from a file")
|
|
111
112
|
.option("--workflow-uuid <uuid>", "Diagram a workflow's deployed release (add --draft for its draft)")
|
|
@@ -113,37 +114,74 @@ function registerDiagramCommand(node, getApi) {
|
|
|
113
114
|
.option("--release-uuid <uuid>", "Diagram a specific release")
|
|
114
115
|
.option("--run-uuid <uuid>", "Diagram the graph a run executed (follows the run's release when it has one)")
|
|
115
116
|
.option("--title <text>", "Title rendered above the diagram")
|
|
116
|
-
.option("--
|
|
117
|
+
.option("--format <ascii|mermaid>", "ascii = readable in a terminal · mermaid = paste into a PR, docs or a page (default: mermaid)", "mermaid")
|
|
118
|
+
.option("--direction <TD|LR>", "Mermaid flow direction, ignored by --format ascii (default: TD)", "TD")
|
|
117
119
|
.option("--paid <slugs>", "Comma-separated node slugs/uuids that bill credits — marked 💳")
|
|
118
|
-
.option("--highlight <slugs>", "Comma-separated node slugs/uuids to mark red,
|
|
119
|
-
.option("--raw", "Print the
|
|
120
|
+
.option("--highlight <slugs>", "Comma-separated node slugs/uuids to mark (red in Mermaid, ← in ASCII)")
|
|
121
|
+
.option("--raw", "Print the diagram itself instead of JSON")
|
|
120
122
|
.addHelpText("after", `
|
|
121
123
|
Free, runs nothing: turns a node graph into a picture so a reviewer can see the
|
|
122
124
|
routing, the fallback paths and which steps bill before approving or deploying
|
|
123
125
|
it. Pass exactly one source.
|
|
124
126
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
127
|
+
Which format:
|
|
128
|
+
--format ascii SHOWING a graph to someone at a terminal. Renders as a
|
|
129
|
+
centred flow with named steps and labelled branches, so it
|
|
130
|
+
is readable as-is. Use this whenever you are about to
|
|
131
|
+
explain what a workflow, tool or play does.
|
|
132
|
+
--format ascii resolves step names from the integration catalog, so a step
|
|
133
|
+
reads 'Apollo.io / Enrich person'. --nodes and --file skip that lookup and
|
|
134
|
+
keep the slugs, so they work with no credential at all.
|
|
135
|
+
|
|
136
|
+
--format mermaid PASTING the graph somewhere that renders it — a PR body, a
|
|
137
|
+
markdown doc, a published page. In a terminal it is source
|
|
138
|
+
code, not a picture. This is the default, for
|
|
139
|
+
compatibility.
|
|
140
|
+
|
|
141
|
+
Returns {"diagram":"…","format":"ascii"|"mermaid","warnings":[…]}, or the
|
|
142
|
+
diagram itself with --raw (fenced for mermaid, plain text for ascii).
|
|
143
|
+
'warnings' reports structural problems worth saying out loud — nodes
|
|
144
|
+
unreachable from start, dangling childrenUuids.
|
|
128
145
|
|
|
129
146
|
A run created by 'action execute' carries its own graph; a run of a deployed
|
|
130
147
|
tool or play carries only a releaseUuid, and --run-uuid follows it either way.
|
|
131
148
|
|
|
132
149
|
Examples:
|
|
133
|
-
$ cargo-ai orchestration node diagram --workflow-uuid <uuid> --raw
|
|
134
|
-
$ cargo-ai orchestration node diagram --workflow-uuid <uuid> --draft --paid enrich,verify
|
|
135
|
-
$ cargo-ai orchestration node diagram --run-uuid <uuid> --highlight branch_1 --raw
|
|
136
|
-
$ cargo-ai orchestration
|
|
150
|
+
$ cargo-ai orchestration node diagram --workflow-uuid <uuid> --format ascii --raw
|
|
151
|
+
$ cargo-ai orchestration node diagram --workflow-uuid <uuid> --draft --paid enrich,verify --format ascii --raw
|
|
152
|
+
$ cargo-ai orchestration node diagram --run-uuid <uuid> --highlight branch_1 --format ascii --raw
|
|
153
|
+
$ cargo-ai orchestration node diagram --workflow-uuid <uuid> --raw # mermaid, for a PR
|
|
154
|
+
$ cargo-ai orchestration release get-deployed --workflow-uuid <uuid> | cargo-ai orchestration node diagram --nodes - --format ascii --raw`)
|
|
137
155
|
.action(async (opts) => {
|
|
156
|
+
const format = (opts.format === undefined ? "mermaid" : opts.format).toLowerCase();
|
|
157
|
+
if (format !== "ascii" && format !== "mermaid") {
|
|
158
|
+
failWith(`--format must be 'ascii' or 'mermaid', got '${format}'`, {
|
|
159
|
+
code: ExitCodes.InvalidUsage,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
138
162
|
const nodes = await resolveNodes(getApi, opts);
|
|
139
|
-
const result =
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
163
|
+
const result = format === "ascii"
|
|
164
|
+
? renderNodeAscii(nodes, {
|
|
165
|
+
title: opts.title,
|
|
166
|
+
paid: splitList(opts.paid),
|
|
167
|
+
highlight: splitList(opts.highlight),
|
|
168
|
+
// `--nodes` / `--file` are the offline sources: resolving them
|
|
169
|
+
// touched no API, and reaching for one now to prettify a label
|
|
170
|
+
// would make an offline command demand a login.
|
|
171
|
+
names: opts.nodes === undefined && opts.file === undefined
|
|
172
|
+
? await catalogNames(getApi, nodes)
|
|
173
|
+
: undefined,
|
|
174
|
+
})
|
|
175
|
+
: renderNodeDiagram(nodes, {
|
|
176
|
+
title: opts.title,
|
|
177
|
+
direction: (opts.direction === undefined
|
|
178
|
+
? "TD"
|
|
179
|
+
: opts.direction).toUpperCase(),
|
|
180
|
+
paid: splitList(opts.paid),
|
|
181
|
+
highlight: splitList(opts.highlight),
|
|
182
|
+
});
|
|
145
183
|
if (opts.raw === true) {
|
|
146
|
-
console.log(toMermaidBlock(result.diagram));
|
|
184
|
+
console.log(format === "ascii" ? result.diagram : toMermaidBlock(result.diagram));
|
|
147
185
|
for (const warning of result.warnings)
|
|
148
186
|
console.error(`⚠ ${warning}`);
|
|
149
187
|
return;
|
|
@@ -151,6 +189,81 @@ Examples:
|
|
|
151
189
|
outputJson(result);
|
|
152
190
|
});
|
|
153
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* Display names for the steps a graph actually uses, so it reads
|
|
194
|
+
* `Apollo.io` / `Enrich person` rather than `apolloio` / `enrichPerson`.
|
|
195
|
+
*
|
|
196
|
+
* Both halves come from the platform's own catalogs — the integration catalog
|
|
197
|
+
* for connector steps, the native-integration catalog for built-in ones — so a
|
|
198
|
+
* step is never labelled with a name this CLI invented. Renaming an action on
|
|
199
|
+
* the platform reaches the drawing without a code change.
|
|
200
|
+
*
|
|
201
|
+
* Best-effort, and only for graphs that came from the API in the first place.
|
|
202
|
+
* `--nodes` and `--file` skip it entirely: those resolve without a credential,
|
|
203
|
+
* and `getApi()` exits the process when there is none, so asking for names
|
|
204
|
+
* there would turn an offline command into one that demands a login. A catalog
|
|
205
|
+
* that is reachable but fails — a token without the scope, a network blip —
|
|
206
|
+
* falls back to slugs rather than losing the drawing.
|
|
207
|
+
*/
|
|
208
|
+
async function catalogNames(getApi, nodes) {
|
|
209
|
+
const used = new Set();
|
|
210
|
+
let hasNative = false;
|
|
211
|
+
// Group bodies live in `config._nodes` and are drawn too, so their steps need
|
|
212
|
+
// names as much as the outer ones — otherwise a loop shows wire slugs beside
|
|
213
|
+
// an outer graph showing product names.
|
|
214
|
+
const collect = (list) => {
|
|
215
|
+
for (const node of list) {
|
|
216
|
+
if (node.kind === "connector" &&
|
|
217
|
+
typeof node.integrationSlug === "string") {
|
|
218
|
+
used.add(node.integrationSlug);
|
|
219
|
+
}
|
|
220
|
+
if (node.kind === "native")
|
|
221
|
+
hasNative = true;
|
|
222
|
+
const config = node.config;
|
|
223
|
+
if (config !== null && config !== undefined) {
|
|
224
|
+
const nested = config["_nodes"];
|
|
225
|
+
if (Array.isArray(nested))
|
|
226
|
+
collect(nested);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
collect(nodes);
|
|
231
|
+
if (used.size === 0 && !hasNative)
|
|
232
|
+
return undefined;
|
|
233
|
+
try {
|
|
234
|
+
const api = getApi();
|
|
235
|
+
const [integrations, native] = await Promise.all([
|
|
236
|
+
used.size === 0
|
|
237
|
+
? undefined
|
|
238
|
+
: api.connection.integration
|
|
239
|
+
.list({ slugs: [...used] })
|
|
240
|
+
.catch(() => undefined),
|
|
241
|
+
hasNative
|
|
242
|
+
? api.connection.nativeIntegration.get().catch(() => undefined)
|
|
243
|
+
: undefined,
|
|
244
|
+
]);
|
|
245
|
+
const byIntegration = {};
|
|
246
|
+
const byAction = {};
|
|
247
|
+
const listed = integrations === undefined ? [] : integrations.integrations;
|
|
248
|
+
for (const integration of listed) {
|
|
249
|
+
const actions = integration.actions === undefined ? {} : integration.actions;
|
|
250
|
+
byIntegration[integration.slug] = integration.name;
|
|
251
|
+
byAction[integration.slug] = Object.fromEntries(Object.entries(actions).map(([slug, action]) => [slug, action.name]));
|
|
252
|
+
}
|
|
253
|
+
const nativeActions = native === undefined ? {} : native.nativeIntegration.actions;
|
|
254
|
+
return {
|
|
255
|
+
integrations: byIntegration,
|
|
256
|
+
actions: byAction,
|
|
257
|
+
native: Object.fromEntries(Object.entries(nativeActions).map(([slug, action]) => [
|
|
258
|
+
slug,
|
|
259
|
+
action.name,
|
|
260
|
+
])),
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
return undefined;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
154
267
|
function splitList(value) {
|
|
155
268
|
if (value === undefined)
|
|
156
269
|
return [];
|
|
@@ -30,8 +30,50 @@ export type DiagramResult = {
|
|
|
30
30
|
/** Structural problems worth saying out loud rather than drawing over. */
|
|
31
31
|
warnings: string[];
|
|
32
32
|
};
|
|
33
|
+
/** Actions that route rather than do work. Shared, so adding one to the
|
|
34
|
+
* platform does not have to be mirrored in each renderer. */
|
|
35
|
+
export declare const ROUTING_ACTIONS: Set<string>;
|
|
36
|
+
export declare function isStart(node: DiagramNode | undefined): boolean;
|
|
37
|
+
export declare function isEnd(node: DiagramNode | undefined): boolean;
|
|
38
|
+
export declare function indexByUuid(nodes: DiagramNode[]): Map<string, DiagramNode>;
|
|
39
|
+
/** Where the graph begins — the `start` node, or the first node if it has none. */
|
|
40
|
+
export declare function findStart(nodes: DiagramNode[]): DiagramNode | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Whether a user-supplied `--paid` / `--highlight` token names this node.
|
|
43
|
+
* Slugs repeat within a release, so a slug token marks every node carrying it;
|
|
44
|
+
* pass a uuid to mark exactly one.
|
|
45
|
+
*/
|
|
46
|
+
export declare function matchesNode(node: DiagramNode, needles: string[]): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* A node's outgoing edges, in `childrenUuids` order, labelled by what the
|
|
49
|
+
* routing node means. Shared because this encodes routing *semantics* — index
|
|
50
|
+
* order, and the rule that a fallback pointing at the node's own next step is
|
|
51
|
+
* the same arrow rather than a second one. Two copies would let the two formats
|
|
52
|
+
* draw different graphs from one release.
|
|
53
|
+
*/
|
|
54
|
+
/**
|
|
55
|
+
* A fallback pointing at the node's own next step: a failure here does not stop
|
|
56
|
+
* the run. It is not a second arrow — it is the same one — so it shows as a mark
|
|
57
|
+
* on the step rather than an edge. Silently dropping it makes a step that
|
|
58
|
+
* survives a provider outage look like one that dies on it, which is the exact
|
|
59
|
+
* misread this command exists to prevent.
|
|
60
|
+
*/
|
|
61
|
+
export declare function continuesOnFailure(node: DiagramNode): boolean;
|
|
62
|
+
export declare function outgoingEdges(node: DiagramNode, byUuid: Map<string, DiagramNode>): {
|
|
63
|
+
to: string;
|
|
64
|
+
label?: string;
|
|
65
|
+
dashed?: boolean;
|
|
66
|
+
}[];
|
|
67
|
+
/** Structural problems worth saying out loud rather than drawing over. */
|
|
68
|
+
export declare function graphWarnings(nodes: DiagramNode[], byUuid: Map<string, DiagramNode>, reached: Set<string>): string[];
|
|
33
69
|
/** What the node is, in words a user recognises. */
|
|
34
70
|
export declare function labelFor(node: DiagramNode): string;
|
|
71
|
+
/**
|
|
72
|
+
* `childrenUuids` order carries the routing semantics — index 0 of a `branch`
|
|
73
|
+
* is the matched path, index 1 the unmatched one. Getting this backwards
|
|
74
|
+
* inverts the diagram's meaning, so it is table-driven, never inferred.
|
|
75
|
+
*/
|
|
76
|
+
export declare function edgeLabels(node: DiagramNode, count: number): (string | undefined)[];
|
|
35
77
|
export declare function renderNodeDiagram(nodes: DiagramNode[], options?: DiagramOptions): DiagramResult;
|
|
36
78
|
/** Node-shaped means: a uuid plus the fields a graph is walked by. */
|
|
37
79
|
export declare function isNodeArray(value: unknown): value is DiagramNode[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodeDiagram.d.ts","sourceRoot":"","sources":["../../../src/commands/orchestration/nodeDiagram.ts"],"names":[],"mappings":"AAgBA;;2CAE2C;AAC3C,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,CAAC,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC;IACzC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACzC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,CAAC;IAClB,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;
|
|
1
|
+
{"version":3,"file":"nodeDiagram.d.ts","sourceRoot":"","sources":["../../../src/commands/orchestration/nodeDiagram.ts"],"names":[],"mappings":"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"}
|