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