codexmate 0.0.23 → 0.0.24
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 +4 -3
- package/README.zh.md +4 -2
- package/cli/auth-profiles.js +23 -7
- package/cli/doctor-core.js +903 -0
- package/cli/import-skills-url.js +334 -0
- package/cli.js +304 -208
- package/lib/cli-models-utils.js +0 -40
- package/lib/cli-network-utils.js +28 -2
- package/package.json +5 -2
- package/plugins/README.md +20 -0
- package/plugins/README.zh-CN.md +20 -0
- package/plugins/prompt-templates/comment-polish/index.mjs +25 -0
- package/plugins/prompt-templates/computed.mjs +253 -0
- package/plugins/prompt-templates/index.mjs +8 -0
- package/plugins/prompt-templates/manifest.mjs +15 -0
- package/plugins/prompt-templates/methods.mjs +619 -0
- package/plugins/prompt-templates/overview.mjs +90 -0
- package/plugins/prompt-templates/ownership.mjs +19 -0
- package/plugins/prompt-templates/rule-ack/index.mjs +21 -0
- package/plugins/prompt-templates/storage.mjs +64 -0
- package/plugins/registry.mjs +16 -0
- package/res/logo-pack.webp +0 -0
- package/web-ui/app.js +15 -32
- package/web-ui/index.html +4 -3
- package/web-ui/modules/app.computed.dashboard.mjs +22 -22
- package/web-ui/modules/app.computed.main-tabs.mjs +3 -0
- package/web-ui/modules/app.methods.agents.mjs +91 -3
- package/web-ui/modules/app.methods.codex-config.mjs +153 -164
- package/web-ui/modules/app.methods.navigation.mjs +34 -1
- package/web-ui/modules/app.methods.runtime.mjs +24 -2
- package/web-ui/modules/app.methods.session-browser.mjs +9 -2
- package/web-ui/modules/config-mode.computed.mjs +1 -3
- package/web-ui/modules/i18n.dict.mjs +2039 -0
- package/web-ui/modules/i18n.mjs +2 -1769
- package/web-ui/partials/index/layout-header.html +36 -32
- package/web-ui/partials/index/modal-config-template-agents.html +3 -4
- package/web-ui/partials/index/modal-health-check.html +33 -60
- package/web-ui/partials/index/panel-config-claude.html +35 -15
- package/web-ui/partials/index/panel-config-codex.html +47 -19
- package/web-ui/partials/index/panel-config-openclaw.html +8 -3
- package/web-ui/partials/index/panel-dashboard.html +186 -0
- package/web-ui/partials/index/panel-docs.html +1 -1
- package/web-ui/partials/index/panel-market.html +3 -0
- package/web-ui/partials/index/panel-orchestration.html +3 -0
- package/web-ui/partials/index/panel-plugins.html +16 -10
- package/web-ui/partials/index/panel-sessions.html +4 -1
- package/web-ui/partials/index/panel-settings.html +1 -1
- package/web-ui/partials/index/panel-usage.html +2 -1
- package/web-ui/styles/controls-forms.css +9 -2
- package/web-ui/styles/dashboard.css +274 -0
- package/web-ui/styles/layout-shell.css +2 -2
- package/web-ui/styles/sessions-list.css +3 -3
- package/web-ui/styles/sessions-usage.css +9 -0
- package/web-ui/styles.css +1 -0
- package/res/logo.png +0 -0
package/cli.js
CHANGED
|
@@ -42,8 +42,9 @@ const {
|
|
|
42
42
|
extractModelNames,
|
|
43
43
|
hasModelsListPayload,
|
|
44
44
|
buildModelsCacheKey,
|
|
45
|
+
buildApiProbeUrlCandidates,
|
|
45
46
|
buildModelProbeSpec,
|
|
46
|
-
|
|
47
|
+
buildModelProbeSpecs,
|
|
47
48
|
extractModelResponseText,
|
|
48
49
|
normalizeWireApi,
|
|
49
50
|
getSupplementalModelsForBaseUrl,
|
|
@@ -74,6 +75,7 @@ const {
|
|
|
74
75
|
executeTaskPlan
|
|
75
76
|
} = require('./lib/task-orchestrator');
|
|
76
77
|
const { buildConfigHealthReport: buildConfigHealthReportCore } = require('./cli/config-health');
|
|
78
|
+
const { buildDoctorReport, buildDoctorLegacyPayload, renderDoctorMarkdown } = require('./cli/doctor-core');
|
|
77
79
|
const {
|
|
78
80
|
createAuthProfileController
|
|
79
81
|
} = require('./cli/auth-profiles');
|
|
@@ -125,6 +127,7 @@ const {
|
|
|
125
127
|
deleteSkills,
|
|
126
128
|
deleteCodexSkills
|
|
127
129
|
} = require('./cli/skills');
|
|
130
|
+
const { cmdImportSkills: cmdImportSkillsFromUrl } = require('./cli/import-skills-url');
|
|
128
131
|
const {
|
|
129
132
|
getFileStatSafe,
|
|
130
133
|
isBootstrapLikeText,
|
|
@@ -4155,12 +4158,14 @@ async function listAllSessions(params = {}) {
|
|
|
4155
4158
|
const queryTokens = expandSessionQueryTokens(normalizeQueryTokens(params.query));
|
|
4156
4159
|
const hasQuery = queryTokens.length > 0;
|
|
4157
4160
|
const browseLightweight = params.browseLightweight === true && !hasQuery && !hasPathFilter;
|
|
4158
|
-
const
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4161
|
+
const queryKeyRaw = typeof params.query === 'string' ? params.query.trim() : '';
|
|
4162
|
+
const queryKey = queryKeyRaw.length > 240 ? queryKeyRaw.slice(0, 240) : queryKeyRaw;
|
|
4163
|
+
const cacheKey = hasQuery
|
|
4164
|
+
? `query:${source}:${limit}:${normalizedPathFilter}:${params.queryMode || ''}:${params.queryScope || ''}:${params.roleFilter || ''}:${Number(params.contentScanLimit) || ''}:${Number(params.contentScanBytes) || ''}:${queryKey}`
|
|
4165
|
+
: `${browseLightweight ? 'browse' : 'default'}:${source}:${limit}:${normalizedPathFilter}`;
|
|
4166
|
+
const cached = getSessionListCache(cacheKey, forceRefresh);
|
|
4167
|
+
if (cached) {
|
|
4168
|
+
return cached;
|
|
4164
4169
|
}
|
|
4165
4170
|
|
|
4166
4171
|
const scanOptions = hasPathFilter
|
|
@@ -4201,9 +4206,7 @@ async function listAllSessions(params = {}) {
|
|
|
4201
4206
|
});
|
|
4202
4207
|
}
|
|
4203
4208
|
result = mergeAndLimitSessions(result, limit);
|
|
4204
|
-
|
|
4205
|
-
setSessionListCache(cacheKey, result);
|
|
4206
|
-
}
|
|
4209
|
+
setSessionListCache(cacheKey, result);
|
|
4207
4210
|
return result;
|
|
4208
4211
|
}
|
|
4209
4212
|
|
|
@@ -6003,6 +6006,60 @@ function importConfigData(payload, options = {}) {
|
|
|
6003
6006
|
function resolveSpeedTestTarget(params) {
|
|
6004
6007
|
if (!params) return { error: 'Missing params' };
|
|
6005
6008
|
|
|
6009
|
+
if (typeof params.kind === 'string' && params.kind.trim() === 'claude') {
|
|
6010
|
+
const baseUrl = typeof params.url === 'string' ? params.url.trim() : '';
|
|
6011
|
+
const apiKey = typeof params.apiKey === 'string' ? params.apiKey.trim() : '';
|
|
6012
|
+
const model = typeof params.model === 'string' ? params.model.trim() : '';
|
|
6013
|
+
if (!baseUrl) {
|
|
6014
|
+
return { error: 'Missing url' };
|
|
6015
|
+
}
|
|
6016
|
+
if (!apiKey) {
|
|
6017
|
+
return { error: 'Missing apiKey' };
|
|
6018
|
+
}
|
|
6019
|
+
if (!model) {
|
|
6020
|
+
return { error: 'Missing model' };
|
|
6021
|
+
}
|
|
6022
|
+
const normalizedBase = baseUrl.replace(/\/+$/, '');
|
|
6023
|
+
let parsed = null;
|
|
6024
|
+
try {
|
|
6025
|
+
parsed = new URL(normalizedBase);
|
|
6026
|
+
} catch (_) {
|
|
6027
|
+
return { error: 'Invalid URL' };
|
|
6028
|
+
}
|
|
6029
|
+
const pathname = typeof parsed.pathname === 'string' ? parsed.pathname : '';
|
|
6030
|
+
const trimmedPath = pathname.replace(/\/+$/, '');
|
|
6031
|
+
const isRootPath = !trimmedPath || trimmedPath === '/';
|
|
6032
|
+
const endsWithV1 = trimmedPath.endsWith('/v1');
|
|
6033
|
+
const makeCandidate = (url) => ({
|
|
6034
|
+
method: 'POST',
|
|
6035
|
+
url,
|
|
6036
|
+
body: {
|
|
6037
|
+
model,
|
|
6038
|
+
max_tokens: 16,
|
|
6039
|
+
messages: [{ role: 'user', content: 'ping' }]
|
|
6040
|
+
}
|
|
6041
|
+
});
|
|
6042
|
+
const candidates = [];
|
|
6043
|
+
if (endsWithV1) {
|
|
6044
|
+
candidates.push(makeCandidate(`${normalizedBase}/messages`));
|
|
6045
|
+
} else if (isRootPath) {
|
|
6046
|
+
candidates.push(makeCandidate(`${normalizedBase}/v1/messages`));
|
|
6047
|
+
candidates.push(makeCandidate(`${normalizedBase}/messages`));
|
|
6048
|
+
} else {
|
|
6049
|
+
candidates.push(makeCandidate(`${normalizedBase}/messages`));
|
|
6050
|
+
candidates.push(makeCandidate(`${normalizedBase}/v1/messages`));
|
|
6051
|
+
}
|
|
6052
|
+
return {
|
|
6053
|
+
kind: 'claude',
|
|
6054
|
+
candidates,
|
|
6055
|
+
apiKey,
|
|
6056
|
+
apiKeyHeader: 'x-api-key',
|
|
6057
|
+
headers: {
|
|
6058
|
+
'anthropic-version': '2023-06-01'
|
|
6059
|
+
}
|
|
6060
|
+
};
|
|
6061
|
+
}
|
|
6062
|
+
|
|
6006
6063
|
if (params.name) {
|
|
6007
6064
|
const { config } = readConfigOrVirtualDefault();
|
|
6008
6065
|
const providers = config.model_providers || {};
|
|
@@ -6013,20 +6070,32 @@ function resolveSpeedTestTarget(params) {
|
|
|
6013
6070
|
if (!provider.base_url) {
|
|
6014
6071
|
return { error: 'Provider missing URL' };
|
|
6015
6072
|
}
|
|
6016
|
-
const
|
|
6017
|
-
const
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6073
|
+
const providerName = String(params.name).trim();
|
|
6074
|
+
const currentModels = readCurrentModels();
|
|
6075
|
+
const selectedModel = typeof currentModels[providerName] === 'string' && currentModels[providerName].trim()
|
|
6076
|
+
? currentModels[providerName].trim()
|
|
6077
|
+
: (typeof config.model === 'string' ? config.model.trim() : '');
|
|
6078
|
+
|
|
6079
|
+
const apiKey = typeof provider.preferred_auth_method === 'string'
|
|
6080
|
+
? provider.preferred_auth_method.trim()
|
|
6081
|
+
: '';
|
|
6082
|
+
|
|
6083
|
+
const candidates = [];
|
|
6084
|
+
for (const spec of buildModelProbeSpecs(provider, selectedModel, provider.base_url)) {
|
|
6085
|
+
if (!spec || !spec.url) continue;
|
|
6086
|
+
candidates.push({ method: 'POST', url: spec.url, body: spec.body });
|
|
6087
|
+
}
|
|
6088
|
+
for (const url of buildApiProbeUrlCandidates(provider.base_url, 'models')) {
|
|
6089
|
+
candidates.push({ method: 'GET', url });
|
|
6025
6090
|
}
|
|
6091
|
+
if (candidates.length === 0) {
|
|
6092
|
+
candidates.push({ method: 'GET', url: provider.base_url });
|
|
6093
|
+
}
|
|
6094
|
+
|
|
6026
6095
|
return {
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
apiKey
|
|
6096
|
+
kind: 'provider',
|
|
6097
|
+
candidates,
|
|
6098
|
+
apiKey
|
|
6030
6099
|
};
|
|
6031
6100
|
}
|
|
6032
6101
|
|
|
@@ -6041,155 +6110,6 @@ function resolveSpeedTestTarget(params) {
|
|
|
6041
6110
|
return { error: 'Missing name or url' };
|
|
6042
6111
|
}
|
|
6043
6112
|
|
|
6044
|
-
function extractApiPayloadErrorMessage(payload) {
|
|
6045
|
-
if (!payload || typeof payload !== 'object') {
|
|
6046
|
-
return '';
|
|
6047
|
-
}
|
|
6048
|
-
if (typeof payload.error === 'string' && payload.error.trim()) {
|
|
6049
|
-
return payload.error.trim();
|
|
6050
|
-
}
|
|
6051
|
-
if (!payload.error || typeof payload.error !== 'object') {
|
|
6052
|
-
return '';
|
|
6053
|
-
}
|
|
6054
|
-
if (typeof payload.error.message === 'string' && payload.error.message.trim()) {
|
|
6055
|
-
return payload.error.message.trim();
|
|
6056
|
-
}
|
|
6057
|
-
if (typeof payload.error.code === 'string' && payload.error.code.trim()) {
|
|
6058
|
-
return payload.error.code.trim();
|
|
6059
|
-
}
|
|
6060
|
-
return '';
|
|
6061
|
-
}
|
|
6062
|
-
|
|
6063
|
-
function resolveProviderChatTarget(params) {
|
|
6064
|
-
const providerName = typeof (params && params.name) === 'string' ? params.name.trim() : '';
|
|
6065
|
-
const prompt = typeof (params && params.prompt) === 'string' ? params.prompt.trim() : '';
|
|
6066
|
-
if (!providerName) {
|
|
6067
|
-
return { error: 'Provider name is required' };
|
|
6068
|
-
}
|
|
6069
|
-
if (!prompt) {
|
|
6070
|
-
return { error: 'Prompt is required' };
|
|
6071
|
-
}
|
|
6072
|
-
|
|
6073
|
-
const { config } = readConfigOrVirtualDefault();
|
|
6074
|
-
const providers = config.model_providers || {};
|
|
6075
|
-
const provider = providers[providerName];
|
|
6076
|
-
if (!provider || typeof provider !== 'object') {
|
|
6077
|
-
return { error: `Provider not found: ${providerName}` };
|
|
6078
|
-
}
|
|
6079
|
-
|
|
6080
|
-
const baseUrl = typeof provider.base_url === 'string' ? provider.base_url.trim() : '';
|
|
6081
|
-
if (!baseUrl) {
|
|
6082
|
-
return { error: `Provider ${providerName} missing URL` };
|
|
6083
|
-
}
|
|
6084
|
-
|
|
6085
|
-
const currentModels = readCurrentModels();
|
|
6086
|
-
const savedModel = currentModels && typeof currentModels[providerName] === 'string'
|
|
6087
|
-
? currentModels[providerName].trim()
|
|
6088
|
-
: '';
|
|
6089
|
-
const activeProvider = typeof config.model_provider === 'string' ? config.model_provider.trim() : '';
|
|
6090
|
-
const activeModel = typeof config.model === 'string' ? config.model.trim() : '';
|
|
6091
|
-
const model = savedModel || (activeProvider === providerName ? activeModel : '');
|
|
6092
|
-
if (!model) {
|
|
6093
|
-
return { error: `Provider ${providerName} missing current model` };
|
|
6094
|
-
}
|
|
6095
|
-
|
|
6096
|
-
const specs = buildModelConversationSpecs(provider, model, baseUrl, prompt, {
|
|
6097
|
-
maxOutputTokens: 256
|
|
6098
|
-
});
|
|
6099
|
-
if (!specs.length) {
|
|
6100
|
-
return { error: `Provider ${providerName} missing available conversation endpoint` };
|
|
6101
|
-
}
|
|
6102
|
-
|
|
6103
|
-
return {
|
|
6104
|
-
providerName,
|
|
6105
|
-
provider,
|
|
6106
|
-
model,
|
|
6107
|
-
prompt,
|
|
6108
|
-
specs,
|
|
6109
|
-
apiKey: typeof provider.preferred_auth_method === 'string'
|
|
6110
|
-
? provider.preferred_auth_method.trim()
|
|
6111
|
-
: ''
|
|
6112
|
-
};
|
|
6113
|
-
}
|
|
6114
|
-
|
|
6115
|
-
async function runProviderChatCheck(params = {}) {
|
|
6116
|
-
const target = resolveProviderChatTarget(params);
|
|
6117
|
-
if (target.error) {
|
|
6118
|
-
return { ok: false, error: target.error };
|
|
6119
|
-
}
|
|
6120
|
-
|
|
6121
|
-
const timeoutMs = Number.isFinite(params.timeoutMs)
|
|
6122
|
-
? Math.max(1000, Number(params.timeoutMs))
|
|
6123
|
-
: 30000;
|
|
6124
|
-
let finalSpec = target.specs[0];
|
|
6125
|
-
let result = null;
|
|
6126
|
-
|
|
6127
|
-
for (let index = 0; index < target.specs.length; index += 1) {
|
|
6128
|
-
const candidate = target.specs[index];
|
|
6129
|
-
const probeResult = await probeJsonPost(candidate.url, candidate.body, {
|
|
6130
|
-
apiKey: target.apiKey,
|
|
6131
|
-
timeoutMs,
|
|
6132
|
-
maxBytes: 512 * 1024
|
|
6133
|
-
});
|
|
6134
|
-
finalSpec = candidate;
|
|
6135
|
-
result = probeResult;
|
|
6136
|
-
const shouldTryNextCandidate = index < target.specs.length - 1
|
|
6137
|
-
&& (!probeResult.ok || probeResult.status === 404);
|
|
6138
|
-
if (!shouldTryNextCandidate) {
|
|
6139
|
-
break;
|
|
6140
|
-
}
|
|
6141
|
-
}
|
|
6142
|
-
|
|
6143
|
-
if (!result || !result.ok) {
|
|
6144
|
-
return {
|
|
6145
|
-
ok: false,
|
|
6146
|
-
provider: target.providerName,
|
|
6147
|
-
model: target.model,
|
|
6148
|
-
url: finalSpec.url,
|
|
6149
|
-
status: Number.isFinite(result && result.status) ? result.status : 0,
|
|
6150
|
-
durationMs: Number.isFinite(result && result.durationMs) ? result.durationMs : 0,
|
|
6151
|
-
reply: '',
|
|
6152
|
-
rawPreview: '',
|
|
6153
|
-
error: result && result.error ? result.error : 'request failed'
|
|
6154
|
-
};
|
|
6155
|
-
}
|
|
6156
|
-
|
|
6157
|
-
let payload = null;
|
|
6158
|
-
try {
|
|
6159
|
-
payload = result.body ? JSON.parse(result.body) : null;
|
|
6160
|
-
} catch (e) {
|
|
6161
|
-
payload = null;
|
|
6162
|
-
}
|
|
6163
|
-
|
|
6164
|
-
const payloadError = extractApiPayloadErrorMessage(payload);
|
|
6165
|
-
if (result.status >= 400 || payloadError) {
|
|
6166
|
-
return {
|
|
6167
|
-
ok: false,
|
|
6168
|
-
provider: target.providerName,
|
|
6169
|
-
model: target.model,
|
|
6170
|
-
url: finalSpec.url,
|
|
6171
|
-
status: Number.isFinite(result.status) ? result.status : 0,
|
|
6172
|
-
durationMs: Number.isFinite(result.durationMs) ? result.durationMs : 0,
|
|
6173
|
-
reply: '',
|
|
6174
|
-
rawPreview: result.body ? truncateText(result.body, 600) : '',
|
|
6175
|
-
error: payloadError || `HTTP ${result.status}`
|
|
6176
|
-
};
|
|
6177
|
-
}
|
|
6178
|
-
|
|
6179
|
-
const reply = extractModelResponseText(payload);
|
|
6180
|
-
return {
|
|
6181
|
-
ok: true,
|
|
6182
|
-
provider: target.providerName,
|
|
6183
|
-
model: target.model,
|
|
6184
|
-
url: finalSpec.url,
|
|
6185
|
-
status: Number.isFinite(result.status) ? result.status : 0,
|
|
6186
|
-
durationMs: Number.isFinite(result.durationMs) ? result.durationMs : 0,
|
|
6187
|
-
reply,
|
|
6188
|
-
rawPreview: reply ? '' : (result.body ? truncateText(result.body, 600) : ''),
|
|
6189
|
-
error: ''
|
|
6190
|
-
};
|
|
6191
|
-
}
|
|
6192
|
-
|
|
6193
6113
|
function runSpeedTest(targetUrl, apiKey, options = {}) {
|
|
6194
6114
|
const timeoutMs = Number.isFinite(options.timeoutMs)
|
|
6195
6115
|
? Math.max(1000, Number(options.timeoutMs))
|
|
@@ -6198,6 +6118,8 @@ function runSpeedTest(targetUrl, apiKey, options = {}) {
|
|
|
6198
6118
|
if (method === 'POST') {
|
|
6199
6119
|
return probeJsonPost(targetUrl, options.body || {}, {
|
|
6200
6120
|
apiKey,
|
|
6121
|
+
apiKeyHeader: typeof options.apiKeyHeader === 'string' ? options.apiKeyHeader : '',
|
|
6122
|
+
headers: options.headers && typeof options.headers === 'object' ? options.headers : null,
|
|
6201
6123
|
timeoutMs,
|
|
6202
6124
|
maxBytes: 256 * 1024
|
|
6203
6125
|
}).then((result) => ({
|
|
@@ -6209,6 +6131,8 @@ function runSpeedTest(targetUrl, apiKey, options = {}) {
|
|
|
6209
6131
|
}
|
|
6210
6132
|
return probeUrl(targetUrl, {
|
|
6211
6133
|
apiKey,
|
|
6134
|
+
apiKeyHeader: typeof options.apiKeyHeader === 'string' ? options.apiKeyHeader : '',
|
|
6135
|
+
headers: options.headers && typeof options.headers === 'object' ? options.headers : null,
|
|
6212
6136
|
timeoutMs,
|
|
6213
6137
|
maxBytes: 256 * 1024
|
|
6214
6138
|
}).then((result) => ({
|
|
@@ -6492,6 +6416,123 @@ function cmdStatus() {
|
|
|
6492
6416
|
console.log();
|
|
6493
6417
|
}
|
|
6494
6418
|
|
|
6419
|
+
function parseDoctorCommandArgs(argv = []) {
|
|
6420
|
+
const options = {
|
|
6421
|
+
format: 'json',
|
|
6422
|
+
lang: '',
|
|
6423
|
+
range: '7d',
|
|
6424
|
+
targetApp: 'codex',
|
|
6425
|
+
remote: true,
|
|
6426
|
+
includeInstall: true,
|
|
6427
|
+
includeUsage: true,
|
|
6428
|
+
includeTasks: true,
|
|
6429
|
+
includeSkills: true,
|
|
6430
|
+
output: ''
|
|
6431
|
+
};
|
|
6432
|
+
let cursor = 0;
|
|
6433
|
+
while (cursor < argv.length) {
|
|
6434
|
+
const token = String(argv[cursor] || '');
|
|
6435
|
+
if (token === '--json') {
|
|
6436
|
+
options.format = 'json';
|
|
6437
|
+
cursor += 1;
|
|
6438
|
+
continue;
|
|
6439
|
+
}
|
|
6440
|
+
if (token === '--format') {
|
|
6441
|
+
const value = String(argv[cursor + 1] || '').trim().toLowerCase();
|
|
6442
|
+
if (!value || value.startsWith('--')) {
|
|
6443
|
+
throw new Error('错误: --format 需要一个值(json/md)');
|
|
6444
|
+
}
|
|
6445
|
+
options.format = value === 'md' || value === 'markdown' ? 'md' : 'json';
|
|
6446
|
+
cursor += 2;
|
|
6447
|
+
continue;
|
|
6448
|
+
}
|
|
6449
|
+
if (token === '--output') {
|
|
6450
|
+
const value = String(argv[cursor + 1] || '').trim();
|
|
6451
|
+
if (!value || value.startsWith('--')) {
|
|
6452
|
+
throw new Error('错误: --output 需要一个值(文件路径)');
|
|
6453
|
+
}
|
|
6454
|
+
options.output = value;
|
|
6455
|
+
cursor += 2;
|
|
6456
|
+
continue;
|
|
6457
|
+
}
|
|
6458
|
+
if (token === '--lang') {
|
|
6459
|
+
const value = String(argv[cursor + 1] || '').trim().toLowerCase();
|
|
6460
|
+
if (!value || value.startsWith('--')) {
|
|
6461
|
+
throw new Error('错误: --lang 需要一个值(zh/en)');
|
|
6462
|
+
}
|
|
6463
|
+
options.lang = value === 'en' ? 'en' : 'zh';
|
|
6464
|
+
cursor += 2;
|
|
6465
|
+
continue;
|
|
6466
|
+
}
|
|
6467
|
+
if (token === '--range') {
|
|
6468
|
+
const value = String(argv[cursor + 1] || '').trim().toLowerCase();
|
|
6469
|
+
if (!value || value.startsWith('--')) {
|
|
6470
|
+
throw new Error('错误: --range 需要一个值(7d/30d/all)');
|
|
6471
|
+
}
|
|
6472
|
+
options.range = value === 'all' ? 'all' : (value === '30d' ? '30d' : '7d');
|
|
6473
|
+
cursor += 2;
|
|
6474
|
+
continue;
|
|
6475
|
+
}
|
|
6476
|
+
if (token === '--target-app') {
|
|
6477
|
+
const value = String(argv[cursor + 1] || '').trim().toLowerCase();
|
|
6478
|
+
if (!value || value.startsWith('--')) {
|
|
6479
|
+
throw new Error('错误: --target-app 需要一个值(codex/claude)');
|
|
6480
|
+
}
|
|
6481
|
+
options.targetApp = value === 'claude' ? 'claude' : 'codex';
|
|
6482
|
+
cursor += 2;
|
|
6483
|
+
continue;
|
|
6484
|
+
}
|
|
6485
|
+
if (token === '--no-remote') {
|
|
6486
|
+
options.remote = false;
|
|
6487
|
+
cursor += 1;
|
|
6488
|
+
continue;
|
|
6489
|
+
}
|
|
6490
|
+
if (token === '--no-install') {
|
|
6491
|
+
options.includeInstall = false;
|
|
6492
|
+
cursor += 1;
|
|
6493
|
+
continue;
|
|
6494
|
+
}
|
|
6495
|
+
cursor += 1;
|
|
6496
|
+
}
|
|
6497
|
+
return options;
|
|
6498
|
+
}
|
|
6499
|
+
|
|
6500
|
+
async function cmdDoctor(argv = []) {
|
|
6501
|
+
try {
|
|
6502
|
+
const options = parseDoctorCommandArgs(argv);
|
|
6503
|
+
const report = await buildDoctorReport(options, {
|
|
6504
|
+
getStatusPayload: buildMcpStatusPayload,
|
|
6505
|
+
buildInstallStatusReport,
|
|
6506
|
+
buildConfigHealthReport,
|
|
6507
|
+
listSessionUsage,
|
|
6508
|
+
buildTaskOverviewPayload,
|
|
6509
|
+
listSkills
|
|
6510
|
+
});
|
|
6511
|
+
const format = options.format === 'md' ? 'md' : 'json';
|
|
6512
|
+
const text = format === 'md'
|
|
6513
|
+
? renderDoctorMarkdown(report)
|
|
6514
|
+
: JSON.stringify(report, null, 2);
|
|
6515
|
+
if (options.output) {
|
|
6516
|
+
ensureDir(path.dirname(options.output));
|
|
6517
|
+
fs.writeFileSync(options.output, text);
|
|
6518
|
+
} else {
|
|
6519
|
+
process.stdout.write(text + '\n');
|
|
6520
|
+
}
|
|
6521
|
+
} catch (e) {
|
|
6522
|
+
console.error('错误:', e && e.message ? e.message : e);
|
|
6523
|
+
process.exitCode = 1;
|
|
6524
|
+
}
|
|
6525
|
+
}
|
|
6526
|
+
|
|
6527
|
+
async function cmdImportSkills(argv = []) {
|
|
6528
|
+
try {
|
|
6529
|
+
await cmdImportSkillsFromUrl(argv);
|
|
6530
|
+
} catch (e) {
|
|
6531
|
+
console.error('错误:', e && e.message ? e.message : e);
|
|
6532
|
+
process.exitCode = 1;
|
|
6533
|
+
}
|
|
6534
|
+
}
|
|
6535
|
+
|
|
6495
6536
|
// 列出所有提供商
|
|
6496
6537
|
function cmdList() {
|
|
6497
6538
|
const configResult = readConfigOrVirtualDefault();
|
|
@@ -8339,6 +8380,21 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
8339
8380
|
case 'config-health-check':
|
|
8340
8381
|
result = await buildConfigHealthReport(params || {});
|
|
8341
8382
|
break;
|
|
8383
|
+
case 'doctor':
|
|
8384
|
+
{
|
|
8385
|
+
const doctorParams = isPlainObject(params) ? params : {};
|
|
8386
|
+
const report = await buildDoctorReport(doctorParams, {
|
|
8387
|
+
getStatusPayload: buildMcpStatusPayload,
|
|
8388
|
+
buildInstallStatusReport,
|
|
8389
|
+
buildConfigHealthReport,
|
|
8390
|
+
listSessionUsage,
|
|
8391
|
+
buildTaskOverviewPayload,
|
|
8392
|
+
listSkills
|
|
8393
|
+
});
|
|
8394
|
+
result = buildDoctorLegacyPayload(report);
|
|
8395
|
+
result.markdown = renderDoctorMarkdown(report);
|
|
8396
|
+
}
|
|
8397
|
+
break;
|
|
8342
8398
|
case 'get-agents-file':
|
|
8343
8399
|
result = readAgentsFile(params || {});
|
|
8344
8400
|
break;
|
|
@@ -8446,11 +8502,41 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
8446
8502
|
result = { error: target.error };
|
|
8447
8503
|
break;
|
|
8448
8504
|
}
|
|
8449
|
-
|
|
8450
|
-
|
|
8451
|
-
|
|
8452
|
-
|
|
8453
|
-
|
|
8505
|
+
const timeoutMs = Number.isFinite(params && params.timeoutMs)
|
|
8506
|
+
? Math.max(1000, Number(params.timeoutMs))
|
|
8507
|
+
: 0;
|
|
8508
|
+
if (Array.isArray(target.candidates) && target.candidates.length > 0) {
|
|
8509
|
+
let finalCandidate = target.candidates[0];
|
|
8510
|
+
let finalResult = null;
|
|
8511
|
+
for (let index = 0; index < target.candidates.length; index += 1) {
|
|
8512
|
+
const candidate = target.candidates[index];
|
|
8513
|
+
const probeResult = await runSpeedTest(candidate.url, target.apiKey, {
|
|
8514
|
+
...candidate,
|
|
8515
|
+
apiKeyHeader: target.apiKeyHeader,
|
|
8516
|
+
headers: target.headers,
|
|
8517
|
+
timeoutMs: timeoutMs || undefined
|
|
8518
|
+
});
|
|
8519
|
+
finalCandidate = candidate;
|
|
8520
|
+
finalResult = probeResult;
|
|
8521
|
+
const status = Number.isFinite(probeResult && probeResult.status) ? probeResult.status : 0;
|
|
8522
|
+
const shouldTryNext = index < target.candidates.length - 1 && status === 404;
|
|
8523
|
+
if (!shouldTryNext) {
|
|
8524
|
+
break;
|
|
8525
|
+
}
|
|
8526
|
+
}
|
|
8527
|
+
result = {
|
|
8528
|
+
ok: !!(finalResult && finalResult.ok),
|
|
8529
|
+
status: Number.isFinite(finalResult && finalResult.status) ? finalResult.status : 0,
|
|
8530
|
+
durationMs: Number.isFinite(finalResult && finalResult.durationMs) ? finalResult.durationMs : 0,
|
|
8531
|
+
error: finalResult && finalResult.ok ? '' : (finalResult && finalResult.error ? finalResult.error : ''),
|
|
8532
|
+
url: finalCandidate && finalCandidate.url ? finalCandidate.url : ''
|
|
8533
|
+
};
|
|
8534
|
+
break;
|
|
8535
|
+
}
|
|
8536
|
+
result = await runSpeedTest(target.url, target.apiKey, {
|
|
8537
|
+
...target,
|
|
8538
|
+
timeoutMs: timeoutMs || undefined
|
|
8539
|
+
});
|
|
8454
8540
|
break;
|
|
8455
8541
|
}
|
|
8456
8542
|
case 'openai-bridge-get-provider': {
|
|
@@ -8913,7 +8999,8 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
8913
8999
|
const openUrl = `http://${formatHostForUrl(openHost)}:${port}`;
|
|
8914
9000
|
server.listen(port, host, () => {
|
|
8915
9001
|
console.log('\n✓ Web UI 已启动');
|
|
8916
|
-
|
|
9002
|
+
const willOpenBrowser = !!openBrowser && !process.env.CODEXMATE_NO_BROWSER;
|
|
9003
|
+
console.log(` ${willOpenBrowser ? '已打开' : '待访问'}: ${openUrl}`);
|
|
8917
9004
|
if (host && host !== openHost) {
|
|
8918
9005
|
console.log(' 监听地址:', host);
|
|
8919
9006
|
}
|
|
@@ -8923,9 +9010,8 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
8923
9010
|
console.warn(' 建议仅在可信网络使用,或改用 --host 127.0.0.1。');
|
|
8924
9011
|
}
|
|
8925
9012
|
|
|
8926
|
-
if (
|
|
8927
|
-
|
|
8928
|
-
openBrowserAfterReady(url);
|
|
9013
|
+
if (willOpenBrowser) {
|
|
9014
|
+
openBrowserAfterReady(openUrl);
|
|
8929
9015
|
}
|
|
8930
9016
|
});
|
|
8931
9017
|
|
|
@@ -9036,14 +9122,15 @@ function cmdStart(options = {}) {
|
|
|
9036
9122
|
|| process.env.CODEXMATE_DEV === '1'
|
|
9037
9123
|
|| process.env.CODEXMATE_DEV === 'true';
|
|
9038
9124
|
|
|
9039
|
-
|
|
9125
|
+
const shouldOpenBrowser = !options.noBrowser && !process.env.CODEXMATE_NO_BROWSER;
|
|
9126
|
+
|
|
9040
9127
|
let serverHandle = createWebServer({
|
|
9041
9128
|
htmlPath,
|
|
9042
9129
|
assetsDir,
|
|
9043
9130
|
webDir,
|
|
9044
9131
|
host,
|
|
9045
9132
|
port,
|
|
9046
|
-
openBrowser:
|
|
9133
|
+
openBrowser: shouldOpenBrowser
|
|
9047
9134
|
});
|
|
9048
9135
|
|
|
9049
9136
|
// 禁止前端变更侦测与自动重启:避免终端输出噪音与访问时短暂 Connection Refused。
|
|
@@ -13076,6 +13163,37 @@ async function cmdMcp(args = []) {
|
|
|
13076
13163
|
});
|
|
13077
13164
|
}
|
|
13078
13165
|
|
|
13166
|
+
function printMainHelp() {
|
|
13167
|
+
console.log('\nCodex Mate - Codex 提供商管理工具');
|
|
13168
|
+
console.log('\n用法:');
|
|
13169
|
+
console.log(' codexmate status 显示当前状态');
|
|
13170
|
+
console.log(' codexmate doctor [--format json|md] [--lang zh|en] [--output <PATH>] 输出诊断报告');
|
|
13171
|
+
console.log(' codexmate import-skills <URL> [--target-app codex|claude] [--name <NAME>] [--timeout-ms <MS>] 从 URL 导入 skills');
|
|
13172
|
+
console.log(' codexmate setup 交互式配置向导');
|
|
13173
|
+
console.log(' codexmate list 列出所有提供商');
|
|
13174
|
+
console.log(' codexmate models 列出所有模型');
|
|
13175
|
+
console.log(' codexmate switch <名称> 切换提供商');
|
|
13176
|
+
console.log(' codexmate use <模型> 切换模型');
|
|
13177
|
+
console.log(' codexmate add <名称> <URL> [密钥] [--bridge <openai>]');
|
|
13178
|
+
console.log(' codexmate delete <名称> 删除提供商');
|
|
13179
|
+
console.log(' codexmate claude <BaseURL> <API密钥> [模型] 写入 Claude Code 配置');
|
|
13180
|
+
console.log(' codexmate auth <list|import|switch|delete|status> 认证管理');
|
|
13181
|
+
console.log(' codexmate add-model <模型> 添加模型');
|
|
13182
|
+
console.log(' codexmate delete-model <模型> 删除模型');
|
|
13183
|
+
console.log(' codexmate workflow <list|get|validate|run|runs> MCP 工作流中心');
|
|
13184
|
+
console.log(' codexmate task <plan|run|runs|queue|retry|cancel|logs> 本地任务编排');
|
|
13185
|
+
console.log(' codexmate run [--host <HOST>] [--no-browser] 启动 Web 界面');
|
|
13186
|
+
console.log(' codexmate codex [参数...] [--follow-up <文本>|--queued-follow-up <文本> 可重复] 等同于 codex --yolo');
|
|
13187
|
+
console.log(' 注: follow-up 自动排队仅支持 linux/android/netbsd/openbsd/darwin/freebsd 且 stdin 必须是 TTY,其他平台会报错');
|
|
13188
|
+
console.log(' codexmate qwen [参数...] 等同于 qwen --yolo');
|
|
13189
|
+
console.log(' codexmate mcp [serve] [--transport stdio] [--allow-write|--read-only]');
|
|
13190
|
+
console.log(' codexmate export-session --source <codex|claude> (--session-id <ID>|--file <PATH>) [--output <PATH>] [--max-messages <N|all|Infinity>]');
|
|
13191
|
+
console.log(' codexmate zip <路径> [--max:级别] 压缩(系统 zip 优先,其次 zip-lib)');
|
|
13192
|
+
console.log(' codexmate unzip <zip文件> [输出目录] 解压(zip-lib)');
|
|
13193
|
+
console.log(' codexmate unzip-ext <zip目录> [输出目录] [--ext:后缀[,后缀...]] [--no-recursive] 批量提取 ZIP 指定后缀文件(默认递归)');
|
|
13194
|
+
console.log('');
|
|
13195
|
+
}
|
|
13196
|
+
|
|
13079
13197
|
// ============================================================================
|
|
13080
13198
|
// 主程序
|
|
13081
13199
|
// ============================================================================
|
|
@@ -13091,32 +13209,8 @@ async function main() {
|
|
|
13091
13209
|
}
|
|
13092
13210
|
}
|
|
13093
13211
|
|
|
13094
|
-
if (args.length === 0) {
|
|
13095
|
-
|
|
13096
|
-
console.log('\n用法:');
|
|
13097
|
-
console.log(' codexmate status 显示当前状态');
|
|
13098
|
-
console.log(' codexmate setup 交互式配置向导');
|
|
13099
|
-
console.log(' codexmate list 列出所有提供商');
|
|
13100
|
-
console.log(' codexmate models 列出所有模型');
|
|
13101
|
-
console.log(' codexmate switch <名称> 切换提供商');
|
|
13102
|
-
console.log(' codexmate use <模型> 切换模型');
|
|
13103
|
-
console.log(' codexmate add <名称> <URL> [密钥] [--bridge <openai>]');
|
|
13104
|
-
console.log(' codexmate delete <名称> 删除提供商');
|
|
13105
|
-
console.log(' codexmate claude <BaseURL> <API密钥> [模型] 写入 Claude Code 配置');
|
|
13106
|
-
console.log(' codexmate add-model <模型> 添加模型');
|
|
13107
|
-
console.log(' codexmate delete-model <模型> 删除模型');
|
|
13108
|
-
console.log(' codexmate workflow <list|get|validate|run|runs> MCP 工作流中心');
|
|
13109
|
-
console.log(' codexmate task <plan|run|runs|queue|retry|cancel|logs> 本地任务编排');
|
|
13110
|
-
console.log(' codexmate run [--host <HOST>] [--no-browser] 启动 Web 界面');
|
|
13111
|
-
console.log(' codexmate codex [参数...] [--follow-up <文本>|--queued-follow-up <文本> 可重复] 等同于 codex --yolo');
|
|
13112
|
-
console.log(' 注: follow-up 自动排队仅支持 linux/android/netbsd/openbsd/darwin/freebsd 且 stdin 必须是 TTY,其他平台会报错');
|
|
13113
|
-
console.log(' codexmate qwen [参数...] 等同于 qwen --yolo');
|
|
13114
|
-
console.log(' codexmate mcp [serve] [--transport stdio] [--allow-write|--read-only]');
|
|
13115
|
-
console.log(' codexmate export-session --source <codex|claude> (--session-id <ID>|--file <PATH>) [--output <PATH>] [--max-messages <N|all|Infinity>]');
|
|
13116
|
-
console.log(' codexmate zip <路径> [--max:级别] 压缩(系统 zip 优先,其次 zip-lib)');
|
|
13117
|
-
console.log(' codexmate unzip <zip文件> [输出目录] 解压(zip-lib)');
|
|
13118
|
-
console.log(' codexmate unzip-ext <zip目录> [输出目录] [--ext:后缀[,后缀...]] [--no-recursive] 批量提取 ZIP 指定后缀文件(默认递归)');
|
|
13119
|
-
console.log('');
|
|
13212
|
+
if (args.length === 0 || command === '--help' || command === '-h' || command === 'help') {
|
|
13213
|
+
printMainHelp();
|
|
13120
13214
|
process.exit(0);
|
|
13121
13215
|
}
|
|
13122
13216
|
|
|
@@ -13155,6 +13249,8 @@ async function main() {
|
|
|
13155
13249
|
switch (command) {
|
|
13156
13250
|
case '__task-worker': await cmdTaskWorker(args.slice(1)); break;
|
|
13157
13251
|
case 'status': cmdStatus(); break;
|
|
13252
|
+
case 'doctor': await cmdDoctor(args.slice(1)); break;
|
|
13253
|
+
case 'import-skills': await cmdImportSkills(args.slice(1)); break;
|
|
13158
13254
|
case 'setup': await cmdSetup(); break;
|
|
13159
13255
|
case 'list': cmdList(); break;
|
|
13160
13256
|
case 'models': await cmdModels(); break;
|