mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
Merge remote-tracking branch 'origin/master' into feat/web-message-feedback-ui
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 apps/cli/README.md
|
||||
README.md: 98a856261bc632c97f350db8fc7bb0b10c22235d
|
||||
README.zh.md: 283e54138e24202ed6b88d1d309538c58cd66b3e
|
||||
README.md: 4fbd0692a2df403c6395235e096e193c994ea198
|
||||
README.zh.md: 7861f5b4f8cb447aff01ec8b64b12fd298485b4b
|
||||
|
||||
@@ -35,4 +35,4 @@ The [CLI behavior reference](reference/README.md) owns exact layer precedence, f
|
||||
|
||||
## Development
|
||||
|
||||
Production runs require built package and frontend artifacts. From the repository root, `pnpm dsh <args...>` builds those artifacts, runs the TypeScript entry, and forwards every argument; the [source-execution reference](reference/README.md#source-execution) owns the module-resolution contract.
|
||||
Production runs require built package and frontend artifacts. From the repository root, run `pnpm run build` separately, then use `pnpm dsh <args...>` to run the TypeScript entry and forward every argument; the [source-execution reference](reference/README.md#source-execution) owns the module-resolution contract.
|
||||
|
||||
@@ -35,4 +35,4 @@ profile 目录包含一个 `package.json`(树外插件依赖,加上 profile
|
||||
|
||||
## 开发
|
||||
|
||||
生产运行需要已构建的包与前端产物。从仓库根目录运行 `pnpm dsh <args...>` 会先构建这些产物,再运行 TypeScript 入口并转发所有参数;模块解析约定由[源码执行参考](reference/README.md#source-execution)负责。
|
||||
生产运行需要已构建的包与前端产物。请在仓库根目录单独运行 `pnpm run build`,然后使用 `pnpm dsh <args...>` 运行 TypeScript 入口并转发所有参数;模块解析约定由[源码执行参考](reference/README.md#source-execution)负责。
|
||||
|
||||
@@ -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 apps/cli/reference/README.md
|
||||
README.md: 46ea3c241d6775ce90a89c7be58901375a0634a3
|
||||
README.zh.md: f020f46260d6b04b87a4918a671ca6bcbed251d9
|
||||
README.md: e8bc99f7b2b15f0679ec268a8e778e815758c00f
|
||||
README.zh.md: c75c487caa7e2ffd7ca5c4b16f3c6243f0a86f3e
|
||||
|
||||
@@ -81,4 +81,4 @@ Install external plugin bundles through `dsh plugin --profile <name> add <packag
|
||||
|
||||
## Source execution
|
||||
|
||||
From the repository root, use `pnpm dsh <args...>`. The `package.json` script runs the complete repository build, launches `apps/cli/src/bin.ts` with `node --import tsx/esm`, and forwards every argument. Build output appears before CLI output. The process inherits the launch environment; set `NODE_USE_ENV_PROXY=1` when a supporting Node version must honor `HTTP_PROXY` and `HTTPS_PROXY`. The installed form launches the built `apps/cli/lib/bin.js` without rebuilding the repository.
|
||||
From the repository root, run `pnpm run build` separately after a fresh checkout and whenever artifacts need updating, then use `pnpm dsh <args...>`. The `package.json` script launches `apps/cli/src/bin.ts` with `node --import tsx/esm` without building and forwards every argument. Missing TypeRT host artifacts fail profile boot through module-resolution errors without a build instruction. Once those host artifacts exist, missing frontend or client-plugin bundles fail at startup with an instruction to run `pnpm run build`. The launcher does not check freshness, so existing stale bundles can run older browser code until rebuilt. The process inherits the launch environment; set `NODE_USE_ENV_PROXY=1` when a supporting Node version must honor `HTTP_PROXY` and `HTTPS_PROXY`. The installed form launches the built `apps/cli/lib/bin.js` without rebuilding the repository.
|
||||
|
||||
@@ -81,4 +81,4 @@ dsh web --help
|
||||
|
||||
## 源码执行
|
||||
|
||||
请从仓库根目录使用 `pnpm dsh <args...>`。`package.json` 中的脚本会完成整个仓库的构建,通过 `node --import tsx/esm` 启动 `apps/cli/src/bin.ts`,并转发所有参数。构建输出会显示在 CLI 输出之前。该进程会继承启动环境;当支持环境代理的 Node 版本必须遵循 `HTTP_PROXY` 和 `HTTPS_PROXY` 时,请设置 `NODE_USE_ENV_PROXY=1`。安装形式会直接启动构建后的 `apps/cli/lib/bin.js`,不会重新构建仓库。
|
||||
请在仓库根目录中,于全新 checkout 之后及产物需要更新时单独运行 `pnpm run build`,然后使用 `pnpm dsh <args...>`。`package.json` 中的脚本不会构建,而是通过 `node --import tsx/esm` 启动 `apps/cli/src/bin.ts`,并转发所有参数。TypeRT Host 产物缺失时,profile 启动会因不含构建指引的模块解析错误而失败。这些 Host 产物存在后,如果前端或 Client plugin 组合包缺失,启动会失败并提示运行 `pnpm run build`。启动器不会检查产物是否为最新,因此已有的陈旧组合包可能继续运行旧版浏览器代码,直至重新构建。该进程会继承启动环境;当支持环境代理的 Node 版本必须遵循 `HTTP_PROXY` 和 `HTTPS_PROXY` 时,请设置 `NODE_USE_ENV_PROXY=1`。安装形式会直接启动构建后的 `apps/cli/lib/bin.js`,不会重新构建仓库。
|
||||
|
||||
@@ -6,7 +6,7 @@ import { describe, expect, it } from 'vitest'
|
||||
/**
|
||||
* Keyless smoke for SOURCE `dsh` execution: run `apps/cli/src/bin.ts`
|
||||
* with the exact production runtime vector (`node --import tsx/esm`, the
|
||||
* vector the root `dsh` script invokes after building) and assert the
|
||||
* vector the root `dsh` script invokes directly) and assert the
|
||||
* required-config diagnostic. The Node compatibility matrix runs this
|
||||
* WHOLE file, so a Node release changing module hooks or TypeScript handling
|
||||
* breaks this gate instead of every developer's `pnpm dsh`; the built-bin
|
||||
@@ -17,11 +17,11 @@ const repoRoot = fileURLToPath(new URL('../../../', import.meta.url))
|
||||
const dshSourceBin = 'apps/cli/src/bin.ts'
|
||||
|
||||
describe('dsh SOURCE launcher (node --import tsx/esm)', () => {
|
||||
it('builds before launching the source CLI', async () => {
|
||||
it('launches the source CLI without building', async () => {
|
||||
const rootPackage = JSON.parse(await readFile(new URL('../../../package.json', import.meta.url), 'utf8')) as {
|
||||
readonly scripts?: Record<string, string>
|
||||
}
|
||||
expect(rootPackage.scripts?.dsh).toBe('pnpm run build && node --import tsx/esm apps/cli/src/bin.ts')
|
||||
expect(rootPackage.scripts?.dsh).toBe('node --import tsx/esm apps/cli/src/bin.ts')
|
||||
})
|
||||
|
||||
it('boots the source entry and requires a profile', async () => {
|
||||
|
||||
@@ -143,8 +143,55 @@ it('accepts pasted images into the composer rail in order and removes them', asy
|
||||
},
|
||||
})
|
||||
const toast = await screen.findByRole('alert')
|
||||
expect(toast.textContent).toContain('Unsupported image format: text/plain')
|
||||
expect(toast.textContent).toContain('Only PNG, JPG, WebP, and GIF images are supported')
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole('alert')).toBeNull()
|
||||
}, { timeout: 6_000 })
|
||||
})
|
||||
|
||||
it('accepts a whole-page drop under the limits-labeled overlay and refuses an over-limit batch at intake', async () => {
|
||||
mountAssembledApp()
|
||||
|
||||
const tree = await screen.findByRole('tree', { name: 'Sessions' }, { timeout: 10_000 })
|
||||
const start = tree.querySelector<HTMLButtonElement>('button[aria-label="New session in fixture"]')
|
||||
if (start === null) throw new Error('fixture Workspace new-session action missing')
|
||||
fireEvent.click(start)
|
||||
const textarea = await screen.findByPlaceholderText('Describe what you want to build', {}, { timeout: 10_000 })
|
||||
|
||||
// A file drag anywhere over the page raises the full-viewport overlay whose
|
||||
// desc line carries the projected limits — copy that can only render after
|
||||
// the imageLimits projection crossed the real fixture transport.
|
||||
const image = new File([new Uint8Array([137, 80, 78, 71])], 'dropped.png', { type: 'image/png' })
|
||||
const dataTransfer = { types: ['Files'], files: [image], dropEffect: 'none' }
|
||||
fireEvent.dragEnter(document.body, { dataTransfer })
|
||||
const overlay = await screen.findByRole('status')
|
||||
expect(overlay.textContent).toContain('Drag images here to add them')
|
||||
await waitFor(() => {
|
||||
expect(overlay.textContent).toContain('Up to 20 images, 5MB each')
|
||||
})
|
||||
|
||||
// Dropping on the transcript area (not the composer card) lands in the rail.
|
||||
fireEvent.drop(document.body, { dataTransfer })
|
||||
await waitFor(() => {
|
||||
const rail = document.querySelector('[role="group"][aria-label="Pending images"]')
|
||||
if (rail === null) throw new Error('attachment rail missing after page drop')
|
||||
expect([...rail.querySelectorAll('img')].map(img => img.getAttribute('alt'))).toEqual(['dropped.png'])
|
||||
}, { timeout: 5_000 })
|
||||
expect(screen.queryByRole('status')).toBeNull()
|
||||
|
||||
// An intake that would exceed the projected per-message count is refused as
|
||||
// a whole batch at add time: the banner names the limit and the rail keeps
|
||||
// only the previously accepted thumbnail — no submit-time rollback.
|
||||
const batch = Array.from({ length: 20 }, (_, i) =>
|
||||
new File([new Uint8Array([137, 80, 78, 71])], `bulk-${String(i)}.png`, { type: 'image/png' }))
|
||||
fireEvent.paste(textarea, {
|
||||
clipboardData: {
|
||||
items: batch.map(file => ({ kind: 'file', type: 'image/png', getAsFile: () => file })),
|
||||
getData: () => '',
|
||||
},
|
||||
})
|
||||
const banner = await screen.findByRole('alert')
|
||||
expect(banner.textContent).toContain('A message can include up to 20 images')
|
||||
const rail = document.querySelector('[role="group"][aria-label="Pending images"]')
|
||||
expect([...(rail?.querySelectorAll('img') ?? [])]).toHaveLength(1)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user