codexmate 0.0.42 → 0.0.44
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 +2 -0
- package/README.zh.md +2 -0
- package/cli/claude-proxy.js +611 -14
- package/cli/update.js +77 -7
- package/cli.js +191 -23
- package/package.json +2 -2
- package/web-ui/app.js +39 -10
- package/web-ui/logic.claude.mjs +65 -2
- package/web-ui/logic.runtime.mjs +0 -7
- package/web-ui/modules/app.methods.agents.mjs +6 -6
- package/web-ui/modules/app.methods.claude-config.mjs +65 -49
- package/web-ui/modules/app.methods.codex-config.mjs +10 -10
- package/web-ui/modules/app.methods.index.mjs +1 -1
- package/web-ui/modules/app.methods.install.mjs +129 -1
- package/web-ui/modules/app.methods.openclaw-persist.mjs +1 -1
- package/web-ui/modules/app.methods.providers.mjs +17 -16
- package/web-ui/modules/app.methods.runtime.mjs +27 -21
- package/web-ui/modules/app.methods.session-actions.mjs +25 -20
- package/web-ui/modules/app.methods.session-timeline.mjs +0 -1
- package/web-ui/modules/app.methods.startup-claude.mjs +29 -3
- package/web-ui/modules/app.methods.tool-config-permissions.mjs +3 -0
- package/web-ui/modules/i18n/locales/en.mjs +65 -1
- package/web-ui/modules/i18n/locales/ja.mjs +65 -1
- package/web-ui/modules/i18n/locales/vi.mjs +77 -2
- package/web-ui/modules/i18n/locales/zh.mjs +66 -2
- package/web-ui/partials/index/layout-header.html +24 -0
- package/web-ui/partials/index/modals-basic.html +18 -1
- package/web-ui/partials/index/panel-config-claude.html +5 -2
- package/web-ui/partials/index/panel-config-codex.html +2 -1
- package/web-ui/res/web-ui-render.precompiled.js +115 -22
- package/web-ui/styles/controls-forms.css +5 -5
- package/web-ui/styles/layout-shell.css +145 -0
- package/web-ui/styles/responsive.css +12 -0
- package/web-ui/styles/titles-cards.css +10 -4
package/cli/update.js
CHANGED
|
@@ -64,26 +64,92 @@ async function cmdToolUpdate(args = []) {
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
async function fetchLatestVersion() {
|
|
67
|
+
async function fetchLatestVersion(options = {}) {
|
|
68
68
|
return new Promise((resolve, reject) => {
|
|
69
|
+
const timeoutMs = Number.isFinite(Number(options.timeoutMs))
|
|
70
|
+
? Math.max(0, Number(options.timeoutMs))
|
|
71
|
+
: 5000;
|
|
69
72
|
const url = 'https://registry.npmjs.org/codexmate/latest';
|
|
70
|
-
|
|
73
|
+
let settled = false;
|
|
74
|
+
const finish = (fn, value) => {
|
|
75
|
+
if (settled) return;
|
|
76
|
+
settled = true;
|
|
77
|
+
fn(value);
|
|
78
|
+
};
|
|
79
|
+
const req = https.get(url, (res) => {
|
|
71
80
|
let data = '';
|
|
72
81
|
res.on('data', (chunk) => { data += chunk; });
|
|
73
82
|
res.on('end', () => {
|
|
74
83
|
try {
|
|
84
|
+
if (res.statusCode && (res.statusCode < 200 || res.statusCode >= 300)) {
|
|
85
|
+
finish(reject, new Error(`NPM registry returned ${res.statusCode}`));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
75
88
|
const json = JSON.parse(data);
|
|
76
|
-
resolve
|
|
89
|
+
finish(resolve, json.version || '');
|
|
77
90
|
} catch (e) {
|
|
78
|
-
reject
|
|
91
|
+
finish(reject, new Error('解析 NPM 响应失败'));
|
|
79
92
|
}
|
|
80
93
|
});
|
|
81
|
-
})
|
|
82
|
-
|
|
94
|
+
});
|
|
95
|
+
if (timeoutMs > 0) {
|
|
96
|
+
req.setTimeout(timeoutMs, () => {
|
|
97
|
+
req.destroy(new Error('获取 NPM 最新版本超时'));
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
req.on('error', (err) => {
|
|
101
|
+
finish(reject, err);
|
|
83
102
|
});
|
|
84
103
|
});
|
|
85
104
|
}
|
|
86
105
|
|
|
106
|
+
function normalizePackageVersion(value) {
|
|
107
|
+
const normalized = typeof value === 'string' ? value.trim().replace(/^v/i, '') : '';
|
|
108
|
+
return /^\d+(?:\.\d+){0,2}(?:[-+][0-9A-Za-z.-]+)?$/.test(normalized) ? normalized : '';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function comparePackageVersions(left, right) {
|
|
112
|
+
const normalizeParts = (value) => {
|
|
113
|
+
const normalized = normalizePackageVersion(value);
|
|
114
|
+
if (!normalized) return null;
|
|
115
|
+
return normalized.split(/[+-]/)[0].split('.').map((part) => Number.parseInt(part, 10) || 0);
|
|
116
|
+
};
|
|
117
|
+
const a = normalizeParts(left);
|
|
118
|
+
const b = normalizeParts(right);
|
|
119
|
+
if (!a || !b) return 0;
|
|
120
|
+
for (let i = 0; i < 3; i += 1) {
|
|
121
|
+
const diff = (a[i] || 0) - (b[i] || 0);
|
|
122
|
+
if (diff < 0) return -1;
|
|
123
|
+
if (diff > 0) return 1;
|
|
124
|
+
}
|
|
125
|
+
return 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let latestVersionStatusCache = null;
|
|
129
|
+
|
|
130
|
+
async function fetchLatestVersionStatus(options = {}) {
|
|
131
|
+
const currentVersion = normalizePackageVersion(options.currentVersion) || String(options.currentVersion || '');
|
|
132
|
+
const timeoutMs = Number.isFinite(Number(options.timeoutMs)) ? Number(options.timeoutMs) : 5000;
|
|
133
|
+
const cacheTtlMs = Number.isFinite(Number(options.cacheTtlMs)) ? Math.max(0, Number(options.cacheTtlMs)) : 10 * 60 * 1000;
|
|
134
|
+
const now = typeof options.now === 'function' ? options.now() : Date.now();
|
|
135
|
+
if (latestVersionStatusCache && cacheTtlMs > 0 && now - latestVersionStatusCache.checkedAtMs < cacheTtlMs) {
|
|
136
|
+
return { ...latestVersionStatusCache.payload, cached: true };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const latestVersionRaw = await fetchLatestVersion({ timeoutMs });
|
|
140
|
+
const latestVersion = normalizePackageVersion(latestVersionRaw) || String(latestVersionRaw || '');
|
|
141
|
+
const payload = {
|
|
142
|
+
currentVersion,
|
|
143
|
+
latestVersion,
|
|
144
|
+
updateAvailable: !!currentVersion && !!latestVersion && comparePackageVersions(currentVersion, latestVersion) < 0,
|
|
145
|
+
source: 'npm',
|
|
146
|
+
checkedAt: new Date(now).toISOString(),
|
|
147
|
+
cached: false
|
|
148
|
+
};
|
|
149
|
+
latestVersionStatusCache = { checkedAtMs: now, payload };
|
|
150
|
+
return payload;
|
|
151
|
+
}
|
|
152
|
+
|
|
87
153
|
function detectInstallMethod() {
|
|
88
154
|
const cliPath = path.resolve(__dirname, '..');
|
|
89
155
|
|
|
@@ -167,5 +233,9 @@ function updateViaStandalone(version) {
|
|
|
167
233
|
}
|
|
168
234
|
|
|
169
235
|
module.exports = {
|
|
170
|
-
cmdToolUpdate
|
|
236
|
+
cmdToolUpdate,
|
|
237
|
+
fetchLatestVersion,
|
|
238
|
+
fetchLatestVersionStatus,
|
|
239
|
+
normalizePackageVersion,
|
|
240
|
+
comparePackageVersions
|
|
171
241
|
};
|
package/cli.js
CHANGED
|
@@ -148,7 +148,7 @@ const {
|
|
|
148
148
|
deleteCodexSkills
|
|
149
149
|
} = require('./cli/skills');
|
|
150
150
|
const { cmdImportSkills: cmdImportSkillsFromUrl } = require('./cli/import-skills-url');
|
|
151
|
-
const { cmdToolUpdate } = require('./cli/update');
|
|
151
|
+
const { cmdToolUpdate, fetchLatestVersionStatus } = require('./cli/update');
|
|
152
152
|
const {
|
|
153
153
|
getFileStatSafe,
|
|
154
154
|
isBootstrapLikeText,
|
|
@@ -291,7 +291,11 @@ const DEFAULT_BUILTIN_CLAUDE_PROXY_SETTINGS = Object.freeze({
|
|
|
291
291
|
host: '127.0.0.1',
|
|
292
292
|
port: 8328,
|
|
293
293
|
provider: '',
|
|
294
|
+
upstreamProviderName: '',
|
|
295
|
+
upstreamBaseUrl: '',
|
|
296
|
+
upstreamApiKey: '',
|
|
294
297
|
authSource: 'provider',
|
|
298
|
+
targetApi: 'responses',
|
|
295
299
|
timeoutMs: 30000
|
|
296
300
|
});
|
|
297
301
|
const CLI_INSTALL_TARGETS = Object.freeze([
|
|
@@ -5740,7 +5744,9 @@ const {
|
|
|
5740
5744
|
HTTPS_KEEP_ALIVE_AGENT,
|
|
5741
5745
|
readConfigOrVirtualDefault,
|
|
5742
5746
|
resolveBuiltinProxyProviderName,
|
|
5743
|
-
resolveAuthTokenFromCurrentProfile
|
|
5747
|
+
resolveAuthTokenFromCurrentProfile,
|
|
5748
|
+
OPENAI_BRIDGE_SETTINGS_FILE,
|
|
5749
|
+
resolveOpenaiBridgeUpstream
|
|
5744
5750
|
});
|
|
5745
5751
|
|
|
5746
5752
|
function applyBuiltinProxyProvider(params = {}) {
|
|
@@ -8082,15 +8088,17 @@ function buildClaudeSharePayload(config = {}) {
|
|
|
8082
8088
|
const apiKey = typeof config.apiKey === 'string' ? config.apiKey : '';
|
|
8083
8089
|
const baseUrl = typeof config.baseUrl === 'string' ? config.baseUrl : '';
|
|
8084
8090
|
const model = typeof config.model === 'string' ? config.model : '';
|
|
8091
|
+
const targetApi = normalizeClaudeTargetApi(config.targetApi);
|
|
8085
8092
|
|
|
8086
8093
|
if (!baseUrl) return { error: 'Claude Base URL 未设置' };
|
|
8087
|
-
if (!apiKey) return { error: 'Claude API 密钥未设置' };
|
|
8094
|
+
if (!apiKey && targetApi !== 'ollama') return { error: 'Claude API 密钥未设置' };
|
|
8088
8095
|
|
|
8089
8096
|
return {
|
|
8090
8097
|
payload: {
|
|
8091
8098
|
baseUrl: baseUrl.trim(),
|
|
8092
8099
|
apiKey: apiKey.trim(),
|
|
8093
|
-
model: (model && model.trim()) || DEFAULT_CLAUDE_MODEL
|
|
8100
|
+
model: (model && model.trim()) || DEFAULT_CLAUDE_MODEL,
|
|
8101
|
+
targetApi
|
|
8094
8102
|
}
|
|
8095
8103
|
};
|
|
8096
8104
|
}
|
|
@@ -9404,19 +9412,93 @@ function maskKey(key) {
|
|
|
9404
9412
|
return key.substring(0, 4) + '...' + key.substring(key.length - 4);
|
|
9405
9413
|
}
|
|
9406
9414
|
|
|
9415
|
+
function normalizeClaudeTargetApi(value) {
|
|
9416
|
+
const raw = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
9417
|
+
if (raw === 'chat_completions' || raw === 'chat-completions' || raw === 'chat/completions') {
|
|
9418
|
+
return 'chat_completions';
|
|
9419
|
+
}
|
|
9420
|
+
if (raw === 'ollama') {
|
|
9421
|
+
return 'ollama';
|
|
9422
|
+
}
|
|
9423
|
+
return 'responses';
|
|
9424
|
+
}
|
|
9425
|
+
|
|
9426
|
+
function resetBuiltinClaudeProxySavedSettingsToResponses() {
|
|
9427
|
+
const proxySettingsResult = readJsonObjectFromFile(BUILTIN_CLAUDE_PROXY_SETTINGS_FILE, DEFAULT_BUILTIN_CLAUDE_PROXY_SETTINGS);
|
|
9428
|
+
const proxySettings = proxySettingsResult.ok && proxySettingsResult.data && typeof proxySettingsResult.data === 'object' && !Array.isArray(proxySettingsResult.data)
|
|
9429
|
+
? proxySettingsResult.data
|
|
9430
|
+
: DEFAULT_BUILTIN_CLAUDE_PROXY_SETTINGS;
|
|
9431
|
+
writeJsonAtomic(BUILTIN_CLAUDE_PROXY_SETTINGS_FILE, {
|
|
9432
|
+
...DEFAULT_BUILTIN_CLAUDE_PROXY_SETTINGS,
|
|
9433
|
+
...proxySettings,
|
|
9434
|
+
enabled: false,
|
|
9435
|
+
targetApi: 'responses'
|
|
9436
|
+
});
|
|
9437
|
+
}
|
|
9438
|
+
|
|
9407
9439
|
// 应用到 Claude Code settings.json(跨平台)
|
|
9408
|
-
function applyToClaudeSettings(config = {}) {
|
|
9409
|
-
|
|
9440
|
+
async function applyToClaudeSettings(config = {}) {
|
|
9441
|
+
let proxyStarted = false;
|
|
9410
9442
|
try {
|
|
9443
|
+
assertToolConfigWriteAllowed('claude');
|
|
9411
9444
|
const apiKey = (config.apiKey || '').trim();
|
|
9412
|
-
|
|
9445
|
+
const targetApi = normalizeClaudeTargetApi(config.targetApi);
|
|
9446
|
+
if (!apiKey && targetApi !== 'ollama') {
|
|
9413
9447
|
return { success: false, mode: 'settings-file', error: '请先输入 API Key' };
|
|
9414
9448
|
}
|
|
9415
9449
|
|
|
9416
|
-
const
|
|
9450
|
+
const configuredBaseUrl = typeof config.baseUrl === 'string' ? config.baseUrl.trim() : '';
|
|
9451
|
+
const baseUrl = (configuredBaseUrl || (targetApi === 'ollama' ? 'http://127.0.0.1:11434' : 'https://open.bigmodel.cn/api/anthropic')).trim();
|
|
9417
9452
|
const model = (config.model || DEFAULT_CLAUDE_MODEL).trim();
|
|
9453
|
+
let settingsBaseUrl = baseUrl;
|
|
9454
|
+
let settingsApiKey = apiKey;
|
|
9455
|
+
let proxyResult = null;
|
|
9456
|
+
|
|
9457
|
+
if (targetApi === 'chat_completions' || targetApi === 'ollama') {
|
|
9458
|
+
const upstreamProviderName = typeof config.name === 'string' ? config.name.trim() : '';
|
|
9459
|
+
if (targetApi === 'chat_completions' && !configuredBaseUrl && !upstreamProviderName) {
|
|
9460
|
+
return {
|
|
9461
|
+
success: false,
|
|
9462
|
+
mode: 'claude-proxy',
|
|
9463
|
+
error: 'chat_completions 模式需要显式的上游 Base URL 或可解析的 provider 名称'
|
|
9464
|
+
};
|
|
9465
|
+
}
|
|
9466
|
+
await stopBuiltinClaudeProxyRuntime();
|
|
9467
|
+
const proxyToken = crypto.randomBytes(24).toString('hex');
|
|
9468
|
+
proxyResult = await startBuiltinClaudeProxyRuntime({
|
|
9469
|
+
enabled: true,
|
|
9470
|
+
host: DEFAULT_BUILTIN_CLAUDE_PROXY_SETTINGS.host,
|
|
9471
|
+
provider: upstreamProviderName,
|
|
9472
|
+
authSource: 'provider',
|
|
9473
|
+
targetApi,
|
|
9474
|
+
timeoutMs: DEFAULT_BUILTIN_CLAUDE_PROXY_SETTINGS.timeoutMs,
|
|
9475
|
+
upstreamProviderName,
|
|
9476
|
+
...(configuredBaseUrl ? { upstreamBaseUrl: configuredBaseUrl } : {}),
|
|
9477
|
+
upstreamApiKey: apiKey
|
|
9478
|
+
});
|
|
9479
|
+
if (!proxyResult || proxyResult.error || proxyResult.success === false || !proxyResult.listenUrl) {
|
|
9480
|
+
await stopBuiltinClaudeProxyRuntime();
|
|
9481
|
+
resetBuiltinClaudeProxySavedSettingsToResponses();
|
|
9482
|
+
return {
|
|
9483
|
+
success: false,
|
|
9484
|
+
mode: 'claude-proxy',
|
|
9485
|
+
error: (proxyResult && proxyResult.error) || '启动 Claude 兼容代理失败'
|
|
9486
|
+
};
|
|
9487
|
+
}
|
|
9488
|
+
proxyStarted = true;
|
|
9489
|
+
settingsBaseUrl = proxyResult.listenUrl;
|
|
9490
|
+
settingsApiKey = proxyToken;
|
|
9491
|
+
} else {
|
|
9492
|
+
await stopBuiltinClaudeProxyRuntime();
|
|
9493
|
+
resetBuiltinClaudeProxySavedSettingsToResponses();
|
|
9494
|
+
}
|
|
9495
|
+
|
|
9418
9496
|
const readResult = readJsonObjectFromFile(CLAUDE_SETTINGS_FILE, {});
|
|
9419
9497
|
if (!readResult.ok) {
|
|
9498
|
+
if (proxyStarted) {
|
|
9499
|
+
await stopBuiltinClaudeProxyRuntime();
|
|
9500
|
+
resetBuiltinClaudeProxySavedSettingsToResponses();
|
|
9501
|
+
}
|
|
9420
9502
|
return { success: false, mode: 'settings-file', error: readResult.error };
|
|
9421
9503
|
}
|
|
9422
9504
|
|
|
@@ -9427,8 +9509,8 @@ function applyToClaudeSettings(config = {}) {
|
|
|
9427
9509
|
|
|
9428
9510
|
const nextEnv = {
|
|
9429
9511
|
...currentEnv,
|
|
9430
|
-
ANTHROPIC_API_KEY:
|
|
9431
|
-
ANTHROPIC_BASE_URL:
|
|
9512
|
+
ANTHROPIC_API_KEY: settingsApiKey,
|
|
9513
|
+
ANTHROPIC_BASE_URL: settingsBaseUrl,
|
|
9432
9514
|
ANTHROPIC_MODEL: model
|
|
9433
9515
|
};
|
|
9434
9516
|
delete nextEnv.ANTHROPIC_AUTH_TOKEN;
|
|
@@ -9445,7 +9527,8 @@ function applyToClaudeSettings(config = {}) {
|
|
|
9445
9527
|
|
|
9446
9528
|
const result = {
|
|
9447
9529
|
success: true,
|
|
9448
|
-
mode: 'settings-file',
|
|
9530
|
+
mode: targetApi === 'responses' ? 'settings-file' : 'claude-proxy',
|
|
9531
|
+
targetApi,
|
|
9449
9532
|
targetPath: CLAUDE_SETTINGS_FILE,
|
|
9450
9533
|
updatedKeys: [
|
|
9451
9534
|
'env.ANTHROPIC_API_KEY',
|
|
@@ -9453,11 +9536,23 @@ function applyToClaudeSettings(config = {}) {
|
|
|
9453
9536
|
'env.ANTHROPIC_MODEL'
|
|
9454
9537
|
]
|
|
9455
9538
|
};
|
|
9539
|
+
if (proxyResult) {
|
|
9540
|
+
result.proxy = {
|
|
9541
|
+
running: true,
|
|
9542
|
+
listenUrl: proxyResult.listenUrl,
|
|
9543
|
+
upstreamProvider: proxyResult.upstreamProvider || '',
|
|
9544
|
+
mode: proxyResult.mode || (targetApi === 'ollama' ? 'anthropic-to-ollama' : 'anthropic-to-chat-completions')
|
|
9545
|
+
};
|
|
9546
|
+
}
|
|
9456
9547
|
if (backupPath) {
|
|
9457
9548
|
result.backupPath = backupPath;
|
|
9458
9549
|
}
|
|
9459
9550
|
return result;
|
|
9460
9551
|
} catch (e) {
|
|
9552
|
+
if (proxyStarted) {
|
|
9553
|
+
try { await stopBuiltinClaudeProxyRuntime(); } catch (_) {}
|
|
9554
|
+
try { resetBuiltinClaudeProxySavedSettingsToResponses(); } catch (_) {}
|
|
9555
|
+
}
|
|
9461
9556
|
return {
|
|
9462
9557
|
success: false,
|
|
9463
9558
|
mode: 'settings-file',
|
|
@@ -9570,6 +9665,40 @@ async function restoreCodexDir(payload) {
|
|
|
9570
9665
|
}
|
|
9571
9666
|
|
|
9572
9667
|
// CLI: 一行写入 Claude Code 配置
|
|
9668
|
+
function parseClaudeCommandArgs(argv = []) {
|
|
9669
|
+
const positionals = [];
|
|
9670
|
+
let targetApi = 'responses';
|
|
9671
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
9672
|
+
const token = String(argv[i] ?? '');
|
|
9673
|
+
if (token === '--target-api' || token === '--targetApi') {
|
|
9674
|
+
const nextValue = String(argv[i + 1] ?? '');
|
|
9675
|
+
if (!nextValue || nextValue.startsWith('--')) {
|
|
9676
|
+
throw new Error('错误: --target-api 需要一个值(responses、chat_completions 或 ollama)');
|
|
9677
|
+
}
|
|
9678
|
+
targetApi = normalizeClaudeTargetApi(nextValue);
|
|
9679
|
+
i += 1;
|
|
9680
|
+
continue;
|
|
9681
|
+
}
|
|
9682
|
+
positionals.push(token);
|
|
9683
|
+
}
|
|
9684
|
+
|
|
9685
|
+
const baseUrl = positionals[0];
|
|
9686
|
+
if (targetApi === 'ollama' && positionals.length === 2) {
|
|
9687
|
+
return {
|
|
9688
|
+
baseUrl,
|
|
9689
|
+
apiKey: '',
|
|
9690
|
+
model: positionals[1],
|
|
9691
|
+
targetApi
|
|
9692
|
+
};
|
|
9693
|
+
}
|
|
9694
|
+
return {
|
|
9695
|
+
baseUrl,
|
|
9696
|
+
apiKey: positionals[1],
|
|
9697
|
+
model: positionals[2],
|
|
9698
|
+
targetApi
|
|
9699
|
+
};
|
|
9700
|
+
}
|
|
9701
|
+
|
|
9573
9702
|
async function cmdClaude(args = []) {
|
|
9574
9703
|
const argv = Array.isArray(args) ? args : [];
|
|
9575
9704
|
// 无参数 → 代理启动
|
|
@@ -9577,7 +9706,7 @@ async function cmdClaude(args = []) {
|
|
|
9577
9706
|
return runProxyCommand('Claude', 'claude', [], '', { autoFlag: '--dangerously-skip-permissions' });
|
|
9578
9707
|
}
|
|
9579
9708
|
// 有参数 → 配置写入
|
|
9580
|
-
const
|
|
9709
|
+
const { baseUrl, apiKey, model, targetApi } = parseClaudeCommandArgs(argv);
|
|
9581
9710
|
const normalizedBaseUrl = typeof baseUrl === 'string' ? baseUrl.trim() : '';
|
|
9582
9711
|
const normalizedKey = typeof apiKey === 'string' ? apiKey.trim() : '';
|
|
9583
9712
|
const normalizedModel = typeof model === 'string' && model.trim()
|
|
@@ -9586,19 +9715,21 @@ async function cmdClaude(args = []) {
|
|
|
9586
9715
|
|
|
9587
9716
|
const silent = false;
|
|
9588
9717
|
|
|
9589
|
-
if (!normalizedBaseUrl || !normalizedKey) {
|
|
9718
|
+
if (!normalizedBaseUrl || (!normalizedKey && targetApi !== 'ollama')) {
|
|
9590
9719
|
if (!silent) {
|
|
9591
|
-
console.error('用法: codexmate claude <BaseURL> <API密钥> [模型]');
|
|
9720
|
+
console.error('用法: codexmate claude <BaseURL> <API密钥> [模型] [--target-api responses|chat_completions|ollama]');
|
|
9592
9721
|
console.log('\n示例:');
|
|
9593
9722
|
console.log(' codexmate claude https://open.bigmodel.cn/api/anthropic sk-ant-xxx glm-4.7');
|
|
9723
|
+
console.log(" codexmate claude http://127.0.0.1:11434 '' llama3.1:8b --target-api ollama");
|
|
9594
9724
|
}
|
|
9595
|
-
throw new Error('BaseURL 和 API 密钥必填');
|
|
9725
|
+
throw new Error(targetApi === 'ollama' ? 'BaseURL 必填' : 'BaseURL 和 API 密钥必填');
|
|
9596
9726
|
}
|
|
9597
9727
|
|
|
9598
|
-
const result = applyToClaudeSettings({
|
|
9728
|
+
const result = await applyToClaudeSettings({
|
|
9599
9729
|
baseUrl: normalizedBaseUrl,
|
|
9600
9730
|
apiKey: normalizedKey,
|
|
9601
|
-
model: normalizedModel
|
|
9731
|
+
model: normalizedModel,
|
|
9732
|
+
targetApi
|
|
9602
9733
|
});
|
|
9603
9734
|
|
|
9604
9735
|
if (!result || result.success === false) {
|
|
@@ -10461,6 +10592,7 @@ function assertRequestAuthorized(req, res) {
|
|
|
10461
10592
|
|
|
10462
10593
|
function isProtectedWebSurfacePath(requestPath) {
|
|
10463
10594
|
return requestPath === '/'
|
|
10595
|
+
|| requestPath === '/session'
|
|
10464
10596
|
|| requestPath === '/web-ui/index.html'
|
|
10465
10597
|
|| requestPath.startsWith('/web-ui/')
|
|
10466
10598
|
|| requestPath.startsWith('/res/');
|
|
@@ -11104,6 +11236,31 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
11104
11236
|
case 'install-status':
|
|
11105
11237
|
result = buildInstallStatusReport();
|
|
11106
11238
|
break;
|
|
11239
|
+
case 'version-status': {
|
|
11240
|
+
const currentVersion = (() => {
|
|
11241
|
+
try {
|
|
11242
|
+
const pkg = require('./package.json');
|
|
11243
|
+
return pkg && pkg.version ? pkg.version : '';
|
|
11244
|
+
} catch (_) {
|
|
11245
|
+
return '';
|
|
11246
|
+
}
|
|
11247
|
+
})();
|
|
11248
|
+
try {
|
|
11249
|
+
const force = !!(params && params.force);
|
|
11250
|
+
result = await fetchLatestVersionStatus({ currentVersion, timeoutMs: 2000, cacheTtlMs: force ? 0 : undefined });
|
|
11251
|
+
} catch (e) {
|
|
11252
|
+
result = {
|
|
11253
|
+
currentVersion,
|
|
11254
|
+
latestVersion: '',
|
|
11255
|
+
updateAvailable: false,
|
|
11256
|
+
source: 'npm',
|
|
11257
|
+
checkedAt: new Date().toISOString(),
|
|
11258
|
+
cached: false,
|
|
11259
|
+
error: e && e.message ? e.message : '获取最新版本失败'
|
|
11260
|
+
};
|
|
11261
|
+
}
|
|
11262
|
+
break;
|
|
11263
|
+
}
|
|
11107
11264
|
case 'list':
|
|
11108
11265
|
result = buildMcpProviderListPayload();
|
|
11109
11266
|
break;
|
|
@@ -11296,7 +11453,7 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
11296
11453
|
result = applyClaudeSettingsRaw(params || {});
|
|
11297
11454
|
break;
|
|
11298
11455
|
case 'apply-claude-config':
|
|
11299
|
-
result = applyToClaudeSettings(params.config);
|
|
11456
|
+
result = await applyToClaudeSettings(params.config);
|
|
11300
11457
|
if (result && !result.error) {
|
|
11301
11458
|
const cfgName = (params && params.config && typeof params.config.name === 'string') ? params.config.name : '';
|
|
11302
11459
|
const cfgFrom = (params && typeof params.previousName === 'string') ? params.previousName : '';
|
|
@@ -11889,8 +12046,8 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
11889
12046
|
});
|
|
11890
12047
|
fs.createReadStream(filePath).pipe(res);
|
|
11891
12048
|
} else {
|
|
11892
|
-
//
|
|
11893
|
-
if (requestPath === '/') {
|
|
12049
|
+
// Serve the SPA shell for routable entry points. Keep /web-ui as 404.
|
|
12050
|
+
if (requestPath === '/' || requestPath === '/session') {
|
|
11894
12051
|
try {
|
|
11895
12052
|
const html = readBundledWebUiHtml(htmlPath);
|
|
11896
12053
|
res.writeHead(200, {
|
|
@@ -15893,9 +16050,20 @@ function createMcpTools(options = {}) {
|
|
|
15893
16050
|
properties: {
|
|
15894
16051
|
apiKey: { type: 'string' },
|
|
15895
16052
|
baseUrl: { type: 'string' },
|
|
15896
|
-
model: { type: 'string' }
|
|
16053
|
+
model: { type: 'string' },
|
|
16054
|
+
name: { type: 'string' },
|
|
16055
|
+
targetApi: { type: 'string' }
|
|
15897
16056
|
},
|
|
15898
|
-
|
|
16057
|
+
allOf: [{
|
|
16058
|
+
if: {
|
|
16059
|
+
not: {
|
|
16060
|
+
type: 'object',
|
|
16061
|
+
properties: { targetApi: { type: 'string', pattern: '^[\\s]*[oO][lL][lL][aA][mM][aA][\\s]*$' } },
|
|
16062
|
+
required: ['targetApi']
|
|
16063
|
+
}
|
|
16064
|
+
},
|
|
16065
|
+
then: { required: ['apiKey'] }
|
|
16066
|
+
}],
|
|
15899
16067
|
additionalProperties: false
|
|
15900
16068
|
},
|
|
15901
16069
|
handler: async (args = {}) => applyToClaudeSettings(args || {})
|
|
@@ -16351,7 +16519,7 @@ function printMainHelp() {
|
|
|
16351
16519
|
console.log(' codexmate add <名称> <URL> [密钥] [--bridge <openai>]');
|
|
16352
16520
|
console.log(' codexmate delete <名称> 删除提供商');
|
|
16353
16521
|
console.log(' codexmate claude 等同于 claude --dangerously-skip-permissions');
|
|
16354
|
-
console.log(' codexmate claude <BaseURL> <API密钥> [模型] 写入 Claude Code 配置');
|
|
16522
|
+
console.log(' codexmate claude <BaseURL> <API密钥> [模型] [--target-api responses|chat_completions|ollama] 写入 Claude Code 配置');
|
|
16355
16523
|
console.log(' codexmate auth <list|import|switch|delete|status> 认证管理');
|
|
16356
16524
|
console.log(' codexmate add-model <模型> 添加模型');
|
|
16357
16525
|
console.log(' codexmate delete-model <模型> 删除模型');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codexmate",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.44",
|
|
4
4
|
"description": "Codex/Claude Code/OpenClaw 配置、会话与任务编排 CLI + Web 工具",
|
|
5
5
|
"main": "cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -46,7 +46,6 @@
|
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@iarna/toml": "^2.2.5",
|
|
49
|
-
"@vue/compiler-dom": "^3.5.30",
|
|
50
49
|
"json5": "^2.2.3",
|
|
51
50
|
"yauzl": "^3.2.1",
|
|
52
51
|
"zip-lib": "^1.2.1"
|
|
@@ -72,6 +71,7 @@
|
|
|
72
71
|
"author": "ymkiux",
|
|
73
72
|
"license": "Apache-2.0",
|
|
74
73
|
"devDependencies": {
|
|
74
|
+
"@vue/compiler-dom": "^3.5.30",
|
|
75
75
|
"vitepress": "^1.6.4"
|
|
76
76
|
}
|
|
77
77
|
}
|
package/web-ui/app.js
CHANGED
|
@@ -270,6 +270,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
270
270
|
installRegistryPreset: 'default',
|
|
271
271
|
installRegistryCustom: '',
|
|
272
272
|
installStatusTargets: null,
|
|
273
|
+
appLatestVersion: '',
|
|
274
|
+
appVersionStatusLoading: false,
|
|
275
|
+
appVersionStatusError: '',
|
|
276
|
+
appVersionStatusChecked: false,
|
|
277
|
+
appVersionStatusCheckedAt: '',
|
|
278
|
+
appVersionStatusSource: '',
|
|
273
279
|
newProvider: { name: '', url: '', key: '', model: '', useTransform: false },
|
|
274
280
|
resetConfigLoading: false,
|
|
275
281
|
editingProvider: { name: '', url: '', key: '', readOnly: false, nonEditable: false },
|
|
@@ -277,12 +283,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
277
283
|
currentClaudeConfig: '',
|
|
278
284
|
currentClaudeModel: '',
|
|
279
285
|
claudeCustomModelDraft: '',
|
|
280
|
-
editingConfig: { name: '', apiKey: '', baseUrl: '', model: '' },
|
|
286
|
+
editingConfig: { name: '', apiKey: '', baseUrl: '', model: '', targetApi: 'responses' },
|
|
281
287
|
claudeConfigs: {
|
|
282
288
|
'智谱GLM': {
|
|
283
289
|
apiKey: '',
|
|
284
290
|
baseUrl: 'https://open.bigmodel.cn/api/anthropic',
|
|
285
291
|
model: 'glm-4.7',
|
|
292
|
+
targetApi: 'responses',
|
|
286
293
|
hasKey: false
|
|
287
294
|
}
|
|
288
295
|
},
|
|
@@ -290,7 +297,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
290
297
|
name: '',
|
|
291
298
|
apiKey: '',
|
|
292
299
|
baseUrl: '',
|
|
293
|
-
model: ''
|
|
300
|
+
model: '',
|
|
301
|
+
targetApi: 'responses'
|
|
294
302
|
},
|
|
295
303
|
currentOpenclawConfig: '',
|
|
296
304
|
openclawConfigs: {
|
|
@@ -366,7 +374,21 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
366
374
|
codexDownloadProgress: 0,
|
|
367
375
|
codexDownloadTimer: null,
|
|
368
376
|
settingsTab: 'general',
|
|
369
|
-
toolConfigPermissions: {
|
|
377
|
+
toolConfigPermissions: (function() {
|
|
378
|
+
try {
|
|
379
|
+
const cached = localStorage.getItem('toolConfigPermissions');
|
|
380
|
+
if (cached) {
|
|
381
|
+
const parsed = JSON.parse(cached);
|
|
382
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
383
|
+
return {
|
|
384
|
+
codex: parsed.codex === true,
|
|
385
|
+
claude: parsed.claude === true
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
} catch (_) {}
|
|
390
|
+
return { codex: false, claude: false };
|
|
391
|
+
})(),
|
|
370
392
|
toolConfigPermissionSaving: { codex: false, claude: false },
|
|
371
393
|
sessionTrashEnabled: true,
|
|
372
394
|
sessionTrashItems: [],
|
|
@@ -445,13 +467,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
445
467
|
window.location.replace(url.toString());
|
|
446
468
|
return;
|
|
447
469
|
}
|
|
448
|
-
//
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
url.search = '';
|
|
452
|
-
url.hash = '';
|
|
453
|
-
window.history.replaceState(null, '', url.toString());
|
|
454
|
-
}
|
|
470
|
+
// Do not strip query/hash during startup: /session uses them to identify the
|
|
471
|
+
// standalone session, and shareable tab/filter URLs are consumed below before
|
|
472
|
+
// later runtime canonicalization can clean the address bar.
|
|
455
473
|
} catch (_) {}
|
|
456
474
|
|
|
457
475
|
if (typeof this.initI18n === 'function') {
|
|
@@ -544,6 +562,14 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
544
562
|
config.apiKey = '';
|
|
545
563
|
config.hasKey = false;
|
|
546
564
|
}
|
|
565
|
+
const targetApiRaw = typeof config.targetApi === 'string' ? config.targetApi.trim().toLowerCase() : '';
|
|
566
|
+
if (targetApiRaw === 'chat_completions' || targetApiRaw === 'chat-completions' || targetApiRaw === 'chat/completions') {
|
|
567
|
+
config.targetApi = 'chat_completions';
|
|
568
|
+
} else if (targetApiRaw === 'ollama') {
|
|
569
|
+
config.targetApi = 'ollama';
|
|
570
|
+
} else {
|
|
571
|
+
config.targetApi = 'responses';
|
|
572
|
+
}
|
|
547
573
|
}
|
|
548
574
|
localStorage.setItem('claudeConfigs', JSON.stringify(this.claudeConfigs));
|
|
549
575
|
} catch (e) {
|
|
@@ -620,6 +646,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
620
646
|
}
|
|
621
647
|
}
|
|
622
648
|
}
|
|
649
|
+
if (typeof this.loadAppVersionStatus === 'function') {
|
|
650
|
+
void this.loadAppVersionStatus({ silent: true });
|
|
651
|
+
}
|
|
623
652
|
void this.refreshClaudeSelectionFromSettings({ silent: true });
|
|
624
653
|
void this.syncDefaultOpenclawConfigEntry({ silent: true });
|
|
625
654
|
};
|