dsh-caduceus 0.7.1 → 0.12.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/README.md +8 -2
- package/README.zh.md +8 -2
- package/cordis.patch.yml +2 -0
- package/dist/bridge/bridge.d.ts +7 -2
- package/dist/bridge/bridge.js +425 -17
- package/dist/bridge/types.d.ts +21 -1
- package/dist/commands/dispatch.d.ts +2 -2
- package/dist/commands/dispatch.js +3 -3
- package/dist/commands/parse.d.ts +15 -2
- package/dist/commands/parse.js +34 -3
- package/dist/config.d.ts +18 -4
- package/dist/config.js +9 -3
- package/dist/export.d.ts +44 -0
- package/dist/export.js +222 -0
- package/dist/home.d.ts +2 -0
- package/dist/home.js +4 -0
- package/dist/i18n.d.ts +42 -0
- package/dist/i18n.js +94 -0
- package/dist/index.js +64 -1
- package/dist/interactive/approval.js +1 -1
- package/dist/interactive/cards.d.ts +2 -1
- package/dist/interactive/cards.js +3 -1
- package/dist/interactive/runtime.d.ts +4 -0
- package/dist/interactive/runtime.js +15 -0
- package/dist/interactive/types.d.ts +2 -0
- package/dist/metrics.d.ts +28 -0
- package/dist/metrics.js +135 -0
- package/dist/render/cards.d.ts +4 -0
- package/dist/render/cards.js +17 -0
- package/dist/render/permission-card.d.ts +23 -0
- package/dist/render/permission-card.js +22 -0
- package/dist/status.d.ts +22 -0
- package/dist/status.js +95 -0
- package/dist/transport/feishu-response.d.ts +1 -0
- package/dist/transport/feishu-response.js +25 -0
- package/dist/transport/lark.js +13 -4
- package/dist/usage.d.ts +23 -0
- package/dist/usage.js +77 -0
- package/lib/client.js +175 -0
- package/lib/client.js.map +1 -0
- package/package.json +12 -4
package/dist/transport/lark.js
CHANGED
|
@@ -13,6 +13,7 @@ import { silentLogger } from '../logger.js';
|
|
|
13
13
|
import { normalizeCardAction, normalizeMessageEvent, } from './normalize.js';
|
|
14
14
|
import { formatGateLine } from '../gate.js';
|
|
15
15
|
import { FeishuApiError } from './types.js';
|
|
16
|
+
import { unwrapFeishuData } from './feishu-response.js';
|
|
16
17
|
/** Long-connection Feishu implementation of {@link FeishuApi}. */
|
|
17
18
|
export class LarkFeishuApi {
|
|
18
19
|
client;
|
|
@@ -60,6 +61,14 @@ export class LarkFeishuApi {
|
|
|
60
61
|
}
|
|
61
62
|
this.handler?.(message);
|
|
62
63
|
},
|
|
64
|
+
// Feishu pushes the app's own reaction writes back as events, and this
|
|
65
|
+
// plugin has no reaction-based interaction. Without a registered handle
|
|
66
|
+
// the SDK warns "no <key> handle" on every OnIt/DONE we add ourselves,
|
|
67
|
+
// which drowned the log (59.9% of dsh journal lines on 2026-11-07).
|
|
68
|
+
// An explicit no-op keeps the drop intentional and leaves the keys in
|
|
69
|
+
// place for a future reaction-driven interaction.
|
|
70
|
+
'im.message.reaction.created_v1': () => undefined,
|
|
71
|
+
'im.message.reaction.deleted_v1': () => undefined,
|
|
63
72
|
});
|
|
64
73
|
this.ws = new WSClient({ appId: options.appId, appSecret: options.appSecret });
|
|
65
74
|
}
|
|
@@ -174,8 +183,8 @@ export class LarkFeishuApi {
|
|
|
174
183
|
file: Buffer.from(bytes),
|
|
175
184
|
},
|
|
176
185
|
}));
|
|
177
|
-
|
|
178
|
-
const fileKey =
|
|
186
|
+
const data = unwrapFeishuData(response, 'im.v1.file.create');
|
|
187
|
+
const fileKey = data.file_key;
|
|
179
188
|
if (fileKey === undefined || fileKey.length === 0) {
|
|
180
189
|
throw new FeishuApiError('feishu im.v1.file.create returned no file_key', -1);
|
|
181
190
|
}
|
|
@@ -188,8 +197,8 @@ export class LarkFeishuApi {
|
|
|
188
197
|
image: Buffer.from(bytes),
|
|
189
198
|
},
|
|
190
199
|
}));
|
|
191
|
-
|
|
192
|
-
const imageKey =
|
|
200
|
+
const data = unwrapFeishuData(response, 'im.v1.image.create');
|
|
201
|
+
const imageKey = data.image_key;
|
|
193
202
|
if (imageKey === undefined || imageKey.length === 0) {
|
|
194
203
|
throw new FeishuApiError('feishu im.v1.image.create returned no image_key', -1);
|
|
195
204
|
}
|
package/dist/usage.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token-usage aggregation from a session log (`sessionQuery.readSession`).
|
|
3
|
+
*
|
|
4
|
+
* The in-memory StatusTracker still feeds the process card, but `/usage` must
|
|
5
|
+
* work on a freshly restarted gateway, so it aggregates the durable event log
|
|
6
|
+
* instead.
|
|
7
|
+
*
|
|
8
|
+
* @module dsh-caduceus/usage
|
|
9
|
+
*/
|
|
10
|
+
import type { UsageTotals } from './status.js';
|
|
11
|
+
export interface UsageTurn {
|
|
12
|
+
readonly turn: number;
|
|
13
|
+
readonly usage: UsageTotals;
|
|
14
|
+
}
|
|
15
|
+
export interface UsageReport {
|
|
16
|
+
/** Per-turn totals, ordered by turn descending (newest first). */
|
|
17
|
+
readonly turns: readonly UsageTurn[];
|
|
18
|
+
readonly total: UsageTotals;
|
|
19
|
+
}
|
|
20
|
+
/** Aggregate every `assistant/message` usage event, grouped by `data.turn`. */
|
|
21
|
+
export declare function aggregateUsage(events: readonly unknown[]): UsageReport;
|
|
22
|
+
/** Render the three-line usage report; empty log is a distinct state. */
|
|
23
|
+
export declare function renderUsageReport(report: UsageReport): string;
|
package/dist/usage.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
function asRecord(value) {
|
|
2
|
+
return typeof value === 'object' && value !== null
|
|
3
|
+
? value
|
|
4
|
+
: undefined;
|
|
5
|
+
}
|
|
6
|
+
function usageOf(value) {
|
|
7
|
+
const record = asRecord(value);
|
|
8
|
+
if (record === undefined)
|
|
9
|
+
return undefined;
|
|
10
|
+
const num = (key) => typeof record[key] === 'number' ? record[key] : 0;
|
|
11
|
+
const input = num('inputTokens');
|
|
12
|
+
const output = num('outputTokens');
|
|
13
|
+
const cacheRead = num('cacheReadTokens');
|
|
14
|
+
const cacheWrite = num('cacheWriteTokens');
|
|
15
|
+
const providerTotal = typeof record['totalTokens'] === 'number' ? record['totalTokens'] : 0;
|
|
16
|
+
if (input === 0 && output === 0 && cacheRead === 0 && cacheWrite === 0 && providerTotal === 0) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
const total = providerTotal > 0 ? providerTotal : input + output + cacheRead + cacheWrite;
|
|
20
|
+
return { input, output, cacheRead, cacheWrite, total };
|
|
21
|
+
}
|
|
22
|
+
function add(total, delta) {
|
|
23
|
+
return {
|
|
24
|
+
input: total.input + delta.input,
|
|
25
|
+
output: total.output + delta.output,
|
|
26
|
+
cacheRead: total.cacheRead + delta.cacheRead,
|
|
27
|
+
cacheWrite: total.cacheWrite + delta.cacheWrite,
|
|
28
|
+
total: total.total + delta.total,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const ZERO = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };
|
|
32
|
+
/** Aggregate every `assistant/message` usage event, grouped by `data.turn`. */
|
|
33
|
+
export function aggregateUsage(events) {
|
|
34
|
+
const byTurn = new Map();
|
|
35
|
+
let total = ZERO;
|
|
36
|
+
for (const event of events) {
|
|
37
|
+
const row = asRecord(event);
|
|
38
|
+
if (row?.type !== 'assistant/message')
|
|
39
|
+
continue;
|
|
40
|
+
const data = asRecord(row.data);
|
|
41
|
+
if (data === undefined)
|
|
42
|
+
continue;
|
|
43
|
+
const usage = usageOf(data.usage);
|
|
44
|
+
if (usage === undefined)
|
|
45
|
+
continue;
|
|
46
|
+
const turn = typeof data.turn === 'number' ? data.turn : 0;
|
|
47
|
+
byTurn.set(turn, add(byTurn.get(turn) ?? ZERO, usage));
|
|
48
|
+
total = add(total, usage);
|
|
49
|
+
}
|
|
50
|
+
const turns = [...byTurn.entries()]
|
|
51
|
+
.sort((left, right) => right[0] - left[0])
|
|
52
|
+
.map(([turn, usage]) => ({ turn, usage }));
|
|
53
|
+
return { turns, total };
|
|
54
|
+
}
|
|
55
|
+
function line(label, usage) {
|
|
56
|
+
return (label +
|
|
57
|
+
':输入 ' +
|
|
58
|
+
usage.input +
|
|
59
|
+
' · 输出 ' +
|
|
60
|
+
usage.output +
|
|
61
|
+
' · 缓存命中 ' +
|
|
62
|
+
usage.cacheRead +
|
|
63
|
+
' · 合计 ' +
|
|
64
|
+
usage.total);
|
|
65
|
+
}
|
|
66
|
+
/** Render the three-line usage report; empty log is a distinct state. */
|
|
67
|
+
export function renderUsageReport(report) {
|
|
68
|
+
if (report.turns.length === 0)
|
|
69
|
+
return '会话里还没有用量记录';
|
|
70
|
+
const current = report.turns[0];
|
|
71
|
+
const previous = report.turns[1];
|
|
72
|
+
return [
|
|
73
|
+
line('本回合', current.usage),
|
|
74
|
+
previous === undefined ? '上回合:暂无' : line('上回合', previous.usage),
|
|
75
|
+
line('会话合计', report.total),
|
|
76
|
+
].join('\n');
|
|
77
|
+
}
|
package/lib/client.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "dsh-caduceus",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let react = require("react");
|
|
8
|
+
//#region client/labels.ts
|
|
9
|
+
/** Client-side copy for the header button, drawer, and settings row. */
|
|
10
|
+
const NS = "caduceus";
|
|
11
|
+
const ZH = {
|
|
12
|
+
"switch.panel": "双蛇杖",
|
|
13
|
+
"panel.title": "双蛇杖工作台",
|
|
14
|
+
"panel.loaded": "面板已加载",
|
|
15
|
+
"panel.version": "版本",
|
|
16
|
+
"drawer.session": "会话",
|
|
17
|
+
"drawer.none": "无会话",
|
|
18
|
+
"drawer.close": "关闭",
|
|
19
|
+
"settings.title": "双蛇杖",
|
|
20
|
+
"settings.lang": "语言",
|
|
21
|
+
"settings.metrics": "指标",
|
|
22
|
+
"settings.busyMode": "忙碌策略",
|
|
23
|
+
"settings.unavailable": "客户端配置尚未注入"
|
|
24
|
+
};
|
|
25
|
+
const EN = {
|
|
26
|
+
"switch.panel": "Caduceus",
|
|
27
|
+
"panel.title": "Caduceus Workbench",
|
|
28
|
+
"panel.loaded": "Panel loaded",
|
|
29
|
+
"panel.version": "Version",
|
|
30
|
+
"drawer.session": "Session",
|
|
31
|
+
"drawer.none": "no session",
|
|
32
|
+
"drawer.close": "Close",
|
|
33
|
+
"settings.title": "Caduceus",
|
|
34
|
+
"settings.lang": "Language",
|
|
35
|
+
"settings.metrics": "Metrics",
|
|
36
|
+
"settings.busyMode": "Busy mode",
|
|
37
|
+
"settings.unavailable": "Client config not injected yet"
|
|
38
|
+
};
|
|
39
|
+
const FALLBACK = ZH;
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region client/index.ts
|
|
42
|
+
/**
|
|
43
|
+
* Browser half of dsh-caduceus. It contributes only to additive list slots:
|
|
44
|
+
* a Session-header action button and a General-settings row. The drawer it
|
|
45
|
+
* opens is rendered by this module (fixed-position DOM), not by a host panel.
|
|
46
|
+
*/
|
|
47
|
+
const inject = ["slots", "locale"];
|
|
48
|
+
function tr(props) {
|
|
49
|
+
return props.t ?? ((key) => FALLBACK[key]);
|
|
50
|
+
}
|
|
51
|
+
function globalConfig() {
|
|
52
|
+
return globalThis.__CADUCEUS_CLIENT_CONFIG__;
|
|
53
|
+
}
|
|
54
|
+
/** Self-rendered drawer: no main panel, no shell.overlay, no host-owned slots. */
|
|
55
|
+
function openDrawer(props) {
|
|
56
|
+
if (typeof document === "undefined") return;
|
|
57
|
+
document.getElementById("caduceus-drawer")?.remove();
|
|
58
|
+
const t = tr(props);
|
|
59
|
+
const root = document.createElement("div");
|
|
60
|
+
root.id = "caduceus-drawer";
|
|
61
|
+
Object.assign(root.style, {
|
|
62
|
+
position: "fixed",
|
|
63
|
+
inset: "0",
|
|
64
|
+
zIndex: "10000",
|
|
65
|
+
background: "rgba(0,0,0,0.45)",
|
|
66
|
+
display: "flex",
|
|
67
|
+
alignItems: "flex-end",
|
|
68
|
+
justifyContent: "center"
|
|
69
|
+
});
|
|
70
|
+
const panel = document.createElement("div");
|
|
71
|
+
Object.assign(panel.style, {
|
|
72
|
+
width: "min(560px, calc(100vw - 32px))",
|
|
73
|
+
margin: "16px",
|
|
74
|
+
padding: "20px",
|
|
75
|
+
borderRadius: "12px",
|
|
76
|
+
background: "#17171d",
|
|
77
|
+
color: "#fff",
|
|
78
|
+
boxShadow: "0 12px 40px rgba(0,0,0,0.45)",
|
|
79
|
+
font: "14px/1.6 system-ui, sans-serif"
|
|
80
|
+
});
|
|
81
|
+
const title = document.createElement("h2");
|
|
82
|
+
title.textContent = t("panel.title");
|
|
83
|
+
Object.assign(title.style, {
|
|
84
|
+
margin: "0 0 12px",
|
|
85
|
+
fontSize: "18px"
|
|
86
|
+
});
|
|
87
|
+
const version = document.createElement("p");
|
|
88
|
+
version.textContent = `${t("panel.version")} 0.12.1`;
|
|
89
|
+
const session = document.createElement("p");
|
|
90
|
+
session.textContent = `${t("drawer.session")}: ${props.sessionId ?? t("drawer.none")}`;
|
|
91
|
+
const loaded = document.createElement("p");
|
|
92
|
+
loaded.textContent = t("panel.loaded");
|
|
93
|
+
const close = document.createElement("button");
|
|
94
|
+
close.type = "button";
|
|
95
|
+
close.textContent = t("drawer.close");
|
|
96
|
+
Object.assign(close.style, {
|
|
97
|
+
marginTop: "12px",
|
|
98
|
+
padding: "4px 12px",
|
|
99
|
+
border: "1px solid rgba(255,255,255,0.25)",
|
|
100
|
+
borderRadius: "6px",
|
|
101
|
+
background: "transparent",
|
|
102
|
+
color: "#fff",
|
|
103
|
+
cursor: "pointer"
|
|
104
|
+
});
|
|
105
|
+
const dismiss = () => root.remove();
|
|
106
|
+
close.addEventListener("click", dismiss);
|
|
107
|
+
root.addEventListener("click", (event) => {
|
|
108
|
+
if (event.target === root) dismiss();
|
|
109
|
+
});
|
|
110
|
+
panel.append(title, version, session, loaded, close);
|
|
111
|
+
root.append(panel);
|
|
112
|
+
document.body.append(root);
|
|
113
|
+
}
|
|
114
|
+
function HeaderAction(props) {
|
|
115
|
+
return (0, react.createElement)("button", {
|
|
116
|
+
type: "button",
|
|
117
|
+
style: {
|
|
118
|
+
padding: "4px 10px",
|
|
119
|
+
border: "1px solid rgba(255,255,255,0.25)",
|
|
120
|
+
borderRadius: "6px",
|
|
121
|
+
background: "transparent",
|
|
122
|
+
color: "inherit",
|
|
123
|
+
font: "12px/1.4 system-ui, sans-serif",
|
|
124
|
+
cursor: "pointer"
|
|
125
|
+
},
|
|
126
|
+
onClick: () => openDrawer(props)
|
|
127
|
+
}, tr(props)("switch.panel"));
|
|
128
|
+
}
|
|
129
|
+
function SettingsItem(props) {
|
|
130
|
+
const t = tr(props);
|
|
131
|
+
const config = globalConfig();
|
|
132
|
+
const value = config === void 0 ? t("settings.unavailable") : `${t("settings.lang")}: ${config.lang ?? "-"} · ${t("settings.metrics")}: ${config.metrics ?? "-"} · ${t("settings.busyMode")}: ${config.busyMode ?? "-"}`;
|
|
133
|
+
return (0, react.createElement)("div", { style: {
|
|
134
|
+
padding: "8px 0",
|
|
135
|
+
font: "13px/1.5 system-ui, sans-serif"
|
|
136
|
+
} }, (0, react.createElement)("div", { style: { fontWeight: 600 } }, t("settings.title")), (0, react.createElement)("div", { style: { opacity: .75 } }, value));
|
|
137
|
+
}
|
|
138
|
+
function apply(ctx) {
|
|
139
|
+
ctx.effect(() => ctx.locale.register(NS, {
|
|
140
|
+
zh: ZH,
|
|
141
|
+
en: EN
|
|
142
|
+
}), "caduceus: client dictionaries");
|
|
143
|
+
try {
|
|
144
|
+
console.info("[caduceus] apply start");
|
|
145
|
+
registerWhenDeclared(ctx, "conversation.session.header.actions", HeaderAction, "headerAction");
|
|
146
|
+
registerWhenDeclared(ctx, "settings.general.item", SettingsItem, "settingsRow");
|
|
147
|
+
console.info("[caduceus] apply ok");
|
|
148
|
+
} catch (error) {
|
|
149
|
+
console.error("[caduceus] apply failed:", error?.message ?? error);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Contribute into a slot another entry declares.
|
|
154
|
+
*
|
|
155
|
+
* Boot order is not ours: the UI packages declare these slots after this entry
|
|
156
|
+
* applies, so a bare `register` throws `slot "<name>" is not declared`.
|
|
157
|
+
* `subscribeDeclaration` is the sanctioned wait-for-declaration primitive.
|
|
158
|
+
*/
|
|
159
|
+
function registerWhenDeclared(ctx, name, component, id) {
|
|
160
|
+
ctx.effect(() => ctx.slots.inject(name, () => ctx.slots.register({
|
|
161
|
+
name,
|
|
162
|
+
id: `caduceus-${id}`,
|
|
163
|
+
order: 40,
|
|
164
|
+
locale: NS
|
|
165
|
+
}, component)), `caduceus: ${id}`);
|
|
166
|
+
}
|
|
167
|
+
//#endregion
|
|
168
|
+
exports.apply = apply;
|
|
169
|
+
exports.inject = inject;
|
|
170
|
+
|
|
171
|
+
return module.exports;
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","names":[],"sources":["../client/labels.ts","../client/index.ts"],"sourcesContent":["/** Client-side copy for the header button, drawer, and settings row. */\nexport const NS = 'caduceus';\n\nexport const ZH = {\n 'switch.panel': '双蛇杖',\n 'panel.title': '双蛇杖工作台',\n 'panel.loaded': '面板已加载',\n 'panel.version': '版本',\n 'drawer.session': '会话',\n 'drawer.none': '无会话',\n 'drawer.close': '关闭',\n 'settings.title': '双蛇杖',\n 'settings.lang': '语言',\n 'settings.metrics': '指标',\n 'settings.busyMode': '忙碌策略',\n 'settings.unavailable': '客户端配置尚未注入',\n} as const;\n\nexport const EN = {\n 'switch.panel': 'Caduceus',\n 'panel.title': 'Caduceus Workbench',\n 'panel.loaded': 'Panel loaded',\n 'panel.version': 'Version',\n 'drawer.session': 'Session',\n 'drawer.none': 'no session',\n 'drawer.close': 'Close',\n 'settings.title': 'Caduceus',\n 'settings.lang': 'Language',\n 'settings.metrics': 'Metrics',\n 'settings.busyMode': 'Busy mode',\n 'settings.unavailable': 'Client config not injected yet',\n} as const;\n\nexport type LabelKey = keyof typeof ZH;\nexport const FALLBACK: Record<LabelKey, string> = ZH;\n","/**\n * Browser half of dsh-caduceus. It contributes only to additive list slots:\n * a Session-header action button and a General-settings row. The drawer it\n * opens is rendered by this module (fixed-position DOM), not by a host panel.\n */\nimport { createElement } from 'react';\nimport { EN, FALLBACK, NS, ZH, type LabelKey } from './labels.ts';\n\ndeclare const __CADUCEUS_VERSION__: string;\n\nexport const inject = ['slots', 'locale'];\n\ninterface StandardProps {\n readonly t?: (key: LabelKey) => string;\n readonly sessionId?: string;\n}\n\ninterface ClientConfig {\n readonly lang?: string;\n readonly metrics?: string;\n readonly busyMode?: string;\n}\n\ntype SlotName = 'conversation.session.header.actions' | 'settings.general.item';\n\ninterface ClientCtx {\n readonly locale: {\n register(\n namespace: string,\n dictionaries: { zh: Record<string, string>; en: Record<string, string> },\n ): () => void;\n };\n readonly slots: {\n register(\n spec: {\n name: SlotName;\n id: string;\n order: number;\n locale?: string;\n },\n component: (props: StandardProps) => unknown,\n ): () => void;\n /** Wait for another entry's declaration, then contribute (facade: {inject, register}). */\n inject(name: SlotName, contribute: () => () => void): () => void;\n };\n effect(callback: () => () => void, label?: string): void;\n}\n\nfunction tr(props: StandardProps): (key: LabelKey) => string {\n return props.t ?? ((key) => FALLBACK[key]);\n}\n\nfunction globalConfig(): ClientConfig | undefined {\n return (globalThis as { __CADUCEUS_CLIENT_CONFIG__?: ClientConfig })\n .__CADUCEUS_CLIENT_CONFIG__;\n}\n\n/** Self-rendered drawer: no main panel, no shell.overlay, no host-owned slots. */\nfunction openDrawer(props: StandardProps): void {\n if (typeof document === 'undefined') return;\n document.getElementById('caduceus-drawer')?.remove();\n const t = tr(props);\n const root = document.createElement('div');\n root.id = 'caduceus-drawer';\n Object.assign(root.style, {\n position: 'fixed',\n inset: '0',\n zIndex: '10000',\n background: 'rgba(0,0,0,0.45)',\n display: 'flex',\n alignItems: 'flex-end',\n justifyContent: 'center',\n });\n const panel = document.createElement('div');\n Object.assign(panel.style, {\n width: 'min(560px, calc(100vw - 32px))',\n margin: '16px',\n padding: '20px',\n borderRadius: '12px',\n background: '#17171d',\n color: '#fff',\n boxShadow: '0 12px 40px rgba(0,0,0,0.45)',\n font: '14px/1.6 system-ui, sans-serif',\n });\n const title = document.createElement('h2');\n title.textContent = t('panel.title');\n Object.assign(title.style, { margin: '0 0 12px', fontSize: '18px' });\n const version = document.createElement('p');\n version.textContent = `${t('panel.version')} ${__CADUCEUS_VERSION__}`;\n const session = document.createElement('p');\n session.textContent = `${t('drawer.session')}: ${props.sessionId ?? t('drawer.none')}`;\n const loaded = document.createElement('p');\n loaded.textContent = t('panel.loaded');\n const close = document.createElement('button');\n close.type = 'button';\n close.textContent = t('drawer.close');\n Object.assign(close.style, {\n marginTop: '12px',\n padding: '4px 12px',\n border: '1px solid rgba(255,255,255,0.25)',\n borderRadius: '6px',\n background: 'transparent',\n color: '#fff',\n cursor: 'pointer',\n });\n const dismiss = (): void => root.remove();\n close.addEventListener('click', dismiss);\n root.addEventListener('click', (event) => {\n if (event.target === root) dismiss();\n });\n panel.append(title, version, session, loaded, close);\n root.append(panel);\n document.body.append(root);\n}\n\nfunction HeaderAction(props: StandardProps): unknown {\n const t = tr(props);\n return createElement(\n 'button',\n {\n type: 'button',\n style: {\n padding: '4px 10px',\n border: '1px solid rgba(255,255,255,0.25)',\n borderRadius: '6px',\n background: 'transparent',\n color: 'inherit',\n font: '12px/1.4 system-ui, sans-serif',\n cursor: 'pointer',\n },\n onClick: () => openDrawer(props),\n },\n t('switch.panel'),\n );\n}\n\nfunction SettingsItem(props: StandardProps): unknown {\n const t = tr(props);\n const config = globalConfig();\n const value = config === undefined\n ? t('settings.unavailable')\n : `${t('settings.lang')}: ${config.lang ?? '-'} · ` +\n `${t('settings.metrics')}: ${config.metrics ?? '-'} · ` +\n `${t('settings.busyMode')}: ${config.busyMode ?? '-'}`;\n return createElement(\n 'div',\n { style: { padding: '8px 0', font: '13px/1.5 system-ui, sans-serif' } },\n createElement('div', { style: { fontWeight: 600 } }, t('settings.title')),\n createElement('div', { style: { opacity: 0.75 } }, value),\n );\n}\n\nexport function apply(ctx: ClientCtx): void {\n ctx.effect(\n () => ctx.locale.register(NS, { zh: ZH, en: EN }),\n 'caduceus: client dictionaries',\n );\n try {\n console.info('[caduceus] apply start');\n registerWhenDeclared(ctx, 'conversation.session.header.actions', HeaderAction, 'headerAction');\n registerWhenDeclared(ctx, 'settings.general.item', SettingsItem, 'settingsRow');\n console.info('[caduceus] apply ok');\n } catch (error) {\n console.error('[caduceus] apply failed:', (error as Error)?.message ?? error);\n }\n}\n\n/**\n * Contribute into a slot another entry declares.\n *\n * Boot order is not ours: the UI packages declare these slots after this entry\n * applies, so a bare `register` throws `slot \"<name>\" is not declared`.\n * `subscribeDeclaration` is the sanctioned wait-for-declaration primitive.\n */\nfunction registerWhenDeclared(\n ctx: ClientCtx,\n name: SlotName,\n component: (props: StandardProps) => unknown,\n id: string,\n): void {\n ctx.effect(\n () =>\n ctx.slots.inject(name, () =>\n ctx.slots.register({ name, id: `caduceus-${id}`, order: 40, locale: NS }, component),\n ),\n `caduceus: ${id}`,\n );\n}\n"],"mappings":";;;;;;;;;AACA,MAAa,KAAK;AAElB,MAAa,KAAK;CAChB,gBAAgB;CAChB,eAAe;CACf,gBAAgB;CAChB,iBAAiB;CACjB,kBAAkB;CAClB,eAAe;CACf,gBAAgB;CAChB,kBAAkB;CAClB,iBAAiB;CACjB,oBAAoB;CACpB,qBAAqB;CACrB,wBAAwB;AAC1B;AAEA,MAAa,KAAK;CAChB,gBAAgB;CAChB,eAAe;CACf,gBAAgB;CAChB,iBAAiB;CACjB,kBAAkB;CAClB,eAAe;CACf,gBAAgB;CAChB,kBAAkB;CAClB,iBAAiB;CACjB,oBAAoB;CACpB,qBAAqB;CACrB,wBAAwB;AAC1B;AAGA,MAAa,WAAqC;;;;;;;;ACxBlD,MAAa,SAAS,CAAC,SAAS,QAAQ;AAsCxC,SAAS,GAAG,OAAiD;CAC3D,OAAO,MAAM,OAAO,QAAQ,SAAS;AACvC;AAEA,SAAS,eAAyC;CAChD,OAAQ,WACL;AACL;;AAGA,SAAS,WAAW,OAA4B;CAC9C,IAAI,OAAO,aAAa,aAAa;CACrC,SAAS,eAAe,iBAAiB,CAAC,EAAE,OAAO;CACnD,MAAM,IAAI,GAAG,KAAK;CAClB,MAAM,OAAO,SAAS,cAAc,KAAK;CACzC,KAAK,KAAK;CACV,OAAO,OAAO,KAAK,OAAO;EACxB,UAAU;EACV,OAAO;EACP,QAAQ;EACR,YAAY;EACZ,SAAS;EACT,YAAY;EACZ,gBAAgB;CAClB,CAAC;CACD,MAAM,QAAQ,SAAS,cAAc,KAAK;CAC1C,OAAO,OAAO,MAAM,OAAO;EACzB,OAAO;EACP,QAAQ;EACR,SAAS;EACT,cAAc;EACd,YAAY;EACZ,OAAO;EACP,WAAW;EACX,MAAM;CACR,CAAC;CACD,MAAM,QAAQ,SAAS,cAAc,IAAI;CACzC,MAAM,cAAc,EAAE,aAAa;CACnC,OAAO,OAAO,MAAM,OAAO;EAAE,QAAQ;EAAY,UAAU;CAAO,CAAC;CACnE,MAAM,UAAU,SAAS,cAAc,GAAG;CAC1C,QAAQ,cAAc,GAAG,EAAE,eAAe,EAAE;CAC5C,MAAM,UAAU,SAAS,cAAc,GAAG;CAC1C,QAAQ,cAAc,GAAG,EAAE,gBAAgB,EAAE,IAAI,MAAM,aAAa,EAAE,aAAa;CACnF,MAAM,SAAS,SAAS,cAAc,GAAG;CACzC,OAAO,cAAc,EAAE,cAAc;CACrC,MAAM,QAAQ,SAAS,cAAc,QAAQ;CAC7C,MAAM,OAAO;CACb,MAAM,cAAc,EAAE,cAAc;CACpC,OAAO,OAAO,MAAM,OAAO;EACzB,WAAW;EACX,SAAS;EACT,QAAQ;EACR,cAAc;EACd,YAAY;EACZ,OAAO;EACP,QAAQ;CACV,CAAC;CACD,MAAM,gBAAsB,KAAK,OAAO;CACxC,MAAM,iBAAiB,SAAS,OAAO;CACvC,KAAK,iBAAiB,UAAU,UAAU;EACxC,IAAI,MAAM,WAAW,MAAM,QAAQ;CACrC,CAAC;CACD,MAAM,OAAO,OAAO,SAAS,SAAS,QAAQ,KAAK;CACnD,KAAK,OAAO,KAAK;CACjB,SAAS,KAAK,OAAO,IAAI;AAC3B;AAEA,SAAS,aAAa,OAA+B;CAEnD,QAAA,GAAA,MAAA,cAAA,CACE,UACA;EACE,MAAM;EACN,OAAO;GACL,SAAS;GACT,QAAQ;GACR,cAAc;GACd,YAAY;GACZ,OAAO;GACP,MAAM;GACN,QAAQ;EACV;EACA,eAAe,WAAW,KAAK;CACjC,GAfQ,GAAG,KAgBX,CAAC,CAAC,cAAc,CAClB;AACF;AAEA,SAAS,aAAa,OAA+B;CACnD,MAAM,IAAI,GAAG,KAAK;CAClB,MAAM,SAAS,aAAa;CAC5B,MAAM,QAAQ,WAAW,KAAA,IACrB,EAAE,sBAAsB,IACxB,GAAG,EAAE,eAAe,EAAE,IAAI,OAAO,QAAQ,IAAI,KAC1C,EAAE,kBAAkB,EAAE,IAAI,OAAO,WAAW,IAAI,KAChD,EAAE,mBAAmB,EAAE,IAAI,OAAO,YAAY;CACrD,QAAA,GAAA,MAAA,cAAA,CACE,OACA,EAAE,OAAO;EAAE,SAAS;EAAS,MAAM;CAAiC,EAAE,IAAA,GAAA,MAAA,cAAA,CACxD,OAAO,EAAE,OAAO,EAAE,YAAY,IAAI,EAAE,GAAG,EAAE,gBAAgB,CAAC,IAAA,GAAA,MAAA,cAAA,CAC1D,OAAO,EAAE,OAAO,EAAE,SAAS,IAAK,EAAE,GAAG,KAAK,CAC1D;AACF;AAEA,SAAgB,MAAM,KAAsB;CAC1C,IAAI,aACI,IAAI,OAAO,SAAS,IAAI;EAAE,IAAI;EAAI,IAAI;CAAG,CAAC,GAChD,+BACF;CACA,IAAI;EACF,QAAQ,KAAK,wBAAwB;EACrC,qBAAqB,KAAK,uCAAuC,cAAc,cAAc;EAC7F,qBAAqB,KAAK,yBAAyB,cAAc,aAAa;EAC9E,QAAQ,KAAK,qBAAqB;CACpC,SAAS,OAAO;EACd,QAAQ,MAAM,4BAA6B,OAAiB,WAAW,KAAK;CAC9E;AACF;;;;;;;;AASA,SAAS,qBACP,KACA,MACA,WACA,IACM;CACN,IAAI,aAEA,IAAI,MAAM,OAAO,YACf,IAAI,MAAM,SAAS;EAAE;EAAM,IAAI,YAAY;EAAM,OAAO;EAAI,QAAQ;CAAG,GAAG,SAAS,CACrF,GACF,aAAa,IACf;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-caduceus",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "Feishu/Lark surface plugin for DeepSeek Harness (dsh): one topic per session, SOUL persona sync, streaming process cards, /status and /doctor.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -31,10 +31,14 @@
|
|
|
31
31
|
"default": "./dist/render/truncate.js"
|
|
32
32
|
},
|
|
33
33
|
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
34
|
-
"./package.json": "./package.json"
|
|
34
|
+
"./package.json": "./package.json",
|
|
35
|
+
"./client": {
|
|
36
|
+
"default": "./lib/client.js"
|
|
37
|
+
}
|
|
35
38
|
},
|
|
36
39
|
"files": [
|
|
37
40
|
"dist/",
|
|
41
|
+
"lib/",
|
|
38
42
|
"cordis.patch.yml",
|
|
39
43
|
"README.md",
|
|
40
44
|
"LICENSE",
|
|
@@ -55,6 +59,9 @@
|
|
|
55
59
|
"dshReleases": {
|
|
56
60
|
"0.2.1-rc.2": "compatible"
|
|
57
61
|
}
|
|
62
|
+
},
|
|
63
|
+
"client": {
|
|
64
|
+
"platform": "web"
|
|
58
65
|
}
|
|
59
66
|
},
|
|
60
67
|
"peerDependencies": {
|
|
@@ -79,12 +86,13 @@
|
|
|
79
86
|
"typescript": "6.0.3"
|
|
80
87
|
},
|
|
81
88
|
"scripts": {
|
|
82
|
-
"build": "tsc -p tsconfig.json",
|
|
89
|
+
"build": "tsc -p tsconfig.json && node scripts/build-client.mjs",
|
|
83
90
|
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json --noEmit",
|
|
84
91
|
"test": "tsc -p tsconfig.test.json && node --test .test-dist/test/*.test.js",
|
|
85
92
|
"smoke": "node scripts/smoke.mjs",
|
|
86
93
|
"clean": "rm -rf dist .test-dist",
|
|
87
|
-
"prepare": "tsc -p tsconfig.json"
|
|
94
|
+
"prepare": "tsc -p tsconfig.json",
|
|
95
|
+
"check": "node scripts/check-workbench.mjs"
|
|
88
96
|
},
|
|
89
97
|
"packageManager": "pnpm@11.7.0",
|
|
90
98
|
"license": "MIT",
|