codexmate 0.0.39 → 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/config-bootstrap.js +6 -1
- package/cli.js +170 -3
- 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 +2 -0
- package/web-ui/modules/app.computed.main-tabs.mjs +3 -1
- package/web-ui/modules/app.methods.index.mjs +2 -0
- package/web-ui/modules/app.methods.startup-claude.mjs +6 -0
- package/web-ui/modules/app.methods.tool-config-permissions.mjs +87 -0
- 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 -3208
- package/web-ui/partials/index/panel-config-claude.html +31 -1
- package/web-ui/partials/index/panel-config-codex.html +30 -1
- package/web-ui/res/web-ui-render.precompiled.js +967 -877
- package/web-ui/styles/controls-forms.css +93 -0
package/cli/config-bootstrap.js
CHANGED
|
@@ -273,7 +273,12 @@ stream_idle_timeout_ms = 300000
|
|
|
273
273
|
fs.writeFileSync(INIT_MARK_FILE, JSON.stringify(payload, null, 2), 'utf-8');
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
-
function ensureManagedConfigBootstrap() {
|
|
276
|
+
function ensureManagedConfigBootstrap(options = {}) {
|
|
277
|
+
const allowWrite = !(options && options.allowWrite === false);
|
|
278
|
+
if (!allowWrite) {
|
|
279
|
+
initNotice = '';
|
|
280
|
+
return { notice: '', readOnly: true };
|
|
281
|
+
}
|
|
277
282
|
ensureConfigDir();
|
|
278
283
|
|
|
279
284
|
const initializedAt = new Date().toISOString();
|
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',
|
|
@@ -5543,6 +5683,7 @@ function readLocalBridgeSettings() {
|
|
|
5543
5683
|
}
|
|
5544
5684
|
|
|
5545
5685
|
function writeLocalBridgeSettings(settings) {
|
|
5686
|
+
assertToolConfigWriteAllowed('codex');
|
|
5546
5687
|
fs.writeFileSync(LOCAL_BRIDGE_SETTINGS_FILE, JSON.stringify(settings, null, 2), 'utf-8');
|
|
5547
5688
|
}
|
|
5548
5689
|
|
|
@@ -5641,6 +5782,7 @@ function readClaudeLocalBridgeSettings() {
|
|
|
5641
5782
|
}
|
|
5642
5783
|
|
|
5643
5784
|
function writeClaudeLocalBridgeSettings(settings) {
|
|
5785
|
+
assertToolConfigWriteAllowed('claude');
|
|
5644
5786
|
fs.writeFileSync(CLAUDE_LOCAL_BRIDGE_SETTINGS_FILE, JSON.stringify(settings, null, 2), 'utf-8');
|
|
5645
5787
|
}
|
|
5646
5788
|
|
|
@@ -5655,6 +5797,7 @@ function readClaudeLocalProvidersFile() {
|
|
|
5655
5797
|
}
|
|
5656
5798
|
|
|
5657
5799
|
function writeClaudeLocalProvidersFile(data) {
|
|
5800
|
+
assertToolConfigWriteAllowed('claude');
|
|
5658
5801
|
ensureDir(CONFIG_DIR);
|
|
5659
5802
|
fs.writeFileSync(CLAUDE_LOCAL_PROVIDERS_FILE, JSON.stringify(data, null, 2), 'utf-8');
|
|
5660
5803
|
}
|
|
@@ -5669,6 +5812,7 @@ function syncClaudeProvidersToBridgeFile() {
|
|
|
5669
5812
|
}
|
|
5670
5813
|
|
|
5671
5814
|
function toggleClaudeLocalBridge(params = {}) {
|
|
5815
|
+
assertToolConfigWriteAllowed('claude');
|
|
5672
5816
|
const enable = !!params.enable;
|
|
5673
5817
|
const settings = readClaudeLocalBridgeSettings();
|
|
5674
5818
|
|
|
@@ -9177,6 +9321,7 @@ function maskKey(key) {
|
|
|
9177
9321
|
|
|
9178
9322
|
// 应用到 Claude Code settings.json(跨平台)
|
|
9179
9323
|
function applyToClaudeSettings(config = {}) {
|
|
9324
|
+
assertToolConfigWriteAllowed('claude');
|
|
9180
9325
|
try {
|
|
9181
9326
|
const apiKey = (config.apiKey || '').trim();
|
|
9182
9327
|
if (!apiKey) {
|
|
@@ -9276,6 +9421,7 @@ function readClaudeSettingsRaw() {
|
|
|
9276
9421
|
}
|
|
9277
9422
|
|
|
9278
9423
|
function applyClaudeSettingsRaw(params = {}) {
|
|
9424
|
+
assertToolConfigWriteAllowed('claude');
|
|
9279
9425
|
const content = typeof params.content === 'string' ? params.content : '';
|
|
9280
9426
|
if (!content.trim()) {
|
|
9281
9427
|
return { error: '内容不能为空' };
|
|
@@ -10769,14 +10915,28 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
10769
10915
|
});
|
|
10770
10916
|
req.on('end', async () => {
|
|
10771
10917
|
if (bodyTooLarge) return;
|
|
10918
|
+
let leaveToolConfigWriteGuard = null;
|
|
10772
10919
|
try {
|
|
10773
10920
|
const { action, params } = JSON.parse(body || '{}');
|
|
10921
|
+
leaveToolConfigWriteGuard = typeof enterToolConfigWriteGuard === 'function'
|
|
10922
|
+
? enterToolConfigWriteGuard()
|
|
10923
|
+
: () => {};
|
|
10774
10924
|
let result;
|
|
10775
10925
|
|
|
10776
|
-
|
|
10926
|
+
const guardedToolConfigTarget = getApiToolConfigWriteTarget(action);
|
|
10927
|
+
if (guardedToolConfigTarget && !isToolConfigWriteAllowed(guardedToolConfigTarget)) {
|
|
10928
|
+
result = buildToolConfigWriteDeniedPayload(guardedToolConfigTarget);
|
|
10929
|
+
} else {
|
|
10930
|
+
switch (action) {
|
|
10777
10931
|
case 'health-check':
|
|
10778
10932
|
result = { ok: true };
|
|
10779
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;
|
|
10780
10940
|
case 'status': {
|
|
10781
10941
|
const statusConfigResult = readConfigOrVirtualDefault();
|
|
10782
10942
|
const config = statusConfigResult.config;
|
|
@@ -10815,7 +10975,8 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
10815
10975
|
configReady: !statusConfigResult.isVirtual,
|
|
10816
10976
|
configErrorType: statusConfigResult.errorType || '',
|
|
10817
10977
|
configNotice: statusConfigResult.reason || '',
|
|
10818
|
-
initNotice: consumeInitNotice()
|
|
10978
|
+
initNotice: consumeInitNotice(),
|
|
10979
|
+
toolConfigPermissions: readToolConfigPermissions()
|
|
10819
10980
|
};
|
|
10820
10981
|
break;
|
|
10821
10982
|
}
|
|
@@ -11455,6 +11616,7 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
11455
11616
|
break;
|
|
11456
11617
|
default:
|
|
11457
11618
|
result = { error: '未知操作' };
|
|
11619
|
+
}
|
|
11458
11620
|
}
|
|
11459
11621
|
|
|
11460
11622
|
const responseBody = JSON.stringify(result, null, 2);
|
|
@@ -11463,7 +11625,9 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
11463
11625
|
'Content-Length': Buffer.byteLength(responseBody, 'utf-8')
|
|
11464
11626
|
});
|
|
11465
11627
|
res.end(responseBody, 'utf-8');
|
|
11628
|
+
if (leaveToolConfigWriteGuard) leaveToolConfigWriteGuard();
|
|
11466
11629
|
} catch (e) {
|
|
11630
|
+
if (leaveToolConfigWriteGuard) leaveToolConfigWriteGuard();
|
|
11467
11631
|
const errorBody = JSON.stringify({ error: e.message }, null, 2);
|
|
11468
11632
|
res.writeHead(500, {
|
|
11469
11633
|
'Content-Type': 'application/json; charset=utf-8',
|
|
@@ -16062,7 +16226,10 @@ async function main() {
|
|
|
16062
16226
|
const args = process.argv.slice(2);
|
|
16063
16227
|
const command = args[0];
|
|
16064
16228
|
const isMcpCommand = command === 'mcp';
|
|
16065
|
-
const
|
|
16229
|
+
const shouldGateInitialBootstrap = command === 'run' || isMcpCommand;
|
|
16230
|
+
const bootstrap = ensureManagedConfigBootstrap({
|
|
16231
|
+
allowWrite: shouldGateInitialBootstrap ? isToolConfigWriteAllowed('codex') : true
|
|
16232
|
+
});
|
|
16066
16233
|
if (bootstrap && bootstrap.notice) {
|
|
16067
16234
|
// MCP stdio transport requires stdout to be protocol-clean.
|
|
16068
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
|
@@ -366,6 +366,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
366
366
|
codexDownloadProgress: 0,
|
|
367
367
|
codexDownloadTimer: null,
|
|
368
368
|
settingsTab: 'general',
|
|
369
|
+
toolConfigPermissions: { codex: false, claude: false },
|
|
370
|
+
toolConfigPermissionSaving: { codex: false, claude: false },
|
|
369
371
|
sessionTrashEnabled: true,
|
|
370
372
|
sessionTrashItems: [],
|
|
371
373
|
sessionTrashVisibleCount: SESSION_TRASH_PAGE_SIZE,
|
|
@@ -44,7 +44,9 @@ function readTaskOrchestrationDraftMetrics(taskOrchestration) {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
function translateTaskText(t, key, fallback, params = null) {
|
|
47
|
-
|
|
47
|
+
if (typeof t !== 'function') return fallback;
|
|
48
|
+
const translated = t(key, params);
|
|
49
|
+
return translated === key ? fallback : translated;
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
function createTaskDraftChecklist(metrics, t = null) {
|
|
@@ -20,6 +20,7 @@ import { createOpenclawEditingMethods } from './app.methods.openclaw-editing.mjs
|
|
|
20
20
|
import { createOpenclawPersistMethods } from './app.methods.openclaw-persist.mjs';
|
|
21
21
|
import { createProvidersMethods } from './app.methods.providers.mjs';
|
|
22
22
|
import { createRuntimeMethods } from './app.methods.runtime.mjs';
|
|
23
|
+
import { createToolConfigPermissionMethods } from './app.methods.tool-config-permissions.mjs';
|
|
23
24
|
import { createTaskOrchestrationMethods } from './app.methods.task-orchestration.mjs';
|
|
24
25
|
import { createSessionActionMethods } from './app.methods.session-actions.mjs';
|
|
25
26
|
import { createSessionBrowserMethods } from './app.methods.session-browser.mjs';
|
|
@@ -81,6 +82,7 @@ export function createAppMethods() {
|
|
|
81
82
|
...createAgentsMethods({ api, apiWithMeta }),
|
|
82
83
|
...createProvidersMethods({ api }),
|
|
83
84
|
...createClaudeConfigMethods({ api }),
|
|
85
|
+
...createToolConfigPermissionMethods({ api }),
|
|
84
86
|
...createOpenclawCoreMethods(),
|
|
85
87
|
...createOpenclawEditingMethods(),
|
|
86
88
|
...createOpenclawPersistMethods({
|
|
@@ -120,6 +120,12 @@ export function createStartupClaudeMethods(options = {}) {
|
|
|
120
120
|
: String(defaultModelAutoCompactTokenLimit);
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
|
+
if (statusRes.toolConfigPermissions && typeof statusRes.toolConfigPermissions === 'object') {
|
|
124
|
+
this.toolConfigPermissions = {
|
|
125
|
+
codex: statusRes.toolConfigPermissions.codex === true,
|
|
126
|
+
claude: statusRes.toolConfigPermissions.claude === true
|
|
127
|
+
};
|
|
128
|
+
}
|
|
123
129
|
this.providersList = listRes.providers;
|
|
124
130
|
if (typeof this.loadLocalBridgeExcluded === 'function') { this.loadLocalBridgeExcluded(); }
|
|
125
131
|
if (typeof this.loadClaudeLocalBridgeStatus === 'function') { this.loadClaudeLocalBridgeStatus(); }
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export function createToolConfigPermissionMethods(options = {}) {
|
|
2
|
+
const { api } = options;
|
|
3
|
+
|
|
4
|
+
function normalizeTarget(value) {
|
|
5
|
+
const target = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
6
|
+
return target === 'codex' || target === 'claude' ? target : '';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function normalizePermissions(value) {
|
|
10
|
+
const source = value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
11
|
+
return {
|
|
12
|
+
codex: source.codex === true,
|
|
13
|
+
claude: source.claude === true
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
isToolConfigWriteAllowed(target) {
|
|
19
|
+
const normalizedTarget = normalizeTarget(target);
|
|
20
|
+
if (!normalizedTarget) return false;
|
|
21
|
+
return normalizePermissions(this.toolConfigPermissions)[normalizedTarget] === true;
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
toolConfigPermissionStatusLabel(target) {
|
|
25
|
+
return this.isToolConfigWriteAllowed(target)
|
|
26
|
+
? this.t('toolConfig.allow')
|
|
27
|
+
: this.t('toolConfig.viewOnly');
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
async setToolConfigPermission(target, allowWrite) {
|
|
31
|
+
const normalizedTarget = normalizeTarget(target);
|
|
32
|
+
if (!normalizedTarget || this.toolConfigPermissionSaving[normalizedTarget]) return;
|
|
33
|
+
|
|
34
|
+
const nextAllowWrite = allowWrite === true;
|
|
35
|
+
const previous = normalizePermissions(this.toolConfigPermissions);
|
|
36
|
+
if (previous[normalizedTarget] === nextAllowWrite) return;
|
|
37
|
+
|
|
38
|
+
if (nextAllowWrite) {
|
|
39
|
+
const confirmed = await this.requestConfirmDialog({
|
|
40
|
+
title: this.t('toolConfig.confirmTitle'),
|
|
41
|
+
message: this.t(`toolConfig.${normalizedTarget}.confirmMessage`),
|
|
42
|
+
confirmText: this.t('toolConfig.confirmAllow'),
|
|
43
|
+
cancelText: this.t('confirm.cancel'),
|
|
44
|
+
danger: true
|
|
45
|
+
});
|
|
46
|
+
if (!confirmed) {
|
|
47
|
+
this.toolConfigPermissions = { ...previous };
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
this.toolConfigPermissionSaving = {
|
|
53
|
+
...this.toolConfigPermissionSaving,
|
|
54
|
+
[normalizedTarget]: true
|
|
55
|
+
};
|
|
56
|
+
try {
|
|
57
|
+
const res = await api('set-tool-config-permission', {
|
|
58
|
+
target: normalizedTarget,
|
|
59
|
+
allowWrite: nextAllowWrite
|
|
60
|
+
});
|
|
61
|
+
if (res && res.error) {
|
|
62
|
+
this.toolConfigPermissions = { ...previous };
|
|
63
|
+
this.showMessage(res.error, 'error');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
this.toolConfigPermissions = normalizePermissions(res && res.permissions);
|
|
67
|
+
this.showMessage(
|
|
68
|
+
nextAllowWrite
|
|
69
|
+
? this.t('toolConfig.allowToast')
|
|
70
|
+
: this.t('toolConfig.viewOnlyToast'),
|
|
71
|
+
'success'
|
|
72
|
+
);
|
|
73
|
+
try {
|
|
74
|
+
await this.loadAll({ preserveLoading: true });
|
|
75
|
+
} catch (_) {}
|
|
76
|
+
} catch (_) {
|
|
77
|
+
this.toolConfigPermissions = { ...previous };
|
|
78
|
+
this.showMessage(this.t('toolConfig.saveFailed'), 'error');
|
|
79
|
+
} finally {
|
|
80
|
+
this.toolConfigPermissionSaving = {
|
|
81
|
+
...this.toolConfigPermissionSaving,
|
|
82
|
+
[normalizedTarget]: false
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|