clixad 0.0.1-beta.27 → 0.0.1-beta.29

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/dist/clixad.mjs +111 -22
  2. package/package.json +1 -1
package/dist/clixad.mjs CHANGED
@@ -310,8 +310,10 @@ function accessColumn(m, premium) {
310
310
  function premiumLocked(premium) {
311
311
  return premium !== void 0 && !premium.unlocked && premium.credits <= 0;
312
312
  }
313
- function premiumUnlockHint(unlockUsd) {
314
- return typeof unlockUsd === "number" && unlockUsd > 0 ? `premium: a survey or ${usd(unlockUsd)} unlocks it` : "premium: a survey unlocks it";
313
+ function premiumUnlockHint(unlockUsd, streak) {
314
+ const base = typeof unlockUsd === "number" && unlockUsd > 0 ? `premium: a survey or ${usd(unlockUsd)} unlocks it` : "premium: a survey unlocks it";
315
+ const s = usableStreak(streak);
316
+ return s && s.day < s.min_day ? `${base}, or a ${s.min_day}-day streak` : base;
315
317
  }
316
318
  function premiumHeadline(unlockUsd) {
317
319
  return typeof unlockUsd === "number" && unlockUsd > 0 ? `Premium: complete a survey or unlock from ${usd(unlockUsd)}.` : "Premium: complete a survey to unlock it.";
@@ -319,6 +321,36 @@ function premiumHeadline(unlockUsd) {
319
321
  function usd(v) {
320
322
  return Number.isInteger(v) ? `$${v}` : `$${v.toFixed(2)}`;
321
323
  }
324
+ function packsFromClause(cheapestUsd) {
325
+ return typeof cheapestUsd === "number" && cheapestUsd > 0 ? `, from ${usd(cheapestUsd)}` : "";
326
+ }
327
+ function usableStreak(streak) {
328
+ if (typeof streak !== "object" || streak === null) return void 0;
329
+ const s = streak;
330
+ const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
331
+ const day = num(s.day);
332
+ const minDay = num(s.min_day);
333
+ const daily = num(s.daily_credits);
334
+ const left = num(s.credits_left);
335
+ if (day === void 0 || minDay === void 0 || daily === void 0 || left === void 0 || daily <= 0) {
336
+ return void 0;
337
+ }
338
+ return { day, min_day: minDay, daily_credits: daily, credits_left: left };
339
+ }
340
+ function premiumStreakLine(streak) {
341
+ const s = usableStreak(streak);
342
+ if (!s) return null;
343
+ const n = (x) => x.toLocaleString("en-US");
344
+ if (s.day < s.min_day) {
345
+ const wait = s.min_day - s.day;
346
+ const when = wait === 1 ? "tomorrow" : wait === 2 ? "the day after tomorrow" : `in ${wait} days`;
347
+ return `From day ${s.min_day} of your login streak, ${n(s.daily_credits)} bonus credits a day work on premium models too. You are on day ${s.day}, so that starts ${when} if you open Clixad every day.`;
348
+ }
349
+ if (s.credits_left <= 0) {
350
+ return `Today's ${n(s.daily_credits)} bonus credits for premium models are used up. They come back at 00:00 UTC while your login streak holds.`;
351
+ }
352
+ return `Your login streak lets ${n(s.credits_left)} more bonus credits go to premium models today.`;
353
+ }
322
354
  function adsPerTaskLabel(estAdsPerTask) {
323
355
  const effort = estAdsPerTask >= 1 ? `~${estAdsPerTask} ads/task` : "<1 ad/task";
324
356
  return `${effort} on completion (median)`;
@@ -336,7 +368,18 @@ function streakLabel(streak) {
336
368
  if (!streak.claimed) return `daily streak: ${where} \xB7 today's already counted`;
337
369
  const tomorrow = streak.schedule[Math.min(streak.day, streak.length - 1)];
338
370
  const next = tomorrow === void 0 ? "" : ` \xB7 tomorrow ${n(tomorrow)}`;
339
- return `daily streak: ${where} \xB7 +${n(streak.credits)} credits${next}`;
371
+ return `daily streak: ${where} \xB7 +${n(streak.credits)} credits${next}${streakPremiumClause(streak)}`;
372
+ }
373
+ function streakPremiumClause(streak) {
374
+ const p = streak.premium;
375
+ if (!p) return "";
376
+ return streak.day < p.minDay ? ` \xB7 bonus credits on premium from day ${p.minDay}` : ` \xB7 ${p.dailyCredits.toLocaleString("en-US")} bonus credits a day on premium`;
377
+ }
378
+ function streakPremium(raw) {
379
+ if (typeof raw !== "object" || raw === null) return {};
380
+ const p = raw;
381
+ if (typeof p.min_day !== "number" || typeof p.daily_credits !== "number" || p.daily_credits <= 0) return {};
382
+ return { premium: { minDay: p.min_day, dailyCredits: p.daily_credits } };
340
383
  }
341
384
  function referLines(info) {
342
385
  const n = (x) => x.toLocaleString("en-US");
@@ -431,7 +474,8 @@ function premiumLockedError(message, model, body) {
431
474
  num(body?.premium_credits),
432
475
  typeof body?.unlock_usd === "number" && body.unlock_usd > 0 ? body.unlock_usd : null,
433
476
  num(body?.grant_per_ad),
434
- freeAlternatives(body?.free_models)
477
+ freeAlternatives(body?.free_models),
478
+ usableStreak(body?.streak)
435
479
  );
436
480
  }
437
481
  function shortfallHeadline(err, model) {
@@ -440,7 +484,14 @@ function shortfallHeadline(err, model) {
440
484
  return `Not enough credits for this turn${model ? ` on ${model}` : ""}: it needs about ${n(err.estimatedCost)}, and the balance is ${n(err.balance)}.`;
441
485
  }
442
486
  function paywallError(body) {
443
- return new PaywallError(body.balance, body.estimated_cost, body.grant_per_ad, freeAlternatives(body.free_models));
487
+ const pack = body.cheapest_pack_usd;
488
+ return new PaywallError(
489
+ body.balance,
490
+ body.estimated_cost,
491
+ body.grant_per_ad,
492
+ freeAlternatives(body.free_models),
493
+ typeof pack === "number" && Number.isFinite(pack) && pack > 0 ? pack : null
494
+ );
444
495
  }
445
496
  function freeAlternatives(list) {
446
497
  return Array.isArray(list) ? list.filter(
@@ -613,21 +664,23 @@ var init_client = __esm({
613
664
  waitlistUrl;
614
665
  };
615
666
  PaywallError = class extends Error {
616
- constructor(balance, estimatedCost, grantPerAd, freeModels = []) {
667
+ constructor(balance, estimatedCost, grantPerAd, freeModels = [], cheapestPackUsd = null) {
617
668
  super("Out of credits");
618
669
  this.balance = balance;
619
670
  this.estimatedCost = estimatedCost;
620
671
  this.grantPerAd = grantPerAd;
621
672
  this.freeModels = freeModels;
673
+ this.cheapestPackUsd = cheapestPackUsd;
622
674
  this.name = "PaywallError";
623
675
  }
624
676
  balance;
625
677
  estimatedCost;
626
678
  grantPerAd;
627
679
  freeModels;
680
+ cheapestPackUsd;
628
681
  };
629
682
  PremiumLockedError = class extends Error {
630
- constructor(message, model, neededCredits, premiumCredits, unlockUsd, grantPerAd, freeModels) {
683
+ constructor(message, model, neededCredits, premiumCredits, unlockUsd, grantPerAd, freeModels, streak) {
631
684
  super(message);
632
685
  this.model = model;
633
686
  this.neededCredits = neededCredits;
@@ -635,6 +688,7 @@ var init_client = __esm({
635
688
  this.unlockUsd = unlockUsd;
636
689
  this.grantPerAd = grantPerAd;
637
690
  this.freeModels = freeModels;
691
+ this.streak = streak;
638
692
  this.name = "PremiumLockedError";
639
693
  }
640
694
  model;
@@ -643,6 +697,7 @@ var init_client = __esm({
643
697
  unlockUsd;
644
698
  grantPerAd;
645
699
  freeModels;
700
+ streak;
646
701
  };
647
702
  GatewayUnreachableError = class extends Error {
648
703
  constructor(host, code) {
@@ -801,7 +856,8 @@ var init_client = __esm({
801
856
  claimed: body.claimed === true,
802
857
  balance: typeof body.balance === "number" ? body.balance : 0,
803
858
  schedule: Array.isArray(body.schedule) ? body.schedule : [],
804
- length: typeof body.length === "number" ? body.length : 0
859
+ length: typeof body.length === "number" ? body.length : 0,
860
+ ...streakPremium(body.premium)
805
861
  };
806
862
  } catch {
807
863
  return null;
@@ -1751,7 +1807,9 @@ function freeTierNotice(o) {
1751
1807
  if (!free || !(free.daily_credits > 0)) return void 0;
1752
1808
  const pick = free.credits_left > 0 ? freeModelToName(free, o.model) : void 0;
1753
1809
  if (!pick) {
1754
- return o.balance <= 0 ? "Your balance is empty and today's free turns are used up. They come back at midnight UTC, and /earn tops up before then." : void 0;
1810
+ if (o.balance > 0) return void 0;
1811
+ const pack = o.cheapestPackUsd;
1812
+ return "Your balance is empty and today's free turns are used up. They come back at midnight UTC, and /earn tops up before then" + (typeof pack === "number" && pack > 0 ? `, or a pack from ${usd(pack)} with /buy.` : ".");
1755
1813
  }
1756
1814
  if (pick.id === o.model) {
1757
1815
  return `${pick.id} is free today, ${turns(pick.turns)} left. /model shows the other free models.`;
@@ -1778,6 +1836,7 @@ var turns;
1778
1836
  var init_free_tier = __esm({
1779
1837
  "src/free-tier.ts"() {
1780
1838
  "use strict";
1839
+ init_client();
1781
1840
  turns = (n) => `about ${n.toLocaleString("en-US")} turn${n === 1 ? "" : "s"}`;
1782
1841
  }
1783
1842
  });
@@ -6366,7 +6425,12 @@ function App({
6366
6425
  }
6367
6426
  const streakLine = streak && streak.claimed ? streakLabel(streak) : null;
6368
6427
  if (streakLine) push({ kind: "notice", tone: "good", text: ` ${streakLine}` });
6369
- const freeLine = freeTierNotice({ model: config.model, balance: wallet?.balance ?? 0, free: wallet?.free_tier });
6428
+ const freeLine = freeTierNotice({
6429
+ model: config.model,
6430
+ balance: wallet?.balance ?? 0,
6431
+ free: wallet?.free_tier,
6432
+ cheapestPackUsd: client.premiumUnlockUsd
6433
+ });
6370
6434
  if (freeLine) {
6371
6435
  push({ kind: "notice", tone: (wallet?.balance ?? 0) <= 0 ? "warn" : "good", text: ` ${freeLine}` });
6372
6436
  }
@@ -6641,7 +6705,7 @@ function App({
6641
6705
  if (event.creditsCharged > 0 && premiumRef.current) {
6642
6706
  const p = premiumRef.current;
6643
6707
  setPremium({
6644
- unlocked: p.unlocked,
6708
+ ...p,
6645
6709
  credits: Math.max(0, p.unlocked ? event.balance : Math.min(event.balance, p.credits - event.creditsCharged))
6646
6710
  });
6647
6711
  }
@@ -6767,7 +6831,12 @@ ${NO_SEARCH_INSTRUCTION}`),
6767
6831
  const on = model2;
6768
6832
  void client.wallet().then((w) => {
6769
6833
  setFreeTier(w.free_tier);
6770
- const line2 = freeTierNotice({ model: on, balance: w.balance, free: w.free_tier });
6834
+ const line2 = freeTierNotice({
6835
+ model: on,
6836
+ balance: w.balance,
6837
+ free: w.free_tier,
6838
+ cheapestPackUsd: client.premiumUnlockUsd
6839
+ });
6771
6840
  if (line2) push({ kind: "notice", tone: "warn", text: ` ${line2}` });
6772
6841
  }).catch(() => void 0);
6773
6842
  }
@@ -6796,15 +6865,17 @@ ${NO_SEARCH_INSTRUCTION}`),
6796
6865
  if (err instanceof PremiumLockedError) {
6797
6866
  pendingTaskRef.current = { task, images };
6798
6867
  pendingPremiumRef.current = err;
6799
- setPremium({ unlocked: false, credits: err.premiumCredits });
6868
+ setPremium({ unlocked: false, credits: err.premiumCredits, ...err.streak ? { streak: err.streak } : {} });
6800
6869
  const short2 = err.premiumCredits > 0 ? ` This turn needs about ${err.neededCredits.toLocaleString("en-US")} of those credits, and you have ${err.premiumCredits.toLocaleString("en-US")}.
6801
6870
  ` : "";
6871
+ const streakLine = premiumStreakLine(err.streak);
6802
6872
  push({
6803
6873
  kind: "notice",
6804
6874
  tone: "warn",
6805
6875
  text: ` ${premiumHeadline(err.unlockUsd)}
6806
6876
  ${err.model} is a premium model. It runs on credits you earned on the offer walls or bought.
6807
- ` + short2 + " Your bonus credits (the signup bonus, our own surveys) still work on budget models.\n Press enter to unlock it or to switch to a budget model. Your request is kept."
6877
+ ` + short2 + (streakLine ? ` ${streakLine}
6878
+ ` : "") + " Your bonus credits (the signup bonus, our own surveys) still work on budget models.\n Press enter to unlock it or to switch to a budget model. Your request is kept."
6808
6879
  });
6809
6880
  } else if (err instanceof PaywallError) {
6810
6881
  pendingTaskRef.current = { task, images };
@@ -6819,7 +6890,7 @@ ${NO_SEARCH_INSTRUCTION}`),
6819
6890
  Press enter \u2014 ${free.id} is still free today (~${free.turnsLeft} turn${free.turnsLeft === 1 ? "" : "s"} left), or take an offer instead.
6820
6891
  Your request is kept either way, so you will not have to type it again.
6821
6892
  Offers you are screened out of pay nothing; that is normal, just start another.
6822
- /buy is there too, if you would rather pay than do an offer.` : ` ${shortfallHeadline(err, model2)}
6893
+ /buy is there too${packsFromClause(err.cheapestPackUsd)}, if you would rather pay than do an offer.` : ` ${shortfallHeadline(err, model2)}
6823
6894
  Press enter to open the ad wall \u2014 offers pay ${grantLabel(err.grantPerAd)}.
6824
6895
  I'll continue automatically. Offers you are screened out of pay nothing;
6825
6896
  that is normal, just start another.
@@ -6831,8 +6902,11 @@ ${NO_SEARCH_INSTRUCTION}`),
6831
6902
  ` : "") + // One clause, last, and it stays last. The wall is how this product
6832
6903
  // is meant to be paid for and the offer is free; naming /buy here
6833
6904
  // is discoverability rather than a pitch, and it is the moment
6834
- // somebody actually wants to know the command exists.
6835
- ` /buy is there too, if you would rather pay than do an offer.`
6905
+ // somebody actually wants to know the command exists. The
6906
+ // cheapest pack's price rides along, the gateway's figure: the
6907
+ // median refusal is a couple of thousand credits short, and the
6908
+ // smallest pack is what fits that.
6909
+ ` /buy is there too${packsFromClause(err.cheapestPackUsd)}, if you would rather pay than do an offer.`
6836
6910
  });
6837
6911
  } else if (err instanceof AuthError) {
6838
6912
  push({ kind: "notice", tone: "error", text: NOT_SIGNED_IN });
@@ -6930,7 +7004,12 @@ ${NO_SEARCH_INSTRUCTION}`),
6930
7004
  if (w) {
6931
7005
  setBalance(w.balance);
6932
7006
  setFreeTier(w.free_tier);
6933
- const freeLine = freeTierNotice({ model: modelRef.current, balance: w.balance, free: w.free_tier });
7007
+ const freeLine = freeTierNotice({
7008
+ model: modelRef.current,
7009
+ balance: w.balance,
7010
+ free: w.free_tier,
7011
+ cheapestPackUsd: client.premiumUnlockUsd
7012
+ });
6934
7013
  if (freeLine) push({ kind: "notice", tone: w.balance <= 0 ? "warn" : "good", text: ` ${freeLine}` });
6935
7014
  }
6936
7015
  return;
@@ -7267,7 +7346,7 @@ ${NO_SEARCH_INSTRUCTION}`),
7267
7346
  const items = models.map((m) => ({
7268
7347
  value: m.id,
7269
7348
  label: m.id,
7270
- hint: accessColumn(m, premium2) + (m.access === "premium" && premiumLocked(premium2) ? premiumUnlockHint(unlockUsd) : modelHint(m, free)),
7349
+ hint: accessColumn(m, premium2) + (m.access === "premium" && premiumLocked(premium2) ? premiumUnlockHint(unlockUsd, premium2?.streak) : modelHint(m, free)),
7271
7350
  current: m.id === model
7272
7351
  }));
7273
7352
  const preselect = items.findIndex((i) => i.value === arg);
@@ -7283,12 +7362,14 @@ ${NO_SEARCH_INSTRUCTION}`),
7283
7362
  push({ kind: "notice", text: ` model \u2192 ${choice.value}` });
7284
7363
  const picked = models.find((m) => m.id === choice.value);
7285
7364
  if (picked?.access === "premium" && premiumLocked(premium2)) {
7365
+ const streakLine = premiumStreakLine(premium2?.streak);
7286
7366
  push({
7287
7367
  kind: "notice",
7288
7368
  tone: "warn",
7289
7369
  text: ` ${premiumHeadline(unlockUsd)}
7290
7370
  ${choice.value} runs on credits you earned on the offer walls or bought.
7291
- /earn opens the offer walls` + (unlockUsd ? `, /buy unlocks premium from ${usd(unlockUsd)}.` : ".") + " Your bonus credits work on budget models."
7371
+ ` + (streakLine ? ` ${streakLine}
7372
+ ` : "") + " /earn opens the offer walls" + (unlockUsd ? `, /buy unlocks premium from ${usd(unlockUsd)}.` : ".") + " Your bonus credits work on budget models."
7292
7373
  });
7293
7374
  }
7294
7375
  }
@@ -8672,6 +8753,8 @@ function printPremiumRefusal(err) {
8672
8753
  )
8673
8754
  );
8674
8755
  }
8756
+ const streakLine = premiumStreakLine(err.streak);
8757
+ if (streakLine) console.log(c.yellow(streakLine));
8675
8758
  console.log(c.yellow(`Run \`clixad earn\` for a survey on the offer walls. Offers pay ${grantLabel(err.grantPerAd)}.`));
8676
8759
  if (err.unlockUsd) {
8677
8760
  console.log(c.yellow(`Or unlock premium from ${usd(err.unlockUsd)}: start \`clixad\` and type /buy.`));
@@ -8703,6 +8786,8 @@ async function setModel(client, config, model) {
8703
8786
  `${model} runs on credits you earned on the offer walls or bought. Run \`clixad earn\` for a survey` + (client.premiumUnlockUsd ? `, or start \`clixad\` and type /buy.` : ".")
8704
8787
  )
8705
8788
  );
8789
+ const streakLine = premiumStreakLine(w?.premium?.streak);
8790
+ if (streakLine) console.log(c.yellow(streakLine));
8706
8791
  }
8707
8792
  }
8708
8793
  }
@@ -8851,7 +8936,9 @@ ${shortfallHeadline(err, config.model)}`));
8851
8936
  `Run \`clixad earn\` to open the offerwall \u2014 offers pay ${grantLabel(err.grantPerAd)}.`
8852
8937
  )
8853
8938
  );
8854
- console.log(c.yellow("Credits can also be bought: start `clixad` and type /buy."));
8939
+ console.log(
8940
+ c.yellow(`Credits can also be bought${packsFromClause(err.cheapestPackUsd)}: start \`clixad\` and type /buy.`)
8941
+ );
8855
8942
  console.log(c.dim(" Offers you are screened out of pay nothing \u2014 that is normal, just start another."));
8856
8943
  } else if (err instanceof AuthError) {
8857
8944
  console.log(c.red(err.message));
@@ -8985,7 +9072,9 @@ async function runTurn(client, config, messages) {
8985
9072
  console.log(
8986
9073
  c.yellow(`Run \`clixad earn\` to open the offerwall \u2014 offers pay ${grantLabel(err.grantPerAd)}.`)
8987
9074
  );
8988
- console.log(c.yellow("Credits can also be bought: start `clixad` and type /buy."));
9075
+ console.log(
9076
+ c.yellow(`Credits can also be bought${packsFromClause(err.cheapestPackUsd)}: start \`clixad\` and type /buy.`)
9077
+ );
8989
9078
  console.log(c.dim(" Offers you are screened out of pay nothing \u2014 that is normal, just start another."));
8990
9079
  } else if (err instanceof AuthError) {
8991
9080
  console.log(c.red(err.message));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clixad",
3
- "version": "0.0.1-beta.27",
3
+ "version": "0.0.1-beta.29",
4
4
  "description": "Free AI coding agent in your terminal, funded by rewarded ads.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",