upfynai-code 2.6.0 → 2.6.1
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 +123 -88
- package/bin/cli.js +63 -0
- package/package.json +48 -106
- package/src/auth.js +115 -0
- package/src/config.js +33 -0
- package/src/connect.js +314 -0
- package/src/launch.js +54 -0
- package/src/mcp.js +57 -0
- package/src/server.js +54 -0
- package/client/dist/api-docs.html +0 -879
- package/client/dist/assets/AppContent-C0CyP3g5.js +0 -513
- package/client/dist/assets/CanvasPanel-0u9QR7U-.js +0 -34
- package/client/dist/assets/CanvasPanel-WhZulBJw.css +0 -1
- package/client/dist/assets/DashboardPanel-Dgqw1yZk.js +0 -1
- package/client/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/client/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/client/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/client/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/client/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/client/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/client/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/client/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/client/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/client/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/client/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/client/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/client/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/client/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/client/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/client/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/client/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/client/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/client/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/client/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/client/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/client/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/client/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/client/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/client/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/client/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/client/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/client/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/client/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/client/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/client/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/client/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/client/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/client/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/client/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/client/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/client/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/client/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/client/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/client/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/client/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/client/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/client/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/client/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/client/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/client/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/client/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/client/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/client/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/client/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/client/dist/assets/LoginModal-CZDEzqjK.js +0 -19
- package/client/dist/assets/MarkdownPreview-CYdvwJaV.js +0 -1
- package/client/dist/assets/Onboarding-DR6NZ4Vz.js +0 -1
- package/client/dist/assets/SetupForm-D49gtWY4.js +0 -1
- package/client/dist/assets/Tableau10-B-NsZVaP.js +0 -1
- package/client/dist/assets/WorkflowsPanel-CqlbEJA_.js +0 -1
- package/client/dist/assets/_commonjs-dynamic-modules-TDtrdbi3.js +0 -1
- package/client/dist/assets/ar-SA-G6X2FPQ2-BWqa1yBH.js +0 -10
- package/client/dist/assets/arc-BegSKqEW.js +0 -1
- package/client/dist/assets/array-BKyUJesY.js +0 -1
- package/client/dist/assets/az-AZ-76LH7QW2-DrVlbZDP.js +0 -1
- package/client/dist/assets/bg-BG-XCXSNQG7-DdunjBgT.js +0 -5
- package/client/dist/assets/blockDiagram-38ab4fdb-BKMbwGHu.js +0 -118
- package/client/dist/assets/bn-BD-2XOGV67Q-_7DtmvwO.js +0 -5
- package/client/dist/assets/c4Diagram-3d4e48cf-hJuiHhSn.js +0 -10
- package/client/dist/assets/ca-ES-6MX7JW3Y-BFIrmojG.js +0 -8
- package/client/dist/assets/channel-Bur-rRTp.js +0 -1
- package/client/dist/assets/classDiagram-70f12bd4-BjiAf9cM.js +0 -2
- package/client/dist/assets/classDiagram-v2-f2320105-pwBewejc.js +0 -2
- package/client/dist/assets/clone-BtqXeoBJ.js +0 -1
- package/client/dist/assets/createText-2e5e7dd3-Dq_acOWe.js +0 -5
- package/client/dist/assets/cs-CZ-2BRQDIVT-B-x4F6TJ.js +0 -11
- package/client/dist/assets/da-DK-5WZEPLOC-Btlc8Dgn.js +0 -5
- package/client/dist/assets/de-DE-XR44H4JA-BVu3ZIoD.js +0 -8
- package/client/dist/assets/directory-open-01563666-DWU9wJ6I.js +0 -1
- package/client/dist/assets/directory-open-4ed118d0-CunoC1EB.js +0 -1
- package/client/dist/assets/edges-e0da2a9e-DH0wVTXR.js +0 -4
- package/client/dist/assets/el-GR-BZB4AONW-h2ll8_ZC.js +0 -10
- package/client/dist/assets/erDiagram-9861fffd-BYezLIR7.js +0 -51
- package/client/dist/assets/es-ES-U4NZUMDT-Cveiulwt.js +0 -9
- package/client/dist/assets/eu-ES-A7QVB2H4-DQluL2PY.js +0 -11
- package/client/dist/assets/fa-IR-HGAKTJCU-BJtcMBSv.js +0 -8
- package/client/dist/assets/fi-FI-Z5N7JZ37-D8NfbVXV.js +0 -6
- package/client/dist/assets/file-open-002ab408-DIuFHtCF.js +0 -1
- package/client/dist/assets/file-open-7c801643-684qeFg4.js +0 -1
- package/client/dist/assets/file-save-3189631c-C1wFhQhH.js +0 -1
- package/client/dist/assets/file-save-745eba88-Bb9F9Kg7.js +0 -1
- package/client/dist/assets/flowDb-956e92f1-scnUykhM.js +0 -10
- package/client/dist/assets/flowDiagram-66a62f08-jVyWsfyU.js +0 -4
- package/client/dist/assets/flowDiagram-v2-96b9c2cf-N6xgi25h.js +0 -1
- package/client/dist/assets/flowchart-elk-definition-4a651766-gKGX3HqR.js +0 -139
- package/client/dist/assets/fr-FR-RHASNOE6-vdj42kC6.js +0 -9
- package/client/dist/assets/ganttDiagram-c361ad54-C2CiWFUP.js +0 -257
- package/client/dist/assets/gitGraphDiagram-72cf32ee-C59Yz2LK.js +0 -70
- package/client/dist/assets/gl-ES-HMX3MZ6V-DQo0TzoP.js +0 -10
- package/client/dist/assets/graph-Dx_H43Kv.js +0 -1
- package/client/dist/assets/he-IL-6SHJWFNN-DKXK5e33.js +0 -10
- package/client/dist/assets/hi-IN-IWLTKZ5I-C2Qgqc0R.js +0 -4
- package/client/dist/assets/hu-HU-A5ZG7DT2-Ss-6vX0m.js +0 -7
- package/client/dist/assets/id-ID-SAP4L64H-D7Wsg1S2.js +0 -10
- package/client/dist/assets/image-blob-reduce.esm-D6s-rqMO.js +0 -7
- package/client/dist/assets/index-3862675e-u8Nv7hHC.js +0 -1
- package/client/dist/assets/index-BVowJdZF.js +0 -97
- package/client/dist/assets/index-ce18TYkg.js +0 -27
- package/client/dist/assets/index-kQoJx-bc.css +0 -1
- package/client/dist/assets/infoDiagram-f8f76790-LmoJYsxo.js +0 -7
- package/client/dist/assets/init-Gi6I4Gst.js +0 -1
- package/client/dist/assets/it-IT-JPQ66NNP-CAPTVl7M.js +0 -11
- package/client/dist/assets/ja-JP-DBVTYXUO-eNVPawR2.js +0 -8
- package/client/dist/assets/journeyDiagram-49397b02-BaJqehpR.js +0 -139
- package/client/dist/assets/kaa-6HZHGXH3-tpuNkKhS.js +0 -1
- package/client/dist/assets/kab-KAB-ZGHBKWFO-Dp83kx4x.js +0 -8
- package/client/dist/assets/kk-KZ-P5N5QNE5-B9IlC6YN.js +0 -1
- package/client/dist/assets/km-KH-HSX4SM5Z-B_KMYaMj.js +0 -11
- package/client/dist/assets/ko-KR-MTYHY66A-yebnUNdb.js +0 -9
- package/client/dist/assets/ku-TR-6OUDTVRD-BR6fh6-5.js +0 -9
- package/client/dist/assets/layout-DLl5Jwcl.js +0 -1
- package/client/dist/assets/line-FpB7omSK.js +0 -1
- package/client/dist/assets/linear-CkXqUFJ8.js +0 -1
- package/client/dist/assets/lt-LT-XHIRWOB4-SutZSWtR.js +0 -3
- package/client/dist/assets/lv-LV-5QDEKY6T-DuAxdcZL.js +0 -7
- package/client/dist/assets/mindmap-definition-fc14e90a-DyxXOExh.js +0 -425
- package/client/dist/assets/mr-IN-CRQNXWMA-DqDUWM_8.js +0 -13
- package/client/dist/assets/my-MM-5M5IBNSE-C40kMFMR.js +0 -1
- package/client/dist/assets/nb-NO-T6EIAALU-DVij32Ju.js +0 -10
- package/client/dist/assets/nl-NL-IS3SIHDZ-rT84mDYq.js +0 -8
- package/client/dist/assets/nn-NO-6E72VCQL-BBZXBW8V.js +0 -8
- package/client/dist/assets/oc-FR-POXYY2M6-DzjOugOf.js +0 -8
- package/client/dist/assets/ordinal-Cboi1Yqb.js +0 -1
- package/client/dist/assets/pa-IN-N4M65BXN-DD1iU8_F.js +0 -4
- package/client/dist/assets/path-CbwjOpE9.js +0 -1
- package/client/dist/assets/pdf-CE_K4jFx.js +0 -12
- package/client/dist/assets/pdf.worker-BA9kU3Pw.mjs +0 -61080
- package/client/dist/assets/percentages-BXMCSKIN-WVlHS4wx.js +0 -207
- package/client/dist/assets/pica-CQIY57Tf.js +0 -7
- package/client/dist/assets/pieDiagram-8a3498a8-Dd_85qBH.js +0 -35
- package/client/dist/assets/pl-PL-T2D74RX3-ukVXa48G.js +0 -9
- package/client/dist/assets/pt-BR-5N22H2LF-BibawarT.js +0 -9
- package/client/dist/assets/pt-PT-UZXXM6DQ-So3i9l9w.js +0 -9
- package/client/dist/assets/quadrantDiagram-120e2f19-C4dFVDEx.js +0 -7
- package/client/dist/assets/requirementDiagram-deff3bca-DrTO7yFl.js +0 -52
- package/client/dist/assets/ro-RO-JPDTUUEW-DY0Xq_Hd.js +0 -11
- package/client/dist/assets/roundRect-0PYZxl1G.js +0 -1
- package/client/dist/assets/ru-RU-B4JR7IUQ-B7u_Zvkd.js +0 -9
- package/client/dist/assets/sankeyDiagram-04a897e0-D24gfzuS.js +0 -8
- package/client/dist/assets/sequenceDiagram-704730f1-Dgji2XLQ.js +0 -122
- package/client/dist/assets/si-LK-N5RQ5JYF-OejsLzQ_.js +0 -1
- package/client/dist/assets/sk-SK-C5VTKIMK-_vy2Bt-M.js +0 -6
- package/client/dist/assets/sl-SI-NN7IZMDC-DKOl_u2M.js +0 -6
- package/client/dist/assets/stateDiagram-587899a1-CJ8eBaiU.js +0 -1
- package/client/dist/assets/stateDiagram-v2-d93cdb3a-C5K3l-Nt.js +0 -1
- package/client/dist/assets/styles-6aaf32cf-DAKE0jbx.js +0 -207
- package/client/dist/assets/styles-9a916d00-LFAJCgEy.js +0 -160
- package/client/dist/assets/styles-c10674c1-CllKO8NG.js +0 -116
- package/client/dist/assets/subset-shared.chunk-Uy-J87FQ.js +0 -84
- package/client/dist/assets/subset-worker.chunk-dvgDvqt9.js +0 -1
- package/client/dist/assets/sv-SE-XGPEYMSR-CDCB2ZV5.js +0 -10
- package/client/dist/assets/svgDrawCommon-08f97a94-CObOzbFQ.js +0 -1
- package/client/dist/assets/ta-IN-2NMHFXQM-DHUNdO69.js +0 -9
- package/client/dist/assets/th-TH-HPSO5L25-zI2hnBq3.js +0 -2
- package/client/dist/assets/timeline-definition-85554ec2-C2XHRmxK.js +0 -61
- package/client/dist/assets/tr-TR-DEFEU3FU-l-6Hu4-D.js +0 -7
- package/client/dist/assets/uk-UA-QMV73CPH-CqSOwrl7.js +0 -6
- package/client/dist/assets/vendor-codemirror-D_s0aGBu.js +0 -35
- package/client/dist/assets/vendor-i18n-DCFGyhQR.js +0 -1
- package/client/dist/assets/vendor-icons-Lb69KSFJ.js +0 -646
- package/client/dist/assets/vendor-markdown-BXEi_H3G.js +0 -298
- package/client/dist/assets/vendor-react-9mUTKBHH.js +0 -67
- package/client/dist/assets/vendor-syntax-DnmwQQJF.js +0 -16
- package/client/dist/assets/vendor-xterm-CZq1hqo1.js +0 -66
- package/client/dist/assets/vendor-xterm-qxJ8_QYu.css +0 -32
- package/client/dist/assets/vi-VN-M7AON7JQ-CUL8-mBZ.js +0 -5
- package/client/dist/assets/xychartDiagram-e933f94c-1fmf6slj.js +0 -7
- package/client/dist/assets/zh-CN-LNUGB5OW-CB5y5VVU.js +0 -10
- package/client/dist/assets/zh-HK-E62DVLB3-BHcrrEeJ.js +0 -1
- package/client/dist/assets/zh-TW-RAJ6MFWO-DoDUdkaJ.js +0 -9
- package/client/dist/clear-cache.html +0 -85
- package/client/dist/convert-icons.md +0 -53
- package/client/dist/favicon.png +0 -0
- package/client/dist/favicon.svg +0 -9
- package/client/dist/generate-icons.js +0 -49
- package/client/dist/icons/claude-ai-icon.svg +0 -1
- package/client/dist/icons/codex-white.svg +0 -3
- package/client/dist/icons/codex.svg +0 -3
- package/client/dist/icons/cursor-white.svg +0 -12
- package/client/dist/icons/cursor.svg +0 -1
- package/client/dist/icons/icon-128x128.png +0 -0
- package/client/dist/icons/icon-128x128.svg +0 -12
- package/client/dist/icons/icon-144x144.png +0 -0
- package/client/dist/icons/icon-144x144.svg +0 -12
- package/client/dist/icons/icon-152x152.png +0 -0
- package/client/dist/icons/icon-152x152.svg +0 -12
- package/client/dist/icons/icon-192x192.png +0 -0
- package/client/dist/icons/icon-192x192.svg +0 -12
- package/client/dist/icons/icon-384x384.png +0 -0
- package/client/dist/icons/icon-384x384.svg +0 -12
- package/client/dist/icons/icon-512x512.png +0 -0
- package/client/dist/icons/icon-512x512.svg +0 -12
- package/client/dist/icons/icon-72x72.png +0 -0
- package/client/dist/icons/icon-72x72.svg +0 -12
- package/client/dist/icons/icon-96x96.png +0 -0
- package/client/dist/icons/icon-96x96.svg +0 -12
- package/client/dist/icons/icon-template.svg +0 -12
- package/client/dist/index.html +0 -128
- package/client/dist/logo-128.png +0 -0
- package/client/dist/logo-256.png +0 -0
- package/client/dist/logo-32.png +0 -0
- package/client/dist/logo-512.png +0 -0
- package/client/dist/logo-64.png +0 -0
- package/client/dist/logo.svg +0 -17
- package/client/dist/manifest.json +0 -61
- package/client/dist/mcp-docs.html +0 -119
- package/client/dist/screenshots/cli-selection.png +0 -0
- package/client/dist/screenshots/desktop-main.png +0 -0
- package/client/dist/screenshots/mobile-chat.png +0 -0
- package/client/dist/screenshots/tools-modal.png +0 -0
- package/client/dist/sw.js +0 -19
- package/commands/upfynai-connect.md +0 -59
- package/commands/upfynai-disconnect.md +0 -31
- package/commands/upfynai-doctor.md +0 -99
- package/commands/upfynai-export.md +0 -49
- package/commands/upfynai-local.md +0 -82
- package/commands/upfynai-status.md +0 -75
- package/commands/upfynai-stop.md +0 -49
- package/commands/upfynai-uninstall.md +0 -58
- package/commands/upfynai.md +0 -69
- package/scripts/build-client.js +0 -17
- package/scripts/fix-node-pty.js +0 -67
- package/scripts/install-commands.js +0 -78
- package/server/claude-sdk.js +0 -714
- package/server/cli-ui.js +0 -785
- package/server/cli.js +0 -596
- package/server/constants/config.js +0 -31
- package/server/cursor-cli.js +0 -270
- package/server/database/auth.db +0 -0
- package/server/database/db.js +0 -822
- package/server/database/init.sql +0 -70
- package/server/index.js +0 -2738
- package/server/load-env.js +0 -26
- package/server/mcp-server.js +0 -621
- package/server/middleware/auth.js +0 -181
- package/server/openai-codex.js +0 -403
- package/server/openrouter.js +0 -137
- package/server/projects.js +0 -1742
- package/server/relay-client.js +0 -672
- package/server/routes/agent.js +0 -1226
- package/server/routes/auth.js +0 -266
- package/server/routes/cli-auth.js +0 -263
- package/server/routes/codex.js +0 -344
- package/server/routes/commands.js +0 -598
- package/server/routes/cursor.js +0 -807
- package/server/routes/dashboard.js +0 -205
- package/server/routes/git.js +0 -1151
- package/server/routes/mcp-utils.js +0 -48
- package/server/routes/mcp.js +0 -535
- package/server/routes/payments.js +0 -172
- package/server/routes/projects.js +0 -552
- package/server/routes/settings.js +0 -261
- package/server/routes/taskmaster.js +0 -1928
- package/server/routes/user.js +0 -106
- package/server/routes/vapi-chat.js +0 -94
- package/server/routes/voice.js +0 -194
- package/server/routes/webhooks.js +0 -166
- package/server/routes/workflows.js +0 -118
- package/server/sandbox.js +0 -120
- package/server/services/whisperService.js +0 -84
- package/server/services/workflowScheduler.js +0 -186
- package/server/utils/commandParser.js +0 -303
- package/server/utils/gitConfig.js +0 -24
- package/server/utils/mcp-detector.js +0 -198
- package/server/utils/taskmaster-websocket.js +0 -129
- package/shared/modelConstants.js +0 -96
package/server/claude-sdk.js
DELETED
|
@@ -1,714 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Claude SDK Integration
|
|
3
|
-
*
|
|
4
|
-
* This module provides SDK-based integration with Claude using the @anthropic-ai/claude-agent-sdk.
|
|
5
|
-
* It mirrors the interface of claude-cli.js but uses the SDK internally for better performance
|
|
6
|
-
* and maintainability.
|
|
7
|
-
*
|
|
8
|
-
* Key features:
|
|
9
|
-
* - Direct SDK integration without child processes
|
|
10
|
-
* - Session management with abort capability
|
|
11
|
-
* - Options mapping between CLI and SDK formats
|
|
12
|
-
* - WebSocket message streaming
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
16
|
-
import crypto from 'crypto';
|
|
17
|
-
import { promises as fs } from 'fs';
|
|
18
|
-
import path from 'path';
|
|
19
|
-
import os from 'os';
|
|
20
|
-
import { CLAUDE_MODELS } from '../shared/modelConstants.js';
|
|
21
|
-
|
|
22
|
-
const activeSessions = new Map();
|
|
23
|
-
const pendingToolApprovals = new Map();
|
|
24
|
-
|
|
25
|
-
const TOOL_APPROVAL_TIMEOUT_MS = parseInt(process.env.CLAUDE_TOOL_APPROVAL_TIMEOUT_MS, 10) || 55000;
|
|
26
|
-
|
|
27
|
-
const TOOLS_REQUIRING_INTERACTION = new Set(['AskUserQuestion']);
|
|
28
|
-
|
|
29
|
-
function createRequestId() {
|
|
30
|
-
if (typeof crypto.randomUUID === 'function') {
|
|
31
|
-
return crypto.randomUUID();
|
|
32
|
-
}
|
|
33
|
-
return crypto.randomBytes(16).toString('hex');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function waitForToolApproval(requestId, options = {}) {
|
|
37
|
-
const { timeoutMs = TOOL_APPROVAL_TIMEOUT_MS, signal, onCancel } = options;
|
|
38
|
-
|
|
39
|
-
return new Promise(resolve => {
|
|
40
|
-
let settled = false;
|
|
41
|
-
|
|
42
|
-
const finalize = (decision) => {
|
|
43
|
-
if (settled) return;
|
|
44
|
-
settled = true;
|
|
45
|
-
cleanup();
|
|
46
|
-
resolve(decision);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
let timeout;
|
|
50
|
-
|
|
51
|
-
const cleanup = () => {
|
|
52
|
-
pendingToolApprovals.delete(requestId);
|
|
53
|
-
if (timeout) clearTimeout(timeout);
|
|
54
|
-
if (signal && abortHandler) {
|
|
55
|
-
signal.removeEventListener('abort', abortHandler);
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
// timeoutMs 0 = wait indefinitely (interactive tools)
|
|
60
|
-
if (timeoutMs > 0) {
|
|
61
|
-
timeout = setTimeout(() => {
|
|
62
|
-
onCancel?.('timeout');
|
|
63
|
-
finalize(null);
|
|
64
|
-
}, timeoutMs);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const abortHandler = () => {
|
|
68
|
-
onCancel?.('cancelled');
|
|
69
|
-
finalize({ cancelled: true });
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
if (signal) {
|
|
73
|
-
if (signal.aborted) {
|
|
74
|
-
onCancel?.('cancelled');
|
|
75
|
-
finalize({ cancelled: true });
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
signal.addEventListener('abort', abortHandler, { once: true });
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
pendingToolApprovals.set(requestId, (decision) => {
|
|
82
|
-
finalize(decision);
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function resolveToolApproval(requestId, decision) {
|
|
88
|
-
const resolver = pendingToolApprovals.get(requestId);
|
|
89
|
-
if (resolver) {
|
|
90
|
-
resolver(decision);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Match stored permission entries against a tool + input combo.
|
|
95
|
-
// This only supports exact tool names and the Bash(command:*) shorthand
|
|
96
|
-
// used by the UI; it intentionally does not implement full glob semantics,
|
|
97
|
-
// introduced to stay consistent with the UI's "Allow rule" format.
|
|
98
|
-
function matchesToolPermission(entry, toolName, input) {
|
|
99
|
-
if (!entry || !toolName) {
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (entry === toolName) {
|
|
104
|
-
return true;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const bashMatch = entry.match(/^Bash\((.+):\*\)$/);
|
|
108
|
-
if (toolName === 'Bash' && bashMatch) {
|
|
109
|
-
const allowedPrefix = bashMatch[1];
|
|
110
|
-
let command = '';
|
|
111
|
-
|
|
112
|
-
if (typeof input === 'string') {
|
|
113
|
-
command = input.trim();
|
|
114
|
-
} else if (input && typeof input === 'object' && typeof input.command === 'string') {
|
|
115
|
-
command = input.command.trim();
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (!command) {
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return command.startsWith(allowedPrefix);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Maps CLI options to SDK-compatible options format
|
|
130
|
-
* @param {Object} options - CLI options
|
|
131
|
-
* @returns {Object} SDK-compatible options
|
|
132
|
-
*/
|
|
133
|
-
function mapCliOptionsToSDK(options = {}) {
|
|
134
|
-
const { sessionId, cwd, toolsSettings, permissionMode, images } = options;
|
|
135
|
-
|
|
136
|
-
const sdkOptions = {};
|
|
137
|
-
|
|
138
|
-
// Map working directory
|
|
139
|
-
if (cwd) {
|
|
140
|
-
sdkOptions.cwd = cwd;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Map permission mode
|
|
144
|
-
if (permissionMode && permissionMode !== 'default') {
|
|
145
|
-
sdkOptions.permissionMode = permissionMode;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Map tool settings
|
|
149
|
-
const settings = toolsSettings || {
|
|
150
|
-
allowedTools: [],
|
|
151
|
-
disallowedTools: [],
|
|
152
|
-
skipPermissions: false
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
// Handle tool permissions
|
|
156
|
-
if (settings.skipPermissions && permissionMode !== 'plan') {
|
|
157
|
-
// When skipping permissions, use bypassPermissions mode
|
|
158
|
-
sdkOptions.permissionMode = 'bypassPermissions';
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
let allowedTools = [...(settings.allowedTools || [])];
|
|
162
|
-
|
|
163
|
-
// Add plan mode default tools
|
|
164
|
-
if (permissionMode === 'plan') {
|
|
165
|
-
const planModeTools = ['Read', 'Task', 'exit_plan_mode', 'TodoRead', 'TodoWrite', 'WebFetch', 'WebSearch'];
|
|
166
|
-
for (const tool of planModeTools) {
|
|
167
|
-
if (!allowedTools.includes(tool)) {
|
|
168
|
-
allowedTools.push(tool);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
sdkOptions.allowedTools = allowedTools;
|
|
174
|
-
|
|
175
|
-
// Use the tools preset to make all default built-in tools available (including AskUserQuestion).
|
|
176
|
-
// This was introduced in SDK 0.1.57. Omitting this preserves existing behavior (all tools available),
|
|
177
|
-
// but being explicit ensures forward compatibility and clarity.
|
|
178
|
-
sdkOptions.tools = { type: 'preset', preset: 'claude_code' };
|
|
179
|
-
|
|
180
|
-
sdkOptions.disallowedTools = settings.disallowedTools || [];
|
|
181
|
-
|
|
182
|
-
// Map model (default to sonnet)
|
|
183
|
-
// Valid models: sonnet, opus, haiku, opusplan, sonnet[1m]
|
|
184
|
-
sdkOptions.model = options.model || CLAUDE_MODELS.DEFAULT;
|
|
185
|
-
// model selected
|
|
186
|
-
|
|
187
|
-
// Map system prompt configuration
|
|
188
|
-
sdkOptions.systemPrompt = {
|
|
189
|
-
type: 'preset',
|
|
190
|
-
preset: 'claude_code' // Required to use CLAUDE.md
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
// Map setting sources for CLAUDE.md loading
|
|
194
|
-
// This loads CLAUDE.md from project, user (~/.config/claude/CLAUDE.md), and local directories
|
|
195
|
-
sdkOptions.settingSources = ['project', 'user', 'local'];
|
|
196
|
-
|
|
197
|
-
// Map resume session
|
|
198
|
-
if (sessionId) {
|
|
199
|
-
sdkOptions.resume = sessionId;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return sdkOptions;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Adds a session to the active sessions map
|
|
207
|
-
* @param {string} sessionId - Session identifier
|
|
208
|
-
* @param {Object} queryInstance - SDK query instance
|
|
209
|
-
* @param {Array<string>} tempImagePaths - Temp image file paths for cleanup
|
|
210
|
-
* @param {string} tempDir - Temp directory for cleanup
|
|
211
|
-
*/
|
|
212
|
-
function addSession(sessionId, queryInstance, tempImagePaths = [], tempDir = null) {
|
|
213
|
-
activeSessions.set(sessionId, {
|
|
214
|
-
instance: queryInstance,
|
|
215
|
-
startTime: Date.now(),
|
|
216
|
-
status: 'active',
|
|
217
|
-
tempImagePaths,
|
|
218
|
-
tempDir
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Removes a session from the active sessions map
|
|
224
|
-
* @param {string} sessionId - Session identifier
|
|
225
|
-
*/
|
|
226
|
-
function removeSession(sessionId) {
|
|
227
|
-
activeSessions.delete(sessionId);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Gets a session from the active sessions map
|
|
232
|
-
* @param {string} sessionId - Session identifier
|
|
233
|
-
* @returns {Object|undefined} Session data or undefined
|
|
234
|
-
*/
|
|
235
|
-
function getSession(sessionId) {
|
|
236
|
-
return activeSessions.get(sessionId);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Gets all active session IDs
|
|
241
|
-
* @returns {Array<string>} Array of active session IDs
|
|
242
|
-
*/
|
|
243
|
-
function getAllSessions() {
|
|
244
|
-
return Array.from(activeSessions.keys());
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Transforms SDK messages to WebSocket format expected by frontend
|
|
249
|
-
* @param {Object} sdkMessage - SDK message object
|
|
250
|
-
* @returns {Object} Transformed message ready for WebSocket
|
|
251
|
-
*/
|
|
252
|
-
function transformMessage(sdkMessage) {
|
|
253
|
-
// Extract parent_tool_use_id for subagent tool grouping
|
|
254
|
-
if (sdkMessage.parent_tool_use_id) {
|
|
255
|
-
return {
|
|
256
|
-
...sdkMessage,
|
|
257
|
-
parentToolUseId: sdkMessage.parent_tool_use_id
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
return sdkMessage;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Extracts token usage from SDK result messages
|
|
265
|
-
* @param {Object} resultMessage - SDK result message
|
|
266
|
-
* @returns {Object|null} Token budget object or null
|
|
267
|
-
*/
|
|
268
|
-
function extractTokenBudget(resultMessage) {
|
|
269
|
-
if (resultMessage.type !== 'result' || !resultMessage.modelUsage) {
|
|
270
|
-
return null;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Get the first model's usage data
|
|
274
|
-
const modelKey = Object.keys(resultMessage.modelUsage)[0];
|
|
275
|
-
const modelData = resultMessage.modelUsage[modelKey];
|
|
276
|
-
|
|
277
|
-
if (!modelData) {
|
|
278
|
-
return null;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Use cumulative tokens if available (tracks total for the session)
|
|
282
|
-
// Otherwise fall back to per-request tokens
|
|
283
|
-
const inputTokens = modelData.cumulativeInputTokens || modelData.inputTokens || 0;
|
|
284
|
-
const outputTokens = modelData.cumulativeOutputTokens || modelData.outputTokens || 0;
|
|
285
|
-
const cacheReadTokens = modelData.cumulativeCacheReadInputTokens || modelData.cacheReadInputTokens || 0;
|
|
286
|
-
const cacheCreationTokens = modelData.cumulativeCacheCreationInputTokens || modelData.cacheCreationInputTokens || 0;
|
|
287
|
-
|
|
288
|
-
// Total used = input + output + cache tokens
|
|
289
|
-
const totalUsed = inputTokens + outputTokens + cacheReadTokens + cacheCreationTokens;
|
|
290
|
-
|
|
291
|
-
// Use configured context window budget from environment (default 160000)
|
|
292
|
-
// This is the user's budget limit, not the model's context window
|
|
293
|
-
const contextWindow = parseInt(process.env.CONTEXT_WINDOW) || 160000;
|
|
294
|
-
|
|
295
|
-
// token calculation done
|
|
296
|
-
|
|
297
|
-
return {
|
|
298
|
-
used: totalUsed,
|
|
299
|
-
total: contextWindow
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Handles image processing for SDK queries
|
|
305
|
-
* Saves base64 images to temporary files and returns modified prompt with file paths
|
|
306
|
-
* @param {string} command - Original user prompt
|
|
307
|
-
* @param {Array} images - Array of image objects with base64 data
|
|
308
|
-
* @param {string} cwd - Working directory for temp file creation
|
|
309
|
-
* @returns {Promise<Object>} {modifiedCommand, tempImagePaths, tempDir}
|
|
310
|
-
*/
|
|
311
|
-
async function handleImages(command, images, cwd) {
|
|
312
|
-
const tempImagePaths = [];
|
|
313
|
-
let tempDir = null;
|
|
314
|
-
|
|
315
|
-
if (!images || images.length === 0) {
|
|
316
|
-
return { modifiedCommand: command, tempImagePaths, tempDir };
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
try {
|
|
320
|
-
// Create temp directory in the project directory
|
|
321
|
-
const workingDir = cwd || process.cwd();
|
|
322
|
-
tempDir = path.join(workingDir, '.tmp', 'images', Date.now().toString());
|
|
323
|
-
await fs.mkdir(tempDir, { recursive: true });
|
|
324
|
-
|
|
325
|
-
// Save each image to a temp file
|
|
326
|
-
for (const [index, image] of images.entries()) {
|
|
327
|
-
// Extract base64 data and mime type
|
|
328
|
-
const matches = image.data.match(/^data:([^;]+);base64,(.+)$/);
|
|
329
|
-
if (!matches) {
|
|
330
|
-
// invalid image format
|
|
331
|
-
continue;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
const [, mimeType, base64Data] = matches;
|
|
335
|
-
const extension = mimeType.split('/')[1] || 'png';
|
|
336
|
-
const filename = `image_${index}.${extension}`;
|
|
337
|
-
const filepath = path.join(tempDir, filename);
|
|
338
|
-
|
|
339
|
-
// Write base64 data to file
|
|
340
|
-
await fs.writeFile(filepath, Buffer.from(base64Data, 'base64'));
|
|
341
|
-
tempImagePaths.push(filepath);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// Include the full image paths in the prompt
|
|
345
|
-
let modifiedCommand = command;
|
|
346
|
-
if (tempImagePaths.length > 0 && command && command.trim()) {
|
|
347
|
-
const imageNote = `\n\n[Images provided at the following paths:]\n${tempImagePaths.map((p, i) => `${i + 1}. ${p}`).join('\n')}`;
|
|
348
|
-
modifiedCommand = command + imageNote;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// images processed
|
|
352
|
-
return { modifiedCommand, tempImagePaths, tempDir };
|
|
353
|
-
} catch (error) {
|
|
354
|
-
// image processing error
|
|
355
|
-
return { modifiedCommand: command, tempImagePaths, tempDir };
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* Cleans up temporary image files
|
|
361
|
-
* @param {Array<string>} tempImagePaths - Array of temp file paths to delete
|
|
362
|
-
* @param {string} tempDir - Temp directory to remove
|
|
363
|
-
*/
|
|
364
|
-
async function cleanupTempFiles(tempImagePaths, tempDir) {
|
|
365
|
-
if (!tempImagePaths || tempImagePaths.length === 0) {
|
|
366
|
-
return;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
try {
|
|
370
|
-
// Delete individual temp files
|
|
371
|
-
for (const imagePath of tempImagePaths) {
|
|
372
|
-
await fs.unlink(imagePath).catch(() => {});
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Delete temp directory
|
|
376
|
-
if (tempDir) {
|
|
377
|
-
await fs.rm(tempDir, { recursive: true, force: true }).catch(() => {});
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// temp files cleaned
|
|
381
|
-
} catch (error) {
|
|
382
|
-
// cleanup error
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* Loads MCP server configurations from ~/.claude.json
|
|
388
|
-
* @param {string} cwd - Current working directory for project-specific configs
|
|
389
|
-
* @returns {Object|null} MCP servers object or null if none found
|
|
390
|
-
*/
|
|
391
|
-
async function loadMcpConfig(cwd) {
|
|
392
|
-
try {
|
|
393
|
-
const claudeConfigPath = path.join(os.homedir(), '.claude.json');
|
|
394
|
-
|
|
395
|
-
// Check if config file exists
|
|
396
|
-
try {
|
|
397
|
-
await fs.access(claudeConfigPath);
|
|
398
|
-
} catch (error) {
|
|
399
|
-
// File doesn't exist, return null
|
|
400
|
-
// no MCP config found
|
|
401
|
-
return null;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// Read and parse config file
|
|
405
|
-
let claudeConfig;
|
|
406
|
-
try {
|
|
407
|
-
const configContent = await fs.readFile(claudeConfigPath, 'utf8');
|
|
408
|
-
claudeConfig = JSON.parse(configContent);
|
|
409
|
-
} catch (error) {
|
|
410
|
-
// MCP config parse error
|
|
411
|
-
return null;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// Extract MCP servers (merge global and project-specific)
|
|
415
|
-
let mcpServers = {};
|
|
416
|
-
|
|
417
|
-
// Add global MCP servers
|
|
418
|
-
if (claudeConfig.mcpServers && typeof claudeConfig.mcpServers === 'object') {
|
|
419
|
-
mcpServers = { ...claudeConfig.mcpServers };
|
|
420
|
-
// global MCP servers loaded
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// Add/override with project-specific MCP servers
|
|
424
|
-
if (claudeConfig.claudeProjects && cwd) {
|
|
425
|
-
const projectConfig = claudeConfig.claudeProjects[cwd];
|
|
426
|
-
if (projectConfig && projectConfig.mcpServers && typeof projectConfig.mcpServers === 'object') {
|
|
427
|
-
mcpServers = { ...mcpServers, ...projectConfig.mcpServers };
|
|
428
|
-
// project MCP servers loaded
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
// Return null if no servers found
|
|
433
|
-
if (Object.keys(mcpServers).length === 0) {
|
|
434
|
-
// no MCP servers configured
|
|
435
|
-
return null;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
// MCP config loaded
|
|
439
|
-
return mcpServers;
|
|
440
|
-
} catch (error) {
|
|
441
|
-
// MCP config load error
|
|
442
|
-
return null;
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
/**
|
|
447
|
-
* Executes a Claude query using the SDK
|
|
448
|
-
* @param {string} command - User prompt/command
|
|
449
|
-
* @param {Object} options - Query options
|
|
450
|
-
* @param {Object} ws - WebSocket connection
|
|
451
|
-
* @returns {Promise<void>}
|
|
452
|
-
*/
|
|
453
|
-
async function queryClaudeSDK(command, options = {}, ws) {
|
|
454
|
-
const { sessionId } = options;
|
|
455
|
-
let capturedSessionId = sessionId;
|
|
456
|
-
let sessionCreatedSent = false;
|
|
457
|
-
let tempImagePaths = [];
|
|
458
|
-
let tempDir = null;
|
|
459
|
-
|
|
460
|
-
try {
|
|
461
|
-
// Map CLI options to SDK format
|
|
462
|
-
const sdkOptions = mapCliOptionsToSDK(options);
|
|
463
|
-
|
|
464
|
-
// Load MCP configuration
|
|
465
|
-
const mcpServers = await loadMcpConfig(options.cwd);
|
|
466
|
-
if (mcpServers) {
|
|
467
|
-
sdkOptions.mcpServers = mcpServers;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// Handle images - save to temp files and modify prompt
|
|
471
|
-
const imageResult = await handleImages(command, options.images, options.cwd);
|
|
472
|
-
const finalCommand = imageResult.modifiedCommand;
|
|
473
|
-
tempImagePaths = imageResult.tempImagePaths;
|
|
474
|
-
tempDir = imageResult.tempDir;
|
|
475
|
-
|
|
476
|
-
sdkOptions.canUseTool = async (toolName, input, context) => {
|
|
477
|
-
const requiresInteraction = TOOLS_REQUIRING_INTERACTION.has(toolName);
|
|
478
|
-
|
|
479
|
-
if (!requiresInteraction) {
|
|
480
|
-
if (sdkOptions.permissionMode === 'bypassPermissions') {
|
|
481
|
-
return { behavior: 'allow', updatedInput: input };
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
const isDisallowed = (sdkOptions.disallowedTools || []).some(entry =>
|
|
485
|
-
matchesToolPermission(entry, toolName, input)
|
|
486
|
-
);
|
|
487
|
-
if (isDisallowed) {
|
|
488
|
-
return { behavior: 'deny', message: 'Tool disallowed by settings' };
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
const isAllowed = (sdkOptions.allowedTools || []).some(entry =>
|
|
492
|
-
matchesToolPermission(entry, toolName, input)
|
|
493
|
-
);
|
|
494
|
-
if (isAllowed) {
|
|
495
|
-
return { behavior: 'allow', updatedInput: input };
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
const requestId = createRequestId();
|
|
500
|
-
ws.send({
|
|
501
|
-
type: 'claude-permission-request',
|
|
502
|
-
requestId,
|
|
503
|
-
toolName,
|
|
504
|
-
input,
|
|
505
|
-
sessionId: capturedSessionId || sessionId || null
|
|
506
|
-
});
|
|
507
|
-
|
|
508
|
-
const decision = await waitForToolApproval(requestId, {
|
|
509
|
-
timeoutMs: requiresInteraction ? 0 : undefined,
|
|
510
|
-
signal: context?.signal,
|
|
511
|
-
onCancel: (reason) => {
|
|
512
|
-
ws.send({
|
|
513
|
-
type: 'claude-permission-cancelled',
|
|
514
|
-
requestId,
|
|
515
|
-
reason,
|
|
516
|
-
sessionId: capturedSessionId || sessionId || null
|
|
517
|
-
});
|
|
518
|
-
}
|
|
519
|
-
});
|
|
520
|
-
if (!decision) {
|
|
521
|
-
return { behavior: 'deny', message: 'Permission request timed out' };
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
if (decision.cancelled) {
|
|
525
|
-
return { behavior: 'deny', message: 'Permission request cancelled' };
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
if (decision.allow) {
|
|
529
|
-
if (decision.rememberEntry && typeof decision.rememberEntry === 'string') {
|
|
530
|
-
if (!sdkOptions.allowedTools.includes(decision.rememberEntry)) {
|
|
531
|
-
sdkOptions.allowedTools.push(decision.rememberEntry);
|
|
532
|
-
}
|
|
533
|
-
if (Array.isArray(sdkOptions.disallowedTools)) {
|
|
534
|
-
sdkOptions.disallowedTools = sdkOptions.disallowedTools.filter(entry => entry !== decision.rememberEntry);
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
return { behavior: 'allow', updatedInput: decision.updatedInput ?? input };
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
return { behavior: 'deny', message: decision.message ?? 'User denied tool use' };
|
|
541
|
-
};
|
|
542
|
-
|
|
543
|
-
// Set stream-close timeout for interactive tools (Query constructor reads it synchronously). Claude Agent SDK has a default of 5s and this overrides it
|
|
544
|
-
const prevStreamTimeout = process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT;
|
|
545
|
-
process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT = '300000';
|
|
546
|
-
|
|
547
|
-
const queryInstance = query({
|
|
548
|
-
prompt: finalCommand,
|
|
549
|
-
options: sdkOptions
|
|
550
|
-
});
|
|
551
|
-
|
|
552
|
-
// Restore immediately — Query constructor already captured the value
|
|
553
|
-
if (prevStreamTimeout !== undefined) {
|
|
554
|
-
process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT = prevStreamTimeout;
|
|
555
|
-
} else {
|
|
556
|
-
delete process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
// Track the query instance for abort capability
|
|
560
|
-
if (capturedSessionId) {
|
|
561
|
-
addSession(capturedSessionId, queryInstance, tempImagePaths, tempDir);
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
// Process streaming messages
|
|
565
|
-
// streaming session started
|
|
566
|
-
for await (const message of queryInstance) {
|
|
567
|
-
// Capture session ID from first message
|
|
568
|
-
if (message.session_id && !capturedSessionId) {
|
|
569
|
-
|
|
570
|
-
capturedSessionId = message.session_id;
|
|
571
|
-
addSession(capturedSessionId, queryInstance, tempImagePaths, tempDir);
|
|
572
|
-
|
|
573
|
-
// Set session ID on writer
|
|
574
|
-
if (ws.setSessionId && typeof ws.setSessionId === 'function') {
|
|
575
|
-
ws.setSessionId(capturedSessionId);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
// Send session-created event only once for new sessions
|
|
579
|
-
if (!sessionId && !sessionCreatedSent) {
|
|
580
|
-
sessionCreatedSent = true;
|
|
581
|
-
ws.send({
|
|
582
|
-
type: 'session-created',
|
|
583
|
-
sessionId: capturedSessionId
|
|
584
|
-
});
|
|
585
|
-
} else {
|
|
586
|
-
// session-created already sent
|
|
587
|
-
}
|
|
588
|
-
} else {
|
|
589
|
-
// session_id already captured or missing
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
// Transform and send message to WebSocket
|
|
593
|
-
const transformedMessage = transformMessage(message);
|
|
594
|
-
ws.send({
|
|
595
|
-
type: 'claude-response',
|
|
596
|
-
data: transformedMessage,
|
|
597
|
-
sessionId: capturedSessionId || sessionId || null
|
|
598
|
-
});
|
|
599
|
-
|
|
600
|
-
// Extract and send token budget updates from result messages
|
|
601
|
-
if (message.type === 'result') {
|
|
602
|
-
const tokenBudget = extractTokenBudget(message);
|
|
603
|
-
if (tokenBudget) {
|
|
604
|
-
// token budget calculated
|
|
605
|
-
ws.send({
|
|
606
|
-
type: 'token-budget',
|
|
607
|
-
data: tokenBudget,
|
|
608
|
-
sessionId: capturedSessionId || sessionId || null
|
|
609
|
-
});
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
// Clean up session on completion
|
|
615
|
-
if (capturedSessionId) {
|
|
616
|
-
removeSession(capturedSessionId);
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
// Clean up temporary image files
|
|
620
|
-
await cleanupTempFiles(tempImagePaths, tempDir);
|
|
621
|
-
|
|
622
|
-
// Send completion event
|
|
623
|
-
// streaming complete
|
|
624
|
-
ws.send({
|
|
625
|
-
type: 'claude-complete',
|
|
626
|
-
sessionId: capturedSessionId,
|
|
627
|
-
exitCode: 0,
|
|
628
|
-
isNewSession: !sessionId && !!command
|
|
629
|
-
});
|
|
630
|
-
// complete event sent
|
|
631
|
-
|
|
632
|
-
} catch (error) {
|
|
633
|
-
// SDK query error occurred
|
|
634
|
-
|
|
635
|
-
// Clean up session on error
|
|
636
|
-
if (capturedSessionId) {
|
|
637
|
-
removeSession(capturedSessionId);
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
// Clean up temporary image files on error
|
|
641
|
-
await cleanupTempFiles(tempImagePaths, tempDir);
|
|
642
|
-
|
|
643
|
-
// Send error to WebSocket
|
|
644
|
-
ws.send({
|
|
645
|
-
type: 'claude-error',
|
|
646
|
-
error: error.message,
|
|
647
|
-
sessionId: capturedSessionId || sessionId || null
|
|
648
|
-
});
|
|
649
|
-
|
|
650
|
-
throw error;
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
/**
|
|
655
|
-
* Aborts an active SDK session
|
|
656
|
-
* @param {string} sessionId - Session identifier
|
|
657
|
-
* @returns {boolean} True if session was aborted, false if not found
|
|
658
|
-
*/
|
|
659
|
-
async function abortClaudeSDKSession(sessionId) {
|
|
660
|
-
const session = getSession(sessionId);
|
|
661
|
-
|
|
662
|
-
if (!session) {
|
|
663
|
-
// session not found
|
|
664
|
-
return false;
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
try {
|
|
668
|
-
// aborting SDK session
|
|
669
|
-
|
|
670
|
-
// Call interrupt() on the query instance
|
|
671
|
-
await session.instance.interrupt();
|
|
672
|
-
|
|
673
|
-
// Update session status
|
|
674
|
-
session.status = 'aborted';
|
|
675
|
-
|
|
676
|
-
// Clean up temporary image files
|
|
677
|
-
await cleanupTempFiles(session.tempImagePaths, session.tempDir);
|
|
678
|
-
|
|
679
|
-
// Clean up session
|
|
680
|
-
removeSession(sessionId);
|
|
681
|
-
|
|
682
|
-
return true;
|
|
683
|
-
} catch (error) {
|
|
684
|
-
// abort error occurred
|
|
685
|
-
return false;
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
/**
|
|
690
|
-
* Checks if an SDK session is currently active
|
|
691
|
-
* @param {string} sessionId - Session identifier
|
|
692
|
-
* @returns {boolean} True if session is active
|
|
693
|
-
*/
|
|
694
|
-
function isClaudeSDKSessionActive(sessionId) {
|
|
695
|
-
const session = getSession(sessionId);
|
|
696
|
-
return session && session.status === 'active';
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
/**
|
|
700
|
-
* Gets all active SDK session IDs
|
|
701
|
-
* @returns {Array<string>} Array of active session IDs
|
|
702
|
-
*/
|
|
703
|
-
function getActiveClaudeSDKSessions() {
|
|
704
|
-
return getAllSessions();
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
// Export public API
|
|
708
|
-
export {
|
|
709
|
-
queryClaudeSDK,
|
|
710
|
-
abortClaudeSDKSession,
|
|
711
|
-
isClaudeSDKSessionActive,
|
|
712
|
-
getActiveClaudeSDKSessions,
|
|
713
|
-
resolveToolApproval
|
|
714
|
-
};
|