@cargo-ai/cli 1.0.25 → 1.0.27
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/README.md +18 -18
- package/build/commands/hosting/app.js +2 -2
- package/build/commands/hosting/worker.d.ts.map +1 -1
- package/build/commands/hosting/worker.js +13 -6
- package/build/commands/runHandler.d.ts +5 -1
- package/build/commands/runHandler.d.ts.map +1 -1
- package/build/commands/runHandler.js +36 -4
- package/build/commands/templateUtils.d.ts.map +1 -0
- package/build/index.js +4 -0
- package/package.json +5 -4
- package/build/commands/hosting/templateUtils.d.ts.map +0 -1
- package/build/commands/workflow/index.d.ts +0 -4
- package/build/commands/workflow/index.d.ts.map +0 -1
- package/build/commands/workflow/index.js +0 -10
- package/build/commands/workflow/inputTypes.d.ts +0 -18
- package/build/commands/workflow/inputTypes.d.ts.map +0 -1
- package/build/commands/workflow/inputTypes.js +0 -175
- package/build/commands/workflow/sync.d.ts +0 -4
- package/build/commands/workflow/sync.d.ts.map +0 -1
- package/build/commands/workflow/sync.js +0 -454
- /package/build/commands/{hosting/templateUtils.d.ts → templateUtils.d.ts} +0 -0
- /package/build/commands/{hosting/templateUtils.js → templateUtils.js} +0 -0
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ cargo-ai logout
|
|
|
45
45
|
|
|
46
46
|
### Browser sign-in (`--oauth`)
|
|
47
47
|
|
|
48
|
-
`cargo-ai login --oauth` runs the standard [OAuth 2.0 Device Authorization Flow](https://datatracker.ietf.org/doc/html/rfc8628)
|
|
48
|
+
`cargo-ai login --oauth` runs the standard [OAuth 2.0 Device Authorization Flow](https://datatracker.ietf.org/doc/html/rfc8628) against the Cargo OAuth provider, prints a verification URL and user code, opens your default browser, and polls until you complete sign-in. On success the access token is written to `~/.config/cargo-ai/credentials.json`.
|
|
49
49
|
|
|
50
50
|
The CLI ships with a built-in OAuth client, so no setup is required. Advanced users can override the provider via `--client-id`, `--domain`, and `--audience`.
|
|
51
51
|
|
|
@@ -98,23 +98,23 @@ cargo-ai orchestration workflow --help
|
|
|
98
98
|
|
|
99
99
|
### Domains and example commands
|
|
100
100
|
|
|
101
|
-
| Domain | Description
|
|
102
|
-
| ------------------------ |
|
|
103
|
-
| **init** | Workspace initialisation (user, workspace, datasets, etc.)
|
|
104
|
-
| **orchestration** | Workflows, plays, runs, batches, tools, templates
|
|
105
|
-
| **workspaceManagement** | Workspaces, users, tokens, roles, folders
|
|
106
|
-
| **storage** | Datasets, models, relationships, runs, records
|
|
107
|
-
| **connection** | Connectors and integrations
|
|
108
|
-
| **billing** | Usage and subscription
|
|
109
|
-
| **segmentation** | Segments and changes
|
|
110
|
-
| **revenue-organization** | Allocations, capacities, members, territories
|
|
111
|
-
| **expression** | Recipes and expression evaluation
|
|
112
|
-
| **system-of-record** | System of record, client, logs
|
|
113
|
-
| **user-management** | Current user (no workspace context)
|
|
114
|
-
| **ai** | AI templates, agents, releases, chats, MCP, files
|
|
115
|
-
| **context** | Context repository, runtime sandbox, and knowledge graph
|
|
116
|
-
| **hosting** | Cargo Hosting apps (Vite SPAs), workers, and deployments
|
|
117
|
-
| **
|
|
101
|
+
| Domain | Description | Example commands |
|
|
102
|
+
| ------------------------ | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
103
|
+
| **init** | Workspace initialisation (user, workspace, datasets, etc.) | `cargo-ai init` |
|
|
104
|
+
| **orchestration** | Workflows, plays, runs, batches, tools, templates | `cargo-ai orchestration workflow list`, `cargo-ai orchestration run list --workflow-uuid <uuid>` |
|
|
105
|
+
| **workspaceManagement** | Workspaces, users, tokens, roles, folders | `cargo-ai workspaceManagement workspaces list`, `cargo-ai workspaceManagement token list` |
|
|
106
|
+
| **storage** | Datasets, models, relationships, runs, records | `cargo-ai storage dataset list`, `cargo-ai storage model list` |
|
|
107
|
+
| **connection** | Connectors and integrations | `cargo-ai connection connector list`, `cargo-ai connection integration list` |
|
|
108
|
+
| **billing** | Usage and subscription | `cargo-ai billing subscription get`, `cargo-ai billing usage get-metrics --payload '{"from":"2025-01-01","to":"2025-01-31"}'` |
|
|
109
|
+
| **segmentation** | Segments and changes | `cargo-ai segmentation segment list`, `cargo-ai segmentation change list --payload '{}'` |
|
|
110
|
+
| **revenue-organization** | Allocations, capacities, members, territories | `cargo-ai revenue-organization member list`, `cargo-ai revenue-organization territory list` |
|
|
111
|
+
| **expression** | Recipes and expression evaluation | `cargo-ai expression recipe list`, `cargo-ai expression eval evaluate --payload '{}'` |
|
|
112
|
+
| **system-of-record** | System of record, client, logs | `cargo-ai system-of-record sor list`, `cargo-ai system-of-record log list --payload '{}'` |
|
|
113
|
+
| **user-management** | Current user (no workspace context) | `cargo-ai user-management user get-current` |
|
|
114
|
+
| **ai** | AI templates, agents, releases, chats, MCP, files | `cargo-ai ai template list`, `cargo-ai ai agent list`, `cargo-ai ai file list` |
|
|
115
|
+
| **context** | Context repository, runtime sandbox, and knowledge graph | `cargo-ai context repository get`, `cargo-ai context runtime browse --path <path>`, `cargo-ai context graph get` |
|
|
116
|
+
| **hosting** | Cargo Hosting apps (Vite SPAs), workers, and deployments | `cargo-ai hosting app list`, `cargo-ai hosting worker list`, `cargo-ai hosting deployment list --payload '{}'` |
|
|
117
|
+
| **cdk** | Declarative IaC: define resources in code, deploy, generate types | `cargo-ai cdk init <dir>`, `cargo-ai cdk types`, `cargo-ai cdk plan`, `cargo-ai cdk deploy` |
|
|
118
118
|
|
|
119
119
|
Commands that accept complex payloads use a `--payload <json>` option (e.g. `cargo-ai orchestration play create --payload '{"name":"My Play",...}'`). Use `--help` on any subcommand for options.
|
|
120
120
|
|
|
@@ -3,7 +3,7 @@ import { createRequire } from "node:module";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { handleApiCall, outputJson } from "../runHandler.js";
|
|
6
|
-
import { copyDirectory, listTemplates } from "
|
|
6
|
+
import { copyDirectory, listTemplates } from "../templateUtils.js";
|
|
7
7
|
const require = createRequire(import.meta.url);
|
|
8
8
|
export function registerAppCommands(parent, getApi) {
|
|
9
9
|
const app = parent
|
|
@@ -32,7 +32,7 @@ export function registerAppCommands(parent, getApi) {
|
|
|
32
32
|
.command("create")
|
|
33
33
|
.description("Create a new app slot")
|
|
34
34
|
.requiredOption("--name <name>", "Display name for the app")
|
|
35
|
-
.requiredOption("--slug <slug>", "URL slug (must be
|
|
35
|
+
.requiredOption("--slug <slug>", "URL slug (must be unique within your workspace; the public host is suffixed with your workspace UUID prefix)")
|
|
36
36
|
.option("--folder-uuid <uuid>", "Optional folder UUID")
|
|
37
37
|
.action(async (opts) => {
|
|
38
38
|
const api = getApi();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../../src/commands/hosting/worker.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAMxC,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,GAAG,GAChB,IAAI,
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../../src/commands/hosting/worker.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAMxC,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,GAAG,GAChB,IAAI,CAuON"}
|
|
@@ -2,8 +2,8 @@ import { existsSync, promises as fs } from "node:fs";
|
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { handleApiCall, outputJson } from "../runHandler.js";
|
|
6
|
-
import { copyDirectory, listTemplates } from "
|
|
5
|
+
import { handleApiCall, outputJson, parseJson } from "../runHandler.js";
|
|
6
|
+
import { copyDirectory, listTemplates } from "../templateUtils.js";
|
|
7
7
|
const require = createRequire(import.meta.url);
|
|
8
8
|
export function registerWorkerCommands(parent, getApi) {
|
|
9
9
|
const worker = parent
|
|
@@ -32,29 +32,37 @@ export function registerWorkerCommands(parent, getApi) {
|
|
|
32
32
|
.command("create")
|
|
33
33
|
.description("Create a new worker slot")
|
|
34
34
|
.requiredOption("--name <name>", "Display name for the worker")
|
|
35
|
-
.requiredOption("--slug <slug>", "URL slug (must be
|
|
35
|
+
.requiredOption("--slug <slug>", "URL slug (must be unique within your workspace; the public host is suffixed with your workspace UUID prefix)")
|
|
36
36
|
.option("--folder-uuid <uuid>", "Optional folder UUID")
|
|
37
|
+
.option("--triggers <json>", 'Cron triggers, e.g. \'[{"type":"cron","name":"Sync","cron":"@every 1h","path":"/cron/sync","method":"POST"}]\'')
|
|
37
38
|
.action(async (opts) => {
|
|
38
39
|
const api = getApi();
|
|
39
40
|
const result = await handleApiCall(() => api.hosting.worker.create({
|
|
40
41
|
name: opts.name,
|
|
41
42
|
slug: opts.slug,
|
|
42
43
|
folderUuid: opts.folderUuid,
|
|
44
|
+
triggers: opts.triggers !== undefined
|
|
45
|
+
? parseJson(opts.triggers, "--triggers")
|
|
46
|
+
: undefined,
|
|
43
47
|
}));
|
|
44
48
|
outputJson(result);
|
|
45
49
|
});
|
|
46
50
|
worker
|
|
47
51
|
.command("update")
|
|
48
|
-
.description("Update an existing worker (name, folder)")
|
|
52
|
+
.description("Update an existing worker (name, folder, triggers)")
|
|
49
53
|
.requiredOption("--uuid <uuid>", "Worker UUID")
|
|
50
54
|
.option("--name <name>", "New display name")
|
|
51
55
|
.option("--folder-uuid <uuid>", "New folder UUID (pass 'null' to move to the workspace root)")
|
|
56
|
+
.option("--triggers <json>", "Full replacement list of cron triggers (pass '[]' to remove all)")
|
|
52
57
|
.action(async (opts) => {
|
|
53
58
|
const api = getApi();
|
|
54
59
|
const result = await handleApiCall(() => api.hosting.worker.update({
|
|
55
60
|
uuid: opts.uuid,
|
|
56
61
|
name: opts.name,
|
|
57
62
|
folderUuid: opts.folderUuid === "null" ? null : opts.folderUuid,
|
|
63
|
+
triggers: opts.triggers !== undefined
|
|
64
|
+
? parseJson(opts.triggers, "--triggers")
|
|
65
|
+
: undefined,
|
|
58
66
|
}));
|
|
59
67
|
outputJson(result);
|
|
60
68
|
});
|
|
@@ -128,8 +136,7 @@ export function registerWorkerCommands(parent, getApi) {
|
|
|
128
136
|
`cd ${path.relative(process.cwd(), targetDir)}`,
|
|
129
137
|
"npm install",
|
|
130
138
|
`cargo-ai hosting worker create --name '${workerName}' --slug '<your-slug>'`,
|
|
131
|
-
"
|
|
132
|
-
"cargo-ai hosting deployment create --worker-uuid <workerUuid> --source ./dist",
|
|
139
|
+
"cargo-ai hosting deployment create --worker-uuid <workerUuid> --source ./",
|
|
133
140
|
"cargo-ai hosting deployment promote --uuid <deploymentUuid>",
|
|
134
141
|
];
|
|
135
142
|
if (opts.template === "custom-integration") {
|
|
@@ -18,14 +18,18 @@ export declare const colors: {
|
|
|
18
18
|
export declare function outputJson(value: unknown): void;
|
|
19
19
|
export declare function info(message: string): void;
|
|
20
20
|
export declare function success(message: string): void;
|
|
21
|
+
export declare function confirm(question: string): Promise<boolean>;
|
|
21
22
|
type FailWithOpts = {
|
|
22
23
|
code?: ExitCode;
|
|
23
24
|
extra?: Record<string, unknown>;
|
|
24
25
|
};
|
|
25
26
|
export declare function failWith(message: string, opts?: FailWithOpts): never;
|
|
26
|
-
export
|
|
27
|
+
export type Spinner = {
|
|
28
|
+
update: (message: string) => void;
|
|
29
|
+
log: (line: string) => void;
|
|
27
30
|
stop: () => void;
|
|
28
31
|
};
|
|
32
|
+
export declare function startSpinner(message: string): Spinner;
|
|
29
33
|
export declare function parseJson(value: string, optionName: string): any;
|
|
30
34
|
type HandleApiCallOpts = {
|
|
31
35
|
spinner?: string | false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runHandler.d.ts","sourceRoot":"","sources":["../../src/commands/runHandler.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,SAAS;;;;;;;CAOZ,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC;AAclE,eAAO,MAAM,MAAM;aACR,MAAM,KAAG,MAAM;eACb,MAAM,KAAG,MAAM;gBACd,MAAM,KAAG,MAAM;cACjB,MAAM,KAAG,MAAM;aAChB,MAAM,KAAG,MAAM;cACd,MAAM,KAAG,MAAM;CAC1B,CAAC;AAEF,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAM/C;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED,KAAK,YAAY,GAAG;IAClB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC,CAAC;AAEF,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,KAAK,CAyBpE;AAKD,
|
|
1
|
+
{"version":3,"file":"runHandler.d.ts","sourceRoot":"","sources":["../../src/commands/runHandler.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,SAAS;;;;;;;CAOZ,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC;AAclE,eAAO,MAAM,MAAM;aACR,MAAM,KAAG,MAAM;eACb,MAAM,KAAG,MAAM;gBACd,MAAM,KAAG,MAAM;cACjB,MAAM,KAAG,MAAM;aAChB,MAAM,KAAG,MAAM;cACd,MAAM,KAAG,MAAM;CAC1B,CAAC;AAEF,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAM/C;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAID,wBAAsB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAUhE;AAED,KAAK,YAAY,GAAG;IAClB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC,CAAC;AAEF,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,KAAK,CAyBpE;AAKD,MAAM,MAAM,OAAO,GAAG;IAEpB,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAGlC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB,CAAC;AAEF,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CA6CrD;AAGD,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,GAAG,CAQhE;AAKD,KAAK,iBAAiB,GAAG;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CAC1B,CAAC;AAEF,wBAAsB,aAAa,CAAC,CAAC,EACnC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,IAAI,CAAC,EAAE,iBAAiB,GACvB,OAAO,CAAC,CAAC,CAAC,CAiDZ;AAmFD,wBAAsB,oBAAoB,CAAC,CAAC,EAC1C,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC3B,SAAS,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,EAChC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,CAAC,CAAC,CASZ;AAED,wBAAsB,sBAAsB,CAAC,CAAC,EAC5C,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC3B,SAAS,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,EAChC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,CAAC,CAAC,CASZ;AAED,wBAAsB,wBAAwB,CAAC,CAAC,EAC9C,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC3B,aAAa,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,EACvD,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,CAAC,CAAC,CAWZ"}
|
|
@@ -37,6 +37,21 @@ export function info(message) {
|
|
|
37
37
|
export function success(message) {
|
|
38
38
|
console.error(`${colors.green("✓")} ${message}`);
|
|
39
39
|
}
|
|
40
|
+
// Ask a yes/no question on stderr (stdout stays reserved for JSON). Resolves
|
|
41
|
+
// false on a non-TTY stdin (callers should require an explicit --yes there).
|
|
42
|
+
export async function confirm(question) {
|
|
43
|
+
if (process.stdin.isTTY !== true)
|
|
44
|
+
return false;
|
|
45
|
+
const { createInterface } = await import("node:readline/promises");
|
|
46
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
47
|
+
try {
|
|
48
|
+
const answer = await rl.question(`${question} [y/N] `);
|
|
49
|
+
return /^y(es)?$/i.test(answer.trim());
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
rl.close();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
40
55
|
export function failWith(message, opts) {
|
|
41
56
|
const code = opts !== undefined && opts.code !== undefined
|
|
42
57
|
? opts.code
|
|
@@ -64,20 +79,37 @@ const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧",
|
|
|
64
79
|
const SPINNER_INTERVAL_MS = 80;
|
|
65
80
|
export function startSpinner(message) {
|
|
66
81
|
if (process.stderr.isTTY !== true) {
|
|
82
|
+
// Non-TTY (CI, piped): no animation — just emit each line so progress is
|
|
83
|
+
// still visible in logs.
|
|
67
84
|
process.stderr.write(`${message}\n`);
|
|
68
|
-
return {
|
|
85
|
+
return {
|
|
86
|
+
update: (m) => process.stderr.write(`${m}\n`),
|
|
87
|
+
log: (line) => process.stderr.write(`${line}\n`),
|
|
88
|
+
stop: () => undefined,
|
|
89
|
+
};
|
|
69
90
|
}
|
|
70
91
|
process.stderr.write("\x1B[?25l");
|
|
92
|
+
let current = message;
|
|
71
93
|
let frame = 0;
|
|
72
|
-
const
|
|
94
|
+
const render = () => {
|
|
73
95
|
const ch = SPINNER_FRAMES[frame % SPINNER_FRAMES.length];
|
|
74
96
|
if (ch !== undefined) {
|
|
75
|
-
|
|
97
|
+
// \x1B[K clears any stale characters when the message shrinks.
|
|
98
|
+
process.stderr.write(`\r\x1B[K${colors.cyan(ch)} ${colors.dim(current)}`);
|
|
76
99
|
}
|
|
77
100
|
frame += 1;
|
|
78
|
-
}
|
|
101
|
+
};
|
|
102
|
+
const id = setInterval(render, SPINNER_INTERVAL_MS);
|
|
79
103
|
let stopped = false;
|
|
80
104
|
return {
|
|
105
|
+
update: (m) => {
|
|
106
|
+
current = m;
|
|
107
|
+
},
|
|
108
|
+
log: (line) => {
|
|
109
|
+
// Wipe the spinner line, drop a permanent line, let the next frame redraw
|
|
110
|
+
// the spinner beneath it.
|
|
111
|
+
process.stderr.write(`\r\x1B[K${line}\n`);
|
|
112
|
+
},
|
|
81
113
|
stop: () => {
|
|
82
114
|
if (stopped) {
|
|
83
115
|
return;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templateUtils.d.ts","sourceRoot":"","sources":["../../src/commands/templateUtils.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,aAAa,SAAgB,MAAM,KAAG,QAAQ,MAAM,EAAE,CAUlE,CAAC;AAEF,eAAO,MAAM,aAAa,QACnB,MAAM,QACL,MAAM,UACJ;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EAAE,KACrC,QAAQ,IAAI,CAmBd,CAAC"}
|
package/build/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
+
import { registerCommands as registerCdkCommands } from "@cargo-ai/cdk/cli";
|
|
3
4
|
import { Command } from "commander";
|
|
4
5
|
import { createApi } from "./api.js";
|
|
5
6
|
import { registerAiCommands } from "./commands/ai/index.js";
|
|
@@ -72,6 +73,9 @@ registerSystemOfRecordIntegrationCommands(program, getApi);
|
|
|
72
73
|
registerUserManagementCommands(program, getApi);
|
|
73
74
|
registerAiCommands(program, getApi);
|
|
74
75
|
registerHostingCommands(program, getApi);
|
|
76
|
+
registerCdkCommands(program
|
|
77
|
+
.command("cdk")
|
|
78
|
+
.description("Cargo CDK — define resources in code and deploy them (plan/deploy)"), getApi);
|
|
75
79
|
program
|
|
76
80
|
.parseAsync()
|
|
77
81
|
.then(() => maybeNotifyUpdate(version))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cargo-ai/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.27",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"description": "Command-line interface for the Cargo API",
|
|
@@ -29,9 +29,10 @@
|
|
|
29
29
|
"format:check": "prettier --check ."
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@cargo-ai/api": "^1.0.
|
|
33
|
-
"@cargo-ai/app-sdk": "^1.0.
|
|
34
|
-
"@cargo-ai/
|
|
32
|
+
"@cargo-ai/api": "^1.0.37",
|
|
33
|
+
"@cargo-ai/app-sdk": "^1.0.3",
|
|
34
|
+
"@cargo-ai/cdk": "*",
|
|
35
|
+
"@cargo-ai/worker-sdk": "^1.0.6",
|
|
35
36
|
"commander": "^12.1.0",
|
|
36
37
|
"http-proxy-agent": "^9.1.0",
|
|
37
38
|
"https-proxy-agent": "^9.1.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"templateUtils.d.ts","sourceRoot":"","sources":["../../../src/commands/hosting/templateUtils.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,aAAa,SAAgB,MAAM,KAAG,QAAQ,MAAM,EAAE,CAUlE,CAAC;AAEF,eAAO,MAAM,aAAa,QACnB,MAAM,QACL,MAAM,UACJ;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EAAE,KACrC,QAAQ,IAAI,CAmBd,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/workflow/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAMxC,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,GAAG,GAChB,IAAI,CAMN"}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { registerSyncCommand } from "./sync.js";
|
|
2
|
-
// SDK deploy lives under `cargo-ai orchestration release deploy-draft --file
|
|
3
|
-
// <path>` (alongside the raw-JSON deploy path), so this group only ships the
|
|
4
|
-
// `sync` developer tool today.
|
|
5
|
-
export function registerWorkflowCommands(parent, getApi) {
|
|
6
|
-
const workflow = parent
|
|
7
|
-
.command("workflow")
|
|
8
|
-
.description("Workflow SDK developer tools (type sync, codegen)");
|
|
9
|
-
registerSyncCommand(workflow, getApi);
|
|
10
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Fixed input shape of every agent node. Matches the SDK's `AgentInput`
|
|
3
|
-
* type and the engine's `AiUtils.agentConfig` schema.
|
|
4
|
-
*/
|
|
5
|
-
export declare const AGENT_INPUT_TYPE_SRC: string;
|
|
6
|
-
/**
|
|
7
|
-
* Print an integration action's JSON Schema config as a TS input type.
|
|
8
|
-
* Returns `"Record<string, unknown>"` when the schema is missing or not an
|
|
9
|
-
* object schema we can render.
|
|
10
|
-
*/
|
|
11
|
-
export declare function printJsonSchemaInput(schema: unknown): string;
|
|
12
|
-
/**
|
|
13
|
-
* Print a tool release's `formFields` as a TS input type. Returns
|
|
14
|
-
* `undefined` when the fields can't be interpreted (caller falls back to
|
|
15
|
-
* `Record<string, unknown>`).
|
|
16
|
-
*/
|
|
17
|
-
export declare function printFormFieldsInput(formFields: unknown): string | undefined;
|
|
18
|
-
//# sourceMappingURL=inputTypes.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"inputTypes.d.ts","sourceRoot":"","sources":["../../../src/commands/workflow/inputTypes.ts"],"names":[],"mappings":"AAgCA;;;GAGG;AACH,eAAO,MAAM,oBAAoB,QAE+D,CAAC;AAEjG;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAM5D;AAgGD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAU5E"}
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
// TypeScript input-type printers for `cargo-ai workflow sync` codegen.
|
|
2
|
-
//
|
|
3
|
-
// Three sources of truth, three printers:
|
|
4
|
-
// - Integration actions expose their config as JSON Schema on
|
|
5
|
-
// `connection.integration.list` (`action.config.schema`).
|
|
6
|
-
// - Workspace tools expose their input as `formFields` on the tool
|
|
7
|
-
// workflow's deployed release (`orchestration.release.getDeployed`).
|
|
8
|
-
// - Agent nodes accept a single global shape (`{ prompt, output? }`) —
|
|
9
|
-
// the engine validates every agent node against `AiUtils.agentConfig`.
|
|
10
|
-
//
|
|
11
|
-
// All printers run in "input mode": each top-level property is widened to
|
|
12
|
-
// `Ref<T> | T` so callers can pass either a builder Ref or a literal.
|
|
13
|
-
// Anything unrecognized falls back to `unknown` — codegen must never abort
|
|
14
|
-
// on a schema edge case.
|
|
15
|
-
//
|
|
16
|
-
// Mirrors `packages/workflow-sdk/scripts/lib/jsonSchemaToTs.ts` (which is a
|
|
17
|
-
// build-time-only script the CLI can't import).
|
|
18
|
-
/**
|
|
19
|
-
* Fixed input shape of every agent node. Matches the SDK's `AgentInput`
|
|
20
|
-
* type and the engine's `AiUtils.agentConfig` schema.
|
|
21
|
-
*/
|
|
22
|
-
export const AGENT_INPUT_TYPE_SRC = `{ prompt: Ref<string> | string; ` +
|
|
23
|
-
`output?: { type: "default" | "text" | "jsonSchema"; jsonSchema?: Record<string, unknown> } }`;
|
|
24
|
-
/**
|
|
25
|
-
* Print an integration action's JSON Schema config as a TS input type.
|
|
26
|
-
* Returns `"Record<string, unknown>"` when the schema is missing or not an
|
|
27
|
-
* object schema we can render.
|
|
28
|
-
*/
|
|
29
|
-
export function printJsonSchemaInput(schema) {
|
|
30
|
-
if (schema === null || typeof schema !== "object") {
|
|
31
|
-
return "Record<string, unknown>";
|
|
32
|
-
}
|
|
33
|
-
const printed = printSchema(schema, true);
|
|
34
|
-
return printed === "unknown" ? "Record<string, unknown>" : printed;
|
|
35
|
-
}
|
|
36
|
-
function printSchema(schema, topLevel) {
|
|
37
|
-
if (Array.isArray(schema.type)) {
|
|
38
|
-
return uniqueUnion(schema.type.map((t) => printPrimitive(t, schema, false)));
|
|
39
|
-
}
|
|
40
|
-
if (schema.enum !== undefined && schema.enum.length > 0) {
|
|
41
|
-
return schema.enum.map((v) => JSON.stringify(v)).join(" | ");
|
|
42
|
-
}
|
|
43
|
-
if (schema.const !== undefined) {
|
|
44
|
-
return JSON.stringify(schema.const);
|
|
45
|
-
}
|
|
46
|
-
if (schema.oneOf !== undefined && schema.oneOf.length > 0) {
|
|
47
|
-
return uniqueUnion(schema.oneOf.map((s) => printSchema(s, false)));
|
|
48
|
-
}
|
|
49
|
-
if (schema.anyOf !== undefined && schema.anyOf.length > 0) {
|
|
50
|
-
return uniqueUnion(schema.anyOf.map((s) => printSchema(s, false)));
|
|
51
|
-
}
|
|
52
|
-
if (schema.allOf !== undefined && schema.allOf.length > 0) {
|
|
53
|
-
// Conservative: render the head rather than a TS intersection.
|
|
54
|
-
return printSchema(schema.allOf[0], topLevel);
|
|
55
|
-
}
|
|
56
|
-
if (typeof schema.type !== "string")
|
|
57
|
-
return "unknown";
|
|
58
|
-
return printPrimitive(schema.type, schema, topLevel);
|
|
59
|
-
}
|
|
60
|
-
function printPrimitive(type, schema, topLevel) {
|
|
61
|
-
switch (type) {
|
|
62
|
-
case "string":
|
|
63
|
-
return "string";
|
|
64
|
-
case "number":
|
|
65
|
-
case "integer":
|
|
66
|
-
return "number";
|
|
67
|
-
case "boolean":
|
|
68
|
-
return "boolean";
|
|
69
|
-
case "null":
|
|
70
|
-
return "null";
|
|
71
|
-
case "array":
|
|
72
|
-
return printArray(schema);
|
|
73
|
-
case "object":
|
|
74
|
-
return printObject(schema, topLevel);
|
|
75
|
-
default:
|
|
76
|
-
return "unknown";
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
function printArray(schema) {
|
|
80
|
-
if (schema.items === undefined)
|
|
81
|
-
return "unknown[]";
|
|
82
|
-
if (Array.isArray(schema.items)) {
|
|
83
|
-
return `[${schema.items.map((s) => printSchema(s, false)).join(", ")}]`;
|
|
84
|
-
}
|
|
85
|
-
return `Array<${printSchema(schema.items, false)}>`;
|
|
86
|
-
}
|
|
87
|
-
function printObject(schema, topLevel) {
|
|
88
|
-
const props = schema.properties;
|
|
89
|
-
if (props === undefined) {
|
|
90
|
-
if (typeof schema.additionalProperties === "object" &&
|
|
91
|
-
schema.additionalProperties !== null) {
|
|
92
|
-
return `Record<string, ${printSchema(schema.additionalProperties, false)}>`;
|
|
93
|
-
}
|
|
94
|
-
return "Record<string, unknown>";
|
|
95
|
-
}
|
|
96
|
-
const required = new Set(Array.isArray(schema.required) ? schema.required : []);
|
|
97
|
-
const fields = [];
|
|
98
|
-
for (const [key, child] of Object.entries(props)) {
|
|
99
|
-
const inner = printSchema(child, false);
|
|
100
|
-
// Only top-level properties are Ref-widened; nested values are plain.
|
|
101
|
-
const value = topLevel ? `Ref<${inner}> | ${inner}` : inner;
|
|
102
|
-
const opt = required.has(key) ? "" : "?";
|
|
103
|
-
fields.push(`${tsKey(key)}${opt}: ${value}`);
|
|
104
|
-
}
|
|
105
|
-
if (fields.length === 0)
|
|
106
|
-
return "Record<string, never>";
|
|
107
|
-
return `{ ${fields.join("; ")} }`;
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Print a tool release's `formFields` as a TS input type. Returns
|
|
111
|
-
* `undefined` when the fields can't be interpreted (caller falls back to
|
|
112
|
-
* `Record<string, unknown>`).
|
|
113
|
-
*/
|
|
114
|
-
export function printFormFieldsInput(formFields) {
|
|
115
|
-
if (!Array.isArray(formFields))
|
|
116
|
-
return undefined;
|
|
117
|
-
const fields = formFields.filter(isFormFieldLike);
|
|
118
|
-
if (fields.length === 0)
|
|
119
|
-
return "Record<string, never>";
|
|
120
|
-
const rendered = fields.map((f) => {
|
|
121
|
-
const inner = formFieldToTs(f);
|
|
122
|
-
const opt = f.isRequired === true ? "" : "?";
|
|
123
|
-
return `${tsKey(f.slug)}${opt}: Ref<${inner}> | ${inner}`;
|
|
124
|
-
});
|
|
125
|
-
return `{ ${rendered.join("; ")} }`;
|
|
126
|
-
}
|
|
127
|
-
function isFormFieldLike(value) {
|
|
128
|
-
if (value === null || typeof value !== "object")
|
|
129
|
-
return false;
|
|
130
|
-
const v = value;
|
|
131
|
-
return typeof v["slug"] === "string" && typeof v["kind"] === "string";
|
|
132
|
-
}
|
|
133
|
-
function formFieldToTs(field) {
|
|
134
|
-
switch (field.kind) {
|
|
135
|
-
case "string":
|
|
136
|
-
case "date":
|
|
137
|
-
return "string";
|
|
138
|
-
case "number":
|
|
139
|
-
return "number";
|
|
140
|
-
case "boolean":
|
|
141
|
-
return "boolean";
|
|
142
|
-
case "enum": {
|
|
143
|
-
const values = Array.isArray(field.enum)
|
|
144
|
-
? field.enum.filter((v) => typeof v === "string")
|
|
145
|
-
: [];
|
|
146
|
-
if (values.length === 0)
|
|
147
|
-
return "string";
|
|
148
|
-
return values.map((v) => JSON.stringify(v)).join(" | ");
|
|
149
|
-
}
|
|
150
|
-
case "array": {
|
|
151
|
-
const nested = Array.isArray(field.fields)
|
|
152
|
-
? field.fields.filter(isFormFieldLike)
|
|
153
|
-
: [];
|
|
154
|
-
if (nested.length === 0)
|
|
155
|
-
return "unknown[]";
|
|
156
|
-
const inner = nested
|
|
157
|
-
.map((f) => {
|
|
158
|
-
const opt = f.isRequired === true ? "" : "?";
|
|
159
|
-
return `${tsKey(f.slug)}${opt}: ${formFieldToTs(f)}`;
|
|
160
|
-
})
|
|
161
|
-
.join("; ");
|
|
162
|
-
return `Array<{ ${inner} }>`;
|
|
163
|
-
}
|
|
164
|
-
case "any":
|
|
165
|
-
default:
|
|
166
|
-
return "unknown";
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
function uniqueUnion(parts) {
|
|
170
|
-
const dedup = Array.from(new Set(parts));
|
|
171
|
-
return dedup.length === 1 ? dedup[0] : dedup.join(" | ");
|
|
172
|
-
}
|
|
173
|
-
function tsKey(key) {
|
|
174
|
-
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
|
|
175
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../../src/commands/workflow/sync.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAgGxC,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,GAAG,IAAI,CAsD5E"}
|
|
@@ -1,454 +0,0 @@
|
|
|
1
|
-
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { resolve } from "node:path";
|
|
3
|
-
import { handleApiCall, info, success } from "../runHandler.js";
|
|
4
|
-
import { AGENT_INPUT_TYPE_SRC, printFormFieldsInput, printJsonSchemaInput, } from "./inputTypes.js";
|
|
5
|
-
// Composite natives surfaced via dedicated SDK syntax / scope helpers —
|
|
6
|
-
// excluded so we don't double-register them. Mirrors the codegen exclusion
|
|
7
|
-
// list in `packages/workflow-sdk/scripts/generateNativeIntegration.ts`
|
|
8
|
-
// (everything in the `logic` category gets dedicated syntax instead of
|
|
9
|
-
// going through `native.<slug>`).
|
|
10
|
-
const COMPOSITE_NATIVE = new Set([
|
|
11
|
-
"start",
|
|
12
|
-
"end",
|
|
13
|
-
"agent",
|
|
14
|
-
"balance",
|
|
15
|
-
"branch",
|
|
16
|
-
"delay",
|
|
17
|
-
"filter",
|
|
18
|
-
"group",
|
|
19
|
-
"humanReview",
|
|
20
|
-
"memory",
|
|
21
|
-
"split",
|
|
22
|
-
"switch",
|
|
23
|
-
"tool",
|
|
24
|
-
"variables",
|
|
25
|
-
]);
|
|
26
|
-
// Native actions the SDK already ships typed declarations + registrations
|
|
27
|
-
// for (platform-level, identical in every workspace) — skipped so the
|
|
28
|
-
// synced `.d.ts` doesn't redeclare `Native` properties with conflicting
|
|
29
|
-
// (looser) types. Source of truth lives in
|
|
30
|
-
// `packages/workflow-sdk/src/native/manifest.generated.ts`.
|
|
31
|
-
const BUNDLED_NATIVE = new Set([
|
|
32
|
-
"note",
|
|
33
|
-
"python",
|
|
34
|
-
"scoring",
|
|
35
|
-
"script",
|
|
36
|
-
]);
|
|
37
|
-
export function registerSyncCommand(parent, getApi) {
|
|
38
|
-
parent
|
|
39
|
-
.command("sync")
|
|
40
|
-
.description("Generate per-workspace TypeScript types + eager registration for the Cargo Workflow SDK")
|
|
41
|
-
.option("--out <dir>", "Output directory for generated files (default: .cargo-ai)", ".cargo-ai")
|
|
42
|
-
.option("--cwd <dir>", "Working directory the --out path is resolved against (default: process.cwd())")
|
|
43
|
-
.action(async (opts) => {
|
|
44
|
-
const api = getApi();
|
|
45
|
-
const baseDir = opts.cwd !== undefined ? opts.cwd : process.cwd();
|
|
46
|
-
const outDir = resolve(baseDir, opts.out);
|
|
47
|
-
info(`Fetching workspace surface…`);
|
|
48
|
-
const payload = await fetchWorkspaceSurface(api);
|
|
49
|
-
mkdirSync(outDir, { recursive: true });
|
|
50
|
-
const typesPath = resolve(outDir, "cargo-types.d.ts");
|
|
51
|
-
writeFileSync(typesPath, renderTypes(payload));
|
|
52
|
-
success(`Wrote ${relativeToBase(baseDir, typesPath)}`);
|
|
53
|
-
const eagerPath = resolve(outDir, "cargo-register.ts");
|
|
54
|
-
writeFileSync(eagerPath, renderEagerRegistration(payload));
|
|
55
|
-
success(`Wrote ${relativeToBase(baseDir, eagerPath)}`);
|
|
56
|
-
info(``);
|
|
57
|
-
info([
|
|
58
|
-
`Next steps:`,
|
|
59
|
-
` 1. Add ${relativeToBase(baseDir, outDir)} to your tsconfig include`,
|
|
60
|
-
` so the .d.ts is picked up.`,
|
|
61
|
-
` 2. Add \`import "./${relativeToBase(baseDir, eagerPath).replace(/\.ts$/, ".js")}";\``,
|
|
62
|
-
` to your project's entry file (or before any defineWorkflow call)`,
|
|
63
|
-
` so the workspace's integrations / tools / agents register at runtime.`,
|
|
64
|
-
].join("\n"));
|
|
65
|
-
info(``);
|
|
66
|
-
info(`Synced ${String(payload.integrations.length)} integration(s), ${String(payload.tools.length)} tool(s), ${String(payload.agents.length)} agent(s), ${String(payload.native.length)} native action(s).`);
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
async function fetchWorkspaceSurface(api) {
|
|
70
|
-
const integrationsResult = await handleApiCall(() => api.connection.integration.list({}));
|
|
71
|
-
const nativeIntegrationResult = await handleApiCall(() => api.connection.nativeIntegration.get());
|
|
72
|
-
const orchestrationToolsResult = await handleApiCall(() => api.orchestration.tool.all());
|
|
73
|
-
const aiAgentsResult = await handleApiCall(() => api.ai.agent.all());
|
|
74
|
-
const integrations = collectIntegrations(integrationsResult.integrations);
|
|
75
|
-
const tools = collectTools(orchestrationToolsResult.tools);
|
|
76
|
-
await resolveToolInputTypes(api, tools);
|
|
77
|
-
const agents = collectAgents(aiAgentsResult);
|
|
78
|
-
const native = collectNative(nativeIntegrationResult.nativeIntegration.actions);
|
|
79
|
-
return { integrations, tools, agents, native };
|
|
80
|
-
}
|
|
81
|
-
function collectIntegrations(integrations) {
|
|
82
|
-
const out = [];
|
|
83
|
-
for (const integration of integrations) {
|
|
84
|
-
const actionEntries = Object.entries(integration.actions ?? {}).sort(([a], [b]) => a.localeCompare(b));
|
|
85
|
-
if (actionEntries.length === 0)
|
|
86
|
-
continue;
|
|
87
|
-
const actions = actionEntries.map(([slug, raw]) => {
|
|
88
|
-
const meta = raw;
|
|
89
|
-
return {
|
|
90
|
-
slug,
|
|
91
|
-
name: trimOrUndefined(meta.name),
|
|
92
|
-
description: trimOrUndefined(meta.description),
|
|
93
|
-
inputTypeSrc: printJsonSchemaInput(meta.config?.schema),
|
|
94
|
-
};
|
|
95
|
-
});
|
|
96
|
-
out.push({
|
|
97
|
-
slug: integration.slug,
|
|
98
|
-
name: trimOrUndefined(integration.name),
|
|
99
|
-
description: trimOrUndefined(integration.description),
|
|
100
|
-
actions,
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
out.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
104
|
-
return out;
|
|
105
|
-
}
|
|
106
|
-
function collectTools(workspaceTools) {
|
|
107
|
-
const out = [];
|
|
108
|
-
const used = new Set();
|
|
109
|
-
for (const t of workspaceTools) {
|
|
110
|
-
out.push({
|
|
111
|
-
slug: pickSlug(t.name, t.uuid, used),
|
|
112
|
-
name: t.name,
|
|
113
|
-
description: trimOrUndefined(t.description),
|
|
114
|
-
uuid: t.uuid,
|
|
115
|
-
workflowUuid: t.workflowUuid,
|
|
116
|
-
updatedAt: dateToIso(t.updatedAt),
|
|
117
|
-
inputTypeSrc: "Record<string, unknown>",
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
out.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
121
|
-
return out;
|
|
122
|
-
}
|
|
123
|
-
// The tools list endpoint doesn't carry input schemas — they live on each
|
|
124
|
-
// tool workflow's deployed release as `formFields`. Resolve them with one
|
|
125
|
-
// `release.getDeployed` call per tool (bounded concurrency). A tool without
|
|
126
|
-
// a deployed release (or whose release fetch fails) keeps the
|
|
127
|
-
// `Record<string, unknown>` fallback; sync must not abort over one tool.
|
|
128
|
-
const TOOL_RELEASE_CONCURRENCY = 5;
|
|
129
|
-
async function resolveToolInputTypes(api, tools) {
|
|
130
|
-
const queue = [...tools];
|
|
131
|
-
const workers = Array.from({ length: Math.min(TOOL_RELEASE_CONCURRENCY, queue.length) }, async () => {
|
|
132
|
-
for (;;) {
|
|
133
|
-
const tool = queue.shift();
|
|
134
|
-
if (tool === undefined)
|
|
135
|
-
return;
|
|
136
|
-
try {
|
|
137
|
-
const result = await api.orchestration.release.getDeployed({
|
|
138
|
-
workflowUuid: tool.workflowUuid,
|
|
139
|
-
});
|
|
140
|
-
const formFields = result.release?.formFields;
|
|
141
|
-
const printed = printFormFieldsInput(formFields);
|
|
142
|
-
if (printed !== undefined) {
|
|
143
|
-
tool.inputTypeSrc = printed;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
catch {
|
|
147
|
-
// Not deployed / not accessible — keep the loose fallback type.
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
await Promise.all(workers);
|
|
152
|
-
}
|
|
153
|
-
function collectAgents(workspaceAgents) {
|
|
154
|
-
const out = [];
|
|
155
|
-
const used = new Set();
|
|
156
|
-
const allAgents = isAgentsResult(workspaceAgents)
|
|
157
|
-
? workspaceAgents.agents
|
|
158
|
-
: [];
|
|
159
|
-
for (const a of allAgents) {
|
|
160
|
-
out.push({
|
|
161
|
-
slug: pickSlug(a.name, a.uuid, used),
|
|
162
|
-
name: a.name,
|
|
163
|
-
description: trimOrUndefined(a.description),
|
|
164
|
-
uuid: a.uuid,
|
|
165
|
-
updatedAt: dateToIso(a.updatedAt),
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
out.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
169
|
-
return out;
|
|
170
|
-
}
|
|
171
|
-
function isAgentsResult(value) {
|
|
172
|
-
if (value === null || typeof value !== "object")
|
|
173
|
-
return false;
|
|
174
|
-
const v = value;
|
|
175
|
-
return Array.isArray(v.agents);
|
|
176
|
-
}
|
|
177
|
-
function collectNative(actions) {
|
|
178
|
-
const out = [];
|
|
179
|
-
for (const [slug, meta] of Object.entries(actions)) {
|
|
180
|
-
if (COMPOSITE_NATIVE.has(slug))
|
|
181
|
-
continue;
|
|
182
|
-
if (BUNDLED_NATIVE.has(slug))
|
|
183
|
-
continue;
|
|
184
|
-
const name = meta.name.length > 0 ? meta.name : slug;
|
|
185
|
-
out.push({
|
|
186
|
-
slug,
|
|
187
|
-
name,
|
|
188
|
-
description: trimOrUndefined(meta.description),
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
out.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
192
|
-
return out;
|
|
193
|
-
}
|
|
194
|
-
function trimOrUndefined(value) {
|
|
195
|
-
if (typeof value !== "string")
|
|
196
|
-
return undefined;
|
|
197
|
-
const trimmed = value.trim();
|
|
198
|
-
return trimmed.length > 0 ? trimmed : undefined;
|
|
199
|
-
}
|
|
200
|
-
function dateToIso(value) {
|
|
201
|
-
if (value === null || value === undefined)
|
|
202
|
-
return undefined;
|
|
203
|
-
if (value instanceof Date) {
|
|
204
|
-
return Number.isNaN(value.getTime()) ? undefined : value.toISOString();
|
|
205
|
-
}
|
|
206
|
-
if (typeof value === "string") {
|
|
207
|
-
const date = new Date(value);
|
|
208
|
-
return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
|
|
209
|
-
}
|
|
210
|
-
return undefined;
|
|
211
|
-
}
|
|
212
|
-
// Slugify `name` to a JS identifier; append a UUID-based suffix on collision
|
|
213
|
-
// (or when the name slugifies to nothing). `used` is mutated.
|
|
214
|
-
function pickSlug(name, uuid, used) {
|
|
215
|
-
const base = slugify(name);
|
|
216
|
-
if (base.length > 0 && !used.has(base)) {
|
|
217
|
-
used.add(base);
|
|
218
|
-
return base;
|
|
219
|
-
}
|
|
220
|
-
const suffix = uuid.replace(/-/g, "_");
|
|
221
|
-
const fallback = base.length > 0 ? `${base}_${suffix}` : `_${suffix}`;
|
|
222
|
-
used.add(fallback);
|
|
223
|
-
return fallback;
|
|
224
|
-
}
|
|
225
|
-
function slugify(name) {
|
|
226
|
-
const normalized = name
|
|
227
|
-
.normalize("NFKD")
|
|
228
|
-
.replace(/[^\w]+/g, "_")
|
|
229
|
-
.replace(/^_+|_+$/g, "")
|
|
230
|
-
.toLowerCase();
|
|
231
|
-
if (normalized.length === 0)
|
|
232
|
-
return "";
|
|
233
|
-
// JS identifiers can't start with a digit.
|
|
234
|
-
return /^\d/.test(normalized) ? `_${normalized}` : normalized;
|
|
235
|
-
}
|
|
236
|
-
const GENERATED_AT = new Date().toISOString();
|
|
237
|
-
const HEADER = `// THIS FILE IS GENERATED by \`cargo-ai workflow sync\`. Do not edit by hand.
|
|
238
|
-
// Re-run the command to refresh after adding/removing workspace integrations,
|
|
239
|
-
// tools, or agents.
|
|
240
|
-
// Last regenerated: ${GENERATED_AT}
|
|
241
|
-
|
|
242
|
-
`;
|
|
243
|
-
function renderTypes(payload) {
|
|
244
|
-
const lines = [];
|
|
245
|
-
lines.push(`declare module "@cargo-ai/workflow-sdk" {`);
|
|
246
|
-
if (payload.integrations.length > 0) {
|
|
247
|
-
lines.push(` interface Integrations {`);
|
|
248
|
-
for (const c of payload.integrations) {
|
|
249
|
-
const intDoc = formatJsDoc({
|
|
250
|
-
name: c.name ?? c.slug,
|
|
251
|
-
description: c.description,
|
|
252
|
-
updatedAt: GENERATED_AT,
|
|
253
|
-
}, " ");
|
|
254
|
-
if (intDoc !== "")
|
|
255
|
-
lines.push(intDoc.trimEnd());
|
|
256
|
-
lines.push(` ${jsonKey(c.slug)}: {`);
|
|
257
|
-
for (const action of c.actions) {
|
|
258
|
-
const actionDoc = formatJsDoc({
|
|
259
|
-
name: action.name,
|
|
260
|
-
description: action.description,
|
|
261
|
-
category: c.slug,
|
|
262
|
-
updatedAt: GENERATED_AT,
|
|
263
|
-
}, " ");
|
|
264
|
-
if (actionDoc !== "")
|
|
265
|
-
lines.push(actionDoc.trimEnd());
|
|
266
|
-
lines.push(` ${jsonKey(action.slug)}: IntegrationAction<${action.inputTypeSrc}, Record<string, unknown>>;`);
|
|
267
|
-
}
|
|
268
|
-
lines.push(` };`);
|
|
269
|
-
}
|
|
270
|
-
lines.push(` }`);
|
|
271
|
-
}
|
|
272
|
-
if (payload.tools.length > 0) {
|
|
273
|
-
lines.push(` interface Tools {`);
|
|
274
|
-
for (const t of payload.tools) {
|
|
275
|
-
const doc = formatJsDoc({
|
|
276
|
-
name: t.name,
|
|
277
|
-
description: t.description,
|
|
278
|
-
updatedAt: t.updatedAt ?? GENERATED_AT,
|
|
279
|
-
}, " ");
|
|
280
|
-
if (doc !== "")
|
|
281
|
-
lines.push(doc.trimEnd());
|
|
282
|
-
lines.push(` ${jsonKey(t.slug)}: ToolFn<${t.inputTypeSrc}, Record<string, unknown>>;`);
|
|
283
|
-
}
|
|
284
|
-
lines.push(` }`);
|
|
285
|
-
}
|
|
286
|
-
if (payload.agents.length > 0) {
|
|
287
|
-
lines.push(` interface Agents {`);
|
|
288
|
-
for (const a of payload.agents) {
|
|
289
|
-
const doc = formatJsDoc({
|
|
290
|
-
name: a.name,
|
|
291
|
-
description: a.description,
|
|
292
|
-
updatedAt: a.updatedAt ?? GENERATED_AT,
|
|
293
|
-
}, " ");
|
|
294
|
-
if (doc !== "")
|
|
295
|
-
lines.push(doc.trimEnd());
|
|
296
|
-
lines.push(` ${jsonKey(a.slug)}: AgentFn<${AGENT_INPUT_TYPE_SRC}, AgentDefaultOutput>;`);
|
|
297
|
-
}
|
|
298
|
-
lines.push(` }`);
|
|
299
|
-
}
|
|
300
|
-
if (payload.native.length > 0) {
|
|
301
|
-
lines.push(` interface Native {`);
|
|
302
|
-
for (const n of payload.native) {
|
|
303
|
-
const doc = formatJsDoc({
|
|
304
|
-
name: n.name,
|
|
305
|
-
description: n.description,
|
|
306
|
-
updatedAt: GENERATED_AT,
|
|
307
|
-
}, " ");
|
|
308
|
-
if (doc !== "")
|
|
309
|
-
lines.push(doc.trimEnd());
|
|
310
|
-
lines.push(` ${jsonKey(n.slug)}: NativeAction<Record<string, unknown>, Record<string, unknown>>;`);
|
|
311
|
-
}
|
|
312
|
-
lines.push(` }`);
|
|
313
|
-
}
|
|
314
|
-
lines.push(`}`);
|
|
315
|
-
lines.push(``);
|
|
316
|
-
const body = lines.join("\n");
|
|
317
|
-
// `Ref` only appears when at least one entry has a typed input, so import
|
|
318
|
-
// it conditionally to keep the generated .d.ts free of unused imports.
|
|
319
|
-
const imports = [
|
|
320
|
-
"AgentDefaultOutput",
|
|
321
|
-
"AgentFn",
|
|
322
|
-
"IntegrationAction",
|
|
323
|
-
"NativeAction",
|
|
324
|
-
...(body.includes("Ref<") ? ["Ref"] : []),
|
|
325
|
-
"ToolFn",
|
|
326
|
-
];
|
|
327
|
-
const header = [
|
|
328
|
-
HEADER.trim(),
|
|
329
|
-
`import type {`,
|
|
330
|
-
...imports.map((name) => ` ${name},`),
|
|
331
|
-
`} from "@cargo-ai/workflow-sdk";`,
|
|
332
|
-
``,
|
|
333
|
-
].join("\n");
|
|
334
|
-
return `${header}\n${body}`;
|
|
335
|
-
}
|
|
336
|
-
function renderEagerRegistration(payload) {
|
|
337
|
-
const lines = [];
|
|
338
|
-
lines.push(HEADER.trim());
|
|
339
|
-
const imports = [];
|
|
340
|
-
if (payload.integrations.length > 0)
|
|
341
|
-
imports.push("registerIntegration");
|
|
342
|
-
if (payload.tools.length > 0)
|
|
343
|
-
imports.push("registerTool");
|
|
344
|
-
if (payload.agents.length > 0)
|
|
345
|
-
imports.push("registerAgent");
|
|
346
|
-
if (payload.native.length > 0)
|
|
347
|
-
imports.push("registerNative");
|
|
348
|
-
if (imports.length === 0) {
|
|
349
|
-
lines.push(``);
|
|
350
|
-
lines.push(`// Workspace surface is empty (no custom integrations / tools / agents / natives).`);
|
|
351
|
-
lines.push(``);
|
|
352
|
-
return lines.join("\n");
|
|
353
|
-
}
|
|
354
|
-
lines.push(`import { ${imports.join(", ")} } from "@cargo-ai/workflow-sdk";`);
|
|
355
|
-
lines.push(``);
|
|
356
|
-
for (const c of payload.integrations) {
|
|
357
|
-
lines.push(`registerIntegration(${JSON.stringify(c.slug)}, {`);
|
|
358
|
-
for (const action of c.actions) {
|
|
359
|
-
const displayName = action.name !== undefined && action.name.length > 0
|
|
360
|
-
? action.name
|
|
361
|
-
: `${capitalize(c.slug)} ${humanize(action.slug)}`;
|
|
362
|
-
lines.push(` ${jsonKey(action.slug)}: { name: ${JSON.stringify(displayName)} },`);
|
|
363
|
-
}
|
|
364
|
-
lines.push(`});`);
|
|
365
|
-
}
|
|
366
|
-
for (const t of payload.tools) {
|
|
367
|
-
lines.push(`registerTool(${JSON.stringify(t.slug)}, { toolUuid: ${JSON.stringify(t.uuid)} }, ${JSON.stringify(t.name)});`);
|
|
368
|
-
}
|
|
369
|
-
for (const a of payload.agents) {
|
|
370
|
-
lines.push(`registerAgent(${JSON.stringify(a.slug)}, { agentUuid: ${JSON.stringify(a.uuid)} }, ${JSON.stringify(a.name)});`);
|
|
371
|
-
}
|
|
372
|
-
for (const n of payload.native) {
|
|
373
|
-
lines.push(`registerNative(${JSON.stringify(n.slug)}, ${JSON.stringify(n.name)});`);
|
|
374
|
-
}
|
|
375
|
-
lines.push(``);
|
|
376
|
-
return lines.join("\n");
|
|
377
|
-
}
|
|
378
|
-
function jsonKey(key) {
|
|
379
|
-
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
|
|
380
|
-
}
|
|
381
|
-
// Render a JSDoc block above a generated declaration so editors surface the
|
|
382
|
-
// entry's metadata on hover. Returns "" when there's nothing to document.
|
|
383
|
-
function formatJsDoc(doc, indent) {
|
|
384
|
-
const body = [];
|
|
385
|
-
if (doc.name !== undefined && doc.name.trim().length > 0) {
|
|
386
|
-
body.push(doc.name.trim());
|
|
387
|
-
}
|
|
388
|
-
if (doc.description !== undefined && doc.description.trim().length > 0) {
|
|
389
|
-
if (body.length > 0)
|
|
390
|
-
body.push("");
|
|
391
|
-
for (const line of doc.description.split("\n")) {
|
|
392
|
-
body.push(line.trimEnd());
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
const tags = [];
|
|
396
|
-
if (doc.category !== undefined) {
|
|
397
|
-
const cats = Array.isArray(doc.category)
|
|
398
|
-
? doc.category.join(", ")
|
|
399
|
-
: String(doc.category);
|
|
400
|
-
if (cats.trim().length > 0) {
|
|
401
|
-
tags.push(`@category ${cats}`);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
if (doc.author !== undefined && doc.author.trim().length > 0) {
|
|
405
|
-
tags.push(`@author ${doc.author.trim()}`);
|
|
406
|
-
}
|
|
407
|
-
if (doc.version !== undefined && String(doc.version).length > 0) {
|
|
408
|
-
tags.push(`@version ${String(doc.version)}`);
|
|
409
|
-
}
|
|
410
|
-
if (doc.updatedAt !== undefined && doc.updatedAt !== null) {
|
|
411
|
-
const date = doc.updatedAt instanceof Date ? doc.updatedAt : new Date(doc.updatedAt);
|
|
412
|
-
if (!Number.isNaN(date.getTime())) {
|
|
413
|
-
tags.push(`@updated ${date.toISOString().slice(0, 10)}`);
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
if (doc.see !== undefined && doc.see.trim().length > 0) {
|
|
417
|
-
tags.push(`@see ${doc.see.trim()}`);
|
|
418
|
-
}
|
|
419
|
-
if (body.length > 0 && tags.length > 0) {
|
|
420
|
-
body.push("");
|
|
421
|
-
}
|
|
422
|
-
body.push(...tags);
|
|
423
|
-
if (body.length === 0)
|
|
424
|
-
return "";
|
|
425
|
-
const out = [`${indent}/**`];
|
|
426
|
-
for (const line of body) {
|
|
427
|
-
const safe = line.replace(/\*\//g, "*\\/");
|
|
428
|
-
out.push(safe.length > 0 ? `${indent} * ${safe}` : `${indent} *`);
|
|
429
|
-
}
|
|
430
|
-
out.push(`${indent} */`);
|
|
431
|
-
return `${out.join("\n")}\n`;
|
|
432
|
-
}
|
|
433
|
-
function capitalize(s) {
|
|
434
|
-
if (s.length === 0)
|
|
435
|
-
return s;
|
|
436
|
-
const first = s[0];
|
|
437
|
-
if (first === undefined)
|
|
438
|
-
return s;
|
|
439
|
-
return first.toUpperCase() + s.slice(1);
|
|
440
|
-
}
|
|
441
|
-
function humanize(slug) {
|
|
442
|
-
return slug
|
|
443
|
-
.replace(/([A-Z])/g, " $1")
|
|
444
|
-
.replace(/[_-]+/g, " ")
|
|
445
|
-
.trim()
|
|
446
|
-
.toLowerCase();
|
|
447
|
-
}
|
|
448
|
-
function relativeToBase(base, path) {
|
|
449
|
-
if (path.startsWith(base)) {
|
|
450
|
-
const rel = path.slice(base.length);
|
|
451
|
-
return rel.startsWith("/") ? rel.slice(1) : rel;
|
|
452
|
-
}
|
|
453
|
-
return path;
|
|
454
|
-
}
|
|
File without changes
|
|
File without changes
|