@lqc123qwe/car-runtime 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +12 -0
- package/README.md +31 -2
- package/package.json +5 -1
- package/scripts/release-pipeline.ts +19 -2
- package/src/cli.ts +43 -21
- package/src/dx/doctor.ts +9 -7
- package/src/host/facade.ts +44 -7
- package/src/host/hostGateway.ts +2 -1
- package/src/host/stdio.ts +11 -4
- package/src/load/config.ts +210 -137
- package/src/mcp/httpClient.ts +176 -0
- package/src/runtime-core/chatStep.ts +9 -3
- package/src/runtime-core/credentials.ts +6 -1
- package/src/runtime-core/llm.ts +289 -75
- package/src/session/log.ts +22 -4
- package/test/s33-anthropic-adapter.spec.ts +354 -0
- package/test/s34-mcp-remote-transport.spec.ts +250 -0
- package/test/s35-config-ts-zod.spec.ts +145 -0
- package/test/s36-host-tool-surface.spec.ts +250 -0
- package/test/s37-projection-dedupe.spec.ts +161 -0
- package/test/s38-config-jiti.spec.ts +116 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 1.6-S3 · jiti 载体(S38):car.config.ts 经 jiti 自包含转译(D-24)
|
|
3
|
+
*
|
|
4
|
+
* 覆盖:
|
|
5
|
+
* - 非可擦除语法(enum)真载——1.5 D-22 原生类型剥离不可表达的 transform-only 形态,
|
|
6
|
+
* erasable-only 预检登记项随之出清(被取代)
|
|
7
|
+
* - strip-only spawn 差分(1.6-GO-5 生产调用点):`--experimental-strip-types`(无 transform)
|
|
8
|
+
* 下 enum 配置经 jiti 真载成功 + 原生 import 同文件失败(负控——证明语法确属非可擦除、
|
|
9
|
+
* jiti 是载体);同进程实测首载时延(登记口径,无硬门禁,CI 宽上界防抖)
|
|
10
|
+
* - CLI 真路径:car run 于 enum 配置目录——配置经 jiti 装载生效(CAR-E-LLM-CONFIG 引导 =
|
|
11
|
+
* 配置解析通过、模型未配;若配置载体失败应为 CAR-E-CONFIG)
|
|
12
|
+
* - s35 存量零改动全量回归 = 载体迁移验收线(interoptDefault 显式关:jiti 缺省 true 会把
|
|
13
|
+
* 无 default 模块的 exports 整体当 default,「缺 default export」校验即失效)
|
|
14
|
+
*/
|
|
15
|
+
import { test } from 'node:test'
|
|
16
|
+
import assert from 'node:assert/strict'
|
|
17
|
+
import { spawn } from 'node:child_process'
|
|
18
|
+
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
|
19
|
+
import { tmpdir } from 'node:os'
|
|
20
|
+
import { join, dirname } from 'node:path'
|
|
21
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
22
|
+
import { warmCarConfig, loadCarConfig } from '../src/load/config.ts'
|
|
23
|
+
|
|
24
|
+
const CLI = join(dirname(fileURLToPath(import.meta.url)), '..', 'src', 'cli.ts')
|
|
25
|
+
const SRC_CONFIG = join(dirname(fileURLToPath(import.meta.url)), '..', 'src', 'load', 'config.ts')
|
|
26
|
+
|
|
27
|
+
function withTempDir(name: string, fn: (dir: string) => Promise<void> | void): Promise<void> {
|
|
28
|
+
const dir = mkdtempSync(join(tmpdir(), `car-s38-${name}-`))
|
|
29
|
+
try {
|
|
30
|
+
const r = fn(dir)
|
|
31
|
+
const cleanup = () => { try { rmSync(dir, { recursive: true, force: true }) } catch { /* 红线 8 */ } }
|
|
32
|
+
if (r instanceof Promise) return r.finally(cleanup)
|
|
33
|
+
cleanup()
|
|
34
|
+
return Promise.resolve()
|
|
35
|
+
} catch (e) {
|
|
36
|
+
try { rmSync(dir, { recursive: true, force: true }) } catch { /* 红线 8 */ }
|
|
37
|
+
throw e
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
test('S38: 非可擦除语法(enum)经 jiti 真载——D-22 原生类型剥离不可表达形态', async () => {
|
|
42
|
+
await withTempDir('enum', async dir => {
|
|
43
|
+
writeFileSync(join(dir, 'car.config.ts'), [
|
|
44
|
+
`export enum LlmMode { full = 'full-model' }`,
|
|
45
|
+
`export default { llm: { model: LlmMode.full } }`,
|
|
46
|
+
].join('\n'))
|
|
47
|
+
await warmCarConfig({ cwd: dir })
|
|
48
|
+
const r = loadCarConfig({ cwd: dir })
|
|
49
|
+
assert.equal(r.error, undefined)
|
|
50
|
+
assert.equal(r.config.llm?.model, 'full-model', 'enum 值经 jiti 转译进配置')
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test('S38: strip-only spawn 差分——原生 import enum 载体失败(负控)+ jiti 真载成功 + 首载时延实测', async () => {
|
|
55
|
+
await withTempDir('strip', async dir => {
|
|
56
|
+
const configPath = join(dir, 'car.config.ts')
|
|
57
|
+
writeFileSync(configPath, [
|
|
58
|
+
`export enum LlmMode { full = 'strip-model' }`,
|
|
59
|
+
`export default { llm: { model: LlmMode.full } }`,
|
|
60
|
+
].join('\n'))
|
|
61
|
+
// 夹具自身仅可擦除语法(strip-only 可载);非可擦除只出现在被测配置里
|
|
62
|
+
const fixture = join(dir, 'fixture.ts')
|
|
63
|
+
writeFileSync(fixture, [
|
|
64
|
+
`import { warmCarConfig, loadCarConfig } from ${JSON.stringify(pathToFileURL(SRC_CONFIG).href)}`,
|
|
65
|
+
`import { pathToFileURL as toURL } from 'node:url'`,
|
|
66
|
+
`const configPath = process.argv[2]!`,
|
|
67
|
+
`let nativeError = ''`,
|
|
68
|
+
`try { await import(toURL(configPath).href) } catch (e) { nativeError = String((e as Error).message) }`,
|
|
69
|
+
`if (!nativeError) { console.log('NATIVE_UNEXPECTEDLY_OK'); process.exit(1) }`,
|
|
70
|
+
`const t0 = performance.now()`,
|
|
71
|
+
`await warmCarConfig({ explicitPath: configPath })`,
|
|
72
|
+
`const elapsed = Math.round(performance.now() - t0)`,
|
|
73
|
+
`const r = loadCarConfig({ explicitPath: configPath })`,
|
|
74
|
+
`if (r.error) { console.log('JITI_FAIL ' + r.error); process.exit(1) }`,
|
|
75
|
+
`console.log('OK model=' + r.config.llm?.model + ' firstLoadMs=' + elapsed + ' nativeRejected=' + nativeError.slice(0, 60).replace(/\\n/g, ' '))`,
|
|
76
|
+
].join('\n'))
|
|
77
|
+
// --experimental-strip-types:仅类型剥离无转译(22.19+ 可用;enum 属 transform-only 形态)
|
|
78
|
+
const { code, stdout } = await new Promise<{ code: number | null; stdout: string }>(resolve => {
|
|
79
|
+
const child = spawn(process.execPath, ['--experimental-strip-types', fixture, configPath], { stdio: ['ignore', 'pipe', 'pipe'] })
|
|
80
|
+
let out = ''
|
|
81
|
+
child.stdout.on('data', d => { out += d })
|
|
82
|
+
child.on('exit', c => resolve({ code: c, stdout: out }))
|
|
83
|
+
})
|
|
84
|
+
assert.equal(code, 0, 'strip-only 环境下 jiti 真载 enum 配置')
|
|
85
|
+
const m = /OK model=(\S+) firstLoadMs=(\d+) nativeRejected=(.+)/.exec(stdout.trim())!
|
|
86
|
+
assert.equal(m[1], 'strip-model')
|
|
87
|
+
assert.ok(m[2] !== undefined, '首载时延实测出值')
|
|
88
|
+
console.log(` [登记] car.config.ts jiti 首载时延(spawn 冷启,本机实跑):${m[2]} ms`)
|
|
89
|
+
assert.ok(Number(m[2]) < 15_000, '首载时延宽上界(CI 防抖;真实数值进收口报告)')
|
|
90
|
+
assert.ok(m[3]!.length > 0, '负控:原生 import 对非可擦除语法显式失败——jiti 是载体而非原生剥离')
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
test('S38: CLI 真路径——car run 于 enum 配置目录:配置经 jiti 装载生效(模型未配引导而非配置报错)', async () => {
|
|
95
|
+
await withTempDir('cli', async dir => {
|
|
96
|
+
writeFileSync(join(dir, 'car.config.ts'), [
|
|
97
|
+
`export enum LlmMode { full = 'full-model' }`,
|
|
98
|
+
`export default { llm: { model: LlmMode.full } }`,
|
|
99
|
+
].join('\n'))
|
|
100
|
+
const plugin = join(dir, 'noop.ts')
|
|
101
|
+
writeFileSync(plugin, [
|
|
102
|
+
`export const manifest = { name: "s38p", version: "1.0.0" }`,
|
|
103
|
+
`export default function apply(api) { api.registerTool({ name: "noop", declaredSideEffect: "readonly", run: async () => "ok" }) }`,
|
|
104
|
+
].join('\n'))
|
|
105
|
+
const { code, output } = await new Promise<{ code: number | null; output: string }>(resolve => {
|
|
106
|
+
const child = spawn(process.execPath, ['--experimental-transform-types', CLI, 'run', plugin], { cwd: dir, stdio: ['ignore', 'pipe', 'pipe'] })
|
|
107
|
+
let out = ''
|
|
108
|
+
child.stdout.on('data', d => { out += d })
|
|
109
|
+
child.stderr.on('data', d => { out += d })
|
|
110
|
+
child.on('exit', c => resolve({ code: c, output: out }))
|
|
111
|
+
})
|
|
112
|
+
assert.equal(code, 2, 'D-13:无凭据/端点 → exit 2 显式引导')
|
|
113
|
+
assert.match(output, /CAR-E-LLM-CONFIG/, '配置经 jiti 解析通过(enum 值进 llm.model)走到模型就绪检查')
|
|
114
|
+
assert.doesNotMatch(output, /CAR-E-CONFIG/, '配置载体本身零错误')
|
|
115
|
+
})
|
|
116
|
+
})
|