fix(runtime): separate packaged JSON-RPC resolution
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/examples/jsonrpc-demo/README.md
|
||||
README.md: fff8e78698cd3d6320606084ef5c533be7c52633
|
||||
README.zh.md: 75382b97ea1837cf1415e8a7f5004206596e168c
|
||||
README.md: 40ced3ee1fe2d3eac69b82501d417130267d7634
|
||||
README.zh.md: 451bdf7428f8265750082af240418d4653bb8595
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
English | [中文](README.zh.md)
|
||||
|
||||
Bin-only app that boots an external `cordis.yml`; its [`jsonrpc`](../../scaffold/server/README.md) entry serves SDK clients over newline-delimited stdio. The config composes the spine, backends, and serving plugin. The published bin is `dsh-jsonrpc-agent`, and `lib/bin.js` also ships as the `dsh-jsonrpc-agent-pkg` [single-executable runtime](../../../.agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.md) used by the Python SDK.
|
||||
Bin-only app that boots an external `cordis.yml`; its [`jsonrpc`](../../scaffold/server/README.md) entry serves SDK clients over newline-delimited stdio. The config composes the spine, backends, and serving plugin. The published `dsh-jsonrpc-agent` bin resolves bare plugins from the configuration project. The Python SDK's `dsh-jsonrpc-agent-pkg` [single-executable runtime](../../../.agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.md) uses `lib/packaged-bin.js` instead: packaged bare plugins resolve from its closed runtime tree, while relative plugins remain configuration-relative.
|
||||
|
||||
## Config discovery
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
只包含 bin 的应用,启动外部 `cordis.yml`;其 [`jsonrpc`](../../scaffold/server/README.md) 入口通过按换行分隔的 stdio 为 SDK 客户端提供服务。配置负责组合主干、后端和服务插件。发布的 bin 名为 `dsh-jsonrpc-agent`,`lib/bin.js` 还会作为 Python SDK 使用的 `dsh-jsonrpc-agent-pkg` [单文件可执行运行时](../../../.agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.md)交付。
|
||||
只包含 bin 的应用,启动外部 `cordis.yml`;其 [`jsonrpc`](../../scaffold/server/README.md) 入口通过按换行分隔的 stdio 为 SDK 客户端提供服务。配置负责组合主干、后端和服务插件。发布的 `dsh-jsonrpc-agent` bin 从配置项目解析裸插件。Python SDK 的 `dsh-jsonrpc-agent-pkg` [单文件可执行运行时](../../../.agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.md)改用 `lib/packaged-bin.js`:已打包的裸插件从封闭运行时包树解析,相对插件仍以配置目录为基准。
|
||||
|
||||
## 配置发现
|
||||
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
"types": "./lib/types/bin.d.ts",
|
||||
"default": "./lib/bin.js"
|
||||
},
|
||||
"./packaged-bin": {
|
||||
"types": "./lib/types/packaged-bin.d.ts",
|
||||
"default": "./lib/packaged-bin.js"
|
||||
},
|
||||
"./src/*": "./src/*",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
@@ -29,6 +33,7 @@
|
||||
"lib/index.js",
|
||||
"lib/invariant.js",
|
||||
"lib/bin.js",
|
||||
"lib/packaged-bin.js",
|
||||
"lib/types/**/*.d.ts"
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
|
||||
@@ -1,54 +1,11 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Boots an external `cordis.yml`; its `@deepseek-ai/dsh-jsonrpc` entry serves
|
||||
* newline-delimited JSON-RPC on stdio. `$DSH_CORDIS_CONFIG` wins over `argv[2]`;
|
||||
* empty or missing paths exit 1, with no default config or `DSH_SNAPSHOT` mode.
|
||||
* App-boot owns env loading, Loader guards, and settled-tree startup.
|
||||
* stdin EOF and SIGTERM dispose the root context and exit 0; SIGINT exits 130.
|
||||
* Protocol `shutdown` belongs to the server plugin. Stdout is reserved for frames.
|
||||
* Generic JSON-RPC agent bin. External configurations own their bare plugin
|
||||
* packages; the packaged runtime uses `packaged-bin.ts` instead.
|
||||
*
|
||||
* @module @deepseek-ai/dsh-jsonrpc-demo/bin
|
||||
*/
|
||||
|
||||
import { existsSync } from 'node:fs'
|
||||
import { boot, installFailLoud, loadEnv, resolveConfigPath } from '@deepseek-ai/dsh-app-boot'
|
||||
import { runJsonrpcAgent } from './runner.ts'
|
||||
|
||||
const NAME = 'dsh-jsonrpc-agent'
|
||||
|
||||
/* v8 ignore start -- composition over tested app-boot/jsonrpc and executable acceptance paths */
|
||||
installFailLoud(NAME)
|
||||
loadEnv(NAME)
|
||||
|
||||
// Env wins over argv; empty values are absent. External config defines the deployment.
|
||||
const fromEnv = process.env['DSH_CORDIS_CONFIG']
|
||||
const fromArgv = process.argv[2]
|
||||
const requested = fromEnv !== undefined && fromEnv !== ''
|
||||
? fromEnv
|
||||
: fromArgv !== undefined && fromArgv !== '' ? fromArgv : undefined
|
||||
const configPath = requested === undefined ? undefined : resolveConfigPath(requested, undefined)
|
||||
if (configPath === undefined || !existsSync(configPath)) {
|
||||
process.stderr.write(
|
||||
`usage: ${NAME} <path/to/cordis.yml> (or set DSH_CORDIS_CONFIG=<path>, which wins); the config is required — there is no built-in fallback\n`,
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// The executable owns a closed plugin set; config-adjacent node_modules must
|
||||
// not shadow the packages embedded beside this bin in the VFS.
|
||||
const ctx = await boot(NAME, configPath, undefined, undefined, import.meta.url)
|
||||
let exiting = false
|
||||
|
||||
async function disposeAndExit(code: number): Promise<void> {
|
||||
if (exiting) return
|
||||
exiting = true
|
||||
try {
|
||||
await ctx.fiber.dispose()
|
||||
} finally {
|
||||
process.exit(code)
|
||||
}
|
||||
}
|
||||
|
||||
process.stdin.on('end', () => { void disposeAndExit(0) })
|
||||
process.on('SIGTERM', () => { void disposeAndExit(0) })
|
||||
process.on('SIGINT', () => { void disposeAndExit(130) })
|
||||
/* v8 ignore stop */
|
||||
await runJsonrpcAgent()
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Bin-only app package: `bin.ts` discovers an external `cordis.yml` and owns
|
||||
* process exit. This module exports no composition plugin; the config chooses
|
||||
* whether to load the {@link @deepseek-ai/dsh-jsonrpc} serving plugin.
|
||||
* Bin-only app package: its generic and packaged entries discover an external
|
||||
* `cordis.yml` and own process exit. This module exports no composition plugin;
|
||||
* the config chooses whether to load the
|
||||
* {@link @deepseek-ai/dsh-jsonrpc} serving plugin.
|
||||
*
|
||||
* @module @deepseek-ai/dsh-jsonrpc-demo
|
||||
*/
|
||||
|
||||
11
packages/examples/jsonrpc-demo/src/packaged-bin.ts
Normal file
11
packages/examples/jsonrpc-demo/src/packaged-bin.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Closed-runtime JSON-RPC agent bin. Bare plugins resolve from the installed
|
||||
* runtime closure while relative plugins remain configuration-relative.
|
||||
*
|
||||
* @module @deepseek-ai/dsh-jsonrpc-demo/packaged-bin
|
||||
*/
|
||||
|
||||
import { runJsonrpcAgent } from './runner.ts'
|
||||
|
||||
await runJsonrpcAgent(import.meta.url)
|
||||
55
packages/examples/jsonrpc-demo/src/runner.ts
Normal file
55
packages/examples/jsonrpc-demo/src/runner.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Shared process lifecycle for the generic and closed-runtime JSON-RPC bins.
|
||||
*
|
||||
* @module @deepseek-ai/dsh-jsonrpc-demo/runner
|
||||
*/
|
||||
|
||||
import { existsSync } from 'node:fs'
|
||||
import { boot, installFailLoud, loadEnv, resolveConfigPath } from '@deepseek-ai/dsh-app-boot'
|
||||
|
||||
const NAME = 'dsh-jsonrpc-agent'
|
||||
|
||||
/**
|
||||
* Boot the explicitly selected external configuration and own process exit.
|
||||
* @param bareModuleBaseUrl - optional installed-runtime base for bare plugins;
|
||||
* omit it when the configuration project owns its plugin packages.
|
||||
* @returns after process handlers are installed; process lifetime then belongs
|
||||
* to stdin and signal events.
|
||||
*/
|
||||
export async function runJsonrpcAgent(bareModuleBaseUrl?: string): Promise<void> {
|
||||
/* v8 ignore start -- composition over tested app-boot/jsonrpc and executable acceptance paths */
|
||||
installFailLoud(NAME)
|
||||
loadEnv(NAME)
|
||||
|
||||
// Env wins over argv; empty values are absent. External config defines the deployment.
|
||||
const fromEnv = process.env['DSH_CORDIS_CONFIG']
|
||||
const fromArgv = process.argv[2]
|
||||
const requested = fromEnv !== undefined && fromEnv !== ''
|
||||
? fromEnv
|
||||
: fromArgv !== undefined && fromArgv !== '' ? fromArgv : undefined
|
||||
const configPath = requested === undefined ? undefined : resolveConfigPath(requested, undefined)
|
||||
if (configPath === undefined || !existsSync(configPath)) {
|
||||
process.stderr.write(
|
||||
`usage: ${NAME} <path/to/cordis.yml> (or set DSH_CORDIS_CONFIG=<path>, which wins); the config is required — there is no built-in fallback\n`,
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const ctx = await boot(NAME, configPath, undefined, undefined, bareModuleBaseUrl)
|
||||
let exiting = false
|
||||
|
||||
async function disposeAndExit(code: number): Promise<void> {
|
||||
if (exiting) return
|
||||
exiting = true
|
||||
try {
|
||||
await ctx.fiber.dispose()
|
||||
} finally {
|
||||
process.exit(code)
|
||||
}
|
||||
}
|
||||
|
||||
process.stdin.on('end', () => { void disposeAndExit(0) })
|
||||
process.on('SIGTERM', () => { void disposeAndExit(0) })
|
||||
process.on('SIGINT', () => { void disposeAndExit(130) })
|
||||
/* v8 ignore stop */
|
||||
}
|
||||
@@ -1,15 +1,21 @@
|
||||
import { defineConfig } from 'tsdown'
|
||||
|
||||
/**
|
||||
* Build the doc-only module and CLI entry; `tsc -b` supplies declarations.
|
||||
*/
|
||||
export default defineConfig({
|
||||
entry: ['lib/types/index.js', 'lib/types/invariant.js', 'lib/types/bin.js'],
|
||||
outDir: 'lib',
|
||||
format: ['esm'],
|
||||
platform: 'node',
|
||||
target: 'es2024',
|
||||
fixedExtension: false,
|
||||
dts: false,
|
||||
clean: false,
|
||||
})
|
||||
/** Builds each published entry as a self-contained file admitted by the package whitelist. */
|
||||
export default defineConfig([
|
||||
{
|
||||
entry: ['lib/types/index.js'], outDir: 'lib', format: ['esm'], platform: 'node', target: 'es2024',
|
||||
fixedExtension: false, outputOptions: { codeSplitting: false }, dts: false, clean: false,
|
||||
},
|
||||
{
|
||||
entry: ['lib/types/invariant.js'], outDir: 'lib', format: ['esm'], platform: 'node', target: 'es2024',
|
||||
fixedExtension: false, outputOptions: { codeSplitting: false }, dts: false, clean: false,
|
||||
},
|
||||
{
|
||||
entry: ['lib/types/bin.js'], outDir: 'lib', format: ['esm'], platform: 'node', target: 'es2024',
|
||||
fixedExtension: false, outputOptions: { codeSplitting: false }, dts: false, clean: false,
|
||||
},
|
||||
{
|
||||
entry: ['lib/types/packaged-bin.js'], outDir: 'lib', format: ['esm'], platform: 'node', target: 'es2024',
|
||||
fixedExtension: false, outputOptions: { codeSplitting: false }, dts: false, clean: false,
|
||||
},
|
||||
])
|
||||
|
||||
Reference in New Issue
Block a user