codeksei 0.1.0 → 0.1.1

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 (68) hide show
  1. package/LICENSE +661 -661
  2. package/README.en.md +109 -47
  3. package/README.md +79 -58
  4. package/bin/cyberboss.js +1 -1
  5. package/package.json +86 -86
  6. package/scripts/open_shared_wechat_thread.sh +77 -77
  7. package/scripts/open_wechat_thread.sh +108 -108
  8. package/scripts/shared-common.js +144 -144
  9. package/scripts/shared-open.js +14 -14
  10. package/scripts/shared-start.js +5 -5
  11. package/scripts/shared-status.js +27 -27
  12. package/scripts/show_shared_status.sh +45 -45
  13. package/scripts/start_shared_app_server.sh +52 -52
  14. package/scripts/start_shared_wechat.sh +94 -94
  15. package/scripts/timeline-screenshot.sh +14 -14
  16. package/src/adapters/channel/weixin/account-store.js +99 -99
  17. package/src/adapters/channel/weixin/api-v2.js +50 -50
  18. package/src/adapters/channel/weixin/api.js +169 -169
  19. package/src/adapters/channel/weixin/context-token-store.js +84 -84
  20. package/src/adapters/channel/weixin/index.js +618 -604
  21. package/src/adapters/channel/weixin/legacy.js +579 -566
  22. package/src/adapters/channel/weixin/media-mime.js +22 -22
  23. package/src/adapters/channel/weixin/media-receive.js +370 -370
  24. package/src/adapters/channel/weixin/media-send.js +102 -102
  25. package/src/adapters/channel/weixin/message-utils-v2.js +282 -282
  26. package/src/adapters/channel/weixin/message-utils.js +199 -199
  27. package/src/adapters/channel/weixin/redact.js +41 -41
  28. package/src/adapters/channel/weixin/reminder-queue-store.js +101 -101
  29. package/src/adapters/channel/weixin/sync-buffer-store.js +35 -35
  30. package/src/adapters/runtime/codex/events.js +215 -215
  31. package/src/adapters/runtime/codex/index.js +109 -104
  32. package/src/adapters/runtime/codex/message-utils.js +95 -95
  33. package/src/adapters/runtime/codex/model-catalog.js +106 -106
  34. package/src/adapters/runtime/codex/protocol-leak-monitor.js +75 -75
  35. package/src/adapters/runtime/codex/rpc-client.js +339 -339
  36. package/src/adapters/runtime/codex/session-store.js +286 -286
  37. package/src/app/channel-send-file-cli.js +57 -57
  38. package/src/app/diary-write-cli.js +236 -88
  39. package/src/app/note-sync-cli.js +2 -2
  40. package/src/app/reminder-write-cli.js +215 -210
  41. package/src/app/review-cli.js +7 -5
  42. package/src/app/system-checkin-poller.js +64 -64
  43. package/src/app/system-send-cli.js +129 -129
  44. package/src/app/timeline-event-cli.js +28 -25
  45. package/src/app/timeline-screenshot-cli.js +103 -100
  46. package/src/core/app.js +1763 -1763
  47. package/src/core/branding.js +2 -1
  48. package/src/core/command-registry.js +381 -369
  49. package/src/core/config.js +30 -14
  50. package/src/core/default-targets.js +163 -163
  51. package/src/core/durable-note-schema.js +9 -8
  52. package/src/core/instructions-template.js +17 -16
  53. package/src/core/note-sync.js +8 -7
  54. package/src/core/path-utils.js +54 -0
  55. package/src/core/project-radar.js +11 -10
  56. package/src/core/review.js +48 -50
  57. package/src/core/stream-delivery.js +1162 -983
  58. package/src/core/system-message-dispatcher.js +68 -68
  59. package/src/core/system-message-queue-store.js +128 -128
  60. package/src/core/thread-state-store.js +96 -96
  61. package/src/core/timeline-screenshot-queue-store.js +134 -134
  62. package/src/core/timezone.js +436 -0
  63. package/src/core/workspace-bootstrap.js +9 -1
  64. package/src/index.js +148 -146
  65. package/src/integrations/timeline/index.js +130 -74
  66. package/src/integrations/timeline/state-sync.js +240 -0
  67. package/templates/weixin-instructions.md +12 -38
  68. package/templates/weixin-operations.md +29 -31
