@tom2012/cc-web 2026.4.26-b → 2026.4.26-d
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 +1 -1
- package/backend/dist/approval-manager.d.ts.map +1 -1
- package/backend/dist/approval-manager.js +19 -1
- package/backend/dist/approval-manager.js.map +1 -1
- package/backend/dist/hooks-manager.d.ts.map +1 -1
- package/backend/dist/hooks-manager.js +10 -1
- package/backend/dist/hooks-manager.js.map +1 -1
- package/backend/dist/index.d.ts.map +1 -1
- package/backend/dist/index.js +99 -120
- package/backend/dist/index.js.map +1 -1
- package/backend/dist/notify-service.d.ts.map +1 -1
- package/backend/dist/notify-service.js +55 -18
- package/backend/dist/notify-service.js.map +1 -1
- package/backend/dist/plugin-manager.d.ts +3 -1
- package/backend/dist/plugin-manager.d.ts.map +1 -1
- package/backend/dist/plugin-manager.js +6 -1
- package/backend/dist/plugin-manager.js.map +1 -1
- package/backend/dist/routes/claude.d.ts.map +1 -1
- package/backend/dist/routes/claude.js +2 -1
- package/backend/dist/routes/claude.js.map +1 -1
- package/backend/dist/routes/filesystem.d.ts.map +1 -1
- package/backend/dist/routes/filesystem.js +88 -25
- package/backend/dist/routes/filesystem.js.map +1 -1
- package/backend/dist/routes/git.d.ts.map +1 -1
- package/backend/dist/routes/git.js +15 -1
- package/backend/dist/routes/git.js.map +1 -1
- package/backend/dist/routes/notify.d.ts.map +1 -1
- package/backend/dist/routes/notify.js +3 -7
- package/backend/dist/routes/notify.js.map +1 -1
- package/backend/dist/routes/plugin-bridge.d.ts.map +1 -1
- package/backend/dist/routes/plugin-bridge.js +53 -3
- package/backend/dist/routes/plugin-bridge.js.map +1 -1
- package/backend/dist/routes/projects.d.ts.map +1 -1
- package/backend/dist/routes/projects.js +22 -1
- package/backend/dist/routes/projects.js.map +1 -1
- package/frontend/dist/assets/{AssistantMessageContent-BBXSX58q.js → AssistantMessageContent-DqHct3Ck.js} +2 -2
- package/frontend/dist/assets/{GraphPreview-BAnpFmtm.js → GraphPreview-DDzwYb3w.js} +2 -2
- package/frontend/dist/assets/MobilePage-5nKL4ztD.js +14 -0
- package/frontend/dist/assets/{OfficePreview-C6EJB80i.js → OfficePreview-BUzkkas_.js} +2 -2
- package/frontend/dist/assets/{ProjectPage-fWJrKu-y.js → ProjectPage-CNFXiRv-.js} +5 -5
- package/frontend/dist/assets/SettingsPage-Dhmz6Kys.js +13 -0
- package/frontend/dist/assets/{SkillHubPage-DJzYMG0j.js → SkillHubPage-DRLSM0ND.js} +3 -3
- package/frontend/dist/assets/{chevron-down-CxmD4ySz.js → chevron-down-CtzzxYuu.js} +1 -1
- package/frontend/dist/assets/{chevron-up-BznxDhNi.js → chevron-up-CBkngNQ6.js} +1 -1
- package/frontend/dist/assets/index-BrpW1kym.js +75 -0
- package/frontend/dist/assets/index-CR3beFmz.css +1 -0
- package/frontend/dist/assets/{index-BptnURrX.js → index-UPu5taSd.js} +1 -1
- package/frontend/dist/assets/index-YAu_Jeuq.js +13 -0
- package/frontend/dist/assets/{jszip.min-CfribT02.js → jszip.min-CIqhAS2U.js} +1 -1
- package/frontend/dist/assets/{search-BPzU-LtZ.js → search-W7udjbkr.js} +1 -1
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/frontend/dist/assets/MobilePage-BR1R_xht.js +0 -14
- package/frontend/dist/assets/SettingsPage-BxT6q4Fq.js +0 -13
- package/frontend/dist/assets/index-Big9FXD0.css +0 -1
- package/frontend/dist/assets/index-CxHOCf_s.js +0 -75
- package/frontend/dist/assets/index-DwNsYVM3.js +0 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A self-hosted web application (distributed as npm package) that provides a browser-based interface for [Claude Code](https://docs.anthropic.com/en/docs/claude-code) CLI sessions. Create projects, each with a persistent terminal running Claude Code, and interact with them through a real-time terminal UI.
|
|
4
4
|
|
|
5
|
-
**Current version**: v2026.4.26-
|
|
5
|
+
**Current version**: v2026.4.26-d | [GitHub](https://github.com/zbc0315/cc-web) | MIT License
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval-manager.d.ts","sourceRoot":"","sources":["../src/approval-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAYH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"approval-manager.d.ts","sourceRoot":"","sources":["../src/approval-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAYH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAyCD,cAAM,eAAe;IACnB,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAA2C;;IAM5D,aAAa,IAAI,MAAM;IAEvB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAI1B,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO;IAQhD,OAAO,CAAC,KAAK;IAIb,qFAAqF;IACrF,QAAQ,CAAC,GAAG,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAkB5E,6EAA6E;IAC7E,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,OAAO;IAWjF,uHAAuH;IACvH,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO;IAWrE,mEAAmE;IACnE,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,EAAE;IAWjD,SAAS,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,GAAG,MAAM,IAAI;IAKvD,OAAO,CAAC,IAAI;CAKb;AAED,MAAM,MAAM,aAAa,GACrB,CAAC;IAAE,IAAI,EAAE,kBAAkB,CAAA;CAAE,GAAG,eAAe,CAAC,GAChD;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAErH,eAAO,MAAM,eAAe,iBAAwB,CAAC"}
|
|
@@ -67,8 +67,26 @@ function loadOrCreateSecret() {
|
|
|
67
67
|
if (fs.existsSync(SECRET_FILE)) {
|
|
68
68
|
try {
|
|
69
69
|
const existing = fs.readFileSync(SECRET_FILE, 'utf-8').trim();
|
|
70
|
-
if (existing)
|
|
70
|
+
if (existing) {
|
|
71
|
+
// Verify file is still 0600. An earlier ccweb version (or a user
|
|
72
|
+
// copy/restore) may have left it world-readable; tighten in place
|
|
73
|
+
// rather than silently trusting it.
|
|
74
|
+
try {
|
|
75
|
+
const st = fs.statSync(SECRET_FILE);
|
|
76
|
+
const tooOpen = (st.mode & 0o077) !== 0;
|
|
77
|
+
if (tooOpen) {
|
|
78
|
+
try {
|
|
79
|
+
fs.chmodSync(SECRET_FILE, 0o600);
|
|
80
|
+
log.warn({ file: SECRET_FILE, oldMode: (st.mode & 0o777).toString(8) }, 'approval-secret was group/other-readable; restored to 0600');
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
log.error({ err, file: SECRET_FILE, mode: (st.mode & 0o777).toString(8) }, 'approval-secret has unsafe perms and chmod failed — fix manually before relying on this secret');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch { /* stat fail — leave as is */ }
|
|
71
88
|
return existing;
|
|
89
|
+
}
|
|
72
90
|
}
|
|
73
91
|
catch (err) {
|
|
74
92
|
log.error({ err }, 'approval-secret exists but unreadable — refusing to regenerate (would break active hooks); fix permissions');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval-manager.js","sourceRoot":"","sources":["../src/approval-manager.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,+CAAiC;AACjC,uCAAyB;AACzB,uCAAyB;AACzB,2CAA6B;AAC7B,qCAAqC;AAErC,MAAM,GAAG,GAAG,IAAA,kBAAS,EAAC,UAAU,CAAC,CAAC;AAElC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,iBAAiB,CAAC,CAAC;AAqBzE,SAAS,kBAAkB;IACzB,IAAI,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;YAC9D,IAAI,QAAQ;
|
|
1
|
+
{"version":3,"file":"approval-manager.js","sourceRoot":"","sources":["../src/approval-manager.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,+CAAiC;AACjC,uCAAyB;AACzB,uCAAyB;AACzB,2CAA6B;AAC7B,qCAAqC;AAErC,MAAM,GAAG,GAAG,IAAA,kBAAS,EAAC,UAAU,CAAC,CAAC;AAElC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,iBAAiB,CAAC,CAAC;AAqBzE,SAAS,kBAAkB;IACzB,IAAI,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;YAC9D,IAAI,QAAQ,EAAE,CAAC;gBACb,iEAAiE;gBACjE,kEAAkE;gBAClE,oCAAoC;gBACpC,IAAI,CAAC;oBACH,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;oBACpC,MAAM,OAAO,GAAG,CAAC,EAAE,CAAC,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;oBACxC,IAAI,OAAO,EAAE,CAAC;wBACZ,IAAI,CAAC;4BACH,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;4BACjC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,4DAA4D,CAAC,CAAC;wBACxI,CAAC;wBAAC,OAAO,GAAG,EAAE,CAAC;4BACb,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,gGAAgG,CAAC,CAAC;wBAC/K,CAAC;oBACH,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC,CAAC,6BAA6B,CAAC,CAAC;gBACzC,OAAO,QAAQ,CAAC;YAClB,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,4GAA4G,CAAC,CAAC;YACjI,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACtD,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACtC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7E,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1E,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,eAAe;IAKnB;QAJQ,YAAO,GAAG,IAAI,GAAG,EAAwB,CAAC;QAE1C,cAAS,GAAG,IAAI,GAAG,EAAgC,CAAC;QAG1D,IAAI,CAAC,MAAM,GAAG,kBAAkB,EAAE,CAAC;IACrC,CAAC;IAED,aAAa,KAAa,OAAO,WAAW,CAAC,CAAC,CAAC;IAE/C,IAAI,CAAC,IAAY;QACf,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7E,CAAC;IAED,MAAM,CAAC,IAAY,EAAE,SAAiB;QACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QACvC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC;QACvD,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC1D,OAAO,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACtC,CAAC;IAEO,KAAK,CAAC,SAAiB,EAAE,SAAiB;QAChD,OAAO,GAAG,SAAS,IAAI,SAAS,EAAE,CAAC;IACrC,CAAC;IAED,qFAAqF;IACrF,QAAQ,CAAC,GAAoB,EAAE,SAAiB;QAC9C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;QACrD,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC,CAAC;QAC7E,CAAC;QACD,OAAO,IAAI,OAAO,CAAmB,CAAC,OAAO,EAAE,EAAE;YAC/C,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACpC,IAAI,CAAC,KAAK;oBAAE,OAAO;gBACnB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACzB,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;gBAClI,OAAO,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC,CAAC;YAC7D,CAAC,EAAE,SAAS,CAAC,CAAC;YACd,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;YAClD,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;IACL,CAAC;IAED,6EAA6E;IAC7E,MAAM,CAAC,SAAiB,EAAE,SAAiB,EAAE,QAA0B;QACrE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,CAAC,KAAK;YAAE,OAAO,KAAK,CAAC;QACzB,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC1B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACzB,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC5F,OAAO,IAAI,CAAC;IACd,CAAC;IAED,uHAAuH;IACvH,MAAM,CAAC,SAAiB,EAAE,SAAiB,EAAE,MAAc;QACzD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,CAAC,KAAK;YAAE,OAAO,KAAK,CAAC;QACzB,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC1B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACzB,KAAK,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QACrD,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACzF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,mEAAmE;IACnE,WAAW,CAAC,SAAiB;QAC3B,MAAM,GAAG,GAAsB,EAAE,CAAC;QAClC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1C,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;gBAClC,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC;gBAClD,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjB,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,SAAS,CAAC,EAAgC;QACxC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvB,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC;IAEO,IAAI,CAAC,GAAkB;QAC7B,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAChC,IAAI,CAAC;gBAAC,EAAE,CAAC,GAAG,CAAC,CAAC;YAAC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBAAC,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,yBAAyB,CAAC,CAAC;YAAC,CAAC;QAChF,CAAC;IACH,CAAC;CACF;AAMY,QAAA,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks-manager.d.ts","sourceRoot":"","sources":["../src/hooks-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;
|
|
1
|
+
{"version":3,"file":"hooks-manager.d.ts","sourceRoot":"","sources":["../src/hooks-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAuCH,cAAM,YAAY;IAChB,OAAO,CAAC,QAAQ,CAAS;gBAEb,QAAQ,EAAE,MAAM;IAI5B,wFAAwF;IACxF,OAAO,CAAC,mBAAmB;IAwC3B,2CAA2C;IAC3C,OAAO,CAAC,iBAAiB;IAoCzB,6DAA6D;IAC7D,SAAS,IAAI,IAAI;IAMjB,mGAAmG;IACnG,OAAO,IAAI,IAAI;IAMf,WAAW,IAAI,OAAO;CAavB;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -62,7 +62,16 @@ function readSettings(settingsPath) {
|
|
|
62
62
|
if (!fs.existsSync(settingsPath))
|
|
63
63
|
return {};
|
|
64
64
|
try {
|
|
65
|
-
|
|
65
|
+
const parsed = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
|
66
|
+
// Refuse non-plain-object roots — a settings.json that parsed to an
|
|
67
|
+
// array or primitive would survive readSettings, get a named property
|
|
68
|
+
// assigned (`settings.hooks = …`), and stringify back into invalid
|
|
69
|
+
// structure that Claude Code can no longer parse.
|
|
70
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
71
|
+
log.warn({ settingsPath }, 'settings file is not a plain object — hook management skipped');
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
return parsed;
|
|
66
75
|
}
|
|
67
76
|
catch {
|
|
68
77
|
log.warn({ settingsPath }, 'settings file has invalid JSON — hook management skipped');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks-manager.js","sourceRoot":"","sources":["../src/hooks-manager.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,uCAAyB;AACzB,2CAA6B;AAC7B,yCAAwC;AAGxC,qCAAqC;AAErC,MAAM,GAAG,GAAG,IAAA,kBAAS,EAAC,OAAO,CAAC,CAAC;AAC/B,MAAM,YAAY,GAAG,cAAc,CAAC;AAEpC,SAAS,YAAY,CAAC,YAAoB;IACxC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO,EAAE,CAAC;IAC5C,IAAI,CAAC;QACH,
|
|
1
|
+
{"version":3,"file":"hooks-manager.js","sourceRoot":"","sources":["../src/hooks-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA+EA,QAAA,MAAM,GAAG,6CAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA+EA,QAAA,MAAM,GAAG,6CAAY,CAAC;AAk0BtB,eAAe,GAAG,CAAC"}
|
package/backend/dist/index.js
CHANGED
|
@@ -323,15 +323,43 @@ function writeTerminalInputSplit(projectId, data) {
|
|
|
323
323
|
terminal_manager_1.terminalManager.writeRaw(projectId, submitCr);
|
|
324
324
|
});
|
|
325
325
|
}
|
|
326
|
+
/**
|
|
327
|
+
* Bounded WS send. Skips closed sockets, enforces a per-socket bufferedAmount
|
|
328
|
+
* ceiling so a slow/stuck client cannot accumulate unbounded backlog. When the
|
|
329
|
+
* ceiling is hit we terminate the socket — preferable to silently growing the
|
|
330
|
+
* Node heap. Replaces direct `ws.send` calls per CLAUDE.md red line ("WS send
|
|
331
|
+
* 必须经队列, 不直接 ws.send").
|
|
332
|
+
*
|
|
333
|
+
* Threshold (8 MiB) is generous for normal terminal data + chat blocks; only
|
|
334
|
+
* pathological clients hit it. A real queue with drop policy would be more
|
|
335
|
+
* sophisticated but this single guard already closes the unbounded-memory hole.
|
|
336
|
+
*/
|
|
337
|
+
const WS_BACKPRESSURE_LIMIT_BYTES = 8 * 1024 * 1024;
|
|
338
|
+
function safeSend(ws, payload) {
|
|
339
|
+
if (ws.readyState !== WebSocket.WebSocket.OPEN)
|
|
340
|
+
return false;
|
|
341
|
+
if (ws.bufferedAmount > WS_BACKPRESSURE_LIMIT_BYTES) {
|
|
342
|
+
try {
|
|
343
|
+
ws.terminate();
|
|
344
|
+
}
|
|
345
|
+
catch { /**/ }
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
try {
|
|
349
|
+
ws.send(payload);
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
catch {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
326
356
|
function broadcast(projectId, rawData) {
|
|
327
357
|
const clients = projectClients.get(projectId);
|
|
328
358
|
if (!clients)
|
|
329
359
|
return;
|
|
330
360
|
const payload = JSON.stringify({ type: 'terminal_data', data: rawData });
|
|
331
|
-
for (const client of clients)
|
|
332
|
-
|
|
333
|
-
client.send(payload);
|
|
334
|
-
}
|
|
361
|
+
for (const client of clients)
|
|
362
|
+
safeSend(client, payload);
|
|
335
363
|
}
|
|
336
364
|
// Approval events leak tool inputs (command strings, file paths). Withhold from view-only clients.
|
|
337
365
|
approval_manager_1.approvalManager.subscribe((evt) => {
|
|
@@ -342,14 +370,9 @@ approval_manager_1.approvalManager.subscribe((evt) => {
|
|
|
342
370
|
return;
|
|
343
371
|
const payload = JSON.stringify(evt);
|
|
344
372
|
for (const client of clients) {
|
|
345
|
-
if (client.readyState !== WebSocket.WebSocket.OPEN)
|
|
346
|
-
continue;
|
|
347
373
|
if (client.__readOnly)
|
|
348
374
|
continue;
|
|
349
|
-
|
|
350
|
-
client.send(payload);
|
|
351
|
-
}
|
|
352
|
-
catch { /* ignore */ }
|
|
375
|
+
safeSend(client, payload);
|
|
353
376
|
}
|
|
354
377
|
});
|
|
355
378
|
function isLocalWs(req) {
|
|
@@ -359,6 +382,32 @@ function isLocalWs(req) {
|
|
|
359
382
|
// ── Dashboard WebSocket clients (activity push) ─────────────────────────────
|
|
360
383
|
const dashboardClients = new Set();
|
|
361
384
|
const SEMANTIC_STALE_MS = 30000;
|
|
385
|
+
/**
|
|
386
|
+
* Per-user visibility check used by every dashboard WS broadcast. Without it,
|
|
387
|
+
* any connected user sees activity / semantic / project_stopped events for
|
|
388
|
+
* every other user's projects (cross-user information leak). __username is
|
|
389
|
+
* tagged on the socket at auth time (localhost path → admin; jwt path → user).
|
|
390
|
+
*/
|
|
391
|
+
function userCanSeeProject(username, projectId) {
|
|
392
|
+
if (!username)
|
|
393
|
+
return false;
|
|
394
|
+
if ((0, config_1.isAdminUser)(username))
|
|
395
|
+
return true; // admin (incl. localhost preauth) sees all
|
|
396
|
+
const project = (0, config_1.getProject)(projectId);
|
|
397
|
+
if (!project)
|
|
398
|
+
return false;
|
|
399
|
+
if ((0, config_1.isProjectOwner)(project, username))
|
|
400
|
+
return true;
|
|
401
|
+
return project.shares?.some((s) => s.username === username) ?? false;
|
|
402
|
+
}
|
|
403
|
+
function sendToDashboardClientsForProject(projectId, payload) {
|
|
404
|
+
for (const client of dashboardClients) {
|
|
405
|
+
const clientUser = client.__username;
|
|
406
|
+
if (!userCanSeeProject(clientUser, projectId))
|
|
407
|
+
continue;
|
|
408
|
+
safeSend(client, payload);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
362
411
|
function broadcastDashboardActivity(projectId, lastActivityAt) {
|
|
363
412
|
if (dashboardClients.size === 0)
|
|
364
413
|
return;
|
|
@@ -373,14 +422,7 @@ function broadcastDashboardActivity(projectId, lastActivityAt) {
|
|
|
373
422
|
status: terminal_manager_1.terminalManager.getProjectStatus(projectId),
|
|
374
423
|
semantic: semantic && !stale ? semantic : undefined,
|
|
375
424
|
});
|
|
376
|
-
|
|
377
|
-
if (client.readyState === WebSocket.OPEN) {
|
|
378
|
-
try {
|
|
379
|
-
client.send(payload);
|
|
380
|
-
}
|
|
381
|
-
catch { /**/ }
|
|
382
|
-
}
|
|
383
|
-
}
|
|
425
|
+
sendToDashboardClientsForProject(projectId, payload);
|
|
384
426
|
}
|
|
385
427
|
function broadcastDashboardSemantic(projectId, status) {
|
|
386
428
|
if (dashboardClients.size === 0)
|
|
@@ -397,14 +439,7 @@ function broadcastDashboardSemantic(projectId, status) {
|
|
|
397
439
|
status: terminal_manager_1.terminalManager.getProjectStatus(projectId),
|
|
398
440
|
semantic: status ?? undefined,
|
|
399
441
|
});
|
|
400
|
-
|
|
401
|
-
if (client.readyState === WebSocket.OPEN) {
|
|
402
|
-
try {
|
|
403
|
-
client.send(payload);
|
|
404
|
-
}
|
|
405
|
-
catch { /**/ }
|
|
406
|
-
}
|
|
407
|
-
}
|
|
442
|
+
sendToDashboardClientsForProject(projectId, payload);
|
|
408
443
|
}
|
|
409
444
|
function buildSemanticSnapshot(projectId) {
|
|
410
445
|
const semantic = session_manager_1.sessionManager.getSemanticStatus(projectId);
|
|
@@ -416,14 +451,8 @@ function broadcastProjectSemantic(projectId, status) {
|
|
|
416
451
|
if (!clients || clients.size === 0)
|
|
417
452
|
return;
|
|
418
453
|
const payload = JSON.stringify({ type: 'semantic_update', active: !!status, semantic: status ?? undefined });
|
|
419
|
-
for (const client of clients)
|
|
420
|
-
|
|
421
|
-
try {
|
|
422
|
-
client.send(payload);
|
|
423
|
-
}
|
|
424
|
-
catch { /**/ }
|
|
425
|
-
}
|
|
426
|
-
}
|
|
454
|
+
for (const client of clients)
|
|
455
|
+
safeSend(client, payload);
|
|
427
456
|
}
|
|
428
457
|
/**
|
|
429
458
|
* PTY-driven `semantic_update` push. Emits `{active: true, semantic?:...}`
|
|
@@ -447,14 +476,8 @@ function broadcastProjectActivity(projectId, lastActivityAt) {
|
|
|
447
476
|
active: Date.now() - lastActivityAt < 3000,
|
|
448
477
|
semantic: fresh ? semantic : undefined,
|
|
449
478
|
});
|
|
450
|
-
for (const client of clients)
|
|
451
|
-
|
|
452
|
-
try {
|
|
453
|
-
client.send(payload);
|
|
454
|
-
}
|
|
455
|
-
catch { /**/ }
|
|
456
|
-
}
|
|
457
|
-
}
|
|
479
|
+
for (const client of clients)
|
|
480
|
+
safeSend(client, payload);
|
|
458
481
|
}
|
|
459
482
|
/**
|
|
460
483
|
* When PTY / semantic activity stops, the project WS needs an explicit
|
|
@@ -474,14 +497,8 @@ function broadcastProjectIdle(projectId) {
|
|
|
474
497
|
if (!clients || clients.size === 0)
|
|
475
498
|
return;
|
|
476
499
|
const payload = JSON.stringify({ type: 'semantic_update', active: false, semantic: undefined });
|
|
477
|
-
for (const client of clients)
|
|
478
|
-
|
|
479
|
-
try {
|
|
480
|
-
client.send(payload);
|
|
481
|
-
}
|
|
482
|
-
catch { /**/ }
|
|
483
|
-
}
|
|
484
|
-
}
|
|
500
|
+
for (const client of clients)
|
|
501
|
+
safeSend(client, payload);
|
|
485
502
|
}
|
|
486
503
|
function armProjectIdleTimer(projectId) {
|
|
487
504
|
const prev = projectIdleTimers.get(projectId);
|
|
@@ -503,16 +520,20 @@ function initProjectTerminal(project, projectId) {
|
|
|
503
520
|
terminal_manager_1.terminalManager.updateBroadcast(projectId, fn);
|
|
504
521
|
}
|
|
505
522
|
function sendActivitySnapshot(ws) {
|
|
523
|
+
const username = ws.__username;
|
|
506
524
|
const allActivity = terminal_manager_1.terminalManager.getAllActivity();
|
|
507
525
|
const allSemantic = session_manager_1.sessionManager.getAllSemanticStatus();
|
|
508
526
|
// Include all running/restarting projects, even those with no PTY output yet
|
|
509
527
|
const allRunningIds = new Set([...Object.keys(allActivity), ...terminal_manager_1.terminalManager.getAllRunningIds()]);
|
|
510
528
|
const now = Date.now();
|
|
511
529
|
for (const id of allRunningIds) {
|
|
530
|
+
// Per-user filter: never leak other users' project state on initial snapshot.
|
|
531
|
+
if (!userCanSeeProject(username, id))
|
|
532
|
+
continue;
|
|
512
533
|
const lastActivityAt = allActivity[id] ?? 0;
|
|
513
534
|
const semantic = allSemantic[id];
|
|
514
535
|
const stale = semantic && now - semantic.updatedAt > SEMANTIC_STALE_MS;
|
|
515
|
-
ws
|
|
536
|
+
safeSend(ws, JSON.stringify({
|
|
516
537
|
type: 'activity_update',
|
|
517
538
|
projectId: id,
|
|
518
539
|
lastActivityAt,
|
|
@@ -545,27 +566,12 @@ session_manager_1.sessionManager.on('semantic', ({ projectId, status }) => {
|
|
|
545
566
|
});
|
|
546
567
|
notify_service_1.notifyService.on('stopped', ({ projectId, projectName }) => {
|
|
547
568
|
const msg = JSON.stringify({ type: 'project_stopped', projectId, projectName });
|
|
548
|
-
//
|
|
549
|
-
|
|
550
|
-
if (client.readyState === WebSocket.OPEN) {
|
|
551
|
-
try {
|
|
552
|
-
client.send(msg);
|
|
553
|
-
}
|
|
554
|
-
catch { /**/ }
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
// Broadcast to project-specific clients (so ProjectPage also receives notifications)
|
|
569
|
+
// Per-user filter on dashboard side (project clients are already per-project + ownership-gated at subscribe time).
|
|
570
|
+
sendToDashboardClientsForProject(projectId, msg);
|
|
558
571
|
const clients = projectClients.get(projectId);
|
|
559
|
-
if (clients)
|
|
560
|
-
for (const client of clients)
|
|
561
|
-
|
|
562
|
-
try {
|
|
563
|
-
client.send(msg);
|
|
564
|
-
}
|
|
565
|
-
catch { /**/ }
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
}
|
|
572
|
+
if (clients)
|
|
573
|
+
for (const client of clients)
|
|
574
|
+
safeSend(client, msg);
|
|
569
575
|
});
|
|
570
576
|
// ── Sync progress bridge ────────────────────────────────────────────────────
|
|
571
577
|
// Forward rsync-driven start/progress/done events to both the per-project WS
|
|
@@ -581,26 +587,14 @@ notify_service_1.notifyService.on('stopped', ({ projectId, projectName }) => {
|
|
|
581
587
|
sync_service_1.syncEvents.on('event', (evt) => {
|
|
582
588
|
const payload = JSON.stringify({ type: `sync.${evt.kind}`, ...evt });
|
|
583
589
|
const projectBucket = projectClients.get(evt.projectId);
|
|
584
|
-
if (projectBucket)
|
|
585
|
-
for (const client of projectBucket)
|
|
586
|
-
|
|
587
|
-
try {
|
|
588
|
-
client.send(payload);
|
|
589
|
-
}
|
|
590
|
-
catch { /**/ }
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
}
|
|
590
|
+
if (projectBucket)
|
|
591
|
+
for (const client of projectBucket)
|
|
592
|
+
safeSend(client, payload);
|
|
594
593
|
for (const client of dashboardClients) {
|
|
595
|
-
if (client.readyState !== WebSocket.OPEN)
|
|
596
|
-
continue;
|
|
597
594
|
const clientUser = client.__username;
|
|
598
595
|
if (clientUser !== evt.username)
|
|
599
596
|
continue;
|
|
600
|
-
|
|
601
|
-
client.send(payload);
|
|
602
|
-
}
|
|
603
|
-
catch { /**/ }
|
|
597
|
+
safeSend(client, payload);
|
|
604
598
|
}
|
|
605
599
|
});
|
|
606
600
|
wss.on('connection', (ws, req) => {
|
|
@@ -650,10 +644,7 @@ wss.on('connection', (ws, req) => {
|
|
|
650
644
|
let authenticated = localConnection; // localhost = pre-authenticated
|
|
651
645
|
let wsReadOnly = false; // true for view-only shared projects
|
|
652
646
|
const chatListener = (msg) => {
|
|
653
|
-
|
|
654
|
-
ws.send(JSON.stringify({ type: 'chat_message', ...msg }));
|
|
655
|
-
}
|
|
656
|
-
catch { /**/ }
|
|
647
|
+
safeSend(ws, JSON.stringify({ type: 'chat_message', ...msg }));
|
|
657
648
|
};
|
|
658
649
|
const authTimeout = localConnection ? null : setTimeout(() => {
|
|
659
650
|
if (!authenticated)
|
|
@@ -663,13 +654,13 @@ wss.on('connection', (ws, req) => {
|
|
|
663
654
|
if (localConnection) {
|
|
664
655
|
const project = (0, config_1.getProject)(projectId);
|
|
665
656
|
if (!project) {
|
|
666
|
-
ws
|
|
657
|
+
safeSend(ws, JSON.stringify({ type: 'error', message: 'Project not found' }));
|
|
667
658
|
ws.close(1008, 'Project not found');
|
|
668
659
|
return;
|
|
669
660
|
}
|
|
670
661
|
initProjectTerminal(project, projectId);
|
|
671
|
-
ws
|
|
672
|
-
ws
|
|
662
|
+
safeSend(ws, JSON.stringify({ type: 'connected', projectId }));
|
|
663
|
+
safeSend(ws, JSON.stringify({ type: 'status', status: project.status }));
|
|
673
664
|
}
|
|
674
665
|
ws.on('message', (rawMsg) => {
|
|
675
666
|
try {
|
|
@@ -696,7 +687,7 @@ wss.on('connection', (ws, req) => {
|
|
|
696
687
|
authenticated = true;
|
|
697
688
|
const project = (0, config_1.getProject)(projectId);
|
|
698
689
|
if (!project) {
|
|
699
|
-
ws
|
|
690
|
+
safeSend(ws, JSON.stringify({ type: 'error', message: 'Project not found' }));
|
|
700
691
|
ws.close(1008, 'Project not found');
|
|
701
692
|
return;
|
|
702
693
|
}
|
|
@@ -705,7 +696,7 @@ wss.on('connection', (ws, req) => {
|
|
|
705
696
|
if (!(0, config_1.isProjectOwner)(project, wsUsername)) {
|
|
706
697
|
const share = project.shares?.find((s) => s.username === wsUsername);
|
|
707
698
|
if (!share) {
|
|
708
|
-
ws
|
|
699
|
+
safeSend(ws, JSON.stringify({ type: 'error', message: 'Access denied' }));
|
|
709
700
|
ws.close(1008, 'Access denied');
|
|
710
701
|
return;
|
|
711
702
|
}
|
|
@@ -714,8 +705,8 @@ wss.on('connection', (ws, req) => {
|
|
|
714
705
|
}
|
|
715
706
|
ws.__readOnly = wsReadOnly;
|
|
716
707
|
initProjectTerminal(project, projectId);
|
|
717
|
-
ws
|
|
718
|
-
ws
|
|
708
|
+
safeSend(ws, JSON.stringify({ type: 'connected', projectId, readOnly: wsReadOnly }));
|
|
709
|
+
safeSend(ws, JSON.stringify({ type: 'status', status: project.status }));
|
|
719
710
|
return;
|
|
720
711
|
}
|
|
721
712
|
// For local connections, skip the auth message if sent anyway
|
|
@@ -732,22 +723,22 @@ wss.on('connection', (ws, req) => {
|
|
|
732
723
|
{
|
|
733
724
|
const scrollback = terminal_manager_1.terminalManager.getScrollback(projectId);
|
|
734
725
|
if (scrollback)
|
|
735
|
-
ws
|
|
726
|
+
safeSend(ws, JSON.stringify({ type: 'terminal_data', data: scrollback }));
|
|
736
727
|
}
|
|
737
728
|
// Register as a live client
|
|
738
729
|
if (!projectClients.has(projectId))
|
|
739
730
|
projectClients.set(projectId, new Set());
|
|
740
731
|
projectClients.get(projectId).add(ws);
|
|
741
|
-
ws
|
|
732
|
+
safeSend(ws, JSON.stringify({ type: 'terminal_subscribed' }));
|
|
742
733
|
// Send initial context data if available
|
|
743
734
|
{
|
|
744
735
|
const ctxData = (0, hooks_1.getContextData)(projectId);
|
|
745
736
|
if (ctxData)
|
|
746
|
-
ws
|
|
737
|
+
safeSend(ws, JSON.stringify({ type: 'context_update', ...ctxData }));
|
|
747
738
|
}
|
|
748
739
|
{
|
|
749
740
|
const snap = buildSemanticSnapshot(projectId);
|
|
750
|
-
ws
|
|
741
|
+
safeSend(ws, JSON.stringify({ type: 'semantic_update', ...snap }));
|
|
751
742
|
}
|
|
752
743
|
break;
|
|
753
744
|
case 'terminal_input':
|
|
@@ -773,10 +764,7 @@ wss.on('connection', (ws, req) => {
|
|
|
773
764
|
const history = session_manager_1.sessionManager.getChatHistory(projectId);
|
|
774
765
|
const slice = replayLimit >= history.length ? history : history.slice(-replayLimit);
|
|
775
766
|
for (const block of slice) {
|
|
776
|
-
|
|
777
|
-
ws.send(JSON.stringify({ type: 'chat_message', ...block }));
|
|
778
|
-
}
|
|
779
|
-
catch { /**/ }
|
|
767
|
+
safeSend(ws, JSON.stringify({ type: 'chat_message', ...block }));
|
|
780
768
|
}
|
|
781
769
|
}
|
|
782
770
|
}
|
|
@@ -790,21 +778,18 @@ wss.on('connection', (ws, req) => {
|
|
|
790
778
|
{
|
|
791
779
|
const ctxData = (0, hooks_1.getContextData)(projectId);
|
|
792
780
|
if (ctxData)
|
|
793
|
-
ws
|
|
781
|
+
safeSend(ws, JSON.stringify({ type: 'context_update', ...ctxData }));
|
|
794
782
|
}
|
|
795
783
|
{
|
|
796
784
|
const snap = buildSemanticSnapshot(projectId);
|
|
797
|
-
ws
|
|
785
|
+
safeSend(ws, JSON.stringify({ type: 'semantic_update', ...snap }));
|
|
798
786
|
}
|
|
799
787
|
break;
|
|
800
788
|
}
|
|
801
789
|
}
|
|
802
790
|
catch (err) {
|
|
803
791
|
log.error({ err, projectId, mod: 'ws' }, 'ws message handling error');
|
|
804
|
-
|
|
805
|
-
ws.send(JSON.stringify({ type: 'error', message: 'Internal server error' }));
|
|
806
|
-
}
|
|
807
|
-
catch { /**/ }
|
|
792
|
+
safeSend(ws, JSON.stringify({ type: 'error', message: 'Internal server error' }));
|
|
808
793
|
}
|
|
809
794
|
});
|
|
810
795
|
ws.on('close', () => {
|
|
@@ -893,14 +878,8 @@ function tryListen(port, maxAttempts = 20) {
|
|
|
893
878
|
if (!clients)
|
|
894
879
|
return;
|
|
895
880
|
const payload = JSON.stringify({ type: 'context_update', ...data });
|
|
896
|
-
for (const client of clients)
|
|
897
|
-
|
|
898
|
-
try {
|
|
899
|
-
client.send(payload);
|
|
900
|
-
}
|
|
901
|
-
catch { /**/ }
|
|
902
|
-
}
|
|
903
|
-
}
|
|
881
|
+
for (const client of clients)
|
|
882
|
+
safeSend(client, payload);
|
|
904
883
|
});
|
|
905
884
|
});
|
|
906
885
|
}
|