coding-tool-x 3.7.5 → 3.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/web/assets/{Analytics-DnnufrQi.js → Analytics-wCRLv26m.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-BTVhMSfe.js → ConfigTemplates-aa1blEHh.js} +1 -1
- package/dist/web/assets/{Home-D03PEREx.js → Home-eIxO50-k.js} +1 -1
- package/dist/web/assets/{PluginManager-BiMqEiyQ.js → PluginManager-BAwGFNjN.js} +1 -1
- package/dist/web/assets/ProjectList-CRiAEixW.js +1 -0
- package/dist/web/assets/ProjectList-Dw2omgOH.css +1 -0
- package/dist/web/assets/SessionList-BQ0FW6Gd.js +1 -0
- package/dist/web/assets/SessionList-kYJ6-Ies.css +1 -0
- package/dist/web/assets/{SkillManager-DAy8k5CL.js → SkillManager-Bhzs1O5S.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-Bs2D6R9j.js → WorkspaceManager-CLdX-pTT.js} +1 -1
- package/dist/web/assets/index-B_nFqixU.css +1 -0
- package/dist/web/assets/index-ClMNA9Hf.js +2 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/src/server/api/omp-channels.js +50 -7
- package/src/server/services/dashboard-snapshot-worker.js +1 -1
- package/src/server/services/native-oauth-adapters.js +2 -1
- package/src/server/services/omp-auth-provider-worker.js +29 -0
- package/src/server/services/omp-auth-providers.js +137 -11
- package/src/server/services/omp-config.js +7 -4
- package/src/server/services/omp-sessions.js +18 -14
- package/src/server/services/plugins-service.js +2 -1
- package/src/server/services/project-snapshots.js +8 -1
- package/src/server/services/session-snapshots.js +9 -1
- package/src/server/services/snapshot-cache.js +47 -3
- package/dist/web/assets/ProjectList-C6BA3V49.js +0 -1
- package/dist/web/assets/ProjectList-DQ5ZCJO2.css +0 -1
- package/dist/web/assets/SessionList--9Bl6EhV.css +0 -1
- package/dist/web/assets/SessionList-DChG04jz.js +0 -1
- package/dist/web/assets/index-DnDBHGCC.js +0 -2
- package/dist/web/assets/index-U3TOywtB.css +0 -1
package/dist/web/index.html
CHANGED
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
<link rel="icon" href="/favicon.ico">
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
7
|
<title>coding-tool-x · AI 编程工作台</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-ClMNA9Hf.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/markdown-BNJfEgCA.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/vue-vendor-B5RqjEjQ.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/naive-ui-CVyvdmg1.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/icons-nGdSKQn2.js">
|
|
13
13
|
<link rel="modulepreload" crossorigin href="/assets/vendors-DQA4Hw9v.js">
|
|
14
14
|
<link rel="stylesheet" crossorigin href="/assets/markdown-BfC0goYb.css">
|
|
15
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
15
|
+
<link rel="stylesheet" crossorigin href="/assets/index-B_nFqixU.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
|
18
18
|
<div id="app"></div>
|
package/package.json
CHANGED
|
@@ -23,9 +23,13 @@ const {
|
|
|
23
23
|
} = require('../services/model-detector');
|
|
24
24
|
const {
|
|
25
25
|
findAuthProviderForKey,
|
|
26
|
-
|
|
26
|
+
getCachedOmpAuthProviderSnapshot,
|
|
27
|
+
getOmpAuthProviderCacheMeta,
|
|
28
|
+
warmOmpAuthProviderSnapshot
|
|
27
29
|
} = require('../services/omp-auth-providers');
|
|
28
30
|
|
|
31
|
+
const CHANNEL_LIST_AUTH_OPTIONS = { accountCheck: false, includeStatus: false };
|
|
32
|
+
|
|
29
33
|
function uniqueModels(models = []) {
|
|
30
34
|
const seen = new Set();
|
|
31
35
|
const result = [];
|
|
@@ -136,19 +140,53 @@ function attachAuthProvider(channel, snapshot) {
|
|
|
136
140
|
};
|
|
137
141
|
}
|
|
138
142
|
|
|
143
|
+
function getAuthSnapshotForChannelList() {
|
|
144
|
+
const snapshot = getCachedOmpAuthProviderSnapshot(CHANNEL_LIST_AUTH_OPTIONS);
|
|
145
|
+
const shouldWarm = !snapshot || snapshot.stale;
|
|
146
|
+
if (shouldWarm) {
|
|
147
|
+
warmOmpAuthProviderSnapshot(CHANNEL_LIST_AUTH_OPTIONS);
|
|
148
|
+
}
|
|
149
|
+
const meta = getOmpAuthProviderCacheMeta(CHANNEL_LIST_AUTH_OPTIONS);
|
|
150
|
+
return {
|
|
151
|
+
snapshot,
|
|
152
|
+
meta: {
|
|
153
|
+
...meta,
|
|
154
|
+
stale: !snapshot || Boolean(snapshot.stale),
|
|
155
|
+
refreshing: shouldWarm || meta.refreshing,
|
|
156
|
+
fallback: !snapshot
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function buildUnavailableAuthProviderMeta(error) {
|
|
162
|
+
return {
|
|
163
|
+
cached: false,
|
|
164
|
+
stale: false,
|
|
165
|
+
refreshing: false,
|
|
166
|
+
fallback: true,
|
|
167
|
+
checkedAt: new Date().toISOString(),
|
|
168
|
+
error: error || 'omp-not-available'
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
139
172
|
module.exports = () => {
|
|
140
173
|
router.get('/', (req, res) => {
|
|
141
174
|
try {
|
|
142
175
|
if (!isOmpInstalled()) {
|
|
143
|
-
return res.json({
|
|
176
|
+
return res.json({
|
|
177
|
+
channels: [],
|
|
178
|
+
installed: false,
|
|
179
|
+
error: 'OMP CLI not installed',
|
|
180
|
+
authProviderMeta: buildUnavailableAuthProviderMeta('OMP CLI not installed')
|
|
181
|
+
});
|
|
144
182
|
}
|
|
145
183
|
const data = getChannels();
|
|
146
|
-
const
|
|
184
|
+
const { snapshot: authSnapshot, meta: authProviderMeta } = getAuthSnapshotForChannelList();
|
|
147
185
|
const channels = (data.channels || []).map(ch => ({
|
|
148
186
|
...attachAuthProvider(ch, authSnapshot),
|
|
149
187
|
health: getChannelHealthStatus(ch.id, 'omp')
|
|
150
188
|
}));
|
|
151
|
-
res.json({ channels, installed: true });
|
|
189
|
+
res.json({ channels, installed: true, authProviderMeta });
|
|
152
190
|
} catch (err) {
|
|
153
191
|
console.error('[OMP Channels API] Failed to get channels:', err);
|
|
154
192
|
res.status(500).json({ error: err.message });
|
|
@@ -158,14 +196,19 @@ module.exports = () => {
|
|
|
158
196
|
router.get('/enabled', (req, res) => {
|
|
159
197
|
try {
|
|
160
198
|
if (!isOmpInstalled()) {
|
|
161
|
-
return res.json({
|
|
199
|
+
return res.json({
|
|
200
|
+
channels: [],
|
|
201
|
+
installed: false,
|
|
202
|
+
error: 'OMP CLI not installed',
|
|
203
|
+
authProviderMeta: buildUnavailableAuthProviderMeta('OMP CLI not installed')
|
|
204
|
+
});
|
|
162
205
|
}
|
|
163
206
|
const data = getChannels();
|
|
164
|
-
const
|
|
207
|
+
const { snapshot: authSnapshot, meta: authProviderMeta } = getAuthSnapshotForChannelList();
|
|
165
208
|
const channels = (data.channels || [])
|
|
166
209
|
.filter(ch => ch.enabled !== false)
|
|
167
210
|
.map(ch => ({ ...attachAuthProvider(ch, authSnapshot), health: getChannelHealthStatus(ch.id, 'omp') }));
|
|
168
|
-
res.json({ channels, installed: true });
|
|
211
|
+
res.json({ channels, installed: true, authProviderMeta });
|
|
169
212
|
} catch (err) {
|
|
170
213
|
console.error('[OMP Channels API] Failed to get enabled channels:', err);
|
|
171
214
|
res.status(500).json({ error: err.message });
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { fork } = require('child_process');
|
|
4
4
|
|
|
5
|
-
const DASHBOARD_SNAPSHOT_WORKER_TIMEOUT_MS =
|
|
5
|
+
const DASHBOARD_SNAPSHOT_WORKER_TIMEOUT_MS = 180 * 1000;
|
|
6
6
|
|
|
7
7
|
function sortClaudeProjects(projects, order = []) {
|
|
8
8
|
if (!Array.isArray(order) || order.length === 0) {
|
|
@@ -1029,7 +1029,8 @@ function runOmpCommand(args = [], options = {}) {
|
|
|
1029
1029
|
...process.env,
|
|
1030
1030
|
...(options.env || {})
|
|
1031
1031
|
},
|
|
1032
|
-
timeout: options.timeout || 10000
|
|
1032
|
+
timeout: options.timeout || 10000,
|
|
1033
|
+
windowsHide: true
|
|
1033
1034
|
});
|
|
1034
1035
|
const status = result?.status === undefined || result?.status === null ? 0 : result.status;
|
|
1035
1036
|
return {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { getOmpAuthProviderSnapshot } = require('./omp-auth-providers');
|
|
4
|
+
|
|
5
|
+
function attachWorkerHandler() {
|
|
6
|
+
process.on('message', (options = {}) => {
|
|
7
|
+
try {
|
|
8
|
+
const value = getOmpAuthProviderSnapshot(options);
|
|
9
|
+
if (process.send) {
|
|
10
|
+
process.send({ ok: true, value }, () => process.exit(0));
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
} catch (error) {
|
|
14
|
+
if (process.send) {
|
|
15
|
+
process.send({ ok: false, error: error?.message || String(error) }, () => process.exit(1));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
process.exit(1);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (process.env.CC_TOOL_OMP_AUTH_PROVIDER_WORKER === '1' || require.main === module) {
|
|
24
|
+
attachWorkerHandler();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = {
|
|
28
|
+
attachWorkerHandler
|
|
29
|
+
};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
const
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const { fork, spawnSync } = require('child_process');
|
|
2
3
|
const ompConfig = require('./omp-config');
|
|
3
4
|
|
|
4
5
|
const COMMAND_TIMEOUT_MS = 5000;
|
|
5
6
|
const TOKEN_LIST_TIMEOUT_MS = 3000;
|
|
6
7
|
const CACHE_TTL_MS = 15000;
|
|
8
|
+
const WARMUP_WORKER_TIMEOUT_MS = COMMAND_TIMEOUT_MS + 1000;
|
|
7
9
|
|
|
8
10
|
const LOGIN_PROVIDER_IDS = new Set([
|
|
9
11
|
'openai-codex',
|
|
@@ -50,6 +52,20 @@ const PROVIDER_ALIASES = {
|
|
|
50
52
|
};
|
|
51
53
|
|
|
52
54
|
let snapshotCache = {};
|
|
55
|
+
const warmupTimers = new Map();
|
|
56
|
+
const warmupErrors = new Map();
|
|
57
|
+
|
|
58
|
+
function getSnapshotCacheKey(options = {}) {
|
|
59
|
+
return options.accountCheck === false
|
|
60
|
+
? (options.includeStatus === false ? 'metadata' : 'metadata-status')
|
|
61
|
+
: 'full';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function writeSnapshotCache(cacheKey, loadedAt, value) {
|
|
65
|
+
snapshotCache[cacheKey] = { loadedAt, value };
|
|
66
|
+
warmupErrors.delete(cacheKey);
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
53
69
|
|
|
54
70
|
function normalizeProviderId(value = '') {
|
|
55
71
|
return String(value || '')
|
|
@@ -95,7 +111,8 @@ function runCommand(command, args = [], options = {}) {
|
|
|
95
111
|
...process.env,
|
|
96
112
|
...(options.env || {})
|
|
97
113
|
},
|
|
98
|
-
timeout: options.timeout || COMMAND_TIMEOUT_MS
|
|
114
|
+
timeout: options.timeout || COMMAND_TIMEOUT_MS,
|
|
115
|
+
windowsHide: true
|
|
99
116
|
});
|
|
100
117
|
if (typeof result === 'string' || Buffer.isBuffer(result)) {
|
|
101
118
|
return { status: 0, stdout: normalizeCommandOutput(result), stderr: '' };
|
|
@@ -205,9 +222,7 @@ function buildEmptySnapshot(runtime, reason) {
|
|
|
205
222
|
|
|
206
223
|
function getOmpAuthProviderSnapshot(options = {}) {
|
|
207
224
|
const now = Date.now();
|
|
208
|
-
const cacheKey = options
|
|
209
|
-
? (options.includeStatus === false ? 'metadata' : 'metadata-status')
|
|
210
|
-
: 'full';
|
|
225
|
+
const cacheKey = getSnapshotCacheKey(options);
|
|
211
226
|
const cached = snapshotCache[cacheKey];
|
|
212
227
|
if (!options.forceRefresh && cached && now - cached.loadedAt < CACHE_TTL_MS) {
|
|
213
228
|
return cached.value;
|
|
@@ -217,16 +232,14 @@ function getOmpAuthProviderSnapshot(options = {}) {
|
|
|
217
232
|
const runtime = options.runtime || ompConfig.resolveOmpRuntime(env, options.runtimeOptions || {});
|
|
218
233
|
if (!runtime || runtime.runtime !== 'omp' || !runtime.installed) {
|
|
219
234
|
const value = buildEmptySnapshot(runtime || null, 'omp-not-available');
|
|
220
|
-
|
|
221
|
-
return value;
|
|
235
|
+
return writeSnapshotCache(cacheKey, now, value);
|
|
222
236
|
}
|
|
223
237
|
|
|
224
238
|
const command = runtime.command;
|
|
225
239
|
const listResult = parseJsonCommand(command, ['auth-broker', 'list', '--json'], options);
|
|
226
240
|
if (!listResult.ok || !Array.isArray(listResult.value)) {
|
|
227
241
|
const value = buildEmptySnapshot(runtime, listResult.error || listResult.stderr || 'auth-provider-list-failed');
|
|
228
|
-
|
|
229
|
-
return value;
|
|
242
|
+
return writeSnapshotCache(cacheKey, now, value);
|
|
230
243
|
}
|
|
231
244
|
|
|
232
245
|
const supportedProviders = listResult.value
|
|
@@ -268,8 +281,112 @@ function getOmpAuthProviderSnapshot(options = {}) {
|
|
|
268
281
|
aliases: { ...PROVIDER_ALIASES },
|
|
269
282
|
checkedAt: new Date().toISOString()
|
|
270
283
|
};
|
|
271
|
-
|
|
272
|
-
|
|
284
|
+
return writeSnapshotCache(cacheKey, now, value);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function getCachedOmpAuthProviderSnapshot(options = {}) {
|
|
288
|
+
const cacheKey = getSnapshotCacheKey(options);
|
|
289
|
+
const cached = snapshotCache[cacheKey];
|
|
290
|
+
if (!cached) return null;
|
|
291
|
+
return {
|
|
292
|
+
...cached.value,
|
|
293
|
+
stale: Date.now() - cached.loadedAt >= CACHE_TTL_MS
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function getOmpAuthProviderCacheMeta(options = {}) {
|
|
298
|
+
const cacheKey = getSnapshotCacheKey(options);
|
|
299
|
+
const cached = snapshotCache[cacheKey];
|
|
300
|
+
const stale = !cached || Date.now() - cached.loadedAt >= CACHE_TTL_MS;
|
|
301
|
+
const cachedReason = cached?.value?.available === false ? cached.value.reason || null : null;
|
|
302
|
+
return {
|
|
303
|
+
cached: Boolean(cached),
|
|
304
|
+
stale,
|
|
305
|
+
refreshing: warmupTimers.has(cacheKey),
|
|
306
|
+
fallback: !cached,
|
|
307
|
+
checkedAt: cached?.value?.checkedAt || null,
|
|
308
|
+
error: warmupErrors.get(cacheKey) || cachedReason || null
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function warmOmpAuthProviderSnapshot(options = {}) {
|
|
313
|
+
const cacheKey = getSnapshotCacheKey(options);
|
|
314
|
+
if (warmupTimers.has(cacheKey)) return;
|
|
315
|
+
|
|
316
|
+
const finishWarmup = (task, error = null, value = null) => {
|
|
317
|
+
if (!warmupTimers.has(cacheKey)) return;
|
|
318
|
+
if (task.timeout) clearTimeout(task.timeout);
|
|
319
|
+
if (task.child) {
|
|
320
|
+
task.child.removeAllListeners();
|
|
321
|
+
if (!task.child.killed) task.child.kill();
|
|
322
|
+
}
|
|
323
|
+
warmupTimers.delete(cacheKey);
|
|
324
|
+
if (error) {
|
|
325
|
+
warmupErrors.set(cacheKey, sanitizeIdentity(error?.message || String(error || 'auth-provider-warmup-failed')));
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
if (value) {
|
|
329
|
+
writeSnapshotCache(cacheKey, Date.now(), value);
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
// Tests and callers that supply a function-based command runner cannot cross
|
|
334
|
+
// the process boundary. Production channel-list warmups use the worker path.
|
|
335
|
+
if (process.env.NODE_ENV === 'test' || options.commandRunner) {
|
|
336
|
+
const task = { timeout: null, child: null };
|
|
337
|
+
const timer = setTimeout(() => {
|
|
338
|
+
try {
|
|
339
|
+
finishWarmup(task, null, getOmpAuthProviderSnapshot(options));
|
|
340
|
+
} catch (error) {
|
|
341
|
+
finishWarmup(task, error);
|
|
342
|
+
}
|
|
343
|
+
}, 0);
|
|
344
|
+
task.timeout = timer;
|
|
345
|
+
warmupTimers.set(cacheKey, task);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const workerPath = path.join(__dirname, 'omp-auth-provider-worker.js');
|
|
350
|
+
const task = { timeout: null, child: null };
|
|
351
|
+
try {
|
|
352
|
+
const child = fork(workerPath, [], {
|
|
353
|
+
stdio: ['ignore', 'ignore', 'ignore', 'ipc'],
|
|
354
|
+
env: {
|
|
355
|
+
...process.env,
|
|
356
|
+
CC_TOOL_OMP_AUTH_PROVIDER_WORKER: '1'
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
task.child = child;
|
|
360
|
+
task.timeout = setTimeout(() => {
|
|
361
|
+
finishWarmup(task, new Error('auth-provider-warmup-timed-out'));
|
|
362
|
+
}, WARMUP_WORKER_TIMEOUT_MS);
|
|
363
|
+
warmupTimers.set(cacheKey, task);
|
|
364
|
+
|
|
365
|
+
child.on('message', (message) => {
|
|
366
|
+
if (message?.ok) {
|
|
367
|
+
finishWarmup(task, null, message.value);
|
|
368
|
+
} else {
|
|
369
|
+
finishWarmup(task, new Error(message?.error || 'auth-provider-warmup-failed'));
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
child.on('error', (error) => finishWarmup(task, error));
|
|
373
|
+
child.on('exit', (code, signal) => {
|
|
374
|
+
if (warmupTimers.get(cacheKey) === task) {
|
|
375
|
+
finishWarmup(task, new Error(`auth-provider-warmup-worker-exited-${code || signal || 'unknown'}`));
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
child.send({
|
|
379
|
+
accountCheck: options.accountCheck,
|
|
380
|
+
includeStatus: options.includeStatus,
|
|
381
|
+
forceRefresh: options.forceRefresh === true,
|
|
382
|
+
runtime: options.runtime,
|
|
383
|
+
runtimeOptions: options.runtimeOptions
|
|
384
|
+
});
|
|
385
|
+
} catch (error) {
|
|
386
|
+
// `fork` itself can fail before the task is registered. Preserve the
|
|
387
|
+
// failure for the non-blocking status endpoint instead of dropping it.
|
|
388
|
+
warmupErrors.set(cacheKey, sanitizeIdentity(error?.message || String(error || 'auth-provider-warmup-failed')));
|
|
389
|
+
}
|
|
273
390
|
}
|
|
274
391
|
|
|
275
392
|
function findAuthProviderForKey(providerKey, snapshot) {
|
|
@@ -280,6 +397,12 @@ function findAuthProviderForKey(providerKey, snapshot) {
|
|
|
280
397
|
|
|
281
398
|
function clearOmpAuthProviderCache() {
|
|
282
399
|
snapshotCache = {};
|
|
400
|
+
for (const task of warmupTimers.values()) {
|
|
401
|
+
if (task.timeout) clearTimeout(task.timeout);
|
|
402
|
+
if (task.child && !task.child.killed) task.child.kill();
|
|
403
|
+
}
|
|
404
|
+
warmupTimers.clear();
|
|
405
|
+
warmupErrors.clear();
|
|
283
406
|
}
|
|
284
407
|
|
|
285
408
|
module.exports = {
|
|
@@ -287,7 +410,10 @@ module.exports = {
|
|
|
287
410
|
ACCOUNT_CHECK_PROVIDER_IDS,
|
|
288
411
|
clearOmpAuthProviderCache,
|
|
289
412
|
findAuthProviderForKey,
|
|
413
|
+
getCachedOmpAuthProviderSnapshot,
|
|
414
|
+
getOmpAuthProviderCacheMeta,
|
|
290
415
|
getOmpAuthProviderSnapshot,
|
|
416
|
+
warmOmpAuthProviderSnapshot,
|
|
291
417
|
normalizeProviderId,
|
|
292
418
|
resolveProviderAlias,
|
|
293
419
|
sanitizeIdentity
|
|
@@ -61,7 +61,10 @@ function runOmpCommand(command, args = [], env = process.env, options = {}, stdi
|
|
|
61
61
|
encoding: 'utf8',
|
|
62
62
|
env: buildCommandEnv(env),
|
|
63
63
|
stdio,
|
|
64
|
-
timeout: options.timeout || 3000
|
|
64
|
+
timeout: options.timeout || 3000,
|
|
65
|
+
// On Windows, an npm/PowerShell command shim can otherwise create a visible
|
|
66
|
+
// console every time we probe OMP or resolve its configuration directory.
|
|
67
|
+
windowsHide: true
|
|
65
68
|
});
|
|
66
69
|
}
|
|
67
70
|
|
|
@@ -76,8 +79,8 @@ function readOmpAgentDirFromCommand(command, env = process.env, options = {}) {
|
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
function getOmpAgentDir(env = process.env, options = {}) {
|
|
79
|
-
const runtime = options.runtime || resolveOmpRuntime(env, options);
|
|
80
|
-
if (runtime
|
|
82
|
+
const runtime = options.runtime || (options.resolveRuntime === false ? null : resolveOmpRuntime(env, options));
|
|
83
|
+
if (runtime?.runtime === 'omp' && runtime.installed) {
|
|
81
84
|
const commandAgentDir = readOmpAgentDirFromCommand(runtime.command, env, options);
|
|
82
85
|
if (commandAgentDir) {
|
|
83
86
|
return commandAgentDir;
|
|
@@ -87,7 +90,7 @@ function getOmpAgentDir(env = process.env, options = {}) {
|
|
|
87
90
|
}
|
|
88
91
|
|
|
89
92
|
function getOmpPaths(env = process.env, options = {}) {
|
|
90
|
-
const runtime = options.runtime || resolveOmpRuntime(env, options);
|
|
93
|
+
const runtime = options.runtime || (options.resolveRuntime === false ? null : resolveOmpRuntime(env, options));
|
|
91
94
|
const agentDir = getOmpAgentDir(env, { ...options, runtime });
|
|
92
95
|
return {
|
|
93
96
|
agentDir,
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const { execFileSync } = require('child_process');
|
|
4
3
|
const { PATHS, HOME_DIR } = require('../../config/paths');
|
|
5
|
-
const { getOmpCommand, getOmpPaths,
|
|
4
|
+
const { getOmpCommand, getOmpPaths, resolveOmpRuntime } = require('./omp-config');
|
|
6
5
|
|
|
7
6
|
const PROJECT_ORDER_FILE = PATHS.ompProjectOrder;
|
|
8
7
|
const SESSION_ORDER_FILE = PATHS.ompSessionOrder;
|
|
@@ -213,7 +212,20 @@ function parseSessionFile(filePath) {
|
|
|
213
212
|
};
|
|
214
213
|
}
|
|
215
214
|
|
|
216
|
-
function
|
|
215
|
+
function getOmpSessionPaths() {
|
|
216
|
+
// Reading historical sessions normally only needs OMP's native directory
|
|
217
|
+
// convention. Avoid starting the CLI here: project/count snapshot workers
|
|
218
|
+
// invoke this path independently, which made Windows repeatedly open
|
|
219
|
+
// PowerShell shims. Keep the CLI lookup as a fallback for non-standard OMP
|
|
220
|
+
// locations that cannot be derived from the environment.
|
|
221
|
+
const nativePaths = getOmpPaths(process.env, { resolveRuntime: false });
|
|
222
|
+
if (fs.existsSync(nativePaths.agentDir) || fs.existsSync(nativePaths.sessions)) {
|
|
223
|
+
return nativePaths;
|
|
224
|
+
}
|
|
225
|
+
return getOmpPaths();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function scanSessionFiles(rootDir = getOmpSessionPaths().sessions) {
|
|
217
229
|
if (!fs.existsSync(rootDir)) {
|
|
218
230
|
return [];
|
|
219
231
|
}
|
|
@@ -462,20 +474,12 @@ function isOmpCliInstalled() {
|
|
|
462
474
|
}
|
|
463
475
|
|
|
464
476
|
let installed = false;
|
|
465
|
-
|
|
477
|
+
const nativePaths = getOmpSessionPaths();
|
|
478
|
+
if (fs.existsSync(nativePaths.agentDir)) {
|
|
466
479
|
installed = true;
|
|
467
480
|
} else {
|
|
468
481
|
const runtime = resolveOmpRuntime();
|
|
469
|
-
|
|
470
|
-
installed = true;
|
|
471
|
-
} else {
|
|
472
|
-
try {
|
|
473
|
-
execFileSync(getOmpCommand(), ['--version'], { stdio: 'ignore', timeout: 3000 });
|
|
474
|
-
installed = true;
|
|
475
|
-
} catch {
|
|
476
|
-
installed = false;
|
|
477
|
-
}
|
|
478
|
-
}
|
|
482
|
+
installed = Boolean(runtime && runtime.installed);
|
|
479
483
|
}
|
|
480
484
|
|
|
481
485
|
ompInstallCache = {
|
|
@@ -6,6 +6,8 @@ const {
|
|
|
6
6
|
} = require('./snapshot-cache');
|
|
7
7
|
|
|
8
8
|
const PROJECT_SNAPSHOT_TTL_MS = 60 * 1000;
|
|
9
|
+
const PROJECT_SNAPSHOT_WAIT_ON_MISS_MS = process.env.NODE_ENV === 'test' ? 0 : 1500;
|
|
10
|
+
const PROJECT_SNAPSHOT_WAIT_ON_FORCE_MS = process.env.NODE_ENV === 'test' ? 0 : 1500;
|
|
9
11
|
|
|
10
12
|
function projectListKey(source) {
|
|
11
13
|
return `projects:list:${source}`;
|
|
@@ -33,7 +35,10 @@ async function getProjectListSnapshot(source, {
|
|
|
33
35
|
fallbackValue,
|
|
34
36
|
refresh,
|
|
35
37
|
force,
|
|
36
|
-
backgroundOnMiss: !force
|
|
38
|
+
backgroundOnMiss: !force,
|
|
39
|
+
staleWhileForce: true,
|
|
40
|
+
waitOnMissMs: PROJECT_SNAPSHOT_WAIT_ON_MISS_MS,
|
|
41
|
+
waitOnForceMs: PROJECT_SNAPSHOT_WAIT_ON_FORCE_MS
|
|
37
42
|
});
|
|
38
43
|
}
|
|
39
44
|
|
|
@@ -44,6 +49,8 @@ function invalidateProjectSnapshots(source) {
|
|
|
44
49
|
|
|
45
50
|
module.exports = {
|
|
46
51
|
PROJECT_SNAPSHOT_TTL_MS,
|
|
52
|
+
PROJECT_SNAPSHOT_WAIT_ON_MISS_MS,
|
|
53
|
+
PROJECT_SNAPSHOT_WAIT_ON_FORCE_MS,
|
|
47
54
|
projectListKey,
|
|
48
55
|
projectCountKey,
|
|
49
56
|
emptyProjectList,
|
|
@@ -9,7 +9,9 @@ const {
|
|
|
9
9
|
|
|
10
10
|
const SESSION_SNAPSHOT_TTL_MS = 60 * 1000;
|
|
11
11
|
const SESSION_SNAPSHOT_DEFER_MS = process.env.NODE_ENV === 'test' ? 0 : 750;
|
|
12
|
-
const SESSION_SNAPSHOT_WORKER_TIMEOUT_MS =
|
|
12
|
+
const SESSION_SNAPSHOT_WORKER_TIMEOUT_MS = 180 * 1000;
|
|
13
|
+
const SESSION_SNAPSHOT_WAIT_ON_MISS_MS = process.env.NODE_ENV === 'test' ? 0 : 2500;
|
|
14
|
+
const SESSION_SNAPSHOT_WAIT_ON_FORCE_MS = process.env.NODE_ENV === 'test' ? 0 : 2500;
|
|
13
15
|
|
|
14
16
|
function sessionListKey(source, projectName) {
|
|
15
17
|
return `sessions:list:${source}:${projectName}`;
|
|
@@ -113,6 +115,9 @@ async function getSessionListSnapshot(source, projectName, {
|
|
|
113
115
|
refresh,
|
|
114
116
|
force: process.env.NODE_ENV === 'test' ? true : force,
|
|
115
117
|
backgroundOnMiss: process.env.NODE_ENV === 'test' ? false : !force,
|
|
118
|
+
staleWhileForce: true,
|
|
119
|
+
waitOnMissMs: SESSION_SNAPSHOT_WAIT_ON_MISS_MS,
|
|
120
|
+
waitOnForceMs: SESSION_SNAPSHOT_WAIT_ON_FORCE_MS,
|
|
116
121
|
deferMs: SESSION_SNAPSHOT_DEFER_MS
|
|
117
122
|
});
|
|
118
123
|
}
|
|
@@ -128,6 +133,9 @@ function invalidateSessionSnapshots(source, projectName = null) {
|
|
|
128
133
|
|
|
129
134
|
module.exports = {
|
|
130
135
|
SESSION_SNAPSHOT_TTL_MS,
|
|
136
|
+
SESSION_SNAPSHOT_WAIT_ON_MISS_MS,
|
|
137
|
+
SESSION_SNAPSHOT_WAIT_ON_FORCE_MS,
|
|
138
|
+
SESSION_SNAPSHOT_WORKER_TIMEOUT_MS,
|
|
131
139
|
defaultProjectInfo,
|
|
132
140
|
emptySessionList,
|
|
133
141
|
getSessionListSnapshot,
|
|
@@ -9,6 +9,7 @@ const DEFAULT_TTL_MS = 60 * 1000;
|
|
|
9
9
|
const SNAPSHOT_VERSION = 1;
|
|
10
10
|
const DEFAULT_BACKGROUND_DELAY_MS = process.env.NODE_ENV === 'test' ? 0 : 750;
|
|
11
11
|
const DEFAULT_BACKGROUND_GAP_MS = process.env.NODE_ENV === 'test' ? 0 : 100;
|
|
12
|
+
const WAIT_TIMEOUT = Symbol('snapshot-wait-timeout');
|
|
12
13
|
|
|
13
14
|
const snapshots = new Map();
|
|
14
15
|
const inflight = new Map();
|
|
@@ -196,6 +197,28 @@ function isExpired(entry, ttlMs, nowMs) {
|
|
|
196
197
|
return nowMs - Number(entry.updatedAtMs || 0) >= ttlMs;
|
|
197
198
|
}
|
|
198
199
|
|
|
200
|
+
async function waitForRefresh(promise, waitMs) {
|
|
201
|
+
const timeoutMs = Number(waitMs) || 0;
|
|
202
|
+
if (timeoutMs <= 0) return { skipped: true };
|
|
203
|
+
|
|
204
|
+
let timer = null;
|
|
205
|
+
const result = await Promise.race([
|
|
206
|
+
promise
|
|
207
|
+
.then(entry => ({ entry }))
|
|
208
|
+
.catch(error => ({ error })),
|
|
209
|
+
new Promise(resolve => {
|
|
210
|
+
timer = setTimeout(() => resolve(WAIT_TIMEOUT), timeoutMs);
|
|
211
|
+
})
|
|
212
|
+
]);
|
|
213
|
+
if (timer) {
|
|
214
|
+
clearTimeout(timer);
|
|
215
|
+
}
|
|
216
|
+
if (result === WAIT_TIMEOUT) {
|
|
217
|
+
return { timedOut: true };
|
|
218
|
+
}
|
|
219
|
+
return result;
|
|
220
|
+
}
|
|
221
|
+
|
|
199
222
|
function makeMeta(entry, {
|
|
200
223
|
key,
|
|
201
224
|
stale,
|
|
@@ -220,7 +243,9 @@ async function getSnapshot(key, {
|
|
|
220
243
|
force = false,
|
|
221
244
|
staleWhileForce = false,
|
|
222
245
|
nowMs = Date.now(),
|
|
223
|
-
deferMs = DEFAULT_BACKGROUND_DELAY_MS
|
|
246
|
+
deferMs = DEFAULT_BACKGROUND_DELAY_MS,
|
|
247
|
+
waitOnMissMs = 0,
|
|
248
|
+
waitOnForceMs = 0
|
|
224
249
|
} = {}) {
|
|
225
250
|
if (typeof refresh !== 'function') {
|
|
226
251
|
const entry = getEntry(key);
|
|
@@ -241,7 +266,26 @@ async function getSnapshot(key, {
|
|
|
241
266
|
let refreshError = null;
|
|
242
267
|
|
|
243
268
|
if (expired) {
|
|
244
|
-
|
|
269
|
+
const coldWaitMs = !entry
|
|
270
|
+
? (force ? (waitOnForceMs || waitOnMissMs) : waitOnMissMs)
|
|
271
|
+
: 0;
|
|
272
|
+
|
|
273
|
+
if (!entry && Number(coldWaitMs) > 0) {
|
|
274
|
+
refreshPromise = refreshSnapshot(key, refresh, { bypassInflight: force });
|
|
275
|
+
const waitResult = await waitForRefresh(refreshPromise, coldWaitMs);
|
|
276
|
+
if (waitResult.entry) {
|
|
277
|
+
entry = waitResult.entry;
|
|
278
|
+
expired = false;
|
|
279
|
+
refreshPromise = null;
|
|
280
|
+
} else if (waitResult.error) {
|
|
281
|
+
refreshError = waitResult.error?.message || String(waitResult.error);
|
|
282
|
+
entry = getEntry(key);
|
|
283
|
+
if (entry) {
|
|
284
|
+
expired = isExpired(entry, ttlMs, nowMs);
|
|
285
|
+
}
|
|
286
|
+
refreshPromise = null;
|
|
287
|
+
}
|
|
288
|
+
} else if (!entry && backgroundOnMiss === false) {
|
|
245
289
|
try {
|
|
246
290
|
entry = await refreshSnapshot(key, refresh, { bypassInflight: force });
|
|
247
291
|
expired = false;
|
|
@@ -251,7 +295,7 @@ async function getSnapshot(key, {
|
|
|
251
295
|
if (!entry) throw error;
|
|
252
296
|
}
|
|
253
297
|
} else {
|
|
254
|
-
refreshPromise = refreshSnapshot(key, refresh, { defer:
|
|
298
|
+
refreshPromise = refreshSnapshot(key, refresh, { defer: !force, deferMs, bypassInflight: force });
|
|
255
299
|
}
|
|
256
300
|
}
|
|
257
301
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as e,f as a,c as t,o as s,a as l,X as n,s as o,Y as r,N as c,_ as i,G as u,Z as d,p,a0 as v,W as m,a8 as f,a5 as h,w as y,h as g,K as _,F as k,$ as j,a6 as w,R as x,v as C}from"./vue-vendor-B5RqjEjQ.js";import{d as b}from"./vendors-DQA4Hw9v.js";import{_ as $,k as z,l as L,m as N,o as P,p as M,q as D}from"./index-DnDBHGCC.js";import{a9 as I,T,d as E,ad as K,h as U}from"./icons-nGdSKQn2.js";import{N as R,B as q,c as V,a as H,_ as O,q as S,m as A,g as B,x as F,h as G,p as W}from"./naive-ui-CVyvdmg1.js";import"./markdown-BNJfEgCA.js";const X={class:"card-content"},Y={class:"header-row"},Z={class:"project-name"},J={class:"stats-row"},Q=$({__name:"ProjectCard",props:{project:{type:Object,required:!0}},emits:["click","delete"],setup(f,{emit:h}){const y=f,g=h,_=e(!1);function k(){g("click")}function j(){g("delete",y.project)}const w=a(()=>{var e,a;return(null==(e=y.project)?void 0:e.displayName)||(null==(a=y.project)?void 0:a.name)||""}),x=a(()=>{var e,a,t;return(null==(e=y.project)?void 0:e.path)||(null==(a=y.project)?void 0:a.fullPath)||(null==(t=y.project)?void 0:t.name)||""}),C=a(()=>{var e;return(null==(e=y.project)?void 0:e.sessionCount)||0}),b=a(()=>{var e;return null==(e=y.project)?void 0:e.lastUsed}),$=a(()=>{var e;const a=["#18a058","#2080f0","#f0a020","#d03050","#9333ea","#06b6d4"];return a[((null==(e=y.project)?void 0:e.name)||"").split("").reduce((e,a)=>e+a.charCodeAt(0),0)%a.length]});function z(e){if(!e)return"";const a=new Date(e),t=new Date-a;return t<6e4?"刚刚":t<36e5?`${Math.floor(t/6e4)}分钟前`:t<864e5?`${Math.floor(t/36e5)}小时前`:t<6048e5?`${Math.floor(t/864e5)}天前`:a.toLocaleDateString("zh-CN")}return(e,a)=>(s(),t("div",{class:"project-card",onClick:k,onMouseenter:a[0]||(a[0]=e=>_.value=!0),onMouseleave:a[1]||(a[1]=e=>_.value=!1)},[l("div",X,[l("div",Y,[n(c(R),{size:"18",color:$.value},{default:r(()=>[n(c(I))]),_:1},8,["color"]),l("span",Z,i(w.value),1),o(n(c(q),{text:"",type:"error",size:"tiny",class:"delete-btn",onClick:d(j,["stop"])},{icon:r(()=>[n(c(R),{size:"14"},{default:r(()=>[n(c(T))]),_:1})]),_:1},512),[[u,_.value]])]),n(c(V),{depth:"3",class:"project-path"},{default:r(()=>[p(i(x.value),1)]),_:1}),l("div",J,[n(c(H),{size:"tiny",bordered:!1,type:"success"},{default:r(()=>[p(i(C.value)+" 会话 ",1)]),_:1}),b.value?(s(),v(c(V),{key:0,depth:"3",class:"last-used"},{default:r(()=>[p(i(z(b.value)),1)]),_:1})):m("",!0)])])],32))}},[["__scopeId","data-v-3a18e66d"]]),ee={class:"project-list-container"},ae={class:"header"},te={class:"header-text"},se={key:0,class:"loading-container"},le={key:3,class:"projects-grid"},ne={class:"search-hint"},oe={style:{"margin-bottom":"16px"}},re={key:0,style:{"max-height":"60vh","overflow-y":"auto"}},ce={class:"search-result-header"},ie={class:"search-result-title"},ue=$({__name:"ProjectList",setup(o){const u=f(),d=h(),$=z(),T=a(()=>d.meta.channel||"claude"),X=e(""),Y=e([]),Z=e(null),J=e(!1),ue=e(""),de=e(null),pe=e(!1),ve=e(null),me=a(()=>{const e=X.value.toLowerCase();return Y.value.filter(a=>{const t=(a.displayName||"").toLowerCase(),s=(a.fullPath||"").toLowerCase();return t.includes(e)||s.includes(e)})});function fe(e){const a=d.meta.channel||"claude";u.push({name:`${a}-sessions`,params:{projectName:e}})}async function he(){const e=Y.value.map(e=>e.name);await $.saveProjectOrder(e)}function ye(e){const a=e.fullPath||e.name,t=e.sessionCount||0;P.warning({title:"删除项目",content:`确定要删除项目 "${a}" 吗?\n\n当前项目包含 ${t} 个会话,删除后所有会话数据将无法恢复!`,positiveText:"确定删除",negativeText:"取消",onPositiveClick:async()=>{try{await $.deleteProject(e.name),N.success("项目已删除")}catch(a){N.error("删除失败: "+a.message)}}})}async function ge(){try{const a="codex"===(e=T.value||"claude")?"codex":"gemini"===e?"gemini":"opencode"===e?"opencode":"omp"===e?"omp":"claude",t=await L(a);if("manual"===(null==t?void 0:t.method))return void N.warning("自动复制失败,已弹出手动复制框");N.success("基础启动命令已复制到剪贴板")}catch(a){N.error("复制失败: "+a.message)}var e}async function _e(){if(ue.value){pe.value=!0;try{const e=await M(ue.value,35,T.value);de.value=e}catch(e){N.error("搜索失败: "+e.message)}finally{pe.value=!1}}}function ke(e,a){if(!a||!e)return e;const t=new RegExp(`(${a})`,"gi");return e.replace(t,'<mark style="background-color: #ffd700; padding: 2px 4px; border-radius: 2px; font-weight: 600;">$1</mark>')}function je(e){(e.metaKey||e.ctrlKey)&&"k"===e.key&&(e.preventDefault(),J.value=!0,C(()=>{var e;null==(e=ve.value)||e.focus()}))}return y(()=>$.projects,e=>{Y.value=[...e]},{immediate:!0}),y(J,e=>{e||(ue.value="",de.value=null)}),y(T,e=>{$.setChannel(e),$.fetchProjects()},{immediate:!0}),g(()=>{document.addEventListener("keydown",je)}),_(()=>{document.removeEventListener("keydown",je)}),(e,a)=>(s(),t("div",ee,[l("div",ae,[l("div",te,[n(c(O),{style:{margin:"0"}},{default:r(()=>[...a[4]||(a[4]=[p("我的项目",-1)])]),_:1}),n(c(V),{depth:"3"},{default:r(()=>[...a[5]||(a[5]=[p("选择一个项目查看会话,拖拽可调整顺序",-1)])]),_:1})]),n(c(A),null,{default:r(()=>[n(c(q),{type:"primary",size:"medium",onClick:ge},{icon:r(()=>[n(c(R),null,{default:r(()=>[n(c(E))]),_:1})]),default:r(()=>[a[6]||(a[6]=p(" 新建项目 ",-1))]),_:1}),n(c(S),{value:X.value,"onUpdate:value":a[0]||(a[0]=e=>X.value=e),placeholder:"搜索项目...",clearable:"",class:"search-input"},{prefix:r(()=>[n(c(R),null,{default:r(()=>[n(c(K))]),_:1})]),_:1},8,["value"])]),_:1})]),l("div",{class:"content",ref_key:"contentEl",ref:Z},[c($).loading?(s(),t("div",se,[n(c(B),{size:"large"},{description:r(()=>[...a[7]||(a[7]=[p(" 加载项目列表... ",-1)])]),_:1})])):c($).error?(s(),v(c(F),{key:1,type:"error",title:"加载失败",style:{"margin-top":"20px"}},{default:r(()=>[p(i(c($).error),1)]),_:1})):X.value?(s(),t("div",le,[(s(!0),t(k,null,j(me.value,e=>(s(),v(Q,{key:e.name,project:e,onClick:a=>fe(e.name),onDelete:ye},null,8,["project","onClick"]))),128))])):(s(),v(c(b),{key:2,modelValue:Y.value,"onUpdate:modelValue":a[1]||(a[1]=e=>Y.value=e),"item-key":"name",class:"projects-grid","ghost-class":"ghost","chosen-class":"chosen","drag-class":"drag",animation:"200",onEnd:he},{item:r(({element:e})=>[n(Q,{project:e,onClick:a=>fe(e.name),onDelete:ye},null,8,["project","onClick"])]),_:1},8,["modelValue"])),c($).loading||c($).error||0!==c($).projects.length?m("",!0):(s(),v(c(G),{key:4,description:"没有找到项目",style:{"margin-top":"60px"}},{icon:r(()=>[n(c(R),null,{default:r(()=>[n(c(I))]),_:1})]),_:1}))],512),l("div",ne,[n(c(V),{depth:"3",style:{"font-size":"12px"}},{default:r(()=>[...a[8]||(a[8]=[l("kbd",null,"Cmd",-1),p(" / ",-1),l("kbd",null,"Ctrl",-1),p(" + ",-1),l("kbd",null,"K",-1),p(" 全局搜索 ",-1)])]),_:1})]),n(c(W),{show:J.value,"onUpdate:show":a[3]||(a[3]=e=>J.value=e),preset:"card",title:"全局搜索",style:x([{width:"800px","max-width":"90vw"},{marginTop:"80px"}])},{default:r(()=>[l("div",oe,[n(c(S),{ref_key:"globalSearchInputRef",ref:ve,value:ue.value,"onUpdate:value":a[2]||(a[2]=e=>ue.value=e),placeholder:"搜索所有项目的对话内容...",clearable:"",onKeyup:w(_e,["enter"]),disabled:pe.value},{prefix:r(()=>[n(c(R),null,{default:r(()=>[n(c(K))]),_:1})]),suffix:r(()=>[n(c(q),{text:"",onClick:_e,disabled:!ue.value||pe.value,loading:pe.value},{default:r(()=>[...a[9]||(a[9]=[p(" 搜索 ",-1)])]),_:1},8,["disabled","loading"])]),_:1},8,["value","disabled"])]),de.value?(s(),t("div",re,[n(c(F),{type:"info",style:{"margin-bottom":"16px"}},{default:r(()=>[p(' 关键词 "'+i(de.value.keyword)+'" 共找到 '+i(de.value.totalMatches)+" 处匹配 ",1)]),_:1}),(s(!0),t(k,null,j(de.value.sessions,e=>(s(),t("div",{key:`${e.projectName}-${e.sessionId}`,class:"search-result-item"},[l("div",ce,[l("div",ie,[n(c(V),{strong:"",style:{"font-size":"15px","font-weight":"700"}},{default:r(()=>[p(i(e.projectDisplayName),1)]),_:2},1024),n(c(V),{depth:"2",style:{margin:"0 8px"}},{default:r(()=>[...a[10]||(a[10]=[p("·",-1)])]),_:1}),n(c(V),{strong:""},{default:r(()=>[p(i(e.alias?`${e.alias} (${e.sessionId.substring(0,8)})`:e.sessionId.substring(0,8)),1)]),_:2},1024),n(c(H),{size:"small",bordered:!1},{default:r(()=>[p(i(e.matchCount)+" 个匹配",1)]),_:2},1024)]),n(c(q),{size:"small",type:"primary",onClick:a=>async function(e,a){try{const{copyResult:t}=await D(e,a,T.value);"manual"===(null==t?void 0:t.method)?N.warning("自动复制失败,已弹出手动复制框"):N.success("启动命令已复制到剪贴板"),J.value=!1}catch(t){N.error("复制失败: "+t.message)}}(e.projectName,e.sessionId)},{icon:r(()=>[n(c(R),null,{default:r(()=>[n(c(U))]),_:1})]),default:r(()=>[a[11]||(a[11]=p(" 使用对话 ",-1))]),_:1},8,["onClick"])]),(s(!0),t(k,null,j(e.matches,(e,a)=>(s(),t("div",{key:a,class:"search-match"},[n(c(H),{size:"tiny",type:"user"===e.role?"info":"success",bordered:!1},{default:r(()=>[p(i("user"===e.role?"用户":"助手"),1)]),_:2},1032,["type"]),n(c(V),{depth:"3",class:"search-match-text",innerHTML:ke(e.context,de.value.keyword)},null,8,["innerHTML"])]))),128))]))),128)),0===de.value.sessions.length?(s(),v(c(G),{key:0,description:"没有找到匹配的内容"})):m("",!0)])):m("",!0)]),_:1},8,["show"])]))}},[["__scopeId","data-v-f9fdf45c"]]);export{ue as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.project-card[data-v-3a18e66d]{padding:12px 14px;cursor:pointer;transition:all .25s cubic-bezier(.4,0,.2,1);border-radius:8px;border:1px solid var(--border-primary);box-shadow:0 1px 3px #0000000a;background:var(--bg-primary);position:relative;overflow:hidden}.project-card[data-v-3a18e66d]:before{content:"";position:absolute;top:0;left:0;width:3px;height:100%;background:linear-gradient(180deg,#18a058,#18a05866);opacity:0;transition:opacity .25s ease}.project-card[data-v-3a18e66d]:hover{transform:translateY(-3px);box-shadow:0 8px 20px #18a0581f,0 2px 6px #0000000f;border-color:#18a05866}.project-card[data-v-3a18e66d]:hover:before{opacity:1}.card-content[data-v-3a18e66d]{display:flex;flex-direction:column;gap:6px}.header-row[data-v-3a18e66d]{display:flex;align-items:center;gap:8px}.project-name[data-v-3a18e66d]{font-size:14px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;color:var(--text-primary);letter-spacing:-.2px}.delete-btn[data-v-3a18e66d]{opacity:.7;transition:opacity .2s;padding:2px!important}.delete-btn[data-v-3a18e66d]:hover{opacity:1}.project-path[data-v-3a18e66d]{font-size:11px;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.3}.stats-row[data-v-3a18e66d]{display:flex;align-items:center;justify-content:space-between;margin-top:4px;padding-top:8px;border-top:1px dashed var(--border-primary)}.stats-row[data-v-3a18e66d] .n-tag{font-size:11px;font-weight:600;padding:0 6px;height:18px;border-radius:4px}.last-used[data-v-3a18e66d]{font-size:11px;font-weight:500}.project-list-container[data-v-f9fdf45c]{width:100%;height:100%;display:flex;flex-direction:column;box-sizing:border-box}.header[data-v-f9fdf45c]{flex-shrink:0;padding:24px 24px 20px;background:var(--gradient-bg);border-bottom:1px solid var(--border-primary);display:flex;justify-content:space-between;align-items:flex-start;gap:24px;position:relative}.header[data-v-f9fdf45c]:after{content:"";position:absolute;bottom:0;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent,rgba(24,160,88,.1),transparent)}.header-text[data-v-f9fdf45c]{flex:1}.header-text[data-v-f9fdf45c] .n-h2{font-size:26px;font-weight:700;background:linear-gradient(135deg,#1f2937,#374151);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;letter-spacing:-.5px}[data-theme=dark] .header-text[data-v-f9fdf45c] .n-h2{background:linear-gradient(135deg,#f9fafb,#d1d5db);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.search-input[data-v-f9fdf45c]{width:320px;flex-shrink:0}.search-input[data-v-f9fdf45c] .n-input{border-radius:10px;background:var(--bg-primary);border:1px solid var(--border-primary);transition:all .2s ease}.search-input[data-v-f9fdf45c] .n-input:hover{border-color:var(--border-secondary);box-shadow:var(--shadow-sm)}.search-input[data-v-f9fdf45c] .n-input:focus-within{border-color:#18a058;box-shadow:0 0 0 3px #18a0581a}.content[data-v-f9fdf45c]{flex:1;overflow-y:auto;padding:20px 24px 24px;background:var(--gradient-bg)}.loading-container[data-v-f9fdf45c]{display:flex;justify-content:center;align-items:center;min-height:400px}.projects-grid[data-v-f9fdf45c]{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px}.ghost[data-v-f9fdf45c]{opacity:.4}.chosen[data-v-f9fdf45c]{transform:scale(1.05);box-shadow:0 8px 24px #0003!important;cursor:move!important}.drag[data-v-f9fdf45c]{opacity:.8;transform:rotate(2deg);box-shadow:0 12px 32px #00000040!important}.search-hint[data-v-f9fdf45c]{position:fixed;bottom:24px;left:24px;padding:10px 14px;background:#ffffffe6;border:1px solid var(--border-primary);border-radius:8px;box-shadow:0 4px 12px #00000014;z-index:10;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:.8;transition:all .2s ease}[data-theme=dark] .search-hint[data-v-f9fdf45c]{background:#1e293bf2;border:1px solid rgba(71,85,105,.6);box-shadow:0 4px 16px #0006}.search-hint[data-v-f9fdf45c]:hover{opacity:1;transform:translateY(-2px);box-shadow:0 6px 16px #0000001f}[data-theme=dark] .search-hint[data-v-f9fdf45c]:hover{box-shadow:0 6px 20px #00000080}.search-hint kbd[data-v-f9fdf45c]{display:inline-block;padding:4px 8px;font-family:SF Mono,Monaco,monospace;font-size:12px;line-height:1.4;font-weight:600;color:var(--text-secondary);background:var(--bg-primary);border:1px solid var(--border-secondary);border-radius:4px;box-shadow:0 2px 0 var(--border-primary),0 1px 3px #0000001a}[data-theme=dark] .search-hint kbd[data-v-f9fdf45c]{background:#33415599;border:1px solid rgba(71,85,105,.8);box-shadow:0 2px #1e293bcc,0 1px 4px #0006;color:#e2e8f0}.search-result-item[data-v-f9fdf45c]{margin-bottom:16px;padding:12px;border:1px solid var(--border-primary);border-radius:6px;background:var(--bg-elevated)}.search-result-header[data-v-f9fdf45c]{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.search-result-title[data-v-f9fdf45c]{display:flex;align-items:center;gap:8px}.search-match[data-v-f9fdf45c]{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-f9fdf45c]{flex:1;line-height:1.6}
|