cc-viewer 1.7.6 → 1.7.8
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/README.md +20 -1
- package/cli.js +41 -31
- package/dist/assets/App-Bs6-c4Ov.css +1 -0
- package/dist/assets/App-DtsIVPu6.js +2 -0
- package/dist/assets/{MdxEditorPanel-BQ5MgI4D.js → MdxEditorPanel-DWPAEnpm.js} +1 -1
- package/dist/assets/Mobile-DHwMZG4N.js +1 -0
- package/dist/assets/ProxyStatsModal-D9lTz0-T.js +1 -0
- package/dist/assets/ProxyStatsModal-Dz5GuoUa.css +1 -0
- package/dist/assets/abnfDiagram-VRR7QNED-9ax220Vv.js +1 -0
- package/dist/assets/arc-Cshrl52z.js +1 -0
- package/dist/assets/architectureDiagram-ZJ3FMSHR-QtERolgF.js +1 -0
- package/dist/assets/blockDiagram-677ZJIJ3-BaCd4R0Z.js +1 -0
- package/dist/assets/{c4Diagram-AHTNJAMY-C7JB9PqP.js → c4Diagram-LMCZKHZV-C2QvkF2i.js} +1 -1
- package/dist/assets/{channel-BnzX9zBS.js → channel-ClOiNc35.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-CNEAHnZV.js → chunk-2Q5K7J3B-BvZ7-Ft4.js} +1 -1
- package/dist/assets/{chunk-YZCP3GAM-BOlts_Dm.js → chunk-32BRIVSS-Dg3PlJIt.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-CGL9Lo51.js → chunk-5VM5RSS4-CMJWLQsQ.js} +1 -1
- package/dist/assets/chunk-EX3LRPZG-CmoC-M2G.js +1 -0
- package/dist/assets/{chunk-4BX2VUAB-dVKiUtqe.js → chunk-JWPE2WC7-D5RR93-U.js} +1 -1
- package/dist/assets/chunk-MOJQB5TN-JXgBLjCY.js +1 -0
- package/dist/assets/chunk-RYQCIY6F-gT-v276m.js +1 -0
- package/dist/assets/chunk-V7JOEXUC-Cl-E_fxl.js +1 -0
- package/dist/assets/{chunk-EDXVE4YY-sk9g7szp.js → chunk-VR4S4FIN-BR0zG1Ep.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-BZuKI6JQ.js → chunk-XXDRQBXY-Blw-tThR.js} +1 -1
- package/dist/assets/classDiagram-OUVF2IWQ-ClkcbuHu.js +1 -0
- package/dist/assets/classDiagram-v2-EOCWNBFH-ClkcbuHu.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-DH4_YiGi.js → cose-bilkent-JH36ORCC-BnDDyyuN.js} +1 -1
- package/dist/assets/cynefin-VYW2F7L2-Ch8_Ihra.js +15 -0
- package/dist/assets/cynefinDiagram-TSTJHNR4-CuX_xTSU.js +1 -0
- package/dist/assets/cytoscape.esm-D0POlJZ1.js +9 -0
- package/dist/assets/dagre-VKFMJZFB-DHDO7DUw.js +1 -0
- package/dist/assets/diagram-FQU43EPY-CxDVpL25.js +1 -0
- package/dist/assets/diagram-G47NLZAW-DHX7L6pT.js +1 -0
- package/dist/assets/diagram-NH7WQ7WH-HlwtIdWA.js +1 -0
- package/dist/assets/diagram-OA4YK3LP-BnTSu9CG.js +1 -0
- package/dist/assets/diagram-WEI45ONY-C712tBJk.js +1 -0
- package/dist/assets/ebnfDiagram-CCIWWBDH-DzMcH3cz.js +1 -0
- package/dist/assets/erDiagram-Q63AITRT-0fyhQG92.js +1 -0
- package/dist/assets/flowDiagram-23GEKE2U-lgfs10IO.js +1 -0
- package/dist/assets/{ganttDiagram-T4ZO3ILL-BL_XQ958.js → ganttDiagram-NO4QXBWP-_4nnZ3ta.js} +1 -1
- package/dist/assets/gitGraphDiagram-IHSO6WYX-DrgEKJkZ.js +1 -0
- package/dist/assets/graph-CnJdn47S.js +1 -0
- package/dist/assets/{index-CzPHj3iF.js → index-7VeS_xrl.js} +1 -1
- package/dist/assets/{index-BgW_nP_0.js → index-BJ9Xm7_G.js} +1 -1
- package/dist/assets/{index-B3-JUU7J.js → index-BiYKFU6w.js} +1 -1
- package/dist/assets/index-By8aqIvN.js +2 -0
- package/dist/assets/{index-Do5xNTOT.js → index-CBix0zSu.js} +1 -1
- package/dist/assets/{index-DDlAMOiV.js → index-CPAQ767L.js} +1 -1
- package/dist/assets/{index-LjVy-Pzg.js → index-DuUUHxox.js} +1 -1
- package/dist/assets/{index-vImWXiMM.js → index-_pxC-jz_.js} +1 -1
- package/dist/assets/infoDiagram-FWYZ7A6U-Cki8RIbq.js +1 -0
- package/dist/assets/{ishikawaDiagram-UXIWVN3A-D3odpv9_.js → ishikawaDiagram-FXEZZL3T-DHHu_UGe.js} +1 -1
- package/dist/assets/{journeyDiagram-VCZTEJTY-Dj3NaOWF.js → journeyDiagram-5HDEW3XC-Bx06Sp3y.js} +1 -1
- package/dist/assets/{jszip.min-GmVScWAV.js → jszip.min-Bn5J9HN-.js} +1 -1
- package/dist/assets/{kanban-definition-6JOO6SKY-GKjJaLwD.js → kanban-definition-HUTT4EX6-BuEuKkQp.js} +1 -1
- package/dist/assets/layout-CCUE9ZRw.js +1 -0
- package/dist/assets/{linear-CLk9nvqn.js → linear-BJRZNsdX.js} +1 -1
- package/dist/assets/map-DQeg2hXV.js +1 -0
- package/dist/assets/mermaid.core-DH-sQJgr.js +7 -0
- package/dist/assets/{mindmap-definition-QFDTVHPH-DgDD6Jbu.js → mindmap-definition-LN4V7U3C-DYtgJ2f4.js} +1 -1
- package/dist/assets/pegDiagram-2B236MQR-BVN6OIEN.js +1 -0
- package/dist/assets/pieDiagram-ENE6RG2P-mVeQtQTD.js +1 -0
- package/dist/assets/quadrantDiagram-ABIIQ3AL-Croqf1qH.js +1 -0
- package/dist/assets/railroadDiagram-RFXS5EU6-jkGuNfMY.js +1 -0
- package/dist/assets/{requirementDiagram-MS252O5E-CtBOCtbk.js → requirementDiagram-TGXJPOKE-Cwb2FU3N.js} +1 -1
- package/dist/assets/sankeyDiagram-HTMAVEWB-D-HWOuX0.js +1 -0
- package/dist/assets/seqResourceLoaders-C-pxCjNh.js +2 -0
- package/dist/assets/sequenceDiagram-DBY2YBRQ-Bh3XgPxy.js +1 -0
- package/dist/assets/sizeCapture-X5ZJPWSS-C_acEkFf.js +1 -0
- package/dist/assets/stateDiagram-2N3HPSRC-CmbRj6R7.js +1 -0
- package/dist/assets/stateDiagram-v2-6OUMAXLB-ClrX-Ofc.js +1 -0
- package/dist/assets/swimlanes-5IMT3BWC-CIfoRrLM.js +2 -0
- package/dist/assets/swimlanesDiagram-G3AALYLV-DZuW0-dQ.js +1 -0
- package/dist/assets/{timeline-definition-GMOUNBTQ-B5cPasy_.js → timeline-definition-FHXFAJF6-CK8MkCLz.js} +1 -1
- package/dist/assets/vendor-antd-DNUTLr77.js +17 -0
- package/dist/assets/{vendor-codemirror-_NbrtdQc.js → vendor-codemirror-6ck0hLRU.js} +1 -1
- package/dist/assets/vendor-markdown-DBlfs4dR.js +3 -0
- package/dist/assets/vendor-mdxeditor-DT0rad22.js +2 -0
- package/dist/assets/vendor-qrcode-CPmWG6hm.js +6 -0
- package/dist/assets/vendor-virtuoso-YwK7i7L6.js +1 -0
- package/dist/assets/{vennDiagram-DHZGUBPP-a_ybZDST.js → vennDiagram-L72KCM5P-CWA2Gdi_.js} +1 -1
- package/dist/assets/wardleyDiagram-EHGQE667-U22kgZ8v.js +1 -0
- package/dist/assets/xychartDiagram-FW5EYKEG-BiGsMUKC.js +1 -0
- package/dist/index.html +4 -4
- package/findcc.js +281 -5
- package/package.json +1 -1
- package/server/lib/proxy-stats.js +75 -32
- package/server/pty-manager.js +3 -8
- package/server/routes/preferences.js +71 -1
- package/server/system-prompt-templates/presets/index.json +8 -0
- package/server/system-prompt-templates/presets/kimi-k3.md +68 -0
- package/src/utils/helpers.js +6 -1
- package/dist/assets/App-3VM471BO.js +0 -2
- package/dist/assets/App-CCDFxj11.css +0 -1
- package/dist/assets/Mobile-wwcXmnrU.js +0 -1
- package/dist/assets/UnifiedProxyRetryPage-BpcKIn9N.js +0 -1
- package/dist/assets/UnifiedProxyRetryPage-CFz4vOH5.css +0 -1
- package/dist/assets/_baseUniq-yiNE5Ckw.js +0 -1
- package/dist/assets/arc-M-eBYYbN.js +0 -1
- package/dist/assets/architectureDiagram-Q4EWVU46-CtweeLKi.js +0 -1
- package/dist/assets/blockDiagram-DXYQGD6D-DCVQ-go_.js +0 -1
- package/dist/assets/chunk-4TB4RGXK-CQjD9-fD.js +0 -1
- package/dist/assets/chunk-OYMX7WX6-DaN0AdQD.js +0 -1
- package/dist/assets/classDiagram-6PBFFD2Q-CumOMdO7.js +0 -1
- package/dist/assets/classDiagram-v2-HSJHXN6E-CumOMdO7.js +0 -1
- package/dist/assets/clone-C3SZDzs1.js +0 -1
- package/dist/assets/cytoscape.esm-CT1mZavR.js +0 -9
- package/dist/assets/dagre-KV5264BT-RqusStYI.js +0 -1
- package/dist/assets/diagram-5BDNPKRD-8haVbBnt.js +0 -1
- package/dist/assets/diagram-G4DWMVQ6-CsrJNUhT.js +0 -1
- package/dist/assets/diagram-MMDJMWI5-1luPOM38.js +0 -1
- package/dist/assets/diagram-TYMM5635-Dad1XR3W.js +0 -1
- package/dist/assets/erDiagram-SMLLAGMA-Br6-kbp9.js +0 -1
- package/dist/assets/flowDiagram-DWJPFMVM-DuX1mbA7.js +0 -1
- package/dist/assets/gitGraphDiagram-UUTBAWPF-DzvW5FEs.js +0 -1
- package/dist/assets/graph-CbUmu0Oe.js +0 -1
- package/dist/assets/index-BjNLFJ0z.js +0 -2
- package/dist/assets/infoDiagram-42DDH7IO-DGsgRji7.js +0 -1
- package/dist/assets/layout-Bhk66H0F.js +0 -1
- package/dist/assets/mermaid.core-DH-GfeNS.js +0 -7
- package/dist/assets/min-DQftOGfo.js +0 -1
- package/dist/assets/pieDiagram-DEJITSTG-jy1yFaxA.js +0 -1
- package/dist/assets/quadrantDiagram-34T5L4WZ-BdvMwrSD.js +0 -1
- package/dist/assets/sankeyDiagram-XADWPNL6-DEFlQp-h.js +0 -1
- package/dist/assets/seqResourceLoaders-CDRtUSxI.js +0 -2
- package/dist/assets/sequenceDiagram-FGHM5R23-0F2R8Ky0.js +0 -1
- package/dist/assets/stateDiagram-FHFEXIEX-D4xOwcSW.js +0 -1
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-DSf_BSF8.js +0 -1
- package/dist/assets/vendor-antd-DeqwrDxf.js +0 -17
- package/dist/assets/vendor-markdown-DOJHsAxX.js +0 -3
- package/dist/assets/vendor-mdxeditor-DG0Nyxw5.js +0 -2
- package/dist/assets/vendor-qrcode-B3HN--HO.js +0 -6
- package/dist/assets/vendor-virtuoso-BvrezPQx.js +0 -1
- package/dist/assets/wardley-RL74JXVD-CKla3KF8.js +0 -1
- package/dist/assets/wardleyDiagram-NUSXRM2D-BKke2aih.js +0 -1
- package/dist/assets/xychartDiagram-5P7HB3ND-CozWtcdf.js +0 -1
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { mkdirSync } from 'node:fs';
|
|
10
10
|
import { join } from 'node:path';
|
|
11
11
|
import { AsyncWriteQueue } from './async-write-queue.js';
|
|
12
|
+
import { reportSwallowed } from './error-report.js';
|
|
12
13
|
|
|
13
14
|
// ── Detail record schema ──────────────────────────────────────────────
|
|
14
15
|
// Appends one JSON line after each proxied LLM API request completes. Fields align with llm-retry-proxy's retry records.
|
|
@@ -120,7 +121,11 @@ export function appendRecord(filePath, record, onDone) {
|
|
|
120
121
|
try {
|
|
121
122
|
mkdirSync(dir, { recursive: true });
|
|
122
123
|
_dirsEnsured.add(dir);
|
|
123
|
-
} catch {
|
|
124
|
+
} catch (err) {
|
|
125
|
+
// No permission / read-only fs — the queued append will surface the failure
|
|
126
|
+
// as a silent no-op. Record it so the cause isn't fully invisible.
|
|
127
|
+
reportSwallowed('proxyStats.mkdir', err);
|
|
128
|
+
}
|
|
124
129
|
}
|
|
125
130
|
_statsWriteQueue.appendTo(filePath, JSON.stringify(record) + '\n', onDone);
|
|
126
131
|
}
|
|
@@ -147,9 +152,9 @@ export function emitProxyStatsUpdate(fileName) {
|
|
|
147
152
|
try {
|
|
148
153
|
_statsUpdateListener(fileName);
|
|
149
154
|
} catch (err) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
155
|
+
// A throwing listener would silently break stats propagation (the SSE push,
|
|
156
|
+
// recompute trigger, etc.). Record it instead of swallowing silently.
|
|
157
|
+
reportSwallowed('proxyStats.emit', err);
|
|
153
158
|
}
|
|
154
159
|
}
|
|
155
160
|
|
|
@@ -196,6 +201,37 @@ export function computeStreak(records) {
|
|
|
196
201
|
return { current: { type: curType || 'success', count: curCount }, worstFailure };
|
|
197
202
|
}
|
|
198
203
|
|
|
204
|
+
/**
|
|
205
|
+
* Retry-burden bucket definitions: turns the raw retry-count distribution into
|
|
206
|
+
* actionable buckets (0 / 1-5 / 6-20 / 21-50 / >50). Aligned with llm-retry-proxy's
|
|
207
|
+
* renderBurdenChart buckets. `key` is the stable i18n id (ui.proxyStats.retryBurdenBuckets.<key>).
|
|
208
|
+
*
|
|
209
|
+
* `range` is [min, max] with `max: null` meaning "open ended". Deliberately not
|
|
210
|
+
* `Infinity`: this table's shape is mirrored into the stats JSON, and
|
|
211
|
+
* JSON.stringify(Infinity) is `null` anyway — spelling it null keeps the
|
|
212
|
+
* in-process value and the serialized value identical.
|
|
213
|
+
*
|
|
214
|
+
* Exposed for tests that want to assert the frozen bucket contract without re-typing
|
|
215
|
+
* the boundaries.
|
|
216
|
+
*/
|
|
217
|
+
export const RETRY_BURDEN_BUCKETS = [
|
|
218
|
+
{ key: '0', range: [0, 0] },
|
|
219
|
+
{ key: '1_5', range: [1, 5] },
|
|
220
|
+
{ key: '6_20', range: [6, 20] },
|
|
221
|
+
{ key: '21_50', range: [21, 50] },
|
|
222
|
+
{ key: 'over50', range: [51, null] },
|
|
223
|
+
];
|
|
224
|
+
|
|
225
|
+
// Bump the bucket whose range covers `retries`. Buckets are ordered low→high
|
|
226
|
+
// and non-overlapping, so the first match wins; returns void (mutates `counts`).
|
|
227
|
+
// A null upper bound is open ended.
|
|
228
|
+
function bumpBurden(counts, retries) {
|
|
229
|
+
for (const b of counts) {
|
|
230
|
+
const [min, max] = b.range;
|
|
231
|
+
if (retries >= min && (max === null || retries <= max)) { b.count++; return; }
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
199
235
|
/**
|
|
200
236
|
* Aggregate a batch of detail records into a stats structure. Called by stats-worker, also used directly by /api/proxy-stats.
|
|
201
237
|
*
|
|
@@ -233,6 +269,8 @@ export function aggregateRecords(records, opts = {}) {
|
|
|
233
269
|
const byModelMap = new Map();
|
|
234
270
|
const byPathMap = new Map();
|
|
235
271
|
const byProfileMap = new Map();
|
|
272
|
+
// Retry-burden buckets bumped inline during the main loop (avoids a second O(n) pass).
|
|
273
|
+
const retryBurdenCounts = RETRY_BURDEN_BUCKETS.map(b => ({ ...b, count: 0 }));
|
|
236
274
|
let slowest = null;
|
|
237
275
|
let fastest = null;
|
|
238
276
|
|
|
@@ -249,21 +287,22 @@ export function aggregateRecords(records, opts = {}) {
|
|
|
249
287
|
}
|
|
250
288
|
durations.push(dur);
|
|
251
289
|
retryDist.set(retries, (retryDist.get(retries) || 0) + 1);
|
|
290
|
+
bumpBurden(retryBurdenCounts, retries);
|
|
252
291
|
for (const c of (r.retry_codes || [])) {
|
|
253
292
|
retryCodeCounts.set(c, (retryCodeCounts.get(c) || 0) + 1);
|
|
254
293
|
}
|
|
255
294
|
// by model
|
|
256
295
|
const model = r.model || '(unknown)';
|
|
257
296
|
if (!byModelMap.has(model)) byModelMap.set(model, newBucket());
|
|
258
|
-
accBucket(byModelMap.get(model), retries, ok, dur);
|
|
297
|
+
accBucket(byModelMap.get(model), retries, ok, dur, r.retry_codes);
|
|
259
298
|
// by path
|
|
260
299
|
const path = r.path || '/';
|
|
261
300
|
if (!byPathMap.has(path)) byPathMap.set(path, newBucket());
|
|
262
|
-
accBucket(byPathMap.get(path), retries, ok, dur);
|
|
301
|
+
accBucket(byPathMap.get(path), retries, ok, dur, r.retry_codes);
|
|
263
302
|
// by profile
|
|
264
303
|
const profileKey = r.profile_id || 'default';
|
|
265
304
|
if (!byProfileMap.has(profileKey)) byProfileMap.set(profileKey, { ...newBucket(), profile_name: r.profile_name || 'Default' });
|
|
266
|
-
accBucket(byProfileMap.get(profileKey), retries, ok, dur);
|
|
305
|
+
accBucket(byProfileMap.get(profileKey), retries, ok, dur, r.retry_codes);
|
|
267
306
|
// slowest / fastest (fastest only counts successes with dur>0)
|
|
268
307
|
const candidate = { ts: r.ts, path, model, attempts: r.attempts, retries, duration_ms: dur, final_status: r.final_status, retry_codes: r.retry_codes || [] };
|
|
269
308
|
if (!slowest || dur > slowest.duration_ms) slowest = candidate;
|
|
@@ -301,10 +340,11 @@ export function aggregateRecords(records, opts = {}) {
|
|
|
301
340
|
},
|
|
302
341
|
byModel: mapToSortedArray(byModelMap, 'model'),
|
|
303
342
|
byPath: mapToSortedArray(byPathMap, 'path').slice(0, 10), // Top 10 path
|
|
304
|
-
byProfile:
|
|
343
|
+
byProfile: mapToSortedArray(byProfileMap, 'profile_id', 'profile_name'),
|
|
305
344
|
retryDistribution: [...retryDist.entries()]
|
|
306
345
|
.sort((a, b) => a[0] - b[0])
|
|
307
346
|
.map(([retries, count]) => ({ retries, count })),
|
|
347
|
+
retryBurden: retryBurdenCounts,
|
|
308
348
|
retryCodes: [...retryCodeCounts.entries()]
|
|
309
349
|
.sort((a, b) => b[1] - a[1])
|
|
310
350
|
.map(([code, count]) => ({ code, count })),
|
|
@@ -315,10 +355,10 @@ export function aggregateRecords(records, opts = {}) {
|
|
|
315
355
|
}
|
|
316
356
|
|
|
317
357
|
function newBucket() {
|
|
318
|
-
return { requests: 0, retries: 0, succeeded: 0, firstOk: 0, failed: 0, durations: [] };
|
|
358
|
+
return { requests: 0, retries: 0, succeeded: 0, firstOk: 0, failed: 0, durations: [], retryCodeCounts: new Map() };
|
|
319
359
|
}
|
|
320
360
|
|
|
321
|
-
function accBucket(b, retries, ok, dur) {
|
|
361
|
+
function accBucket(b, retries, ok, dur, retryCodes) {
|
|
322
362
|
b.requests++;
|
|
323
363
|
b.retries += retries;
|
|
324
364
|
if (ok) {
|
|
@@ -328,46 +368,48 @@ function accBucket(b, retries, ok, dur) {
|
|
|
328
368
|
b.failed++;
|
|
329
369
|
}
|
|
330
370
|
b.durations.push(dur);
|
|
371
|
+
for (const c of (retryCodes || [])) {
|
|
372
|
+
if (Number.isFinite(c)) b.retryCodeCounts.set(c, (b.retryCodeCounts.get(c) || 0) + 1);
|
|
373
|
+
}
|
|
331
374
|
}
|
|
332
375
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
upstreamAvailabilityPct: b.requests ? round2(b.firstOk / b.requests * 100) : 0,
|
|
346
|
-
p95Ms: round3(percentile(b.durations, 0.95)),
|
|
347
|
-
};
|
|
348
|
-
arr.push(entry);
|
|
349
|
-
}
|
|
350
|
-
arr.sort((a, b) => b.requests - a.requests);
|
|
351
|
-
return arr;
|
|
376
|
+
// Finalize a bucket's per-bucket retry-code map into top-5 array + dominant fail code.
|
|
377
|
+
function finalizeRetryCodes(b) {
|
|
378
|
+
const arr = [...(b.retryCodeCounts?.entries() || [])]
|
|
379
|
+
.sort((a, c) => c[1] - a[1])
|
|
380
|
+
.slice(0, 5)
|
|
381
|
+
.map(([code, count]) => ({ code, count }));
|
|
382
|
+
const top = arr[0] || null;
|
|
383
|
+
return {
|
|
384
|
+
retryCodeCounts: arr,
|
|
385
|
+
dominantFailStatus: top ? top.code : null,
|
|
386
|
+
dominantFailCount: top ? top.count : 0,
|
|
387
|
+
};
|
|
352
388
|
}
|
|
353
389
|
|
|
354
|
-
//
|
|
355
|
-
|
|
390
|
+
// Flatten a dimension's bucket map into a sorted entry array.
|
|
391
|
+
// `extraName` (optional, used by byProfile) emits an extra `[extraName]: bucket[extraName] || k`
|
|
392
|
+
// field — the only structural difference between the byModel/byPath and byProfile outputs.
|
|
393
|
+
function mapToSortedArray(map, keyName, extraName) {
|
|
356
394
|
const arr = [];
|
|
357
395
|
for (const [k, b] of map.entries()) {
|
|
358
396
|
b.durations.sort((a, c) => a - c);
|
|
397
|
+
const fin = finalizeRetryCodes(b);
|
|
359
398
|
const entry = {
|
|
360
399
|
[keyName]: k,
|
|
361
|
-
[extraName]: b[extraName] || k,
|
|
362
400
|
requests: b.requests,
|
|
363
401
|
retries: b.retries,
|
|
364
402
|
succeeded: b.succeeded,
|
|
365
403
|
firstOk: b.firstOk,
|
|
366
404
|
failed: b.failed,
|
|
405
|
+
retryCodeCounts: fin.retryCodeCounts,
|
|
406
|
+
dominantFailStatus: fin.dominantFailStatus,
|
|
407
|
+
dominantFailCount: fin.dominantFailCount,
|
|
367
408
|
availabilityPct: b.requests ? round2(b.succeeded / b.requests * 100) : 0,
|
|
368
409
|
upstreamAvailabilityPct: b.requests ? round2(b.firstOk / b.requests * 100) : 0,
|
|
369
410
|
p95Ms: round3(percentile(b.durations, 0.95)),
|
|
370
411
|
};
|
|
412
|
+
if (extraName) entry[extraName] = b[extraName] || k;
|
|
371
413
|
arr.push(entry);
|
|
372
414
|
}
|
|
373
415
|
arr.sort((a, b) => b.requests - a.requests);
|
|
@@ -386,6 +428,7 @@ function emptyStats() {
|
|
|
386
428
|
byPath: [],
|
|
387
429
|
byProfile: [],
|
|
388
430
|
retryDistribution: [],
|
|
431
|
+
retryBurden: [],
|
|
389
432
|
retryCodes: [],
|
|
390
433
|
slowest: null,
|
|
391
434
|
fastest: null,
|
package/server/pty-manager.js
CHANGED
|
@@ -231,14 +231,9 @@ async function _spawnClaudeImpl(proxyPort, cwd, extraArgs = [], claudePath = nul
|
|
|
231
231
|
}
|
|
232
232
|
|
|
233
233
|
const env = { ...process.env };
|
|
234
|
-
//
|
|
235
|
-
// that
|
|
236
|
-
|
|
237
|
-
// be silently replaced during a CCV session.
|
|
238
|
-
const normalizedClaudePath = String(claudePath || '').replace(/\\/g, '/');
|
|
239
|
-
if (normalizedClaudePath.includes('/.codefuse/fuse/engine/bin/claude/')) {
|
|
240
|
-
env.DISABLE_AUTOUPDATER = '1';
|
|
241
|
-
}
|
|
234
|
+
// CCV owns executable selection. Never let the selected Claude replace itself
|
|
235
|
+
// behind that configuration (especially on enterprise allowlisted machines).
|
|
236
|
+
env.DISABLE_AUTOUPDATER = '1';
|
|
242
237
|
env.ANTHROPIC_BASE_URL = `http://127.0.0.1:${proxyPort}`;
|
|
243
238
|
env.CCV_PROXY_MODE = '1'; // 告诉 interceptor.js 不要再启动 server
|
|
244
239
|
env.CCV_LOG_DIR = LOG_DIR; // 让 fork 出的 Claude Code 进程找到同一份 profile.json 等资源
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
3
3
|
import { join, dirname } from 'node:path';
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
|
-
import { LOG_DIR, setLogDir, getClaudeConfigDir } from '../../findcc.js';
|
|
5
|
+
import { LOG_DIR, setLogDir, getClaudeConfigDir, discoverClaudeExecutables, resolveExplicitClaudePath, CLAUDE_EXECUTABLE_PREF_KEY } from '../../findcc.js';
|
|
6
6
|
import { PROFILE_PATH, _defaultConfig, getActiveProfileId, setActiveProfileForWorkspace, _loadProxyProfile, RETRY_CONFIG_PATH, _retryConfigState, _loadRetryConfigState } from '../interceptor.js';
|
|
7
7
|
import { migrateProxyProfileList } from '../lib/interceptor-core.js';
|
|
8
8
|
import { DEFAULT_RETRY_CONFIG, validateRetryConfig, resolveRetryConfig } from '../lib/proxy-retry.js';
|
|
@@ -25,6 +25,22 @@ function stripImConfigs(obj) {
|
|
|
25
25
|
if (obj) for (const id of listPlatforms()) delete obj[id];
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
// Loopback identifies the socket peer, not the browser page that initiated a
|
|
29
|
+
// request. Since the server intentionally has permissive CORS for legacy APIs,
|
|
30
|
+
// sensitive executable-selection endpoints need their own same-origin guard.
|
|
31
|
+
function isSameOriginBrowserRequest(req, parsedUrl) {
|
|
32
|
+
const origin = req.headers?.origin;
|
|
33
|
+
if (!origin) return true; // CLI/native clients do not send Origin.
|
|
34
|
+
if (req.headers?.['sec-fetch-site'] === 'cross-site') return false;
|
|
35
|
+
try {
|
|
36
|
+
const originUrl = new URL(origin);
|
|
37
|
+
if (parsedUrl?.origin) return originUrl.origin.toLowerCase() === parsedUrl.origin.toLowerCase();
|
|
38
|
+
return originUrl.host.toLowerCase() === String(req.headers?.host || '').toLowerCase();
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
28
44
|
// /theme 选择器特征:选项文案高特异、不太可能出现在普通生成输出里(ESC 兜底的门控签名)
|
|
29
45
|
const THEME_PICKER_RE = /Auto \(match terminal\)|colorblind-friendly/;
|
|
30
46
|
|
|
@@ -41,6 +57,9 @@ function preferencesGet(req, res, parsedUrl, isLocal, deps) {
|
|
|
41
57
|
// 全局 auth 与每个项目的 authByProject 覆盖都要剥离(后者同样含明文密码)。
|
|
42
58
|
delete prefs.auth;
|
|
43
59
|
delete prefs.authByProject;
|
|
60
|
+
if (!isLocal || !isSameOriginBrowserRequest(req, parsedUrl)) {
|
|
61
|
+
delete prefs[CLAUDE_EXECUTABLE_PREF_KEY];
|
|
62
|
+
}
|
|
44
63
|
stripImConfigs(prefs); // dingtalk / feishu / … — admin-only, never to a LAN client
|
|
45
64
|
// 项目独立配置(多人共用一台 server 时按项目隔离偏好):非本机(LAN)客户端若当前项目有 fork,
|
|
46
65
|
// 解析出该项目的有效偏好;本机(admin)始终看全局。forks blob 绝不下发,仅以 _projectPrefsKeys
|
|
@@ -99,6 +118,30 @@ function preferencesPost(req, res, parsedUrl, isLocal, deps) {
|
|
|
99
118
|
// 任意项目的覆盖,绕过 admin 门禁。
|
|
100
119
|
delete incoming.auth;
|
|
101
120
|
delete incoming.authByProject;
|
|
121
|
+
// Claude executable selection controls which local program CCV executes.
|
|
122
|
+
// It is machine-global and therefore writable only by the loopback admin,
|
|
123
|
+
// never by a remote/project-scoped preferences client.
|
|
124
|
+
if (!isLocal) {
|
|
125
|
+
delete incoming[CLAUDE_EXECUTABLE_PREF_KEY];
|
|
126
|
+
} else if (Object.prototype.hasOwnProperty.call(incoming, CLAUDE_EXECUTABLE_PREF_KEY)) {
|
|
127
|
+
const value = incoming[CLAUDE_EXECUTABLE_PREF_KEY];
|
|
128
|
+
if (!isSameOriginBrowserRequest(req, parsedUrl)) {
|
|
129
|
+
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
130
|
+
res.end(JSON.stringify({ error: 'Same-origin request required' }));
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (typeof value === 'string' && value.trim()) {
|
|
134
|
+
const resolved = resolveExplicitClaudePath(value.trim().slice(0, 4096));
|
|
135
|
+
if (!resolved) {
|
|
136
|
+
res.writeHead(422, { 'Content-Type': 'application/json' });
|
|
137
|
+
res.end(JSON.stringify({ error: 'Claude executable path is not launchable' }));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
incoming[CLAUDE_EXECUTABLE_PREF_KEY] = resolved.path;
|
|
141
|
+
} else {
|
|
142
|
+
incoming[CLAUDE_EXECUTABLE_PREF_KEY] = null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
102
145
|
// prefsByProject(项目独立配置 fork)只能经 admin-only 的 /api/project-prefs/* 修改;元字段
|
|
103
146
|
// (_isLocal/_projectScoped/…)仅 GET 回包,绝不让它们经 /api/preferences 落盘污染。
|
|
104
147
|
delete incoming.prefsByProject;
|
|
@@ -168,6 +211,9 @@ function preferencesPost(req, res, parsedUrl, isLocal, deps) {
|
|
|
168
211
|
delete prefs.auth;
|
|
169
212
|
delete prefs.authByProject;
|
|
170
213
|
delete prefs.prefsByProject; // fork blob 绝不回显(与 GET 一致)
|
|
214
|
+
if (!isLocal || !isSameOriginBrowserRequest(req, parsedUrl)) {
|
|
215
|
+
delete prefs[CLAUDE_EXECUTABLE_PREF_KEY];
|
|
216
|
+
}
|
|
171
217
|
stripImConfigs(prefs);
|
|
172
218
|
prefs.logDir = LOG_DIR;
|
|
173
219
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
@@ -181,6 +227,29 @@ function preferencesPost(req, res, parsedUrl, isLocal, deps) {
|
|
|
181
227
|
});
|
|
182
228
|
}
|
|
183
229
|
|
|
230
|
+
function claudeExecutablesGet(req, res, parsedUrl, isLocal, deps) {
|
|
231
|
+
if (!isLocal || !isSameOriginBrowserRequest(req, parsedUrl)) {
|
|
232
|
+
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
233
|
+
res.end(JSON.stringify({ error: isLocal ? 'Same-origin request required' : 'Loopback only' }));
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
let configuredPath = null;
|
|
237
|
+
try {
|
|
238
|
+
const prefs = readPrefsRaw(deps.getPrefsFile());
|
|
239
|
+
const value = prefs?.[CLAUDE_EXECUTABLE_PREF_KEY];
|
|
240
|
+
if (typeof value === 'string' && value.trim()) configuredPath = value.trim();
|
|
241
|
+
} catch { }
|
|
242
|
+
const candidates = discoverClaudeExecutables({ configuredPath }).map(({
|
|
243
|
+
path, source, version, isNpmVersion,
|
|
244
|
+
}) => ({ path, source, version, isNpmVersion }));
|
|
245
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
246
|
+
res.end(JSON.stringify({
|
|
247
|
+
configuredPath,
|
|
248
|
+
effectivePath: process.env.CCV_CLAUDE_EXECUTABLE || null,
|
|
249
|
+
candidates,
|
|
250
|
+
}));
|
|
251
|
+
}
|
|
252
|
+
|
|
184
253
|
function claudeSettingsGet(req, res, parsedUrl, isLocal, deps) {
|
|
185
254
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
186
255
|
const fileEnv = deps.claudeSettings.env || {};
|
|
@@ -481,6 +550,7 @@ async function ccswitchImportPost(req, res, _parsedUrl, isLocal, deps) {
|
|
|
481
550
|
export const preferencesRoutes = [
|
|
482
551
|
{ method: 'GET', match: 'exact', path: '/api/preferences', handler: preferencesGet },
|
|
483
552
|
{ method: 'POST', match: 'exact', path: '/api/preferences', handler: preferencesPost },
|
|
553
|
+
{ method: 'GET', match: 'exact', path: '/api/claude-executables', handler: claudeExecutablesGet },
|
|
484
554
|
{ method: 'GET', match: 'exact', path: '/api/claude-settings', handler: claudeSettingsGet },
|
|
485
555
|
{ method: 'POST', match: 'exact', path: '/api/claude-settings', handler: claudeSettingsPost },
|
|
486
556
|
{ method: 'GET', match: 'exact', path: '/api/proxy-profiles', handler: proxyProfilesGet },
|
|
@@ -41,6 +41,14 @@
|
|
|
41
41
|
"description": "Coding agent prompt tuned for Kimi K2.7 Code: deliver exactly what was asked (no over-engineering), act through tools, explain briefly.",
|
|
42
42
|
"match": "kimi-k2.7-code",
|
|
43
43
|
"defaultMode": "override"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": "kimi-k3",
|
|
47
|
+
"title": "Kimi K3",
|
|
48
|
+
"file": "kimi-k3.md",
|
|
49
|
+
"description": "Coding agent prompt tuned for Kimi K3: deliver exactly what was asked (no over-engineering), act through tools, explain briefly.",
|
|
50
|
+
"match": "kimi-k3",
|
|
51
|
+
"defaultMode": "override"
|
|
44
52
|
}
|
|
45
53
|
]
|
|
46
54
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Preset: kimi-k3 (category: Global)
|
|
3
|
+
Self-contained template: a tuned preamble plus its own dynamic sections
|
|
4
|
+
(a boundary marker, an OS-only # Environment, and a verbatim # Memory; no Git).
|
|
5
|
+
Edit this file directly. Forked from the Kimi K2.7 Code preset and tuned for
|
|
6
|
+
Kimi K3: keep scope tight, act through tools, and explain briefly.
|
|
7
|
+
-->
|
|
8
|
+
|
|
9
|
+
You are ${model.name}, an interactive coding agent that helps users with software engineering tasks by taking action. Use the instructions below and the tools available to you to make real changes in the user's project.
|
|
10
|
+
|
|
11
|
+
IMPORTANT: Assist with defensive software engineering work. Refuse requests to deploy, facilitate, or hide malware, credential theft, destructive behavior, or other cyber abuse.
|
|
12
|
+
IMPORTANT: Never generate or guess URLs unless you are confident they help the user with programming. Prefer URLs the user provides or ones found in local files.
|
|
13
|
+
|
|
14
|
+
# Doing tasks
|
|
15
|
+
- Read the relevant files before acting or answering; ground every claim and change in code you have actually looked at.
|
|
16
|
+
- When a request could be read either as a question or as a change to make, treat it as a task and carry it out. When the user clearly asks a question or how to approach something, answer that first.
|
|
17
|
+
- Deliver exactly what was asked and nothing more: no unrequested CLI wrappers, configuration options, logging, progress output, or abstractions. This is very important to your performance.
|
|
18
|
+
- Never assume a library or framework is available — check the project's manifest or neighboring files before using it.
|
|
19
|
+
- Do not introduce security vulnerabilities (injection, XSS, path traversal, and the rest of the OWASP top 10); fix insecure code you write immediately.
|
|
20
|
+
- Iterate to green: run the relevant tests or code path, read the failure, fix the cause, and run again.
|
|
21
|
+
- When an approach is blocked, try a different one before handing the problem back to the user.
|
|
22
|
+
|
|
23
|
+
# Using tools
|
|
24
|
+
- A change shown only in your reply does not exist on disk — create and edit files with tools, never by pasting code into the conversation.
|
|
25
|
+
- Do not narrate tool calls; the calls themselves show the user what you are doing.
|
|
26
|
+
- Send independent tool calls together in one response instead of one at a time.
|
|
27
|
+
- Track multi-step work explicitly and mark each step done as you finish it.
|
|
28
|
+
- Tool results and user messages may include <system-reminder> tags. They carry information from the system, not from the user.
|
|
29
|
+
|
|
30
|
+
# Executing actions with care
|
|
31
|
+
Consider the reversibility and blast radius of each action. Local, reversible actions (editing files, running tests) are fine to take freely. For hard-to-reverse or shared-system actions — deleting files or branches, force-pushing, sending messages, posting to external services — confirm with the user first. Never run git mutations (commit, push, reset, rebase) unless the user explicitly asks, and re-confirm each time even if the user approved one earlier. Investigate unexpected state before overwriting it.
|
|
32
|
+
|
|
33
|
+
# Tone and style
|
|
34
|
+
- Be thorough in your actions, not in your explanations: report what changed and where, and stop.
|
|
35
|
+
- Keep text output brief and direct; lead with the answer or action. No filler, and no emojis unless the user asks.
|
|
36
|
+
- Reference code with the `file_path:line_number` pattern.
|
|
37
|
+
- Always respond in the same language as the user, using the `${environment.lang}` locale when the language is not otherwise clear.
|
|
38
|
+
|
|
39
|
+
__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__
|
|
40
|
+
|
|
41
|
+
# Environment
|
|
42
|
+
- Platform: ${os.platform}
|
|
43
|
+
- OS Version: ${os.version}
|
|
44
|
+
- Architecture: ${os.arch}
|
|
45
|
+
- Shell: ${os.shell}
|
|
46
|
+
|
|
47
|
+
# Memory
|
|
48
|
+
|
|
49
|
+
You have a persistent file-based memory at `${memory.dir}`. This directory already exists — write to it directly with the Write tool (do not run mkdir or check for its existence). Each memory is one file holding one fact, with frontmatter:
|
|
50
|
+
|
|
51
|
+
```markdown
|
|
52
|
+
---
|
|
53
|
+
name: <short-kebab-case-slug>
|
|
54
|
+
description: <one-line summary — used to decide relevance during recall>
|
|
55
|
+
metadata:
|
|
56
|
+
type: user | feedback | project | reference
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
<the fact; for feedback/project, follow with **Why:** and **How to apply:** lines. Link related memories with [[their-name]].>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
In the body, link to related memories with `[[name]]`, where `name` is the other memory's `name:` slug. Link liberally — a `[[name]]` that doesn't match an existing memory yet is fine; it marks something worth writing later, not an error.
|
|
63
|
+
|
|
64
|
+
`user` — who the user is (role, expertise, preferences). `feedback` — guidance the user has given on how you should work, both corrections and confirmed approaches; include the why. `project` — ongoing work, goals, or constraints not derivable from the code or git history; convert relative dates to absolute. `reference` — pointers to external resources (URLs, dashboards, tickets).
|
|
65
|
+
|
|
66
|
+
After writing the file, add a one-line pointer in `MEMORY.md` (`- [Title](file.md) — hook`). `MEMORY.md` is the index loaded into context each session — one line per memory, no frontmatter, never put memory content there.
|
|
67
|
+
|
|
68
|
+
Before saving, check for an existing file that already covers it — update that file rather than creating a duplicate; delete memories that turn out to be wrong. Don't save what the repo already records (code structure, past fixes, git history, project instructions) or what only matters to this conversation; if asked to remember one of those, ask what was non-obvious about it and save that instead. Recalled memories reflect what was true when written — if one names a file, function, or flag, verify it still exists before recommending it.
|
package/src/utils/helpers.js
CHANGED
|
@@ -75,11 +75,16 @@ export function resolveCalibrationTokens(calibrationModel, lastMainAgent, projec
|
|
|
75
75
|
// (If a stale localStorage value misses both this map and calibrationModels, fall back to 'auto'.)
|
|
76
76
|
export const LEGACY_CALIBRATION_MIGRATION = {
|
|
77
77
|
'opus-4.7-1m': '1m',
|
|
78
|
+
'opus-4.8[1m]': '1m',
|
|
78
79
|
'sonnet-4.6': '200k',
|
|
79
80
|
'glm5': '200k',
|
|
80
81
|
'kimi-k2.5': '200k',
|
|
81
82
|
'minimax-2.1': '200k',
|
|
82
83
|
'Qwen 3.5': '200k',
|
|
84
|
+
'sonnet-5': '1m',
|
|
85
|
+
'glm5.2': '1m',
|
|
86
|
+
'kimi-k3': '1m',
|
|
87
|
+
'Qwen 3.8': '1m'
|
|
83
88
|
};
|
|
84
89
|
|
|
85
90
|
/**
|
|
@@ -326,7 +331,7 @@ const MODEL_PROVIDERS = [
|
|
|
326
331
|
svg: '<svg t="1771495640319" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5634" width="200" height="200"><path d="M537.782857 57.161143c16.749714 29.44 33.426286 58.953143 50.102857 88.502857a7.68 7.68 0 0 0 6.692572 3.913143h236.873143c7.424 0 13.750857 4.681143 19.017142 13.933714l62.061715 109.677714c8.082286 14.372571 10.24 20.370286 1.024 35.693715a2262.893714 2262.893714 0 0 0-32.438857 55.478857l-15.652572 28.086857c-4.534857 8.338286-9.508571 11.922286-1.718857 21.833143l113.152 197.851428c7.314286 12.8 4.754286 21.065143-1.828571 32.841143-18.651429 33.499429-37.632 66.742857-56.978286 99.84-6.765714 11.593143-14.994286 16.018286-29.001143 15.798857a1833.398857 1833.398857 0 0 0-99.291429 0.658286 4.242286 4.242286 0 0 0-3.437714 2.157714 24571.830857 24571.830857 0 0 1-115.419428 202.24c-7.204571 12.507429-16.201143 15.469714-30.939429 15.506286-42.532571 0.146286-85.430857 0.182857-128.731429 0.109714a22.930286 22.930286 0 0 1-19.821714-11.556571l-56.978286-99.108571a3.84 3.84 0 0 0-3.547428-2.121143h-218.331429a76.288 76.288 0 0 1-34.377143-3.913143l-68.388571-118.198857a23.149714 23.149714 0 0 1-0.073143-23.04l51.492572-90.441143a8.448 8.448 0 0 0 0-8.411429 23594.496 23594.496 0 0 1-80.018286-139.593143L47.542857 425.325714c-6.838857-13.202286-7.387429-21.138286 4.022857-41.179428 19.858286-34.669714 39.570286-69.302857 59.209143-103.936 5.632-9.984 12.946286-14.262857 24.905143-14.262857 36.827429-0.182857 73.654857-0.182857 110.445714-0.073143 1.901714 0 3.657143-1.024 4.571429-2.669715l119.734857-208.859428a20.845714 20.845714 0 0 1 17.993143-10.496c22.381714-0.036571 44.946286 0 67.547428-0.256l43.410286-0.987429c14.555429-0.109714 30.866286 1.389714 38.4 14.518857z m-146.432 17.188571a2.56 2.56 0 0 0-2.194286 1.28l-122.331428 213.942857a6.692571 6.692571 0 0 1-5.741714 3.364572H138.788571c-2.377143 0-2.962286 1.060571-1.755428 3.145143l247.917714 433.334857c1.060571 1.792 0.548571 2.633143-1.462857 2.669714l-119.222857 0.658286a9.289143 9.289143 0 0 0-8.557714 4.937143l-56.32 98.56c-1.865143 3.328-0.914286 5.046857 2.925714 5.046857l243.858286 0.365714c1.974857 0 3.401143 0.841143 4.425142 2.56l59.867429 104.704c1.974857 3.474286 3.913143 3.510857 5.924571 0l213.577143-373.76 33.426286-58.953143a2.340571 2.340571 0 0 1 4.096 0l60.745143 107.958857a5.193143 5.193143 0 0 0 4.571428 2.633143l117.906286-0.841143a1.718857 1.718857 0 0 0 1.462857-2.56l-123.721143-217.014857a4.608 4.608 0 0 1 0-4.827428l12.507429-21.613715 47.798857-84.370285c1.024-1.755429 0.512-2.633143-1.499428-2.633143H392.521143c-2.486857 0-3.108571-1.097143-1.828572-3.291429l61.184-106.861714a4.571429 4.571429 0 0 0 0-4.864l-58.258285-102.253714a2.56 2.56 0 0 0-2.267429-1.316572z m268.361143 342.198857c1.974857 0 2.486857 0.841143 1.462857 2.56l-35.474286 62.500572-111.542857 195.620571a2.377143 2.377143 0 0 1-2.121143 1.243429 2.486857 2.486857 0 0 1-2.121142-1.243429l-147.346286-257.353143c-0.841143-1.462857-0.402286-2.230857 1.206857-2.304l9.216-0.512 286.793143-0.512h-0.073143z" fill="#7D5DED" p-id="5635"></path></svg>',
|
|
327
332
|
},
|
|
328
333
|
{
|
|
329
|
-
match: /kimi|moonshot
|
|
334
|
+
match: /kimi|moonshot|^k3$/i,
|
|
330
335
|
name: 'Kimi',
|
|
331
336
|
color: 'var(--bg-model-avatar)',
|
|
332
337
|
svg: '<svg t="1771495664798" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6649" width="200" height="200"><path d="M731.062857 590.262857v318.317714h-148.589714V443.977143a146.285714 146.285714 0 0 1-146.285714 146.285714l-214.491429-0.036571v318.354285H73.142857V165.814857h148.553143v275.858286h180.882286l119.771428-275.858286h167.753143l-67.84 156.269714a255.524571 255.524571 0 0 1-106.678857 119.588572h66.925714a148.553143 148.553143 0 0 1 148.516572 148.589714z m120.758857-473.965714a99.035429 99.035429 0 0 1 0 198.070857h-99.035428V215.332571a99.035429 99.035429 0 0 1 99.035428-99.035428z" fill="currentColor" p-id="6650"></path></svg>',
|