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.
- package/LICENSE +661 -0
- package/README.en.md +215 -0
- package/README.md +259 -0
- package/bin/codeksei.js +10 -0
- package/bin/cyberboss.js +11 -0
- package/package.json +86 -0
- package/scripts/install-background-tasks.ps1 +135 -0
- package/scripts/open_shared_wechat_thread.sh +94 -0
- package/scripts/open_wechat_thread.sh +117 -0
- package/scripts/shared-common.js +791 -0
- package/scripts/shared-open.js +46 -0
- package/scripts/shared-start.js +41 -0
- package/scripts/shared-status.js +74 -0
- package/scripts/shared-supervisor.js +141 -0
- package/scripts/shared-task-runner.ps1 +87 -0
- package/scripts/shared-watchdog.js +290 -0
- package/scripts/show_shared_status.sh +53 -0
- package/scripts/start_shared_app_server.sh +65 -0
- package/scripts/start_shared_wechat.sh +108 -0
- package/scripts/timeline-screenshot.sh +15 -0
- package/scripts/uninstall-background-tasks.ps1 +23 -0
- package/src/adapters/channel/weixin/account-store.js +135 -0
- package/src/adapters/channel/weixin/api-v2.js +258 -0
- package/src/adapters/channel/weixin/api.js +180 -0
- package/src/adapters/channel/weixin/context-token-store.js +84 -0
- package/src/adapters/channel/weixin/index.js +605 -0
- package/src/adapters/channel/weixin/legacy.js +567 -0
- package/src/adapters/channel/weixin/login-common.js +63 -0
- package/src/adapters/channel/weixin/login-legacy.js +124 -0
- package/src/adapters/channel/weixin/login-v2.js +186 -0
- package/src/adapters/channel/weixin/media-mime.js +22 -0
- package/src/adapters/channel/weixin/media-receive.js +370 -0
- package/src/adapters/channel/weixin/media-send.js +331 -0
- package/src/adapters/channel/weixin/message-utils-v2.js +282 -0
- package/src/adapters/channel/weixin/message-utils.js +199 -0
- package/src/adapters/channel/weixin/protocol.js +77 -0
- package/src/adapters/channel/weixin/redact.js +41 -0
- package/src/adapters/channel/weixin/reminder-queue-store.js +101 -0
- package/src/adapters/channel/weixin/sync-buffer-store.js +35 -0
- package/src/adapters/runtime/codex/events.js +252 -0
- package/src/adapters/runtime/codex/index.js +502 -0
- package/src/adapters/runtime/codex/message-utils.js +141 -0
- package/src/adapters/runtime/codex/model-catalog.js +106 -0
- package/src/adapters/runtime/codex/protocol-leak-monitor.js +75 -0
- package/src/adapters/runtime/codex/rpc-client.js +443 -0
- package/src/adapters/runtime/codex/session-store.js +376 -0
- package/src/app/channel-send-file-cli.js +57 -0
- package/src/app/diary-write-cli.js +620 -0
- package/src/app/note-auto-cli.js +201 -0
- package/src/app/note-sync-cli.js +130 -0
- package/src/app/project-radar-cli.js +165 -0
- package/src/app/reminder-write-cli.js +210 -0
- package/src/app/review-cli.js +134 -0
- package/src/app/system-checkin-poller.js +100 -0
- package/src/app/system-send-cli.js +129 -0
- package/src/app/timeline-event-cli.js +273 -0
- package/src/app/timeline-screenshot-cli.js +109 -0
- package/src/core/app.js +1810 -0
- package/src/core/branding.js +167 -0
- package/src/core/command-registry.js +609 -0
- package/src/core/config.js +84 -0
- package/src/core/default-targets.js +163 -0
- package/src/core/durable-note-schema.js +325 -0
- package/src/core/instructions-template.js +31 -0
- package/src/core/note-sync.js +433 -0
- package/src/core/project-radar.js +402 -0
- package/src/core/review-semantic.js +524 -0
- package/src/core/review.js +1081 -0
- package/src/core/shared-bridge-heartbeat.js +140 -0
- package/src/core/stream-delivery.js +990 -0
- package/src/core/system-message-dispatcher.js +68 -0
- package/src/core/system-message-queue-store.js +128 -0
- package/src/core/thread-state-store.js +135 -0
- package/src/core/timeline-screenshot-queue-store.js +134 -0
- package/src/core/workspace-alias.js +163 -0
- package/src/core/workspace-bootstrap.js +338 -0
- package/src/index.js +270 -0
- package/src/integrations/timeline/index.js +191 -0
- package/templates/weixin-instructions.md +53 -0
- package/templates/weixin-operations.md +69 -0
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
const DEFAULT_BOOTSTRAP_PROFILE = Object.freeze({
|
|
5
|
+
primaryFiles: [
|
|
6
|
+
{
|
|
7
|
+
path: "AGENTS.md",
|
|
8
|
+
role: "workspace routing and boundary contract",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
path: "README.md",
|
|
12
|
+
role: "workspace overview and operating instructions",
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
path: "Home.md",
|
|
16
|
+
role: "workspace home and current control page",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
path: ".codex/AGENT_GUIDE.md",
|
|
20
|
+
role: "agent write/update rules for this workspace",
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
conditionalFiles: [
|
|
24
|
+
{
|
|
25
|
+
path: ".codex/timeline/README.md",
|
|
26
|
+
role: "timeline write/read contract for this workspace",
|
|
27
|
+
when: "timeline read/write/build/screenshot work",
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
recentFiles: [],
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
let workspaceBootstrapConfigCache = {
|
|
34
|
+
filePath: "",
|
|
35
|
+
mtimeMs: -1,
|
|
36
|
+
value: {},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
function buildWorkspaceContinuityInstructions(workspaceRoot, config = {}) {
|
|
40
|
+
const normalizedWorkspaceRoot = normalizeWorkspaceRoot(workspaceRoot);
|
|
41
|
+
if (!normalizedWorkspaceRoot) {
|
|
42
|
+
return "";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const profile = resolveWorkspaceBootstrapProfile(normalizedWorkspaceRoot, config);
|
|
46
|
+
// Keep the bootstrap read set intentionally narrow and curated. This path is
|
|
47
|
+
// supposed to rehydrate durable context for a thread, not silently turn a
|
|
48
|
+
// workspace switch into a broad vault scan.
|
|
49
|
+
const primaryFiles = collectExistingFiles(normalizedWorkspaceRoot, profile.primaryFiles);
|
|
50
|
+
const recentFiles = collectRecentFiles(normalizedWorkspaceRoot, profile.recentFiles);
|
|
51
|
+
const conditionalFiles = collectExistingFiles(normalizedWorkspaceRoot, profile.conditionalFiles);
|
|
52
|
+
|
|
53
|
+
const primarySequence = primaryFiles.concat(recentFiles);
|
|
54
|
+
if (!primarySequence.length && !conditionalFiles.length) {
|
|
55
|
+
return "";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const lines = [
|
|
59
|
+
"This workspace keeps durable continuity in local notes.",
|
|
60
|
+
`Current workspace root: ${normalizedWorkspaceRoot}`,
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
if (primarySequence.length) {
|
|
64
|
+
lines.push(
|
|
65
|
+
"Before your first substantive reply in this workspace, recover context from these files in order:"
|
|
66
|
+
);
|
|
67
|
+
for (const [index, file] of primarySequence.entries()) {
|
|
68
|
+
lines.push(`${index + 1}. ${file.absolutePath} - ${file.role}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
for (const group of groupConditionalFiles(conditionalFiles)) {
|
|
73
|
+
lines.push("");
|
|
74
|
+
lines.push(`If the current user request touches ${group.when}, also read:`);
|
|
75
|
+
for (const file of group.files) {
|
|
76
|
+
lines.push(`- ${file.absolutePath} - ${file.role}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
lines.push("");
|
|
81
|
+
lines.push("Use AGENTS / Home / workspace README as durable truth.");
|
|
82
|
+
if (recentFiles.length) {
|
|
83
|
+
lines.push("Treat any auto-discovered recent note only as recent context, not as a permanent rulebook.");
|
|
84
|
+
}
|
|
85
|
+
lines.push("Do not paste these file paths or summarize them back unless the user explicitly asks.");
|
|
86
|
+
return lines.join("\n").trim();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function resolveWorkspaceBootstrapProfile(workspaceRoot, config = {}) {
|
|
90
|
+
const externalConfig = loadWorkspaceBootstrapConfig(config);
|
|
91
|
+
const externalDefaults = externalConfig.defaults || externalConfig.default || {};
|
|
92
|
+
const baseProfile = mergeProfiles(DEFAULT_BOOTSTRAP_PROFILE, externalDefaults);
|
|
93
|
+
const workspaceOverrides = selectWorkspaceOverrides(externalConfig.workspaces, workspaceRoot);
|
|
94
|
+
return mergeProfiles(baseProfile, workspaceOverrides || {});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function loadWorkspaceBootstrapConfig(config = {}) {
|
|
98
|
+
const filePath = normalizeText(config.workspaceBootstrapConfigFile);
|
|
99
|
+
if (!filePath) {
|
|
100
|
+
return {};
|
|
101
|
+
}
|
|
102
|
+
let stats = null;
|
|
103
|
+
try {
|
|
104
|
+
stats = fs.statSync(filePath);
|
|
105
|
+
} catch {
|
|
106
|
+
workspaceBootstrapConfigCache = {
|
|
107
|
+
filePath: "",
|
|
108
|
+
mtimeMs: -1,
|
|
109
|
+
value: {},
|
|
110
|
+
};
|
|
111
|
+
return {};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (!stats.isFile()) {
|
|
115
|
+
return {};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const normalizedPath = normalizeDisplayPath(filePath);
|
|
119
|
+
if (
|
|
120
|
+
workspaceBootstrapConfigCache.filePath === normalizedPath
|
|
121
|
+
&& workspaceBootstrapConfigCache.mtimeMs === stats.mtimeMs
|
|
122
|
+
) {
|
|
123
|
+
return workspaceBootstrapConfigCache.value;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
128
|
+
const parsed = JSON.parse(raw);
|
|
129
|
+
const value = parsed && typeof parsed === "object" ? parsed : {};
|
|
130
|
+
workspaceBootstrapConfigCache = {
|
|
131
|
+
filePath: normalizedPath,
|
|
132
|
+
mtimeMs: stats.mtimeMs,
|
|
133
|
+
value,
|
|
134
|
+
};
|
|
135
|
+
return value;
|
|
136
|
+
} catch {
|
|
137
|
+
workspaceBootstrapConfigCache = {
|
|
138
|
+
filePath: normalizedPath,
|
|
139
|
+
mtimeMs: stats.mtimeMs,
|
|
140
|
+
value: {},
|
|
141
|
+
};
|
|
142
|
+
return {};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function selectWorkspaceOverrides(workspaces, workspaceRoot) {
|
|
147
|
+
if (!workspaces || typeof workspaces !== "object") {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
const normalizedWorkspaceRoot = normalizeDisplayPath(workspaceRoot);
|
|
151
|
+
for (const [candidateRoot, profile] of Object.entries(workspaces)) {
|
|
152
|
+
if (normalizeDisplayPath(candidateRoot) === normalizedWorkspaceRoot) {
|
|
153
|
+
return profile;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function mergeProfiles(baseProfile, overrideProfile) {
|
|
160
|
+
const override = overrideProfile && typeof overrideProfile === "object" ? overrideProfile : {};
|
|
161
|
+
return {
|
|
162
|
+
primaryFiles: hasOwn(override, "primaryFiles")
|
|
163
|
+
? normalizeFileCandidates(override.primaryFiles)
|
|
164
|
+
: normalizeFileCandidates(baseProfile.primaryFiles),
|
|
165
|
+
conditionalFiles: hasOwn(override, "conditionalFiles")
|
|
166
|
+
? normalizeFileCandidates(override.conditionalFiles)
|
|
167
|
+
: normalizeFileCandidates(baseProfile.conditionalFiles),
|
|
168
|
+
recentFiles: hasOwn(override, "recentFiles")
|
|
169
|
+
? normalizeRecentFileSpecs(override.recentFiles)
|
|
170
|
+
: normalizeRecentFileSpecs(baseProfile.recentFiles),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function normalizeFileCandidates(rawCandidates) {
|
|
175
|
+
const candidates = Array.isArray(rawCandidates) ? rawCandidates : [];
|
|
176
|
+
return candidates
|
|
177
|
+
.map((candidate) => normalizeFileCandidate(candidate))
|
|
178
|
+
.filter(Boolean);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function normalizeFileCandidate(rawCandidate) {
|
|
182
|
+
const candidate = rawCandidate && typeof rawCandidate === "object" ? rawCandidate : {};
|
|
183
|
+
const relativePath = normalizeRelativePath(candidate.path || candidate.relativePath);
|
|
184
|
+
if (!relativePath) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
relativePath,
|
|
189
|
+
role: normalizeText(candidate.role) || "workspace entry file",
|
|
190
|
+
when: normalizeText(candidate.when),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function normalizeRecentFileSpecs(rawSpecs) {
|
|
195
|
+
const specs = Array.isArray(rawSpecs) ? rawSpecs : [];
|
|
196
|
+
return specs
|
|
197
|
+
.map((spec) => normalizeRecentFileSpec(spec))
|
|
198
|
+
.filter(Boolean);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function normalizeRecentFileSpec(rawSpec) {
|
|
202
|
+
const spec = rawSpec && typeof rawSpec === "object" ? rawSpec : {};
|
|
203
|
+
const directory = normalizeRelativePath(spec.directory);
|
|
204
|
+
const patternText = normalizeText(spec.pattern);
|
|
205
|
+
if (!directory || !patternText) {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
let pattern = null;
|
|
209
|
+
try {
|
|
210
|
+
pattern = new RegExp(patternText);
|
|
211
|
+
} catch {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
const maxCount = Math.max(1, Number.parseInt(spec.maxCount, 10) || 1);
|
|
215
|
+
return {
|
|
216
|
+
directory,
|
|
217
|
+
pattern,
|
|
218
|
+
role: normalizeText(spec.role) || "newest workspace note",
|
|
219
|
+
maxCount,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function collectExistingFiles(workspaceRoot, candidates) {
|
|
224
|
+
const normalizedCandidates = Array.isArray(candidates) ? candidates : [];
|
|
225
|
+
const files = [];
|
|
226
|
+
for (const candidate of normalizedCandidates) {
|
|
227
|
+
const relativePath = normalizeRelativePath(candidate?.relativePath);
|
|
228
|
+
if (!relativePath) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
const absolutePath = path.join(workspaceRoot, ...relativePath.split("/"));
|
|
232
|
+
if (!isReadableFile(absolutePath)) {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
files.push({
|
|
236
|
+
absolutePath: normalizeDisplayPath(absolutePath),
|
|
237
|
+
role: normalizeText(candidate?.role) || "workspace entry file",
|
|
238
|
+
when: normalizeText(candidate?.when),
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
return files;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function collectRecentFiles(workspaceRoot, specs) {
|
|
245
|
+
const normalizedSpecs = Array.isArray(specs) ? specs : [];
|
|
246
|
+
const files = [];
|
|
247
|
+
for (const spec of normalizedSpecs) {
|
|
248
|
+
const directoryPath = path.join(workspaceRoot, ...String(spec.directory || "").split("/"));
|
|
249
|
+
if (!isReadableDirectory(directoryPath)) {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
const entries = fs.readdirSync(directoryPath, { withFileTypes: true })
|
|
253
|
+
.filter((entry) => entry.isFile() && spec.pattern.test(entry.name))
|
|
254
|
+
.map((entry) => entry.name)
|
|
255
|
+
.sort((left, right) => right.localeCompare(left))
|
|
256
|
+
.slice(0, spec.maxCount);
|
|
257
|
+
for (const entryName of entries) {
|
|
258
|
+
files.push({
|
|
259
|
+
absolutePath: normalizeDisplayPath(path.join(directoryPath, entryName)),
|
|
260
|
+
role: spec.role,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return files;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function groupConditionalFiles(files) {
|
|
268
|
+
const groups = [];
|
|
269
|
+
const byWhen = new Map();
|
|
270
|
+
for (const file of Array.isArray(files) ? files : []) {
|
|
271
|
+
const when = normalizeText(file.when);
|
|
272
|
+
if (!when) {
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
if (!byWhen.has(when)) {
|
|
276
|
+
byWhen.set(when, []);
|
|
277
|
+
groups.push({
|
|
278
|
+
when,
|
|
279
|
+
files: byWhen.get(when),
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
byWhen.get(when).push(file);
|
|
283
|
+
}
|
|
284
|
+
return groups;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function isReadableFile(filePath) {
|
|
288
|
+
try {
|
|
289
|
+
return fs.statSync(filePath).isFile();
|
|
290
|
+
} catch {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function isReadableDirectory(directoryPath) {
|
|
296
|
+
try {
|
|
297
|
+
return fs.statSync(directoryPath).isDirectory();
|
|
298
|
+
} catch {
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function normalizeWorkspaceRoot(workspaceRoot) {
|
|
304
|
+
const normalized = normalizeText(workspaceRoot);
|
|
305
|
+
if (!normalized) {
|
|
306
|
+
return "";
|
|
307
|
+
}
|
|
308
|
+
return normalizeDisplayPath(normalized);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function normalizeRelativePath(value) {
|
|
312
|
+
if (Array.isArray(value)) {
|
|
313
|
+
return value
|
|
314
|
+
.map((segment) => normalizeText(segment))
|
|
315
|
+
.filter(Boolean)
|
|
316
|
+
.join("/");
|
|
317
|
+
}
|
|
318
|
+
return normalizeText(value)
|
|
319
|
+
.replace(/\\/g, "/")
|
|
320
|
+
.replace(/^\/+/, "")
|
|
321
|
+
.replace(/\/+$/, "");
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function normalizeDisplayPath(targetPath) {
|
|
325
|
+
return normalizeText(targetPath).replace(/\\/g, "/");
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function normalizeText(value) {
|
|
329
|
+
return typeof value === "string" ? value.trim() : "";
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function hasOwn(value, key) {
|
|
333
|
+
return Object.prototype.hasOwnProperty.call(value || {}, key);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
module.exports = {
|
|
337
|
+
buildWorkspaceContinuityInstructions,
|
|
338
|
+
};
|
package/src/index.js
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const os = require("os");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const dotenv = require("dotenv");
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
PACKAGE_NAME,
|
|
8
|
+
ensureCompatHomeEnv,
|
|
9
|
+
ensureStateDirectory,
|
|
10
|
+
listEnvFileCandidates,
|
|
11
|
+
} = require("./core/branding");
|
|
12
|
+
const { readConfig } = require("./core/config");
|
|
13
|
+
const { renderInstructionTemplate } = require("./core/instructions-template");
|
|
14
|
+
const { CyberbossApp } = require("./core/app");
|
|
15
|
+
const { createTimelineIntegration } = require("./integrations/timeline");
|
|
16
|
+
const { runDiaryWriteCommand } = require("./app/diary-write-cli");
|
|
17
|
+
const { runReminderWriteCommand } = require("./app/reminder-write-cli");
|
|
18
|
+
const { runChannelSendFileCommand } = require("./app/channel-send-file-cli");
|
|
19
|
+
const { runNoteAutoCommand, runNoteMaybeCommand } = require("./app/note-auto-cli");
|
|
20
|
+
const { runNoteSyncCommand } = require("./app/note-sync-cli");
|
|
21
|
+
const { runProjectRadarCommand } = require("./app/project-radar-cli");
|
|
22
|
+
const { runReviewCommand } = require("./app/review-cli");
|
|
23
|
+
const { runTimelineEventCommand } = require("./app/timeline-event-cli");
|
|
24
|
+
const { runTimelineScreenshotCommand } = require("./app/timeline-screenshot-cli");
|
|
25
|
+
const { runSystemCheckinPoller } = require("./app/system-checkin-poller");
|
|
26
|
+
const { runSystemSendCommand } = require("./app/system-send-cli");
|
|
27
|
+
const {
|
|
28
|
+
buildTerminalHelpText,
|
|
29
|
+
buildTerminalTopicHelp,
|
|
30
|
+
isPlannedTerminalTopic,
|
|
31
|
+
} = require("./core/command-registry");
|
|
32
|
+
|
|
33
|
+
function ensureDefaultStateDirectory() {
|
|
34
|
+
ensureStateDirectory();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function loadEnv() {
|
|
38
|
+
ensureDefaultStateDirectory();
|
|
39
|
+
const candidates = listEnvFileCandidates();
|
|
40
|
+
for (const envPath of candidates) {
|
|
41
|
+
if (!fs.existsSync(envPath)) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
dotenv.config({ path: envPath });
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
dotenv.config();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function ensureRuntimeEnv() {
|
|
51
|
+
ensureCompatHomeEnv({ fallbackRoot: path.resolve(__dirname, "..") });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function ensureBootstrapFiles(config) {
|
|
55
|
+
ensureInstructionsTemplate(config);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function ensureInstructionsTemplate(config) {
|
|
59
|
+
const filePath = typeof config?.weixinInstructionsFile === "string"
|
|
60
|
+
? config.weixinInstructionsFile.trim()
|
|
61
|
+
: "";
|
|
62
|
+
if (!filePath || fs.existsSync(filePath)) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const templatePath = path.resolve(__dirname, "..", "templates", "weixin-instructions.md");
|
|
67
|
+
let template = "";
|
|
68
|
+
try {
|
|
69
|
+
template = fs.readFileSync(templatePath, "utf8");
|
|
70
|
+
} catch {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const userName = String(config?.userName || "").trim() || "用户";
|
|
75
|
+
const content = renderInstructionTemplate(template, {
|
|
76
|
+
...config,
|
|
77
|
+
userName,
|
|
78
|
+
}).trimEnd() + "\n";
|
|
79
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
80
|
+
fs.writeFileSync(filePath, content, "utf8");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function printHelp() {
|
|
84
|
+
console.log(buildTerminalHelpText());
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let runtimeErrorHooksInstalled = false;
|
|
88
|
+
|
|
89
|
+
function installRuntimeErrorHooks() {
|
|
90
|
+
if (runtimeErrorHooksInstalled) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
runtimeErrorHooksInstalled = true;
|
|
94
|
+
|
|
95
|
+
process.on("unhandledRejection", (reason) => {
|
|
96
|
+
const message = reason instanceof Error ? reason.stack || reason.message : String(reason);
|
|
97
|
+
console.error(`[${PACKAGE_NAME}] unhandled rejection ${message}`);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
process.on("uncaughtException", (error) => {
|
|
101
|
+
const message = error instanceof Error ? error.stack || error.message : String(error);
|
|
102
|
+
console.error(`[${PACKAGE_NAME}] uncaught exception ${message}`);
|
|
103
|
+
process.exitCode = 1;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function main() {
|
|
108
|
+
loadEnv();
|
|
109
|
+
ensureRuntimeEnv();
|
|
110
|
+
installRuntimeErrorHooks();
|
|
111
|
+
const argv = process.argv.slice(2);
|
|
112
|
+
const config = readConfig();
|
|
113
|
+
ensureBootstrapFiles(config);
|
|
114
|
+
const command = config.mode || "help";
|
|
115
|
+
const subcommand = argv[1] || "";
|
|
116
|
+
let app = null;
|
|
117
|
+
const getApp = () => {
|
|
118
|
+
if (!app) {
|
|
119
|
+
app = new CyberbossApp(config);
|
|
120
|
+
}
|
|
121
|
+
return app;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
if (command === "help" || command === "--help" || command === "-h") {
|
|
125
|
+
const topicHelp = subcommand ? buildTerminalTopicHelp(subcommand) : "";
|
|
126
|
+
console.log(topicHelp || buildTerminalHelpText());
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (isPlannedTerminalTopic(command)) {
|
|
131
|
+
const topicHelp = buildTerminalTopicHelp(command);
|
|
132
|
+
const subcommandArgs = argv.slice(2);
|
|
133
|
+
const wantsSubcommandHelp = subcommandArgs.includes("--help") || subcommandArgs.includes("-h");
|
|
134
|
+
if (subcommand === "help" || !subcommand) {
|
|
135
|
+
console.log(topicHelp);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (command === "diary" && subcommand === "write") {
|
|
139
|
+
if (wantsSubcommandHelp) {
|
|
140
|
+
console.log(topicHelp);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
await runDiaryWriteCommand(config);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (command === "reminder" && subcommand === "write") {
|
|
147
|
+
if (wantsSubcommandHelp) {
|
|
148
|
+
console.log(topicHelp);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
await runReminderWriteCommand(config);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (command === "system" && subcommand === "send") {
|
|
155
|
+
await runSystemSendCommand(config);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (command === "system" && subcommand === "checkin-poller") {
|
|
159
|
+
await runSystemCheckinPoller(config);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (command === "channel" && subcommand === "send-file") {
|
|
163
|
+
await runChannelSendFileCommand(getApp());
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (command === "note" && subcommand === "sync") {
|
|
167
|
+
if (wantsSubcommandHelp) {
|
|
168
|
+
console.log(topicHelp);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
await runNoteSyncCommand(config);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (command === "note" && subcommand === "auto") {
|
|
175
|
+
if (wantsSubcommandHelp) {
|
|
176
|
+
console.log(topicHelp);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
await runNoteAutoCommand(config);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (command === "note" && subcommand === "maybe") {
|
|
183
|
+
if (wantsSubcommandHelp) {
|
|
184
|
+
console.log(topicHelp);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
runNoteMaybeCommand(config);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (command === "project" && subcommand === "radar") {
|
|
191
|
+
if (wantsSubcommandHelp) {
|
|
192
|
+
console.log(topicHelp);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
await runProjectRadarCommand(config);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (command === "review" && subcommand === "weekly") {
|
|
199
|
+
if (wantsSubcommandHelp) {
|
|
200
|
+
console.log(topicHelp);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
await runReviewCommand(config, "weekly");
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (command === "review" && subcommand === "nightly") {
|
|
207
|
+
if (wantsSubcommandHelp) {
|
|
208
|
+
console.log(topicHelp);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
await runReviewCommand(config, "nightly");
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (command === "review" && subcommand === "monthly") {
|
|
215
|
+
if (wantsSubcommandHelp) {
|
|
216
|
+
console.log(topicHelp);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
await runReviewCommand(config, "monthly");
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (command === "timeline") {
|
|
225
|
+
const timelineIntegration = createTimelineIntegration(config);
|
|
226
|
+
if (!subcommand || subcommand === "help") {
|
|
227
|
+
console.log(buildTerminalTopicHelp("timeline"));
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (subcommand === "event") {
|
|
231
|
+
await runTimelineEventCommand(timelineIntegration, argv.slice(2));
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (subcommand === "screenshot") {
|
|
235
|
+
const screenshotArgs = argv.slice(2);
|
|
236
|
+
if (screenshotArgs.includes("--help") || screenshotArgs.includes("-h")) {
|
|
237
|
+
await timelineIntegration.runSubcommand(subcommand, screenshotArgs);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
await runTimelineScreenshotCommand(config, argv.slice(2));
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
await timelineIntegration.runSubcommand(subcommand, argv.slice(2));
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (command === "doctor") {
|
|
248
|
+
getApp().printDoctor();
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (command === "login") {
|
|
253
|
+
await getApp().login();
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (command === "accounts") {
|
|
258
|
+
getApp().printAccounts();
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (command === "start") {
|
|
263
|
+
await getApp().start();
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
throw new Error(`未知命令: ${command}`);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
module.exports = { main };
|