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
|
@@ -7,6 +7,10 @@ const DEFAULT_BOOTSTRAP_PROFILE = Object.freeze({
|
|
|
7
7
|
path: "AGENTS.md",
|
|
8
8
|
role: "workspace routing and boundary contract",
|
|
9
9
|
},
|
|
10
|
+
{
|
|
11
|
+
path: "AGENTS.local.md",
|
|
12
|
+
role: "private operator overlay for this workspace",
|
|
13
|
+
},
|
|
10
14
|
{
|
|
11
15
|
path: "README.md",
|
|
12
16
|
role: "workspace overview and operating instructions",
|
|
@@ -19,12 +23,16 @@ const DEFAULT_BOOTSTRAP_PROFILE = Object.freeze({
|
|
|
19
23
|
path: ".codex/AGENT_GUIDE.md",
|
|
20
24
|
role: "agent write/update rules for this workspace",
|
|
21
25
|
},
|
|
26
|
+
{
|
|
27
|
+
path: ".codex/AGENT_GUIDE.local.md",
|
|
28
|
+
role: "private agent write/update overlay for this workspace",
|
|
29
|
+
},
|
|
22
30
|
],
|
|
23
31
|
conditionalFiles: [
|
|
24
32
|
{
|
|
25
33
|
path: ".codex/timeline/README.md",
|
|
26
34
|
role: "timeline write/read contract for this workspace",
|
|
27
|
-
when: "timeline read/write/build/screenshot work",
|
|
35
|
+
when: "timeline read/write/build/screenshot work, or cutover/closeout bookkeeping that may append timeline facts/events",
|
|
28
36
|
},
|
|
29
37
|
],
|
|
30
38
|
recentFiles: [],
|
package/src/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
const fs = require("fs");
|
|
2
|
-
const os = require("os");
|
|
3
|
-
const path = require("path");
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const os = require("os");
|
|
3
|
+
const path = require("path");
|
|
4
4
|
const dotenv = require("dotenv");
|
|
5
5
|
|
|
6
6
|
const {
|
|
@@ -24,12 +24,12 @@ const { runTimelineEventCommand } = require("./app/timeline-event-cli");
|
|
|
24
24
|
const { runTimelineScreenshotCommand } = require("./app/timeline-screenshot-cli");
|
|
25
25
|
const { runSystemCheckinPoller } = require("./app/system-checkin-poller");
|
|
26
26
|
const { runSystemSendCommand } = require("./app/system-send-cli");
|
|
27
|
-
const {
|
|
28
|
-
buildTerminalHelpText,
|
|
29
|
-
buildTerminalTopicHelp,
|
|
30
|
-
isPlannedTerminalTopic,
|
|
31
|
-
} = require("./core/command-registry");
|
|
32
|
-
|
|
27
|
+
const {
|
|
28
|
+
buildTerminalHelpText,
|
|
29
|
+
buildTerminalTopicHelp,
|
|
30
|
+
isPlannedTerminalTopic,
|
|
31
|
+
} = require("./core/command-registry");
|
|
32
|
+
|
|
33
33
|
function ensureDefaultStateDirectory() {
|
|
34
34
|
ensureStateDirectory();
|
|
35
35
|
}
|
|
@@ -40,58 +40,60 @@ function loadEnv() {
|
|
|
40
40
|
for (const envPath of candidates) {
|
|
41
41
|
if (!fs.existsSync(envPath)) {
|
|
42
42
|
continue;
|
|
43
|
-
}
|
|
44
|
-
dotenv.config({ path: envPath });
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
dotenv.config();
|
|
43
|
+
}
|
|
44
|
+
dotenv.config({ path: envPath });
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
dotenv.config();
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
function ensureRuntimeEnv() {
|
|
51
51
|
ensureCompatHomeEnv({ fallbackRoot: path.resolve(__dirname, "..") });
|
|
52
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
+
// The repo template is now the default persona source. Keep this bootstrap
|
|
67
|
+
// path only for explicit custom file paths that may still need seeding.
|
|
68
|
+
const templatePath = path.resolve(__dirname, "..", "templates", "weixin-instructions.md");
|
|
69
|
+
let template = "";
|
|
70
|
+
try {
|
|
71
|
+
template = fs.readFileSync(templatePath, "utf8");
|
|
72
|
+
} catch {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const userName = String(config?.userName || "").trim() || "用户";
|
|
77
|
+
const content = renderInstructionTemplate(template, {
|
|
78
|
+
...config,
|
|
79
|
+
userName,
|
|
80
|
+
}).trimEnd() + "\n";
|
|
81
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
82
|
+
fs.writeFileSync(filePath, content, "utf8");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function printHelp() {
|
|
86
|
+
console.log(buildTerminalHelpText());
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let runtimeErrorHooksInstalled = false;
|
|
90
|
+
|
|
91
|
+
function installRuntimeErrorHooks() {
|
|
92
|
+
if (runtimeErrorHooksInstalled) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
runtimeErrorHooksInstalled = true;
|
|
96
|
+
|
|
95
97
|
process.on("unhandledRejection", (reason) => {
|
|
96
98
|
const message = reason instanceof Error ? reason.stack || reason.message : String(reason);
|
|
97
99
|
console.error(`[${PACKAGE_NAME}] unhandled rejection ${message}`);
|
|
@@ -103,62 +105,62 @@ function installRuntimeErrorHooks() {
|
|
|
103
105
|
process.exitCode = 1;
|
|
104
106
|
});
|
|
105
107
|
}
|
|
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
|
-
}
|
|
108
|
+
|
|
109
|
+
async function main() {
|
|
110
|
+
loadEnv();
|
|
111
|
+
ensureRuntimeEnv();
|
|
112
|
+
installRuntimeErrorHooks();
|
|
113
|
+
const argv = process.argv.slice(2);
|
|
114
|
+
const config = readConfig();
|
|
115
|
+
ensureBootstrapFiles(config);
|
|
116
|
+
const command = config.mode || "help";
|
|
117
|
+
const subcommand = argv[1] || "";
|
|
118
|
+
let app = null;
|
|
119
|
+
const getApp = () => {
|
|
120
|
+
if (!app) {
|
|
121
|
+
app = new CyberbossApp(config);
|
|
122
|
+
}
|
|
123
|
+
return app;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
if (command === "help" || command === "--help" || command === "-h") {
|
|
127
|
+
const topicHelp = subcommand ? buildTerminalTopicHelp(subcommand) : "";
|
|
128
|
+
console.log(topicHelp || buildTerminalHelpText());
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (isPlannedTerminalTopic(command)) {
|
|
133
|
+
const topicHelp = buildTerminalTopicHelp(command);
|
|
134
|
+
const subcommandArgs = argv.slice(2);
|
|
135
|
+
const wantsSubcommandHelp = subcommandArgs.includes("--help") || subcommandArgs.includes("-h");
|
|
136
|
+
if (subcommand === "help" || !subcommand) {
|
|
137
|
+
console.log(topicHelp);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (command === "diary" && subcommand === "write") {
|
|
141
|
+
if (wantsSubcommandHelp) {
|
|
142
|
+
console.log(topicHelp);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
await runDiaryWriteCommand(config);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (command === "reminder" && subcommand === "write") {
|
|
149
|
+
if (wantsSubcommandHelp) {
|
|
150
|
+
console.log(topicHelp);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
await runReminderWriteCommand(config);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (command === "system" && subcommand === "send") {
|
|
157
|
+
await runSystemSendCommand(config);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (command === "system" && subcommand === "checkin-poller") {
|
|
161
|
+
await runSystemCheckinPoller(config);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
162
164
|
if (command === "channel" && subcommand === "send-file") {
|
|
163
165
|
await runChannelSendFileCommand(getApp());
|
|
164
166
|
return;
|
|
@@ -220,7 +222,7 @@ async function main() {
|
|
|
220
222
|
return;
|
|
221
223
|
}
|
|
222
224
|
}
|
|
223
|
-
|
|
225
|
+
|
|
224
226
|
if (command === "timeline") {
|
|
225
227
|
const timelineIntegration = createTimelineIntegration(config);
|
|
226
228
|
if (!subcommand || subcommand === "help") {
|
|
@@ -228,7 +230,7 @@ async function main() {
|
|
|
228
230
|
return;
|
|
229
231
|
}
|
|
230
232
|
if (subcommand === "event") {
|
|
231
|
-
await runTimelineEventCommand(timelineIntegration, argv.slice(2));
|
|
233
|
+
await runTimelineEventCommand(timelineIntegration, config, argv.slice(2));
|
|
232
234
|
return;
|
|
233
235
|
}
|
|
234
236
|
if (subcommand === "screenshot") {
|
|
@@ -236,35 +238,35 @@ async function main() {
|
|
|
236
238
|
if (screenshotArgs.includes("--help") || screenshotArgs.includes("-h")) {
|
|
237
239
|
await timelineIntegration.runSubcommand(subcommand, screenshotArgs);
|
|
238
240
|
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 };
|
|
241
|
+
}
|
|
242
|
+
await runTimelineScreenshotCommand(config, argv.slice(2));
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
await timelineIntegration.runSubcommand(subcommand, argv.slice(2));
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (command === "doctor") {
|
|
250
|
+
getApp().printDoctor();
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (command === "login") {
|
|
255
|
+
await getApp().login();
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (command === "accounts") {
|
|
260
|
+
getApp().printAccounts();
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (command === "start") {
|
|
265
|
+
await getApp().start();
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
throw new Error(`未知命令: ${command}`);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
module.exports = { main };
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
const path = require("path");
|
|
2
2
|
const { spawn } = require("child_process");
|
|
3
3
|
const { readPrefixedEnv } = require("../../core/branding");
|
|
4
|
+
const { resolveTimelineStateFiles } = require("../../core/timezone");
|
|
5
|
+
const { ensureTimelineStateTimezone } = require("./state-sync");
|
|
4
6
|
|
|
5
7
|
function createTimelineIntegration(config) {
|
|
6
8
|
const binPath = resolveTimelineBinPath();
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
const timelineFiles = resolveTimelineStateFiles(config.timelineStateDir);
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
describe() {
|
|
10
13
|
return {
|
|
11
14
|
id: "timeline-for-agent",
|
|
12
15
|
kind: "integration",
|
|
13
16
|
command: `${process.execPath} ${binPath}`,
|
|
14
17
|
stateDir: config.timelineStateDir,
|
|
18
|
+
timelineDir: timelineFiles.dir,
|
|
15
19
|
};
|
|
16
20
|
},
|
|
17
21
|
async runSubcommand(subcommand, args = []) {
|
|
@@ -19,26 +23,37 @@ function createTimelineIntegration(config) {
|
|
|
19
23
|
if (!normalizedSubcommand) {
|
|
20
24
|
throw new Error("timeline 子命令不能为空");
|
|
21
25
|
}
|
|
26
|
+
ensureTimelineStateTimezone(config);
|
|
27
|
+
// Pass the fully resolved files so Codeksei, state sync, and
|
|
28
|
+
// timeline-for-agent all operate on the same layout during direct,
|
|
29
|
+
// nested, and migrated state-dir variants.
|
|
22
30
|
return runTimelineCommand(binPath, [normalizedSubcommand, ...normalizeTimelineArgs(normalizedSubcommand, args)], {
|
|
23
31
|
TIMELINE_FOR_AGENT_STATE_DIR: config.timelineStateDir,
|
|
32
|
+
TIMELINE_FOR_AGENT_DIR: timelineFiles.dir,
|
|
33
|
+
TIMELINE_FOR_AGENT_STATE_FILE: timelineFiles.stateFile,
|
|
34
|
+
TIMELINE_FOR_AGENT_TAXONOMY_FILE: timelineFiles.taxonomyFile,
|
|
35
|
+
TIMELINE_FOR_AGENT_FACTS_FILE: timelineFiles.factsFile,
|
|
36
|
+
TIMELINE_FOR_AGENT_DB_FILE: path.join(timelineFiles.dir, "timeline-db.json"),
|
|
37
|
+
TIMELINE_FOR_AGENT_SITE_DIR: path.join(timelineFiles.dir, "site"),
|
|
38
|
+
TIMELINE_FOR_AGENT_WRITE_LOCK_DIR: path.join(timelineFiles.dir, "timeline-write.lock"),
|
|
24
39
|
TIMELINE_FOR_AGENT_CHROME_PATH: resolveTimelineChromePath(),
|
|
25
40
|
}, {
|
|
26
41
|
subcommand: normalizedSubcommand,
|
|
27
42
|
});
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function resolveTimelineBinPath() {
|
|
33
|
-
const packageJsonPath = require.resolve("timeline-for-agent/package.json");
|
|
34
|
-
return path.join(path.dirname(packageJsonPath), "bin", "timeline-for-agent.js");
|
|
35
|
-
}
|
|
36
|
-
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function resolveTimelineBinPath() {
|
|
48
|
+
const packageJsonPath = require.resolve("timeline-for-agent/package.json");
|
|
49
|
+
return path.join(path.dirname(packageJsonPath), "bin", "timeline-for-agent.js");
|
|
50
|
+
}
|
|
51
|
+
|
|
37
52
|
function runTimelineCommand(binPath, args, extraEnv = {}, options = {}) {
|
|
38
53
|
return new Promise((resolve, reject) => {
|
|
39
54
|
const spawnSpec = buildTimelineSpawnSpec(binPath, args);
|
|
40
55
|
const child = spawn(spawnSpec.command, spawnSpec.args, {
|
|
41
|
-
stdio: ["
|
|
56
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
42
57
|
env: {
|
|
43
58
|
...process.env,
|
|
44
59
|
...extraEnv,
|
|
@@ -46,42 +61,45 @@ function runTimelineCommand(binPath, args, extraEnv = {}, options = {}) {
|
|
|
46
61
|
shell: false,
|
|
47
62
|
windowsHide: true,
|
|
48
63
|
});
|
|
49
|
-
|
|
50
|
-
let stdout = "";
|
|
51
|
-
let stderr = "";
|
|
52
|
-
|
|
53
|
-
child.stdout.on("data", (chunk) => {
|
|
54
|
-
const text = chunk.toString("utf8");
|
|
55
|
-
stdout += text;
|
|
56
|
-
process.stdout.write(text);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
child.stderr.on("data", (chunk) => {
|
|
60
|
-
const text = chunk.toString("utf8");
|
|
61
|
-
stderr += text;
|
|
62
|
-
process.stderr.write(text);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
child
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
64
|
+
|
|
65
|
+
let stdout = "";
|
|
66
|
+
let stderr = "";
|
|
67
|
+
|
|
68
|
+
child.stdout.on("data", (chunk) => {
|
|
69
|
+
const text = chunk.toString("utf8");
|
|
70
|
+
stdout += text;
|
|
71
|
+
process.stdout.write(text);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
child.stderr.on("data", (chunk) => {
|
|
75
|
+
const text = chunk.toString("utf8");
|
|
76
|
+
stderr += text;
|
|
77
|
+
process.stderr.write(text);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
wireTimelineStdin(child, args);
|
|
81
|
+
|
|
82
|
+
child.once("error", reject);
|
|
83
|
+
child.once("exit", (code, signal) => {
|
|
84
|
+
if (signal) {
|
|
85
|
+
reject(new Error(`timeline 进程被信号中断: ${signal}`));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (code !== 0) {
|
|
89
|
+
const detail = extractTimelineCommandFailure(stdout, stderr);
|
|
90
|
+
reject(new Error(detail || `timeline 命令执行失败,退出码 ${code}`));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (options.subcommand === "write") {
|
|
94
|
+
const failure = detectTimelineWriteFailure(stdout, stderr);
|
|
95
|
+
if (failure) {
|
|
96
|
+
reject(new Error(failure));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
resolve();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
85
103
|
}
|
|
86
104
|
|
|
87
105
|
function buildTimelineSpawnSpec(binPath, args = []) {
|
|
@@ -93,7 +111,7 @@ function buildTimelineSpawnSpec(binPath, args = []) {
|
|
|
93
111
|
args: [binPath, ...args],
|
|
94
112
|
};
|
|
95
113
|
}
|
|
96
|
-
|
|
114
|
+
|
|
97
115
|
function normalizeArgs(args) {
|
|
98
116
|
return Array.isArray(args)
|
|
99
117
|
? args
|
|
@@ -159,33 +177,71 @@ function normalizeTimelineArgs(subcommand, args) {
|
|
|
159
177
|
function isIsoDateToken(value) {
|
|
160
178
|
return /^\d{4}-\d{2}-\d{2}$/.test(normalizeText(value));
|
|
161
179
|
}
|
|
162
|
-
|
|
163
|
-
function normalizeText(value) {
|
|
164
|
-
return typeof value === "string" ? value.trim() : "";
|
|
165
|
-
}
|
|
166
|
-
|
|
180
|
+
|
|
181
|
+
function normalizeText(value) {
|
|
182
|
+
return typeof value === "string" ? value.trim() : "";
|
|
183
|
+
}
|
|
184
|
+
|
|
167
185
|
function resolveTimelineChromePath() {
|
|
168
186
|
const configured = normalizeText(process.env.TIMELINE_FOR_AGENT_CHROME_PATH)
|
|
169
187
|
|| normalizeText(readPrefixedEnv(process.env, "SCREENSHOT_CHROME_PATH"));
|
|
170
188
|
if (configured) {
|
|
171
189
|
return configured;
|
|
172
190
|
}
|
|
173
|
-
if (process.platform === "darwin") {
|
|
174
|
-
return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
|
|
175
|
-
}
|
|
176
|
-
return "";
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function detectTimelineWriteFailure(stdout, stderr) {
|
|
180
|
-
const output = `${stdout}\n${stderr}`;
|
|
181
|
-
const statusMatch = output.match(/^\s*status:\s*(.+)\s*$/m);
|
|
182
|
-
const eventsMatch = output.match(/^\s*events:\s*(\d+)\s*$/m);
|
|
183
|
-
const status = normalizeText(statusMatch?.[1]);
|
|
184
|
-
const events = Number.parseInt(eventsMatch?.[1] || "", 10);
|
|
185
|
-
if (status === "missing" && Number.isFinite(events) && events <= 0) {
|
|
186
|
-
return "timeline write 没有写入任何事件;当前结果是 events: 0 且 status: missing。请检查是否真的传入了有效 JSON events。";
|
|
187
|
-
}
|
|
188
|
-
return "";
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
|
|
191
|
+
if (process.platform === "darwin") {
|
|
192
|
+
return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
|
|
193
|
+
}
|
|
194
|
+
return "";
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function detectTimelineWriteFailure(stdout, stderr) {
|
|
198
|
+
const output = `${stdout}\n${stderr}`;
|
|
199
|
+
const statusMatch = output.match(/^\s*status:\s*(.+)\s*$/m);
|
|
200
|
+
const eventsMatch = output.match(/^\s*events:\s*(\d+)\s*$/m);
|
|
201
|
+
const status = normalizeText(statusMatch?.[1]);
|
|
202
|
+
const events = Number.parseInt(eventsMatch?.[1] || "", 10);
|
|
203
|
+
if (status === "missing" && Number.isFinite(events) && events <= 0) {
|
|
204
|
+
return "timeline write 没有写入任何事件;当前结果是 events: 0 且 status: missing。请检查是否真的传入了有效 JSON events。";
|
|
205
|
+
}
|
|
206
|
+
return "";
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function extractTimelineCommandFailure(stdout, stderr) {
|
|
210
|
+
const output = `${stderr}\n${stdout}`;
|
|
211
|
+
const lines = output
|
|
212
|
+
.split("\n")
|
|
213
|
+
.map((line) => line.trim())
|
|
214
|
+
.filter(Boolean);
|
|
215
|
+
|
|
216
|
+
return lines.find((line) => line.includes("timeline 事件无效"))
|
|
217
|
+
|| lines.find((line) => line.includes("timeline 事件不能跨天"))
|
|
218
|
+
|| lines.find((line) => line.includes("timeline-write"))
|
|
219
|
+
|| lines.at(-1)
|
|
220
|
+
|| "";
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function shouldForwardTimelineStdin(args = [], stdin = process.stdin) {
|
|
224
|
+
return Array.isArray(args)
|
|
225
|
+
&& args.some((value) => String(value || "").trim() === "--stdin")
|
|
226
|
+
&& stdin
|
|
227
|
+
&& stdin.isTTY === false;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function wireTimelineStdin(child, args = [], stdin = process.stdin) {
|
|
231
|
+
if (!child?.stdin) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (!shouldForwardTimelineStdin(args, stdin)) {
|
|
235
|
+
child.stdin.end();
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
stdin.pipe(child.stdin);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
module.exports = {
|
|
242
|
+
createTimelineIntegration,
|
|
243
|
+
detectTimelineWriteFailure,
|
|
244
|
+
extractTimelineCommandFailure,
|
|
245
|
+
normalizeTimelineArgs,
|
|
246
|
+
shouldForwardTimelineStdin,
|
|
247
|
+
};
|