@i14ks/ccv 0.12.3 → 0.13.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 +206 -76
  2. package/package.json +1 -1
  3. package/web/assets/{codemirror-D11GWMsB.js → codemirror-DAgbW3uz.js} +1 -1
  4. package/web/assets/{index-Iwl1w0_V.js → index-1j2ILa-W.js} +1 -1
  5. package/web/assets/{index-J2jvoYvL.js → index-BI8B49yl.js} +1 -1
  6. package/web/assets/{index-Bw0U50QY.js → index-BO3Em_j_.js} +1 -1
  7. package/web/assets/{index-CmCj0IE6.js → index-BTNpZcr3.js} +1 -1
  8. package/web/assets/{index-BIhkivIg.js → index-B_KTK-Ue.js} +1 -1
  9. package/web/assets/{index-4S0cl_O2.js → index-Bjh8yEB4.js} +1 -1
  10. package/web/assets/{index-D57ZXpBD.js → index-C0igZOa9.js} +1 -1
  11. package/web/assets/{index-CZF-u-hv.js → index-C11bbOpw.js} +1 -1
  12. package/web/assets/{index-xSo7_4Td.js → index-CBTNRCgC.js} +1 -1
  13. package/web/assets/{index-09MC_4AF.js → index-CFzKsiua.js} +1 -1
  14. package/web/assets/{index-Dn5EQ5YI.js → index-CIpZI228.js} +1 -1
  15. package/web/assets/index-CWCyiPjz.js +132 -0
  16. package/web/assets/{index-Czu0PbLS.js → index-Cd-2HW_i.js} +1 -1
  17. package/web/assets/{index-BDuAzkOQ.js → index-CquqZqWN.js} +1 -1
  18. package/web/assets/{index-BSEf83Uc.js → index-CzYpffog.js} +1 -1
  19. package/web/assets/{index-DO1FY4r1.js → index-D-pPtz2I.js} +1 -1
  20. package/web/assets/{index-DdCJlB8h.js → index-DDJ5NYOk.js} +1 -1
  21. package/web/assets/{index-Bg0VD211.js → index-DPG5pWPG.js} +1 -1
  22. package/web/assets/{index-DABElhgt.js → index-DmfDW_VA.js} +1 -1
  23. package/web/assets/{index-ZstREAcb.js → index-XkZY-Wpw.js} +1 -1
  24. package/web/assets/index-s-BhjU3m.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,9 +4296,9 @@ 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;
4207
4304
  var MIN_AUTO_SAVE_AFTER_PAUSE_MS = 500;
@@ -4230,18 +4327,20 @@ var DEFAULT_APP_SETTINGS = {
4230
4327
  // Терминал по умолчанию закрыт — его включают, а не выключают: приложение,
4231
4328
  // открытое в браузере, не обязано с первого запуска уметь запускать на этой
4232
4329
  // машине что угодно.
4233
- terminalEnabled: false
4330
+ terminalEnabled: false,
4331
+ // Выключено по умолчанию: списки идут в том порядке, в каком их расставили.
4332
+ liveSessionsFirst: false
4234
4333
  };
