@vibedrift/cli 0.3.0 → 0.3.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.
package/dist/index.js CHANGED
@@ -7889,6 +7889,104 @@ function previewToken(token) {
7889
7889
  return token.slice(0, 12) + "\u2026";
7890
7890
  }
7891
7891
 
7892
+ // src/auth/api.ts
7893
+ init_esm_shims();
7894
+ var REQUEST_TIMEOUT_MS = 3e4;
7895
+ var VibeDriftApiError = class extends Error {
7896
+ constructor(status, message) {
7897
+ super(message);
7898
+ this.status = status;
7899
+ this.name = "VibeDriftApiError";
7900
+ }
7901
+ status;
7902
+ };
7903
+ async function jsonFetch(url, init = {}) {
7904
+ const controller = new AbortController();
7905
+ const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
7906
+ try {
7907
+ const res = await fetch(url, {
7908
+ ...init,
7909
+ signal: controller.signal,
7910
+ headers: {
7911
+ Accept: "application/json",
7912
+ ...init.body ? { "Content-Type": "application/json" } : {},
7913
+ ...init.headers ?? {}
7914
+ }
7915
+ });
7916
+ if (!res.ok) {
7917
+ let detail = "";
7918
+ try {
7919
+ const body = await res.json();
7920
+ detail = body.detail ?? body.message ?? "";
7921
+ } catch {
7922
+ try {
7923
+ detail = await res.text();
7924
+ } catch {
7925
+ }
7926
+ }
7927
+ throw new VibeDriftApiError(res.status, detail || `HTTP ${res.status}`);
7928
+ }
7929
+ return await res.json();
7930
+ } catch (err) {
7931
+ if (err?.name === "AbortError") {
7932
+ throw new VibeDriftApiError(0, `Request timed out after ${REQUEST_TIMEOUT_MS / 1e3}s`);
7933
+ }
7934
+ if (err instanceof VibeDriftApiError) throw err;
7935
+ throw new VibeDriftApiError(0, err?.message ?? String(err));
7936
+ } finally {
7937
+ clearTimeout(timer);
7938
+ }
7939
+ }
7940
+ async function startDeviceAuth(opts) {
7941
+ const base = await resolveApiUrl(opts?.apiUrl);
7942
+ return jsonFetch(`${base}/auth/device`, {
7943
+ method: "POST",
7944
+ body: JSON.stringify({ client_id: "vibedrift-cli" })
7945
+ });
7946
+ }
7947
+ async function pollDeviceAuth(deviceCode, opts) {
7948
+ const base = await resolveApiUrl(opts?.apiUrl);
7949
+ return jsonFetch(`${base}/auth/poll`, {
7950
+ method: "POST",
7951
+ body: JSON.stringify({ device_code: deviceCode })
7952
+ });
7953
+ }
7954
+ async function validateToken(token, opts) {
7955
+ const base = await resolveApiUrl(opts?.apiUrl);
7956
+ return jsonFetch(`${base}/auth/validate`, {
7957
+ method: "GET",
7958
+ headers: { Authorization: `Bearer ${token}` }
7959
+ });
7960
+ }
7961
+ async function revokeToken(token, opts) {
7962
+ const base = await resolveApiUrl(opts?.apiUrl);
7963
+ await jsonFetch(`${base}/auth/revoke`, {
7964
+ method: "POST",
7965
+ headers: { Authorization: `Bearer ${token}` }
7966
+ });
7967
+ }
7968
+ async function fetchUsage(token, opts) {
7969
+ const base = await resolveApiUrl(opts?.apiUrl);
7970
+ return jsonFetch(`${base}/account/usage`, {
7971
+ method: "GET",
7972
+ headers: { Authorization: `Bearer ${token}` }
7973
+ });
7974
+ }
7975
+ async function fetchCredits(token, opts) {
7976
+ const base = await resolveApiUrl(opts?.apiUrl);
7977
+ return jsonFetch(`${base}/account/credits`, {
7978
+ method: "GET",
7979
+ headers: { Authorization: `Bearer ${token}` }
7980
+ });
7981
+ }
7982
+ async function createPortalSession(token, opts) {
7983
+ const base = await resolveApiUrl(opts?.apiUrl);
7984
+ return jsonFetch(`${base}/account/portal`, {
7985
+ method: "POST",
7986
+ headers: { Authorization: `Bearer ${token}` }
7987
+ });
7988
+ }
7989
+
7892
7990
  // src/cli/commands/scan.ts
