@raster-app/cli 0.3.0 → 0.3.2

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.
Files changed (3) hide show
  1. package/README.md +4 -4
  2. package/dist/index.js +39 -26
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -16,13 +16,13 @@ npx -p @raster-app/cli raster whoami
16
16
 
17
17
  ## Authenticate
18
18
 
19
- There are two ways to sign in: an interactive OAuth login tied to your user account, or an organization API key for CI.
19
+ **OAuth is the default** — an interactive login tied to your user account. For CI and automation, use an organization API key instead.
20
20
 
21
21
  ```sh
22
22
  raster auth login
23
23
  ```
24
24
 
25
- With no `--api-key`, `raster auth login` signs you in over OAuth. When a browser is available (an interactive session with a display) it opens a **browser authorization-code flow** with a local loopback redirect; otherwise — over SSH or on a headless machine — it falls back to the **device authorization grant**, printing a short code and a URL you approve in a browser on any device. Pass `--browser` or `--device` to force either.
25
+ `raster auth login` signs you in over OAuth (no API key needed). When a browser is available — an interactive session with a display — it opens a **browser authorization-code flow** with a local loopback redirect; otherwise, over SSH or on a headless machine, it falls back to the **device authorization grant**, printing a short code and a URL you approve in a browser on any device. Pass `--browser` or `--device` to force either.
26
26
 
27
27
  OAuth tokens (access + refresh) are stored in `~/.config/raster/config.json` with owner-only permissions. The CLI refreshes the access token automatically before it expires and rotates the refresh token; when the saved login can no longer be refreshed, it asks you to run `raster auth login` again.
28
28
 
