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
@@ -8,12 +8,19 @@ const {
8
8
  resolveAppHome,
9
9
  resolveStateDir,
10
10
  } = require("./branding");
11
+ const { resolveCrossPlatformPathFromRoot } = require("./path-utils");
12
+ const { resolveTimezoneConfig } = require("./timezone");
11
13
 
12
14
  function readConfig() {
13
15
  const argv = process.argv.slice(2);
14
16
  const mode = argv[0] || "";
15
17
  const stateDir = resolveStateDir({ env: process.env });
16
18
  const workspaceRoot = readPrefixedEnv(process.env, "WORKSPACE_ROOT") || process.cwd();
19
+ const timelineStateDir = readPrefixedEnv(process.env, "TIMELINE_STATE_DIR") || stateDir;
20
+ const timezoneConfig = resolveTimezoneConfig({
21
+ explicitTimezone: readPrefixedEnv(process.env, "TIMEZONE"),
22
+ timelineStateDir,
23
+ });
17
24
  const appHome = resolveAppHome({
18
25
  env: process.env,
19
26
  fallbackRoot: path.resolve(__dirname, "..", ".."),
@@ -27,8 +34,12 @@ function readConfig() {
27
34
  cyberbossHome: appHome,
28
35
  workspaceId: readPrefixedEnv(process.env, "WORKSPACE_ID") || "default",
29
36
  workspaceRoot,
37
+ timezone: timezoneConfig.timezone,
38
+ timezoneSource: timezoneConfig.source,
39
+ timezoneExplicit: timezoneConfig.explicit,
40
+ timelineStateTimezone: timezoneConfig.timelineStateTimezone,
30
41
  diaryDir: readPrefixedEnv(process.env, "DIARY_DIR") || path.join(stateDir, "diary"),
31
- timelineStateDir: readPrefixedEnv(process.env, "TIMELINE_STATE_DIR") || stateDir,
42
+ timelineStateDir,
32
43
  userName: readPrefixedEnv(process.env, "USER_NAME") || "用户",
33
44
  userGender: readPrefixedEnv(process.env, "USER_GENDER") || "female",
34
45
  allowedUserIds: readPrefixedListEnv(process.env, "ALLOWED_USER_IDS"),
@@ -47,10 +58,15 @@ function readConfig() {
47
58
  accountsDir: path.join(stateDir, "accounts"),
48
59
  logDir: path.join(stateDir, "logs"),
49
60
  reminderQueueFile: path.join(stateDir, "reminder-queue.json"),
50
- systemMessageQueueFile: path.join(stateDir, "system-message-queue.json"),
51
- timelineScreenshotQueueFile: path.join(stateDir, "timeline-screenshot-queue.json"),
52
- weixinInstructionsFile: path.join(stateDir, "weixin-instructions.md"),
61
+ systemMessageQueueFile: path.join(stateDir, "system-message-queue.json"),
62
+ timelineScreenshotQueueFile: path.join(stateDir, "timeline-screenshot-queue.json"),
63
+ weixinInstructionsFile: readPrefixedEnv(process.env, "WEIXIN_INSTRUCTIONS_FILE")
64
+ || path.resolve(__dirname, "..", "..", "templates", "weixin-instructions.md"),
65
+ weixinInstructionsOverlayFile: readPrefixedEnv(process.env, "WEIXIN_INSTRUCTIONS_OVERLAY_FILE")
66
+ || path.join(stateDir, "weixin-instructions.local.md"),
53
67
  weixinOperationsFile: path.resolve(__dirname, "..", "..", "templates", "weixin-operations.md"),
68
+ weixinOperationsOverlayFile: readPrefixedEnv(process.env, "WEIXIN_OPERATIONS_OVERLAY_FILE")
69
+ || path.join(stateDir, "weixin-operations.local.md"),
54
70
  syncBufferDir: path.join(stateDir, "sync-buffers"),
55
71
  codexEndpoint: readPrefixedEnv(process.env, "CODEX_ENDPOINT"),
56
72
  codexCommand: readPrefixedEnv(process.env, "CODEX_COMMAND"),
@@ -59,11 +75,11 @@ function readConfig() {
59
75
  workspaceBootstrapConfigFile: readPrefixedEnv(process.env, "WORKSPACE_BOOTSTRAP_CONFIG")
60
76
  || path.join(stateDir, "workspace-bootstrap.json"),
61
77
  projectRadarConfigFile: readPrefixedEnv(process.env, "PROJECT_RADAR_CONFIG")
62
- || path.resolve(workspaceRoot, ".codex", "code-projects.json"),
78
+ || resolveCrossPlatformPathFromRoot(workspaceRoot, ".codex", "code-projects.json"),
63
79
  durableNoteSchemaConfigFile: readPrefixedEnv(process.env, "DURABLE_NOTE_SCHEMA_CONFIG")
64
- || path.resolve(workspaceRoot, ".codex", "durable-note-schema.json"),
80
+ || resolveCrossPlatformPathFromRoot(workspaceRoot, ".codex", "durable-note-schema.json"),
65
81
  reviewSchemaConfigFile: readPrefixedEnv(process.env, "REVIEW_SCHEMA_CONFIG")
66
- || path.resolve(workspaceRoot, ".codex", "review-schema.json"),
82
+ || resolveCrossPlatformPathFromRoot(workspaceRoot, ".codex", "review-schema.json"),
67
83
  reviewSemanticMode: readPrefixedEnv(process.env, "REVIEW_SEMANTIC_MODE") || "hybrid",
68
84
  reviewSemanticModel: readPrefixedEnv(process.env, "REVIEW_SEMANTIC_MODEL"),
69
85
  reviewSemanticTimeoutMs: readPrefixedIntEnv(process.env, "REVIEW_SEMANTIC_TIMEOUT_MS") || 120000,
@@ -72,13 +88,13 @@ function readConfig() {
72
88
  startWithCheckin: (mode === "start" && hasArgFlag(argv, "--checkin")) || readPrefixedBoolEnv(process.env, "ENABLE_CHECKIN"),
73
89
  };
74
90
  }
75
-
76
- function normalizeWeixinReplyMode(value) {
77
- return String(value || "").trim().toLowerCase() === "settled" ? "settled" : "stream";
78
- }
79
-
91
+
92
+ function normalizeWeixinReplyMode(value) {
93
+ return String(value || "").trim().toLowerCase() === "settled" ? "settled" : "stream";
94
+ }
95
+
80
96
  function hasArgFlag(argv, flag) {
81
97
  return Array.isArray(argv) && argv.some((item) => String(item || "").trim() === flag);
82
98
  }
83
-
84
- module.exports = { readConfig };
99
+
100
+ module.exports = { readConfig };
@@ -1,163 +1,163 @@
1
- const { loadPersistedContextTokens } = require("../adapters/channel/weixin/context-token-store");
2
-
3
- function resolvePreferredSenderId({
4
- config,
5
- accountId,
6
- explicitUser = "",
7
- sessionStore = null,
8
- }) {
9
- const normalizedExplicitUser = normalizeText(explicitUser);
10
- if (normalizedExplicitUser) {
11
- return normalizedExplicitUser;
12
- }
13
-
14
- const configuredUsers = Array.isArray(config?.allowedUserIds)
15
- ? config.allowedUserIds.map((value) => normalizeText(value)).filter(Boolean)
16
- : [];
17
- if (configuredUsers.length) {
18
- return configuredUsers[0];
19
- }
20
-
21
- const bindingCandidates = collectBindingSenderIds({ config, accountId, sessionStore });
22
- if (bindingCandidates.length === 1) {
23
- return bindingCandidates[0];
24
- }
25
-
26
- const persistedUserIds = Object.keys(loadPersistedContextTokens(config, accountId) || {})
27
- .map((value) => normalizeText(value))
28
- .filter(Boolean);
29
- if (persistedUserIds.length === 1) {
30
- return persistedUserIds[0];
31
- }
32
-
33
- return "";
34
- }
35
-
36
- function resolvePreferredWorkspaceRoot({
37
- config,
38
- accountId,
39
- senderId = "",
40
- explicitWorkspace = "",
41
- sessionStore = null,
42
- }) {
43
- const normalizedExplicitWorkspace = normalizeText(explicitWorkspace);
44
- if (normalizedExplicitWorkspace) {
45
- return normalizedExplicitWorkspace;
46
- }
47
-
48
- const normalizedSenderId = normalizeText(senderId);
49
- const normalizedAccountId = normalizeText(accountId);
50
- const store = sessionStore && typeof sessionStore.getBinding === "function"
51
- ? sessionStore
52
- : null;
53
-
54
- if (store && normalizedSenderId && normalizedAccountId) {
55
- const bindingKey = store.buildBindingKey({
56
- workspaceId: config.workspaceId,
57
- accountId: normalizedAccountId,
58
- senderId: normalizedSenderId,
59
- });
60
- const activeWorkspaceRoot = normalizeText(store.getActiveWorkspaceRoot(bindingKey));
61
- if (activeWorkspaceRoot) {
62
- return activeWorkspaceRoot;
63
- }
64
-
65
- const binding = store.getBinding(bindingKey);
66
- const boundWorkspaceRoots = collectWorkspaceRoots(binding);
67
- if (boundWorkspaceRoots.length === 1) {
68
- return boundWorkspaceRoots[0];
69
- }
70
- }
71
-
72
- const globalWorkspaceCandidates = collectBindingWorkspaceRoots({ config, accountId, sessionStore: store });
73
- if (globalWorkspaceCandidates.length === 1) {
74
- return globalWorkspaceCandidates[0];
75
- }
76
-
77
- return normalizeText(config?.workspaceRoot);
78
- }
79
-
80
- function collectBindingSenderIds({ config, accountId, sessionStore }) {
81
- const store = sessionStore && typeof sessionStore.getBinding === "function"
82
- ? sessionStore
83
- : null;
84
- if (!store) {
85
- return [];
86
- }
87
- const normalizedAccountId = normalizeText(accountId);
88
- if (!normalizedAccountId) {
89
- return [];
90
- }
91
-
92
- const senderIds = new Set();
93
- for (const binding of Object.values(store.state?.bindings || {})) {
94
- const bindingAccountId = normalizeText(binding?.accountId);
95
- const bindingWorkspaceId = normalizeText(binding?.workspaceId);
96
- const senderId = normalizeText(binding?.senderId);
97
- if (!senderId || bindingAccountId !== normalizedAccountId) {
98
- continue;
99
- }
100
- if (bindingWorkspaceId && bindingWorkspaceId !== normalizeText(config?.workspaceId)) {
101
- continue;
102
- }
103
- senderIds.add(senderId);
104
- }
105
- return Array.from(senderIds).sort((left, right) => left.localeCompare(right));
106
- }
107
-
108
- function collectBindingWorkspaceRoots({ config, accountId, sessionStore }) {
109
- const store = sessionStore && typeof sessionStore.getBinding === "function"
110
- ? sessionStore
111
- : null;
112
- if (!store) {
113
- return [];
114
- }
115
- const normalizedAccountId = normalizeText(accountId);
116
- const workspaceRoots = new Set();
117
-
118
- for (const binding of Object.values(store.state?.bindings || {})) {
119
- const bindingAccountId = normalizeText(binding?.accountId);
120
- const bindingWorkspaceId = normalizeText(binding?.workspaceId);
121
- if (bindingAccountId !== normalizedAccountId) {
122
- continue;
123
- }
124
- if (bindingWorkspaceId && bindingWorkspaceId !== normalizeText(config?.workspaceId)) {
125
- continue;
126
- }
127
- for (const workspaceRoot of collectWorkspaceRoots(binding)) {
128
- workspaceRoots.add(workspaceRoot);
129
- }
130
- }
131
-
132
- return Array.from(workspaceRoots).sort((left, right) => left.localeCompare(right));
133
- }
134
-
135
- function collectWorkspaceRoots(binding) {
136
- const workspaceRoots = new Set();
137
- const activeWorkspaceRoot = normalizeText(binding?.activeWorkspaceRoot);
138
- if (activeWorkspaceRoot) {
139
- workspaceRoots.add(activeWorkspaceRoot);
140
- }
141
- for (const workspaceRoot of Object.keys(binding?.threadIdByWorkspaceRoot || {})) {
142
- const normalizedWorkspaceRoot = normalizeText(workspaceRoot);
143
- if (normalizedWorkspaceRoot) {
144
- workspaceRoots.add(normalizedWorkspaceRoot);
145
- }
146
- }
147
- for (const workspaceRoot of Object.keys(binding?.codexParamsByWorkspaceRoot || {})) {
148
- const normalizedWorkspaceRoot = normalizeText(workspaceRoot);
149
- if (normalizedWorkspaceRoot) {
150
- workspaceRoots.add(normalizedWorkspaceRoot);
151
- }
152
- }
153
- return Array.from(workspaceRoots).sort((left, right) => left.localeCompare(right));
154
- }
155
-
156
- function normalizeText(value) {
157
- return typeof value === "string" ? value.trim() : "";
158
- }
159
-
160
- module.exports = {
161
- resolvePreferredSenderId,
162
- resolvePreferredWorkspaceRoot,
163
- };
1
+ const { loadPersistedContextTokens } = require("../adapters/channel/weixin/context-token-store");
2
+
3
+ function resolvePreferredSenderId({
4
+ config,
5
+ accountId,
6
+ explicitUser = "",
7
+ sessionStore = null,
8
+ }) {
9
+ const normalizedExplicitUser = normalizeText(explicitUser);
10
+ if (normalizedExplicitUser) {
11
+ return normalizedExplicitUser;
12
+ }
13
+
14
+ const configuredUsers = Array.isArray(config?.allowedUserIds)
15
+ ? config.allowedUserIds.map((value) => normalizeText(value)).filter(Boolean)
16
+ : [];
17
+ if (configuredUsers.length) {
18
+ return configuredUsers[0];
19
+ }
20
+
21
+ const bindingCandidates = collectBindingSenderIds({ config, accountId, sessionStore });
22
+ if (bindingCandidates.length === 1) {
23
+ return bindingCandidates[0];
24
+ }
25
+
26
+ const persistedUserIds = Object.keys(loadPersistedContextTokens(config, accountId) || {})
27
+ .map((value) => normalizeText(value))
28
+ .filter(Boolean);
29
+ if (persistedUserIds.length === 1) {
30
+ return persistedUserIds[0];
31
+ }
32
+
33
+ return "";
34
+ }
35
+
36
+ function resolvePreferredWorkspaceRoot({
37
+ config,
38
+ accountId,
39
+ senderId = "",
40
+ explicitWorkspace = "",
41
+ sessionStore = null,
42
+ }) {
43
+ const normalizedExplicitWorkspace = normalizeText(explicitWorkspace);
44
+ if (normalizedExplicitWorkspace) {
45
+ return normalizedExplicitWorkspace;
46
+ }
47
+
48
+ const normalizedSenderId = normalizeText(senderId);
49
+ const normalizedAccountId = normalizeText(accountId);
50
+ const store = sessionStore && typeof sessionStore.getBinding === "function"
51
+ ? sessionStore
52
+ : null;
53
+
54
+ if (store && normalizedSenderId && normalizedAccountId) {
55
+ const bindingKey = store.buildBindingKey({
56
+ workspaceId: config.workspaceId,
57
+ accountId: normalizedAccountId,
58
+ senderId: normalizedSenderId,
59
+ });
60
+ const activeWorkspaceRoot = normalizeText(store.getActiveWorkspaceRoot(bindingKey));
61
+ if (activeWorkspaceRoot) {
62
+ return activeWorkspaceRoot;
63
+ }
64
+
65
+ const binding = store.getBinding(bindingKey);
66
+ const boundWorkspaceRoots = collectWorkspaceRoots(binding);
67
+ if (boundWorkspaceRoots.length === 1) {
68
+ return boundWorkspaceRoots[0];
69
+ }
70
+ }
71
+
72
+ const globalWorkspaceCandidates = collectBindingWorkspaceRoots({ config, accountId, sessionStore: store });
73
+ if (globalWorkspaceCandidates.length === 1) {
74
+ return globalWorkspaceCandidates[0];
75
+ }
76
+
77
+ return normalizeText(config?.workspaceRoot);
78
+ }
79
+
80
+ function collectBindingSenderIds({ config, accountId, sessionStore }) {
81
+ const store = sessionStore && typeof sessionStore.getBinding === "function"
82
+ ? sessionStore
83
+ : null;
84
+ if (!store) {
85
+ return [];
86
+ }
87
+ const normalizedAccountId = normalizeText(accountId);
88
+ if (!normalizedAccountId) {
89
+ return [];
90
+ }
91
+
92
+ const senderIds = new Set();
93
+ for (const binding of Object.values(store.state?.bindings || {})) {
94
+ const bindingAccountId = normalizeText(binding?.accountId);
95
+ const bindingWorkspaceId = normalizeText(binding?.workspaceId);
96
+ const senderId = normalizeText(binding?.senderId);
97
+ if (!senderId || bindingAccountId !== normalizedAccountId) {
98
+ continue;
99
+ }
100
+ if (bindingWorkspaceId && bindingWorkspaceId !== normalizeText(config?.workspaceId)) {
101
+ continue;
102
+ }
103
+ senderIds.add(senderId);
104
+ }
105
+ return Array.from(senderIds).sort((left, right) => left.localeCompare(right));
106
+ }
107
+
108
+ function collectBindingWorkspaceRoots({ config, accountId, sessionStore }) {
109
+ const store = sessionStore && typeof sessionStore.getBinding === "function"
110
+ ? sessionStore
111
+ : null;
112
+ if (!store) {
113
+ return [];
114
+ }
115
+ const normalizedAccountId = normalizeText(accountId);
116
+ const workspaceRoots = new Set();
117
+
118
+ for (const binding of Object.values(store.state?.bindings || {})) {
119
+ const bindingAccountId = normalizeText(binding?.accountId);
120
+ const bindingWorkspaceId = normalizeText(binding?.workspaceId);
121
+ if (bindingAccountId !== normalizedAccountId) {
122
+ continue;
123
+ }
124
+ if (bindingWorkspaceId && bindingWorkspaceId !== normalizeText(config?.workspaceId)) {
125
+ continue;
126
+ }
127
+ for (const workspaceRoot of collectWorkspaceRoots(binding)) {
128
+ workspaceRoots.add(workspaceRoot);
129
+ }
130
+ }
131
+
132
+ return Array.from(workspaceRoots).sort((left, right) => left.localeCompare(right));
133
+ }
134
+
135
+ function collectWorkspaceRoots(binding) {
136
+ const workspaceRoots = new Set();
137
+ const activeWorkspaceRoot = normalizeText(binding?.activeWorkspaceRoot);
138
+ if (activeWorkspaceRoot) {
139
+ workspaceRoots.add(activeWorkspaceRoot);
140
+ }
141
+ for (const workspaceRoot of Object.keys(binding?.threadIdByWorkspaceRoot || {})) {
142
+ const normalizedWorkspaceRoot = normalizeText(workspaceRoot);
143
+ if (normalizedWorkspaceRoot) {
144
+ workspaceRoots.add(normalizedWorkspaceRoot);
145
+ }
146
+ }
147
+ for (const workspaceRoot of Object.keys(binding?.codexParamsByWorkspaceRoot || {})) {
148
+ const normalizedWorkspaceRoot = normalizeText(workspaceRoot);
149
+ if (normalizedWorkspaceRoot) {
150
+ workspaceRoots.add(normalizedWorkspaceRoot);
151
+ }
152
+ }
153
+ return Array.from(workspaceRoots).sort((left, right) => left.localeCompare(right));
154
+ }
155
+
156
+ function normalizeText(value) {
157
+ return typeof value === "string" ? value.trim() : "";
158
+ }
159
+
160
+ module.exports = {
161
+ resolvePreferredSenderId,
162
+ resolvePreferredWorkspaceRoot,
163
+ };
@@ -3,6 +3,11 @@ const path = require("path");
3
3
 
4
4
  const { listTrackedProjects } = require("./project-radar");
5
5
  const { appendSection, findSectionRange, resolveNoteSyncTarget } = require("./note-sync");
6
+ const {
7
+ normalizeDisplayPath,
8
+ resolveCrossPlatformPath,
9
+ resolveCrossPlatformPathFromRoot,
10
+ } = require("./path-utils");
6
11
 
7
12
  function loadDurableNoteSchemaConfig(config = {}) {
8
13
  const filePath = normalizeText(config.durableNoteSchemaConfigFile);
@@ -26,7 +31,7 @@ function loadDurableNoteSchemaConfig(config = {}) {
26
31
  }
27
32
 
28
33
  function resolveDurableNoteProfile(config = {}) {
29
- const workspaceRoot = normalizeDisplayPath(path.resolve(String(config.workspaceRoot || process.cwd())));
34
+ const workspaceRoot = resolveCrossPlatformPath(String(config.workspaceRoot || process.cwd()));
30
35
  const schemaConfig = loadDurableNoteSchemaConfig(config);
31
36
  const workspaceProfile = selectWorkspaceProfile(schemaConfig.workspaces, workspaceRoot);
32
37
  const projectDefaults = normalizeFamily(workspaceProfile?.projectDefaults);
@@ -285,10 +290,10 @@ function resolveWorkspaceNotePath(workspaceRoot, targetPath) {
285
290
  if (!normalizedTargetPath) {
286
291
  return "";
287
292
  }
288
- if (path.isAbsolute(normalizedTargetPath)) {
289
- return normalizeDisplayPath(path.resolve(normalizedTargetPath));
293
+ if (path.isAbsolute(normalizedTargetPath) || path.win32.isAbsolute(normalizedTargetPath)) {
294
+ return resolveCrossPlatformPath(normalizedTargetPath);
290
295
  }
291
- return normalizeDisplayPath(path.resolve(workspaceRoot, ...normalizedTargetPath.split("/")));
296
+ return resolveCrossPlatformPathFromRoot(workspaceRoot, ...normalizedTargetPath.split("/"));
292
297
  }
293
298
 
294
299
  function listAvailableScopes(profile) {
@@ -299,10 +304,6 @@ function normalizeText(value) {
299
304
  return typeof value === "string" ? value.trim() : "";
300
305
  }
301
306
 
302
- function normalizeDisplayPath(targetPath) {
303
- return normalizeText(targetPath).replace(/\\/g, "/");
304
- }
305
-
306
307
  function normalizeLineEnding(value) {
307
308
  return String(value || "").replace(/\r\n/g, "\n");
308
309
  }
@@ -1,14 +1,14 @@
1
- function resolveUserPronoun(gender) {
2
- const normalized = String(gender || "").trim().toLowerCase();
3
- if (normalized === "male" || normalized === "man" || normalized === "m" || normalized === "男") {
4
- return "他";
5
- }
6
- if (normalized === "neutral" || normalized === "nonbinary" || normalized === "nb" || normalized === "ta") {
7
- return "TA";
8
- }
9
- return "她";
10
- }
11
-
1
+ function resolveUserPronoun(gender) {
2
+ const normalized = String(gender || "").trim().toLowerCase();
3
+ if (normalized === "male" || normalized === "man" || normalized === "m" || normalized === "男") {
4
+ return "他";
5
+ }
6
+ if (normalized === "neutral" || normalized === "nonbinary" || normalized === "nb" || normalized === "ta") {
7
+ return "TA";
8
+ }
9
+ return "她";
10
+ }
11
+
12
12
  function renderInstructionTemplate(template, config = {}) {
13
13
  const userName = String(config?.userName || "").trim() || "用户";
14
14
  const pronoun = resolveUserPronoun(config?.userGender);
@@ -21,11 +21,12 @@ function renderInstructionTemplate(template, config = {}) {
21
21
  ).trim();
22
22
  return String(template || "")
23
23
  .replaceAll("{{USER_NAME}}", userName)
24
+ .replaceAll("{{CODEKSEI_HOME}}", codekseiHome)
24
25
  .replaceAll("{{CYBERBOSS_HOME}}", codekseiHome)
25
26
  .replaceAll("她", pronoun);
26
27
  }
27
-
28
- module.exports = {
29
- renderInstructionTemplate,
30
- resolveUserPronoun,
31
- };
28
+
29
+ module.exports = {
30
+ renderInstructionTemplate,
31
+ resolveUserPronoun,
32
+ };
@@ -4,6 +4,11 @@ const {
4
4
  PRIMARY_NOTE_SYNC_MARKER_PREFIX,
5
5
  LEGACY_NOTE_SYNC_MARKER_PREFIX,
6
6
  } = require("./branding");
7
+ const {
8
+ normalizeDisplayPath,
9
+ resolveCrossPlatformPath,
10
+ resolveCrossPlatformPathFromRoot,
11
+ } = require("./path-utils");
7
12
 
8
13
  const { listTrackedProjects } = require("./project-radar");
9
14
 
@@ -42,11 +47,11 @@ function resolveNoteSyncTarget(config = {}, options = {}) {
42
47
  }
43
48
 
44
49
  function resolveNotePath(workspaceRoot, targetPath) {
45
- if (path.isAbsolute(targetPath)) {
46
- return normalizeDisplayPath(path.resolve(targetPath));
50
+ if (path.isAbsolute(targetPath) || path.win32.isAbsolute(targetPath)) {
51
+ return resolveCrossPlatformPath(targetPath);
47
52
  }
48
53
  const baseRoot = normalizeText(workspaceRoot) || process.cwd();
49
- return normalizeDisplayPath(path.resolve(baseRoot, targetPath));
54
+ return resolveCrossPlatformPathFromRoot(baseRoot, targetPath);
50
55
  }
51
56
 
52
57
  function syncNoteFile(options = {}) {
@@ -397,10 +402,6 @@ function normalizeComparableText(value) {
397
402
  return normalizeParagraphText(value).replace(/\s+/gu, " ").toLowerCase();
398
403
  }
399
404
 
400
- function normalizeDisplayPath(targetPath) {
401
- return normalizeText(targetPath).replace(/\\/g, "/");
402
- }
403
-
404
405
  function normalizeFileEnding(value) {
405
406
  return String(value || "").replace(/\r\n/g, "\n");
406
407
  }
@@ -0,0 +1,54 @@
1
+ const path = require("path");
2
+
3
+ function normalizeText(value) {
4
+ return typeof value === "string" ? value.trim() : "";
5
+ }
6
+
7
+ function normalizeDisplayPath(targetPath) {
8
+ return normalizeText(targetPath).replace(/\\/g, "/");
9
+ }
10
+
11
+ function isCrossPlatformAbsolutePath(targetPath) {
12
+ const normalized = normalizeText(targetPath);
13
+ if (!normalized) {
14
+ return false;
15
+ }
16
+ return path.isAbsolute(normalized) || path.win32.isAbsolute(normalized);
17
+ }
18
+
19
+ function resolveCrossPlatformPath(targetPath) {
20
+ const normalized = normalizeText(targetPath);
21
+ if (!normalized) {
22
+ return "";
23
+ }
24
+ if (isCrossPlatformAbsolutePath(normalized)) {
25
+ return normalizeDisplayPath(normalized);
26
+ }
27
+ return normalizeDisplayPath(path.resolve(normalized));
28
+ }
29
+
30
+ function resolveCrossPlatformPathFromRoot(rootPath, ...segments) {
31
+ const normalizedRoot = normalizeText(rootPath);
32
+ const normalizedSegments = segments
33
+ .flat()
34
+ .map((segment) => normalizeText(segment))
35
+ .filter(Boolean);
36
+
37
+ if (!normalizedRoot) {
38
+ return resolveCrossPlatformPath(path.join(...normalizedSegments));
39
+ }
40
+
41
+ // CI often runs on Linux even when users configure Windows absolute roots.
42
+ // Preserve those roots instead of reinterpreting `E:/...` as `<cwd>/E:/...`.
43
+ if (path.win32.isAbsolute(normalizedRoot) && !path.isAbsolute(normalizedRoot)) {
44
+ return normalizeDisplayPath(path.win32.resolve(normalizedRoot, ...normalizedSegments));
45
+ }
46
+ return normalizeDisplayPath(path.resolve(normalizedRoot, ...normalizedSegments));
47
+ }
48
+
49
+ module.exports = {
50
+ isCrossPlatformAbsolutePath,
51
+ normalizeDisplayPath,
52
+ resolveCrossPlatformPath,
53
+ resolveCrossPlatformPathFromRoot,
54
+ };
@@ -1,12 +1,17 @@
1
1
  const fs = require("fs");
2
2
  const path = require("path");
3
3
  const { spawnSync } = require("child_process");
4
+ const {
5
+ normalizeDisplayPath,
6
+ resolveCrossPlatformPath,
7
+ resolveCrossPlatformPathFromRoot,
8
+ } = require("./path-utils");
4
9
 
5
10
  function loadProjectRadarConfig(config = {}) {
6
- const workspaceRoot = normalizeDisplayPath(path.resolve(String(config.workspaceRoot || process.cwd())));
7
- const configFile = normalizeDisplayPath(path.resolve(String(
11
+ const workspaceRoot = resolveCrossPlatformPath(String(config.workspaceRoot || process.cwd()));
12
+ const configFile = resolveCrossPlatformPath(String(
8
13
  config.projectRadarConfigFile || path.join(workspaceRoot, ".codex", "code-projects.json")
9
- )));
14
+ ));
10
15
 
11
16
  let raw = "";
12
17
  try {
@@ -196,7 +201,7 @@ function buildMissingRepoFacts(repoRoot) {
196
201
  function buildWorkspaceFileInfo(root, relativePath, kind) {
197
202
  const normalizedRelativePath = normalizeRelativePath(relativePath);
198
203
  const absolutePath = normalizedRelativePath
199
- ? normalizeDisplayPath(path.resolve(root, ...normalizedRelativePath.split("/")))
204
+ ? resolveCrossPlatformPathFromRoot(root, ...normalizedRelativePath.split("/"))
200
205
  : "";
201
206
  return {
202
207
  kind,
@@ -218,9 +223,9 @@ function normalizeProjectEntry(entry, workspaceRoot) {
218
223
  slug,
219
224
  title: normalizeText(project.title) || slug,
220
225
  aliases: normalizeAliases(project.aliases),
221
- repoRoot: normalizeDisplayPath(path.resolve(repoRoot)),
226
+ repoRoot: resolveCrossPlatformPath(repoRoot),
222
227
  notePath,
223
- noteAbsolutePath: normalizeDisplayPath(path.resolve(workspaceRoot, ...notePath.split("/"))),
228
+ noteAbsolutePath: resolveCrossPlatformPathFromRoot(workspaceRoot, ...notePath.split("/")),
224
229
  overviewFiles: normalizeRelativePathList(project.overviewFiles),
225
230
  graphReportPath: normalizeRelativePath(project.graphReportPath),
226
231
  timelineLabel: normalizeText(project.timelineLabel),
@@ -383,10 +388,6 @@ function normalizeCommandStderr(value) {
383
388
  return String(value || "").replace(/\r\n/g, "\n").trim();
384
389
  }
385
390
 
386
- function normalizeDisplayPath(targetPath) {
387
- return normalizeText(targetPath).replace(/\\/g, "/");
388
- }
389
-
390
391
  function normalizeText(value) {
391
392
  return typeof value === "string" ? value.trim() : "";
392
393
  }