@captain-app/openclaw 2026.2.4
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/CHANGELOG.md +1528 -0
- package/LICENSE +21 -0
- package/README-header.png +0 -0
- package/README.md +539 -0
- package/assets/avatar-placeholder.svg +19 -0
- package/assets/chrome-extension/README.md +22 -0
- package/assets/chrome-extension/background.js +438 -0
- package/assets/chrome-extension/icons/icon128.png +0 -0
- package/assets/chrome-extension/icons/icon16.png +0 -0
- package/assets/chrome-extension/icons/icon32.png +0 -0
- package/assets/chrome-extension/icons/icon48.png +0 -0
- package/assets/chrome-extension/manifest.json +25 -0
- package/assets/chrome-extension/options.html +196 -0
- package/assets/chrome-extension/options.js +59 -0
- package/assets/dmg-background-small.png +0 -0
- package/assets/dmg-background.png +0 -0
- package/dist/accounts-ClnuDahN.js +250 -0
- package/dist/accounts-DzBgAM3C.js +251 -0
- package/dist/acp-cli-BEDDkzXH.js +926 -0
- package/dist/acp-cli-CwV4mdnW.js +923 -0
- package/dist/agent-CArjbSeE.js +695 -0
- package/dist/agent-CB7x5HLT.js +695 -0
- package/dist/agent-scope-BbT4OG2N.js +545 -0
- package/dist/agent-scope-C_O6Vpl0.js +545 -0
- package/dist/agent-scope-Csu2B6AM.js +606 -0
- package/dist/archive-CWrnG1CH.js +85 -0
- package/dist/archive-ccN9aDgq.js +85 -0
- package/dist/audit-DXPkQ275.js +1686 -0
- package/dist/audit-eH7nwgsM.js +1686 -0
- package/dist/auth-health-Bj7Gjbv0.js +149 -0
- package/dist/auth-health-CjjJhHey.js +149 -0
- package/dist/auth-nnRYiqpH.js +192 -0
- package/dist/auth-profiles-DVLfuixr.js +2954 -0
- package/dist/auth-y1BLPUhX.js +192 -0
- package/dist/boolean-Wzu0-e0P.js +30 -0
- package/dist/brew-Cqi8b49_.js +46 -0
- package/dist/brew-DyBGNK8A.js +46 -0
- package/dist/build-info.json +5 -0
- package/dist/call-B7EveN4V.js +252 -0
- package/dist/call-vuUQIjOj.js +252 -0
- package/dist/canvas-host/a2ui/.bundle.hash +1 -0
- package/dist/canvas-host/a2ui/a2ui.bundle.js +17780 -0
- package/dist/canvas-host/a2ui/index.html +307 -0
- package/dist/channel-options-CJfmOkol.js +32 -0
- package/dist/channel-options-lFguMTz1.js +62 -0
- package/dist/channel-selection-BmND9mWj.js +51 -0
- package/dist/channel-selection-cGhL9G0c.js +51 -0
- package/dist/channel-summary-B4G513Eb.js +1154 -0
- package/dist/channel-summary-MCwBCa5y.js +1154 -0
- package/dist/channels-cli-C2Of1mZG.js +1411 -0
- package/dist/channels-cli-DFynrP1H.js +1413 -0
- package/dist/channels-status-issues-YohTjZ-I.js +18 -0
- package/dist/channels-status-issues-ZcR1U-m5.js +18 -0
- package/dist/chrome-B3IuUad-.js +1953 -0
- package/dist/chrome-BZ9K48w9.js +1973 -0
- package/dist/clack-prompter-B9yLhyOm.js +92 -0
- package/dist/clack-prompter-BybM9xdL.js +92 -0
- package/dist/cli/daemon-cli.js +2 -0
- package/dist/cli-BJZdJwug.js +89 -0
- package/dist/cli-BQSoKu3d.js +86 -0
- package/dist/cli-utils-CO4jEMn0.js +43 -0
- package/dist/cli-utils-gtE-0a0D.js +43 -0
- package/dist/client-DPqNOpK3.js +1609 -0
- package/dist/client-DySXIFCA.js +1609 -0
- package/dist/command-format-CFzL448l.js +52 -0
- package/dist/command-format-DELazozB.js +52 -0
- package/dist/command-format-ayFsmwwz.js +38 -0
- package/dist/command-options-CsjxK4cZ.js +33 -0
- package/dist/commands-BRe9VTyU.js +229 -0
- package/dist/completion-cli-DlkjK0iC.js +390 -0
- package/dist/completion-cli-E6Pt41AL.js +387 -0
- package/dist/config-BtSTwPcH.js +4882 -0
- package/dist/config-DfMIMT-f.js +4881 -0
- package/dist/config-_d7_WcRv.js +5623 -0
- package/dist/config-guard-wSnm-U8a.js +5628 -0
- package/dist/configure-CPHAFKlg.js +895 -0
- package/dist/configure-DK1XgXYx.js +894 -0
- package/dist/constants-CNTiY-ZN.js +65 -0
- package/dist/constants-hpmbslG7.js +65 -0
- package/dist/control-service-CqX5g_ko.js +61 -0
- package/dist/control-service-o6xe3hEb.js +61 -0
- package/dist/control-ui/apple-touch-icon.png +0 -0
- package/dist/control-ui/assets/index-RwcW4Xl0.css +1 -0
- package/dist/control-ui/assets/index-ryaCcbyp.js +4584 -0
- package/dist/control-ui/assets/index-ryaCcbyp.js.map +1 -0
- package/dist/control-ui/favicon-32.png +0 -0
- package/dist/control-ui/favicon.ico +0 -0
- package/dist/control-ui/favicon.svg +22 -0
- package/dist/control-ui/index.html +17 -0
- package/dist/cron-cli-BoSaDgvH.js +453 -0
- package/dist/cron-cli-qVandvsD.js +456 -0
- package/dist/daemon-cli-C4gGWa15.js +760 -0
- package/dist/daemon-cli-DV_X0Krf.js +761 -0
- package/dist/daemon-runtime-D5hbSrdO.js +460 -0
- package/dist/daemon-runtime-DXUfrXBC.js +460 -0
- package/dist/deliver-BxK5nI2P.js +2587 -0
- package/dist/deliver-Dwzg9LUd.js +2557 -0
- package/dist/deliver-qpYZp20m.js +2587 -0
- package/dist/deps-CUXtMV9d.js +27 -0
- package/dist/deps-WBvZpFV_.js +27 -0
- package/dist/devices-cli-B2s18Qrh.js +207 -0
- package/dist/devices-cli-D1UEtMUJ.js +204 -0
- package/dist/directory-cli-BXQbrkfM.js +247 -0
- package/dist/directory-cli-CqA7tRbq.js +244 -0
- package/dist/dispatcher-Dyv7T-1r.js +160 -0
- package/dist/dns-cli-CWxKD22D.js +198 -0
- package/dist/dns-cli-CzKr_Fxj.js +201 -0
- package/dist/docs-cli-BNyxUbWr.js +159 -0
- package/dist/docs-cli-DYpTbo3i.js +161 -0
- package/dist/doctor-C8fDYZLq.js +2583 -0
- package/dist/doctor-hYcqp7c0.js +2585 -0
- package/dist/entry.js +1326 -0
- package/dist/env-l7QVNj6j.js +32 -0
- package/dist/errors-CMCg46fK.js +1952 -0
- package/dist/exec-B8JKbXKW.js +246 -0
- package/dist/exec-BMnoMcZW.js +1099 -0
- package/dist/exec-HEWTMJ7j.js +246 -0
- package/dist/exec-approvals-DtrnHx6M.js +1026 -0
- package/dist/exec-approvals-Y42bE8ud.js +1026 -0
- package/dist/exec-approvals-cli-CIedYxP3.js +385 -0
- package/dist/exec-approvals-cli-xpbxnj4O.js +388 -0
- package/dist/extensionAPI.js +66572 -0
- package/dist/format-Dzy9uRLE.js +34 -0
- package/dist/format-sj0fELBK.js +34 -0
- package/dist/gateway-cli-5A-KNLEC.js +16635 -0
- package/dist/gateway-cli-B8kS4chb.js +16637 -0
- package/dist/gateway-rpc-15n38Ize.js +28 -0
- package/dist/gateway-rpc-TMVRgGfj.js +28 -0
- package/dist/github-copilot-auth-DVZj4Zgh.js +1104 -0
- package/dist/github-copilot-auth-DeGYyLY9.js +1104 -0
- package/dist/github-copilot-token-B3SA95yo.js +103 -0
- package/dist/github-copilot-token-C8XFYz0i.js +103 -0
- package/dist/github-copilot-token-CnxakiSC.js +103 -0
- package/dist/gmail-setup-utils-CWPC386a.js +428 -0
- package/dist/gmail-setup-utils-eJVB5Ewp.js +428 -0
- package/dist/health-format-B0tMTk3C.js +1189 -0
- package/dist/health-format-DaURVaUn.js +1188 -0
- package/dist/help-format-CEsRHU2f.js +17 -0
- package/dist/help-format-GuCWws6r.js +17 -0
- package/dist/helpers-BEJ-phFf.js +25 -0
- package/dist/helpers-BtbBZVKZ.js +10 -0
- package/dist/helpers-C12w9zxf.js +10 -0
- package/dist/helpers-CzjGJZmJ.js +25 -0
- package/dist/hooks/bundled/boot-md/HOOK.md +19 -0
- package/dist/hooks/bundled/command-logger/HOOK.md +122 -0
- package/dist/hooks/bundled/session-memory/HOOK.md +109 -0
- package/dist/hooks/bundled/soul-evil/HOOK.md +71 -0
- package/dist/hooks-cli-BZCMAnW2.js +1058 -0
- package/dist/hooks-cli-D0CEFg3P.js +1061 -0
- package/dist/hooks-status-Bn7_O8PM.js +443 -0
- package/dist/hooks-status-BrWVfIn0.js +443 -0
- package/dist/image-BS022pvv.js +1421 -0
- package/dist/image-BzJtY34J.js +629 -0
- package/dist/image-CBqmIbQQ.js +629 -0
- package/dist/index.js +5809 -0
- package/dist/installs-BP4K5L33.js +388 -0
- package/dist/installs-DOpTt7VZ.js +388 -0
- package/dist/is-main-B4o72sqg.js +25 -0
- package/dist/is-main-PYGa3tDA.js +25 -0
- package/dist/links-B4nk2iDf.js +15 -0
- package/dist/links-DwjRqxgR.js +15 -0
- package/dist/loader-Cn4EV_pf.js +63690 -0
- package/dist/logging-CS3tbYDj.js +15 -0
- package/dist/logging-CY-Q5cwf.js +1 -0
- package/dist/logging-DhiLkhLw.js +15 -0
- package/dist/logging-pqyrk15z.js +1 -0
- package/dist/login-qr-CD164Aw1.js +478 -0
- package/dist/login-qr-D7Zdgji2.js +478 -0
- package/dist/login-qr-YgILJ4VC.js +475 -0
- package/dist/logs-cli-BdS0Uv0I.js +227 -0
- package/dist/logs-cli-CqSN1GzB.js +230 -0
- package/dist/manager-CfGY5zND.js +2870 -0
- package/dist/manager-CjuBqFRL.js +2870 -0
- package/dist/manager-CoBEAQKm.js +2872 -0
- package/dist/manifest-registry-Bwjq9Iev.js +668 -0
- package/dist/manifest-registry-D2Yntqcb.js +668 -0
- package/dist/message-channel-Cjsiqxok.js +105 -0
- package/dist/message-channel-D6v_oPAg.js +105 -0
- package/dist/model-selection-Cv5Ox_tY.js +2956 -0
- package/dist/model-selection-Dr-5U5-l.js +2708 -0
- package/dist/models-cli-B39ckynD.js +2541 -0
- package/dist/models-cli-DoiYsBYw.js +2544 -0
- package/dist/net-CFCxaipF.js +137 -0
- package/dist/net-DKJPqXuW.js +137 -0
- package/dist/node-cli-C_FYF-RA.js +1456 -0
- package/dist/node-cli-DWPoNsQS.js +1459 -0
- package/dist/node-service-DcJREOww.js +67 -0
- package/dist/node-service-DuZ9Us9h.js +67 -0
- package/dist/nodes-cli-Elo6tlen.js +1210 -0
- package/dist/nodes-cli-zqryRUWB.js +1207 -0
- package/dist/nodes-screen-C4aCrxie.js +157 -0
- package/dist/nodes-screen-D4PSynkR.js +157 -0
- package/dist/note-CQhSvgQv.js +73 -0
- package/dist/note-_C44YfAQ.js +73 -0
- package/dist/onboard-channels-CHBDi-ZA.js +670 -0
- package/dist/onboard-channels-DOEKyxaL.js +670 -0
- package/dist/onboard-skills-BUTXREDZ.js +3327 -0
- package/dist/onboard-skills-CSLYZmZA.js +3327 -0
- package/dist/onboarding-CgKb8b39.js +3232 -0
- package/dist/openclaw-root-9ILYSmJ9.js +84 -0
- package/dist/openclaw-root-Cvotktkd.js +84 -0
- package/dist/pairing-cli-B4UGR2at.js +114 -0
- package/dist/pairing-cli-BWDDl8cf.js +117 -0
- package/dist/pairing-labels-ClZ-fTWT.js +9 -0
- package/dist/pairing-labels-Ds7BPOkj.js +9 -0
- package/dist/pairing-store-DDLNuzmx.js +391 -0
- package/dist/pairing-store-DRn08lZD.js +391 -0
- package/dist/parse-87ybtYW1.js +23 -0
- package/dist/parse-OCFfznr3.js +23 -0
- package/dist/parse-log-line-C9aL5PUL.js +44 -0
- package/dist/parse-log-line-DxRaGzQb.js +44 -0
- package/dist/parse-timeout-CFqNj7No.js +16 -0
- package/dist/parse-timeout-DV8NQQWk.js +16 -0
- package/dist/path-env-C7kiJUgG.js +77 -0
- package/dist/path-env-DEj4CiFN.js +77 -0
- package/dist/paths-B-q1nXdY.js +43 -0
- package/dist/paths-B1kfl4h5.js +164 -0
- package/dist/paths-B4kigINg.js +40 -0
- package/dist/paths-CHGbP1-A.js +43 -0
- package/dist/paths-scjhy7N2.js +180 -0
- package/dist/pi-embedded-helpers-C19wUpMB.js +8451 -0
- package/dist/pi-embedded-helpers-CT5VuLCb.js +1293 -0
- package/dist/pi-embedded-helpers-Dl8e5Rf8.js +1293 -0
- package/dist/pi-model-discovery-B6CsmK6Y.js +20 -0
- package/dist/pi-model-discovery-DsRqYJLy.js +20 -0
- package/dist/pi-model-discovery-EhM2JAQo.js +20 -0
- package/dist/pi-tools.policy-BvkSDFDN.js +229 -0
- package/dist/plugin-auto-enable-Bd_StZzz.js +461 -0
- package/dist/plugin-auto-enable-DBhXb_0x.js +461 -0
- package/dist/plugin-sdk/agent-scope-DdwUKIOe.js +606 -0
- package/dist/plugin-sdk/chrome-G8apFa5p.js +1953 -0
- package/dist/plugin-sdk/command-format-qUVxzqYm.js +52 -0
- package/dist/plugin-sdk/config-Cm1M7tgH.js +5623 -0
- package/dist/plugin-sdk/deliver-Cl8uowiO.js +2557 -0
- package/dist/plugin-sdk/exec-Cm9b2r9Q.js +1107 -0
- package/dist/plugin-sdk/github-copilot-token-BHNcM4_B.js +103 -0
- package/dist/plugin-sdk/image-7PgoS2VD.js +1421 -0
- package/dist/plugin-sdk/index.d.ts +8908 -0
- package/dist/plugin-sdk/index.js +70888 -0
- package/dist/plugin-sdk/login-qr-qTALvWi2.js +475 -0
- package/dist/plugin-sdk/manager-Cs3EQZCb.js +2870 -0
- package/dist/plugin-sdk/model-selection-BgC1E1a7.js +2708 -0
- package/dist/plugin-sdk/paths-BYpoyRv5.js +164 -0
- package/dist/plugin-sdk/paths-DNQE-bvr.js +40 -0
- package/dist/plugin-sdk/pi-embedded-helpers-5jNqW_dE.js +8755 -0
- package/dist/plugin-sdk/pi-model-discovery-BUGEht9A.js +20 -0
- package/dist/plugin-sdk/pw-ai-COTtei4a.js +1649 -0
- package/dist/plugin-sdk/qmd-manager-ClSwiAJl.js +615 -0
- package/dist/plugin-sdk/redact-2AzjOfk2.js +94 -0
- package/dist/plugin-sdk/rolldown-runtime-Cbj13DAv.js +20 -0
- package/dist/plugin-sdk/sqlite-gCW7MlLs.js +215 -0
- package/dist/plugin-sdk/transcript-events-DGF257vD.js +17 -0
- package/dist/plugins-C3Bm-HQV.js +494 -0
- package/dist/plugins-QJjTXliB.js +495 -0
- package/dist/plugins-cli-DTci0JQb.js +443 -0
- package/dist/plugins-cli-wJsN1HHK.js +440 -0
- package/dist/ports-CiW9dmMq.js +96 -0
- package/dist/program-BWpTHh1I.js +188 -0
- package/dist/progress-Bcjniu7m.js +133 -0
- package/dist/progress-CvaSPjS9.js +133 -0
- package/dist/prompt-style-CFsleyxV.js +9 -0
- package/dist/prompt-style-DYJdrXyV.js +9 -0
- package/dist/prompts-Bt9fwsg2.js +10 -0
- package/dist/prompts-CudpZgTI.js +10 -0
- package/dist/pw-ai-08F3GD-3.js +1649 -0
- package/dist/pw-ai-ZmHxHQnx.js +1651 -0
- package/dist/pw-ai-tNPuRNn3.js +1649 -0
- package/dist/qmd-manager-2r-4n3sP.js +617 -0
- package/dist/qmd-manager-CF52nuBg.js +615 -0
- package/dist/qmd-manager-HEm5H2mk.js +616 -0
- package/dist/redact-BICFkpn7.js +97 -0
- package/dist/redact-BIMJ3ntQ.js +94 -0
- package/dist/redact-KzWHRS5J.js +97 -0
- package/dist/register.subclis-D2K25c84.js +348 -0
- package/dist/register.subclis-Dd8LbOLi.js +342 -0
- package/dist/reply-5UNWRwMn.js +63693 -0
- package/dist/restart-sentinel-Cr0vUxB8.js +65 -0
- package/dist/restart-sentinel-DUemCjgU.js +65 -0
- package/dist/rolldown-runtime-Cbj13DAv.js +20 -0
- package/dist/routes-C6UpTPas.js +2410 -0
- package/dist/routes-ClNyEvlm.js +2410 -0
- package/dist/rpc-D0mf7DIw.js +95 -0
- package/dist/rpc-DYdOrgd9.js +95 -0
- package/dist/run-main-CojI7gWx.js +194 -0
- package/dist/runtime-guard-68M_evhb.js +60 -0
- package/dist/runtime-guard-DkjmhnBD.js +60 -0
- package/dist/sandbox-Ca81z3Tw.js +2924 -0
- package/dist/sandbox-cli-D75GApgp.js +459 -0
- package/dist/sandbox-cli-E4SJsC1C.js +462 -0
- package/dist/sandbox-knontqD9.js +2925 -0
- package/dist/security-cli-BLihvXO-.js +503 -0
- package/dist/security-cli-IGQCsK4g.js +506 -0
- package/dist/server-context-B9GX5GOI.js +740 -0
- package/dist/server-context-BFH7HB_M.js +740 -0
- package/dist/server-node-events-CTdHBiEA.js +218 -0
- package/dist/server-node-events-DAV14qPr.js +215 -0
- package/dist/service-BZNBq9hq.js +680 -0
- package/dist/service-C-BLXx9U.js +680 -0
- package/dist/service-audit-BfJv4NqZ.js +542 -0
- package/dist/service-audit-Bw3M2OEI.js +542 -0
- package/dist/shared-5SH-45AX.js +74 -0
- package/dist/shared-BxRm5uLU.js +74 -0
- package/dist/shared-C80Rmxsd.js +150 -0
- package/dist/shared-fGK6_D2v.js +150 -0
- package/dist/skills-Bhp0l6UK.js +693 -0
- package/dist/skills-Tky2kCMO.js +694 -0
- package/dist/skills-cli-6rCClAE4.js +287 -0
- package/dist/skills-cli-C4nLCrLw.js +290 -0
- package/dist/skills-status-CENcKr3I.js +187 -0
- package/dist/skills-status-DX1eUYvk.js +187 -0
- package/dist/sqlite-CmdZSZRx.js +197 -0
- package/dist/sqlite-Dnmf3LS7.js +215 -0
- package/dist/sqlite-QDf0yuU0.js +215 -0
- package/dist/status-BSfGAp2D.js +27 -0
- package/dist/status-Bp_2NMjt.js +27 -0
- package/dist/status-C0ANDr0T.js +3140 -0
- package/dist/status-CCHBIZnm.js +21 -0
- package/dist/status-Vuqbw2Bb.js +21 -0
- package/dist/status.update-BZW5r8Su.js +79 -0
- package/dist/status.update-BnD93_O8.js +79 -0
- package/dist/subsystem-CAq3uyo7.js +834 -0
- package/dist/system-cli-Bb9zmCO1.js +83 -0
- package/dist/system-cli-TIIQ04ls.js +80 -0
- package/dist/systemd-0Qa_nGqe.js +438 -0
- package/dist/systemd-Czb0Xsm7.js +438 -0
- package/dist/systemd-hints-CWoEOQRb.js +19 -0
- package/dist/systemd-hints-Cv3RN_mZ.js +19 -0
- package/dist/systemd-linger-CsdvcIoS.js +75 -0
- package/dist/systemd-linger-DKUFHcLn.js +75 -0
- package/dist/table-DNPESyNj.js +279 -0
- package/dist/table-DS4-gmkV.js +279 -0
- package/dist/tailnet-Bg_vE5qi.js +42 -0
- package/dist/tailnet-CrNWlQRJ.js +42 -0
- package/dist/tailscale-CBv58toW.js +252 -0
- package/dist/tailscale-DCnMs7_q.js +225 -0
- package/dist/tool-display-BEACy9rK.js +795 -0
- package/dist/tool-display-NYQnSpdo.js +795 -0
- package/dist/transcript-events-CsB1Saa6.js +17 -0
- package/dist/transcript-events-DDYvbmRV.js +17 -0
- package/dist/transcript-events-JLH5W4He.js +17 -0
- package/dist/tui--NY0rnjr.js +2542 -0
- package/dist/tui-DqJfGtvM.js +2543 -0
- package/dist/tui-cli-BuHNY6wF.js +54 -0
- package/dist/tui-cli-LMFV982e.js +57 -0
- package/dist/update-CRpHtCgz.js +317 -0
- package/dist/update-D3qruxhj.js +317 -0
- package/dist/update-cli-CFF-pslM.js +948 -0
- package/dist/update-cli-cn9pEMX7.js +951 -0
- package/dist/update-runner-CxGU142L.js +1221 -0
- package/dist/update-runner-DNobz_ft.js +1221 -0
- package/dist/utils-CKSrBNwq.js +192 -0
- package/dist/utils-DX85MiPR.js +188 -0
- package/dist/webhooks-cli-BGtt2HAR.js +312 -0
- package/dist/webhooks-cli-DHLZrEO_.js +309 -0
- package/dist/widearea-dns-BpG7ATO8.js +127 -0
- package/dist/widearea-dns-D4wkCJly.js +127 -0
- package/dist/ws-3zr8WUwL.js +13 -0
- package/dist/ws-log-BXcT2xQk.js +267 -0
- package/dist/ws-log-DbDIUsgz.js +267 -0
- package/dist/ws-lzrgabja.js +13 -0
- package/dist/wsl-D2O2qOrl.js +160 -0
- package/docs/.i18n/README.md +31 -0
- package/docs/.i18n/glossary.zh-CN.json +190 -0
- package/docs/.i18n/zh-CN.tm.jsonl +1329 -0
- package/docs/CNAME +1 -0
- package/docs/_config.yml +53 -0
- package/docs/_layouts/default.html +145 -0
- package/docs/assets/markdown.css +179 -0
- package/docs/assets/openclaw-logo-text-dark.png +0 -0
- package/docs/assets/openclaw-logo-text.png +0 -0
- package/docs/assets/pixel-lobster.svg +60 -0
- package/docs/assets/showcase/agents-ui.jpg +0 -0
- package/docs/assets/showcase/bambu-cli.png +0 -0
- package/docs/assets/showcase/codexmonitor.png +0 -0
- package/docs/assets/showcase/gohome-grafana.png +0 -0
- package/docs/assets/showcase/ios-testflight.jpg +0 -0
- package/docs/assets/showcase/oura-health.png +0 -0
- package/docs/assets/showcase/padel-cli.svg +11 -0
- package/docs/assets/showcase/padel-screenshot.jpg +0 -0
- package/docs/assets/showcase/papla-tts.jpg +0 -0
- package/docs/assets/showcase/pr-review-telegram.jpg +0 -0
- package/docs/assets/showcase/roborock-screenshot.jpg +0 -0
- package/docs/assets/showcase/roborock-status.svg +13 -0
- package/docs/assets/showcase/roof-camera-sky.jpg +0 -0
- package/docs/assets/showcase/snag.png +0 -0
- package/docs/assets/showcase/tesco-shop.jpg +0 -0
- package/docs/assets/showcase/wienerlinien.png +0 -0
- package/docs/assets/showcase/wine-cellar-skill.jpg +0 -0
- package/docs/assets/showcase/winix-air-purifier.jpg +0 -0
- package/docs/assets/showcase/xuezh-pronunciation.jpeg +0 -0
- package/docs/assets/terminal.css +473 -0
- package/docs/assets/theme.js +55 -0
- package/docs/automation/auth-monitoring.md +44 -0
- package/docs/automation/cron-jobs.md +468 -0
- package/docs/automation/cron-vs-heartbeat.md +282 -0
- package/docs/automation/gmail-pubsub.md +256 -0
- package/docs/automation/poll.md +69 -0
- package/docs/automation/webhook.md +163 -0
- package/docs/bedrock.md +176 -0
- package/docs/brave-search.md +41 -0
- package/docs/broadcast-groups.md +442 -0
- package/docs/channels/bluebubbles.md +338 -0
- package/docs/channels/discord.md +475 -0
- package/docs/channels/feishu.md +507 -0
- package/docs/channels/googlechat.md +250 -0
- package/docs/channels/grammy.md +31 -0
- package/docs/channels/imessage.md +299 -0
- package/docs/channels/index.md +46 -0
- package/docs/channels/line.md +186 -0
- package/docs/channels/location.md +56 -0
- package/docs/channels/matrix.md +233 -0
- package/docs/channels/mattermost.md +138 -0
- package/docs/channels/msteams.md +768 -0
- package/docs/channels/nextcloud-talk.md +136 -0
- package/docs/channels/nostr.md +233 -0
- package/docs/channels/signal.md +202 -0
- package/docs/channels/slack.md +548 -0
- package/docs/channels/telegram.md +750 -0
- package/docs/channels/tlon.md +132 -0
- package/docs/channels/troubleshooting.md +29 -0
- package/docs/channels/twitch.md +379 -0
- package/docs/channels/whatsapp.md +404 -0
- package/docs/channels/zalo.md +189 -0
- package/docs/channels/zalouser.md +140 -0
- package/docs/cli/acp.md +170 -0
- package/docs/cli/agent.md +24 -0
- package/docs/cli/agents.md +75 -0
- package/docs/cli/approvals.md +50 -0
- package/docs/cli/browser.md +107 -0
- package/docs/cli/channels.md +79 -0
- package/docs/cli/config.md +50 -0
- package/docs/cli/configure.md +33 -0
- package/docs/cli/cron.md +42 -0
- package/docs/cli/dashboard.md +16 -0
- package/docs/cli/devices.md +67 -0
- package/docs/cli/directory.md +63 -0
- package/docs/cli/dns.md +23 -0
- package/docs/cli/docs.md +15 -0
- package/docs/cli/doctor.md +41 -0
- package/docs/cli/gateway.md +199 -0
- package/docs/cli/health.md +21 -0
- package/docs/cli/hooks.md +304 -0
- package/docs/cli/index.md +1029 -0
- package/docs/cli/logs.md +24 -0
- package/docs/cli/memory.md +45 -0
- package/docs/cli/message.md +239 -0
- package/docs/cli/models.md +79 -0
- package/docs/cli/node.md +112 -0
- package/docs/cli/nodes.md +73 -0
- package/docs/cli/onboard.md +29 -0
- package/docs/cli/pairing.md +21 -0
- package/docs/cli/plugins.md +62 -0
- package/docs/cli/reset.md +17 -0
- package/docs/cli/sandbox.md +152 -0
- package/docs/cli/security.md +26 -0
- package/docs/cli/sessions.md +16 -0
- package/docs/cli/setup.md +29 -0
- package/docs/cli/skills.md +26 -0
- package/docs/cli/status.md +26 -0
- package/docs/cli/system.md +60 -0
- package/docs/cli/tui.md +23 -0
- package/docs/cli/uninstall.md +17 -0
- package/docs/cli/update.md +98 -0
- package/docs/cli/voicecall.md +34 -0
- package/docs/cli/webhooks.md +25 -0
- package/docs/concepts/agent-loop.md +146 -0
- package/docs/concepts/agent-workspace.md +233 -0
- package/docs/concepts/agent.md +123 -0
- package/docs/concepts/architecture.md +129 -0
- package/docs/concepts/channel-routing.md +114 -0
- package/docs/concepts/compaction.md +61 -0
- package/docs/concepts/context.md +161 -0
- package/docs/concepts/group-messages.md +84 -0
- package/docs/concepts/groups.md +373 -0
- package/docs/concepts/markdown-formatting.md +130 -0
- package/docs/concepts/memory.md +546 -0
- package/docs/concepts/messages.md +154 -0
- package/docs/concepts/model-failover.md +149 -0
- package/docs/concepts/model-providers.md +316 -0
- package/docs/concepts/models.md +208 -0
- package/docs/concepts/multi-agent.md +376 -0
- package/docs/concepts/oauth.md +145 -0
- package/docs/concepts/presence.md +102 -0
- package/docs/concepts/queue.md +89 -0
- package/docs/concepts/retry.md +69 -0
- package/docs/concepts/session-pruning.md +122 -0
- package/docs/concepts/session-tool.md +193 -0
- package/docs/concepts/session.md +188 -0
- package/docs/concepts/sessions.md +10 -0
- package/docs/concepts/streaming.md +135 -0
- package/docs/concepts/system-prompt.md +115 -0
- package/docs/concepts/timezone.md +91 -0
- package/docs/concepts/typebox.md +289 -0
- package/docs/concepts/typing-indicators.md +68 -0
- package/docs/concepts/usage-tracking.md +35 -0
- package/docs/date-time.md +128 -0
- package/docs/debug/node-issue.md +83 -0
- package/docs/debugging.md +162 -0
- package/docs/diagnostics/flags.md +91 -0
- package/docs/docs.json +1587 -0
- package/docs/environment.md +81 -0
- package/docs/experiments/onboarding-config-protocol.md +40 -0
- package/docs/experiments/plans/cron-add-hardening.md +63 -0
- package/docs/experiments/plans/group-policy-hardening.md +40 -0
- package/docs/experiments/plans/openresponses-gateway.md +123 -0
- package/docs/experiments/proposals/model-config.md +36 -0
- package/docs/experiments/research/memory.md +228 -0
- package/docs/gateway/authentication.md +145 -0
- package/docs/gateway/background-process.md +93 -0
- package/docs/gateway/bonjour.md +167 -0
- package/docs/gateway/bridge-protocol.md +89 -0
- package/docs/gateway/cli-backends.md +223 -0
- package/docs/gateway/configuration-examples.md +606 -0
- package/docs/gateway/configuration.md +3393 -0
- package/docs/gateway/discovery.md +116 -0
- package/docs/gateway/doctor.md +282 -0
- package/docs/gateway/gateway-lock.md +34 -0
- package/docs/gateway/health.md +35 -0
- package/docs/gateway/heartbeat.md +302 -0
- package/docs/gateway/index.md +328 -0
- package/docs/gateway/local-models.md +150 -0
- package/docs/gateway/logging.md +113 -0
- package/docs/gateway/multiple-gateways.md +112 -0
- package/docs/gateway/openai-http-api.md +118 -0
- package/docs/gateway/openresponses-http-api.md +317 -0
- package/docs/gateway/pairing.md +99 -0
- package/docs/gateway/protocol.md +221 -0
- package/docs/gateway/remote-gateway-readme.md +157 -0
- package/docs/gateway/remote.md +127 -0
- package/docs/gateway/sandbox-vs-tool-policy-vs-elevated.md +128 -0
- package/docs/gateway/sandboxing.md +193 -0
- package/docs/gateway/security/formal-verification.md +164 -0
- package/docs/gateway/security/index.md +825 -0
- package/docs/gateway/tailscale.md +127 -0
- package/docs/gateway/tools-invoke-http-api.md +85 -0
- package/docs/gateway/troubleshooting.md +767 -0
- package/docs/help/faq.md +2830 -0
- package/docs/help/index.md +21 -0
- package/docs/help/troubleshooting.md +98 -0
- package/docs/hooks/soul-evil.md +69 -0
- package/docs/hooks.md +913 -0
- package/docs/images/feishu-step2-create-app.png +0 -0
- package/docs/images/feishu-step3-credentials.png +0 -0
- package/docs/images/feishu-step4-permissions.png +0 -0
- package/docs/images/feishu-step5-bot-capability.png +0 -0
- package/docs/images/feishu-step6-event-subscription.png +0 -0
- package/docs/images/groups-flow.svg +52 -0
- package/docs/images/mobile-ui-screenshot.png +0 -0
- package/docs/index.md +258 -0
- package/docs/install/ansible.md +208 -0
- package/docs/install/bun.md +59 -0
- package/docs/install/development-channels.md +75 -0
- package/docs/install/docker.md +567 -0
- package/docs/install/index.md +185 -0
- package/docs/install/installer.md +123 -0
- package/docs/install/migrating.md +192 -0
- package/docs/install/nix.md +96 -0
- package/docs/install/node.md +78 -0
- package/docs/install/uninstall.md +128 -0
- package/docs/install/updating.md +228 -0
- package/docs/logging.md +350 -0
- package/docs/multi-agent-sandbox-tools.md +395 -0
- package/docs/network.md +54 -0
- package/docs/nodes/audio.md +114 -0
- package/docs/nodes/camera.md +156 -0
- package/docs/nodes/images.md +72 -0
- package/docs/nodes/index.md +341 -0
- package/docs/nodes/location-command.md +113 -0
- package/docs/nodes/media-understanding.md +379 -0
- package/docs/nodes/talk.md +90 -0
- package/docs/nodes/voicewake.md +65 -0
- package/docs/northflank.mdx +53 -0
- package/docs/perplexity.md +80 -0
- package/docs/pi-dev.md +70 -0
- package/docs/pi.md +612 -0
- package/docs/platforms/android.md +148 -0
- package/docs/platforms/digitalocean.md +262 -0
- package/docs/platforms/exe-dev.md +125 -0
- package/docs/platforms/fly.md +486 -0
- package/docs/platforms/gcp.md +503 -0
- package/docs/platforms/hetzner.md +330 -0
- package/docs/platforms/index.md +53 -0
- package/docs/platforms/ios.md +107 -0
- package/docs/platforms/linux.md +94 -0
- package/docs/platforms/mac/bundled-gateway.md +73 -0
- package/docs/platforms/mac/canvas.md +125 -0
- package/docs/platforms/mac/child-process.md +69 -0
- package/docs/platforms/mac/dev-setup.md +102 -0
- package/docs/platforms/mac/health.md +34 -0
- package/docs/platforms/mac/icon.md +31 -0
- package/docs/platforms/mac/logging.md +57 -0
- package/docs/platforms/mac/menu-bar.md +81 -0
- package/docs/platforms/mac/peekaboo.md +65 -0
- package/docs/platforms/mac/permissions.md +44 -0
- package/docs/platforms/mac/release.md +85 -0
- package/docs/platforms/mac/remote.md +83 -0
- package/docs/platforms/mac/signing.md +47 -0
- package/docs/platforms/mac/skills.md +33 -0
- package/docs/platforms/mac/voice-overlay.md +60 -0
- package/docs/platforms/mac/voicewake.md +67 -0
- package/docs/platforms/mac/webchat.md +41 -0
- package/docs/platforms/mac/xpc.md +61 -0
- package/docs/platforms/macos-vm.md +281 -0
- package/docs/platforms/macos.md +203 -0
- package/docs/platforms/oracle.md +303 -0
- package/docs/platforms/raspberry-pi.md +358 -0
- package/docs/platforms/windows.md +159 -0
- package/docs/plugin.md +664 -0
- package/docs/plugins/agent-tools.md +99 -0
- package/docs/plugins/manifest.md +71 -0
- package/docs/plugins/voice-call.md +284 -0
- package/docs/plugins/zalouser.md +81 -0
- package/docs/prose.md +134 -0
- package/docs/providers/anthropic.md +152 -0
- package/docs/providers/claude-max-api-proxy.md +148 -0
- package/docs/providers/cloudflare-ai-gateway.md +71 -0
- package/docs/providers/deepgram.md +93 -0
- package/docs/providers/github-copilot.md +72 -0
- package/docs/providers/glm.md +33 -0
- package/docs/providers/index.md +63 -0
- package/docs/providers/minimax.md +208 -0
- package/docs/providers/models.md +51 -0
- package/docs/providers/moonshot.md +142 -0
- package/docs/providers/ollama.md +223 -0
- package/docs/providers/openai.md +62 -0
- package/docs/providers/opencode.md +36 -0
- package/docs/providers/openrouter.md +37 -0
- package/docs/providers/qwen.md +53 -0
- package/docs/providers/synthetic.md +99 -0
- package/docs/providers/venice.md +267 -0
- package/docs/providers/vercel-ai-gateway.md +50 -0
- package/docs/providers/xiaomi.md +64 -0
- package/docs/providers/zai.md +36 -0
- package/docs/railway.mdx +99 -0
- package/docs/refactor/clawnet.md +417 -0
- package/docs/refactor/exec-host.md +316 -0
- package/docs/refactor/outbound-session-mirroring.md +85 -0
- package/docs/refactor/plugin-sdk.md +214 -0
- package/docs/refactor/strict-config.md +93 -0
- package/docs/reference/AGENTS.default.md +124 -0
- package/docs/reference/RELEASING.md +120 -0
- package/docs/reference/api-usage-costs.md +137 -0
- package/docs/reference/device-models.md +47 -0
- package/docs/reference/rpc.md +43 -0
- package/docs/reference/session-management-compaction.md +285 -0
- package/docs/reference/templates/AGENTS.dev.md +83 -0
- package/docs/reference/templates/AGENTS.md +218 -0
- package/docs/reference/templates/BOOT.md +10 -0
- package/docs/reference/templates/BOOTSTRAP.md +61 -0
- package/docs/reference/templates/HEARTBEAT.md +11 -0
- package/docs/reference/templates/IDENTITY.dev.md +47 -0
- package/docs/reference/templates/IDENTITY.md +27 -0
- package/docs/reference/templates/SOUL.dev.md +76 -0
- package/docs/reference/templates/SOUL.md +42 -0
- package/docs/reference/templates/TOOLS.dev.md +24 -0
- package/docs/reference/templates/TOOLS.md +46 -0
- package/docs/reference/templates/USER.dev.md +18 -0
- package/docs/reference/templates/USER.md +22 -0
- package/docs/reference/test.md +50 -0
- package/docs/reference/transcript-hygiene.md +129 -0
- package/docs/render.mdx +165 -0
- package/docs/scripts.md +28 -0
- package/docs/security/formal-verification.md +164 -0
- package/docs/start/getting-started.md +208 -0
- package/docs/start/hubs.md +185 -0
- package/docs/start/lore.md +219 -0
- package/docs/start/onboarding.md +110 -0
- package/docs/start/openclaw.md +241 -0
- package/docs/start/pairing.md +86 -0
- package/docs/start/setup.md +149 -0
- package/docs/start/showcase.md +416 -0
- package/docs/start/wizard.md +349 -0
- package/docs/testing.md +368 -0
- package/docs/token-use.md +112 -0
- package/docs/tools/agent-send.md +53 -0
- package/docs/tools/apply-patch.md +50 -0
- package/docs/tools/browser-linux-troubleshooting.md +139 -0
- package/docs/tools/browser-login.md +68 -0
- package/docs/tools/browser.md +576 -0
- package/docs/tools/chrome-extension.md +178 -0
- package/docs/tools/clawhub.md +257 -0
- package/docs/tools/creating-skills.md +54 -0
- package/docs/tools/elevated.md +57 -0
- package/docs/tools/exec-approvals.md +246 -0
- package/docs/tools/exec.md +179 -0
- package/docs/tools/firecrawl.md +61 -0
- package/docs/tools/index.md +509 -0
- package/docs/tools/llm-task.md +115 -0
- package/docs/tools/lobster.md +342 -0
- package/docs/tools/reactions.md +22 -0
- package/docs/tools/skills-config.md +76 -0
- package/docs/tools/skills.md +300 -0
- package/docs/tools/slash-commands.md +198 -0
- package/docs/tools/subagents.md +151 -0
- package/docs/tools/thinking.md +73 -0
- package/docs/tools/web.md +261 -0
- package/docs/tts.md +396 -0
- package/docs/tui.md +159 -0
- package/docs/vps.md +43 -0
- package/docs/web/control-ui.md +221 -0
- package/docs/web/dashboard.md +46 -0
- package/docs/web/index.md +116 -0
- package/docs/web/webchat.md +49 -0
- package/docs/whatsapp-openclaw-ai-zh.jpg +0 -0
- package/docs/whatsapp-openclaw.jpg +0 -0
- package/docs/zh-CN/AGENTS.md +59 -0
- package/docs/zh-CN/automation/auth-monitoring.md +47 -0
- package/docs/zh-CN/automation/cron-jobs.md +424 -0
- package/docs/zh-CN/automation/cron-vs-heartbeat.md +286 -0
- package/docs/zh-CN/automation/gmail-pubsub.md +249 -0
- package/docs/zh-CN/automation/poll.md +76 -0
- package/docs/zh-CN/automation/webhook.md +163 -0
- package/docs/zh-CN/bedrock.md +170 -0
- package/docs/zh-CN/brave-search.md +48 -0
- package/docs/zh-CN/broadcast-groups.md +449 -0
- package/docs/zh-CN/channels/bluebubbles.md +271 -0
- package/docs/zh-CN/channels/discord.md +468 -0
- package/docs/zh-CN/channels/feishu.md +513 -0
- package/docs/zh-CN/channels/googlechat.md +257 -0
- package/docs/zh-CN/channels/grammy.md +38 -0
- package/docs/zh-CN/channels/imessage.md +302 -0
- package/docs/zh-CN/channels/index.md +53 -0
- package/docs/zh-CN/channels/line.md +180 -0
- package/docs/zh-CN/channels/location.md +63 -0
- package/docs/zh-CN/channels/matrix.md +221 -0
- package/docs/zh-CN/channels/mattermost.md +144 -0
- package/docs/zh-CN/channels/msteams.md +775 -0
- package/docs/zh-CN/channels/nextcloud-talk.md +142 -0
- package/docs/zh-CN/channels/nostr.md +240 -0
- package/docs/zh-CN/channels/signal.md +209 -0
- package/docs/zh-CN/channels/slack.md +531 -0
- package/docs/zh-CN/channels/telegram.md +751 -0
- package/docs/zh-CN/channels/tlon.md +136 -0
- package/docs/zh-CN/channels/troubleshooting.md +36 -0
- package/docs/zh-CN/channels/twitch.md +385 -0
- package/docs/zh-CN/channels/whatsapp.md +411 -0
- package/docs/zh-CN/channels/zalo.md +196 -0
- package/docs/zh-CN/channels/zalouser.md +147 -0
- package/docs/zh-CN/cli/acp.md +173 -0
- package/docs/zh-CN/cli/agent.md +30 -0
- package/docs/zh-CN/cli/agents.md +82 -0
- package/docs/zh-CN/cli/approvals.md +57 -0
- package/docs/zh-CN/cli/browser.md +114 -0
- package/docs/zh-CN/cli/channels.md +86 -0
- package/docs/zh-CN/cli/config.md +57 -0
- package/docs/zh-CN/cli/configure.md +38 -0
- package/docs/zh-CN/cli/cron.md +43 -0
- package/docs/zh-CN/cli/dashboard.md +23 -0
- package/docs/zh-CN/cli/devices.md +74 -0
- package/docs/zh-CN/cli/directory.md +70 -0
- package/docs/zh-CN/cli/dns.md +30 -0
- package/docs/zh-CN/cli/docs.md +22 -0
- package/docs/zh-CN/cli/doctor.md +48 -0
- package/docs/zh-CN/cli/gateway.md +206 -0
- package/docs/zh-CN/cli/health.md +28 -0
- package/docs/zh-CN/cli/hooks.md +311 -0
- package/docs/zh-CN/cli/index.md +1032 -0
- package/docs/zh-CN/cli/logs.md +31 -0
- package/docs/zh-CN/cli/memory.md +52 -0
- package/docs/zh-CN/cli/message.md +246 -0
- package/docs/zh-CN/cli/models.md +85 -0
- package/docs/zh-CN/cli/node.md +115 -0
- package/docs/zh-CN/cli/nodes.md +80 -0
- package/docs/zh-CN/cli/onboard.md +36 -0
- package/docs/zh-CN/cli/pairing.md +28 -0
- package/docs/zh-CN/cli/plugins.md +66 -0
- package/docs/zh-CN/cli/reset.md +24 -0
- package/docs/zh-CN/cli/sandbox.md +158 -0
- package/docs/zh-CN/cli/security.md +33 -0
- package/docs/zh-CN/cli/sessions.md +23 -0
- package/docs/zh-CN/cli/setup.md +36 -0
- package/docs/zh-CN/cli/skills.md +33 -0
- package/docs/zh-CN/cli/status.md +33 -0
- package/docs/zh-CN/cli/system.md +63 -0
- package/docs/zh-CN/cli/tui.md +30 -0
- package/docs/zh-CN/cli/uninstall.md +24 -0
- package/docs/zh-CN/cli/update.md +101 -0
- package/docs/zh-CN/cli/voicecall.md +41 -0
- package/docs/zh-CN/cli/webhooks.md +32 -0
- package/docs/zh-CN/concepts/agent-loop.md +146 -0
- package/docs/zh-CN/concepts/agent-workspace.md +219 -0
- package/docs/zh-CN/concepts/agent.md +115 -0
- package/docs/zh-CN/concepts/architecture.md +123 -0
- package/docs/zh-CN/concepts/channel-routing.md +117 -0
- package/docs/zh-CN/concepts/compaction.md +67 -0
- package/docs/zh-CN/concepts/context.md +168 -0
- package/docs/zh-CN/concepts/group-messages.md +91 -0
- package/docs/zh-CN/concepts/groups.md +379 -0
- package/docs/zh-CN/concepts/markdown-formatting.md +117 -0
- package/docs/zh-CN/concepts/memory.md +412 -0
- package/docs/zh-CN/concepts/messages.md +141 -0
- package/docs/zh-CN/concepts/model-failover.md +145 -0
- package/docs/zh-CN/concepts/model-providers.md +320 -0
- package/docs/zh-CN/concepts/models.md +196 -0
- package/docs/zh-CN/concepts/multi-agent.md +372 -0
- package/docs/zh-CN/concepts/oauth.md +151 -0
- package/docs/zh-CN/concepts/presence.md +99 -0
- package/docs/zh-CN/concepts/queue.md +94 -0
- package/docs/zh-CN/concepts/retry.md +76 -0
- package/docs/zh-CN/concepts/session-pruning.md +129 -0
- package/docs/zh-CN/concepts/session-tool.md +200 -0
- package/docs/zh-CN/concepts/session.md +166 -0
- package/docs/zh-CN/concepts/sessions.md +17 -0
- package/docs/zh-CN/concepts/streaming.md +133 -0
- package/docs/zh-CN/concepts/system-prompt.md +101 -0
- package/docs/zh-CN/concepts/timezone.md +96 -0
- package/docs/zh-CN/concepts/typebox.md +284 -0
- package/docs/zh-CN/concepts/typing-indicators.md +74 -0
- package/docs/zh-CN/concepts/usage-tracking.md +42 -0
- package/docs/zh-CN/date-time.md +129 -0
- package/docs/zh-CN/debug/node-issue.md +90 -0
- package/docs/zh-CN/debugging.md +160 -0
- package/docs/zh-CN/diagnostics/flags.md +98 -0
- package/docs/zh-CN/environment.md +88 -0
- package/docs/zh-CN/experiments/onboarding-config-protocol.md +47 -0
- package/docs/zh-CN/experiments/plans/cron-add-hardening.md +70 -0
- package/docs/zh-CN/experiments/plans/group-policy-hardening.md +45 -0
- package/docs/zh-CN/experiments/plans/openresponses-gateway.md +121 -0
- package/docs/zh-CN/experiments/proposals/model-config.md +42 -0
- package/docs/zh-CN/experiments/research/memory.md +235 -0
- package/docs/zh-CN/gateway/authentication.md +142 -0
- package/docs/zh-CN/gateway/background-process.md +100 -0
- package/docs/zh-CN/gateway/bonjour.md +174 -0
- package/docs/zh-CN/gateway/bridge-protocol.md +86 -0
- package/docs/zh-CN/gateway/cli-backends.md +213 -0
- package/docs/zh-CN/gateway/configuration-examples.md +587 -0
- package/docs/zh-CN/gateway/configuration.md +3332 -0
- package/docs/zh-CN/gateway/discovery.md +123 -0
- package/docs/zh-CN/gateway/doctor.md +238 -0
- package/docs/zh-CN/gateway/gateway-lock.md +41 -0
- package/docs/zh-CN/gateway/health.md +42 -0
- package/docs/zh-CN/gateway/heartbeat.md +274 -0
- package/docs/zh-CN/gateway/index.md +335 -0
- package/docs/zh-CN/gateway/local-models.md +157 -0
- package/docs/zh-CN/gateway/logging.md +114 -0
- package/docs/zh-CN/gateway/multiple-gateways.md +119 -0
- package/docs/zh-CN/gateway/openai-http-api.md +125 -0
- package/docs/zh-CN/gateway/openresponses-http-api.md +317 -0
- package/docs/zh-CN/gateway/pairing.md +99 -0
- package/docs/zh-CN/gateway/protocol.md +220 -0
- package/docs/zh-CN/gateway/remote-gateway-readme.md +164 -0
- package/docs/zh-CN/gateway/remote.md +133 -0
- package/docs/zh-CN/gateway/sandbox-vs-tool-policy-vs-elevated.md +135 -0
- package/docs/zh-CN/gateway/sandboxing.md +188 -0
- package/docs/zh-CN/gateway/security/formal-verification.md +169 -0
- package/docs/zh-CN/gateway/security/index.md +777 -0
- package/docs/zh-CN/gateway/tailscale.md +124 -0
- package/docs/zh-CN/gateway/tools-invoke-http-api.md +92 -0
- package/docs/zh-CN/gateway/troubleshooting.md +771 -0
- package/docs/zh-CN/help/faq.md +2628 -0
- package/docs/zh-CN/help/index.md +28 -0
- package/docs/zh-CN/help/troubleshooting.md +104 -0
- package/docs/zh-CN/hooks/soul-evil.md +72 -0
- package/docs/zh-CN/hooks.md +919 -0
- package/docs/zh-CN/index.md +264 -0
- package/docs/zh-CN/install/ansible.md +215 -0
- package/docs/zh-CN/install/bun.md +65 -0
- package/docs/zh-CN/install/development-channels.md +81 -0
- package/docs/zh-CN/install/docker.md +532 -0
- package/docs/zh-CN/install/index.md +193 -0
- package/docs/zh-CN/install/installer.md +128 -0
- package/docs/zh-CN/install/migrating.md +199 -0
- package/docs/zh-CN/install/nix.md +99 -0
- package/docs/zh-CN/install/node.md +85 -0
- package/docs/zh-CN/install/uninstall.md +135 -0
- package/docs/zh-CN/install/updating.md +233 -0
- package/docs/zh-CN/logging.md +329 -0
- package/docs/zh-CN/multi-agent-sandbox-tools.md +401 -0
- package/docs/zh-CN/network.md +59 -0
- package/docs/zh-CN/nodes/audio.md +120 -0
- package/docs/zh-CN/nodes/camera.md +162 -0
- package/docs/zh-CN/nodes/images.md +79 -0
- package/docs/zh-CN/nodes/index.md +348 -0
- package/docs/zh-CN/nodes/location-command.md +120 -0
- package/docs/zh-CN/nodes/media-understanding.md +380 -0
- package/docs/zh-CN/nodes/talk.md +97 -0
- package/docs/zh-CN/nodes/voicewake.md +72 -0
- package/docs/zh-CN/northflank.mdx +60 -0
- package/docs/zh-CN/perplexity.md +84 -0
- package/docs/zh-CN/pi-dev.md +77 -0
- package/docs/zh-CN/pi.md +619 -0
- package/docs/zh-CN/platforms/android.md +155 -0
- package/docs/zh-CN/platforms/digitalocean.md +269 -0
- package/docs/zh-CN/platforms/exe-dev.md +127 -0
- package/docs/zh-CN/platforms/fly.md +490 -0
- package/docs/zh-CN/platforms/gcp.md +510 -0
- package/docs/zh-CN/platforms/hetzner.md +337 -0
- package/docs/zh-CN/platforms/index.md +60 -0
- package/docs/zh-CN/platforms/ios.md +114 -0
- package/docs/zh-CN/platforms/linux.md +101 -0
- package/docs/zh-CN/platforms/mac/bundled-gateway.md +75 -0
- package/docs/zh-CN/platforms/mac/canvas.md +128 -0
- package/docs/zh-CN/platforms/mac/child-process.md +73 -0
- package/docs/zh-CN/platforms/mac/dev-setup.md +109 -0
- package/docs/zh-CN/platforms/mac/health.md +41 -0
- package/docs/zh-CN/platforms/mac/icon.md +38 -0
- package/docs/zh-CN/platforms/mac/logging.md +64 -0
- package/docs/zh-CN/platforms/mac/menu-bar.md +88 -0
- package/docs/zh-CN/platforms/mac/peekaboo.md +62 -0
- package/docs/zh-CN/platforms/mac/permissions.md +46 -0
- package/docs/zh-CN/platforms/mac/release.md +92 -0
- package/docs/zh-CN/platforms/mac/remote.md +90 -0
- package/docs/zh-CN/platforms/mac/signing.md +54 -0
- package/docs/zh-CN/platforms/mac/skills.md +40 -0
- package/docs/zh-CN/platforms/mac/voice-overlay.md +67 -0
- package/docs/zh-CN/platforms/mac/voicewake.md +74 -0
- package/docs/zh-CN/platforms/mac/webchat.md +43 -0
- package/docs/zh-CN/platforms/mac/xpc.md +68 -0
- package/docs/zh-CN/platforms/macos-vm.md +288 -0
- package/docs/zh-CN/platforms/macos.md +193 -0
- package/docs/zh-CN/platforms/oracle.md +310 -0
- package/docs/zh-CN/platforms/raspberry-pi.md +365 -0
- package/docs/zh-CN/platforms/windows.md +156 -0
- package/docs/zh-CN/plugin.md +639 -0
- package/docs/zh-CN/plugins/agent-tools.md +99 -0
- package/docs/zh-CN/plugins/manifest.md +68 -0
- package/docs/zh-CN/plugins/voice-call.md +250 -0
- package/docs/zh-CN/plugins/zalouser.md +88 -0
- package/docs/zh-CN/prose.md +141 -0
- package/docs/zh-CN/providers/anthropic.md +159 -0
- package/docs/zh-CN/providers/claude-max-api-proxy.md +155 -0
- package/docs/zh-CN/providers/deepgram.md +97 -0
- package/docs/zh-CN/providers/github-copilot.md +67 -0
- package/docs/zh-CN/providers/glm.md +39 -0
- package/docs/zh-CN/providers/index.md +68 -0
- package/docs/zh-CN/providers/minimax.md +206 -0
- package/docs/zh-CN/providers/models.md +55 -0
- package/docs/zh-CN/providers/moonshot.md +145 -0
- package/docs/zh-CN/providers/ollama.md +230 -0
- package/docs/zh-CN/providers/openai.md +68 -0
- package/docs/zh-CN/providers/opencode.md +41 -0
- package/docs/zh-CN/providers/openrouter.md +43 -0
- package/docs/zh-CN/providers/qwen.md +55 -0
- package/docs/zh-CN/providers/synthetic.md +102 -0
- package/docs/zh-CN/providers/venice.md +274 -0
- package/docs/zh-CN/providers/vercel-ai-gateway.md +57 -0
- package/docs/zh-CN/providers/xiaomi.md +68 -0
- package/docs/zh-CN/providers/zai.md +41 -0
- package/docs/zh-CN/railway.mdx +106 -0
- package/docs/zh-CN/refactor/clawnet.md +424 -0
- package/docs/zh-CN/refactor/exec-host.md +323 -0
- package/docs/zh-CN/refactor/outbound-session-mirroring.md +92 -0
- package/docs/zh-CN/refactor/plugin-sdk.md +221 -0
- package/docs/zh-CN/refactor/strict-config.md +100 -0
- package/docs/zh-CN/reference/AGENTS.default.md +131 -0
- package/docs/zh-CN/reference/RELEASING.md +123 -0
- package/docs/zh-CN/reference/api-usage-costs.md +136 -0
- package/docs/zh-CN/reference/device-models.md +54 -0
- package/docs/zh-CN/reference/rpc.md +48 -0
- package/docs/zh-CN/reference/session-management-compaction.md +287 -0
- package/docs/zh-CN/reference/templates/AGENTS.dev.md +89 -0
- package/docs/zh-CN/reference/templates/AGENTS.md +225 -0
- package/docs/zh-CN/reference/templates/BOOT.md +17 -0
- package/docs/zh-CN/reference/templates/BOOTSTRAP.md +68 -0
- package/docs/zh-CN/reference/templates/HEARTBEAT.md +18 -0
- package/docs/zh-CN/reference/templates/IDENTITY.dev.md +54 -0
- package/docs/zh-CN/reference/templates/IDENTITY.md +35 -0
- package/docs/zh-CN/reference/templates/SOUL.dev.md +83 -0
- package/docs/zh-CN/reference/templates/SOUL.md +49 -0
- package/docs/zh-CN/reference/templates/TOOLS.dev.md +31 -0
- package/docs/zh-CN/reference/templates/TOOLS.md +53 -0
- package/docs/zh-CN/reference/templates/USER.dev.md +25 -0
- package/docs/zh-CN/reference/templates/USER.md +30 -0
- package/docs/zh-CN/reference/test.md +57 -0
- package/docs/zh-CN/reference/transcript-hygiene.md +109 -0
- package/docs/zh-CN/render.mdx +169 -0
- package/docs/zh-CN/scripts.md +35 -0
- package/docs/zh-CN/security/formal-verification.md +171 -0
- package/docs/zh-CN/start/getting-started.md +206 -0
- package/docs/zh-CN/start/hubs.md +191 -0
- package/docs/zh-CN/start/lore.md +226 -0
- package/docs/zh-CN/start/onboarding.md +105 -0
- package/docs/zh-CN/start/openclaw.md +248 -0
- package/docs/zh-CN/start/pairing.md +89 -0
- package/docs/zh-CN/start/setup.md +153 -0
- package/docs/zh-CN/start/showcase.md +423 -0
- package/docs/zh-CN/start/wizard.md +331 -0
- package/docs/zh-CN/testing.md +375 -0
- package/docs/zh-CN/token-use.md +119 -0
- package/docs/zh-CN/tools/agent-send.md +59 -0
- package/docs/zh-CN/tools/apply-patch.md +57 -0
- package/docs/zh-CN/tools/browser-linux-troubleshooting.md +144 -0
- package/docs/zh-CN/tools/browser-login.md +75 -0
- package/docs/zh-CN/tools/browser.md +553 -0
- package/docs/zh-CN/tools/chrome-extension.md +183 -0
- package/docs/zh-CN/tools/clawhub.md +209 -0
- package/docs/zh-CN/tools/creating-skills.md +61 -0
- package/docs/zh-CN/tools/elevated.md +64 -0
- package/docs/zh-CN/tools/exec-approvals.md +234 -0
- package/docs/zh-CN/tools/exec.md +169 -0
- package/docs/zh-CN/tools/firecrawl.md +68 -0
- package/docs/zh-CN/tools/index.md +515 -0
- package/docs/zh-CN/tools/llm-task.md +117 -0
- package/docs/zh-CN/tools/lobster.md +349 -0
- package/docs/zh-CN/tools/reactions.md +29 -0
- package/docs/zh-CN/tools/skills-config.md +78 -0
- package/docs/zh-CN/tools/skills.md +279 -0
- package/docs/zh-CN/tools/slash-commands.md +205 -0
- package/docs/zh-CN/tools/subagents.md +156 -0
- package/docs/zh-CN/tools/thinking.md +80 -0
- package/docs/zh-CN/tools/web.md +257 -0
- package/docs/zh-CN/tts.md +375 -0
- package/docs/zh-CN/tui.md +166 -0
- package/docs/zh-CN/vps.md +47 -0
- package/docs/zh-CN/web/control-ui.md +191 -0
- package/docs/zh-CN/web/dashboard.md +53 -0
- package/docs/zh-CN/web/index.md +118 -0
- package/docs/zh-CN/web/webchat.md +56 -0
- package/extensions/bluebubbles/README.md +45 -0
- package/extensions/bluebubbles/index.ts +19 -0
- package/extensions/bluebubbles/node_modules/.bin/openclaw +21 -0
- package/extensions/bluebubbles/openclaw.plugin.json +9 -0
- package/extensions/bluebubbles/package.json +36 -0
- package/extensions/bluebubbles/src/accounts.ts +88 -0
- package/extensions/bluebubbles/src/actions.test.ts +650 -0
- package/extensions/bluebubbles/src/actions.ts +438 -0
- package/extensions/bluebubbles/src/attachments.test.ts +345 -0
- package/extensions/bluebubbles/src/attachments.ts +300 -0
- package/extensions/bluebubbles/src/channel.ts +414 -0
- package/extensions/bluebubbles/src/chat.test.ts +461 -0
- package/extensions/bluebubbles/src/chat.ts +378 -0
- package/extensions/bluebubbles/src/config-schema.ts +51 -0
- package/extensions/bluebubbles/src/media-send.ts +174 -0
- package/extensions/bluebubbles/src/monitor.test.ts +2342 -0
- package/extensions/bluebubbles/src/monitor.ts +2490 -0
- package/extensions/bluebubbles/src/onboarding.ts +352 -0
- package/extensions/bluebubbles/src/probe.ts +135 -0
- package/extensions/bluebubbles/src/reactions.test.ts +392 -0
- package/extensions/bluebubbles/src/reactions.ts +188 -0
- package/extensions/bluebubbles/src/runtime.ts +14 -0
- package/extensions/bluebubbles/src/send.test.ts +808 -0
- package/extensions/bluebubbles/src/send.ts +467 -0
- package/extensions/bluebubbles/src/targets.test.ts +183 -0
- package/extensions/bluebubbles/src/targets.ts +422 -0
- package/extensions/bluebubbles/src/types.ts +127 -0
- package/extensions/copilot-proxy/README.md +24 -0
- package/extensions/copilot-proxy/index.ts +148 -0
- package/extensions/copilot-proxy/node_modules/.bin/openclaw +21 -0
- package/extensions/copilot-proxy/openclaw.plugin.json +9 -0
- package/extensions/copilot-proxy/package.json +14 -0
- package/extensions/diagnostics-otel/index.ts +15 -0
- package/extensions/diagnostics-otel/node_modules/.bin/openclaw +21 -0
- package/extensions/diagnostics-otel/openclaw.plugin.json +8 -0
- package/extensions/diagnostics-otel/package.json +27 -0
- package/extensions/diagnostics-otel/src/service.test.ts +226 -0
- package/extensions/diagnostics-otel/src/service.ts +635 -0
- package/extensions/discord/index.ts +17 -0
- package/extensions/discord/node_modules/.bin/openclaw +21 -0
- package/extensions/discord/openclaw.plugin.json +9 -0
- package/extensions/discord/package.json +14 -0
- package/extensions/discord/src/channel.ts +422 -0
- package/extensions/discord/src/runtime.ts +14 -0
- package/extensions/feishu/README.md +47 -0
- package/extensions/feishu/index.ts +15 -0
- package/extensions/feishu/node_modules/.bin/openclaw +21 -0
- package/extensions/feishu/openclaw.plugin.json +9 -0
- package/extensions/feishu/package.json +33 -0
- package/extensions/feishu/src/channel.ts +276 -0
- package/extensions/feishu/src/config-schema.ts +46 -0
- package/extensions/feishu/src/onboarding.ts +278 -0
- package/extensions/google-antigravity-auth/README.md +24 -0
- package/extensions/google-antigravity-auth/index.ts +461 -0
- package/extensions/google-antigravity-auth/node_modules/.bin/openclaw +21 -0
- package/extensions/google-antigravity-auth/openclaw.plugin.json +9 -0
- package/extensions/google-antigravity-auth/package.json +14 -0
- package/extensions/google-gemini-cli-auth/README.md +35 -0
- package/extensions/google-gemini-cli-auth/index.ts +88 -0
- package/extensions/google-gemini-cli-auth/node_modules/.bin/openclaw +21 -0
- package/extensions/google-gemini-cli-auth/oauth.test.ts +240 -0
- package/extensions/google-gemini-cli-auth/oauth.ts +662 -0
- package/extensions/google-gemini-cli-auth/openclaw.plugin.json +9 -0
- package/extensions/google-gemini-cli-auth/package.json +14 -0
- package/extensions/googlechat/index.ts +19 -0
- package/extensions/googlechat/node_modules/.bin/openclaw +21 -0
- package/extensions/googlechat/openclaw.plugin.json +9 -0
- package/extensions/googlechat/package.json +39 -0
- package/extensions/googlechat/src/accounts.ts +147 -0
- package/extensions/googlechat/src/actions.ts +181 -0
- package/extensions/googlechat/src/api.test.ts +61 -0
- package/extensions/googlechat/src/api.ts +282 -0
- package/extensions/googlechat/src/auth.ts +123 -0
- package/extensions/googlechat/src/channel.ts +583 -0
- package/extensions/googlechat/src/monitor.test.ts +22 -0
- package/extensions/googlechat/src/monitor.ts +949 -0
- package/extensions/googlechat/src/onboarding.ts +269 -0
- package/extensions/googlechat/src/runtime.ts +14 -0
- package/extensions/googlechat/src/targets.test.ts +32 -0
- package/extensions/googlechat/src/targets.ts +65 -0
- package/extensions/googlechat/src/types.config.ts +3 -0
- package/extensions/googlechat/src/types.ts +73 -0
- package/extensions/imessage/index.ts +17 -0
- package/extensions/imessage/node_modules/.bin/openclaw +21 -0
- package/extensions/imessage/openclaw.plugin.json +9 -0
- package/extensions/imessage/package.json +14 -0
- package/extensions/imessage/src/channel.ts +294 -0
- package/extensions/imessage/src/runtime.ts +14 -0
- package/extensions/line/index.ts +19 -0
- package/extensions/line/node_modules/.bin/openclaw +21 -0
- package/extensions/line/openclaw.plugin.json +9 -0
- package/extensions/line/package.json +29 -0
- package/extensions/line/src/card-command.ts +344 -0
- package/extensions/line/src/channel.logout.test.ts +99 -0
- package/extensions/line/src/channel.sendPayload.test.ts +306 -0
- package/extensions/line/src/channel.ts +780 -0
- package/extensions/line/src/runtime.ts +14 -0
- package/extensions/llm-task/README.md +97 -0
- package/extensions/llm-task/index.ts +6 -0
- package/extensions/llm-task/node_modules/.bin/openclaw +21 -0
- package/extensions/llm-task/openclaw.plugin.json +21 -0
- package/extensions/llm-task/package.json +14 -0
- package/extensions/llm-task/src/llm-task-tool.test.ts +138 -0
- package/extensions/llm-task/src/llm-task-tool.ts +245 -0
- package/extensions/lobster/README.md +75 -0
- package/extensions/lobster/SKILL.md +97 -0
- package/extensions/lobster/index.ts +14 -0
- package/extensions/lobster/node_modules/.bin/openclaw +21 -0
- package/extensions/lobster/openclaw.plugin.json +10 -0
- package/extensions/lobster/package.json +14 -0
- package/extensions/lobster/src/lobster-tool.test.ts +247 -0
- package/extensions/lobster/src/lobster-tool.ts +328 -0
- package/extensions/matrix/CHANGELOG.md +87 -0
- package/extensions/matrix/index.ts +17 -0
- package/extensions/matrix/node_modules/.bin/markdown-it +21 -0
- package/extensions/matrix/node_modules/.bin/openclaw +21 -0
- package/extensions/matrix/openclaw.plugin.json +9 -0
- package/extensions/matrix/package.json +36 -0
- package/extensions/matrix/src/actions.ts +195 -0
- package/extensions/matrix/src/channel.directory.test.ts +64 -0
- package/extensions/matrix/src/channel.ts +439 -0
- package/extensions/matrix/src/config-schema.ts +62 -0
- package/extensions/matrix/src/directory-live.ts +188 -0
- package/extensions/matrix/src/group-mentions.ts +66 -0
- package/extensions/matrix/src/matrix/accounts.test.ts +82 -0
- package/extensions/matrix/src/matrix/accounts.ts +65 -0
- package/extensions/matrix/src/matrix/actions/client.ts +57 -0
- package/extensions/matrix/src/matrix/actions/messages.ts +128 -0
- package/extensions/matrix/src/matrix/actions/pins.ts +76 -0
- package/extensions/matrix/src/matrix/actions/reactions.ts +96 -0
- package/extensions/matrix/src/matrix/actions/room.ts +85 -0
- package/extensions/matrix/src/matrix/actions/summary.ts +75 -0
- package/extensions/matrix/src/matrix/actions/types.ts +84 -0
- package/extensions/matrix/src/matrix/actions.ts +15 -0
- package/extensions/matrix/src/matrix/active-client.ts +11 -0
- package/extensions/matrix/src/matrix/client/config.ts +160 -0
- package/extensions/matrix/src/matrix/client/create-client.ts +123 -0
- package/extensions/matrix/src/matrix/client/logging.ts +36 -0
- package/extensions/matrix/src/matrix/client/runtime.ts +4 -0
- package/extensions/matrix/src/matrix/client/shared.ts +170 -0
- package/extensions/matrix/src/matrix/client/storage.ts +131 -0
- package/extensions/matrix/src/matrix/client/types.ts +34 -0
- package/extensions/matrix/src/matrix/client.test.ts +56 -0
- package/extensions/matrix/src/matrix/client.ts +5 -0
- package/extensions/matrix/src/matrix/credentials.ts +105 -0
- package/extensions/matrix/src/matrix/deps.ts +60 -0
- package/extensions/matrix/src/matrix/format.test.ts +33 -0
- package/extensions/matrix/src/matrix/format.ts +22 -0
- package/extensions/matrix/src/matrix/index.ts +11 -0
- package/extensions/matrix/src/matrix/monitor/allowlist.test.ts +45 -0
- package/extensions/matrix/src/matrix/monitor/allowlist.ts +103 -0
- package/extensions/matrix/src/matrix/monitor/auto-join.ts +71 -0
- package/extensions/matrix/src/matrix/monitor/direct.ts +104 -0
- package/extensions/matrix/src/matrix/monitor/events.ts +101 -0
- package/extensions/matrix/src/matrix/monitor/handler.ts +661 -0
- package/extensions/matrix/src/matrix/monitor/index.ts +338 -0
- package/extensions/matrix/src/matrix/monitor/location.ts +100 -0
- package/extensions/matrix/src/matrix/monitor/media.test.ts +102 -0
- package/extensions/matrix/src/matrix/monitor/media.ts +113 -0
- package/extensions/matrix/src/matrix/monitor/mentions.ts +31 -0
- package/extensions/matrix/src/matrix/monitor/replies.ts +97 -0
- package/extensions/matrix/src/matrix/monitor/room-info.ts +55 -0
- package/extensions/matrix/src/matrix/monitor/rooms.test.ts +39 -0
- package/extensions/matrix/src/matrix/monitor/rooms.ts +47 -0
- package/extensions/matrix/src/matrix/monitor/threads.ts +68 -0
- package/extensions/matrix/src/matrix/monitor/types.ts +39 -0
- package/extensions/matrix/src/matrix/poll-types.test.ts +21 -0
- package/extensions/matrix/src/matrix/poll-types.ts +166 -0
- package/extensions/matrix/src/matrix/probe.ts +70 -0
- package/extensions/matrix/src/matrix/send/client.ts +66 -0
- package/extensions/matrix/src/matrix/send/formatting.ts +89 -0
- package/extensions/matrix/src/matrix/send/media.ts +229 -0
- package/extensions/matrix/src/matrix/send/targets.test.ts +98 -0
- package/extensions/matrix/src/matrix/send/targets.ts +136 -0
- package/extensions/matrix/src/matrix/send/types.ts +109 -0
- package/extensions/matrix/src/matrix/send.test.ts +171 -0
- package/extensions/matrix/src/matrix/send.ts +260 -0
- package/extensions/matrix/src/onboarding.ts +449 -0
- package/extensions/matrix/src/outbound.ts +52 -0
- package/extensions/matrix/src/resolve-targets.test.ts +48 -0
- package/extensions/matrix/src/resolve-targets.ts +135 -0
- package/extensions/matrix/src/runtime.ts +14 -0
- package/extensions/matrix/src/tool-actions.ts +164 -0
- package/extensions/matrix/src/types.ts +95 -0
- package/extensions/mattermost/index.ts +17 -0
- package/extensions/mattermost/node_modules/.bin/openclaw +21 -0
- package/extensions/mattermost/openclaw.plugin.json +9 -0
- package/extensions/mattermost/package.json +28 -0
- package/extensions/mattermost/src/channel.test.ts +48 -0
- package/extensions/mattermost/src/channel.ts +337 -0
- package/extensions/mattermost/src/config-schema.ts +55 -0
- package/extensions/mattermost/src/group-mentions.ts +15 -0
- package/extensions/mattermost/src/mattermost/accounts.ts +128 -0
- package/extensions/mattermost/src/mattermost/client.ts +220 -0
- package/extensions/mattermost/src/mattermost/index.ts +9 -0
- package/extensions/mattermost/src/mattermost/monitor-helpers.ts +166 -0
- package/extensions/mattermost/src/mattermost/monitor.ts +987 -0
- package/extensions/mattermost/src/mattermost/probe.ts +74 -0
- package/extensions/mattermost/src/mattermost/send.ts +231 -0
- package/extensions/mattermost/src/normalize.ts +46 -0
- package/extensions/mattermost/src/onboarding-helpers.ts +44 -0
- package/extensions/mattermost/src/onboarding.ts +186 -0
- package/extensions/mattermost/src/runtime.ts +14 -0
- package/extensions/mattermost/src/types.ts +50 -0
- package/extensions/memory-core/index.ts +38 -0
- package/extensions/memory-core/node_modules/.bin/openclaw +21 -0
- package/extensions/memory-core/openclaw.plugin.json +9 -0
- package/extensions/memory-core/package.json +17 -0
- package/extensions/memory-lancedb/config.ts +139 -0
- package/extensions/memory-lancedb/index.test.ts +295 -0
- package/extensions/memory-lancedb/index.ts +608 -0
- package/extensions/memory-lancedb/node_modules/.bin/openai +21 -0
- package/extensions/memory-lancedb/node_modules/.bin/openclaw +21 -0
- package/extensions/memory-lancedb/openclaw.plugin.json +60 -0
- package/extensions/memory-lancedb/package.json +19 -0
- package/extensions/minimax-portal-auth/README.md +33 -0
- package/extensions/minimax-portal-auth/index.ts +155 -0
- package/extensions/minimax-portal-auth/node_modules/.bin/openclaw +21 -0
- package/extensions/minimax-portal-auth/oauth.ts +247 -0
- package/extensions/minimax-portal-auth/openclaw.plugin.json +9 -0
- package/extensions/minimax-portal-auth/package.json +14 -0
- package/extensions/msteams/CHANGELOG.md +83 -0
- package/extensions/msteams/index.ts +17 -0
- package/extensions/msteams/node_modules/.bin/openclaw +21 -0
- package/extensions/msteams/openclaw.plugin.json +9 -0
- package/extensions/msteams/package.json +39 -0
- package/extensions/msteams/src/attachments/download.ts +283 -0
- package/extensions/msteams/src/attachments/graph.ts +353 -0
- package/extensions/msteams/src/attachments/html.ts +90 -0
- package/extensions/msteams/src/attachments/payload.ts +22 -0
- package/extensions/msteams/src/attachments/shared.ts +291 -0
- package/extensions/msteams/src/attachments/types.ts +37 -0
- package/extensions/msteams/src/attachments.test.ts +459 -0
- package/extensions/msteams/src/attachments.ts +18 -0
- package/extensions/msteams/src/channel.directory.test.ts +48 -0
- package/extensions/msteams/src/channel.ts +459 -0
- package/extensions/msteams/src/conversation-store-fs.test.ts +88 -0
- package/extensions/msteams/src/conversation-store-fs.ts +165 -0
- package/extensions/msteams/src/conversation-store-memory.ts +47 -0
- package/extensions/msteams/src/conversation-store.ts +41 -0
- package/extensions/msteams/src/directory-live.ts +205 -0
- package/extensions/msteams/src/errors.test.ts +45 -0
- package/extensions/msteams/src/errors.ts +190 -0
- package/extensions/msteams/src/file-consent-helpers.test.ts +243 -0
- package/extensions/msteams/src/file-consent-helpers.ts +73 -0
- package/extensions/msteams/src/file-consent.ts +126 -0
- package/extensions/msteams/src/graph-chat.ts +53 -0
- package/extensions/msteams/src/graph-upload.ts +453 -0
- package/extensions/msteams/src/inbound.test.ts +66 -0
- package/extensions/msteams/src/inbound.ts +48 -0
- package/extensions/msteams/src/index.ts +4 -0
- package/extensions/msteams/src/media-helpers.test.ts +189 -0
- package/extensions/msteams/src/media-helpers.ts +86 -0
- package/extensions/msteams/src/messenger.test.ts +248 -0
- package/extensions/msteams/src/messenger.ts +495 -0
- package/extensions/msteams/src/monitor-handler/inbound-media.ts +128 -0
- package/extensions/msteams/src/monitor-handler/message-handler.ts +640 -0
- package/extensions/msteams/src/monitor-handler.ts +162 -0
- package/extensions/msteams/src/monitor-types.ts +5 -0
- package/extensions/msteams/src/monitor.ts +295 -0
- package/extensions/msteams/src/onboarding.ts +431 -0
- package/extensions/msteams/src/outbound.ts +46 -0
- package/extensions/msteams/src/pending-uploads.ts +89 -0
- package/extensions/msteams/src/policy.test.ts +209 -0
- package/extensions/msteams/src/policy.ts +273 -0
- package/extensions/msteams/src/polls-store-memory.ts +32 -0
- package/extensions/msteams/src/polls-store.test.ts +38 -0
- package/extensions/msteams/src/polls.test.ts +72 -0
- package/extensions/msteams/src/polls.ts +315 -0
- package/extensions/msteams/src/probe.test.ts +58 -0
- package/extensions/msteams/src/probe.ts +107 -0
- package/extensions/msteams/src/reply-dispatcher.ts +130 -0
- package/extensions/msteams/src/resolve-allowlist.ts +297 -0
- package/extensions/msteams/src/runtime.ts +14 -0
- package/extensions/msteams/src/sdk-types.ts +19 -0
- package/extensions/msteams/src/sdk.ts +33 -0
- package/extensions/msteams/src/send-context.ts +164 -0
- package/extensions/msteams/src/send.ts +519 -0
- package/extensions/msteams/src/sent-message-cache.test.ts +15 -0
- package/extensions/msteams/src/sent-message-cache.ts +47 -0
- package/extensions/msteams/src/storage.ts +25 -0
- package/extensions/msteams/src/store-fs.ts +83 -0
- package/extensions/msteams/src/token.ts +19 -0
- package/extensions/nextcloud-talk/index.ts +17 -0
- package/extensions/nextcloud-talk/node_modules/.bin/openclaw +21 -0
- package/extensions/nextcloud-talk/openclaw.plugin.json +9 -0
- package/extensions/nextcloud-talk/package.json +33 -0
- package/extensions/nextcloud-talk/src/accounts.ts +174 -0
- package/extensions/nextcloud-talk/src/channel.ts +409 -0
- package/extensions/nextcloud-talk/src/config-schema.ts +78 -0
- package/extensions/nextcloud-talk/src/format.ts +79 -0
- package/extensions/nextcloud-talk/src/inbound.ts +317 -0
- package/extensions/nextcloud-talk/src/monitor.ts +246 -0
- package/extensions/nextcloud-talk/src/normalize.ts +39 -0
- package/extensions/nextcloud-talk/src/onboarding.ts +343 -0
- package/extensions/nextcloud-talk/src/policy.test.ts +33 -0
- package/extensions/nextcloud-talk/src/policy.ts +180 -0
- package/extensions/nextcloud-talk/src/room-info.ts +125 -0
- package/extensions/nextcloud-talk/src/runtime.ts +14 -0
- package/extensions/nextcloud-talk/src/send.ts +210 -0
- package/extensions/nextcloud-talk/src/signature.ts +72 -0
- package/extensions/nextcloud-talk/src/types.ts +179 -0
- package/extensions/nostr/CHANGELOG.md +74 -0
- package/extensions/nostr/README.md +136 -0
- package/extensions/nostr/index.ts +68 -0
- package/extensions/nostr/node_modules/.bin/openclaw +21 -0
- package/extensions/nostr/openclaw.plugin.json +9 -0
- package/extensions/nostr/package.json +34 -0
- package/extensions/nostr/src/channel.test.ts +151 -0
- package/extensions/nostr/src/channel.ts +353 -0
- package/extensions/nostr/src/config-schema.ts +90 -0
- package/extensions/nostr/src/metrics.ts +478 -0
- package/extensions/nostr/src/nostr-bus.fuzz.test.ts +533 -0
- package/extensions/nostr/src/nostr-bus.integration.test.ts +448 -0
- package/extensions/nostr/src/nostr-bus.test.ts +199 -0
- package/extensions/nostr/src/nostr-bus.ts +715 -0
- package/extensions/nostr/src/nostr-profile-http.test.ts +378 -0
- package/extensions/nostr/src/nostr-profile-http.ts +519 -0
- package/extensions/nostr/src/nostr-profile-import.test.ts +119 -0
- package/extensions/nostr/src/nostr-profile-import.ts +262 -0
- package/extensions/nostr/src/nostr-profile.fuzz.test.ts +477 -0
- package/extensions/nostr/src/nostr-profile.test.ts +410 -0
- package/extensions/nostr/src/nostr-profile.ts +277 -0
- package/extensions/nostr/src/nostr-state-store.test.ts +131 -0
- package/extensions/nostr/src/nostr-state-store.ts +226 -0
- package/extensions/nostr/src/runtime.ts +14 -0
- package/extensions/nostr/src/seen-tracker.ts +303 -0
- package/extensions/nostr/src/types.test.ts +157 -0
- package/extensions/nostr/src/types.ts +101 -0
- package/extensions/nostr/test/setup.ts +5 -0
- package/extensions/open-prose/README.md +25 -0
- package/extensions/open-prose/index.ts +5 -0
- package/extensions/open-prose/node_modules/.bin/openclaw +21 -0
- package/extensions/open-prose/openclaw.plugin.json +11 -0
- package/extensions/open-prose/package.json +14 -0
- package/extensions/open-prose/skills/prose/LICENSE +21 -0
- package/extensions/open-prose/skills/prose/SKILL.md +323 -0
- package/extensions/open-prose/skills/prose/alt-borges.md +141 -0
- package/extensions/open-prose/skills/prose/alts/arabian-nights.md +358 -0
- package/extensions/open-prose/skills/prose/alts/borges.md +360 -0
- package/extensions/open-prose/skills/prose/alts/folk.md +322 -0
- package/extensions/open-prose/skills/prose/alts/homer.md +346 -0
- package/extensions/open-prose/skills/prose/alts/kafka.md +373 -0
- package/extensions/open-prose/skills/prose/compiler.md +2971 -0
- package/extensions/open-prose/skills/prose/examples/01-hello-world.prose +4 -0
- package/extensions/open-prose/skills/prose/examples/02-research-and-summarize.prose +6 -0
- package/extensions/open-prose/skills/prose/examples/03-code-review.prose +17 -0
- package/extensions/open-prose/skills/prose/examples/04-write-and-refine.prose +14 -0
- package/extensions/open-prose/skills/prose/examples/05-debug-issue.prose +20 -0
- package/extensions/open-prose/skills/prose/examples/06-explain-codebase.prose +17 -0
- package/extensions/open-prose/skills/prose/examples/07-refactor.prose +20 -0
- package/extensions/open-prose/skills/prose/examples/08-blog-post.prose +20 -0
- package/extensions/open-prose/skills/prose/examples/09-research-with-agents.prose +25 -0
- package/extensions/open-prose/skills/prose/examples/10-code-review-agents.prose +32 -0
- package/extensions/open-prose/skills/prose/examples/11-skills-and-imports.prose +27 -0
- package/extensions/open-prose/skills/prose/examples/12-secure-agent-permissions.prose +43 -0
- package/extensions/open-prose/skills/prose/examples/13-variables-and-context.prose +51 -0
- package/extensions/open-prose/skills/prose/examples/14-composition-blocks.prose +48 -0
- package/extensions/open-prose/skills/prose/examples/15-inline-sequences.prose +23 -0
- package/extensions/open-prose/skills/prose/examples/16-parallel-reviews.prose +19 -0
- package/extensions/open-prose/skills/prose/examples/17-parallel-research.prose +19 -0
- package/extensions/open-prose/skills/prose/examples/18-mixed-parallel-sequential.prose +36 -0
- package/extensions/open-prose/skills/prose/examples/19-advanced-parallel.prose +71 -0
- package/extensions/open-prose/skills/prose/examples/20-fixed-loops.prose +20 -0
- package/extensions/open-prose/skills/prose/examples/21-pipeline-operations.prose +35 -0
- package/extensions/open-prose/skills/prose/examples/22-error-handling.prose +51 -0
- package/extensions/open-prose/skills/prose/examples/23-retry-with-backoff.prose +63 -0
- package/extensions/open-prose/skills/prose/examples/24-choice-blocks.prose +86 -0
- package/extensions/open-prose/skills/prose/examples/25-conditionals.prose +114 -0
- package/extensions/open-prose/skills/prose/examples/26-parameterized-blocks.prose +100 -0
- package/extensions/open-prose/skills/prose/examples/27-string-interpolation.prose +105 -0
- package/extensions/open-prose/skills/prose/examples/28-automated-pr-review.prose +37 -0
- package/extensions/open-prose/skills/prose/examples/28-gas-town.prose +1572 -0
- package/extensions/open-prose/skills/prose/examples/29-captains-chair.prose +218 -0
- package/extensions/open-prose/skills/prose/examples/30-captains-chair-simple.prose +42 -0
- package/extensions/open-prose/skills/prose/examples/31-captains-chair-with-memory.prose +145 -0
- package/extensions/open-prose/skills/prose/examples/33-pr-review-autofix.prose +168 -0
- package/extensions/open-prose/skills/prose/examples/34-content-pipeline.prose +204 -0
- package/extensions/open-prose/skills/prose/examples/35-feature-factory.prose +296 -0
- package/extensions/open-prose/skills/prose/examples/36-bug-hunter.prose +237 -0
- package/extensions/open-prose/skills/prose/examples/37-the-forge.prose +1474 -0
- package/extensions/open-prose/skills/prose/examples/38-skill-scan.prose +455 -0
- package/extensions/open-prose/skills/prose/examples/39-architect-by-simulation.prose +277 -0
- package/extensions/open-prose/skills/prose/examples/40-rlm-self-refine.prose +32 -0
- package/extensions/open-prose/skills/prose/examples/41-rlm-divide-conquer.prose +38 -0
- package/extensions/open-prose/skills/prose/examples/42-rlm-filter-recurse.prose +46 -0
- package/extensions/open-prose/skills/prose/examples/43-rlm-pairwise.prose +50 -0
- package/extensions/open-prose/skills/prose/examples/44-run-endpoint-ux-test.prose +261 -0
- package/extensions/open-prose/skills/prose/examples/45-plugin-release.prose +159 -0
- package/extensions/open-prose/skills/prose/examples/45-run-endpoint-ux-test-with-remediation.prose +637 -0
- package/extensions/open-prose/skills/prose/examples/46-run-endpoint-ux-test-fast.prose +148 -0
- package/extensions/open-prose/skills/prose/examples/46-workflow-crystallizer.prose +225 -0
- package/extensions/open-prose/skills/prose/examples/47-language-self-improvement.prose +356 -0
- package/extensions/open-prose/skills/prose/examples/48-habit-miner.prose +445 -0
- package/extensions/open-prose/skills/prose/examples/49-prose-run-retrospective.prose +210 -0
- package/extensions/open-prose/skills/prose/examples/README.md +391 -0
- package/extensions/open-prose/skills/prose/examples/roadmap/README.md +22 -0
- package/extensions/open-prose/skills/prose/examples/roadmap/iterative-refinement.prose +20 -0
- package/extensions/open-prose/skills/prose/examples/roadmap/parallel-review.prose +18 -0
- package/extensions/open-prose/skills/prose/examples/roadmap/simple-pipeline.prose +17 -0
- package/extensions/open-prose/skills/prose/examples/roadmap/syntax/open-prose-syntax.prose +223 -0
- package/extensions/open-prose/skills/prose/guidance/antipatterns.md +951 -0
- package/extensions/open-prose/skills/prose/guidance/patterns.md +700 -0
- package/extensions/open-prose/skills/prose/guidance/system-prompt.md +180 -0
- package/extensions/open-prose/skills/prose/help.md +144 -0
- package/extensions/open-prose/skills/prose/lib/README.md +108 -0
- package/extensions/open-prose/skills/prose/lib/calibrator.prose +215 -0
- package/extensions/open-prose/skills/prose/lib/cost-analyzer.prose +174 -0
- package/extensions/open-prose/skills/prose/lib/error-forensics.prose +250 -0
- package/extensions/open-prose/skills/prose/lib/inspector.prose +196 -0
- package/extensions/open-prose/skills/prose/lib/profiler.prose +460 -0
- package/extensions/open-prose/skills/prose/lib/program-improver.prose +275 -0
- package/extensions/open-prose/skills/prose/lib/project-memory.prose +118 -0
- package/extensions/open-prose/skills/prose/lib/user-memory.prose +93 -0
- package/extensions/open-prose/skills/prose/lib/vm-improver.prose +243 -0
- package/extensions/open-prose/skills/prose/primitives/session.md +593 -0
- package/extensions/open-prose/skills/prose/prose.md +1237 -0
- package/extensions/open-prose/skills/prose/state/filesystem.md +498 -0
- package/extensions/open-prose/skills/prose/state/in-context.md +384 -0
- package/extensions/open-prose/skills/prose/state/postgres.md +880 -0
- package/extensions/open-prose/skills/prose/state/sqlite.md +574 -0
- package/extensions/qwen-portal-auth/README.md +24 -0
- package/extensions/qwen-portal-auth/index.ts +130 -0
- package/extensions/qwen-portal-auth/oauth.ts +190 -0
- package/extensions/qwen-portal-auth/openclaw.plugin.json +9 -0
- package/extensions/signal/index.ts +17 -0
- package/extensions/signal/node_modules/.bin/openclaw +21 -0
- package/extensions/signal/openclaw.plugin.json +9 -0
- package/extensions/signal/package.json +14 -0
- package/extensions/signal/src/channel.ts +315 -0
- package/extensions/signal/src/runtime.ts +14 -0
- package/extensions/slack/index.ts +17 -0
- package/extensions/slack/node_modules/.bin/openclaw +21 -0
- package/extensions/slack/openclaw.plugin.json +9 -0
- package/extensions/slack/package.json +14 -0
- package/extensions/slack/src/channel.ts +604 -0
- package/extensions/slack/src/runtime.ts +14 -0
- package/extensions/telegram/index.ts +17 -0
- package/extensions/telegram/node_modules/.bin/openclaw +21 -0
- package/extensions/telegram/openclaw.plugin.json +9 -0
- package/extensions/telegram/package.json +14 -0
- package/extensions/telegram/src/channel.ts +482 -0
- package/extensions/telegram/src/runtime.ts +14 -0
- package/extensions/tlon/README.md +5 -0
- package/extensions/tlon/index.ts +17 -0
- package/extensions/tlon/node_modules/.bin/openclaw +21 -0
- package/extensions/tlon/openclaw.plugin.json +9 -0
- package/extensions/tlon/package.json +33 -0
- package/extensions/tlon/src/channel.ts +392 -0
- package/extensions/tlon/src/config-schema.test.ts +31 -0
- package/extensions/tlon/src/config-schema.ts +43 -0
- package/extensions/tlon/src/monitor/discovery.ts +76 -0
- package/extensions/tlon/src/monitor/history.ts +90 -0
- package/extensions/tlon/src/monitor/index.ts +553 -0
- package/extensions/tlon/src/monitor/processed-messages.test.ts +23 -0
- package/extensions/tlon/src/monitor/processed-messages.ts +46 -0
- package/extensions/tlon/src/monitor/utils.ts +105 -0
- package/extensions/tlon/src/onboarding.ts +214 -0
- package/extensions/tlon/src/runtime.ts +14 -0
- package/extensions/tlon/src/targets.ts +89 -0
- package/extensions/tlon/src/types.ts +92 -0
- package/extensions/tlon/src/urbit/auth.ts +18 -0
- package/extensions/tlon/src/urbit/http-api.ts +38 -0
- package/extensions/tlon/src/urbit/send.test.ts +38 -0
- package/extensions/tlon/src/urbit/send.ts +131 -0
- package/extensions/tlon/src/urbit/sse-client.test.ts +40 -0
- package/extensions/tlon/src/urbit/sse-client.ts +395 -0
- package/extensions/twitch/CHANGELOG.md +45 -0
- package/extensions/twitch/README.md +89 -0
- package/extensions/twitch/index.ts +20 -0
- package/extensions/twitch/node_modules/.bin/openclaw +21 -0
- package/extensions/twitch/openclaw.plugin.json +9 -0
- package/extensions/twitch/package.json +20 -0
- package/extensions/twitch/src/access-control.test.ts +489 -0
- package/extensions/twitch/src/access-control.ts +166 -0
- package/extensions/twitch/src/actions.ts +173 -0
- package/extensions/twitch/src/client-manager-registry.ts +115 -0
- package/extensions/twitch/src/config-schema.ts +82 -0
- package/extensions/twitch/src/config.test.ts +87 -0
- package/extensions/twitch/src/config.ts +116 -0
- package/extensions/twitch/src/monitor.ts +261 -0
- package/extensions/twitch/src/onboarding.test.ts +311 -0
- package/extensions/twitch/src/onboarding.ts +417 -0
- package/extensions/twitch/src/outbound.test.ts +373 -0
- package/extensions/twitch/src/outbound.ts +184 -0
- package/extensions/twitch/src/plugin.test.ts +39 -0
- package/extensions/twitch/src/plugin.ts +274 -0
- package/extensions/twitch/src/probe.test.ts +195 -0
- package/extensions/twitch/src/probe.ts +120 -0
- package/extensions/twitch/src/resolver.ts +137 -0
- package/extensions/twitch/src/runtime.ts +14 -0
- package/extensions/twitch/src/send.test.ts +289 -0
- package/extensions/twitch/src/send.ts +136 -0
- package/extensions/twitch/src/status.test.ts +270 -0
- package/extensions/twitch/src/status.ts +178 -0
- package/extensions/twitch/src/token.test.ts +171 -0
- package/extensions/twitch/src/token.ts +91 -0
- package/extensions/twitch/src/twitch-client.test.ts +589 -0
- package/extensions/twitch/src/twitch-client.ts +277 -0
- package/extensions/twitch/src/types.ts +141 -0
- package/extensions/twitch/src/utils/markdown.ts +98 -0
- package/extensions/twitch/src/utils/twitch.ts +78 -0
- package/extensions/twitch/test/setup.ts +7 -0
- package/extensions/voice-call/CHANGELOG.md +109 -0
- package/extensions/voice-call/README.md +139 -0
- package/extensions/voice-call/index.ts +493 -0
- package/extensions/voice-call/node_modules/.bin/openclaw +21 -0
- package/extensions/voice-call/openclaw.plugin.json +559 -0
- package/extensions/voice-call/package.json +19 -0
- package/extensions/voice-call/src/allowlist.ts +19 -0
- package/extensions/voice-call/src/cli.ts +279 -0
- package/extensions/voice-call/src/config.test.ts +234 -0
- package/extensions/voice-call/src/config.ts +523 -0
- package/extensions/voice-call/src/core-bridge.ts +159 -0
- package/extensions/voice-call/src/manager/context.ts +21 -0
- package/extensions/voice-call/src/manager/events.ts +188 -0
- package/extensions/voice-call/src/manager/lookup.ts +35 -0
- package/extensions/voice-call/src/manager/outbound.ts +275 -0
- package/extensions/voice-call/src/manager/state.ts +48 -0
- package/extensions/voice-call/src/manager/store.ts +91 -0
- package/extensions/voice-call/src/manager/timers.ts +89 -0
- package/extensions/voice-call/src/manager/twiml.ts +9 -0
- package/extensions/voice-call/src/manager.test.ts +194 -0
- package/extensions/voice-call/src/manager.ts +887 -0
- package/extensions/voice-call/src/media-stream.test.ts +96 -0
- package/extensions/voice-call/src/media-stream.ts +411 -0
- package/extensions/voice-call/src/providers/base.ts +67 -0
- package/extensions/voice-call/src/providers/index.ts +10 -0
- package/extensions/voice-call/src/providers/mock.ts +165 -0
- package/extensions/voice-call/src/providers/plivo.test.ts +27 -0
- package/extensions/voice-call/src/providers/plivo.ts +515 -0
- package/extensions/voice-call/src/providers/stt-openai-realtime.ts +311 -0
- package/extensions/voice-call/src/providers/telnyx.ts +371 -0
- package/extensions/voice-call/src/providers/tts-openai.ts +259 -0
- package/extensions/voice-call/src/providers/twilio/api.ts +42 -0
- package/extensions/voice-call/src/providers/twilio/webhook.ts +32 -0
- package/extensions/voice-call/src/providers/twilio.test.ts +60 -0
- package/extensions/voice-call/src/providers/twilio.ts +626 -0
- package/extensions/voice-call/src/response-generator.ts +158 -0
- package/extensions/voice-call/src/runtime.ts +212 -0
- package/extensions/voice-call/src/telephony-audio.ts +90 -0
- package/extensions/voice-call/src/telephony-tts.ts +104 -0
- package/extensions/voice-call/src/tunnel.ts +314 -0
- package/extensions/voice-call/src/types.ts +272 -0
- package/extensions/voice-call/src/utils.ts +14 -0
- package/extensions/voice-call/src/voice-mapping.ts +67 -0
- package/extensions/voice-call/src/webhook-security.test.ts +377 -0
- package/extensions/voice-call/src/webhook-security.ts +689 -0
- package/extensions/voice-call/src/webhook.ts +491 -0
- package/extensions/whatsapp/index.ts +17 -0
- package/extensions/whatsapp/node_modules/.bin/openclaw +21 -0
- package/extensions/whatsapp/openclaw.plugin.json +9 -0
- package/extensions/whatsapp/package.json +14 -0
- package/extensions/whatsapp/src/channel.ts +508 -0
- package/extensions/whatsapp/src/runtime.ts +14 -0
- package/extensions/zalo/CHANGELOG.md +89 -0
- package/extensions/zalo/README.md +50 -0
- package/extensions/zalo/index.ts +19 -0
- package/extensions/zalo/node_modules/.bin/openclaw +21 -0
- package/extensions/zalo/openclaw.plugin.json +9 -0
- package/extensions/zalo/package.json +36 -0
- package/extensions/zalo/src/accounts.ts +80 -0
- package/extensions/zalo/src/actions.ts +67 -0
- package/extensions/zalo/src/api.ts +208 -0
- package/extensions/zalo/src/channel.directory.test.ts +43 -0
- package/extensions/zalo/src/channel.ts +414 -0
- package/extensions/zalo/src/config-schema.ts +24 -0
- package/extensions/zalo/src/monitor.ts +753 -0
- package/extensions/zalo/src/monitor.webhook.test.ts +73 -0
- package/extensions/zalo/src/onboarding.ts +401 -0
- package/extensions/zalo/src/probe.ts +46 -0
- package/extensions/zalo/src/proxy.ts +21 -0
- package/extensions/zalo/src/runtime.ts +14 -0
- package/extensions/zalo/src/send.ts +124 -0
- package/extensions/zalo/src/status-issues.ts +53 -0
- package/extensions/zalo/src/token.ts +63 -0
- package/extensions/zalo/src/types.ts +42 -0
- package/extensions/zalouser/CHANGELOG.md +61 -0
- package/extensions/zalouser/README.md +225 -0
- package/extensions/zalouser/index.ts +31 -0
- package/extensions/zalouser/node_modules/.bin/openclaw +21 -0
- package/extensions/zalouser/openclaw.plugin.json +9 -0
- package/extensions/zalouser/package.json +36 -0
- package/extensions/zalouser/src/accounts.ts +135 -0
- package/extensions/zalouser/src/channel.test.ts +18 -0
- package/extensions/zalouser/src/channel.ts +686 -0
- package/extensions/zalouser/src/config-schema.ts +27 -0
- package/extensions/zalouser/src/monitor.ts +590 -0
- package/extensions/zalouser/src/onboarding.ts +504 -0
- package/extensions/zalouser/src/probe.ts +28 -0
- package/extensions/zalouser/src/runtime.ts +14 -0
- package/extensions/zalouser/src/send.ts +160 -0
- package/extensions/zalouser/src/status-issues.test.ts +57 -0
- package/extensions/zalouser/src/status-issues.ts +89 -0
- package/extensions/zalouser/src/tool.ts +164 -0
- package/extensions/zalouser/src/types.ts +108 -0
- package/extensions/zalouser/src/zca.ts +202 -0
- package/openclaw.mjs +14 -0
- package/package.json +245 -0
- package/skills/1password/SKILL.md +70 -0
- package/skills/1password/references/cli-examples.md +29 -0
- package/skills/1password/references/get-started.md +17 -0
- package/skills/apple-notes/SKILL.md +77 -0
- package/skills/apple-reminders/SKILL.md +96 -0
- package/skills/bear-notes/SKILL.md +107 -0
- package/skills/bird/SKILL.md +224 -0
- package/skills/blogwatcher/SKILL.md +69 -0
- package/skills/blucli/SKILL.md +47 -0
- package/skills/bluebubbles/SKILL.md +131 -0
- package/skills/camsnap/SKILL.md +45 -0
- package/skills/canvas/SKILL.md +198 -0
- package/skills/clawhub/SKILL.md +77 -0
- package/skills/coding-agent/SKILL.md +284 -0
- package/skills/discord/SKILL.md +578 -0
- package/skills/eightctl/SKILL.md +50 -0
- package/skills/food-order/SKILL.md +48 -0
- package/skills/gemini/SKILL.md +43 -0
- package/skills/ghostly-projects/SKILL.md +160 -0
- package/skills/gifgrep/SKILL.md +79 -0
- package/skills/github/SKILL.md +77 -0
- package/skills/gog/SKILL.md +116 -0
- package/skills/goplaces/SKILL.md +52 -0
- package/skills/healthcheck/SKILL.md +245 -0
- package/skills/himalaya/SKILL.md +257 -0
- package/skills/himalaya/references/configuration.md +184 -0
- package/skills/himalaya/references/message-composition.md +199 -0
- package/skills/imsg/SKILL.md +74 -0
- package/skills/linear/SKILL.md +111 -0
- package/skills/linear/linear.sh +204 -0
- package/skills/local-places/SERVER_README.md +101 -0
- package/skills/local-places/SKILL.md +102 -0
- package/skills/local-places/pyproject.toml +21 -0
- package/skills/local-places/src/local_places/__init__.py +2 -0
- package/skills/local-places/src/local_places/google_places.py +314 -0
- package/skills/local-places/src/local_places/main.py +65 -0
- package/skills/local-places/src/local_places/schemas.py +107 -0
- package/skills/mcporter/SKILL.md +61 -0
- package/skills/model-usage/SKILL.md +69 -0
- package/skills/model-usage/references/codexbar-cli.md +33 -0
- package/skills/model-usage/scripts/model_usage.py +310 -0
- package/skills/nano-banana-pro/SKILL.md +58 -0
- package/skills/nano-banana-pro/scripts/generate_image.py +184 -0
- package/skills/nano-pdf/SKILL.md +38 -0
- package/skills/notion/SKILL.md +172 -0
- package/skills/obsidian/SKILL.md +81 -0
- package/skills/openai-image-gen/SKILL.md +89 -0
- package/skills/openai-image-gen/scripts/gen.py +240 -0
- package/skills/openai-whisper/SKILL.md +38 -0
- package/skills/openai-whisper-api/SKILL.md +52 -0
- package/skills/openai-whisper-api/scripts/transcribe.sh +85 -0
- package/skills/openhue/SKILL.md +51 -0
- package/skills/oracle/SKILL.md +125 -0
- package/skills/ordercli/SKILL.md +78 -0
- package/skills/peekaboo/SKILL.md +190 -0
- package/skills/sag/SKILL.md +87 -0
- package/skills/session-logs/SKILL.md +115 -0
- package/skills/sherpa-onnx-tts/SKILL.md +103 -0
- package/skills/sherpa-onnx-tts/bin/sherpa-onnx-tts +178 -0
- package/skills/skill-creator/SKILL.md +370 -0
- package/skills/skill-creator/license.txt +202 -0
- package/skills/skill-creator/scripts/init_skill.py +378 -0
- package/skills/skill-creator/scripts/package_skill.py +111 -0
- package/skills/skill-creator/scripts/quick_validate.py +101 -0
- package/skills/slack/SKILL.md +144 -0
- package/skills/songsee/SKILL.md +49 -0
- package/skills/sonoscli/SKILL.md +46 -0
- package/skills/spotify-player/SKILL.md +64 -0
- package/skills/summarize/SKILL.md +87 -0
- package/skills/things-mac/SKILL.md +86 -0
- package/skills/tmux/SKILL.md +135 -0
- package/skills/tmux/scripts/find-sessions.sh +112 -0
- package/skills/tmux/scripts/wait-for-text.sh +83 -0
- package/skills/trello/SKILL.md +95 -0
- package/skills/video-frames/SKILL.md +46 -0
- package/skills/video-frames/scripts/frame.sh +81 -0
- package/skills/voice-call/SKILL.md +45 -0
- package/skills/wacli/SKILL.md +72 -0
- package/skills/weather/SKILL.md +54 -0
|
@@ -0,0 +1,2587 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-Cbj13DAv.js";
|
|
2
|
+
import { i as isMessagingToolDuplicate } from "./pi-embedded-helpers-Dl8e5Rf8.js";
|
|
3
|
+
import { M as normalizeAccountId$1 } from "./agent-scope-BbT4OG2N.js";
|
|
4
|
+
import { A as logVerbose, C as getActivePluginRegistry, N as shouldLogVerbose } from "./subsystem-CAq3uyo7.js";
|
|
5
|
+
import { h as resolveUserPath } from "./utils-CKSrBNwq.js";
|
|
6
|
+
import { i as loadConfig } from "./config-BtSTwPcH.js";
|
|
7
|
+
import { r as normalizeChannelId, t as getChannelPlugin } from "./plugins-QJjTXliB.js";
|
|
8
|
+
import { V as getChannelDock, l as appendAssistantMessageToSessionTranscript, q as resolveSignalAccount, u as resolveMirroredTranscriptText } from "./sandbox-knontqD9.js";
|
|
9
|
+
import { C as resolvePinnedHostnameWithPolicy, S as resolvePinnedHostname, _ as maxBytesForKind, a as optimizeImageToPng, b as closeDispatcher, c as saveMediaBuffer, i as hasAlphaChannel, l as detectMime, n as convertHeicToJpeg, o as resizeToJpeg, u as extensionForMime, v as mediaKindFromMime, x as createPinnedDispatcher } from "./routes-ClNyEvlm.js";
|
|
10
|
+
import { t as INTERNAL_MESSAGE_CHANNEL } from "./message-channel-D6v_oPAg.js";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
import fs from "node:fs/promises";
|
|
14
|
+
import { randomUUID } from "node:crypto";
|
|
15
|
+
import MarkdownIt from "markdown-it";
|
|
16
|
+
|
|
17
|
+
//#region src/infra/net/fetch-guard.ts
|
|
18
|
+
const DEFAULT_MAX_REDIRECTS = 3;
|
|
19
|
+
function isRedirectStatus(status) {
|
|
20
|
+
return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
|
|
21
|
+
}
|
|
22
|
+
function buildAbortSignal(params) {
|
|
23
|
+
const { timeoutMs, signal } = params;
|
|
24
|
+
if (!timeoutMs && !signal) return {
|
|
25
|
+
signal: void 0,
|
|
26
|
+
cleanup: () => {}
|
|
27
|
+
};
|
|
28
|
+
if (!timeoutMs) return {
|
|
29
|
+
signal,
|
|
30
|
+
cleanup: () => {}
|
|
31
|
+
};
|
|
32
|
+
const controller = new AbortController();
|
|
33
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
34
|
+
const onAbort = () => controller.abort();
|
|
35
|
+
if (signal) if (signal.aborted) controller.abort();
|
|
36
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
37
|
+
const cleanup = () => {
|
|
38
|
+
clearTimeout(timeoutId);
|
|
39
|
+
if (signal) signal.removeEventListener("abort", onAbort);
|
|
40
|
+
};
|
|
41
|
+
return {
|
|
42
|
+
signal: controller.signal,
|
|
43
|
+
cleanup
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
async function fetchWithSsrFGuard(params) {
|
|
47
|
+
const fetcher = params.fetchImpl ?? globalThis.fetch;
|
|
48
|
+
if (!fetcher) throw new Error("fetch is not available");
|
|
49
|
+
const maxRedirects = typeof params.maxRedirects === "number" && Number.isFinite(params.maxRedirects) ? Math.max(0, Math.floor(params.maxRedirects)) : DEFAULT_MAX_REDIRECTS;
|
|
50
|
+
const { signal, cleanup } = buildAbortSignal({
|
|
51
|
+
timeoutMs: params.timeoutMs,
|
|
52
|
+
signal: params.signal
|
|
53
|
+
});
|
|
54
|
+
let released = false;
|
|
55
|
+
const release = async (dispatcher) => {
|
|
56
|
+
if (released) return;
|
|
57
|
+
released = true;
|
|
58
|
+
cleanup();
|
|
59
|
+
await closeDispatcher(dispatcher ?? void 0);
|
|
60
|
+
};
|
|
61
|
+
const visited = /* @__PURE__ */ new Set();
|
|
62
|
+
let currentUrl = params.url;
|
|
63
|
+
let redirectCount = 0;
|
|
64
|
+
while (true) {
|
|
65
|
+
let parsedUrl;
|
|
66
|
+
try {
|
|
67
|
+
parsedUrl = new URL(currentUrl);
|
|
68
|
+
} catch {
|
|
69
|
+
await release();
|
|
70
|
+
throw new Error("Invalid URL: must be http or https");
|
|
71
|
+
}
|
|
72
|
+
if (!["http:", "https:"].includes(parsedUrl.protocol)) {
|
|
73
|
+
await release();
|
|
74
|
+
throw new Error("Invalid URL: must be http or https");
|
|
75
|
+
}
|
|
76
|
+
let dispatcher = null;
|
|
77
|
+
try {
|
|
78
|
+
const pinned = Boolean(params.policy?.allowPrivateNetwork || params.policy?.allowedHostnames?.length) ? await resolvePinnedHostnameWithPolicy(parsedUrl.hostname, {
|
|
79
|
+
lookupFn: params.lookupFn,
|
|
80
|
+
policy: params.policy
|
|
81
|
+
}) : await resolvePinnedHostname(parsedUrl.hostname, params.lookupFn);
|
|
82
|
+
if (params.pinDns !== false) dispatcher = createPinnedDispatcher(pinned);
|
|
83
|
+
const init = {
|
|
84
|
+
...params.init ? { ...params.init } : {},
|
|
85
|
+
redirect: "manual",
|
|
86
|
+
...dispatcher ? { dispatcher } : {},
|
|
87
|
+
...signal ? { signal } : {}
|
|
88
|
+
};
|
|
89
|
+
const response = await fetcher(parsedUrl.toString(), init);
|
|
90
|
+
if (isRedirectStatus(response.status)) {
|
|
91
|
+
const location = response.headers.get("location");
|
|
92
|
+
if (!location) {
|
|
93
|
+
await release(dispatcher);
|
|
94
|
+
throw new Error(`Redirect missing location header (${response.status})`);
|
|
95
|
+
}
|
|
96
|
+
redirectCount += 1;
|
|
97
|
+
if (redirectCount > maxRedirects) {
|
|
98
|
+
await release(dispatcher);
|
|
99
|
+
throw new Error(`Too many redirects (limit: ${maxRedirects})`);
|
|
100
|
+
}
|
|
101
|
+
const nextUrl = new URL(location, parsedUrl).toString();
|
|
102
|
+
if (visited.has(nextUrl)) {
|
|
103
|
+
await release(dispatcher);
|
|
104
|
+
throw new Error("Redirect loop detected");
|
|
105
|
+
}
|
|
106
|
+
visited.add(nextUrl);
|
|
107
|
+
response.body?.cancel();
|
|
108
|
+
await closeDispatcher(dispatcher);
|
|
109
|
+
currentUrl = nextUrl;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
response,
|
|
114
|
+
finalUrl: currentUrl,
|
|
115
|
+
release: async () => release(dispatcher)
|
|
116
|
+
};
|
|
117
|
+
} catch (err) {
|
|
118
|
+
await release(dispatcher);
|
|
119
|
+
throw err;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/media/fetch.ts
|
|
126
|
+
var MediaFetchError = class extends Error {
|
|
127
|
+
constructor(code, message) {
|
|
128
|
+
super(message);
|
|
129
|
+
this.code = code;
|
|
130
|
+
this.name = "MediaFetchError";
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
function stripQuotes(value) {
|
|
134
|
+
return value.replace(/^["']|["']$/g, "");
|
|
135
|
+
}
|
|
136
|
+
function parseContentDispositionFileName(header) {
|
|
137
|
+
if (!header) return;
|
|
138
|
+
const starMatch = /filename\*\s*=\s*([^;]+)/i.exec(header);
|
|
139
|
+
if (starMatch?.[1]) {
|
|
140
|
+
const cleaned = stripQuotes(starMatch[1].trim());
|
|
141
|
+
const encoded = cleaned.split("''").slice(1).join("''") || cleaned;
|
|
142
|
+
try {
|
|
143
|
+
return path.basename(decodeURIComponent(encoded));
|
|
144
|
+
} catch {
|
|
145
|
+
return path.basename(encoded);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const match = /filename\s*=\s*([^;]+)/i.exec(header);
|
|
149
|
+
if (match?.[1]) return path.basename(stripQuotes(match[1].trim()));
|
|
150
|
+
}
|
|
151
|
+
async function readErrorBodySnippet(res, maxChars = 200) {
|
|
152
|
+
try {
|
|
153
|
+
const text = await res.text();
|
|
154
|
+
if (!text) return;
|
|
155
|
+
const collapsed = text.replace(/\s+/g, " ").trim();
|
|
156
|
+
if (!collapsed) return;
|
|
157
|
+
if (collapsed.length <= maxChars) return collapsed;
|
|
158
|
+
return `${collapsed.slice(0, maxChars)}…`;
|
|
159
|
+
} catch {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
async function fetchRemoteMedia(options) {
|
|
164
|
+
const { url, fetchImpl, filePathHint, maxBytes, maxRedirects, ssrfPolicy, lookupFn } = options;
|
|
165
|
+
let res;
|
|
166
|
+
let finalUrl = url;
|
|
167
|
+
let release = null;
|
|
168
|
+
try {
|
|
169
|
+
const result = await fetchWithSsrFGuard({
|
|
170
|
+
url,
|
|
171
|
+
fetchImpl,
|
|
172
|
+
maxRedirects,
|
|
173
|
+
policy: ssrfPolicy,
|
|
174
|
+
lookupFn
|
|
175
|
+
});
|
|
176
|
+
res = result.response;
|
|
177
|
+
finalUrl = result.finalUrl;
|
|
178
|
+
release = result.release;
|
|
179
|
+
} catch (err) {
|
|
180
|
+
throw new MediaFetchError("fetch_failed", `Failed to fetch media from ${url}: ${String(err)}`);
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
if (!res.ok) {
|
|
184
|
+
const statusText = res.statusText ? ` ${res.statusText}` : "";
|
|
185
|
+
const redirected = finalUrl !== url ? ` (redirected to ${finalUrl})` : "";
|
|
186
|
+
let detail = `HTTP ${res.status}${statusText}`;
|
|
187
|
+
if (!res.body) detail = `HTTP ${res.status}${statusText}; empty response body`;
|
|
188
|
+
else {
|
|
189
|
+
const snippet = await readErrorBodySnippet(res);
|
|
190
|
+
if (snippet) detail += `; body: ${snippet}`;
|
|
191
|
+
}
|
|
192
|
+
throw new MediaFetchError("http_error", `Failed to fetch media from ${url}${redirected}: ${detail}`);
|
|
193
|
+
}
|
|
194
|
+
const contentLength = res.headers.get("content-length");
|
|
195
|
+
if (maxBytes && contentLength) {
|
|
196
|
+
const length = Number(contentLength);
|
|
197
|
+
if (Number.isFinite(length) && length > maxBytes) throw new MediaFetchError("max_bytes", `Failed to fetch media from ${url}: content length ${length} exceeds maxBytes ${maxBytes}`);
|
|
198
|
+
}
|
|
199
|
+
const buffer = maxBytes ? await readResponseWithLimit(res, maxBytes) : Buffer.from(await res.arrayBuffer());
|
|
200
|
+
let fileNameFromUrl;
|
|
201
|
+
try {
|
|
202
|
+
const parsed = new URL(finalUrl);
|
|
203
|
+
fileNameFromUrl = path.basename(parsed.pathname) || void 0;
|
|
204
|
+
} catch {}
|
|
205
|
+
const headerFileName = parseContentDispositionFileName(res.headers.get("content-disposition"));
|
|
206
|
+
let fileName = headerFileName || fileNameFromUrl || (filePathHint ? path.basename(filePathHint) : void 0);
|
|
207
|
+
const filePathForMime = headerFileName && path.extname(headerFileName) ? headerFileName : filePathHint ?? finalUrl;
|
|
208
|
+
const contentType = await detectMime({
|
|
209
|
+
buffer,
|
|
210
|
+
headerMime: res.headers.get("content-type"),
|
|
211
|
+
filePath: filePathForMime
|
|
212
|
+
});
|
|
213
|
+
if (fileName && !path.extname(fileName) && contentType) {
|
|
214
|
+
const ext = extensionForMime(contentType);
|
|
215
|
+
if (ext) fileName = `${fileName}${ext}`;
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
buffer,
|
|
219
|
+
contentType: contentType ?? void 0,
|
|
220
|
+
fileName
|
|
221
|
+
};
|
|
222
|
+
} finally {
|
|
223
|
+
if (release) await release();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
async function readResponseWithLimit(res, maxBytes) {
|
|
227
|
+
const body = res.body;
|
|
228
|
+
if (!body || typeof body.getReader !== "function") {
|
|
229
|
+
const fallback = Buffer.from(await res.arrayBuffer());
|
|
230
|
+
if (fallback.length > maxBytes) throw new MediaFetchError("max_bytes", `Failed to fetch media from ${res.url || "response"}: payload exceeds maxBytes ${maxBytes}`);
|
|
231
|
+
return fallback;
|
|
232
|
+
}
|
|
233
|
+
const reader = body.getReader();
|
|
234
|
+
const chunks = [];
|
|
235
|
+
let total = 0;
|
|
236
|
+
try {
|
|
237
|
+
while (true) {
|
|
238
|
+
const { done, value } = await reader.read();
|
|
239
|
+
if (done) break;
|
|
240
|
+
if (value?.length) {
|
|
241
|
+
total += value.length;
|
|
242
|
+
if (total > maxBytes) {
|
|
243
|
+
try {
|
|
244
|
+
await reader.cancel();
|
|
245
|
+
} catch {}
|
|
246
|
+
throw new MediaFetchError("max_bytes", `Failed to fetch media from ${res.url || "response"}: payload exceeds maxBytes ${maxBytes}`);
|
|
247
|
+
}
|
|
248
|
+
chunks.push(value);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
} finally {
|
|
252
|
+
try {
|
|
253
|
+
reader.releaseLock();
|
|
254
|
+
} catch {}
|
|
255
|
+
}
|
|
256
|
+
return Buffer.concat(chunks.map((chunk) => Buffer.from(chunk)), total);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
//#endregion
|
|
260
|
+
//#region src/auto-reply/tokens.ts
|
|
261
|
+
const HEARTBEAT_TOKEN = "HEARTBEAT_OK";
|
|
262
|
+
const SILENT_REPLY_TOKEN = "NO_REPLY";
|
|
263
|
+
function escapeRegExp(value) {
|
|
264
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
265
|
+
}
|
|
266
|
+
function isSilentReplyText(text, token = SILENT_REPLY_TOKEN) {
|
|
267
|
+
if (!text) return false;
|
|
268
|
+
const escaped = escapeRegExp(token);
|
|
269
|
+
if (new RegExp(`^\\s*${escaped}(?=$|\\W)`).test(text)) return true;
|
|
270
|
+
return new RegExp(`\\b${escaped}\\b\\W*$`).test(text);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
//#endregion
|
|
274
|
+
//#region src/markdown/fences.ts
|
|
275
|
+
function parseFenceSpans(buffer) {
|
|
276
|
+
const spans = [];
|
|
277
|
+
let open;
|
|
278
|
+
let offset = 0;
|
|
279
|
+
while (offset <= buffer.length) {
|
|
280
|
+
const nextNewline = buffer.indexOf("\n", offset);
|
|
281
|
+
const lineEnd = nextNewline === -1 ? buffer.length : nextNewline;
|
|
282
|
+
const line = buffer.slice(offset, lineEnd);
|
|
283
|
+
const match = line.match(/^( {0,3})(`{3,}|~{3,})(.*)$/);
|
|
284
|
+
if (match) {
|
|
285
|
+
const indent = match[1];
|
|
286
|
+
const marker = match[2];
|
|
287
|
+
const markerChar = marker[0];
|
|
288
|
+
const markerLen = marker.length;
|
|
289
|
+
if (!open) open = {
|
|
290
|
+
start: offset,
|
|
291
|
+
markerChar,
|
|
292
|
+
markerLen,
|
|
293
|
+
openLine: line,
|
|
294
|
+
marker,
|
|
295
|
+
indent
|
|
296
|
+
};
|
|
297
|
+
else if (open.markerChar === markerChar && markerLen >= open.markerLen) {
|
|
298
|
+
const end = lineEnd;
|
|
299
|
+
spans.push({
|
|
300
|
+
start: open.start,
|
|
301
|
+
end,
|
|
302
|
+
openLine: open.openLine,
|
|
303
|
+
marker: open.marker,
|
|
304
|
+
indent: open.indent
|
|
305
|
+
});
|
|
306
|
+
open = void 0;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (nextNewline === -1) break;
|
|
310
|
+
offset = nextNewline + 1;
|
|
311
|
+
}
|
|
312
|
+
if (open) spans.push({
|
|
313
|
+
start: open.start,
|
|
314
|
+
end: buffer.length,
|
|
315
|
+
openLine: open.openLine,
|
|
316
|
+
marker: open.marker,
|
|
317
|
+
indent: open.indent
|
|
318
|
+
});
|
|
319
|
+
return spans;
|
|
320
|
+
}
|
|
321
|
+
function findFenceSpanAt(spans, index) {
|
|
322
|
+
return spans.find((span) => index > span.start && index < span.end);
|
|
323
|
+
}
|
|
324
|
+
function isSafeFenceBreak(spans, index) {
|
|
325
|
+
return !findFenceSpanAt(spans, index);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
//#endregion
|
|
329
|
+
//#region src/auto-reply/chunk.ts
|
|
330
|
+
const DEFAULT_CHUNK_LIMIT = 4e3;
|
|
331
|
+
const DEFAULT_CHUNK_MODE = "length";
|
|
332
|
+
function resolveChunkLimitForProvider(cfgSection, accountId) {
|
|
333
|
+
if (!cfgSection) return;
|
|
334
|
+
const normalizedAccountId = normalizeAccountId$1(accountId);
|
|
335
|
+
const accounts = cfgSection.accounts;
|
|
336
|
+
if (accounts && typeof accounts === "object") {
|
|
337
|
+
const direct = accounts[normalizedAccountId];
|
|
338
|
+
if (typeof direct?.textChunkLimit === "number") return direct.textChunkLimit;
|
|
339
|
+
const matchKey = Object.keys(accounts).find((key) => key.toLowerCase() === normalizedAccountId.toLowerCase());
|
|
340
|
+
const match = matchKey ? accounts[matchKey] : void 0;
|
|
341
|
+
if (typeof match?.textChunkLimit === "number") return match.textChunkLimit;
|
|
342
|
+
}
|
|
343
|
+
return cfgSection.textChunkLimit;
|
|
344
|
+
}
|
|
345
|
+
function resolveTextChunkLimit(cfg, provider, accountId, opts) {
|
|
346
|
+
const fallback = typeof opts?.fallbackLimit === "number" && opts.fallbackLimit > 0 ? opts.fallbackLimit : DEFAULT_CHUNK_LIMIT;
|
|
347
|
+
const providerOverride = (() => {
|
|
348
|
+
if (!provider || provider === INTERNAL_MESSAGE_CHANNEL) return;
|
|
349
|
+
return resolveChunkLimitForProvider((cfg?.channels)?.[provider] ?? cfg?.[provider], accountId);
|
|
350
|
+
})();
|
|
351
|
+
if (typeof providerOverride === "number" && providerOverride > 0) return providerOverride;
|
|
352
|
+
return fallback;
|
|
353
|
+
}
|
|
354
|
+
function resolveChunkModeForProvider(cfgSection, accountId) {
|
|
355
|
+
if (!cfgSection) return;
|
|
356
|
+
const normalizedAccountId = normalizeAccountId$1(accountId);
|
|
357
|
+
const accounts = cfgSection.accounts;
|
|
358
|
+
if (accounts && typeof accounts === "object") {
|
|
359
|
+
const direct = accounts[normalizedAccountId];
|
|
360
|
+
if (direct?.chunkMode) return direct.chunkMode;
|
|
361
|
+
const matchKey = Object.keys(accounts).find((key) => key.toLowerCase() === normalizedAccountId.toLowerCase());
|
|
362
|
+
const match = matchKey ? accounts[matchKey] : void 0;
|
|
363
|
+
if (match?.chunkMode) return match.chunkMode;
|
|
364
|
+
}
|
|
365
|
+
return cfgSection.chunkMode;
|
|
366
|
+
}
|
|
367
|
+
function resolveChunkMode(cfg, provider, accountId) {
|
|
368
|
+
if (!provider || provider === INTERNAL_MESSAGE_CHANNEL) return DEFAULT_CHUNK_MODE;
|
|
369
|
+
return resolveChunkModeForProvider((cfg?.channels)?.[provider] ?? cfg?.[provider], accountId) ?? DEFAULT_CHUNK_MODE;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Split text on newlines, trimming line whitespace.
|
|
373
|
+
* Blank lines are folded into the next non-empty line as leading "\n" prefixes.
|
|
374
|
+
* Long lines can be split by length (default) or kept intact via splitLongLines:false.
|
|
375
|
+
*/
|
|
376
|
+
function chunkByNewline(text, maxLineLength, opts) {
|
|
377
|
+
if (!text) return [];
|
|
378
|
+
if (maxLineLength <= 0) return text.trim() ? [text] : [];
|
|
379
|
+
const splitLongLines = opts?.splitLongLines !== false;
|
|
380
|
+
const trimLines = opts?.trimLines !== false;
|
|
381
|
+
const lines = splitByNewline(text, opts?.isSafeBreak);
|
|
382
|
+
const chunks = [];
|
|
383
|
+
let pendingBlankLines = 0;
|
|
384
|
+
for (const line of lines) {
|
|
385
|
+
const trimmed = line.trim();
|
|
386
|
+
if (!trimmed) {
|
|
387
|
+
pendingBlankLines += 1;
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
const maxPrefix = Math.max(0, maxLineLength - 1);
|
|
391
|
+
const cappedBlankLines = pendingBlankLines > 0 ? Math.min(pendingBlankLines, maxPrefix) : 0;
|
|
392
|
+
const prefix = cappedBlankLines > 0 ? "\n".repeat(cappedBlankLines) : "";
|
|
393
|
+
pendingBlankLines = 0;
|
|
394
|
+
const lineValue = trimLines ? trimmed : line;
|
|
395
|
+
if (!splitLongLines || lineValue.length + prefix.length <= maxLineLength) {
|
|
396
|
+
chunks.push(prefix + lineValue);
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
const firstLimit = Math.max(1, maxLineLength - prefix.length);
|
|
400
|
+
const first = lineValue.slice(0, firstLimit);
|
|
401
|
+
chunks.push(prefix + first);
|
|
402
|
+
const remaining = lineValue.slice(firstLimit);
|
|
403
|
+
if (remaining) chunks.push(...chunkText(remaining, maxLineLength));
|
|
404
|
+
}
|
|
405
|
+
if (pendingBlankLines > 0 && chunks.length > 0) chunks[chunks.length - 1] += "\n".repeat(pendingBlankLines);
|
|
406
|
+
return chunks;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Split text into chunks on paragraph boundaries (blank lines), preserving lists and
|
|
410
|
+
* single-newline line wraps inside paragraphs.
|
|
411
|
+
*
|
|
412
|
+
* - Only breaks at paragraph separators ("\n\n" or more, allowing whitespace on blank lines)
|
|
413
|
+
* - Packs multiple paragraphs into a single chunk up to `limit`
|
|
414
|
+
* - Falls back to length-based splitting when a single paragraph exceeds `limit`
|
|
415
|
+
* (unless `splitLongParagraphs` is disabled)
|
|
416
|
+
*/
|
|
417
|
+
function chunkByParagraph(text, limit, opts) {
|
|
418
|
+
if (!text) return [];
|
|
419
|
+
if (limit <= 0) return [text];
|
|
420
|
+
const splitLongParagraphs = opts?.splitLongParagraphs !== false;
|
|
421
|
+
const normalized = text.replace(/\r\n?/g, "\n");
|
|
422
|
+
if (!/\n[\t ]*\n+/.test(normalized)) {
|
|
423
|
+
if (normalized.length <= limit) return [normalized];
|
|
424
|
+
if (!splitLongParagraphs) return [normalized];
|
|
425
|
+
return chunkText(normalized, limit);
|
|
426
|
+
}
|
|
427
|
+
const spans = parseFenceSpans(normalized);
|
|
428
|
+
const parts = [];
|
|
429
|
+
const re = /\n[\t ]*\n+/g;
|
|
430
|
+
let lastIndex = 0;
|
|
431
|
+
for (const match of normalized.matchAll(re)) {
|
|
432
|
+
const idx = match.index ?? 0;
|
|
433
|
+
if (!isSafeFenceBreak(spans, idx)) continue;
|
|
434
|
+
parts.push(normalized.slice(lastIndex, idx));
|
|
435
|
+
lastIndex = idx + match[0].length;
|
|
436
|
+
}
|
|
437
|
+
parts.push(normalized.slice(lastIndex));
|
|
438
|
+
const chunks = [];
|
|
439
|
+
for (const part of parts) {
|
|
440
|
+
const paragraph = part.replace(/\s+$/g, "");
|
|
441
|
+
if (!paragraph.trim()) continue;
|
|
442
|
+
if (paragraph.length <= limit) chunks.push(paragraph);
|
|
443
|
+
else if (!splitLongParagraphs) chunks.push(paragraph);
|
|
444
|
+
else chunks.push(...chunkText(paragraph, limit));
|
|
445
|
+
}
|
|
446
|
+
return chunks;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Unified chunking function that dispatches based on mode.
|
|
450
|
+
*/
|
|
451
|
+
function chunkTextWithMode(text, limit, mode) {
|
|
452
|
+
if (mode === "newline") return chunkByParagraph(text, limit);
|
|
453
|
+
return chunkText(text, limit);
|
|
454
|
+
}
|
|
455
|
+
function chunkMarkdownTextWithMode(text, limit, mode) {
|
|
456
|
+
if (mode === "newline") {
|
|
457
|
+
const paragraphChunks = chunkByParagraph(text, limit, { splitLongParagraphs: false });
|
|
458
|
+
const out = [];
|
|
459
|
+
for (const chunk of paragraphChunks) {
|
|
460
|
+
const nested = chunkMarkdownText(chunk, limit);
|
|
461
|
+
if (!nested.length && chunk) out.push(chunk);
|
|
462
|
+
else out.push(...nested);
|
|
463
|
+
}
|
|
464
|
+
return out;
|
|
465
|
+
}
|
|
466
|
+
return chunkMarkdownText(text, limit);
|
|
467
|
+
}
|
|
468
|
+
function splitByNewline(text, isSafeBreak = () => true) {
|
|
469
|
+
const lines = [];
|
|
470
|
+
let start = 0;
|
|
471
|
+
for (let i = 0; i < text.length; i++) if (text[i] === "\n" && isSafeBreak(i)) {
|
|
472
|
+
lines.push(text.slice(start, i));
|
|
473
|
+
start = i + 1;
|
|
474
|
+
}
|
|
475
|
+
lines.push(text.slice(start));
|
|
476
|
+
return lines;
|
|
477
|
+
}
|
|
478
|
+
function chunkText(text, limit) {
|
|
479
|
+
if (!text) return [];
|
|
480
|
+
if (limit <= 0) return [text];
|
|
481
|
+
if (text.length <= limit) return [text];
|
|
482
|
+
const chunks = [];
|
|
483
|
+
let remaining = text;
|
|
484
|
+
while (remaining.length > limit) {
|
|
485
|
+
const { lastNewline, lastWhitespace } = scanParenAwareBreakpoints(remaining.slice(0, limit));
|
|
486
|
+
let breakIdx = lastNewline > 0 ? lastNewline : lastWhitespace;
|
|
487
|
+
if (breakIdx <= 0) breakIdx = limit;
|
|
488
|
+
const chunk = remaining.slice(0, breakIdx).trimEnd();
|
|
489
|
+
if (chunk.length > 0) chunks.push(chunk);
|
|
490
|
+
const brokeOnSeparator = breakIdx < remaining.length && /\s/.test(remaining[breakIdx]);
|
|
491
|
+
const nextStart = Math.min(remaining.length, breakIdx + (brokeOnSeparator ? 1 : 0));
|
|
492
|
+
remaining = remaining.slice(nextStart).trimStart();
|
|
493
|
+
}
|
|
494
|
+
if (remaining.length) chunks.push(remaining);
|
|
495
|
+
return chunks;
|
|
496
|
+
}
|
|
497
|
+
function chunkMarkdownText(text, limit) {
|
|
498
|
+
if (!text) return [];
|
|
499
|
+
if (limit <= 0) return [text];
|
|
500
|
+
if (text.length <= limit) return [text];
|
|
501
|
+
const chunks = [];
|
|
502
|
+
let remaining = text;
|
|
503
|
+
while (remaining.length > limit) {
|
|
504
|
+
const spans = parseFenceSpans(remaining);
|
|
505
|
+
const softBreak = pickSafeBreakIndex(remaining.slice(0, limit), spans);
|
|
506
|
+
let breakIdx = softBreak > 0 ? softBreak : limit;
|
|
507
|
+
const initialFence = isSafeFenceBreak(spans, breakIdx) ? void 0 : findFenceSpanAt(spans, breakIdx);
|
|
508
|
+
let fenceToSplit = initialFence;
|
|
509
|
+
if (initialFence) {
|
|
510
|
+
const closeLine = `${initialFence.indent}${initialFence.marker}`;
|
|
511
|
+
const maxIdxIfNeedNewline = limit - (closeLine.length + 1);
|
|
512
|
+
if (maxIdxIfNeedNewline <= 0) {
|
|
513
|
+
fenceToSplit = void 0;
|
|
514
|
+
breakIdx = limit;
|
|
515
|
+
} else {
|
|
516
|
+
const minProgressIdx = Math.min(remaining.length, initialFence.start + initialFence.openLine.length + 2);
|
|
517
|
+
const maxIdxIfAlreadyNewline = limit - closeLine.length;
|
|
518
|
+
let pickedNewline = false;
|
|
519
|
+
let lastNewline = remaining.lastIndexOf("\n", Math.max(0, maxIdxIfAlreadyNewline - 1));
|
|
520
|
+
while (lastNewline !== -1) {
|
|
521
|
+
const candidateBreak = lastNewline + 1;
|
|
522
|
+
if (candidateBreak < minProgressIdx) break;
|
|
523
|
+
const candidateFence = findFenceSpanAt(spans, candidateBreak);
|
|
524
|
+
if (candidateFence && candidateFence.start === initialFence.start) {
|
|
525
|
+
breakIdx = Math.max(1, candidateBreak);
|
|
526
|
+
pickedNewline = true;
|
|
527
|
+
break;
|
|
528
|
+
}
|
|
529
|
+
lastNewline = remaining.lastIndexOf("\n", lastNewline - 1);
|
|
530
|
+
}
|
|
531
|
+
if (!pickedNewline) if (minProgressIdx > maxIdxIfAlreadyNewline) {
|
|
532
|
+
fenceToSplit = void 0;
|
|
533
|
+
breakIdx = limit;
|
|
534
|
+
} else breakIdx = Math.max(minProgressIdx, maxIdxIfNeedNewline);
|
|
535
|
+
}
|
|
536
|
+
const fenceAtBreak = findFenceSpanAt(spans, breakIdx);
|
|
537
|
+
fenceToSplit = fenceAtBreak && fenceAtBreak.start === initialFence.start ? fenceAtBreak : void 0;
|
|
538
|
+
}
|
|
539
|
+
let rawChunk = remaining.slice(0, breakIdx);
|
|
540
|
+
if (!rawChunk) break;
|
|
541
|
+
const brokeOnSeparator = breakIdx < remaining.length && /\s/.test(remaining[breakIdx]);
|
|
542
|
+
const nextStart = Math.min(remaining.length, breakIdx + (brokeOnSeparator ? 1 : 0));
|
|
543
|
+
let next = remaining.slice(nextStart);
|
|
544
|
+
if (fenceToSplit) {
|
|
545
|
+
const closeLine = `${fenceToSplit.indent}${fenceToSplit.marker}`;
|
|
546
|
+
rawChunk = rawChunk.endsWith("\n") ? `${rawChunk}${closeLine}` : `${rawChunk}\n${closeLine}`;
|
|
547
|
+
next = `${fenceToSplit.openLine}\n${next}`;
|
|
548
|
+
} else next = stripLeadingNewlines(next);
|
|
549
|
+
chunks.push(rawChunk);
|
|
550
|
+
remaining = next;
|
|
551
|
+
}
|
|
552
|
+
if (remaining.length) chunks.push(remaining);
|
|
553
|
+
return chunks;
|
|
554
|
+
}
|
|
555
|
+
function stripLeadingNewlines(value) {
|
|
556
|
+
let i = 0;
|
|
557
|
+
while (i < value.length && value[i] === "\n") i++;
|
|
558
|
+
return i > 0 ? value.slice(i) : value;
|
|
559
|
+
}
|
|
560
|
+
function pickSafeBreakIndex(window, spans) {
|
|
561
|
+
const { lastNewline, lastWhitespace } = scanParenAwareBreakpoints(window, (index) => isSafeFenceBreak(spans, index));
|
|
562
|
+
if (lastNewline > 0) return lastNewline;
|
|
563
|
+
if (lastWhitespace > 0) return lastWhitespace;
|
|
564
|
+
return -1;
|
|
565
|
+
}
|
|
566
|
+
function scanParenAwareBreakpoints(window, isAllowed = () => true) {
|
|
567
|
+
let lastNewline = -1;
|
|
568
|
+
let lastWhitespace = -1;
|
|
569
|
+
let depth = 0;
|
|
570
|
+
for (let i = 0; i < window.length; i++) {
|
|
571
|
+
if (!isAllowed(i)) continue;
|
|
572
|
+
const char = window[i];
|
|
573
|
+
if (char === "(") {
|
|
574
|
+
depth += 1;
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
if (char === ")" && depth > 0) {
|
|
578
|
+
depth -= 1;
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
if (depth !== 0) continue;
|
|
582
|
+
if (char === "\n") lastNewline = i;
|
|
583
|
+
else if (/\s/.test(char)) lastWhitespace = i;
|
|
584
|
+
}
|
|
585
|
+
return {
|
|
586
|
+
lastNewline,
|
|
587
|
+
lastWhitespace
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
//#endregion
|
|
592
|
+
//#region src/config/markdown-tables.ts
|
|
593
|
+
const DEFAULT_TABLE_MODES = new Map([["signal", "bullets"], ["whatsapp", "bullets"]]);
|
|
594
|
+
const isMarkdownTableMode = (value) => value === "off" || value === "bullets" || value === "code";
|
|
595
|
+
function resolveMarkdownModeFromSection(section, accountId) {
|
|
596
|
+
if (!section) return;
|
|
597
|
+
const normalizedAccountId = normalizeAccountId$1(accountId);
|
|
598
|
+
const accounts = section.accounts;
|
|
599
|
+
if (accounts && typeof accounts === "object") {
|
|
600
|
+
const directMode = accounts[normalizedAccountId]?.markdown?.tables;
|
|
601
|
+
if (isMarkdownTableMode(directMode)) return directMode;
|
|
602
|
+
const matchKey = Object.keys(accounts).find((key) => key.toLowerCase() === normalizedAccountId.toLowerCase());
|
|
603
|
+
const matchMode = (matchKey ? accounts[matchKey] : void 0)?.markdown?.tables;
|
|
604
|
+
if (isMarkdownTableMode(matchMode)) return matchMode;
|
|
605
|
+
}
|
|
606
|
+
const sectionMode = section.markdown?.tables;
|
|
607
|
+
return isMarkdownTableMode(sectionMode) ? sectionMode : void 0;
|
|
608
|
+
}
|
|
609
|
+
function resolveMarkdownTableMode(params) {
|
|
610
|
+
const channel = normalizeChannelId(params.channel);
|
|
611
|
+
const defaultMode = channel ? DEFAULT_TABLE_MODES.get(channel) ?? "code" : "code";
|
|
612
|
+
if (!channel || !params.cfg) return defaultMode;
|
|
613
|
+
return resolveMarkdownModeFromSection(params.cfg.channels?.[channel] ?? params.cfg?.[channel], params.accountId) ?? defaultMode;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
//#endregion
|
|
617
|
+
//#region src/web/media.ts
|
|
618
|
+
const HEIC_MIME_RE = /^image\/hei[cf]$/i;
|
|
619
|
+
const HEIC_EXT_RE = /\.(heic|heif)$/i;
|
|
620
|
+
const MB$1 = 1024 * 1024;
|
|
621
|
+
function formatMb(bytes, digits = 2) {
|
|
622
|
+
return (bytes / MB$1).toFixed(digits);
|
|
623
|
+
}
|
|
624
|
+
function formatCapLimit(label, cap, size) {
|
|
625
|
+
return `${label} exceeds ${formatMb(cap, 0)}MB limit (got ${formatMb(size)}MB)`;
|
|
626
|
+
}
|
|
627
|
+
function formatCapReduce(label, cap, size) {
|
|
628
|
+
return `${label} could not be reduced below ${formatMb(cap, 0)}MB (got ${formatMb(size)}MB)`;
|
|
629
|
+
}
|
|
630
|
+
function isHeicSource(opts) {
|
|
631
|
+
if (opts.contentType && HEIC_MIME_RE.test(opts.contentType.trim())) return true;
|
|
632
|
+
if (opts.fileName && HEIC_EXT_RE.test(opts.fileName.trim())) return true;
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
635
|
+
function toJpegFileName(fileName) {
|
|
636
|
+
if (!fileName) return;
|
|
637
|
+
const trimmed = fileName.trim();
|
|
638
|
+
if (!trimmed) return fileName;
|
|
639
|
+
const parsed = path.parse(trimmed);
|
|
640
|
+
if (!parsed.ext || HEIC_EXT_RE.test(parsed.ext)) return path.format({
|
|
641
|
+
dir: parsed.dir,
|
|
642
|
+
name: parsed.name || trimmed,
|
|
643
|
+
ext: ".jpg"
|
|
644
|
+
});
|
|
645
|
+
return path.format({
|
|
646
|
+
dir: parsed.dir,
|
|
647
|
+
name: parsed.name,
|
|
648
|
+
ext: ".jpg"
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
function logOptimizedImage(params) {
|
|
652
|
+
if (!shouldLogVerbose()) return;
|
|
653
|
+
if (params.optimized.optimizedSize >= params.originalSize) return;
|
|
654
|
+
if (params.optimized.format === "png") {
|
|
655
|
+
logVerbose(`Optimized PNG (preserving alpha) from ${formatMb(params.originalSize)}MB to ${formatMb(params.optimized.optimizedSize)}MB (side≤${params.optimized.resizeSide}px)`);
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
logVerbose(`Optimized media from ${formatMb(params.originalSize)}MB to ${formatMb(params.optimized.optimizedSize)}MB (side≤${params.optimized.resizeSide}px, q=${params.optimized.quality})`);
|
|
659
|
+
}
|
|
660
|
+
async function optimizeImageWithFallback(params) {
|
|
661
|
+
const { buffer, cap, meta } = params;
|
|
662
|
+
if ((meta?.contentType === "image/png" || meta?.fileName?.toLowerCase().endsWith(".png")) && await hasAlphaChannel(buffer)) {
|
|
663
|
+
const optimized = await optimizeImageToPng(buffer, cap);
|
|
664
|
+
if (optimized.buffer.length <= cap) return {
|
|
665
|
+
...optimized,
|
|
666
|
+
format: "png"
|
|
667
|
+
};
|
|
668
|
+
if (shouldLogVerbose()) logVerbose(`PNG with alpha still exceeds ${formatMb(cap, 0)}MB after optimization; falling back to JPEG`);
|
|
669
|
+
}
|
|
670
|
+
return {
|
|
671
|
+
...await optimizeImageToJpeg(buffer, cap, meta),
|
|
672
|
+
format: "jpeg"
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
async function loadWebMediaInternal(mediaUrl, options = {}) {
|
|
676
|
+
const { maxBytes, optimizeImages = true, ssrfPolicy } = options;
|
|
677
|
+
if (mediaUrl.startsWith("file://")) try {
|
|
678
|
+
mediaUrl = fileURLToPath(mediaUrl);
|
|
679
|
+
} catch {
|
|
680
|
+
throw new Error(`Invalid file:// URL: ${mediaUrl}`);
|
|
681
|
+
}
|
|
682
|
+
const optimizeAndClampImage = async (buffer, cap, meta) => {
|
|
683
|
+
const originalSize = buffer.length;
|
|
684
|
+
const optimized = await optimizeImageWithFallback({
|
|
685
|
+
buffer,
|
|
686
|
+
cap,
|
|
687
|
+
meta
|
|
688
|
+
});
|
|
689
|
+
logOptimizedImage({
|
|
690
|
+
originalSize,
|
|
691
|
+
optimized
|
|
692
|
+
});
|
|
693
|
+
if (optimized.buffer.length > cap) throw new Error(formatCapReduce("Media", cap, optimized.buffer.length));
|
|
694
|
+
const contentType = optimized.format === "png" ? "image/png" : "image/jpeg";
|
|
695
|
+
const fileName = optimized.format === "jpeg" && meta && isHeicSource(meta) ? toJpegFileName(meta.fileName) : meta?.fileName;
|
|
696
|
+
return {
|
|
697
|
+
buffer: optimized.buffer,
|
|
698
|
+
contentType,
|
|
699
|
+
kind: "image",
|
|
700
|
+
fileName
|
|
701
|
+
};
|
|
702
|
+
};
|
|
703
|
+
const clampAndFinalize = async (params) => {
|
|
704
|
+
const cap = maxBytes !== void 0 ? maxBytes : maxBytesForKind(params.kind);
|
|
705
|
+
if (params.kind === "image") {
|
|
706
|
+
const isGif = params.contentType === "image/gif";
|
|
707
|
+
if (isGif || !optimizeImages) {
|
|
708
|
+
if (params.buffer.length > cap) throw new Error(formatCapLimit(isGif ? "GIF" : "Media", cap, params.buffer.length));
|
|
709
|
+
return {
|
|
710
|
+
buffer: params.buffer,
|
|
711
|
+
contentType: params.contentType,
|
|
712
|
+
kind: params.kind,
|
|
713
|
+
fileName: params.fileName
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
return { ...await optimizeAndClampImage(params.buffer, cap, {
|
|
717
|
+
contentType: params.contentType,
|
|
718
|
+
fileName: params.fileName
|
|
719
|
+
}) };
|
|
720
|
+
}
|
|
721
|
+
if (params.buffer.length > cap) throw new Error(formatCapLimit("Media", cap, params.buffer.length));
|
|
722
|
+
return {
|
|
723
|
+
buffer: params.buffer,
|
|
724
|
+
contentType: params.contentType ?? void 0,
|
|
725
|
+
kind: params.kind,
|
|
726
|
+
fileName: params.fileName
|
|
727
|
+
};
|
|
728
|
+
};
|
|
729
|
+
if (/^https?:\/\//i.test(mediaUrl)) {
|
|
730
|
+
const defaultFetchCap = maxBytesForKind("unknown");
|
|
731
|
+
const { buffer, contentType, fileName } = await fetchRemoteMedia({
|
|
732
|
+
url: mediaUrl,
|
|
733
|
+
maxBytes: maxBytes === void 0 ? defaultFetchCap : optimizeImages ? Math.max(maxBytes, defaultFetchCap) : maxBytes,
|
|
734
|
+
ssrfPolicy
|
|
735
|
+
});
|
|
736
|
+
return await clampAndFinalize({
|
|
737
|
+
buffer,
|
|
738
|
+
contentType,
|
|
739
|
+
kind: mediaKindFromMime(contentType),
|
|
740
|
+
fileName
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
if (mediaUrl.startsWith("~")) mediaUrl = resolveUserPath(mediaUrl);
|
|
744
|
+
const data = await fs.readFile(mediaUrl);
|
|
745
|
+
const mime = await detectMime({
|
|
746
|
+
buffer: data,
|
|
747
|
+
filePath: mediaUrl
|
|
748
|
+
});
|
|
749
|
+
const kind = mediaKindFromMime(mime);
|
|
750
|
+
let fileName = path.basename(mediaUrl) || void 0;
|
|
751
|
+
if (fileName && !path.extname(fileName) && mime) {
|
|
752
|
+
const ext = extensionForMime(mime);
|
|
753
|
+
if (ext) fileName = `${fileName}${ext}`;
|
|
754
|
+
}
|
|
755
|
+
return await clampAndFinalize({
|
|
756
|
+
buffer: data,
|
|
757
|
+
contentType: mime,
|
|
758
|
+
kind,
|
|
759
|
+
fileName
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
async function loadWebMedia(mediaUrl, maxBytes, options) {
|
|
763
|
+
return await loadWebMediaInternal(mediaUrl, {
|
|
764
|
+
maxBytes,
|
|
765
|
+
optimizeImages: true,
|
|
766
|
+
ssrfPolicy: options?.ssrfPolicy
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
async function loadWebMediaRaw(mediaUrl, maxBytes, options) {
|
|
770
|
+
return await loadWebMediaInternal(mediaUrl, {
|
|
771
|
+
maxBytes,
|
|
772
|
+
optimizeImages: false,
|
|
773
|
+
ssrfPolicy: options?.ssrfPolicy
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
async function optimizeImageToJpeg(buffer, maxBytes, opts = {}) {
|
|
777
|
+
let source = buffer;
|
|
778
|
+
if (isHeicSource(opts)) try {
|
|
779
|
+
source = await convertHeicToJpeg(buffer);
|
|
780
|
+
} catch (err) {
|
|
781
|
+
throw new Error(`HEIC image conversion failed: ${String(err)}`, { cause: err });
|
|
782
|
+
}
|
|
783
|
+
const sides = [
|
|
784
|
+
2048,
|
|
785
|
+
1536,
|
|
786
|
+
1280,
|
|
787
|
+
1024,
|
|
788
|
+
800
|
|
789
|
+
];
|
|
790
|
+
const qualities = [
|
|
791
|
+
80,
|
|
792
|
+
70,
|
|
793
|
+
60,
|
|
794
|
+
50,
|
|
795
|
+
40
|
|
796
|
+
];
|
|
797
|
+
let smallest = null;
|
|
798
|
+
for (const side of sides) for (const quality of qualities) try {
|
|
799
|
+
const out = await resizeToJpeg({
|
|
800
|
+
buffer: source,
|
|
801
|
+
maxSide: side,
|
|
802
|
+
quality,
|
|
803
|
+
withoutEnlargement: true
|
|
804
|
+
});
|
|
805
|
+
const size = out.length;
|
|
806
|
+
if (!smallest || size < smallest.size) smallest = {
|
|
807
|
+
buffer: out,
|
|
808
|
+
size,
|
|
809
|
+
resizeSide: side,
|
|
810
|
+
quality
|
|
811
|
+
};
|
|
812
|
+
if (size <= maxBytes) return {
|
|
813
|
+
buffer: out,
|
|
814
|
+
optimizedSize: size,
|
|
815
|
+
resizeSide: side,
|
|
816
|
+
quality
|
|
817
|
+
};
|
|
818
|
+
} catch {}
|
|
819
|
+
if (smallest) return {
|
|
820
|
+
buffer: smallest.buffer,
|
|
821
|
+
optimizedSize: smallest.size,
|
|
822
|
+
resizeSide: smallest.resizeSide,
|
|
823
|
+
quality: smallest.quality
|
|
824
|
+
};
|
|
825
|
+
throw new Error("Failed to optimize image");
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
//#endregion
|
|
829
|
+
//#region src/markdown/ir.ts
|
|
830
|
+
function createMarkdownIt(options) {
|
|
831
|
+
const md = new MarkdownIt({
|
|
832
|
+
html: false,
|
|
833
|
+
linkify: options.linkify ?? true,
|
|
834
|
+
breaks: false,
|
|
835
|
+
typographer: false
|
|
836
|
+
});
|
|
837
|
+
md.enable("strikethrough");
|
|
838
|
+
if (options.tableMode && options.tableMode !== "off") md.enable("table");
|
|
839
|
+
else md.disable("table");
|
|
840
|
+
if (options.autolink === false) md.disable("autolink");
|
|
841
|
+
return md;
|
|
842
|
+
}
|
|
843
|
+
function getAttr(token, name) {
|
|
844
|
+
if (token.attrGet) return token.attrGet(name);
|
|
845
|
+
if (token.attrs) {
|
|
846
|
+
for (const [key, value] of token.attrs) if (key === name) return value;
|
|
847
|
+
}
|
|
848
|
+
return null;
|
|
849
|
+
}
|
|
850
|
+
function createTextToken(base, content) {
|
|
851
|
+
return {
|
|
852
|
+
...base,
|
|
853
|
+
type: "text",
|
|
854
|
+
content,
|
|
855
|
+
children: void 0
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
function applySpoilerTokens(tokens) {
|
|
859
|
+
for (const token of tokens) if (token.children && token.children.length > 0) token.children = injectSpoilersIntoInline(token.children);
|
|
860
|
+
}
|
|
861
|
+
function injectSpoilersIntoInline(tokens) {
|
|
862
|
+
const result = [];
|
|
863
|
+
const state = { spoilerOpen: false };
|
|
864
|
+
for (const token of tokens) {
|
|
865
|
+
if (token.type !== "text") {
|
|
866
|
+
result.push(token);
|
|
867
|
+
continue;
|
|
868
|
+
}
|
|
869
|
+
const content = token.content ?? "";
|
|
870
|
+
if (!content.includes("||")) {
|
|
871
|
+
result.push(token);
|
|
872
|
+
continue;
|
|
873
|
+
}
|
|
874
|
+
let index = 0;
|
|
875
|
+
while (index < content.length) {
|
|
876
|
+
const next = content.indexOf("||", index);
|
|
877
|
+
if (next === -1) {
|
|
878
|
+
if (index < content.length) result.push(createTextToken(token, content.slice(index)));
|
|
879
|
+
break;
|
|
880
|
+
}
|
|
881
|
+
if (next > index) result.push(createTextToken(token, content.slice(index, next)));
|
|
882
|
+
state.spoilerOpen = !state.spoilerOpen;
|
|
883
|
+
result.push({ type: state.spoilerOpen ? "spoiler_open" : "spoiler_close" });
|
|
884
|
+
index = next + 2;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
return result;
|
|
888
|
+
}
|
|
889
|
+
function initRenderTarget() {
|
|
890
|
+
return {
|
|
891
|
+
text: "",
|
|
892
|
+
styles: [],
|
|
893
|
+
openStyles: [],
|
|
894
|
+
links: [],
|
|
895
|
+
linkStack: []
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
function resolveRenderTarget(state) {
|
|
899
|
+
return state.table?.currentCell ?? state;
|
|
900
|
+
}
|
|
901
|
+
function appendText(state, value) {
|
|
902
|
+
if (!value) return;
|
|
903
|
+
const target = resolveRenderTarget(state);
|
|
904
|
+
target.text += value;
|
|
905
|
+
}
|
|
906
|
+
function openStyle(state, style) {
|
|
907
|
+
const target = resolveRenderTarget(state);
|
|
908
|
+
target.openStyles.push({
|
|
909
|
+
style,
|
|
910
|
+
start: target.text.length
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
function closeStyle(state, style) {
|
|
914
|
+
const target = resolveRenderTarget(state);
|
|
915
|
+
for (let i = target.openStyles.length - 1; i >= 0; i -= 1) if (target.openStyles[i]?.style === style) {
|
|
916
|
+
const start = target.openStyles[i].start;
|
|
917
|
+
target.openStyles.splice(i, 1);
|
|
918
|
+
const end = target.text.length;
|
|
919
|
+
if (end > start) target.styles.push({
|
|
920
|
+
start,
|
|
921
|
+
end,
|
|
922
|
+
style
|
|
923
|
+
});
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
function appendParagraphSeparator(state) {
|
|
928
|
+
if (state.env.listStack.length > 0) return;
|
|
929
|
+
if (state.table) return;
|
|
930
|
+
state.text += "\n\n";
|
|
931
|
+
}
|
|
932
|
+
function appendListPrefix(state) {
|
|
933
|
+
const stack = state.env.listStack;
|
|
934
|
+
const top = stack[stack.length - 1];
|
|
935
|
+
if (!top) return;
|
|
936
|
+
top.index += 1;
|
|
937
|
+
const indent = " ".repeat(Math.max(0, stack.length - 1));
|
|
938
|
+
const prefix = top.type === "ordered" ? `${top.index}. ` : "• ";
|
|
939
|
+
state.text += `${indent}${prefix}`;
|
|
940
|
+
}
|
|
941
|
+
function renderInlineCode(state, content) {
|
|
942
|
+
if (!content) return;
|
|
943
|
+
const target = resolveRenderTarget(state);
|
|
944
|
+
const start = target.text.length;
|
|
945
|
+
target.text += content;
|
|
946
|
+
target.styles.push({
|
|
947
|
+
start,
|
|
948
|
+
end: start + content.length,
|
|
949
|
+
style: "code"
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
function renderCodeBlock(state, content) {
|
|
953
|
+
let code = content ?? "";
|
|
954
|
+
if (!code.endsWith("\n")) code = `${code}\n`;
|
|
955
|
+
const target = resolveRenderTarget(state);
|
|
956
|
+
const start = target.text.length;
|
|
957
|
+
target.text += code;
|
|
958
|
+
target.styles.push({
|
|
959
|
+
start,
|
|
960
|
+
end: start + code.length,
|
|
961
|
+
style: "code_block"
|
|
962
|
+
});
|
|
963
|
+
if (state.env.listStack.length === 0) target.text += "\n";
|
|
964
|
+
}
|
|
965
|
+
function handleLinkClose(state) {
|
|
966
|
+
const target = resolveRenderTarget(state);
|
|
967
|
+
const link = target.linkStack.pop();
|
|
968
|
+
if (!link?.href) return;
|
|
969
|
+
const href = link.href.trim();
|
|
970
|
+
if (!href) return;
|
|
971
|
+
const start = link.labelStart;
|
|
972
|
+
const end = target.text.length;
|
|
973
|
+
if (end <= start) {
|
|
974
|
+
target.links.push({
|
|
975
|
+
start,
|
|
976
|
+
end,
|
|
977
|
+
href
|
|
978
|
+
});
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
target.links.push({
|
|
982
|
+
start,
|
|
983
|
+
end,
|
|
984
|
+
href
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
function initTableState() {
|
|
988
|
+
return {
|
|
989
|
+
headers: [],
|
|
990
|
+
rows: [],
|
|
991
|
+
currentRow: [],
|
|
992
|
+
currentCell: null,
|
|
993
|
+
inHeader: false
|
|
994
|
+
};
|
|
995
|
+
}
|
|
996
|
+
function finishTableCell(cell) {
|
|
997
|
+
closeRemainingStyles(cell);
|
|
998
|
+
return {
|
|
999
|
+
text: cell.text,
|
|
1000
|
+
styles: cell.styles,
|
|
1001
|
+
links: cell.links
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
function trimCell(cell) {
|
|
1005
|
+
const text = cell.text;
|
|
1006
|
+
let start = 0;
|
|
1007
|
+
let end = text.length;
|
|
1008
|
+
while (start < end && /\s/.test(text[start] ?? "")) start += 1;
|
|
1009
|
+
while (end > start && /\s/.test(text[end - 1] ?? "")) end -= 1;
|
|
1010
|
+
if (start === 0 && end === text.length) return cell;
|
|
1011
|
+
const trimmedText = text.slice(start, end);
|
|
1012
|
+
const trimmedLength = trimmedText.length;
|
|
1013
|
+
const trimmedStyles = [];
|
|
1014
|
+
for (const span of cell.styles) {
|
|
1015
|
+
const sliceStart = Math.max(0, span.start - start);
|
|
1016
|
+
const sliceEnd = Math.min(trimmedLength, span.end - start);
|
|
1017
|
+
if (sliceEnd > sliceStart) trimmedStyles.push({
|
|
1018
|
+
start: sliceStart,
|
|
1019
|
+
end: sliceEnd,
|
|
1020
|
+
style: span.style
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
const trimmedLinks = [];
|
|
1024
|
+
for (const span of cell.links) {
|
|
1025
|
+
const sliceStart = Math.max(0, span.start - start);
|
|
1026
|
+
const sliceEnd = Math.min(trimmedLength, span.end - start);
|
|
1027
|
+
if (sliceEnd > sliceStart) trimmedLinks.push({
|
|
1028
|
+
start: sliceStart,
|
|
1029
|
+
end: sliceEnd,
|
|
1030
|
+
href: span.href
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
return {
|
|
1034
|
+
text: trimmedText,
|
|
1035
|
+
styles: trimmedStyles,
|
|
1036
|
+
links: trimmedLinks
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
function appendCell(state, cell) {
|
|
1040
|
+
if (!cell.text) return;
|
|
1041
|
+
const start = state.text.length;
|
|
1042
|
+
state.text += cell.text;
|
|
1043
|
+
for (const span of cell.styles) state.styles.push({
|
|
1044
|
+
start: start + span.start,
|
|
1045
|
+
end: start + span.end,
|
|
1046
|
+
style: span.style
|
|
1047
|
+
});
|
|
1048
|
+
for (const link of cell.links) state.links.push({
|
|
1049
|
+
start: start + link.start,
|
|
1050
|
+
end: start + link.end,
|
|
1051
|
+
href: link.href
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
function renderTableAsBullets(state) {
|
|
1055
|
+
if (!state.table) return;
|
|
1056
|
+
const headers = state.table.headers.map(trimCell);
|
|
1057
|
+
const rows = state.table.rows.map((row) => row.map(trimCell));
|
|
1058
|
+
if (headers.length === 0 && rows.length === 0) return;
|
|
1059
|
+
if (headers.length > 1 && rows.length > 0) for (const row of rows) {
|
|
1060
|
+
if (row.length === 0) continue;
|
|
1061
|
+
const rowLabel = row[0];
|
|
1062
|
+
if (rowLabel?.text) {
|
|
1063
|
+
const labelStart = state.text.length;
|
|
1064
|
+
appendCell(state, rowLabel);
|
|
1065
|
+
const labelEnd = state.text.length;
|
|
1066
|
+
if (labelEnd > labelStart) state.styles.push({
|
|
1067
|
+
start: labelStart,
|
|
1068
|
+
end: labelEnd,
|
|
1069
|
+
style: "bold"
|
|
1070
|
+
});
|
|
1071
|
+
state.text += "\n";
|
|
1072
|
+
}
|
|
1073
|
+
for (let i = 1; i < row.length; i++) {
|
|
1074
|
+
const header = headers[i];
|
|
1075
|
+
const value = row[i];
|
|
1076
|
+
if (!value?.text) continue;
|
|
1077
|
+
state.text += "• ";
|
|
1078
|
+
if (header?.text) {
|
|
1079
|
+
appendCell(state, header);
|
|
1080
|
+
state.text += ": ";
|
|
1081
|
+
} else state.text += `Column ${i}: `;
|
|
1082
|
+
appendCell(state, value);
|
|
1083
|
+
state.text += "\n";
|
|
1084
|
+
}
|
|
1085
|
+
state.text += "\n";
|
|
1086
|
+
}
|
|
1087
|
+
else for (const row of rows) {
|
|
1088
|
+
for (let i = 0; i < row.length; i++) {
|
|
1089
|
+
const header = headers[i];
|
|
1090
|
+
const value = row[i];
|
|
1091
|
+
if (!value?.text) continue;
|
|
1092
|
+
state.text += "• ";
|
|
1093
|
+
if (header?.text) {
|
|
1094
|
+
appendCell(state, header);
|
|
1095
|
+
state.text += ": ";
|
|
1096
|
+
}
|
|
1097
|
+
appendCell(state, value);
|
|
1098
|
+
state.text += "\n";
|
|
1099
|
+
}
|
|
1100
|
+
state.text += "\n";
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
function renderTableAsCode(state) {
|
|
1104
|
+
if (!state.table) return;
|
|
1105
|
+
const headers = state.table.headers.map(trimCell);
|
|
1106
|
+
const rows = state.table.rows.map((row) => row.map(trimCell));
|
|
1107
|
+
const columnCount = Math.max(headers.length, ...rows.map((row) => row.length));
|
|
1108
|
+
if (columnCount === 0) return;
|
|
1109
|
+
const widths = Array.from({ length: columnCount }, () => 0);
|
|
1110
|
+
const updateWidths = (cells) => {
|
|
1111
|
+
for (let i = 0; i < columnCount; i += 1) {
|
|
1112
|
+
const width = cells[i]?.text.length ?? 0;
|
|
1113
|
+
if (widths[i] < width) widths[i] = width;
|
|
1114
|
+
}
|
|
1115
|
+
};
|
|
1116
|
+
updateWidths(headers);
|
|
1117
|
+
for (const row of rows) updateWidths(row);
|
|
1118
|
+
const codeStart = state.text.length;
|
|
1119
|
+
const appendRow = (cells) => {
|
|
1120
|
+
state.text += "|";
|
|
1121
|
+
for (let i = 0; i < columnCount; i += 1) {
|
|
1122
|
+
state.text += " ";
|
|
1123
|
+
const cell = cells[i];
|
|
1124
|
+
if (cell) appendCell(state, cell);
|
|
1125
|
+
const pad = widths[i] - (cell?.text.length ?? 0);
|
|
1126
|
+
if (pad > 0) state.text += " ".repeat(pad);
|
|
1127
|
+
state.text += " |";
|
|
1128
|
+
}
|
|
1129
|
+
state.text += "\n";
|
|
1130
|
+
};
|
|
1131
|
+
const appendDivider = () => {
|
|
1132
|
+
state.text += "|";
|
|
1133
|
+
for (let i = 0; i < columnCount; i += 1) {
|
|
1134
|
+
const dashCount = Math.max(3, widths[i]);
|
|
1135
|
+
state.text += ` ${"-".repeat(dashCount)} |`;
|
|
1136
|
+
}
|
|
1137
|
+
state.text += "\n";
|
|
1138
|
+
};
|
|
1139
|
+
appendRow(headers);
|
|
1140
|
+
appendDivider();
|
|
1141
|
+
for (const row of rows) appendRow(row);
|
|
1142
|
+
const codeEnd = state.text.length;
|
|
1143
|
+
if (codeEnd > codeStart) state.styles.push({
|
|
1144
|
+
start: codeStart,
|
|
1145
|
+
end: codeEnd,
|
|
1146
|
+
style: "code_block"
|
|
1147
|
+
});
|
|
1148
|
+
if (state.env.listStack.length === 0) state.text += "\n";
|
|
1149
|
+
}
|
|
1150
|
+
function renderTokens(tokens, state) {
|
|
1151
|
+
for (const token of tokens) switch (token.type) {
|
|
1152
|
+
case "inline":
|
|
1153
|
+
if (token.children) renderTokens(token.children, state);
|
|
1154
|
+
break;
|
|
1155
|
+
case "text":
|
|
1156
|
+
appendText(state, token.content ?? "");
|
|
1157
|
+
break;
|
|
1158
|
+
case "em_open":
|
|
1159
|
+
openStyle(state, "italic");
|
|
1160
|
+
break;
|
|
1161
|
+
case "em_close":
|
|
1162
|
+
closeStyle(state, "italic");
|
|
1163
|
+
break;
|
|
1164
|
+
case "strong_open":
|
|
1165
|
+
openStyle(state, "bold");
|
|
1166
|
+
break;
|
|
1167
|
+
case "strong_close":
|
|
1168
|
+
closeStyle(state, "bold");
|
|
1169
|
+
break;
|
|
1170
|
+
case "s_open":
|
|
1171
|
+
openStyle(state, "strikethrough");
|
|
1172
|
+
break;
|
|
1173
|
+
case "s_close":
|
|
1174
|
+
closeStyle(state, "strikethrough");
|
|
1175
|
+
break;
|
|
1176
|
+
case "code_inline":
|
|
1177
|
+
renderInlineCode(state, token.content ?? "");
|
|
1178
|
+
break;
|
|
1179
|
+
case "spoiler_open":
|
|
1180
|
+
if (state.enableSpoilers) openStyle(state, "spoiler");
|
|
1181
|
+
break;
|
|
1182
|
+
case "spoiler_close":
|
|
1183
|
+
if (state.enableSpoilers) closeStyle(state, "spoiler");
|
|
1184
|
+
break;
|
|
1185
|
+
case "link_open": {
|
|
1186
|
+
const href = getAttr(token, "href") ?? "";
|
|
1187
|
+
const target = resolveRenderTarget(state);
|
|
1188
|
+
target.linkStack.push({
|
|
1189
|
+
href,
|
|
1190
|
+
labelStart: target.text.length
|
|
1191
|
+
});
|
|
1192
|
+
break;
|
|
1193
|
+
}
|
|
1194
|
+
case "link_close":
|
|
1195
|
+
handleLinkClose(state);
|
|
1196
|
+
break;
|
|
1197
|
+
case "image":
|
|
1198
|
+
appendText(state, token.content ?? "");
|
|
1199
|
+
break;
|
|
1200
|
+
case "softbreak":
|
|
1201
|
+
case "hardbreak":
|
|
1202
|
+
appendText(state, "\n");
|
|
1203
|
+
break;
|
|
1204
|
+
case "paragraph_close":
|
|
1205
|
+
appendParagraphSeparator(state);
|
|
1206
|
+
break;
|
|
1207
|
+
case "heading_open":
|
|
1208
|
+
if (state.headingStyle === "bold") openStyle(state, "bold");
|
|
1209
|
+
break;
|
|
1210
|
+
case "heading_close":
|
|
1211
|
+
if (state.headingStyle === "bold") closeStyle(state, "bold");
|
|
1212
|
+
appendParagraphSeparator(state);
|
|
1213
|
+
break;
|
|
1214
|
+
case "blockquote_open":
|
|
1215
|
+
if (state.blockquotePrefix) state.text += state.blockquotePrefix;
|
|
1216
|
+
break;
|
|
1217
|
+
case "blockquote_close":
|
|
1218
|
+
state.text += "\n";
|
|
1219
|
+
break;
|
|
1220
|
+
case "bullet_list_open":
|
|
1221
|
+
state.env.listStack.push({
|
|
1222
|
+
type: "bullet",
|
|
1223
|
+
index: 0
|
|
1224
|
+
});
|
|
1225
|
+
break;
|
|
1226
|
+
case "bullet_list_close":
|
|
1227
|
+
state.env.listStack.pop();
|
|
1228
|
+
break;
|
|
1229
|
+
case "ordered_list_open": {
|
|
1230
|
+
const start = Number(getAttr(token, "start") ?? "1");
|
|
1231
|
+
state.env.listStack.push({
|
|
1232
|
+
type: "ordered",
|
|
1233
|
+
index: start - 1
|
|
1234
|
+
});
|
|
1235
|
+
break;
|
|
1236
|
+
}
|
|
1237
|
+
case "ordered_list_close":
|
|
1238
|
+
state.env.listStack.pop();
|
|
1239
|
+
break;
|
|
1240
|
+
case "list_item_open":
|
|
1241
|
+
appendListPrefix(state);
|
|
1242
|
+
break;
|
|
1243
|
+
case "list_item_close":
|
|
1244
|
+
state.text += "\n";
|
|
1245
|
+
break;
|
|
1246
|
+
case "code_block":
|
|
1247
|
+
case "fence":
|
|
1248
|
+
renderCodeBlock(state, token.content ?? "");
|
|
1249
|
+
break;
|
|
1250
|
+
case "html_block":
|
|
1251
|
+
case "html_inline":
|
|
1252
|
+
appendText(state, token.content ?? "");
|
|
1253
|
+
break;
|
|
1254
|
+
case "table_open":
|
|
1255
|
+
if (state.tableMode !== "off") {
|
|
1256
|
+
state.table = initTableState();
|
|
1257
|
+
state.hasTables = true;
|
|
1258
|
+
}
|
|
1259
|
+
break;
|
|
1260
|
+
case "table_close":
|
|
1261
|
+
if (state.table) {
|
|
1262
|
+
if (state.tableMode === "bullets") renderTableAsBullets(state);
|
|
1263
|
+
else if (state.tableMode === "code") renderTableAsCode(state);
|
|
1264
|
+
}
|
|
1265
|
+
state.table = null;
|
|
1266
|
+
break;
|
|
1267
|
+
case "thead_open":
|
|
1268
|
+
if (state.table) state.table.inHeader = true;
|
|
1269
|
+
break;
|
|
1270
|
+
case "thead_close":
|
|
1271
|
+
if (state.table) state.table.inHeader = false;
|
|
1272
|
+
break;
|
|
1273
|
+
case "tbody_open":
|
|
1274
|
+
case "tbody_close": break;
|
|
1275
|
+
case "tr_open":
|
|
1276
|
+
if (state.table) state.table.currentRow = [];
|
|
1277
|
+
break;
|
|
1278
|
+
case "tr_close":
|
|
1279
|
+
if (state.table) {
|
|
1280
|
+
if (state.table.inHeader) state.table.headers = state.table.currentRow;
|
|
1281
|
+
else state.table.rows.push(state.table.currentRow);
|
|
1282
|
+
state.table.currentRow = [];
|
|
1283
|
+
}
|
|
1284
|
+
break;
|
|
1285
|
+
case "th_open":
|
|
1286
|
+
case "td_open":
|
|
1287
|
+
if (state.table) state.table.currentCell = initRenderTarget();
|
|
1288
|
+
break;
|
|
1289
|
+
case "th_close":
|
|
1290
|
+
case "td_close":
|
|
1291
|
+
if (state.table?.currentCell) {
|
|
1292
|
+
state.table.currentRow.push(finishTableCell(state.table.currentCell));
|
|
1293
|
+
state.table.currentCell = null;
|
|
1294
|
+
}
|
|
1295
|
+
break;
|
|
1296
|
+
case "hr":
|
|
1297
|
+
state.text += "\n";
|
|
1298
|
+
break;
|
|
1299
|
+
default:
|
|
1300
|
+
if (token.children) renderTokens(token.children, state);
|
|
1301
|
+
break;
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
function closeRemainingStyles(target) {
|
|
1305
|
+
for (let i = target.openStyles.length - 1; i >= 0; i -= 1) {
|
|
1306
|
+
const open = target.openStyles[i];
|
|
1307
|
+
const end = target.text.length;
|
|
1308
|
+
if (end > open.start) target.styles.push({
|
|
1309
|
+
start: open.start,
|
|
1310
|
+
end,
|
|
1311
|
+
style: open.style
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1314
|
+
target.openStyles = [];
|
|
1315
|
+
}
|
|
1316
|
+
function clampStyleSpans(spans, maxLength) {
|
|
1317
|
+
const clamped = [];
|
|
1318
|
+
for (const span of spans) {
|
|
1319
|
+
const start = Math.max(0, Math.min(span.start, maxLength));
|
|
1320
|
+
const end = Math.max(start, Math.min(span.end, maxLength));
|
|
1321
|
+
if (end > start) clamped.push({
|
|
1322
|
+
start,
|
|
1323
|
+
end,
|
|
1324
|
+
style: span.style
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
return clamped;
|
|
1328
|
+
}
|
|
1329
|
+
function clampLinkSpans(spans, maxLength) {
|
|
1330
|
+
const clamped = [];
|
|
1331
|
+
for (const span of spans) {
|
|
1332
|
+
const start = Math.max(0, Math.min(span.start, maxLength));
|
|
1333
|
+
const end = Math.max(start, Math.min(span.end, maxLength));
|
|
1334
|
+
if (end > start) clamped.push({
|
|
1335
|
+
start,
|
|
1336
|
+
end,
|
|
1337
|
+
href: span.href
|
|
1338
|
+
});
|
|
1339
|
+
}
|
|
1340
|
+
return clamped;
|
|
1341
|
+
}
|
|
1342
|
+
function mergeStyleSpans(spans) {
|
|
1343
|
+
const sorted = [...spans].toSorted((a, b) => {
|
|
1344
|
+
if (a.start !== b.start) return a.start - b.start;
|
|
1345
|
+
if (a.end !== b.end) return a.end - b.end;
|
|
1346
|
+
return a.style.localeCompare(b.style);
|
|
1347
|
+
});
|
|
1348
|
+
const merged = [];
|
|
1349
|
+
for (const span of sorted) {
|
|
1350
|
+
const prev = merged[merged.length - 1];
|
|
1351
|
+
if (prev && prev.style === span.style && span.start <= prev.end) {
|
|
1352
|
+
prev.end = Math.max(prev.end, span.end);
|
|
1353
|
+
continue;
|
|
1354
|
+
}
|
|
1355
|
+
merged.push({ ...span });
|
|
1356
|
+
}
|
|
1357
|
+
return merged;
|
|
1358
|
+
}
|
|
1359
|
+
function sliceStyleSpans(spans, start, end) {
|
|
1360
|
+
if (spans.length === 0) return [];
|
|
1361
|
+
const sliced = [];
|
|
1362
|
+
for (const span of spans) {
|
|
1363
|
+
const sliceStart = Math.max(span.start, start);
|
|
1364
|
+
const sliceEnd = Math.min(span.end, end);
|
|
1365
|
+
if (sliceEnd > sliceStart) sliced.push({
|
|
1366
|
+
start: sliceStart - start,
|
|
1367
|
+
end: sliceEnd - start,
|
|
1368
|
+
style: span.style
|
|
1369
|
+
});
|
|
1370
|
+
}
|
|
1371
|
+
return mergeStyleSpans(sliced);
|
|
1372
|
+
}
|
|
1373
|
+
function sliceLinkSpans(spans, start, end) {
|
|
1374
|
+
if (spans.length === 0) return [];
|
|
1375
|
+
const sliced = [];
|
|
1376
|
+
for (const span of spans) {
|
|
1377
|
+
const sliceStart = Math.max(span.start, start);
|
|
1378
|
+
const sliceEnd = Math.min(span.end, end);
|
|
1379
|
+
if (sliceEnd > sliceStart) sliced.push({
|
|
1380
|
+
start: sliceStart - start,
|
|
1381
|
+
end: sliceEnd - start,
|
|
1382
|
+
href: span.href
|
|
1383
|
+
});
|
|
1384
|
+
}
|
|
1385
|
+
return sliced;
|
|
1386
|
+
}
|
|
1387
|
+
function markdownToIR(markdown, options = {}) {
|
|
1388
|
+
return markdownToIRWithMeta(markdown, options).ir;
|
|
1389
|
+
}
|
|
1390
|
+
function markdownToIRWithMeta(markdown, options = {}) {
|
|
1391
|
+
const env = { listStack: [] };
|
|
1392
|
+
const tokens = createMarkdownIt(options).parse(markdown ?? "", env);
|
|
1393
|
+
if (options.enableSpoilers) applySpoilerTokens(tokens);
|
|
1394
|
+
const tableMode = options.tableMode ?? "off";
|
|
1395
|
+
const state = {
|
|
1396
|
+
text: "",
|
|
1397
|
+
styles: [],
|
|
1398
|
+
openStyles: [],
|
|
1399
|
+
links: [],
|
|
1400
|
+
linkStack: [],
|
|
1401
|
+
env,
|
|
1402
|
+
headingStyle: options.headingStyle ?? "none",
|
|
1403
|
+
blockquotePrefix: options.blockquotePrefix ?? "",
|
|
1404
|
+
enableSpoilers: options.enableSpoilers ?? false,
|
|
1405
|
+
tableMode,
|
|
1406
|
+
table: null,
|
|
1407
|
+
hasTables: false
|
|
1408
|
+
};
|
|
1409
|
+
renderTokens(tokens, state);
|
|
1410
|
+
closeRemainingStyles(state);
|
|
1411
|
+
const trimmedLength = state.text.trimEnd().length;
|
|
1412
|
+
let codeBlockEnd = 0;
|
|
1413
|
+
for (const span of state.styles) {
|
|
1414
|
+
if (span.style !== "code_block") continue;
|
|
1415
|
+
if (span.end > codeBlockEnd) codeBlockEnd = span.end;
|
|
1416
|
+
}
|
|
1417
|
+
const finalLength = Math.max(trimmedLength, codeBlockEnd);
|
|
1418
|
+
return {
|
|
1419
|
+
ir: {
|
|
1420
|
+
text: finalLength === state.text.length ? state.text : state.text.slice(0, finalLength),
|
|
1421
|
+
styles: mergeStyleSpans(clampStyleSpans(state.styles, finalLength)),
|
|
1422
|
+
links: clampLinkSpans(state.links, finalLength)
|
|
1423
|
+
},
|
|
1424
|
+
hasTables: state.hasTables
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
function chunkMarkdownIR(ir, limit) {
|
|
1428
|
+
if (!ir.text) return [];
|
|
1429
|
+
if (limit <= 0 || ir.text.length <= limit) return [ir];
|
|
1430
|
+
const chunks = chunkText(ir.text, limit);
|
|
1431
|
+
const results = [];
|
|
1432
|
+
let cursor = 0;
|
|
1433
|
+
chunks.forEach((chunk, index) => {
|
|
1434
|
+
if (!chunk) return;
|
|
1435
|
+
if (index > 0) while (cursor < ir.text.length && /\s/.test(ir.text[cursor] ?? "")) cursor += 1;
|
|
1436
|
+
const start = cursor;
|
|
1437
|
+
const end = Math.min(ir.text.length, start + chunk.length);
|
|
1438
|
+
results.push({
|
|
1439
|
+
text: chunk,
|
|
1440
|
+
styles: sliceStyleSpans(ir.styles, start, end),
|
|
1441
|
+
links: sliceLinkSpans(ir.links, start, end)
|
|
1442
|
+
});
|
|
1443
|
+
cursor = end;
|
|
1444
|
+
});
|
|
1445
|
+
return results;
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
//#endregion
|
|
1449
|
+
//#region src/infra/fetch.ts
|
|
1450
|
+
function withDuplex(init, input) {
|
|
1451
|
+
const hasInitBody = init?.body != null;
|
|
1452
|
+
const hasRequestBody = !hasInitBody && typeof Request !== "undefined" && input instanceof Request && input.body != null;
|
|
1453
|
+
if (!hasInitBody && !hasRequestBody) return init;
|
|
1454
|
+
if (init && "duplex" in init) return init;
|
|
1455
|
+
return init ? {
|
|
1456
|
+
...init,
|
|
1457
|
+
duplex: "half"
|
|
1458
|
+
} : { duplex: "half" };
|
|
1459
|
+
}
|
|
1460
|
+
function wrapFetchWithAbortSignal(fetchImpl) {
|
|
1461
|
+
const wrapped = ((input, init) => {
|
|
1462
|
+
const patchedInit = withDuplex(init, input);
|
|
1463
|
+
const signal = patchedInit?.signal;
|
|
1464
|
+
if (!signal) return fetchImpl(input, patchedInit);
|
|
1465
|
+
if (typeof AbortSignal !== "undefined" && signal instanceof AbortSignal) return fetchImpl(input, patchedInit);
|
|
1466
|
+
if (typeof AbortController === "undefined") return fetchImpl(input, patchedInit);
|
|
1467
|
+
if (typeof signal.addEventListener !== "function") return fetchImpl(input, patchedInit);
|
|
1468
|
+
const controller = new AbortController();
|
|
1469
|
+
const onAbort = () => controller.abort();
|
|
1470
|
+
if (signal.aborted) controller.abort();
|
|
1471
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
1472
|
+
const response = fetchImpl(input, {
|
|
1473
|
+
...patchedInit,
|
|
1474
|
+
signal: controller.signal
|
|
1475
|
+
});
|
|
1476
|
+
if (typeof signal.removeEventListener === "function") response.finally(() => {
|
|
1477
|
+
signal.removeEventListener("abort", onAbort);
|
|
1478
|
+
});
|
|
1479
|
+
return response;
|
|
1480
|
+
});
|
|
1481
|
+
const fetchWithPreconnect = fetchImpl;
|
|
1482
|
+
wrapped.preconnect = typeof fetchWithPreconnect.preconnect === "function" ? fetchWithPreconnect.preconnect.bind(fetchWithPreconnect) : () => {};
|
|
1483
|
+
return Object.assign(wrapped, fetchImpl);
|
|
1484
|
+
}
|
|
1485
|
+
function resolveFetch(fetchImpl) {
|
|
1486
|
+
const resolved = fetchImpl ?? globalThis.fetch;
|
|
1487
|
+
if (!resolved) return;
|
|
1488
|
+
return wrapFetchWithAbortSignal(resolved);
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
//#endregion
|
|
1492
|
+
//#region src/signal/client.ts
|
|
1493
|
+
const DEFAULT_TIMEOUT_MS = 1e4;
|
|
1494
|
+
function normalizeBaseUrl(url) {
|
|
1495
|
+
const trimmed = url.trim();
|
|
1496
|
+
if (!trimmed) throw new Error("Signal base URL is required");
|
|
1497
|
+
if (/^https?:\/\//i.test(trimmed)) return trimmed.replace(/\/+$/, "");
|
|
1498
|
+
return `http://${trimmed}`.replace(/\/+$/, "");
|
|
1499
|
+
}
|
|
1500
|
+
async function fetchWithTimeout(url, init, timeoutMs) {
|
|
1501
|
+
const fetchImpl = resolveFetch();
|
|
1502
|
+
if (!fetchImpl) throw new Error("fetch is not available");
|
|
1503
|
+
const controller = new AbortController();
|
|
1504
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
1505
|
+
try {
|
|
1506
|
+
return await fetchImpl(url, {
|
|
1507
|
+
...init,
|
|
1508
|
+
signal: controller.signal
|
|
1509
|
+
});
|
|
1510
|
+
} finally {
|
|
1511
|
+
clearTimeout(timer);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
async function signalRpcRequest(method, params, opts) {
|
|
1515
|
+
const baseUrl = normalizeBaseUrl(opts.baseUrl);
|
|
1516
|
+
const id = randomUUID();
|
|
1517
|
+
const body = JSON.stringify({
|
|
1518
|
+
jsonrpc: "2.0",
|
|
1519
|
+
method,
|
|
1520
|
+
params,
|
|
1521
|
+
id
|
|
1522
|
+
});
|
|
1523
|
+
const res = await fetchWithTimeout(`${baseUrl}/api/v1/rpc`, {
|
|
1524
|
+
method: "POST",
|
|
1525
|
+
headers: { "Content-Type": "application/json" },
|
|
1526
|
+
body
|
|
1527
|
+
}, opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
1528
|
+
if (res.status === 201) return;
|
|
1529
|
+
const text = await res.text();
|
|
1530
|
+
if (!text) throw new Error(`Signal RPC empty response (status ${res.status})`);
|
|
1531
|
+
const parsed = JSON.parse(text);
|
|
1532
|
+
if (parsed.error) {
|
|
1533
|
+
const code = parsed.error.code ?? "unknown";
|
|
1534
|
+
const msg = parsed.error.message ?? "Signal RPC error";
|
|
1535
|
+
throw new Error(`Signal RPC ${code}: ${msg}`);
|
|
1536
|
+
}
|
|
1537
|
+
return parsed.result;
|
|
1538
|
+
}
|
|
1539
|
+
async function signalCheck(baseUrl, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
1540
|
+
const normalized = normalizeBaseUrl(baseUrl);
|
|
1541
|
+
try {
|
|
1542
|
+
const res = await fetchWithTimeout(`${normalized}/api/v1/check`, { method: "GET" }, timeoutMs);
|
|
1543
|
+
if (!res.ok) return {
|
|
1544
|
+
ok: false,
|
|
1545
|
+
status: res.status,
|
|
1546
|
+
error: `HTTP ${res.status}`
|
|
1547
|
+
};
|
|
1548
|
+
return {
|
|
1549
|
+
ok: true,
|
|
1550
|
+
status: res.status,
|
|
1551
|
+
error: null
|
|
1552
|
+
};
|
|
1553
|
+
} catch (err) {
|
|
1554
|
+
return {
|
|
1555
|
+
ok: false,
|
|
1556
|
+
status: null,
|
|
1557
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1558
|
+
};
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
async function streamSignalEvents(params) {
|
|
1562
|
+
const baseUrl = normalizeBaseUrl(params.baseUrl);
|
|
1563
|
+
const url = new URL(`${baseUrl}/api/v1/events`);
|
|
1564
|
+
if (params.account) url.searchParams.set("account", params.account);
|
|
1565
|
+
const fetchImpl = resolveFetch();
|
|
1566
|
+
if (!fetchImpl) throw new Error("fetch is not available");
|
|
1567
|
+
const res = await fetchImpl(url, {
|
|
1568
|
+
method: "GET",
|
|
1569
|
+
headers: { Accept: "text/event-stream" },
|
|
1570
|
+
signal: params.abortSignal
|
|
1571
|
+
});
|
|
1572
|
+
if (!res.ok || !res.body) throw new Error(`Signal SSE failed (${res.status} ${res.statusText || "error"})`);
|
|
1573
|
+
const reader = res.body.getReader();
|
|
1574
|
+
const decoder = new TextDecoder();
|
|
1575
|
+
let buffer = "";
|
|
1576
|
+
let currentEvent = {};
|
|
1577
|
+
const flushEvent = () => {
|
|
1578
|
+
if (!currentEvent.data && !currentEvent.event && !currentEvent.id) return;
|
|
1579
|
+
params.onEvent({
|
|
1580
|
+
event: currentEvent.event,
|
|
1581
|
+
data: currentEvent.data,
|
|
1582
|
+
id: currentEvent.id
|
|
1583
|
+
});
|
|
1584
|
+
currentEvent = {};
|
|
1585
|
+
};
|
|
1586
|
+
while (true) {
|
|
1587
|
+
const { value, done } = await reader.read();
|
|
1588
|
+
if (done) break;
|
|
1589
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1590
|
+
let lineEnd = buffer.indexOf("\n");
|
|
1591
|
+
while (lineEnd !== -1) {
|
|
1592
|
+
let line = buffer.slice(0, lineEnd);
|
|
1593
|
+
buffer = buffer.slice(lineEnd + 1);
|
|
1594
|
+
if (line.endsWith("\r")) line = line.slice(0, -1);
|
|
1595
|
+
if (line === "") {
|
|
1596
|
+
flushEvent();
|
|
1597
|
+
lineEnd = buffer.indexOf("\n");
|
|
1598
|
+
continue;
|
|
1599
|
+
}
|
|
1600
|
+
if (line.startsWith(":")) {
|
|
1601
|
+
lineEnd = buffer.indexOf("\n");
|
|
1602
|
+
continue;
|
|
1603
|
+
}
|
|
1604
|
+
const [rawField, ...rest] = line.split(":");
|
|
1605
|
+
const field = rawField.trim();
|
|
1606
|
+
const rawValue = rest.join(":");
|
|
1607
|
+
const value = rawValue.startsWith(" ") ? rawValue.slice(1) : rawValue;
|
|
1608
|
+
if (field === "event") currentEvent.event = value;
|
|
1609
|
+
else if (field === "data") currentEvent.data = currentEvent.data ? `${currentEvent.data}\n${value}` : value;
|
|
1610
|
+
else if (field === "id") currentEvent.id = value;
|
|
1611
|
+
lineEnd = buffer.indexOf("\n");
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
flushEvent();
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
//#endregion
|
|
1618
|
+
//#region src/signal/format.ts
|
|
1619
|
+
function mapStyle(style) {
|
|
1620
|
+
switch (style) {
|
|
1621
|
+
case "bold": return "BOLD";
|
|
1622
|
+
case "italic": return "ITALIC";
|
|
1623
|
+
case "strikethrough": return "STRIKETHROUGH";
|
|
1624
|
+
case "code":
|
|
1625
|
+
case "code_block": return "MONOSPACE";
|
|
1626
|
+
case "spoiler": return "SPOILER";
|
|
1627
|
+
default: return null;
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
function mergeStyles(styles) {
|
|
1631
|
+
const sorted = [...styles].toSorted((a, b) => {
|
|
1632
|
+
if (a.start !== b.start) return a.start - b.start;
|
|
1633
|
+
if (a.length !== b.length) return a.length - b.length;
|
|
1634
|
+
return a.style.localeCompare(b.style);
|
|
1635
|
+
});
|
|
1636
|
+
const merged = [];
|
|
1637
|
+
for (const style of sorted) {
|
|
1638
|
+
const prev = merged[merged.length - 1];
|
|
1639
|
+
if (prev && prev.style === style.style && style.start <= prev.start + prev.length) {
|
|
1640
|
+
const prevEnd = prev.start + prev.length;
|
|
1641
|
+
prev.length = Math.max(prevEnd, style.start + style.length) - prev.start;
|
|
1642
|
+
continue;
|
|
1643
|
+
}
|
|
1644
|
+
merged.push({ ...style });
|
|
1645
|
+
}
|
|
1646
|
+
return merged;
|
|
1647
|
+
}
|
|
1648
|
+
function clampStyles(styles, maxLength) {
|
|
1649
|
+
const clamped = [];
|
|
1650
|
+
for (const style of styles) {
|
|
1651
|
+
const start = Math.max(0, Math.min(style.start, maxLength));
|
|
1652
|
+
const length = Math.min(style.start + style.length, maxLength) - start;
|
|
1653
|
+
if (length > 0) clamped.push({
|
|
1654
|
+
start,
|
|
1655
|
+
length,
|
|
1656
|
+
style: style.style
|
|
1657
|
+
});
|
|
1658
|
+
}
|
|
1659
|
+
return clamped;
|
|
1660
|
+
}
|
|
1661
|
+
function applyInsertionsToStyles(spans, insertions) {
|
|
1662
|
+
if (insertions.length === 0) return spans;
|
|
1663
|
+
const sortedInsertions = [...insertions].toSorted((a, b) => a.pos - b.pos);
|
|
1664
|
+
let updated = spans;
|
|
1665
|
+
for (const insertion of sortedInsertions) {
|
|
1666
|
+
const next = [];
|
|
1667
|
+
for (const span of updated) {
|
|
1668
|
+
if (span.end <= insertion.pos) {
|
|
1669
|
+
next.push(span);
|
|
1670
|
+
continue;
|
|
1671
|
+
}
|
|
1672
|
+
if (span.start >= insertion.pos) {
|
|
1673
|
+
next.push({
|
|
1674
|
+
start: span.start + insertion.length,
|
|
1675
|
+
end: span.end + insertion.length,
|
|
1676
|
+
style: span.style
|
|
1677
|
+
});
|
|
1678
|
+
continue;
|
|
1679
|
+
}
|
|
1680
|
+
if (span.start < insertion.pos && span.end > insertion.pos) {
|
|
1681
|
+
if (insertion.pos > span.start) next.push({
|
|
1682
|
+
start: span.start,
|
|
1683
|
+
end: insertion.pos,
|
|
1684
|
+
style: span.style
|
|
1685
|
+
});
|
|
1686
|
+
const shiftedStart = insertion.pos + insertion.length;
|
|
1687
|
+
const shiftedEnd = span.end + insertion.length;
|
|
1688
|
+
if (shiftedEnd > shiftedStart) next.push({
|
|
1689
|
+
start: shiftedStart,
|
|
1690
|
+
end: shiftedEnd,
|
|
1691
|
+
style: span.style
|
|
1692
|
+
});
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
updated = next;
|
|
1696
|
+
}
|
|
1697
|
+
return updated;
|
|
1698
|
+
}
|
|
1699
|
+
function renderSignalText(ir) {
|
|
1700
|
+
const text = ir.text ?? "";
|
|
1701
|
+
if (!text) return {
|
|
1702
|
+
text: "",
|
|
1703
|
+
styles: []
|
|
1704
|
+
};
|
|
1705
|
+
const sortedLinks = [...ir.links].toSorted((a, b) => a.start - b.start);
|
|
1706
|
+
let out = "";
|
|
1707
|
+
let cursor = 0;
|
|
1708
|
+
const insertions = [];
|
|
1709
|
+
for (const link of sortedLinks) {
|
|
1710
|
+
if (link.start < cursor) continue;
|
|
1711
|
+
out += text.slice(cursor, link.end);
|
|
1712
|
+
const href = link.href.trim();
|
|
1713
|
+
const trimmedLabel = text.slice(link.start, link.end).trim();
|
|
1714
|
+
const comparableHref = href.startsWith("mailto:") ? href.slice(7) : href;
|
|
1715
|
+
if (href) {
|
|
1716
|
+
if (!trimmedLabel) {
|
|
1717
|
+
out += href;
|
|
1718
|
+
insertions.push({
|
|
1719
|
+
pos: link.end,
|
|
1720
|
+
length: href.length
|
|
1721
|
+
});
|
|
1722
|
+
} else if (trimmedLabel !== href && trimmedLabel !== comparableHref) {
|
|
1723
|
+
const addition = ` (${href})`;
|
|
1724
|
+
out += addition;
|
|
1725
|
+
insertions.push({
|
|
1726
|
+
pos: link.end,
|
|
1727
|
+
length: addition.length
|
|
1728
|
+
});
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
cursor = link.end;
|
|
1732
|
+
}
|
|
1733
|
+
out += text.slice(cursor);
|
|
1734
|
+
const adjusted = applyInsertionsToStyles(ir.styles.map((span) => {
|
|
1735
|
+
const mapped = mapStyle(span.style);
|
|
1736
|
+
if (!mapped) return null;
|
|
1737
|
+
return {
|
|
1738
|
+
start: span.start,
|
|
1739
|
+
end: span.end,
|
|
1740
|
+
style: mapped
|
|
1741
|
+
};
|
|
1742
|
+
}).filter((span) => span !== null), insertions);
|
|
1743
|
+
const trimmedText = out.trimEnd();
|
|
1744
|
+
const trimmedLength = trimmedText.length;
|
|
1745
|
+
return {
|
|
1746
|
+
text: trimmedText,
|
|
1747
|
+
styles: mergeStyles(clampStyles(adjusted.map((span) => ({
|
|
1748
|
+
start: span.start,
|
|
1749
|
+
length: span.end - span.start,
|
|
1750
|
+
style: span.style
|
|
1751
|
+
})), trimmedLength))
|
|
1752
|
+
};
|
|
1753
|
+
}
|
|
1754
|
+
function markdownToSignalText(markdown, options = {}) {
|
|
1755
|
+
return renderSignalText(markdownToIR(markdown ?? "", {
|
|
1756
|
+
linkify: true,
|
|
1757
|
+
enableSpoilers: true,
|
|
1758
|
+
headingStyle: "none",
|
|
1759
|
+
blockquotePrefix: "",
|
|
1760
|
+
tableMode: options.tableMode
|
|
1761
|
+
}));
|
|
1762
|
+
}
|
|
1763
|
+
function markdownToSignalTextChunks(markdown, limit, options = {}) {
|
|
1764
|
+
return chunkMarkdownIR(markdownToIR(markdown ?? "", {
|
|
1765
|
+
linkify: true,
|
|
1766
|
+
enableSpoilers: true,
|
|
1767
|
+
headingStyle: "none",
|
|
1768
|
+
blockquotePrefix: "",
|
|
1769
|
+
tableMode: options.tableMode
|
|
1770
|
+
}), limit).map((chunk) => renderSignalText(chunk));
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
//#endregion
|
|
1774
|
+
//#region src/signal/send.ts
|
|
1775
|
+
function parseTarget(raw) {
|
|
1776
|
+
let value = raw.trim();
|
|
1777
|
+
if (!value) throw new Error("Signal recipient is required");
|
|
1778
|
+
if (value.toLowerCase().startsWith("signal:")) value = value.slice(7).trim();
|
|
1779
|
+
const normalized = value.toLowerCase();
|
|
1780
|
+
if (normalized.startsWith("group:")) return {
|
|
1781
|
+
type: "group",
|
|
1782
|
+
groupId: value.slice(6).trim()
|
|
1783
|
+
};
|
|
1784
|
+
if (normalized.startsWith("username:")) return {
|
|
1785
|
+
type: "username",
|
|
1786
|
+
username: value.slice(9).trim()
|
|
1787
|
+
};
|
|
1788
|
+
if (normalized.startsWith("u:")) return {
|
|
1789
|
+
type: "username",
|
|
1790
|
+
username: value.trim()
|
|
1791
|
+
};
|
|
1792
|
+
return {
|
|
1793
|
+
type: "recipient",
|
|
1794
|
+
recipient: value
|
|
1795
|
+
};
|
|
1796
|
+
}
|
|
1797
|
+
function buildTargetParams(target, allow) {
|
|
1798
|
+
if (target.type === "recipient") {
|
|
1799
|
+
if (!allow.recipient) return null;
|
|
1800
|
+
return { recipient: [target.recipient] };
|
|
1801
|
+
}
|
|
1802
|
+
if (target.type === "group") {
|
|
1803
|
+
if (!allow.group) return null;
|
|
1804
|
+
return { groupId: target.groupId };
|
|
1805
|
+
}
|
|
1806
|
+
if (target.type === "username") {
|
|
1807
|
+
if (!allow.username) return null;
|
|
1808
|
+
return { username: [target.username] };
|
|
1809
|
+
}
|
|
1810
|
+
return null;
|
|
1811
|
+
}
|
|
1812
|
+
function resolveSignalRpcContext(opts, accountInfo) {
|
|
1813
|
+
const hasBaseUrl = Boolean(opts.baseUrl?.trim());
|
|
1814
|
+
const hasAccount = Boolean(opts.account?.trim());
|
|
1815
|
+
const resolvedAccount = accountInfo || (!hasBaseUrl || !hasAccount ? resolveSignalAccount({
|
|
1816
|
+
cfg: loadConfig(),
|
|
1817
|
+
accountId: opts.accountId
|
|
1818
|
+
}) : void 0);
|
|
1819
|
+
const baseUrl = opts.baseUrl?.trim() || resolvedAccount?.baseUrl;
|
|
1820
|
+
if (!baseUrl) throw new Error("Signal base URL is required");
|
|
1821
|
+
return {
|
|
1822
|
+
baseUrl,
|
|
1823
|
+
account: opts.account?.trim() || resolvedAccount?.config.account?.trim()
|
|
1824
|
+
};
|
|
1825
|
+
}
|
|
1826
|
+
async function resolveAttachment(mediaUrl, maxBytes) {
|
|
1827
|
+
const media = await loadWebMedia(mediaUrl, maxBytes);
|
|
1828
|
+
const saved = await saveMediaBuffer(media.buffer, media.contentType ?? void 0, "outbound", maxBytes);
|
|
1829
|
+
return {
|
|
1830
|
+
path: saved.path,
|
|
1831
|
+
contentType: saved.contentType
|
|
1832
|
+
};
|
|
1833
|
+
}
|
|
1834
|
+
async function sendMessageSignal(to, text, opts = {}) {
|
|
1835
|
+
const cfg = loadConfig();
|
|
1836
|
+
const accountInfo = resolveSignalAccount({
|
|
1837
|
+
cfg,
|
|
1838
|
+
accountId: opts.accountId
|
|
1839
|
+
});
|
|
1840
|
+
const { baseUrl, account } = resolveSignalRpcContext(opts, accountInfo);
|
|
1841
|
+
const target = parseTarget(to);
|
|
1842
|
+
let message = text ?? "";
|
|
1843
|
+
let messageFromPlaceholder = false;
|
|
1844
|
+
let textStyles = [];
|
|
1845
|
+
const textMode = opts.textMode ?? "markdown";
|
|
1846
|
+
const maxBytes = (() => {
|
|
1847
|
+
if (typeof opts.maxBytes === "number") return opts.maxBytes;
|
|
1848
|
+
if (typeof accountInfo.config.mediaMaxMb === "number") return accountInfo.config.mediaMaxMb * 1024 * 1024;
|
|
1849
|
+
if (typeof cfg.agents?.defaults?.mediaMaxMb === "number") return cfg.agents.defaults.mediaMaxMb * 1024 * 1024;
|
|
1850
|
+
return 8 * 1024 * 1024;
|
|
1851
|
+
})();
|
|
1852
|
+
let attachments;
|
|
1853
|
+
if (opts.mediaUrl?.trim()) {
|
|
1854
|
+
const resolved = await resolveAttachment(opts.mediaUrl.trim(), maxBytes);
|
|
1855
|
+
attachments = [resolved.path];
|
|
1856
|
+
const kind = mediaKindFromMime(resolved.contentType ?? void 0);
|
|
1857
|
+
if (!message && kind) {
|
|
1858
|
+
message = kind === "image" ? "<media:image>" : `<media:${kind}>`;
|
|
1859
|
+
messageFromPlaceholder = true;
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
if (message.trim() && !messageFromPlaceholder) if (textMode === "plain") textStyles = opts.textStyles ?? [];
|
|
1863
|
+
else {
|
|
1864
|
+
const tableMode = resolveMarkdownTableMode({
|
|
1865
|
+
cfg,
|
|
1866
|
+
channel: "signal",
|
|
1867
|
+
accountId: accountInfo.accountId
|
|
1868
|
+
});
|
|
1869
|
+
const formatted = markdownToSignalText(message, { tableMode });
|
|
1870
|
+
message = formatted.text;
|
|
1871
|
+
textStyles = formatted.styles;
|
|
1872
|
+
}
|
|
1873
|
+
if (!message.trim() && (!attachments || attachments.length === 0)) throw new Error("Signal send requires text or media");
|
|
1874
|
+
const params = { message };
|
|
1875
|
+
if (textStyles.length > 0) params["text-style"] = textStyles.map((style) => `${style.start}:${style.length}:${style.style}`);
|
|
1876
|
+
if (account) params.account = account;
|
|
1877
|
+
if (attachments && attachments.length > 0) params.attachments = attachments;
|
|
1878
|
+
const targetParams = buildTargetParams(target, {
|
|
1879
|
+
recipient: true,
|
|
1880
|
+
group: true,
|
|
1881
|
+
username: true
|
|
1882
|
+
});
|
|
1883
|
+
if (!targetParams) throw new Error("Signal recipient is required");
|
|
1884
|
+
Object.assign(params, targetParams);
|
|
1885
|
+
const timestamp = (await signalRpcRequest("send", params, {
|
|
1886
|
+
baseUrl,
|
|
1887
|
+
timeoutMs: opts.timeoutMs
|
|
1888
|
+
}))?.timestamp;
|
|
1889
|
+
return {
|
|
1890
|
+
messageId: timestamp ? String(timestamp) : "unknown",
|
|
1891
|
+
timestamp
|
|
1892
|
+
};
|
|
1893
|
+
}
|
|
1894
|
+
async function sendTypingSignal(to, opts = {}) {
|
|
1895
|
+
const { baseUrl, account } = resolveSignalRpcContext(opts);
|
|
1896
|
+
const targetParams = buildTargetParams(parseTarget(to), {
|
|
1897
|
+
recipient: true,
|
|
1898
|
+
group: true
|
|
1899
|
+
});
|
|
1900
|
+
if (!targetParams) return false;
|
|
1901
|
+
const params = { ...targetParams };
|
|
1902
|
+
if (account) params.account = account;
|
|
1903
|
+
if (opts.stop) params.stop = true;
|
|
1904
|
+
await signalRpcRequest("sendTyping", params, {
|
|
1905
|
+
baseUrl,
|
|
1906
|
+
timeoutMs: opts.timeoutMs
|
|
1907
|
+
});
|
|
1908
|
+
return true;
|
|
1909
|
+
}
|
|
1910
|
+
async function sendReadReceiptSignal(to, targetTimestamp, opts = {}) {
|
|
1911
|
+
if (!Number.isFinite(targetTimestamp) || targetTimestamp <= 0) return false;
|
|
1912
|
+
const { baseUrl, account } = resolveSignalRpcContext(opts);
|
|
1913
|
+
const targetParams = buildTargetParams(parseTarget(to), { recipient: true });
|
|
1914
|
+
if (!targetParams) return false;
|
|
1915
|
+
const params = {
|
|
1916
|
+
...targetParams,
|
|
1917
|
+
targetTimestamp,
|
|
1918
|
+
type: opts.type ?? "read"
|
|
1919
|
+
};
|
|
1920
|
+
if (account) params.account = account;
|
|
1921
|
+
await signalRpcRequest("sendReceipt", params, {
|
|
1922
|
+
baseUrl,
|
|
1923
|
+
timeoutMs: opts.timeoutMs
|
|
1924
|
+
});
|
|
1925
|
+
return true;
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
//#endregion
|
|
1929
|
+
//#region src/utils/directive-tags.ts
|
|
1930
|
+
const AUDIO_TAG_RE = /\[\[\s*audio_as_voice\s*\]\]/gi;
|
|
1931
|
+
const REPLY_TAG_RE = /\[\[\s*(?:reply_to_current|reply_to\s*:\s*([^\]\n]+))\s*\]\]/gi;
|
|
1932
|
+
function normalizeDirectiveWhitespace(text) {
|
|
1933
|
+
return text.replace(/[ \t]+/g, " ").replace(/[ \t]*\n[ \t]*/g, "\n").trim();
|
|
1934
|
+
}
|
|
1935
|
+
function parseInlineDirectives(text, options = {}) {
|
|
1936
|
+
const { currentMessageId, stripAudioTag = true, stripReplyTags = true } = options;
|
|
1937
|
+
if (!text) return {
|
|
1938
|
+
text: "",
|
|
1939
|
+
audioAsVoice: false,
|
|
1940
|
+
replyToCurrent: false,
|
|
1941
|
+
hasAudioTag: false,
|
|
1942
|
+
hasReplyTag: false
|
|
1943
|
+
};
|
|
1944
|
+
let cleaned = text;
|
|
1945
|
+
let audioAsVoice = false;
|
|
1946
|
+
let hasAudioTag = false;
|
|
1947
|
+
let hasReplyTag = false;
|
|
1948
|
+
let sawCurrent = false;
|
|
1949
|
+
let lastExplicitId;
|
|
1950
|
+
cleaned = cleaned.replace(AUDIO_TAG_RE, (match) => {
|
|
1951
|
+
audioAsVoice = true;
|
|
1952
|
+
hasAudioTag = true;
|
|
1953
|
+
return stripAudioTag ? " " : match;
|
|
1954
|
+
});
|
|
1955
|
+
cleaned = cleaned.replace(REPLY_TAG_RE, (match, idRaw) => {
|
|
1956
|
+
hasReplyTag = true;
|
|
1957
|
+
if (idRaw === void 0) sawCurrent = true;
|
|
1958
|
+
else {
|
|
1959
|
+
const id = idRaw.trim();
|
|
1960
|
+
if (id) lastExplicitId = id;
|
|
1961
|
+
}
|
|
1962
|
+
return stripReplyTags ? " " : match;
|
|
1963
|
+
});
|
|
1964
|
+
cleaned = normalizeDirectiveWhitespace(cleaned);
|
|
1965
|
+
const replyToId = lastExplicitId ?? (sawCurrent ? currentMessageId?.trim() || void 0 : void 0);
|
|
1966
|
+
return {
|
|
1967
|
+
text: cleaned,
|
|
1968
|
+
audioAsVoice,
|
|
1969
|
+
replyToId,
|
|
1970
|
+
replyToExplicitId: lastExplicitId,
|
|
1971
|
+
replyToCurrent: sawCurrent,
|
|
1972
|
+
hasAudioTag,
|
|
1973
|
+
hasReplyTag
|
|
1974
|
+
};
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
//#endregion
|
|
1978
|
+
//#region src/media/audio-tags.ts
|
|
1979
|
+
/**
|
|
1980
|
+
* Extract audio mode tag from text.
|
|
1981
|
+
* Supports [[audio_as_voice]] to send audio as voice bubble instead of file.
|
|
1982
|
+
* Default is file (preserves backward compatibility).
|
|
1983
|
+
*/
|
|
1984
|
+
function parseAudioTag(text) {
|
|
1985
|
+
const result = parseInlineDirectives(text, { stripReplyTags: false });
|
|
1986
|
+
return {
|
|
1987
|
+
text: result.text,
|
|
1988
|
+
audioAsVoice: result.audioAsVoice,
|
|
1989
|
+
hadTag: result.hasAudioTag
|
|
1990
|
+
};
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
//#endregion
|
|
1994
|
+
//#region src/media/parse.ts
|
|
1995
|
+
const MEDIA_TOKEN_RE = /\bMEDIA:\s*`?([^\n]+)`?/gi;
|
|
1996
|
+
function normalizeMediaSource(src) {
|
|
1997
|
+
return src.startsWith("file://") ? src.replace("file://", "") : src;
|
|
1998
|
+
}
|
|
1999
|
+
function cleanCandidate(raw) {
|
|
2000
|
+
return raw.replace(/^[`"'[{(]+/, "").replace(/[`"'\\})\],]+$/, "");
|
|
2001
|
+
}
|
|
2002
|
+
function isValidMedia(candidate, opts) {
|
|
2003
|
+
if (!candidate) return false;
|
|
2004
|
+
if (candidate.length > 4096) return false;
|
|
2005
|
+
if (!opts?.allowSpaces && /\s/.test(candidate)) return false;
|
|
2006
|
+
if (/^https?:\/\//i.test(candidate)) return true;
|
|
2007
|
+
return candidate.startsWith("./") && !candidate.includes("..");
|
|
2008
|
+
}
|
|
2009
|
+
function unwrapQuoted(value) {
|
|
2010
|
+
const trimmed = value.trim();
|
|
2011
|
+
if (trimmed.length < 2) return;
|
|
2012
|
+
const first = trimmed[0];
|
|
2013
|
+
if (first !== trimmed[trimmed.length - 1]) return;
|
|
2014
|
+
if (first !== `"` && first !== "'" && first !== "`") return;
|
|
2015
|
+
return trimmed.slice(1, -1).trim();
|
|
2016
|
+
}
|
|
2017
|
+
function isInsideFence(fenceSpans, offset) {
|
|
2018
|
+
return fenceSpans.some((span) => offset >= span.start && offset < span.end);
|
|
2019
|
+
}
|
|
2020
|
+
function splitMediaFromOutput(raw) {
|
|
2021
|
+
const trimmedRaw = raw.trimEnd();
|
|
2022
|
+
if (!trimmedRaw.trim()) return { text: "" };
|
|
2023
|
+
const media = [];
|
|
2024
|
+
let foundMediaToken = false;
|
|
2025
|
+
const fenceSpans = parseFenceSpans(trimmedRaw);
|
|
2026
|
+
const lines = trimmedRaw.split("\n");
|
|
2027
|
+
const keptLines = [];
|
|
2028
|
+
let lineOffset = 0;
|
|
2029
|
+
for (const line of lines) {
|
|
2030
|
+
if (isInsideFence(fenceSpans, lineOffset)) {
|
|
2031
|
+
keptLines.push(line);
|
|
2032
|
+
lineOffset += line.length + 1;
|
|
2033
|
+
continue;
|
|
2034
|
+
}
|
|
2035
|
+
if (!line.trimStart().startsWith("MEDIA:")) {
|
|
2036
|
+
keptLines.push(line);
|
|
2037
|
+
lineOffset += line.length + 1;
|
|
2038
|
+
continue;
|
|
2039
|
+
}
|
|
2040
|
+
const matches = Array.from(line.matchAll(MEDIA_TOKEN_RE));
|
|
2041
|
+
if (matches.length === 0) {
|
|
2042
|
+
keptLines.push(line);
|
|
2043
|
+
lineOffset += line.length + 1;
|
|
2044
|
+
continue;
|
|
2045
|
+
}
|
|
2046
|
+
const pieces = [];
|
|
2047
|
+
let cursor = 0;
|
|
2048
|
+
for (const match of matches) {
|
|
2049
|
+
const start = match.index ?? 0;
|
|
2050
|
+
pieces.push(line.slice(cursor, start));
|
|
2051
|
+
const payload = match[1];
|
|
2052
|
+
const unwrapped = unwrapQuoted(payload);
|
|
2053
|
+
const payloadValue = unwrapped ?? payload;
|
|
2054
|
+
const parts = unwrapped ? [unwrapped] : payload.split(/\s+/).filter(Boolean);
|
|
2055
|
+
const mediaStartIndex = media.length;
|
|
2056
|
+
let validCount = 0;
|
|
2057
|
+
const invalidParts = [];
|
|
2058
|
+
let hasValidMedia = false;
|
|
2059
|
+
for (const part of parts) {
|
|
2060
|
+
const candidate = normalizeMediaSource(cleanCandidate(part));
|
|
2061
|
+
if (isValidMedia(candidate, unwrapped ? { allowSpaces: true } : void 0)) {
|
|
2062
|
+
media.push(candidate);
|
|
2063
|
+
hasValidMedia = true;
|
|
2064
|
+
foundMediaToken = true;
|
|
2065
|
+
validCount += 1;
|
|
2066
|
+
} else invalidParts.push(part);
|
|
2067
|
+
}
|
|
2068
|
+
const trimmedPayload = payloadValue.trim();
|
|
2069
|
+
const looksLikeLocalPath = trimmedPayload.startsWith("/") || trimmedPayload.startsWith("./") || trimmedPayload.startsWith("../") || trimmedPayload.startsWith("~") || trimmedPayload.startsWith("file://");
|
|
2070
|
+
if (!unwrapped && validCount === 1 && invalidParts.length > 0 && /\s/.test(payloadValue) && looksLikeLocalPath) {
|
|
2071
|
+
const fallback = normalizeMediaSource(cleanCandidate(payloadValue));
|
|
2072
|
+
if (isValidMedia(fallback, { allowSpaces: true })) {
|
|
2073
|
+
media.splice(mediaStartIndex, media.length - mediaStartIndex, fallback);
|
|
2074
|
+
hasValidMedia = true;
|
|
2075
|
+
foundMediaToken = true;
|
|
2076
|
+
validCount = 1;
|
|
2077
|
+
invalidParts.length = 0;
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
if (!hasValidMedia) {
|
|
2081
|
+
const fallback = normalizeMediaSource(cleanCandidate(payloadValue));
|
|
2082
|
+
if (isValidMedia(fallback, { allowSpaces: true })) {
|
|
2083
|
+
media.push(fallback);
|
|
2084
|
+
hasValidMedia = true;
|
|
2085
|
+
foundMediaToken = true;
|
|
2086
|
+
invalidParts.length = 0;
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
if (hasValidMedia) {
|
|
2090
|
+
if (invalidParts.length > 0) pieces.push(invalidParts.join(" "));
|
|
2091
|
+
} else pieces.push(match[0]);
|
|
2092
|
+
cursor = start + match[0].length;
|
|
2093
|
+
}
|
|
2094
|
+
pieces.push(line.slice(cursor));
|
|
2095
|
+
const cleanedLine = pieces.join("").replace(/[ \t]{2,}/g, " ").trim();
|
|
2096
|
+
if (cleanedLine) keptLines.push(cleanedLine);
|
|
2097
|
+
lineOffset += line.length + 1;
|
|
2098
|
+
}
|
|
2099
|
+
let cleanedText = keptLines.join("\n").replace(/[ \t]+\n/g, "\n").replace(/[ \t]{2,}/g, " ").replace(/\n{2,}/g, "\n").trim();
|
|
2100
|
+
const audioTagResult = parseAudioTag(cleanedText);
|
|
2101
|
+
const hasAudioAsVoice = audioTagResult.audioAsVoice;
|
|
2102
|
+
if (audioTagResult.hadTag) cleanedText = audioTagResult.text.replace(/\n{2,}/g, "\n").trim();
|
|
2103
|
+
if (media.length === 0) {
|
|
2104
|
+
const result = { text: foundMediaToken || hasAudioAsVoice ? cleanedText : trimmedRaw };
|
|
2105
|
+
if (hasAudioAsVoice) result.audioAsVoice = true;
|
|
2106
|
+
return result;
|
|
2107
|
+
}
|
|
2108
|
+
return {
|
|
2109
|
+
text: cleanedText,
|
|
2110
|
+
mediaUrls: media,
|
|
2111
|
+
mediaUrl: media[0],
|
|
2112
|
+
...hasAudioAsVoice ? { audioAsVoice: true } : {}
|
|
2113
|
+
};
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
//#endregion
|
|
2117
|
+
//#region src/auto-reply/reply/reply-directives.ts
|
|
2118
|
+
function parseReplyDirectives(raw, options = {}) {
|
|
2119
|
+
const split = splitMediaFromOutput(raw);
|
|
2120
|
+
let text = split.text ?? "";
|
|
2121
|
+
const replyParsed = parseInlineDirectives(text, {
|
|
2122
|
+
currentMessageId: options.currentMessageId,
|
|
2123
|
+
stripAudioTag: false,
|
|
2124
|
+
stripReplyTags: true
|
|
2125
|
+
});
|
|
2126
|
+
if (replyParsed.hasReplyTag) text = replyParsed.text;
|
|
2127
|
+
const silentToken = options.silentToken ?? SILENT_REPLY_TOKEN;
|
|
2128
|
+
const isSilent = isSilentReplyText(text, silentToken);
|
|
2129
|
+
if (isSilent) text = "";
|
|
2130
|
+
return {
|
|
2131
|
+
text,
|
|
2132
|
+
mediaUrls: split.mediaUrls,
|
|
2133
|
+
mediaUrl: split.mediaUrl,
|
|
2134
|
+
replyToId: replyParsed.replyToId,
|
|
2135
|
+
replyToCurrent: replyParsed.replyToCurrent,
|
|
2136
|
+
replyToTag: replyParsed.hasReplyTag,
|
|
2137
|
+
audioAsVoice: split.audioAsVoice,
|
|
2138
|
+
isSilent
|
|
2139
|
+
};
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
//#endregion
|
|
2143
|
+
//#region src/infra/outbound/target-normalization.ts
|
|
2144
|
+
function normalizeChannelTargetInput(raw) {
|
|
2145
|
+
return raw.trim();
|
|
2146
|
+
}
|
|
2147
|
+
function normalizeTargetForProvider(provider, raw) {
|
|
2148
|
+
if (!raw) return;
|
|
2149
|
+
const providerId = normalizeChannelId(provider);
|
|
2150
|
+
return ((providerId ? getChannelPlugin(providerId) : void 0)?.messaging?.normalizeTarget?.(raw) ?? (raw.trim().toLowerCase() || void 0)) || void 0;
|
|
2151
|
+
}
|
|
2152
|
+
function buildTargetResolverSignature(channel) {
|
|
2153
|
+
const resolver = getChannelPlugin(channel)?.messaging?.targetResolver;
|
|
2154
|
+
const hint = resolver?.hint ?? "";
|
|
2155
|
+
const looksLike = resolver?.looksLikeId;
|
|
2156
|
+
return hashSignature(`${hint}|${looksLike ? looksLike.toString() : ""}`);
|
|
2157
|
+
}
|
|
2158
|
+
function hashSignature(value) {
|
|
2159
|
+
let hash = 5381;
|
|
2160
|
+
for (let i = 0; i < value.length; i += 1) hash = (hash << 5) + hash ^ value.charCodeAt(i);
|
|
2161
|
+
return (hash >>> 0).toString(36);
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
//#endregion
|
|
2165
|
+
//#region src/channels/plugins/media-limits.ts
|
|
2166
|
+
const MB = 1024 * 1024;
|
|
2167
|
+
function resolveChannelMediaMaxBytes(params) {
|
|
2168
|
+
const accountId = normalizeAccountId$1(params.accountId);
|
|
2169
|
+
const channelLimit = params.resolveChannelLimitMb({
|
|
2170
|
+
cfg: params.cfg,
|
|
2171
|
+
accountId
|
|
2172
|
+
});
|
|
2173
|
+
if (channelLimit) return channelLimit * MB;
|
|
2174
|
+
if (params.cfg.agents?.defaults?.mediaMaxMb) return params.cfg.agents.defaults.mediaMaxMb * MB;
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
//#endregion
|
|
2178
|
+
//#region src/channels/plugins/outbound/load.ts
|
|
2179
|
+
const cache = /* @__PURE__ */ new Map();
|
|
2180
|
+
let lastRegistry = null;
|
|
2181
|
+
function ensureCacheForRegistry(registry) {
|
|
2182
|
+
if (registry === lastRegistry) return;
|
|
2183
|
+
cache.clear();
|
|
2184
|
+
lastRegistry = registry;
|
|
2185
|
+
}
|
|
2186
|
+
async function loadChannelOutboundAdapter(id) {
|
|
2187
|
+
const registry = getActivePluginRegistry();
|
|
2188
|
+
ensureCacheForRegistry(registry);
|
|
2189
|
+
const cached = cache.get(id);
|
|
2190
|
+
if (cached) return cached;
|
|
2191
|
+
const outbound = (registry?.channels.find((entry) => entry.plugin.id === id))?.plugin.outbound;
|
|
2192
|
+
if (outbound) {
|
|
2193
|
+
cache.set(id, outbound);
|
|
2194
|
+
return outbound;
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
//#endregion
|
|
2199
|
+
//#region src/auto-reply/reply/reply-tags.ts
|
|
2200
|
+
function extractReplyToTag(text, currentMessageId) {
|
|
2201
|
+
const result = parseInlineDirectives(text, {
|
|
2202
|
+
currentMessageId,
|
|
2203
|
+
stripAudioTag: false
|
|
2204
|
+
});
|
|
2205
|
+
return {
|
|
2206
|
+
cleaned: result.text,
|
|
2207
|
+
replyToId: result.replyToId,
|
|
2208
|
+
replyToCurrent: result.replyToCurrent,
|
|
2209
|
+
hasTag: result.hasReplyTag
|
|
2210
|
+
};
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2213
|
+
//#endregion
|
|
2214
|
+
//#region src/auto-reply/reply/reply-threading.ts
|
|
2215
|
+
function resolveReplyToMode(cfg, channel, accountId, chatType) {
|
|
2216
|
+
const provider = normalizeChannelId(channel);
|
|
2217
|
+
if (!provider) return "all";
|
|
2218
|
+
return getChannelDock(provider)?.threading?.resolveReplyToMode?.({
|
|
2219
|
+
cfg,
|
|
2220
|
+
accountId,
|
|
2221
|
+
chatType
|
|
2222
|
+
}) ?? "all";
|
|
2223
|
+
}
|
|
2224
|
+
function createReplyToModeFilter(mode, opts = {}) {
|
|
2225
|
+
let hasThreaded = false;
|
|
2226
|
+
return (payload) => {
|
|
2227
|
+
if (!payload.replyToId) return payload;
|
|
2228
|
+
if (mode === "off") {
|
|
2229
|
+
if (opts.allowTagsWhenOff && payload.replyToTag) return payload;
|
|
2230
|
+
return {
|
|
2231
|
+
...payload,
|
|
2232
|
+
replyToId: void 0
|
|
2233
|
+
};
|
|
2234
|
+
}
|
|
2235
|
+
if (mode === "all") return payload;
|
|
2236
|
+
if (hasThreaded) return {
|
|
2237
|
+
...payload,
|
|
2238
|
+
replyToId: void 0
|
|
2239
|
+
};
|
|
2240
|
+
hasThreaded = true;
|
|
2241
|
+
return payload;
|
|
2242
|
+
};
|
|
2243
|
+
}
|
|
2244
|
+
function createReplyToModeFilterForChannel(mode, channel) {
|
|
2245
|
+
const provider = normalizeChannelId(channel);
|
|
2246
|
+
return createReplyToModeFilter(mode, { allowTagsWhenOff: provider ? Boolean(getChannelDock(provider)?.threading?.allowTagsWhenOff) : false });
|
|
2247
|
+
}
|
|
2248
|
+
|
|
2249
|
+
//#endregion
|
|
2250
|
+
//#region src/auto-reply/reply/reply-payloads.ts
|
|
2251
|
+
function applyReplyTagsToPayload(payload, currentMessageId) {
|
|
2252
|
+
if (typeof payload.text !== "string") {
|
|
2253
|
+
if (!payload.replyToCurrent || payload.replyToId) return payload;
|
|
2254
|
+
return {
|
|
2255
|
+
...payload,
|
|
2256
|
+
replyToId: currentMessageId?.trim() || void 0
|
|
2257
|
+
};
|
|
2258
|
+
}
|
|
2259
|
+
if (!payload.text.includes("[[")) {
|
|
2260
|
+
if (!payload.replyToCurrent || payload.replyToId) return payload;
|
|
2261
|
+
return {
|
|
2262
|
+
...payload,
|
|
2263
|
+
replyToId: currentMessageId?.trim() || void 0,
|
|
2264
|
+
replyToTag: payload.replyToTag ?? true
|
|
2265
|
+
};
|
|
2266
|
+
}
|
|
2267
|
+
const { cleaned, replyToId, replyToCurrent, hasTag } = extractReplyToTag(payload.text, currentMessageId);
|
|
2268
|
+
return {
|
|
2269
|
+
...payload,
|
|
2270
|
+
text: cleaned ? cleaned : void 0,
|
|
2271
|
+
replyToId: replyToId ?? payload.replyToId,
|
|
2272
|
+
replyToTag: hasTag || payload.replyToTag,
|
|
2273
|
+
replyToCurrent: replyToCurrent || payload.replyToCurrent
|
|
2274
|
+
};
|
|
2275
|
+
}
|
|
2276
|
+
function isRenderablePayload(payload) {
|
|
2277
|
+
return Boolean(payload.text || payload.mediaUrl || payload.mediaUrls && payload.mediaUrls.length > 0 || payload.audioAsVoice || payload.channelData);
|
|
2278
|
+
}
|
|
2279
|
+
function applyReplyThreading(params) {
|
|
2280
|
+
const { payloads, replyToMode, replyToChannel, currentMessageId } = params;
|
|
2281
|
+
const applyReplyToMode = createReplyToModeFilterForChannel(replyToMode, replyToChannel);
|
|
2282
|
+
return payloads.map((payload) => applyReplyTagsToPayload(payload, currentMessageId)).filter(isRenderablePayload).map(applyReplyToMode);
|
|
2283
|
+
}
|
|
2284
|
+
function filterMessagingToolDuplicates(params) {
|
|
2285
|
+
const { payloads, sentTexts } = params;
|
|
2286
|
+
if (sentTexts.length === 0) return payloads;
|
|
2287
|
+
return payloads.filter((payload) => !isMessagingToolDuplicate(payload.text ?? "", sentTexts));
|
|
2288
|
+
}
|
|
2289
|
+
function normalizeAccountId(value) {
|
|
2290
|
+
const trimmed = value?.trim();
|
|
2291
|
+
return trimmed ? trimmed.toLowerCase() : void 0;
|
|
2292
|
+
}
|
|
2293
|
+
function shouldSuppressMessagingToolReplies(params) {
|
|
2294
|
+
const provider = params.messageProvider?.trim().toLowerCase();
|
|
2295
|
+
if (!provider) return false;
|
|
2296
|
+
const originTarget = normalizeTargetForProvider(provider, params.originatingTo);
|
|
2297
|
+
if (!originTarget) return false;
|
|
2298
|
+
const originAccount = normalizeAccountId(params.accountId);
|
|
2299
|
+
const sentTargets = params.messagingToolSentTargets ?? [];
|
|
2300
|
+
if (sentTargets.length === 0) return false;
|
|
2301
|
+
return sentTargets.some((target) => {
|
|
2302
|
+
if (!target?.provider) return false;
|
|
2303
|
+
if (target.provider.trim().toLowerCase() !== provider) return false;
|
|
2304
|
+
const targetKey = normalizeTargetForProvider(provider, target.to);
|
|
2305
|
+
if (!targetKey) return false;
|
|
2306
|
+
const targetAccount = normalizeAccountId(target.accountId);
|
|
2307
|
+
if (originAccount && targetAccount && originAccount !== targetAccount) return false;
|
|
2308
|
+
return targetKey === originTarget;
|
|
2309
|
+
});
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2312
|
+
//#endregion
|
|
2313
|
+
//#region src/infra/outbound/payloads.ts
|
|
2314
|
+
function mergeMediaUrls(...lists) {
|
|
2315
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2316
|
+
const merged = [];
|
|
2317
|
+
for (const list of lists) {
|
|
2318
|
+
if (!list) continue;
|
|
2319
|
+
for (const entry of list) {
|
|
2320
|
+
const trimmed = entry?.trim();
|
|
2321
|
+
if (!trimmed) continue;
|
|
2322
|
+
if (seen.has(trimmed)) continue;
|
|
2323
|
+
seen.add(trimmed);
|
|
2324
|
+
merged.push(trimmed);
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
return merged;
|
|
2328
|
+
}
|
|
2329
|
+
function normalizeReplyPayloadsForDelivery(payloads) {
|
|
2330
|
+
return payloads.flatMap((payload) => {
|
|
2331
|
+
const parsed = parseReplyDirectives(payload.text ?? "");
|
|
2332
|
+
const explicitMediaUrls = payload.mediaUrls ?? parsed.mediaUrls;
|
|
2333
|
+
const explicitMediaUrl = payload.mediaUrl ?? parsed.mediaUrl;
|
|
2334
|
+
const mergedMedia = mergeMediaUrls(explicitMediaUrls, explicitMediaUrl ? [explicitMediaUrl] : void 0);
|
|
2335
|
+
const resolvedMediaUrl = (explicitMediaUrls?.length ?? 0) > 1 ? void 0 : explicitMediaUrl;
|
|
2336
|
+
const next = {
|
|
2337
|
+
...payload,
|
|
2338
|
+
text: parsed.text ?? "",
|
|
2339
|
+
mediaUrls: mergedMedia.length ? mergedMedia : void 0,
|
|
2340
|
+
mediaUrl: resolvedMediaUrl,
|
|
2341
|
+
replyToId: payload.replyToId ?? parsed.replyToId,
|
|
2342
|
+
replyToTag: payload.replyToTag || parsed.replyToTag,
|
|
2343
|
+
replyToCurrent: payload.replyToCurrent || parsed.replyToCurrent,
|
|
2344
|
+
audioAsVoice: Boolean(payload.audioAsVoice || parsed.audioAsVoice)
|
|
2345
|
+
};
|
|
2346
|
+
if (parsed.isSilent && mergedMedia.length === 0) return [];
|
|
2347
|
+
if (!isRenderablePayload(next)) return [];
|
|
2348
|
+
return [next];
|
|
2349
|
+
});
|
|
2350
|
+
}
|
|
2351
|
+
function normalizeOutboundPayloads(payloads) {
|
|
2352
|
+
return normalizeReplyPayloadsForDelivery(payloads).map((payload) => {
|
|
2353
|
+
const channelData = payload.channelData;
|
|
2354
|
+
const normalized = {
|
|
2355
|
+
text: payload.text ?? "",
|
|
2356
|
+
mediaUrls: payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : [])
|
|
2357
|
+
};
|
|
2358
|
+
if (channelData && Object.keys(channelData).length > 0) normalized.channelData = channelData;
|
|
2359
|
+
return normalized;
|
|
2360
|
+
}).filter((payload) => payload.text || payload.mediaUrls.length > 0 || Boolean(payload.channelData && Object.keys(payload.channelData).length > 0));
|
|
2361
|
+
}
|
|
2362
|
+
function normalizeOutboundPayloadsForJson(payloads) {
|
|
2363
|
+
return normalizeReplyPayloadsForDelivery(payloads).map((payload) => ({
|
|
2364
|
+
text: payload.text ?? "",
|
|
2365
|
+
mediaUrl: payload.mediaUrl ?? null,
|
|
2366
|
+
mediaUrls: payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : void 0),
|
|
2367
|
+
channelData: payload.channelData
|
|
2368
|
+
}));
|
|
2369
|
+
}
|
|
2370
|
+
function formatOutboundPayloadLog(payload) {
|
|
2371
|
+
const lines = [];
|
|
2372
|
+
if (payload.text) lines.push(payload.text.trimEnd());
|
|
2373
|
+
for (const url of payload.mediaUrls) lines.push(`MEDIA:${url}`);
|
|
2374
|
+
return lines.join("\n");
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
//#endregion
|
|
2378
|
+
//#region src/infra/outbound/deliver.ts
|
|
2379
|
+
var deliver_exports = /* @__PURE__ */ __exportAll({ deliverOutboundPayloads: () => deliverOutboundPayloads });
|
|
2380
|
+
function throwIfAborted(abortSignal) {
|
|
2381
|
+
if (abortSignal?.aborted) throw new Error("Outbound delivery aborted");
|
|
2382
|
+
}
|
|
2383
|
+
async function createChannelHandler(params) {
|
|
2384
|
+
const outbound = await loadChannelOutboundAdapter(params.channel);
|
|
2385
|
+
if (!outbound?.sendText || !outbound?.sendMedia) throw new Error(`Outbound not configured for channel: ${params.channel}`);
|
|
2386
|
+
const handler = createPluginHandler({
|
|
2387
|
+
outbound,
|
|
2388
|
+
cfg: params.cfg,
|
|
2389
|
+
channel: params.channel,
|
|
2390
|
+
to: params.to,
|
|
2391
|
+
accountId: params.accountId,
|
|
2392
|
+
replyToId: params.replyToId,
|
|
2393
|
+
threadId: params.threadId,
|
|
2394
|
+
deps: params.deps,
|
|
2395
|
+
gifPlayback: params.gifPlayback
|
|
2396
|
+
});
|
|
2397
|
+
if (!handler) throw new Error(`Outbound not configured for channel: ${params.channel}`);
|
|
2398
|
+
return handler;
|
|
2399
|
+
}
|
|
2400
|
+
function createPluginHandler(params) {
|
|
2401
|
+
const outbound = params.outbound;
|
|
2402
|
+
if (!outbound?.sendText || !outbound?.sendMedia) return null;
|
|
2403
|
+
const sendText = outbound.sendText;
|
|
2404
|
+
const sendMedia = outbound.sendMedia;
|
|
2405
|
+
return {
|
|
2406
|
+
chunker: outbound.chunker ?? null,
|
|
2407
|
+
chunkerMode: outbound.chunkerMode,
|
|
2408
|
+
textChunkLimit: outbound.textChunkLimit,
|
|
2409
|
+
sendPayload: outbound.sendPayload ? async (payload) => outbound.sendPayload({
|
|
2410
|
+
cfg: params.cfg,
|
|
2411
|
+
to: params.to,
|
|
2412
|
+
text: payload.text ?? "",
|
|
2413
|
+
mediaUrl: payload.mediaUrl,
|
|
2414
|
+
accountId: params.accountId,
|
|
2415
|
+
replyToId: params.replyToId,
|
|
2416
|
+
threadId: params.threadId,
|
|
2417
|
+
gifPlayback: params.gifPlayback,
|
|
2418
|
+
deps: params.deps,
|
|
2419
|
+
payload
|
|
2420
|
+
}) : void 0,
|
|
2421
|
+
sendText: async (text) => sendText({
|
|
2422
|
+
cfg: params.cfg,
|
|
2423
|
+
to: params.to,
|
|
2424
|
+
text,
|
|
2425
|
+
accountId: params.accountId,
|
|
2426
|
+
replyToId: params.replyToId,
|
|
2427
|
+
threadId: params.threadId,
|
|
2428
|
+
gifPlayback: params.gifPlayback,
|
|
2429
|
+
deps: params.deps
|
|
2430
|
+
}),
|
|
2431
|
+
sendMedia: async (caption, mediaUrl) => sendMedia({
|
|
2432
|
+
cfg: params.cfg,
|
|
2433
|
+
to: params.to,
|
|
2434
|
+
text: caption,
|
|
2435
|
+
mediaUrl,
|
|
2436
|
+
accountId: params.accountId,
|
|
2437
|
+
replyToId: params.replyToId,
|
|
2438
|
+
threadId: params.threadId,
|
|
2439
|
+
gifPlayback: params.gifPlayback,
|
|
2440
|
+
deps: params.deps
|
|
2441
|
+
})
|
|
2442
|
+
};
|
|
2443
|
+
}
|
|
2444
|
+
async function deliverOutboundPayloads(params) {
|
|
2445
|
+
const { cfg, channel, to, payloads } = params;
|
|
2446
|
+
const accountId = params.accountId;
|
|
2447
|
+
const deps = params.deps;
|
|
2448
|
+
const abortSignal = params.abortSignal;
|
|
2449
|
+
const sendSignal = params.deps?.sendSignal ?? sendMessageSignal;
|
|
2450
|
+
const results = [];
|
|
2451
|
+
const handler = await createChannelHandler({
|
|
2452
|
+
cfg,
|
|
2453
|
+
channel,
|
|
2454
|
+
to,
|
|
2455
|
+
deps,
|
|
2456
|
+
accountId,
|
|
2457
|
+
replyToId: params.replyToId,
|
|
2458
|
+
threadId: params.threadId,
|
|
2459
|
+
gifPlayback: params.gifPlayback
|
|
2460
|
+
});
|
|
2461
|
+
const textLimit = handler.chunker ? resolveTextChunkLimit(cfg, channel, accountId, { fallbackLimit: handler.textChunkLimit }) : void 0;
|
|
2462
|
+
const chunkMode = handler.chunker ? resolveChunkMode(cfg, channel, accountId) : "length";
|
|
2463
|
+
const isSignalChannel = channel === "signal";
|
|
2464
|
+
const signalTableMode = isSignalChannel ? resolveMarkdownTableMode({
|
|
2465
|
+
cfg,
|
|
2466
|
+
channel: "signal",
|
|
2467
|
+
accountId
|
|
2468
|
+
}) : "code";
|
|
2469
|
+
const signalMaxBytes = isSignalChannel ? resolveChannelMediaMaxBytes({
|
|
2470
|
+
cfg,
|
|
2471
|
+
resolveChannelLimitMb: ({ cfg, accountId }) => cfg.channels?.signal?.accounts?.[accountId]?.mediaMaxMb ?? cfg.channels?.signal?.mediaMaxMb,
|
|
2472
|
+
accountId
|
|
2473
|
+
}) : void 0;
|
|
2474
|
+
const sendTextChunks = async (text) => {
|
|
2475
|
+
throwIfAborted(abortSignal);
|
|
2476
|
+
if (!handler.chunker || textLimit === void 0) {
|
|
2477
|
+
results.push(await handler.sendText(text));
|
|
2478
|
+
return;
|
|
2479
|
+
}
|
|
2480
|
+
if (chunkMode === "newline") {
|
|
2481
|
+
const blockChunks = (handler.chunkerMode ?? "text") === "markdown" ? chunkMarkdownTextWithMode(text, textLimit, "newline") : chunkByParagraph(text, textLimit);
|
|
2482
|
+
if (!blockChunks.length && text) blockChunks.push(text);
|
|
2483
|
+
for (const blockChunk of blockChunks) {
|
|
2484
|
+
const chunks = handler.chunker(blockChunk, textLimit);
|
|
2485
|
+
if (!chunks.length && blockChunk) chunks.push(blockChunk);
|
|
2486
|
+
for (const chunk of chunks) {
|
|
2487
|
+
throwIfAborted(abortSignal);
|
|
2488
|
+
results.push(await handler.sendText(chunk));
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
return;
|
|
2492
|
+
}
|
|
2493
|
+
const chunks = handler.chunker(text, textLimit);
|
|
2494
|
+
for (const chunk of chunks) {
|
|
2495
|
+
throwIfAborted(abortSignal);
|
|
2496
|
+
results.push(await handler.sendText(chunk));
|
|
2497
|
+
}
|
|
2498
|
+
};
|
|
2499
|
+
const sendSignalText = async (text, styles) => {
|
|
2500
|
+
throwIfAborted(abortSignal);
|
|
2501
|
+
return {
|
|
2502
|
+
channel: "signal",
|
|
2503
|
+
...await sendSignal(to, text, {
|
|
2504
|
+
maxBytes: signalMaxBytes,
|
|
2505
|
+
accountId: accountId ?? void 0,
|
|
2506
|
+
textMode: "plain",
|
|
2507
|
+
textStyles: styles
|
|
2508
|
+
})
|
|
2509
|
+
};
|
|
2510
|
+
};
|
|
2511
|
+
const sendSignalTextChunks = async (text) => {
|
|
2512
|
+
throwIfAborted(abortSignal);
|
|
2513
|
+
let signalChunks = textLimit === void 0 ? markdownToSignalTextChunks(text, Number.POSITIVE_INFINITY, { tableMode: signalTableMode }) : markdownToSignalTextChunks(text, textLimit, { tableMode: signalTableMode });
|
|
2514
|
+
if (signalChunks.length === 0 && text) signalChunks = [{
|
|
2515
|
+
text,
|
|
2516
|
+
styles: []
|
|
2517
|
+
}];
|
|
2518
|
+
for (const chunk of signalChunks) {
|
|
2519
|
+
throwIfAborted(abortSignal);
|
|
2520
|
+
results.push(await sendSignalText(chunk.text, chunk.styles));
|
|
2521
|
+
}
|
|
2522
|
+
};
|
|
2523
|
+
const sendSignalMedia = async (caption, mediaUrl) => {
|
|
2524
|
+
throwIfAborted(abortSignal);
|
|
2525
|
+
const formatted = markdownToSignalTextChunks(caption, Number.POSITIVE_INFINITY, { tableMode: signalTableMode })[0] ?? {
|
|
2526
|
+
text: caption,
|
|
2527
|
+
styles: []
|
|
2528
|
+
};
|
|
2529
|
+
return {
|
|
2530
|
+
channel: "signal",
|
|
2531
|
+
...await sendSignal(to, formatted.text, {
|
|
2532
|
+
mediaUrl,
|
|
2533
|
+
maxBytes: signalMaxBytes,
|
|
2534
|
+
accountId: accountId ?? void 0,
|
|
2535
|
+
textMode: "plain",
|
|
2536
|
+
textStyles: formatted.styles
|
|
2537
|
+
})
|
|
2538
|
+
};
|
|
2539
|
+
};
|
|
2540
|
+
const normalizedPayloads = normalizeReplyPayloadsForDelivery(payloads);
|
|
2541
|
+
for (const payload of normalizedPayloads) {
|
|
2542
|
+
const payloadSummary = {
|
|
2543
|
+
text: payload.text ?? "",
|
|
2544
|
+
mediaUrls: payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : []),
|
|
2545
|
+
channelData: payload.channelData
|
|
2546
|
+
};
|
|
2547
|
+
try {
|
|
2548
|
+
throwIfAborted(abortSignal);
|
|
2549
|
+
params.onPayload?.(payloadSummary);
|
|
2550
|
+
if (handler.sendPayload && payload.channelData) {
|
|
2551
|
+
results.push(await handler.sendPayload(payload));
|
|
2552
|
+
continue;
|
|
2553
|
+
}
|
|
2554
|
+
if (payloadSummary.mediaUrls.length === 0) {
|
|
2555
|
+
if (isSignalChannel) await sendSignalTextChunks(payloadSummary.text);
|
|
2556
|
+
else await sendTextChunks(payloadSummary.text);
|
|
2557
|
+
continue;
|
|
2558
|
+
}
|
|
2559
|
+
let first = true;
|
|
2560
|
+
for (const url of payloadSummary.mediaUrls) {
|
|
2561
|
+
throwIfAborted(abortSignal);
|
|
2562
|
+
const caption = first ? payloadSummary.text : "";
|
|
2563
|
+
first = false;
|
|
2564
|
+
if (isSignalChannel) results.push(await sendSignalMedia(caption, url));
|
|
2565
|
+
else results.push(await handler.sendMedia(caption, url));
|
|
2566
|
+
}
|
|
2567
|
+
} catch (err) {
|
|
2568
|
+
if (!params.bestEffort) throw err;
|
|
2569
|
+
params.onError?.(err, payloadSummary);
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
if (params.mirror && results.length > 0) {
|
|
2573
|
+
const mirrorText = resolveMirroredTranscriptText({
|
|
2574
|
+
text: params.mirror.text,
|
|
2575
|
+
mediaUrls: params.mirror.mediaUrls
|
|
2576
|
+
});
|
|
2577
|
+
if (mirrorText) await appendAssistantMessageToSessionTranscript({
|
|
2578
|
+
agentId: params.mirror.agentId,
|
|
2579
|
+
sessionKey: params.mirror.sessionKey,
|
|
2580
|
+
text: mirrorText
|
|
2581
|
+
});
|
|
2582
|
+
}
|
|
2583
|
+
return results;
|
|
2584
|
+
}
|
|
2585
|
+
|
|
2586
|
+
//#endregion
|
|
2587
|
+
export { markdownToIRWithMeta as A, resolveTextChunkLimit as B, signalCheck as C, wrapFetchWithAbortSignal as D, resolveFetch as E, chunkMarkdownText as F, SILENT_REPLY_TOKEN as G, isSafeFenceBreak as H, chunkMarkdownTextWithMode as I, fetchRemoteMedia as J, isSilentReplyText as K, chunkText as L, loadWebMediaRaw as M, resolveMarkdownTableMode as N, chunkMarkdownIR as O, chunkByNewline as P, chunkTextWithMode as R, sendTypingSignal as S, streamSignalEvents as T, parseFenceSpans as U, findFenceSpanAt as V, HEARTBEAT_TOKEN as W, fetchWithSsrFGuard as Y, parseReplyDirectives as _, normalizeOutboundPayloadsForJson as a, sendMessageSignal as b, applyReplyThreading as c, shouldSuppressMessagingToolReplies as d, createReplyToModeFilterForChannel as f, normalizeTargetForProvider as g, normalizeChannelTargetInput as h, normalizeOutboundPayloads as i, loadWebMedia as j, markdownToIR as k, filterMessagingToolDuplicates as l, buildTargetResolverSignature as m, deliver_exports as n, normalizeReplyPayloadsForDelivery as o, resolveReplyToMode as p, MediaFetchError as q, formatOutboundPayloadLog as r, applyReplyTagsToPayload as s, deliverOutboundPayloads as t, isRenderablePayload as u, splitMediaFromOutput as v, signalRpcRequest as w, sendReadReceiptSignal as x, parseInlineDirectives as y, resolveChunkMode as z };
|