clixad 0.0.1-beta.11 → 0.0.1-beta.13
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 +558 -181
- package/package.json +6 -2
package/dist/clixad.mjs
CHANGED
|
@@ -197,6 +197,7 @@ var init_client = __esm({
|
|
|
197
197
|
signup_bonus: "signup bonus",
|
|
198
198
|
ad_reward: "offer reward",
|
|
199
199
|
ad_screenout_bonus: "screenout bonus (didn't qualify)",
|
|
200
|
+
ad_sponsor_impression: "sponsored line shown",
|
|
200
201
|
ad_reversal: "offer reward reversed by the provider",
|
|
201
202
|
purchase: "credits purchased",
|
|
202
203
|
usage: "model usage",
|
|
@@ -369,6 +370,92 @@ var init_client = __esm({
|
|
|
369
370
|
if (!res.ok) throw new Error(`wallet failed: ${res.status}`);
|
|
370
371
|
return res.json();
|
|
371
372
|
}
|
|
373
|
+
/**
|
|
374
|
+
* Ask the gateway for a sponsored line, or `null` if it has none.
|
|
375
|
+
*
|
|
376
|
+
* **This never throws and never reports a failure to anybody.** Every caller is
|
|
377
|
+
* painting a row of dim chrome that already has a house line in it, so there is
|
|
378
|
+
* no state a user could act on and nothing a message would improve — a 404 from
|
|
379
|
+
* a gateway older than the route, a 401, a cold start, a dropped connection and
|
|
380
|
+
* an empty inventory are all the same event here, which is "keep showing ours".
|
|
381
|
+
* That is the opposite of every other method on this client, and it is why the
|
|
382
|
+
* catch is this broad rather than a list of statuses.
|
|
383
|
+
*
|
|
384
|
+
* The serve itself happens on the gateway, deliberately: an impression is worth
|
|
385
|
+
* credits, and this program is installed by strangers from npm. All the CLI
|
|
386
|
+
* gets to decide is when to ask.
|
|
387
|
+
*/
|
|
388
|
+
/**
|
|
389
|
+
* The serve parameters for this session, or `null` for "no ad network".
|
|
390
|
+
*
|
|
391
|
+
* Fetched once per session rather than per model call: the publisher id and
|
|
392
|
+
* the subject are stable for the life of an account, and the SDK's slot holds
|
|
393
|
+
* its own dwell cache. This route used to be `GET /v1/sponsor/line`, and used
|
|
394
|
+
* to be asked before every single call.
|
|
395
|
+
*
|
|
396
|
+
* Never throws, for the reason `sponsorLine` never threw: the caller starts it
|
|
397
|
+
* beside a turn that is already running, and a status line must not be able to
|
|
398
|
+
* take a session with it. A 404 (an older gateway that still serves lines
|
|
399
|
+
* itself) and a 401 both land on `null`, which means the house line — the
|
|
400
|
+
* correct outcome in both cases.
|
|
401
|
+
*/
|
|
402
|
+
async sponsorConfig(signal) {
|
|
403
|
+
try {
|
|
404
|
+
const res = await fetch(`${this.config.gatewayUrl}/v1/sponsor/config`, {
|
|
405
|
+
headers: this.headers(),
|
|
406
|
+
...signal ? { signal } : {}
|
|
407
|
+
});
|
|
408
|
+
if (!res.ok) return null;
|
|
409
|
+
const body = await res.json();
|
|
410
|
+
const publisherId = typeof body.publisher_id === "string" ? body.publisher_id.trim() : "";
|
|
411
|
+
if (!publisherId) return null;
|
|
412
|
+
return {
|
|
413
|
+
publisherId,
|
|
414
|
+
...typeof body.category === "string" ? { category: body.category } : {},
|
|
415
|
+
...typeof body.subject === "string" ? { subject: body.subject } : {},
|
|
416
|
+
...typeof body.dwell_ms === "number" ? { dwellMs: body.dwell_ms } : {}
|
|
417
|
+
};
|
|
418
|
+
} catch {
|
|
419
|
+
return null;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Report one impression, and answer what it was credited.
|
|
424
|
+
*
|
|
425
|
+
* Fire-and-forget from the caller's point of view — the number comes back for
|
|
426
|
+
* the session tally and for tests, and nothing on screen depends on it. Like
|
|
427
|
+
* `sponsorConfig` it never throws: this is called from a `void`, and an
|
|
428
|
+
* unhandled rejection there is a Node process exiting over a line of chrome
|
|
429
|
+
* nobody asked for.
|
|
430
|
+
*
|
|
431
|
+
* **Call this after the model call the line was shown during has finished.**
|
|
432
|
+
* The gateway credits an impression only against a *settled* model call, and a
|
|
433
|
+
* report that arrives while the call is still in flight is refused every time
|
|
434
|
+
* — silently, since a refused grant is a 200 with `credited: 0`. That is the
|
|
435
|
+
* shape this shipped in once. `reportSponsor` in `app.tsx` is the caller, and
|
|
436
|
+
* it fires from `runTurn`'s `finally` for exactly this reason.
|
|
437
|
+
*/
|
|
438
|
+
async reportImpression(report, signal) {
|
|
439
|
+
try {
|
|
440
|
+
const res = await fetch(`${this.config.gatewayUrl}/v1/sponsor/impression`, {
|
|
441
|
+
method: "POST",
|
|
442
|
+
headers: this.headers(),
|
|
443
|
+
...signal ? { signal } : {},
|
|
444
|
+
body: JSON.stringify({
|
|
445
|
+
impression_id: report.impressionId,
|
|
446
|
+
...report.adId ? { ad_id: report.adId } : {},
|
|
447
|
+
billable: report.billable,
|
|
448
|
+
from_cache: report.fromCache,
|
|
449
|
+
credit: report.credit
|
|
450
|
+
})
|
|
451
|
+
});
|
|
452
|
+
if (!res.ok) return 0;
|
|
453
|
+
const body = await res.json();
|
|
454
|
+
return typeof body.credited === "number" ? body.credited : 0;
|
|
455
|
+
} catch {
|
|
456
|
+
return 0;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
372
459
|
/**
|
|
373
460
|
* Search the web, through the gateway.
|
|
374
461
|
*
|
|
@@ -3626,6 +3713,8 @@ If it's installed under a different name/path, set CLIXAD_KIMI_BIN.`;
|
|
|
3626
3713
|
|
|
3627
3714
|
// src/banner.ts
|
|
3628
3715
|
import { homedir as homedir6 } from "node:os";
|
|
3716
|
+
import sliceAnsi from "slice-ansi";
|
|
3717
|
+
import stringWidth from "string-width";
|
|
3629
3718
|
function duckLines() {
|
|
3630
3719
|
const lines = [];
|
|
3631
3720
|
for (let row = 0; row < DUCK.length; row += 2) {
|
|
@@ -3709,22 +3798,29 @@ function welcomeBanner(opts) {
|
|
|
3709
3798
|
return out.join("\n");
|
|
3710
3799
|
}
|
|
3711
3800
|
function compactBanner(opts) {
|
|
3712
|
-
const
|
|
3801
|
+
const width = Math.max(1, opts.width ?? MIN_BANNER_WIDTH);
|
|
3802
|
+
const welcome = truncate(opts.name ? `Welcome back, ${opts.name}!` : "Welcome to Clixad!", Math.max(1, width - 2));
|
|
3713
3803
|
const hint = bannerTip(opts.tipIndex);
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
${MANGO}\u25C6${R2} ${BOLD}${TEXT}${hint.key}${R2} ${FAINT}${
|
|
3804
|
+
const balance = `${opts.balance.toLocaleString("en-US")} credits`;
|
|
3805
|
+
const room = width - (4 + stringWidth(hint.key) + 1 + 4 + stringWidth(balance));
|
|
3806
|
+
const desc = room >= 3 ? truncate(hint.desc, room) : "";
|
|
3807
|
+
const tipRow = desc ? ` ${MANGO}\u25C6${R2} ${BOLD}${TEXT}${hint.key}${R2} ${FAINT}${desc}${R2} ${FAINT}\xB7${R2} ${balance}` : truncate(` ${MANGO}\u25C6${R2} ${BOLD}${TEXT}${hint.key}${R2} ${FAINT}\xB7${R2} ${balance}`, width);
|
|
3808
|
+
const duck = width >= DUCK_W + 2 ? duckLines().map((d) => " " + d).join("\n") + "\n\n" : "";
|
|
3809
|
+
return `${duck} ${BOLD}${TEXT}${welcome}${R2}
|
|
3810
|
+
${tipRow}
|
|
3718
3811
|
`;
|
|
3719
3812
|
}
|
|
3720
|
-
function clearScreen() {
|
|
3721
|
-
|
|
3722
|
-
if (!out.isTTY && !process.stdin.isTTY) return;
|
|
3813
|
+
function clearScreen(out = process.stdout) {
|
|
3814
|
+
if (!out.isTTY) return;
|
|
3723
3815
|
out.write("\n".repeat(out.rows ?? 40));
|
|
3724
3816
|
out.write("\x1B[2J\x1B[3J\x1B[H\x1B[0f");
|
|
3725
3817
|
}
|
|
3726
3818
|
function visibleLength(s) {
|
|
3727
|
-
return s
|
|
3819
|
+
return stringWidth(s);
|
|
3820
|
+
}
|
|
3821
|
+
function sliceColumns(s, columns) {
|
|
3822
|
+
if (columns <= 0) return "";
|
|
3823
|
+
return stringWidth(s) <= columns ? s : sliceAnsi(s, 0, columns);
|
|
3728
3824
|
}
|
|
3729
3825
|
var R2, BOLD, fg, bg, MANGO, TEXT, MUTED, FAINT, PAL, DUCK, DUCK_W, cell, padTo, truncate, BANNER_TIPS, DUCK_FRAMES, BRAILLE_FRAMES, SPINNER_TICKS_PER_FRAME, MIN_BANNER_WIDTH, RIGHT_MAX;
|
|
3730
3826
|
var init_banner = __esm({
|
|
@@ -3767,7 +3863,7 @@ var init_banner = __esm({
|
|
|
3767
3863
|
DUCK_W = 14;
|
|
3768
3864
|
cell = (text, w) => ({ text, w });
|
|
3769
3865
|
padTo = (c2, width) => (c2?.text ?? "") + " ".repeat(Math.max(0, width - (c2?.w ?? 0)));
|
|
3770
|
-
truncate = (s, max) => s
|
|
3866
|
+
truncate = (s, max) => stringWidth(s) > max ? sliceColumns(s, Math.max(0, max - 1)) + "\u2026" : s;
|
|
3771
3867
|
BANNER_TIPS = [
|
|
3772
3868
|
{ key: "/init", desc: "make a CLIXAD.md" },
|
|
3773
3869
|
{ key: "shift+tab", desc: "switch mode" },
|
|
@@ -3789,6 +3885,45 @@ var init_banner = __esm({
|
|
|
3789
3885
|
}
|
|
3790
3886
|
});
|
|
3791
3887
|
|
|
3888
|
+
// src/color.ts
|
|
3889
|
+
function colorEnabled({ isTTY, env }) {
|
|
3890
|
+
const forced = env.FORCE_COLOR?.trim().toLowerCase();
|
|
3891
|
+
if (forced === "0" || forced === "false") return false;
|
|
3892
|
+
if (forced !== void 0 && forced !== "") return true;
|
|
3893
|
+
if (env.NO_COLOR !== void 0 && env.NO_COLOR !== "") return false;
|
|
3894
|
+
if (!isTTY) return false;
|
|
3895
|
+
if (env.TERM === "dumb") return false;
|
|
3896
|
+
return true;
|
|
3897
|
+
}
|
|
3898
|
+
function palette(input) {
|
|
3899
|
+
const on = colorEnabled(input);
|
|
3900
|
+
const paint = (code) => on ? (s) => `\x1B[${code}m${s}\x1B[0m` : PLAIN;
|
|
3901
|
+
return {
|
|
3902
|
+
cyan: paint(CODES.cyan),
|
|
3903
|
+
green: paint(CODES.green),
|
|
3904
|
+
yellow: paint(CODES.yellow),
|
|
3905
|
+
red: paint(CODES.red),
|
|
3906
|
+
dim: paint(CODES.dim),
|
|
3907
|
+
bold: paint(CODES.bold),
|
|
3908
|
+
enabled: on
|
|
3909
|
+
};
|
|
3910
|
+
}
|
|
3911
|
+
var CODES, PLAIN;
|
|
3912
|
+
var init_color = __esm({
|
|
3913
|
+
"src/color.ts"() {
|
|
3914
|
+
"use strict";
|
|
3915
|
+
CODES = {
|
|
3916
|
+
cyan: "36",
|
|
3917
|
+
green: "32",
|
|
3918
|
+
yellow: "33",
|
|
3919
|
+
red: "31",
|
|
3920
|
+
dim: "2",
|
|
3921
|
+
bold: "1"
|
|
3922
|
+
};
|
|
3923
|
+
PLAIN = (s) => s;
|
|
3924
|
+
}
|
|
3925
|
+
});
|
|
3926
|
+
|
|
3792
3927
|
// src/version.ts
|
|
3793
3928
|
import { readFileSync as readFileSync6 } from "node:fs";
|
|
3794
3929
|
function readVersion() {
|
|
@@ -4002,6 +4137,23 @@ function sponsorSource(provider = "house") {
|
|
|
4002
4137
|
return houseSource();
|
|
4003
4138
|
}
|
|
4004
4139
|
}
|
|
4140
|
+
function adtentionSource(fetchLine) {
|
|
4141
|
+
return {
|
|
4142
|
+
provider: "adtention",
|
|
4143
|
+
async fetch(signal) {
|
|
4144
|
+
let line2;
|
|
4145
|
+
try {
|
|
4146
|
+
line2 = await fetchLine(signal);
|
|
4147
|
+
} catch {
|
|
4148
|
+
return null;
|
|
4149
|
+
}
|
|
4150
|
+
if (!line2) return null;
|
|
4151
|
+
const text = line2.text.trim();
|
|
4152
|
+
if (!text) return null;
|
|
4153
|
+
return { ...line2, text };
|
|
4154
|
+
}
|
|
4155
|
+
};
|
|
4156
|
+
}
|
|
4005
4157
|
var init_provider = __esm({
|
|
4006
4158
|
"src/sponsor/provider.ts"() {
|
|
4007
4159
|
"use strict";
|
|
@@ -4022,19 +4174,98 @@ function sponsorEnabled({ isTTY, env, configEnabled }) {
|
|
|
4022
4174
|
if (env.CI !== void 0 && env.CI !== "") return false;
|
|
4023
4175
|
return true;
|
|
4024
4176
|
}
|
|
4025
|
-
function
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
const
|
|
4031
|
-
|
|
4177
|
+
function adFitsWidth(cols) {
|
|
4178
|
+
return cols >= AD_MIN_COLS;
|
|
4179
|
+
}
|
|
4180
|
+
function sponsorRow(line2, cols, origin) {
|
|
4181
|
+
const marker = origin === "house" || cols >= AD_FULL_PREFIX_MIN_COLS ? HOUSE_MARKER_ROW : AD_MARKER_ROW;
|
|
4182
|
+
const label = origin === "network" ? LABEL_ROW : null;
|
|
4183
|
+
if (origin === "network" && !adFitsWidth(cols)) return null;
|
|
4184
|
+
const room = cols - visibleLength(marker) - (label ? visibleLength(label) : 0);
|
|
4185
|
+
if (room < MIN_BODY_COLS) return null;
|
|
4186
|
+
const body = visibleLength(line2) <= room ? line2 : `${sliceColumns(line2, room - 1).trimEnd()}\u2026`;
|
|
4187
|
+
return { marker, label, body, origin };
|
|
4188
|
+
}
|
|
4189
|
+
function adColorEnabled({ isTTY, env }) {
|
|
4190
|
+
if (!colorEnabled({ isTTY, env })) return false;
|
|
4191
|
+
if (env.CI !== void 0 && env.CI !== "") return false;
|
|
4192
|
+
return true;
|
|
4032
4193
|
}
|
|
4033
|
-
var SPONSOR_MARKER;
|
|
4194
|
+
var SPONSOR_SIGIL, SPONSOR_MARKER, AD_LABEL, INDENT, HOUSE_MARKER_ROW, AD_MARKER_ROW, LABEL_ROW, MIN_BODY_COLS, SHORT_AD_COLS, AD_MIN_COLS, AD_FULL_PREFIX_MIN_COLS;
|
|
4034
4195
|
var init_render = __esm({
|
|
4035
4196
|
"src/sponsor/render.ts"() {
|
|
4036
4197
|
"use strict";
|
|
4037
|
-
|
|
4198
|
+
init_banner();
|
|
4199
|
+
init_color();
|
|
4200
|
+
SPONSOR_SIGIL = "\u2726";
|
|
4201
|
+
SPONSOR_MARKER = `${SPONSOR_SIGIL} clixad \xB7`;
|
|
4202
|
+
AD_LABEL = "Ad";
|
|
4203
|
+
INDENT = " ";
|
|
4204
|
+
HOUSE_MARKER_ROW = `${INDENT}${SPONSOR_MARKER} `;
|
|
4205
|
+
AD_MARKER_ROW = `${INDENT}${SPONSOR_SIGIL} `;
|
|
4206
|
+
LABEL_ROW = `${AD_LABEL} \xB7 `;
|
|
4207
|
+
MIN_BODY_COLS = 12;
|
|
4208
|
+
SHORT_AD_COLS = 29;
|
|
4209
|
+
AD_MIN_COLS = visibleLength(AD_MARKER_ROW) + visibleLength(LABEL_ROW) + SHORT_AD_COLS;
|
|
4210
|
+
AD_FULL_PREFIX_MIN_COLS = visibleLength(HOUSE_MARKER_ROW) + visibleLength(LABEL_ROW) + SHORT_AD_COLS;
|
|
4211
|
+
}
|
|
4212
|
+
});
|
|
4213
|
+
|
|
4214
|
+
// src/sponsor/network.ts
|
|
4215
|
+
import { isCategory, SponsorSlot } from "@adtention/sdk";
|
|
4216
|
+
function sponsorLineText(ad) {
|
|
4217
|
+
const text = typeof ad.text === "string" ? ad.text.trim() : "";
|
|
4218
|
+
if (!text) return void 0;
|
|
4219
|
+
return text.length > MAX_TEXT_CHARS3 ? text.slice(0, MAX_TEXT_CHARS3).trimEnd() : text;
|
|
4220
|
+
}
|
|
4221
|
+
function createSponsorSlot(config) {
|
|
4222
|
+
const slot = new SponsorSlot({
|
|
4223
|
+
publisherId: config.publisherId,
|
|
4224
|
+
serveOnly: true,
|
|
4225
|
+
// `isCategory` is the SDK's own guard, and it is checked here even though
|
|
4226
|
+
// the gateway already validated `ADTENTION_CATEGORY` at boot — because the
|
|
4227
|
+
// interesting direction is the other one: an older CLI against a newer
|
|
4228
|
+
// gateway that has learned a seventh category. Passing an unknown value
|
|
4229
|
+
// through would be rejected per serve and look exactly like empty
|
|
4230
|
+
// inventory; dropping it falls back to `general`, which the SDK documents
|
|
4231
|
+
// as always filling from broad inventory. A worse-targeted line beats none.
|
|
4232
|
+
...isCategory(config.category) ? { category: config.category } : {},
|
|
4233
|
+
...config.dwellMs && config.dwellMs > 0 ? { dwellMs: config.dwellMs } : {},
|
|
4234
|
+
timeoutMs: 2500,
|
|
4235
|
+
// Deliberately silent. A failed serve is not something the person at the
|
|
4236
|
+
// keyboard can act on, and this program's output is a coding session — an ad
|
|
4237
|
+
// network's transport error printed into it would be the single most
|
|
4238
|
+
// annoying line in the product.
|
|
4239
|
+
onError: () => {
|
|
4240
|
+
}
|
|
4241
|
+
});
|
|
4242
|
+
return {
|
|
4243
|
+
async serve(cols, signal) {
|
|
4244
|
+
if (!adFitsWidth(cols)) return null;
|
|
4245
|
+
const ad = await slot.next({
|
|
4246
|
+
...config.subject ? { subject: config.subject } : {},
|
|
4247
|
+
...isCategory(config.category) ? { category: config.category } : {}
|
|
4248
|
+
});
|
|
4249
|
+
if (signal?.aborted || !ad) return null;
|
|
4250
|
+
const text = sponsorLineText(ad);
|
|
4251
|
+
if (!text) return null;
|
|
4252
|
+
return {
|
|
4253
|
+
id: ad.adId,
|
|
4254
|
+
text,
|
|
4255
|
+
impressionId: ad.impressionId,
|
|
4256
|
+
billable: ad.billable,
|
|
4257
|
+
fromCache: ad.fromCache,
|
|
4258
|
+
credit: ad.credit
|
|
4259
|
+
};
|
|
4260
|
+
}
|
|
4261
|
+
};
|
|
4262
|
+
}
|
|
4263
|
+
var MAX_TEXT_CHARS3;
|
|
4264
|
+
var init_network = __esm({
|
|
4265
|
+
"src/sponsor/network.ts"() {
|
|
4266
|
+
"use strict";
|
|
4267
|
+
init_render();
|
|
4268
|
+
MAX_TEXT_CHARS3 = 200;
|
|
4038
4269
|
}
|
|
4039
4270
|
});
|
|
4040
4271
|
|
|
@@ -4398,48 +4629,42 @@ var init_markdown = __esm({
|
|
|
4398
4629
|
// src/tui/views.tsx
|
|
4399
4630
|
import "react";
|
|
4400
4631
|
import { Box, Text } from "ink";
|
|
4632
|
+
import wrapAnsi from "wrap-ansi";
|
|
4401
4633
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4634
|
+
function wrapRows(text, cols) {
|
|
4635
|
+
return wrapAnsi(text, Math.max(1, cols), WRAP_LIKE_INK).split("\n");
|
|
4636
|
+
}
|
|
4402
4637
|
function lineCount(text, cols) {
|
|
4403
|
-
return text
|
|
4638
|
+
return wrapRows(text, cols).length;
|
|
4404
4639
|
}
|
|
4405
4640
|
function tailRows(text, cols, max) {
|
|
4406
4641
|
if (max <= 0) return "";
|
|
4407
|
-
const
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
return
|
|
4642
|
+
const rows = wrapRows(text, cols);
|
|
4643
|
+
if (rows.length <= max) return text;
|
|
4644
|
+
return rows.slice(-max).join("\n");
|
|
4645
|
+
}
|
|
4646
|
+
function fitRow(text, cols) {
|
|
4647
|
+
const width = Math.max(0, cols);
|
|
4648
|
+
if (width === 0) return "";
|
|
4649
|
+
if (visibleLength(text) <= width) return text;
|
|
4650
|
+
if (width === 1) return "\u2026";
|
|
4651
|
+
return `${sliceColumns(text, width - 1)}\u2026`;
|
|
4652
|
+
}
|
|
4653
|
+
function pickHint(variants, cols) {
|
|
4654
|
+
for (const variant of variants) if (visibleLength(variant) <= cols) return variant;
|
|
4655
|
+
return fitRow(variants[variants.length - 1] ?? "", cols);
|
|
4656
|
+
}
|
|
4657
|
+
function padColumns(text, width) {
|
|
4658
|
+
return text + " ".repeat(Math.max(0, width - visibleLength(text)));
|
|
4659
|
+
}
|
|
4660
|
+
function dialogInner(cols) {
|
|
4661
|
+
return Math.max(1, cols - DIALOG_CHROME_COLS);
|
|
4424
4662
|
}
|
|
4425
4663
|
function headRows(text, cols, max) {
|
|
4426
4664
|
if (max <= 0) return "";
|
|
4427
|
-
const
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
let used = 0;
|
|
4431
|
-
for (const line2 of lines) {
|
|
4432
|
-
const h = Math.max(1, Math.ceil(visibleLength(line2) / width));
|
|
4433
|
-
if (used + h <= max) {
|
|
4434
|
-
kept.push(line2);
|
|
4435
|
-
used += h;
|
|
4436
|
-
continue;
|
|
4437
|
-
}
|
|
4438
|
-
const room = max - used;
|
|
4439
|
-
if (room > 0) kept.push(line2.slice(0, room * width));
|
|
4440
|
-
break;
|
|
4441
|
-
}
|
|
4442
|
-
return kept.join("\n");
|
|
4665
|
+
const rows = wrapRows(text, cols);
|
|
4666
|
+
if (rows.length <= max) return text;
|
|
4667
|
+
return rows.slice(0, max).join("\n");
|
|
4443
4668
|
}
|
|
4444
4669
|
function errorNotice(message, rows = ERROR_NOTICE_ROWS) {
|
|
4445
4670
|
const clean = message.replace(/\u001b\][^\u0007]*(?:\u0007|\u001b\\)?/g, " ").replace(/\u001b\[[0-9;?]*[ -\/]*[@-~]/g, " ").replace(/[\u0000-\u0009\u000b-\u001f\u007f-\u009f]/g, " ");
|
|
@@ -4494,7 +4719,7 @@ function EntryView({ entry }) {
|
|
|
4494
4719
|
) });
|
|
4495
4720
|
}
|
|
4496
4721
|
}
|
|
4497
|
-
var MANGO3, MANGO_BRIGHT, SLATE, MODE_STYLE, ERROR_NOTICE_ROWS, ERROR_NOTICE_COLS;
|
|
4722
|
+
var MANGO3, MANGO_BRIGHT, SLATE, AD_TEXT, AD_MARK, MODE_STYLE, WRAP_LIKE_INK, DIALOG_CHROME_COLS, ERROR_NOTICE_ROWS, ERROR_NOTICE_COLS;
|
|
4498
4723
|
var init_views = __esm({
|
|
4499
4724
|
"src/tui/views.tsx"() {
|
|
4500
4725
|
"use strict";
|
|
@@ -4503,37 +4728,52 @@ var init_views = __esm({
|
|
|
4503
4728
|
MANGO3 = "#f5b841";
|
|
4504
4729
|
MANGO_BRIGHT = "#ffcf6b";
|
|
4505
4730
|
SLATE = "#8b98ae";
|
|
4731
|
+
AD_TEXT = "#8a80b4";
|
|
4732
|
+
AD_MARK = "#6b6390";
|
|
4506
4733
|
MODE_STYLE = {
|
|
4507
4734
|
normal: { color: SLATE, glyph: "\u23F5" },
|
|
4508
4735
|
acceptEdits: { color: "#7ee787", glyph: "\u23F5\u23F5" },
|
|
4509
4736
|
plan: { color: "#79c0ff", glyph: "\u23F8" }
|
|
4510
4737
|
};
|
|
4738
|
+
WRAP_LIKE_INK = { trim: false, hard: true };
|
|
4739
|
+
DIALOG_CHROME_COLS = 4;
|
|
4511
4740
|
ERROR_NOTICE_ROWS = 12;
|
|
4512
4741
|
ERROR_NOTICE_COLS = 200;
|
|
4513
4742
|
}
|
|
4514
4743
|
});
|
|
4515
4744
|
|
|
4516
4745
|
// src/tui/app.tsx
|
|
4517
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4746
|
+
import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
4518
4747
|
import { Box as Box2, Static, Text as Text2, useApp, useInput, usePaste, useStdout } from "ink";
|
|
4519
4748
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
4520
4749
|
function useTerminalSize() {
|
|
4521
|
-
const
|
|
4522
|
-
|
|
4523
|
-
|
|
4750
|
+
const { stdout } = useStdout();
|
|
4751
|
+
const snapshot2 = useRef({
|
|
4752
|
+
rows: stdout.rows ?? FALLBACK_ROWS,
|
|
4753
|
+
cols: stdout.columns ?? FALLBACK_COLS
|
|
4524
4754
|
});
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4755
|
+
const subscribe = useCallback(
|
|
4756
|
+
(onChange) => {
|
|
4757
|
+
stdout.prependListener("resize", onChange);
|
|
4758
|
+
return () => {
|
|
4759
|
+
stdout.off("resize", onChange);
|
|
4760
|
+
};
|
|
4761
|
+
},
|
|
4762
|
+
[stdout]
|
|
4763
|
+
);
|
|
4764
|
+
const getSnapshot = useCallback(() => {
|
|
4765
|
+
const rows = stdout.rows ?? FALLBACK_ROWS;
|
|
4766
|
+
const cols = stdout.columns ?? FALLBACK_COLS;
|
|
4767
|
+
if (rows !== snapshot2.current.rows || cols !== snapshot2.current.cols) {
|
|
4768
|
+
snapshot2.current = { rows, cols };
|
|
4769
|
+
}
|
|
4770
|
+
return snapshot2.current;
|
|
4771
|
+
}, [stdout]);
|
|
4772
|
+
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
4533
4773
|
}
|
|
4534
|
-
function App({ client, config, wallet, session, initialTask }) {
|
|
4774
|
+
function App({ client, config, wallet, session, initialTask, sponsorServe }) {
|
|
4535
4775
|
const { exit } = useApp();
|
|
4536
|
-
const { write: writeToStdout } = useStdout();
|
|
4776
|
+
const { stdout, write: writeToStdout } = useStdout();
|
|
4537
4777
|
const idRef = useRef(1);
|
|
4538
4778
|
const { rows, cols } = useTerminalSize();
|
|
4539
4779
|
const root = process.cwd();
|
|
@@ -4541,8 +4781,12 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
4541
4781
|
{
|
|
4542
4782
|
id: 0,
|
|
4543
4783
|
kind: "banner",
|
|
4544
|
-
|
|
4545
|
-
|
|
4784
|
+
// The width ink is actually laying out against. Read once, because a
|
|
4785
|
+
// committed <Static> line is written to the scrollback and belongs to the
|
|
4786
|
+
// terminal from then on — but "once" now means the width at mount rather
|
|
4787
|
+
// than whatever `process.stdout` happened to say.
|
|
4788
|
+
text: (cols >= MIN_BANNER_WIDTH ? welcomeBanner : compactBanner)({
|
|
4789
|
+
width: cols,
|
|
4546
4790
|
name: config.login ?? config.email?.split("@")[0],
|
|
4547
4791
|
model: config.model,
|
|
4548
4792
|
balance: wallet?.balance ?? 0,
|
|
@@ -4605,6 +4849,38 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
4605
4849
|
const sponsorRef = useRef(sponsorSource());
|
|
4606
4850
|
const sponsorIdxRef = useRef(0);
|
|
4607
4851
|
const sponsorPrevRef = useRef(void 0);
|
|
4852
|
+
const sponsorCallRef = useRef(0);
|
|
4853
|
+
const pendingImpressionRef = useRef(null);
|
|
4854
|
+
const sponsorSlotRef = useRef(null);
|
|
4855
|
+
const colsRef = useRef(cols);
|
|
4856
|
+
colsRef.current = cols;
|
|
4857
|
+
const sponsorConfigRef = useRef(null);
|
|
4858
|
+
const remoteSponsorRef = useRef(
|
|
4859
|
+
adtentionSource(async (signal) => {
|
|
4860
|
+
let served;
|
|
4861
|
+
if (sponsorServe) {
|
|
4862
|
+
served = await sponsorServe(colsRef.current, signal);
|
|
4863
|
+
} else {
|
|
4864
|
+
sponsorConfigRef.current ??= client.sponsorConfig(signal);
|
|
4865
|
+
const conf = await sponsorConfigRef.current;
|
|
4866
|
+
if (!conf) return null;
|
|
4867
|
+
sponsorSlotRef.current ??= createSponsorSlot(conf);
|
|
4868
|
+
served = await sponsorSlotRef.current.serve(colsRef.current, signal);
|
|
4869
|
+
}
|
|
4870
|
+
if (!served) return null;
|
|
4871
|
+
return {
|
|
4872
|
+
id: served.id,
|
|
4873
|
+
text: served.text,
|
|
4874
|
+
impression: {
|
|
4875
|
+
impressionId: served.impressionId,
|
|
4876
|
+
adId: served.id,
|
|
4877
|
+
billable: served.billable,
|
|
4878
|
+
fromCache: served.fromCache,
|
|
4879
|
+
credit: served.credit
|
|
4880
|
+
}
|
|
4881
|
+
};
|
|
4882
|
+
})
|
|
4883
|
+
);
|
|
4608
4884
|
const tallyRef = useRef(createTally());
|
|
4609
4885
|
const heightCacheRef = useRef(/* @__PURE__ */ new Map());
|
|
4610
4886
|
const push = useCallback((e) => {
|
|
@@ -4692,8 +4968,9 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
4692
4968
|
);
|
|
4693
4969
|
const nextSponsor = useCallback(
|
|
4694
4970
|
(modelId) => {
|
|
4971
|
+
const call = ++sponsorCallRef.current;
|
|
4695
4972
|
if (!sponsorEnabled({
|
|
4696
|
-
isTTY:
|
|
4973
|
+
isTTY: stdout.isTTY,
|
|
4697
4974
|
env: process.env,
|
|
4698
4975
|
configEnabled: config.sponsor
|
|
4699
4976
|
})) {
|
|
@@ -4706,16 +4983,31 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
4706
4983
|
adsPerTask: est === void 0 ? void 0 : adsPerTaskLabel(est)
|
|
4707
4984
|
};
|
|
4708
4985
|
const picked = sponsorRef.current.pick(sponsorIdxRef.current++, sponsorPrevRef.current, facts);
|
|
4709
|
-
if (
|
|
4986
|
+
if (picked) {
|
|
4987
|
+
sponsorPrevRef.current = picked.id;
|
|
4988
|
+
recordImpression(tallyRef.current, picked.id);
|
|
4989
|
+
setSponsor({ text: picked.text, origin: "house" });
|
|
4990
|
+
} else {
|
|
4710
4991
|
setSponsor(null);
|
|
4711
|
-
return;
|
|
4712
4992
|
}
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4993
|
+
if (!adFitsWidth(colsRef.current)) return;
|
|
4994
|
+
void remoteSponsorRef.current.fetch().then((line2) => {
|
|
4995
|
+
if (!line2 || call !== sponsorCallRef.current) return;
|
|
4996
|
+
if (!sponsorRow(line2.text, colsRef.current, "network")) return;
|
|
4997
|
+
sponsorPrevRef.current = line2.id;
|
|
4998
|
+
recordImpression(tallyRef.current, line2.id);
|
|
4999
|
+
setSponsor({ text: line2.text, origin: "network" });
|
|
5000
|
+
pendingImpressionRef.current = { call, impression: line2.impression };
|
|
5001
|
+
});
|
|
4716
5002
|
},
|
|
4717
|
-
[config.sponsor, wallet]
|
|
5003
|
+
[config.sponsor, stdout, wallet]
|
|
4718
5004
|
);
|
|
5005
|
+
const reportSponsor = useCallback(() => {
|
|
5006
|
+
const pending = pendingImpressionRef.current;
|
|
5007
|
+
pendingImpressionRef.current = null;
|
|
5008
|
+
if (!pending || pending.call !== sponsorCallRef.current) return;
|
|
5009
|
+
void client.reportImpression(pending.impression);
|
|
5010
|
+
}, [client]);
|
|
4719
5011
|
const askUser = useCallback(
|
|
4720
5012
|
(req) => new Promise((resolve2) => setAsk({ req, resolve: resolve2 })),
|
|
4721
5013
|
[]
|
|
@@ -4800,7 +5092,7 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
4800
5092
|
return;
|
|
4801
5093
|
}
|
|
4802
5094
|
const lines = event.result.split("\n").filter((l) => l.trim() !== "");
|
|
4803
|
-
const shown = lines.slice(0, COMMITTED_OUTPUT_LINES).map((l) => l
|
|
5095
|
+
const shown = lines.slice(0, COMMITTED_OUTPUT_LINES).map((l) => fitRow(l, Math.max(20, cols - 4)));
|
|
4804
5096
|
const more = Math.max(0, lines.length - shown.length);
|
|
4805
5097
|
const id = push({
|
|
4806
5098
|
kind: "tool",
|
|
@@ -4939,6 +5231,8 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
4939
5231
|
dropDelta();
|
|
4940
5232
|
setLive(null);
|
|
4941
5233
|
setBusy(false);
|
|
5234
|
+
reportSponsor();
|
|
5235
|
+
sponsorCallRef.current++;
|
|
4942
5236
|
setSponsor(null);
|
|
4943
5237
|
}
|
|
4944
5238
|
},
|
|
@@ -4950,6 +5244,7 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
4950
5244
|
dropDelta,
|
|
4951
5245
|
handleEvent,
|
|
4952
5246
|
model,
|
|
5247
|
+
reportSponsor,
|
|
4953
5248
|
nextSponsor,
|
|
4954
5249
|
permit,
|
|
4955
5250
|
push,
|
|
@@ -5268,7 +5563,14 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
5268
5563
|
push({
|
|
5269
5564
|
kind: "notice",
|
|
5270
5565
|
text: list.map(
|
|
5271
|
-
(s) =>
|
|
5566
|
+
(s) => (
|
|
5567
|
+
// A command is arbitrary text, so it is cut in columns like
|
|
5568
|
+
// everything else that is measured against the width.
|
|
5569
|
+
fitRow(
|
|
5570
|
+
` ${s.id.padEnd(8)} ${s.status.padEnd(8)} ${Math.round(s.runningMs / 1e3)}s ${s.command}`,
|
|
5571
|
+
Math.max(28, cols)
|
|
5572
|
+
)
|
|
5573
|
+
)
|
|
5272
5574
|
).join("\n").concat("\n /bg kill <id> \xB7 /bg kill all")
|
|
5273
5575
|
});
|
|
5274
5576
|
return;
|
|
@@ -5322,7 +5624,7 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
5322
5624
|
{ command }
|
|
5323
5625
|
);
|
|
5324
5626
|
const lines = output.split("\n").filter((l) => l.trim() !== "");
|
|
5325
|
-
const shown = lines.slice(0, COMMITTED_OUTPUT_LINES).map((l) => l
|
|
5627
|
+
const shown = lines.slice(0, COMMITTED_OUTPUT_LINES).map((l) => fitRow(l, Math.max(20, cols - 4)));
|
|
5326
5628
|
const more = Math.max(0, lines.length - shown.length);
|
|
5327
5629
|
const id = push({
|
|
5328
5630
|
kind: "tool",
|
|
@@ -5562,36 +5864,82 @@ ${dropped.map((l) => ` \xB7 ${l}`).join("\n")}`
|
|
|
5562
5864
|
});
|
|
5563
5865
|
const elapsed = busy && startedAt ? Math.floor((Date.now() - startedAt) / 1e3) : 0;
|
|
5564
5866
|
const spinner = spinnerRef.current[Math.floor(tick / SPINNER_TICKS_PER_FRAME) % spinnerRef.current.length];
|
|
5565
|
-
const sponsorLine = busy && sponsor ?
|
|
5566
|
-
const
|
|
5867
|
+
const sponsorLine = busy && sponsor ? sponsorRow(sponsor.text, cols, sponsor.origin) : null;
|
|
5868
|
+
const adColor = adColorEnabled({ isTTY: stdout.isTTY, env: process.env });
|
|
5869
|
+
const viewport = Math.max(1, rows - 1);
|
|
5567
5870
|
const inputRows = Math.max(1, Math.min(MAX_INPUT_ROWS, editor.lines.length));
|
|
5568
5871
|
const inputFrom = windowStart(editor.row, editor.lines.length, inputRows);
|
|
5569
|
-
const
|
|
5570
|
-
|
|
5571
|
-
...queueView.slice(0, MAX_QUEUE_ROWS).map((line2) => line2.replace(/\s+/g, " ").slice(0, Math.max(10, cols - 6))),
|
|
5872
|
+
const queuedRows = [
|
|
5873
|
+
...queueView.slice(0, MAX_QUEUE_ROWS).map((line2) => line2.replace(/\s+/g, " ")),
|
|
5572
5874
|
...queueView.length > MAX_QUEUE_ROWS ? [`+${queueView.length - MAX_QUEUE_ROWS} more queued`] : []
|
|
5573
|
-
];
|
|
5574
|
-
const
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
const
|
|
5875
|
+
].map((line2) => fitRow(` ${QUEUE_MARKER} ${line2}`, cols));
|
|
5876
|
+
const inner = dialogInner(cols);
|
|
5877
|
+
const modalFloor = picker ? PICKER_CHROME_COMPACT + 1 : ask2 ? ASK_CHROME + 1 : plan ? PLAN_CHROME + 1 : 0;
|
|
5878
|
+
let slack = Math.max(0, viewport - inputRows - modalFloor - menu.length);
|
|
5879
|
+
const grant = (want) => {
|
|
5880
|
+
if (want <= 0 || want > slack) return 0;
|
|
5881
|
+
slack -= want;
|
|
5882
|
+
return want;
|
|
5883
|
+
};
|
|
5884
|
+
const inputBorder = grant(2) > 0;
|
|
5885
|
+
const busyHeight = grant(busy ? 2 : 0);
|
|
5886
|
+
const showStatus = grant(2) > 0;
|
|
5887
|
+
const queueRows = grant(queuedRows.length) > 0 ? queuedRows : [];
|
|
5888
|
+
const showSponsor = grant(sponsorLine ? 1 : 0) > 0;
|
|
5889
|
+
const inputBoxHeight = inputRows + (inputBorder ? 2 : 0);
|
|
5890
|
+
const chromeHeight = inputBoxHeight + (showStatus ? 2 : 0) + (showSponsor ? 1 : 0) + queueRows.length;
|
|
5891
|
+
let budget = Math.max(0, viewport - chromeHeight - menu.length - busyHeight);
|
|
5892
|
+
const pickerCompact = Boolean(picker) && budget < PICKER_CHROME + 1;
|
|
5893
|
+
const pickerChrome = pickerCompact ? PICKER_CHROME_COMPACT : PICKER_CHROME;
|
|
5894
|
+
const pickerRows = picker ? Math.max(1, Math.min(MAX_PICKER_ROWS, picker.items.length, budget - pickerChrome)) : 0;
|
|
5578
5895
|
const pickerFrom = picker ? windowStart(pickerSel, picker.items.length, pickerRows) : 0;
|
|
5579
5896
|
const pickerItems = picker ? picker.items.slice(pickerFrom, pickerFrom + pickerRows) : [];
|
|
5580
|
-
const pickerHeight = picker ? pickerItems.length +
|
|
5897
|
+
const pickerHeight = picker ? pickerItems.length + pickerChrome : 0;
|
|
5581
5898
|
pickerWindowRef.current = { from: pickerFrom, count: pickerItems.length };
|
|
5582
|
-
const pickerLabelW = picker ?
|
|
5899
|
+
const pickerLabelW = picker ? Math.min(
|
|
5900
|
+
Math.max(1, Math.floor(inner * 0.6)),
|
|
5901
|
+
picker.items.reduce((w, i) => Math.max(w, visibleLength(i.label)), 0)
|
|
5902
|
+
) : 0;
|
|
5903
|
+
const pickerViewRows = pickerItems.map((item, i) => {
|
|
5904
|
+
const index = pickerFrom + i;
|
|
5905
|
+
const marker = item.current ? PICKER_CURRENT : "";
|
|
5906
|
+
const markerW = Math.min(visibleLength(marker), Math.max(0, inner - MIN_PICKER_LABEL_COLS));
|
|
5907
|
+
const head = fitRow(
|
|
5908
|
+
`${index === pickerSel ? "\u276F " : " "}${i + 1}. ${padColumns(item.label, pickerLabelW + 2)}`,
|
|
5909
|
+
Math.max(1, inner - markerW)
|
|
5910
|
+
);
|
|
5911
|
+
const room = inner - visibleLength(head) - markerW;
|
|
5912
|
+
return {
|
|
5913
|
+
key: item.value,
|
|
5914
|
+
selected: index === pickerSel,
|
|
5915
|
+
head,
|
|
5916
|
+
hint: room > 1 ? fitRow(item.hint, room) : "",
|
|
5917
|
+
marker: markerW >= visibleLength(marker) ? marker : ""
|
|
5918
|
+
};
|
|
5919
|
+
});
|
|
5920
|
+
const pickerTitle = picker ? fitRow(picker.title, inner) : "";
|
|
5921
|
+
const pickerSubtitle = picker ? fitRow(picker.subtitle, inner) : "";
|
|
5922
|
+
const pickerHint = picker ? pickHint(
|
|
5923
|
+
picker.items.length > pickerItems.length ? [
|
|
5924
|
+
`\u2191\u2193 choose (${pickerFrom + 1}-${pickerFrom + pickerItems.length} of ${picker.items.length}) \xB7 1-${pickerItems.length} jump \xB7 \u23CE confirm \xB7 esc cancel`,
|
|
5925
|
+
`\u2191\u2193 ${pickerFrom + 1}-${pickerFrom + pickerItems.length}/${picker.items.length} \xB7 \u23CE confirm \xB7 esc cancel`,
|
|
5926
|
+
"\u2191\u2193 \xB7 \u23CE \xB7 esc"
|
|
5927
|
+
] : [
|
|
5928
|
+
`\u2191\u2193 choose \xB7 1-${Math.min(9, pickerItems.length)} jump straight to a row \xB7 \u23CE confirm \xB7 esc cancel`,
|
|
5929
|
+
`\u2191\u2193 choose \xB7 1-${Math.min(9, pickerItems.length)} jump \xB7 \u23CE confirm \xB7 esc cancel`,
|
|
5930
|
+
"\u2191\u2193 \xB7 \u23CE \xB7 esc"
|
|
5931
|
+
],
|
|
5932
|
+
inner
|
|
5933
|
+
) : "";
|
|
5583
5934
|
budget -= pickerHeight;
|
|
5584
|
-
const
|
|
5585
|
-
const askSummaryRows = ask2 ? lineCount(
|
|
5586
|
-
const askPreview = ask2?.req.preview ? headRows(ask2.req.preview,
|
|
5587
|
-
const askHeight = ask2 ? askSummaryRows +
|
|
5935
|
+
const askSummary = ask2 ? headRows(ask2.req.summary, inner, Math.max(1, budget - ASK_CHROME)) : "";
|
|
5936
|
+
const askSummaryRows = ask2 ? lineCount(askSummary, inner) : 0;
|
|
5937
|
+
const askPreview = ask2?.req.preview ? headRows(ask2.req.preview, inner, Math.max(0, budget - ASK_CHROME - askSummaryRows)) : "";
|
|
5938
|
+
const askHeight = ask2 ? askSummaryRows + (askPreview ? lineCount(askPreview, inner) : 0) + ASK_CHROME : 0;
|
|
5588
5939
|
budget -= askHeight;
|
|
5589
|
-
const
|
|
5590
|
-
const
|
|
5591
|
-
const planHeight = plan ? lineCount(planBlock, cols) + PLAN_CHROME : 0;
|
|
5940
|
+
const planBlock = plan ? headRows(renderMarkdown(plan.plan), inner, Math.max(1, budget - PLAN_CHROME)) : "";
|
|
5941
|
+
const planHeight = plan ? lineCount(planBlock, inner) + PLAN_CHROME : 0;
|
|
5592
5942
|
budget -= planHeight;
|
|
5593
|
-
const busyHeight = busy ? 2 : 0;
|
|
5594
|
-
budget -= busyHeight;
|
|
5595
5943
|
const liveRaw = [
|
|
5596
5944
|
// Trailing blank lines are rows the clamp would spend on nothing, and a
|
|
5597
5945
|
// streamed answer ends on one more often than not.
|
|
@@ -5615,7 +5963,10 @@ ${dropped.map((l) => ` \xB7 ${l}`).join("\n")}`
|
|
|
5615
5963
|
}, [entries, cols]);
|
|
5616
5964
|
const used = chromeHeight + menu.length + pickerHeight + askHeight + planHeight + busyHeight + liveHeight;
|
|
5617
5965
|
const spacer = Math.max(0, viewport - printed - used);
|
|
5618
|
-
const labelW =
|
|
5966
|
+
const labelW = Math.min(
|
|
5967
|
+
Math.max(1, Math.floor(cols * 0.5)),
|
|
5968
|
+
menu.reduce((w, c2) => Math.max(w, visibleLength(c2.label)), 0)
|
|
5969
|
+
);
|
|
5619
5970
|
const contextPct = useMemo(() => {
|
|
5620
5971
|
const window = contextWindow(model);
|
|
5621
5972
|
if (!window) return 0;
|
|
@@ -5625,81 +5976,86 @@ ${dropped.map((l) => ` \xB7 ${l}`).join("\n")}`
|
|
|
5625
5976
|
]);
|
|
5626
5977
|
return used2 / window;
|
|
5627
5978
|
}, [contextWindow, messages, model]);
|
|
5979
|
+
const modeText = ` ${MODE_STYLE[mode].glyph} ${MODE_LABEL[mode]}`;
|
|
5980
|
+
const statusText = fitRow(
|
|
5981
|
+
` ${statusLine(
|
|
5982
|
+
{ model, balance, spent, contextPct, minutes: (Date.now() - runStartedAtRef.current) / 6e4 },
|
|
5983
|
+
Math.max(1, cols - visibleLength(modeText) - 2)
|
|
5984
|
+
)}`,
|
|
5985
|
+
Math.max(0, cols - visibleLength(modeText))
|
|
5986
|
+
);
|
|
5987
|
+
const hintText = quitHint ? fitRow(" press ctrl+c again to quit", cols) : ` ${pickHint(
|
|
5988
|
+
["\u2191\u2193 choose \xB7 \u23CE run \xB7 tab complete \xB7 esc close", "\u2191\u2193 \xB7 \u23CE run \xB7 tab \xB7 esc", "\u2191\u2193 \u23CE tab esc"],
|
|
5989
|
+
Math.max(1, cols - 2)
|
|
5990
|
+
)}`;
|
|
5991
|
+
const menuRows = menu.map((item, i) => {
|
|
5992
|
+
const head = fitRow(`${i === sel ? " \u276F " : " "}${padColumns(item.label, labelW + 2)}`, cols);
|
|
5993
|
+
const room = cols - visibleLength(head);
|
|
5994
|
+
return { key: item.value, selected: i === sel, head, hint: room > 1 ? fitRow(item.hint, room) : "" };
|
|
5995
|
+
});
|
|
5996
|
+
const busyText = fitRow(`${busyLabel} ${elapsed}s \xB7 esc to stop`, Math.max(1, cols - visibleLength(`${spinner} `)));
|
|
5628
5997
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
5629
5998
|
/* @__PURE__ */ jsx2(Static, { items: entries, children: (entry) => /* @__PURE__ */ jsx2(EntryView, { entry }, entry.id) }, staticEpoch),
|
|
5630
5999
|
spacer > 0 ? /* @__PURE__ */ jsx2(Box2, { height: spacer }) : null,
|
|
5631
6000
|
liveBlock ? /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(Text2, { children: liveBlock }) }) : null,
|
|
5632
|
-
|
|
6001
|
+
busyHeight > 0 ? /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, children: [
|
|
5633
6002
|
/* @__PURE__ */ jsxs2(Text2, { color: MANGO3, children: [
|
|
5634
6003
|
spinner,
|
|
5635
6004
|
" "
|
|
5636
6005
|
] }),
|
|
5637
|
-
/* @__PURE__ */
|
|
5638
|
-
busyLabel,
|
|
5639
|
-
" ",
|
|
5640
|
-
elapsed,
|
|
5641
|
-
"s \xB7 esc to stop"
|
|
5642
|
-
] })
|
|
6006
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, wrap: "truncate-end", children: busyText })
|
|
5643
6007
|
] }) : null,
|
|
5644
6008
|
ask2 ? /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
5645
|
-
/* @__PURE__ */ jsx2(Text2, { bold: true, color: "yellow", children:
|
|
6009
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, color: "yellow", children: askSummary }),
|
|
5646
6010
|
askPreview ? /* @__PURE__ */ jsx2(Text2, { children: askPreview }) : null,
|
|
5647
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true,
|
|
6011
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, wrap: "truncate-end", children: pickHint(ASK_HINT, inner) })
|
|
5648
6012
|
] }) : null,
|
|
5649
6013
|
plan ? /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: MODE_STYLE.plan.color, paddingX: 1, children: [
|
|
5650
|
-
/* @__PURE__ */ jsx2(Text2, { bold: true, color: MODE_STYLE.plan.color, children: `${MODE_STYLE.plan.glyph} Ready to act on this plan
|
|
6014
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, color: MODE_STYLE.plan.color, wrap: "truncate-end", children: fitRow(`${MODE_STYLE.plan.glyph} Ready to act on this plan?`, inner) }),
|
|
5651
6015
|
/* @__PURE__ */ jsx2(Box2, { height: 1 }),
|
|
5652
|
-
/* @__PURE__ */ jsx2(Text2, { children:
|
|
5653
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true,
|
|
6016
|
+
/* @__PURE__ */ jsx2(Text2, { children: planBlock }),
|
|
6017
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, wrap: "truncate-end", children: pickHint(PLAN_HINT, inner) })
|
|
5654
6018
|
] }) : null,
|
|
5655
6019
|
picker ? /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: MANGO3, paddingX: 1, children: [
|
|
5656
|
-
/* @__PURE__ */ jsx2(Text2, { bold: true, color: MANGO_BRIGHT, children:
|
|
5657
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children:
|
|
5658
|
-
/* @__PURE__ */ jsx2(Box2, { height: 1 }),
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
] }),
|
|
5667
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: item.hint }),
|
|
5668
|
-
item.current ? /* @__PURE__ */ jsx2(Text2, { color: "green", children: " \u2190 current" }) : null
|
|
5669
|
-
] }, item.value);
|
|
5670
|
-
}),
|
|
5671
|
-
/* @__PURE__ */ jsx2(Box2, { height: 1 }),
|
|
5672
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: picker.items.length > pickerItems.length ? `\u2191\u2193 choose (${pickerFrom + 1}-${pickerFrom + pickerItems.length} of ${picker.items.length}) \xB7 1-${pickerItems.length} jump \xB7 \u23CE confirm \xB7 esc cancel` : `\u2191\u2193 choose \xB7 1-${Math.min(9, pickerItems.length)} jump straight to a row \xB7 \u23CE confirm \xB7 esc cancel` })
|
|
6020
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, color: MANGO_BRIGHT, children: pickerTitle }),
|
|
6021
|
+
pickerCompact ? null : /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: pickerSubtitle }),
|
|
6022
|
+
pickerCompact ? null : /* @__PURE__ */ jsx2(Box2, { height: 1 }),
|
|
6023
|
+
pickerViewRows.map((row) => /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
6024
|
+
/* @__PURE__ */ jsx2(Text2, { color: row.selected ? MANGO_BRIGHT : void 0, bold: row.selected, wrap: "truncate-end", children: row.head }),
|
|
6025
|
+
row.hint ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, wrap: "truncate-end", children: row.hint }) : null,
|
|
6026
|
+
row.marker ? /* @__PURE__ */ jsx2(Text2, { color: "green", children: row.marker }) : null
|
|
6027
|
+
] }, row.key)),
|
|
6028
|
+
pickerCompact ? null : /* @__PURE__ */ jsx2(Box2, { height: 1 }),
|
|
6029
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: pickerHint })
|
|
5673
6030
|
] }) : null,
|
|
5674
6031
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: 1, children: [
|
|
5675
|
-
queueRows.map((line2, i) => /* @__PURE__ */ jsx2(Text2, { dimColor: true, children:
|
|
5676
|
-
sponsorLine ? /* @__PURE__ */
|
|
5677
|
-
|
|
6032
|
+
queueRows.map((line2, i) => /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: line2 }, i)),
|
|
6033
|
+
showSponsor && sponsorLine ? /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
6034
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, wrap: "truncate-end", children: sponsorLine.marker }),
|
|
6035
|
+
sponsorLine.label ? /* @__PURE__ */ jsx2(Text2, { ...adColor ? { color: AD_MARK } : { dimColor: true }, wrap: "truncate-end", children: sponsorLine.label }) : null,
|
|
6036
|
+
/* @__PURE__ */ jsx2(
|
|
6037
|
+
Text2,
|
|
6038
|
+
{
|
|
6039
|
+
...adColor && sponsorLine.origin === "network" ? { color: AD_TEXT } : { dimColor: true },
|
|
6040
|
+
wrap: "truncate-end",
|
|
6041
|
+
children: sponsorLine.body
|
|
6042
|
+
}
|
|
6043
|
+
)
|
|
6044
|
+
] }) : null,
|
|
6045
|
+
inputBorder ? /* @__PURE__ */ jsxs2(Box2, { borderStyle: "round", borderColor: busy ? MANGO3 : MANGO_BRIGHT, paddingX: 1, children: [
|
|
5678
6046
|
/* @__PURE__ */ jsx2(Text2, { color: MANGO_BRIGHT, children: "\u276F " }),
|
|
5679
6047
|
/* @__PURE__ */ jsx2(Text2, { children: renderInput(editor, inputFrom, inputRows) })
|
|
5680
|
-
] }),
|
|
5681
|
-
menu.map((item, i) => /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
5682
|
-
/* @__PURE__ */ jsxs2(Text2, { color: i === sel ? MANGO_BRIGHT : MANGO3, bold: i === sel, children: [
|
|
5683
|
-
i === sel ? " \u276F " : " ",
|
|
5684
|
-
item.label.padEnd(labelW + 2)
|
|
5685
|
-
] }),
|
|
5686
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: i !== sel, children: item.hint })
|
|
5687
|
-
] }, item.value)),
|
|
5688
|
-
quitHint || menu.length > 0 ? /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
5689
|
-
" ",
|
|
5690
|
-
quitHint ? "press ctrl+c again to quit" : "\u2191\u2193 choose \xB7 \u23CE run \xB7 tab complete \xB7 esc close"
|
|
5691
6048
|
] }) : /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
5692
|
-
/* @__PURE__ */ jsx2(Text2, { color:
|
|
5693
|
-
/* @__PURE__ */
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
] })
|
|
6049
|
+
/* @__PURE__ */ jsx2(Text2, { color: MANGO_BRIGHT, children: "\u276F " }),
|
|
6050
|
+
/* @__PURE__ */ jsx2(Text2, { children: renderInput(editor, inputFrom, inputRows) })
|
|
6051
|
+
] }),
|
|
6052
|
+
menuRows.map((row) => /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
6053
|
+
/* @__PURE__ */ jsx2(Text2, { color: row.selected ? MANGO_BRIGHT : MANGO3, bold: row.selected, wrap: "truncate-end", children: row.head }),
|
|
6054
|
+
row.hint ? /* @__PURE__ */ jsx2(Text2, { dimColor: !row.selected, wrap: "truncate-end", children: row.hint }) : null
|
|
6055
|
+
] }, row.key)),
|
|
6056
|
+
!showStatus ? null : quitHint || menu.length > 0 ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, wrap: "truncate-end", children: hintText }) : /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
6057
|
+
/* @__PURE__ */ jsx2(Text2, { color: MODE_STYLE[mode].color, bold: true, wrap: "truncate-end", children: modeText }),
|
|
6058
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, wrap: "truncate-end", children: statusText })
|
|
5703
6059
|
] })
|
|
5704
6060
|
] })
|
|
5705
6061
|
] });
|
|
@@ -5731,17 +6087,23 @@ function renderInput(state, from, rows) {
|
|
|
5731
6087
|
] }, row);
|
|
5732
6088
|
});
|
|
5733
6089
|
}
|
|
5734
|
-
function statusLine(o) {
|
|
6090
|
+
function statusLine(o, cols) {
|
|
5735
6091
|
const burn = o.spent > 0 && o.minutes >= 1 ? `${Math.round(o.spent / o.minutes).toLocaleString("en-US")} cr/min` : void 0;
|
|
5736
|
-
const
|
|
5737
|
-
o.model,
|
|
5738
|
-
`${o.balance.toLocaleString("en-US")} cr`,
|
|
5739
|
-
o.spent > 0 ? `\u2212${o.spent.toLocaleString("en-US")}` : void 0,
|
|
5740
|
-
burn,
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
6092
|
+
const clauses = [
|
|
6093
|
+
{ text: o.model, rank: KEEP_ALWAYS },
|
|
6094
|
+
{ text: `${o.balance.toLocaleString("en-US")} cr`, rank: KEEP_ALWAYS },
|
|
6095
|
+
{ text: o.spent > 0 ? `\u2212${o.spent.toLocaleString("en-US")}` : void 0, rank: 2 },
|
|
6096
|
+
{ text: burn, rank: 1 },
|
|
6097
|
+
{
|
|
6098
|
+
text: o.contextPct >= CONTEXT_NOTICE_AT ? `context ${Math.round(o.contextPct * 100)}%` : void 0,
|
|
6099
|
+
rank: 3
|
|
6100
|
+
},
|
|
6101
|
+
{ text: "/help", rank: 0 }
|
|
6102
|
+
].filter((c2) => Boolean(c2.text));
|
|
6103
|
+
const join7 = (minRank) => clauses.filter((c2) => c2.rank >= minRank).map((c2) => c2.text).join(" \xB7 ");
|
|
6104
|
+
let line2 = join7(0);
|
|
6105
|
+
for (let minRank = 1; minRank <= 4 && visibleLength(line2) > cols; minRank++) line2 = join7(minRank);
|
|
6106
|
+
return fitRow(line2, cols);
|
|
5745
6107
|
}
|
|
5746
6108
|
function windowStart(sel, total, size) {
|
|
5747
6109
|
if (total <= size) return 0;
|
|
@@ -5792,7 +6154,7 @@ function tailLines(text, max) {
|
|
|
5792
6154
|
const lines = text.split("\n");
|
|
5793
6155
|
return lines.length <= max ? text : lines.slice(-max).join("\n");
|
|
5794
6156
|
}
|
|
5795
|
-
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, EXPAND_MAX_LINES, CONTEXT_NOTICE_AT;
|
|
6157
|
+
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;
|
|
5796
6158
|
var init_app = __esm({
|
|
5797
6159
|
"src/tui/app.tsx"() {
|
|
5798
6160
|
"use strict";
|
|
@@ -5816,6 +6178,7 @@ var init_app = __esm({
|
|
|
5816
6178
|
init_session();
|
|
5817
6179
|
init_counter();
|
|
5818
6180
|
init_provider();
|
|
6181
|
+
init_network();
|
|
5819
6182
|
init_render();
|
|
5820
6183
|
init_commands();
|
|
5821
6184
|
init_editor();
|
|
@@ -5835,8 +6198,28 @@ var init_app = __esm({
|
|
|
5835
6198
|
MAX_INPUT_ROWS = 10;
|
|
5836
6199
|
MAX_PICKER_ROWS = 12;
|
|
5837
6200
|
MAX_QUEUE_ROWS = 3;
|
|
6201
|
+
QUEUE_MARKER = "\u23F3";
|
|
6202
|
+
PICKER_CURRENT = " \u2190 current";
|
|
6203
|
+
MIN_PICKER_LABEL_COLS = 8;
|
|
6204
|
+
PICKER_CHROME = 8;
|
|
6205
|
+
PICKER_CHROME_COMPACT = 5;
|
|
6206
|
+
ASK_CHROME = 4;
|
|
6207
|
+
PLAN_CHROME = 6;
|
|
6208
|
+
ASK_HINT = [
|
|
6209
|
+
"[y/\u23CE] once \xB7 [a] always \xB7 [n] no \xB7 esc stops the turn",
|
|
6210
|
+
"y once \xB7 a always \xB7 n no \xB7 esc stops",
|
|
6211
|
+
"y/a/n \xB7 esc"
|
|
6212
|
+
];
|
|
6213
|
+
PLAN_HINT = [
|
|
6214
|
+
"[y/\u23CE] yes, ask before edits \xB7 [a] yes, auto-accept edits \xB7 [n] keep planning \xB7 esc stops the turn",
|
|
6215
|
+
"y ask first \xB7 a auto-accept \xB7 n keep planning \xB7 esc stops",
|
|
6216
|
+
"y/a/n \xB7 esc"
|
|
6217
|
+
];
|
|
5838
6218
|
EXPAND_MAX_LINES = 400;
|
|
6219
|
+
FALLBACK_ROWS = 24;
|
|
6220
|
+
FALLBACK_COLS = 80;
|
|
5839
6221
|
CONTEXT_NOTICE_AT = 0.6;
|
|
6222
|
+
KEEP_ALWAYS = Number.POSITIVE_INFINITY;
|
|
5840
6223
|
}
|
|
5841
6224
|
});
|
|
5842
6225
|
|
|
@@ -5878,6 +6261,7 @@ init_session();
|
|
|
5878
6261
|
init_kimi();
|
|
5879
6262
|
init_banner();
|
|
5880
6263
|
init_browser();
|
|
6264
|
+
init_color();
|
|
5881
6265
|
|
|
5882
6266
|
// src/title.ts
|
|
5883
6267
|
var APP_TITLE = "Clixad";
|
|
@@ -5907,14 +6291,7 @@ function clearTerminalTitle(out = titleStream()) {
|
|
|
5907
6291
|
|
|
5908
6292
|
// src/main.ts
|
|
5909
6293
|
init_version();
|
|
5910
|
-
var c = {
|
|
5911
|
-
cyan: (s) => `\x1B[36m${s}\x1B[0m`,
|
|
5912
|
-
green: (s) => `\x1B[32m${s}\x1B[0m`,
|
|
5913
|
-
yellow: (s) => `\x1B[33m${s}\x1B[0m`,
|
|
5914
|
-
red: (s) => `\x1B[31m${s}\x1B[0m`,
|
|
5915
|
-
dim: (s) => `\x1B[2m${s}\x1B[0m`,
|
|
5916
|
-
bold: (s) => `\x1B[1m${s}\x1B[0m`
|
|
5917
|
-
};
|
|
6294
|
+
var c = palette({ isTTY: process.stdout.isTTY, env: process.env });
|
|
5918
6295
|
async function main() {
|
|
5919
6296
|
setTerminalTitle();
|
|
5920
6297
|
process.on("exit", () => clearTerminalTitle());
|
|
@@ -5982,10 +6359,10 @@ async function login(client, config, args) {
|
|
|
5982
6359
|
console.log(`
|
|
5983
6360
|
Open ${c.cyan(start.verification_uri)} and enter code: ${c.bold(start.user_code)}
|
|
5984
6361
|
`);
|
|
5985
|
-
process.stdout.write(c.dim(" waiting for GitHub authorization\u2026 (Ctrl+C to cancel)"));
|
|
6362
|
+
if (c.enabled) process.stdout.write(c.dim(" waiting for GitHub authorization\u2026 (Ctrl+C to cancel)"));
|
|
5986
6363
|
},
|
|
5987
6364
|
tick() {
|
|
5988
|
-
process.stdout.write(c.dim("."));
|
|
6365
|
+
if (c.enabled) process.stdout.write(c.dim("."));
|
|
5989
6366
|
}
|
|
5990
6367
|
});
|
|
5991
6368
|
switch (outcome.status) {
|
|
@@ -6110,11 +6487,11 @@ async function earn(client, config) {
|
|
|
6110
6487
|
console.log(
|
|
6111
6488
|
c.dim(" Credits are granted on completion. Surveys screen people out part way through \u2014\n") + c.dim(" that pays nothing and is the normal case, so just start another one.")
|
|
6112
6489
|
);
|
|
6113
|
-
process.stdout.write(c.dim(" waiting for an offer to clear"));
|
|
6490
|
+
if (c.enabled) process.stdout.write(c.dim(" waiting for an offer to clear"));
|
|
6114
6491
|
const deadline = Date.now() + 5 * 60 * 1e3;
|
|
6115
6492
|
while (Date.now() < deadline) {
|
|
6116
6493
|
await sleep4(3e3);
|
|
6117
|
-
process.stdout.write(c.dim("."));
|
|
6494
|
+
if (c.enabled) process.stdout.write(c.dim("."));
|
|
6118
6495
|
const balance = (await safeWallet(client))?.balance ?? before;
|
|
6119
6496
|
if (balance > before) {
|
|
6120
6497
|
console.log(
|
|
@@ -6154,11 +6531,11 @@ async function buyCmd(client, config, pack) {
|
|
|
6154
6531
|
Stripe Checkout: ${c.bold(checkout.pack)} \u2014 ${checkout.credits.toLocaleString("en-US")} credits for ${c.bold("$" + checkout.price_usd.toFixed(2))}`);
|
|
6155
6532
|
console.log(c.dim(` ${checkout.url}`));
|
|
6156
6533
|
openBrowser(checkout.url);
|
|
6157
|
-
process.stdout.write(c.dim(" waiting for payment to clear"));
|
|
6534
|
+
if (c.enabled) process.stdout.write(c.dim(" waiting for payment to clear"));
|
|
6158
6535
|
const deadline = Date.now() + 5 * 60 * 1e3;
|
|
6159
6536
|
while (Date.now() < deadline) {
|
|
6160
6537
|
await sleep4(3e3);
|
|
6161
|
-
process.stdout.write(c.dim("."));
|
|
6538
|
+
if (c.enabled) process.stdout.write(c.dim("."));
|
|
6162
6539
|
const balance = (await safeWallet(client))?.balance ?? before;
|
|
6163
6540
|
if (balance > before) {
|
|
6164
6541
|
console.log(c.green(`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clixad",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
3
|
+
"version": "0.0.1-beta.13",
|
|
4
4
|
"description": "Free AI coding agent in your terminal, funded by rewarded ads.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -39,7 +39,11 @@
|
|
|
39
39
|
"ink-testing-library": "^4.0.0"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
+
"@adtention/sdk": "^0.4.0",
|
|
42
43
|
"ink": "^7.1.1",
|
|
43
|
-
"react": "^19.2.8"
|
|
44
|
+
"react": "^19.2.8",
|
|
45
|
+
"slice-ansi": "^9.0.0",
|
|
46
|
+
"string-width": "^8.2.0",
|
|
47
|
+
"wrap-ansi": "^10.0.0"
|
|
44
48
|
}
|
|
45
49
|
}
|