build(vendor): rescope the vendored Cordis packages into @deepseek-ai

Machine-produced by `pnpm run rescope-vendor --apply` plus the regeneration it
prints: `pnpm install` for the lockfile, `pnpm run gen-third-party-notices`,
`verify-translation-pairing --write` for the touched bilingual pairs,
`gen-doc-graphs`, and one typert snapshot whose ids embed character offsets.
`pnpm run rescope-vendor --check` verifies the result.

Renames nine vendored packages (cordis, cosmokit, schemastery and the six
@cordisjs plugins) and every reference that resolves them: manifest names and
dependency keys, module specifiers including declare-module merges, cordis.yml
plugin names, tsconfig paths, every Markdown fence, and `docs/` prose.
Directory names, upstream versions, and dependency ranges are unchanged, so
vendor/README.md still reads as an upstream snapshot; its manifest table gains
an upstream-name column so THIRD_PARTY_NOTICES keeps MIT attribution pointed
at each fork's origin.

The tutorial tier follows the rename end to end: its yaml fences named plugins
the Loader can no longer resolve, its `ts ignore-check` fences disagreed with
the compiled fences beside them, and its prose quoted both. The contracts that
told readers to keep upstream names — the root convention and the vendoring
cookbook's tree comment and manifest invariant — now say to rescope instead.

Two rules read `@deepseek-ai/` as "another workspace plugin": the client bundle
purity gate now names the vendored libraries a browser bundle inlines, and the
files where a bare `cordis` is an agent-preset id keep that product data.
This commit is contained in:
imccyu
2026-08-10 22:04:06 +08:00
parent 78e9b8bec5
commit ec601ca13d
1400 changed files with 3337 additions and 3317 deletions

View File

