codexmate 0.1.9 → 0.1.12

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 (45) hide show
  1. package/README.md +0 -4
  2. package/README.vi.md +0 -4
  3. package/README.zh.md +0 -4
  4. package/cli/agents-files.js +27 -3
  5. package/cli/prompt-history.js +171 -0
  6. package/cli/system-prompt-files.js +20 -2
  7. package/cli.js +83 -2694
  8. package/package.json +1 -1
  9. package/web-ui/app.js +39 -56
  10. package/web-ui/index.html +0 -1
  11. package/web-ui/modules/app.computed.dashboard.mjs +0 -3
  12. package/web-ui/modules/app.computed.main-tabs.mjs +0 -474
  13. package/web-ui/modules/app.methods.agents.mjs +90 -0
  14. package/web-ui/modules/app.methods.index.mjs +1 -3
  15. package/web-ui/modules/app.methods.navigation.mjs +8 -3
  16. package/web-ui/modules/app.methods.session-actions.mjs +57 -25
  17. package/web-ui/modules/app.methods.system-prompt.mjs +81 -0
  18. package/web-ui/modules/app.methods.web-ui-preferences.mjs +26 -9
  19. package/web-ui/modules/i18n/locales/en.mjs +10 -265
  20. package/web-ui/modules/i18n/locales/ja.mjs +10 -265
  21. package/web-ui/modules/i18n/locales/vi.mjs +10 -266
  22. package/web-ui/modules/i18n/locales/zh-tw.mjs +10 -265
  23. package/web-ui/modules/i18n/locales/zh.mjs +10 -265
  24. package/web-ui/partials/index/layout-header.html +5 -48
  25. package/web-ui/partials/index/panel-config-claude.html +3 -3
  26. package/web-ui/partials/index/panel-config-codex.html +3 -3
  27. package/web-ui/partials/index/panel-config-kilocode.html +5 -5
  28. package/web-ui/partials/index/panel-config-openclaw.html +3 -3
  29. package/web-ui/partials/index/panel-config-opencode.html +5 -5
  30. package/web-ui/partials/index/panel-dashboard.html +0 -21
  31. package/web-ui/partials/index/panel-prompts.html +52 -0
  32. package/web-ui/partials/index/panel-sessions.html +12 -0
  33. package/web-ui/partials/index/panel-settings.html +35 -0
  34. package/web-ui/res/web-ui-render.precompiled.js +543 -1376
  35. package/web-ui/session-helpers.mjs +0 -16
  36. package/web-ui/styles/modals-core.css +86 -0
  37. package/web-ui/styles/sessions-preview.css +38 -0
  38. package/web-ui/styles/titles-cards.css +2 -2
  39. package/web-ui/styles.css +0 -1
  40. package/lib/automation.js +0 -404
  41. package/lib/task-orchestrator.js +0 -938
  42. package/lib/task-workspace-chat.js +0 -292
  43. package/web-ui/modules/app.methods.task-orchestration.mjs +0 -895
  44. package/web-ui/partials/index/panel-orchestration.html +0 -598
  45. package/web-ui/styles/task-orchestration.css +0 -2979
package/README.md CHANGED
@@ -184,10 +184,6 @@ Special thanks to all contributors for their contributions to Codex Mate ❤️
184
184
  <img src="https://contrib.rocks/image?repo=SakuraByteCore/codexmate" />
185
185
  </a>
186
186
 
