@i14ks/ccv 0.23.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.
Files changed (28) hide show
  1. package/README.md +10 -2
  2. package/bundle/ccv.mjs +1033 -139
  3. package/package.json +5 -2
  4. package/web/assets/{codemirror-BdKpW4CC.js → codemirror-BABguQzY.js} +1 -1
  5. package/web/assets/{index-B2BGvdag.js → index-BHEVBqv1.js} +1 -1
  6. package/web/assets/{index-D83he8x-.js → index-BJQdUP7a.js} +1 -1
  7. package/web/assets/{index-Dd7rh20x.js → index-BQiRpvdM.js} +1 -1
  8. package/web/assets/{index-BroMLvyS.js → index-BRzmWdWn.js} +1 -1
  9. package/web/assets/{index-Si6no75_.js → index-BWAAro79.js} +1 -1
  10. package/web/assets/{index-C59b0_Vk.js → index-BaKf7c69.js} +1 -1
  11. package/web/assets/{index-CRsndRFc.js → index-BmRYCxGv.js} +1 -1
  12. package/web/assets/{index-DqnCWDLh.js → index-BnqCpVA0.js} +1 -1
  13. package/web/assets/{index-yXtesgZ4.js → index-Bqjj9q2W.js} +1 -1
  14. package/web/assets/{index-UxKgaDrj.js → index-CIsgyM-R.js} +1 -1
  15. package/web/assets/{index-BrkwvvWk.js → index-CZppS1t2.js} +1 -1
  16. package/web/assets/{index-ChyP1fTD.js → index-Ck0zkLid.js} +1 -1
  17. package/web/assets/{index-YCUybqa_.js → index-CxF-ndAb.js} +1 -1
  18. package/web/assets/{index-DPrfFlZq.js → index-DW1Fnc9x.js} +1 -1
  19. package/web/assets/index-DXMZoXqv.js +136 -0
  20. package/web/assets/index-DafZE925.css +1 -0
  21. package/web/assets/{index-D6DYiY6f.js → index-Di0Nghq9.js} +1 -1
  22. package/web/assets/{index-CWTFMuFd.js → index-DnlEeQVW.js} +1 -1
  23. package/web/assets/{index-wsSNo9PM.js → index-DogVeywh.js} +1 -1
  24. package/web/assets/{index-CZuUjzhP.js → index-U9tL3Foh.js} +1 -1
  25. package/web/assets/{index-DXHZO0ym.js → index-dSYnVYDn.js} +1 -1
  26. package/web/index.html +2 -2
  27. package/web/assets/index-BCLbqlhA.js +0 -134
  28. package/web/assets/index-C6Y6TadO.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 spawn3 } from "node:child_process";
4
+ import { spawn as spawn4 } from "node:child_process";
5
5
  import { readFile as readFile17 } from "node:fs/promises";
6
- import path24 from "node:path";
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 existsSync4, statSync as statSync3 } from "node:fs";
149
- import path23 from "node:path";
150
- import { homedir as homedir6 } from "node:os";
151
- import { randomUUID as randomUUID5 } from "node:crypto";
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 path25 of paths) {
191
- const id = workspaceIdFromPath(path25);
192
- for (const legacy of formerWorkspaceIdsFromPath(path25)) {
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: path25 });
195
+ group.push({ id, path: path26 });
196
196
  byLegacy.set(legacy, group);
197
197
  }
198
198
  }
@@ -4217,18 +4217,865 @@ function describe2(error) {
4217
4217
  return error instanceof Error ? error.message : String(error);
4218
4218
  }
4219
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({
4886
+ kind: "result",
4887
+ subtype,
4888
+ isError: this.#lastTurn === "failed",
4889
+ durationMs: durationMs ?? Math.max(0, Date.now() - this.#turnStartedAt),
4890
+ costUsd: null,
4891
+ usage: this.#turnUsage,
4892
+ contextTokens: this.#context?.tokens ?? null,
4893
+ text: null
4894
+ });
4895
+ this.#busy = false;
4896
+ this.#turnId = null;
4897
+ this.#interruptRequested = false;
4898
+ this.#setState("idle");
4899
+ void this.#drain();
4900
+ }
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");
4923
+ }
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 {
4939
+ }
4940
+ }
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();
5002
+ const entry = this.journal.append(event);
5003
+ this.emit("event", entry);
5004
+ if (["assistant.delta", "thinking.delta", "state"].includes(event.kind))
5005
+ return;
5006
+ this.#unsaved.push({ ts: entry.ts, event });
5007
+ if (!this.#flushTimer) {
5008
+ this.#flushTimer = setTimeout(() => {
5009
+ void this.#flush();
5010
+ }, 500);
5011
+ this.#flushTimer.unref?.();
5012
+ }
5013
+ }
5014
+ async #flush() {
5015
+ if (this.#flushTimer)
5016
+ clearTimeout(this.#flushTimer);
5017
+ this.#flushTimer = null;
5018
+ const batch = this.#unsaved;
5019
+ this.#unsaved = [];
5020
+ await this.#store.appendEvents(this.id, batch);
5021
+ await this.#store.save(this.#record);
5022
+ }
5023
+ #setState(state) {
5024
+ this.#state = state;
5025
+ this.#append({ kind: "state", state });
5026
+ if (["idle", "error", "closed"].includes(state))
5027
+ void this.#flush();
5028
+ this.emit("summary", this.summary());
5029
+ }
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");
5032
+ }
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
+ };
5066
+
4220
5067
  // ../host/dist/antigravity-store.js
4221
5068
  import { appendFile, mkdir as mkdir3, readFile as readFile4, rm as rm2, writeFile as writeFile3 } from "node:fs/promises";
