@i14ks/ccv 0.12.3 → 0.14.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 (27) hide show
  1. package/bundle/ccv.mjs +316 -78
  2. package/package.json +1 -1
  3. package/web/assets/{codemirror-D11GWMsB.js → codemirror-DfnUEAQ1.js} +1 -1
  4. package/web/assets/{index-BIhkivIg.js → index-1NZdgXKt.js} +1 -1
  5. package/web/assets/{index-Iwl1w0_V.js → index-6WrTm4Cl.js} +1 -1
  6. package/web/assets/{index-CZF-u-hv.js → index-Aa3M3Mcp.js} +1 -1
  7. package/web/assets/{index-Dn5EQ5YI.js → index-B4QS6XYB.js} +1 -1
  8. package/web/assets/{index-DO1FY4r1.js → index-BBJ7uyxo.js} +1 -1
  9. package/web/assets/{index-Bg0VD211.js → index-BFyhFly9.js} +1 -1
  10. package/web/assets/{index-J2jvoYvL.js → index-BSHub4Y3.js} +1 -1
  11. package/web/assets/{index-D57ZXpBD.js → index-BUVVNNln.js} +1 -1
  12. package/web/assets/{index-BDuAzkOQ.js → index-BrhFysJH.js} +1 -1
  13. package/web/assets/{index-09MC_4AF.js → index-C33PhVuR.js} +1 -1
  14. package/web/assets/{index-ZstREAcb.js → index-CE-6gz9W.js} +1 -1
  15. package/web/assets/{index-BSEf83Uc.js → index-CReKaug6.js} +1 -1
  16. package/web/assets/{index-4S0cl_O2.js → index-C_0hLH-6.js} +1 -1
  17. package/web/assets/{index-Bw0U50QY.js → index-Cu9yVjDd.js} +1 -1
  18. package/web/assets/{index-Czu0PbLS.js → index-DLoVg97c.js} +1 -1
  19. package/web/assets/{index-CmCj0IE6.js → index-DNvUBsh8.js} +1 -1
  20. package/web/assets/{index-DdCJlB8h.js → index-DdCEnSCp.js} +1 -1
  21. package/web/assets/{index-DABElhgt.js → index-DdWyOJPD.js} +1 -1
  22. package/web/assets/{index-xSo7_4Td.js → index-UUIF3jgc.js} +1 -1
  23. package/web/assets/index-i3oOZBX4.js +133 -0
  24. package/web/assets/index-ihyDKGfF.css +1 -0
  25. package/web/index.html +2 -2
  26. package/web/assets/index-9Toz507b.js +0 -132
  27. package/web/assets/index-D5y0Nk74.css +0 -1
package/bundle/ccv.mjs CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  // dist/ccv.js
4
4
  import { spawn as spawn2 } from "node:child_process";
5
- import { readFile as readFile14 } from "node:fs/promises";
6
- import path19 from "node:path";
5
+ import { readFile as readFile15 } from "node:fs/promises";
6
+ import path20 from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import { parseArgs } from "node:util";
9
9
 
