clixad 0.0.1-beta.4 → 0.0.1-beta.6
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 +269 -114
- 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}`);
|
|
@@ -1482,21 +1615,6 @@ var init_banner = __esm({
|
|
|
1482
1615
|
}
|
|
1483
1616
|
});
|
|
1484
1617
|
|
|
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
1618
|
// src/version.ts
|
|
1501
1619
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
1502
1620
|
function readVersion() {
|
|
@@ -1713,6 +1831,17 @@ var init_commands = __esm({
|
|
|
1713
1831
|
// forms, surveys and app trials, and describing the top-up as a video costs
|
|
1714
1832
|
// the user the one expectation that makes a screenout make sense.
|
|
1715
1833
|
{ name: "earn", desc: "open the offer wall to earn credits" },
|
|
1834
|
+
// Reachable from inside the REPL because that is where the 401 is printed.
|
|
1835
|
+
// "Run `clixad login` first" is not a runnable instruction at this prompt —
|
|
1836
|
+
// anything without a leading slash is a prompt, so it went to the model and
|
|
1837
|
+
// came back as the same 401.
|
|
1838
|
+
//
|
|
1839
|
+
// Deliberately *no* `args`, though `/login <email>` works: an arg hint makes
|
|
1840
|
+
// enter complete the command instead of running it, and this is the one
|
|
1841
|
+
// command whose whole job is to be the exit from a stuck REPL. The email form
|
|
1842
|
+
// is the dev shortcut, documented on `clixad login [email]`, not the path
|
|
1843
|
+
// somebody in a 401 loop needs.
|
|
1844
|
+
{ name: "login", desc: "sign in, or switch account" },
|
|
1716
1845
|
{ name: "compact", desc: "summarise the conversation to free context" },
|
|
1717
1846
|
{ name: "clear", desc: "clear the conversation context" },
|
|
1718
1847
|
{ name: "init", desc: "write a CLIXAD.md for this project" },
|
|
@@ -2339,7 +2468,7 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
2339
2468
|
that is normal, just start another.`
|
|
2340
2469
|
});
|
|
2341
2470
|
} else if (err instanceof AuthError) {
|
|
2342
|
-
push({ kind: "notice", tone: "error", text:
|
|
2471
|
+
push({ kind: "notice", tone: "error", text: NOT_SIGNED_IN });
|
|
2343
2472
|
} else {
|
|
2344
2473
|
push({ kind: "notice", tone: "error", text: ` error: ${err.message}` });
|
|
2345
2474
|
}
|
|
@@ -2367,6 +2496,7 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
2367
2496
|
await fn(ac.signal);
|
|
2368
2497
|
} catch (err) {
|
|
2369
2498
|
if (ac.signal.aborted) push({ kind: "notice", tone: "warn", text: " (stopped)" });
|
|
2499
|
+
else if (err instanceof AuthError) push({ kind: "notice", tone: "error", text: NOT_SIGNED_IN });
|
|
2370
2500
|
else push({ kind: "notice", tone: "error", text: ` ${err.message}` });
|
|
2371
2501
|
} finally {
|
|
2372
2502
|
busyAbortRef.current = null;
|
|
@@ -2376,6 +2506,56 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
2376
2506
|
},
|
|
2377
2507
|
[push]
|
|
2378
2508
|
);
|
|
2509
|
+
const runLogin = useCallback(
|
|
2510
|
+
async (arg) => {
|
|
2511
|
+
const args = parseLoginArgs(arg.split(/\s+/).filter(Boolean));
|
|
2512
|
+
await runBusy(SIGNING_IN, async (signal) => {
|
|
2513
|
+
const outcome = await performLogin(
|
|
2514
|
+
client,
|
|
2515
|
+
config,
|
|
2516
|
+
args,
|
|
2517
|
+
{
|
|
2518
|
+
verify: (start) => push({
|
|
2519
|
+
kind: "notice",
|
|
2520
|
+
text: ` Open ${start.verification_uri}
|
|
2521
|
+
and enter the code: ${start.user_code}
|
|
2522
|
+
Waiting for GitHub \u2014 esc to cancel.`
|
|
2523
|
+
})
|
|
2524
|
+
},
|
|
2525
|
+
signal
|
|
2526
|
+
);
|
|
2527
|
+
switch (outcome.status) {
|
|
2528
|
+
case "ok": {
|
|
2529
|
+
const { account } = outcome;
|
|
2530
|
+
setBalance(account.balance);
|
|
2531
|
+
push({
|
|
2532
|
+
kind: "notice",
|
|
2533
|
+
tone: "good",
|
|
2534
|
+
text: ` signed in as ${account.login ?? account.email ?? "you"} \xB7 balance ${account.balance.toLocaleString("en-US")} credits` + (account.created ? " (signup bonus)" : "")
|
|
2535
|
+
});
|
|
2536
|
+
return;
|
|
2537
|
+
}
|
|
2538
|
+
case "closed":
|
|
2539
|
+
push({
|
|
2540
|
+
kind: "notice",
|
|
2541
|
+
tone: "warn",
|
|
2542
|
+
text: ` Clixad isn't open yet.
|
|
2543
|
+
${outcome.message}` + (outcome.hadInvite ? "\n That invite code wasn't accepted \u2014 check it for typos." : "")
|
|
2544
|
+
});
|
|
2545
|
+
return;
|
|
2546
|
+
case "timeout":
|
|
2547
|
+
push({ kind: "notice", tone: "warn", text: " login timed out \u2014 /login to try again." });
|
|
2548
|
+
return;
|
|
2549
|
+
case "aborted":
|
|
2550
|
+
push({ kind: "notice", tone: "warn", text: " (stopped) \u2014 /login to try again." });
|
|
2551
|
+
return;
|
|
2552
|
+
default:
|
|
2553
|
+
push({ kind: "notice", tone: "error", text: ` login failed: ${outcome.error}` });
|
|
2554
|
+
}
|
|
2555
|
+
});
|
|
2556
|
+
},
|
|
2557
|
+
[client, config, push, runBusy]
|
|
2558
|
+
);
|
|
2379
2559
|
const runAdWall = useCallback(async () => {
|
|
2380
2560
|
const task = pendingTaskRef.current;
|
|
2381
2561
|
pendingTaskRef.current = null;
|
|
@@ -2394,7 +2574,7 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
2394
2574
|
await runBusy(WAITING_FOR_REWARD, async (signal) => {
|
|
2395
2575
|
const deadline = Date.now() + 5 * 6e4;
|
|
2396
2576
|
while (Date.now() < deadline && !credited && !signal.aborted) {
|
|
2397
|
-
await
|
|
2577
|
+
await sleep2(3e3, signal);
|
|
2398
2578
|
if (signal.aborted) break;
|
|
2399
2579
|
const w = await client.wallet(signal).catch(() => void 0);
|
|
2400
2580
|
if (w && w.balance > before) {
|
|
@@ -2523,9 +2703,12 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
2523
2703
|
setBalance(w.balance);
|
|
2524
2704
|
const reversals = w.ledger.filter((e) => e.reason === "ad_reversal");
|
|
2525
2705
|
const reversed = reversals.reduce((sum, e) => sum + Math.abs(e.delta), 0);
|
|
2706
|
+
const screenouts = w.screenouts_today ?? 0;
|
|
2707
|
+
const screenoutCredits = w.screenout_credits_today ?? 0;
|
|
2526
2708
|
push({
|
|
2527
2709
|
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` + (
|
|
2710
|
+
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 ? `
|
|
2711
|
+
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
2712
|
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
2713
|
});
|
|
2531
2714
|
});
|
|
@@ -2536,11 +2719,14 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
2536
2719
|
case "earn":
|
|
2537
2720
|
await runAdWall();
|
|
2538
2721
|
return;
|
|
2722
|
+
case "login":
|
|
2723
|
+
await runLogin(arg);
|
|
2724
|
+
return;
|
|
2539
2725
|
default:
|
|
2540
2726
|
push({ kind: "notice", tone: "warn", text: ` unknown command: /${cmd} \u2014 try /help` });
|
|
2541
2727
|
}
|
|
2542
2728
|
},
|
|
2543
|
-
[client, config, cycleMode, exit, model, push, runAdWall, runBusy, runTurn2]
|
|
2729
|
+
[client, config, cycleMode, exit, model, push, runAdWall, runBusy, runLogin, runTurn2]
|
|
2544
2730
|
);
|
|
2545
2731
|
const submit = useCallback(
|
|
2546
2732
|
async (raw) => {
|
|
@@ -2753,7 +2939,7 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
2753
2939
|
] })
|
|
2754
2940
|
] });
|
|
2755
2941
|
}
|
|
2756
|
-
function
|
|
2942
|
+
function sleep2(ms, signal) {
|
|
2757
2943
|
return new Promise((resolve2) => {
|
|
2758
2944
|
if (signal.aborted) return resolve2();
|
|
2759
2945
|
const done = () => {
|
|
@@ -2825,12 +3011,13 @@ function tailLines(text, max) {
|
|
|
2825
3011
|
const lines = text.split("\n");
|
|
2826
3012
|
return lines.length <= max ? text : lines.slice(-max).join("\n");
|
|
2827
3013
|
}
|
|
2828
|
-
var SPINNER, WORKING, WAITING_FOR_REWARD, LOADING, COMPACTING, LIVE_OUTPUT_LINES, COMMITTED_OUTPUT_LINES;
|
|
3014
|
+
var SPINNER, WORKING, WAITING_FOR_REWARD, LOADING, COMPACTING, SIGNING_IN, NOT_SIGNED_IN, LIVE_OUTPUT_LINES, COMMITTED_OUTPUT_LINES;
|
|
2829
3015
|
var init_app = __esm({
|
|
2830
3016
|
"src/tui/app.tsx"() {
|
|
2831
3017
|
"use strict";
|
|
2832
3018
|
init_client();
|
|
2833
3019
|
init_config();
|
|
3020
|
+
init_login();
|
|
2834
3021
|
init_banner();
|
|
2835
3022
|
init_agent();
|
|
2836
3023
|
init_context();
|
|
@@ -2853,6 +3040,8 @@ var init_app = __esm({
|
|
|
2853
3040
|
WAITING_FOR_REWARD = "waiting for the offer\u2026";
|
|
2854
3041
|
LOADING = "loading\u2026";
|
|
2855
3042
|
COMPACTING = "compacting\u2026";
|
|
3043
|
+
SIGNING_IN = "signing in\u2026";
|
|
3044
|
+
NOT_SIGNED_IN = " Not signed in, or the stored token isn't valid for this gateway.\n Run /login to sign in.";
|
|
2856
3045
|
LIVE_OUTPUT_LINES = 5;
|
|
2857
3046
|
COMMITTED_OUTPUT_LINES = 4;
|
|
2858
3047
|
}
|
|
@@ -2880,6 +3069,7 @@ var init_tui = __esm({
|
|
|
2880
3069
|
// src/main.ts
|
|
2881
3070
|
init_client();
|
|
2882
3071
|
init_config();
|
|
3072
|
+
init_login();
|
|
2883
3073
|
init_agent();
|
|
2884
3074
|
init_context();
|
|
2885
3075
|
init_permissions();
|
|
@@ -2980,25 +3170,44 @@ async function main() {
|
|
|
2980
3170
|
process.exitCode = 1;
|
|
2981
3171
|
}
|
|
2982
3172
|
}
|
|
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
3173
|
async function login(client, config, args) {
|
|
2995
|
-
const
|
|
2996
|
-
const
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3174
|
+
const parsed = parseLoginArgs(args);
|
|
3175
|
+
const outcome = await performLogin(client, config, parsed, {
|
|
3176
|
+
verify(start) {
|
|
3177
|
+
console.log(`
|
|
3178
|
+
Open ${c.cyan(start.verification_uri)} and enter code: ${c.bold(start.user_code)}
|
|
3179
|
+
`);
|
|
3180
|
+
process.stdout.write(c.dim(" waiting for GitHub authorization\u2026 (Ctrl+C to cancel)"));
|
|
3181
|
+
},
|
|
3182
|
+
tick() {
|
|
3183
|
+
process.stdout.write(c.dim("."));
|
|
3184
|
+
}
|
|
3185
|
+
});
|
|
3186
|
+
switch (outcome.status) {
|
|
3187
|
+
case "ok": {
|
|
3188
|
+
const { account } = outcome;
|
|
3189
|
+
const who = account.login ?? account.email ?? "you";
|
|
3190
|
+
const suffix = account.login && account.email ? c.dim(` (${account.email})`) : "";
|
|
3191
|
+
console.log(c.green(`
|
|
3192
|
+
\u2713 logged in as ${who}`) + suffix);
|
|
3193
|
+
const bonus = account.created ? c.dim(account.signupBonus === void 0 ? " (signup bonus)" : ` (signup bonus ${credits(account.signupBonus)})`) : "";
|
|
3194
|
+
console.log(` balance: ${c.bold(credits(account.balance))} credits${bonus}`);
|
|
3195
|
+
console.log(c.dim(` token stored in ${configPath()}`));
|
|
3196
|
+
return;
|
|
3197
|
+
}
|
|
3198
|
+
case "closed":
|
|
3199
|
+
reportSignupClosed(outcome.message, outcome.hadInvite);
|
|
3200
|
+
process.exitCode = 1;
|
|
3201
|
+
return;
|
|
3202
|
+
case "timeout":
|
|
3203
|
+
console.log(c.red("\n login timed out \u2014 run `clixad login` again."));
|
|
3204
|
+
return;
|
|
3205
|
+
case "aborted":
|
|
3206
|
+
return;
|
|
3207
|
+
default:
|
|
3208
|
+
console.log(c.red(`
|
|
3209
|
+
login failed: ${outcome.error}`));
|
|
3000
3210
|
}
|
|
3001
|
-
return devLogin(client, config, email, invite);
|
|
3002
3211
|
}
|
|
3003
3212
|
function reportSignupClosed(message, hadInvite) {
|
|
3004
3213
|
console.log(c.yellow("\n Clixad isn't open yet.\n"));
|
|
@@ -3022,67 +3231,8 @@ function wrapPlain(text, width) {
|
|
|
3022
3231
|
if (line2) lines.push(line2);
|
|
3023
3232
|
return lines;
|
|
3024
3233
|
}
|
|
3025
|
-
|
|
3026
|
-
|
|
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 ${poll.signupBonus})`) : "";
|
|
3055
|
-
console.log(` balance: ${c.bold(String(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(String(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));
|
|
3234
|
+
var sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
3235
|
+
var credits = (n) => n.toLocaleString("en-US");
|
|
3086
3236
|
function logout(config) {
|
|
3087
3237
|
delete config.token;
|
|
3088
3238
|
delete config.userId;
|
|
@@ -3094,7 +3244,7 @@ async function whoami(client, config) {
|
|
|
3094
3244
|
if (!config.token) return console.log(c.yellow("not logged in \u2014 run `clixad login`"));
|
|
3095
3245
|
const w = await client.wallet();
|
|
3096
3246
|
console.log(`${c.bold(config.email ?? "unknown")} \xB7 model ${c.cyan(config.model)}`);
|
|
3097
|
-
console.log(`balance: ${c.bold(
|
|
3247
|
+
console.log(`balance: ${c.bold(credits(w.balance))} credits \xB7 ${earnedToday(w)}`);
|
|
3098
3248
|
}
|
|
3099
3249
|
function earnedToday(w) {
|
|
3100
3250
|
const offers = `${w.ads_today} offer${w.ads_today === 1 ? "" : "s"}`;
|
|
@@ -3158,7 +3308,7 @@ async function earn(client, config) {
|
|
|
3158
3308
|
process.stdout.write(c.dim(" waiting for an offer to clear"));
|
|
3159
3309
|
const deadline = Date.now() + 5 * 60 * 1e3;
|
|
3160
3310
|
while (Date.now() < deadline) {
|
|
3161
|
-
await
|
|
3311
|
+
await sleep3(3e3);
|
|
3162
3312
|
process.stdout.write(c.dim("."));
|
|
3163
3313
|
const balance = (await safeWallet(client))?.balance ?? before;
|
|
3164
3314
|
if (balance > before) {
|
|
@@ -3202,12 +3352,12 @@ async function buyCmd(client, config, pack) {
|
|
|
3202
3352
|
process.stdout.write(c.dim(" waiting for payment to clear"));
|
|
3203
3353
|
const deadline = Date.now() + 5 * 60 * 1e3;
|
|
3204
3354
|
while (Date.now() < deadline) {
|
|
3205
|
-
await
|
|
3355
|
+
await sleep3(3e3);
|
|
3206
3356
|
process.stdout.write(c.dim("."));
|
|
3207
3357
|
const balance = (await safeWallet(client))?.balance ?? before;
|
|
3208
3358
|
if (balance > before) {
|
|
3209
3359
|
console.log(c.green(`
|
|
3210
|
-
\u2713 +${(balance - before)
|
|
3360
|
+
\u2713 +${credits(balance - before)} credits`) + ` \xB7 balance ${c.bold(credits(balance))}`);
|
|
3211
3361
|
return;
|
|
3212
3362
|
}
|
|
3213
3363
|
}
|
|
@@ -3291,10 +3441,6 @@ async function codeCmd(client, config, rest) {
|
|
|
3291
3441
|
if (code !== 0) process.exitCode = code;
|
|
3292
3442
|
}
|
|
3293
3443
|
async function repl(client, config, opts = {}) {
|
|
3294
|
-
if (!config.token) {
|
|
3295
|
-
console.log(c.yellow("Not logged in. Run `clixad login` first.\n"));
|
|
3296
|
-
return;
|
|
3297
|
-
}
|
|
3298
3444
|
if (opts.resume === "pick") {
|
|
3299
3445
|
const sessions = listSessions(void 0, 10);
|
|
3300
3446
|
if (!sessions.length) return console.log(c.yellow("no saved sessions yet"));
|
|
@@ -3306,9 +3452,18 @@ async function repl(client, config, opts = {}) {
|
|
|
3306
3452
|
return;
|
|
3307
3453
|
}
|
|
3308
3454
|
if (!process.stdin.isTTY) {
|
|
3455
|
+
if (!config.token) {
|
|
3456
|
+
console.log(c.yellow("Not logged in, and interactive mode needs a terminal."));
|
|
3457
|
+
console.log(c.dim(' Run `clixad login` in a terminal, then `clixad -p "..."` for scripted runs.'));
|
|
3458
|
+
return;
|
|
3459
|
+
}
|
|
3309
3460
|
console.log(c.yellow('Interactive mode needs a terminal. Use `clixad -p "..."` for scripted runs.'));
|
|
3310
3461
|
return;
|
|
3311
3462
|
}
|
|
3463
|
+
if (!config.token) {
|
|
3464
|
+
await login(client, config, []);
|
|
3465
|
+
if (!config.token) return;
|
|
3466
|
+
}
|
|
3312
3467
|
let session;
|
|
3313
3468
|
if (opts.resume === "latest") {
|
|
3314
3469
|
session = latestSession(process.cwd());
|
|
@@ -3372,7 +3527,7 @@ function printHelp() {
|
|
|
3372
3527
|
${c.cyan("--continue")} resume the last session in this directory
|
|
3373
3528
|
${c.cyan("--resume")} [id] list saved sessions, or resume one
|
|
3374
3529
|
${c.cyan("--version")} print the version and exit
|
|
3375
|
-
(no command) interactive coding REPL`);
|
|
3530
|
+
(no command) interactive coding REPL ${c.dim("(signs you in if needed)")}`);
|
|
3376
3531
|
}
|
|
3377
3532
|
main().catch((err) => {
|
|
3378
3533
|
console.error(c.red(err.message));
|