codexmate 0.0.45 → 0.0.48

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.
@@ -2,6 +2,19 @@ export const SESSION_TRASH_LIST_LIMIT = 500;
2
2
  export const SESSION_TRASH_PAGE_SIZE = 200;
3
3
  export const DEFAULT_MODEL_CONTEXT_WINDOW = 190000;
4
4
  export const DEFAULT_MODEL_AUTO_COMPACT_TOKEN_LIMIT = 185000;
5
+ export const OPENCODE_MODEL_CATALOG = Object.freeze({
6
+ anthropic: Object.freeze(['claude-4-sonnet', 'claude-4-opus', 'claude-3.7-sonnet', 'claude-3.5-sonnet']),
7
+ openai: Object.freeze(['gpt-4.1', 'gpt-4o', 'o4-mini', 'o3-mini']),
8
+ gemini: Object.freeze(['gemini-2.5-pro', 'gemini-2.5-flash', 'gemini-1.5-pro']),
9
+ groq: Object.freeze(['llama-3.3-70b-versatile', 'llama-3.1-8b-instant']),
10
+ openrouter: Object.freeze(['anthropic/claude-3.7-sonnet', 'openai/gpt-4.1', 'google/gemini-2.5-pro']),
11
+ copilot: Object.freeze(['gpt-4o', 'claude-3.7-sonnet']),
12
+ azure: Object.freeze(['gpt-4.1', 'gpt-4o']),
13
+ bedrock: Object.freeze(['anthropic.claude-3-7-sonnet-20250219-v1:0']),
14
+ vertexai: Object.freeze(['gemini-2.5-pro', 'gemini-2.5-flash']),
15
+ xai: Object.freeze(['grok-3', 'grok-3-mini']),
16
+ local: Object.freeze(['local.model'])
17
+ });
5
18
  export const DEFAULT_OPENCLAW_TEMPLATE = `{
6
19
  // OpenClaw config (JSON5)
7
20
  agent: {
@@ -750,9 +750,13 @@ export function createCodexConfigMethods(options = {}) {
750
750
  && this._configTemplateDiffPreviewRequestToken === requestToken
751
751
  && this.buildConfigTemplateDiffFingerprint() === requestFingerprint
752
752
  );
753
- const res = await api('preview-config-template-diff', {
754
- template: this.configTemplateContent
755
- });
753
+ const res = this.configTemplateContext === 'claude'
754
+ ? await api('preview-claude-settings-diff', {
755
+ content: this.configTemplateContent
756
+ })
757
+ : await api('preview-config-template-diff', {
758
+ template: this.configTemplateContent
759
+ });
756
760
  if (!shouldApply()) {
757
761
  return;
758
762
  }
@@ -7,6 +7,7 @@ import {
7
7
  DEFAULT_MODEL_AUTO_COMPACT_TOKEN_LIMIT,
8
8
  DEFAULT_MODEL_CONTEXT_WINDOW,
9
9
  DEFAULT_OPENCLAW_TEMPLATE,
10
+ OPENCODE_MODEL_CATALOG,
10
11
  SESSION_TRASH_LIST_LIMIT,
11
12
  SESSION_TRASH_PAGE_SIZE
12
13
  } from './app.constants.mjs';
@@ -18,6 +19,7 @@ import { createNavigationMethods } from './app.methods.navigation.mjs';
18
19
  import { createOpenclawCoreMethods } from './app.methods.openclaw-core.mjs';
19
20
  import { createOpenclawEditingMethods } from './app.methods.openclaw-editing.mjs';
20
21
  import { createOpenclawPersistMethods } from './app.methods.openclaw-persist.mjs';
22
+ import { createOpencodeConfigMethods } from './app.methods.opencode-config.mjs';
21
23
  import { createProvidersMethods } from './app.methods.providers.mjs';
22
24
  import { createRuntimeMethods } from './app.methods.runtime.mjs';
23
25
  import { createToolConfigPermissionMethods } from './app.methods.tool-config-permissions.mjs';
@@ -89,6 +91,10 @@ export function createAppMethods() {
89
91
  api,
90
92
  defaultOpenclawTemplate: DEFAULT_OPENCLAW_TEMPLATE
91
93
  }),
94
+ ...createOpencodeConfigMethods({
95
+ api,
96
+ modelCatalog: OPENCODE_MODEL_CATALOG
97
+ }),
92
98
  ...createInstallMethods({ api }),
93
99
  ...createRuntimeMethods({ api }),
94
100
  ...createTaskOrchestrationMethods({ api })
@@ -0,0 +1,228 @@
1
+ function normalizeOpencodeProviderName(value) {
2
+ const name = typeof value === 'string' ? value.trim().toLowerCase() : '';
3
+ return /^[a-z0-9_.-]+$/.test(name) ? name : '';
4
+ }
5
+
6
+ function normalizeOpencodeAgentName(value) {
7
+ const name = typeof value === 'string' ? value.trim() : '';
8
+ return /^[a-zA-Z0-9_.-]+$/.test(name) ? name : '';
9
+ }
10
+
11
+ function getOpencodeJsonParser() {
12
+ const root = typeof globalThis !== 'undefined' ? globalThis : {};
13
+ const json5 = root.JSON5 || (root.window && root.window.JSON5);
14
+ if (json5 && typeof json5.parse === 'function') {
15
+ return json5;
16
+ }
17
+ return JSON;
18
+ }
19
+
20
+ function summarizeOpencodeDraft(content) {
21
+ const parsed = getOpencodeJsonParser().parse(content || '{}');
22
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
23
+ throw new Error('OpenCode config must be a JSON/JSONC object');
24
+ }
25
+ return parsed;
26
+ }
27
+
28
+ export function createOpencodeConfigMethods(options = {}) {
29
+ const { api, modelCatalog = {} } = options;
30
+
31
+ return {
32
+ opencodeProviderCatalog() {
33
+ const configured = Array.isArray(this.opencodeProviders)
34
+ ? this.opencodeProviders.map(item => item && item.name).filter(Boolean)
35
+ : [];
36
+ const builtin = Object.keys(modelCatalog || {});
37
+ return [...new Set([...configured, ...builtin])].sort((a, b) => a.localeCompare(b));
38
+ },
39
+
40
+ opencodeModelCatalogForProvider(providerName = this.opencodeProvider) {
41
+ const provider = normalizeOpencodeProviderName(providerName);
42
+ const list = provider && Array.isArray(modelCatalog[provider]) ? modelCatalog[provider] : [];
43
+ const configured = Array.isArray(this.opencodeAgents)
44
+ ? this.opencodeAgents.map(item => item && item.model).filter(Boolean)
45
+ : [];
46
+ return [...new Set([...list, ...configured])];
47
+ },
48
+
49
+ fillOpencodeProvider(provider) {
50
+ const name = normalizeOpencodeProviderName(provider);
51
+ if (!name) return;
52
+ this.opencodeProvider = name;
53
+ const models = this.opencodeModelCatalogForProvider(name);
54
+ if (!this.opencodeModel || !models.includes(this.opencodeModel)) {
55
+ this.opencodeModel = models[0] || '';
56
+ }
57
+ const selectedProvider = Array.isArray(this.opencodeProviders)
58
+ ? this.opencodeProviders.find(item => normalizeOpencodeProviderName(item && item.name) === name)
59
+ : null;
60
+ this.opencodeProviderDisabled = !!(selectedProvider && selectedProvider.disabled);
61
+ },
62
+
63
+ refreshOpencodeSelectionFromSummary(res = {}) {
64
+ const providers = Array.isArray(res.providers) ? res.providers : [];
65
+ const agents = Array.isArray(res.agents) ? res.agents : [];
66
+ this.opencodeProviders = providers;
67
+ this.opencodeAgents = agents;
68
+ const currentAgent = normalizeOpencodeAgentName(res.currentAgent) || 'build';
69
+ this.opencodeAgent = currentAgent;
70
+ const currentModel = typeof res.currentModel === 'string' ? res.currentModel.trim() : '';
71
+ this.opencodeModel = currentModel;
72
+ if (typeof res.autoCompact === 'boolean') {
73
+ this.opencodeAutoCompact = res.autoCompact;
74
+ }
75
+ const enabledProvider = providers.find(item => item && item.disabled !== true && item.hasKey);
76
+ const firstProvider = providers.find(item => item && item.name);
77
+ this.opencodeProvider = normalizeOpencodeProviderName(res.currentProvider)
78
+ || normalizeOpencodeProviderName(enabledProvider && enabledProvider.name)
79
+ || normalizeOpencodeProviderName(firstProvider && firstProvider.name)
80
+ || normalizeOpencodeProviderName(this.opencodeProvider)
81
+ || 'anthropic';
82
+ const selectedProvider = providers.find(item => normalizeOpencodeProviderName(item && item.name) === this.opencodeProvider);
83
+ this.opencodeProviderDisabled = !!(selectedProvider && selectedProvider.disabled);
84
+ const models = this.opencodeModelCatalogForProvider(this.opencodeProvider);
85
+ if (!this.opencodeModel || (models.length && !models.includes(this.opencodeModel))) {
86
+ this.opencodeModel = models[0] || '';
87
+ }
88
+ },
89
+
90
+ async loadOpencodeConfig(options = {}) {
91
+ if (this.opencodeLoading) return;
92
+ this.opencodeLoading = true;
93
+ this.opencodeError = '';
94
+ try {
95
+ const res = await api('get-opencode-config');
96
+ if (res && res.error) {
97
+ this.opencodeError = res.error;
98
+ return;
99
+ }
100
+ this.opencodeContent = typeof res.content === 'string' ? res.content : '{}';
101
+ this.opencodeConfigPath = typeof res.targetPath === 'string' ? res.targetPath : '';
102
+ this.opencodeProviderStorePath = typeof res.providerStorePath === 'string' ? res.providerStorePath : '';
103
+ this.opencodeConfigExists = res.exists === true;
104
+ this.refreshOpencodeSelectionFromSummary(res || {});
105
+ if (options.toast === true) {
106
+ this.showMessage('OpenCode 配置已刷新', 'success');
107
+ }
108
+ } catch (e) {
109
+ this.opencodeError = e && e.message ? e.message : '读取 OpenCode 配置失败';
110
+ } finally {
111
+ this.opencodeLoading = false;
112
+ }
113
+ },
114
+
115
+ parseOpencodeImportContent(content, fileName = '') {
116
+ const raw = typeof content === 'string' ? content : '';
117
+ if (!raw.trim()) {
118
+ return { error: '导入文件为空' };
119
+ }
120
+ try {
121
+ const parsed = summarizeOpencodeDraft(raw);
122
+ const pretty = JSON.stringify(parsed, null, 2) + '\n';
123
+ return { content: pretty, fileName };
124
+ } catch (e) {
125
+ return { error: `OpenCode JSON/JSONC 解析失败: ${e.message}` };
126
+ }
127
+ },
128
+
129
+ async handleOpencodeImportChange(event) {
130
+ const input = event && event.target ? event.target : null;
131
+ const file = input && input.files && input.files[0] ? input.files[0] : null;
132
+ if (!file) return;
133
+ this.opencodeImportError = '';
134
+ try {
135
+ const content = await file.text();
136
+ const parsed = this.parseOpencodeImportContent(content, file.name || '');
137
+ if (parsed.error) {
138
+ this.opencodeImportError = parsed.error;
139
+ this.showMessage(parsed.error, 'error');
140
+ return;
141
+ }
142
+ this.opencodeContent = parsed.content;
143
+ this.opencodeImportFileName = parsed.fileName;
144
+ this.showMessage('已解析 OpenCode 配置,确认后可保存', 'success');
145
+ } catch (e) {
146
+ this.opencodeImportError = e && e.message ? e.message : '读取导入文件失败';
147
+ this.showMessage(this.opencodeImportError, 'error');
148
+ } finally {
149
+ if (input) input.value = '';
150
+ }
151
+ },
152
+
153
+ async saveOpencodeConfig() {
154
+ if (this.opencodeSaving) return;
155
+ if (!this.isToolConfigWriteAllowed('opencode')) {
156
+ this.showMessage('请先打开 OpenCode 写入开关', 'error');
157
+ return;
158
+ }
159
+ this.opencodeSaving = true;
160
+ this.opencodeError = '';
161
+ try {
162
+ const res = await api('apply-opencode-config', { content: this.opencodeContent });
163
+ if (res && res.error) {
164
+ this.opencodeError = res.error;
165
+ this.showMessage(res.error, 'error');
166
+ return;
167
+ }
168
+ this.opencodeConfigExists = true;
169
+ if (res && typeof res.targetPath === 'string') this.opencodeConfigPath = res.targetPath;
170
+ if (res && typeof res.providerStorePath === 'string') this.opencodeProviderStorePath = res.providerStorePath;
171
+ this.refreshOpencodeSelectionFromSummary(res || {});
172
+ this.showMessage('OpenCode 配置已保存', 'success');
173
+ await this.loadOpencodeConfig();
174
+ } catch (e) {
175
+ this.opencodeError = e && e.message ? e.message : '保存 OpenCode 配置失败';
176
+ this.showMessage(this.opencodeError, 'error');
177
+ } finally {
178
+ this.opencodeSaving = false;
179
+ }
180
+ },
181
+
182
+ async applyOpencodeSelection() {
183
+ if (this.opencodeApplying) return;
184
+ if (!this.isToolConfigWriteAllowed('opencode')) {
185
+ this.showMessage('请先打开 OpenCode 写入开关', 'error');
186
+ return;
187
+ }
188
+ const provider = normalizeOpencodeProviderName(this.opencodeProvider);
189
+ const model = typeof this.opencodeModel === 'string' ? this.opencodeModel.trim() : '';
190
+ if (!provider || !model) {
191
+ this.showMessage('请选择 OpenCode provider 和 model', 'error');
192
+ return;
193
+ }
194
+ this.opencodeApplying = true;
195
+ this.opencodeError = '';
196
+ try {
197
+ const res = await api('update-opencode-selection', {
198
+ provider,
199
+ model,
200
+ apiKey: this.opencodeApiKey,
201
+ agent: this.opencodeAgent || 'build',
202
+ applyToCoreAgents: this.opencodeApplyToCoreAgents === true,
203
+ disabled: this.opencodeProviderDisabled === true,
204
+ autoCompact: this.opencodeAutoCompact !== false,
205
+ maxTokens: this.opencodeMaxTokens,
206
+ reasoningEffort: this.opencodeReasoningEffort
207
+ });
208
+ if (res && res.error) {
209
+ this.opencodeError = res.error;
210
+ this.showMessage(res.error, 'error');
211
+ return;
212
+ }
213
+ if (res && typeof res.content === 'string') this.opencodeContent = res.content;
214
+ if (res && typeof res.targetPath === 'string') this.opencodeConfigPath = res.targetPath;
215
+ if (res && typeof res.providerStorePath === 'string') this.opencodeProviderStorePath = res.providerStorePath;
216
+ this.opencodeConfigExists = true;
217
+ this.opencodeApiKey = '';
218
+ this.refreshOpencodeSelectionFromSummary(res || {});
219
+ this.showMessage('OpenCode provider/model 已应用', 'success');
220
+ } catch (e) {
221
+ this.opencodeError = e && e.message ? e.message : '应用 OpenCode 配置失败';
222
+ this.showMessage(this.opencodeError, 'error');
223
+ } finally {
224
+ this.opencodeApplying = false;
225
+ }
226
+ }
227
+ };
228
+ }
@@ -125,7 +125,8 @@ export function createStartupClaudeMethods(options = {}) {
125
125
  if (statusRes.toolConfigPermissions && typeof statusRes.toolConfigPermissions === 'object') {
126
126
  this.toolConfigPermissions = {
127
127
  codex: statusRes.toolConfigPermissions.codex === true,
128
- claude: statusRes.toolConfigPermissions.claude === true
128
+ claude: statusRes.toolConfigPermissions.claude === true,
129
+ opencode: statusRes.toolConfigPermissions.opencode === true
129
130
  };
130
131
  try {
131
132
  localStorage.setItem('toolConfigPermissions', JSON.stringify(this.toolConfigPermissions));
@@ -134,6 +135,7 @@ export function createStartupClaudeMethods(options = {}) {
134
135
  this.providersList = listRes.providers;
135
136
  if (typeof this.loadLocalBridgeExcluded === 'function') { this.loadLocalBridgeExcluded(); }
136
137
  if (typeof this.loadClaudeLocalBridgeStatus === 'function') { this.loadClaudeLocalBridgeStatus(); }
138
+ if (typeof this.loadOpencodeConfig === 'function') { this.loadOpencodeConfig(); }
137
139
  if (statusRes.configReady === false) {
138
140
  this.showMessage('配置已加载', 'info');
139
141
  }
@@ -143,9 +145,9 @@ export function createStartupClaudeMethods(options = {}) {
143
145
  this.maybeShowStarPrompt();
144
146
  return true;
145
147
  } catch (e) {
146
- this.initError = e && e.message === 'timeout'
147
- ? '读取配置超时'
148
- : '连接失败: ' + (e && e.message ? e.message : '');
148
+ if (e && e.message !== 'timeout') {
149
+ this.initError = '连接失败: ' + (e.message || '');
150
+ }
149
151
  return false;
150
152
  } finally {
151
153
  if (!preserveLoading) {
@@ -3,14 +3,15 @@ export function createToolConfigPermissionMethods(options = {}) {
3
3
 
4
4
  function normalizeTarget(value) {
5
5
  const target = typeof value === 'string' ? value.trim().toLowerCase() : '';
6
- return target === 'codex' || target === 'claude' ? target : '';
6
+ return target === 'codex' || target === 'claude' || target === 'opencode' ? target : '';
7
7
  }
8
8
 
9
9
  function normalizePermissions(value) {
10
10
  const source = value && typeof value === 'object' && !Array.isArray(value) ? value : {};
11
11
  return {
12
12
  codex: source.codex === true,
13
- claude: source.claude === true
13
+ claude: source.claude === true,
14
+ opencode: source.opencode === true
14
15
  };
15
16
  }
16
17
 
@@ -10,7 +10,8 @@
10
10
  export const CONFIG_MODE_SET = new Set([
11
11
  ...Object.keys(PROVIDER_CONFIG_MODE_META),
12
12
  'claude',
13
- 'openclaw'
13
+ 'openclaw',
14
+ 'opencode'
14
15
  ]);
15
16
 
16
17
  export function getProviderConfigModeMeta(mode) {
@@ -61,6 +62,7 @@ export function createConfigModeComputed() {
61
62
  if (providerMeta) return providerMeta.label;
62
63
  if (this.configMode === 'claude') return 'Claude Code';
63
64
  if (this.configMode === 'openclaw') return 'OpenClaw';
65
+ if (this.configMode === 'opencode') return 'OpenCode';
64
66
  return '未选择';
65
67
  },
66
68
  inspectorCurrentConfigLabel() {
@@ -76,6 +78,10 @@ export function createConfigModeComputed() {
76
78
  const openclaw = typeof this.currentOpenclawConfig === 'string' ? this.currentOpenclawConfig.trim() : '';
77
79
  return openclaw || '未选择';
78
80
  }
81
+ if (this.configMode === 'opencode') {
82
+ const provider = typeof this.opencodeProvider === 'string' ? this.opencodeProvider.trim() : '';
83
+ return provider || '未选择';
84
+ }
79
85
  return '未选择';
80
86
  },
81
87
  inspectorCurrentModelLabel() {
@@ -93,6 +99,10 @@ export function createConfigModeComputed() {
93
99
  : '';
94
100
  return model || '按配置文件';
95
101
  }
102
+ if (this.configMode === 'opencode') {
103
+ const model = typeof this.opencodeModel === 'string' ? this.opencodeModel.trim() : '';
104
+ return model || '未选择';
105
+ }
96
106
  return '未选择';
97
107
  },
98
108
  inspectorTemplateStatus() {
@@ -118,6 +128,12 @@ export function createConfigModeComputed() {
118
128
  }
119
129
  return 'JSON5 可保存并应用';
120
130
  }
131
+ if (this.configMode === 'opencode') {
132
+ if (this.opencodeSaving || this.opencodeApplying) {
133
+ return 'OpenCode 保存/应用中';
134
+ }
135
+ return 'JSON 可编辑并应用';
136
+ }
121
137
  return '未选择';
122
138
  }
123
139
  };
@@ -1,6 +1,7 @@
1
1
  const en = Object.freeze({
2
2
  // Global
3
3
  'lang.zh': '中文',
4
+ 'lang.zh-tw': '繁體中文',
4
5
  'lang.en': 'English',
5
6
  'lang.vi': 'Vietnamese',
6
7
  'lang.label': 'Language',
@@ -65,6 +66,8 @@ const en = Object.freeze({
65
66
  'common.none': 'None',
66
67
  'common.configured': 'Configured',
67
68
  'common.notConfigured': 'Not configured',
69
+ 'common.enabled': 'Enabled',
70
+ 'common.disabled': 'Disabled',
68
71
  'cli.missing.title': '{name} CLI not installed',
69
72
  'cli.missing.subtitle': 'Install {name} CLI before using this page.',
70
73
  'cli.missing.openDocs': 'Open install guide',
@@ -125,6 +128,7 @@ const en = Object.freeze({
125
128
  'tab.config.codex': 'Codex',
126
129
  'tab.config.claude': 'Claude',
127
130
  'tab.config.openclaw': 'OpenClaw',
131
+ 'tab.config.opencode': 'OpenCode',
128
132
  'tab.sessions': 'Sessions',
129
133
  'tab.usage': 'Usage',
130
134
  'tab.orchestration': 'Tasks',
@@ -166,6 +170,8 @@ const en = Object.freeze({
166
170
  'side.config.claude.meta': 'Claude Settings',
167
171
  'side.config.openclaw': 'OpenClaw',
168
172
  'side.config.openclaw.meta': 'JSON5 / AGENTS',
173
+ 'side.config.opencode': 'OpenCode',
174
+ 'side.config.opencode.meta': 'Config / Provider',
169
175
  'side.sessions.browser': 'Session Browser',
170
176
  'side.sessions.browser.meta': 'Browse / Export / Cleanup',
171
177
  'side.prompts.agents': 'AGENTS.md',
@@ -480,6 +486,7 @@ const en = Object.freeze({
480
486
  'modal.configTemplate.mode.twoStep': 'Two-step confirm: preview diff, then apply.',
481
487
  'modal.configTemplate.mode.oneStep': 'One-step apply: write immediately.',
482
488
  'diff.title.configTemplate': 'Diff preview (config.toml)',
489
+ 'diff.title.claudeSettings': 'Diff preview (settings.json)',
483
490
  'diff.generating': 'Generating...',
484
491
  'diff.failed': 'Failed',
485
492
  'diff.noChanges': 'No changes detected',
@@ -805,6 +812,39 @@ const en = Object.freeze({
805
812
  'toolConfig.claude.lockedTitle': 'Claude providers are read-only',
806
813
  'toolConfig.claude.lockedDesc': 'Claude config will not be modified. Enable writes in this tab to add, apply, edit, or delete providers.',
807
814
  'toolConfig.claude.confirmMessage': 'After enabling this, apply actions in the Claude tab may write ~/.claude/settings.json and related Claude config.',
815
+ 'toolConfig.opencode.title': 'OpenCode config writes',
816
+ 'toolConfig.opencode.desc': 'Read-only by default; enable to write OpenCode config.',
817
+ 'toolConfig.opencode.lockedTitle': 'Read-only',
818
+ 'toolConfig.opencode.lockedDesc': 'Enable write access to save, import, or apply config.',
819
+ 'toolConfig.opencode.confirmMessage': 'After enabling this, actions in the OpenCode tab may write the XDG OpenCode config file (for example ~/.config/opencode/opencode.jsonc) or the file specified by OPENCODE_CONFIG.',
820
+ 'opencode.providerModel.title': 'OpenCode provider / model',
821
+ 'opencode.writeAria': 'OpenCode write access',
822
+ 'opencode.applySelection': 'Apply to OpenCode',
823
+ 'opencode.targetFile': 'Active OpenCode config: {path} · {status}',
824
+ 'opencode.providerStoreFile': 'Provider draft store: {path} (written to OpenCode on apply)',
825
+ 'opencode.field.agent': 'Agent',
826
+ 'opencode.field.apiKeyKeep': 'API key (leave blank to keep current key)',
827
+ 'opencode.field.maxTokens': 'maxTokens (optional)',
828
+ 'opencode.field.reasoningEffort': 'reasoningEffort (optional)',
829
+ 'opencode.option.keepUnchanged': 'Keep unchanged',
830
+ 'opencode.option.reasoningLow': 'low',
831
+ 'opencode.option.reasoningMedium': 'medium',
832
+ 'opencode.option.reasoningHigh': 'high',
833
+ 'opencode.applyToCoreAgents': 'Apply to all core agents',
834
+ 'opencode.enableAutoCompaction': 'Enable compaction.auto',
835
+ 'opencode.disableProvider': 'Disable this provider',
836
+ 'opencode.configFile.title': 'OpenCode config file',
837
+ 'opencode.importParse': 'Import / parse file',
838
+ 'opencode.saveConfig': 'Save config',
839
+ 'opencode.parsedFile': 'Parsed: {file}',
840
+ 'opencode.textarea.placeholder': 'Edit OpenCode config',
841
+ 'opencode.configFile.hint': 'Active config preview; drafts stored in separate Provider store.',
842
+ 'opencode.summary.title': 'Parsed summary',
843
+ 'opencode.summary.noApiKey': 'No API key configured',
844
+ 'opencode.summary.noModel': 'No model set',
845
+ 'opencode.summary.agentType': 'agent',
846
+ 'opencode.summary.sourceCodexMate': 'CodexMate store',
847
+ 'opencode.summary.sourceOpenCode': 'Active OpenCode',
808
848
  'config.providerTemplate.title': 'Provider presets',
809
849
  'config.models': 'Model',
810
850
  'config.modelLoading': 'Loading...',
@@ -1026,6 +1066,9 @@ const en = Object.freeze({
1026
1066
  'status.claudeConfig': 'Claude config',
1027
1067
  'status.claudeModel': 'Claude model',
1028
1068
  'status.openclawConfig': 'OpenClaw config',
1069
+ 'status.opencodeProvider': 'OpenCode provider',
1070
+ 'status.opencodeModel': 'OpenCode model',
1071
+ 'status.opencodeConfig': 'OpenCode config',
1029
1072
  'status.workspaceFile': 'Workspace file',
1030
1073
  'status.configMode': 'Config mode',
1031
1074
  'status.currentProvider': 'Current provider',
@@ -1168,6 +1211,10 @@ const en = Object.freeze({
1168
1211
  'claude.model': 'Model',
1169
1212
  'claude.model.placeholder': 'e.g. claude-3-7-sonnet',
1170
1213
  'claude.model.hint': 'Model changes are saved and applied to the current config automatically.',
1214
+ 'claude.model.haiku': 'Haiku Model',
1215
+ 'claude.model.sonnet': 'Sonnet Model',
1216
+ 'claude.model.opus': 'Opus Model',
1217
+ 'claude.model.sub.placeholder': 'Defaults to the main model if left empty',
1171
1218
  'claude.targetApi.label': 'Target API',
1172
1219
  'claude.targetApi.responses': 'Anthropic',
1173
1220
  'claude.targetApi.chatCompletions': 'OpenAI Chat Completions (/v1/chat/completions)',
@@ -2,6 +2,7 @@ const ja = Object.freeze({
2
2
 
3
3
  // Global
4
4
  'lang.zh': '中国語',
5
+ 'lang.zh-tw': '繁體中文',
5
6
  'lang.en': 'English',
6
7
  'lang.vi': 'ベトナム語',
7
8
  'lang.label': '言語',
@@ -66,6 +67,8 @@ const ja = Object.freeze({
66
67
  'common.none': 'なし',
67
68
  'common.configured': '設定済み',
68
69
  'common.notConfigured': '未設定',
70
+ 'common.enabled': '有効',
71
+ 'common.disabled': '無効',
69
72
  'cli.missing.title': '{name} CLI がインストールされていません',
70
73
  'cli.missing.subtitle': '{name} CLI をインストールしてからこのページをご利用ください。',
71
74
  'cli.missing.openDocs': 'インストールガイドを開く',
@@ -126,6 +129,7 @@ const ja = Object.freeze({
126
129
  'tab.config.codex': 'Codex',
127
130
  'tab.config.claude': 'Claude',
128
131
  'tab.config.openclaw': 'OpenClaw',
132
+ 'tab.config.opencode': 'OpenCode',
129
133
  'tab.sessions': 'セッション',
130
134
  'tab.usage': '使用量',
131
135
  'tab.orchestration': 'タスク',
@@ -167,6 +171,8 @@ const ja = Object.freeze({
167
171
  'side.config.claude.meta': 'Claude Settings',
168
172
  'side.config.openclaw': 'OpenClaw',
169
173
  'side.config.openclaw.meta': 'JSON5 / AGENTS',
174
+ 'side.config.opencode': 'OpenCode',
175
+ 'side.config.opencode.meta': 'Config / Provider',
170
176
  'side.sessions.browser': 'セッション閲覧',
171
177
  'side.sessions.browser.meta': '閲覧 / エクスポート / クリーンアップ',
172
178
  'side.prompts.agents': 'AGENTS.md',
@@ -482,6 +488,7 @@ const ja = Object.freeze({
482
488
  'modal.configTemplate.mode.twoStep': '二段階確認:先に差分をプレビューし、その後適用します。',
483
489
  'modal.configTemplate.mode.oneStep': '一段階適用:「適用」をクリックすると直接書き込みます。',
484
490
  'diff.title.configTemplate': '差分プレビュー(config.toml)',
491
+ 'diff.title.claudeSettings': '差分プレビュー(settings.json)',
485
492
  'diff.generating': '生成中...',
486
493
  'diff.failed': '生成失敗',
487
494
  'diff.noChanges': '変更が検出されませんでした',
@@ -794,6 +801,39 @@ const ja = Object.freeze({
794
801
  'toolConfig.claude.lockedTitle': 'Claude プロバイダーは読み取り専用です',
795
802
  'toolConfig.claude.lockedDesc': 'Claude 設定には書き込みません。追加・適用・編集・削除するには、このタブの書き込みを有効化してください。',
796
803
  'toolConfig.claude.confirmMessage': '有効化すると、Claude タブ内の適用操作が ~/.claude/settings.json などの Claude 設定を書き込みます。',
804
+ 'toolConfig.opencode.title': 'OpenCode 設定の書き込み',
805
+ 'toolConfig.opencode.desc': 'デフォルトは読み取り専用。有効化すると OpenCode 設定ファイルに書き込みます。',
806
+ 'toolConfig.opencode.lockedTitle': '読み取り専用',
807
+ 'toolConfig.opencode.lockedDesc': '書き込み権限を有効化すると、保存・インポート・設定の適用ができます。',
808
+ 'toolConfig.opencode.confirmMessage': '有効化すると、OpenCode タブ内の操作が XDG OpenCode 設定ファイル(例: ~/.config/opencode/opencode.jsonc)または OPENCODE_CONFIG 指定ファイルを書き込みます。',
809
+ 'opencode.providerModel.title': 'OpenCode provider / model',
810
+ 'opencode.writeAria': 'OpenCode 書き込み',
811
+ 'opencode.applySelection': 'OpenCode に適用',
812
+ 'opencode.targetFile': '有効な OpenCode 設定: {path} · {status}',
813
+ 'opencode.providerStoreFile': 'プロバイダ下書きストア:{path}(適用時に OpenCode へ書き込み)',
814
+ 'opencode.field.agent': 'Agent',
815
+ 'opencode.field.apiKeyKeep': 'API Key(空なら既存 key を保持)',
816
+ 'opencode.field.maxTokens': 'maxTokens(任意)',
817
+ 'opencode.field.reasoningEffort': 'reasoningEffort(任意)',
818
+ 'opencode.option.keepUnchanged': '変更しない',
819
+ 'opencode.option.reasoningLow': 'low',
820
+ 'opencode.option.reasoningMedium': 'medium',
821
+ 'opencode.option.reasoningHigh': 'high',
822
+ 'opencode.applyToCoreAgents': 'すべてのコア agent に適用',
823
+ 'opencode.enableAutoCompaction': 'compaction.auto を有効化',
824
+ 'opencode.disableProvider': 'この provider を無効化',
825
+ 'opencode.configFile.title': 'OpenCode 設定ファイル',
826
+ 'opencode.importParse': 'ファイルをインポート/解析',
827
+ 'opencode.saveConfig': '設定を保存',
828
+ 'opencode.parsedFile': '解析済み: {file}',
829
+ 'opencode.textarea.placeholder': 'OpenCode 設定を編集',
830
+ 'opencode.configFile.hint': '有効設定のプレビュー。下書きは個別のプロバイダストアに保存されます。',
831
+ 'opencode.summary.title': '解析サマリー',
832
+ 'opencode.summary.noApiKey': 'API Key 未設定',
833
+ 'opencode.summary.noModel': 'model 未設定',
834
+ 'opencode.summary.agentType': 'agent',
835
+ 'opencode.summary.sourceCodexMate': 'CodexMate store',
836
+ 'opencode.summary.sourceOpenCode': 'Active OpenCode',
797
837
  'config.providerTemplate.title': 'プリセットプロバイダー',
798
838
  'config.models': 'モデル',
799
839
  'config.modelLoading': '読み込み中...',
@@ -1015,6 +1055,9 @@ const ja = Object.freeze({
1015
1055
  'status.claudeConfig': 'Claude 設定',
1016
1056
  'status.claudeModel': 'Claude モデル',
1017
1057
  'status.openclawConfig': 'OpenClaw 設定',
1058
+ 'status.opencodeProvider': 'OpenCode Provider',
1059
+ 'status.opencodeModel': 'OpenCode モデル',
1060
+ 'status.opencodeConfig': 'OpenCode 設定',
1018
1061
  'status.workspaceFile': 'ワークスペースファイル',
1019
1062
  'status.configMode': '設定モード',
1020
1063
  'status.currentProvider': '現在の Provider',
@@ -1161,6 +1204,10 @@ const ja = Object.freeze({
1161
1204
  'claude.model': 'モデル',
1162
1205
  'claude.model.placeholder': '例: claude-3-7-sonnet',
1163
1206
  'claude.model.hint': 'モデル変更後は自動保存され、現在の設定に適用されます。',
1207
+ 'claude.model.haiku': 'Haiku モデル',
1208
+ 'claude.model.sonnet': 'Sonnet モデル',
1209
+ 'claude.model.opus': 'Opus モデル',
1210
+ 'claude.model.sub.placeholder': '空欄の場合、メインモデルに従います',
1164
1211
  'claude.targetApi.label': 'ターゲット API',
1165
1212
  'claude.targetApi.responses': 'Anthropic',
1166
1213
  'claude.targetApi.chatCompletions': 'OpenAI Chat Completions (/v1/chat/completions)',