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.
Files changed (62) hide show
  1. package/cli.js +36 -12
  2. package/dist/assets/{App-D4nsyVOs.js → App-hH0rDzOq.js} +1 -1
  3. package/dist/assets/{AppHeader-q5FApoep.css → AppHeader-DN09T_4w.css} +1 -1
  4. package/dist/assets/{AppHeader.module-S7b16Luh.js → AppHeader.module-_fn3e-3Y.js} +167 -167
  5. package/dist/assets/{Mobile-ZoUy632Q.js → Mobile-DfiaHmP0.js} +1 -1
  6. package/dist/assets/{_basePickBy-uFgBWU_1.js → _basePickBy-Dwc6xlH0.js} +1 -1
  7. package/dist/assets/{_baseUniq-D9OrCsW-.js → _baseUniq-CIhWoRX6.js} +1 -1
  8. package/dist/assets/{arc-B5paIHbL.js → arc-ClMosRWS.js} +1 -1
  9. package/dist/assets/{architectureDiagram-2XIMDMQ5-CVR0S5_A.js → architectureDiagram-2XIMDMQ5-CiuBPipP.js} +1 -1
  10. package/dist/assets/{blockDiagram-WCTKOSBZ-DbExq97J.js → blockDiagram-WCTKOSBZ-Bifbw0Pj.js} +1 -1
  11. package/dist/assets/{c4Diagram-IC4MRINW-CyyZQH7U.js → c4Diagram-IC4MRINW-Dwx54q11.js} +1 -1
  12. package/dist/assets/channel-CA7t6n2N.js +1 -0
  13. package/dist/assets/{chunk-4BX2VUAB-D-TxZu2j.js → chunk-4BX2VUAB-BQo_Scg4.js} +1 -1
  14. package/dist/assets/{chunk-55IACEB6-BpiK1KHs.js → chunk-55IACEB6-DmMYL0yS.js} +1 -1
  15. package/dist/assets/{chunk-FMBD7UC4-BsVETng4.js → chunk-FMBD7UC4-D54EDOVv.js} +1 -1
  16. package/dist/assets/{chunk-JSJVCQXG-ByEZLFF3.js → chunk-JSJVCQXG-C4wM91-8.js} +1 -1
  17. package/dist/assets/{chunk-KX2RTZJC-DGedEdfQ.js → chunk-KX2RTZJC-BuTQbTst.js} +1 -1
  18. package/dist/assets/{chunk-NQ4KR5QH-CVuE5E20.js → chunk-NQ4KR5QH-pwWGt1sO.js} +1 -1
  19. package/dist/assets/{chunk-QZHKN3VN-BDDf3H2l.js → chunk-QZHKN3VN-DzdyrpMz.js} +1 -1
  20. package/dist/assets/{chunk-WL4C6EOR-D57kZtSM.js → chunk-WL4C6EOR-BsqkjkKt.js} +1 -1
  21. package/dist/assets/classDiagram-VBA2DB6C-iCP3aAMs.js +1 -0
  22. package/dist/assets/classDiagram-v2-RAHNMMFH-iCP3aAMs.js +1 -0
  23. package/dist/assets/clone-DykZ1sNt.js +1 -0
  24. package/dist/assets/{cose-bilkent-S5V4N54A-CF2LKXX0.js → cose-bilkent-S5V4N54A-C_Zw3kps.js} +1 -1
  25. package/dist/assets/{dagre-KLK3FWXG-AKm2eMFa.js → dagre-KLK3FWXG-BBym-Z-r.js} +1 -1
  26. package/dist/assets/{diagram-E7M64L7V-Dy0ZpqYE.js → diagram-E7M64L7V-CJK3xILI.js} +1 -1
  27. package/dist/assets/{diagram-IFDJBPK2-BwulOlMM.js → diagram-IFDJBPK2-CG0k9GmI.js} +1 -1
  28. package/dist/assets/{diagram-P4PSJMXO-BlGWqbdU.js → diagram-P4PSJMXO-BHJpgqgl.js} +1 -1
  29. package/dist/assets/{erDiagram-INFDFZHY-DlKoWq8q.js → erDiagram-INFDFZHY-DY2TxE9e.js} +1 -1
  30. package/dist/assets/{flowDiagram-PKNHOUZH-fbS7K31R.js → flowDiagram-PKNHOUZH-C6ujoYh5.js} +1 -1
  31. package/dist/assets/{ganttDiagram-A5KZAMGK-CrXC6aZY.js → ganttDiagram-A5KZAMGK-DacCPw0V.js} +1 -1
  32. package/dist/assets/{gitGraphDiagram-K3NZZRJ6-DgL6ifkO.js → gitGraphDiagram-K3NZZRJ6-1Hd4fFZI.js} +1 -1
  33. package/dist/assets/{graph-D8OSNuBb.js → graph-D8YLYosB.js} +1 -1
  34. package/dist/assets/{index-vXPJvGyq.js → index-DLkyZ-Zb.js} +2 -2
  35. package/dist/assets/{infoDiagram-LFFYTUFH-D-yXUDBn.js → infoDiagram-LFFYTUFH-ClN29cDZ.js} +1 -1
  36. package/dist/assets/{ishikawaDiagram-PHBUUO56-BSLzUZNI.js → ishikawaDiagram-PHBUUO56-DDgBFnJ4.js} +1 -1
  37. package/dist/assets/{journeyDiagram-4ABVD52K-BsZGTLTS.js → journeyDiagram-4ABVD52K-Cl9xLEhq.js} +1 -1
  38. package/dist/assets/{kanban-definition-K7BYSVSG-qHcQ8IDf.js → kanban-definition-K7BYSVSG-CBfTb1XX.js} +1 -1
  39. package/dist/assets/{layout-BT82KtcO.js → layout-BSGsZPU6.js} +1 -1
  40. package/dist/assets/{linear-C-NY3Tys.js → linear-Cm4oUzYV.js} +1 -1
  41. package/dist/assets/{mermaid.core-Iu-Haj2d.js → mermaid.core-Bfq98W72.js} +4 -4
  42. package/dist/assets/{mindmap-definition-YRQLILUH-bYKflbjM.js → mindmap-definition-YRQLILUH-S_0pZiXc.js} +1 -1
  43. package/dist/assets/{pieDiagram-SKSYHLDU-EBV2nFfI.js → pieDiagram-SKSYHLDU-CUof-7Ag.js} +1 -1
  44. package/dist/assets/{quadrantDiagram-337W2JSQ-CDbCpLQ6.js → quadrantDiagram-337W2JSQ-74oAPb7-.js} +1 -1
  45. package/dist/assets/{requirementDiagram-Z7DCOOCP-DTepHdzH.js → requirementDiagram-Z7DCOOCP-Dx_1a8Sm.js} +1 -1
  46. package/dist/assets/{sankeyDiagram-WA2Y5GQK-BDGKah3z.js → sankeyDiagram-WA2Y5GQK-Ollqup46.js} +1 -1
  47. package/dist/assets/{sequenceDiagram-2WXFIKYE-DOTt340N.js → sequenceDiagram-2WXFIKYE-CUQdI1hH.js} +1 -1
  48. package/dist/assets/{stateDiagram-RAJIS63D-C-dx3ZVl.js → stateDiagram-RAJIS63D-DA4bEZTV.js} +1 -1
  49. package/dist/assets/stateDiagram-v2-FVOUBMTO-DaqDUk5Z.js +1 -0
  50. package/dist/assets/{timeline-definition-YZTLITO2-CQuH8MMr.js → timeline-definition-YZTLITO2-hIqAiIh9.js} +1 -1
  51. package/dist/assets/{treemap-KZPCXAKY-Bh6K3rqD.js → treemap-KZPCXAKY-BPMtLzV0.js} +1 -1
  52. package/dist/assets/{vennDiagram-LZ73GAT5-B4Avwovu.js → vennDiagram-LZ73GAT5-8C63L59W.js} +1 -1
  53. package/dist/assets/{xychartDiagram-JWTSCODW-CNDvIAFk.js → xychartDiagram-JWTSCODW-rb793_nJ.js} +1 -1
  54. package/dist/index.html +1 -1
  55. package/lib/perm-bridge.js +122 -0
  56. package/package.json +1 -1
  57. package/server.js +160 -2
  58. package/dist/assets/channel-Bk-jFRd9.js +0 -1
  59. package/dist/assets/classDiagram-VBA2DB6C-BIEQkv07.js +0 -1
  60. package/dist/assets/classDiagram-v2-RAHNMMFH-BIEQkv07.js +0 -1
  61. package/dist/assets/clone-CDtWj5DG.js +0 -1
  62. 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
- res.writeHead(200, { 'Content-Type': mime, 'Content-Length': size });
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};