codexmate 0.0.31 → 0.0.33
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 +92 -308
- package/README.zh.md +94 -318
- package/cli/local-bridge.js +227 -0
- package/cli/update.js +162 -0
- package/cli.js +357 -112
- package/lib/cli-sessions.js +16 -6
- package/lib/win-tray.js +119 -0
- package/package.json +2 -2
- package/web-ui/app.js +4 -0
- package/web-ui/logic.sessions.mjs +17 -1
- package/web-ui/modules/app.computed.session.mjs +51 -315
- package/web-ui/modules/app.methods.agents.mjs +19 -0
- package/web-ui/modules/app.methods.claude-config.mjs +71 -2
- package/web-ui/modules/app.methods.codex-config.mjs +20 -0
- package/web-ui/modules/app.methods.providers.mjs +53 -7
- package/web-ui/modules/app.methods.session-actions.mjs +1 -1
- package/web-ui/modules/app.methods.session-browser.mjs +29 -1
- package/web-ui/modules/app.methods.startup-claude.mjs +4 -0
- package/web-ui/modules/i18n.dict.mjs +21 -3
- package/web-ui/partials/index/layout-header.html +1 -2
- package/web-ui/partials/index/modal-config-template-agents.html +12 -1
- package/web-ui/partials/index/modals-basic.html +14 -3
- package/web-ui/partials/index/panel-config-claude.html +57 -85
- package/web-ui/partials/index/panel-config-codex.html +60 -226
- package/web-ui/partials/index/panel-dashboard.html +0 -33
- package/web-ui/partials/index/panel-docs.html +21 -53
- package/web-ui/partials/index/panel-sessions.html +37 -20
- package/web-ui/partials/index/panel-trash.html +33 -38
- package/web-ui/partials/index/panel-usage.html +71 -304
- package/web-ui/styles/controls-forms.css +11 -0
- package/web-ui/styles/docs-panel.css +57 -83
- package/web-ui/styles/layout-shell.css +26 -24
- package/web-ui/styles/modals-core.css +33 -0
- package/web-ui/styles/responsive.css +5 -67
- package/web-ui/styles/sessions-list.css +274 -8
- package/web-ui/styles/sessions-toolbar-trash.css +185 -15
- package/web-ui/styles/sessions-usage.css +336 -788
package/lib/cli-sessions.js
CHANGED
|
@@ -153,20 +153,30 @@ function expandSessionQueryTokens(tokens) {
|
|
|
153
153
|
return result;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
function matchTokensInText(text, tokens, mode
|
|
156
|
+
function matchTokensInText(text, tokens, mode) {
|
|
157
|
+
if (mode === void 0) { mode = "and"; }
|
|
157
158
|
if (!Array.isArray(tokens) || tokens.length === 0) {
|
|
158
159
|
return true;
|
|
159
160
|
}
|
|
160
|
-
|
|
161
|
+
var haystack = String(text || "").toLowerCase();
|
|
161
162
|
if (!haystack) {
|
|
162
163
|
return false;
|
|
163
164
|
}
|
|
164
|
-
|
|
165
|
-
|
|
165
|
+
var boundaryChars = "\\s_\\-.,;:!?(){}[\\]<>/\"'`";
|
|
166
|
+
var hasCjk = function(token) { return /[⺀-鿿\u{20000}-\u{2fa1f}]/u.test(token); };
|
|
167
|
+
var matchToken = function(token) {
|
|
168
|
+
if (/^\d+$/.test(token) || hasCjk(token)) {
|
|
169
|
+
return haystack.indexOf(token) !== -1;
|
|
170
|
+
}
|
|
171
|
+
var escaped = token.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
172
|
+
var pattern = new RegExp("(?:^|[" + boundaryChars + "])" + escaped + "(?:$|[" + boundaryChars + "])", "i");
|
|
173
|
+
return pattern.test(haystack);
|
|
174
|
+
};
|
|
175
|
+
if (mode === "or") {
|
|
176
|
+
return tokens.some(function(token) { return matchToken(token); });
|
|
166
177
|
}
|
|
167
|
-
return tokens.every(token
|
|
178
|
+
return tokens.every(function(token) { return matchToken(token); });
|
|
168
179
|
}
|
|
169
|
-
|
|
170
180
|
function extractMessageFromRecord(record, source) {
|
|
171
181
|
if (!record) {
|
|
172
182
|
return null;
|
package/lib/win-tray.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
|
|
8
|
+
let trayProcess = null;
|
|
9
|
+
let exitMarkerPath = null;
|
|
10
|
+
let checkTimer = null;
|
|
11
|
+
|
|
12
|
+
function startWinTray(options = {}) {
|
|
13
|
+
if (process.platform !== 'win32') return;
|
|
14
|
+
|
|
15
|
+
const name = String(options.name || 'App');
|
|
16
|
+
const port = Number(options.port) || 3000;
|
|
17
|
+
const pid = process.pid;
|
|
18
|
+
const url = `http://localhost:${port}`;
|
|
19
|
+
|
|
20
|
+
exitMarkerPath = path.join(os.tmpdir(), `codexmate-tray-exit-${pid}.marker`);
|
|
21
|
+
|
|
22
|
+
try { fs.unlinkSync(exitMarkerPath); } catch (_) { }
|
|
23
|
+
|
|
24
|
+
const script = `
|
|
25
|
+
$ErrorActionPreference = 'Stop'
|
|
26
|
+
Add-Type -AssemblyName System.Windows.Forms, System.Drawing
|
|
27
|
+
|
|
28
|
+
$name = "${name}"
|
|
29
|
+
$url = "${url}"
|
|
30
|
+
$exitMarkerPath = "${exitMarkerPath.replace(/\\/g, '/')}"
|
|
31
|
+
|
|
32
|
+
$form = New-Object System.Windows.Forms.Form
|
|
33
|
+
$form.ShowInTaskbar = $false
|
|
34
|
+
$form.WindowState = [System.Windows.Forms.FormWindowState]::Minimized
|
|
35
|
+
$form.Visible = $false
|
|
36
|
+
|
|
37
|
+
$icon = New-Object System.Windows.Forms.NotifyIcon
|
|
38
|
+
try {
|
|
39
|
+
$bmp = New-Object System.Drawing.Bitmap 16, 16
|
|
40
|
+
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
|
41
|
+
$g.Clear([System.Drawing.Color]::FromArgb(59, 130, 246))
|
|
42
|
+
$font = New-Object System.Drawing.Font "Arial", 10, [System.Drawing.FontStyle]::Bold
|
|
43
|
+
$brush = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::White)
|
|
44
|
+
$g.DrawString("C", $font, $brush, -1, -1)
|
|
45
|
+
$g.Dispose()
|
|
46
|
+
$hIcon = $bmp.GetHicon()
|
|
47
|
+
$icon.Icon = [System.Drawing.Icon]::FromHandle($hIcon)
|
|
48
|
+
} catch {
|
|
49
|
+
$icon.Icon = [System.Drawing.SystemIcons]::Information
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
$icon.Text = if ($name.Length -gt 63) { $name.Substring(0, 63) } else { $name }
|
|
53
|
+
$icon.Visible = $true
|
|
54
|
+
|
|
55
|
+
$menu = New-Object System.Windows.Forms.ContextMenuStrip
|
|
56
|
+
$openItem = $menu.Items.Add("Open")
|
|
57
|
+
$openItem.add_Click({ try { [System.Diagnostics.Process]::Start($url) } catch {} })
|
|
58
|
+
$menu.Items.Add("-") | Out-Null
|
|
59
|
+
$exitItem = $menu.Items.Add("Exit")
|
|
60
|
+
$exitItem.add_Click({
|
|
61
|
+
try { New-Item -Path $exitMarkerPath -ItemType File -Force | Out-Null } catch {}
|
|
62
|
+
$icon.Visible = $false
|
|
63
|
+
$icon.Dispose()
|
|
64
|
+
[System.Windows.Forms.Application]::Exit()
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
$icon.ContextMenuStrip = $menu
|
|
68
|
+
$icon.add_DoubleClick({ try { [System.Diagnostics.Process]::Start($url) } catch {} })
|
|
69
|
+
|
|
70
|
+
[System.Windows.Forms.Application]::Run($form)
|
|
71
|
+
`.trim();
|
|
72
|
+
|
|
73
|
+
const scriptPath = path.join(os.tmpdir(), `codexmate-tray-${pid}.ps1`);
|
|
74
|
+
fs.writeFileSync(scriptPath, script, 'utf8');
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
trayProcess = spawn('powershell.exe', [
|
|
78
|
+
'-NoProfile', '-Sta', '-WindowStyle', 'Hidden',
|
|
79
|
+
'-ExecutionPolicy', 'Bypass', '-File', scriptPath
|
|
80
|
+
], { stdio: 'ignore', detached: true, windowsHide: true });
|
|
81
|
+
trayProcess.unref();
|
|
82
|
+
trayProcess.on('error', () => { });
|
|
83
|
+
trayProcess.on('exit', () => {
|
|
84
|
+
trayProcess = null;
|
|
85
|
+
try { fs.unlinkSync(scriptPath); } catch (_) { }
|
|
86
|
+
});
|
|
87
|
+
} catch (_) {
|
|
88
|
+
try { fs.unlinkSync(scriptPath); } catch (_) { }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
checkTimer = setInterval(() => {
|
|
92
|
+
if (!exitMarkerPath) return;
|
|
93
|
+
try {
|
|
94
|
+
fs.accessSync(exitMarkerPath, fs.constants.F_OK);
|
|
95
|
+
try { fs.unlinkSync(exitMarkerPath); } catch (_) { }
|
|
96
|
+
process.exit(0);
|
|
97
|
+
} catch (_) { }
|
|
98
|
+
}, 1500);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function stopWinTray() {
|
|
102
|
+
if (checkTimer) { clearInterval(checkTimer); checkTimer = null; }
|
|
103
|
+
if (trayProcess) {
|
|
104
|
+
try { trayProcess.kill(); } catch (_) { }
|
|
105
|
+
trayProcess = null;
|
|
106
|
+
}
|
|
107
|
+
if (exitMarkerPath) {
|
|
108
|
+
try { fs.unlinkSync(exitMarkerPath); } catch (_) { }
|
|
109
|
+
exitMarkerPath = null;
|
|
110
|
+
}
|
|
111
|
+
if (process.platform === 'win32') {
|
|
112
|
+
try {
|
|
113
|
+
const scriptPath = path.join(os.tmpdir(), `codexmate-tray-${process.pid}.ps1`);
|
|
114
|
+
fs.unlinkSync(scriptPath);
|
|
115
|
+
} catch (_) { }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
module.exports = { startWinTray, stopWinTray };
|
package/package.json
CHANGED
package/web-ui/app.js
CHANGED
|
@@ -30,6 +30,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
30
30
|
data() {
|
|
31
31
|
return {
|
|
32
32
|
lang: 'zh',
|
|
33
|
+
appVersion: '',
|
|
33
34
|
mainTab: 'dashboard',
|
|
34
35
|
configMode: 'codex',
|
|
35
36
|
currentProvider: '',
|
|
@@ -42,6 +43,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
42
43
|
editingCodexBudgetField: '',
|
|
43
44
|
providersList: [],
|
|
44
45
|
localBridgeExcluded: [],
|
|
46
|
+
claudeLocalBridgeExcluded: [],
|
|
45
47
|
models: [],
|
|
46
48
|
codexModelsLoading: false,
|
|
47
49
|
modelsSource: 'remote',
|
|
@@ -58,10 +60,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
58
60
|
messageType: '',
|
|
59
61
|
showAddModal: false,
|
|
60
62
|
showEditModal: false,
|
|
63
|
+
showEditProviderKey: false,
|
|
61
64
|
showModelModal: false,
|
|
62
65
|
showModelListModal: false,
|
|
63
66
|
showClaudeConfigModal: false,
|
|
64
67
|
showEditConfigModal: false,
|
|
68
|
+
showEditClaudeConfigKey: false,
|
|
65
69
|
showOpenclawConfigModal: false,
|
|
66
70
|
showConfigTemplateModal: false,
|
|
67
71
|
showAgentsModal: false,
|
|
@@ -424,6 +424,22 @@ export function buildUsageChartGroups(sessions = [], options = {}) {
|
|
|
424
424
|
}
|
|
425
425
|
const { range, buckets } = buildUsageBuckets(normalizedSessions, options);
|
|
426
426
|
const bucketMap = new Map(buckets.map((bucket) => [bucket.key, bucket]));
|
|
427
|
+
|
|
428
|
+
// Range window: filter normalizedSessions to the selected range
|
|
429
|
+
var rangeStartMs = 0;
|
|
430
|
+
var rangeEndMs = Number.POSITIVE_INFINITY;
|
|
431
|
+
if (range !== "all" && buckets.length > 0) {
|
|
432
|
+
var firstKey = buckets[0].key;
|
|
433
|
+
var lastKey = buckets[buckets.length - 1].key;
|
|
434
|
+
rangeStartMs = Date.parse(firstKey + "T00:00:00.000Z");
|
|
435
|
+
rangeEndMs = Date.parse(lastKey + "T23:59:59.999Z");
|
|
436
|
+
}
|
|
437
|
+
var rangeFiltered = range === "all"
|
|
438
|
+
? normalizedSessions
|
|
439
|
+
: normalizedSessions.filter(function(ns) {
|
|
440
|
+
return ns.updatedAtMs >= rangeStartMs && ns.updatedAtMs <= rangeEndMs;
|
|
441
|
+
});
|
|
442
|
+
|
|
427
443
|
let codexTotal = 0;
|
|
428
444
|
let claudeTotal = 0;
|
|
429
445
|
let messageTotal = 0;
|
|
@@ -455,7 +471,7 @@ export function buildUsageChartGroups(sessions = [], options = {}) {
|
|
|
455
471
|
const topSessionsByMessages = [];
|
|
456
472
|
const filteredSessions = [];
|
|
457
473
|
|
|
458
|
-
for (const normalized of
|
|
474
|
+
for (const normalized of rangeFiltered) {
|
|
459
475
|
const { session, sessionIndex, source, updatedAtMs, sessionStartedAtMs, sessionEndedAtMs, bucketKey } = normalized;
|
|
460
476
|
const stamp = new Date(updatedAtMs);
|
|
461
477
|
const bucket = bucketMap.get(bucketKey);
|