@lqc123qwe/car-runtime 1.8.0 → 1.10.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/README.md +29 -1
- package/dist/SHA256SUMS +1 -0
- package/dist/authz/authz.d.ts +44 -0
- package/dist/authz/authz.js +65 -0
- package/dist/car-runtime-1.10.0.tgz +0 -0
- package/dist/car-runtime-1.10.0.tgz.sig.bundle +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +825 -0
- package/dist/dx/doctor.d.ts +62 -0
- package/dist/dx/doctor.js +127 -0
- package/dist/governance/dump.d.ts +47 -0
- package/dist/governance/dump.js +17 -0
- package/dist/host/facade.d.ts +13 -0
- package/dist/host/facade.js +154 -0
- package/dist/host/hostGateway.d.ts +117 -0
- package/dist/host/hostGateway.js +65 -0
- package/dist/host/mappings.d.ts +27 -0
- package/dist/host/mappings.js +52 -0
- package/dist/host/stdio.d.ts +14 -0
- package/dist/host/stdio.js +83 -0
- package/dist/kernel/context.d.ts +76 -0
- package/dist/kernel/context.js +203 -0
- package/dist/kernel/events.d.ts +37 -0
- package/dist/kernel/events.js +82 -0
- package/dist/load/config.d.ts +79 -0
- package/dist/load/config.js +285 -0
- package/dist/load/loader.d.ts +72 -0
- package/dist/load/loader.js +162 -0
- package/dist/load/registry.d.ts +44 -0
- package/dist/load/registry.js +58 -0
- package/dist/load/report.d.ts +98 -0
- package/dist/load/report.js +301 -0
- package/dist/load/sigGate.d.ts +22 -0
- package/dist/load/sigGate.js +68 -0
- package/dist/load/sign.d.ts +25 -0
- package/dist/load/sign.js +47 -0
- package/dist/load/verifier.d.ts +35 -0
- package/dist/load/verifier.js +56 -0
- package/dist/loop/goal.d.ts +31 -0
- package/dist/loop/goal.js +34 -0
- package/dist/loop/recover.d.ts +17 -0
- package/dist/loop/recover.js +39 -0
- package/dist/loop/stop.d.ts +79 -0
- package/dist/loop/stop.js +151 -0
- package/dist/mcp/gateway.d.ts +96 -0
- package/dist/mcp/gateway.js +69 -0
- package/dist/mcp/httpClient.d.ts +27 -0
- package/dist/mcp/httpClient.js +196 -0
- package/dist/mcp/stdioClient.d.ts +8 -0
- package/dist/mcp/stdioClient.js +73 -0
- package/dist/ptc/budget.d.ts +21 -0
- package/dist/ptc/budget.js +36 -0
- package/dist/ptc/erasable.d.ts +14 -0
- package/dist/ptc/erasable.js +27 -0
- package/dist/ptc/runCode.d.ts +65 -0
- package/dist/ptc/runCode.js +140 -0
- package/dist/ptc/schemaRender.d.ts +15 -0
- package/dist/ptc/schemaRender.js +90 -0
- package/dist/ptc/sdk.d.ts +27 -0
- package/dist/ptc/sdk.js +23 -0
- package/dist/ptc/worker-entry.d.ts +1 -0
- package/dist/ptc/worker-entry.js +71 -0
- package/dist/release-audit.jsonl +4 -0
- package/dist/runtime-core/chatStep.d.ts +40 -0
- package/dist/runtime-core/chatStep.js +104 -0
- package/dist/runtime-core/credentials.d.ts +49 -0
- package/dist/runtime-core/credentials.js +118 -0
- package/dist/runtime-core/errors.d.ts +30 -0
- package/dist/runtime-core/errors.js +36 -0
- package/dist/runtime-core/llm.d.ts +76 -0
- package/dist/runtime-core/llm.js +553 -0
- package/dist/runtime-core/redaction.d.ts +39 -0
- package/dist/runtime-core/redaction.js +79 -0
- package/dist/runtime-core/telemetry.d.ts +88 -0
- package/dist/runtime-core/telemetry.js +332 -0
- package/dist/runtime-core/tools.d.ts +15 -0
- package/dist/runtime-core/tools.js +12 -0
- package/dist/runtime-core/trace.d.ts +55 -0
- package/dist/runtime-core/trace.js +137 -0
- package/dist/runtime-core/types.d.ts +84 -0
- package/dist/runtime-core/types.js +13 -0
- package/dist/sandbox/sandbox.d.ts +68 -0
- package/dist/sandbox/sandbox.js +153 -0
- package/dist/sbom.json +263 -0
- package/dist/seam.d.ts +21 -0
- package/dist/seam.js +20 -0
- package/dist/security/secrets-baseline.d.ts +43 -0
- package/dist/security/secrets-baseline.js +176 -0
- package/dist/security/secrets.d.ts +45 -0
- package/dist/security/secrets.js +138 -0
- package/dist/session/export.d.ts +55 -0
- package/dist/session/export.js +81 -0
- package/dist/session/fork.d.ts +51 -0
- package/dist/session/fork.js +63 -0
- package/dist/session/format.d.ts +21 -0
- package/dist/session/format.js +49 -0
- package/dist/session/indexStore.d.ts +44 -0
- package/dist/session/indexStore.js +265 -0
- package/dist/session/log.d.ts +79 -0
- package/dist/session/log.js +157 -0
- package/dist/session/store.d.ts +9 -0
- package/dist/session/store.js +37 -0
- package/dist/telemetry/metrics.d.ts +25 -0
- package/dist/telemetry/metrics.js +26 -0
- package/package.json +17 -1
- package/src/mcp/gateway.ts +10 -2
- package/src/mcp/httpClient.ts +23 -5
- package/src/runtime-core/trace.ts +5 -0
- package/src/seam.ts +21 -0
- package/.gitattributes +0 -11
- package/.github/workflows/ci.yml +0 -293
- package/.github/workflows/release.yml +0 -54
- package/adr/ADR-001.md +0 -4
- package/adr/ADR-002.md +0 -5
- package/adr/ADR-003.md +0 -35
- package/adr/host-onboarding.md +0 -68
- package/adr/mlps-audit-checklist.md +0 -41
- package/adr/win32-spike.md +0 -45
- package/docs/plugin-authoring.md +0 -82
- package/native/landlock-run/Makefile +0 -19
- package/native/landlock-run/landlock-run.c +0 -162
- package/scripts/contract-check.ts +0 -56
- package/scripts/invariant-n1.ts +0 -96
- package/scripts/package-check.ts +0 -29
- package/scripts/ptc-baseline/run-baseline.ts +0 -118
- package/scripts/release-artifacts.ts +0 -111
- package/scripts/release-pipeline.ts +0 -245
- package/scripts/secrets-baseline.ts +0 -33
- package/scripts/secrets-scan.ts +0 -56
- package/scripts/win32-ffi-debug.ts +0 -143
- package/scripts/win32-koffi-spike.ts +0 -537
- package/test/contract/eventmap.golden.json +0 -7
- package/test/fixtures/hello-plugin.ts +0 -5
- package/test/kernel.spec.ts +0 -136
- package/test/registry-e2e/run-e2e.ts +0 -213
- package/test/s11.spec.ts +0 -129
- package/test/s12.spec.ts +0 -135
- package/test/s13.spec.ts +0 -117
- package/test/s14.spec.ts +0 -117
- package/test/s15.spec.ts +0 -80
- package/test/s16.spec.ts +0 -133
- package/test/s17.spec.ts +0 -102
- package/test/s18.spec.ts +0 -81
- package/test/s19.spec.ts +0 -138
- package/test/s2.spec.ts +0 -245
- package/test/s20.spec.ts +0 -241
- package/test/s21.spec.ts +0 -231
- package/test/s22-m7-store-format.spec.ts +0 -135
- package/test/s23-m7-export-index.spec.ts +0 -189
- package/test/s24-signature-gate.spec.ts +0 -185
- package/test/s25-m8-runtime-core.spec.ts +0 -600
- package/test/s26-signature-closeout.spec.ts +0 -349
- package/test/s27-host-load-wiring.spec.ts +0 -134
- package/test/s28-session-index-followup.spec.ts +0 -236
- package/test/s29-otel-producer.spec.ts +0 -252
- package/test/s3.spec.ts +0 -179
- package/test/s30-release-artifacts.spec.ts +0 -127
- package/test/s31-real-model-wiring.spec.ts +0 -329
- package/test/s32-telemetry-mcp-ptc.spec.ts +0 -218
- package/test/s33-anthropic-adapter.spec.ts +0 -354
- package/test/s34-mcp-remote-transport.spec.ts +0 -250
- package/test/s35-config-ts-zod.spec.ts +0 -145
- package/test/s36-host-tool-surface.spec.ts +0 -250
- package/test/s37-projection-dedupe.spec.ts +0 -161
- package/test/s38-config-jiti.spec.ts +0 -116
- package/test/s39-otlp-span-semantics.spec.ts +0 -100
- package/test/s4.spec.ts +0 -169
- package/test/s40-metrics-cumulative.spec.ts +0 -67
- package/test/s41-mcp-serve-turn-span.spec.ts +0 -148
- package/test/s42-traceparent-propagation.spec.ts +0 -415
- package/test/s5.spec.ts +0 -95
- package/test/s6.spec.ts +0 -169
- package/test/s7.spec.ts +0 -136
- package/test/s7b-baseline.spec.ts +0 -20
- package/test/s9.spec.ts +0 -89
- package/test/sandbox-escape/j17-orchestrate.sh +0 -30
- package/test/sandbox-escape/run-matrix.sh +0 -98
- package/tsconfig.json +0 -18
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T-1 · 多宿主归一化 schema(host-mappings-v1,M3-S11)
|
|
3
|
+
*
|
|
4
|
+
* 口径(M3系统设计增补 T-1 / 决议⑤):
|
|
5
|
+
* - session id 归一化:确定性派生 SH- + sha256(hostId|hostSessionId)——幂等免映射表存储;
|
|
6
|
+
* 首次映射落哈希链登记事件(调用方职责);显式拒绝解析宿主 id 语义(对闭源漂移免疫);
|
|
7
|
+
* - 事件归一化:版本化映射表数据驱动(HOST_MAPPINGS),pattern 命中 → CAR EventKind + actor;
|
|
8
|
+
* **未命中一律降级 hostRaw 留痕(零静默,机器可断言)**;
|
|
9
|
+
* - 宿主标识字符串只允许出现在本数据文件(静态架构断言红线,测试强制)。
|
|
10
|
+
*/
|
|
11
|
+
import { createHash } from 'node:crypto';
|
|
12
|
+
/** 跨宿主会话 id:SH- 前缀 + 24 hex(sha256 前 12 字节) */
|
|
13
|
+
export function deriveSessionId(hostId, hostSessionId) {
|
|
14
|
+
return 'SH-' + createHash('sha256').update(`${hostId}|${hostSessionId}`).digest('hex').slice(0, 24);
|
|
15
|
+
}
|
|
16
|
+
/** host-mappings-v1:Claude Code / Codex 等价映射(同一接入面,两份纯数据 profile) */
|
|
17
|
+
export const HOST_MAPPINGS = [
|
|
18
|
+
{
|
|
19
|
+
hostId: 'claude-code',
|
|
20
|
+
schemaVersion: 'host-mappings-v1',
|
|
21
|
+
mappings: [
|
|
22
|
+
{ hostEvent: 'user_message', kind: 'user', actor: 'user', payloadMap: { content: 'text' } },
|
|
23
|
+
{ hostEvent: 'assistant_message', kind: 'assistant', actor: 'model', payloadMap: { content: 'text' } },
|
|
24
|
+
{ hostEvent: 'tool_use', kind: 'toolCall', actor: 'model', payloadMap: { tool_name: 'tool', tool_call_id: 'id' } },
|
|
25
|
+
{ hostEvent: 'tool_result', kind: 'toolResult', actor: 'model', payloadMap: { tool_call_id: 'id' } },
|
|
26
|
+
{ hostEvent: 'turn_complete', kind: 'turnEnd', actor: 'runtime' },
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
hostId: 'codex',
|
|
31
|
+
schemaVersion: 'host-mappings-v1',
|
|
32
|
+
mappings: [
|
|
33
|
+
{ hostEvent: 'input_item', kind: 'user', actor: 'user', payloadMap: { text: 'text' } },
|
|
34
|
+
{ hostEvent: 'agent_message', kind: 'assistant', actor: 'model', payloadMap: { message: 'text' } },
|
|
35
|
+
{ hostEvent: 'function_call', kind: 'toolCall', actor: 'model', payloadMap: { name: 'tool', call_id: 'id' } },
|
|
36
|
+
{ hostEvent: 'function_call_output', kind: 'toolResult', actor: 'model', payloadMap: { call_id: 'id' } },
|
|
37
|
+
{ hostEvent: 'task_complete', kind: 'turnEnd', actor: 'runtime' },
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
/** 事件归一化:命中映射 → 转写;未命中 → hostRaw 降级(payload 原样保留,零静默) */
|
|
42
|
+
export function normalizeHostEvent(profile, hostEvent, payload = {}) {
|
|
43
|
+
const m = profile.mappings.find(x => x.hostEvent === hostEvent);
|
|
44
|
+
if (!m) {
|
|
45
|
+
return { kind: 'hostRaw', actor: 'runtime', payload: { hostId: profile.hostId, hostEvent, raw: payload }, degraded: true };
|
|
46
|
+
}
|
|
47
|
+
const out = {};
|
|
48
|
+
for (const [k, v] of Object.entries(payload)) {
|
|
49
|
+
out[m.payloadMap?.[k] ?? k] = v;
|
|
50
|
+
}
|
|
51
|
+
return { kind: m.kind, actor: m.actor, payload: out, degraded: false };
|
|
52
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface StdioDispatcher {
|
|
2
|
+
(tool: string, args: Record<string, unknown>, meta?: {
|
|
3
|
+
traceparent?: string;
|
|
4
|
+
}): Promise<{
|
|
5
|
+
ok: boolean;
|
|
6
|
+
result?: unknown;
|
|
7
|
+
error?: string;
|
|
8
|
+
}>;
|
|
9
|
+
}
|
|
10
|
+
export interface StdioServer {
|
|
11
|
+
/** 消费 stdin 行直到结束;返回处理请求数(供测试断言) */
|
|
12
|
+
serve(input: NodeJS.ReadableStream, output: NodeJS.WritableStream): Promise<number>;
|
|
13
|
+
}
|
|
14
|
+
export declare function createStdioServer(dispatch: StdioDispatcher): StdioServer;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T-1 · stdio ServerTransport 实装(M3-S12):宿主 → CAR 的 JSON-RPC over stdin/stdout
|
|
3
|
+
*
|
|
4
|
+
* 口径(M3系统设计增补 T-1 / M3部署设计增补 §4 多宿主拓扑:stdio 为 M3 主形态):
|
|
5
|
+
* - 换行分隔 JSON-RPC 2.0(与 M1 client 侧 gateway 同帧格式);
|
|
6
|
+
* - 方法面:initialize/notifications 握手(MCP 协议兼容)+ tools/list(10 tool 能力发现)+ tools/call(分发到 HostGateway.handle);
|
|
7
|
+
* - 所有到达请求与响应均经 HostGateway 审计(无旁路);未登记宿主在 handle 层拒绝;
|
|
8
|
+
* - stdin 结束(宿主退出)→ 通道关闭,进程内状态由审计日志承载(BD-02 等价:不静默丢数据)。
|
|
9
|
+
*
|
|
10
|
+
* 1.8(D-30 W3C traceparent server 侧提取):tools/call params._meta 原样透传 dispatch 第三参——
|
|
11
|
+
* 解析/校验归装配层(trace.ts parseTraceparent fail-open),传输层保持无协议语义;未知键对端
|
|
12
|
+
* 忽略(MCP `_meta` 保留扩展点),缺席 = meta 缺省 undefined(既有调用方零改动)。
|
|
13
|
+
*/
|
|
14
|
+
import { createInterface } from 'node:readline';
|
|
15
|
+
import { HOST_TOOLS } from './hostGateway.js';
|
|
16
|
+
export function createStdioServer(dispatch) {
|
|
17
|
+
let nextId = 0;
|
|
18
|
+
// 1.6-S2:tools/call 按请求序串行派发(会话态依赖 + 响应序确定性——JSON-RPC 允许乱序,
|
|
19
|
+
// 但 stdio 宿主与测试按序解析;tools/list 等同步方法不受此链影响)
|
|
20
|
+
let callChain = Promise.resolve();
|
|
21
|
+
return {
|
|
22
|
+
async serve(input, output) {
|
|
23
|
+
let count = 0;
|
|
24
|
+
const rl = createInterface({ input });
|
|
25
|
+
const done = new Promise(resolve => rl.on('close', resolve));
|
|
26
|
+
rl.on('line', line => {
|
|
27
|
+
const trimmed = line.trim();
|
|
28
|
+
if (!trimmed)
|
|
29
|
+
return;
|
|
30
|
+
count++;
|
|
31
|
+
let req;
|
|
32
|
+
try {
|
|
33
|
+
req = JSON.parse(trimmed);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
output.write(JSON.stringify({ jsonrpc: '2.0', id: null, error: { code: -32700, message: 'parse error' } }) + '\n');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
nextId++;
|
|
40
|
+
const respond = (result, error) => {
|
|
41
|
+
output.write(JSON.stringify({ jsonrpc: '2.0', id: req.id ?? nextId, ...(error ? { error } : { result }) }) + '\n');
|
|
42
|
+
};
|
|
43
|
+
// MCP 协议握手(E-6 实连前置):真实宿主客户端先发 initialize → 回 serverInfo;
|
|
44
|
+
// notifications/initialized 为无 id 通知,不响应(协议规定)
|
|
45
|
+
if (req.method === 'initialize') {
|
|
46
|
+
respond({
|
|
47
|
+
protocolVersion: req.params?.protocolVersion ?? '2024-11-05',
|
|
48
|
+
capabilities: { tools: { listChanged: false } },
|
|
49
|
+
serverInfo: { name: 'car-runtime', version: '0.3.0' },
|
|
50
|
+
});
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (req.method === 'notifications/initialized' || (req.method ?? '').startsWith('notifications/')) {
|
|
54
|
+
return; // 通知无响应
|
|
55
|
+
}
|
|
56
|
+
if (req.method === 'tools/list') {
|
|
57
|
+
respond({ tools: HOST_TOOLS.map((t) => ({ name: t })) });
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (req.method === 'tools/call') {
|
|
61
|
+
const name = req.params?.name ?? '';
|
|
62
|
+
const args = req.params?.arguments ?? {};
|
|
63
|
+
// 1.8-D30:_meta 原样透传(解析归装配层);缺席 = undefined
|
|
64
|
+
const meta = req.params?._meta;
|
|
65
|
+
callChain = callChain
|
|
66
|
+
.then(() => dispatch(name, args, meta))
|
|
67
|
+
.then(r => {
|
|
68
|
+
if (r.ok)
|
|
69
|
+
respond({ content: [{ type: 'text', text: JSON.stringify(r.result ?? {}) }], carOk: true });
|
|
70
|
+
else
|
|
71
|
+
respond(undefined, { code: -32000, message: r.error ?? 'host call failed' });
|
|
72
|
+
})
|
|
73
|
+
.catch(e => respond(undefined, { code: -32000, message: String(e) }));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
respond(undefined, { code: -32601, message: `unknown method "${req.method}"(支持 initialize / tools/list / tools/call)` });
|
|
77
|
+
});
|
|
78
|
+
await done;
|
|
79
|
+
await callChain; // 串行链收口:进程退出前最后一批调用响应写完(win32 退出断言同源教训)
|
|
80
|
+
return count;
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CAR 微内核骨架(F1 可逆注册+依赖注入 / F9 加载-运行两阶段 / ADR-002 双模式卸载)
|
|
3
|
+
*
|
|
4
|
+
* 设计口径(《系统设计》v1.1):
|
|
5
|
+
* - 自研实现,范式借鉴 dsh/Cordis(inject 依赖推导、ctx.effect 可逆注册),代码不复用
|
|
6
|
+
* - 卸载双模式(ADR-002):strict-topo = 消费者先于提供者逐层排空(依赖计数屏障);
|
|
7
|
+
* concurrent = 跨 fiber Promise.all 并发。fiber 内一律注册逆序串行(确定性回卷)
|
|
8
|
+
* - 加载期错误必须显式(冲突链定位),禁止静默覆盖(US-7/N2)
|
|
9
|
+
*/
|
|
10
|
+
export type Disposer = () => void | Promise<void>;
|
|
11
|
+
export interface PluginDef {
|
|
12
|
+
name: string;
|
|
13
|
+
/** 声明所需服务依赖;加载顺序由依赖推导(缺依赖 = PENDING 等待,不手工编排) */
|
|
14
|
+
inject?: string[];
|
|
15
|
+
apply(ctx: PluginContext): void | Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export interface DisposeReport {
|
|
18
|
+
unloaded: string[];
|
|
19
|
+
/** 实际卸载顺序(strict-topo = 消费者→提供者;供治理视图审计,M2) */
|
|
20
|
+
order: string[];
|
|
21
|
+
errors: {
|
|
22
|
+
plugin: string;
|
|
23
|
+
label: string;
|
|
24
|
+
error: unknown;
|
|
25
|
+
}[];
|
|
26
|
+
}
|
|
27
|
+
export declare class Context {
|
|
28
|
+
private fibers;
|
|
29
|
+
private services;
|
|
30
|
+
private pending;
|
|
31
|
+
/** 提供服务(provider 侧);重复 provide 同名服务 = 加载期显式报错(禁静默覆盖) */
|
|
32
|
+
provide(name: string, impl: unknown, provider?: string): void;
|
|
33
|
+
/** 读取服务(consumer 侧,仅限已 inject 的插件 apply 内) */
|
|
34
|
+
get(name: string): unknown;
|
|
35
|
+
/** 根作用域副作用登记(宿主 SDK 面,§3.2.M8 SDK 行)——与插件 fiber Effect 同纪律:body 即执行,disposer 入回卷清单 */
|
|
36
|
+
private rootDisposables;
|
|
37
|
+
/** 根作用域 Effect:宿主侧注册(RuntimeCore.registerLlmAdapter 等)入 disposeRuntime 回卷范围 */
|
|
38
|
+
effect(body: () => Disposer | Disposer[], label?: string): void;
|
|
39
|
+
/** 注册插件:依赖就绪即挂载,否则进入 PENDING 队列 */
|
|
40
|
+
plugin(def: PluginDef): void;
|
|
41
|
+
private mount;
|
|
42
|
+
private tryMountPending;
|
|
43
|
+
private defs;
|
|
44
|
+
/** 卸载单个插件(回卷其全部副作用;fiber 内注册逆序) */
|
|
45
|
+
unload(name: string): Promise<void>;
|
|
46
|
+
/** 运行时整体卸载(ADR-002 双模式) */
|
|
47
|
+
disposeRuntime(opts?: {
|
|
48
|
+
order?: 'strict-topo' | 'concurrent';
|
|
49
|
+
}): Promise<DisposeReport>;
|
|
50
|
+
/** 治理视图(F14):装配配置树快照(插件/依赖/服务/副作用计数/状态)——只读,不改运行时 */
|
|
51
|
+
governanceSnapshot(): {
|
|
52
|
+
plugins: {
|
|
53
|
+
name: string;
|
|
54
|
+
inject: string[];
|
|
55
|
+
state: "ACTIVE" | "DISPOSED" | "DISPOSING" | "PENDING";
|
|
56
|
+
effects: number;
|
|
57
|
+
effectLabels: string[];
|
|
58
|
+
provides: string[];
|
|
59
|
+
}[];
|
|
60
|
+
services: {
|
|
61
|
+
name: string;
|
|
62
|
+
provider: string;
|
|
63
|
+
}[];
|
|
64
|
+
pending: string[];
|
|
65
|
+
};
|
|
66
|
+
/** 依赖图拓扑分层(Kahn 分层;环 = 加载期显式报错) */
|
|
67
|
+
private topoOrder;
|
|
68
|
+
}
|
|
69
|
+
/** 插件作用域上下文(effect 注册绑定到插件 fiber —— POC-2 实证的约束:不得透传根 ctx) */
|
|
70
|
+
export interface PluginContext {
|
|
71
|
+
pluginName: string;
|
|
72
|
+
/** 可逆注册:body 返回 disposer 或 disposer 数组;卸载时按注册逆序回卷 */
|
|
73
|
+
effect(body: () => Disposer | Disposer[], label?: string): void;
|
|
74
|
+
provide(name: string, impl: unknown): void;
|
|
75
|
+
get(name: string): unknown;
|
|
76
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
const DEP_CYCLE = 'CAR-E-DEPCYCLE';
|
|
2
|
+
export class Context {
|
|
3
|
+
fibers = new Map();
|
|
4
|
+
services = new Map();
|
|
5
|
+
pending = [];
|
|
6
|
+
/** 提供服务(provider 侧);重复 provide 同名服务 = 加载期显式报错(禁静默覆盖) */
|
|
7
|
+
provide(name, impl, provider = '<root>') {
|
|
8
|
+
if (this.services.has(name)) {
|
|
9
|
+
throw new Error(`${DEP_CYCLE}: service "${name}" already provided by "${this.services.get(name).provider}" (conflict chain: ${provider})`);
|
|
10
|
+
}
|
|
11
|
+
this.services.set(name, { impl, provider });
|
|
12
|
+
this.tryMountPending();
|
|
13
|
+
}
|
|
14
|
+
/** 读取服务(consumer 侧,仅限已 inject 的插件 apply 内) */
|
|
15
|
+
get(name) {
|
|
16
|
+
const s = this.services.get(name);
|
|
17
|
+
if (!s)
|
|
18
|
+
throw new Error(`${DEP_CYCLE}: service "${name}" not provided (inject 声明缺失或 provider 未就绪)`);
|
|
19
|
+
return s.impl;
|
|
20
|
+
}
|
|
21
|
+
/** 根作用域副作用登记(宿主 SDK 面,§3.2.M8 SDK 行)——与插件 fiber Effect 同纪律:body 即执行,disposer 入回卷清单 */
|
|
22
|
+
rootDisposables = [];
|
|
23
|
+
/** 根作用域 Effect:宿主侧注册(RuntimeCore.registerLlmAdapter 等)入 disposeRuntime 回卷范围 */
|
|
24
|
+
effect(body, label = 'anonymous') {
|
|
25
|
+
const res = body();
|
|
26
|
+
const disposers = typeof res === 'function' ? [res] : [...res];
|
|
27
|
+
for (const d of disposers)
|
|
28
|
+
this.rootDisposables.push({ label, disposer: d });
|
|
29
|
+
}
|
|
30
|
+
/** 注册插件:依赖就绪即挂载,否则进入 PENDING 队列 */
|
|
31
|
+
plugin(def) {
|
|
32
|
+
if (this.fibers.has(def.name)) {
|
|
33
|
+
throw new Error(`${DEP_CYCLE}: plugin "${def.name}" already registered (duplicate registration is blocked)`);
|
|
34
|
+
}
|
|
35
|
+
const fiber = { name: def.name, inject: def.inject ?? [], disposables: [], state: 'PENDING' };
|
|
36
|
+
this.fibers.set(def.name, fiber);
|
|
37
|
+
this.defs.set(def.name, def);
|
|
38
|
+
if (fiber.inject.every(d => this.services.has(d)))
|
|
39
|
+
this.mount(def, fiber);
|
|
40
|
+
else
|
|
41
|
+
this.pending.push(def.name);
|
|
42
|
+
}
|
|
43
|
+
mount(def, fiber) {
|
|
44
|
+
fiber.state = 'ACTIVE';
|
|
45
|
+
const pluginCtx = {
|
|
46
|
+
effect: (body, label = 'anonymous') => {
|
|
47
|
+
const res = body();
|
|
48
|
+
const disposers = typeof res === 'function' ? [res] : [...res];
|
|
49
|
+
for (const d of disposers)
|
|
50
|
+
fiber.disposables.push({ label, disposer: d });
|
|
51
|
+
},
|
|
52
|
+
provide: (name, impl) => this.provide(name, impl, def.name),
|
|
53
|
+
get: (name) => this.get(name),
|
|
54
|
+
pluginName: def.name,
|
|
55
|
+
};
|
|
56
|
+
const ret = def.apply(pluginCtx);
|
|
57
|
+
if (ret && typeof ret.then === 'function')
|
|
58
|
+
throw new Error(`${DEP_CYCLE}: async apply 不被 S1 骨架支持(启动期必须同步完成注册,异步初始化请走 effect)`);
|
|
59
|
+
// 新提供的服务可能解锁 PENDING 插件
|
|
60
|
+
this.tryMountPending();
|
|
61
|
+
}
|
|
62
|
+
tryMountPending() {
|
|
63
|
+
let progressed = true;
|
|
64
|
+
while (progressed) {
|
|
65
|
+
progressed = false;
|
|
66
|
+
for (let i = this.pending.length - 1; i >= 0; i--) {
|
|
67
|
+
const name = this.pending[i];
|
|
68
|
+
const fiber = this.fibers.get(name);
|
|
69
|
+
const def = this.defs.get(name);
|
|
70
|
+
if (fiber.inject.every(d => this.services.has(d))) {
|
|
71
|
+
this.pending.splice(i, 1);
|
|
72
|
+
this.mount(def, fiber);
|
|
73
|
+
progressed = true;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
defs = new Map();
|
|
79
|
+
/** 卸载单个插件(回卷其全部副作用;fiber 内注册逆序) */
|
|
80
|
+
async unload(name) {
|
|
81
|
+
const fiber = this.fibers.get(name);
|
|
82
|
+
if (!fiber || fiber.state === 'DISPOSED')
|
|
83
|
+
return;
|
|
84
|
+
fiber.state = 'DISPOSING';
|
|
85
|
+
for (const { label, disposer } of [...fiber.disposables].reverse()) {
|
|
86
|
+
await disposer();
|
|
87
|
+
}
|
|
88
|
+
fiber.disposables = [];
|
|
89
|
+
fiber.state = 'DISPOSED';
|
|
90
|
+
}
|
|
91
|
+
/** 运行时整体卸载(ADR-002 双模式) */
|
|
92
|
+
async disposeRuntime(opts = {}) {
|
|
93
|
+
const order = this.topoOrder();
|
|
94
|
+
const report = { unloaded: [], order: [], errors: [] };
|
|
95
|
+
// 根作用域(宿主 SDK 注册面,§3.2.M8)先于插件回卷:创建序的逆 = 根最后建、最先撤
|
|
96
|
+
for (const { label, disposer } of [...this.rootDisposables].reverse()) {
|
|
97
|
+
try {
|
|
98
|
+
await disposer();
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
report.errors.push({ plugin: '<root>', label, error });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
this.rootDisposables = [];
|
|
105
|
+
if ((opts.order ?? 'strict-topo') === 'strict-topo') {
|
|
106
|
+
// 逐层排空:Kahn 分层为 provider→consumer 方向,卸载需反转(消费者层先于提供者层)
|
|
107
|
+
for (const layer of [...order.layers].reverse()) {
|
|
108
|
+
await Promise.all(layer.map(async (name) => {
|
|
109
|
+
const fiber = this.fibers.get(name);
|
|
110
|
+
if (fiber.state !== 'ACTIVE')
|
|
111
|
+
return;
|
|
112
|
+
fiber.state = 'DISPOSING';
|
|
113
|
+
for (const { label, disposer } of [...fiber.disposables].reverse()) {
|
|
114
|
+
try {
|
|
115
|
+
await disposer();
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
report.errors.push({ plugin: name, label, error });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
fiber.disposables = [];
|
|
122
|
+
fiber.state = 'DISPOSED';
|
|
123
|
+
report.unloaded.push(name);
|
|
124
|
+
report.order.push(name);
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
// concurrent:全部 fiber 并发(对应 dsh Fiber._unload 行为——作为降级/对照模式保留)
|
|
130
|
+
await Promise.all(order.all.map(async (name) => {
|
|
131
|
+
const fiber = this.fibers.get(name);
|
|
132
|
+
if (fiber.state !== 'ACTIVE')
|
|
133
|
+
return;
|
|
134
|
+
fiber.state = 'DISPOSING';
|
|
135
|
+
await Promise.all([...fiber.disposables].reverse().map(async ({ label, disposer }) => {
|
|
136
|
+
try {
|
|
137
|
+
await disposer();
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
report.errors.push({ plugin: name, label, error });
|
|
141
|
+
}
|
|
142
|
+
}));
|
|
143
|
+
fiber.disposables = [];
|
|
144
|
+
fiber.state = 'DISPOSED';
|
|
145
|
+
report.unloaded.push(name);
|
|
146
|
+
report.order.push(name);
|
|
147
|
+
}));
|
|
148
|
+
}
|
|
149
|
+
return report;
|
|
150
|
+
}
|
|
151
|
+
/** 治理视图(F14):装配配置树快照(插件/依赖/服务/副作用计数/状态)——只读,不改运行时 */
|
|
152
|
+
governanceSnapshot() {
|
|
153
|
+
return {
|
|
154
|
+
plugins: [...this.fibers.values()].map(f => ({
|
|
155
|
+
name: f.name,
|
|
156
|
+
inject: [...f.inject],
|
|
157
|
+
state: f.state,
|
|
158
|
+
effects: f.disposables.length,
|
|
159
|
+
effectLabels: f.disposables.map(d => d.label),
|
|
160
|
+
provides: [...this.services.entries()].filter(([, s]) => s.provider === f.name).map(([n]) => n),
|
|
161
|
+
})),
|
|
162
|
+
services: [...this.services.entries()].map(([name, s]) => ({ name, provider: s.provider })),
|
|
163
|
+
pending: [...this.pending],
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/** 依赖图拓扑分层(Kahn 分层;环 = 加载期显式报错) */
|
|
167
|
+
topoOrder() {
|
|
168
|
+
const active = [...this.fibers.values()].filter(f => f.state === 'ACTIVE');
|
|
169
|
+
// provider 关系:插件 P provide 了服务 s,Q inject s => Q 依赖 P(Q 先卸载)
|
|
170
|
+
const depsOf = new Map(); // name -> 依赖的 provider 集合
|
|
171
|
+
for (const f of active)
|
|
172
|
+
depsOf.set(f.name, new Set());
|
|
173
|
+
for (const [name, s] of this.services) {
|
|
174
|
+
if (depsOf.has(s.provider) && depsOf.has(name)) {
|
|
175
|
+
// name 是 inject s 的插件(近似:以插件名=服务名匹配 inject 声明)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
for (const f of active) {
|
|
179
|
+
for (const d of f.inject) {
|
|
180
|
+
const s = this.services.get(d);
|
|
181
|
+
if (s && s.provider !== '<root>' && depsOf.has(s.provider) && s.provider !== f.name) {
|
|
182
|
+
depsOf.get(f.name).add(s.provider);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// Kahn 分层
|
|
187
|
+
const remaining = new Map(depsOf);
|
|
188
|
+
const layers = [];
|
|
189
|
+
while (remaining.size) {
|
|
190
|
+
const layer = [...remaining.entries()].filter(([, deps]) => deps.size === 0).map(([n]) => n);
|
|
191
|
+
if (!layer.length)
|
|
192
|
+
throw new Error(`${DEP_CYCLE}: dependency cycle among [${[...remaining.keys()].join(', ')}]`);
|
|
193
|
+
layers.push(layer);
|
|
194
|
+
for (const n of layer)
|
|
195
|
+
remaining.delete(n);
|
|
196
|
+
for (const deps of remaining.values()) {
|
|
197
|
+
for (const n of layer)
|
|
198
|
+
deps.delete(n);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return { layers, all: layers.flat() };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* F7 · 事件分发契约(@mode 声明 + 启动期机器校验)
|
|
3
|
+
*
|
|
4
|
+
* 设计口径(《系统设计》F7:五种分发模式可裁剪起步):
|
|
5
|
+
* - S2 实装全部五模式:emit(广播)/ serial(有序检查点)/ bail(首断即止)/
|
|
6
|
+
* waterfall(环绕中间件 next())/ parallel(扇出)
|
|
7
|
+
* - 分发模式是事件的公开契约:未声明的事件禁止注册与分发(启动期静态报错 A010003,US-7 AC2)
|
|
8
|
+
* - 短路易错点(D3 §4.1 警示):waterfall 观察型监听器忘调 next() 会静默截断——
|
|
9
|
+
* 文档红线 + validateWaterfallUsage 辅助检测
|
|
10
|
+
*/
|
|
11
|
+
export type DispatchMode = 'emit' | 'serial' | 'bail' | 'waterfall' | 'parallel';
|
|
12
|
+
export declare function declareEvent(name: string, mode: DispatchMode): void;
|
|
13
|
+
export declare function declaredMode(name: string): DispatchMode | undefined;
|
|
14
|
+
/** 启动期机器校验:目录自洽性(声明与分发点一致性由 EventBus.dispatch 强制) */
|
|
15
|
+
export declare function verifyContracts(): {
|
|
16
|
+
ok: true;
|
|
17
|
+
declared: number;
|
|
18
|
+
};
|
|
19
|
+
type Handler<P, R> = (payload: P, next: () => R) => R;
|
|
20
|
+
export interface HandlerOpts {
|
|
21
|
+
label?: string; /** 优先级:升序小先(内核约定负值);同优先级保持注册序(稳定排序,ES2019) */
|
|
22
|
+
priority?: number;
|
|
23
|
+
}
|
|
24
|
+
export declare class EventBus {
|
|
25
|
+
#private;
|
|
26
|
+
/** 注册处理器:事件未声明 = 启动期静态报错;label 与 HandlerOpts 双形态向后兼容(M1 兼容) */
|
|
27
|
+
on<P = unknown, R = unknown>(event: string, fn: Handler<P, R>, labelOrOpts?: string | HandlerOpts): void;
|
|
28
|
+
/** 治理视图(M2 F14):返回某事件的处理器链(label+priority,按实际执行序) */
|
|
29
|
+
handlerChain(event: string): Array<{
|
|
30
|
+
label: string;
|
|
31
|
+
priority: number;
|
|
32
|
+
}>;
|
|
33
|
+
handlerCount(event: string): number;
|
|
34
|
+
/** 分发:严格按声明模式执行(声明与分发点不一致 = 契约违例) */
|
|
35
|
+
dispatch<P, R = unknown>(event: string, payload: P, init?: () => R): Promise<R | undefined>;
|
|
36
|
+
}
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/** 事件契约目录(对应 dsh typert 生成目录的 CAR 自研等价物) */
|
|
2
|
+
const catalog = new Map();
|
|
3
|
+
export function declareEvent(name, mode) {
|
|
4
|
+
if (catalog.has(name) && catalog.get(name) !== mode) {
|
|
5
|
+
throw new Error(`CAR-E-CONTRACT: event "${name}" re-declared with mode "${mode}" (was "${catalog.get(name)}")`);
|
|
6
|
+
}
|
|
7
|
+
catalog.set(name, mode);
|
|
8
|
+
}
|
|
9
|
+
export function declaredMode(name) {
|
|
10
|
+
return catalog.get(name);
|
|
11
|
+
}
|
|
12
|
+
/** 启动期机器校验:目录自洽性(声明与分发点一致性由 EventBus.dispatch 强制) */
|
|
13
|
+
export function verifyContracts() {
|
|
14
|
+
return { ok: true, declared: catalog.size };
|
|
15
|
+
}
|
|
16
|
+
export class EventBus {
|
|
17
|
+
#handlers = new Map();
|
|
18
|
+
#seq = 0;
|
|
19
|
+
/** 注册处理器:事件未声明 = 启动期静态报错;label 与 HandlerOpts 双形态向后兼容(M1 兼容) */
|
|
20
|
+
on(event, fn, labelOrOpts = 'anonymous') {
|
|
21
|
+
if (!catalog.has(event)) {
|
|
22
|
+
throw new Error(`CAR-E-CONTRACT: cannot subscribe to undeclared event "${event}"(@mode 契约缺失,先 declareEvent)`);
|
|
23
|
+
}
|
|
24
|
+
const opts = typeof labelOrOpts === 'string' ? { label: labelOrOpts } : labelOrOpts;
|
|
25
|
+
const list = this.#handlers.get(event) ?? [];
|
|
26
|
+
list.push({ fn: fn, label: opts.label ?? 'anonymous', priority: opts.priority ?? 0, seq: this.#seq++ });
|
|
27
|
+
// F11:优先级升序稳定排序(同 priority 保持注册序)
|
|
28
|
+
list.sort((a, b) => a.priority - b.priority || a.seq - b.seq);
|
|
29
|
+
this.#handlers.set(event, list);
|
|
30
|
+
}
|
|
31
|
+
/** 治理视图(M2 F14):返回某事件的处理器链(label+priority,按实际执行序) */
|
|
32
|
+
handlerChain(event) {
|
|
33
|
+
return (this.#handlers.get(event) ?? []).map(h => ({ label: h.label, priority: h.priority }));
|
|
34
|
+
}
|
|
35
|
+
handlerCount(event) {
|
|
36
|
+
return this.#handlers.get(event)?.length ?? 0;
|
|
37
|
+
}
|
|
38
|
+
/** 分发:严格按声明模式执行(声明与分发点不一致 = 契约违例) */
|
|
39
|
+
async dispatch(event, payload, init) {
|
|
40
|
+
const mode = catalog.get(event);
|
|
41
|
+
if (!mode)
|
|
42
|
+
throw new Error(`CAR-E-CONTRACT: dispatch of undeclared event "${event}"`);
|
|
43
|
+
const handlers = [...(this.#handlers.get(event) ?? [])]; // 已按 priority 稳定排序
|
|
44
|
+
const fallback = init ?? (() => undefined);
|
|
45
|
+
switch (mode) {
|
|
46
|
+
case 'emit': // 广播:不等待、无返回值(同步通知语义)
|
|
47
|
+
for (const h of handlers) {
|
|
48
|
+
const r = h.fn(payload, () => undefined);
|
|
49
|
+
void r;
|
|
50
|
+
}
|
|
51
|
+
return undefined;
|
|
52
|
+
case 'serial': { // 有序检查点:await 串行,链式传值
|
|
53
|
+
let acc = fallback();
|
|
54
|
+
for (const h of handlers)
|
|
55
|
+
acc = await h.fn(payload, () => acc);
|
|
56
|
+
return acc;
|
|
57
|
+
}
|
|
58
|
+
case 'bail': { // 首断即止:首个非 undefined 返回即终止(短路是设计意图)
|
|
59
|
+
for (const h of handlers) {
|
|
60
|
+
const r = await h.fn(payload, () => undefined);
|
|
61
|
+
if (r !== undefined)
|
|
62
|
+
return r;
|
|
63
|
+
}
|
|
64
|
+
return fallback();
|
|
65
|
+
}
|
|
66
|
+
case 'waterfall': { // 环绕中间件:next() 前后均可介入,不调 next() = 短路整条链
|
|
67
|
+
let index = -1;
|
|
68
|
+
const run = async (i) => {
|
|
69
|
+
if (i === handlers.length)
|
|
70
|
+
return fallback();
|
|
71
|
+
index = i;
|
|
72
|
+
return handlers[i].fn(payload, () => run(i + 1));
|
|
73
|
+
};
|
|
74
|
+
return await run(0);
|
|
75
|
+
}
|
|
76
|
+
case 'parallel': { // 扇出:全部并发,聚合数组
|
|
77
|
+
const results = await Promise.all(handlers.map(h => h.fn(payload, () => undefined)));
|
|
78
|
+
return results;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export declare const CONFIG_FILENAME = "car.config.json";
|
|
2
|
+
export declare const CONFIG_TS_FILENAME = "car.config.ts";
|
|
3
|
+
export interface SandboxSigConfig {
|
|
4
|
+
unsignedAllow?: boolean;
|
|
5
|
+
sigEnforce?: boolean;
|
|
6
|
+
sigTrustRoot?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface LlmConfig {
|
|
9
|
+
baseUrl?: string;
|
|
10
|
+
model?: string;
|
|
11
|
+
adapterId?: string;
|
|
12
|
+
maxTokens?: number;
|
|
13
|
+
allowEnvFallback?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface McpServerSpec {
|
|
16
|
+
/** stdio 通道启动命令(与 url 互斥——1.5-S2 起二选一显式登记) */
|
|
17
|
+
command?: string;
|
|
18
|
+
args?: string[];
|
|
19
|
+
/** 启动环境变量(禁明文凭据——gateway.containsPlaintextCredential 注册时强制检查;仅 command 通道适用) */
|
|
20
|
+
env?: Record<string, string>;
|
|
21
|
+
/** 1.5-S2(D-21):远程 Streamable HTTP 通道(与 command 互斥) */
|
|
22
|
+
url?: string;
|
|
23
|
+
/** 1.5-S2(D-21):远程请求头;值支持 ${ENV_VAR} 引用(凭据禁令 CR-05;仅 url 通道适用) */
|
|
24
|
+
headers?: Record<string, string>;
|
|
25
|
+
}
|
|
26
|
+
export interface CarConfig {
|
|
27
|
+
sandbox?: SandboxSigConfig;
|
|
28
|
+
/** 1.4-S3(D-16):模型接入配置键位空间;凭据禁入(CR-05)——只放端点/模型名/开关 */
|
|
29
|
+
llm?: LlmConfig;
|
|
30
|
+
/** 1.4-S7(D-18):MCP server 登记面(--mcp <id> 显式启用) */
|
|
31
|
+
mcp?: {
|
|
32
|
+
servers: Record<string, McpServerSpec>;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export interface ConfigLoadResult {
|
|
36
|
+
/** 实际读取的配置文件路径(cwd 未发现时缺省——未发现是正常态非错误) */
|
|
37
|
+
path?: string;
|
|
38
|
+
config: CarConfig;
|
|
39
|
+
/** fail-visible:显式路径不存在 / 坏 JSON / 坏 TS / 未登记键 / 类型错的原因(非空 = 调用方必须中止) */
|
|
40
|
+
error?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 1.5-S3(D-22)→ 1.6-S3(D-24):car.config.ts 载体预热。
|
|
44
|
+
* jiti 自包含转译(每 epoch 新实例 + moduleCache 关 = 击穿语义与 1.5 等价)——
|
|
45
|
+
* Node 22.19+ 无需 --experimental-transform-types;直接调用 loadCarConfig 的库方
|
|
46
|
+
* 在存在 car.config.ts 时须先 await 本函数(CLI 入口统一预热,协议不变)。
|
|
47
|
+
*/
|
|
48
|
+
export declare function warmCarConfig(opts?: {
|
|
49
|
+
explicitPath?: string;
|
|
50
|
+
cwd?: string;
|
|
51
|
+
}): Promise<void>;
|
|
52
|
+
/** 加载配置:显式路径(必须存在,否则报错)或 cwd 发现(未发现 = 正常态返回空配置) */
|
|
53
|
+
export declare function loadCarConfig(opts?: {
|
|
54
|
+
explicitPath?: string;
|
|
55
|
+
cwd?: string;
|
|
56
|
+
}): ConfigLoadResult;
|
|
57
|
+
/** 合并产出(mode/unsignedAllow 恒有值——比 SignatureGateOptions 的可选字段更窄,doctor 等消费方免缺省分支) */
|
|
58
|
+
export interface ResolvedSignatureGate {
|
|
59
|
+
mode: 'warn' | 'enforce';
|
|
60
|
+
trustRootPublicKey?: string;
|
|
61
|
+
unsignedAllow: boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 1.4-S3(D-16):模型接入配置合并(flag > env > 配置 > 缺省)。
|
|
65
|
+
* env 已定义为显式意见(压过配置),未定义落配置层——与 mergeSignatureGate 同纪律。
|
|
66
|
+
* 凭据不在此层(CR-05):走 CredentialService(keychain → env OPENAI_API_KEY/ANTHROPIC_API_KEY/
|
|
67
|
+
* CAR_LLM_API_KEY,env fallback 需显式开启 = CAR_ALLOW_ENV_CREDENTIALS=1 或 llm.allowEnvFallback)。
|
|
68
|
+
*/
|
|
69
|
+
export declare function mergeLlmConfig(env: NodeJS.ProcessEnv, cfg: CarConfig, flags?: {
|
|
70
|
+
baseUrl?: string;
|
|
71
|
+
model?: string;
|
|
72
|
+
adapterId?: string;
|
|
73
|
+
maxTokens?: number;
|
|
74
|
+
}): LlmConfig;
|
|
75
|
+
/**
|
|
76
|
+
* 优先级合并(flag > env > 配置 > 缺省):产出装载签名门选项。
|
|
77
|
+
* flagEnforce = CLI --sig-enforce;env 已定义即为显式意见(压过配置),未定义落配置层。
|
|
78
|
+
*/
|
|
79
|
+
export declare function mergeSignatureGate(env: NodeJS.ProcessEnv, cfg: CarConfig, flagEnforce?: boolean): ResolvedSignatureGate;
|