codexmate 0.0.28 → 0.0.30
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/cli/builtin-proxy.js +107 -2
- package/cli/config-bootstrap.js +30 -12
- package/cli/config-health.js +117 -1
- package/cli/local-bridge.js +324 -0
- package/cli/openai-bridge.js +195 -31
- package/cli.js +245 -28
- package/lib/cli-webhook.js +126 -0
- package/package.json +1 -1
- package/web-ui/app.js +28 -8
- package/web-ui/index.html +1 -0
- package/web-ui/logic.codex.mjs +13 -0
- package/web-ui/modules/app.computed.dashboard.mjs +25 -2
- package/web-ui/modules/app.computed.session.mjs +22 -17
- package/web-ui/modules/app.methods.claude-config.mjs +12 -2
- package/web-ui/modules/app.methods.codex-config.mjs +25 -0
- package/web-ui/modules/app.methods.index.mjs +2 -0
- package/web-ui/modules/app.methods.navigation.mjs +39 -8
- package/web-ui/modules/app.methods.providers.mjs +125 -8
- package/web-ui/modules/app.methods.session-actions.mjs +1 -1
- package/web-ui/modules/app.methods.session-browser.mjs +1 -1
- package/web-ui/modules/app.methods.session-trash.mjs +3 -4
- package/web-ui/modules/app.methods.startup-claude.mjs +1 -0
- package/web-ui/modules/app.methods.webhook.mjs +79 -0
- package/web-ui/modules/i18n.dict.mjs +1109 -72
- package/web-ui/modules/i18n.mjs +9 -3
- package/web-ui/modules/skills.methods.mjs +1 -0
- package/web-ui/partials/index/layout-header.html +25 -0
- package/web-ui/partials/index/modals-basic.html +0 -3
- package/web-ui/partials/index/panel-config-claude.html +8 -2
- package/web-ui/partials/index/panel-config-codex.html +28 -3
- package/web-ui/partials/index/panel-dashboard.html +33 -0
- package/web-ui/partials/index/panel-market.html +3 -3
- package/web-ui/partials/index/panel-plugins.html +2 -2
- package/web-ui/partials/index/panel-sessions.html +1 -9
- package/web-ui/partials/index/panel-settings.html +71 -134
- package/web-ui/partials/index/panel-trash.html +88 -0
- package/web-ui/session-helpers.mjs +20 -2
- package/web-ui/styles/dashboard.css +132 -0
- package/web-ui/styles/docs-panel.css +63 -39
- package/web-ui/styles/layout-shell.css +54 -34
- package/web-ui/styles/plugins-panel.css +121 -80
- package/web-ui/styles/sessions-list.css +41 -43
- package/web-ui/styles/sessions-preview.css +34 -38
- package/web-ui/styles/sessions-toolbar-trash.css +31 -27
- package/web-ui/styles/settings-panel.css +197 -33
- package/web-ui/styles/skills-list.css +12 -10
- package/web-ui/styles/skills-market.css +67 -44
- package/web-ui/styles/trash-panel.css +90 -0
- package/web-ui/styles/webhook.css +81 -0
- package/web-ui/styles.css +2 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const http = require('http');
|
|
4
|
+
const https = require('https');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
const ALLOWED_EVENTS = ['provider-switch', 'claude-md-edit'];
|
|
8
|
+
const DEFAULT_TIMEOUT_MS = 5000;
|
|
9
|
+
|
|
10
|
+
function defaultConfigPath() {
|
|
11
|
+
return path.join(os.homedir(), '.codex', 'codexmate-webhook.json');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function normalizeConfig(cfg) {
|
|
15
|
+
const out = { enabled: false, url: '', events: ALLOWED_EVENTS.slice() };
|
|
16
|
+
if (!cfg || typeof cfg !== 'object') return out;
|
|
17
|
+
out.enabled = !!cfg.enabled;
|
|
18
|
+
out.url = typeof cfg.url === 'string' ? cfg.url.trim() : '';
|
|
19
|
+
if (Array.isArray(cfg.events)) {
|
|
20
|
+
const filtered = cfg.events.filter(function (e) { return ALLOWED_EVENTS.indexOf(e) !== -1; });
|
|
21
|
+
out.events = filtered.length ? filtered : ALLOWED_EVENTS.slice();
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function loadWebhookConfig(filePath) {
|
|
27
|
+
const target = filePath || defaultConfigPath();
|
|
28
|
+
try {
|
|
29
|
+
if (!fs.existsSync(target)) {
|
|
30
|
+
return normalizeConfig({});
|
|
31
|
+
}
|
|
32
|
+
const raw = fs.readFileSync(target, 'utf-8');
|
|
33
|
+
return normalizeConfig(JSON.parse(raw));
|
|
34
|
+
} catch (_) {
|
|
35
|
+
return normalizeConfig({});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function saveWebhookConfig(cfg, filePath) {
|
|
40
|
+
const target = filePath || defaultConfigPath();
|
|
41
|
+
const normalized = normalizeConfig(cfg);
|
|
42
|
+
try {
|
|
43
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
44
|
+
} catch (_) {}
|
|
45
|
+
fs.writeFileSync(target, JSON.stringify(normalized, null, 2), 'utf-8');
|
|
46
|
+
return normalized;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function postJson(targetUrl, payload, timeoutMs) {
|
|
50
|
+
return new Promise(function (resolve) {
|
|
51
|
+
let parsed;
|
|
52
|
+
try {
|
|
53
|
+
parsed = new URL(targetUrl);
|
|
54
|
+
} catch (_) {
|
|
55
|
+
resolve({ ok: false, error: 'invalid-url' });
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const transport = parsed.protocol === 'https:' ? https : http;
|
|
59
|
+
const body = JSON.stringify(payload || {});
|
|
60
|
+
let req;
|
|
61
|
+
try {
|
|
62
|
+
req = transport.request({
|
|
63
|
+
method: 'POST',
|
|
64
|
+
protocol: parsed.protocol,
|
|
65
|
+
hostname: parsed.hostname,
|
|
66
|
+
port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80),
|
|
67
|
+
path: (parsed.pathname || '/') + (parsed.search || ''),
|
|
68
|
+
headers: {
|
|
69
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
70
|
+
'Content-Length': Buffer.byteLength(body, 'utf-8'),
|
|
71
|
+
'User-Agent': 'codexmate-webhook/1'
|
|
72
|
+
}
|
|
73
|
+
}, function (res) {
|
|
74
|
+
let raw = '';
|
|
75
|
+
res.on('data', function (chunk) {
|
|
76
|
+
if (raw.length < 1024) raw += chunk.toString('utf-8');
|
|
77
|
+
});
|
|
78
|
+
res.on('end', function () {
|
|
79
|
+
const status = res.statusCode || 0;
|
|
80
|
+
resolve({ ok: status >= 200 && status < 300, status: status, body: raw.slice(0, 200) });
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
} catch (e) {
|
|
84
|
+
resolve({ ok: false, error: e && e.message ? e.message : String(e) });
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const wait = Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : DEFAULT_TIMEOUT_MS;
|
|
88
|
+
req.setTimeout(wait, function () { req.destroy(new Error('timeout')); });
|
|
89
|
+
req.on('error', function (err) { resolve({ ok: false, error: err && err.message ? err.message : String(err) }); });
|
|
90
|
+
req.write(body);
|
|
91
|
+
req.end();
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function buildPayload(event, summary, details) {
|
|
96
|
+
return {
|
|
97
|
+
event: String(event || ''),
|
|
98
|
+
summary: String(summary || ''),
|
|
99
|
+
operator: process.env.USER || process.env.USERNAME || (os.userInfo && os.userInfo().username) || '',
|
|
100
|
+
timestamp: new Date().toISOString(),
|
|
101
|
+
details: details && typeof details === 'object' ? details : {}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function notifyWebhook(event, summary, details, options) {
|
|
106
|
+
const opts = options || {};
|
|
107
|
+
const cfg = opts.config ? normalizeConfig(opts.config) : loadWebhookConfig(opts.filePath);
|
|
108
|
+
if (!cfg.enabled || !cfg.url) {
|
|
109
|
+
return Promise.resolve({ ok: false, skipped: true, reason: 'disabled' });
|
|
110
|
+
}
|
|
111
|
+
if (cfg.events.indexOf(event) === -1) {
|
|
112
|
+
return Promise.resolve({ ok: false, skipped: true, reason: 'event-filtered' });
|
|
113
|
+
}
|
|
114
|
+
return postJson(cfg.url, buildPayload(event, summary, details), opts.timeoutMs);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = {
|
|
118
|
+
ALLOWED_EVENTS,
|
|
119
|
+
defaultConfigPath,
|
|
120
|
+
normalizeConfig,
|
|
121
|
+
loadWebhookConfig,
|
|
122
|
+
saveWebhookConfig,
|
|
123
|
+
notifyWebhook,
|
|
124
|
+
buildPayload,
|
|
125
|
+
postJson
|
|
126
|
+
};
|
package/package.json
CHANGED
package/web-ui/app.js
CHANGED
|
@@ -41,6 +41,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
41
41
|
modelAutoCompactTokenLimitInput: String(DEFAULT_MODEL_AUTO_COMPACT_TOKEN_LIMIT),
|
|
42
42
|
editingCodexBudgetField: '',
|
|
43
43
|
providersList: [],
|
|
44
|
+
localBridgeExcluded: [],
|
|
44
45
|
models: [],
|
|
45
46
|
codexModelsLoading: false,
|
|
46
47
|
modelsSource: 'remote',
|
|
@@ -202,8 +203,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
202
203
|
activeSessionDetailClipped: false,
|
|
203
204
|
sessionDetailLoading: false,
|
|
204
205
|
sessionDetailRequestSeq: 0,
|
|
205
|
-
sessionDetailInitialMessageLimit:
|
|
206
|
-
sessionDetailFetchStep:
|
|
206
|
+
sessionDetailInitialMessageLimit: 300,
|
|
207
|
+
sessionDetailFetchStep: 300,
|
|
207
208
|
sessionDetailMessageLimit: 80,
|
|
208
209
|
sessionDetailMessageLimitCap: 1000,
|
|
209
210
|
sessionTimelineActiveKey: '',
|
|
@@ -221,8 +222,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
221
222
|
sessionPreviewHeaderResizeObserver: null,
|
|
222
223
|
sessionListRenderEnabled: false,
|
|
223
224
|
sessionListVisibleCount: 0,
|
|
224
|
-
sessionListInitialBatchSize:
|
|
225
|
-
sessionListLoadStep:
|
|
225
|
+
sessionListInitialBatchSize: 40,
|
|
226
|
+
sessionListLoadStep: 80,
|
|
226
227
|
sessionPreviewRenderEnabled: false,
|
|
227
228
|
sessionTabRenderTicket: 0,
|
|
228
229
|
sessionPreviewVisibleCount: 0,
|
|
@@ -274,8 +275,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
274
275
|
newClaudeConfig: {
|
|
275
276
|
name: '',
|
|
276
277
|
apiKey: '',
|
|
277
|
-
baseUrl: '
|
|
278
|
-
model: '
|
|
278
|
+
baseUrl: '',
|
|
279
|
+
model: ''
|
|
279
280
|
},
|
|
280
281
|
currentOpenclawConfig: '',
|
|
281
282
|
openclawConfigs: {
|
|
@@ -336,6 +337,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
336
337
|
healthCheckLoading: false,
|
|
337
338
|
healthCheckResult: null,
|
|
338
339
|
healthCheckRemote: false,
|
|
340
|
+
providersHealthLoading: false,
|
|
341
|
+
providersHealthResult: null,
|
|
339
342
|
claudeDownloadLoading: false,
|
|
340
343
|
claudeDownloadProgress: 0,
|
|
341
344
|
claudeDownloadTimer: null,
|
|
@@ -398,7 +401,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
398
401
|
lastLoadedAt: '',
|
|
399
402
|
lastError: ''
|
|
400
403
|
},
|
|
401
|
-
_taskOrchestrationPollTimer: 0
|
|
404
|
+
_taskOrchestrationPollTimer: 0,
|
|
405
|
+
webhookConfig: { enabled: false, url: '', events: ['provider-switch', 'claude-md-edit'] },
|
|
406
|
+
webhookEventOptions: ['provider-switch', 'claude-md-edit'],
|
|
407
|
+
webhookSaving: false,
|
|
408
|
+
webhookTestResult: null,
|
|
409
|
+
webhookTesting: false,
|
|
402
410
|
};
|
|
403
411
|
},
|
|
404
412
|
|
|
@@ -406,6 +414,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
406
414
|
if (typeof this.initI18n === 'function') {
|
|
407
415
|
this.initI18n();
|
|
408
416
|
}
|
|
417
|
+
if (typeof this.loadWebhookSettings === 'function') {
|
|
418
|
+
this.loadWebhookSettings();
|
|
419
|
+
}
|
|
409
420
|
if (typeof this.t === 'function') {
|
|
410
421
|
this.confirmDialogConfirmText = this.t('confirm.ok');
|
|
411
422
|
this.confirmDialogCancelText = this.t('confirm.cancel');
|
|
@@ -414,7 +425,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
414
425
|
}
|
|
415
426
|
{
|
|
416
427
|
const NAV_STATE_STORAGE_KEY = 'codexmateNavState.v1';
|
|
417
|
-
const mainTabSet = new Set(['dashboard', 'config', 'sessions', 'usage', 'orchestration', 'market', 'plugins', 'docs', 'settings']);
|
|
428
|
+
const mainTabSet = new Set(['dashboard', 'config', 'sessions', 'usage', 'orchestration', 'market', 'plugins', 'docs', 'settings', 'trash']);
|
|
418
429
|
let restored = null;
|
|
419
430
|
try {
|
|
420
431
|
const raw = localStorage.getItem(NAV_STATE_STORAGE_KEY) || '';
|
|
@@ -428,6 +439,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
428
439
|
const nextConfigMode = restored && typeof restored.configMode === 'string'
|
|
429
440
|
? restored.configMode.trim().toLowerCase()
|
|
430
441
|
: '';
|
|
442
|
+
const nextSettingsTab = restored && typeof restored.settingsTab === 'string'
|
|
443
|
+
? restored.settingsTab.trim().toLowerCase()
|
|
444
|
+
: '';
|
|
431
445
|
let urlMainTab = '';
|
|
432
446
|
try {
|
|
433
447
|
const url = new URL(window.location.href);
|
|
@@ -440,6 +454,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
440
454
|
const resolvedMainTab = urlMainTab && mainTabSet.has(urlMainTab)
|
|
441
455
|
? urlMainTab
|
|
442
456
|
: nextMainTab;
|
|
457
|
+
if (nextSettingsTab && (nextSettingsTab === 'general' || nextSettingsTab === 'data')) {
|
|
458
|
+
this.settingsTab = nextSettingsTab;
|
|
459
|
+
}
|
|
443
460
|
if (nextConfigMode && typeof this.switchConfigMode === 'function') {
|
|
444
461
|
this.__navStateRestoring = true;
|
|
445
462
|
try {
|
|
@@ -554,6 +571,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
554
571
|
if (typeof this.runHealthCheck === 'function') {
|
|
555
572
|
void this.runHealthCheck({ doctor: true, silent: true });
|
|
556
573
|
}
|
|
574
|
+
if (typeof this.runProvidersHealthCheck === 'function') {
|
|
575
|
+
void this.runProvidersHealthCheck({ remote: true });
|
|
576
|
+
}
|
|
557
577
|
}
|
|
558
578
|
}
|
|
559
579
|
void this.refreshClaudeSelectionFromSettings({ silent: true });
|
package/web-ui/index.html
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
<!-- @include ./partials/index/panel-orchestration.html -->
|
|
22
22
|
<!-- @include ./partials/index/panel-docs.html -->
|
|
23
23
|
<!-- @include ./partials/index/panel-settings.html -->
|
|
24
|
+
<!-- @include ./partials/index/panel-trash.html -->
|
|
24
25
|
<!-- @include ./partials/index/panel-market.html -->
|
|
25
26
|
<!-- @include ./partials/index/panel-plugins.html -->
|
|
26
27
|
<!-- @include ./partials/index/layout-footer.html -->
|
package/web-ui/logic.codex.mjs
CHANGED
|
@@ -52,5 +52,18 @@ export const CODEX_PROVIDER_TEMPLATES = Object.freeze([
|
|
|
52
52
|
url: 'https://ai.muapi.cn/v1',
|
|
53
53
|
model: 'mimo-v2-pro',
|
|
54
54
|
useTransform: true
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
label: 'Telepub',
|
|
58
|
+
name: 'telepub',
|
|
59
|
+
url: 'https://voyage.prod.telepub.cn/voyage/api',
|
|
60
|
+
model: 'DeepSeek-V4-pro',
|
|
61
|
+
useTransform: true
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
label: 'AnyRouter',
|
|
65
|
+
name: 'anyrouter',
|
|
66
|
+
url: 'https://anyrouter.top/v1',
|
|
67
|
+
model: 'gpt-5.5'
|
|
55
68
|
}
|
|
56
69
|
]);
|
|
@@ -35,6 +35,7 @@ export function createDashboardComputed() {
|
|
|
35
35
|
return (name) => {
|
|
36
36
|
const target = String(name || '').trim();
|
|
37
37
|
if (!target) return '';
|
|
38
|
+
if (target === 'local') return '';
|
|
38
39
|
const dict = this.currentModels && typeof this.currentModels === 'object' ? this.currentModels : {};
|
|
39
40
|
const fromDict = typeof dict[target] === 'string' ? dict[target].trim() : '';
|
|
40
41
|
if (fromDict) return fromDict;
|
|
@@ -82,12 +83,20 @@ export function createDashboardComputed() {
|
|
|
82
83
|
return current;
|
|
83
84
|
},
|
|
84
85
|
displayProvidersList() {
|
|
85
|
-
const list = Array.isArray(this.providersList) ? this.providersList : [];
|
|
86
|
+
const list = Array.isArray(this.providersList) ? [...this.providersList] : [];
|
|
87
|
+
list.sort((a, b) => {
|
|
88
|
+
if (a.name === 'local') return -1;
|
|
89
|
+
if (b.name === 'local') return 1;
|
|
90
|
+
return 0;
|
|
91
|
+
});
|
|
86
92
|
return list;
|
|
87
93
|
},
|
|
88
94
|
|
|
89
95
|
displayProviderUrl() {
|
|
90
|
-
return (provider) =>
|
|
96
|
+
return (provider) => {
|
|
97
|
+
if (provider && provider.name === 'local') return '';
|
|
98
|
+
return getProviderDisplayUrl(provider);
|
|
99
|
+
};
|
|
91
100
|
},
|
|
92
101
|
|
|
93
102
|
isTransformProvider() {
|
|
@@ -220,6 +229,20 @@ export function createDashboardComputed() {
|
|
|
220
229
|
this.t('docs.tip.unix.2'),
|
|
221
230
|
this.t('docs.tip.unix.3')
|
|
222
231
|
];
|
|
232
|
+
},
|
|
233
|
+
providersHealthSummary() {
|
|
234
|
+
if (!this.providersHealthResult || !this.providersHealthResult.summary) {
|
|
235
|
+
return { total: 0, green: 0, yellow: 0, red: 0 };
|
|
236
|
+
}
|
|
237
|
+
return this.providersHealthResult.summary;
|
|
238
|
+
},
|
|
239
|
+
providersHealthTone() {
|
|
240
|
+
if (!this.providersHealthResult) return '';
|
|
241
|
+
const s = this.providersHealthResult.summary;
|
|
242
|
+
if (!s) return '';
|
|
243
|
+
if (s.red > 0) return 'error';
|
|
244
|
+
if (s.yellow > 0) return 'warn';
|
|
245
|
+
return 'ok';
|
|
223
246
|
}
|
|
224
247
|
};
|
|
225
248
|
}
|
|
@@ -153,7 +153,14 @@ const KNOWN_USAGE_MODEL_PRICING = Object.freeze({
|
|
|
153
153
|
'gpt-5.4': Object.freeze({ input: 2.5, output: 15, cacheRead: 0.25, cacheWrite: 0 }),
|
|
154
154
|
'gpt-5.4-mini': Object.freeze({ input: 0.75, output: 4.5, cacheRead: 0.075, cacheWrite: 0 }),
|
|
155
155
|
'gpt-5.3-codex': Object.freeze({ input: 1.75, output: 14, cacheRead: 0.175, cacheWrite: 0 }),
|
|
156
|
-
'gpt-5.2-codex': Object.freeze({ input: 1.75, output: 14, cacheRead: 0.175, cacheWrite: 0 })
|
|
156
|
+
'gpt-5.2-codex': Object.freeze({ input: 1.75, output: 14, cacheRead: 0.175, cacheWrite: 0 }),
|
|
157
|
+
'claude-opus-4-6': Object.freeze({ input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75, reasoningOutput: 75 }),
|
|
158
|
+
'claude-opus-4-7': Object.freeze({ input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75, reasoningOutput: 75 }),
|
|
159
|
+
'claude-sonnet-4-6': Object.freeze({ input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75, reasoningOutput: 15 }),
|
|
160
|
+
'claude-haiku-4-5': Object.freeze({ input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1, reasoningOutput: 4 }),
|
|
161
|
+
'claude-3-5-sonnet': Object.freeze({ input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75, reasoningOutput: 15 }),
|
|
162
|
+
'claude-3-5-haiku': Object.freeze({ input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1, reasoningOutput: 4 }),
|
|
163
|
+
'claude-3-opus': Object.freeze({ input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75, reasoningOutput: 75 })
|
|
157
164
|
});
|
|
158
165
|
|
|
159
166
|
function createUsagePricingEntry(pricing, source) {
|
|
@@ -234,22 +241,17 @@ function resolveUsagePricingForSession(session, pricingIndex, fallbackProvider =
|
|
|
234
241
|
if (knownPricing) {
|
|
235
242
|
return knownPricing;
|
|
236
243
|
}
|
|
244
|
+
const strippedModel = model.replace(/-\d{8}(?=[-_]|$)/, '');
|
|
245
|
+
if (strippedModel !== model) {
|
|
246
|
+
const strippedKnown = pricingIndex.knownByModel instanceof Map ? pricingIndex.knownByModel.get(strippedModel) : null;
|
|
247
|
+
if (strippedKnown) {
|
|
248
|
+
return strippedKnown;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
237
251
|
return null;
|
|
238
252
|
}
|
|
239
253
|
|
|
240
|
-
function shouldEstimateUsageCostForSession(
|
|
241
|
-
if (!session || typeof session !== 'object') {
|
|
242
|
-
return false;
|
|
243
|
-
}
|
|
244
|
-
const source = typeof session.source === 'string' ? session.source.trim().toLowerCase() : '';
|
|
245
|
-
const provider = typeof session.provider === 'string' ? session.provider.trim().toLowerCase() : '';
|
|
246
|
-
const model = typeof session.model === 'string' ? session.model.trim().toLowerCase() : '';
|
|
247
|
-
if (source === 'claude' || provider === 'claude') {
|
|
248
|
-
return false;
|
|
249
|
-
}
|
|
250
|
-
if (/^claude(?:[-_]|$)/.test(model)) {
|
|
251
|
-
return false;
|
|
252
|
-
}
|
|
254
|
+
function shouldEstimateUsageCostForSession() {
|
|
253
255
|
return true;
|
|
254
256
|
}
|
|
255
257
|
|
|
@@ -313,10 +315,11 @@ function estimateUsageCostSummary(sessions, providersList, currentProvider) {
|
|
|
313
315
|
function estimateUsageCostForSession(session, pricingIndex, currentProvider) {
|
|
314
316
|
const inputTokens = Number.isFinite(Number(session.inputTokens)) ? Math.max(0, Math.floor(Number(session.inputTokens))) : null;
|
|
315
317
|
const cachedInputTokens = Number.isFinite(Number(session.cachedInputTokens)) ? Math.max(0, Math.floor(Number(session.cachedInputTokens))) : 0;
|
|
318
|
+
const cacheCreationInputTokens = Number.isFinite(Number(session.cacheCreationInputTokens)) ? Math.max(0, Math.floor(Number(session.cacheCreationInputTokens))) : 0;
|
|
316
319
|
const outputTokens = Number.isFinite(Number(session.outputTokens)) ? Math.max(0, Math.floor(Number(session.outputTokens))) : null;
|
|
317
320
|
const reasoningOutputTokens = Number.isFinite(Number(session.reasoningOutputTokens)) ? Math.max(0, Math.floor(Number(session.reasoningOutputTokens))) : 0;
|
|
318
|
-
const billableInputTokens = Math.max(0, (inputTokens || 0) - cachedInputTokens);
|
|
319
|
-
const fallbackSessionTokens = billableInputTokens + cachedInputTokens + (outputTokens || 0) + reasoningOutputTokens;
|
|
321
|
+
const billableInputTokens = Math.max(0, (inputTokens || 0) - cachedInputTokens - cacheCreationInputTokens);
|
|
322
|
+
const fallbackSessionTokens = billableInputTokens + cachedInputTokens + cacheCreationInputTokens + (outputTokens || 0) + reasoningOutputTokens;
|
|
320
323
|
const totalSessionTokens = Number.isFinite(Number(session.totalTokens))
|
|
321
324
|
? Math.max(0, Math.floor(Number(session.totalTokens)))
|
|
322
325
|
: fallbackSessionTokens;
|
|
@@ -325,12 +328,14 @@ function estimateUsageCostForSession(session, pricingIndex, currentProvider) {
|
|
|
325
328
|
const reasoningRate = pricing
|
|
326
329
|
? ((pricing.reasoningOutput != null ? pricing.reasoningOutput : pricing.output) || 0)
|
|
327
330
|
: 0;
|
|
331
|
+
const visibleOutputTokens = Math.max(0, (outputTokens || 0) - reasoningOutputTokens);
|
|
328
332
|
const estimatedUsd = pricing && hasTokenBreakdown
|
|
329
333
|
? (
|
|
330
334
|
((pricing.input || 0) * billableInputTokens)
|
|
331
335
|
+ ((pricing.cacheRead || 0) * cachedInputTokens)
|
|
336
|
+
+ ((pricing.cacheWrite || 0) * cacheCreationInputTokens)
|
|
332
337
|
+ (reasoningRate * reasoningOutputTokens)
|
|
333
|
-
+ ((pricing.output || 0) *
|
|
338
|
+
+ ((pricing.output || 0) * visibleOutputTokens)
|
|
334
339
|
) / 1000000
|
|
335
340
|
: 0;
|
|
336
341
|
return {
|
|
@@ -43,6 +43,16 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
45
|
|
|
46
|
+
openCloneClaudeConfigModal(name, config) {
|
|
47
|
+
this.newClaudeConfig = {
|
|
48
|
+
name: '',
|
|
49
|
+
apiKey: config.apiKey || '',
|
|
50
|
+
baseUrl: config.baseUrl || '',
|
|
51
|
+
model: config.model || ''
|
|
52
|
+
};
|
|
53
|
+
this.showClaudeConfigModal = true;
|
|
54
|
+
},
|
|
55
|
+
|
|
46
56
|
openEditConfigModal(name) {
|
|
47
57
|
const config = this.claudeConfigs[name];
|
|
48
58
|
this.editingConfig = {
|
|
@@ -182,8 +192,8 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
182
192
|
this.newClaudeConfig = {
|
|
183
193
|
name: '',
|
|
184
194
|
apiKey: '',
|
|
185
|
-
baseUrl: '
|
|
186
|
-
model: '
|
|
195
|
+
baseUrl: '',
|
|
196
|
+
model: ''
|
|
187
197
|
};
|
|
188
198
|
}
|
|
189
199
|
};
|
|
@@ -236,6 +236,10 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
238
|
}
|
|
239
|
+
|
|
240
|
+
if (typeof this.loadLocalBridgeExcluded === 'function') {
|
|
241
|
+
this.loadLocalBridgeExcluded();
|
|
242
|
+
}
|
|
239
243
|
},
|
|
240
244
|
|
|
241
245
|
async switchProvider(name) {
|
|
@@ -557,6 +561,27 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
557
561
|
this.healthCheckBatchTotal = this.healthCheckBatchTotal || 0;
|
|
558
562
|
this.healthCheckBatchDone = Math.min(this.healthCheckBatchDone || 0, this.healthCheckBatchTotal || 0);
|
|
559
563
|
this.healthCheckLoading = false;
|
|
564
|
+
if (typeof this.runProvidersHealthCheck === 'function' && this.configMode === 'codex') {
|
|
565
|
+
void this.runProvidersHealthCheck({ remote: true });
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
},
|
|
569
|
+
|
|
570
|
+
async runProvidersHealthCheck(options = {}) {
|
|
571
|
+
this.providersHealthLoading = true;
|
|
572
|
+
try {
|
|
573
|
+
const res = await api('providers-health', {
|
|
574
|
+
remote: options.remote !== false
|
|
575
|
+
});
|
|
576
|
+
if (res && typeof res === 'object' && !res.error) {
|
|
577
|
+
this.providersHealthResult = res;
|
|
578
|
+
} else {
|
|
579
|
+
this.providersHealthResult = null;
|
|
580
|
+
}
|
|
581
|
+
} catch (e) {
|
|
582
|
+
this.providersHealthResult = null;
|
|
583
|
+
} finally {
|
|
584
|
+
this.providersHealthLoading = false;
|
|
560
585
|
}
|
|
561
586
|
},
|
|
562
587
|
|
|
@@ -29,6 +29,7 @@ import { createStartupClaudeMethods } from './app.methods.startup-claude.mjs';
|
|
|
29
29
|
import { createSkillsMethods } from './skills.methods.mjs';
|
|
30
30
|
import { createPluginsMethods } from './plugins.methods.mjs';
|
|
31
31
|
import { createI18nMethods } from './i18n.mjs';
|
|
32
|
+
import { createWebhookMethods } from './app.methods.webhook.mjs';
|
|
32
33
|
import {
|
|
33
34
|
CONFIG_MODE_SET,
|
|
34
35
|
getProviderConfigModeMeta
|
|
@@ -43,6 +44,7 @@ import {
|
|
|
43
44
|
export function createAppMethods() {
|
|
44
45
|
return {
|
|
45
46
|
...createI18nMethods(),
|
|
47
|
+
...createWebhookMethods(),
|
|
46
48
|
...createStartupClaudeMethods({
|
|
47
49
|
api,
|
|
48
50
|
defaultModelContextWindow: DEFAULT_MODEL_CONTEXT_WINDOW,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export function createNavigationMethods(options = {}) {
|
|
1
|
+
export function createNavigationMethods(options = {}) {
|
|
2
2
|
const {
|
|
3
3
|
configModeSet,
|
|
4
4
|
switchMainTabHelper,
|
|
@@ -14,7 +14,8 @@ export function createNavigationMethods(options = {}) {
|
|
|
14
14
|
'market',
|
|
15
15
|
'plugins',
|
|
16
16
|
'docs',
|
|
17
|
-
'settings'
|
|
17
|
+
'settings',
|
|
18
|
+
'trash'
|
|
18
19
|
]);
|
|
19
20
|
const loadDoctorOverview = async (vm, options = {}) => {
|
|
20
21
|
if (!vm || typeof vm !== 'object') return false;
|
|
@@ -67,9 +68,15 @@ export function createNavigationMethods(options = {}) {
|
|
|
67
68
|
: vm.configMode;
|
|
68
69
|
const mainTab = typeof mainTabSource === 'string' ? mainTabSource.trim().toLowerCase() : '';
|
|
69
70
|
const configMode = typeof configModeSource === 'string' ? configModeSource.trim().toLowerCase() : '';
|
|
71
|
+
const settingsTab = typeof vm.settingsTab === 'string' ? vm.settingsTab.trim().toLowerCase() : 'general';
|
|
72
|
+
const skillsTargetApp = typeof vm.skillsTargetApp === 'string' && (vm.skillsTargetApp === 'codex' || vm.skillsTargetApp === 'claude') ? vm.skillsTargetApp : 'codex';
|
|
73
|
+
const promptTemplatesMode = typeof vm.promptTemplatesMode === 'string' && (vm.promptTemplatesMode === 'compose' || vm.promptTemplatesMode === 'manage') ? vm.promptTemplatesMode : 'compose';
|
|
70
74
|
const snapshot = {
|
|
75
|
+
settingsTab: settingsTab === 'data' ? 'data' : 'general',
|
|
71
76
|
mainTab: MAIN_TAB_SET.has(mainTab) ? mainTab : 'dashboard',
|
|
72
|
-
configMode: configModeSet && configModeSet.has(configMode) ? configMode : 'codex'
|
|
77
|
+
configMode: configModeSet && configModeSet.has(configMode) ? configMode : 'codex',
|
|
78
|
+
skillsTargetApp,
|
|
79
|
+
promptTemplatesMode
|
|
73
80
|
};
|
|
74
81
|
try {
|
|
75
82
|
localStorage.setItem(NAV_STATE_STORAGE_KEY, JSON.stringify(snapshot));
|
|
@@ -77,6 +84,9 @@ export function createNavigationMethods(options = {}) {
|
|
|
77
84
|
};
|
|
78
85
|
|
|
79
86
|
return {
|
|
87
|
+
saveNavState() {
|
|
88
|
+
persistNavState(this);
|
|
89
|
+
},
|
|
80
90
|
restoreNavStateFromStorage() {
|
|
81
91
|
if (this.__navStateRestoring) return false;
|
|
82
92
|
const restored = readNavState();
|
|
@@ -89,7 +99,17 @@ export function createNavigationMethods(options = {}) {
|
|
|
89
99
|
: '';
|
|
90
100
|
const shouldUpdateConfigMode = !!(nextConfigMode && configModeSet && configModeSet.has(nextConfigMode));
|
|
91
101
|
const shouldUpdateMainTab = !!(nextMainTab && MAIN_TAB_SET.has(nextMainTab) && nextMainTab !== this.mainTab);
|
|
92
|
-
|
|
102
|
+
const nextSettingsTab = restored && typeof restored.settingsTab === 'string'
|
|
103
|
+
? restored.settingsTab.trim().toLowerCase()
|
|
104
|
+
: '';
|
|
105
|
+
const shouldUpdateSettingsTab = !!(nextSettingsTab && (nextSettingsTab === 'general' || nextSettingsTab === 'data') && nextSettingsTab !== this.settingsTab);
|
|
106
|
+
const nextSkillsTargetApp = restored && typeof restored.skillsTargetApp === 'string' && (restored.skillsTargetApp === 'codex' || restored.skillsTargetApp === 'claude')
|
|
107
|
+
? restored.skillsTargetApp : '';
|
|
108
|
+
const shouldUpdateSkillsTargetApp = !!(nextSkillsTargetApp && nextSkillsTargetApp !== this.skillsTargetApp);
|
|
109
|
+
const nextPromptTemplatesMode = restored && typeof restored.promptTemplatesMode === 'string' && (restored.promptTemplatesMode === 'compose' || restored.promptTemplatesMode === 'manage')
|
|
110
|
+
? restored.promptTemplatesMode : '';
|
|
111
|
+
const shouldUpdatePromptTemplatesMode = !!(nextPromptTemplatesMode && nextPromptTemplatesMode !== this.promptTemplatesMode);
|
|
112
|
+
if (!shouldUpdateConfigMode && !shouldUpdateMainTab && !shouldUpdateSettingsTab && !shouldUpdateSkillsTargetApp && !shouldUpdatePromptTemplatesMode) {
|
|
93
113
|
return false;
|
|
94
114
|
}
|
|
95
115
|
this.__navStateRestoring = true;
|
|
@@ -97,9 +117,18 @@ export function createNavigationMethods(options = {}) {
|
|
|
97
117
|
if (shouldUpdateConfigMode) {
|
|
98
118
|
this.configMode = nextConfigMode;
|
|
99
119
|
}
|
|
120
|
+
if (shouldUpdateSettingsTab) {
|
|
121
|
+
this.settingsTab = nextSettingsTab;
|
|
122
|
+
}
|
|
100
123
|
if (shouldUpdateMainTab) {
|
|
101
124
|
this.switchMainTab(nextMainTab);
|
|
102
125
|
}
|
|
126
|
+
if (shouldUpdateSkillsTargetApp) {
|
|
127
|
+
this.skillsTargetApp = nextSkillsTargetApp;
|
|
128
|
+
}
|
|
129
|
+
if (shouldUpdatePromptTemplatesMode) {
|
|
130
|
+
this.promptTemplatesMode = nextPromptTemplatesMode;
|
|
131
|
+
}
|
|
103
132
|
} finally {
|
|
104
133
|
this.__navStateRestoring = false;
|
|
105
134
|
}
|
|
@@ -411,6 +440,11 @@ export function createNavigationMethods(options = {}) {
|
|
|
411
440
|
switchState.ticket += 1;
|
|
412
441
|
switchState.pendingTarget = '';
|
|
413
442
|
if (targetTab === 'dashboard' && !this.__doctorLoadedOnce) {
|
|
443
|
+
if (targetTab === 'trash' && !this.sessionTrashLoadedOnce) {
|
|
444
|
+
if (typeof this.loadSessionTrash === 'function') {
|
|
445
|
+
void this.loadSessionTrash({ forceRefresh: false });
|
|
446
|
+
}
|
|
447
|
+
}
|
|
414
448
|
void loadDoctorOverview(this);
|
|
415
449
|
}
|
|
416
450
|
if (
|
|
@@ -656,10 +690,7 @@ export function createNavigationMethods(options = {}) {
|
|
|
656
690
|
? this.activeSessionMessages.length
|
|
657
691
|
: 0;
|
|
658
692
|
if (total <= 0) return;
|
|
659
|
-
|
|
660
|
-
? Math.max(1, Math.floor(this.sessionPreviewInitialBatchSize))
|
|
661
|
-
: 40;
|
|
662
|
-
this.sessionPreviewVisibleCount = Math.min(baseSize, total);
|
|
693
|
+
this.sessionPreviewVisibleCount = total;
|
|
663
694
|
this.invalidateSessionTimelineMeasurementCache();
|
|
664
695
|
},
|
|
665
696
|
|