pi-proactive 0.2.0-beta.3 → 0.2.0-beta.5
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 +27 -0
- package/README.md +46 -29
- package/build/cli.mjs +70 -6
- package/build/extensions/proactive.js +187 -2
- package/build/src/assistant.js +572 -0
- package/build/src/commands.js +13 -0
- package/build/src/config.js +20 -0
- package/build/src/content.js +2 -0
- package/build/src/daemon-host.js +2 -0
- package/build/src/daemon.js +2 -0
- package/build/src/dedupe.js +2 -0
- package/build/src/health.js +2 -0
- package/build/src/lineage.js +2 -0
- package/build/src/mailbox.js +2 -0
- package/build/src/notify.js +2 -0
- package/build/src/peer-spawn.js +2 -0
- package/build/src/peer.js +2 -0
- package/build/src/registry.js +2 -0
- package/build/src/socket.js +2 -0
- package/build/src/supervisor-state.js +355 -0
- package/build/src/supervisor-tui.js +85 -0
- package/build/src/supervisor-web.js +262 -0
- package/build/src/task-store.js +2 -0
- package/build/src/thread.js +2 -0
- package/package.json +7 -6
- package/skills/pi-proactive/SKILL.md +92 -27
- package/tools/cli.mjs +70 -6
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web 监督面(**只读 · 本地优先 · 无依赖**)。
|
|
3
|
+
*
|
|
4
|
+
* 形态:`pi-proactive web [--port N] [--agent-dir D]` → Node 内置 http,**只监听 127.0.0.1**。
|
|
5
|
+
*
|
|
6
|
+
* 边界(越界即不合格):
|
|
7
|
+
* - **只读**:没有任何写配置的入口(无表单 / 无按钮 / 无写路由;写方法一律 405);
|
|
8
|
+
* - **单一数据源**:页面与 API 的数字全部来自 `src/supervisor-state.ts` 的**同一份聚合**
|
|
9
|
+
* (本模块**不重算**任何指标);
|
|
10
|
+
* - **本地优先**:绑定地址固定 `127.0.0.1`(不从其它网卡暴露);
|
|
11
|
+
* - **无依赖**:只用 Node 内置 `http` / `fs` / `path`,前端零框架零构建(样式内联)。
|
|
12
|
+
*
|
|
13
|
+
* 页面六块:状态卡 / 联系时间线 / 托付 / 阻塞 / 预算 / 守恒灯。
|
|
14
|
+
*/
|
|
15
|
+
import http from "node:http";
|
|
16
|
+
import { collectSupervisorState, formatBudgetLine, formatConservationLine, formatEntrustmentConservationLine, formatStatusLine, formatSupervisorSummary, } from "./supervisor-state.js";
|
|
17
|
+
/** 只监听的地址(**本地优先**:默认不暴露到局域网/公网)。 */
|
|
18
|
+
export const SUPERVISOR_HOST = "127.0.0.1";
|
|
19
|
+
/** HTML 转义(页面内容来自本地文件,仍按不可信处理)。 */
|
|
20
|
+
function esc(text) {
|
|
21
|
+
return text
|
|
22
|
+
.replace(/&/g, "&")
|
|
23
|
+
.replace(/</g, "<")
|
|
24
|
+
.replace(/>/g, ">")
|
|
25
|
+
.replace(/"/g, """);
|
|
26
|
+
}
|
|
27
|
+
/** 毫秒 → `MM-DD HH:mm:ss`(本地时区)。 */
|
|
28
|
+
function fmtTime(ts) {
|
|
29
|
+
const d = new Date(ts);
|
|
30
|
+
const p = (n) => String(n).padStart(2, "0");
|
|
31
|
+
return `${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
|
|
32
|
+
}
|
|
33
|
+
const STYLE = `
|
|
34
|
+
:root { color-scheme: light dark; }
|
|
35
|
+
body { font: 14px/1.6 -apple-system, "PingFang SC", "Noto Sans CJK SC", system-ui, sans-serif; margin: 0; padding: 24px; }
|
|
36
|
+
h1 { font-size: 18px; margin: 0 0 4px; }
|
|
37
|
+
.sub { opacity: .65; font-size: 12px; margin-bottom: 16px; }
|
|
38
|
+
.grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); }
|
|
39
|
+
section { border: 1px solid rgba(128,128,128,.35); border-radius: 8px; padding: 12px 14px; }
|
|
40
|
+
section h2 { font-size: 14px; margin: 0 0 8px; letter-spacing: .04em; }
|
|
41
|
+
table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
|
|
42
|
+
td, th { text-align: left; padding: 2px 6px 2px 0; vertical-align: top; }
|
|
43
|
+
.k { opacity: .6; white-space: nowrap; }
|
|
44
|
+
.ok { color: #1a7f37; font-weight: 600; }
|
|
45
|
+
.bad { color: #b42318; font-weight: 600; }
|
|
46
|
+
ul { margin: 4px 0 0; padding-left: 18px; }
|
|
47
|
+
li { font-size: 12.5px; }
|
|
48
|
+
details { margin-top: 8px; font-size: 12px; opacity: .8; }
|
|
49
|
+
code { font-size: 11.5px; }
|
|
50
|
+
`;
|
|
51
|
+
/** 一行键值表。 */
|
|
52
|
+
function row(key, value) {
|
|
53
|
+
return `<tr><td class="k">${esc(key)}</td><td>${value}</td></tr>`;
|
|
54
|
+
}
|
|
55
|
+
/** 状态卡。 */
|
|
56
|
+
function renderStatus(state) {
|
|
57
|
+
const s = state.status;
|
|
58
|
+
return [
|
|
59
|
+
"<table>",
|
|
60
|
+
row("摘要", esc(formatSupervisorSummary(state))),
|
|
61
|
+
row("宿主 / 进程", esc(formatStatusLine(state))),
|
|
62
|
+
row("在不在", s.running ? '<span class="ok">在跑</span>' : `<span class="bad">不在${s.notRunningReason ? `(${esc(s.notRunningReason)})` : ""}</span>`),
|
|
63
|
+
row("心跳时刻", s.heartbeatTs === undefined ? "—" : esc(fmtTime(s.heartbeatTs))),
|
|
64
|
+
row("上次联系", s.lastContactTs === undefined ? "从未" : esc(fmtTime(s.lastContactTs))),
|
|
65
|
+
row("值班身份", esc(s.agentId ?? "—")),
|
|
66
|
+
row("运行形态", esc(s.runMode ?? "—")),
|
|
67
|
+
row("总开关 / 联系开关", `${s.enabled === undefined ? "—" : s.enabled ? "开" : "关"} / ${s.contactEnabled === undefined ? "—" : s.contactEnabled ? "开" : "关"}`),
|
|
68
|
+
"</table>",
|
|
69
|
+
].join("");
|
|
70
|
+
}
|
|
71
|
+
/** 联系时间线。 */
|
|
72
|
+
function renderTimeline(state) {
|
|
73
|
+
if (state.timeline.length === 0)
|
|
74
|
+
return "<p>暂无记录</p>";
|
|
75
|
+
const rows = state.timeline
|
|
76
|
+
.slice()
|
|
77
|
+
.reverse()
|
|
78
|
+
.map((t) => {
|
|
79
|
+
const len = t.bodyLen === undefined ? "" : ` · 正文 ${t.bodyLen} 字`;
|
|
80
|
+
const digest = t.bodyDigest === undefined ? "" : ` · 摘要 ${t.bodyDigest}`;
|
|
81
|
+
const peer = t.peer === undefined ? "" : ` · 对端 ${t.peer}`;
|
|
82
|
+
return `<li><span class="k">${esc(fmtTime(t.ts))}</span> · ${esc(t.summary)}${esc(len)}${esc(digest)}${esc(peer)}</li>`;
|
|
83
|
+
})
|
|
84
|
+
.join("");
|
|
85
|
+
return `<ul>${rows}</ul>`;
|
|
86
|
+
}
|
|
87
|
+
/** 托付(含目标)。 */
|
|
88
|
+
function renderTrusts(state) {
|
|
89
|
+
if (state.trusts.length === 0 && state.goals.length === 0)
|
|
90
|
+
return "<p>暂无托付</p>";
|
|
91
|
+
const parts = [];
|
|
92
|
+
if (state.trusts.length > 0) {
|
|
93
|
+
const rows = state.trusts
|
|
94
|
+
.map((t) => `<li>${esc(t.target)} · <span class="k">${esc(t.status)}</span>${t.needsHuman ? " · 需人决策" : ""}${t.humanReason ? ` · ${esc(t.humanReason)}` : ""}</li>`)
|
|
95
|
+
.join("");
|
|
96
|
+
parts.push(`<table><tr><th>托付</th><th>状态</th></tr></table><ul>${rows}</ul>`);
|
|
97
|
+
}
|
|
98
|
+
if (state.goals.length > 0) {
|
|
99
|
+
const rows = state.goals
|
|
100
|
+
.map((g) => `<li>${esc(g.title)} · <span class="k">${esc(g.status)}</span> · 推进 ${g.progressSteps} 步 · 行动 ${g.usedActions}/${g.maxActions}${g.humanReason ? ` · ${esc(g.humanReason)}` : ""}</li>`)
|
|
101
|
+
.join("");
|
|
102
|
+
parts.push(`<ul>${rows}</ul>`);
|
|
103
|
+
}
|
|
104
|
+
return parts.join("");
|
|
105
|
+
}
|
|
106
|
+
/** 阻塞。 */
|
|
107
|
+
function renderBlockers(state) {
|
|
108
|
+
if (state.blockers.length === 0)
|
|
109
|
+
return '<p class="ok">无阻塞</p>';
|
|
110
|
+
const rows = state.blockers
|
|
111
|
+
.map((b) => `<li>${esc(b.title)}${b.detail ? ` · ${esc(b.detail)}` : ""}</li>`)
|
|
112
|
+
.join("");
|
|
113
|
+
return `<ul>${rows}</ul>`;
|
|
114
|
+
}
|
|
115
|
+
/** 预算。 */
|
|
116
|
+
function renderBudget(state) {
|
|
117
|
+
return [
|
|
118
|
+
"<table>",
|
|
119
|
+
row("余量", esc(formatBudgetLine(state))),
|
|
120
|
+
row("闸门", state.budget.exhausted ? `<span class="bad">${esc(state.budget.reason ?? "已耗尽")}</span>` : '<span class="ok">未耗尽</span>'),
|
|
121
|
+
row("耗尽时降级", state.budget.degradeToObserve ? "降级为只观察" : "直接停下"),
|
|
122
|
+
row("配置", state.budget.configured ? "已读 daemon.json" : "未配置(按缺省上限显示)"),
|
|
123
|
+
"</table>",
|
|
124
|
+
].join("");
|
|
125
|
+
}
|
|
126
|
+
/** 守恒灯。 */
|
|
127
|
+
function renderConservation(state) {
|
|
128
|
+
const c = state.conservation;
|
|
129
|
+
return [
|
|
130
|
+
"<table>",
|
|
131
|
+
row("行为四段", esc(formatConservationLine(state))),
|
|
132
|
+
row("托付四段", esc(formatEntrustmentConservationLine(state))),
|
|
133
|
+
row("回应率", `${(c.responseRate * 100).toFixed(1)}%`),
|
|
134
|
+
row("托付闭环率", `${(c.closureRate * 100).toFixed(1)}%`),
|
|
135
|
+
row("守候时长", `${Math.round(c.watchDurationMs / 1000)} 秒`),
|
|
136
|
+
row("判定", c.equal ? '<span class="ok">四段相等(无悬空)</span>' : '<span class="bad">四段不等(有悬空)</span>'),
|
|
137
|
+
"</table>",
|
|
138
|
+
].join("");
|
|
139
|
+
}
|
|
140
|
+
/** 渲染整页(**服务端渲染**:一处渲染实现 → 页面与 API 不会各说各话)。 */
|
|
141
|
+
export function renderSupervisorHtml(state) {
|
|
142
|
+
return `<!doctype html>
|
|
143
|
+
<html lang="zh-CN">
|
|
144
|
+
<head>
|
|
145
|
+
<meta charset="utf-8">
|
|
146
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
147
|
+
<meta http-equiv="refresh" content="5">
|
|
148
|
+
<title>pi-proactive 监督面</title>
|
|
149
|
+
<style>${STYLE}</style>
|
|
150
|
+
</head>
|
|
151
|
+
<body>
|
|
152
|
+
<h1>pi-proactive 监督面(只读)</h1>
|
|
153
|
+
<p class="sub">数据源:${esc(state.agentDir)} · 聚合于 ${esc(fmtTime(state.generatedAt))} · 本页只读,改配置请回 Pi 会话</p>
|
|
154
|
+
<div class="grid">
|
|
155
|
+
<section><h2>① 状态卡</h2>${renderStatus(state)}</section>
|
|
156
|
+
<section><h2>② 联系时间线</h2>${renderTimeline(state)}</section>
|
|
157
|
+
<section><h2>③ 托付</h2>${renderTrusts(state)}</section>
|
|
158
|
+
<section><h2>④ 阻塞</h2>${renderBlockers(state)}</section>
|
|
159
|
+
<section><h2>⑤ 预算</h2>${renderBudget(state)}</section>
|
|
160
|
+
<section><h2>⑥ 守恒灯</h2>${renderConservation(state)}</section>
|
|
161
|
+
</div>
|
|
162
|
+
<details><summary>数据源路径(可折叠)</summary>
|
|
163
|
+
<code>${esc(state.agentDir)}/proactive/lineage/*.jsonl</code><br>
|
|
164
|
+
<code>${esc(state.agentDir)}/proactive/daemon/heartbeat.json</code><br>
|
|
165
|
+
<code>${esc(state.agentDir)}/proactive/daemon/state.json</code><br>
|
|
166
|
+
<code>${esc(state.agentDir)}/proactive/daemon.json</code><br>
|
|
167
|
+
<code>${esc(state.agentDir)}/proactive/trust/registry.json</code>
|
|
168
|
+
</details>
|
|
169
|
+
</body>
|
|
170
|
+
</html>
|
|
171
|
+
`;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* 启动 Web 监督面(**只监听 127.0.0.1**)。
|
|
175
|
+
*
|
|
176
|
+
* @param opts 见 {@link SupervisorWebOptions}。
|
|
177
|
+
*/
|
|
178
|
+
export async function startSupervisorWeb(opts = {}) {
|
|
179
|
+
const port = opts.port ?? 7799;
|
|
180
|
+
const collect = () => collectSupervisorState({
|
|
181
|
+
...(opts.agentDir !== undefined ? { agentDir: opts.agentDir } : {}),
|
|
182
|
+
...(opts.agentId !== undefined ? { agentId: opts.agentId } : {}),
|
|
183
|
+
...(opts.timelineLimit !== undefined ? { timelineLimit: opts.timelineLimit } : {}),
|
|
184
|
+
});
|
|
185
|
+
const server = http.createServer((req, res) => {
|
|
186
|
+
// **只读**:只接受 GET / HEAD;其余方法一律 405(页面不提供任何写入口)。
|
|
187
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
188
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
189
|
+
res.writeHead(405, { "content-type": "text/plain; charset=utf-8" });
|
|
190
|
+
res.end("只读:监督面不接受写操作\n");
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const url = req.url ?? "/";
|
|
194
|
+
const pathOnly = url.split("?")[0];
|
|
195
|
+
try {
|
|
196
|
+
if (pathOnly === "/" || pathOnly === "/index.html") {
|
|
197
|
+
const body = renderSupervisorHtml(collect());
|
|
198
|
+
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
199
|
+
res.end(method === "HEAD" ? undefined : body);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (pathOnly === "/api/state") {
|
|
203
|
+
const body = JSON.stringify(collect(), null, 2);
|
|
204
|
+
res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
|
|
205
|
+
res.end(method === "HEAD" ? undefined : body);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (pathOnly === "/api/timeline") {
|
|
209
|
+
const state = collect();
|
|
210
|
+
const body = JSON.stringify({ timeline: state.timeline, conservation: state.conservation }, null, 2);
|
|
211
|
+
res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
|
|
212
|
+
res.end(method === "HEAD" ? undefined : body);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (pathOnly === "/api/trusts") {
|
|
216
|
+
const state = collect();
|
|
217
|
+
const body = JSON.stringify({ trusts: state.trusts, goals: state.goals }, null, 2);
|
|
218
|
+
res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
|
|
219
|
+
res.end(method === "HEAD" ? undefined : body);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (pathOnly === "/api/budget") {
|
|
223
|
+
const state = collect();
|
|
224
|
+
const body = JSON.stringify({ budget: state.budget, blockers: state.blockers }, null, 2);
|
|
225
|
+
res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
|
|
226
|
+
res.end(method === "HEAD" ? undefined : body);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
res.writeHead(404, { "content-type": "text/plain; charset=utf-8" });
|
|
230
|
+
res.end("没有这个页面\n");
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
res.writeHead(500, { "content-type": "text/plain; charset=utf-8" });
|
|
234
|
+
res.end(`监督面内部错误:${err instanceof Error ? err.message : String(err)}\n`);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
await new Promise((resolve, reject) => {
|
|
238
|
+
server.once("error", reject);
|
|
239
|
+
// **只监听 127.0.0.1**(不传 host → 会监听全部网卡 → 违反本地优先)。
|
|
240
|
+
server.listen(port, SUPERVISOR_HOST, () => {
|
|
241
|
+
server.removeListener("error", reject);
|
|
242
|
+
resolve();
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
const address = server.address();
|
|
246
|
+
const actualPort = typeof address === "object" && address !== null ? address.port : port;
|
|
247
|
+
let closed = false;
|
|
248
|
+
return {
|
|
249
|
+
url: `http://${SUPERVISOR_HOST}:${actualPort}`,
|
|
250
|
+
port: actualPort,
|
|
251
|
+
close: async () => {
|
|
252
|
+
if (closed)
|
|
253
|
+
return;
|
|
254
|
+
closed = true;
|
|
255
|
+
await new Promise((resolve) => {
|
|
256
|
+
server.close(() => resolve());
|
|
257
|
+
// 断开残留连接,保证端口**立即**释放(否则 close 会挂住)。
|
|
258
|
+
server.closeAllConnections?.();
|
|
259
|
+
});
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
}
|
package/build/src/task-store.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// 【S0 归属】L1 内核 · 宿主无关(契约:docs/project/kernel-contract-s0.md §6.1)
|
|
2
|
+
// → 不得 import 宿主包、不得出现宿主类型/字段;对外只经宿主无关接口(HostPort 一族)。
|
|
1
3
|
import fs from "node:fs";
|
|
2
4
|
import path from "node:path";
|
|
3
5
|
import { PROACTIVE_FILE_MODE, chmodQuiet, ensureDir, proactiveDir, resolveAgentDir, writeAtomicFile, } from "./config.js";
|
package/build/src/thread.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-proactive",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.5",
|
|
4
4
|
"description": "给 Pi 增加主动性:空闲时主动推进或提问、按时间点主动发起任务、多个会话之间对话协作。(专有软件:分发物不含源码,使用条款见包内 LICENSE)",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"repository": {
|
|
@@ -46,16 +46,17 @@
|
|
|
46
46
|
"apiVerificationNote": "本扩展的全部宿主 API 断言只对 @earendil-works/pi-coding-agent 1.3.0 验证过;升级宿主后行为可能变化,需重跑 runtime 探针后再用。"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
+
"@earendil-works/pi-agent-core": "*",
|
|
49
50
|
"@earendil-works/pi-coding-agent": "*",
|
|
50
|
-
"
|
|
51
|
+
"typebox": "*"
|
|
51
52
|
},
|
|
52
|
-
"
|
|
53
|
-
"
|
|
53
|
+
"bin": {
|
|
54
|
+
"pi-proactive": "./build/cli.mjs"
|
|
54
55
|
},
|
|
55
56
|
"bundledDependencies": [
|
|
56
57
|
"typebox"
|
|
57
58
|
],
|
|
58
|
-
"
|
|
59
|
-
"
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"typebox": "^1.3.27"
|
|
60
61
|
}
|
|
61
62
|
}
|
|
@@ -21,14 +21,15 @@ description: >-
|
|
|
21
21
|
<!-- BEGIN:GEN tools -->
|
|
22
22
|
| 工具 | 参数 | 作用 | 定义位置 |
|
|
23
23
|
| --- | --- | --- | --- |
|
|
24
|
-
| `schedule_task` | prompt(必填):任务提示词<br>intent(可选):投递意图,默认 wake(取值:wake \| interrupt \| queue \| notify)<br>askUser(可选):触发时是否向用户提问(仅本地)<br>when(可选):触发时间:时长(10m)/ 时刻(18:30)/ ISO 8601;缺省按 10m 处理 | 预约一个稍后触发的任务(真正落库到任务表,到点由调度器投递)。when 支持时长(10m)、时刻(18:30)或 ISO 时间;缺省按 10m 处理。 | `pi-proactive/extensions/proactive.ts:
|
|
25
|
-
| `message_agent` | to(必填):目标 agent 的名字或 agentId<br>kind(可选):消息语义(对方要不要回我);缺省 notify(取值:notify \| ask \| task \| reply \| broadcast)<br>subject(可选):标题<br>body(必填):正文<br>intent(可选):投递意图(怎么投给对方);缺省 wake(取值:wake \| interrupt \| queue \| notify)<br>threadId(可选):话题 / 会话 id(可选) | 给另一个正在运行的 agent 会话发消息(跨进程协作)。对端在线时走 socket 直达;不在线时消息落盘到对端邮箱,待其上线后处理。 | `pi-proactive/extensions/proactive.ts:
|
|
26
|
-
| `list_peers` | name(可选):名字子串(大小写敏感;也命中 agentId)。同名歧义时返回与 message_agent 报错完全一致的候选<br>status(可选):按在场状态过滤(online = 在线 = 空闲 ∪ 忙碌)(取值:online \| idle \| busy \| offline)<br>capability(可选):按能力标签过滤(如 message_agent)<br>includeOffline(可选):是否包含离线对端(缺省 false = 只给在线) | 列出本机可见的其它 agent 对端(名字 / agentId / 在场状态 / 心跳龄 / 能力标签 / 在途任务数 / 传输方式),可按名字、状态、能力过滤。**只读**:不产生任何对外行为。发消息前先查它(先查后发),别盲发 —— 离线对端现在打扰它不合适。 | `pi-proactive/extensions/proactive.ts:
|
|
27
|
-
| `proactive_query` | action(必填):查询动作(与 /proactive 子命令同名)(取值:peers \| history \| status \| tasks \| doctor \| help \| config)<br>name(可选):peers:名字子串过滤<br>all(可选):peers:是否包含离线对端<br>idle(可选):peers:只看空闲对端<br>detail(可选):peers:只看这一个 agentId<br>scope(可选):history:范围(对端 / 我)(取值:peer \| me)<br>peer(可选):history:按对端 agentId 过滤<br>limit(可选):history:条数(1-200 的整数)<br>json(可选):doctor / config:输出 JSON | 查询域:查对端(peers)/ 看主动行为历史(history)/ 看状态(status)/ 看任务(tasks)/ 只读体检(doctor)/ 看命令手册(help)/ 查配置清单(config)。**只读**:不产生任何对外行为。 | `pi-proactive/extensions/proactive.ts:
|
|
28
|
-
| `progress_query` | | 查项目进度与 ETA:返回**区间 + 置信度 + 归因 + 阻塞项**(不给单点数字)。口径唯一(scripts/progress-clock.py),与人侧 `/proactive progress` 同源同口径、逐字一致。**只读**:不产生任何对外行为。 | `pi-proactive/extensions/proactive.ts:
|
|
29
|
-
| `proactive_schedule` | action(必填):排程动作(与 /proactive 子命令同名)(取值:in \| at \| every \| cancel)<br>when(可选):in/at/every:触发时间(时长 10m / 时刻 18:30)<br>content(可选):in/at/every:任务内容<br>id(可选):cancel:任务 id | 排程域:定时(in)/ 定点(at)/ 周期(every)/ 取消(cancel)。落到真实任务表,到点由调度器投递;与命令面 `/proactive in\|at\|every\|cancel` 同源。 | `pi-proactive/extensions/proactive.ts:
|
|
30
|
-
| `proactive_peer` | action(必填):对端动作(与 /proactive 子命令同名)(取值:tell \| spawn)<br>peer(可选):tell:目标对端(名字或 agentId)<br>content(可选):tell:指令内容<br>kind(可选):tell:指令类型(缺省 ask)(取值:ask \| task \| notify)<br>subject(可选):tell:标题<br>thread(可选):tell:线程 id<br>name(可选):spawn:对端名字 | 对端协作域:给对端下指令(tell)/ 创建对端会话(spawn,受同时存活数上限)。tell 的目标解析与回执三态与命令面 `/proactive tell` 完全同源。 | `pi-proactive/extensions/proactive.ts:
|
|
31
|
-
| `proactive_manage` | action(必填):管理动作(与 /proactive 子命令同名)(取值:on \| off \| idle \| test)<br>state(可选):idle:开 / 关(取值:on \| off) | 管理域:主动性总开关(on/off)、空闲触发开关(idle)、自检(test)。这些能力属 human-gated:调用只会回报「需人工确认」并给出可复制的人工指令,**不会**自行改动全局开关。 | `pi-proactive/extensions/proactive.ts:
|
|
24
|
+
| `schedule_task` | prompt(必填):任务提示词<br>intent(可选):投递意图,默认 wake(取值:wake \| interrupt \| queue \| notify)<br>askUser(可选):触发时是否向用户提问(仅本地)<br>when(可选):触发时间:时长(10m)/ 时刻(18:30)/ ISO 8601;缺省按 10m 处理 | 预约一个稍后触发的任务(真正落库到任务表,到点由调度器投递)。when 支持时长(10m)、时刻(18:30)或 ISO 时间;缺省按 10m 处理。 | `pi-proactive/extensions/proactive.ts:647` |
|
|
25
|
+
| `message_agent` | to(必填):目标 agent 的名字或 agentId<br>kind(可选):消息语义(对方要不要回我);缺省 notify(取值:notify \| ask \| task \| reply \| broadcast)<br>subject(可选):标题<br>body(必填):正文<br>intent(可选):投递意图(怎么投给对方);缺省 wake(取值:wake \| interrupt \| queue \| notify)<br>threadId(可选):话题 / 会话 id(可选) | 给另一个正在运行的 agent 会话发消息(跨进程协作)。对端在线时走 socket 直达;不在线时消息落盘到对端邮箱,待其上线后处理。 | `pi-proactive/extensions/proactive.ts:692` |
|
|
26
|
+
| `list_peers` | name(可选):名字子串(大小写敏感;也命中 agentId)。同名歧义时返回与 message_agent 报错完全一致的候选<br>status(可选):按在场状态过滤(online = 在线 = 空闲 ∪ 忙碌)(取值:online \| idle \| busy \| offline)<br>capability(可选):按能力标签过滤(如 message_agent)<br>includeOffline(可选):是否包含离线对端(缺省 false = 只给在线) | 列出本机可见的其它 agent 对端(名字 / agentId / 在场状态 / 心跳龄 / 能力标签 / 在途任务数 / 传输方式),可按名字、状态、能力过滤。**只读**:不产生任何对外行为。发消息前先查它(先查后发),别盲发 —— 离线对端现在打扰它不合适。 | `pi-proactive/extensions/proactive.ts:766` |
|
|
27
|
+
| `proactive_query` | action(必填):查询动作(与 /proactive 子命令同名)(取值:peers \| history \| status \| tasks \| doctor \| help \| config \| supervisor)<br>name(可选):peers:名字子串过滤<br>all(可选):peers:是否包含离线对端<br>idle(可选):peers:只看空闲对端<br>detail(可选):peers:只看这一个 agentId<br>scope(可选):history:范围(对端 / 我)(取值:peer \| me)<br>peer(可选):history:按对端 agentId 过滤<br>limit(可选):history:条数(1-200 的整数)<br>json(可选):doctor / config:输出 JSON | 查询域:查对端(peers)/ 看主动行为历史(history)/ 看状态(status)/ 看任务(tasks)/ 只读体检(doctor)/ 看命令手册(help)/ 查配置清单(config)/ 看监督面(supervisor)。**只读**:不产生任何对外行为。 | `pi-proactive/extensions/proactive.ts:803` |
|
|
28
|
+
| `progress_query` | | 查项目进度与 ETA:返回**区间 + 置信度 + 归因 + 阻塞项**(不给单点数字)。口径唯一(scripts/progress-clock.py),与人侧 `/proactive progress` 同源同口径、逐字一致。**只读**:不产生任何对外行为。 | `pi-proactive/extensions/proactive.ts:841` |
|
|
29
|
+
| `proactive_schedule` | action(必填):排程动作(与 /proactive 子命令同名)(取值:in \| at \| every \| cancel)<br>when(可选):in/at/every:触发时间(时长 10m / 时刻 18:30)<br>content(可选):in/at/every:任务内容<br>id(可选):cancel:任务 id | 排程域:定时(in)/ 定点(at)/ 周期(every)/ 取消(cancel)。落到真实任务表,到点由调度器投递;与命令面 `/proactive in\|at\|every\|cancel` 同源。 | `pi-proactive/extensions/proactive.ts:855` |
|
|
30
|
+
| `proactive_peer` | action(必填):对端动作(与 /proactive 子命令同名)(取值:tell \| spawn)<br>peer(可选):tell:目标对端(名字或 agentId)<br>content(可选):tell:指令内容<br>kind(可选):tell:指令类型(缺省 ask)(取值:ask \| task \| notify)<br>subject(可选):tell:标题<br>thread(可选):tell:线程 id<br>name(可选):spawn:对端名字 | 对端协作域:给对端下指令(tell)/ 创建对端会话(spawn,受同时存活数上限)。tell 的目标解析与回执三态与命令面 `/proactive tell` 完全同源。 | `pi-proactive/extensions/proactive.ts:874` |
|
|
31
|
+
| `proactive_manage` | action(必填):管理动作(与 /proactive 子命令同名)(取值:on \| off \| idle \| test)<br>state(可选):idle:开 / 关(取值:on \| off) | 管理域:主动性总开关(on/off)、空闲触发开关(idle)、自检(test)。这些能力属 human-gated:调用只会回报「需人工确认」并给出可复制的人工指令,**不会**自行改动全局开关。 | `pi-proactive/extensions/proactive.ts:913` |
|
|
32
|
+
| `proactive_assistant` | action(必填):常驻助理动作:on 打开 / off 关闭 / status 看状态 / restart 重装(与 /proactive assistant 同名)(取值:on \| off \| status \| restart) | 常驻助理(7×24)控制:on 打开 / off 关闭 / status 看状态 / restart 重装。与人侧 `/proactive assistant …` 同源同分派 → 逐字一致;安装**不阻塞**(立即回执「正在安装…」,结果用 status 查)。装上默认 enabled=false + 干跑 → 不花钱、不主动打扰。 | `pi-proactive/extensions/proactive.ts:925` |
|
|
32
33
|
<!-- END:GEN tools -->
|
|
33
34
|
|
|
34
35
|
> 工具面真源 = 实现:上表由 `scripts/gen-doc-tables.sh` 从扩展的 `registerTool` 注册项与参数 schema 自动生成,**不要手改**。
|
|
@@ -140,27 +141,28 @@ description: >-
|
|
|
140
141
|
<!-- BEGIN:GEN commands -->
|
|
141
142
|
| 命令 | 参数形态 | 做什么 | 状态 |
|
|
142
143
|
| --- | --- | --- | --- |
|
|
143
|
-
| `/proactive status` | `无` | 总览:开关、空闲计时、待触发任务、对端在线、上次主动、本小时用量。 | 已实现 · `pi-proactive/src/commands.ts:
|
|
144
|
-
| `/proactive on` | `无` | 打开全部「代理自发」行为(空闲触发、模型自调度、跨会话自发请求)。 | 已实现 · `pi-proactive/src/commands.ts:
|
|
145
|
-
| `/proactive off` | `无` | 关闭全部「代理自发」行为;已排期任务保留但不再触发。 | 已实现 · `pi-proactive/src/commands.ts:
|
|
146
|
-
| `/proactive test` | `无` | 立即模拟一次触发(走完整「护栏 → 投递」链路),用于验证主动能力是否真的生效。 | 已实现 · `pi-proactive/src/commands.ts:
|
|
147
|
-
| `/proactive tasks` | `无` | 列出所有待触发任务(编号 / 类型 / 下次到期 / 归属 / 触发次数)。 | 已实现 · `pi-proactive/src/commands.ts:
|
|
148
|
-
| `/proactive cancel` | `<id>` | 取消一个任务。 | 已实现 · `pi-proactive/src/commands.ts:
|
|
149
|
-
| `/proactive idle` | `on\|off` | 打开 / 关闭空闲触发并设定阈值(on 可带时长参数,省略 = 用上次设定)。 | 已实现 · `pi-proactive/src/commands.ts:
|
|
150
|
-
| `/proactive peers` | `[--all\|--idle\|--name <子串>\|--detail <agentId>]` | 列出已注册的对端与在线状态(名字 / 忙闲 / 最后心跳);可加 --all / --idle / --name / --detail。 | 已实现 · `pi-proactive/src/commands.ts:
|
|
151
|
-
| `/proactive spawn` | `[名字]` | 创建一个对端会话(子进程 pi --mode rpc);本会话退出时一并终止。 | 已实现 · `pi-proactive/src/commands.ts:
|
|
152
|
-
| `/proactive in` | `<时长> <内容>` | 排一个延迟任务(默认只属于当前会话)。 | 已实现 · `pi-proactive/src/commands.ts:
|
|
153
|
-
| `/proactive at` | `<HH:MM> <内容>` | 排一个绝对时间任务。 | 已实现 · `pi-proactive/src/commands.ts:
|
|
154
|
-
| `/proactive every` | `<时长> <内容>` | 排一个周期任务。 | 已实现 · `pi-proactive/src/commands.ts:
|
|
155
|
-
| `/proactive help` | `无` | 打印命令速查表(命令表 / 配置键 / 最常见故障 / 详细文档路径)。 | 已实现 · `pi-proactive/src/commands.ts:
|
|
156
|
-
| `/proactive history` | `[peer\|me] [条数]` | 查看最近的主动行为记录(时间 / 类型 / 目标 / 结果 / 被护栏拦下的原因)。 | 已实现 · `pi-proactive/src/commands.ts:
|
|
157
|
-
| `/proactive doctor` | `[--json]` | 只读体检:逐项健康判定 + 处置建议(--json 机器可读);不打扰、不耗额度。 | 已实现 · `pi-proactive/src/commands.ts:
|
|
158
|
-
| `/proactive progress` | `无` | 项目进度 + ETA:区间 + 置信度 + 归因 + 阻塞项(不给单点数字;口径 = scripts/progress-clock.py)。 | 已实现 · `pi-proactive/src/commands.ts:
|
|
159
|
-
| `/proactive tell` | `<peer> <内容> [--kind ask\|task\|notify]` | 人侧给另一个会话下指令(--kind ask\|task\|notify,缺省 ask 要回话);回信落回本会话。 | 已实现 · `pi-proactive/src/commands.ts:
|
|
160
|
-
| `/proactive
|
|
144
|
+
| `/proactive status` | `无` | 总览:开关、空闲计时、待触发任务、对端在线、上次主动、本小时用量。 | 已实现 · `pi-proactive/src/commands.ts:632` |
|
|
145
|
+
| `/proactive on` | `无` | 打开全部「代理自发」行为(空闲触发、模型自调度、跨会话自发请求)。 | 已实现 · `pi-proactive/src/commands.ts:668` |
|
|
146
|
+
| `/proactive off` | `无` | 关闭全部「代理自发」行为;已排期任务保留但不再触发。 | 已实现 · `pi-proactive/src/commands.ts:669` |
|
|
147
|
+
| `/proactive test` | `无` | 立即模拟一次触发(走完整「护栏 → 投递」链路),用于验证主动能力是否真的生效。 | 已实现 · `pi-proactive/src/commands.ts:758` |
|
|
148
|
+
| `/proactive tasks` | `无` | 列出所有待触发任务(编号 / 类型 / 下次到期 / 归属 / 触发次数)。 | 已实现 · `pi-proactive/src/commands.ts:761` |
|
|
149
|
+
| `/proactive cancel` | `<id>` | 取消一个任务。 | 已实现 · `pi-proactive/src/commands.ts:787` |
|
|
150
|
+
| `/proactive idle` | `on\|off` | 打开 / 关闭空闲触发并设定阈值(on 可带时长参数,省略 = 用上次设定)。 | 已实现 · `pi-proactive/src/commands.ts:827` |
|
|
151
|
+
| `/proactive peers` | `[--all\|--idle\|--name <子串>\|--detail <agentId>]` | 列出已注册的对端与在线状态(名字 / 忙闲 / 最后心跳);可加 --all / --idle / --name / --detail。 | 已实现 · `pi-proactive/src/commands.ts:689` |
|
|
152
|
+
| `/proactive spawn` | `[名字]` | 创建一个对端会话(子进程 pi --mode rpc);本会话退出时一并终止。 | 已实现 · `pi-proactive/src/commands.ts:735` |
|
|
153
|
+
| `/proactive in` | `<时长> <内容>` | 排一个延迟任务(默认只属于当前会话)。 | 已实现 · `pi-proactive/src/commands.ts:805` |
|
|
154
|
+
| `/proactive at` | `<HH:MM> <内容>` | 排一个绝对时间任务。 | 已实现 · `pi-proactive/src/commands.ts:806` |
|
|
155
|
+
| `/proactive every` | `<时长> <内容>` | 排一个周期任务。 | 已实现 · `pi-proactive/src/commands.ts:807` |
|
|
156
|
+
| `/proactive help` | `无` | 打印命令速查表(命令表 / 配置键 / 最常见故障 / 详细文档路径)。 | 已实现 · `pi-proactive/src/commands.ts:842` |
|
|
157
|
+
| `/proactive history` | `[peer\|me] [条数]` | 查看最近的主动行为记录(时间 / 类型 / 目标 / 结果 / 被护栏拦下的原因)。 | 已实现 · `pi-proactive/src/commands.ts:765` |
|
|
158
|
+
| `/proactive doctor` | `[--json]` | 只读体检:逐项健康判定 + 处置建议(--json 机器可读);不打扰、不耗额度。 | 已实现 · `pi-proactive/src/commands.ts:640` |
|
|
159
|
+
| `/proactive progress` | `无` | 项目进度 + ETA:区间 + 置信度 + 归因 + 阻塞项(不给单点数字;口径 = scripts/progress-clock.py)。 | 已实现 · `pi-proactive/src/commands.ts:648` |
|
|
160
|
+
| `/proactive tell` | `<peer> <内容> [--kind ask\|task\|notify]` | 人侧给另一个会话下指令(--kind ask\|task\|notify,缺省 ask 要回话);回信落回本会话。 | 已实现 · `pi-proactive/src/commands.ts:755` |
|
|
161
|
+
| `/proactive assistant` | `on\|off\|status\|restart` | 常驻助理(7×24)控制:on 打开(后台装,不阻塞会话)/ off 关闭(回执含残留终检)/ status 看宿主·进程·心跳·上次联系 / restart 重装。与终端 CLI 共用同一套安装/卸载实现。 | 已实现 · `pi-proactive/src/commands.ts:724` |
|
|
162
|
+
| `/proactive config` | `[undo] [--json]` | 列出全部配置项(当前值 / 缺省值 / 取值范围 / 一句说明),先报警写错的项;config undo 撤销上一次配置改动。 | 已实现 · `pi-proactive/src/commands.ts:655` |
|
|
161
163
|
<!-- END:GEN commands -->
|
|
162
164
|
|
|
163
|
-
> 命令面真源 = 实现:上表由 `scripts/gen-doc-tables.sh` 从扩展的 `SUBCOMMANDS` + `switch (sub)` 分派自动生成(**共
|
|
165
|
+
> 命令面真源 = 实现:上表由 `scripts/gen-doc-tables.sh` 从扩展的 `SUBCOMMANDS` + `switch (sub)` 分派自动生成(**共 19 个命令**),**不要手改**。
|
|
164
166
|
|
|
165
167
|
## 不要做的事
|
|
166
168
|
|
|
@@ -183,3 +185,66 @@ description: >-
|
|
|
183
185
|
|
|
184
186
|
补充:消息回执是 `queued` → 尚未送达(见陷阱 1);`/proactive test` 可强制走一次投递链路自检
|
|
185
187
|
(不受总开关与冷却影响)。
|
|
188
|
+
|
|
189
|
+
## 数字员工与托付(用户说人话即可,先理解意图再选能力)
|
|
190
|
+
|
|
191
|
+
> 这一节是【模型用的说明书】:用户会用自然语言提要求,你要把人话映射到下面的能力。
|
|
192
|
+
|
|
193
|
+
### 常驻数字员工("让它一直在 / 别老烦我 / 它现在在干嘛")
|
|
194
|
+
```
|
|
195
|
+
/proactive assistant on 打开常驻数字员工(装常驻进程 + 注册系统通知;回执含"已常驻"与三证据)
|
|
196
|
+
/proactive assistant status 看它是否在跑:宿主 / 进程 / 心跳龄期 / 上次联系 / 预算余量
|
|
197
|
+
/proactive assistant off 关闭(残留终检为空:任务 0 / 进程 0 / 单元 0)
|
|
198
|
+
/proactive assistant restart 重启(等价再跑一次 on)
|
|
199
|
+
```
|
|
200
|
+
人话映射示例:
|
|
201
|
+
"让它以后一直帮我盯着" → assistant on | "它现在在干嘛" → assistant status | "别老烦我了" → assistant off
|
|
202
|
+
★ 不要让用户手改配置文件、不要让用户装系统服务 —— 这些由上面一条命令完成。
|
|
203
|
+
|
|
204
|
+
### 托付("帮我盯着 X,Y 发生时告诉我")
|
|
205
|
+
```
|
|
206
|
+
/proactive trust add "<目标>" [--when <条件>] [--notify <渠道>] 登记托付
|
|
207
|
+
/proactive trust list 看托付:状态(活跃/已达成/已撤销)/ 下次检查 / 进度
|
|
208
|
+
/proactive trust cancel <id> 撤销托付
|
|
209
|
+
/proactive inbox 它问我、我还没答的
|
|
210
|
+
/proactive ask "<问题>" [--wait] 主动问它(默认异步回执;--wait 会等,超时也回执)
|
|
211
|
+
```
|
|
212
|
+
人话映射示例:
|
|
213
|
+
"帮我盯着 CI 变绿" → trust add(并回执"记下了,我会在变绿时告诉你")
|
|
214
|
+
"我托付的事怎么样了" → trust list | "这个不用盯了" → trust cancel
|
|
215
|
+
"它找我啥了" → inbox | "问一下它进度" → ask
|
|
216
|
+
★ 托付里的"需要人担责的决定"必须回到人(human-gated),不要自行结账。
|
|
217
|
+
|
|
218
|
+
### 三层主动性(用户想要"它自己动起来"时,说清是哪一层)
|
|
219
|
+
```
|
|
220
|
+
L-B 托付型 我明确说"盯着 X" → trust add
|
|
221
|
+
L-C 目标驱动 我给目标("把发布流程跑通") → goal 相关命令,由它自己拆解推进、遇阻才问
|
|
222
|
+
L-D 自主推进 它从未竟目标里自己发现下一步 → 由它自己推进;不可逆/对外动作仍需人确认
|
|
223
|
+
```
|
|
224
|
+
★ 空闲触发的主动是【浅层】:默认静默,只在确有价值且可等时开口;空转的主动是负价值。
|
|
225
|
+
|
|
226
|
+
### 监督("它今天对我做了什么")
|
|
227
|
+
```
|
|
228
|
+
会话内 状态栏有摘要;pi-proactive web 起本地看板(127.0.0.1,只读)
|
|
229
|
+
/proactive progress 进度与 ETA:区间 + 置信度 + 归因 + 第 3 层阻塞项(【无单点 ETA】)
|
|
230
|
+
/proactive history 血缘与应答记录(触发→投递→回应→结账 四段)
|
|
231
|
+
/proactive doctor 巡检健康与告警
|
|
232
|
+
```
|
|
233
|
+
★ 监督面是【看】的;改配置请回会话里用自然语言,不要在页面里改。
|
|
234
|
+
|
|
235
|
+
### 配置(三层作用域,改配置前先知道值来自哪一层)
|
|
236
|
+
```
|
|
237
|
+
会话级 > agent 级 > 全局默认(每层只覆盖显式声明的键)
|
|
238
|
+
★ 安全类键(预算上限 / 权限阶梯 / 来源校验 / 联系白名单)取【最严】——局部不得放宽
|
|
239
|
+
/proactive config 看全部配置项与当前值、默认值、取值范围
|
|
240
|
+
```
|
|
241
|
+
人话映射示例:"把空闲时间改成 20 分钟" → 改对应配置项 | "它多久找我一次" → 看 idleTrigger.afterMs
|
|
242
|
+
★ 拼错或未知的配置项会被指名并给出最相近键 —— 不要绕过它。
|
|
243
|
+
|
|
244
|
+
## 不要做的事(模型侧)
|
|
245
|
+
```
|
|
246
|
+
× 不要让用户去装系统服务、改配置文件、开终端 —— 门槛最低的形态是"在 pi-agent 里说人话"
|
|
247
|
+
× 不要在用户没表达托付时替他登记托付(那是替他做决定)
|
|
248
|
+
× 不要把"待办/想法"说成"已达成" —— 未达成必须显式说明
|
|
249
|
+
× 不要用单点时间承诺,用区间 + 置信度 + 阻塞项
|
|
250
|
+
```
|
package/tools/cli.mjs
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// 单一真源:本文件是 build/cli.mjs 的**源文件**(build/ 是编译产物,不入库)。
|
|
6
6
|
// build-release.sh 负责把它复制到 build/cli.mjs → npm 包的 bin 入口。
|
|
7
7
|
import { spawn } from "node:child_process";
|
|
8
|
-
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
9
9
|
import path from "node:path";
|
|
10
10
|
import fs from "node:fs";
|
|
11
11
|
|
|
@@ -39,9 +39,18 @@ const CLI_FLAGS = {
|
|
|
39
39
|
"--service-name",
|
|
40
40
|
"--no-start",
|
|
41
41
|
"--purge-data",
|
|
42
|
+
"--purge-data",
|
|
42
43
|
],
|
|
43
44
|
};
|
|
44
45
|
|
|
46
|
+
/**
|
|
47
|
+
* `web` 子命令自己的参数(**不进 CLI_FLAGS**)。
|
|
48
|
+
*
|
|
49
|
+
* 为什么单独一张表:CLI_FLAGS 是「**转发给 .sh 脚本**的参数契约」(T1 逐条核对
|
|
50
|
+
* cli 声明的每个参数都被对应 .sh 接受);web 不转发给任何 .sh → 不属于那张表。
|
|
51
|
+
*/
|
|
52
|
+
const WEB_FLAGS = ["--port", "--agent-dir", "--json"];
|
|
53
|
+
|
|
45
54
|
const USAGE = `pi-proactive — 给 Pi 增加主动性(空闲主动、按点主动、多会话协作)
|
|
46
55
|
|
|
47
56
|
用法:
|
|
@@ -51,6 +60,7 @@ const USAGE = `pi-proactive — 给 Pi 增加主动性(空闲主动、按点
|
|
|
51
60
|
pi-proactive progress 进度与 ETA(区间/置信度/阻塞项)
|
|
52
61
|
pi-proactive config 查看配置(全部项/当前值/默认值/范围/说明)
|
|
53
62
|
pi-proactive history 主动行为与应答的血缘记录
|
|
63
|
+
pi-proactive web 本地 Web 监督面(只读;只监听 127.0.0.1;--port/--agent-dir/--json)
|
|
54
64
|
|
|
55
65
|
安装(任选其一):
|
|
56
66
|
npx pi-proactive daemon install
|
|
@@ -76,8 +86,8 @@ const script = (name) => {
|
|
|
76
86
|
};
|
|
77
87
|
|
|
78
88
|
/** 参数白名单校验(**转发前**拦下未知参数 → 不把错误留给脚本去猜)。 */
|
|
79
|
-
|
|
80
|
-
|
|
89
|
+
/** 参数白名单校验(**转发前**拦下未知参数 → 不把错误留给脚本去猜)。 */
|
|
90
|
+
function checkFlags(allowed, args) {
|
|
81
91
|
for (const a of args) {
|
|
82
92
|
if (!a.startsWith("--")) continue;
|
|
83
93
|
const name = a.split("=")[0];
|
|
@@ -105,7 +115,7 @@ if (cmd === "daemon") {
|
|
|
105
115
|
process.exit(2);
|
|
106
116
|
}
|
|
107
117
|
const rest = argv.slice(2);
|
|
108
|
-
checkFlags(sub, rest);
|
|
118
|
+
checkFlags(CLI_FLAGS[sub] ?? [], rest);
|
|
109
119
|
const code = await run("bash", [s, ...rest]);
|
|
110
120
|
if (code !== 0 && sub === "install") {
|
|
111
121
|
// 失败即回滚(不给自己留 launchd 任务 / 进程 / 单元文件)。
|
|
@@ -116,6 +126,60 @@ if (cmd === "daemon") {
|
|
|
116
126
|
process.exit(code);
|
|
117
127
|
}
|
|
118
128
|
|
|
129
|
+
/**
|
|
130
|
+
* 定位监督面模块(**两种落位**都要认:源码态 .ts / 产物态 .js)。
|
|
131
|
+
*
|
|
132
|
+
* 产物态:cli 在 <pkg>/build/cli.mjs → 模块在 <pkg>/build/src/*.js;
|
|
133
|
+
* 源码态:cli 在 <pkg>/tools/cli.mjs → 模块在 <pkg>/src/*.ts。
|
|
134
|
+
*/
|
|
135
|
+
async function loadSupervisorModule(base) {
|
|
136
|
+
const candidates = [
|
|
137
|
+
path.join(here, "src", `${base}.js`),
|
|
138
|
+
path.join(here, "..", "src", `${base}.ts`),
|
|
139
|
+
];
|
|
140
|
+
for (const file of candidates) {
|
|
141
|
+
if (!fs.existsSync(file)) continue;
|
|
142
|
+
return await import(pathToFileURL(file).href);
|
|
143
|
+
}
|
|
144
|
+
throw new Error(`包内缺少监督面模块(${base})—— 请重新安装 pi-proactive`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** `pi-proactive web`:本地 Web 监督面(**只读**;只监听 127.0.0.1)。 */
|
|
148
|
+
if (cmd === "web") {
|
|
149
|
+
const rest = argv.slice(1);
|
|
150
|
+
checkFlags(WEB_FLAGS, rest);
|
|
151
|
+
const portIdx = rest.indexOf("--port");
|
|
152
|
+
let port = 7799;
|
|
153
|
+
if (portIdx >= 0) {
|
|
154
|
+
port = Number(rest[portIdx + 1]);
|
|
155
|
+
if (!Number.isInteger(port) || port < 0 || port > 65535) {
|
|
156
|
+
console.error(`--port 需要 0-65535 的整数(实际:${rest[portIdx + 1]})`);
|
|
157
|
+
process.exit(2);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const dirIdx = rest.indexOf("--agent-dir");
|
|
161
|
+
const agentDir = dirIdx >= 0 ? rest[dirIdx + 1] : undefined;
|
|
162
|
+
const stateMod = await loadSupervisorModule("supervisor-state");
|
|
163
|
+
const webMod = await loadSupervisorModule("supervisor-web");
|
|
164
|
+
// --json:非交互取数(不开服务)→ 监督信息在无 TUI 的环境里同样可取。
|
|
165
|
+
if (rest.includes("--json")) {
|
|
166
|
+
const state = stateMod.collectSupervisorState(agentDir !== undefined ? { agentDir } : {});
|
|
167
|
+
console.log(JSON.stringify(state, null, 2));
|
|
168
|
+
process.exit(0);
|
|
169
|
+
}
|
|
170
|
+
const handle = await webMod.startSupervisorWeb({ port, ...(agentDir !== undefined ? { agentDir } : {}) });
|
|
171
|
+
console.log(`[pi-proactive] 监督面已启动:${handle.url}`);
|
|
172
|
+
console.log("[pi-proactive] 只读(改配置请回 Pi 会话);只监听 127.0.0.1;Ctrl-C 退出");
|
|
173
|
+
const shutdown = async () => {
|
|
174
|
+
await handle.close();
|
|
175
|
+
process.exit(0);
|
|
176
|
+
};
|
|
177
|
+
process.on("SIGINT", () => void shutdown());
|
|
178
|
+
process.on("SIGTERM", () => void shutdown());
|
|
179
|
+
}
|
|
180
|
+
|
|
119
181
|
// 其余子命令暂由宿主命令面提供(保持"同一分派"口径);此处给出明确指引而非另写实现
|
|
120
|
-
|
|
121
|
-
|
|
182
|
+
else {
|
|
183
|
+
console.error(`「${cmd}」由 Pi 宿主内的 /proactive ${cmd} 提供;请在 Pi 会话中执行。`);
|
|
184
|
+
process.exit(2);
|
|
185
|
+
}
|