mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
test(api): refresh Remote migration artifacts
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/api/settings-controller/README.md
|
||||
README.md: f57bab1cf68ff05d807102a831f4ad9ce65dba76
|
||||
README.zh.md: 41062db004b8c98f544f70c42137a71aee997ec8
|
||||
README.md: b545b27ff1716e54be1a25a9bbd4131f86a12b68
|
||||
README.zh.md: 46977c6e0e3fbd43eb0a688b37e7db6eb7007f85
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
description: "Host Remote owner for settings and credential configuration surfaces, including redacted reads, path-addressed settings writes, and credential reference management."
|
||||
description: "Host Remote owner for settings and credential configuration surfaces, including redacted reads, writes, credential references, and native document opening."
|
||||
kind: "package-reference"
|
||||
---
|
||||
# Settings Controller
|
||||
@@ -8,11 +8,12 @@ English | [中文](README.zh.md)
|
||||
|
||||
## Summary
|
||||
|
||||
`@deepseek-ai/dsh-api-settings-controller` exposes generated `ctx.remote.settings` and `ctx.remote.credentials` namespaces for browser configuration surfaces. It returns redacted settings and credential metadata, supports merge, replacement, and path-addressed settings writes, and stores or removes credential references without returning secret values. When either provider is absent, its namespace remains registered and returns an actionable configuration error.
|
||||
`@deepseek-ai/dsh-api-settings-controller` exposes generated `ctx.remote.settings` and `ctx.remote.credentials` namespaces for browser configuration surfaces. It returns redacted settings and credential metadata, supports settings and credential writes without returning secret values, and opens provider-owned settings or Agent preset locations on the Host desktop. When a provider is absent, the namespace remains registered and returns an actionable configuration error.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Use this package](#use-this-package)
|
||||
- [Configuration](#configuration)
|
||||
- [Model Experience](#model-experience)
|
||||
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
||||
- [Dev Note](#dev-note)
|
||||
@@ -28,6 +29,19 @@ Mount this package as a Loader entry in a profile that serves browser configurat
|
||||
|
||||
`settings.describe()` returns deployment facts and every namespace under `redactSecrets: true`. `settings.update`, `settings.replace`, and `settings.mutate` expose the settings service's three write operations and return the namespace's new redacted view; stale writes use `settings-conflict` and other provider refusals use `settings-rejected`.
|
||||
|
||||
`settings.openSettingsDocument()` prepares the provider-owned document and opens it with the native text-editor intent. `settings.openAgentPresetDirectory(id)` resolves only a user-authored preset and either opens its directory or returns the path when native opening is unavailable; neither method accepts a browser-supplied filesystem target.
|
||||
|
||||
-----
|
||||
|
||||
<a id="configuration"></a>
|
||||
## Configuration
|
||||
|
||||
| Field | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `nativeOpen` | platform-detected | Whether Agent preset directories can be handed to a native desktop opener |
|
||||
|
||||
The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-api-settings-controller) is the exhaustive source for accepted fields and their JSDoc.
|
||||
|
||||
-----
|
||||
|
||||
<a id="model-experience"></a>
|
||||
@@ -43,7 +57,6 @@ No direct effect; reading or writing these configuration values does not alter m
|
||||
|
||||
<a id="known-limitations-and-deferred-work"></a>
|
||||
|
||||
- Settings document opening uses API Proxy rather than this Remote namespace.
|
||||
- The batch bound is fixed at 64 references and is not a deployment-configurable field.
|
||||
|
||||
<a id="dev-note"></a>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
description: "settings 与凭据配置界面的 Host Remote owner,涵盖脱敏读取、按路径写入 settings 和管理凭据引用。"
|
||||
description: "settings 与凭据配置界面的 Host Remote owner,涵盖脱敏读取、写入、凭据引用与原生文档打开。"
|
||||
kind: "package-reference"
|
||||
---
|
||||
# Settings Controller
|
||||
@@ -8,11 +8,12 @@ kind: "package-reference"
|
||||
|
||||
## 概述
|
||||
|
||||
`@deepseek-ai/dsh-api-settings-controller` 为浏览器配置界面提供生成的 `ctx.remote.settings` 与 `ctx.remote.credentials` namespace。它返回脱敏的 settings 与凭据元数据,支持合并、替换和按路径表达的 settings 写入,并在不返回密钥值的前提下写入或移除凭据引用。任一 provider 缺失时,对应 namespace 仍会注册,并返回可操作的配置错误。
|
||||
`@deepseek-ai/dsh-api-settings-controller` 为浏览器配置界面提供生成的 `ctx.remote.settings` 与 `ctx.remote.credentials` namespace。它返回脱敏的 settings 与凭据元数据,支持 settings 与凭据写入而不返回密钥值,并在 Host 桌面打开由 provider 持有的 settings 或 Agent preset 位置。provider 缺失时,namespace 仍会注册,并返回可操作的配置错误。
|
||||
|
||||
## 目录
|
||||
|
||||
- [使用本包](#use-this-package)
|
||||
- [配置](#configuration)
|
||||
- [模型体验](#model-experience)
|
||||
- [已知限制与延期工作](#known-limitations-and-deferred-work)
|
||||
- [开发备注](#dev-note)
|
||||
@@ -28,6 +29,19 @@ kind: "package-reference"
|
||||
|
||||
`settings.describe()` 返回部署信息,以及在 `redactSecrets: true` 下读取的所有 namespace。`settings.update`、`settings.replace` 与 `settings.mutate` 暴露 settings service 的三种写入操作,并返回该 namespace 的新脱敏视图;过期写入使用 `settings-conflict`,其他 provider 拒绝使用 `settings-rejected`。
|
||||
|
||||
`settings.openSettingsDocument()` 准备 provider 持有的文档,并用原生文本编辑器意图将其打开。`settings.openAgentPresetDirectory(id)` 只解析用户创作的 preset,并在原生打开不可用时返回目录路径;两种方法都不接受浏览器提供的文件系统目标。
|
||||
|
||||
-----
|
||||
|
||||
<a id="configuration"></a>
|
||||
## 配置
|
||||
|
||||
| 字段 | 默认值 | 含义 |
|
||||
|---|---|---|
|
||||
| `nativeOpen` | 平台探测 | Agent preset 目录能否交给原生桌面打开器 |
|
||||
|
||||
生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-api-settings-controller)是所有受支持字段及其 JSDoc 的完整来源。
|
||||
|
||||
-----
|
||||
|
||||
<a id="model-experience"></a>
|
||||
@@ -43,7 +57,6 @@ kind: "package-reference"
|
||||
|
||||
<a id="known-limitations-and-deferred-work"></a>
|
||||
|
||||
- settings 文档打开使用 API Proxy,而不经过本 Remote namespace。
|
||||
- 批量上限固定为 64 个引用,不是可按部署配置的字段。
|
||||
|
||||
<a id="dev-note"></a>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import z from '@deepseek-ai/schemastery'
|
||||
import { settingsNamespace } from '@deepseek-ai/dsh-settings'
|
||||
@@ -80,6 +80,8 @@ describe('the settings Remote namespace a configuration page calls', () => {
|
||||
{ method: 'update', invocation: { kind: 'direct' } },
|
||||
{ method: 'replace', invocation: { kind: 'direct' } },
|
||||
{ method: 'mutate', invocation: { kind: 'direct' } },
|
||||
{ method: 'openSettingsDocument', invocation: { kind: 'direct' } },
|
||||
{ method: 'openAgentPresetDirectory', invocation: { kind: 'direct' } },
|
||||
])
|
||||
})
|
||||
|
||||
@@ -91,6 +93,7 @@ describe('the settings Remote namespace a configuration page calls', () => {
|
||||
() => ctx.settingsController.update('ui-test', {}, undefined),
|
||||
() => ctx.settingsController.replace('ui-test', {}, undefined),
|
||||
() => ctx.settingsController.mutate('ui-test', [], undefined),
|
||||
() => ctx.settingsController.openSettingsDocument(new AbortController().signal),
|
||||
]
|
||||
for (const call of calls) {
|
||||
const failure = await Promise.resolve().then(call).catch((error: unknown) => error)
|
||||
@@ -239,4 +242,108 @@ describe('the settings Remote namespace a configuration page calls', () => {
|
||||
expect(code).toBe('internal')
|
||||
expect(message).toContain('was disposed after the mutate')
|
||||
})
|
||||
|
||||
it('prepares and opens the provider-owned settings document', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(DocumentSettings)
|
||||
const prepare = vi.spyOn(ctx.settings, 'prepareDocument').mockResolvedValue('/tmp/settings.yaml')
|
||||
const openTextFile = vi.fn((_path: string, _signal: AbortSignal) => Promise.resolve())
|
||||
const controller = new SettingsController(ctx, {}, { openTextFile })
|
||||
const signal = new AbortController().signal
|
||||
|
||||
await expect(controller.openSettingsDocument(signal)).resolves.toEqual({ opened: true })
|
||||
expect(prepare).toHaveBeenCalledOnce()
|
||||
expect(openTextFile).toHaveBeenCalledWith('/tmp/settings.yaml', signal)
|
||||
})
|
||||
|
||||
it('preserves settings-document absence, failure, and cancellation', async () => {
|
||||
const absent = await boot()
|
||||
await expect(absent.controller.openSettingsDocument(new AbortController().signal))
|
||||
.rejects.toMatchObject({ failure: { code: 'internal', message: expect.stringContaining('no local document') } })
|
||||
|
||||
const failed = await boot(DocumentSettings)
|
||||
vi.spyOn(failed.ctx.settings, 'prepareDocument').mockRejectedValue(new Error('read failed'))
|
||||
await expect(failed.controller.openSettingsDocument(new AbortController().signal))
|
||||
.rejects.toMatchObject({ failure: { code: 'internal', message: expect.stringContaining('read failed') } })
|
||||
|
||||
const cancelled = new AbortController()
|
||||
cancelled.abort(new Error('cancelled'))
|
||||
const prepare = vi.spyOn(failed.ctx.settings, 'prepareDocument')
|
||||
prepare.mockClear()
|
||||
await expect(failed.controller.openSettingsDocument(cancelled.signal))
|
||||
.rejects.toMatchObject({ failure: { code: 'cancelled' } })
|
||||
expect(prepare).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('does not open a settings document cancelled during preparation', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(DocumentSettings)
|
||||
const prepared = Promise.withResolvers<string | undefined>()
|
||||
vi.spyOn(ctx.settings, 'prepareDocument').mockReturnValue(prepared.promise)
|
||||
const openTextFile = vi.fn((_path: string, _signal: AbortSignal) => Promise.resolve())
|
||||
const controller = new SettingsController(ctx, {}, { openTextFile })
|
||||
const abort = new AbortController()
|
||||
|
||||
const opening = controller.openSettingsDocument(abort.signal)
|
||||
abort.abort(new Error('cancelled'))
|
||||
prepared.resolve('/tmp/settings.yaml')
|
||||
|
||||
await expect(opening).rejects.toMatchObject({ failure: { code: 'cancelled' } })
|
||||
expect(openTextFile).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('maps native settings-document opener failures', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(DocumentSettings)
|
||||
vi.spyOn(ctx.settings, 'prepareDocument').mockResolvedValue('/tmp/settings.yaml')
|
||||
const controller = new SettingsController(ctx, {}, {
|
||||
openTextFile: () => Promise.reject(new Error('no default editor')),
|
||||
})
|
||||
|
||||
await expect(controller.openSettingsDocument(new AbortController().signal))
|
||||
.rejects.toMatchObject({
|
||||
failure: { code: 'internal', message: 'path open failed: no default editor' },
|
||||
})
|
||||
})
|
||||
|
||||
it('opens a user Agent preset directory or returns its path without a native opener', async () => {
|
||||
const ctx = new Context()
|
||||
ctx.provide('agentPresets', {
|
||||
resolve: (id: string) => Promise.resolve({
|
||||
id, trust: 'user', path: `/presets/${id}/agent.cordis.yml`,
|
||||
}),
|
||||
} as never)
|
||||
const openPath = vi.fn((_path: string, _signal: AbortSignal) => Promise.resolve())
|
||||
const openable = new SettingsController(ctx, { nativeOpen: true }, { openPath })
|
||||
const signal = new AbortController().signal
|
||||
await expect(openable.openAgentPresetDirectory('mine', signal))
|
||||
.resolves.toEqual({ opened: true })
|
||||
expect(openPath).toHaveBeenCalledWith('/presets/mine', signal)
|
||||
|
||||
const headless = new Context()
|
||||
headless.provide('agentPresets', {
|
||||
resolve: (id: string) => Promise.resolve({
|
||||
id, trust: 'user', path: `/presets/${id}/agent.cordis.yml`,
|
||||
}),
|
||||
} as never)
|
||||
const reveal = new SettingsController(headless, { nativeOpen: false })
|
||||
await expect(reveal.openAgentPresetDirectory('mine', new AbortController().signal))
|
||||
.resolves.toEqual({ opened: false, path: '/presets/mine' })
|
||||
})
|
||||
|
||||
it('refuses a shipped Agent preset and a missing preset provider', async () => {
|
||||
const ctx = new Context()
|
||||
ctx.provide('agentPresets', {
|
||||
resolve: (id: string) => Promise.resolve({
|
||||
id, trust: 'system', path: `/presets/${id}/agent.cordis.yml`,
|
||||
}),
|
||||
} as never)
|
||||
const controller = new SettingsController(ctx)
|
||||
await expect(controller.openAgentPresetDirectory('standard', new AbortController().signal))
|
||||
.rejects.toMatchObject({ failure: { code: 'agent-preset-read-only' } })
|
||||
|
||||
const missing = new SettingsController(new Context())
|
||||
await expect(missing.openAgentPresetDirectory('mine', new AbortController().signal))
|
||||
.rejects.toMatchObject({ failure: { code: 'agent-preset-not-found' } })
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user