codexmate 0.1.10 → 0.1.13
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 +5 -7
- package/README.vi.md +4 -7
- package/README.zh.md +5 -7
- 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 +162 -5
- package/package.json +1 -1
- package/web-ui/app.js +95 -4
- package/web-ui/index.html +2 -1
- package/web-ui/modules/app.methods.agents.mjs +90 -0
- package/web-ui/modules/app.methods.index.mjs +2 -0
- package/web-ui/modules/app.methods.navigation.mjs +7 -1
- package/web-ui/modules/app.methods.pi-config.mjs +371 -0
- package/web-ui/modules/app.methods.session-actions.mjs +57 -0
- package/web-ui/modules/app.methods.startup-claude.mjs +2 -1
- package/web-ui/modules/app.methods.system-prompt.mjs +81 -0
- package/web-ui/modules/app.methods.tool-config-permissions.mjs +3 -2
- package/web-ui/modules/app.methods.web-ui-preferences.mjs +8 -1
- package/web-ui/modules/config-mode.computed.mjs +2 -1
- package/web-ui/modules/i18n/locales/en.mjs +52 -0
- package/web-ui/modules/i18n/locales/ja.mjs +52 -0
- package/web-ui/modules/i18n/locales/vi.mjs +52 -0
- package/web-ui/modules/i18n/locales/zh-tw.mjs +52 -0
- package/web-ui/modules/i18n/locales/zh.mjs +52 -0
- package/web-ui/partials/index/layout-header.html +20 -0
- 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-config-pi.html +171 -0
- 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 +42 -0
- package/web-ui/res/web-ui-render.precompiled.js +1101 -252
- package/web-ui/styles/modals-core.css +86 -0
- package/web-ui/styles/sessions-preview.css +38 -0
- package/web-ui/styles/settings-panel.css +7 -0
- package/web-ui/styles/titles-cards.css +2 -2
package/cli.js
CHANGED
|
@@ -108,6 +108,9 @@ const {
|
|
|
108
108
|
const {
|
|
109
109
|
createSystemPromptFileController
|
|
110
110
|
} = require('./cli/system-prompt-files');
|
|
111
|
+
const {
|
|
112
|
+
createPromptHistoryController
|
|
113
|
+
} = require('./cli/prompt-history');
|
|
111
114
|
const {
|
|
112
115
|
createArchiveHelperController
|
|
113
116
|
} = require('./cli/archive-helpers');
|
|
@@ -888,8 +891,8 @@ function isPlainObject(value) {
|
|
|
888
891
|
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
889
892
|
}
|
|
890
893
|
|
|
891
|
-
const TOOL_CONFIG_PERMISSION_TARGETS = new Set(['codex', 'claude', 'opencode', 'kilocode', 'openclaw']);
|
|
892
|
-
const TOOL_CONFIG_PERMISSION_DEFAULTS = Object.freeze({ codex: false, claude: false, opencode: false, kilocode: false, openclaw: false });
|
|
894
|
+
const TOOL_CONFIG_PERMISSION_TARGETS = new Set(['codex', 'claude', 'opencode', 'kilocode', 'openclaw', 'pi']);
|
|
895
|
+
const TOOL_CONFIG_PERMISSION_DEFAULTS = Object.freeze({ codex: false, claude: false, opencode: false, kilocode: false, openclaw: false, pi: false });
|
|
893
896
|
let toolConfigWriteGuardDepth = 0;
|
|
894
897
|
|
|
895
898
|
function enterToolConfigWriteGuard() {
|
|
@@ -918,7 +921,8 @@ function normalizeToolConfigPermissions(value) {
|
|
|
918
921
|
claude: source.claude === true,
|
|
919
922
|
opencode: source.opencode === true,
|
|
920
923
|
kilocode: source.kilocode === true,
|
|
921
|
-
openclaw: source.openclaw === true
|
|
924
|
+
openclaw: source.openclaw === true,
|
|
925
|
+
pi: source.pi === true
|
|
922
926
|
};
|
|
923
927
|
}
|
|
924
928
|
|
|
@@ -976,7 +980,7 @@ function normalizeMainTabPreference(value) {
|
|
|
976
980
|
|
|
977
981
|
function normalizeConfigModePreference(value) {
|
|
978
982
|
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
979
|
-
return ['codex', 'claude', 'openclaw', 'opencode', 'kilocode'].includes(normalized) ? normalized : 'codex';
|
|
983
|
+
return ['codex', 'claude', 'openclaw', 'opencode', 'kilocode', 'pi'].includes(normalized) ? normalized : 'codex';
|
|
980
984
|
}
|
|
981
985
|
|
|
982
986
|
function normalizeUsageTimeRangePreference(value) {
|
|
@@ -1200,9 +1204,123 @@ function getApiToolConfigWriteTarget(action) {
|
|
|
1200
1204
|
if (opencodeWriteActions.has(name)) return 'opencode';
|
|
1201
1205
|
if (kilocodeWriteActions.has(name)) return 'kilocode';
|
|
1202
1206
|
if (openclawWriteActions.has(name)) return 'openclaw';
|
|
1207
|
+
const piWriteActions = new Set([
|
|
1208
|
+
'write-pi-models',
|
|
1209
|
+
'write-pi-settings'
|
|
1210
|
+
]);
|
|
1211
|
+
if (piWriteActions.has(name)) return 'pi';
|
|
1203
1212
|
return '';
|
|
1204
1213
|
}
|
|
1205
1214
|
|
|
1215
|
+
function getPiAgentDir() {
|
|
1216
|
+
const homeDir = (typeof os.homedir === 'function' ? os.homedir() : null) || process.env.HOME || process.env.USERPROFILE || '';
|
|
1217
|
+
return path.join(homeDir, '.pi', 'agent');
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
function readPiModels(params = {}) {
|
|
1221
|
+
try {
|
|
1222
|
+
const dir = getPiAgentDir();
|
|
1223
|
+
const filePath = path.join(dir, 'models.json');
|
|
1224
|
+
if (!fs.existsSync(filePath)) {
|
|
1225
|
+
return { providers: {} };
|
|
1226
|
+
}
|
|
1227
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
1228
|
+
const data = JSON.parse(raw);
|
|
1229
|
+
const providers = (data && typeof data === 'object' && data.providers && typeof data.providers === 'object')
|
|
1230
|
+
? data.providers
|
|
1231
|
+
: {};
|
|
1232
|
+
return { providers };
|
|
1233
|
+
} catch (e) {
|
|
1234
|
+
return { error: e && e.message ? e.message : '读取 Pi models.json 失败' };
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
function writePiModels(params = {}) {
|
|
1239
|
+
const providers = params && typeof params.providers === 'object' ? params.providers : null;
|
|
1240
|
+
if (providers === null) {
|
|
1241
|
+
return { error: 'providers 不能为空' };
|
|
1242
|
+
}
|
|
1243
|
+
try {
|
|
1244
|
+
const dir = getPiAgentDir();
|
|
1245
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1246
|
+
const filePath = path.join(dir, 'models.json');
|
|
1247
|
+
let existing = {};
|
|
1248
|
+
if (fs.existsSync(filePath)) {
|
|
1249
|
+
try {
|
|
1250
|
+
existing = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
1251
|
+
} catch (_) {
|
|
1252
|
+
existing = {};
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
existing.providers = providers;
|
|
1256
|
+
fs.writeFileSync(filePath, JSON.stringify(existing, null, 2), 'utf-8');
|
|
1257
|
+
return { success: true };
|
|
1258
|
+
} catch (e) {
|
|
1259
|
+
return { error: e && e.message ? e.message : '写入 Pi models.json 失败' };
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
async function fetchPiRemoteModels(params = {}) {
|
|
1264
|
+
const baseUrl = typeof params.baseUrl === 'string' ? params.baseUrl.trim() : '';
|
|
1265
|
+
const apiKey = typeof params.apiKey === 'string' ? params.apiKey.trim() : '';
|
|
1266
|
+
if (!baseUrl || !isValidHttpUrl(baseUrl)) {
|
|
1267
|
+
return { error: 'baseUrl 无效', models: [] };
|
|
1268
|
+
}
|
|
1269
|
+
try {
|
|
1270
|
+
const result = await fetchModelsFromBaseUrl(baseUrl, apiKey);
|
|
1271
|
+
const models = result && Array.isArray(result.models)
|
|
1272
|
+
? [...new Set(result.models.filter((id) => typeof id === 'string' && id !== ''))].sort()
|
|
1273
|
+
: [];
|
|
1274
|
+
if (models.length > 0) return { models };
|
|
1275
|
+
return { error: (result && result.error) || '未获取到可用模型', models: [] };
|
|
1276
|
+
} catch (e) {
|
|
1277
|
+
return { error: e && e.message ? e.message : '请求失败', models: [] };
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
function readPiSettings(params = {}) {
|
|
1282
|
+
try {
|
|
1283
|
+
const dir = getPiAgentDir();
|
|
1284
|
+
const filePath = path.join(dir, 'settings.json');
|
|
1285
|
+
if (!fs.existsSync(filePath)) {
|
|
1286
|
+
return { settings: {} };
|
|
1287
|
+
}
|
|
1288
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
1289
|
+
const data = JSON.parse(raw);
|
|
1290
|
+
return { settings: data || {} };
|
|
1291
|
+
} catch (e) {
|
|
1292
|
+
return { error: e && e.message ? e.message : '读取 Pi settings.json 失败' };
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
function writePiSettings(params = {}) {
|
|
1297
|
+
const updates = {};
|
|
1298
|
+
if (typeof (params && params.defaultProvider) === 'string') updates.defaultProvider = params.defaultProvider;
|
|
1299
|
+
if (typeof (params && params.defaultModel) === 'string') updates.defaultModel = params.defaultModel;
|
|
1300
|
+
if (Object.keys(updates).length === 0) {
|
|
1301
|
+
return { error: '缺少可写入的设置项' };
|
|
1302
|
+
}
|
|
1303
|
+
try {
|
|
1304
|
+
const dir = getPiAgentDir();
|
|
1305
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1306
|
+
const filePath = path.join(dir, 'settings.json');
|
|
1307
|
+
let existing = {};
|
|
1308
|
+
if (fs.existsSync(filePath)) {
|
|
1309
|
+
try {
|
|
1310
|
+
existing = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
1311
|
+
} catch (_) {
|
|
1312
|
+
existing = {};
|
|
1313
|
+
}
|
|
1314
|
+
if (!existing || typeof existing !== 'object' || Array.isArray(existing)) existing = {};
|
|
1315
|
+
}
|
|
1316
|
+
const updated = { ...existing, ...updates };
|
|
1317
|
+
fs.writeFileSync(filePath, JSON.stringify(updated, null, 2), 'utf-8');
|
|
1318
|
+
return { success: true, settings: updated };
|
|
1319
|
+
} catch (e) {
|
|
1320
|
+
return { error: e && e.message ? e.message : '写入 Pi settings.json 失败' };
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1206
1324
|
function setToolConfigPermission(params = {}) {
|
|
1207
1325
|
const target = normalizeToolConfigTarget(params && params.target);
|
|
1208
1326
|
if (!target) return { error: '未知配置对象' };
|
|
@@ -2057,6 +2175,17 @@ async function fetchProviderModels(providerName, overrides = {}) {
|
|
|
2057
2175
|
}
|
|
2058
2176
|
|
|
2059
2177
|
// buildAgentsDiff keeps the metaOnly optimization inside cli/agents-files.js.
|
|
2178
|
+
const {
|
|
2179
|
+
backupPromptBeforeWrite: historyBackup,
|
|
2180
|
+
listPromptHistory,
|
|
2181
|
+
readPromptHistory,
|
|
2182
|
+
clearPromptHistory
|
|
2183
|
+
} = createPromptHistoryController({
|
|
2184
|
+
fs,
|
|
2185
|
+
path,
|
|
2186
|
+
CONFIG_DIR
|
|
2187
|
+
});
|
|
2188
|
+
|
|
2060
2189
|
const {
|
|
2061
2190
|
resolveAgentsFilePath,
|
|
2062
2191
|
validateAgentsBaseDir,
|
|
@@ -2083,6 +2212,7 @@ const {
|
|
|
2083
2212
|
AGENTS_FILE_NAME,
|
|
2084
2213
|
CLAUDE_DIR,
|
|
2085
2214
|
CLAUDE_MD_FILE_NAME,
|
|
2215
|
+
backupPromptBeforeWrite: historyBackup,
|
|
2086
2216
|
readOpenclawAgentsFile() {
|
|
2087
2217
|
return readOpenclawAgentsFile(...arguments);
|
|
2088
2218
|
},
|
|
@@ -2102,7 +2232,8 @@ const {
|
|
|
2102
2232
|
crypto,
|
|
2103
2233
|
buildLineDiff,
|
|
2104
2234
|
CONFIG_DIR,
|
|
2105
|
-
PI_AGENT_DIR
|
|
2235
|
+
PI_AGENT_DIR,
|
|
2236
|
+
backupPromptBeforeWrite: historyBackup
|
|
2106
2237
|
});
|
|
2107
2238
|
|
|
2108
2239
|
const {
|
|
@@ -13157,6 +13288,26 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
13157
13288
|
};
|
|
13158
13289
|
break;
|
|
13159
13290
|
}
|
|
13291
|
+
case 'read-pi-models': {
|
|
13292
|
+
result = readPiModels(params || {});
|
|
13293
|
+
break;
|
|
13294
|
+
}
|
|
13295
|
+
case 'write-pi-models': {
|
|
13296
|
+
result = writePiModels(params || {});
|
|
13297
|
+
break;
|
|
13298
|
+
}
|
|
13299
|
+
case 'read-pi-settings': {
|
|
13300
|
+
result = readPiSettings(params || {});
|
|
13301
|
+
break;
|
|
13302
|
+
}
|
|
13303
|
+
case 'write-pi-settings': {
|
|
13304
|
+
result = writePiSettings(params || {});
|
|
13305
|
+
break;
|
|
13306
|
+
}
|
|
13307
|
+
case 'fetch-pi-remote-models': {
|
|
13308
|
+
result = await fetchPiRemoteModels(params || {});
|
|
13309
|
+
break;
|
|
13310
|
+
}
|
|
13160
13311
|
case 'install-status':
|
|
13161
13312
|
result = buildInstallStatusReport();
|
|
13162
13313
|
break;
|
|
@@ -13378,6 +13529,12 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
13378
13529
|
case 'preview-system-prompt-diff':
|
|
13379
13530
|
result = buildSystemPromptDiff(params || {});
|
|
13380
13531
|
break;
|
|
13532
|
+
case 'list-prompt-history':
|
|
13533
|
+
result = listPromptHistory((params && params.bucket) || '');
|
|
13534
|
+
break;
|
|
13535
|
+
case 'get-prompt-history':
|
|
13536
|
+
result = readPromptHistory((params && params.bucket) || '', (params && params.id) || '');
|
|
13537
|
+
break;
|
|
13381
13538
|
case 'switch':
|
|
13382
13539
|
case 'use':
|
|
13383
13540
|
case 'add':
|
package/package.json
CHANGED
package/web-ui/app.js
CHANGED
|
@@ -26,6 +26,35 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
const { createApp } = Vue;
|
|
29
|
+
const showFatalErrorOverlay = (label, message, stack, extra) => {
|
|
30
|
+
try {
|
|
31
|
+
const target = document.querySelector('#app') || document.body;
|
|
32
|
+
const pre = document.createElement('pre');
|
|
33
|
+
pre.style.cssText = 'position:fixed;bottom:0;left:0;right:0;background:#900;color:#fff;padding:12px;white-space:pre-wrap;font-size:12px;z-index:9999;';
|
|
34
|
+
pre.textContent = '[' + label + '] ' + (message || '') + '\n' + (stack || '') + (extra ? '\n' + extra : '');
|
|
35
|
+
target.appendChild(pre);
|
|
36
|
+
} catch (_) {}
|
|
37
|
+
};
|
|
38
|
+
window.addEventListener('error', (event) => {
|
|
39
|
+
console.error('[window error]', event.message, event.filename, event.lineno, event.colno, event.error);
|
|
40
|
+
try {
|
|
41
|
+
const target = document.querySelector('#app') || document.body;
|
|
42
|
+
const pre = document.createElement('pre');
|
|
43
|
+
pre.style.cssText = 'position:fixed;bottom:60px;left:0;right:0;background:#b00;color:#fff;padding:12px;white-space:pre-wrap;font-size:12px;z-index:9999;';
|
|
44
|
+
pre.textContent = '[window error] ' + (event.message || '') + '\n' + ((event.error && event.error.stack) || (event.filename + ':' + event.lineno + ':' + event.colno));
|
|
45
|
+
target.appendChild(pre);
|
|
46
|
+
} catch (_) {}
|
|
47
|
+
});
|
|
48
|
+
window.addEventListener('unhandledrejection', (event) => {
|
|
49
|
+
console.error('[unhandled rejection]', event.reason);
|
|
50
|
+
try {
|
|
51
|
+
const target = document.querySelector('#app') || document.body;
|
|
52
|
+
const pre = document.createElement('pre');
|
|
53
|
+
pre.style.cssText = 'position:fixed;bottom:120px;left:0;right:0;background:#b00;color:#fff;padding:12px;white-space:pre-wrap;font-size:12px;z-index:9999;';
|
|
54
|
+
pre.textContent = '[unhandled rejection] ' + (event.reason && event.reason.message ? event.reason.message : String(event.reason)) + '\n' + ((event.reason && event.reason.stack) || '');
|
|
55
|
+
target.appendChild(pre);
|
|
56
|
+
} catch (_) {}
|
|
57
|
+
});
|
|
29
58
|
|
|
30
59
|
const appOptions = {
|
|
31
60
|
data() {
|
|
@@ -74,6 +103,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
74
103
|
showAgentsModal: false,
|
|
75
104
|
promptsSubTab: 'codex',
|
|
76
105
|
projectClaudeMdPath: '',
|
|
106
|
+
promptHistoryVisible: false,
|
|
107
|
+
promptHistoryLoading: false,
|
|
108
|
+
promptHistoryBucket: '',
|
|
109
|
+
promptHistoryItems: [],
|
|
110
|
+
promptHistoryPreviewId: '',
|
|
111
|
+
promptHistoryPreviewContent: '',
|
|
112
|
+
promptHistoryError: '',
|
|
77
113
|
promptPresets: [],
|
|
78
114
|
selectedPromptPresetId: '',
|
|
79
115
|
promptPresetNameDraft: '',
|
|
@@ -89,6 +125,29 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
89
125
|
showCodexBridgePoolModal: false,
|
|
90
126
|
showClaudeBridgePoolModal: false,
|
|
91
127
|
showWebhookModal: false,
|
|
128
|
+
piProviders: {},
|
|
129
|
+
piProviderIds: [],
|
|
130
|
+
editingPiProvider: null,
|
|
131
|
+
addingPiProviderId: '',
|
|
132
|
+
addingPiProviderName: '',
|
|
133
|
+
addingPiProviderBaseUrl: '',
|
|
134
|
+
addingPiProviderApi: 'openai-completions',
|
|
135
|
+
addingPiProviderApiKey: '',
|
|
136
|
+
addingPiProviderModel: '',
|
|
137
|
+
addingPiRemoteModels: [],
|
|
138
|
+
addingPiRemoteLoading: false,
|
|
139
|
+
addingPiRemoteError: '',
|
|
140
|
+
addingPiRemoteDebounce: null,
|
|
141
|
+
showAddPiProviderModal: false,
|
|
142
|
+
piProviderLoading: false,
|
|
143
|
+
piSaving: false,
|
|
144
|
+
piRemoteModels: [],
|
|
145
|
+
piRemoteModelsLoading: false,
|
|
146
|
+
piRemoteModelError: '',
|
|
147
|
+
piModelSearch: '',
|
|
148
|
+
piActiveProvider: '',
|
|
149
|
+
piActiveModel: '',
|
|
150
|
+
piShowKey: false,
|
|
92
151
|
// Plugins
|
|
93
152
|
pluginsActiveId: 'prompt-templates',
|
|
94
153
|
pluginsLoading: false,
|
|
@@ -130,6 +189,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
130
189
|
configTemplateDiffFingerprint: '',
|
|
131
190
|
_configTemplateDiffPreviewRequestToken: null,
|
|
132
191
|
configTemplateDiffConfirmEnabled: true,
|
|
192
|
+
configModeVisibility: { codex: true, claude: true, openclaw: true, opencode: true, kilocode: true, pi: true },
|
|
133
193
|
codexApplying: false,
|
|
134
194
|
_pendingCodexApplyOptions: null,
|
|
135
195
|
agentsContent: '',
|
|
@@ -161,6 +221,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
161
221
|
sysPromptPath: '',
|
|
162
222
|
sysPromptExists: false,
|
|
163
223
|
sysPromptHash: '',
|
|
224
|
+
sysHistoryBucket: '',
|
|
225
|
+
sysHistoryVisible: false,
|
|
226
|
+
sysHistoryLoading: false,
|
|
227
|
+
sysHistoryItems: [],
|
|
228
|
+
sysHistoryPreviewId: '',
|
|
229
|
+
sysHistoryPreviewContent: '',
|
|
230
|
+
sysHistoryError: '',
|
|
164
231
|
sysPromptLoading: false,
|
|
165
232
|
sysPromptSaving: false,
|
|
166
233
|
sysPromptDiffVisible: false,
|
|
@@ -418,8 +485,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
418
485
|
providerCacheError: '',
|
|
419
486
|
providerCacheRequestSeq: 0,
|
|
420
487
|
settingsTab: 'general',
|
|
421
|
-
toolConfigPermissions: { codex: false, claude: false, opencode: false, kilocode: false, openclaw: false },
|
|
422
|
-
toolConfigPermissionSaving: { codex: false, claude: false, opencode: false, kilocode: false, openclaw: false },
|
|
488
|
+
toolConfigPermissions: { codex: false, claude: false, opencode: false, kilocode: false, openclaw: false, pi: false },
|
|
489
|
+
toolConfigPermissionSaving: { codex: false, claude: false, opencode: false, kilocode: false, openclaw: false, pi: false },
|
|
423
490
|
sessionTrashEnabled: true,
|
|
424
491
|
sessionTrashItems: [],
|
|
425
492
|
sessionTrashVisibleCount: SESSION_TRASH_PAGE_SIZE,
|
|
@@ -762,8 +829,19 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
762
829
|
}
|
|
763
830
|
},
|
|
764
831
|
|
|
832
|
+
watch: {
|
|
833
|
+
configMode(newMode) {
|
|
834
|
+
if (newMode === 'pi' && typeof this.loadPiSources === 'function') {
|
|
835
|
+
this.loadPiSources();
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
},
|
|
839
|
+
|
|
765
840
|
computed: createAppComputed(),
|
|
766
|
-
methods:
|
|
841
|
+
methods: {
|
|
842
|
+
...createAppMethods(),
|
|
843
|
+
...(typeof createPiConfigMethods === 'function' ? createPiConfigMethods({ api }) : {})
|
|
844
|
+
}
|
|
767
845
|
};
|
|
768
846
|
|
|
769
847
|
if (typeof window.__CODEXMATE_WEB_UI_RENDER__ === 'function') {
|
|
@@ -771,6 +849,19 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
771
849
|
}
|
|
772
850
|
|
|
773
851
|
const app = createApp(appOptions);
|
|
852
|
+
app.config.errorHandler = (err, vm, info) => {
|
|
853
|
+
console.error('[Vue error handler]', err, info);
|
|
854
|
+
if (err && err.stack) console.error(err.stack);
|
|
855
|
+
showFatalErrorOverlay('Vue error', err && err.message ? err.message : String(err), err && err.stack ? err.stack : '', info || '');
|
|
856
|
+
};
|
|
774
857
|
|
|
775
|
-
|
|
858
|
+
try {
|
|
859
|
+
app.mount('#app');
|
|
860
|
+
} catch (error) {
|
|
861
|
+
console.error('Failed to mount Web UI:', error);
|
|
862
|
+
const fallback = document.querySelector('#app');
|
|
863
|
+
if (fallback) {
|
|
864
|
+
fallback.innerHTML = '<pre style="color:red;white-space:pre-wrap;">Failed to mount Web UI\n' + (error && error.stack ? error.stack : String(error)) + '</pre>';
|
|
865
|
+
}
|
|
866
|
+
}
|
|
776
867
|
});
|
package/web-ui/index.html
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
<!-- @include ./partials/index/panel-config-openclaw.html -->
|
|
19
19
|
<!-- @include ./partials/index/panel-config-opencode.html -->
|
|
20
20
|
<!-- @include ./partials/index/panel-config-kilocode.html -->
|
|
21
|
+
<!-- @include ./partials/index/panel-config-pi.html -->
|
|
21
22
|
<!-- @include ./partials/index/panel-sessions.html -->
|
|
22
23
|
<!-- @include ./partials/index/panel-usage.html -->
|
|
23
24
|
<!-- @include ./partials/index/panel-docs.html -->
|
|
@@ -34,6 +35,6 @@
|
|
|
34
35
|
<!-- @include ./partials/index/modal-skills.html -->
|
|
35
36
|
<!-- @include ./partials/index/modal-health-check.html -->
|
|
36
37
|
<!-- @include ./partials/index/modal-confirm-toast.html -->
|
|
37
|
-
<script type="module" src="/web-ui/app.js"></script>
|
|
38
|
+
<script type="module" src="/web-ui/app.js?cb=3"></script>
|
|
38
39
|
</body>
|
|
39
40
|
</html>
|
|
@@ -20,6 +20,22 @@ function isValidOpenclawWorkspaceFileName(fileName) {
|
|
|
20
20
|
return true;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
function sanitizePromptHistoryId(raw) {
|
|
24
|
+
const safe = typeof raw === 'string' ? raw.trim() : '';
|
|
25
|
+
if (!safe) return 'global';
|
|
26
|
+
const replaced = safe.replace(/[^A-Za-z0-9_.-]/g, '_');
|
|
27
|
+
return (replaced || 'global').slice(0, 64);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function resolveAgentsHistoryBucket(instance) {
|
|
31
|
+
const subTab = typeof instance.promptsSubTab === 'string' ? instance.promptsSubTab : 'codex';
|
|
32
|
+
if (subTab === 'claude-project') {
|
|
33
|
+
const projectPath = (instance.projectClaudeMdPath || '').trim();
|
|
34
|
+
return 'claude-project_' + sanitizePromptHistoryId(projectPath || 'global');
|
|
35
|
+
}
|
|
36
|
+
return 'codex_global';
|
|
37
|
+
}
|
|
38
|
+
|
|
23
39
|
export function createAgentsMethods(options = {}) {
|
|
24
40
|
const {
|
|
25
41
|
api,
|
|
@@ -681,6 +697,9 @@ export function createAgentsMethods(options = {}) {
|
|
|
681
697
|
this.showMessage(res.error, 'error');
|
|
682
698
|
return;
|
|
683
699
|
}
|
|
700
|
+
if (typeof res.historyBucket === 'string' && res.historyBucket) {
|
|
701
|
+
this.promptHistoryBucket = res.historyBucket;
|
|
702
|
+
}
|
|
684
703
|
const successLabel = this.agentsContext === 'openclaw-workspace'
|
|
685
704
|
? this.t('toast.agents.saved.workspace', { name: this.agentsWorkspaceFileName || '' }).replace(/:\s*$/, '')
|
|
686
705
|
: (this.agentsContext === 'claude-project'
|
|
@@ -884,6 +903,77 @@ export function createAgentsMethods(options = {}) {
|
|
|
884
903
|
await this.persistPromptPresets();
|
|
885
904
|
this.showMessage(this.t('prompts.presets.toast.deleted'), 'success');
|
|
886
905
|
},
|
|
906
|
+
async openPromptHistory() {
|
|
907
|
+
if (this.promptHistoryLoading) return;
|
|
908
|
+
const bucket = this.promptHistoryBucket || resolveAgentsHistoryBucket(this);
|
|
909
|
+
this.promptHistoryVisible = true;
|
|
910
|
+
this.promptHistoryError = '';
|
|
911
|
+
await this.loadPromptHistory(bucket);
|
|
912
|
+
},
|
|
913
|
+
|
|
914
|
+
async loadPromptHistory(bucketArg) {
|
|
915
|
+
if (this.promptHistoryLoading) return;
|
|
916
|
+
const bucket = typeof bucketArg === 'string' && bucketArg.trim()
|
|
917
|
+
? bucketArg.trim()
|
|
918
|
+
: (this.promptHistoryBucket || resolveAgentsHistoryBucket(this));
|
|
919
|
+
this.promptHistoryBucket = bucket;
|
|
920
|
+
this.promptHistoryLoading = true;
|
|
921
|
+
this.promptHistoryError = '';
|
|
922
|
+
this.promptHistoryItems = [];
|
|
923
|
+
this.promptHistoryPreviewId = '';
|
|
924
|
+
this.promptHistoryPreviewContent = '';
|
|
925
|
+
try {
|
|
926
|
+
const res = await api('list-prompt-history', { bucket });
|
|
927
|
+
if (res && res.error) {
|
|
928
|
+
this.promptHistoryError = res.error;
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
this.promptHistoryItems = Array.isArray(res) ? res : [];
|
|
932
|
+
} catch (e) {
|
|
933
|
+
this.promptHistoryError = this.t('toast.load.fail');
|
|
934
|
+
} finally {
|
|
935
|
+
this.promptHistoryLoading = false;
|
|
936
|
+
}
|
|
937
|
+
},
|
|
938
|
+
|
|
939
|
+
closePromptHistory() {
|
|
940
|
+
this.promptHistoryVisible = false;
|
|
941
|
+
this.promptHistoryPreviewId = '';
|
|
942
|
+
this.promptHistoryPreviewContent = '';
|
|
943
|
+
this.promptHistoryError = '';
|
|
944
|
+
},
|
|
945
|
+
|
|
946
|
+
async viewPromptHistoryItem(item) {
|
|
947
|
+
if (!item || !item.id) return;
|
|
948
|
+
if (this.promptHistoryLoading) return;
|
|
949
|
+
if (this.promptHistoryPreviewId === item.id && this.promptHistoryPreviewContent) return;
|
|
950
|
+
this.promptHistoryPreviewId = item.id;
|
|
951
|
+
this.promptHistoryPreviewContent = '';
|
|
952
|
+
try {
|
|
953
|
+
const res = await api('get-prompt-history', { bucket: this.promptHistoryBucket, id: item.id });
|
|
954
|
+
if (res && res.error) {
|
|
955
|
+
this.promptHistoryError = res.error;
|
|
956
|
+
this.promptHistoryPreviewId = '';
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
this.promptHistoryPreviewContent = typeof res.content === 'string' ? res.content : '';
|
|
960
|
+
} catch (e) {
|
|
961
|
+
this.promptHistoryError = this.t('toast.load.fail');
|
|
962
|
+
this.promptHistoryPreviewId = '';
|
|
963
|
+
}
|
|
964
|
+
},
|
|
965
|
+
|
|
966
|
+
applyPromptHistoryToEditor() {
|
|
967
|
+
if (!this.promptHistoryPreviewContent) return;
|
|
968
|
+
if (this.agentsSaving || this.agentsDiffVisible) return;
|
|
969
|
+
this.agentsContent = this.promptHistoryPreviewContent;
|
|
970
|
+
if (typeof this.onAgentsContentInput === 'function') this.onAgentsContentInput();
|
|
971
|
+
this.promptHistoryVisible = false;
|
|
972
|
+
this.promptHistoryPreviewId = '';
|
|
973
|
+
this.promptHistoryPreviewContent = '';
|
|
974
|
+
this.showMessage(this.t('toast.history.restored'), 'success');
|
|
975
|
+
},
|
|
976
|
+
|
|
887
977
|
switchPromptsSubTab(subTab) {
|
|
888
978
|
const normalized = subTab === 'claude-project' || subTab === 'system' ? subTab : 'codex';
|
|
889
979
|
if (normalized === 'claude-project' && !this.projectPathOptions.length && !this.projectPathOptionsLoading) {
|
|
@@ -22,6 +22,7 @@ import { createOpenclawEditingMethods } from './app.methods.openclaw-editing.mjs
|
|
|
22
22
|
import { createOpenclawPersistMethods } from './app.methods.openclaw-persist.mjs';
|
|
23
23
|
import { createOpencodeConfigMethods } from './app.methods.opencode-config.mjs';
|
|
24
24
|
import { createKilocodeConfigMethods } from './app.methods.kilocode-config.mjs';
|
|
25
|
+
import { createPiConfigMethods } from './app.methods.pi-config.mjs';
|
|
25
26
|
import { createProvidersMethods } from './app.methods.providers.mjs';
|
|
26
27
|
import { createProviderCacheMethods } from './app.methods.provider-cache.mjs';
|
|
27
28
|
import { createRuntimeMethods } from './app.methods.runtime.mjs';
|
|
@@ -102,6 +103,7 @@ export function createAppMethods() {
|
|
|
102
103
|
modelCatalog: OPENCODE_MODEL_CATALOG
|
|
103
104
|
}),
|
|
104
105
|
...createKilocodeConfigMethods({ api }),
|
|
106
|
+
...createPiConfigMethods({ api }),
|
|
105
107
|
...createInstallMethods({ api }),
|
|
106
108
|
...createRuntimeMethods({ api })
|
|
107
109
|
};
|
|
@@ -145,7 +145,13 @@
|
|
|
145
145
|
if (typeof this.ensureMainTabSwitchState === 'function') {
|
|
146
146
|
this.ensureMainTabSwitchState().pendingConfigMode = '';
|
|
147
147
|
}
|
|
148
|
-
|
|
148
|
+
const resolvedMode = configModeSet.has(normalizedMode) ? normalizedMode : 'codex';
|
|
149
|
+
if (typeof this.isConfigModeVisible === 'function' && !this.isConfigModeVisible(resolvedMode)) {
|
|
150
|
+
const fallback = ['codex', 'claude', 'openclaw', 'opencode', 'kilocode', 'pi'].find(m => this.isConfigModeVisible(m)) || 'codex';
|
|
151
|
+
this.configMode = configModeSet.has(fallback) ? fallback : 'codex';
|
|
152
|
+
} else {
|
|
153
|
+
this.configMode = resolvedMode;
|
|
154
|
+
}
|
|
149
155
|
if (this.mainTab === 'config') {
|
|
150
156
|
if (this.configMode === 'kilocode' && typeof this.loadKilocodeConfig === 'function') {
|
|
151
157
|
this.loadKilocodeConfig();
|