Merge origin/master into xjt/incremental-proofreading-275-apply

This commit is contained in:
xjt
2026-08-09 12:17:59 +08:00
47 changed files with 1433 additions and 202 deletions

View File

@@ -0,0 +1,10 @@
{
"mcpServers": {
"github_repository": {
"command": "node",
"args": [
"lib/mcp-server.mjs"
]
}
}
}

View File

@@ -0,0 +1,30 @@
{
"name": "dsh-github-repository-plugin-e2e-fixture",
"version": "0.0.0",
"private": true,
"type": "module",
"files": [
"lib",
"dsh-plugin.mjs",
"dsh-plugin-assets"
],
"scripts": {
"prepack": "tsc --noEmit && tsdown src/plugin.ts src/mcp-server.ts --no-config --tsconfig tsconfig.json --out-dir lib --platform node --target es2024 --clean && dsh-plugin-prepare"
},
"dsh": {
"skills": [
"../skills"
],
"mcpServers": "./.mcp.json",
"entry": "./lib/plugin.mjs"
},
"dependencies": {
"@modelcontextprotocol/sdk": "1.29.0"
},
"devDependencies": {
"@deepseek-ai/dsh-repository-plugin": "0.0.1",
"cordis": "4.0.0-rc.7",
"tsdown": "0.22.2",
"typescript": "6.0.3"
}
}

View File

@@ -0,0 +1,19 @@
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
// The repository root's linter cannot resolve this independently installed
// Git-package dependency; the package's prepack tsc validates the SDK types.
/* oxlint-disable typescript/no-unsafe-assignment, typescript/no-unsafe-call, typescript/no-unsafe-member-access */
const server = new McpServer({
name: 'github-repository-plugin-e2e',
version: '0.0.0',
})
server.registerTool('proof', {
description: 'Proves that an MCP server compiled from the exact GitHub repository package is active.',
inputSchema: {},
}, async () => ({
content: [{ type: 'text', text: 'MCP_FROM_GITHUB_REPOSITORY' }],
}))
await server.connect(new StdioServerTransport())

View File

@@ -0,0 +1,59 @@
import type { Context } from 'cordis'
const PROOF_TOOL_NAME = 'mcp__github_repository__proof'
interface TextBlock {
readonly type: 'text'
readonly text: string
}
interface ToolExecution {
readonly name: string
}
interface ToolResult {
readonly isError: boolean
readonly content: readonly TextBlock[]
}
type PostDecision =
| { readonly kind: 'accept'; readonly content?: readonly TextBlock[]; readonly value?: unknown; readonly additionalContexts?: readonly unknown[] }
| { readonly kind: 'block'; readonly feedback: readonly TextBlock[] }
type PostListener = (
execution: ToolExecution,
result: ToolResult,
next: () => Promise<PostDecision>,
) => Promise<PostDecision>
type DshContext = Context & {
on(event: 'tools/post-execute', listener: PostListener): () => void
}
/** Cordis plugin name used by the repository acceptance fixture. */
export const name = 'github-repository-typescript-proof'
/** DSH tool registry required by the post-execute contribution. */
export const inject = ['tools']
/**
* Append a marker after the repository MCP proof tool succeeds.
* @param ctx - trusted DSH Cordis context supplied to the repository package.
*/
export function apply(ctx: Context): void {
const dsh = ctx as DshContext
dsh.on('tools/post-execute', async (execution, result, next): Promise<PostDecision> => {
const decision = await next()
if (execution.name !== PROOF_TOOL_NAME || result.isError || decision.kind !== 'accept' || Object.hasOwn(decision, 'value')) {
return decision
}
return {
kind: 'accept',
content: [
...(decision.content ?? result.content),
{ type: 'text', text: 'TS_PLUGIN_FROM_GITHUB_REPOSITORY' },
],
...decision.additionalContexts === undefined ? {} : { additionalContexts: decision.additionalContexts },
}
})
}

View File

@@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "ES2024",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"skipLibCheck": true,
"noEmit": true
},
"include": [
"src/**/*.ts"
]
}

View File

