Merge branch 'master' into xtr/node-pty-1.2-beta
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 docs/user/develop/basic/publish.md
|
||||
publish.md: 8283f9e7ff0c28580343975d67c5715d17c53074
|
||||
publish.zh.md: 5a87901fe39aa00e94db81dc840e39a52e0cc88c
|
||||
publish.md: 9548fe2f8e4636dd160c79cd0d50f01f8bdfd3d1
|
||||
publish.zh.md: 2cff05edcca55bac5fde9d2e7d24ed9c8b48a6f5
|
||||
|
||||
@@ -4,6 +4,8 @@ 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. It assumes the `dsh` CLI is installed. Complete [plugin configuration](./config.md) first.
|
||||
|
||||
To use a fresh source checkout instead, complete the [run-from-source section](../../../../README.md#run-from-source), keep this tutorial's `hello-plugin` directory at the repository root, and run the remaining `dsh ...` commands from there as `pnpm dsh ...`. See [source execution](../../../../apps/cli/reference/README.md#source-execution) for build and launcher behavior.
|
||||
|
||||
## Two concepts, two manifests
|
||||
|
||||
Installation is built on two concepts. Both are described by a `package.json`, but they carry different kinds of manifest under the `dsh` key, and they answer different questions:
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
前几篇教程通过 `--patch` overlay 加载本地插件。本教程把它打包成可安装的**组合包**(bundle),用 `dsh plugin add` 安装进一个 **profile**,并解释决定组合后配置的层顺序。本文假设 `dsh` CLI 已安装。请先完成[插件配置](./config.md)。
|
||||
|
||||
如果改用全新的源码 checkout,请先按照[从源码运行章节](../../../../README.md#run-from-source)完成准备,将本教程的 `hello-plugin` 目录放在仓库根目录,并从该目录把下文的 `dsh ...` 命令改为 `pnpm dsh ...`。构建与启动器行为见[源码执行](../../../../apps/cli/reference/README.md#source-execution)。
|
||||
|
||||
## 两个概念,两种 manifest
|
||||
|
||||
安装机制建立在两个概念之上。二者都由一份 `package.json` 描述,但它们在 `dsh` 键下携带的 manifest(元数据清单)种类不同,回答的问题也不同:
|
||||
|
||||
@@ -26,7 +26,7 @@ export function OnboardingModal({
|
||||
useEffect(() => {
|
||||
const appRoot = document.getElementById('root')
|
||||
if (appRoot === null) return
|
||||
const previous = appRoot.inert === true
|
||||
const previous = appRoot.inert
|
||||
appRoot.inert = true
|
||||
return () => { appRoot.inert = previous }
|
||||
}, [])
|
||||
@@ -41,7 +41,7 @@ export function OnboardingModal({
|
||||
title={title}
|
||||
onClose={ignoreImplicitDismiss}
|
||||
headless
|
||||
className={css['dialog'] ?? ''}
|
||||
className={css.dialog as string}
|
||||
>
|
||||
<div className={css.content}>
|
||||
<h2 ref={titleRef} className={css.title} tabIndex={focusTitle ? -1 : undefined}>{title}</h2>
|
||||
|
||||
@@ -103,7 +103,7 @@ export const en = {
|
||||
export type ModelsKey = keyof typeof en
|
||||
|
||||
/** Chinese strings (same keys as {@link en}). */
|
||||
export const zh: typeof en = {
|
||||
export const zh: { [Key in keyof typeof en]: string } = {
|
||||
nav: '模型',
|
||||
title: '模型',
|
||||
intro: '填入各提供方的 API 密钥即可使用其模型。',
|
||||
|
||||
@@ -18,8 +18,8 @@ export const WELCOME_NOTICE_COPY = {
|
||||
continueLabel: '继续',
|
||||
},
|
||||
en: {
|
||||
title: '内测声明',
|
||||
body: 'DeepSeek Harness 目前的 0.1 版本仍处在面向 Harness 开发者进行测试的阶段,还有许多地方需要持续改进和打磨,希望听取广大开发者的反馈建议。预计 DeepSeek Harness 的核心插件以及基础 API 都会在接下来的一段时间内快速迭代、持续演化。\n\n我们期待与全球开发者一起,在开源、开放、可复用、可组合的基础设施之上,共同探索智能上限。欢迎全球 Harness 开发者加入 DSH 插件生态。',
|
||||
continueLabel: '继续',
|
||||
title: 'Internal Testing Notice',
|
||||
body: "DeepSeek Harness 0.1 remains in testing for Harness developers. Many areas need further improvement, and we welcome feedback from the developer community. DeepSeek Harness's core plugins and foundational APIs will continue to evolve rapidly over the coming months.\n\nWe look forward to exploring the limits of intelligence with developers around the world, building on open-source, open, reusable, and composable infrastructure. We welcome Harness developers everywhere to join the DSH plugin ecosystem.",
|
||||
continueLabel: 'Continue',
|
||||
},
|
||||
} as const
|
||||
|
||||
@@ -14,7 +14,7 @@ import { WelcomeNotice } from '../src/client/WelcomeNotice.tsx'
|
||||
// the shipped Chinese copy, so they state the browser they assume.
|
||||
usePinnedBrowserLanguages('zh-CN')
|
||||
|
||||
async function bench() {
|
||||
async function bench(isLoopback = true) {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SlotRegistry).await()
|
||||
const locale = new LocaleRuntime(ctx)
|
||||
@@ -24,7 +24,7 @@ async function bench() {
|
||||
new TestRemote(ctx)
|
||||
// The apply path only captures the wire face; no call leaves this fake
|
||||
// until a section actually loads.
|
||||
ctx.provide('connection', { api: {}, isLoopback: true } as never)
|
||||
ctx.provide('connection', { api: {}, isLoopback } as never)
|
||||
return { ctx, slots: ctx.get('slots') as SlotRegistry, locale }
|
||||
}
|
||||
|
||||
@@ -142,6 +142,22 @@ describe('ui-settings-models apply', () => {
|
||||
expect(() => b.locale.register('settings.models', 'zh', {})).not.toThrow()
|
||||
expect(() => b.locale.register('settings.models', 'en', {})).not.toThrow()
|
||||
})
|
||||
|
||||
it('keeps remote-browser acknowledgement in process memory', async () => {
|
||||
const b = await bench(false)
|
||||
declare(b.slots)
|
||||
await b.ctx.plugin({ inject: [...inject], apply }).await()
|
||||
const entry = b.slots.entries('settings.onboarding')
|
||||
.find(candidate => candidate.options.id === 'welcome-notice')!
|
||||
const injected = (
|
||||
entry.inject as unknown as () => import('../src/client/WelcomeNotice.tsx').WelcomeNoticeInjected
|
||||
)()
|
||||
|
||||
await injected.controller.load()
|
||||
expect(injected.controller.store.getSnapshot()).toEqual({
|
||||
status: 'ready', acknowledged: false, error: null,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('pushed invalidations', () => {
|
||||
|
||||
@@ -146,6 +146,13 @@ function harness(options: {
|
||||
}
|
||||
|
||||
describe('DeepSeekOnboardingDialog', () => {
|
||||
it('renders when the shell root is absent', async () => {
|
||||
const h = harness()
|
||||
document.getElementById('root')!.remove()
|
||||
render(<DeepSeekOnboardingDialog {...h.props} />)
|
||||
expect(await screen.findByRole('dialog', { name: en.onboardingTitle })).toBeTruthy()
|
||||
})
|
||||
|
||||
it('loads a credential-only modal, inerts the product, and focuses the key', async () => {
|
||||
const h = harness()
|
||||
render(<DeepSeekOnboardingDialog {...h.props} />)
|
||||
@@ -185,22 +192,6 @@ describe('DeepSeekOnboardingDialog', () => {
|
||||
expect(h.set).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('stores only the official credential, refreshes, and completes without opening Settings', async () => {
|
||||
const h = harness()
|
||||
render(<DeepSeekOnboardingDialog {...h.props} />)
|
||||
await screen.findByRole('dialog')
|
||||
fireEvent.change(screen.getByLabelText(en.keyInput), { target: { value: ' sk-live ' } })
|
||||
fireEvent.click(screen.getByRole('button', { name: en.onboardingSave }))
|
||||
await waitFor(() => {
|
||||
expect(h.set).toHaveBeenCalledWith({ ref: 'DEEPSEEK_API_KEY', value: 'sk-live' })
|
||||
})
|
||||
expect(h.mutate).not.toHaveBeenCalled()
|
||||
expect(h.openSection).not.toHaveBeenCalled()
|
||||
await waitFor(() => { expect(h.complete).toHaveBeenCalledOnce() })
|
||||
expect(screen.queryByRole('dialog')).toBeNull()
|
||||
expect(document.getElementById('root')?.inert).toBe(false)
|
||||
})
|
||||
|
||||
it('keeps the modal open and reports rejected and failed credential writes', async () => {
|
||||
for (const [options, message] of [
|
||||
[{ setFailure: 'credential was rejected' }, 'credential was rejected'],
|
||||
|
||||
@@ -62,7 +62,11 @@ function mount(version?: string, mutateImpl: () => Promise<unknown> = () => Prom
|
||||
|
||||
describe('WelcomeNotice', () => {
|
||||
it('uses the exact owner copy in both GUI locales', () => {
|
||||
expect(WELCOME_NOTICE_COPY.en).toEqual(WELCOME_NOTICE_COPY.zh)
|
||||
expect(WELCOME_NOTICE_COPY.en).toEqual({
|
||||
title: 'Internal Testing Notice',
|
||||
body: "DeepSeek Harness 0.1 remains in testing for Harness developers. Many areas need further improvement, and we welcome feedback from the developer community. DeepSeek Harness's core plugins and foundational APIs will continue to evolve rapidly over the coming months.\n\nWe look forward to exploring the limits of intelligence with developers around the world, building on open-source, open, reusable, and composable infrastructure. We welcome Harness developers everywhere to join the DSH plugin ecosystem.",
|
||||
continueLabel: 'Continue',
|
||||
})
|
||||
expect(en.welcomeBody).toBe(WELCOME_NOTICE_COPY.en.body)
|
||||
expect(zh.welcomeBody).toBe(WELCOME_NOTICE_COPY.zh.body)
|
||||
})
|
||||
|
||||
@@ -91,7 +91,7 @@ describe('WelcomeNoticeStore', () => {
|
||||
|
||||
const nonError = new WelcomeNoticeStore({
|
||||
// Durable/wire failures are unknown; exercise containment of a non-Error rejection.
|
||||
settings: { describe: () => Promise.reject('offline string') },
|
||||
settings: { describe: () => Promise.reject(new Error('offline string')) },
|
||||
} as never)
|
||||
await nonError.load()
|
||||
expect(nonError.store.getSnapshot().error).toBe('offline string')
|
||||
|
||||
@@ -243,6 +243,8 @@ export default defineConfig({
|
||||
'packages/client/ui-workspace/src/client/index.ts',
|
||||
'packages/test-support/client-runtime/src/translate.ts',
|
||||
'packages/client/ui-primitives/src/JsonTree.tsx',
|
||||
'packages/client/ui-settings-models/src/client/DeepSeekOnboardingDialog.tsx',
|
||||
'packages/client/ui-settings-models/src/client/welcome-store.ts',
|
||||
'packages/extensions/*/src/**/*.ts',
|
||||
'packages/extensions/*/src/**/*.tsx',
|
||||
// Typert generator: correctness is pinned by its fixture suites and
|
||||
|
||||
Reference in New Issue
Block a user