返回源码地图

packages/sdk/client/src/launch.ts

main snapshot · da00f7f5358f · 正文引用章节 14;完整原文可核对,不声称全文件人工逐行审计

完整原文供逐行核对;页面收录不代表每行都经过人工语义审核。MIT 许可见 许可证。

1/**
2 * Resolve the public SDK launch configuration to one dsh subprocess.
3 * @module @deepseek-ai/dsh-sdk-client/launch
4 */
5
6import { existsSync, readFileSync } from 'node:fs'
7import { dirname, resolve } from 'node:path'
8import { fileURLToPath } from 'node:url'
9import type { HarnessClientOptions } from './types.ts'
10
11/** Default bound for a profile to answer the SDK initialize handshake. */
12export const DEFAULT_INITIALIZE_TIMEOUT_MS = 10_000
13
14/** Internal generic process launch used by the transport and fake-runtime tests. */
15export interface RuntimeProcessOptions {
16 command: string
17 args: string[]
18 cwd?: string
19 /** Materialize the complete child environment when the client starts its subprocess. */
20 environment: () => NodeJS.ProcessEnv
21 description: string
22 initializeTimeoutMs: number
23 requestTimeoutMs?: number
24 shutdownTimeoutMs?: number
25 disposeEofGraceMs?: number
26 disposeGraceMs?: number
27}
28
29/** Node argv plus internal profile patches required by one resolved dsh entry. */
30export interface DshNodeLaunch {
31 /** Arguments before the profile selector. */
32 nodeArgs: string[]
33 /** Internal patches applied below caller-supplied patches. */
34 patches: string[]
35 /** Environment values required by the resolved entry mode. */
36 environment: NodeJS.ProcessEnv
37}
38
39interface PackageManifest {
40 version?: unknown
41 bin?: unknown
42}
43
44/** Read a package manifest from one resolved package.json URL. */
45function manifest(url: string): PackageManifest {
46 return JSON.parse(readFileSync(fileURLToPath(url), 'utf8')) as PackageManifest
47}
48
49/**
50 * Resolve and version-check a dsh executable from package manifests.
51 * @param dshManifestUrl - resolved URL of the dsh package manifest.
52 * @param clientManifestUrl - resolved URL of the SDK client manifest.
53 * @returns the absolute dsh executable path.
54 */
55export function resolveDshBinFromManifests(dshManifestUrl: string, clientManifestUrl: string): string {
56 const dshManifest = manifest(dshManifestUrl)
57 const clientManifest = manifest(clientManifestUrl)
58 if (typeof dshManifest.version !== 'string' || dshManifest.version !== clientManifest.version) {
59 throw new Error(`dsh SDK client ${String(clientManifest.version)} requires the same dsh version, got ${String(dshManifest.version)}`)
60 }
61 const bin = typeof dshManifest.bin === 'object' && dshManifest.bin !== null
62 ? (dshManifest.bin as Record<string, unknown>).dsh
63 : dshManifest.bin
64 if (typeof bin !== 'string' || bin === '') throw new Error('@deepseek-ai/dsh declares no dsh executable')
65 return resolve(dirname(fileURLToPath(dshManifestUrl)), bin)
66}
67
68/**
69 * Resolve and version-check the built dsh executable installed with this SDK.
70 * @returns the absolute built executable path, whether or not it exists in a source checkout.
71 */
72export function installedDshBin(): string {
73 return resolveDshBinFromManifests(
74 import.meta.resolve('@deepseek-ai/dsh/package.json'),
75 new URL('../package.json', import.meta.url).href,
76 )
77}
78
79/**
80 * Resolve the Node launch for one same-version dsh package.
81 * @param dshManifestUrl - resolved URL of the dsh package manifest.
82 * @param clientManifestUrl - resolved URL of the SDK client manifest.
83 * @param sourceLoaderUrl - optional absolute tsx loader URL for deterministic tests.
84 * @returns built output, or the source entry plus its compatibility patch and tsx environment.
85 */
86export function resolveDshNodeLaunchFromManifests(
87 dshManifestUrl: string,
88 clientManifestUrl: string,
89 sourceLoaderUrl?: string,
90): DshNodeLaunch {
91 const bin = resolveDshBinFromManifests(dshManifestUrl, clientManifestUrl)
92 if (existsSync(bin)) return { nodeArgs: [bin], patches: [], environment: {} }
93
94 const packageDir = dirname(fileURLToPath(dshManifestUrl))
95 const sourceBin = resolve(packageDir, 'src/bin.ts')
96 const sourcePatch = resolve(packageDir, 'src/sdk-source.cordis.patch.yml')
97 const sourceTsconfig = resolve(packageDir, 'tsconfig.json')
98 if (!existsSync(sourceBin) || !existsSync(sourcePatch) || !existsSync(sourceTsconfig)) {
99 throw new Error(
100 `@deepseek-ai/dsh is missing its built executable ${bin} and complete source launch files ${sourceBin}, ${sourcePatch}, ${sourceTsconfig}`,
101 )
102 }
103 const loader = sourceLoaderUrl ?? import.meta.resolve('tsx/esm')
104 return {
105 nodeArgs: ['--import', loader, sourceBin],
106 patches: [sourcePatch],
107 environment: { TSX_TSCONFIG_PATH: sourceTsconfig },
108 }
109}
110
111/**
112 * Resolve the installed dsh package to a built or source Node launch.
113 * @returns the launch descriptor for the current checkout or installed package.
114 */
115function installedDshNodeLaunch(): DshNodeLaunch {
116 return resolveDshNodeLaunchFromManifests(
117 import.meta.resolve('@deepseek-ai/dsh/package.json'),
118 new URL('../package.json', import.meta.url).href,
119 )
120}
121
122/**
123 * Resolve caller-relative filesystem inputs and construct canonical dsh argv.
124 * @param options - public SDK launch options.
125 * @param callerCwd - parent-process directory used for lexical resolution.
126 * @returns one generic subprocess spec for the JSON-RPC transport.
127 */
128export function resolveDshLaunch(
129 options: HarnessClientOptions = {},
130 callerCwd: string = process.cwd(),
131): RuntimeProcessOptions {
132 const profile = options.profile ?? 'sdk'
133 const dshLaunch = options.dshBin === undefined
134 ? installedDshNodeLaunch()
135 : { nodeArgs: [resolve(callerCwd, options.dshBin)], patches: [], environment: {} }
136 const patches = [
137 ...dshLaunch.patches,
138 ...(options.patches ?? []).map(path => resolve(callerCwd, path)),
139 ]
140 const dshHome = options.dshHome === undefined ? undefined : resolve(callerCwd, options.dshHome)
141 return {
142 command: process.execPath,
143 args: [...dshLaunch.nodeArgs, '--profile', profile, ...patches.flatMap(path => ['--patch', path])],
144 ...options.processCwd === undefined ? {} : { cwd: resolve(callerCwd, options.processCwd) },
145 environment: () => ({
146 ...(options.env ?? process.env),
147 ...dshLaunch.environment,
148 ...dshHome === undefined ? {} : { DSH_HOME: dshHome },
149 }),
150 description: `dsh profile ${JSON.stringify(profile)}`,
151 initializeTimeoutMs: options.initializeTimeoutMs ?? DEFAULT_INITIALIZE_TIMEOUT_MS,
152 ...options.requestTimeoutMs === undefined ? {} : { requestTimeoutMs: options.requestTimeoutMs },
153 ...options.shutdownTimeoutMs === undefined ? {} : { shutdownTimeoutMs: options.shutdownTimeoutMs },
154 ...options.disposeEofGraceMs === undefined ? {} : { disposeEofGraceMs: options.disposeEofGraceMs },
155 ...options.disposeGraceMs === undefined ? {} : { disposeGraceMs: options.disposeGraceMs },
156 }
157}