@@ -0,0 +1,6 @@
---
name: github-source-proof
description: Proves that dsh installed a private repository Plugin from an exact GitHub source.
---
This skill exists only in the GitHub repository source fixture.

View File

@@ -0,0 +1,256 @@
import { createHash } from 'node:crypto'
import { cpSync, existsSync, globSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs'
import { createServer } from 'node:http'
import { createRequire } from 'node:module'
import { tmpdir } from 'node:os'
import { delimiter, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { startMockLlmServer } from '@deepseek-ai/dsh-llm-mock-server'
import { execa } from 'execa'
import { describe, expect, it } from 'vitest'
const repoRoot = fileURLToPath(new URL('../../../', import.meta.url))
const dshBin = join(repoRoot, 'apps/cli/lib/bin.js')
const repositoryPluginPackage = join(repoRoot, 'packages/self-modification/repository-plugin')
const releasePackageNames = new Set(globSync([
'vendor/*/package.json',
'packages/*/*/package.json',
'apps/*/package.json',
], { cwd: repoRoot }).map((filename) => {
const manifest = JSON.parse(readFileSync(join(repoRoot, filename), 'utf8')) as Record<string, unknown>
if (typeof manifest.name !== 'string') throw new Error(`workspace package name is missing: ${filename}`)
return manifest.name
}))
const source = process.env.DSH_GITHUB_REPOSITORY_PLUGIN_SOURCE
const required = process.env.DSH_REQUIRE_GITHUB_REPOSITORY_PLUGIN_E2E === '1'
const enabled = required || source !== undefined
interface PublishedPackageRegistry {
url: string
requests: string[]
close(): Promise<void>
}
function publishedManifest(): Record<string, unknown> {
const manifest = JSON.parse(readFileSync(join(repositoryPluginPackage, 'package.json'), 'utf8')) as Record<string, unknown>
const version = manifest.version
if (typeof version !== 'string') throw new Error('repository Plugin package version is missing')
Reflect.deleteProperty(manifest, 'private')
for (const field of ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']) {
const dependencies = manifest[field]
if (typeof dependencies !== 'object' || dependencies === null || Array.isArray(dependencies)) continue
const entries = dependencies as Record<string, unknown>
for (const name of Object.keys(entries)) {
if (releasePackageNames.has(name)) {
entries[name] = version
}
}
}
return manifest
}
async function startPublishedPackageRegistry(root: string): Promise<PublishedPackageRegistry> {
const staging = join(root, 'published-repository-plugin')
const artifacts = join(root, 'npm-registry-artifacts')
mkdirSync(staging)
mkdirSync(artifacts)
cpSync(join(repositoryPluginPackage, 'lib'), join(staging, 'lib'), { recursive: true })
for (const filename of ['README.md', 'README.zh.md', 'README.i18n.yaml']) {
cpSync(join(repositoryPluginPackage, filename), join(staging, filename))
}
cpSync(join(repoRoot, 'LICENSE'), join(staging, 'LICENSE'))
const manifest = publishedManifest()
writeFileSync(join(staging, 'package.json'), `${JSON.stringify(manifest, undefined, 2)}\n`)
const packed = await execa('pnpm', ['pack', '--pack-destination', artifacts], {
cwd: staging,
reject: false,
})
if (packed.exitCode !== 0) {
throw new Error(`failed to pack the simulated published prepare package:\n${packed.stderr}\n${packed.stdout}`)
}
const tarballs = readdirSync(artifacts).filter(filename => filename.endsWith('.tgz'))
if (tarballs.length !== 1) throw new Error(`expected one simulated published tarball, found ${tarballs.length}`)
const tarball = readFileSync(join(artifacts, tarballs[0]!))
const name = manifest.name as string
const version = manifest.version as string
const requests: string[] = []
let registryUrl = ''
const server = createServer((request, response) => {
const path = decodeURIComponent(new URL(request.url ?? '/', registryUrl).pathname)
requests.push(`${request.method ?? 'GET'} ${path}`)
if (path === `/${name}`) {
const metadata = {
name,
'dist-tags': { latest: version },
versions: {
[version]: {
...manifest,
dist: {
tarball: `${registryUrl}${name}/-/${name.split('/').at(-1)}-${version}.tgz`,
shasum: createHash('sha1').update(tarball).digest('hex'),
integrity: `sha512-${createHash('sha512').update(tarball).digest('base64')}`,
},
},
},
}
response.writeHead(200, { 'content-type': 'application/json' })
response.end(JSON.stringify(metadata))
return
}
if (path === `/${name}/-/${name.split('/').at(-1)}-${version}.tgz`) {
response.writeHead(200, {
'content-type': 'application/octet-stream',
'content-length': String(tarball.length),
})
response.end(tarball)
return
}
response.writeHead(404, { 'content-type': 'application/json' })
response.end(JSON.stringify({ error: 'not found' }))
})
await new Promise<void>((resolve, reject) => {
server.once('error', reject)
server.listen(0, '127.0.0.1', resolve)
})
const address = server.address()
if (address === null || typeof address === 'string') throw new Error('simulated npm registry did not expose a TCP address')
registryUrl = `http://127.0.0.1:${address.port}/`
return {
url: registryUrl,
requests,
close: () => new Promise<void>((resolve, reject) => {
server.close((error) => { if (error === undefined) resolve(); else reject(error) })
}),
}
}
describe.skipIf(!enabled)('dsh run GitHub repository Plugin installation', () => {
it('installs the published prepare dependency, then builds and runs skill, MCP, and TypeScript Plugin contributions from a private exact GitHub source', async () => {
expect(existsSync(dshBin), 'the repository Plugin acceptance must run the built dsh entry').toBe(true)
expect(source, 'DSH_GITHUB_REPOSITORY_PLUGIN_SOURCE is required by this CI lane').toMatch(
/^github:[^/\s#&]+\/[^/\s#&]+#[0-9a-f]{40}&path:\/.*\/\.dsh-plugin$/u,
)
const apiKey = 'github-repository-plugin-e2e-key'
const server = await startMockLlmServer({
sequence: ['tool_call_success', 'success'],
apiKey,
toolName: 'mcp__github_repository__proof',
toolArguments: '{}',
successText: 'trusted GitHub repository package reached dsh run',
})
const home = mkdtempSync(join(tmpdir(), 'dsh-github-repository-plugin-'))
const registry = await startPublishedPackageRegistry(home)
const npmrc = join(home, 'npmrc')
writeFileSync(npmrc, `@deepseek-ai:registry=${registry.url}\n`)
const hostBin = join(home, 'host-bin')
mkdirSync(hostBin)
writeFileSync(join(hostBin, 'dsh-plugin-prepare'), [
'#!/bin/sh',
'echo "host PATH supplied dsh-plugin-prepare instead of the declared npm dependency" >&2',
'exit 91',
'',
].join('\n'), { mode: 0o700 })
const patch = join(home, 'github-repository-plugin.cordis.patch.yml')
writeFileSync(patch, [
'- id: repository-plugins',
' config:',
' repositories:',
` - ${JSON.stringify(source)}`,
'- id: session-title-llm',
' disabled: true',
'',
].join('\n'))
try {
const result = await execa(process.execPath, [
dshBin,
'run',
'--patch',
patch,
'prove the private GitHub repository Plugin is active',
], {
cwd: repoRoot,
input: '',
timeout: 180_000,
killSignal: 'SIGKILL',
reject: false,
env: {
...process.env,
DSH_HOME: home,
DSH_TELEMETRY_DISABLED: '1',
DEEPSEEK_API_KEY: apiKey,
DEEPSEEK_BASE_URL: server.baseURL,
NPM_CONFIG_USERCONFIG: npmrc,
// A warm runner cache could satisfy the exact tarball without
// contacting this test's registry, which would stop proving the
// unpublished package was installed through the simulated release.
PNPM_CONFIG_CACHE_DIR: join(home, 'pnpm-cache'),
PNPM_CONFIG_STORE_DIR: join(home, 'pnpm-store'),
PATH: process.env.PATH === undefined ? hostBin : `${hostBin}${delimiter}${process.env.PATH}`,
},
})
if (result.timedOut) {
throw new Error(`dsh GitHub repository Plugin run did not exit within 180s. stdout:\n${result.stdout}\nstderr:\n${result.stderr}`)
}
expect(result.exitCode, `${result.stderr}\nstdout:\n${result.stdout}`).toBe(0)
expect(result.stdout).toBe('trusted GitHub repository package reached dsh run')
expect(server.requests).toHaveLength(2)
const runtimeDiagnostic = `${result.stderr}\nstdout:\n${result.stdout}`
expect(registry.requests, runtimeDiagnostic).toContain('GET /@deepseek-ai/dsh-repository-plugin')
expect(registry.requests, runtimeDiagnostic).toContain('GET /@deepseek-ai/dsh-repository-plugin/-/dsh-repository-plugin-0.0.1.tgz')
const firstRequest = JSON.stringify(server.requests[0]!.body)
const secondRequest = JSON.stringify(server.requests[1]!.body)
expect(firstRequest, runtimeDiagnostic).toContain(
'Proves that dsh installed a private repository Plugin from an exact GitHub source.',
)
expect(firstRequest, runtimeDiagnostic).toContain('mcp__github_repository__proof')
expect(firstRequest, runtimeDiagnostic).toContain('Proves that an MCP server compiled from the exact GitHub repository package is active.')
expect(secondRequest, runtimeDiagnostic).toContain('MCP_FROM_GITHUB_REPOSITORY')
expect(secondRequest, runtimeDiagnostic).toContain('TS_PLUGIN_FROM_GITHUB_REPOSITORY')
const cacheRoot = join(home, 'cache', 'repository-plugins')
const generations = readdirSync(cacheRoot, { withFileTypes: true }).filter(entry => entry.isDirectory())
expect(generations).toHaveLength(1)
const installed = join(cacheRoot, generations[0]!.name, 'node_modules', 'repository')
const manifest = JSON.parse(readFileSync(join(installed, 'package.json'), 'utf8')) as Record<string, unknown>
expect(manifest).toMatchObject({
name: 'dsh-github-repository-plugin-e2e-fixture',
private: true,
scripts: {
prepack: 'tsc --noEmit && tsdown src/plugin.ts src/mcp-server.ts --no-config --tsconfig tsconfig.json --out-dir lib --platform node --target es2024 --clean && dsh-plugin-prepare',
},
dsh: {
skills: ['../skills'],
mcpServers: './.mcp.json',
entry: './lib/plugin.mjs',
},
dependencies: {
'@modelcontextprotocol/sdk': '1.29.0',
},
devDependencies: {
'@deepseek-ai/dsh-repository-plugin': '0.0.1',
cordis: '4.0.0-rc.7',
tsdown: '0.22.2',
typescript: '6.0.3',
},
})
expect(readFileSync(join(installed, 'dsh-plugin-assets/skills/0/github-source-proof/SKILL.md'), 'utf8'))
.toContain('This skill exists only in the GitHub repository source fixture.')
expect(readFileSync(join(installed, 'dsh-plugin-assets/.mcp.json'), 'utf8')).toContain('lib/mcp-server.mjs')
expect(readFileSync(join(installed, 'lib/plugin.mjs'), 'utf8')).toContain('TS_PLUGIN_FROM_GITHUB_REPOSITORY')
expect(readFileSync(join(installed, 'lib/mcp-server.mjs'), 'utf8')).toContain('MCP_FROM_GITHUB_REPOSITORY')
expect(existsSync(join(installed, 'src'))).toBe(false)
const installedRequire = createRequire(join(installed, 'lib/mcp-server.mjs'))
expect(existsSync(installedRequire.resolve('@modelcontextprotocol/sdk/server/mcp.js'))).toBe(true)
const wrapper = readFileSync(join(installed, 'dsh-plugin.mjs'), 'utf8')
expect(wrapper).toContain('dsh-repository-plugin')
expect(wrapper).toContain('await import(manifest.entry)')
expect(wrapper).toContain('"entry":"./lib/plugin.mjs"')
} finally {
await server.close()
await registry.close()
rmSync(home, { recursive: true, force: true })
}
}, 190_000)
})