clixad 0.0.1-beta.15 → 0.0.1-beta.16
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/README.md +4 -0
- package/dist/clixad.mjs +345 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,6 +19,10 @@ npm install -g clixad
|
|
|
19
19
|
|
|
20
20
|
Requires Node.js >= 20.
|
|
21
21
|
|
|
22
|
+
## Community
|
|
23
|
+
|
|
24
|
+
Discord: <https://discord.gg/pwxPM3QyF> — bug reports and feature requests are read there.
|
|
25
|
+
|
|
22
26
|
## What it does
|
|
23
27
|
|
|
24
28
|
It reads, searches and edits files and runs shell commands in the directory you start it in, asking
|
package/dist/clixad.mjs
CHANGED
|
@@ -101,6 +101,52 @@ function grantRangeLabel(range) {
|
|
|
101
101
|
const n = (v) => v.toLocaleString("en-US");
|
|
102
102
|
return low === high ? `${n(low)} credits on completion` : `${n(low)}\u2013${n(high)} credits on completion`;
|
|
103
103
|
}
|
|
104
|
+
function streakLabel(streak) {
|
|
105
|
+
if (streak.length <= 0 || streak.day <= 0) return null;
|
|
106
|
+
const n = (x) => x.toLocaleString("en-US");
|
|
107
|
+
const where = `day ${streak.day} of ${streak.length}`;
|
|
108
|
+
if (!streak.claimed) return `daily streak: ${where} \xB7 today's already counted`;
|
|
109
|
+
const tomorrow = streak.schedule[Math.min(streak.day, streak.length - 1)];
|
|
110
|
+
const next = tomorrow === void 0 ? "" : ` \xB7 tomorrow ${n(tomorrow)}`;
|
|
111
|
+
return `daily streak: ${where} \xB7 +${n(streak.credits)} credits${next}`;
|
|
112
|
+
}
|
|
113
|
+
function referLines(info) {
|
|
114
|
+
const n = (x) => x.toLocaleString("en-US");
|
|
115
|
+
const lines = [
|
|
116
|
+
`your code: ${info.code}`,
|
|
117
|
+
`share it: clixad login --invite ${info.code}`,
|
|
118
|
+
`you both get ${n(info.inviterCredits)} credits once they complete their first offer`
|
|
119
|
+
];
|
|
120
|
+
if (info.invited === 0) {
|
|
121
|
+
lines.push("nobody has signed up with it yet");
|
|
122
|
+
return lines;
|
|
123
|
+
}
|
|
124
|
+
const waiting = info.invited - info.credited;
|
|
125
|
+
lines.push(
|
|
126
|
+
`${n(info.invited)} signed up \xB7 ${n(info.credited)} completed an offer \xB7 ${n(info.creditsEarned)} credits earned`
|
|
127
|
+
);
|
|
128
|
+
if (waiting > 0) {
|
|
129
|
+
lines.push(`${n(waiting)} haven't completed an offer yet \u2014 that pays nothing until they do`);
|
|
130
|
+
}
|
|
131
|
+
if (info.remaining === 0 && info.maxCredited > 0) {
|
|
132
|
+
lines.push(`you've been paid for the maximum of ${n(info.maxCredited)} referrals`);
|
|
133
|
+
}
|
|
134
|
+
return lines;
|
|
135
|
+
}
|
|
136
|
+
function redeemMessage(result) {
|
|
137
|
+
switch (result.status) {
|
|
138
|
+
case "redeemed":
|
|
139
|
+
return { ok: true, text: `+${result.credited.toLocaleString("en-US")} credits` };
|
|
140
|
+
case "used":
|
|
141
|
+
return { ok: false, text: "that code has already been used" };
|
|
142
|
+
case "expired":
|
|
143
|
+
return { ok: false, text: "that code has expired" };
|
|
144
|
+
case "wrong_account":
|
|
145
|
+
return { ok: false, text: "that code belongs to a different account \u2014 check who you're signed in as" };
|
|
146
|
+
default:
|
|
147
|
+
return { ok: false, text: "that isn't a code we recognise \u2014 check it for typos" };
|
|
148
|
+
}
|
|
149
|
+
}
|
|
104
150
|
function ledgerReasonLabel(reason) {
|
|
105
151
|
return LEDGER_REASONS[reason] ?? reason.replace(/_/g, " ");
|
|
106
152
|
}
|
|
@@ -195,6 +241,10 @@ var init_client = __esm({
|
|
|
195
241
|
CHAT_ENCODED_CONTENT_TYPE = "application/vnd.clixad.chat+gzip";
|
|
196
242
|
LEDGER_REASONS = {
|
|
197
243
|
signup_bonus: "signup bonus",
|
|
244
|
+
login_streak: "daily streak",
|
|
245
|
+
feedback_bonus: "thanks for the feedback",
|
|
246
|
+
referral_bonus: "referral bonus",
|
|
247
|
+
redeem_code: "code redeemed",
|
|
198
248
|
ad_reward: "offer reward",
|
|
199
249
|
ad_screenout_bonus: "screenout bonus (didn't qualify)",
|
|
200
250
|
ad_sponsor_impression: "sponsored line shown",
|
|
@@ -323,6 +373,138 @@ var init_client = __esm({
|
|
|
323
373
|
return void 0;
|
|
324
374
|
}
|
|
325
375
|
}
|
|
376
|
+
/**
|
|
377
|
+
* Claim today's login-streak grant, or find out that today is already claimed.
|
|
378
|
+
*
|
|
379
|
+
* Safe to call from anywhere and as often as you like: the gateway keys it on
|
|
380
|
+
* `(account, UTC day)`, so the second call of the day credits nothing and
|
|
381
|
+
* answers with the same day number. That is what lets every surface which
|
|
382
|
+
* might be somebody's first contact today — a login, the REPL opening,
|
|
383
|
+
* `wallet` — claim without coordinating.
|
|
384
|
+
*
|
|
385
|
+
* **Never throws.** Like `sponsorConfig`, every caller is decorating something
|
|
386
|
+
* that already works: a gateway too old to have the route, a cold start, a
|
|
387
|
+
* dropped connection and an expired token all mean the same thing here, which
|
|
388
|
+
* is "say nothing about a streak". A missed grant is claimed by the next
|
|
389
|
+
* command; a session that died over one would be a real loss.
|
|
390
|
+
*/
|
|
391
|
+
async claimStreak(signal) {
|
|
392
|
+
try {
|
|
393
|
+
const res = await fetch(`${this.config.gatewayUrl}/v1/streak/claim`, {
|
|
394
|
+
method: "POST",
|
|
395
|
+
headers: this.headers(),
|
|
396
|
+
...signal ? { signal } : {}
|
|
397
|
+
});
|
|
398
|
+
if (!res.ok) return null;
|
|
399
|
+
const body = await res.json();
|
|
400
|
+
if (typeof body.day !== "number" || typeof body.credits !== "number") return null;
|
|
401
|
+
return {
|
|
402
|
+
day: body.day,
|
|
403
|
+
credits: body.credits,
|
|
404
|
+
claimed: body.claimed === true,
|
|
405
|
+
balance: typeof body.balance === "number" ? body.balance : 0,
|
|
406
|
+
schedule: Array.isArray(body.schedule) ? body.schedule : [],
|
|
407
|
+
length: typeof body.length === "number" ? body.length : 0
|
|
408
|
+
};
|
|
409
|
+
} catch {
|
|
410
|
+
return null;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Send one rating, and answer what it was paid.
|
|
415
|
+
*
|
|
416
|
+
* **Never throws**, for `claimStreak`'s reason and one of its own: this is
|
|
417
|
+
* called from a prompt that has already interrupted somebody, and the worst
|
|
418
|
+
* possible outcome is that answering it takes the session down. A gateway too
|
|
419
|
+
* old for the route, a cold start and an expired token all mean the same thing
|
|
420
|
+
* — the rating is lost, which costs the user nothing and us one data point.
|
|
421
|
+
*
|
|
422
|
+
* `null` for "nothing to say"; an outcome with `credited: 0` for "recorded,
|
|
423
|
+
* unpaid", which is what the daily ceiling looks like from here and is worth
|
|
424
|
+
* telling the user apart from a grant.
|
|
425
|
+
*/
|
|
426
|
+
async sendFeedback(report, signal) {
|
|
427
|
+
try {
|
|
428
|
+
const res = await fetch(`${this.config.gatewayUrl}/v1/feedback`, {
|
|
429
|
+
method: "POST",
|
|
430
|
+
headers: this.headers(),
|
|
431
|
+
...signal ? { signal } : {},
|
|
432
|
+
body: JSON.stringify({
|
|
433
|
+
session_id: report.session,
|
|
434
|
+
turn: report.turn,
|
|
435
|
+
rating: report.rating,
|
|
436
|
+
model: report.model,
|
|
437
|
+
prompt: report.prompt,
|
|
438
|
+
response: report.response,
|
|
439
|
+
...report.steps === void 0 ? {} : { steps: report.steps }
|
|
440
|
+
})
|
|
441
|
+
});
|
|
442
|
+
if (!res.ok) return null;
|
|
443
|
+
const body = await res.json();
|
|
444
|
+
return {
|
|
445
|
+
recorded: body.recorded === true,
|
|
446
|
+
credited: typeof body.credited === "number" ? body.credited : 0,
|
|
447
|
+
balance: typeof body.balance === "number" ? body.balance : 0
|
|
448
|
+
};
|
|
449
|
+
} catch {
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* This account's referral code and standing, minting the code if it has none.
|
|
455
|
+
*
|
|
456
|
+
* **This one throws**, unlike `claimStreak` and `sendFeedback`. Those two
|
|
457
|
+
* decorate something that already works and a failure costs a line of chrome;
|
|
458
|
+
* this *is* the command — `clixad refer` with nothing to print is a command
|
|
459
|
+
* that did not run, and saying so beats printing an empty summary.
|
|
460
|
+
*/
|
|
461
|
+
async referral(signal) {
|
|
462
|
+
const res = await fetch(`${this.config.gatewayUrl}/v1/referrals`, {
|
|
463
|
+
headers: this.headers(),
|
|
464
|
+
...signal ? { signal } : {}
|
|
465
|
+
});
|
|
466
|
+
if (res.status === 401) throw await authFailure(res, "referrals");
|
|
467
|
+
if (!res.ok) throw new Error(await httpFailure(res, "referrals"));
|
|
468
|
+
const body = await res.json();
|
|
469
|
+
const num = (value) => typeof value === "number" ? value : 0;
|
|
470
|
+
if (typeof body.code !== "string" || !body.code) {
|
|
471
|
+
throw new Error("the gateway did not return a referral code");
|
|
472
|
+
}
|
|
473
|
+
return {
|
|
474
|
+
code: body.code,
|
|
475
|
+
invited: num(body.invited),
|
|
476
|
+
credited: num(body.credited),
|
|
477
|
+
creditsEarned: num(body.credits_earned),
|
|
478
|
+
remaining: num(body.remaining),
|
|
479
|
+
maxCredited: num(body.max_credited),
|
|
480
|
+
inviterCredits: num(body.inviter_credits),
|
|
481
|
+
inviteeCredits: num(body.invitee_credits)
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Spend a redeem code.
|
|
486
|
+
*
|
|
487
|
+
* **Throws**, like `referral` and unlike `claimStreak`: this is the command
|
|
488
|
+
* itself, and somebody who typed a code and got silence has been told nothing.
|
|
489
|
+
* A refusal is not a throw though — an expired or already-spent code answers
|
|
490
|
+
* 200 with a status, because that is an answer rather than a failure.
|
|
491
|
+
*/
|
|
492
|
+
async redeem(code, signal) {
|
|
493
|
+
const res = await fetch(`${this.config.gatewayUrl}/v1/redeem`, {
|
|
494
|
+
method: "POST",
|
|
495
|
+
headers: this.headers(),
|
|
496
|
+
...signal ? { signal } : {},
|
|
497
|
+
body: JSON.stringify({ code })
|
|
498
|
+
});
|
|
499
|
+
if (res.status === 401) throw await authFailure(res, "redeem");
|
|
500
|
+
if (!res.ok) throw new Error(await httpFailure(res, "redeem"));
|
|
501
|
+
const body = await res.json();
|
|
502
|
+
return {
|
|
503
|
+
status: body.status ?? "unknown",
|
|
504
|
+
credited: typeof body.credited === "number" ? body.credited : 0,
|
|
505
|
+
balance: typeof body.balance === "number" ? body.balance : 0
|
|
506
|
+
};
|
|
507
|
+
}
|
|
326
508
|
async creditPacks() {
|
|
327
509
|
const res = await fetch(`${this.config.gatewayUrl}/v1/billing/packs`);
|
|
328
510
|
if (!res.ok) throw new Error(`packs failed: ${res.status}`);
|
|
@@ -4452,6 +4634,15 @@ var init_commands = __esm({
|
|
|
4452
4634
|
// forms, surveys and app trials, and describing the top-up as a video costs
|
|
4453
4635
|
// the user the one expectation that makes a screenout make sense.
|
|
4454
4636
|
{ name: "earn", desc: "open the offer wall to earn credits" },
|
|
4637
|
+
// Deliberately *no* `args`, for /login's reason: an arg hint makes enter
|
|
4638
|
+
// complete the command instead of running it, and printing the code is the
|
|
4639
|
+
// whole command.
|
|
4640
|
+
{ name: "refer", desc: "your invite code, and what it has earned" },
|
|
4641
|
+
// This one *does* carry an `args` hint, unlike /refer and /login: the code is
|
|
4642
|
+
// the whole command, and `/redeem` on its own has nothing to do — so enter
|
|
4643
|
+
// completing to "/redeem " is the helpful outcome rather than the surprising
|
|
4644
|
+
// one.
|
|
4645
|
+
{ name: "redeem", args: "<code>", desc: "spend a code you were given" },
|
|
4455
4646
|
// Reachable from inside the REPL because that is where the 401 is printed.
|
|
4456
4647
|
// "Run `clixad login` first" is not a runnable instruction at this prompt —
|
|
4457
4648
|
// anything without a leading slash is a prompt, so it went to the model and
|
|
@@ -4926,7 +5117,7 @@ function useTerminalSize() {
|
|
|
4926
5117
|
}, [stdout]);
|
|
4927
5118
|
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
4928
5119
|
}
|
|
4929
|
-
function App({ client, config, wallet, session, initialTask, sponsorServe }) {
|
|
5120
|
+
function App({ client, config, wallet, streak, session, initialTask, sponsorServe }) {
|
|
4930
5121
|
const { exit } = useApp();
|
|
4931
5122
|
const { stdout, write: writeToStdout } = useStdout();
|
|
4932
5123
|
const idRef = useRef(1);
|
|
@@ -5001,6 +5192,8 @@ function App({ client, config, wallet, session, initialTask, sponsorServe }) {
|
|
|
5001
5192
|
const pendingTaskRef = useRef(null);
|
|
5002
5193
|
const mcpRef = useRef(McpHub.empty());
|
|
5003
5194
|
const todosRef = useRef(new TodoList());
|
|
5195
|
+
const lastTurnRef = useRef(null);
|
|
5196
|
+
const turnCountRef = useRef(config.turns ?? 0);
|
|
5004
5197
|
const deltaBufRef = useRef("");
|
|
5005
5198
|
const deltaTimerRef = useRef(null);
|
|
5006
5199
|
const sponsorRef = useRef(sponsorSource());
|
|
@@ -5080,6 +5273,8 @@ function App({ client, config, wallet, session, initialTask, sponsorServe }) {
|
|
|
5080
5273
|
client.onNotice = (text2) => push({ kind: "notice", tone: "warn", text: text2 });
|
|
5081
5274
|
const files = contextRef.current.files;
|
|
5082
5275
|
if (files.length) push({ kind: "notice", text: ` context: ${files.join(", ")}` });
|
|
5276
|
+
const streakLine = streak && streak.claimed ? streakLabel(streak) : null;
|
|
5277
|
+
if (streakLine) push({ kind: "notice", tone: "good", text: ` ${streakLine}` });
|
|
5083
5278
|
if (session?.messages.length) {
|
|
5084
5279
|
push({ kind: "notice", text: ` resumed session ${session.id} (${session.messages.length} messages)` });
|
|
5085
5280
|
}
|
|
@@ -5364,6 +5559,16 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
5364
5559
|
text: result.content,
|
|
5365
5560
|
meta: `[${result.creditsCharged} credits \xB7 balance ${result.balance.toLocaleString("en-US")}]`
|
|
5366
5561
|
});
|
|
5562
|
+
lastTurnRef.current = {
|
|
5563
|
+
turn: turnCountRef.current,
|
|
5564
|
+
model,
|
|
5565
|
+
prompt: task,
|
|
5566
|
+
response: result.content,
|
|
5567
|
+
steps: result.steps
|
|
5568
|
+
};
|
|
5569
|
+
turnCountRef.current += 1;
|
|
5570
|
+
config.turns = turnCountRef.current;
|
|
5571
|
+
saveConfig(config);
|
|
5367
5572
|
}
|
|
5368
5573
|
} catch (err) {
|
|
5369
5574
|
if (err instanceof PaywallError) {
|
|
@@ -5458,6 +5663,10 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
5458
5663
|
tone: "good",
|
|
5459
5664
|
text: ` signed in as ${account.login ?? account.email ?? "you"} \xB7 balance ${account.balance.toLocaleString("en-US")} credits` + (account.created ? " (signup bonus)" : "")
|
|
5460
5665
|
});
|
|
5666
|
+
const claimed = await client.claimStreak(signal);
|
|
5667
|
+
const line2 = claimed?.claimed ? streakLabel(claimed) : null;
|
|
5668
|
+
if (claimed?.claimed) setBalance(claimed.balance);
|
|
5669
|
+
if (line2) push({ kind: "notice", tone: "good", text: ` ${line2}` });
|
|
5461
5670
|
return;
|
|
5462
5671
|
}
|
|
5463
5672
|
case "closed":
|
|
@@ -5549,6 +5758,7 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
5549
5758
|
expandedRef.current = /* @__PURE__ */ new Set();
|
|
5550
5759
|
heightCacheRef.current = /* @__PURE__ */ new Map();
|
|
5551
5760
|
todosRef.current = new TodoList();
|
|
5761
|
+
lastTurnRef.current = null;
|
|
5552
5762
|
sessionRef.current = { id: newSessionId(), started: (/* @__PURE__ */ new Date()).toISOString() };
|
|
5553
5763
|
writeToStdout("\x1B[2J\x1B[3J\x1B[H");
|
|
5554
5764
|
push({ kind: "notice", text: " cleared \u2014 new session, empty context" });
|
|
@@ -5635,16 +5845,19 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
5635
5845
|
return;
|
|
5636
5846
|
case "wallet":
|
|
5637
5847
|
await runBusy(LOADING, async (signal) => {
|
|
5848
|
+
const streakNow = await client.claimStreak(signal);
|
|
5638
5849
|
const w = await client.wallet(signal);
|
|
5639
5850
|
setBalance(w.balance);
|
|
5640
5851
|
const reversals = w.ledger.filter((e) => e.reason === "ad_reversal");
|
|
5641
5852
|
const reversed = reversals.reduce((sum, e) => sum + Math.abs(e.delta), 0);
|
|
5642
5853
|
const screenouts = w.screenouts_today ?? 0;
|
|
5643
5854
|
const screenoutCredits = w.screenout_credits_today ?? 0;
|
|
5855
|
+
const streakLine = streakNow && streakLabel(streakNow);
|
|
5644
5856
|
push({
|
|
5645
5857
|
kind: "notice",
|
|
5646
5858
|
text: ` balance ${w.balance.toLocaleString("en-US")} credits \xB7 ${w.ads_today} offers today \xB7 $${w.earned_usd_today.toFixed(2)}/$${w.max_reward_usd_per_day.toFixed(2)} earned` + (screenouts ? `
|
|
5647
|
-
plus ${screenoutCredits.toLocaleString("en-US")} credits from ${screenouts} screenout bonus${screenouts === 1 ? "" : "es"} \u2014 attempts that didn't qualify, which CPX pays a little for anyway.` : "") + (
|
|
5859
|
+
plus ${screenoutCredits.toLocaleString("en-US")} credits from ${screenouts} screenout bonus${screenouts === 1 ? "" : "es"} \u2014 attempts that didn't qualify, which CPX pays a little for anyway.` : "") + (streakLine ? `
|
|
5860
|
+
${streakLine}` : "") + (reversals.length ? `
|
|
5648
5861
|
recently: ${reversed.toLocaleString("en-US")} credits from ${reversals.length} offer${reversals.length === 1 ? "" : "s"} were reversed by the provider. Run \`clixad wallet\` for the full ledger.` : "")
|
|
5649
5862
|
});
|
|
5650
5863
|
});
|
|
@@ -5752,6 +5965,32 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
5752
5965
|
case "earn":
|
|
5753
5966
|
await runAdWall();
|
|
5754
5967
|
return;
|
|
5968
|
+
// Through `runBusy` like every other gateway call: the free instance can
|
|
5969
|
+
// cold-start for the better part of a minute, and the box says "esc to
|
|
5970
|
+
// stop" whenever it is locked.
|
|
5971
|
+
case "refer":
|
|
5972
|
+
await runBusy(LOADING, async (signal) => {
|
|
5973
|
+
const info = await client.referral(signal);
|
|
5974
|
+
push({ kind: "notice", text: referLines(info).map((line3) => ` ${line3}`).join("\n") });
|
|
5975
|
+
});
|
|
5976
|
+
return;
|
|
5977
|
+
case "redeem": {
|
|
5978
|
+
if (!arg) {
|
|
5979
|
+
push({ kind: "notice", tone: "warn", text: " usage: /redeem <code>" });
|
|
5980
|
+
return;
|
|
5981
|
+
}
|
|
5982
|
+
await runBusy(LOADING, async (signal) => {
|
|
5983
|
+
const result = await client.redeem(arg, signal);
|
|
5984
|
+
const { ok, text: text2 } = redeemMessage(result);
|
|
5985
|
+
if (ok) setBalance(result.balance);
|
|
5986
|
+
push({
|
|
5987
|
+
kind: "notice",
|
|
5988
|
+
tone: ok ? "good" : "warn",
|
|
5989
|
+
text: ok ? ` ${text2} \xB7 balance ${result.balance.toLocaleString("en-US")}` : ` ${text2}`
|
|
5990
|
+
});
|
|
5991
|
+
});
|
|
5992
|
+
return;
|
|
5993
|
+
}
|
|
5755
5994
|
case "login":
|
|
5756
5995
|
await runLogin(arg);
|
|
5757
5996
|
return;
|
|
@@ -5819,14 +6058,58 @@ ${output}` }
|
|
|
5819
6058
|
},
|
|
5820
6059
|
[push, root, runCommand, runShell, runTurn2]
|
|
5821
6060
|
);
|
|
6061
|
+
const askFeedback = useCallback(async () => {
|
|
6062
|
+
const turn = lastTurnRef.current;
|
|
6063
|
+
if (!turn) return;
|
|
6064
|
+
lastTurnRef.current = null;
|
|
6065
|
+
const amount = wallet?.rewards?.feedback_credits;
|
|
6066
|
+
const pay = amount ? `Both answers pay ${amount.toLocaleString("en-US")} credits` : "Both answers pay the same";
|
|
6067
|
+
const choice = await new Promise((resolve2) => {
|
|
6068
|
+
setPickerSel(0);
|
|
6069
|
+
setPicker({
|
|
6070
|
+
title: "Was that last answer any good?",
|
|
6071
|
+
subtitle: `${pay} \xB7 we keep that exchange to test against \xB7 esc skips`,
|
|
6072
|
+
items: [
|
|
6073
|
+
{ value: "up", label: "yes \u2014 that worked", hint: "" },
|
|
6074
|
+
{ value: "down", label: "no \u2014 that missed", hint: "" },
|
|
6075
|
+
{ value: "skip", label: "skip", hint: "nothing is sent, nothing is stored" }
|
|
6076
|
+
],
|
|
6077
|
+
onPick: (item) => resolve2(item.value),
|
|
6078
|
+
// Esc and "skip" are the same outcome and must stay that way: a skip
|
|
6079
|
+
// that cost something is a prompt people stop reading.
|
|
6080
|
+
onCancel: () => resolve2(null)
|
|
6081
|
+
});
|
|
6082
|
+
});
|
|
6083
|
+
if (choice !== "up" && choice !== "down") return;
|
|
6084
|
+
await runBusy(SENDING_FEEDBACK, async (signal) => {
|
|
6085
|
+
const outcome = await client.sendFeedback(
|
|
6086
|
+
{ ...turn, session: sessionRef.current.id, rating: choice },
|
|
6087
|
+
signal
|
|
6088
|
+
);
|
|
6089
|
+
if (!outcome) return;
|
|
6090
|
+
if (outcome.credited > 0) setBalance(outcome.balance);
|
|
6091
|
+
push({
|
|
6092
|
+
kind: "notice",
|
|
6093
|
+
tone: "good",
|
|
6094
|
+
text: outcome.credited > 0 ? ` thanks \u2014 +${outcome.credited.toLocaleString("en-US")} credits` : (
|
|
6095
|
+
// Recorded and unpaid: the daily ceiling, or a turn already rated.
|
|
6096
|
+
// Worth saying, because the offer of credits was made a moment ago.
|
|
6097
|
+
" thanks \u2014 noted (today's feedback credits are already in)"
|
|
6098
|
+
)
|
|
6099
|
+
});
|
|
6100
|
+
});
|
|
6101
|
+
}, [client, push, runBusy, wallet]);
|
|
5822
6102
|
const runSerially = useCallback(
|
|
5823
6103
|
async (line2) => {
|
|
5824
6104
|
await dispatchLine(line2);
|
|
5825
6105
|
for (let next = dequeue(); next !== void 0; next = dequeue()) {
|
|
5826
6106
|
await dispatchLine(next);
|
|
5827
6107
|
}
|
|
6108
|
+
if (lastTurnRef.current && turnCountRef.current % FEEDBACK_TURN_INTERVAL === 0) {
|
|
6109
|
+
await askFeedback();
|
|
6110
|
+
}
|
|
5828
6111
|
},
|
|
5829
|
-
[dequeue, dispatchLine]
|
|
6112
|
+
[askFeedback, dequeue, dispatchLine]
|
|
5830
6113
|
);
|
|
5831
6114
|
const submit = useCallback(
|
|
5832
6115
|
async (raw) => {
|
|
@@ -6326,7 +6609,7 @@ function tailLines(text, max) {
|
|
|
6326
6609
|
const lines = text.split("\n");
|
|
6327
6610
|
return lines.length <= max ? text : lines.slice(-max).join("\n");
|
|
6328
6611
|
}
|
|
6329
|
-
var WORKING, WAITING_FOR_REWARD, LOADING, COMPACTING, SIGNING_IN, RUNNING, 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, ASK_HINT, PLAN_HINT, EXPAND_MAX_LINES, FALLBACK_ROWS, FALLBACK_COLS, CONTEXT_NOTICE_AT, KEEP_ALWAYS;
|
|
6612
|
+
var WORKING, WAITING_FOR_REWARD, LOADING, COMPACTING, SIGNING_IN, RUNNING, SENDING_FEEDBACK, 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, ASK_HINT, PLAN_HINT, EXPAND_MAX_LINES, FALLBACK_ROWS, FALLBACK_COLS, CONTEXT_NOTICE_AT, KEEP_ALWAYS;
|
|
6330
6613
|
var init_app = __esm({
|
|
6331
6614
|
"src/tui/app.tsx"() {
|
|
6332
6615
|
"use strict";
|
|
@@ -6363,6 +6646,8 @@ var init_app = __esm({
|
|
|
6363
6646
|
COMPACTING = "compacting\u2026";
|
|
6364
6647
|
SIGNING_IN = "signing in\u2026";
|
|
6365
6648
|
RUNNING = "running\u2026";
|
|
6649
|
+
SENDING_FEEDBACK = "sending\u2026";
|
|
6650
|
+
FEEDBACK_TURN_INTERVAL = 50;
|
|
6366
6651
|
NOT_SIGNED_IN = " Not signed in, or the stored token isn't valid for this gateway.\n Run /login to sign in.";
|
|
6367
6652
|
LIVE_OUTPUT_LINES = 5;
|
|
6368
6653
|
COMMITTED_OUTPUT_LINES = 4;
|
|
@@ -6485,6 +6770,10 @@ async function main() {
|
|
|
6485
6770
|
return showWallet(client);
|
|
6486
6771
|
case "earn":
|
|
6487
6772
|
return earn(client, config);
|
|
6773
|
+
case "refer":
|
|
6774
|
+
return refer(client, config);
|
|
6775
|
+
case "redeem":
|
|
6776
|
+
return redeem(client, config, rest[0]);
|
|
6488
6777
|
case "buy":
|
|
6489
6778
|
return buyCmd(client, config, rest[0]);
|
|
6490
6779
|
case "ask":
|
|
@@ -6546,6 +6835,7 @@ async function login(client, config, args) {
|
|
|
6546
6835
|
\u2713 logged in as ${who}`) + suffix);
|
|
6547
6836
|
const bonus = account.created ? c.dim(account.signupBonus === void 0 ? " (signup bonus)" : ` (signup bonus ${credits(account.signupBonus)})`) : "";
|
|
6548
6837
|
console.log(` balance: ${c.bold(credits(account.balance))} credits${bonus}`);
|
|
6838
|
+
await showStreak(client);
|
|
6549
6839
|
console.log(c.dim(` token stored in ${configPath()}`));
|
|
6550
6840
|
return;
|
|
6551
6841
|
}
|
|
@@ -6630,9 +6920,12 @@ async function setModel(client, config, model) {
|
|
|
6630
6920
|
console.log(c.green(`\u2713 model set to ${model}`));
|
|
6631
6921
|
}
|
|
6632
6922
|
async function showWallet(client) {
|
|
6923
|
+
const streak = await client.claimStreak();
|
|
6633
6924
|
const w = await client.wallet();
|
|
6634
6925
|
console.log(`balance: ${c.bold(w.balance.toLocaleString("en-US"))} credits`);
|
|
6635
6926
|
console.log(`${earnedToday(w)} \xB7 offers pay ${grantRangeLabel(w.grant_per_ad_range)}`);
|
|
6927
|
+
const line2 = streak && streakLabel(streak);
|
|
6928
|
+
if (line2) console.log(streak.claimed ? c.green(line2) : c.dim(line2));
|
|
6636
6929
|
if (w.ledger.length) {
|
|
6637
6930
|
console.log(c.dim("recent:"));
|
|
6638
6931
|
const label = (e) => ledgerReasonLabel(e.reason);
|
|
@@ -6679,6 +6972,40 @@ async function earn(client, config) {
|
|
|
6679
6972
|
);
|
|
6680
6973
|
console.log(c.yellow(" pays nothing and is normal. Start another, or check `clixad wallet` later."));
|
|
6681
6974
|
}
|
|
6975
|
+
async function refer(client, config) {
|
|
6976
|
+
if (!config.token) return console.log(c.yellow("Not logged in. Run `clixad login` first."));
|
|
6977
|
+
let info;
|
|
6978
|
+
try {
|
|
6979
|
+
info = await client.referral();
|
|
6980
|
+
} catch (err) {
|
|
6981
|
+
console.error(c.red(err.message));
|
|
6982
|
+
process.exitCode = 1;
|
|
6983
|
+
return;
|
|
6984
|
+
}
|
|
6985
|
+
const [code, share, deal, ...rest] = referLines(info);
|
|
6986
|
+
console.log(`
|
|
6987
|
+
${c.bold(code)}`);
|
|
6988
|
+
console.log(` ${c.cyan(share)}`);
|
|
6989
|
+
console.log(c.dim(` ${deal}`));
|
|
6990
|
+
for (const line2 of rest) console.log(c.dim(` ${line2}`));
|
|
6991
|
+
console.log("");
|
|
6992
|
+
}
|
|
6993
|
+
async function redeem(client, config, code) {
|
|
6994
|
+
if (!config.token) return console.log(c.yellow("Not logged in. Run `clixad login` first."));
|
|
6995
|
+
if (!code) return console.error(c.red("usage: clixad redeem <code>"));
|
|
6996
|
+
let result;
|
|
6997
|
+
try {
|
|
6998
|
+
result = await client.redeem(code);
|
|
6999
|
+
} catch (err) {
|
|
7000
|
+
console.error(c.red(err.message));
|
|
7001
|
+
process.exitCode = 1;
|
|
7002
|
+
return;
|
|
7003
|
+
}
|
|
7004
|
+
const { ok, text } = redeemMessage(result);
|
|
7005
|
+
if (!ok) return console.log(c.yellow(` ${text}`));
|
|
7006
|
+
console.log(c.green(`
|
|
7007
|
+
\u2713 ${text}`) + ` \xB7 balance ${c.bold(credits(result.balance))}`);
|
|
7008
|
+
}
|
|
6682
7009
|
async function buyCmd(client, config, pack) {
|
|
6683
7010
|
if (!config.token) return console.log(c.yellow("Not logged in. Run `clixad login` first."));
|
|
6684
7011
|
const { enabled, packs } = await client.creditPacks();
|
|
@@ -6845,10 +7172,11 @@ async function repl(client, config, opts = {}) {
|
|
|
6845
7172
|
session = loadSession(opts.resume);
|
|
6846
7173
|
if (!session) return console.log(c.red(`no such session: ${opts.resume}`));
|
|
6847
7174
|
}
|
|
7175
|
+
const streak = await client.claimStreak();
|
|
6848
7176
|
const wallet = await safeWallet(client);
|
|
6849
7177
|
clearScreen();
|
|
6850
7178
|
const { startTui: startTui2 } = await Promise.resolve().then(() => (init_tui(), tui_exports));
|
|
6851
|
-
await startTui2({ client, config, wallet, session, initialTask: opts.task });
|
|
7179
|
+
await startTui2({ client, config, wallet, streak, session, initialTask: opts.task });
|
|
6852
7180
|
}
|
|
6853
7181
|
async function runTurn(client, config, messages) {
|
|
6854
7182
|
try {
|
|
@@ -6875,6 +7203,13 @@ async function runTurn(client, config, messages) {
|
|
|
6875
7203
|
return void 0;
|
|
6876
7204
|
}
|
|
6877
7205
|
}
|
|
7206
|
+
async function showStreak(client) {
|
|
7207
|
+
const streak = await client.claimStreak();
|
|
7208
|
+
const line2 = streak && streakLabel(streak);
|
|
7209
|
+
if (!line2) return streak;
|
|
7210
|
+
console.log(streak.claimed ? c.green(` ${line2}`) : c.dim(` ${line2}`));
|
|
7211
|
+
return streak;
|
|
7212
|
+
}
|
|
6878
7213
|
async function safeWallet(client) {
|
|
6879
7214
|
try {
|
|
6880
7215
|
return await client.wallet();
|
|
@@ -6892,6 +7227,8 @@ function printHelp() {
|
|
|
6892
7227
|
${c.cyan("model")} <id> set default model
|
|
6893
7228
|
${c.cyan("wallet")} balance, ads today, ledger
|
|
6894
7229
|
${c.cyan("earn")} open the ad wall to earn credits
|
|
7230
|
+
${c.cyan("refer")} your invite code, and what it has earned
|
|
7231
|
+
${c.cyan("redeem")} <code> spend a code you were given
|
|
6895
7232
|
${c.cyan("buy")} [pack] list credit packs / buy one via Stripe
|
|
6896
7233
|
${c.cyan("ask")} "<prompt>" one-shot completion (no tools)
|
|
6897
7234
|
${c.cyan("agent")} "<task>" open the REPL with a task already running
|
|
@@ -6901,7 +7238,9 @@ function printHelp() {
|
|
|
6901
7238
|
${c.cyan("--resume")} [id] list saved sessions, or resume one
|
|
6902
7239
|
${c.cyan("--version")} print the version and exit
|
|
6903
7240
|
(no command) interactive coding REPL ${c.dim("(signs you in if needed)")}
|
|
6904
|
-
"<task>" same as ${c.cyan("agent")} \u2014 anything that is not a command is a task
|
|
7241
|
+
"<task>" same as ${c.cyan("agent")} \u2014 anything that is not a command is a task
|
|
7242
|
+
|
|
7243
|
+
${c.dim("Discord: https://discord.gg/pwxPM3QyF \u2014 bugs and feature requests are read there")}`);
|
|
6905
7244
|
}
|
|
6906
7245
|
main().catch((err) => {
|
|
6907
7246
|
console.error(c.red(err.message));
|