mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-07-09 17:19:02 +00:00
refactor(cli): Finish serve kebab-case filenames (#5604)
This commit is contained in:
parent
f29f210201
commit
a8863c203c
35 changed files with 88 additions and 111 deletions
|
|
@ -25,7 +25,7 @@ standard ACP method (model switch, workspace introspection, heartbeat,
|
|||
multi-client permission policy, SSE backpressure tuning). Rationale in §5.
|
||||
|
||||
A complete, locally-runnable reference implementation ships in this PR
|
||||
(`packages/cli/src/serve/acpHttp/`) plus a verification harness
|
||||
(`packages/cli/src/serve/acp-http/`) plus a verification harness
|
||||
(`scripts/acp-http-smoke.mjs`).
|
||||
|
||||
---
|
||||
|
|
@ -151,15 +151,15 @@ client ────────────────────────
|
|||
qwen --acp child
|
||||
```
|
||||
|
||||
### 3.1 New module layout (`packages/cli/src/serve/acpHttp/`)
|
||||
### 3.1 New module layout (`packages/cli/src/serve/acp-http/`)
|
||||
|
||||
| File | Responsibility |
|
||||
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `index.ts` | `mountAcpHttp(app, bridge, opts)` — registers `/acp` routes on the existing Express app. |
|
||||
| `connectionRegistry.ts` | `Acp-Connection-Id` → `AcpConnection` (connection SSE writer, `Map<sessionId, SessionStream>`, pending agent→client requests by JSON-RPC id, monotonic id allocator). TTL + DELETE cleanup. |
|
||||
| `jsonRpc.ts` | JSON-RPC 2.0 parse/validate/serialize helpers; error codes (`-32600` etc.); `_qwen/` namespace guard. |
|
||||
| `dispatch.ts` | Maps inbound JSON-RPC methods → `HttpAcpBridge` calls. Maps `BridgeEvent`s → outbound JSON-RPC frames. The translation table (§4). |
|
||||
| `sseStream.ts` | Long-lived SSE writer (reuses the backpressure/heartbeat pattern from `server.ts`). Distinct from REST `/events` (different framing: full JSON-RPC objects, not qwen event envelopes). |
|
||||
| File | Responsibility |
|
||||
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `index.ts` | `mountAcpHttp(app, bridge, opts)` — registers `/acp` routes on the existing Express app. |
|
||||
| `connection-registry.ts` | `Acp-Connection-Id` → `AcpConnection` (connection SSE writer, `Map<sessionId, SessionStream>`, pending agent→client requests by JSON-RPC id, monotonic id allocator). TTL + DELETE cleanup. |
|
||||
| `json-rpc.ts` | JSON-RPC 2.0 parse/validate/serialize helpers; error codes (`-32600` etc.); `_qwen/` namespace guard. |
|
||||
| `dispatch.ts` | Maps inbound JSON-RPC methods → `HttpAcpBridge` calls. Maps `BridgeEvent`s → outbound JSON-RPC frames. The translation table (§4). |
|
||||
| `sse-stream.ts` | Long-lived SSE writer (reuses the backpressure/heartbeat pattern from `server.ts`). Distinct from REST `/events` (different framing: full JSON-RPC objects, not qwen event envelopes). |
|
||||
|
||||
No change to `bridge.ts` / `eventBus.ts` (additive consumer only).
|
||||
|
||||
|
|
@ -284,7 +284,7 @@ thin compat shim over `/acp` (separate, later PR).
|
|||
- `session/request_permission` agent→client round-trip.
|
||||
- `_qwen/session/set_model` extension as the worked example of #2.
|
||||
- Bearer-auth + host allowlist reuse (same middleware as REST).
|
||||
- Unit tests (`acpHttp/*.test.ts`) + a black-box smoke script driving a real daemon.
|
||||
- Unit tests (`acp-http/*.test.ts`) + a black-box smoke script driving a real daemon.
|
||||
|
||||
**Deferred (documented, not built now):**
|
||||
|
||||
|
|
@ -309,7 +309,7 @@ thin compat shim over `/acp` (separate, later PR).
|
|||
- Trigger a tool needing permission → assert `session/request_permission` request,
|
||||
POST a grant response → assert prompt completes.
|
||||
- `POST {_qwen/session/set_model}` → assert model switch + `session/update`.
|
||||
4. Vitest: `acpHttp/*.test.ts` green.
|
||||
4. Vitest: `acp-http/*.test.ts` green.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -326,11 +326,11 @@ thin compat shim over `/acp` (separate, later PR).
|
|||
|
||||
## 10. Implementation & verification log (v1)
|
||||
|
||||
Implemented in `packages/cli/src/serve/acpHttp/` (`jsonRpc.ts`, `sseStream.ts`,
|
||||
`connectionRegistry.ts`, `dispatch.ts`, `index.ts`), mounted from `server.ts`
|
||||
Implemented in `packages/cli/src/serve/acp-http/` (`json-rpc.ts`, `sse-stream.ts`,
|
||||
`connection-registry.ts`, `dispatch.ts`, `index.ts`), mounted from `server.ts`
|
||||
via `mountAcpHttp(app, bridge, { boundWorkspace })`.
|
||||
|
||||
### Automated (`packages/cli/src/serve/acpHttp/*.test.ts`)
|
||||
### Automated (`packages/cli/src/serve/acp-http/*.test.ts`)
|
||||
|
||||
`transport.test.ts` boots a real Express server + the real `mountAcpHttp` over
|
||||
a controllable fake bridge and drives it with `fetch` + manual SSE parsing.
|
||||
|
|
@ -496,8 +496,8 @@ Another reviewer pass (qwen3.7-max). Suite **30 tests**, live re-verified.
|
|||
| F3 | **P2** | 503 connection-cap rejection had no stderr log. | `writeStderrLine` with the cap value. |
|
||||
| F4 | **P2** | `_qwen/notify stream_error` spread let `event.data.kind` shadow the discriminator. | Spread first, then `kind: 'stream_error'`. |
|
||||
| F5 | **P2** | `MAX_WORKSPACE_PATH_LENGTH` redeclared (`= 4096`) vs the canonical `fs/paths.js`. | Import from `../fs/paths.js` (no divergence). |
|
||||
| F6 | **P2** | `isObjectParams` duplicated `jsonRpc.isObject`. | Import `isObject`. |
|
||||
| F7 | **P2** | Raw `process.stderr.write` in `index.ts`/`sseStream.ts` vs `writeStderrLine` elsewhere. | Unified on `writeStderrLine` across the module. |
|
||||
| F6 | **P2** | `isObjectParams` duplicated `json-rpc.isObject`. | Import `isObject`. |
|
||||
| F7 | **P2** | Raw `process.stderr.write` in `index.ts`/`sse-stream.ts` vs `writeStderrLine` elsewhere. | Unified on `writeStderrLine` across the module. |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -37,19 +37,19 @@
|
|||
|
||||
**Subsystems**:
|
||||
|
||||
| Path | Role |
|
||||
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `serve/fs/` | `WorkspaceFileSystem` factory plus `policy.ts` (size/trust/binary checks), `paths.ts` (canonicalize, resolveWithin, symlink rejection), `audit.ts`, and typed `FsError` values. |
|
||||
| Path | Role |
|
||||
| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `serve/fs/` | `WorkspaceFileSystem` factory plus `policy.ts` (size/trust/binary checks), `paths.ts` (canonicalize, resolveWithin, symlink rejection), `audit.ts`, and typed `FsError` values. |
|
||||
| `serve/routes/workspace-file-read.ts`, `workspace-file-write.ts` | HTTP handlers for `GET /file`, `GET /file/bytes`, `POST /file/write`, and `POST /file/edit`. |
|
||||
| `serve/workspace-memory.ts` | `GET/POST /workspace/memory` (QWEN.md CRUD). |
|
||||
| `serve/workspace-agents.ts` | `GET/POST/DELETE /workspace/agents` (subagent CRUD). |
|
||||
| `serve/daemon-status-provider.ts` | Env snapshot plus daemon-host preflight cells: Node version, CLI entry, workspace stat, ripgrep, git, npm. |
|
||||
| `serve/permission-audit.ts` | `PermissionAuditRing` (512-entry FIFO) and `createPermissionAuditPublisher`. |
|
||||
| `serve/workspace-memory.ts` | `GET/POST /workspace/memory` (QWEN.md CRUD). |
|
||||
| `serve/workspace-agents.ts` | `GET/POST/DELETE /workspace/agents` (subagent CRUD). |
|
||||
| `serve/daemon-status-provider.ts` | Env snapshot plus daemon-host preflight cells: Node version, CLI entry, workspace stat, ripgrep, git, npm. |
|
||||
| `serve/permission-audit.ts` | `PermissionAuditRing` (512-entry FIFO) and `createPermissionAuditPublisher`. |
|
||||
| `serve/auth/device-flow.ts`, `qwen-device-flow-provider.ts` | Device-flow OAuth routes. See [`12-auth-security.md`](./12-auth-security.md). |
|
||||
| `serve/daemonLogger.ts` | `DaemonLogger` structured file logs. See [`19-observability.md`](./19-observability.md). |
|
||||
| `serve/debug-mode.ts` | Shared `isServeDebugMode()` predicate controlling verbose error context in HTTP responses. |
|
||||
| `serve/acpHttp/` | ACP Streamable HTTP transport (RFD #721), mounted at `/acp`. Seven files implement JSON-RPC POST, SSE GET, DELETE teardown, and shared bridge usage in parallel with the REST surface. |
|
||||
| `serve/demo.ts` | Self-contained inline HTML for `GET /demo`: browser debug console with chat UI, event log, and workspace inspector. On loopback without `--require-auth`, it is registered **before** `bearerAuth`; on non-loopback or with `--require-auth`, it is registered **after** `bearerAuth`. Served with CSP `default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; connect-src 'self'; frame-ancestors 'none'` plus `X-Frame-Options: DENY`. |
|
||||
| `serve/daemon-logger.ts` | `DaemonLogger` structured file logs. See [`19-observability.md`](./19-observability.md). |
|
||||
| `serve/debug-mode.ts` | Shared `isServeDebugMode()` predicate controlling verbose error context in HTTP responses. |
|
||||
| `serve/acp-http/` | ACP Streamable HTTP transport (RFD #721), mounted at `/acp`. Seven files implement JSON-RPC POST, SSE GET, DELETE teardown, and shared bridge usage in parallel with the REST surface. |
|
||||
| `serve/demo.ts` | Self-contained inline HTML for `GET /demo`: browser debug console with chat UI, event log, and workspace inspector. On loopback without `--require-auth`, it is registered **before** `bearerAuth`; on non-loopback or with `--require-auth`, it is registered **after** `bearerAuth`. Served with CSP `default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; connect-src 'self'; frame-ancestors 'none'` plus `X-Frame-Options: DENY`. |
|
||||
|
||||
**Re-export shims** for compatibility with pre-F1 import paths:
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
| ------------------------------------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `QWEN_SERVE_DEBUG` stderr logs | `bridge.ts` and call sites | Env values `1` / `true` / `on` / `yes` (case-insensitive) print `qwen serve debug: ...` lines to stderr. |
|
||||
| OpenTelemetry span instrumentation | `server.ts` `daemonTelemetryMiddleware` | Each HTTP request is wrapped in `withDaemonRequestSpan`; attributes include route, sessionId, clientId, and status code. Permission routes have dedicated spans. Prompt lifecycle is traced end-to-end. Configuration lives in `settings.json` `telemetry`. |
|
||||
| `DaemonLogger` structured file logs | `serve/daemonLogger.ts` | Structured JSON-like log lines are written to a file. Boot prints `daemon log -> <path>`. Supports `info` / `warn` / `error` levels, with structured fields such as `route`, `sessionId`, `clientId`, `childPid`, and `channelId`. |
|
||||
| `DaemonLogger` structured file logs | `serve/daemon-logger.ts` | Structured JSON-like log lines are written to a file. Boot prints `daemon log -> <path>`. Supports `info` / `warn` / `error` levels, with structured fields such as `route`, `sessionId`, `clientId`, `childPid`, and `channelId`. |
|
||||
| Per-request access-log middleware | `server.ts`, registered before `bearerAuth` | Logs `method`, `path`, `status`, `durationMs`, `sessionId`, and `clientId` after each request. Skips `GET /health` and heartbeat. 4xx+ uses `warn`; success uses `info`. |
|
||||
| `/health` | `server.ts` route | Liveness probe; `?deep=1` returns extended details. |
|
||||
| `/capabilities` | `server.ts` route | Preflight feature discovery. See [`11-capabilities-versioning.md`](./11-capabilities-versioning.md). |
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
| `/workspace/skills`, `/workspace/providers` | Routes | ACP-side live snapshots; return empty idle data when no session exists. |
|
||||
| Per-session SSE | `GET /session/:id/events` | Real-time event stream. |
|
||||
| `/demo` debug console | `GET /demo` (`packages/cli/src/serve/demo.ts`) | Browser-accessible single-page console: chat, event log, workspace inspector, and permission UX. On loopback, `http://127.0.0.1:4170/demo` is the quickest end-to-end validation path without writing SDK code. Registration rules are in [`02-serve-runtime.md`](./02-serve-runtime.md). |
|
||||
| `PermissionAuditRing` | `permission-audit.ts` | In-memory FIFO of 512 permission decisions. |
|
||||
| `PermissionAuditRing` | `permission-audit.ts` | In-memory FIFO of 512 permission decisions. |
|
||||
| Mediator `decisionReason` audit | `permissionMediator.ts` | Internal structured record explaining why a permission request resolved the way it did. |
|
||||
|
||||
## What does not exist today
|
||||
|
|
@ -143,7 +143,7 @@ flowchart TD
|
|||
## References
|
||||
|
||||
- `packages/cli/src/serve/daemon-status-provider.ts`
|
||||
- `packages/cli/src/serve/daemonLogger.ts` (`DaemonLogger`, `buildDaemonLogLine`)
|
||||
- `packages/cli/src/serve/daemon-logger.ts` (`DaemonLogger`, `buildDaemonLogLine`)
|
||||
- `packages/cli/src/serve/debug-mode.ts` (`isServeDebugMode`)
|
||||
- `packages/acp-bridge/src/permissionMediator.ts` (`PermissionDecisionReason`)
|
||||
- `packages/cli/src/serve/server.ts` (`daemonTelemetryMiddleware`, access-log middleware)
|
||||
|
|
|
|||
|
|
@ -82,17 +82,13 @@ export const legacyFilenames = [
|
|||
'cronScheduler',
|
||||
'customBanner',
|
||||
'customMatchers',
|
||||
'daemonStatusProvider',
|
||||
'DaemonTuiAdapter',
|
||||
'dangerousRules',
|
||||
'DataProcessor',
|
||||
'debugLogger',
|
||||
'debugMode',
|
||||
'deepMerge',
|
||||
'deleteCommand',
|
||||
'denialTracking',
|
||||
'detectTodoChanges',
|
||||
'deviceFlow',
|
||||
'dialogScopeUtils',
|
||||
'diffCommand',
|
||||
'diffOptions',
|
||||
|
|
@ -109,7 +105,6 @@ export const legacyFilenames = [
|
|||
'editorSettingsManager',
|
||||
'envInterpolator',
|
||||
'environmentContext',
|
||||
'envSnapshot',
|
||||
'envVarResolver',
|
||||
'errorCodes',
|
||||
'errorHandler',
|
||||
|
|
@ -205,7 +200,6 @@ export const legacyFilenames = [
|
|||
'LlmRewriter',
|
||||
'loadedSettingsAdapter',
|
||||
'loggingContentGenerator',
|
||||
'loopbackBinds',
|
||||
'loopDetectionService',
|
||||
'lowlightLoader',
|
||||
'LruCache',
|
||||
|
|
@ -283,7 +277,6 @@ export const legacyFilenames = [
|
|||
'quitCommand',
|
||||
'quotaErrorDetection',
|
||||
'qwenContentGenerator',
|
||||
'qwenDeviceFlowProvider',
|
||||
'qwenIgnoreParser',
|
||||
'qwenOAuth2',
|
||||
'rateLimit',
|
||||
|
|
@ -309,7 +302,6 @@ export const legacyFilenames = [
|
|||
'ripgrepUtils',
|
||||
'rulesDiscovery',
|
||||
'runBudget',
|
||||
'runQwenServe',
|
||||
'runtimeDiagnostics',
|
||||
'runtimeFetchOptions',
|
||||
'runtimeOutputDirContext',
|
||||
|
|
@ -511,12 +503,7 @@ export const legacyFilenames = [
|
|||
'warningHandler',
|
||||
'windowsPath',
|
||||
'windowTitle',
|
||||
'workspaceAgents',
|
||||
'workspaceContext',
|
||||
'workspaceFileRead',
|
||||
'workspaceFileSystem',
|
||||
'workspaceFileWrite',
|
||||
'workspaceMemory',
|
||||
'worktreeCleanup',
|
||||
'worktreeSessionService',
|
||||
'worktreeStartup',
|
||||
|
|
@ -525,10 +512,7 @@ export const legacyFilenames = [
|
|||
'CodeColorizer',
|
||||
'SKILL',
|
||||
'TeamManager',
|
||||
'a2uiAction',
|
||||
'acpSessionBridge',
|
||||
'asciiCharts',
|
||||
'bridgeFileSystemAdapter',
|
||||
'btwUtils',
|
||||
'cdCommand',
|
||||
'claudeMcpImport',
|
||||
|
|
@ -537,12 +521,9 @@ export const legacyFilenames = [
|
|||
'compressFastCommand',
|
||||
'concurrencyLimiter',
|
||||
'configHash',
|
||||
'connectionRegistry',
|
||||
'corruptFile',
|
||||
'cronTasksFile',
|
||||
'cronTasksLock',
|
||||
'daemonLogger',
|
||||
'daemonStatus',
|
||||
'enterPlanMode',
|
||||
'extensionPreferences',
|
||||
'forkCommand',
|
||||
|
|
@ -555,7 +536,6 @@ export const legacyFilenames = [
|
|||
'importConfigCommand',
|
||||
'inlineMediaLimit',
|
||||
'instructionsLoadedCallback',
|
||||
'jsonRpc',
|
||||
'leaderPermissionBridge',
|
||||
'liveAgentPanelVisibility',
|
||||
'mcpApprovals',
|
||||
|
|
@ -564,7 +544,6 @@ export const legacyFilenames = [
|
|||
'mcpServers',
|
||||
'midTurnUserMessage',
|
||||
'normalizeDisabledTools',
|
||||
'permissionAudit',
|
||||
'planApprovalGate',
|
||||
'promptAddendum',
|
||||
'retryErrorClassification',
|
||||
|
|
@ -578,7 +557,6 @@ export const legacyFilenames = [
|
|||
'settingsWatcher',
|
||||
'sleepInhibitor',
|
||||
'sourceRegistry',
|
||||
'sseStream',
|
||||
'statsDataService',
|
||||
'tasksSnapshot',
|
||||
'teamHelpers',
|
||||
|
|
@ -587,15 +565,11 @@ export const legacyFilenames = [
|
|||
'toolCallIdUtils',
|
||||
'toolResultCleanup',
|
||||
'toolResultDisplayCompaction',
|
||||
'transportStream',
|
||||
'usageHistoryService',
|
||||
'useMcpApproval',
|
||||
'useResizeSettleRepaint',
|
||||
'useStatsDialog',
|
||||
'useTeamInProcess',
|
||||
'userMemory',
|
||||
'webShellStatic',
|
||||
'workflowsCommand',
|
||||
'workspaceSettings',
|
||||
'wsStream',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
*/
|
||||
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { ConnectionRegistry } from './connectionRegistry.js';
|
||||
import type { TransportStream } from './transportStream.js';
|
||||
import { ConnectionRegistry } from './connection-registry.js';
|
||||
import type { TransportStream } from './transport-stream.js';
|
||||
|
||||
class FakeStream implements TransportStream {
|
||||
isClosed = false;
|
||||
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import { writeStderrLine } from '../../utils/stdioHelpers.js';
|
||||
import { logSafe } from './jsonRpc.js';
|
||||
import type { TransportStream } from './transportStream.js';
|
||||
import { logSafe } from './json-rpc.js';
|
||||
import type { TransportStream } from './transport-stream.js';
|
||||
|
||||
/**
|
||||
* Per-stream cap on frames buffered before the client attaches its SSE
|
||||
|
|
@ -50,7 +50,7 @@ import type {
|
|||
DaemonWorkspaceService,
|
||||
WorkspaceRequestContext,
|
||||
} from '../workspace-service/types.js';
|
||||
import type { AcpConnection } from './connectionRegistry.js';
|
||||
import type { AcpConnection } from './connection-registry.js';
|
||||
import {
|
||||
QWEN_META_KEY,
|
||||
QWEN_METHOD_NS,
|
||||
|
|
@ -68,7 +68,7 @@ import {
|
|||
type JsonRpcInbound,
|
||||
type JsonRpcRequest,
|
||||
type JsonRpcResponse,
|
||||
} from './jsonRpc.js';
|
||||
} from './json-rpc.js';
|
||||
|
||||
function errMsg(err: unknown): string {
|
||||
return err instanceof Error ? err.message : String(err);
|
||||
|
|
@ -2697,5 +2697,5 @@ export class AcpDispatcher {
|
|||
}
|
||||
}
|
||||
|
||||
// Re-export so tests can reference the request type without the jsonRpc path.
|
||||
// Re-export so tests can reference the request type without the json-rpc path.
|
||||
export type { JsonRpcRequest };
|
||||
|
|
@ -18,11 +18,11 @@ import { AcpDispatcher } from './dispatch.js';
|
|||
import {
|
||||
ConnectionRegistry,
|
||||
type AcpConnection,
|
||||
} from './connectionRegistry.js';
|
||||
import { SseStream } from './sseStream.js';
|
||||
import { WsStream } from './wsStream.js';
|
||||
} from './connection-registry.js';
|
||||
import { SseStream } from './sse-stream.js';
|
||||
import { WsStream } from './ws-stream.js';
|
||||
import type { RateLimitTier } from '../rate-limit.js';
|
||||
import { RPC, error as rpcError, isRequest, parseInbound } from './jsonRpc.js';
|
||||
import { RPC, error as rpcError, isRequest, parseInbound } from './json-rpc.js';
|
||||
|
||||
export const ACP_CONNECTION_HEADER = 'acp-connection-id';
|
||||
export const ACP_SESSION_HEADER = 'acp-session-id';
|
||||
|
|
@ -362,7 +362,7 @@ export function mountAcpHttp(
|
|||
// stream. A reconnect already installed a newer stream — the prompt
|
||||
// must survive the old stream's close. CONTRACT: this identity guard
|
||||
// pairs with `attachSessionStream`'s install-before-close ordering
|
||||
// (connectionRegistry.ts) — keep both in lockstep.
|
||||
// (connection-registry.ts) — keep both in lockstep.
|
||||
if (conn.sessions.get(sessionId)?.stream === stream) {
|
||||
conn.sessions.get(sessionId)?.promptAbort?.abort();
|
||||
}
|
||||
|
|
@ -799,7 +799,7 @@ function headerOf(req: Request, name: string): string | undefined {
|
|||
* True when the request's KERNEL-stamped peer address is loopback. Mirrors
|
||||
* the REST surface's `detectFromLoopback` (NOT derived from forgeable
|
||||
* headers like `X-Forwarded-For`). Replicated here rather than imported
|
||||
* from `server.ts` to avoid a server↔acpHttp import cycle.
|
||||
* from `server.ts` to avoid a server↔acp-http import cycle.
|
||||
*/
|
||||
function isLoopbackSocket(socket: Duplex): boolean {
|
||||
const addr = (socket as unknown as { remoteAddress?: string }).remoteAddress;
|
||||
|
|
@ -12,9 +12,9 @@ import {
|
|||
parseInbound,
|
||||
QWEN_METHOD_NS,
|
||||
RPC,
|
||||
} from './jsonRpc.js';
|
||||
} from './json-rpc.js';
|
||||
|
||||
describe('jsonRpc helpers', () => {
|
||||
describe('json-rpc helpers', () => {
|
||||
it('classifies a request', () => {
|
||||
const m = { jsonrpc: '2.0', id: 1, method: 'initialize' };
|
||||
expect(isRequest(m)).toBe(true);
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
/**
|
||||
* Minimal JSON-RPC 2.0 helpers for the ACP-over-HTTP transport
|
||||
* (`packages/cli/src/serve/acpHttp/`). The official ACP Streamable HTTP
|
||||
* (`packages/cli/src/serve/acp-http/`). The official ACP Streamable HTTP
|
||||
* transport (RFD #721) frames every message as a JSON-RPC 2.0 object;
|
||||
* this module owns the wire types + parse/validate/serialize so the
|
||||
* dispatcher stays focused on bridge routing.
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
import { EventEmitter } from 'node:events';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import type { Response } from 'express';
|
||||
import { SseStream } from './sseStream.js';
|
||||
import { SseStream } from './sse-stream.js';
|
||||
|
||||
/**
|
||||
* Minimal Express `Response` mock: an EventEmitter with the `write`/`end`/
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { EventEmitter } from 'node:events';
|
||||
import { WsStream } from './wsStream.js';
|
||||
import { WsStream } from './ws-stream.js';
|
||||
|
||||
// Minimal WebSocket mock that implements the surface WsStream uses.
|
||||
class MockWebSocket extends EventEmitter {
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import type { WebSocket } from 'ws';
|
||||
import { writeStderrLine } from '../../utils/stdioHelpers.js';
|
||||
import type { TransportStream } from './transportStream.js';
|
||||
import type { TransportStream } from './transport-stream.js';
|
||||
|
||||
export class WsStream implements TransportStream {
|
||||
readonly kind = 'ws' as const;
|
||||
|
|
@ -10,10 +10,10 @@
|
|||
* #4175 PR F1 lifted the bridge core (`BridgeClient`,
|
||||
* `defaultSpawnChannelFactory`, `createAcpSessionBridge` factory closure,
|
||||
* plus the supporting types/errors/options/status) to
|
||||
* `@qwen-code/acp-bridge`. This shim preserves every existing relative
|
||||
* import path (`./acpSessionBridge.js`) so `server.ts`, `run-qwen-serve.ts`,
|
||||
* `workspace-agents.ts`, `workspace-memory.ts`, `index.ts`, plus the
|
||||
* bridge test suite, keep resolving without any call-site changes.
|
||||
* `@qwen-code/acp-bridge`. This shim preserves the CLI-local bridge import
|
||||
* surface so `server.ts`, `run-qwen-serve.ts`, `workspace-agents.ts`,
|
||||
* `workspace-memory.ts`, `index.ts`, plus the bridge test suite, keep resolving
|
||||
* through one module.
|
||||
*
|
||||
* The implementation now lives at:
|
||||
* - `@qwen-code/acp-bridge/bridge` — `createAcpSessionBridge` factory
|
||||
|
|
@ -16,7 +16,7 @@ import {
|
|||
lstatSync,
|
||||
} from 'node:fs';
|
||||
import { describe, it, expect, afterEach, beforeEach } from 'vitest';
|
||||
import { buildDaemonLogLine, initDaemonLogger } from './daemonLogger.js';
|
||||
import { buildDaemonLogLine, initDaemonLogger } from './daemon-logger.js';
|
||||
|
||||
describe('buildDaemonLogLine', () => {
|
||||
const FIXED = new Date('2026-05-26T03:14:15.926Z');
|
||||
|
|
@ -6,16 +6,16 @@
|
|||
|
||||
import type { RequestHandler } from 'express';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import type { AcpHttpHandle } from './acpHttp/index.js';
|
||||
import type { AcpHttpHandle } from './acp-http/index.js';
|
||||
import type {
|
||||
AcpSessionBridge,
|
||||
BridgeDaemonStatusSnapshot,
|
||||
} from './acpSessionBridge.js';
|
||||
} from './acp-session-bridge.js';
|
||||
import { DeviceFlowRegistry } from './auth/device-flow.js';
|
||||
import {
|
||||
buildDaemonStatusResponse,
|
||||
type BuildDaemonStatusOptions,
|
||||
} from './daemonStatus.js';
|
||||
} from './daemon-status.js';
|
||||
import type { RateLimiterInstance, RateLimitTier } from './rate-limit.js';
|
||||
import type { DaemonWorkspaceService } from './workspace-service/index.js';
|
||||
|
||||
|
|
@ -5,13 +5,13 @@
|
|||
*/
|
||||
|
||||
import type { ServeProtocolVersions } from './capabilities.js';
|
||||
import type { AcpHttpHandle } from './acpHttp/index.js';
|
||||
import type { AcpHttpHandle } from './acp-http/index.js';
|
||||
import type { DeviceFlowRegistry } from './auth/device-flow.js';
|
||||
import type { DaemonLogger } from './daemonLogger.js';
|
||||
import type { DaemonLogger } from './daemon-logger.js';
|
||||
import type {
|
||||
AcpSessionBridge,
|
||||
BridgeDaemonStatusSnapshot,
|
||||
} from './acpSessionBridge.js';
|
||||
} from './acp-session-bridge.js';
|
||||
import { isLoopbackBind } from './loopback-binds.js';
|
||||
import type { RateLimiterInstance, RateLimitTier } from './rate-limit.js';
|
||||
import type { ServeOptions } from './types.js';
|
||||
|
|
@ -116,7 +116,7 @@ export {
|
|||
// embeds that want to recognize these errors (parallel to how
|
||||
// they already match `WorkspaceInitConflictError` /
|
||||
// `SessionNotFoundError`) need them on the public barrel; without
|
||||
// this they have to deep-import `./acpSessionBridge.js`.
|
||||
// this they have to deep-import `./acp-session-bridge.js`.
|
||||
McpServerNotFoundError,
|
||||
McpServerRestartFailedError,
|
||||
SessionNotFoundError,
|
||||
|
|
@ -133,7 +133,7 @@ export {
|
|||
type BridgeSpawnRequest,
|
||||
type ChannelFactory,
|
||||
type HttpAcpBridge,
|
||||
} from './acpSessionBridge.js';
|
||||
} from './acp-session-bridge.js';
|
||||
export {
|
||||
EventBus,
|
||||
EVENT_SCHEMA_VERSION,
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ import {
|
|||
type A2uiActionResult,
|
||||
type McpServerCell,
|
||||
type McpServerConfigLike,
|
||||
} from './a2uiAction.js';
|
||||
} from './a2ui-action.js';
|
||||
|
||||
function makeApp(opts: {
|
||||
servers?: McpServerCell[];
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import type { Application, Request, RequestHandler, Response } from 'express';
|
||||
import type { AcpSessionBridge } from '../acpSessionBridge.js';
|
||||
import type { AcpSessionBridge } from '../acp-session-bridge.js';
|
||||
import {
|
||||
isContentHash,
|
||||
type ContentHash,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {
|
|||
runQwenServe,
|
||||
validatePolicyConfig,
|
||||
} from './run-qwen-serve.js';
|
||||
import type { HttpAcpBridge } from './acpSessionBridge.js';
|
||||
import type { HttpAcpBridge } from './acp-session-bridge.js';
|
||||
|
||||
/**
|
||||
* #4297 fold-in 7 (deepseek S1, addresses #3262690842). Lock the
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import {
|
|||
canonicalizeWorkspace,
|
||||
createAcpSessionBridge,
|
||||
type AcpSessionBridge,
|
||||
} from './acpSessionBridge.js';
|
||||
} from './acp-session-bridge.js';
|
||||
import {
|
||||
DEFAULT_OTLP_ENDPOINT,
|
||||
DEFAULT_TELEMETRY_TARGET,
|
||||
|
|
@ -51,7 +51,7 @@ import {
|
|||
import { createBridgeFileSystemAdapter } from './bridge-file-system-adapter.js';
|
||||
import { createDaemonStatusProvider } from './daemon-status-provider.js';
|
||||
import { isLoopbackBind } from './loopback-binds.js';
|
||||
import { resolveWebShellDir } from './webShellStatic.js';
|
||||
import { resolveWebShellDir } from './web-shell-static.js';
|
||||
import { parseAllowOriginPatterns } from './auth.js';
|
||||
import {
|
||||
createPermissionAuditPublisher,
|
||||
|
|
@ -62,7 +62,7 @@ import {
|
|||
getActiveSseCount,
|
||||
resolveBridgeFsFactory,
|
||||
} from './server.js';
|
||||
import { initDaemonLogger, type DaemonLogger } from './daemonLogger.js';
|
||||
import { initDaemonLogger, type DaemonLogger } from './daemon-logger.js';
|
||||
import { createSpawnChannelFactory } from '@qwen-code/acp-bridge/spawnChannel';
|
||||
import { createDaemonWorkspaceService } from './workspace-service/index.js';
|
||||
import { SERVE_CAPABILITY_REGISTRY } from './capabilities.js';
|
||||
|
|
@ -75,7 +75,7 @@ import type { WorkspaceFileSystemFactory } from './fs/index.js';
|
|||
import type { PermissionPolicy } from '@qwen-code/acp-bridge';
|
||||
import { getCliVersion } from '../utils/version.js';
|
||||
import { getRateLimiter } from './rate-limit.js';
|
||||
import type { AcpHttpHandle } from './acpHttp/index.js';
|
||||
import type { AcpHttpHandle } from './acp-http/index.js';
|
||||
|
||||
const QWEN_SERVER_TOKEN_ENV = 'QWEN_SERVER_TOKEN';
|
||||
const QWEN_SERVE_PROMPT_DEADLINE_MS_ENV = 'QWEN_SERVE_PROMPT_DEADLINE_MS';
|
||||
|
|
|
|||
|
|
@ -20,7 +20,10 @@ import {
|
|||
resolvePromptDeadlineMs,
|
||||
} from './server.js';
|
||||
import { runQwenServe, type RunHandle } from './run-qwen-serve.js';
|
||||
import { resolveWebShellDir, isDocumentNavigation } from './webShellStatic.js';
|
||||
import {
|
||||
resolveWebShellDir,
|
||||
isDocumentNavigation,
|
||||
} from './web-shell-static.js';
|
||||
import {
|
||||
CONDITIONAL_SERVE_FEATURES,
|
||||
getAdvertisedServeFeatures,
|
||||
|
|
@ -77,7 +80,7 @@ import {
|
|||
type BridgeSpawnRequest,
|
||||
type AcpSessionBridge,
|
||||
type SessionMetadataUpdate,
|
||||
} from './acpSessionBridge.js';
|
||||
} from './acp-session-bridge.js';
|
||||
import type { BridgeEvent, SubscribeOptions } from './event-bus.js';
|
||||
import type {
|
||||
ServeSessionContextStatus,
|
||||
|
|
@ -96,7 +99,7 @@ import type {
|
|||
ServeWorkspaceToolsStatus,
|
||||
} from './status.js';
|
||||
import { CAPABILITIES_SCHEMA_VERSION, type ServeOptions } from './types.js';
|
||||
import type { DaemonLogger } from './daemonLogger.js';
|
||||
import type { DaemonLogger } from './daemon-logger.js';
|
||||
import { FsError, type WorkspaceFileSystemFactory } from './fs/index.js';
|
||||
|
||||
const baseOpts: ServeOptions = {
|
||||
|
|
@ -12229,7 +12232,7 @@ describe('sendBridgeError daemonLog routing', () => {
|
|||
it('routes 5xx errors through daemonLog when provided', async () => {
|
||||
const tmp = await fsp.mkdtemp(path.join(os.tmpdir(), 'daemon-log-'));
|
||||
const stderrLines: string[] = [];
|
||||
const { initDaemonLogger } = await import('./daemonLogger.js');
|
||||
const { initDaemonLogger } = await import('./daemon-logger.js');
|
||||
const daemonLog = initDaemonLogger({
|
||||
boundWorkspace: '/w',
|
||||
pid: 1,
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import {
|
|||
type ExtensionSetting,
|
||||
} from '@qwen-code/qwen-code-core';
|
||||
import { writeStderrLine } from '../utils/stdioHelpers.js';
|
||||
import type { DaemonLogger } from './daemonLogger.js';
|
||||
import type { DaemonLogger } from './daemon-logger.js';
|
||||
import {
|
||||
allowOriginCors,
|
||||
bearerAuth,
|
||||
|
|
@ -65,11 +65,11 @@ import { SUPPORTED_LANGUAGES } from '../i18n/index.js';
|
|||
import { loadSettings } from '../config/settings.js';
|
||||
import { isWorkspaceTrusted } from '../config/trustedFolders.js';
|
||||
import { isLoopbackBind } from './loopback-binds.js';
|
||||
import { mountAcpHttp, type AcpHttpHandle } from './acpHttp/index.js';
|
||||
import { mountAcpHttp, type AcpHttpHandle } from './acp-http/index.js';
|
||||
import {
|
||||
buildDaemonStatusResponse,
|
||||
parseDaemonStatusDetail,
|
||||
} from './daemonStatus.js';
|
||||
} from './daemon-status.js';
|
||||
import {
|
||||
canonicalizeWorkspace,
|
||||
CancelSentinelCollisionError,
|
||||
|
|
@ -99,7 +99,7 @@ import {
|
|||
WorkspaceMismatchError,
|
||||
type BridgeSessionSummary,
|
||||
type AcpSessionBridge,
|
||||
} from './acpSessionBridge.js';
|
||||
} from './acp-session-bridge.js';
|
||||
import {
|
||||
getAdvertisedServeFeatures,
|
||||
getServeProtocolVersions,
|
||||
|
|
@ -118,7 +118,7 @@ import { getDemoHtml } from './demo.js';
|
|||
import {
|
||||
mountWebShellAssets,
|
||||
mountWebShellSpaFallback,
|
||||
} from './webShellStatic.js';
|
||||
} from './web-shell-static.js';
|
||||
import { mountWorkspaceMemoryRoutes } from './workspace-memory.js';
|
||||
import { mountWorkspaceAgentsRoutes } from './workspace-agents.js';
|
||||
import {
|
||||
|
|
@ -133,7 +133,7 @@ import {
|
|||
type WorkspaceRequestContext,
|
||||
} from './workspace-service/index.js';
|
||||
import { registerWorkspaceSettingsRoutes } from './routes/workspace-settings.js';
|
||||
import { registerA2uiActionRoutes } from './routes/a2uiAction.js';
|
||||
import { registerA2uiActionRoutes } from './routes/a2ui-action.js';
|
||||
import {
|
||||
createRateLimiter,
|
||||
setRateLimiter,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import {
|
|||
} from 'vitest';
|
||||
import { Storage, QWEN_DIR } from '@qwen-code/qwen-code-core';
|
||||
import { createMutationGate } from './auth.js';
|
||||
import type { AcpSessionBridge } from './acpSessionBridge.js';
|
||||
import type { AcpSessionBridge } from './acp-session-bridge.js';
|
||||
import type { BridgeEvent } from './event-bus.js';
|
||||
import { mountWorkspaceAgentsRoutes } from './workspace-agents.js';
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import { isServeDebugMode } from './debug-mode.js';
|
|||
import {
|
||||
InvalidClientIdError,
|
||||
type AcpSessionBridge,
|
||||
} from './acpSessionBridge.js';
|
||||
} from './acp-session-bridge.js';
|
||||
|
||||
/**
|
||||
* Pattern for the route-layer `:agentType` URL parameter. Matches the
|
||||
|
|
@ -1370,5 +1370,5 @@ export function createDaemonSubagentManager(
|
|||
|
||||
// Re-export the bridge error type used by route helpers so test files
|
||||
// can import it from a single module without reaching into
|
||||
// acpSessionBridge directly.
|
||||
// acp-session-bridge directly.
|
||||
export { InvalidClientIdError };
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import { createMutationGate } from './auth.js';
|
|||
import {
|
||||
InvalidClientIdError,
|
||||
type AcpSessionBridge,
|
||||
} from './acpSessionBridge.js';
|
||||
} from './acp-session-bridge.js';
|
||||
import type { BridgeEvent } from './event-bus.js';
|
||||
import { mountWorkspaceMemoryRoutes } from './workspace-memory.js';
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import {
|
|||
} from '@qwen-code/qwen-code-core';
|
||||
import { writeStderrLine } from '../utils/stdioHelpers.js';
|
||||
import { isServeDebugMode } from './debug-mode.js';
|
||||
import type { AcpSessionBridge } from './acpSessionBridge.js';
|
||||
import type { AcpSessionBridge } from './acp-session-bridge.js';
|
||||
import {
|
||||
createIdleWorkspaceMemoryStatus,
|
||||
STATUS_SCHEMA_VERSION,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import type {
|
|||
DaemonWorkspaceService,
|
||||
WorkspaceRequestContext,
|
||||
} from '../types.js';
|
||||
import type { AcpSessionBridge } from '../../acpSessionBridge.js';
|
||||
import type { AcpSessionBridge } from '../../acp-session-bridge.js';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Test fixtures
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue