@jingyi0605/codingns4dsh 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1114 -0
- package/assets/provider-icons/claude-code.png +0 -0
- package/assets/provider-icons/codex.png +0 -0
- package/assets/provider-icons/command-code.svg +5 -0
- package/assets/provider-icons/deepseek-harness.svg +9 -0
- package/assets/provider-icons/gemini.png +0 -0
- package/assets/provider-icons/grok.png +0 -0
- package/assets/provider-icons/kimi.png +0 -0
- package/assets/provider-icons/opencode.png +0 -0
- package/assets/provider-icons/pi.svg +7 -0
- package/data/build/dist/bootstrap/dsh-connection-adapter.d.ts +97 -0
- package/data/build/dist/bootstrap/dsh-connection-adapter.js +169 -0
- package/data/build/dist/bootstrap/index.d.ts +38 -0
- package/data/build/dist/bootstrap/index.js +44 -0
- package/data/build/dist/client/account-bar.d.ts +9 -0
- package/data/build/dist/client/account-bar.js +398 -0
- package/data/build/dist/client/bundle.js +26167 -0
- package/data/build/dist/client/cli-catalog.d.ts +24 -0
- package/data/build/dist/client/cli-catalog.js +108 -0
- package/data/build/dist/client/cli-slots.d.ts +38 -0
- package/data/build/dist/client/cli-slots.js +297 -0
- package/data/build/dist/client/debug/ui.d.ts +12 -0
- package/data/build/dist/client/debug/ui.js +451 -0
- package/data/build/dist/client/dsh-h5-bootstrap.d.ts +64 -0
- package/data/build/dist/client/dsh-h5-bootstrap.js +293 -0
- package/data/build/dist/client/dsh-runtime-version.d.ts +3 -0
- package/data/build/dist/client/dsh-runtime-version.js +11 -0
- package/data/build/dist/client/external-tool-stream.d.ts +24 -0
- package/data/build/dist/client/external-tool-stream.js +114 -0
- package/data/build/dist/client/features/cli-adapters.d.ts +7 -0
- package/data/build/dist/client/features/cli-adapters.js +215 -0
- package/data/build/dist/client/features/debug.d.ts +4 -0
- package/data/build/dist/client/features/debug.js +24 -0
- package/data/build/dist/client/features/index.d.ts +33 -0
- package/data/build/dist/client/features/index.js +44 -0
- package/data/build/dist/client/features/lan-access-panel.d.ts +5 -0
- package/data/build/dist/client/features/lan-access-panel.js +146 -0
- package/data/build/dist/client/features/lan-access.d.ts +9 -0
- package/data/build/dist/client/features/lan-access.js +30 -0
- package/data/build/dist/client/features/login-protection-panel.d.ts +5 -0
- package/data/build/dist/client/features/login-protection-panel.js +91 -0
- package/data/build/dist/client/features/login-protection-session.d.ts +6 -0
- package/data/build/dist/client/features/login-protection-session.js +25 -0
- package/data/build/dist/client/features/login-protection.d.ts +4 -0
- package/data/build/dist/client/features/login-protection.js +23 -0
- package/data/build/dist/client/features/reverse-proxy-panel.d.ts +9 -0
- package/data/build/dist/client/features/reverse-proxy-panel.js +159 -0
- package/data/build/dist/client/features/reverse-proxy.d.ts +12 -0
- package/data/build/dist/client/features/reverse-proxy.js +112 -0
- package/data/build/dist/client/features/terminal-enhancement-panel.d.ts +5 -0
- package/data/build/dist/client/features/terminal-enhancement-panel.js +160 -0
- package/data/build/dist/client/features/terminal-enhancement.d.ts +4 -0
- package/data/build/dist/client/features/terminal-enhancement.js +24 -0
- package/data/build/dist/client/features/types.d.ts +58 -0
- package/data/build/dist/client/features/types.js +2 -0
- package/data/build/dist/client/features/workspace-session-enhancement-panel.d.ts +5 -0
- package/data/build/dist/client/features/workspace-session-enhancement-panel.js +66 -0
- package/data/build/dist/client/features/workspace-session-enhancement.d.ts +4 -0
- package/data/build/dist/client/features/workspace-session-enhancement.js +101 -0
- package/data/build/dist/client/h5-bootstrap-entry.d.ts +2 -0
- package/data/build/dist/client/h5-bootstrap-entry.js +17 -0
- package/data/build/dist/client/index.d.ts +31 -0
- package/data/build/dist/client/index.js +98 -0
- package/data/build/dist/client/lan-access.d.ts +28 -0
- package/data/build/dist/client/lan-access.js +68 -0
- package/data/build/dist/client/locale.d.ts +20 -0
- package/data/build/dist/client/locale.js +393 -0
- package/data/build/dist/client/provider-icon-assets.d.ts +2 -0
- package/data/build/dist/client/provider-icon-assets.js +23 -0
- package/data/build/dist/client/provider-icons.d.ts +19 -0
- package/data/build/dist/client/provider-icons.js +39 -0
- package/data/build/dist/client/remote-web-context.d.ts +57 -0
- package/data/build/dist/client/remote-web-context.js +939 -0
- package/data/build/dist/client/session-adapter-cache.d.ts +18 -0
- package/data/build/dist/client/session-adapter-cache.js +84 -0
- package/data/build/dist/client/settings-bridge.d.ts +32 -0
- package/data/build/dist/client/settings-bridge.js +126 -0
- package/data/build/dist/client/settings-section.d.ts +30 -0
- package/data/build/dist/client/settings-section.js +93 -0
- package/data/build/dist/client/subscription-slot.d.ts +18 -0
- package/data/build/dist/client/subscription-slot.js +223 -0
- package/data/build/dist/client/terminal/index.d.ts +4 -0
- package/data/build/dist/client/terminal/index.js +3 -0
- package/data/build/dist/client/terminal/model.d.ts +116 -0
- package/data/build/dist/client/terminal/model.js +435 -0
- package/data/build/dist/client/terminal/styles.d.ts +21 -0
- package/data/build/dist/client/terminal/styles.js +51 -0
- package/data/build/dist/client/terminal/ui.d.ts +19 -0
- package/data/build/dist/client/terminal/ui.js +257 -0
- package/data/build/dist/client/terminal/xterm-view.d.ts +13 -0
- package/data/build/dist/client/terminal/xterm-view.js +213 -0
- package/data/build/dist/client/theme.d.ts +56 -0
- package/data/build/dist/client/theme.js +186 -0
- package/data/build/dist/client/workspace-session-archive-dom.d.ts +29 -0
- package/data/build/dist/client/workspace-session-archive-dom.js +525 -0
- package/data/build/dist/client/workspace-session-fiber.d.ts +3 -0
- package/data/build/dist/client/workspace-session-fiber.js +33 -0
- package/data/build/dist/client/workspace-session-logo-dom.d.ts +26 -0
- package/data/build/dist/client/workspace-session-logo-dom.js +128 -0
- package/data/build/dist/features/index.d.ts +3 -0
- package/data/build/dist/features/index.js +2 -0
- package/data/build/dist/features/registry.d.ts +74 -0
- package/data/build/dist/features/registry.js +382 -0
- package/data/build/dist/features/resource-scope/index.d.ts +39 -0
- package/data/build/dist/features/resource-scope/index.js +161 -0
- package/data/build/dist/host/auth-session.d.ts +42 -0
- package/data/build/dist/host/auth-session.js +186 -0
- package/data/build/dist/host/cli-adapters/claude-driver.d.ts +14 -0
- package/data/build/dist/host/cli-adapters/claude-driver.js +103 -0
- package/data/build/dist/host/cli-adapters/codex-driver.d.ts +45 -0
- package/data/build/dist/host/cli-adapters/codex-driver.js +534 -0
- package/data/build/dist/host/cli-adapters/command-code-driver.d.ts +38 -0
- package/data/build/dist/host/cli-adapters/command-code-driver.js +394 -0
- package/data/build/dist/host/cli-adapters/command-code-subscription.d.ts +19 -0
- package/data/build/dist/host/cli-adapters/command-code-subscription.js +126 -0
- package/data/build/dist/host/cli-adapters/driver.d.ts +41 -0
- package/data/build/dist/host/cli-adapters/driver.js +2 -0
- package/data/build/dist/host/cli-adapters/dsh-message-projector.d.ts +37 -0
- package/data/build/dist/host/cli-adapters/dsh-message-projector.js +180 -0
- package/data/build/dist/host/cli-adapters/dsh-tool-history.d.ts +48 -0
- package/data/build/dist/host/cli-adapters/dsh-tool-history.js +373 -0
- package/data/build/dist/host/cli-adapters/feature.d.ts +7 -0
- package/data/build/dist/host/cli-adapters/feature.js +335 -0
- package/data/build/dist/host/cli-adapters/gemini-driver.d.ts +24 -0
- package/data/build/dist/host/cli-adapters/gemini-driver.js +427 -0
- package/data/build/dist/host/cli-adapters/grok-driver.d.ts +38 -0
- package/data/build/dist/host/cli-adapters/grok-driver.js +372 -0
- package/data/build/dist/host/cli-adapters/http-sse-client.d.ts +22 -0
- package/data/build/dist/host/cli-adapters/http-sse-client.js +99 -0
- package/data/build/dist/host/cli-adapters/interaction-events.d.ts +11 -0
- package/data/build/dist/host/cli-adapters/interaction-events.js +80 -0
- package/data/build/dist/host/cli-adapters/json-rpc-process.d.ts +65 -0
- package/data/build/dist/host/cli-adapters/json-rpc-process.js +256 -0
- package/data/build/dist/host/cli-adapters/kimi-driver.d.ts +21 -0
- package/data/build/dist/host/cli-adapters/kimi-driver.js +382 -0
- package/data/build/dist/host/cli-adapters/model-catalog.d.ts +22 -0
- package/data/build/dist/host/cli-adapters/model-catalog.js +89 -0
- package/data/build/dist/host/cli-adapters/opencode-driver.d.ts +55 -0
- package/data/build/dist/host/cli-adapters/opencode-driver.js +728 -0
- package/data/build/dist/host/cli-adapters/pi-driver.d.ts +43 -0
- package/data/build/dist/host/cli-adapters/pi-driver.js +312 -0
- package/data/build/dist/host/cli-adapters/process-utils.d.ts +16 -0
- package/data/build/dist/host/cli-adapters/process-utils.js +27 -0
- package/data/build/dist/host/cli-adapters/provider-subscription.d.ts +82 -0
- package/data/build/dist/host/cli-adapters/provider-subscription.js +610 -0
- package/data/build/dist/host/cli-adapters/registry.d.ts +95 -0
- package/data/build/dist/host/cli-adapters/registry.js +659 -0
- package/data/build/dist/host/cli-adapters/rpc-driver-utils.d.ts +24 -0
- package/data/build/dist/host/cli-adapters/rpc-driver-utils.js +119 -0
- package/data/build/dist/host/cli-adapters/session-probe.d.ts +20 -0
- package/data/build/dist/host/cli-adapters/session-probe.js +139 -0
- package/data/build/dist/host/cli-adapters/session-store.d.ts +68 -0
- package/data/build/dist/host/cli-adapters/session-store.js +180 -0
- package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts +45 -0
- package/data/build/dist/host/cli-adapters/standard-stream-driver.js +205 -0
- package/data/build/dist/host/cli-adapters/stream-normalizer.d.ts +25 -0
- package/data/build/dist/host/cli-adapters/stream-normalizer.js +83 -0
- package/data/build/dist/host/cli-adapters/tool-observation.d.ts +9 -0
- package/data/build/dist/host/cli-adapters/tool-observation.js +39 -0
- package/data/build/dist/host/control-api-client.d.ts +84 -0
- package/data/build/dist/host/control-api-client.js +130 -0
- package/data/build/dist/host/credential-store.d.ts +54 -0
- package/data/build/dist/host/credential-store.js +114 -0
- package/data/build/dist/host/debug.d.ts +79 -0
- package/data/build/dist/host/debug.js +295 -0
- package/data/build/dist/host/dsh-device-runtime.d.ts +39 -0
- package/data/build/dist/host/dsh-device-runtime.js +118 -0
- package/data/build/dist/host/dsh-gateway-feature.d.ts +5 -0
- package/data/build/dist/host/dsh-gateway-feature.js +55 -0
- package/data/build/dist/host/dsh-runtime-version.d.ts +12 -0
- package/data/build/dist/host/dsh-runtime-version.js +69 -0
- package/data/build/dist/host/features/auth.d.ts +11 -0
- package/data/build/dist/host/features/auth.js +273 -0
- package/data/build/dist/host/features/debug.d.ts +5 -0
- package/data/build/dist/host/features/debug.js +90 -0
- package/data/build/dist/host/features/host-status.d.ts +12 -0
- package/data/build/dist/host/features/host-status.js +54 -0
- package/data/build/dist/host/features/index.d.ts +19 -0
- package/data/build/dist/host/features/index.js +29 -0
- package/data/build/dist/host/features/lan-access-dsh.d.ts +8 -0
- package/data/build/dist/host/features/lan-access-dsh.js +45 -0
- package/data/build/dist/host/features/terminal-process.d.ts +5 -0
- package/data/build/dist/host/features/terminal-process.js +110 -0
- package/data/build/dist/host/features/terminal-status.d.ts +18 -0
- package/data/build/dist/host/features/terminal-status.js +56 -0
- package/data/build/dist/host/features/types.d.ts +41 -0
- package/data/build/dist/host/features/types.js +2 -0
- package/data/build/dist/host/index.d.ts +43 -0
- package/data/build/dist/host/index.js +135 -0
- package/data/build/dist/host/lan-access-dsh.d.ts +83 -0
- package/data/build/dist/host/lan-access-dsh.js +831 -0
- package/data/build/dist/host/native-session-bridge.d.ts +130 -0
- package/data/build/dist/host/native-session-bridge.js +317 -0
- package/data/build/dist/host/relay-tunnel-runtime.d.ts +97 -0
- package/data/build/dist/host/relay-tunnel-runtime.js +361 -0
- package/data/build/dist/host/remote-web-runtime.d.ts +78 -0
- package/data/build/dist/host/remote-web-runtime.js +559 -0
- package/data/build/dist/host/rpc-table.d.ts +32 -0
- package/data/build/dist/host/rpc-table.js +47 -0
- package/data/build/dist/host/rpc.d.ts +18 -0
- package/data/build/dist/host/rpc.js +248 -0
- package/data/build/dist/host/settings.d.ts +19 -0
- package/data/build/dist/host/settings.js +80 -0
- package/data/build/dist/host/terminal/attachment-registry.d.ts +17 -0
- package/data/build/dist/host/terminal/attachment-registry.js +49 -0
- package/data/build/dist/host/terminal/backends/conpty-backend.d.ts +39 -0
- package/data/build/dist/host/terminal/backends/conpty-backend.js +160 -0
- package/data/build/dist/host/terminal/backends/local-pty-backend.d.ts +36 -0
- package/data/build/dist/host/terminal/backends/local-pty-backend.js +178 -0
- package/data/build/dist/host/terminal/backends/tmux-backend.d.ts +40 -0
- package/data/build/dist/host/terminal/backends/tmux-backend.js +191 -0
- package/data/build/dist/host/terminal/broker/conpty-broker-client.d.ts +27 -0
- package/data/build/dist/host/terminal/broker/conpty-broker-client.js +154 -0
- package/data/build/dist/host/terminal/broker/conpty-broker-process.d.ts +10 -0
- package/data/build/dist/host/terminal/broker/conpty-broker-process.js +147 -0
- package/data/build/dist/host/terminal/broker/conpty-broker-protocol.d.ts +92 -0
- package/data/build/dist/host/terminal/broker/conpty-broker-protocol.js +134 -0
- package/data/build/dist/host/terminal/controller-factory.d.ts +37 -0
- package/data/build/dist/host/terminal/controller-factory.js +82 -0
- package/data/build/dist/host/terminal/index.d.ts +17 -0
- package/data/build/dist/host/terminal/index.js +17 -0
- package/data/build/dist/host/terminal/runtime-adapter.d.ts +77 -0
- package/data/build/dist/host/terminal/runtime-adapter.js +18 -0
- package/data/build/dist/host/terminal/runtime-manager.d.ts +41 -0
- package/data/build/dist/host/terminal/runtime-manager.js +179 -0
- package/data/build/dist/host/terminal/shell-detection.d.ts +23 -0
- package/data/build/dist/host/terminal/shell-detection.js +105 -0
- package/data/build/dist/host/terminal/startup.d.ts +33 -0
- package/data/build/dist/host/terminal/startup.js +100 -0
- package/data/build/dist/host/terminal/terminal-controller.d.ts +78 -0
- package/data/build/dist/host/terminal/terminal-controller.js +324 -0
- package/data/build/dist/host/terminal/terminal-process-service.d.ts +30 -0
- package/data/build/dist/host/terminal/terminal-process-service.js +194 -0
- package/data/build/dist/host/terminal/terminal-process-store.d.ts +45 -0
- package/data/build/dist/host/terminal/terminal-process-store.js +183 -0
- package/data/build/dist/host/terminal/terminal-service.d.ts +87 -0
- package/data/build/dist/host/terminal/terminal-service.js +528 -0
- package/data/build/dist/host/terminal/terminal-store.d.ts +49 -0
- package/data/build/dist/host/terminal/terminal-store.js +242 -0
- package/data/build/dist/index.d.ts +37 -0
- package/data/build/dist/index.js +26 -0
- package/data/build/dist/shared/contracts/auth.d.ts +122 -0
- package/data/build/dist/shared/contracts/auth.js +8 -0
- package/data/build/dist/shared/contracts/cli-adapter.d.ts +192 -0
- package/data/build/dist/shared/contracts/cli-adapter.js +2 -0
- package/data/build/dist/shared/contracts/command-code.d.ts +5 -0
- package/data/build/dist/shared/contracts/command-code.js +2 -0
- package/data/build/dist/shared/contracts/config.d.ts +113 -0
- package/data/build/dist/shared/contracts/config.js +86 -0
- package/data/build/dist/shared/contracts/debug-config.d.ts +28 -0
- package/data/build/dist/shared/contracts/debug-config.js +78 -0
- package/data/build/dist/shared/contracts/debug.d.ts +75 -0
- package/data/build/dist/shared/contracts/debug.js +121 -0
- package/data/build/dist/shared/contracts/dsh-device.d.ts +72 -0
- package/data/build/dist/shared/contracts/dsh-device.js +2 -0
- package/data/build/dist/shared/contracts/errors.d.ts +33 -0
- package/data/build/dist/shared/contracts/errors.js +41 -0
- package/data/build/dist/shared/contracts/feature.d.ts +71 -0
- package/data/build/dist/shared/contracts/feature.js +2 -0
- package/data/build/dist/shared/contracts/host-status.d.ts +14 -0
- package/data/build/dist/shared/contracts/host-status.js +2 -0
- package/data/build/dist/shared/contracts/lan-access-dsh.d.ts +30 -0
- package/data/build/dist/shared/contracts/lan-access-dsh.js +2 -0
- package/data/build/dist/shared/contracts/peer-host.d.ts +37 -0
- package/data/build/dist/shared/contracts/peer-host.js +9 -0
- package/data/build/dist/shared/contracts/signaling.d.ts +67 -0
- package/data/build/dist/shared/contracts/signaling.js +2 -0
- package/data/build/dist/shared/contracts/subscription.d.ts +69 -0
- package/data/build/dist/shared/contracts/subscription.js +2 -0
- package/data/build/dist/shared/contracts/terminal-process.d.ts +65 -0
- package/data/build/dist/shared/contracts/terminal-process.js +2 -0
- package/data/build/dist/shared/contracts/terminal.d.ts +127 -0
- package/data/build/dist/shared/contracts/terminal.js +2 -0
- package/data/build/dist/shared/contracts/transport.d.ts +45 -0
- package/data/build/dist/shared/contracts/transport.js +3 -0
- package/data/build/dist/shared/contracts/version.d.ts +19 -0
- package/data/build/dist/shared/contracts/version.js +74 -0
- package/data/build/dist/shared/index.d.ts +22 -0
- package/data/build/dist/shared/index.js +9 -0
- package/data/build/dist/transport/carrier.d.ts +38 -0
- package/data/build/dist/transport/carrier.js +268 -0
- package/data/build/dist/transport/debug.d.ts +22 -0
- package/data/build/dist/transport/debug.js +57 -0
- package/data/build/dist/transport/dsh-envelope.d.ts +41 -0
- package/data/build/dist/transport/dsh-envelope.js +107 -0
- package/data/build/dist/transport/dsh-gateway.d.ts +64 -0
- package/data/build/dist/transport/dsh-gateway.js +254 -0
- package/data/build/dist/transport/dsh-session.d.ts +55 -0
- package/data/build/dist/transport/dsh-session.js +256 -0
- package/data/build/dist/transport/dsh-transport.d.ts +57 -0
- package/data/build/dist/transport/dsh-transport.js +125 -0
- package/data/build/dist/transport/frame.d.ts +132 -0
- package/data/build/dist/transport/frame.js +168 -0
- package/data/build/dist/transport/index.d.ts +11 -0
- package/data/build/dist/transport/index.js +11 -0
- package/data/build/dist/transport/multiplexer.d.ts +63 -0
- package/data/build/dist/transport/multiplexer.js +323 -0
- package/data/build/dist/transport/webrtc-client.d.ts +65 -0
- package/data/build/dist/transport/webrtc-client.js +255 -0
- package/data/build/dist/transport/webrtc-host.d.ts +58 -0
- package/data/build/dist/transport/webrtc-host.js +252 -0
- package/data/build/dist/typert.host.d.ts +25 -0
- package/data/build/dist/typert.host.js +150 -0
- package/dsh.bundle.patch +12 -0
- package/package.json +135 -0
- package/profile/README.md +27 -0
- package/profile/cordis.patch.yml +2 -0
- package/profile/package.json +20 -0
- package/profile/scripts/check-dsh-install.mjs +71 -0
- package/profile/version.json +5 -0
- package/scripts/check-dsh-install.mjs +88 -0
- package/version.json +6 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { basename, join } from 'node:path';
|
|
4
|
+
import { JsonRpcProcess } from './json-rpc-process.js';
|
|
5
|
+
import { detectBinary, emptyCatalog, isRecord, textValue, usageChunk } from './rpc-driver-utils.js';
|
|
6
|
+
import { GROK_CATALOG, isProviderDefaultModel } from './model-catalog.js';
|
|
7
|
+
import { isRegularFile, probeStoredSession, resolveSessionDirectory } from './session-probe.js';
|
|
8
|
+
import { firstToolText, isToolRecord, normalizeToolStatus, serializeToolValue } from './tool-observation.js';
|
|
9
|
+
/** Grok Build 的 ACP stdio 驱动。ACP 会话和权限细节只在 Host 进程内处理。 */
|
|
10
|
+
export class GrokBuildDriver {
|
|
11
|
+
descriptor = { id: 'grok', name: 'Grok Build', protocol: 'acp', capabilities: ['models', 'stream', 'tool-events', 'reasoning', 'usage', 'permission'] };
|
|
12
|
+
binaries;
|
|
13
|
+
runSpawnSync;
|
|
14
|
+
runSpawn;
|
|
15
|
+
sessionRoots;
|
|
16
|
+
cachedBinary = null;
|
|
17
|
+
processes = new Set();
|
|
18
|
+
sessions = new Map();
|
|
19
|
+
constructor(options = {}) {
|
|
20
|
+
this.binaries = options.binaries ?? ['grok', 'grok-build'];
|
|
21
|
+
this.runSpawnSync = options.spawnSync ?? spawnSync;
|
|
22
|
+
this.runSpawn = options.spawn ?? spawn;
|
|
23
|
+
this.sessionRoots = options.sessionRoots ?? [join(process.env.GROK_HOME ?? join(homedir(), '.grok'), 'sessions')];
|
|
24
|
+
}
|
|
25
|
+
async detect() {
|
|
26
|
+
const result = await detectBinary({ binaries: this.binaries, spawnSync: this.runSpawnSync });
|
|
27
|
+
if (result.installed)
|
|
28
|
+
this.cachedBinary = result.command;
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
async listModels() {
|
|
32
|
+
const command = this.cachedBinary ?? (await this.detect()).command;
|
|
33
|
+
if (command === null)
|
|
34
|
+
return emptyCatalog();
|
|
35
|
+
const rpc = new JsonRpcProcess({ command, args: ['agent', '--no-leader', 'stdio'], spawn: this.runSpawn });
|
|
36
|
+
try {
|
|
37
|
+
await rpc.request('initialize', { protocolVersion: 1, clientInfo: { name: 'codingns4dsh', version: '0.1.0' }, capabilities: {} });
|
|
38
|
+
rpc.notify('initialized', {});
|
|
39
|
+
const session = await rpc.request('session/new', { cwd: process.cwd(), mcpServers: [] });
|
|
40
|
+
const parsed = parseGrokCatalog(session);
|
|
41
|
+
return parsed.groups.length > 0 ? parsed : GROK_CATALOG;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return GROK_CATALOG;
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
rpc.dispose();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async probeSession(input) {
|
|
51
|
+
return probeStoredSession(input, {
|
|
52
|
+
roots: this.sessionRoots,
|
|
53
|
+
matches: (path, entry, id) => entry.isDirectory() && basename(path) === id,
|
|
54
|
+
validate: async (path, id) => {
|
|
55
|
+
const directory = await resolveSessionDirectory(path);
|
|
56
|
+
return basename(directory) === id && await isRegularFile(join(directory, 'updates.jsonl'));
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
async *executeTurn(input) {
|
|
61
|
+
const command = this.cachedBinary ?? (await this.detect()).command;
|
|
62
|
+
if (command === null)
|
|
63
|
+
throw new Error('Grok Build 未安装');
|
|
64
|
+
const state = await this.getSession(input, command);
|
|
65
|
+
const rpc = state.rpc;
|
|
66
|
+
let providerSessionId = state.providerSessionId;
|
|
67
|
+
try {
|
|
68
|
+
if (providerSessionId === '') {
|
|
69
|
+
const session = await rpc.request('session/new', {
|
|
70
|
+
cwd: input.cwd ?? process.cwd(),
|
|
71
|
+
mcpServers: [],
|
|
72
|
+
...(!isProviderDefaultModel(input.modelId) ? { model: input.modelId } : {}),
|
|
73
|
+
}, { signal: input.signal });
|
|
74
|
+
providerSessionId = readSessionId(session) ?? input.sessionId;
|
|
75
|
+
state.providerSessionId = providerSessionId;
|
|
76
|
+
this.sessions.set(input.sessionId, state);
|
|
77
|
+
this.sessions.set(providerSessionId, state);
|
|
78
|
+
}
|
|
79
|
+
yield { type: 'session-binding', providerSessionId };
|
|
80
|
+
const stream = streamGrokPrompt(rpc, providerSessionId, input.prompt, input.signal, (notification) => {
|
|
81
|
+
const requestId = interactionRequestId(notification);
|
|
82
|
+
if (requestId !== null && notification.id !== undefined && notification.id !== null)
|
|
83
|
+
state.requests.set(requestId, notification.id);
|
|
84
|
+
});
|
|
85
|
+
let finishReason;
|
|
86
|
+
while (true) {
|
|
87
|
+
const item = await stream.next();
|
|
88
|
+
if (item.done) {
|
|
89
|
+
finishReason = item.value;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
const chunk = acpMessageToChunk(item.value);
|
|
93
|
+
if (chunk !== null)
|
|
94
|
+
yield chunk;
|
|
95
|
+
}
|
|
96
|
+
if (finishReason === 'cancel') {
|
|
97
|
+
try {
|
|
98
|
+
await rpc.request('session/cancel', { sessionId: providerSessionId }, { killOnAbort: false });
|
|
99
|
+
}
|
|
100
|
+
catch { /* 不同 ACP 版本的取消方法可能不同,请求级取消已经先行发出。 */ }
|
|
101
|
+
}
|
|
102
|
+
yield { type: 'finish', reason: finishReason };
|
|
103
|
+
}
|
|
104
|
+
finally { /* ACP 进程和会话跨轮复用,统一由 dispose() 回收。 */ }
|
|
105
|
+
}
|
|
106
|
+
respondPermission(sessionId, response) {
|
|
107
|
+
const state = this.sessions.get(sessionId);
|
|
108
|
+
if (state === undefined)
|
|
109
|
+
throw new Error('Grok 权限请求已结束');
|
|
110
|
+
const rpcId = state.requests.get(response.requestId);
|
|
111
|
+
if (rpcId === undefined)
|
|
112
|
+
throw new Error('Grok 权限请求不存在');
|
|
113
|
+
state.requests.delete(response.requestId);
|
|
114
|
+
const optionId = response.approved ? 'allow-once' : 'reject-once';
|
|
115
|
+
state.rpc.respond(rpcId, { outcome: { outcome: 'selected', optionId } });
|
|
116
|
+
}
|
|
117
|
+
dispose() { for (const process of this.processes)
|
|
118
|
+
process.dispose(); this.processes.clear(); this.sessions.clear(); this.cachedBinary = null; }
|
|
119
|
+
async getSession(input, command) {
|
|
120
|
+
const previous = this.sessions.get(input.sessionId);
|
|
121
|
+
if (previous !== undefined && previous.cwd === input.cwd)
|
|
122
|
+
return previous;
|
|
123
|
+
previous?.rpc.dispose();
|
|
124
|
+
const rpc = new JsonRpcProcess({ command, args: ['agent', '--no-leader', 'stdio'], cwd: input.cwd, spawn: this.runSpawn });
|
|
125
|
+
this.processes.add(rpc);
|
|
126
|
+
const state = { rpc, cwd: input.cwd, providerSessionId: '', requests: new Map() };
|
|
127
|
+
await rpc.request('initialize', {
|
|
128
|
+
protocolVersion: 1,
|
|
129
|
+
clientInfo: { name: 'codingns4dsh', version: '0.1.0' },
|
|
130
|
+
capabilities: {},
|
|
131
|
+
});
|
|
132
|
+
rpc.notify('initialized', {});
|
|
133
|
+
if (input.providerSessionId) {
|
|
134
|
+
try {
|
|
135
|
+
const loaded = await rpc.request('session/load', { sessionId: input.providerSessionId, cwd: input.cwd ?? process.cwd(), mcpServers: [] });
|
|
136
|
+
state.providerSessionId = readSessionId(loaded) ?? input.providerSessionId;
|
|
137
|
+
}
|
|
138
|
+
catch { /* 旧版 ACP 没有 load,下一轮在同一进程创建新会话 */ }
|
|
139
|
+
}
|
|
140
|
+
// ACP 权限是 Grok 发起的 server request,先挂起响应,待标准权限入口明确回复原始 id。
|
|
141
|
+
rpc.setServerRequestHandler((message) => {
|
|
142
|
+
const requestId = interactionRequestId(message);
|
|
143
|
+
if (requestId !== null && message.id !== undefined && message.id !== null)
|
|
144
|
+
state.requests.set(requestId, message.id);
|
|
145
|
+
return new Promise(() => undefined);
|
|
146
|
+
});
|
|
147
|
+
this.sessions.set(input.sessionId, state);
|
|
148
|
+
if (state.providerSessionId !== '')
|
|
149
|
+
this.sessions.set(state.providerSessionId, state);
|
|
150
|
+
return state;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const GROK_DRAIN_WAIT_MS = 250;
|
|
154
|
+
/**
|
|
155
|
+
* Grok 可能先返回 prompt 响应,再补发最后一批 session/update。
|
|
156
|
+
* 响应或终态任一先到都会启动固定排空窗口,窗口内的文本和错误仍会被消费。
|
|
157
|
+
*/
|
|
158
|
+
async function* streamGrokPrompt(rpc, sessionId, prompt, signal, onNotification) {
|
|
159
|
+
const queue = [];
|
|
160
|
+
const requestController = new AbortController();
|
|
161
|
+
let wake;
|
|
162
|
+
let drainDeadline = null;
|
|
163
|
+
let terminalReason = null;
|
|
164
|
+
const notify = () => { wake?.(); wake = undefined; };
|
|
165
|
+
const beginDrain = () => {
|
|
166
|
+
if (drainDeadline === null)
|
|
167
|
+
drainDeadline = Date.now() + GROK_DRAIN_WAIT_MS;
|
|
168
|
+
notify();
|
|
169
|
+
};
|
|
170
|
+
const listener = (message) => {
|
|
171
|
+
queue.push(message);
|
|
172
|
+
onNotification(message);
|
|
173
|
+
const notificationReason = grokNotificationReason(message);
|
|
174
|
+
if (notificationReason === null) {
|
|
175
|
+
notify();
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
terminalReason = mergeGrokReason(terminalReason, notificationReason);
|
|
179
|
+
// 部分 Grok 版本只发终态通知而不回复 prompt,主动取消可清理待处理请求。
|
|
180
|
+
requestController.abort();
|
|
181
|
+
beginDrain();
|
|
182
|
+
};
|
|
183
|
+
const removeListener = rpc.addNotificationListener(listener);
|
|
184
|
+
const onAbort = () => {
|
|
185
|
+
terminalReason = 'cancel';
|
|
186
|
+
queue.length = 0;
|
|
187
|
+
requestController.abort();
|
|
188
|
+
drainDeadline = Date.now();
|
|
189
|
+
notify();
|
|
190
|
+
};
|
|
191
|
+
if (signal?.aborted)
|
|
192
|
+
onAbort();
|
|
193
|
+
else
|
|
194
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
195
|
+
void rpc.request('session/prompt', {
|
|
196
|
+
sessionId,
|
|
197
|
+
prompt: [{ type: 'text', text: prompt }],
|
|
198
|
+
}, { signal: requestController.signal, killOnAbort: false }).then((response) => {
|
|
199
|
+
const responseReason = grokPromptReason(response);
|
|
200
|
+
if (responseReason !== null)
|
|
201
|
+
terminalReason = mergeGrokReason(terminalReason, responseReason);
|
|
202
|
+
beginDrain();
|
|
203
|
+
}, () => {
|
|
204
|
+
if (terminalReason === null)
|
|
205
|
+
terminalReason = signal?.aborted ? 'cancel' : 'error';
|
|
206
|
+
beginDrain();
|
|
207
|
+
});
|
|
208
|
+
try {
|
|
209
|
+
while (true) {
|
|
210
|
+
if (queue.length > 0) {
|
|
211
|
+
yield queue.shift();
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (drainDeadline === null) {
|
|
215
|
+
await new Promise((resolve) => { wake = resolve; });
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
const remaining = drainDeadline - Date.now();
|
|
219
|
+
if (remaining <= 0)
|
|
220
|
+
break;
|
|
221
|
+
await Promise.race([
|
|
222
|
+
new Promise((resolve) => { wake = resolve; }),
|
|
223
|
+
new Promise((resolve) => setTimeout(resolve, remaining)),
|
|
224
|
+
]);
|
|
225
|
+
wake = undefined;
|
|
226
|
+
}
|
|
227
|
+
return terminalReason ?? 'stop';
|
|
228
|
+
}
|
|
229
|
+
finally {
|
|
230
|
+
signal?.removeEventListener('abort', onAbort);
|
|
231
|
+
removeListener();
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function grokPromptReason(value) {
|
|
235
|
+
if (!isRecord(value) || typeof value.stopReason !== 'string')
|
|
236
|
+
return null;
|
|
237
|
+
const reason = value.stopReason.toLowerCase();
|
|
238
|
+
return reason === 'cancelled' || reason === 'error' || reason === 'failed' ? 'error' : 'stop';
|
|
239
|
+
}
|
|
240
|
+
function mergeGrokReason(current, next) {
|
|
241
|
+
if (current === 'error' || next === 'error')
|
|
242
|
+
return 'error';
|
|
243
|
+
if (current === 'cancel' || next === 'cancel')
|
|
244
|
+
return 'cancel';
|
|
245
|
+
return 'stop';
|
|
246
|
+
}
|
|
247
|
+
function grokNotificationReason(message) {
|
|
248
|
+
const params = isRecord(message.params) ? message.params : message;
|
|
249
|
+
const update = isRecord(params.update) ? params.update : params;
|
|
250
|
+
const type = typeof update.sessionUpdate === 'string'
|
|
251
|
+
? update.sessionUpdate.toLowerCase()
|
|
252
|
+
: typeof update.type === 'string'
|
|
253
|
+
? update.type.toLowerCase()
|
|
254
|
+
: '';
|
|
255
|
+
if (type.includes('error') || type.includes('failed'))
|
|
256
|
+
return 'error';
|
|
257
|
+
if (type.includes('turn_completed') || type.includes('turn_complete') || type === 'completed' || type === 'done')
|
|
258
|
+
return 'stop';
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
function acpMessageToChunk(message) {
|
|
262
|
+
const params = isRecord(message.params) ? message.params : message;
|
|
263
|
+
const update = isRecord(params.update) ? params.update : params;
|
|
264
|
+
const type = typeof update.sessionUpdate === 'string' ? update.sessionUpdate : typeof update.type === 'string' ? update.type : typeof message.method === 'string' ? message.method : '';
|
|
265
|
+
const text = textValue(update.delta ?? update.text ?? update.content ?? update.message ?? update.detail);
|
|
266
|
+
if (type.includes('permission')) {
|
|
267
|
+
const requestId = permissionRequestId(message) ?? (typeof update.requestId === 'string' ? update.requestId : typeof update.id === 'string' ? update.id : null);
|
|
268
|
+
if (requestId !== null)
|
|
269
|
+
return { type: 'permission-request', requestId, kind: typeof update.kind === 'string' ? update.kind : 'unknown', ...(text ? { detail: text } : {}) };
|
|
270
|
+
}
|
|
271
|
+
if (type.includes('agent_message') || type.includes('text') || type === 'message')
|
|
272
|
+
return text ? { type: 'text-delta', text } : null;
|
|
273
|
+
if (type.includes('thought') || type.includes('reason'))
|
|
274
|
+
return text ? { type: 'reasoning-delta', text } : null;
|
|
275
|
+
if (type.includes('tool') || type.includes('command')) {
|
|
276
|
+
const tool = isToolRecord(update.toolCall) ? update.toolCall : isToolRecord(update.tool_call) ? update.tool_call : update;
|
|
277
|
+
const name = firstToolText(tool.name, tool.toolName, tool.tool_name, tool.title, update.name, update.toolName, update.title);
|
|
278
|
+
const callId = firstToolText(tool.callId, tool.call_id, tool.toolCallId, tool.tool_call_id, tool.id, update.toolCallId, update.tool_call_id, update.id);
|
|
279
|
+
if (!name && !callId)
|
|
280
|
+
return null;
|
|
281
|
+
const input = serializeToolValue(tool.rawInput ?? tool.input ?? tool.arguments ?? tool.args ?? update.rawInput);
|
|
282
|
+
const output = serializeToolValue(tool.rawOutput ?? tool.output ?? tool.result ?? update.rawOutput);
|
|
283
|
+
const error = serializeToolValue(tool.error ?? update.error);
|
|
284
|
+
const agentId = firstToolText(tool.agentId, tool.agent_id, update.agentId, update.agent_id);
|
|
285
|
+
const detail = serializeToolValue(tool.detail ?? update.detail ?? update.content);
|
|
286
|
+
const fallback = error !== undefined || type.includes('error') || type.includes('fail')
|
|
287
|
+
? 'failed'
|
|
288
|
+
: output !== undefined || type.includes('result') || type.includes('complete')
|
|
289
|
+
? 'completed'
|
|
290
|
+
: 'running';
|
|
291
|
+
return {
|
|
292
|
+
type: 'tool-event',
|
|
293
|
+
toolName: name ?? 'tool',
|
|
294
|
+
status: normalizeToolStatus(tool.status ?? tool.state ?? update.status, fallback),
|
|
295
|
+
...(callId ? { callId } : {}),
|
|
296
|
+
...(input !== undefined ? { input } : {}),
|
|
297
|
+
...(output !== undefined ? { output } : {}),
|
|
298
|
+
...(output !== undefined ? { outputMode: type.toLowerCase().includes('delta') ? 'delta' : 'snapshot' } : {}),
|
|
299
|
+
...(error !== undefined ? { error } : {}),
|
|
300
|
+
...(agentId ? { agentId } : {}),
|
|
301
|
+
...(detail !== undefined ? { detail } : {}),
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
return usageChunk(update);
|
|
305
|
+
}
|
|
306
|
+
function permissionRequestId(message) {
|
|
307
|
+
const params = isRecord(message.params) ? message.params : message;
|
|
308
|
+
const update = isRecord(params.update) ? params.update : params;
|
|
309
|
+
const type = typeof update.sessionUpdate === 'string' ? update.sessionUpdate : typeof update.type === 'string' ? update.type : typeof message.method === 'string' ? message.method : '';
|
|
310
|
+
if (!type.toLowerCase().includes('permission'))
|
|
311
|
+
return null;
|
|
312
|
+
const id = message.id ?? update.requestId ?? update.id;
|
|
313
|
+
return typeof id === 'string' || typeof id === 'number' ? String(id) : null;
|
|
314
|
+
}
|
|
315
|
+
function interactionRequestId(message) {
|
|
316
|
+
return permissionRequestId(message);
|
|
317
|
+
}
|
|
318
|
+
function readSessionId(value) {
|
|
319
|
+
if (!isRecord(value))
|
|
320
|
+
return null;
|
|
321
|
+
if (typeof value.sessionId === 'string')
|
|
322
|
+
return value.sessionId;
|
|
323
|
+
if (isRecord(value.session) && typeof value.session.id === 'string')
|
|
324
|
+
return value.session.id;
|
|
325
|
+
return typeof value.id === 'string' ? value.id : null;
|
|
326
|
+
}
|
|
327
|
+
function parseGrokCatalog(value) {
|
|
328
|
+
if (!isRecord(value))
|
|
329
|
+
return emptyCatalog();
|
|
330
|
+
const models = isRecord(value.models) && Array.isArray(value.models.availableModels)
|
|
331
|
+
? value.models.availableModels
|
|
332
|
+
: [];
|
|
333
|
+
const configOptions = Array.isArray(value.configOptions) ? value.configOptions : [];
|
|
334
|
+
const efforts = new Map();
|
|
335
|
+
for (const option of configOptions) {
|
|
336
|
+
if (!isRecord(option))
|
|
337
|
+
continue;
|
|
338
|
+
const id = typeof option.id === 'string' ? option.id : '';
|
|
339
|
+
if (!/model|reasoning/iu.test(id))
|
|
340
|
+
continue;
|
|
341
|
+
const values = Array.isArray(option.options) ? option.options : Array.isArray(option.values) ? option.values : [];
|
|
342
|
+
if (!/reasoning/iu.test(id))
|
|
343
|
+
continue;
|
|
344
|
+
for (const value of values) {
|
|
345
|
+
const effort = typeof value === 'string' ? value : isRecord(value) && typeof value.id === 'string' ? value.id : null;
|
|
346
|
+
if (!effort)
|
|
347
|
+
continue;
|
|
348
|
+
const modelId = typeof option.modelId === 'string' ? option.modelId : '*';
|
|
349
|
+
const current = efforts.get(modelId) ?? [];
|
|
350
|
+
if (!current.includes(effort))
|
|
351
|
+
current.push(effort);
|
|
352
|
+
efforts.set(modelId, current);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
const items = models.flatMap((entry) => {
|
|
356
|
+
if (typeof entry === 'string')
|
|
357
|
+
return [{ id: entry, name: entry, efforts: efforts.get(entry) ?? [] }];
|
|
358
|
+
if (!isRecord(entry))
|
|
359
|
+
return [];
|
|
360
|
+
const id = typeof entry.modelId === 'string' ? entry.modelId : typeof entry.id === 'string' ? entry.id : null;
|
|
361
|
+
if (!id)
|
|
362
|
+
return [];
|
|
363
|
+
const meta = isRecord(entry._meta) ? entry._meta : {};
|
|
364
|
+
const declared = Array.isArray(meta.reasoningEfforts) ? meta.reasoningEfforts.flatMap((item) => typeof item === 'string' ? [item] : isRecord(item) && typeof item.id === 'string' ? [item.id] : []) : [];
|
|
365
|
+
return [{ id, name: typeof entry.name === 'string' ? entry.name : id, efforts: declared.length > 0 ? declared : efforts.get(id) ?? efforts.get('*') ?? [] }];
|
|
366
|
+
});
|
|
367
|
+
if (items.length === 0)
|
|
368
|
+
return emptyCatalog();
|
|
369
|
+
const deduped = [...new Map(items.map((item) => [item.id, item])).values()];
|
|
370
|
+
return { groups: [{ id: 'grok', name: 'Grok', models: deduped }], currentModel: null, currentEffort: null };
|
|
371
|
+
}
|
|
372
|
+
//# sourceMappingURL=grok-driver.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface HttpSseClientOptions {
|
|
2
|
+
readonly fetch?: typeof fetch;
|
|
3
|
+
}
|
|
4
|
+
export interface HttpJsonResponse<T = unknown> {
|
|
5
|
+
readonly status: number;
|
|
6
|
+
readonly headers: Headers;
|
|
7
|
+
readonly data: T | null;
|
|
8
|
+
readonly text: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SseEvent {
|
|
11
|
+
readonly event: string | null;
|
|
12
|
+
readonly data: string;
|
|
13
|
+
readonly id?: string;
|
|
14
|
+
}
|
|
15
|
+
/** 只负责 HTTP JSON 与 SSE framing,不理解任何 Agent 私有事件。 */
|
|
16
|
+
export declare class HttpSseClient {
|
|
17
|
+
private readonly fetchImpl;
|
|
18
|
+
constructor(options?: HttpSseClientOptions);
|
|
19
|
+
json<T = unknown>(url: string, init?: RequestInit): Promise<HttpJsonResponse<T>>;
|
|
20
|
+
sse(url: string, init?: RequestInit): AsyncIterable<SseEvent>;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=http-sse-client.d.ts.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/** 只负责 HTTP JSON 与 SSE framing,不理解任何 Agent 私有事件。 */
|
|
2
|
+
export class HttpSseClient {
|
|
3
|
+
fetchImpl;
|
|
4
|
+
constructor(options = {}) {
|
|
5
|
+
this.fetchImpl = options.fetch ?? fetch;
|
|
6
|
+
}
|
|
7
|
+
async json(url, init = {}) {
|
|
8
|
+
const headers = new Headers(init.headers);
|
|
9
|
+
headers.set('accept', 'application/json');
|
|
10
|
+
const response = await this.fetchImpl(url, {
|
|
11
|
+
...init,
|
|
12
|
+
headers,
|
|
13
|
+
});
|
|
14
|
+
const text = await response.text();
|
|
15
|
+
let data = null;
|
|
16
|
+
if (text.trim()) {
|
|
17
|
+
try {
|
|
18
|
+
data = JSON.parse(text);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
data = null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return { status: response.status, headers: response.headers, data, text };
|
|
25
|
+
}
|
|
26
|
+
async *sse(url, init = {}) {
|
|
27
|
+
const headers = new Headers(init.headers);
|
|
28
|
+
headers.set('accept', 'text/event-stream');
|
|
29
|
+
const response = await this.fetchImpl(url, {
|
|
30
|
+
...init,
|
|
31
|
+
headers,
|
|
32
|
+
});
|
|
33
|
+
if (!response.ok)
|
|
34
|
+
throw new Error(`HTTP ${response.status} ${response.statusText}`);
|
|
35
|
+
if (!response.body)
|
|
36
|
+
return;
|
|
37
|
+
const reader = response.body.getReader();
|
|
38
|
+
const decoder = new TextDecoder();
|
|
39
|
+
let buffer = '';
|
|
40
|
+
let eventName = null;
|
|
41
|
+
let eventId;
|
|
42
|
+
let dataLines = [];
|
|
43
|
+
const flush = () => {
|
|
44
|
+
if (dataLines.length === 0 && eventName === null && eventId === undefined)
|
|
45
|
+
return null;
|
|
46
|
+
const event = { event: eventName, data: dataLines.join('\n'), ...(eventId === undefined ? {} : { id: eventId }) };
|
|
47
|
+
eventName = null;
|
|
48
|
+
eventId = undefined;
|
|
49
|
+
dataLines = [];
|
|
50
|
+
return event;
|
|
51
|
+
};
|
|
52
|
+
try {
|
|
53
|
+
while (true) {
|
|
54
|
+
const result = await reader.read();
|
|
55
|
+
if (result.done)
|
|
56
|
+
break;
|
|
57
|
+
buffer += decoder.decode(result.value, { stream: true }).replace(/\r\n/gu, '\n').replace(/\r/gu, '\n');
|
|
58
|
+
let newline = buffer.indexOf('\n');
|
|
59
|
+
while (newline >= 0) {
|
|
60
|
+
const line = buffer.slice(0, newline);
|
|
61
|
+
buffer = buffer.slice(newline + 1);
|
|
62
|
+
if (line === '') {
|
|
63
|
+
const event = flush();
|
|
64
|
+
if (event !== null)
|
|
65
|
+
yield event;
|
|
66
|
+
}
|
|
67
|
+
else if (line.startsWith(':')) {
|
|
68
|
+
// SSE 注释通常只是心跳。
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
const separator = line.indexOf(':');
|
|
72
|
+
const field = separator < 0 ? line : line.slice(0, separator);
|
|
73
|
+
const value = separator < 0 ? '' : line.slice(separator + 1).replace(/^ /u, '');
|
|
74
|
+
if (field === 'event')
|
|
75
|
+
eventName = value;
|
|
76
|
+
else if (field === 'id')
|
|
77
|
+
eventId = value;
|
|
78
|
+
else if (field === 'data')
|
|
79
|
+
dataLines.push(value);
|
|
80
|
+
}
|
|
81
|
+
newline = buffer.indexOf('\n');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
buffer += decoder.decode();
|
|
85
|
+
if (buffer.trim())
|
|
86
|
+
dataLines.push(buffer);
|
|
87
|
+
const event = flush();
|
|
88
|
+
if (event !== null)
|
|
89
|
+
yield event;
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
try {
|
|
93
|
+
await reader.cancel();
|
|
94
|
+
}
|
|
95
|
+
catch { /* 连接可能已经关闭 */ }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=http-sse-client.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CodingNsAgentQuestion, CodingNsAgentQuestionResponse } from '../../shared/contracts/cli-adapter.js';
|
|
2
|
+
/** 从不同 Provider 的问题结构中提取公共问题列表。 */
|
|
3
|
+
export declare function readAgentQuestions(value: unknown): readonly CodingNsAgentQuestion[];
|
|
4
|
+
/** 把公共回答转换成 Provider 常用的 id -> answers 结构。 */
|
|
5
|
+
export declare function questionAnswersRecord(response: CodingNsAgentQuestionResponse): Record<string, {
|
|
6
|
+
answers: string[];
|
|
7
|
+
}>;
|
|
8
|
+
/** 把公共回答转换成 ACP/OpenCode 常用的二维字符串数组。 */
|
|
9
|
+
export declare function questionAnswersList(response: CodingNsAgentQuestionResponse): string[][];
|
|
10
|
+
export declare function isQuestionEvent(type: string): boolean;
|
|
11
|
+
//# sourceMappingURL=interaction-events.d.ts.map
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/** 从不同 Provider 的问题结构中提取公共问题列表。 */
|
|
2
|
+
export function readAgentQuestions(value) {
|
|
3
|
+
const record = isRecord(value) ? value : null;
|
|
4
|
+
const source = Array.isArray(value)
|
|
5
|
+
? value
|
|
6
|
+
: Array.isArray(record?.questions)
|
|
7
|
+
? record.questions
|
|
8
|
+
: Array.isArray(record?.items)
|
|
9
|
+
? record.items
|
|
10
|
+
: [];
|
|
11
|
+
return source.flatMap((item, index) => {
|
|
12
|
+
const question = readQuestion(item, index);
|
|
13
|
+
return question === null ? [] : [question];
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
/** 把公共回答转换成 Provider 常用的 id -> answers 结构。 */
|
|
17
|
+
export function questionAnswersRecord(response) {
|
|
18
|
+
return Object.fromEntries(response.answers.map((answer) => [
|
|
19
|
+
answer.id,
|
|
20
|
+
{ answers: [...answer.selected, ...(answer.custom?.trim() ? [answer.custom.trim()] : [])] },
|
|
21
|
+
]));
|
|
22
|
+
}
|
|
23
|
+
/** 把公共回答转换成 ACP/OpenCode 常用的二维字符串数组。 */
|
|
24
|
+
export function questionAnswersList(response) {
|
|
25
|
+
return response.answers.map((answer) => [
|
|
26
|
+
...answer.selected,
|
|
27
|
+
...(answer.custom?.trim() ? [answer.custom.trim()] : []),
|
|
28
|
+
]);
|
|
29
|
+
}
|
|
30
|
+
export function isQuestionEvent(type) {
|
|
31
|
+
const normalized = type
|
|
32
|
+
.replace(/([a-z0-9])([A-Z])/gu, '$1_$2')
|
|
33
|
+
.toLowerCase()
|
|
34
|
+
.replace(/[./-]+/gu, '_');
|
|
35
|
+
return normalized.includes('question') || normalized.includes('request_user_input') || normalized.includes('request_input') || normalized.includes('ask_user');
|
|
36
|
+
}
|
|
37
|
+
function readQuestion(value, index) {
|
|
38
|
+
const record = isRecord(value) ? value : null;
|
|
39
|
+
const text = typeof value === 'string'
|
|
40
|
+
? value
|
|
41
|
+
: firstString(record, ['question', 'prompt', 'message', 'text']);
|
|
42
|
+
if (text === null || text.trim() === '')
|
|
43
|
+
return null;
|
|
44
|
+
const optionsSource = Array.isArray(record?.options) ? record.options : Array.isArray(record?.choices) ? record.choices : [];
|
|
45
|
+
const options = optionsSource.flatMap((option) => {
|
|
46
|
+
if (typeof option === 'string' && option.trim())
|
|
47
|
+
return [{ label: option.trim() }];
|
|
48
|
+
if (!isRecord(option))
|
|
49
|
+
return [];
|
|
50
|
+
const label = firstString(option, ['label', 'value', 'name', 'title']);
|
|
51
|
+
if (label === null || label.trim() === '')
|
|
52
|
+
return [];
|
|
53
|
+
const description = firstString(option, ['description', 'detail']);
|
|
54
|
+
return [{ label: label.trim(), ...(description?.trim() ? { description: description.trim() } : {}) }];
|
|
55
|
+
});
|
|
56
|
+
const id = firstString(record, ['id', 'questionId', 'question_id'])?.trim() || `question-${index + 1}`;
|
|
57
|
+
const detail = firstString(record, ['detail', 'description']);
|
|
58
|
+
const header = firstString(record, ['header', 'title']);
|
|
59
|
+
const multiSelect = record?.multiSelect === true || record?.multi_select === true || record?.multiple === true;
|
|
60
|
+
return {
|
|
61
|
+
id,
|
|
62
|
+
question: text.trim(),
|
|
63
|
+
...(detail?.trim() ? { detail: detail.trim() } : {}),
|
|
64
|
+
...(header?.trim() ? { header: header.trim() } : {}),
|
|
65
|
+
...(options.length > 0 ? { options } : {}),
|
|
66
|
+
...(multiSelect ? { multiSelect: true } : {}),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function firstString(record, keys) {
|
|
70
|
+
if (record === null)
|
|
71
|
+
return null;
|
|
72
|
+
for (const key of keys)
|
|
73
|
+
if (typeof record[key] === 'string')
|
|
74
|
+
return record[key];
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
function isRecord(value) {
|
|
78
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=interaction-events.js.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
/** JSON-RPC 消息的最小形状。不同 Agent 的扩展字段保持在 unknown 中。 */
|
|
3
|
+
export interface JsonRpcMessage {
|
|
4
|
+
readonly jsonrpc?: string;
|
|
5
|
+
readonly id?: number | string | null;
|
|
6
|
+
readonly method?: string;
|
|
7
|
+
readonly params?: unknown;
|
|
8
|
+
readonly result?: unknown;
|
|
9
|
+
readonly error?: {
|
|
10
|
+
readonly code?: number;
|
|
11
|
+
readonly message?: string;
|
|
12
|
+
readonly data?: unknown;
|
|
13
|
+
};
|
|
14
|
+
readonly [key: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
export interface JsonRpcProcessOptions {
|
|
17
|
+
readonly command: string;
|
|
18
|
+
readonly args?: readonly string[];
|
|
19
|
+
readonly cwd?: string | undefined;
|
|
20
|
+
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
21
|
+
readonly spawn?: typeof spawn;
|
|
22
|
+
}
|
|
23
|
+
export interface JsonRpcRequestOptions {
|
|
24
|
+
readonly signal?: AbortSignal | undefined;
|
|
25
|
+
readonly onNotification?: (message: JsonRpcMessage) => void;
|
|
26
|
+
/** 取消本次请求时是否同时终止整个 Agent 进程;长期会话应关闭此项。 */
|
|
27
|
+
readonly killOnAbort?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 统一管理 stdio JSON-RPC 子进程:请求编号、逐行解码、取消和退出清理都在这里完成。
|
|
31
|
+
* 凭据不会被记录,也不会进入错误文本。
|
|
32
|
+
*/
|
|
33
|
+
export declare class JsonRpcProcess {
|
|
34
|
+
private readonly options;
|
|
35
|
+
private readonly runSpawn;
|
|
36
|
+
private child;
|
|
37
|
+
private nextId;
|
|
38
|
+
private readonly pending;
|
|
39
|
+
private closed;
|
|
40
|
+
private lineLoop;
|
|
41
|
+
private exitPromise;
|
|
42
|
+
private readonly notificationHandlers;
|
|
43
|
+
private serverRequestHandler;
|
|
44
|
+
constructor(options: JsonRpcProcessOptions);
|
|
45
|
+
request(method: string, params?: unknown, options?: JsonRpcRequestOptions): Promise<unknown>;
|
|
46
|
+
/** 发送无需响应的 JSON-RPC 通知,例如 initialized 或 cancel。 */
|
|
47
|
+
notify(method: string, params?: unknown): void;
|
|
48
|
+
/** 注册 Agent 发起的 JSON-RPC 服务端请求处理器,例如 Codex 权限审批。 */
|
|
49
|
+
setServerRequestHandler(handler: ((message: JsonRpcMessage) => unknown | Promise<unknown>) | undefined): void;
|
|
50
|
+
/** 为一段流式请求注册独立监听器,响应先到时也不会提前移除。 */
|
|
51
|
+
addNotificationListener(listener: (message: JsonRpcMessage) => void): () => void;
|
|
52
|
+
/** 回复由 Agent 发起的 JSON-RPC server request,保留原始 id。 */
|
|
53
|
+
respond(id: number | string, result: unknown): void;
|
|
54
|
+
/** 回复 JSON-RPC server request 的错误结果。错误文本不回传 Provider 原文。 */
|
|
55
|
+
respondError(id: number | string, code?: number, message?: string): void;
|
|
56
|
+
cancel(id: number | string): void;
|
|
57
|
+
dispose(): void;
|
|
58
|
+
/** 销毁并等待子进程退出,避免下一轮恢复与上一轮残留进程交叉。 */
|
|
59
|
+
disposeAndWait(timeoutMs?: number): Promise<void>;
|
|
60
|
+
private ensureStarted;
|
|
61
|
+
private consumeLines;
|
|
62
|
+
private write;
|
|
63
|
+
private fail;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=json-rpc-process.d.ts.map
|