refactor(cli): discover app startup rows from injection

This commit is contained in:
Turtle
2026-08-07 15:43:26 +08:00
parent 1f0a0440f3
commit b692f38506
28 changed files with 302 additions and 283 deletions

View File

@@ -1,8 +1,8 @@
# The dsh-headless bundle patch: one-shot task mode directly over dsh-base.
# It mounts no Host, HTTP server, Web runtime, or browser plugin. The startup
# row owns the task positional (`dsh --profile headless "<task>"`) and this
# app's --help; the direct driver creates an Agent through the core registry
# and prints the final durable assistant message.
# row injects `cmdlineArgs`, owns the task positional
# (`dsh --profile headless "<task>"`) and this app's --help; the direct driver
# creates an Agent through the core registry and prints its durable result.
- id: system-prompt
config:
@@ -25,6 +25,7 @@
- id: headless-startup
name: '@deepseek-ai/dsh-headless/startup'
inject: [cmdlineArgs]
# Reads its task from the headlessStartup service after the startup row
# resolves this app's command line.

View File

@@ -33,8 +33,7 @@
"license": "BSD-3-Clause",
"dsh": {
"bundle": {
"patch": "./cordis.patch.yml",
"entrypoint": "headless-startup"
"patch": "./cordis.patch.yml"
}
},
"dependencies": {
@@ -50,7 +49,6 @@
"@deepseek-ai/dsh-invariants": "^0.0.1",
"@deepseek-ai/dsh-llm": "^0.0.1",
"@deepseek-ai/dsh-session": "^0.0.1",
"@deepseek-ai/dsh-web-app": "^0.0.1",
"@deepseek-ai/cordis": "^4.0.0-rc.7"
},
"devDependencies": {
@@ -60,7 +58,6 @@
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-web-app": "workspace:^",
"@deepseek-ai/cordis": "^4.0.0-rc.7"
}
}

View File

@@ -4,11 +4,6 @@
* `--help` text, then provides {@link HEADLESS_STARTUP_SERVICE} with the task
* the user asked for. The runner waits for it, so a missing task is a usage
* error printed by this command instead of a schema failure inside the runner.
*
* This app layers over the web app, and a composition has exactly one
* command-line owner: the bundle patch disables the web startup row, and this
* one also provides {@link WEB_STARTUP_SERVICE} so the web rows start on their
* composed (one-shot) values.
* @module @deepseek-ai/dsh-headless/startup
*/
@@ -16,7 +11,6 @@ import { Command } from 'commander'
import type { Context } from 'cordis'
import type { EntryOptions } from '@cordisjs/plugin-loader'
import { runStartup } from '@deepseek-ai/dsh-cmdline'
import { WEB_STARTUP_SERVICE } from '@deepseek-ai/dsh-web-app/startup'
/** Stable Cordis plugin name. */
export const name = 'headless-startup'
@@ -75,5 +69,5 @@ function planHeadlessStartup(program: Command, rows: readonly EntryOptions[]): H
* @returns nothing once the runner is started, or once `--help` or a missing task requested exit.
*/
export function apply(ctx: Context): void {
runStartup(ctx, [HEADLESS_STARTUP_SERVICE, WEB_STARTUP_SERVICE], headlessCommand(), planHeadlessStartup)
runStartup(ctx, HEADLESS_STARTUP_SERVICE, headlessCommand(), planHeadlessStartup)
}

View File

@@ -1,5 +1,5 @@
/**
* The one-shot app's entrypoint row over a REAL Loader tree: the task
* The one-shot app's startup row over a REAL Loader tree: the task
* positional becomes the value the runner row reads, a missing task is a usage
* error, and the web service this app absorbs is provided too, so the web rows
* it rides over resolve on their own fallbacks.
@@ -32,7 +32,7 @@ afterEach(async () => {
})
/**
* Mount the real entrypoint row over stand-ins for the runner row and one web
* Mount the real startup row over stand-ins for the runner row and one web
* row this app absorbs, the way a profile mounts phase one.
* @param args - the invocation's inner arguments.
* @param options - fixture knobs for the shapes a composition can take.
@@ -48,7 +48,7 @@ async function bootStartup(
// The Loader imports a row through Node's own resolver, which cannot resolve
// this workspace's sources; the row delegates to the real plugin the test
// imported through the source-plane path mapping.
writeFileSync(join(dir, 'entrypoint.mjs'), `
writeFileSync(join(dir, 'startup.mjs'), `
export const name = 'headless-startup'
export const inject = ['cmdlineArgs']
export const apply = ctx => globalThis.__headlessStartupApply(ctx)
@@ -56,7 +56,7 @@ export const apply = ctx => globalThis.__headlessStartupApply(ctx)
const rowUrl = pathToFileURL(join(dir, 'row.mjs')).href
writeFileSync(join(dir, 'cordis.yml'), [
// A composition that lost the runner still injects the service, so the
// entrypoint reaches its own row check rather than the generic one.
// startup row reaches its own row check rather than the generic one.
options.withoutRunner === true ? '- id: displaced-runner' : '- id: headless-runner',
` name: ${rowUrl}`,
` inject: [${HEADLESS_STARTUP_SERVICE}]`,
@@ -66,7 +66,8 @@ export const apply = ctx => globalThis.__headlessStartupApply(ctx)
` inject: [${WEB_STARTUP_SERVICE}]`,
' disabled: true',
'- id: headless-startup',
` name: ${pathToFileURL(join(dir, 'entrypoint.mjs')).href}`,
` name: ${pathToFileURL(join(dir, 'startup.mjs')).href}`,
' inject: [cmdlineArgs]',
'',
].join('\n'))
const observing = { write: (chunk: string) => { observed.out += chunk; return true } }