@cargo-ai/cli 1.0.119 → 1.0.121
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.
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type WorkspaceManagement } from "@cargo-ai/api";
|
|
2
|
+
import { type Api } from "../../api.js";
|
|
2
3
|
export type ResolvedWorkspace = {
|
|
3
4
|
uuid: string;
|
|
4
5
|
name: string;
|
|
@@ -15,4 +16,23 @@ export declare function resolveWorkspace(opts: {
|
|
|
15
16
|
explicitName: string | undefined;
|
|
16
17
|
}): Promise<WorkspaceResolution>;
|
|
17
18
|
export declare function findWorkspaceByName(workspaces: readonly WorkspaceManagement.Workspace[], name: string): ResolvedWorkspace | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* The workspace the sign-in Action just created, when it is the account's only
|
|
21
|
+
* one: named "Personal" and created within SIGN_IN_WORKSPACE_MAX_AGE_MS.
|
|
22
|
+
*
|
|
23
|
+
* A fresh "Personal" can also be somebody else's that this account was invited
|
|
24
|
+
* into, which `isOnlyMember` rules out.
|
|
25
|
+
*/
|
|
26
|
+
export declare function findSignInWorkspace(workspaces: readonly Pick<WorkspaceManagement.Workspace, "uuid" | "name" | "createdAt">[], now: Date): ResolvedWorkspace | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Whether the signed-in account is the workspace's only member — what tells
|
|
29
|
+
* the workspace sign-in just created for it apart from one it was invited
|
|
30
|
+
* into, which still holds whoever did the inviting.
|
|
31
|
+
*
|
|
32
|
+
* A listing that fails reads as "someone else's": renaming a colleague's
|
|
33
|
+
* workspace is a worse outcome than the second workspace this avoids.
|
|
34
|
+
*
|
|
35
|
+
* Exported for tests; `resolveWorkspace` is the only production caller.
|
|
36
|
+
*/
|
|
37
|
+
export declare function isOnlyMember(api: Api): Promise<boolean>;
|
|
18
38
|
//# sourceMappingURL=workspace.d.ts.map
|
|
@@ -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;
|
|
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;AAsHD;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ7D"}
|
|
@@ -9,6 +9,17 @@ import { ExitCodes, failWith, handleApiCall, info, success, } from "../runHandle
|
|
|
9
9
|
*/
|
|
10
10
|
const CREATE = " create";
|
|
11
11
|
const DEFAULT_WORKSPACE_NAME = "My workspace";
|
|
12
|
+
/**
|
|
13
|
+
* What the sign-in Action names the workspace it creates for an account that
|
|
14
|
+
* has none. Matches PERSONAL_WORKSPACE_NAME in the backend's action route.
|
|
15
|
+
*/
|
|
16
|
+
const SIGN_IN_WORKSPACE_NAME = "Personal";
|
|
17
|
+
/**
|
|
18
|
+
* How recently that workspace must have been created to count as this
|
|
19
|
+
* sign-in's. An older "Personal" may hold work, so naming a workspace then
|
|
20
|
+
* creates a new one rather than renaming it.
|
|
21
|
+
*/
|
|
22
|
+
const SIGN_IN_WORKSPACE_MAX_AGE_MS = 10 * 60 * 1000;
|
|
12
23
|
export async function resolveWorkspace(opts) {
|
|
13
24
|
const accessToken = opts.accessToken;
|
|
14
25
|
const api = createApi({
|
|
@@ -24,6 +35,25 @@ export async function resolveWorkspace(opts) {
|
|
|
24
35
|
if (existingWorkspace !== undefined) {
|
|
25
36
|
return { workspace: existingWorkspace, wasCreated: false };
|
|
26
37
|
}
|
|
38
|
+
// A brand-new account already has the workspace sign-in created for it,
|
|
39
|
+
// and the name given here is for that one, not a second beside it.
|
|
40
|
+
const signInWorkspace = findSignInWorkspace(workspaces, new Date());
|
|
41
|
+
if (signInWorkspace !== undefined) {
|
|
42
|
+
// Reading the members and renaming both go through workspace-scoped
|
|
43
|
+
// routes, so this client carries the workspace the listing one could
|
|
44
|
+
// not.
|
|
45
|
+
const signInApi = createApi({
|
|
46
|
+
baseUrl: opts.baseUrl,
|
|
47
|
+
getAccessToken: () => Promise.resolve(accessToken),
|
|
48
|
+
workspaceUuid: signInWorkspace.uuid,
|
|
49
|
+
});
|
|
50
|
+
if ((await isOnlyMember(signInApi)) === true) {
|
|
51
|
+
return {
|
|
52
|
+
workspace: await renameWorkspace(signInApi, opts.explicitName),
|
|
53
|
+
wasCreated: false,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
27
57
|
return {
|
|
28
58
|
workspace: await createWorkspace(api, opts.explicitName),
|
|
29
59
|
wasCreated: true,
|
|
@@ -86,6 +116,27 @@ export function findWorkspaceByName(workspaces, name) {
|
|
|
86
116
|
}
|
|
87
117
|
return { uuid: matched.uuid, name: matched.name };
|
|
88
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* The workspace the sign-in Action just created, when it is the account's only
|
|
121
|
+
* one: named "Personal" and created within SIGN_IN_WORKSPACE_MAX_AGE_MS.
|
|
122
|
+
*
|
|
123
|
+
* A fresh "Personal" can also be somebody else's that this account was invited
|
|
124
|
+
* into, which `isOnlyMember` rules out.
|
|
125
|
+
*/
|
|
126
|
+
export function findSignInWorkspace(workspaces, now) {
|
|
127
|
+
const [onlyWorkspace] = workspaces;
|
|
128
|
+
if (workspaces.length !== 1 || onlyWorkspace === undefined) {
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
if (onlyWorkspace.name !== SIGN_IN_WORKSPACE_NAME) {
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
const age = now.getTime() - new Date(onlyWorkspace.createdAt).getTime();
|
|
135
|
+
if (age > SIGN_IN_WORKSPACE_MAX_AGE_MS) {
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
return { uuid: onlyWorkspace.uuid, name: onlyWorkspace.name };
|
|
139
|
+
}
|
|
89
140
|
/** Pick from the workspaces you have, or name a new one, in one question. */
|
|
90
141
|
async function chooseWorkspace(api, workspaces) {
|
|
91
142
|
if (workspaces.length === 0) {
|
|
@@ -169,6 +220,29 @@ async function createWorkspace(api, name) {
|
|
|
169
220
|
const result = await handleApiCall(() => api.workspaceManagement.workspace.create({ name }), { spinner: "Creating workspace..." });
|
|
170
221
|
return { uuid: result.workspace.uuid, name: result.workspace.name };
|
|
171
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Whether the signed-in account is the workspace's only member — what tells
|
|
225
|
+
* the workspace sign-in just created for it apart from one it was invited
|
|
226
|
+
* into, which still holds whoever did the inviting.
|
|
227
|
+
*
|
|
228
|
+
* A listing that fails reads as "someone else's": renaming a colleague's
|
|
229
|
+
* workspace is a worse outcome than the second workspace this avoids.
|
|
230
|
+
*
|
|
231
|
+
* Exported for tests; `resolveWorkspace` is the only production caller.
|
|
232
|
+
*/
|
|
233
|
+
export async function isOnlyMember(api) {
|
|
234
|
+
try {
|
|
235
|
+
const { users } = await api.workspaceManagement.user.all();
|
|
236
|
+
return users.length === 1;
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
async function renameWorkspace(api, name) {
|
|
243
|
+
const result = await handleApiCall(() => api.workspaceManagement.workspace.update({ name }), { spinner: "Naming workspace..." });
|
|
244
|
+
return { uuid: result.workspace.uuid, name: result.workspace.name };
|
|
245
|
+
}
|
|
172
246
|
async function promptWorkspaceSelection(workspaces, input) {
|
|
173
247
|
const rl = readline.createInterface({
|
|
174
248
|
input,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../../src/commands/content/file.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../../src/commands/content/file.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAGxC,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,GAAG,IAAI,CAsK7E"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
|
-
import { basename } from "node:path";
|
|
2
|
+
import { basename, extname } from "node:path";
|
|
3
3
|
import { handleApiCall, outputJson } from "../runHandler.js";
|
|
4
4
|
export function registerFileCommands(parent, getApi) {
|
|
5
5
|
const file = parent.command("file").description("Storage file operations");
|
|
@@ -47,6 +47,8 @@ export function registerFileCommands(parent, getApi) {
|
|
|
47
47
|
.description("Upload a file")
|
|
48
48
|
.requiredOption("--file <path>", "Path to the file to upload")
|
|
49
49
|
.option("--folder-uuid <uuid>", "Folder UUID")
|
|
50
|
+
.option("--index <none|vectorStore>", "vectorStore (default) makes the file searchable by agents; none only stores it")
|
|
51
|
+
.option("--visibility <private|public>", "Defaults to private")
|
|
50
52
|
.action(async (opts) => {
|
|
51
53
|
const buffer = await readFile(opts.file);
|
|
52
54
|
const name = basename(opts.file);
|
|
@@ -56,6 +58,8 @@ export function registerFileCommands(parent, getApi) {
|
|
|
56
58
|
api.content.file.upload({
|
|
57
59
|
file: fileObj,
|
|
58
60
|
folderUuid: opts.folderUuid,
|
|
61
|
+
indexMode: parseIndexMode(opts.index),
|
|
62
|
+
visibility: parseVisibility(opts.visibility),
|
|
59
63
|
onProgress: (pct) => {
|
|
60
64
|
process.stderr.write(`\rUpload progress: ${pct}%`);
|
|
61
65
|
},
|
|
@@ -71,4 +75,89 @@ export function registerFileCommands(parent, getApi) {
|
|
|
71
75
|
});
|
|
72
76
|
}));
|
|
73
77
|
});
|
|
78
|
+
file
|
|
79
|
+
.command("put <path>")
|
|
80
|
+
.description("Store a file at a path (e.g. reports/2026-q3/summary.pdf), creating folders and overwriting what is there. Any size: large files go straight to storage in parts.")
|
|
81
|
+
.requiredOption("--file <path>", "Path to the local file")
|
|
82
|
+
.option("--content-type <type>", "MIME type (guessed from the extension)")
|
|
83
|
+
.option("--index <none|vectorStore>", "none (default for a new file) only stores it; vectorStore makes it searchable by agents")
|
|
84
|
+
.option("--visibility <private|public>", "Defaults to private")
|
|
85
|
+
.option("--mirror-to-context", "Also write it under .files/ in the context")
|
|
86
|
+
.action(async (path, opts) => {
|
|
87
|
+
const contentType = opts.contentType !== undefined
|
|
88
|
+
? opts.contentType
|
|
89
|
+
: guessContentType(opts.file);
|
|
90
|
+
const buffer = await readFile(opts.file);
|
|
91
|
+
const fileObj = new File([buffer], basename(opts.file), { type: contentType });
|
|
92
|
+
const api = getApi();
|
|
93
|
+
const uploaded = await handleApiCall(() => new Promise((resolve, reject) => {
|
|
94
|
+
api.workspaceManagement.file.upload({
|
|
95
|
+
file: fileObj,
|
|
96
|
+
onProgress: (pct) => {
|
|
97
|
+
process.stderr.write(`\rUpload progress: ${pct}%`);
|
|
98
|
+
},
|
|
99
|
+
onComplete: (result) => {
|
|
100
|
+
process.stderr.write("\n");
|
|
101
|
+
resolve(result);
|
|
102
|
+
},
|
|
103
|
+
onError: () => {
|
|
104
|
+
process.stderr.write("\n");
|
|
105
|
+
reject(new Error("Upload failed"));
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
}));
|
|
109
|
+
const result = await handleApiCall(() => api.content.file.import({
|
|
110
|
+
s3Filename: uploaded.s3Filename,
|
|
111
|
+
path,
|
|
112
|
+
contentType,
|
|
113
|
+
indexMode: parseIndexMode(opts.index),
|
|
114
|
+
visibility: parseVisibility(opts.visibility),
|
|
115
|
+
mirrorToContext: opts.mirrorToContext,
|
|
116
|
+
}));
|
|
117
|
+
outputJson(result);
|
|
118
|
+
});
|
|
74
119
|
}
|
|
120
|
+
const parseIndexMode = (value) => {
|
|
121
|
+
if (value === undefined) {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
if (value === "none" || value === "vectorStore") {
|
|
125
|
+
return value;
|
|
126
|
+
}
|
|
127
|
+
throw new Error(`--index must be none or vectorStore, got ${value}`);
|
|
128
|
+
};
|
|
129
|
+
const parseVisibility = (value) => {
|
|
130
|
+
if (value === undefined) {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
if (value === "private" || value === "public") {
|
|
134
|
+
return value;
|
|
135
|
+
}
|
|
136
|
+
throw new Error(`--visibility must be private or public, got ${value}`);
|
|
137
|
+
};
|
|
138
|
+
const CONTENT_TYPES_BY_EXTENSION = {
|
|
139
|
+
".csv": "text/csv",
|
|
140
|
+
".gif": "image/gif",
|
|
141
|
+
".html": "text/html",
|
|
142
|
+
".jpeg": "image/jpeg",
|
|
143
|
+
".jpg": "image/jpeg",
|
|
144
|
+
".json": "application/json",
|
|
145
|
+
".md": "text/markdown",
|
|
146
|
+
".mp3": "audio/mpeg",
|
|
147
|
+
".mp4": "video/mp4",
|
|
148
|
+
".pdf": "application/pdf",
|
|
149
|
+
".png": "image/png",
|
|
150
|
+
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
151
|
+
".svg": "image/svg+xml",
|
|
152
|
+
".txt": "text/plain",
|
|
153
|
+
".wav": "audio/wav",
|
|
154
|
+
".webm": "video/webm",
|
|
155
|
+
".webp": "image/webp",
|
|
156
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
157
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
158
|
+
".zip": "application/zip",
|
|
159
|
+
};
|
|
160
|
+
const guessContentType = (filePath) => {
|
|
161
|
+
const contentType = CONTENT_TYPES_BY_EXTENSION[extname(filePath).toLowerCase()];
|
|
162
|
+
return contentType !== undefined ? contentType : "application/octet-stream";
|
|
163
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cargo-ai/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.121",
|
|
4
4
|
"mcpName": "io.github.getcargohq/cargo",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -74,10 +74,10 @@
|
|
|
74
74
|
"dependencies": {
|
|
75
75
|
"@ai-sdk/harness": "^1.0.144",
|
|
76
76
|
"@ai-sdk/harness-claude-code": "^1.0.148",
|
|
77
|
-
"@cargo-ai/api": "^1.0.
|
|
77
|
+
"@cargo-ai/api": "^1.0.100",
|
|
78
78
|
"@cargo-ai/app-sdk": "^1.0.11",
|
|
79
79
|
"@cargo-ai/cdk": "^1.0.116",
|
|
80
|
-
"@cargo-ai/types": "^1.0.
|
|
80
|
+
"@cargo-ai/types": "^1.0.100",
|
|
81
81
|
"@cargo-ai/utils": "^1.0.33",
|
|
82
82
|
"@cargo-ai/worker-sdk": "^1.0.21",
|
|
83
83
|
"@modelcontextprotocol/sdk": "^1.31.0",
|