cc-viewer 1.6.92 → 1.6.93
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/cli.js +36 -12
- package/dist/assets/{App-D4nsyVOs.js → App-hH0rDzOq.js} +1 -1
- package/dist/assets/{AppHeader-q5FApoep.css → AppHeader-DN09T_4w.css} +1 -1
- package/dist/assets/{AppHeader.module-S7b16Luh.js → AppHeader.module-_fn3e-3Y.js} +167 -167
- package/dist/assets/{Mobile-ZoUy632Q.js → Mobile-DfiaHmP0.js} +1 -1
- package/dist/assets/{_basePickBy-uFgBWU_1.js → _basePickBy-Dwc6xlH0.js} +1 -1
- package/dist/assets/{_baseUniq-D9OrCsW-.js → _baseUniq-CIhWoRX6.js} +1 -1
- package/dist/assets/{arc-B5paIHbL.js → arc-ClMosRWS.js} +1 -1
- package/dist/assets/{architectureDiagram-2XIMDMQ5-CVR0S5_A.js → architectureDiagram-2XIMDMQ5-CiuBPipP.js} +1 -1
- package/dist/assets/{blockDiagram-WCTKOSBZ-DbExq97J.js → blockDiagram-WCTKOSBZ-Bifbw0Pj.js} +1 -1
- package/dist/assets/{c4Diagram-IC4MRINW-CyyZQH7U.js → c4Diagram-IC4MRINW-Dwx54q11.js} +1 -1
- package/dist/assets/channel-CA7t6n2N.js +1 -0
- package/dist/assets/{chunk-4BX2VUAB-D-TxZu2j.js → chunk-4BX2VUAB-BQo_Scg4.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-BpiK1KHs.js → chunk-55IACEB6-DmMYL0yS.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-BsVETng4.js → chunk-FMBD7UC4-D54EDOVv.js} +1 -1
- package/dist/assets/{chunk-JSJVCQXG-ByEZLFF3.js → chunk-JSJVCQXG-C4wM91-8.js} +1 -1
- package/dist/assets/{chunk-KX2RTZJC-DGedEdfQ.js → chunk-KX2RTZJC-BuTQbTst.js} +1 -1
- package/dist/assets/{chunk-NQ4KR5QH-CVuE5E20.js → chunk-NQ4KR5QH-pwWGt1sO.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-BDDf3H2l.js → chunk-QZHKN3VN-DzdyrpMz.js} +1 -1
- package/dist/assets/{chunk-WL4C6EOR-D57kZtSM.js → chunk-WL4C6EOR-BsqkjkKt.js} +1 -1
- package/dist/assets/classDiagram-VBA2DB6C-iCP3aAMs.js +1 -0
- package/dist/assets/classDiagram-v2-RAHNMMFH-iCP3aAMs.js +1 -0
- package/dist/assets/clone-DykZ1sNt.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-CF2LKXX0.js → cose-bilkent-S5V4N54A-C_Zw3kps.js} +1 -1
- package/dist/assets/{dagre-KLK3FWXG-AKm2eMFa.js → dagre-KLK3FWXG-BBym-Z-r.js} +1 -1
- package/dist/assets/{diagram-E7M64L7V-Dy0ZpqYE.js → diagram-E7M64L7V-CJK3xILI.js} +1 -1
- package/dist/assets/{diagram-IFDJBPK2-BwulOlMM.js → diagram-IFDJBPK2-CG0k9GmI.js} +1 -1
- package/dist/assets/{diagram-P4PSJMXO-BlGWqbdU.js → diagram-P4PSJMXO-BHJpgqgl.js} +1 -1
- package/dist/assets/{erDiagram-INFDFZHY-DlKoWq8q.js → erDiagram-INFDFZHY-DY2TxE9e.js} +1 -1
- package/dist/assets/{flowDiagram-PKNHOUZH-fbS7K31R.js → flowDiagram-PKNHOUZH-C6ujoYh5.js} +1 -1
- package/dist/assets/{ganttDiagram-A5KZAMGK-CrXC6aZY.js → ganttDiagram-A5KZAMGK-DacCPw0V.js} +1 -1
- package/dist/assets/{gitGraphDiagram-K3NZZRJ6-DgL6ifkO.js → gitGraphDiagram-K3NZZRJ6-1Hd4fFZI.js} +1 -1
- package/dist/assets/{graph-D8OSNuBb.js → graph-D8YLYosB.js} +1 -1
- package/dist/assets/{index-vXPJvGyq.js → index-DLkyZ-Zb.js} +2 -2
- package/dist/assets/{infoDiagram-LFFYTUFH-D-yXUDBn.js → infoDiagram-LFFYTUFH-ClN29cDZ.js} +1 -1
- package/dist/assets/{ishikawaDiagram-PHBUUO56-BSLzUZNI.js → ishikawaDiagram-PHBUUO56-DDgBFnJ4.js} +1 -1
- package/dist/assets/{journeyDiagram-4ABVD52K-BsZGTLTS.js → journeyDiagram-4ABVD52K-Cl9xLEhq.js} +1 -1
- package/dist/assets/{kanban-definition-K7BYSVSG-qHcQ8IDf.js → kanban-definition-K7BYSVSG-CBfTb1XX.js} +1 -1
- package/dist/assets/{layout-BT82KtcO.js → layout-BSGsZPU6.js} +1 -1
- package/dist/assets/{linear-C-NY3Tys.js → linear-Cm4oUzYV.js} +1 -1
- package/dist/assets/{mermaid.core-Iu-Haj2d.js → mermaid.core-Bfq98W72.js} +4 -4
- package/dist/assets/{mindmap-definition-YRQLILUH-bYKflbjM.js → mindmap-definition-YRQLILUH-S_0pZiXc.js} +1 -1
- package/dist/assets/{pieDiagram-SKSYHLDU-EBV2nFfI.js → pieDiagram-SKSYHLDU-CUof-7Ag.js} +1 -1
- package/dist/assets/{quadrantDiagram-337W2JSQ-CDbCpLQ6.js → quadrantDiagram-337W2JSQ-74oAPb7-.js} +1 -1
- package/dist/assets/{requirementDiagram-Z7DCOOCP-DTepHdzH.js → requirementDiagram-Z7DCOOCP-Dx_1a8Sm.js} +1 -1
- package/dist/assets/{sankeyDiagram-WA2Y5GQK-BDGKah3z.js → sankeyDiagram-WA2Y5GQK-Ollqup46.js} +1 -1
- package/dist/assets/{sequenceDiagram-2WXFIKYE-DOTt340N.js → sequenceDiagram-2WXFIKYE-CUQdI1hH.js} +1 -1
- package/dist/assets/{stateDiagram-RAJIS63D-C-dx3ZVl.js → stateDiagram-RAJIS63D-DA4bEZTV.js} +1 -1
- package/dist/assets/stateDiagram-v2-FVOUBMTO-DaqDUk5Z.js +1 -0
- package/dist/assets/{timeline-definition-YZTLITO2-CQuH8MMr.js → timeline-definition-YZTLITO2-hIqAiIh9.js} +1 -1
- package/dist/assets/{treemap-KZPCXAKY-Bh6K3rqD.js → treemap-KZPCXAKY-BPMtLzV0.js} +1 -1
- package/dist/assets/{vennDiagram-LZ73GAT5-B4Avwovu.js → vennDiagram-LZ73GAT5-8C63L59W.js} +1 -1
- package/dist/assets/{xychartDiagram-JWTSCODW-CNDvIAFk.js → xychartDiagram-JWTSCODW-rb793_nJ.js} +1 -1
- package/dist/index.html +1 -1
- package/lib/perm-bridge.js +122 -0
- package/package.json +1 -1
- package/server.js +160 -2
- package/dist/assets/channel-Bk-jFRd9.js +0 -1
- package/dist/assets/classDiagram-VBA2DB6C-BIEQkv07.js +0 -1
- package/dist/assets/classDiagram-v2-RAHNMMFH-BIEQkv07.js +0 -1
- package/dist/assets/clone-CDtWj5DG.js +0 -1
- package/dist/assets/stateDiagram-v2-FVOUBMTO-Dn8gbgvz.js +0 -1
package/server.js
CHANGED
|
@@ -101,6 +101,9 @@ let _workspaceLaunched = false; // 工作区是否已经启动了会话
|
|
|
101
101
|
// At most one pending request at a time (Claude Code is single-threaded)
|
|
102
102
|
let pendingAskHook = null; // { questions, res, timer, createdAt }
|
|
103
103
|
|
|
104
|
+
// Permission hook bridge state (for PreToolUse permission approval)
|
|
105
|
+
let pendingPermHook = null; // { toolName, input, res, timer, createdAt }
|
|
106
|
+
|
|
104
107
|
// Editor session state (for $EDITOR intercept)
|
|
105
108
|
const editorSessions = new Map(); // sessionId → { filePath, done, createdAt }
|
|
106
109
|
// Periodically clean up abandoned editor sessions (older than 1 hour)
|
|
@@ -1200,6 +1203,61 @@ async function handleRequest(req, res) {
|
|
|
1200
1203
|
return;
|
|
1201
1204
|
}
|
|
1202
1205
|
|
|
1206
|
+
// 用系统默认应用打开文件
|
|
1207
|
+
if (url === '/api/open-file' && method === 'POST') {
|
|
1208
|
+
let body = '';
|
|
1209
|
+
req.on('data', chunk => { body += chunk; if (body.length > MAX_POST_BODY) req.destroy(); });
|
|
1210
|
+
req.on('end', () => {
|
|
1211
|
+
let parsed;
|
|
1212
|
+
try { parsed = JSON.parse(body); } catch {
|
|
1213
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1214
|
+
res.end(JSON.stringify({ error: 'Invalid request body' }));
|
|
1215
|
+
return;
|
|
1216
|
+
}
|
|
1217
|
+
try {
|
|
1218
|
+
const { path: filePath } = parsed;
|
|
1219
|
+
if (!filePath) {
|
|
1220
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1221
|
+
res.end(JSON.stringify({ error: 'Missing path' }));
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
if (filePath.startsWith('/') || filePath.includes('..')) {
|
|
1225
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1226
|
+
res.end(JSON.stringify({ error: 'Invalid path' }));
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
const cwd = process.env.CCV_PROJECT_DIR || process.cwd();
|
|
1230
|
+
const fullPath = join(cwd, filePath);
|
|
1231
|
+
if (!existsSync(fullPath)) {
|
|
1232
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
1233
|
+
res.end(JSON.stringify({ error: 'File not found' }));
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
const realFull = realpathSync(fullPath);
|
|
1237
|
+
const realCwd = realpathSync(cwd);
|
|
1238
|
+
if (!realFull.startsWith(realCwd + '/')) {
|
|
1239
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1240
|
+
res.end(JSON.stringify({ error: 'Path traversal not allowed' }));
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1243
|
+
const plat = process.platform;
|
|
1244
|
+
if (plat === 'darwin') {
|
|
1245
|
+
execFile('open', [fullPath], () => {});
|
|
1246
|
+
} else if (plat === 'win32') {
|
|
1247
|
+
execFile('cmd.exe', ['/c', 'start', '', fullPath], () => {});
|
|
1248
|
+
} else {
|
|
1249
|
+
execFile('xdg-open', [fullPath], () => {});
|
|
1250
|
+
}
|
|
1251
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1252
|
+
res.end(JSON.stringify({ ok: true }));
|
|
1253
|
+
} catch (err) {
|
|
1254
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1255
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
1256
|
+
}
|
|
1257
|
+
});
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1203
1261
|
// 解析相对路径为绝对路径(不触发任何副作用)
|
|
1204
1262
|
if (url === '/api/resolve-path' && method === 'POST') {
|
|
1205
1263
|
let body = '';
|
|
@@ -1605,6 +1663,90 @@ async function handleRequest(req, res) {
|
|
|
1605
1663
|
return;
|
|
1606
1664
|
}
|
|
1607
1665
|
|
|
1666
|
+
// Permission hook bridge: receive tool permission request from perm-bridge.js, long-poll for user decision
|
|
1667
|
+
if (url === '/api/perm-hook' && method === 'POST') {
|
|
1668
|
+
let body = '';
|
|
1669
|
+
req.on('data', (chunk) => {
|
|
1670
|
+
body += chunk;
|
|
1671
|
+
if (body.length > 1000000) {
|
|
1672
|
+
res.writeHead(413, { 'Content-Type': 'application/json' });
|
|
1673
|
+
res.end(JSON.stringify({ error: 'Request body too large' }));
|
|
1674
|
+
return;
|
|
1675
|
+
}
|
|
1676
|
+
});
|
|
1677
|
+
req.on('end', () => {
|
|
1678
|
+
try {
|
|
1679
|
+
const { toolName, input } = JSON.parse(body);
|
|
1680
|
+
if (!toolName) {
|
|
1681
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1682
|
+
res.end(JSON.stringify({ error: 'Missing toolName' }));
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
// Cancel any previous pending permission request
|
|
1687
|
+
if (pendingPermHook) {
|
|
1688
|
+
try {
|
|
1689
|
+
if (!pendingPermHook.res.headersSent) {
|
|
1690
|
+
pendingPermHook.res.writeHead(409, { 'Content-Type': 'application/json' });
|
|
1691
|
+
pendingPermHook.res.end(JSON.stringify({ error: 'Superseded' }));
|
|
1692
|
+
}
|
|
1693
|
+
} catch {}
|
|
1694
|
+
clearTimeout(pendingPermHook.timer);
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
const HOOK_TIMEOUT = 5 * 60 * 1000;
|
|
1698
|
+
const id = `perm_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
1699
|
+
const timer = setTimeout(() => {
|
|
1700
|
+
if (pendingPermHook && pendingPermHook.id === id) {
|
|
1701
|
+
pendingPermHook = null;
|
|
1702
|
+
try {
|
|
1703
|
+
if (!res.headersSent) {
|
|
1704
|
+
res.writeHead(408, { 'Content-Type': 'application/json' });
|
|
1705
|
+
res.end(JSON.stringify({ error: 'Timeout' }));
|
|
1706
|
+
}
|
|
1707
|
+
} catch {}
|
|
1708
|
+
if (terminalWss) {
|
|
1709
|
+
const tmsg = JSON.stringify({ type: 'perm-hook-timeout' });
|
|
1710
|
+
terminalWss.clients.forEach((c) => {
|
|
1711
|
+
if (c.readyState === 1) try { c.send(tmsg); } catch {}
|
|
1712
|
+
});
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
}, HOOK_TIMEOUT);
|
|
1716
|
+
|
|
1717
|
+
pendingPermHook = { id, toolName, input, res, timer, createdAt: Date.now() };
|
|
1718
|
+
|
|
1719
|
+
// Broadcast to all terminal WS clients
|
|
1720
|
+
if (terminalWss) {
|
|
1721
|
+
const pmsg = JSON.stringify({ type: 'perm-hook-pending', id, toolName, input });
|
|
1722
|
+
terminalWss.clients.forEach((client) => {
|
|
1723
|
+
if (client.readyState === 1) {
|
|
1724
|
+
try { client.send(pmsg); } catch {}
|
|
1725
|
+
}
|
|
1726
|
+
});
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
// Handle perm-bridge.js disconnection
|
|
1730
|
+
res.on('close', () => {
|
|
1731
|
+
if (pendingPermHook && pendingPermHook.id === id) {
|
|
1732
|
+
clearTimeout(pendingPermHook.timer);
|
|
1733
|
+
pendingPermHook = null;
|
|
1734
|
+
if (terminalWss) {
|
|
1735
|
+
const tmsg = JSON.stringify({ type: 'perm-hook-timeout' });
|
|
1736
|
+
terminalWss.clients.forEach((c) => {
|
|
1737
|
+
if (c.readyState === 1) try { c.send(tmsg); } catch {}
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
});
|
|
1742
|
+
} catch {
|
|
1743
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1744
|
+
res.end(JSON.stringify({ error: 'Invalid request body' }));
|
|
1745
|
+
}
|
|
1746
|
+
});
|
|
1747
|
+
return;
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1608
1750
|
// 读取文件内容 API
|
|
1609
1751
|
if (url === '/api/file-content' && method === 'GET') {
|
|
1610
1752
|
const reqPath = parsedUrl.searchParams.get('path');
|
|
@@ -1678,13 +1820,16 @@ async function handleRequest(req, res) {
|
|
|
1678
1820
|
const extMime = {
|
|
1679
1821
|
'.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg',
|
|
1680
1822
|
'.gif': 'image/gif', '.svg': 'image/svg+xml', '.ico': 'image/x-icon',
|
|
1681
|
-
'.webp': 'image/webp',
|
|
1823
|
+
'.webp': 'image/webp', '.html': 'text/html', '.htm': 'text/html',
|
|
1682
1824
|
};
|
|
1683
1825
|
const ext = (targetFile.match(/\.[^.]+$/) || [''])[0].toLowerCase();
|
|
1684
1826
|
const mime = extMime[ext] || 'application/octet-stream';
|
|
1685
1827
|
const data = method === 'HEAD' ? null : readFileSync(targetFile);
|
|
1686
1828
|
const size = method === 'HEAD' ? stat.size : data.length;
|
|
1687
|
-
|
|
1829
|
+
const headers = { 'Content-Type': mime, 'Content-Length': size };
|
|
1830
|
+
// 防止用户项目中的恶意 HTML 在同源下执行脚本(XSS 防护)
|
|
1831
|
+
if (mime === 'text/html') headers['Content-Security-Policy'] = 'sandbox';
|
|
1832
|
+
res.writeHead(200, headers);
|
|
1688
1833
|
res.end(data);
|
|
1689
1834
|
} catch (err) {
|
|
1690
1835
|
const status = ERROR_STATUS_MAP[err.code] || 500;
|
|
@@ -2495,6 +2640,19 @@ async function setupTerminalWebSocket(httpServer) {
|
|
|
2495
2640
|
}
|
|
2496
2641
|
} catch {}
|
|
2497
2642
|
}
|
|
2643
|
+
} else if (msg.type === 'perm-hook-answer') {
|
|
2644
|
+
// Client answered permission approval via hook bridge
|
|
2645
|
+
if (pendingPermHook && msg.id && msg.id === pendingPermHook.id) {
|
|
2646
|
+
const { res: hookRes, timer } = pendingPermHook;
|
|
2647
|
+
clearTimeout(timer);
|
|
2648
|
+
pendingPermHook = null;
|
|
2649
|
+
try {
|
|
2650
|
+
if (!hookRes.headersSent) {
|
|
2651
|
+
hookRes.writeHead(200, { 'Content-Type': 'application/json' });
|
|
2652
|
+
hookRes.end(JSON.stringify({ decision: msg.decision || 'deny' }));
|
|
2653
|
+
}
|
|
2654
|
+
} catch {}
|
|
2655
|
+
}
|
|
2498
2656
|
} else if (msg.type === 'resize') {
|
|
2499
2657
|
// 存储该客户端的尺寸
|
|
2500
2658
|
clientSizes.set(ws, { cols: msg.cols, rows: msg.rows });
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{ap as o,aq as n}from"./mermaid.core-Iu-Haj2d.js";const t=(a,r)=>o.lang.round(n.parse(a)[r]);export{t as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as e,C as t}from"./chunk-WL4C6EOR-D57kZtSM.js";import{_ as i}from"./mermaid.core-Iu-Haj2d.js";import"./chunk-FMBD7UC4-BsVETng4.js";import"./chunk-JSJVCQXG-ByEZLFF3.js";import"./chunk-55IACEB6-BpiK1KHs.js";import"./chunk-KX2RTZJC-DGedEdfQ.js";import"./AppHeader.module-S7b16Luh.js";import"./index-vXPJvGyq.js";var f={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{f as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as e,C as t}from"./chunk-WL4C6EOR-D57kZtSM.js";import{_ as i}from"./mermaid.core-Iu-Haj2d.js";import"./chunk-FMBD7UC4-BsVETng4.js";import"./chunk-JSJVCQXG-ByEZLFF3.js";import"./chunk-55IACEB6-BpiK1KHs.js";import"./chunk-KX2RTZJC-DGedEdfQ.js";import"./AppHeader.module-S7b16Luh.js";import"./index-vXPJvGyq.js";var f={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{f as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{b as r}from"./_baseUniq-D9OrCsW-.js";var e=4;function a(o){return r(o,e)}export{a as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as r,b as e,a,S as s}from"./chunk-NQ4KR5QH-CVuE5E20.js";import{_ as i}from"./mermaid.core-Iu-Haj2d.js";import"./chunk-55IACEB6-BpiK1KHs.js";import"./chunk-KX2RTZJC-DGedEdfQ.js";import"./index-vXPJvGyq.js";import"./AppHeader.module-S7b16Luh.js";var u={parser:a,get db(){return new s(2)},renderer:e,styles:r,init:i(t=>{t.state||(t.state={}),t.state.arrowMarkerAbsolute=t.arrowMarkerAbsolute},"init")};export{u as diagram};
|