From 857a4941beb890dd8a893f5d6a2ed86a21e6423d Mon Sep 17 00:00:00 2001 From: Turtle Date: Wed, 29 Jul 2026 17:34:50 +0800 Subject: [PATCH] fix(cli): restore shipped surface capabilities --- apps/cli/base.cordis.yml | 19 +++++++--- apps/cli/composition.md | 12 +++++-- apps/cli/package.json | 9 ++++- apps/cli/src/args.ts | 19 ++-------- apps/cli/src/tui.ts | 12 ++++--- apps/cli/tests/args.spec.ts | 11 ++---- apps/cli/tui.cordis.yml | 5 ++- apps/cli/web.cordis.yml | 48 ++++++++++++++++++++++++++ examples/acp-agent/composition.md | 2 +- examples/headless-agent/composition.md | 2 +- pnpm-lock.yaml | 27 +++++++++++++-- scripts/gen-doc-graphs.ts | 14 ++++---- scripts/verify-cordis-config.ts | 7 +++- 13 files changed, 133 insertions(+), 54 deletions(-) diff --git a/apps/cli/base.cordis.yml b/apps/cli/base.cordis.yml index 40bb3fe946..cb1fd7925d 100644 --- a/apps/cli/base.cordis.yml +++ b/apps/cli/base.cordis.yml @@ -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' diff --git a/apps/cli/composition.md b/apps/cli/composition.md index 47d14ce88d..9c0d5b5f06 100644 --- a/apps/cli/composition.md +++ b/apps/cli/composition.md @@ -7,7 +7,7 @@ The TUI agent combines the real DeepSeek adapter, coding tools, compaction, suba ```mermaid flowchart LR - cfg["apps/cli (dsh)
cordis.yml"] + cfg["apps/cli (dsh)
base.cordis.yml + tui.cordis.yml"] plugin_tui_timer["timer
@cordisjs/plugin-timer"] cfg --> plugin_tui_timer plugin_tui_llm["llm
@deepseek-ai/dsh-llm"] @@ -26,6 +26,8 @@ flowchart LR cfg --> plugin_tui_tasks plugin_tui_llm_retry["llm-retry
@deepseek-ai/dsh-llm-retry"] cfg --> plugin_tui_llm_retry + plugin_tui_llm_pi_ai["llm-pi-ai
@deepseek-ai/dsh-llm-pi-ai"] + cfg --> plugin_tui_llm_pi_ai plugin_tui_session_persistence_jsonl["session-persistence-jsonl
@deepseek-ai/dsh-session-persistence-jsonl"] cfg --> plugin_tui_session_persistence_jsonl plugin_tui_subprocess["subprocess
@deepseek-ai/dsh-subprocess-local"] @@ -94,6 +96,8 @@ flowchart LR cfg --> plugin_tui_fs_local plugin_tui_llm_deepseek["llm-deepseek
@deepseek-ai/dsh-llm-deepseek"] cfg --> plugin_tui_llm_deepseek + plugin_tui_tools["tools
@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. diff --git a/apps/cli/package.json b/apps/cli/package.json index ddbef2cd03..87cbf29060 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -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:^", diff --git a/apps/cli/src/args.ts b/apps/cli/src/args.ts index 56827f2764..e470af1299 100644 --- a/apps/cli/src/args.ts +++ b/apps/cli/src/args.ts @@ -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 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 { diff --git a/apps/cli/src/tui.ts b/apps/cli/src/tui.ts index f0ef5ddc02..e9d108bfb0 100644 --- a/apps/cli/src/tui.ts +++ b/apps/cli/src/tui.ts @@ -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 diff --git a/apps/cli/tests/args.spec.ts b/apps/cli/tests/args.spec.ts index 9e79e333f2..7eea63b68d 100644 --- a/apps/cli/tests/args.spec.ts +++ b/apps/cli/tests/args.spec.ts @@ -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) diff --git a/apps/cli/tui.cordis.yml b/apps/cli/tui.cordis.yml index 13b8c8930d..82f5e7aa9e 100644 --- a/apps/cli/tui.cordis.yml +++ b/apps/cli/tui.cordis.yml @@ -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' diff --git a/apps/cli/web.cordis.yml b/apps/cli/web.cordis.yml index 9cbd73a6f6..6a2d5df9e5 100644 --- a/apps/cli/web.cordis.yml +++ b/apps/cli/web.cordis.yml @@ -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' diff --git a/examples/acp-agent/composition.md b/examples/acp-agent/composition.md index b4a3236920..6cd896ca31 100644 --- a/examples/acp-agent/composition.md +++ b/examples/acp-agent/composition.md @@ -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. diff --git a/examples/headless-agent/composition.md b/examples/headless-agent/composition.md index 53a01260e1..7c55a80fdd 100644 --- a/examples/headless-agent/composition.md +++ b/examples/headless-agent/composition.md @@ -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. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6e54ba3a05..26d195da58 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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 diff --git a/scripts/gen-doc-graphs.ts b/scripts/gen-doc-graphs.ts index 73ba05f748..54cdc11a50 100644 --- a/scripts/gen-doc-graphs.ts +++ b/scripts/gen-doc-graphs.ts @@ -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)}
cordis.yml"]`, + ` cfg["${escLabel(example.label)}
${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') diff --git a/scripts/verify-cordis-config.ts b/scripts/verify-cordis-config.ts index b0f7b06eb6..9a188a5c75 100644 --- a/scripts/verify-cordis-config.ts +++ b/scripts/verify-cordis-config.ts @@ -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') }