@cargo-ai/cli 1.0.145 → 1.0.146
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/commands/auth/workspace.d.ts +17 -0
- package/build/commands/auth/workspace.d.ts.map +1 -1
- package/build/commands/auth/workspace.js +74 -7
- package/build/utils/openBrowser.d.ts +14 -0
- package/build/utils/openBrowser.d.ts.map +1 -1
- package/build/utils/openBrowser.js +20 -8
- package/package.json +2 -2
|
@@ -15,6 +15,23 @@ export declare function resolveWorkspace(opts: {
|
|
|
15
15
|
explicitUuid: string | undefined;
|
|
16
16
|
explicitName: string | undefined;
|
|
17
17
|
}): Promise<WorkspaceResolution>;
|
|
18
|
+
/**
|
|
19
|
+
* Without a terminal nothing can be picked, so the error itself has to carry
|
|
20
|
+
* the choice: every workspace with its uuid, and the flag that selects one.
|
|
21
|
+
* The sign-in is not saved, and an emailed code works once, so the command is
|
|
22
|
+
* re-run from the start.
|
|
23
|
+
*
|
|
24
|
+
* Exported for tests; `resolveWorkspace` is the only production caller.
|
|
25
|
+
*/
|
|
26
|
+
export declare function describeWorkspaceChoice(workspaces: readonly Pick<WorkspaceManagement.Workspace, "uuid" | "name">[]): string;
|
|
27
|
+
/**
|
|
28
|
+
* The free plan caps how many workspaces one person owns, so naming a new one
|
|
29
|
+
* can be refused. The fix is to use a workspace that already exists, which the
|
|
30
|
+
* message lists with the flag that picks it.
|
|
31
|
+
*
|
|
32
|
+
* Exported for tests; `createWorkspace` is the only production caller.
|
|
33
|
+
*/
|
|
34
|
+
export declare function describeFreeWorkspaceLimit(name: string, workspaces: readonly Pick<WorkspaceManagement.Workspace, "uuid" | "name">[]): string;
|
|
18
35
|
export declare function findWorkspaceByName(workspaces: readonly WorkspaceManagement.Workspace[], name: string): ResolvedWorkspace | undefined;
|
|
19
36
|
/**
|
|
20
37
|
* The workspace the sign-in Action just created, when it is the account's only
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/workspace.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAGzD,OAAO,EAAE,KAAK,GAAG,EAAa,MAAM,cAAc,CAAC;AAUnD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,iBAAiB,CAAC;IAC7B,mFAAmF;IACnF,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AAuBF,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAwH/B;AAED,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,SAAS,mBAAmB,CAAC,SAAS,EAAE,EACpD,IAAI,EAAE,MAAM,GACX,iBAAiB,GAAG,SAAS,CAY/B;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,SAAS,IAAI,CACvB,mBAAmB,CAAC,SAAS,EAC7B,MAAM,GAAG,MAAM,GAAG,WAAW,CAC9B,EAAE,EACH,GAAG,EAAE,IAAI,GACR,iBAAiB,GAAG,SAAS,CAkB/B;
|
|
1
|
+
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/workspace.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAGzD,OAAO,EAAE,KAAK,GAAG,EAAa,MAAM,cAAc,CAAC;AAUnD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,iBAAiB,CAAC;IAC7B,mFAAmF;IACnF,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AAuBF,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAwH/B;AAUD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,SAAS,IAAI,CAAC,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,EAAE,GAC1E,MAAM,CASR;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,SAAS,IAAI,CAAC,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,EAAE,GAC1E,MAAM,CAsBR;AAED,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,SAAS,mBAAmB,CAAC,SAAS,EAAE,EACpD,IAAI,EAAE,MAAM,GACX,iBAAiB,GAAG,SAAS,CAY/B;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,SAAS,IAAI,CACvB,mBAAmB,CAAC,SAAS,EAC7B,MAAM,GAAG,MAAM,GAAG,WAAW,CAC9B,EAAE,EACH,GAAG,EAAE,IAAI,GACR,iBAAiB,GAAG,SAAS,CAkB/B;AAqJD;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ7D"}
|
|
@@ -55,7 +55,7 @@ export async function resolveWorkspace(opts) {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
return {
|
|
58
|
-
workspace: await createWorkspace(api, opts.explicitName),
|
|
58
|
+
workspace: await createWorkspace(api, opts.explicitName, workspaces),
|
|
59
59
|
wasCreated: true,
|
|
60
60
|
};
|
|
61
61
|
}
|
|
@@ -92,7 +92,10 @@ export async function resolveWorkspace(opts) {
|
|
|
92
92
|
}
|
|
93
93
|
const promptInput = openPromptInput();
|
|
94
94
|
if (promptInput === undefined) {
|
|
95
|
-
failWith(
|
|
95
|
+
failWith(describeWorkspaceChoice(workspaces), {
|
|
96
|
+
code: ExitCodes.InvalidUsage,
|
|
97
|
+
extra: { workspaces: listWorkspaceChoices(workspaces) },
|
|
98
|
+
});
|
|
96
99
|
}
|
|
97
100
|
let selectedWorkspace;
|
|
98
101
|
try {
|
|
@@ -106,6 +109,49 @@ export async function resolveWorkspace(opts) {
|
|
|
106
109
|
}
|
|
107
110
|
return { workspace: selectedWorkspace, wasCreated: false };
|
|
108
111
|
}
|
|
112
|
+
function listWorkspaceChoices(workspaces) {
|
|
113
|
+
return workspaces.map((workspace) => {
|
|
114
|
+
return { uuid: workspace.uuid, name: workspace.name };
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Without a terminal nothing can be picked, so the error itself has to carry
|
|
119
|
+
* the choice: every workspace with its uuid, and the flag that selects one.
|
|
120
|
+
* The sign-in is not saved, and an emailed code works once, so the command is
|
|
121
|
+
* re-run from the start.
|
|
122
|
+
*
|
|
123
|
+
* Exported for tests; `resolveWorkspace` is the only production caller.
|
|
124
|
+
*/
|
|
125
|
+
export function describeWorkspaceChoice(workspaces) {
|
|
126
|
+
return [
|
|
127
|
+
`You have ${String(workspaces.length)} workspaces and there is no interactive terminal to pick one, so this sign-in was not saved.`,
|
|
128
|
+
"Run the same login command again with --workspace-uuid <uuid> (or set CARGO_WORKSPACE_UUID). With --email, request a new code first: a code works only once.",
|
|
129
|
+
"Your workspaces:",
|
|
130
|
+
...workspaces.map((workspace) => {
|
|
131
|
+
return ` --workspace-uuid ${workspace.uuid} (${workspace.name})`;
|
|
132
|
+
}),
|
|
133
|
+
].join("\n");
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The free plan caps how many workspaces one person owns, so naming a new one
|
|
137
|
+
* can be refused. The fix is to use a workspace that already exists, which the
|
|
138
|
+
* message lists with the flag that picks it.
|
|
139
|
+
*
|
|
140
|
+
* Exported for tests; `createWorkspace` is the only production caller.
|
|
141
|
+
*/
|
|
142
|
+
export function describeFreeWorkspaceLimit(name, workspaces) {
|
|
143
|
+
const lines = [
|
|
144
|
+
`Cannot create workspace "${name}": you have reached the free plan's workspace limit.`,
|
|
145
|
+
];
|
|
146
|
+
if (workspaces.length === 0) {
|
|
147
|
+
lines.push("Upgrade a workspace, or ask a teammate to invite you to theirs.");
|
|
148
|
+
return lines.join("\n");
|
|
149
|
+
}
|
|
150
|
+
lines.push("Use an existing workspace instead: re-run the login command with --workspace-uuid <uuid> in place of --workspace-name. Or upgrade one of them to create more.", "Your workspaces:", ...workspaces.map((workspace) => {
|
|
151
|
+
return ` --workspace-uuid ${workspace.uuid} (${workspace.name})`;
|
|
152
|
+
}));
|
|
153
|
+
return lines.join("\n");
|
|
154
|
+
}
|
|
109
155
|
export function findWorkspaceByName(workspaces, name) {
|
|
110
156
|
const wanted = name.trim().toLowerCase();
|
|
111
157
|
const matched = workspaces.find((workspace) => {
|
|
@@ -142,7 +188,7 @@ async function chooseWorkspace(api, workspaces) {
|
|
|
142
188
|
if (workspaces.length === 0) {
|
|
143
189
|
info("You don't have any workspaces yet. Let's create one.");
|
|
144
190
|
return {
|
|
145
|
-
workspace: await createWorkspace(api, await askWorkspaceName()),
|
|
191
|
+
workspace: await createWorkspace(api, await askWorkspaceName(), workspaces),
|
|
146
192
|
wasCreated: true,
|
|
147
193
|
};
|
|
148
194
|
}
|
|
@@ -169,7 +215,7 @@ async function chooseWorkspace(api, workspaces) {
|
|
|
169
215
|
});
|
|
170
216
|
if (chosen === CREATE) {
|
|
171
217
|
return {
|
|
172
|
-
workspace: await createWorkspace(api, await askWorkspaceName()),
|
|
218
|
+
workspace: await createWorkspace(api, await askWorkspaceName(), workspaces),
|
|
173
219
|
wasCreated: true,
|
|
174
220
|
};
|
|
175
221
|
}
|
|
@@ -212,12 +258,33 @@ async function createWorkspaceByAsking(api) {
|
|
|
212
258
|
code: ExitCodes.InvalidUsage,
|
|
213
259
|
});
|
|
214
260
|
}
|
|
215
|
-
|
|
261
|
+
// Only reached with no workspaces, so the limit message has none to list.
|
|
262
|
+
const workspace = await createWorkspace(api, name, []);
|
|
216
263
|
success(`Created workspace: ${workspace.name} (${workspace.uuid})`);
|
|
217
264
|
return { workspace, wasCreated: true };
|
|
218
265
|
}
|
|
219
|
-
async function createWorkspace(api, name
|
|
220
|
-
|
|
266
|
+
async function createWorkspace(api, name,
|
|
267
|
+
// Required, not defaulted: the free-plan-limit message lists these as the
|
|
268
|
+
// way out, and a caller that forgot them printed "you have none" to someone
|
|
269
|
+
// who has several.
|
|
270
|
+
existingWorkspaces) {
|
|
271
|
+
const result = await handleApiCall(async () => {
|
|
272
|
+
try {
|
|
273
|
+
return await api.workspaceManagement.workspace.create({ name });
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
if (error.reason === "freeWorkspaceLimitReached") {
|
|
277
|
+
failWith(describeFreeWorkspaceLimit(name, existingWorkspaces), {
|
|
278
|
+
code: ExitCodes.InvalidUsage,
|
|
279
|
+
extra: {
|
|
280
|
+
reason: "freeWorkspaceLimitReached",
|
|
281
|
+
workspaces: listWorkspaceChoices(existingWorkspaces),
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
throw error;
|
|
286
|
+
}
|
|
287
|
+
}, { spinner: "Creating workspace..." });
|
|
221
288
|
return { uuid: result.workspace.uuid, name: result.workspace.name };
|
|
222
289
|
}
|
|
223
290
|
/**
|
|
@@ -1,7 +1,21 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
1
2
|
/**
|
|
2
3
|
* Best-effort browser launch. Every caller also prints the URL, so a failure
|
|
3
4
|
* here is never fatal: headless boxes and sandboxes simply fall back to the
|
|
4
5
|
* user opening the link themselves.
|
|
5
6
|
*/
|
|
6
7
|
export declare function openBrowser(url: string): void;
|
|
8
|
+
/**
|
|
9
|
+
* The command that hands `url` to the default browser.
|
|
10
|
+
*
|
|
11
|
+
* Windows goes through rundll32 rather than `cmd /c start`: cmd parses its
|
|
12
|
+
* command line, so every `&` in a query string ends the command there and the
|
|
13
|
+
* browser opens a truncated URL. rundll32 takes the URL as a plain argument.
|
|
14
|
+
*
|
|
15
|
+
* Exported for tests.
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildOpenCommand(os: NodeJS.Platform, url: string): {
|
|
18
|
+
command: string;
|
|
19
|
+
args: string[];
|
|
20
|
+
};
|
|
7
21
|
//# sourceMappingURL=openBrowser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openBrowser.d.ts","sourceRoot":"","sources":["../../src/utils/openBrowser.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"openBrowser.d.ts","sourceRoot":"","sources":["../../src/utils/openBrowser.ts"],"names":[],"mappings":";AAGA;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAa7C;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,MAAM,CAAC,QAAQ,EACnB,GAAG,EAAE,MAAM,GACV;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CASrC"}
|
|
@@ -6,8 +6,7 @@ import { platform } from "node:process";
|
|
|
6
6
|
* user opening the link themselves.
|
|
7
7
|
*/
|
|
8
8
|
export function openBrowser(url) {
|
|
9
|
-
const command =
|
|
10
|
-
const args = platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
9
|
+
const { command, args } = buildOpenCommand(platform, url);
|
|
11
10
|
try {
|
|
12
11
|
const child = spawn(command, args, {
|
|
13
12
|
detached: true,
|
|
@@ -20,10 +19,23 @@ export function openBrowser(url) {
|
|
|
20
19
|
// Intentionally ignored — see above.
|
|
21
20
|
}
|
|
22
21
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
/**
|
|
23
|
+
* The command that hands `url` to the default browser.
|
|
24
|
+
*
|
|
25
|
+
* Windows goes through rundll32 rather than `cmd /c start`: cmd parses its
|
|
26
|
+
* command line, so every `&` in a query string ends the command there and the
|
|
27
|
+
* browser opens a truncated URL. rundll32 takes the URL as a plain argument.
|
|
28
|
+
*
|
|
29
|
+
* Exported for tests.
|
|
30
|
+
*/
|
|
31
|
+
export function buildOpenCommand(os, url) {
|
|
32
|
+
if (os === "darwin")
|
|
33
|
+
return { command: "open", args: [url] };
|
|
34
|
+
if (os === "win32") {
|
|
35
|
+
return {
|
|
36
|
+
command: "rundll32",
|
|
37
|
+
args: ["url.dll,FileProtocolHandler", url],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return { command: "xdg-open", args: [url] };
|
|
29
41
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cargo-ai/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.146",
|
|
4
4
|
"mcpName": "io.github.getcargohq/cargo",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"@cargo-ai/api": "^1.0.107",
|
|
78
78
|
"@cargo-ai/app-sdk": "^1.0.12",
|
|
79
79
|
"@cargo-ai/cdk": "^1.0.140",
|
|
80
|
-
"@cargo-ai/types": "^1.0.
|
|
80
|
+
"@cargo-ai/types": "^1.0.108",
|
|
81
81
|
"@cargo-ai/utils": "^1.0.37",
|
|
82
82
|
"@cargo-ai/worker-sdk": "^1.0.21",
|
|
83
83
|
"@modelcontextprotocol/sdk": "^1.32.0",
|