@@ -1,129 +1,129 @@
1
- const crypto = require("crypto");
2
- const fs = require("fs");
3
- const path = require("path");
4
-
5
- const { resolveSelectedAccount } = require("../adapters/channel/weixin/account-store");
6
- const { loadPersistedContextTokens } = require("../adapters/channel/weixin/context-token-store");
7
- const { SessionStore } = require("../adapters/runtime/codex/session-store");
8
- const { resolvePreferredSenderId, resolvePreferredWorkspaceRoot } = require("../core/default-targets");
9
- const { SystemMessageQueueStore } = require("../core/system-message-queue-store");
10
-
11
- async function runSystemSendCommand(config) {
12
- const options = parseSystemSendArgs(process.argv.slice(4));
13
- if (options.help) {
14
- printSystemSendHelp();
15
- return;
16
- }
17
-
18
- const account = resolveSelectedAccount(config);
19
- const sessionStore = new SessionStore({ filePath: config.sessionsFile });
20
- const senderId = resolvePreferredSenderId({
21
- config,
22
- accountId: account.accountId,
23
- explicitUser: options.user,
24
- sessionStore,
25
- });
26
- const text = options.text;
27
- const workspaceRoot = resolvePreferredWorkspaceRoot({
28
- config,
29
- accountId: account.accountId,
30
- senderId,
31
- explicitWorkspace: options.workspace,
32
- sessionStore,
33
- });
34
- if (!senderId || !text || !workspaceRoot) {
35
- printSystemSendHelp();
36
- throw new Error("system send 缺少必要参数");
37
- }
38
- if (!path.isAbsolute(workspaceRoot)) {
39
- throw new Error(`workspace 必须是绝对路径: ${workspaceRoot}`);
40
- }
41
-
42
- let workspaceStats = null;
43
- try {
44
- workspaceStats = fs.statSync(workspaceRoot);
45
- } catch {
46
- throw new Error(`workspace 不存在: ${workspaceRoot}`);
47
- }
48
- if (!workspaceStats.isDirectory()) {
49
- throw new Error(`workspace 不是目录: ${workspaceRoot}`);
50
- }
51
-
52
- const contextTokens = loadPersistedContextTokens(config, account.accountId);
53
- if (!contextTokens[senderId]) {
54
- throw new Error(`找不到用户 ${senderId} 的 context token,先让这个用户和 bot 聊过一次`);
55
- }
56
- const queue = new SystemMessageQueueStore({ filePath: config.systemMessageQueueFile });
57
- const queued = queue.enqueue({
58
- id: crypto.randomUUID(),
59
- accountId: account.accountId,
60
- senderId,
61
- workspaceRoot,
62
- text,
63
- createdAt: new Date().toISOString(),
64
- });
65
-
66
- console.log(`system message queued: ${queued.id}`);
67
- console.log(`user: ${queued.senderId}`);
68
- console.log(`workspace: ${queued.workspaceRoot}`);
69
- }
70
-
71
- function parseSystemSendArgs(args) {
72
- const options = {
73
- help: false,
74
- user: "",
75
- text: "",
76
- workspace: "",
77
- };
78
-
79
- for (let index = 0; index < args.length; index += 1) {
80
- const token = String(args[index] || "").trim();
81
- if (!token) {
82
- continue;
83
- }
84
-
85
- if (token === "--help" || token === "-h") {
86
- options.help = true;
87
- continue;
88
- }
89
-
90
- if (!token.startsWith("--")) {
91
- throw new Error(`未知参数: ${token}`);
92
- }
93
-
94
- const key = token.slice(2);
95
- const value = String(args[index + 1] || "");
96
- if (!value || value.startsWith("--")) {
97
- throw new Error(`参数缺少值: ${token}`);
98
- }
99
-
100
- if (key === "user") {
101
- options.user = value.trim();
102
- } else if (key === "text") {
103
- options.text = value.trim();
104
- } else if (key === "workspace") {
105
- options.workspace = value.trim();
106
- } else {
107
- throw new Error(`未知参数: ${token}`);
108
- }
109
-
110
- index += 1;
111
- }
112
-
113
- return options;
114
- }
115
-
116
- function printSystemSendHelp() {
117
- console.log(`
118
- 用法: npm run system:send -- --text "<message>" [--user <wechat_user_id>] [--workspace /绝对路径]
119
-
120
- 示例:
121
- npm run system:send -- --text "提醒她今天早点睡" --workspace "$(pwd)"
122
- `);
123
- }
124
-
125
- function normalizeWorkspacePath(value) {
126
- return typeof value === "string" ? value.trim() : "";
127
- }
128
-
129
- module.exports = { runSystemSendCommand };
1
+ const crypto = require("crypto");
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+
5
+ const { resolveSelectedAccount } = require("../adapters/channel/weixin/account-store");
6
+ const { loadPersistedContextTokens } = require("../adapters/channel/weixin/context-token-store");
7
+ const { SessionStore } = require("../adapters/runtime/codex/session-store");
8
+ const { resolvePreferredSenderId, resolvePreferredWorkspaceRoot } = require("../core/default-targets");
9
+ const { SystemMessageQueueStore } = require("../core/system-message-queue-store");
10
+
11
+ async function runSystemSendCommand(config) {
12
+ const options = parseSystemSendArgs(process.argv.slice(4));
13
+ if (options.help) {
14
+ printSystemSendHelp();
15
+ return;
16
+ }
17
+
18
+ const account = resolveSelectedAccount(config);
19
+ const sessionStore = new SessionStore({ filePath: config.sessionsFile });
20
+ const senderId = resolvePreferredSenderId({
21
+ config,
22
+ accountId: account.accountId,
23
+ explicitUser: options.user,
24
+ sessionStore,
25
+ });
26
+ const text = options.text;
27
+ const workspaceRoot = resolvePreferredWorkspaceRoot({
28
+ config,
29
+ accountId: account.accountId,
30
+ senderId,
31
+ explicitWorkspace: options.workspace,
32
+ sessionStore,
33
+ });
34
+ if (!senderId || !text || !workspaceRoot) {
35
+ printSystemSendHelp();
36
+ throw new Error("system send 缺少必要参数");
37
+ }
38
+ if (!path.isAbsolute(workspaceRoot)) {
39
+ throw new Error(`workspace 必须是绝对路径: ${workspaceRoot}`);
40
+ }
41
+
42
+ let workspaceStats = null;
43
+ try {
44
+ workspaceStats = fs.statSync(workspaceRoot);
45
+ } catch {
46
+ throw new Error(`workspace 不存在: ${workspaceRoot}`);
47
+ }
48
+ if (!workspaceStats.isDirectory()) {
49
+ throw new Error(`workspace 不是目录: ${workspaceRoot}`);
50
+ }
51
+
52
+ const contextTokens = loadPersistedContextTokens(config, account.accountId);
53
+ if (!contextTokens[senderId]) {
54
+ throw new Error(`找不到用户 ${senderId} 的 context token,先让这个用户和 bot 聊过一次`);
55
+ }
56
+ const queue = new SystemMessageQueueStore({ filePath: config.systemMessageQueueFile });
57
+ const queued = queue.enqueue({
58
+ id: crypto.randomUUID(),
59
+ accountId: account.accountId,
60
+ senderId,
61
+ workspaceRoot,
62
+ text,
63
+ createdAt: new Date().toISOString(),
64
+ });
65
+
66
+ console.log(`system message queued: ${queued.id}`);
67
+ console.log(`user: ${queued.senderId}`);
68
+ console.log(`workspace: ${queued.workspaceRoot}`);
69
+ }
70
+
71
+ function parseSystemSendArgs(args) {
72
+ const options = {
73
+ help: false,
74
+ user: "",
75
+ text: "",
76
+ workspace: "",
77
+ };
78
+
79
+ for (let index = 0; index < args.length; index += 1) {
80
+ const token = String(args[index] || "").trim();
81
+ if (!token) {
82
+ continue;
83
+ }
84
+
85
+ if (token === "--help" || token === "-h") {
86
+ options.help = true;
87
+ continue;
88
+ }
89
+
90
+ if (!token.startsWith("--")) {
91
+ throw new Error(`未知参数: ${token}`);
92
+ }
93
+
94
+ const key = token.slice(2);
95
+ const value = String(args[index + 1] || "");
96
+ if (!value || value.startsWith("--")) {
97
+ throw new Error(`参数缺少值: ${token}`);
98
+ }
99
+
100
+ if (key === "user") {
101
+ options.user = value.trim();
102
+ } else if (key === "text") {
103
+ options.text = value.trim();
104
+ } else if (key === "workspace") {
105
+ options.workspace = value.trim();
106
+ } else {
107
+ throw new Error(`未知参数: ${token}`);
108
+ }
109
+
110
+ index += 1;
111
+ }
112
+
113
+ return options;
114
+ }
115
+
116
+ function printSystemSendHelp() {
117
+ console.log(`
118
+ 用法: npm run system:send -- --text "<message>" [--user <wechat_user_id>] [--workspace /绝对路径]
119
+
120
+ 示例:
121
+ npm run system:send -- --text "提醒她今天早点睡" --workspace "$(pwd)"
122
+ `);
123
+ }
124
+
125
+ function normalizeWorkspacePath(value) {
126
+ return typeof value === "string" ? value.trim() : "";
127
+ }
128
+
129
+ module.exports = { runSystemSendCommand };
@@ -1,16 +1,20 @@
1
1
  const crypto = require("crypto");
