dsh-foreman 0.3.1 → 0.3.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/CHANGELOG.md +36 -0
- package/lib/index.js +43 -4
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,42 @@
|
|
|
6
6
|
|
|
7
7
|
本项目自己的版本从 `0.1.0` 开始。
|
|
8
8
|
|
|
9
|
+
## 0.3.3 — 2026-11-08
|
|
10
|
+
|
|
11
|
+
### 改进:派单时直接告诉设备「你就是那台机器」
|
|
12
|
+
|
|
13
|
+
人在 hub 上写「看看宿舍电脑上的项目结构」。「宿舍电脑」就是收到这条任务的那台机器,但**派单文本里
|
|
14
|
+
没有一句话说明这件事**——设备收到的只有标题和请求原文。
|
|
15
|
+
|
|
16
|
+
于是模型每次都要先花不少步数去比对设备名、列目录、找自己在哪儿,确认之后才真正开始干活。
|
|
17
|
+
|
|
18
|
+
现在每张派单的开头多了一段:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
## Where this job runs
|
|
22
|
+
|
|
23
|
+
You are the device **宿舍电脑** (id `pc`) working in `F:\dsh`. When the request names this machine —
|
|
24
|
+
"宿舍电脑", or "this computer", "here", "the machine you are on" — it means you, and the files are
|
|
25
|
+
already under that directory. Do not go looking for another machine.
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
只把**登记的设备名**当作别名。目录的最后一段通常是项目名(`shop`),把它说成机器别名会让模型
|
|
29
|
+
搞不清那是项目还是主机。
|
|
30
|
+
|
|
31
|
+
## 0.3.2 — 2026-11-08
|
|
32
|
+
|
|
33
|
+
### 修复:派单文本没有说要回报
|
|
34
|
+
|
|
35
|
+
真机上一次派单,设备做了 30 步、写完整答案,**一次都没调用 `foreman_report`**——因为派单文本里
|
|
36
|
+
根本没提这件事。设备收到的只有标题、请求原文和附件说明,模型自然当成普通任务做完就停,
|
|
37
|
+
hub 于是永远停在 `running`。
|
|
38
|
+
|
|
39
|
+
现在每张派单的末尾都有一段:`foreman_report` 是结果到达问话人的唯一途径,答在对话里
|
|
40
|
+
不算完成,**没回报就不算做完**。
|
|
41
|
+
|
|
42
|
+
配套地,设备侧 `dsh-foreman-node@0.2.1` 加了一层兜底:一轮结束而还没回报时自动补一条
|
|
43
|
+
`ready`。两层一起,模型照做时走正路,不照做也不会让工单挂死。
|
|
44
|
+
|
|
9
45
|
## 0.3.1 — 2026-11-08
|
|
10
46
|
|
|
11
47
|
### 修复:同一 id 的两个连接互相踢,设备永远上不了线
|
package/lib/index.js
CHANGED
|
@@ -9622,6 +9622,30 @@ function attachmentSection(names) {
|
|
|
9622
9622
|
*
|
|
9623
9623
|
* @throws When the attachments together exceed the frame budget: a truncated task would be worse than a refused one.
|
|
9624
9624
|
*/
|
|
9625
|
+
/**
|
|
9626
|
+
* What a device is told about reporting, appended to every assignment.
|
|
9627
|
+
*
|
|
9628
|
+
* Without it the job Session simply answers and stops: on a real deployment a device did thirty steps of real work,
|
|
9629
|
+
* wrote a complete answer, and never called `foreman_report`, so the hub sat at `running` with no report forever and
|
|
9630
|
+
* a person saw a job that never finished. The tool exists, but nothing told the model it was required or final.
|
|
9631
|
+
*/
|
|
9632
|
+
/**
|
|
9633
|
+
* Where this job is running, stated so the device never has to deduce that the request referred to itself.
|
|
9634
|
+
*
|
|
9635
|
+
* A person at the hub writes about their machines by name ("the dorm computer", "my laptop"). The device that receives
|
|
9636
|
+
* that sentence IS one of those machines, but nothing in the text said which one, so the model spent its first steps
|
|
9637
|
+
* comparing names, listing directories and looking for itself. This is one line that ends that.
|
|
9638
|
+
*/
|
|
9639
|
+
function locationSection(device) {
|
|
9640
|
+
return `\n## Where this job runs\n\nYou are the device **${device.name}** (id \`${device.id}\`) working in \`${device.directory}\`. When the request names this machine — "${device.name}", or "this computer", "here", "the machine you are on" — it means you, and the files are already under that directory. Do not go looking for another machine.\n`;
|
|
9641
|
+
}
|
|
9642
|
+
const REPORT_SECTION = [
|
|
9643
|
+
"\n## Reporting back\n",
|
|
9644
|
+
"\nWhen you have finished, call `foreman_report` — that tool is how this job reaches the person who asked, and ",
|
|
9645
|
+
"answering in chat does not reach them. Report `ready` with a one-sentence summary of the result, or `blocked` with ",
|
|
9646
|
+
"the one question only they can answer, or `failed` if the work cannot be done. The job is not finished until you ",
|
|
9647
|
+
"have reported.\n"
|
|
9648
|
+
].join("");
|
|
9625
9649
|
function renderForDevice(input) {
|
|
9626
9650
|
const { ticket, trigger } = input;
|
|
9627
9651
|
const refs = refsOf(trigger);
|
|
@@ -9640,12 +9664,15 @@ function renderForDevice(input) {
|
|
|
9640
9664
|
}
|
|
9641
9665
|
const scope = scopeOf(ticket);
|
|
9642
9666
|
const request = trigger?.text?.trim() ?? "";
|
|
9667
|
+
const location = input.device === void 0 ? "" : locationSection(input.device);
|
|
9643
9668
|
return {
|
|
9644
9669
|
text: [
|
|
9645
9670
|
`# ${ticket.title}`,
|
|
9671
|
+
location,
|
|
9646
9672
|
scope === "" ? "" : `\n${scope}\n`,
|
|
9647
9673
|
request === "" ? "" : `\n## The request as it was written\n\n${request}\n`,
|
|
9648
|
-
attachmentSection(attachments.map((attachment) => attachment.name ?? attachment.id))
|
|
9674
|
+
attachmentSection(attachments.map((attachment) => attachment.name ?? attachment.id)),
|
|
9675
|
+
REPORT_SECTION
|
|
9649
9676
|
].filter((section) => section !== "").join(""),
|
|
9650
9677
|
attachments
|
|
9651
9678
|
};
|
|
@@ -9797,7 +9824,11 @@ var ForemanExecutor = class {
|
|
|
9797
9824
|
}));
|
|
9798
9825
|
}
|
|
9799
9826
|
assignFrame(ticket, assignment, deviceId, directory) {
|
|
9800
|
-
const
|
|
9827
|
+
const device = this.input.deviceOf?.(deviceId);
|
|
9828
|
+
const rendered = this.render(ticket, device === void 0 ? void 0 : {
|
|
9829
|
+
...device,
|
|
9830
|
+
directory
|
|
9831
|
+
});
|
|
9801
9832
|
return {
|
|
9802
9833
|
v: 1,
|
|
9803
9834
|
type: "assign",
|
|
@@ -9814,11 +9845,12 @@ var ForemanExecutor = class {
|
|
|
9814
9845
|
* The task as the device reads it. Rendered on the hub because the device has none of the hub's resources; the
|
|
9815
9846
|
* device directory rides on the frame, not in the text.
|
|
9816
9847
|
*/
|
|
9817
|
-
render(ticket) {
|
|
9848
|
+
render(ticket, device) {
|
|
9818
9849
|
return renderForDevice({
|
|
9819
9850
|
ticket,
|
|
9820
9851
|
...(this.input.triggerOf ?? (() => ({})))(ticket),
|
|
9821
|
-
readAttachment: this.input.readAttachment ?? (() => Buffer.alloc(0))
|
|
9852
|
+
readAttachment: this.input.readAttachment ?? (() => Buffer.alloc(0)),
|
|
9853
|
+
...device === void 0 ? {} : { device }
|
|
9822
9854
|
});
|
|
9823
9855
|
}
|
|
9824
9856
|
/** Append work to the send chain, keeping the chain alive after a failure. */
|
|
@@ -11909,6 +11941,13 @@ var Runtime = class extends Service {
|
|
|
11909
11941
|
local: executor,
|
|
11910
11942
|
readAttachment: (id) => readFileSync(attachmentPath(id)),
|
|
11911
11943
|
triggerOf: (ticket) => ({ trigger: store.triggers.get(ticket.origin.triggerId) }),
|
|
11944
|
+
deviceOf: (id) => {
|
|
11945
|
+
const device = this.devices.get(id);
|
|
11946
|
+
return device === void 0 ? void 0 : {
|
|
11947
|
+
id: device.id,
|
|
11948
|
+
name: device.name
|
|
11949
|
+
};
|
|
11950
|
+
},
|
|
11912
11951
|
engine: () => this.engine,
|
|
11913
11952
|
transport: { send: (deviceId, frame) => this.hub?.send(deviceId, frame) ?? Promise.reject(/* @__PURE__ */ new Error(`the foreman hub is not listening, so ${deviceId} cannot be reached`)) },
|
|
11914
11953
|
log: (text) => ctx.logger.warn("dsh-foreman: %s", text)
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "dsh-foreman",
|
|
3
3
|
"author": "niechao136",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "0.3.
|
|
5
|
+
"version": "0.3.3",
|
|
6
6
|
"description": "dsh-foreman:主 loop 在多设备上派单的 DSH bundle:需求来自本机操作者的主会话而不是 GitHub/飞书,每个工作区各有一条主会话,主会话只负责路由,真正的活儿派给 Worker 在该工作区目录里执行。",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "lib/index.js",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
},
|
|
63
63
|
"packageManager": "pnpm@11.7.0",
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"dsh-foreman-node": "0.2.
|
|
65
|
+
"dsh-foreman-node": "0.2.1",
|
|
66
66
|
"proper-lockfile": "4.1.2",
|
|
67
67
|
"ws": "8.22.0",
|
|
68
68
|
"zod": "4.4.3"
|