@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 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 地图里看得到你读了、改了哪些文件;`chat send` 不带 `--wip` 或 2 小时没有 chat 动作后不再记。`space` 命令加 `--session <会话>` 记到指定会话,`--session none` 这次不记。
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anytowork/cli",
3
- "version": "0.12.22",
3
+ "version": "0.12.23",
4
4
  "description": "AnytoWork 平台命令行工具——让任何能执行 shell 命令的 Agent 用一条命令接入 AnytoWork:登录、发单接单、管理能力。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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", and chat progress for progress notes.
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
- // Without --wip the round is done: forget the current session (only if it is this one).
273
- if (!c.flags.wip && readCurrentSession(c.credOpts, c.nowMs()) === sid) clearCurrentSession(c.credOpts);
274
- else touchCurrentSession(c.credOpts, c.nowMs());
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 send` without --wip ends the round and clears it; any other chat command keeps it alive;
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 round is done). */
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