7893
7991
  async function runScan(targetPath, options) {
7894
7992
  const rootDir = resolve(targetPath);
@@ -7910,7 +8008,9 @@ async function runScan(targetPath, options) {
7910
8008
  console.error("");
7911
8009
  console.error(chalk2.red(" \u2717 Deep scans require a VibeDrift account."));
7912
8010
  console.error("");
7913
- console.error(" Run " + chalk2.bold("vibedrift login") + " to sign in.");
8011
+ console.error(chalk2.bgYellow.black.bold(" \u{1F381} But your first deep scan is FREE. "));
8012
+ console.error("");
8013
+ console.error(" Run " + chalk2.bold("vibedrift login") + " to sign in and claim it.");
7914
8014
  console.error(" Or set " + chalk2.bold("VIBEDRIFT_TOKEN") + " in your environment for CI.");
7915
8015
  console.error("");
7916
8016
  process.exit(1);
@@ -7927,6 +8027,25 @@ async function runScan(targetPath, options) {
7927
8027
  } catch {
7928
8028
  }
7929
8029
  }
8030
+ if (!options.json && options.format !== "json" && !options.deep) {
8031
+ if (bearerToken) {
8032
+ try {
8033
+ const credits = await fetchCredits(bearerToken, { apiUrl });
8034
+ if (credits.has_free_deep_scan && !credits.unlimited) {
8035
+ console.log("");
8036
+ console.log(chalk2.bgYellow.black.bold(" \u{1F381} 1 FREE DEEP SCAN AVAILABLE "));
8037
+ console.log(chalk2.yellow(" Run with ") + chalk2.bold.cyan("--deep") + chalk2.yellow(" to use Claude-powered analysis (no card required)."));
8038
+ console.log("");
8039
+ }
8040
+ } catch {
8041
+ }
8042
+ } else {
8043
+ console.log("");
8044
+ console.log(chalk2.dim(" Tip: ") + chalk2.yellow("sign up free to get 1 deep scan included"));
8045
+ console.log(chalk2.dim(" Run ") + chalk2.bold("vibedrift login") + chalk2.dim(" to claim it (no card required)."));
8046
+ console.log("");
8047
+ }
8048
+ }
7930
8049
  const startTime = Date.now();
7931
8050
  const timings = {};
7932
8051
  const isTerminal = options.format === "terminal" && !options.json;
@@ -8281,97 +8400,6 @@ Update failed: ${message}`));
8281
8400
  init_esm_shims();
8282
8401
  import chalk4 from "chalk";
8283
8402
 
8284
- // src/auth/api.ts
8285
- init_esm_shims();
8286
- var REQUEST_TIMEOUT_MS = 3e4;
8287
- var VibeDriftApiError = class extends Error {
8288
- constructor(status, message) {
8289
- super(message);
8290
- this.status = status;
8291
- this.name = "VibeDriftApiError";
8292
- }
8293
- status;
8294
- };
8295
- async function jsonFetch(url, init = {}) {
8296
- const controller = new AbortController();
8297
- const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
8298
- try {
8299
- const res = await fetch(url, {
8300
- ...init,
8301
- signal: controller.signal,
8302
- headers: {
8303
- Accept: "application/json",
8304
- ...init.body ? { "Content-Type": "application/json" } : {},
8305
- ...init.headers ?? {}
8306
- }
8307
- });
8308
- if (!res.ok) {
8309
- let detail = "";
8310
- try {
8311
- const body = await res.json();
8312
- detail = body.detail ?? body.message ?? "";
8313
- } catch {
8314
- try {
8315
- detail = await res.text();
8316
- } catch {
8317
- }
8318
- }
8319
- throw new VibeDriftApiError(res.status, detail || `HTTP ${res.status}`);
8320
- }
8321
- return await res.json();
8322
- } catch (err) {
8323
- if (err?.name === "AbortError") {
8324
- throw new VibeDriftApiError(0, `Request timed out after ${REQUEST_TIMEOUT_MS / 1e3}s`);
8325
- }
8326
- if (err instanceof VibeDriftApiError) throw err;
8327
- throw new VibeDriftApiError(0, err?.message ?? String(err));
8328
- } finally {
8329
- clearTimeout(timer);
8330
- }
8331
- }
8332
- async function startDeviceAuth(opts) {
8333
- const base = await resolveApiUrl(opts?.apiUrl);
8334
- return jsonFetch(`${base}/auth/device`, {
8335
- method: "POST",
8336
- body: JSON.stringify({ client_id: "vibedrift-cli" })
8337
- });
8338
- }
8339
- async function pollDeviceAuth(deviceCode, opts) {
8340
- const base = await resolveApiUrl(opts?.apiUrl);
8341
- return jsonFetch(`${base}/auth/poll`, {
8342
- method: "POST",
8343
- body: JSON.stringify({ device_code: deviceCode })
8344
- });
8345
- }
8346
- async function validateToken(token, opts) {
8347
- const base = await resolveApiUrl(opts?.apiUrl);
8348
- return jsonFetch(`${base}/auth/validate`, {
8349
- method: "GET",
8350
- headers: { Authorization: `Bearer ${token}` }
8351
- });
8352
- }
8353
- async function revokeToken(token, opts) {
8354
- const base = await resolveApiUrl(opts?.apiUrl);
8355
- await jsonFetch(`${base}/auth/revoke`, {
8356
- method: "POST",
8357
- headers: { Authorization: `Bearer ${token}` }
8358
- });
8359
- }
8360
- async function fetchUsage(token, opts) {
8361
- const base = await resolveApiUrl(opts?.apiUrl);
8362
- return jsonFetch(`${base}/account/usage`, {
8363
- method: "GET",
8364
- headers: { Authorization: `Bearer ${token}` }
8365
- });
8366
- }
8367
- async function createPortalSession(token, opts) {
8368
- const base = await resolveApiUrl(opts?.apiUrl);
8369
- return jsonFetch(`${base}/account/portal`, {
8370
- method: "POST",
8371
- headers: { Authorization: `Bearer ${token}` }
8372
- });
8373
- }
8374
-
8375
8403
  // src/auth/browser.ts
8376
8404
  init_esm_shims();
8377
8405
  import { spawn as spawn2 } from "child_process";
@@ -8478,11 +8506,43 @@ async function runLogin(options = {}) {
8478
8506
  console.log(` Account: ${chalk4.bold(result.email)}`);
8479
8507
  console.log(` Plan: ${chalk4.bold(result.plan)}`);
8480
8508
  console.log("");
8481
- if (result.plan === "free") {
8482
- console.log(chalk4.dim(" Run `vibedrift upgrade` to enable deep AI scans."));
8483
- console.log("");
8484
- } else {
8485
- console.log(chalk4.dim(" Run `vibedrift . --deep` to use AI-powered analysis."));
8509
+ try {
8510
+ const credits = await fetchCredits(result.access_token, {
8511
+ apiUrl: options.apiUrl
8512
+ });
8513
+ if (credits.has_free_deep_scan && !credits.unlimited) {
8514
+ console.log(
8515
+ chalk4.bgYellow.black.bold(" \u{1F381} 1 FREE deep scan included with your account ")
8516
+ );
8517
+ console.log("");
8518
+ console.log(
8519
+ chalk4.yellow(" Try the full pipeline (Claude analysis, security review,")
8520
+ );
8521
+ console.log(
8522
+ chalk4.yellow(" AI-powered drift detection) on any project \u2014 no card needed.")
8523
+ );
8524
+ console.log("");
8525
+ console.log(` ${chalk4.cyan("vibedrift . --deep")}`);
8526
+ console.log("");
8527
+ } else if (credits.unlimited) {
8528
+ console.log(chalk4.dim(" Run `vibedrift . --deep` to use AI-powered analysis."));
8529
+ console.log("");
8530
+ } else if (credits.available_total > 0) {
8531
+ console.log(
8532
+ chalk4.dim(` You have ${credits.available_total} deep scan credit${credits.available_total === 1 ? "" : "s"} available.`)
8533
+ );
8534
+ console.log(chalk4.dim(" Run `vibedrift . --deep` to use one."));
8535
+ console.log("");
8536
+ } else {
8537
+ console.log(chalk4.dim(" Run `vibedrift upgrade` to enable deep AI scans."));
8538
+ console.log("");
8539
+ }
8540
+ } catch {
8541
+ if (result.plan === "free") {
8542
+ console.log(chalk4.dim(" Run `vibedrift upgrade` to enable deep AI scans."));
8543
+ } else {
8544
+ console.log(chalk4.dim(" Run `vibedrift . --deep` to use AI-powered analysis."));
8545
+ }
8486
8546
  console.log("");
8487
8547
  }
8488
8548
  return;
@@ -8594,6 +8654,21 @@ async function runStatus() {
8594
8654
  console.log(chalk6.dim(` ${err.message}`));
8595
8655
  }
8596
8656
  }
8657
+ try {
8658
+ const credits = await fetchCredits(resolved.token, { apiUrl: config.apiUrl });
8659
+ console.log("");
8660
+ if (credits.unlimited) {
8661
+ console.log(` Deep scans: ${chalk6.bold.green("unlimited")} (${credits.plan})`);
8662
+ } else if (credits.has_free_deep_scan) {
8663
+ console.log(` Deep scans: ${chalk6.bold.yellow("1 free")} + ${credits.available_purchased} purchased`);
8664
+ console.log(chalk6.dim(" Run `vibedrift . --deep` to use your free credit."));
8665
+ } else if (credits.available_total > 0) {
8666
+ console.log(` Deep scans: ${chalk6.bold(credits.available_total)} credit${credits.available_total === 1 ? "" : "s"} available`);
8667
+ } else {
8668
+ console.log(` Deep scans: ${chalk6.dim("0 credits")} \u2014 run \`vibedrift upgrade\` for more`);
8669
+ }
8670
+ } catch {
8671
+ }
8597
8672
  console.log("");
8598
8673
  }
8599
8674
  function describeSource(source) {