Merge remote-tracking branch 'origin/master' into worktree/custom-provider-edit-parity-f8c160

This commit is contained in:
Yichen Jiang
2026-08-11 15:16:23 +08:00
2325 changed files with 38307 additions and 17353 deletions

View File

@@ -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 docs/user/develop/basic/config.md
config.md: 02998c32415b5ba7acf82700034cabc1f7314f33
config.zh.md: 161af5d6703b4cc77d63443a5a80846593d1c7fd
config.md: 21ba39fd7de1795e9139aff3e2b11743eedd4833
config.zh.md: a882c4d59b0ac8e8ec27a5b32da5376b534a7f62

View File

@@ -9,8 +9,8 @@ Accept configuration supplied through `cordis.yml`.
Export a `Config` type and a same-named Schemastery schema. Put defaults directly on the schema fields:
```ts
import type { Context } from 'cordis'
import Schema from 'schemastery'
import type { Context } from '@deepseek-ai/cordis'
import Schema from '@deepseek-ai/schemastery'
export const name = 'my-plugin'
@@ -49,8 +49,8 @@ When loading the plugin, Cordis uses the exported schema to validate configurati
Use Schemastery to express stricter validation:
```ts
import type { Context } from 'cordis'
import Schema from 'schemastery'
import type { Context } from '@deepseek-ai/cordis'
import Schema from '@deepseek-ai/schemastery'
export const name = 'validated-plugin'

View File

@@ -9,8 +9,8 @@
在插件中导出一个 `Config` 类型和同名的 Schemastery schema默认值直接写在 schema 中:
```ts
import type { Context } from 'cordis'
import Schema from 'schemastery'
import type { Context } from '@deepseek-ai/cordis'
import Schema from '@deepseek-ai/schemastery'
export const name = 'my-plugin'
@@ -49,8 +49,8 @@ export function apply(ctx: Context, config: Config) {
对于需要严格校验的场景,使用 Schemastery 定义 schema
```ts
import type { Context } from 'cordis'
import Schema from 'schemastery'
import type { Context } from '@deepseek-ai/cordis'
import Schema from '@deepseek-ai/schemastery'
export const name = 'validated-plugin'

View File

@@ -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 docs/user/develop/basic/index.md
index.md: 7fe66bb19ddb978a4b5a96768b62151b97bca0ec
index.zh.md: 59f4e5b58b6cf1fbc15de8fafb5f4b0db2e220d6
index.md: e57a42b42690bd92450cc26876c13a1622bb80cc
index.zh.md: 240623341618acd6501f2897ae2844fde0a5b73b

View File

