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.
- package/README.md +0 -4
- package/README.vi.md +0 -4
- package/README.zh.md +0 -4
- package/cli/agents-files.js +27 -3
- package/cli/prompt-history.js +171 -0
- package/cli/system-prompt-files.js +20 -2
- package/cli.js +83 -2694
- package/package.json +1 -1
- package/web-ui/app.js +39 -56
- package/web-ui/index.html +0 -1
- package/web-ui/modules/app.computed.dashboard.mjs +0 -3
- package/web-ui/modules/app.computed.main-tabs.mjs +0 -474
- package/web-ui/modules/app.methods.agents.mjs +90 -0
- package/web-ui/modules/app.methods.index.mjs +1 -3
- package/web-ui/modules/app.methods.navigation.mjs +8 -3
- package/web-ui/modules/app.methods.session-actions.mjs +57 -25
- package/web-ui/modules/app.methods.system-prompt.mjs +81 -0
- package/web-ui/modules/app.methods.web-ui-preferences.mjs +26 -9
- package/web-ui/modules/i18n/locales/en.mjs +10 -265
- package/web-ui/modules/i18n/locales/ja.mjs +10 -265
- package/web-ui/modules/i18n/locales/vi.mjs +10 -266
- package/web-ui/modules/i18n/locales/zh-tw.mjs +10 -265
- package/web-ui/modules/i18n/locales/zh.mjs +10 -265
- package/web-ui/partials/index/layout-header.html +5 -48
- package/web-ui/partials/index/panel-config-claude.html +3 -3
- package/web-ui/partials/index/panel-config-codex.html +3 -3
- package/web-ui/partials/index/panel-config-kilocode.html +5 -5
- package/web-ui/partials/index/panel-config-openclaw.html +3 -3
- package/web-ui/partials/index/panel-config-opencode.html +5 -5
- package/web-ui/partials/index/panel-dashboard.html +0 -21
- package/web-ui/partials/index/panel-prompts.html +52 -0
- package/web-ui/partials/index/panel-sessions.html +12 -0
- package/web-ui/partials/index/panel-settings.html +35 -0
- package/web-ui/res/web-ui-render.precompiled.js +543 -1376
- package/web-ui/session-helpers.mjs +0 -16
- package/web-ui/styles/modals-core.css +86 -0
- package/web-ui/styles/sessions-preview.css +38 -0
- package/web-ui/styles/titles-cards.css +2 -2
- package/web-ui/styles.css +0 -1
- package/lib/automation.js +0 -404
- package/lib/task-orchestrator.js +0 -938
- package/lib/task-workspace-chat.js +0 -292
- package/web-ui/modules/app.methods.task-orchestration.mjs +0 -895
- package/web-ui/partials/index/panel-orchestration.html +0 -598
- 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
|
-
[](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
|
-
[](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
|
-
[](https://star-history.com/#SakuraByteCore/codexmate&Date)
|
|
187
|
-
|
|
188
184
|
## 开源协议
|
|
189
185
|
|
|
190
186
|
Apache-2.0
|
package/cli/agents-files.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
132
|
+
const saved = readSystemPromptFilePath(target);
|
|
133
|
+
if (sysHistoryBucket) saved.historyBucket = sysHistoryBucket;
|
|
134
|
+
return saved;
|
|
117
135
|
}
|
|
118
136
|
|
|
119
137
|
function readSystemPromptFilePath(resolved) {
|