@asteroidai/cli 1.8.193 → 1.8.197

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/dist/asteroid.js CHANGED
@@ -5192,6 +5192,8 @@ var CredentialStore = class {
5192
5192
  // src/auth/credentials.ts
5193
5193
  var AuthError = class extends Error {
5194
5194
  };
5195
+ var API_KEY_OVERRIDES = "ASTEROID_API_KEY is set, so the CLI uses that key and ignores `asteroid login`.";
5196
+ var UNSET_API_KEY = "Run `unset ASTEROID_API_KEY` to use your login instead.";
5195
5197
  function orgFromToken(token) {
5196
5198
  const claims = tokenClaims(token);
5197
5199
  if (!claims.org_id) return void 0;
@@ -5301,7 +5303,7 @@ var ApiError = class extends Error {
5301
5303
  status;
5302
5304
  body;
5303
5305
  };
5304
- var VERSION = "1.8.193";
5306
+ var VERSION = "1.8.197";
5305
5307
  function problem(status, body, fallback) {
5306
5308
  if (body && typeof body === "object") {
5307
5309
  const record2 = body;
@@ -5368,11 +5370,13 @@ async function send(apiUrl, auth, request, binary, fetchImpl = fetch) {
5368
5370
  } catch {
5369
5371
  parsed = text2;
5370
5372
  }
5371
- throw new ApiError(
5372
- response.status,
5373
- problem(response.status, parsed, text2.slice(0, 200)),
5374
- parsed
5375
- );
5373
+ let message = problem(response.status, parsed, text2.slice(0, 200));
5374
+ const sentKey = "X-Api-Key" in hopHeaders;
5375
+ if (sentKey && response.status === 401)
5376
+ message = `ASTEROID_API_KEY was rejected: ${message}. Check the key. ${UNSET_API_KEY}`;
5377
+ else if (sentKey && response.status === 403)
5378
+ message = `${message} (sent with ASTEROID_API_KEY. ${UNSET_API_KEY})`;
5379
+ throw new ApiError(response.status, message, parsed);
5376
5380
  }
5377
5381
  if (binary)
5378
5382
  return {
@@ -5401,7 +5405,8 @@ async function chooseOrg(deps, orgs2) {
5401
5405
  async function login(deps) {
5402
5406
  if (deps.env.apiKey)
5403
5407
  deps.stderr(
5404
- "Note: ASTEROID_API_KEY is set and takes precedence over this sign-in.\n"
5408
+ `Warning: ${API_KEY_OVERRIDES} Every command will use the key, not this sign-in. ${UNSET_API_KEY}
5409
+ `
5405
5410
  );
5406
5411
  const client = {
5407
5412
  clientId: deps.env.workosClientId,
@@ -5467,7 +5472,7 @@ async function logout(deps) {
5467
5472
  async function orgs(deps, flagOrg, json) {
5468
5473
  if (deps.env.apiKey)
5469
5474
  throw new UsageError(
5470
- "ASTEROID_API_KEY acts in one organization. Run `asteroid whoami` to see it."
5475
+ `${API_KEY_OVERRIDES} The key acts in one organization; \`asteroid whoami\` shows it. ${UNSET_API_KEY}`
5471
5476
  );
5472
5477
  const config = loadConfig(deps.env);
5473
5478
  const auth = await resolveAuth(
@@ -5497,7 +5502,7 @@ async function orgs(deps, flagOrg, json) {
5497
5502
  async function use(deps, ref) {
5498
5503
  if (deps.env.apiKey)
5499
5504
  throw new UsageError(
5500
- "ASTEROID_API_KEY is pinned to one organization; `use` needs `asteroid login`."
5505
+ `${API_KEY_OVERRIDES} \`use\` only switches the login's organization. ${UNSET_API_KEY}`
5501
5506
  );
5502
5507
  const config = loadConfig(deps.env);
5503
5508
  if (!ref && deps.ui && (config.orgs?.length ?? 0) > 0) {
@@ -5556,6 +5561,7 @@ async function whoami(deps, flagOrg, json) {
5556
5561
  as: person?.email ?? body.id,
5557
5562
  kind: body.kind,
5558
5563
  credential: body.credential,
5564
+ source: auth.kind === "api-key" ? "ASTEROID_API_KEY" : "asteroid login",
5559
5565
  organization,
5560
5566
  ...minutes === void 0 ? {} : { tokenExpiresIn: `${minutes}m` }
5561
5567
  })}
@@ -5765,7 +5771,7 @@ function rejectUnknown(flags) {
5765
5771
  function rejectOrgWithApiKey(deps, flagOrg) {
5766
5772
  if (deps.env.apiKey && (flagOrg || deps.env.org)) {
5767
5773
  throw new UsageError(
5768
- "ASTEROID_API_KEY acts in its own organization. Unset --org and ASTEROID_ORG, or sign in with `asteroid login`."
5774
+ `${API_KEY_OVERRIDES} The key acts in its own organization, so drop --org and ASTEROID_ORG. ${UNSET_API_KEY}`
5769
5775
  );
5770
5776
  }
5771
5777
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asteroidai/cli",
3
- "version": "1.8.193",
3
+ "version": "1.8.197",
4
4
  "description": "The asteroid command-line interface for the Asteroid platform.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/account.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  import { UsageError } from "./args";
2
2
  import {
3
+ API_KEY_OVERRIDES,
3
4
  AuthError,
4
5
  orgFromToken,
5
6
  rememberOrg,
6
7
  resolveAuth,
8
+ UNSET_API_KEY,
7
9
  } from "./auth/credentials";
8
10
  import { CredentialStore } from "./auth/store";
9
11
  import {
@@ -40,7 +42,7 @@ async function chooseOrg(deps: Deps, orgs: WorkosOrg[]): Promise<WorkosOrg> {
40
42
  export async function login(deps: Deps): Promise<number> {
41
43
  if (deps.env.apiKey)
42
44
  deps.stderr(
43
- "Note: ASTEROID_API_KEY is set and takes precedence over this sign-in.\n",
45
+ `Warning: ${API_KEY_OVERRIDES} Every command will use the key, not this sign-in. ${UNSET_API_KEY}\n`,
44
46
  );
45
47
  const client = {
46
48
  clientId: deps.env.workosClientId,
@@ -116,7 +118,7 @@ export async function orgs(
116
118
  ): Promise<number> {
117
119
  if (deps.env.apiKey)
118
120
  throw new UsageError(
119
- "ASTEROID_API_KEY acts in one organization. Run `asteroid whoami` to see it.",
121
+ `${API_KEY_OVERRIDES} The key acts in one organization; \`asteroid whoami\` shows it. ${UNSET_API_KEY}`,
120
122
  );
121
123
  const config = loadConfig(deps.env);
122
124
  const auth = await resolveAuth(
@@ -148,7 +150,7 @@ export async function use(
148
150
  ): Promise<number> {
149
151
  if (deps.env.apiKey)
150
152
  throw new UsageError(
151
- "ASTEROID_API_KEY is pinned to one organization; `use` needs `asteroid login`.",
153
+ `${API_KEY_OVERRIDES} \`use\` only switches the login's organization. ${UNSET_API_KEY}`,
152
154
  );
153
155
  const config = loadConfig(deps.env);
154
156
  if (!ref && deps.ui && (config.orgs?.length ?? 0) > 0) {
@@ -213,6 +215,7 @@ export async function whoami(
213
215
  as: person?.email ?? body.id,
214
216
  kind: body.kind,
215
217
  credential: body.credential,
218
+ source: auth.kind === "api-key" ? "ASTEROID_API_KEY" : "asteroid login",
216
219
  organization,
217
220
  ...(minutes === undefined ? {} : { tokenExpiresIn: `${minutes}m` }),
218
221
  })}\n`,
@@ -12,6 +12,11 @@ import { refresh, tokenClaims, WorkosError } from "./workos";
12
12
 
13
13
  export class AuthError extends Error {}
14
14
 
15
+ export const API_KEY_OVERRIDES =
16
+ "ASTEROID_API_KEY is set, so the CLI uses that key and ignores `asteroid login`.";
17
+ export const UNSET_API_KEY =
18
+ "Run `unset ASTEROID_API_KEY` to use your login instead.";
19
+
15
20
  export interface Auth {
16
21
  kind: "api-key" | "bearer";
17
22
  headers: Record<string, string>;
package/src/http.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Auth } from "./auth/credentials";
1
+ import { type Auth, UNSET_API_KEY } from "./auth/credentials";
2
2
  import type { BuiltRequest } from "./request";
3
3
 
4
4
  export class ApiError extends Error {
@@ -95,11 +95,13 @@ export async function send(
95
95
  } catch {
96
96
  parsed = text;
97
97
  }
98
- throw new ApiError(
99
- response.status,
100
- problem(response.status, parsed, text.slice(0, 200)),
101
- parsed,
102
- );
98
+ let message = problem(response.status, parsed, text.slice(0, 200));
99
+ const sentKey = "X-Api-Key" in hopHeaders;
100
+ if (sentKey && response.status === 401)
101
+ message = `ASTEROID_API_KEY was rejected: ${message}. Check the key. ${UNSET_API_KEY}`;
102
+ else if (sentKey && response.status === 403)
103
+ message = `${message} (sent with ASTEROID_API_KEY. ${UNSET_API_KEY})`;
104
+ throw new ApiError(response.status, message, parsed);
103
105
  }
104
106
  if (binary)
105
107
  return {
package/src/run.test.ts CHANGED
@@ -184,6 +184,24 @@ describe("guards", () => {
184
184
  expect(h.calls).toHaveLength(0);
185
185
  });
186
186
 
187
+ it("tells a signed-in user that the API key overrides use", async () => {
188
+ const h = harness({ ASTEROID_API_KEY: "sk_test" }, () => json({}));
189
+ await new CredentialStore(h.deps.env).writeLogin({ refreshToken: "r1" });
190
+ expect(await run(["use", "org_A"], h.deps)).toBe(2);
191
+ expect(h.err.join("")).toContain("ignores `asteroid login`");
192
+ expect(h.err.join("")).toContain("unset ASTEROID_API_KEY");
193
+ });
194
+
195
+ it("names the API key when the API rejects it", async () => {
196
+ const h = harness({ ASTEROID_API_KEY: "sk_bad" }, () =>
197
+ json({ detail: "invalid API key" }, 401),
198
+ );
199
+ expect(await run(["workflow", "list"], h.deps)).toBe(1);
200
+ expect(h.err.join("")).toContain(
201
+ "401: ASTEROID_API_KEY was rejected: invalid API key.",
202
+ );
203
+ });
204
+
187
205
  it("rejects extra arguments and unknown flags on account commands", async () => {
188
206
  const h = harness({}, () => json({}));
189
207
  expect(await run(["use", "a", "b"], h.deps)).toBe(2);
@@ -209,6 +227,22 @@ describe("guards", () => {
209
227
  ).toBeUndefined();
210
228
  });
211
229
 
230
+ it("does not blame the API key for an error after a cross-host redirect", async () => {
231
+ const h = harness({ ASTEROID_API_KEY: "sk_test" }, (call) =>
232
+ call.url.startsWith("https://odyssey")
233
+ ? new Response(null, {
234
+ status: 303,
235
+ headers: { location: "https://storage.test/file" },
236
+ })
237
+ : new Response("expired", { status: 403 }),
238
+ );
239
+ expect(
240
+ await run(["execution", "file", "download", "e1", "f1"], h.deps),
241
+ ).toBe(1);
242
+ expect(h.err.join("")).toContain("403: expired");
243
+ expect(h.err.join("")).not.toContain("ASTEROID_API_KEY");
244
+ });
245
+
212
246
  it("keeps the method and body on a 307", async () => {
213
247
  const h = harness({ ASTEROID_API_KEY: "sk_test" }, (call) =>
214
248
  call.url.endsWith("/validate")
package/src/run.ts CHANGED
@@ -2,7 +2,13 @@ import { writeFileSync } from "node:fs";
2
2
 
3
3
  import { login, logout, orgs, use, whoami } from "./account";
4
4
  import { type Parsed, UsageError } from "./args";
5
- import { type Auth, AuthError, resolveAuth } from "./auth/credentials";
5
+ import {
6
+ API_KEY_OVERRIDES,
7
+ type Auth,
8
+ AuthError,
9
+ resolveAuth,
10
+ UNSET_API_KEY,
11
+ } from "./auth/credentials";
6
12
  import { WorkosError } from "./auth/workos";
7
13
  import { type Env, loadConfig, orgLabel, requestedOrg } from "./config";
8
14
  import { COMMANDS } from "./generated/commands";
@@ -30,7 +36,7 @@ export interface Deps {
30
36
  function rejectOrgWithApiKey(deps: Deps, flagOrg: string | undefined) {
31
37
  if (deps.env.apiKey && (flagOrg || deps.env.org)) {
32
38
  throw new UsageError(
33
- "ASTEROID_API_KEY acts in its own organization. Unset --org and ASTEROID_ORG, or sign in with `asteroid login`.",
39
+ `${API_KEY_OVERRIDES} The key acts in its own organization, so drop --org and ASTEROID_ORG. ${UNSET_API_KEY}`,
34
40
  );
35
41
  }
36
42
  }