@walkhi/code-relax 0.1.0-beta.10 → 0.1.0-beta.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -44
- package/dist/bin/cleanup-accounts.mjs +63 -0
- package/dist/bin/codex-remote.mjs +387 -265
- package/dist/bin/manage-relay-admin.mjs +20 -0
- package/dist/bin/migrate-relay-db.mjs +109 -0
- package/dist/bin/publish-android-release.mjs +63 -0
- package/dist/bin/publish-harmony-release.mjs +46 -0
- package/dist/bin/release-upload-server.mjs +190 -0
- package/dist/bin/self-relay-demo.mjs +1 -1
- package/dist/bin/self-relay-server.mjs +43 -30
- package/dist/bin/set-relay-admin-password.mjs +11 -0
- package/dist/build-platform.json +1 -0
- package/dist/get-shared-service-url.ps1 +12 -4
- package/dist/shared/app-server-events.cjs +452 -235
- package/dist/shared/code-browser.cjs +159 -0
- package/dist/shared/file-changes.cjs +31 -0
- package/dist/shared/file-citations.cjs +117 -0
- package/dist/shared/file-mentions.cjs +92 -0
- package/dist/shared/message-delivery.cjs +115 -0
- package/dist/shared/p2p-data-channel.cjs +22 -0
- package/dist/shared/p2p-diagnostics.cjs +1 -1
- package/dist/shared/p2p-file-channel.cjs +87 -0
- package/dist/shared/p2p-ice-policy.cjs +22 -0
- package/dist/shared/plan-pricing.cjs +32 -0
- package/dist/shared/question-attention.cjs +100 -0
- package/dist/shared/release-version.cjs +31 -0
- package/dist/shared/server-requests.cjs +347 -67
- package/dist/shared/service-endpoints.cjs +21 -0
- package/dist/shared/timeline-sync.cjs +173 -67
- package/dist/skills/code-relax/SKILL.md +22 -17
- package/dist/src/archived-threads.mjs +22 -0
- package/dist/src/bounded-process-log.mjs +63 -0
- package/dist/src/browser-auth-operations.mjs +46 -0
- package/dist/src/celsius-client.mjs +107 -0
- package/dist/src/code-browser.mjs +99 -0
- package/dist/src/desktop-launcher.mjs +58 -36
- package/dist/src/desktop-operation.mjs +46 -0
- package/dist/src/dots-approval-timeline.mjs +99 -0
- package/dist/src/dots-avatar.mjs +42 -0
- package/dist/src/dots-chat-projection.mjs +50 -0
- package/dist/src/dots-chat-server.mjs +623 -0
- package/dist/src/dots-message-images.mjs +318 -0
- package/dist/src/dots-profile.mjs +77 -0
- package/dist/src/dots-room-activity.mjs +199 -0
- package/dist/src/dots-room-client.mjs +163 -0
- package/dist/src/dots-room-projection.mjs +94 -0
- package/dist/src/dots-schedules.mjs +66 -0
- package/dist/src/dots-summary-tasks.mjs +32 -0
- package/dist/src/dots-task-catalog.mjs +433 -0
- package/dist/src/dots-task-files.mjs +52 -0
- package/dist/src/file-transfer-range.mjs +22 -0
- package/dist/src/file-upload-session.mjs +151 -0
- package/dist/src/hosted-history-app-server.mjs +103 -0
- package/dist/src/hosted-history-cache.mjs +113 -0
- package/dist/src/lan-socket-server.mjs +271 -38
- package/dist/src/managed-app-server.mjs +398 -227
- package/dist/src/managed-endpoint.mjs +25 -0
- package/dist/src/message-upload-files.mjs +111 -0
- package/dist/src/onboarding.mjs +9 -26
- package/dist/src/plan-pricing.mjs +47 -0
- package/dist/src/platform/README.md +7 -12
- package/dist/src/platform/codex-protocol.mjs +51 -0
- package/dist/src/platform/codex-runtime.mjs +4 -0
- package/dist/src/platform/credential-store.mjs +19 -0
- package/dist/src/platform/desktop-host.mjs +3 -0
- package/dist/src/platform/desktop-permission-mode.mjs +17 -0
- package/dist/src/platform/desktop-project-registration.mjs +3 -0
- package/dist/src/platform/desktop-project-store.mjs +219 -0
- package/dist/src/platform/file-opener.mjs +9 -0
- package/dist/src/platform/linux/codex-runtime.mjs +56 -0
- package/dist/src/platform/linux/desktop-host.mjs +87 -0
- package/dist/src/platform/linux/desktop-installation.mjs +36 -0
- package/dist/src/platform/linux/desktop-project-registration.mjs +11 -0
- package/dist/src/platform/linux/network-info.mjs +11 -0
- package/dist/src/platform/linux/process.mjs +53 -0
- package/dist/src/platform/linux/project-locations.mjs +17 -0
- package/dist/src/platform/linux/resident-startup.mjs +52 -0
- package/dist/src/platform/linux/retained-runtime.mjs +76 -0
- package/dist/src/platform/linux/service-host.mjs +18 -0
- package/dist/src/platform/linux/system.mjs +50 -0
- package/dist/src/platform/linux/user-environment.mjs +31 -0
- package/dist/src/platform/macos/codex-runtime.mjs +121 -0
- package/dist/src/platform/macos/desktop-host.mjs +114 -0
- package/dist/src/platform/macos/desktop-project-registration.mjs +11 -0
- package/dist/src/platform/macos/network-info.mjs +8 -0
- package/dist/src/platform/macos/project-locations.mjs +8 -0
- package/dist/src/platform/macos/resident-startup.mjs +84 -0
- package/dist/src/platform/macos/retained-runtime.mjs +127 -0
- package/dist/src/platform/macos/service-host.mjs +18 -0
- package/dist/src/platform/macos/system.mjs +29 -0
- package/dist/src/platform/macos/user-environment.mjs +31 -0
- package/dist/src/platform/managed-user-environment.mjs +78 -0
- package/dist/src/platform/native-package.mjs +69 -0
- package/dist/src/platform/network-info.mjs +3 -0
- package/dist/src/platform/path-identity.mjs +5 -0
- package/dist/src/platform/posix/network-candidates.mjs +23 -0
- package/dist/src/platform/posix/runtime-paths.mjs +25 -0
- package/dist/src/platform/power-inhibitor.mjs +22 -0
- package/dist/src/platform/project-locations.mjs +3 -0
- package/dist/src/platform/runtime-paths.mjs +8 -0
- package/dist/src/platform/select.mjs +5 -0
- package/dist/src/platform/service-host.mjs +3 -0
- package/dist/src/platform/user-environment.mjs +3 -0
- package/dist/src/platform/windows/activate-desktop.ps1 +49 -0
- package/dist/src/platform/windows/codex-runtime.mjs +215 -107
- package/dist/src/platform/windows/desktop-helper.mjs +272 -0
- package/dist/src/platform/windows/desktop-host.mjs +45 -33
- package/dist/src/platform/windows/desktop-info.ps1 +27 -3
- package/dist/src/platform/windows/desktop-project-registration.mjs +4 -0
- package/dist/src/platform/windows/desktop-runtime.ps1 +10 -0
- package/dist/src/platform/windows/desktop-shortcuts.ps1 +6 -13
- package/dist/src/platform/windows/dots-profile.mjs +49 -0
- package/dist/src/platform/windows/keep-awake.ps1 +20 -0
- package/dist/src/platform/windows/launch-worker.mjs +41 -42
- package/dist/src/platform/windows/project-locations.mjs +16 -0
- package/dist/src/platform/windows/resident-start.js +20 -0
- package/dist/src/platform/windows/resident-startup.ps1 +27 -11
- package/dist/src/platform/windows/rustdesk-video-probe.mjs +168 -0
- package/dist/src/platform/windows/service-host.mjs +17 -22
- package/dist/src/platform/windows/start-hidden-console.ps1 +11 -2
- package/dist/src/platform/windows/user-environment.mjs +52 -50
- package/dist/src/project-create.mjs +121 -0
- package/dist/src/project-management.mjs +59 -0
- package/dist/src/project-remove.mjs +55 -0
- package/dist/src/project-update.mjs +59 -0
- package/dist/src/relay-config-import.mjs +45 -0
- package/dist/src/remote-control.mjs +51 -0
- package/dist/src/runtime-discovery.mjs +65 -41
- package/dist/src/self-relay/account-cleanup.mjs +30 -0
- package/dist/src/self-relay/account-connections.mjs +63 -0
- package/dist/src/self-relay/admin-auth.mjs +59 -0
- package/dist/src/self-relay/admin-login-page.mjs +7 -0
- package/dist/src/self-relay/admin-page.mjs +87 -71
- package/dist/src/self-relay/admin-server.mjs +68 -24
- package/dist/src/self-relay/admin-state.mjs +11 -6
- package/dist/src/self-relay/admin-style.mjs +58 -0
- package/dist/src/self-relay/admin-workstations-page.mjs +95 -0
- package/dist/src/self-relay/admin-workstations.mjs +145 -0
- package/dist/src/self-relay/android-releases.mjs +61 -0
- package/dist/src/self-relay/client.mjs +45 -45
- package/dist/src/self-relay/connection-reporting.mjs +38 -0
- package/dist/src/self-relay/connector.mjs +456 -145
- package/dist/src/self-relay/demo.mjs +1 -1
- package/dist/src/self-relay/device-info.mjs +27 -0
- package/dist/src/self-relay/device-store.mjs +10 -2
- package/dist/src/self-relay/diagnostics.mjs +54 -0
- package/dist/src/self-relay/email-accounts.mjs +195 -0
- package/dist/src/self-relay/file-wire.mjs +76 -0
- package/dist/src/self-relay/lan-authorizations.mjs +44 -2
- package/dist/src/self-relay/lifecycle.mjs +252 -259
- package/dist/src/self-relay/long-pairing.mjs +40 -0
- package/dist/src/self-relay/mysql-state.mjs +118 -0
- package/dist/src/self-relay/p2p-probe.mjs +168 -123
- package/dist/src/self-relay/phone-connections.mjs +55 -0
- package/dist/src/self-relay/privacy-page.mjs +26 -0
- package/dist/src/self-relay/public-releases.mjs +67 -0
- package/dist/src/self-relay/rtc-file-bridge.mjs +46 -0
- package/dist/src/self-relay/server.mjs +1078 -441
- package/dist/src/self-relay/usage-daily.mjs +55 -0
- package/dist/src/self-relay/usage-meter.mjs +141 -0
- package/dist/src/server.mjs +1442 -716
- package/dist/src/service-control.mjs +17 -7
- package/dist/src/service-doctor.mjs +150 -51
- package/dist/src/service-health.mjs +2 -2
- package/dist/src/service-install.mjs +16 -10
- package/dist/src/service-lifecycle.mjs +52 -42
- package/dist/src/service-power.mjs +58 -0
- package/dist/src/service-restart.mjs +45 -12
- package/dist/src/service-update-worker.mjs +42 -0
- package/dist/src/service-update.mjs +80 -0
- package/dist/src/shared-app-server.mjs +511 -307
- package/dist/src/shared-catalog.mjs +132 -50
- package/dist/src/shared-recovery.mjs +21 -13
- package/dist/src/shared-thread-stream.mjs +143 -51
- package/dist/src/skill-install.mjs +0 -13
- package/dist/src/thread-goals.mjs +40 -0
- package/dist/src/turn-model-history.mjs +47 -0
- package/dist/src/usage-reset.mjs +17 -0
- package/dist/src/workstation-downloads.mjs +120 -0
- package/package.json +48 -48
- package/tools/postinstall.mjs +186 -146
- package/dist/src/debug-ui.mjs +0 -131
- package/dist/src/platform/windows/credential-store.mjs +0 -27
- package/dist/src/platform/windows/credential-store.ps1 +0 -49
- package/dist/src/platform/windows/desktop-shortcut-run.ps1 +0 -5
- package/dist/web/activity-view.js +0 -283
- package/dist/web/approval-ui.js +0 -188
- package/dist/web/capabilities.js +0 -50
- package/dist/web/chat-transport.js +0 -196
- package/dist/web/chat.css +0 -778
- package/dist/web/chat.js +0 -3815
- package/dist/web/client-platform.js +0 -11
- package/dist/web/community.css +0 -82
- package/dist/web/community.html +0 -36
- package/dist/web/composer-controller.js +0 -97
- package/dist/web/conversation-controller.js +0 -99
- package/dist/web/disclosure-state-controller.js +0 -95
- package/dist/web/draft-controller.js +0 -103
- package/dist/web/harmony-platform.js +0 -52
- package/dist/web/highlight-powershell.min.js +0 -39
- package/dist/web/highlight.min.js +0 -1262
- package/dist/web/history-cache.js +0 -240
- package/dist/web/history-controller.js +0 -167
- package/dist/web/index.html +0 -288
- package/dist/web/lan-session.js +0 -122
- package/dist/web/link-action-controller.js +0 -212
- package/dist/web/message-send-controller.js +0 -177
- package/dist/web/message-view.js +0 -98
- package/dist/web/p2p-data-channel.js +0 -50
- package/dist/web/p2p-diagnostics.js +0 -28
- package/dist/web/p2p-probe.js +0 -123
- package/dist/web/page-resume.js +0 -28
- package/dist/web/pending-message-store.js +0 -108
- package/dist/web/queue-controller.js +0 -82
- package/dist/web/resources.json +0 -1
- package/dist/web/self-relay-session.js +0 -129
- package/dist/web/server-requests.js +0 -67
- package/dist/web/station-connection-controller.js +0 -75
- package/dist/web/task-list-view.js +0 -289
- package/dist/web/thread-attention-controller.js +0 -124
- package/dist/web/thread-context-controller.js +0 -30
- package/dist/web/thread-list-controller.js +0 -61
- package/dist/web/thread-list-sync.js +0 -86
- package/dist/web/thread-title-controller.js +0 -57
- package/dist/web/timeline-formatters.js +0 -249
- package/dist/web/timeline-reducer.js +0 -77
- package/dist/web/timeline-renderer.js +0 -161
- package/dist/web/timeline-scroll-controller.js +0 -50
- package/dist/web/timeline-sync.js +0 -67
- package/dist/web/usage-controller.js +0 -258
- package/dist/web/vendor/README.md +0 -14
- package/dist/web/vendor/highlight-powershell.min.js +0 -39
- package/dist/web/vendor/highlight.LICENSE.txt +0 -29
- package/dist/web/vendor/highlight.min.js +0 -1262
- package/dist/web/vendor/lucide.LICENSE.txt +0 -17
- package/node_modules/@img/colour/LICENSE.md +0 -82
- package/node_modules/@img/colour/README.md +0 -15
- package/node_modules/@img/colour/color.cjs +0 -1596
- package/node_modules/@img/colour/index.cjs +0 -1
- package/node_modules/@img/colour/index.d.ts +0 -929
- package/node_modules/@img/colour/package.json +0 -58
- package/node_modules/@img/sharp-win32-x64/LICENSE +0 -191
- package/node_modules/@img/sharp-win32-x64/README.md +0 -59
- package/node_modules/@img/sharp-win32-x64/index.cjs +0 -1
- package/node_modules/@img/sharp-win32-x64/lib/libvips-42.dll +0 -0
- package/node_modules/@img/sharp-win32-x64/lib/libvips-cpp-8.18.6.dll +0 -0
- package/node_modules/@img/sharp-win32-x64/lib/sharp-win32-x64-0.35.4.node +0 -0
- package/node_modules/@img/sharp-win32-x64/package.json +0 -40
- package/node_modules/@img/sharp-win32-x64/versions.json +0 -30
- package/node_modules/@node-datachannel/win32-x64-msvc/node_datachannel.node +0 -0
- package/node_modules/@node-datachannel/win32-x64-msvc/package.json +0 -20
- package/node_modules/ansi-regex/index.d.ts +0 -37
- package/node_modules/ansi-regex/index.js +0 -10
- package/node_modules/ansi-regex/license +0 -9
- package/node_modules/ansi-regex/package.json +0 -55
- package/node_modules/ansi-regex/readme.md +0 -78
- package/node_modules/ansi-styles/index.d.ts +0 -345
- package/node_modules/ansi-styles/index.js +0 -163
- package/node_modules/ansi-styles/license +0 -9
- package/node_modules/ansi-styles/package.json +0 -56
- package/node_modules/ansi-styles/readme.md +0 -152
- package/node_modules/camelcase/index.d.ts +0 -63
- package/node_modules/camelcase/index.js +0 -76
- package/node_modules/camelcase/license +0 -9
- package/node_modules/camelcase/package.json +0 -43
- package/node_modules/camelcase/readme.md +0 -99
- package/node_modules/cliui/CHANGELOG.md +0 -76
- package/node_modules/cliui/LICENSE.txt +0 -14
- package/node_modules/cliui/README.md +0 -115
- package/node_modules/cliui/index.js +0 -354
- package/node_modules/cliui/package.json +0 -65
- package/node_modules/color-convert/CHANGELOG.md +0 -54
- package/node_modules/color-convert/LICENSE +0 -21
- package/node_modules/color-convert/README.md +0 -68
- package/node_modules/color-convert/conversions.js +0 -839
- package/node_modules/color-convert/index.js +0 -81
- package/node_modules/color-convert/package.json +0 -48
- package/node_modules/color-convert/route.js +0 -97
- package/node_modules/color-name/LICENSE +0 -8
- package/node_modules/color-name/README.md +0 -11
- package/node_modules/color-name/index.js +0 -152
- package/node_modules/color-name/package.json +0 -28
- package/node_modules/decamelize/index.js +0 -13
- package/node_modules/decamelize/license +0 -21
- package/node_modules/decamelize/package.json +0 -38
- package/node_modules/decamelize/readme.md +0 -48
- package/node_modules/detect-libc/LICENSE +0 -201
- package/node_modules/detect-libc/README.md +0 -163
- package/node_modules/detect-libc/index.d.ts +0 -14
- package/node_modules/detect-libc/lib/detect-libc.js +0 -313
- package/node_modules/detect-libc/lib/elf.js +0 -39
- package/node_modules/detect-libc/lib/filesystem.js +0 -51
- package/node_modules/detect-libc/lib/process.js +0 -24
- package/node_modules/detect-libc/package.json +0 -44
- package/node_modules/dijkstrajs/.travis.yml +0 -4
- package/node_modules/dijkstrajs/CONTRIBUTING.md +0 -8
- package/node_modules/dijkstrajs/LICENSE.md +0 -19
- package/node_modules/dijkstrajs/README.md +0 -18
- package/node_modules/dijkstrajs/dijkstra.js +0 -165
- package/node_modules/dijkstrajs/package.json +0 -60
- package/node_modules/dijkstrajs/test/dijkstra.test.js +0 -96
- package/node_modules/emoji-regex/LICENSE-MIT.txt +0 -20
- package/node_modules/emoji-regex/README.md +0 -73
- package/node_modules/emoji-regex/es2015/index.js +0 -6
- package/node_modules/emoji-regex/es2015/text.js +0 -6
- package/node_modules/emoji-regex/index.d.ts +0 -23
- package/node_modules/emoji-regex/index.js +0 -6
- package/node_modules/emoji-regex/package.json +0 -50
- package/node_modules/emoji-regex/text.js +0 -6
- package/node_modules/find-up/index.d.ts +0 -137
- package/node_modules/find-up/index.js +0 -89
- package/node_modules/find-up/license +0 -9
- package/node_modules/find-up/package.json +0 -53
- package/node_modules/find-up/readme.md +0 -156
- package/node_modules/get-caller-file/LICENSE.md +0 -6
- package/node_modules/get-caller-file/README.md +0 -41
- package/node_modules/get-caller-file/index.d.ts +0 -2
- package/node_modules/get-caller-file/index.js +0 -22
- package/node_modules/get-caller-file/index.js.map +0 -1
- package/node_modules/get-caller-file/package.json +0 -42
- package/node_modules/is-fullwidth-code-point/index.d.ts +0 -17
- package/node_modules/is-fullwidth-code-point/index.js +0 -50
- package/node_modules/is-fullwidth-code-point/license +0 -9
- package/node_modules/is-fullwidth-code-point/package.json +0 -42
- package/node_modules/is-fullwidth-code-point/readme.md +0 -39
- package/node_modules/locate-path/index.d.ts +0 -83
- package/node_modules/locate-path/index.js +0 -65
- package/node_modules/locate-path/license +0 -9
- package/node_modules/locate-path/package.json +0 -45
- package/node_modules/locate-path/readme.md +0 -122
- package/node_modules/node-datachannel/.clang-format +0 -17
- package/node_modules/node-datachannel/.editorconfig +0 -12
- package/node_modules/node-datachannel/.eslintignore +0 -8
- package/node_modules/node-datachannel/.eslintrc.json +0 -27
- package/node_modules/node-datachannel/.gitmodules +0 -3
- package/node_modules/node-datachannel/.prettierignore +0 -7
- package/node_modules/node-datachannel/.prettierrc +0 -13
- package/node_modules/node-datachannel/API.md +0 -247
- package/node_modules/node-datachannel/BULDING.md +0 -26
- package/node_modules/node-datachannel/CMakeLists.txt +0 -153
- package/node_modules/node-datachannel/LICENSE +0 -373
- package/node_modules/node-datachannel/README.md +0 -129
- package/node_modules/node-datachannel/dist/cjs/index.cjs +0 -11
- package/node_modules/node-datachannel/dist/cjs/index.cjs.map +0 -1
- package/node_modules/node-datachannel/dist/cjs/lib/datachannel-stream.cjs +0 -101
- package/node_modules/node-datachannel/dist/cjs/lib/datachannel-stream.cjs.map +0 -1
- package/node_modules/node-datachannel/dist/cjs/lib/index.cjs +0 -91
- package/node_modules/node-datachannel/dist/cjs/lib/index.cjs.map +0 -1
- package/node_modules/node-datachannel/dist/cjs/lib/node-datachannel.cjs +0 -94
- package/node_modules/node-datachannel/dist/cjs/lib/node-datachannel.cjs.map +0 -1
- package/node_modules/node-datachannel/dist/cjs/lib/websocket-server.cjs +0 -45
- package/node_modules/node-datachannel/dist/cjs/lib/websocket-server.cjs.map +0 -1
- package/node_modules/node-datachannel/dist/cjs/lib/websocket.cjs +0 -8
- package/node_modules/node-datachannel/dist/cjs/lib/websocket.cjs.map +0 -1
- package/node_modules/node-datachannel/dist/cjs/polyfill/Events.cjs +0 -86
- package/node_modules/node-datachannel/dist/cjs/polyfill/Events.cjs.map +0 -1
- package/node_modules/node-datachannel/dist/cjs/polyfill/Exception.cjs +0 -34
- package/node_modules/node-datachannel/dist/cjs/polyfill/Exception.cjs.map +0 -1
- package/node_modules/node-datachannel/dist/cjs/polyfill/RTCCertificate.cjs +0 -34
- package/node_modules/node-datachannel/dist/cjs/polyfill/RTCCertificate.cjs.map +0 -1
- package/node_modules/node-datachannel/dist/cjs/polyfill/RTCDataChannel.cjs +0 -214
- package/node_modules/node-datachannel/dist/cjs/polyfill/RTCDataChannel.cjs.map +0 -1
- package/node_modules/node-datachannel/dist/cjs/polyfill/RTCDtlsTransport.cjs +0 -53
- package/node_modules/node-datachannel/dist/cjs/polyfill/RTCDtlsTransport.cjs.map +0 -1
- package/node_modules/node-datachannel/dist/cjs/polyfill/RTCError.cjs +0 -83
- package/node_modules/node-datachannel/dist/cjs/polyfill/RTCError.cjs.map +0 -1
- package/node_modules/node-datachannel/dist/cjs/polyfill/RTCIceCandidate.cjs +0 -132
- package/node_modules/node-datachannel/dist/cjs/polyfill/RTCIceCandidate.cjs.map +0 -1
- package/node_modules/node-datachannel/dist/cjs/polyfill/RTCIceTransport.cjs +0 -94
- package/node_modules/node-datachannel/dist/cjs/polyfill/RTCIceTransport.cjs.map +0 -1
- package/node_modules/node-datachannel/dist/cjs/polyfill/RTCPeerConnection.cjs +0 -434
- package/node_modules/node-datachannel/dist/cjs/polyfill/RTCPeerConnection.cjs.map +0 -1
- package/node_modules/node-datachannel/dist/cjs/polyfill/RTCSctpTransport.cjs +0 -59
- package/node_modules/node-datachannel/dist/cjs/polyfill/RTCSctpTransport.cjs.map +0 -1
- package/node_modules/node-datachannel/dist/cjs/polyfill/RTCSessionDescription.cjs +0 -45
- package/node_modules/node-datachannel/dist/cjs/polyfill/RTCSessionDescription.cjs.map +0 -1
- package/node_modules/node-datachannel/dist/cjs/polyfill/index.cjs +0 -42
- package/node_modules/node-datachannel/dist/cjs/polyfill/index.cjs.map +0 -1
- package/node_modules/node-datachannel/dist/esm/index.mjs +0 -8
- package/node_modules/node-datachannel/dist/esm/index.mjs.map +0 -1
- package/node_modules/node-datachannel/dist/esm/lib/datachannel-stream.mjs +0 -78
- package/node_modules/node-datachannel/dist/esm/lib/datachannel-stream.mjs.map +0 -1
- package/node_modules/node-datachannel/dist/esm/lib/index.mjs +0 -64
- package/node_modules/node-datachannel/dist/esm/lib/index.mjs.map +0 -1
- package/node_modules/node-datachannel/dist/esm/lib/node-datachannel.mjs +0 -78
- package/node_modules/node-datachannel/dist/esm/lib/node-datachannel.mjs.map +0 -1
- package/node_modules/node-datachannel/dist/esm/lib/websocket-server.mjs +0 -43
- package/node_modules/node-datachannel/dist/esm/lib/websocket-server.mjs.map +0 -1
- package/node_modules/node-datachannel/dist/esm/lib/websocket.mjs +0 -6
- package/node_modules/node-datachannel/dist/esm/lib/websocket.mjs.map +0 -1
- package/node_modules/node-datachannel/dist/esm/polyfill/Events.mjs +0 -82
- package/node_modules/node-datachannel/dist/esm/polyfill/Events.mjs.map +0 -1
- package/node_modules/node-datachannel/dist/esm/polyfill/Exception.mjs +0 -28
- package/node_modules/node-datachannel/dist/esm/polyfill/Exception.mjs.map +0 -1
- package/node_modules/node-datachannel/dist/esm/polyfill/RTCCertificate.mjs +0 -30
- package/node_modules/node-datachannel/dist/esm/polyfill/RTCCertificate.mjs.map +0 -1
- package/node_modules/node-datachannel/dist/esm/polyfill/RTCDataChannel.mjs +0 -210
- package/node_modules/node-datachannel/dist/esm/polyfill/RTCDataChannel.mjs.map +0 -1
- package/node_modules/node-datachannel/dist/esm/polyfill/RTCDtlsTransport.mjs +0 -49
- package/node_modules/node-datachannel/dist/esm/polyfill/RTCDtlsTransport.mjs.map +0 -1
- package/node_modules/node-datachannel/dist/esm/polyfill/RTCError.mjs +0 -79
- package/node_modules/node-datachannel/dist/esm/polyfill/RTCError.mjs.map +0 -1
- package/node_modules/node-datachannel/dist/esm/polyfill/RTCIceCandidate.mjs +0 -128
- package/node_modules/node-datachannel/dist/esm/polyfill/RTCIceCandidate.mjs.map +0 -1
- package/node_modules/node-datachannel/dist/esm/polyfill/RTCIceTransport.mjs +0 -89
- package/node_modules/node-datachannel/dist/esm/polyfill/RTCIceTransport.mjs.map +0 -1
- package/node_modules/node-datachannel/dist/esm/polyfill/RTCPeerConnection.mjs +0 -430
- package/node_modules/node-datachannel/dist/esm/polyfill/RTCPeerConnection.mjs.map +0 -1
- package/node_modules/node-datachannel/dist/esm/polyfill/RTCSctpTransport.mjs +0 -55
- package/node_modules/node-datachannel/dist/esm/polyfill/RTCSctpTransport.mjs.map +0 -1
- package/node_modules/node-datachannel/dist/esm/polyfill/RTCSessionDescription.mjs +0 -41
- package/node_modules/node-datachannel/dist/esm/polyfill/RTCSessionDescription.mjs.map +0 -1
- package/node_modules/node-datachannel/dist/esm/polyfill/index.mjs +0 -27
- package/node_modules/node-datachannel/dist/esm/polyfill/index.mjs.map +0 -1
- package/node_modules/node-datachannel/dist/types/lib/datachannel-stream.d.ts +0 -24
- package/node_modules/node-datachannel/dist/types/lib/index.d.ts +0 -241
- package/node_modules/node-datachannel/dist/types/lib/types.d.ts +0 -119
- package/node_modules/node-datachannel/dist/types/lib/websocket-server.d.ts +0 -13
- package/node_modules/node-datachannel/dist/types/lib/websocket.d.ts +0 -25
- package/node_modules/node-datachannel/dist/types/polyfill/Events.d.ts +0 -15
- package/node_modules/node-datachannel/dist/types/polyfill/RTCCertificate.d.ts +0 -9
- package/node_modules/node-datachannel/dist/types/polyfill/RTCDataChannel.d.ts +0 -29
- package/node_modules/node-datachannel/dist/types/polyfill/RTCDtlsTransport.d.ts +0 -15
- package/node_modules/node-datachannel/dist/types/polyfill/RTCError.d.ts +0 -17
- package/node_modules/node-datachannel/dist/types/polyfill/RTCIceCandidate.d.ts +0 -21
- package/node_modules/node-datachannel/dist/types/polyfill/RTCIceTransport.d.ts +0 -30
- package/node_modules/node-datachannel/dist/types/polyfill/RTCPeerConnection.d.ts +0 -64
- package/node_modules/node-datachannel/dist/types/polyfill/RTCSctpTransport.d.ts +0 -15
- package/node_modules/node-datachannel/dist/types/polyfill/RTCSessionDescription.d.ts +0 -10
- package/node_modules/node-datachannel/dist/types/polyfill/index.d.ts +0 -26
- package/node_modules/node-datachannel/jest.config.ts +0 -14
- package/node_modules/node-datachannel/package.json +0 -133
- package/node_modules/node-datachannel/rollup.config.mjs +0 -67
- package/node_modules/node-datachannel/src/cpp/data-channel-wrapper.cpp +0 -530
- package/node_modules/node-datachannel/src/cpp/data-channel-wrapper.h +0 -63
- package/node_modules/node-datachannel/src/cpp/ice-udp-mux-listener-wrapper.cpp +0 -172
- package/node_modules/node-datachannel/src/cpp/ice-udp-mux-listener-wrapper.h +0 -43
- package/node_modules/node-datachannel/src/cpp/main.cpp +0 -58
- package/node_modules/node-datachannel/src/cpp/media-audio-wrapper.cpp +0 -457
- package/node_modules/node-datachannel/src/cpp/media-audio-wrapper.h +0 -52
- package/node_modules/node-datachannel/src/cpp/media-av1packetization.cpp +0 -24
- package/node_modules/node-datachannel/src/cpp/media-av1packetization.h +0 -11
- package/node_modules/node-datachannel/src/cpp/media-av1rtppacketizer-wrapper.cpp +0 -126
- package/node_modules/node-datachannel/src/cpp/media-av1rtppacketizer-wrapper.h +0 -29
- package/node_modules/node-datachannel/src/cpp/media-direction.cpp +0 -43
- package/node_modules/node-datachannel/src/cpp/media-direction.h +0 -10
- package/node_modules/node-datachannel/src/cpp/media-h264rtppacketizer-wrapper.cpp +0 -126
- package/node_modules/node-datachannel/src/cpp/media-h264rtppacketizer-wrapper.h +0 -30
- package/node_modules/node-datachannel/src/cpp/media-h265rtppacketizer-wrapper.cpp +0 -126
- package/node_modules/node-datachannel/src/cpp/media-h265rtppacketizer-wrapper.h +0 -30
- package/node_modules/node-datachannel/src/cpp/media-h26xseparator.cpp +0 -32
- package/node_modules/node-datachannel/src/cpp/media-h26xseparator.h +0 -11
- package/node_modules/node-datachannel/src/cpp/media-mediahandler-helper.cpp +0 -31
- package/node_modules/node-datachannel/src/cpp/media-mediahandler-helper.h +0 -11
- package/node_modules/node-datachannel/src/cpp/media-pacinghandler-wrapper.cpp +0 -79
- package/node_modules/node-datachannel/src/cpp/media-pacinghandler-wrapper.h +0 -28
- package/node_modules/node-datachannel/src/cpp/media-rtcpnackresponder-wrapper.cpp +0 -68
- package/node_modules/node-datachannel/src/cpp/media-rtcpnackresponder-wrapper.h +0 -28
- package/node_modules/node-datachannel/src/cpp/media-rtcpreceivingsession-wrapper.cpp +0 -57
- package/node_modules/node-datachannel/src/cpp/media-rtcpreceivingsession-wrapper.h +0 -28
- package/node_modules/node-datachannel/src/cpp/media-rtcpsrreporter-wrapper.cpp +0 -93
- package/node_modules/node-datachannel/src/cpp/media-rtcpsrreporter-wrapper.h +0 -30
- package/node_modules/node-datachannel/src/cpp/media-rtppacketizationconfig-wrapper.cpp +0 -167
- package/node_modules/node-datachannel/src/cpp/media-rtppacketizationconfig-wrapper.h +0 -35
- package/node_modules/node-datachannel/src/cpp/media-rtppacketizer-wrapper.cpp +0 -95
- package/node_modules/node-datachannel/src/cpp/media-rtppacketizer-wrapper.h +0 -30
- package/node_modules/node-datachannel/src/cpp/media-track-wrapper.cpp +0 -458
- package/node_modules/node-datachannel/src/cpp/media-track-wrapper.h +0 -61
- package/node_modules/node-datachannel/src/cpp/media-video-wrapper.cpp +0 -526
- package/node_modules/node-datachannel/src/cpp/media-video-wrapper.h +0 -56
- package/node_modules/node-datachannel/src/cpp/peer-connection-wrapper.cpp +0 -1298
- package/node_modules/node-datachannel/src/cpp/peer-connection-wrapper.h +0 -89
- package/node_modules/node-datachannel/src/cpp/rtc-wrapper.cpp +0 -205
- package/node_modules/node-datachannel/src/cpp/rtc-wrapper.h +0 -24
- package/node_modules/node-datachannel/src/cpp/thread-safe-callback.cpp +0 -57
- package/node_modules/node-datachannel/src/cpp/thread-safe-callback.h +0 -47
- package/node_modules/node-datachannel/src/cpp/web-socket-server-wrapper.cpp +0 -275
- package/node_modules/node-datachannel/src/cpp/web-socket-server-wrapper.h +0 -41
- package/node_modules/node-datachannel/src/cpp/web-socket-wrapper.cpp +0 -796
- package/node_modules/node-datachannel/src/cpp/web-socket-wrapper.h +0 -63
- package/node_modules/node-datachannel/src/index.ts +0 -9
- package/node_modules/node-datachannel/src/lib/datachannel-stream.ts +0 -100
- package/node_modules/node-datachannel/src/lib/index.ts +0 -290
- package/node_modules/node-datachannel/src/lib/node-datachannel.ts +0 -84
- package/node_modules/node-datachannel/src/lib/types.ts +0 -169
- package/node_modules/node-datachannel/src/lib/websocket-server.ts +0 -37
- package/node_modules/node-datachannel/src/lib/websocket.ts +0 -26
- package/node_modules/node-datachannel/src/polyfill/Events.ts +0 -82
- package/node_modules/node-datachannel/src/polyfill/Exception.ts +0 -37
- package/node_modules/node-datachannel/src/polyfill/README.md +0 -41
- package/node_modules/node-datachannel/src/polyfill/RTCCertificate.ts +0 -21
- package/node_modules/node-datachannel/src/polyfill/RTCDataChannel.ts +0 -225
- package/node_modules/node-datachannel/src/polyfill/RTCDtlsTransport.ts +0 -46
- package/node_modules/node-datachannel/src/polyfill/RTCError.ts +0 -78
- package/node_modules/node-datachannel/src/polyfill/RTCIceCandidate.ts +0 -128
- package/node_modules/node-datachannel/src/polyfill/RTCIceTransport.ts +0 -90
- package/node_modules/node-datachannel/src/polyfill/RTCPeerConnection.ts +0 -527
- package/node_modules/node-datachannel/src/polyfill/RTCSctpTransport.ts +0 -51
- package/node_modules/node-datachannel/src/polyfill/RTCSessionDescription.ts +0 -41
- package/node_modules/node-datachannel/src/polyfill/index.ts +0 -38
- package/node_modules/node-datachannel/tsconfig.json +0 -21
- package/node_modules/p-limit/index.d.ts +0 -38
- package/node_modules/p-limit/index.js +0 -57
- package/node_modules/p-limit/license +0 -9
- package/node_modules/p-limit/package.json +0 -52
- package/node_modules/p-limit/readme.md +0 -101
- package/node_modules/p-locate/index.d.ts +0 -64
- package/node_modules/p-locate/index.js +0 -52
- package/node_modules/p-locate/license +0 -9
- package/node_modules/p-locate/package.json +0 -53
- package/node_modules/p-locate/readme.md +0 -90
- package/node_modules/p-try/index.d.ts +0 -39
- package/node_modules/p-try/index.js +0 -9
- package/node_modules/p-try/license +0 -9
- package/node_modules/p-try/package.json +0 -42
- package/node_modules/p-try/readme.md +0 -58
- package/node_modules/path-exists/index.d.ts +0 -28
- package/node_modules/path-exists/index.js +0 -23
- package/node_modules/path-exists/license +0 -9
- package/node_modules/path-exists/package.json +0 -39
- package/node_modules/path-exists/readme.md +0 -52
- package/node_modules/pngjs/.eslintignore +0 -2
- package/node_modules/pngjs/.eslintrc.json +0 -19
- package/node_modules/pngjs/.prettierignore +0 -3
- package/node_modules/pngjs/LICENSE +0 -20
- package/node_modules/pngjs/README.md +0 -429
- package/node_modules/pngjs/browser.js +0 -16096
- package/node_modules/pngjs/coverage/lcov-report/base.css +0 -354
- package/node_modules/pngjs/coverage/lcov-report/bitmapper.js.html +0 -881
- package/node_modules/pngjs/coverage/lcov-report/bitpacker.js.html +0 -554
- package/node_modules/pngjs/coverage/lcov-report/block-navigation.js +0 -77
- package/node_modules/pngjs/coverage/lcov-report/chunkstream.js.html +0 -647
- package/node_modules/pngjs/coverage/lcov-report/constants.js.html +0 -176
- package/node_modules/pngjs/coverage/lcov-report/crc.js.html +0 -200
- package/node_modules/pngjs/coverage/lcov-report/favicon.png +0 -0
- package/node_modules/pngjs/coverage/lcov-report/filter-pack.js.html +0 -593
- package/node_modules/pngjs/coverage/lcov-report/filter-parse-async.js.html +0 -152
- package/node_modules/pngjs/coverage/lcov-report/filter-parse-sync.js.html +0 -143
- package/node_modules/pngjs/coverage/lcov-report/filter-parse.js.html +0 -611
- package/node_modules/pngjs/coverage/lcov-report/format-normaliser.js.html +0 -359
- package/node_modules/pngjs/coverage/lcov-report/index.html +0 -426
- package/node_modules/pngjs/coverage/lcov-report/interlace.js.html +0 -365
- package/node_modules/pngjs/coverage/lcov-report/packer-async.js.html +0 -230
- package/node_modules/pngjs/coverage/lcov-report/packer-sync.js.html +0 -248
- package/node_modules/pngjs/coverage/lcov-report/packer.js.html +0 -467
- package/node_modules/pngjs/coverage/lcov-report/paeth-predictor.js.html +0 -128
- package/node_modules/pngjs/coverage/lcov-report/parser-async.js.html +0 -575
- package/node_modules/pngjs/coverage/lcov-report/parser-sync.js.html +0 -404
- package/node_modules/pngjs/coverage/lcov-report/parser.js.html +0 -950
- package/node_modules/pngjs/coverage/lcov-report/png-sync.js.html +0 -116
- package/node_modules/pngjs/coverage/lcov-report/png.js.html +0 -662
- package/node_modules/pngjs/coverage/lcov-report/prettify.css +0 -101
- package/node_modules/pngjs/coverage/lcov-report/prettify.js +0 -1007
- package/node_modules/pngjs/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/node_modules/pngjs/coverage/lcov-report/sorter.js +0 -169
- package/node_modules/pngjs/coverage/lcov-report/sync-inflate.js.html +0 -584
- package/node_modules/pngjs/coverage/lcov-report/sync-reader.js.html +0 -215
- package/node_modules/pngjs/coverage/lcov.info +0 -2177
- package/node_modules/pngjs/lib/bitmapper.js +0 -267
- package/node_modules/pngjs/lib/bitpacker.js +0 -158
- package/node_modules/pngjs/lib/chunkstream.js +0 -189
- package/node_modules/pngjs/lib/constants.js +0 -32
- package/node_modules/pngjs/lib/crc.js +0 -40
- package/node_modules/pngjs/lib/filter-pack.js +0 -171
- package/node_modules/pngjs/lib/filter-parse-async.js +0 -24
- package/node_modules/pngjs/lib/filter-parse-sync.js +0 -21
- package/node_modules/pngjs/lib/filter-parse.js +0 -177
- package/node_modules/pngjs/lib/format-normaliser.js +0 -93
- package/node_modules/pngjs/lib/interlace.js +0 -95
- package/node_modules/pngjs/lib/packer-async.js +0 -50
- package/node_modules/pngjs/lib/packer-sync.js +0 -56
- package/node_modules/pngjs/lib/packer.js +0 -129
- package/node_modules/pngjs/lib/paeth-predictor.js +0 -16
- package/node_modules/pngjs/lib/parser-async.js +0 -165
- package/node_modules/pngjs/lib/parser-sync.js +0 -108
- package/node_modules/pngjs/lib/parser.js +0 -290
- package/node_modules/pngjs/lib/png-sync.js +0 -12
- package/node_modules/pngjs/lib/png.js +0 -194
- package/node_modules/pngjs/lib/sync-inflate.js +0 -168
- package/node_modules/pngjs/lib/sync-reader.js +0 -45
- package/node_modules/pngjs/package.json +0 -73
- package/node_modules/qrcode/README.md +0 -770
- package/node_modules/qrcode/bin/qrcode +0 -159
- package/node_modules/qrcode/helper/to-sjis-browser.js +0 -2
- package/node_modules/qrcode/helper/to-sjis.js +0 -105
- package/node_modules/qrcode/lib/browser.js +0 -76
- package/node_modules/qrcode/lib/can-promise.js +0 -7
- package/node_modules/qrcode/lib/core/alignment-pattern.js +0 -83
- package/node_modules/qrcode/lib/core/alphanumeric-data.js +0 -59
- package/node_modules/qrcode/lib/core/bit-buffer.js +0 -37
- package/node_modules/qrcode/lib/core/bit-matrix.js +0 -65
- package/node_modules/qrcode/lib/core/byte-data.js +0 -30
- package/node_modules/qrcode/lib/core/error-correction-code.js +0 -135
- package/node_modules/qrcode/lib/core/error-correction-level.js +0 -50
- package/node_modules/qrcode/lib/core/finder-pattern.js +0 -22
- package/node_modules/qrcode/lib/core/format-info.js +0 -29
- package/node_modules/qrcode/lib/core/galois-field.js +0 -69
- package/node_modules/qrcode/lib/core/kanji-data.js +0 -54
- package/node_modules/qrcode/lib/core/mask-pattern.js +0 -234
- package/node_modules/qrcode/lib/core/mode.js +0 -167
- package/node_modules/qrcode/lib/core/numeric-data.js +0 -43
- package/node_modules/qrcode/lib/core/polynomial.js +0 -62
- package/node_modules/qrcode/lib/core/qrcode.js +0 -495
- package/node_modules/qrcode/lib/core/reed-solomon-encoder.js +0 -56
- package/node_modules/qrcode/lib/core/regex.js +0 -31
- package/node_modules/qrcode/lib/core/segments.js +0 -330
- package/node_modules/qrcode/lib/core/utils.js +0 -63
- package/node_modules/qrcode/lib/core/version-check.js +0 -9
- package/node_modules/qrcode/lib/core/version.js +0 -163
- package/node_modules/qrcode/lib/index.js +0 -12
- package/node_modules/qrcode/lib/renderer/canvas.js +0 -63
- package/node_modules/qrcode/lib/renderer/png.js +0 -78
- package/node_modules/qrcode/lib/renderer/svg-tag.js +0 -81
- package/node_modules/qrcode/lib/renderer/svg.js +0 -19
- package/node_modules/qrcode/lib/renderer/terminal/terminal-small.js +0 -85
- package/node_modules/qrcode/lib/renderer/terminal/terminal.js +0 -49
- package/node_modules/qrcode/lib/renderer/terminal.js +0 -9
- package/node_modules/qrcode/lib/renderer/utf8.js +0 -71
- package/node_modules/qrcode/lib/renderer/utils.js +0 -99
- package/node_modules/qrcode/lib/server.js +0 -138
- package/node_modules/qrcode/license +0 -10
- package/node_modules/qrcode/package.json +0 -77
- package/node_modules/require-directory/.jshintrc +0 -67
- package/node_modules/require-directory/.npmignore +0 -1
- package/node_modules/require-directory/.travis.yml +0 -3
- package/node_modules/require-directory/LICENSE +0 -22
- package/node_modules/require-directory/README.markdown +0 -184
- package/node_modules/require-directory/index.js +0 -86
- package/node_modules/require-directory/package.json +0 -40
- package/node_modules/require-main-filename/CHANGELOG.md +0 -26
- package/node_modules/require-main-filename/LICENSE.txt +0 -14
- package/node_modules/require-main-filename/README.md +0 -26
- package/node_modules/require-main-filename/index.js +0 -18
- package/node_modules/require-main-filename/package.json +0 -35
- package/node_modules/semver/LICENSE +0 -15
- package/node_modules/semver/README.md +0 -680
- package/node_modules/semver/bin/semver.js +0 -195
- package/node_modules/semver/classes/comparator.js +0 -143
- package/node_modules/semver/classes/index.js +0 -7
- package/node_modules/semver/classes/range.js +0 -577
- package/node_modules/semver/classes/semver.js +0 -350
- package/node_modules/semver/functions/clean.js +0 -8
- package/node_modules/semver/functions/cmp.js +0 -54
- package/node_modules/semver/functions/coerce.js +0 -62
- package/node_modules/semver/functions/compare-build.js +0 -9
- package/node_modules/semver/functions/compare-loose.js +0 -5
- package/node_modules/semver/functions/compare.js +0 -7
- package/node_modules/semver/functions/diff.js +0 -60
- package/node_modules/semver/functions/eq.js +0 -5
- package/node_modules/semver/functions/gt.js +0 -5
- package/node_modules/semver/functions/gte.js +0 -5
- package/node_modules/semver/functions/inc.js +0 -21
- package/node_modules/semver/functions/lt.js +0 -5
- package/node_modules/semver/functions/lte.js +0 -5
- package/node_modules/semver/functions/major.js +0 -5
- package/node_modules/semver/functions/minor.js +0 -5
- package/node_modules/semver/functions/neq.js +0 -5
- package/node_modules/semver/functions/parse.js +0 -18
- package/node_modules/semver/functions/patch.js +0 -5
- package/node_modules/semver/functions/prerelease.js +0 -8
- package/node_modules/semver/functions/rcompare.js +0 -5
- package/node_modules/semver/functions/rsort.js +0 -5
- package/node_modules/semver/functions/satisfies.js +0 -12
- package/node_modules/semver/functions/sort.js +0 -5
- package/node_modules/semver/functions/truncate.js +0 -48
- package/node_modules/semver/functions/valid.js +0 -8
- package/node_modules/semver/index.js +0 -93
- package/node_modules/semver/internal/constants.js +0 -37
- package/node_modules/semver/internal/debug.js +0 -11
- package/node_modules/semver/internal/identifiers.js +0 -29
- package/node_modules/semver/internal/lrucache.js +0 -42
- package/node_modules/semver/internal/parse-options.js +0 -17
- package/node_modules/semver/internal/re.js +0 -223
- package/node_modules/semver/package.json +0 -78
- package/node_modules/semver/preload.js +0 -4
- package/node_modules/semver/range.bnf +0 -17
- package/node_modules/semver/ranges/gtr.js +0 -6
- package/node_modules/semver/ranges/intersects.js +0 -9
- package/node_modules/semver/ranges/ltr.js +0 -6
- package/node_modules/semver/ranges/max-satisfying.js +0 -27
- package/node_modules/semver/ranges/min-satisfying.js +0 -26
- package/node_modules/semver/ranges/min-version.js +0 -63
- package/node_modules/semver/ranges/outside.js +0 -82
- package/node_modules/semver/ranges/simplify.js +0 -49
- package/node_modules/semver/ranges/subset.js +0 -249
- package/node_modules/semver/ranges/to-comparators.js +0 -10
- package/node_modules/semver/ranges/valid.js +0 -13
- package/node_modules/set-blocking/CHANGELOG.md +0 -26
- package/node_modules/set-blocking/LICENSE.txt +0 -14
- package/node_modules/set-blocking/README.md +0 -31
- package/node_modules/set-blocking/index.js +0 -7
- package/node_modules/set-blocking/package.json +0 -42
- package/node_modules/sharp/LICENSE +0 -191
- package/node_modules/sharp/README.md +0 -112
- package/node_modules/sharp/dist/channel.cjs +0 -177
- package/node_modules/sharp/dist/channel.mjs +0 -177
- package/node_modules/sharp/dist/colour.cjs +0 -199
- package/node_modules/sharp/dist/colour.mjs +0 -199
- package/node_modules/sharp/dist/composite.cjs +0 -213
- package/node_modules/sharp/dist/composite.mjs +0 -213
- package/node_modules/sharp/dist/constructor.cjs +0 -511
- package/node_modules/sharp/dist/constructor.mjs +0 -511
- package/node_modules/sharp/dist/index.cjs +0 -25
- package/node_modules/sharp/dist/index.d.cts +0 -1999
- package/node_modules/sharp/dist/index.d.mts +0 -2046
- package/node_modules/sharp/dist/index.mjs +0 -25
- package/node_modules/sharp/dist/input.cjs +0 -819
- package/node_modules/sharp/dist/input.mjs +0 -819
- package/node_modules/sharp/dist/is.cjs +0 -143
- package/node_modules/sharp/dist/is.mjs +0 -143
- package/node_modules/sharp/dist/libvips.cjs +0 -212
- package/node_modules/sharp/dist/libvips.mjs +0 -212
- package/node_modules/sharp/dist/operation.cjs +0 -1002
- package/node_modules/sharp/dist/operation.mjs +0 -1002
- package/node_modules/sharp/dist/output.cjs +0 -1785
- package/node_modules/sharp/dist/output.mjs +0 -1785
- package/node_modules/sharp/dist/resize.cjs +0 -617
- package/node_modules/sharp/dist/resize.mjs +0 -617
- package/node_modules/sharp/dist/sharp.cjs +0 -174
- package/node_modules/sharp/dist/sharp.mjs +0 -174
- package/node_modules/sharp/dist/utility.cjs +0 -301
- package/node_modules/sharp/dist/utility.mjs +0 -301
- package/node_modules/sharp/install/build.js +0 -38
- package/node_modules/sharp/lib/index.d.ts +0 -1999
- package/node_modules/sharp/package.json +0 -227
- package/node_modules/sharp/src/binding.gyp +0 -303
- package/node_modules/sharp/src/common.cc +0 -1186
- package/node_modules/sharp/src/common.h +0 -421
- package/node_modules/sharp/src/metadata.cc +0 -404
- package/node_modules/sharp/src/metadata.h +0 -95
- package/node_modules/sharp/src/operations.cc +0 -516
- package/node_modules/sharp/src/operations.h +0 -137
- package/node_modules/sharp/src/pipeline.cc +0 -1948
- package/node_modules/sharp/src/pipeline.h +0 -422
- package/node_modules/sharp/src/sharp.cc +0 -43
- package/node_modules/sharp/src/stats.cc +0 -186
- package/node_modules/sharp/src/stats.h +0 -62
- package/node_modules/sharp/src/utilities.cc +0 -289
- package/node_modules/sharp/src/utilities.h +0 -22
- package/node_modules/string-width/index.d.ts +0 -29
- package/node_modules/string-width/index.js +0 -47
- package/node_modules/string-width/license +0 -9
- package/node_modules/string-width/package.json +0 -56
- package/node_modules/string-width/readme.md +0 -50
- package/node_modules/strip-ansi/index.d.ts +0 -17
- package/node_modules/strip-ansi/index.js +0 -4
- package/node_modules/strip-ansi/license +0 -9
- package/node_modules/strip-ansi/package.json +0 -54
- package/node_modules/strip-ansi/readme.md +0 -46
- package/node_modules/which-module/LICENSE +0 -13
- package/node_modules/which-module/README.md +0 -58
- package/node_modules/which-module/index.js +0 -9
- package/node_modules/which-module/package.json +0 -41
- package/node_modules/wrap-ansi/index.js +0 -186
- package/node_modules/wrap-ansi/license +0 -9
- package/node_modules/wrap-ansi/package.json +0 -61
- package/node_modules/wrap-ansi/readme.md +0 -97
- package/node_modules/ws/LICENSE +0 -20
- package/node_modules/ws/README.md +0 -548
- package/node_modules/ws/browser.js +0 -8
- package/node_modules/ws/index.js +0 -22
- package/node_modules/ws/lib/buffer-util.js +0 -131
- package/node_modules/ws/lib/constants.js +0 -19
- package/node_modules/ws/lib/event-target.js +0 -292
- package/node_modules/ws/lib/extension.js +0 -203
- package/node_modules/ws/lib/limiter.js +0 -55
- package/node_modules/ws/lib/permessage-deflate.js +0 -530
- package/node_modules/ws/lib/receiver.js +0 -743
- package/node_modules/ws/lib/sender.js +0 -607
- package/node_modules/ws/lib/stream.js +0 -161
- package/node_modules/ws/lib/subprotocol.js +0 -62
- package/node_modules/ws/lib/validation.js +0 -152
- package/node_modules/ws/lib/websocket-server.js +0 -562
- package/node_modules/ws/lib/websocket.js +0 -1407
- package/node_modules/ws/package.json +0 -74
- package/node_modules/ws/wrapper.mjs +0 -21
- package/node_modules/y18n/CHANGELOG.md +0 -35
- package/node_modules/y18n/LICENSE +0 -13
- package/node_modules/y18n/README.md +0 -109
- package/node_modules/y18n/index.js +0 -188
- package/node_modules/y18n/package.json +0 -39
- package/node_modules/yargs/CHANGELOG.md +0 -420
- package/node_modules/yargs/LICENSE +0 -21
- package/node_modules/yargs/README.md +0 -140
- package/node_modules/yargs/build/lib/apply-extends.d.ts +0 -2
- package/node_modules/yargs/build/lib/apply-extends.js +0 -65
- package/node_modules/yargs/build/lib/argsert.d.ts +0 -2
- package/node_modules/yargs/build/lib/argsert.js +0 -65
- package/node_modules/yargs/build/lib/command.d.ts +0 -64
- package/node_modules/yargs/build/lib/command.js +0 -416
- package/node_modules/yargs/build/lib/common-types.d.ts +0 -36
- package/node_modules/yargs/build/lib/common-types.js +0 -25
- package/node_modules/yargs/build/lib/completion-templates.d.ts +0 -2
- package/node_modules/yargs/build/lib/completion-templates.js +0 -50
- package/node_modules/yargs/build/lib/completion.d.ts +0 -21
- package/node_modules/yargs/build/lib/completion.js +0 -135
- package/node_modules/yargs/build/lib/is-promise.d.ts +0 -1
- package/node_modules/yargs/build/lib/is-promise.js +0 -9
- package/node_modules/yargs/build/lib/levenshtein.d.ts +0 -1
- package/node_modules/yargs/build/lib/levenshtein.js +0 -58
- package/node_modules/yargs/build/lib/middleware.d.ts +0 -10
- package/node_modules/yargs/build/lib/middleware.js +0 -57
- package/node_modules/yargs/build/lib/obj-filter.d.ts +0 -1
- package/node_modules/yargs/build/lib/obj-filter.js +0 -14
- package/node_modules/yargs/build/lib/parse-command.d.ts +0 -11
- package/node_modules/yargs/build/lib/parse-command.js +0 -36
- package/node_modules/yargs/build/lib/process-argv.d.ts +0 -2
- package/node_modules/yargs/build/lib/process-argv.js +0 -31
- package/node_modules/yargs/build/lib/usage.d.ts +0 -49
- package/node_modules/yargs/build/lib/usage.js +0 -540
- package/node_modules/yargs/build/lib/validation.d.ts +0 -34
- package/node_modules/yargs/build/lib/validation.js +0 -330
- package/node_modules/yargs/build/lib/yargs.d.ts +0 -274
- package/node_modules/yargs/build/lib/yargs.js +0 -1190
- package/node_modules/yargs/build/lib/yerror.d.ts +0 -4
- package/node_modules/yargs/build/lib/yerror.js +0 -11
- package/node_modules/yargs/index.js +0 -40
- package/node_modules/yargs/locales/be.json +0 -46
- package/node_modules/yargs/locales/de.json +0 -46
- package/node_modules/yargs/locales/en.json +0 -51
- package/node_modules/yargs/locales/es.json +0 -46
- package/node_modules/yargs/locales/fi.json +0 -49
- package/node_modules/yargs/locales/fr.json +0 -53
- package/node_modules/yargs/locales/hi.json +0 -49
- package/node_modules/yargs/locales/hu.json +0 -46
- package/node_modules/yargs/locales/id.json +0 -50
- package/node_modules/yargs/locales/it.json +0 -46
- package/node_modules/yargs/locales/ja.json +0 -51
- package/node_modules/yargs/locales/ko.json +0 -49
- package/node_modules/yargs/locales/nb.json +0 -44
- package/node_modules/yargs/locales/nl.json +0 -49
- package/node_modules/yargs/locales/nn.json +0 -44
- package/node_modules/yargs/locales/pirate.json +0 -13
- package/node_modules/yargs/locales/pl.json +0 -49
- package/node_modules/yargs/locales/pt.json +0 -45
- package/node_modules/yargs/locales/pt_BR.json +0 -48
- package/node_modules/yargs/locales/ru.json +0 -46
- package/node_modules/yargs/locales/th.json +0 -46
- package/node_modules/yargs/locales/tr.json +0 -48
- package/node_modules/yargs/locales/zh_CN.json +0 -48
- package/node_modules/yargs/locales/zh_TW.json +0 -47
- package/node_modules/yargs/package.json +0 -92
- package/node_modules/yargs/yargs.js +0 -14
- package/node_modules/yargs-parser/CHANGELOG.md +0 -601
- package/node_modules/yargs-parser/LICENSE.txt +0 -14
- package/node_modules/yargs-parser/README.md +0 -449
- package/node_modules/yargs-parser/index.js +0 -1032
- package/node_modules/yargs-parser/lib/tokenize-arg-string.js +0 -40
- package/node_modules/yargs-parser/package.json +0 -46
package/dist/src/server.mjs
CHANGED
|
@@ -1,126 +1,286 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
|
+
import { RemoteControl } from './remote-control.mjs';
|
|
3
|
+
import { RustDeskVideoProbe } from './platform/windows/rustdesk-video-probe.mjs';
|
|
2
4
|
import fs from 'node:fs';
|
|
3
5
|
import http from 'node:http';
|
|
6
|
+
import { createPowerManager } from './service-power.mjs';
|
|
4
7
|
import path from 'node:path';
|
|
5
8
|
import os from 'node:os';
|
|
6
9
|
import { fileURLToPath } from 'node:url';
|
|
7
|
-
import vm from 'node:vm';
|
|
8
|
-
import sharp from 'sharp';
|
|
10
|
+
import vm from 'node:vm';
|
|
11
|
+
import sharp from 'sharp';
|
|
9
12
|
import { attachLanSocket } from './lan-socket-server.mjs';
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
13
|
+
import { connectionReporting } from './self-relay/connection-reporting.mjs';
|
|
14
|
+
import fileMentions from '../shared/file-mentions.cjs';
|
|
15
|
+
import fileCitations from '../shared/file-citations.cjs';
|
|
16
|
+
import { validateInlineImage } from './message-images.mjs';
|
|
17
|
+
import { ThreadWriterConflictError } from './shared-app-server.mjs';
|
|
18
|
+
import { readThreadGoal, changeThreadGoal } from './thread-goals.mjs';
|
|
19
|
+
import { ManagedAppServer } from './managed-app-server.mjs';
|
|
20
|
+
import { RuntimeDiscovery } from './runtime-discovery.mjs';
|
|
21
|
+
import { streamSharedThread, threadListNotificationMethods } from './shared-thread-stream.mjs';
|
|
15
22
|
import appServerEvents from '../shared/app-server-events.cjs';
|
|
16
|
-
import
|
|
23
|
+
import questionAttention from '../shared/question-attention.cjs';
|
|
24
|
+
import messageDelivery from '../shared/message-delivery.cjs';
|
|
25
|
+
import fileChanges from '../shared/file-changes.cjs';
|
|
26
|
+
import { runtimePaths } from './platform/runtime-paths.mjs';
|
|
17
27
|
import { openControl } from './service-control.mjs';
|
|
18
28
|
import { writeRecord, readRecord } from './service-lifecycle.mjs';
|
|
19
29
|
import { ExecutionHealth } from './execution-health.mjs';
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
30
|
+
import { authorizeLanDevice, onLanAuthorizationsChanged, recordLanConnection } from './self-relay/lan-authorizations.mjs';
|
|
31
|
+
import { createRelayManager } from './self-relay/lifecycle.mjs';
|
|
22
32
|
import { generateAndSetThreadTitle } from './thread-title-generation.mjs';
|
|
23
|
-
import { desktopHost } from './platform/
|
|
33
|
+
import { desktopHost } from './platform/desktop-host.mjs';
|
|
24
34
|
import { SharedCatalog } from './shared-catalog.mjs';
|
|
35
|
+
import { DotsAppServer } from './dots-task-catalog.mjs';
|
|
36
|
+
import { DotsChatServer } from './dots-chat-server.mjs';
|
|
37
|
+
import { BrowserAuthOperations } from './browser-auth-operations.mjs';
|
|
38
|
+
import { HostedHistoryCache } from './hosted-history-cache.mjs';
|
|
39
|
+
import { readDotsSummaryTasks } from './dots-summary-tasks.mjs';
|
|
40
|
+
import { readDotsSchedules } from './dots-schedules.mjs';
|
|
41
|
+
import { DotsMessageImages } from './dots-message-images.mjs';
|
|
42
|
+
import { desktopDotsProfiles } from './platform/windows/dots-profile.mjs';
|
|
43
|
+
import { readPlanPricing } from './plan-pricing.mjs';
|
|
25
44
|
import { requestRecovery, recoveryStatus } from './shared-recovery.mjs';
|
|
45
|
+
import { DesktopOperation } from './desktop-operation.mjs';
|
|
26
46
|
import { requestServiceRestart } from './service-restart.mjs';
|
|
47
|
+
import { checkServiceUpdate, requestServiceUpdate, serviceUpdateStatus } from './service-update.mjs';
|
|
48
|
+
import { TurnModelHistory } from './turn-model-history.mjs';
|
|
49
|
+
import { browseCode } from './code-browser.mjs';
|
|
50
|
+
import { fileRange, fileRevision, FileRangeError } from './file-transfer-range.mjs';
|
|
51
|
+
import { browseProjectFolders, createProjectFolder } from './project-management.mjs';
|
|
52
|
+
import { projectLocations } from './platform/project-locations.mjs';
|
|
53
|
+
import { createWorkstationProject } from './project-create.mjs';
|
|
54
|
+
import { removeWorkstationProject } from './project-remove.mjs';
|
|
55
|
+
import { archivedThreadsPage } from './archived-threads.mjs';
|
|
56
|
+
import { updateWorkstationProject } from './project-update.mjs';
|
|
57
|
+
import { WorkstationDownloads } from './workstation-downloads.mjs';
|
|
58
|
+
import { stageWorkstationMessageFile } from './message-upload-files.mjs';
|
|
59
|
+
import { consumeUsageReset } from './usage-reset.mjs';
|
|
60
|
+
import { DesktopHelper } from './platform/windows/desktop-helper.mjs';
|
|
27
61
|
|
|
28
|
-
const
|
|
62
|
+
const turnModelHistory = new TurnModelHistory();
|
|
63
|
+
const messageReceipts = new messageDelivery.Store();
|
|
64
|
+
const desktopHelper = process.platform === 'win32' ? new DesktopHelper() : null;
|
|
65
|
+
const remoteControl = new RemoteControl(desktopHelper);
|
|
66
|
+
|
|
67
|
+
const MAX_IMAGE_PREVIEW_BYTES = 20 * 1024 * 1024;
|
|
29
68
|
const MAX_CACHED_ATTACHMENTS = 32;
|
|
30
69
|
const MAX_CACHED_FILES = 64;
|
|
31
70
|
const MAX_CONTEXT_TAIL_BYTES = 4 * 1024 * 1024;
|
|
32
|
-
const MAX_LIVE_ACTIVITY_TAIL_BYTES = 1024 * 1024;
|
|
33
|
-
const SHARED_HISTORY_PAGE_BYTES = 500 * 1024;
|
|
34
|
-
const SHARED_HISTORY_PAGE_TURNS = 8;
|
|
71
|
+
const MAX_LIVE_ACTIVITY_TAIL_BYTES = 1024 * 1024;
|
|
72
|
+
const SHARED_HISTORY_PAGE_BYTES = 500 * 1024;
|
|
73
|
+
const SHARED_HISTORY_PAGE_TURNS = 8;
|
|
35
74
|
const MAX_SETTINGS_SCAN_BYTES = 64 * 1024 * 1024;
|
|
36
|
-
const ALLOWED_MODELS = new Set([
|
|
37
|
-
'gpt-6-astra',
|
|
38
|
-
'gpt-5.6-sol',
|
|
39
|
-
'gpt-5.6-terra',
|
|
40
|
-
'gpt-5.6-luna',
|
|
41
|
-
'gpt-5.5',
|
|
42
|
-
'gpt-5.3-codex-spark',
|
|
43
|
-
]);
|
|
44
75
|
const ALLOWED_THINKING = new Set(['low', 'medium', 'high', 'xhigh', 'max', 'ultra']);
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
76
|
+
|
|
77
|
+
class RequestError extends Error {
|
|
78
|
+
constructor(statusCode, message, details) {
|
|
79
|
+
super(message);
|
|
80
|
+
this.statusCode = statusCode;
|
|
81
|
+
this.details = details;
|
|
82
|
+
this.code = details?.code;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function readRuntimeVersion() {
|
|
87
|
+
let directory = path.dirname(fileURLToPath(import.meta.url));
|
|
88
|
+
while (true) {
|
|
89
|
+
const manifest = readRecord(path.join(directory, 'package.json'));
|
|
90
|
+
if (manifest?.name === '@walkhi/code-relax') return manifest.version || '未知';
|
|
91
|
+
const parent = path.dirname(directory);
|
|
92
|
+
if (parent === directory) return '未知';
|
|
93
|
+
directory = parent;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const options = parseOptions(process.argv.slice(2));
|
|
98
|
+
const runtimeVersion = readRuntimeVersion();
|
|
99
|
+
const serviceInstanceId = crypto.randomUUID();
|
|
100
|
+
|
|
101
|
+
function runtimeDiagnostic(event, details) {
|
|
102
|
+
console.log('[CodeRelax:runtime]', JSON.stringify({ at: new Date().toISOString(),
|
|
103
|
+
serviceVersion: runtimeVersion, servicePid: process.pid, serviceInstanceId, event, ...details }));
|
|
104
|
+
}
|
|
59
105
|
|
|
60
106
|
const webRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'web');
|
|
61
|
-
const repositoryRoot = path.resolve(webRoot, '..', '..');
|
|
62
107
|
const projectRoot = os.homedir();
|
|
63
108
|
const { codexDataRoot, stateRoot, sessionsRoot, uploadsRoot } = runtimePaths();
|
|
109
|
+
const desktopVideoProbe = process.platform === 'win32' ? new RustDeskVideoProbe({ stateRoot }) : null;
|
|
110
|
+
process.once('exit', () => desktopVideoProbe?.stop());
|
|
111
|
+
const workstationDownloads = new WorkstationDownloads(stateRoot);
|
|
64
112
|
const markdownItScript = fs.readFileSync(path.join(webRoot, 'vendor', 'markdown-it.umd.min.js'));
|
|
65
113
|
const markdownSandbox = { atob };
|
|
66
114
|
vm.runInNewContext(markdownItScript.toString(), markdownSandbox);
|
|
67
115
|
const imageMarkdown = markdownSandbox.markdownit({ html: false });
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const allowedUploadsRoots = [uploadsRoot, legacyUploadsRoot,
|
|
75
|
-
...JSON.parse(process.env.CODEX_REMOTE_LEGACY_UPLOADS_ROOTS || '[]')];
|
|
116
|
+
fileCitations.install(imageMarkdown);
|
|
117
|
+
const attachmentCache = new Map();
|
|
118
|
+
const fileCache = new Map();
|
|
119
|
+
const fileCapabilitySecret = crypto.randomBytes(32);
|
|
120
|
+
const imageCapabilityKey = crypto.createHash('sha256')
|
|
121
|
+
.update('code-relax-image-capability\0').update(options.token || fileCapabilitySecret).digest();
|
|
76
122
|
const sessionPathCache = new Map();
|
|
77
123
|
const sessionSettingsCache = new Map();
|
|
78
124
|
const liveActivityCache = new Map();
|
|
79
|
-
let sharedServer = null;
|
|
80
|
-
const managedAppServer = new ManagedAppServer({ stateRoot, host: desktopHost });
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
return
|
|
114
|
-
|
|
115
|
-
|
|
125
|
+
let sharedServer = null;
|
|
126
|
+
const managedAppServer = new ManagedAppServer({ stateRoot, host: desktopHost, diagnose: runtimeDiagnostic });
|
|
127
|
+
const hostedHistoryCache = new HostedHistoryCache(stateRoot);
|
|
128
|
+
const dotsServer = new DotsAppServer(managedAppServer.client, desktopDotsProfiles(codexDataRoot, stateRoot), hostedHistoryCache, { uploadsRoot });
|
|
129
|
+
const dotsImages = new DotsMessageImages({ localServer: managedAppServer.client, identity: () => dotsServer.profileIdentity,
|
|
130
|
+
cache: attachmentCache, capabilityKey: imageCapabilityKey, maxEntries: MAX_CACHED_ATTACHMENTS, maxBytes: MAX_IMAGE_PREVIEW_BYTES });
|
|
131
|
+
const dotsChat = new DotsChatServer(dotsServer, undefined, { uploadsRoot, browserAuthOperations: new BrowserAuthOperations(stateRoot, codexDataRoot) });
|
|
132
|
+
managedAppServer.on('state', state => { if (state.ready) dotsChat.startBackgroundActivity(); });
|
|
133
|
+
const runtimeDiscovery = new RuntimeDiscovery({ stateRoot, host: desktopHost,
|
|
134
|
+
url: () => managedAppServer.snapshot().url, diagnose: runtimeDiagnostic });
|
|
135
|
+
const desktopOperation = new DesktopOperation({ stateRoot, host: desktopHost, sample: () => runtimeDiscovery.sample() });
|
|
136
|
+
const relayManager = createRelayManager(stateRoot);
|
|
137
|
+
let sharedCatalog = null, executionMode = 'ordinary';
|
|
138
|
+
function selectSharedExecution(enabled) {
|
|
139
|
+
const next = enabled ? managedAppServer.client : null;
|
|
140
|
+
if (sharedServer === next) return;
|
|
141
|
+
sharedServer = next;
|
|
142
|
+
sharedCatalog = sharedServer ? new SharedCatalog(sharedServer, dotsServer, dotsChat) : null;
|
|
143
|
+
}
|
|
144
|
+
const executionHealth = new ExecutionHealth(async () => {
|
|
145
|
+
await managedAppServer.ensure();
|
|
146
|
+
const managed = managedAppServer.snapshot();
|
|
147
|
+
// The managed app-server owns execution. Desktop is another client of the
|
|
148
|
+
// same service, so closing Desktop must not disable phone writes.
|
|
149
|
+
selectSharedExecution(managed.ready);
|
|
150
|
+
executionMode = sharedServer ? 'shared' : 'ordinary';
|
|
151
|
+
const managedRestarting = managed.state === 'restarting';
|
|
152
|
+
const ready = !managedRestarting && sharedServer?.ready === true;
|
|
153
|
+
const state = managedRestarting ? 'restarting'
|
|
154
|
+
: ready ? 'full' : 'unavailable';
|
|
155
|
+
return { state, ready, canWrite: ready, mode: executionMode };
|
|
156
|
+
});
|
|
157
|
+
function workstationHealth() {
|
|
158
|
+
const executionReady = executionHealth.state.ready && sharedServer?.ready === true;
|
|
159
|
+
return {
|
|
160
|
+
status: 'ok', resident: true, version: runtimeVersion, serviceInstanceId,
|
|
161
|
+
keepAwake: powerManager.snapshot(),
|
|
162
|
+
serviceUpdate: serviceUpdateStatus(stateRoot),
|
|
163
|
+
execution: { ...executionHealth.state, ready: executionReady, canWrite: executionReady,
|
|
164
|
+
mode: executionMode },
|
|
165
|
+
managedAppServer: managedAppServer.snapshot(),
|
|
166
|
+
...runtimeDiscovery.state,
|
|
167
|
+
desktopOperation: desktopOperation.snapshot(),
|
|
168
|
+
sharedAppServerConnected: sharedServer?.ready === true,
|
|
169
|
+
sharedRecoveryAvailable: runtimeDiscovery.state.desktop?.installed === true,
|
|
170
|
+
mode: options.lan ? 'lan' : 'loopback',
|
|
171
|
+
tokenRequired: Boolean(options.token),
|
|
172
|
+
nativeImageTransport: sharedServer ? 'shared-app-server' : null,
|
|
173
|
+
transportMode: sharedServer?.ready === true ? 'lan-shared' : 'unavailable',
|
|
174
|
+
};
|
|
175
|
+
}
|
|
116
176
|
|
|
177
|
+
async function refreshExecution() {
|
|
178
|
+
await executionHealth.refresh();
|
|
179
|
+
return { mode: executionMode };
|
|
180
|
+
}
|
|
181
|
+
async function requireExecutionWritable(threadId, client = sharedServer) {
|
|
182
|
+
const execution = await executionHealth.refresh();
|
|
183
|
+
if (!execution.canWrite) {
|
|
184
|
+
throw new RequestError(503, 'Codex 内核未就绪。请重启工作站服务;若仍失败,运行 relax doctor。',
|
|
185
|
+
{ code: 'full_control_required', execution });
|
|
186
|
+
}
|
|
187
|
+
if (threadId && client) client.requireWritable(threadId);
|
|
188
|
+
return execution;
|
|
189
|
+
}
|
|
190
|
+
managedAppServer.client.on('subscription-error', error => console.error('Codex 内核任务取消订阅失败:', error.message));
|
|
191
|
+
dotsServer.on('subscription-error', error => console.error('Dots 任务取消订阅失败:', error.message));
|
|
192
|
+
dotsChat.on('subscription-error', error => console.error('Dots 对话取消订阅失败:', error.message));
|
|
193
|
+
dotsChat.on('activity-error', error => console.error('点点状态通道不可用:', error.message));
|
|
194
|
+
|
|
195
|
+
dotsServer.on('profile-error', error => console.error(error.message));
|
|
117
196
|
async function handleRequest(request, response) {
|
|
197
|
+
let messageSubmissionStarted = false;
|
|
118
198
|
try {
|
|
119
199
|
const url = new URL(request.url || '/', `http://${request.headers.host || 'localhost'}`);
|
|
120
200
|
|
|
121
201
|
if (!request.socketRequest) {
|
|
122
|
-
|
|
123
|
-
|
|
202
|
+
sendJson(response, 404, { error: '此端口不提供网页。' });
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (request.method === 'POST' && url.pathname === '/api/remote-control') {
|
|
207
|
+
try { sendJson(response, 200, await remoteControl.request(await readJsonBody(request), request.controlContext)); }
|
|
208
|
+
catch (error) { throw new RequestError(400, error.message); }
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const remoteDesktopControl = ['/api/desktop/video-probe', '/api/desktop/frame', '/api/desktop/move', '/api/desktop/click'].includes(url.pathname)
|
|
213
|
+
|| url.pathname.startsWith('/api/desktop/frames/');
|
|
214
|
+
if (process.platform !== 'win32' && remoteDesktopControl)
|
|
215
|
+
throw new RequestError(501, '此平台不提供远程桌面控制。', { code: 'desktop_control_unsupported' });
|
|
216
|
+
|
|
217
|
+
if (url.pathname === '/api/desktop/video-probe') {
|
|
218
|
+
if (request.method === 'POST') {
|
|
219
|
+
try { sendJson(response, 200, await desktopVideoProbe.begin()); }
|
|
220
|
+
catch (error) { throw new RequestError(error.statusCode || 502, error.message, { code: error.code || 'desktop_video_failed' }); }
|
|
221
|
+
}
|
|
222
|
+
else if (request.method === 'PUT' || request.method === 'DELETE') {
|
|
223
|
+
const body = await readJsonBody(request);
|
|
224
|
+
if (typeof body.leaseId !== 'string' || !/^[a-f0-9]{32}$/.test(body.leaseId)) throw new RequestError(400, '无效的视频会话');
|
|
225
|
+
sendJson(response, 200, request.method === 'PUT' ? desktopVideoProbe.renew(body.leaseId) : desktopVideoProbe.release(body.leaseId));
|
|
226
|
+
} else throw new RequestError(405, '不支持的操作');
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (request.method === 'GET' && url.pathname === '/api/desktop/frame') {
|
|
230
|
+
const started = Date.now();
|
|
231
|
+
try {
|
|
232
|
+
const preview = url.searchParams.get('preview') === '1';
|
|
233
|
+
const frame = await desktopHelper.frame({ preview }, request.controlContext);
|
|
234
|
+
if (!preview) console.info(JSON.stringify({ time: new Date().toISOString(), event: 'desktop-frame', state: frame.state,
|
|
235
|
+
kind: frame.kind, width: frame.width, height: frame.height, bytes: frame.size, ms: Date.now() - started }));
|
|
236
|
+
if (response.writableEnded) { desktopHelper.release(frame.frameId); return; }
|
|
237
|
+
sendJson(response, 200, frame);
|
|
238
|
+
} catch (error) {
|
|
239
|
+
console.error(JSON.stringify({ time: new Date().toISOString(), event: 'desktop-frame', state: 'failed',
|
|
240
|
+
error: error.message.slice(0, 160), ms: Date.now() - started }));
|
|
241
|
+
throw error;
|
|
242
|
+
}
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const desktopImage = /^\/api\/desktop\/frames\/([a-f0-9]{32})$/.exec(url.pathname);
|
|
246
|
+
if (request.method === 'GET' && desktopImage) {
|
|
247
|
+
if (!request.fileTransfer) throw new RequestError(400, '请通过文件通道读取桌面画面。');
|
|
248
|
+
const image = desktopHelper.image(desktopImage[1]);
|
|
249
|
+
const { offset, length } = fileRange(url.searchParams, image.length, desktopImage[1]);
|
|
250
|
+
const mimeType = desktopHelper.active.frame.mimeType;
|
|
251
|
+
response.filePart({ name: mimeType === 'image/jpeg' ? 'desktop.jpg' : 'desktop.png', size: image.length, offset, revision: desktopImage[1], mimeType },
|
|
252
|
+
image.subarray(offset, offset + length));
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (request.method === 'POST' && url.pathname === '/api/desktop/move') {
|
|
256
|
+
try { sendJson(response, 200, await desktopHelper.move(await readJsonBody(request), request.controlContext)); }
|
|
257
|
+
catch (error) {
|
|
258
|
+
console.error(JSON.stringify({ time: new Date().toISOString(), event: 'desktop-move', state: 'failed',
|
|
259
|
+
code: error.code, error: error.message.slice(0, 160) }));
|
|
260
|
+
throw error;
|
|
261
|
+
}
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (request.method === 'POST' && url.pathname === '/api/desktop/click') {
|
|
265
|
+
const started = Date.now();
|
|
266
|
+
console.info(JSON.stringify({ time: new Date().toISOString(), event: 'desktop-click', state: 'received' }));
|
|
267
|
+
try {
|
|
268
|
+
const result = await desktopHelper.click(await readJsonBody(request), request.controlContext);
|
|
269
|
+
console.info(JSON.stringify({ time: new Date().toISOString(), event: 'desktop-click', state: result.state,
|
|
270
|
+
inputCount: result.inputCount, ms: Date.now() - started }));
|
|
271
|
+
sendJson(response, 200, result);
|
|
272
|
+
} catch (error) {
|
|
273
|
+
console.error(JSON.stringify({ time: new Date().toISOString(), event: 'desktop-click', state: 'failed',
|
|
274
|
+
code: error.code, error: error.message.slice(0, 160), ms: Date.now() - started }));
|
|
275
|
+
throw error;
|
|
276
|
+
}
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (request.method === 'DELETE' && url.pathname === '/api/desktop/frame') {
|
|
280
|
+
const body = await readJsonBody(request);
|
|
281
|
+
if (typeof body.sessionId !== 'string' || !/^[a-f0-9]{32}$/.test(body.sessionId)) throw new RequestError(400, '无效的桌面会话');
|
|
282
|
+
desktopHelper.release(undefined, body.sessionId);
|
|
283
|
+
sendJson(response, 200, { released: true });
|
|
124
284
|
return;
|
|
125
285
|
}
|
|
126
286
|
|
|
@@ -143,123 +303,409 @@ async function handleRequest(request, response) {
|
|
|
143
303
|
if (request.method === 'GET' && url.pathname === '/api/shared-recovery') {
|
|
144
304
|
sendJson(response, 200, await recoveryStatus(stateRoot)); return;
|
|
145
305
|
}
|
|
306
|
+
if (request.method === 'POST' && ['/api/desktop/start', '/api/desktop/restart'].includes(url.pathname)) {
|
|
307
|
+
const body = await readJsonBody(request);
|
|
308
|
+
if (body.confirmed !== true || !/^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i.test(body.operationId || ''))
|
|
309
|
+
throw new RequestError(400, '请确认启动桌面端并提供操作编号。');
|
|
310
|
+
try {
|
|
311
|
+
sendJson(response, 202, desktopOperation.request({ operationId: body.operationId,
|
|
312
|
+
restart: url.pathname === '/api/desktop/restart' }));
|
|
313
|
+
} catch (error) { throw new RequestError(error.statusCode || 502, error.message); }
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
146
316
|
if (request.method === 'POST' && url.pathname === '/api/shared-recovery') {
|
|
147
317
|
const body = await readJsonBody(request);
|
|
148
|
-
if (body.confirmed !== true || typeof body.operationId !== 'string') throw new RequestError(400, '请确认重新打开当前 Codex 并接入 Codex 内核后再继续。');
|
|
318
|
+
if (body.confirmed !== true || typeof body.operationId !== 'string') throw new RequestError(400, '请确认重新打开当前 Codex 并接入 Codex 内核后再继续。');
|
|
149
319
|
sendJson(response, 202, await requestRecovery(stateRoot, body)); return;
|
|
150
320
|
}
|
|
151
|
-
if (
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
throw
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
});
|
|
321
|
+
if (url.pathname === '/api/service/update' && request.method === 'GET') {
|
|
322
|
+
sendJson(response, 200, await checkServiceUpdate(stateRoot)); return;
|
|
323
|
+
}
|
|
324
|
+
if (url.pathname === '/api/service/update' && request.method === 'POST') {
|
|
325
|
+
sendJson(response, 202, await requestServiceUpdate(stateRoot, await readJsonBody(request))); return;
|
|
326
|
+
}
|
|
327
|
+
if (request.method === 'POST' && url.pathname === '/api/service/restart') {
|
|
328
|
+
const body = await readJsonBody(request);
|
|
329
|
+
if (body.confirmed !== true) throw new RequestError(400, '请确认重启工作站服务后再继续。');
|
|
330
|
+
sendJson(response, 202, { ...await requestServiceRestart(stateRoot), serviceInstanceId }); return;
|
|
331
|
+
}
|
|
332
|
+
if (request.method === 'POST' && url.pathname === '/api/managed-app-server/restart') {
|
|
333
|
+
const body = await readJsonBody(request);
|
|
334
|
+
if (body.confirmed !== true || typeof body.operationId !== 'string' || !body.operationId.trim()) {
|
|
335
|
+
throw new RequestError(400, '请先确认中断风险,再重启 Codex 内核。');
|
|
336
|
+
}
|
|
337
|
+
try {
|
|
338
|
+
sendJson(response, 202, managedAppServer.restart(body.operationId.trim()));
|
|
339
|
+
} catch (error) {
|
|
340
|
+
if (error?.code === 'managed_app_server_restarting') throw new RequestError(409, error.message);
|
|
341
|
+
throw error;
|
|
342
|
+
}
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (request.method === 'GET' && url.pathname === '/api/official-remote/status') {
|
|
347
|
+
if (!managedAppServer.client.ready) throw new RequestError(503, 'Codex 内核未就绪,无法读取官方工作站状态。');
|
|
348
|
+
const result = await managedAppServer.client.request('remoteControl/status/read', {});
|
|
349
|
+
sendJson(response, 200, { status: result.status, environmentId: result.environmentId });
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
if (request.method === 'POST' && url.pathname === '/api/official-remote/pairing') {
|
|
353
|
+
if (!managedAppServer.client.ready) throw new RequestError(503, 'Codex 内核未就绪,无法生成官方配对码。');
|
|
354
|
+
const status = await managedAppServer.client.request('remoteControl/status/read', {});
|
|
355
|
+
if (status.status !== 'connected') throw new RequestError(409, '当前 Codex 内核未连接 OpenAI Remote,无法配对。');
|
|
356
|
+
const result = await managedAppServer.client.request('remoteControl/pairing/start', { manualCode: true });
|
|
357
|
+
if (!result.manualPairingCode || !result.environmentId) throw new RequestError(502, 'Codex 内核未返回有效的官方配对码。');
|
|
358
|
+
// One-time code: return only to the authenticated station controller; never persist or log it.
|
|
359
|
+
sendJson(response, 200, { manualPairingCode: result.manualPairingCode,
|
|
360
|
+
environmentId: result.environmentId, expiresAt: result.expiresAt });
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (request.method === 'GET' && url.pathname === '/api/health') {
|
|
365
|
+
if (url.searchParams.get('refresh') === 'desktop') await runtimeDiscovery.sample();
|
|
366
|
+
sendJson(response, 200, workstationHealth()); return;
|
|
367
|
+
}
|
|
368
|
+
if (request.method === 'GET' && url.pathname === '/api/health/events') {
|
|
369
|
+
response.startEvents();
|
|
370
|
+
let previous = '', stopped = false, timer;
|
|
371
|
+
const publish = () => {
|
|
372
|
+
if (stopped) return;
|
|
373
|
+
try {
|
|
374
|
+
const health = workstationHealth();
|
|
375
|
+
// Sampling timestamps alone do not change a workstation's visible state.
|
|
376
|
+
const signature = JSON.stringify(health, (key, value) => key === 'checkedAt' ? undefined : value);
|
|
377
|
+
if (signature !== previous) { previous = signature; response.event('health', health); }
|
|
378
|
+
} catch (error) { previous = ''; response.event('health-error', { error: error.message }); }
|
|
379
|
+
};
|
|
380
|
+
const close = () => { stopped = true; clearInterval(timer); managedAppServer.off('state', publish); };
|
|
381
|
+
response.once('close', close);
|
|
382
|
+
managedAppServer.on('state', publish);
|
|
383
|
+
publish();
|
|
384
|
+
// Reads only existing local samples and operation records, never chat history
|
|
385
|
+
// or an extra Desktop/process probe. Idle snapshots are not sent over the network.
|
|
386
|
+
timer = setInterval(publish, 1000); timer.unref?.();
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const fileLinkMatch = /^\/api\/files\/([a-f0-9]{64})\/link$/.exec(url.pathname);
|
|
391
|
+
if (request.method === 'GET' && fileLinkMatch) {
|
|
392
|
+
const attachment = attachmentCache.get(fileLinkMatch[1])
|
|
393
|
+
|| dotsImages.restore(fileLinkMatch[1], url.searchParams.get('recovery') || '');
|
|
394
|
+
if (!attachment?.dotsFile) throw new RequestError(404, '此附件没有网络链接,请重新读取对话。');
|
|
395
|
+
sendJson(response, 200, { url: await dotsImages.fileLink(attachment) });
|
|
187
396
|
return;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const imageMatch = /^\/api\/images\/([a-f0-9]{64})$/.exec(url.pathname);
|
|
191
|
-
if (request.method === 'GET' && imageMatch) {
|
|
192
|
-
let image = attachmentCache.get(imageMatch[1])
|
|
193
|
-
||
|
|
194
|
-
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const imageMatch = /^\/api\/images\/([a-f0-9]{64})$/.exec(url.pathname);
|
|
400
|
+
if (request.method === 'GET' && imageMatch) {
|
|
401
|
+
let image = attachmentCache.get(imageMatch[1])
|
|
402
|
+
|| dotsImages.restore(imageMatch[1], url.searchParams.get('recovery') || '')
|
|
403
|
+
|| recoverLocalImage(imageMatch[1], url.searchParams.get('recovery') || '');
|
|
404
|
+
const contextThreadId = url.searchParams.get('threadId') || '';
|
|
405
|
+
const contextTurnId = url.searchParams.get('turnId') || '';
|
|
406
|
+
if (!image && sharedServer && contextThreadId && contextTurnId
|
|
407
|
+
&& contextThreadId.length <= 128 && contextTurnId.length <= 128) {
|
|
408
|
+
try {
|
|
409
|
+
normalizeTurn(await sharedServer.turnContext(contextThreadId, contextTurnId));
|
|
410
|
+
image = attachmentCache.get(imageMatch[1]);
|
|
411
|
+
} catch {}
|
|
412
|
+
}
|
|
413
|
+
if (!image) throw new RequestError(404, '这张图片已失效,无法从工作站重新读取。');
|
|
414
|
+
if (image.dotsFile) await dotsImages.load(image);
|
|
415
|
+
if (url.searchParams.get('variant') === 'thumbnail') {
|
|
416
|
+
const asset = await attachmentThumbnail(image);
|
|
417
|
+
sendJson(response, 200, { url: `data:${asset.mimeType};base64,${asset.buffer.toString('base64')}` });
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const size = image.buffer.length;
|
|
421
|
+
if (!size) throw new RequestError(400, '图片为空,无法下载。');
|
|
422
|
+
if (!request.fileTransfer) throw new RequestError(400, '请通过文件通道读取原图。');
|
|
423
|
+
const revision = imageMatch[1];
|
|
424
|
+
const { offset, length } = fileRange(url.searchParams, size, revision);
|
|
425
|
+
response.filePart({ name: image.name || '图片', size, offset, revision, mimeType: image.mimeType },
|
|
426
|
+
image.buffer.subarray(offset, offset + length));
|
|
207
427
|
return;
|
|
208
428
|
}
|
|
209
429
|
|
|
430
|
+
const localFileRequest = url.pathname === '/api/files/local';
|
|
210
431
|
const fileMatch = /^\/api\/files\/([a-f0-9]{64})$/.exec(url.pathname);
|
|
211
|
-
if (request.method === 'GET' && fileMatch) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
432
|
+
if (request.method === 'GET' && (fileMatch || localFileRequest)) {
|
|
433
|
+
if (!request.fileTransfer) throw new RequestError(400, '请通过文件通道下载附件。');
|
|
434
|
+
const hosted = !localFileRequest && (attachmentCache.get(fileMatch[1])
|
|
435
|
+
|| dotsImages.restore(fileMatch[1], url.searchParams.get('recovery') || ''));
|
|
436
|
+
if (hosted?.dotsFile) {
|
|
437
|
+
const { bytes, ...metadata } = await dotsImages.readPart(hosted, url.searchParams);
|
|
438
|
+
response.filePart(metadata, bytes);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
const localPath = url.searchParams.get('path') || '';
|
|
442
|
+
if (localFileRequest && (/[\x00-\x1f]/.test(localPath)
|
|
443
|
+
|| !(process.platform === 'win32' ? /^[a-z]:[\\/]/i : /^\/(?!\/)/).test(localPath))) {
|
|
444
|
+
throw new RequestError(400, '无效的工作站文件路径。');
|
|
219
445
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
446
|
+
const file = localFileRequest ? describeLocalFile(localPath) : fileCache.get(fileMatch[1]);
|
|
447
|
+
if (!file) throw new RequestError(404, '电脑上的文件不存在或无法读取。');
|
|
448
|
+
if (file.externalIdentity) await dotsImages.authorization(file.externalIdentity);
|
|
449
|
+
const { offset, length } = fileRange(url.searchParams, file.size, file.revision);
|
|
450
|
+
let handle;
|
|
451
|
+
try { handle = fs.openSync(file.path, 'r'); }
|
|
452
|
+
catch (error) {
|
|
453
|
+
if (error.code === 'ENOENT') throw new RequestError(404, '电脑上的文件已不存在。');
|
|
454
|
+
throw error;
|
|
455
|
+
}
|
|
456
|
+
try {
|
|
457
|
+
const validate = () => {
|
|
458
|
+
const stat = fs.fstatSync(handle);
|
|
459
|
+
if (!stat.isFile() || fileRevision(stat) !== file.revision || fileRevision(fs.statSync(file.path)) !== file.revision) {
|
|
460
|
+
if (fileMatch) fileCache.delete(fileMatch[1]);
|
|
461
|
+
throw new RequestError(409, '电脑上的文件已发生变化,请重新读取对话。');
|
|
462
|
+
}
|
|
463
|
+
if (!stat.size) throw new RequestError(400, '文件为空,无法下载。');
|
|
464
|
+
};
|
|
465
|
+
validate();
|
|
466
|
+
const data = Buffer.alloc(length);
|
|
467
|
+
let bytesRead = 0;
|
|
468
|
+
while (bytesRead < data.length) {
|
|
469
|
+
const count = fs.readSync(handle, data, bytesRead, data.length - bytesRead, offset + bytesRead);
|
|
470
|
+
if (!count) throw new RequestError(409, '电脑上的文件读取不完整,请重新读取对话。');
|
|
471
|
+
bytesRead += count;
|
|
472
|
+
}
|
|
473
|
+
validate();
|
|
474
|
+
response.filePart({ name: file.name, size: file.size, offset, revision: file.revision }, data);
|
|
475
|
+
} finally { fs.closeSync(handle); }
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (url.pathname.startsWith('/api/app-server/') || url.pathname === '/api/wait') {
|
|
480
|
+
throw new RequestError(410, '此连接方式已停用。请检查 Codex 内核状态。', { code: 'unsupported_control' });
|
|
481
|
+
}
|
|
482
|
+
const taskPath = /^\/api\/threads\/([^/]+)/.exec(url.pathname);
|
|
483
|
+
if (taskPath && (url.pathname.endsWith('/takeover') || url.pathname === `/api/threads/${taskPath[1]}`)) {
|
|
484
|
+
throw new RequestError(410, '此连接方式已停用。', { code: 'unsupported_control' });
|
|
485
|
+
}
|
|
486
|
+
const hostId = url.searchParams.get('hostId') || 'local';
|
|
487
|
+
if (!['local', 'durable'].includes(hostId)) {
|
|
488
|
+
throw new RequestError(410, '此对话的连接方式已停用,请检查 Codex 内核状态。', { code: 'unsupported_control' });
|
|
489
|
+
}
|
|
490
|
+
if (!executionHealth.state.ready || sharedServer?.ready !== true) {
|
|
491
|
+
throw new RequestError(503, 'Codex 内核未就绪。请重启工作站服务;若仍失败,运行 relax doctor。',
|
|
492
|
+
{ code: 'full_control_required' });
|
|
493
|
+
}
|
|
494
|
+
const rootChat = hostId === 'durable' && taskPath
|
|
495
|
+
&& await dotsServer.source(decodeURIComponent(taskPath[1])) === 'aeon';
|
|
496
|
+
const execution = rootChat ? dotsChat : hostId === 'durable' ? dotsServer : sharedServer;
|
|
497
|
+
|
|
498
|
+
if (url.pathname === '/api/permissions' && ['GET', 'POST'].includes(request.method)) {
|
|
499
|
+
if (hostId !== 'local') throw new RequestError(405, '当前来源不提供工作站全局审批方式。');
|
|
500
|
+
const body = request.method === 'POST' ? await readJsonBody(request) : {};
|
|
501
|
+
if (request.method === 'POST' && typeof body.mode !== 'string') throw new RequestError(400, '缺少审批方式。');
|
|
502
|
+
sendJson(response, 200, await sharedServer.globalPermissions(body.mode, {
|
|
503
|
+
threadId: url.searchParams.get('threadId') || '', cwd: url.searchParams.get('cwd') || '',
|
|
504
|
+
}));
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const dotsActivityRetryMatch = /^\/api\/threads\/([^/]+)\/dots-activity\/retry$/.exec(url.pathname);
|
|
509
|
+
if (request.method === 'POST' && dotsActivityRetryMatch) {
|
|
510
|
+
if (!rootChat) throw new RequestError(405, '只有点点主聊天提供活动状态。');
|
|
511
|
+
sendJson(response, 200, { started: dotsChat.retryActivity(decodeURIComponent(dotsActivityRetryMatch[1])) });
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
const dotsActivityMatch = /^\/api\/threads\/([^/]+)\/dots-activity-events$/.exec(url.pathname);
|
|
515
|
+
if (request.method === 'GET' && dotsActivityMatch) {
|
|
516
|
+
if (!rootChat) throw new RequestError(405, '只有点点主聊天提供此活动面板。');
|
|
517
|
+
response.startEvents();
|
|
518
|
+
const disconnected = () => response.end();
|
|
519
|
+
dotsChat.on('disconnected', disconnected);
|
|
520
|
+
const release = dotsChat.acquirePanelActivity(decodeURIComponent(dotsActivityMatch[1]), value => {
|
|
521
|
+
if (!response.destroyed) response.event('dots-activity', { threadId: value.threadId, activity: value.activity });
|
|
522
|
+
});
|
|
523
|
+
response.once('close', () => { release(); dotsChat.off('disconnected', disconnected); });
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const dotsTasksMatch = /^\/api\/threads\/([^/]+)\/dots-tasks$/.exec(url.pathname);
|
|
528
|
+
if (request.method === 'GET' && dotsTasksMatch) {
|
|
529
|
+
if (!rootChat) throw new RequestError(405, '只有点点主聊天提供此任务面板。');
|
|
530
|
+
sendJson(response, 200, await readDotsSummaryTasks(dotsServer, decodeURIComponent(dotsTasksMatch[1])));
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const dotsSchedulesMatch = /^\/api\/threads\/([^/]+)\/dots-schedules$/.exec(url.pathname);
|
|
535
|
+
if (request.method === 'GET' && dotsSchedulesMatch) {
|
|
536
|
+
if (!rootChat) throw new RequestError(405, '只有点点主聊天提供此定时任务列表。');
|
|
537
|
+
sendJson(response, 200, await readDotsSchedules(dotsServer, decodeURIComponent(dotsSchedulesMatch[1])));
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
if (request.method === 'GET' && url.pathname.startsWith('/api/code/')) {
|
|
542
|
+
const operation = url.pathname.slice('/api/code/'.length);
|
|
543
|
+
if (hostId === 'durable' && operation !== 'change') throw new RequestError(405, 'Dots 委派任务尚未接入代码浏览。');
|
|
544
|
+
try {
|
|
545
|
+
if ((operation === 'image' || operation === 'binary') && !request.fileTransfer) throw new RequestError(400, '请通过文件通道读取预览文件。');
|
|
546
|
+
const result = await browseCode((method, params) => execution.request(method, params), operation, url.searchParams);
|
|
547
|
+
if (operation === 'image' || operation === 'binary') {
|
|
548
|
+
const { data, ...metadata } = result;
|
|
549
|
+
response.filePart(metadata, data);
|
|
550
|
+
} else sendJson(response, 200, result);
|
|
551
|
+
} catch (error) { throw new RequestError(error.statusCode || 502, error.message); }
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (request.method === 'GET' && url.pathname === '/api/models') {
|
|
556
|
+
sendJson(response, 200, await execution.models());
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
if (request.method === 'GET' && url.pathname === '/api/archived-threads') {
|
|
561
|
+
sendJson(response, 200, await archivedThreadsPage(execution, sharedServer, hostId, url.searchParams.get('cursor')));
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
244
564
|
|
|
245
565
|
if (request.method === 'GET' && url.pathname === '/api/threads') {
|
|
246
566
|
const limit = boundedInteger(url.searchParams.get('limit'), 20, 1, 200);
|
|
247
567
|
const archived = url.searchParams.get('archived') === 'true';
|
|
248
|
-
|
|
568
|
+
const scope = url.searchParams.get('scope') || 'all';
|
|
569
|
+
if (!['all', 'local', 'durable'].includes(scope)) { sendJson(response, 400, { error: '无效的会话列表范围' }); return; }
|
|
570
|
+
const catalog = await sharedCatalog.threads(limit, archived, scope, url.searchParams.get('refresh') === '1');
|
|
571
|
+
const creations = await messageReceipts.creationBindings();
|
|
572
|
+
for (const thread of [...(catalog.threads || []), ...(catalog.pinnedThreads || [])]) {
|
|
573
|
+
const clientId = creations.get(thread.id);
|
|
574
|
+
if (clientId && (thread.hostId || 'local') === 'local') thread.creationClientId = clientId;
|
|
575
|
+
}
|
|
576
|
+
sendJson(response, 200, catalog);
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (request.method === 'GET' && url.pathname === '/api/file-local-copy') {
|
|
581
|
+
const value = url.searchParams.get('source') || '';
|
|
582
|
+
const match = /^([a-f0-9]{64})(?:\?recovery=([A-Za-z0-9_-]{1,4096}))?$/.exec(value);
|
|
583
|
+
if (!match) throw new RequestError(400, '无效的文件标识。');
|
|
584
|
+
const asset = attachmentCache.get(match[1])
|
|
585
|
+
|| dotsImages.restore(match[1], match[2] || url.searchParams.get('recovery') || '');
|
|
586
|
+
// A missing process cache does not make a local image an external file.
|
|
587
|
+
// The actual image endpoint owns recovery and download authorization.
|
|
588
|
+
if (!asset?.dotsFile) {
|
|
589
|
+
sendJson(response, 200, { external: false, localCopy: null }); return;
|
|
590
|
+
}
|
|
591
|
+
await dotsImages.authorization(asset.identity);
|
|
592
|
+
const copy = await workstationDownloads.copy(asset.id);
|
|
593
|
+
sendJson(response, 200, { external: true, localCopy: copy ? workstationCopyReference(copy, asset) : null,
|
|
594
|
+
cachedContent: asset.buffer ? { remotePath: asset.id, name: asset.name, size: asset.buffer.length, mimeType: asset.mimeType } : null });
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
if (request.method === 'POST' && url.pathname === '/api/workstation-file-attachments') {
|
|
599
|
+
await requireExecutionWritable();
|
|
600
|
+
const body = await readJsonBody(request);
|
|
601
|
+
const source = body.source;
|
|
602
|
+
const file = typeof source === 'string' && /^[a-z]:[\\/]/i.test(source) ? describeLocalFile(source)
|
|
603
|
+
: typeof source === 'string' && /^[a-f0-9]{64}$/.test(source) ? fileCache.get(source) : null;
|
|
604
|
+
if (!file) throw new RequestError(404, '电脑文件不可用,请从原文件入口重新添加。');
|
|
605
|
+
if (file.externalIdentity) await dotsImages.authorization(file.externalIdentity);
|
|
606
|
+
sendJson(response, 201, { file: await stageWorkstationMessageFile(file, uploadsRoot) });
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if (request.method === 'GET' && url.pathname === '/api/file-save-locations') {
|
|
611
|
+
await requireExecutionWritable();
|
|
612
|
+
const locations = await projectLocations();
|
|
613
|
+
const projects = (await sharedCatalog.projects()).projects.filter(project => project.hostId === 'local' && project.roots?.length);
|
|
614
|
+
sendJson(response, 200, { projects, downloads: locations.downloads });
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if (request.method === 'GET' && url.pathname === '/api/file-save-folders') {
|
|
619
|
+
await requireExecutionWritable();
|
|
620
|
+
sendJson(response, 200, await workstationDownloads.browse((method, params) => sharedServer.request(method, params),
|
|
621
|
+
await projectLocations(), Object.fromEntries(url.searchParams)));
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
if (request.method === 'POST' && url.pathname === '/api/file-saves') {
|
|
626
|
+
await requireExecutionWritable();
|
|
627
|
+
const body = await readJsonBody(request);
|
|
628
|
+
const asset = await externalDownloadAsset(body.source, body.recovery);
|
|
629
|
+
const controller = new AbortController(), cancel = () => controller.abort();
|
|
630
|
+
response.once('close', cancel);
|
|
631
|
+
try {
|
|
632
|
+
const result = await workstationDownloads.save((method, params) => sharedServer.request(method, params),
|
|
633
|
+
await projectLocations(), body, asset, (file, query) => dotsImages.readPart(file, query), controller.signal);
|
|
634
|
+
sendJson(response, 201, { localCopy: workstationCopyReference(result, asset), warning: result.warning || '' });
|
|
635
|
+
} finally { response.off('close', cancel); }
|
|
249
636
|
return;
|
|
250
637
|
}
|
|
251
638
|
|
|
252
639
|
if (request.method === 'GET' && url.pathname === '/api/projects') {
|
|
253
|
-
sendJson(response, 200, { projects: (await sharedCatalog.projects()).projects });
|
|
640
|
+
sendJson(response, 200, { projects: (await sharedCatalog.projects()).projects });
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
if (url.pathname === '/api/project-folders' && ['GET', 'POST'].includes(request.method)) {
|
|
645
|
+
await requireExecutionWritable();
|
|
646
|
+
try {
|
|
647
|
+
const rpc = (method, params) => sharedServer.request(method, params);
|
|
648
|
+
const result = request.method === 'GET'
|
|
649
|
+
? await browseProjectFolders(rpc, !url.searchParams.get('path') || url.searchParams.get('locations') === '1' ? await projectLocations() : null, url.searchParams.get('path'))
|
|
650
|
+
: await createProjectFolder(rpc, await readJsonBody(request));
|
|
651
|
+
sendJson(response, request.method === 'GET' ? 200 : 201, result);
|
|
652
|
+
} catch (error) { throw new RequestError(error.statusCode || 502, error.message); }
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
const projectActionMatch = /^\/api\/projects\/([^/]+)\/(remove|move)$/.exec(url.pathname);
|
|
657
|
+
if (projectActionMatch) {
|
|
658
|
+
await requireExecutionWritable();
|
|
659
|
+
const projectId = decodeURIComponent(projectActionMatch[1]);
|
|
660
|
+
if (request.method === 'POST' && projectActionMatch[2] === 'move') {
|
|
661
|
+
const { beforeProjectId } = await readJsonBody(request);
|
|
662
|
+
if (beforeProjectId !== null && (typeof beforeProjectId !== 'string' || !beforeProjectId.trim()) || beforeProjectId === projectId) {
|
|
663
|
+
throw new RequestError(400, '排序目标无效。');
|
|
664
|
+
}
|
|
665
|
+
await sharedServer.request('project/move', { projectId, beforeProjectId });
|
|
666
|
+
sendJson(response, 200, await sharedCatalog.projects().then(({ projects }) => ({ projects })));
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
if (request.method === 'POST' && projectActionMatch[2] === 'remove') {
|
|
670
|
+
const result = await removeWorkstationProject(sharedServer, stateRoot, codexDataRoot, projectId, await readJsonBody(request));
|
|
671
|
+
sendJson(response, 200, result);
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
throw new RequestError(405, '不支持的项目操作。');
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
const updateProjectMatch = /^\/api\/projects\/([^/]+)$/.exec(url.pathname);
|
|
678
|
+
if (request.method === 'POST' && (url.pathname === '/api/projects' || updateProjectMatch)) {
|
|
679
|
+
await requireExecutionWritable();
|
|
680
|
+
const body = await readJsonBody(request);
|
|
681
|
+
try {
|
|
682
|
+
const result = !updateProjectMatch ? await createWorkstationProject(sharedServer, stateRoot, codexDataRoot, body)
|
|
683
|
+
: await updateWorkstationProject(sharedServer, stateRoot, codexDataRoot, decodeURIComponent(updateProjectMatch[1]), body);
|
|
684
|
+
sendJson(response, updateProjectMatch ? 200 : 201, result);
|
|
685
|
+
} catch (error) { throw new RequestError(error.statusCode || 502, error.message, error.details); }
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
if (request.method === 'GET' && url.pathname === '/api/plan-pricing') {
|
|
690
|
+
sendJson(response, 200, await readPlanPricing(sharedServer));
|
|
254
691
|
return;
|
|
255
692
|
}
|
|
256
693
|
|
|
257
694
|
if (request.method === 'GET' && url.pathname === '/api/usage-limits') {
|
|
258
|
-
sendJson(response, 200, await sharedServer.usage());
|
|
695
|
+
sendJson(response, 200, await sharedServer.usage());
|
|
259
696
|
return;
|
|
260
697
|
}
|
|
261
698
|
|
|
262
|
-
if (request.method === '
|
|
699
|
+
if (request.method === 'POST' && url.pathname === '/api/usage-reset') {
|
|
700
|
+
await requireExecutionWritable();
|
|
701
|
+
const body = await readJsonBody(request);
|
|
702
|
+
try { sendJson(response, 200, await consumeUsageReset(sharedServer, body)); }
|
|
703
|
+
catch (error) { throw new RequestError(error.statusCode || 502, error.message,
|
|
704
|
+
error.code === 'usage_reset_not_submitted' ? { code: error.code, deliveryUnknown: false } : undefined); }
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
if (request.method === 'GET' && url.pathname === '/api/usage-events') {
|
|
263
709
|
const execution = sharedServer;
|
|
264
710
|
response.startEvents();
|
|
265
711
|
const emit = value => { if (!response.destroyed) response.event('usage', value); };
|
|
@@ -271,186 +717,383 @@ async function handleRequest(request, response) {
|
|
|
271
717
|
return;
|
|
272
718
|
}
|
|
273
719
|
|
|
720
|
+
if (request.method === 'GET' && url.pathname === '/api/thread-events') {
|
|
721
|
+
await sharedServer.connect();
|
|
722
|
+
const clients = [sharedServer, dotsServer, dotsChat];
|
|
723
|
+
response.startEvents();
|
|
724
|
+
const listener = ({ method, params }) => {
|
|
725
|
+
const questionChange = questionAttention.fromNotification(method, params);
|
|
726
|
+
if ((!threadListNotificationMethods.has(method) && !questionChange) || response.destroyed) return;
|
|
727
|
+
response.event('thread-list-changed', { method, threadId: params.threadId || params.thread?.id || '',
|
|
728
|
+
...(questionChange ? { questionChange } : {}),
|
|
729
|
+
...(params.hostId ? { hostId: params.hostId } : {}),
|
|
730
|
+
...(params.chatRevision !== undefined ? { chatRevision: params.chatRevision } : {}),
|
|
731
|
+
...(params.updatedAt !== undefined ? { updatedAt: params.updatedAt } : {}),
|
|
732
|
+
...(params.chatMessageRevision !== undefined ? { chatMessageRevision: params.chatMessageRevision } : {}),
|
|
733
|
+
...(params.status ? { status: typeof params.status === 'string' ? params.status : params.status.type } : {}) });
|
|
734
|
+
};
|
|
735
|
+
const disconnected = () => response.end();
|
|
736
|
+
for (const client of clients) {
|
|
737
|
+
client.on('notification', listener);
|
|
738
|
+
client.on('disconnected', disconnected);
|
|
739
|
+
}
|
|
740
|
+
response.once('close', () => {
|
|
741
|
+
for (const client of clients) {
|
|
742
|
+
client.off('notification', listener);
|
|
743
|
+
client.off('disconnected', disconnected);
|
|
744
|
+
}
|
|
745
|
+
});
|
|
746
|
+
response.event('ready', {});
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
if (request.method === 'GET' && url.pathname === '/api/turn-events') {
|
|
751
|
+
const clients = [sharedServer, dotsServer, dotsChat];
|
|
752
|
+
response.startEvents();
|
|
753
|
+
const listener = ({ method, params }) => {
|
|
754
|
+
if (!['turn/started', 'turn/completed'].includes(method)) return;
|
|
755
|
+
const threadId = params?.threadId, turnId = params?.turn?.id;
|
|
756
|
+
if (typeof threadId === 'string' && typeof turnId === 'string' && !response.destroyed) {
|
|
757
|
+
response.event('turn', { method, threadId, turnId });
|
|
758
|
+
}
|
|
759
|
+
};
|
|
760
|
+
const disconnected = () => response.end();
|
|
761
|
+
for (const client of clients) {
|
|
762
|
+
client.on('notification', listener);
|
|
763
|
+
client.on('disconnected', disconnected);
|
|
764
|
+
}
|
|
765
|
+
response.once('close', () => {
|
|
766
|
+
for (const client of clients) {
|
|
767
|
+
client.off('notification', listener);
|
|
768
|
+
client.off('disconnected', disconnected);
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
const activeTurnMatch = /^\/api\/threads\/([^/]+)\/active-turn$/.exec(url.pathname);
|
|
775
|
+
if (request.method === 'GET' && activeTurnMatch) {
|
|
776
|
+
if (rootChat) { sendJson(response, 200, { turnId: null }); return; }
|
|
777
|
+
const threadId = decodeURIComponent(activeTurnMatch[1]);
|
|
778
|
+
await execution.connect();
|
|
779
|
+
const { thread } = await execution.request('thread/read', { threadId, includeTurns: false });
|
|
780
|
+
const status = typeof thread.status === 'string' ? thread.status : thread.status?.type;
|
|
781
|
+
let turnId = null;
|
|
782
|
+
if (status === 'active') {
|
|
783
|
+
const page = await execution.request('thread/turns/list', {
|
|
784
|
+
threadId, limit: 1, sortDirection: 'desc', itemsView: 'summary',
|
|
785
|
+
});
|
|
786
|
+
const latest = page.data?.[0];
|
|
787
|
+
if (latest?.status === 'inProgress') turnId = latest.id;
|
|
788
|
+
}
|
|
789
|
+
sendJson(response, 200, { turnId });
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
const createStatusMatch = /^\/api\/projects\/([^/]+)\/threads\/([a-f0-9]{32})\/status$/.exec(url.pathname);
|
|
794
|
+
if (request.method === 'GET' && createStatusMatch) {
|
|
795
|
+
const creationScope = ['project', decodeURIComponent(createStatusMatch[1])], clientId = createStatusMatch[2];
|
|
796
|
+
let status = messageReceipts.status(creationScope, clientId);
|
|
797
|
+
if (status.state === 'unknown' && status.createdThreadId) {
|
|
798
|
+
const threadId = status.createdThreadId;
|
|
799
|
+
status = await messageReceipts.reconcile(creationScope, clientId, async () => {
|
|
800
|
+
const found = await sharedServer.findSentMessage(threadId, clientId);
|
|
801
|
+
return found ? { threadId, hostId: 'local', turnId: found.data?.turnId } : null;
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
sendJson(response, 200, status); return;
|
|
805
|
+
}
|
|
274
806
|
const createThreadMatch = /^\/api\/projects\/([^/]+)\/threads$/.exec(url.pathname);
|
|
275
|
-
if (request.method === 'POST' && createThreadMatch) {
|
|
276
|
-
await requireExecutionWritable();
|
|
807
|
+
if (request.method === 'POST' && createThreadMatch) {
|
|
277
808
|
const projectId = decodeURIComponent(createThreadMatch[1]);
|
|
278
|
-
const body = await readJsonBody(request);
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
809
|
+
const body = await readJsonBody(request);
|
|
810
|
+
const result = await messageReceipts.submit(['project', projectId], body.clientUserMessageId, async () => {
|
|
811
|
+
const bindCreation = messageReceipts.beginCreation(['project', projectId], body.clientUserMessageId);
|
|
812
|
+
try {
|
|
813
|
+
await requireExecutionWritable();
|
|
814
|
+
if (body.transport === 'app-server' || (body.hostId && body.hostId !== 'local')) {
|
|
815
|
+
throw new RequestError(410, '此连接方式已停用,请检查 Codex 内核状态。', { code: 'unsupported_control' });
|
|
816
|
+
}
|
|
817
|
+
const projects = (await sharedCatalog.projects()).projects;
|
|
818
|
+
const project = projects.find(item => item.projectId === projectId);
|
|
819
|
+
if (projectId !== '__unassigned__' && !project) throw new RequestError(404, '没有找到对应项目。');
|
|
820
|
+
const execution = sharedServer;
|
|
821
|
+
await execution.connect();
|
|
822
|
+
const rpc = (method, params) => execution.request(method, params);
|
|
823
|
+
let target = appServerEvents.projectlessThreadTarget(projectRoot);
|
|
824
|
+
if (project) {
|
|
825
|
+
const nativeProjects = await appServerEvents.readProjects(rpc);
|
|
826
|
+
const nativeProject = nativeProjects.find(item => item.id === project.projectId);
|
|
827
|
+
if (!nativeProject) throw new RequestError(404, 'Codex 内核中没有找到对应项目,请刷新项目列表。');
|
|
828
|
+
target = appServerEvents.projectThreadTarget(nativeProject);
|
|
829
|
+
}
|
|
830
|
+
const { input } = appServerTurnArguments('', body);
|
|
831
|
+
const preference = await execution.globalPermissions(undefined, { cwd: target.cwd });
|
|
832
|
+
messageSubmissionStarted = true;
|
|
833
|
+
const createRpc = (method, params) => method === 'thread/start'
|
|
834
|
+
? execution.startThread(params) : rpc(method, params);
|
|
835
|
+
const created = await appServerEvents.createThread(createRpc, target, input,
|
|
836
|
+
{ ...body, desktopPermissionMode: preference.globalPermissionMode }, undefined, bindCreation);
|
|
837
|
+
const prompt = input.filter(part => part.type === 'text').map(part => part.text).join('\n');
|
|
838
|
+
void generateAndSetThreadTitle({
|
|
839
|
+
request: rpc,
|
|
840
|
+
subscribe: listener => {
|
|
841
|
+
execution.on('notification', listener);
|
|
842
|
+
return () => execution.off('notification', listener);
|
|
843
|
+
},
|
|
844
|
+
threadId: created.threadId,
|
|
845
|
+
prompt,
|
|
846
|
+
cwd: target.cwd || null,
|
|
847
|
+
}).catch(error => console.error(`生成对话标题失败:${error.message}`));
|
|
848
|
+
return { ...created, hostId:'local' };
|
|
849
|
+
} catch (error) {
|
|
850
|
+
error.deliveryUnknown = Boolean(messageReceipts.status(['project', projectId], body.clientUserMessageId).createdThreadId)
|
|
851
|
+
|| messageSubmissionStarted && !messageDelivery.rejectedBeforeSubmission(error);
|
|
852
|
+
if (error.deliveryResult) error.deliveryResult = error.deliveryResult.then(created => ({ ...created, hostId: 'local' })).finally(() => bindCreation());
|
|
853
|
+
else bindCreation();
|
|
854
|
+
throw error;
|
|
855
|
+
}
|
|
856
|
+
});
|
|
857
|
+
sendJson(response, 201, result);
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
const timelineMatch = /^\/api\/threads\/([^/]+)\/timeline$/.exec(url.pathname);
|
|
862
|
+
if (request.method === 'GET' && timelineMatch) {
|
|
863
|
+
const threadId = decodeURIComponent(timelineMatch[1]);
|
|
864
|
+
const knownSeq = url.searchParams.get('knownSeq');
|
|
865
|
+
const knownSync = { id: url.searchParams.get('knownSyncId'),
|
|
866
|
+
epoch: url.searchParams.get('knownEpoch'), seq: knownSeq === null ? undefined : Number(knownSeq),
|
|
867
|
+
updatedAt: Number(url.searchParams.get('knownUpdatedAt')), chatRevision: url.searchParams.get('knownChatRevision') };
|
|
868
|
+
const pageOptions = sharedHistoryPageOptions();
|
|
869
|
+
pageOptions.reconcileObserved = url.searchParams.get('refresh') === '1';
|
|
870
|
+
if (request.socketRequest) {
|
|
871
|
+
pageOptions.onWindow = async window => {
|
|
872
|
+
if (response.writableEnded) return;
|
|
873
|
+
await response.historyPage(await prepareSharedTimeline(window));
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
const timeline = await prepareSharedTimeline(await execution.timeline(threadId, pageOptions, knownSync));
|
|
877
|
+
if (request.socketRequest) {
|
|
878
|
+
response.on('progress', () => execution.keepSnapshotAlive(threadId, timeline.sync));
|
|
879
|
+
response.once('close', () => execution.keepSnapshotAlive(threadId, timeline.sync));
|
|
880
|
+
}
|
|
881
|
+
sendJson(response, 200, timeline);
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
334
884
|
|
|
885
|
+
const browserAuthMatch = /^\/api\/threads\/([^/]+)\/approvals\/browser-auth$/.exec(url.pathname);
|
|
886
|
+
if (request.method === 'POST' && browserAuthMatch) {
|
|
887
|
+
if (!rootChat) throw new RequestError(405, '当前安全登录入口仅支持点点主聊天的登录请求。');
|
|
888
|
+
const threadId = decodeURIComponent(browserAuthMatch[1]);
|
|
889
|
+
await requireExecutionWritable(threadId, execution);
|
|
890
|
+
const body = await readJsonBody(request);
|
|
891
|
+
sendJson(response, 200, await execution.authenticateApproval(threadId, body)); return;
|
|
892
|
+
}
|
|
335
893
|
const approvalMatch = /^\/api\/threads\/([^/]+)\/approvals$/.exec(url.pathname);
|
|
336
|
-
if (request.method === '
|
|
337
|
-
const threadId = decodeURIComponent(approvalMatch[1]);
|
|
338
|
-
await
|
|
339
|
-
|
|
340
|
-
|
|
894
|
+
if (request.method === 'GET' && approvalMatch && rootChat) {
|
|
895
|
+
const threadId = decodeURIComponent(approvalMatch[1]);
|
|
896
|
+
sendJson(response, 200, await execution.inspectApproval(threadId, {
|
|
897
|
+
id: url.searchParams.get('requestId'), roomMessageId: url.searchParams.get('roomMessageId'),
|
|
898
|
+
})); return;
|
|
899
|
+
}
|
|
900
|
+
if (request.method === 'POST' && approvalMatch) {
|
|
901
|
+
const threadId = decodeURIComponent(approvalMatch[1]);
|
|
902
|
+
await requireExecutionWritable(threadId, execution);
|
|
903
|
+
const body = await readJsonBody(request);
|
|
904
|
+
sendJson(response, 200, await execution.respondApproval(threadId, body)); return;
|
|
341
905
|
}
|
|
342
906
|
|
|
343
907
|
const eventsMatch = /^\/api\/threads\/([^/]+)\/events$/.exec(url.pathname);
|
|
344
|
-
if (request.method === 'GET' && eventsMatch) {
|
|
345
|
-
const threadId = decodeURIComponent(eventsMatch[1]);
|
|
346
|
-
const after = Number(url.searchParams.get('afterSeq'));
|
|
347
|
-
const requestedSync = url.searchParams.get('syncId')
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
} : undefined;
|
|
355
|
-
await streamSharedThread(response, threadId, sharedServer, normalizeSharedTimeline, requestedSync, known);
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
908
|
+
if (request.method === 'GET' && eventsMatch) {
|
|
909
|
+
const threadId = decodeURIComponent(eventsMatch[1]);
|
|
910
|
+
const after = Number(url.searchParams.get('afterSeq'));
|
|
911
|
+
const requestedSync = { id: url.searchParams.get('syncId'), epoch: url.searchParams.get('syncEpoch'),
|
|
912
|
+
after: Number.isSafeInteger(after) && after >= 0 ? after : undefined,
|
|
913
|
+
updatedAt: Number(url.searchParams.get('knownUpdatedAt')), chatRevision: url.searchParams.get('knownChatRevision') };
|
|
914
|
+
await streamSharedThread(response, threadId, execution,
|
|
915
|
+
prepareSharedTimeline, requestedSync);
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
358
918
|
|
|
919
|
+
const modelChangesMatch = /^\/api\/threads\/([^/]+)\/model-changes$/.exec(url.pathname);
|
|
920
|
+
if (request.method === 'GET' && modelChangesMatch) {
|
|
921
|
+
const threadId = decodeURIComponent(modelChangesMatch[1]);
|
|
922
|
+
if (!/^[0-9a-f]{8}-[0-9a-f-]{20,55}$/i.test(threadId)) throw new RequestError(400, '对话 ID 无效。');
|
|
923
|
+
sendJson(response, 200, hostId === 'durable' ? [] : await turnModelHistory.read(findSessionPath(threadId), (url.searchParams.get('turnIds') || '').split(',').filter(Boolean)));
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
359
926
|
const contextMatch = /^\/api\/threads\/([^/]+)\/context$/.exec(url.pathname);
|
|
360
927
|
if (request.method === 'GET' && contextMatch) {
|
|
361
928
|
const threadId = decodeURIComponent(contextMatch[1]);
|
|
362
|
-
const current = readContextUsage(threadId);
|
|
363
|
-
const native = await
|
|
929
|
+
const current = hostId === 'durable' ? { available: false } : readContextUsage(threadId);
|
|
930
|
+
const native = await execution.readContext(threadId, url.searchParams.get('refreshSettings') === 'true');
|
|
364
931
|
sendJson(response, 200, { ...current, ...(native.model ? { model: native.model, thinking: native.thinking, serviceTier: native.serviceTier,
|
|
365
|
-
permissionMode: native.permissionMode, permissionModes: native.permissionModes } : {}), ...(native.available ? native : {}) });
|
|
932
|
+
permissionMode: native.permissionMode, permissionModes: native.permissionModes, globalPermissionMode: native.globalPermissionMode } : {}), ...(native.available ? native : {}) });
|
|
366
933
|
return;
|
|
367
934
|
}
|
|
368
935
|
|
|
369
936
|
const sharedAction = /^\/api\/threads\/([^/]+)\/(history|settings|permissions)$/.exec(url.pathname);
|
|
370
|
-
if (sharedAction) {
|
|
937
|
+
if (sharedAction) {
|
|
371
938
|
const id = decodeURIComponent(sharedAction[1]);
|
|
372
939
|
if (request.method === 'GET' && sharedAction[2] === 'history') {
|
|
373
|
-
|
|
940
|
+
const pageOptions = sharedHistoryPageOptions();
|
|
941
|
+
if (url.searchParams.has('itemLimit')) {
|
|
942
|
+
const itemLimit = Number(url.searchParams.get('itemLimit'));
|
|
943
|
+
if (!Number.isInteger(itemLimit) || itemLimit < 1 || itemLimit > 100) throw new RequestError(400, '历史条目数量无效。');
|
|
944
|
+
pageOptions.itemLimit = itemLimit;
|
|
945
|
+
}
|
|
946
|
+
const history = await execution.history(id, url.searchParams.get('cursor'), pageOptions);
|
|
947
|
+
sendJson(response, 200, await prepareSharedTimeline(history, hostId));
|
|
374
948
|
return;
|
|
375
949
|
}
|
|
376
|
-
if (request.method === 'POST' && sharedAction[2] === 'settings') {
|
|
377
|
-
|
|
950
|
+
if (request.method === 'POST' && sharedAction[2] === 'settings') {
|
|
951
|
+
if (rootChat) throw new RequestError(405, 'Dots 主对话不提供 Codex 模型设置。');
|
|
952
|
+
await requireExecutionWritable(id, execution);
|
|
378
953
|
const body = await readJsonBody(request);
|
|
379
|
-
sendJson(response, 200, await
|
|
954
|
+
sendJson(response, 200, await execution.settings(id, body.model, body.thinking, body.serviceTier));
|
|
380
955
|
return;
|
|
381
956
|
}
|
|
382
|
-
if (request.method === 'POST' && sharedAction[2] === 'permissions') {
|
|
383
|
-
|
|
957
|
+
if (request.method === 'POST' && sharedAction[2] === 'permissions') {
|
|
958
|
+
if (rootChat) throw new RequestError(405, 'Dots 主对话不提供 Codex 权限设置。');
|
|
959
|
+
await requireExecutionWritable(id, execution);
|
|
384
960
|
const body = await readJsonBody(request);
|
|
385
|
-
sendJson(response, 200, await
|
|
961
|
+
sendJson(response, 200, await execution.permissions(id, body.mode));
|
|
386
962
|
return;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
const
|
|
391
|
-
if (
|
|
392
|
-
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
const goalMatch = /^\/api\/threads\/([^/]+)\/goal$/.exec(url.pathname);
|
|
967
|
+
if (goalMatch && ['GET', 'POST'].includes(request.method)) {
|
|
968
|
+
if (rootChat) throw new RequestError(405, '点点主聊天不提供线程目标操作。');
|
|
969
|
+
const threadId = decodeURIComponent(goalMatch[1]);
|
|
970
|
+
if (request.method === 'GET') sendJson(response, 200, await readThreadGoal(execution, threadId));
|
|
971
|
+
else {
|
|
972
|
+
await requireExecutionWritable(threadId, execution);
|
|
973
|
+
const body = await readJsonBody(request);
|
|
974
|
+
await execution.connect();
|
|
975
|
+
sendJson(response, 200, await changeThreadGoal(execution, threadId, body, {
|
|
976
|
+
beforeActivate: () => execution.prepareGoalActivation(threadId, { useGlobalPermissions: hostId !== 'durable' }),
|
|
977
|
+
}));
|
|
978
|
+
}
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
const renameMatch = /^\/api\/threads\/([^/]+)\/name$/.exec(url.pathname);
|
|
983
|
+
if (request.method === 'POST' && renameMatch) {
|
|
984
|
+
if (rootChat) throw new RequestError(405, '点点主聊天名称由点点资料管理。');
|
|
985
|
+
const threadId = decodeURIComponent(renameMatch[1]);
|
|
986
|
+
await requireExecutionWritable(threadId, execution);
|
|
987
|
+
const body = await readJsonBody(request);
|
|
988
|
+
const name = typeof body.name === 'string' ? body.name.trim() : '';
|
|
989
|
+
if (!name || /[\r\n]/.test(name)) throw new RequestError(400, '请输入单行对话名称。');
|
|
990
|
+
await execution.request('thread/name/set', { threadId, name });
|
|
991
|
+
sendJson(response, 200, { threadId, name });
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
const archiveMatch = /^\/api\/threads\/([^/]+)\/(archive|unarchive)$/.exec(url.pathname);
|
|
996
|
+
if (request.method === 'POST' && archiveMatch) {
|
|
997
|
+
if (rootChat) throw new RequestError(405, 'Dots 主对话不提供任务归档。');
|
|
998
|
+
const threadId = decodeURIComponent(archiveMatch[1]);
|
|
999
|
+
await requireExecutionWritable(threadId, execution);
|
|
1000
|
+
if (archiveMatch[2] === 'archive') {
|
|
1001
|
+
const { thread } = await execution.request('thread/read', { threadId, includeTurns: false });
|
|
1002
|
+
const status = typeof thread.status === 'string' ? thread.status : thread.status?.type;
|
|
1003
|
+
if (['active', 'inProgress'].includes(status)) throw new RequestError(409, '对话正在运行,请先停止任务。');
|
|
1004
|
+
}
|
|
1005
|
+
await execution.request(`thread/${archiveMatch[2]}`, { threadId });
|
|
406
1006
|
sendJson(response, 200, { threadId, archived: archiveMatch[2] === 'archive' });
|
|
407
1007
|
return;
|
|
408
1008
|
}
|
|
409
1009
|
|
|
410
|
-
const queueMatch = /^\/api\/threads\/([^/]+)\/queue$/.exec(url.pathname);
|
|
411
|
-
if (queueMatch) {
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
if (request.method === '
|
|
417
|
-
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
1010
|
+
const queueMatch = /^\/api\/threads\/([^/]+)\/queue$/.exec(url.pathname);
|
|
1011
|
+
if (queueMatch) {
|
|
1012
|
+
if (hostId === 'durable') throw new RequestError(405, '当前托管连接不提供排队消息。');
|
|
1013
|
+
const threadId = decodeURIComponent(queueMatch[1]);
|
|
1014
|
+
await sharedServer.connect();
|
|
1015
|
+
const rpc = (method, params) => sharedServer.request(method, params);
|
|
1016
|
+
if (request.method === 'GET') { sendJson(response, 200, await appServerEvents.readQueue(rpc, threadId)); return; }
|
|
1017
|
+
if (request.method === 'POST') {
|
|
1018
|
+
const body = await readJsonBody(request);
|
|
1019
|
+
const result = await messageReceipts.submit([hostId, threadId], body.clientUserMessageId, async () => {
|
|
1020
|
+
let args;
|
|
1021
|
+
try { await requireExecutionWritable(threadId); args = appServerTurnArguments(threadId, body); }
|
|
1022
|
+
catch (error) { error.deliveryUnknown = false; throw error; }
|
|
1023
|
+
messageSubmissionStarted = true;
|
|
1024
|
+
try { return await appServerEvents.addQueue(rpc, threadId, args.input, body.clientUserMessageId, body.containsVoiceInput); }
|
|
1025
|
+
catch (error) { if (messageDelivery.rejectedBeforeSubmission(error)) error.deliveryUnknown = false; throw error; }
|
|
1026
|
+
});
|
|
1027
|
+
sendJson(response, 202, result); return;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
422
1030
|
|
|
423
1031
|
const queueItemMatch = /^\/api\/threads\/([^/]+)\/queue\/([a-f0-9-]+)(?:\/(steer))?$/.exec(url.pathname);
|
|
424
1032
|
if (queueItemMatch) {
|
|
1033
|
+
if (hostId === 'durable') throw new RequestError(405, '当前托管连接不提供排队消息。');
|
|
425
1034
|
const threadId = decodeURIComponent(queueItemMatch[1]);
|
|
426
1035
|
const itemId = queueItemMatch[2];
|
|
427
|
-
await sharedServer.connect();
|
|
428
|
-
const action = request.method === 'DELETE' ? 'delete' : queueItemMatch[3] === 'steer' ? 'steer' : null;
|
|
429
|
-
if (!action) throw new RequestError(405, '不支持的队列操作');
|
|
430
|
-
await requireExecutionWritable(threadId);
|
|
431
|
-
sendJson(response, 200, await appServerEvents.changeQueue((method, params) => sharedServer.request(method, params), threadId, itemId, action)); return;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
const interruptMatch = /^\/api\/threads\/([^/]+)\/interrupt$/.exec(url.pathname);
|
|
435
|
-
if (request.method === 'POST' && interruptMatch) {
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
1036
|
+
await sharedServer.connect();
|
|
1037
|
+
const action = request.method === 'DELETE' ? 'delete' : queueItemMatch[3] === 'steer' ? 'steer' : null;
|
|
1038
|
+
if (!action) throw new RequestError(405, '不支持的队列操作');
|
|
1039
|
+
await requireExecutionWritable(threadId);
|
|
1040
|
+
sendJson(response, 200, await appServerEvents.changeQueue((method, params) => sharedServer.request(method, params), threadId, itemId, action)); return;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
const interruptMatch = /^\/api\/threads\/([^/]+)\/interrupt$/.exec(url.pathname);
|
|
1044
|
+
if (request.method === 'POST' && interruptMatch) {
|
|
1045
|
+
if (rootChat) throw new RequestError(405, 'Dots 主对话不提供停止 Codex 任务的操作。');
|
|
1046
|
+
const threadId = decodeURIComponent(interruptMatch[1]);
|
|
1047
|
+
await requireExecutionWritable(threadId, execution);
|
|
1048
|
+
const body = await readJsonBody(request);
|
|
1049
|
+
if (!body.turnId) throw new RequestError(400, '缺少活动轮次 ID。');
|
|
1050
|
+
await execution.interrupt(threadId, body.turnId);
|
|
1051
|
+
sendJson(response, 200, { requested: true });
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
const roomReadMatch = /^\/api\/threads\/([^/]+)\/read$/.exec(url.pathname);
|
|
1056
|
+
if (request.method === 'POST' && roomReadMatch) {
|
|
1057
|
+
if (!rootChat) throw new RequestError(405, '此对话不提供消息房间已读操作。');
|
|
1058
|
+
await dotsChat.markRead(decodeURIComponent(roomReadMatch[1]), await readJsonBody(request));
|
|
1059
|
+
sendJson(response, 200, { success: true });
|
|
1060
|
+
return;
|
|
443
1061
|
}
|
|
444
1062
|
|
|
1063
|
+
const messageStatusMatch = /^\/api\/threads\/([^/]+)\/messages\/([a-f0-9]{32})\/status$/.exec(url.pathname);
|
|
1064
|
+
if (request.method === 'GET' && messageStatusMatch) {
|
|
1065
|
+
const threadId = decodeURIComponent(messageStatusMatch[1]), clientId = messageStatusMatch[2];
|
|
1066
|
+
const result = await messageReceipts.reconcile([hostId, threadId], clientId, async () => {
|
|
1067
|
+
if (hostId !== 'durable') {
|
|
1068
|
+
await sharedServer.connect();
|
|
1069
|
+
const queue = await appServerEvents.readQueue((method, params) => sharedServer.request(method, params), threadId);
|
|
1070
|
+
if (queue.items.some(item => messageDelivery.normalizeId(item.clientUserMessageId) === clientId)) return queue;
|
|
1071
|
+
}
|
|
1072
|
+
return execution.findSentMessage(threadId, clientId, url.searchParams.get('anchorItemId') || '');
|
|
1073
|
+
});
|
|
1074
|
+
sendJson(response, 200, result);
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
445
1077
|
const messageMatch = /^\/api\/threads\/([^/]+)\/messages$/.exec(url.pathname);
|
|
446
|
-
if (request.method === 'POST' && messageMatch) {
|
|
447
|
-
const threadId = decodeURIComponent(messageMatch[1]);
|
|
448
|
-
await
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
1078
|
+
if (request.method === 'POST' && messageMatch) {
|
|
1079
|
+
const threadId = decodeURIComponent(messageMatch[1]);
|
|
1080
|
+
const body = await readJsonBody(request);
|
|
1081
|
+
if (body.hostId && body.hostId !== hostId) throw new RequestError(400, '请求中的对话主机不一致。');
|
|
1082
|
+
const result = await messageReceipts.submit([hostId, threadId], body.clientUserMessageId, async () => {
|
|
1083
|
+
let args;
|
|
1084
|
+
try { await requireExecutionWritable(threadId, execution); args = execution === dotsChat ? { ...body, threadId } : appServerTurnArguments(threadId, { ...body, hostId }); }
|
|
1085
|
+
catch (error) { error.deliveryUnknown = false; throw error; }
|
|
1086
|
+
messageSubmissionStarted = true;
|
|
1087
|
+
const responseFor = submitted => ({ success: true, data: submitted.result, usage: submitted.usage });
|
|
1088
|
+
try { return responseFor(await execution.send(args)); }
|
|
1089
|
+
catch (error) {
|
|
1090
|
+
if (error.deliveryResult) error.deliveryResult = error.deliveryResult.then(responseFor);
|
|
1091
|
+
throw error;
|
|
1092
|
+
}
|
|
1093
|
+
});
|
|
1094
|
+
sendJson(response, 200, result);
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
454
1097
|
|
|
455
1098
|
sendJson(response, 404, { error: '未找到请求的资源。' });
|
|
456
1099
|
} catch (error) {
|
|
@@ -458,49 +1101,76 @@ async function handleRequest(request, response) {
|
|
|
458
1101
|
if (!response.writableEnded) response.end();
|
|
459
1102
|
return;
|
|
460
1103
|
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
1104
|
+
if (!messageSubmissionStarted && error.deliveryUnknown === undefined && request.method === 'POST'
|
|
1105
|
+
&& /^\/api\/threads\/[^/]+\/(?:messages|queue)(?:\?|$)/.test(request.url || '')) error.deliveryUnknown = false;
|
|
1106
|
+
const status = error instanceof RequestError || error instanceof ThreadWriterConflictError || error instanceof FileRangeError ? (error.statusCode || 409) : 502;
|
|
1107
|
+
sendJson(response, status, {
|
|
1108
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1109
|
+
code: error?.code,
|
|
1110
|
+
threadId: error?.threadId,
|
|
1111
|
+
readOnly: error?.readOnly,
|
|
1112
|
+
details: error?.deliveryUnknown === undefined ? error?.details : { ...error?.details, deliveryUnknown: error.deliveryUnknown },
|
|
468
1113
|
});
|
|
469
1114
|
}
|
|
470
1115
|
}
|
|
471
1116
|
|
|
472
1117
|
const server = http.createServer(handleRequest);
|
|
473
1118
|
const lanSocket = attachLanSocket(server, { token: options.token, dispatch: handleRequest,
|
|
1119
|
+
uploadsRoot,
|
|
474
1120
|
authorizeDevice: (clientId, credential) => authorizeLanDevice(stateRoot, clientId, credential),
|
|
1121
|
+
onDeviceAuthorizationsChanged: listener => onLanAuthorizationsChanged(stateRoot, listener),
|
|
1122
|
+
onPhoneConnected: (...args) => connectionReporting(stateRoot).connected(...args),
|
|
1123
|
+
onPhoneDisconnected: id => connectionReporting(stateRoot).disconnected(id),
|
|
1124
|
+
onDeviceConnected: (clientId, info) => {
|
|
1125
|
+
try { recordLanConnection(stateRoot, clientId, info); }
|
|
1126
|
+
catch (error) { console.error('记录局域网设备连接失败:', error.message); }
|
|
1127
|
+
},
|
|
475
1128
|
});
|
|
476
1129
|
|
|
477
|
-
let control;
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
1130
|
+
let control;
|
|
1131
|
+
const powerManager = createPowerManager(stateRoot);
|
|
1132
|
+
runtimeDiagnostic('service.starting', { platform: process.platform, nodeVersion: process.version });
|
|
1133
|
+
void managedAppServer.start();
|
|
1134
|
+
executionHealth.start();
|
|
1135
|
+
runtimeDiscovery.start();
|
|
481
1136
|
server.on('error', error => {
|
|
482
|
-
console.error('
|
|
1137
|
+
console.error('桌面服务监听失败:', error.message);
|
|
483
1138
|
process.exit(1);
|
|
484
1139
|
});
|
|
485
1140
|
server.listen(options.port, options.lan ? '0.0.0.0' : '127.0.0.1', async () => {
|
|
486
1141
|
try {
|
|
1142
|
+
await powerManager.start();
|
|
487
1143
|
if (process.env.CODEX_REMOTE_INSTANCE && process.env.CODEX_REMOTE_CONTROL_SECRET && process.env.CODEX_REMOTE_READY_PATH) {
|
|
488
|
-
control = await openControl({ instanceId: process.env.CODEX_REMOTE_INSTANCE,
|
|
489
|
-
secret: process.env.CODEX_REMOTE_CONTROL_SECRET, shutdown: shutDown, actions: {
|
|
490
|
-
'refresh-execution': refreshExecution,
|
|
491
|
-
'
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
1144
|
+
control = await openControl({ instanceId: process.env.CODEX_REMOTE_INSTANCE,
|
|
1145
|
+
secret: process.env.CODEX_REMOTE_CONTROL_SECRET, shutdown: shutDown, actions: {
|
|
1146
|
+
'refresh-execution': refreshExecution,
|
|
1147
|
+
'keep-awake-status': () => powerManager.snapshot(),
|
|
1148
|
+
'keep-awake-enable': () => powerManager.setEnabled(true),
|
|
1149
|
+
'keep-awake-disable': () => powerManager.setEnabled(false),
|
|
1150
|
+
'relay-start': () => relayManager.run('relay-start'),
|
|
1151
|
+
'relay-status': () => relayManager.run('relay-status'),
|
|
1152
|
+
'relay-stop': () => relayManager.run('relay-stop'),
|
|
1153
|
+
'relay-pair': () => relayManager.run('relay-pair'),
|
|
1154
|
+
'relay-revoke': () => relayManager.run('relay-revoke'),
|
|
1155
|
+
'invite-create': details => relayManager.run('invite-create', details),
|
|
1156
|
+
'invite-list': () => relayManager.run('invite-list'),
|
|
1157
|
+
'invite-revoke': details => relayManager.run('invite-revoke', details),
|
|
1158
|
+
'devices-list': () => relayManager.run('devices-list'),
|
|
1159
|
+
'device-revoke': details => relayManager.run('device-revoke', details),
|
|
1160
|
+
'restart-managed-app-server': () => {
|
|
1161
|
+
const state = managedAppServer.snapshot();
|
|
1162
|
+
return state.launcherRestartRequired ? managedAppServer.restart(crypto.randomUUID()) : state;
|
|
1163
|
+
},
|
|
1164
|
+
'restart-managed-app-server-confirmed': () => managedAppServer.restart(crypto.randomUUID()),
|
|
1165
|
+
} });
|
|
496
1166
|
writeRecord(process.env.CODEX_REMOTE_READY_PATH, { instanceId: process.env.CODEX_REMOTE_INSTANCE,
|
|
497
1167
|
pid: process.pid, controlPort: control.port });
|
|
498
1168
|
}
|
|
499
1169
|
} catch (error) {
|
|
500
|
-
console.error('
|
|
1170
|
+
console.error('桌面服务管理通道启动失败:', error.message);
|
|
501
1171
|
process.exit(1);
|
|
502
1172
|
}
|
|
503
|
-
console.log(`Code Relax
|
|
1173
|
+
console.log(`Code Relax 桌面服务已启动:http://127.0.0.1:${options.port}/`);
|
|
504
1174
|
if (options.lan) {
|
|
505
1175
|
console.log('LAN 模式使用鉴权 WebSocket,仅适合受信任局域网测试,不要暴露到公网。');
|
|
506
1176
|
}
|
|
@@ -510,11 +1180,16 @@ let shuttingDown = false;
|
|
|
510
1180
|
async function shutDown() {
|
|
511
1181
|
if (shuttingDown) return;
|
|
512
1182
|
shuttingDown = true;
|
|
1183
|
+
await powerManager.close();
|
|
1184
|
+
desktopHelper?.release();
|
|
513
1185
|
lanSocket.close();
|
|
514
1186
|
server.close();
|
|
515
|
-
executionHealth?.stop();
|
|
516
|
-
runtimeDiscovery.stop();
|
|
517
|
-
|
|
1187
|
+
executionHealth?.stop();
|
|
1188
|
+
runtimeDiscovery.stop();
|
|
1189
|
+
await relayManager.close();
|
|
1190
|
+
dotsServer.close();
|
|
1191
|
+
dotsChat.close();
|
|
1192
|
+
managedAppServer.close();
|
|
518
1193
|
await control?.close();
|
|
519
1194
|
process.exit(0);
|
|
520
1195
|
}
|
|
@@ -656,6 +1331,14 @@ function messagePayload(body) {
|
|
|
656
1331
|
if (!fs.existsSync(file)) throw new RequestError(400, '图片不存在,请重新添加。');
|
|
657
1332
|
return file;
|
|
658
1333
|
});
|
|
1334
|
+
for (const input of fileMentions.originalInputs(body)) {
|
|
1335
|
+
const incoming = fs.realpathSync(path.join(uploadsRoot, 'incoming'));
|
|
1336
|
+
const imagePath = fs.realpathSync(input.path);
|
|
1337
|
+
if (path.dirname(imagePath).toLowerCase() !== incoming.toLowerCase()) throw new RequestError(400, '原图必须来自上传目录。');
|
|
1338
|
+
const stat = fs.statSync(imagePath);
|
|
1339
|
+
if (!stat.isFile() || stat.size <= 0) throw new RequestError(400, '原图为空。');
|
|
1340
|
+
images.push(imagePath);
|
|
1341
|
+
}
|
|
659
1342
|
if (images.filter(image => image.startsWith('data:')).reduce((size, image) => size + Buffer.byteLength(image.slice(image.indexOf(',') + 1), 'base64'), 0) > 8 * 1024 * 1024) throw new RequestError(413, '本条消息图片合计不能超过 8 MiB。');
|
|
660
1343
|
if (!prompt && !images.length) throw new RequestError(400, 'prompt 不能为空。');
|
|
661
1344
|
if (prompt.length > 32_000) throw new RequestError(400, 'prompt 不能超过 32000 个字符。');
|
|
@@ -663,9 +1346,7 @@ function messagePayload(body) {
|
|
|
663
1346
|
const payload = { prompt, images };
|
|
664
1347
|
if (typeof body.hostId === 'string' && body.hostId.trim()) payload.hostId = body.hostId.trim();
|
|
665
1348
|
if (typeof body.model === 'string' && body.model.trim()) {
|
|
666
|
-
|
|
667
|
-
if (!ALLOWED_MODELS.has(model)) throw new RequestError(400, '不支持的模型。');
|
|
668
|
-
payload.model = model;
|
|
1349
|
+
payload.model = body.model.trim();
|
|
669
1350
|
}
|
|
670
1351
|
if (typeof body.thinking === 'string' && body.thinking.trim()) {
|
|
671
1352
|
const thinking = body.thinking.trim();
|
|
@@ -681,83 +1362,119 @@ function messagePayload(body) {
|
|
|
681
1362
|
|
|
682
1363
|
function appServerTurnArguments(threadId, body) {
|
|
683
1364
|
const payload = messagePayload(body);
|
|
1365
|
+
const mentioned = payload.hostId === 'durable' ? fileMentions.parse(payload.prompt) : null;
|
|
1366
|
+
if (mentioned?.files.length) payload.prompt = mentioned.text;
|
|
684
1367
|
const input = [];
|
|
685
1368
|
if (payload.prompt) input.push({ type: 'text', text: payload.prompt });
|
|
686
|
-
for (const image of payload.images)
|
|
687
|
-
|
|
1369
|
+
for (const image of payload.images) {
|
|
1370
|
+
if (image.startsWith('data:')) input.push({ type: 'image', url: image });
|
|
1371
|
+
else if (payload.hostId === 'durable') {
|
|
1372
|
+
const mimeType = imageMimeType(image);
|
|
1373
|
+
if (!mimeType) throw new RequestError(400, '云端图片格式无效。');
|
|
1374
|
+
input.push({ type: 'image', url: `data:${mimeType};base64,${fs.readFileSync(image).toString('base64')}` });
|
|
1375
|
+
} else input.push({ type: 'localImage', path: image });
|
|
1376
|
+
}
|
|
1377
|
+
const value = { threadId, input, ...appServerEvents.mobileTurnSource(body.clientUserMessageId, body.containsVoiceInput) };
|
|
1378
|
+
if (mentioned?.files.length) value.uploadFiles = mentioned.files;
|
|
688
1379
|
if (typeof body.clientUserMessageId === 'string' && /^[a-f0-9]{32}$/.test(body.clientUserMessageId)) value.clientUserMessageId = body.clientUserMessageId;
|
|
689
1380
|
if (payload.model) value.model = payload.model;
|
|
690
1381
|
if (payload.thinking) value.effort = payload.thinking;
|
|
691
1382
|
return value;
|
|
692
1383
|
}
|
|
693
1384
|
|
|
694
|
-
function normalizeTimeline(data) {
|
|
695
|
-
const source = data && typeof data === 'object' ? data : {};
|
|
696
|
-
const sourceTurns = Array.isArray(source.turns) ? source.turns : source.thread?.turns;
|
|
697
|
-
const turns = Array.isArray(sourceTurns) ? sourceTurns.map(normalizeTurn) : [];
|
|
698
|
-
return {
|
|
1385
|
+
function normalizeTimeline(data) {
|
|
1386
|
+
const source = data && typeof data === 'object' ? data : {};
|
|
1387
|
+
const sourceTurns = Array.isArray(source.turns) ? source.turns : source.thread?.turns;
|
|
1388
|
+
const turns = Array.isArray(sourceTurns) ? sourceTurns.map(normalizeTurn) : [];
|
|
1389
|
+
return {
|
|
699
1390
|
schemaVersion: 1,
|
|
700
1391
|
thread: normalizeThread(source.thread),
|
|
701
1392
|
turns,
|
|
702
1393
|
};
|
|
703
1394
|
}
|
|
704
1395
|
|
|
705
|
-
function normalizeSharedTimeline({ thread, nextCursor, sync }) {
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
1396
|
+
function normalizeSharedTimeline({ thread, nextCursor, sync, notModified, incremental }, hostId = thread?.hostId || 'local') {
|
|
1397
|
+
const timeline = normalizeTimeline({ thread: { ...thread, title: thread.name || thread.title || '', hostId,
|
|
1398
|
+
transport: 'shared-app-server' } });
|
|
1399
|
+
return { ...timeline,
|
|
1400
|
+
historyItemCount: (thread.turns || []).reduce((count, turn) => count + (turn.roomMessage ? 1 : turn.items?.length || 0), 0),
|
|
1401
|
+
historyView: thread?.threadSource === 'aeon' ? 'chat-items-v1' : 'item-pages-v1',
|
|
1402
|
+
...(notModified ? { notModified: true } : {}),
|
|
1403
|
+
...(incremental ? { incremental: true } : {}),
|
|
1404
|
+
...(nextCursor !== undefined ? { nextCursor } : {}), ...(sync ? { sync } : {}) };
|
|
1405
|
+
}
|
|
710
1406
|
|
|
711
|
-
function
|
|
1407
|
+
async function prepareSharedTimeline(value, hostId) {
|
|
1408
|
+
if (value.thread?.threadSource !== 'aeon') return normalizeSharedTimeline(value, hostId);
|
|
1409
|
+
const epoch = dotsChat.epoch, threadId = value.thread.id;
|
|
1410
|
+
const current = () => {
|
|
1411
|
+
if (epoch !== dotsChat.epoch) throw new Error('点点连接已变化,请重新同步。');
|
|
1412
|
+
};
|
|
1413
|
+
if (value.notModified) {
|
|
1414
|
+
const record = dotsChat.threads.get(threadId);
|
|
1415
|
+
if (await dotsImages.prepareTimeline(record?.thread)) {
|
|
1416
|
+
current();
|
|
1417
|
+
value = dotsChat.snapshot(threadId, record);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
const snapshot = structuredClone(value);
|
|
1421
|
+
await dotsImages.prepareTimeline(snapshot.thread);
|
|
1422
|
+
current();
|
|
1423
|
+
return normalizeSharedTimeline(snapshot, hostId);
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
function normalizeThread(thread) {
|
|
712
1427
|
const source = thread && typeof thread === 'object' ? thread : {};
|
|
713
1428
|
return {
|
|
714
|
-
id: source.id || '',
|
|
715
|
-
title: source.name || source.title || source.preview || '',
|
|
716
|
-
status: statusText(source.status),
|
|
717
|
-
updatedAt: Number(source.updatedAt) || 0,
|
|
718
|
-
recencyAt: Number(source.recencyAt ?? source.updatedAt) || 0,
|
|
719
|
-
projectId: source.projectId || '',
|
|
1429
|
+
id: source.id || '',
|
|
1430
|
+
title: source.name || source.title || source.preview || '',
|
|
1431
|
+
status: statusText(source.status),
|
|
1432
|
+
updatedAt: Number(source.updatedAt) || 0,
|
|
1433
|
+
recencyAt: Number(source.recencyAt ?? source.updatedAt) || 0,
|
|
1434
|
+
projectId: source.projectId || '',
|
|
720
1435
|
cwd: source.cwd || '',
|
|
721
1436
|
hostId: source.hostId || '',
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
1437
|
+
threadSource: source.threadSource || '',
|
|
1438
|
+
...(['aeon', 'aeon_child'].includes(source.threadSource) ? { chatRevision: source.chatRevision || '' } : {}),
|
|
1439
|
+
...(source.threadSource === 'aeon' ? { avatar: source.avatar || '', color: source.color || '',
|
|
1440
|
+
chatMessageRevision: source.chatMessageRevision, messagingRoomId: source.messagingRoomId, lastMessageAt: source.lastMessageAt } : {}),
|
|
1441
|
+
transport: source.transport || 'shared-app-server',
|
|
1442
|
+
readOnly: source.readOnly === true,
|
|
1443
|
+
readOnlyReason: source.readOnlyReason || '',
|
|
725
1444
|
};
|
|
726
1445
|
}
|
|
727
1446
|
|
|
728
|
-
function normalizeTurn(turn) {
|
|
729
|
-
const source = turn && typeof turn === 'object' ? turn : {};
|
|
730
|
-
const turnId = source.id || '';
|
|
731
|
-
const entries = Array.isArray(source.items) ? source.items.map(normalizeItem).filter(Boolean) : [];
|
|
732
|
-
for (const entry of entries) {
|
|
733
|
-
const images = [...(entry.attachments || []), entry.image, ...Object.values(entry.imageAssets || {})].filter(Boolean);
|
|
734
|
-
for (const image of images) if (image.remotePath && !image.recoveryTurnId) image.recoveryTurnId = turnId;
|
|
735
|
-
}
|
|
736
|
-
return {
|
|
737
|
-
id: turnId,
|
|
738
|
-
status: statusText(source.status),
|
|
1447
|
+
function normalizeTurn(turn) {
|
|
1448
|
+
const source = turn && typeof turn === 'object' ? turn : {};
|
|
1449
|
+
const turnId = source.id || '';
|
|
1450
|
+
const entries = Array.isArray(source.items) ? source.items.map(normalizeItem).filter(Boolean) : [];
|
|
1451
|
+
for (const entry of entries) {
|
|
1452
|
+
const images = [...(entry.attachments || []), entry.image, ...Object.values(entry.imageAssets || {})].filter(Boolean);
|
|
1453
|
+
for (const image of images) if (image.remotePath && !image.recoveryTurnId) image.recoveryTurnId = turnId;
|
|
1454
|
+
}
|
|
1455
|
+
return {
|
|
1456
|
+
id: turnId,
|
|
1457
|
+
status: statusText(source.status),
|
|
739
1458
|
startedAt: source.startedAt || null,
|
|
740
1459
|
completedAt: source.completedAt || null,
|
|
741
1460
|
durationMs: source.durationMs ?? null,
|
|
742
|
-
error: source.error ?? null,
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
};
|
|
760
|
-
}
|
|
1461
|
+
error: source.error ?? null,
|
|
1462
|
+
runtimeNotice: source.runtimeNotice ?? null,
|
|
1463
|
+
...(source.itemPage ? { itemPage: source.itemPage } : {}),
|
|
1464
|
+
...(source.roomMessage ? { roomMessage: true } : {}),
|
|
1465
|
+
...(source.deleted ? { deleted: true } : {}),
|
|
1466
|
+
...(source.historyOnly ? { historyOnly: true } : {}),
|
|
1467
|
+
...(source.cacheStub ? { cacheStub: true } : {}),
|
|
1468
|
+
entries,
|
|
1469
|
+
};
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
function normalizeItem(item) {
|
|
1473
|
+
if (!item || typeof item !== 'object') return null;
|
|
1474
|
+
|
|
1475
|
+
if (item.type === 'approval') {
|
|
1476
|
+
return { ...item, type: 'activity', kind: 'approval', title: item.params?.message || '需要你的批准' };
|
|
1477
|
+
}
|
|
761
1478
|
|
|
762
1479
|
if (item.type === 'contextCompaction') {
|
|
763
1480
|
return {
|
|
@@ -768,24 +1485,21 @@ function normalizeItem(item) {
|
|
|
768
1485
|
};
|
|
769
1486
|
}
|
|
770
1487
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
type: 'message',
|
|
778
|
-
id: item.id || '',
|
|
779
|
-
role: 'user',
|
|
780
|
-
source: 'remote',
|
|
781
|
-
...splitUploadedImages(text),
|
|
782
|
-
};
|
|
783
|
-
}
|
|
1488
|
+
const delegated = appServerEvents.toolDelegation(item);
|
|
1489
|
+
if (delegated) {
|
|
1490
|
+
return {
|
|
1491
|
+
type: 'message', id: item.id || '', role: 'user', source: 'remote',
|
|
1492
|
+
delegation: delegationSource(delegated), ...splitUploadedImages(delegated.input),
|
|
1493
|
+
};
|
|
784
1494
|
}
|
|
785
1495
|
|
|
786
1496
|
if (item.type === 'userMessage') {
|
|
787
|
-
const text = extractUserText(item.content);
|
|
788
|
-
const attachments = normalizeAttachments(item.content)
|
|
1497
|
+
const { text, files, delegation } = extractUserText(item.content);
|
|
1498
|
+
const attachments = [...normalizeAttachments(item.content), ...files.map(file => file.fileId ? normalizeAttachment(file) : ({
|
|
1499
|
+
...file,
|
|
1500
|
+
remotePath: file.path,
|
|
1501
|
+
previewRemotePath: file.previewPath || '',
|
|
1502
|
+
}))];
|
|
789
1503
|
if (!text && !attachments.length) return null;
|
|
790
1504
|
return {
|
|
791
1505
|
clientId: item.clientId,
|
|
@@ -794,6 +1508,7 @@ function normalizeItem(item) {
|
|
|
794
1508
|
role: 'user',
|
|
795
1509
|
text,
|
|
796
1510
|
attachments,
|
|
1511
|
+
...(delegation ? { delegation: delegationSource(delegation) } : {}),
|
|
797
1512
|
};
|
|
798
1513
|
}
|
|
799
1514
|
|
|
@@ -806,172 +1521,180 @@ function normalizeItem(item) {
|
|
|
806
1521
|
delivery: item.delivery,
|
|
807
1522
|
questions: item.questions,
|
|
808
1523
|
text: item.text || '',
|
|
1524
|
+
attachments: normalizeAttachments(item.attachments),
|
|
809
1525
|
imageAssets: markdownImageAssets(item.text || ''),
|
|
810
1526
|
fileAssets: markdownFileAssets(item.text || ''),
|
|
811
1527
|
};
|
|
812
1528
|
}
|
|
813
1529
|
|
|
814
|
-
if (item.type === 'reasoning') {
|
|
815
|
-
const detail = Array.isArray(item.summary)
|
|
816
|
-
? item.summary.map(summaryText).filter(Boolean).join('\n')
|
|
817
|
-
: summaryText(item.summary);
|
|
818
|
-
return {
|
|
819
|
-
type: 'activity',
|
|
820
|
-
id: item.id || '',
|
|
821
|
-
kind: 'reasoning',
|
|
822
|
-
title: '思考摘要',
|
|
823
|
-
detail,
|
|
824
|
-
};
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
if (item.type === 'commandExecution') {
|
|
828
|
-
return {
|
|
829
|
-
type: 'activity',
|
|
830
|
-
id: item.id || '',
|
|
831
|
-
kind: 'command',
|
|
832
|
-
title: '命令',
|
|
833
|
-
titleOnly: true,
|
|
834
|
-
status: statusText(item.status),
|
|
835
|
-
command: item.command,
|
|
836
|
-
cwd: item.cwd,
|
|
837
|
-
exitCode: item.exitCode,
|
|
838
|
-
durationMs: item.durationMs,
|
|
839
|
-
};
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
if (item.type === 'mcpToolCall') {
|
|
843
|
-
return {
|
|
844
|
-
type: 'activity',
|
|
845
|
-
id: item.id || '',
|
|
846
|
-
kind: 'tool',
|
|
847
|
-
title: '工具',
|
|
848
|
-
titleOnly: true,
|
|
849
|
-
status: statusText(item.status),
|
|
850
|
-
server: item.server,
|
|
851
|
-
tool: item.tool,
|
|
852
|
-
arguments: item.arguments,
|
|
853
|
-
durationMs: item.durationMs,
|
|
854
|
-
error: item.error,
|
|
855
|
-
};
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
if (item.type === 'dynamicToolCall') {
|
|
859
|
-
return {
|
|
860
|
-
type: 'activity',
|
|
861
|
-
id: item.id || '',
|
|
862
|
-
kind: 'tool',
|
|
863
|
-
title: '工具',
|
|
864
|
-
titleOnly: true,
|
|
865
|
-
status: statusText(item.status),
|
|
866
|
-
namespace: item.namespace,
|
|
867
|
-
tool: item.tool,
|
|
868
|
-
arguments: item.arguments,
|
|
869
|
-
durationMs: item.durationMs,
|
|
870
|
-
success: item.success,
|
|
871
|
-
};
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
if (item.type === 'fileChange') {
|
|
875
|
-
return {
|
|
876
|
-
type: 'activity',
|
|
877
|
-
id: item.id || '',
|
|
878
|
-
kind: 'fileChange',
|
|
879
|
-
title: '文件修改',
|
|
880
|
-
titleOnly: true,
|
|
881
|
-
status: statusText(item.status),
|
|
882
|
-
changes:
|
|
883
|
-
};
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
if (item.type === 'webSearch') {
|
|
887
|
-
return {
|
|
888
|
-
type: 'activity',
|
|
889
|
-
id: item.id || '',
|
|
890
|
-
kind: 'webSearch',
|
|
891
|
-
title: '网页搜索',
|
|
892
|
-
titleOnly: true,
|
|
893
|
-
query: item.query || item.action?.query || '',
|
|
894
|
-
...(item.action && typeof item.action === 'object' ? { action: item.action } : {}),
|
|
895
|
-
};
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
if (item.type === 'imageView') {
|
|
899
|
-
|
|
900
|
-
type: '
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
type: '
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
1530
|
+
if (item.type === 'reasoning') {
|
|
1531
|
+
const detail = Array.isArray(item.summary)
|
|
1532
|
+
? item.summary.map(summaryText).filter(Boolean).join('\n')
|
|
1533
|
+
: summaryText(item.summary);
|
|
1534
|
+
return {
|
|
1535
|
+
type: 'activity',
|
|
1536
|
+
id: item.id || '',
|
|
1537
|
+
kind: 'reasoning',
|
|
1538
|
+
title: '思考摘要',
|
|
1539
|
+
detail,
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
if (item.type === 'commandExecution') {
|
|
1544
|
+
return {
|
|
1545
|
+
type: 'activity',
|
|
1546
|
+
id: item.id || '',
|
|
1547
|
+
kind: 'command',
|
|
1548
|
+
title: '命令',
|
|
1549
|
+
titleOnly: true,
|
|
1550
|
+
status: statusText(item.status),
|
|
1551
|
+
command: item.command,
|
|
1552
|
+
cwd: item.cwd,
|
|
1553
|
+
exitCode: item.exitCode,
|
|
1554
|
+
durationMs: item.durationMs,
|
|
1555
|
+
};
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
if (item.type === 'mcpToolCall') {
|
|
1559
|
+
return {
|
|
1560
|
+
type: 'activity',
|
|
1561
|
+
id: item.id || '',
|
|
1562
|
+
kind: 'tool',
|
|
1563
|
+
title: '工具',
|
|
1564
|
+
titleOnly: true,
|
|
1565
|
+
status: statusText(item.status),
|
|
1566
|
+
server: item.server,
|
|
1567
|
+
tool: item.tool,
|
|
1568
|
+
arguments: item.arguments,
|
|
1569
|
+
durationMs: item.durationMs,
|
|
1570
|
+
error: item.error,
|
|
1571
|
+
};
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
if (item.type === 'dynamicToolCall') {
|
|
1575
|
+
return {
|
|
1576
|
+
type: 'activity',
|
|
1577
|
+
id: item.id || '',
|
|
1578
|
+
kind: 'tool',
|
|
1579
|
+
title: '工具',
|
|
1580
|
+
titleOnly: true,
|
|
1581
|
+
status: statusText(item.status),
|
|
1582
|
+
namespace: item.namespace,
|
|
1583
|
+
tool: item.tool,
|
|
1584
|
+
arguments: item.arguments,
|
|
1585
|
+
durationMs: item.durationMs,
|
|
1586
|
+
success: item.success,
|
|
1587
|
+
};
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
if (item.type === 'fileChange') {
|
|
1591
|
+
return {
|
|
1592
|
+
type: 'activity',
|
|
1593
|
+
id: item.id || '',
|
|
1594
|
+
kind: 'fileChange',
|
|
1595
|
+
title: '文件修改',
|
|
1596
|
+
titleOnly: true,
|
|
1597
|
+
status: statusText(item.status),
|
|
1598
|
+
changes: fileChanges.summarize(item.changes),
|
|
1599
|
+
};
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
if (item.type === 'webSearch') {
|
|
1603
|
+
return {
|
|
1604
|
+
type: 'activity',
|
|
1605
|
+
id: item.id || '',
|
|
1606
|
+
kind: 'webSearch',
|
|
1607
|
+
title: '网页搜索',
|
|
1608
|
+
titleOnly: true,
|
|
1609
|
+
query: item.query || item.action?.query || '',
|
|
1610
|
+
...(item.action && typeof item.action === 'object' ? { action: item.action } : {}),
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
if (item.type === 'imageView') {
|
|
1615
|
+
const image = typeof item.path === 'string' && item.path
|
|
1616
|
+
? normalizeAttachment({ type: 'localImage', path: item.path })
|
|
1617
|
+
: null;
|
|
1618
|
+
return {
|
|
1619
|
+
type: 'activity',
|
|
1620
|
+
id: item.id || '',
|
|
1621
|
+
kind: 'imageView',
|
|
1622
|
+
title: '查看图片',
|
|
1623
|
+
titleOnly: true,
|
|
1624
|
+
path: item.path || '',
|
|
1625
|
+
image,
|
|
1626
|
+
};
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
if (item.type === 'imageGeneration') {
|
|
1630
|
+
const result = typeof item.result === 'string' ? item.result.trim() : '';
|
|
1631
|
+
const image = typeof item.savedPath === 'string' && item.savedPath
|
|
1632
|
+
? normalizeAttachment({ type: 'localImage', path: item.savedPath, name: '生成的图片' })
|
|
1633
|
+
: result ? normalizeAttachment({ type: 'image', name: '生成的图片',
|
|
1634
|
+
url: result.startsWith('data:image/') ? result : `data:image/png;base64,${result}` }) : null;
|
|
1635
|
+
return {
|
|
1636
|
+
type: 'activity',
|
|
1637
|
+
id: item.id || '',
|
|
1638
|
+
kind: 'imageGeneration',
|
|
1639
|
+
title: '生成图片',
|
|
1640
|
+
titleOnly: true,
|
|
1641
|
+
status: statusText(item.status),
|
|
1642
|
+
image,
|
|
1643
|
+
failure: item.failure || null,
|
|
1644
|
+
transparentBackground: item.transparentBackground === true,
|
|
1645
|
+
};
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
if (item.type === 'subAgentActivity') {
|
|
1649
|
+
return {
|
|
1650
|
+
type: 'activity',
|
|
1651
|
+
id: item.id || '',
|
|
1652
|
+
kind: 'subAgent',
|
|
1653
|
+
title: '子任务',
|
|
1654
|
+
titleOnly: true,
|
|
1655
|
+
status: statusText(item.kind),
|
|
1656
|
+
agentPath: item.agentPath || '',
|
|
1657
|
+
agentThreadId: item.agentThreadId || '',
|
|
1658
|
+
};
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
if (item.type === 'collabAgentToolCall') {
|
|
1662
|
+
return {
|
|
1663
|
+
type: 'activity',
|
|
1664
|
+
id: item.id || '',
|
|
1665
|
+
kind: 'collaboration',
|
|
1666
|
+
title: '任务协作',
|
|
1667
|
+
titleOnly: true,
|
|
1668
|
+
status: statusText(item.status),
|
|
1669
|
+
tool: item.tool || '',
|
|
1670
|
+
prompt: item.prompt || '',
|
|
1671
|
+
senderThreadId: item.senderThreadId || '',
|
|
1672
|
+
receiverThreadIds: Array.isArray(item.receiverThreadIds) ? item.receiverThreadIds : [],
|
|
1673
|
+
model: item.model || '',
|
|
1674
|
+
reasoningEffort: item.reasoningEffort || '',
|
|
1675
|
+
agentsStates: item.agentsStates && typeof item.agentsStates === 'object' ? item.agentsStates : {},
|
|
1676
|
+
};
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
return {
|
|
957
1680
|
type: 'activity',
|
|
958
1681
|
id: item.id || '',
|
|
959
1682
|
kind: item.type || 'event',
|
|
960
1683
|
title: item.type || '事件',
|
|
961
1684
|
status: statusText(item.status),
|
|
962
1685
|
detail: JSON.stringify(item, null, 2),
|
|
963
|
-
};
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
function sharedHistoryPageOptions() {
|
|
967
|
-
return {
|
|
968
|
-
maxTurns: SHARED_HISTORY_PAGE_TURNS,
|
|
969
|
-
maxBytes: SHARED_HISTORY_PAGE_BYTES,
|
|
970
|
-
measure: value => Buffer.byteLength(JSON.stringify(normalizeSharedTimeline(value))),
|
|
971
|
-
};
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
function normalizeAttachments(content) {
|
|
1686
|
+
};
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
function sharedHistoryPageOptions() {
|
|
1690
|
+
return {
|
|
1691
|
+
maxTurns: SHARED_HISTORY_PAGE_TURNS,
|
|
1692
|
+
maxBytes: SHARED_HISTORY_PAGE_BYTES,
|
|
1693
|
+
measure: value => Buffer.byteLength(JSON.stringify(normalizeSharedTimeline(value))),
|
|
1694
|
+
};
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
function normalizeAttachments(content) {
|
|
975
1698
|
if (!Array.isArray(content)) return [];
|
|
976
1699
|
const items = content.filter(item => item && !['text', 'input_text'].includes(item.type)).map(item => ({ ...item }));
|
|
977
1700
|
const text = content.filter(item => ['text', 'input_text'].includes(item?.type)).map(item => item.text || '').join('\n');
|
|
@@ -983,7 +1706,7 @@ function normalizeAttachments(content) {
|
|
|
983
1706
|
else items.push({ type: 'localImage', path: imagePath });
|
|
984
1707
|
}
|
|
985
1708
|
const seen = new Set();
|
|
986
|
-
return items.map(normalizeAttachment).filter(item => {
|
|
1709
|
+
return items.map(item => normalizeAttachment(item)).filter(item => {
|
|
987
1710
|
if (!item.url) return true;
|
|
988
1711
|
if (seen.has(item.url)) return false;
|
|
989
1712
|
seen.add(item.url); return true;
|
|
@@ -995,64 +1718,55 @@ function markdownImageAssets(text) {
|
|
|
995
1718
|
function visit(tokens) {
|
|
996
1719
|
for (const token of tokens) {
|
|
997
1720
|
const value = token.type === 'image' ? token.attrGet('src')
|
|
998
|
-
: token.type === 'link_open' ? token.attrGet('href') : null;
|
|
1721
|
+
: token.type === 'link_open' && !token.meta?.fileCitation ? token.attrGet('href') : null;
|
|
999
1722
|
if (value && !Object.hasOwn(assets, value)) {
|
|
1000
|
-
|
|
1001
|
-
try { localPath = decodeURIComponent(value); } catch { continue; }
|
|
1723
|
+
const localPath = fileCitations.localReference(value)?.path;
|
|
1002
1724
|
// Only paths actually referenced by Desktop messages become accessible.
|
|
1003
1725
|
// Do not expose a client-supplied filesystem path endpoint or UNC shares.
|
|
1004
|
-
if (
|
|
1005
|
-
localPath = localPath.replace(/^\/(?=[a-z]:)/i, '');
|
|
1726
|
+
if (localPath && /^[a-z]:[\\/]/i.test(localPath)) {
|
|
1006
1727
|
const asset = normalizeAttachment({ type: 'localImage', path: localPath });
|
|
1007
1728
|
if (asset.available) assets[value] = asset;
|
|
1008
1729
|
}
|
|
1009
1730
|
}
|
|
1010
|
-
if (token.children) visit(token.children);
|
|
1731
|
+
if (token.children && token.type !== 'image') visit(token.children);
|
|
1011
1732
|
}
|
|
1012
1733
|
}
|
|
1013
|
-
visit(imageMarkdown.parse(text, {}));
|
|
1734
|
+
visit(imageMarkdown.parse(text, { allowFileCitations: true }));
|
|
1014
1735
|
return assets;
|
|
1015
1736
|
}
|
|
1016
1737
|
|
|
1017
|
-
function markdownFileAssets(text) {
|
|
1738
|
+
function markdownFileAssets(text) {
|
|
1018
1739
|
const assets = Object.create(null);
|
|
1019
1740
|
function visit(tokens) {
|
|
1020
1741
|
for (const token of tokens) {
|
|
1021
1742
|
if (token.type === 'link_open') {
|
|
1022
1743
|
const value = token.attrGet('href');
|
|
1023
1744
|
if (value && !Object.hasOwn(assets, value)) {
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
localPath = localPath.replace(/:(\d+)(?::\d+)?$/, '');
|
|
1029
|
-
const asset = rememberLocalFile(localPath);
|
|
1030
|
-
if (asset) assets[value] = { remotePath: asset.id, name: asset.name, size: asset.size };
|
|
1745
|
+
const localPath = fileCitations.localReference(value)?.path;
|
|
1746
|
+
if (localPath && /^[a-z]:[\\/]/i.test(localPath)) {
|
|
1747
|
+
assets[value] = { remotePath: localPath, name: path.basename(localPath) };
|
|
1748
|
+
// Link rendering must not depend on file existence or transient backend state.
|
|
1031
1749
|
}
|
|
1032
1750
|
}
|
|
1033
1751
|
}
|
|
1034
|
-
if (token.children) visit(token.children);
|
|
1752
|
+
if (token.children && token.type !== 'image') visit(token.children);
|
|
1035
1753
|
}
|
|
1036
1754
|
}
|
|
1037
|
-
visit(imageMarkdown.parse(text, {}));
|
|
1755
|
+
visit(imageMarkdown.parse(text, { allowFileCitations: true }));
|
|
1038
1756
|
return assets;
|
|
1039
1757
|
}
|
|
1040
1758
|
|
|
1041
|
-
function
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
while (fileCache.size >= MAX_CACHED_FILES) fileCache.delete(fileCache.keys().next().value);
|
|
1050
|
-
fileCache.set(id, { id, path: resolved, name: path.basename(resolved), size: stat.size, mtimeMs: stat.mtimeMs });
|
|
1051
|
-
}
|
|
1052
|
-
return fileCache.get(id);
|
|
1053
|
-
} catch {
|
|
1054
|
-
return null;
|
|
1759
|
+
function describeLocalFile(filePath) {
|
|
1760
|
+
const resolved = path.resolve(filePath);
|
|
1761
|
+
let stat;
|
|
1762
|
+
try { stat = fs.statSync(resolved); }
|
|
1763
|
+
catch (error) {
|
|
1764
|
+
if (error.code === 'ENOENT') throw new RequestError(404, '电脑上的文件已不存在。');
|
|
1765
|
+
if (error.code === 'EACCES' || error.code === 'EPERM') throw new RequestError(403, '电脑系统无法读取此文件:' + error.code);
|
|
1766
|
+
throw error;
|
|
1055
1767
|
}
|
|
1768
|
+
if (!stat.isFile()) throw new RequestError(400, '此路径不是文件。');
|
|
1769
|
+
return { path: resolved, name: path.basename(resolved), size: stat.size, revision: fileRevision(stat) };
|
|
1056
1770
|
}
|
|
1057
1771
|
|
|
1058
1772
|
function splitUploadedImages(text) {
|
|
@@ -1069,21 +1783,38 @@ function splitUploadedImages(text) {
|
|
|
1069
1783
|
const paths = children.map(token => {
|
|
1070
1784
|
try { return path.resolve(decodeURIComponent(token.attrGet('src') || '')); } catch { return ''; }
|
|
1071
1785
|
});
|
|
1072
|
-
if (paths.some(file =>
|
|
1786
|
+
if (paths.some(file => path.dirname(file).toLowerCase() !== uploadsRoot.toLowerCase()
|
|
1073
1787
|
|| !/^[a-f0-9]{64}\.(png|webp)$/.test(path.basename(file)))) continue;
|
|
1074
|
-
for (const file of paths) attachments.push(normalizeAttachment({ type: 'localImage', path: file, name: '
|
|
1788
|
+
for (const file of paths) attachments.push(normalizeAttachment({ type: 'localImage', path: file, name: '移动端图片' }));
|
|
1075
1789
|
for (let line = block.map[0]; line < block.map[1]; line++) removed.add(line);
|
|
1076
1790
|
}
|
|
1077
1791
|
const visibleText = lines.filter((line, index) => !removed.has(index)).join('\n').trim();
|
|
1078
1792
|
return { text: visibleText, attachments, imageAssets: markdownImageAssets(visibleText) };
|
|
1079
1793
|
}
|
|
1080
1794
|
|
|
1081
|
-
function
|
|
1795
|
+
async function externalDownloadAsset(value, recovery = '') {
|
|
1796
|
+
const match = /^([a-f0-9]{64})(?:\?recovery=([A-Za-z0-9_-]{1,4096}))?$/.exec(value || '');
|
|
1797
|
+
if (!match) throw new RequestError(400, '无效的外部文件标识。');
|
|
1798
|
+
const asset = attachmentCache.get(match[1]) || dotsImages.restore(match[1], match[2] || recovery || '');
|
|
1799
|
+
if (!asset?.dotsFile) throw new RequestError(404, '外部文件授权已失效,请重新读取对话。');
|
|
1800
|
+
await dotsImages.authorization(asset.identity);
|
|
1801
|
+
return asset;
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
function workstationCopyReference(copy, asset) {
|
|
1805
|
+
const id = crypto.createHmac('sha256', fileCapabilitySecret).update(JSON.stringify([asset.id, copy.path, copy.revision])).digest('hex');
|
|
1806
|
+
fileCache.set(id, { ...copy, id, externalIdentity: asset.identity });
|
|
1807
|
+
while (fileCache.size > MAX_CACHED_FILES) fileCache.delete(fileCache.keys().next().value);
|
|
1808
|
+
return { remotePath: id, path: copy.path, name: copy.name, size: copy.size, mimeType: copy.mimeType };
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
function normalizeAttachment(item, identity) {
|
|
1082
1812
|
const name = item.name || item.fileName || (typeof item.path === 'string' ? path.basename(item.path) : '');
|
|
1083
1813
|
let cached = null;
|
|
1084
1814
|
const isImage = ['image', 'input_image', 'localImage'].includes(item.type);
|
|
1815
|
+
if (isImage || item.type === 'file') cached = dotsImages.remember(item, identity);
|
|
1085
1816
|
if (isImage && typeof item.path === 'string') {
|
|
1086
|
-
cached = cacheLocalImage(item.path, name);
|
|
1817
|
+
cached = cacheLocalImage(item.path, name) || cached;
|
|
1087
1818
|
}
|
|
1088
1819
|
if (!cached) {
|
|
1089
1820
|
const dataUrl = typeof item.url === 'string' && item.url.startsWith('data:image/')
|
|
@@ -1093,26 +1824,28 @@ function normalizeAttachment(item) {
|
|
|
1093
1824
|
}
|
|
1094
1825
|
return {
|
|
1095
1826
|
type: isImage ? 'image' : item.type || 'attachment',
|
|
1096
|
-
name,
|
|
1827
|
+
name: cached?.name || name,
|
|
1097
1828
|
mimeType: cached?.mimeType || item.mimeType || '',
|
|
1829
|
+
...(cached?.size ? { size: cached.size } : {}),
|
|
1830
|
+
...(cached?.dotsFile ? { networkFile: true } : {}),
|
|
1098
1831
|
available: Boolean(cached),
|
|
1099
|
-
remotePath: cached ? cached.id : '',
|
|
1100
|
-
recoveryToken: cached?.recoveryToken || '',
|
|
1832
|
+
remotePath: cached ? (isImage ? cached.id : `${cached.id}?recovery=${cached.recoveryToken}`) : '',
|
|
1833
|
+
recoveryToken: cached?.recoveryToken || '',
|
|
1101
1834
|
url: '',
|
|
1102
1835
|
thumbnailUrl: '',
|
|
1103
1836
|
};
|
|
1104
1837
|
}
|
|
1105
1838
|
|
|
1106
|
-
function cacheLocalImage(filePath, name) {
|
|
1107
|
-
try {
|
|
1108
|
-
const resolved = path.resolve(filePath);
|
|
1109
|
-
const stat = fs.statSync(resolved);
|
|
1110
|
-
const mimeType = imageMimeType(resolved);
|
|
1111
|
-
if (!stat.isFile() || !mimeType || stat.size >
|
|
1112
|
-
const cached = rememberAttachment(fs.readFileSync(resolved), mimeType, name,
|
|
1113
|
-
localImageRecoveryToken(resolved, stat));
|
|
1114
|
-
void attachmentThumbnail(cached).catch(() => {});
|
|
1115
|
-
return cached;
|
|
1839
|
+
function cacheLocalImage(filePath, name) {
|
|
1840
|
+
try {
|
|
1841
|
+
const resolved = path.resolve(filePath);
|
|
1842
|
+
const stat = fs.statSync(resolved);
|
|
1843
|
+
const mimeType = imageMimeType(resolved);
|
|
1844
|
+
if (!stat.isFile() || !mimeType || stat.size > MAX_IMAGE_PREVIEW_BYTES) return null;
|
|
1845
|
+
const cached = rememberAttachment(fs.readFileSync(resolved), mimeType, name,
|
|
1846
|
+
localImageRecoveryToken(resolved, stat));
|
|
1847
|
+
void attachmentThumbnail(cached).catch(() => {});
|
|
1848
|
+
return cached;
|
|
1116
1849
|
} catch {
|
|
1117
1850
|
return null;
|
|
1118
1851
|
}
|
|
@@ -1123,72 +1856,72 @@ function cacheDataImage(dataUrl, name) {
|
|
|
1123
1856
|
if (!match) return null;
|
|
1124
1857
|
try {
|
|
1125
1858
|
const buffer = Buffer.from(match[2], 'base64');
|
|
1126
|
-
if (!buffer.length || buffer.length >
|
|
1859
|
+
if (!buffer.length || buffer.length > MAX_IMAGE_PREVIEW_BYTES) return null;
|
|
1127
1860
|
return rememberAttachment(buffer, match[1].toLowerCase(), name);
|
|
1128
1861
|
} catch {
|
|
1129
1862
|
return null;
|
|
1130
1863
|
}
|
|
1131
1864
|
}
|
|
1132
1865
|
|
|
1133
|
-
function rememberAttachment(buffer, mimeType, name, recoveryToken = '') {
|
|
1134
|
-
const id = crypto.createHash('sha256').update(buffer).digest('hex');
|
|
1135
|
-
if (!attachmentCache.has(id)) {
|
|
1866
|
+
function rememberAttachment(buffer, mimeType, name, recoveryToken = '') {
|
|
1867
|
+
const id = crypto.createHash('sha256').update(buffer).digest('hex');
|
|
1868
|
+
if (!attachmentCache.has(id)) {
|
|
1136
1869
|
while (attachmentCache.size >= MAX_CACHED_ATTACHMENTS) {
|
|
1137
1870
|
attachmentCache.delete(attachmentCache.keys().next().value);
|
|
1138
|
-
}
|
|
1139
|
-
attachmentCache.set(id, { id, buffer, mimeType, name, recoveryToken });
|
|
1140
|
-
} else if (recoveryToken && !attachmentCache.get(id).recoveryToken) {
|
|
1141
|
-
attachmentCache.get(id).recoveryToken = recoveryToken;
|
|
1142
|
-
}
|
|
1143
|
-
return attachmentCache.get(id);
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
function localImageRecoveryToken(filePath, stat) {
|
|
1147
|
-
const iv = crypto.randomBytes(12);
|
|
1148
|
-
const cipher = crypto.createCipheriv('aes-256-gcm', imageCapabilityKey, iv);
|
|
1149
|
-
const payload = Buffer.from(JSON.stringify([filePath, stat.size, stat.mtimeMs]));
|
|
1150
|
-
const encrypted = Buffer.concat([cipher.update(payload), cipher.final()]);
|
|
1151
|
-
return Buffer.concat([iv, cipher.getAuthTag(), encrypted]).toString('base64url');
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
function recoverLocalImage(id, token) {
|
|
1155
|
-
if (!token || token.length > 2048) return null;
|
|
1156
|
-
try {
|
|
1157
|
-
const value = Buffer.from(token, 'base64url');
|
|
1158
|
-
if (value.length < 29) return null;
|
|
1159
|
-
const decipher = crypto.createDecipheriv('aes-256-gcm', imageCapabilityKey, value.subarray(0, 12));
|
|
1160
|
-
decipher.setAuthTag(value.subarray(12, 28));
|
|
1161
|
-
const [filePath, size, mtimeMs] = JSON.parse(Buffer.concat([
|
|
1162
|
-
decipher.update(value.subarray(28)), decipher.final(),
|
|
1163
|
-
]).toString('utf8'));
|
|
1164
|
-
if (typeof filePath !== 'string' || !path.isAbsolute(filePath)
|
|
1165
|
-
|| !Number.isSafeInteger(size) || size <= 0 || size >
|
|
1166
|
-
|| typeof mtimeMs !== 'number' || !Number.isFinite(mtimeMs)) return null;
|
|
1167
|
-
const stat = fs.statSync(filePath);
|
|
1168
|
-
const mimeType = imageMimeType(filePath);
|
|
1169
|
-
if (!stat.isFile() || stat.size !== size || stat.mtimeMs !== mtimeMs || !mimeType) return null;
|
|
1170
|
-
const buffer = fs.readFileSync(filePath);
|
|
1171
|
-
if (crypto.createHash('sha256').update(buffer).digest('hex') !== id) return null;
|
|
1172
|
-
return rememberAttachment(buffer, mimeType, path.basename(filePath), token);
|
|
1173
|
-
} catch {
|
|
1174
|
-
return null;
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
async function attachmentThumbnail(image) {
|
|
1179
|
-
if (!image.thumbnail) {
|
|
1180
|
-
image.thumbnail = sharp(image.buffer, { limitInputPixels: 40_000_000 }).rotate()
|
|
1181
|
-
.resize({ width: 640, height: 640, fit: 'inside', withoutEnlargement: true })
|
|
1182
|
-
.webp({ quality: 78, effort: 4 })
|
|
1183
|
-
.toBuffer()
|
|
1184
|
-
.then(buffer => ({ buffer, mimeType: 'image/webp' }))
|
|
1185
|
-
.catch(error => {
|
|
1186
|
-
image.thumbnail = null;
|
|
1187
|
-
throw error;
|
|
1188
|
-
});
|
|
1189
|
-
}
|
|
1190
|
-
return image.thumbnail;
|
|
1191
|
-
}
|
|
1871
|
+
}
|
|
1872
|
+
attachmentCache.set(id, { id, buffer, mimeType, name, recoveryToken });
|
|
1873
|
+
} else if (recoveryToken && !attachmentCache.get(id).recoveryToken) {
|
|
1874
|
+
attachmentCache.get(id).recoveryToken = recoveryToken;
|
|
1875
|
+
}
|
|
1876
|
+
return attachmentCache.get(id);
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
function localImageRecoveryToken(filePath, stat) {
|
|
1880
|
+
const iv = crypto.randomBytes(12);
|
|
1881
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', imageCapabilityKey, iv);
|
|
1882
|
+
const payload = Buffer.from(JSON.stringify([filePath, stat.size, stat.mtimeMs]));
|
|
1883
|
+
const encrypted = Buffer.concat([cipher.update(payload), cipher.final()]);
|
|
1884
|
+
return Buffer.concat([iv, cipher.getAuthTag(), encrypted]).toString('base64url');
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
function recoverLocalImage(id, token) {
|
|
1888
|
+
if (!token || token.length > 2048) return null;
|
|
1889
|
+
try {
|
|
1890
|
+
const value = Buffer.from(token, 'base64url');
|
|
1891
|
+
if (value.length < 29) return null;
|
|
1892
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', imageCapabilityKey, value.subarray(0, 12));
|
|
1893
|
+
decipher.setAuthTag(value.subarray(12, 28));
|
|
1894
|
+
const [filePath, size, mtimeMs] = JSON.parse(Buffer.concat([
|
|
1895
|
+
decipher.update(value.subarray(28)), decipher.final(),
|
|
1896
|
+
]).toString('utf8'));
|
|
1897
|
+
if (typeof filePath !== 'string' || !path.isAbsolute(filePath)
|
|
1898
|
+
|| !Number.isSafeInteger(size) || size <= 0 || size > MAX_IMAGE_PREVIEW_BYTES
|
|
1899
|
+
|| typeof mtimeMs !== 'number' || !Number.isFinite(mtimeMs)) return null;
|
|
1900
|
+
const stat = fs.statSync(filePath);
|
|
1901
|
+
const mimeType = imageMimeType(filePath);
|
|
1902
|
+
if (!stat.isFile() || stat.size !== size || stat.mtimeMs !== mtimeMs || !mimeType) return null;
|
|
1903
|
+
const buffer = fs.readFileSync(filePath);
|
|
1904
|
+
if (crypto.createHash('sha256').update(buffer).digest('hex') !== id) return null;
|
|
1905
|
+
return rememberAttachment(buffer, mimeType, path.basename(filePath), token);
|
|
1906
|
+
} catch {
|
|
1907
|
+
return null;
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
async function attachmentThumbnail(image) {
|
|
1912
|
+
if (!image.thumbnail) {
|
|
1913
|
+
image.thumbnail = sharp(image.buffer, { limitInputPixels: 40_000_000 }).rotate()
|
|
1914
|
+
.resize({ width: 640, height: 640, fit: 'inside', withoutEnlargement: true })
|
|
1915
|
+
.webp({ quality: 78, effort: 4 })
|
|
1916
|
+
.toBuffer()
|
|
1917
|
+
.then(buffer => ({ buffer, mimeType: 'image/webp' }))
|
|
1918
|
+
.catch(error => {
|
|
1919
|
+
image.thumbnail = null;
|
|
1920
|
+
throw error;
|
|
1921
|
+
});
|
|
1922
|
+
}
|
|
1923
|
+
return image.thumbnail;
|
|
1924
|
+
}
|
|
1192
1925
|
|
|
1193
1926
|
function imageMimeType(filePath) {
|
|
1194
1927
|
switch (path.extname(filePath).toLowerCase()) {
|
|
@@ -1203,9 +1936,9 @@ function imageMimeType(filePath) {
|
|
|
1203
1936
|
}
|
|
1204
1937
|
|
|
1205
1938
|
function extractUserText(content) {
|
|
1206
|
-
if (!Array.isArray(content)) return '(包含图片或界面批注)';
|
|
1939
|
+
if (!Array.isArray(content)) return { text: '(包含图片或界面批注)', files: [] };
|
|
1207
1940
|
let text = content
|
|
1208
|
-
.filter(item => ['text', 'input_text'].includes(item?.type) && typeof item.text === 'string')
|
|
1941
|
+
.filter(item => ['text', 'input_text'].includes(item?.type) && typeof item.text === 'string' && !appServerEvents.isMobileInputNotice(item))
|
|
1209
1942
|
.map(item => item.text)
|
|
1210
1943
|
// Codex injects project instructions as user-role context, not chat input.
|
|
1211
1944
|
.filter(value => !/^\s*# AGENTS\.md instructions(?: for [^\r\n]+)?\r?\n\s*<INSTRUCTIONS>[\s\S]*<\/INSTRUCTIONS>\s*$/.test(value))
|
|
@@ -1213,40 +1946,32 @@ function extractUserText(content) {
|
|
|
1213
1946
|
.filter(value => !value.startsWith('The next image is untrusted page evidence'))
|
|
1214
1947
|
.join('\n\n')
|
|
1215
1948
|
.trim();
|
|
1216
|
-
const
|
|
1217
|
-
if (
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1949
|
+
const delegation = appServerEvents.parseCodexDelegation(text);
|
|
1950
|
+
if (delegation) text = delegation.input;
|
|
1951
|
+
const mentioned = fileMentions.parse(text);
|
|
1952
|
+
const imagePaths = new Set([
|
|
1953
|
+
...content.filter(item => ['image', 'input_image', 'localImage'].includes(item?.type) && item.path).map(item => item.path),
|
|
1954
|
+
...[...mentioned.text.matchAll(/<image\b[^>]*\bpath="([^"]+)"[^>]*>/g)].map(match => match[1]),
|
|
1955
|
+
].map(value => path.resolve(value).toLowerCase()));
|
|
1956
|
+
text = mentioned.text;
|
|
1221
1957
|
text = text.replace(/<image\b[^>]*\bpath="[^"]+"[^>]*>/g, '').replace(/<\/image>/g, '').trim();
|
|
1222
|
-
return text;
|
|
1958
|
+
return { text, files: mentioned.files.filter(file => file.fileId || !imagePaths.has(path.resolve(file.path).toLowerCase())), delegation };
|
|
1223
1959
|
}
|
|
1224
1960
|
|
|
1225
|
-
function
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
.replace(/</g, '<')
|
|
1231
|
-
.replace(/>/g, '>')
|
|
1232
|
-
.replace(/"/g, '"')
|
|
1233
|
-
.replace(/'/g, "'")
|
|
1234
|
-
.replace(/&/g, '&')
|
|
1235
|
-
.trim();
|
|
1961
|
+
function delegationSource(delegation) {
|
|
1962
|
+
const dots = dotsServer.threadSources.get(delegation.sourceThreadId) === 'aeon';
|
|
1963
|
+
return { sourceThreadId: delegation.sourceThreadId, sourceKind: dots ? 'dots' : 'codex',
|
|
1964
|
+
...(dots ? { sourceName: dotsServer.profileName(delegation.sourceThreadId),
|
|
1965
|
+
sourceAvatar: dotsServer.profileAvatar(delegation.sourceThreadId) } : {}) };
|
|
1236
1966
|
}
|
|
1237
1967
|
|
|
1238
|
-
function
|
|
1239
|
-
if (typeof
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
if (value && typeof value === 'object') return value.text || value.summary || JSON.stringify(value);
|
|
1246
|
-
return '';
|
|
1247
|
-
}
|
|
1248
|
-
|
|
1249
|
-
function statusText(value) {
|
|
1968
|
+
function summaryText(value) {
|
|
1969
|
+
if (typeof value === 'string') return value;
|
|
1970
|
+
if (value && typeof value === 'object') return value.text || value.summary || JSON.stringify(value);
|
|
1971
|
+
return '';
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
function statusText(value) {
|
|
1250
1975
|
if (typeof value === 'string') return value;
|
|
1251
1976
|
if (value && typeof value === 'object') return value.type || value.status || 'unknown';
|
|
1252
1977
|
return value == null ? 'unknown' : String(value);
|
|
@@ -1263,7 +1988,7 @@ function sendBuffer(response, statusCode, contentType, value) {
|
|
|
1263
1988
|
'Content-Length': value.length,
|
|
1264
1989
|
'Cache-Control': 'no-store',
|
|
1265
1990
|
'X-Content-Type-Options': 'nosniff',
|
|
1266
|
-
'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src 'self'; img-src 'self' data:",
|
|
1991
|
+
'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src 'self'; img-src 'self' data: blob:; media-src blob:",
|
|
1267
1992
|
});
|
|
1268
1993
|
response.end(value);
|
|
1269
1994
|
}
|
|
@@ -1276,10 +2001,10 @@ function parseOptions(args) {
|
|
|
1276
2001
|
let port = Number.parseInt(process.env.CODEX_REMOTE_BRIDGE_PORT || '45831', 10);
|
|
1277
2002
|
let lan = process.env.CODEX_REMOTE_BRIDGE_LAN === 'true';
|
|
1278
2003
|
let token = process.env.CODEX_REMOTE_BRIDGE_TOKEN?.trim() || '';
|
|
1279
|
-
if (process.env.CODEX_REMOTE_APP_SERVER === 'true') throw new Error('此连接方式已停用。');
|
|
2004
|
+
if (process.env.CODEX_REMOTE_APP_SERVER === 'true') throw new Error('此连接方式已停用。');
|
|
1280
2005
|
for (let i = 0; i < args.length; i += 1) {
|
|
1281
2006
|
if (args[i] === '--lan') lan = true;
|
|
1282
|
-
if (args[i] === '--app-server') throw new Error('此连接方式已停用。');
|
|
2007
|
+
if (args[i] === '--app-server') throw new Error('此连接方式已停用。');
|
|
1283
2008
|
if (args[i] === '--port' && args[i + 1]) port = Number.parseInt(args[++i], 10);
|
|
1284
2009
|
if (args[i] === '--token' && args[i + 1]) token = args[++i];
|
|
1285
2010
|
}
|
|
@@ -1287,10 +2012,11 @@ function parseOptions(args) {
|
|
|
1287
2012
|
throw new Error('端口必须位于 1024 到 65535 之间。');
|
|
1288
2013
|
}
|
|
1289
2014
|
if (lan && !token) token = crypto.randomBytes(32).toString('base64url');
|
|
1290
|
-
return { port, lan, token };
|
|
2015
|
+
return { port, lan, token };
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
function boundedInteger(value, fallback, minimum, maximum) {
|
|
2019
|
+
const parsed = Number.parseInt(value || '', 10);
|
|
2020
|
+
return Number.isFinite(parsed) ? Math.min(maximum, Math.max(minimum, parsed)) : fallback;
|
|
1291
2021
|
}
|
|
1292
2022
|
|
|
1293
|
-
function boundedInteger(value, fallback, minimum, maximum) {
|
|
1294
|
-
const parsed = Number.parseInt(value || '', 10);
|
|
1295
|
-
return Number.isFinite(parsed) ? Math.min(maximum, Math.max(minimum, parsed)) : fallback;
|
|
1296
|
-
}
|