mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
fix(python-sdk): satisfy runtime packaging gates
This commit is contained in:
@@ -2,5 +2,5 @@
|
|||||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
# 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:
|
# after editing either side, bring the other along and re-record with:
|
||||||
# pnpm run verify-translation-pairing --write docs/config-catalog.md
|
# pnpm run verify-translation-pairing --write docs/config-catalog.md
|
||||||
config-catalog.md: 82f6d26c79d32c6952f3bc11c96fa1c2ddceecdc
|
config-catalog.md: 2e4aad7532b061e8328f25a53c2c3b0c4bb4dfa0
|
||||||
config-catalog.zh.md: 958d3115447db37de248bbf30b0744308ff8dbb8
|
config-catalog.zh.md: 958d3115447db37de248bbf30b0744308ff8dbb8
|
||||||
|
|||||||
@@ -2370,7 +2370,7 @@ export interface Config {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Source: [`packages/shell/tool-bash-persistent/src/index.ts:405`](../packages/shell/tool-bash-persistent/src/index.ts)
|
Source: [`packages/shell/tool-bash-persistent/src/index.ts:406`](../packages/shell/tool-bash-persistent/src/index.ts)
|
||||||
|
|
||||||
<a id="deepseek-aidsh-tool-fs"></a>
|
<a id="deepseek-aidsh-tool-fs"></a>
|
||||||
|
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
# 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:
|
# after editing either side, bring the other along and re-record with:
|
||||||
# pnpm run verify-translation-pairing --write docs/subsystems/terminal.md
|
# pnpm run verify-translation-pairing --write docs/subsystems/terminal.md
|
||||||
terminal.md: eedf6157b256e83d3b57e07bf738429773b87574
|
terminal.md: c7031143117a514f8579a22fed07a1461babb15e
|
||||||
terminal.zh.md: 3cc2c281e8c2090bb7e92808f932be689fd0b710
|
terminal.zh.md: 7c469c028e8b3da7c9d5012f9f49c1ae65a9bd03
|
||||||
|
|||||||
@@ -180,5 +180,5 @@ list(owner: Agent): TerminalSessionSnapshot[]
|
|||||||
|
|
||||||
Types: [Agent](core.md)
|
Types: [Agent](core.md)
|
||||||
|
|
||||||
Source: [`packages/terminal/terminal/src/index.ts:105`](../../packages/terminal/terminal/src/index.ts)
|
Source: [`packages/terminal/terminal/src/index.ts:108`](../../packages/terminal/terminal/src/index.ts)
|
||||||
<!-- END GENERATED cordis-surface -->
|
<!-- END GENERATED cordis-surface -->
|
||||||
|
|||||||
@@ -180,5 +180,5 @@ list(owner: Agent): TerminalSessionSnapshot[]
|
|||||||
|
|
||||||
Types: [Agent](core.md)
|
Types: [Agent](core.md)
|
||||||
|
|
||||||
Source: [`packages/terminal/terminal/src/index.ts:105`](../../packages/terminal/terminal/src/index.ts)
|
Source: [`packages/terminal/terminal/src/index.ts:108`](../../packages/terminal/terminal/src/index.ts)
|
||||||
<!-- END GENERATED cordis-surface -->
|
<!-- END GENERATED cordis-surface -->
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { describe, expect, it, vi } from 'vitest'
|
||||||
|
|
||||||
|
const existsSync = vi.hoisted(() => vi.fn(() => true))
|
||||||
|
|
||||||
|
vi.mock('node:fs', async (importOriginal) => {
|
||||||
|
const actual = await importOriginal<typeof import('node:fs')>()
|
||||||
|
return { ...actual, existsSync }
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('@vscode/ripgrep', () => new Proxy({}, {
|
||||||
|
get() {
|
||||||
|
throw new Error('the platform package must not load when the executable sidecar exists')
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
import { resolveRgPath } from '@deepseek-ai/dsh-tool-fs-search'
|
||||||
|
|
||||||
|
describe('single-executable ripgrep resolution', () => {
|
||||||
|
it('uses the native sidecar beside the current executable', async () => {
|
||||||
|
const sidecar = `${process.execPath}-rg`
|
||||||
|
|
||||||
|
await expect(resolveRgPath()).resolves.toBe(sidecar)
|
||||||
|
expect(existsSync).toHaveBeenCalledWith(sidecar)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -140,6 +140,32 @@ describe('Linux process inspector', () => {
|
|||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('contains cycles in the procfs children index', () => {
|
||||||
|
const fake = fakeInternals()
|
||||||
|
fake.files.set('/proc/10/stat', stat(10, 10, 10, 10, '500'))
|
||||||
|
fake.files.set('/proc/10/task/10/children', '11')
|
||||||
|
fake.files.set('/proc/11/stat', stat(11, 10, 10, 10, '501', 10))
|
||||||
|
fake.files.set('/proc/11/task/11/children', '10')
|
||||||
|
|
||||||
|
expect(createProcessInspector('linux', 'x64', fake.internals).processTree(10)).toEqual([
|
||||||
|
{ pid: 11, started: '501' },
|
||||||
|
{ pid: 10, started: '500' },
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('falls back to the PID namespace when a descendant children index is unreadable', () => {
|
||||||
|
const fake = fakeInternals()
|
||||||
|
fake.dirs.set('/proc', ['10', '11', '12'])
|
||||||
|
fake.files.set('/proc/10/stat', stat(10, 10, 10, 10, '500'))
|
||||||
|
fake.files.set('/proc/10/task/10/children', '12 invalid 11')
|
||||||
|
fake.files.set('/proc/11/stat', stat(11, 10, 10, 10, '501', 10))
|
||||||
|
|
||||||
|
expect(createProcessInspector('linux', 'x64', fake.internals).processTree(10)).toEqual([
|
||||||
|
{ pid: 11, started: '501' },
|
||||||
|
{ pid: 10, started: '500' },
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
it('keeps readiness inspection local when procfs has no children index', () => {
|
it('keeps readiness inspection local when procfs has no children index', () => {
|
||||||
const fake = fakeInternals()
|
const fake = fakeInternals()
|
||||||
fake.files.set('/proc/10/stat', stat(10, 10, 10, 10, '500'))
|
fake.files.set('/proc/10/stat', stat(10, 10, 10, 10, '500'))
|
||||||
@@ -147,11 +173,19 @@ describe('Linux process inspector', () => {
|
|||||||
|
|
||||||
expect(inspector.processTree(10, false)).toEqual([{ pid: 10, started: '500' }])
|
expect(inspector.processTree(10, false)).toEqual([{ pid: 10, started: '500' }])
|
||||||
expect(inspector.isStdinWaiting(10, false)).toBe(false)
|
expect(inspector.isStdinWaiting(10, false)).toBe(false)
|
||||||
|
|
||||||
|
const readFile = fake.internals.readFile.bind(fake.internals)
|
||||||
|
let statReads = 0
|
||||||
|
fake.internals.readFile = (path) => {
|
||||||
|
if (path === '/proc/10/stat' && statReads++ > 0) throw new Error('process exited')
|
||||||
|
return readFile(path)
|
||||||
|
}
|
||||||
|
expect(inspector.processTree(10, false)).toEqual([])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('detects read, select, poll, and epoll waits across non-leader threads', () => {
|
it('detects read, select, poll, and epoll waits across non-leader threads', () => {
|
||||||
const fake = fakeInternals()
|
const fake = fakeInternals()
|
||||||
fake.dirs.set('/proc', ['100', '101'])
|
fake.dirs.set('/proc', ['77', '100', '101'])
|
||||||
fake.files.set('/proc/100/stat', stat(100, 77, 100, 77, '1'))
|
fake.files.set('/proc/100/stat', stat(100, 77, 100, 77, '1'))
|
||||||
fake.files.set('/proc/101/stat', stat(101, 77, 100, 77, '2'))
|
fake.files.set('/proc/101/stat', stat(101, 77, 100, 77, '2'))
|
||||||
fake.dirs.set('/proc/100/task', ['100'])
|
fake.dirs.set('/proc/100/task', ['100'])
|
||||||
|
|||||||
@@ -51,7 +51,10 @@ def test_runtime_requires_spawn_helper_only_on_macos(
|
|||||||
runtime_dir.mkdir()
|
runtime_dir.mkdir()
|
||||||
linux = runtime_dir / "dsh-jsonrpc-agent-pkg-linux-x64"
|
linux = runtime_dir / "dsh-jsonrpc-agent-pkg-linux-x64"
|
||||||
linux.touch()
|
linux.touch()
|
||||||
(runtime_dir / "dsh-jsonrpc-agent-pkg-macos-arm64").touch()
|
Path(f"{linux}-rg").touch()
|
||||||
|
macos = runtime_dir / "dsh-jsonrpc-agent-pkg-macos-arm64"
|
||||||
|
macos.touch()
|
||||||
|
Path(f"{macos}-rg").touch()
|
||||||
monkeypatch.setattr(runtime, "bundled_package_dir", lambda: tmp_path)
|
monkeypatch.setattr(runtime, "bundled_package_dir", lambda: tmp_path)
|
||||||
|
|
||||||
monkeypatch.setattr(runtime, "_current_platform_tag", lambda: "macos-arm64")
|
monkeypatch.setattr(runtime, "_current_platform_tag", lambda: "macos-arm64")
|
||||||
@@ -59,3 +62,16 @@ def test_runtime_requires_spawn_helper_only_on_macos(
|
|||||||
runtime.bundled_runtime_path()
|
runtime.bundled_runtime_path()
|
||||||
monkeypatch.setattr(runtime, "_current_platform_tag", lambda: "linux-x64")
|
monkeypatch.setattr(runtime, "_current_platform_tag", lambda: "linux-x64")
|
||||||
assert runtime.bundled_runtime_path() == linux
|
assert runtime.bundled_runtime_path() == linux
|
||||||
|
|
||||||
|
|
||||||
|
def test_runtime_requires_ripgrep_sidecar(
|
||||||
|
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||||
|
) -> None:
|
||||||
|
runtime_dir = tmp_path / "runtime"
|
||||||
|
runtime_dir.mkdir()
|
||||||
|
(runtime_dir / "dsh-jsonrpc-agent-pkg-linux-x64").touch()
|
||||||
|
monkeypatch.setattr(runtime, "bundled_package_dir", lambda: tmp_path)
|
||||||
|
monkeypatch.setattr(runtime, "_current_platform_tag", lambda: "linux-x64")
|
||||||
|
|
||||||
|
with pytest.raises(FileNotFoundError, match="ripgrep sidecar"):
|
||||||
|
runtime.bundled_runtime_path()
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import * as yaml from 'js-yaml'
|
||||||
|
|
||||||
|
export interface JsExpr {
|
||||||
|
__jsExpr: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const jsExprType = new yaml.Type('tag:yaml.org,2002:js', {
|
||||||
|
kind: 'scalar',
|
||||||
|
resolve: data => typeof data === 'string',
|
||||||
|
construct: (data: unknown): JsExpr => {
|
||||||
|
if (typeof data !== 'string') throw new TypeError('!!js requires a scalar string')
|
||||||
|
return { __jsExpr: data }
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const schema = yaml.JSON_SCHEMA.extend(jsExprType)
|
||||||
|
|
||||||
|
/** Parse a Cordis config while preserving Loader `!!js` expressions as data. */
|
||||||
|
export function loadCordisYaml(source: string): unknown {
|
||||||
|
return yaml.load(source, { schema })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isJsExpr(value: unknown): value is JsExpr {
|
||||||
|
return typeof value === 'object'
|
||||||
|
&& value !== null
|
||||||
|
&& typeof (value as Record<string, unknown>).__jsExpr === 'string'
|
||||||
|
}
|
||||||
@@ -12,13 +12,9 @@
|
|||||||
|
|
||||||
import { globSync, readFileSync } from 'node:fs'
|
import { globSync, readFileSync } from 'node:fs'
|
||||||
import { dirname, relative, resolve } from 'node:path'
|
import { dirname, relative, resolve } from 'node:path'
|
||||||
import * as yaml from 'js-yaml'
|
|
||||||
import ts from 'typescript'
|
import ts from 'typescript'
|
||||||
import { cordisConfigFiles } from './cordis-config-files.ts'
|
import { cordisConfigFiles } from './cordis-config-files.ts'
|
||||||
|
import { isJsExpr, loadCordisYaml } from './cordis-yaml.ts'
|
||||||
interface JsExpr {
|
|
||||||
__jsExpr: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PackageManifest {
|
interface PackageManifest {
|
||||||
name?: string
|
name?: string
|
||||||
@@ -56,16 +52,6 @@ const CHOOSER_BACKEND_PACKAGES = [
|
|||||||
'@deepseek-ai/dsh-client-ui-directory-picker-browse',
|
'@deepseek-ai/dsh-client-ui-directory-picker-browse',
|
||||||
'@deepseek-ai/dsh-client-ui-directory-picker-native',
|
'@deepseek-ai/dsh-client-ui-directory-picker-native',
|
||||||
]
|
]
|
||||||
const jsExprType = new yaml.Type('tag:yaml.org,2002:js', {
|
|
||||||
kind: 'scalar',
|
|
||||||
resolve: data => typeof data === 'string',
|
|
||||||
construct: (data: unknown): JsExpr => {
|
|
||||||
if (typeof data !== 'string') throw new TypeError('!!js requires a scalar string')
|
|
||||||
return { __jsExpr: data }
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const schema = yaml.JSON_SCHEMA.extend(jsExprType)
|
|
||||||
|
|
||||||
const errors: string[] = []
|
const errors: string[] = []
|
||||||
const pluginReferences: PluginReference[] = []
|
const pluginReferences: PluginReference[] = []
|
||||||
|
|
||||||
@@ -73,7 +59,7 @@ if (import.meta.main) {
|
|||||||
const files = cordisConfigFiles(root)
|
const files = cordisConfigFiles(root)
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
const document: unknown = yaml.load(readFileSync(resolve(root, file), 'utf8'), { schema })
|
const document = loadCordisYaml(readFileSync(resolve(root, file), 'utf8'))
|
||||||
if (!isUnknownArray(document)) {
|
if (!isUnknownArray(document)) {
|
||||||
errors.push(`${file}: root must be a Loader entry array`)
|
errors.push(`${file}: root must be a Loader entry array`)
|
||||||
continue
|
continue
|
||||||
@@ -172,7 +158,7 @@ function validatePresetPlaneSeparation(): string[] {
|
|||||||
|
|
||||||
/** Every entry of one config file, or an empty list when it is not an entry array. */
|
/** Every entry of one config file, or an empty list when it is not an entry array. */
|
||||||
function loadEntries(file: string): unknown[] {
|
function loadEntries(file: string): unknown[] {
|
||||||
const document: unknown = yaml.load(readFileSync(resolve(root, file), 'utf8'), { schema })
|
const document = loadCordisYaml(readFileSync(resolve(root, file), 'utf8'))
|
||||||
return isUnknownArray(document) ? document : []
|
return isUnknownArray(document) ? document : []
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -462,7 +448,6 @@ export function metadataExpressionErrors(entry: Record<string, unknown>, path: s
|
|||||||
function disabledExpressionProblem(expression: string): string | undefined {
|
function disabledExpressionProblem(expression: string): string | undefined {
|
||||||
try {
|
try {
|
||||||
// Compilation only — the constructor never executes the body.
|
// Compilation only — the constructor never executes the body.
|
||||||
// oxlint-disable-next-line typescript/no-implied-eval
|
|
||||||
new Function(`return (${expression})`)
|
new Function(`return (${expression})`)
|
||||||
return undefined
|
return undefined
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -484,10 +469,6 @@ function collectExpressionPaths(value: unknown, path: string, output: string[]):
|
|||||||
for (const [key, child] of Object.entries(value)) collectExpressionPaths(child, `${path}.${key}`, output)
|
for (const [key, child] of Object.entries(value)) collectExpressionPaths(child, `${path}.${key}`, output)
|
||||||
}
|
}
|
||||||
|
|
||||||
function isJsExpr(value: unknown): value is JsExpr {
|
|
||||||
return isRecord(value) && typeof value.__jsExpr === 'string'
|
|
||||||
}
|
|
||||||
|
|
||||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||||
return value !== null && typeof value === 'object'
|
return value !== null && typeof value === 'object'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,7 @@ import { globSync } from 'node:fs'
|
|||||||
import { readFile } from 'node:fs/promises'
|
import { readFile } from 'node:fs/promises'
|
||||||
import { basename, dirname, resolve } from 'node:path'
|
import { basename, dirname, resolve } from 'node:path'
|
||||||
import { parseArgs } from 'node:util'
|
import { parseArgs } from 'node:util'
|
||||||
import * as yaml from 'js-yaml'
|
import { loadCordisYaml } from './cordis-yaml.ts'
|
||||||
|
|
||||||
interface JsExpr {
|
|
||||||
__jsExpr: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PackageManifest {
|
interface PackageManifest {
|
||||||
name?: string
|
name?: string
|
||||||
@@ -34,16 +30,6 @@ interface RuntimePlatform {
|
|||||||
|
|
||||||
type RuntimePlatformManifest = Record<string, RuntimePlatform>
|
type RuntimePlatformManifest = Record<string, RuntimePlatform>
|
||||||
|
|
||||||
const jsExprType = new yaml.Type('tag:yaml.org,2002:js', {
|
|
||||||
kind: 'scalar',
|
|
||||||
resolve: data => typeof data === 'string',
|
|
||||||
construct: (data: unknown): JsExpr => {
|
|
||||||
if (typeof data !== 'string') throw new TypeError('!!js requires a scalar string')
|
|
||||||
return { __jsExpr: data }
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const schema = yaml.JSON_SCHEMA.extend(jsExprType)
|
|
||||||
|
|
||||||
const root = resolve(import.meta.dirname, '..')
|
const root = resolve(import.meta.dirname, '..')
|
||||||
const { values } = parseArgs({
|
const { values } = parseArgs({
|
||||||
args: process.argv.slice(2),
|
args: process.argv.slice(2),
|
||||||
@@ -107,7 +93,7 @@ async function missingPresetPlugins(
|
|||||||
const failures: string[] = []
|
const failures: string[] = []
|
||||||
const presetPaths = globSync('apps/cli/config/agent-presets/*/agent.cordis.yml', { cwd: root }).sort()
|
const presetPaths = globSync('apps/cli/config/agent-presets/*/agent.cordis.yml', { cwd: root }).sort()
|
||||||
for (const presetPath of presetPaths) {
|
for (const presetPath of presetPaths) {
|
||||||
const document: unknown = yaml.load(await readFile(resolve(root, presetPath), 'utf8'), { schema })
|
const document = loadCordisYaml(await readFile(resolve(root, presetPath), 'utf8'))
|
||||||
if (!Array.isArray(document)) {
|
if (!Array.isArray(document)) {
|
||||||
failures.push(`${presetPath}: preset root must be a Loader entry array`)
|
failures.push(`${presetPath}: preset root must be a Loader entry array`)
|
||||||
continue
|
continue
|
||||||
|
|||||||
Reference in New Issue
Block a user