shennian 0.2.89 → 0.2.90

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.
Files changed (118) hide show
  1. package/dist/assets/wechat-channel/macos/manifest.json +13 -4
  2. package/dist/assets/wechat-channel/macos/shennian-wechat-channel-helper +0 -0
  3. package/dist/bin/shennian.js +1 -1
  4. package/dist/publish-build-manifest.json +548 -0
  5. package/dist/scripts/wechat-rpa-confirmation.mjs +5 -97
  6. package/dist/src/agent-env.js +4 -105
  7. package/dist/src/agents/adapter.js +1 -19
  8. package/dist/src/agents/claude.js +8 -305
  9. package/dist/src/agents/codex-control.js +2 -188
  10. package/dist/src/agents/codex-utils.js +7 -200
  11. package/dist/src/agents/codex.js +15 -916
  12. package/dist/src/agents/command-spec.js +2 -413
  13. package/dist/src/agents/config-status.js +1 -226
  14. package/dist/src/agents/cursor.js +1 -249
  15. package/dist/src/agents/custom.js +4 -271
  16. package/dist/src/agents/detect.js +1 -56
  17. package/dist/src/agents/external-channel-instructions.js +10 -94
  18. package/dist/src/agents/gemini.js +1 -173
  19. package/dist/src/agents/manager.js +13 -157
  20. package/dist/src/agents/model-registry/cache.js +1 -37
  21. package/dist/src/agents/model-registry/discovery.js +2 -187
  22. package/dist/src/agents/model-registry/parsers.js +4 -447
  23. package/dist/src/agents/model-registry/runner.js +1 -30
  24. package/dist/src/agents/model-registry/service.js +1 -78
  25. package/dist/src/agents/model-registry/types.js +1 -8
  26. package/dist/src/agents/model-registry.js +1 -18
  27. package/dist/src/agents/openclaw.js +2 -275
  28. package/dist/src/agents/opencode.js +1 -231
  29. package/dist/src/agents/pi-context.js +12 -217
  30. package/dist/src/agents/pi.js +14 -723
  31. package/dist/src/agents/platform-instructions.js +9 -54
  32. package/dist/src/channels/base.js +1 -3
  33. package/dist/src/channels/registry.js +1 -30
  34. package/dist/src/channels/reply-split.js +10 -89
  35. package/dist/src/channels/runtime.js +5 -564
  36. package/dist/src/channels/secret-registry.js +1 -46
  37. package/dist/src/channels/websocket.js +8 -378
  38. package/dist/src/channels/wechat-channel/anchor.js +1 -65
  39. package/dist/src/channels/wechat-channel/client.js +1 -96
  40. package/dist/src/channels/wechat-channel/cooldown.js +1 -38
  41. package/dist/src/channels/wechat-channel/fingerprint.js +1 -71
  42. package/dist/src/channels/wechat-channel/helper-assets.d.ts +10 -1
  43. package/dist/src/channels/wechat-channel/helper-assets.js +1 -68
  44. package/dist/src/channels/wechat-channel/helper-client.js +3 -149
  45. package/dist/src/channels/wechat-channel/helper-protocol.d.ts +1 -1
  46. package/dist/src/channels/wechat-channel/helper-protocol.js +1 -115
  47. package/dist/src/channels/wechat-channel/index.d.ts +1 -0
  48. package/dist/src/channels/wechat-channel/index.js +1 -19
  49. package/dist/src/channels/wechat-channel/ledger.js +1 -54
  50. package/dist/src/channels/wechat-channel/media-resolver.js +1 -181
  51. package/dist/src/channels/wechat-channel/message-key.js +1 -105
  52. package/dist/src/channels/wechat-channel/observer.js +1 -118
  53. package/dist/src/channels/wechat-channel/outbound-ledger.d.ts +3 -0
  54. package/dist/src/channels/wechat-channel/outbound-ledger.js +2 -112
  55. package/dist/src/channels/wechat-channel/outbound-sender.d.ts +26 -0
  56. package/dist/src/channels/wechat-channel/outbound-sender.js +1 -0
  57. package/dist/src/channels/wechat-channel/preflight.js +1 -48
  58. package/dist/src/channels/wechat-channel/runner.js +1 -84
  59. package/dist/src/channels/wechat-channel/runtime.js +1 -66
  60. package/dist/src/channels/wechat-channel/scheduler.d.ts +5 -0
  61. package/dist/src/channels/wechat-channel/scheduler.js +1 -152
  62. package/dist/src/channels/wechat-rpa/macos-flow.js +1 -96
  63. package/dist/src/channels/wechat-rpa/macos.js +6 -48
  64. package/dist/src/channels/wechat-rpa/normalizer.js +7 -127
  65. package/dist/src/channels/wechat-rpa.js +6 -1028
  66. package/dist/src/channels/wecom.js +4 -357
  67. package/dist/src/commands/agent.js +6 -131
  68. package/dist/src/commands/daemon-windows.js +8 -48
  69. package/dist/src/commands/daemon.js +19 -1013
  70. package/dist/src/commands/external-attachments.js +1 -51
  71. package/dist/src/commands/external.js +1 -137
  72. package/dist/src/commands/manager.js +2 -391
  73. package/dist/src/commands/pair-qr.js +1 -6
  74. package/dist/src/commands/pair.js +9 -287
  75. package/dist/src/commands/tools.js +1 -34
  76. package/dist/src/commands/upgrade.js +1 -198
  77. package/dist/src/config/index.js +1 -35
  78. package/dist/src/daemon-log.js +6 -58
  79. package/dist/src/env-path.js +1 -64
  80. package/dist/src/fs/boundary.js +1 -126
  81. package/dist/src/fs/handler.js +1 -130
  82. package/dist/src/fs/security.js +1 -32
  83. package/dist/src/fs/text-decoder.js +1 -110
  84. package/dist/src/index.js +2 -404
  85. package/dist/src/log-reporter.js +1 -16
  86. package/dist/src/manager/prompt.js +29 -34
  87. package/dist/src/manager/registry.js +2 -269
  88. package/dist/src/manager/runtime.js +19 -1007
  89. package/dist/src/native-fusion/config.js +1 -5
  90. package/dist/src/native-fusion/opencode-parser.js +3 -123
  91. package/dist/src/native-fusion/parser-common.js +8 -264
  92. package/dist/src/native-fusion/parsers.js +8 -729
  93. package/dist/src/native-fusion/service.js +2 -225
  94. package/dist/src/native-fusion/state.js +1 -22
  95. package/dist/src/native-fusion/types.js +1 -1
  96. package/dist/src/region.js +1 -88
  97. package/dist/src/relay/client.js +1 -343
  98. package/dist/src/session/archive-zip.js +1 -220
  99. package/dist/src/session/handlers/agent-config.js +1 -150
  100. package/dist/src/session/handlers/agents.js +1 -55
  101. package/dist/src/session/handlers/chat.js +2 -751
  102. package/dist/src/session/handlers/control.js +1 -55
  103. package/dist/src/session/handlers/fs.js +1 -783
  104. package/dist/src/session/handlers/session-refresh.js +1 -47
  105. package/dist/src/session/handlers/skills.js +1 -121
  106. package/dist/src/session/handlers/title.js +1 -60
  107. package/dist/src/session/handlers/tool-detail.js +1 -218
  108. package/dist/src/session/manager.js +1 -319
  109. package/dist/src/session/projection.js +1 -54
  110. package/dist/src/session/queue.js +4 -317
  111. package/dist/src/session/remote-attachments.js +1 -72
  112. package/dist/src/session/store.js +3 -109
  113. package/dist/src/session/types.js +1 -4
  114. package/dist/src/skills/registry.js +15 -148
  115. package/dist/src/skills/setup.js +1 -101
  116. package/dist/src/tools/markdown-to-pdf.js +10 -346
  117. package/dist/src/upgrade/engine.js +3 -347
  118. package/package.json +3 -2