@@ -145,9 +145,9 @@ function envDefaults() {
145
145
 
146
146
  // ../host/dist/server.js
147
147
  import { createServer } from "node:http";
148
- import { createReadStream, existsSync as existsSync3, statSync as statSync3 } from "node:fs";
149
- import path18 from "node:path";
150
- import { homedir as homedir5 } from "node:os";
148
+ import { createReadStream, existsSync as existsSync4, statSync as statSync3 } from "node:fs";
149
+ import path19 from "node:path";
150
+ import { homedir as homedir6 } from "node:os";
151
151
  import { randomUUID as randomUUID4 } from "node:crypto";
152
152
  import { WebSocketServer } from "ws";
153
153
  import { renameSession as renameSession2 } from "@anthropic-ai/claude-agent-sdk";
@@ -164,6 +164,8 @@ function workspaceIdFromPath(absolutePath) {
164
164
  // ../host/dist/session.js
165
165
  import { EventEmitter } from "node:events";
166
166
  import { randomUUID } from "node:crypto";
167
+ import { existsSync, writeFileSync } from "node:fs";
168
+ import { homedir as homedir3 } from "node:os";
167
169
  import path5 from "node:path";
168
170
  import { query, renameSession } from "@anthropic-ai/claude-agent-sdk";
169
171
 
@@ -1109,6 +1111,7 @@ function previewAnnotations(questions, answers) {
1109
1111
  return annotations;
1110
1112
  }
1111
1113
  var USAGE_MIN_INTERVAL_MS = 2e4;
1114
+ var USAGE_TURN_INTERVAL_MS = 12e4;
1112
1115
  var CONTEXT_MIN_INTERVAL_MS = 5e3;
1113
1116
  var ClaudeSession = class extends EventEmitter {
1114
1117
  id;
@@ -1864,6 +1867,7 @@ var ClaudeSession = class extends EventEmitter {
1864
1867
  const raw = await q.usage_EXPERIMENTAL_MAY_CHANGE_DO_NOT_RELY_ON_THIS_API_YET();
1865
1868
  this.#usage = toUsageState(raw);
1866
1869
  this.emit("usage", this.#usage);
1870
+ writeFinisherSnapshot(this.#usage, this.#claudeSessionId, this.cwd);
1867
1871
  } catch {
1868
1872
  } finally {
1869
1873
  this.#usageAt = Date.now();
@@ -2172,6 +2176,8 @@ var ClaudeSession = class extends EventEmitter {
2172
2176
  break;
2173
2177
  case "user":
2174
2178
  this.#handleToolResults(message);
2179
+ if (Date.now() - this.#usageAt >= USAGE_TURN_INTERVAL_MS)
2180
+ void this.refreshUsage();
2175
2181
  break;
2176
2182
  case "stream_event":
2177
2183
  this.#handleStreamEvent(message);
@@ -2421,6 +2427,27 @@ function toUsageState(raw) {
2421
2427
  fetchedAt: Date.now()
2422
2428
  };
2423
2429
  }
2430
+ function writeFinisherSnapshot(usage, sessionId, cwd) {
2431
+ if (!usage.available)
2432
+ return;
2433
+ const dir = path5.join(homedir3(), ".claude", "finisher");
2434
+ if (!existsSync(dir))
2435
+ return;
2436
+ const snapshot = {
2437
+ updated_at: Date.now(),
2438
+ session_id: sessionId,
2439
+ cwd,
2440
+ rate_limits: {
2441
+ five_hour: usage.fiveHour?.utilization ?? null,
2442
+ seven_day: usage.sevenDay?.utilization ?? null,
2443
+ spend_limit: null
2444
+ }
2445
+ };
2446
+ try {
2447
+ writeFileSync(path5.join(dir, "usage-cache.json"), JSON.stringify(snapshot, null, 2));
2448
+ } catch {
2449
+ }
2450
+ }
2424
2451
  function toUsageWindow(raw) {
2425
2452
  if (!raw || typeof raw.utilization !== "number")
2426
2453
  return null;
@@ -3021,9 +3048,9 @@ function parsePatch(raw) {
3021
3048
  body = "";
3022
3049
  }
3023
3050
  total += body.length;
3024
- const path20 = status === "deleted" ? oldPath : newPath || oldPath;
3051
+ const path21 = status === "deleted" ? oldPath : newPath || oldPath;
3025
3052
  files.push({
3026
- path: path20,
3053
+ path: path21,
3027
3054
  oldPath: status === "renamed" ? oldPath : "",
3028
3055
  status,
3029
3056
  added: count(body, "+"),
@@ -3035,10 +3062,10 @@ function parsePatch(raw) {
3035
3062
  return { files, truncated };
3036
3063
  }
3037
3064
  function stripPrefix(value) {
3038
- const path20 = unquote(value.trim());
3039
- if (path20 === "/dev/null")
3065
+ const path21 = unquote(value.trim());
3066
+ if (path21 === "/dev/null")
3040
3067
  return "";
3041
- return path20.replace(/^[ab]\//, "");
3068
+ return path21.replace(/^[ab]\//, "");
3042
3069
  }
3043
3070
  function count(body, mark) {
3044
3071
  let n = 0;
@@ -3280,7 +3307,7 @@ function describe3(error) {
3280
3307
  }
3281
3308
 
3282
3309
  // ../host/dist/finder.js
3283
- import { existsSync, statSync } from "node:fs";
3310
+ import { existsSync as existsSync2, statSync } from "node:fs";
3284
3311
  import { readdir as readdir2 } from "node:fs/promises";
3285
3312
  import os2 from "node:os";
3286
3313
  import path8 from "node:path";
@@ -3468,7 +3495,7 @@ var ProjectFinder = class {
3468
3495
  }
3469
3496
  };
3470
3497
  function usable(dir) {
3471
- return !contains(os2.tmpdir(), dir) && existsSync(dir);
3498
+ return !contains(os2.tmpdir(), dir) && existsSync2(dir);
3472
3499
  }
3473
3500
  function tokens(query2) {
3474
3501
  return query2.toLowerCase().split(/[^a-z0-9а-яё]+/i).filter(Boolean);
@@ -3565,7 +3592,7 @@ function fromText(text) {
3565
3592
  }
3566
3593
 
3567
3594
  // ../host/dist/workspaces.js
3568
- import { existsSync as existsSync2, statSync as statSync2 } from "node:fs";
3595
+ import { existsSync as existsSync3, statSync as statSync2 } from "node:fs";
3569
3596
  import { mkdir as mkdir3, readFile as readFile6, writeFile as writeFile4 } from "node:fs/promises";
3570
3597
  import path9 from "node:path";
3571
3598
  import { listSessions as listSessions2 } from "@anthropic-ai/claude-agent-sdk";
@@ -3589,7 +3616,7 @@ var WorkspaceRegistry = class {
3589
3616
  if (isEntry(item))
3590
3617
  return item;
3591
3618
  return null;
3592
- }).filter((e) => e !== null && existsSync2(e.path));
3619
+ }).filter((e) => e !== null && existsSync3(e.path));
3593
3620
  } catch {
3594
3621
  this.#entries = [];
3595
3622
  }
@@ -3612,7 +3639,7 @@ var WorkspaceRegistry = class {
3612
3639
  }
3613
3640
  async open(rawPath) {
3614
3641
  const absolute = path9.resolve(rawPath);
3615
- if (!existsSync2(absolute)) {
3642
+ if (!existsSync3(absolute)) {
3616
3643
  throw new Error(`\u041A\u0430\u0442\u0430\u043B\u043E\u0433 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D: ${absolute}`);
3617
3644
  }
3618
3645
  const existing = this.#entries.find((e) => e.path === absolute);
@@ -3679,14 +3706,14 @@ var WorkspaceRegistry = class {
3679
3706
  const name = nameRaw.trim();
3680
3707
  requireName(name);
3681
3708
  const parent = path9.resolve(parentRaw.trim());
3682
- if (!existsSync2(parent)) {
3709
+ if (!existsSync3(parent)) {
3683
3710
  throw new Error(`\u041A\u0430\u0442\u0430\u043B\u043E\u0433 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D: ${parent}`);
3684
3711
  }
3685
3712
  if (!statSync2(parent).isDirectory()) {
3686
3713
  throw new Error(`\u042D\u0442\u043E \u043D\u0435 \u043A\u0430\u0442\u0430\u043B\u043E\u0433: ${parent}`);
3687
3714
  }
3688
3715
  const target = path9.join(parent, name);
3689
- if (existsSync2(target)) {
3716
+ if (existsSync3(target)) {
3690
3717
  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`);
3691
3718
  }
3692
3719
  await mkdir3(target);
@@ -3768,7 +3795,7 @@ function toInfo(entry) {
3768
3795
  id: workspaceIdFromPath(absolute),
3769
3796
  path: absolute,
3770
3797
  name: path9.basename(absolute) || absolute,
3771
- hasClaudeMd: existsSync2(path9.join(absolute, "CLAUDE.md")),
3798
+ hasClaudeMd: existsSync3(path9.join(absolute, "CLAUDE.md")),
3772
3799
  ...archivedAt ? { archivedAt } : {}
3773
3800
  };
3774
3801
  }
@@ -3984,9 +4011,79 @@ function isRecord2(value) {
3984
4011
  return typeof value === "object" && value !== null && typeof value.workspaceId === "string" && typeof value.at === "number";
3985
4012
  }
3986
4013
 
3987
- // ../host/dist/folders.js
4014
+ // ../host/dist/order.js
3988
4015
  import { mkdir as mkdir6, readFile as readFile9, writeFile as writeFile7 } from "node:fs/promises";
3989
4016
  import path12 from "node:path";
4017
+ var ListOrderStore = class {
4018
+ #order = emptyOrder();
4019
+ #storePath;
4020
+ constructor(dataDir) {
4021
+ this.#storePath = path12.join(dataDir, "list-order.json");
4022
+ }
4023
+ async load() {
4024
+ try {
4025
+ const raw = await readFile9(this.#storePath, "utf8");
4026
+ this.#order = sanitize(JSON.parse(raw));
4027
+ } catch {
4028
+ this.#order = emptyOrder();
4029
+ }
4030
+ }
4031
+ get current() {
4032
+ return {
4033
+ workspaces: [...this.#order.workspaces],
4034
+ sessions: Object.fromEntries(Object.entries(this.#order.sessions).map(([k, v]) => [k, [...v]])),
4035
+ pinned: [...this.#order.pinned],
4036
+ live: [...this.#order.live]
4037
+ };
4038
+ }
4039
+ /**
4040
+ * Записать порядок одного списка. `ids === null` — сбросить его: список
4041
+ * вернётся к обычному порядку.
4042
+ */
4043
+ async set(scope, ids, workspaceId) {
4044
+ const clean = ids === null ? null : dedupe(ids);
4045
+ if (scope === "sessions") {
4046
+ if (!workspaceId)
4047
+ throw new Error("\u0414\u043B\u044F \u043F\u043E\u0440\u044F\u0434\u043A\u0430 \u0441\u0435\u0441\u0441\u0438\u0439 \u043D\u0443\u0436\u0435\u043D workspaceId");
4048
+ if (clean === null || clean.length === 0)
4049
+ delete this.#order.sessions[workspaceId];
4050
+ else
4051
+ this.#order.sessions[workspaceId] = clean;
4052
+ } else {
4053
+ this.#order[scope] = clean ?? [];
4054
+ }
4055
+ await mkdir6(path12.dirname(this.#storePath), { recursive: true });
4056
+ await writeFile7(this.#storePath, JSON.stringify(this.#order, null, 2), "utf8");
4057
+ }
4058
+ };
4059
+ function emptyOrder() {
4060
+ return { workspaces: [], sessions: {}, pinned: [], live: [] };
4061
+ }
4062
+ function dedupe(ids) {
4063
+ return [...new Set(ids.filter((id) => typeof id === "string" && id !== ""))];
4064
+ }
4065
+ function sanitize(value) {
4066
+ const result = emptyOrder();
4067
+ if (typeof value !== "object" || value === null)
4068
+ return result;
4069
+ const record = value;
4070
+ const list = (v) => Array.isArray(v) ? dedupe(v) : [];
4071
+ result.workspaces = list(record.workspaces);
4072
+ result.pinned = list(record.pinned);
4073
+ result.live = list(record.live);
4074
+ if (typeof record.sessions === "object" && record.sessions !== null) {
4075
+ for (const [workspaceId, ids] of Object.entries(record.sessions)) {
4076
+ const clean = list(ids);
4077
+ if (clean.length > 0)
4078
+ result.sessions[workspaceId] = clean;
4079
+ }
4080
+ }
4081
+ return result;
4082
+ }
4083
+
4084
+ // ../host/dist/folders.js
4085
+ import { mkdir as mkdir7, readFile as readFile10, writeFile as writeFile8 } from "node:fs/promises";
4086
+ import path13 from "node:path";
3990
4087
  import { randomUUID as randomUUID2 } from "node:crypto";
3991
4088
  var Folders = class {
3992
4089
  #workspaceFolders = /* @__PURE__ */ new Map();
@@ -3997,11 +4094,11 @@ var Folders = class {
3997
4094
  #pinnedAssignments = /* @__PURE__ */ new Map();
3998
4095
  #storePath;
3999
4096
  constructor(dataDir) {
4000
- this.#storePath = path12.join(dataDir, "folders.json");
4097
+ this.#storePath = path13.join(dataDir, "folders.json");
4001
4098
  }
4002
4099
  async load() {
4003
4100
  try {
4004
- const raw = await readFile9(this.#storePath, "utf8");
4101
+ const raw = await readFile10(this.#storePath, "utf8");
4005
4102
  const parsed = JSON.parse(raw);
4006
4103
  this.#workspaceFolders = new Map(Object.entries(parsed.workspaceFolders ?? {}).filter(([, r]) => isWorkspaceFolder(r)));
4007
4104
  this.#sessionFolders = new Map(Object.entries(parsed.sessionFolders ?? {}).filter(([, r]) => isSessionFolder(r)));
@@ -4184,8 +4281,8 @@ var Folders = class {
4184
4281
  sessionAssignments: Object.fromEntries(this.#sessionAssignments),
4185
4282
  pinnedAssignments: Object.fromEntries(this.#pinnedAssignments)
4186
4283
  };
4187
- await mkdir6(path12.dirname(this.#storePath), { recursive: true });
4188
- await writeFile7(this.#storePath, JSON.stringify(file, null, 2), "utf8");
4284
+ await mkdir7(path13.dirname(this.#storePath), { recursive: true });
4285
+ await writeFile8(this.#storePath, JSON.stringify(file, null, 2), "utf8");
4189
4286
  }
4190
4287
  };
4191
4288
  function isWorkspaceFolder(value) {
@@ -4199,11 +4296,13 @@ function isPinnedFolder(value) {
4199
4296
  }
4200
4297
 
4201
4298
  // ../host/dist/app-settings.js
4202
- import { mkdir as mkdir7, readFile as readFile10, writeFile as writeFile8 } from "node:fs/promises";
4203
- import { homedir as homedir3 } from "node:os";
4204
- import path13 from "node:path";
4299
+ import { mkdir as mkdir8, readFile as readFile11, writeFile as writeFile9 } from "node:fs/promises";
4300
+ import { homedir as homedir4 } from "node:os";
4301
+ import path14 from "node:path";
4205
4302
  var DEFAULT_AUTO_ARCHIVE_AFTER_MS = 2 * 24 * 60 * 60 * 1e3;
4206
4303
  var MIN_AUTO_ARCHIVE_AFTER_MS = 60 * 60 * 1e3;
4304
+ var DEFAULT_AUTO_STOP_AFTER_MS = 10 * 60 * 1e3;
4305
+ var MIN_AUTO_STOP_AFTER_MS = 60 * 1e3;
4207
4306
  var MIN_AUTO_SAVE_AFTER_PAUSE_MS = 500;
4208
4307
  var FOLDER_ACCESS = ["all", "projects", "project"];
4209
4308
  var PERMISSION_MODES = [
@@ -4216,6 +4315,7 @@ var PERMISSION_MODES = [
4216
4315
  ];
4217
4316
  var DEFAULT_APP_SETTINGS = {
4218
4317
  autoArchiveAfterMs: DEFAULT_AUTO_ARCHIVE_AFTER_MS,
4318
+ autoStopAfterMs: DEFAULT_AUTO_STOP_AFTER_MS,
4219
4319
  // Строгий режим по умолчанию: молча писать в чужой проект приложение начнёт
4220
4320
  // только после того, как об этом попросили явно.
4221
4321
  folderAccess: "project",
@@ -4230,18 +4330,20 @@ var DEFAULT_APP_SETTINGS = {
4230
4330
  // Терминал по умолчанию закрыт — его включают, а не выключают: приложение,
4231
4331
  // открытое в браузере, не обязано с первого запуска уметь запускать на этой
4232
4332
  // машине что угодно.
4233
- terminalEnabled: false
4333
+ terminalEnabled: false,
4334
+ // Выключено по умолчанию: списки идут в том порядке, в каком их расставили.
4335
+ liveSessionsFirst: false
4234
4336
  };
4235
4337
  var AppSettingsStore = class {
4236
4338
  #settings = { ...DEFAULT_APP_SETTINGS };
4237
4339
  #storePath;
4238
4340
  constructor(dataDir) {
4239
- this.#storePath = path13.join(dataDir, "app-settings.json");
4341
+ this.#storePath = path14.join(dataDir, "app-settings.json");
4240
4342
  }
4241
4343
  async load() {
4242
4344
  try {
4243
- const raw = await readFile10(this.#storePath, "utf8");
4244
- this.#settings = sanitize(JSON.parse(raw), DEFAULT_APP_SETTINGS);
4345
+ const raw = await readFile11(this.#storePath, "utf8");
4346
+ this.#settings = sanitize2(JSON.parse(raw), DEFAULT_APP_SETTINGS);
4245
4347
  } catch {
4246
4348
  this.#settings = { ...DEFAULT_APP_SETTINGS };
4247
4349
  }
@@ -4258,14 +4360,14 @@ var AppSettingsStore = class {
4258
4360
  * всё равно получит в ответ то, что реально записано, и покажет именно это.
4259
4361
  */
4260
4362
  async update(patch) {
4261
- const next = sanitize(patch, this.#settings);
4363
+ const next = sanitize2(patch, this.#settings);
4262
4364
  this.#settings = next;
4263
- await mkdir7(path13.dirname(this.#storePath), { recursive: true });
4264
- await writeFile8(this.#storePath, JSON.stringify(next, null, 2), "utf8");
4365
+ await mkdir8(path14.dirname(this.#storePath), { recursive: true });
4366
+ await writeFile9(this.#storePath, JSON.stringify(next, null, 2), "utf8");
4265
4367
  return this.current;
4266
4368
  }
4267
4369
  };
4268
- function sanitize(value, fallback) {
4370
+ function sanitize2(value, fallback) {
4269
4371
  const result = { ...fallback };
4270
4372
  if (typeof value !== "object" || value === null)
4271
4373
  return result;
@@ -4274,6 +4376,10 @@ function sanitize(value, fallback) {
4274
4376
  if (typeof after === "number" && Number.isFinite(after)) {
4275
4377
  result.autoArchiveAfterMs = after <= 0 ? 0 : Math.max(Math.round(after), MIN_AUTO_ARCHIVE_AFTER_MS);
4276
4378
  }
4379
+ const stopAfter = record.autoStopAfterMs;
4380
+ if (typeof stopAfter === "number" && Number.isFinite(stopAfter)) {
4381
+ result.autoStopAfterMs = stopAfter <= 0 ? 0 : Math.max(Math.round(stopAfter), MIN_AUTO_STOP_AFTER_MS);
4382
+ }
4277
4383
  const access3 = record.folderAccess;
4278
4384
  if (typeof access3 === "string" && FOLDER_ACCESS.includes(access3)) {
4279
4385
  result.folderAccess = access3;
@@ -4296,10 +4402,13 @@ function sanitize(value, fallback) {
4296
4402
  if (typeof record.terminalEnabled === "boolean") {
4297
4403
  result.terminalEnabled = record.terminalEnabled;
4298
4404
  }
4405
+ if (typeof record.liveSessionsFirst === "boolean") {
4406
+ result.liveSessionsFirst = record.liveSessionsFirst;
4407
+ }
4299
4408
  return result;
4300
4409
  }
4301
4410
  function claudeHome() {
4302
- return path13.join(homedir3(), ".claude");
4411
+ return path14.join(homedir4(), ".claude");
4303
4412
  }
4304
4413
  function writableRoots(mode, cwd, workspacePaths) {
4305
4414
  if (mode === "all")
@@ -4309,7 +4418,7 @@ function writableRoots(mode, cwd, workspacePaths) {
4309
4418
  return [...roots];
4310
4419
  for (const workspace of [cwd, ...workspacePaths]) {
4311
4420
  roots.add(workspace);
4312
- const parent = path13.dirname(workspace);
4421
+ const parent = path14.dirname(workspace);
4313
4422
  if (parent !== workspace)
4314
4423
  roots.add(parent);
4315
4424
  }
@@ -4317,9 +4426,9 @@ function writableRoots(mode, cwd, workspacePaths) {
4317
4426
  }
4318
4427
 
4319
4428
  // ../host/dist/plugins.js
4320
- import { readFile as readFile11 } from "node:fs/promises";
4321
- import { homedir as homedir4 } from "node:os";
4322
- import path14 from "node:path";
4429
+ import { readFile as readFile12 } from "node:fs/promises";
4430
+ import { homedir as homedir5 } from "node:os";
4431
+ import path15 from "node:path";
4323
4432
  var TIMEOUT_MS2 = 12e4;
4324
4433
  var SAFE_ARG = /^[A-Za-z0-9][A-Za-z0-9._@/-]*$/;
4325
4434
  var PluginCli = class {
@@ -4418,10 +4527,10 @@ async function readCatalog(markets, installed) {
4418
4527
  return lists.flat().sort((a, b) => a.name.localeCompare(b.name));
4419
4528
  }
4420
4529
  async function readMarketplace(market, installed) {
4421
- const root = market.installLocation || path14.join(homedir4(), ".claude", "plugins", "marketplaces", market.name);
4530
+ const root = market.installLocation || path15.join(homedir5(), ".claude", "plugins", "marketplaces", market.name);
4422
4531
  let manifest;
4423
4532
  try {
4424
- const text = await readFile11(path14.join(root, ".claude-plugin", "marketplace.json"), "utf8");
4533
+ const text = await readFile12(path15.join(root, ".claude-plugin", "marketplace.json"), "utf8");
4425
4534
  manifest = JSON.parse(text);
4426
4535
  } catch {
4427
4536
  return [];
@@ -4662,8 +4771,8 @@ function describe4(error) {
4662
4771
 
4663
4772
  // ../host/dist/relay-link.js
4664
4773
  import { EventEmitter as EventEmitter3 } from "node:events";
4665
- import { readFile as readFile12, writeFile as writeFile9, rm as rm3, mkdir as mkdir8 } from "node:fs/promises";
4666
- import path15 from "node:path";
4774
+ import { readFile as readFile13, writeFile as writeFile10, rm as rm3, mkdir as mkdir9 } from "node:fs/promises";
4775
+ import path16 from "node:path";
4667
4776
  import { hostname } from "node:os";
4668
4777
  import WebSocket from "ws";
4669
4778
 
@@ -4718,7 +4827,7 @@ var RelayLink = class extends EventEmitter3 {
4718
4827
  #log;
4719
4828
  constructor(options2) {
4720
4829
  super();
4721
- this.#file = path15.join(options2.dataDir, "relay.json");
4830
+ this.#file = path16.join(options2.dataDir, "relay.json");
4722
4831
  this.#defaultUrl = options2.defaultUrl.replace(/\/+$/, "");
4723
4832
  this.#log = options2.log ?? ((msg) => console.log(`[relay] ${msg}`));
4724
4833
  }
@@ -4799,7 +4908,7 @@ var RelayLink = class extends EventEmitter3 {
4799
4908
  }
4800
4909
  async #read() {
4801
4910
  try {
4802
- const raw = await readFile12(this.#file, "utf8");
4911
+ const raw = await readFile13(this.#file, "utf8");
4803
4912
  const parsed = JSON.parse(raw);
4804
4913
  if (typeof parsed !== "object" || parsed === null)
4805
4914
  return null;
@@ -4812,8 +4921,8 @@ var RelayLink = class extends EventEmitter3 {
4812
4921
  }
4813
4922
  }
4814
4923
  async #write(credentials) {
4815
- await mkdir8(path15.dirname(this.#file), { recursive: true });
4816
- await writeFile9(this.#file, `${JSON.stringify(credentials, null, 2)}
4924
+ await mkdir9(path16.dirname(this.#file), { recursive: true });
4925
+ await writeFile10(this.#file, `${JSON.stringify(credentials, null, 2)}
4817
4926
  `, {
4818
4927
  encoding: "utf8",
4819
4928
  mode: 384
@@ -5070,7 +5179,7 @@ function humanize(error) {
5070
5179
  // ../host/dist/terminal.js
5071
5180
  import { EventEmitter as EventEmitter4 } from "node:events";
5072
5181
  import { access as access2, constants as constants2 } from "node:fs/promises";
5073
- import path16 from "node:path";
5182
+ import path17 from "node:path";
5074
5183
  import { randomUUID as randomUUID3 } from "node:crypto";
5075
5184
  var SCROLLBACK_BYTES = 256 * 1024;
5076
5185
  var FLUSH_MS = 12;
@@ -5240,22 +5349,22 @@ async function defaultShell() {
5240
5349
  if (pwsh)
5241
5350
  return { file: pwsh, args: [], title: "pwsh" };
5242
5351
  const system = process.env.SystemRoot ?? "C:\\Windows";
5243
- const powershell = path16.join(system, "System32/WindowsPowerShell/v1.0/powershell.exe");
5352
+ const powershell = path17.join(system, "System32/WindowsPowerShell/v1.0/powershell.exe");
5244
5353
  if (await exists2(powershell))
5245
5354
  return { file: powershell, args: [], title: "powershell" };
5246
5355
  return { file: process.env.COMSPEC ?? "cmd.exe", args: [], title: "cmd" };
5247
5356
  }
5248
5357
  const shell = process.env.SHELL;
5249
5358
  if (shell && await exists2(shell))
5250
- return { file: shell, args: [], title: path16.basename(shell) };
5359
+ return { file: shell, args: [], title: path17.basename(shell) };
5251
5360
  if (await exists2("/bin/bash"))
5252
5361
  return { file: "/bin/bash", args: [], title: "bash" };
5253
5362
  return { file: "/bin/sh", args: [], title: "sh" };
5254
5363
  }
5255
5364
  async function findOnPath(name) {
5256
- const dirs = (process.env.PATH ?? "").split(path16.delimiter).filter(Boolean);
5365
+ const dirs = (process.env.PATH ?? "").split(path17.delimiter).filter(Boolean);
5257
5366
  for (const dir of dirs) {
5258
- const candidate = path16.join(dir, name);
5367
+ const candidate = path17.join(dir, name);
5259
5368
  if (await exists2(candidate))
5260
5369
  return candidate;
5261
5370
  }
@@ -5266,10 +5375,10 @@ function exists2(file) {
5266
5375
  }
5267
5376
 
5268
5377
  // ../host/dist/mcp-config.js
5269
- import { readFile as readFile13, writeFile as writeFile10, mkdir as mkdir9 } from "node:fs/promises";
5270
- import path17 from "node:path";
5378
+ import { readFile as readFile14, writeFile as writeFile11, mkdir as mkdir10 } from "node:fs/promises";
5379
+ import path18 from "node:path";
5271
5380
  var MCP_FILE = ".mcp.json";
5272
- var LOCAL_SETTINGS2 = path17.join(".claude", "settings.local.json");
5381
+ var LOCAL_SETTINGS2 = path18.join(".claude", "settings.local.json");
5273
5382
  var queues = /* @__PURE__ */ new Map();
5274
5383
  function serialize(cwd, work) {
5275
5384
  const previous = queues.get(cwd) ?? Promise.resolve();
@@ -5278,7 +5387,7 @@ function serialize(cwd, work) {
5278
5387
  return next;
5279
5388
  }
5280
5389
  async function readProjectServers(cwd) {
5281
- const file = await readJson(path17.join(cwd, MCP_FILE));
5390
+ const file = await readJson(path18.join(cwd, MCP_FILE));
5282
5391
  const servers = file?.mcpServers;
5283
5392
  if (!servers || typeof servers !== "object")
5284
5393
  return {};
@@ -5286,23 +5395,23 @@ async function readProjectServers(cwd) {
5286
5395
  }
5287
5396
  function addProjectServer(cwd, name, config) {
5288
5397
  return serialize(cwd, async () => {
5289
- const file = await readJson(path17.join(cwd, MCP_FILE)) ?? {};
5398
+ const file = await readJson(path18.join(cwd, MCP_FILE)) ?? {};
5290
5399
  const servers = file.mcpServers ?? {};
5291
5400
  servers[name] = config;
5292
5401
  file.mcpServers = servers;
5293
- await writeJson(path17.join(cwd, MCP_FILE), file);
5402
+ await writeJson(path18.join(cwd, MCP_FILE), file);
5294
5403
  await approve(cwd, name);
5295
5404
  });
5296
5405
  }
5297
5406
  function removeProjectServer(cwd, name) {
5298
5407
  return serialize(cwd, async () => {
5299
- const file = await readJson(path17.join(cwd, MCP_FILE));
5408
+ const file = await readJson(path18.join(cwd, MCP_FILE));
5300
5409
  const servers = file?.mcpServers;
5301
5410
  if (!servers || !(name in servers))
5302
5411
  return false;
5303
5412
  delete servers[name];
5304
- await writeJson(path17.join(cwd, MCP_FILE), file);
5305
- const settingsPath = path17.join(cwd, LOCAL_SETTINGS2);
5413
+ await writeJson(path18.join(cwd, MCP_FILE), file);
5414
+ const settingsPath = path18.join(cwd, LOCAL_SETTINGS2);
5306
5415
  const settings = await readJson(settingsPath);
5307
5416
  const enabled = settings?.enabledMcpjsonServers;
5308
5417
  if (settings && Array.isArray(enabled) && enabled.includes(name)) {
@@ -5313,7 +5422,7 @@ function removeProjectServer(cwd, name) {
5313
5422
  });
5314
5423
  }
5315
5424
  async function approve(cwd, name) {
5316
- const file = path17.join(cwd, LOCAL_SETTINGS2);
5425
+ const file = path18.join(cwd, LOCAL_SETTINGS2);
5317
5426
  const settings = await readJson(file) ?? {};
5318
5427
  const enabled = Array.isArray(settings.enabledMcpjsonServers) ? settings.enabledMcpjsonServers : [];
5319
5428
  if (!enabled.includes(name))
@@ -5352,21 +5461,21 @@ function describeConfig(config) {
5352
5461
  }
5353
5462
  async function readJson(file) {
5354
5463
  try {
5355
- const raw = await readFile13(file, "utf8");
5464
+ const raw = await readFile14(file, "utf8");
5356
5465
  const parsed = JSON.parse(raw);
5357
5466
  return parsed && typeof parsed === "object" ? parsed : null;
5358
5467
  } catch (error) {
5359
5468
  if (error.code === "ENOENT")
5360
5469
  return null;
5361
5470
  if (error instanceof SyntaxError) {
5362
- throw new Error(`\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0440\u0430\u0437\u043E\u0431\u0440\u0430\u0442\u044C ${path17.basename(file)}: ${error.message}`);
5471
+ throw new Error(`\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0440\u0430\u0437\u043E\u0431\u0440\u0430\u0442\u044C ${path18.basename(file)}: ${error.message}`);
5363
5472
  }
5364
5473
  throw error;
5365
5474
  }
5366
5475
  }
5367
5476
  async function writeJson(file, data) {
5368
- await mkdir9(path17.dirname(file), { recursive: true });
5369
- await writeFile10(file, `${JSON.stringify(data, null, 2)}
5477
+ await mkdir10(path18.dirname(file), { recursive: true });
5478
+ await writeFile11(file, `${JSON.stringify(data, null, 2)}
5370
5479
  `, "utf8");
5371
5480
  }
5372
5481
 
@@ -5375,11 +5484,18 @@ var SIDE_TITLE = "\xB7 \u0432\u043E\u043F\u0440\u043E\u0441 \u043D\u0430 \u043F\
5375
5484
  var FINDER_TITLE = "\xB7 \u043F\u043E\u0438\u0441\u043A \u043F\u0440\u043E\u0435\u043A\u0442\u0430";
5376
5485
  var HISTORY_PAGE = 250;
5377
5486
  var HISTORY_PAGE_MAX = 500;
5487
+ var AUTO_STOP_SWEEP_MS = 30 * 1e3;
5488
+ var BUSY_STATES = /* @__PURE__ */ new Set([
5489
+ "running",
5490
+ "waiting_permission",
5491
+ "reconnecting"
5492
+ ]);
5378
5493
  var HostServer = class {
5379
5494
  #options;
5380
5495
  #workspaces;
5381
5496
  #archive;
5382
5497
  #pins;
5498
+ #order;
5383
5499
  #folders;
5384
5500
  #settings;
5385
5501
  #finder;
@@ -5398,6 +5514,21 @@ var HostServer = class {
5398
5514
  #clients = /* @__PURE__ */ new Set();
5399
5515
  /** Удалённые вкладки по их id в relay — чтобы адресовать ответ именно той. */
5400
5516
  #remotes = /* @__PURE__ */ new Map();
5517
+ /**
5518
+ * Сессии с недописанным черновиком — по окнам (кадр `session.drafts`).
5519
+ *
5520
+ * По окнам, а не одним множеством: черновик держит сессию, пока окно с ним
5521
+ * подключено, и уйти вместе с окном он должен сам, не дожидаясь, пока
5522
+ * кто-то пришлёт пустой список.
5523
+ */
5524
+ #drafts = /* @__PURE__ */ new Map();
5525
+ /**
5526
+ * Последнее движение черновика по сессии — появился, исчез, окно с ним
5527
+ * отключилось. Отсчёт простоя идёт от позднего из этого и событий самой
5528
+ * сессии: стёртый черновик — тоже действие человека в ней.
5529
+ */
5530
+ #draftTouchedAt = /* @__PURE__ */ new Map();
5531
+ #autoStopTimer = null;
5401
5532
  #hostId = randomUUID4();
5402
5533
  #plugins = new PluginCli();
5403
5534
  #auth = new AuthCli();
@@ -5418,6 +5549,7 @@ var HostServer = class {
5418
5549
  this.#workspaces = new WorkspaceRegistry(options2.dataDir);
5419
5550
  this.#archive = new SessionArchive(options2.dataDir);
5420
5551
  this.#pins = new SessionPins(options2.dataDir);
5552
+ this.#order = new ListOrderStore(options2.dataDir);
5421
5553
  this.#folders = new Folders(options2.dataDir);
5422
5554
  this.#settings = new AppSettingsStore(options2.dataDir);
5423
5555
  this.#finder = new ProjectFinder(this.#workspaces);
@@ -5436,6 +5568,7 @@ var HostServer = class {
5436
5568
  await this.#workspaces.load();
5437
5569
  await this.#archive.load();
5438
5570
  await this.#pins.load();
5571
+ await this.#order.load();
5439
5572
  await this.#folders.load();
5440
5573
  await this.#settings.load();
5441
5574
  await this.#github.load();
@@ -5460,8 +5593,13 @@ var HostServer = class {
5460
5593
  await this.#relay.start().catch((error) => {
5461
5594
  console.error("[relay]", error instanceof Error ? error.message : String(error));
5462
5595
  });
5596
+ this.#autoStopTimer = setInterval(() => void this.#stopIdleSessions(), AUTO_STOP_SWEEP_MS);
5597
+ this.#autoStopTimer.unref();
5463
5598
  }
5464
5599
  async shutdown() {
5600
+ if (this.#autoStopTimer)
5601
+ clearInterval(this.#autoStopTimer);
5602
+ this.#autoStopTimer = null;
5465
5603
  this.#auth.cancel();
5466
5604
  this.#terminals.closeAll();
5467
5605
  await this.#relay.close();
@@ -5481,7 +5619,7 @@ var HostServer = class {
5481
5619
  }
5482
5620
  };
5483
5621
  this.#clients.add(client);
5484
- socket.on("close", () => this.#clients.delete(client));
5622
+ socket.on("close", () => this.#forgetClient(client));
5485
5623
  socket.on("message", (raw) => {
5486
5624
  let parsed;
5487
5625
  try {
@@ -5512,7 +5650,7 @@ var HostServer = class {
5512
5650
  if (!client)
5513
5651
  return;
5514
5652
  this.#remotes.delete(clientId);
5515
- this.#clients.delete(client);
5653
+ this.#forgetClient(client);
5516
5654
  });
5517
5655
  this.#relay.on("frame", (clientId, body) => {
5518
5656
  const client = this.#remotes.get(clientId);
@@ -5619,6 +5757,8 @@ var HostServer = class {
5619
5757
  });
5620
5758
  this.#send(socket, { type: "workspace.list", ...this.#workspaceLists() });
5621
5759
  this.#sendFolders(socket);
5760
+ this.#send(socket, { type: "order.state", order: this.#order.current });
5761
+ this.#sendLive(socket);
5622
5762
  this.#auth.status().then((status) => this.#send(socket, { type: "auth.status", status })).catch(() => void 0);
5623
5763
  this.#send(socket, { type: "relay.state", state: this.#relay.state });
5624
5764
  return;
@@ -5693,6 +5833,22 @@ var HostServer = class {
5693
5833
  case "session.close":
5694
5834
  await this.#dropSession(frame.sessionId, "closed by client");
5695
5835
  return;
5836
+ case "session.drafts": {
5837
+ const next = new Set(frame.sessionIds.filter((id) => typeof id === "string"));
5838
+ const previous = this.#drafts.get(socket) ?? /* @__PURE__ */ new Set();
5839
+ const now = Date.now();
5840
+ for (const id of next)
5841
+ if (!previous.has(id))
5842
+ this.#draftTouchedAt.set(id, now);
5843
+ for (const id of previous)
5844
+ if (!next.has(id))
5845
+ this.#draftTouchedAt.set(id, now);
5846
+ if (next.size > 0)
5847
+ this.#drafts.set(socket, next);
5848
+ else
5849
+ this.#drafts.delete(socket);
5850
+ return;
5851
+ }
5696
5852
  case "session.reconnect":
5697
5853
  this.#requireSession(frame.sessionId).reconnect("\u043F\u043E \u043F\u0440\u043E\u0441\u044C\u0431\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F");
5698
5854
  return;
@@ -5734,6 +5890,13 @@ var HostServer = class {
5734
5890
  case "session.pins":
5735
5891
  await this.#sendPinned();
5736
5892
  return;
5893
+ case "order.get":
5894
+ this.#send(socket, { type: "order.state", order: this.#order.current });
5895
+ return;
5896
+ case "order.set":
5897
+ await this.#order.set(frame.scope, frame.ids, frame.workspaceId);
5898
+ this.#broadcast({ type: "order.state", order: this.#order.current });
5899
+ return;
5737
5900
  case "folder.create": {
5738
5901
  if (frame.scope === "session") {
5739
5902
  if (!frame.workspaceId)
@@ -6375,8 +6538,82 @@ var HostServer = class {
6375
6538
  await session.close(reason);
6376
6539
  this.#sessions.delete(sessionId);
6377
6540
  this.#broadcast({ type: "session.closed", sessionId, reason });
6541
+ this.#sendLive();
6378
6542
  return true;
6379
6543
  }
6544
+ /**
6545
+ * Окно ушло — вместе с ним уходят и его черновики.
6546
+ *
6547
+ * Сам текст остаётся в localStorage окна и вернётся, когда сессию откроют
6548
+ * снова; держать ради него процесс CLI, пока окна нет, незачем. Момент
6549
+ * ухода считается действием: сессия, которую только что бросили с
6550
+ * черновиком, получает полный срок, а не остаток.
6551
+ */
6552
+ #forgetClient(client) {
6553
+ this.#clients.delete(client);
6554
+ const drafts = this.#drafts.get(client);
6555
+ if (!drafts)
6556
+ return;
6557
+ this.#drafts.delete(client);
6558
+ const now = Date.now();
6559
+ for (const id of drafts)
6560
+ this.#draftTouchedAt.set(id, now);
6561
+ }
6562
+ /**
6563
+ * Остановить живые сессии, простаивающие дольше `autoStopAfterMs`.
6564
+ *
6565
+ * Только пользовательские: боковой вопрос уходит вместе с родителем, у
6566
+ * сессии поиска проекта своя жизнь. Простой — это когда сессия ничего не
6567
+ * делает, в ней ничего не происходит и ни одно окно не держит для неё
6568
+ * черновик (см. `AppSettings.autoStopAfterMs`).
6569
+ */
6570
+ async #stopIdleSessions() {
6571
+ const after = this.#settings.current.autoStopAfterMs;
6572
+ if (after <= 0)
6573
+ return;
6574
+ const now = Date.now();
6575
+ const drafted = /* @__PURE__ */ new Set();
6576
+ for (const ids of this.#drafts.values())
6577
+ for (const id of ids)
6578
+ drafted.add(id);
6579
+ for (const session of [...this.#sessions.values()]) {
6580
+ if (!session.workspaceId || session === this.#finderSession)
6581
+ continue;
6582
+ const summary = session.summary();
6583
+ if (summary.parentSessionId)
6584
+ continue;
6585
+ if (drafted.has(session.id))
6586
+ continue;
6587
+ if (BUSY_STATES.has(summary.state) || summary.backgroundWork)
6588
+ continue;
6589
+ const side = this.#sides.get(session.id);
6590
+ if (side && BUSY_STATES.has(side.state))
6591
+ continue;
6592
+ const lastActivity = Math.max(summary.updatedAt, side?.summary().updatedAt ?? 0, this.#draftTouchedAt.get(session.id) ?? 0);
6593
+ if (now - lastActivity < after)
6594
+ continue;
6595
+ try {
6596
+ await this.#dropSession(session.id, "idle");
6597
+ } catch (error) {
6598
+ console.error("[auto-stop]", error instanceof Error ? error.message : String(error));
6599
+ }
6600
+ this.#draftTouchedAt.delete(session.id);
6601
+ }
6602
+ }
6603
+ /**
6604
+ * Живые сессии всех проектов — вкладке запущенных.
6605
+ *
6606
+ * Без `socket` — всем окнам: состав живых поменялся. Боковые сессии и сессия
6607
+ * поиска проекта в список не идут, их пользователь не заводил.
6608
+ */
6609
+ #sendLive(socket) {
6610
+ const sessions = [...this.#sessions.values()].filter((s) => s !== this.#finderSession).map((s) => s.summary()).filter((s) => !s.parentSessionId);
6611
+ const frame = { type: "session.live", sessions };
6612
+ if (socket)
6613
+ this.#send(socket, frame);
6614
+ else
6615
+ this.#broadcast(frame);
6616
+ }
6380
6617
  /**
6381
6618
  * Разослать список сессий проекта всем окнам.
6382
6619
  *
@@ -6438,6 +6675,7 @@ var HostServer = class {
6438
6675
  this.#wire(session);
6439
6676
  this.#sessions.set(session.id, session);
6440
6677
  session.start();
6678
+ this.#sendLive();
6441
6679
  this.#send(socket, { type: "session.summary", session: session.summary() });
6442
6680
  }
6443
6681
  /**
@@ -6551,7 +6789,7 @@ var HostServer = class {
6551
6789
  */
6552
6790
  #roots() {
6553
6791
  const roots = this.#finder.roots();
6554
- return roots.length > 0 ? roots : [homedir5()];
6792
+ return roots.length > 0 ? roots : [homedir6()];
6555
6793
  }
6556
6794
  /**
6557
6795
  * Где сессии этого каталога разрешено писать без вопроса.
@@ -6884,15 +7122,15 @@ var HostServer = class {
6884
7122
  }
6885
7123
  #serveStatic(req, res) {
6886
7124
  const webRoot = this.#options.webRoot;
6887
- if (!webRoot || !existsSync3(webRoot)) {
7125
+ if (!webRoot || !existsSync4(webRoot)) {
6888
7126
  res.writeHead(200, { "content-type": "text/plain; charset=utf-8" });
6889
7127
  res.end("CCV host is running. UI is served by the Vite dev server.\n");
6890
7128
  return;
6891
7129
  }
6892
7130
  const url2 = new URL(req.url ?? "/", "http://localhost");
6893
- const requested = path18.join(webRoot, path18.normalize(url2.pathname));
6894
- const target = requested.startsWith(webRoot) && existsSync3(requested) && statSync3(requested).isFile() ? requested : path18.join(webRoot, "index.html");
6895
- if (!existsSync3(target)) {
7131
+ const requested = path19.join(webRoot, path19.normalize(url2.pathname));
7132
+ const target = requested.startsWith(webRoot) && existsSync4(requested) && statSync3(requested).isFile() ? requested : path19.join(webRoot, "index.html");
7133
+ if (!existsSync4(target)) {
6896
7134
  res.writeHead(404).end("not found");
6897
7135
  return;
6898
7136
  }
@@ -6917,12 +7155,12 @@ function contentType(file) {
6917
7155
  ".svg": "image/svg+xml",
6918
7156
  ".woff2": "font/woff2"
6919
7157
  };
6920
- return types[path18.extname(file)] ?? "application/octet-stream";
7158
+ return types[path19.extname(file)] ?? "application/octet-stream";
6921
7159
  }
6922
7160
 
6923
7161
  // dist/ccv.js
6924
- var HERE = path19.dirname(fileURLToPath(import.meta.url));
6925
- var PACKAGE_ROOT = path19.resolve(HERE, "..");
7162
+ var HERE = path20.dirname(fileURLToPath(import.meta.url));
7163
+ var PACKAGE_ROOT = path20.resolve(HERE, "..");
6926
7164
  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.
6927
7165
 
6928
7166
  \u0418\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u0435:
@@ -6989,7 +7227,7 @@ var server = new HostServer({
6989
7227
  dataDir: defaults.dataDir,
6990
7228
  // UI лежит в пакете рядом с бандлом — это и есть разница между установленной
6991
7229
  // утилитой и запуском из репозитория, где сборка живёт в `apps/web/dist`.
6992
- webRoot: process.env.CCV_WEB_ROOT ?? path19.join(PACKAGE_ROOT, "web"),
7230
+ webRoot: process.env.CCV_WEB_ROOT ?? path20.join(PACKAGE_ROOT, "web"),
6993
7231
  version,
6994
7232
  relayUrl: defaults.relayUrl
6995
7233
  });
@@ -7018,7 +7256,7 @@ for (const signal of ["SIGINT", "SIGTERM"]) {
7018
7256
  }
7019
7257
  async function packageVersion() {
7020
7258
  try {
7021
- const text = await readFile14(path19.join(PACKAGE_ROOT, "package.json"), "utf8");
7259
+ const text = await readFile15(path20.join(PACKAGE_ROOT, "package.json"), "utf8");
7022
7260
  return JSON.parse(text).version ?? "0.0.0";
7023
7261
  } catch {
7024
7262
  return "0.0.0";