docs: fix release smoke test failures

This commit is contained in:
Turtle
2026-08-13 13:43:43 +08:00
parent be96840412
commit 62080d49c2
66 changed files with 1150 additions and 124 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/index.md
index.md: 494b7869be6ffdf5767fac260b36b2585305b516
index.zh.md: a55b8e31151c5cfa5445069974be9fa022fea1eb
index.md: 08199624e638aaf4a36b04446c39c228b2af6025
index.zh.md: c45a30d0bfffaf4a6c78303f9ca043c3397c8a08

View File

@@ -45,14 +45,16 @@ export function apply(ctx: Context) {
## Register it in cordis.yml
Create `scratch-plugin/cordis.yml` as a Web overlay that inserts the local plugin:
Run `pwd` from the repository root, then create `scratch-plugin/cordis.yml` as a Web overlay that inserts the local plugin. Replace `/absolute/path/to/deepseek-harness` below with the printed path:
```yaml
- insert:
- id: hello
name: './src/my-plugin.ts'
name: '/absolute/path/to/deepseek-harness/scratch-plugin/src/my-plugin.ts'
```
The plugin path must be absolute. A patch file contributes configuration but does not change the profile directory from which the loader resolves module paths.
Start the Web UI with that overlay:
```sh

View File

@@ -45,14 +45,16 @@ export function apply(ctx: Context) {
## 注册到 cordis.yml
创建 `scratch-plugin/cordis.yml`,作为插入本地插件的 Web 覆盖层:
在仓库根目录运行 `pwd`,然后创建 `scratch-plugin/cordis.yml`,作为插入本地插件的 Web 覆盖层。请将下文的 `/absolute/path/to/deepseek-harness` 替换为命令打印的路径
```yaml
- insert:
- id: hello
name: './src/my-plugin.ts'
name: '/absolute/path/to/deepseek-harness/scratch-plugin/src/my-plugin.ts'
```
插件路径必须是绝对路径。patch 文件只贡献配置,不会改变 loader 解析模块路径时使用的 profile 目录。
使用该覆盖层启动 Web UI
```sh

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: 588531a28020ebe620643cd1aaaa43de000e658a
publish.zh.md: b86bd43369c027972705394fded43ae053248c0f
publish.md: edc85b74049c5b993efb02663195fcc0001b4620
publish.zh.md: cb824ad12eec0d706efa17b28f3bdc874ba59131

View File

@@ -2,7 +2,7 @@
English | [中文](publish.zh.md)
The previous tutorials loaded a local plugin through a `--patch` overlay. This tutorial packages it as an installable **bundle**, installs it into a **profile** with `dsh plugin add`, and explains the layer order that determines the composed configuration. Complete [plugin configuration](./config.md) first.
The previous tutorials loaded a local plugin through a `--patch` overlay. This tutorial packages it as an installable **bundle**, installs it into a **profile** with `pnpm dsh plugin add`, and explains the layer order that determines the composed configuration. Complete [plugin configuration](./config.md) first.
## Two concepts, two manifests
@@ -11,10 +11,16 @@ Installation is built on two concepts. Both are described by a `package.json`, b
- A **bundle** is an npm package that ships a configuration layer. Its manifest declares `dsh.bundle`, answering "what does this package contribute?": a patch file that inserts or overrides plugin rows.
- A **profile** is a directory under `$DSH_HOME/profiles/<name>` describing one runnable composition. Its manifest declares `dsh.profile`, answering "which bundles compose this setup, in what order?".
A bundle is what you author and distribute; a profile is what a user boots with `dsh --profile <name>`. Nothing is both.
A bundle is what you author and distribute; a profile is what a user boots from this source checkout with `pnpm dsh --profile <name>`. Nothing is both.
### The bundle manifest
From the repository root, create the package directory:
```sh
mkdir -p hello-plugin
```
```
hello-plugin/
├── package.json # declares dsh.bundle
@@ -22,6 +28,8 @@ hello-plugin/
└── index.js # plugin modules the patch rows reference
```
Create `hello-plugin/package.json`:
```json
{
"name": "dsh-hello-plugin",
@@ -33,7 +41,17 @@ hello-plugin/
}
```
The patch file is a YAML array of patch entries, like the `--patch` overlays you have been writing, except plugin rows reference the package by name instead of a relative source path so Node resolution finds the installed code:
Create `hello-plugin/index.js` with the plugin entry point:
```js
export const name = 'hello-plugin'
export function apply() {
console.log('[hello-plugin] plugin loaded!')
}
```
Create `hello-plugin/cordis.patch.yml`. The patch is a YAML array like the `--patch` overlays you have been writing, except plugin rows reference the package by name instead of a relative source path so Node resolution finds the installed code:
```yaml
- insert:
@@ -41,7 +59,7 @@ The patch file is a YAML array of patch entries, like the `--patch` overlays you
name: dsh-hello-plugin
```
A package without the `dsh.bundle` declaration still installs, but only as a plain dependency: `dsh plugin` prints a warning and activates no layer. Use that package format for a library that plugin packages import rather than a plugin users enable.
A package without the `dsh.bundle` declaration still installs, but only as a plain dependency: `pnpm dsh plugin` prints a warning and activates no layer. Use that package format for a library that plugin packages import rather than a plugin users enable.
### The profile manifest
@@ -50,15 +68,14 @@ A profile directory holds two files:
- `package.json` — the profile's out-of-tree plugin dependencies (managed by pnpm) plus the `dsh.profile` manifest with its ordered `bundles` list.
- `cordis.patch.yml` — the user's own patch layer, applied after every bundle layer.
You never write a profile manifest by hand: `dsh plugin` creates and maintains it. The next section shows the result.
You never write a profile manifest by hand: `pnpm dsh plugin` creates and maintains it. The next section shows the result.
## Install into a profile
`dsh plugin --profile <name> <args...>` forwards to pnpm in the profile directory, so every pnpm verb works. Install your package from its checkout:
`pnpm dsh plugin --profile <name> <args...>` forwards to pnpm in the profile directory, so every pnpm verb works. From the repository root, install the package checkout:
```sh
cd hello-plugin
dsh plugin --profile demo add .
pnpm dsh plugin --profile demo add ./hello-plugin
```
The first use initializes the profile (with `@deepseek-ai/dsh-base` as its first bundle), pnpm links the checkout, and `dsh` appends the bundle to `dsh.profile.bundles` because the package declares `dsh.bundle`:
@@ -84,11 +101,11 @@ The first use initializes the profile (with `@deepseek-ai/dsh-base` as its first
Verify the layer without booting, then boot:
```sh
dsh --profile demo --dump-config # shows a "# == dsh-hello-plugin" layer
dsh --profile demo
pnpm dsh --profile demo --dump-config # shows a "# == dsh-hello-plugin" layer
pnpm dsh --profile demo
```
`dsh plugin --profile demo remove dsh-hello-plugin` removes both the dependency and the layer.
`pnpm dsh plugin --profile demo remove dsh-hello-plugin` removes both the dependency and the layer.
## The loading order
@@ -136,7 +153,7 @@ On `--help`, the provider publishes no service, so those rows never activate. Lo
Publishing to a registry is not required — users can install straight from a git host:
```sh
dsh plugin --profile demo add github:you/hello-plugin
pnpm dsh plugin --profile demo add github:you/hello-plugin
```
But a git install fetches **sources, not built artifacts**: nothing runs your `build` script, so a TypeScript package arrives without its `lib/` output and fails to load. Two things must happen, one on each side:
@@ -155,8 +172,8 @@ Treat that allowance as what it is: **permission to execute the package's code o
If you would rather not ask users for the allowance, distribute built artifacts instead — neither form needs any build permission:
- **Publish to npm** with `lib/` built at `pnpm publish` time; `dsh plugin add your-package` then installs prebuilt code.
- **Ship a tarball** from `pnpm pack`; users run `dsh plugin add ./hello-plugin-0.1.0.tgz`.
- **Publish to npm** with `lib/` built at `pnpm publish` time; `pnpm dsh plugin add your-package` then installs prebuilt code.
- **Ship a tarball** from `pnpm pack`; users run `pnpm dsh plugin add ./hello-plugin-0.1.0.tgz`.
## Next steps

View File

@@ -2,7 +2,7 @@
[English](publish.md) | 中文
前几篇教程通过 `--patch` overlay 加载本地插件。本教程把它打包成可安装的**组合包**bundle`dsh plugin add` 安装进一个 **profile**,并解释决定组合后配置的层顺序。请先完成[插件配置](./config.md)。
前几篇教程通过 `--patch` overlay 加载本地插件。本教程把它打包成可安装的**组合包**bundle`pnpm dsh plugin add` 安装进一个 **profile**,并解释决定组合后配置的层顺序。请先完成[插件配置](./config.md)。
## 两个概念,两种 manifest
@@ -11,10 +11,16 @@
- **组合包**是附带一个配置层的 npm 包。它的 manifest 声明 `dsh.bundle`,回答的是"这个包贡献什么?":一个插入或覆盖插件行的 patch 文件。
- **profile** 是位于 `$DSH_HOME/profiles/<name>` 下、描述一份可启动组合的目录。它的 manifest 声明 `dsh.profile`,回答的是"这套配置由哪些组合包按什么顺序组成?"。
组合包是你编写并分发的东西profile 是用户`dsh --profile <name>` 启动的东西。没有东西同时是两者。
组合包是你编写并分发的东西profile 是用户在当前源码 checkout 中用 `pnpm dsh --profile <name>` 启动的东西。没有东西同时是两者。
### 组合包 manifest
在仓库根目录创建包目录:
```sh
mkdir -p hello-plugin
```
```
hello-plugin/
├── package.json # declares dsh.bundle
@@ -22,6 +28,8 @@ hello-plugin/
└── index.js # plugin modules the patch rows reference
```
创建 `hello-plugin/package.json`
```json
{
"name": "dsh-hello-plugin",
@@ -33,7 +41,17 @@ hello-plugin/
}
```
patch 文件与一直在写的 `--patch` overlay 一样,是一个 patch 条目的 YAML 数组;区别是插件行按包名而不是相对源码路径引用这个包,这样 Node 的模块解析才能找到已安装的代码
创建 `hello-plugin/index.js`,写入插件入口
```js
export const name = 'hello-plugin'
export function apply() {
console.log('[hello-plugin] plugin loaded!')
}
```
创建 `hello-plugin/cordis.patch.yml`。这个 patch 与一直在写的 `--patch` overlay 一样,是一个 patch 条目的 YAML 数组;区别是插件行按包名而不是相对源码路径引用这个包,这样 Node 的模块解析才能找到已安装的代码:
```yaml
- insert:
@@ -41,7 +59,7 @@ patch 文件与一直在写的 `--patch` overlay 一样,是一个 patch 条目
name: dsh-hello-plugin
```
没有 `dsh.bundle` 声明的包仍然可以安装,但只作为普通依赖:`dsh plugin` 会打印警告,且不激活任何层。如果一个库供插件包 import而不是供用户启用就使用这种包格式。
没有 `dsh.bundle` 声明的包仍然可以安装,但只作为普通依赖:`pnpm dsh plugin` 会打印警告,且不激活任何层。如果一个库供插件包 import而不是供用户启用就使用这种包格式。
### profile manifest
@@ -50,15 +68,14 @@ profile 目录包含两个文件:
- `package.json` — profile 的树外插件依赖(由 pnpm 管理),加上 `dsh.profile` manifest 及其有序的 `bundles` 列表。
- `cordis.patch.yml` — 用户自己的 patch 层,在每个组合包层之后应用。
profile manifest 从不需要手写:`dsh plugin` 负责创建和维护它。下一节展示其结果。
profile manifest 从不需要手写:`pnpm dsh plugin` 负责创建和维护它。下一节展示其结果。
## 安装进 profile
`dsh plugin --profile <name> <args...>` 在 profile 目录内转发给 pnpm因此所有 pnpm 子命令都可用。 checkout 安装你的包
`pnpm dsh plugin --profile <name> <args...>` 在 profile 目录内转发给 pnpm因此所有 pnpm 子命令都可用。在仓库根目录安装该包的 checkout
```sh
cd hello-plugin
dsh plugin --profile demo add .
pnpm dsh plugin --profile demo add ./hello-plugin
```
首次使用会初始化 profile`@deepseek-ai/dsh-base` 作为它的第一个组合包pnpm 链接该 checkout`dsh` 因为这个包声明了 `dsh.bundle`,把它追加进 `dsh.profile.bundles`
@@ -84,11 +101,11 @@ dsh plugin --profile demo add .
先不启动、只验证该层,再启动:
```sh
dsh --profile demo --dump-config # shows a "# == dsh-hello-plugin" layer
dsh --profile demo
pnpm dsh --profile demo --dump-config # shows a "# == dsh-hello-plugin" layer
pnpm dsh --profile demo
```
`dsh plugin --profile demo remove dsh-hello-plugin` 会同时移除依赖和对应的层。
`pnpm dsh plugin --profile demo remove dsh-hello-plugin` 会同时移除依赖和对应的层。
## 加载顺序
@@ -136,7 +153,7 @@ dsh --profile demo
发布到注册表不是必须的——用户可以直接从 git 托管安装:
```sh
dsh plugin --profile demo add github:you/hello-plugin
pnpm dsh plugin --profile demo add github:you/hello-plugin
```
但 git 安装拉取的是**源码,不是构建产物**:没有任何环节运行你的 `build` 脚本,因此 TypeScript 包到手时没有 `lib/` 输出,加载会失败。必须两边各做一件事:
@@ -155,8 +172,8 @@ dsh plugin --profile demo add github:you/hello-plugin
如果不想让用户做这项授权,就改为分发构建产物——以下两种形式都不需要任何构建权限:
- **发布到 npm**,在 `pnpm publish` 时构建好 `lib/``dsh plugin add your-package` 安装的就是预构建代码。
- **交付 tarball**:用 `pnpm pack` 打包;用户执行 `dsh plugin add ./hello-plugin-0.1.0.tgz`。
- **发布到 npm**,在 `pnpm publish` 时构建好 `lib/``pnpm dsh plugin add your-package` 安装的就是预构建代码。
- **交付 tarball**:用 `pnpm pack` 打包;用户执行 `pnpm dsh plugin add ./hello-plugin-0.1.0.tgz`。
## 下一步

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: 4a6ecbad614cf2debccaee9ace1086273d8fb95b
events.zh.md: c77747bf6ac767c69e4f2d9cfe7375b71bd388e6
events.md: 1d9fe5c8f5068de6ad8b2abaa85cf67be35c8459
events.zh.md: 8bb9447a270cc2db966b1e01298a831a60a1b9c1

View File

@@ -40,7 +40,7 @@ ctx.on('my-plugin/ready', ({ id }) => {
### bail — short circuit
Listeners run in order; the first non-`undefined` result becomes the final result:
Listeners run in order; the first result other than `null`, `false`, or `undefined` becomes the final result:
```ts ignore-check
// Dispatch
@@ -49,13 +49,13 @@ const result = ctx.bail('some-check', input)
// Listen: a returned value stops later listeners.
ctx.on('some-check', (input) => {
if (shouldBlock(input)) return 'blocked'
// Return undefined to continue to the next listener.
// Return null, false, or undefined to continue to the next listener.
})
```
### serial — ordered execution
Listeners run in registration order and asynchronous results are awaited. The first listener to return a non-empty value stops further execution:
Listeners run in registration order and asynchronous results are awaited. The first result other than `null`, `false`, or `undefined` stops further execution:
```ts ignore-check
await ctx.serial('setup-phase', context)

View File

@@ -40,7 +40,7 @@ ctx.on('my-plugin/ready', ({ id }) => {
### bail — 短路
依次调用监听器,第一个非 `undefined` 的返回值将作为最终结果:
监听器按顺序运行,第一个不是 `null`、`false` 或 `undefined` 的返回值会成为最终结果:
```ts ignore-check
// Dispatch
@@ -49,13 +49,13 @@ const result = ctx.bail('some-check', input)
// Listen: a returned value stops later listeners.
ctx.on('some-check', (input) => {
if (shouldBlock(input)) return 'blocked'
// Return undefined to continue to the next listener.
// Return null, false, or undefined to continue to the next listener.
})
```
### serial — 顺序执行
监听器按注册顺序依次执行,并等待异步结果;第一个返回非空值的监听器会终止后续执行:
监听器按注册顺序依次执行,并等待异步结果;第一个不是 `null`、`false` 或 `undefined` 的返回值会终止后续执行:
```ts ignore-check
await ctx.serial('setup-phase', context)

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: a9e873f1ba969b1f2d22266f8ec03207f84eeddc
service.zh.md: a201280bc38176f57f5354ab205ae700c7b04a89
service.md: 03f4e7dc4df934495a4b203066183753b621339e
service.zh.md: 2f4c01e0ea6a87a87ca694b265b07e07098f3e59

View File

@@ -117,7 +117,7 @@ This prevents a plugin from calling a service that no longer exists.
name: '@deepseek-ai/cordis-plugin-group'
group: true
isolate:
bash: true
shell: true
config:
- name: '@deepseek-ai/dsh-bash-local'
config:
@@ -128,7 +128,7 @@ This prevents a plugin from calling a service that no longer exists.
name: '@deepseek-ai/cordis-plugin-group'
group: true
isolate:
bash: true
shell: true
config:
- name: '@deepseek-ai/dsh-bash-local'
config:

View File

@@ -108,6 +108,8 @@ export function apply(ctx: Context) {
这可以防止插件调用已不存在的服务。
<a id="service-isolation"></a>
## 服务隔离
`cordis.yml` 支持服务隔离——同一个服务可以有多个实例,不同插件组看到不同实例:
@@ -117,7 +119,7 @@ export function apply(ctx: Context) {
name: '@deepseek-ai/cordis-plugin-group'
group: true
isolate:
bash: true
shell: true
config:
- name: '@deepseek-ai/dsh-bash-local'
config:
@@ -128,7 +130,7 @@ export function apply(ctx: Context) {
name: '@deepseek-ai/cordis-plugin-group'
group: true
isolate:
bash: true
shell: true
config:
- name: '@deepseek-ai/dsh-bash-local'
config:

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: 56ca7249b8b156ceb78327b547413dbf65726dcc
index.zh.md: aec5980ea45cfd5f8f7408877743920e68e15cbd
index.md: cc6bd7a234305f6fa193341f15354b40855f72e9
index.zh.md: aed13b00f9bc74946aace614953bed30705c8281

View File

@@ -23,7 +23,7 @@ The Bash execution capability consists of:
└─────────────┘ └──────────────────┘ └──────────────┘
▲ │
└────────────────────────────────────────────┘
inject: ['bash']
inject: ['shell']
```
## Benefits of the split

View File

@@ -23,7 +23,7 @@
└─────────────┘ └──────────────────┘ └──────────────┘
▲ │
└────────────────────────────────────────────┘
inject: ['bash']
inject: ['shell']
```
## 拆分的好处

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: aba4a6d0c8ee42e78ca5a804d9a0dd9b31c1e240
llm-adapter.zh.md: 5c7a6483e29a28871257df6a31e13d859a245097
llm-adapter.md: 882e82d880ac622cf886c6c24e75a1987e1c6702
llm-adapter.zh.md: 27c480af2a19a68ac35900a2ce1b5e9dbdc85bf8

View File

@@ -32,12 +32,12 @@ class MyAdapter extends LlmAdapter {
export interface Config {
apiKey: string
models: string[]
providers: string[]
}
export const Config: Schema<Config> = Schema.object({
apiKey: Schema.string().required(),
models: Schema.array(Schema.string()).required(),
providers: Schema.array(Schema.string()).required(),
})
export const name = 'my-llm-adapter'
@@ -45,7 +45,7 @@ export const inject = ['llm']
export function apply(ctx: Context, config: Config) {
const adapter = new MyAdapter(config.apiKey)
ctx.llm.registerAdapter(config.models, adapter)
ctx.llm.registerAdapter(config.providers, adapter)
}
```
@@ -117,10 +117,10 @@ Override `resolveModel(provider, model, signal?)` to return exact provider/model
## Register an adapter
```ts ignore-check
ctx.llm.registerAdapter(['model-name-1', 'model-name-2'], adapter)
ctx.llm.registerAdapter(['my-provider'], adapter)
```
The first argument lists the model names handled by the adapter. If `cordis.yml` selects `model: model-name-1`, the service routes that request to this adapter.
The first argument lists provider routes handled by the adapter. `GenerateOptions.provider` selects the registered adapter, while `GenerateOptions.model` passes an adapter-owned model id without lifecycle registration. Override `listModels()` when the adapter can advertise model choices to selectors.
## Use it from cordis.yml
@@ -129,18 +129,16 @@ The first argument lists the model names handled by the adapter. If `cordis.yml`
name: './src/my-llm-adapter.ts'
config:
apiKey: !!js process.env.MY_API_KEY
models:
- my-model-v1
- my-model-v2
providers:
- my-provider
- id: agent-loop
name: '@deepseek-ai/dsh-agent-loop'
config:
agents:
- id: main
provider: my-llm
model: my-model-v1 # References the model registered above.
workspaceContext: false
provider: my-provider
model: my-model-v1
```
## Reference implementations

View File

@@ -32,12 +32,12 @@ class MyAdapter extends LlmAdapter {
export interface Config {
apiKey: string
models: string[]
providers: string[]
}
export const Config: Schema<Config> = Schema.object({
apiKey: Schema.string().required(),
models: Schema.array(Schema.string()).required(),
providers: Schema.array(Schema.string()).required(),
})
export const name = 'my-llm-adapter'
@@ -45,7 +45,7 @@ export const inject = ['llm']
export function apply(ctx: Context, config: Config) {
const adapter = new MyAdapter(config.apiKey)
ctx.llm.registerAdapter(config.models, adapter)
ctx.llm.registerAdapter(config.providers, adapter)
}
```
@@ -117,10 +117,10 @@ async function* exampleChunks(): AsyncIterable<StreamChunk> {
## 注册适配器
```ts ignore-check
ctx.llm.registerAdapter(['model-name-1', 'model-name-2'], adapter)
ctx.llm.registerAdapter(['my-provider'], adapter)
```
第一个参数是该适配器支持的模型名列表。当用户在 `cordis.yml` 中配置 `model: model-name-1` 时,框架会将请求路由到该适配器
第一个参数是该适配器处理的提供方路由列表。`GenerateOptions.provider` 选择已注册的适配器,`GenerateOptions.model` 则传入由适配器拥有、无需在生命周期启动时注册的模型 id。适配器能够向选择器公布模型选项时请覆写 `listModels()`
## 在 cordis.yml 中使用
@@ -129,18 +129,16 @@ ctx.llm.registerAdapter(['model-name-1', 'model-name-2'], adapter)
name: './src/my-llm-adapter.ts'
config:
apiKey: !!js process.env.MY_API_KEY
models:
- my-model-v1
- my-model-v2
providers:
- my-provider
- id: agent-loop
name: '@deepseek-ai/dsh-agent-loop'
config:
agents:
- id: main
provider: my-llm
model: my-model-v1 # References the model registered above.
workspaceContext: false
provider: my-provider
model: my-model-v1
```
## 实战参考

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/index.md
index.md: 80d288b1aba37e7f0863fe5fc8237cbd2a6ab9b5
index.zh.md: addfbc94ff93ed015e52f509a23a3f981e36770b
index.md: 282a5c11b317a8fb8706bb03f41cf03fb2aca49d
index.zh.md: 4ec53b16fc8987b5eb40ef4854cb41d1436a9015

View File

@@ -2,9 +2,7 @@
English | [中文](index.zh.md)
Start the Web UI through the [root README](../../../README.md#run); the command prints its URL. This guide begins after that server is running.
The invoking directory is the default workspace, so the agent can inspect and modify the project where you started `dsh`.
Start the Web UI through the [root README](../../../README.md#run); the command prints its URL. This guide begins after that server is running. The `dsh` process uses its invoking directory as the default filesystem location, but a fresh Web UI has no selected workspace until you add one.
## Configure a model
@@ -12,6 +10,10 @@ Open **Settings → Models**, enter a DeepSeek API key, and save it. The model r
The [model configuration guide](./providers.md) covers other providers and custom OpenAI-compatible endpoints.
## Choose a workspace
Click **Choose workspace**, add the project directory where you started `dsh`, and select it. The session composer remains unavailable until a workspace is selected.
## Run a task
Start a session and send:

View File

@@ -2,9 +2,7 @@
[English](index.md) | 中文
先按照[根 README](../../../README.md#run)启动 Web UI命令会打印其访问地址。本指南从服务器已经运行的状态开始。
调用目录是默认工作区,因此 agent智能体可以检查并修改启动 `dsh` 时所在的项目。
先按照[根 README](../../../README.md#run)启动 Web UI命令会打印其访问地址。本指南从服务器已经运行的状态开始。`dsh` 进程会把调用目录作为默认文件系统位置,但新的 Web UI 在添加工作区前不会选中任何工作区。
## 配置模型
@@ -12,6 +10,10 @@
[模型配置指南](./providers.md)介绍其他提供方和自定义 OpenAI 兼容端点。
## 选择工作区
点击**选择工作区**,添加启动 `dsh` 时所在的项目目录,然后选中它。选中工作区前,会话输入框不可用。
## 运行任务
启动一个会话并发送: