dsh-expert-group 0.1.1
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/LICENSE +38 -0
- package/README.md +355 -0
- package/THIRD_PARTY_NOTICES.md +35 -0
- package/assets/notification-whoosh.wav +0 -0
- package/bin/prompt-inspector.mjs +63 -0
- package/client-bundle.js +14891 -0
- package/client-expert-group-model.js +426 -0
- package/client-expert-group-sidebar.js +5401 -0
- package/client-freshness.js +364 -0
- package/client-model-config.js +2155 -0
- package/client-notifications.js +38 -0
- package/client.js +6487 -0
- package/cordis.patch.yml +10 -0
- package/fanout-host.js +1507 -0
- package/fanout.js +2981 -0
- package/host/composition.js +155 -0
- package/host/expert-group-runtime.js +822 -0
- package/host/expert-group-snapshot.js +450 -0
- package/host/fanout-runtime.js +822 -0
- package/host/handoff.js +80 -0
- package/host/lineage.js +274 -0
- package/host/notifications.js +189 -0
- package/host/persistence-health.js +389 -0
- package/host/recovery.js +242 -0
- package/host/rpc-transport.js +573 -0
- package/host/settings-runtime.js +168 -0
- package/host/settings-source.js +265 -0
- package/host/storage-binding.js +94 -0
- package/host/system-toast.js +916 -0
- package/image-routing.js +263 -0
- package/index.js +9327 -0
- package/install.ps1 +219 -0
- package/orchestrator.js +239 -0
- package/package.json +142 -0
- package/prompt-contracts.js +148 -0
- package/prompt-inspector.js +584 -0
- package/prompt-sidecar.js +610 -0
- package/resource-budget.js +540 -0
- package/role-registry.js +286 -0
- package/rpc-host.js +310 -0
- package/scripts/assemble-client.mjs +605 -0
- package/scripts/build.mjs +124 -0
- package/shared.js +4003 -0
- package/storage.js +190 -0
- package/uninstall.ps1 +47 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host 模块装配层(expand 阶段)。
|
|
3
|
+
*
|
|
4
|
+
* 只做依赖注入与生命周期注册:每个关注点模块由自己的工厂创建,装配层把它们
|
|
5
|
+
* 冻结成一份可观察的装配记录,并按 Cordis 逆序语义提供幂等的 dispose。
|
|
6
|
+
* 旧组合根(index.js apply)继续作为唯一外部 seam;未迁移逻辑留在 index.js,
|
|
7
|
+
* 由任务 06 收缩。
|
|
8
|
+
*
|
|
9
|
+
* 生命周期契约(任务 06):
|
|
10
|
+
* - 已构造的模块实例可以直接注入同名槽位(判定:同时提供 describe 与 dispose),
|
|
11
|
+
* 宿主自持的生命周期参与者(例如 recovery)因此不必被装配层二次构造。
|
|
12
|
+
* - 槽位按依赖顺序构造;任何一步失败都按**逆序回滚已创建模块**,并保留首个构造错误
|
|
13
|
+
* (释放异常只记录在 error.rollback 里,绝不覆盖首错)。
|
|
14
|
+
* - dispose 逆序串行释放:同步 dispose 在同一轮同步完成,异步 dispose 会被等待,
|
|
15
|
+
* 且下一个模块必须等它 settle 之后才开始释放;单个模块的释放异常按模块名记录,
|
|
16
|
+
* 不中断其它模块。重复调用返回同一个 promise,每个模块只释放一次。
|
|
17
|
+
*
|
|
18
|
+
* @module dsh-expert-group/host/composition
|
|
19
|
+
*/
|
|
20
|
+
import { createHandoffRegistryModule } from "./handoff.js";
|
|
21
|
+
import { createLineageModule } from "./lineage.js";
|
|
22
|
+
import { createNotificationModule } from "./notifications.js";
|
|
23
|
+
import { createRpcTransportModule } from "./rpc-transport.js";
|
|
24
|
+
import { createSettingsSourceModule } from "./settings-source.js";
|
|
25
|
+
import { createStorageBindingModule } from "./storage-binding.js";
|
|
26
|
+
|
|
27
|
+
/** 模块实例判定:描述面 + 释放面缺一不可,否则仍按配置对象交给对应工厂。 */
|
|
28
|
+
function isModule(value) {
|
|
29
|
+
return value !== null && typeof value === "object"
|
|
30
|
+
&& typeof value.describe === "function"
|
|
31
|
+
&& typeof value.dispose === "function";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isThenable(value) {
|
|
35
|
+
return value !== null && typeof value === "object" && typeof value.then === "function";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 构造中途失败时的逆序回滚。
|
|
40
|
+
*
|
|
41
|
+
* 只释放**已经创建成功**的模块;返回首个构造错误本身,调用方原样抛出。释放过程中
|
|
42
|
+
* 的异常只记录在 error.rollback.errors 里,绝不覆盖构造首错。
|
|
43
|
+
*
|
|
44
|
+
* 构造过程是同步的,因此异步 dispose 无法在这里 await:它的结果登记在
|
|
45
|
+
* error.rollback.settled 上,调用方可以等待它收敛而不产生未处理的 rejection。
|
|
46
|
+
*/
|
|
47
|
+
function rollbackCreatedModules(modules, error) {
|
|
48
|
+
const released = [];
|
|
49
|
+
const errors = [];
|
|
50
|
+
const pending = [];
|
|
51
|
+
for (let index = modules.length - 1; index >= 0; index -= 1) {
|
|
52
|
+
const [name, module] = modules[index];
|
|
53
|
+
released.push(name);
|
|
54
|
+
try {
|
|
55
|
+
const result = module.dispose();
|
|
56
|
+
if (isThenable(result)) {
|
|
57
|
+
pending.push(Promise.resolve(result).catch((disposeError) => { errors.push({ name, error: disposeError }); }));
|
|
58
|
+
}
|
|
59
|
+
} catch (disposeError) {
|
|
60
|
+
errors.push({ name, error: disposeError });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const rollback = {
|
|
64
|
+
released: Object.freeze(released),
|
|
65
|
+
// 迟到(异步)释放异常也写进同一个数组:记录对象被冻结,但数组内容仍可追加。
|
|
66
|
+
errors,
|
|
67
|
+
settled: Promise.all(pending).then(() => Object.freeze({ errors: Object.freeze([...errors]) })),
|
|
68
|
+
};
|
|
69
|
+
try {
|
|
70
|
+
Object.defineProperty(error, "rollback", { value: Object.freeze(rollback), enumerable: false, configurable: true });
|
|
71
|
+
} catch {
|
|
72
|
+
// 冻结或不可扩展的构造错误:回滚已经完成,只是没有可观察的附加记录。
|
|
73
|
+
}
|
|
74
|
+
return error;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @param {object} options - 每个关注点的工厂参数(见各模块)。
|
|
79
|
+
* 已构造模块实例可直接注入同名槽位(rpc/settings/storage/lineage/notifications/handoff);
|
|
80
|
+
* recovery 是可选槽位,注入时必须已经是模块实例(入口持有它的 limit/taskBindingLimit)。
|
|
81
|
+
* @returns {object} 冻结的装配记录:模块 + describe()/dispose()。
|
|
82
|
+
*/
|
|
83
|
+
export function createHostComposition(options = {}) {
|
|
84
|
+
if (options === null || typeof options !== "object") throw new TypeError("createHostComposition 需要配置对象");
|
|
85
|
+
// 注入校验在任何模块创建之前完成:配置错误不得留下半初始化的模块。
|
|
86
|
+
if (options.recovery !== undefined && !isModule(options.recovery)) {
|
|
87
|
+
throw new TypeError("createHostComposition 的 recovery 必须是模块实例(带 describe/dispose)");
|
|
88
|
+
}
|
|
89
|
+
const slots = [
|
|
90
|
+
["rpc", () => createRpcTransportModule(options.rpc)],
|
|
91
|
+
["settings", () => createSettingsSourceModule(options.settings)],
|
|
92
|
+
["storage", () => createStorageBindingModule(options.storage)],
|
|
93
|
+
["lineage", () => createLineageModule(options.lineage)],
|
|
94
|
+
// recovery 依赖 lineage 的 root 查询:晚于 lineage 构造,因此早于 lineage 释放;
|
|
95
|
+
// 同时早于 notifications/handoff 释放,保证关闭之后不再有重建意图被消费。
|
|
96
|
+
["recovery", () => options.recovery],
|
|
97
|
+
["notifications", () => createNotificationModule(options.notifications)],
|
|
98
|
+
["handoff", () => createHandoffRegistryModule(options.handoff)],
|
|
99
|
+
];
|
|
100
|
+
const modules = [];
|
|
101
|
+
for (const [name, create] of slots) {
|
|
102
|
+
// 未注入的 recovery 槽位整块跳过;其余槽位始终存在。
|
|
103
|
+
if (name === "recovery" && options.recovery === undefined) continue;
|
|
104
|
+
try {
|
|
105
|
+
const provided = options[name];
|
|
106
|
+
const module = isModule(provided) ? provided : create();
|
|
107
|
+
if (!isModule(module)) throw new TypeError(name + " 模块必须提供 describe/dispose");
|
|
108
|
+
modules.push([name, module]);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
throw rollbackCreatedModules(modules, error);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const byName = new Map(modules);
|
|
114
|
+
const recovery = byName.get("recovery");
|
|
115
|
+
let disposePromise;
|
|
116
|
+
|
|
117
|
+
const composition = {
|
|
118
|
+
rpc: byName.get("rpc"),
|
|
119
|
+
settings: byName.get("settings"),
|
|
120
|
+
storage: byName.get("storage"),
|
|
121
|
+
lineage: byName.get("lineage"),
|
|
122
|
+
notifications: byName.get("notifications"),
|
|
123
|
+
handoff: byName.get("handoff"),
|
|
124
|
+
// 未注入 recovery 时保持既有六模块键集:装配记录与冻结键都不得凭空多出槽位。
|
|
125
|
+
...(recovery === undefined ? {} : { recovery }),
|
|
126
|
+
/** 装配记录:模块名 + 各自 describe() 快照。 */
|
|
127
|
+
describe() {
|
|
128
|
+
return Object.freeze({
|
|
129
|
+
modules: Object.freeze(modules.map(([name]) => name)),
|
|
130
|
+
records: Object.freeze(Object.fromEntries(modules.map(([name, module]) => [name, module.describe?.() ?? {}]))),
|
|
131
|
+
});
|
|
132
|
+
},
|
|
133
|
+
module: (name) => byName.get(name),
|
|
134
|
+
/** 逆序 dispose;重复调用返回同一个 promise,且只执行一次。 */
|
|
135
|
+
dispose() {
|
|
136
|
+
if (disposePromise !== undefined) return disposePromise;
|
|
137
|
+
disposePromise = (async () => {
|
|
138
|
+
const errors = [];
|
|
139
|
+
for (let index = modules.length - 1; index >= 0; index -= 1) {
|
|
140
|
+
const [name, module] = modules[index];
|
|
141
|
+
try {
|
|
142
|
+
const result = module.dispose();
|
|
143
|
+
// 同步 dispose 不让出微任务:默认六个模块的卸载仍在同一轮同步完成。
|
|
144
|
+
if (isThenable(result)) await result;
|
|
145
|
+
} catch (error) {
|
|
146
|
+
errors.push({ name, error });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return Object.freeze({ errors: Object.freeze(errors) });
|
|
150
|
+
})();
|
|
151
|
+
return disposePromise;
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
return Object.freeze(composition);
|
|
155
|
+
}
|