@@ -21,15 +21,15 @@ const workspaceGlobs = [
{ dir: 'apps', depth: 1 },
] as const
const vendoredPackages = new Set([
'cordis',
'cosmokit',
'schemastery',
'@cordisjs/plugin-loader',
'@cordisjs/plugin-include',
'@cordisjs/plugin-group',
'@cordisjs/plugin-timer',
'@cordisjs/plugin-hmr',
'@cordisjs/plugin-logger-console',
'@deepseek-ai/cordis',
'@deepseek-ai/cosmokit',
'@deepseek-ai/schemastery',
'@deepseek-ai/cordis-plugin-loader',
'@deepseek-ai/cordis-plugin-include',
'@deepseek-ai/cordis-plugin-group',
'@deepseek-ai/cordis-plugin-timer',
'@deepseek-ai/cordis-plugin-hmr',
'@deepseek-ai/cordis-plugin-logger-console',
])
const publicLandlockPackages = new Set([
'@deepseek-ai/node-addon-landlock-run',
@@ -271,13 +271,13 @@ function checkWorkspace({ dir, manifest }: WorkspaceManifest): string[] {
}
if (dir.startsWith('packages/') && manifest.name?.startsWith('@deepseek-ai/dsh-')) {
const peer = manifest.peerDependencies?.cordis
const dev = manifest.devDependencies?.cordis
const peer = manifest.peerDependencies?.['@deepseek-ai/cordis']
const dev = manifest.devDependencies?.['@deepseek-ai/cordis']
if (!peer) errors.push(`${label}: cordis must be a peerDependency`)
if (!dev) errors.push(`${label}: cordis must also be a devDependency`)
if (!peer) errors.push(`${label}: @deepseek-ai/cordis must be a peerDependency`)
if (!dev) errors.push(`${label}: @deepseek-ai/cordis must also be a devDependency`)
if (peer && dev && peer !== dev) {
errors.push(`${label}: cordis peer (${peer}) and dev (${dev}) ranges must match`)
errors.push(`${label}: @deepseek-ai/cordis peer (${peer}) and dev (${dev}) ranges must match`)
}
if (manifest.version !== repositoryVersion) {
errors.push(`${label}: package.json version must match root version ${repositoryVersion ?? '(missing)'}`)

View File

@@ -11,12 +11,12 @@ import ts from 'typescript'
/** Cheap textual prefilter for a cordis module merge, quote-style agnostic
* (the AST match below reads `stmt.name.text` and never sees the quotes). */
const MERGE_HEAD = /declare module ['"](?:cordis|\.\/context\.ts)['"]/
const MERGE_HEAD = /declare module ['"](?:@deepseek-ai\/cordis|\.\/context\.ts)['"]/
/**
* Parse every file matching `patterns` (repo-relative, sorted, `/`-normalized)
* that textually contains a cordis module merge, yielding one entry per merge
* BLOCK — a file may legally hold several `declare module 'cordis'` blocks
* BLOCK — a file may legally hold several `declare module '@deepseek-ai/cordis'` blocks
* (the Typert analyzer reads them all), so the exhaustiveness scan must too.
* Files without a merge are skipped.
* @param scanRoot - Repository root the patterns are resolved against.
@@ -39,14 +39,14 @@ export function contextMergeFiles(
return out
}
/** Every cordis module-merge body in `sf`: `declare module 'cordis'` (harness
/** Every cordis module-merge body in `sf`: `declare module '@deepseek-ai/cordis'` (harness
* packages) or `declare module './context.ts'` (vendor core), in source order.
* Module-local: consumers walk blocks through {@link contextMergeFiles}. */
function cordisModuleBodies(sf: ts.SourceFile): ts.ModuleBlock[] {
const bodies: ts.ModuleBlock[] = []
for (const stmt of sf.statements) {
if (!ts.isModuleDeclaration(stmt) || !ts.isStringLiteral(stmt.name)) continue
if (stmt.name.text !== 'cordis' && stmt.name.text !== './context.ts') continue
if (stmt.name.text !== '@deepseek-ai/cordis' && stmt.name.text !== './context.ts') continue
if (stmt.body && ts.isModuleBlock(stmt.body)) bodies.push(stmt.body)
}
return bodies
@@ -60,7 +60,7 @@ export function cordisModuleBody(sf: ts.SourceFile): ts.ModuleBlock | null {
}
/**
* Every `key: Type` property a `declare module 'cordis'` Context merge
* Every `key: Type` property a `declare module '@deepseek-ai/cordis'` Context merge
* declares in one module body.
* @param body - The cordis module augmentation block.
* @param sf - Owning source file (for text extraction).
@@ -79,7 +79,7 @@ export function contextKeyMap(body: ts.ModuleBlock, sf: ts.SourceFile): Map<stri
}
/**
* Every event name a `declare module 'cordis'` Events merge declares in one
* Every event name a `declare module '@deepseek-ai/cordis'` Events merge declares in one
* module body. Names are the literal member keys (`'agent/created'`), read
* from method and property members alike so a declaration form the projector
* would reject still enters the exhaustiveness scan.

View File

@@ -128,7 +128,7 @@ describe('cordis-walk scan reach', () => {
const dir = join(root, 'packages/client/ui-x/src/client')
mkdirSync(dir, { recursive: true })
writeFileSync(join(dir, 'index.ts'), [
"declare module 'cordis' {",
"declare module '@deepseek-ai/cordis' {",
' interface Events {',
" 'x/changed'(): void",
' }',
@@ -153,12 +153,12 @@ describe('cordis-walk scan reach', () => {
// backstop must not stop at the first one, skip the double-quoted legal
// form, or ignore .tsx sources.
writeFileSync(join(dir, 'split.ts'), [
"declare module 'cordis' {",
"declare module '@deepseek-ai/cordis' {",
' interface Context {',
' first: FirstService',
' }',
'}',
'declare module "cordis" {',
'declare module "@deepseek-ai/cordis" {',
' interface Events {',
" 'second/changed'(): void",
' }',
@@ -167,7 +167,7 @@ describe('cordis-walk scan reach', () => {
'',
].join('\n'))
writeFileSync(join(dir, 'view.tsx'), [
"declare module 'cordis' {",
"declare module '@deepseek-ai/cordis' {",
' interface Context {',
' fromTsx: TsxService',
' }',
@@ -189,7 +189,7 @@ describe('cordis-walk scan reach', () => {
it('reads string-literal and identifier member names from an Events merge', () => {
const sf = ts.createSourceFile('x.ts', [
"declare module 'cordis' {",
"declare module '@deepseek-ai/cordis' {",
' interface Events {',
" 'scope/list'(items: string[]): void",
' plain(): void',

View File

@@ -99,7 +99,7 @@ export const SERVICE_PAGE: Record<string, string> = {
/**
* Context keys declared in `interface Context` merges that the rendering
* projection cannot see, each with the reason and its documentation owner.
* The scan that enforces this list reads EVERY `declare module 'cordis'`
* The scan that enforces this list reads EVERY `declare module '@deepseek-ai/cordis'`
* Context merge under `packages/x/x/src/**` — any depth, not only root
* `index.ts` files with a same-named service class — so a new service can
* never silently join this blind spot: it either enters {@link SERVICE_PAGE}
@@ -168,7 +168,7 @@ export const EVENT_SCOPE_PAGE: Record<string, string> = {
* Event names declared in `interface Events` merges that the rendering
* projection cannot see, each with the reason and its documentation owner.
* The mirror of {@link SERVICE_WALK_EXEMPTIONS} for events: an independent
* scan reads EVERY `declare module 'cordis'` Events merge under
* scan reads EVERY `declare module '@deepseek-ai/cordis'` Events merge under
* `packages/x/x/src/**`, so a declared event either renders onto a subsystems
* page (via {@link EVENT_SCOPE_PAGE}) or names itself here — never vanishes
* silently. Keys are full event names, not scopes: client-face events share

View File

@@ -309,14 +309,14 @@ class ScopedEventGenerator {
}
}
/** Return whether an Events interface is inside declare module 'cordis'. */
/** Return whether an Events interface is inside declare module '@deepseek-ai/cordis'. */
function isCordisModuleInterface(node: ts.InterfaceDeclaration): boolean {
const block = node.parent
const declaration = block.parent
return ts.isModuleBlock(block)
&& ts.isModuleDeclaration(declaration)
&& ts.isStringLiteral(declaration.name)
&& declaration.name.text === 'cordis'
&& declaration.name.text === '@deepseek-ai/cordis'
}
/** Return whether a parameter is the explicit TypeScript this receiver. */

View File

@@ -134,13 +134,17 @@ describe('parseVendoredRows', () => {
const rows = parseVendoredRows(readFileSync(resolve(root, 'vendor/README.md'), 'utf8'))
expect(rows.length).toBeGreaterThan(0)
expect(rows).toContainEqual({ npmName: 'cordis', upstream: 'https://github.com/cordiverse/cordis' })
expect(rows).toContainEqual({
npmName: '@deepseek-ai/cordis',
upstreamName: 'cordis',
upstream: 'https://github.com/cordiverse/cordis',
})
// The upstream column carries a trailing package path for some rows; it is not part of the URL.
expect(rows.every(row => /^https:\/\/\S+$/.test(row.upstream))).toBe(true)
})
it('yields nothing when the table columns change, so the generator fails loud', () => {
expect(parseVendoredRows('| `cordis/` | cordis | 4.0.0 | https://example.com | `abc123` |\n')).toEqual([])
expect(parseVendoredRows('| `cordis/` | `@deepseek-ai/cordis` | cordis | 4.0.0 | https://example.com | `abc123` |\n')).toEqual([])
})
it('covers every vendored directory, so no package can drop out of the notices', () => {

View File

@@ -387,6 +387,8 @@ export function tierExternalDeps(manifests: Map<string, Manifest>, names: Set<st
/** A vendored package row parsed out of the `vendor/README.md` manifest table. */
export interface VendoredRow {
npmName: string
/** The name this package carries upstream; MIT attribution names the fork's origin, not our scope. */
upstreamName: string
upstream: string
}
@@ -398,11 +400,12 @@ export interface VendoredRow {
export function parseVendoredRows(text: string): VendoredRow[] {
const rows: VendoredRow[] = []
for (const line of text.split('\n')) {
const match = /^\| \x60\S+\/\x60 \| \x60([^\x60]+)\x60 \| \S+ \| (https:\/\/\S+?)(?: \([^)]*\))? \| \x60[0-9a-f]+\x60 \|$/.exec(line)
const match = new RegExp(String.raw`^\| \x60\S+\/\x60 \| \x60([^\x60]+)\x60 \| \x60([^\x60]+)\x60 \| \S+ \| `
+ String.raw`(https:\/\/\S+?)(?: \([^)]*\))? \| \x60[0-9a-f]+\x60 \|$`).exec(line)
if (match === null) continue
const [, npmName, upstream] = match
if (npmName === undefined || upstream === undefined) continue
rows.push({ npmName, upstream })
const [, npmName, upstreamName, upstream] = match
if (npmName === undefined || upstreamName === undefined || upstream === undefined) continue
rows.push({ npmName, upstreamName, upstream })
}
return rows
}
@@ -696,11 +699,11 @@ The complete npm transitive closure, including the Landlock launcher workspace,
## Vendored source (\`vendor/\`)
The Cordis framework and its foundation libraries are source-vendored into this repository rather than consumed from npm. All are MIT-licensed; each directory preserves its upstream \`LICENSE\` file. Exact upstream commits and local modifications are recorded in [\`vendor/README.md\`](vendor/README.md).
The Cordis framework and its foundation libraries are source-vendored into this repository rather than consumed from npm, and republished under the \`@deepseek-ai\` scope. All are MIT-licensed; each directory preserves its upstream \`LICENSE\` file. Exact upstream commits and local modifications are recorded in [\`vendor/README.md\`](vendor/README.md).
| Package | Upstream | License |
| --- | --- | --- |
${vendored.map(row => `| \`${row.npmName}\` | [${row.upstream.replace('https://', '')}](${row.upstream}) | MIT |`).join('\n')}
| Package | Upstream name | Upstream | License |
| --- | --- | --- | --- |
${vendored.map(row => `| \`${row.npmName}\` | \`${row.upstreamName}\` | [${row.upstream.replace('https://', '')}](${row.upstream}) | MIT |`).join('\n')}
## Runtime npm dependencies

View File

@@ -8,7 +8,7 @@
import { globSync, readFileSync, writeFileSync } from 'node:fs'
import { basename, resolve } from 'node:path'
import { Context } from 'cordis'
import { Context } from '@deepseek-ai/cordis'
import type { ToolSchema } from '@deepseek-ai/dsh-llm'
import AgentRegistry from '@deepseek-ai/dsh-agent'
import type { Agent } from '@deepseek-ai/dsh-agent'

View File

@@ -258,7 +258,9 @@ class WorkspacePackageSet {
const name = expectString(manifest, 'name', manifestPath)
const version = expectString(manifest, 'version', manifestPath)
const isVendored = manifestPath.startsWith('vendor/')
if (!isVendored && !name.startsWith('@deepseek-ai/')) {
// Vendored packages are rescoped too (vendor/README.md), so publication
// never carries an upstream name that would squat it on the registry.
if (!name.startsWith('@deepseek-ai/')) {
throw new Error(`${manifestPath} must name an @deepseek-ai package`)
}
if (name === '@deepseek-ai/dsh-root') {

View File

@@ -1,7 +1,7 @@
import { describe, expect, it, vi } from 'vitest'
import { Context, FiberState, Service, ValidationError } from 'cordis'
import Loader from '@cordisjs/plugin-loader'
import z from 'schemastery'
import { Context, FiberState, Service, ValidationError } from '@deepseek-ai/cordis'
import Loader from '@deepseek-ai/cordis-plugin-loader'
import z from '@deepseek-ai/schemastery'
import InvariantService from '@deepseek-ai/dsh-invariants'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
import { packageInvariantOwners } from './package-invariants.ts'
@@ -13,7 +13,7 @@ import {
usesManualInvariantTree,
} from './test-invariants.ts'
declare module 'cordis' {
declare module '@deepseek-ai/cordis' {
interface Context {
testInvariantProbe: TestInvariantProbe
}

View File

@@ -6,8 +6,8 @@
*/
import { expect } from 'vitest'
import { FiberState, Inject, RegistryService } from 'cordis'
import type { Context, Plugin } from 'cordis'
import { FiberState, Inject, RegistryService } from '@deepseek-ai/cordis'
import type { Context, Plugin } from '@deepseek-ai/cordis'
import { AttachmentStore } from '@deepseek-ai/dsh-attachment'
import type {
ImageAttachmentLimits,

View File

@@ -165,7 +165,7 @@ function validateEntry(value: unknown, file: string, path: string): void {
}
recordPlugin(value, file)
validateMetadata(value, file, path)
if ((value.group === true || value.name === '@cordisjs/plugin-group') && isUnknownArray(value.config)) {
if ((value.group === true || value.name === '@deepseek-ai/cordis-plugin-group') && isUnknownArray(value.config)) {
for (let index = 0; index < value.config.length; index++) {
validateEntry(value.config[index], file, `${path}.config[${index}]`)
}
@@ -175,7 +175,7 @@ function validateEntry(value: unknown, file: string, path: string): void {
validateEntry(value.insert[index], file, `${path}.insert[${index}]`)
}
}
if (value.name !== '@cordisjs/plugin-include') return
if (value.name !== '@deepseek-ai/cordis-plugin-include') return
const config = value.config
if (!isRecord(config) || !isUnknownArray(config.patches)) return
for (let index = 0; index < config.patches.length; index++) {