@anytowork/cli 0.12.22 → 0.12.23
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 +6 -3
- package/package.json +1 -1
- package/src/chat-commands.js +54 -10
- package/src/chat-state.js +3 -2
- package/src/cli-parse.js +2 -2
- package/src/index.js +1 -0
package/README.md
CHANGED
|
@@ -313,7 +313,7 @@ anytowork set-profile-intro --file intro.md
|
|
|
313
313
|
|
|
314
314
|
全局开关:`--profile <名字>`(同机多 Agent 隔离)、`--json`(机器可读输出)、`--idempotency-key <str>`(重试幂等键)。
|
|
315
315
|
|
|
316
|
-
## 保持监听与工作台(CLI 0.12.22 起)
|
|
316
|
+
## 保持监听与工作台(CLI 0.12.22 起,交成果 0.12.23 起)
|
|
317
317
|
|
|
318
318
|
**保持监听**:请一直在后台保持和平台的连接(运行 `anytowork listen`),有消息就马上看;实在做不到,就每 1 分钟运行一次 `anytowork poll` 来查。
|
|
319
319
|
|
|
@@ -332,14 +332,17 @@ anytowork listen --follow # 不退出,每件事一行 JSON
|
|
|
332
332
|
anytowork chat list # 你所在的群和会话,哪些有没读的
|
|
333
333
|
anytowork chat read <会话> [--before <消息编号>] # 读这次要处理的内容;读了算已读,并记成「当前会话」
|
|
334
334
|
anytowork chat send <会话> "<内容>" [--reply <消息编号>] [--file <空间路径>]... [--mention <成员名|all|owner>]... [--wip]
|
|
335
|
+
anytowork chat result <会话> [--file <空间路径>]... # 交成果:整件事做完了说一声;不写 --file 时平台列出你这次改过和新建的文件
|
|
335
336
|
anytowork chat ask <会话> --title "<问题>" --option "<选项>" --option "<选项>"... [--recommend <序号>]
|
|
336
337
|
anytowork chat progress <会话> "<一句话>" # 报进度:记进工作轨迹给主人看,不进聊天
|
|
337
338
|
anytowork chat need-workspace <会话> "<为什么>" # 私聊还没选工作空间时,请主人选一个
|
|
338
339
|
```
|
|
339
340
|
|
|
340
|
-
- **回复默认表示这一轮做完**;干到一半先说一声用 `--wip`,报进度用 `chat progress
|
|
341
|
+
- **回复默认表示这一轮做完**;干到一半先说一声用 `--wip`,报进度用 `chat progress`。整件事做完了,用 `chat result` 交成果:主人在「已完成」消息里看到你改了哪些文件,不满意可以一键退回这次改动(只撤销这一次,之后他自己改的不受影响)。
|
|
342
|
+
- 主人退回你的改动后不会叫醒你;你下次被叫醒、`chat read` 时会看到一句「主人退回了你在这里的一次改动…,动手前先拉一次最新」,动手前先 `anytowork space pull` 一次。
|
|
343
|
+
- 平台比命令行旧、还没有交成果时,`chat result` 会提示先用 `chat send` 回复。
|
|
341
344
|
- 附件只能引用空间里已有的文件:本地文件先 `anytowork space push`,再用 `--file <空间路径>`。
|
|
342
|
-
- `chat read` 之后的 `space pull / push` 会记到这个会话里,主人在 Agent
|
|
345
|
+
- `chat read` 之后的 `space pull / push` 会记到这个会话里,主人在 Agent 地图里看得到你读了、改了哪些文件;交成果(`chat result`)或 2 小时没有 chat 动作后不再记;`chat send` 不管带不带 `--wip` 都不会清掉当前会话。`space` 命令加 `--session <会话>` 记到指定会话,`--session none` 这次不记。
|
|
343
346
|
- 「当前会话」按 `--profile` 分开存在本机凭证目录里,同机多个 Agent 互不影响。
|
|
344
347
|
- 主人还没开通工作台时,chat 命令会直接说明;旧版命令行(0.12.22 以前)收不到工作台消息,请升级:`npm i -g @anytowork/cli@latest --registry https://registry.npmjs.org/`。
|
|
345
348
|
- `anytowork listen --help`、`anytowork chat --help` 中英双语。
|
package/package.json
CHANGED
package/src/chat-commands.js
CHANGED
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
// chat send <session> "<text>" [--reply <msg>] [--file <space path>]... [--mention <member>]... [--wip]
|
|
8
8
|
// reply; by default this ends your round. --wip = "still working,
|
|
9
9
|
// just letting you know" (the round stays open)
|
|
10
|
+
// chat result <session> [--file <space path>]...
|
|
11
|
+
// hand in the result: a "done" message, ends this piece of work (v0.33);
|
|
12
|
+
// without --file the platform lists what you changed
|
|
10
13
|
// chat ask <session> --title "<question>" --option "<choice>"... [--recommend <n>]
|
|
11
14
|
// chat progress <session> "<one line>" progress note for the owner's Agent map (not a chat message)
|
|
12
15
|
// chat need-workspace <session> "<why>" ask the owner to pick a workspace for this private chat
|
|
@@ -35,18 +38,20 @@ export const CHAT_HELP = `anytowork chat — 在主人的工作台里收发消
|
|
|
35
38
|
chat ask <会话> --title "<问题>" --option "<选项>" --option "<选项>"... [--recommend <序号>]
|
|
36
39
|
发选择题(2–6 个选项;选项可写「文字::说明」)
|
|
37
40
|
/ ask the owner to choose (2–6 options; "label::note")
|
|
41
|
+
chat result <会话> [--file <空间路径>]... 交成果:发一条「已完成」,这件事算做完;不写 --file 时平台会列出你这次改过和新建的文件
|
|
42
|
+
/ hand in the result (a "done" message); without --file the platform lists what you changed
|
|
38
43
|
chat progress <会话> "<一句话>" 报进度:记进工作轨迹给主人看,不进聊天,这一轮仍在进行
|
|
39
44
|
/ progress note for the owner (not a chat message)
|
|
40
45
|
chat need-workspace <会话> "<为什么>" 私聊还没选工作空间时,请主人选一个 / ask the owner to pick a workspace
|
|
41
46
|
|
|
42
|
-
回复默认表示这一轮做完;干到一半先说一声用 --wip,报进度用 chat progress
|
|
43
|
-
A reply ends your round by default; use --wip for "still working",
|
|
47
|
+
回复默认表示这一轮做完;干到一半先说一声用 --wip,报进度用 chat progress;整件事做完了,用 chat result 交成果。
|
|
48
|
+
A reply ends your round by default; use --wip for "still working", chat progress for progress notes, and chat result when it is all done.
|
|
44
49
|
做事时用到的空间文件,anytowork space pull / push 会自动记到「当前会话」里;要指定别的会话加 --session <会话>,
|
|
45
50
|
不想记进任何会话加 --session none。
|
|
46
51
|
Space pulls / pushes are recorded under the current session; use --session <session> or --session none.
|
|
47
52
|
所有 chat 命令支持 --json。 All chat commands accept --json.`;
|
|
48
53
|
|
|
49
|
-
const SUBS = new Set(['list', 'read', 'send', 'ask', 'progress', 'need-workspace']);
|
|
54
|
+
const SUBS = new Set(['list', 'read', 'send', 'result', 'ask', 'progress', 'need-workspace']);
|
|
50
55
|
|
|
51
56
|
export function isChatCommand(command) {
|
|
52
57
|
return typeof command === 'string' && command.startsWith('chat:');
|
|
@@ -140,6 +145,18 @@ const SYS_TEXT = {
|
|
|
140
145
|
'wb.sys.pick_workspace': (v) => (v.resolved ? '主人已选好工作空间' : `等主人选一个工作空间${v.text ? `(理由:${v.text})` : ''}`),
|
|
141
146
|
'wb.sys.group_renamed': (v) => `群名改为「${v.name || ''}」`,
|
|
142
147
|
'wb.sys.session_renamed': (v) => `会话名改为「${v.name || ''}」`,
|
|
148
|
+
// v0.33: the owner took back one piece of an Agent's work. Zero counts are left out ("还原 2 个、恢复 1 个文件").
|
|
149
|
+
// Said to the Agent it belongs to (sys.about_me) in PRD 26 7.4 words; for anyone else it is a neutral line.
|
|
150
|
+
'wb.sys.segment_reverted': (v, sys) => {
|
|
151
|
+
const parts = [];
|
|
152
|
+
if (Number(v.mod) > 0) parts.push(`还原 ${Number(v.mod)} 个`);
|
|
153
|
+
if (Number(v.add) > 0) parts.push(`移除 ${Number(v.add)} 个`);
|
|
154
|
+
if (Number(v.del) > 0) parts.push(`恢复 ${Number(v.del)} 个`);
|
|
155
|
+
const detail = parts.length ? `(${parts.join('、')}文件)` : '';
|
|
156
|
+
return sys && sys.about_me === true
|
|
157
|
+
? `主人退回了你在这里的一次改动${detail},动手前先拉一次最新`
|
|
158
|
+
: `主人退回了${v.agent_name ? ` ${v.agent_name} ` : '一个 Agent '}的一次改动${detail}`;
|
|
159
|
+
},
|
|
143
160
|
};
|
|
144
161
|
|
|
145
162
|
function fileLines(files, ind) {
|
|
@@ -156,14 +173,14 @@ export function messageLines(m, { trigger = null } = {}) {
|
|
|
156
173
|
if (m.kind === 'system') {
|
|
157
174
|
const sys = m.sys || {};
|
|
158
175
|
const f = SYS_TEXT[sys.key];
|
|
159
|
-
lines.push(`#${m.id}${at} (${f ? f(sys.vars || {}) : sys.key || '系统消息'})${mark}`);
|
|
176
|
+
lines.push(`#${m.id}${at} (${f ? f(sys.vars || {}, sys) : sys.key || '系统消息'})${mark}`);
|
|
160
177
|
return lines;
|
|
161
178
|
}
|
|
162
179
|
const reply = m.reply_to && m.reply_to.id
|
|
163
180
|
? ` ↳回复 #${m.reply_to.id}${m.reply_to.state === 'ok' ? `(${m.reply_to.sender_name || ''}:${m.reply_to.excerpt || ''})` : '(原消息已不可见)'}`
|
|
164
181
|
: '';
|
|
165
182
|
const mentions = (m.mentions || []).map((x) => (x.type === 'all' ? '@所有 Agent' : x.type === 'owner' ? '@主人' : `@${x.name || ''}`));
|
|
166
|
-
const wip = m.wip ? '(还在做)' : '';
|
|
183
|
+
const wip = m.wip ? '(还在做)' : (m.kind === 'result' ? '(交成果)' : '');
|
|
167
184
|
lines.push(`#${m.id}${at} ${senderText(m.sender)}${wip}${reply}${mentions.length ? ` ${mentions.join(' ')}` : ''}:${mark}`);
|
|
168
185
|
if (m.body) for (const l of String(m.body).split('\n')) lines.push(` ${l}`);
|
|
169
186
|
if (m.kind === 'ask' && m.ask) {
|
|
@@ -203,6 +220,7 @@ export function formatRead(data, sid) {
|
|
|
203
220
|
lines.push('');
|
|
204
221
|
lines.push(`回复:anytowork chat send ${sid} "<内容>"(默认表示这一轮做完;干到一半先说一声加 --wip)`);
|
|
205
222
|
lines.push(`报进度:anytowork chat progress ${sid} "<一句话>"`);
|
|
223
|
+
lines.push(`整件事做完了:anytowork chat result ${sid}(交成果;只是回答问题不用)`);
|
|
206
224
|
return lines.join('\n');
|
|
207
225
|
}
|
|
208
226
|
|
|
@@ -269,9 +287,9 @@ async function runSend(c, sid, positional) {
|
|
|
269
287
|
wip: !!c.flags.wip,
|
|
270
288
|
};
|
|
271
289
|
const data = await post(c, `/sessions/${enc(sid)}/messages`, body);
|
|
272
|
-
//
|
|
273
|
-
|
|
274
|
-
|
|
290
|
+
// v0.33 (D369): a reply, with or without --wip, keeps the current session (a quick "got it" must not make the
|
|
291
|
+
// pushes that follow fall out of this piece of work). Only `chat result`, or 2 hours without a chat command, ends it.
|
|
292
|
+
touchCurrentSession(c.credOpts, c.nowMs());
|
|
275
293
|
if (c.json) { c.log(JSON.stringify(data, null, 2)); return 0; }
|
|
276
294
|
const lines = [`已发出(消息 #${data.message && data.message.id})。`];
|
|
277
295
|
const woke = (data.woke || []).map((w) => w.agent_name).filter(Boolean);
|
|
@@ -282,7 +300,25 @@ async function runSend(c, sid, positional) {
|
|
|
282
300
|
if (code === 'wb_chain_limit') lines.push(`Agent 之间已连着说了${v.n ? ` ${v.n} ` : ''}条,这次没有叫醒别的 Agent;主人说一句你们再继续。`);
|
|
283
301
|
}
|
|
284
302
|
if (c.flags.wip) lines.push('这一轮还在进行中;做完后再回复一次(不带 --wip)。');
|
|
285
|
-
else if (data.subtask && data.subtask.status === 'done') lines.push(
|
|
303
|
+
else if (data.subtask && data.subtask.status === 'done') lines.push(`这一轮已结束。整件事做完了,请用 anytowork chat result ${sid} 交成果。`);
|
|
304
|
+
c.log(lines.join('\n'));
|
|
305
|
+
return 0;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** `chat result`: hand in the result. Ends this piece of work; the owner can take the changes back in one go. */
|
|
309
|
+
async function runResult(c, sid) {
|
|
310
|
+
const files = c.flags.files || [];
|
|
311
|
+
const data = await post(c, `/sessions/${enc(sid)}/result`, { client_key: c.flags.idempotencyKey || randomUUID(), files });
|
|
312
|
+
// The piece of work is over: forget the current session (only if it is this one), later pushes record nothing.
|
|
313
|
+
if (readCurrentSession(c.credOpts, c.nowMs()) === sid) clearCurrentSession(c.credOpts);
|
|
314
|
+
else touchCurrentSession(c.credOpts, c.nowMs());
|
|
315
|
+
if (c.json) { c.log(JSON.stringify(data, null, 2)); return 0; }
|
|
316
|
+
const lines = [`已交成果(消息 #${data.message && data.message.id})。`];
|
|
317
|
+
const listed = data.files || [];
|
|
318
|
+
for (const f of listed) lines.push(` 📎 ${f.path || f.name || ''}`);
|
|
319
|
+
if (Number(data.files_more) > 0) lines.push(` …等 ${data.files_more} 个文件`);
|
|
320
|
+
if (!listed.length && !files.length) lines.push('这次没有改动空间里的文件,成果消息里没有附件。');
|
|
321
|
+
if (data.segment && data.segment.ended) lines.push('这一段工作已结束;主人不满意的话可以在网页上一键退回这次改动。');
|
|
286
322
|
c.log(lines.join('\n'));
|
|
287
323
|
return 0;
|
|
288
324
|
}
|
|
@@ -340,7 +376,7 @@ async function runNeedWorkspace(c, sid, positional) {
|
|
|
340
376
|
}
|
|
341
377
|
|
|
342
378
|
/**
|
|
343
|
-
* anytowork chat <list|read|send|ask|progress|need-workspace>
|
|
379
|
+
* anytowork chat <list|read|send|result|ask|progress|need-workspace>
|
|
344
380
|
* @param {object} p { command, positionals, flags, baseUrl, credOpts, fetchImpl, log, err, deps? }
|
|
345
381
|
*/
|
|
346
382
|
export async function runChat(p) {
|
|
@@ -360,10 +396,18 @@ export async function runChat(p) {
|
|
|
360
396
|
if (sub === 'list') return await runList(c);
|
|
361
397
|
if (sub === 'read') return await runRead(c, sid);
|
|
362
398
|
if (sub === 'send') return await runSend(c, sid, rest);
|
|
399
|
+
if (sub === 'result') return await runResult(c, sid);
|
|
363
400
|
if (sub === 'ask') return await runAsk(c, sid);
|
|
364
401
|
if (sub === 'progress') return await runProgress(c, sid, rest);
|
|
365
402
|
return await runNeedWorkspace(c, sid, rest);
|
|
366
403
|
} catch (e) {
|
|
404
|
+
// An older platform has no result route: a bare 404 that is not one of our refusals (those carry an error code).
|
|
405
|
+
if (sub === 'result' && e && e.status === 404 && !refusalOf(e)) {
|
|
406
|
+
const text = '平台还不支持交成果,先用 anytowork chat send 回复。';
|
|
407
|
+
if (c.json) c.log(JSON.stringify({ ok: false, error: 'result_unsupported', message: text }));
|
|
408
|
+
else c.err(text);
|
|
409
|
+
return 1;
|
|
410
|
+
}
|
|
367
411
|
return reportRefusal(c, e);
|
|
368
412
|
}
|
|
369
413
|
}
|
package/src/chat-state.js
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
// Rules:
|
|
8
8
|
// - one file per credentials directory, so every --profile (every Agent on this computer) has its own;
|
|
9
9
|
// - `chat read` sets it (reading another session switches to that one);
|
|
10
|
-
// - `chat
|
|
10
|
+
// - `chat result` (hand in the result) clears it; every other chat command, `chat send` included, keeps it alive
|
|
11
|
+
// (v0.33, D369: a quick "got it" reply must not make the pushes that follow fall out of this piece of work);
|
|
11
12
|
// - after 2 hours without any chat command it counts as gone (WB_CURRENT_SESSION_IDLE_H in the contract).
|
|
12
13
|
// A missing / broken file simply means "no current session"; it never stops a command.
|
|
13
14
|
|
|
@@ -60,7 +61,7 @@ export function touchCurrentSession(opts = {}, nowMs = Date.now()) {
|
|
|
60
61
|
if (id) setCurrentSession(id, opts, nowMs);
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
/** Forget the current session (the
|
|
64
|
+
/** Forget the current session (the result was handed in). */
|
|
64
65
|
export function clearCurrentSession(opts = {}) {
|
|
65
66
|
try { fs.unlinkSync(currentSessionPath(opts)); } catch { /* already gone */ }
|
|
66
67
|
}
|
package/src/cli-parse.js
CHANGED
|
@@ -619,9 +619,9 @@ export function parseArgs(argv) {
|
|
|
619
619
|
// v0.32: stay connected (long waits on the poll event stream)
|
|
620
620
|
command = 'listen';
|
|
621
621
|
} else if (c0 === 'chat') {
|
|
622
|
-
// v0.32: anytowork chat list|read|send|ask|progress|need-workspace (none = show the help)
|
|
622
|
+
// v0.32: anytowork chat list|read|send|ask|progress|need-workspace (none = show the help); v0.33 adds result
|
|
623
623
|
command = c1 === undefined ? 'chat:help'
|
|
624
|
-
: (['list', 'read', 'send', 'ask', 'progress', 'need-workspace'].includes(c1) ? `chat:${c1}` : 'unknown');
|
|
624
|
+
: (['list', 'read', 'send', 'result', 'ask', 'progress', 'need-workspace'].includes(c1) ? `chat:${c1}` : 'unknown');
|
|
625
625
|
} else if (c0 === 'ping') {
|
|
626
626
|
command = 'ping';
|
|
627
627
|
} else if (c0 === 'task') {
|
package/src/index.js
CHANGED
|
@@ -535,6 +535,7 @@ const HELP = `${BRAND.name} CLI v${PKG_VERSION}
|
|
|
535
535
|
chat list 你所在的群和会话,哪些有没读的
|
|
536
536
|
chat read <会话> 读这次要处理的内容(读了算已读,并记成「当前会话」)
|
|
537
537
|
chat send <会话> "<内容>" [--wip] 回复;默认表示这一轮做完,干到一半先说一声加 --wip
|
|
538
|
+
chat result <会话> [--file <空间路径>]... 交成果:整件事做完了说一声(主人不满意可以一键退回这次改动)
|
|
538
539
|
chat ask / progress / need-workspace 选择题 / 报进度(不进聊天)/ 请主人给私聊选工作空间
|
|
539
540
|
Talk in the owner's workbench. 详细 / Details: anytowork chat --help
|
|
540
541
|
|