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.
- package/LICENSE +661 -661
- package/README.en.md +109 -47
- package/README.md +79 -58
- package/bin/cyberboss.js +1 -1
- package/package.json +86 -86
- package/scripts/open_shared_wechat_thread.sh +77 -77
- package/scripts/open_wechat_thread.sh +108 -108
- package/scripts/shared-common.js +144 -144
- package/scripts/shared-open.js +14 -14
- package/scripts/shared-start.js +5 -5
- package/scripts/shared-status.js +27 -27
- package/scripts/show_shared_status.sh +45 -45
- package/scripts/start_shared_app_server.sh +52 -52
- package/scripts/start_shared_wechat.sh +94 -94
- package/scripts/timeline-screenshot.sh +14 -14
- package/src/adapters/channel/weixin/account-store.js +99 -99
- package/src/adapters/channel/weixin/api-v2.js +50 -50
- package/src/adapters/channel/weixin/api.js +169 -169
- package/src/adapters/channel/weixin/context-token-store.js +84 -84
- package/src/adapters/channel/weixin/index.js +618 -604
- package/src/adapters/channel/weixin/legacy.js +579 -566
- package/src/adapters/channel/weixin/media-mime.js +22 -22
- package/src/adapters/channel/weixin/media-receive.js +370 -370
- package/src/adapters/channel/weixin/media-send.js +102 -102
- package/src/adapters/channel/weixin/message-utils-v2.js +282 -282
- package/src/adapters/channel/weixin/message-utils.js +199 -199
- package/src/adapters/channel/weixin/redact.js +41 -41
- package/src/adapters/channel/weixin/reminder-queue-store.js +101 -101
- package/src/adapters/channel/weixin/sync-buffer-store.js +35 -35
- package/src/adapters/runtime/codex/events.js +215 -215
- package/src/adapters/runtime/codex/index.js +109 -104
- package/src/adapters/runtime/codex/message-utils.js +95 -95
- package/src/adapters/runtime/codex/model-catalog.js +106 -106
- package/src/adapters/runtime/codex/protocol-leak-monitor.js +75 -75
- package/src/adapters/runtime/codex/rpc-client.js +339 -339
- package/src/adapters/runtime/codex/session-store.js +286 -286
- package/src/app/channel-send-file-cli.js +57 -57
- package/src/app/diary-write-cli.js +236 -88
- package/src/app/note-sync-cli.js +2 -2
- package/src/app/reminder-write-cli.js +215 -210
- package/src/app/review-cli.js +7 -5
- package/src/app/system-checkin-poller.js +64 -64
- package/src/app/system-send-cli.js +129 -129
- package/src/app/timeline-event-cli.js +28 -25
- package/src/app/timeline-screenshot-cli.js +103 -100
- package/src/core/app.js +1763 -1763
- package/src/core/branding.js +2 -1
- package/src/core/command-registry.js +381 -369
- package/src/core/config.js +30 -14
- package/src/core/default-targets.js +163 -163
- package/src/core/durable-note-schema.js +9 -8
- package/src/core/instructions-template.js +17 -16
- package/src/core/note-sync.js +8 -7
- package/src/core/path-utils.js +54 -0
- package/src/core/project-radar.js +11 -10
- package/src/core/review.js +48 -50
- package/src/core/stream-delivery.js +1162 -983
- package/src/core/system-message-dispatcher.js +68 -68
- package/src/core/system-message-queue-store.js +128 -128
- package/src/core/thread-state-store.js +96 -96
- package/src/core/timeline-screenshot-queue-store.js +134 -134
- package/src/core/timezone.js +436 -0
- package/src/core/workspace-bootstrap.js +9 -1
- package/src/index.js +148 -146
- package/src/integrations/timeline/index.js +130 -74
- package/src/integrations/timeline/state-sync.js +240 -0
- package/templates/weixin-instructions.md +12 -38
- 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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if (
|
|
193
|
-
return
|
|
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 === "--
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
+
};
|