dsh-cliworker-now 0.0.0-stage → 0.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.en.md +364 -0
- package/README.md +368 -2
- package/cordis.patch.yml +3 -0
- package/lib/account-models.mjs +262 -0
- package/lib/client.js +22139 -0
- package/lib/client.js.map +1 -0
- package/lib/grok-catalog.mjs +145 -0
- package/lib/hermes-native-entry.py +96 -0
- package/lib/hermes-nous-refresh.py +331 -0
- package/lib/index.js +14544 -0
- package/lib/index.js.map +1 -0
- package/lib/native-launch.mjs +234 -0
- package/lib/native-spawn.mjs +32 -0
- package/lib/own-account-lease.mjs +105 -0
- package/lib/pi-installation.mjs +87 -0
- package/lib/pi-login.mjs +80 -0
- package/lib/pi-native-catalog.mjs +417 -0
- package/lib/pi-omp-bridge.mjs +602 -0
- package/lib/pi-omp-environment.mjs +15 -0
- package/lib/pi-omp-refresh.mjs +324 -0
- package/lib/private-launch.mjs +23 -0
- package/lib/terminal-bridge.mjs +18 -0
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +1430 -0
- package/lib/typert.remote-client.d.ts +72 -0
- package/lib/typert.remote-client.d.ts.map +1 -0
- package/lib/typert.remote-client.js +1256 -0
- package/lib/types/client/account-query-queue.d.ts +16 -0
- package/lib/types/client/account-query-queue.js +61 -0
- package/lib/types/client/account-query-queue.js.map +1 -0
- package/lib/types/client/account-terminal-css.d.ts +1 -0
- package/lib/types/client/account-terminal-css.js +3 -0
- package/lib/types/client/account-terminal-css.js.map +1 -0
- package/lib/types/client/account-terminal.d.ts +17 -0
- package/lib/types/client/account-terminal.js +525 -0
- package/lib/types/client/account-terminal.js.map +1 -0
- package/lib/types/client/assets/antigravity.png +0 -0
- package/lib/types/client/assets/claude-code.png +0 -0
- package/lib/types/client/assets/cliworker-now-logo.png +0 -0
- package/lib/types/client/assets/codex.png +0 -0
- package/lib/types/client/assets/grok.png +0 -0
- package/lib/types/client/assets/hermes.png +0 -0
- package/lib/types/client/assets/kimi-dark.png +0 -0
- package/lib/types/client/assets/kimi-light.png +0 -0
- package/lib/types/client/assets/mimo-code.png +0 -0
- package/lib/types/client/assets/omp.png +0 -0
- package/lib/types/client/assets/opencode.png +0 -0
- package/lib/types/client/assets/pi.png +0 -0
- package/lib/types/client/assets/zcode.png +0 -0
- package/lib/types/client/composer-keymap.d.ts +18 -0
- package/lib/types/client/composer-keymap.js +49 -0
- package/lib/types/client/composer-keymap.js.map +1 -0
- package/lib/types/client/conversation-timeline.d.ts +6 -0
- package/lib/types/client/conversation-timeline.js +65 -0
- package/lib/types/client/conversation-timeline.js.map +1 -0
- package/lib/types/client/copy-text.d.ts +5 -0
- package/lib/types/client/copy-text.js +45 -0
- package/lib/types/client/copy-text.js.map +1 -0
- package/lib/types/client/icons.d.ts +21 -0
- package/lib/types/client/icons.js +68 -0
- package/lib/types/client/icons.js.map +1 -0
- package/lib/types/client/index.d.ts +3 -0
- package/lib/types/client/index.js +124 -0
- package/lib/types/client/index.js.map +1 -0
- package/lib/types/client/management-styles.d.ts +1 -0
- package/lib/types/client/management-styles.js +17 -0
- package/lib/types/client/management-styles.js.map +1 -0
- package/lib/types/client/name-copy.d.ts +5 -0
- package/lib/types/client/name-copy.js +107 -0
- package/lib/types/client/name-copy.js.map +1 -0
- package/lib/types/client/native-job-overlay.d.ts +11 -0
- package/lib/types/client/native-job-overlay.js +129 -0
- package/lib/types/client/native-job-overlay.js.map +1 -0
- package/lib/types/client/operation-error.d.ts +2 -0
- package/lib/types/client/operation-error.js +12 -0
- package/lib/types/client/operation-error.js.map +1 -0
- package/lib/types/client/panel.d.ts +8 -0
- package/lib/types/client/panel.js +310 -0
- package/lib/types/client/panel.js.map +1 -0
- package/lib/types/client/rename-copy-styles.d.ts +1 -0
- package/lib/types/client/rename-copy-styles.js +21 -0
- package/lib/types/client/rename-copy-styles.js.map +1 -0
- package/lib/types/client/rename-worker.d.ts +9 -0
- package/lib/types/client/rename-worker.js +53 -0
- package/lib/types/client/rename-worker.js.map +1 -0
- package/lib/types/client/role-presets.d.ts +6 -0
- package/lib/types/client/role-presets.js +111 -0
- package/lib/types/client/role-presets.js.map +1 -0
- package/lib/types/client/settings-dialog.d.ts +13 -0
- package/lib/types/client/settings-dialog.js +442 -0
- package/lib/types/client/settings-dialog.js.map +1 -0
- package/lib/types/client/stat-popover.d.ts +10 -0
- package/lib/types/client/stat-popover.js +32 -0
- package/lib/types/client/stat-popover.js.map +1 -0
- package/lib/types/client/styles.d.ts +9 -0
- package/lib/types/client/styles.js +221 -0
- package/lib/types/client/styles.js.map +1 -0
- package/lib/types/client/telemetry.d.ts +11 -0
- package/lib/types/client/telemetry.js +24 -0
- package/lib/types/client/telemetry.js.map +1 -0
- package/lib/types/client/worker-card.d.ts +10 -0
- package/lib/types/client/worker-card.js +75 -0
- package/lib/types/client/worker-card.js.map +1 -0
- package/lib/types/client/worker-model-menu.d.ts +9 -0
- package/lib/types/client/worker-model-menu.js +136 -0
- package/lib/types/client/worker-model-menu.js.map +1 -0
- package/lib/types/client/workers.d.ts +21 -0
- package/lib/types/client/workers.js +59 -0
- package/lib/types/client/workers.js.map +1 -0
- package/lib/types/client/zcode-login-link-provider.d.ts +9 -0
- package/lib/types/client/zcode-login-link-provider.js +105 -0
- package/lib/types/client/zcode-login-link-provider.js.map +1 -0
- package/lib/types/client/zcode-login-link.d.ts +16 -0
- package/lib/types/client/zcode-login-link.js +194 -0
- package/lib/types/client/zcode-login-link.js.map +1 -0
- package/lib/types/host/account-identity.d.ts +16 -0
- package/lib/types/host/account-identity.js +126 -0
- package/lib/types/host/account-identity.js.map +1 -0
- package/lib/types/host/account-login.d.ts +10 -0
- package/lib/types/host/account-login.js +126 -0
- package/lib/types/host/account-login.js.map +1 -0
- package/lib/types/host/account-models.d.mts +21 -0
- package/lib/types/host/account-models.mjs +262 -0
- package/lib/types/host/accounts.d.ts +42 -0
- package/lib/types/host/accounts.js +945 -0
- package/lib/types/host/accounts.js.map +1 -0
- package/lib/types/host/adapters.d.ts +24 -0
- package/lib/types/host/adapters.js +380 -0
- package/lib/types/host/adapters.js.map +1 -0
- package/lib/types/host/agy-context.d.ts +8 -0
- package/lib/types/host/agy-context.js +122 -0
- package/lib/types/host/agy-context.js.map +1 -0
- package/lib/types/host/agy-models.d.ts +5 -0
- package/lib/types/host/agy-models.js +37 -0
- package/lib/types/host/agy-models.js.map +1 -0
- package/lib/types/host/agy-windows-account.d.ts +14 -0
- package/lib/types/host/agy-windows-account.js +104 -0
- package/lib/types/host/agy-windows-account.js.map +1 -0
- package/lib/types/host/authorized-catalog.d.ts +14 -0
- package/lib/types/host/authorized-catalog.js +35 -0
- package/lib/types/host/authorized-catalog.js.map +1 -0
- package/lib/types/host/bridge-protocol.d.ts +15 -0
- package/lib/types/host/bridge-protocol.js +80 -0
- package/lib/types/host/bridge-protocol.js.map +1 -0
- package/lib/types/host/claude-native-oauth.d.ts +17 -0
- package/lib/types/host/claude-native-oauth.js +272 -0
- package/lib/types/host/claude-native-oauth.js.map +1 -0
- package/lib/types/host/claude-own-route.d.ts +33 -0
- package/lib/types/host/claude-own-route.js +250 -0
- package/lib/types/host/claude-own-route.js.map +1 -0
- package/lib/types/host/cli-account-binding.d.ts +33 -0
- package/lib/types/host/cli-account-binding.js +540 -0
- package/lib/types/host/cli-account-binding.js.map +1 -0
- package/lib/types/host/cli-protocol.d.ts +27 -0
- package/lib/types/host/cli-protocol.js +212 -0
- package/lib/types/host/cli-protocol.js.map +1 -0
- package/lib/types/host/codex-account.d.ts +8 -0
- package/lib/types/host/codex-account.js +143 -0
- package/lib/types/host/codex-account.js.map +1 -0
- package/lib/types/host/codex-models.d.ts +6 -0
- package/lib/types/host/codex-models.js +169 -0
- package/lib/types/host/codex-models.js.map +1 -0
- package/lib/types/host/extended-adapters.d.ts +44 -0
- package/lib/types/host/extended-adapters.js +207 -0
- package/lib/types/host/extended-adapters.js.map +1 -0
- package/lib/types/host/first-party-account-context.d.ts +26 -0
- package/lib/types/host/first-party-account-context.js +55 -0
- package/lib/types/host/first-party-account-context.js.map +1 -0
- package/lib/types/host/first-party-models.d.ts +29 -0
- package/lib/types/host/first-party-models.js +459 -0
- package/lib/types/host/first-party-models.js.map +1 -0
- package/lib/types/host/grok-adapter.d.ts +44 -0
- package/lib/types/host/grok-adapter.js +293 -0
- package/lib/types/host/grok-adapter.js.map +1 -0
- package/lib/types/host/grok-models.d.ts +47 -0
- package/lib/types/host/grok-models.js +320 -0
- package/lib/types/host/grok-models.js.map +1 -0
- package/lib/types/host/harness-opencode-accounts.d.ts +17 -0
- package/lib/types/host/harness-opencode-accounts.js +80 -0
- package/lib/types/host/harness-opencode-accounts.js.map +1 -0
- package/lib/types/host/hermes-account-context.d.ts +8 -0
- package/lib/types/host/hermes-account-context.js +99 -0
- package/lib/types/host/hermes-account-context.js.map +1 -0
- package/lib/types/host/hermes-accounts.d.ts +19 -0
- package/lib/types/host/hermes-accounts.js +356 -0
- package/lib/types/host/hermes-accounts.js.map +1 -0
- package/lib/types/host/hermes-adapter.d.ts +93 -0
- package/lib/types/host/hermes-adapter.js +455 -0
- package/lib/types/host/hermes-adapter.js.map +1 -0
- package/lib/types/host/hermes-env.d.ts +4 -0
- package/lib/types/host/hermes-env.js +22 -0
- package/lib/types/host/hermes-env.js.map +1 -0
- package/lib/types/host/hermes-installation.d.ts +10 -0
- package/lib/types/host/hermes-installation.js +181 -0
- package/lib/types/host/hermes-installation.js.map +1 -0
- package/lib/types/host/hermes-models.d.ts +26 -0
- package/lib/types/host/hermes-models.js +328 -0
- package/lib/types/host/hermes-models.js.map +1 -0
- package/lib/types/host/hermes-native-entry.py +96 -0
- package/lib/types/host/hermes-nous-refresh.py +331 -0
- package/lib/types/host/hermes-nous.d.ts +53 -0
- package/lib/types/host/hermes-nous.js +343 -0
- package/lib/types/host/hermes-nous.js.map +1 -0
- package/lib/types/host/hermes-renewal.d.ts +27 -0
- package/lib/types/host/hermes-renewal.js +292 -0
- package/lib/types/host/hermes-renewal.js.map +1 -0
- package/lib/types/host/hermes-suppression.d.ts +7 -0
- package/lib/types/host/hermes-suppression.js +69 -0
- package/lib/types/host/hermes-suppression.js.map +1 -0
- package/lib/types/host/hermes-windows-installation.d.ts +5 -0
- package/lib/types/host/hermes-windows-installation.js +91 -0
- package/lib/types/host/hermes-windows-installation.js.map +1 -0
- package/lib/types/host/index.d.ts +140 -0
- package/lib/types/host/index.js +947 -0
- package/lib/types/host/index.js.map +1 -0
- package/lib/types/host/kimi-accounts.d.ts +17 -0
- package/lib/types/host/kimi-accounts.js +220 -0
- package/lib/types/host/kimi-accounts.js.map +1 -0
- package/lib/types/host/kimi-oauth-models.d.ts +14 -0
- package/lib/types/host/kimi-oauth-models.js +246 -0
- package/lib/types/host/kimi-oauth-models.js.map +1 -0
- package/lib/types/host/managed-agent.d.ts +4 -0
- package/lib/types/host/managed-agent.js +127 -0
- package/lib/types/host/managed-agent.js.map +1 -0
- package/lib/types/host/mimo-configuration.d.ts +22 -0
- package/lib/types/host/mimo-configuration.js +258 -0
- package/lib/types/host/mimo-configuration.js.map +1 -0
- package/lib/types/host/mimo-metadata-cache.d.ts +22 -0
- package/lib/types/host/mimo-metadata-cache.js +121 -0
- package/lib/types/host/mimo-metadata-cache.js.map +1 -0
- package/lib/types/host/native-capability-cache.d.ts +11 -0
- package/lib/types/host/native-capability-cache.js +94 -0
- package/lib/types/host/native-capability-cache.js.map +1 -0
- package/lib/types/host/native-launch.d.mts +2 -0
- package/lib/types/host/native-launch.mjs +234 -0
- package/lib/types/host/native-process.d.ts +5 -0
- package/lib/types/host/native-process.js +41 -0
- package/lib/types/host/native-process.js.map +1 -0
- package/lib/types/host/native-spawn.mjs +32 -0
- package/lib/types/host/opencode-adapter.d.ts +94 -0
- package/lib/types/host/opencode-adapter.js +507 -0
- package/lib/types/host/opencode-adapter.js.map +1 -0
- package/lib/types/host/opencode-native.d.ts +27 -0
- package/lib/types/host/opencode-native.js +403 -0
- package/lib/types/host/opencode-native.js.map +1 -0
- package/lib/types/host/own-account-lease.d.mts +5 -0
- package/lib/types/host/own-account-lease.mjs +105 -0
- package/lib/types/host/pi-installation.d.mts +7 -0
- package/lib/types/host/pi-installation.mjs +87 -0
- package/lib/types/host/pi-omp-accounts.d.ts +28 -0
- package/lib/types/host/pi-omp-accounts.js +175 -0
- package/lib/types/host/pi-omp-accounts.js.map +1 -0
- package/lib/types/host/pi-omp-adapter.d.ts +32 -0
- package/lib/types/host/pi-omp-adapter.js +190 -0
- package/lib/types/host/pi-omp-adapter.js.map +1 -0
- package/lib/types/host/pi-omp-environment.d.mts +1 -0
- package/lib/types/host/pi-omp-environment.mjs +15 -0
- package/lib/types/host/pi-omp-identity.d.ts +2 -0
- package/lib/types/host/pi-omp-identity.js +142 -0
- package/lib/types/host/pi-omp-identity.js.map +1 -0
- package/lib/types/host/pi-omp-logout.d.ts +3 -0
- package/lib/types/host/pi-omp-logout.js +152 -0
- package/lib/types/host/pi-omp-logout.js.map +1 -0
- package/lib/types/host/pi-omp-native.d.ts +71 -0
- package/lib/types/host/pi-omp-native.js +753 -0
- package/lib/types/host/pi-omp-native.js.map +1 -0
- package/lib/types/host/pi-omp-refresh.d.mts +7 -0
- package/lib/types/host/pi-omp-refresh.mjs +324 -0
- package/lib/types/host/private-launch.mjs +23 -0
- package/lib/types/host/process.d.ts +43 -0
- package/lib/types/host/process.js +134 -0
- package/lib/types/host/process.js.map +1 -0
- package/lib/types/host/protocol.d.ts +23 -0
- package/lib/types/host/protocol.js +112 -0
- package/lib/types/host/protocol.js.map +1 -0
- package/lib/types/host/roles.d.ts +34 -0
- package/lib/types/host/roles.js +80 -0
- package/lib/types/host/roles.js.map +1 -0
- package/lib/types/host/routing.d.ts +31 -0
- package/lib/types/host/routing.js +248 -0
- package/lib/types/host/routing.js.map +1 -0
- package/lib/types/host/runtime.d.ts +60 -0
- package/lib/types/host/runtime.js +598 -0
- package/lib/types/host/runtime.js.map +1 -0
- package/lib/types/host/storage.d.ts +48 -0
- package/lib/types/host/storage.js +449 -0
- package/lib/types/host/storage.js.map +1 -0
- package/lib/types/host/telemetry.d.ts +17 -0
- package/lib/types/host/telemetry.js +158 -0
- package/lib/types/host/telemetry.js.map +1 -0
- package/lib/types/host/windows-account-bootstrap.ps1 +44 -0
- package/lib/types/host/windows-account-launch.mjs +47 -0
- package/lib/types/host/windows-account-owner.ps1 +137 -0
- package/lib/types/host/windows-account-stdio.mjs +19 -0
- package/lib/types/host/windows-account-terminal.d.ts +17 -0
- package/lib/types/host/windows-account-terminal.js +208 -0
- package/lib/types/host/windows-account-terminal.js.map +1 -0
- package/lib/types/host/windows-agy-account.ps1 +81 -0
- package/lib/types/host/windows-cli-installation.d.ts +31 -0
- package/lib/types/host/windows-cli-installation.js +157 -0
- package/lib/types/host/windows-cli-installation.js.map +1 -0
- package/lib/types/host/windows-hidden-launch.mjs +49 -0
- package/lib/types/host/windows-hidden-process.d.ts +12 -0
- package/lib/types/host/windows-hidden-process.js +116 -0
- package/lib/types/host/windows-hidden-process.js.map +1 -0
- package/lib/types/host/zcode-account-models.d.ts +16 -0
- package/lib/types/host/zcode-account-models.js +114 -0
- package/lib/types/host/zcode-account-models.js.map +1 -0
- package/lib/types/host/zcode-adapter.d.ts +93 -0
- package/lib/types/host/zcode-adapter.js +808 -0
- package/lib/types/host/zcode-adapter.js.map +1 -0
- package/lib/types/host/zcode-grok-accounts.d.ts +30 -0
- package/lib/types/host/zcode-grok-accounts.js +408 -0
- package/lib/types/host/zcode-grok-accounts.js.map +1 -0
- package/lib/types/shared/accounts.d.ts +47 -0
- package/lib/types/shared/accounts.js +2 -0
- package/lib/types/shared/accounts.js.map +1 -0
- package/lib/types/shared/builtin-presets.d.ts +2 -0
- package/lib/types/shared/builtin-presets.js +231 -0
- package/lib/types/shared/builtin-presets.js.map +1 -0
- package/lib/types/shared/cli-settings.d.ts +5 -0
- package/lib/types/shared/cli-settings.js +2 -0
- package/lib/types/shared/cli-settings.js.map +1 -0
- package/lib/types/shared/model-presentation.d.ts +10 -0
- package/lib/types/shared/model-presentation.js +95 -0
- package/lib/types/shared/model-presentation.js.map +1 -0
- package/lib/types/shared/models.d.ts +7 -0
- package/lib/types/shared/models.js +35 -0
- package/lib/types/shared/models.js.map +1 -0
- package/lib/types/shared/telemetry.d.ts +18 -0
- package/lib/types/shared/telemetry.js +16 -0
- package/lib/types/shared/telemetry.js.map +1 -0
- package/lib/types/shared/types.d.ts +127 -0
- package/lib/types/shared/types.js +147 -0
- package/lib/types/shared/types.js.map +1 -0
- package/lib/windows-account-bootstrap.ps1 +44 -0
- package/lib/windows-account-launch.mjs +47 -0
- package/lib/windows-account-owner.ps1 +137 -0
- package/lib/windows-account-stdio.mjs +19 -0
- package/lib/windows-agy-account.ps1 +81 -0
- package/lib/windows-hidden-launch.mjs +49 -0
- package/lib/zcode-resume.mjs +423 -0
- package/package.json +109 -4
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
// Subprocess protocol bridge. The Host owns the external sandbox, credentials,
|
|
2
|
+
// deadlines, directory/session locks, and descendant cleanup. No shell is used.
|
|
3
|
+
import { spawnNativeProcess } from './native-spawn.mjs'
|
|
4
|
+
import { createHash, randomBytes } from 'node:crypto'
|
|
5
|
+
import { mkdir, writeFile, rename, realpath, unlink, rmdir, lstat, open, rm } from 'node:fs/promises'
|
|
6
|
+
import { constants } from 'node:fs'
|
|
7
|
+
import { dirname, isAbsolute, join, resolve, sep } from 'node:path'
|
|
8
|
+
import { ompConfigurationEnvironment } from './pi-omp-environment.mjs'
|
|
9
|
+
import { readFile } from 'node:fs/promises'
|
|
10
|
+
import { nativeLaunchArgv } from './native-launch.mjs'
|
|
11
|
+
import { ownsOwnAccountLease, OWN_ACCOUNT_BUSY } from './own-account-lease.mjs'
|
|
12
|
+
import { assertPiOmpRefreshSources, assertPiOmpRefreshCache } from './pi-omp-refresh.mjs'
|
|
13
|
+
import {
|
|
14
|
+
queryNativeCandidates,
|
|
15
|
+
snapshotNativeCandidates,
|
|
16
|
+
accountSupportedChoices,
|
|
17
|
+
} from './pi-native-catalog.mjs'
|
|
18
|
+
|
|
19
|
+
process.umask(0o077)
|
|
20
|
+
const output = (value) => process.stdout.write(JSON.stringify(value) + '\n')
|
|
21
|
+
const event = (value) => output({ type: 'event', event: value })
|
|
22
|
+
const safeErrors = new Set([
|
|
23
|
+
'任务已停止',
|
|
24
|
+
'所选模型或强度不在原生可用目录中',
|
|
25
|
+
'无法读取 Pi/OMP 原生模型目录,请检查本机配置',
|
|
26
|
+
OWN_ACCOUNT_BUSY,
|
|
27
|
+
'账号配置已变化,请重新刷新模型',
|
|
28
|
+
])
|
|
29
|
+
const errorMessage = (error) =>
|
|
30
|
+
error instanceof Error && safeErrors.has(error.message)
|
|
31
|
+
? error.message
|
|
32
|
+
: 'Pi/OMP 原生配置、模型或协议校验失败'
|
|
33
|
+
const inside = (root, path) => path.startsWith(root + sep)
|
|
34
|
+
let selectedProvider, selectedModel
|
|
35
|
+
async function privateDirectory(path) {
|
|
36
|
+
await mkdir(path, { recursive: true, mode: 0o700 })
|
|
37
|
+
const info = await lstat(path)
|
|
38
|
+
if (info.isSymbolicLink() || !info.isDirectory()) throw new Error('Unsafe Pi/OMP state directory symlink')
|
|
39
|
+
if (process.platform === 'win32') {
|
|
40
|
+
// Windows cannot open directory descriptors with Node's POSIX O_DIRECTORY.
|
|
41
|
+
const canonical = await realpath(path),
|
|
42
|
+
current = await lstat(path)
|
|
43
|
+
if (current.isSymbolicLink() || current.dev !== info.dev || current.ino !== info.ino)
|
|
44
|
+
throw new Error('Pi/OMP state directory changed while opening')
|
|
45
|
+
return canonical
|
|
46
|
+
}
|
|
47
|
+
const handle = await open(path, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW)
|
|
48
|
+
try {
|
|
49
|
+
const opened = await handle.stat()
|
|
50
|
+
if (info.dev !== opened.dev || info.ino !== opened.ino)
|
|
51
|
+
throw new Error('Pi/OMP state directory changed while opening')
|
|
52
|
+
await handle.chmod(0o700)
|
|
53
|
+
} finally {
|
|
54
|
+
await handle.close()
|
|
55
|
+
}
|
|
56
|
+
return realpath(path)
|
|
57
|
+
}
|
|
58
|
+
async function privateFile(path, read = false, limit = 1024 * 1024) {
|
|
59
|
+
const handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK)
|
|
60
|
+
try {
|
|
61
|
+
const info = await handle.stat()
|
|
62
|
+
if (!info.isFile() || info.nlink !== 1) throw new Error('Unsafe Pi/OMP state file or hard link')
|
|
63
|
+
if (read) {
|
|
64
|
+
if (info.size > limit) throw new Error('Pi/OMP state JSON exceeds limit')
|
|
65
|
+
const contents = await handle.readFile('utf8')
|
|
66
|
+
const after = await handle.stat()
|
|
67
|
+
if (Buffer.byteLength(contents) > limit || info.size !== after.size || info.mtimeMs !== after.mtimeMs)
|
|
68
|
+
throw new Error('Pi/OMP state JSON changed while reading')
|
|
69
|
+
return contents
|
|
70
|
+
}
|
|
71
|
+
} finally {
|
|
72
|
+
await handle.close()
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function writePrivateFile(path, contents) {
|
|
76
|
+
await privateDirectory(dirname(path))
|
|
77
|
+
const temporary = join(dirname(path), `.cliworker-${randomBytes(16).toString('hex')}.tmp`)
|
|
78
|
+
try {
|
|
79
|
+
await writeFile(temporary, contents, { flag: 'wx', mode: 0o600 })
|
|
80
|
+
// Replaces a planted file link itself; never truncates its external inode.
|
|
81
|
+
await rename(temporary, path)
|
|
82
|
+
} finally {
|
|
83
|
+
await rm(temporary, { force: true })
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
let child,
|
|
87
|
+
stopped = false,
|
|
88
|
+
failure,
|
|
89
|
+
closeTimer,
|
|
90
|
+
forceTimer,
|
|
91
|
+
startupTimer,
|
|
92
|
+
config,
|
|
93
|
+
session,
|
|
94
|
+
finalized = false
|
|
95
|
+
const metadataController = new AbortController()
|
|
96
|
+
const stop = (reason) => {
|
|
97
|
+
failure ??= reason
|
|
98
|
+
child?.stdin?.end()
|
|
99
|
+
child?.kill('SIGTERM')
|
|
100
|
+
forceTimer ??= setTimeout(() => child?.kill('SIGKILL'), 5000)
|
|
101
|
+
}
|
|
102
|
+
process.on('SIGTERM', () => {
|
|
103
|
+
stopped = true
|
|
104
|
+
metadataController.abort()
|
|
105
|
+
stop('任务已停止')
|
|
106
|
+
})
|
|
107
|
+
process.on('SIGINT', () => {
|
|
108
|
+
stopped = true
|
|
109
|
+
metadataController.abort()
|
|
110
|
+
stop('任务已停止')
|
|
111
|
+
})
|
|
112
|
+
const persist = async () => {
|
|
113
|
+
if (!session || config.discover) return
|
|
114
|
+
const path = await realpath(session.path)
|
|
115
|
+
const nativeRoot = await privateDirectory(config.nativeRoot ?? join(config.stateDirectory, 'agent'))
|
|
116
|
+
if (!inside(nativeRoot, path) || path !== resolve(session.path))
|
|
117
|
+
throw new Error('CLI session path escaped its private directory or used a symlink')
|
|
118
|
+
await privateFile(path)
|
|
119
|
+
const mapDirectory = join(config.stateDirectory, 'sessions')
|
|
120
|
+
await privateDirectory(mapDirectory)
|
|
121
|
+
const filename = join(mapDirectory, createHash('sha256').update(session.id).digest('hex') + '.json')
|
|
122
|
+
await writePrivateFile(
|
|
123
|
+
filename,
|
|
124
|
+
JSON.stringify({
|
|
125
|
+
cli: config.cli,
|
|
126
|
+
project: config.project,
|
|
127
|
+
id: session.id,
|
|
128
|
+
path,
|
|
129
|
+
model: config.preference.model,
|
|
130
|
+
}),
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
const requestPath = process.argv[2]
|
|
135
|
+
config = JSON.parse(await privateFile(requestPath, true))
|
|
136
|
+
await unlink(requestPath)
|
|
137
|
+
await rmdir(dirname(requestPath))
|
|
138
|
+
if (
|
|
139
|
+
!['pi', 'omp'].includes(config.cli) ||
|
|
140
|
+
typeof config.executable !== 'string' ||
|
|
141
|
+
!isAbsolute(config.stateDirectory)
|
|
142
|
+
)
|
|
143
|
+
throw new Error('Invalid bridge request')
|
|
144
|
+
config.stateDirectory = await privateDirectory(config.stateDirectory)
|
|
145
|
+
if (config.leaseDirectory) {
|
|
146
|
+
if (typeof config.nativeRoot !== 'string' || dirname(config.nativeRoot) !== config.leaseDirectory)
|
|
147
|
+
throw new Error('Invalid shared account directory')
|
|
148
|
+
if (!(await ownsOwnAccountLease(config.leaseDirectory, config.leaseNonce)))
|
|
149
|
+
throw new Error(OWN_ACCOUNT_BUSY)
|
|
150
|
+
await assertPiOmpRefreshSources(config.leaseDirectory, config.refreshReceipt)
|
|
151
|
+
await assertPiOmpRefreshCache(config.leaseDirectory, config.refreshReceipt)
|
|
152
|
+
}
|
|
153
|
+
const nativeRoot = await privateDirectory(config.nativeRoot ?? join(config.stateDirectory, 'agent'))
|
|
154
|
+
const temporaryDirectory = await privateDirectory(join(config.stateDirectory, 't'))
|
|
155
|
+
await privateDirectory(join(nativeRoot, 'sessions'))
|
|
156
|
+
if (!config.discover) await privateDirectory(join(config.stateDirectory, 'sessions'))
|
|
157
|
+
const env = Object.fromEntries(
|
|
158
|
+
[
|
|
159
|
+
'PATH',
|
|
160
|
+
'BUN_INSTALL',
|
|
161
|
+
'HOME',
|
|
162
|
+
'USERPROFILE',
|
|
163
|
+
'SystemRoot',
|
|
164
|
+
'SYSTEMROOT',
|
|
165
|
+
'WINDIR',
|
|
166
|
+
'COMSPEC',
|
|
167
|
+
'ComSpec',
|
|
168
|
+
'PATHEXT',
|
|
169
|
+
'APPDATA',
|
|
170
|
+
'LOCALAPPDATA',
|
|
171
|
+
'TEMP',
|
|
172
|
+
'TMP',
|
|
173
|
+
'LANG',
|
|
174
|
+
'LC_ALL',
|
|
175
|
+
'USER',
|
|
176
|
+
'LOGNAME',
|
|
177
|
+
'SHELL',
|
|
178
|
+
'TMPDIR',
|
|
179
|
+
'NODE_EXTRA_CA_CERTS',
|
|
180
|
+
'SSL_CERT_FILE',
|
|
181
|
+
'HTTPS_PROXY',
|
|
182
|
+
'HTTP_PROXY',
|
|
183
|
+
'ALL_PROXY',
|
|
184
|
+
'NO_PROXY',
|
|
185
|
+
].flatMap((key) => (process.env[key] ? [[key, process.env[key]]] : [])),
|
|
186
|
+
)
|
|
187
|
+
Object.assign(env, {
|
|
188
|
+
PI_CODING_AGENT_DIR: nativeRoot,
|
|
189
|
+
TMPDIR: temporaryDirectory,
|
|
190
|
+
...(process.platform === 'win32' ? { TMP: temporaryDirectory, TEMP: temporaryDirectory } : {}),
|
|
191
|
+
PI_OFFLINE: '1',
|
|
192
|
+
PI_TELEMETRY: '0',
|
|
193
|
+
ELECTRON_RUN_AS_NODE: '1',
|
|
194
|
+
NO_COLOR: '1',
|
|
195
|
+
})
|
|
196
|
+
const nativeEnvironment = JSON.parse(await privateFile(join(nativeRoot, 'native-env.json'), true))
|
|
197
|
+
if (
|
|
198
|
+
!nativeEnvironment ||
|
|
199
|
+
typeof nativeEnvironment !== 'object' ||
|
|
200
|
+
Array.isArray(nativeEnvironment) ||
|
|
201
|
+
Object.entries(nativeEnvironment).some(
|
|
202
|
+
([key, value]) =>
|
|
203
|
+
!/^[A-Z][A-Z0-9_]*$/.test(key) ||
|
|
204
|
+
/^(?:PATH|HOME|USERPROFILE|SYSTEMROOT|WINDIR|COMSPEC|PATHEXT|APPDATA|LOCALAPPDATA|SHELL|TMPDIR|TMP|TEMP|NODE_OPTIONS|NODE_PATH|ELECTRON_RUN_AS_NODE|OMP_AUTH_BROKER_.*|OMP_PROFILE|PI_PROFILE|PI_CONFIG_DIR|PI_CODING_AGENT_DIR|LD_.*|DYLD_.*)$/i.test(
|
|
205
|
+
key,
|
|
206
|
+
) ||
|
|
207
|
+
typeof value !== 'string',
|
|
208
|
+
)
|
|
209
|
+
)
|
|
210
|
+
throw new Error('Invalid native environment snapshot')
|
|
211
|
+
Object.assign(env, nativeEnvironment)
|
|
212
|
+
// Pi's SDK is loaded in this process and consults process.env itself. Apply
|
|
213
|
+
// the same own-source environment before importing it, not only at spawn.
|
|
214
|
+
process.env = env
|
|
215
|
+
const isolated =
|
|
216
|
+
config.cli === 'pi'
|
|
217
|
+
? [
|
|
218
|
+
'--no-extensions',
|
|
219
|
+
'--no-skills',
|
|
220
|
+
'--no-prompt-templates',
|
|
221
|
+
'--no-themes',
|
|
222
|
+
'--no-context-files',
|
|
223
|
+
'--no-approve',
|
|
224
|
+
]
|
|
225
|
+
: [
|
|
226
|
+
'--no-extensions',
|
|
227
|
+
'--no-skills',
|
|
228
|
+
'--no-rules',
|
|
229
|
+
'--no-title',
|
|
230
|
+
'--no-lsp',
|
|
231
|
+
'--no-pty',
|
|
232
|
+
'--approval-mode',
|
|
233
|
+
'write',
|
|
234
|
+
'--config',
|
|
235
|
+
join(nativeRoot, 'config.yml'),
|
|
236
|
+
]
|
|
237
|
+
if (config.cli === 'omp') {
|
|
238
|
+
Object.assign(env, ompConfigurationEnvironment(dirname(nativeRoot)))
|
|
239
|
+
Object.assign(process.env, ompConfigurationEnvironment(dirname(nativeRoot)))
|
|
240
|
+
const nativeAuth = JSON.parse(await readFile(join(nativeRoot, 'native-auth.json'), 'utf8'))
|
|
241
|
+
await writePrivateFile(
|
|
242
|
+
join(nativeRoot, 'config.yml'),
|
|
243
|
+
JSON.stringify({
|
|
244
|
+
...nativeAuth,
|
|
245
|
+
startup: { setupWizard: false, checkUpdate: false, showSplash: false },
|
|
246
|
+
disabledProviders: [
|
|
247
|
+
'native',
|
|
248
|
+
'claude',
|
|
249
|
+
'claude-plugins',
|
|
250
|
+
'codex',
|
|
251
|
+
'cursor',
|
|
252
|
+
'gemini',
|
|
253
|
+
'opencode',
|
|
254
|
+
'mcp-json',
|
|
255
|
+
'vscode',
|
|
256
|
+
'windsurf',
|
|
257
|
+
'omp-plugins',
|
|
258
|
+
],
|
|
259
|
+
mcp: { enableProjectConfig: false },
|
|
260
|
+
retry: { modelFallback: false },
|
|
261
|
+
memory: { backend: 'off' },
|
|
262
|
+
tools: { approvalMode: 'write' },
|
|
263
|
+
}),
|
|
264
|
+
)
|
|
265
|
+
}
|
|
266
|
+
let resume
|
|
267
|
+
let catalog
|
|
268
|
+
if (config.metadataPhase === 'native-candidates') {
|
|
269
|
+
const candidates = await queryNativeCandidates(
|
|
270
|
+
config.cli,
|
|
271
|
+
config.executable,
|
|
272
|
+
nativeRoot,
|
|
273
|
+
env,
|
|
274
|
+
(process) => {
|
|
275
|
+
child = process
|
|
276
|
+
if (stopped) stop('任务已停止')
|
|
277
|
+
},
|
|
278
|
+
{ signal: metadataController.signal },
|
|
279
|
+
)
|
|
280
|
+
if (stopped || failure) throw new Error('任务已停止')
|
|
281
|
+
const snapshot = snapshotNativeCandidates(candidates, env)
|
|
282
|
+
await writePrivateFile(join(nativeRoot, 'catalog-candidates.json'), JSON.stringify(snapshot))
|
|
283
|
+
output({ phase: 'native-candidates', count: snapshot.length })
|
|
284
|
+
finalized = true
|
|
285
|
+
} else if (config.metadataPhase === 'account-scope') {
|
|
286
|
+
const candidates = JSON.parse(
|
|
287
|
+
await privateFile(join(nativeRoot, 'catalog-candidates.json'), true, 16 * 1024 * 1024),
|
|
288
|
+
)
|
|
289
|
+
if (!Array.isArray(candidates)) throw new Error('Invalid native candidate snapshot')
|
|
290
|
+
catalog = await accountSupportedChoices(config.cli, nativeRoot, candidates, env, {
|
|
291
|
+
signal: metadataController.signal,
|
|
292
|
+
})
|
|
293
|
+
} else {
|
|
294
|
+
catalog = config.confirmedCatalog
|
|
295
|
+
if (!Array.isArray(catalog)) throw new Error('Missing confirmed account model catalog')
|
|
296
|
+
}
|
|
297
|
+
if (stopped || failure) throw new Error('任务已停止')
|
|
298
|
+
if (finalized) {
|
|
299
|
+
// Candidate phase has no account networking or model execution.
|
|
300
|
+
} else if (config.discover || config.metadataPhase === 'account-scope') {
|
|
301
|
+
output(catalog)
|
|
302
|
+
finalized = true
|
|
303
|
+
} else {
|
|
304
|
+
if (
|
|
305
|
+
typeof config.preference?.model !== 'string' ||
|
|
306
|
+
!/^[A-Za-z0-9_.:-]+\/[^\s\x00-\x1f]+$/.test(config.preference.model)
|
|
307
|
+
)
|
|
308
|
+
throw new Error('Invalid selected native provider/model')
|
|
309
|
+
const slash = config.preference.model.indexOf('/')
|
|
310
|
+
selectedProvider = config.preference.model.slice(0, slash)
|
|
311
|
+
selectedModel = config.preference.model.slice(slash + 1)
|
|
312
|
+
const selected = catalog.find((model) => model.id === config.preference.model)
|
|
313
|
+
if (
|
|
314
|
+
!selected ||
|
|
315
|
+
(config.preference.effort !== 'default' && !selected.efforts.includes(config.preference.effort))
|
|
316
|
+
)
|
|
317
|
+
throw new Error('所选模型或强度不在原生可用目录中')
|
|
318
|
+
if (
|
|
319
|
+
!['plan', 'accept-edits'].includes(config.mode) ||
|
|
320
|
+
typeof config.prompt !== 'string' ||
|
|
321
|
+
!config.prompt.trim()
|
|
322
|
+
)
|
|
323
|
+
throw new Error('Invalid task mode or prompt')
|
|
324
|
+
config.project = await realpath(config.project)
|
|
325
|
+
if (config.conversationId) {
|
|
326
|
+
await privateDirectory(join(config.stateDirectory, 'sessions'))
|
|
327
|
+
const mapping = join(
|
|
328
|
+
config.stateDirectory,
|
|
329
|
+
'sessions',
|
|
330
|
+
createHash('sha256').update(config.conversationId).digest('hex') + '.json',
|
|
331
|
+
)
|
|
332
|
+
const mapped = JSON.parse(await privateFile(mapping, true))
|
|
333
|
+
if (
|
|
334
|
+
mapped.cli !== config.cli ||
|
|
335
|
+
mapped.project !== config.project ||
|
|
336
|
+
mapped.id !== config.conversationId
|
|
337
|
+
)
|
|
338
|
+
throw new Error('Saved session identity does not match this request')
|
|
339
|
+
resume = await realpath(mapped.path)
|
|
340
|
+
if (!inside(nativeRoot, resume) || resume !== resolve(mapped.path))
|
|
341
|
+
throw new Error('Saved session path escaped its private directory or used a symlink')
|
|
342
|
+
await privateFile(resume)
|
|
343
|
+
}
|
|
344
|
+
const tools = config.cli === 'pi' ? ['read', 'grep', 'find', 'ls'] : ['read', 'grep', 'glob']
|
|
345
|
+
if (config.mode === 'accept-edits') tools.push('write', 'edit')
|
|
346
|
+
const nativeArgs = [
|
|
347
|
+
'--mode',
|
|
348
|
+
'rpc',
|
|
349
|
+
...isolated,
|
|
350
|
+
'--provider',
|
|
351
|
+
selectedProvider,
|
|
352
|
+
'--model',
|
|
353
|
+
selectedModel,
|
|
354
|
+
...(config.preference.effort === 'default'
|
|
355
|
+
? []
|
|
356
|
+
: ['--thinking', config.preference.effort === 'none' ? 'off' : config.preference.effort]),
|
|
357
|
+
'--tools',
|
|
358
|
+
tools.join(','),
|
|
359
|
+
...(resume ? [config.cli === 'pi' ? '--session' : '--resume', resume] : []),
|
|
360
|
+
]
|
|
361
|
+
const launch = nativeLaunchArgv([config.executable, ...nativeArgs])
|
|
362
|
+
if (stopped) throw new Error('任务已停止')
|
|
363
|
+
child = spawnNativeProcess(launch[0], launch.slice(1), {
|
|
364
|
+
cwd: config.project,
|
|
365
|
+
env,
|
|
366
|
+
detached: false,
|
|
367
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
368
|
+
})
|
|
369
|
+
let pending = '',
|
|
370
|
+
bytes = 0,
|
|
371
|
+
stderrBytes = 0,
|
|
372
|
+
settled = false,
|
|
373
|
+
finalState = false,
|
|
374
|
+
sent = false,
|
|
375
|
+
response = '',
|
|
376
|
+
currentText = '',
|
|
377
|
+
assistantStep = 0
|
|
378
|
+
const toolSteps = new Map()
|
|
379
|
+
const send = (value) => {
|
|
380
|
+
if (!child.stdin.destroyed) child.stdin.write(JSON.stringify(value) + '\n')
|
|
381
|
+
}
|
|
382
|
+
const validateState = (state, validateModel = true) => {
|
|
383
|
+
if (
|
|
384
|
+
!state ||
|
|
385
|
+
typeof state.sessionId !== 'string' ||
|
|
386
|
+
!state.sessionId ||
|
|
387
|
+
typeof state.sessionFile !== 'string' ||
|
|
388
|
+
!inside(nativeRoot, resolve(state.sessionFile))
|
|
389
|
+
)
|
|
390
|
+
throw new Error('CLI did not return a private session identity')
|
|
391
|
+
if (
|
|
392
|
+
validateModel &&
|
|
393
|
+
(state.model?.provider !== selectedProvider ||
|
|
394
|
+
state.model?.id !== selectedModel ||
|
|
395
|
+
(config.preference.effort !== 'default' &&
|
|
396
|
+
state.thinkingLevel !== (config.preference.effort === 'none' ? 'off' : config.preference.effort)))
|
|
397
|
+
)
|
|
398
|
+
throw new Error('CLI model or effort differs from the selected preference')
|
|
399
|
+
if (
|
|
400
|
+
(session && session.id !== state.sessionId) ||
|
|
401
|
+
(config.conversationId && config.conversationId !== state.sessionId)
|
|
402
|
+
)
|
|
403
|
+
throw new Error('CLI changed the conversation identity')
|
|
404
|
+
if (
|
|
405
|
+
(resume && resume !== resolve(state.sessionFile)) ||
|
|
406
|
+
(session && session.path !== resolve(state.sessionFile))
|
|
407
|
+
)
|
|
408
|
+
throw new Error('CLI changed the saved session file')
|
|
409
|
+
session = { id: state.sessionId, path: resolve(state.sessionFile) }
|
|
410
|
+
}
|
|
411
|
+
const consume = (value) => {
|
|
412
|
+
if (!value || typeof value !== 'object' || typeof value.type !== 'string')
|
|
413
|
+
throw new Error('Invalid native RPC record')
|
|
414
|
+
if (value.type === 'response') {
|
|
415
|
+
if (!value.success) throw new Error('Native CLI rejected an RPC request')
|
|
416
|
+
if (value.id === 'select-model') {
|
|
417
|
+
if (config.preference.effort !== 'default')
|
|
418
|
+
send({
|
|
419
|
+
id: 'select-effort',
|
|
420
|
+
type: 'set_thinking_level',
|
|
421
|
+
level: config.preference.effort === 'none' ? 'off' : config.preference.effort,
|
|
422
|
+
})
|
|
423
|
+
else send({ id: 'selected', type: 'get_state' })
|
|
424
|
+
} else if (value.id === 'select-effort') send({ id: 'selected', type: 'get_state' })
|
|
425
|
+
else if (value.id === 'initial' || value.id === 'selected') {
|
|
426
|
+
validateState(value.data, value.id === 'selected')
|
|
427
|
+
if (value.data.isStreaming) throw new Error('Native session is already streaming')
|
|
428
|
+
if (
|
|
429
|
+
value.id === 'initial' &&
|
|
430
|
+
(value.data.model?.provider !== selectedProvider || value.data.model?.id !== selectedModel)
|
|
431
|
+
) {
|
|
432
|
+
send({
|
|
433
|
+
id: 'select-model',
|
|
434
|
+
type: 'set_model',
|
|
435
|
+
provider: selectedProvider,
|
|
436
|
+
modelId: selectedModel,
|
|
437
|
+
})
|
|
438
|
+
return
|
|
439
|
+
}
|
|
440
|
+
if (
|
|
441
|
+
value.id === 'initial' &&
|
|
442
|
+
config.preference.effort !== 'default' &&
|
|
443
|
+
value.data.thinkingLevel !==
|
|
444
|
+
(config.preference.effort === 'none' ? 'off' : config.preference.effort)
|
|
445
|
+
) {
|
|
446
|
+
send({
|
|
447
|
+
id: 'select-effort',
|
|
448
|
+
type: 'set_thinking_level',
|
|
449
|
+
level: config.preference.effort === 'none' ? 'off' : config.preference.effort,
|
|
450
|
+
})
|
|
451
|
+
return
|
|
452
|
+
}
|
|
453
|
+
validateState(value.data)
|
|
454
|
+
clearTimeout(startupTimer)
|
|
455
|
+
output({
|
|
456
|
+
type: 'session',
|
|
457
|
+
id: session.id,
|
|
458
|
+
model: `${value.data.model.provider}/${value.data.model.id}`,
|
|
459
|
+
})
|
|
460
|
+
event({
|
|
461
|
+
kind: 'status',
|
|
462
|
+
text: `${config.cli === 'pi' ? 'Pi' : 'OMP'} 原生模型已确认`,
|
|
463
|
+
observedModel: `${value.data.model.provider}/${value.data.model.id}`,
|
|
464
|
+
})
|
|
465
|
+
if (!stopped && !failure) {
|
|
466
|
+
sent = true
|
|
467
|
+
send({ id: 'prompt', type: 'prompt', message: config.prompt })
|
|
468
|
+
}
|
|
469
|
+
} else if (value.id === 'final') {
|
|
470
|
+
validateState(value.data)
|
|
471
|
+
if (value.data.isStreaming || value.data.isCompacting || (value.data.pendingMessageCount ?? 0) > 0)
|
|
472
|
+
throw new Error('Native agent is still active after its terminal event')
|
|
473
|
+
finalState = true
|
|
474
|
+
child.stdin.end()
|
|
475
|
+
closeTimer ??= setTimeout(() => stop('CLI did not exit after completion'), 5000)
|
|
476
|
+
}
|
|
477
|
+
return
|
|
478
|
+
}
|
|
479
|
+
if (config.discover) return
|
|
480
|
+
if (value.type === 'extension_ui_request') {
|
|
481
|
+
// Notifications carry no obligation. Interactive requests are denied.
|
|
482
|
+
if (['confirm', 'select', 'input', 'editor'].includes(value.method)) {
|
|
483
|
+
failure ??= 'CLI 需要额外原生交互授权'
|
|
484
|
+
send({ type: 'extension_ui_response', id: value.id, cancelled: true, confirmed: false })
|
|
485
|
+
}
|
|
486
|
+
return
|
|
487
|
+
}
|
|
488
|
+
if (value.type === 'message_start' && value.message?.role === 'assistant') {
|
|
489
|
+
currentText = ''
|
|
490
|
+
assistantStep++
|
|
491
|
+
return
|
|
492
|
+
}
|
|
493
|
+
if (value.type === 'message_update' && value.assistantMessageEvent?.type === 'text_delta') {
|
|
494
|
+
const text = value.assistantMessageEvent.delta
|
|
495
|
+
if (typeof text !== 'string') throw new Error('Invalid text delta')
|
|
496
|
+
currentText += text
|
|
497
|
+
response += text
|
|
498
|
+
event({ kind: 'assistant', step: assistantStep, text })
|
|
499
|
+
return
|
|
500
|
+
}
|
|
501
|
+
if (value.type === 'message_end' && value.message?.role === 'assistant') {
|
|
502
|
+
if (['error', 'aborted'].includes(value.message.stopReason)) failure ??= '模型请求失败或被中止'
|
|
503
|
+
if (value.message.model && value.message.model !== selectedModel)
|
|
504
|
+
failure ??= '模型响应与所选型号不一致'
|
|
505
|
+
if (value.message.provider && value.message.provider !== selectedProvider)
|
|
506
|
+
failure ??= '模型响应与所选提供商不一致'
|
|
507
|
+
if (!currentText) {
|
|
508
|
+
const text = (value.message.content ?? [])
|
|
509
|
+
.filter((c) => c.type === 'text')
|
|
510
|
+
.map((c) => c.text ?? '')
|
|
511
|
+
.join('')
|
|
512
|
+
if (text) {
|
|
513
|
+
response += text
|
|
514
|
+
event({ kind: 'assistant', step: assistantStep, text })
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return
|
|
518
|
+
}
|
|
519
|
+
if (value.type === 'tool_execution_start') {
|
|
520
|
+
const step = 100000 + toolSteps.size
|
|
521
|
+
toolSteps.set(value.toolCallId, step)
|
|
522
|
+
event({ kind: 'tool', step, text: String(value.toolName ?? '工具'), state: 'RUNNING' })
|
|
523
|
+
return
|
|
524
|
+
}
|
|
525
|
+
if (value.type === 'tool_execution_end') {
|
|
526
|
+
if (value.isError) failure ??= '至少一个 CLI 工具执行失败'
|
|
527
|
+
const detail = (value.result?.content ?? [])
|
|
528
|
+
.filter((c) => c.type === 'text')
|
|
529
|
+
.map((c) => c.text ?? '')
|
|
530
|
+
.join('\n')
|
|
531
|
+
.slice(0, 32768)
|
|
532
|
+
event({
|
|
533
|
+
kind: 'tool',
|
|
534
|
+
step: toolSteps.get(value.toolCallId),
|
|
535
|
+
text: String(value.toolName ?? '工具'),
|
|
536
|
+
state: value.isError ? 'FAILED' : 'COMPLETED',
|
|
537
|
+
detail,
|
|
538
|
+
})
|
|
539
|
+
return
|
|
540
|
+
}
|
|
541
|
+
if (
|
|
542
|
+
(config.cli === 'pi' && value.type === 'agent_settled') ||
|
|
543
|
+
(config.cli === 'omp' && value.type === 'agent_end' && value.isTerminal !== false)
|
|
544
|
+
) {
|
|
545
|
+
if (!sent) throw new Error('Unexpected native terminal event')
|
|
546
|
+
settled = true
|
|
547
|
+
send({ id: 'final', type: 'get_state' })
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
child.stdin.on('error', () => {})
|
|
551
|
+
child.stdout.setEncoding('utf8')
|
|
552
|
+
child.stdout.on('data', (chunk) => {
|
|
553
|
+
try {
|
|
554
|
+
bytes += Buffer.byteLength(chunk)
|
|
555
|
+
if (bytes > 16 * 1024 * 1024) throw new Error('Native RPC output exceeds run limit')
|
|
556
|
+
pending += chunk
|
|
557
|
+
let end
|
|
558
|
+
while ((end = pending.indexOf('\n')) >= 0) {
|
|
559
|
+
const line = pending.slice(0, end)
|
|
560
|
+
pending = pending.slice(end + 1)
|
|
561
|
+
if (Buffer.byteLength(line) > 1024 * 1024) throw new Error('Native RPC line exceeds limit')
|
|
562
|
+
if (line.trim()) consume(JSON.parse(line))
|
|
563
|
+
}
|
|
564
|
+
if (Buffer.byteLength(pending) > 1024 * 1024) throw new Error('Native RPC line exceeds limit')
|
|
565
|
+
} catch (error) {
|
|
566
|
+
stop(errorMessage(error))
|
|
567
|
+
}
|
|
568
|
+
})
|
|
569
|
+
child.stderr.on('data', (chunk) => {
|
|
570
|
+
stderrBytes += chunk.length
|
|
571
|
+
if (stderrBytes > 2 * 1024 * 1024) stop('Native stderr exceeds limit')
|
|
572
|
+
})
|
|
573
|
+
startupTimer = setTimeout(() => stop('Native RPC startup timed out'), 20000)
|
|
574
|
+
send({ id: 'initial', type: 'get_state' })
|
|
575
|
+
const outcome = await new Promise((resolve) => {
|
|
576
|
+
child.once('error', () => {
|
|
577
|
+
failure ??= '无法启动原生 CLI'
|
|
578
|
+
})
|
|
579
|
+
child.once('close', (code, signal) => resolve({ code, signal }))
|
|
580
|
+
})
|
|
581
|
+
clearTimeout(startupTimer)
|
|
582
|
+
clearTimeout(closeTimer)
|
|
583
|
+
clearTimeout(forceTimer)
|
|
584
|
+
if (pending.trim()) failure ??= 'Truncated native RPC frame'
|
|
585
|
+
if (outcome.code !== 0) failure ??= '原生 CLI 异常退出'
|
|
586
|
+
if (!settled || !finalState || !sent) failure ??= '原生 CLI 未确认任务完成'
|
|
587
|
+
try {
|
|
588
|
+
await persist()
|
|
589
|
+
} catch (error) {
|
|
590
|
+
failure ??= errorMessage(error)
|
|
591
|
+
}
|
|
592
|
+
output({ type: 'result', status: failure || stopped ? 'ERROR' : 'SUCCESS', response, error: failure })
|
|
593
|
+
finalized = true
|
|
594
|
+
if (failure || stopped) process.exitCode = 1
|
|
595
|
+
}
|
|
596
|
+
} catch (error) {
|
|
597
|
+
clearTimeout(startupTimer)
|
|
598
|
+
clearTimeout(closeTimer)
|
|
599
|
+
clearTimeout(forceTimer)
|
|
600
|
+
if (!finalized) output({ type: 'result', status: 'ERROR', response: '', error: errorMessage(error) })
|
|
601
|
+
process.exitCode = 1
|
|
602
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { homedir } from 'node:os'
|
|
2
|
+
import { basename, dirname, relative, resolve } from 'node:path'
|
|
3
|
+
|
|
4
|
+
/** OMP 16 resolves its config root with join(os.homedir(), PI_CONFIG_DIR).
|
|
5
|
+
* Windows roots on another drive cannot be expressed relative to the real home.
|
|
6
|
+
* Rebase only this child environment; Host account discovery retains the real home.
|
|
7
|
+
*/
|
|
8
|
+
export function ompConfigurationEnvironment(configRoot) {
|
|
9
|
+
if (typeof configRoot !== 'string' || !configRoot.trim() || configRoot.includes('\0'))
|
|
10
|
+
throw new Error('Invalid OMP configuration root')
|
|
11
|
+
const root = resolve(configRoot)
|
|
12
|
+
if (process.platform === 'win32')
|
|
13
|
+
return { HOME: dirname(root), USERPROFILE: dirname(root), PI_CONFIG_DIR: basename(root) }
|
|
14
|
+
return { PI_CONFIG_DIR: relative(homedir(), root) }
|
|
15
|
+
}
|