Ctrl K
ComunidadGuías…

scripts/dashboard-smoke.mjs

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env node
/**
 * Dashboard operator-flow smoke suite.
 *
 * Boots the REAL server in this process with a throwaway DATA_DIR and an
 * ephemeral loopback port, then drives the dashboard HTTP surface end to end.
 * The upstream is scripted through the product's existing test seams (see
 * test/helpers/dashboard-smoke-upstream.mjs), so no request leaves the box:
 * scripts/mutation-network-deny.mjs is installed as an independent barrier,
 * and a missed seam fails the run with NETWORK_STUB_MISS instead of dialling a
 * real provider.
 *
 * Covered flows (also exported as SMOKE_FLOWS for the CI wrapper):
 *   dashboard HTML · en + zh-CN locale files · models auth · login-method probe
 *   (email-code and password accounts) · email code start/complete · add account
 *   by token · account list · account probe · credit refresh · catalog diff ·
 *   request ring · usage · account delete · secret scan.
 *
 * Usage: node scripts/dashboard-smoke.mjs
 * Exit 0 = every flow passed. Exit 1 = a flow failed (the failing line names it).
 * The last stdout line is always:
 *   DASHBOARD-SMOKE SUMMARY: <passed>/<total> flows passed, <failed> failed
 */

import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { once } from 'node:events';
import { setTimeout as delay } from 'node:timers/promises';

import {
  SMOKE,
  SMOKE_FLOWS,
  SMOKE_SECRETS,
  scriptedLoginTransport,
  scriptedPostJson,
  scriptedCatalogTransport,
  scriptedFetch,
} from '../test/helpers/dashboard-smoke-upstream.mjs';

const DATA_DIR = mkdtempSync(join(tmpdir(), 'windsurfapi-dashboard-smoke-'));

// Everything the product reads at import time is set BEFORE the dynamic
// imports below. PORT=0 lets the OS pick a free loopback port.
Object.assign(process.env, {
  WINDSURFAPI_SKIP_DOTENV: '1',
  DATA_DIR,
  HOST: '127.0.0.1',
  PORT: '0',
  API_KEY: SMOKE.apiKey,
  DASHBOARD_PASSWORD: SMOKE.dashboardPassword,
  DEVIN_CONNECT: '1',
  DEVIN_CONNECT_AUTO_RELOGIN: '0',
  RELOGIN_LIVE: '0',
  WINDSURFAPI_NO_OPEN: '1',
  LOG_LEVEL: 'error',
  NO_COLOR: '1',
});