@@ -17,7 +17,7 @@ mkdir -p scratch-plugin/src
In Harness, a plugin is a TypeScript module that exports an `apply` function. The framework calls `apply` when loading the plugin and passes a `ctx` context object through which the plugin registers capabilities:
```ts
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
export const name = 'my-plugin'
@@ -33,7 +33,7 @@ That is the complete configuration.
Create `scratch-plugin/src/my-plugin.ts`:
```ts
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
export const name = 'hello-plugin'
@@ -56,7 +56,7 @@ Create `scratch-plugin/cordis.yml` as a Web overlay that inserts the local plugi
Start the Web UI with that overlay:
```sh
pnpm run dsh web --patch ./scratch-plugin/cordis.yml
pnpm dsh web --patch ./scratch-plugin/cordis.yml
```
Open `http://127.0.0.1:3080`. The terminal prints `[hello-plugin] plugin loaded!` during startup.
@@ -68,7 +68,7 @@ Anything registered through `ctx`—event listeners, tools, or timers—is clean
For a resource that needs explicit cleanup, such as a network connection, use `ctx.effect()` to provide its disposer:
```ts
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
export function apply(ctx: Context) {
ctx.effect(() => {
@@ -87,7 +87,7 @@ export function apply(ctx: Context) {
If the plugin consumes another service such as `tools` or `llm`, declare it in `inject`:
```ts ignore-check
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
export const name = 'my-tool-plugin'
export const inject = ['tools']
@@ -107,7 +107,7 @@ In addition to a function module, a plugin can use object or class form.
### Object form
```ts
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
export default {
name: 'my-plugin',
@@ -121,7 +121,7 @@ export default {
### Class form
```ts
import { Service, type Context } from 'cordis'
import { Service, type Context } from '@deepseek-ai/cordis'
export default class MyService extends Service {
static inject = ['tools']

View File

@@ -17,7 +17,7 @@ mkdir -p scratch-plugin/src
在 Harness 中,插件是一个导出 `apply` 函数的 TypeScript 模块。框架在加载时调用 `apply`,传入一个 `ctx`(上下文对象),你通过 `ctx` 注册能力:
```ts
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
export const name = 'my-plugin'
@@ -33,7 +33,7 @@ export function apply(ctx: Context) {
创建 `scratch-plugin/src/my-plugin.ts`
```ts
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
export const name = 'hello-plugin'
@@ -56,7 +56,7 @@ export function apply(ctx: Context) {
使用该覆盖层启动 Web UI
```sh
pnpm run dsh web --patch ./scratch-plugin/cordis.yml
pnpm dsh web --patch ./scratch-plugin/cordis.yml
```
打开 `http://127.0.0.1:3080`。启动期间,终端会打印 `[hello-plugin] plugin loaded!`
@@ -68,7 +68,7 @@ pnpm run dsh web --patch ./scratch-plugin/cordis.yml
如果你有需要手动清理的资源(比如一个网络连接),用 `ctx.effect()` 告诉框架怎么清理:
```ts
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
export function apply(ctx: Context) {
ctx.effect(() => {
@@ -87,7 +87,7 @@ export function apply(ctx: Context) {
如果你的插件需要使用其他服务(如 `tools``llm`),需要声明 `inject`
```ts ignore-check
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
export const name = 'my-tool-plugin'
export const inject = ['tools']
@@ -107,7 +107,7 @@ export function apply(ctx: Context) {
### 对象形式
```ts
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
export default {
name: 'my-plugin',
@@ -121,7 +121,7 @@ export default {
### 类形式
```ts
import { Service, type Context } from 'cordis'
import { Service, type Context } from '@deepseek-ai/cordis'
export default class MyService extends Service {
static inject = ['tools']

View File

@@ -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 docs/user/develop/basic/publish.md
publish.md: 7657654b1467c14b22e0eb6372c2bc4e77db2f38
publish.zh.md: 7af2ae3a06cc74597d5cbd6fddd46fbab069e287
publish.md: 8437c7ea5c4cb966f9f3d68977949c78986ec9a5
publish.zh.md: 4409dbfda060a84b316029d87ec985209cfa286a

View File

@@ -98,7 +98,8 @@ The effective configuration composes over an empty root by applying, in order:
2. The profile's own `cordis.patch.yml`.
3. The home-level `$DSH_HOME/cordis.patch.yml` — machine-local preferences shared by every profile.
4. Each `--patch <path>` overlay, in argv order.
5. Launcher flag patches (for example `dsh web --port`).
App arguments are not another patch layer. A surface bundle can resolve them through an ordinary app-owned service, described below.
Later layers win per row, and a patch replaces a row's entire `config` value rather than deep-merging keys. Two consequences for bundle authors:
@@ -107,6 +108,29 @@ Later layers win per row, and a patch replaces a row's entire `config` value rat
In-box bundle names always resolve from the dsh installation itself; pnpm manages only out-of-tree packages, so your bundle can rely on `@deepseek-ai/dsh-base` being present and current.
## Give a surface bundle its own command line
A bundle that defines a runnable app mounts an ordinary provider plugin:
```yaml
- id: hello-startup
name: 'dsh-hello-plugin/startup'
```
The plugin exports `inject = ['cmdlineArgs']`, calls `parseCmdline` from [`@deepseek-ai/dsh-cmdline`](../../../../packages/boot/cmdline/README.md) with its own commander program, and provides the returned value as its app-owned service. The launcher hands every plugin the same immutable arguments after launcher flags, so app-specific flags need no launcher change and multiple plugins may parse the snapshot. The Loader row needs no launcher marker or special kind.
Rows configured by those arguments inject the provider's service and read it from their own `!!js` options, with the deployment value beside it as the fallback:
```yaml
- id: my-app
name: '@example/my-app'
inject: [myAppStartup]
config:
port: !!js ctx.myAppStartup.port ?? 8080
```
On `--help`, the provider publishes no service, so those rows never activate. Loader mounts the composition once, waits for each row's ordinary injections, and only then evaluates that row's `!!js` config against its injected context.
## Installing from GitHub: the build-script catch
Publishing to a registry is not required — users can install straight from a git host:

View File

@@ -2,14 +2,14 @@
[English](publish.md) | 中文
前几篇教程通过 `--patch` overlay 加载本地插件。本教程把它打包成可安装的**组合包**,用 `dsh plugin add` 安装进一个 **profile**,并解释决定组合后配置的层顺序。请先完成[插件配置](./config.md)。
前几篇教程通过 `--patch` overlay 加载本地插件。本教程把它打包成可安装的**组合包**bundle,用 `dsh plugin add` 安装进一个 **profile**,并解释决定组合后配置的层顺序。请先完成[插件配置](./config.md)。
## 两个概念,两种 manifest(元数据清单)
## 两个概念,两种 manifest
安装机制建立在两个概念之上。二者都由一份 `package.json` 描述,但它们在 `dsh` 键下携带的 manifest 种类不同,回答的问题也不同:
安装机制建立在两个概念之上。二者都由一份 `package.json` 描述,但它们在 `dsh` 键下携带的 manifest(元数据清单)种类不同,回答的问题也不同:
- **组合包**是附带一个配置层的 npm 包。它的 manifest 声明 `dsh.bundle`,回答的是这个包贡献什么?:一个插入或覆盖插件行的 patch 文件。
- **profile** 是位于 `$DSH_HOME/profiles/<name>` 下、描述一份可启动组合的目录。它的 manifest 声明 `dsh.profile`,回答的是这套配置由哪些组合包按什么顺序组成?
- **组合包**是附带一个配置层的 npm 包。它的 manifest 声明 `dsh.bundle`,回答的是"这个包贡献什么?":一个插入或覆盖插件行的 patch 文件。
- **profile** 是位于 `$DSH_HOME/profiles/<name>` 下、描述一份可启动组合的目录。它的 manifest 声明 `dsh.profile`,回答的是"这套配置由哪些组合包按什么顺序组成?"
组合包是你编写并分发的东西profile 是用户用 `dsh --profile <name>` 启动的东西。没有东西同时是两者。
@@ -98,7 +98,8 @@ dsh --profile demo
2. profile 自己的 `cordis.patch.yml`
3. home 级的 `$DSH_HOME/cordis.patch.yml`——各 profile 共享的机器本地偏好。
4. 每个 `--patch <path>` overlay按 argv 顺序。
5. 启动器 flag patch例如 `dsh web --port`)。
应用参数不是另一层 patch。表层组合包可以通过下文所述的普通应用自有服务解析它们。
后应用的层按行胜出,且 patch 会替换目标行的整个 `config` 值,而不是深度合并各键。这给组合包作者带来两个推论:
@@ -107,6 +108,29 @@ dsh --profile demo
内置组合包名称始终从 dsh 安装目录本身解析pnpm 只管理树外的包,所以你的组合包可以放心依赖 `@deepseek-ai/dsh-base` 存在且与安装保持一致。
## 让表层组合包持有自己的命令行
定义了可运行应用的组合包挂载一个普通提供方插件:
```yaml
- id: hello-startup
name: 'dsh-hello-plugin/startup'
```
该插件导出 `inject = ['cmdlineArgs']`,使用自己的 commander program 调用 [`@deepseek-ai/dsh-cmdline`](../../../../packages/boot/cmdline/README.md) 中的 `parseCmdline`,再把返回值作为应用自有服务提供出去。启动器把自身 flag 之后的同一份不可变参数交给每个插件,因此添加应用专属 flag 无需修改启动器多个插件也可以解析该快照。Loader 行不需要启动器标记或特殊类型。
受这些参数配置的行会注入提供方服务,并在自己的 `!!js` 选项中读取它,同时把部署取值写在旁边作为回退:
```yaml
- id: my-app
name: '@example/my-app'
inject: [myAppStartup]
config:
port: !!js ctx.myAppStartup.port ?? 8080
```
遇到 `--help`提供方不会发布该服务所以这些行不会激活。Loader 只挂载一次组合,等待每一行的普通注入,再基于其已注入的上下文求值该行的 `!!js` 配置。
## 从 GitHub 安装:构建脚本这道坎
发布到注册表不是必须的——用户可以直接从 git 托管安装:
@@ -127,7 +151,7 @@ dsh plugin --profile demo add github:you/hello-plugin
然后重新执行 `add`。
请如实看待这项授权:**允许该包的代码在安装时于你的机器上执行**,且不在 agent(智能体)运行的任何沙箱之内。只对源码可信的包授权,并锁定 commit`github:you/hello-plugin#<sha>`),让后续推送无法悄悄改变实际运行的内容。
请如实看待这项授权:**允许该包的代码在安装时于你的机器上执行**,且不在 agent 运行的任何沙箱之内。只对源码可信的包授权,并锁定 commit`github:you/hello-plugin#<sha>`),让后续推送无法悄悄改变实际运行的内容。
如果不想让用户做这项授权,就改为分发构建产物——以下两种形式都不需要任何构建权限:

View File

@@ -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 docs/user/develop/basic/tool.md
tool.md: ba2f3b1302ba31735d67be264f498a0395394d06
tool.zh.md: 676f8fc996d752a05d94b55d4522e61e3b3e2161
tool.md: a149f6876c573c9ece4b066ef6c211f69b0b03a8
tool.zh.md: 1f9a1172bcde33a1d7319492f15686c47de01174

View File

@@ -9,7 +9,7 @@ This tutorial adds a `greet` tool to the Web UI. Complete [Your first plugin](./
Replace `scratch-plugin/src/my-plugin.ts` with:
```ts
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'greet-tool'
@@ -40,7 +40,7 @@ export function apply(ctx: Context) {
Restart the development command if it is not running:
```sh
pnpm run dsh web --patch ./scratch-plugin/cordis.yml
pnpm dsh web --patch ./scratch-plugin/cordis.yml
```
Open `http://127.0.0.1:3080` and ask: `Use the greet tool to greet Ada.` The model can call `greet` and receives `Hello, Ada!` as the tool result.

View File

@@ -9,7 +9,7 @@
`scratch-plugin/src/my-plugin.ts` 替换为:
```ts
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'greet-tool'
@@ -40,7 +40,7 @@ export function apply(ctx: Context) {
如果开发命令未在运行,请重新启动:
```sh
pnpm run dsh web --patch ./scratch-plugin/cordis.yml
pnpm dsh web --patch ./scratch-plugin/cordis.yml
```
打开 `http://127.0.0.1:3080`,然后输入:`Use the greet tool to greet Ada.` 模型可以调用 `greet`,并收到 `Hello, Ada!` 这一工具结果。

View File

@@ -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 docs/user/develop/framework/events.md
events.md: 8a8c076d9c7b40d73182db074c4f494fded8c6dd
events.zh.md: 9649d89d575a1b05fa524bd460043da71dc9ae43
events.md: 4b5f9ee215186398ee5aee7a438f792f9b5a3639
events.zh.md: b48c8020803239d3c9636f81052d3b70afa315f2

View File

@@ -85,9 +85,9 @@ A waterfall listener **must call `next()`**. Omitting it short-circuits the pipe
Harness uses TypeScript declaration merging for type-safe events:
```ts
import 'cordis'
import '@deepseek-ai/cordis'
declare module 'cordis' {
declare module '@deepseek-ai/cordis' {
interface Events {
'my-plugin/ready': (payload: { id: string }) => void
'my-plugin/check': (input: string) => boolean | undefined
@@ -121,7 +121,7 @@ export function apply(ctx: Context) {
This plugin logs tool calls and results:
```ts
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import '@deepseek-ai/dsh-tools'
export const name = 'tool-logger'

View File

@@ -85,9 +85,9 @@ waterfall 监听器**必须调用 `next()`**。不调用 `next` 会短路整个
Harness 使用 TypeScript 声明合并来为事件提供类型安全:
```ts
import 'cordis'
import '@deepseek-ai/cordis'
declare module 'cordis' {
declare module '@deepseek-ai/cordis' {
interface Events {
'my-plugin/ready': (payload: { id: string }) => void
'my-plugin/check': (input: string) => boolean | undefined
@@ -121,7 +121,7 @@ export function apply(ctx: Context) {
这个插件记录工具调用和工具结果:
```ts
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import '@deepseek-ai/dsh-tools'
export const name = 'tool-logger'

View File

@@ -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 docs/user/develop/framework/index.md
index.md: 79e925b54509da41535735527e283850384257ec
index.zh.md: 962677dc468c9cc233a51d50758247e028d9c3ed
index.md: 85701ce281d92da0c805b39291179df73eb65f51
index.zh.md: 871aa55ef81a7dcbfe3cbde5986244220ee32f98

View File

@@ -80,7 +80,7 @@ export function apply(ctx: Context) {
To stop a plugin instance early:
```ts
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
declare const ctx: Context
declare function myPlugin(ctx: Context): void
@@ -98,7 +98,7 @@ await fiber.dispose()
## Hot replacement (HMR)
With `@cordisjs/plugin-hmr` loaded from `cordis.yml`, editing a plugin source file triggers:
With `@deepseek-ai/cordis-plugin-hmr` loaded from `cordis.yml`, editing a plugin source file triggers:
1. Unload the old plugin and clean up its registrations.
2. Load the new code.

View File

@@ -80,7 +80,7 @@ export function apply(ctx: Context) {
当你需要提前终止一个插件实例:
```ts
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
declare const ctx: Context
declare function myPlugin(ctx: Context): void
@@ -98,7 +98,7 @@ await fiber.dispose()
## HMR热模块替换
通过 `cordis.yml` 加载 `@cordisjs/plugin-hmr` 后,修改插件源文件会触发:
通过 `cordis.yml` 加载 `@deepseek-ai/cordis-plugin-hmr` 后,修改插件源文件会触发:
1. 卸载旧插件(清理所有注册)
2. 重新加载新代码

View File

@@ -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 docs/user/develop/framework/service.md
service.md: 040b1388cc431c30045e05f7d372ab5885bb3f9d
service.zh.md: 0786b684c1688440a24cc729288835ad636f8ff7
service.md: 3358f82ca5391741a7f531b7504de7335959ad03
service.zh.md: 8fb4beeac43051c0f08483fe61e22c588127c360

View File

@@ -36,7 +36,7 @@ When `apply` runs, every service declared by `inject` is ready. If a service is
### Extend Service
```ts
import { Service, type Context } from 'cordis'
import { Service, type Context } from '@deepseek-ai/cordis'
export default class MetricsService extends Service {
static inject = ['llm'] // A service may depend on other services.
@@ -67,9 +67,9 @@ export function apply(ctx: Context) {
Use TypeScript declaration merging to type `ctx.metrics`:
```ts
import { Service, type Context } from 'cordis'
import { Service, type Context } from '@deepseek-ai/cordis'
declare module 'cordis' {
declare module '@deepseek-ai/cordis' {
interface Context {
metrics: MetricsService
}
@@ -114,7 +114,7 @@ This prevents a plugin from calling a service that no longer exists.
```yaml
- id: group-a
name: '@cordisjs/plugin-group'
name: '@deepseek-ai/cordis-plugin-group'
group: true
isolate:
bash: true
@@ -125,7 +125,7 @@ This prevents a plugin from calling a service that no longer exists.
- name: './src/plugin-a.ts'
- id: group-b
name: '@cordisjs/plugin-group'
name: '@deepseek-ai/cordis-plugin-group'
group: true
isolate:
bash: true

View File

@@ -36,7 +36,7 @@ export function apply(ctx: Context) {
### 使用 Service 基类
```ts
import { Service, type Context } from 'cordis'
import { Service, type Context } from '@deepseek-ai/cordis'
export default class MetricsService extends Service {
static inject = ['llm'] // A service may depend on other services.
@@ -67,9 +67,9 @@ export function apply(ctx: Context) {
使用 TypeScript 声明合并让 `ctx.metrics` 有正确类型:
```ts
import { Service, type Context } from 'cordis'
import { Service, type Context } from '@deepseek-ai/cordis'
declare module 'cordis' {
declare module '@deepseek-ai/cordis' {
interface Context {
metrics: MetricsService
}
@@ -114,7 +114,7 @@ export function apply(ctx: Context) {
```yaml
- id: group-a
name: '@cordisjs/plugin-group'
name: '@deepseek-ai/cordis-plugin-group'
group: true
isolate:
bash: true
@@ -125,7 +125,7 @@ export function apply(ctx: Context) {
- name: './src/plugin-a.ts'
- id: group-b
name: '@cordisjs/plugin-group'
name: '@deepseek-ai/cordis-plugin-group'
group: true
isolate:
bash: true

View File

@@ -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 docs/user/develop/practice/index.md
index.md: 1eb33e17ab6c5d0a2b37ff97d5948dfbcba497ca
index.zh.md: 31afa80407f81f571615b5ed68a9370775f5188f
index.md: 7ca9f0b1abe472dc90c6d4e56543e43b6d2ec727
index.zh.md: 216b1cb01b355e411bf1949c59fd3720ae47139e

View File

@@ -61,9 +61,9 @@ The [capability-seam reference](../../../capability-seams.md) owns the current b
```ts ignore-check
// packages/my-cap/my-cap/src/index.ts
import { Service, type Context } from 'cordis'
import { Service, type Context } from '@deepseek-ai/cordis'
declare module 'cordis' {
declare module '@deepseek-ai/cordis' {
interface Context {
myCap: MyCapService
}
@@ -91,7 +91,7 @@ export interface MyCapResult {
```ts ignore-check
// packages/my-cap/my-cap-local/src/index.ts
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import { MyCapService, type MyCapRequest, type MyCapResult } from '@deepseek-ai/dsh-my-cap'
class MyCapLocal extends MyCapService {
@@ -112,7 +112,7 @@ export function apply(ctx: Context) {
```ts ignore-check
// packages/my-cap/tool-my-cap/src/index.ts
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'tool-my-cap'

View File

@@ -61,9 +61,9 @@
```ts ignore-check
// packages/my-cap/my-cap/src/index.ts
import { Service, type Context } from 'cordis'
import { Service, type Context } from '@deepseek-ai/cordis'
declare module 'cordis' {
declare module '@deepseek-ai/cordis' {
interface Context {
myCap: MyCapService
}
@@ -91,7 +91,7 @@ export interface MyCapResult {
```ts ignore-check
// packages/my-cap/my-cap-local/src/index.ts
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import { MyCapService, type MyCapRequest, type MyCapResult } from '@deepseek-ai/dsh-my-cap'
class MyCapLocal extends MyCapService {
@@ -112,7 +112,7 @@ export function apply(ctx: Context) {
```ts ignore-check
// packages/my-cap/tool-my-cap/src/index.ts
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'tool-my-cap'

View File

@@ -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 docs/user/develop/practice/llm-adapter.md
llm-adapter.md: 7445688530c1ba61e5c065f9f5e49db6498da5b1
llm-adapter.zh.md: c726735ff2679584d1c061ef8acddc8981dadd26
llm-adapter.md: aba4a6d0c8ee42e78ca5a804d9a0dd9b31c1e240
llm-adapter.zh.md: dff9eef464599823d6cd99e83d668485109b2ec0

View File

@@ -11,8 +11,8 @@ An LLM adapter extends `LlmAdapter` and implements `stream()`, translating Harne
## Minimal implementation
```ts
import type { Context } from 'cordis'
import Schema from 'schemastery'
import type { Context } from '@deepseek-ai/cordis'
import Schema from '@deepseek-ai/schemastery'
import { LlmAdapter, type GenerateOptions, type StreamChunk } from '@deepseek-ai/dsh-llm'
class MyAdapter extends LlmAdapter {

View File

@@ -11,8 +11,8 @@ LLM 适配器是一个继承 `LlmAdapter` 并实现 `stream()` 方法的类,
## 最小实现
```ts
import type { Context } from 'cordis'
import Schema from 'schemastery'
import type { Context } from '@deepseek-ai/cordis'
import Schema from '@deepseek-ai/schemastery'
import { LlmAdapter, type GenerateOptions, type StreamChunk } from '@deepseek-ai/dsh-llm'
class MyAdapter extends LlmAdapter {

View File

@@ -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 docs/user/guide/config.md
config.md: cd778065801ae58a46703ae3447f835f80abf062
config.zh.md: 6f6d37bfe8f7ad29c154d65c1763279655006435
config.md: 1d3ad5ce36d4b360ba5156b6be28a6caae4a23d4
config.zh.md: 7f8bfaa77066f2976a5667e3ac402814a7afdf96

View File

@@ -18,6 +18,10 @@ A minimal configuration is a list of plugin entries:
```yaml
- id: llm-deepseek
name: '@deepseek-ai/dsh-llm-deepseek'
config:
apiKey: !!js process.env.DEEPSEEK_API_KEY
models:
- deepseek-v4-flash
- id: bash
name: '@deepseek-ai/dsh-bash-local'
@@ -47,16 +51,17 @@ Cordis starts sibling entries concurrently. A plugin declares required services
## CLI patch layers
`dsh --profile <name>` composes the profile's bundle patch layers (its manifest's `dsh.profile.bundles` list, in order) over an empty root, then the profile's own `~/.dsh/profiles/<name>/cordis.patch.yml`, then each `--patch <path>` overlay, then CLI-flag patches. Later layers win per row.
`dsh --profile <name>` composes the profile's bundle patch layers (its manifest's `dsh.profile.bundles` list, in order) over an empty root, then the profile's own `~/.dsh/profiles/<name>/cordis.patch.yml`, the home-level `$DSH_HOME/cordis.patch.yml`, and each `--patch <path>` overlay. Later layers win per row. App flags are not another patch layer: an ordinary bundle plugin injects `cmdlineArgs` and provides parsed values as its own service, while rows that inject and retain a `!!js` read of that service give the invocation value precedence.
A patch replaces a row's entire `config` value; it does not deep-merge keys. For example, patching `llm-deepseek` with only `config: { thinking: disabled }` also removes that row's configured `apiKeyEnv` and `baseURL`, so restate every key the row must retain.
A patch replaces a row's entire `config` value; it does not deep-merge keys. For example, patching `llm-deepseek` with only `config: { thinking: disabled }` also removes that row's configured `apiKey` and `baseURL`, so restate every key the row must retain.
## JavaScript values and environment variables
The Cordis loader evaluates runtime expressions tagged with `!!js` for non-secret runtime values. Bundled LLM adapters carry credential references such as `apiKeyEnv`; the value belongs in an environment layer or `$DSH_HOME/.credentials.yaml`, not Cordis configuration.
The Cordis loader evaluates runtime expressions tagged with `!!js`. Keep API keys and other secrets in the gitignored `.env` file at the repository root, never in committed configuration.
```yaml
config:
apiKey: !!js process.env.DEEPSEEK_API_KEY
cwd: !!js process.cwd()
```

View File

@@ -18,6 +18,10 @@ Harness 使用 `cordis.yml` 描述 agent智能体加载哪些插件以及
```yaml
- id: llm-deepseek
name: '@deepseek-ai/dsh-llm-deepseek'
config:
apiKey: !!js process.env.DEEPSEEK_API_KEY
models:
- deepseek-v4-flash
- id: bash
name: '@deepseek-ai/dsh-bash-local'
@@ -47,16 +51,17 @@ Cordis 会并发启动同级配置项。插件通过 `inject` 声明必需服务
## CLI 补丁层
`dsh --profile <name>` 按该 profile 的 manifest元数据清单`dsh.profile.bundles` 列表的顺序,在空根之上组合各组合包补丁层,随后依次应用该 profile 自己的 `~/.dsh/profiles/<name>/cordis.patch.yml`、home 级 `$DSH_HOME/cordis.patch.yml`每个 `--patch <path>` overlay,最后是 CLI命令行界面标志补丁。同一行以较后的层为准。
`dsh --profile <name>` 按该 profile 的 manifest元数据清单`dsh.profile.bundles` 列表的顺序,在空根之上组合各组合包补丁层,随后依次应用该 profile 自己的 `~/.dsh/profiles/<name>/cordis.patch.yml`、home 级 `$DSH_HOME/cordis.patch.yml`每个 `--patch <path>` overlay。同一行以较后的层为准。应用 flag 并不是另一层 patch组合包中的普通插件注入 `cmdlineArgs`,再把解析值作为自身服务提供;注入该服务并保留其 `!!js` 读取的行会让本次调用的取值优先
补丁会替换目标行的整个 `config` 值,而不是深度合并各个键。例如,只用 `config: { thinking: disabled }` 修补 `llm-deepseek`,也会移除该行原有的 `apiKeyEnv``baseURL`;因此必须重新写出该行需要保留的全部键。
补丁会替换目标行的整个 `config` 值,而不是深度合并各个键。例如,只用 `config: { thinking: disabled }` 修补 `llm-deepseek`,也会移除该行原有的 `apiKey``baseURL`;因此必须重新写出该行需要保留的全部键。
## JavaScript 值和环境变量
Cordis loader 会求值以 `!!js`记的运行时表达式,用于非机密的运行时值。仓库内置的 LLM大语言模型适配器携带 `apiKeyEnv` 等凭据引用;对应的值应放在环境层或 `$DSH_HOME/.credentials.yaml`,而不是 Cordis 配置中
Cordis loader 使用 `!!js`签读取运行时表达式。API key 等凭据应放在仓库根目录、已被 Git 忽略的 `.env` 中,不能提交到配置文件
```yaml
config:
apiKey: !!js process.env.DEEPSEEK_API_KEY
cwd: !!js process.cwd()
```
@@ -64,4 +69,4 @@ config:
## 精确配置参考
每个插件当前支持的字段、类型和默认值见自动生成的[插件配置目录](../../config-catalog.md)。理解插件如何组合可继续阅读[架构说明](../../architecture.md)和[能力 seam](../../capability-seams.md);要创建自己的配置,优先复制并修改[示例目录说明](../../../examples/README.md)中最接近的例子。
每个插件当前支持的字段、类型和默认值见自动生成的[插件配置目录](../../config-catalog.md)。理解插件如何组合可继续阅读[架构说明](../../architecture.md)和[能力接口](../../capability-seams.md);要创建自己的配置,优先复制并修改[示例目录说明](../../../examples/README.md)中最接近的例子。

View File

@@ -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 docs/user/guide/providers.md
providers.md: b5217ca7eaa00e7f31db4b3269ddb0d489fe3923
providers.zh.md: a27adefdbb76c43d099982b0503a9adde139597f
providers.md: 0e7ed11d1b09a8361d75b576a400978ac66d08a7
providers.zh.md: 060bf3dc41b773e89cd0d78de921c3a20cfc6076

View File

@@ -15,7 +15,7 @@ Adding a provider therefore rarely means editing `cordis.yml` — writing settin
## Configure from the web UI
Start `pnpm run dsh web` and open **Settings → Models**.
Start `pnpm dsh web` and open **Settings → Models**.
![The Models page: the DeepSeek card, with Add provider and Add a custom provider below it](providers-models-page.png)

View File

@@ -15,7 +15,7 @@ Harness 出厂自带 DeepSeek同时预装了一个通用的多提供方适配
## 在 Web 界面里配置
启动 `pnpm run dsh web`,打开**设置 → 模型**。
启动 `pnpm dsh web`,打开**设置 → 模型**。
![模型页DeepSeek 卡片,以及添加提供方与添加自定义提供方两个入口](providers-models-page.zh.png)

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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 docs/user/guide/python-sdk.md
python-sdk.md: 0713de6f96c110624d5f5a8f2ccf56dd4ce01096
python-sdk.zh.md: f8fa877b407a43c643fda405e1059fb487182761

View File

@@ -0,0 +1,130 @@
# Get started with the Python SDK
English | [中文](python-sdk.zh.md)
This tutorial installs the Python SDK, runs a checked-in Cordis composition without the Web UI, and uses the same API in your own program. It uses the compact [`minimal.cordis.yml`](../../../examples/jsonrpc-agent/minimal.cordis.yml) configuration as a complete example with a fixed system prompt, tool catalog, persistent-shell behavior, and compaction policy.
## Prerequisites
- Python 3.10 or newer
- Linux x64, Linux arm64, or macOS arm64
- A DeepSeek-compatible API endpoint and credential
- An isolated workspace that the agent may modify
## Install the SDK
Choose either the public package or a source build. Both install the `deepseek-harness-sdk` distribution and expose the `deepseek_harness` Python module.
### Install from PyPI
Create a virtual environment and install the SDK with its same-version bundled runtime:
```sh
python -m venv .venv
. .venv/bin/activate
python -m pip install deepseek-harness-sdk
```
### Build from source
A source build additionally requires Git, Node.js ^22.19 or >= 24, Corepack-enabled pnpm 11, and `uv`. The following commands build the runtime for the current supported host platform, build both wheels, and install them into the active virtual environment:
```sh
git clone https://github.com/deepseek-ai/deepseek-harness.git deepseek-harness
cd deepseek-harness
python -m pip install uv==0.11.23
corepack enable
pnpm install
case "$(uname -s):$(uname -m)" in
Linux:x86_64) runtime_platform=linux-x64 ;;
Linux:aarch64|Linux:arm64) runtime_platform=linux-arm64 ;;
Darwin:arm64) runtime_platform=macos-arm64 ;;
*) echo "unsupported platform" >&2; exit 1 ;;
esac
pnpm exec tsx scripts/build-exe-for-python-sdk.ts --targets="node24-$runtime_platform"
version="$(node -p "require('./package.json').version")"
python scripts/build-python-release.py --package sdk --output-dir dist-python
python scripts/build-python-release.py \
--package runtime \
--platform "$runtime_platform" \
--runtime-exe "dist-exe/dsh-jsonrpc-agent-pkg-$runtime_platform" \
--output-dir dist-python
python -m pip install --find-links dist-python "deepseek-harness-sdk==$version"
```
The runtime wheel contains the JSON-RPC executable and every plugin used by the complete [`minimal.cordis.yml`](../../../examples/jsonrpc-agent/minimal.cordis.yml), so neither installation path needs Node.js after installation.
## Run the checked-in example
Set the credential in the environment. Set `DEEPSEEK_BASE_URL` as well when the model is served by an OpenAI-compatible proxy rather than the default DeepSeek endpoint.
```sh
export DEEPSEEK_API_KEY=sk-your-key-here
# export DEEPSEEK_BASE_URL=http://127.0.0.1:8000/v1
```
Run one task from the repository checkout:
```sh
python examples/jsonrpc-agent/minimal.py \
--workspace /absolute/path/to/workspace \
--session-root /absolute/path/to/sessions \
--session-id example-001 \
"Inspect the repository and fix the failing tests."
```
The script prints the final assistant response. The session root receives a JSONL session log containing the assembled model request and every tool call.
## Use the SDK in your own program
The example is a thin wrapper around this SDK call:
```python
from pathlib import Path
from deepseek_harness import DeepSeekHarness
config = Path("examples/jsonrpc-agent/minimal.cordis.yml").resolve()
workspace = Path("/absolute/path/to/workspace").resolve()
sessions = Path("/absolute/path/to/sessions").resolve()
with DeepSeekHarness(
provider="deepseek-official",
model="deepseek-v4-flash",
max_tokens=49_152,
cwd=str(workspace),
session_root=str(sessions),
cordis=str(config),
) as harness:
result = harness.run(
"Inspect the repository and fix the failing tests.",
session_id="example-001",
)
print(result.final_response)
```
`DeepSeekHarness` starts the bundled JSON-RPC runtime lazily and reuses it until the context manager exits. Reusing the same harness and session id across calls also preserves the session-owned Bash process, including its working directory, exported variables, and shell functions.
## Understand the example configuration
| Surface | Fixed value |
|---|---|
| System prompt | `You are a helpful software engineer assistant.` |
| Model-facing tools | Persistent `bash` and `str_replace_editor` only |
| Bash timeout | 300 seconds |
| Editor output limit | 16,000 characters |
| Compaction | Trigger ratio `0.8`, retain `20,480` tokens, summary cap `8,192` tokens, one retry |
| Session persistence | Uncompressed JSONL under `DSH_SESSION_ROOT` |
The configuration omits harness identity, workspace prompt text, skills, one-shot Bash, task tools, and every other model-facing plugin. Filesystem policy facts are logged as runtime user context rather than appended to the system prompt. The editor requires absolute paths as an unconditional current contract, so the obsolete `requireAbsolutePath` option is absent.
## Choose workspace and session IDs
`cwd` selects the workspace available to the agent, while `session_root` stores session logs and state. Use a fresh session id for an independent task; reuse an id only when the next call should continue the same conversation and persistent shell state.
The composition uses `danger-full-access`. Run it only inside a disposable checkout or container: Bash and the editor can modify any path allowed to the runtime process. The persistent PTY backend requires a POSIX terminal substrate and is not a Windows agent surface.
For the complete SDK lifecycle and result contract, see the [Python SDK reference](../../../python/sdk/README.md). For Cordis composition syntax, see [Configuration](./config.md).

View File

@@ -0,0 +1,130 @@
# Python SDK 快速上手
[English](python-sdk.md) | 中文
本教程介绍如何安装 Python SDK、在不使用 Web UI 的情况下运行仓库内置 Cordis 组合,以及如何在自己的程序中调用同一套 API。教程使用精简且完整的 [`minimal.cordis.yml`](../../../examples/jsonrpc-agent/minimal.cordis.yml) 作为示例,其中固定了系统提示词、工具目录、持久 shell 行为和压缩compaction策略。
## 前置要求
- Python 3.10 或更高版本
- Linux x64、Linux arm64 或 macOS arm64
- DeepSeek 兼容的 API 端点与凭据
- agent 可以修改的隔离 workspace
## 安装 SDK
可以选择安装公开包或从源码构建。两种方式都会安装 `deepseek-harness-sdk` 分发包,并提供 `deepseek_harness` Python 模块。
### 从 PyPI 安装
请创建虚拟环境,并安装 SDK 及其同版本内置运行时:
```sh
python -m venv .venv
. .venv/bin/activate
python -m pip install deepseek-harness-sdk
```
### 从源码构建
从源码构建还需要 Git、Node.js ^22.19 或 >= 24、通过 Corepack 启用的 pnpm 11以及 `uv`。以下命令为当前受支持的宿主平台构建运行时和两个 wheel 包,并将它们安装进当前虚拟环境:
```sh
git clone https://github.com/deepseek-ai/deepseek-harness.git deepseek-harness
cd deepseek-harness
python -m pip install uv==0.11.23
corepack enable
pnpm install
case "$(uname -s):$(uname -m)" in
Linux:x86_64) runtime_platform=linux-x64 ;;
Linux:aarch64|Linux:arm64) runtime_platform=linux-arm64 ;;
Darwin:arm64) runtime_platform=macos-arm64 ;;
*) echo "unsupported platform" >&2; exit 1 ;;
esac
pnpm exec tsx scripts/build-exe-for-python-sdk.ts --targets="node24-$runtime_platform"
version="$(node -p "require('./package.json').version")"
python scripts/build-python-release.py --package sdk --output-dir dist-python
python scripts/build-python-release.py \
--package runtime \
--platform "$runtime_platform" \
--runtime-exe "dist-exe/dsh-jsonrpc-agent-pkg-$runtime_platform" \
--output-dir dist-python
python -m pip install --find-links dist-python "deepseek-harness-sdk==$version"
```
运行时 wheel 包含 JSON-RPC 可执行文件,以及完整 [`minimal.cordis.yml`](../../../examples/jsonrpc-agent/minimal.cordis.yml) 使用的每个插件,因此两种安装方式完成后都不再需要 Node.js。
## 运行仓库内置示例
请在环境中设置凭据。如果模型不是由默认 DeepSeek 端点提供,而是通过 OpenAI 兼容代理提供,还需要设置 `DEEPSEEK_BASE_URL`
```sh
export DEEPSEEK_API_KEY=sk-your-key-here
# export DEEPSEEK_BASE_URL=http://127.0.0.1:8000/v1
```
从仓库 checkout 运行一个任务:
```sh
python examples/jsonrpc-agent/minimal.py \
--workspace /absolute/path/to/workspace \
--session-root /absolute/path/to/sessions \
--session-id example-001 \
"Inspect the repository and fix the failing tests."
```
脚本会打印 assistant 的最终回复。会话根目录会收到 JSONL 会话日志,其中包含组装后的模型请求与每次工具调用。
## 在自己的程序中使用 SDK
该示例是以下 SDK 调用的轻量包装层:
```python
from pathlib import Path
from deepseek_harness import DeepSeekHarness
config = Path("examples/jsonrpc-agent/minimal.cordis.yml").resolve()
workspace = Path("/absolute/path/to/workspace").resolve()
sessions = Path("/absolute/path/to/sessions").resolve()
with DeepSeekHarness(
provider="deepseek-official",
model="deepseek-v4-flash",
max_tokens=49_152,
cwd=str(workspace),
session_root=str(sessions),
cordis=str(config),
) as harness:
result = harness.run(
"Inspect the repository and fix the failing tests.",
session_id="example-001",
)
print(result.final_response)
```
`DeepSeekHarness` 会延迟启动内置 JSON-RPC 运行时,并持续复用,直至退出上下文管理器。在多次调用中复用同一个 harness 和 session id还会保留该会话拥有的 Bash 进程,包括其工作目录、已导出的变量与 shell 函数。
## 了解示例配置
| 方面 | 固定值 |
|---|---|
| 系统提示词 | `You are a helpful software engineer assistant.` |
| 面向模型的工具 | 仅持久 `bash``str_replace_editor` |
| Bash 超时 | 300 秒 |
| 编辑器输出上限 | 16,000 个字符 |
| 压缩 | 触发比例 `0.8`、保留 `20,480` 个 token、摘要上限 `8,192` 个 token、重试 1 次 |
| 会话持久化 | `DSH_SESSION_ROOT` 下未压缩的 JSONL |
该配置省略了 harness 身份、workspace 提示词文本、skill技能、一次性 Bash、任务工具和其他所有面向模型的插件。文件系统策略事实记录为运行时用户上下文而不会追加到系统提示词中。编辑器无条件要求绝对路径因此配置中没有已经废弃的 `requireAbsolutePath` 选项。
## 选择 workspace 与 session id
`cwd` 用于选择 agent 可访问的 workspace`session_root` 用于保存会话日志和状态。独立任务应使用新的 session id只有下一次调用需要延续同一段对话和持久 shell 状态时,才复用原有 id。
该组合使用 `danger-full-access`。只能在可丢弃的 checkout 或容器内运行Bash 与编辑器可以修改运行时进程有权访问的任何路径。持久 PTY 后端需要 POSIX 终端环境,因此该模式不适用于 Windows agent。
完整的 SDK 生命周期与结果约定见 [Python SDK 参考](../../../python/sdk/README.md)。Cordis 组合语法见[配置](./config.md)。

View File

@@ -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 docs/user/guide/quickstart.md
quickstart.md: ce196641f205324334533c025b4ac1dc791f857d
quickstart.zh.md: 3a5d6d0748ec0c7ec83c74570d0fad1e8d66a97c
quickstart.md: e93e5a430f0cb345728581cd6fa3175ffd20b7d1
quickstart.zh.md: 69cde830bb802ef19cc1204685395b957a0e02e3

View File

@@ -19,10 +19,9 @@ pnpm -v
## Step 1: install and configure the API key
```sh
git clone https://github.com/deepseek-ai/deepseek-harness-sdk.git
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
pnpm install
pnpm run build
```
Create the gitignored repository-root `.env`:
@@ -36,27 +35,28 @@ DEEPSEEK_API_KEY=sk-your-key-here
Run a non-interactive task and print its final answer:
```sh
pnpm run dsh run "summarize the architecture of this workspace"
pnpm dsh --profile headless "summarize the architecture of this workspace"
```
`dsh run` creates and persists a fresh session, prints the final assistant answer, and exits. It starts no Web server or listening port, and a successful run leaves stderr empty.
`dsh --profile headless` creates and persists a fresh session, prints the final assistant answer, and exits. It starts no Web server or listening port, and a successful run leaves stderr empty.
## Step 3: use the Web UI
Start the browser interface:
```sh
pnpm run dsh web
pnpm dsh web
```
Open `http://127.0.0.1:3080`. The agent can read and write files, run commands, delegate subtasks, and track a plan. Try: `Create hello.js in the current directory, print "Hello from Harness!", and run it`.
## What happened
`dsh run` boots the `headless` profile: [`dsh-base`](../../../packages/bundle/base/cordis.patch.yml) and [`dsh-headless`](../../../packages/bundle/headless/cordis.patch.yml) compose over an empty root, then the runner drives the core Agent and Session services directly. `dsh web` instead composes `dsh-base` with [`dsh-web-app`](../../../packages/bundle/web-app/cordis.patch.yml), which owns the Host, HTTP, and browser layers. Both read the same default DeepSeek model route from `dsh-base`.
`dsh --profile headless` boots the `headless` profile: [`dsh-base`](../../../packages/bundle/base/cordis.patch.yml) and [`dsh-headless`](../../../packages/bundle/headless/cordis.patch.yml) compose over an empty root, then the runner drives the core Agent and Session services directly. `dsh web` instead composes `dsh-base` with [`dsh-web-app`](../../../packages/bundle/web-app/cordis.patch.yml), which owns the Host, HTTP, and browser layers. Both read the same default DeepSeek model route from `dsh-base`.
## Next steps
- [Get started with the Python SDK](./python-sdk.md) — install the SDK and run a complete Cordis configuration without the Web UI
- [Configure models](./providers.md) — reach providers beyond DeepSeek, and custom gateways
- [Configuration](./config.md) — understand the `cordis.yml` format
- [Develop a plugin](../develop/basic/) — build your own tool or backend

View File

@@ -19,10 +19,9 @@ pnpm -v
## 第一步:安装并配置 API 密钥
```sh
git clone https://github.com/deepseek-ai/deepseek-harness-sdk.git
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
pnpm install
pnpm run build
```
在仓库根目录创建已被 Git 忽略的 `.env`
@@ -36,27 +35,28 @@ DEEPSEEK_API_KEY=sk-your-key-here
运行一个非交互式任务并打印最终回答:
```sh
pnpm run dsh run "summarize the architecture of this workspace"
pnpm dsh --profile headless "summarize the architecture of this workspace"
```
`dsh run` 创建并持久化一个新会话,打印最终助手回答,然后退出。它不会启动 Web 服务器或监听端口;成功运行时 stderr 为空。
`dsh --profile headless` 创建并持久化一个新会话,打印最终助手回答,然后退出。它不会启动 Web 服务器或监听端口;成功运行时 stderr 为空。
## 第三步:使用 Web UI
启动浏览器界面:
```sh
pnpm run dsh web
pnpm dsh web
```
打开 `http://127.0.0.1:3080`。agent 可以读写文件、运行命令、分配子任务和跟踪计划。可以尝试:`Create hello.js in the current directory, print "Hello from Harness!", and run it`
## 运行原理
`dsh run` 启动 `headless` profile[`dsh-base`](../../../packages/bundle/base/cordis.patch.yml) 和 [`dsh-headless`](../../../packages/bundle/headless/cordis.patch.yml) 在空根之上组合,随后 runner 直接驱动 core Agent 与 Session 服务。`dsh web` 则由 `dsh-base` 与 [`dsh-web-app`](../../../packages/bundle/web-app/cordis.patch.yml) 组合,后者拥有 Host、HTTP 与浏览器层。二者都从 `dsh-base` 读取同一个默认 DeepSeek 模型路由。
`dsh --profile headless` 启动 `headless` profile[`dsh-base`](../../../packages/bundle/base/cordis.patch.yml) 和 [`dsh-headless`](../../../packages/bundle/headless/cordis.patch.yml) 在空根之上组合,随后 runner 直接驱动 core Agent 与 Session 服务。`dsh web` 则由 `dsh-base` 与 [`dsh-web-app`](../../../packages/bundle/web-app/cordis.patch.yml) 组合,后者拥有 Host、HTTP 与浏览器层。二者都从 `dsh-base` 读取同一个默认 DeepSeek 模型路由。
## 下一步
- [Python SDK 快速上手](./python-sdk.md) — 安装 SDK并在不使用 Web UI 的情况下运行完整 Cordis 配置
- [配置模型](./providers.md) — 接入 DeepSeek 之外的提供方与自定义网关
- [配置文件](./config.md) — 了解 `cordis.yml` 的格式
- [开发插件](../develop/basic/) — 编写自己的工具或后端