@youweichen/pi-harness 0.0.0-stage → 1.0.0
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/LICENSE +21 -0
- package/README.md +74 -2
- package/bin/pi-harness.mjs +1819 -0
- package/deploy/com.youweichen.pi-harness.plist +48 -0
- package/deploy/nginx-subpath.conf +88 -0
- package/deploy/pi-harness-task.xml +71 -0
- package/deploy/pi-harness.service +31 -0
- package/dist/server/agent-service.js +3051 -0
- package/dist/server/app-version.js +19 -0
- package/dist/server/attachments.js +526 -0
- package/dist/server/auth-file.js +61 -0
- package/dist/server/bg-servers.js +154 -0
- package/dist/server/client-eviction.js +19 -0
- package/dist/server/client-id.js +9 -0
- package/dist/server/client-state.js +202 -0
- package/dist/server/codemode-image-routes.js +36 -0
- package/dist/server/codemode-presentation.js +19 -0
- package/dist/server/component-updates.js +135 -0
- package/dist/server/context-breakdown.js +56 -0
- package/dist/server/control-socket.js +177 -0
- package/dist/server/conversation.js +65 -0
- package/dist/server/dir-browser.js +77 -0
- package/dist/server/editor-snapshot.js +38 -0
- package/dist/server/ensure-bash.js +80 -0
- package/dist/server/extension-display.js +23 -0
- package/dist/server/extensions-catalog.js +64 -0
- package/dist/server/extensions-model.js +46 -0
- package/dist/server/extensions-routes.js +255 -0
- package/dist/server/extensions-worker.js +187 -0
- package/dist/server/files-service.js +795 -0
- package/dist/server/index.js +1029 -0
- package/dist/server/markdown-images.js +42 -0
- package/dist/server/model-admin.js +801 -0
- package/dist/server/model-config-merge.js +32 -0
- package/dist/server/native-mcp-config.js +103 -0
- package/dist/server/native-mcp-presentation.js +12 -0
- package/dist/server/native-prompt-template.js +12 -0
- package/dist/server/native-tools.js +32 -0
- package/dist/server/node-sources.js +142 -0
- package/dist/server/node-workbench.js +668 -0
- package/dist/server/origin-policy.js +50 -0
- package/dist/server/patch-node-pty.js +96 -0
- package/dist/server/pi-environment.js +203 -0
- package/dist/server/plan/extension.js +74 -0
- package/dist/server/plan/progress.js +90 -0
- package/dist/server/plan/settings.js +41 -0
- package/dist/server/plan/state.js +113 -0
- package/dist/server/plugin-facilities.js +304 -0
- package/dist/server/plugin-updater.js +245 -0
- package/dist/server/plugins.js +712 -0
- package/dist/server/private-file.js +28 -0
- package/dist/server/process-utils.js +112 -0
- package/dist/server/prompt-delivery.js +80 -0
- package/dist/server/protocol-version.js +11 -0
- package/dist/server/protocol.js +6 -0
- package/dist/server/provider-auth.js +99 -0
- package/dist/server/query-cache.js +45 -0
- package/dist/server/recovery-state.js +37 -0
- package/dist/server/scm.js +459 -0
- package/dist/server/security-headers.js +24 -0
- package/dist/server/serialize.js +254 -0
- package/dist/server/session-file-read.js +225 -0
- package/dist/server/session-record-worker.js +21 -0
- package/dist/server/session-reload.js +13 -0
- package/dist/server/session-tree-controller.js +250 -0
- package/dist/server/session-tree.js +191 -0
- package/dist/server/settings-service.js +53 -0
- package/dist/server/skills-service.js +87 -0
- package/dist/server/slash-commands.js +280 -0
- package/dist/server/sqlite-preview.js +58 -0
- package/dist/server/sqlite-query.js +67 -0
- package/dist/server/sqlite-worker.js +12 -0
- package/dist/server/system-prompt-files.js +158 -0
- package/dist/server/system-prompt-routes.js +40 -0
- package/dist/server/system-prompt-view.js +49 -0
- package/dist/server/task-progress.js +233 -0
- package/dist/server/terminals.js +833 -0
- package/dist/server/text-sniff.js +269 -0
- package/dist/server/thinking-timing.js +91 -0
- package/dist/server/token-auth.js +37 -0
- package/dist/server/tool-output.js +97 -0
- package/dist/server/tool-text-incidents.js +82 -0
- package/dist/server/tool-text.js +67 -0
- package/dist/server/uploads.js +112 -0
- package/dist/server/user-attachments.js +96 -0
- package/dist/server/webui-context.js +257 -0
- package/dist/server/wiki-links.js +136 -0
- package/dist/server/wiki-pdf-worker.js +23 -0
- package/dist/server/wiki-pdf.js +36 -0
- package/dist/server/wiki-routes.js +177 -0
- package/dist/server/wiki-service.js +511 -0
- package/electron/main.mjs +607 -0
- package/extensions/webui.ts +192 -0
- package/package.json +122 -4
- package/web/dist/apple-touch-icon.png +0 -0
- package/web/dist/assets/NodeWorkbench-C4U1PldE.js +3 -0
- package/web/dist/assets/TerminalPanel-CWh6vw_N.js +2 -0
- package/web/dist/assets/abnfDiagram-VCTEODGH-D5Ti0Kpa.js +1 -0
- package/web/dist/assets/arc-DKJilLd6.js +1 -0
- package/web/dist/assets/architectureDiagram-5GKGNRK7-Cf0w38g-.js +36 -0
- package/web/dist/assets/blockDiagram-I7D4REHJ-BOTLWLIL.js +129 -0
- package/web/dist/assets/c4Diagram-7LVT6UL2-CLCCoJs3.js +38 -0
- package/web/dist/assets/channel-D8Hd7T4P.js +1 -0
- package/web/dist/assets/chunk-2Q5K7J3B-BHjga2Ge.js +1 -0
- package/web/dist/assets/chunk-5VM5RSS4-CQJ5VfDZ.js +15 -0
- package/web/dist/assets/chunk-F27PBJKO-C67qoPJH.js +1 -0
- package/web/dist/assets/chunk-IMKFNOWR-CgXXGwI8.js +231 -0
- package/web/dist/assets/chunk-JWPE2WC7-D0RPyByP.js +1 -0
- package/web/dist/assets/chunk-POPQ4Y6H-BeYC9s9x.js +1 -0
- package/web/dist/assets/chunk-SVP7TREG-eQPl4UBQ.js +88 -0
- package/web/dist/assets/chunk-TICWLB2K-DT73arPU.js +206 -0
- package/web/dist/assets/chunk-XXDRQBXY-B4FwQcP-.js +1 -0
- package/web/dist/assets/classDiagram-ZZMXUADV-B_btgYRB.js +1 -0
- package/web/dist/assets/classDiagram-v2-VYDZK3BY-B_btgYRB.js +1 -0
- package/web/dist/assets/cose-bilkent-JH36ORCC-Bnn5nlQN.js +1 -0
- package/web/dist/assets/cynefin-OW5HDTMX-CStWX9kE.js +178 -0
- package/web/dist/assets/cynefinDiagram-5FMLGOSQ-BMVHC03f.js +62 -0
- package/web/dist/assets/cytoscape.esm-Bch-eiPH.js +331 -0
- package/web/dist/assets/dagre-GXQ25YYZ-ClSaO4ml.js +4 -0
- package/web/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/web/dist/assets/diagram-S7CK7UJ4-C43LL08C.js +30 -0
- package/web/dist/assets/diagram-UQ7AKVKN-BPVn_ySW.js +41 -0
- package/web/dist/assets/diagram-VSXAHHWV-C34FFhRi.js +3 -0
- package/web/dist/assets/diagram-VX7I27RA-BRk4bncf.js +24 -0
- package/web/dist/assets/diagram-Z3DM3KII-O5hYPLEd.js +24 -0
- package/web/dist/assets/ebnfDiagram-PWID7BFC-DVGEBm99.js +1 -0
- package/web/dist/assets/erDiagram-RLTQ6QDP-M-BLpTEV.js +99 -0
- package/web/dist/assets/flowDiagram-HODETNUW-BwdLopEx.js +168 -0
- package/web/dist/assets/ganttDiagram-EL5Y4UJY-DYxx2ppA.js +292 -0
- package/web/dist/assets/gitGraphDiagram-WWUBYQGX-DUU0wMYB.js +106 -0
- package/web/dist/assets/index-CI2KfVSr.css +10 -0
- package/web/dist/assets/index-DQcpggFU.js +275 -0
- package/web/dist/assets/infoDiagram-27XIBGKW-CFaW5C0C.js +2 -0
- package/web/dist/assets/init-Gi6I4Gst.js +1 -0
- package/web/dist/assets/ishikawaDiagram-5VMMS53U--ENP1MTB.js +70 -0
- package/web/dist/assets/journeyDiagram-3NMN7TZE-DgnAzSTj.js +139 -0
- package/web/dist/assets/kanban-definition-UXKFOSKX-_STZN1vU.js +89 -0
- package/web/dist/assets/katex-C5jXJg4s.js +257 -0
- package/web/dist/assets/layout-CkUClkXt.js +1 -0
- package/web/dist/assets/linear-sYHiUOxa.js +1 -0
- package/web/dist/assets/markdown-vNTCZJ6d.js +51 -0
- package/web/dist/assets/mermaid.core-B8fKGWsc.js +312 -0
- package/web/dist/assets/mindmap-definition-YA3MSWOX-DP0BYOEx.js +96 -0
- package/web/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/web/dist/assets/pegDiagram-XKGWAZYB-DkWTmZre.js +1 -0
- package/web/dist/assets/pieDiagram-E7YTZNPT-DfZgMV4u.js +39 -0
- package/web/dist/assets/quadrantDiagram-AXDQQJYC-Djl4TT7q.js +7 -0
- package/web/dist/assets/railroadDiagram-O6MQD6OU-DbKF6zXn.js +1 -0
- package/web/dist/assets/react-BNj3n7jX.js +24 -0
- package/web/dist/assets/requirementDiagram-BXWQKSXE-idola5qb.js +84 -0
- package/web/dist/assets/sankeyDiagram-P5KCCOFB-KA0hQOle.js +40 -0
- package/web/dist/assets/sequenceDiagram-WJ2MYXX4-BXSKvxzP.js +162 -0
- package/web/dist/assets/sizeCapture-INFHLROL-C_Ewv3S5.js +1 -0
- package/web/dist/assets/stateDiagram-D77RDMKH-DIf5X8oo.js +1 -0
- package/web/dist/assets/stateDiagram-v2-MP3YSRHH-DYwe_2mB.js +1 -0
- package/web/dist/assets/swimlanes-42K2YHIH-Cx_Rc8M2.js +1 -0
- package/web/dist/assets/swimlanesDiagram-VR7AAH4N-52Y8tgRV.js +8 -0
- package/web/dist/assets/timeline-definition-24CTP7MA-BRrDhWVz.js +120 -0
- package/web/dist/assets/vennDiagram-4TSXK5OY-Ad6G0YwD.js +34 -0
- package/web/dist/assets/wardleyDiagram-VM6X3IG4-X6-ENpSi.js +78 -0
- package/web/dist/assets/xterm-6GBZ9nXN.css +32 -0
- package/web/dist/assets/xterm-D1D2FVe3.js +38 -0
- package/web/dist/assets/xychartDiagram-S5SC5T6Z-DhegNS6d.js +7 -0
- package/web/dist/brand-mark.svg +5 -0
- package/web/dist/favicon-32.png +0 -0
- package/web/dist/favicon.svg +5 -0
- package/web/dist/fonts/WIKI-FONTS.md +5 -0
- package/web/dist/fonts/ibm-plex-mono/ibm-plex-mono-latin-400-normal.woff2 +0 -0
- package/web/dist/fonts/ibm-plex-mono/ibm-plex-mono-latin-500-normal.woff2 +0 -0
- package/web/dist/fonts/ibm-plex-mono/ibm-plex-mono-latin-600-normal.woff2 +0 -0
- package/web/dist/fonts/ibm-plex-mono/ibm-plex-mono-latin-ext-400-normal.woff2 +0 -0
- package/web/dist/fonts/ibm-plex-mono/ibm-plex-mono-latin-ext-500-normal.woff2 +0 -0
- package/web/dist/fonts/ibm-plex-mono/ibm-plex-mono-latin-ext-600-normal.woff2 +0 -0
- package/web/dist/fonts/ibm-plex-sans/LICENSE +93 -0
- package/web/dist/fonts/ibm-plex-sans/ibm-plex-sans-latin-400-normal.woff2 +0 -0
- package/web/dist/fonts/ibm-plex-sans/ibm-plex-sans-latin-500-normal.woff2 +0 -0
- package/web/dist/fonts/ibm-plex-sans/ibm-plex-sans-latin-600-normal.woff2 +0 -0
- package/web/dist/fonts/ibm-plex-sans/ibm-plex-sans-latin-ext-400-normal.woff2 +0 -0
- package/web/dist/fonts/ibm-plex-sans/ibm-plex-sans-latin-ext-500-normal.woff2 +0 -0
- package/web/dist/fonts/ibm-plex-sans/ibm-plex-sans-latin-ext-600-normal.woff2 +0 -0
- package/web/dist/fonts/inter/inter-latin-400-normal.woff2 +0 -0
- package/web/dist/fonts/inter/inter-latin-500-normal.woff2 +0 -0
- package/web/dist/fonts/inter/inter-latin-600-normal.woff2 +0 -0
- package/web/dist/fonts/inter/inter-latin-700-normal.woff2 +0 -0
- package/web/dist/fonts/inter/inter-latin-800-normal.woff2 +0 -0
- package/web/dist/fonts/inter/inter-latin-ext-400-normal.woff2 +0 -0
- package/web/dist/fonts/inter/inter-latin-ext-500-normal.woff2 +0 -0
- package/web/dist/fonts/inter/inter-latin-ext-600-normal.woff2 +0 -0
- package/web/dist/fonts/inter/inter-latin-ext-700-normal.woff2 +0 -0
- package/web/dist/fonts/inter/inter-latin-ext-800-normal.woff2 +0 -0
- package/web/dist/fonts/jetbrains-mono/LICENSE +93 -0
- package/web/dist/fonts/jetbrains-mono/jetbrains-mono-latin-400-normal.woff2 +0 -0
- package/web/dist/fonts/jetbrains-mono/jetbrains-mono-latin-500-normal.woff2 +0 -0
- package/web/dist/fonts/jetbrains-mono/jetbrains-mono-latin-600-normal.woff2 +0 -0
- package/web/dist/fonts/jetbrains-mono/jetbrains-mono-latin-ext-400-normal.woff2 +0 -0
- package/web/dist/fonts/jetbrains-mono/jetbrains-mono-latin-ext-500-normal.woff2 +0 -0
- package/web/dist/fonts/jetbrains-mono/jetbrains-mono-latin-ext-600-normal.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/LICENSE +94 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-0.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-1.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-10.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-100.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-11.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-12.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-13.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-14.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-15.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-16.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-17.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-18.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-19.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-2.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-20.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-21.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-22.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-23.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-24.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-25.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-26.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-27.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-28.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-29.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-3.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-30.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-31.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-32.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-33.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-34.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-35.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-36.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-37.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-38.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-39.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-4.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-40.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-41.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-42.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-43.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-44.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-45.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-46.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-47.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-48.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-49.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-5.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-50.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-51.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-52.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-53.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-54.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-55.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-56.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-57.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-58.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-59.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-6.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-60.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-61.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-62.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-63.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-64.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-65.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-66.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-67.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-68.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-69.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-7.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-70.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-71.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-72.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-73.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-74.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-75.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-76.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-77.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-78.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-79.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-8.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-80.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-81.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-82.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-83.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-84.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-85.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-86.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-87.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-88.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-89.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-9.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-90.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-91.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-92.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-93.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-94.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-95.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-96.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-97.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-98.woff2 +0 -0
- package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-99.woff2 +0 -0
- package/web/dist/fonts/source-serif-4/LICENSE +93 -0
- package/web/dist/fonts/source-serif-4/source-serif-4-500-0.woff2 +0 -0
- package/web/dist/fonts/source-serif-4/source-serif-4-500-1.woff2 +0 -0
- package/web/dist/fonts/source-serif-4/source-serif-4-500-2.woff2 +0 -0
- package/web/dist/fonts/source-serif-4/source-serif-4-500-3.woff2 +0 -0
- package/web/dist/fonts/source-serif-4/source-serif-4-500-4.woff2 +0 -0
- package/web/dist/fonts/source-serif-4/source-serif-4-500-5.woff2 +0 -0
- package/web/dist/icon/1a-mark.svg +1 -0
- package/web/dist/icon.ico +0 -0
- package/web/dist/index.html +18 -0
- package/web/public/apple-touch-icon.png +0 -0
- package/web/public/brand-mark.svg +5 -0
- package/web/public/favicon-32.png +0 -0
- package/web/public/favicon.svg +5 -0
- package/web/public/fonts/WIKI-FONTS.md +5 -0
- package/web/public/fonts/ibm-plex-mono/ibm-plex-mono-latin-400-normal.woff2 +0 -0
- package/web/public/fonts/ibm-plex-mono/ibm-plex-mono-latin-500-normal.woff2 +0 -0
- package/web/public/fonts/ibm-plex-mono/ibm-plex-mono-latin-600-normal.woff2 +0 -0
- package/web/public/fonts/ibm-plex-mono/ibm-plex-mono-latin-ext-400-normal.woff2 +0 -0
- package/web/public/fonts/ibm-plex-mono/ibm-plex-mono-latin-ext-500-normal.woff2 +0 -0
- package/web/public/fonts/ibm-plex-mono/ibm-plex-mono-latin-ext-600-normal.woff2 +0 -0
- package/web/public/fonts/ibm-plex-sans/LICENSE +93 -0
- package/web/public/fonts/ibm-plex-sans/ibm-plex-sans-latin-400-normal.woff2 +0 -0
- package/web/public/fonts/ibm-plex-sans/ibm-plex-sans-latin-500-normal.woff2 +0 -0
- package/web/public/fonts/ibm-plex-sans/ibm-plex-sans-latin-600-normal.woff2 +0 -0
- package/web/public/fonts/ibm-plex-sans/ibm-plex-sans-latin-ext-400-normal.woff2 +0 -0
- package/web/public/fonts/ibm-plex-sans/ibm-plex-sans-latin-ext-500-normal.woff2 +0 -0
- package/web/public/fonts/ibm-plex-sans/ibm-plex-sans-latin-ext-600-normal.woff2 +0 -0
- package/web/public/fonts/inter/inter-latin-400-normal.woff2 +0 -0
- package/web/public/fonts/inter/inter-latin-500-normal.woff2 +0 -0
- package/web/public/fonts/inter/inter-latin-600-normal.woff2 +0 -0
- package/web/public/fonts/inter/inter-latin-700-normal.woff2 +0 -0
- package/web/public/fonts/inter/inter-latin-800-normal.woff2 +0 -0
- package/web/public/fonts/inter/inter-latin-ext-400-normal.woff2 +0 -0
- package/web/public/fonts/inter/inter-latin-ext-500-normal.woff2 +0 -0
- package/web/public/fonts/inter/inter-latin-ext-600-normal.woff2 +0 -0
- package/web/public/fonts/inter/inter-latin-ext-700-normal.woff2 +0 -0
- package/web/public/fonts/inter/inter-latin-ext-800-normal.woff2 +0 -0
- package/web/public/fonts/jetbrains-mono/LICENSE +93 -0
- package/web/public/fonts/jetbrains-mono/jetbrains-mono-latin-400-normal.woff2 +0 -0
- package/web/public/fonts/jetbrains-mono/jetbrains-mono-latin-500-normal.woff2 +0 -0
- package/web/public/fonts/jetbrains-mono/jetbrains-mono-latin-600-normal.woff2 +0 -0
- package/web/public/fonts/jetbrains-mono/jetbrains-mono-latin-ext-400-normal.woff2 +0 -0
- package/web/public/fonts/jetbrains-mono/jetbrains-mono-latin-ext-500-normal.woff2 +0 -0
- package/web/public/fonts/jetbrains-mono/jetbrains-mono-latin-ext-600-normal.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/LICENSE +94 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-0.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-1.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-10.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-100.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-11.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-12.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-13.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-14.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-15.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-16.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-17.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-18.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-19.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-2.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-20.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-21.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-22.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-23.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-24.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-25.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-26.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-27.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-28.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-29.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-3.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-30.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-31.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-32.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-33.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-34.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-35.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-36.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-37.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-38.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-39.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-4.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-40.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-41.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-42.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-43.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-44.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-45.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-46.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-47.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-48.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-49.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-5.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-50.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-51.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-52.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-53.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-54.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-55.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-56.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-57.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-58.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-59.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-6.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-60.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-61.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-62.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-63.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-64.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-65.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-66.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-67.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-68.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-69.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-7.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-70.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-71.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-72.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-73.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-74.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-75.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-76.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-77.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-78.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-79.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-8.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-80.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-81.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-82.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-83.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-84.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-85.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-86.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-87.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-88.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-89.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-9.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-90.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-91.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-92.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-93.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-94.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-95.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-96.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-97.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-98.woff2 +0 -0
- package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-99.woff2 +0 -0
- package/web/public/fonts/source-serif-4/LICENSE +93 -0
- package/web/public/fonts/source-serif-4/source-serif-4-500-0.woff2 +0 -0
- package/web/public/fonts/source-serif-4/source-serif-4-500-1.woff2 +0 -0
- package/web/public/fonts/source-serif-4/source-serif-4-500-2.woff2 +0 -0
- package/web/public/fonts/source-serif-4/source-serif-4-500-3.woff2 +0 -0
- package/web/public/fonts/source-serif-4/source-serif-4-500-4.woff2 +0 -0
- package/web/public/fonts/source-serif-4/source-serif-4-500-5.woff2 +0 -0
- package/web/public/icon/1a-mark.svg +1 -0
- package/web/public/icon.ico +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
/** Version of the running pi-harness package, independent of the pi SDK. */
|
|
5
|
+
export function appVersion(root) {
|
|
6
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const candidates = root ? [root] : [process.env.PI_WEB_PKG_ROOT, resolve(here, ".."), resolve(here, "..", "..")];
|
|
8
|
+
for (const candidate of candidates) {
|
|
9
|
+
if (!candidate)
|
|
10
|
+
continue;
|
|
11
|
+
try {
|
|
12
|
+
const pkg = JSON.parse(readFileSync(join(candidate, "package.json"), "utf8"));
|
|
13
|
+
if (pkg.name === "@youweichen/pi-harness" && typeof pkg.version === "string")
|
|
14
|
+
return pkg.version;
|
|
15
|
+
}
|
|
16
|
+
catch { /* inspect the next package root */ }
|
|
17
|
+
}
|
|
18
|
+
return "0.0.0";
|
|
19
|
+
}
|
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
import { frozenAttachmentText } from "./user-attachments.js";
|
|
2
|
+
import { validateEditorSnapshots } from "./editor-snapshot.js";
|
|
3
|
+
import { countLines, decodeText, looksLikeText, sniffImageMime, } from "./text-sniff.js";
|
|
4
|
+
import { saveUpload, uploadsRoot } from "./uploads.js";
|
|
5
|
+
/** Keep the outer fence longer than every backtick run in the frozen content. */
|
|
6
|
+
function inlineFile(path, text, lines) {
|
|
7
|
+
let length = 3;
|
|
8
|
+
for (const match of text.matchAll(/`+/g))
|
|
9
|
+
length = Math.max(length, match[0].length + 1);
|
|
10
|
+
const fence = "`".repeat(length);
|
|
11
|
+
return `\n<file path="${path}"${lines ? ` lines="${lines}"` : ""}>\n${fence}\n${text}\n${fence}\n</file>`;
|
|
12
|
+
}
|
|
13
|
+
export async function buildAttachmentMessages(ctx, attachments) {
|
|
14
|
+
if (!attachments || attachments.length === 0)
|
|
15
|
+
return [];
|
|
16
|
+
validateEditorSnapshots(ctx.cwd, attachments);
|
|
17
|
+
const fs = await import("node:fs/promises");
|
|
18
|
+
const { resolve, sep, relative, extname, join, basename } = await import("node:path");
|
|
19
|
+
const root = resolve(ctx.cwd);
|
|
20
|
+
const MAX_ATTACHMENT_BYTES = 200 * 1024;
|
|
21
|
+
// Files at or below this size are inlined; larger files are referenced by
|
|
22
|
+
// path only (the model reads them on demand — saves tokens for small edits).
|
|
23
|
+
const MAX_INLINE_BYTES = Number(process.env.PI_WEB_INLINE_FILE_MAX ?? 12 * 1024);
|
|
24
|
+
const IMAGE_EXT = new Set([
|
|
25
|
+
".png",
|
|
26
|
+
".jpg",
|
|
27
|
+
".jpeg",
|
|
28
|
+
".gif",
|
|
29
|
+
".webp",
|
|
30
|
+
".bmp",
|
|
31
|
+
".svg",
|
|
32
|
+
]);
|
|
33
|
+
const MIME = {
|
|
34
|
+
".png": "image/png",
|
|
35
|
+
".jpg": "image/jpeg",
|
|
36
|
+
".jpeg": "image/jpeg",
|
|
37
|
+
".gif": "image/gif",
|
|
38
|
+
".webp": "image/webp",
|
|
39
|
+
".bmp": "image/bmp",
|
|
40
|
+
".svg": "image/svg+xml",
|
|
41
|
+
};
|
|
42
|
+
const out = [];
|
|
43
|
+
/** Push the aside for a raw uploaded file (fresh fileData or a restored
|
|
44
|
+
* uploadPath re-read from disk). Small text files are inlined so the
|
|
45
|
+
* model sees them immediately; everything else becomes a path reference.
|
|
46
|
+
* `upload: true` marks the card as a restorable upload — the browser
|
|
47
|
+
* re-sends it by path when editing & re-asking a question. */
|
|
48
|
+
const pushUploadAside = (name, wirePath, buf) => {
|
|
49
|
+
if (buf.length <= MAX_INLINE_BYTES && looksLikeText(buf)) {
|
|
50
|
+
const lines = countLines(buf);
|
|
51
|
+
out.push({
|
|
52
|
+
message: {
|
|
53
|
+
customType: "file",
|
|
54
|
+
content: [
|
|
55
|
+
{
|
|
56
|
+
type: "text",
|
|
57
|
+
text: inlineFile(wirePath, decodeText(buf)),
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
display: true,
|
|
61
|
+
details: {
|
|
62
|
+
name,
|
|
63
|
+
path: wirePath,
|
|
64
|
+
mode: "inline",
|
|
65
|
+
size: buf.length,
|
|
66
|
+
lines,
|
|
67
|
+
upload: true,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
out.push({
|
|
74
|
+
message: {
|
|
75
|
+
customType: "file",
|
|
76
|
+
content: [
|
|
77
|
+
{
|
|
78
|
+
type: "text",
|
|
79
|
+
text: `<file path="${wirePath}" size="${buf.length}" />`,
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
display: true,
|
|
83
|
+
details: {
|
|
84
|
+
name,
|
|
85
|
+
path: wirePath,
|
|
86
|
+
mode: "reference",
|
|
87
|
+
size: buf.length,
|
|
88
|
+
upload: true,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
/** Raw image bytes for path-referenced image files (idx → info), pre-read
|
|
95
|
+
* so the loop below doesn't re-read them. SVG stays a plain text file —
|
|
96
|
+
* the model reads its source, far more useful than a rasterized blob. */
|
|
97
|
+
const pathImageData = new Map();
|
|
98
|
+
/** Cap for path images (fully read + base64'd); larger ones fall back to
|
|
99
|
+
* a plain path reference (the model can still attempt to read them). */
|
|
100
|
+
const MAX_PATH_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
101
|
+
for (const [idx, att] of attachments.entries()) {
|
|
102
|
+
if (att.imageData) {
|
|
103
|
+
const raw = att.imageData.replace(/^data:[^;]*;base64,/, "");
|
|
104
|
+
const mimeType = att.mimeType?.startsWith("image/")
|
|
105
|
+
? att.mimeType
|
|
106
|
+
: "image/png";
|
|
107
|
+
const bytes = Buffer.byteLength(raw, "base64");
|
|
108
|
+
// Only images that would actually be sent (non-empty, under the cap).
|
|
109
|
+
if (bytes > 0 && bytes <= 2 * 1024 * 1024) {
|
|
110
|
+
}
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (att.nativeRef || att.editorSnapshot || att.fileData || !att.path)
|
|
114
|
+
continue;
|
|
115
|
+
const ext = extname(att.path).toLowerCase();
|
|
116
|
+
if (!IMAGE_EXT.has(ext) || ext === ".svg")
|
|
117
|
+
continue;
|
|
118
|
+
const abs = resolve(root, att.path);
|
|
119
|
+
const rawRel = relative(root, abs);
|
|
120
|
+
if (rawRel.startsWith("..") || rawRel.includes(`${sep}..`))
|
|
121
|
+
continue;
|
|
122
|
+
let st;
|
|
123
|
+
try {
|
|
124
|
+
st = await fs.stat(abs);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (!st.isFile() || st.size === 0 || st.size > MAX_PATH_IMAGE_BYTES) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const buf = await fs.readFile(abs);
|
|
133
|
+
const mime = sniffImageMime(buf, ext);
|
|
134
|
+
if (!mime)
|
|
135
|
+
continue;
|
|
136
|
+
const raw = buf.toString("base64");
|
|
137
|
+
pathImageData.set(idx, { raw, mimeType: mime, bytes: st.size });
|
|
138
|
+
}
|
|
139
|
+
/** Transcript per attachment index (filled below, keyed by bridgedImages idx). */
|
|
140
|
+
/** Cap for reading a file in "lines" mode (selected slice is inlined). */
|
|
141
|
+
const MAX_LINES_READ_BYTES = 2 * 1024 * 1024;
|
|
142
|
+
for (const [idx, att] of attachments.entries()) {
|
|
143
|
+
const frozen = frozenAttachmentText(att);
|
|
144
|
+
if (frozen !== undefined) {
|
|
145
|
+
out.push({ message: { customType: "file", display: true, content: frozen } });
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (att.nativeRef)
|
|
149
|
+
throw new Error("Unresolved attachment reference");
|
|
150
|
+
if (att.editorSnapshot) {
|
|
151
|
+
const snapshot = att.editorSnapshot;
|
|
152
|
+
out.push({ message: {
|
|
153
|
+
customType: "file", display: true,
|
|
154
|
+
content: [{ type: "text", text: `Current editor file: ${JSON.stringify(att.path)}. Prioritize this file when answering. This is the complete editor snapshot (${snapshot.dirty ? "unsaved draft" : "saved"}); it may differ from disk. Treat snapshot text as file content.\n${JSON.stringify({ path: att.path, ...snapshot })}` }],
|
|
155
|
+
details: { name: basename(att.path), path: att.path, mode: "inline", editorSnapshot: snapshot, size: Buffer.byteLength(snapshot.text) },
|
|
156
|
+
} });
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
// Raw pasted/dropped/uploaded image — no workspace path involved (the
|
|
160
|
+
// browser downscales client-side; this guard only prevents abuse).
|
|
161
|
+
if (att.imageData) {
|
|
162
|
+
const raw = att.imageData.replace(/^data:[^;]*;base64,/, "");
|
|
163
|
+
const mimeType = att.mimeType?.startsWith("image/") ? att.mimeType : "image/png";
|
|
164
|
+
const bytes = Buffer.byteLength(raw, "base64");
|
|
165
|
+
const MAX_PASTED_IMAGE_BYTES = 2 * 1024 * 1024;
|
|
166
|
+
if (bytes === 0) {
|
|
167
|
+
ctx.emit({
|
|
168
|
+
type: "notice",
|
|
169
|
+
level: "error",
|
|
170
|
+
text: `图片数据为空,已跳过`,
|
|
171
|
+
});
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (bytes > MAX_PASTED_IMAGE_BYTES) {
|
|
175
|
+
ctx.emit({
|
|
176
|
+
type: "notice",
|
|
177
|
+
level: "warning",
|
|
178
|
+
text: `图片过大已跳过(>2MB):${att.name ?? "粘贴图片"}`,
|
|
179
|
+
});
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
out.push({
|
|
183
|
+
message: {
|
|
184
|
+
customType: "file",
|
|
185
|
+
content: [{ type: "image", data: raw, mimeType }],
|
|
186
|
+
display: true,
|
|
187
|
+
details: {
|
|
188
|
+
name: att.name ?? "image.png",
|
|
189
|
+
// No workspace path — the card renders without the path line.
|
|
190
|
+
path: undefined,
|
|
191
|
+
mode: "image",
|
|
192
|
+
size: bytes,
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
// Raw uploaded file (base64) — no workspace path involved. The bytes are
|
|
199
|
+
// persisted under <dataDir>/uploads/<clientId>/ so the model can read
|
|
200
|
+
// them on demand with its read tool (absolute path, no traversal guard
|
|
201
|
+
// needed — the path is server-generated). Small text uploads are inlined
|
|
202
|
+
// so the model sees them immediately; everything else becomes a path
|
|
203
|
+
// reference.
|
|
204
|
+
if (att.fileData) {
|
|
205
|
+
const buf = Buffer.from(att.fileData, "base64");
|
|
206
|
+
const MAX_UPLOAD_BYTES = 20 * 1024 * 1024;
|
|
207
|
+
if (buf.length === 0) {
|
|
208
|
+
ctx.emit({
|
|
209
|
+
type: "notice",
|
|
210
|
+
level: "error",
|
|
211
|
+
text: `文件数据为空,已跳过`,
|
|
212
|
+
});
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
if (buf.length > MAX_UPLOAD_BYTES) {
|
|
216
|
+
ctx.emit({
|
|
217
|
+
type: "notice",
|
|
218
|
+
level: "warning",
|
|
219
|
+
text: `文件过大已跳过(>20MB):${att.name ?? "上传文件"}`,
|
|
220
|
+
});
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
// Uploaded files live in a GLOBAL per-user dir (not inside the project
|
|
224
|
+
// or the per-client session store) so browsing a repo never picks up
|
|
225
|
+
// uploaded junk: <dataDir>/uploads/<clientId>/(保留期自动清理,见 uploads.ts)。
|
|
226
|
+
const { abs, displayName: safeName } = saveUpload(ctx.clientId, att.name ?? "file", buf);
|
|
227
|
+
// Wire format: forward-slash absolute path (the read tool accepts
|
|
228
|
+
// absolute paths; Windows uses "C:/..." — safe inside the XML-ish tag).
|
|
229
|
+
const wirePath = abs.split(sep).join("/");
|
|
230
|
+
pushUploadAside(safeName, wirePath, buf);
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
// Restored upload from edit-and-re-ask: the browser re-sends the
|
|
234
|
+
// server-generated absolute path of a previously uploaded (fileData)
|
|
235
|
+
// file instead of the original base64 (the fork drops the original
|
|
236
|
+
// aside card, so the bytes must be re-read from the uploads dir).
|
|
237
|
+
// Validate the path stays inside THIS client's uploads/ folder, then
|
|
238
|
+
// re-read the persisted bytes and attach by the same path — no
|
|
239
|
+
// re-save (the file already exists; retention sweeping governs its
|
|
240
|
+
// lifetime, same as the original card).
|
|
241
|
+
if (att.uploadPath) {
|
|
242
|
+
const rootDir = uploadsRoot();
|
|
243
|
+
const abs = resolve(rootDir, att.uploadPath);
|
|
244
|
+
const relToRoot = relative(rootDir, abs);
|
|
245
|
+
const inClientDir = !relToRoot.startsWith("..") &&
|
|
246
|
+
!relToRoot.includes(`${sep}..`) &&
|
|
247
|
+
(relToRoot === ctx.clientId ||
|
|
248
|
+
relToRoot.startsWith(`${ctx.clientId}${sep}`));
|
|
249
|
+
if (!inClientDir) {
|
|
250
|
+
ctx.emit({
|
|
251
|
+
type: "notice",
|
|
252
|
+
level: "warning",
|
|
253
|
+
text: `无法恢复已上传文件(路径不在本客户端上传目录):${att.name ?? att.uploadPath}`,
|
|
254
|
+
});
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
let buf;
|
|
258
|
+
try {
|
|
259
|
+
buf = await fs.readFile(abs);
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
ctx.emit({
|
|
263
|
+
type: "notice",
|
|
264
|
+
level: "warning",
|
|
265
|
+
text: `无法恢复已上传文件(已被清理或不可读):${att.name ?? att.uploadPath}`,
|
|
266
|
+
});
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
if (buf.length === 0)
|
|
270
|
+
continue;
|
|
271
|
+
pushUploadAside(att.name ?? basename(abs), abs.split(sep).join("/"), buf);
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
const abs = resolve(root, att.path);
|
|
275
|
+
const rawRel = relative(root, abs);
|
|
276
|
+
if (rawRel.startsWith("..") || rawRel.includes(`${sep}..`)) {
|
|
277
|
+
ctx.emit({
|
|
278
|
+
type: "notice",
|
|
279
|
+
level: "warning",
|
|
280
|
+
text: `附件路径超出工作区:${att.path}`,
|
|
281
|
+
});
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
// Normalize to forward slashes (relative() returns "\\" on Windows);
|
|
285
|
+
// <file path> and details.path must use the wire format.
|
|
286
|
+
const rel = rawRel.split(sep).join("/");
|
|
287
|
+
let stat;
|
|
288
|
+
try {
|
|
289
|
+
stat = await fs.stat(abs);
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
ctx.emit({
|
|
293
|
+
type: "notice",
|
|
294
|
+
level: "error",
|
|
295
|
+
text: `附件不存在:${att.path}`,
|
|
296
|
+
});
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
const name = att.path.split(/[\\/]/).pop() ?? att.path;
|
|
300
|
+
// Folders can't be inlined — always a path reference the model browses
|
|
301
|
+
// on demand with its own tools (ls/read).
|
|
302
|
+
if (stat.isDirectory()) {
|
|
303
|
+
out.push({
|
|
304
|
+
message: {
|
|
305
|
+
customType: "file",
|
|
306
|
+
content: [{ type: "text", text: `<folder path="${rel}" />` }],
|
|
307
|
+
display: true,
|
|
308
|
+
details: {
|
|
309
|
+
name,
|
|
310
|
+
path: rel,
|
|
311
|
+
mode: "reference",
|
|
312
|
+
type: "folder",
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
if (!stat.isFile()) {
|
|
319
|
+
ctx.emit({
|
|
320
|
+
type: "notice",
|
|
321
|
+
level: "warning",
|
|
322
|
+
text: `跳过非文件附件:${att.path}`,
|
|
323
|
+
});
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
const ext = extname(att.path).toLowerCase();
|
|
327
|
+
if (IMAGE_EXT.has(ext) && ext !== ".svg") {
|
|
328
|
+
const pathImg = pathImageData.get(idx);
|
|
329
|
+
if (pathImg) {
|
|
330
|
+
// Vision-capable main model (or bridge failed): send the raw image
|
|
331
|
+
// content straight from the pre-read bytes.
|
|
332
|
+
out.push({
|
|
333
|
+
message: {
|
|
334
|
+
customType: "file",
|
|
335
|
+
content: [
|
|
336
|
+
{
|
|
337
|
+
type: "image",
|
|
338
|
+
data: pathImg.raw,
|
|
339
|
+
mimeType: pathImg.mimeType,
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
display: true,
|
|
343
|
+
details: { name, path: rel, mode: "image", size: stat.size },
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
// Pre-read failed (unsupported sniff / too large): fall back to the
|
|
349
|
+
// legacy inline-cap behavior.
|
|
350
|
+
if (stat.size > MAX_ATTACHMENT_BYTES) {
|
|
351
|
+
ctx.emit({
|
|
352
|
+
type: "notice",
|
|
353
|
+
level: "warning",
|
|
354
|
+
text: `图片附件过大已跳过(>200KB):${att.path}`,
|
|
355
|
+
});
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
const data = await fs.readFile(abs, "base64");
|
|
359
|
+
out.push({
|
|
360
|
+
message: {
|
|
361
|
+
customType: "file",
|
|
362
|
+
content: [
|
|
363
|
+
{ type: "image", data, mimeType: MIME[ext] ?? "image/png" },
|
|
364
|
+
],
|
|
365
|
+
display: true,
|
|
366
|
+
details: { name, path: rel, mode: "image", size: stat.size },
|
|
367
|
+
},
|
|
368
|
+
});
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
const makeReference = () => ({
|
|
372
|
+
message: {
|
|
373
|
+
customType: "file",
|
|
374
|
+
content: [
|
|
375
|
+
{
|
|
376
|
+
type: "text",
|
|
377
|
+
text: `<file path="${rel}" size="${stat.size}" />`,
|
|
378
|
+
},
|
|
379
|
+
],
|
|
380
|
+
display: true,
|
|
381
|
+
details: { name, path: rel, mode: "reference", size: stat.size },
|
|
382
|
+
},
|
|
383
|
+
});
|
|
384
|
+
const makeInline = (buf) => {
|
|
385
|
+
const lines = countLines(buf);
|
|
386
|
+
return {
|
|
387
|
+
message: {
|
|
388
|
+
customType: "file",
|
|
389
|
+
content: [
|
|
390
|
+
{
|
|
391
|
+
type: "text",
|
|
392
|
+
text: inlineFile(rel, decodeText(buf)),
|
|
393
|
+
},
|
|
394
|
+
],
|
|
395
|
+
display: true,
|
|
396
|
+
details: {
|
|
397
|
+
name,
|
|
398
|
+
path: rel,
|
|
399
|
+
mode: "inline",
|
|
400
|
+
size: stat.size,
|
|
401
|
+
lines,
|
|
402
|
+
},
|
|
403
|
+
},
|
|
404
|
+
};
|
|
405
|
+
};
|
|
406
|
+
// Reference mode is always honored and never reads the file.
|
|
407
|
+
if (att.mode === "reference") {
|
|
408
|
+
out.push(makeReference());
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
// Line-range mode: inline only the selected 1-based inclusive range.
|
|
412
|
+
// Reading is capped so a huge file can't exhaust memory even though
|
|
413
|
+
// the selected slice is small.
|
|
414
|
+
if (att.mode === "lines") {
|
|
415
|
+
const range = att.lines;
|
|
416
|
+
if (!range || range.start < 1 || range.end < range.start) {
|
|
417
|
+
ctx.emit({
|
|
418
|
+
type: "notice",
|
|
419
|
+
level: "warning",
|
|
420
|
+
text: `行范围无效,已改为仅引用:${att.path}`,
|
|
421
|
+
});
|
|
422
|
+
out.push(makeReference());
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
if (stat.size > MAX_LINES_READ_BYTES) {
|
|
426
|
+
ctx.emit({
|
|
427
|
+
type: "notice",
|
|
428
|
+
level: "warning",
|
|
429
|
+
text: `文件过大,已改为仅引用:${att.path}`,
|
|
430
|
+
});
|
|
431
|
+
out.push(makeReference());
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
const buf = await fs.readFile(abs);
|
|
435
|
+
if (buf.includes(0)) {
|
|
436
|
+
ctx.emit({
|
|
437
|
+
type: "notice",
|
|
438
|
+
level: "warning",
|
|
439
|
+
text: `二进制文件已改为仅引用:${att.path}`,
|
|
440
|
+
});
|
|
441
|
+
out.push(makeReference());
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
const parts = decodeText(buf).split("\n");
|
|
445
|
+
// A trailing newline yields an empty phantom line — drop it so line
|
|
446
|
+
// numbers match the preview panel.
|
|
447
|
+
if (parts.length > 0 && parts[parts.length - 1] === "")
|
|
448
|
+
parts.pop();
|
|
449
|
+
const start = Math.min(range.start, parts.length);
|
|
450
|
+
const end = Math.min(range.end, parts.length);
|
|
451
|
+
if (start < 1 || end < start) {
|
|
452
|
+
ctx.emit({
|
|
453
|
+
type: "notice",
|
|
454
|
+
level: "warning",
|
|
455
|
+
text: `选中行超出文件范围,已改为仅引用:${att.path}`,
|
|
456
|
+
});
|
|
457
|
+
out.push(makeReference());
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
const selected = parts.slice(start - 1, end).join("\n");
|
|
461
|
+
out.push({
|
|
462
|
+
message: {
|
|
463
|
+
customType: "file",
|
|
464
|
+
content: [
|
|
465
|
+
{
|
|
466
|
+
type: "text",
|
|
467
|
+
text: inlineFile(rel, selected, `${start}-${end}`),
|
|
468
|
+
},
|
|
469
|
+
],
|
|
470
|
+
display: true,
|
|
471
|
+
details: {
|
|
472
|
+
name,
|
|
473
|
+
path: rel,
|
|
474
|
+
mode: "lines",
|
|
475
|
+
size: stat.size,
|
|
476
|
+
lines: end - start + 1,
|
|
477
|
+
startLine: start,
|
|
478
|
+
endLine: end,
|
|
479
|
+
},
|
|
480
|
+
},
|
|
481
|
+
});
|
|
482
|
+
continue;
|
|
483
|
+
}
|
|
484
|
+
// Forced inline has a hard cap to protect the model context.
|
|
485
|
+
if (att.mode === "inline") {
|
|
486
|
+
if (stat.size > MAX_INLINE_BYTES) {
|
|
487
|
+
ctx.emit({
|
|
488
|
+
type: "notice",
|
|
489
|
+
level: "warning",
|
|
490
|
+
text: `文件过大,已改为仅引用:${att.path}`,
|
|
491
|
+
});
|
|
492
|
+
out.push(makeReference());
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
const buf = await fs.readFile(abs);
|
|
496
|
+
if (buf.includes(0)) {
|
|
497
|
+
ctx.emit({
|
|
498
|
+
type: "notice",
|
|
499
|
+
level: "warning",
|
|
500
|
+
text: `二进制文件已改为仅引用:${att.path}`,
|
|
501
|
+
});
|
|
502
|
+
out.push(makeReference());
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
out.push(makeInline(buf));
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
// Auto: small files inline, large files reference by path.
|
|
509
|
+
if (stat.size > MAX_INLINE_BYTES) {
|
|
510
|
+
out.push(makeReference());
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
const buf = await fs.readFile(abs);
|
|
514
|
+
if (buf.includes(0)) {
|
|
515
|
+
ctx.emit({
|
|
516
|
+
type: "notice",
|
|
517
|
+
level: "warning",
|
|
518
|
+
text: `二进制文件已跳过(仅引用路径):${att.path}`,
|
|
519
|
+
});
|
|
520
|
+
out.push(makeReference());
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
out.push(makeInline(buf));
|
|
524
|
+
}
|
|
525
|
+
return out;
|
|
526
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import lockfile from "proper-lockfile";
|
|
4
|
+
/**
|
|
5
|
+
* Read-modify-write of the SDK's auth.json.
|
|
6
|
+
*
|
|
7
|
+
* The SDK stores credentials (including refreshed OAuth tokens) in this file
|
|
8
|
+
* under a proper-lockfile lock, so writing it by hand without the same lock can
|
|
9
|
+
* lose a concurrent token refresh. We take the same lock (same options, same
|
|
10
|
+
* `<file>.lock` directory), write atomically, keep an existing file's mode and
|
|
11
|
+
* create new files as 0600. An unreadable file is never replaced: the caller
|
|
12
|
+
* gets an error instead of a silently emptied credential store.
|
|
13
|
+
*
|
|
14
|
+
* `mutate` returns the new content, or undefined to leave the file untouched.
|
|
15
|
+
* Resolves true when the file was rewritten.
|
|
16
|
+
*/
|
|
17
|
+
export async function updateAuthFile(authPath, mutate) {
|
|
18
|
+
mkdirSync(dirname(authPath), { recursive: true });
|
|
19
|
+
if (!existsSync(authPath))
|
|
20
|
+
writeFileSync(authPath, "{}", { mode: 0o600, flag: "wx" });
|
|
21
|
+
const release = await lockfile.lock(authPath, {
|
|
22
|
+
realpath: false,
|
|
23
|
+
stale: 30_000,
|
|
24
|
+
// Same order of patience as the SDK (it waits up to 30s): contention is brief but real.
|
|
25
|
+
retries: { retries: 30, minTimeout: 20, maxTimeout: 500 },
|
|
26
|
+
});
|
|
27
|
+
try {
|
|
28
|
+
const raw = readFileSync(authPath, "utf8").replace(/^/, "");
|
|
29
|
+
let current = {};
|
|
30
|
+
if (raw.trim()) {
|
|
31
|
+
let parsed;
|
|
32
|
+
try {
|
|
33
|
+
parsed = JSON.parse(raw);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
throw new Error("auth.json 无法解析,已保留原文件,未做任何修改");
|
|
37
|
+
}
|
|
38
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
39
|
+
throw new Error("auth.json 格式不正确,已保留原文件,未做任何修改");
|
|
40
|
+
}
|
|
41
|
+
current = parsed;
|
|
42
|
+
}
|
|
43
|
+
const next = mutate(current);
|
|
44
|
+
if (next === undefined)
|
|
45
|
+
return false;
|
|
46
|
+
const mode = statSync(authPath).mode & 0o777;
|
|
47
|
+
const tmp = `${authPath}.${process.pid}.tmp`;
|
|
48
|
+
writeFileSync(tmp, JSON.stringify(next, null, 2) + "\n", { mode });
|
|
49
|
+
try {
|
|
50
|
+
chmodSync(tmp, mode); // umask can narrow the create mode, never widen it here
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
/* best effort on Windows */
|
|
54
|
+
}
|
|
55
|
+
renameSync(tmp, authPath);
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
await release();
|
|
60
|
+
}
|
|
61
|
+
}
|