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.
Files changed (37) hide show
  1. package/README.md +92 -308
  2. package/README.zh.md +94 -318
  3. package/cli/local-bridge.js +227 -0
  4. package/cli/update.js +162 -0
  5. package/cli.js +357 -112
  6. package/lib/cli-sessions.js +16 -6
  7. package/lib/win-tray.js +119 -0
  8. package/package.json +2 -2
  9. package/web-ui/app.js +4 -0
  10. package/web-ui/logic.sessions.mjs +17 -1
  11. package/web-ui/modules/app.computed.session.mjs +51 -315
  12. package/web-ui/modules/app.methods.agents.mjs +19 -0
  13. package/web-ui/modules/app.methods.claude-config.mjs +71 -2
  14. package/web-ui/modules/app.methods.codex-config.mjs +20 -0
  15. package/web-ui/modules/app.methods.providers.mjs +53 -7
  16. package/web-ui/modules/app.methods.session-actions.mjs +1 -1
  17. package/web-ui/modules/app.methods.session-browser.mjs +29 -1
  18. package/web-ui/modules/app.methods.startup-claude.mjs +4 -0
  19. package/web-ui/modules/i18n.dict.mjs +21 -3
  20. package/web-ui/partials/index/layout-header.html +1 -2
  21. package/web-ui/partials/index/modal-config-template-agents.html +12 -1
  22. package/web-ui/partials/index/modals-basic.html +14 -3
  23. package/web-ui/partials/index/panel-config-claude.html +57 -85
  24. package/web-ui/partials/index/panel-config-codex.html +60 -226
  25. package/web-ui/partials/index/panel-dashboard.html +0 -33
  26. package/web-ui/partials/index/panel-docs.html +21 -53
  27. package/web-ui/partials/index/panel-sessions.html +37 -20
  28. package/web-ui/partials/index/panel-trash.html +33 -38
  29. package/web-ui/partials/index/panel-usage.html +71 -304
  30. package/web-ui/styles/controls-forms.css +11 -0
  31. package/web-ui/styles/docs-panel.css +57 -83
  32. package/web-ui/styles/layout-shell.css +26 -24
  33. package/web-ui/styles/modals-core.css +33 -0
  34. package/web-ui/styles/responsive.css +5 -67
  35. package/web-ui/styles/sessions-list.css +274 -8
  36. package/web-ui/styles/sessions-toolbar-trash.css +185 -15
  37. package/web-ui/styles/sessions-usage.css +336 -788
@@ -153,20 +153,30 @@ function expandSessionQueryTokens(tokens) {
153
153
  return result;
154
154
  }
155
155
 
156
- function matchTokensInText(text, tokens, mode = 'and') {
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
- const haystack = String(text || '').toLowerCase();
161
+ var haystack = String(text || "").toLowerCase();
161
162
  if (!haystack) {
162
163
  return false;
163
164
  }
164
- if (mode === 'or') {
165
- return tokens.some(token => haystack.includes(token));
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 => haystack.includes(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;
@@ -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
@@ -1,6 +1,6 @@
1
- {
1
+ {
2
2
  "name": "codexmate",
3
- "version": "0.0.31",
3
+ "version": "0.0.33",
4
4
  "description": "Codex/Claude Code/OpenClaw 配置、会话与任务编排 CLI + Web 工具",
5
5
  "main": "cli.js",
6
6
  "bin": {
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 normalizedSessions) {
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);