2
-
3
- const LOCAL_TIMEZONE_OFFSET = "+08:00";
4
-
5
- async function runTimelineEventCommand(timelineIntegration, args = process.argv.slice(4)) {
2
+ const {
3
+ LEGACY_TIMELINE_TIMEZONE,
4
+ coerceLocalDateTimeToIso,
5
+ } = require("../core/timezone");
6
+
7
+ async function runTimelineEventCommand(timelineIntegration, configOrArgs = {}, argsMaybe = process.argv.slice(4)) {
8
+ const config = Array.isArray(configOrArgs) ? {} : (configOrArgs || {});
9
+ const args = Array.isArray(configOrArgs) ? configOrArgs : argsMaybe;
6
10
  const options = parseTimelineEventArgs(args);
7
11
  if (options.help) {
8
- printTimelineEventHelp();
12
+ printTimelineEventHelp(config.timezone);
9
13
  return;
10
14
  }
11
15
 
12
16
  const note = await resolveNote(options);
13
- const writeArgs = buildTimelineEventWriteArgs(options, note);
17
+ const writeArgs = buildTimelineEventWriteArgs(options, note, config);
14
18
  await timelineIntegration.runSubcommand("write", writeArgs);
15
19
  }
16
20
 
@@ -109,11 +113,12 @@ async function resolveNote(options) {
109
113
  return normalizeText(await readStdin());
110
114
  }
111
115
 
112
- function buildTimelineEventWriteArgs(options, note = "") {
116
+ function buildTimelineEventWriteArgs(options, note = "", config = {}) {
113
117
  const date = normalizeDate(options.date);
114
118
  if (!date) {
115
119
  throw new Error("缺少有效日期,使用 --date YYYY-MM-DD");
116
120
  }
121
+ const timezone = normalizeTimezoneConfigValue(config.timezone) || LEGACY_TIMELINE_TIMEZONE;
117
122
 
118
123
  const title = normalizeText(options.title);
119
124
  if (!title) {
@@ -129,8 +134,8 @@ function buildTimelineEventWriteArgs(options, note = "") {
129
134
  throw new Error("缺少分类信息,至少传 --event-node 或 --subcategory");
130
135
  }
131
136
 
132
- const startAt = normalizeTimelineEventTimestamp(date, options.start, "--start");
133
- const endAt = normalizeTimelineEventTimestamp(date, options.end, "--end");
137
+ const startAt = normalizeTimelineEventTimestamp(date, options.start, "--start", timezone);
138
+ const endAt = normalizeTimelineEventTimestamp(date, options.end, "--end", timezone);
134
139
  validateEventRange({ date, startAt, endAt });
135
140
 
136
141
  const event = {
@@ -179,26 +184,18 @@ function normalizeDate(value) {
179
184
  return /^\d{4}-\d{2}-\d{2}$/.test(normalized) ? normalized : "";
180
185
  }
181
186
 
182
- function normalizeTimelineEventTimestamp(date, value, flagName) {
187
+ function normalizeTimelineEventTimestamp(date, value, flagName, timezone = LEGACY_TIMELINE_TIMEZONE) {
183
188
  const normalized = normalizeText(value);
184
189
  if (!normalized) {
185
190
  throw new Error(`缺少时间,使用 ${flagName} HH:mm 或完整时间戳`);
186
191
  }
187
192
 
188
- if (/^\d{2}:\d{2}$/.test(normalized)) {
189
- return `${date}T${normalized}:00${LOCAL_TIMEZONE_OFFSET}`;
190
- }
191
-
192
- if (/^\d{2}:\d{2}:\d{2}$/.test(normalized)) {
193
- return `${date}T${normalized}${LOCAL_TIMEZONE_OFFSET}`;
194
- }
195
-
196
- if (/^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}(:\d{2})?$/.test(normalized)) {
197
- return `${normalized.replace(" ", "T")}${LOCAL_TIMEZONE_OFFSET}`;
198
- }
199
-
200
- if (/^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(:\d{2})?([zZ]|[+-]\d{2}:\d{2})$/.test(normalized)) {
201
- return normalized.replace(" ", "T");
193
+ const timestamp = coerceLocalDateTimeToIso(normalized, {
194
+ timeZone: timezone,
195
+ defaultDate: date,
196
+ });
197
+ if (timestamp) {
198
+ return timestamp;
202
199
  }
203
200
 
204
201
  throw new Error(`不支持的时间格式: ${flagName}=${normalized}`);
@@ -235,7 +232,8 @@ function normalizeText(value) {
235
232
  return String(value || "").replace(/\r\n/g, "\n").trim();
236
233
  }
237
234
 
238
- function printTimelineEventHelp() {
235
+ function printTimelineEventHelp(timezone = LEGACY_TIMELINE_TIMEZONE) {
236
+ const resolvedTimezone = normalizeTimezoneConfigValue(timezone) || LEGACY_TIMELINE_TIMEZONE;
239
237
  console.log(`
240
238
  用法: npm run timeline:event -- --date YYYY-MM-DD --start HH:mm --end HH:mm --title "标题" (--event-node <id> | --subcategory <id>) [其他参数]
241
239
 
@@ -243,6 +241,7 @@ function printTimelineEventHelp() {
243
241
  - 写单条时间轴事件,不必手写 raw JSON
244
242
  - 适合把一个明确的时间块快速追加进当天 timeline
245
243
  - 如果要一次写多条事件,或直接替换整批 events,继续用 timeline:write
244
+ - 不带 offset 的本地时间默认按 ${resolvedTimezone} 解释
246
245
 
247
246
  常用参数:
248
247
  --date YYYY-MM-DD
@@ -271,3 +270,7 @@ module.exports = {
271
270
  buildTimelineEventWriteArgs,
272
271
  normalizeTimelineEventTimestamp,
273
272
  };
273
+
274
+ function normalizeTimezoneConfigValue(value) {
275
+ return typeof value === "string" ? value.trim() : "";
276
+ }
@@ -1,109 +1,112 @@
1
- const crypto = require("crypto");
2
- const path = require("path");
3
-
4
- const { resolveSelectedAccount } = require("../adapters/channel/weixin/account-store");
5
- const { SessionStore } = require("../adapters/runtime/codex/session-store");
6
- const { resolvePreferredSenderId } = require("../core/default-targets");
7
- const { TimelineScreenshotQueueStore } = require("../core/timeline-screenshot-queue-store");
8
-
9
- async function runTimelineScreenshotCommand(config, args = process.argv.slice(4)) {
10
- const options = parseTimelineScreenshotArgs(args);
11
- if (options.help) {
12
- printTimelineScreenshotHelp();
13
- return;
14
- }
15
-
16
- const account = resolveSelectedAccount(config);
17
- const sessionStore = new SessionStore({ filePath: config.sessionsFile });
18
- const senderId = resolvePreferredSenderId({
19
- config,
20
- accountId: account.accountId,
21
- explicitUser: options.user,
22
- sessionStore,
23
- });
24
-
1
+ const crypto = require("crypto");
2
+ const path = require("path");
3
+
4
+ const { resolveSelectedAccount } = require("../adapters/channel/weixin/account-store");
5
+ const { SessionStore } = require("../adapters/runtime/codex/session-store");
6
+ const { resolvePreferredSenderId } = require("../core/default-targets");
7
+ const { TimelineScreenshotQueueStore } = require("../core/timeline-screenshot-queue-store");
8
+
9
+ async function runTimelineScreenshotCommand(config, args = process.argv.slice(4)) {
10
+ const options = parseTimelineScreenshotArgs(args);
11
+ if (options.help) {
12
+ printTimelineScreenshotHelp();
13
+ return;
14
+ }
15
+
16
+ const account = resolveSelectedAccount(config);
17
+ const sessionStore = new SessionStore({ filePath: config.sessionsFile });
18
+ const senderId = resolvePreferredSenderId({
19
+ config,
20
+ accountId: account.accountId,
21
+ explicitUser: options.user,
22
+ sessionStore,
23
+ });
24
+
25
25
  if (!senderId) {
26
26
  throw new Error("缺少发送目标,传 --user 或配置 CODEKSEI_ALLOWED_USER_IDS(或旧的 CYBERBOSS_ALLOWED_USER_IDS)");
27
27
  }
28
-
29
- const queue = new TimelineScreenshotQueueStore({ filePath: config.timelineScreenshotQueueFile });
30
- const queued = queue.enqueue({
31
- id: crypto.randomUUID(),
32
- accountId: account.accountId,
33
- senderId,
34
- outputFile: options.outputFile,
35
- args: options.forwardArgs,
36
- createdAt: new Date().toISOString(),
37
- });
38
-
28
+
29
+ const queue = new TimelineScreenshotQueueStore({ filePath: config.timelineScreenshotQueueFile });
30
+ const queued = queue.enqueue({
31
+ id: crypto.randomUUID(),
32
+ accountId: account.accountId,
33
+ senderId,
34
+ outputFile: options.outputFile,
35
+ args: options.forwardArgs,
36
+ createdAt: new Date().toISOString(),
37
+ });
38
+
39
39
  console.log(`timeline screenshot queued: ${queued.id}`);
40
40
  console.log("delivery_status: pending_bridge_send");
41
41
  }
42
-
43
- function parseTimelineScreenshotArgs(args) {
44
- const options = {
45
- help: false,
46
- user: "",
47
- outputFile: "",
48
- forwardArgs: [],
49
- };
50
-
51
- for (let index = 0; index < args.length; index += 1) {
52
- const token = String(args[index] || "").trim();
53
- if (!token) {
54
- continue;
55
- }
56
- if (token === "--help" || token === "-h") {
57
- options.help = true;
58
- continue;
59
- }
60
- if (token === "--send") {
61
- continue;
62
- }
63
- if (token === "--user") {
64
- const value = String(args[index + 1] || "").trim();
65
- if (!value || value.startsWith("--")) {
66
- throw new Error("参数缺少值: --user");
67
- }
68
- options.user = value;
69
- index += 1;
70
- continue;
71
- }
72
- if (token === "--output") {
73
- const value = String(args[index + 1] || "").trim();
74
- if (!value || value.startsWith("--")) {
75
- throw new Error("参数缺少值: --output");
76
- }
77
- options.outputFile = path.resolve(value);
78
- index += 1;
79
- continue;
80
- }
81
-
82
- options.forwardArgs.push(token);
83
- const next = String(args[index + 1] || "").trim();
84
- if (token.startsWith("--") && next && !next.startsWith("--")) {
85
- options.forwardArgs.push(next);
86
- index += 1;
87
- }
88
- }
89
-
90
- return options;
91
- }
92
-
93
- function printTimelineScreenshotHelp() {
94
- console.log(`
95
- 用法: npm run timeline:screenshot -- --send [--user <wechatUserId>] [--output /绝对路径] [其他 timeline screenshot 参数]
96
-
42
+
43
+ function parseTimelineScreenshotArgs(args) {
44
+ const options = {
45
+ help: false,
46
+ user: "",
47
+ outputFile: "",
48
+ forwardArgs: [],
49
+ };
50
+
51
+ for (let index = 0; index < args.length; index += 1) {
52
+ const token = String(args[index] || "").trim();
53
+ if (!token) {
54
+ continue;
55
+ }
56
+ if (token === "--help" || token === "-h") {
57
+ options.help = true;
58
+ continue;
59
+ }
60
+ if (token === "--send") {
61
+ continue;
62
+ }
63
+ if (token === "--demo") {
64
+ continue;
65
+ }
66
+ if (token === "--user") {
67
+ const value = String(args[index + 1] || "").trim();
68
+ if (!value || value.startsWith("--")) {
69
+ throw new Error("参数缺少值: --user");
70
+ }
71
+ options.user = value;
72
+ index += 1;
73
+ continue;
74
+ }
75
+ if (token === "--output") {
76
+ const value = String(args[index + 1] || "").trim();
77
+ if (!value || value.startsWith("--")) {
78
+ throw new Error("参数缺少值: --output");
79
+ }
80
+ options.outputFile = path.resolve(value);
81
+ index += 1;
82
+ continue;
83
+ }
84
+
85
+ options.forwardArgs.push(token);
86
+ const next = String(args[index + 1] || "").trim();
87
+ if (token.startsWith("--") && next && !next.startsWith("--")) {
88
+ options.forwardArgs.push(next);
89
+ index += 1;
90
+ }
91
+ }
92
+
93
+ return options;
94
+ }
95
+
96
+ function printTimelineScreenshotHelp() {
97
+ console.log(`
98
+ 用法: npm run timeline:screenshot -- --send [--user <wechatUserId>] [--output /绝对路径] [其他 timeline screenshot 参数]
99
+
97
100
  说明:
98
101
  这条命令只负责把截图任务排进本地队列,真正截图和发送由正在运行的微信 bridge 异步执行。
99
102
  queued 不等于“已经发到微信”;只有 bridge 真正送达后,用户那边才会看到图片或文件。
100
-
101
- 示例:
102
- npm run timeline:screenshot -- --send --selector timeline
103
- `);
104
- }
105
-
106
- module.exports = {
107
- runTimelineScreenshotCommand,
108
- parseTimelineScreenshotArgs,
109
- };
103
+
104
+ 示例:
105
+ npm run timeline:screenshot -- --send --selector timeline
106
+ `);
107
+ }
108
+
109
+ module.exports = {
110
+ runTimelineScreenshotCommand,
111
+ parseTimelineScreenshotArgs,
112
+ };