mirror of
https://github.com/wu736139669/hapi.git
synced 2026-11-13 02:37:02 +00:00
fix(dsh): default ACP entrypoint to the official dsh CLI (#2031)
* fix(dsh): default ACP entrypoint to the official dsh CLI The default spawn command was `dsh-acp-demo` — the standalone legacy demo package. Since `@deepseek-ai/dsh` 0.2.0 the ACP profile ships with the main CLI, so `dsh --profile acp` is the maintained entrypoint and the one a plain `npm i -g @deepseek-ai/dsh` install provides. The old default required every self-hoster to also discover and pin the demo package (whose npm `latest` tag is stale) before `hapi dsh` worked at all. - resolveDshAcpCommand defaults to `dsh --profile acp` when no DSH env is set; any explicit `HAPI_DSH_ACP_COMMAND` keeps bare `[]` args so custom wrappers never silently receive the official CLI's flags - DEFAULT_COMMANDS/availability follow, so the PATH probe now checks `dsh` itself - the demo package remains fully supported via explicit `HAPI_DSH_ACP_COMMAND=dsh-acp-demo`; docs updated accordingly Co-Authored-By: Claude Code <noreply@anthropic.com> * Apply config-only DSH overrides through the official CLI's patch overlay HAPI_DSH_ACP_CONFIG without HAPI_DSH_ACP_COMMAND fell through to the default official CLI but passed `--config <path>`, a flag the official dsh CLI does not have — and dropped the required `--profile acp`, so the boot would run the wrong entrypoint with an unknown flag. A config-only override now composes the path as a `--patch` overlay on top of the ACP profile (verified against dsh 0.2.0-rc.2 --dump-config). The `--config` form remains available for legacy community servers pinned via HAPI_DSH_ACP_COMMAND, and both READMEs now document the split. Co-Authored-By: Claude Code <noreply@anthropic.com> --------- Co-authored-by: Claude Code <noreply@anthropic.com>
This commit is contained in:
+10
-4
@@ -163,8 +163,9 @@ environment automatically.
|
||||
|
||||
See `src/configuration.ts` for all options.
|
||||
|
||||
DeepSeek Harness ACP uses `dsh-acp-demo` by default. Override the executable or
|
||||
its arguments without shell parsing:
|
||||
DeepSeek Harness ACP uses the official `dsh` CLI with `--profile acp` by default
|
||||
(the ACP profile ships with `@deepseek-ai/dsh` since 0.2.0). Override the
|
||||
executable or its arguments without shell parsing:
|
||||
|
||||
```bash
|
||||
export HAPI_DSH_ACP_COMMAND=dsh-acp-demo
|
||||
@@ -172,6 +173,11 @@ export HAPI_DSH_ACP_CONFIG=/path/to/deepseek-harness/examples/acp-agent/cordis.y
|
||||
hapi dsh
|
||||
```
|
||||
|
||||
`HAPI_DSH_ACP_CONFIG` maps to `--config` for the configured server. Without
|
||||
`HAPI_DSH_ACP_COMMAND` it instead applies to the official CLI, which has no
|
||||
`--config` flag — the path is passed as a `--patch` overlay on top of
|
||||
`--profile acp`.
|
||||
|
||||
For a source checkout, use JSON arguments:
|
||||
|
||||
```bash
|
||||
@@ -196,8 +202,8 @@ controls for DSH.
|
||||
- `HAPI_EXPERIMENTAL` - Enable experimental features (true/1/yes).
|
||||
- `HAPI_EXTRA_HEADERS_JSON` - JSON object of extra headers to send on CLI → hub requests, e.g. `{"Cookie":"CF_Authorization=..."}`. Can also be set as the `extraHeaders` object in `~/.hapi/settings.json` (environment variable wins).
|
||||
- `HAPI_CLAUDE_PATH` - Path to a specific `claude` executable.
|
||||
- `HAPI_DSH_ACP_COMMAND` - ACP server executable for `hapi dsh` (default: `dsh-acp-demo`).
|
||||
- `HAPI_DSH_ACP_CONFIG` - Optional `dsh-acp-demo --config` path.
|
||||
- `HAPI_DSH_ACP_COMMAND` - ACP server executable for `hapi dsh` (default: `dsh` with `--profile acp`).
|
||||
- `HAPI_DSH_ACP_CONFIG` - Optional config path for the configured DSH ACP server (`--config` with `HAPI_DSH_ACP_COMMAND`, `--patch` overlay on the default official CLI).
|
||||
- `HAPI_DSH_ACP_ARGS_JSON` - Optional JSON array of ACP server arguments.
|
||||
- `HAPI_HTTP_MCP_URL` - Default MCP target for `hapi mcp`.
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@ describe('agent executable resolution', () => {
|
||||
|
||||
it('rejects malformed or missing DSH static configuration', async () => {
|
||||
const directory = await mkdtemp(join(tmpdir(), 'hapi-agent-path-'))
|
||||
await makeExecutable(directory, 'dsh')
|
||||
await makeExecutable(directory, 'dsh-acp-demo')
|
||||
expect(getAgentAvailability('dsh', {
|
||||
PATH: directory,
|
||||
@@ -107,6 +108,11 @@ describe('agent executable resolution', () => {
|
||||
PATH: directory,
|
||||
HAPI_DSH_ACP_CONFIG: config,
|
||||
})).toEqual({ agent: 'dsh', available: true })
|
||||
expect(getAgentAvailability('dsh', {
|
||||
PATH: directory,
|
||||
HAPI_DSH_ACP_COMMAND: 'dsh-acp-demo',
|
||||
HAPI_DSH_ACP_CONFIG: config,
|
||||
})).toEqual({ agent: 'dsh', available: true })
|
||||
})
|
||||
|
||||
it('accepts a macOS Codex app executable when the CLI is absent', async () => {
|
||||
|
||||
@@ -11,7 +11,7 @@ const DEFAULT_COMMANDS: Record<AgentFlavor, string> = {
|
||||
codex: 'codex',
|
||||
copilot: 'copilot',
|
||||
cursor: 'agent',
|
||||
dsh: 'dsh-acp-demo',
|
||||
dsh: 'dsh',
|
||||
gemini: 'gemini',
|
||||
grok: 'grok',
|
||||
kimi: 'kimi',
|
||||
|
||||
@@ -2,20 +2,45 @@ import { describe, expect, it } from 'vitest'
|
||||
import { resolveDshAcpCommand } from './dshBackend'
|
||||
|
||||
describe('resolveDshAcpCommand', () => {
|
||||
it('uses the official ACP demo executable by default', () => {
|
||||
it('defaults to the official dsh CLI with the ACP profile', () => {
|
||||
expect(resolveDshAcpCommand({})).toEqual({
|
||||
command: 'dsh-acp-demo',
|
||||
args: []
|
||||
command: 'dsh',
|
||||
args: ['--profile', 'acp']
|
||||
})
|
||||
})
|
||||
|
||||
it('supports a config path for dsh-acp-demo', () => {
|
||||
expect(resolveDshAcpCommand({ HAPI_DSH_ACP_CONFIG: '/tmp/cordis.yml' })).toEqual({
|
||||
it('supports a config path override', () => {
|
||||
expect(resolveDshAcpCommand({
|
||||
HAPI_DSH_ACP_COMMAND: 'dsh-acp-demo',
|
||||
HAPI_DSH_ACP_CONFIG: '/tmp/cordis.yml'
|
||||
})).toEqual({
|
||||
command: 'dsh-acp-demo',
|
||||
args: ['--config', '/tmp/cordis.yml']
|
||||
})
|
||||
})
|
||||
|
||||
it('maps a config-only override onto the official CLI profile and patch overlay', () => {
|
||||
// The official CLI has no `--config` flag: config overlays go through
|
||||
// `--patch` on top of a profile, and the ACP server still needs
|
||||
// `--profile acp` to boot. `--config` stays a legacy-server escape
|
||||
// hatch behind HAPI_DSH_ACP_COMMAND.
|
||||
expect(resolveDshAcpCommand({
|
||||
HAPI_DSH_ACP_CONFIG: '/tmp/cordis.yml'
|
||||
})).toEqual({
|
||||
command: 'dsh',
|
||||
args: ['--profile', 'acp', '--patch', '/tmp/cordis.yml']
|
||||
})
|
||||
})
|
||||
|
||||
it('keeps bare arguments for an explicitly configured server', () => {
|
||||
// Custom wrappers must not silently receive the official CLI's
|
||||
// `--profile acp` flags; they opt into args via ARGS_JSON or CONFIG.
|
||||
expect(resolveDshAcpCommand({ HAPI_DSH_ACP_COMMAND: 'dsh-acp-demo' })).toEqual({
|
||||
command: 'dsh-acp-demo',
|
||||
args: []
|
||||
})
|
||||
})
|
||||
|
||||
it('supports source checkouts and community servers through JSON args', () => {
|
||||
expect(resolveDshAcpCommand({
|
||||
HAPI_DSH_ACP_COMMAND: 'pnpm',
|
||||
|
||||
@@ -21,16 +21,32 @@ function parseArgsJson(raw: string): string[] {
|
||||
return value
|
||||
}
|
||||
|
||||
/** Default ACP entrypoint: the official `dsh` CLI ships the ACP profile since 0.2.x. */
|
||||
export const DSH_DEFAULT_ACP_COMMAND = 'dsh'
|
||||
export const DSH_DEFAULT_ACP_ARGS = ['--profile', 'acp']
|
||||
|
||||
export function resolveDshAcpCommand(env: DshAcpEnvironment = process.env): {
|
||||
command: string
|
||||
args: string[]
|
||||
} {
|
||||
const command = env.HAPI_DSH_ACP_COMMAND?.trim() || 'dsh-acp-demo'
|
||||
const override = env.HAPI_DSH_ACP_COMMAND?.trim()
|
||||
const command = override || DSH_DEFAULT_ACP_COMMAND
|
||||
if (env.HAPI_DSH_ACP_ARGS_JSON?.trim()) {
|
||||
return { command, args: parseArgsJson(env.HAPI_DSH_ACP_ARGS_JSON.trim()) }
|
||||
}
|
||||
const config = env.HAPI_DSH_ACP_CONFIG?.trim()
|
||||
return { command, args: config ? ['--config', config] : [] }
|
||||
if (config) {
|
||||
// The official CLI has no `--config` flag: it composes config overlays
|
||||
// via `--patch` on top of a profile, so a config-only override still
|
||||
// needs `--profile acp`. Legacy community servers that speak
|
||||
// `--config` must be pinned with HAPI_DSH_ACP_COMMAND.
|
||||
if (override) return { command, args: ['--config', config] }
|
||||
return { command, args: [...DSH_DEFAULT_ACP_ARGS, '--patch', config] }
|
||||
}
|
||||
// An explicitly configured server keeps bare `[]` args: custom wrappers
|
||||
// must not silently receive the official CLI's `--profile acp` flags.
|
||||
if (override) return { command, args: [] }
|
||||
return { command, args: DSH_DEFAULT_ACP_ARGS }
|
||||
}
|
||||
|
||||
export function createDshBackend(env: NodeJS.ProcessEnv = process.env): AcpSdkBackend {
|
||||
|
||||
+15
-5
@@ -19,7 +19,7 @@ agent's integration; their supported syntax varies by agent.
|
||||
| GitHub Copilot | `hapi copilot` | ACP (`copilot --acp --stdio`) | ✓ | ✓ | `default` `read-only` `safe-yolo` `yolo` | ✓ |
|
||||
| Kimi | `hapi kimi` | ACP (`kimi acp`) | ✓ | ✓ | `default` `read-only` `safe-yolo` `yolo` | ✓ |
|
||||
| OpenCode | `hapi opencode` | ACP (`opencode acp`) | ✓ | ✓ | `default` `plan` `yolo` | ✓ |
|
||||
| DeepSeek Harness | `hapi dsh` | ACP (`dsh-acp-demo` or configured server) | — | ✓ | Managed by DSH ACP composition | — |
|
||||
| DeepSeek Harness | `hapi dsh` | ACP (`dsh --profile acp` or configured server) | — | ✓ | Managed by DSH ACP composition | — |
|
||||
| Antigravity (agy) | `hapi agy` | Headless print mode (per-turn `agy -p` + NDJSON) | — | ✓ | `request-review` `always-proceed` | ✓ |
|
||||
| Pi | `hapi pi` | `pi --mode rpc` (JSON-line RPC over stdio) | — | ✓ | none (always auto-approve) | ✓ |
|
||||
| Gemini CLI | — | **Removed** — Google sunset the consumer Gemini CLI (2026-06-18) | — | — | — | — |
|
||||
@@ -256,12 +256,22 @@ If a remote session reports authentication failure, run `grok login --device-aut
|
||||
## DeepSeek Harness
|
||||
|
||||
`hapi dsh` uses the shared ACP transport and keeps DSH's runtime outside HAPI. The
|
||||
default executable is `dsh-acp-demo`; configure a different ACP server or a
|
||||
default entrypoint is the official `dsh` CLI with `--profile acp` (the ACP profile
|
||||
ships with `@deepseek-ai/dsh` since 0.2.0). Configure a different ACP server or a
|
||||
source checkout with `HAPI_DSH_ACP_COMMAND`, `HAPI_DSH_ACP_CONFIG`, or the JSON
|
||||
argument array `HAPI_DSH_ACP_ARGS_JSON`.
|
||||
argument array `HAPI_DSH_ACP_ARGS_JSON`. `HAPI_DSH_ACP_CONFIG` passes a `--config`
|
||||
path to the server named by `HAPI_DSH_ACP_COMMAND`; on the default official CLI
|
||||
(which has no `--config` flag) it is applied as a `--patch` overlay on top of
|
||||
`--profile acp`.
|
||||
|
||||
The official demo is published as `@deepseek-ai/dsh-acp-demo`; use an exact
|
||||
version such as `0.1.0-rc.7` rather than npm's stale `latest` tag:
|
||||
```bash
|
||||
npm install -g @deepseek-ai/dsh@0.2.0-rc.2
|
||||
hapi dsh
|
||||
```
|
||||
|
||||
The standalone legacy demo is still published as `@deepseek-ai/dsh-acp-demo`; use
|
||||
it through an explicit override and an exact version such as `0.1.0-rc.7` rather
|
||||
than npm's stale `latest` tag:
|
||||
|
||||
```bash
|
||||
npm install -g @deepseek-ai/dsh-acp-demo@0.1.0-rc.7
|
||||
|
||||
Reference in New Issue
Block a user