codeksei 0.1.0

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 (80) hide show
  1. package/LICENSE +661 -0
  2. package/README.en.md +215 -0
  3. package/README.md +259 -0
  4. package/bin/codeksei.js +10 -0
  5. package/bin/cyberboss.js +11 -0
  6. package/package.json +86 -0
  7. package/scripts/install-background-tasks.ps1 +135 -0
  8. package/scripts/open_shared_wechat_thread.sh +94 -0
  9. package/scripts/open_wechat_thread.sh +117 -0
  10. package/scripts/shared-common.js +791 -0
  11. package/scripts/shared-open.js +46 -0
  12. package/scripts/shared-start.js +41 -0
  13. package/scripts/shared-status.js +74 -0
  14. package/scripts/shared-supervisor.js +141 -0
  15. package/scripts/shared-task-runner.ps1 +87 -0
  16. package/scripts/shared-watchdog.js +290 -0
  17. package/scripts/show_shared_status.sh +53 -0
  18. package/scripts/start_shared_app_server.sh +65 -0
  19. package/scripts/start_shared_wechat.sh +108 -0
  20. package/scripts/timeline-screenshot.sh +15 -0
  21. package/scripts/uninstall-background-tasks.ps1 +23 -0
  22. package/src/adapters/channel/weixin/account-store.js +135 -0
  23. package/src/adapters/channel/weixin/api-v2.js +258 -0
  24. package/src/adapters/channel/weixin/api.js +180 -0
  25. package/src/adapters/channel/weixin/context-token-store.js +84 -0
  26. package/src/adapters/channel/weixin/index.js +605 -0
  27. package/src/adapters/channel/weixin/legacy.js +567 -0
  28. package/src/adapters/channel/weixin/login-common.js +63 -0
  29. package/src/adapters/channel/weixin/login-legacy.js +124 -0
  30. package/src/adapters/channel/weixin/login-v2.js +186 -0
  31. package/src/adapters/channel/weixin/media-mime.js +22 -0
  32. package/src/adapters/channel/weixin/media-receive.js +370 -0
  33. package/src/adapters/channel/weixin/media-send.js +331 -0
  34. package/src/adapters/channel/weixin/message-utils-v2.js +282 -0
  35. package/src/adapters/channel/weixin/message-utils.js +199 -0
  36. package/src/adapters/channel/weixin/protocol.js +77 -0
  37. package/src/adapters/channel/weixin/redact.js +41 -0
  38. package/src/adapters/channel/weixin/reminder-queue-store.js +101 -0
  39. package/src/adapters/channel/weixin/sync-buffer-store.js +35 -0
  40. package/src/adapters/runtime/codex/events.js +252 -0
  41. package/src/adapters/runtime/codex/index.js +502 -0
  42. package/src/adapters/runtime/codex/message-utils.js +141 -0
  43. package/src/adapters/runtime/codex/model-catalog.js +106 -0
  44. package/src/adapters/runtime/codex/protocol-leak-monitor.js +75 -0
  45. package/src/adapters/runtime/codex/rpc-client.js +443 -0
  46. package/src/adapters/runtime/codex/session-store.js +376 -0
  47. package/src/app/channel-send-file-cli.js +57 -0
  48. package/src/app/diary-write-cli.js +620 -0
  49. package/src/app/note-auto-cli.js +201 -0
  50. package/src/app/note-sync-cli.js +130 -0
  51. package/src/app/project-radar-cli.js +165 -0
  52. package/src/app/reminder-write-cli.js +210 -0
  53. package/src/app/review-cli.js +134 -0
  54. package/src/app/system-checkin-poller.js +100 -0
  55. package/src/app/system-send-cli.js +129 -0
  56. package/src/app/timeline-event-cli.js +273 -0
  57. package/src/app/timeline-screenshot-cli.js +109 -0
  58. package/src/core/app.js +1810 -0
  59. package/src/core/branding.js +167 -0
  60. package/src/core/command-registry.js +609 -0
  61. package/src/core/config.js +84 -0
  62. package/src/core/default-targets.js +163 -0
  63. package/src/core/durable-note-schema.js +325 -0
  64. package/src/core/instructions-template.js +31 -0
  65. package/src/core/note-sync.js +433 -0
  66. package/src/core/project-radar.js +402 -0
  67. package/src/core/review-semantic.js +524 -0
  68. package/src/core/review.js +1081 -0
  69. package/src/core/shared-bridge-heartbeat.js +140 -0
  70. package/src/core/stream-delivery.js +990 -0
  71. package/src/core/system-message-dispatcher.js +68 -0
  72. package/src/core/system-message-queue-store.js +128 -0
  73. package/src/core/thread-state-store.js +135 -0
  74. package/src/core/timeline-screenshot-queue-store.js +134 -0
  75. package/src/core/workspace-alias.js +163 -0
  76. package/src/core/workspace-bootstrap.js +338 -0
  77. package/src/index.js +270 -0
  78. package/src/integrations/timeline/index.js +191 -0
  79. package/templates/weixin-instructions.md +53 -0
  80. package/templates/weixin-operations.md +69 -0
