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;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/config-catalog.md
|
||||
config-catalog.md: 82f6d26c79d32c6952f3bc11c96fa1c2ddceecdc
|
||||
config-catalog.md: 2e4aad7532b061e8328f25a53c2c3b0c4bb4dfa0
|
||||
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>
|
||||
|
||||
|
||||
@@ -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/subsystems/terminal.md
|
||||
terminal.md: eedf6157b256e83d3b57e07bf738429773b87574
|
||||
terminal.zh.md: 3cc2c281e8c2090bb7e92808f932be689fd0b710
|
||||
terminal.md: c7031143117a514f8579a22fed07a1461babb15e
|
||||
terminal.zh.md: 7c469c028e8b3da7c9d5012f9f49c1ae65a9bd03
|
||||
|
||||
@@ -180,5 +180,5 @@ list(owner: Agent): TerminalSessionSnapshot[]
|
||||
|
||||
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 -->
|
||||
|
||||
@@ -180,5 +180,5 @@ list(owner: Agent): TerminalSessionSnapshot[]
|
||||
|
||||
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 -->
|
||||
|
||||
@@ -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', () => {
|
||||
const fake = fakeInternals()
|
||||
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.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', () => {
|
||||
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/101/stat', stat(101, 77, 100, 77, '2'))
|
||||
fake.dirs.set('/proc/100/task', ['100'])
|
||||
|
||||
@@ -51,7 +51,10 @@ def test_runtime_requires_spawn_helper_only_on_macos(
|
||||
runtime_dir.mkdir()
|
||||
linux = runtime_dir / "dsh-jsonrpc-agent-pkg-linux-x64"
|
||||
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, "_current_platform_tag", lambda: "macos-arm64")
|
||||
@@ -59,3 +62,16 @@ def test_runtime_requires_spawn_helper_only_on_macos(
|
||||
runtime.bundled_runtime_path()
|
||||
monkeypatch.setattr(runtime, "_current_platform_tag", lambda: "linux-x64")
|
||||
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 { dirname, relative, resolve } from 'node:path'
|
||||
import * as yaml from 'js-yaml'
|
||||
import ts from 'typescript'
|
||||
import { cordisConfigFiles } from './cordis-config-files.ts'
|
||||
|
||||
interface JsExpr {
|
||||
__jsExpr: string
|
||||
}
|
||||
import { isJsExpr, loadCordisYaml } from './cordis-yaml.ts'
|
||||
|
||||
interface PackageManifest {
|
||||
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-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 pluginReferences: PluginReference[] = []
|
||||
|
||||
@@ -73,7 +59,7 @@ if (import.meta.main) {
|
||||
const files = cordisConfigFiles(root)
|
||||
|
||||
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)) {
|
||||
errors.push(`${file}: root must be a Loader entry array`)
|
||||
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. */
|
||||
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 : []
|
||||
}
|
||||
|
||||
@@ -462,7 +448,6 @@ export function metadataExpressionErrors(entry: Record<string, unknown>, path: s
|
||||
function disabledExpressionProblem(expression: string): string | undefined {
|
||||
try {
|
||||
// Compilation only — the constructor never executes the body.
|
||||
// oxlint-disable-next-line typescript/no-implied-eval
|
||||
new Function(`return (${expression})`)
|
||||
return undefined
|
||||
} 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)
|
||||
}
|
||||
|
||||
function isJsExpr(value: unknown): value is JsExpr {
|
||||
return isRecord(value) && typeof value.__jsExpr === 'string'
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return value !== null && typeof value === 'object'
|
||||
}
|
||||
|
||||
@@ -8,11 +8,7 @@ import { globSync } from 'node:fs'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { basename, dirname, resolve } from 'node:path'
|
||||
import { parseArgs } from 'node:util'
|
||||
import * as yaml from 'js-yaml'
|
||||
|
||||
interface JsExpr {
|
||||
__jsExpr: string
|
||||
}
|
||||
import { loadCordisYaml } from './cordis-yaml.ts'
|
||||
|
||||
interface PackageManifest {
|
||||
name?: string
|
||||
@@ -34,16 +30,6 @@ interface 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 { values } = parseArgs({
|
||||
args: process.argv.slice(2),
|
||||
@@ -107,7 +93,7 @@ async function missingPresetPlugins(
|
||||
const failures: string[] = []
|
||||
const presetPaths = globSync('apps/cli/config/agent-presets/*/agent.cordis.yml', { cwd: root }).sort()
|
||||
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)) {
|
||||
failures.push(`${presetPath}: preset root must be a Loader entry array`)
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user