coding-tool-x 3.5.0 → 3.5.2

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.
@@ -5,7 +5,7 @@
5
5
  <link rel="icon" href="/favicon.ico">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <title>CC-TOOL - ClaudeCode增强工作助手</title>
8
- <script type="module" crossorigin src="/assets/index-EMrm1wk-.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-B1ujw2sM.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/markdown-DyTJGI4N.js">
10
10
  <link rel="modulepreload" crossorigin href="/assets/vue-vendor-3bf-fPGP.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/vendors-CKPV1OAU.js">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coding-tool-x",
3
- "version": "3.5.0",
3
+ "version": "3.5.2",
4
4
  "description": "Vibe Coding 增强工作助手 - 智能会话管理、动态渠道切换、全局搜索、实时监控",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -3,7 +3,13 @@ const path = require('path');
3
3
  const chalk = require('chalk');
4
4
  const { loadConfig } = require('../config/loader');
5
5
  const { PATHS, ensureStorageDirMigrated } = require('../config/paths');
6
- const { findProcessByPort, getPortToolIssue, formatPortToolIssue } = require('../utils/port-helper');
6
+ const {
7
+ findProcessByPort,
8
+ killProcessByPort,
9
+ waitForPortRelease,
10
+ getPortToolIssue,
11
+ formatPortToolIssue
12
+ } = require('../utils/port-helper');
7
13
 
8
14
  const PM2_APP_NAME = 'cc-tool';
9
15
 
@@ -83,6 +89,50 @@ function shouldTreatPortOwnershipAsReady(ownsPort) {
83
89
  return ownsPort === true || ownsPort === null;
84
90
  }
85
91
 
