@cargo-ai/cli 1.0.118 → 1.0.120

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 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/session.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,OAAO,EAAE,KAAK,iBAAiB,EAAoB,MAAM,gBAAgB,CAAC;AAE1E,eAAO,MAAM,gBAAgB,4BAA4B,CAAC;AAE1D,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAMnE;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,MAAM,EAAE,cAAc,CAAC;IACvB,6EAA6E;IAC7E,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA4C7B"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/session.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,KAAK,iBAAiB,EAAoB,MAAM,gBAAgB,CAAC;AAE1E,eAAO,MAAM,gBAAgB,4BAA4B,CAAC;AAE1D,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAMnE;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,MAAM,EAAE,cAAc,CAAC;IACvB,6EAA6E;IAC7E,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA4B7B"}
@@ -1,7 +1,6 @@
1
1
  import { saveCredentials } from "../../credentials.js";
2
2
  import { getOAuthConfig } from "../../oauthConfig.js";
3
3
  import { failWith } from "../runHandler.js";
4
- import { refreshAccessToken } from "./passwordless.js";
5
4
  import { resolveWorkspace } from "./workspace.js";
6
5
  export const DEFAULT_BASE_URL = "https://api.getcargo.io";
7
6
  export function resolveBaseUrl(override) {
@@ -21,29 +20,16 @@ export function resolveBaseUrl(override) {
21
20
  */
22
21
  export async function establishSession(opts) {
23
22
  const baseUrl = resolveBaseUrl(opts.baseUrlOverride);
24
- const { workspace, wasCreated } = await resolveWorkspace({
23
+ const { workspace } = await resolveWorkspace({
25
24
  baseUrl,
26
25
  accessToken: opts.tokens.accessToken,
27
26
  explicitUuid: opts.workspaceUuid,
28
27
  explicitName: opts.workspaceName,
29
28
  });
30
- let { accessToken, refreshToken, expiresAt } = opts.tokens;
29
+ const { accessToken, refreshToken, expiresAt } = opts.tokens;
31
30
  if (refreshToken === undefined) {
32
31
  failWith("The identity provider issued no refresh token, so the CLI cannot keep this session alive. Enable the Refresh Token grant for the CLI application in Auth0, then sign in again.");
33
32
  }
34
- if (wasCreated === true) {
35
- // The workspace list is a claim on the access token, so a workspace created
36
- // moments ago is absent from the one we are holding and every request for
37
- // it would be rejected. Only reissuing the token picks it up.
38
- const refreshed = await refreshAccessToken({ refreshToken });
39
- accessToken = refreshed.accessToken;
40
- expiresAt = refreshed.expiresAt;
41
- // Only a tenant that rotates refresh tokens returns a new one; keep the
42
- // existing one otherwise, since it is still the valid one.
43
- if (refreshed.refreshToken !== undefined) {
44
- refreshToken = refreshed.refreshToken;
45
- }
46
- }
47
33
  const { domain, clientId } = getOAuthConfig();
48
34
  saveCredentials({
49
35
  accessToken,
@@ -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;AAazD,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;AAUF,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,CAkG/B;AAED,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,SAAS,mBAAmB,CAAC,SAAS,EAAE,EACpD,IAAI,EAAE,MAAM,GACX,iBAAiB,GAAG,SAAS,CAY/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;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({
@@ -16,7 +27,7 @@ export async function resolveWorkspace(opts) {
16
27
  getAccessToken: () => Promise.resolve(accessToken),
17
28
  workspaceUuid: undefined,
18
29
  });
19
- const { workspaces } = await handleApiCall(() => api.workspaceManagement.workspace.all({ refresh: false }), { spinner: "Loading workspaces..." });
30
+ const { workspaces } = await handleApiCall(() => api.workspaceManagement.workspace.all(), { spinner: "Loading workspaces..." });
20
31
  if (opts.explicitName !== undefined && opts.explicitName.length > 0) {
21
32
  // Reuse a same-named workspace rather than creating a duplicate, so that
22
33
  // re-running a non-interactive sign-in is idempotent as documented.
@@ -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":"workspaces.d.ts","sourceRoot":"","sources":["../../../src/commands/workspaceManagement/workspaces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAIxC,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,GAAG,GAChB,IAAI,CAkLN"}
1
+ {"version":3,"file":"workspaces.d.ts","sourceRoot":"","sources":["../../../src/commands/workspaceManagement/workspaces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAGxC,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,GAAG,GAChB,IAAI,CA0KN"}
@@ -1,4 +1,3 @@
1
- import { parseBooleanOption } from "../../utils/booleanOption.js";
2
1
  import { handleApiCall, outputJson, parseJson } from "../runHandler.js";
3
2
  export function registerWorkspacesCommands(parent, getApi) {
4
3
  const workspaces = parent
@@ -7,17 +6,13 @@ export function registerWorkspacesCommands(parent, getApi) {
7
6
  workspaces
8
7
  .command("list")
9
8
  .description("List all workspaces accessible to the current user")
10
- .option("--refresh [true|false]", "Bypass any token cache and re-fetch from the server (useful after permission changes)", parseBooleanOption)
11
9
  .addHelpText("after", `
12
10
  Examples:
13
11
  $ cargo-ai workspaceManagement workspaces list
14
- $ cargo-ai workspaceManagement workspaces list --refresh
15
12
  $ cargo-ai workspaceManagement workspaces list | jq -r '.workspaces[].uuid'`)
16
- .action(async (opts) => {
13
+ .action(async () => {
17
14
  const api = getApi();
18
- const result = await handleApiCall(() => api.workspaceManagement.workspace.all({
19
- refresh: opts.refresh === true,
20
- }));
15
+ const result = await handleApiCall(() => api.workspaceManagement.workspace.all());
21
16
  outputJson(result);
22
17
  });
23
18
  workspaces
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cargo-ai/cli",
3
- "version": "1.0.118",
3
+ "version": "1.0.120",
4
4
  "mcpName": "io.github.getcargohq/cargo",
5
5
  "private": false,
6
6
  "license": "UNLICENSED",
@@ -74,11 +74,11 @@
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.97",
77
+ "@cargo-ai/api": "^1.0.99",
78
78
  "@cargo-ai/app-sdk": "^1.0.11",
79
79
  "@cargo-ai/cdk": "^1.0.116",
80
- "@cargo-ai/types": "^1.0.96",
81
- "@cargo-ai/utils": "^1.0.32",
80
+ "@cargo-ai/types": "^1.0.99",
81
+ "@cargo-ai/utils": "^1.0.33",
82
82
  "@cargo-ai/worker-sdk": "^1.0.21",
83
83
  "@modelcontextprotocol/sdk": "^1.31.0",
84
84
  "ai": "^7.0.133",