// Capture everything this process writes (including product logs) so the final
// secret scan covers the real stdout, not just our own PASS lines.
const captured = [];
for (const stream of [process.stdout, process.stderr]) {
  const original = stream.write.bind(stream);
  stream.write = (chunk, encoding, callback) => {
    try {
      captured.push(typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8'));
    } catch { /* capture is best-effort; the CI wrapper scans again */ }
    return original(chunk, encoding, callback);
  };
}

const responseTexts = [];
let server = null;
let base = '';
let passed = 0;
let failed = 0;
let exitCode = 0;
const completed = new Set();

// Seam handles, assigned in main() and cleared in cleanup().
let __setLoginTransportForTests = null;
let __setWindsurfApiPostJsonForTest = null;
let __setCatalogRequestImpl = null;
let __setFetchImplForTest = null;

function out(line) {
  process.stdout.write(`${line}\n`);
}

// `process.exit()` does not wait for a piped stdout buffer. Resolve both write
// callbacks first so the summary line cannot be truncated under CI's pipe.
async function flushStdio() {
  await Promise.all([process.stdout, process.stderr].map((stream) => new Promise((resolve) => {
    try { stream.write('', resolve); } catch { resolve(); }
  })));
}

function requireThat(condition, detail) {
  if (!condition) throw new Error(detail);
}

async function runFlow(name, fn) {
  try {
    const detail = await fn();
    passed++;
    completed.add(name);
    out(`PASS  ${name}${detail ? ` — ${detail}` : ''}`);
    return true;
  } catch (err) {
    failed++;
    exitCode = 1;
    out(`FAIL  ${name} — ${err?.message || String(err)}`);
    return false;
  }
}

async function http(method, path, { body, auth = true } = {}) {
  const headers = {};
  if (body !== undefined) headers['content-type'] = 'application/json';
  if (auth) headers['x-dashboard-password'] = SMOKE.dashboardPassword;
  const res = await fetch(base + path, {
    method,
    headers,
    body: body === undefined ? undefined : JSON.stringify(body),
  });
  const text = await res.text();
  responseTexts.push(text);
  let json = null;
  try { json = text ? JSON.parse(text) : null; } catch { /* non-JSON is checked by callers */ }
  return { status: res.status, json, text, contentType: res.headers.get('content-type') || '' };
}

function readPath(value, path) {
  return path.split('.').reduce((node, key) => (node == null ? undefined : node[key]), value);
}

async function waitFor(predicate, timeoutMs = 5000) {
  const deadline = Date.now() + timeoutMs;
  for (;;) {
    if (predicate()) return true;
    if (Date.now() >= deadline) return false;
    await delay(10);
  }
}

function buildCatalogResponse(writeStringField, writeVarintField, writeMessageField) {
  const row = Buffer.concat([
    writeStringField(1, SMOKE.liveLabel),
    writeStringField(22, SMOKE.liveSelector),
    writeVarintField(10, 2), // provider: OpenAI
    writeMessageField(23, writeStringField(23, SMOKE.liveAlias)),
  ]);
  return writeMessageField(1, row);
}

function buildStatusResponse(writeStringField, writeVarintField, writeMessageField) {
  const plan = writeStringField(2, 'Pro');
  const billing = Buffer.concat([
    writeMessageField(1, plan),
    writeVarintField(16, 12_500_000), // balance, micro-USD → $12.50
    writeVarintField(17, 1_790_000_000), // daily quota reset, epoch seconds
    writeVarintField(18, 1_790_604_800), // weekly quota reset, epoch seconds
  ]);
  return writeMessageField(1, writeMessageField(13, billing));
}

const REST_STATUS = {
  userStatus: {
    planStatus: {
      planInfo: { planName: 'Pro' },
      dailyQuotaRemainingPercent: 42,
      weeklyQuotaRemainingPercent: 88,
      dailyQuotaResetAtUnix: 1_790_000_000,
      weeklyQuotaResetAtUnix: 1_790_604_800,
      usedPromptCredits: 1000,
      availablePromptCredits: 4000,
      usedFlexCredits: 0,
      availableFlexCredits: 0,
    },
  },
};

const USAGE_QUOTA = {
  is_quota_plan: true,
  has_quota_allocation: true,
  daily_percentage: 42.5,
  weekly_percentage: 17,
  daily_reset_at: '2026-10-23T00:00:00Z',
  weekly_reset_at: '2026-10-27T00:00:00Z',
  overage_balance: 0,
  hide_daily_quota: false,
};
const USAGE_STATUS = {
  plan_slug: 'pro',
  is_subscription_valid: true,
  available_credits: 1234,
  overage_credits: 0,
  billing_method: 'card',
};
const USAGE_LIMITS = { max_acu_limit: 100, max_total_session_acu_limit: 50 };
const USAGE_DAILY = {
  days: [{ date: '2026-10-22', amount: 5, cumulative: 5 }],
  products: [{ view: 'chat', total: 5, days: [{ date: '2026-10-22', amount: 5, cumulative: 5 }] }],
  total: 5,
  cycle_start: '2026-10-01',
  cycle_end: '2026-11-01',
  previous_cycle_available: true,
};

async function main() {
  // Independent egress barrier. Importing the same URL the CI wrapper preloads
  // is a module-cache no-op, so manual runs get the guard too.
  await import('../scripts/mutation-network-deny.mjs');

  const { startServer } = await import('../src/server.js');
  const { initAuth } = await import('../src/auth.js');
  const loginModule = await import('../src/dashboard/windsurf-login.js');
  const windsurfApiModule = await import('../src/windsurf-api.js');
  const catalogModule = await import('../src/devin-connect-catalog.js');
  const devinReadModule = await import('../src/dashboard/devin-read.js');
  const { getLiveCatalog } = await import('../src/devin-connect-models.js');
  const { recordRequest, __resetRequestRingForTest } = await import('../src/dashboard/request-ring.js');
  const { writeStringField, writeVarintField, writeMessageField } = await import('../src/proto.js');

  __setLoginTransportForTests = loginModule.__setLoginTransportForTests;
  __setWindsurfApiPostJsonForTest = windsurfApiModule.__setWindsurfApiPostJsonForTest;
  __setCatalogRequestImpl = catalogModule.__setCatalogRequestImpl;
  __setFetchImplForTest = devinReadModule.__setFetchImplForTest;
  const resetEmailFlows = loginModule._resetEmailFlowsForTests;
  const resetEmailLockout = loginModule._resetEmailLockoutForTests;
  const resetReadCache = devinReadModule.__resetReadCache;

  const catalogBytes = buildCatalogResponse(writeStringField, writeVarintField, writeMessageField);
  const statusBytes = buildStatusResponse(writeStringField, writeVarintField, writeMessageField);

  const loginTransport = scriptedLoginTransport([
    {
      match: (url) => url.includes('/_devin-auth/connections'),
      reply: (_url, body) => ({
        json: body?.email === SMOKE.emailHasPassword
          ? {
            connections: [{ id: 'password', type: 'password', enabled: true }],
            auth_method: { method: 'auth1', has_password: true },
          }
          : {
            connections: [{ id: 'email', type: 'email', enabled: true }],
            auth_method: { method: 'auth1', has_password: false },
          },
      }),
    },
    {
      match: (url) => url.includes('/_devin-auth/email/start'),
      reply: { json: { email_verification_token: SMOKE.verifyToken } },
    },
    {
      match: (url) => url.includes('/_devin-auth/email/complete'),
      reply: { json: { token: SMOKE.auth1Token } },
    },
    {
      match: (url) => url.includes('WindsurfPostAuth'),
      reply: {
        buffer: Buffer.from(JSON.stringify({
          sessionToken: SMOKE.sessionTokenEmail,
          accountId: 'account-smoke-email',
        })),
      },
    },
  ]);

  const postJson = scriptedPostJson([
    {
      match: (path) => path.includes('GetCascadeModelConfigs'),
      reply: { json: { clientModelConfigs: [], clientModelSorts: [] } },
    },
    { match: (path) => path.includes('GetUserStatus'), reply: { json: REST_STATUS } },
  ]);

  const catalogTransport = scriptedCatalogTransport([
    { match: (path) => path.includes('GetCliModelConfigs'), reply: { buffer: catalogBytes } },
    { match: (path) => path.includes('GetUserStatus'), reply: { buffer: statusBytes } },
  ]);

  const fetchImpl = scriptedFetch([
    {
      match: (url) => url.includes('/users/current-membership'),
      reply: { json: { org: { org_id: SMOKE.orgId } } },
    },
    { match: (url) => url.includes('/billing/quota/usage'), reply: { json: USAGE_QUOTA } },
    { match: (url) => url.includes('/billing/status'), reply: { json: USAGE_STATUS } },
    { match: (url) => url.includes('/billing/usage/limits'), reply: { json: USAGE_LIMITS } },
    { match: (url) => url.includes('/billing/usage/daily-usage'), reply: { json: USAGE_DAILY } },
  ]);

  __setLoginTransportForTests(loginTransport);
  __setWindsurfApiPostJsonForTest(postJson);
  __setCatalogRequestImpl(catalogTransport);
  __setFetchImplForTest(fetchImpl);
  resetReadCache();
  resetEmailFlows();
  resetEmailLockout();
  __resetRequestRingForTest();

  await initAuth();
  server = startServer();
  if (!server.listening) await once(server, 'listening');
  base = `http://127.0.0.1:${server.address().port}`;

  let emailAccountId = '';
  let tokenAccountId = '';
  let flowId = '';

  const flows = [
    ['dashboard-html', async () => {
      const r = await http('GET', '/dashboard', { auth: false });
      requireThat(r.status === 200, `status ${r.status}`);
      requireThat(/text\/html/i.test(r.contentType), `content-type ${r.contentType}`);
      requireThat(r.text.includes('id="lang-toggle-btn"'), 'language toggle marker missing');
      requireThat(r.text.includes('data-i18n="nav.overview"'), 'i18n marker missing');
      return '200 text/html, i18n markers present';
    }],

    ['locale-en', async () => {
      const r = await http('GET', '/dashboard/i18n/en.json', { auth: false });
      requireThat(r.status === 200, `status ${r.status}`);
      requireThat(readPath(r.json, 'nav.overview') === 'Dashboard', 'nav.overview mismatch');
      requireThat(typeof readPath(r.json, 'emailLogin.methodEmailCode') === 'string', 'emailLogin.methodEmailCode missing');
      requireThat(typeof readPath(r.json, 'section.catalogDiff.title') === 'string', 'section.catalogDiff.title missing');
      return 'en.json 200, 3 keys';
    }],

    ['locale-zh-CN', async () => {
      const en = await http('GET', '/dashboard/i18n/en.json', { auth: false });
      const r = await http('GET', '/dashboard/i18n/zh-CN.json', { auth: false });
      requireThat(r.status === 200, `status ${r.status}`);
      requireThat(readPath(r.json, 'nav.overview') === '仪表盘', 'nav.overview mismatch');
      requireThat(typeof readPath(r.json, 'emailLogin.methodPassword') === 'string', 'emailLogin.methodPassword missing');
      requireThat(readPath(r.json, 'nav.overview') !== readPath(en.json, 'nav.overview'), 'zh-CN and en are identical');
      return 'zh-CN.json 200, distinct translation';
    }],

    ['models-auth', async () => {
      const unauth = await http('GET', '/dashboard/api/models', { auth: false });
      requireThat(unauth.status === 401, `unauthenticated status ${unauth.status}`);
      const authed = await http('GET', '/dashboard/api/models');
      requireThat(authed.status === 200, `authenticated status ${authed.status}`);
      requireThat(Array.isArray(authed.json?.models) && authed.json.models.length > 0, 'models array empty');
      return `401 without password, 200 with ${authed.json.models.length} models`;
    }],

    ['login-probe-no-password', async () => {
      const r = await http('POST', '/dashboard/api/windsurf-login/probe', {
        body: { email: SMOKE.emailNoPassword },
      });
      requireThat(r.status === 200 && r.json?.ok === true, `status ${r.status} error ${r.json?.error || '?'}`);
      requireThat(r.json.hasPassword === false, `hasPassword=${r.json.hasPassword}`);
      requireThat(r.json.emailCode === true, `emailCode=${r.json.emailCode}`);
      requireThat(r.json.userExists === true, `userExists=${r.json.userExists}`);
      requireThat(!r.text.includes(SMOKE.verifyToken), 'verification token echoed by probe');
      requireThat(
        loginTransport.seen.some((url) => url.includes('/_devin-auth/connections')),
        'probe never reached the scripted connections endpoint',
      );
      return 'hasPassword:false → email_code';
    }],

    ['login-probe-has-password', async () => {
      const r = await http('POST', '/dashboard/api/windsurf-login/probe', {
        body: { email: SMOKE.emailHasPassword },
      });
      requireThat(r.status === 200 && r.json?.ok === true, `status ${r.status} error ${r.json?.error || '?'}`);
      requireThat(r.json.hasPassword === true, `hasPassword=${r.json.hasPassword}`);
      requireThat(r.json.emailCode === false, `emailCode=${r.json.emailCode}`);
      requireThat(r.json.method === 'auth1', `method=${r.json.method}`);
      requireThat(
        loginTransport.seen.some((url) => url.includes('/_devin-auth/connections')),
        'probe never reached the scripted connections endpoint',
      );
      return 'hasPassword:true → password form';
    }],

    ['email-start', async () => {
      const r = await http('POST', '/dashboard/api/windsurf-login/email/start', {
        body: { email: SMOKE.emailNoPassword },
      });
      requireThat(r.status === 200 && r.json?.sent === true, `status ${r.status} error ${r.json?.error || '?'}`);
      requireThat(typeof r.json.flowId === 'string' && r.json.flowId.length >= 16, 'opaque flowId missing');
      requireThat(!r.text.includes(SMOKE.verifyToken), 'verification token echoed by start');
      requireThat(
        loginTransport.seen.some((url) => url.includes('/_devin-auth/email/start')),
        'start never reached the scripted email/start endpoint',
      );
      flowId = r.json.flowId;
      return 'flowId issued, verification token kept server-side';
    }],

    ['email-complete', async () => {
      const r = await http('POST', '/dashboard/api/windsurf-login/email/complete', {
        body: { flowId, code: SMOKE.emailCode },
      });
      requireThat(r.status === 200 && r.json?.success === true, `status ${r.status} error ${r.json?.error || '?'}`);
      requireThat(r.json.method === 'email_code', `method=${r.json.method}`);
      requireThat(r.json.email === SMOKE.emailNoPassword, `email=${r.json.email}`);
      requireThat(typeof r.json.account?.id === 'string' && r.json.account.id, 'account not auto-added');
      requireThat(!r.text.includes(SMOKE.sessionTokenEmail), 'raw session token echoed by complete');
      requireThat(!r.text.includes(SMOKE.auth1Token), 'Auth1 token echoed by complete');
      requireThat(
        loginTransport.seen.some((url) => url.includes('/_devin-auth/email/complete')),
        'complete never reached the scripted email/complete endpoint',
      );
      requireThat(
        loginTransport.seen.some((url) => url.includes('WindsurfPostAuth')),
        'complete never exchanged the Auth1 token through PostAuth',
      );
      emailAccountId = r.json.account.id;
      return `logged in + auto-added ${emailAccountId}, key masked`;
    }],

    ['account-add-token', async () => {
      const r = await http('POST', '/dashboard/api/accounts', {
        body: { token: SMOKE.sessionTokenAdded, label: 'smoke-token' },
      });
      requireThat(r.status === 200 && r.json?.success === true, `status ${r.status} error ${r.json?.error || '?'}`);
      requireThat(typeof r.json.account?.id === 'string' && r.json.account.id, 'account id missing');
      requireThat(!r.text.includes(SMOKE.sessionTokenAdded), 'pasted token echoed by add');
      tokenAccountId = r.json.account.id;
      return `added ${tokenAccountId} from a pasted session token`;
    }],

    ['account-list', async () => {
      const r = await http('GET', '/dashboard/api/accounts');
      requireThat(r.status === 200 && Array.isArray(r.json?.accounts), `status ${r.status}`);
      const ids = r.json.accounts.map((a) => a.id);
      requireThat(ids.includes(emailAccountId), 'email-code account missing from list');
      requireThat(ids.includes(tokenAccountId), 'token account missing from list');
      requireThat(!r.text.includes(SMOKE.sessionTokenEmail), 'email session token in account list');
      requireThat(!r.text.includes(SMOKE.sessionTokenAdded), 'pasted session token in account list');
      return `${r.json.accounts.length} accounts, credentials masked`;
    }],

    ['account-probe', async () => {
      const r = await http('POST', `/dashboard/api/accounts/${encodeURIComponent(tokenAccountId)}/probe`, { body: {} });
      requireThat(r.status === 200 && r.json?.success === true, `status ${r.status} error ${r.json?.error || '?'}`);
      requireThat(r.json.skipped === true || typeof r.json.tier === 'string', 'probe returned neither a tier nor a skip reason');
      return r.json.skipped ? `skipped: ${r.json.reason}` : `tier: ${r.json.tier}`;
    }],

    ['refresh-credits', async () => {
      const r = await http('POST', `/dashboard/api/accounts/${encodeURIComponent(tokenAccountId)}/refresh-credits`, { body: {} });
      requireThat(r.status === 200 && r.json?.ok === true, `status ${r.status} error ${r.json?.error || '?'}`);
      requireThat(r.json.credits?.planName === 'Pro', `planName=${r.json.credits?.planName}`);
      requireThat(r.json.credits?.dailyPercent === 42, `dailyPercent=${r.json.credits?.dailyPercent}`);
      requireThat(r.json.credits?.balance === 12.5, `balance=${r.json.credits?.balance}`);
      requireThat(
        postJson.seen.some((entry) => entry.includes('GetUserStatus')),
        'credit refresh never called the scripted REST GetUserStatus',
      );
      requireThat(
        catalogTransport.seen.some((entry) => entry.includes('GetUserStatus')),
        'credit refresh never called the scripted Connect GetUserStatus',
      );
      return 'REST status + billing RPC merged (plan Pro, $12.50, 42% daily)';
    }],

    ['catalog-diff', async () => {
      const synced = await waitFor(() => getLiveCatalog().length > 0);
      requireThat(synced, 'live catalog did not sync through the scripted GetCliModelConfigs transport');
      const live = getLiveCatalog();
      requireThat(
        live.some((row) => (typeof row === 'string' ? row : row?.selector) === SMOKE.liveSelector),
        'scripted live selector missing from the synced catalog',
      );
      const r = await http('GET', '/dashboard/api/models/catalog');
      requireThat(r.status === 200, `status ${r.status}`);
      requireThat(r.json?.synced === true, 'catalog reported unsynced');
      requireThat(r.json.liveCount >= 1, `liveCount=${r.json.liveCount}`);
      requireThat(r.json.snapshotCount > 0, `snapshotCount=${r.json.snapshotCount}`);
      requireThat(r.json.added.includes(SMOKE.liveSelector), 'live-only selector missing from added[]');
      requireThat(
        catalogTransport.seen.some((entry) => entry.includes('GetCliModelConfigs')),
        'catalog diff did not ride a scripted GetCliModelConfigs sync',
      );
      return `live ${r.json.liveCount} vs snapshot ${r.json.snapshotCount}, added includes ${SMOKE.liveSelector}`;
    }],

    ['request-ring', async () => {
      recordRequest({
        model: 'smoke-ring-model',
        selector: SMOKE.liveSelector,
        status: 200,
        stream: false,
        durationMs: 12,
        class: 'success',
        protocol: 'openai',
        caller: SMOKE.apiKey,
      });
      const r = await http('GET', '/dashboard/api/requests?limit=10');
      requireThat(r.status === 200 && Array.isArray(r.json?.items), `status ${r.status}`);
      const row = r.json.items.find((item) => item.model === 'smoke-ring-model');
      requireThat(!!row, 'seeded ring row missing');
      requireThat(row.status === 200 && row.caller !== SMOKE.apiKey, 'ring row malformed or caller not hashed');
      requireThat(r.json.ring?.total >= 1, 'ring stats not reported');
      return `row served, caller hashed, ring total ${r.json.ring.total}`;
    }],

    ['devin-panel', async () => {
      const r = await http('GET', '/dashboard/api/devin/panel');
      requireThat(r.status === 200 && r.json?.ok === true, `status ${r.status} error ${r.json?.error || '?'}`);
      const runtime = r.json.runtime || {};
      requireThat(Array.isArray(runtime.switches) && runtime.switches.length === 6, 'runtime switch rows missing');
      requireThat(runtime.credentials?.writable === false, 'credential-store switches must stay read-only');
      const sources = r.json.sessions?.sources || [];
      const byId = Object.fromEntries(sources.map((s) => [s.id, s]));
      requireThat(byId.proxyTurns?.available === true, 'proxy turn source must be available');
      requireThat(byId.cascadeTrajectories?.reason === 'ls_only', 'cascade trajectory source must state ls_only');
      requireThat(byId.acpSessions?.reason === 'not_tracked', 'ACP source must state not_tracked');
      requireThat(Array.isArray(r.json.sessions?.turns?.items), 'turns.items missing');
      requireThat(Array.isArray(r.json.quota?.knobs) && r.json.quota.knobs.length === 5, 'quota knobs missing');
      requireThat(!r.text.includes(SMOKE.sessionTokenAdded), 'pasted session token in panel body');
      requireThat(!r.text.includes(SMOKE.sessionTokenEmail), 'email session token in panel body');
      return `4 sections, ${sources.length} sources, ${r.json.sessions.turns.retained} retained turns`;
    }],

    ['usage', async () => {
      const r = await http('GET', `/dashboard/api/usage?account=${encodeURIComponent(tokenAccountId)}&refresh=1`);
      requireThat(r.status === 200 && r.json?.ok === true, `status ${r.status} error ${r.json?.error || '?'}`);
      requireThat(r.json.plan === 'pro', `plan=${r.json.plan}`);
      requireThat(r.json.availableCredits === 1234, `availableCredits=${r.json.availableCredits}`);
      requireThat(r.json.quota?.dailyPercentage === 42.5, `dailyPercentage=${r.json.quota?.dailyPercentage}`);
      requireThat(Object.values(r.json.sources || {}).every((s) => s.ok), 'a usage source failed');
      for (const path of [
        '/users/current-membership',
        '/billing/quota/usage',
        '/billing/status',
        '/billing/usage/limits',
        '/billing/usage/daily-usage',
      ]) {
        requireThat(fetchImpl.seen.some((url) => url.includes(path)), `usage never read ${path}`);
      }
      return 'quota/status/limits/daily normalized from app.devin.ai fake';
    }],

    ['account-delete', async () => {
      const del = await http('DELETE', `/dashboard/api/accounts/${encodeURIComponent(tokenAccountId)}`);
      requireThat(del.status === 200 && del.json?.success === true, `delete status ${del.status}`);
      const after = await http('GET', '/dashboard/api/accounts');
      requireThat(after.status === 200 && Array.isArray(after.json?.accounts), `list status ${after.status}`);
      const ids = after.json.accounts.map((a) => a.id);
      requireThat(!ids.includes(tokenAccountId), 'deleted account still listed');
      requireThat(ids.includes(emailAccountId), 'unrelated account disappeared');
      return 'deleted account gone, email-code account retained';
    }],
  ];

  for (const [name, fn] of flows) {
    if (!(await runFlow(name, fn))) break;
  }

  // Always scan, even after a failed flow: a leak is the one failure that must
  // never be hidden by an earlier red line.
  await runFlow('no-secret-leak', async () => {
    const haystack = [...captured, ...responseTexts].join('\n');
    for (const secret of SMOKE_SECRETS) {
      requireThat(!haystack.includes(secret.value), `${secret.label} appeared in stdout/stderr or an HTTP body`);
    }
    return `${SMOKE_SECRETS.length} synthetic secrets absent from output and API bodies`;
  });

  if (exitCode === 0) {
    const missing = SMOKE_FLOWS.filter((name) => !completed.has(name));
    if (missing.length) {
      failed++;
      exitCode = 1;
      out(`FAIL  flow-manifest — unrun: ${missing.join(', ')}`);
    }
  }
}

async function cleanup() {
  try { __setLoginTransportForTests?.(null); } catch { /* best-effort */ }
  try { __setWindsurfApiPostJsonForTest?.(null); } catch { /* best-effort */ }
  try { __setCatalogRequestImpl?.(null); } catch { /* best-effort */ }
  try { __setFetchImplForTest?.(null); } catch { /* best-effort */ }
  if (server) {
    await new Promise((resolve) => {
      let settled = false;
      const finish = () => { if (!settled) { settled = true; resolve(); } };
      server.close(finish);
      server.closeAllConnections?.();
      const timer = setTimeout(finish, 2000);
      timer.unref?.();
    });
  }
  try { rmSync(DATA_DIR, { recursive: true, force: true }); } catch { /* best-effort */ }
}

try {
  await main();
} catch (err) {
  failed++;
  exitCode = 1;
  out(`FAIL  boot — ${err?.message || String(err)}`);
}

await cleanup();
out(`DASHBOARD-SMOKE SUMMARY: ${passed}/${SMOKE_FLOWS.length} flows passed, ${failed} failed`);
await flushStdio();
process.exit(exitCode);