4235
4334
  var AppSettingsStore = class {
4236
4335
  #settings = { ...DEFAULT_APP_SETTINGS };
4237
4336
  #storePath;
4238
4337
  constructor(dataDir) {
4239
- this.#storePath = path13.join(dataDir, "app-settings.json");
4338
+ this.#storePath = path14.join(dataDir, "app-settings.json");
4240
4339
  }
4241
4340
  async load() {
4242
4341
  try {
4243
- const raw = await readFile10(this.#storePath, "utf8");
4244
- this.#settings = sanitize(JSON.parse(raw), DEFAULT_APP_SETTINGS);
4342
+ const raw = await readFile11(this.#storePath, "utf8");
4343
+ this.#settings = sanitize2(JSON.parse(raw), DEFAULT_APP_SETTINGS);
4245
4344
  } catch {
4246
4345
  this.#settings = { ...DEFAULT_APP_SETTINGS };
4247
4346
  }
@@ -4258,14 +4357,14 @@ var AppSettingsStore = class {
4258
4357
  * всё равно получит в ответ то, что реально записано, и покажет именно это.
4259
4358
  */
4260
4359
  async update(patch) {
4261
- const next = sanitize(patch, this.#settings);
4360
+ const next = sanitize2(patch, this.#settings);
4262
4361
  this.#settings = next;
4263
- await mkdir7(path13.dirname(this.#storePath), { recursive: true });
4264
- await writeFile8(this.#storePath, JSON.stringify(next, null, 2), "utf8");
4362
+ await mkdir8(path14.dirname(this.#storePath), { recursive: true });
4363
+ await writeFile9(this.#storePath, JSON.stringify(next, null, 2), "utf8");
4265
4364
  return this.current;
4266
4365
  }
4267
4366
  };
4268
- function sanitize(value, fallback) {
4367
+ function sanitize2(value, fallback) {
4269
4368
  const result = { ...fallback };
4270
4369
  if (typeof value !== "object" || value === null)
4271
4370
  return result;
@@ -4296,10 +4395,13 @@ function sanitize(value, fallback) {
4296
4395
  if (typeof record.terminalEnabled === "boolean") {
4297
4396
  result.terminalEnabled = record.terminalEnabled;
4298
4397
  }
4398
+ if (typeof record.liveSessionsFirst === "boolean") {
4399
+ result.liveSessionsFirst = record.liveSessionsFirst;
4400
+ }
4299
4401
  return result;
4300
4402
  }
4301
4403
  function claudeHome() {
4302
- return path13.join(homedir3(), ".claude");
4404
+ return path14.join(homedir4(), ".claude");
4303
4405
  }
4304
4406
  function writableRoots(mode, cwd, workspacePaths) {
4305
4407
  if (mode === "all")
@@ -4309,7 +4411,7 @@ function writableRoots(mode, cwd, workspacePaths) {
4309
4411
  return [...roots];
4310
4412
  for (const workspace of [cwd, ...workspacePaths]) {
4311
4413
  roots.add(workspace);
4312
- const parent = path13.dirname(workspace);
4414
+ const parent = path14.dirname(workspace);
4313
4415
  if (parent !== workspace)
4314
4416
  roots.add(parent);
4315
4417
  }
@@ -4317,9 +4419,9 @@ function writableRoots(mode, cwd, workspacePaths) {
4317
4419
  }
4318
4420
 
4319
4421
  // ../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";
4422
+ import { readFile as readFile12 } from "node:fs/promises";
4423
+ import { homedir as homedir5 } from "node:os";
4424
+ import path15 from "node:path";
4323
4425
  var TIMEOUT_MS2 = 12e4;
4324
4426
  var SAFE_ARG = /^[A-Za-z0-9][A-Za-z0-9._@/-]*$/;
4325
4427
  var PluginCli = class {
@@ -4418,10 +4520,10 @@ async function readCatalog(markets, installed) {
4418
4520
  return lists.flat().sort((a, b) => a.name.localeCompare(b.name));
4419
4521
  }
4420
4522
  async function readMarketplace(market, installed) {
4421
- const root = market.installLocation || path14.join(homedir4(), ".claude", "plugins", "marketplaces", market.name);
4523
+ const root = market.installLocation || path15.join(homedir5(), ".claude", "plugins", "marketplaces", market.name);
4422
4524
  let manifest;
4423
4525
  try {
4424
- const text = await readFile11(path14.join(root, ".claude-plugin", "marketplace.json"), "utf8");
4526
+ const text = await readFile12(path15.join(root, ".claude-plugin", "marketplace.json"), "utf8");
4425
4527
  manifest = JSON.parse(text);
4426
4528
  } catch {
4427
4529
  return [];
@@ -4662,8 +4764,8 @@ function describe4(error) {
4662
4764
 
4663
4765
  // ../host/dist/relay-link.js
4664
4766
  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";
4767
+ import { readFile as readFile13, writeFile as writeFile10, rm as rm3, mkdir as mkdir9 } from "node:fs/promises";
4768
+ import path16 from "node:path";
4667
4769
  import { hostname } from "node:os";
4668
4770
  import WebSocket from "ws";
4669
4771
 
@@ -4718,7 +4820,7 @@ var RelayLink = class extends EventEmitter3 {
4718
4820
  #log;
4719
4821
  constructor(options2) {
4720
4822
  super();
4721
- this.#file = path15.join(options2.dataDir, "relay.json");
4823
+ this.#file = path16.join(options2.dataDir, "relay.json");
4722
4824
  this.#defaultUrl = options2.defaultUrl.replace(/\/+$/, "");
4723
4825
  this.#log = options2.log ?? ((msg) => console.log(`[relay] ${msg}`));
4724
4826
  }
@@ -4799,7 +4901,7 @@ var RelayLink = class extends EventEmitter3 {
4799
4901
  }
4800
4902
  async #read() {
4801
4903
  try {
4802
- const raw = await readFile12(this.#file, "utf8");
4904
+ const raw = await readFile13(this.#file, "utf8");
4803
4905
  const parsed = JSON.parse(raw);
4804
4906
  if (typeof parsed !== "object" || parsed === null)
4805
4907
  return null;
@@ -4812,8 +4914,8 @@ var RelayLink = class extends EventEmitter3 {
4812
4914
  }
4813
4915
  }
4814
4916
  async #write(credentials) {
4815
- await mkdir8(path15.dirname(this.#file), { recursive: true });
4816
- await writeFile9(this.#file, `${JSON.stringify(credentials, null, 2)}
4917
+ await mkdir9(path16.dirname(this.#file), { recursive: true });
4918
+ await writeFile10(this.#file, `${JSON.stringify(credentials, null, 2)}
4817
4919
  `, {
4818
4920
  encoding: "utf8",
4819
4921
  mode: 384
@@ -5070,7 +5172,7 @@ function humanize(error) {
5070
5172
  // ../host/dist/terminal.js
5071
5173
  import { EventEmitter as EventEmitter4 } from "node:events";
5072
5174
  import { access as access2, constants as constants2 } from "node:fs/promises";
5073
- import path16 from "node:path";
5175
+ import path17 from "node:path";
5074
5176
  import { randomUUID as randomUUID3 } from "node:crypto";
5075
5177
  var SCROLLBACK_BYTES = 256 * 1024;
5076
5178
  var FLUSH_MS = 12;
@@ -5240,22 +5342,22 @@ async function defaultShell() {
5240
5342
  if (pwsh)
5241
5343
  return { file: pwsh, args: [], title: "pwsh" };
5242
5344
  const system = process.env.SystemRoot ?? "C:\\Windows";
5243
- const powershell = path16.join(system, "System32/WindowsPowerShell/v1.0/powershell.exe");
5345
+ const powershell = path17.join(system, "System32/WindowsPowerShell/v1.0/powershell.exe");
5244
5346
  if (await exists2(powershell))
5245
5347
  return { file: powershell, args: [], title: "powershell" };
5246
5348
  return { file: process.env.COMSPEC ?? "cmd.exe", args: [], title: "cmd" };
5247
5349
  }
5248
5350
  const shell = process.env.SHELL;
5249
5351
  if (shell && await exists2(shell))
5250
- return { file: shell, args: [], title: path16.basename(shell) };
5352
+ return { file: shell, args: [], title: path17.basename(shell) };
5251
5353
  if (await exists2("/bin/bash"))
5252
5354
  return { file: "/bin/bash", args: [], title: "bash" };
5253
5355
  return { file: "/bin/sh", args: [], title: "sh" };
5254
5356
  }
5255
5357
  async function findOnPath(name) {
5256
- const dirs = (process.env.PATH ?? "").split(path16.delimiter).filter(Boolean);
5358
+ const dirs = (process.env.PATH ?? "").split(path17.delimiter).filter(Boolean);
5257
5359
  for (const dir of dirs) {
5258
- const candidate = path16.join(dir, name);
5360
+ const candidate = path17.join(dir, name);
5259
5361
  if (await exists2(candidate))
5260
5362
  return candidate;
5261
5363
  }
@@ -5266,10 +5368,10 @@ function exists2(file) {
5266
5368
  }
5267
5369
 
5268
5370
  // ../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";
5371
+ import { readFile as readFile14, writeFile as writeFile11, mkdir as mkdir10 } from "node:fs/promises";
5372
+ import path18 from "node:path";
5271
5373
  var MCP_FILE = ".mcp.json";
5272
- var LOCAL_SETTINGS2 = path17.join(".claude", "settings.local.json");
5374
+ var LOCAL_SETTINGS2 = path18.join(".claude", "settings.local.json");
5273
5375
  var queues = /* @__PURE__ */ new Map();
5274
5376
  function serialize(cwd, work) {
5275
5377
  const previous = queues.get(cwd) ?? Promise.resolve();
@@ -5278,7 +5380,7 @@ function serialize(cwd, work) {
5278
5380
  return next;
5279
5381
  }
5280
5382
  async function readProjectServers(cwd) {
5281
- const file = await readJson(path17.join(cwd, MCP_FILE));
5383
+ const file = await readJson(path18.join(cwd, MCP_FILE));
5282
5384
  const servers = file?.mcpServers;
5283
5385
  if (!servers || typeof servers !== "object")
5284
5386
  return {};
@@ -5286,23 +5388,23 @@ async function readProjectServers(cwd) {
5286
5388
  }
5287
5389
  function addProjectServer(cwd, name, config) {
5288
5390
  return serialize(cwd, async () => {
5289
- const file = await readJson(path17.join(cwd, MCP_FILE)) ?? {};
5391
+ const file = await readJson(path18.join(cwd, MCP_FILE)) ?? {};
5290
5392
  const servers = file.mcpServers ?? {};
5291
5393
  servers[name] = config;
5292
5394
  file.mcpServers = servers;
5293
- await writeJson(path17.join(cwd, MCP_FILE), file);
5395
+ await writeJson(path18.join(cwd, MCP_FILE), file);
5294
5396
  await approve(cwd, name);
5295
5397
  });
5296
5398
  }
5297
5399
  function removeProjectServer(cwd, name) {
5298
5400
  return serialize(cwd, async () => {
5299
- const file = await readJson(path17.join(cwd, MCP_FILE));
5401
+ const file = await readJson(path18.join(cwd, MCP_FILE));
5300
5402
  const servers = file?.mcpServers;
5301
5403
  if (!servers || !(name in servers))
5302
5404
  return false;
5303
5405
  delete servers[name];
5304
- await writeJson(path17.join(cwd, MCP_FILE), file);
5305
- const settingsPath = path17.join(cwd, LOCAL_SETTINGS2);
5406
+ await writeJson(path18.join(cwd, MCP_FILE), file);
5407
+ const settingsPath = path18.join(cwd, LOCAL_SETTINGS2);
5306
5408
  const settings = await readJson(settingsPath);
5307
5409
  const enabled = settings?.enabledMcpjsonServers;
5308
5410
  if (settings && Array.isArray(enabled) && enabled.includes(name)) {
@@ -5313,7 +5415,7 @@ function removeProjectServer(cwd, name) {
5313
5415
  });
5314
5416
  }
5315
5417
  async function approve(cwd, name) {
5316
- const file = path17.join(cwd, LOCAL_SETTINGS2);
5418
+ const file = path18.join(cwd, LOCAL_SETTINGS2);
5317
5419
  const settings = await readJson(file) ?? {};
5318
5420
  const enabled = Array.isArray(settings.enabledMcpjsonServers) ? settings.enabledMcpjsonServers : [];
5319
5421
  if (!enabled.includes(name))
@@ -5352,21 +5454,21 @@ function describeConfig(config) {
5352
5454
  }
5353
5455
  async function readJson(file) {
5354
5456
  try {
5355
- const raw = await readFile13(file, "utf8");
5457
+ const raw = await readFile14(file, "utf8");
5356
5458
  const parsed = JSON.parse(raw);
5357
5459
  return parsed && typeof parsed === "object" ? parsed : null;
5358
5460
  } catch (error) {
5359
5461
  if (error.code === "ENOENT")
5360
5462
  return null;
5361
5463
  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}`);
5464
+ 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
5465
  }
5364
5466
  throw error;
5365
5467
  }
5366
5468
  }
5367
5469
  async function writeJson(file, data) {
5368
- await mkdir9(path17.dirname(file), { recursive: true });
5369
- await writeFile10(file, `${JSON.stringify(data, null, 2)}
5470
+ await mkdir10(path18.dirname(file), { recursive: true });
5471
+ await writeFile11(file, `${JSON.stringify(data, null, 2)}
5370
5472
  `, "utf8");
5371
5473
  }
5372
5474
 
@@ -5380,6 +5482,7 @@ var HostServer = class {
5380
5482
  #workspaces;
5381
5483
  #archive;
5382
5484
  #pins;
5485
+ #order;
5383
5486
  #folders;
5384
5487
  #settings;
5385
5488
  #finder;
@@ -5418,6 +5521,7 @@ var HostServer = class {
5418
5521
  this.#workspaces = new WorkspaceRegistry(options2.dataDir);
5419
5522
  this.#archive = new SessionArchive(options2.dataDir);
5420
5523
  this.#pins = new SessionPins(options2.dataDir);
5524
+ this.#order = new ListOrderStore(options2.dataDir);
5421
5525
  this.#folders = new Folders(options2.dataDir);
5422
5526
  this.#settings = new AppSettingsStore(options2.dataDir);
5423
5527
  this.#finder = new ProjectFinder(this.#workspaces);
@@ -5436,6 +5540,7 @@ var HostServer = class {
5436
5540
  await this.#workspaces.load();
5437
5541
  await this.#archive.load();
5438
5542
  await this.#pins.load();
5543
+ await this.#order.load();
5439
5544
  await this.#folders.load();
5440
5545
  await this.#settings.load();
5441
5546
  await this.#github.load();
@@ -5619,6 +5724,8 @@ var HostServer = class {
5619
5724
  });
5620
5725
  this.#send(socket, { type: "workspace.list", ...this.#workspaceLists() });
5621
5726
  this.#sendFolders(socket);
5727
+ this.#send(socket, { type: "order.state", order: this.#order.current });
5728
+ this.#sendLive(socket);
5622
5729
  this.#auth.status().then((status) => this.#send(socket, { type: "auth.status", status })).catch(() => void 0);
5623
5730
  this.#send(socket, { type: "relay.state", state: this.#relay.state });
5624
5731
  return;
@@ -5734,6 +5841,13 @@ var HostServer = class {
5734
5841
  case "session.pins":
5735
5842
  await this.#sendPinned();
5736
5843
  return;
5844
+ case "order.get":
5845
+ this.#send(socket, { type: "order.state", order: this.#order.current });
5846
+ return;
5847
+ case "order.set":
5848
+ await this.#order.set(frame.scope, frame.ids, frame.workspaceId);
5849
+ this.#broadcast({ type: "order.state", order: this.#order.current });
5850
+ return;
5737
5851
  case "folder.create": {
5738
5852
  if (frame.scope === "session") {
5739
5853
  if (!frame.workspaceId)
@@ -6375,8 +6489,23 @@ var HostServer = class {
6375
6489
  await session.close(reason);
6376
6490
  this.#sessions.delete(sessionId);
6377
6491
  this.#broadcast({ type: "session.closed", sessionId, reason });
6492
+ this.#sendLive();
6378
6493
  return true;
6379
6494
  }
6495
+ /**
6496
+ * Живые сессии всех проектов — вкладке запущенных.
6497
+ *
6498
+ * Без `socket` — всем окнам: состав живых поменялся. Боковые сессии и сессия
6499
+ * поиска проекта в список не идут, их пользователь не заводил.
6500
+ */
6501
+ #sendLive(socket) {
6502
+ const sessions = [...this.#sessions.values()].filter((s) => s !== this.#finderSession).map((s) => s.summary()).filter((s) => !s.parentSessionId);
6503
+ const frame = { type: "session.live", sessions };
6504
+ if (socket)
6505
+ this.#send(socket, frame);
6506
+ else
6507
+ this.#broadcast(frame);
6508
+ }
6380
6509
  /**
6381
6510
  * Разослать список сессий проекта всем окнам.
6382
6511
  *
@@ -6438,6 +6567,7 @@ var HostServer = class {
6438
6567
  this.#wire(session);
6439
6568
  this.#sessions.set(session.id, session);
6440
6569
  session.start();
6570
+ this.#sendLive();
6441
6571
  this.#send(socket, { type: "session.summary", session: session.summary() });
6442
6572
  }
6443
6573
  /**
@@ -6551,7 +6681,7 @@ var HostServer = class {
6551
6681
  */
6552
6682
  #roots() {
6553
6683
  const roots = this.#finder.roots();
6554
- return roots.length > 0 ? roots : [homedir5()];
6684
+ return roots.length > 0 ? roots : [homedir6()];
6555
6685
  }
6556
6686
  /**
6557
6687
  * Где сессии этого каталога разрешено писать без вопроса.
@@ -6884,15 +7014,15 @@ var HostServer = class {
6884
7014
  }
6885
7015
  #serveStatic(req, res) {
6886
7016
  const webRoot = this.#options.webRoot;
6887
- if (!webRoot || !existsSync3(webRoot)) {
7017
+ if (!webRoot || !existsSync4(webRoot)) {
6888
7018
  res.writeHead(200, { "content-type": "text/plain; charset=utf-8" });
6889
7019
  res.end("CCV host is running. UI is served by the Vite dev server.\n");
6890
7020
  return;
6891
7021
  }
6892
7022
  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)) {
7023
+ const requested = path19.join(webRoot, path19.normalize(url2.pathname));
7024
+ const target = requested.startsWith(webRoot) && existsSync4(requested) && statSync3(requested).isFile() ? requested : path19.join(webRoot, "index.html");
7025
+ if (!existsSync4(target)) {
6896
7026
  res.writeHead(404).end("not found");
6897
7027
  return;
6898
7028
  }
@@ -6917,12 +7047,12 @@ function contentType(file) {
6917
7047
  ".svg": "image/svg+xml",
6918
7048
  ".woff2": "font/woff2"
6919
7049
  };
6920
- return types[path18.extname(file)] ?? "application/octet-stream";
7050
+ return types[path19.extname(file)] ?? "application/octet-stream";
6921
7051
  }
6922
7052
 
6923
7053
  // dist/ccv.js
6924
- var HERE = path19.dirname(fileURLToPath(import.meta.url));
6925
- var PACKAGE_ROOT = path19.resolve(HERE, "..");
7054
+ var HERE = path20.dirname(fileURLToPath(import.meta.url));
7055
+ var PACKAGE_ROOT = path20.resolve(HERE, "..");
6926
7056
  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
7057
 
6928
7058
  \u0418\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u0435:
@@ -6989,7 +7119,7 @@ var server = new HostServer({
6989
7119
  dataDir: defaults.dataDir,
6990
7120
  // UI лежит в пакете рядом с бандлом — это и есть разница между установленной
6991
7121
  // утилитой и запуском из репозитория, где сборка живёт в `apps/web/dist`.
6992
- webRoot: process.env.CCV_WEB_ROOT ?? path19.join(PACKAGE_ROOT, "web"),
7122
+ webRoot: process.env.CCV_WEB_ROOT ?? path20.join(PACKAGE_ROOT, "web"),
6993
7123
  version,
6994
7124
  relayUrl: defaults.relayUrl
6995
7125
  });
@@ -7018,7 +7148,7 @@ for (const signal of ["SIGINT", "SIGTERM"]) {
7018
7148
  }
7019
7149
  async function packageVersion() {
7020
7150
  try {
7021
- const text = await readFile14(path19.join(PACKAGE_ROOT, "package.json"), "utf8");
7151
+ const text = await readFile15(path20.join(PACKAGE_ROOT, "package.json"), "utf8");
7022
7152
  return JSON.parse(text).version ?? "0.0.0";
7023
7153
  } catch {
7024
7154
  return "0.0.0";