codexmate 0.0.22 → 0.0.23
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 +1 -0
- package/README.zh.md +4 -3
- package/package.json +1 -1
- package/web-ui/app.js +54 -3
- package/web-ui/modules/app.computed.main-tabs.mjs +6 -2
- package/web-ui/modules/app.methods.install.mjs +16 -0
- package/web-ui/modules/app.methods.navigation.mjs +43 -0
- package/web-ui/modules/app.methods.session-browser.mjs +66 -1
- package/web-ui/modules/app.methods.startup-claude.mjs +12 -0
- package/web-ui/modules/app.methods.task-orchestration.mjs +96 -11
- package/web-ui/modules/i18n.mjs +214 -0
- package/web-ui/modules/plugins.computed.mjs +2 -219
- package/web-ui/modules/plugins.methods.mjs +2 -619
- package/web-ui/modules/plugins.storage.mjs +11 -37
- package/web-ui/modules/sessions-filters-url.mjs +85 -0
- package/web-ui/partials/index/layout-header.html +2 -2
- package/web-ui/partials/index/panel-config-claude.html +21 -0
- package/web-ui/partials/index/panel-config-codex.html +21 -0
- package/web-ui/partials/index/panel-orchestration.html +102 -111
- package/web-ui/partials/index/panel-plugins.html +34 -4
- package/web-ui/partials/index/panel-sessions.html +9 -3
- package/web-ui/partials/index/panel-usage.html +5 -5
- package/web-ui/styles/controls-forms.css +7 -0
- package/web-ui/styles/layout-shell.css +9 -3
- package/web-ui/styles/navigation-panels.css +8 -0
- package/web-ui/styles/plugins-panel.css +5 -0
- package/web-ui/styles/sessions-usage.css +28 -0
- package/web-ui/styles/skills-market.css +12 -2
- package/web-ui/styles/task-orchestration.css +57 -11
package/README.md
CHANGED
|
@@ -31,6 +31,7 @@ Codex Mate is a local-first CLI + Web UI for unified management of:
|
|
|
31
31
|
- Local skills market for Codex / Claude Code (target switching, local skills management, cross-app import, ZIP distribution)
|
|
32
32
|
- Local Codex/Claude sessions (list/filter/export/delete) with Usage analytics overview
|
|
33
33
|
- Plugins (Prompt templates): reusable templates with variables and one-click copy
|
|
34
|
+
- Task orchestration: plan/queue/run/review local tasks
|
|
34
35
|
|
|
35
36
|
It works on local files directly and does not require cloud hosting. The skills market is also local-first: it operates on local directories and does not depend on a remote marketplace.
|
|
36
37
|
|
package/README.zh.md
CHANGED
|
@@ -31,7 +31,7 @@ Codex Mate 提供一套本地优先的 CLI + Web UI,用于统一管理:
|
|
|
31
31
|
- Codex / Claude Code Skills 市场(安装目标切换、本地 skills 管理、跨应用导入、ZIP 分发)
|
|
32
32
|
- Codex / Claude 本地会话浏览、筛选、导出、删除与 Usage 统计概览
|
|
33
33
|
- 插件(提示词模板):模板复用、变量填写、一键复制
|
|
34
|
-
-
|
|
34
|
+
- 任务编排:规划 / 排队 / 执行 / 回看
|
|
35
35
|
|
|
36
36
|
项目不依赖云端托管,配置写入你的本地文件,便于审计和回滚。Skills 市场同样坚持本地优先,只操作本地目录,不依赖远程在线市场。
|
|
37
37
|
|
|
@@ -75,8 +75,9 @@ Codex Mate 提供一套本地优先的 CLI + Web UI,用于统一管理:
|
|
|
75
75
|
- 提示词模板:本地保存/编辑/复用(支持变量)
|
|
76
76
|
- 编写 → 填参 → 一键复制的工作流(模板数据保存在浏览器存储)
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
-
|
|
78
|
+
**任务编排**
|
|
79
|
+
- DAG 节点拆分与波次并发
|
|
80
|
+
- 支持计划预览、执行、队列与运行详情
|
|
80
81
|
|
|
81
82
|
**工程能力**
|
|
82
83
|
- MCP stdio 能力(tools/resources/prompts)
|
package/package.json
CHANGED
package/web-ui/app.js
CHANGED
|
@@ -69,6 +69,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
69
69
|
// Plugins
|
|
70
70
|
pluginsActiveId: 'prompt-templates',
|
|
71
71
|
pluginsLoading: false,
|
|
72
|
+
pluginsError: '',
|
|
72
73
|
promptTemplatesListRaw: [],
|
|
73
74
|
promptTemplatesLoadedOnce: false,
|
|
74
75
|
promptTemplatesKeyword: '',
|
|
@@ -161,6 +162,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
161
162
|
sessionsUsageTimeRange: '7d',
|
|
162
163
|
sessionsUsageList: [],
|
|
163
164
|
sessionsUsageLoadedOnce: false,
|
|
165
|
+
sessionsUsageLoadedLimit: 0,
|
|
164
166
|
sessionsUsageLoading: false,
|
|
165
167
|
sessionsUsageError: '',
|
|
166
168
|
sessionsList: [],
|
|
@@ -382,7 +384,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
382
384
|
codexImportLoading: false,
|
|
383
385
|
codexAuthProfiles: [],
|
|
384
386
|
forceCompactLayout: false,
|
|
385
|
-
taskOrchestrationTabEnabled:
|
|
387
|
+
taskOrchestrationTabEnabled: true,
|
|
386
388
|
taskOrchestration: {
|
|
387
389
|
loading: false,
|
|
388
390
|
planning: false,
|
|
@@ -396,11 +398,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
396
398
|
followUpsText: '',
|
|
397
399
|
workflowIdsText: '',
|
|
398
400
|
selectedEngine: 'codex',
|
|
399
|
-
|
|
400
|
-
dryRun: false,
|
|
401
|
+
runMode: 'write',
|
|
401
402
|
concurrency: 2,
|
|
402
403
|
autoFixRounds: 1,
|
|
403
404
|
plan: null,
|
|
405
|
+
planFingerprint: '',
|
|
404
406
|
planIssues: [],
|
|
405
407
|
planWarnings: [],
|
|
406
408
|
overviewWarnings: [],
|
|
@@ -430,6 +432,55 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
430
432
|
this.agentsModalTitle = this.t('modal.agents.title');
|
|
431
433
|
this.agentsModalHint = this.t('modal.agents.hint');
|
|
432
434
|
}
|
|
435
|
+
{
|
|
436
|
+
const NAV_STATE_STORAGE_KEY = 'codexmateNavState.v1';
|
|
437
|
+
const mainTabSet = new Set(['config', 'sessions', 'usage', 'orchestration', 'market', 'plugins', 'docs', 'settings']);
|
|
438
|
+
let restored = null;
|
|
439
|
+
try {
|
|
440
|
+
const raw = localStorage.getItem(NAV_STATE_STORAGE_KEY) || '';
|
|
441
|
+
restored = raw ? JSON.parse(raw) : null;
|
|
442
|
+
} catch (_) {
|
|
443
|
+
restored = null;
|
|
444
|
+
}
|
|
445
|
+
const nextMainTab = restored && typeof restored.mainTab === 'string'
|
|
446
|
+
? restored.mainTab.trim().toLowerCase()
|
|
447
|
+
: '';
|
|
448
|
+
const nextConfigMode = restored && typeof restored.configMode === 'string'
|
|
449
|
+
? restored.configMode.trim().toLowerCase()
|
|
450
|
+
: '';
|
|
451
|
+
let urlMainTab = '';
|
|
452
|
+
try {
|
|
453
|
+
const url = new URL(window.location.href);
|
|
454
|
+
if (url.pathname !== '/session') {
|
|
455
|
+
urlMainTab = String(url.searchParams.get('tab') || '').trim().toLowerCase();
|
|
456
|
+
}
|
|
457
|
+
} catch (_) {
|
|
458
|
+
urlMainTab = '';
|
|
459
|
+
}
|
|
460
|
+
const resolvedMainTab = urlMainTab && mainTabSet.has(urlMainTab)
|
|
461
|
+
? urlMainTab
|
|
462
|
+
: nextMainTab;
|
|
463
|
+
if (nextConfigMode && typeof this.switchConfigMode === 'function') {
|
|
464
|
+
this.__navStateRestoring = true;
|
|
465
|
+
try {
|
|
466
|
+
if (nextConfigMode === 'codex' || nextConfigMode === 'claude' || nextConfigMode === 'openclaw') {
|
|
467
|
+
this.configMode = nextConfigMode;
|
|
468
|
+
}
|
|
469
|
+
if (resolvedMainTab && mainTabSet.has(resolvedMainTab) && resolvedMainTab !== this.mainTab) {
|
|
470
|
+
this.switchMainTab(resolvedMainTab);
|
|
471
|
+
}
|
|
472
|
+
} finally {
|
|
473
|
+
this.__navStateRestoring = false;
|
|
474
|
+
}
|
|
475
|
+
} else if (resolvedMainTab && mainTabSet.has(resolvedMainTab) && resolvedMainTab !== this.mainTab) {
|
|
476
|
+
this.__navStateRestoring = true;
|
|
477
|
+
try {
|
|
478
|
+
this.switchMainTab(resolvedMainTab);
|
|
479
|
+
} finally {
|
|
480
|
+
this.__navStateRestoring = false;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
433
484
|
this.initSessionStandalone();
|
|
434
485
|
this.updateCompactLayoutMode();
|
|
435
486
|
if (!this.taskOrchestrationTabEnabled && this.mainTab === 'orchestration') {
|
|
@@ -13,12 +13,16 @@ function readTaskOrchestrationDraftMetrics(taskOrchestration) {
|
|
|
13
13
|
const workflowIds = normalizeTaskDraftLines(state.workflowIdsText);
|
|
14
14
|
const followUps = normalizeTaskDraftLines(state.followUpsText);
|
|
15
15
|
const engine = String(state.selectedEngine || 'codex').trim().toLowerCase() === 'workflow' ? 'workflow' : 'codex';
|
|
16
|
+
const runMode = String(state.runMode || 'write').trim().toLowerCase();
|
|
17
|
+
const allowWrite = runMode === 'write';
|
|
18
|
+
const dryRun = runMode === 'dry-run';
|
|
16
19
|
const plan = state.plan && typeof state.plan === 'object' ? state.plan : null;
|
|
17
20
|
const planNodes = Array.isArray(plan && plan.nodes) ? plan.nodes : [];
|
|
18
21
|
const planIssues = Array.isArray(state.planIssues) ? state.planIssues : [];
|
|
19
22
|
const planWarnings = Array.isArray(state.planWarnings) ? state.planWarnings : [];
|
|
20
23
|
return {
|
|
21
24
|
engine,
|
|
25
|
+
runMode,
|
|
22
26
|
title,
|
|
23
27
|
target,
|
|
24
28
|
notes,
|
|
@@ -34,8 +38,8 @@ function readTaskOrchestrationDraftMetrics(taskOrchestration) {
|
|
|
34
38
|
workflowCount: workflowIds.length,
|
|
35
39
|
followUpCount: followUps.length,
|
|
36
40
|
planNodeCount: planNodes.length,
|
|
37
|
-
allowWrite
|
|
38
|
-
dryRun
|
|
41
|
+
allowWrite,
|
|
42
|
+
dryRun
|
|
39
43
|
};
|
|
40
44
|
}
|
|
41
45
|
|
|
@@ -156,6 +156,22 @@ export function createInstallMethods() {
|
|
|
156
156
|
|
|
157
157
|
setInstallRegistryPreset(presetName) {
|
|
158
158
|
this.installRegistryPreset = this.normalizeInstallRegistryPreset(presetName);
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
getInstallStatusTarget(targetId) {
|
|
162
|
+
const key = typeof targetId === 'string' ? targetId.trim() : '';
|
|
163
|
+
if (!key) return null;
|
|
164
|
+
const list = Array.isArray(this.installStatusTargets) ? this.installStatusTargets : [];
|
|
165
|
+
return list.find((item) => item && item.id === key) || null;
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
isInstallTargetInstalled(targetId) {
|
|
169
|
+
const target = this.getInstallStatusTarget(targetId);
|
|
170
|
+
return !!(target && target.installed === true);
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
shouldShowCliInstallPlaceholder(targetId) {
|
|
174
|
+
return Array.isArray(this.installStatusTargets) && !this.isInstallTargetInstalled(targetId);
|
|
159
175
|
}
|
|
160
176
|
};
|
|
161
177
|
}
|
|
@@ -4,6 +4,46 @@ export function createNavigationMethods(options = {}) {
|
|
|
4
4
|
switchMainTabHelper,
|
|
5
5
|
loadMoreSessionMessagesHelper
|
|
6
6
|
} = options;
|
|
7
|
+
const NAV_STATE_STORAGE_KEY = 'codexmateNavState.v1';
|
|
8
|
+
const MAIN_TAB_SET = new Set([
|
|
9
|
+
'config',
|
|
10
|
+
'sessions',
|
|
11
|
+
'usage',
|
|
12
|
+
'orchestration',
|
|
13
|
+
'market',
|
|
14
|
+
'plugins',
|
|
15
|
+
'docs',
|
|
16
|
+
'settings'
|
|
17
|
+
]);
|
|
18
|
+
const readNavState = () => {
|
|
19
|
+
if (typeof localStorage === 'undefined') return null;
|
|
20
|
+
let raw = '';
|
|
21
|
+
try {
|
|
22
|
+
raw = localStorage.getItem(NAV_STATE_STORAGE_KEY) || '';
|
|
23
|
+
} catch (_) {
|
|
24
|
+
raw = '';
|
|
25
|
+
}
|
|
26
|
+
if (!raw) return null;
|
|
27
|
+
try {
|
|
28
|
+
const parsed = JSON.parse(raw);
|
|
29
|
+
return parsed && typeof parsed === 'object' ? parsed : null;
|
|
30
|
+
} catch (_) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const persistNavState = (vm) => {
|
|
35
|
+
if (!vm || vm.__navStateRestoring) return;
|
|
36
|
+
if (typeof localStorage === 'undefined') return;
|
|
37
|
+
const mainTab = typeof vm.mainTab === 'string' ? vm.mainTab.trim().toLowerCase() : '';
|
|
38
|
+
const configMode = typeof vm.configMode === 'string' ? vm.configMode.trim().toLowerCase() : '';
|
|
39
|
+
const snapshot = {
|
|
40
|
+
mainTab: MAIN_TAB_SET.has(mainTab) ? mainTab : 'docs',
|
|
41
|
+
configMode: configModeSet && configModeSet.has(configMode) ? configMode : 'codex'
|
|
42
|
+
};
|
|
43
|
+
try {
|
|
44
|
+
localStorage.setItem(NAV_STATE_STORAGE_KEY, JSON.stringify(snapshot));
|
|
45
|
+
} catch (_) {}
|
|
46
|
+
};
|
|
7
47
|
|
|
8
48
|
return {
|
|
9
49
|
switchConfigMode(mode) {
|
|
@@ -34,6 +74,7 @@ export function createNavigationMethods(options = {}) {
|
|
|
34
74
|
this.scheduleAfterFrame(() => {
|
|
35
75
|
this.clearMainTabSwitchIntent('config');
|
|
36
76
|
});
|
|
77
|
+
persistNavState(this);
|
|
37
78
|
return;
|
|
38
79
|
}
|
|
39
80
|
this.switchMainTab('config');
|
|
@@ -324,6 +365,7 @@ export function createNavigationMethods(options = {}) {
|
|
|
324
365
|
switchState.ticket += 1;
|
|
325
366
|
switchState.pendingTarget = '';
|
|
326
367
|
const result = switchMainTabHelper.call(this, targetTab);
|
|
368
|
+
persistNavState(this);
|
|
327
369
|
this.scheduleAfterFrame(() => {
|
|
328
370
|
this.clearMainTabSwitchIntent(normalizedTab);
|
|
329
371
|
});
|
|
@@ -338,6 +380,7 @@ export function createNavigationMethods(options = {}) {
|
|
|
338
380
|
const pendingTarget = liveState.pendingTarget || targetTab;
|
|
339
381
|
liveState.pendingTarget = '';
|
|
340
382
|
switchMainTabHelper.call(this, pendingTarget);
|
|
383
|
+
persistNavState(this);
|
|
341
384
|
this.clearMainTabSwitchIntent(normalizedTab);
|
|
342
385
|
});
|
|
343
386
|
},
|
|
@@ -4,6 +4,14 @@ import {
|
|
|
4
4
|
normalizeSessionMessageRole,
|
|
5
5
|
normalizeSessionPathFilter
|
|
6
6
|
} from '../logic.mjs';
|
|
7
|
+
import {
|
|
8
|
+
applySessionsFilterUrlState,
|
|
9
|
+
buildSessionsFilterShareUrl,
|
|
10
|
+
normalizeSessionRoleFilter,
|
|
11
|
+
normalizeSessionTimePreset,
|
|
12
|
+
readSessionsFilterUrlState,
|
|
13
|
+
syncSessionsFilterUrl
|
|
14
|
+
} from './sessions-filters-url.mjs';
|
|
7
15
|
|
|
8
16
|
function isSessionLoadNativeDialogEnabled(vm) {
|
|
9
17
|
if (vm && typeof vm.isSessionLoadNativeDialogEnabled === 'function' && vm.isSessionLoadNativeDialogEnabled !== isSessionLoadNativeDialogEnabled) {
|
|
@@ -241,11 +249,22 @@ export function createSessionBrowserMethods(options = {}) {
|
|
|
241
249
|
},
|
|
242
250
|
|
|
243
251
|
restoreSessionFilterCache() {
|
|
252
|
+
const urlState = readSessionsFilterUrlState();
|
|
253
|
+
if (urlState) {
|
|
254
|
+
applySessionsFilterUrlState(this, urlState);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
244
257
|
const sourceCache = localStorage.getItem('codexmateSessionFilterSource');
|
|
245
258
|
const pathCache = localStorage.getItem('codexmateSessionPathFilter');
|
|
246
259
|
const cached = buildSessionFilterCacheState(sourceCache, pathCache);
|
|
247
260
|
this.sessionFilterSource = cached.source;
|
|
248
261
|
this.sessionPathFilter = cached.pathFilter;
|
|
262
|
+
const queryCache = localStorage.getItem('codexmateSessionQuery');
|
|
263
|
+
const roleCache = localStorage.getItem('codexmateSessionRoleFilter');
|
|
264
|
+
const timeCache = localStorage.getItem('codexmateSessionTimePreset');
|
|
265
|
+
this.sessionQuery = typeof queryCache === 'string' ? queryCache : '';
|
|
266
|
+
this.sessionRoleFilter = normalizeSessionRoleFilter(roleCache);
|
|
267
|
+
this.sessionTimePreset = normalizeSessionTimePreset(timeCache);
|
|
249
268
|
this.refreshSessionPathOptions(this.sessionFilterSource);
|
|
250
269
|
},
|
|
251
270
|
|
|
@@ -257,6 +276,13 @@ export function createSessionBrowserMethods(options = {}) {
|
|
|
257
276
|
} else {
|
|
258
277
|
localStorage.removeItem('codexmateSessionPathFilter');
|
|
259
278
|
}
|
|
279
|
+
if (this.sessionQuery && isSessionQueryEnabled(this.sessionFilterSource)) {
|
|
280
|
+
localStorage.setItem('codexmateSessionQuery', this.sessionQuery);
|
|
281
|
+
} else {
|
|
282
|
+
localStorage.removeItem('codexmateSessionQuery');
|
|
283
|
+
}
|
|
284
|
+
localStorage.setItem('codexmateSessionRoleFilter', normalizeSessionRoleFilter(this.sessionRoleFilter));
|
|
285
|
+
localStorage.setItem('codexmateSessionTimePreset', normalizeSessionTimePreset(this.sessionTimePreset));
|
|
260
286
|
},
|
|
261
287
|
|
|
262
288
|
normalizeSessionPinnedMap(raw) {
|
|
@@ -389,15 +415,19 @@ export function createSessionBrowserMethods(options = {}) {
|
|
|
389
415
|
async onSessionSourceChange() {
|
|
390
416
|
this.refreshSessionPathOptions(this.sessionFilterSource);
|
|
391
417
|
this.persistSessionFilterCache();
|
|
418
|
+
syncSessionsFilterUrl(this);
|
|
392
419
|
await this.loadSessions();
|
|
393
420
|
},
|
|
394
421
|
|
|
395
422
|
async onSessionPathFilterChange() {
|
|
396
423
|
this.persistSessionFilterCache();
|
|
424
|
+
syncSessionsFilterUrl(this);
|
|
397
425
|
await this.loadSessions();
|
|
398
426
|
},
|
|
399
427
|
|
|
400
428
|
async onSessionFilterChange() {
|
|
429
|
+
this.persistSessionFilterCache();
|
|
430
|
+
syncSessionsFilterUrl(this);
|
|
401
431
|
await this.loadSessions();
|
|
402
432
|
},
|
|
403
433
|
|
|
@@ -408,9 +438,24 @@ export function createSessionBrowserMethods(options = {}) {
|
|
|
408
438
|
this.sessionRoleFilter = 'all';
|
|
409
439
|
this.sessionTimePreset = 'all';
|
|
410
440
|
this.persistSessionFilterCache();
|
|
441
|
+
syncSessionsFilterUrl(this);
|
|
411
442
|
await this.onSessionSourceChange();
|
|
412
443
|
},
|
|
413
444
|
|
|
445
|
+
copySessionsFilterShareUrl() {
|
|
446
|
+
const url = buildSessionsFilterShareUrl(this);
|
|
447
|
+
if (!url) {
|
|
448
|
+
this.showMessage('无法生成链接', 'error');
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
const ok = typeof this.fallbackCopyText === 'function' ? this.fallbackCopyText(url) : false;
|
|
452
|
+
if (ok) {
|
|
453
|
+
this.showMessage(typeof this.t === 'function' ? this.t('toast.copy.ok') : 'Copied', 'success');
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
this.showMessage(typeof this.t === 'function' ? this.t('toast.copy.fail') : 'Copy failed', 'error');
|
|
457
|
+
},
|
|
458
|
+
|
|
414
459
|
normalizeSessionMessage(message) {
|
|
415
460
|
const fallback = {
|
|
416
461
|
role: 'assistant',
|
|
@@ -464,21 +509,40 @@ export function createSessionBrowserMethods(options = {}) {
|
|
|
464
509
|
|
|
465
510
|
invalidateSessionsUsageData(options = {}) {
|
|
466
511
|
this.sessionsUsageLoadedOnce = false;
|
|
512
|
+
this.sessionsUsageLoadedLimit = 0;
|
|
467
513
|
this.sessionsUsageError = '';
|
|
468
514
|
if (options.preserveList !== true) {
|
|
469
515
|
this.sessionsUsageList = [];
|
|
470
516
|
}
|
|
471
517
|
},
|
|
472
518
|
|
|
519
|
+
setSessionsUsageTimeRange(nextRange) {
|
|
520
|
+
const normalized = typeof nextRange === 'string' ? nextRange.trim().toLowerCase() : '';
|
|
521
|
+
const range = normalized === 'all' ? 'all' : (normalized === '30d' ? '30d' : '7d');
|
|
522
|
+
this.sessionsUsageTimeRange = range;
|
|
523
|
+
void this.loadSessionsUsage({ range });
|
|
524
|
+
},
|
|
525
|
+
|
|
473
526
|
async loadSessionsUsage(options = {}) {
|
|
474
527
|
if (this.sessionsUsageLoading) return;
|
|
528
|
+
const normalizedRange = typeof options.range === 'string'
|
|
529
|
+
? options.range.trim().toLowerCase()
|
|
530
|
+
: (typeof this.sessionsUsageTimeRange === 'string' ? this.sessionsUsageTimeRange.trim().toLowerCase() : '');
|
|
531
|
+
const range = normalizedRange === 'all' ? 'all' : (normalizedRange === '30d' ? '30d' : '7d');
|
|
532
|
+
const defaultLimit = range === 'all' ? 2000 : (range === '30d' ? 1200 : 600);
|
|
533
|
+
const rawLimit = Number(options.limit);
|
|
534
|
+
const limit = Number.isFinite(rawLimit) ? Math.max(1, Math.min(rawLimit, 2000)) : defaultLimit;
|
|
535
|
+
const loadedLimit = Number(this.sessionsUsageLoadedLimit || 0);
|
|
536
|
+
if (this.sessionsUsageLoadedOnce && !options.forceRefresh && loadedLimit >= limit) {
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
475
539
|
this.sessionsUsageLoading = true;
|
|
476
540
|
this.sessionsUsageError = '';
|
|
477
541
|
let loadSucceeded = false;
|
|
478
542
|
try {
|
|
479
543
|
const res = await api('list-sessions-usage', {
|
|
480
544
|
source: 'all',
|
|
481
|
-
limit
|
|
545
|
+
limit,
|
|
482
546
|
forceRefresh: !!options.forceRefresh
|
|
483
547
|
});
|
|
484
548
|
if (res.error) {
|
|
@@ -495,6 +559,7 @@ export function createSessionBrowserMethods(options = {}) {
|
|
|
495
559
|
this.sessionsUsageLoading = false;
|
|
496
560
|
if (loadSucceeded) {
|
|
497
561
|
this.sessionsUsageLoadedOnce = true;
|
|
562
|
+
this.sessionsUsageLoadedLimit = limit;
|
|
498
563
|
}
|
|
499
564
|
}
|
|
500
565
|
},
|
|
@@ -34,6 +34,18 @@ export function createStartupClaudeMethods(options = {}) {
|
|
|
34
34
|
startupOk = true;
|
|
35
35
|
this.currentProvider = statusRes.provider;
|
|
36
36
|
this.currentModel = statusRes.model;
|
|
37
|
+
try {
|
|
38
|
+
const installRes = await api('install-status');
|
|
39
|
+
if (installRes && !installRes.error) {
|
|
40
|
+
const targets = Array.isArray(installRes.targets) ? installRes.targets : null;
|
|
41
|
+
if (targets) {
|
|
42
|
+
this.installStatusTargets = targets;
|
|
43
|
+
}
|
|
44
|
+
if (typeof installRes.packageManager === 'string' && typeof this.normalizeInstallPackageManager === 'function') {
|
|
45
|
+
this.installPackageManager = this.normalizeInstallPackageManager(installRes.packageManager);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
} catch (_) {}
|
|
37
49
|
{
|
|
38
50
|
const tier = typeof statusRes.serviceTier === 'string'
|
|
39
51
|
? statusRes.serviceTier.trim().toLowerCase()
|
|
@@ -12,11 +12,11 @@ function createDefaultTaskOrchestrationState() {
|
|
|
12
12
|
followUpsText: '',
|
|
13
13
|
workflowIdsText: '',
|
|
14
14
|
selectedEngine: 'codex',
|
|
15
|
-
|
|
16
|
-
dryRun: false,
|
|
15
|
+
runMode: 'write',
|
|
17
16
|
concurrency: 2,
|
|
18
17
|
autoFixRounds: 1,
|
|
19
18
|
plan: null,
|
|
19
|
+
planFingerprint: '',
|
|
20
20
|
planIssues: [],
|
|
21
21
|
planWarnings: [],
|
|
22
22
|
overviewWarnings: [],
|
|
@@ -62,6 +62,22 @@ function isActiveStatus(status) {
|
|
|
62
62
|
return normalized === 'running' || normalized === 'queued';
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
function normalizeTaskRunMode(value) {
|
|
66
|
+
const normalized = String(value || '').trim().toLowerCase();
|
|
67
|
+
if (normalized === 'read') return 'read';
|
|
68
|
+
if (normalized === 'dry-run' || normalized === 'dryrun' || normalized === 'plan') return 'dry-run';
|
|
69
|
+
return 'write';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function buildRunModeFlags(runMode) {
|
|
73
|
+
const normalized = normalizeTaskRunMode(runMode);
|
|
74
|
+
return {
|
|
75
|
+
runMode: normalized,
|
|
76
|
+
allowWrite: normalized === 'write',
|
|
77
|
+
dryRun: normalized === 'dry-run'
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
65
81
|
export function createTaskOrchestrationMethods(options = {}) {
|
|
66
82
|
const { api } = options;
|
|
67
83
|
|
|
@@ -75,6 +91,7 @@ export function createTaskOrchestrationMethods(options = {}) {
|
|
|
75
91
|
current[key] = value;
|
|
76
92
|
}
|
|
77
93
|
}
|
|
94
|
+
current.runMode = normalizeTaskRunMode(current.runMode);
|
|
78
95
|
return current;
|
|
79
96
|
}
|
|
80
97
|
this.taskOrchestration = createDefaultTaskOrchestrationState();
|
|
@@ -83,6 +100,7 @@ export function createTaskOrchestrationMethods(options = {}) {
|
|
|
83
100
|
|
|
84
101
|
buildTaskOrchestrationRequest() {
|
|
85
102
|
const state = this.ensureTaskOrchestrationState();
|
|
103
|
+
const flags = buildRunModeFlags(state.runMode);
|
|
86
104
|
return {
|
|
87
105
|
title: String(state.title || '').trim(),
|
|
88
106
|
target: String(state.target || '').trim(),
|
|
@@ -90,13 +108,29 @@ export function createTaskOrchestrationMethods(options = {}) {
|
|
|
90
108
|
followUps: normalizeLines(state.followUpsText),
|
|
91
109
|
workflowIds: normalizeLines(state.workflowIdsText),
|
|
92
110
|
engine: String(state.selectedEngine || 'codex').trim().toLowerCase() === 'workflow' ? 'workflow' : 'codex',
|
|
93
|
-
allowWrite:
|
|
94
|
-
dryRun:
|
|
111
|
+
allowWrite: flags.allowWrite,
|
|
112
|
+
dryRun: flags.dryRun,
|
|
95
113
|
concurrency: normalizePositiveInteger(state.concurrency, 2, 1, 8),
|
|
96
114
|
autoFixRounds: normalizePositiveInteger(state.autoFixRounds, 1, 0, 5)
|
|
97
115
|
};
|
|
98
116
|
},
|
|
99
117
|
|
|
118
|
+
buildTaskOrchestrationFingerprint() {
|
|
119
|
+
const req = this.buildTaskOrchestrationRequest();
|
|
120
|
+
return JSON.stringify({
|
|
121
|
+
title: req.title,
|
|
122
|
+
target: req.target,
|
|
123
|
+
notes: req.notes,
|
|
124
|
+
followUps: req.followUps,
|
|
125
|
+
workflowIds: req.workflowIds,
|
|
126
|
+
engine: req.engine,
|
|
127
|
+
allowWrite: req.allowWrite,
|
|
128
|
+
dryRun: req.dryRun,
|
|
129
|
+
concurrency: req.concurrency,
|
|
130
|
+
autoFixRounds: req.autoFixRounds
|
|
131
|
+
});
|
|
132
|
+
},
|
|
133
|
+
|
|
100
134
|
taskRunStatusTone(status) {
|
|
101
135
|
return normalizeTaskStatusTone(status);
|
|
102
136
|
},
|
|
@@ -190,6 +224,7 @@ export function createTaskOrchestrationMethods(options = {}) {
|
|
|
190
224
|
state.plan = res && res.plan ? res.plan : null;
|
|
191
225
|
state.planIssues = Array.isArray(res && res.issues) ? res.issues : [];
|
|
192
226
|
state.planWarnings = Array.isArray(res && res.warnings) ? res.warnings : [];
|
|
227
|
+
state.planFingerprint = state.plan ? this.buildTaskOrchestrationFingerprint() : '';
|
|
193
228
|
if (res && res.error) {
|
|
194
229
|
if (!options.silent) {
|
|
195
230
|
this.showMessage(res.error, 'error');
|
|
@@ -246,7 +281,7 @@ export function createTaskOrchestrationMethods(options = {}) {
|
|
|
246
281
|
}
|
|
247
282
|
},
|
|
248
283
|
|
|
249
|
-
async addTaskOrchestrationToQueue() {
|
|
284
|
+
async addTaskOrchestrationToQueue(options = {}) {
|
|
250
285
|
const state = this.ensureTaskOrchestrationState();
|
|
251
286
|
if (state.queueAdding) {
|
|
252
287
|
return null;
|
|
@@ -255,21 +290,72 @@ export function createTaskOrchestrationMethods(options = {}) {
|
|
|
255
290
|
try {
|
|
256
291
|
const res = await api('task-queue-add', this.buildTaskOrchestrationRequest());
|
|
257
292
|
if (res && res.error) {
|
|
258
|
-
|
|
293
|
+
if (!options.silent) {
|
|
294
|
+
this.showMessage(res.error, 'error');
|
|
295
|
+
}
|
|
259
296
|
return res;
|
|
260
297
|
}
|
|
261
|
-
|
|
262
|
-
|
|
298
|
+
if (!options.deferRefresh) {
|
|
299
|
+
await this.loadTaskOrchestrationOverview({ silent: true, includeDetail: false });
|
|
300
|
+
}
|
|
301
|
+
if (!options.silent) {
|
|
302
|
+
this.showMessage(`已加入队列: ${res && res.task ? res.task.taskId : ''}`.trim(), 'success');
|
|
303
|
+
}
|
|
263
304
|
return res;
|
|
264
305
|
} catch (error) {
|
|
265
306
|
const message = error && error.message ? error.message : '加入队列失败';
|
|
266
|
-
|
|
307
|
+
if (!options.silent) {
|
|
308
|
+
this.showMessage(message, 'error');
|
|
309
|
+
}
|
|
267
310
|
return { error: message };
|
|
268
311
|
} finally {
|
|
269
312
|
state.queueAdding = false;
|
|
270
313
|
}
|
|
271
314
|
},
|
|
272
315
|
|
|
316
|
+
async planAndRunTaskOrchestration() {
|
|
317
|
+
const state = this.ensureTaskOrchestrationState();
|
|
318
|
+
if (state.running || state.planning) {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
if (!String(state.target || '').trim()) {
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
if (buildRunModeFlags(state.runMode).dryRun) {
|
|
325
|
+
return this.previewTaskPlan({ silent: false });
|
|
326
|
+
}
|
|
327
|
+
const fingerprint = this.buildTaskOrchestrationFingerprint();
|
|
328
|
+
const shouldPreview = !state.plan || state.planFingerprint !== fingerprint;
|
|
329
|
+
if (shouldPreview) {
|
|
330
|
+
const preview = await this.previewTaskPlan({ silent: true });
|
|
331
|
+
if (preview && preview.error) {
|
|
332
|
+
this.showMessage(preview.error, 'error');
|
|
333
|
+
return preview;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (state.planIssues && state.planIssues.length) {
|
|
337
|
+
this.showMessage('计划存在问题,请先修复再执行', 'error');
|
|
338
|
+
return { error: 'Plan has blocking issues' };
|
|
339
|
+
}
|
|
340
|
+
return this.runTaskOrchestration();
|
|
341
|
+
},
|
|
342
|
+
|
|
343
|
+
async queueTaskOrchestrationAndStart() {
|
|
344
|
+
const state = this.ensureTaskOrchestrationState();
|
|
345
|
+
if (state.queueAdding || state.queueStarting || state.planning || state.running) {
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
if (!String(state.target || '').trim()) {
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
const queued = await this.addTaskOrchestrationToQueue({ silent: true, deferRefresh: true });
|
|
352
|
+
if (queued && queued.error) {
|
|
353
|
+
this.showMessage(queued.error, 'error');
|
|
354
|
+
return queued;
|
|
355
|
+
}
|
|
356
|
+
return this.startTaskQueueRunner();
|
|
357
|
+
},
|
|
358
|
+
|
|
273
359
|
async startTaskQueueRunner() {
|
|
274
360
|
const state = this.ensureTaskOrchestrationState();
|
|
275
361
|
if (state.queueStarting) {
|
|
@@ -457,8 +543,7 @@ export function createTaskOrchestrationMethods(options = {}) {
|
|
|
457
543
|
state.followUpsText = '';
|
|
458
544
|
state.workflowIdsText = '';
|
|
459
545
|
state.selectedEngine = 'codex';
|
|
460
|
-
state.
|
|
461
|
-
state.dryRun = false;
|
|
546
|
+
state.runMode = 'write';
|
|
462
547
|
state.concurrency = 2;
|
|
463
548
|
state.autoFixRounds = 1;
|
|
464
549
|
state.plan = null;
|