dsh-remote-wecom 0.1.2 → 0.1.3
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/docs/DESIGN.md +1 -1
- package/lib/core/remote.mjs +9 -0
- package/lib/core/watchdog.mjs +7 -1
- package/lib/core/wecom.mjs +8 -3
- package/package.json +1 -1
package/docs/DESIGN.md
CHANGED
|
@@ -55,7 +55,7 @@ DeepSeek Harness.app ── host 进程
|
|
|
55
55
|
|
|
56
56
|
事件(`ctx.on(name, fn, { global: true })`):
|
|
57
57
|
|
|
58
|
-
- `session/event (session, event)`:`turn/start{turn}`、`user/message{source.kind, source.rpcId}`(`source.kind` 除 `user` 外还有注入类 `runtime-context` / `skill-catalog` / `time-context`,以及宿主唤起的 `schedule` / `goal`)、`assistant/message{message.content[]}`、`tool/call{name, arguments}`、`tool/result{message.isError, meta.path}`、`turn/end{reason.kind: completed|aborted|error|max-tokens, reason.error.message}`、`session/title{title, source}`、`model/selection{provider, model, reasoningEffort}`(只在切模型时发)、`request/header{header.config{provider, model, reasoningEffort}}`(每次 LLM 请求,新会话首轮靠它拿模型,此时 `modelSelection`
|
|
58
|
+
- `session/event (session, event)`:`turn/start{turn}`、`user/message{source.kind, source.rpcId}`(`source.kind` 除 `user` 外还有注入类 `runtime-context` / `skill-catalog` / `time-context`,以及宿主唤起的 `schedule` / `goal`)、`assistant/message{message.content[]}`、`tool/call{name, arguments}`、`tool/result{message.isError, meta.path}`、`turn/end{reason.kind: completed|aborted|error|max-tokens, reason.error.message}`、`session/title{title, source}`、`model/selection{provider, model, reasoningEffort}`(只在切模型时发)、`request/header{header.config{provider, model, reasoningEffort}}`(每次 LLM 请求,新会话首轮靠它拿模型,此时 `modelSelection` 投影还是空的;还没发首条时按 `modelCatalog.default` 显示)。`user/message` 的 data 就是消息本身(`data.source`、`data.content`),混有 runtime-context 等系统注入消息,只认 `source.kind === 'user'`。一轮的顺序:`agent/inbox/spliced` → `api-session/status [sid, true]` → `turn/start` → `user/message` → … → `turn/end`。标题先来一条 `source.kind:'fallback'`(首条消息截断),模型起的标题随后再来一条。
|
|
59
59
|
- `api-session/added (summary)`、`api-session/removed (sessionId)`、`api-session/status (sessionId, running)`。
|
|
60
60
|
- `approval/request (req, next)`,注册时加 `prepend: true`:`req.{toolName, callId, reason, displayReason.zh, agent, signal}`,返回 `'allowed-once' | 'rejected' | 'cancelled' | 'unavailable'`。会话 id 取 `req.agent.session.id ?? req.agent.id`。
|
|
61
61
|
- `user-questions/request (req, next)`:`req.questions[{id, question, header, options[{label, description}], multiSelect}]`,返回 `{answers:[{id, selected:[label], custom?}]}`。
|
package/lib/core/remote.mjs
CHANGED
|
@@ -665,6 +665,15 @@ export function createRemote({ host, dir, config, log: echo = () => { }, createT
|
|
|
665
665
|
catch (e) {
|
|
666
666
|
log(`dsh[${this.key}] projections 失败(沿用上次信息):${e?.message || e}`);
|
|
667
667
|
}
|
|
668
|
+
// 新会话跑完首轮前 modelSelection 投影为空,按部署默认模型显示。
|
|
669
|
+
if (!this.model) {
|
|
670
|
+
const d = await host.modelCatalog().then((c) => c?.default, () => null);
|
|
671
|
+
if (d?.model) {
|
|
672
|
+
this.model = String(d.model);
|
|
673
|
+
this.modelProvider = String(d.provider || "");
|
|
674
|
+
this.thinkingLevel = d.reasoningEffort ? String(d.reasoningEffort) : "";
|
|
675
|
+
}
|
|
676
|
+
}
|
|
668
677
|
}
|
|
669
678
|
async stats() {
|
|
670
679
|
await this.refreshMeta();
|
package/lib/core/watchdog.mjs
CHANGED
|
@@ -76,8 +76,14 @@ export function takeLaunched(dir, now = Date.now()) {
|
|
|
76
76
|
return Number.isFinite(at) && now - at <= SPOOL_TTL_MS;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
/** 看门狗跑在 ELECTRON_RUN_AS_NODE 下;`open` 会把环境传给 Desktop,不去掉它 Desktop 会以 Node 模式启动后立即退出。 */
|
|
80
|
+
export function desktopEnv(env = process.env) {
|
|
81
|
+
const {ELECTRON_RUN_AS_NODE, ...rest} = env;
|
|
82
|
+
return rest;
|
|
83
|
+
}
|
|
84
|
+
|
|
79
85
|
export function launchDesktop() {
|
|
80
|
-
return new Promise((resolve, reject) => execFile('open', ['-g', '-j', '-b', DESKTOP_BUNDLE_ID], {timeout: 15_000},
|
|
86
|
+
return new Promise((resolve, reject) => execFile('open', ['-g', '-j', '-b', DESKTOP_BUNDLE_ID], {timeout: 15_000, env: desktopEnv()},
|
|
81
87
|
e => e ? reject(e) : resolve()));
|
|
82
88
|
}
|
|
83
89
|
|
package/lib/core/wecom.mjs
CHANGED
|
@@ -353,6 +353,11 @@ export class WeComTransport {
|
|
|
353
353
|
async flush() {
|
|
354
354
|
if (!this.connected || this.closed || this.working) return;
|
|
355
355
|
this.working = true;
|
|
356
|
+
// 回执超时时服务端多半已送达(常见于电脑睡眠),重发只会重复,按已送达处理;明确报错才重试。
|
|
357
|
+
const deliver = async request => {
|
|
358
|
+
try { await request(); }
|
|
359
|
+
catch (e) { if (!/ack timeout/i.test(String(e?.message || ''))) throw e; this.log('企微回执超时,按已送达处理'); }
|
|
360
|
+
};
|
|
356
361
|
try {
|
|
357
362
|
for (const name of this.queueFiles()) {
|
|
358
363
|
if (!this.connected || this.closed) break;
|
|
@@ -363,10 +368,10 @@ export class WeComTransport {
|
|
|
363
368
|
try {
|
|
364
369
|
for (; packet.cursor < packet.steps.length; packet.cursor++) {
|
|
365
370
|
const step = packet.steps[packet.cursor];
|
|
366
|
-
if (step.kind === 'text') await this.client.sendMessage(packet.recipient, {msgtype: 'markdown', markdown: {content: step.text}});
|
|
371
|
+
if (step.kind === 'text') await deliver(() => this.client.sendMessage(packet.recipient, {msgtype: 'markdown', markdown: {content: step.text}}));
|
|
367
372
|
if (step.kind === 'card') {
|
|
368
373
|
try {
|
|
369
|
-
await this.client.sendMessage(packet.recipient, {msgtype: 'template_card', template_card: step.card});
|
|
374
|
+
await deliver(() => this.client.sendMessage(packet.recipient, {msgtype: 'template_card', template_card: step.card}));
|
|
370
375
|
this.rememberCard(step.card);
|
|
371
376
|
}
|
|
372
377
|
catch (e) {
|
|
@@ -380,7 +385,7 @@ export class WeComTransport {
|
|
|
380
385
|
}
|
|
381
386
|
if (step.kind === 'file') {
|
|
382
387
|
const media = await this.client.uploadMedia(fs.readFileSync(step.file), {type: 'file', filename: path.basename(step.file)});
|
|
383
|
-
await this.client.sendMediaMessage(packet.recipient, 'file', media.media_id);
|
|
388
|
+
await deliver(() => this.client.sendMediaMessage(packet.recipient, 'file', media.media_id));
|
|
384
389
|
}
|
|
385
390
|
writeJson(file, {...packet, cursor: packet.cursor + 1});
|
|
386
391
|
}
|
package/package.json
CHANGED