4222
- import path8 from "node:path";
5069
+ import path9 from "node:path";
4223
5070
  var AntigravityStore = class {
4224
5071
  #dir;
4225
5072
  #file;
4226
5073
  #records = /* @__PURE__ */ new Map();
4227
5074
  #loaded = null;
4228
5075
  #writing = Promise.resolve();
4229
- constructor(dataDir) {
4230
- this.#dir = path8.join(dataDir, "antigravity");
4231
- this.#file = path8.join(this.#dir, "sessions.json");
5076
+ constructor(dataDir, provider = "antigravity") {
5077
+ this.#dir = path9.join(dataDir, provider);
5078
+ this.#file = path9.join(this.#dir, "sessions.json");
4232
5079
  }
4233
5080
  #load() {
4234
5081
  this.#loaded ??= (async () => {
@@ -4325,7 +5172,7 @@ var AntigravityStore = class {
4325
5172
  return entries;
4326
5173
  }
4327
5174
  #eventsFile(id) {
4328
- return path8.join(this.#dir, `${id.replace(/[^\w-]/g, "_")}.jsonl`);
5175
+ return path9.join(this.#dir, `${id.replace(/[^\w-]/g, "_")}.jsonl`);
4329
5176
  }
4330
5177
  #persist() {
4331
5178
  const snapshot = JSON.stringify([...this.#records.values()], null, 2);
@@ -4345,10 +5192,10 @@ var AntigravityStore = class {
4345
5192
  };
4346
5193
 
4347
5194
  // ../host/dist/git.js
4348
- import { execFile as execFile4 } from "node:child_process";
5195
+ import { execFile as execFile5 } from "node:child_process";
4349
5196
  import { mkdtemp, readFile as readFile5, rm as rm3, writeFile as writeFile4 } from "node:fs/promises";
4350
5197
  import { tmpdir as tmpdir2 } from "node:os";
4351
- import path9 from "node:path";
5198
+ import path10 from "node:path";
4352
5199
  var TIMEOUT_MS = 2e4;
4353
5200
  var NET_TIMEOUT_MS = 5 * 6e4;
4354
5201
  var MAX_BUFFER = 32 * 1024 * 1024;
@@ -4664,7 +5511,7 @@ var GitRepo = class {
4664
5511
  * важнее всего (см. `checkIgnored`).
4665
5512
  */
4666
5513
  async #withoutRepo(args) {
4667
- const dir = await mkdtemp(path9.join(tmpdir2(), "ccv-git-"));
5514
+ const dir = await mkdtemp(path10.join(tmpdir2(), "ccv-git-"));
4668
5515
  try {
4669
5516
  await this.#run(["init", "--bare", "--quiet", dir]);
4670
5517
  return await this.#soft([`--git-dir=${dir}`, `--work-tree=${this.cwd}`, ...args]);
@@ -4710,7 +5557,7 @@ var GitRepo = class {
4710
5557
  * исключил. Повторы отсеиваются, чтобы файл не рос от каждой публикации.
4711
5558
  */
4712
5559
  async #appendGitignore(paths) {
4713
- const file = path9.join(this.cwd, ".gitignore");
5560
+ const file = path10.join(this.cwd, ".gitignore");
4714
5561
  const existing = await readFile5(file, "utf8").catch(() => "");
4715
5562
  const known = new Set(existing.split(/\r?\n/).map((line) => line.trim()));
4716
5563
  const lines = paths.map(ignorePattern).filter((line) => !known.has(line));
@@ -4735,7 +5582,7 @@ var GitRepo = class {
4735
5582
  #run(args, options2 = {}) {
4736
5583
  const token = options2.token ?? "";
4737
5584
  return new Promise((resolve, reject) => {
4738
- const child = execFile4(
5585
+ const child = execFile5(
4739
5586
  "git",
4740
5587
  // core.quotepath=false — иначе кириллические пути приезжают в виде
4741
5588
  // «\320\277\321\200…» и в панели читаются как мусор.
@@ -4916,9 +5763,9 @@ function parsePatch(raw) {
4916
5763
  body = "";
4917
5764
  }
4918
5765
  total += body.length;
4919
- const path25 = status === "deleted" ? oldPath : newPath || oldPath;
5766
+ const path26 = status === "deleted" ? oldPath : newPath || oldPath;
4920
5767
  files.push({
4921
- path: path25,
5768
+ path: path26,
4922
5769
  oldPath: status === "renamed" ? oldPath : "",
4923
5770
  status,
4924
5771
  added: count(body, "+"),
@@ -4930,10 +5777,10 @@ function parsePatch(raw) {
4930
5777
  return { files, truncated };
4931
5778
  }
4932
5779
  function stripPrefix(value) {
4933
- const path25 = unquote(value.trim());
4934
- if (path25 === "/dev/null")
5780
+ const path26 = unquote(value.trim());
5781
+ if (path26 === "/dev/null")
4935
5782
  return "";
4936
- return path25.replace(/^[ab]\//, "");
5783
+ return path26.replace(/^[ab]\//, "");
4937
5784
  }
4938
5785
  function count(body, mark) {
4939
5786
  let n = 0;
@@ -4945,9 +5792,9 @@ function count(body, mark) {
4945
5792
  }
4946
5793
 
4947
5794
  // ../host/dist/github.js
4948
- import { execFile as execFile5 } from "node:child_process";
5795
+ import { execFile as execFile6 } from "node:child_process";
4949
5796
  import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile5 } from "node:fs/promises";
4950
- import path10 from "node:path";
5797
+ import path11 from "node:path";
4951
5798
  var API = "https://api.github.com";
4952
5799
  var API_VERSION = "2022-11-28";
4953
5800
  var API_TIMEOUT_MS = 2e4;
@@ -4968,7 +5815,7 @@ var GitHubAccount = class {
4968
5815
  */
4969
5816
  #ghInstalled = null;
4970
5817
  constructor(dataDir) {
4971
- this.#storePath = path10.join(dataDir, "github.json");
5818
+ this.#storePath = path11.join(dataDir, "github.json");
4972
5819
  }
4973
5820
  async load() {
4974
5821
  try {
@@ -4982,7 +5829,7 @@ var GitHubAccount = class {
4982
5829
  /** Сохранить токен или стереть его пустой строкой. */
4983
5830
  async setToken(token) {
4984
5831
  this.#token = token.trim();
4985
- await mkdir4(path10.dirname(this.#storePath), { recursive: true });
5832
+ await mkdir4(path11.dirname(this.#storePath), { recursive: true });
4986
5833
  await writeFile5(this.#storePath, JSON.stringify({ token: this.#token }, null, 2), {
4987
5834
  encoding: "utf8",
4988
5835
  mode: 384
@@ -5134,7 +5981,7 @@ var GitHubAccount = class {
5134
5981
  };
5135
5982
  function gh(args) {
5136
5983
  return new Promise((resolve, reject) => {
5137
- execFile5("gh", args, { timeout: GH_TIMEOUT_MS, windowsHide: true, encoding: "utf8" }, (error, stdout, stderr) => {
5984
+ execFile6("gh", args, { timeout: GH_TIMEOUT_MS, windowsHide: true, encoding: "utf8" }, (error, stdout, stderr) => {
5138
5985
  if (error)
5139
5986
  return reject(new Error(stderr.trim() || error.message));
5140
5987
  resolve(stdout);
@@ -5175,10 +6022,10 @@ function describe4(error) {
5175
6022
  }
5176
6023
 
5177
6024
  // ../host/dist/finder.js
5178
- import { existsSync as existsSync2, statSync } from "node:fs";
6025
+ import { existsSync as existsSync3, statSync as statSync2 } from "node:fs";
5179
6026
  import { readdir as readdir2 } from "node:fs/promises";
5180
6027
  import os2 from "node:os";
5181
- import path11 from "node:path";
6028
+ import path12 from "node:path";
5182
6029
  import { listSessions } from "@anthropic-ai/claude-agent-sdk";
5183
6030
  var CACHE_TTL = 6e4;
5184
6031
  var FIND_LIMIT = 50;
@@ -5217,7 +6064,7 @@ var ProjectFinder = class {
5217
6064
  if (needles.length === 0)
5218
6065
  return { candidates: [], truncated: false };
5219
6066
  const open = new Map(this.registry.list().map((workspace) => [workspace.path.toLowerCase(), workspace]));
5220
- const byName = this.#dirs.filter((dir) => matches(path11.basename(dir.path), needles));
6067
+ const byName = this.#dirs.filter((dir) => matches(path12.basename(dir.path), needles));
5221
6068
  const matched = byName.length > 0 ? byName : this.#dirs.filter((dir) => matches(dir.path, needles));
5222
6069
  const sorted = [...matched].sort((a, b) => {
5223
6070
  const aOpen = open.has(a.path.toLowerCase());
@@ -5228,7 +6075,7 @@ var ProjectFinder = class {
5228
6075
  return a.sessions > 0 ? -1 : 1;
5229
6076
  if (a.lastUsedAt !== b.lastUsedAt)
5230
6077
  return b.lastUsedAt - a.lastUsedAt;
5231
- return path11.basename(a.path).localeCompare(path11.basename(b.path), void 0, {
6078
+ return path12.basename(a.path).localeCompare(path12.basename(b.path), void 0, {
5232
6079
  numeric: true,
5233
6080
  sensitivity: "base"
5234
6081
  });
@@ -5236,7 +6083,7 @@ var ProjectFinder = class {
5236
6083
  return {
5237
6084
  candidates: sorted.slice(0, FIND_LIMIT).map((dir) => ({
5238
6085
  path: dir.path,
5239
- name: path11.basename(dir.path) || dir.path,
6086
+ name: path12.basename(dir.path) || dir.path,
5240
6087
  sessions: dir.sessions,
5241
6088
  lastUsedAt: dir.lastUsedAt || null,
5242
6089
  open: open.has(dir.path.toLowerCase()),
@@ -5260,7 +6107,7 @@ var ProjectFinder = class {
5260
6107
  const candidates = [];
5261
6108
  for (const entry of named) {
5262
6109
  const absolute = entry.path.trim();
5263
- if (!absolute || !path11.isAbsolute(absolute))
6110
+ if (!absolute || !path12.isAbsolute(absolute))
5264
6111
  continue;
5265
6112
  const key = absolute.toLowerCase();
5266
6113
  if (seen.has(key))
@@ -5271,7 +6118,7 @@ var ProjectFinder = class {
5271
6118
  const record = known.get(key);
5272
6119
  candidates.push({
5273
6120
  path: record?.path ?? absolute,
5274
- name: path11.basename(absolute) || absolute,
6121
+ name: path12.basename(absolute) || absolute,
5275
6122
  sessions: record?.sessions ?? 0,
5276
6123
  lastUsedAt: record?.lastUsedAt || null,
5277
6124
  open: open.has(key),
@@ -5328,7 +6175,7 @@ var ProjectFinder = class {
5328
6175
  if (!dirent.isDirectory() || SKIP_NAMES.has(dirent.name) || dirent.name.startsWith(".")) {
5329
6176
  continue;
5330
6177
  }
5331
- const full = path11.join(parent, dirent.name);
6178
+ const full = path12.join(parent, dirent.name);
5332
6179
  const key = full.toLowerCase();
5333
6180
  if (dirs.has(key))
5334
6181
  continue;
@@ -5349,7 +6196,7 @@ var ProjectFinder = class {
5349
6196
  #parents(dirs) {
5350
6197
  const counted = /* @__PURE__ */ new Map();
5351
6198
  for (const dir of dirs) {
5352
- const parent = path11.dirname(dir.path);
6199
+ const parent = path12.dirname(dir.path);
5353
6200
  if (parent === dir.path || !usable(parent))
5354
6201
  continue;
5355
6202
  const key = parent.toLowerCase();
@@ -5363,7 +6210,7 @@ var ProjectFinder = class {
5363
6210
  }
5364
6211
  };
5365
6212
  function usable(dir) {
5366
- return !contains(os2.tmpdir(), dir) && existsSync2(dir);
6213
+ return !contains(os2.tmpdir(), dir) && existsSync3(dir);
5367
6214
  }
5368
6215
  function tokens(query2) {
5369
6216
  return query2.toLowerCase().split(/[^a-z0-9а-яё]+/i).filter(Boolean);
@@ -5374,7 +6221,7 @@ function matches(value, needles) {
5374
6221
  }
5375
6222
  function isDirectory(dir) {
5376
6223
  try {
5377
- return statSync(dir).isDirectory();
6224
+ return statSync2(dir).isDirectory();
5378
6225
  } catch {
5379
6226
  return false;
5380
6227
  }
@@ -5460,15 +6307,15 @@ function fromText(text) {
5460
6307
  }
5461
6308
 
5462
6309
  // ../host/dist/workspaces.js
5463
- import { existsSync as existsSync3, statSync as statSync2 } from "node:fs";
6310
+ import { existsSync as existsSync4, statSync as statSync3 } from "node:fs";
5464
6311
  import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile6 } from "node:fs/promises";
5465
- import path12 from "node:path";
6312
+ import path13 from "node:path";
5466
6313
  import { listSessions as listSessions2 } from "@anthropic-ai/claude-agent-sdk";
5467
6314
  var WorkspaceRegistry = class {
5468
6315
  #entries = [];
5469
6316
  #storePath;
5470
6317
  constructor(dataDir) {
5471
- this.#storePath = path12.join(dataDir, "workspaces.json");
6318
+ this.#storePath = path13.join(dataDir, "workspaces.json");
5472
6319
  }
5473
6320
  async load() {
5474
6321
  try {
@@ -5484,13 +6331,13 @@ var WorkspaceRegistry = class {
5484
6331
  if (isEntry(item))
5485
6332
  return item;
5486
6333
  return null;
5487
- }).filter((e) => e !== null && existsSync3(e.path));
6334
+ }).filter((e) => e !== null && existsSync4(e.path));
5488
6335
  } catch {
5489
6336
  this.#entries = [];
5490
6337
  }
5491
6338
  }
5492
6339
  async #persist() {
5493
- await mkdir5(path12.dirname(this.#storePath), { recursive: true });
6340
+ await mkdir5(path13.dirname(this.#storePath), { recursive: true });
5494
6341
  await writeFile6(this.#storePath, JSON.stringify(this.#entries, null, 2), "utf8");
5495
6342
  }
5496
6343
  /** Активные проекты. */
@@ -5510,8 +6357,8 @@ var WorkspaceRegistry = class {
5510
6357
  return entry ? toInfo(entry) : void 0;
5511
6358
  }
5512
6359
  async open(rawPath) {
5513
- const absolute = path12.resolve(rawPath);
5514
- if (!existsSync3(absolute)) {
6360
+ const absolute = path13.resolve(rawPath);
6361
+ if (!existsSync4(absolute)) {
5515
6362
  throw new Error(`\u041A\u0430\u0442\u0430\u043B\u043E\u0433 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D: ${absolute}`);
5516
6363
  }
5517
6364
  const existing = this.#entries.find((e) => e.path === absolute);
@@ -5577,24 +6424,24 @@ var WorkspaceRegistry = class {
5577
6424
  async create(parentRaw, nameRaw, init) {
5578
6425
  const name = nameRaw.trim();
5579
6426
  requireName(name);
5580
- const parent = path12.resolve(parentRaw.trim());
5581
- if (!existsSync3(parent)) {
6427
+ const parent = path13.resolve(parentRaw.trim());
6428
+ if (!existsSync4(parent)) {
5582
6429
  throw new Error(`\u041A\u0430\u0442\u0430\u043B\u043E\u0433 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D: ${parent}`);
5583
6430
  }
5584
- if (!statSync2(parent).isDirectory()) {
6431
+ if (!statSync3(parent).isDirectory()) {
5585
6432
  throw new Error(`\u042D\u0442\u043E \u043D\u0435 \u043A\u0430\u0442\u0430\u043B\u043E\u0433: ${parent}`);
5586
6433
  }
5587
- const target = path12.join(parent, name);
5588
- if (existsSync3(target)) {
6434
+ const target = path13.join(parent, name);
6435
+ if (existsSync4(target)) {
5589
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`);
5590
6437
  }
5591
6438
  await mkdir5(target);
5592
6439
  const warnings = [];
5593
6440
  if (init.claudeMd) {
5594
- await step(warnings, "CLAUDE.md", () => writeFile6(path12.join(target, "CLAUDE.md"), claudeMdStub(name), "utf8"));
6441
+ await step(warnings, "CLAUDE.md", () => writeFile6(path13.join(target, "CLAUDE.md"), claudeMdStub(name), "utf8"));
5595
6442
  }
5596
6443
  if (init.readme) {
5597
- await step(warnings, "README.md", () => writeFile6(path12.join(target, "README.md"), readmeStub(name), "utf8"));
6444
+ await step(warnings, "README.md", () => writeFile6(path13.join(target, "README.md"), readmeStub(name), "utf8"));
5598
6445
  }
5599
6446
  if (init.git) {
5600
6447
  await step(warnings, "git init", () => new GitRepo(target).init());
@@ -5666,8 +6513,8 @@ function toInfo(entry) {
5666
6513
  return {
5667
6514
  id: workspaceIdFromPath(absolute),
5668
6515
  path: absolute,
5669
- name: path12.basename(absolute) || absolute,
5670
- hasClaudeMd: existsSync3(path12.join(absolute, "CLAUDE.md")),
6516
+ name: path13.basename(absolute) || absolute,
6517
+ hasClaudeMd: existsSync4(path13.join(absolute, "CLAUDE.md")),
5671
6518
  ...archivedAt ? { archivedAt } : {}
5672
6519
  };
5673
6520
  }
@@ -5677,13 +6524,13 @@ function isEntry(value) {
5677
6524
 
5678
6525
  // ../host/dist/archive.js
5679
6526
  import { mkdir as mkdir6, readFile as readFile8, writeFile as writeFile7 } from "node:fs/promises";
5680
- import path13 from "node:path";
6527
+ import path14 from "node:path";
5681
6528
  var SessionArchive = class {
5682
6529
  #archived = /* @__PURE__ */ new Map();
5683
6530
  #restored = /* @__PURE__ */ new Map();
5684
6531
  #storePath;
5685
6532
  constructor(dataDir) {
5686
- this.#storePath = path13.join(dataDir, "session-archive.json");
6533
+ this.#storePath = path14.join(dataDir, "session-archive.json");
5687
6534
  }
5688
6535
  async load() {
5689
6536
  try {
@@ -5795,7 +6642,7 @@ var SessionArchive = class {
5795
6642
  archived: Object.fromEntries(this.#archived),
5796
6643
  restored: Object.fromEntries(this.#restored)
5797
6644
  };
5798
- await mkdir6(path13.dirname(this.#storePath), { recursive: true });
6645
+ await mkdir6(path14.dirname(this.#storePath), { recursive: true });
5799
6646
  await writeFile7(this.#storePath, JSON.stringify(file, null, 2), "utf8");
5800
6647
  }
5801
6648
  };
@@ -5805,7 +6652,7 @@ function isRecord(value) {
5805
6652
 
5806
6653
  // ../host/dist/pins.js
5807
6654
  import { mkdir as mkdir7, readFile as readFile9, writeFile as writeFile8 } from "node:fs/promises";
5808
- import path14 from "node:path";
6655
+ import path15 from "node:path";
5809
6656
  var SessionPins = class {
5810
6657
  #pinned = /* @__PURE__ */ new Map();
5811
6658
  /**
@@ -5820,7 +6667,7 @@ var SessionPins = class {
5820
6667
  #moved = /* @__PURE__ */ new Map();
5821
6668
  #storePath;
5822
6669
  constructor(dataDir) {
5823
- this.#storePath = path14.join(dataDir, "session-pins.json");
6670
+ this.#storePath = path15.join(dataDir, "session-pins.json");
5824
6671
  }
5825
6672
  async load() {
5826
6673
  try {
@@ -5977,7 +6824,7 @@ var SessionPins = class {
5977
6824
  pinned: Object.fromEntries(this.#pinned),
5978
6825
  ...this.#moved.size > 0 ? { moved: Object.fromEntries(this.#moved) } : {}
5979
6826
  };
5980
- await mkdir7(path14.dirname(this.#storePath), { recursive: true });
6827
+ await mkdir7(path15.dirname(this.#storePath), { recursive: true });
5981
6828
  await writeFile8(this.#storePath, JSON.stringify(file, null, 2), "utf8");
5982
6829
  }
5983
6830
  };
@@ -5987,12 +6834,12 @@ function isRecord2(value) {
5987
6834
 
5988
6835
  // ../host/dist/order.js
5989
6836
  import { mkdir as mkdir8, readFile as readFile10, writeFile as writeFile9 } from "node:fs/promises";
5990
- import path15 from "node:path";
6837
+ import path16 from "node:path";
5991
6838
  var ListOrderStore = class {
5992
6839
  #order = emptyOrder();
5993
6840
  #storePath;
5994
6841
  constructor(dataDir) {
5995
- this.#storePath = path15.join(dataDir, "list-order.json");
6842
+ this.#storePath = path16.join(dataDir, "list-order.json");
5996
6843
  }
5997
6844
  async load() {
5998
6845
  try {
@@ -6068,7 +6915,7 @@ var ListOrderStore = class {
6068
6915
  return changed;
6069
6916
  }
6070
6917
  async #persist() {
6071
- await mkdir8(path15.dirname(this.#storePath), { recursive: true });
6918
+ await mkdir8(path16.dirname(this.#storePath), { recursive: true });
6072
6919
  await writeFile9(this.#storePath, JSON.stringify(this.#order, null, 2), "utf8");
6073
6920
  }
6074
6921
  };
@@ -6099,8 +6946,8 @@ function sanitize(value) {
6099
6946
 
6100
6947
  // ../host/dist/folders.js
6101
6948
  import { mkdir as mkdir9, readFile as readFile11, writeFile as writeFile10 } from "node:fs/promises";
6102
- import path16 from "node:path";
6103
- import { randomUUID as randomUUID3 } from "node:crypto";
6949
+ import path17 from "node:path";
6950
+ import { randomUUID as randomUUID4 } from "node:crypto";
6104
6951
  var COLOR_PATTERN = /^#[0-9a-f]{6}$/i;
6105
6952
  var Folders = class {
6106
6953
  #workspaceFolders = /* @__PURE__ */ new Map();
@@ -6111,7 +6958,7 @@ var Folders = class {
6111
6958
  #pinnedAssignments = /* @__PURE__ */ new Map();
6112
6959
  #storePath;
6113
6960
  constructor(dataDir) {
6114
- this.#storePath = path16.join(dataDir, "folders.json");
6961
+ this.#storePath = path17.join(dataDir, "folders.json");
6115
6962
  }
6116
6963
  async load() {
6117
6964
  try {
@@ -6200,19 +7047,19 @@ var Folders = class {
6200
7047
  };
6201
7048
  }
6202
7049
  async createWorkspaceFolder(name) {
6203
- const id = randomUUID3();
7050
+ const id = randomUUID4();
6204
7051
  this.#workspaceFolders.set(id, { name });
6205
7052
  await this.#persist();
6206
7053
  return id;
6207
7054
  }
6208
7055
  async createSessionFolder(workspaceId, name) {
6209
- const id = randomUUID3();
7056
+ const id = randomUUID4();
6210
7057
  this.#sessionFolders.set(id, { name, workspaceId });
6211
7058
  await this.#persist();
6212
7059
  return id;
6213
7060
  }
6214
7061
  async createPinnedFolder(name) {
6215
- const id = randomUUID3();
7062
+ const id = randomUUID4();
6216
7063
  this.#pinnedFolders.set(id, { name });
6217
7064
  await this.#persist();
6218
7065
  return id;
@@ -6392,7 +7239,7 @@ var Folders = class {
6392
7239
  sessionAssignments: Object.fromEntries(this.#sessionAssignments),
6393
7240
  pinnedAssignments: Object.fromEntries(this.#pinnedAssignments)
6394
7241
  };
6395
- await mkdir9(path16.dirname(this.#storePath), { recursive: true });
7242
+ await mkdir9(path17.dirname(this.#storePath), { recursive: true });
6396
7243
  await writeFile10(this.#storePath, JSON.stringify(file, null, 2), "utf8");
6397
7244
  }
6398
7245
  };
@@ -6415,8 +7262,8 @@ function isPinnedFolder(value) {
6415
7262
 
6416
7263
  // ../host/dist/app-settings.js
6417
7264
  import { mkdir as mkdir10, readFile as readFile12, writeFile as writeFile11 } from "node:fs/promises";
6418
- import { homedir as homedir4 } from "node:os";
6419
- import path17 from "node:path";
7265
+ import { homedir as homedir5 } from "node:os";
7266
+ import path18 from "node:path";
6420
7267
  var DEFAULT_AUTO_ARCHIVE_AFTER_MS = 2 * 24 * 60 * 60 * 1e3;
6421
7268
  var MIN_AUTO_ARCHIVE_AFTER_MS = 60 * 60 * 1e3;
6422
7269
  var DEFAULT_AUTO_STOP_AFTER_MS = 10 * 60 * 1e3;
@@ -6424,7 +7271,7 @@ var MIN_AUTO_STOP_AFTER_MS = 60 * 1e3;
6424
7271
  var MIN_AUTO_SAVE_AFTER_PAUSE_MS = 500;
6425
7272
  var DEFAULT_BLOCKED_COUNTRIES = ["RU", "BY", "CN", "IR", "KP", "SY", "CU"];
6426
7273
  var FOLDER_ACCESS = ["all", "projects", "project"];
6427
- var PROVIDERS = ["claude", "antigravity"];
7274
+ var PROVIDERS = ["claude", "antigravity", "codex"];
6428
7275
  var PERMISSION_MODES = [
6429
7276
  "default",
6430
7277
  "acceptEdits",
@@ -6459,7 +7306,7 @@ var DEFAULT_APP_SETTINGS = {
6459
7306
  limitStopFiveHourPct: 95,
6460
7307
  limitStopSevenDayPct: 99,
6461
7308
  // Оба: до появления настройки приложение предлагало обоих агентов.
6462
- enabledProviders: ["claude", "antigravity"],
7309
+ enabledProviders: ["claude", "antigravity", "codex"],
6463
7310
  // Включено: запрос без VPN может стоить аккаунта, а тому, у кого маршрут и
6464
7311
  // так из разрешённой страны, защита ничем не мешает.
6465
7312
  networkGuardEnabled: true,
@@ -6469,7 +7316,7 @@ var AppSettingsStore = class {
6469
7316
  #settings = { ...DEFAULT_APP_SETTINGS };
6470
7317
  #storePath;
6471
7318
  constructor(dataDir) {
6472
- this.#storePath = path17.join(dataDir, "app-settings.json");
7319
+ this.#storePath = path18.join(dataDir, "app-settings.json");
6473
7320
  }
6474
7321
  async load() {
6475
7322
  try {
@@ -6493,7 +7340,7 @@ var AppSettingsStore = class {
6493
7340
  async update(patch) {
6494
7341
  const next = sanitize2(patch, this.#settings);
6495
7342
  this.#settings = next;
6496
- await mkdir10(path17.dirname(this.#storePath), { recursive: true });
7343
+ await mkdir10(path18.dirname(this.#storePath), { recursive: true });
6497
7344
  await writeFile11(this.#storePath, JSON.stringify(next, null, 2), "utf8");
6498
7345
  return this.current;
6499
7346
  }
@@ -6560,7 +7407,7 @@ function sanitize2(value, fallback) {
6560
7407
  return result;
6561
7408
  }
6562
7409
  function claudeHome() {
6563
- return path17.join(homedir4(), ".claude");
7410
+ return path18.join(homedir5(), ".claude");
6564
7411
  }
6565
7412
  function writableRoots(mode, cwd, workspacePaths) {
6566
7413
  if (mode === "all")
@@ -6570,7 +7417,7 @@ function writableRoots(mode, cwd, workspacePaths) {
6570
7417
  return [...roots];
6571
7418
  for (const workspace of [cwd, ...workspacePaths]) {
6572
7419
  roots.add(workspace);
6573
- const parent = path17.dirname(workspace);
7420
+ const parent = path18.dirname(workspace);
6574
7421
  if (parent !== workspace)
6575
7422
  roots.add(parent);
6576
7423
  }
@@ -6579,11 +7426,11 @@ function writableRoots(mode, cwd, workspacePaths) {
6579
7426
 
6580
7427
  // ../host/dist/handoff-store.js
6581
7428
  import { mkdir as mkdir11, readFile as readFile13, readdir as readdir3, rm as rm4, writeFile as writeFile12 } from "node:fs/promises";
6582
- import path18 from "node:path";
7429
+ import path19 from "node:path";
6583
7430
  var HandoffStore = class {
6584
7431
  #dir;
6585
7432
  constructor(dataDir) {
6586
- this.#dir = path18.join(dataDir, "handoffs");
7433
+ this.#dir = path19.join(dataDir, "handoffs");
6587
7434
  }
6588
7435
  async get(sessionId) {
6589
7436
  const file = this.#file(sessionId);
@@ -6620,7 +7467,7 @@ var HandoffStore = class {
6620
7467
  #file(sessionId) {
6621
7468
  if (!/^[\w-]{1,128}$/.test(sessionId))
6622
7469
  return null;
6623
- return path18.join(this.#dir, `${sessionId}.json`);
7470
+ return path19.join(this.#dir, `${sessionId}.json`);
6624
7471
  }
6625
7472
  };
6626
7473
  var SAME_WINDOW_MS = 5 * 6e4;
@@ -6628,7 +7475,7 @@ var LimitStopState = class {
6628
7475
  #fired = {};
6629
7476
  #storePath;
6630
7477
  constructor(dataDir) {
6631
- this.#storePath = path18.join(dataDir, "limit-stop.json");
7478
+ this.#storePath = path19.join(dataDir, "limit-stop.json");
6632
7479
  }
6633
7480
  async load() {
6634
7481
  try {
@@ -6664,7 +7511,7 @@ var LimitStopState = class {
6664
7511
  /** Запомнить срабатывание до `until` (момент сброса окна). */
6665
7512
  async mark(limit, until) {
6666
7513
  this.#fired[limit] = until;
6667
- await mkdir11(path18.dirname(this.#storePath), { recursive: true });
7514
+ await mkdir11(path19.dirname(this.#storePath), { recursive: true });
6668
7515
  await writeFile12(this.#storePath, JSON.stringify(this.#fired, null, 2), "utf8");
6669
7516
  }
6670
7517
  };
@@ -6770,8 +7617,8 @@ function capitalize(text) {
6770
7617
 
6771
7618
  // ../host/dist/plugins.js
6772
7619
  import { readFile as readFile14 } from "node:fs/promises";
6773
- import { homedir as homedir5 } from "node:os";
6774
- import path19 from "node:path";
7620
+ import { homedir as homedir6 } from "node:os";
7621
+ import path20 from "node:path";
6775
7622
  var TIMEOUT_MS2 = 12e4;
6776
7623
  var SAFE_ARG = /^[A-Za-z0-9][A-Za-z0-9._@/-]*$/;
6777
7624
  var PluginCli = class {
@@ -6870,10 +7717,10 @@ async function readCatalog(markets, installed) {
6870
7717
  return lists.flat().sort((a, b) => a.name.localeCompare(b.name));
6871
7718
  }
6872
7719
  async function readMarketplace(market, installed) {
6873
- const root = market.installLocation || path19.join(homedir5(), ".claude", "plugins", "marketplaces", market.name);
7720
+ const root = market.installLocation || path20.join(homedir6(), ".claude", "plugins", "marketplaces", market.name);
6874
7721
  let manifest;
6875
7722
  try {
6876
- const text = await readFile14(path19.join(root, ".claude-plugin", "marketplace.json"), "utf8");
7723
+ const text = await readFile14(path20.join(root, ".claude-plugin", "marketplace.json"), "utf8");
6877
7724
  manifest = JSON.parse(text);
6878
7725
  } catch {
6879
7726
  return [];
@@ -6916,7 +7763,7 @@ function authorName(raw) {
6916
7763
  }
6917
7764
 
6918
7765
  // ../host/dist/auth.js
6919
- import { EventEmitter as EventEmitter4 } from "node:events";
7766
+ import { EventEmitter as EventEmitter6 } from "node:events";
6920
7767
  var QUICK_TIMEOUT_MS = 3e4;
6921
7768
  var LOGIN_TIMEOUT_MS = 10 * 6e4;
6922
7769
  var URL_RE = /https:\/\/\S*\/oauth\/authorize\S*/;
@@ -6928,7 +7775,7 @@ var IDLE = {
6928
7775
  output: "",
6929
7776
  error: null
6930
7777
  };
6931
- var AuthCli = class extends EventEmitter4 {
7778
+ var AuthCli = class extends EventEmitter6 {
6932
7779
  #child = null;
6933
7780
  #state = IDLE;
6934
7781
  #timer = null;
@@ -7113,9 +7960,9 @@ function describe5(error) {
7113
7960
  }
7114
7961
 
7115
7962
  // ../host/dist/relay-link.js
7116
- import { EventEmitter as EventEmitter5 } from "node:events";
7963
+ import { EventEmitter as EventEmitter7 } from "node:events";
7117
7964
  import { readFile as readFile15, writeFile as writeFile13, rm as rm5, mkdir as mkdir12 } from "node:fs/promises";
7118
- import path20 from "node:path";
7965
+ import path21 from "node:path";
7119
7966
  import { hostname } from "node:os";
7120
7967
  import WebSocket from "ws";
7121
7968
 
@@ -7132,7 +7979,7 @@ var SILENCE_TIMEOUT_MS = 95e3;
7132
7979
  function jitter(delay2) {
7133
7980
  return Math.round(delay2 * (0.8 + Math.random() * 0.4));
7134
7981
  }
7135
- var RelayLink = class extends EventEmitter5 {
7982
+ var RelayLink = class extends EventEmitter7 {
7136
7983
  #credentials = null;
7137
7984
  #socket = null;
7138
7985
  #retry = RETRY_MIN_MS;
@@ -7170,7 +8017,7 @@ var RelayLink = class extends EventEmitter5 {
7170
8017
  #log;
7171
8018
  constructor(options2) {
7172
8019
  super();
7173
- this.#file = path20.join(options2.dataDir, "relay.json");
8020
+ this.#file = path21.join(options2.dataDir, "relay.json");
7174
8021
  this.#defaultUrl = options2.defaultUrl.replace(/\/+$/, "");
7175
8022
  this.#log = options2.log ?? ((msg) => console.log(`[relay] ${msg}`));
7176
8023
  }
@@ -7264,7 +8111,7 @@ var RelayLink = class extends EventEmitter5 {
7264
8111
  }
7265
8112
  }
7266
8113
  async #write(credentials) {
7267
- await mkdir12(path20.dirname(this.#file), { recursive: true });
8114
+ await mkdir12(path21.dirname(this.#file), { recursive: true });
7268
8115
  await writeFile13(this.#file, `${JSON.stringify(credentials, null, 2)}
7269
8116
  `, {
7270
8117
  encoding: "utf8",
@@ -7520,14 +8367,14 @@ function humanize(error) {
7520
8367
  }
7521
8368
 
7522
8369
  // ../host/dist/terminal.js
7523
- import { EventEmitter as EventEmitter6 } from "node:events";
7524
- import { access as access2, constants as constants2 } from "node:fs/promises";
7525
- import path21 from "node:path";
7526
- import { randomUUID as randomUUID4 } from "node:crypto";
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";
7527
8374
  var SCROLLBACK_BYTES = 256 * 1024;
7528
8375
  var FLUSH_MS = 12;
7529
8376
  var MAX_TERMINALS = 24;
7530
- var TerminalHub = class extends EventEmitter6 {
8377
+ var TerminalHub = class extends EventEmitter8 {
7531
8378
  #terminals = /* @__PURE__ */ new Map();
7532
8379
  #module = null;
7533
8380
  list(workspaceId) {
@@ -7560,7 +8407,7 @@ var TerminalHub = class extends EventEmitter6 {
7560
8407
  env: environment()
7561
8408
  });
7562
8409
  const info = {
7563
- id: randomUUID4(),
8410
+ id: randomUUID5(),
7564
8411
  workspaceId: options2.workspaceId,
7565
8412
  title: shell.title,
7566
8413
  shell: shell.file,
@@ -7692,36 +8539,36 @@ async function defaultShell() {
7692
8539
  if (pwsh)
7693
8540
  return { file: pwsh, args: [], title: "pwsh" };
7694
8541
  const system = process.env.SystemRoot ?? "C:\\Windows";
7695
- const powershell = path21.join(system, "System32/WindowsPowerShell/v1.0/powershell.exe");
8542
+ const powershell = path22.join(system, "System32/WindowsPowerShell/v1.0/powershell.exe");
7696
8543
  if (await exists2(powershell))
7697
8544
  return { file: powershell, args: [], title: "powershell" };
7698
8545
  return { file: process.env.COMSPEC ?? "cmd.exe", args: [], title: "cmd" };
7699
8546
  }
7700
8547
  const shell = process.env.SHELL;
7701
8548
  if (shell && await exists2(shell))
7702
- return { file: shell, args: [], title: path21.basename(shell) };
8549
+ return { file: shell, args: [], title: path22.basename(shell) };
7703
8550
  if (await exists2("/bin/bash"))
7704
8551
  return { file: "/bin/bash", args: [], title: "bash" };
7705
8552
  return { file: "/bin/sh", args: [], title: "sh" };
7706
8553
  }
7707
8554
  async function findOnPath(name) {
7708
- const dirs = (process.env.PATH ?? "").split(path21.delimiter).filter(Boolean);
8555
+ const dirs = (process.env.PATH ?? "").split(path22.delimiter).filter(Boolean);
7709
8556
  for (const dir of dirs) {
7710
- const candidate = path21.join(dir, name);
8557
+ const candidate = path22.join(dir, name);
7711
8558
  if (await exists2(candidate))
7712
8559
  return candidate;
7713
8560
  }
7714
8561
  return null;
7715
8562
  }
7716
8563
  function exists2(file) {
7717
- return access2(file, constants2.X_OK).then(() => true, () => false);
8564
+ return access2(file, constants3.X_OK).then(() => true, () => false);
7718
8565
  }
7719
8566
 
7720
8567
  // ../host/dist/mcp-config.js
7721
8568
  import { readFile as readFile16, writeFile as writeFile14, mkdir as mkdir13 } from "node:fs/promises";
7722
- import path22 from "node:path";
8569
+ import path23 from "node:path";
7723
8570
  var MCP_FILE = ".mcp.json";
7724
- var LOCAL_SETTINGS2 = path22.join(".claude", "settings.local.json");
8571
+ var LOCAL_SETTINGS2 = path23.join(".claude", "settings.local.json");
7725
8572
  var queues = /* @__PURE__ */ new Map();
7726
8573
  function serialize(cwd, work) {
7727
8574
  const previous = queues.get(cwd) ?? Promise.resolve();
@@ -7730,7 +8577,7 @@ function serialize(cwd, work) {
7730
8577
  return next;
7731
8578
  }
7732
8579
  async function readProjectServers(cwd) {
7733
- const file = await readJson(path22.join(cwd, MCP_FILE));
8580
+ const file = await readJson(path23.join(cwd, MCP_FILE));
7734
8581
  const servers = file?.mcpServers;
7735
8582
  if (!servers || typeof servers !== "object")
7736
8583
  return {};
@@ -7738,23 +8585,23 @@ async function readProjectServers(cwd) {
7738
8585
  }
7739
8586
  function addProjectServer(cwd, name, config) {
7740
8587
  return serialize(cwd, async () => {
7741
- const file = await readJson(path22.join(cwd, MCP_FILE)) ?? {};
8588
+ const file = await readJson(path23.join(cwd, MCP_FILE)) ?? {};
7742
8589
  const servers = file.mcpServers ?? {};
7743
8590
  servers[name] = config;
7744
8591
  file.mcpServers = servers;
7745
- await writeJson(path22.join(cwd, MCP_FILE), file);
8592
+ await writeJson(path23.join(cwd, MCP_FILE), file);
7746
8593
  await approve(cwd, name);
7747
8594
  });
7748
8595
  }
7749
8596
  function removeProjectServer(cwd, name) {
7750
8597
  return serialize(cwd, async () => {
7751
- const file = await readJson(path22.join(cwd, MCP_FILE));
8598
+ const file = await readJson(path23.join(cwd, MCP_FILE));
7752
8599
  const servers = file?.mcpServers;
7753
8600
  if (!servers || !(name in servers))
7754
8601
  return false;
7755
8602
  delete servers[name];
7756
- await writeJson(path22.join(cwd, MCP_FILE), file);
7757
- const settingsPath = path22.join(cwd, LOCAL_SETTINGS2);
8603
+ await writeJson(path23.join(cwd, MCP_FILE), file);
8604
+ const settingsPath = path23.join(cwd, LOCAL_SETTINGS2);
7758
8605
  const settings = await readJson(settingsPath);
7759
8606
  const enabled = settings?.enabledMcpjsonServers;
7760
8607
  if (settings && Array.isArray(enabled) && enabled.includes(name)) {
@@ -7765,7 +8612,7 @@ function removeProjectServer(cwd, name) {
7765
8612
  });
7766
8613
  }
7767
8614
  async function approve(cwd, name) {
7768
- const file = path22.join(cwd, LOCAL_SETTINGS2);
8615
+ const file = path23.join(cwd, LOCAL_SETTINGS2);
7769
8616
  const settings = await readJson(file) ?? {};
7770
8617
  const enabled = Array.isArray(settings.enabledMcpjsonServers) ? settings.enabledMcpjsonServers : [];
7771
8618
  if (!enabled.includes(name))
@@ -7811,13 +8658,13 @@ async function readJson(file) {
7811
8658
  if (error.code === "ENOENT")
7812
8659
  return null;
7813
8660
  if (error instanceof SyntaxError) {
7814
- throw new Error(`\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0440\u0430\u0437\u043E\u0431\u0440\u0430\u0442\u044C ${path22.basename(file)}: ${error.message}`);
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}`);
7815
8662
  }
7816
8663
  throw error;
7817
8664
  }
7818
8665
  }
7819
8666
  async function writeJson(file, data) {
7820
- await mkdir13(path22.dirname(file), { recursive: true });
8667
+ await mkdir13(path23.dirname(file), { recursive: true });
7821
8668
  await writeFile14(file, `${JSON.stringify(data, null, 2)}
7822
8669
  `, "utf8");
7823
8670
  }
@@ -7836,6 +8683,8 @@ var BUSY_STATES2 = /* @__PURE__ */ new Set([
7836
8683
  "reconnecting"
7837
8684
  ]);
7838
8685
  var HostServer = class {
8686
+ #httpServer = null;
8687
+ #webSockets = null;
7839
8688
  #options;
7840
8689
  #workspaces;
7841
8690
  #archive;
@@ -7848,6 +8697,7 @@ var HostServer = class {
7848
8697
  /** Опросы статуса для идущих OAuth-входов MCP, ключ — сессия и сервер. */
7849
8698
  #mcpAuthWatch = /* @__PURE__ */ new Map();
7850
8699
  #antigravity;
8700
+ #codex;
7851
8701
  /** Боковые сессии («вопрос на полях») по id родителя — не больше одной на разговор. */
7852
8702
  #sides = /* @__PURE__ */ new Map();
7853
8703
  /**
@@ -7895,7 +8745,7 @@ var HostServer = class {
7895
8745
  #limitHandoffBusy = /* @__PURE__ */ new Set();
7896
8746
  /** Живые id сессий, у которых сейчас пишется ручной хэндофф. */
7897
8747
  #handoffBusy = /* @__PURE__ */ new Set();
7898
- #hostId = randomUUID5();
8748
+ #hostId = randomUUID6();
7899
8749
  #plugins = new PluginCli();
7900
8750
  #auth = new AuthCli();
7901
8751
  #relay;
@@ -7919,6 +8769,7 @@ var HostServer = class {
7919
8769
  this.#folders = new Folders(options2.dataDir);
7920
8770
  this.#settings = new AppSettingsStore(options2.dataDir);
7921
8771
  this.#antigravity = new AntigravityStore(options2.dataDir);
8772
+ this.#codex = new AntigravityStore(options2.dataDir, "codex");
7922
8773
  this.#handoffs = new HandoffStore(options2.dataDir);
7923
8774
  this.#limitStop = new LimitStopState(options2.dataDir);
7924
8775
  this.#finder = new ProjectFinder(this.#workspaces);
@@ -7945,9 +8796,11 @@ var HostServer = class {
7945
8796
  const id = workspaceIdFromPath(dir);
7946
8797
  const onDisk = await this.#workspaces.sessionsOnDisk(id);
7947
8798
  const antigravity = await this.#antigravity.list(id);
7948
- return [...onDisk.map((s) => s.sessionId), ...antigravity.map((r) => r.id)];
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)];
7949
8801
  });
7950
8802
  await this.#antigravity.remapWorkspaceIds(remap);
8803
+ await this.#codex.remapWorkspaceIds(remap);
7951
8804
  await this.#archive.remapWorkspaceIds(remap);
7952
8805
  await this.#pins.remapWorkspaceIds(remap);
7953
8806
  await this.#order.remapWorkspaceIds(remap);
@@ -7982,6 +8835,8 @@ var HostServer = class {
7982
8835
  resolve();
7983
8836
  });
7984
8837
  });
8838
+ this.#httpServer = httpServer;
8839
+ this.#webSockets = wss;
7985
8840
  await this.#relay.start().catch((error) => {
7986
8841
  console.error("[relay]", error instanceof Error ? error.message : String(error));
7987
8842
  });
@@ -8020,6 +8875,16 @@ var HostServer = class {
8020
8875
  await this.#relay.close();
8021
8876
  await Promise.all([...this.#sessions.values()].map((s) => s.close("host shutdown")));
8022
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
+ ]);
8023
8888
  }
8024
8889
  // -------------------------------------------------------------------------
8025
8890
  // WebSocket
@@ -9419,7 +10284,31 @@ var HostServer = class {
9419
10284
  throw new Error(`\u0412\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D: ${frame.workspaceId}`);
9420
10285
  const provider = frame.provider ?? "claude";
9421
10286
  if (!frame.resume && !this.#settings.current.enabledProviders.includes(provider)) {
9422
- 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;
9423
10312
  }
9424
10313
  const agyRecord = frame.resume ? await this.#antigravity.find(frame.resume) : null;
9425
10314
  if (agyRecord || !frame.resume && frame.provider === "antigravity") {
@@ -9583,7 +10472,7 @@ var HostServer = class {
9583
10472
  this.#broadcast({ type: "context.state", sessionId: session.id, context });
9584
10473
  });
9585
10474
  session.on("stderr", (data) => {
9586
- const tag = session instanceof AntigravitySession ? "agy" : "claude";
10475
+ const tag = session instanceof AntigravitySession ? "agy" : session instanceof CodexSession ? "codex" : "claude";
9587
10476
  process.stderr.write(`[${tag} ${session.id.slice(0, 8)}] ${data}`);
9588
10477
  });
9589
10478
  }
@@ -9603,7 +10492,7 @@ var HostServer = class {
9603
10492
  * разговора пока и не было.
9604
10493
  */
9605
10494
  #ensureSide(parent) {
9606
- if (parent instanceof AntigravitySession) {
10495
+ if (!(parent instanceof ClaudeSession)) {
9607
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");
9608
10497
  }
9609
10498
  const existing = this.#sides.get(parent.id);
@@ -9661,7 +10550,7 @@ var HostServer = class {
9661
10550
  */
9662
10551
  #roots() {
9663
10552
  const roots = this.#finder.roots();
9664
- return roots.length > 0 ? roots : [homedir6()];
10553
+ return roots.length > 0 ? roots : [homedir7()];
9665
10554
  }
9666
10555
  /**
9667
10556
  * Где сессии этого каталога разрешено писать без вопроса.
@@ -9903,7 +10792,7 @@ var HostServer = class {
9903
10792
  live.setTitle(title);
9904
10793
  return;
9905
10794
  }
9906
- if (await this.#antigravity.rename(sessionId, title)) {
10795
+ if (await this.#antigravity.rename(sessionId, title) || await this.#codex.rename(sessionId, title)) {
9907
10796
  if (workspaceId)
9908
10797
  await this.#sendSessionList(workspaceId);
9909
10798
  return;
@@ -9960,6 +10849,11 @@ var HostServer = class {
9960
10849
  continue;
9961
10850
  historical.push(antigravitySummary(record));
9962
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
+ }
9963
10857
  const known = /* @__PURE__ */ new Set([...liveIds, ...historical.map((s) => s.id)]);
9964
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);
9965
10859
  await this.#pins.sweep(workspaceId, known);
@@ -10006,15 +10900,15 @@ var HostServer = class {
10006
10900
  }
10007
10901
  #serveStatic(req, res) {
10008
10902
  const webRoot = this.#options.webRoot;
10009
- if (!webRoot || !existsSync4(webRoot)) {
10903
+ if (!webRoot || !existsSync5(webRoot)) {
10010
10904
  res.writeHead(200, { "content-type": "text/plain; charset=utf-8" });
10011
10905
  res.end("CCV host is running. UI is served by the Vite dev server.\n");
10012
10906
  return;
10013
10907
  }
10014
10908
  const url2 = new URL(req.url ?? "/", "http://localhost");
10015
- const requested = path23.join(webRoot, path23.normalize(url2.pathname));
10016
- const target = requested.startsWith(webRoot) && existsSync4(requested) && statSync3(requested).isFile() ? requested : path23.join(webRoot, "index.html");
10017
- if (!existsSync4(target)) {
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)) {
10018
10912
  res.writeHead(404).end("not found");
10019
10913
  return;
10020
10914
  }
@@ -10039,7 +10933,7 @@ function contentType(file) {
10039
10933
  ".svg": "image/svg+xml",
10040
10934
  ".woff2": "font/woff2"
10041
10935
  };
10042
- return types[path23.extname(file)] ?? "application/octet-stream";
10936
+ return types[path24.extname(file)] ?? "application/octet-stream";
10043
10937
  }
10044
10938
  function antigravitySummary(record) {
10045
10939
  return {
@@ -10068,9 +10962,9 @@ function antigravitySummary(record) {
10068
10962
  }
10069
10963
 
10070
10964
  // dist/ccv.js
10071
- var HERE = path24.dirname(fileURLToPath(import.meta.url));
10072
- var PACKAGE_ROOT = path24.resolve(HERE, "..");
10073
- var USAGE = `ccv \u2014 Claude Code Visual: \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0439 \u0445\u043E\u0441\u0442 Claude Code \u0441 \u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043E\u043C \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435.
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.
10074
10968
 
10075
10969
  \u0418\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u0435:
10076
10970
  ccv [\u043E\u043F\u0446\u0438\u0438]
@@ -10079,12 +10973,12 @@ var USAGE = `ccv \u2014 Claude Code Visual: \u043B\u043E\u043A\u0430\u043B\u044C
10079
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)
10080
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)
10081
10975
  --no-open \u043D\u0435 \u043E\u0442\u043A\u0440\u044B\u0432\u0430\u0442\u044C \u0431\u0440\u0430\u0443\u0437\u0435\u0440
10082
- --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
10083
10977
  -v, --version \u0432\u0435\u0440\u0441\u0438\u044F
10084
10978
  -h, --help \u044D\u0442\u0430 \u0441\u043F\u0440\u0430\u0432\u043A\u0430
10085
10979
 
10086
10980
  \u041E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435: CCV_PORT, CCV_HOST, CCV_DATA_DIR, CCV_WEB_ROOT, CCV_RELAY_URL,
10087
- CCV_CLAUDE_PATH, CCV_BUNDLED_CLAUDE.
10981
+ CCV_CLAUDE_PATH, CCV_BUNDLED_CLAUDE, CCV_CODEX_PATH, CCV_AGY_PATH.
10088
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.`;
10089
10983
  var options = {
10090
10984
  port: { type: "string", short: "p" },
@@ -10136,7 +11030,7 @@ var server = new HostServer({
10136
11030
  dataDir: defaults.dataDir,
10137
11031
  // UI лежит в пакете рядом с бандлом — это и есть разница между установленной
10138
11032
  // утилитой и запуском из репозитория, где сборка живёт в `apps/web/dist`.
10139
- webRoot: process.env.CCV_WEB_ROOT ?? path24.join(PACKAGE_ROOT, "web"),
11033
+ webRoot: process.env.CCV_WEB_ROOT ?? path25.join(PACKAGE_ROOT, "web"),
10140
11034
  version,
10141
11035
  relayUrl: defaults.relayUrl
10142
11036
  });
@@ -10165,7 +11059,7 @@ for (const signal of ["SIGINT", "SIGTERM"]) {
10165
11059
  }
10166
11060
  async function packageVersion() {
10167
11061
  try {
10168
- const text = await readFile17(path24.join(PACKAGE_ROOT, "package.json"), "utf8");
11062
+ const text = await readFile17(path25.join(PACKAGE_ROOT, "package.json"), "utf8");
10169
11063
  return JSON.parse(text).version ?? "0.0.0";
10170
11064
  } catch {
10171
11065
  return "0.0.0";
@@ -10209,7 +11103,7 @@ function openBrowser(target) {
10209
11103
  return;
10210
11104
  }
10211
11105
  const command = process.platform === "win32" ? { file: "cmd", args: ["/c", "start", "", target] } : process.platform === "darwin" ? { file: "open", args: [target] } : { file: "xdg-open", args: [target] };
10212
- const child = spawn3(command.file, command.args, {
11106
+ const child = spawn4(command.file, command.args, {
10213
11107
  stdio: "ignore",
10214
11108
  detached: true,
10215
11109
  windowsHide: true