codexmate 0.0.38 → 0.0.40
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 +626 -207
- package/cli/config-bootstrap.js +6 -1
- package/cli/openai-bridge.js +541 -210
- package/cli.js +189 -4
- package/package.json +1 -1
- package/plugins/prompt-templates/computed.mjs +61 -3
- package/plugins/prompt-templates/manifest.mjs +3 -0
- package/web-ui/app.js +14 -3
- package/web-ui/modules/app.computed.main-tabs.mjs +39 -30
- package/web-ui/modules/app.methods.claude-config.mjs +111 -9
- package/web-ui/modules/app.methods.index.mjs +2 -0
- package/web-ui/modules/app.methods.openclaw-editing.mjs +48 -0
- package/web-ui/modules/app.methods.openclaw-persist.mjs +13 -7
- package/web-ui/modules/app.methods.providers.mjs +36 -10
- package/web-ui/modules/app.methods.runtime.mjs +76 -1
- package/web-ui/modules/app.methods.startup-claude.mjs +7 -0
- package/web-ui/modules/app.methods.tool-config-permissions.mjs +87 -0
- package/web-ui/modules/config-mode.computed.mjs +3 -3
- package/web-ui/modules/i18n/locales/en.mjs +1140 -0
- package/web-ui/modules/i18n/locales/ja.mjs +1130 -0
- package/web-ui/modules/i18n/locales/vi.mjs +239 -0
- package/web-ui/modules/i18n/locales/zh.mjs +1143 -0
- package/web-ui/modules/i18n.dict.mjs +9 -3195
- package/web-ui/modules/i18n.mjs +65 -16
- package/web-ui/partials/index/layout-header.html +16 -46
- package/web-ui/partials/index/modal-openclaw-config.html +135 -71
- package/web-ui/partials/index/modal-webhook.html +8 -8
- package/web-ui/partials/index/modals-basic.html +56 -16
- package/web-ui/partials/index/panel-config-claude.html +51 -21
- package/web-ui/partials/index/panel-config-codex.html +34 -5
- package/web-ui/partials/index/panel-config-openclaw.html +70 -64
- package/web-ui/partials/index/panel-dashboard.html +62 -77
- package/web-ui/partials/index/panel-settings.html +28 -7
- package/web-ui/partials/index/panel-trash.html +14 -14
- package/web-ui/res/web-ui-render.precompiled.js +1783 -1386
- package/web-ui/styles/controls-forms.css +99 -0
- package/web-ui/styles/dashboard.css +46 -14
- package/web-ui/styles/layout-shell.css +45 -0
- package/web-ui/styles/navigation-panels.css +3 -3
- package/web-ui/styles/openclaw-structured.css +383 -33
- package/web-ui/styles/responsive.css +68 -0
- package/web-ui/styles/sessions-usage.css +105 -9
- package/web-ui/styles/settings-panel.css +4 -0
package/cli.js
CHANGED
|
@@ -213,6 +213,7 @@ const CLAUDE_PROJECTS_DIR = path.join(os.homedir(), '.claude', 'projects');
|
|
|
213
213
|
const CODEBUDDY_DIR = path.join(os.homedir(), '.codebuddy');
|
|
214
214
|
const CODEBUDDY_PROJECTS_DIR = path.join(CODEBUDDY_DIR, 'projects');
|
|
215
215
|
const CODEXMATE_DIR = path.join(os.homedir(), '.codexmate');
|
|
216
|
+
const CODEXMATE_PREFERENCES_FILE = path.join(CODEXMATE_DIR, 'preferences.json');
|
|
216
217
|
const CODEXMATE_SESSIONS_DIR = path.join(CODEXMATE_DIR, 'sessions');
|
|
217
218
|
const CODEXMATE_DERIVED_SESSIONS_DIR = path.join(CODEXMATE_SESSIONS_DIR, 'derived');
|
|
218
219
|
const CODEXMATE_DERIVED_CODEX_DIR = path.join(CODEXMATE_DERIVED_SESSIONS_DIR, 'codex');
|
|
@@ -717,6 +718,7 @@ function readConfig() {
|
|
|
717
718
|
}
|
|
718
719
|
|
|
719
720
|
function writeConfig(content) {
|
|
721
|
+
assertToolConfigWriteAllowed('codex');
|
|
720
722
|
try {
|
|
721
723
|
fs.writeFileSync(CONFIG_FILE, content, 'utf-8');
|
|
722
724
|
} catch (e) {
|
|
@@ -734,6 +736,7 @@ function readModels() {
|
|
|
734
736
|
}
|
|
735
737
|
|
|
736
738
|
function writeModels(models) {
|
|
739
|
+
assertToolConfigWriteAllowed('codex');
|
|
737
740
|
fs.writeFileSync(MODELS_FILE, JSON.stringify(models, null, 2), 'utf-8');
|
|
738
741
|
}
|
|
739
742
|
|
|
@@ -747,10 +750,12 @@ function readCurrentModels() {
|
|
|
747
750
|
}
|
|
748
751
|
|
|
749
752
|
function writeCurrentModels(data) {
|
|
753
|
+
assertToolConfigWriteAllowed('codex');
|
|
750
754
|
fs.writeFileSync(CURRENT_MODELS_FILE, JSON.stringify(data, null, 2), 'utf-8');
|
|
751
755
|
}
|
|
752
756
|
|
|
753
757
|
function updateAuthJson(apiKey) {
|
|
758
|
+
assertToolConfigWriteAllowed('codex');
|
|
754
759
|
let authData = {};
|
|
755
760
|
if (fs.existsSync(AUTH_FILE)) {
|
|
756
761
|
try {
|
|
@@ -766,6 +771,141 @@ function isPlainObject(value) {
|
|
|
766
771
|
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
767
772
|
}
|
|
768
773
|
|
|
774
|
+
const TOOL_CONFIG_PERMISSION_TARGETS = new Set(['codex', 'claude']);
|
|
775
|
+
const TOOL_CONFIG_PERMISSION_DEFAULTS = Object.freeze({ codex: false, claude: false });
|
|
776
|
+
let toolConfigWriteGuardDepth = 0;
|
|
777
|
+
|
|
778
|
+
function enterToolConfigWriteGuard() {
|
|
779
|
+
toolConfigWriteGuardDepth += 1;
|
|
780
|
+
let active = true;
|
|
781
|
+
return () => {
|
|
782
|
+
if (!active) return;
|
|
783
|
+
active = false;
|
|
784
|
+
toolConfigWriteGuardDepth = Math.max(0, toolConfigWriteGuardDepth - 1);
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
function isToolConfigWriteGuardActive() {
|
|
789
|
+
return toolConfigWriteGuardDepth > 0;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
function normalizeToolConfigTarget(value) {
|
|
793
|
+
const target = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
794
|
+
return TOOL_CONFIG_PERMISSION_TARGETS.has(target) ? target : '';
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
function normalizeToolConfigPermissions(value) {
|
|
798
|
+
const source = isPlainObject(value) ? value : {};
|
|
799
|
+
return {
|
|
800
|
+
codex: source.codex === true,
|
|
801
|
+
claude: source.claude === true
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
function readCodexmatePreferences() {
|
|
806
|
+
if (!fs.existsSync(CODEXMATE_PREFERENCES_FILE)) return {};
|
|
807
|
+
try {
|
|
808
|
+
const raw = fs.readFileSync(CODEXMATE_PREFERENCES_FILE, 'utf-8');
|
|
809
|
+
const parsed = raw && raw.trim() ? JSON.parse(raw) : {};
|
|
810
|
+
return isPlainObject(parsed) ? parsed : {};
|
|
811
|
+
} catch (_) {
|
|
812
|
+
return {};
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
function writeCodexmatePreferences(preferences) {
|
|
817
|
+
ensureDir(CODEXMATE_DIR);
|
|
818
|
+
writeJsonAtomic(CODEXMATE_PREFERENCES_FILE, isPlainObject(preferences) ? preferences : {});
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
function readToolConfigPermissions() {
|
|
822
|
+
const preferences = readCodexmatePreferences();
|
|
823
|
+
return normalizeToolConfigPermissions(preferences.toolConfigPermissions || TOOL_CONFIG_PERMISSION_DEFAULTS);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
function isToolConfigWriteAllowed(target) {
|
|
827
|
+
const normalizedTarget = normalizeToolConfigTarget(target);
|
|
828
|
+
if (!normalizedTarget) return false;
|
|
829
|
+
return readToolConfigPermissions()[normalizedTarget] === true;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
function buildToolConfigWriteDeniedPayload(target) {
|
|
833
|
+
const normalizedTarget = normalizeToolConfigTarget(target) || target || '';
|
|
834
|
+
return {
|
|
835
|
+
error: '当前为仅浏览,未修改配置。',
|
|
836
|
+
errorCode: 'tool-config-write-disabled',
|
|
837
|
+
target: normalizedTarget,
|
|
838
|
+
permissions: readToolConfigPermissions()
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
function assertToolConfigWriteAllowed(target) {
|
|
843
|
+
if (!isToolConfigWriteGuardActive()) return;
|
|
844
|
+
if (isToolConfigWriteAllowed(target)) return;
|
|
845
|
+
const payload = buildToolConfigWriteDeniedPayload(target);
|
|
846
|
+
const err = new Error(payload.error);
|
|
847
|
+
err.code = payload.errorCode;
|
|
848
|
+
err.target = payload.target;
|
|
849
|
+
throw err;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
function getApiToolConfigWriteTarget(action) {
|
|
853
|
+
const name = typeof action === 'string' ? action.trim() : '';
|
|
854
|
+
if (!name) return '';
|
|
855
|
+
const codexWriteActions = new Set([
|
|
856
|
+
'apply-config-template',
|
|
857
|
+
'add-provider',
|
|
858
|
+
'update-provider',
|
|
859
|
+
'delete-provider',
|
|
860
|
+
'reset-config',
|
|
861
|
+
'add-model',
|
|
862
|
+
'delete-model',
|
|
863
|
+
'restore-codex-dir',
|
|
864
|
+
'import-config',
|
|
865
|
+
'import-auth-profile',
|
|
866
|
+
'switch-auth-profile',
|
|
867
|
+
'delete-auth-profile',
|
|
868
|
+
'proxy-enable-codex-default',
|
|
869
|
+
'proxy-apply-provider',
|
|
870
|
+
'local-bridge-toggle',
|
|
871
|
+
'local-bridge-set-excluded'
|
|
872
|
+
]);
|
|
873
|
+
const claudeWriteActions = new Set([
|
|
874
|
+
'apply-claude-settings-raw',
|
|
875
|
+
'apply-claude-config',
|
|
876
|
+
'restore-claude-dir',
|
|
877
|
+
'claude-local-bridge-toggle',
|
|
878
|
+
'claude-local-bridge-set-excluded',
|
|
879
|
+
'claude-local-bridge-sync-providers'
|
|
880
|
+
]);
|
|
881
|
+
if (codexWriteActions.has(name)) return 'codex';
|
|
882
|
+
if (claudeWriteActions.has(name)) return 'claude';
|
|
883
|
+
return '';
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
function setToolConfigPermission(params = {}) {
|
|
887
|
+
const target = normalizeToolConfigTarget(params && params.target);
|
|
888
|
+
if (!target) return { error: '未知配置对象' };
|
|
889
|
+
const preferences = readCodexmatePreferences();
|
|
890
|
+
const current = normalizeToolConfigPermissions(preferences.toolConfigPermissions || TOOL_CONFIG_PERMISSION_DEFAULTS);
|
|
891
|
+
current[target] = params && params.allowWrite === true;
|
|
892
|
+
preferences.toolConfigPermissions = current;
|
|
893
|
+
writeCodexmatePreferences(preferences);
|
|
894
|
+
|
|
895
|
+
let bootstrapNotice = '';
|
|
896
|
+
if (target === 'codex' && current.codex) {
|
|
897
|
+
const bootstrap = ensureManagedConfigBootstrap({ allowWrite: true });
|
|
898
|
+
bootstrapNotice = bootstrap && bootstrap.notice ? bootstrap.notice : '';
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
return {
|
|
902
|
+
success: true,
|
|
903
|
+
target,
|
|
904
|
+
permissions: current,
|
|
905
|
+
bootstrapNotice
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
|
|
769
909
|
const PROVIDER_CONFIG_KEYS = new Set([
|
|
770
910
|
'name',
|
|
771
911
|
'base_url',
|
|
@@ -2081,12 +2221,22 @@ function addProviderToConfig(params = {}) {
|
|
|
2081
2221
|
const name = typeof params.name === 'string' ? params.name.trim() : '';
|
|
2082
2222
|
const url = typeof params.url === 'string' ? params.url.trim() : '';
|
|
2083
2223
|
const key = typeof params.key === 'string' ? params.key.trim() : '';
|
|
2224
|
+
const requireModel = !!params.requireModel;
|
|
2225
|
+
const fallbackModel = (() => {
|
|
2226
|
+
if (requireModel) return '';
|
|
2227
|
+
const list = readModels();
|
|
2228
|
+
return Array.isArray(list) && typeof list[0] === 'string' ? list[0].trim() : '';
|
|
2229
|
+
})();
|
|
2230
|
+
const model = typeof params.model === 'string' && params.model.trim()
|
|
2231
|
+
? params.model.trim()
|
|
2232
|
+
: fallbackModel;
|
|
2084
2233
|
const useTransform = !!params.useTransform;
|
|
2085
2234
|
const allowManaged = !!params.allowManaged;
|
|
2086
2235
|
const normalizedUrl = normalizeBaseUrl(url);
|
|
2087
2236
|
|
|
2088
2237
|
if (!name) return { error: '名称不能为空' };
|
|
2089
2238
|
if (!url) return { error: 'URL 不能为空' };
|
|
2239
|
+
if (!model) return { error: '模型名称不能为空' };
|
|
2090
2240
|
if (!isValidProviderName(name)) {
|
|
2091
2241
|
return { error: '名称仅支持字母/数字/._-' };
|
|
2092
2242
|
}
|
|
@@ -2163,6 +2313,7 @@ function addProviderToConfig(params = {}) {
|
|
|
2163
2313
|
`wire_api = "responses"`,
|
|
2164
2314
|
`requires_openai_auth = ${requiresOpenaiAuth ? 'true' : 'false'}`,
|
|
2165
2315
|
`preferred_auth_method = "${safeKey}"`,
|
|
2316
|
+
`models = [{ id = "${escapeTomlBasicString(model)}", name = "${escapeTomlBasicString(model)}" }]`,
|
|
2166
2317
|
...extraLines,
|
|
2167
2318
|
`request_max_retries = 4`,
|
|
2168
2319
|
`stream_max_retries = 10`,
|
|
@@ -2173,6 +2324,13 @@ function addProviderToConfig(params = {}) {
|
|
|
2173
2324
|
|
|
2174
2325
|
try {
|
|
2175
2326
|
writeConfig(newContent);
|
|
2327
|
+
const models = readModels();
|
|
2328
|
+
if (!models.includes(model)) {
|
|
2329
|
+
writeModels([...models, model]);
|
|
2330
|
+
}
|
|
2331
|
+
const currentModels = readCurrentModels();
|
|
2332
|
+
currentModels[name] = model;
|
|
2333
|
+
writeCurrentModels(currentModels);
|
|
2176
2334
|
} catch (e) {
|
|
2177
2335
|
return { error: `写入配置失败: ${e.message}` };
|
|
2178
2336
|
}
|
|
@@ -5525,6 +5683,7 @@ function readLocalBridgeSettings() {
|
|
|
5525
5683
|
}
|
|
5526
5684
|
|
|
5527
5685
|
function writeLocalBridgeSettings(settings) {
|
|
5686
|
+
assertToolConfigWriteAllowed('codex');
|
|
5528
5687
|
fs.writeFileSync(LOCAL_BRIDGE_SETTINGS_FILE, JSON.stringify(settings, null, 2), 'utf-8');
|
|
5529
5688
|
}
|
|
5530
5689
|
|
|
@@ -5623,6 +5782,7 @@ function readClaudeLocalBridgeSettings() {
|
|
|
5623
5782
|
}
|
|
5624
5783
|
|
|
5625
5784
|
function writeClaudeLocalBridgeSettings(settings) {
|
|
5785
|
+
assertToolConfigWriteAllowed('claude');
|
|
5626
5786
|
fs.writeFileSync(CLAUDE_LOCAL_BRIDGE_SETTINGS_FILE, JSON.stringify(settings, null, 2), 'utf-8');
|
|
5627
5787
|
}
|
|
5628
5788
|
|
|
@@ -5637,6 +5797,7 @@ function readClaudeLocalProvidersFile() {
|
|
|
5637
5797
|
}
|
|
5638
5798
|
|
|
5639
5799
|
function writeClaudeLocalProvidersFile(data) {
|
|
5800
|
+
assertToolConfigWriteAllowed('claude');
|
|
5640
5801
|
ensureDir(CONFIG_DIR);
|
|
5641
5802
|
fs.writeFileSync(CLAUDE_LOCAL_PROVIDERS_FILE, JSON.stringify(data, null, 2), 'utf-8');
|
|
5642
5803
|
}
|
|
@@ -5651,6 +5812,7 @@ function syncClaudeProvidersToBridgeFile() {
|
|
|
5651
5812
|
}
|
|
5652
5813
|
|
|
5653
5814
|
function toggleClaudeLocalBridge(params = {}) {
|
|
5815
|
+
assertToolConfigWriteAllowed('claude');
|
|
5654
5816
|
const enable = !!params.enable;
|
|
5655
5817
|
const settings = readClaudeLocalBridgeSettings();
|
|
5656
5818
|
|
|
@@ -9159,6 +9321,7 @@ function maskKey(key) {
|
|
|
9159
9321
|
|
|
9160
9322
|
// 应用到 Claude Code settings.json(跨平台)
|
|
9161
9323
|
function applyToClaudeSettings(config = {}) {
|
|
9324
|
+
assertToolConfigWriteAllowed('claude');
|
|
9162
9325
|
try {
|
|
9163
9326
|
const apiKey = (config.apiKey || '').trim();
|
|
9164
9327
|
if (!apiKey) {
|
|
@@ -9258,6 +9421,7 @@ function readClaudeSettingsRaw() {
|
|
|
9258
9421
|
}
|
|
9259
9422
|
|
|
9260
9423
|
function applyClaudeSettingsRaw(params = {}) {
|
|
9424
|
+
assertToolConfigWriteAllowed('claude');
|
|
9261
9425
|
const content = typeof params.content === 'string' ? params.content : '';
|
|
9262
9426
|
if (!content.trim()) {
|
|
9263
9427
|
return { error: '内容不能为空' };
|
|
@@ -10751,14 +10915,28 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
10751
10915
|
});
|
|
10752
10916
|
req.on('end', async () => {
|
|
10753
10917
|
if (bodyTooLarge) return;
|
|
10918
|
+
let leaveToolConfigWriteGuard = null;
|
|
10754
10919
|
try {
|
|
10755
10920
|
const { action, params } = JSON.parse(body || '{}');
|
|
10921
|
+
leaveToolConfigWriteGuard = typeof enterToolConfigWriteGuard === 'function'
|
|
10922
|
+
? enterToolConfigWriteGuard()
|
|
10923
|
+
: () => {};
|
|
10756
10924
|
let result;
|
|
10757
10925
|
|
|
10758
|
-
|
|
10926
|
+
const guardedToolConfigTarget = getApiToolConfigWriteTarget(action);
|
|
10927
|
+
if (guardedToolConfigTarget && !isToolConfigWriteAllowed(guardedToolConfigTarget)) {
|
|
10928
|
+
result = buildToolConfigWriteDeniedPayload(guardedToolConfigTarget);
|
|
10929
|
+
} else {
|
|
10930
|
+
switch (action) {
|
|
10759
10931
|
case 'health-check':
|
|
10760
10932
|
result = { ok: true };
|
|
10761
10933
|
break;
|
|
10934
|
+
case 'get-tool-config-permissions':
|
|
10935
|
+
result = { permissions: readToolConfigPermissions() };
|
|
10936
|
+
break;
|
|
10937
|
+
case 'set-tool-config-permission':
|
|
10938
|
+
result = setToolConfigPermission(params || {});
|
|
10939
|
+
break;
|
|
10762
10940
|
case 'status': {
|
|
10763
10941
|
const statusConfigResult = readConfigOrVirtualDefault();
|
|
10764
10942
|
const config = statusConfigResult.config;
|
|
@@ -10797,7 +10975,8 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
10797
10975
|
configReady: !statusConfigResult.isVirtual,
|
|
10798
10976
|
configErrorType: statusConfigResult.errorType || '',
|
|
10799
10977
|
configNotice: statusConfigResult.reason || '',
|
|
10800
|
-
initNotice: consumeInitNotice()
|
|
10978
|
+
initNotice: consumeInitNotice(),
|
|
10979
|
+
toolConfigPermissions: readToolConfigPermissions()
|
|
10801
10980
|
};
|
|
10802
10981
|
break;
|
|
10803
10982
|
}
|
|
@@ -10866,7 +11045,7 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
10866
11045
|
result = buildConfigTemplateDiff(params || {});
|
|
10867
11046
|
break;
|
|
10868
11047
|
case 'add-provider':
|
|
10869
|
-
result = addProviderToConfig(params || {});
|
|
11048
|
+
result = addProviderToConfig({ ...(params || {}), requireModel: true });
|
|
10870
11049
|
break;
|
|
10871
11050
|
case 'update-provider':
|
|
10872
11051
|
result = updateProviderInConfig(params || {});
|
|
@@ -11437,6 +11616,7 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
11437
11616
|
break;
|
|
11438
11617
|
default:
|
|
11439
11618
|
result = { error: '未知操作' };
|
|
11619
|
+
}
|
|
11440
11620
|
}
|
|
11441
11621
|
|
|
11442
11622
|
const responseBody = JSON.stringify(result, null, 2);
|
|
@@ -11445,7 +11625,9 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
11445
11625
|
'Content-Length': Buffer.byteLength(responseBody, 'utf-8')
|
|
11446
11626
|
});
|
|
11447
11627
|
res.end(responseBody, 'utf-8');
|
|
11628
|
+
if (leaveToolConfigWriteGuard) leaveToolConfigWriteGuard();
|
|
11448
11629
|
} catch (e) {
|
|
11630
|
+
if (leaveToolConfigWriteGuard) leaveToolConfigWriteGuard();
|
|
11449
11631
|
const errorBody = JSON.stringify({ error: e.message }, null, 2);
|
|
11450
11632
|
res.writeHead(500, {
|
|
11451
11633
|
'Content-Type': 'application/json; charset=utf-8',
|
|
@@ -16044,7 +16226,10 @@ async function main() {
|
|
|
16044
16226
|
const args = process.argv.slice(2);
|
|
16045
16227
|
const command = args[0];
|
|
16046
16228
|
const isMcpCommand = command === 'mcp';
|
|
16047
|
-
const
|
|
16229
|
+
const shouldGateInitialBootstrap = command === 'run' || isMcpCommand;
|
|
16230
|
+
const bootstrap = ensureManagedConfigBootstrap({
|
|
16231
|
+
allowWrite: shouldGateInitialBootstrap ? isToolConfigWriteAllowed('codex') : true
|
|
16232
|
+
});
|
|
16048
16233
|
if (bootstrap && bootstrap.notice) {
|
|
16049
16234
|
// MCP stdio transport requires stdout to be protocol-clean.
|
|
16050
16235
|
if (!isMcpCommand) {
|
package/package.json
CHANGED
|
@@ -102,18 +102,75 @@ function renderTemplate(templateText, values = {}) {
|
|
|
102
102
|
});
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
function translate(t, key, fallback, params = null) {
|
|
106
|
+
if (typeof t !== 'function') return fallback;
|
|
107
|
+
const translated = t(key, params);
|
|
108
|
+
return translated === key ? fallback : translated;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function localizePluginMeta(meta, t) {
|
|
112
|
+
const safe = meta && typeof meta === 'object' ? meta : {};
|
|
113
|
+
const titleKey = typeof safe.titleKey === 'string' ? safe.titleKey : '';
|
|
114
|
+
const descriptionKey = typeof safe.descriptionKey === 'string' ? safe.descriptionKey : '';
|
|
115
|
+
const statusLabelKey = typeof safe.statusLabelKey === 'string' ? safe.statusLabelKey : '';
|
|
116
|
+
return {
|
|
117
|
+
...safe,
|
|
118
|
+
title: titleKey ? translate(t, titleKey, safe.title || '') : (safe.title || ''),
|
|
119
|
+
description: descriptionKey ? translate(t, descriptionKey, safe.description || '') : (safe.description || ''),
|
|
120
|
+
statusLabel: statusLabelKey ? translate(t, statusLabelKey, safe.statusLabel || '') : (safe.statusLabel || '')
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const BUILTIN_TEMPLATE_I18N = Object.freeze({
|
|
125
|
+
builtin_comment_polish: Object.freeze({
|
|
126
|
+
nameKey: 'plugins.builtin.commentPolish.name',
|
|
127
|
+
descKey: 'plugins.builtin.commentPolish.desc',
|
|
128
|
+
lineKey: 'plugins.builtin.commentPolish.line1',
|
|
129
|
+
fallbackName: '代码注释润色',
|
|
130
|
+
fallbackDesc: '轻微收敛以下代码注释 {{code}}',
|
|
131
|
+
fallbackLine: '轻微收敛以下代码注释',
|
|
132
|
+
vars: ['{{code}}']
|
|
133
|
+
}),
|
|
134
|
+
builtin_rule_ack: Object.freeze({
|
|
135
|
+
nameKey: 'plugins.builtin.ruleAck.name',
|
|
136
|
+
descKey: 'plugins.builtin.ruleAck.desc',
|
|
137
|
+
lineKey: 'plugins.builtin.ruleAck.line1',
|
|
138
|
+
fallbackName: '规则确认回复',
|
|
139
|
+
fallbackDesc: '请根据【{{rule}}】,收到请回复',
|
|
140
|
+
fallbackLine: '请根据【{{rule}}】,收到请回复',
|
|
141
|
+
vars: []
|
|
142
|
+
})
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
function localizeBuiltinPromptTemplate(item, t) {
|
|
146
|
+
const safe = item && typeof item === 'object' ? item : {};
|
|
147
|
+
if (safe.isBuiltin !== true) return safe;
|
|
148
|
+
const spec = BUILTIN_TEMPLATE_I18N[safe.id];
|
|
149
|
+
if (!spec) return safe;
|
|
150
|
+
const line = translate(t, spec.lineKey, spec.fallbackLine);
|
|
151
|
+
return {
|
|
152
|
+
...safe,
|
|
153
|
+
name: translate(t, spec.nameKey, spec.fallbackName),
|
|
154
|
+
description: translate(t, spec.descKey, spec.fallbackDesc),
|
|
155
|
+
template: spec.vars && spec.vars.length ? [line, '', ...spec.vars].join('\n') : line
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
105
159
|
import { pluginsRegistry } from '../registry.mjs';
|
|
106
160
|
|
|
107
161
|
export function createPluginsComputed() {
|
|
108
162
|
return {
|
|
109
163
|
pluginsCatalog() {
|
|
110
|
-
return pluginsRegistry
|
|
164
|
+
return pluginsRegistry
|
|
165
|
+
.map((entry) => entry && entry.meta)
|
|
166
|
+
.filter(Boolean)
|
|
167
|
+
.map((meta) => localizePluginMeta(meta, this.t));
|
|
111
168
|
},
|
|
112
169
|
|
|
113
170
|
pluginsActiveMeta() {
|
|
114
171
|
const id = typeof this.pluginsActiveId === 'string' ? this.pluginsActiveId.trim() : '';
|
|
115
172
|
const entry = pluginsRegistry.find((item) => item && item.id === id) || null;
|
|
116
|
-
return entry && entry.meta ? entry.meta : null;
|
|
173
|
+
return entry && entry.meta ? localizePluginMeta(entry.meta, this.t) : null;
|
|
117
174
|
},
|
|
118
175
|
|
|
119
176
|
pluginsActiveAttribution() {
|
|
@@ -138,6 +195,7 @@ export function createPluginsComputed() {
|
|
|
138
195
|
const list = Array.isArray(this.promptTemplatesListRaw) ? this.promptTemplatesListRaw : [];
|
|
139
196
|
return list
|
|
140
197
|
.map((item) => normalizePromptTemplateEntry(item))
|
|
198
|
+
.map((item) => localizeBuiltinPromptTemplate(item, this.t))
|
|
141
199
|
.filter((item) => item.id && item.name)
|
|
142
200
|
.map((item) => {
|
|
143
201
|
const vars = parseTemplateVariables(item.template);
|
|
@@ -178,7 +236,7 @@ export function createPluginsComputed() {
|
|
|
178
236
|
const id = typeof draft.id === 'string' ? draft.id : '';
|
|
179
237
|
const name = typeof draft.name === 'string' ? draft.name : '';
|
|
180
238
|
if (!id && !name) return null;
|
|
181
|
-
return normalizePromptTemplateEntry(draft);
|
|
239
|
+
return localizeBuiltinPromptTemplate(normalizePromptTemplateEntry(draft), this.t);
|
|
182
240
|
},
|
|
183
241
|
|
|
184
242
|
promptTemplateVars() {
|
|
@@ -3,8 +3,11 @@ import { pluginOwnership } from './ownership.mjs';
|
|
|
3
3
|
const baseMeta = {
|
|
4
4
|
id: 'prompt-templates',
|
|
5
5
|
title: 'Prompt Templates',
|
|
6
|
+
titleKey: 'plugins.catalog.promptTemplates.title',
|
|
6
7
|
description: 'Standardized, template-driven prompts with variables and copy/export helpers.',
|
|
8
|
+
descriptionKey: 'plugins.catalog.promptTemplates.description',
|
|
7
9
|
statusLabel: 'standard',
|
|
10
|
+
statusLabelKey: 'plugins.status.standard',
|
|
8
11
|
tone: 'configured'
|
|
9
12
|
};
|
|
10
13
|
|
package/web-ui/app.js
CHANGED
|
@@ -62,11 +62,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
62
62
|
messageType: '',
|
|
63
63
|
showAddModal: false,
|
|
64
64
|
showEditModal: false,
|
|
65
|
+
showAddProviderKey: false,
|
|
65
66
|
showEditProviderKey: false,
|
|
66
67
|
showModelModal: false,
|
|
67
68
|
showModelListModal: false,
|
|
68
69
|
showClaudeConfigModal: false,
|
|
69
70
|
showEditConfigModal: false,
|
|
71
|
+
showAddClaudeConfigKey: false,
|
|
70
72
|
showEditClaudeConfigKey: false,
|
|
71
73
|
showOpenclawConfigModal: false,
|
|
72
74
|
showConfigTemplateModal: false,
|
|
@@ -268,7 +270,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
268
270
|
installRegistryPreset: 'default',
|
|
269
271
|
installRegistryCustom: '',
|
|
270
272
|
installStatusTargets: null,
|
|
271
|
-
newProvider: { name: '', url: '', key: '',
|
|
273
|
+
newProvider: { name: '', url: '', key: '', model: '', useTransform: false },
|
|
272
274
|
resetConfigLoading: false,
|
|
273
275
|
editingProvider: { name: '', url: '', key: '', readOnly: false, nonEditable: false },
|
|
274
276
|
newModelName: '',
|
|
@@ -293,7 +295,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
293
295
|
currentOpenclawConfig: '',
|
|
294
296
|
openclawConfigs: {
|
|
295
297
|
'默认配置': {
|
|
296
|
-
content: DEFAULT_OPENCLAW_TEMPLATE
|
|
298
|
+
content: DEFAULT_OPENCLAW_TEMPLATE,
|
|
299
|
+
isDefault: true
|
|
297
300
|
}
|
|
298
301
|
},
|
|
299
302
|
openclawEditing: { name: '', content: '', lockName: false },
|
|
@@ -343,6 +346,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
343
346
|
overrideModels: true,
|
|
344
347
|
showKey: false
|
|
345
348
|
},
|
|
349
|
+
openclawAccordionStep: 1,
|
|
350
|
+
openclawValidation: {
|
|
351
|
+
providerName: { valid: true, message: '' },
|
|
352
|
+
modelId: { valid: true, message: '' }
|
|
353
|
+
},
|
|
346
354
|
openclawAgentsList: [],
|
|
347
355
|
openclawProviders: [],
|
|
348
356
|
openclawMissingProviders: [],
|
|
@@ -358,6 +366,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
358
366
|
codexDownloadProgress: 0,
|
|
359
367
|
codexDownloadTimer: null,
|
|
360
368
|
settingsTab: 'general',
|
|
369
|
+
toolConfigPermissions: { codex: false, claude: false },
|
|
370
|
+
toolConfigPermissionSaving: { codex: false, claude: false },
|
|
361
371
|
sessionTrashEnabled: true,
|
|
362
372
|
sessionTrashItems: [],
|
|
363
373
|
sessionTrashVisibleCount: SESSION_TRASH_PAGE_SIZE,
|
|
@@ -567,7 +577,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
567
577
|
: { content: DEFAULT_OPENCLAW_TEMPLATE };
|
|
568
578
|
const normalized = {
|
|
569
579
|
'默认配置': {
|
|
570
|
-
content: typeof defaultEntry.content === 'string' ? defaultEntry.content : DEFAULT_OPENCLAW_TEMPLATE
|
|
580
|
+
content: typeof defaultEntry.content === 'string' ? defaultEntry.content : DEFAULT_OPENCLAW_TEMPLATE,
|
|
581
|
+
isDefault: true
|
|
571
582
|
}
|
|
572
583
|
};
|
|
573
584
|
for (const [name, value] of Object.entries(source)) {
|