@@ -1,51 +1 @@
1
- // @arch docs/features/wecom-managed-channel.md
2
- // @test src/__tests__/external-command.test.ts
3
- import fs from 'node:fs';
4
- import path from 'node:path';
5
- const MIME_BY_EXT = {
6
- '.jpg': 'image/jpeg',
7
- '.jpeg': 'image/jpeg',
8
- '.png': 'image/png',
9
- '.gif': 'image/gif',
10
- '.webp': 'image/webp',
11
- '.mp4': 'video/mp4',
12
- '.mov': 'video/quicktime',
13
- '.pdf': 'application/pdf',
14
- '.txt': 'text/plain',
15
- '.md': 'text/markdown',
16
- '.csv': 'text/csv',
17
- '.doc': 'application/msword',
18
- '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
19
- '.xls': 'application/vnd.ms-excel',
20
- '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
21
- '.ppt': 'application/vnd.ms-powerpoint',
22
- '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
23
- '.zip': 'application/zip',
24
- };
25
- const MAX_EXTERNAL_ATTACHMENT_BYTES = Number(process.env.SHENNIAN_EXTERNAL_ATTACHMENT_MAX_BYTES || 20 * 1024 * 1024);
26
- function inferMimeType(filePath, kind) {
27
- const ext = path.extname(filePath).toLowerCase();
28
- if (MIME_BY_EXT[ext])
29
- return MIME_BY_EXT[ext];
30
- if (kind === 'image')
31
- return 'image/jpeg';
32
- if (kind === 'video')
33
- return 'video/mp4';
34
- return 'application/octet-stream';
35
- }
36
- export function readExternalAttachment(filePath, kind) {
37
- const absolutePath = path.resolve(filePath);
38
- const stat = fs.statSync(absolutePath);
39
- if (!stat.isFile())
40
- throw new Error(`Attachment is not a file: ${absolutePath}`);
41
- if (stat.size > MAX_EXTERNAL_ATTACHMENT_BYTES) {
42
- throw new Error(`Attachment is too large: ${stat.size} bytes. Max: ${MAX_EXTERNAL_ATTACHMENT_BYTES} bytes.`);
43
- }
44
- return {
45
- kind,
46
- name: path.basename(absolutePath),
47
- mimeType: inferMimeType(absolutePath, kind),
48
- size: stat.size,
49
- localPath: absolutePath,
50
- };
51
- }
1
+ import m from"node:fs";import n from"node:path";const p={".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png",".gif":"image/gif",".webp":"image/webp",".mp4":"video/mp4",".mov":"video/quicktime",".pdf":"application/pdf",".txt":"text/plain",".md":"text/markdown",".csv":"text/csv",".doc":"application/msword",".docx":"application/vnd.openxmlformats-officedocument.wordprocessingml.document",".xls":"application/vnd.ms-excel",".xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",".ppt":"application/vnd.ms-powerpoint",".pptx":"application/vnd.openxmlformats-officedocument.presentationml.presentation",".zip":"application/zip"},a=Number(process.env.SHENNIAN_EXTERNAL_ATTACHMENT_MAX_BYTES||20*1024*1024);function r(i,t){const e=n.extname(i).toLowerCase();return p[e]?p[e]:t==="image"?"image/jpeg":t==="video"?"video/mp4":"application/octet-stream"}function l(i,t){const e=n.resolve(i),o=m.statSync(e);if(!o.isFile())throw new Error(`Attachment is not a file: ${e}`);if(o.size>a)throw new Error(`Attachment is too large: ${o.size} bytes. Max: ${a} bytes.`);return{kind:t,name:n.basename(e),mimeType:r(e,t),size:o.size,localPath:e}}export{l as readExternalAttachment};
@@ -1,137 +1 @@
1
- // @arch docs/features/wecom-managed-channel.md
2
- // @test src/__tests__/external-command.test.ts
3
- import fs from 'node:fs';
4
- import chalk from 'chalk';
5
- import { resolveShennianPath } from '../config/index.js';
6
- import { readExternalAttachment } from './external-attachments.js';
7
- function loadManagerIpcFromRuntimeFile() {
8
- const filePath = resolveShennianPath('runtime', 'manager-ipc.json');
9
- try {
10
- const parsed = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
11
- const pid = Number(parsed.pid);
12
- if (Number.isInteger(pid) && pid > 0 && !processExists(pid)) {
13
- return { error: `Manager IPC runtime file is stale for PID ${pid}. Restart the desktop daemon and retry.` };
14
- }
15
- return {
16
- url: typeof parsed.url === 'string' ? parsed.url : undefined,
17
- token: typeof parsed.token === 'string' ? parsed.token : undefined,
18
- };
19
- }
20
- catch {
21
- return {};
22
- }
23
- }
24
- function requireExternalContext(explicitSessionId) {
25
- const runtimeIpc = !process.env.SHENNIAN_MANAGER_IPC_URL || !process.env.SHENNIAN_MANAGER_IPC_TOKEN
26
- ? loadManagerIpcFromRuntimeFile()
27
- : {};
28
- if (runtimeIpc.error) {
29
- console.error(chalk.red(`✗ ${runtimeIpc.error}`));
30
- process.exit(1);
31
- }
32
- const url = process.env.SHENNIAN_MANAGER_IPC_URL || runtimeIpc.url;
33
- const token = process.env.SHENNIAN_MANAGER_IPC_TOKEN || runtimeIpc.token;
34
- const sessionId = explicitSessionId?.trim() || process.env.SHENNIAN_EXTERNAL_SESSION_ID || process.env.SHENNIAN_MANAGER_SESSION_ID;
35
- if (!url || !token || !sessionId) {
36
- console.error(chalk.red('✗ This command must run inside a Shennian conversation with an external channel, or pass --session-id <id>.'));
37
- process.exit(1);
38
- }
39
- return { url, token, sessionId };
40
- }
41
- function processExists(pid) {
42
- try {
43
- process.kill(pid, 0);
44
- return true;
45
- }
46
- catch {
47
- return false;
48
- }
49
- }
50
- async function sendExternal(input) {
51
- const ctx = requireExternalContext(input.sessionId);
52
- const replyTarget = input.replyTarget?.trim() || process.env.SHENNIAN_EXTERNAL_REPLY_TARGET || undefined;
53
- const response = await fetch(`${ctx.url}/external/reply`, {
54
- method: 'POST',
55
- headers: {
56
- authorization: `Bearer ${ctx.token}`,
57
- 'content-type': 'application/json',
58
- 'x-shennian-manager-session-id': ctx.sessionId,
59
- },
60
- body: JSON.stringify({
61
- managerSessionId: ctx.sessionId,
62
- text: input.text,
63
- attachment: input.attachment,
64
- idempotencyKey: input.idempotencyKey,
65
- replyTarget,
66
- }),
67
- });
68
- const data = await response.json().catch(() => ({ ok: false, error: response.statusText }));
69
- if (!response.ok || !data.ok) {
70
- throw new Error(data.error || `External send failed: ${response.status}`);
71
- }
72
- console.log('ok');
73
- }
74
- export function registerExternalCommand(program) {
75
- const external = program.command('external').description('External channel tools for this Shennian conversation');
76
- external
77
- .command('send')
78
- .description('Send a message to the external channel bound to this conversation')
79
- .requiredOption('--text <text>', 'Message text')
80
- .option('--session-id <id>', 'Shennian conversation/session id; defaults to injected current-session env')
81
- .option('--reply-target <id>', 'Daemon-generated reply target; defaults to SHENNIAN_EXTERNAL_REPLY_TARGET or latest external message')
82
- .option('--idempotency-key <key>', 'Idempotency key')
83
- .action(async (opts) => {
84
- await sendExternal({ text: opts.text, replyTarget: opts.replyTarget, idempotencyKey: opts.idempotencyKey, sessionId: opts.sessionId });
85
- });
86
- external
87
- .command('send-image')
88
- .description('Send an image file to the external channel bound to this conversation')
89
- .requiredOption('--path <path>', 'Image file path')
90
- .option('--caption <text>', 'Optional text to send before the image')
91
- .option('--session-id <id>', 'Shennian conversation/session id; defaults to injected current-session env')
92
- .option('--reply-target <id>', 'Daemon-generated reply target; defaults to SHENNIAN_EXTERNAL_REPLY_TARGET or latest external message')
93
- .option('--idempotency-key <key>', 'Idempotency key')
94
- .action(async (opts) => {
95
- await sendExternal({
96
- text: opts.caption,
97
- attachment: readExternalAttachment(opts.path, 'image'),
98
- replyTarget: opts.replyTarget,
99
- idempotencyKey: opts.idempotencyKey,
100
- sessionId: opts.sessionId,
101
- });
102
- });
103
- external
104
- .command('send-video')
105
- .description('Send a video file to the external channel bound to this conversation')
106
- .requiredOption('--path <path>', 'Video file path')
107
- .option('--caption <text>', 'Optional text to send before the video')
108
- .option('--session-id <id>', 'Shennian conversation/session id; defaults to injected current-session env')
109
- .option('--reply-target <id>', 'Daemon-generated reply target; defaults to SHENNIAN_EXTERNAL_REPLY_TARGET or latest external message')
110
- .option('--idempotency-key <key>', 'Idempotency key')
111
- .action(async (opts) => {
112
- await sendExternal({
113
- text: opts.caption,
114
- attachment: readExternalAttachment(opts.path, 'video'),
115
- replyTarget: opts.replyTarget,
116
- idempotencyKey: opts.idempotencyKey,
117
- sessionId: opts.sessionId,
118
- });
119
- });
120
- external
121
- .command('send-file')
122
- .description('Send a file to the external channel bound to this conversation')
123
- .requiredOption('--path <path>', 'File path')
124
- .option('--caption <text>', 'Optional text to send before the file')
125
- .option('--session-id <id>', 'Shennian conversation/session id; defaults to injected current-session env')
126
- .option('--reply-target <id>', 'Daemon-generated reply target; defaults to SHENNIAN_EXTERNAL_REPLY_TARGET or latest external message')
127
- .option('--idempotency-key <key>', 'Idempotency key')
128
- .action(async (opts) => {
129
- await sendExternal({
130
- text: opts.caption,
131
- attachment: readExternalAttachment(opts.path, 'file'),
132
- replyTarget: opts.replyTarget,
133
- idempotencyKey: opts.idempotencyKey,
134
- sessionId: opts.sessionId,
135
- });
136
- });
137
- }
1
+ import d from"node:fs";import s from"chalk";import{resolveShennianPath as c}from"../config/index.js";import{readExternalAttachment as a}from"./external-attachments.js";function p(){const n=c("runtime","manager-ipc.json");try{const t=JSON.parse(d.readFileSync(n,"utf-8")),e=Number(t.pid);return Number.isInteger(e)&&e>0&&!m(e)?{error:`Manager IPC runtime file is stale for PID ${e}. Restart the desktop daemon and retry.`}:{url:typeof t.url=="string"?t.url:void 0,token:typeof t.token=="string"?t.token:void 0}}catch{return{}}}function l(n){const t=!process.env.SHENNIAN_MANAGER_IPC_URL||!process.env.SHENNIAN_MANAGER_IPC_TOKEN?p():{};t.error&&(console.error(s.red(`\u2717 ${t.error}`)),process.exit(1));const e=process.env.SHENNIAN_MANAGER_IPC_URL||t.url,o=process.env.SHENNIAN_MANAGER_IPC_TOKEN||t.token,i=n?.trim()||process.env.SHENNIAN_EXTERNAL_SESSION_ID||process.env.SHENNIAN_MANAGER_SESSION_ID;return(!e||!o||!i)&&(console.error(s.red("\u2717 This command must run inside a Shennian conversation with an external channel, or pass --session-id <id>.")),process.exit(1)),{url:e,token:o,sessionId:i}}function m(n){try{return process.kill(n,0),!0}catch{return!1}}async function r(n){const t=l(n.sessionId),e=n.replyTarget?.trim()||process.env.SHENNIAN_EXTERNAL_REPLY_TARGET||void 0,o=await fetch(`${t.url}/external/reply`,{method:"POST",headers:{authorization:`Bearer ${t.token}`,"content-type":"application/json","x-shennian-manager-session-id":t.sessionId},body:JSON.stringify({managerSessionId:t.sessionId,text:n.text,attachment:n.attachment,idempotencyKey:n.idempotencyKey,replyTarget:e})}),i=await o.json().catch(()=>({ok:!1,error:o.statusText}));if(!o.ok||!i.ok)throw new Error(i.error||`External send failed: ${o.status}`);console.log("ok")}function E(n){const t=n.command("external").description("External channel tools for this Shennian conversation");t.command("send").description("Send a message to the external channel bound to this conversation").requiredOption("--text <text>","Message text").option("--session-id <id>","Shennian conversation/session id; defaults to injected current-session env").option("--reply-target <id>","Daemon-generated reply target; defaults to SHENNIAN_EXTERNAL_REPLY_TARGET or latest external message").option("--idempotency-key <key>","Idempotency key").action(async e=>{await r({text:e.text,replyTarget:e.replyTarget,idempotencyKey:e.idempotencyKey,sessionId:e.sessionId})}),t.command("send-image").description("Send an image file to the external channel bound to this conversation").requiredOption("--path <path>","Image file path").option("--caption <text>","Optional text to send before the image").option("--session-id <id>","Shennian conversation/session id; defaults to injected current-session env").option("--reply-target <id>","Daemon-generated reply target; defaults to SHENNIAN_EXTERNAL_REPLY_TARGET or latest external message").option("--idempotency-key <key>","Idempotency key").action(async e=>{await r({text:e.caption,attachment:a(e.path,"image"),replyTarget:e.replyTarget,idempotencyKey:e.idempotencyKey,sessionId:e.sessionId})}),t.command("send-video").description("Send a video file to the external channel bound to this conversation").requiredOption("--path <path>","Video file path").option("--caption <text>","Optional text to send before the video").option("--session-id <id>","Shennian conversation/session id; defaults to injected current-session env").option("--reply-target <id>","Daemon-generated reply target; defaults to SHENNIAN_EXTERNAL_REPLY_TARGET or latest external message").option("--idempotency-key <key>","Idempotency key").action(async e=>{await r({text:e.caption,attachment:a(e.path,"video"),replyTarget:e.replyTarget,idempotencyKey:e.idempotencyKey,sessionId:e.sessionId})}),t.command("send-file").description("Send a file to the external channel bound to this conversation").requiredOption("--path <path>","File path").option("--caption <text>","Optional text to send before the file").option("--session-id <id>","Shennian conversation/session id; defaults to injected current-session env").option("--reply-target <id>","Daemon-generated reply target; defaults to SHENNIAN_EXTERNAL_REPLY_TARGET or latest external message").option("--idempotency-key <key>","Idempotency key").action(async e=>{await r({text:e.caption,attachment:a(e.path,"file"),replyTarget:e.replyTarget,idempotencyKey:e.idempotencyKey,sessionId:e.sessionId})})}export{E as registerExternalCommand};
@@ -1,391 +1,2 @@
1
- // @arch docs/features/manager-agent.md
2
- // @test src/__tests__/manager-runtime.test.ts
3
- // @test src/__tests__/manager-command.test.ts
4
- import fs from 'node:fs';
5
- import chalk from 'chalk';
6
- import { readExternalAttachment } from './external-attachments.js';
7
- function requireManagerContext() {
8
- const url = process.env.SHENNIAN_MANAGER_IPC_URL;
9
- const token = process.env.SHENNIAN_MANAGER_IPC_TOKEN;
10
- const managerSessionId = process.env.SHENNIAN_MANAGER_SESSION_ID;
11
- if (!url || !token || !managerSessionId) {
12
- console.error(chalk.red('✗ This command must run inside a Shennian Manager Agent session.'));
13
- process.exit(1);
14
- }
15
- return { url, token, managerSessionId };
16
- }
17
- async function ipc(path, body) {
18
- const ctx = requireManagerContext();
19
- const response = await fetch(`${ctx.url}${path}`, {
20
- method: 'POST',
21
- headers: {
22
- authorization: `Bearer ${ctx.token}`,
23
- 'content-type': 'application/json',
24
- 'x-shennian-manager-session-id': ctx.managerSessionId,
25
- },
26
- body: JSON.stringify({ ...body, managerSessionId: ctx.managerSessionId }),
27
- });
28
- const data = await response.json().catch(() => ({ ok: false, error: response.statusText }));
29
- if (!response.ok || !data.ok) {
30
- throw new Error(data.error || `Manager IPC failed: ${response.status}`);
31
- }
32
- return data;
33
- }
34
- function printJson(data) {
35
- console.log(JSON.stringify(data, null, 2));
36
- }
37
- function readMessage(opts) {
38
- if (opts.messageFile)
39
- return fs.readFileSync(opts.messageFile, 'utf-8');
40
- return opts.message ?? '';
41
- }
42
- function collect(value, previous = []) {
43
- previous.push(value);
44
- return previous;
45
- }
46
- function parseBool(value) {
47
- if (value === undefined)
48
- return undefined;
49
- return value === 'true';
50
- }
51
- function parseNumber(value) {
52
- const number = Number(value);
53
- return Number.isFinite(number) ? number : undefined;
54
- }
55
- function printWeChatRpaStatus(channel) {
56
- if (!channel) {
57
- console.log('not configured');
58
- return;
59
- }
60
- const groups = Array.isArray(channel.wechatRpaGroups)
61
- ? channel.wechatRpaGroups
62
- .map((group) => typeof group === 'object' && group ? String(group.name || '').trim() : '')
63
- .filter(Boolean)
64
- .join(', ')
65
- : '';
66
- const fields = [
67
- `enabled=${Boolean(channel.enabled)}`,
68
- `connected=${Boolean(channel.tokenConfigured ?? channel.enabled)}`,
69
- channel.wechatRpaSource ? `source=${String(channel.wechatRpaSource)}` : '',
70
- groups ? `groups=${groups}` : 'groups=',
71
- channel.pollIntervalMs ? `poll=${String(channel.pollIntervalMs)}ms` : '',
72
- channel.wechatRpaRuntimeState ? `state=${String(channel.wechatRpaRuntimeState)}` : '',
73
- Number.isFinite(channel.wechatRpaPendingReplyCount) ? `pendingReplies=${String(channel.wechatRpaPendingReplyCount)}` : '',
74
- channel.wechatRpaLastRunAt ? `lastRun=${String(channel.wechatRpaLastRunAt)}` : '',
75
- channel.wechatRpaLastMessageAt ? `lastMessage=${String(channel.wechatRpaLastMessageAt)}` : '',
76
- channel.wechatRpaLastInterruptedAt ? `lastInterrupted=${String(channel.wechatRpaLastInterruptedAt)}` : '',
77
- channel.wechatRpaLastError ? `lastError=${String(channel.wechatRpaLastError)}` : 'lastError=none',
78
- ].filter(Boolean);
79
- console.log(fields.join('\n'));
80
- }
81
- export function registerManagerCommand(program) {
82
- const manager = program.command('manager').description('Manager Agent local tools');
83
- const sessions = manager.command('sessions').description('Manage same-project worker sessions');
84
- sessions
85
- .command('list')
86
- .description('List same-project sessions visible to this Manager')
87
- .option('--json', 'Print JSON')
88
- .action(async (opts) => {
89
- const result = await ipc('/sessions/list', {});
90
- if (opts.json)
91
- printJson(result);
92
- else {
93
- const sessions = result.sessions ?? [];
94
- for (const session of sessions) {
95
- console.log(`${session.sessionId}\t${session.agentType}\t${session.role ?? ''}\t${session.status}\t${session.title ?? session.summary ?? ''}`);
96
- }
97
- }
98
- });
99
- sessions
100
- .command('start')
101
- .description('Start a worker session in the same project')
102
- .requiredOption('--agent <agent>', 'Worker agent type, e.g. codex, claude, gemini, cursor, opencode, pi, or custom:<name>')
103
- .requiredOption('--workdir <path>', 'Worker workdir; must match Manager workdir')
104
- .option('--model <model>', 'Worker model id')
105
- .option('--message <text>', 'Worker prompt')
106
- .option('--message-file <path>', 'Read worker prompt from file')
107
- .option('--json', 'Print JSON')
108
- .action(async (opts) => {
109
- const result = await ipc('/sessions/start', {
110
- agentType: opts.agent,
111
- workDir: opts.workdir,
112
- modelId: opts.model,
113
- message: readMessage(opts),
114
- });
115
- if (opts.json)
116
- printJson(result);
117
- else
118
- console.log(result.session?.sessionId ?? 'ok');
119
- });
120
- sessions
121
- .command('send')
122
- .description('Queue a message for a managed worker; runs immediately if idle')
123
- .requiredOption('--session-id <id>', 'Worker session id')
124
- .option('--message <text>', 'Message text')
125
- .option('--message-file <path>', 'Read message from file')
126
- .option('--direct', 'Bypass queue and send immediately')
127
- .action(async (opts) => {
128
- await ipc('/sessions/send', {
129
- sessionId: opts.sessionId,
130
- message: readMessage(opts),
131
- enqueue: !opts.direct,
132
- });
133
- console.log('ok');
134
- });
135
- const queue = sessions.command('queue').description('Inspect and edit pending worker messages');
136
- queue
137
- .command('list')
138
- .description('List pending messages for a managed worker')
139
- .requiredOption('--session-id <id>', 'Worker session id')
140
- .option('--json', 'Print JSON')
141
- .action(async (opts) => {
142
- const result = await ipc('/sessions/queue', { sessionId: opts.sessionId });
143
- if (opts.json)
144
- printJson(result);
145
- else {
146
- const pending = result.queue?.pending ?? [];
147
- for (const message of pending)
148
- console.log(`${message.id}\t${message.text.replace(/\s+/g, ' ').slice(0, 120)}`);
149
- }
150
- });
151
- queue
152
- .command('edit')
153
- .description('Edit a pending worker message')
154
- .requiredOption('--session-id <id>', 'Worker session id')
155
- .requiredOption('--message-id <id>', 'Queued message id')
156
- .option('--message <text>', 'Replacement message text')
157
- .option('--message-file <path>', 'Read replacement message from file')
158
- .action(async (opts) => {
159
- await ipc('/sessions/queue/edit', {
160
- sessionId: opts.sessionId,
161
- queueMessageId: opts.messageId,
162
- message: readMessage(opts),
163
- });
164
- console.log('ok');
165
- });
166
- queue
167
- .command('delete')
168
- .description('Delete a pending worker message')
169
- .requiredOption('--session-id <id>', 'Worker session id')
170
- .requiredOption('--message-id <id>', 'Queued message id')
171
- .action(async (opts) => {
172
- await ipc('/sessions/queue/delete', {
173
- sessionId: opts.sessionId,
174
- queueMessageId: opts.messageId,
175
- });
176
- console.log('ok');
177
- });
178
- const stopWorker = async (opts) => {
179
- await ipc('/sessions/stop', { sessionId: opts.sessionId });
180
- console.log('ok');
181
- };
182
- sessions
183
- .command('stop')
184
- .aliases(['terminate', 'kill'])
185
- .description('Stop or terminate a running managed worker')
186
- .requiredOption('--session-id <id>', 'Worker session id')
187
- .action(stopWorker);
188
- sessions
189
- .command('read')
190
- .description('Read a managed worker transcript')
191
- .requiredOption('--session-id <id>', 'Worker session id')
192
- .option('--limit <n>', 'Message limit', '200')
193
- .option('--json', 'Print JSON')
194
- .action(async (opts) => {
195
- const result = await ipc('/sessions/read', { sessionId: opts.sessionId, limit: Number(opts.limit) });
196
- if (opts.json)
197
- printJson(result);
198
- else
199
- printJson(result.messages ?? []);
200
- });
201
- manager
202
- .command('memory')
203
- .description('Manager project memory helpers')
204
- .command('path')
205
- .description('Print project memory directory path')
206
- .action(async () => {
207
- const result = await ipc('/memory/path', {});
208
- console.log(result.path);
209
- });
210
- const external = manager.command('external').description('External channel tools');
211
- const sendExternal = async (opts) => {
212
- await ipc('/external/reply', opts);
213
- console.log('ok');
214
- };
215
- external
216
- .command('send')
217
- .description('Send a message to the Manager-bound external channel')
218
- .requiredOption('--text <text>', 'Message text')
219
- .option('--reply-target <id>', 'Daemon-generated reply target; omitted means latest external message for this Manager')
220
- .option('--idempotency-key <key>', 'Idempotency key')
221
- .action(sendExternal);
222
- external
223
- .command('send-image')
224
- .description('Send an image file to the Manager-bound external channel')
225
- .requiredOption('--path <path>', 'Image file path')
226
- .option('--caption <text>', 'Optional text to send before the image')
227
- .option('--reply-target <id>', 'Daemon-generated reply target; omitted means latest external message for this Manager')
228
- .option('--idempotency-key <key>', 'Idempotency key')
229
- .action(async (opts) => {
230
- await sendExternal({
231
- text: opts.caption,
232
- attachment: readExternalAttachment(opts.path, 'image'),
233
- replyTarget: opts.replyTarget,
234
- idempotencyKey: opts.idempotencyKey,
235
- });
236
- });
237
- external
238
- .command('send-video')
239
- .description('Send a video file to the Manager-bound external channel')
240
- .requiredOption('--path <path>', 'Video file path')
241
- .option('--caption <text>', 'Optional text to send before the video')
242
- .option('--reply-target <id>', 'Daemon-generated reply target; omitted means latest external message for this Manager')
243
- .option('--idempotency-key <key>', 'Idempotency key')
244
- .action(async (opts) => {
245
- await sendExternal({
246
- text: opts.caption,
247
- attachment: readExternalAttachment(opts.path, 'video'),
248
- replyTarget: opts.replyTarget,
249
- idempotencyKey: opts.idempotencyKey,
250
- });
251
- });
252
- external
253
- .command('send-file')
254
- .description('Send a file to the Manager-bound external channel')
255
- .requiredOption('--path <path>', 'File path')
256
- .option('--caption <text>', 'Optional text to send before the file')
257
- .option('--reply-target <id>', 'Daemon-generated reply target; omitted means latest external message for this Manager')
258
- .option('--idempotency-key <key>', 'Idempotency key')
259
- .action(async (opts) => {
260
- await sendExternal({
261
- text: opts.caption,
262
- attachment: readExternalAttachment(opts.path, 'file'),
263
- replyTarget: opts.replyTarget,
264
- idempotencyKey: opts.idempotencyKey,
265
- });
266
- });
267
- external
268
- .command('reply')
269
- .description('Send an explicit reply to an external channel')
270
- .option('--reply-target <id>', 'Daemon-generated reply target; omitted means latest external message for this Manager')
271
- .option('--channel-id <id>', 'Channel id')
272
- .option('--conversation-id <id>', 'Conversation id')
273
- .requiredOption('--text <text>', 'Reply text')
274
- .option('--idempotency-key <key>', 'Idempotency key')
275
- .action(sendExternal);
276
- const channel = manager.command('channel').description('Manager external channel config');
277
- channel
278
- .command('get')
279
- .description('Get bound external channel config')
280
- .option('--json', 'Print JSON')
281
- .action(async (opts) => {
282
- const result = await ipc('/channel/get', {});
283
- if (opts.json)
284
- printJson(result);
285
- else
286
- printJson(result.channel ?? null);
287
- });
288
- channel
289
- .command('upsert')
290
- .description('Create or update an external channel binding')
291
- .option('--id <id>', 'Channel id')
292
- .option('--name <name>', 'Channel display name')
293
- .requiredOption('--enabled <true|false>', 'Whether the channel should be enabled')
294
- .option('--ws-url <url>', 'External websocket URL')
295
- .option('--token <token>', 'Bearer token')
296
- .option('--can-reply <true|false>', 'Whether reply should be allowed')
297
- .option('--json', 'Print JSON')
298
- .action(async (opts) => {
299
- const result = await ipc('/channel/upsert', {
300
- id: opts.id,
301
- name: opts.name,
302
- enabled: opts.enabled === 'true',
303
- wsUrl: opts.wsUrl,
304
- token: opts.token,
305
- canReply: opts.canReply === undefined ? undefined : opts.canReply === 'true',
306
- });
307
- if (opts.json)
308
- printJson(result);
309
- else
310
- printJson(result.channel ?? null);
311
- });
312
- const wechatRpa = manager.command('wechat-rpa').description('Local WeChat RPA channel config');
313
- wechatRpa
314
- .command('get')
315
- .description('Get local WeChat RPA channel config')
316
- .option('--json', 'Print JSON')
317
- .action(async (opts) => {
318
- const result = await ipc('/wechat-rpa/channel/get', {});
319
- if (opts.json)
320
- printJson(result);
321
- else
322
- printJson(result.channel ?? null);
323
- });
324
- wechatRpa
325
- .command('status')
326
- .description('Print local WeChat RPA channel runtime status')
327
- .option('--json', 'Print JSON')
328
- .action(async (opts) => {
329
- const result = await ipc('/wechat-rpa/channel/get', {});
330
- if (opts.json)
331
- printJson(result);
332
- else
333
- printWeChatRpaStatus((result.channel ?? null));
334
- });
335
- wechatRpa
336
- .command('sync')
337
- .description('Run one immediate WeChat RPA sync and print the updated runtime status')
338
- .option('--json', 'Print JSON')
339
- .action(async (opts) => {
340
- const result = await ipc('/wechat-rpa/channel/sync', {});
341
- if (opts.json)
342
- printJson(result);
343
- else
344
- printWeChatRpaStatus((result.channel ?? null));
345
- });
346
- wechatRpa
347
- .command('upsert')
348
- .description('Create or update a local WeChat RPA channel binding')
349
- .option('--id <id>', 'Channel id')
350
- .option('--name <name>', 'Channel display name')
351
- .requiredOption('--enabled <true|false>', 'Whether the channel should be enabled')
352
- .option('--group <name>', 'Bound WeChat conversation name; pass once per Shennian conversation', collect, [])
353
- .option('--can-reply <true|false>', 'Whether reply should be allowed')
354
- .option('--source <macos-flow|wechat-rpa-lab|macos-probe|fixture-jsonl>', 'WeChat RPA implementation source')
355
- .option('--poll-interval-ms <n>', 'Polling interval in milliseconds')
356
- .option('--recent-limit <n>', 'Recent message limit')
357
- .option('--idle-seconds <n>', 'Minimum user idle seconds before foreground automation')
358
- .option('--force-foreground <true|false>', 'Force WeChat foreground while syncing')
359
- .option('--restore-previous <true|false>', 'Restore previous foreground app after syncing')
360
- .option('--download-attachments <true|false>', 'Click and localize inbound attachment candidates')
361
- .option('--download-attachments-dir <path>', 'Directory for localized inbound WeChat attachments')
362
- .option('--privacy-consent <true|false>', 'Confirm WeChat channel data and privacy consent')
363
- .option('--flow-script-path <path>', 'Override macOS flow script path')
364
- .option('--json', 'Print JSON')
365
- .action(async (opts) => {
366
- if (opts.enabled === 'true' && opts.group.length > 1) {
367
- throw new Error('WeChat RPA 每个对话只能绑定一个群');
368
- }
369
- const result = await ipc('/wechat-rpa/channel/upsert', {
370
- id: opts.id,
371
- name: opts.name,
372
- enabled: opts.enabled === 'true',
373
- groups: opts.group.map((name) => ({ name })),
374
- canReply: parseBool(opts.canReply),
375
- source: opts.source,
376
- pollIntervalMs: parseNumber(opts.pollIntervalMs),
377
- recentLimit: parseNumber(opts.recentLimit),
378
- idleSeconds: parseNumber(opts.idleSeconds),
379
- forceForeground: parseBool(opts.forceForeground),
380
- noRestore: opts.restorePrevious === undefined ? undefined : opts.restorePrevious !== 'true',
381
- downloadAttachments: parseBool(opts.downloadAttachments),
382
- downloadAttachmentsDir: opts.downloadAttachmentsDir,
383
- privacyConsentAccepted: parseBool(opts.privacyConsent),
384
- flowScriptPath: opts.flowScriptPath,
385
- });
386
- if (opts.json)
387
- printJson(result);
388
- else
389
- printJson(result.channel ?? null);
390
- });
391
- }
1
+ import R from"node:fs";import S from"chalk";import{readExternalAttachment as h}from"./external-attachments.js";function x(){const n=process.env.SHENNIAN_MANAGER_IPC_URL,o=process.env.SHENNIAN_MANAGER_IPC_TOKEN,a=process.env.SHENNIAN_MANAGER_SESSION_ID;return(!n||!o||!a)&&(console.error(S.red("\u2717 This command must run inside a Shennian Manager Agent session.")),process.exit(1)),{url:n,token:o,managerSessionId:a}}async function i(n,o){const a=x(),r=await fetch(`${a.url}${n}`,{method:"POST",headers:{authorization:`Bearer ${a.token}`,"content-type":"application/json","x-shennian-manager-session-id":a.managerSessionId},body:JSON.stringify({...o,managerSessionId:a.managerSessionId})}),c=await r.json().catch(()=>({ok:!1,error:r.statusText}));if(!r.ok||!c.ok)throw new Error(c.error||`Manager IPC failed: ${r.status}`);return c}function s(n){console.log(JSON.stringify(n,null,2))}function y(n){return n.messageFile?R.readFileSync(n.messageFile,"utf-8"):n.message??""}function I(n,o=[]){return o.push(n),o}function g(n){if(n!==void 0)return n==="true"}function f(n){const o=Number(n);return Number.isFinite(o)?o:void 0}function k(n){if(!n){console.log("not configured");return}const o=Array.isArray(n.wechatRpaGroups)?n.wechatRpaGroups.map(r=>typeof r=="object"&&r?String(r.name||"").trim():"").filter(Boolean).join(", "):"",a=[`enabled=${!!n.enabled}`,`connected=${!!(n.tokenConfigured??n.enabled)}`,n.wechatRpaSource?`source=${String(n.wechatRpaSource)}`:"",o?`groups=${o}`:"groups=",n.pollIntervalMs?`poll=${String(n.pollIntervalMs)}ms`:"",n.wechatRpaRuntimeState?`state=${String(n.wechatRpaRuntimeState)}`:"",Number.isFinite(n.wechatRpaPendingReplyCount)?`pendingReplies=${String(n.wechatRpaPendingReplyCount)}`:"",n.wechatRpaLastRunAt?`lastRun=${String(n.wechatRpaLastRunAt)}`:"",n.wechatRpaLastMessageAt?`lastMessage=${String(n.wechatRpaLastMessageAt)}`:"",n.wechatRpaLastInterruptedAt?`lastInterrupted=${String(n.wechatRpaLastInterruptedAt)}`:"",n.wechatRpaLastError?`lastError=${String(n.wechatRpaLastError)}`:"lastError=none"].filter(Boolean);console.log(a.join(`
2
+ `))}function A(n){const o=n.command("manager").description("Manager Agent local tools"),a=o.command("sessions").description("Manage same-project worker sessions");a.command("list").description("List same-project sessions visible to this Manager").option("--json","Print JSON").action(async e=>{const t=await i("/sessions/list",{});if(e.json)s(t);else{const m=t.sessions??[];for(const d of m)console.log(`${d.sessionId} ${d.agentType} ${d.role??""} ${d.status} ${d.title??d.summary??""}`)}}),a.command("start").description("Start a worker session in the same project").requiredOption("--agent <agent>","Worker agent type, e.g. codex, claude, gemini, cursor, opencode, pi, or custom:<name>").requiredOption("--workdir <path>","Worker workdir; must match Manager workdir").option("--model <model>","Worker model id").option("--message <text>","Worker prompt").option("--message-file <path>","Read worker prompt from file").option("--json","Print JSON").action(async e=>{const t=await i("/sessions/start",{agentType:e.agent,workDir:e.workdir,modelId:e.model,message:y(e)});e.json?s(t):console.log(t.session?.sessionId??"ok")}),a.command("send").description("Queue a message for a managed worker; runs immediately if idle").requiredOption("--session-id <id>","Worker session id").option("--message <text>","Message text").option("--message-file <path>","Read message from file").option("--direct","Bypass queue and send immediately").action(async e=>{await i("/sessions/send",{sessionId:e.sessionId,message:y(e),enqueue:!e.direct}),console.log("ok")});const r=a.command("queue").description("Inspect and edit pending worker messages");r.command("list").description("List pending messages for a managed worker").requiredOption("--session-id <id>","Worker session id").option("--json","Print JSON").action(async e=>{const t=await i("/sessions/queue",{sessionId:e.sessionId});if(e.json)s(t);else{const m=t.queue?.pending??[];for(const d of m)console.log(`${d.id} ${d.text.replace(/\s+/g," ").slice(0,120)}`)}}),r.command("edit").description("Edit a pending worker message").requiredOption("--session-id <id>","Worker session id").requiredOption("--message-id <id>","Queued message id").option("--message <text>","Replacement message text").option("--message-file <path>","Read replacement message from file").action(async e=>{await i("/sessions/queue/edit",{sessionId:e.sessionId,queueMessageId:e.messageId,message:y(e)}),console.log("ok")}),r.command("delete").description("Delete a pending worker message").requiredOption("--session-id <id>","Worker session id").requiredOption("--message-id <id>","Queued message id").action(async e=>{await i("/sessions/queue/delete",{sessionId:e.sessionId,queueMessageId:e.messageId}),console.log("ok")});const c=async e=>{await i("/sessions/stop",{sessionId:e.sessionId}),console.log("ok")};a.command("stop").aliases(["terminate","kill"]).description("Stop or terminate a running managed worker").requiredOption("--session-id <id>","Worker session id").action(c),a.command("read").description("Read a managed worker transcript").requiredOption("--session-id <id>","Worker session id").option("--limit <n>","Message limit","200").option("--json","Print JSON").action(async e=>{const t=await i("/sessions/read",{sessionId:e.sessionId,limit:Number(e.limit)});e.json?s(t):s(t.messages??[])}),o.command("memory").description("Manager project memory helpers").command("path").description("Print project memory directory path").action(async()=>{const e=await i("/memory/path",{});console.log(e.path)});const l=o.command("external").description("External channel tools"),p=async e=>{await i("/external/reply",e),console.log("ok")};l.command("send").description("Send a message to the Manager-bound external channel").requiredOption("--text <text>","Message text").option("--reply-target <id>","Daemon-generated reply target; omitted means latest external message for this Manager").option("--idempotency-key <key>","Idempotency key").action(p),l.command("send-image").description("Send an image file to the Manager-bound external channel").requiredOption("--path <path>","Image file path").option("--caption <text>","Optional text to send before the image").option("--reply-target <id>","Daemon-generated reply target; omitted means latest external message for this Manager").option("--idempotency-key <key>","Idempotency key").action(async e=>{await p({text:e.caption,attachment:h(e.path,"image"),replyTarget:e.replyTarget,idempotencyKey:e.idempotencyKey})}),l.command("send-video").description("Send a video file to the Manager-bound external channel").requiredOption("--path <path>","Video file path").option("--caption <text>","Optional text to send before the video").option("--reply-target <id>","Daemon-generated reply target; omitted means latest external message for this Manager").option("--idempotency-key <key>","Idempotency key").action(async e=>{await p({text:e.caption,attachment:h(e.path,"video"),replyTarget:e.replyTarget,idempotencyKey:e.idempotencyKey})}),l.command("send-file").description("Send a file to the Manager-bound external channel").requiredOption("--path <path>","File path").option("--caption <text>","Optional text to send before the file").option("--reply-target <id>","Daemon-generated reply target; omitted means latest external message for this Manager").option("--idempotency-key <key>","Idempotency key").action(async e=>{await p({text:e.caption,attachment:h(e.path,"file"),replyTarget:e.replyTarget,idempotencyKey:e.idempotencyKey})}),l.command("reply").description("Send an explicit reply to an external channel").option("--reply-target <id>","Daemon-generated reply target; omitted means latest external message for this Manager").option("--channel-id <id>","Channel id").option("--conversation-id <id>","Conversation id").requiredOption("--text <text>","Reply text").option("--idempotency-key <key>","Idempotency key").action(p);const w=o.command("channel").description("Manager external channel config");w.command("get").description("Get bound external channel config").option("--json","Print JSON").action(async e=>{const t=await i("/channel/get",{});e.json?s(t):s(t.channel??null)}),w.command("upsert").description("Create or update an external channel binding").option("--id <id>","Channel id").option("--name <name>","Channel display name").requiredOption("--enabled <true|false>","Whether the channel should be enabled").option("--ws-url <url>","External websocket URL").option("--token <token>","Bearer token").option("--can-reply <true|false>","Whether reply should be allowed").option("--json","Print JSON").action(async e=>{const t=await i("/channel/upsert",{id:e.id,name:e.name,enabled:e.enabled==="true",wsUrl:e.wsUrl,token:e.token,canReply:e.canReply===void 0?void 0:e.canReply==="true"});e.json?s(t):s(t.channel??null)});const u=o.command("wechat-rpa").description("Local WeChat RPA channel config");u.command("get").description("Get local WeChat RPA channel config").option("--json","Print JSON").action(async e=>{const t=await i("/wechat-rpa/channel/get",{});e.json?s(t):s(t.channel??null)}),u.command("status").description("Print local WeChat RPA channel runtime status").option("--json","Print JSON").action(async e=>{const t=await i("/wechat-rpa/channel/get",{});e.json?s(t):k(t.channel??null)}),u.command("sync").description("Run one immediate WeChat RPA sync and print the updated runtime status").option("--json","Print JSON").action(async e=>{const t=await i("/wechat-rpa/channel/sync",{});e.json?s(t):k(t.channel??null)}),u.command("upsert").description("Create or update a local WeChat RPA channel binding").option("--id <id>","Channel id").option("--name <name>","Channel display name").requiredOption("--enabled <true|false>","Whether the channel should be enabled").option("--group <name>","Bound WeChat conversation name; pass once per Shennian conversation",I,[]).option("--can-reply <true|false>","Whether reply should be allowed").option("--source <macos-flow|wechat-rpa-lab|macos-probe|fixture-jsonl>","WeChat RPA implementation source").option("--poll-interval-ms <n>","Polling interval in milliseconds").option("--recent-limit <n>","Recent message limit").option("--idle-seconds <n>","Minimum user idle seconds before foreground automation").option("--force-foreground <true|false>","Force WeChat foreground while syncing").option("--restore-previous <true|false>","Restore previous foreground app after syncing").option("--download-attachments <true|false>","Click and localize inbound attachment candidates").option("--download-attachments-dir <path>","Directory for localized inbound WeChat attachments").option("--privacy-consent <true|false>","Confirm WeChat channel data and privacy consent").option("--flow-script-path <path>","Override macOS flow script path").option("--json","Print JSON").action(async e=>{if(e.enabled==="true"&&e.group.length>1)throw new Error("WeChat RPA \u6BCF\u4E2A\u5BF9\u8BDD\u53EA\u80FD\u7ED1\u5B9A\u4E00\u4E2A\u7FA4");const t=await i("/wechat-rpa/channel/upsert",{id:e.id,name:e.name,enabled:e.enabled==="true",groups:e.group.map(m=>({name:m})),canReply:g(e.canReply),source:e.source,pollIntervalMs:f(e.pollIntervalMs),recentLimit:f(e.recentLimit),idleSeconds:f(e.idleSeconds),forceForeground:g(e.forceForeground),noRestore:e.restorePrevious===void 0?void 0:e.restorePrevious!=="true",downloadAttachments:g(e.downloadAttachments),downloadAttachmentsDir:e.downloadAttachmentsDir,privacyConsentAccepted:g(e.privacyConsent),flowScriptPath:e.flowScriptPath});e.json?s(t):s(t.channel??null)})}export{A as registerManagerCommand};
@@ -1,6 +1 @@
1
- // @arch docs/product/PRD.md#手机扫码配对
2
- // @test src/__tests__/pair-qr.test.ts
3
- export const PAIR_QR_RENDER_OPTIONS = { small: false };
4
- export function buildPairQrPayload(pairToken) {
5
- return `pair:${pairToken}`;
6
- }
1
+ const a={small:!1};function l(r){return`pair:${r}`}export{a as PAIR_QR_RENDER_OPTIONS,l as buildPairQrPayload};