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
@@ -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
- 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
-
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
- return {
9
- describe() {
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: ["inherit", "pipe", "pipe"],
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.once("error", reject);
66
- child.once("exit", (code, signal) => {
67
- if (signal) {
68
- reject(new Error(`timeline 进程被信号中断: ${signal}`));
69
- return;
70
- }
71
- if (code !== 0) {
72
- reject(new Error(`timeline 命令执行失败,退出码 ${code}`));
73
- return;
74
- }
75
- if (options.subcommand === "write") {
76
- const failure = detectTimelineWriteFailure(stdout, stderr);
77
- if (failure) {
78
- reject(new Error(failure));
79
- return;
80
- }
81
- }
82
- resolve();
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
- module.exports = { createTimelineIntegration };
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
+ };