@i14ks/ccv 0.4.1 → 0.6.0
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/bundle/ccv.mjs +412 -32
- package/package.json +1 -1
- package/web/assets/{codemirror-ixGO-cbX.js → codemirror-DILVW5V7.js} +1 -1
- package/web/assets/index-5NnNKmcv.js +129 -0
- package/web/assets/{index-D1EGRMsF.js → index-BTNk6BN3.js} +1 -1
- package/web/assets/{index-_Y-EJULt.js → index-BjUFOQlC.js} +1 -1
- package/web/assets/index-Brn_uUCW.css +1 -0
- package/web/assets/{index-BuniMO1x.js → index-C2x9TM0V.js} +1 -1
- package/web/assets/{index-DqcI6biV.js → index-C41Hz81g.js} +1 -1
- package/web/assets/{index-Byl4r_xt.js → index-CC6apbYr.js} +1 -1
- package/web/assets/{index-EaPgYsZb.js → index-CP6Bv1lU.js} +1 -1
- package/web/assets/{index-D3avVSzn.js → index-CSzD2Fvc.js} +1 -1
- package/web/assets/{index-EHaCUl16.js → index-CVhjeQrh.js} +1 -1
- package/web/assets/{index-u0GQ-BLb.js → index-Cp4y8ln8.js} +1 -1
- package/web/assets/{index-GQ3tJswl.js → index-Cptzajqg.js} +1 -1
- package/web/assets/{index-CXiddYxZ.js → index-D18_1PH5.js} +1 -1
- package/web/assets/{index-CzWhd9WN.js → index-D2sBuEGv.js} +1 -1
- package/web/assets/{index-D2QcAee0.js → index-DJ1r3yCI.js} +1 -1
- package/web/assets/{index-DcGZZXlE.js → index-DJhubbAh.js} +1 -1
- package/web/assets/{index-CELwK_ze.js → index-Dr-2FTDh.js} +1 -1
- package/web/assets/{index-DAN-BZ4M.js → index-DuJE332D.js} +1 -1
- package/web/assets/{index-CtUwaQ4R.js → index-fBBR-7pG.js} +1 -1
- package/web/assets/{index-CbrZsnLR.js → index-qQ4Nk5pP.js} +1 -1
- package/web/assets/{index-jausLDAl.js → index-zZliJVYN.js} +1 -1
- package/web/index.html +2 -2
- package/web/assets/index-CQJq2xwX.css +0 -1
- package/web/assets/index-VtR2fk7J.js +0 -128
package/bundle/ccv.mjs
CHANGED
|
@@ -215,7 +215,7 @@ var AsyncQueue = class {
|
|
|
215
215
|
};
|
|
216
216
|
|
|
217
217
|
// ../host/dist/files.js
|
|
218
|
-
import { lstat, mkdir, readFile as readFile2, readdir, realpath, rename as renamePath, rm, stat, writeFile } from "node:fs/promises";
|
|
218
|
+
import { cp, lstat, mkdir, readFile as readFile2, readdir, realpath, rename as renamePath, rm, stat, writeFile } from "node:fs/promises";
|
|
219
219
|
import path3 from "node:path";
|
|
220
220
|
var MAX_FILE = 4 * 1024 * 1024;
|
|
221
221
|
var SNIFF = 8192;
|
|
@@ -353,9 +353,12 @@ var ProjectFiles = class {
|
|
|
353
353
|
if (!info || info.size > MAX_FILE)
|
|
354
354
|
continue;
|
|
355
355
|
const buffer = await readFile2(full).catch(() => null);
|
|
356
|
-
if (!buffer
|
|
356
|
+
if (!buffer)
|
|
357
357
|
continue;
|
|
358
|
-
const
|
|
358
|
+
const decoded = decode(buffer);
|
|
359
|
+
if (!decoded)
|
|
360
|
+
continue;
|
|
361
|
+
const lines = decoded.text.split(/\r\n|\n/);
|
|
359
362
|
for (let i = 0; i < lines.length; i++) {
|
|
360
363
|
const line = lines[i];
|
|
361
364
|
const lower = line.toLowerCase();
|
|
@@ -400,6 +403,7 @@ var ProjectFiles = class {
|
|
|
400
403
|
path: normalize(relative),
|
|
401
404
|
content: "",
|
|
402
405
|
eol: "lf",
|
|
406
|
+
encoding: "utf8",
|
|
403
407
|
mtime: Math.round(info.mtimeMs),
|
|
404
408
|
size: info.size,
|
|
405
409
|
binary: false,
|
|
@@ -408,14 +412,15 @@ var ProjectFiles = class {
|
|
|
408
412
|
if (info.size > MAX_FILE)
|
|
409
413
|
return { ...blank, tooLarge: true };
|
|
410
414
|
const buffer = await readFile2(file);
|
|
411
|
-
|
|
415
|
+
const decoded = decode(buffer);
|
|
416
|
+
if (!decoded)
|
|
412
417
|
return { ...blank, binary: true };
|
|
413
|
-
const
|
|
414
|
-
const crlf = text.includes("\r\n");
|
|
418
|
+
const crlf = decoded.text.includes("\r\n");
|
|
415
419
|
return {
|
|
416
420
|
...blank,
|
|
417
|
-
content: crlf ? text.replace(/\r\n/g, "\n") : text,
|
|
418
|
-
eol: crlf ? "crlf" : "lf"
|
|
421
|
+
content: crlf ? decoded.text.replace(/\r\n/g, "\n") : decoded.text,
|
|
422
|
+
eol: crlf ? "crlf" : "lf",
|
|
423
|
+
encoding: decoded.encoding
|
|
419
424
|
};
|
|
420
425
|
}
|
|
421
426
|
/**
|
|
@@ -426,7 +431,7 @@ var ProjectFiles = class {
|
|
|
426
431
|
* молчаливая перезапись стёрла бы его работу. `force` снимает проверку —
|
|
427
432
|
* это сознательное решение пользователя, принятое после отказа.
|
|
428
433
|
*/
|
|
429
|
-
async write(relative, content, eol, baseMtime, force = false) {
|
|
434
|
+
async write(relative, content, eol, encoding, baseMtime, force = false) {
|
|
430
435
|
const file = await this.#resolve(relative);
|
|
431
436
|
const info = await stat(file).catch(() => null);
|
|
432
437
|
if (info?.isDirectory())
|
|
@@ -437,7 +442,7 @@ var ProjectFiles = class {
|
|
|
437
442
|
throw new ConflictError(info ? "\u0424\u0430\u0439\u043B \u043D\u0430 \u0434\u0438\u0441\u043A\u0435 \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0441\u044F \u043F\u043E\u0441\u043B\u0435 \u0442\u043E\u0433\u043E, \u043A\u0430\u043A \u0435\u0433\u043E \u043E\u0442\u043A\u0440\u044B\u043B\u0438 \u0432 \u0440\u0435\u0434\u0430\u043A\u0442\u043E\u0440\u0435" : "\u0424\u0430\u0439\u043B \u043D\u0430 \u0434\u0438\u0441\u043A\u0435 \u0438\u0441\u0447\u0435\u0437 \u043F\u043E\u0441\u043B\u0435 \u0442\u043E\u0433\u043E, \u043A\u0430\u043A \u0435\u0433\u043E \u043E\u0442\u043A\u0440\u044B\u043B\u0438 \u0432 \u0440\u0435\u0434\u0430\u043A\u0442\u043E\u0440\u0435");
|
|
438
443
|
}
|
|
439
444
|
}
|
|
440
|
-
await writeFile(file,
|
|
445
|
+
await writeFile(file, encode(content, eol, encoding));
|
|
441
446
|
const saved = await stat(file);
|
|
442
447
|
return { mtime: Math.round(saved.mtimeMs), size: saved.size };
|
|
443
448
|
}
|
|
@@ -462,6 +467,40 @@ var ProjectFiles = class {
|
|
|
462
467
|
await mkdir(path3.dirname(target), { recursive: true });
|
|
463
468
|
await renamePath(from, target);
|
|
464
469
|
}
|
|
470
|
+
/**
|
|
471
|
+
* Скопировать файл или каталог внутрь `toDir`; возвращает путь копии.
|
|
472
|
+
*
|
|
473
|
+
* Имя подбирает хост, а не клиент: обычная вставка — рядом с оригиналом, в
|
|
474
|
+
* тот же каталог, и там исходное имя занято. Свободное имя ищется перебором
|
|
475
|
+
* (`имя copy`, `имя copy 2`, …) — так же, как это делает проводник; на
|
|
476
|
+
* гонку между проверкой и копированием это не рассчитано, но `cp` с
|
|
477
|
+
* `errorOnExist` в ней откажет, а не затрёт чужое.
|
|
478
|
+
*/
|
|
479
|
+
async copy(relative, toDir) {
|
|
480
|
+
const source = normalize(relative);
|
|
481
|
+
if (!source)
|
|
482
|
+
throw new Error("\u041A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043A\u043E\u0440\u0435\u043D\u044C \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u043D\u0435\u043B\u044C\u0437\u044F");
|
|
483
|
+
const from = await this.#resolve(source);
|
|
484
|
+
const info = await stat(from);
|
|
485
|
+
const dir = normalize(toDir);
|
|
486
|
+
if (info.isDirectory() && (dir === source || dir.startsWith(`${source}/`))) {
|
|
487
|
+
throw new Error("\u041A\u0430\u0442\u0430\u043B\u043E\u0433 \u043D\u0435\u043B\u044C\u0437\u044F \u0441\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0432\u043D\u0443\u0442\u0440\u044C \u0441\u0430\u043C\u043E\u0433\u043E \u0441\u0435\u0431\u044F");
|
|
488
|
+
}
|
|
489
|
+
const name = source.slice(source.lastIndexOf("/") + 1);
|
|
490
|
+
let target = "";
|
|
491
|
+
let to = "";
|
|
492
|
+
for (let attempt = 0; ; attempt++) {
|
|
493
|
+
if (attempt > COPY_NAME_LIMIT)
|
|
494
|
+
throw new Error("\u041D\u0435\u043A\u0443\u0434\u0430 \u043F\u043E\u043B\u043E\u0436\u0438\u0442\u044C \u043A\u043E\u043F\u0438\u044E: \u0438\u043C\u0435\u043D\u0430 \u0437\u0430\u043D\u044F\u0442\u044B");
|
|
495
|
+
target = join(dir, copyName(name, attempt, info.isDirectory()));
|
|
496
|
+
to = await this.#resolve(target);
|
|
497
|
+
if (!await exists(to))
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
await mkdir(path3.dirname(to), { recursive: true });
|
|
501
|
+
await cp(from, to, { recursive: true, errorOnExist: true, force: false });
|
|
502
|
+
return target;
|
|
503
|
+
}
|
|
465
504
|
async remove(relative) {
|
|
466
505
|
const target = await this.#resolve(relative);
|
|
467
506
|
if (!normalize(relative))
|
|
@@ -513,9 +552,88 @@ function contains(root, target) {
|
|
|
513
552
|
async function exists(file) {
|
|
514
553
|
return lstat(file).then(() => true, () => false);
|
|
515
554
|
}
|
|
516
|
-
|
|
555
|
+
var COPY_NAME_LIMIT = 200;
|
|
556
|
+
function copyName(name, attempt, isDir) {
|
|
557
|
+
if (attempt === 0)
|
|
558
|
+
return name;
|
|
559
|
+
const dot = isDir ? -1 : name.lastIndexOf(".");
|
|
560
|
+
const stem = dot > 0 ? name.slice(0, dot) : name;
|
|
561
|
+
const ext = dot > 0 ? name.slice(dot) : "";
|
|
562
|
+
return attempt === 1 ? `${stem} copy${ext}` : `${stem} copy ${attempt}${ext}`;
|
|
563
|
+
}
|
|
564
|
+
function decode(buffer) {
|
|
565
|
+
if (buffer.length >= 3 && buffer[0] === 239 && buffer[1] === 187 && buffer[2] === 191) {
|
|
566
|
+
return { encoding: "utf8bom", text: buffer.toString("utf8", 3) };
|
|
567
|
+
}
|
|
568
|
+
if (buffer.length >= 2 && buffer[0] === 255 && buffer[1] === 254) {
|
|
569
|
+
return { encoding: "utf16le", text: fromUtf16(buffer.subarray(2), "le") };
|
|
570
|
+
}
|
|
571
|
+
if (buffer.length >= 2 && buffer[0] === 254 && buffer[1] === 255) {
|
|
572
|
+
return { encoding: "utf16be", text: fromUtf16(buffer.subarray(2), "be") };
|
|
573
|
+
}
|
|
517
574
|
const head = buffer.subarray(0, SNIFF);
|
|
518
|
-
|
|
575
|
+
if (!head.includes(0))
|
|
576
|
+
return { encoding: "utf8", text: buffer.toString("utf8") };
|
|
577
|
+
const guess = sniffUtf16(head);
|
|
578
|
+
if (!guess)
|
|
579
|
+
return null;
|
|
580
|
+
const text = fromUtf16(buffer, guess === "utf16le" ? "le" : "be");
|
|
581
|
+
return looksTextual(text) ? { encoding: guess, text } : null;
|
|
582
|
+
}
|
|
583
|
+
function encode(content, eol, encoding) {
|
|
584
|
+
const text = eol === "crlf" ? content.replace(/\n/g, "\r\n") : content;
|
|
585
|
+
switch (encoding) {
|
|
586
|
+
case "utf8bom":
|
|
587
|
+
return Buffer.concat([Buffer.from([239, 187, 191]), Buffer.from(text, "utf8")]);
|
|
588
|
+
// BOM пишем и тогда, когда его не было: без него кодировку каждый читатель
|
|
589
|
+
// файла определяет тем же гаданием, что и мы, — и ошибается там же.
|
|
590
|
+
case "utf16le":
|
|
591
|
+
return Buffer.concat([Buffer.from([255, 254]), Buffer.from(text, "utf16le")]);
|
|
592
|
+
case "utf16be": {
|
|
593
|
+
const body = Buffer.from(text, "utf16le");
|
|
594
|
+
body.swap16();
|
|
595
|
+
return Buffer.concat([Buffer.from([254, 255]), body]);
|
|
596
|
+
}
|
|
597
|
+
default:
|
|
598
|
+
return Buffer.from(text, "utf8");
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
function fromUtf16(body, order) {
|
|
602
|
+
const even = body.length % 2 === 0 ? body : body.subarray(0, body.length - 1);
|
|
603
|
+
if (order === "le")
|
|
604
|
+
return even.toString("utf16le");
|
|
605
|
+
const swapped = Buffer.from(even);
|
|
606
|
+
swapped.swap16();
|
|
607
|
+
return swapped.toString("utf16le");
|
|
608
|
+
}
|
|
609
|
+
function sniffUtf16(head) {
|
|
610
|
+
const len = head.length - head.length % 2;
|
|
611
|
+
if (len < 2)
|
|
612
|
+
return null;
|
|
613
|
+
let evenZero = 0;
|
|
614
|
+
let oddZero = 0;
|
|
615
|
+
for (let i = 0; i < len; i += 2) {
|
|
616
|
+
if (head[i] === 0)
|
|
617
|
+
evenZero++;
|
|
618
|
+
if (head[i + 1] === 0)
|
|
619
|
+
oddZero++;
|
|
620
|
+
}
|
|
621
|
+
if (oddZero > 0 && evenZero === 0)
|
|
622
|
+
return "utf16le";
|
|
623
|
+
if (evenZero > 0 && oddZero === 0)
|
|
624
|
+
return "utf16be";
|
|
625
|
+
return null;
|
|
626
|
+
}
|
|
627
|
+
function looksTextual(text) {
|
|
628
|
+
const head = text.length > SNIFF ? text.slice(0, SNIFF) : text;
|
|
629
|
+
for (const char of head) {
|
|
630
|
+
const code = char.codePointAt(0);
|
|
631
|
+
if (code === 65533)
|
|
632
|
+
return false;
|
|
633
|
+
if (code < 32 && char !== " " && char !== "\n" && char !== "\r")
|
|
634
|
+
return false;
|
|
635
|
+
}
|
|
636
|
+
return true;
|
|
519
637
|
}
|
|
520
638
|
|
|
521
639
|
// ../host/dist/journal.js
|
|
@@ -835,11 +953,16 @@ var STOPPED_REASONS = /* @__PURE__ */ new Set([
|
|
|
835
953
|
"tool_deferred_unavailable",
|
|
836
954
|
"background_requested"
|
|
837
955
|
]);
|
|
956
|
+
var API_ERROR_REASON = "api_error";
|
|
957
|
+
var PERMANENT_API_STATUS = /* @__PURE__ */ new Set([400, 404, 413, 422, 429]);
|
|
958
|
+
var RECONNECT_DELAYS_MS = [1e3, 5e3, 2e4, 6e4];
|
|
838
959
|
function turnOutcome(terminalReason, subtype, interruptRequested) {
|
|
839
960
|
if (terminalReason && ABORTED_REASONS.has(terminalReason))
|
|
840
961
|
return "interrupted";
|
|
841
962
|
if (terminalReason && STOPPED_REASONS.has(terminalReason))
|
|
842
963
|
return "stopped";
|
|
964
|
+
if (terminalReason === API_ERROR_REASON)
|
|
965
|
+
return "failed";
|
|
843
966
|
if (subtype === "success")
|
|
844
967
|
return "completed";
|
|
845
968
|
if (interruptRequested)
|
|
@@ -1016,6 +1139,37 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1016
1139
|
/** Когда была последняя попытка его получить — удачная или нет. */
|
|
1017
1140
|
#contextAt = 0;
|
|
1018
1141
|
#contextInFlight = false;
|
|
1142
|
+
/**
|
|
1143
|
+
* Сессию закрывают намеренно — переподключаться не надо.
|
|
1144
|
+
*
|
|
1145
|
+
* Обрыв потока сам по себе не говорит, что случилось: и `close()`, и упавший
|
|
1146
|
+
* VPN выглядят одинаково — итератор кончился. Отличает их только этот флаг.
|
|
1147
|
+
*/
|
|
1148
|
+
#closing = false;
|
|
1149
|
+
/** Сколько раз подряд уже пробовали переподключиться; сбрасывается удачей. */
|
|
1150
|
+
#reconnectAttempt = 0;
|
|
1151
|
+
#reconnectTimer = null;
|
|
1152
|
+
/**
|
|
1153
|
+
* Промпты, которые не доехали: их отправят заново, когда сессия вернётся.
|
|
1154
|
+
*
|
|
1155
|
+
* Повторяем только то, что заведомо ничего не успело сделать (см.
|
|
1156
|
+
* `#turnDidWork`), — иначе повтор доделывал бы работу второй раз.
|
|
1157
|
+
*
|
|
1158
|
+
* Список, а не одно значение: пока сессия ищет себе новый процесс, человек
|
|
1159
|
+
* вполне может дописать ещё реплику — и в ленте она уже есть. Затереть ею
|
|
1160
|
+
* несостоявшуюся первую значило бы показывать вопрос, на который никто
|
|
1161
|
+
* никогда не ответит.
|
|
1162
|
+
*/
|
|
1163
|
+
#pendingRetry = [];
|
|
1164
|
+
/** Промпт текущего хода — то, что придётся отправить заново, если ход сорвётся. */
|
|
1165
|
+
#lastPrompt = null;
|
|
1166
|
+
/**
|
|
1167
|
+
* Ход успел что-то предпринять: вызвал инструмент или сказал что-то своё.
|
|
1168
|
+
*
|
|
1169
|
+
* Граница между «запрос не ушёл» и «работа началась». Повторять можно только
|
|
1170
|
+
* первое: ход, успевший записать файл, при повторе запишет его дважды.
|
|
1171
|
+
*/
|
|
1172
|
+
#turnDidWork = false;
|
|
1019
1173
|
#createdAt = Date.now();
|
|
1020
1174
|
#updatedAt = Date.now();
|
|
1021
1175
|
#options;
|
|
@@ -1096,6 +1250,17 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1096
1250
|
const until = Date.now();
|
|
1097
1251
|
this.#history = readTranscript(this.#options.resume, { dir: this.cwd, until });
|
|
1098
1252
|
}
|
|
1253
|
+
this.#spawn(false);
|
|
1254
|
+
}
|
|
1255
|
+
/**
|
|
1256
|
+
* Поднять сессии процесс CLI — первый раз или взамен оборвавшегося.
|
|
1257
|
+
*
|
|
1258
|
+
* Всё, что отличает переподключение от старта, собрано в `restarted`:
|
|
1259
|
+
* транскрипт при перезапуске не перечитывается (разговор уже в журнале),
|
|
1260
|
+
* продолжаем всегда свою собственную сессию, а настройки, выбранные человеком
|
|
1261
|
+
* на лету, отправляются в новый процесс заново — из файлов он их не возьмёт.
|
|
1262
|
+
*/
|
|
1263
|
+
#spawn(restarted) {
|
|
1099
1264
|
const options2 = {
|
|
1100
1265
|
cwd: this.cwd,
|
|
1101
1266
|
// Без этого SDK не читает с диска ничего: ни CLAUDE.md, ни кастомные
|
|
@@ -1138,9 +1303,10 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1138
1303
|
}
|
|
1139
1304
|
if (this.#model)
|
|
1140
1305
|
options2.model = this.#model;
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1306
|
+
const resume = this.#claudeSessionId ?? this.#options.resume;
|
|
1307
|
+
if (resume)
|
|
1308
|
+
options2.resume = resume;
|
|
1309
|
+
if (this.#options.fork && !this.#claudeSessionId)
|
|
1144
1310
|
options2.forkSession = true;
|
|
1145
1311
|
if (this.#options.outputFormat)
|
|
1146
1312
|
options2.outputFormat = this.#options.outputFormat;
|
|
@@ -1149,9 +1315,60 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1149
1315
|
options2.allowedTools = READ_ONLY_TOOLS;
|
|
1150
1316
|
options2.strictMcpConfig = true;
|
|
1151
1317
|
}
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
void this.#
|
|
1318
|
+
const q = query({ prompt: this.#inbox, options: options2 });
|
|
1319
|
+
this.#query = q;
|
|
1320
|
+
void this.#consume(q);
|
|
1321
|
+
if (restarted)
|
|
1322
|
+
void this.#resume(q);
|
|
1323
|
+
else
|
|
1324
|
+
void this.#loadCatalog();
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Довести поднятый заново процесс до рабочего состояния.
|
|
1328
|
+
*
|
|
1329
|
+
* Ждём, пока он отзовётся, возвращаем ему настройки, выбранные на лету, и
|
|
1330
|
+
* только потом отпускаем накопленные промпты.
|
|
1331
|
+
*
|
|
1332
|
+
* Ждать приходится явно: `system/init` в streaming-режиме приезжает вместе с
|
|
1333
|
+
* первым ответом, то есть уже после промпта, — и сессия, которая ждала бы
|
|
1334
|
+
* его, чтобы промпт отправить, ждала бы вечно. `initializationResult()`,
|
|
1335
|
+
* наоборот, поднимает CLI сам и не требует, чтобы его о чём-то спросили.
|
|
1336
|
+
*/
|
|
1337
|
+
async #resume(q) {
|
|
1338
|
+
const ready = await q.initializationResult().then(() => true, () => false);
|
|
1339
|
+
if (q !== this.#query)
|
|
1340
|
+
return;
|
|
1341
|
+
if (!ready) {
|
|
1342
|
+
this.#scheduleReconnect("\u043D\u043E\u0432\u044B\u0439 \u043F\u0440\u043E\u0446\u0435\u0441\u0441 CLI \u043D\u0435 \u043E\u0442\u043E\u0437\u0432\u0430\u043B\u0441\u044F");
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1345
|
+
try {
|
|
1346
|
+
await q.applyFlagSettings({
|
|
1347
|
+
...this.#effort ? { effortLevel: this.#effort } : {},
|
|
1348
|
+
ultracode: this.#ultracode,
|
|
1349
|
+
alwaysThinkingEnabled: this.#thinking,
|
|
1350
|
+
switchModelsOnFlag: this.#switchModelsOnFlag
|
|
1351
|
+
});
|
|
1352
|
+
} catch {
|
|
1353
|
+
}
|
|
1354
|
+
if (q !== this.#query)
|
|
1355
|
+
return;
|
|
1356
|
+
const retry = this.#pendingRetry;
|
|
1357
|
+
this.#pendingRetry = [];
|
|
1358
|
+
if (retry.length === 0) {
|
|
1359
|
+
this.#setState("idle");
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1362
|
+
this.#record({
|
|
1363
|
+
kind: "error",
|
|
1364
|
+
message: retry.length === 1 ? "\u0421\u0435\u0441\u0441\u0438\u044F \u043F\u043E\u0434\u043D\u044F\u0442\u0430 \u0437\u0430\u043D\u043E\u0432\u043E, \u043F\u043E\u0432\u0442\u043E\u0440\u044F\u044E \u0437\u0430\u043F\u0440\u043E\u0441." : `\u0421\u0435\u0441\u0441\u0438\u044F \u043F\u043E\u0434\u043D\u044F\u0442\u0430 \u0437\u0430\u043D\u043E\u0432\u043E, \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u044F\u044E \u043D\u0430\u043A\u043E\u043F\u043B\u0435\u043D\u043D\u043E\u0435 (${retry.length}).`
|
|
1365
|
+
});
|
|
1366
|
+
this.#lastTurn = null;
|
|
1367
|
+
this.#turnDidWork = false;
|
|
1368
|
+
this.#lastPrompt = retry[retry.length - 1] ?? null;
|
|
1369
|
+
this.#setState("running");
|
|
1370
|
+
for (const text of retry)
|
|
1371
|
+
this.#push(text);
|
|
1155
1372
|
}
|
|
1156
1373
|
/**
|
|
1157
1374
|
* Что известно о сессии до system/init.
|
|
@@ -1176,6 +1393,22 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1176
1393
|
};
|
|
1177
1394
|
}
|
|
1178
1395
|
async close(reason = "closed by client") {
|
|
1396
|
+
this.#closing = true;
|
|
1397
|
+
if (this.#reconnectTimer) {
|
|
1398
|
+
clearTimeout(this.#reconnectTimer);
|
|
1399
|
+
this.#reconnectTimer = null;
|
|
1400
|
+
}
|
|
1401
|
+
this.#teardown(reason);
|
|
1402
|
+
this.#setState("closed");
|
|
1403
|
+
}
|
|
1404
|
+
/**
|
|
1405
|
+
* Отпустить текущий процесс CLI и всё, что на нём висело.
|
|
1406
|
+
*
|
|
1407
|
+
* Общее у закрытия и переподключения: и там, и там прежний процесс больше не
|
|
1408
|
+
* ответит, а незакрытые запросы прав, если их не разрешить, повиснут навсегда
|
|
1409
|
+
* — SDK ждёт промис, которому уже некому прийти.
|
|
1410
|
+
*/
|
|
1411
|
+
#teardown(reason) {
|
|
1179
1412
|
this.#inbox.close();
|
|
1180
1413
|
for (const [requestId, pending] of this.#pendingPermissions) {
|
|
1181
1414
|
pending.resolve({ behavior: "deny", message: reason });
|
|
@@ -1185,29 +1418,119 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1185
1418
|
this.#query?.close();
|
|
1186
1419
|
} catch {
|
|
1187
1420
|
}
|
|
1188
|
-
this.#
|
|
1421
|
+
this.#query = null;
|
|
1422
|
+
}
|
|
1423
|
+
// -------------------------------------------------------------------------
|
|
1424
|
+
// Переподключение
|
|
1425
|
+
// -------------------------------------------------------------------------
|
|
1426
|
+
/**
|
|
1427
|
+
* Поднять сессии новый процесс CLI, сохранив разговор.
|
|
1428
|
+
*
|
|
1429
|
+
* Зачем вообще: у CLI, которому запрос к API вернул 403 (упал VPN — сеть
|
|
1430
|
+
* отвечает вместо API), состояние аутентификации портится на всю жизнь
|
|
1431
|
+
* процесса. Разговор при этом цел, ключи целы, VPN через минуту вернулся — а
|
|
1432
|
+
* сессия так и отвечает «Failed to authenticate» на каждый промпт, пока её не
|
|
1433
|
+
* закроют и не откроют заново. Собственно, единственным лекарством и был этот
|
|
1434
|
+
* ручной круг: закрыть, открыть, продолжить.
|
|
1435
|
+
*
|
|
1436
|
+
* Здесь тот же круг делается сам. Новый процесс продолжает (`resume`) ту же
|
|
1437
|
+
* сессию Claude Code, поэтому разговор, файлы и контекст остаются на месте —
|
|
1438
|
+
* человек видит паузу, а не потерю.
|
|
1439
|
+
*/
|
|
1440
|
+
reconnect(reason) {
|
|
1441
|
+
this.#reconnectAttempt = 0;
|
|
1442
|
+
this.#restart(reason);
|
|
1443
|
+
}
|
|
1444
|
+
/** Собственно смена процесса — без разговора о том, чья это была идея. */
|
|
1445
|
+
#restart(reason) {
|
|
1446
|
+
if (this.#closing || this.#state === "closed")
|
|
1447
|
+
return;
|
|
1448
|
+
if (this.#reconnectTimer) {
|
|
1449
|
+
clearTimeout(this.#reconnectTimer);
|
|
1450
|
+
this.#reconnectTimer = null;
|
|
1451
|
+
}
|
|
1452
|
+
this.#setState("reconnecting");
|
|
1453
|
+
this.#teardown("\u043F\u0435\u0440\u0435\u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0435");
|
|
1454
|
+
this.#inbox = new AsyncQueue();
|
|
1455
|
+
this.#streamMessageId = null;
|
|
1456
|
+
this.#blockCounts.clear();
|
|
1457
|
+
this.#record({ kind: "error", message: `\u041F\u0435\u0440\u0435\u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0430\u044E\u0441\u044C: ${reason}` });
|
|
1458
|
+
this.#spawn(true);
|
|
1459
|
+
}
|
|
1460
|
+
/**
|
|
1461
|
+
* Попробовать переподключиться ещё раз — с паузой и с оглядкой на счётчик.
|
|
1462
|
+
*
|
|
1463
|
+
* Процесс отпускаем сразу, а поднимаем новый через паузу: между этими двумя
|
|
1464
|
+
* моментами сессия должна быть заведомо без процесса, иначе промпт, посланный
|
|
1465
|
+
* в эту щель, уехал бы в очередь, которую никто не читает, и завис бы там
|
|
1466
|
+
* навсегда — `sendPrompt` отличает эти состояния именно по `#query`.
|
|
1467
|
+
*
|
|
1468
|
+
* Попытки конечны намеренно. Обрыв VPN лечится сам за секунды, а вот
|
|
1469
|
+
* отозванный ключ или снесённый CLI не вылечится никогда, и бесконечный
|
|
1470
|
+
* круг перезапусков в этом случае только жёг бы процессы, показывая человеку
|
|
1471
|
+
* ровно ту же ошибку. Кончились попытки — говорим об этом прямо и ждём
|
|
1472
|
+
* человека: и промпт, и кнопка «переподключить» начинают отсчёт заново.
|
|
1473
|
+
*/
|
|
1474
|
+
#scheduleReconnect(reason) {
|
|
1475
|
+
if (this.#closing || this.#reconnectTimer)
|
|
1476
|
+
return;
|
|
1477
|
+
this.#teardown("\u043F\u0435\u0440\u0435\u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0435");
|
|
1478
|
+
const delay = RECONNECT_DELAYS_MS[this.#reconnectAttempt];
|
|
1479
|
+
if (delay === void 0) {
|
|
1480
|
+
this.#pendingRetry = [];
|
|
1481
|
+
this.#record({
|
|
1482
|
+
kind: "error",
|
|
1483
|
+
message: `\u041F\u0435\u0440\u0435\u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0438\u0442\u044C\u0441\u044F \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C: ${reason}. \u041F\u0440\u043E\u0432\u0435\u0440\u044C\u0442\u0435 \u0441\u0435\u0442\u044C \u0438 VPN, \u0430 \u043F\u043E\u0442\u043E\u043C \u043E\u0442\u043F\u0440\u0430\u0432\u044C\u0442\u0435 \u043F\u0440\u043E\u043C\u043F\u0442 \u0438\u043B\u0438 \u043D\u0430\u0436\u043C\u0438\u0442\u0435 \xAB\u041F\u0435\u0440\u0435\u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0438\u0442\u044C\xBB \u2014 \u0440\u0430\u0437\u0433\u043E\u0432\u043E\u0440 \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D, \u0441\u0435\u0441\u0441\u0438\u044F \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442 \u0441 \u044D\u0442\u043E\u0433\u043E \u043C\u0435\u0441\u0442\u0430.`
|
|
1484
|
+
});
|
|
1485
|
+
this.#setState("error");
|
|
1486
|
+
return;
|
|
1487
|
+
}
|
|
1488
|
+
this.#reconnectAttempt += 1;
|
|
1489
|
+
this.#record({
|
|
1490
|
+
kind: "error",
|
|
1491
|
+
message: `\u0421\u0432\u044F\u0437\u044C \u0441 Claude \u043F\u043E\u0442\u0435\u0440\u044F\u043D\u0430: ${reason}. \u041F\u0435\u0440\u0435\u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 ${Math.round(delay / 1e3)} \u0441 (\u043F\u043E\u043F\u044B\u0442\u043A\u0430 ${this.#reconnectAttempt} \u0438\u0437 ${RECONNECT_DELAYS_MS.length}).`
|
|
1492
|
+
});
|
|
1493
|
+
this.#setState("reconnecting");
|
|
1494
|
+
this.#reconnectTimer = setTimeout(() => {
|
|
1495
|
+
this.#reconnectTimer = null;
|
|
1496
|
+
this.#restart(reason);
|
|
1497
|
+
}, delay);
|
|
1498
|
+
this.#reconnectTimer.unref?.();
|
|
1189
1499
|
}
|
|
1190
1500
|
// -------------------------------------------------------------------------
|
|
1191
1501
|
// Ввод от пользователя
|
|
1192
1502
|
// -------------------------------------------------------------------------
|
|
1193
1503
|
sendPrompt(text) {
|
|
1194
|
-
if (this.#
|
|
1504
|
+
if (this.#closing)
|
|
1195
1505
|
throw new Error("session is closed");
|
|
1196
1506
|
this.#record({ kind: "user.message", text });
|
|
1197
1507
|
if (!this.#title || this.#titleIsDefault) {
|
|
1198
1508
|
this.#title = text.slice(0, 80);
|
|
1199
1509
|
this.#titleIsDefault = false;
|
|
1200
1510
|
}
|
|
1201
|
-
if (this.#state === "error") {
|
|
1202
|
-
this.#record({
|
|
1203
|
-
kind: "error",
|
|
1204
|
-
message: "\u0421\u0435\u0441\u0441\u0438\u044F \u043D\u0435 \u0437\u0430\u043F\u0443\u0449\u0435\u043D\u0430, \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u044F\u0442\u044C \u043F\u0440\u043E\u043C\u043F\u0442 \u043D\u0435\u043A\u0443\u0434\u0430 \u2014 \u0441\u043C. \u043E\u0448\u0438\u0431\u043A\u0443 \u0432\u044B\u0448\u0435."
|
|
1205
|
-
});
|
|
1206
|
-
return;
|
|
1207
|
-
}
|
|
1208
1511
|
this.#lastTurn = null;
|
|
1209
1512
|
this.#interruptRequested = false;
|
|
1513
|
+
this.#lastPrompt = text;
|
|
1514
|
+
this.#turnDidWork = false;
|
|
1515
|
+
if (!this.#query) {
|
|
1516
|
+
this.#pendingRetry.push(text);
|
|
1517
|
+
this.reconnect("\u0441\u0435\u0441\u0441\u0438\u0438 \u043D\u0443\u0436\u0435\u043D \u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u0434\u043B\u044F \u043D\u043E\u0432\u043E\u0433\u043E \u043F\u0440\u043E\u043C\u043F\u0442\u0430");
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1210
1520
|
this.#setState("running");
|
|
1521
|
+
this.#push(text);
|
|
1522
|
+
}
|
|
1523
|
+
/**
|
|
1524
|
+
* Стоит ли вообще переподключаться из-за этого кода ответа.
|
|
1525
|
+
*
|
|
1526
|
+
* Отсутствие кода — тоже повод: ответа не было вовсе (сеть оборвалась на
|
|
1527
|
+
* полуслове), а это ровно тот случай, ради которого всё и затевалось.
|
|
1528
|
+
*/
|
|
1529
|
+
#retryable(status) {
|
|
1530
|
+
return status === void 0 || !PERMANENT_API_STATUS.has(status);
|
|
1531
|
+
}
|
|
1532
|
+
/** Промпт в очередь SDK — без записи в журнал: при повторе она уже там. */
|
|
1533
|
+
#push(text) {
|
|
1211
1534
|
this.#inbox.push({
|
|
1212
1535
|
type: "user",
|
|
1213
1536
|
message: { role: "user", content: [{ type: "text", text }] },
|
|
@@ -1576,13 +1899,27 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1576
1899
|
async #consume(q) {
|
|
1577
1900
|
try {
|
|
1578
1901
|
for await (const message of q) {
|
|
1902
|
+
if (q !== this.#query)
|
|
1903
|
+
return;
|
|
1579
1904
|
this.#handleMessage(message);
|
|
1580
1905
|
}
|
|
1581
|
-
this.#
|
|
1906
|
+
if (q !== this.#query)
|
|
1907
|
+
return;
|
|
1908
|
+
if (this.#closing)
|
|
1909
|
+
this.#setState("closed");
|
|
1910
|
+
else
|
|
1911
|
+
this.#scheduleReconnect("\u043F\u0440\u043E\u0446\u0435\u0441\u0441 CLI \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0441\u044F");
|
|
1582
1912
|
} catch (error) {
|
|
1913
|
+
if (q !== this.#query || this.#closing)
|
|
1914
|
+
return;
|
|
1583
1915
|
const message = error instanceof Error ? error.message : String(error);
|
|
1584
|
-
|
|
1585
|
-
|
|
1916
|
+
if (!this.#capabilities) {
|
|
1917
|
+
this.#record({ kind: "error", message: message + this.#executableHint() });
|
|
1918
|
+
this.#setState("error");
|
|
1919
|
+
return;
|
|
1920
|
+
}
|
|
1921
|
+
this.#record({ kind: "error", message });
|
|
1922
|
+
this.#scheduleReconnect(message);
|
|
1586
1923
|
}
|
|
1587
1924
|
}
|
|
1588
1925
|
/**
|
|
@@ -1614,7 +1951,10 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1614
1951
|
const usage = message.message.usage;
|
|
1615
1952
|
this.#contextTokens = (usage.input_tokens ?? 0) + (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0);
|
|
1616
1953
|
}
|
|
1954
|
+
const synthetic2 = isApiErrorMessage(message);
|
|
1617
1955
|
for (const block of message.message.content) {
|
|
1956
|
+
if (!synthetic2)
|
|
1957
|
+
this.#turnDidWork = true;
|
|
1618
1958
|
const blockId = `${message.message.id}:${this.#nextBlockIndex(message.message.id)}`;
|
|
1619
1959
|
if (block.type === "text") {
|
|
1620
1960
|
this.#record({ kind: "assistant.text", text: block.text, blockId });
|
|
@@ -1666,8 +2006,21 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1666
2006
|
text: "result" in message ? message.result : null
|
|
1667
2007
|
});
|
|
1668
2008
|
}
|
|
1669
|
-
|
|
2009
|
+
const terminalReason = "terminal_reason" in message ? message.terminal_reason : void 0;
|
|
2010
|
+
this.#lastTurn = turnOutcome(terminalReason, message.subtype, this.#interruptRequested);
|
|
2011
|
+
const interrupted = this.#interruptRequested;
|
|
1670
2012
|
this.#interruptRequested = false;
|
|
2013
|
+
if (terminalReason === API_ERROR_REASON && !interrupted) {
|
|
2014
|
+
const status = "api_error_status" in message ? message.api_error_status : void 0;
|
|
2015
|
+
if (this.#retryable(status ?? void 0)) {
|
|
2016
|
+
if (!this.#turnDidWork && this.#lastPrompt !== null) {
|
|
2017
|
+
this.#pendingRetry.push(this.#lastPrompt);
|
|
2018
|
+
}
|
|
2019
|
+
this.#scheduleReconnect(apiErrorReason(status ?? void 0));
|
|
2020
|
+
break;
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
this.#reconnectAttempt = 0;
|
|
1671
2024
|
const alreadyIdle = this.#state === "idle";
|
|
1672
2025
|
this.#setState("idle");
|
|
1673
2026
|
if (alreadyIdle)
|
|
@@ -1838,6 +2191,12 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1838
2191
|
function describe(error) {
|
|
1839
2192
|
return error instanceof Error ? error.message : String(error);
|
|
1840
2193
|
}
|
|
2194
|
+
function isApiErrorMessage(message) {
|
|
2195
|
+
return message.is_api_error_message === true;
|
|
2196
|
+
}
|
|
2197
|
+
function apiErrorReason(status) {
|
|
2198
|
+
return status === void 0 ? "\u0437\u0430\u043F\u0440\u043E\u0441 \u043A API \u043D\u0435 \u0434\u043E\u0448\u0451\u043B" : `API \u043E\u0442\u0432\u0435\u0442\u0438\u043B ${status}`;
|
|
2199
|
+
}
|
|
1841
2200
|
function toSlashCommand(command) {
|
|
1842
2201
|
return {
|
|
1843
2202
|
name: command.name,
|
|
@@ -4324,6 +4683,9 @@ var HostServer = class {
|
|
|
4324
4683
|
case "session.close":
|
|
4325
4684
|
await this.#dropSession(frame.sessionId, "closed by client");
|
|
4326
4685
|
return;
|
|
4686
|
+
case "session.reconnect":
|
|
4687
|
+
this.#requireSession(frame.sessionId).reconnect("\u043F\u043E \u043F\u0440\u043E\u0441\u044C\u0431\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F");
|
|
4688
|
+
return;
|
|
4327
4689
|
case "session.archive": {
|
|
4328
4690
|
const ids = new Set(frame.sessionIds);
|
|
4329
4691
|
for (const session of [...this.#sessions.values()]) {
|
|
@@ -4597,7 +4959,7 @@ var HostServer = class {
|
|
|
4597
4959
|
const files = this.#files(frame.workspaceId);
|
|
4598
4960
|
let saved;
|
|
4599
4961
|
try {
|
|
4600
|
-
saved = await files.write(frame.path, frame.content, frame.eol, frame.baseMtime, frame.force === true);
|
|
4962
|
+
saved = await files.write(frame.path, frame.content, frame.eol, frame.encoding ?? "utf8", frame.baseMtime, frame.force === true);
|
|
4601
4963
|
} catch (error) {
|
|
4602
4964
|
this.#fsFailed(frame.workspaceId, frame.path, error);
|
|
4603
4965
|
return;
|
|
@@ -4643,6 +5005,24 @@ var HostServer = class {
|
|
|
4643
5005
|
}
|
|
4644
5006
|
return;
|
|
4645
5007
|
}
|
|
5008
|
+
case "fs.copy": {
|
|
5009
|
+
let created;
|
|
5010
|
+
try {
|
|
5011
|
+
created = await this.#files(frame.workspaceId).copy(frame.path, frame.toDir);
|
|
5012
|
+
} catch (error) {
|
|
5013
|
+
this.#fsFailed(frame.workspaceId, frame.path, error);
|
|
5014
|
+
return;
|
|
5015
|
+
}
|
|
5016
|
+
this.#broadcast({
|
|
5017
|
+
type: "fs.result",
|
|
5018
|
+
workspaceId: frame.workspaceId,
|
|
5019
|
+
ok: true,
|
|
5020
|
+
message: `\u0421\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u043D\u043E \u0432 ${created}`,
|
|
5021
|
+
path: created
|
|
5022
|
+
});
|
|
5023
|
+
await this.#sendDir(frame.workspaceId, parentOf(created));
|
|
5024
|
+
return;
|
|
5025
|
+
}
|
|
4646
5026
|
// Терминал. Каждая ручка начинается с одной и той же проверки права: их
|
|
4647
5027
|
// пять, и «забыл в одной» здесь означает не потерянную кнопку, а чужую
|
|
4648
5028
|
// оболочку в руках у того, кому её не давали.
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{s as r,H as a}from"./index-
|
|
1
|
+
import{s as r,H as a}from"./index-CP6Bv1lU.js";import{E as e}from"./index-891wE-Rj.js";import{t as o}from"./index-5NnNKmcv.js";import"./index-BHot-lnj.js";const m=e.theme({"&":{height:"100%",color:"var(--text)",backgroundColor:"var(--bg)",fontSize:"13px"},".cm-scroller":{fontFamily:"var(--mono)",lineHeight:"1.6"},".cm-content":{caretColor:"var(--accent)"},".cm-cursor, .cm-dropCursor":{borderLeftColor:"var(--accent)"},"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":{backgroundColor:"var(--cm-selection)"},".cm-gutters":{backgroundColor:"var(--bg)",color:"var(--muted)",borderRight:"1px solid var(--border-soft)"},".cm-activeLineGutter":{backgroundColor:"var(--surface-2)",color:"var(--text-2)"},".cm-activeLine":{backgroundColor:"var(--cm-active)"},".cm-selectionMatch":{backgroundColor:"var(--cm-match)"},"&.cm-focused .cm-matchingBracket":{backgroundColor:"var(--cm-match)",outline:"1px solid var(--accent-line)"},".cm-foldPlaceholder":{backgroundColor:"var(--surface-2)",border:"1px solid var(--border)",color:"var(--muted)"},".cm-panels":{backgroundColor:"var(--surface)",color:"var(--text)",border:"1px solid var(--border)"},".cm-panels input, .cm-panels button, .cm-panels select":{fontFamily:"var(--sans)",fontSize:"12px",background:"var(--surface-2)",color:"var(--text)",border:"1px solid var(--border)",borderRadius:"6px",padding:"3px 6px"},".cm-searchMatch":{backgroundColor:"var(--cm-match)"},".cm-searchMatch.cm-searchMatch-selected":{backgroundColor:"var(--accent-soft)"},".cm-tooltip":{background:"var(--surface)",border:"1px solid var(--border)",borderRadius:"8px",boxShadow:"var(--shadow-md)",color:"var(--text)"},".cm-tooltip-autocomplete ul li[aria-selected]":{background:"var(--surface-2)",color:"var(--text)"}}),i=r(a.define([{tag:[o.keyword,o.modifier,o.controlKeyword,o.moduleKeyword],color:"var(--cm-key)"},{tag:[o.name,o.deleted,o.character,o.macroName],color:"var(--text)"},{tag:[o.propertyName],color:"var(--cm-prop)"},{tag:[o.function(o.variableName),o.labelName],color:"var(--cm-fn)"},{tag:[o.color,o.constant(o.name),o.standard(o.name),o.bool],color:"var(--cm-const)"},{tag:[o.definition(o.name),o.separator],color:"var(--text)"},{tag:[o.typeName,o.className,o.changed,o.annotation,o.self,o.namespace],color:"var(--cm-type)"},{tag:[o.number,o.integer,o.float],color:"var(--cm-num)"},{tag:[o.string,o.special(o.string),o.regexp],color:"var(--cm-str)"},{tag:[o.operator,o.operatorKeyword,o.escape],color:"var(--cm-op)"},{tag:[o.meta,o.comment],color:"var(--cm-comment)",fontStyle:"italic"},{tag:o.strong,fontWeight:"650"},{tag:o.emphasis,fontStyle:"italic"},{tag:o.strikethrough,textDecoration:"line-through"},{tag:o.link,color:"var(--cm-str)",textDecoration:"underline"},{tag:o.heading,fontWeight:"650",color:"var(--cm-key)"},{tag:o.invalid,color:"var(--danger)"}]));export{i as editorHighlight,m as editorTheme};
|