clixad 0.0.1-beta.12 → 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 +503 -188
- package/package.json +6 -2
package/dist/clixad.mjs
CHANGED
|
@@ -385,25 +385,77 @@ var init_client = __esm({
|
|
|
385
385
|
* credits, and this program is installed by strangers from npm. All the CLI
|
|
386
386
|
* gets to decide is when to ask.
|
|
387
387
|
*/
|
|
388
|
-
|
|
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) {
|
|
389
403
|
try {
|
|
390
|
-
const res = await fetch(`${this.config.gatewayUrl}/v1/sponsor/
|
|
404
|
+
const res = await fetch(`${this.config.gatewayUrl}/v1/sponsor/config`, {
|
|
391
405
|
headers: this.headers(),
|
|
392
|
-
signal
|
|
406
|
+
...signal ? { signal } : {}
|
|
393
407
|
});
|
|
394
408
|
if (!res.ok) return null;
|
|
395
409
|
const body = await res.json();
|
|
396
|
-
|
|
410
|
+
const publisherId = typeof body.publisher_id === "string" ? body.publisher_id.trim() : "";
|
|
411
|
+
if (!publisherId) return null;
|
|
397
412
|
return {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
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 } : {}
|
|
402
417
|
};
|
|
403
418
|
} catch {
|
|
404
419
|
return null;
|
|
405
420
|
}
|
|
406
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
|
+
}
|
|
407
459
|
/**
|
|
408
460
|
* Search the web, through the gateway.
|
|
409
461
|
*
|
|
@@ -3661,6 +3713,8 @@ If it's installed under a different name/path, set CLIXAD_KIMI_BIN.`;
|
|
|
3661
3713
|
|
|
3662
3714
|
// src/banner.ts
|
|
3663
3715
|
import { homedir as homedir6 } from "node:os";
|
|
3716
|
+
import sliceAnsi from "slice-ansi";
|
|
3717
|
+
import stringWidth from "string-width";
|
|
3664
3718
|
function duckLines() {
|
|
3665
3719
|
const lines = [];
|
|
3666
3720
|
for (let row = 0; row < DUCK.length; row += 2) {
|
|
@@ -3744,22 +3798,29 @@ function welcomeBanner(opts) {
|
|
|
3744
3798
|
return out.join("\n");
|
|
3745
3799
|
}
|
|
3746
3800
|
function compactBanner(opts) {
|
|
3747
|
-
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));
|
|
3748
3803
|
const hint = bannerTip(opts.tipIndex);
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
${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}
|
|
3753
3811
|
`;
|
|
3754
3812
|
}
|
|
3755
|
-
function clearScreen() {
|
|
3756
|
-
|
|
3757
|
-
if (!out.isTTY && !process.stdin.isTTY) return;
|
|
3813
|
+
function clearScreen(out = process.stdout) {
|
|
3814
|
+
if (!out.isTTY) return;
|
|
3758
3815
|
out.write("\n".repeat(out.rows ?? 40));
|
|
3759
3816
|
out.write("\x1B[2J\x1B[3J\x1B[H\x1B[0f");
|
|
3760
3817
|
}
|
|
3761
3818
|
function visibleLength(s) {
|
|
3762
|
-
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);
|
|
3763
3824
|
}
|
|
3764
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;
|
|
3765
3826
|
var init_banner = __esm({
|
|
@@ -3802,7 +3863,7 @@ var init_banner = __esm({
|
|
|
3802
3863
|
DUCK_W = 14;
|
|
3803
3864
|
cell = (text, w) => ({ text, w });
|
|
3804
3865
|
padTo = (c2, width) => (c2?.text ?? "") + " ".repeat(Math.max(0, width - (c2?.w ?? 0)));
|
|
3805
|
-
truncate = (s, max) => s
|
|
3866
|
+
truncate = (s, max) => stringWidth(s) > max ? sliceColumns(s, Math.max(0, max - 1)) + "\u2026" : s;
|
|
3806
3867
|
BANNER_TIPS = [
|
|
3807
3868
|
{ key: "/init", desc: "make a CLIXAD.md" },
|
|
3808
3869
|
{ key: "shift+tab", desc: "switch mode" },
|
|
@@ -3824,6 +3885,45 @@ var init_banner = __esm({
|
|
|
3824
3885
|
}
|
|
3825
3886
|
});
|
|
3826
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
|
+
|
|
3827
3927
|
// src/version.ts
|
|
3828
3928
|
import { readFileSync as readFileSync6 } from "node:fs";
|
|
3829
3929
|
function readVersion() {
|
|
@@ -4050,7 +4150,7 @@ function adtentionSource(fetchLine) {
|
|
|
4050
4150
|
if (!line2) return null;
|
|
4051
4151
|
const text = line2.text.trim();
|
|
4052
4152
|
if (!text) return null;
|
|
4053
|
-
return {
|
|
4153
|
+
return { ...line2, text };
|
|
4054
4154
|
}
|
|
4055
4155
|
};
|
|
4056
4156
|
}
|
|
@@ -4074,19 +4174,98 @@ function sponsorEnabled({ isTTY, env, configEnabled }) {
|
|
|
4074
4174
|
if (env.CI !== void 0 && env.CI !== "") return false;
|
|
4075
4175
|
return true;
|
|
4076
4176
|
}
|
|
4077
|
-
function
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
const
|
|
4083
|
-
|
|
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;
|
|
4084
4193
|
}
|
|
4085
|
-
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;
|
|
4086
4195
|
var init_render = __esm({
|
|
4087
4196
|
"src/sponsor/render.ts"() {
|
|
4088
4197
|
"use strict";
|
|
4089
|
-
|
|
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;
|
|
4090
4269
|
}
|
|
4091
4270
|
});
|
|
4092
4271
|
|
|
@@ -4450,48 +4629,42 @@ var init_markdown = __esm({
|
|
|
4450
4629
|
// src/tui/views.tsx
|
|
4451
4630
|
import "react";
|
|
4452
4631
|
import { Box, Text } from "ink";
|
|
4632
|
+
import wrapAnsi from "wrap-ansi";
|
|
4453
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
|
+
}
|
|
4454
4637
|
function lineCount(text, cols) {
|
|
4455
|
-
return text
|
|
4638
|
+
return wrapRows(text, cols).length;
|
|
4456
4639
|
}
|
|
4457
4640
|
function tailRows(text, cols, max) {
|
|
4458
4641
|
if (max <= 0) return "";
|
|
4459
|
-
const
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
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);
|
|
4476
4662
|
}
|
|
4477
4663
|
function headRows(text, cols, max) {
|
|
4478
4664
|
if (max <= 0) return "";
|
|
4479
|
-
const
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
let used = 0;
|
|
4483
|
-
for (const line2 of lines) {
|
|
4484
|
-
const h = Math.max(1, Math.ceil(visibleLength(line2) / width));
|
|
4485
|
-
if (used + h <= max) {
|
|
4486
|
-
kept.push(line2);
|
|
4487
|
-
used += h;
|
|
4488
|
-
continue;
|
|
4489
|
-
}
|
|
4490
|
-
const room = max - used;
|
|
4491
|
-
if (room > 0) kept.push(line2.slice(0, room * width));
|
|
4492
|
-
break;
|
|
4493
|
-
}
|
|
4494
|
-
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");
|
|
4495
4668
|
}
|
|
4496
4669
|
function errorNotice(message, rows = ERROR_NOTICE_ROWS) {
|
|
4497
4670
|
const clean = message.replace(/\u001b\][^\u0007]*(?:\u0007|\u001b\\)?/g, " ").replace(/\u001b\[[0-9;?]*[ -\/]*[@-~]/g, " ").replace(/[\u0000-\u0009\u000b-\u001f\u007f-\u009f]/g, " ");
|
|
@@ -4546,7 +4719,7 @@ function EntryView({ entry }) {
|
|
|
4546
4719
|
) });
|
|
4547
4720
|
}
|
|
4548
4721
|
}
|
|
4549
|
-
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;
|
|
4550
4723
|
var init_views = __esm({
|
|
4551
4724
|
"src/tui/views.tsx"() {
|
|
4552
4725
|
"use strict";
|
|
@@ -4555,37 +4728,52 @@ var init_views = __esm({
|
|
|
4555
4728
|
MANGO3 = "#f5b841";
|
|
4556
4729
|
MANGO_BRIGHT = "#ffcf6b";
|
|
4557
4730
|
SLATE = "#8b98ae";
|
|
4731
|
+
AD_TEXT = "#8a80b4";
|
|
4732
|
+
AD_MARK = "#6b6390";
|
|
4558
4733
|
MODE_STYLE = {
|
|
4559
4734
|
normal: { color: SLATE, glyph: "\u23F5" },
|
|
4560
4735
|
acceptEdits: { color: "#7ee787", glyph: "\u23F5\u23F5" },
|
|
4561
4736
|
plan: { color: "#79c0ff", glyph: "\u23F8" }
|
|
4562
4737
|
};
|
|
4738
|
+
WRAP_LIKE_INK = { trim: false, hard: true };
|
|
4739
|
+
DIALOG_CHROME_COLS = 4;
|
|
4563
4740
|
ERROR_NOTICE_ROWS = 12;
|
|
4564
4741
|
ERROR_NOTICE_COLS = 200;
|
|
4565
4742
|
}
|
|
4566
4743
|
});
|
|
4567
4744
|
|
|
4568
4745
|
// src/tui/app.tsx
|
|
4569
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4746
|
+
import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
4570
4747
|
import { Box as Box2, Static, Text as Text2, useApp, useInput, usePaste, useStdout } from "ink";
|
|
4571
4748
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
4572
4749
|
function useTerminalSize() {
|
|
4573
|
-
const
|
|
4574
|
-
|
|
4575
|
-
|
|
4750
|
+
const { stdout } = useStdout();
|
|
4751
|
+
const snapshot2 = useRef({
|
|
4752
|
+
rows: stdout.rows ?? FALLBACK_ROWS,
|
|
4753
|
+
cols: stdout.columns ?? FALLBACK_COLS
|
|
4576
4754
|
});
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
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);
|
|
4585
4773
|
}
|
|
4586
|
-
function App({ client, config, wallet, session, initialTask }) {
|
|
4774
|
+
function App({ client, config, wallet, session, initialTask, sponsorServe }) {
|
|
4587
4775
|
const { exit } = useApp();
|
|
4588
|
-
const { write: writeToStdout } = useStdout();
|
|
4776
|
+
const { stdout, write: writeToStdout } = useStdout();
|
|
4589
4777
|
const idRef = useRef(1);
|
|
4590
4778
|
const { rows, cols } = useTerminalSize();
|
|
4591
4779
|
const root = process.cwd();
|
|
@@ -4593,8 +4781,12 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
4593
4781
|
{
|
|
4594
4782
|
id: 0,
|
|
4595
4783
|
kind: "banner",
|
|
4596
|
-
|
|
4597
|
-
|
|
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,
|
|
4598
4790
|
name: config.login ?? config.email?.split("@")[0],
|
|
4599
4791
|
model: config.model,
|
|
4600
4792
|
balance: wallet?.balance ?? 0,
|
|
@@ -4658,7 +4850,37 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
4658
4850
|
const sponsorIdxRef = useRef(0);
|
|
4659
4851
|
const sponsorPrevRef = useRef(void 0);
|
|
4660
4852
|
const sponsorCallRef = useRef(0);
|
|
4661
|
-
const
|
|
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
|
+
);
|
|
4662
4884
|
const tallyRef = useRef(createTally());
|
|
4663
4885
|
const heightCacheRef = useRef(/* @__PURE__ */ new Map());
|
|
4664
4886
|
const push = useCallback((e) => {
|
|
@@ -4748,7 +4970,7 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
4748
4970
|
(modelId) => {
|
|
4749
4971
|
const call = ++sponsorCallRef.current;
|
|
4750
4972
|
if (!sponsorEnabled({
|
|
4751
|
-
isTTY:
|
|
4973
|
+
isTTY: stdout.isTTY,
|
|
4752
4974
|
env: process.env,
|
|
4753
4975
|
configEnabled: config.sponsor
|
|
4754
4976
|
})) {
|
|
@@ -4764,19 +4986,28 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
4764
4986
|
if (picked) {
|
|
4765
4987
|
sponsorPrevRef.current = picked.id;
|
|
4766
4988
|
recordImpression(tallyRef.current, picked.id);
|
|
4767
|
-
setSponsor(picked.text);
|
|
4989
|
+
setSponsor({ text: picked.text, origin: "house" });
|
|
4768
4990
|
} else {
|
|
4769
4991
|
setSponsor(null);
|
|
4770
4992
|
}
|
|
4993
|
+
if (!adFitsWidth(colsRef.current)) return;
|
|
4771
4994
|
void remoteSponsorRef.current.fetch().then((line2) => {
|
|
4772
4995
|
if (!line2 || call !== sponsorCallRef.current) return;
|
|
4996
|
+
if (!sponsorRow(line2.text, colsRef.current, "network")) return;
|
|
4773
4997
|
sponsorPrevRef.current = line2.id;
|
|
4774
4998
|
recordImpression(tallyRef.current, line2.id);
|
|
4775
|
-
setSponsor(line2.text);
|
|
4999
|
+
setSponsor({ text: line2.text, origin: "network" });
|
|
5000
|
+
pendingImpressionRef.current = { call, impression: line2.impression };
|
|
4776
5001
|
});
|
|
4777
5002
|
},
|
|
4778
|
-
[config.sponsor, wallet]
|
|
5003
|
+
[config.sponsor, stdout, wallet]
|
|
4779
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]);
|
|
4780
5011
|
const askUser = useCallback(
|
|
4781
5012
|
(req) => new Promise((resolve2) => setAsk({ req, resolve: resolve2 })),
|
|
4782
5013
|
[]
|
|
@@ -4861,7 +5092,7 @@ function App({ client, config, wallet, session, initialTask }) {
|
|
|
4861
5092
|
return;
|
|
4862
5093
|
}
|
|
4863
5094
|
const lines = event.result.split("\n").filter((l) => l.trim() !== "");
|
|
4864
|
-
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)));
|
|
4865
5096
|
const more = Math.max(0, lines.length - shown.length);
|
|
4866
5097
|
const id = push({
|
|
4867
5098
|
kind: "tool",
|
|
@@ -5000,6 +5231,7 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
5000
5231
|
dropDelta();
|
|
5001
5232
|
setLive(null);
|
|
5002
5233
|
setBusy(false);
|
|
5234
|
+
reportSponsor();
|
|
5003
5235
|
sponsorCallRef.current++;
|
|
5004
5236
|
setSponsor(null);
|
|
5005
5237
|
}
|
|
@@ -5012,6 +5244,7 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
5012
5244
|
dropDelta,
|
|
5013
5245
|
handleEvent,
|
|
5014
5246
|
model,
|
|
5247
|
+
reportSponsor,
|
|
5015
5248
|
nextSponsor,
|
|
5016
5249
|
permit,
|
|
5017
5250
|
push,
|
|
@@ -5330,7 +5563,14 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
5330
5563
|
push({
|
|
5331
5564
|
kind: "notice",
|
|
5332
5565
|
text: list.map(
|
|
5333
|
-
(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
|
+
)
|
|
5334
5574
|
).join("\n").concat("\n /bg kill <id> \xB7 /bg kill all")
|
|
5335
5575
|
});
|
|
5336
5576
|
return;
|
|
@@ -5384,7 +5624,7 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
5384
5624
|
{ command }
|
|
5385
5625
|
);
|
|
5386
5626
|
const lines = output.split("\n").filter((l) => l.trim() !== "");
|
|
5387
|
-
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)));
|
|
5388
5628
|
const more = Math.max(0, lines.length - shown.length);
|
|
5389
5629
|
const id = push({
|
|
5390
5630
|
kind: "tool",
|
|
@@ -5624,36 +5864,82 @@ ${dropped.map((l) => ` \xB7 ${l}`).join("\n")}`
|
|
|
5624
5864
|
});
|
|
5625
5865
|
const elapsed = busy && startedAt ? Math.floor((Date.now() - startedAt) / 1e3) : 0;
|
|
5626
5866
|
const spinner = spinnerRef.current[Math.floor(tick / SPINNER_TICKS_PER_FRAME) % spinnerRef.current.length];
|
|
5627
|
-
const sponsorLine = busy && sponsor ?
|
|
5628
|
-
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);
|
|
5629
5870
|
const inputRows = Math.max(1, Math.min(MAX_INPUT_ROWS, editor.lines.length));
|
|
5630
5871
|
const inputFrom = windowStart(editor.row, editor.lines.length, inputRows);
|
|
5631
|
-
const
|
|
5632
|
-
|
|
5633
|
-
...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, " ")),
|
|
5634
5874
|
...queueView.length > MAX_QUEUE_ROWS ? [`+${queueView.length - MAX_QUEUE_ROWS} more queued`] : []
|
|
5635
|
-
];
|
|
5636
|
-
const
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
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;
|
|
5640
5895
|
const pickerFrom = picker ? windowStart(pickerSel, picker.items.length, pickerRows) : 0;
|
|
5641
5896
|
const pickerItems = picker ? picker.items.slice(pickerFrom, pickerFrom + pickerRows) : [];
|
|
5642
|
-
const pickerHeight = picker ? pickerItems.length +
|
|
5897
|
+
const pickerHeight = picker ? pickerItems.length + pickerChrome : 0;
|
|
5643
5898
|
pickerWindowRef.current = { from: pickerFrom, count: pickerItems.length };
|
|
5644
|
-
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
|
+
) : "";
|
|
5645
5934
|
budget -= pickerHeight;
|
|
5646
|
-
const
|
|
5647
|
-
const askSummaryRows = ask2 ? lineCount(
|
|
5648
|
-
const askPreview = ask2?.req.preview ? headRows(ask2.req.preview,
|
|
5649
|
-
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;
|
|
5650
5939
|
budget -= askHeight;
|
|
5651
|
-
const
|
|
5652
|
-
const
|
|
5653
|
-
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;
|
|
5654
5942
|
budget -= planHeight;
|
|
5655
|
-
const busyHeight = busy ? 2 : 0;
|
|
5656
|
-
budget -= busyHeight;
|
|
5657
5943
|
const liveRaw = [
|
|
5658
5944
|
// Trailing blank lines are rows the clamp would spend on nothing, and a
|
|
5659
5945
|
// streamed answer ends on one more often than not.
|
|
@@ -5677,7 +5963,10 @@ ${dropped.map((l) => ` \xB7 ${l}`).join("\n")}`
|
|
|
5677
5963
|
}, [entries, cols]);
|
|
5678
5964
|
const used = chromeHeight + menu.length + pickerHeight + askHeight + planHeight + busyHeight + liveHeight;
|
|
5679
5965
|
const spacer = Math.max(0, viewport - printed - used);
|
|
5680
|
-
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
|
+
);
|
|
5681
5970
|
const contextPct = useMemo(() => {
|
|
5682
5971
|
const window = contextWindow(model);
|
|
5683
5972
|
if (!window) return 0;
|
|
@@ -5687,81 +5976,86 @@ ${dropped.map((l) => ` \xB7 ${l}`).join("\n")}`
|
|
|
5687
5976
|
]);
|
|
5688
5977
|
return used2 / window;
|
|
5689
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} `)));
|
|
5690
5997
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
5691
5998
|
/* @__PURE__ */ jsx2(Static, { items: entries, children: (entry) => /* @__PURE__ */ jsx2(EntryView, { entry }, entry.id) }, staticEpoch),
|
|
5692
5999
|
spacer > 0 ? /* @__PURE__ */ jsx2(Box2, { height: spacer }) : null,
|
|
5693
6000
|
liveBlock ? /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(Text2, { children: liveBlock }) }) : null,
|
|
5694
|
-
|
|
6001
|
+
busyHeight > 0 ? /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, children: [
|
|
5695
6002
|
/* @__PURE__ */ jsxs2(Text2, { color: MANGO3, children: [
|
|
5696
6003
|
spinner,
|
|
5697
6004
|
" "
|
|
5698
6005
|
] }),
|
|
5699
|
-
/* @__PURE__ */
|
|
5700
|
-
busyLabel,
|
|
5701
|
-
" ",
|
|
5702
|
-
elapsed,
|
|
5703
|
-
"s \xB7 esc to stop"
|
|
5704
|
-
] })
|
|
6006
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, wrap: "truncate-end", children: busyText })
|
|
5705
6007
|
] }) : null,
|
|
5706
6008
|
ask2 ? /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
5707
|
-
/* @__PURE__ */ jsx2(Text2, { bold: true, color: "yellow", children:
|
|
6009
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, color: "yellow", children: askSummary }),
|
|
5708
6010
|
askPreview ? /* @__PURE__ */ jsx2(Text2, { children: askPreview }) : null,
|
|
5709
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true,
|
|
6011
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, wrap: "truncate-end", children: pickHint(ASK_HINT, inner) })
|
|
5710
6012
|
] }) : null,
|
|
5711
6013
|
plan ? /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: MODE_STYLE.plan.color, paddingX: 1, children: [
|
|
5712
|
-
/* @__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) }),
|
|
5713
6015
|
/* @__PURE__ */ jsx2(Box2, { height: 1 }),
|
|
5714
|
-
/* @__PURE__ */ jsx2(Text2, { children:
|
|
5715
|
-
/* @__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) })
|
|
5716
6018
|
] }) : null,
|
|
5717
6019
|
picker ? /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: MANGO3, paddingX: 1, children: [
|
|
5718
|
-
/* @__PURE__ */ jsx2(Text2, { bold: true, color: MANGO_BRIGHT, children:
|
|
5719
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children:
|
|
5720
|
-
/* @__PURE__ */ jsx2(Box2, { height: 1 }),
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
] }),
|
|
5729
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: item.hint }),
|
|
5730
|
-
item.current ? /* @__PURE__ */ jsx2(Text2, { color: "green", children: " \u2190 current" }) : null
|
|
5731
|
-
] }, item.value);
|
|
5732
|
-
}),
|
|
5733
|
-
/* @__PURE__ */ jsx2(Box2, { height: 1 }),
|
|
5734
|
-
/* @__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 })
|
|
5735
6030
|
] }) : null,
|
|
5736
6031
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: 1, children: [
|
|
5737
|
-
queueRows.map((line2, i) => /* @__PURE__ */ jsx2(Text2, { dimColor: true, children:
|
|
5738
|
-
sponsorLine ? /* @__PURE__ */
|
|
5739
|
-
|
|
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: [
|
|
5740
6046
|
/* @__PURE__ */ jsx2(Text2, { color: MANGO_BRIGHT, children: "\u276F " }),
|
|
5741
6047
|
/* @__PURE__ */ jsx2(Text2, { children: renderInput(editor, inputFrom, inputRows) })
|
|
5742
|
-
] }),
|
|
5743
|
-
menu.map((item, i) => /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
5744
|
-
/* @__PURE__ */ jsxs2(Text2, { color: i === sel ? MANGO_BRIGHT : MANGO3, bold: i === sel, children: [
|
|
5745
|
-
i === sel ? " \u276F " : " ",
|
|
5746
|
-
item.label.padEnd(labelW + 2)
|
|
5747
|
-
] }),
|
|
5748
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: i !== sel, children: item.hint })
|
|
5749
|
-
] }, item.value)),
|
|
5750
|
-
quitHint || menu.length > 0 ? /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
5751
|
-
" ",
|
|
5752
|
-
quitHint ? "press ctrl+c again to quit" : "\u2191\u2193 choose \xB7 \u23CE run \xB7 tab complete \xB7 esc close"
|
|
5753
6048
|
] }) : /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
5754
|
-
/* @__PURE__ */ jsx2(Text2, { color:
|
|
5755
|
-
/* @__PURE__ */
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
] })
|
|
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 })
|
|
5765
6059
|
] })
|
|
5766
6060
|
] })
|
|
5767
6061
|
] });
|
|
@@ -5793,17 +6087,23 @@ function renderInput(state, from, rows) {
|
|
|
5793
6087
|
] }, row);
|
|
5794
6088
|
});
|
|
5795
6089
|
}
|
|
5796
|
-
function statusLine(o) {
|
|
6090
|
+
function statusLine(o, cols) {
|
|
5797
6091
|
const burn = o.spent > 0 && o.minutes >= 1 ? `${Math.round(o.spent / o.minutes).toLocaleString("en-US")} cr/min` : void 0;
|
|
5798
|
-
const
|
|
5799
|
-
o.model,
|
|
5800
|
-
`${o.balance.toLocaleString("en-US")} cr`,
|
|
5801
|
-
o.spent > 0 ? `\u2212${o.spent.toLocaleString("en-US")}` : void 0,
|
|
5802
|
-
burn,
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
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);
|
|
5807
6107
|
}
|
|
5808
6108
|
function windowStart(sel, total, size) {
|
|
5809
6109
|
if (total <= size) return 0;
|
|
@@ -5854,7 +6154,7 @@ function tailLines(text, max) {
|
|
|
5854
6154
|
const lines = text.split("\n");
|
|
5855
6155
|
return lines.length <= max ? text : lines.slice(-max).join("\n");
|
|
5856
6156
|
}
|
|
5857
|
-
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;
|
|
5858
6158
|
var init_app = __esm({
|
|
5859
6159
|
"src/tui/app.tsx"() {
|
|
5860
6160
|
"use strict";
|
|
@@ -5878,6 +6178,7 @@ var init_app = __esm({
|
|
|
5878
6178
|
init_session();
|
|
5879
6179
|
init_counter();
|
|
5880
6180
|
init_provider();
|
|
6181
|
+
init_network();
|
|
5881
6182
|
init_render();
|
|
5882
6183
|
init_commands();
|
|
5883
6184
|
init_editor();
|
|
@@ -5897,8 +6198,28 @@ var init_app = __esm({
|
|
|
5897
6198
|
MAX_INPUT_ROWS = 10;
|
|
5898
6199
|
MAX_PICKER_ROWS = 12;
|
|
5899
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
|
+
];
|
|
5900
6218
|
EXPAND_MAX_LINES = 400;
|
|
6219
|
+
FALLBACK_ROWS = 24;
|
|
6220
|
+
FALLBACK_COLS = 80;
|
|
5901
6221
|
CONTEXT_NOTICE_AT = 0.6;
|
|
6222
|
+
KEEP_ALWAYS = Number.POSITIVE_INFINITY;
|
|
5902
6223
|
}
|
|
5903
6224
|
});
|
|
5904
6225
|
|
|
@@ -5940,6 +6261,7 @@ init_session();
|
|
|
5940
6261
|
init_kimi();
|
|
5941
6262
|
init_banner();
|
|
5942
6263
|
init_browser();
|
|
6264
|
+
init_color();
|
|
5943
6265
|
|
|
5944
6266
|
// src/title.ts
|
|
5945
6267
|
var APP_TITLE = "Clixad";
|
|
@@ -5969,14 +6291,7 @@ function clearTerminalTitle(out = titleStream()) {
|
|
|
5969
6291
|
|
|
5970
6292
|
// src/main.ts
|
|
5971
6293
|
init_version();
|
|
5972
|
-
var c = {
|
|
5973
|
-
cyan: (s) => `\x1B[36m${s}\x1B[0m`,
|
|
5974
|
-
green: (s) => `\x1B[32m${s}\x1B[0m`,
|
|
5975
|
-
yellow: (s) => `\x1B[33m${s}\x1B[0m`,
|
|
5976
|
-
red: (s) => `\x1B[31m${s}\x1B[0m`,
|
|
5977
|
-
dim: (s) => `\x1B[2m${s}\x1B[0m`,
|
|
5978
|
-
bold: (s) => `\x1B[1m${s}\x1B[0m`
|
|
5979
|
-
};
|
|
6294
|
+
var c = palette({ isTTY: process.stdout.isTTY, env: process.env });
|
|
5980
6295
|
async function main() {
|
|
5981
6296
|
setTerminalTitle();
|
|
5982
6297
|
process.on("exit", () => clearTerminalTitle());
|
|
@@ -6044,10 +6359,10 @@ async function login(client, config, args) {
|
|
|
6044
6359
|
console.log(`
|
|
6045
6360
|
Open ${c.cyan(start.verification_uri)} and enter code: ${c.bold(start.user_code)}
|
|
6046
6361
|
`);
|
|
6047
|
-
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)"));
|
|
6048
6363
|
},
|
|
6049
6364
|
tick() {
|
|
6050
|
-
process.stdout.write(c.dim("."));
|
|
6365
|
+
if (c.enabled) process.stdout.write(c.dim("."));
|
|
6051
6366
|
}
|
|
6052
6367
|
});
|
|
6053
6368
|
switch (outcome.status) {
|
|
@@ -6172,11 +6487,11 @@ async function earn(client, config) {
|
|
|
6172
6487
|
console.log(
|
|
6173
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.")
|
|
6174
6489
|
);
|
|
6175
|
-
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"));
|
|
6176
6491
|
const deadline = Date.now() + 5 * 60 * 1e3;
|
|
6177
6492
|
while (Date.now() < deadline) {
|
|
6178
6493
|
await sleep4(3e3);
|
|
6179
|
-
process.stdout.write(c.dim("."));
|
|
6494
|
+
if (c.enabled) process.stdout.write(c.dim("."));
|
|
6180
6495
|
const balance = (await safeWallet(client))?.balance ?? before;
|
|
6181
6496
|
if (balance > before) {
|
|
6182
6497
|
console.log(
|
|
@@ -6216,11 +6531,11 @@ async function buyCmd(client, config, pack) {
|
|
|
6216
6531
|
Stripe Checkout: ${c.bold(checkout.pack)} \u2014 ${checkout.credits.toLocaleString("en-US")} credits for ${c.bold("$" + checkout.price_usd.toFixed(2))}`);
|
|
6217
6532
|
console.log(c.dim(` ${checkout.url}`));
|
|
6218
6533
|
openBrowser(checkout.url);
|
|
6219
|
-
process.stdout.write(c.dim(" waiting for payment to clear"));
|
|
6534
|
+
if (c.enabled) process.stdout.write(c.dim(" waiting for payment to clear"));
|
|
6220
6535
|
const deadline = Date.now() + 5 * 60 * 1e3;
|
|
6221
6536
|
while (Date.now() < deadline) {
|
|
6222
6537
|
await sleep4(3e3);
|
|
6223
|
-
process.stdout.write(c.dim("."));
|
|
6538
|
+
if (c.enabled) process.stdout.write(c.dim("."));
|
|
6224
6539
|
const balance = (await safeWallet(client))?.balance ?? before;
|
|
6225
6540
|
if (balance > before) {
|
|
6226
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
|
}
|