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,199 +1,199 @@
|
|
|
1
|
-
const TEXT_ITEM_TYPE = 1;
|
|
2
|
-
const IMAGE_ITEM_TYPE = 2;
|
|
3
|
-
const VOICE_ITEM_TYPE = 3;
|
|
4
|
-
const FILE_ITEM_TYPE = 4;
|
|
5
|
-
const VIDEO_ITEM_TYPE = 5;
|
|
6
|
-
const BOT_MESSAGE_TYPE = 2;
|
|
7
|
-
|
|
8
|
-
function normalizeWeixinIncomingMessage(message, config, accountId) {
|
|
9
|
-
if (!message || typeof message !== "object") {
|
|
10
|
-
return null;
|
|
11
|
-
}
|
|
12
|
-
if (Number(message.message_type) === BOT_MESSAGE_TYPE) {
|
|
13
|
-
return null;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const senderId = normalizeText(message.from_user_id);
|
|
17
|
-
if (!senderId) {
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const text = extractTextBody(message.item_list);
|
|
22
|
-
const attachments = extractAttachmentItems(message.item_list);
|
|
23
|
-
if (!text && !attachments.length) {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return {
|
|
28
|
-
provider: "weixin",
|
|
29
|
-
accountId,
|
|
30
|
-
workspaceId: config.workspaceId,
|
|
31
|
-
senderId,
|
|
32
|
-
chatId: senderId,
|
|
33
|
-
messageId: normalizeText(message.message_id),
|
|
34
|
-
threadKey: normalizeText(message.session_id),
|
|
35
|
-
text,
|
|
36
|
-
attachments,
|
|
37
|
-
contextToken: normalizeText(message.context_token),
|
|
38
|
-
receivedAt: resolveReceivedAt(message),
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function extractTextBody(itemList) {
|
|
43
|
-
if (!Array.isArray(itemList) || !itemList.length) {
|
|
44
|
-
return "";
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
for (const item of itemList) {
|
|
48
|
-
if (Number(item?.type) === TEXT_ITEM_TYPE && typeof item?.text_item?.text === "string") {
|
|
49
|
-
return item.text_item.text.trim();
|
|
50
|
-
}
|
|
51
|
-
if (Number(item?.type) === VOICE_ITEM_TYPE && typeof item?.voice_item?.text === "string") {
|
|
52
|
-
return item.voice_item.text.trim();
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return "";
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function extractAttachmentItems(itemList) {
|
|
60
|
-
if (!Array.isArray(itemList) || !itemList.length) {
|
|
61
|
-
return [];
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const attachments = [];
|
|
65
|
-
for (let index = 0; index < itemList.length; index += 1) {
|
|
66
|
-
const normalized = normalizeAttachmentItem(itemList[index], index);
|
|
67
|
-
if (normalized) {
|
|
68
|
-
attachments.push(normalized);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return attachments;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function normalizeAttachmentItem(item, index) {
|
|
76
|
-
const itemType = Number(item?.type);
|
|
77
|
-
const payload = resolveAttachmentPayload(itemType, item);
|
|
78
|
-
if (!payload) {
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const media = payload.media && typeof payload.media === "object"
|
|
83
|
-
? payload.media
|
|
84
|
-
: {};
|
|
85
|
-
|
|
86
|
-
return {
|
|
87
|
-
kind: payload.kind,
|
|
88
|
-
itemType,
|
|
89
|
-
index,
|
|
90
|
-
fileName: normalizeText(
|
|
91
|
-
payload.body?.file_name
|
|
92
|
-
|| payload.body?.filename
|
|
93
|
-
|| item?.file_name
|
|
94
|
-
|| item?.filename
|
|
95
|
-
),
|
|
96
|
-
sizeBytes: parseOptionalInt(
|
|
97
|
-
payload.body?.len
|
|
98
|
-
|| payload.body?.file_size
|
|
99
|
-
|| payload.body?.size
|
|
100
|
-
|| payload.body?.video_size
|
|
101
|
-
|| item?.len
|
|
102
|
-
),
|
|
103
|
-
directUrls: collectStringValues([
|
|
104
|
-
payload.body?.url,
|
|
105
|
-
payload.body?.download_url,
|
|
106
|
-
payload.body?.cdn_url,
|
|
107
|
-
media?.url,
|
|
108
|
-
media?.download_url,
|
|
109
|
-
media?.cdn_url,
|
|
110
|
-
]),
|
|
111
|
-
mediaRef: {
|
|
112
|
-
encryptQueryParam: normalizeText(
|
|
113
|
-
media?.encrypt_query_param
|
|
114
|
-
|| media?.encrypted_query_param
|
|
115
|
-
|| payload.body?.encrypt_query_param
|
|
116
|
-
|| payload.body?.encrypted_query_param
|
|
117
|
-
|| item?.encrypt_query_param
|
|
118
|
-
|| item?.encrypted_query_param
|
|
119
|
-
),
|
|
120
|
-
aesKey: normalizeText(
|
|
121
|
-
media?.aes_key
|
|
122
|
-
|| payload.body?.aes_key
|
|
123
|
-
|| item?.aes_key
|
|
124
|
-
),
|
|
125
|
-
aesKeyHex: normalizeText(
|
|
126
|
-
payload.body?.aeskey
|
|
127
|
-
|| payload.body?.aes_key_hex
|
|
128
|
-
|| item?.aeskey
|
|
129
|
-
),
|
|
130
|
-
encryptType: Number(
|
|
131
|
-
media?.encrypt_type
|
|
132
|
-
?? payload.body?.encrypt_type
|
|
133
|
-
?? item?.encrypt_type
|
|
134
|
-
?? 1
|
|
135
|
-
),
|
|
136
|
-
fileKey: normalizeText(
|
|
137
|
-
media?.filekey
|
|
138
|
-
|| payload.body?.filekey
|
|
139
|
-
|| item?.filekey
|
|
140
|
-
),
|
|
141
|
-
},
|
|
142
|
-
rawItem: item,
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function resolveAttachmentPayload(itemType, item) {
|
|
147
|
-
if (itemType === IMAGE_ITEM_TYPE && item?.image_item && typeof item.image_item === "object") {
|
|
148
|
-
return { kind: "image", body: item.image_item, media: item.image_item.media };
|
|
149
|
-
}
|
|
150
|
-
if (itemType === FILE_ITEM_TYPE && item?.file_item && typeof item.file_item === "object") {
|
|
151
|
-
return { kind: "file", body: item.file_item, media: item.file_item.media };
|
|
152
|
-
}
|
|
153
|
-
if (itemType === VIDEO_ITEM_TYPE && item?.video_item && typeof item.video_item === "object") {
|
|
154
|
-
return { kind: "video", body: item.video_item, media: item.video_item.media };
|
|
155
|
-
}
|
|
156
|
-
return null;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function collectStringValues(values) {
|
|
160
|
-
const seen = new Set();
|
|
161
|
-
const result = [];
|
|
162
|
-
for (const value of values) {
|
|
163
|
-
const normalized = normalizeText(value);
|
|
164
|
-
if (!normalized || seen.has(normalized)) {
|
|
165
|
-
continue;
|
|
166
|
-
}
|
|
167
|
-
seen.add(normalized);
|
|
168
|
-
result.push(normalized);
|
|
169
|
-
}
|
|
170
|
-
return result;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function parseOptionalInt(value) {
|
|
174
|
-
if (value == null || value === "") {
|
|
175
|
-
return 0;
|
|
176
|
-
}
|
|
177
|
-
const parsed = Number.parseInt(String(value), 10);
|
|
178
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function normalizeText(value) {
|
|
182
|
-
return typeof value === "string" ? value.trim() : "";
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function resolveReceivedAt(message) {
|
|
186
|
-
const rawMs = Number(message?.create_time_ms);
|
|
187
|
-
if (Number.isFinite(rawMs) && rawMs > 0) {
|
|
188
|
-
return new Date(rawMs).toISOString();
|
|
189
|
-
}
|
|
190
|
-
const rawSeconds = Number(message?.create_time);
|
|
191
|
-
if (Number.isFinite(rawSeconds) && rawSeconds > 0) {
|
|
192
|
-
return new Date(rawSeconds * 1000).toISOString();
|
|
193
|
-
}
|
|
194
|
-
return new Date().toISOString();
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
module.exports = {
|
|
198
|
-
normalizeWeixinIncomingMessage,
|
|
199
|
-
};
|
|
1
|
+
const TEXT_ITEM_TYPE = 1;
|
|
2
|
+
const IMAGE_ITEM_TYPE = 2;
|
|
3
|
+
const VOICE_ITEM_TYPE = 3;
|
|
4
|
+
const FILE_ITEM_TYPE = 4;
|
|
5
|
+
const VIDEO_ITEM_TYPE = 5;
|
|
6
|
+
const BOT_MESSAGE_TYPE = 2;
|
|
7
|
+
|
|
8
|
+
function normalizeWeixinIncomingMessage(message, config, accountId) {
|
|
9
|
+
if (!message || typeof message !== "object") {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
if (Number(message.message_type) === BOT_MESSAGE_TYPE) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const senderId = normalizeText(message.from_user_id);
|
|
17
|
+
if (!senderId) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const text = extractTextBody(message.item_list);
|
|
22
|
+
const attachments = extractAttachmentItems(message.item_list);
|
|
23
|
+
if (!text && !attachments.length) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
provider: "weixin",
|
|
29
|
+
accountId,
|
|
30
|
+
workspaceId: config.workspaceId,
|
|
31
|
+
senderId,
|
|
32
|
+
chatId: senderId,
|
|
33
|
+
messageId: normalizeText(message.message_id),
|
|
34
|
+
threadKey: normalizeText(message.session_id),
|
|
35
|
+
text,
|
|
36
|
+
attachments,
|
|
37
|
+
contextToken: normalizeText(message.context_token),
|
|
38
|
+
receivedAt: resolveReceivedAt(message),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function extractTextBody(itemList) {
|
|
43
|
+
if (!Array.isArray(itemList) || !itemList.length) {
|
|
44
|
+
return "";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
for (const item of itemList) {
|
|
48
|
+
if (Number(item?.type) === TEXT_ITEM_TYPE && typeof item?.text_item?.text === "string") {
|
|
49
|
+
return item.text_item.text.trim();
|
|
50
|
+
}
|
|
51
|
+
if (Number(item?.type) === VOICE_ITEM_TYPE && typeof item?.voice_item?.text === "string") {
|
|
52
|
+
return item.voice_item.text.trim();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return "";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function extractAttachmentItems(itemList) {
|
|
60
|
+
if (!Array.isArray(itemList) || !itemList.length) {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const attachments = [];
|
|
65
|
+
for (let index = 0; index < itemList.length; index += 1) {
|
|
66
|
+
const normalized = normalizeAttachmentItem(itemList[index], index);
|
|
67
|
+
if (normalized) {
|
|
68
|
+
attachments.push(normalized);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return attachments;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function normalizeAttachmentItem(item, index) {
|
|
76
|
+
const itemType = Number(item?.type);
|
|
77
|
+
const payload = resolveAttachmentPayload(itemType, item);
|
|
78
|
+
if (!payload) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const media = payload.media && typeof payload.media === "object"
|
|
83
|
+
? payload.media
|
|
84
|
+
: {};
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
kind: payload.kind,
|
|
88
|
+
itemType,
|
|
89
|
+
index,
|
|
90
|
+
fileName: normalizeText(
|
|
91
|
+
payload.body?.file_name
|
|
92
|
+
|| payload.body?.filename
|
|
93
|
+
|| item?.file_name
|
|
94
|
+
|| item?.filename
|
|
95
|
+
),
|
|
96
|
+
sizeBytes: parseOptionalInt(
|
|
97
|
+
payload.body?.len
|
|
98
|
+
|| payload.body?.file_size
|
|
99
|
+
|| payload.body?.size
|
|
100
|
+
|| payload.body?.video_size
|
|
101
|
+
|| item?.len
|
|
102
|
+
),
|
|
103
|
+
directUrls: collectStringValues([
|
|
104
|
+
payload.body?.url,
|
|
105
|
+
payload.body?.download_url,
|
|
106
|
+
payload.body?.cdn_url,
|
|
107
|
+
media?.url,
|
|
108
|
+
media?.download_url,
|
|
109
|
+
media?.cdn_url,
|
|
110
|
+
]),
|
|
111
|
+
mediaRef: {
|
|
112
|
+
encryptQueryParam: normalizeText(
|
|
113
|
+
media?.encrypt_query_param
|
|
114
|
+
|| media?.encrypted_query_param
|
|
115
|
+
|| payload.body?.encrypt_query_param
|
|
116
|
+
|| payload.body?.encrypted_query_param
|
|
117
|
+
|| item?.encrypt_query_param
|
|
118
|
+
|| item?.encrypted_query_param
|
|
119
|
+
),
|
|
120
|
+
aesKey: normalizeText(
|
|
121
|
+
media?.aes_key
|
|
122
|
+
|| payload.body?.aes_key
|
|
123
|
+
|| item?.aes_key
|
|
124
|
+
),
|
|
125
|
+
aesKeyHex: normalizeText(
|
|
126
|
+
payload.body?.aeskey
|
|
127
|
+
|| payload.body?.aes_key_hex
|
|
128
|
+
|| item?.aeskey
|
|
129
|
+
),
|
|
130
|
+
encryptType: Number(
|
|
131
|
+
media?.encrypt_type
|
|
132
|
+
?? payload.body?.encrypt_type
|
|
133
|
+
?? item?.encrypt_type
|
|
134
|
+
?? 1
|
|
135
|
+
),
|
|
136
|
+
fileKey: normalizeText(
|
|
137
|
+
media?.filekey
|
|
138
|
+
|| payload.body?.filekey
|
|
139
|
+
|| item?.filekey
|
|
140
|
+
),
|
|
141
|
+
},
|
|
142
|
+
rawItem: item,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function resolveAttachmentPayload(itemType, item) {
|
|
147
|
+
if (itemType === IMAGE_ITEM_TYPE && item?.image_item && typeof item.image_item === "object") {
|
|
148
|
+
return { kind: "image", body: item.image_item, media: item.image_item.media };
|
|
149
|
+
}
|
|
150
|
+
if (itemType === FILE_ITEM_TYPE && item?.file_item && typeof item.file_item === "object") {
|
|
151
|
+
return { kind: "file", body: item.file_item, media: item.file_item.media };
|
|
152
|
+
}
|
|
153
|
+
if (itemType === VIDEO_ITEM_TYPE && item?.video_item && typeof item.video_item === "object") {
|
|
154
|
+
return { kind: "video", body: item.video_item, media: item.video_item.media };
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function collectStringValues(values) {
|
|
160
|
+
const seen = new Set();
|
|
161
|
+
const result = [];
|
|
162
|
+
for (const value of values) {
|
|
163
|
+
const normalized = normalizeText(value);
|
|
164
|
+
if (!normalized || seen.has(normalized)) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
seen.add(normalized);
|
|
168
|
+
result.push(normalized);
|
|
169
|
+
}
|
|
170
|
+
return result;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function parseOptionalInt(value) {
|
|
174
|
+
if (value == null || value === "") {
|
|
175
|
+
return 0;
|
|
176
|
+
}
|
|
177
|
+
const parsed = Number.parseInt(String(value), 10);
|
|
178
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function normalizeText(value) {
|
|
182
|
+
return typeof value === "string" ? value.trim() : "";
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function resolveReceivedAt(message) {
|
|
186
|
+
const rawMs = Number(message?.create_time_ms);
|
|
187
|
+
if (Number.isFinite(rawMs) && rawMs > 0) {
|
|
188
|
+
return new Date(rawMs).toISOString();
|
|
189
|
+
}
|
|
190
|
+
const rawSeconds = Number(message?.create_time);
|
|
191
|
+
if (Number.isFinite(rawSeconds) && rawSeconds > 0) {
|
|
192
|
+
return new Date(rawSeconds * 1000).toISOString();
|
|
193
|
+
}
|
|
194
|
+
return new Date().toISOString();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
module.exports = {
|
|
198
|
+
normalizeWeixinIncomingMessage,
|
|
199
|
+
};
|
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
const SENSITIVE_FIELD_NAMES = [
|
|
2
|
-
"context_token",
|
|
3
|
-
"bot_token",
|
|
4
|
-
"token",
|
|
5
|
-
"authorization",
|
|
6
|
-
"Authorization",
|
|
7
|
-
"aeskey",
|
|
8
|
-
"aes_key",
|
|
9
|
-
"upload_param",
|
|
10
|
-
"encrypted_query_param",
|
|
11
|
-
];
|
|
12
|
-
|
|
13
|
-
const JSON_FIELD_PATTERN = new RegExp(
|
|
14
|
-
`"(${SENSITIVE_FIELD_NAMES.join("|")})"\\s*:\\s*"[^"]*"`,
|
|
15
|
-
"g"
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
const QUERY_FIELD_PATTERN = new RegExp(
|
|
19
|
-
`([?&](?:${SENSITIVE_FIELD_NAMES.join("|")})=)[^&\\s]+`,
|
|
20
|
-
"g"
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
const BEARER_PATTERN = /\bBearer\s+[A-Za-z0-9._~+/=-]+/g;
|
|
24
|
-
|
|
25
|
-
function redactSensitiveText(input, maxLen = 800) {
|
|
26
|
-
const text = typeof input === "string" ? input : String(input || "");
|
|
27
|
-
if (!text) {
|
|
28
|
-
return "";
|
|
29
|
-
}
|
|
30
|
-
const redacted = text
|
|
31
|
-
.replace(JSON_FIELD_PATTERN, '"$1":"<redacted>"')
|
|
32
|
-
.replace(QUERY_FIELD_PATTERN, "$1<redacted>")
|
|
33
|
-
.replace(BEARER_PATTERN, "Bearer <redacted>");
|
|
34
|
-
if (redacted.length <= maxLen) {
|
|
35
|
-
return redacted;
|
|
36
|
-
}
|
|
37
|
-
return `${redacted.slice(0, maxLen)}…(truncated, totalLen=${redacted.length})`;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
module.exports = { redactSensitiveText };
|
|
41
|
-
|
|
1
|
+
const SENSITIVE_FIELD_NAMES = [
|
|
2
|
+
"context_token",
|
|
3
|
+
"bot_token",
|
|
4
|
+
"token",
|
|
5
|
+
"authorization",
|
|
6
|
+
"Authorization",
|
|
7
|
+
"aeskey",
|
|
8
|
+
"aes_key",
|
|
9
|
+
"upload_param",
|
|
10
|
+
"encrypted_query_param",
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
const JSON_FIELD_PATTERN = new RegExp(
|
|
14
|
+
`"(${SENSITIVE_FIELD_NAMES.join("|")})"\\s*:\\s*"[^"]*"`,
|
|
15
|
+
"g"
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const QUERY_FIELD_PATTERN = new RegExp(
|
|
19
|
+
`([?&](?:${SENSITIVE_FIELD_NAMES.join("|")})=)[^&\\s]+`,
|
|
20
|
+
"g"
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const BEARER_PATTERN = /\bBearer\s+[A-Za-z0-9._~+/=-]+/g;
|
|
24
|
+
|
|
25
|
+
function redactSensitiveText(input, maxLen = 800) {
|
|
26
|
+
const text = typeof input === "string" ? input : String(input || "");
|
|
27
|
+
if (!text) {
|
|
28
|
+
return "";
|
|
29
|
+
}
|
|
30
|
+
const redacted = text
|
|
31
|
+
.replace(JSON_FIELD_PATTERN, '"$1":"<redacted>"')
|
|
32
|
+
.replace(QUERY_FIELD_PATTERN, "$1<redacted>")
|
|
33
|
+
.replace(BEARER_PATTERN, "Bearer <redacted>");
|
|
34
|
+
if (redacted.length <= maxLen) {
|
|
35
|
+
return redacted;
|
|
36
|
+
}
|
|
37
|
+
return `${redacted.slice(0, maxLen)}…(truncated, totalLen=${redacted.length})`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = { redactSensitiveText };
|
|
41
|
+
|
|
@@ -1,101 +1,101 @@
|
|
|
1
|
-
const fs = require("fs");
|
|
2
|
-
const path = require("path");
|
|
3
|
-
|
|
4
|
-
class ReminderQueueStore {
|
|
5
|
-
constructor({ filePath }) {
|
|
6
|
-
this.filePath = filePath;
|
|
7
|
-
this.state = { reminders: [] };
|
|
8
|
-
this.ensureParentDirectory();
|
|
9
|
-
this.load();
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
ensureParentDirectory() {
|
|
13
|
-
fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
load() {
|
|
17
|
-
try {
|
|
18
|
-
const raw = fs.readFileSync(this.filePath, "utf8");
|
|
19
|
-
const parsed = JSON.parse(raw);
|
|
20
|
-
const reminders = Array.isArray(parsed?.reminders) ? parsed.reminders : [];
|
|
21
|
-
this.state = {
|
|
22
|
-
reminders: reminders
|
|
23
|
-
.map(normalizeReminder)
|
|
24
|
-
.filter(Boolean)
|
|
25
|
-
.sort((left, right) => left.dueAtMs - right.dueAtMs),
|
|
26
|
-
};
|
|
27
|
-
} catch {
|
|
28
|
-
this.state = { reminders: [] };
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
save() {
|
|
33
|
-
fs.writeFileSync(this.filePath, JSON.stringify(this.state, null, 2));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
enqueue(reminder) {
|
|
37
|
-
this.load();
|
|
38
|
-
const normalized = normalizeReminder(reminder);
|
|
39
|
-
if (!normalized) {
|
|
40
|
-
throw new Error("invalid reminder");
|
|
41
|
-
}
|
|
42
|
-
this.state.reminders.push(normalized);
|
|
43
|
-
this.state.reminders.sort((left, right) => left.dueAtMs - right.dueAtMs);
|
|
44
|
-
this.save();
|
|
45
|
-
return normalized;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
listDue(nowMs = Date.now()) {
|
|
49
|
-
this.load();
|
|
50
|
-
const due = [];
|
|
51
|
-
const pending = [];
|
|
52
|
-
|
|
53
|
-
for (const reminder of this.state.reminders) {
|
|
54
|
-
if (reminder.dueAtMs <= nowMs) {
|
|
55
|
-
due.push(reminder);
|
|
56
|
-
} else {
|
|
57
|
-
pending.push(reminder);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (due.length) {
|
|
62
|
-
this.state.reminders = pending;
|
|
63
|
-
this.save();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return due;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
peekNextDueAtMs() {
|
|
70
|
-
this.load();
|
|
71
|
-
const first = this.state.reminders[0];
|
|
72
|
-
return Number.isFinite(first?.dueAtMs) ? first.dueAtMs : 0;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function normalizeReminder(reminder) {
|
|
77
|
-
if (!reminder || typeof reminder !== "object") {
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
const id = typeof reminder.id === "string" ? reminder.id.trim() : "";
|
|
81
|
-
const accountId = typeof reminder.accountId === "string" ? reminder.accountId.trim() : "";
|
|
82
|
-
const senderId = typeof reminder.senderId === "string" ? reminder.senderId.trim() : "";
|
|
83
|
-
const contextToken = typeof reminder.contextToken === "string" ? reminder.contextToken.trim() : "";
|
|
84
|
-
const text = typeof reminder.text === "string" ? reminder.text.trim() : "";
|
|
85
|
-
const dueAtMs = Number(reminder.dueAtMs);
|
|
86
|
-
const createdAt = typeof reminder.createdAt === "string" ? reminder.createdAt.trim() : "";
|
|
87
|
-
if (!id || !accountId || !senderId || !contextToken || !text || !Number.isFinite(dueAtMs) || dueAtMs <= 0) {
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
return {
|
|
91
|
-
id,
|
|
92
|
-
accountId,
|
|
93
|
-
senderId,
|
|
94
|
-
contextToken,
|
|
95
|
-
text,
|
|
96
|
-
dueAtMs,
|
|
97
|
-
createdAt: createdAt || new Date().toISOString(),
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
module.exports = { ReminderQueueStore };
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
class ReminderQueueStore {
|
|
5
|
+
constructor({ filePath }) {
|
|
6
|
+
this.filePath = filePath;
|
|
7
|
+
this.state = { reminders: [] };
|
|
8
|
+
this.ensureParentDirectory();
|
|
9
|
+
this.load();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
ensureParentDirectory() {
|
|
13
|
+
fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
load() {
|
|
17
|
+
try {
|
|
18
|
+
const raw = fs.readFileSync(this.filePath, "utf8");
|
|
19
|
+
const parsed = JSON.parse(raw);
|
|
20
|
+
const reminders = Array.isArray(parsed?.reminders) ? parsed.reminders : [];
|
|
21
|
+
this.state = {
|
|
22
|
+
reminders: reminders
|
|
23
|
+
.map(normalizeReminder)
|
|
24
|
+
.filter(Boolean)
|
|
25
|
+
.sort((left, right) => left.dueAtMs - right.dueAtMs),
|
|
26
|
+
};
|
|
27
|
+
} catch {
|
|
28
|
+
this.state = { reminders: [] };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
save() {
|
|
33
|
+
fs.writeFileSync(this.filePath, JSON.stringify(this.state, null, 2));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
enqueue(reminder) {
|
|
37
|
+
this.load();
|
|
38
|
+
const normalized = normalizeReminder(reminder);
|
|
39
|
+
if (!normalized) {
|
|
40
|
+
throw new Error("invalid reminder");
|
|
41
|
+
}
|
|
42
|
+
this.state.reminders.push(normalized);
|
|
43
|
+
this.state.reminders.sort((left, right) => left.dueAtMs - right.dueAtMs);
|
|
44
|
+
this.save();
|
|
45
|
+
return normalized;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
listDue(nowMs = Date.now()) {
|
|
49
|
+
this.load();
|
|
50
|
+
const due = [];
|
|
51
|
+
const pending = [];
|
|
52
|
+
|
|
53
|
+
for (const reminder of this.state.reminders) {
|
|
54
|
+
if (reminder.dueAtMs <= nowMs) {
|
|
55
|
+
due.push(reminder);
|
|
56
|
+
} else {
|
|
57
|
+
pending.push(reminder);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (due.length) {
|
|
62
|
+
this.state.reminders = pending;
|
|
63
|
+
this.save();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return due;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
peekNextDueAtMs() {
|
|
70
|
+
this.load();
|
|
71
|
+
const first = this.state.reminders[0];
|
|
72
|
+
return Number.isFinite(first?.dueAtMs) ? first.dueAtMs : 0;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function normalizeReminder(reminder) {
|
|
77
|
+
if (!reminder || typeof reminder !== "object") {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
const id = typeof reminder.id === "string" ? reminder.id.trim() : "";
|
|
81
|
+
const accountId = typeof reminder.accountId === "string" ? reminder.accountId.trim() : "";
|
|
82
|
+
const senderId = typeof reminder.senderId === "string" ? reminder.senderId.trim() : "";
|
|
83
|
+
const contextToken = typeof reminder.contextToken === "string" ? reminder.contextToken.trim() : "";
|
|
84
|
+
const text = typeof reminder.text === "string" ? reminder.text.trim() : "";
|
|
85
|
+
const dueAtMs = Number(reminder.dueAtMs);
|
|
86
|
+
const createdAt = typeof reminder.createdAt === "string" ? reminder.createdAt.trim() : "";
|
|
87
|
+
if (!id || !accountId || !senderId || !contextToken || !text || !Number.isFinite(dueAtMs) || dueAtMs <= 0) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
id,
|
|
92
|
+
accountId,
|
|
93
|
+
senderId,
|
|
94
|
+
contextToken,
|
|
95
|
+
text,
|
|
96
|
+
dueAtMs,
|
|
97
|
+
createdAt: createdAt || new Date().toISOString(),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
module.exports = { ReminderQueueStore };
|