@dosu/cli 0.13.0 → 0.13.1

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 (2) hide show
  1. package/bin/dosu.js +129 -6
  2. package/package.json +1 -1
package/bin/dosu.js CHANGED
@@ -4341,7 +4341,7 @@ function getSupabaseAnonKey() {
4341
4341
  function getVersionString() {
4342
4342
  return `v${VERSION}`;
4343
4343
  }
4344
- var VERSION = "0.13.0";
4344
+ var VERSION = "0.13.1";
4345
4345
 
4346
4346
  // src/debug/logger.ts
4347
4347
  import {
@@ -9473,7 +9473,85 @@ var init_github_doc_import_step = __esm(() => {
9473
9473
  init_github_doc_import_prompt();
9474
9474
  });
9475
9475
 
9476
+ // src/auth/errors.ts
9477
+ var exports_errors = {};
9478
+ __export(exports_errors, {
9479
+ OAuthCallbackError: () => OAuthCallbackError
9480
+ });
9481
+ var OAuthCallbackError;
9482
+ var init_errors = __esm(() => {
9483
+ OAuthCallbackError = class OAuthCallbackError extends Error {
9484
+ error;
9485
+ errorCode;
9486
+ errorDescription;
9487
+ constructor(message, details = {}) {
9488
+ super(message);
9489
+ this.name = "OAuthCallbackError";
9490
+ this.error = details.error;
9491
+ this.errorCode = details.errorCode;
9492
+ this.errorDescription = details.errorDescription;
9493
+ }
9494
+ get userMessage() {
9495
+ const desc = this.errorDescription ?? this.message;
9496
+ if (this.errorCode === "bad_oauth_state" || /state/i.test(desc)) {
9497
+ return `Authentication failed: ${desc}. Run \`dosu login\` again.`;
9498
+ }
9499
+ return `Authentication failed: ${desc}`;
9500
+ }
9501
+ };
9502
+ });
9503
+
9476
9504
  // src/auth/server.ts
9505
+ function escapeHtml(value) {
9506
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
9507
+ }
9508
+ function buildErrorHtml(message) {
9509
+ const safe = escapeHtml(message);
9510
+ return `<!DOCTYPE html>
9511
+ <html lang="en">
9512
+ <head>
9513
+ <meta charset="UTF-8">
9514
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
9515
+ <title>Dosu CLI - Authentication Failed</title>
9516
+ <style>
9517
+ * { margin: 0; padding: 0; box-sizing: border-box; }
9518
+ body {
9519
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
9520
+ background: #fafafa;
9521
+ color: #171717;
9522
+ min-height: 100vh;
9523
+ display: flex;
9524
+ align-items: center;
9525
+ justify-content: center;
9526
+ padding: 20px;
9527
+ }
9528
+ .container {
9529
+ max-width: 480px;
9530
+ width: 100%;
9531
+ text-align: center;
9532
+ background: white;
9533
+ border: 1px solid #e5e7eb;
9534
+ border-radius: 12px;
9535
+ padding: 40px 32px;
9536
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
9537
+ }
9538
+ h1 { font-size: 20px; font-weight: 600; color: #b91c1c; margin-bottom: 12px; }
9539
+ p { font-size: 14px; color: #4b5563; line-height: 1.5; }
9540
+ .detail { margin-top: 16px; padding: 12px; background: #fef2f2; border-radius: 8px; color: #991b1b; font-size: 13px; }
9541
+ .next { margin-top: 20px; color: #6b7280; font-size: 13px; }
9542
+ code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; background: #f3f4f6; padding: 2px 6px; border-radius: 4px; color: #111827; }
9543
+ </style>
9544
+ </head>
9545
+ <body>
9546
+ <div class="container">
9547
+ <h1>Authentication Failed</h1>
9548
+ <p>The OAuth flow could not be completed.</p>
9549
+ <div class="detail">${safe}</div>
9550
+ <p class="next">You can close this tab. In your terminal, run <code>dosu login</code> again.</p>
9551
+ </div>
9552
+ </body>
9553
+ </html>`;
9554
+ }
9477
9555
  function buildSuccessHtml(email) {
9478
9556
  const emailLine = email ? `<p class="email">Signed in as <strong>${email.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")}</strong></p>` : "";
9479
9557
  return `<!DOCTYPE html>
@@ -9597,8 +9675,10 @@ h1 {
9597
9675
  }
9598
9676
  async function startCallbackServer() {
9599
9677
  let resolveToken;
9600
- const tokenPromise = new Promise((resolve) => {
9678
+ let rejectToken;
9679
+ const tokenPromise = new Promise((resolve, reject) => {
9601
9680
  resolveToken = resolve;
9681
+ rejectToken = reject;
9602
9682
  });
9603
9683
  const http = __require("node:http");
9604
9684
  const httpServer = http.createServer((req, res) => {
@@ -9612,6 +9692,22 @@ async function startCallbackServer() {
9612
9692
  res.end("Not Found");
9613
9693
  return;
9614
9694
  }
9695
+ const errorParam = url.searchParams.get("error");
9696
+ const errorCodeParam = url.searchParams.get("error_code");
9697
+ const errorDescriptionParam = url.searchParams.get("error_description");
9698
+ if (errorParam || errorCodeParam || errorDescriptionParam) {
9699
+ const rawDescription = errorDescriptionParam ?? errorCodeParam ?? errorParam ?? "OAuth authentication failed";
9700
+ const sanitized = rawDescription.replace(/<[^>]*>/g, "");
9701
+ logger.warn("auth.server", `OAuth callback received error: code=${errorCodeParam ?? "n/a"} description=${sanitized}`);
9702
+ rejectToken?.(new OAuthCallbackError(sanitized, {
9703
+ error: errorParam ?? undefined,
9704
+ errorCode: errorCodeParam ?? undefined,
9705
+ errorDescription: sanitized
9706
+ }));
9707
+ res.writeHead(200, { "Content-Type": "text/html" });
9708
+ res.end(buildErrorHtml(rawDescription));
9709
+ return;
9710
+ }
9615
9711
  const accessToken = url.searchParams.get("access_token");
9616
9712
  const refreshToken = url.searchParams.get("refresh_token");
9617
9713
  const expiresIn = url.searchParams.get("expires_in");
@@ -9719,6 +9815,7 @@ if (hash) {
9719
9815
  </html>`;
9720
9816
  var init_server = __esm(() => {
9721
9817
  init_logger();
9818
+ init_errors();
9722
9819
  });
9723
9820
 
9724
9821
  // src/auth/flow.ts
@@ -9739,9 +9836,9 @@ async function startOAuthFlow(signal, path2 = "/cli/auth", params = {}) {
9739
9836
  logger.info("auth.flow", "Browser open command executed");
9740
9837
  const timeout = new Promise((_3, reject) => {
9741
9838
  timeoutId = setTimeout(() => {
9742
- logger.warn("auth.flow", "Authentication timed out (15min)");
9743
- reject(new Error("authentication timeout - please try again"));
9744
- }, 900000);
9839
+ logger.warn("auth.flow", "Authentication timed out (8min)");
9840
+ reject(new Error("Authentication did not complete within 8 minutes. The OAuth state may have expired — please run `dosu login` again."));
9841
+ }, 480000);
9745
9842
  });
9746
9843
  const abort = signal ? new Promise((_3, reject) => {
9747
9844
  signal.addEventListener("abort", () => {
@@ -10106,6 +10203,16 @@ async function openBrowserForSetup(cfg, onboardingRunID) {
10106
10203
  } catch (err) {
10107
10204
  const msg = err instanceof Error ? err.stack ?? err.message : String(err);
10108
10205
  logger.error("setup", `Auth failed: ${msg}`);
10206
+ const { OAuthCallbackError: OAuthCallbackError2 } = await Promise.resolve().then(() => (init_errors(), exports_errors));
10207
+ if (err instanceof OAuthCallbackError2) {
10208
+ M2.error(err.userMessage);
10209
+ if (onboardingRunID) {
10210
+ await trackCliOnboardingPreAuthEvent(onboardingRunID, "cli_onboarding_auth_failed", {
10211
+ reason: err.errorCode ?? err.errorDescription ?? "oauth_callback_error"
10212
+ });
10213
+ }
10214
+ return null;
10215
+ }
10109
10216
  M2.error(`Authentication failed: ${err instanceof Error ? err.message : String(err)}`);
10110
10217
  if (onboardingRunID) {
10111
10218
  await trackCliOnboardingPreAuthEvent(onboardingRunID, "cli_onboarding_auth_failed", {
@@ -10791,6 +10898,11 @@ async function handleAuthenticate(cfg) {
10791
10898
  cfg.expires_at = Math.floor(Date.now() / 1000) + token.expires_in;
10792
10899
  saveConfig(cfg);
10793
10900
  } catch (err) {
10901
+ const { OAuthCallbackError: OAuthCallbackError2 } = await Promise.resolve().then(() => (init_errors(), exports_errors));
10902
+ if (err instanceof OAuthCallbackError2) {
10903
+ M2.error(err.userMessage);
10904
+ return;
10905
+ }
10794
10906
  M2.error(`Authentication failed: ${err instanceof Error ? err.message : String(err)}`);
10795
10907
  }
10796
10908
  }
@@ -12383,7 +12495,18 @@ function createProgram() {
12383
12495
  }
12384
12496
  console.log("Opening browser for authentication...");
12385
12497
  const { startOAuthFlow: startOAuthFlow2 } = await Promise.resolve().then(() => (init_flow(), exports_flow));
12386
- const token = await startOAuthFlow2();
12498
+ const { OAuthCallbackError: OAuthCallbackError2 } = await Promise.resolve().then(() => (init_errors(), exports_errors));
12499
+ let token;
12500
+ try {
12501
+ token = await startOAuthFlow2();
12502
+ } catch (err) {
12503
+ if (err instanceof OAuthCallbackError2) {
12504
+ console.error(err.userMessage);
12505
+ process.exitCode = 1;
12506
+ return;
12507
+ }
12508
+ throw err;
12509
+ }
12387
12510
  cfg.access_token = token.access_token;
12388
12511
  cfg.refresh_token = token.refresh_token;
12389
12512
  cfg.expires_at = Math.floor(Date.now() / 1000) + token.expires_in;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dosu/cli",
3
- "version": "0.13.0",
3
+ "version": "0.13.1",
4
4
  "type": "module",
5
5
  "description": "Dosu CLI - Manage MCP servers for AI tools",
6
6
  "license": "MIT",