codexmate 0.0.19 → 0.0.20
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.en.md +8 -4
- package/README.md +8 -4
- package/cli/config-health.js +338 -0
- package/cli.js +1136 -584
- package/lib/cli-models-utils.js +186 -27
- package/lib/cli-network-utils.js +117 -101
- package/package.json +8 -1
- package/web-ui/app.js +381 -5532
- package/web-ui/index.html +15 -2231
- package/web-ui/logic.agents-diff.mjs +386 -0
- package/web-ui/logic.claude.mjs +108 -0
- package/web-ui/logic.mjs +5 -793
- package/web-ui/logic.runtime.mjs +124 -0
- package/web-ui/logic.sessions.mjs +263 -0
- package/web-ui/modules/api.mjs +69 -0
- package/web-ui/modules/app.computed.dashboard.mjs +113 -0
- package/web-ui/modules/app.computed.index.mjs +13 -0
- package/web-ui/modules/app.computed.session.mjs +141 -0
- package/web-ui/modules/app.constants.mjs +15 -0
- package/web-ui/modules/app.methods.agents.mjs +493 -0
- package/web-ui/modules/app.methods.claude-config.mjs +174 -0
- package/web-ui/modules/app.methods.codex-config.mjs +640 -0
- package/web-ui/modules/app.methods.index.mjs +86 -0
- package/web-ui/modules/app.methods.install.mjs +157 -0
- package/web-ui/modules/app.methods.navigation.mjs +478 -0
- package/web-ui/modules/app.methods.openclaw-core.mjs +514 -0
- package/web-ui/modules/app.methods.openclaw-editing.mjs +337 -0
- package/web-ui/modules/app.methods.openclaw-persist.mjs +251 -0
- package/web-ui/modules/app.methods.providers.mjs +265 -0
- package/web-ui/modules/app.methods.runtime.mjs +323 -0
- package/web-ui/modules/app.methods.session-actions.mjs +457 -0
- package/web-ui/modules/app.methods.session-browser.mjs +435 -0
- package/web-ui/modules/app.methods.session-timeline.mjs +441 -0
- package/web-ui/modules/app.methods.session-trash.mjs +419 -0
- package/web-ui/modules/app.methods.startup-claude.mjs +406 -0
- package/web-ui/partials/index/layout-footer.html +69 -0
- package/web-ui/partials/index/layout-header.html +337 -0
- package/web-ui/partials/index/modal-config-template-agents.html +125 -0
- package/web-ui/partials/index/modal-confirm-toast.html +32 -0
- package/web-ui/partials/index/modal-health-check.html +72 -0
- package/web-ui/partials/index/modal-openclaw-config.html +275 -0
- package/web-ui/partials/index/modal-skills.html +184 -0
- package/web-ui/partials/index/modals-basic.html +196 -0
- package/web-ui/partials/index/panel-config-claude.html +100 -0
- package/web-ui/partials/index/panel-config-codex.html +237 -0
- package/web-ui/partials/index/panel-config-openclaw.html +84 -0
- package/web-ui/partials/index/panel-market.html +174 -0
- package/web-ui/partials/index/panel-sessions.html +387 -0
- package/web-ui/partials/index/panel-settings.html +166 -0
- package/web-ui/source-bundle.cjs +233 -0
- package/web-ui/styles/base-theme.css +373 -0
- package/web-ui/styles/controls-forms.css +354 -0
- package/web-ui/styles/feedback.css +108 -0
- package/web-ui/styles/health-check-dialog.css +144 -0
- package/web-ui/styles/layout-shell.css +330 -0
- package/web-ui/styles/modals-core.css +449 -0
- package/web-ui/styles/navigation-panels.css +381 -0
- package/web-ui/styles/openclaw-structured.css +266 -0
- package/web-ui/styles/responsive.css +416 -0
- package/web-ui/styles/sessions-list.css +414 -0
- package/web-ui/styles/sessions-preview.css +405 -0
- package/web-ui/styles/sessions-toolbar-trash.css +243 -0
- package/web-ui/styles/sessions-usage.css +276 -0
- package/web-ui/styles/skills-list.css +298 -0
- package/web-ui/styles/skills-market.css +335 -0
- package/web-ui/styles/titles-cards.css +407 -0
- package/web-ui/styles.css +16 -4668
package/lib/cli-models-utils.js
CHANGED
|
@@ -82,47 +82,202 @@ function normalizeWireApi(value) {
|
|
|
82
82
|
return raw.replace(/[\s\-\/]/g, '_');
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
function buildApiProbeUrlCandidates(baseUrl, pathSuffix) {
|
|
86
|
+
const normalized = normalizeBaseUrl(baseUrl);
|
|
87
|
+
if (!normalized) return [];
|
|
88
|
+
|
|
89
|
+
const safeSuffix = String(pathSuffix || '').replace(/^\/+/g, '');
|
|
90
|
+
if (!safeSuffix) return [normalized];
|
|
91
|
+
|
|
92
|
+
const candidates = [];
|
|
93
|
+
const pushUnique = (value) => {
|
|
94
|
+
if (value && !candidates.includes(value)) {
|
|
95
|
+
candidates.push(value);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
let pathname = '';
|
|
100
|
+
try {
|
|
101
|
+
pathname = new URL(normalized).pathname.replace(/\/+$/g, '');
|
|
102
|
+
} catch (e) {
|
|
103
|
+
pathname = '';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const directUrl = `${normalized}/${safeSuffix}`;
|
|
107
|
+
const versionedUrl = joinApiUrl(normalized, safeSuffix);
|
|
108
|
+
if (/\/v\d+$/i.test(pathname)) {
|
|
109
|
+
pushUnique(directUrl);
|
|
110
|
+
return candidates;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!pathname || pathname === '/') {
|
|
114
|
+
pushUnique(versionedUrl);
|
|
115
|
+
pushUnique(directUrl);
|
|
116
|
+
return candidates;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
pushUnique(directUrl);
|
|
120
|
+
pushUnique(versionedUrl);
|
|
121
|
+
return candidates;
|
|
122
|
+
}
|
|
123
|
+
|
|
85
124
|
function buildModelsProbeUrl(baseUrl) {
|
|
86
|
-
return
|
|
125
|
+
return buildApiProbeUrlCandidates(baseUrl, 'models')[0] || '';
|
|
87
126
|
}
|
|
88
127
|
|
|
89
|
-
function
|
|
128
|
+
function buildModelProbeSpecs(provider, modelName, baseUrl) {
|
|
90
129
|
const model = typeof modelName === 'string' ? modelName.trim() : '';
|
|
91
|
-
if (!model) return
|
|
130
|
+
if (!model) return [];
|
|
92
131
|
|
|
93
132
|
const wireApi = normalizeWireApi(provider && provider.wire_api);
|
|
133
|
+
let pathSuffix = 'responses';
|
|
134
|
+
let body = {
|
|
135
|
+
model,
|
|
136
|
+
input: 'ping',
|
|
137
|
+
max_output_tokens: 1
|
|
138
|
+
};
|
|
139
|
+
|
|
94
140
|
if (wireApi === 'chat_completions' || wireApi === 'chat') {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
141
|
+
pathSuffix = 'chat/completions';
|
|
142
|
+
body = {
|
|
143
|
+
model,
|
|
144
|
+
messages: [{ role: 'user', content: 'ping' }],
|
|
145
|
+
max_tokens: 1,
|
|
146
|
+
temperature: 0
|
|
147
|
+
};
|
|
148
|
+
} else if (wireApi === 'completions') {
|
|
149
|
+
pathSuffix = 'completions';
|
|
150
|
+
body = {
|
|
151
|
+
model,
|
|
152
|
+
prompt: 'ping',
|
|
153
|
+
max_tokens: 1,
|
|
154
|
+
temperature: 0
|
|
103
155
|
};
|
|
104
156
|
}
|
|
105
157
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
158
|
+
return buildApiProbeUrlCandidates(baseUrl, pathSuffix).map((url) => ({
|
|
159
|
+
url,
|
|
160
|
+
body
|
|
161
|
+
}));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function buildModelProbeSpec(provider, modelName, baseUrl) {
|
|
165
|
+
return buildModelProbeSpecs(provider, modelName, baseUrl)[0] || null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function buildModelConversationSpecs(provider, modelName, baseUrl, prompt, options = {}) {
|
|
169
|
+
const model = typeof modelName === 'string' ? modelName.trim() : '';
|
|
170
|
+
const userPrompt = typeof prompt === 'string' ? prompt.trim() : '';
|
|
171
|
+
if (!model || !userPrompt) return [];
|
|
172
|
+
|
|
173
|
+
const wireApi = normalizeWireApi(provider && provider.wire_api);
|
|
174
|
+
const maxOutputTokens = Number.isFinite(options.maxOutputTokens)
|
|
175
|
+
? Math.max(1, Number(options.maxOutputTokens))
|
|
176
|
+
: 256;
|
|
177
|
+
let pathSuffix = 'responses';
|
|
178
|
+
let body = {
|
|
179
|
+
model,
|
|
180
|
+
input: userPrompt,
|
|
181
|
+
max_output_tokens: maxOutputTokens
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
if (wireApi === 'chat_completions' || wireApi === 'chat') {
|
|
185
|
+
pathSuffix = 'chat/completions';
|
|
186
|
+
body = {
|
|
187
|
+
model,
|
|
188
|
+
messages: [{ role: 'user', content: userPrompt }],
|
|
189
|
+
max_tokens: maxOutputTokens
|
|
190
|
+
};
|
|
191
|
+
} else if (wireApi === 'completions') {
|
|
192
|
+
pathSuffix = 'completions';
|
|
193
|
+
body = {
|
|
194
|
+
model,
|
|
195
|
+
prompt: userPrompt,
|
|
196
|
+
max_tokens: maxOutputTokens
|
|
115
197
|
};
|
|
116
198
|
}
|
|
117
199
|
|
|
118
|
-
return {
|
|
119
|
-
url
|
|
120
|
-
body
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
200
|
+
return buildApiProbeUrlCandidates(baseUrl, pathSuffix).map((url) => ({
|
|
201
|
+
url,
|
|
202
|
+
body,
|
|
203
|
+
wireApi
|
|
204
|
+
}));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function collectStructuredText(content, pieces) {
|
|
208
|
+
if (typeof content === 'string') {
|
|
209
|
+
const text = content.trim();
|
|
210
|
+
if (text) pieces.push(text);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (Array.isArray(content)) {
|
|
214
|
+
for (const item of content) {
|
|
215
|
+
collectStructuredText(item, pieces);
|
|
124
216
|
}
|
|
125
|
-
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (!content || typeof content !== 'object') {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (typeof content.output_text === 'string' && content.output_text.trim()) {
|
|
224
|
+
pieces.push(content.output_text.trim());
|
|
225
|
+
}
|
|
226
|
+
if (typeof content.text === 'string' && content.text.trim()) {
|
|
227
|
+
pieces.push(content.text.trim());
|
|
228
|
+
}
|
|
229
|
+
if (typeof content.content === 'string' && content.content.trim()) {
|
|
230
|
+
pieces.push(content.content.trim());
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (Array.isArray(content.content)) {
|
|
234
|
+
collectStructuredText(content.content, pieces);
|
|
235
|
+
}
|
|
236
|
+
if (content.message) {
|
|
237
|
+
collectStructuredText(content.message, pieces);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function extractModelResponseText(payload) {
|
|
242
|
+
if (!payload || typeof payload !== 'object') {
|
|
243
|
+
return '';
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (typeof payload.output_text === 'string' && payload.output_text.trim()) {
|
|
247
|
+
return payload.output_text.trim();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const pieces = [];
|
|
251
|
+
|
|
252
|
+
if (Array.isArray(payload.output)) {
|
|
253
|
+
for (const item of payload.output) {
|
|
254
|
+
collectStructuredText(item, pieces);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (Array.isArray(payload.choices)) {
|
|
259
|
+
for (const choice of payload.choices) {
|
|
260
|
+
if (!choice || typeof choice !== 'object') continue;
|
|
261
|
+
if (typeof choice.text === 'string' && choice.text.trim()) {
|
|
262
|
+
pieces.push(choice.text.trim());
|
|
263
|
+
}
|
|
264
|
+
if (choice.message) {
|
|
265
|
+
collectStructuredText(choice.message, pieces);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (payload.message) {
|
|
271
|
+
collectStructuredText(payload.message, pieces);
|
|
272
|
+
}
|
|
273
|
+
if (payload.content) {
|
|
274
|
+
collectStructuredText(payload.content, pieces);
|
|
275
|
+
}
|
|
276
|
+
if (typeof payload.text === 'string' && payload.text.trim()) {
|
|
277
|
+
pieces.push(payload.text.trim());
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return Array.from(new Set(pieces)).join('\n\n').trim();
|
|
126
281
|
}
|
|
127
282
|
|
|
128
283
|
function hashModelsCacheValue(value) {
|
|
@@ -145,8 +300,12 @@ module.exports = {
|
|
|
145
300
|
hasModelsListPayload,
|
|
146
301
|
extractModelIds,
|
|
147
302
|
normalizeWireApi,
|
|
303
|
+
buildApiProbeUrlCandidates,
|
|
148
304
|
buildModelsProbeUrl,
|
|
305
|
+
buildModelProbeSpecs,
|
|
149
306
|
buildModelProbeSpec,
|
|
307
|
+
buildModelConversationSpecs,
|
|
308
|
+
extractModelResponseText,
|
|
150
309
|
hashModelsCacheValue,
|
|
151
310
|
buildModelsCacheKey
|
|
152
311
|
};
|
package/lib/cli-network-utils.js
CHANGED
|
@@ -1,58 +1,38 @@
|
|
|
1
1
|
const http = require('http');
|
|
2
2
|
const https = require('https');
|
|
3
3
|
|
|
4
|
-
function
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return resolve({ ok: false, error: 'Invalid URL' });
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const protocol = parsed.protocol;
|
|
14
|
-
if (protocol !== 'http:' && protocol !== 'https:') {
|
|
15
|
-
return resolve({
|
|
16
|
-
ok: false,
|
|
17
|
-
error: `ERR_INVALID_PROTOCOL: Protocol "${protocol}" not supported. Expected "http:" or "https:"`
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const transport = protocol === 'https:' ? https : http;
|
|
22
|
-
const headers = {
|
|
23
|
-
'User-Agent': 'codexmate-health-check',
|
|
24
|
-
'Accept': 'application/json'
|
|
25
|
-
};
|
|
26
|
-
if (options.apiKey) {
|
|
27
|
-
headers['Authorization'] = `Bearer ${options.apiKey}`;
|
|
28
|
-
}
|
|
4
|
+
function shouldRetryWithIpv4(result) {
|
|
5
|
+
if (!result || result.ok || typeof result.error !== 'string') {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
return /timeout|timed out|ETIMEDOUT|ENETUNREACH|EHOSTUNREACH|EAI_AGAIN/i.test(result.error);
|
|
9
|
+
}
|
|
29
10
|
|
|
30
|
-
|
|
31
|
-
|
|
11
|
+
function performProbeRequest(transport, parsed, requestOptions, options = {}) {
|
|
12
|
+
return new Promise((resolve) => {
|
|
32
13
|
const start = Date.now();
|
|
33
|
-
const req = transport.request(parsed,
|
|
14
|
+
const req = transport.request(parsed, requestOptions, (res) => {
|
|
34
15
|
const chunks = [];
|
|
35
16
|
let size = 0;
|
|
36
17
|
res.on('data', (chunk) => {
|
|
37
18
|
if (!chunk) return;
|
|
38
19
|
size += chunk.length;
|
|
39
|
-
if (size <= maxBytes) {
|
|
20
|
+
if (size <= options.maxBytes) {
|
|
40
21
|
chunks.push(chunk);
|
|
41
22
|
}
|
|
42
23
|
});
|
|
43
24
|
res.on('end', () => {
|
|
44
|
-
const body = chunks.length > 0 ? Buffer.concat(chunks).toString('utf-8') : '';
|
|
45
25
|
resolve({
|
|
46
26
|
ok: true,
|
|
47
27
|
status: res.statusCode || 0,
|
|
48
28
|
durationMs: Date.now() - start,
|
|
49
|
-
body
|
|
29
|
+
body: chunks.length > 0 ? Buffer.concat(chunks).toString('utf-8') : ''
|
|
50
30
|
});
|
|
51
31
|
});
|
|
52
32
|
});
|
|
53
33
|
|
|
54
|
-
if (timeoutMs > 0) {
|
|
55
|
-
req.setTimeout(timeoutMs, () => {
|
|
34
|
+
if (options.timeoutMs > 0) {
|
|
35
|
+
req.setTimeout(options.timeoutMs, () => {
|
|
56
36
|
req.destroy(new Error('timeout'));
|
|
57
37
|
});
|
|
58
38
|
}
|
|
@@ -65,81 +45,117 @@ function probeUrl(targetUrl, options = {}) {
|
|
|
65
45
|
});
|
|
66
46
|
});
|
|
67
47
|
|
|
48
|
+
if (options.payload) {
|
|
49
|
+
req.write(options.payload);
|
|
50
|
+
}
|
|
68
51
|
req.end();
|
|
69
52
|
});
|
|
70
53
|
}
|
|
71
54
|
|
|
72
|
-
function
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
error: `ERR_INVALID_PROTOCOL: Protocol "${protocol}" not supported. Expected "http:" or "https:"`
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const transport = protocol === 'https:' ? https : http;
|
|
90
|
-
const headers = {
|
|
91
|
-
'User-Agent': 'codexmate-health-check',
|
|
92
|
-
'Accept': 'application/json',
|
|
93
|
-
'Content-Type': 'application/json'
|
|
55
|
+
async function probeUrl(targetUrl, options = {}) {
|
|
56
|
+
let parsed;
|
|
57
|
+
try {
|
|
58
|
+
parsed = new URL(targetUrl);
|
|
59
|
+
} catch (e) {
|
|
60
|
+
return { ok: false, error: 'Invalid URL' };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const protocol = parsed.protocol;
|
|
64
|
+
if (protocol !== 'http:' && protocol !== 'https:') {
|
|
65
|
+
return {
|
|
66
|
+
ok: false,
|
|
67
|
+
error: `ERR_INVALID_PROTOCOL: Protocol "${protocol}" not supported. Expected "http:" or "https:"`
|
|
94
68
|
};
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
req.on('error', (err) => {
|
|
133
|
-
resolve({
|
|
134
|
-
ok: false,
|
|
135
|
-
error: err.message || 'request failed',
|
|
136
|
-
durationMs: Date.now() - start
|
|
137
|
-
});
|
|
138
|
-
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const transport = protocol === 'https:' ? https : http;
|
|
72
|
+
const headers = {
|
|
73
|
+
'User-Agent': 'codexmate-health-check',
|
|
74
|
+
'Accept': 'application/json'
|
|
75
|
+
};
|
|
76
|
+
if (options.apiKey) {
|
|
77
|
+
headers['Authorization'] = `Bearer ${options.apiKey}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const timeoutMs = Number.isFinite(options.timeoutMs) ? options.timeoutMs : 0;
|
|
81
|
+
const maxBytes = Number.isFinite(options.maxBytes) ? options.maxBytes : 256 * 1024;
|
|
82
|
+
const requestOptions = {
|
|
83
|
+
method: 'GET',
|
|
84
|
+
headers
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const firstResult = await performProbeRequest(transport, parsed, requestOptions, {
|
|
88
|
+
timeoutMs,
|
|
89
|
+
maxBytes
|
|
90
|
+
});
|
|
91
|
+
if (!shouldRetryWithIpv4(firstResult)) {
|
|
92
|
+
return firstResult;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const secondResult = await performProbeRequest(transport, parsed, {
|
|
96
|
+
...requestOptions,
|
|
97
|
+
family: 4
|
|
98
|
+
}, {
|
|
99
|
+
timeoutMs,
|
|
100
|
+
maxBytes
|
|
101
|
+
});
|
|
102
|
+
return secondResult.ok ? secondResult : firstResult;
|
|
103
|
+
}
|
|
139
104
|
|
|
140
|
-
|
|
141
|
-
|
|
105
|
+
async function probeJsonPost(targetUrl, body, options = {}) {
|
|
106
|
+
let parsed;
|
|
107
|
+
try {
|
|
108
|
+
parsed = new URL(targetUrl);
|
|
109
|
+
} catch (e) {
|
|
110
|
+
return { ok: false, error: 'Invalid URL' };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const protocol = parsed.protocol;
|
|
114
|
+
if (protocol !== 'http:' && protocol !== 'https:') {
|
|
115
|
+
return {
|
|
116
|
+
ok: false,
|
|
117
|
+
error: `ERR_INVALID_PROTOCOL: Protocol "${protocol}" not supported. Expected "http:" or "https:"`
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const transport = protocol === 'https:' ? https : http;
|
|
122
|
+
const headers = {
|
|
123
|
+
'User-Agent': 'codexmate-health-check',
|
|
124
|
+
'Accept': 'application/json',
|
|
125
|
+
'Content-Type': 'application/json'
|
|
126
|
+
};
|
|
127
|
+
if (options.apiKey) {
|
|
128
|
+
headers['Authorization'] = `Bearer ${options.apiKey}`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const payload = JSON.stringify(body || {});
|
|
132
|
+
headers['Content-Length'] = Buffer.byteLength(payload);
|
|
133
|
+
|
|
134
|
+
const timeoutMs = Number.isFinite(options.timeoutMs) ? options.timeoutMs : 0;
|
|
135
|
+
const maxBytes = Number.isFinite(options.maxBytes) ? options.maxBytes : 256 * 1024;
|
|
136
|
+
const requestOptions = {
|
|
137
|
+
method: 'POST',
|
|
138
|
+
headers
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const firstResult = await performProbeRequest(transport, parsed, requestOptions, {
|
|
142
|
+
timeoutMs,
|
|
143
|
+
maxBytes,
|
|
144
|
+
payload
|
|
145
|
+
});
|
|
146
|
+
if (!shouldRetryWithIpv4(firstResult)) {
|
|
147
|
+
return firstResult;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const secondResult = await performProbeRequest(transport, parsed, {
|
|
151
|
+
...requestOptions,
|
|
152
|
+
family: 4
|
|
153
|
+
}, {
|
|
154
|
+
timeoutMs,
|
|
155
|
+
maxBytes,
|
|
156
|
+
payload
|
|
142
157
|
});
|
|
158
|
+
return secondResult.ok ? secondResult : firstResult;
|
|
143
159
|
}
|
|
144
160
|
|
|
145
161
|
module.exports = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codexmate",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.20",
|
|
4
4
|
"description": "Codex/Claude Code 配置与会话管理 CLI + Web 工具",
|
|
5
5
|
"main": "cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"cli.js",
|
|
11
|
+
"cli/",
|
|
11
12
|
"web-ui.html",
|
|
12
13
|
"lib/",
|
|
13
14
|
"web-ui/",
|
|
@@ -26,10 +27,16 @@
|
|
|
26
27
|
"scripts": {
|
|
27
28
|
"dev": "node cli.js run",
|
|
28
29
|
"start": "node cli.js",
|
|
30
|
+
"reset": "node cmd/reset-main.js",
|
|
29
31
|
"docs:dev": "node ./node_modules/vitepress/dist/node/cli.js dev site",
|
|
30
32
|
"docs:build": "node ./node_modules/vitepress/dist/node/cli.js build site",
|
|
31
33
|
"docs:preview": "node ./node_modules/vitepress/dist/node/cli.js preview site",
|
|
34
|
+
"ci:install": "node scripts/run-ci-check.js install",
|
|
35
|
+
"ci:lint": "node scripts/run-ci-check.js lint",
|
|
36
|
+
"ci:test": "node scripts/run-ci-check.js test",
|
|
37
|
+
"lint": "node scripts/lint.js",
|
|
32
38
|
"test": "npm run test:unit && npm run test:e2e",
|
|
39
|
+
"test:ci": "node scripts/run-ci-check.js all",
|
|
33
40
|
"test:unit": "node tests/unit/run.mjs",
|
|
34
41
|
"test:e2e": "node tests/e2e/run.js",
|
|
35
42
|
"pretest": "node scripts/ensure-test-deps.js"
|