mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
fix(cli): restore shipped surface capabilities
This commit is contained in:
@@ -8,8 +8,8 @@
|
||||
# A patch replaces the targeted row's whole `config` rather than merging into
|
||||
# it, so a row whose value differs per surface does NOT live here: it belongs to
|
||||
# each overlay, keeping any single row down to one overlay layer plus the user's.
|
||||
# That is why `agent-loop`, `system-prompt`, `tools`, `fs-local`, and
|
||||
# `llm-deepseek` thinking defaults are absent below.
|
||||
# Rows with surface-specific values appear below only with shared plugin identity
|
||||
# and neutral defaults; each overlay restates the complete surface configuration.
|
||||
#
|
||||
# Row order carries no load semantics (activation is service-availability
|
||||
# driven); the grouping is for readers.
|
||||
@@ -51,9 +51,20 @@
|
||||
- id: llm-retry
|
||||
name: '@deepseek-ai/dsh-llm-retry'
|
||||
|
||||
- id: llm-pi-ai
|
||||
name: '@deepseek-ai/dsh-llm-pi-ai'
|
||||
config:
|
||||
providers:
|
||||
- provider: openai
|
||||
apiKey: !!js process.env.OPENAI_API_KEY
|
||||
baseURL: !!js process.env.OPENAI_BASE_URL
|
||||
- provider: anthropic
|
||||
apiKey: !!js process.env.ANTHROPIC_API_KEY
|
||||
baseURL: !!js process.env.ANTHROPIC_BASE_URL
|
||||
|
||||
# The session store root is the launcher's policy, not a plugin's: `dsh` shares
|
||||
# one store under the Harness home across every cwd, so `/resume` and
|
||||
# `/resume` spans workspaces. Without a launcher the project-local fallback keeps
|
||||
# one store under the Harness home across every cwd, so `/resume` spans
|
||||
# workspaces. Without a launcher the project-local fallback keeps
|
||||
# an embedder's sessions beside its project.
|
||||
- id: session-persistence-jsonl
|
||||
name: '@deepseek-ai/dsh-session-persistence-jsonl'
|
||||
|
||||
@@ -7,7 +7,7 @@ The TUI agent combines the real DeepSeek adapter, coding tools, compaction, suba
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
cfg["apps/cli (dsh)<br/>cordis.yml"]
|
||||
cfg["apps/cli (dsh)<br/>base.cordis.yml + tui.cordis.yml"]
|
||||
plugin_tui_timer["timer<br/>@cordisjs/plugin-timer"]
|
||||
cfg --> plugin_tui_timer
|
||||
plugin_tui_llm["llm<br/>@deepseek-ai/dsh-llm"]
|
||||
@@ -26,6 +26,8 @@ flowchart LR
|
||||
cfg --> plugin_tui_tasks
|
||||
plugin_tui_llm_retry["llm-retry<br/>@deepseek-ai/dsh-llm-retry"]
|
||||
cfg --> plugin_tui_llm_retry
|
||||
plugin_tui_llm_pi_ai["llm-pi-ai<br/>@deepseek-ai/dsh-llm-pi-ai"]
|
||||
cfg --> plugin_tui_llm_pi_ai
|
||||
plugin_tui_session_persistence_jsonl["session-persistence-jsonl<br/>@deepseek-ai/dsh-session-persistence-jsonl"]
|
||||
cfg --> plugin_tui_session_persistence_jsonl
|
||||
plugin_tui_subprocess["subprocess<br/>@deepseek-ai/dsh-subprocess-local"]
|
||||
@@ -94,6 +96,8 @@ flowchart LR
|
||||
cfg --> plugin_tui_fs_local
|
||||
plugin_tui_llm_deepseek["llm-deepseek<br/>@deepseek-ai/dsh-llm-deepseek"]
|
||||
cfg --> plugin_tui_llm_deepseek
|
||||
plugin_tui_tools["tools<br/>@deepseek-ai/dsh-tool-ask-user"]
|
||||
cfg --> plugin_tui_tools
|
||||
```
|
||||
|
||||
| Plugin id | Package / module |
|
||||
@@ -107,6 +111,7 @@ flowchart LR
|
||||
| `agent` | `@deepseek-ai/dsh-agent` |
|
||||
| `tasks` | `@deepseek-ai/dsh-tasks-local` |
|
||||
| `llm-retry` | `@deepseek-ai/dsh-llm-retry` |
|
||||
| `llm-pi-ai` | `@deepseek-ai/dsh-llm-pi-ai` |
|
||||
| `session-persistence-jsonl` | `@deepseek-ai/dsh-session-persistence-jsonl` |
|
||||
| `subprocess` | `@deepseek-ai/dsh-subprocess-local` |
|
||||
| `bash-local` | `@deepseek-ai/dsh-bash-local` |
|
||||
@@ -141,7 +146,8 @@ flowchart LR
|
||||
| `agent-loop` | `@deepseek-ai/dsh-agent-loop` |
|
||||
| `fs-local` | `@deepseek-ai/dsh-fs-local` |
|
||||
| `llm-deepseek` | `@deepseek-ai/dsh-llm-deepseek` |
|
||||
| `tools` | `@deepseek-ai/dsh-tool-ask-user` |
|
||||
|
||||
Source config: [`apps/cli/base.cordis.yml`](base.cordis.yml).
|
||||
Source configs: [`apps/cli/base.cordis.yml`](base.cordis.yml), [`apps/cli/tui.cordis.yml`](tui.cordis.yml).
|
||||
|
||||
Maintenance mode: hybrid: the leaf plugin list is parsed from its `cordis.yml`; app package expansion is curated from package source.
|
||||
Maintenance mode: hybrid: the leaf plugin list is parsed from its shipped config files; app package expansion is curated from package source.
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"@deepseek-ai/dsh-agent-loop": "workspace:^",
|
||||
"@deepseek-ai/dsh-app-boot": "workspace:^",
|
||||
"@deepseek-ai/dsh-bash-local": "workspace:^",
|
||||
"@deepseek-ai/dsh-bash-sandbox": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-connection": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-hmr": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
@@ -33,6 +34,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-layout": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-model": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-models": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-permission": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-plan": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-question": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-settings": "workspace:^",
|
||||
@@ -52,10 +54,11 @@
|
||||
"@deepseek-ai/dsh-frontend": "workspace:^",
|
||||
"@deepseek-ai/dsh-fs-local": "workspace:^",
|
||||
"@deepseek-ai/dsh-fs-policy": "workspace:^",
|
||||
"@deepseek-ai/dsh-fs-sandbox": "workspace:^",
|
||||
"@deepseek-ai/dsh-goal": "workspace:^",
|
||||
"@deepseek-ai/dsh-goal-session": "workspace:^",
|
||||
"@deepseek-ai/dsh-helper": "workspace:^",
|
||||
"@deepseek-ai/dsh-host-apiproxy": "workspace:^",
|
||||
"@deepseek-ai/dsh-host-directory-picker-browse": "workspace:^",
|
||||
"@deepseek-ai/dsh-host-webserver": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/dsh-llm": "workspace:^",
|
||||
@@ -63,7 +66,10 @@
|
||||
"@deepseek-ai/dsh-llm-pi-ai": "workspace:^",
|
||||
"@deepseek-ai/dsh-llm-retry": "workspace:^",
|
||||
"@deepseek-ai/dsh-paths": "workspace:^",
|
||||
"@deepseek-ai/dsh-permission": "workspace:^",
|
||||
"@deepseek-ai/dsh-plan-mode": "workspace:^",
|
||||
"@deepseek-ai/dsh-sandbox-local": "workspace:^",
|
||||
"@deepseek-ai/dsh-sandbox-policy": "workspace:^",
|
||||
"@deepseek-ai/dsh-scope": "workspace:^",
|
||||
"@deepseek-ai/dsh-session": "workspace:^",
|
||||
"@deepseek-ai/dsh-session-checkpoint-policy": "workspace:^",
|
||||
@@ -102,6 +108,7 @@
|
||||
"@deepseek-ai/dsh-tool-workflow": "workspace:^",
|
||||
"@deepseek-ai/dsh-tools": "workspace:^",
|
||||
"@deepseek-ai/dsh-tui": "workspace:^",
|
||||
"@deepseek-ai/dsh-user-approval": "workspace:^",
|
||||
"@deepseek-ai/dsh-user-interaction": "workspace:^",
|
||||
"@deepseek-ai/dsh-workflow-workerthread": "workspace:^",
|
||||
"@deepseek-ai/dsh-workspace": "workspace:^",
|
||||
|
||||
@@ -213,27 +213,12 @@ Examples:
|
||||
resolved = resolveWeb(options)
|
||||
})
|
||||
|
||||
// `--resume` is NOT redeclared here: an option a subcommand shares with its
|
||||
// parent parses into `program.opts()` and leaves the subcommand's own options
|
||||
// empty, so redeclaring it would silently drop the id. Commander therefore
|
||||
// omits it from this subcommand's option list, hence the trailing help text.
|
||||
program
|
||||
.command('meta')
|
||||
.description('work on the dsh source that runs this command, from any directory')
|
||||
.addHelpText('after', '\nAccepts --resume <id> to resume a persisted session from this checkout.\n')
|
||||
.action(() => {
|
||||
// Commander parses the parent (default-surface) options on either side of
|
||||
// the subcommand into `program.opts()`. `meta` accepts only `--resume`, so
|
||||
// a leaked `--config`/`-p` is a mistyped invocation that must fail loud
|
||||
// rather than silently be dropped.
|
||||
const parent = program.opts<{ config?: string; configReplace?: string; prompt?: string; resume?: string }>()
|
||||
if (parent.config !== undefined || parent.configReplace !== undefined || parent.prompt !== undefined) {
|
||||
program.error('error: meta takes none of --config, --config-replace, or -p/--prompt')
|
||||
}
|
||||
// Same reason as the default surface: an empty id would start a fresh
|
||||
// session downstream instead of failing the mistyped resume.
|
||||
if (parent.resume === '') program.error('error: --resume needs a session id')
|
||||
resolved = { mode: 'meta', ...parent.resume !== undefined && { resume: parent.resume } }
|
||||
rejectParentOptions('meta')
|
||||
resolved = { mode: 'meta' }
|
||||
})
|
||||
|
||||
try {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { join } from 'node:path'
|
||||
import { join, resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import {
|
||||
addHarnessSourceSection,
|
||||
@@ -155,6 +155,8 @@ export async function runTui(
|
||||
// selected session may belong to another workspace, so the handoff also enters
|
||||
// that directory. The host is offered only when Node exposes `process.execve`
|
||||
// and knows its own entry.
|
||||
const resolvedConfig = config === undefined ? undefined : resolve(config)
|
||||
const resolvedConfigReplace = configReplace === undefined ? undefined : resolve(configReplace)
|
||||
const entry = process.argv[1]
|
||||
const execve = process.execve?.bind(process)
|
||||
const app: { current?: Context } = {}
|
||||
@@ -172,8 +174,8 @@ export async function runTui(
|
||||
`--resume=${sessionId}`,
|
||||
// Both config flags must survive the handoff: resuming into a different
|
||||
// tree than the session was created in would silently change the agent.
|
||||
...config !== undefined ? ['--config', config] : [],
|
||||
...configReplace !== undefined ? ['--config-replace', configReplace] : [],
|
||||
...resolvedConfig !== undefined ? ['--config', resolvedConfig] : [],
|
||||
...resolvedConfigReplace !== undefined ? ['--config-replace', resolvedConfigReplace] : [],
|
||||
]
|
||||
// Mint the fresh id here rather than in the app bundle: the exit line names
|
||||
// the session to resume, so the launcher must know it before the tree boots.
|
||||
@@ -223,11 +225,11 @@ export async function runTui(
|
||||
...loadOverlayPatches(NAME, TUI_OVERLAY),
|
||||
...config === undefined
|
||||
? loadPersonalPatches(NAME) ?? []
|
||||
: loadOverlayPatches(NAME, resolveConfigPath(config, undefined)),
|
||||
: loadOverlayPatches(NAME, resolveConfigPath(resolve(config), undefined)),
|
||||
]
|
||||
const ctx = await boot(
|
||||
NAME,
|
||||
replaceTree ? resolveConfigPath(configReplace, undefined) : BASE_CONFIG,
|
||||
replaceTree ? resolveConfigPath(resolve(configReplace), undefined) : BASE_CONFIG,
|
||||
patches,
|
||||
(hostCtx) => {
|
||||
// The launcher owns session identity and the exit line: a config-mounted
|
||||
|
||||
@@ -30,12 +30,7 @@ describe('parseDshArgs', () => {
|
||||
expect(parse(['--config-replace', 'tree.yml'])).toEqual({ mode: 'tui', configReplace: 'tree.yml' })
|
||||
expect(parse(['--resume', 'sess', '--config', 'app.yml'])).toEqual({ mode: 'tui', config: 'app.yml', resume: 'sess' })
|
||||
expect(parse(['-p', 'do the thing'])).toEqual({ mode: 'headless', prompt: 'do the thing' })
|
||||
// `meta` accepts `--resume` but does not redeclare it: a shared option parses
|
||||
// into program.opts() on either side of the subcommand, and redeclaring it
|
||||
// would leave the subcommand's own options empty and drop the id.
|
||||
expect(parse(['meta'])).toEqual({ mode: 'meta' })
|
||||
expect(parse(['meta', '--resume', 'sess'])).toEqual({ mode: 'meta', resume: 'sess' })
|
||||
expect(parse(['--resume', 'sess', 'meta'])).toEqual({ mode: 'meta', resume: 'sess' })
|
||||
// Credential setup is option-free: it writes the Harness-home .env, so
|
||||
// there is nothing for a flag to select.
|
||||
// Bare `web` carries no host/port: the shipped cordis.yml owns the default.
|
||||
@@ -72,9 +67,9 @@ describe('parseDshArgs', () => {
|
||||
expect(exitCode(['--config-replace', 'tree.yml', 'web'])).toBe(1)
|
||||
// Same rule for credential setup: it shares no option with the default
|
||||
// surface, so a leaked flag is a typo, not something to ignore.
|
||||
// `meta` fixes its own config tree and is interactive, so --config/-p are
|
||||
// rejected; an empty id is swallowed downstream exactly as above.
|
||||
expect(exitCode(['meta', '--resume='])).toBe(1)
|
||||
// `meta` fixes its own config tree and always starts fresh, so every
|
||||
// default-surface option is rejected.
|
||||
expect(exitCode(['meta', '--resume', 's'])).toBe(1)
|
||||
expect(exitCode(['meta', '--config', 'c.yml'])).toBe(1)
|
||||
expect(exitCode(['meta', '--config-replace', 'tree.yml'])).toBe(1)
|
||||
expect(exitCode(['meta', '-p', 'task'])).toBe(1)
|
||||
|
||||
@@ -94,9 +94,8 @@
|
||||
- id: session-reference
|
||||
name: '@deepseek-ai/dsh-session-reference'
|
||||
|
||||
# Refuses write/edit inside the dsh checkout this launcher runs from, on that
|
||||
# checkout's own branch, until the session loads dsh-customize. Inert
|
||||
# everywhere else, so an ordinary project sees no change.
|
||||
# Compacts oversized tool results before the broader conversation compactor
|
||||
# runs, preserving the model-visible result within the configured budget.
|
||||
- id: tool-result-prune
|
||||
name: '@deepseek-ai/dsh-compact-tool-result-prune'
|
||||
|
||||
|
||||
@@ -31,7 +31,49 @@
|
||||
apiKey: !!js process.env.DEEPSEEK_API_KEY
|
||||
baseURL: !!js process.env.DEEPSEEK_BASE_URL
|
||||
|
||||
# The web surface replaces the unrestricted local executors with the shared
|
||||
# sandbox policy. Its default preserves the previous unrestricted behavior;
|
||||
# DSH_PERMISSION_MODE and the browser permission picker can confine a session.
|
||||
- insert:
|
||||
- id: sandbox
|
||||
name: '@deepseek-ai/dsh-sandbox-local'
|
||||
|
||||
- id: sandbox-policy
|
||||
name: '@deepseek-ai/dsh-sandbox-policy'
|
||||
config:
|
||||
mode: !!js process.env.DSH_PERMISSION_MODE ?? 'danger-full-access'
|
||||
workspaceRoot: !!js process.cwd()
|
||||
|
||||
- id: bash-sandbox
|
||||
name: '@deepseek-ai/dsh-bash-sandbox'
|
||||
|
||||
- id: approval
|
||||
name: '@deepseek-ai/dsh-user-approval'
|
||||
config:
|
||||
policy: !!js "(process.env.DSH_PERMISSION_MODE ?? 'danger-full-access') === 'danger-full-access' ? 'never' : 'ask'"
|
||||
|
||||
- id: permission
|
||||
name: '@deepseek-ai/dsh-permission'
|
||||
config:
|
||||
presets:
|
||||
read-only:
|
||||
sandbox: read-only
|
||||
approval: ask
|
||||
workspace-write:
|
||||
sandbox: workspace-write
|
||||
approval: ask
|
||||
danger-full-access:
|
||||
sandbox: danger-full-access
|
||||
approval: never
|
||||
|
||||
- id: fs-sandbox
|
||||
name: '@deepseek-ai/dsh-fs-sandbox'
|
||||
|
||||
- id: bash-local
|
||||
remove: true
|
||||
|
||||
- id: fs-local
|
||||
remove: true
|
||||
|
||||
# ── web-only host rows, the transport layer, and the browser roster ─────────
|
||||
|
||||
@@ -72,6 +114,9 @@
|
||||
# The API gateway: the transport-agnostic dispatch face every client shape
|
||||
# shares. provider/model are the host default routing — the profile json's
|
||||
# mapping target (user config overrides these engineering defaults).
|
||||
- id: directory-picker
|
||||
name: '@deepseek-ai/dsh-host-directory-picker-browse'
|
||||
|
||||
- id: api-gateway
|
||||
name: '@deepseek-ai/dsh-host-apiproxy'
|
||||
config:
|
||||
@@ -156,6 +201,9 @@
|
||||
- id: ui-model
|
||||
name: '@deepseek-ai/dsh-client-ui-model'
|
||||
|
||||
- id: ui-permission
|
||||
name: '@deepseek-ai/dsh-client-ui-permission'
|
||||
|
||||
# Plan control: the composer plan seat over the plan projection + /plan channel.
|
||||
- id: ui-plan
|
||||
name: '@deepseek-ai/dsh-client-ui-plan'
|
||||
|
||||
@@ -94,4 +94,4 @@ flowchart LR
|
||||
|
||||
Source config: [`examples/acp-agent/cordis.yml`](cordis.yml).
|
||||
|
||||
Maintenance mode: hybrid: the leaf plugin list is parsed from its `cordis.yml`; app package expansion is curated from package source.
|
||||
Maintenance mode: hybrid: the leaf plugin list is parsed from its shipped config files; app package expansion is curated from package source.
|
||||
|
||||
@@ -76,4 +76,4 @@ flowchart LR
|
||||
|
||||
Source config: [`examples/headless-agent/cordis.yml`](cordis.yml).
|
||||
|
||||
Maintenance mode: hybrid: the leaf plugin list is parsed from its `cordis.yml`; app package expansion is curated from package source.
|
||||
Maintenance mode: hybrid: the leaf plugin list is parsed from its shipped config files; app package expansion is curated from package source.
|
||||
|
||||
27
pnpm-lock.yaml
generated
27
pnpm-lock.yaml
generated
@@ -134,6 +134,9 @@ importers:
|
||||
'@deepseek-ai/dsh-bash-local':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/bash/bash-local
|
||||
'@deepseek-ai/dsh-bash-sandbox':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/bash/bash-sandbox
|
||||
'@deepseek-ai/dsh-client-connection':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/client/connection
|
||||
@@ -167,6 +170,9 @@ importers:
|
||||
'@deepseek-ai/dsh-client-ui-models':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/client/ui-models
|
||||
'@deepseek-ai/dsh-client-ui-permission':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/client/ui-permission
|
||||
'@deepseek-ai/dsh-client-ui-plan':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/client/ui-plan
|
||||
@@ -224,18 +230,21 @@ importers:
|
||||
'@deepseek-ai/dsh-fs-policy':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/fs/fs-policy
|
||||
'@deepseek-ai/dsh-fs-sandbox':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/fs/fs-sandbox
|
||||
'@deepseek-ai/dsh-goal':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/goal/goal
|
||||
'@deepseek-ai/dsh-goal-session':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/goal/goal-session
|
||||
'@deepseek-ai/dsh-helper':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/sdk/helper
|
||||
'@deepseek-ai/dsh-host-apiproxy':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/host/apiproxy
|
||||
'@deepseek-ai/dsh-host-directory-picker-browse':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/host/directory-picker-browse
|
||||
'@deepseek-ai/dsh-host-webserver':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/host/webserver
|
||||
@@ -257,9 +266,18 @@ importers:
|
||||
'@deepseek-ai/dsh-paths':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/util/paths
|
||||
'@deepseek-ai/dsh-permission':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/ui/permission
|
||||
'@deepseek-ai/dsh-plan-mode':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/plan/plan-mode
|
||||
'@deepseek-ai/dsh-sandbox-local':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/sandbox/sandbox-local
|
||||
'@deepseek-ai/dsh-sandbox-policy':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/sandbox/sandbox-policy
|
||||
'@deepseek-ai/dsh-scope':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/core/scope
|
||||
@@ -374,6 +392,9 @@ importers:
|
||||
'@deepseek-ai/dsh-tui':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/ui/tui
|
||||
'@deepseek-ai/dsh-user-approval':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/ui/user-approval
|
||||
'@deepseek-ai/dsh-user-interaction':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/ui/user-interaction
|
||||
|
||||
@@ -597,7 +597,7 @@ const APP_EXAMPLES = [
|
||||
rel: 'apps/cli/composition.md',
|
||||
title: 'dsh TUI Composition',
|
||||
label: 'apps/cli (dsh)',
|
||||
config: 'apps/cli/base.cordis.yml',
|
||||
configs: ['apps/cli/base.cordis.yml', 'apps/cli/tui.cordis.yml'],
|
||||
summary: 'The TUI agent combines the real DeepSeek adapter, coding tools, compaction, subagents, and workflows with the full-screen terminal app package.',
|
||||
},
|
||||
{
|
||||
@@ -605,7 +605,7 @@ const APP_EXAMPLES = [
|
||||
rel: 'examples/headless-agent/composition.md',
|
||||
title: 'Headless Agent App Composition',
|
||||
label: 'examples/headless-agent',
|
||||
config: 'examples/headless-agent/cordis.yml',
|
||||
configs: ['examples/headless-agent/cordis.yml'],
|
||||
summary: 'The headless demo combines the real DeepSeek adapter and coding capabilities with the one-shot app package, format-pure stdout, and one fresh persisted top-level session.',
|
||||
},
|
||||
{
|
||||
@@ -613,7 +613,7 @@ const APP_EXAMPLES = [
|
||||
rel: 'examples/acp-agent/composition.md',
|
||||
title: 'ACP Automation App Composition',
|
||||
label: 'examples/acp-agent',
|
||||
config: 'examples/acp-agent/cordis.yml',
|
||||
configs: ['examples/acp-agent/cordis.yml'],
|
||||
summary: 'The ACP demo exposes fresh baseline-prompt agent sessions to programmatic clients over JSON-RPC stdio, with no stdout logger, human UI, or pre-created agent.',
|
||||
},
|
||||
]
|
||||
@@ -639,15 +639,15 @@ function renderAppExpansion(lines: string[], appNode: string, pluginName: string
|
||||
}
|
||||
|
||||
function renderAppComposition(example: AppExample): string {
|
||||
const plugins = parseExampleCordis(example.config)
|
||||
const maintenance = 'hybrid: the leaf plugin list is parsed from its `cordis.yml`; app package expansion is curated from package source'
|
||||
const plugins = example.configs.flatMap(parseExampleCordis)
|
||||
const maintenance = 'hybrid: the leaf plugin list is parsed from its shipped config files; app package expansion is curated from package source'
|
||||
const lines = generatedHeader(example.title)
|
||||
lines.push(
|
||||
example.summary,
|
||||
'',
|
||||
'```mermaid',
|
||||
'flowchart LR',
|
||||
` cfg["${escLabel(example.label)}<br/>cordis.yml"]`,
|
||||
` cfg["${escLabel(example.label)}<br/>${escLabel(example.configs.map(config => config.split('/').at(-1)).join(' + '))}"]`,
|
||||
)
|
||||
for (const plugin of plugins) {
|
||||
const pluginNode = nodeId(`plugin_${example.id}`, plugin.id)
|
||||
@@ -664,7 +664,7 @@ function renderAppComposition(example: AppExample): string {
|
||||
'| --- | --- |',
|
||||
...plugins.map(plugin => `| \`${plugin.id}\` | \`${plugin.name}\` |`),
|
||||
'',
|
||||
`Source config: [\`${example.config}\`](${linkFromDoc(example.rel, example.config)}).`,
|
||||
`Source config${example.configs.length === 1 ? '' : 's'}: ${example.configs.map(config => `[\`${config}\`](${linkFromDoc(example.rel, config)})`).join(', ')}.`,
|
||||
)
|
||||
lines.push('', ...maintenanceFooter(maintenance))
|
||||
return lines.join('\n')
|
||||
|
||||
@@ -78,6 +78,11 @@ function validateEntry(value: unknown, file: string, path: string): void {
|
||||
validateEntry(value.config[index], file, `${path}.config[${index}]`)
|
||||
}
|
||||
}
|
||||
if (isUnknownArray(value.insert)) {
|
||||
for (let index = 0; index < value.insert.length; index++) {
|
||||
validateEntry(value.insert[index], file, `${path}.insert[${index}]`)
|
||||
}
|
||||
}
|
||||
if (value.name !== '@cordisjs/plugin-include') return
|
||||
const config = value.config
|
||||
if (!isRecord(config) || !isUnknownArray(config.patches)) return
|
||||
@@ -124,7 +129,7 @@ function validateExampleResolution(): string[] {
|
||||
|
||||
function validateAppResolution(): string[] {
|
||||
const dependencies = readManifest('apps/cli/package.json').dependencies ?? {}
|
||||
const references = pluginReferences.filter(reference => reference.file === 'apps/cli/base.cordis.yml')
|
||||
const references = pluginReferences.filter(reference => reference.file.startsWith('apps/cli/'))
|
||||
return missingPluginDependencies(references, dependencies, 'apps/cli/package.json')
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user