92
+ function getManagedPorts(config = loadConfig()) {
93
+ return [
94
+ config.ports?.webUI || 19999,
95
+ config.ports?.proxy || 20088,
96
+ config.ports?.codexProxy || 20089,
97
+ config.ports?.geminiProxy || 20090,
98
+ config.ports?.opencodeProxy || 20091
99
+ ].filter((port, index, list) => Number.isInteger(port) && port > 0 && list.indexOf(port) === index);
100
+ }
101
+
102
+ async function cleanupManagedPorts(config = loadConfig(), options = {}) {
103
+ const timeoutMs = Number.isFinite(options.timeoutMs) ? options.timeoutMs : 3000;
104
+ const ports = getManagedPorts(config);
105
+ const released = [];
106
+ const forced = [];
107
+ const stillInUse = [];
108
+
109
+ for (const port of ports) {
110
+ if (await waitForPortRelease(port, timeoutMs)) {
111
+ released.push(port);
112
+ continue;
113
+ }
114
+
115
+ const killed = killProcessByPort(port);
116
+ if (killed) {
117
+ forced.push(port);
118
+ if (await waitForPortRelease(port, timeoutMs)) {
119
+ released.push(port);
120
+ continue;
121
+ }
122
+ }
123
+
124
+ stillInUse.push(port);
125
+ }
126
+
127
+ return {
128
+ ports,
129
+ released,
130
+ forced,
131
+ stillInUse,
132
+ toolIssue: getPortToolIssue()
133
+ };
134
+ }
135
+
86
136
  async function waitForServiceReady(port, timeoutMs = 15000, intervalMs = 500) {
87
137
  const startAt = Date.now();
88
138
  let lastProcess = null;
@@ -232,10 +282,21 @@ async function handleStart() {
232
282
  async function handleStop() {
233
283
  try {
234
284
  await connectPM2();
285
+ const config = loadConfig();
235
286
 
236
287
  const existing = await getCCToolProcess();
237
288
  if (!existing) {
289
+ const cleanup = await cleanupManagedPorts(config, { timeoutMs: 3000 });
238
290
  console.log(chalk.yellow('\n[WARN] 服务未在运行\n'));
291
+
292
+ if (cleanup.forced.length > 0) {
293
+ console.log(chalk.yellow(`[WARN] 已额外清理残留端口: ${cleanup.forced.join(', ')}`));
294
+ }
295
+ if (cleanup.stillInUse.length > 0) {
296
+ console.log(chalk.red(`[ERROR] 以下端口仍被占用: ${cleanup.stillInUse.join(', ')}`));
297
+ printPortToolIssue(cleanup.toolIssue);
298
+ console.log(chalk.yellow('[TIP] 请检查是否有外部进程仍占用这些端口\n'));
299
+ }
239
300
  disconnectPM2();
240
301
  return;
241
302
  }
@@ -248,11 +309,21 @@ async function handleStop() {
248
309
  }
249
310
 
250
311
  // 删除进程
251
- pm2.delete(PM2_APP_NAME, (err) => {
312
+ pm2.delete(PM2_APP_NAME, async (err) => {
252
313
  if (err) {
253
314
  console.error(chalk.red('删除进程失败:'), err.message);
254
315
  } else {
316
+ const cleanup = await cleanupManagedPorts(config, { timeoutMs: 3000 });
255
317
  console.log(chalk.green('\n[OK] Coding-Tool 服务已停止\n'));
318
+
319
+ if (cleanup.forced.length > 0) {
320
+ console.log(chalk.yellow(`[WARN] 已额外清理残留端口: ${cleanup.forced.join(', ')}`));
321
+ }
322
+ if (cleanup.stillInUse.length > 0) {
323
+ console.log(chalk.red(`[ERROR] 以下端口仍被占用: ${cleanup.stillInUse.join(', ')}`));
324
+ printPortToolIssue(cleanup.toolIssue);
325
+ console.log(chalk.yellow('[TIP] 请检查是否有外部进程仍占用这些端口\n'));
326
+ }
256
327
  }
257
328
 
258
329
  pm2.dump((err) => {
@@ -399,6 +470,7 @@ module.exports = {
399
470
  handleRestart,
400
471
  handleStatus,
401
472
  _test: {
402
- shouldTreatPortOwnershipAsReady
473
+ shouldTreatPortOwnershipAsReady,
474
+ getManagedPorts
403
475
  }
404
476
  };
@@ -356,6 +356,60 @@ module.exports = (config) => {
356
356
  }
357
357
  });
358
358
 
359
+ /**
360
+ * POST /api/codex/sessions/:projectName/batch-delete
361
+ * 批量删除会话
362
+ */
363
+ router.post('/:projectName/batch-delete', (req, res) => {
364
+ try {
365
+ if (!isCodexInstalled()) {
366
+ return res.status(404).json({ error: 'Codex CLI not installed' });
367
+ }
368
+
369
+ const { sessionIds } = req.body || {};
370
+ if (!Array.isArray(sessionIds) || sessionIds.length === 0) {
371
+ return res.status(400).json({ error: 'sessionIds must be a non-empty array' });
372
+ }
373
+
374
+ const uniqueSessionIds = Array.from(new Set(
375
+ sessionIds
376
+ .filter(sessionId => typeof sessionId === 'string')
377
+ .map(sessionId => sessionId.trim())
378
+ .filter(Boolean)
379
+ ));
380
+
381
+ if (uniqueSessionIds.length === 0) {
382
+ return res.status(400).json({ error: 'sessionIds must be a non-empty array' });
383
+ }
384
+
385
+ const deletedSessionIds = [];
386
+ const failed = [];
387
+
388
+ uniqueSessionIds.forEach((sessionId) => {
389
+ try {
390
+ deleteSession(sessionId);
391
+ deletedSessionIds.push(sessionId);
392
+ } catch (err) {
393
+ failed.push({
394
+ sessionId,
395
+ error: err.message
396
+ });
397
+ }
398
+ });
399
+
400
+ res.json({
401
+ success: failed.length === 0,
402
+ requestedCount: uniqueSessionIds.length,
403
+ deletedCount: deletedSessionIds.length,
404
+ deletedSessionIds,
405
+ failed
406
+ });
407
+ } catch (err) {
408
+ console.error('[Codex API] Failed to batch delete sessions:', err);
409
+ res.status(500).json({ error: err.message });
410
+ }
411
+ });
412
+
359
413
  /**
360
414
  * POST /api/codex/sessions/:projectName/:sessionId/fork
361
415
  * Fork 一个会话
@@ -256,6 +256,60 @@ module.exports = (config) => {
256
256
  }
257
257
  });
258
258
 
259
+ /**
260
+ * POST /api/gemini/sessions/:projectHash/batch-delete
261
+ * 批量删除会话
262
+ */
263
+ router.post('/:projectHash/batch-delete', (req, res) => {
264
+ try {
265
+ if (!isGeminiInstalled()) {
266
+ return res.status(404).json({ error: 'Gemini CLI not installed' });
267
+ }
268
+
269
+ const { sessionIds } = req.body || {};
270
+ if (!Array.isArray(sessionIds) || sessionIds.length === 0) {
271
+ return res.status(400).json({ error: 'sessionIds must be a non-empty array' });
272
+ }
273
+
274
+ const uniqueSessionIds = Array.from(new Set(
275
+ sessionIds
276
+ .filter(sessionId => typeof sessionId === 'string')
277
+ .map(sessionId => sessionId.trim())
278
+ .filter(Boolean)
279
+ ));
280
+
281
+ if (uniqueSessionIds.length === 0) {
282
+ return res.status(400).json({ error: 'sessionIds must be a non-empty array' });
283
+ }
284
+
285
+ const deletedSessionIds = [];
286
+ const failed = [];
287
+
288
+ uniqueSessionIds.forEach((sessionId) => {
289
+ try {
290
+ deleteSession(sessionId);
291
+ deletedSessionIds.push(sessionId);
292
+ } catch (err) {
293
+ failed.push({
294
+ sessionId,
295
+ error: err.message
296
+ });
297
+ }
298
+ });
299
+
300
+ res.json({
301
+ success: failed.length === 0,
302
+ requestedCount: uniqueSessionIds.length,
303
+ deletedCount: deletedSessionIds.length,
304
+ deletedSessionIds,
305
+ failed
306
+ });
307
+ } catch (err) {
308
+ console.error('[Gemini API] Failed to batch delete sessions:', err);
309
+ res.status(500).json({ error: err.message });
310
+ }
311
+ });
312
+
259
313
  /**
260
314
  * POST /api/gemini/sessions/:projectHash/:sessionId/fork
261
315
  * Fork 一个会话
@@ -228,6 +228,60 @@ module.exports = (config) => {
228
228
  }
229
229
  });
230
230
 
231
+ /**
232
+ * POST /api/opencode/sessions/:projectName/batch-delete
233
+ * 批量删除会话
234
+ */
235
+ router.post('/:projectName/batch-delete', (req, res) => {
236
+ try {
237
+ if (!isOpenCodeInstalled()) {
238
+ return res.status(404).json({ error: 'OpenCode CLI not installed' });
239
+ }
240
+
241
+ const { sessionIds } = req.body || {};
242
+ if (!Array.isArray(sessionIds) || sessionIds.length === 0) {
243
+ return res.status(400).json({ error: 'sessionIds must be a non-empty array' });
244
+ }
245
+
246
+ const uniqueSessionIds = Array.from(new Set(
247
+ sessionIds
248
+ .filter(sessionId => typeof sessionId === 'string')
249
+ .map(sessionId => sessionId.trim())
250
+ .filter(Boolean)
251
+ ));
252
+
253
+ if (uniqueSessionIds.length === 0) {
254
+ return res.status(400).json({ error: 'sessionIds must be a non-empty array' });
255
+ }
256
+
257
+ const deletedSessionIds = [];
258
+ const failed = [];
259
+
260
+ uniqueSessionIds.forEach((sessionId) => {
261
+ try {
262
+ deleteSession(sessionId);
263
+ deletedSessionIds.push(sessionId);
264
+ } catch (err) {
265
+ failed.push({
266
+ sessionId,
267
+ error: err.message
268
+ });
269
+ }
270
+ });
271
+
272
+ res.json({
273
+ success: failed.length === 0,
274
+ requestedCount: uniqueSessionIds.length,
275
+ deletedCount: deletedSessionIds.length,
276
+ deletedSessionIds,
277
+ failed
278
+ });
279
+ } catch (err) {
280
+ console.error('[OpenCode API] Failed to batch delete sessions:', err);
281
+ res.status(500).json({ error: err.message });
282
+ }
283
+ });
284
+
231
285
  /**
232
286
  * POST /api/opencode/sessions/:projectName/:sessionId/fork
233
287
  * Fork 一个会话
@@ -83,6 +83,55 @@ module.exports = (config) => {
83
83
  }
84
84
  });
85
85
 
86
+ // POST /api/sessions/:projectName/batch-delete - Delete multiple sessions
87
+ router.post('/:projectName/batch-delete', (req, res) => {
88
+ try {
89
+ const { projectName } = req.params;
90
+ const { sessionIds } = req.body || {};
91
+
92
+ if (!Array.isArray(sessionIds) || sessionIds.length === 0) {
93
+ return res.status(400).json({ error: 'sessionIds must be a non-empty array' });
94
+ }
95
+
96
+ const uniqueSessionIds = Array.from(new Set(
97
+ sessionIds
98
+ .filter(sessionId => typeof sessionId === 'string')
99
+ .map(sessionId => sessionId.trim())
100
+ .filter(Boolean)
101
+ ));
102
+
103
+ if (uniqueSessionIds.length === 0) {
104
+ return res.status(400).json({ error: 'sessionIds must be a non-empty array' });
105
+ }
106
+
107
+ const deletedSessionIds = [];
108
+ const failed = [];
109
+
110
+ uniqueSessionIds.forEach((sessionId) => {
111
+ try {
112
+ deleteSession(config, projectName, sessionId);
113
+ deletedSessionIds.push(sessionId);
114
+ } catch (error) {
115
+ failed.push({
116
+ sessionId,
117
+ error: error.message
118
+ });
119
+ }
120
+ });
121
+
122
+ res.json({
123
+ success: failed.length === 0,
124
+ requestedCount: uniqueSessionIds.length,
125
+ deletedCount: deletedSessionIds.length,
126
+ deletedSessionIds,
127
+ failed
128
+ });
129
+ } catch (error) {
130
+ console.error('Error batch deleting sessions:', error);
131
+ res.status(500).json({ error: error.message });
132
+ }
133
+ });
134
+
86
135
  // POST /api/sessions/:projectName/:sessionId/fork - Fork a session
87
136
  router.post('/:projectName/:sessionId/fork', (req, res) => {
88
137
  try {
@@ -37,6 +37,33 @@ function hasBackup() {
37
37
  return fs.existsSync(getConfigBackupPath()) || fs.existsSync(getAuthBackupPath());
38
38
  }
39
39
 
40
+ function isRecoverableEnvSyncError(err) {
41
+ const message = String(err?.message || '');
42
+ return err?.code === 'ETIMEDOUT' ||
43
+ /timed out|spawnSync (?:powershell|pwsh)|No PowerShell executable available/i.test(message);
44
+ }
45
+
46
+ function trySyncCodexUserEnvironment(envMap, options) {
47
+ try {
48
+ return {
49
+ success: true,
50
+ result: syncCodexUserEnvironment(envMap, options),
51
+ warning: null
52
+ };
53
+ } catch (err) {
54
+ if (!isRecoverableEnvSyncError(err)) {
55
+ throw err;
56
+ }
57
+
58
+ console.warn(`[Codex Settings] 跳过持久化环境变量同步: ${err.message}`);
59
+ return {
60
+ success: false,
61
+ result: null,
62
+ warning: err.message
63
+ };
64
+ }
65
+ }
66
+
40
67
 
41
68
  // 读取 config.toml
42
69
  function readConfig() {
@@ -191,7 +218,7 @@ function restoreSettings() {
191
218
  fs.unlinkSync(getAuthBackupPath());
192
219
  }
193
220
 
194
- syncCodexUserEnvironment({}, {
221
+ const envSync = trySyncCodexUserEnvironment({}, {
195
222
  replace: false,
196
223
  removeKeys: ['CC_PROXY_KEY']
197
224
  });
@@ -200,7 +227,11 @@ function restoreSettings() {
200
227
  delete process.env.CC_PROXY_KEY;
201
228
 
202
229
  console.log('Codex settings restored from backup');
203
- return { success: true };
230
+ const result = { success: true };
231
+ if (envSync.warning) {
232
+ result.envSyncWarning = envSync.warning;
233
+ }
234
+ return result;
204
235
  } catch (err) {
205
236
  throw new Error('Failed to restore settings: ' + err.message);
206
237
  }
@@ -246,22 +277,27 @@ function setProxyConfig(proxyPort) {
246
277
  // 直接设置 process.env 确保从本进程派生的 Codex CLI 能读到 CC_PROXY_KEY
247
278
  process.env.CC_PROXY_KEY = 'PROXY_KEY';
248
279
 
249
- const envResult = syncCodexUserEnvironment({
280
+ const envSync = trySyncCodexUserEnvironment({
250
281
  CC_PROXY_KEY: 'PROXY_KEY'
251
282
  }, {
252
283
  replace: false
253
284
  });
285
+ const envResult = envSync.result;
254
286
 
255
287
  console.log(`Codex settings updated to use proxy on port ${proxyPort}`);
256
- return {
288
+ const result = {
257
289
  success: true,
258
290
  port: proxyPort,
259
- envInjected: true,
260
- isFirstTime: envResult.isFirstTime,
261
- shellConfigPath: envResult.shellConfigPath,
262
- sourceCommand: envResult.sourceCommand,
263
- reloadRequired: envResult.reloadRequired
291
+ envInjected: envSync.success,
292
+ isFirstTime: envResult?.isFirstTime || false,
293
+ shellConfigPath: envResult?.shellConfigPath || null,
294
+ sourceCommand: envResult?.sourceCommand || null,
295
+ reloadRequired: envResult?.reloadRequired || false
264
296
  };
297
+ if (envSync.warning) {
298
+ result.envSyncWarning = envSync.warning;
299
+ }
300
+ return result;
265
301
  } catch (err) {
266
302
  throw new Error('Failed to set proxy config: ' + err.message);
267
303
  }
@@ -335,4 +371,7 @@ module.exports = {
335
371
  setProxyConfig,
336
372
  isProxyConfig,
337
373
  getCurrentProxyPort,
374
+ _test: {
375
+ isRecoverableEnvSyncError
376
+ }
338
377
  };
@@ -1 +0,0 @@
1
- .session-list-container[data-v-05ca5429]{width:100%;height:100%;display:flex;flex-direction:column;box-sizing:border-box}.header[data-v-05ca5429]{flex-shrink:0;padding:24px 24px 16px;background:var(--bg-primary);border-bottom:1px solid var(--border-primary)}.content[data-v-05ca5429]{flex:1;overflow-y:auto;overflow-x:hidden;padding:16px 24px 24px}.sessions-list[data-v-05ca5429]{position:relative;overflow:hidden}.back-button[data-v-05ca5429]{flex-shrink:0;margin-right:12px}.title-bar[data-v-05ca5429]{display:flex;align-items:center;gap:16px}.title-section[data-v-05ca5429]{flex:1;min-width:0}.title-with-count[data-v-05ca5429]{display:flex;align-items:baseline;gap:8px;margin-bottom:2px}.title-section h2[data-v-05ca5429]{margin:0;font-size:20px}.session-count[data-v-05ca5429]{font-size:14px;color:#666}.total-size-tag[data-v-05ca5429]{margin-left:8px}.project-path[data-v-05ca5429]{font-size:13px;display:block;color:#666;margin-bottom:2px}.search-input[data-v-05ca5429]{width:320px;flex-shrink:0}.loading-container[data-v-05ca5429]{display:flex;justify-content:center;align-items:center;min-height:400px}.session-item[data-v-05ca5429]{display:flex;align-items:center;gap:12px;padding:16px;background:var(--bg-primary);border:1px solid var(--border-primary);border-radius:8px;margin-bottom:8px;transition:all .2s;cursor:pointer}.session-item[data-v-05ca5429]:hover{border-color:#18a058;box-shadow:0 2px 8px #18a0581a}.drag-handle[data-v-05ca5429]{cursor:move;width:24px;height:24px;padding:4px;opacity:.4;transition:all .2s;flex-shrink:0;display:flex;align-items:center;justify-content:center}.session-item:hover .drag-handle[data-v-05ca5429]{opacity:1;background-color:#18a0581a;border-radius:4px}.session-left[data-v-05ca5429]{flex:1;display:flex;align-items:center;gap:16px;min-width:0}.session-icon[data-v-05ca5429]{flex-shrink:0}.session-info[data-v-05ca5429]{flex:1;min-width:0}.session-header[data-v-05ca5429]{display:flex;align-items:center;margin-bottom:6px}.session-title-row[data-v-05ca5429]{display:flex;align-items:center;gap:8px}.session-title[data-v-05ca5429]{font-size:15px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-shrink:1;min-width:0}.session-meta[data-v-05ca5429]{display:flex;align-items:center;gap:8px;margin-bottom:6px;font-size:13px}.session-message[data-v-05ca5429]{display:block;max-width:600px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px}.session-message-empty[data-v-05ca5429]{font-style:italic;opacity:.5}.session-right[data-v-05ca5429]{display:flex;flex-direction:column;justify-content:flex-start;align-items:flex-end;min-width:280px;flex-shrink:0;gap:12px}.session-tags-area[data-v-05ca5429]{min-height:24px;display:flex;align-items:flex-start;justify-content:flex-end}.session-actions[data-v-05ca5429]{display:flex;align-items:center;margin-top:auto}.ghost[data-v-05ca5429]{opacity:.4}.chosen[data-v-05ca5429]{box-shadow:0 4px 16px #00000026}.search-result-item[data-v-05ca5429]{margin-bottom:16px;padding:12px;border:1px solid var(--border-primary);border-radius:6px;background:var(--bg-elevated)}.search-result-header[data-v-05ca5429]{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.search-result-title[data-v-05ca5429]{display:flex;align-items:center;gap:8px}.search-match[data-v-05ca5429]{display:flex;align-items:flex-start;gap:8px;margin-top:6px;padding:6px;background:var(--bg-secondary);border-radius:4px}.search-match-text[data-v-05ca5429]{flex:1;line-height:1.6}
@@ -1 +0,0 @@
1
- import{a8 as e,a5 as s,f as a,r as l,w as t,h as n,K as o,c as i,o as r,a as u,X as c,a0 as d,W as v,Y as f,N as p,p as m,_ as h,a6 as y,s as g,Z as k,G as _,y as w,F as j,$ as C,v as I}from"./vue-vendor-3bf-fPGP.js";import{d as b}from"./vendors-CKPV1OAU.js";import{_ as x,m as z,t as N,C as F,v as B,o as M,p as L,r as $,w as S}from"./index-EMrm1wk-.js";import{X as P,d as T,ac as U,ay as E,j as K,G as O,T as R,a2 as V,az as A,p as H,h as q,V as D}from"./icons-B5Pl4lrD.js";import{N as G,B as W,_ as X,c as Y,a as Z,q as J,g as Q,x as ee,s as se,n as ae,h as le,p as te}from"./naive-ui-Bdxp09n2.js";import"./markdown-DyTJGI4N.js";const ne={class:"session-list-container"},oe={class:"header"},ie={class:"title-bar"},re={class:"title-section"},ue={class:"title-with-count"},ce={key:0,class:"loading-container"},de=["onMouseenter","onClick"],ve={class:"drag-handle"},fe={class:"session-left"},pe={class:"session-icon"},me={class:"session-info"},he={class:"session-header"},ye={class:"session-title-row"},ge={class:"session-title"},ke={class:"session-meta"},_e={class:"session-right"},we={class:"session-tags-area"},je={class:"session-actions"},Ce={key:0,style:{"max-height":"70vh","overflow-y":"auto"}},Ie={class:"search-result-header"},be={class:"search-result-title"},xe=x({__name:"SessionList",props:{projectName:{type:String,required:!0}},setup(x){const xe=x,ze=e(),Ne=s(),Fe=z(),{addFavorite:Be,removeFavorite:Me,isFavorite:Le}=N(),$e=a(()=>Ne.meta.channel||"claude"),Se=l(xe.projectName),Pe=a(()=>Se.value||xe.projectName),Te=l(""),Ue=l(!1),Ee=l(null),Ke=l(""),Oe=l(null),Re=l([]),Ve=l(null),Ae=l(!1),He=l(null),qe=l(!1),De=l(!1),Ge=l(""),We=l(""),Xe=l(null),Ye=l(!1),Ze={group:{name:`${$e.value}-sessions`,pull:!1,put:!1},forceFallback:!0,fallbackOnBody:!1,fallbackTolerance:4,scroll:!0},Je=a(()=>{var e;return(null==(e=Fe.currentProjectInfo)?void 0:e.displayName)||xe.projectName}),Qe=a(()=>{var e;return(null==(e=Fe.currentProjectInfo)?void 0:e.fullPath)||Pe.value});async function es(){const e=await async function(){Fe.projects.length||await Fe.fetchProjects();const e=Fe.projects.find(e=>e.name===xe.projectName);if(e)return Se.value=e.name,e.name;const s=Fe.projects.find(e=>e.displayName===xe.projectName||e.fullPath===xe.projectName);return s?(Se.value=s.name,s.name!==xe.projectName&&await ze.replace({name:`${$e.value}-sessions`,params:{projectName:s.name}}),s.name):(Se.value=xe.projectName,xe.projectName)}();await Fe.fetchSessions(e)}t(()=>Fe.sessionsWithAlias,e=>{Re.value=[...e]},{immediate:!0});const ss=a(()=>{if(!Te.value)return Re.value;const e=Te.value.toLowerCase();return Re.value.filter(s=>s.sessionId.toLowerCase().includes(e)||s.alias&&s.alias.toLowerCase().includes(e)||s.firstMessage&&s.firstMessage.toLowerCase().includes(e)||s.gitBranch&&s.gitBranch.toLowerCase().includes(e))});function as(){const e=Ne.meta.channel||"claude";ze.push({name:`${e}-projects`})}async function ls(){if(Te.value){qe.value=!0;try{const e=await B(Pe.value,Te.value,35,$e.value);Ve.value=e,Ae.value=!0}catch(e){M.error("搜索失败: "+e.message)}finally{qe.value=!1}}}async function ts(){const e=Re.value.map(e=>e.sessionId);await Fe.saveSessionOrder(e)}async function ns(){if(Ee.value)try{const e=Ee.value.sessionId;Ke.value?(await Fe.setAlias(e,Ke.value),M.success("别名设置成功")):(await Fe.deleteAlias(e),M.success("别名已删除")),Ue.value=!1,Ee.value=null,Ke.value=""}catch(e){M.error("操作失败: "+e.message)}}function os(e){M.error(e)}async function is(e){try{const{copyResult:s}=await $(Pe.value,e,$e.value);if("manual"===(null==s?void 0:s.method))return void M.warning("自动复制失败,已弹出手动复制框");M.success("启动命令已复制到剪贴板")}catch(s){M.error("复制失败: "+s.message)}}async function rs(){Ye.value=!0;try{const e=$e.value||"claude",s=function(e){return"codex"===e?"codex":"gemini"===e?"gemini":"opencode"===e?null:"claude"}(e);if(!s)return void M.warning("OpenCode 暂不支持在此新建会话,请在 CLI 中手动创建");const a=await S(Pe.value,s),l=null==a?void 0:a.sessionId;if(!l)throw new Error("创建会话成功但未返回 sessionId");const{copyResult:t}=await $(Pe.value,l,e);if(await es(),"manual"===(null==t?void 0:t.method))return void M.warning("新会话已创建,自动复制失败,已弹出手动复制框");M.success("新会话已创建,启动命令已复制到剪贴板")}catch(e){M.error("创建会话失败: "+e.message)}finally{Ye.value=!1}}function us(e){if(!e)return"";return new Date(e).toLocaleString("zh-CN")}function cs(e){if(!e)return"0 B";const s=1024;return e<s?e+" B":e<s*s?(e/s).toFixed(1)+" KB":(e/s/s).toFixed(1)+" MB"}function ds(e,s){if(!s||!e)return e;const a=new RegExp(`(${s})`,"gi");return e.replace(a,'<mark style="background-color: #ffd700; padding: 2px 4px; border-radius: 2px; font-weight: 600;">$1</mark>')}function vs(e,s=80){return e?e.length>s?e.substring(0,s)+"...":e:""}return t([$e,()=>xe.projectName],([e])=>{Fe.setChannel(e),es()},{immediate:!0}),n(()=>{}),o(()=>{}),(e,s)=>(r(),i("div",ne,[u("div",oe,[u("div",ie,[c(p(W),{size:"small",onClick:as,class:"back-button"},{icon:f(()=>[c(p(G),{size:"18"},{default:f(()=>[c(p(P))]),_:1})]),_:1}),u("div",re,[u("div",ue,[c(p(X),null,{default:f(()=>[m(h(Je.value),1)]),_:1}),c(p(Y),{depth:"3",class:"session-count"},{default:f(()=>[m("("+h(p(Fe).sessions.length)+" 个对话)",1)]),_:1}),c(p(Z),{size:"small",bordered:!1,type:"info",class:"total-size-tag"},{default:f(()=>[m(h(cs(p(Fe).totalSize)),1)]),_:1}),c(p(W),{type:"primary",size:"small",loading:Ye.value,onClick:rs,style:{"margin-left":"12px"}},{icon:f(()=>[c(p(G),null,{default:f(()=>[c(p(T))]),_:1})]),default:f(()=>[s[8]||(s[8]=m(" 新建会话 ",-1))]),_:1},8,["loading"])]),c(p(Y),{depth:"3",class:"project-path"},{default:f(()=>[m(h(Qe.value),1)]),_:1})]),c(p(J),{value:Te.value,"onUpdate:value":s[0]||(s[0]=e=>Te.value=e),placeholder:"搜索会话...",clearable:"",class:"search-input",onKeyup:y(ls,["enter"]),disabled:qe.value},{prefix:f(()=>[c(p(G),null,{default:f(()=>[c(p(U))]),_:1})]),suffix:f(()=>[c(p(W),{text:"",onClick:ls,disabled:!Te.value||qe.value,loading:qe.value},{default:f(()=>[...s[9]||(s[9]=[m(" 搜索 ",-1)])]),_:1},8,["disabled","loading"])]),_:1},8,["value","disabled"])])]),u("div",{class:"content",ref_key:"contentEl",ref:He},[p(Fe).loading?(r(),i("div",ce,[c(p(Q),{size:"large"},{description:f(()=>[...s[10]||(s[10]=[m(" 加载会话列表... ",-1)])]),_:1})])):p(Fe).error?(r(),d(p(ee),{key:1,type:"error",title:"加载失败",style:{"margin-bottom":"16px"}},{default:f(()=>[m(h(p(Fe).error),1)]),_:1})):ss.value.length>0?(r(),d(p(b),w({key:2,modelValue:Re.value,"onUpdate:modelValue":s[2]||(s[2]=e=>Re.value=e),"item-key":"sessionId",class:"sessions-list",handle:".drag-handle"},Ze,{"ghost-class":"ghost","chosen-class":"chosen",animation:"200",onEnd:ts}),{item:f(({element:e})=>[u("div",{class:"session-item",onMouseenter:s=>Oe.value=e.sessionId,onMouseleave:s[1]||(s[1]=e=>Oe.value=null),onClick:s=>function(e){Ge.value=e.sessionId,We.value=e.alias||"",De.value=!0,I(()=>{var e;null==(e=Xe.value)||e.open()})}(e)},[u("div",ve,[c(p(G),{size:"16",color:"#999"},{default:f(()=>[c(p(E))]),_:1})]),u("div",fe,[u("div",pe,[c(p(G),{size:"24",color:"#18a058"},{default:f(()=>[c(p(K))]),_:1})]),u("div",me,[u("div",he,[u("div",ye,[u("span",ge,h(e.alias?`${e.alias} (${e.sessionId.substring(0,8)})`:e.sessionId),1),e.forkedFrom?(r(),d(p(se),{key:0,placement:"top"},{trigger:f(()=>[c(p(Z),{size:"small",type:"warning",bordered:!1,style:{"margin-left":"8px",cursor:"help"}},{icon:f(()=>[c(p(G),null,{default:f(()=>[c(p(O))]),_:1})]),default:f(()=>[s[11]||(s[11]=m(" Fork ",-1))]),_:1})]),default:f(()=>[m(" Fork 自: "+h(e.forkedFrom),1)]),_:2},1024)):v("",!0)])]),u("div",ke,[c(p(Y),{depth:"3"},{default:f(()=>[m(h(us(e.mtime)),1)]),_:2},1024),c(p(Y),{depth:"3"},{default:f(()=>[...s[12]||(s[12]=[m("•",-1)])]),_:1}),c(p(Z),{size:"small",bordered:!1},{default:f(()=>[m(h(cs(e.size)),1)]),_:2},1024)]),e.firstMessage?(r(),d(p(Y),{key:0,depth:"3",class:"session-message"},{default:f(()=>[m(h(vs(e.firstMessage,80)),1)]),_:2},1024)):e.gitBranch||e.summary?v("",!0):(r(),d(p(Y),{key:1,depth:"3",class:"session-message session-message-empty"},{default:f(()=>[...s[13]||(s[13]=[m(" 暂未读取到对话内容 ",-1)])]),_:1}))])]),u("div",_e,[u("div",we,[e.gitBranch?(r(),d(p(Z),{key:0,size:"small",type:"info",bordered:!1},{icon:f(()=>[c(p(G),null,{default:f(()=>[c(p(O))]),_:1})]),default:f(()=>[m(" "+h(e.gitBranch),1)]),_:2},1024)):v("",!0)]),u("div",je,[c(p(ae),null,{default:f(()=>[g(c(p(W),{size:"small",type:"error",onClick:k(s=>{return a=e.sessionId,void L.warning({title:"删除会话",content:"确定要删除这个会话吗?此操作不可恢复!",positiveText:"确定删除",negativeText:"取消",onPositiveClick:async()=>{try{await Fe.deleteSession(a),M.success("会话已删除")}catch(e){M.error("删除失败: "+e.message)}}});var a},["stop"])},{icon:f(()=>[c(p(G),null,{default:f(()=>[c(p(R))]),_:1})]),default:f(()=>[s[14]||(s[14]=m(" 删除 ",-1))]),_:1},8,["onClick"]),[[_,Oe.value===e.sessionId]]),c(p(W),{size:"small",onClick:k(s=>function(e){Ee.value=e,Ke.value=e.alias||"",Ue.value=!0}(e),["stop"])},{icon:f(()=>[c(p(G),null,{default:f(()=>[c(p(V))]),_:1})]),default:f(()=>[s[15]||(s[15]=m(" 别名 ",-1))]),_:1},8,["onClick"]),c(p(W),{size:"small",type:p(Le)($e.value,Pe.value,e.sessionId)?"warning":"default",onClick:k(s=>async function(e){const s=$e.value,a=Le(s,Pe.value,e.sessionId);try{if(a)await Me(s,Pe.value,e.sessionId),M.success("已取消收藏");else{const a={sessionId:e.sessionId,projectName:Pe.value,projectDisplayName:Je.value,projectFullPath:Qe.value,alias:e.alias||"",firstMessage:e.firstMessage||"",mtime:e.mtime,size:e.size,gitBranch:e.gitBranch||"",forkedFrom:e.forkedFrom||""};await Be(s,a),M.success("已添加到收藏")}}catch(l){M.error("操作失败: "+l.message)}}(e),["stop"])},{icon:f(()=>[c(p(G),null,{default:f(()=>[p(Le)($e.value,Pe.value,e.sessionId)?(r(),d(p(A),{key:0})):(r(),d(p(H),{key:1}))]),_:2},1024)]),default:f(()=>[m(" "+h(p(Le)($e.value,Pe.value,e.sessionId)?"已收藏":"收藏"),1)]),_:2},1032,["type","onClick"]),"opencode"!==$e.value?(r(),d(p(W),{key:0,size:"small",onClick:k(s=>async function(e){if("opencode"!==$e.value)try{await Fe.forkSession(e),M.success("Fork 成功!")}catch(s){M.error("Fork 失败: "+s.message)}else M.warning("OpenCode 当前不支持该 Fork 操作")}(e.sessionId),["stop"])},{icon:f(()=>[c(p(G),null,{default:f(()=>[c(p(O))]),_:1})]),default:f(()=>[s[16]||(s[16]=m(" Fork ",-1))]),_:1},8,["onClick"])):v("",!0),c(p(W),{size:"small",type:"primary",onClick:k(s=>is(e.sessionId),["stop"])},{icon:f(()=>[c(p(G),null,{default:f(()=>[c(p(q))]),_:1})]),default:f(()=>[s[17]||(s[17]=m(" 使用对话 ",-1))]),_:1},8,["onClick"])]),_:2},1024)])])],40,de)]),_:1},16,["modelValue"])):(r(),d(p(le),{key:3,description:"没有找到会话",style:{"margin-top":"60px"}},{icon:f(()=>[c(p(G),null,{default:f(()=>[c(p(D))]),_:1})]),_:1}))],512),c(p(te),{show:Ue.value,"onUpdate:show":s[5]||(s[5]=e=>Ue.value=e),preset:"dialog",title:"设置别名"},{action:f(()=>[c(p(ae),null,{default:f(()=>[c(p(W),{onClick:s[4]||(s[4]=e=>Ue.value=!1)},{default:f(()=>[...s[18]||(s[18]=[m("取消",-1)])]),_:1}),c(p(W),{type:"primary",onClick:ns},{default:f(()=>[...s[19]||(s[19]=[m("确定",-1)])]),_:1})]),_:1})]),default:f(()=>[c(p(J),{value:Ke.value,"onUpdate:value":s[3]||(s[3]=e=>Ke.value=e),placeholder:"输入别名(留空删除)",onKeyup:y(ns,["enter"])},null,8,["value"])]),_:1},8,["show"]),c(p(te),{show:Ae.value,"onUpdate:show":s[6]||(s[6]=e=>Ae.value=e),preset:"card",title:"搜索结果",style:{width:"1200px"}},{default:f(()=>[Ve.value?(r(),i("div",Ce,[c(p(ee),{type:"info",style:{"margin-bottom":"16px"}},{default:f(()=>[m(' 关键词 "'+h(Ve.value.keyword)+'" 共找到 '+h(Ve.value.totalMatches)+" 处匹配 ",1)]),_:1}),(r(!0),i(j,null,C(Ve.value.sessions,e=>(r(),i("div",{key:e.sessionId,class:"search-result-item"},[u("div",Ie,[u("div",be,[c(p(Y),{strong:""},{default:f(()=>[m(h(e.alias?`${e.alias} (${e.sessionId.substring(0,8)})`:e.sessionId.substring(0,8)),1)]),_:2},1024),c(p(Z),{size:"small",bordered:!1},{default:f(()=>[m(h(e.matchCount)+" 个匹配",1)]),_:2},1024)]),c(p(W),{size:"small",type:"primary",onClick:s=>is(e.sessionId)},{icon:f(()=>[c(p(G),null,{default:f(()=>[c(p(q))]),_:1})]),default:f(()=>[s[20]||(s[20]=m(" 使用对话 ",-1))]),_:1},8,["onClick"])]),(r(!0),i(j,null,C(e.matches,(e,s)=>(r(),i("div",{key:s,class:"search-match"},[c(p(Z),{size:"tiny",type:"user"===e.role?"info":"success",bordered:!1},{default:f(()=>[m(h("user"===e.role?"用户":"助手"),1)]),_:2},1032,["type"]),c(p(Y),{depth:"3",class:"search-match-text",innerHTML:ds(e.context,Ve.value.keyword)},null,8,["innerHTML"])]))),128))]))),128)),0===Ve.value.sessions.length?(r(),d(p(le),{key:0,description:"没有找到匹配的内容"})):v("",!0)])):v("",!0)]),_:1},8,["show"]),Ge.value?(r(),d(F,{key:0,ref_key:"chatHistoryRef",ref:Xe,show:De.value,"onUpdate:show":s[7]||(s[7]=e=>De.value=e),"project-name":Pe.value,"session-id":Ge.value,"session-alias":We.value,channel:$e.value,onError:os},null,8,["show","project-name","session-id","session-alias","channel"])):v("",!0)]))}},[["__scopeId","data-v-05ca5429"]]);export{xe as default};