@i14ks/ccv 0.22.0 → 0.24.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/README.md +10 -2
- package/bundle/ccv.mjs +1455 -251
- package/package.json +5 -2
- package/web/assets/{codemirror-BVRIW86g.js → codemirror-BABguQzY.js} +1 -1
- package/web/assets/{index-BOTbHjsh.js → index-BHEVBqv1.js} +1 -1
- package/web/assets/{index-DiFzORZa.js → index-BJQdUP7a.js} +1 -1
- package/web/assets/{index-CwbKEdO4.js → index-BQiRpvdM.js} +1 -1
- package/web/assets/{index-DcOycRyY.js → index-BRzmWdWn.js} +1 -1
- package/web/assets/{index-Cj3tIh2i.js → index-BWAAro79.js} +1 -1
- package/web/assets/{index-DqNX5AfU.js → index-BaKf7c69.js} +1 -1
- package/web/assets/{index-BRDMJVs_.js → index-BmRYCxGv.js} +1 -1
- package/web/assets/{index-CftAsWDp.js → index-BnqCpVA0.js} +1 -1
- package/web/assets/{index-CHk-wT2e.js → index-Bqjj9q2W.js} +1 -1
- package/web/assets/{index-iBO43oLF.js → index-CIsgyM-R.js} +1 -1
- package/web/assets/{index-B54sB01B.js → index-CZppS1t2.js} +1 -1
- package/web/assets/{index-5z-WKiE2.js → index-Ck0zkLid.js} +1 -1
- package/web/assets/{index-CEtwcv2R.js → index-CxF-ndAb.js} +1 -1
- package/web/assets/{index-BUtKkXYx.js → index-DW1Fnc9x.js} +1 -1
- package/web/assets/index-DXMZoXqv.js +136 -0
- package/web/assets/index-DafZE925.css +1 -0
- package/web/assets/{index-BSbB9pNN.js → index-Di0Nghq9.js} +1 -1
- package/web/assets/{index-A6KnUhy4.js → index-DnlEeQVW.js} +1 -1
- package/web/assets/{index-BTHa3RWC.js → index-DogVeywh.js} +1 -1
- package/web/assets/{index-B5EnmuL9.js → index-U9tL3Foh.js} +1 -1
- package/web/assets/{index-C4RL1RRN.js → index-dSYnVYDn.js} +1 -1
- package/web/index.html +2 -2
- package/web/assets/index-L8M_w2e-.js +0 -134
- package/web/assets/index-xoxa8R94.css +0 -1
package/bundle/ccv.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// dist/ccv.js
|
|
4
|
-
import { spawn as
|
|
4
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
5
5
|
import { readFile as readFile17 } from "node:fs/promises";
|
|
6
|
-
import
|
|
6
|
+
import path25 from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import { parseArgs } from "node:util";
|
|
9
9
|
|
|
@@ -145,10 +145,10 @@ function envDefaults() {
|
|
|
145
145
|
|
|
146
146
|
// ../host/dist/server.js
|
|
147
147
|
import { createServer } from "node:http";
|
|
148
|
-
import { createReadStream, existsSync as
|
|
149
|
-
import
|
|
150
|
-
import { homedir as
|
|
151
|
-
import { randomUUID as
|
|
148
|
+
import { createReadStream, existsSync as existsSync5, statSync as statSync4 } from "node:fs";
|
|
149
|
+
import path24 from "node:path";
|
|
150
|
+
import { homedir as homedir7 } from "node:os";
|
|
151
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
152
152
|
import { WebSocketServer } from "ws";
|
|
153
153
|
import { renameSession as renameSession2 } from "@anthropic-ai/claude-agent-sdk";
|
|
154
154
|
|
|
@@ -187,12 +187,12 @@ function hashText(text) {
|
|
|
187
187
|
// ../host/dist/workspace-ids.js
|
|
188
188
|
function workspaceIdRemap(paths, sessionsOf) {
|
|
189
189
|
const byLegacy = /* @__PURE__ */ new Map();
|
|
190
|
-
for (const
|
|
191
|
-
const id = workspaceIdFromPath(
|
|
192
|
-
for (const legacy of formerWorkspaceIdsFromPath(
|
|
190
|
+
for (const path26 of paths) {
|
|
191
|
+
const id = workspaceIdFromPath(path26);
|
|
192
|
+
for (const legacy of formerWorkspaceIdsFromPath(path26)) {
|
|
193
193
|
const group = byLegacy.get(legacy) ?? [];
|
|
194
194
|
if (!group.some((target) => target.id === id))
|
|
195
|
-
group.push({ id, path:
|
|
195
|
+
group.push({ id, path: path26 });
|
|
196
196
|
byLegacy.set(legacy, group);
|
|
197
197
|
}
|
|
198
198
|
}
|
|
@@ -223,7 +223,7 @@ function workspaceIdRemap(paths, sessionsOf) {
|
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
// ../host/dist/session.js
|
|
226
|
-
import { EventEmitter } from "node:events";
|
|
226
|
+
import { EventEmitter as EventEmitter2 } from "node:events";
|
|
227
227
|
import { randomUUID } from "node:crypto";
|
|
228
228
|
import path5 from "node:path";
|
|
229
229
|
import { query, renameSession } from "@anthropic-ai/claude-agent-sdk";
|
|
@@ -787,6 +787,173 @@ var Journal = class {
|
|
|
787
787
|
}
|
|
788
788
|
};
|
|
789
789
|
|
|
790
|
+
// ../host/dist/network-guard.js
|
|
791
|
+
import { EventEmitter } from "node:events";
|
|
792
|
+
import { request } from "node:https";
|
|
793
|
+
import { networkInterfaces } from "node:os";
|
|
794
|
+
var TRACE_URL = "https://api.anthropic.com/cdn-cgi/trace";
|
|
795
|
+
var NETWORK_CHECK_MS = 5e3;
|
|
796
|
+
var INTERFACES_POLL_MS = 1e3;
|
|
797
|
+
var CHECK_TIMEOUT_MS = 4e3;
|
|
798
|
+
var RETRY_DELAY_MS = 1e3;
|
|
799
|
+
var NETWORK_FRESH_MS = 3e3;
|
|
800
|
+
var UNCHECKED_REASON = "\u041C\u0430\u0440\u0448\u0440\u0443\u0442 \u0434\u043E Anthropic \u0435\u0449\u0451 \u043D\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u0435\u043D";
|
|
801
|
+
var NetworkGuard = class extends EventEmitter {
|
|
802
|
+
#enabled = false;
|
|
803
|
+
#blocked = /* @__PURE__ */ new Set();
|
|
804
|
+
#state = {
|
|
805
|
+
status: "ok",
|
|
806
|
+
country: null,
|
|
807
|
+
reason: null,
|
|
808
|
+
checkedAt: null,
|
|
809
|
+
enabled: false
|
|
810
|
+
};
|
|
811
|
+
#checkTimer = null;
|
|
812
|
+
#interfacesTimer = null;
|
|
813
|
+
#interfaces = "";
|
|
814
|
+
#inFlight = null;
|
|
815
|
+
/** Растёт при каждой смене настроек: ответ проверки, начатой до неё, не применяется. */
|
|
816
|
+
#generation = 0;
|
|
817
|
+
get state() {
|
|
818
|
+
return { ...this.#state };
|
|
819
|
+
}
|
|
820
|
+
/** Почему агентам сейчас нельзя в сеть; `null` — можно. */
|
|
821
|
+
blockedReason() {
|
|
822
|
+
return this.#state.status === "blocked" ? this.#state.reason ?? UNCHECKED_REASON : null;
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Применить настройки. Включение начинает с блока до первой проверки,
|
|
826
|
+
* выключение снимает блок сразу.
|
|
827
|
+
*/
|
|
828
|
+
configure(enabled, blockedCountries) {
|
|
829
|
+
const countries = new Set(blockedCountries);
|
|
830
|
+
const same = enabled === this.#enabled && countries.size === this.#blocked.size && [...countries].every((c) => this.#blocked.has(c));
|
|
831
|
+
if (same)
|
|
832
|
+
return;
|
|
833
|
+
this.#generation += 1;
|
|
834
|
+
this.#blocked = countries;
|
|
835
|
+
const wasEnabled = this.#enabled;
|
|
836
|
+
this.#enabled = enabled;
|
|
837
|
+
if (!enabled) {
|
|
838
|
+
this.#stopTimers();
|
|
839
|
+
this.#apply({ status: "ok", country: this.#state.country, reason: null, checkedAt: null });
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
if (!wasEnabled) {
|
|
843
|
+
this.#apply({ status: "blocked", country: null, reason: UNCHECKED_REASON, checkedAt: null });
|
|
844
|
+
this.#startTimers();
|
|
845
|
+
}
|
|
846
|
+
void this.check();
|
|
847
|
+
}
|
|
848
|
+
stop() {
|
|
849
|
+
this.#stopTimers();
|
|
850
|
+
}
|
|
851
|
+
/** Проверить сейчас. Параллельные вызовы делят одну проверку. */
|
|
852
|
+
check() {
|
|
853
|
+
if (!this.#enabled)
|
|
854
|
+
return Promise.resolve(this.state);
|
|
855
|
+
this.#inFlight ??= this.#runCheck().finally(() => {
|
|
856
|
+
this.#inFlight = null;
|
|
857
|
+
});
|
|
858
|
+
return this.#inFlight;
|
|
859
|
+
}
|
|
860
|
+
/** Проверить, если последняя проверка старше `NETWORK_FRESH_MS`. */
|
|
861
|
+
async ensureFresh() {
|
|
862
|
+
if (!this.#enabled)
|
|
863
|
+
return this.state;
|
|
864
|
+
const at = this.#state.checkedAt;
|
|
865
|
+
if (at !== null && Date.now() - at < NETWORK_FRESH_MS && !this.#inFlight)
|
|
866
|
+
return this.state;
|
|
867
|
+
return this.check();
|
|
868
|
+
}
|
|
869
|
+
async #runCheck() {
|
|
870
|
+
const generation = this.#generation;
|
|
871
|
+
let result = await traceCountry();
|
|
872
|
+
if (!result.ok && generation === this.#generation) {
|
|
873
|
+
await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS));
|
|
874
|
+
result = await traceCountry();
|
|
875
|
+
}
|
|
876
|
+
if (generation !== this.#generation || !this.#enabled)
|
|
877
|
+
return this.state;
|
|
878
|
+
const checkedAt = Date.now();
|
|
879
|
+
if (!result.ok) {
|
|
880
|
+
this.#apply({
|
|
881
|
+
status: "blocked",
|
|
882
|
+
country: null,
|
|
883
|
+
reason: `\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u043C\u0430\u0440\u0448\u0440\u0443\u0442 \u0434\u043E Anthropic: ${result.error}`,
|
|
884
|
+
checkedAt
|
|
885
|
+
});
|
|
886
|
+
} else if (this.#blocked.has(result.country)) {
|
|
887
|
+
this.#apply({
|
|
888
|
+
status: "blocked",
|
|
889
|
+
country: result.country,
|
|
890
|
+
reason: `\u0417\u0430\u043F\u0440\u043E\u0441\u044B \u043A Anthropic \u0438\u0434\u0443\u0442 \u0438\u0437 ${result.country} \u2014 \u043F\u043E\u0445\u043E\u0436\u0435, VPN \u043D\u0435 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442`,
|
|
891
|
+
checkedAt
|
|
892
|
+
});
|
|
893
|
+
} else {
|
|
894
|
+
this.#apply({ status: "ok", country: result.country, reason: null, checkedAt });
|
|
895
|
+
}
|
|
896
|
+
return this.state;
|
|
897
|
+
}
|
|
898
|
+
#apply(next) {
|
|
899
|
+
const previous = this.#state;
|
|
900
|
+
this.#state = { ...next, enabled: this.#enabled };
|
|
901
|
+
const changed = previous.status !== next.status || previous.country !== next.country || previous.reason !== next.reason || previous.enabled !== this.#enabled;
|
|
902
|
+
if (changed)
|
|
903
|
+
this.emit("change", this.state);
|
|
904
|
+
}
|
|
905
|
+
#startTimers() {
|
|
906
|
+
this.#stopTimers();
|
|
907
|
+
this.#interfaces = interfacesSignature();
|
|
908
|
+
this.#checkTimer = setInterval(() => void this.check(), NETWORK_CHECK_MS);
|
|
909
|
+
this.#checkTimer.unref();
|
|
910
|
+
this.#interfacesTimer = setInterval(() => {
|
|
911
|
+
const signature = interfacesSignature();
|
|
912
|
+
if (signature === this.#interfaces)
|
|
913
|
+
return;
|
|
914
|
+
this.#interfaces = signature;
|
|
915
|
+
void this.check();
|
|
916
|
+
}, INTERFACES_POLL_MS);
|
|
917
|
+
this.#interfacesTimer.unref();
|
|
918
|
+
}
|
|
919
|
+
#stopTimers() {
|
|
920
|
+
if (this.#checkTimer)
|
|
921
|
+
clearInterval(this.#checkTimer);
|
|
922
|
+
if (this.#interfacesTimer)
|
|
923
|
+
clearInterval(this.#interfacesTimer);
|
|
924
|
+
this.#checkTimer = null;
|
|
925
|
+
this.#interfacesTimer = null;
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
var networkGuard = new NetworkGuard();
|
|
929
|
+
function traceCountry() {
|
|
930
|
+
return new Promise((resolve) => {
|
|
931
|
+
const req = request(TRACE_URL, { method: "GET", agent: false, timeout: CHECK_TIMEOUT_MS, headers: { "cache-control": "no-store" } }, (res) => {
|
|
932
|
+
let body = "";
|
|
933
|
+
res.setEncoding("utf8");
|
|
934
|
+
res.on("data", (chunk) => {
|
|
935
|
+
body += chunk;
|
|
936
|
+
if (body.length > 16e3)
|
|
937
|
+
req.destroy(new Error("\u0441\u043B\u0438\u0448\u043A\u043E\u043C \u0434\u043B\u0438\u043D\u043D\u044B\u0439 \u043E\u0442\u0432\u0435\u0442"));
|
|
938
|
+
});
|
|
939
|
+
res.on("end", () => {
|
|
940
|
+
const country = /^loc=([A-Z]{2})$/m.exec(body)?.[1];
|
|
941
|
+
if (res.statusCode === 200 && country)
|
|
942
|
+
resolve({ ok: true, country });
|
|
943
|
+
else
|
|
944
|
+
resolve({ ok: false, error: `\u043D\u0435\u043E\u0436\u0438\u0434\u0430\u043D\u043D\u044B\u0439 \u043E\u0442\u0432\u0435\u0442 (HTTP ${res.statusCode ?? "\u2014"})` });
|
|
945
|
+
});
|
|
946
|
+
res.on("error", (error) => resolve({ ok: false, error: error.message }));
|
|
947
|
+
});
|
|
948
|
+
req.on("timeout", () => req.destroy(new Error("\u043D\u0435\u0442 \u043E\u0442\u0432\u0435\u0442\u0430")));
|
|
949
|
+
req.on("error", (error) => resolve({ ok: false, error: error.message }));
|
|
950
|
+
req.end();
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
function interfacesSignature() {
|
|
954
|
+
return Object.entries(networkInterfaces()).map(([name, addresses]) => `${name}:${(addresses ?? []).map((a) => a.address).sort().join(",")}`).sort().join("|");
|
|
955
|
+
}
|
|
956
|
+
|
|
790
957
|
// ../host/dist/settings.js
|
|
791
958
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
792
959
|
import { homedir as homedir2 } from "node:os";
|
|
@@ -1176,7 +1343,7 @@ var INTERRUPT_SETTLE_MS = 15e3;
|
|
|
1176
1343
|
var TASK_STOP_TIMEOUT_MS = 5e3;
|
|
1177
1344
|
var TASK_WAKE_MS = 5e3;
|
|
1178
1345
|
var CONTEXT_MIN_INTERVAL_MS = 5e3;
|
|
1179
|
-
var ClaudeSession = class extends
|
|
1346
|
+
var ClaudeSession = class extends EventEmitter2 {
|
|
1180
1347
|
id;
|
|
1181
1348
|
workspaceId;
|
|
1182
1349
|
cwd;
|
|
@@ -1484,6 +1651,8 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1484
1651
|
* умолчанию) — обычное поведение, ничего не меняется.
|
|
1485
1652
|
*/
|
|
1486
1653
|
#spawn(restarted, resumeAt) {
|
|
1654
|
+
if (this.#refuseOffRoute())
|
|
1655
|
+
return;
|
|
1487
1656
|
const options2 = {
|
|
1488
1657
|
cwd: this.cwd,
|
|
1489
1658
|
// Без этого SDK не читает с диска ничего: ни CLAUDE.md, ни кастомные
|
|
@@ -1668,6 +1837,56 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1668
1837
|
this.#query = null;
|
|
1669
1838
|
this.#resetTasks();
|
|
1670
1839
|
}
|
|
1840
|
+
/**
|
|
1841
|
+
* Погасить процесс CLI, не закрывая сессию: упал VPN (см. network-guard.ts).
|
|
1842
|
+
*
|
|
1843
|
+
* Идущий ход обрывается вместе с процессом, отложенное переподключение
|
|
1844
|
+
* отменяется, накопленные для повтора промпты забываются — повторить их сам
|
|
1845
|
+
* хост не вправе, решать человеку. Разговор цел: следующий промпт после
|
|
1846
|
+
* возвращения VPN поднимет процесс, продолжающий ту же сессию.
|
|
1847
|
+
*/
|
|
1848
|
+
suspend(reason) {
|
|
1849
|
+
if (this.#closing || this.#state === "closed")
|
|
1850
|
+
return;
|
|
1851
|
+
if (this.#reconnectTimer) {
|
|
1852
|
+
clearTimeout(this.#reconnectTimer);
|
|
1853
|
+
this.#reconnectTimer = null;
|
|
1854
|
+
}
|
|
1855
|
+
this.#pendingRetry = [];
|
|
1856
|
+
if (!this.#query) {
|
|
1857
|
+
if (this.#state === "reconnecting")
|
|
1858
|
+
this.#setState("error");
|
|
1859
|
+
return;
|
|
1860
|
+
}
|
|
1861
|
+
const busy = this.#state !== "idle";
|
|
1862
|
+
this.#teardown(reason);
|
|
1863
|
+
this.#inbox = new AsyncQueue();
|
|
1864
|
+
this.#streamMessageId = null;
|
|
1865
|
+
this.#blockCounts.clear();
|
|
1866
|
+
if (busy)
|
|
1867
|
+
this.#lastTurn = "interrupted";
|
|
1868
|
+
this.#record({
|
|
1869
|
+
kind: "error",
|
|
1870
|
+
message: `${reason}. \u041F\u0440\u043E\u0446\u0435\u0441\u0441 Claude \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D${busy ? ", \u0445\u043E\u0434 \u043F\u0440\u0435\u0440\u0432\u0430\u043D" : ""}. \u0420\u0430\u0437\u0433\u043E\u0432\u043E\u0440 \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D \u2014 \u043A\u043E\u0433\u0434\u0430 VPN \u0432\u0435\u0440\u043D\u0451\u0442\u0441\u044F, \u043E\u0442\u043F\u0440\u0430\u0432\u044C\u0442\u0435 \u043F\u0440\u043E\u043C\u043F\u0442, \u0438 \u0441\u0435\u0441\u0441\u0438\u044F \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442.`
|
|
1871
|
+
});
|
|
1872
|
+
this.#setState("error");
|
|
1873
|
+
}
|
|
1874
|
+
/**
|
|
1875
|
+
* Нет VPN — процесс не поднимаем: пишем об этом в ленту и оставляем сессию
|
|
1876
|
+
* без процесса. Следующий промпт попробует снова.
|
|
1877
|
+
*/
|
|
1878
|
+
#refuseOffRoute() {
|
|
1879
|
+
const blocked = networkGuard.blockedReason();
|
|
1880
|
+
if (!blocked)
|
|
1881
|
+
return false;
|
|
1882
|
+
this.#pendingRetry = [];
|
|
1883
|
+
this.#record({
|
|
1884
|
+
kind: "error",
|
|
1885
|
+
message: `${blocked}. \u041F\u0440\u043E\u0446\u0435\u0441\u0441 Claude \u043D\u0435 \u0437\u0430\u043F\u0443\u0449\u0435\u043D \u2014 \u043E\u0442\u043F\u0440\u0430\u0432\u044C\u0442\u0435 \u043F\u0440\u043E\u043C\u043F\u0442, \u043A\u043E\u0433\u0434\u0430 VPN \u0432\u0435\u0440\u043D\u0451\u0442\u0441\u044F.`
|
|
1886
|
+
});
|
|
1887
|
+
this.#setState("error");
|
|
1888
|
+
return true;
|
|
1889
|
+
}
|
|
1671
1890
|
// -------------------------------------------------------------------------
|
|
1672
1891
|
// Переподключение
|
|
1673
1892
|
// -------------------------------------------------------------------------
|
|
@@ -1697,6 +1916,8 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1697
1916
|
clearTimeout(this.#reconnectTimer);
|
|
1698
1917
|
this.#reconnectTimer = null;
|
|
1699
1918
|
}
|
|
1919
|
+
if (this.#refuseOffRoute())
|
|
1920
|
+
return;
|
|
1700
1921
|
this.#setState("reconnecting");
|
|
1701
1922
|
this.#teardown("\u043F\u0435\u0440\u0435\u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0435");
|
|
1702
1923
|
this.#inbox = new AsyncQueue();
|
|
@@ -1751,6 +1972,9 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1751
1972
|
sendPrompt(text, images, service = false) {
|
|
1752
1973
|
if (this.#closing)
|
|
1753
1974
|
throw new Error("session is closed");
|
|
1975
|
+
const blocked = networkGuard.blockedReason();
|
|
1976
|
+
if (blocked)
|
|
1977
|
+
throw new Error(`${blocked}. \u041F\u0440\u043E\u043C\u043F\u0442 \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D.`);
|
|
1754
1978
|
this.#record({ kind: "user.message", text, images });
|
|
1755
1979
|
if (!service && (!this.#title || this.#titleIsDefault)) {
|
|
1756
1980
|
this.#title = text.slice(0, 80);
|
|
@@ -1788,6 +2012,9 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1788
2012
|
async editPrompt(userIndex, text, images) {
|
|
1789
2013
|
if (this.#closing)
|
|
1790
2014
|
throw new Error("session is closed");
|
|
2015
|
+
const blocked = networkGuard.blockedReason();
|
|
2016
|
+
if (blocked)
|
|
2017
|
+
throw new Error(`${blocked}. \u041F\u0440\u0430\u0432\u043A\u0430 \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0430.`);
|
|
1791
2018
|
if (this.#state === "running")
|
|
1792
2019
|
await this.interrupt();
|
|
1793
2020
|
const source = this.#claudeSessionId ?? this.#options.resume;
|
|
@@ -2905,7 +3132,7 @@ function contentToText2(content) {
|
|
|
2905
3132
|
}
|
|
2906
3133
|
|
|
2907
3134
|
// ../host/dist/antigravity-session.js
|
|
2908
|
-
import { EventEmitter as
|
|
3135
|
+
import { EventEmitter as EventEmitter3 } from "node:events";
|
|
2909
3136
|
import { spawn as spawn2, execFile as execFile3 } from "node:child_process";
|
|
2910
3137
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
2911
3138
|
import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
|
|
@@ -3125,7 +3352,7 @@ function agyContextWindow(model) {
|
|
|
3125
3352
|
|
|
3126
3353
|
// ../host/dist/antigravity-session.js
|
|
3127
3354
|
var TRANSIENT = /* @__PURE__ */ new Set(["assistant.delta", "thinking.delta", "state"]);
|
|
3128
|
-
var AntigravitySession = class extends
|
|
3355
|
+
var AntigravitySession = class extends EventEmitter3 {
|
|
3129
3356
|
id;
|
|
3130
3357
|
workspaceId;
|
|
3131
3358
|
cwd;
|
|
@@ -3265,6 +3492,8 @@ var AntigravitySession = class extends EventEmitter2 {
|
|
|
3265
3492
|
}
|
|
3266
3493
|
this.#capabilities = this.#baseCapabilities();
|
|
3267
3494
|
this.emit("capabilities");
|
|
3495
|
+
if (this.#refuseOffRoute())
|
|
3496
|
+
return;
|
|
3268
3497
|
void agyModels(this.#executable).then((models) => {
|
|
3269
3498
|
if (!this.#capabilities || models.length === 0)
|
|
3270
3499
|
return;
|
|
@@ -3323,6 +3552,8 @@ var AntigravitySession = class extends EventEmitter2 {
|
|
|
3323
3552
|
#spawn() {
|
|
3324
3553
|
if (!this.#executable || this.#closing)
|
|
3325
3554
|
return;
|
|
3555
|
+
if (this.#refuseOffRoute())
|
|
3556
|
+
return;
|
|
3326
3557
|
let child;
|
|
3327
3558
|
try {
|
|
3328
3559
|
child = spawn2(this.#executable, this.#args(), {
|
|
@@ -3420,12 +3651,48 @@ var AntigravitySession = class extends EventEmitter2 {
|
|
|
3420
3651
|
reconnect(_reason) {
|
|
3421
3652
|
this.#restart();
|
|
3422
3653
|
}
|
|
3654
|
+
/**
|
|
3655
|
+
* Погасить процесс `agy`, не закрывая сессию: упал VPN (см.
|
|
3656
|
+
* network-guard.ts). Следующий промпт после его возвращения поднимет
|
|
3657
|
+
* процесс на том же разговоре.
|
|
3658
|
+
*/
|
|
3659
|
+
suspend(reason) {
|
|
3660
|
+
if (this.#closing || this.#state === "closed" || !this.#child)
|
|
3661
|
+
return;
|
|
3662
|
+
const busy = this.#pendingTurns > 0;
|
|
3663
|
+
this.#restartPending = false;
|
|
3664
|
+
this.#kill();
|
|
3665
|
+
this.#closeOpenSteps();
|
|
3666
|
+
this.#pendingTurns = 0;
|
|
3667
|
+
if (busy)
|
|
3668
|
+
this.#lastTurn = "interrupted";
|
|
3669
|
+
this.#record_({
|
|
3670
|
+
kind: "error",
|
|
3671
|
+
message: `${reason}. \u041F\u0440\u043E\u0446\u0435\u0441\u0441 Antigravity \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D${busy ? ", \u0445\u043E\u0434 \u043F\u0440\u0435\u0440\u0432\u0430\u043D" : ""}. \u0420\u0430\u0437\u0433\u043E\u0432\u043E\u0440 \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D \u2014 \u043A\u043E\u0433\u0434\u0430 VPN \u0432\u0435\u0440\u043D\u0451\u0442\u0441\u044F, \u043E\u0442\u043F\u0440\u0430\u0432\u044C\u0442\u0435 \u043F\u0440\u043E\u043C\u043F\u0442, \u0438 \u0441\u0435\u0441\u0441\u0438\u044F \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442.`
|
|
3672
|
+
});
|
|
3673
|
+
this.#setState("error");
|
|
3674
|
+
}
|
|
3675
|
+
/** Нет VPN — процесс не поднимаем, пишем об этом в ленту. */
|
|
3676
|
+
#refuseOffRoute() {
|
|
3677
|
+
const blocked = networkGuard.blockedReason();
|
|
3678
|
+
if (!blocked)
|
|
3679
|
+
return false;
|
|
3680
|
+
this.#record_({
|
|
3681
|
+
kind: "error",
|
|
3682
|
+
message: `${blocked}. \u041F\u0440\u043E\u0446\u0435\u0441\u0441 Antigravity \u043D\u0435 \u0437\u0430\u043F\u0443\u0449\u0435\u043D \u2014 \u043E\u0442\u043F\u0440\u0430\u0432\u044C\u0442\u0435 \u043F\u0440\u043E\u043C\u043F\u0442, \u043A\u043E\u0433\u0434\u0430 VPN \u0432\u0435\u0440\u043D\u0451\u0442\u0441\u044F.`
|
|
3683
|
+
});
|
|
3684
|
+
this.#setState("error");
|
|
3685
|
+
return true;
|
|
3686
|
+
}
|
|
3423
3687
|
// -------------------------------------------------------------------------
|
|
3424
3688
|
// Ввод
|
|
3425
3689
|
// -------------------------------------------------------------------------
|
|
3426
3690
|
sendPrompt(text, images) {
|
|
3427
3691
|
if (this.#closing)
|
|
3428
3692
|
throw new Error("session is closed");
|
|
3693
|
+
const blocked = networkGuard.blockedReason();
|
|
3694
|
+
if (blocked)
|
|
3695
|
+
throw new Error(`${blocked}. \u041F\u0440\u043E\u043C\u043F\u0442 \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D.`);
|
|
3429
3696
|
const command = /^\/([\w-]+)(?:\s+([\s\S]*))?$/.exec(text.trim());
|
|
3430
3697
|
if (command && AGY_CLI_COMMANDS.has(command[1]) && (!images || images.length === 0)) {
|
|
3431
3698
|
void this.#runCliCommand(text, command[1], command[2]?.trim() ?? "");
|
|
@@ -3642,7 +3909,7 @@ Attached image${paths.length > 1 ? "s" : ""} (view them with your file viewing t
|
|
|
3642
3909
|
}
|
|
3643
3910
|
/** Квота `agy` (`/usage`) — общая на аккаунт, кэш один на хост. */
|
|
3644
3911
|
async refreshUsage(force = false) {
|
|
3645
|
-
if (!this.#executable)
|
|
3912
|
+
if (!this.#executable || networkGuard.blockedReason())
|
|
3646
3913
|
return;
|
|
3647
3914
|
const usage = await agyUsage(this.#executable, force);
|
|
3648
3915
|
if (!usage)
|
|
@@ -3820,148 +4087,995 @@ Attached image${paths.length > 1 ? "s" : ""} (view them with your file viewing t
|
|
|
3820
4087
|
message: `Antigravity \u043E\u0442\u043A\u043B\u043E\u043D\u0438\u043B \u0431\u0435\u0437 \u0432\u043E\u043F\u0440\u043E\u0441\u0430: ${[...new Set(denied)].join(", ")}. \u0412 headless-\u0440\u0435\u0436\u0438\u043C\u0435 agy \u043D\u0435 \u0443\u043C\u0435\u0435\u0442 \u0441\u043F\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044C \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u0438\u0435: \u043F\u0440\u0430\u0432\u043A\u0438 \u0444\u0430\u0439\u043B\u043E\u0432 \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442 \u0440\u0435\u0436\u0438\u043C acceptEdits, \u043A\u043E\u043C\u0430\u043D\u0434\u044B \u0438 \u0432\u0441\u0451 \u043E\u0441\u0442\u0430\u043B\u044C\u043D\u043E\u0435 \u2014 bypassPermissions. \u041E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u0435 \u043A\u043E\u043C\u0430\u043D\u0434\u044B \u043C\u043E\u0436\u043D\u043E \u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044C \u043F\u0440\u0430\u0432\u0438\u043B\u043E\u043C \u0432 permissions.allow \u0444\u0430\u0439\u043B\u0430 ~/.gemini/antigravity-cli/settings.json, \u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 command(npm test).`
|
|
3821
4088
|
});
|
|
3822
4089
|
}
|
|
3823
|
-
if (!ok && result.error)
|
|
3824
|
-
this.#record_({ kind: "error", message: result.error });
|
|
3825
|
-
this.#record_({
|
|
4090
|
+
if (!ok && result.error)
|
|
4091
|
+
this.#record_({ kind: "error", message: result.error });
|
|
4092
|
+
this.#record_({
|
|
4093
|
+
kind: "result",
|
|
4094
|
+
subtype: ok ? "success" : "error",
|
|
4095
|
+
isError: !ok,
|
|
4096
|
+
durationMs: Math.round((result.duration_seconds ?? 0) * 1e3),
|
|
4097
|
+
costUsd: null,
|
|
4098
|
+
usage,
|
|
4099
|
+
contextTokens: this.#contextTokens,
|
|
4100
|
+
text: result.response ?? null
|
|
4101
|
+
});
|
|
4102
|
+
this.#emitContext();
|
|
4103
|
+
void this.refreshUsage(true);
|
|
4104
|
+
this.#lastTurn = ok ? "completed" : "failed";
|
|
4105
|
+
this.#pendingTurns = Math.max(0, this.#pendingTurns - 1);
|
|
4106
|
+
if (this.#pendingTurns > 0)
|
|
4107
|
+
return;
|
|
4108
|
+
if (this.#restartPending)
|
|
4109
|
+
this.#restart();
|
|
4110
|
+
this.#setState("idle");
|
|
4111
|
+
}
|
|
4112
|
+
/** Незакрытые шаги оборванного хода: текст — в ленту, инструменты — с ошибкой. */
|
|
4113
|
+
#closeOpenSteps() {
|
|
4114
|
+
const prefix = this.#record.conversationId ?? this.id;
|
|
4115
|
+
for (const [index, text] of this.#texts) {
|
|
4116
|
+
if (text)
|
|
4117
|
+
this.#record_({ kind: "assistant.text", text, blockId: `${prefix}:${index}` });
|
|
4118
|
+
}
|
|
4119
|
+
for (const index of this.#tools) {
|
|
4120
|
+
this.#record_({
|
|
4121
|
+
kind: "tool.result",
|
|
4122
|
+
toolUseId: `${prefix}:${index}`,
|
|
4123
|
+
content: "\u041F\u0440\u0435\u0440\u0432\u0430\u043D\u043E",
|
|
4124
|
+
isError: true
|
|
4125
|
+
});
|
|
4126
|
+
}
|
|
4127
|
+
this.#texts.clear();
|
|
4128
|
+
this.#thinking.clear();
|
|
4129
|
+
this.#tools.clear();
|
|
4130
|
+
}
|
|
4131
|
+
// -------------------------------------------------------------------------
|
|
4132
|
+
#record_(event) {
|
|
4133
|
+
this.#updatedAt = Date.now();
|
|
4134
|
+
const entry = this.journal.append(event);
|
|
4135
|
+
this.emit("event", entry);
|
|
4136
|
+
if (TRANSIENT.has(event.kind))
|
|
4137
|
+
return;
|
|
4138
|
+
this.#unsaved.push({ ts: entry.ts, event });
|
|
4139
|
+
this.#scheduleFlush();
|
|
4140
|
+
}
|
|
4141
|
+
#scheduleFlush() {
|
|
4142
|
+
if (this.#flushTimer)
|
|
4143
|
+
return;
|
|
4144
|
+
this.#flushTimer = setTimeout(() => this.#flush(), 500);
|
|
4145
|
+
this.#flushTimer.unref?.();
|
|
4146
|
+
}
|
|
4147
|
+
#flush() {
|
|
4148
|
+
if (this.#flushTimer) {
|
|
4149
|
+
clearTimeout(this.#flushTimer);
|
|
4150
|
+
this.#flushTimer = null;
|
|
4151
|
+
}
|
|
4152
|
+
const batch = this.#unsaved;
|
|
4153
|
+
this.#unsaved = [];
|
|
4154
|
+
this.#record.updatedAt = this.#updatedAt;
|
|
4155
|
+
void this.#store.appendEvents(this.id, batch);
|
|
4156
|
+
void this.#store.save(this.#record);
|
|
4157
|
+
}
|
|
4158
|
+
#setState(state) {
|
|
4159
|
+
if (this.#state === state)
|
|
4160
|
+
return;
|
|
4161
|
+
this.#state = state;
|
|
4162
|
+
this.#record_({ kind: "state", state });
|
|
4163
|
+
if (state === "idle" || state === "error" || state === "closed")
|
|
4164
|
+
this.#flush();
|
|
4165
|
+
this.emit("summary", this.summary());
|
|
4166
|
+
}
|
|
4167
|
+
};
|
|
4168
|
+
function toolInput(parameters) {
|
|
4169
|
+
const input = { ...parameters ?? {} };
|
|
4170
|
+
const pick = (...keys) => keys.map((k) => input[k]).find((v) => typeof v === "string");
|
|
4171
|
+
const file = pick("TargetFile", "AbsolutePath", "File", "FilePath", "Path");
|
|
4172
|
+
if (file && input.file_path === void 0)
|
|
4173
|
+
input.file_path = file;
|
|
4174
|
+
const command = pick("CommandLine", "Command");
|
|
4175
|
+
if (command && input.command === void 0)
|
|
4176
|
+
input.command = command;
|
|
4177
|
+
const pattern = pick("Query", "Pattern", "SearchPattern");
|
|
4178
|
+
if (pattern && input.pattern === void 0)
|
|
4179
|
+
input.pattern = pattern;
|
|
4180
|
+
const dir = pick("DirectoryPath", "SearchPath", "SearchDirectory");
|
|
4181
|
+
if (dir && input.path === void 0)
|
|
4182
|
+
input.path = dir;
|
|
4183
|
+
const url2 = pick("Url", "URL");
|
|
4184
|
+
if (url2 && input.url === void 0)
|
|
4185
|
+
input.url = url2;
|
|
4186
|
+
return input;
|
|
4187
|
+
}
|
|
4188
|
+
function formatUsage(usage) {
|
|
4189
|
+
const rows = (usage.buckets ?? []).map((b) => {
|
|
4190
|
+
const left = Math.round(100 - b.window.utilization);
|
|
4191
|
+
const reset = b.window.resetsAt ? new Date(b.window.resetsAt).toLocaleString("ru-RU") : "\u2014";
|
|
4192
|
+
return `| ${b.title || b.label} | ${left}% | ${reset} |`;
|
|
4193
|
+
});
|
|
4194
|
+
if (rows.length === 0)
|
|
4195
|
+
return "\u041A\u0432\u043E\u0442\u0430 Antigravity: \u0434\u0430\u043D\u043D\u044B\u0445 \u043D\u0435\u0442.";
|
|
4196
|
+
return ["**\u041A\u0432\u043E\u0442\u0430 Antigravity**", "", "| \u0413\u0440\u0443\u043F\u043F\u0430 | \u041E\u0441\u0442\u0430\u043B\u043E\u0441\u044C | \u041E\u0431\u043D\u0443\u043B\u0438\u0442\u0441\u044F |", "|---|---|---|", ...rows].join("\n");
|
|
4197
|
+
}
|
|
4198
|
+
function formatCommandReply(name, response) {
|
|
4199
|
+
const text = response.replace(/\s+$/, "");
|
|
4200
|
+
if (!text)
|
|
4201
|
+
return `/${name}: \u043F\u0443\u0441\u0442\u043E`;
|
|
4202
|
+
const lines = text.split(/\r?\n/);
|
|
4203
|
+
if (lines.every((l) => l.includes(" "))) {
|
|
4204
|
+
return lines.map((l) => {
|
|
4205
|
+
const [head, ...rest] = l.split(" ");
|
|
4206
|
+
return `- **${head}** \u2014 ${rest.join(" ")}`;
|
|
4207
|
+
}).join("\n");
|
|
4208
|
+
}
|
|
4209
|
+
return text;
|
|
4210
|
+
}
|
|
4211
|
+
function agyEffort(effort) {
|
|
4212
|
+
if (!effort)
|
|
4213
|
+
return null;
|
|
4214
|
+
return effort === "xhigh" ? "high" : effort;
|
|
4215
|
+
}
|
|
4216
|
+
function describe2(error) {
|
|
4217
|
+
return error instanceof Error ? error.message : String(error);
|
|
4218
|
+
}
|
|
4219
|
+
|
|
4220
|
+
// ../host/dist/codex-session.js
|
|
4221
|
+
import { EventEmitter as EventEmitter5 } from "node:events";
|
|
4222
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
4223
|
+
|
|
4224
|
+
// ../host/dist/codex-cli.js
|
|
4225
|
+
import { accessSync, constants as constants2, existsSync as existsSync2, readFileSync, statSync } from "node:fs";
|
|
4226
|
+
import { homedir as homedir4 } from "node:os";
|
|
4227
|
+
import path8 from "node:path";
|
|
4228
|
+
var CODEX_MISSING_HINT = "Codex CLI \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D. \u0423\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u0435: npm install -g @openai/codex, \u0437\u0430\u0442\u0435\u043C \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u0435 codex login. \u0415\u0441\u043B\u0438 CLI \u0443\u0436\u0435 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D, \u0437\u0430\u0434\u0430\u0439\u0442\u0435 CCV_CODEX_PATH \u0438 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u0435 ccv.";
|
|
4229
|
+
function codexLauncher() {
|
|
4230
|
+
const forced = process.env.CCV_CODEX_PATH;
|
|
4231
|
+
if (forced)
|
|
4232
|
+
return launcherFor(path8.resolve(forced));
|
|
4233
|
+
const dirs = (process.env.PATH ?? "").split(path8.delimiter).filter(Boolean);
|
|
4234
|
+
if (process.platform === "win32") {
|
|
4235
|
+
const local = process.env.LOCALAPPDATA ?? path8.join(homedir4(), "AppData", "Local");
|
|
4236
|
+
dirs.push(path8.join(local, "Programs", "OpenAI", "Codex", "bin"));
|
|
4237
|
+
dirs.push(path8.join(process.env.APPDATA ?? path8.join(homedir4(), "AppData", "Roaming"), "npm"));
|
|
4238
|
+
} else {
|
|
4239
|
+
dirs.push(path8.join(homedir4(), ".local", "bin"), "/usr/local/bin", "/opt/homebrew/bin");
|
|
4240
|
+
}
|
|
4241
|
+
for (const dir of dirs) {
|
|
4242
|
+
for (const name of process.platform === "win32" ? ["codex.exe", "codex.cmd", "codex"] : ["codex"]) {
|
|
4243
|
+
const found = launcherFor(path8.join(dir.replace(/^"|"$/g, ""), name));
|
|
4244
|
+
if (found)
|
|
4245
|
+
return found;
|
|
4246
|
+
}
|
|
4247
|
+
}
|
|
4248
|
+
return null;
|
|
4249
|
+
}
|
|
4250
|
+
function launcherFor(file) {
|
|
4251
|
+
try {
|
|
4252
|
+
if (!statSync(file).isFile())
|
|
4253
|
+
return null;
|
|
4254
|
+
if (/\.[cm]?js$/i.test(file))
|
|
4255
|
+
return { file: process.execPath, prefix: [file] };
|
|
4256
|
+
if (process.platform === "win32" && /\.(cmd|bat|ps1)$/i.test(file)) {
|
|
4257
|
+
const root = path8.dirname(file);
|
|
4258
|
+
const candidates = [path8.join(root, "node_modules", "@openai", "codex", "bin", "codex.js")];
|
|
4259
|
+
const shim = readFileSync(file, "utf8");
|
|
4260
|
+
for (const match of shim.matchAll(/(?:%dp0%|%~dp0|\$basedir)[\\/]([^"\r\n]+?\.[cm]?js)/gi)) {
|
|
4261
|
+
candidates.push(path8.resolve(root, match[1]));
|
|
4262
|
+
}
|
|
4263
|
+
const script = candidates.find((candidate) => existsSync2(candidate));
|
|
4264
|
+
return script ? { file: process.execPath, prefix: [script] } : null;
|
|
4265
|
+
}
|
|
4266
|
+
if (process.platform === "win32" && !/\.exe$/i.test(file))
|
|
4267
|
+
return null;
|
|
4268
|
+
accessSync(file, constants2.X_OK);
|
|
4269
|
+
return { file, prefix: [] };
|
|
4270
|
+
} catch {
|
|
4271
|
+
return null;
|
|
4272
|
+
}
|
|
4273
|
+
}
|
|
4274
|
+
|
|
4275
|
+
// ../host/dist/codex-rpc.js
|
|
4276
|
+
import { EventEmitter as EventEmitter4 } from "node:events";
|
|
4277
|
+
import { execFile as execFile4, spawn as spawn3 } from "node:child_process";
|
|
4278
|
+
import { createInterface as createInterface2 } from "node:readline";
|
|
4279
|
+
var CodexRpc = class extends EventEmitter4 {
|
|
4280
|
+
#child;
|
|
4281
|
+
#pending = /* @__PURE__ */ new Map();
|
|
4282
|
+
#nextId = 0;
|
|
4283
|
+
#closed = false;
|
|
4284
|
+
#stderr = "";
|
|
4285
|
+
constructor(launcher, cwd) {
|
|
4286
|
+
super();
|
|
4287
|
+
this.#child = spawn3(launcher.file, [...launcher.prefix, "app-server", "--listen", "stdio://"], {
|
|
4288
|
+
cwd,
|
|
4289
|
+
windowsHide: true,
|
|
4290
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
4291
|
+
env: { ...process.env, NO_COLOR: "1" }
|
|
4292
|
+
});
|
|
4293
|
+
createInterface2({ input: this.#child.stdout }).on("line", (line) => {
|
|
4294
|
+
let message;
|
|
4295
|
+
try {
|
|
4296
|
+
message = JSON.parse(line);
|
|
4297
|
+
} catch {
|
|
4298
|
+
return;
|
|
4299
|
+
}
|
|
4300
|
+
if (!message || typeof message !== "object")
|
|
4301
|
+
return;
|
|
4302
|
+
if (message.method) {
|
|
4303
|
+
this.emit("message", message);
|
|
4304
|
+
return;
|
|
4305
|
+
}
|
|
4306
|
+
const pending = typeof message.id === "number" ? this.#pending.get(message.id) : void 0;
|
|
4307
|
+
if (!pending)
|
|
4308
|
+
return;
|
|
4309
|
+
this.#pending.delete(message.id);
|
|
4310
|
+
clearTimeout(pending.timer);
|
|
4311
|
+
if (message.error)
|
|
4312
|
+
pending.reject(new Error(message.error.message));
|
|
4313
|
+
else
|
|
4314
|
+
pending.resolve(message.result);
|
|
4315
|
+
});
|
|
4316
|
+
this.#child.stderr.on("data", (chunk) => {
|
|
4317
|
+
this.#stderr = (this.#stderr + chunk.toString()).slice(-4e3);
|
|
4318
|
+
this.emit("stderr", chunk.toString());
|
|
4319
|
+
});
|
|
4320
|
+
this.#child.stdin.on("error", (error) => this.#finish(error));
|
|
4321
|
+
this.#child.on("error", (error) => this.#finish(error));
|
|
4322
|
+
this.#child.on("exit", (code) => this.#finish(new Error(`Codex \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0441\u044F (${code ?? "signal"}). ${this.#stderr.trim()}`)));
|
|
4323
|
+
}
|
|
4324
|
+
request(method, params = {}) {
|
|
4325
|
+
if (this.#closed)
|
|
4326
|
+
return Promise.reject(new Error("\u0421\u043E\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u0435 \u0441 Codex \u0437\u0430\u043A\u0440\u044B\u0442\u043E"));
|
|
4327
|
+
const id = ++this.#nextId;
|
|
4328
|
+
return new Promise((resolve, reject) => {
|
|
4329
|
+
const timer = setTimeout(() => {
|
|
4330
|
+
this.#pending.delete(id);
|
|
4331
|
+
reject(new Error(`Codex \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u043D\u0430 ${method} \u0437\u0430 60 \u0441\u0435\u043A\u0443\u043D\u0434`));
|
|
4332
|
+
}, 6e4);
|
|
4333
|
+
this.#pending.set(id, { resolve: (value) => resolve(value), reject, timer });
|
|
4334
|
+
this.write({ id, method, params });
|
|
4335
|
+
});
|
|
4336
|
+
}
|
|
4337
|
+
write(message) {
|
|
4338
|
+
if (!this.#closed)
|
|
4339
|
+
this.#child.stdin.write(JSON.stringify(message) + "\n");
|
|
4340
|
+
}
|
|
4341
|
+
close() {
|
|
4342
|
+
this.#finish(new Error("\u0421\u043E\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u0435 \u0441 Codex \u0437\u0430\u043A\u0440\u044B\u0442\u043E"), false);
|
|
4343
|
+
this.#child.stdin.end();
|
|
4344
|
+
const child = this.#child;
|
|
4345
|
+
if (child.exitCode !== null)
|
|
4346
|
+
return;
|
|
4347
|
+
if (process.platform === "win32" && child.pid) {
|
|
4348
|
+
execFile4("taskkill", ["/pid", String(child.pid), "/T", "/F"], { windowsHide: true }, () => {
|
|
4349
|
+
if (child.exitCode === null)
|
|
4350
|
+
child.kill();
|
|
4351
|
+
});
|
|
4352
|
+
} else
|
|
4353
|
+
child.kill("SIGTERM");
|
|
4354
|
+
}
|
|
4355
|
+
#finish(error, notify = true) {
|
|
4356
|
+
if (this.#closed)
|
|
4357
|
+
return;
|
|
4358
|
+
this.#closed = true;
|
|
4359
|
+
for (const pending of this.#pending.values()) {
|
|
4360
|
+
clearTimeout(pending.timer);
|
|
4361
|
+
pending.reject(error);
|
|
4362
|
+
}
|
|
4363
|
+
this.#pending.clear();
|
|
4364
|
+
if (notify)
|
|
4365
|
+
this.emit("exit", error);
|
|
4366
|
+
}
|
|
4367
|
+
};
|
|
4368
|
+
|
|
4369
|
+
// ../host/dist/codex-session.js
|
|
4370
|
+
function codexPolicy(mode) {
|
|
4371
|
+
switch (mode) {
|
|
4372
|
+
case "default":
|
|
4373
|
+
return { approvalPolicy: "untrusted", sandbox: "workspace-write" };
|
|
4374
|
+
case "acceptEdits":
|
|
4375
|
+
return { approvalPolicy: "on-request", sandbox: "workspace-write" };
|
|
4376
|
+
case "plan":
|
|
4377
|
+
return { approvalPolicy: "untrusted", sandbox: "read-only" };
|
|
4378
|
+
case "bypassPermissions":
|
|
4379
|
+
return { approvalPolicy: "never", sandbox: "danger-full-access" };
|
|
4380
|
+
default:
|
|
4381
|
+
throw new Error(`Codex \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0440\u0435\u0436\u0438\u043C \u043F\u0440\u0430\u0432 \xAB${mode}\xBB`);
|
|
4382
|
+
}
|
|
4383
|
+
}
|
|
4384
|
+
function codexUsage(limits) {
|
|
4385
|
+
const window = (value) => value ? {
|
|
4386
|
+
utilization: Math.min(100, Math.max(0, value.usedPercent)),
|
|
4387
|
+
resetsAt: value.resetsAt ? new Date(value.resetsAt * 1e3).toISOString() : ""
|
|
4388
|
+
} : null;
|
|
4389
|
+
return {
|
|
4390
|
+
available: !!(limits.primary || limits.secondary),
|
|
4391
|
+
plan: limits.planType ?? "",
|
|
4392
|
+
fiveHour: window(limits.primary),
|
|
4393
|
+
sevenDay: window(limits.secondary),
|
|
4394
|
+
sevenDayOpus: null,
|
|
4395
|
+
fetchedAt: Date.now()
|
|
4396
|
+
};
|
|
4397
|
+
}
|
|
4398
|
+
var CodexSession = class extends EventEmitter5 {
|
|
4399
|
+
provider = "codex";
|
|
4400
|
+
id;
|
|
4401
|
+
workspaceId;
|
|
4402
|
+
cwd;
|
|
4403
|
+
journal = new Journal();
|
|
4404
|
+
#store;
|
|
4405
|
+
#record;
|
|
4406
|
+
#history = Promise.resolve([]);
|
|
4407
|
+
#rpc = null;
|
|
4408
|
+
#ready = Promise.resolve();
|
|
4409
|
+
#state = "starting";
|
|
4410
|
+
#lastTurn = null;
|
|
4411
|
+
#capabilities = null;
|
|
4412
|
+
#usage = null;
|
|
4413
|
+
#context = null;
|
|
4414
|
+
#turnId = null;
|
|
4415
|
+
#turnStartedAt = 0;
|
|
4416
|
+
#turnUsage = null;
|
|
4417
|
+
#totalUsage = null;
|
|
4418
|
+
#turnUsageBaseline = null;
|
|
4419
|
+
#interruptRequested = false;
|
|
4420
|
+
#busy = false;
|
|
4421
|
+
#startingTurn = false;
|
|
4422
|
+
#generation = 0;
|
|
4423
|
+
#queue = [];
|
|
4424
|
+
#pending = /* @__PURE__ */ new Map();
|
|
4425
|
+
#texts = /* @__PURE__ */ new Map();
|
|
4426
|
+
#tools = /* @__PURE__ */ new Set();
|
|
4427
|
+
#items = /* @__PURE__ */ new Map();
|
|
4428
|
+
#titleIsDefault;
|
|
4429
|
+
#unsaved = [];
|
|
4430
|
+
#flushTimer = null;
|
|
4431
|
+
constructor(options2) {
|
|
4432
|
+
super();
|
|
4433
|
+
this.workspaceId = options2.workspaceId;
|
|
4434
|
+
this.cwd = options2.cwd;
|
|
4435
|
+
this.#store = options2.store;
|
|
4436
|
+
this.#record = options2.record ? { ...options2.record } : {
|
|
4437
|
+
id: randomUUID3(),
|
|
4438
|
+
conversationId: null,
|
|
4439
|
+
workspaceId: options2.workspaceId,
|
|
4440
|
+
cwd: options2.cwd,
|
|
4441
|
+
title: options2.defaultTitle ?? null,
|
|
4442
|
+
model: options2.model ?? null,
|
|
4443
|
+
permissionMode: options2.permissionMode ?? "default",
|
|
4444
|
+
effort: null,
|
|
4445
|
+
createdAt: Date.now(),
|
|
4446
|
+
updatedAt: Date.now()
|
|
4447
|
+
};
|
|
4448
|
+
if (options2.permissionMode)
|
|
4449
|
+
this.#record.permissionMode = options2.permissionMode;
|
|
4450
|
+
if (options2.model)
|
|
4451
|
+
this.#record.model = options2.model;
|
|
4452
|
+
this.id = this.#record.id;
|
|
4453
|
+
this.#context = this.#record.context ?? null;
|
|
4454
|
+
if (!options2.record?.conversationId) {
|
|
4455
|
+
this.#totalUsage = { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheCreationTokens: 0 };
|
|
4456
|
+
}
|
|
4457
|
+
this.#titleIsDefault = !options2.record;
|
|
4458
|
+
}
|
|
4459
|
+
get state() {
|
|
4460
|
+
return this.#state;
|
|
4461
|
+
}
|
|
4462
|
+
get capabilities() {
|
|
4463
|
+
return this.#capabilities;
|
|
4464
|
+
}
|
|
4465
|
+
get claudeSessionId() {
|
|
4466
|
+
return this.id;
|
|
4467
|
+
}
|
|
4468
|
+
get parentSessionId() {
|
|
4469
|
+
return null;
|
|
4470
|
+
}
|
|
4471
|
+
get usage() {
|
|
4472
|
+
return this.#usage;
|
|
4473
|
+
}
|
|
4474
|
+
get context() {
|
|
4475
|
+
return this.#context;
|
|
4476
|
+
}
|
|
4477
|
+
history() {
|
|
4478
|
+
return this.#history;
|
|
4479
|
+
}
|
|
4480
|
+
summary() {
|
|
4481
|
+
return {
|
|
4482
|
+
id: this.id,
|
|
4483
|
+
provider: this.provider,
|
|
4484
|
+
claudeSessionId: this.id,
|
|
4485
|
+
workspaceId: this.workspaceId,
|
|
4486
|
+
cwd: this.cwd,
|
|
4487
|
+
parentSessionId: null,
|
|
4488
|
+
title: this.#record.title,
|
|
4489
|
+
state: this.#state,
|
|
4490
|
+
lastTurn: this.#lastTurn,
|
|
4491
|
+
backgroundWork: false,
|
|
4492
|
+
tasks: [],
|
|
4493
|
+
model: this.#record.model,
|
|
4494
|
+
permissionMode: this.#record.permissionMode,
|
|
4495
|
+
effort: this.#record.effort,
|
|
4496
|
+
ultracode: false,
|
|
4497
|
+
thinking: true,
|
|
4498
|
+
switchModelsOnFlag: false,
|
|
4499
|
+
createdAt: this.#record.createdAt,
|
|
4500
|
+
updatedAt: this.#record.updatedAt,
|
|
4501
|
+
lastSeq: this.journal.lastSeq,
|
|
4502
|
+
totalCostUsd: 0
|
|
4503
|
+
};
|
|
4504
|
+
}
|
|
4505
|
+
start() {
|
|
4506
|
+
this.#history = this.#store.readEvents(this.id).then((entries) => {
|
|
4507
|
+
const pending = /* @__PURE__ */ new Set();
|
|
4508
|
+
for (const { event } of entries) {
|
|
4509
|
+
if (event.kind === "permission.request")
|
|
4510
|
+
pending.add(event.requestId);
|
|
4511
|
+
if (event.kind === "permission.resolved")
|
|
4512
|
+
pending.delete(event.requestId);
|
|
4513
|
+
}
|
|
4514
|
+
for (const requestId of pending)
|
|
4515
|
+
entries.push({ seq: entries.length + 1, ts: Date.now(), event: {
|
|
4516
|
+
kind: "permission.resolved",
|
|
4517
|
+
requestId,
|
|
4518
|
+
decision: "deny",
|
|
4519
|
+
reason: "\u041F\u0440\u0435\u0434\u044B\u0434\u0443\u0449\u0438\u0439 \u043F\u0440\u043E\u0446\u0435\u0441\u0441 Codex \u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043D"
|
|
4520
|
+
} });
|
|
4521
|
+
return entries;
|
|
4522
|
+
});
|
|
4523
|
+
this.#launch();
|
|
4524
|
+
}
|
|
4525
|
+
#launch() {
|
|
4526
|
+
const generation = ++this.#generation;
|
|
4527
|
+
this.#ready = this.#connect(generation);
|
|
4528
|
+
void this.#ready.catch((error) => {
|
|
4529
|
+
if (generation === this.#generation)
|
|
4530
|
+
this.#fail(error);
|
|
4531
|
+
});
|
|
4532
|
+
}
|
|
4533
|
+
async #connect(generation) {
|
|
4534
|
+
await this.#history;
|
|
4535
|
+
await this.#store.save(this.#record);
|
|
4536
|
+
if (generation !== this.#generation)
|
|
4537
|
+
return;
|
|
4538
|
+
const reason = networkGuard.blockedReason();
|
|
4539
|
+
if (reason)
|
|
4540
|
+
throw new Error(reason);
|
|
4541
|
+
const launcher = codexLauncher();
|
|
4542
|
+
if (!launcher)
|
|
4543
|
+
throw new Error(CODEX_MISSING_HINT);
|
|
4544
|
+
const rpc = new CodexRpc(launcher, this.cwd);
|
|
4545
|
+
this.#rpc = rpc;
|
|
4546
|
+
rpc.on("message", (message) => {
|
|
4547
|
+
if (this.#rpc === rpc)
|
|
4548
|
+
this.#message(message);
|
|
4549
|
+
});
|
|
4550
|
+
rpc.on("stderr", (data) => this.emit("stderr", data));
|
|
4551
|
+
rpc.on("exit", (error) => {
|
|
4552
|
+
if (this.#rpc === rpc)
|
|
4553
|
+
this.#fail(error);
|
|
4554
|
+
});
|
|
4555
|
+
await rpc.request("initialize", { clientInfo: { name: "ccv", title: "Claude Code Visual", version: "0.1.0" } });
|
|
4556
|
+
if (this.#rpc !== rpc)
|
|
4557
|
+
return;
|
|
4558
|
+
rpc.write({ method: "initialized", params: {} });
|
|
4559
|
+
const policy = codexPolicy(this.#record.permissionMode);
|
|
4560
|
+
const result = await rpc.request(this.#record.conversationId ? "thread/resume" : "thread/start", {
|
|
4561
|
+
...this.#record.conversationId ? { threadId: this.#record.conversationId } : {},
|
|
4562
|
+
cwd: this.cwd,
|
|
4563
|
+
...this.#record.model ? { model: this.#record.model } : {},
|
|
4564
|
+
approvalPolicy: policy.approvalPolicy,
|
|
4565
|
+
sandbox: policy.sandbox
|
|
4566
|
+
});
|
|
4567
|
+
if (this.#rpc !== rpc)
|
|
4568
|
+
return;
|
|
4569
|
+
this.#record.conversationId = result.thread.id;
|
|
4570
|
+
this.#record.model = result.model;
|
|
4571
|
+
if (!this.#record.effort && result.reasoningEffort)
|
|
4572
|
+
this.#record.effort = result.reasoningEffort;
|
|
4573
|
+
await this.#store.save(this.#record);
|
|
4574
|
+
if (this.#rpc !== rpc)
|
|
4575
|
+
return;
|
|
4576
|
+
this.#capabilities = {
|
|
4577
|
+
cwd: this.cwd,
|
|
4578
|
+
model: result.model,
|
|
4579
|
+
permissionMode: this.#record.permissionMode,
|
|
4580
|
+
slashCommands: [],
|
|
4581
|
+
commands: [],
|
|
4582
|
+
models: [],
|
|
4583
|
+
tools: ["Bash", "apply_patch", "web_search"],
|
|
4584
|
+
agents: [],
|
|
4585
|
+
mcpServers: [],
|
|
4586
|
+
flags: []
|
|
4587
|
+
};
|
|
4588
|
+
this.emit("capabilities");
|
|
4589
|
+
this.#setState("idle");
|
|
4590
|
+
void this.#loadModels(rpc).catch((error) => {
|
|
4591
|
+
if (this.#rpc === rpc)
|
|
4592
|
+
this.#notice(error);
|
|
4593
|
+
});
|
|
4594
|
+
void this.refreshUsage();
|
|
4595
|
+
}
|
|
4596
|
+
async #loadModels(rpc) {
|
|
4597
|
+
const models = [];
|
|
4598
|
+
let cursor = null;
|
|
4599
|
+
do {
|
|
4600
|
+
const page = await rpc.request("model/list", { cursor, limit: 100 });
|
|
4601
|
+
for (const model of page.data) {
|
|
4602
|
+
const levels = (model.supportedReasoningEfforts ?? []).map((e) => e.reasoningEffort).filter((e) => ["low", "medium", "high", "xhigh", "max"].includes(e));
|
|
4603
|
+
models.push({
|
|
4604
|
+
value: model.model,
|
|
4605
|
+
resolvedModel: model.model,
|
|
4606
|
+
displayName: model.displayName,
|
|
4607
|
+
description: model.description,
|
|
4608
|
+
supportsEffort: levels.length > 0,
|
|
4609
|
+
effortLevels: levels,
|
|
4610
|
+
supportsThinking: false,
|
|
4611
|
+
supportsAutoMode: false
|
|
4612
|
+
});
|
|
4613
|
+
}
|
|
4614
|
+
cursor = page.nextCursor;
|
|
4615
|
+
} while (cursor && this.#rpc === rpc);
|
|
4616
|
+
if (this.#capabilities && this.#rpc === rpc) {
|
|
4617
|
+
this.#capabilities.models = models;
|
|
4618
|
+
this.emit("capabilities");
|
|
4619
|
+
}
|
|
4620
|
+
}
|
|
4621
|
+
sendPrompt(text, images) {
|
|
4622
|
+
const reason = networkGuard.blockedReason();
|
|
4623
|
+
if (reason) {
|
|
4624
|
+
this.#notice(new Error(reason));
|
|
4625
|
+
return;
|
|
4626
|
+
}
|
|
4627
|
+
if (this.#state === "closed" || this.#state === "error") {
|
|
4628
|
+
this.#notice(new Error("\u041F\u0435\u0440\u0435\u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0438\u0442\u0435 \u0441\u0435\u0441\u0441\u0438\u044E Codex \u043F\u0435\u0440\u0435\u0434 \u043E\u0442\u043F\u0440\u0430\u0432\u043A\u043E\u0439"));
|
|
4629
|
+
return;
|
|
4630
|
+
}
|
|
4631
|
+
if (!text.trim() && !images?.length)
|
|
4632
|
+
return;
|
|
4633
|
+
this.#append({ kind: "user.message", text, ...images?.length ? { images } : {} });
|
|
4634
|
+
if (this.#titleIsDefault) {
|
|
4635
|
+
this.#record.title = text.trim().split("\n")[0].slice(0, 100) || "Codex";
|
|
4636
|
+
this.#titleIsDefault = false;
|
|
4637
|
+
this.emit("summary", this.summary());
|
|
4638
|
+
}
|
|
4639
|
+
this.#queue.push({ text, images });
|
|
4640
|
+
void this.#drain();
|
|
4641
|
+
}
|
|
4642
|
+
async #drain() {
|
|
4643
|
+
if (this.#busy || this.#startingTurn || !this.#queue.length)
|
|
4644
|
+
return;
|
|
4645
|
+
const generation = this.#generation;
|
|
4646
|
+
this.#busy = true;
|
|
4647
|
+
this.#startingTurn = true;
|
|
4648
|
+
try {
|
|
4649
|
+
await this.#ready;
|
|
4650
|
+
if (generation !== this.#generation)
|
|
4651
|
+
return;
|
|
4652
|
+
const prompt = this.#queue.shift();
|
|
4653
|
+
if (!prompt || !this.#rpc || this.#state === "closed" || this.#state === "error") {
|
|
4654
|
+
this.#busy = false;
|
|
4655
|
+
return;
|
|
4656
|
+
}
|
|
4657
|
+
const reason = networkGuard.blockedReason();
|
|
4658
|
+
if (reason)
|
|
4659
|
+
throw new Error(reason);
|
|
4660
|
+
this.#turnStartedAt = Date.now();
|
|
4661
|
+
this.#turnUsage = null;
|
|
4662
|
+
this.#turnUsageBaseline = this.#totalUsage;
|
|
4663
|
+
this.#lastTurn = null;
|
|
4664
|
+
this.#setState("running");
|
|
4665
|
+
const policy = codexPolicy(this.#record.permissionMode);
|
|
4666
|
+
const sandboxPolicy = this.#record.permissionMode === "bypassPermissions" ? { type: "dangerFullAccess" } : this.#record.permissionMode === "plan" ? { type: "readOnly", networkAccess: false } : {
|
|
4667
|
+
type: "workspaceWrite",
|
|
4668
|
+
writableRoots: [this.cwd],
|
|
4669
|
+
networkAccess: false,
|
|
4670
|
+
excludeTmpdirEnvVar: false,
|
|
4671
|
+
excludeSlashTmp: false
|
|
4672
|
+
};
|
|
4673
|
+
const result = await this.#rpc.request("turn/start", {
|
|
4674
|
+
threadId: this.#record.conversationId,
|
|
4675
|
+
cwd: this.cwd,
|
|
4676
|
+
input: [{ type: "text", text: prompt.text, text_elements: [] }, ...(prompt.images ?? []).map((image) => ({
|
|
4677
|
+
type: "image",
|
|
4678
|
+
url: `data:${image.mediaType};base64,${image.data}`
|
|
4679
|
+
}))],
|
|
4680
|
+
model: this.#record.model,
|
|
4681
|
+
effort: this.#record.effort,
|
|
4682
|
+
approvalPolicy: policy.approvalPolicy,
|
|
4683
|
+
sandboxPolicy
|
|
4684
|
+
});
|
|
4685
|
+
if (this.#busy)
|
|
4686
|
+
this.#turnId = result.turn.id;
|
|
4687
|
+
if (this.#interruptRequested && this.#turnId)
|
|
4688
|
+
await this.interrupt();
|
|
4689
|
+
} catch (error) {
|
|
4690
|
+
if (generation === this.#generation)
|
|
4691
|
+
this.#fail(error);
|
|
4692
|
+
} finally {
|
|
4693
|
+
if (generation === this.#generation) {
|
|
4694
|
+
this.#startingTurn = false;
|
|
4695
|
+
if (!this.#busy)
|
|
4696
|
+
void this.#drain();
|
|
4697
|
+
}
|
|
4698
|
+
}
|
|
4699
|
+
}
|
|
4700
|
+
#message(message) {
|
|
4701
|
+
const p = message.params ?? {};
|
|
4702
|
+
if (message.id !== void 0 && message.method) {
|
|
4703
|
+
this.#request(message.id, message.method, p);
|
|
4704
|
+
return;
|
|
4705
|
+
}
|
|
4706
|
+
if (p.threadId && p.threadId !== this.#record.conversationId)
|
|
4707
|
+
return;
|
|
4708
|
+
switch (message.method) {
|
|
4709
|
+
case "turn/started": {
|
|
4710
|
+
const turn = p.turn;
|
|
4711
|
+
this.#turnId = turn.id;
|
|
4712
|
+
break;
|
|
4713
|
+
}
|
|
4714
|
+
case "item/agentMessage/delta":
|
|
4715
|
+
case "item/reasoning/summaryTextDelta": {
|
|
4716
|
+
const id = String(p.itemId);
|
|
4717
|
+
const thinking = message.method !== "item/agentMessage/delta";
|
|
4718
|
+
const key = `${id}:${thinking ? "thinking" : "text"}`;
|
|
4719
|
+
const delta = String(p.delta ?? "");
|
|
4720
|
+
const block = this.#texts.get(key) ?? { text: "", thinking };
|
|
4721
|
+
block.text += delta;
|
|
4722
|
+
this.#texts.set(key, block);
|
|
4723
|
+
this.#append({ kind: thinking ? "thinking.delta" : "assistant.delta", blockId: key, text: delta });
|
|
4724
|
+
break;
|
|
4725
|
+
}
|
|
4726
|
+
case "item/started":
|
|
4727
|
+
this.#item(p.item, false);
|
|
4728
|
+
break;
|
|
4729
|
+
case "item/completed":
|
|
4730
|
+
this.#item(p.item, true);
|
|
4731
|
+
break;
|
|
4732
|
+
case "thread/tokenUsage/updated": {
|
|
4733
|
+
const usage = p.tokenUsage;
|
|
4734
|
+
if (usage?.last && usage.modelContextWindow) {
|
|
4735
|
+
this.#context = { tokens: usage.last.totalTokens, maxTokens: usage.modelContextWindow, fetchedAt: Date.now() };
|
|
4736
|
+
this.#record.context = this.#context;
|
|
4737
|
+
this.emit("context", this.#context);
|
|
4738
|
+
}
|
|
4739
|
+
if (usage?.total) {
|
|
4740
|
+
const total = {
|
|
4741
|
+
inputTokens: usage.total.inputTokens,
|
|
4742
|
+
outputTokens: usage.total.outputTokens,
|
|
4743
|
+
cacheReadTokens: usage.total.cachedInputTokens ?? 0,
|
|
4744
|
+
cacheCreationTokens: usage.total.cacheWriteInputTokens ?? 0
|
|
4745
|
+
};
|
|
4746
|
+
if (this.#busy && this.#turnUsageBaseline) {
|
|
4747
|
+
const baseline = this.#turnUsageBaseline;
|
|
4748
|
+
this.#turnUsage = {
|
|
4749
|
+
inputTokens: Math.max(0, total.inputTokens - baseline.inputTokens),
|
|
4750
|
+
outputTokens: Math.max(0, total.outputTokens - baseline.outputTokens),
|
|
4751
|
+
cacheReadTokens: Math.max(0, total.cacheReadTokens - baseline.cacheReadTokens),
|
|
4752
|
+
cacheCreationTokens: Math.max(0, total.cacheCreationTokens - baseline.cacheCreationTokens)
|
|
4753
|
+
};
|
|
4754
|
+
}
|
|
4755
|
+
this.#totalUsage = total;
|
|
4756
|
+
}
|
|
4757
|
+
break;
|
|
4758
|
+
}
|
|
4759
|
+
case "account/rateLimits/updated":
|
|
4760
|
+
if (p.rateLimits)
|
|
4761
|
+
this.#setUsage(p.rateLimits);
|
|
4762
|
+
break;
|
|
4763
|
+
case "serverRequest/resolved": {
|
|
4764
|
+
const requestId = String(p.requestId);
|
|
4765
|
+
if (this.#pending.delete(requestId)) {
|
|
4766
|
+
this.#append({ kind: "permission.resolved", requestId, decision: "deny", reason: "\u0417\u0430\u043F\u0440\u043E\u0441 \u0437\u0430\u043A\u0440\u044B\u0442 Codex" });
|
|
4767
|
+
this.#setState(this.#pending.size ? "waiting_permission" : "running");
|
|
4768
|
+
}
|
|
4769
|
+
break;
|
|
4770
|
+
}
|
|
4771
|
+
case "turn/completed": {
|
|
4772
|
+
const turn = p.turn;
|
|
4773
|
+
this.#lastTurn = turn.status === "completed" ? "completed" : turn.status === "interrupted" ? "interrupted" : "failed";
|
|
4774
|
+
if (turn.error?.message)
|
|
4775
|
+
this.#notice(new Error(turn.error.message));
|
|
4776
|
+
this.#finishTurn(turn.status, turn.durationMs);
|
|
4777
|
+
void this.refreshUsage();
|
|
4778
|
+
break;
|
|
4779
|
+
}
|
|
4780
|
+
case "error": {
|
|
4781
|
+
const error = p.error;
|
|
4782
|
+
this.#notice(new Error(error?.message ?? "\u041E\u0448\u0438\u0431\u043A\u0430 Codex"));
|
|
4783
|
+
break;
|
|
4784
|
+
}
|
|
4785
|
+
}
|
|
4786
|
+
}
|
|
4787
|
+
#item(item, done) {
|
|
4788
|
+
if (!item)
|
|
4789
|
+
return;
|
|
4790
|
+
this.#items.set(item.id, item);
|
|
4791
|
+
if (item.type === "agentMessage" || item.type === "reasoning" || item.type === "plan") {
|
|
4792
|
+
if (!done)
|
|
4793
|
+
return;
|
|
4794
|
+
const thinking = item.type === "reasoning";
|
|
4795
|
+
const key = `${item.id}:${thinking ? "thinking" : "text"}`;
|
|
4796
|
+
const text = item.text ?? item.summary?.join("\n") ?? this.#texts.get(key)?.text ?? "";
|
|
4797
|
+
this.#texts.delete(key);
|
|
4798
|
+
if (text)
|
|
4799
|
+
this.#append({ kind: thinking ? "assistant.thinking" : "assistant.text", text, blockId: key });
|
|
4800
|
+
return;
|
|
4801
|
+
}
|
|
4802
|
+
if (!["commandExecution", "fileChange", "mcpToolCall", "webSearch", "imageView", "collabAgentToolCall"].includes(item.type))
|
|
4803
|
+
return;
|
|
4804
|
+
if (!this.#tools.has(item.id)) {
|
|
4805
|
+
this.#tools.add(item.id);
|
|
4806
|
+
const name = item.type === "commandExecution" ? "Bash" : item.type === "fileChange" ? "apply_patch" : item.type === "mcpToolCall" ? `mcp__${item.server}__${item.tool}` : item.type;
|
|
4807
|
+
const input = item.type === "commandExecution" ? { command: item.command, cwd: item.cwd } : item.type === "fileChange" ? { changes: item.changes, file_path: item.changes?.[0]?.path } : item.arguments ?? item.action ?? { query: item.query };
|
|
4808
|
+
this.#append({ kind: "tool.use", toolUseId: item.id, name, input });
|
|
4809
|
+
}
|
|
4810
|
+
if (done) {
|
|
4811
|
+
this.#tools.delete(item.id);
|
|
4812
|
+
const output = item.aggregatedOutput ?? (item.changes ? item.changes.map((c) => `${c.path}
|
|
4813
|
+
${c.diff}`).join("\n") : typeof item.result === "string" ? item.result : JSON.stringify(item.error ?? item.result ?? item.action ?? "\u0413\u043E\u0442\u043E\u0432\u043E"));
|
|
4814
|
+
this.#append({
|
|
4815
|
+
kind: "tool.result",
|
|
4816
|
+
toolUseId: item.id,
|
|
4817
|
+
content: output,
|
|
4818
|
+
isError: ["failed", "declined"].includes(item.status ?? "") || item.exitCode != null && item.exitCode !== 0 || !!item.error
|
|
4819
|
+
});
|
|
4820
|
+
}
|
|
4821
|
+
}
|
|
4822
|
+
#request(id, method, params) {
|
|
4823
|
+
const requestId = String(id);
|
|
4824
|
+
const questions = method === "item/tool/requestUserInput" ? params.questions.map((q) => ({ ...q, options: q.options ?? [], multiSelect: false })) : void 0;
|
|
4825
|
+
const known = [
|
|
4826
|
+
"item/commandExecution/requestApproval",
|
|
4827
|
+
"item/fileChange/requestApproval",
|
|
4828
|
+
"item/permissions/requestApproval",
|
|
4829
|
+
"item/tool/requestUserInput"
|
|
4830
|
+
];
|
|
4831
|
+
if (!known.includes(method)) {
|
|
4832
|
+
this.#rpc?.write({ id, error: { code: -32601, message: `CCV does not support ${method}` } });
|
|
4833
|
+
this.#notice(new Error(`Codex \u0437\u0430\u043F\u0440\u043E\u0441\u0438\u043B \u043D\u0435\u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043C\u043E\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435: ${method}`));
|
|
4834
|
+
return;
|
|
4835
|
+
}
|
|
4836
|
+
this.#pending.set(requestId, { id, method, params, questions });
|
|
4837
|
+
this.#append({
|
|
4838
|
+
kind: "permission.request",
|
|
4839
|
+
requestId,
|
|
4840
|
+
toolUseId: String(params.itemId ?? requestId),
|
|
4841
|
+
toolName: questions ? "AskUserQuestion" : method.includes("commandExecution") ? "Bash" : method.includes("fileChange") ? "apply_patch" : "Codex permissions",
|
|
4842
|
+
input: { ...params, ...this.#items.get(String(params.itemId))?.changes ? { changes: this.#items.get(String(params.itemId)).changes } : {} },
|
|
4843
|
+
...typeof params.reason === "string" ? { reason: params.reason } : {},
|
|
4844
|
+
...questions ? { questions } : {}
|
|
4845
|
+
});
|
|
4846
|
+
this.#setState("waiting_permission");
|
|
4847
|
+
}
|
|
4848
|
+
respondPermission(requestId, decision, reason, answers) {
|
|
4849
|
+
const request2 = this.#pending.get(requestId);
|
|
4850
|
+
if (!request2)
|
|
4851
|
+
return;
|
|
4852
|
+
let result = { decision: decision === "allow" ? "accept" : "decline" };
|
|
4853
|
+
if (request2.questions) {
|
|
4854
|
+
result = { answers: Object.fromEntries(request2.questions.map((q) => [q.id, {
|
|
4855
|
+
answers: decision === "allow" ? [answers?.[q.question] ?? ""] : []
|
|
4856
|
+
}])) };
|
|
4857
|
+
} else if (request2.method === "item/permissions/requestApproval") {
|
|
4858
|
+
result = { permissions: decision === "allow" ? request2.params.permissions : {}, scope: "turn" };
|
|
4859
|
+
}
|
|
4860
|
+
this.#rpc?.write({ id: request2.id, result });
|
|
4861
|
+
this.#pending.delete(requestId);
|
|
4862
|
+
this.#append({ kind: "permission.resolved", requestId, decision, reason, answers });
|
|
4863
|
+
this.#setState(this.#pending.size ? "waiting_permission" : "running");
|
|
4864
|
+
}
|
|
4865
|
+
async interrupt() {
|
|
4866
|
+
this.#queue = [];
|
|
4867
|
+
if (!this.#busy)
|
|
4868
|
+
return;
|
|
4869
|
+
this.#interruptRequested = true;
|
|
4870
|
+
if (this.#turnId && this.#rpc) {
|
|
4871
|
+
await this.#rpc.request("turn/interrupt", { threadId: this.#record.conversationId, turnId: this.#turnId });
|
|
4872
|
+
}
|
|
4873
|
+
}
|
|
4874
|
+
#finishTurn(subtype, durationMs) {
|
|
4875
|
+
for (const [blockId, block] of this.#texts) {
|
|
4876
|
+
this.#append({ kind: block.thinking ? "assistant.thinking" : "assistant.text", text: block.text, blockId });
|
|
4877
|
+
}
|
|
4878
|
+
this.#texts.clear();
|
|
4879
|
+
for (const toolUseId of this.#tools) {
|
|
4880
|
+
this.#append({ kind: "tool.result", toolUseId, content: "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043F\u0440\u0435\u0440\u0432\u0430\u043D\u043E", isError: true });
|
|
4881
|
+
}
|
|
4882
|
+
this.#tools.clear();
|
|
4883
|
+
this.#items.clear();
|
|
4884
|
+
this.#clearApprovals();
|
|
4885
|
+
this.#append({
|
|
3826
4886
|
kind: "result",
|
|
3827
|
-
subtype
|
|
3828
|
-
isError:
|
|
3829
|
-
durationMs: Math.
|
|
4887
|
+
subtype,
|
|
4888
|
+
isError: this.#lastTurn === "failed",
|
|
4889
|
+
durationMs: durationMs ?? Math.max(0, Date.now() - this.#turnStartedAt),
|
|
3830
4890
|
costUsd: null,
|
|
3831
|
-
usage,
|
|
3832
|
-
contextTokens: this.#
|
|
3833
|
-
text:
|
|
4891
|
+
usage: this.#turnUsage,
|
|
4892
|
+
contextTokens: this.#context?.tokens ?? null,
|
|
4893
|
+
text: null
|
|
3834
4894
|
});
|
|
3835
|
-
this.#
|
|
3836
|
-
|
|
3837
|
-
this.#
|
|
3838
|
-
this.#pendingTurns = Math.max(0, this.#pendingTurns - 1);
|
|
3839
|
-
if (this.#pendingTurns > 0)
|
|
3840
|
-
return;
|
|
3841
|
-
if (this.#restartPending)
|
|
3842
|
-
this.#restart();
|
|
4895
|
+
this.#busy = false;
|
|
4896
|
+
this.#turnId = null;
|
|
4897
|
+
this.#interruptRequested = false;
|
|
3843
4898
|
this.#setState("idle");
|
|
4899
|
+
void this.#drain();
|
|
3844
4900
|
}
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
4901
|
+
async setPermissionMode(mode) {
|
|
4902
|
+
codexPolicy(mode);
|
|
4903
|
+
this.#record.permissionMode = mode;
|
|
4904
|
+
this.#settingsChanged();
|
|
4905
|
+
}
|
|
4906
|
+
async setModel(model) {
|
|
4907
|
+
this.#record.model = model;
|
|
4908
|
+
this.#record.effort = null;
|
|
4909
|
+
this.#settingsChanged();
|
|
4910
|
+
}
|
|
4911
|
+
async setEffort(effort) {
|
|
4912
|
+
const model = this.#capabilities?.models.find((m) => m.value === this.#record.model);
|
|
4913
|
+
if (model && !model.effortLevels.includes(effort))
|
|
4914
|
+
throw new Error("\u041C\u043E\u0434\u0435\u043B\u044C Codex \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u044D\u0442\u043E \u0443\u0441\u0438\u043B\u0438\u0435");
|
|
4915
|
+
this.#record.effort = effort;
|
|
4916
|
+
this.#settingsChanged();
|
|
4917
|
+
}
|
|
4918
|
+
#settingsChanged() {
|
|
4919
|
+
if (this.#capabilities) {
|
|
4920
|
+
this.#capabilities.model = this.#record.model ?? "";
|
|
4921
|
+
this.#capabilities.permissionMode = this.#record.permissionMode;
|
|
4922
|
+
this.emit("capabilities");
|
|
3851
4923
|
}
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
4924
|
+
void this.#store.save(this.#record);
|
|
4925
|
+
this.emit("summary", this.summary());
|
|
4926
|
+
}
|
|
4927
|
+
setTitle(title) {
|
|
4928
|
+
this.#record.title = title;
|
|
4929
|
+
this.#titleIsDefault = false;
|
|
4930
|
+
this.#settingsChanged();
|
|
4931
|
+
}
|
|
4932
|
+
async refreshUsage(_force = false) {
|
|
4933
|
+
if (!this.#rpc || networkGuard.blockedReason())
|
|
4934
|
+
return;
|
|
4935
|
+
try {
|
|
4936
|
+
const reply = await this.#rpc.request("account/rateLimits/read");
|
|
4937
|
+
this.#setUsage(reply.rateLimits);
|
|
4938
|
+
} catch {
|
|
3859
4939
|
}
|
|
3860
|
-
this.#texts.clear();
|
|
3861
|
-
this.#thinking.clear();
|
|
3862
|
-
this.#tools.clear();
|
|
3863
4940
|
}
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
this
|
|
4941
|
+
#setUsage(limits) {
|
|
4942
|
+
this.#usage = codexUsage(limits);
|
|
4943
|
+
this.emit("usage", this.#usage);
|
|
4944
|
+
}
|
|
4945
|
+
async refreshContext(_force = false) {
|
|
4946
|
+
if (this.#context)
|
|
4947
|
+
this.emit("context", this.#context);
|
|
4948
|
+
}
|
|
4949
|
+
reconnect(_reason) {
|
|
4950
|
+
this.#disconnect();
|
|
4951
|
+
this.#setState("starting");
|
|
4952
|
+
this.#launch();
|
|
4953
|
+
}
|
|
4954
|
+
suspend(reason) {
|
|
4955
|
+
this.#disconnect();
|
|
4956
|
+
this.#notice(new Error(reason));
|
|
4957
|
+
this.#setState("error");
|
|
4958
|
+
}
|
|
4959
|
+
async close(_reason = "closed by client") {
|
|
4960
|
+
this.#disconnect();
|
|
4961
|
+
this.#setState("closed");
|
|
4962
|
+
await this.#flush();
|
|
4963
|
+
}
|
|
4964
|
+
#disconnect() {
|
|
4965
|
+
this.#generation += 1;
|
|
4966
|
+
this.#startingTurn = false;
|
|
4967
|
+
const rpc = this.#rpc;
|
|
4968
|
+
this.#rpc = null;
|
|
4969
|
+
rpc?.close();
|
|
4970
|
+
this.#queue = [];
|
|
4971
|
+
if (this.#busy) {
|
|
4972
|
+
this.#lastTurn = "interrupted";
|
|
4973
|
+
this.#finishTurn("interrupted");
|
|
4974
|
+
}
|
|
4975
|
+
this.#clearApprovals();
|
|
4976
|
+
}
|
|
4977
|
+
#clearApprovals() {
|
|
4978
|
+
for (const requestId of this.#pending.keys()) {
|
|
4979
|
+
this.#append({ kind: "permission.resolved", requestId, decision: "deny", reason: "\u0425\u043E\u0434 \u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043D" });
|
|
4980
|
+
}
|
|
4981
|
+
this.#pending.clear();
|
|
4982
|
+
}
|
|
4983
|
+
#notice(error) {
|
|
4984
|
+
this.#append({ kind: "error", message: error instanceof Error ? error.message : String(error) });
|
|
4985
|
+
}
|
|
4986
|
+
#fail(error) {
|
|
4987
|
+
if (this.#state === "closed")
|
|
4988
|
+
return;
|
|
4989
|
+
this.#queue = [];
|
|
4990
|
+
if (this.#busy) {
|
|
4991
|
+
this.#lastTurn = "failed";
|
|
4992
|
+
this.#finishTurn("failed");
|
|
4993
|
+
}
|
|
4994
|
+
this.#notice(error);
|
|
4995
|
+
const rpc = this.#rpc;
|
|
4996
|
+
this.#rpc = null;
|
|
4997
|
+
rpc?.close();
|
|
4998
|
+
this.#setState("error");
|
|
4999
|
+
}
|
|
5000
|
+
#append(event) {
|
|
5001
|
+
this.#record.updatedAt = Date.now();
|
|
3867
5002
|
const entry = this.journal.append(event);
|
|
3868
5003
|
this.emit("event", entry);
|
|
3869
|
-
if (
|
|
5004
|
+
if (["assistant.delta", "thinking.delta", "state"].includes(event.kind))
|
|
3870
5005
|
return;
|
|
3871
5006
|
this.#unsaved.push({ ts: entry.ts, event });
|
|
3872
|
-
this.#
|
|
5007
|
+
if (!this.#flushTimer) {
|
|
5008
|
+
this.#flushTimer = setTimeout(() => {
|
|
5009
|
+
void this.#flush();
|
|
5010
|
+
}, 500);
|
|
5011
|
+
this.#flushTimer.unref?.();
|
|
5012
|
+
}
|
|
3873
5013
|
}
|
|
3874
|
-
#
|
|
5014
|
+
async #flush() {
|
|
3875
5015
|
if (this.#flushTimer)
|
|
3876
|
-
return;
|
|
3877
|
-
this.#flushTimer = setTimeout(() => this.#flush(), 500);
|
|
3878
|
-
this.#flushTimer.unref?.();
|
|
3879
|
-
}
|
|
3880
|
-
#flush() {
|
|
3881
|
-
if (this.#flushTimer) {
|
|
3882
5016
|
clearTimeout(this.#flushTimer);
|
|
3883
|
-
|
|
3884
|
-
}
|
|
5017
|
+
this.#flushTimer = null;
|
|
3885
5018
|
const batch = this.#unsaved;
|
|
3886
5019
|
this.#unsaved = [];
|
|
3887
|
-
this.#
|
|
3888
|
-
|
|
3889
|
-
void this.#store.save(this.#record);
|
|
5020
|
+
await this.#store.appendEvents(this.id, batch);
|
|
5021
|
+
await this.#store.save(this.#record);
|
|
3890
5022
|
}
|
|
3891
5023
|
#setState(state) {
|
|
3892
|
-
if (this.#state === state)
|
|
3893
|
-
return;
|
|
3894
5024
|
this.#state = state;
|
|
3895
|
-
this.#
|
|
3896
|
-
if (
|
|
3897
|
-
this.#flush();
|
|
5025
|
+
this.#append({ kind: "state", state });
|
|
5026
|
+
if (["idle", "error", "closed"].includes(state))
|
|
5027
|
+
void this.#flush();
|
|
3898
5028
|
this.emit("summary", this.summary());
|
|
3899
5029
|
}
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
const input = { ...parameters ?? {} };
|
|
3903
|
-
const pick = (...keys) => keys.map((k) => input[k]).find((v) => typeof v === "string");
|
|
3904
|
-
const file = pick("TargetFile", "AbsolutePath", "File", "FilePath", "Path");
|
|
3905
|
-
if (file && input.file_path === void 0)
|
|
3906
|
-
input.file_path = file;
|
|
3907
|
-
const command = pick("CommandLine", "Command");
|
|
3908
|
-
if (command && input.command === void 0)
|
|
3909
|
-
input.command = command;
|
|
3910
|
-
const pattern = pick("Query", "Pattern", "SearchPattern");
|
|
3911
|
-
if (pattern && input.pattern === void 0)
|
|
3912
|
-
input.pattern = pattern;
|
|
3913
|
-
const dir = pick("DirectoryPath", "SearchPath", "SearchDirectory");
|
|
3914
|
-
if (dir && input.path === void 0)
|
|
3915
|
-
input.path = dir;
|
|
3916
|
-
const url2 = pick("Url", "URL");
|
|
3917
|
-
if (url2 && input.url === void 0)
|
|
3918
|
-
input.url = url2;
|
|
3919
|
-
return input;
|
|
3920
|
-
}
|
|
3921
|
-
function formatUsage(usage) {
|
|
3922
|
-
const rows = (usage.buckets ?? []).map((b) => {
|
|
3923
|
-
const left = Math.round(100 - b.window.utilization);
|
|
3924
|
-
const reset = b.window.resetsAt ? new Date(b.window.resetsAt).toLocaleString("ru-RU") : "\u2014";
|
|
3925
|
-
return `| ${b.title || b.label} | ${left}% | ${reset} |`;
|
|
3926
|
-
});
|
|
3927
|
-
if (rows.length === 0)
|
|
3928
|
-
return "\u041A\u0432\u043E\u0442\u0430 Antigravity: \u0434\u0430\u043D\u043D\u044B\u0445 \u043D\u0435\u0442.";
|
|
3929
|
-
return ["**\u041A\u0432\u043E\u0442\u0430 Antigravity**", "", "| \u0413\u0440\u0443\u043F\u043F\u0430 | \u041E\u0441\u0442\u0430\u043B\u043E\u0441\u044C | \u041E\u0431\u043D\u0443\u043B\u0438\u0442\u0441\u044F |", "|---|---|---|", ...rows].join("\n");
|
|
3930
|
-
}
|
|
3931
|
-
function formatCommandReply(name, response) {
|
|
3932
|
-
const text = response.replace(/\s+$/, "");
|
|
3933
|
-
if (!text)
|
|
3934
|
-
return `/${name}: \u043F\u0443\u0441\u0442\u043E`;
|
|
3935
|
-
const lines = text.split(/\r?\n/);
|
|
3936
|
-
if (lines.every((l) => l.includes(" "))) {
|
|
3937
|
-
return lines.map((l) => {
|
|
3938
|
-
const [head, ...rest] = l.split(" ");
|
|
3939
|
-
return `- **${head}** \u2014 ${rest.join(" ")}`;
|
|
3940
|
-
}).join("\n");
|
|
5030
|
+
async editPrompt(_index, _text, _images) {
|
|
5031
|
+
throw new Error("\u0412 \u0441\u0435\u0441\u0441\u0438\u0438 Codex \u043F\u0440\u0430\u0432\u043A\u0430 \u043F\u0440\u043E\u0448\u043B\u044B\u0445 \u0440\u0435\u043F\u043B\u0438\u043A \u043F\u043E\u043A\u0430 \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F");
|
|
3941
5032
|
}
|
|
3942
|
-
|
|
3943
|
-
}
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
5033
|
+
async setUltracode(_enabled) {
|
|
5034
|
+
}
|
|
5035
|
+
async setThinking(_enabled) {
|
|
5036
|
+
}
|
|
5037
|
+
async setSwitchModelsOnFlag(_enabled) {
|
|
5038
|
+
}
|
|
5039
|
+
async reloadPlugins() {
|
|
5040
|
+
}
|
|
5041
|
+
async mcpServerStatus() {
|
|
5042
|
+
return [];
|
|
5043
|
+
}
|
|
5044
|
+
async addMcpServer(_name, _config) {
|
|
5045
|
+
throw this.#mcpHint();
|
|
5046
|
+
}
|
|
5047
|
+
async removeMcpServer(_name) {
|
|
5048
|
+
throw this.#mcpHint();
|
|
5049
|
+
}
|
|
5050
|
+
async reconnectMcpServer(_name) {
|
|
5051
|
+
throw this.#mcpHint();
|
|
5052
|
+
}
|
|
5053
|
+
async toggleMcpServer(_name, _enabled) {
|
|
5054
|
+
throw this.#mcpHint();
|
|
5055
|
+
}
|
|
5056
|
+
async authenticateMcpServer(_name) {
|
|
5057
|
+
throw this.#mcpHint();
|
|
5058
|
+
}
|
|
5059
|
+
async submitMcpOAuthCallback(_name, _url) {
|
|
5060
|
+
throw this.#mcpHint();
|
|
5061
|
+
}
|
|
5062
|
+
#mcpHint() {
|
|
5063
|
+
return new Error("MCP Codex \u043D\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044F \u0447\u0435\u0440\u0435\u0437 codex mcp \u0438\u043B\u0438 ~/.codex/config.toml");
|
|
5064
|
+
}
|
|
5065
|
+
};
|
|
3952
5066
|
|
|
3953
5067
|
// ../host/dist/antigravity-store.js
|
|
3954
5068
|
import { appendFile, mkdir as mkdir3, readFile as readFile4, rm as rm2, writeFile as writeFile3 } from "node:fs/promises";
|
|
3955
|
-
import
|
|
5069
|
+
import path9 from "node:path";
|
|
3956
5070
|
var AntigravityStore = class {
|
|
3957
5071
|
#dir;
|
|
3958
5072
|
#file;
|
|
3959
5073
|
#records = /* @__PURE__ */ new Map();
|
|
3960
5074
|
#loaded = null;
|
|
3961
5075
|
#writing = Promise.resolve();
|
|
3962
|
-
constructor(dataDir) {
|
|
3963
|
-
this.#dir =
|
|
3964
|
-
this.#file =
|
|
5076
|
+
constructor(dataDir, provider = "antigravity") {
|
|
5077
|
+
this.#dir = path9.join(dataDir, provider);
|
|
5078
|
+
this.#file = path9.join(this.#dir, "sessions.json");
|
|
3965
5079
|
}
|
|
3966
5080
|
#load() {
|
|
3967
5081
|
this.#loaded ??= (async () => {
|
|
@@ -4058,7 +5172,7 @@ var AntigravityStore = class {
|
|
|
4058
5172
|
return entries;
|
|
4059
5173
|
}
|
|
4060
5174
|
#eventsFile(id) {
|
|
4061
|
-
return
|
|
5175
|
+
return path9.join(this.#dir, `${id.replace(/[^\w-]/g, "_")}.jsonl`);
|
|
4062
5176
|
}
|
|
4063
5177
|
#persist() {
|
|
4064
5178
|
const snapshot = JSON.stringify([...this.#records.values()], null, 2);
|
|
@@ -4078,10 +5192,10 @@ var AntigravityStore = class {
|
|
|
4078
5192
|
};
|
|
4079
5193
|
|
|
4080
5194
|
// ../host/dist/git.js
|
|
4081
|
-
import { execFile as
|
|
5195
|
+
import { execFile as execFile5 } from "node:child_process";
|
|
4082
5196
|
import { mkdtemp, readFile as readFile5, rm as rm3, writeFile as writeFile4 } from "node:fs/promises";
|
|
4083
5197
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
4084
|
-
import
|
|
5198
|
+
import path10 from "node:path";
|
|
4085
5199
|
var TIMEOUT_MS = 2e4;
|
|
4086
5200
|
var NET_TIMEOUT_MS = 5 * 6e4;
|
|
4087
5201
|
var MAX_BUFFER = 32 * 1024 * 1024;
|
|
@@ -4397,7 +5511,7 @@ var GitRepo = class {
|
|
|
4397
5511
|
* важнее всего (см. `checkIgnored`).
|
|
4398
5512
|
*/
|
|
4399
5513
|
async #withoutRepo(args) {
|
|
4400
|
-
const dir = await mkdtemp(
|
|
5514
|
+
const dir = await mkdtemp(path10.join(tmpdir2(), "ccv-git-"));
|
|
4401
5515
|
try {
|
|
4402
5516
|
await this.#run(["init", "--bare", "--quiet", dir]);
|
|
4403
5517
|
return await this.#soft([`--git-dir=${dir}`, `--work-tree=${this.cwd}`, ...args]);
|
|
@@ -4443,7 +5557,7 @@ var GitRepo = class {
|
|
|
4443
5557
|
* исключил. Повторы отсеиваются, чтобы файл не рос от каждой публикации.
|
|
4444
5558
|
*/
|
|
4445
5559
|
async #appendGitignore(paths) {
|
|
4446
|
-
const file =
|
|
5560
|
+
const file = path10.join(this.cwd, ".gitignore");
|
|
4447
5561
|
const existing = await readFile5(file, "utf8").catch(() => "");
|
|
4448
5562
|
const known = new Set(existing.split(/\r?\n/).map((line) => line.trim()));
|
|
4449
5563
|
const lines = paths.map(ignorePattern).filter((line) => !known.has(line));
|
|
@@ -4468,7 +5582,7 @@ var GitRepo = class {
|
|
|
4468
5582
|
#run(args, options2 = {}) {
|
|
4469
5583
|
const token = options2.token ?? "";
|
|
4470
5584
|
return new Promise((resolve, reject) => {
|
|
4471
|
-
const child =
|
|
5585
|
+
const child = execFile5(
|
|
4472
5586
|
"git",
|
|
4473
5587
|
// core.quotepath=false — иначе кириллические пути приезжают в виде
|
|
4474
5588
|
// «\320\277\321\200…» и в панели читаются как мусор.
|
|
@@ -4649,9 +5763,9 @@ function parsePatch(raw) {
|
|
|
4649
5763
|
body = "";
|
|
4650
5764
|
}
|
|
4651
5765
|
total += body.length;
|
|
4652
|
-
const
|
|
5766
|
+
const path26 = status === "deleted" ? oldPath : newPath || oldPath;
|
|
4653
5767
|
files.push({
|
|
4654
|
-
path:
|
|
5768
|
+
path: path26,
|
|
4655
5769
|
oldPath: status === "renamed" ? oldPath : "",
|
|
4656
5770
|
status,
|
|
4657
5771
|
added: count(body, "+"),
|
|
@@ -4663,10 +5777,10 @@ function parsePatch(raw) {
|
|
|
4663
5777
|
return { files, truncated };
|
|
4664
5778
|
}
|
|
4665
5779
|
function stripPrefix(value) {
|
|
4666
|
-
const
|
|
4667
|
-
if (
|
|
5780
|
+
const path26 = unquote(value.trim());
|
|
5781
|
+
if (path26 === "/dev/null")
|
|
4668
5782
|
return "";
|
|
4669
|
-
return
|
|
5783
|
+
return path26.replace(/^[ab]\//, "");
|
|
4670
5784
|
}
|
|
4671
5785
|
function count(body, mark) {
|
|
4672
5786
|
let n = 0;
|
|
@@ -4678,9 +5792,9 @@ function count(body, mark) {
|
|
|
4678
5792
|
}
|
|
4679
5793
|
|
|
4680
5794
|
// ../host/dist/github.js
|
|
4681
|
-
import { execFile as
|
|
5795
|
+
import { execFile as execFile6 } from "node:child_process";
|
|
4682
5796
|
import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile5 } from "node:fs/promises";
|
|
4683
|
-
import
|
|
5797
|
+
import path11 from "node:path";
|
|
4684
5798
|
var API = "https://api.github.com";
|
|
4685
5799
|
var API_VERSION = "2022-11-28";
|
|
4686
5800
|
var API_TIMEOUT_MS = 2e4;
|
|
@@ -4701,7 +5815,7 @@ var GitHubAccount = class {
|
|
|
4701
5815
|
*/
|
|
4702
5816
|
#ghInstalled = null;
|
|
4703
5817
|
constructor(dataDir) {
|
|
4704
|
-
this.#storePath =
|
|
5818
|
+
this.#storePath = path11.join(dataDir, "github.json");
|
|
4705
5819
|
}
|
|
4706
5820
|
async load() {
|
|
4707
5821
|
try {
|
|
@@ -4715,7 +5829,7 @@ var GitHubAccount = class {
|
|
|
4715
5829
|
/** Сохранить токен или стереть его пустой строкой. */
|
|
4716
5830
|
async setToken(token) {
|
|
4717
5831
|
this.#token = token.trim();
|
|
4718
|
-
await mkdir4(
|
|
5832
|
+
await mkdir4(path11.dirname(this.#storePath), { recursive: true });
|
|
4719
5833
|
await writeFile5(this.#storePath, JSON.stringify({ token: this.#token }, null, 2), {
|
|
4720
5834
|
encoding: "utf8",
|
|
4721
5835
|
mode: 384
|
|
@@ -4867,7 +5981,7 @@ var GitHubAccount = class {
|
|
|
4867
5981
|
};
|
|
4868
5982
|
function gh(args) {
|
|
4869
5983
|
return new Promise((resolve, reject) => {
|
|
4870
|
-
|
|
5984
|
+
execFile6("gh", args, { timeout: GH_TIMEOUT_MS, windowsHide: true, encoding: "utf8" }, (error, stdout, stderr) => {
|
|
4871
5985
|
if (error)
|
|
4872
5986
|
return reject(new Error(stderr.trim() || error.message));
|
|
4873
5987
|
resolve(stdout);
|
|
@@ -4908,10 +6022,10 @@ function describe4(error) {
|
|
|
4908
6022
|
}
|
|
4909
6023
|
|
|
4910
6024
|
// ../host/dist/finder.js
|
|
4911
|
-
import { existsSync as
|
|
6025
|
+
import { existsSync as existsSync3, statSync as statSync2 } from "node:fs";
|
|
4912
6026
|
import { readdir as readdir2 } from "node:fs/promises";
|
|
4913
6027
|
import os2 from "node:os";
|
|
4914
|
-
import
|
|
6028
|
+
import path12 from "node:path";
|
|
4915
6029
|
import { listSessions } from "@anthropic-ai/claude-agent-sdk";
|
|
4916
6030
|
var CACHE_TTL = 6e4;
|
|
4917
6031
|
var FIND_LIMIT = 50;
|
|
@@ -4950,7 +6064,7 @@ var ProjectFinder = class {
|
|
|
4950
6064
|
if (needles.length === 0)
|
|
4951
6065
|
return { candidates: [], truncated: false };
|
|
4952
6066
|
const open = new Map(this.registry.list().map((workspace) => [workspace.path.toLowerCase(), workspace]));
|
|
4953
|
-
const byName = this.#dirs.filter((dir) => matches(
|
|
6067
|
+
const byName = this.#dirs.filter((dir) => matches(path12.basename(dir.path), needles));
|
|
4954
6068
|
const matched = byName.length > 0 ? byName : this.#dirs.filter((dir) => matches(dir.path, needles));
|
|
4955
6069
|
const sorted = [...matched].sort((a, b) => {
|
|
4956
6070
|
const aOpen = open.has(a.path.toLowerCase());
|
|
@@ -4961,7 +6075,7 @@ var ProjectFinder = class {
|
|
|
4961
6075
|
return a.sessions > 0 ? -1 : 1;
|
|
4962
6076
|
if (a.lastUsedAt !== b.lastUsedAt)
|
|
4963
6077
|
return b.lastUsedAt - a.lastUsedAt;
|
|
4964
|
-
return
|
|
6078
|
+
return path12.basename(a.path).localeCompare(path12.basename(b.path), void 0, {
|
|
4965
6079
|
numeric: true,
|
|
4966
6080
|
sensitivity: "base"
|
|
4967
6081
|
});
|
|
@@ -4969,7 +6083,7 @@ var ProjectFinder = class {
|
|
|
4969
6083
|
return {
|
|
4970
6084
|
candidates: sorted.slice(0, FIND_LIMIT).map((dir) => ({
|
|
4971
6085
|
path: dir.path,
|
|
4972
|
-
name:
|
|
6086
|
+
name: path12.basename(dir.path) || dir.path,
|
|
4973
6087
|
sessions: dir.sessions,
|
|
4974
6088
|
lastUsedAt: dir.lastUsedAt || null,
|
|
4975
6089
|
open: open.has(dir.path.toLowerCase()),
|
|
@@ -4993,7 +6107,7 @@ var ProjectFinder = class {
|
|
|
4993
6107
|
const candidates = [];
|
|
4994
6108
|
for (const entry of named) {
|
|
4995
6109
|
const absolute = entry.path.trim();
|
|
4996
|
-
if (!absolute || !
|
|
6110
|
+
if (!absolute || !path12.isAbsolute(absolute))
|
|
4997
6111
|
continue;
|
|
4998
6112
|
const key = absolute.toLowerCase();
|
|
4999
6113
|
if (seen.has(key))
|
|
@@ -5004,7 +6118,7 @@ var ProjectFinder = class {
|
|
|
5004
6118
|
const record = known.get(key);
|
|
5005
6119
|
candidates.push({
|
|
5006
6120
|
path: record?.path ?? absolute,
|
|
5007
|
-
name:
|
|
6121
|
+
name: path12.basename(absolute) || absolute,
|
|
5008
6122
|
sessions: record?.sessions ?? 0,
|
|
5009
6123
|
lastUsedAt: record?.lastUsedAt || null,
|
|
5010
6124
|
open: open.has(key),
|
|
@@ -5061,7 +6175,7 @@ var ProjectFinder = class {
|
|
|
5061
6175
|
if (!dirent.isDirectory() || SKIP_NAMES.has(dirent.name) || dirent.name.startsWith(".")) {
|
|
5062
6176
|
continue;
|
|
5063
6177
|
}
|
|
5064
|
-
const full =
|
|
6178
|
+
const full = path12.join(parent, dirent.name);
|
|
5065
6179
|
const key = full.toLowerCase();
|
|
5066
6180
|
if (dirs.has(key))
|
|
5067
6181
|
continue;
|
|
@@ -5082,7 +6196,7 @@ var ProjectFinder = class {
|
|
|
5082
6196
|
#parents(dirs) {
|
|
5083
6197
|
const counted = /* @__PURE__ */ new Map();
|
|
5084
6198
|
for (const dir of dirs) {
|
|
5085
|
-
const parent =
|
|
6199
|
+
const parent = path12.dirname(dir.path);
|
|
5086
6200
|
if (parent === dir.path || !usable(parent))
|
|
5087
6201
|
continue;
|
|
5088
6202
|
const key = parent.toLowerCase();
|
|
@@ -5096,7 +6210,7 @@ var ProjectFinder = class {
|
|
|
5096
6210
|
}
|
|
5097
6211
|
};
|
|
5098
6212
|
function usable(dir) {
|
|
5099
|
-
return !contains(os2.tmpdir(), dir) &&
|
|
6213
|
+
return !contains(os2.tmpdir(), dir) && existsSync3(dir);
|
|
5100
6214
|
}
|
|
5101
6215
|
function tokens(query2) {
|
|
5102
6216
|
return query2.toLowerCase().split(/[^a-z0-9а-яё]+/i).filter(Boolean);
|
|
@@ -5107,7 +6221,7 @@ function matches(value, needles) {
|
|
|
5107
6221
|
}
|
|
5108
6222
|
function isDirectory(dir) {
|
|
5109
6223
|
try {
|
|
5110
|
-
return
|
|
6224
|
+
return statSync2(dir).isDirectory();
|
|
5111
6225
|
} catch {
|
|
5112
6226
|
return false;
|
|
5113
6227
|
}
|
|
@@ -5193,15 +6307,15 @@ function fromText(text) {
|
|
|
5193
6307
|
}
|
|
5194
6308
|
|
|
5195
6309
|
// ../host/dist/workspaces.js
|
|
5196
|
-
import { existsSync as
|
|
6310
|
+
import { existsSync as existsSync4, statSync as statSync3 } from "node:fs";
|
|
5197
6311
|
import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile6 } from "node:fs/promises";
|
|
5198
|
-
import
|
|
6312
|
+
import path13 from "node:path";
|
|
5199
6313
|
import { listSessions as listSessions2 } from "@anthropic-ai/claude-agent-sdk";
|
|
5200
6314
|
var WorkspaceRegistry = class {
|
|
5201
6315
|
#entries = [];
|
|
5202
6316
|
#storePath;
|
|
5203
6317
|
constructor(dataDir) {
|
|
5204
|
-
this.#storePath =
|
|
6318
|
+
this.#storePath = path13.join(dataDir, "workspaces.json");
|
|
5205
6319
|
}
|
|
5206
6320
|
async load() {
|
|
5207
6321
|
try {
|
|
@@ -5217,13 +6331,13 @@ var WorkspaceRegistry = class {
|
|
|
5217
6331
|
if (isEntry(item))
|
|
5218
6332
|
return item;
|
|
5219
6333
|
return null;
|
|
5220
|
-
}).filter((e) => e !== null &&
|
|
6334
|
+
}).filter((e) => e !== null && existsSync4(e.path));
|
|
5221
6335
|
} catch {
|
|
5222
6336
|
this.#entries = [];
|
|
5223
6337
|
}
|
|
5224
6338
|
}
|
|
5225
6339
|
async #persist() {
|
|
5226
|
-
await mkdir5(
|
|
6340
|
+
await mkdir5(path13.dirname(this.#storePath), { recursive: true });
|
|
5227
6341
|
await writeFile6(this.#storePath, JSON.stringify(this.#entries, null, 2), "utf8");
|
|
5228
6342
|
}
|
|
5229
6343
|
/** Активные проекты. */
|
|
@@ -5243,8 +6357,8 @@ var WorkspaceRegistry = class {
|
|
|
5243
6357
|
return entry ? toInfo(entry) : void 0;
|
|
5244
6358
|
}
|
|
5245
6359
|
async open(rawPath) {
|
|
5246
|
-
const absolute =
|
|
5247
|
-
if (!
|
|
6360
|
+
const absolute = path13.resolve(rawPath);
|
|
6361
|
+
if (!existsSync4(absolute)) {
|
|
5248
6362
|
throw new Error(`\u041A\u0430\u0442\u0430\u043B\u043E\u0433 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D: ${absolute}`);
|
|
5249
6363
|
}
|
|
5250
6364
|
const existing = this.#entries.find((e) => e.path === absolute);
|
|
@@ -5310,24 +6424,24 @@ var WorkspaceRegistry = class {
|
|
|
5310
6424
|
async create(parentRaw, nameRaw, init) {
|
|
5311
6425
|
const name = nameRaw.trim();
|
|
5312
6426
|
requireName(name);
|
|
5313
|
-
const parent =
|
|
5314
|
-
if (!
|
|
6427
|
+
const parent = path13.resolve(parentRaw.trim());
|
|
6428
|
+
if (!existsSync4(parent)) {
|
|
5315
6429
|
throw new Error(`\u041A\u0430\u0442\u0430\u043B\u043E\u0433 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D: ${parent}`);
|
|
5316
6430
|
}
|
|
5317
|
-
if (!
|
|
6431
|
+
if (!statSync3(parent).isDirectory()) {
|
|
5318
6432
|
throw new Error(`\u042D\u0442\u043E \u043D\u0435 \u043A\u0430\u0442\u0430\u043B\u043E\u0433: ${parent}`);
|
|
5319
6433
|
}
|
|
5320
|
-
const target =
|
|
5321
|
-
if (
|
|
6434
|
+
const target = path13.join(parent, name);
|
|
6435
|
+
if (existsSync4(target)) {
|
|
5322
6436
|
throw new Error(`\u0423\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442: ${target} \u2014 \u043E\u0442\u043A\u0440\u043E\u0439\u0442\u0435 \u0435\u0433\u043E \u043A\u0430\u043A \u043F\u0440\u043E\u0435\u043A\u0442`);
|
|
5323
6437
|
}
|
|
5324
6438
|
await mkdir5(target);
|
|
5325
6439
|
const warnings = [];
|
|
5326
6440
|
if (init.claudeMd) {
|
|
5327
|
-
await step(warnings, "CLAUDE.md", () => writeFile6(
|
|
6441
|
+
await step(warnings, "CLAUDE.md", () => writeFile6(path13.join(target, "CLAUDE.md"), claudeMdStub(name), "utf8"));
|
|
5328
6442
|
}
|
|
5329
6443
|
if (init.readme) {
|
|
5330
|
-
await step(warnings, "README.md", () => writeFile6(
|
|
6444
|
+
await step(warnings, "README.md", () => writeFile6(path13.join(target, "README.md"), readmeStub(name), "utf8"));
|
|
5331
6445
|
}
|
|
5332
6446
|
if (init.git) {
|
|
5333
6447
|
await step(warnings, "git init", () => new GitRepo(target).init());
|
|
@@ -5399,8 +6513,8 @@ function toInfo(entry) {
|
|
|
5399
6513
|
return {
|
|
5400
6514
|
id: workspaceIdFromPath(absolute),
|
|
5401
6515
|
path: absolute,
|
|
5402
|
-
name:
|
|
5403
|
-
hasClaudeMd:
|
|
6516
|
+
name: path13.basename(absolute) || absolute,
|
|
6517
|
+
hasClaudeMd: existsSync4(path13.join(absolute, "CLAUDE.md")),
|
|
5404
6518
|
...archivedAt ? { archivedAt } : {}
|
|
5405
6519
|
};
|
|
5406
6520
|
}
|
|
@@ -5410,13 +6524,13 @@ function isEntry(value) {
|
|
|
5410
6524
|
|
|
5411
6525
|
// ../host/dist/archive.js
|
|
5412
6526
|
import { mkdir as mkdir6, readFile as readFile8, writeFile as writeFile7 } from "node:fs/promises";
|
|
5413
|
-
import
|
|
6527
|
+
import path14 from "node:path";
|
|
5414
6528
|
var SessionArchive = class {
|
|
5415
6529
|
#archived = /* @__PURE__ */ new Map();
|
|
5416
6530
|
#restored = /* @__PURE__ */ new Map();
|
|
5417
6531
|
#storePath;
|
|
5418
6532
|
constructor(dataDir) {
|
|
5419
|
-
this.#storePath =
|
|
6533
|
+
this.#storePath = path14.join(dataDir, "session-archive.json");
|
|
5420
6534
|
}
|
|
5421
6535
|
async load() {
|
|
5422
6536
|
try {
|
|
@@ -5528,7 +6642,7 @@ var SessionArchive = class {
|
|
|
5528
6642
|
archived: Object.fromEntries(this.#archived),
|
|
5529
6643
|
restored: Object.fromEntries(this.#restored)
|
|
5530
6644
|
};
|
|
5531
|
-
await mkdir6(
|
|
6645
|
+
await mkdir6(path14.dirname(this.#storePath), { recursive: true });
|
|
5532
6646
|
await writeFile7(this.#storePath, JSON.stringify(file, null, 2), "utf8");
|
|
5533
6647
|
}
|
|
5534
6648
|
};
|
|
@@ -5538,7 +6652,7 @@ function isRecord(value) {
|
|
|
5538
6652
|
|
|
5539
6653
|
// ../host/dist/pins.js
|
|
5540
6654
|
import { mkdir as mkdir7, readFile as readFile9, writeFile as writeFile8 } from "node:fs/promises";
|
|
5541
|
-
import
|
|
6655
|
+
import path15 from "node:path";
|
|
5542
6656
|
var SessionPins = class {
|
|
5543
6657
|
#pinned = /* @__PURE__ */ new Map();
|
|
5544
6658
|
/**
|
|
@@ -5553,7 +6667,7 @@ var SessionPins = class {
|
|
|
5553
6667
|
#moved = /* @__PURE__ */ new Map();
|
|
5554
6668
|
#storePath;
|
|
5555
6669
|
constructor(dataDir) {
|
|
5556
|
-
this.#storePath =
|
|
6670
|
+
this.#storePath = path15.join(dataDir, "session-pins.json");
|
|
5557
6671
|
}
|
|
5558
6672
|
async load() {
|
|
5559
6673
|
try {
|
|
@@ -5710,7 +6824,7 @@ var SessionPins = class {
|
|
|
5710
6824
|
pinned: Object.fromEntries(this.#pinned),
|
|
5711
6825
|
...this.#moved.size > 0 ? { moved: Object.fromEntries(this.#moved) } : {}
|
|
5712
6826
|
};
|
|
5713
|
-
await mkdir7(
|
|
6827
|
+
await mkdir7(path15.dirname(this.#storePath), { recursive: true });
|
|
5714
6828
|
await writeFile8(this.#storePath, JSON.stringify(file, null, 2), "utf8");
|
|
5715
6829
|
}
|
|
5716
6830
|
};
|
|
@@ -5720,12 +6834,12 @@ function isRecord2(value) {
|
|
|
5720
6834
|
|
|
5721
6835
|
// ../host/dist/order.js
|
|
5722
6836
|
import { mkdir as mkdir8, readFile as readFile10, writeFile as writeFile9 } from "node:fs/promises";
|
|
5723
|
-
import
|
|
6837
|
+
import path16 from "node:path";
|
|
5724
6838
|
var ListOrderStore = class {
|
|
5725
6839
|
#order = emptyOrder();
|
|
5726
6840
|
#storePath;
|
|
5727
6841
|
constructor(dataDir) {
|
|
5728
|
-
this.#storePath =
|
|
6842
|
+
this.#storePath = path16.join(dataDir, "list-order.json");
|
|
5729
6843
|
}
|
|
5730
6844
|
async load() {
|
|
5731
6845
|
try {
|
|
@@ -5801,7 +6915,7 @@ var ListOrderStore = class {
|
|
|
5801
6915
|
return changed;
|
|
5802
6916
|
}
|
|
5803
6917
|
async #persist() {
|
|
5804
|
-
await mkdir8(
|
|
6918
|
+
await mkdir8(path16.dirname(this.#storePath), { recursive: true });
|
|
5805
6919
|
await writeFile9(this.#storePath, JSON.stringify(this.#order, null, 2), "utf8");
|
|
5806
6920
|
}
|
|
5807
6921
|
};
|
|
@@ -5832,8 +6946,8 @@ function sanitize(value) {
|
|
|
5832
6946
|
|
|
5833
6947
|
// ../host/dist/folders.js
|
|
5834
6948
|
import { mkdir as mkdir9, readFile as readFile11, writeFile as writeFile10 } from "node:fs/promises";
|
|
5835
|
-
import
|
|
5836
|
-
import { randomUUID as
|
|
6949
|
+
import path17 from "node:path";
|
|
6950
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
5837
6951
|
var COLOR_PATTERN = /^#[0-9a-f]{6}$/i;
|
|
5838
6952
|
var Folders = class {
|
|
5839
6953
|
#workspaceFolders = /* @__PURE__ */ new Map();
|
|
@@ -5844,7 +6958,7 @@ var Folders = class {
|
|
|
5844
6958
|
#pinnedAssignments = /* @__PURE__ */ new Map();
|
|
5845
6959
|
#storePath;
|
|
5846
6960
|
constructor(dataDir) {
|
|
5847
|
-
this.#storePath =
|
|
6961
|
+
this.#storePath = path17.join(dataDir, "folders.json");
|
|
5848
6962
|
}
|
|
5849
6963
|
async load() {
|
|
5850
6964
|
try {
|
|
@@ -5933,19 +7047,19 @@ var Folders = class {
|
|
|
5933
7047
|
};
|
|
5934
7048
|
}
|
|
5935
7049
|
async createWorkspaceFolder(name) {
|
|
5936
|
-
const id =
|
|
7050
|
+
const id = randomUUID4();
|
|
5937
7051
|
this.#workspaceFolders.set(id, { name });
|
|
5938
7052
|
await this.#persist();
|
|
5939
7053
|
return id;
|
|
5940
7054
|
}
|
|
5941
7055
|
async createSessionFolder(workspaceId, name) {
|
|
5942
|
-
const id =
|
|
7056
|
+
const id = randomUUID4();
|
|
5943
7057
|
this.#sessionFolders.set(id, { name, workspaceId });
|
|
5944
7058
|
await this.#persist();
|
|
5945
7059
|
return id;
|
|
5946
7060
|
}
|
|
5947
7061
|
async createPinnedFolder(name) {
|
|
5948
|
-
const id =
|
|
7062
|
+
const id = randomUUID4();
|
|
5949
7063
|
this.#pinnedFolders.set(id, { name });
|
|
5950
7064
|
await this.#persist();
|
|
5951
7065
|
return id;
|
|
@@ -6125,7 +7239,7 @@ var Folders = class {
|
|
|
6125
7239
|
sessionAssignments: Object.fromEntries(this.#sessionAssignments),
|
|
6126
7240
|
pinnedAssignments: Object.fromEntries(this.#pinnedAssignments)
|
|
6127
7241
|
};
|
|
6128
|
-
await mkdir9(
|
|
7242
|
+
await mkdir9(path17.dirname(this.#storePath), { recursive: true });
|
|
6129
7243
|
await writeFile10(this.#storePath, JSON.stringify(file, null, 2), "utf8");
|
|
6130
7244
|
}
|
|
6131
7245
|
};
|
|
@@ -6148,15 +7262,16 @@ function isPinnedFolder(value) {
|
|
|
6148
7262
|
|
|
6149
7263
|
// ../host/dist/app-settings.js
|
|
6150
7264
|
import { mkdir as mkdir10, readFile as readFile12, writeFile as writeFile11 } from "node:fs/promises";
|
|
6151
|
-
import { homedir as
|
|
6152
|
-
import
|
|
7265
|
+
import { homedir as homedir5 } from "node:os";
|
|
7266
|
+
import path18 from "node:path";
|
|
6153
7267
|
var DEFAULT_AUTO_ARCHIVE_AFTER_MS = 2 * 24 * 60 * 60 * 1e3;
|
|
6154
7268
|
var MIN_AUTO_ARCHIVE_AFTER_MS = 60 * 60 * 1e3;
|
|
6155
7269
|
var DEFAULT_AUTO_STOP_AFTER_MS = 10 * 60 * 1e3;
|
|
6156
7270
|
var MIN_AUTO_STOP_AFTER_MS = 60 * 1e3;
|
|
6157
7271
|
var MIN_AUTO_SAVE_AFTER_PAUSE_MS = 500;
|
|
7272
|
+
var DEFAULT_BLOCKED_COUNTRIES = ["RU", "BY", "CN", "IR", "KP", "SY", "CU"];
|
|
6158
7273
|
var FOLDER_ACCESS = ["all", "projects", "project"];
|
|
6159
|
-
var PROVIDERS = ["claude", "antigravity"];
|
|
7274
|
+
var PROVIDERS = ["claude", "antigravity", "codex"];
|
|
6160
7275
|
var PERMISSION_MODES = [
|
|
6161
7276
|
"default",
|
|
6162
7277
|
"acceptEdits",
|
|
@@ -6191,13 +7306,17 @@ var DEFAULT_APP_SETTINGS = {
|
|
|
6191
7306
|
limitStopFiveHourPct: 95,
|
|
6192
7307
|
limitStopSevenDayPct: 99,
|
|
6193
7308
|
// Оба: до появления настройки приложение предлагало обоих агентов.
|
|
6194
|
-
enabledProviders: ["claude", "antigravity"]
|
|
7309
|
+
enabledProviders: ["claude", "antigravity", "codex"],
|
|
7310
|
+
// Включено: запрос без VPN может стоить аккаунта, а тому, у кого маршрут и
|
|
7311
|
+
// так из разрешённой страны, защита ничем не мешает.
|
|
7312
|
+
networkGuardEnabled: true,
|
|
7313
|
+
networkGuardBlockedCountries: DEFAULT_BLOCKED_COUNTRIES
|
|
6195
7314
|
};
|
|
6196
7315
|
var AppSettingsStore = class {
|
|
6197
7316
|
#settings = { ...DEFAULT_APP_SETTINGS };
|
|
6198
7317
|
#storePath;
|
|
6199
7318
|
constructor(dataDir) {
|
|
6200
|
-
this.#storePath =
|
|
7319
|
+
this.#storePath = path18.join(dataDir, "app-settings.json");
|
|
6201
7320
|
}
|
|
6202
7321
|
async load() {
|
|
6203
7322
|
try {
|
|
@@ -6221,7 +7340,7 @@ var AppSettingsStore = class {
|
|
|
6221
7340
|
async update(patch) {
|
|
6222
7341
|
const next = sanitize2(patch, this.#settings);
|
|
6223
7342
|
this.#settings = next;
|
|
6224
|
-
await mkdir10(
|
|
7343
|
+
await mkdir10(path18.dirname(this.#storePath), { recursive: true });
|
|
6225
7344
|
await writeFile11(this.#storePath, JSON.stringify(next, null, 2), "utf8");
|
|
6226
7345
|
return this.current;
|
|
6227
7346
|
}
|
|
@@ -6276,10 +7395,19 @@ function sanitize2(value, fallback) {
|
|
|
6276
7395
|
if (picked.length > 0)
|
|
6277
7396
|
result.enabledProviders = picked;
|
|
6278
7397
|
}
|
|
7398
|
+
if (typeof record.networkGuardEnabled === "boolean") {
|
|
7399
|
+
result.networkGuardEnabled = record.networkGuardEnabled;
|
|
7400
|
+
}
|
|
7401
|
+
const countries = record.networkGuardBlockedCountries;
|
|
7402
|
+
if (Array.isArray(countries)) {
|
|
7403
|
+
result.networkGuardBlockedCountries = [
|
|
7404
|
+
...new Set(countries.filter((c) => typeof c === "string").map((c) => c.trim().toUpperCase()).filter((c) => /^[A-Z]{2}$/.test(c)))
|
|
7405
|
+
];
|
|
7406
|
+
}
|
|
6279
7407
|
return result;
|
|
6280
7408
|
}
|
|
6281
7409
|
function claudeHome() {
|
|
6282
|
-
return
|
|
7410
|
+
return path18.join(homedir5(), ".claude");
|
|
6283
7411
|
}
|
|
6284
7412
|
function writableRoots(mode, cwd, workspacePaths) {
|
|
6285
7413
|
if (mode === "all")
|
|
@@ -6289,7 +7417,7 @@ function writableRoots(mode, cwd, workspacePaths) {
|
|
|
6289
7417
|
return [...roots];
|
|
6290
7418
|
for (const workspace of [cwd, ...workspacePaths]) {
|
|
6291
7419
|
roots.add(workspace);
|
|
6292
|
-
const parent =
|
|
7420
|
+
const parent = path18.dirname(workspace);
|
|
6293
7421
|
if (parent !== workspace)
|
|
6294
7422
|
roots.add(parent);
|
|
6295
7423
|
}
|
|
@@ -6298,11 +7426,11 @@ function writableRoots(mode, cwd, workspacePaths) {
|
|
|
6298
7426
|
|
|
6299
7427
|
// ../host/dist/handoff-store.js
|
|
6300
7428
|
import { mkdir as mkdir11, readFile as readFile13, readdir as readdir3, rm as rm4, writeFile as writeFile12 } from "node:fs/promises";
|
|
6301
|
-
import
|
|
7429
|
+
import path19 from "node:path";
|
|
6302
7430
|
var HandoffStore = class {
|
|
6303
7431
|
#dir;
|
|
6304
7432
|
constructor(dataDir) {
|
|
6305
|
-
this.#dir =
|
|
7433
|
+
this.#dir = path19.join(dataDir, "handoffs");
|
|
6306
7434
|
}
|
|
6307
7435
|
async get(sessionId) {
|
|
6308
7436
|
const file = this.#file(sessionId);
|
|
@@ -6339,7 +7467,7 @@ var HandoffStore = class {
|
|
|
6339
7467
|
#file(sessionId) {
|
|
6340
7468
|
if (!/^[\w-]{1,128}$/.test(sessionId))
|
|
6341
7469
|
return null;
|
|
6342
|
-
return
|
|
7470
|
+
return path19.join(this.#dir, `${sessionId}.json`);
|
|
6343
7471
|
}
|
|
6344
7472
|
};
|
|
6345
7473
|
var SAME_WINDOW_MS = 5 * 6e4;
|
|
@@ -6347,7 +7475,7 @@ var LimitStopState = class {
|
|
|
6347
7475
|
#fired = {};
|
|
6348
7476
|
#storePath;
|
|
6349
7477
|
constructor(dataDir) {
|
|
6350
|
-
this.#storePath =
|
|
7478
|
+
this.#storePath = path19.join(dataDir, "limit-stop.json");
|
|
6351
7479
|
}
|
|
6352
7480
|
async load() {
|
|
6353
7481
|
try {
|
|
@@ -6383,7 +7511,7 @@ var LimitStopState = class {
|
|
|
6383
7511
|
/** Запомнить срабатывание до `until` (момент сброса окна). */
|
|
6384
7512
|
async mark(limit, until) {
|
|
6385
7513
|
this.#fired[limit] = until;
|
|
6386
|
-
await mkdir11(
|
|
7514
|
+
await mkdir11(path19.dirname(this.#storePath), { recursive: true });
|
|
6387
7515
|
await writeFile12(this.#storePath, JSON.stringify(this.#fired, null, 2), "utf8");
|
|
6388
7516
|
}
|
|
6389
7517
|
};
|
|
@@ -6489,8 +7617,8 @@ function capitalize(text) {
|
|
|
6489
7617
|
|
|
6490
7618
|
// ../host/dist/plugins.js
|
|
6491
7619
|
import { readFile as readFile14 } from "node:fs/promises";
|
|
6492
|
-
import { homedir as
|
|
6493
|
-
import
|
|
7620
|
+
import { homedir as homedir6 } from "node:os";
|
|
7621
|
+
import path20 from "node:path";
|
|
6494
7622
|
var TIMEOUT_MS2 = 12e4;
|
|
6495
7623
|
var SAFE_ARG = /^[A-Za-z0-9][A-Za-z0-9._@/-]*$/;
|
|
6496
7624
|
var PluginCli = class {
|
|
@@ -6589,10 +7717,10 @@ async function readCatalog(markets, installed) {
|
|
|
6589
7717
|
return lists.flat().sort((a, b) => a.name.localeCompare(b.name));
|
|
6590
7718
|
}
|
|
6591
7719
|
async function readMarketplace(market, installed) {
|
|
6592
|
-
const root = market.installLocation ||
|
|
7720
|
+
const root = market.installLocation || path20.join(homedir6(), ".claude", "plugins", "marketplaces", market.name);
|
|
6593
7721
|
let manifest;
|
|
6594
7722
|
try {
|
|
6595
|
-
const text = await readFile14(
|
|
7723
|
+
const text = await readFile14(path20.join(root, ".claude-plugin", "marketplace.json"), "utf8");
|
|
6596
7724
|
manifest = JSON.parse(text);
|
|
6597
7725
|
} catch {
|
|
6598
7726
|
return [];
|
|
@@ -6635,7 +7763,7 @@ function authorName(raw) {
|
|
|
6635
7763
|
}
|
|
6636
7764
|
|
|
6637
7765
|
// ../host/dist/auth.js
|
|
6638
|
-
import { EventEmitter as
|
|
7766
|
+
import { EventEmitter as EventEmitter6 } from "node:events";
|
|
6639
7767
|
var QUICK_TIMEOUT_MS = 3e4;
|
|
6640
7768
|
var LOGIN_TIMEOUT_MS = 10 * 6e4;
|
|
6641
7769
|
var URL_RE = /https:\/\/\S*\/oauth\/authorize\S*/;
|
|
@@ -6647,7 +7775,7 @@ var IDLE = {
|
|
|
6647
7775
|
output: "",
|
|
6648
7776
|
error: null
|
|
6649
7777
|
};
|
|
6650
|
-
var AuthCli = class extends
|
|
7778
|
+
var AuthCli = class extends EventEmitter6 {
|
|
6651
7779
|
#child = null;
|
|
6652
7780
|
#state = IDLE;
|
|
6653
7781
|
#timer = null;
|
|
@@ -6832,9 +7960,9 @@ function describe5(error) {
|
|
|
6832
7960
|
}
|
|
6833
7961
|
|
|
6834
7962
|
// ../host/dist/relay-link.js
|
|
6835
|
-
import { EventEmitter as
|
|
7963
|
+
import { EventEmitter as EventEmitter7 } from "node:events";
|
|
6836
7964
|
import { readFile as readFile15, writeFile as writeFile13, rm as rm5, mkdir as mkdir12 } from "node:fs/promises";
|
|
6837
|
-
import
|
|
7965
|
+
import path21 from "node:path";
|
|
6838
7966
|
import { hostname } from "node:os";
|
|
6839
7967
|
import WebSocket from "ws";
|
|
6840
7968
|
|
|
@@ -6851,7 +7979,7 @@ var SILENCE_TIMEOUT_MS = 95e3;
|
|
|
6851
7979
|
function jitter(delay2) {
|
|
6852
7980
|
return Math.round(delay2 * (0.8 + Math.random() * 0.4));
|
|
6853
7981
|
}
|
|
6854
|
-
var RelayLink = class extends
|
|
7982
|
+
var RelayLink = class extends EventEmitter7 {
|
|
6855
7983
|
#credentials = null;
|
|
6856
7984
|
#socket = null;
|
|
6857
7985
|
#retry = RETRY_MIN_MS;
|
|
@@ -6889,7 +8017,7 @@ var RelayLink = class extends EventEmitter4 {
|
|
|
6889
8017
|
#log;
|
|
6890
8018
|
constructor(options2) {
|
|
6891
8019
|
super();
|
|
6892
|
-
this.#file =
|
|
8020
|
+
this.#file = path21.join(options2.dataDir, "relay.json");
|
|
6893
8021
|
this.#defaultUrl = options2.defaultUrl.replace(/\/+$/, "");
|
|
6894
8022
|
this.#log = options2.log ?? ((msg) => console.log(`[relay] ${msg}`));
|
|
6895
8023
|
}
|
|
@@ -6983,7 +8111,7 @@ var RelayLink = class extends EventEmitter4 {
|
|
|
6983
8111
|
}
|
|
6984
8112
|
}
|
|
6985
8113
|
async #write(credentials) {
|
|
6986
|
-
await mkdir12(
|
|
8114
|
+
await mkdir12(path21.dirname(this.#file), { recursive: true });
|
|
6987
8115
|
await writeFile13(this.#file, `${JSON.stringify(credentials, null, 2)}
|
|
6988
8116
|
`, {
|
|
6989
8117
|
encoding: "utf8",
|
|
@@ -7239,14 +8367,14 @@ function humanize(error) {
|
|
|
7239
8367
|
}
|
|
7240
8368
|
|
|
7241
8369
|
// ../host/dist/terminal.js
|
|
7242
|
-
import { EventEmitter as
|
|
7243
|
-
import { access as access2, constants as
|
|
7244
|
-
import
|
|
7245
|
-
import { randomUUID as
|
|
8370
|
+
import { EventEmitter as EventEmitter8 } from "node:events";
|
|
8371
|
+
import { access as access2, constants as constants3 } from "node:fs/promises";
|
|
8372
|
+
import path22 from "node:path";
|
|
8373
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
7246
8374
|
var SCROLLBACK_BYTES = 256 * 1024;
|
|
7247
8375
|
var FLUSH_MS = 12;
|
|
7248
8376
|
var MAX_TERMINALS = 24;
|
|
7249
|
-
var TerminalHub = class extends
|
|
8377
|
+
var TerminalHub = class extends EventEmitter8 {
|
|
7250
8378
|
#terminals = /* @__PURE__ */ new Map();
|
|
7251
8379
|
#module = null;
|
|
7252
8380
|
list(workspaceId) {
|
|
@@ -7279,7 +8407,7 @@ var TerminalHub = class extends EventEmitter5 {
|
|
|
7279
8407
|
env: environment()
|
|
7280
8408
|
});
|
|
7281
8409
|
const info = {
|
|
7282
|
-
id:
|
|
8410
|
+
id: randomUUID5(),
|
|
7283
8411
|
workspaceId: options2.workspaceId,
|
|
7284
8412
|
title: shell.title,
|
|
7285
8413
|
shell: shell.file,
|
|
@@ -7411,36 +8539,36 @@ async function defaultShell() {
|
|
|
7411
8539
|
if (pwsh)
|
|
7412
8540
|
return { file: pwsh, args: [], title: "pwsh" };
|
|
7413
8541
|
const system = process.env.SystemRoot ?? "C:\\Windows";
|
|
7414
|
-
const powershell =
|
|
8542
|
+
const powershell = path22.join(system, "System32/WindowsPowerShell/v1.0/powershell.exe");
|
|
7415
8543
|
if (await exists2(powershell))
|
|
7416
8544
|
return { file: powershell, args: [], title: "powershell" };
|
|
7417
8545
|
return { file: process.env.COMSPEC ?? "cmd.exe", args: [], title: "cmd" };
|
|
7418
8546
|
}
|
|
7419
8547
|
const shell = process.env.SHELL;
|
|
7420
8548
|
if (shell && await exists2(shell))
|
|
7421
|
-
return { file: shell, args: [], title:
|
|
8549
|
+
return { file: shell, args: [], title: path22.basename(shell) };
|
|
7422
8550
|
if (await exists2("/bin/bash"))
|
|
7423
8551
|
return { file: "/bin/bash", args: [], title: "bash" };
|
|
7424
8552
|
return { file: "/bin/sh", args: [], title: "sh" };
|
|
7425
8553
|
}
|
|
7426
8554
|
async function findOnPath(name) {
|
|
7427
|
-
const dirs = (process.env.PATH ?? "").split(
|
|
8555
|
+
const dirs = (process.env.PATH ?? "").split(path22.delimiter).filter(Boolean);
|
|
7428
8556
|
for (const dir of dirs) {
|
|
7429
|
-
const candidate =
|
|
8557
|
+
const candidate = path22.join(dir, name);
|
|
7430
8558
|
if (await exists2(candidate))
|
|
7431
8559
|
return candidate;
|
|
7432
8560
|
}
|
|
7433
8561
|
return null;
|
|
7434
8562
|
}
|
|
7435
8563
|
function exists2(file) {
|
|
7436
|
-
return access2(file,
|
|
8564
|
+
return access2(file, constants3.X_OK).then(() => true, () => false);
|
|
7437
8565
|
}
|
|
7438
8566
|
|
|
7439
8567
|
// ../host/dist/mcp-config.js
|
|
7440
8568
|
import { readFile as readFile16, writeFile as writeFile14, mkdir as mkdir13 } from "node:fs/promises";
|
|
7441
|
-
import
|
|
8569
|
+
import path23 from "node:path";
|
|
7442
8570
|
var MCP_FILE = ".mcp.json";
|
|
7443
|
-
var LOCAL_SETTINGS2 =
|
|
8571
|
+
var LOCAL_SETTINGS2 = path23.join(".claude", "settings.local.json");
|
|
7444
8572
|
var queues = /* @__PURE__ */ new Map();
|
|
7445
8573
|
function serialize(cwd, work) {
|
|
7446
8574
|
const previous = queues.get(cwd) ?? Promise.resolve();
|
|
@@ -7449,7 +8577,7 @@ function serialize(cwd, work) {
|
|
|
7449
8577
|
return next;
|
|
7450
8578
|
}
|
|
7451
8579
|
async function readProjectServers(cwd) {
|
|
7452
|
-
const file = await readJson(
|
|
8580
|
+
const file = await readJson(path23.join(cwd, MCP_FILE));
|
|
7453
8581
|
const servers = file?.mcpServers;
|
|
7454
8582
|
if (!servers || typeof servers !== "object")
|
|
7455
8583
|
return {};
|
|
@@ -7457,23 +8585,23 @@ async function readProjectServers(cwd) {
|
|
|
7457
8585
|
}
|
|
7458
8586
|
function addProjectServer(cwd, name, config) {
|
|
7459
8587
|
return serialize(cwd, async () => {
|
|
7460
|
-
const file = await readJson(
|
|
8588
|
+
const file = await readJson(path23.join(cwd, MCP_FILE)) ?? {};
|
|
7461
8589
|
const servers = file.mcpServers ?? {};
|
|
7462
8590
|
servers[name] = config;
|
|
7463
8591
|
file.mcpServers = servers;
|
|
7464
|
-
await writeJson(
|
|
8592
|
+
await writeJson(path23.join(cwd, MCP_FILE), file);
|
|
7465
8593
|
await approve(cwd, name);
|
|
7466
8594
|
});
|
|
7467
8595
|
}
|
|
7468
8596
|
function removeProjectServer(cwd, name) {
|
|
7469
8597
|
return serialize(cwd, async () => {
|
|
7470
|
-
const file = await readJson(
|
|
8598
|
+
const file = await readJson(path23.join(cwd, MCP_FILE));
|
|
7471
8599
|
const servers = file?.mcpServers;
|
|
7472
8600
|
if (!servers || !(name in servers))
|
|
7473
8601
|
return false;
|
|
7474
8602
|
delete servers[name];
|
|
7475
|
-
await writeJson(
|
|
7476
|
-
const settingsPath =
|
|
8603
|
+
await writeJson(path23.join(cwd, MCP_FILE), file);
|
|
8604
|
+
const settingsPath = path23.join(cwd, LOCAL_SETTINGS2);
|
|
7477
8605
|
const settings = await readJson(settingsPath);
|
|
7478
8606
|
const enabled = settings?.enabledMcpjsonServers;
|
|
7479
8607
|
if (settings && Array.isArray(enabled) && enabled.includes(name)) {
|
|
@@ -7484,7 +8612,7 @@ function removeProjectServer(cwd, name) {
|
|
|
7484
8612
|
});
|
|
7485
8613
|
}
|
|
7486
8614
|
async function approve(cwd, name) {
|
|
7487
|
-
const file =
|
|
8615
|
+
const file = path23.join(cwd, LOCAL_SETTINGS2);
|
|
7488
8616
|
const settings = await readJson(file) ?? {};
|
|
7489
8617
|
const enabled = Array.isArray(settings.enabledMcpjsonServers) ? settings.enabledMcpjsonServers : [];
|
|
7490
8618
|
if (!enabled.includes(name))
|
|
@@ -7530,13 +8658,13 @@ async function readJson(file) {
|
|
|
7530
8658
|
if (error.code === "ENOENT")
|
|
7531
8659
|
return null;
|
|
7532
8660
|
if (error instanceof SyntaxError) {
|
|
7533
|
-
throw new Error(`\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0440\u0430\u0437\u043E\u0431\u0440\u0430\u0442\u044C ${
|
|
8661
|
+
throw new Error(`\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0440\u0430\u0437\u043E\u0431\u0440\u0430\u0442\u044C ${path23.basename(file)}: ${error.message}`);
|
|
7534
8662
|
}
|
|
7535
8663
|
throw error;
|
|
7536
8664
|
}
|
|
7537
8665
|
}
|
|
7538
8666
|
async function writeJson(file, data) {
|
|
7539
|
-
await mkdir13(
|
|
8667
|
+
await mkdir13(path23.dirname(file), { recursive: true });
|
|
7540
8668
|
await writeFile14(file, `${JSON.stringify(data, null, 2)}
|
|
7541
8669
|
`, "utf8");
|
|
7542
8670
|
}
|
|
@@ -7555,6 +8683,8 @@ var BUSY_STATES2 = /* @__PURE__ */ new Set([
|
|
|
7555
8683
|
"reconnecting"
|
|
7556
8684
|
]);
|
|
7557
8685
|
var HostServer = class {
|
|
8686
|
+
#httpServer = null;
|
|
8687
|
+
#webSockets = null;
|
|
7558
8688
|
#options;
|
|
7559
8689
|
#workspaces;
|
|
7560
8690
|
#archive;
|
|
@@ -7567,6 +8697,7 @@ var HostServer = class {
|
|
|
7567
8697
|
/** Опросы статуса для идущих OAuth-входов MCP, ключ — сессия и сервер. */
|
|
7568
8698
|
#mcpAuthWatch = /* @__PURE__ */ new Map();
|
|
7569
8699
|
#antigravity;
|
|
8700
|
+
#codex;
|
|
7570
8701
|
/** Боковые сессии («вопрос на полях») по id родителя — не больше одной на разговор. */
|
|
7571
8702
|
#sides = /* @__PURE__ */ new Map();
|
|
7572
8703
|
/**
|
|
@@ -7614,7 +8745,7 @@ var HostServer = class {
|
|
|
7614
8745
|
#limitHandoffBusy = /* @__PURE__ */ new Set();
|
|
7615
8746
|
/** Живые id сессий, у которых сейчас пишется ручной хэндофф. */
|
|
7616
8747
|
#handoffBusy = /* @__PURE__ */ new Set();
|
|
7617
|
-
#hostId =
|
|
8748
|
+
#hostId = randomUUID6();
|
|
7618
8749
|
#plugins = new PluginCli();
|
|
7619
8750
|
#auth = new AuthCli();
|
|
7620
8751
|
#relay;
|
|
@@ -7638,6 +8769,7 @@ var HostServer = class {
|
|
|
7638
8769
|
this.#folders = new Folders(options2.dataDir);
|
|
7639
8770
|
this.#settings = new AppSettingsStore(options2.dataDir);
|
|
7640
8771
|
this.#antigravity = new AntigravityStore(options2.dataDir);
|
|
8772
|
+
this.#codex = new AntigravityStore(options2.dataDir, "codex");
|
|
7641
8773
|
this.#handoffs = new HandoffStore(options2.dataDir);
|
|
7642
8774
|
this.#limitStop = new LimitStopState(options2.dataDir);
|
|
7643
8775
|
this.#finder = new ProjectFinder(this.#workspaces);
|
|
@@ -7664,9 +8796,11 @@ var HostServer = class {
|
|
|
7664
8796
|
const id = workspaceIdFromPath(dir);
|
|
7665
8797
|
const onDisk = await this.#workspaces.sessionsOnDisk(id);
|
|
7666
8798
|
const antigravity = await this.#antigravity.list(id);
|
|
7667
|
-
|
|
8799
|
+
const codex = await this.#codex.list(id);
|
|
8800
|
+
return [...onDisk.map((s) => s.sessionId), ...antigravity.map((r) => r.id), ...codex.map((r) => r.id)];
|
|
7668
8801
|
});
|
|
7669
8802
|
await this.#antigravity.remapWorkspaceIds(remap);
|
|
8803
|
+
await this.#codex.remapWorkspaceIds(remap);
|
|
7670
8804
|
await this.#archive.remapWorkspaceIds(remap);
|
|
7671
8805
|
await this.#pins.remapWorkspaceIds(remap);
|
|
7672
8806
|
await this.#order.remapWorkspaceIds(remap);
|
|
@@ -7680,6 +8814,7 @@ var HostServer = class {
|
|
|
7680
8814
|
await this.#folders.load();
|
|
7681
8815
|
await this.#carryOverWorkspaceIds();
|
|
7682
8816
|
await this.#settings.load();
|
|
8817
|
+
this.#wireNetworkGuard();
|
|
7683
8818
|
await this.#limitStop.load();
|
|
7684
8819
|
await this.#github.load();
|
|
7685
8820
|
await warmClaudeLauncher();
|
|
@@ -7700,6 +8835,8 @@ var HostServer = class {
|
|
|
7700
8835
|
resolve();
|
|
7701
8836
|
});
|
|
7702
8837
|
});
|
|
8838
|
+
this.#httpServer = httpServer;
|
|
8839
|
+
this.#webSockets = wss;
|
|
7703
8840
|
await this.#relay.start().catch((error) => {
|
|
7704
8841
|
console.error("[relay]", error instanceof Error ? error.message : String(error));
|
|
7705
8842
|
});
|
|
@@ -7708,7 +8845,25 @@ var HostServer = class {
|
|
|
7708
8845
|
this.#limitTimer = setInterval(() => this.#sweepUsage(), LIMIT_SWEEP_MS);
|
|
7709
8846
|
this.#limitTimer.unref();
|
|
7710
8847
|
}
|
|
8848
|
+
/**
|
|
8849
|
+
* Защита от запросов без VPN (см. network-guard.ts): при блоке гасим
|
|
8850
|
+
* процессы всех живых сессий — идущий ход шлёт запрос за запросом, и
|
|
8851
|
+
* дожидаться его конца нельзя.
|
|
8852
|
+
*/
|
|
8853
|
+
#wireNetworkGuard() {
|
|
8854
|
+
networkGuard.on("change", (state) => {
|
|
8855
|
+
this.#broadcast({ type: "network.state", state });
|
|
8856
|
+
if (state.status !== "blocked")
|
|
8857
|
+
return;
|
|
8858
|
+
const reason = state.reason ?? "VPN \u043D\u0435 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442";
|
|
8859
|
+
for (const session of this.#sessions.values())
|
|
8860
|
+
session.suspend(reason);
|
|
8861
|
+
});
|
|
8862
|
+
const { networkGuardEnabled, networkGuardBlockedCountries } = this.#settings.current;
|
|
8863
|
+
networkGuard.configure(networkGuardEnabled, networkGuardBlockedCountries);
|
|
8864
|
+
}
|
|
7711
8865
|
async shutdown() {
|
|
8866
|
+
networkGuard.stop();
|
|
7712
8867
|
if (this.#autoStopTimer)
|
|
7713
8868
|
clearInterval(this.#autoStopTimer);
|
|
7714
8869
|
this.#autoStopTimer = null;
|
|
@@ -7720,6 +8875,16 @@ var HostServer = class {
|
|
|
7720
8875
|
await this.#relay.close();
|
|
7721
8876
|
await Promise.all([...this.#sessions.values()].map((s) => s.close("host shutdown")));
|
|
7722
8877
|
this.#sessions.clear();
|
|
8878
|
+
const wss = this.#webSockets;
|
|
8879
|
+
const http = this.#httpServer;
|
|
8880
|
+
this.#webSockets = null;
|
|
8881
|
+
this.#httpServer = null;
|
|
8882
|
+
for (const socket of wss?.clients ?? [])
|
|
8883
|
+
socket.terminate();
|
|
8884
|
+
await Promise.all([
|
|
8885
|
+
...wss ? [new Promise((resolve) => wss.close(() => resolve()))] : [],
|
|
8886
|
+
...http ? [new Promise((resolve) => http.close(() => resolve()))] : []
|
|
8887
|
+
]);
|
|
7723
8888
|
}
|
|
7724
8889
|
// -------------------------------------------------------------------------
|
|
7725
8890
|
// WebSocket
|
|
@@ -7876,6 +9041,7 @@ var HostServer = class {
|
|
|
7876
9041
|
this.#sendLive(socket);
|
|
7877
9042
|
this.#auth.status().then((status) => this.#send(socket, { type: "auth.status", status })).catch(() => void 0);
|
|
7878
9043
|
this.#send(socket, { type: "relay.state", state: this.#relay.state });
|
|
9044
|
+
this.#send(socket, { type: "network.state", state: networkGuard.state });
|
|
7879
9045
|
return;
|
|
7880
9046
|
case "ping":
|
|
7881
9047
|
this.#send(socket, { type: "pong", nonce: frame.nonce });
|
|
@@ -7921,6 +9087,7 @@ var HostServer = class {
|
|
|
7921
9087
|
return;
|
|
7922
9088
|
}
|
|
7923
9089
|
case "workspace.find.agent":
|
|
9090
|
+
await networkGuard.ensureFresh();
|
|
7924
9091
|
this.#findWithAgent(frame.query);
|
|
7925
9092
|
return;
|
|
7926
9093
|
case "workspace.roots":
|
|
@@ -8135,10 +9302,12 @@ var HostServer = class {
|
|
|
8135
9302
|
return;
|
|
8136
9303
|
}
|
|
8137
9304
|
case "prompt.send":
|
|
9305
|
+
await networkGuard.ensureFresh();
|
|
8138
9306
|
this.#approveAfterLimit(frame.sessionId);
|
|
8139
9307
|
this.#requireSession(frame.sessionId).sendPrompt(frame.text, frame.images);
|
|
8140
9308
|
return;
|
|
8141
9309
|
case "prompt.edit":
|
|
9310
|
+
await networkGuard.ensureFresh();
|
|
8142
9311
|
this.#approveAfterLimit(frame.sessionId);
|
|
8143
9312
|
await this.#requireSession(frame.sessionId).editPrompt(frame.userIndex, frame.text, frame.images);
|
|
8144
9313
|
return;
|
|
@@ -8146,6 +9315,7 @@ var HostServer = class {
|
|
|
8146
9315
|
this.#requireSession(frame.sessionId).respondPermission(frame.requestId, frame.decision, frame.reason, frame.answers, frame.annotations, frame.plan);
|
|
8147
9316
|
return;
|
|
8148
9317
|
case "side.ask":
|
|
9318
|
+
await networkGuard.ensureFresh();
|
|
8149
9319
|
this.#ensureSide(this.#requireSession(frame.sessionId)).sendPrompt(frame.text);
|
|
8150
9320
|
return;
|
|
8151
9321
|
// Поднять форк, ничего не спрашивая: клиенту нужен его id, чтобы
|
|
@@ -8560,6 +9730,7 @@ var HostServer = class {
|
|
|
8560
9730
|
await this.#sendSessionList(workspace.id);
|
|
8561
9731
|
}
|
|
8562
9732
|
}
|
|
9733
|
+
networkGuard.configure(settings.networkGuardEnabled, settings.networkGuardBlockedCountries);
|
|
8563
9734
|
if (before.terminalEnabled && !settings.terminalEnabled) {
|
|
8564
9735
|
for (const terminal of this.#terminals.list()) {
|
|
8565
9736
|
this.#terminals.close(terminal.id);
|
|
@@ -8572,9 +9743,13 @@ var HostServer = class {
|
|
|
8572
9743
|
this.#send(socket, { type: "auth.status", status: await this.#auth.status() });
|
|
8573
9744
|
this.#send(socket, { type: "auth.login", state: this.#auth.loginState });
|
|
8574
9745
|
return;
|
|
8575
|
-
case "auth.login":
|
|
9746
|
+
case "auth.login": {
|
|
9747
|
+
const blocked = (await networkGuard.ensureFresh()).status === "blocked";
|
|
9748
|
+
if (blocked)
|
|
9749
|
+
throw new Error(`${networkGuard.blockedReason()}. \u0412\u0445\u043E\u0434 \u043E\u0442\u043B\u043E\u0436\u0435\u043D.`);
|
|
8576
9750
|
await this.#auth.login(frame.mode, frame.switchAccount === true);
|
|
8577
9751
|
return;
|
|
9752
|
+
}
|
|
8578
9753
|
case "auth.code":
|
|
8579
9754
|
this.#auth.submitCode(frame.code);
|
|
8580
9755
|
return;
|
|
@@ -9109,7 +10284,31 @@ var HostServer = class {
|
|
|
9109
10284
|
throw new Error(`\u0412\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D: ${frame.workspaceId}`);
|
|
9110
10285
|
const provider = frame.provider ?? "claude";
|
|
9111
10286
|
if (!frame.resume && !this.#settings.current.enabledProviders.includes(provider)) {
|
|
9112
|
-
throw new Error(`${provider === "antigravity" ? "Antigravity" : "Claude"} \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D \u0432 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430\u0445`);
|
|
10287
|
+
throw new Error(`${provider === "antigravity" ? "Antigravity" : provider === "codex" ? "Codex" : "Claude"} \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D \u0432 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430\u0445`);
|
|
10288
|
+
}
|
|
10289
|
+
const codexRecord = frame.resume ? await this.#codex.find(frame.resume) : null;
|
|
10290
|
+
if (codexRecord || !frame.resume && provider === "codex") {
|
|
10291
|
+
if (frame.fork)
|
|
10292
|
+
throw new Error("\u0412\u0435\u0442\u0432\u043B\u0435\u043D\u0438\u0435 \u0441\u0435\u0441\u0441\u0438\u0439 Codex \u043F\u043E\u043A\u0430 \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F");
|
|
10293
|
+
if (codexRecord && codexRecord.workspaceId !== workspace.id)
|
|
10294
|
+
throw new Error("\u0421\u0435\u0441\u0441\u0438\u044F Codex \u043F\u0440\u0438\u043D\u0430\u0434\u043B\u0435\u0436\u0438\u0442 \u0434\u0440\u0443\u0433\u043E\u043C\u0443 \u043F\u0440\u043E\u0435\u043A\u0442\u0443");
|
|
10295
|
+
if (codexRecord)
|
|
10296
|
+
await this.#archive.unarchive([codexRecord.id]);
|
|
10297
|
+
const session2 = new CodexSession({
|
|
10298
|
+
workspaceId: workspace.id,
|
|
10299
|
+
cwd: codexRecord?.cwd ?? workspace.path,
|
|
10300
|
+
store: this.#codex,
|
|
10301
|
+
...codexRecord ? { record: codexRecord } : {},
|
|
10302
|
+
...frame.permissionMode ? { permissionMode: frame.permissionMode } : {},
|
|
10303
|
+
...frame.model ? { model: frame.model } : {},
|
|
10304
|
+
defaultTitle: workspace.name
|
|
10305
|
+
});
|
|
10306
|
+
this.#wire(session2);
|
|
10307
|
+
this.#sessions.set(session2.id, session2);
|
|
10308
|
+
session2.start();
|
|
10309
|
+
this.#sendLive();
|
|
10310
|
+
this.#send(socket, { type: "session.summary", session: session2.summary() });
|
|
10311
|
+
return;
|
|
9113
10312
|
}
|
|
9114
10313
|
const agyRecord = frame.resume ? await this.#antigravity.find(frame.resume) : null;
|
|
9115
10314
|
if (agyRecord || !frame.resume && frame.provider === "antigravity") {
|
|
@@ -9273,7 +10472,7 @@ var HostServer = class {
|
|
|
9273
10472
|
this.#broadcast({ type: "context.state", sessionId: session.id, context });
|
|
9274
10473
|
});
|
|
9275
10474
|
session.on("stderr", (data) => {
|
|
9276
|
-
const tag = session instanceof AntigravitySession ? "agy" : "claude";
|
|
10475
|
+
const tag = session instanceof AntigravitySession ? "agy" : session instanceof CodexSession ? "codex" : "claude";
|
|
9277
10476
|
process.stderr.write(`[${tag} ${session.id.slice(0, 8)}] ${data}`);
|
|
9278
10477
|
});
|
|
9279
10478
|
}
|
|
@@ -9293,7 +10492,7 @@ var HostServer = class {
|
|
|
9293
10492
|
* разговора пока и не было.
|
|
9294
10493
|
*/
|
|
9295
10494
|
#ensureSide(parent) {
|
|
9296
|
-
if (parent instanceof
|
|
10495
|
+
if (!(parent instanceof ClaudeSession)) {
|
|
9297
10496
|
throw new Error("\u0412\u043E\u043F\u0440\u043E\u0441 \u043D\u0430 \u043F\u043E\u043B\u044F\u0445 \u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D \u0442\u043E\u043B\u044C\u043A\u043E \u0432 \u0441\u0435\u0441\u0441\u0438\u044F\u0445 Claude");
|
|
9298
10497
|
}
|
|
9299
10498
|
const existing = this.#sides.get(parent.id);
|
|
@@ -9351,7 +10550,7 @@ var HostServer = class {
|
|
|
9351
10550
|
*/
|
|
9352
10551
|
#roots() {
|
|
9353
10552
|
const roots = this.#finder.roots();
|
|
9354
|
-
return roots.length > 0 ? roots : [
|
|
10553
|
+
return roots.length > 0 ? roots : [homedir7()];
|
|
9355
10554
|
}
|
|
9356
10555
|
/**
|
|
9357
10556
|
* Где сессии этого каталога разрешено писать без вопроса.
|
|
@@ -9593,7 +10792,7 @@ var HostServer = class {
|
|
|
9593
10792
|
live.setTitle(title);
|
|
9594
10793
|
return;
|
|
9595
10794
|
}
|
|
9596
|
-
if (await this.#antigravity.rename(sessionId, title)) {
|
|
10795
|
+
if (await this.#antigravity.rename(sessionId, title) || await this.#codex.rename(sessionId, title)) {
|
|
9597
10796
|
if (workspaceId)
|
|
9598
10797
|
await this.#sendSessionList(workspaceId);
|
|
9599
10798
|
return;
|
|
@@ -9650,6 +10849,11 @@ var HostServer = class {
|
|
|
9650
10849
|
continue;
|
|
9651
10850
|
historical.push(antigravitySummary(record));
|
|
9652
10851
|
}
|
|
10852
|
+
for (const record of await this.#codex.list(workspaceId)) {
|
|
10853
|
+
if (liveIds.has(record.id))
|
|
10854
|
+
continue;
|
|
10855
|
+
historical.push({ ...antigravitySummary(record), provider: "codex", switchModelsOnFlag: false });
|
|
10856
|
+
}
|
|
9653
10857
|
const known = /* @__PURE__ */ new Set([...liveIds, ...historical.map((s) => s.id)]);
|
|
9654
10858
|
await this.#archive.sweep(workspaceId, historical.filter((s) => !this.#pins.has(s.id)).map((s) => ({ id: s.id, updatedAt: s.updatedAt })), known, this.#settings.current.autoArchiveAfterMs);
|
|
9655
10859
|
await this.#pins.sweep(workspaceId, known);
|
|
@@ -9696,15 +10900,15 @@ var HostServer = class {
|
|
|
9696
10900
|
}
|
|
9697
10901
|
#serveStatic(req, res) {
|
|
9698
10902
|
const webRoot = this.#options.webRoot;
|
|
9699
|
-
if (!webRoot || !
|
|
10903
|
+
if (!webRoot || !existsSync5(webRoot)) {
|
|
9700
10904
|
res.writeHead(200, { "content-type": "text/plain; charset=utf-8" });
|
|
9701
10905
|
res.end("CCV host is running. UI is served by the Vite dev server.\n");
|
|
9702
10906
|
return;
|
|
9703
10907
|
}
|
|
9704
10908
|
const url2 = new URL(req.url ?? "/", "http://localhost");
|
|
9705
|
-
const requested =
|
|
9706
|
-
const target = requested.startsWith(webRoot) &&
|
|
9707
|
-
if (!
|
|
10909
|
+
const requested = path24.join(webRoot, path24.normalize(url2.pathname));
|
|
10910
|
+
const target = requested.startsWith(webRoot) && existsSync5(requested) && statSync4(requested).isFile() ? requested : path24.join(webRoot, "index.html");
|
|
10911
|
+
if (!existsSync5(target)) {
|
|
9708
10912
|
res.writeHead(404).end("not found");
|
|
9709
10913
|
return;
|
|
9710
10914
|
}
|
|
@@ -9729,7 +10933,7 @@ function contentType(file) {
|
|
|
9729
10933
|
".svg": "image/svg+xml",
|
|
9730
10934
|
".woff2": "font/woff2"
|
|
9731
10935
|
};
|
|
9732
|
-
return types[
|
|
10936
|
+
return types[path24.extname(file)] ?? "application/octet-stream";
|
|
9733
10937
|
}
|
|
9734
10938
|
function antigravitySummary(record) {
|
|
9735
10939
|
return {
|
|
@@ -9758,9 +10962,9 @@ function antigravitySummary(record) {
|
|
|
9758
10962
|
}
|
|
9759
10963
|
|
|
9760
10964
|
// dist/ccv.js
|
|
9761
|
-
var HERE =
|
|
9762
|
-
var PACKAGE_ROOT =
|
|
9763
|
-
var USAGE = `ccv \u2014 Claude Code Visual: \
|
|
10965
|
+
var HERE = path25.dirname(fileURLToPath(import.meta.url));
|
|
10966
|
+
var PACKAGE_ROOT = path25.resolve(HERE, "..");
|
|
10967
|
+
var USAGE = `ccv \u2014 Claude Code Visual: \u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043A Claude Code, Codex \u0438 Antigravity \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435.
|
|
9764
10968
|
|
|
9765
10969
|
\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u0435:
|
|
9766
10970
|
ccv [\u043E\u043F\u0446\u0438\u0438]
|
|
@@ -9769,12 +10973,12 @@ var USAGE = `ccv \u2014 Claude Code Visual: \u043B\u043E\u043A\u0430\u043B\u044C
|
|
|
9769
10973
|
-p, --port <\u043F\u043E\u0440\u0442> \u043F\u043E\u0440\u0442 \u0445\u043E\u0441\u0442\u0430 (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E 8787)
|
|
9770
10974
|
--host <\u0430\u0434\u0440\u0435\u0441> \u0430\u0434\u0440\u0435\u0441 \u043F\u0440\u043E\u0441\u043B\u0443\u0448\u0438\u0432\u0430\u043D\u0438\u044F (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E 127.0.0.1)
|
|
9771
10975
|
--no-open \u043D\u0435 \u043E\u0442\u043A\u0440\u044B\u0432\u0430\u0442\u044C \u0431\u0440\u0430\u0443\u0437\u0435\u0440
|
|
9772
|
-
--claude \u0447\u0435\u043C \u0431\u0443\u0434\u0443\u0442 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0442\u044C\u0441\u044F \u0441\u0435\u0441\u0441\u0438\u0438, \u0438 \u0432\u044B\u0439\u0442\u0438
|
|
10976
|
+
--claude \u0447\u0435\u043C \u0431\u0443\u0434\u0443\u0442 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0442\u044C\u0441\u044F \u0441\u0435\u0441\u0441\u0438\u0438 Claude, \u0438 \u0432\u044B\u0439\u0442\u0438
|
|
9773
10977
|
-v, --version \u0432\u0435\u0440\u0441\u0438\u044F
|
|
9774
10978
|
-h, --help \u044D\u0442\u0430 \u0441\u043F\u0440\u0430\u0432\u043A\u0430
|
|
9775
10979
|
|
|
9776
10980
|
\u041E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435: CCV_PORT, CCV_HOST, CCV_DATA_DIR, CCV_WEB_ROOT, CCV_RELAY_URL,
|
|
9777
|
-
CCV_CLAUDE_PATH, CCV_BUNDLED_CLAUDE.
|
|
10981
|
+
CCV_CLAUDE_PATH, CCV_BUNDLED_CLAUDE, CCV_CODEX_PATH, CCV_AGY_PATH.
|
|
9778
10982
|
\u0424\u043B\u0430\u0433 \u0432\u0430\u0436\u043D\u0435\u0435 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u043E\u0439 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F, \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u0430\u044F \u0432\u0430\u0436\u043D\u0435\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E.`;
|
|
9779
10983
|
var options = {
|
|
9780
10984
|
port: { type: "string", short: "p" },
|
|
@@ -9826,7 +11030,7 @@ var server = new HostServer({
|
|
|
9826
11030
|
dataDir: defaults.dataDir,
|
|
9827
11031
|
// UI лежит в пакете рядом с бандлом — это и есть разница между установленной
|
|
9828
11032
|
// утилитой и запуском из репозитория, где сборка живёт в `apps/web/dist`.
|
|
9829
|
-
webRoot: process.env.CCV_WEB_ROOT ??
|
|
11033
|
+
webRoot: process.env.CCV_WEB_ROOT ?? path25.join(PACKAGE_ROOT, "web"),
|
|
9830
11034
|
version,
|
|
9831
11035
|
relayUrl: defaults.relayUrl
|
|
9832
11036
|
});
|
|
@@ -9855,7 +11059,7 @@ for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
|
9855
11059
|
}
|
|
9856
11060
|
async function packageVersion() {
|
|
9857
11061
|
try {
|
|
9858
|
-
const text = await readFile17(
|
|
11062
|
+
const text = await readFile17(path25.join(PACKAGE_ROOT, "package.json"), "utf8");
|
|
9859
11063
|
return JSON.parse(text).version ?? "0.0.0";
|
|
9860
11064
|
} catch {
|
|
9861
11065
|
return "0.0.0";
|
|
@@ -9899,7 +11103,7 @@ function openBrowser(target) {
|
|
|
9899
11103
|
return;
|
|
9900
11104
|
}
|
|
9901
11105
|
const command = process.platform === "win32" ? { file: "cmd", args: ["/c", "start", "", target] } : process.platform === "darwin" ? { file: "open", args: [target] } : { file: "xdg-open", args: [target] };
|
|
9902
|
-
const child =
|
|
11106
|
+
const child = spawn4(command.file, command.args, {
|
|
9903
11107
|
stdio: "ignore",
|
|
9904
11108
|
detached: true,
|
|
9905
11109
|
windowsHide: true
|