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 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 rendered = this.render(ticket);
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.1",
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.0",
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"