Skip to content

Commit 9c3a46e

Browse files
committed
chore: release v3.3.14-beta.3
1 parent f5c83ee commit 9c3a46e

File tree

7 files changed

+89
-26
lines changed

7 files changed

+89
-26
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ccmam",
3-
"version": "3.3.14-beta.2",
3+
"version": "3.3.14-beta.3",
44
"private": true,
55
"description": "Manage Codex, Claude Code, Gemini CLI, and MCP API service provider configurations",
66
"scripts": {

packages/aicoding/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@2ue/aicoding",
3-
"version": "3.3.14-beta.2",
3+
"version": "3.3.14-beta.3",
44
"description": "一键配置 GMN 到 Codex、OpenCode、OpenClaw",
55
"type": "module",
66
"bin": {

packages/cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ccman",
3-
"version": "3.3.14-beta.2",
3+
"version": "3.3.14-beta.3",
44
"type": "module",
55
"description": "Manage Codex, Claude Code, Gemini CLI, OpenCode, OpenClaw, and MCP API service provider configurations",
66
"main": "./dist/index.js",

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@ccman/core",
3-
"version": "3.3.14-beta.2",
3+
"version": "3.3.14-beta.3",
44
"type": "module",
55
"description": "Core business logic for ccman - Manage Codex, Claude Code, Gemini CLI, OpenCode, OpenClaw, and MCP configurations",
66
"main": "./dist/index.js",

packages/core/src/writers/openclaw.test.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ describe('OpenClaw Writer', () => {
6161
expect(provider.models?.[0]?.id).toBe('gpt-5.3-codex')
6262
})
6363

64-
it('should always overwrite target files instead of merging existing content', () => {
64+
it('should merge openclaw fields incrementally instead of full overwrite', () => {
6565
const configPath = getOpenClawConfigPath()
6666
const modelsPath = getOpenClawModelsPath()
6767

@@ -72,12 +72,23 @@ describe('OpenClaw Writer', () => {
7272
configPath,
7373
JSON.stringify(
7474
{
75+
meta: { keep: true },
76+
models: {
77+
mode: 'merge',
78+
providers: {
79+
legacy: {
80+
baseUrl: 'https://legacy.example.com/v1',
81+
apiKey: 'legacy-key',
82+
},
83+
},
84+
},
7585
agents: {
7686
defaults: {
7787
workspace: '/tmp/custom',
7888
model: {
7989
primary: 'legacy/model',
8090
},
91+
maxConcurrent: 8,
8192
},
8293
},
8394
customField: 'legacy-value',
@@ -111,9 +122,16 @@ describe('OpenClaw Writer', () => {
111122
const openclawConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8'))
112123
const modelsConfig = JSON.parse(fs.readFileSync(modelsPath, 'utf-8'))
113124

114-
expect(openclawConfig.customField).toBeUndefined()
115-
expect(modelsConfig.customField).toBeUndefined()
116-
expect(modelsConfig.providers?.legacy).toBeUndefined()
125+
expect(openclawConfig.customField).toBe('legacy-value')
126+
expect(openclawConfig.meta?.keep).toBe(true)
127+
expect(openclawConfig.models?.providers?.legacy?.baseUrl).toBe('https://legacy.example.com/v1')
128+
expect(openclawConfig.models?.providers?.GMN?.apiKey).toBe('sk-new-openclaw')
129+
expect(openclawConfig.agents?.defaults?.workspace).toBe('/tmp/custom')
130+
expect(openclawConfig.agents?.defaults?.maxConcurrent).toBe(8)
131+
expect(openclawConfig.agents?.defaults?.model?.primary).toBe('GMN/gpt-5.3-codex')
132+
133+
expect(modelsConfig.customField).toBe('legacy-models-value')
134+
expect(modelsConfig.providers?.legacy?.baseUrl).toBe('https://old.example.com')
117135
expect(modelsConfig.providers?.GMN?.apiKey).toBe('sk-new-openclaw')
118136
})
119137

packages/core/src/writers/openclaw.ts

Lines changed: 62 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,23 @@ import * as path from 'path'
33
import { fileURLToPath } from 'url'
44
import type { Provider } from '../tool-manager.js'
55
import { getOpenClawConfigPath, getOpenClawDir, getOpenClawModelsPath } from '../paths.js'
6-
import { ensureDir, writeJSON } from '../utils/file.js'
7-
import { replaceVariables } from '../utils/template.js'
6+
import { ensureDir, fileExists, readJSON, writeJSON } from '../utils/file.js'
7+
import { deepMerge, replaceVariables } from '../utils/template.js'
88

99
interface OpenClawModelsFile {
1010
providers?: Record<string, unknown>
1111
[key: string]: unknown
1212
}
1313

1414
interface OpenClawConfigFile {
15-
models?: {
16-
providers?: Record<string, unknown>
17-
[key: string]: unknown
18-
}
15+
models?: Record<string, unknown>
1916
agents?: {
2017
defaults?: {
2118
workspace?: string
19+
model?: {
20+
primary?: string
21+
[key: string]: unknown
22+
}
2223
[key: string]: unknown
2324
}
2425
[key: string]: unknown
@@ -157,12 +158,22 @@ function resolveProviderName(provider: Provider): string {
157158
return DEFAULT_PROVIDER_NAME
158159
}
159160

161+
function loadExistingJSON<T extends object>(filePath: string): T | null {
162+
if (!fileExists(filePath)) return null
163+
try {
164+
return readJSON<T>(filePath)
165+
} catch {
166+
return null
167+
}
168+
}
169+
160170
/**
161171
* 写入 OpenClaw 配置
162172
*
163173
* 策略:
164174
* 1. 模板优先 + 内置回退
165-
* 2. 每次切换 provider 直接覆盖写入 openclaw.json / models.json
175+
* 2. openclaw.json: models 增量覆盖 + agents 智能合并(强制切换 primary)
176+
* 3. models.json: providers 增量补充(仅更新当前 provider)
166177
* 3. 路径基于 HOME_DIR(通过 paths.ts 统一管理)
167178
*/
168179
export function writeOpenClawConfig(provider: Provider): void {
@@ -191,18 +202,52 @@ export function writeOpenClawConfig(provider: Provider): void {
191202

192203
const nextOpenClawConfig = replaceVariables(rawConfigTemplate, variables) as OpenClawConfigFile
193204
const nextModelsConfig = replaceVariables(rawModelsTemplate, variables) as OpenClawModelsFile
194-
const currentAgents = nextOpenClawConfig.agents || {}
195-
const defaults = currentAgents.defaults || {}
205+
const existingOpenClawConfig = loadExistingJSON<OpenClawConfigFile>(configPath) || {}
206+
const existingModelsConfig = loadExistingJSON<OpenClawModelsFile>(modelsPath) || {}
196207

197-
nextOpenClawConfig.agents = {
198-
...currentAgents,
199-
defaults: {
200-
...defaults,
201-
workspace: homeDir,
208+
const mergedConfigModels = deepMerge(
209+
existingOpenClawConfig.models || {},
210+
nextOpenClawConfig.models || {}
211+
)
212+
213+
const mergedAgents = deepMerge(
214+
nextOpenClawConfig.agents || {},
215+
existingOpenClawConfig.agents || {}
216+
)
217+
const mergedDefaults = mergedAgents.defaults || {}
218+
const mergedModel = mergedDefaults.model || {}
219+
const templatePrimary =
220+
nextOpenClawConfig.agents?.defaults?.model?.primary || `${providerName}/gpt-5.3-codex`
221+
const workspace =
222+
typeof mergedDefaults.workspace === 'string' && mergedDefaults.workspace.trim()
223+
? mergedDefaults.workspace
224+
: homeDir
225+
226+
const finalOpenClawConfig: OpenClawConfigFile = {
227+
...existingOpenClawConfig,
228+
models: mergedConfigModels,
229+
agents: {
230+
...mergedAgents,
231+
defaults: {
232+
...mergedDefaults,
233+
workspace,
234+
model: {
235+
...mergedModel,
236+
primary: templatePrimary,
237+
},
238+
},
202239
},
203240
}
204241

205-
// 直接覆盖写入(不读取/不合并现有文件)
206-
writeJSON(configPath, nextOpenClawConfig)
207-
writeJSON(modelsPath, nextModelsConfig)
242+
const mergedProviders = deepMerge(
243+
existingModelsConfig.providers || {},
244+
nextModelsConfig.providers || {}
245+
)
246+
const finalModelsConfig: OpenClawModelsFile = {
247+
...existingModelsConfig,
248+
providers: mergedProviders,
249+
}
250+
251+
writeJSON(configPath, finalOpenClawConfig)
252+
writeJSON(modelsPath, finalModelsConfig)
208253
}

packages/desktop/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@ccman/desktop",
3-
"version": "3.3.14-beta.2",
3+
"version": "3.3.14-beta.3",
44
"description": "ccman Desktop GUI application",
55
"main": "./dist/main/index.js",
66
"scripts": {

0 commit comments

Comments
 (0)