@@ -32,7 +32,7 @@ For CI and scripts, use an organization API key instead (create one in Raster un
32
32
  raster auth login --api-key pk_…
33
33
  ```
34
34
 
35
- Or set `RASTER_API_KEY`. The bearer credential resolves from the `--api-key` flag, then a stored OAuth token, then `RASTER_API_KEY`, then a stored config API key. `raster auth status` shows which credential is active. `raster auth logout` revokes an OAuth token and removes stored credentials.
35
+ Or set `RASTER_API_KEY`. The bearer credential resolves from the `--api-key` flag, then `RASTER_API_KEY`, then a stored OAuth token, then a stored config API key. `raster auth status` shows which credential is active. `raster auth logout` revokes an OAuth token and removes stored credentials.
36
36
 
37
37
  ## Org and library are derived from the key
38
38
 
@@ -135,7 +135,7 @@ Human-readable tables print to stdout; progress and notes go to stderr, so stdou
135
135
 
136
136
  | Variable | Purpose |
137
137
  | --- | --- |
138
- | `RASTER_API_KEY` | API key (sits below a stored OAuth token, above a config-file key) |
138
+ | `RASTER_API_KEY` | API key (overrides a stored OAuth token; sits below `--api-key`, above a config-file key) |
139
139
  | `RASTER_ORG` | Default organization id for an OAuth login when `--org` is omitted |
140
140
  | `RASTER_CONFIG_HOME` | Config directory override (default `~/.config/raster`) |
141
141
  | `RASTER_AUTH_ISSUER` | OAuth authorization-server issuer (default `https://raster.app`; a `raster.app` host or localhost only) |
package/dist/index.js CHANGED
@@ -16337,7 +16337,7 @@ config(en_default());
16337
16337
  // package.json
16338
16338
  var package_default = {
16339
16339
  name: "@raster-app/cli",
16340
- version: "0.3.0",
16340
+ version: "0.3.2",
16341
16341
  description: "Command-line client for the Raster API",
16342
16342
  license: "Apache-2.0",
16343
16343
  type: "module",
@@ -17410,16 +17410,26 @@ async function ensureFreshOAuthSession(options) {
17410
17410
  function resolveCredential(options) {
17411
17411
  if (options.flagKey)
17412
17412
  return { kind: "apiKey", key: options.flagKey, source: "flag" };
17413
- if (options.config.oauth)
17414
- return { kind: "oauth", tokens: options.config.oauth };
17415
17413
  if (options.env.RASTER_API_KEY)
17416
17414
  return { kind: "apiKey", key: options.env.RASTER_API_KEY, source: "env" };
17415
+ if (options.config.oauth)
17416
+ return { kind: "oauth", tokens: options.config.oauth };
17417
17417
  if (options.config.apiKey)
17418
17418
  return { kind: "apiKey", key: options.config.apiKey, source: "config" };
17419
17419
  return null;
17420
17420
  }
17421
17421
 
17422
17422
  // src/lib/output.ts
17423
+ var ANSI = { reset: "\x1B[0m", bold: "\x1B[1m", dim: "\x1B[2m", cyan: "\x1B[36m" };
17424
+ function paint(text, code, enabled) {
17425
+ return enabled ? `${code}${text}${ANSI.reset}` : text;
17426
+ }
17427
+ function bold(text, enabled) {
17428
+ return paint(text, ANSI.bold, enabled);
17429
+ }
17430
+ function dim(text, enabled) {
17431
+ return paint(text, ANSI.dim, enabled);
17432
+ }
17423
17433
  function printJson(output, payload) {
17424
17434
  output.stdout(`${JSON.stringify(payload, null, 2)}
17425
17435
  `);
@@ -17544,6 +17554,7 @@ async function createCommandContext(globals, overrides = {}) {
17544
17554
  const output = {
17545
17555
  json: globals.json === true,
17546
17556
  verbose: globals.verbose === true,
17557
+ color: overrides.stderr === undefined && process.stderr.isTTY === true && env.NO_COLOR === undefined,
17547
17558
  stdout: overrides.stdout ?? ((text) => void process.stdout.write(text)),
17548
17559
  stderr: overrides.stderr ?? ((text) => void process.stderr.write(text))
17549
17560
  };
@@ -17920,7 +17931,7 @@ async function removeAssetsCommand(context, assetIds, options) {
17920
17931
  printJson(context, result);
17921
17932
  return;
17922
17933
  }
17923
- note(context, `Moved ${assetIds.length} asset(s) to trash. They are recoverable from the library trash.`);
17934
+ note(context, `Moved ${assetIds.length} asset(s) to trash.`);
17924
17935
  }
17925
17936
  var describeOptionsSchema = exports_external.object({
17926
17937
  text: exports_external.string().min(1)
@@ -18001,14 +18012,14 @@ async function startLoopbackServer() {
18001
18012
  const code = url2.searchParams.get("code");
18002
18013
  const state = url2.searchParams.get("state");
18003
18014
  if (code && state) {
18004
- response.writeHead(200, { "Content-Type": "text/html" }).end(SUCCESS_PAGE);
18015
+ response.writeHead(200, { "Content-Type": "text/html", Connection: "close" }).end(SUCCESS_PAGE);
18005
18016
  if (!settled) {
18006
18017
  settled = true;
18007
18018
  resolveCallback({ code, state });
18008
18019
  }
18009
18020
  return;
18010
18021
  }
18011
- response.writeHead(400, { "Content-Type": "text/html" }).end(FAILURE_PAGE);
18022
+ response.writeHead(400, { "Content-Type": "text/html", Connection: "close" }).end(FAILURE_PAGE);
18012
18023
  if (!settled) {
18013
18024
  settled = true;
18014
18025
  const error51 = url2.searchParams.get("error");
@@ -18027,6 +18038,7 @@ async function startLoopbackServer() {
18027
18038
  waitForCallback: () => callbackPromise,
18028
18039
  close: () => {
18029
18040
  server.close();
18041
+ server.closeAllConnections?.();
18030
18042
  }
18031
18043
  };
18032
18044
  }
@@ -18088,6 +18100,7 @@ async function runBrowserLogin(options) {
18088
18100
  challenge: pkce.challenge,
18089
18101
  state
18090
18102
  });
18103
+ options.onAuthorizeUrl?.(authorizeUrl);
18091
18104
  await options.openBrowser(authorizeUrl);
18092
18105
  const callback = await withDeadline(server.waitForCallback(), options.loginTimeoutMs ?? DEFAULT_LOGIN_TIMEOUT_MS);
18093
18106
  if (callback.state !== state) {
@@ -18220,7 +18233,8 @@ async function runOAuthLogin(method, deps) {
18220
18233
  clientId,
18221
18234
  fetch: deps.fetch,
18222
18235
  openBrowser: deps.openBrowser,
18223
- now: deps.now
18236
+ now: deps.now,
18237
+ onAuthorizeUrl: deps.onAuthorizeUrl
18224
18238
  });
18225
18239
  }
18226
18240
  const device = await requestDeviceCode({ metadata, clientId, fetch: deps.fetch });
@@ -18276,13 +18290,6 @@ async function loginCommand(context, options = {}) {
18276
18290
  await apiKeyLogin(context, context.apiKey.key);
18277
18291
  return;
18278
18292
  }
18279
- if (!options.device && !options.browser && context.isInteractive) {
18280
- const promptedKey = await context.promptSecret("Paste your Raster API key (or press Enter to use OAuth)");
18281
- if (promptedKey) {
18282
- await apiKeyLogin(context, promptedKey);
18283
- return;
18284
- }
18285
- }
18286
18293
  if (context.env.RASTER_API_KEY && !context.isInteractive && !options.device && !options.browser) {
18287
18294
  await apiKeyLogin(context, context.env.RASTER_API_KEY);
18288
18295
  return;
@@ -18304,7 +18311,7 @@ async function apiKeyLogin(context, key) {
18304
18311
  return;
18305
18312
  }
18306
18313
  const organizationLabel = viewer?.organizationName ?? viewer?.organizationId ?? "your organization";
18307
- note(context, `Logged in to ${organizationLabel} as ${maskApiKey(key)}. Key stored in ${configPath(context.env)}.`);
18314
+ note(context, `Logged in to ${organizationLabel}.`);
18308
18315
  }
18309
18316
  function chooseLoginMethod(context, options) {
18310
18317
  if (options.device && options.browser) {
@@ -18330,11 +18337,17 @@ async function oauthLogin(context, options) {
18330
18337
  now: context.now,
18331
18338
  openBrowser: context.openBrowser,
18332
18339
  onDevicePrompt: (prompt) => {
18333
- note(context, `To sign in, visit ${prompt.verificationUri} and enter the code: ${prompt.userCode}`);
18334
- if (prompt.verificationUriComplete) {
18335
- note(context, `Or open this link directly: ${prompt.verificationUriComplete}`);
18336
- }
18337
- note(context, "Waiting for you to finish signing in…");
18340
+ const target = prompt.verificationUriComplete ? bold(prompt.verificationUriComplete, context.color) : `${bold(prompt.verificationUri, context.color)} and enter code ${bold(prompt.userCode, context.color)}`;
18341
+ note(context, "");
18342
+ note(context, `Visit ${target}`);
18343
+ note(context, "");
18344
+ note(context, dim("Waiting for authentication…", context.color));
18345
+ },
18346
+ onAuthorizeUrl: (url2) => {
18347
+ note(context, "");
18348
+ note(context, `Visit ${bold(url2, context.color)}`);
18349
+ note(context, "");
18350
+ note(context, dim("Waiting for authentication…", context.color));
18338
18351
  }
18339
18352
  });
18340
18353
  await writeOAuthTokens(tokens, context.env);
@@ -18342,7 +18355,7 @@ async function oauthLogin(context, options) {
18342
18355
  printJson(context, { issuer: tokens.issuer, scope: tokens.scope, resource: tokens.resource });
18343
18356
  return;
18344
18357
  }
18345
- note(context, `Logged in via OAuth (${tokens.scope} scope). Tokens stored in ${configPath(context.env)}. ` + "Pass --org <id> or set RASTER_ORG to choose an organization; run `raster whoami` to list them.");
18358
+ note(context, "Logged in.");
18346
18359
  }
18347
18360
  async function logoutCommand(context) {
18348
18361
  const config2 = await readConfig(context.env);
@@ -18358,7 +18371,7 @@ async function logoutCommand(context) {
18358
18371
  delete cleared.apiKey;
18359
18372
  delete cleared.oauth;
18360
18373
  await writeConfig(cleared, context.env);
18361
- note(context, "Logged out. Stored credentials removed.");
18374
+ note(context, "Logged out.");
18362
18375
  }
18363
18376
  async function statusCommand(context) {
18364
18377
  if (!context.credential) {
@@ -18366,7 +18379,7 @@ async function statusCommand(context) {
18366
18379
  printJson(context, { authenticated: false });
18367
18380
  return;
18368
18381
  }
18369
- note(context, "Not authenticated. Run `raster auth login` or set RASTER_API_KEY.");
18382
+ note(context, "Not authenticated. Run `raster auth login`.");
18370
18383
  return;
18371
18384
  }
18372
18385
  if (context.credential.kind === "oauth") {
@@ -18381,8 +18394,8 @@ function statusForOAuth(context, tokens) {
18381
18394
  printJson(context, { authenticated: true, kind: "oauth", issuer: tokens.issuer, scope: tokens.scope, fresh });
18382
18395
  return;
18383
18396
  }
18384
- note(context, `OAuth login on ${tokens.issuer} (${tokens.scope} scope).`);
18385
- note(context, fresh ? "Access token is fresh." : "Access token will be refreshed on the next request.");
18397
+ note(context, `OAuth login on ${tokens.issuer}, ${tokens.scope} scope.`);
18398
+ note(context, fresh ? "Token fresh." : "Token refreshes on next use.");
18386
18399
  }
18387
18400
  async function statusForApiKey(context, key, source) {
18388
18401
  let viewer = null;
@@ -18396,7 +18409,7 @@ async function statusForApiKey(context, key, source) {
18396
18409
  printJson(context, { authenticated: true, kind: "apiKey", source, keyPrefix: maskApiKey(key), organization: viewer ?? null });
18397
18410
  return;
18398
18411
  }
18399
- note(context, `API key ${maskApiKey(key)} (from ${source}).`);
18412
+ note(context, `API key ${maskApiKey(key)}, from ${source}.`);
18400
18413
  if (viewer) {
18401
18414
  note(context, `Organization: ${viewer.organizationName ?? viewer.organizationId ?? "unknown"} · plan: ${viewer.plan ?? "unknown"}`);
18402
18415
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raster-app/cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Command-line client for the Raster API",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",