clixad 0.0.1-beta.5 → 0.0.1-beta.7
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 +447 -168
- package/package.json +1 -1
package/dist/clixad.mjs
CHANGED
|
@@ -88,6 +88,7 @@ var init_client = __esm({
|
|
|
88
88
|
LEDGER_REASONS = {
|
|
89
89
|
signup_bonus: "signup bonus",
|
|
90
90
|
ad_reward: "offer reward",
|
|
91
|
+
ad_screenout_bonus: "screenout bonus (didn't qualify)",
|
|
91
92
|
ad_reversal: "offer reward reversed by the provider",
|
|
92
93
|
purchase: "credits purchased",
|
|
93
94
|
usage: "model usage",
|
|
@@ -315,12 +316,25 @@ var init_client = __esm({
|
|
|
315
316
|
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
316
317
|
import { homedir } from "node:os";
|
|
317
318
|
import { dirname, join } from "node:path";
|
|
319
|
+
function sameEndpoint(a, b) {
|
|
320
|
+
const norm = (raw) => {
|
|
321
|
+
try {
|
|
322
|
+
const url = new URL(raw);
|
|
323
|
+
const host = url.hostname === "localhost" ? "127.0.0.1" : url.hostname;
|
|
324
|
+
return `${url.protocol}//${host}:${url.port}${url.pathname.replace(/\/+$/, "")}`;
|
|
325
|
+
} catch {
|
|
326
|
+
return raw.trim().replace(/\/+$/, "");
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
return norm(a) === norm(b);
|
|
330
|
+
}
|
|
318
331
|
function loadConfig() {
|
|
319
332
|
let stored = {};
|
|
320
333
|
try {
|
|
321
334
|
stored = JSON.parse(readFileSync(CONFIG_PATH, "utf8"));
|
|
322
335
|
} catch {
|
|
323
336
|
}
|
|
337
|
+
const mintedAgainst = stored.gatewayUrl;
|
|
324
338
|
for (const [key, stale] of Object.entries(SUPERSEDED_DEFAULTS)) {
|
|
325
339
|
const value = stored[key];
|
|
326
340
|
if (typeof value === "string" && stale.includes(value) && value !== DEFAULTS[key]) {
|
|
@@ -332,6 +346,10 @@ function loadConfig() {
|
|
|
332
346
|
const value = process.env[envVar];
|
|
333
347
|
if (value) config[key] = value;
|
|
334
348
|
}
|
|
349
|
+
const migratedGateway = mintedAgainst !== void 0 && stored.gatewayUrl === void 0;
|
|
350
|
+
if (migratedGateway && !sameEndpoint(mintedAgainst, config.gatewayUrl)) {
|
|
351
|
+
for (const key of IDENTITY_KEYS) delete config[key];
|
|
352
|
+
}
|
|
335
353
|
return config;
|
|
336
354
|
}
|
|
337
355
|
function saveConfig(config) {
|
|
@@ -345,7 +363,7 @@ function saveConfig(config) {
|
|
|
345
363
|
function configPath() {
|
|
346
364
|
return CONFIG_PATH;
|
|
347
365
|
}
|
|
348
|
-
var CONFIG_PATH, DEFAULTS, ENV_OVERRIDES, SUPERSEDED_DEFAULTS;
|
|
366
|
+
var CONFIG_PATH, DEFAULTS, ENV_OVERRIDES, SUPERSEDED_DEFAULTS, IDENTITY_KEYS;
|
|
349
367
|
var init_config = __esm({
|
|
350
368
|
"src/config.ts"() {
|
|
351
369
|
"use strict";
|
|
@@ -367,6 +385,121 @@ var init_config = __esm({
|
|
|
367
385
|
gatewayUrl: ["http://127.0.0.1:8787", "http://localhost:8787"],
|
|
368
386
|
dashboardUrl: ["http://127.0.0.1:8788", "http://localhost:8788"]
|
|
369
387
|
};
|
|
388
|
+
IDENTITY_KEYS = ["token", "userId", "email", "login"];
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// src/browser.ts
|
|
393
|
+
import { spawn } from "node:child_process";
|
|
394
|
+
function openBrowser(url) {
|
|
395
|
+
const cmd = process.platform === "win32" ? "cmd" : process.platform === "darwin" ? "open" : "xdg-open";
|
|
396
|
+
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
397
|
+
const child = spawn(cmd, args, { detached: true, stdio: "ignore", windowsHide: true });
|
|
398
|
+
child.on("error", () => void 0);
|
|
399
|
+
child.unref();
|
|
400
|
+
}
|
|
401
|
+
var init_browser = __esm({
|
|
402
|
+
"src/browser.ts"() {
|
|
403
|
+
"use strict";
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
// src/login.ts
|
|
408
|
+
function parseLoginArgs(args) {
|
|
409
|
+
let invite;
|
|
410
|
+
const rest = [];
|
|
411
|
+
for (let i = 0; i < args.length; i++) {
|
|
412
|
+
const a = args[i];
|
|
413
|
+
if (a === "--invite") invite = args[++i];
|
|
414
|
+
else if (a.startsWith("--invite=")) invite = a.slice("--invite=".length);
|
|
415
|
+
else rest.push(a);
|
|
416
|
+
}
|
|
417
|
+
return { invite: invite?.trim() || void 0, email: rest[0] };
|
|
418
|
+
}
|
|
419
|
+
async function performLogin(client, config, args, report, signal) {
|
|
420
|
+
if (!args.email) {
|
|
421
|
+
const start = await client.deviceStart().catch(() => null);
|
|
422
|
+
if (start) return githubLogin(client, config, start, args.invite, report, signal);
|
|
423
|
+
}
|
|
424
|
+
return devLogin(client, config, args.email, args.invite);
|
|
425
|
+
}
|
|
426
|
+
async function githubLogin(client, config, start, invite, report, signal) {
|
|
427
|
+
report.verify(start);
|
|
428
|
+
openBrowser(start.verification_uri);
|
|
429
|
+
const deadline = Date.now() + start.expires_in * 1e3;
|
|
430
|
+
let interval = Math.max(start.interval, 1);
|
|
431
|
+
while (Date.now() < deadline) {
|
|
432
|
+
await sleep(interval * 1e3, signal);
|
|
433
|
+
if (signal?.aborted) return { status: "aborted" };
|
|
434
|
+
report.tick?.();
|
|
435
|
+
const poll = await client.devicePoll(start.session, invite);
|
|
436
|
+
if (poll.status === "pending") {
|
|
437
|
+
if (poll.interval) interval = poll.interval;
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
if (poll.status === "closed") {
|
|
441
|
+
return { status: "closed", message: poll.message, hadInvite: Boolean(invite) };
|
|
442
|
+
}
|
|
443
|
+
if (poll.status === "complete") {
|
|
444
|
+
config.token = poll.token;
|
|
445
|
+
config.userId = poll.userId;
|
|
446
|
+
config.email = poll.email;
|
|
447
|
+
config.login = poll.login;
|
|
448
|
+
saveConfig(config);
|
|
449
|
+
return {
|
|
450
|
+
status: "ok",
|
|
451
|
+
account: {
|
|
452
|
+
email: poll.email,
|
|
453
|
+
login: poll.login,
|
|
454
|
+
balance: poll.balance,
|
|
455
|
+
created: poll.created,
|
|
456
|
+
signupBonus: poll.signupBonus
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
return { status: "failed", error: poll.error ?? poll.status };
|
|
461
|
+
}
|
|
462
|
+
return { status: "timeout" };
|
|
463
|
+
}
|
|
464
|
+
async function devLogin(client, config, email, invite) {
|
|
465
|
+
let res;
|
|
466
|
+
try {
|
|
467
|
+
res = await client.signupDev(email, invite);
|
|
468
|
+
} catch (err) {
|
|
469
|
+
if (err instanceof SignupClosedError) {
|
|
470
|
+
return { status: "closed", message: err.message, hadInvite: Boolean(invite) };
|
|
471
|
+
}
|
|
472
|
+
throw err;
|
|
473
|
+
}
|
|
474
|
+
config.token = res.token;
|
|
475
|
+
config.userId = res.userId;
|
|
476
|
+
config.email = res.email;
|
|
477
|
+
delete config.login;
|
|
478
|
+
saveConfig(config);
|
|
479
|
+
return { status: "ok", account: { email: res.email, balance: res.balance, created: true } };
|
|
480
|
+
}
|
|
481
|
+
function sleep(ms, signal) {
|
|
482
|
+
return new Promise((resolve2) => {
|
|
483
|
+
if (!signal) {
|
|
484
|
+
setTimeout(resolve2, ms);
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
if (signal.aborted) return resolve2();
|
|
488
|
+
const done = () => {
|
|
489
|
+
clearTimeout(timer);
|
|
490
|
+
signal.removeEventListener("abort", done);
|
|
491
|
+
resolve2();
|
|
492
|
+
};
|
|
493
|
+
const timer = setTimeout(done, ms);
|
|
494
|
+
signal.addEventListener("abort", done, { once: true });
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
var init_login = __esm({
|
|
498
|
+
"src/login.ts"() {
|
|
499
|
+
"use strict";
|
|
500
|
+
init_client();
|
|
501
|
+
init_browser();
|
|
502
|
+
init_config();
|
|
370
503
|
}
|
|
371
504
|
});
|
|
372
505
|
|
|
@@ -493,7 +626,7 @@ var init_diff = __esm({
|
|
|
493
626
|
});
|
|
494
627
|
|
|
495
628
|
// src/tools.ts
|
|
496
|
-
import { spawn } from "node:child_process";
|
|
629
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
497
630
|
import { existsSync, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, statSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
498
631
|
import { isAbsolute, relative, resolve, dirname as dirname2, join as join2, sep } from "node:path";
|
|
499
632
|
function safeResolve(root, p) {
|
|
@@ -531,7 +664,7 @@ function killTree(child) {
|
|
|
531
664
|
return;
|
|
532
665
|
}
|
|
533
666
|
if (process.platform === "win32") {
|
|
534
|
-
|
|
667
|
+
spawn2("taskkill", ["/pid", String(child.pid), "/T", "/F"], { stdio: "ignore", windowsHide: true });
|
|
535
668
|
} else {
|
|
536
669
|
try {
|
|
537
670
|
process.kill(-child.pid, "SIGTERM");
|
|
@@ -542,7 +675,7 @@ function killTree(child) {
|
|
|
542
675
|
}
|
|
543
676
|
function execute(ctx, command, timeout) {
|
|
544
677
|
return new Promise((resolvePromise) => {
|
|
545
|
-
const child =
|
|
678
|
+
const child = spawn2(command, {
|
|
546
679
|
cwd: ctx.root,
|
|
547
680
|
shell: true,
|
|
548
681
|
windowsHide: true,
|
|
@@ -1237,7 +1370,7 @@ var init_session = __esm({
|
|
|
1237
1370
|
});
|
|
1238
1371
|
|
|
1239
1372
|
// src/kimi.ts
|
|
1240
|
-
import { spawn as
|
|
1373
|
+
import { spawn as spawn3, spawnSync as spawnSync2 } from "node:child_process";
|
|
1241
1374
|
import { existsSync as existsSync4, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
1242
1375
|
import { homedir as homedir4 } from "node:os";
|
|
1243
1376
|
import { dirname as dirname3, join as join5 } from "node:path";
|
|
@@ -1294,7 +1427,7 @@ function runKimiCode(config, task, opts = {}) {
|
|
|
1294
1427
|
OPENAI_API_KEY: config.token
|
|
1295
1428
|
};
|
|
1296
1429
|
return new Promise((resolve2) => {
|
|
1297
|
-
const child = process.platform === "win32" ?
|
|
1430
|
+
const child = process.platform === "win32" ? spawn3([bin, ...args].map(quoteWin).join(" "), { stdio: "inherit", env, shell: true }) : spawn3(bin, args, { stdio: "inherit", env });
|
|
1298
1431
|
child.on("error", (err) => {
|
|
1299
1432
|
if (err.code === "ENOENT") console.log("\n" + INSTALL_HELP);
|
|
1300
1433
|
else console.error(`failed to launch Kimi CLI: ${err.message}`);
|
|
@@ -1371,6 +1504,10 @@ function centered(rendered, visibleW, colW) {
|
|
|
1371
1504
|
const lead = Math.max(0, Math.floor((colW - visibleW) / 2));
|
|
1372
1505
|
return cell(" ".repeat(lead) + rendered, lead + visibleW);
|
|
1373
1506
|
}
|
|
1507
|
+
function bannerTip(index) {
|
|
1508
|
+
const i = index ?? Math.floor(Math.random() * BANNER_TIPS.length);
|
|
1509
|
+
return BANNER_TIPS[(i % BANNER_TIPS.length + BANNER_TIPS.length) % BANNER_TIPS.length];
|
|
1510
|
+
}
|
|
1374
1511
|
function earnedLabel(opts) {
|
|
1375
1512
|
return `$${opts.earnedUsdToday.toFixed(2)}/$${opts.maxRewardUsd.toFixed(2)} today`;
|
|
1376
1513
|
}
|
|
@@ -1382,14 +1519,19 @@ function welcomeBanner(opts) {
|
|
|
1382
1519
|
const cwd = opts.cwd.replace(homedir5(), "~").replace(/\\/g, "/");
|
|
1383
1520
|
const welcome = opts.name ? `Welcome back, ${truncate(opts.name, 18)}!` : "Welcome to Clixad!";
|
|
1384
1521
|
const duck = duckLines();
|
|
1385
|
-
const model = truncate(opts.model, leftW - 4);
|
|
1386
1522
|
const home = truncate(cwd, leftW);
|
|
1523
|
+
const hint = bannerTip(opts.tipIndex);
|
|
1524
|
+
const hintDesc = truncate(hint.desc, Math.max(1, leftW - 3 - hint.key.length));
|
|
1387
1525
|
const left = [
|
|
1388
1526
|
centered(BOLD + TEXT + welcome + R2, welcome.length, leftW),
|
|
1389
1527
|
cell("", 0),
|
|
1390
1528
|
...duck.map((d) => centered(d, DUCK_W, leftW)),
|
|
1391
1529
|
cell("", 0),
|
|
1392
|
-
centered(
|
|
1530
|
+
centered(
|
|
1531
|
+
MANGO + "\u25C6 " + R2 + BOLD + TEXT + hint.key + R2 + FAINT + " " + hintDesc + R2,
|
|
1532
|
+
2 + hint.key.length + 1 + hintDesc.length,
|
|
1533
|
+
leftW
|
|
1534
|
+
),
|
|
1393
1535
|
centered(FAINT + home + R2, home.length, leftW)
|
|
1394
1536
|
];
|
|
1395
1537
|
const tip = (k, d) => cell(MANGO + k + R2 + FAINT + " " + d + R2, k.length + 1 + d.length);
|
|
@@ -1420,10 +1562,11 @@ function welcomeBanner(opts) {
|
|
|
1420
1562
|
}
|
|
1421
1563
|
function compactBanner(opts) {
|
|
1422
1564
|
const welcome = opts.name ? `Welcome back, ${opts.name}!` : "Welcome to Clixad!";
|
|
1565
|
+
const hint = bannerTip(opts.tipIndex);
|
|
1423
1566
|
return duckLines().map((d) => " " + d).join("\n") + `
|
|
1424
1567
|
|
|
1425
1568
|
${BOLD}${TEXT}${welcome}${R2}
|
|
1426
|
-
${MANGO}\u25C6${R2} ${
|
|
1569
|
+
${MANGO}\u25C6${R2} ${BOLD}${TEXT}${hint.key}${R2} ${FAINT}${hint.desc}${R2} ${FAINT}\xB7${R2} ${opts.balance.toLocaleString("en-US")} credits
|
|
1427
1570
|
`;
|
|
1428
1571
|
}
|
|
1429
1572
|
function clearScreen() {
|
|
@@ -1435,7 +1578,7 @@ function clearScreen() {
|
|
|
1435
1578
|
function visibleLength(s) {
|
|
1436
1579
|
return s.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
1437
1580
|
}
|
|
1438
|
-
var R2, BOLD, fg, bg, MANGO, TEXT, MUTED, FAINT, PAL, DUCK, DUCK_W, cell, padTo, truncate, MIN_BANNER_WIDTH, RIGHT_MAX;
|
|
1581
|
+
var R2, BOLD, fg, bg, MANGO, TEXT, MUTED, FAINT, PAL, DUCK, DUCK_W, cell, padTo, truncate, BANNER_TIPS, MIN_BANNER_WIDTH, RIGHT_MAX;
|
|
1439
1582
|
var init_banner = __esm({
|
|
1440
1583
|
"src/banner.ts"() {
|
|
1441
1584
|
"use strict";
|
|
@@ -1477,26 +1620,20 @@ var init_banner = __esm({
|
|
|
1477
1620
|
cell = (text, w) => ({ text, w });
|
|
1478
1621
|
padTo = (c2, width) => (c2?.text ?? "") + " ".repeat(Math.max(0, width - (c2?.w ?? 0)));
|
|
1479
1622
|
truncate = (s, max) => s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
|
|
1623
|
+
BANNER_TIPS = [
|
|
1624
|
+
{ key: "/init", desc: "make a CLIXAD.md" },
|
|
1625
|
+
{ key: "shift+tab", desc: "switch mode" },
|
|
1626
|
+
{ key: "@file", desc: "add a file to context" },
|
|
1627
|
+
{ key: "ctrl+o", desc: "expand tool output" },
|
|
1628
|
+
{ key: "esc", desc: "stop the current turn" },
|
|
1629
|
+
{ key: "/compact", desc: "free up context" },
|
|
1630
|
+
{ key: "/earn", desc: "top up credits" }
|
|
1631
|
+
];
|
|
1480
1632
|
MIN_BANNER_WIDTH = 66;
|
|
1481
1633
|
RIGHT_MAX = 34;
|
|
1482
1634
|
}
|
|
1483
1635
|
});
|
|
1484
1636
|
|
|
1485
|
-
// src/browser.ts
|
|
1486
|
-
import { spawn as spawn3 } from "node:child_process";
|
|
1487
|
-
function openBrowser(url) {
|
|
1488
|
-
const cmd = process.platform === "win32" ? "cmd" : process.platform === "darwin" ? "open" : "xdg-open";
|
|
1489
|
-
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
1490
|
-
const child = spawn3(cmd, args, { detached: true, stdio: "ignore", windowsHide: true });
|
|
1491
|
-
child.on("error", () => void 0);
|
|
1492
|
-
child.unref();
|
|
1493
|
-
}
|
|
1494
|
-
var init_browser = __esm({
|
|
1495
|
-
"src/browser.ts"() {
|
|
1496
|
-
"use strict";
|
|
1497
|
-
}
|
|
1498
|
-
});
|
|
1499
|
-
|
|
1500
1637
|
// src/version.ts
|
|
1501
1638
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
1502
1639
|
function readVersion() {
|
|
@@ -1713,6 +1850,17 @@ var init_commands = __esm({
|
|
|
1713
1850
|
// forms, surveys and app trials, and describing the top-up as a video costs
|
|
1714
1851
|
// the user the one expectation that makes a screenout make sense.
|
|
1715
1852
|
{ name: "earn", desc: "open the offer wall to earn credits" },
|
|
1853
|
+
// Reachable from inside the REPL because that is where the 401 is printed.
|
|
1854
|
+
// "Run `clixad login` first" is not a runnable instruction at this prompt —
|
|
1855
|
+
// anything without a leading slash is a prompt, so it went to the model and
|
|
1856
|
+
// came back as the same 401.
|
|
1857
|
+
//
|
|
1858
|
+
// Deliberately *no* `args`, though `/login <email>` works: an arg hint makes
|
|
1859
|
+
// enter complete the command instead of running it, and this is the one
|
|
1860
|
+
// command whose whole job is to be the exit from a stuck REPL. The email form
|
|
1861
|
+
// is the dev shortcut, documented on `clixad login [email]`, not the path
|
|
1862
|
+
// somebody in a 401 loop needs.
|
|
1863
|
+
{ name: "login", desc: "sign in, or switch account" },
|
|
1716
1864
|
{ name: "compact", desc: "summarise the conversation to free context" },
|
|
1717
1865
|
{ name: "clear", desc: "clear the conversation context" },
|
|
1718
1866
|
{ name: "init", desc: "write a CLIXAD.md for this project" },
|
|
@@ -2029,6 +2177,45 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
2029
2177
|
function lineCount(text, cols) {
|
|
2030
2178
|
return text.split("\n").reduce((n, line2) => n + Math.max(1, Math.ceil(visibleLength(line2) / Math.max(1, cols))), 0);
|
|
2031
2179
|
}
|
|
2180
|
+
function tailRows(text, cols, max) {
|
|
2181
|
+
if (max <= 0) return "";
|
|
2182
|
+
const width = Math.max(1, cols);
|
|
2183
|
+
const lines = text.split("\n");
|
|
2184
|
+
const kept = [];
|
|
2185
|
+
let used = 0;
|
|
2186
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
2187
|
+
const line2 = lines[i];
|
|
2188
|
+
const h = Math.max(1, Math.ceil(visibleLength(line2) / width));
|
|
2189
|
+
if (used + h <= max) {
|
|
2190
|
+
kept.unshift(line2);
|
|
2191
|
+
used += h;
|
|
2192
|
+
continue;
|
|
2193
|
+
}
|
|
2194
|
+
const room = max - used;
|
|
2195
|
+
if (room > 0) kept.unshift(line2.slice(-(room * width)));
|
|
2196
|
+
break;
|
|
2197
|
+
}
|
|
2198
|
+
return kept.join("\n");
|
|
2199
|
+
}
|
|
2200
|
+
function headRows(text, cols, max) {
|
|
2201
|
+
if (max <= 0) return "";
|
|
2202
|
+
const width = Math.max(1, cols);
|
|
2203
|
+
const lines = text.split("\n");
|
|
2204
|
+
const kept = [];
|
|
2205
|
+
let used = 0;
|
|
2206
|
+
for (const line2 of lines) {
|
|
2207
|
+
const h = Math.max(1, Math.ceil(visibleLength(line2) / width));
|
|
2208
|
+
if (used + h <= max) {
|
|
2209
|
+
kept.push(line2);
|
|
2210
|
+
used += h;
|
|
2211
|
+
continue;
|
|
2212
|
+
}
|
|
2213
|
+
const room = max - used;
|
|
2214
|
+
if (room > 0) kept.push(line2.slice(0, room * width));
|
|
2215
|
+
break;
|
|
2216
|
+
}
|
|
2217
|
+
return kept.join("\n");
|
|
2218
|
+
}
|
|
2032
2219
|
function entryHeight(entry, cols) {
|
|
2033
2220
|
const margin = entry.kind === "banner" ? 0 : 1;
|
|
2034
2221
|
switch (entry.kind) {
|
|
@@ -2075,7 +2262,7 @@ function EntryView({ entry }) {
|
|
|
2075
2262
|
) });
|
|
2076
2263
|
}
|
|
2077
2264
|
}
|
|
2078
|
-
var MANGO3, MANGO_BRIGHT;
|
|
2265
|
+
var MANGO3, MANGO_BRIGHT, SLATE, MODE_STYLE;
|
|
2079
2266
|
var init_views = __esm({
|
|
2080
2267
|
"src/tui/views.tsx"() {
|
|
2081
2268
|
"use strict";
|
|
@@ -2083,6 +2270,12 @@ var init_views = __esm({
|
|
|
2083
2270
|
init_markdown();
|
|
2084
2271
|
MANGO3 = "#f5b841";
|
|
2085
2272
|
MANGO_BRIGHT = "#ffcf6b";
|
|
2273
|
+
SLATE = "#8b98ae";
|
|
2274
|
+
MODE_STYLE = {
|
|
2275
|
+
normal: { color: SLATE, glyph: "\u23F5" },
|
|
2276
|
+
acceptEdits: { color: "#7ee787", glyph: "\u23F5\u23F5" },
|
|
2277
|
+
plan: { color: "#79c0ff", glyph: "\u23F8" }
|
|
2278
|
+
};
|
|
2086
2279
|
}
|
|
2087
2280
|
});
|
|
2088
2281
|
|
|
@@ -2160,13 +2353,34 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
2160
2353
|
const lastOutputRef = useRef("");
|
|
2161
2354
|
const runningToolRef = useRef(null);
|
|
2162
2355
|
const pendingTaskRef = useRef(null);
|
|
2356
|
+
const deltaBufRef = useRef("");
|
|
2357
|
+
const deltaTimerRef = useRef(null);
|
|
2163
2358
|
const sponsorRef = useRef(sponsorSource());
|
|
2164
2359
|
const sponsorIdxRef = useRef(0);
|
|
2165
2360
|
const sponsorPrevRef = useRef(void 0);
|
|
2166
2361
|
const tallyRef = useRef(createTally());
|
|
2362
|
+
const heightCacheRef = useRef(/* @__PURE__ */ new Map());
|
|
2167
2363
|
const push = useCallback((e) => {
|
|
2168
2364
|
setEntries((prev) => [...prev, { ...e, id: idRef.current++ }]);
|
|
2169
2365
|
}, []);
|
|
2366
|
+
const flushDelta = useCallback(() => {
|
|
2367
|
+
if (deltaTimerRef.current) {
|
|
2368
|
+
clearTimeout(deltaTimerRef.current);
|
|
2369
|
+
deltaTimerRef.current = null;
|
|
2370
|
+
}
|
|
2371
|
+
const buffered = deltaBufRef.current;
|
|
2372
|
+
if (!buffered) return;
|
|
2373
|
+
deltaBufRef.current = "";
|
|
2374
|
+
setLive((l) => ({ ...l ?? { text: "" }, text: (l?.text ?? "") + buffered }));
|
|
2375
|
+
}, []);
|
|
2376
|
+
const dropDelta = useCallback(() => {
|
|
2377
|
+
if (deltaTimerRef.current) {
|
|
2378
|
+
clearTimeout(deltaTimerRef.current);
|
|
2379
|
+
deltaTimerRef.current = null;
|
|
2380
|
+
}
|
|
2381
|
+
deltaBufRef.current = "";
|
|
2382
|
+
}, []);
|
|
2383
|
+
useEffect(() => dropDelta, [dropDelta]);
|
|
2170
2384
|
useEffect(() => {
|
|
2171
2385
|
const files = contextRef.current.files;
|
|
2172
2386
|
if (files.length) push({ kind: "notice", text: ` context: ${files.join(", ")}` });
|
|
@@ -2222,10 +2436,13 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
2222
2436
|
}, []);
|
|
2223
2437
|
const handleEvent = useCallback(
|
|
2224
2438
|
(event) => {
|
|
2439
|
+
if (event.type === "delta") {
|
|
2440
|
+
deltaBufRef.current += event.text;
|
|
2441
|
+
if (!deltaTimerRef.current) deltaTimerRef.current = setTimeout(flushDelta, DELTA_FLUSH_MS);
|
|
2442
|
+
return;
|
|
2443
|
+
}
|
|
2444
|
+
flushDelta();
|
|
2225
2445
|
switch (event.type) {
|
|
2226
|
-
case "delta":
|
|
2227
|
-
setLive((l) => ({ ...l ?? { text: "" }, text: (l?.text ?? "") + event.text }));
|
|
2228
|
-
return;
|
|
2229
2446
|
case "message":
|
|
2230
2447
|
push({ kind: "assistant", text: event.content });
|
|
2231
2448
|
setLive((l) => ({ ...l ?? { text: "" }, text: "" }));
|
|
@@ -2265,7 +2482,7 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
2265
2482
|
return;
|
|
2266
2483
|
}
|
|
2267
2484
|
},
|
|
2268
|
-
[cols, push]
|
|
2485
|
+
[cols, flushDelta, push]
|
|
2269
2486
|
);
|
|
2270
2487
|
const runTurn2 = useCallback(
|
|
2271
2488
|
async (task) => {
|
|
@@ -2339,18 +2556,19 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
2339
2556
|
that is normal, just start another.`
|
|
2340
2557
|
});
|
|
2341
2558
|
} else if (err instanceof AuthError) {
|
|
2342
|
-
push({ kind: "notice", tone: "error", text:
|
|
2559
|
+
push({ kind: "notice", tone: "error", text: NOT_SIGNED_IN });
|
|
2343
2560
|
} else {
|
|
2344
2561
|
push({ kind: "notice", tone: "error", text: ` error: ${err.message}` });
|
|
2345
2562
|
}
|
|
2346
2563
|
} finally {
|
|
2347
2564
|
abortRef.current = null;
|
|
2565
|
+
dropDelta();
|
|
2348
2566
|
setLive(null);
|
|
2349
2567
|
setBusy(false);
|
|
2350
2568
|
setSponsor(null);
|
|
2351
2569
|
}
|
|
2352
2570
|
},
|
|
2353
|
-
[client, contextWindow, handleEvent, model, nextSponsor, permit, push, root, session?.title]
|
|
2571
|
+
[client, contextWindow, dropDelta, handleEvent, model, nextSponsor, permit, push, root, session?.title]
|
|
2354
2572
|
);
|
|
2355
2573
|
const stopCurrent = useCallback(() => {
|
|
2356
2574
|
abortRef.current?.abort();
|
|
@@ -2367,6 +2585,7 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
2367
2585
|
await fn(ac.signal);
|
|
2368
2586
|
} catch (err) {
|
|
2369
2587
|
if (ac.signal.aborted) push({ kind: "notice", tone: "warn", text: " (stopped)" });
|
|
2588
|
+
else if (err instanceof AuthError) push({ kind: "notice", tone: "error", text: NOT_SIGNED_IN });
|
|
2370
2589
|
else push({ kind: "notice", tone: "error", text: ` ${err.message}` });
|
|
2371
2590
|
} finally {
|
|
2372
2591
|
busyAbortRef.current = null;
|
|
@@ -2376,6 +2595,56 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
2376
2595
|
},
|
|
2377
2596
|
[push]
|
|
2378
2597
|
);
|
|
2598
|
+
const runLogin = useCallback(
|
|
2599
|
+
async (arg) => {
|
|
2600
|
+
const args = parseLoginArgs(arg.split(/\s+/).filter(Boolean));
|
|
2601
|
+
await runBusy(SIGNING_IN, async (signal) => {
|
|
2602
|
+
const outcome = await performLogin(
|
|
2603
|
+
client,
|
|
2604
|
+
config,
|
|
2605
|
+
args,
|
|
2606
|
+
{
|
|
2607
|
+
verify: (start) => push({
|
|
2608
|
+
kind: "notice",
|
|
2609
|
+
text: ` Open ${start.verification_uri}
|
|
2610
|
+
and enter the code: ${start.user_code}
|
|
2611
|
+
Waiting for GitHub \u2014 esc to cancel.`
|
|
2612
|
+
})
|
|
2613
|
+
},
|
|
2614
|
+
signal
|
|
2615
|
+
);
|
|
2616
|
+
switch (outcome.status) {
|
|
2617
|
+
case "ok": {
|
|
2618
|
+
const { account } = outcome;
|
|
2619
|
+
setBalance(account.balance);
|
|
2620
|
+
push({
|
|
2621
|
+
kind: "notice",
|
|
2622
|
+
tone: "good",
|
|
2623
|
+
text: ` signed in as ${account.login ?? account.email ?? "you"} \xB7 balance ${account.balance.toLocaleString("en-US")} credits` + (account.created ? " (signup bonus)" : "")
|
|
2624
|
+
});
|
|
2625
|
+
return;
|
|
2626
|
+
}
|
|
2627
|
+
case "closed":
|
|
2628
|
+
push({
|
|
2629
|
+
kind: "notice",
|
|
2630
|
+
tone: "warn",
|
|
2631
|
+
text: ` Clixad isn't open yet.
|
|
2632
|
+
${outcome.message}` + (outcome.hadInvite ? "\n That invite code wasn't accepted \u2014 check it for typos." : "")
|
|
2633
|
+
});
|
|
2634
|
+
return;
|
|
2635
|
+
case "timeout":
|
|
2636
|
+
push({ kind: "notice", tone: "warn", text: " login timed out \u2014 /login to try again." });
|
|
2637
|
+
return;
|
|
2638
|
+
case "aborted":
|
|
2639
|
+
push({ kind: "notice", tone: "warn", text: " (stopped) \u2014 /login to try again." });
|
|
2640
|
+
return;
|
|
2641
|
+
default:
|
|
2642
|
+
push({ kind: "notice", tone: "error", text: ` login failed: ${outcome.error}` });
|
|
2643
|
+
}
|
|
2644
|
+
});
|
|
2645
|
+
},
|
|
2646
|
+
[client, config, push, runBusy]
|
|
2647
|
+
);
|
|
2379
2648
|
const runAdWall = useCallback(async () => {
|
|
2380
2649
|
const task = pendingTaskRef.current;
|
|
2381
2650
|
pendingTaskRef.current = null;
|
|
@@ -2394,7 +2663,7 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
2394
2663
|
await runBusy(WAITING_FOR_REWARD, async (signal) => {
|
|
2395
2664
|
const deadline = Date.now() + 5 * 6e4;
|
|
2396
2665
|
while (Date.now() < deadline && !credited && !signal.aborted) {
|
|
2397
|
-
await
|
|
2666
|
+
await sleep2(3e3, signal);
|
|
2398
2667
|
if (signal.aborted) break;
|
|
2399
2668
|
const w = await client.wallet(signal).catch(() => void 0);
|
|
2400
2669
|
if (w && w.balance > before) {
|
|
@@ -2523,9 +2792,12 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
2523
2792
|
setBalance(w.balance);
|
|
2524
2793
|
const reversals = w.ledger.filter((e) => e.reason === "ad_reversal");
|
|
2525
2794
|
const reversed = reversals.reduce((sum, e) => sum + Math.abs(e.delta), 0);
|
|
2795
|
+
const screenouts = w.screenouts_today ?? 0;
|
|
2796
|
+
const screenoutCredits = w.screenout_credits_today ?? 0;
|
|
2526
2797
|
push({
|
|
2527
2798
|
kind: "notice",
|
|
2528
|
-
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` + (
|
|
2799
|
+
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 ? `
|
|
2800
|
+
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.` : "") + (reversals.length ? `
|
|
2529
2801
|
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.` : "")
|
|
2530
2802
|
});
|
|
2531
2803
|
});
|
|
@@ -2536,11 +2808,14 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
2536
2808
|
case "earn":
|
|
2537
2809
|
await runAdWall();
|
|
2538
2810
|
return;
|
|
2811
|
+
case "login":
|
|
2812
|
+
await runLogin(arg);
|
|
2813
|
+
return;
|
|
2539
2814
|
default:
|
|
2540
2815
|
push({ kind: "notice", tone: "warn", text: ` unknown command: /${cmd} \u2014 try /help` });
|
|
2541
2816
|
}
|
|
2542
2817
|
},
|
|
2543
|
-
[client, config, cycleMode, exit, model, push, runAdWall, runBusy, runTurn2]
|
|
2818
|
+
[client, config, cycleMode, exit, model, push, runAdWall, runBusy, runLogin, runTurn2]
|
|
2544
2819
|
);
|
|
2545
2820
|
const submit = useCallback(
|
|
2546
2821
|
async (raw) => {
|
|
@@ -2674,62 +2949,95 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
2674
2949
|
const elapsed = busy && startedAt ? Math.floor((Date.now() - startedAt) / 1e3) : 0;
|
|
2675
2950
|
const spinner = SPINNER[tick % SPINNER.length];
|
|
2676
2951
|
const sponsorLine = busy && sponsor ? sponsorText(sponsor, cols) : null;
|
|
2677
|
-
const
|
|
2678
|
-
const
|
|
2679
|
-
|
|
2952
|
+
const viewport = Math.max(6, rows - 1);
|
|
2953
|
+
const inputRows = Math.max(1, Math.min(MAX_INPUT_ROWS, editor.lines.length));
|
|
2954
|
+
const inputFrom = windowStart(editor.row, editor.lines.length, inputRows);
|
|
2955
|
+
const inputBoxHeight = 2 + inputRows;
|
|
2956
|
+
const chromeHeight = inputBoxHeight + 2 + (sponsorLine ? 1 : 0);
|
|
2957
|
+
let budget = Math.max(0, viewport - chromeHeight - menu.length);
|
|
2958
|
+
const PICKER_CHROME = 8;
|
|
2959
|
+
const pickerRows = picker ? Math.max(1, Math.min(MAX_PICKER_ROWS, picker.items.length, budget - PICKER_CHROME)) : 0;
|
|
2960
|
+
const pickerFrom = picker ? windowStart(pickerSel, picker.items.length, pickerRows) : 0;
|
|
2961
|
+
const pickerItems = picker ? picker.items.slice(pickerFrom, pickerFrom + pickerRows) : [];
|
|
2962
|
+
const pickerHeight = picker ? pickerItems.length + PICKER_CHROME : 0;
|
|
2963
|
+
const pickerLabelW = picker ? picker.items.reduce((w, i) => Math.max(w, i.label.length), 0) : 0;
|
|
2964
|
+
budget -= pickerHeight;
|
|
2965
|
+
const ASK_CHROME = 4;
|
|
2966
|
+
const askSummaryRows = ask2 ? lineCount(ask2.req.summary, cols) : 0;
|
|
2967
|
+
const askPreview = ask2?.req.preview ? headRows(ask2.req.preview, cols, Math.max(1, budget - ASK_CHROME - askSummaryRows)) : "";
|
|
2968
|
+
const askHeight = ask2 ? askSummaryRows + lineCount(askPreview, cols) * (askPreview ? 1 : 0) + ASK_CHROME : 0;
|
|
2969
|
+
budget -= askHeight;
|
|
2970
|
+
const busyHeight = busy ? 2 : 0;
|
|
2971
|
+
budget -= busyHeight;
|
|
2972
|
+
const liveRaw = [
|
|
2973
|
+
// Trailing blank lines are rows the clamp would spend on nothing, and a
|
|
2974
|
+
// streamed answer ends on one more often than not.
|
|
2975
|
+
(live?.text ?? "").replace(/\n+$/, ""),
|
|
2680
2976
|
live?.tool ? `\u23FA ${live.tool.name} ${live.tool.summary}` : "",
|
|
2681
2977
|
live?.tool?.output ?? ""
|
|
2682
2978
|
].filter(Boolean).join("\n");
|
|
2683
|
-
const
|
|
2684
|
-
const pickerHeight = picker ? picker.items.length + 8 : 0;
|
|
2685
|
-
const pickerLabelW = picker ? picker.items.reduce((w, i) => Math.max(w, i.label.length), 0) : 0;
|
|
2686
|
-
const inputBoxHeight = 2 + editor.lines.length;
|
|
2687
|
-
const chromeHeight = inputBoxHeight + 2 + (sponsorLine ? 1 : 0);
|
|
2979
|
+
const liveBlock = tailRows(liveRaw, cols, Math.max(0, budget - 1));
|
|
2688
2980
|
const liveHeight = liveBlock ? lineCount(liveBlock, cols) + 1 : 0;
|
|
2689
|
-
const
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2981
|
+
const printed = useMemo(() => {
|
|
2982
|
+
const cache = heightCacheRef.current;
|
|
2983
|
+
return entries.reduce((n, e) => {
|
|
2984
|
+
const key = `${e.id}:${cols}`;
|
|
2985
|
+
let h = cache.get(key);
|
|
2986
|
+
if (h === void 0) {
|
|
2987
|
+
h = entryHeight(e, cols);
|
|
2988
|
+
cache.set(key, h);
|
|
2989
|
+
}
|
|
2990
|
+
return n + h;
|
|
2991
|
+
}, 0);
|
|
2992
|
+
}, [entries, cols]);
|
|
2993
|
+
const used = chromeHeight + menu.length + pickerHeight + askHeight + busyHeight + liveHeight;
|
|
2994
|
+
const spacer = Math.max(0, viewport - printed - used);
|
|
2695
2995
|
const labelW = menu.reduce((w, c2) => Math.max(w, c2.label.length), 0);
|
|
2696
2996
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
2697
2997
|
/* @__PURE__ */ jsx2(Static, { items: entries, children: (entry) => /* @__PURE__ */ jsx2(EntryView, { entry }, entry.id) }),
|
|
2698
2998
|
spacer > 0 ? /* @__PURE__ */ jsx2(Box2, { height: spacer }) : null,
|
|
2699
2999
|
liveBlock ? /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(Text2, { children: liveBlock }) }) : null,
|
|
3000
|
+
busy ? /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, children: [
|
|
3001
|
+
/* @__PURE__ */ jsxs2(Text2, { color: MANGO3, children: [
|
|
3002
|
+
spinner,
|
|
3003
|
+
" "
|
|
3004
|
+
] }),
|
|
3005
|
+
/* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
3006
|
+
busyLabel,
|
|
3007
|
+
" ",
|
|
3008
|
+
elapsed,
|
|
3009
|
+
"s \xB7 esc to stop"
|
|
3010
|
+
] })
|
|
3011
|
+
] }) : null,
|
|
2700
3012
|
ask2 ? /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
2701
3013
|
/* @__PURE__ */ jsx2(Text2, { bold: true, color: "yellow", children: ask2.req.summary }),
|
|
2702
|
-
|
|
3014
|
+
askPreview ? /* @__PURE__ */ jsx2(Text2, { children: askPreview }) : null,
|
|
2703
3015
|
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "[y/\u23CE] once \xB7 [a] always \xB7 [n] no \xB7 esc cancels" })
|
|
2704
3016
|
] }) : null,
|
|
2705
3017
|
picker ? /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: MANGO3, paddingX: 1, children: [
|
|
2706
3018
|
/* @__PURE__ */ jsx2(Text2, { bold: true, color: MANGO_BRIGHT, children: picker.title }),
|
|
2707
3019
|
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: picker.subtitle }),
|
|
2708
3020
|
/* @__PURE__ */ jsx2(Box2, { height: 1 }),
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
3021
|
+
pickerItems.map((item, i) => {
|
|
3022
|
+
const index = pickerFrom + i;
|
|
3023
|
+
return /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
3024
|
+
/* @__PURE__ */ jsxs2(Text2, { color: index === pickerSel ? MANGO_BRIGHT : void 0, bold: index === pickerSel, children: [
|
|
3025
|
+
index === pickerSel ? "\u276F " : " ",
|
|
3026
|
+
`${index + 1}. `,
|
|
3027
|
+
item.label.padEnd(pickerLabelW + 2)
|
|
3028
|
+
] }),
|
|
3029
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: item.hint }),
|
|
3030
|
+
item.current ? /* @__PURE__ */ jsx2(Text2, { color: "green", children: " \u2190 current" }) : null
|
|
3031
|
+
] }, item.value);
|
|
3032
|
+
}),
|
|
2718
3033
|
/* @__PURE__ */ jsx2(Box2, { height: 1 }),
|
|
2719
3034
|
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\u2191\u2193 choose \xB7 1-9 jump straight to a row \xB7 \u23CE confirm \xB7 esc cancel" })
|
|
2720
3035
|
] }) : null,
|
|
2721
3036
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: 1, children: [
|
|
2722
3037
|
sponsorLine ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: sponsorLine }) : null,
|
|
2723
|
-
/* @__PURE__ */ jsxs2(Box2, { borderStyle: "round", borderColor: busy ?
|
|
2724
|
-
/* @__PURE__ */ jsx2(Text2, { color: MANGO_BRIGHT, children: "\u276F " }),
|
|
2725
|
-
busy ? /* @__PURE__ */
|
|
2726
|
-
spinner,
|
|
2727
|
-
" ",
|
|
2728
|
-
busyLabel,
|
|
2729
|
-
" ",
|
|
2730
|
-
elapsed,
|
|
2731
|
-
"s \xB7 esc to stop"
|
|
2732
|
-
] }) : /* @__PURE__ */ jsx2(Text2, { children: renderInput(editor) })
|
|
3038
|
+
/* @__PURE__ */ jsxs2(Box2, { borderStyle: "round", borderColor: busy ? SLATE : MANGO3, paddingX: 1, children: [
|
|
3039
|
+
/* @__PURE__ */ jsx2(Text2, { color: busy ? SLATE : MANGO_BRIGHT, children: "\u276F " }),
|
|
3040
|
+
busy ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: toText(editor) }) : /* @__PURE__ */ jsx2(Text2, { children: renderInput(editor, inputFrom, inputRows) })
|
|
2733
3041
|
] }),
|
|
2734
3042
|
menu.map((item, i) => /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
2735
3043
|
/* @__PURE__ */ jsxs2(Text2, { color: i === sel ? MANGO_BRIGHT : MANGO3, bold: i === sel, children: [
|
|
@@ -2738,22 +3046,25 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
2738
3046
|
] }),
|
|
2739
3047
|
/* @__PURE__ */ jsx2(Text2, { dimColor: i !== sel, children: item.hint })
|
|
2740
3048
|
] }, item.value)),
|
|
2741
|
-
/* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
3049
|
+
quitHint || menu.length > 0 ? /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
2742
3050
|
" ",
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
3051
|
+
quitHint ? "press ctrl+c again to quit" : "\u2191\u2193 choose \xB7 \u23CE run \xB7 tab complete \xB7 esc close"
|
|
3052
|
+
] }) : /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
3053
|
+
/* @__PURE__ */ jsx2(Text2, { color: MODE_STYLE[mode].color, bold: true, children: ` ${MODE_STYLE[mode].glyph} ${MODE_LABEL[mode]}` }),
|
|
3054
|
+
/* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
3055
|
+
" ",
|
|
3056
|
+
statusLine({
|
|
3057
|
+
model,
|
|
3058
|
+
balance,
|
|
3059
|
+
spent,
|
|
3060
|
+
minutes: (Date.now() - runStartedAtRef.current) / 6e4
|
|
3061
|
+
})
|
|
3062
|
+
] })
|
|
2752
3063
|
] })
|
|
2753
3064
|
] })
|
|
2754
3065
|
] });
|
|
2755
3066
|
}
|
|
2756
|
-
function
|
|
3067
|
+
function sleep2(ms, signal) {
|
|
2757
3068
|
return new Promise((resolve2) => {
|
|
2758
3069
|
if (signal.aborted) return resolve2();
|
|
2759
3070
|
const done = () => {
|
|
@@ -2765,9 +3076,10 @@ function sleep(ms, signal) {
|
|
|
2765
3076
|
signal.addEventListener("abort", done, { once: true });
|
|
2766
3077
|
});
|
|
2767
3078
|
}
|
|
2768
|
-
function renderInput(state) {
|
|
2769
|
-
return state.lines.map((line2,
|
|
2770
|
-
const
|
|
3079
|
+
function renderInput(state, from, rows) {
|
|
3080
|
+
return state.lines.slice(from, from + rows).map((line2, i) => {
|
|
3081
|
+
const row = from + i;
|
|
3082
|
+
const prefix = i === 0 ? "" : "\n";
|
|
2771
3083
|
if (row !== state.row) return /* @__PURE__ */ jsx2(Text2, { children: prefix + line2 }, row);
|
|
2772
3084
|
const before = line2.slice(0, state.col);
|
|
2773
3085
|
const at = line2.slice(state.col, state.col + 1) || " ";
|
|
@@ -2780,19 +3092,20 @@ function renderInput(state) {
|
|
|
2780
3092
|
});
|
|
2781
3093
|
}
|
|
2782
3094
|
function statusLine(o) {
|
|
2783
|
-
if (o.quitHint) return "press ctrl+c again to quit";
|
|
2784
|
-
if (o.menu) return "\u2191\u2193 choose \xB7 \u23CE run \xB7 tab complete \xB7 esc close";
|
|
2785
3095
|
const burn = o.spent > 0 && o.minutes >= 1 ? `${Math.round(o.spent / o.minutes).toLocaleString("en-US")} cr/min` : void 0;
|
|
2786
3096
|
const parts = [
|
|
2787
3097
|
o.model,
|
|
2788
3098
|
`${o.balance.toLocaleString("en-US")} cr`,
|
|
2789
|
-
o.spent > 0 ? `\u2212${o.spent.toLocaleString("en-US")}
|
|
3099
|
+
o.spent > 0 ? `\u2212${o.spent.toLocaleString("en-US")}` : void 0,
|
|
2790
3100
|
burn,
|
|
2791
|
-
MODE_LABEL[o.mode],
|
|
2792
3101
|
"/help"
|
|
2793
3102
|
].filter(Boolean);
|
|
2794
3103
|
return parts.join(" \xB7 ");
|
|
2795
3104
|
}
|
|
3105
|
+
function windowStart(sel, total, size) {
|
|
3106
|
+
if (total <= size) return 0;
|
|
3107
|
+
return Math.max(0, Math.min(total - size, sel - Math.floor(size / 2)));
|
|
3108
|
+
}
|
|
2796
3109
|
function toEditorKey(ch, key) {
|
|
2797
3110
|
const name = key.leftArrow ? "left" : key.rightArrow ? "right" : key.upArrow ? "up" : key.downArrow ? "down" : key.backspace ? "backspace" : key.delete ? "delete" : void 0;
|
|
2798
3111
|
return {
|
|
@@ -2825,12 +3138,13 @@ function tailLines(text, max) {
|
|
|
2825
3138
|
const lines = text.split("\n");
|
|
2826
3139
|
return lines.length <= max ? text : lines.slice(-max).join("\n");
|
|
2827
3140
|
}
|
|
2828
|
-
var SPINNER, WORKING, WAITING_FOR_REWARD, LOADING, COMPACTING, LIVE_OUTPUT_LINES, COMMITTED_OUTPUT_LINES;
|
|
3141
|
+
var SPINNER, WORKING, WAITING_FOR_REWARD, LOADING, COMPACTING, SIGNING_IN, NOT_SIGNED_IN, LIVE_OUTPUT_LINES, COMMITTED_OUTPUT_LINES, DELTA_FLUSH_MS, MAX_INPUT_ROWS, MAX_PICKER_ROWS;
|
|
2829
3142
|
var init_app = __esm({
|
|
2830
3143
|
"src/tui/app.tsx"() {
|
|
2831
3144
|
"use strict";
|
|
2832
3145
|
init_client();
|
|
2833
3146
|
init_config();
|
|
3147
|
+
init_login();
|
|
2834
3148
|
init_banner();
|
|
2835
3149
|
init_agent();
|
|
2836
3150
|
init_context();
|
|
@@ -2853,8 +3167,13 @@ var init_app = __esm({
|
|
|
2853
3167
|
WAITING_FOR_REWARD = "waiting for the offer\u2026";
|
|
2854
3168
|
LOADING = "loading\u2026";
|
|
2855
3169
|
COMPACTING = "compacting\u2026";
|
|
3170
|
+
SIGNING_IN = "signing in\u2026";
|
|
3171
|
+
NOT_SIGNED_IN = " Not signed in, or the stored token isn't valid for this gateway.\n Run /login to sign in.";
|
|
2856
3172
|
LIVE_OUTPUT_LINES = 5;
|
|
2857
3173
|
COMMITTED_OUTPUT_LINES = 4;
|
|
3174
|
+
DELTA_FLUSH_MS = 50;
|
|
3175
|
+
MAX_INPUT_ROWS = 10;
|
|
3176
|
+
MAX_PICKER_ROWS = 12;
|
|
2858
3177
|
}
|
|
2859
3178
|
});
|
|
2860
3179
|
|
|
@@ -2880,6 +3199,7 @@ var init_tui = __esm({
|
|
|
2880
3199
|
// src/main.ts
|
|
2881
3200
|
init_client();
|
|
2882
3201
|
init_config();
|
|
3202
|
+
init_login();
|
|
2883
3203
|
init_agent();
|
|
2884
3204
|
init_context();
|
|
2885
3205
|
init_permissions();
|
|
@@ -2980,25 +3300,44 @@ async function main() {
|
|
|
2980
3300
|
process.exitCode = 1;
|
|
2981
3301
|
}
|
|
2982
3302
|
}
|
|
2983
|
-
function takeInvite(args) {
|
|
2984
|
-
const rest = [];
|
|
2985
|
-
let invite;
|
|
2986
|
-
for (let i = 0; i < args.length; i++) {
|
|
2987
|
-
const a = args[i];
|
|
2988
|
-
if (a === "--invite") invite = args[++i];
|
|
2989
|
-
else if (a.startsWith("--invite=")) invite = a.slice("--invite=".length);
|
|
2990
|
-
else rest.push(a);
|
|
2991
|
-
}
|
|
2992
|
-
return { invite: invite?.trim() || void 0, rest };
|
|
2993
|
-
}
|
|
2994
3303
|
async function login(client, config, args) {
|
|
2995
|
-
const
|
|
2996
|
-
const
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3304
|
+
const parsed = parseLoginArgs(args);
|
|
3305
|
+
const outcome = await performLogin(client, config, parsed, {
|
|
3306
|
+
verify(start) {
|
|
3307
|
+
console.log(`
|
|
3308
|
+
Open ${c.cyan(start.verification_uri)} and enter code: ${c.bold(start.user_code)}
|
|
3309
|
+
`);
|
|
3310
|
+
process.stdout.write(c.dim(" waiting for GitHub authorization\u2026 (Ctrl+C to cancel)"));
|
|
3311
|
+
},
|
|
3312
|
+
tick() {
|
|
3313
|
+
process.stdout.write(c.dim("."));
|
|
3314
|
+
}
|
|
3315
|
+
});
|
|
3316
|
+
switch (outcome.status) {
|
|
3317
|
+
case "ok": {
|
|
3318
|
+
const { account } = outcome;
|
|
3319
|
+
const who = account.login ?? account.email ?? "you";
|
|
3320
|
+
const suffix = account.login && account.email ? c.dim(` (${account.email})`) : "";
|
|
3321
|
+
console.log(c.green(`
|
|
3322
|
+
\u2713 logged in as ${who}`) + suffix);
|
|
3323
|
+
const bonus = account.created ? c.dim(account.signupBonus === void 0 ? " (signup bonus)" : ` (signup bonus ${credits(account.signupBonus)})`) : "";
|
|
3324
|
+
console.log(` balance: ${c.bold(credits(account.balance))} credits${bonus}`);
|
|
3325
|
+
console.log(c.dim(` token stored in ${configPath()}`));
|
|
3326
|
+
return;
|
|
3327
|
+
}
|
|
3328
|
+
case "closed":
|
|
3329
|
+
reportSignupClosed(outcome.message, outcome.hadInvite);
|
|
3330
|
+
process.exitCode = 1;
|
|
3331
|
+
return;
|
|
3332
|
+
case "timeout":
|
|
3333
|
+
console.log(c.red("\n login timed out \u2014 run `clixad login` again."));
|
|
3334
|
+
return;
|
|
3335
|
+
case "aborted":
|
|
3336
|
+
return;
|
|
3337
|
+
default:
|
|
3338
|
+
console.log(c.red(`
|
|
3339
|
+
login failed: ${outcome.error}`));
|
|
3000
3340
|
}
|
|
3001
|
-
return devLogin(client, config, email, invite);
|
|
3002
3341
|
}
|
|
3003
3342
|
function reportSignupClosed(message, hadInvite) {
|
|
3004
3343
|
console.log(c.yellow("\n Clixad isn't open yet.\n"));
|
|
@@ -3022,67 +3361,7 @@ function wrapPlain(text, width) {
|
|
|
3022
3361
|
if (line2) lines.push(line2);
|
|
3023
3362
|
return lines;
|
|
3024
3363
|
}
|
|
3025
|
-
|
|
3026
|
-
console.log(`
|
|
3027
|
-
Open ${c.cyan(start.verification_uri)} and enter code: ${c.bold(start.user_code)}
|
|
3028
|
-
`);
|
|
3029
|
-
openBrowser(start.verification_uri);
|
|
3030
|
-
process.stdout.write(c.dim(" waiting for GitHub authorization\u2026 (Ctrl+C to cancel)"));
|
|
3031
|
-
const deadline = Date.now() + start.expires_in * 1e3;
|
|
3032
|
-
let interval = Math.max(start.interval, 1);
|
|
3033
|
-
while (Date.now() < deadline) {
|
|
3034
|
-
await sleep2(interval * 1e3);
|
|
3035
|
-
process.stdout.write(c.dim("."));
|
|
3036
|
-
const poll = await client.devicePoll(start.session, invite);
|
|
3037
|
-
if (poll.status === "pending") {
|
|
3038
|
-
if (poll.interval) interval = poll.interval;
|
|
3039
|
-
continue;
|
|
3040
|
-
}
|
|
3041
|
-
if (poll.status === "closed") {
|
|
3042
|
-
reportSignupClosed(poll.message, Boolean(invite));
|
|
3043
|
-
process.exitCode = 1;
|
|
3044
|
-
return;
|
|
3045
|
-
}
|
|
3046
|
-
if (poll.status === "complete") {
|
|
3047
|
-
config.token = poll.token;
|
|
3048
|
-
config.userId = poll.userId;
|
|
3049
|
-
config.email = poll.email;
|
|
3050
|
-
config.login = poll.login;
|
|
3051
|
-
saveConfig(config);
|
|
3052
|
-
console.log(c.green(`
|
|
3053
|
-
\u2713 logged in as ${poll.login}`) + c.dim(poll.email ? ` (${poll.email})` : ""));
|
|
3054
|
-
const bonus = poll.created ? c.dim(` (signup bonus ${credits(poll.signupBonus)})`) : "";
|
|
3055
|
-
console.log(` balance: ${c.bold(credits(poll.balance))} credits${bonus}`);
|
|
3056
|
-
console.log(c.dim(` token stored in ${configPath()}`));
|
|
3057
|
-
return;
|
|
3058
|
-
}
|
|
3059
|
-
console.log(c.red(`
|
|
3060
|
-
login failed: ${poll.error ?? poll.status}`));
|
|
3061
|
-
return;
|
|
3062
|
-
}
|
|
3063
|
-
console.log(c.red("\n login timed out \u2014 run `clixad login` again."));
|
|
3064
|
-
}
|
|
3065
|
-
async function devLogin(client, config, email, invite) {
|
|
3066
|
-
let res;
|
|
3067
|
-
try {
|
|
3068
|
-
res = await client.signupDev(email, invite);
|
|
3069
|
-
} catch (err) {
|
|
3070
|
-
if (err instanceof SignupClosedError) {
|
|
3071
|
-
reportSignupClosed(err.message, Boolean(invite));
|
|
3072
|
-
process.exitCode = 1;
|
|
3073
|
-
return;
|
|
3074
|
-
}
|
|
3075
|
-
throw err;
|
|
3076
|
-
}
|
|
3077
|
-
config.token = res.token;
|
|
3078
|
-
config.userId = res.userId;
|
|
3079
|
-
config.email = res.email;
|
|
3080
|
-
saveConfig(config);
|
|
3081
|
-
console.log(c.green(`\u2713 logged in as ${res.email}`));
|
|
3082
|
-
console.log(` balance: ${c.bold(credits(res.balance))} credits (signup bonus)`);
|
|
3083
|
-
console.log(c.dim(` token stored in ${configPath()}`));
|
|
3084
|
-
}
|
|
3085
|
-
var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
3364
|
+
var sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
3086
3365
|
var credits = (n) => n.toLocaleString("en-US");
|
|
3087
3366
|
function logout(config) {
|
|
3088
3367
|
delete config.token;
|
|
@@ -3159,7 +3438,7 @@ async function earn(client, config) {
|
|
|
3159
3438
|
process.stdout.write(c.dim(" waiting for an offer to clear"));
|
|
3160
3439
|
const deadline = Date.now() + 5 * 60 * 1e3;
|
|
3161
3440
|
while (Date.now() < deadline) {
|
|
3162
|
-
await
|
|
3441
|
+
await sleep3(3e3);
|
|
3163
3442
|
process.stdout.write(c.dim("."));
|
|
3164
3443
|
const balance = (await safeWallet(client))?.balance ?? before;
|
|
3165
3444
|
if (balance > before) {
|
|
@@ -3203,7 +3482,7 @@ async function buyCmd(client, config, pack) {
|
|
|
3203
3482
|
process.stdout.write(c.dim(" waiting for payment to clear"));
|
|
3204
3483
|
const deadline = Date.now() + 5 * 60 * 1e3;
|
|
3205
3484
|
while (Date.now() < deadline) {
|
|
3206
|
-
await
|
|
3485
|
+
await sleep3(3e3);
|
|
3207
3486
|
process.stdout.write(c.dim("."));
|
|
3208
3487
|
const balance = (await safeWallet(client))?.balance ?? before;
|
|
3209
3488
|
if (balance > before) {
|