@cargo-ai/cli 1.0.67 → 1.0.70
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 +0 -1
- package/build/commands/auth/index.d.ts.map +1 -1
- package/build/commands/auth/index.js +28 -2
- package/build/commands/doctor.d.ts.map +1 -1
- package/build/commands/doctor.js +91 -32
- package/build/commands/orchestration/run.d.ts.map +1 -1
- package/build/commands/orchestration/run.js +30 -5
- package/build/index.js +74 -10
- package/build/setupIssues.d.ts +37 -0
- package/build/setupIssues.d.ts.map +1 -0
- package/build/setupIssues.js +93 -0
- package/build/updateNotifier.d.ts +14 -5
- package/build/updateNotifier.d.ts.map +1 -1
- package/build/updateNotifier.js +32 -16
- package/package.json +2 -2
- package/build/commands/init.d.ts +0 -4
- package/build/commands/init.d.ts.map +0 -1
- package/build/commands/init.js +0 -11
package/README.md
CHANGED
|
@@ -142,7 +142,6 @@ cargo-ai orchestration workflow --help
|
|
|
142
142
|
|
|
143
143
|
| Domain | Description | Example commands |
|
|
144
144
|
| ------------------------ | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
145
|
-
| **init** | Workspace initialisation (user, workspace, datasets, etc.) | `cargo-ai init` |
|
|
146
145
|
| **orchestration** | Workflows, plays, runs, batches, tools, templates | `cargo-ai orchestration workflow list`, `cargo-ai orchestration run list --workflow-uuid <uuid>` |
|
|
147
146
|
| **workspaceManagement** | Workspaces, users, tokens, roles, folders | `cargo-ai workspaceManagement workspaces list`, `cargo-ai workspaceManagement token list` |
|
|
148
147
|
| **storage** | Datasets, models, relationships, runs, records | `cargo-ai storage dataset list`, `cargo-ai storage model list` |
|
|
@@ -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;AAGjD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAExC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AA0BxD,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,
|
|
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;AA0BxD,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,CAsIN;AAiGD,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,4 +1,4 @@
|
|
|
1
|
-
import { revokeSession } from "@cargo-ai/cdk/cli";
|
|
1
|
+
import { declarePrompt, revokeSession } from "@cargo-ai/cdk/cli";
|
|
2
2
|
import { Option } from "commander";
|
|
3
3
|
import { AGENT_SKILLS_NEXT_STEP } from "../../agentSkills.js";
|
|
4
4
|
import { getConfig } from "../../config.js";
|
|
@@ -10,7 +10,7 @@ import { selectLoginChannel } from "./loginChannel.js";
|
|
|
10
10
|
import { runOAuthDeviceFlow } from "./oauth.js";
|
|
11
11
|
import { DEFAULT_BASE_URL, establishSession, resolveBaseUrl, } from "./session.js";
|
|
12
12
|
export function registerAuthCommands(program, getApi) {
|
|
13
|
-
program
|
|
13
|
+
const login = program
|
|
14
14
|
.command("login")
|
|
15
15
|
.description("Sign in to Cargo with an emailed code (--email), via browser (--oauth), or with an existing API token (--token)")
|
|
16
16
|
.option("--email <email>", "Sign in with a code emailed to this address; creates the account on first use")
|
|
@@ -53,6 +53,32 @@ Environment variables (CARGO_API_TOKEN, CARGO_WORKSPACE_UUID, CARGO_BASE_URL) ta
|
|
|
53
53
|
.action(async (opts) => {
|
|
54
54
|
await runLogin(opts);
|
|
55
55
|
});
|
|
56
|
+
// Commander can say an option is required but not that exactly one of three
|
|
57
|
+
// must be present, so the palette saw a command with no inputs, ran it bare,
|
|
58
|
+
// and got the handler's own usage error back. The choice is declared here,
|
|
59
|
+
// beside the options it chooses between.
|
|
60
|
+
declarePrompt(login, {
|
|
61
|
+
message: "how do you want to sign in?",
|
|
62
|
+
choices: [
|
|
63
|
+
{
|
|
64
|
+
label: "Browser sign-in",
|
|
65
|
+
hint: "opens a browser; no code to copy",
|
|
66
|
+
flag: "--oauth",
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
label: "Emailed code",
|
|
70
|
+
hint: "no browser needed; signs you up on first use",
|
|
71
|
+
flag: "--email",
|
|
72
|
+
value: { name: "email", hint: "you@company.com" },
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
label: "Existing API token",
|
|
76
|
+
hint: "for CI, or a token you already hold",
|
|
77
|
+
flag: "--token",
|
|
78
|
+
value: { name: "token", hint: "", secret: true },
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
});
|
|
56
82
|
program
|
|
57
83
|
.command("logout")
|
|
58
84
|
.description("Sign out: revoke the saved credential and remove it locally")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiKzC,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,MAAM,GACrB,IAAI,CAyEN"}
|
package/build/commands/doctor.js
CHANGED
|
@@ -1,45 +1,29 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as os from "node:os";
|
|
3
|
-
import * as path from "node:path";
|
|
4
1
|
import { determineIfIsFetcherError } from "@cargo-ai/api";
|
|
2
|
+
import { formatSetupIssues } from "@cargo-ai/cdk/cli";
|
|
5
3
|
import { createApi } from "../api.js";
|
|
6
4
|
import { getConfig } from "../config.js";
|
|
7
5
|
import { getCredentialsPath } from "../credentials.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const envDir = process.env["CARGO_SKILLS_DIR"];
|
|
14
|
-
const candidates = [];
|
|
15
|
-
if (envDir !== undefined && envDir.length > 0) {
|
|
16
|
-
candidates.push(path.join(envDir, "cargo", "cli-version"));
|
|
17
|
-
}
|
|
18
|
-
candidates.push(path.join(home, ".claude", "skills", "cargo", "cli-version"), path.join(home, ".claude", "plugins", "marketplaces", "cargo", "cargo", "cli-version"), path.join(home, ".openclaw", "skills", "cargo", "cli-version"));
|
|
19
|
-
return candidates;
|
|
20
|
-
}
|
|
21
|
-
function readSkillsPin() {
|
|
22
|
-
for (const candidate of getSkillsPinCandidates()) {
|
|
23
|
-
try {
|
|
24
|
-
const raw = fs.readFileSync(candidate, "utf8").trim();
|
|
25
|
-
if (SEMVER_PATTERN.test(raw)) {
|
|
26
|
-
return { version: raw, path: candidate };
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
// Missing or unreadable — try the next candidate.
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return undefined;
|
|
34
|
-
}
|
|
6
|
+
import { collectSetupIssues, readSkillsPin } from "../setupIssues.js";
|
|
7
|
+
import { fetchLatestVersion, isOutdated, UPDATE_COMMAND } from "../version.js";
|
|
8
|
+
import { colors, ExitCodes, outputJson } from "./runHandler.js";
|
|
9
|
+
const LOGIN_COMMAND = "cargo-ai login --oauth";
|
|
10
|
+
const STATUS_PAGE = "https://status.getcargo.io";
|
|
35
11
|
function buildCliCheck(currentVersion, latestVersion) {
|
|
36
12
|
if (latestVersion === undefined) {
|
|
37
13
|
return { current: currentVersion, latest: null, upToDate: null };
|
|
38
14
|
}
|
|
15
|
+
if (isOutdated(currentVersion, latestVersion)) {
|
|
16
|
+
return {
|
|
17
|
+
current: currentVersion,
|
|
18
|
+
latest: latestVersion,
|
|
19
|
+
upToDate: false,
|
|
20
|
+
fix: UPDATE_COMMAND,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
39
23
|
return {
|
|
40
24
|
current: currentVersion,
|
|
41
25
|
latest: latestVersion,
|
|
42
|
-
upToDate:
|
|
26
|
+
upToDate: true,
|
|
43
27
|
};
|
|
44
28
|
}
|
|
45
29
|
function buildSkillsPinCheck(currentVersion) {
|
|
@@ -47,11 +31,25 @@ function buildSkillsPinCheck(currentVersion) {
|
|
|
47
31
|
if (pin === undefined) {
|
|
48
32
|
return { found: false };
|
|
49
33
|
}
|
|
34
|
+
if (pin.version === currentVersion) {
|
|
35
|
+
return {
|
|
36
|
+
found: true,
|
|
37
|
+
pinned: pin.version,
|
|
38
|
+
path: pin.path,
|
|
39
|
+
matchesCli: true,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
50
42
|
return {
|
|
51
43
|
found: true,
|
|
52
44
|
pinned: pin.version,
|
|
53
45
|
path: pin.path,
|
|
54
|
-
matchesCli:
|
|
46
|
+
matchesCli: false,
|
|
47
|
+
// Whichever side is behind is the side to update: an older CLI cannot run
|
|
48
|
+
// what the skills ask of it, and older skills describe commands this CLI
|
|
49
|
+
// has moved on from.
|
|
50
|
+
fix: isOutdated(currentVersion, pin.version)
|
|
51
|
+
? UPDATE_COMMAND
|
|
52
|
+
: "npx skills add getcargohq/cargo-skills",
|
|
55
53
|
};
|
|
56
54
|
}
|
|
57
55
|
async function checkApi(config) {
|
|
@@ -63,6 +61,7 @@ async function checkApi(config) {
|
|
|
63
61
|
status: "no-credentials",
|
|
64
62
|
message: 'Not authenticated. Run "cargo-ai login --email <email>" (no browser needed) or "cargo-ai login --oauth".',
|
|
65
63
|
},
|
|
64
|
+
fix: LOGIN_COMMAND,
|
|
66
65
|
},
|
|
67
66
|
exitCode: ExitCodes.NotAuthenticated,
|
|
68
67
|
};
|
|
@@ -105,6 +104,9 @@ async function checkApi(config) {
|
|
|
105
104
|
status,
|
|
106
105
|
message: error instanceof Error ? error.message : String(error),
|
|
107
106
|
},
|
|
107
|
+
// A rejected or mis-scoped credential is fixed by signing in again;
|
|
108
|
+
// anything else is the service, not this machine, so point at status.
|
|
109
|
+
fix: status === 401 || status === 403 ? LOGIN_COMMAND : STATUS_PAGE,
|
|
108
110
|
},
|
|
109
111
|
exitCode,
|
|
110
112
|
};
|
|
@@ -146,10 +148,67 @@ Examples:
|
|
|
146
148
|
credentialsFile: getCredentialsPath(),
|
|
147
149
|
};
|
|
148
150
|
const { check: api, exitCode } = await checkApi(config);
|
|
151
|
+
// One list, most severe first, each entry carrying its own fix — the same
|
|
152
|
+
// shape the pre-command header renders, so a reader who has seen the
|
|
153
|
+
// amber line recognises it here rather than learning a second format.
|
|
154
|
+
const offline = collectSetupIssues({
|
|
155
|
+
currentVersion,
|
|
156
|
+
latestVersion,
|
|
157
|
+
});
|
|
158
|
+
const apiIssue = describeApiIssue(api);
|
|
159
|
+
const issues = apiIssue === undefined
|
|
160
|
+
? offline
|
|
161
|
+
: [
|
|
162
|
+
apiIssue,
|
|
163
|
+
// The live call is authoritative about the credential, so it
|
|
164
|
+
// replaces the offline "is there a file" guess.
|
|
165
|
+
...offline.filter((issue) => issue.key !== "not-signed-in"),
|
|
166
|
+
];
|
|
149
167
|
outputJson({
|
|
150
168
|
ok: exitCode === ExitCodes.Success,
|
|
169
|
+
issues,
|
|
151
170
|
checks: { cli, skillsPin, credentials, api },
|
|
152
171
|
});
|
|
172
|
+
printReport(issues, api);
|
|
153
173
|
process.exit(exitCode);
|
|
154
174
|
});
|
|
155
175
|
}
|
|
176
|
+
function describeApiIssue(api) {
|
|
177
|
+
if (api.ok === true) {
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
if (api.error.status === "no-credentials") {
|
|
181
|
+
return { key: "not-signed-in", label: "not signed in", fix: api.fix };
|
|
182
|
+
}
|
|
183
|
+
if (api.error.status === 401) {
|
|
184
|
+
return {
|
|
185
|
+
key: "credential-rejected",
|
|
186
|
+
label: "credential rejected",
|
|
187
|
+
fix: api.fix,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
if (api.error.status === 403) {
|
|
191
|
+
return {
|
|
192
|
+
key: "workspace-denied",
|
|
193
|
+
label: "no access to this workspace",
|
|
194
|
+
fix: api.fix,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
return { key: "api-unreachable", label: "API unreachable", fix: api.fix };
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* The human half. stdout stays the JSON contract scripts and agents parse, so
|
|
201
|
+
* this goes to stderr and only when someone is watching.
|
|
202
|
+
*/
|
|
203
|
+
function printReport(issues, api) {
|
|
204
|
+
if (process.stderr.isTTY !== true) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const issueLine = formatSetupIssues(issues);
|
|
208
|
+
if (issueLine !== undefined) {
|
|
209
|
+
process.stderr.write(`${issueLine}\n`);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const where = api.ok === true ? ` ${colors.dim("·")} ${api.workspace.name}` : "";
|
|
213
|
+
process.stderr.write(`${colors.green("✓")} Setup looks healthy${where}\n`);
|
|
214
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/commands/orchestration/run.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/commands/orchestration/run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAgBxC,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,GAAG,IAAI,CA+rB5E"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { declarePrompt } from "@cargo-ai/cdk/cli";
|
|
1
2
|
import { parseBooleanOption } from "../../utils/booleanOption.js";
|
|
2
3
|
import { loadCompiledWorkflow, resolveCompiledWorkflowFromState, } from "../../utils/loadCompiledWorkflow.js";
|
|
3
4
|
import { ExitCodes, failWith, handleApiCall, info, outputJson, parseJson, pollRunUntilFinished, } from "../runHandler.js";
|
|
@@ -5,10 +6,10 @@ export function registerRunCommands(parent, getApi) {
|
|
|
5
6
|
const run = parent
|
|
6
7
|
.command("run")
|
|
7
8
|
.description("Manage workflow runs (list, get, create, cancel, count, download)");
|
|
8
|
-
run
|
|
9
|
+
const listRuns = run
|
|
9
10
|
.command("list")
|
|
10
11
|
.description("List runs")
|
|
11
|
-
.
|
|
12
|
+
.option("--workflow-uuid <uuid>", "Workflow UUID (string). Omit to list the runs of every play and tool, which the API allows only alongside --created-after inside a 30-day window")
|
|
12
13
|
.option("--batch-uuid <uuid>", "Filter by batch UUID (string)")
|
|
13
14
|
.option("--release-uuid <uuid>", "Filter by release UUID (string)")
|
|
14
15
|
.option("--statuses <list>", "Filter by statuses (comma-separated: idle,pending,running,success,error,cancelling,cancelled,skipped)")
|
|
@@ -32,7 +33,8 @@ export function registerRunCommands(parent, getApi) {
|
|
|
32
33
|
Examples:
|
|
33
34
|
$ cargo-ai orchestration run list --workflow-uuid 550e8400-...
|
|
34
35
|
$ cargo-ai orchestration run list --workflow-uuid 550e8400-... --statuses running,pending --limit 50
|
|
35
|
-
$ cargo-ai orchestration run list --workflow-uuid 550e8400-... --created-after 2025-01-01T00:00:00Z
|
|
36
|
+
$ cargo-ai orchestration run list --workflow-uuid 550e8400-... --created-after 2025-01-01T00:00:00Z
|
|
37
|
+
$ cargo-ai orchestration run list --created-after 2025-01-01T00:00:00Z # every play and tool`)
|
|
36
38
|
.action(async (opts) => {
|
|
37
39
|
const api = getApi();
|
|
38
40
|
const result = await handleApiCall(() => api.orchestration.run.list({
|
|
@@ -141,10 +143,10 @@ Examples:
|
|
|
141
143
|
await handleApiCall(() => api.orchestration.run.cancel(payload));
|
|
142
144
|
outputJson({ ok: true });
|
|
143
145
|
});
|
|
144
|
-
run
|
|
146
|
+
const countRuns = run
|
|
145
147
|
.command("count")
|
|
146
148
|
.description("Count runs matching the given filters")
|
|
147
|
-
.
|
|
149
|
+
.option("--workflow-uuid <uuid>", "Workflow UUID (string). Omit to count the runs of every play and tool, which the API allows only alongside --created-after inside a 30-day window")
|
|
148
150
|
.option("--batch-uuid <uuid>", "Filter by batch UUID (string)")
|
|
149
151
|
.option("--release-uuid <uuid>", "Filter by release UUID (string)")
|
|
150
152
|
.option("--statuses <list>", "Filter by statuses (comma-separated: idle,pending,running,success,error,cancelling,cancelled,skipped)")
|
|
@@ -188,6 +190,29 @@ Examples:
|
|
|
188
190
|
}));
|
|
189
191
|
outputJson(result);
|
|
190
192
|
});
|
|
193
|
+
// Both of these take the option or go without it, and going without it is
|
|
194
|
+
// only legal alongside --created-after. Commander cannot express that, so the
|
|
195
|
+
// palette would offer a command that fails on the server's own message. The
|
|
196
|
+
// choice is declared here instead, beside the option it chooses between.
|
|
197
|
+
for (const command of [listRuns, countRuns]) {
|
|
198
|
+
declarePrompt(command, {
|
|
199
|
+
message: "which runs?",
|
|
200
|
+
choices: [
|
|
201
|
+
{
|
|
202
|
+
label: "One workflow",
|
|
203
|
+
hint: "a single play or tool",
|
|
204
|
+
flag: "--workflow-uuid",
|
|
205
|
+
value: { name: "workflow-uuid", hint: "550e8400-e29b-41d4-a716-…" },
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
label: "Every play and tool",
|
|
209
|
+
hint: "needs a start date; the API allows at most a 30-day window",
|
|
210
|
+
flag: "--created-after",
|
|
211
|
+
value: { name: "created-after", hint: "2026-08-01T00:00:00Z" },
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
});
|
|
215
|
+
}
|
|
191
216
|
run
|
|
192
217
|
.command("download")
|
|
193
218
|
.description("Download run results as JSON")
|
package/build/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
-
import { registerCommands as registerCdkCommands,
|
|
3
|
+
import { BRAND, canRunPalette, fetchCreditsLeft, fetchWorkspaceName, formatWorkspaceSuffix, printSetupIssues, registerCommands as registerCdkCommands, runPalette, SessionExpiredError, } from "@cargo-ai/cdk/cli";
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { AGENT_SKILLS_HINT } from "./agentSkills.js";
|
|
6
6
|
import { createApi } from "./api.js";
|
|
@@ -13,7 +13,6 @@ import { registerContextCommands } from "./commands/context/index.js";
|
|
|
13
13
|
import { registerDoctorCommand } from "./commands/doctor.js";
|
|
14
14
|
import { registerExpressionCommands } from "./commands/expression/index.js";
|
|
15
15
|
import { registerHostingCommands } from "./commands/hosting/index.js";
|
|
16
|
-
import { registerInitCommand } from "./commands/init.js";
|
|
17
16
|
import { registerMailboxManagementCommands } from "./commands/mailboxManagement/index.js";
|
|
18
17
|
import { registerMcpCommand } from "./commands/mcp.js";
|
|
19
18
|
import { registerObservabilityCommands } from "./commands/observability/index.js";
|
|
@@ -27,7 +26,8 @@ import { registerUserManagementCommands } from "./commands/userManagement/index.
|
|
|
27
26
|
import { registerVersionCommand } from "./commands/version.js";
|
|
28
27
|
import { registerWorkspaceManagementCommands } from "./commands/workspaceManagement/index.js";
|
|
29
28
|
import { getConfig } from "./config.js";
|
|
30
|
-
import {
|
|
29
|
+
import { collectSetupIssues, isSignedIn } from "./setupIssues.js";
|
|
30
|
+
import { cachedLatestVersion, maybeRefreshUpdateCache, } from "./updateNotifier.js";
|
|
31
31
|
const require = createRequire(import.meta.url);
|
|
32
32
|
const { version } = require("../package.json");
|
|
33
33
|
const program = new Command();
|
|
@@ -71,6 +71,40 @@ Exit codes:
|
|
|
71
71
|
|
|
72
72
|
Run "cargo-ai <command> --help" for details on a specific command group.
|
|
73
73
|
Run "cargo-ai <command> <subcommand> --help" for details on a subcommand.`);
|
|
74
|
+
// Commands that are themselves the fix, or that already say all of this
|
|
75
|
+
// better: the header would either duplicate their output or nag someone who is
|
|
76
|
+
// mid-repair.
|
|
77
|
+
const SETUP_HEADER_SKIP = ["login", "logout", "doctor", "version", "mcp"];
|
|
78
|
+
/**
|
|
79
|
+
* Say what is wrong before the command runs rather than after it fails, and
|
|
80
|
+
* name the fix. Every check behind this is a file read (see `setupIssues.ts`)
|
|
81
|
+
* and the line only renders for a TTY, so scripts and agents pay nothing.
|
|
82
|
+
*
|
|
83
|
+
* A one-shot CLI has no session to show this once per, so someone who has
|
|
84
|
+
* decided to live with a mismatch would otherwise see it before every command
|
|
85
|
+
* forever. Give them the same opt-out the update notifier has.
|
|
86
|
+
*/
|
|
87
|
+
program.hook("preAction", (_program, action) => {
|
|
88
|
+
const suppressed = process.env["CARGO_NO_SETUP_HEADER"];
|
|
89
|
+
if (suppressed === "1" || suppressed === "true") {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (SETUP_HEADER_SKIP.includes(rootCommandName(action))) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
printSetupIssues(collectSetupIssues({
|
|
96
|
+
currentVersion: version,
|
|
97
|
+
latestVersion: cachedLatestVersion(),
|
|
98
|
+
}));
|
|
99
|
+
});
|
|
100
|
+
/** The top-level group a subcommand belongs to (`connection`, not `list`). */
|
|
101
|
+
function rootCommandName(command) {
|
|
102
|
+
const parent = command.parent;
|
|
103
|
+
if (parent === null || parent.parent === null) {
|
|
104
|
+
return command.name();
|
|
105
|
+
}
|
|
106
|
+
return rootCommandName(parent);
|
|
107
|
+
}
|
|
74
108
|
const getApi = () => {
|
|
75
109
|
const { baseUrl, getAccessToken, workspaceUuid } = getConfig();
|
|
76
110
|
if (getAccessToken === undefined) {
|
|
@@ -81,7 +115,6 @@ const getApi = () => {
|
|
|
81
115
|
registerAuthCommands(program, getApi);
|
|
82
116
|
registerVersionCommand(program, version);
|
|
83
117
|
registerDoctorCommand(program, version);
|
|
84
|
-
registerInitCommand(program, getApi);
|
|
85
118
|
registerObservabilityCommands(program, getApi);
|
|
86
119
|
registerOrchestrationCommands(program, getApi);
|
|
87
120
|
registerWorkspaceManagementCommands(program, getApi);
|
|
@@ -102,12 +135,43 @@ registerMcpCommand(program, getApi);
|
|
|
102
135
|
registerCdkCommands(program
|
|
103
136
|
.command("cdk")
|
|
104
137
|
.description("Cargo CDK — define resources in code and deploy them (plan/deploy)"), getApi);
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
138
|
+
// Bare `cargo-ai` in a terminal opens the palette instead of printing the help
|
|
139
|
+
// wall: twenty command groups is a list to navigate, not one to read. Anything
|
|
140
|
+
// non-interactive (a pipe, CI, an agent) keeps the help it has always had.
|
|
141
|
+
const bare = process.argv.slice(2).length === 0 && canRunPalette();
|
|
142
|
+
const start = bare ? openPalette() : program.parseAsync();
|
|
143
|
+
async function openPalette() {
|
|
144
|
+
const [title, latestVersion] = await Promise.all([
|
|
145
|
+
paletteTitle(),
|
|
146
|
+
maybeRefreshUpdateCache(),
|
|
147
|
+
]);
|
|
148
|
+
await runPalette({
|
|
149
|
+
program,
|
|
150
|
+
binPath: process.argv[1] === undefined ? "cargo-ai" : process.argv[1],
|
|
151
|
+
title,
|
|
152
|
+
issues: collectSetupIssues({ currentVersion: version, latestVersion }),
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* `🧱 cargo-ai 1.0.66 · Acme · 9,657.48 credits`. The two lookups behind the
|
|
157
|
+
* second half are the same best-effort, time-boxed ones `cargo-cdk` uses, so a
|
|
158
|
+
* bare invocation still opens promptly when the network does not answer — and
|
|
159
|
+
* they are skipped entirely when there is no credential to make them with.
|
|
160
|
+
*/
|
|
161
|
+
async function paletteTitle() {
|
|
162
|
+
const base = `${BRAND} cargo-ai ${version}`;
|
|
163
|
+
if (isSignedIn() === false) {
|
|
164
|
+
return base;
|
|
165
|
+
}
|
|
166
|
+
const api = getApi();
|
|
167
|
+
const [name, creditsLeft] = await Promise.all([
|
|
168
|
+
fetchWorkspaceName(api),
|
|
169
|
+
fetchCreditsLeft(api),
|
|
170
|
+
]);
|
|
171
|
+
return `${base}${formatWorkspaceSuffix(name, creditsLeft)}`;
|
|
172
|
+
}
|
|
173
|
+
start
|
|
174
|
+
.then(() => maybeRefreshUpdateCache())
|
|
111
175
|
.catch((err) => {
|
|
112
176
|
// An expired session is the ordinary way to lose authentication, so it has
|
|
113
177
|
// to carry the documented exit code rather than the generic one.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { SetupIssue } from "@cargo-ai/cdk/cli";
|
|
2
|
+
export type SkillsPin = {
|
|
3
|
+
version: string;
|
|
4
|
+
path: string;
|
|
5
|
+
};
|
|
6
|
+
export type SetupIssueInput = {
|
|
7
|
+
currentVersion: string;
|
|
8
|
+
/**
|
|
9
|
+
* A version from the update cache (or a live fetch). Passing one opts this
|
|
10
|
+
* caller in to reporting an outdated CLI, so the version sits with the
|
|
11
|
+
* other setup issues rather than as a footer after the command. The
|
|
12
|
+
* palette and the pre-command header pass the cache; `doctor` fetches live.
|
|
13
|
+
*/
|
|
14
|
+
latestVersion?: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* The two facts this reads off the machine. Injected so the rules — which side
|
|
18
|
+
* of a pin mismatch to update, when an outdated CLI is this line's business at
|
|
19
|
+
* all — can be tested without a home directory.
|
|
20
|
+
*/
|
|
21
|
+
export type SetupIssueDeps = {
|
|
22
|
+
isSignedIn: () => boolean;
|
|
23
|
+
readSkillsPin: () => SkillsPin | undefined;
|
|
24
|
+
};
|
|
25
|
+
export declare function collectSetupIssues(input: SetupIssueInput, deps?: SetupIssueDeps): SetupIssue[];
|
|
26
|
+
/**
|
|
27
|
+
* The CLI version the installed skills bundle was written against, from
|
|
28
|
+
* whichever agent's skills directory holds one.
|
|
29
|
+
*/
|
|
30
|
+
export declare function readSkillsPin(): SkillsPin | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Deliberately not `getConfig()`: that resolves the whole auth config and exits
|
|
33
|
+
* the process on a workspace conflict, which a header printed before every
|
|
34
|
+
* command must never do. Presence of a credential is all this line needs.
|
|
35
|
+
*/
|
|
36
|
+
export declare function isSignedIn(): boolean;
|
|
37
|
+
//# sourceMappingURL=setupIssues.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setupIssues.d.ts","sourceRoot":"","sources":["../src/setupIssues.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAWpD,MAAM,MAAM,SAAS,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1D,MAAM,MAAM,eAAe,GAAG;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,EAAE,MAAM,OAAO,CAAC;IAC1B,aAAa,EAAE,MAAM,SAAS,GAAG,SAAS,CAAC;CAC5C,CAAC;AAIF,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,eAAe,EACtB,IAAI,GAAE,cAA4B,GACjC,UAAU,EAAE,CAyCd;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,SAAS,GAAG,SAAS,CAcrD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAYpC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// What is wrong with this machine's Cargo setup, and the command that fixes
|
|
2
|
+
// each one.
|
|
3
|
+
//
|
|
4
|
+
// This runs before every command, so every check here is a small file read or
|
|
5
|
+
// cheaper — no network. `cargo-ai doctor` is the same list with the online
|
|
6
|
+
// checks added.
|
|
7
|
+
import * as fs from "node:fs";
|
|
8
|
+
import * as os from "node:os";
|
|
9
|
+
import * as path from "node:path";
|
|
10
|
+
import { loadCredentials } from "./credentials.js";
|
|
11
|
+
import { isOutdated, UPDATE_COMMAND } from "./version.js";
|
|
12
|
+
const SEMVER_PATTERN = /^\d+\.\d+\.\d+$/;
|
|
13
|
+
const SKILLS_UPDATE_COMMAND = "npx skills add getcargohq/cargo-skills";
|
|
14
|
+
const LOGIN_COMMAND = "cargo-ai login --oauth";
|
|
15
|
+
const machineDeps = { isSignedIn, readSkillsPin };
|
|
16
|
+
export function collectSetupIssues(input, deps = machineDeps) {
|
|
17
|
+
const issues = [];
|
|
18
|
+
if (deps.isSignedIn() === false) {
|
|
19
|
+
issues.push({
|
|
20
|
+
key: "not-signed-in",
|
|
21
|
+
label: "not signed in",
|
|
22
|
+
fix: LOGIN_COMMAND,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
const latest = input.latestVersion;
|
|
26
|
+
if (latest !== undefined &&
|
|
27
|
+
isOutdated(input.currentVersion, latest) === true) {
|
|
28
|
+
issues.push({
|
|
29
|
+
key: "cli-outdated",
|
|
30
|
+
label: `CLI ${input.currentVersion} → ${latest}`,
|
|
31
|
+
fix: UPDATE_COMMAND,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
const pin = deps.readSkillsPin();
|
|
35
|
+
if (pin !== undefined && pin.version !== input.currentVersion) {
|
|
36
|
+
// The pin records the CLI the installed skills were written against. Which
|
|
37
|
+
// side is behind decides the fix: an older CLI cannot run what the skills
|
|
38
|
+
// ask of it, and older skills describe commands this CLI has moved on from.
|
|
39
|
+
issues.push({
|
|
40
|
+
key: "skills-pin-mismatch",
|
|
41
|
+
label: `skills expect CLI ${pin.version}`,
|
|
42
|
+
fix: isOutdated(input.currentVersion, pin.version) === true
|
|
43
|
+
? UPDATE_COMMAND
|
|
44
|
+
: SKILLS_UPDATE_COMMAND,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return issues;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The CLI version the installed skills bundle was written against, from
|
|
51
|
+
* whichever agent's skills directory holds one.
|
|
52
|
+
*/
|
|
53
|
+
export function readSkillsPin() {
|
|
54
|
+
for (const candidate of skillsPinCandidates()) {
|
|
55
|
+
try {
|
|
56
|
+
const raw = fs.readFileSync(candidate, "utf8").trim();
|
|
57
|
+
if (SEMVER_PATTERN.test(raw)) {
|
|
58
|
+
return { version: raw, path: candidate };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Missing or unreadable — try the next candidate.
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Deliberately not `getConfig()`: that resolves the whole auth config and exits
|
|
69
|
+
* the process on a workspace conflict, which a header printed before every
|
|
70
|
+
* command must never do. Presence of a credential is all this line needs.
|
|
71
|
+
*/
|
|
72
|
+
export function isSignedIn() {
|
|
73
|
+
const token = process.env["CARGO_API_TOKEN"];
|
|
74
|
+
if (token !== undefined && token.trim() !== "") {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
return loadCredentials() !== undefined;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function skillsPinCandidates() {
|
|
85
|
+
const home = os.homedir();
|
|
86
|
+
const envDir = process.env["CARGO_SKILLS_DIR"];
|
|
87
|
+
const candidates = [];
|
|
88
|
+
if (envDir !== undefined && envDir.length > 0) {
|
|
89
|
+
candidates.push(path.join(envDir, "cargo", "cli-version"));
|
|
90
|
+
}
|
|
91
|
+
candidates.push(path.join(home, ".claude", "skills", "cargo", "cli-version"), path.join(home, ".claude", "plugins", "marketplaces", "cargo", "cargo", "cli-version"), path.join(home, ".openclaw", "skills", "cargo", "cli-version"));
|
|
92
|
+
return candidates;
|
|
93
|
+
}
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
|
|
2
|
+
* Latest version already sitting in the on-disk cache. No network. Undefined
|
|
3
|
+
* when the check is disabled or nothing has been fetched yet — the header
|
|
4
|
+
* then simply omits the outdated-CLI issue rather than guessing.
|
|
5
|
+
*/
|
|
6
|
+
export declare function cachedLatestVersion(): string | undefined;
|
|
7
|
+
/**
|
|
8
|
+
* Refresh the registry cache at most once per 24h. Returns the latest version
|
|
9
|
+
* we now know about, so a caller that is about to render setup issues (the
|
|
10
|
+
* palette) can put an outdated CLI in that list instead of printing a footer
|
|
11
|
+
* after the fact.
|
|
12
|
+
*
|
|
13
|
+
* Stays silent on non-TTY stderr (scripts/CI), when disabled via
|
|
14
|
+
* `CARGO_NO_UPDATE_NOTIFIER`, and on any network/parse failure — it must
|
|
6
15
|
* never block, delay, or break the command that triggered it.
|
|
7
16
|
*/
|
|
8
|
-
export declare function
|
|
17
|
+
export declare function maybeRefreshUpdateCache(): Promise<string | undefined>;
|
|
9
18
|
//# sourceMappingURL=updateNotifier.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updateNotifier.d.ts","sourceRoot":"","sources":["../src/updateNotifier.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"updateNotifier.d.ts","sourceRoot":"","sources":["../src/updateNotifier.ts"],"names":[],"mappings":"AAgBA;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,GAAG,SAAS,CAYxD;AAED;;;;;;;;;GASG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAwB3E"}
|
package/build/updateNotifier.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { fetchLatestVersion, isOutdated, readUpdateCache, UPDATE_COMMAND, writeUpdateCache, } from "./version.js";
|
|
1
|
+
import { fetchLatestVersion, readUpdateCache, writeUpdateCache, } from "./version.js";
|
|
3
2
|
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // once per day
|
|
4
3
|
const NOTIFY_FETCH_TIMEOUT_MS = 1500;
|
|
5
4
|
const isDisabled = () => {
|
|
@@ -7,18 +6,37 @@ const isDisabled = () => {
|
|
|
7
6
|
return (flag === "1" || flag === "true" || process.env["NO_UPDATE_NOTIFIER"] === "1");
|
|
8
7
|
};
|
|
9
8
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
|
|
9
|
+
* Latest version already sitting in the on-disk cache. No network. Undefined
|
|
10
|
+
* when the check is disabled or nothing has been fetched yet — the header
|
|
11
|
+
* then simply omits the outdated-CLI issue rather than guessing.
|
|
12
|
+
*/
|
|
13
|
+
export function cachedLatestVersion() {
|
|
14
|
+
if (isDisabled() === true) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
const cache = readUpdateCache();
|
|
18
|
+
if (cache === undefined) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
return cache.latestVersion;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Refresh the registry cache at most once per 24h. Returns the latest version
|
|
25
|
+
* we now know about, so a caller that is about to render setup issues (the
|
|
26
|
+
* palette) can put an outdated CLI in that list instead of printing a footer
|
|
27
|
+
* after the fact.
|
|
28
|
+
*
|
|
29
|
+
* Stays silent on non-TTY stderr (scripts/CI), when disabled via
|
|
30
|
+
* `CARGO_NO_UPDATE_NOTIFIER`, and on any network/parse failure — it must
|
|
14
31
|
* never block, delay, or break the command that triggered it.
|
|
15
32
|
*/
|
|
16
|
-
export async function
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (isDisabled())
|
|
21
|
-
return;
|
|
33
|
+
export async function maybeRefreshUpdateCache() {
|
|
34
|
+
if (process.stderr.isTTY !== true) {
|
|
35
|
+
return cachedLatestVersion();
|
|
36
|
+
}
|
|
37
|
+
if (isDisabled() === true) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
22
40
|
try {
|
|
23
41
|
let cache = readUpdateCache();
|
|
24
42
|
const now = Date.now();
|
|
@@ -27,12 +45,10 @@ export async function maybeNotifyUpdate(currentVersion) {
|
|
|
27
45
|
cache = { lastCheck: now, latestVersion };
|
|
28
46
|
writeUpdateCache(cache);
|
|
29
47
|
}
|
|
30
|
-
|
|
31
|
-
info(`${colors.yellow("⚠")} A new version of cargo-ai is available: ${colors.dim(currentVersion)} → ${colors.green(cache.latestVersion)}`);
|
|
32
|
-
info(` Update with: ${colors.cyan(UPDATE_COMMAND)}`);
|
|
33
|
-
}
|
|
48
|
+
return cache.latestVersion;
|
|
34
49
|
}
|
|
35
50
|
catch {
|
|
36
51
|
// Fail open: an update check must never get in the way of the command.
|
|
52
|
+
return cachedLatestVersion();
|
|
37
53
|
}
|
|
38
54
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cargo-ai/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.70",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"homepage": "https://getcargo.ai",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"@cargo-ai/api": "^1.0.69",
|
|
68
68
|
"@cargo-ai/app-sdk": "^1.0.9",
|
|
69
|
-
"@cargo-ai/cdk": "^1.0.
|
|
69
|
+
"@cargo-ai/cdk": "^1.0.56",
|
|
70
70
|
"@cargo-ai/types": "^1.0.66",
|
|
71
71
|
"@cargo-ai/worker-sdk": "^1.0.17",
|
|
72
72
|
"@modelcontextprotocol/sdk": "1.29.0",
|
package/build/commands/init.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAGrC,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,GAAG,IAAI,CAW5E"}
|
package/build/commands/init.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { handleApiCall, outputJson } from "./runHandler.js";
|
|
2
|
-
export function registerInitCommand(parent, getApi) {
|
|
3
|
-
parent
|
|
4
|
-
.command("init")
|
|
5
|
-
.description("Fetch workspace initialisation (user, workspace, datasets, etc.)")
|
|
6
|
-
.action(async () => {
|
|
7
|
-
const api = getApi();
|
|
8
|
-
const result = await handleApiCall(() => api.getInitialisation());
|
|
9
|
-
outputJson(result);
|
|
10
|
-
});
|
|
11
|
-
}
|