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
@@ -2,30 +2,30 @@ const fs = require("fs");
2
2
  const path = require("path");
3
3
  const { redactSensitiveText } = require("./redact");
4
4
  const { getStableWechatUin } = require("./protocol");
5
-
6
- function readChannelVersion() {
7
- try {
8
- const pkgPath = path.resolve(__dirname, "../../../../package.json");
9
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
10
- return pkg.version || "unknown";
11
- } catch {
12
- return "unknown";
13
- }
14
- }
15
-
16
- const CHANNEL_VERSION = readChannelVersion();
17
- const DEFAULT_LONG_POLL_TIMEOUT_MS = 35_000;
18
- const DEFAULT_API_TIMEOUT_MS = 15_000;
19
- const DEFAULT_CONFIG_TIMEOUT_MS = 10_000;
20
-
21
- function buildBaseInfo() {
22
- return { channel_version: CHANNEL_VERSION };
23
- }
24
-
25
- function ensureTrailingSlash(url) {
26
- return url.endsWith("/") ? url : `${url}/`;
27
- }
28
-
5
+
6
+ function readChannelVersion() {
7
+ try {
8
+ const pkgPath = path.resolve(__dirname, "../../../../package.json");
9
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
10
+ return pkg.version || "unknown";
11
+ } catch {
12
+ return "unknown";
13
+ }
14
+ }
15
+
16
+ const CHANNEL_VERSION = readChannelVersion();
17
+ const DEFAULT_LONG_POLL_TIMEOUT_MS = 35_000;
18
+ const DEFAULT_API_TIMEOUT_MS = 15_000;
19
+ const DEFAULT_CONFIG_TIMEOUT_MS = 10_000;
20
+
21
+ function buildBaseInfo() {
22
+ return { channel_version: CHANNEL_VERSION };
23
+ }
24
+
25
+ function ensureTrailingSlash(url) {
26
+ return url.endsWith("/") ? url : `${url}/`;
27
+ }
28
+
29
29
  function buildHeaders(opts) {
30
30
  const headers = {
31
31
  "Content-Type": "application/json",
@@ -33,148 +33,148 @@ function buildHeaders(opts) {
33
33
  "Content-Length": String(Buffer.byteLength(opts.body, "utf8")),
34
34
  "X-WECHAT-UIN": getStableWechatUin(),
35
35
  };
36
- if (opts.token && String(opts.token).trim()) {
37
- headers.Authorization = `Bearer ${String(opts.token).trim()}`;
38
- }
39
- return headers;
40
- }
41
-
42
- async function apiFetch(params) {
43
- const base = ensureTrailingSlash(params.baseUrl);
44
- const url = new URL(params.endpoint, base);
45
- const headers = buildHeaders({ token: params.token, body: params.body });
46
- const controller = new AbortController();
47
- const timer = setTimeout(() => controller.abort(), params.timeoutMs);
48
- try {
49
- const response = await fetch(url.toString(), {
50
- method: "POST",
51
- headers,
52
- body: params.body,
53
- signal: controller.signal,
54
- });
55
- clearTimeout(timer);
56
- const rawText = await response.text();
57
- if (!response.ok) {
58
- throw new Error(`${params.label} ${response.status}: ${redactSensitiveText(rawText)}`);
59
- }
60
- return rawText;
61
- } catch (error) {
62
- clearTimeout(timer);
63
- throw error;
64
- }
65
- }
66
-
67
- function parseApiJson(rawText, label) {
68
- try {
69
- return JSON.parse(rawText);
70
- } catch {
71
- throw new Error(`${label} returned invalid JSON: ${redactSensitiveText(rawText)}`);
72
- }
73
- }
74
-
75
- function assertApiSuccess(response, label) {
76
- const ret = response?.ret;
77
- const errcode = response?.errcode;
78
- if ((ret !== undefined && ret !== 0) || (errcode !== undefined && errcode !== 0)) {
79
- const errmsg = typeof response?.errmsg === "string" ? response.errmsg.trim() : "";
80
- throw new Error(`${label} ret=${ret ?? ""} errcode=${errcode ?? ""} errmsg=${redactSensitiveText(errmsg)}`);
81
- }
82
- return response;
83
- }
84
-
85
- async function getUpdates(params) {
86
- const timeout = params.timeoutMs || DEFAULT_LONG_POLL_TIMEOUT_MS;
87
- try {
88
- const rawText = await apiFetch({
89
- baseUrl: params.baseUrl,
90
- endpoint: "ilink/bot/getupdates",
91
- body: JSON.stringify({
92
- get_updates_buf: params.get_updates_buf || "",
93
- base_info: buildBaseInfo(),
94
- }),
95
- token: params.token,
96
- timeoutMs: timeout,
97
- label: "getUpdates",
98
- });
99
- return parseApiJson(rawText, "getUpdates");
100
- } catch (error) {
101
- if (error instanceof Error && error.name === "AbortError") {
102
- return { ret: 0, msgs: [], get_updates_buf: params.get_updates_buf || "" };
103
- }
104
- throw error;
105
- }
106
- }
107
-
108
- async function sendMessage(params) {
109
- const rawText = await apiFetch({
110
- baseUrl: params.baseUrl,
111
- endpoint: "ilink/bot/sendmessage",
112
- body: JSON.stringify({ ...params.body, base_info: buildBaseInfo() }),
113
- token: params.token,
114
- timeoutMs: params.timeoutMs || DEFAULT_API_TIMEOUT_MS,
115
- label: "sendMessage",
116
- });
117
- assertApiSuccess(parseApiJson(rawText, "sendMessage"), "sendMessage");
118
- }
119
-
120
- async function getUploadUrl(params) {
121
- const rawText = await apiFetch({
122
- baseUrl: params.baseUrl,
123
- endpoint: "ilink/bot/getuploadurl",
124
- body: JSON.stringify({
125
- filekey: params.filekey,
126
- media_type: params.media_type,
127
- to_user_id: params.to_user_id,
128
- rawsize: params.rawsize,
129
- rawfilemd5: params.rawfilemd5,
130
- filesize: params.filesize,
131
- thumb_rawsize: params.thumb_rawsize,
132
- thumb_rawfilemd5: params.thumb_rawfilemd5,
133
- thumb_filesize: params.thumb_filesize,
134
- no_need_thumb: params.no_need_thumb,
135
- aeskey: params.aeskey,
136
- base_info: buildBaseInfo(),
137
- }),
138
- token: params.token,
139
- timeoutMs: params.timeoutMs || DEFAULT_API_TIMEOUT_MS,
140
- label: "getUploadUrl",
141
- });
142
- return assertApiSuccess(parseApiJson(rawText, "getUploadUrl"), "getUploadUrl");
143
- }
144
-
145
- async function getConfig(params) {
146
- const rawText = await apiFetch({
147
- baseUrl: params.baseUrl,
148
- endpoint: "ilink/bot/getconfig",
149
- body: JSON.stringify({
150
- ilink_user_id: params.ilinkUserId,
151
- context_token: params.contextToken,
152
- base_info: buildBaseInfo(),
153
- }),
154
- token: params.token,
155
- timeoutMs: params.timeoutMs || DEFAULT_CONFIG_TIMEOUT_MS,
156
- label: "getConfig",
157
- });
158
- return assertApiSuccess(parseApiJson(rawText, "getConfig"), "getConfig");
159
- }
160
-
161
- async function sendTyping(params) {
162
- const rawText = await apiFetch({
163
- baseUrl: params.baseUrl,
164
- endpoint: "ilink/bot/sendtyping",
165
- body: JSON.stringify({ ...params.body, base_info: buildBaseInfo() }),
166
- token: params.token,
167
- timeoutMs: params.timeoutMs || DEFAULT_CONFIG_TIMEOUT_MS,
168
- label: "sendTyping",
169
- });
170
- assertApiSuccess(parseApiJson(rawText, "sendTyping"), "sendTyping");
171
- }
172
-
173
- module.exports = {
174
- buildBaseInfo,
175
- getConfig,
176
- getUploadUrl,
177
- getUpdates,
178
- sendMessage,
179
- sendTyping,
180
- };
36
+ if (opts.token && String(opts.token).trim()) {
37
+ headers.Authorization = `Bearer ${String(opts.token).trim()}`;
38
+ }
39
+ return headers;
40
+ }
41
+
42
+ async function apiFetch(params) {
43
+ const base = ensureTrailingSlash(params.baseUrl);
44
+ const url = new URL(params.endpoint, base);
45
+ const headers = buildHeaders({ token: params.token, body: params.body });
46
+ const controller = new AbortController();
47
+ const timer = setTimeout(() => controller.abort(), params.timeoutMs);
48
+ try {
49
+ const response = await fetch(url.toString(), {
50
+ method: "POST",
51
+ headers,
52
+ body: params.body,
53
+ signal: controller.signal,
54
+ });
55
+ clearTimeout(timer);
56
+ const rawText = await response.text();
57
+ if (!response.ok) {
58
+ throw new Error(`${params.label} ${response.status}: ${redactSensitiveText(rawText)}`);
59
+ }
60
+ return rawText;
61
+ } catch (error) {
62
+ clearTimeout(timer);
63
+ throw error;
64
+ }
65
+ }
66
+
67
+ function parseApiJson(rawText, label) {
68
+ try {
69
+ return JSON.parse(rawText);
70
+ } catch {
71
+ throw new Error(`${label} returned invalid JSON: ${redactSensitiveText(rawText)}`);
72
+ }
73
+ }
74
+
75
+ function assertApiSuccess(response, label) {
76
+ const ret = response?.ret;
77
+ const errcode = response?.errcode;
78
+ if ((ret !== undefined && ret !== 0) || (errcode !== undefined && errcode !== 0)) {
79
+ const errmsg = typeof response?.errmsg === "string" ? response.errmsg.trim() : "";
80
+ throw new Error(`${label} ret=${ret ?? ""} errcode=${errcode ?? ""} errmsg=${redactSensitiveText(errmsg)}`);
81
+ }
82
+ return response;
83
+ }
84
+
85
+ async function getUpdates(params) {
86
+ const timeout = params.timeoutMs || DEFAULT_LONG_POLL_TIMEOUT_MS;
87
+ try {
88
+ const rawText = await apiFetch({
89
+ baseUrl: params.baseUrl,
90
+ endpoint: "ilink/bot/getupdates",
91
+ body: JSON.stringify({
92
+ get_updates_buf: params.get_updates_buf || "",
93
+ base_info: buildBaseInfo(),
94
+ }),
95
+ token: params.token,
96
+ timeoutMs: timeout,
97
+ label: "getUpdates",
98
+ });
99
+ return parseApiJson(rawText, "getUpdates");
100
+ } catch (error) {
101
+ if (error instanceof Error && error.name === "AbortError") {
102
+ return { ret: 0, msgs: [], get_updates_buf: params.get_updates_buf || "" };
103
+ }
104
+ throw error;
105
+ }
106
+ }
107
+
108
+ async function sendMessage(params) {
109
+ const rawText = await apiFetch({
110
+ baseUrl: params.baseUrl,
111
+ endpoint: "ilink/bot/sendmessage",
112
+ body: JSON.stringify({ ...params.body, base_info: buildBaseInfo() }),
113
+ token: params.token,
114
+ timeoutMs: params.timeoutMs || DEFAULT_API_TIMEOUT_MS,
115
+ label: "sendMessage",
116
+ });
117
+ assertApiSuccess(parseApiJson(rawText, "sendMessage"), "sendMessage");
118
+ }
119
+
120
+ async function getUploadUrl(params) {
121
+ const rawText = await apiFetch({
122
+ baseUrl: params.baseUrl,
123
+ endpoint: "ilink/bot/getuploadurl",
124
+ body: JSON.stringify({
125
+ filekey: params.filekey,
126
+ media_type: params.media_type,
127
+ to_user_id: params.to_user_id,
128
+ rawsize: params.rawsize,
129
+ rawfilemd5: params.rawfilemd5,
130
+ filesize: params.filesize,
131
+ thumb_rawsize: params.thumb_rawsize,
132
+ thumb_rawfilemd5: params.thumb_rawfilemd5,
133
+ thumb_filesize: params.thumb_filesize,
134
+ no_need_thumb: params.no_need_thumb,
135
+ aeskey: params.aeskey,
136
+ base_info: buildBaseInfo(),
137
+ }),
138
+ token: params.token,
139
+ timeoutMs: params.timeoutMs || DEFAULT_API_TIMEOUT_MS,
140
+ label: "getUploadUrl",
141
+ });
142
+ return assertApiSuccess(parseApiJson(rawText, "getUploadUrl"), "getUploadUrl");
143
+ }
144
+
145
+ async function getConfig(params) {
146
+ const rawText = await apiFetch({
147
+ baseUrl: params.baseUrl,
148
+ endpoint: "ilink/bot/getconfig",
149
+ body: JSON.stringify({
150
+ ilink_user_id: params.ilinkUserId,
151
+ context_token: params.contextToken,
152
+ base_info: buildBaseInfo(),
153
+ }),
154
+ token: params.token,
155
+ timeoutMs: params.timeoutMs || DEFAULT_CONFIG_TIMEOUT_MS,
156
+ label: "getConfig",
157
+ });
158
+ return assertApiSuccess(parseApiJson(rawText, "getConfig"), "getConfig");
159
+ }
160
+
161
+ async function sendTyping(params) {
162
+ const rawText = await apiFetch({
163
+ baseUrl: params.baseUrl,
164
+ endpoint: "ilink/bot/sendtyping",
165
+ body: JSON.stringify({ ...params.body, base_info: buildBaseInfo() }),
166
+ token: params.token,
167
+ timeoutMs: params.timeoutMs || DEFAULT_CONFIG_TIMEOUT_MS,
168
+ label: "sendTyping",
169
+ });
170
+ assertApiSuccess(parseApiJson(rawText, "sendTyping"), "sendTyping");
171
+ }
172
+
173
+ module.exports = {
174
+ buildBaseInfo,
175
+ getConfig,
176
+ getUploadUrl,
177
+ getUpdates,
178
+ sendMessage,
179
+ sendTyping,
180
+ };
@@ -1,84 +1,84 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- const { normalizeAccountId } = require("./account-store");
4
-
5
- function ensureAccountsDir(config) {
6
- fs.mkdirSync(config.accountsDir, { recursive: true });
7
- }
8
-
9
- function resolveContextTokenPath(config, accountId) {
10
- ensureAccountsDir(config);
11
- return path.join(config.accountsDir, `${normalizeAccountId(accountId)}.context-tokens.json`);
12
- }
13
-
14
- function loadPersistedContextTokens(config, accountId) {
15
- try {
16
- const filePath = resolveContextTokenPath(config, accountId);
17
- if (!fs.existsSync(filePath)) {
18
- return {};
19
- }
20
- const raw = fs.readFileSync(filePath, "utf8");
21
- const parsed = JSON.parse(raw);
22
- if (!parsed || typeof parsed !== "object") {
23
- return {};
24
- }
25
- return Object.fromEntries(
26
- Object.entries(parsed)
27
- .filter(([userId, token]) => typeof userId === "string" && userId.trim() && typeof token === "string" && token.trim())
28
- .map(([userId, token]) => [userId.trim(), token.trim()])
29
- );
30
- } catch {
31
- return {};
32
- }
33
- }
34
-
35
- function savePersistedContextTokens(config, accountId, tokens) {
36
- const normalizedTokens = Object.fromEntries(
37
- Object.entries(tokens || {})
38
- .filter(([userId, token]) => typeof userId === "string" && userId.trim() && typeof token === "string" && token.trim())
39
- .map(([userId, token]) => [userId.trim(), token.trim()])
40
- );
41
- const filePath = resolveContextTokenPath(config, accountId);
42
- fs.writeFileSync(filePath, JSON.stringify(normalizedTokens, null, 2), "utf8");
43
- try {
44
- fs.chmodSync(filePath, 0o600);
45
- } catch {
46
- // best effort
47
- }
48
- return normalizedTokens;
49
- }
50
-
51
- function persistContextToken(config, accountId, userId, token) {
52
- const normalizedUserId = typeof userId === "string" ? userId.trim() : "";
53
- const normalizedToken = typeof token === "string" ? token.trim() : "";
54
- if (!normalizedUserId || !normalizedToken) {
55
- return loadPersistedContextTokens(config, accountId);
56
- }
57
- const existing = loadPersistedContextTokens(config, accountId);
58
- if (existing[normalizedUserId] === normalizedToken) {
59
- return existing;
60
- }
61
- return savePersistedContextTokens(config, accountId, {
62
- ...existing,
63
- [normalizedUserId]: normalizedToken,
64
- });
65
- }
66
-
67
- function clearPersistedContextTokens(config, accountId) {
68
- try {
69
- const filePath = resolveContextTokenPath(config, accountId);
70
- if (fs.existsSync(filePath)) {
71
- fs.unlinkSync(filePath);
72
- }
73
- } catch {
74
- // best effort
75
- }
76
- }
77
-
78
- module.exports = {
79
- clearPersistedContextTokens,
80
- loadPersistedContextTokens,
81
- persistContextToken,
82
- resolveContextTokenPath,
83
- };
84
-
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { normalizeAccountId } = require("./account-store");
4
+
5
+ function ensureAccountsDir(config) {
6
+ fs.mkdirSync(config.accountsDir, { recursive: true });
7
+ }
8
+
9
+ function resolveContextTokenPath(config, accountId) {
10
+ ensureAccountsDir(config);
11
+ return path.join(config.accountsDir, `${normalizeAccountId(accountId)}.context-tokens.json`);
12
+ }
13
+
14
+ function loadPersistedContextTokens(config, accountId) {
15
+ try {
16
+ const filePath = resolveContextTokenPath(config, accountId);
17
+ if (!fs.existsSync(filePath)) {
18
+ return {};
19
+ }
20
+ const raw = fs.readFileSync(filePath, "utf8");
21
+ const parsed = JSON.parse(raw);
22
+ if (!parsed || typeof parsed !== "object") {
23
+ return {};
24
+ }
25
+ return Object.fromEntries(
26
+ Object.entries(parsed)
27
+ .filter(([userId, token]) => typeof userId === "string" && userId.trim() && typeof token === "string" && token.trim())
28
+ .map(([userId, token]) => [userId.trim(), token.trim()])
29
+ );
30
+ } catch {
31
+ return {};
32
+ }
33
+ }
34
+
35
+ function savePersistedContextTokens(config, accountId, tokens) {
36
+ const normalizedTokens = Object.fromEntries(
37
+ Object.entries(tokens || {})
38
+ .filter(([userId, token]) => typeof userId === "string" && userId.trim() && typeof token === "string" && token.trim())
39
+ .map(([userId, token]) => [userId.trim(), token.trim()])
40
+ );
41
+ const filePath = resolveContextTokenPath(config, accountId);
42
+ fs.writeFileSync(filePath, JSON.stringify(normalizedTokens, null, 2), "utf8");
43
+ try {
44
+ fs.chmodSync(filePath, 0o600);
45
+ } catch {
46
+ // best effort
47
+ }
48
+ return normalizedTokens;
49
+ }
50
+
51
+ function persistContextToken(config, accountId, userId, token) {
52
+ const normalizedUserId = typeof userId === "string" ? userId.trim() : "";
53
+ const normalizedToken = typeof token === "string" ? token.trim() : "";
54
+ if (!normalizedUserId || !normalizedToken) {
55
+ return loadPersistedContextTokens(config, accountId);
56
+ }
57
+ const existing = loadPersistedContextTokens(config, accountId);
58
+ if (existing[normalizedUserId] === normalizedToken) {
59
+ return existing;
60
+ }
61
+ return savePersistedContextTokens(config, accountId, {
62
+ ...existing,
63
+ [normalizedUserId]: normalizedToken,
64
+ });
65
+ }
66
+
67
+ function clearPersistedContextTokens(config, accountId) {
68
+ try {
69
+ const filePath = resolveContextTokenPath(config, accountId);
70
+ if (fs.existsSync(filePath)) {
71
+ fs.unlinkSync(filePath);
72
+ }
73
+ } catch {
74
+ // best effort
75
+ }
76
+ }
77
+
78
+ module.exports = {
79
+ clearPersistedContextTokens,
80
+ loadPersistedContextTokens,
81
+ persistContextToken,
82
+ resolveContextTokenPath,
83
+ };
84
+