@@ -0,0 +1,402 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { spawnSync } = require("child_process");
4
+
5
+ function loadProjectRadarConfig(config = {}) {
6
+ const workspaceRoot = normalizeDisplayPath(path.resolve(String(config.workspaceRoot || process.cwd())));
7
+ const configFile = normalizeDisplayPath(path.resolve(String(
8
+ config.projectRadarConfigFile || path.join(workspaceRoot, ".codex", "code-projects.json")
9
+ )));
10
+
11
+ let raw = "";
12
+ try {
13
+ raw = fs.readFileSync(configFile, "utf8");
14
+ } catch {
15
+ throw new Error(`找不到代码项目配置: ${configFile}`);
16
+ }
17
+
18
+ let parsed = {};
19
+ try {
20
+ parsed = JSON.parse(raw);
21
+ } catch (error) {
22
+ throw new Error(`代码项目配置不是合法 JSON: ${configFile} (${formatErrorMessage(error)})`);
23
+ }
24
+
25
+ const projects = Array.isArray(parsed?.projects)
26
+ ? parsed.projects
27
+ .map((entry) => normalizeProjectEntry(entry, workspaceRoot))
28
+ .filter(Boolean)
29
+ : [];
30
+ if (!projects.length) {
31
+ throw new Error(`代码项目配置里没有可用 projects: ${configFile}`);
32
+ }
33
+
34
+ return {
35
+ workspaceRoot,
36
+ configFile,
37
+ projects,
38
+ };
39
+ }
40
+
41
+ function listTrackedProjects(config = {}) {
42
+ const radarConfig = loadProjectRadarConfig(config);
43
+ return radarConfig.projects.map((project) => ({
44
+ slug: project.slug,
45
+ title: project.title,
46
+ aliases: [...project.aliases],
47
+ repoRoot: project.repoRoot,
48
+ notePath: project.noteAbsolutePath,
49
+ timelineLabel: project.timelineLabel,
50
+ }));
51
+ }
52
+
53
+ function collectProjectRadars(config = {}, options = {}) {
54
+ const radarConfig = loadProjectRadarConfig(config);
55
+ const selectedProjects = selectProjects(radarConfig.projects, options.project);
56
+ return {
57
+ generatedAt: new Date().toISOString(),
58
+ workspaceRoot: radarConfig.workspaceRoot,
59
+ configFile: radarConfig.configFile,
60
+ projects: selectedProjects.map((project) => collectSingleProjectRadar(project, options)),
61
+ };
62
+ }
63
+
64
+ function selectProjects(projects, selectedProject) {
65
+ const normalizedSelectedProject = normalizeText(selectedProject).toLowerCase();
66
+ if (!normalizedSelectedProject) {
67
+ return projects;
68
+ }
69
+ const matched = projects.filter((project) => matchesProjectSelector(project, normalizedSelectedProject));
70
+ if (matched.length) {
71
+ return matched;
72
+ }
73
+ const available = projects.map((project) => project.slug).join(", ");
74
+ throw new Error(`找不到代码项目: ${selectedProject};当前可用 slug: ${available}`);
75
+ }
76
+
77
+ function matchesProjectSelector(project, selector) {
78
+ if (project.slug.toLowerCase() === selector) {
79
+ return true;
80
+ }
81
+ if (project.title.toLowerCase() === selector) {
82
+ return true;
83
+ }
84
+ return project.aliases.some((alias) => alias.toLowerCase() === selector);
85
+ }
86
+
87
+ function collectSingleProjectRadar(project, options = {}) {
88
+ const commitLimit = clampPositiveInteger(options.commits, 5);
89
+ const changeLimit = clampPositiveInteger(options.changes, 20);
90
+ const overviewFiles = project.overviewFiles.map((relativePath) => buildWorkspaceFileInfo(project.repoRoot, relativePath, "overview"));
91
+ const graphReport = project.graphReportPath
92
+ ? buildWorkspaceFileInfo(project.repoRoot, project.graphReportPath, "graph")
93
+ : null;
94
+ const readFirst = [
95
+ project.noteAbsolutePath ? {
96
+ kind: "workspace-note",
97
+ path: project.noteAbsolutePath,
98
+ exists: isReadableFile(project.noteAbsolutePath),
99
+ } : null,
100
+ ...overviewFiles.filter((file) => file.exists),
101
+ graphReport?.exists ? graphReport : null,
102
+ ].filter(Boolean);
103
+
104
+ const repoExists = isReadableDirectory(project.repoRoot);
105
+ const repoFacts = repoExists ? collectGitFacts(project.repoRoot, { commitLimit, changeLimit }) : buildMissingRepoFacts(project.repoRoot);
106
+
107
+ return {
108
+ slug: project.slug,
109
+ title: project.title,
110
+ repoRoot: project.repoRoot,
111
+ notePath: project.noteAbsolutePath,
112
+ timelineLabel: project.timelineLabel,
113
+ readFirst,
114
+ overviewFiles,
115
+ graphReport,
116
+ git: repoFacts,
117
+ };
118
+ }
119
+
120
+ function collectGitFacts(repoRoot, { commitLimit, changeLimit }) {
121
+ const repoCheck = runGit(repoRoot, ["rev-parse", "--show-toplevel"]);
122
+ if (!repoCheck.ok) {
123
+ return {
124
+ ok: false,
125
+ reason: "not-a-git-repo",
126
+ message: repoCheck.stderr || `不是 git 仓库: ${repoRoot}`,
127
+ branch: "",
128
+ upstream: "",
129
+ ahead: 0,
130
+ behind: 0,
131
+ dirty: false,
132
+ summary: {
133
+ staged: 0,
134
+ unstaged: 0,
135
+ untracked: 0,
136
+ conflicted: 0,
137
+ total: 0,
138
+ },
139
+ statusEntries: [],
140
+ recentCommits: [],
141
+ };
142
+ }
143
+
144
+ const branch = resolveBranchName(repoRoot);
145
+ const upstream = resolveUpstreamName(repoRoot);
146
+ const aheadBehind = resolveAheadBehind(repoRoot, upstream);
147
+ const statusResult = runGit(repoRoot, ["status", "--short", "--untracked-files=all"]);
148
+ const allStatusEntries = parseStatusEntries(statusResult.stdout);
149
+ const statusEntries = allStatusEntries.slice(0, changeLimit);
150
+ const statusSummary = summarizeStatusEntries(allStatusEntries);
151
+ const recentCommits = parseRecentCommits(runGit(repoRoot, [
152
+ "log",
153
+ "--date=iso-strict",
154
+ "--pretty=format:%H%x09%cI%x09%s",
155
+ "-n",
156
+ String(commitLimit),
157
+ ]).stdout);
158
+
159
+ return {
160
+ ok: true,
161
+ reason: "",
162
+ message: "",
163
+ branch,
164
+ upstream,
165
+ ahead: aheadBehind.ahead,
166
+ behind: aheadBehind.behind,
167
+ dirty: statusSummary.total > 0,
168
+ summary: statusSummary,
169
+ statusEntries,
170
+ recentCommits,
171
+ };
172
+ }
173
+
174
+ function buildMissingRepoFacts(repoRoot) {
175
+ return {
176
+ ok: false,
177
+ reason: "missing-repo",
178
+ message: `仓库目录不存在: ${repoRoot}`,
179
+ branch: "",
180
+ upstream: "",
181
+ ahead: 0,
182
+ behind: 0,
183
+ dirty: false,
184
+ summary: {
185
+ staged: 0,
186
+ unstaged: 0,
187
+ untracked: 0,
188
+ conflicted: 0,
189
+ total: 0,
190
+ },
191
+ statusEntries: [],
192
+ recentCommits: [],
193
+ };
194
+ }
195
+
196
+ function buildWorkspaceFileInfo(root, relativePath, kind) {
197
+ const normalizedRelativePath = normalizeRelativePath(relativePath);
198
+ const absolutePath = normalizedRelativePath
199
+ ? normalizeDisplayPath(path.resolve(root, ...normalizedRelativePath.split("/")))
200
+ : "";
201
+ return {
202
+ kind,
203
+ relativePath: normalizedRelativePath,
204
+ path: absolutePath,
205
+ exists: isReadableFile(absolutePath),
206
+ };
207
+ }
208
+
209
+ function normalizeProjectEntry(entry, workspaceRoot) {
210
+ const project = entry && typeof entry === "object" ? entry : {};
211
+ const slug = normalizeText(project.slug);
212
+ const repoRoot = normalizeText(project.repoRoot);
213
+ const notePath = normalizeRelativePath(project.notePath);
214
+ if (!slug || !repoRoot || !notePath) {
215
+ return null;
216
+ }
217
+ return {
218
+ slug,
219
+ title: normalizeText(project.title) || slug,
220
+ aliases: normalizeAliases(project.aliases),
221
+ repoRoot: normalizeDisplayPath(path.resolve(repoRoot)),
222
+ notePath,
223
+ noteAbsolutePath: normalizeDisplayPath(path.resolve(workspaceRoot, ...notePath.split("/"))),
224
+ overviewFiles: normalizeRelativePathList(project.overviewFiles),
225
+ graphReportPath: normalizeRelativePath(project.graphReportPath),
226
+ timelineLabel: normalizeText(project.timelineLabel),
227
+ };
228
+ }
229
+
230
+ function normalizeAliases(rawAliases) {
231
+ return Array.isArray(rawAliases)
232
+ ? rawAliases.map((alias) => normalizeText(alias)).filter(Boolean)
233
+ : [];
234
+ }
235
+
236
+ function normalizeRelativePathList(rawPaths) {
237
+ return Array.isArray(rawPaths)
238
+ ? rawPaths.map((value) => normalizeRelativePath(value)).filter(Boolean)
239
+ : [];
240
+ }
241
+
242
+ function resolveBranchName(repoRoot) {
243
+ const symbolic = runGit(repoRoot, ["symbolic-ref", "--quiet", "--short", "HEAD"]);
244
+ if (symbolic.ok && normalizeText(symbolic.stdout)) {
245
+ return normalizeText(symbolic.stdout);
246
+ }
247
+ const detached = runGit(repoRoot, ["rev-parse", "--short", "HEAD"]);
248
+ if (detached.ok && normalizeText(detached.stdout)) {
249
+ return `(detached ${normalizeText(detached.stdout)})`;
250
+ }
251
+ return "";
252
+ }
253
+
254
+ function resolveUpstreamName(repoRoot) {
255
+ const upstream = runGit(repoRoot, ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{upstream}"]);
256
+ return upstream.ok ? normalizeText(upstream.stdout) : "";
257
+ }
258
+
259
+ function resolveAheadBehind(repoRoot, upstream) {
260
+ if (!normalizeText(upstream)) {
261
+ return { ahead: 0, behind: 0 };
262
+ }
263
+ const counts = runGit(repoRoot, ["rev-list", "--left-right", "--count", "@{upstream}...HEAD"]);
264
+ if (!counts.ok) {
265
+ return { ahead: 0, behind: 0 };
266
+ }
267
+ const [behindText, aheadText] = normalizeText(counts.stdout).split(/\s+/);
268
+ return {
269
+ ahead: Number.parseInt(aheadText, 10) || 0,
270
+ behind: Number.parseInt(behindText, 10) || 0,
271
+ };
272
+ }
273
+
274
+ function parseStatusEntries(stdout) {
275
+ return String(stdout || "")
276
+ .split(/\r?\n/)
277
+ .map((line) => line.trimEnd())
278
+ .filter(Boolean)
279
+ .map((line) => {
280
+ const code = line.slice(0, 2);
281
+ const filePath = line.slice(3).trim();
282
+ return {
283
+ code,
284
+ path: filePath,
285
+ };
286
+ });
287
+ }
288
+
289
+ function summarizeStatusEntries(entries) {
290
+ const summary = {
291
+ staged: 0,
292
+ unstaged: 0,
293
+ untracked: 0,
294
+ conflicted: 0,
295
+ total: Array.isArray(entries) ? entries.length : 0,
296
+ };
297
+ for (const entry of Array.isArray(entries) ? entries : []) {
298
+ const code = String(entry.code || "");
299
+ if (code === "??") {
300
+ summary.untracked += 1;
301
+ continue;
302
+ }
303
+ if (/[U]/.test(code) || code === "AA" || code === "DD") {
304
+ summary.conflicted += 1;
305
+ }
306
+ if (code[0] && code[0] !== " ") {
307
+ summary.staged += 1;
308
+ }
309
+ if (code[1] && code[1] !== " ") {
310
+ summary.unstaged += 1;
311
+ }
312
+ }
313
+ return summary;
314
+ }
315
+
316
+ function parseRecentCommits(stdout) {
317
+ return String(stdout || "")
318
+ .split(/\r?\n/)
319
+ .map((line) => line.trim())
320
+ .filter(Boolean)
321
+ .map((line) => {
322
+ const [hash, committedAt, subject] = line.split("\t");
323
+ return {
324
+ hash: normalizeText(hash),
325
+ shortHash: normalizeText(hash).slice(0, 7),
326
+ committedAt: normalizeText(committedAt),
327
+ subject: normalizeText(subject),
328
+ };
329
+ });
330
+ }
331
+
332
+ function runGit(repoRoot, args) {
333
+ const result = spawnSync("git", ["-c", "core.quotepath=false", ...args], {
334
+ cwd: repoRoot,
335
+ encoding: "utf8",
336
+ windowsHide: true,
337
+ timeout: 15_000,
338
+ });
339
+ return {
340
+ ok: result.status === 0 && !result.error,
341
+ status: result.status,
342
+ stdout: normalizeCommandStdout(result.stdout),
343
+ stderr: normalizeCommandStderr(result.stderr || result.error?.message || ""),
344
+ };
345
+ }
346
+
347
+ function clampPositiveInteger(value, fallback) {
348
+ const parsed = Number.parseInt(String(value || ""), 10);
349
+ if (!Number.isFinite(parsed) || parsed <= 0) {
350
+ return fallback;
351
+ }
352
+ return parsed;
353
+ }
354
+
355
+ function normalizeRelativePath(value) {
356
+ return normalizeText(value)
357
+ .replace(/\\/g, "/")
358
+ .replace(/^\/+/, "")
359
+ .replace(/\/+$/, "");
360
+ }
361
+
362
+ function isReadableDirectory(directoryPath) {
363
+ try {
364
+ return fs.statSync(directoryPath).isDirectory();
365
+ } catch {
366
+ return false;
367
+ }
368
+ }
369
+
370
+ function isReadableFile(filePath) {
371
+ try {
372
+ return fs.statSync(filePath).isFile();
373
+ } catch {
374
+ return false;
375
+ }
376
+ }
377
+
378
+ function normalizeCommandStdout(value) {
379
+ return String(value || "").replace(/\r\n/g, "\n").trimEnd();
380
+ }
381
+
382
+ function normalizeCommandStderr(value) {
383
+ return String(value || "").replace(/\r\n/g, "\n").trim();
384
+ }
385
+
386
+ function normalizeDisplayPath(targetPath) {
387
+ return normalizeText(targetPath).replace(/\\/g, "/");
388
+ }
389
+
390
+ function normalizeText(value) {
391
+ return typeof value === "string" ? value.trim() : "";
392
+ }
393
+
394
+ function formatErrorMessage(error) {
395
+ return error instanceof Error ? error.message : String(error || "unknown error");
396
+ }
397
+
398
+ module.exports = {
399
+ collectProjectRadars,
400
+ listTrackedProjects,
401
+ loadProjectRadarConfig,
402
+ };