clixad 0.0.1-beta.26 → 0.0.1-beta.27
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/clixad.mjs +249 -32
- package/package.json +1 -1
package/dist/clixad.mjs
CHANGED
|
@@ -302,6 +302,23 @@ import { gzipSync } from "node:zlib";
|
|
|
302
302
|
function encodeChatBody(json) {
|
|
303
303
|
return gzipSync(Buffer.from(json, "utf8"));
|
|
304
304
|
}
|
|
305
|
+
function accessColumn(m, premium) {
|
|
306
|
+
if (m.access === void 0) return m.tier.padEnd(8);
|
|
307
|
+
if (m.access === "budget") return "budget ";
|
|
308
|
+
return premiumLocked(premium) ? "locked " : "premium ";
|
|
309
|
+
}
|
|
310
|
+
function premiumLocked(premium) {
|
|
311
|
+
return premium !== void 0 && !premium.unlocked && premium.credits <= 0;
|
|
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";
|
|
315
|
+
}
|
|
316
|
+
function premiumHeadline(unlockUsd) {
|
|
317
|
+
return typeof unlockUsd === "number" && unlockUsd > 0 ? `Premium: complete a survey or unlock from ${usd(unlockUsd)}.` : "Premium: complete a survey to unlock it.";
|
|
318
|
+
}
|
|
319
|
+
function usd(v) {
|
|
320
|
+
return Number.isInteger(v) ? `$${v}` : `$${v.toFixed(2)}`;
|
|
321
|
+
}
|
|
305
322
|
function adsPerTaskLabel(estAdsPerTask) {
|
|
306
323
|
const effort = estAdsPerTask >= 1 ? `~${estAdsPerTask} ads/task` : "<1 ad/task";
|
|
307
324
|
return `${effort} on completion (median)`;
|
|
@@ -405,16 +422,35 @@ function asSignupClosed(status, data) {
|
|
|
405
422
|
const d = data;
|
|
406
423
|
return d && d.error === "signup_closed" && typeof d.message === "string" ? { error: d.error, message: d.message, waitlist_url: d.waitlist_url ?? "https://clixad.io" } : void 0;
|
|
407
424
|
}
|
|
425
|
+
function premiumLockedError(message, model, body) {
|
|
426
|
+
const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
427
|
+
return new PremiumLockedError(
|
|
428
|
+
message,
|
|
429
|
+
typeof body?.model === "string" ? body.model : model,
|
|
430
|
+
num(body?.needed_credits),
|
|
431
|
+
num(body?.premium_credits),
|
|
432
|
+
typeof body?.unlock_usd === "number" && body.unlock_usd > 0 ? body.unlock_usd : null,
|
|
433
|
+
num(body?.grant_per_ad),
|
|
434
|
+
freeAlternatives(body?.free_models)
|
|
435
|
+
);
|
|
436
|
+
}
|
|
408
437
|
function shortfallHeadline(err, model) {
|
|
409
438
|
const n = (v) => Math.max(0, Math.round(v)).toLocaleString("en-US");
|
|
410
439
|
if (err.balance <= 0) return `Out of credits (this needs about ${n(err.estimatedCost)}).`;
|
|
411
440
|
return `Not enough credits for this turn${model ? ` on ${model}` : ""}: it needs about ${n(err.estimatedCost)}, and the balance is ${n(err.balance)}.`;
|
|
412
441
|
}
|
|
413
442
|
function paywallError(body) {
|
|
414
|
-
|
|
443
|
+
return new PaywallError(body.balance, body.estimated_cost, body.grant_per_ad, freeAlternatives(body.free_models));
|
|
444
|
+
}
|
|
445
|
+
function freeAlternatives(list) {
|
|
446
|
+
return Array.isArray(list) ? list.filter(
|
|
415
447
|
(m) => !!m && typeof m.id === "string" && m.id.length > 0 && Number.isFinite(m.turns_left)
|
|
416
448
|
).map((m) => ({ id: m.id, turnsLeft: Math.max(0, Math.floor(m.turns_left)) })).filter((m) => m.turnsLeft > 0) : [];
|
|
417
|
-
|
|
449
|
+
}
|
|
450
|
+
async function refusal402(res, model) {
|
|
451
|
+
const body = await res.json();
|
|
452
|
+
if (body.error === "premium_locked") return premiumLockedError(body.message ?? "", model, body.premium);
|
|
453
|
+
return paywallError(body);
|
|
418
454
|
}
|
|
419
455
|
function neverConnected(code) {
|
|
420
456
|
return code !== void 0 && NEVER_CONNECTED.has(code);
|
|
@@ -540,7 +576,7 @@ async function* parseSSE(body) {
|
|
|
540
576
|
}
|
|
541
577
|
}
|
|
542
578
|
}
|
|
543
|
-
var CHAT_ENCODED_CONTENT_TYPE, LEDGER_REASONS, SignupClosedError, PaywallError, GatewayUnreachableError, NEVER_CONNECTED, AuthError, GatewayClient;
|
|
579
|
+
var CHAT_ENCODED_CONTENT_TYPE, LEDGER_REASONS, SignupClosedError, PaywallError, PremiumLockedError, GatewayUnreachableError, NEVER_CONNECTED, AuthError, GatewayClient;
|
|
544
580
|
var init_client = __esm({
|
|
545
581
|
"src/client.ts"() {
|
|
546
582
|
"use strict";
|
|
@@ -590,6 +626,24 @@ var init_client = __esm({
|
|
|
590
626
|
grantPerAd;
|
|
591
627
|
freeModels;
|
|
592
628
|
};
|
|
629
|
+
PremiumLockedError = class extends Error {
|
|
630
|
+
constructor(message, model, neededCredits, premiumCredits, unlockUsd, grantPerAd, freeModels) {
|
|
631
|
+
super(message);
|
|
632
|
+
this.model = model;
|
|
633
|
+
this.neededCredits = neededCredits;
|
|
634
|
+
this.premiumCredits = premiumCredits;
|
|
635
|
+
this.unlockUsd = unlockUsd;
|
|
636
|
+
this.grantPerAd = grantPerAd;
|
|
637
|
+
this.freeModels = freeModels;
|
|
638
|
+
this.name = "PremiumLockedError";
|
|
639
|
+
}
|
|
640
|
+
model;
|
|
641
|
+
neededCredits;
|
|
642
|
+
premiumCredits;
|
|
643
|
+
unlockUsd;
|
|
644
|
+
grantPerAd;
|
|
645
|
+
freeModels;
|
|
646
|
+
};
|
|
593
647
|
GatewayUnreachableError = class extends Error {
|
|
594
648
|
constructor(host, code) {
|
|
595
649
|
const where = `${host}${code ? ` (${code})` : ""}`;
|
|
@@ -886,6 +940,7 @@ var init_client = __esm({
|
|
|
886
940
|
if (!res.ok) throw new Error(`models failed: ${res.status}`);
|
|
887
941
|
const data = await res.json();
|
|
888
942
|
this.seen = { web_search: data.capabilities?.web_search === true };
|
|
943
|
+
this.unlockUsd = typeof data.premium_unlock_usd === "number" && data.premium_unlock_usd > 0 ? data.premium_unlock_usd : null;
|
|
889
944
|
this.shape = parseTaskShape(data.task_shape);
|
|
890
945
|
this.latest = typeof data.cli_latest === "string" ? data.cli_latest : void 0;
|
|
891
946
|
const live = new Set(data.data.map((m) => m.id));
|
|
@@ -936,8 +991,23 @@ var init_client = __esm({
|
|
|
936
991
|
return this.seen;
|
|
937
992
|
}
|
|
938
993
|
seen = { web_search: false };
|
|
939
|
-
|
|
940
|
-
|
|
994
|
+
/**
|
|
995
|
+
* What unlocks premium for every credit on the account (the cheapest pack,
|
|
996
|
+
* USD), as of the last catalog fetch. Null where the gateway sells none or
|
|
997
|
+
* did not say, which the hints read as "a survey unlocks it".
|
|
998
|
+
*/
|
|
999
|
+
get premiumUnlockUsd() {
|
|
1000
|
+
return this.unlockUsd;
|
|
1001
|
+
}
|
|
1002
|
+
unlockUsd = null;
|
|
1003
|
+
/**
|
|
1004
|
+
* `premium: true` asks for premium access too, which costs the gateway a read
|
|
1005
|
+
* of this account's whole ledger. Asked for where it is shown (`/model`,
|
|
1006
|
+
* `clixad models`), never by the polls `/earn` and a purchase run.
|
|
1007
|
+
*/
|
|
1008
|
+
async wallet(signal, opts = {}) {
|
|
1009
|
+
const url = `${this.config.gatewayUrl}/v1/wallet${opts.premium ? "?premium=1" : ""}`;
|
|
1010
|
+
const res = await gatewayFetch(url, { headers: this.headers(), signal });
|
|
941
1011
|
if (res.status === 401) throw await authFailure(res, "wallet");
|
|
942
1012
|
if (!res.ok) throw new Error(`wallet failed: ${res.status}`);
|
|
943
1013
|
return res.json();
|
|
@@ -1234,9 +1304,7 @@ var init_client = __esm({
|
|
|
1234
1304
|
signal
|
|
1235
1305
|
);
|
|
1236
1306
|
if (res.status === 401) throw await authFailure(res, "chat");
|
|
1237
|
-
if (res.status === 402)
|
|
1238
|
-
throw paywallError(await res.json());
|
|
1239
|
-
}
|
|
1307
|
+
if (res.status === 402) throw await refusal402(res, model);
|
|
1240
1308
|
if (!res.ok) throw new Error(await httpFailure(res, "chat"));
|
|
1241
1309
|
const data = await res.json();
|
|
1242
1310
|
const choice = data.choices[0];
|
|
@@ -1267,9 +1335,7 @@ var init_client = __esm({
|
|
|
1267
1335
|
opts.signal
|
|
1268
1336
|
);
|
|
1269
1337
|
if (res.status === 401) throw await authFailure(res, "chat");
|
|
1270
|
-
if (res.status === 402)
|
|
1271
|
-
throw paywallError(await res.json());
|
|
1272
|
-
}
|
|
1338
|
+
if (res.status === 402) throw await refusal402(res, model);
|
|
1273
1339
|
if (!res.ok || !res.body) throw new Error(await httpFailure(res, "chat"));
|
|
1274
1340
|
let content = "";
|
|
1275
1341
|
let finishReason = "stop";
|
|
@@ -1277,6 +1343,7 @@ var init_client = __esm({
|
|
|
1277
1343
|
let free = {};
|
|
1278
1344
|
let balance = 0;
|
|
1279
1345
|
let streamError;
|
|
1346
|
+
let premiumRefusal;
|
|
1280
1347
|
const calls = /* @__PURE__ */ new Map();
|
|
1281
1348
|
const reasoning = [];
|
|
1282
1349
|
for await (const evt of parseSSE(res.body)) {
|
|
@@ -1299,12 +1366,16 @@ var init_client = __esm({
|
|
|
1299
1366
|
mergeReasoningDetails(reasoning, choice?.delta?.reasoning_details);
|
|
1300
1367
|
if (choice?.finish_reason) finishReason = choice.finish_reason;
|
|
1301
1368
|
if (json.error) streamError = json.error.message || "the model stream failed";
|
|
1369
|
+
if (json.error?.type === "premium_locked") {
|
|
1370
|
+
premiumRefusal = premiumLockedError(streamError ?? "", model, json.clixad?.premium);
|
|
1371
|
+
}
|
|
1302
1372
|
if (json.clixad) {
|
|
1303
1373
|
creditsCharged = json.clixad.credits_charged;
|
|
1304
1374
|
balance = json.clixad.balance;
|
|
1305
1375
|
free = freeMeta(json.clixad);
|
|
1306
1376
|
}
|
|
1307
1377
|
}
|
|
1378
|
+
if (premiumRefusal) throw premiumRefusal;
|
|
1308
1379
|
if (streamError) throw new Error(streamError);
|
|
1309
1380
|
const message = { role: "assistant", content: content || null };
|
|
1310
1381
|
if (calls.size > 0) {
|
|
@@ -6150,6 +6221,16 @@ function App({
|
|
|
6150
6221
|
const pendingFreeRef = useRef([]);
|
|
6151
6222
|
const pendingGrantRef = useRef(0);
|
|
6152
6223
|
const pendingBalanceRef = useRef(void 0);
|
|
6224
|
+
const pendingPremiumRef = useRef(null);
|
|
6225
|
+
const [premium, setPremiumState] = useState(wallet?.premium);
|
|
6226
|
+
const premiumRef = useRef(wallet?.premium);
|
|
6227
|
+
const setPremium = useCallback((next) => {
|
|
6228
|
+
premiumRef.current = next;
|
|
6229
|
+
setPremiumState(next);
|
|
6230
|
+
}, []);
|
|
6231
|
+
const refreshPremium = useCallback(() => {
|
|
6232
|
+
void client.wallet(void 0, { premium: true }).then((w) => setPremium(w.premium)).catch(() => void 0);
|
|
6233
|
+
}, [client, setPremium]);
|
|
6153
6234
|
const mcpRef = useRef(McpHub.empty());
|
|
6154
6235
|
const todosRef = useRef(new TodoList());
|
|
6155
6236
|
const lastTurnRef = useRef(null);
|
|
@@ -6259,9 +6340,10 @@ function App({
|
|
|
6259
6340
|
useEffect(() => {
|
|
6260
6341
|
if (wallet) return;
|
|
6261
6342
|
const ctl = new AbortController();
|
|
6262
|
-
void client.wallet(ctl.signal).then((w) => {
|
|
6343
|
+
void client.wallet(ctl.signal, { premium: true }).then((w) => {
|
|
6263
6344
|
setBalance((current) => current ?? w.balance);
|
|
6264
6345
|
setFreeTier((current) => current ?? w.free_tier);
|
|
6346
|
+
if (premiumRef.current === void 0) setPremium(w.premium);
|
|
6265
6347
|
}).catch(() => void 0);
|
|
6266
6348
|
return () => ctl.abort();
|
|
6267
6349
|
}, []);
|
|
@@ -6556,6 +6638,13 @@ function App({
|
|
|
6556
6638
|
return;
|
|
6557
6639
|
case "usage":
|
|
6558
6640
|
setBalance(event.balance);
|
|
6641
|
+
if (event.creditsCharged > 0 && premiumRef.current) {
|
|
6642
|
+
const p = premiumRef.current;
|
|
6643
|
+
setPremium({
|
|
6644
|
+
unlocked: p.unlocked,
|
|
6645
|
+
credits: Math.max(0, p.unlocked ? event.balance : Math.min(event.balance, p.credits - event.creditsCharged))
|
|
6646
|
+
});
|
|
6647
|
+
}
|
|
6559
6648
|
setSpent((s) => s + event.creditsCharged);
|
|
6560
6649
|
if (event.free) {
|
|
6561
6650
|
setFreeTier(
|
|
@@ -6567,11 +6656,12 @@ function App({
|
|
|
6567
6656
|
return;
|
|
6568
6657
|
}
|
|
6569
6658
|
},
|
|
6570
|
-
[cols, flushDelta, push]
|
|
6659
|
+
[cols, flushDelta, push, setPremium]
|
|
6571
6660
|
);
|
|
6572
6661
|
const runTurn2 = useCallback(
|
|
6573
6662
|
async (task, images = []) => {
|
|
6574
6663
|
pendingFreeRef.current = [];
|
|
6664
|
+
pendingPremiumRef.current = null;
|
|
6575
6665
|
const model2 = modelRef.current;
|
|
6576
6666
|
const ac = new AbortController();
|
|
6577
6667
|
abortRef.current = ac;
|
|
@@ -6703,7 +6793,20 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
6703
6793
|
}
|
|
6704
6794
|
}
|
|
6705
6795
|
} catch (err) {
|
|
6706
|
-
if (err instanceof
|
|
6796
|
+
if (err instanceof PremiumLockedError) {
|
|
6797
|
+
pendingTaskRef.current = { task, images };
|
|
6798
|
+
pendingPremiumRef.current = err;
|
|
6799
|
+
setPremium({ unlocked: false, credits: err.premiumCredits });
|
|
6800
|
+
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
|
+
` : "";
|
|
6802
|
+
push({
|
|
6803
|
+
kind: "notice",
|
|
6804
|
+
tone: "warn",
|
|
6805
|
+
text: ` ${premiumHeadline(err.unlockUsd)}
|
|
6806
|
+
${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."
|
|
6808
|
+
});
|
|
6809
|
+
} else if (err instanceof PaywallError) {
|
|
6707
6810
|
pendingTaskRef.current = { task, images };
|
|
6708
6811
|
pendingFreeRef.current = err.freeModels;
|
|
6709
6812
|
pendingGrantRef.current = err.grantPerAd;
|
|
@@ -6898,8 +7001,9 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
6898
7001
|
text: " no credit yet \u2014 offers take a while to confirm, and a screenout pays nothing.\n That is normal: start another with /earn, or check /wallet later."
|
|
6899
7002
|
});
|
|
6900
7003
|
push({ kind: "notice", tone: "good", text: " credits added \u2014 continuing" });
|
|
7004
|
+
refreshPremium();
|
|
6901
7005
|
if (pending) await runTurn2(pending.task, pending.images);
|
|
6902
|
-
}, [balance, client, config.dashboardUrl, push, runBusy, runTurn2]);
|
|
7006
|
+
}, [balance, client, config.dashboardUrl, push, refreshPremium, runBusy, runTurn2]);
|
|
6903
7007
|
const runPaywallChoice = useCallback(() => {
|
|
6904
7008
|
const free = pendingFreeRef.current.filter((m) => m.id !== EARN_ROW);
|
|
6905
7009
|
if (free.length === 0) return void runAdWall();
|
|
@@ -6969,6 +7073,47 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
6969
7073
|
});
|
|
6970
7074
|
});
|
|
6971
7075
|
}, [client, push, runBusy]);
|
|
7076
|
+
const runPremiumChoice = useCallback(() => {
|
|
7077
|
+
const refusal = pendingPremiumRef.current;
|
|
7078
|
+
if (!refusal) return void runPaywallChoice();
|
|
7079
|
+
const items = [
|
|
7080
|
+
{
|
|
7081
|
+
value: EARN_ROW,
|
|
7082
|
+
label: "complete a survey",
|
|
7083
|
+
hint: `opens the offer walls, offers pay ${grantLabel(refusal.grantPerAd)}`
|
|
7084
|
+
},
|
|
7085
|
+
...refusal.unlockUsd ? [
|
|
7086
|
+
{
|
|
7087
|
+
value: BUY_ROW,
|
|
7088
|
+
label: `unlock from ${usd(refusal.unlockUsd)}`,
|
|
7089
|
+
hint: "any pack, and premium then takes all your credits"
|
|
7090
|
+
}
|
|
7091
|
+
] : [],
|
|
7092
|
+
...refusal.freeModels.filter((m) => m.id !== EARN_ROW && m.id !== BUY_ROW).map((m) => ({
|
|
7093
|
+
value: m.id,
|
|
7094
|
+
label: m.id,
|
|
7095
|
+
hint: `budget, free: ~${m.turnsLeft} turn${m.turnsLeft === 1 ? "" : "s"} left today`
|
|
7096
|
+
}))
|
|
7097
|
+
];
|
|
7098
|
+
setPickerSel(0);
|
|
7099
|
+
setPicker({
|
|
7100
|
+
title: premiumHeadline(refusal.unlockUsd),
|
|
7101
|
+
subtitle: "Unlock premium, or carry on with a budget model.",
|
|
7102
|
+
items,
|
|
7103
|
+
onPick: (choice) => {
|
|
7104
|
+
if (choice.value === EARN_ROW) return void runAdWall();
|
|
7105
|
+
if (choice.value === BUY_ROW) return void runBuy();
|
|
7106
|
+
const pending = pendingTaskRef.current;
|
|
7107
|
+
pendingTaskRef.current = null;
|
|
7108
|
+
pendingPremiumRef.current = null;
|
|
7109
|
+
config.model = choice.value;
|
|
7110
|
+
saveConfig(config);
|
|
7111
|
+
switchModel(choice.value);
|
|
7112
|
+
push({ kind: "notice", tone: "good", text: ` model \u2192 ${choice.value} (budget) \xB7 continuing where you left off` });
|
|
7113
|
+
if (pending) void runTurn2(pending.task, pending.images);
|
|
7114
|
+
}
|
|
7115
|
+
});
|
|
7116
|
+
}, [config, push, runAdWall, runBuy, runPaywallChoice, runTurn2, switchModel]);
|
|
6972
7117
|
const runCheckout = useCallback(
|
|
6973
7118
|
async (pack) => {
|
|
6974
7119
|
let before = balance ?? (await client.wallet().catch(() => void 0))?.balance;
|
|
@@ -7006,11 +7151,20 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
7006
7151
|
stopped = signal.aborted;
|
|
7007
7152
|
});
|
|
7008
7153
|
if (credited) {
|
|
7009
|
-
|
|
7154
|
+
push({
|
|
7010
7155
|
kind: "notice",
|
|
7011
7156
|
tone: "good",
|
|
7012
7157
|
text: " credits added \u2014 an invoice is on its way to the address on the payment."
|
|
7013
7158
|
});
|
|
7159
|
+
refreshPremium();
|
|
7160
|
+
const pending = pendingPremiumRef.current ? pendingTaskRef.current : null;
|
|
7161
|
+
if (pending) {
|
|
7162
|
+
pendingTaskRef.current = null;
|
|
7163
|
+
pendingPremiumRef.current = null;
|
|
7164
|
+
push({ kind: "notice", tone: "good", text: " premium is unlocked \xB7 continuing where you left off" });
|
|
7165
|
+
await runTurn2(pending.task, pending.images);
|
|
7166
|
+
}
|
|
7167
|
+
return;
|
|
7014
7168
|
}
|
|
7015
7169
|
push({
|
|
7016
7170
|
kind: "notice",
|
|
@@ -7018,7 +7172,7 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
7018
7172
|
text: stopped ? " stopped waiting. Credits still land when the payment clears \u2014 /wallet checks the balance." : " no credits yet \u2014 the payment may still be clearing. /wallet checks the balance."
|
|
7019
7173
|
});
|
|
7020
7174
|
},
|
|
7021
|
-
[balance, client, push, runBusy]
|
|
7175
|
+
[balance, client, push, refreshPremium, runBusy, runTurn2]
|
|
7022
7176
|
);
|
|
7023
7177
|
const runCommand = useCallback(
|
|
7024
7178
|
async (line2) => {
|
|
@@ -7099,15 +7253,21 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
7099
7253
|
if (arg && !models.some((m) => m.id === arg)) {
|
|
7100
7254
|
push({ kind: "notice", tone: "warn", text: ` unknown model: ${arg}` });
|
|
7101
7255
|
}
|
|
7102
|
-
const free = await client.wallet(signal).then((w) => {
|
|
7256
|
+
const free = await client.wallet(signal, { premium: true }).then((w) => {
|
|
7103
7257
|
setBalance(w.balance);
|
|
7104
7258
|
setFreeTier(w.free_tier);
|
|
7259
|
+
setPremium(w.premium);
|
|
7105
7260
|
return w.free_tier;
|
|
7106
|
-
}).catch(() =>
|
|
7261
|
+
}).catch(() => {
|
|
7262
|
+
setPremium(void 0);
|
|
7263
|
+
return freeTier;
|
|
7264
|
+
});
|
|
7265
|
+
const premium2 = premiumRef.current;
|
|
7266
|
+
const unlockUsd = client.premiumUnlockUsd;
|
|
7107
7267
|
const items = models.map((m) => ({
|
|
7108
7268
|
value: m.id,
|
|
7109
7269
|
label: m.id,
|
|
7110
|
-
hint:
|
|
7270
|
+
hint: accessColumn(m, premium2) + (m.access === "premium" && premiumLocked(premium2) ? premiumUnlockHint(unlockUsd) : modelHint(m, free)),
|
|
7111
7271
|
current: m.id === model
|
|
7112
7272
|
}));
|
|
7113
7273
|
const preselect = items.findIndex((i) => i.value === arg);
|
|
@@ -7121,6 +7281,16 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
7121
7281
|
saveConfig(config);
|
|
7122
7282
|
switchModel(choice.value);
|
|
7123
7283
|
push({ kind: "notice", text: ` model \u2192 ${choice.value}` });
|
|
7284
|
+
const picked = models.find((m) => m.id === choice.value);
|
|
7285
|
+
if (picked?.access === "premium" && premiumLocked(premium2)) {
|
|
7286
|
+
push({
|
|
7287
|
+
kind: "notice",
|
|
7288
|
+
tone: "warn",
|
|
7289
|
+
text: ` ${premiumHeadline(unlockUsd)}
|
|
7290
|
+
${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."
|
|
7292
|
+
});
|
|
7293
|
+
}
|
|
7124
7294
|
}
|
|
7125
7295
|
});
|
|
7126
7296
|
});
|
|
@@ -7189,7 +7359,7 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
7189
7359
|
catalogRef.current = ms;
|
|
7190
7360
|
push({
|
|
7191
7361
|
kind: "notice",
|
|
7192
|
-
text: ms.map((m) => ` ${m.id.padEnd(24)} ${modelHint(m, freeTier)}`).join("\n")
|
|
7362
|
+
text: ms.map((m) => ` ${m.id.padEnd(24)} ${accessColumn(m, premium)}${modelHint(m, freeTier)}`).join("\n")
|
|
7193
7363
|
});
|
|
7194
7364
|
});
|
|
7195
7365
|
return;
|
|
@@ -7351,7 +7521,7 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
7351
7521
|
push({ kind: "notice", tone: "warn", text: ` unknown command: /${cmd} \u2014 try /help` });
|
|
7352
7522
|
}
|
|
7353
7523
|
},
|
|
7354
|
-
[client, cols, config, cycleMode, exit, model, push, root, runAdWall, runBusy, runBuy, runLogin, runTurn2, writeToStdout]
|
|
7524
|
+
[client, cols, config, cycleMode, exit, model, premium, push, root, runAdWall, runBusy, runBuy, runLogin, runTurn2, setPremium, writeToStdout]
|
|
7355
7525
|
);
|
|
7356
7526
|
const runShell = useCallback(
|
|
7357
7527
|
async (command) => {
|
|
@@ -7681,7 +7851,9 @@ ${dropped.map((l) => ` \xB7 ${l}`).join("\n")}`
|
|
|
7681
7851
|
if (key.return) {
|
|
7682
7852
|
if (key.meta || key.shift) return setEditor(newline(editor));
|
|
7683
7853
|
if (endsWithContinuation(editor)) return setEditor(continueLine(editor));
|
|
7684
|
-
if (!busy && pendingTaskRef.current && isEmpty(editor))
|
|
7854
|
+
if (!busy && pendingTaskRef.current && isEmpty(editor)) {
|
|
7855
|
+
return void (pendingPremiumRef.current ? runPremiumChoice() : runPaywallChoice());
|
|
7856
|
+
}
|
|
7685
7857
|
return void submit(toText(editor));
|
|
7686
7858
|
}
|
|
7687
7859
|
if (key.ctrl && ch === "j") return setEditor(newline(editor));
|
|
@@ -7862,7 +8034,11 @@ ${dropped.map((l) => ` \xB7 ${l}`).join("\n")}`
|
|
|
7862
8034
|
if (!busy && info && shape && balance !== void 0 && !effortRaised(info, resolveEffort(info, effortBy[model])) && typed.trim() !== "" && !/^[/!]/.test(typed.trimStart())) {
|
|
7863
8035
|
const range = estimateTask(info, conversationUsed + schemaTokens + estimateTokens(typed), shape);
|
|
7864
8036
|
const onFree = Boolean(info.free && freeTier?.models.includes(model));
|
|
7865
|
-
const
|
|
8037
|
+
const spendable = info.access === "premium" && premium && !premium.unlocked ? Math.min(balance, premium.credits) : balance;
|
|
8038
|
+
const verdict = taskVerdict(range, {
|
|
8039
|
+
balance: spendable,
|
|
8040
|
+
...onFree ? { freeLeft: freeTier.credits_left } : {}
|
|
8041
|
+
});
|
|
7866
8042
|
estimate = estimateClause(range, verdict, !onFree && (freeTier?.credits_left ?? 0) > 0);
|
|
7867
8043
|
}
|
|
7868
8044
|
const modeText = ` ${MODE_STYLE[mode].glyph} ${MODE_LABEL[mode]}`;
|
|
@@ -8147,7 +8323,7 @@ function tailLines(text, max) {
|
|
|
8147
8323
|
const lines = text.split("\n");
|
|
8148
8324
|
return lines.length <= max ? text : lines.slice(-max).join("\n");
|
|
8149
8325
|
}
|
|
8150
|
-
var WORKING, WAITING_FOR_REWARD, LOADING, COMPACTING, SIGNING_IN, RUNNING, SENDING_FEEDBACK, WAITING_FOR_PAYMENT, FEEDBACK_TURN_INTERVAL, NOT_SIGNED_IN, LIVE_OUTPUT_LINES, COMMITTED_OUTPUT_LINES, DELTA_FLUSH_MS, MAX_INPUT_ROWS, MAX_PICKER_ROWS, MAX_QUEUE_ROWS, QUEUE_MARKER, PICKER_CURRENT, MIN_PICKER_LABEL_COLS, PICKER_CHROME, PICKER_CHROME_COMPACT, ASK_CHROME, PLAN_CHROME, BUY_CHROME, BUY_CHROME_COMPACT, ASK_HINT, PLAN_HINT, BUY_HINT, BUY_CONFIRM_WORD, BUY_TYPED_MAX, BUY_TOO_SHORT, EXPAND_MAX_LINES, FALLBACK_ROWS, FALLBACK_COLS, INIT_HINT_AFTER_TURNS, EARN_ROW, CONTEXT_NOTICE_AT, KEEP_ALWAYS;
|
|
8326
|
+
var WORKING, WAITING_FOR_REWARD, LOADING, COMPACTING, SIGNING_IN, RUNNING, SENDING_FEEDBACK, WAITING_FOR_PAYMENT, FEEDBACK_TURN_INTERVAL, NOT_SIGNED_IN, LIVE_OUTPUT_LINES, COMMITTED_OUTPUT_LINES, DELTA_FLUSH_MS, MAX_INPUT_ROWS, MAX_PICKER_ROWS, MAX_QUEUE_ROWS, QUEUE_MARKER, PICKER_CURRENT, MIN_PICKER_LABEL_COLS, PICKER_CHROME, PICKER_CHROME_COMPACT, ASK_CHROME, PLAN_CHROME, BUY_CHROME, BUY_CHROME_COMPACT, ASK_HINT, PLAN_HINT, BUY_HINT, BUY_CONFIRM_WORD, BUY_TYPED_MAX, BUY_TOO_SHORT, EXPAND_MAX_LINES, FALLBACK_ROWS, FALLBACK_COLS, INIT_HINT_AFTER_TURNS, EARN_ROW, BUY_ROW, CONTEXT_NOTICE_AT, KEEP_ALWAYS;
|
|
8151
8327
|
var init_app = __esm({
|
|
8152
8328
|
"src/tui/app.tsx"() {
|
|
8153
8329
|
"use strict";
|
|
@@ -8231,6 +8407,7 @@ var init_app = __esm({
|
|
|
8231
8407
|
FALLBACK_COLS = 80;
|
|
8232
8408
|
INIT_HINT_AFTER_TURNS = 3;
|
|
8233
8409
|
EARN_ROW = "\0earn";
|
|
8410
|
+
BUY_ROW = "\0buy";
|
|
8234
8411
|
CONTEXT_NOTICE_AT = 0.6;
|
|
8235
8412
|
KEEP_ALWAYS = Number.POSITIVE_INFINITY;
|
|
8236
8413
|
}
|
|
@@ -8469,16 +8646,39 @@ function earnedToday(w) {
|
|
|
8469
8646
|
}
|
|
8470
8647
|
async function listModels(client) {
|
|
8471
8648
|
const models = await client.models();
|
|
8472
|
-
const
|
|
8649
|
+
const wallet = await client.wallet(void 0, { premium: true }).catch(() => void 0);
|
|
8650
|
+
const free = wallet?.free_tier;
|
|
8651
|
+
const premium = wallet?.premium;
|
|
8473
8652
|
console.log(c.bold("Models (credit prices per 1M tokens)"));
|
|
8474
8653
|
for (const m of models) {
|
|
8475
8654
|
const tag = m.ad_fundable ? "" : c.dim(" [paid/BYOK]");
|
|
8476
8655
|
const turns2 = free?.models.includes(m.id) ? free.turns_left[m.id] ?? 0 : void 0;
|
|
8477
8656
|
const freeTag = !m.free ? "" : turns2 === void 0 ? c.green(" [free]") : turns2 > 0 ? c.green(` [free: ~${turns2} turn${turns2 === 1 ? "" : "s"} left today]`) : c.dim(" [free: none left today]");
|
|
8657
|
+
const lockTag = m.access === "premium" && premiumLocked(premium) ? c.yellow(` [${premiumHeadline(client.premiumUnlockUsd).replace(/\.$/, "")}]`) : "";
|
|
8658
|
+
console.log(
|
|
8659
|
+
` ${c.cyan(m.id.padEnd(22))} ${accessColumn(m, premium)} in ${String(m.credits_per_mtoken_input).padStart(9)} out ${String(m.credits_per_mtoken_output).padStart(9)} ${adsPerTaskLabel(m.est_ads_per_task)}${tag}${freeTag}${lockTag}`
|
|
8660
|
+
);
|
|
8661
|
+
}
|
|
8662
|
+
}
|
|
8663
|
+
function printPremiumRefusal(err) {
|
|
8664
|
+
console.log(c.yellow(premiumHeadline(err.unlockUsd)));
|
|
8665
|
+
console.log(
|
|
8666
|
+
c.yellow(`${err.model} is a premium model. It runs on credits you earned on the offer walls or bought.`)
|
|
8667
|
+
);
|
|
8668
|
+
if (err.premiumCredits > 0) {
|
|
8478
8669
|
console.log(
|
|
8479
|
-
|
|
8670
|
+
c.yellow(
|
|
8671
|
+
`This turn needs about ${err.neededCredits.toLocaleString("en-US")} of those credits, and you have ${err.premiumCredits.toLocaleString("en-US")}.`
|
|
8672
|
+
)
|
|
8480
8673
|
);
|
|
8481
8674
|
}
|
|
8675
|
+
console.log(c.yellow(`Run \`clixad earn\` for a survey on the offer walls. Offers pay ${grantLabel(err.grantPerAd)}.`));
|
|
8676
|
+
if (err.unlockUsd) {
|
|
8677
|
+
console.log(c.yellow(`Or unlock premium from ${usd(err.unlockUsd)}: start \`clixad\` and type /buy.`));
|
|
8678
|
+
}
|
|
8679
|
+
const freeLine = freeAlternativeLine(err.freeModels);
|
|
8680
|
+
console.log(c.green(freeLine ?? "Your bonus credits still work on budget models: `clixad models` lists them."));
|
|
8681
|
+
console.log(c.dim(" Nothing was charged for this request."));
|
|
8482
8682
|
}
|
|
8483
8683
|
async function setModel(client, config, model) {
|
|
8484
8684
|
if (!model) return console.log(`current model: ${c.cyan(config.model)}`);
|
|
@@ -8493,6 +8693,18 @@ async function setModel(client, config, model) {
|
|
|
8493
8693
|
config.model = model;
|
|
8494
8694
|
saveConfig(config);
|
|
8495
8695
|
console.log(c.green(`\u2713 model set to ${model}`));
|
|
8696
|
+
const picked = known?.find((m) => m.id === model);
|
|
8697
|
+
if (picked?.access === "premium") {
|
|
8698
|
+
const w = await client.wallet(void 0, { premium: true }).catch(() => void 0);
|
|
8699
|
+
if (premiumLocked(w?.premium)) {
|
|
8700
|
+
console.log(c.yellow(premiumHeadline(client.premiumUnlockUsd)));
|
|
8701
|
+
console.log(
|
|
8702
|
+
c.yellow(
|
|
8703
|
+
`${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
|
+
)
|
|
8705
|
+
);
|
|
8706
|
+
}
|
|
8707
|
+
}
|
|
8496
8708
|
}
|
|
8497
8709
|
async function showWallet(client) {
|
|
8498
8710
|
const streak = await client.claimStreak();
|
|
@@ -8626,7 +8838,10 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
8626
8838
|
[${cost} \xB7 balance ${res.balance} \xB7 ${res.steps} step(s)]`)
|
|
8627
8839
|
);
|
|
8628
8840
|
} catch (err) {
|
|
8629
|
-
if (err instanceof
|
|
8841
|
+
if (err instanceof PremiumLockedError) {
|
|
8842
|
+
console.log("");
|
|
8843
|
+
printPremiumRefusal(err);
|
|
8844
|
+
} else if (err instanceof PaywallError) {
|
|
8630
8845
|
console.log(c.yellow(`
|
|
8631
8846
|
${shortfallHeadline(err, config.model)}`));
|
|
8632
8847
|
const freeLine = freeAlternativeLine(err.freeModels);
|
|
@@ -8761,7 +8976,9 @@ async function runTurn(client, config, messages) {
|
|
|
8761
8976
|
return result;
|
|
8762
8977
|
} catch (err) {
|
|
8763
8978
|
if (started) process.stdout.write("\n");
|
|
8764
|
-
if (err instanceof
|
|
8979
|
+
if (err instanceof PremiumLockedError) {
|
|
8980
|
+
printPremiumRefusal(err);
|
|
8981
|
+
} else if (err instanceof PaywallError) {
|
|
8765
8982
|
console.log(c.yellow(shortfallHeadline(err, config.model)));
|
|
8766
8983
|
const freeLine = freeAlternativeLine(err.freeModels);
|
|
8767
8984
|
if (freeLine) console.log(c.green(freeLine));
|
|
@@ -8785,9 +9002,9 @@ async function showStreak(client) {
|
|
|
8785
9002
|
console.log(streak.claimed ? c.green(` ${line2}`) : c.dim(` ${line2}`));
|
|
8786
9003
|
return streak;
|
|
8787
9004
|
}
|
|
8788
|
-
async function safeWallet(client) {
|
|
9005
|
+
async function safeWallet(client, opts = {}) {
|
|
8789
9006
|
try {
|
|
8790
|
-
return await client.wallet();
|
|
9007
|
+
return await client.wallet(void 0, opts);
|
|
8791
9008
|
} catch {
|
|
8792
9009
|
return void 0;
|
|
8793
9010
|
}
|
|
@@ -8813,7 +9030,7 @@ async function startupReads(client, gatewayUrl) {
|
|
|
8813
9030
|
)
|
|
8814
9031
|
];
|
|
8815
9032
|
try {
|
|
8816
|
-
const [streak, wallet] = await Promise.all([client.claimStreak(), safeWallet(client)]);
|
|
9033
|
+
const [streak, wallet] = await Promise.all([client.claimStreak(), safeWallet(client, { premium: true })]);
|
|
8817
9034
|
const balance = streak?.claimed ? streak.balance : wallet?.balance;
|
|
8818
9035
|
return {
|
|
8819
9036
|
streak,
|