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,98 +1,98 @@
|
|
|
1
|
-
const crypto = require("crypto");
|
|
1
|
+
const crypto = require("crypto");
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const fs = require("fs/promises");
|
|
4
4
|
|
|
5
5
|
const { getUploadUrl, sendMessage } = require("./api");
|
|
6
6
|
const { getUploadUrlV2, sendMessageV2 } = require("./api-v2");
|
|
7
7
|
const { getMimeFromFilename } = require("./media-mime");
|
|
8
|
-
|
|
9
|
-
const WEIXIN_MEDIA_TYPE = {
|
|
10
|
-
IMAGE: 1,
|
|
11
|
-
VIDEO: 2,
|
|
12
|
-
FILE: 3,
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
function encryptAesEcb(plaintext, key) {
|
|
16
|
-
const cipher = crypto.createCipheriv("aes-128-ecb", key, null);
|
|
17
|
-
return Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function aesEcbPaddedSize(plaintextSize) {
|
|
21
|
-
return Math.ceil((plaintextSize + 1) / 16) * 16;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function buildCdnUploadUrl({ cdnBaseUrl, uploadParam, filekey }) {
|
|
25
|
-
return `${cdnBaseUrl}/upload?encrypted_query_param=${encodeURIComponent(uploadParam)}&filekey=${encodeURIComponent(filekey)}`;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async function uploadBufferToCdn({ buf, uploadParam, filekey, cdnBaseUrl, aeskey }) {
|
|
29
|
-
const ciphertext = encryptAesEcb(buf, aeskey);
|
|
30
|
-
const cdnUrl = buildCdnUploadUrl({ cdnBaseUrl, uploadParam, filekey });
|
|
31
|
-
const response = await fetch(cdnUrl, {
|
|
32
|
-
method: "POST",
|
|
33
|
-
headers: { "Content-Type": "application/octet-stream" },
|
|
34
|
-
body: new Uint8Array(ciphertext),
|
|
35
|
-
});
|
|
36
|
-
if (response.status !== 200) {
|
|
37
|
-
const errMsg = response.headers.get("x-error-message") || await response.text();
|
|
38
|
-
throw new Error(`CDN upload failed: ${errMsg || response.status}`);
|
|
39
|
-
}
|
|
40
|
-
const downloadParam = response.headers.get("x-encrypted-param") || "";
|
|
41
|
-
if (!downloadParam) {
|
|
42
|
-
throw new Error("CDN upload response missing x-encrypted-param header");
|
|
43
|
-
}
|
|
44
|
-
return { downloadParam };
|
|
45
|
-
}
|
|
46
|
-
|
|
8
|
+
|
|
9
|
+
const WEIXIN_MEDIA_TYPE = {
|
|
10
|
+
IMAGE: 1,
|
|
11
|
+
VIDEO: 2,
|
|
12
|
+
FILE: 3,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function encryptAesEcb(plaintext, key) {
|
|
16
|
+
const cipher = crypto.createCipheriv("aes-128-ecb", key, null);
|
|
17
|
+
return Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function aesEcbPaddedSize(plaintextSize) {
|
|
21
|
+
return Math.ceil((plaintextSize + 1) / 16) * 16;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function buildCdnUploadUrl({ cdnBaseUrl, uploadParam, filekey }) {
|
|
25
|
+
return `${cdnBaseUrl}/upload?encrypted_query_param=${encodeURIComponent(uploadParam)}&filekey=${encodeURIComponent(filekey)}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function uploadBufferToCdn({ buf, uploadParam, filekey, cdnBaseUrl, aeskey }) {
|
|
29
|
+
const ciphertext = encryptAesEcb(buf, aeskey);
|
|
30
|
+
const cdnUrl = buildCdnUploadUrl({ cdnBaseUrl, uploadParam, filekey });
|
|
31
|
+
const response = await fetch(cdnUrl, {
|
|
32
|
+
method: "POST",
|
|
33
|
+
headers: { "Content-Type": "application/octet-stream" },
|
|
34
|
+
body: new Uint8Array(ciphertext),
|
|
35
|
+
});
|
|
36
|
+
if (response.status !== 200) {
|
|
37
|
+
const errMsg = response.headers.get("x-error-message") || await response.text();
|
|
38
|
+
throw new Error(`CDN upload failed: ${errMsg || response.status}`);
|
|
39
|
+
}
|
|
40
|
+
const downloadParam = response.headers.get("x-encrypted-param") || "";
|
|
41
|
+
if (!downloadParam) {
|
|
42
|
+
throw new Error("CDN upload response missing x-encrypted-param header");
|
|
43
|
+
}
|
|
44
|
+
return { downloadParam };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
47
|
async function uploadMediaToWeixin({ filePath, toUserId, opts, cdnBaseUrl, mediaType, getUploadUrlImpl }) {
|
|
48
48
|
const plaintext = await fs.readFile(filePath);
|
|
49
49
|
const rawsize = plaintext.length;
|
|
50
50
|
const rawfilemd5 = crypto.createHash("md5").update(plaintext).digest("hex");
|
|
51
|
-
const filesize = aesEcbPaddedSize(rawsize);
|
|
52
|
-
const filekey = crypto.randomBytes(16).toString("hex");
|
|
53
|
-
const aeskey = crypto.randomBytes(16);
|
|
54
|
-
|
|
51
|
+
const filesize = aesEcbPaddedSize(rawsize);
|
|
52
|
+
const filekey = crypto.randomBytes(16).toString("hex");
|
|
53
|
+
const aeskey = crypto.randomBytes(16);
|
|
54
|
+
|
|
55
55
|
const uploadUrlResp = await getUploadUrlImpl({
|
|
56
56
|
...opts,
|
|
57
57
|
filekey,
|
|
58
58
|
media_type: mediaType,
|
|
59
|
-
to_user_id: toUserId,
|
|
60
|
-
rawsize,
|
|
61
|
-
rawfilemd5,
|
|
62
|
-
filesize,
|
|
63
|
-
no_need_thumb: true,
|
|
64
|
-
aeskey: aeskey.toString("hex"),
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
const uploadParam = uploadUrlResp?.upload_param || "";
|
|
68
|
-
if (!uploadParam) {
|
|
69
|
-
throw new Error("getUploadUrl returned no upload_param");
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const { downloadParam } = await uploadBufferToCdn({
|
|
73
|
-
buf: plaintext,
|
|
74
|
-
uploadParam,
|
|
75
|
-
filekey,
|
|
76
|
-
cdnBaseUrl,
|
|
77
|
-
aeskey,
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
downloadEncryptedQueryParam: downloadParam,
|
|
82
|
-
aeskey: aeskey.toString("hex"),
|
|
83
|
-
fileSize: rawsize,
|
|
84
|
-
fileSizeCiphertext: filesize,
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function buildMediaRef(uploaded) {
|
|
89
|
-
return {
|
|
90
|
-
encrypt_query_param: uploaded.downloadEncryptedQueryParam,
|
|
91
|
-
aes_key: Buffer.from(uploaded.aeskey).toString("base64"),
|
|
92
|
-
encrypt_type: 1,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
59
|
+
to_user_id: toUserId,
|
|
60
|
+
rawsize,
|
|
61
|
+
rawfilemd5,
|
|
62
|
+
filesize,
|
|
63
|
+
no_need_thumb: true,
|
|
64
|
+
aeskey: aeskey.toString("hex"),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const uploadParam = uploadUrlResp?.upload_param || "";
|
|
68
|
+
if (!uploadParam) {
|
|
69
|
+
throw new Error("getUploadUrl returned no upload_param");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const { downloadParam } = await uploadBufferToCdn({
|
|
73
|
+
buf: plaintext,
|
|
74
|
+
uploadParam,
|
|
75
|
+
filekey,
|
|
76
|
+
cdnBaseUrl,
|
|
77
|
+
aeskey,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
downloadEncryptedQueryParam: downloadParam,
|
|
82
|
+
aeskey: aeskey.toString("hex"),
|
|
83
|
+
fileSize: rawsize,
|
|
84
|
+
fileSizeCiphertext: filesize,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function buildMediaRef(uploaded) {
|
|
89
|
+
return {
|
|
90
|
+
encrypt_query_param: uploaded.downloadEncryptedQueryParam,
|
|
91
|
+
aes_key: Buffer.from(uploaded.aeskey).toString("base64"),
|
|
92
|
+
encrypt_type: 1,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
96
|
async function sendMediaItem({ to, item, contextToken, baseUrl, token, routeTag = "", clientVersion = "", sendMessageImpl }) {
|
|
97
97
|
await sendMessageImpl({
|
|
98
98
|
baseUrl,
|
|
@@ -103,14 +103,14 @@ async function sendMediaItem({ to, item, contextToken, baseUrl, token, routeTag
|
|
|
103
103
|
msg: {
|
|
104
104
|
from_user_id: "",
|
|
105
105
|
to_user_id: to,
|
|
106
|
-
client_id: crypto.randomUUID(),
|
|
107
|
-
message_type: 2,
|
|
108
|
-
message_state: 2,
|
|
109
|
-
item_list: [item],
|
|
110
|
-
context_token: contextToken,
|
|
111
|
-
},
|
|
112
|
-
},
|
|
113
|
-
});
|
|
106
|
+
client_id: crypto.randomUUID(),
|
|
107
|
+
message_type: 2,
|
|
108
|
+
message_state: 2,
|
|
109
|
+
item_list: [item],
|
|
110
|
+
context_token: contextToken,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
function resolveWeixinMediaApi(apiVariant) {
|
|
@@ -205,11 +205,11 @@ async function sendWeixinMediaFile({
|
|
|
205
205
|
});
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
|
-
|
|
209
|
-
if (mime.startsWith("video/")) {
|
|
210
|
-
const uploaded = await uploadMediaToWeixin({
|
|
211
|
-
filePath,
|
|
212
|
-
toUserId: to,
|
|
208
|
+
|
|
209
|
+
if (mime.startsWith("video/")) {
|
|
210
|
+
const uploaded = await uploadMediaToWeixin({
|
|
211
|
+
filePath,
|
|
212
|
+
toUserId: to,
|
|
213
213
|
opts: uploadOpts,
|
|
214
214
|
cdnBaseUrl,
|
|
215
215
|
mediaType: WEIXIN_MEDIA_TYPE.VIDEO,
|
|
@@ -226,14 +226,14 @@ async function sendWeixinMediaFile({
|
|
|
226
226
|
item: {
|
|
227
227
|
type: 5,
|
|
228
228
|
video_item: {
|
|
229
|
-
media: buildMediaRef(uploaded),
|
|
230
|
-
video_size: uploaded.fileSizeCiphertext,
|
|
231
|
-
},
|
|
232
|
-
},
|
|
233
|
-
});
|
|
234
|
-
return { kind: "video", fileName: path.basename(filePath) };
|
|
235
|
-
}
|
|
236
|
-
|
|
229
|
+
media: buildMediaRef(uploaded),
|
|
230
|
+
video_size: uploaded.fileSizeCiphertext,
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
return { kind: "video", fileName: path.basename(filePath) };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
237
|
return sendFileFallback({
|
|
238
238
|
filePath,
|
|
239
239
|
to,
|