187
- ## Star History
188
-
189
- [![Star History Chart](https://api.star-history.com/svg?repos=SakuraByteCore/codexmate&type=Date)](https://star-history.com/#SakuraByteCore/codexmate&Date)
190
-
191
187
  ## License
192
188
 
193
189
  Apache-2.0
package/README.vi.md CHANGED
@@ -167,10 +167,6 @@ Cảm ơn tất cả những người đóng góp cho Codex Mate ❤️
167
167
  <img src="https://contrib.rocks/image?repo=SakuraByteCore/codexmate" alt="Contributors" />
168
168
  </a>
169
169
 
170
- ## Lịch sử Star
171
-
172
- [![Star History Chart](https://api.star-history.com/svg?repos=SakuraByteCore/codexmate&type=Date)](https://star-history.com/#SakuraByteCore/codexmate&Date)
173
-
174
170
  ## Giấy phép
175
171
 
176
172
  Apache-2.0
package/README.zh.md CHANGED
@@ -181,10 +181,6 @@ flowchart TD
181
181
  <img src="https://contrib.rocks/image?repo=SakuraByteCore/codexmate" />
182
182
  </a>
183
183
 
184
- ## Star 历史
185
-
186
- [![Star History Chart](https://api.star-history.com/svg?repos=SakuraByteCore/codexmate&type=Date)](https://star-history.com/#SakuraByteCore/codexmate&Date)
187
-
188
184
  ## 开源协议
189
185
 
190
186
  Apache-2.0
@@ -14,7 +14,8 @@ function createAgentsFileController(deps = {}) {
14
14
  CLAUDE_DIR,
15
15
  CLAUDE_MD_FILE_NAME,
16
16
  readOpenclawAgentsFile,
17
- readOpenclawWorkspaceFile
17
+ readOpenclawWorkspaceFile,
18
+ backupPromptBeforeWrite
18
19
  } = deps;
19
20
 
20
21
  if (!fs) throw new Error('createAgentsFileController 缺少 fs');
@@ -32,6 +33,7 @@ function createAgentsFileController(deps = {}) {
32
33
  if (typeof CLAUDE_MD_FILE_NAME !== 'string' || !CLAUDE_MD_FILE_NAME) throw new Error('createAgentsFileController 缺少 CLAUDE_MD_FILE_NAME');
33
34
  if (typeof readOpenclawAgentsFile !== 'function') throw new Error('createAgentsFileController 缺少 readOpenclawAgentsFile');
34
35
  if (typeof readOpenclawWorkspaceFile !== 'function') throw new Error('createAgentsFileController 缺少 readOpenclawWorkspaceFile');
36
+ if (typeof backupPromptBeforeWrite !== 'function' && typeof backupPromptBeforeWrite !== 'undefined') throw new Error('createAgentsFileController 备份回调无效');
35
37
 
36
38
  function resolveAgentsFilePath(params = {}) {
37
39
  const baseDir = typeof params.baseDir === 'string' && params.baseDir.trim()
@@ -40,6 +42,12 @@ function createAgentsFileController(deps = {}) {
40
42
  return path.join(baseDir, AGENTS_FILE_NAME);
41
43
  }
42
44
 
45
+ function sanitizeHistoryId(raw) {
46
+ const safe = typeof raw === 'string' ? raw.trim() : '';
47
+ if (!safe) return 'global';
48
+ return safe.replace(/[^A-Za-z0-9_.-]/g, '_').slice(0, 64) || 'global';
49
+ }
50
+
43
51
  function validateAgentsBaseDir(filePath) {
44
52
  const dirPath = path.dirname(filePath);
45
53
  try {
@@ -152,6 +160,13 @@ function createAgentsFileController(deps = {}) {
152
160
  if (content.length > 2 * 1024 * 1024) {
153
161
  return { error: 'content too large (max 2MB)' };
154
162
  }
163
+ if (typeof backupPromptBeforeWrite === 'function') {
164
+ var bucket = resolved.isProject
165
+ ? 'claude-project_' + sanitizeHistoryId(resolved.projectPath)
166
+ : 'claude-global';
167
+ backupPromptBeforeWrite(bucket, filePath);
168
+ resolved.historyBucket = bucket;
169
+ }
155
170
  var lineEnding = params.lineEnding === '\r\n' ? '\r\n' : '\n';
156
171
  var normalized = normalizeLineEnding(content, lineEnding);
157
172
  var finalContent = ensureUtf8Bom(normalized);
@@ -159,6 +174,7 @@ function createAgentsFileController(deps = {}) {
159
174
  ensureDir(path.dirname(filePath));
160
175
  fs.writeFileSync(filePath, finalContent, 'utf-8');
161
176
  var result = { success: true, path: filePath };
177
+ if (resolved.historyBucket) result.historyBucket = resolved.historyBucket;
162
178
  if (resolved.isProject) {
163
179
  result.projectPath = resolved.projectPath;
164
180
  result.detectionSource = resolved.detectionSource;
@@ -218,13 +234,21 @@ function createAgentsFileController(deps = {}) {
218
234
  if (content.length > 2 * 1024 * 1024) {
219
235
  return { error: '内容过大(最大 2MB)' };
220
236
  }
237
+ let agentsHistoryBucket = '';
238
+ if (typeof backupPromptBeforeWrite === 'function') {
239
+ const bucket = 'codex_' + sanitizeHistoryId(String(params.baseDir || '').trim() || 'global');
240
+ backupPromptBeforeWrite(bucket, filePath);
241
+ agentsHistoryBucket = bucket;
242
+ }
221
243
  const lineEnding = params.lineEnding === '\r\n' ? '\r\n' : '\n';
222
- const normalized = normalizeLineEnding(content, lineEnding);
244
+ var normalized = normalizeLineEnding(content, lineEnding);
223
245
  const finalContent = ensureUtf8Bom(normalized);
224
246
 
225
247
  try {
226
248
  fs.writeFileSync(filePath, finalContent, 'utf-8');
227
- return { success: true, path: filePath };
249
+ const agentsResult = { success: true, path: filePath };
250
+ if (agentsHistoryBucket) agentsResult.historyBucket = agentsHistoryBucket;
251
+ return agentsResult;
228
252
  } catch (e) {
229
253
  return { error: `写入 AGENTS.md 失败: ${e.message}` };
230
254
  }
@@ -0,0 +1,171 @@
1
+ 'use strict';
2
+
3
+ function createPromptHistoryController(deps = {}) {
4
+ const {
5
+ fs,
6
+ path,
7
+ CONFIG_DIR,
8
+ MAX_HISTORY = 20
9
+ } = deps;
10
+
11
+ if (!fs) throw new Error('createPromptHistoryController 缺少 fs');
12
+ if (!path) throw new Error('createPromptHistoryController 缺少 path');
13
+ if (typeof CONFIG_DIR !== 'string' || !CONFIG_DIR) throw new Error('createPromptHistoryController 缺少 CONFIG_DIR');
14
+
15
+ const HISTORY_ROOT = path.join(CONFIG_DIR, 'codexmate-prompt-history');
16
+ const STAMP_RE = /^(\d{8})-(\d{6})-(\d{3})\.bak$/;
17
+
18
+ function sanitizeBucket(bucket) {
19
+ const raw = typeof bucket === 'string' ? bucket.trim() : '';
20
+ if (!raw) throw new Error('prompt history bucket 不能为空');
21
+ const safe = raw.replace(/[^A-Za-z0-9_.-]/g, '_');
22
+ if (!safe) throw new Error('prompt history bucket 无效: ' + bucket);
23
+ return safe;
24
+ }
25
+
26
+ function resolveBucketDir(bucket) {
27
+ const safe = sanitizeBucket(bucket);
28
+ return path.join(HISTORY_ROOT, safe);
29
+ }
30
+
31
+ function buildTimestamp(d = new Date()) {
32
+ const pad2 = (n) => String(n).padStart(2, '0');
33
+ const pad3 = (n) => String(n).padStart(3, '0');
34
+ return `${d.getFullYear()}${pad2(d.getMonth() + 1)}${pad2(d.getDate())}`
35
+ + `-${pad2(d.getHours())}${pad2(d.getMinutes())}${pad2(d.getSeconds())}`
36
+ + `-${pad3(d.getMilliseconds())}`;
37
+ }
38
+
39
+ function parseStamp(name) {
40
+ return STAMP_RE.test(name) ? name.slice(0, name.length - 4) : null;
41
+ }
42
+
43
+ function ensureDirSync(dir) {
44
+ fs.mkdirSync(dir, { recursive: true });
45
+ }
46
+
47
+ function rmtreeSync(dir) {
48
+ try {
49
+ fs.rmSync(dir, { recursive: true, force: true });
50
+ } catch (_) {}
51
+ }
52
+
53
+ function backupPromptBeforeWrite(bucket, currentFilePath) {
54
+ const dir = resolveBucketDir(bucket);
55
+ const exists = fs.existsSync(currentFilePath);
56
+ if (!exists) return { backedUp: false, entries: listPromptHistory(bucket) };
57
+ let raw;
58
+ try {
59
+ raw = fs.readFileSync(currentFilePath, 'utf-8');
60
+ } catch (e) {
61
+ return { backedUp: false, error: '读取当前文件失败: ' + e.message };
62
+ }
63
+ ensureDirSync(dir);
64
+ const stamp = buildTimestamp();
65
+ const dest = path.join(dir, stamp + '.bak');
66
+ try {
67
+ fs.writeFileSync(dest, raw, 'utf-8');
68
+ } catch (e) {
69
+ return { backedUp: false, error: '写入备份失败: ' + e.message };
70
+ }
71
+ trimHistory(dir);
72
+ return { backedUp: true, entries: listPromptHistory(bucket) };
73
+ }
74
+
75
+ function listPromptHistory(bucket) {
76
+ const dir = resolveBucketDir(bucket);
77
+ if (!fs.existsSync(dir)) return [];
78
+ let names;
79
+ try {
80
+ names = fs.readdirSync(dir);
81
+ } catch (_) {
82
+ return [];
83
+ }
84
+ const items = [];
85
+ for (const name of names) {
86
+ const stamp = parseStamp(name);
87
+ if (!stamp) continue;
88
+ let stat;
89
+ try {
90
+ stat = fs.statSync(path.join(dir, name));
91
+ } catch (_) {
92
+ continue;
93
+ }
94
+ items.push({
95
+ id: stamp,
96
+ bucket: sanitizeBucket(bucket),
97
+ size: Number(stat.size) || 0,
98
+ mtimeMs: Number(stat.mtimeMs) || 0
99
+ });
100
+ }
101
+ items.sort((a, b) => b.mtimeMs - a.mtimeMs);
102
+ return items;
103
+ }
104
+
105
+ function readPromptHistory(bucket, id) {
106
+ const dir = resolveBucketDir(bucket);
107
+ const stamp = typeof id === 'string' ? id.trim() : '';
108
+ const fullName = stamp + '.bak';
109
+ if (!stamp || !STAMP_RE.test(fullName)) {
110
+ return { error: 'history id invalid' };
111
+ }
112
+ const file = path.join(dir, fullName);
113
+ if (!fs.existsSync(file)) return { error: 'history entry 不存在' };
114
+ try {
115
+ const content = fs.readFileSync(file, 'utf-8');
116
+ return { id: stamp, content, bucket: sanitizeBucket(bucket) };
117
+ } catch (e) {
118
+ return { error: '读取 history 失败: ' + e.message };
119
+ }
120
+ }
121
+
122
+ function trimHistory(dir) {
123
+ let names;
124
+ try {
125
+ names = fs.readdirSync(dir);
126
+ } catch (_) {
127
+ return;
128
+ }
129
+ const stamped = [];
130
+ for (const name of names) {
131
+ const stamp = parseStamp(name);
132
+ if (!stamp) continue;
133
+ let stat;
134
+ try {
135
+ stat = fs.statSync(path.join(dir, name));
136
+ } catch (_) {
137
+ continue;
138
+ }
139
+ stamped.push({ name, mtimeMs: Number(stat.mtimeMs) || 0 });
140
+ }
141
+ stamped.sort((a, b) => b.mtimeMs - a.mtimeMs);
142
+ if (stamped.length <= MAX_HISTORY) return;
143
+ for (let i = MAX_HISTORY; i < stamped.length; i++) {
144
+ try {
145
+ fs.unlinkSync(path.join(dir, stamped[i].name));
146
+ } catch (_) {}
147
+ }
148
+ }
149
+
150
+ function clearPromptHistory(bucket) {
151
+ if (!bucket) {
152
+ rmtreeSync(HISTORY_ROOT);
153
+ return { cleared: true };
154
+ }
155
+ const dir = resolveBucketDir(bucket);
156
+ rmtreeSync(dir);
157
+ return { cleared: true };
158
+ }
159
+
160
+ return {
161
+ sanitizeBucket,
162
+ backupPromptBeforeWrite,
163
+ listPromptHistory,
164
+ readPromptHistory,
165
+ clearPromptHistory
166
+ };
167
+ }
168
+
169
+ module.exports = {
170
+ createPromptHistoryController
171
+ };
@@ -8,7 +8,8 @@ function createSystemPromptFileController(deps = {}) {
8
8
  crypto,
9
9
  buildLineDiff,
10
10
  CONFIG_DIR,
11
- PI_AGENT_DIR
11
+ PI_AGENT_DIR,
12
+ backupPromptBeforeWrite
12
13
  } = deps;
13
14
 
14
15
  if (!fs) throw new Error('createSystemPromptFileController 缺少 fs');
@@ -18,6 +19,7 @@ function createSystemPromptFileController(deps = {}) {
18
19
  if (typeof buildLineDiff !== 'function') throw new Error('createSystemPromptFileController 缺少 buildLineDiff');
19
20
  if (typeof CONFIG_DIR !== 'string' || !CONFIG_DIR) throw new Error('createSystemPromptFileController 缺少 CONFIG_DIR');
20
21
  if (typeof PI_AGENT_DIR !== 'string' || !PI_AGENT_DIR) throw new Error('createSystemPromptFileController 缺少 PI_AGENT_DIR');
22
+ if (typeof backupPromptBeforeWrite !== 'function' && typeof backupPromptBeforeWrite !== 'undefined') throw new Error('createSystemPromptFileController 备份回调无效');
21
23
 
22
24
  const MODES = {
23
25
  system: 'SYSTEM.md',
@@ -34,6 +36,12 @@ function createSystemPromptFileController(deps = {}) {
34
36
  return key;
35
37
  }
36
38
 
39
+ function sanitizeSystemHistoryId(raw) {
40
+ const safe = typeof raw === 'string' ? raw.trim() : '';
41
+ if (!safe) return 'global';
42
+ return safe.replace(/[^A-Za-z0-9_.-]/g, '_').slice(0, 64) || 'global';
43
+ }
44
+
37
45
  function normalizeScope(scope) {
38
46
  const key = typeof scope === 'string' ? scope.trim() : '';
39
47
  if (!SCOPES.includes(key)) {
@@ -104,16 +112,26 @@ function createSystemPromptFileController(deps = {}) {
104
112
  if (baseHash && baseHash !== current.hash) {
105
113
  return { error: '文件已被外部修改,请重新加载后再保存' };
106
114
  }
115
+ let sysHistoryBucket = '';
107
116
  try {
108
117
  const dir = path.dirname(target.path);
109
118
  fs.mkdirSync(dir, { recursive: true });
119
+ if (typeof backupPromptBeforeWrite === 'function') {
120
+ const bucket = 'system_' + target.scope + '_' + (target.scope === 'project'
121
+ ? sanitizeSystemHistoryId(path.dirname(target.path))
122
+ : 'global');
123
+ backupPromptBeforeWrite(bucket, target.path);
124
+ sysHistoryBucket = bucket;
125
+ }
110
126
  const finalContent = content.endsWith('\n') ? content : content + '\n';
111
127
  fs.writeFileSync(target.path, finalContent, { encoding: 'utf8', mode: 0o600 });
112
128
  try { fs.chmodSync(target.path, 0o600); } catch (_) {}
113
129
  } catch (e) {
114
130
  return { error: '写入 system prompt 失败: ' + e.message };
115
131
  }
116
- return readSystemPromptFilePath(target);
132
+ const saved = readSystemPromptFilePath(target);
133
+ if (sysHistoryBucket) saved.historyBucket = sysHistoryBucket;
134
+ return saved;
117
135
  }
118
136
 
119
137
  function readSystemPromptFilePath(resolved) {