@midscene/test 1.17.8-beta-20261106101231.0 → 1.17.8

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.
Files changed (51) hide show
  1. package/dist/es/154.mjs +2 -0
  2. package/dist/es/164.mjs +1469 -0
  3. package/dist/es/164.mjs.map +1 -0
  4. package/dist/es/183.mjs +295 -0
  5. package/dist/es/183.mjs.map +1 -0
  6. package/dist/es/1~112.mjs +372 -0
  7. package/dist/es/1~112.mjs.map +1 -0
  8. package/dist/es/209.mjs +1 -0
  9. package/dist/es/299.mjs +1886 -0
  10. package/dist/es/299.mjs.map +1 -0
  11. package/dist/es/398.mjs +5 -0
  12. package/dist/es/398.mjs.map +1 -0
  13. package/dist/es/431.mjs +375 -0
  14. package/dist/es/431.mjs.map +1 -0
  15. package/dist/es/470.mjs +1 -0
  16. package/dist/es/487.mjs +1 -0
  17. package/dist/es/629.mjs +1 -0
  18. package/dist/es/793.mjs +1 -0
  19. package/dist/es/822.mjs +1 -0
  20. package/dist/es/946.mjs +1 -0
  21. package/dist/es/cli/index.mjs +3 -2196
  22. package/dist/es/cli/index.mjs.map +1 -1
  23. package/dist/es/cli/model-command.mjs +1 -296
  24. package/dist/es/index.mjs +10 -409
  25. package/dist/es/index.mjs.map +1 -1
  26. package/dist/es/midscene/index.mjs +9 -9
  27. package/dist/es/midscene/index.mjs.map +1 -1
  28. package/dist/es/runtime/index.mjs +4 -5
  29. package/dist/es/runtime/index.mjs.map +1 -1
  30. package/dist/es/test-cli.mjs +1 -3786
  31. package/dist/lib/0~703.js.map +1 -1
  32. package/dist/lib/cli/index.js +45 -58
  33. package/dist/lib/cli/index.js.map +1 -1
  34. package/dist/lib/cli/model-command.js +1 -1
  35. package/dist/lib/cli/model-command.js.map +1 -1
  36. package/dist/lib/index.js +8 -37
  37. package/dist/lib/index.js.map +1 -1
  38. package/dist/lib/midscene/index.js +1 -1
  39. package/dist/lib/midscene/index.js.map +1 -1
  40. package/dist/lib/runtime/index.js +1 -1
  41. package/dist/lib/runtime/index.js.map +1 -1
  42. package/dist/lib/test-cli.js +884 -877
  43. package/dist/lib/test-cli.js.map +1 -1
  44. package/dist/types/cli/index.d.ts +4 -0
  45. package/package.json +20 -21
  46. package/dist/es/1~4.mjs +0 -383
  47. package/dist/es/1~4.mjs.map +0 -1
  48. package/dist/es/1~731.mjs +0 -306
  49. package/dist/es/1~731.mjs.map +0 -1
  50. package/dist/es/cli/model-command.mjs.map +0 -1
  51. package/dist/es/test-cli.mjs.map +0 -1
@@ -0,0 +1,295 @@
1
+ import { runConnectivityTest } from "@midscene/core";
2
+ import { existsSync, join } from "./154.mjs";
3
+ import { dotenv } from "./629.mjs";
4
+ import { globalModelConfigManager } from "./209.mjs";
5
+ import { chalk } from "./946.mjs";
6
+ function loadDotenvConfig(options = {}) {
7
+ const dotEnvConfigFile = join(options.cwd ?? process.cwd(), '.env');
8
+ if (!existsSync(dotEnvConfigFile)) return;
9
+ options.log?.(` Env file: ${dotEnvConfigFile}`);
10
+ dotenv.config({
11
+ path: dotEnvConfigFile,
12
+ debug: options.dotenvDebug,
13
+ override: options.dotenvOverride
14
+ });
15
+ }
16
+ const DEFAULT_OPENAI_BASE_URL = 'https://api.openai.com/v1';
17
+ const MODEL_VERIFY_SEPARATOR = '────────────────────────────────────────';
18
+ const modelCommandUsage = (commandName)=>`Usage:
19
+ ${commandName} model verify
20
+
21
+ Verify model connectivity and Midscene compatibility for default, locate, planning, and insight models.
22
+ Reads .env from the current working directory, overriding shell environment variables.
23
+ Shows endpoints, model names, request counts, and per-check results with duration.
24
+ Returns exit code 0 on success and 1 on failure.
25
+
26
+ Options:
27
+ --help, -h Show this help
28
+ `;
29
+ function assertNoModelVerifyOptions(args, commandName) {
30
+ for (const arg of args)throw new Error(`Unknown option for ${commandName} model verify: ${arg}`);
31
+ }
32
+ function formatTable(headers, rows) {
33
+ const widths = headers.map((header, index)=>Math.max(header.length, ...rows.map((row)=>row[index].length)));
34
+ const formatRow = (row)=>row.map((cell, index)=>cell.padEnd(widths[index])).join(' ').trimEnd();
35
+ return [
36
+ formatRow(headers),
37
+ ...rows.map(formatRow)
38
+ ].join('\n');
39
+ }
40
+ function displayEndpoint(endpoint) {
41
+ try {
42
+ const url = new URL(endpoint);
43
+ url.username = '';
44
+ url.password = '';
45
+ url.search = '';
46
+ url.hash = '';
47
+ return url.toString();
48
+ } catch {
49
+ return '(invalid endpoint URL)';
50
+ }
51
+ }
52
+ function shellSingleQuote(value) {
53
+ return `'${value.replace(/'/g, "'\\''")}'`;
54
+ }
55
+ function buildChatCompletionsUrl(baseURL) {
56
+ const normalizedBaseURL = (baseURL || DEFAULT_OPENAI_BASE_URL).replace(/\/+$/, '');
57
+ if (normalizedBaseURL.endsWith('/chat/completions')) return normalizedBaseURL;
58
+ return `${normalizedBaseURL}/chat/completions`;
59
+ }
60
+ function buildCurlCommand(modelConfig) {
61
+ const payload = {
62
+ model: modelConfig.modelName,
63
+ messages: [
64
+ {
65
+ role: 'user',
66
+ content: 'What is 1+1?'
67
+ }
68
+ ]
69
+ };
70
+ return [
71
+ `curl -X POST ${shellSingleQuote(buildChatCompletionsUrl(modelConfig.openaiBaseURL))} \\`,
72
+ ` -H ${shellSingleQuote(`Authorization: Bearer ${modelConfig.openaiApiKey || ''}`)} \\`,
73
+ ` -H ${shellSingleQuote('Content-Type: application/json')} \\`,
74
+ ` -d ${shellSingleQuote(JSON.stringify(payload, null, 2))}`
75
+ ].join('\n');
76
+ }
77
+ function buildCurlDedupKey(modelConfig) {
78
+ return JSON.stringify({
79
+ baseURL: modelConfig.openaiBaseURL || DEFAULT_OPENAI_BASE_URL,
80
+ apiKey: modelConfig.openaiApiKey || '',
81
+ modelName: modelConfig.modelName
82
+ });
83
+ }
84
+ function buildModelVerifyCurlCommands(configs) {
85
+ const commandMap = new Map();
86
+ for (const item of configs){
87
+ const key = buildCurlDedupKey(item.modelConfig);
88
+ const existing = commandMap.get(key);
89
+ if (existing) {
90
+ existing.intents.push(item.intent);
91
+ continue;
92
+ }
93
+ commandMap.set(key, {
94
+ intents: [
95
+ item.intent
96
+ ],
97
+ curl: buildCurlCommand(item.modelConfig),
98
+ usesDefaultBaseURL: !item.modelConfig.openaiBaseURL
99
+ });
100
+ }
101
+ return [
102
+ ...commandMap.values()
103
+ ];
104
+ }
105
+ function formatModelVerifyFailureOutput(message, curlCommands) {
106
+ const details = message?.trim() || 'No failure details were generated.';
107
+ const curlSection = curlCommands.map((item)=>{
108
+ const baseUrlNote = item.usesDefaultBaseURL ? ' (base URL not configured; using OpenAI SDK default)' : '';
109
+ return chalk.gray(`# ${item.intents.join(', ')}${baseUrlNote}\n${item.curl}`);
110
+ }).join('\n\n');
111
+ return [
112
+ chalk.red.bold('❌ Model verify failed with messages:'),
113
+ MODEL_VERIFY_SEPARATOR,
114
+ '',
115
+ details,
116
+ '',
117
+ MODEL_VERIFY_SEPARATOR,
118
+ 'Generated curl requests for basic API connectivity:',
119
+ 'If the error is a basic connectivity issue, use these requests to test the base URL, API key, and model name directly.',
120
+ 'These commands contain your API key. Do not share them publicly.',
121
+ '',
122
+ curlSection
123
+ ].join('\n');
124
+ }
125
+ function getDefaultDeps(io) {
126
+ return {
127
+ loadDotenv: ()=>{
128
+ loadDotenvConfig({
129
+ dotenvDebug: true,
130
+ dotenvOverride: true,
131
+ log: io.stdout
132
+ });
133
+ },
134
+ getModelConfig: (intent)=>globalModelConfigManager.getModelConfig(intent),
135
+ verifyModel: runConnectivityTest
136
+ };
137
+ }
138
+ async function runModelVerifyCommand(args, deps, io, commandName) {
139
+ try {
140
+ if (args.includes('--help') || args.includes('-h')) {
141
+ io.stdout(modelCommandUsage(commandName));
142
+ return 0;
143
+ }
144
+ assertNoModelVerifyOptions(args, commandName);
145
+ io.stdout('Model verify started. This usually completes within 30 seconds.');
146
+ deps.loadDotenv();
147
+ io.stdout('');
148
+ const defaultModelConfig = deps.getModelConfig('default');
149
+ const locateModelConfig = deps.getModelConfig('locate');
150
+ const planningModelConfig = deps.getModelConfig('planning');
151
+ const insightModelConfig = deps.getModelConfig('insight');
152
+ const curlCommands = buildModelVerifyCurlCommands([
153
+ {
154
+ intent: 'default',
155
+ modelConfig: defaultModelConfig
156
+ },
157
+ {
158
+ intent: 'locate',
159
+ modelConfig: locateModelConfig
160
+ },
161
+ {
162
+ intent: 'planning',
163
+ modelConfig: planningModelConfig
164
+ },
165
+ {
166
+ intent: 'insight',
167
+ modelConfig: insightModelConfig
168
+ }
169
+ ]);
170
+ const baseURLs = new Map();
171
+ const modelRows = [
172
+ [
173
+ 'planning',
174
+ planningModelConfig,
175
+ 'Text response'
176
+ ],
177
+ [
178
+ 'insight',
179
+ insightModelConfig,
180
+ 'Image understanding'
181
+ ],
182
+ [
183
+ 'locate',
184
+ locateModelConfig,
185
+ 'AI locate'
186
+ ],
187
+ [
188
+ 'default',
189
+ defaultModelConfig,
190
+ 'Text response'
191
+ ]
192
+ ].map(([intent, config, check])=>{
193
+ const baseURL = config.openaiExtraConfig?.baseURL ?? config.openaiBaseURL;
194
+ const endpoint = baseURL ? displayEndpoint(String(baseURL)) : 'provider default';
195
+ if (!baseURLs.has(endpoint)) baseURLs.set(endpoint, baseURLs.size + 1);
196
+ return [
197
+ intent,
198
+ config.modelName,
199
+ check,
200
+ `[${baseURLs.get(endpoint)}]`
201
+ ];
202
+ });
203
+ io.stdout(formatTable([
204
+ 'Model role',
205
+ 'Model',
206
+ 'Check',
207
+ 'Endpoint'
208
+ ], modelRows));
209
+ io.stdout('');
210
+ for (const [endpoint, index] of baseURLs)io.stdout(`[${index}] Base URL: ${endpoint}`);
211
+ io.stdout('\nRunning 4 checks...\n');
212
+ const result = await deps.verifyModel({
213
+ defaultModelConfig,
214
+ locateModelConfig,
215
+ planningModelConfig,
216
+ insightModelConfig
217
+ });
218
+ const checkLabels = {
219
+ text: 'Text response',
220
+ vision: 'Image understanding',
221
+ aiLocate: 'AI locate'
222
+ };
223
+ io.stdout(formatTable([
224
+ 'Model role',
225
+ 'Check',
226
+ 'Result',
227
+ 'Requests',
228
+ 'Time'
229
+ ], result.checks.map((check)=>[
230
+ check.intent,
231
+ checkLabels[check.name],
232
+ check.passed ? 'PASS' : 'FAIL',
233
+ String(check.requestCount),
234
+ `${check.durationMs} ms`
235
+ ])));
236
+ const requestEndpoints = new Map();
237
+ for (const check of result.checks){
238
+ for (const endpoint of check.endpoints){
239
+ const display = displayEndpoint(endpoint);
240
+ if (!requestEndpoints.has(display)) requestEndpoints.set(display, new Set());
241
+ requestEndpoints.get(display).add(check.intent);
242
+ }
243
+ if (!check.passed && check.message) io.stdout(`\n${check.intent}: ${check.message}`);
244
+ }
245
+ if (requestEndpoints.size > 0) {
246
+ io.stdout('\nRequest endpoints:');
247
+ for (const [endpoint, intents] of requestEndpoints)io.stdout(` ${[
248
+ ...intents
249
+ ].join(', ')}: ${endpoint}`);
250
+ }
251
+ io.stdout('');
252
+ const passedChecks = result.checks.filter((check)=>check.passed).length;
253
+ io.stdout(`Requests sent: ${result.requestCount}. Checks: ${passedChecks} passed, ${result.checks.length - passedChecks} failed.`);
254
+ if (result.passed) {
255
+ io.stdout('✅ Model verify passed.');
256
+ return 0;
257
+ }
258
+ io.stderr(formatModelVerifyFailureOutput(result.message, curlCommands));
259
+ return 1;
260
+ } catch (error) {
261
+ const message = error instanceof Error ? error.message : String(error);
262
+ io.stderr([
263
+ MODEL_VERIFY_SEPARATOR,
264
+ '❌ Model verify failed with messages:',
265
+ '',
266
+ message,
267
+ MODEL_VERIFY_SEPARATOR
268
+ ].join('\n'));
269
+ return 1;
270
+ }
271
+ }
272
+ async function runModelCommand(rawArgs, deps, io = {
273
+ stdout: console.log,
274
+ stderr: console.error
275
+ }, commandName = 'midscene-test') {
276
+ const [, action, ...restArgs] = rawArgs;
277
+ const mergedDeps = {
278
+ ...getDefaultDeps(io),
279
+ ...deps
280
+ };
281
+ if (!action || '--help' === action || '-h' === action) {
282
+ io.stdout(modelCommandUsage(commandName));
283
+ return 0;
284
+ }
285
+ if ('verify' === action) return runModelVerifyCommand(restArgs, mergedDeps, io, commandName);
286
+ if ('eval' === action) {
287
+ io.stderr(`${commandName} model eval is not implemented yet. It is reserved for future model evaluation suites.`);
288
+ return 1;
289
+ }
290
+ io.stderr(`Unknown ${commandName} model command: ${action}\n\n${modelCommandUsage(commandName)}`);
291
+ return 1;
292
+ }
293
+ export { buildModelVerifyCurlCommands, loadDotenvConfig, runModelCommand };
294
+
295
+ //# sourceMappingURL=183.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"183.mjs","sources":["../../src/cli/dotenv-loader.ts","../../src/cli/model-command.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport dotenv from 'dotenv';\n\nexport interface DotenvLoadOptions {\n dotenvOverride?: boolean;\n dotenvDebug?: boolean;\n cwd?: string;\n log?: (message: string) => void;\n}\n\nexport function loadDotenvConfig(options: DotenvLoadOptions = {}) {\n const dotEnvConfigFile = join(options.cwd ?? process.cwd(), '.env');\n if (!existsSync(dotEnvConfigFile)) {\n return;\n }\n\n options.log?.(` Env file: ${dotEnvConfigFile}`);\n dotenv.config({\n path: dotEnvConfigFile,\n debug: options.dotenvDebug,\n override: options.dotenvOverride,\n });\n}\n","import { runConnectivityTest } from '@midscene/core';\nimport {\n type IModelConfig,\n type TIntent,\n globalModelConfigManager,\n} from '@midscene/shared/env';\nimport chalk from 'chalk';\nimport { loadDotenvConfig } from './dotenv-loader';\n\nconst DEFAULT_OPENAI_BASE_URL = 'https://api.openai.com/v1';\nconst MODEL_VERIFY_SEPARATOR = '────────────────────────────────────────';\nconst modelCommandUsage = (commandName: string) => `Usage:\n ${commandName} model verify\n\nVerify model connectivity and Midscene compatibility for default, locate, planning, and insight models.\nReads .env from the current working directory, overriding shell environment variables.\nShows endpoints, model names, request counts, and per-check results with duration.\nReturns exit code 0 on success and 1 on failure.\n\nOptions:\n --help, -h Show this help\n`;\n\ninterface ModelCommandIO {\n stdout: (message: string) => void;\n stderr: (message: string) => void;\n}\n\ninterface ModelCommandDeps {\n loadDotenv: () => void;\n getModelConfig: (intent: TIntent) => IModelConfig;\n verifyModel: typeof runConnectivityTest;\n}\n\nexport interface CurlCommandItem {\n intents: TIntent[];\n curl: string;\n usesDefaultBaseURL: boolean;\n}\n\nfunction assertNoModelVerifyOptions(args: string[], commandName: string) {\n for (const arg of args) {\n throw new Error(`Unknown option for ${commandName} model verify: ${arg}`);\n }\n}\n\nfunction formatTable(headers: string[], rows: string[][]): string {\n const widths = headers.map((header, index) =>\n Math.max(header.length, ...rows.map((row) => row[index].length)),\n );\n const formatRow = (row: string[]) =>\n row\n .map((cell, index) => cell.padEnd(widths[index]))\n .join(' ')\n .trimEnd();\n return [formatRow(headers), ...rows.map(formatRow)].join('\\n');\n}\n\nfunction displayEndpoint(endpoint: string): string {\n try {\n const url = new URL(endpoint);\n url.username = '';\n url.password = '';\n url.search = '';\n url.hash = '';\n return url.toString();\n } catch {\n return '(invalid endpoint URL)';\n }\n}\n\nfunction shellSingleQuote(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`;\n}\n\nfunction buildChatCompletionsUrl(baseURL?: string): string {\n const normalizedBaseURL = (baseURL || DEFAULT_OPENAI_BASE_URL).replace(\n /\\/+$/,\n '',\n );\n if (normalizedBaseURL.endsWith('/chat/completions')) {\n return normalizedBaseURL;\n }\n return `${normalizedBaseURL}/chat/completions`;\n}\n\nfunction buildCurlCommand(modelConfig: IModelConfig): string {\n const payload = {\n model: modelConfig.modelName,\n messages: [{ role: 'user', content: 'What is 1+1?' }],\n };\n\n return [\n `curl -X POST ${shellSingleQuote(buildChatCompletionsUrl(modelConfig.openaiBaseURL))} \\\\`,\n ` -H ${shellSingleQuote(`Authorization: Bearer ${modelConfig.openaiApiKey || ''}`)} \\\\`,\n ` -H ${shellSingleQuote('Content-Type: application/json')} \\\\`,\n ` -d ${shellSingleQuote(JSON.stringify(payload, null, 2))}`,\n ].join('\\n');\n}\n\nfunction buildCurlDedupKey(modelConfig: IModelConfig): string {\n return JSON.stringify({\n baseURL: modelConfig.openaiBaseURL || DEFAULT_OPENAI_BASE_URL,\n apiKey: modelConfig.openaiApiKey || '',\n modelName: modelConfig.modelName,\n });\n}\n\nexport function buildModelVerifyCurlCommands(\n configs: Array<{ intent: TIntent; modelConfig: IModelConfig }>,\n): CurlCommandItem[] {\n const commandMap = new Map<string, CurlCommandItem>();\n\n for (const item of configs) {\n const key = buildCurlDedupKey(item.modelConfig);\n const existing = commandMap.get(key);\n if (existing) {\n existing.intents.push(item.intent);\n continue;\n }\n commandMap.set(key, {\n intents: [item.intent],\n curl: buildCurlCommand(item.modelConfig),\n usesDefaultBaseURL: !item.modelConfig.openaiBaseURL,\n });\n }\n\n return [...commandMap.values()];\n}\n\nfunction formatModelVerifyFailureOutput(\n message: string | undefined,\n curlCommands: CurlCommandItem[],\n): string {\n const details = message?.trim() || 'No failure details were generated.';\n const curlSection = curlCommands\n .map((item) => {\n const baseUrlNote = item.usesDefaultBaseURL\n ? ' (base URL not configured; using OpenAI SDK default)'\n : '';\n return chalk.gray(\n `# ${item.intents.join(', ')}${baseUrlNote}\\n${item.curl}`,\n );\n })\n .join('\\n\\n');\n\n return [\n chalk.red.bold('❌ Model verify failed with messages:'),\n MODEL_VERIFY_SEPARATOR,\n '',\n details,\n '',\n MODEL_VERIFY_SEPARATOR,\n 'Generated curl requests for basic API connectivity:',\n 'If the error is a basic connectivity issue, use these requests to test the base URL, API key, and model name directly.',\n 'These commands contain your API key. Do not share them publicly.',\n '',\n curlSection,\n ].join('\\n');\n}\n\nfunction getDefaultDeps(io: ModelCommandIO): ModelCommandDeps {\n return {\n loadDotenv: () => {\n loadDotenvConfig({\n dotenvDebug: true,\n dotenvOverride: true,\n log: io.stdout,\n });\n },\n getModelConfig: (intent) => globalModelConfigManager.getModelConfig(intent),\n verifyModel: runConnectivityTest,\n };\n}\n\nasync function runModelVerifyCommand(\n args: string[],\n deps: ModelCommandDeps,\n io: ModelCommandIO,\n commandName: string,\n): Promise<number> {\n try {\n if (args.includes('--help') || args.includes('-h')) {\n io.stdout(modelCommandUsage(commandName));\n return 0;\n }\n\n assertNoModelVerifyOptions(args, commandName);\n io.stdout(\n 'Model verify started. This usually completes within 30 seconds.',\n );\n deps.loadDotenv();\n io.stdout('');\n\n const defaultModelConfig = deps.getModelConfig('default');\n const locateModelConfig = deps.getModelConfig('locate');\n const planningModelConfig = deps.getModelConfig('planning');\n const insightModelConfig = deps.getModelConfig('insight');\n const curlCommands = buildModelVerifyCurlCommands([\n { intent: 'default', modelConfig: defaultModelConfig },\n { intent: 'locate', modelConfig: locateModelConfig },\n { intent: 'planning', modelConfig: planningModelConfig },\n { intent: 'insight', modelConfig: insightModelConfig },\n ]);\n\n const baseURLs = new Map<string, number>();\n const modelRows = (\n [\n ['planning', planningModelConfig, 'Text response'],\n ['insight', insightModelConfig, 'Image understanding'],\n ['locate', locateModelConfig, 'AI locate'],\n ['default', defaultModelConfig, 'Text response'],\n ] as const\n ).map(([intent, config, check]) => {\n const baseURL = config.openaiExtraConfig?.baseURL ?? config.openaiBaseURL;\n const endpoint = baseURL\n ? displayEndpoint(String(baseURL))\n : 'provider default';\n if (!baseURLs.has(endpoint)) baseURLs.set(endpoint, baseURLs.size + 1);\n return [intent, config.modelName, check, `[${baseURLs.get(endpoint)}]`];\n });\n io.stdout(\n formatTable(['Model role', 'Model', 'Check', 'Endpoint'], modelRows),\n );\n io.stdout('');\n for (const [endpoint, index] of baseURLs) {\n io.stdout(`[${index}] Base URL: ${endpoint}`);\n }\n io.stdout('\\nRunning 4 checks...\\n');\n\n const result = await deps.verifyModel({\n defaultModelConfig,\n locateModelConfig,\n planningModelConfig,\n insightModelConfig,\n });\n\n const checkLabels = {\n text: 'Text response',\n vision: 'Image understanding',\n aiLocate: 'AI locate',\n };\n io.stdout(\n formatTable(\n ['Model role', 'Check', 'Result', 'Requests', 'Time'],\n result.checks.map((check) => [\n check.intent,\n checkLabels[check.name],\n check.passed ? 'PASS' : 'FAIL',\n String(check.requestCount),\n `${check.durationMs} ms`,\n ]),\n ),\n );\n const requestEndpoints = new Map<string, Set<string>>();\n for (const check of result.checks) {\n for (const endpoint of check.endpoints) {\n const display = displayEndpoint(endpoint);\n if (!requestEndpoints.has(display))\n requestEndpoints.set(display, new Set());\n requestEndpoints.get(display)!.add(check.intent);\n }\n if (!check.passed && check.message)\n io.stdout(`\\n${check.intent}: ${check.message}`);\n }\n if (requestEndpoints.size > 0) {\n io.stdout('\\nRequest endpoints:');\n for (const [endpoint, intents] of requestEndpoints) {\n io.stdout(` ${[...intents].join(', ')}: ${endpoint}`);\n }\n }\n io.stdout('');\n const passedChecks = result.checks.filter((check) => check.passed).length;\n io.stdout(\n `Requests sent: ${result.requestCount}. Checks: ${passedChecks} passed, ${result.checks.length - passedChecks} failed.`,\n );\n\n if (result.passed) {\n io.stdout('✅ Model verify passed.');\n return 0;\n }\n\n io.stderr(formatModelVerifyFailureOutput(result.message, curlCommands));\n return 1;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n io.stderr(\n [\n MODEL_VERIFY_SEPARATOR,\n '❌ Model verify failed with messages:',\n '',\n message,\n MODEL_VERIFY_SEPARATOR,\n ].join('\\n'),\n );\n return 1;\n }\n}\n\nexport async function runModelCommand(\n rawArgs: string[],\n deps?: Partial<ModelCommandDeps>,\n io: ModelCommandIO = {\n stdout: console.log,\n stderr: console.error,\n },\n commandName = 'midscene-test',\n): Promise<number> {\n const [, action, ...restArgs] = rawArgs;\n const mergedDeps = {\n ...getDefaultDeps(io),\n ...deps,\n };\n\n if (!action || action === '--help' || action === '-h') {\n io.stdout(modelCommandUsage(commandName));\n return 0;\n }\n\n if (action === 'verify') {\n return runModelVerifyCommand(restArgs, mergedDeps, io, commandName);\n }\n\n if (action === 'eval') {\n io.stderr(\n `${commandName} model eval is not implemented yet. It is reserved for future model evaluation suites.`,\n );\n return 1;\n }\n\n io.stderr(\n `Unknown ${commandName} model command: ${action}\\n\\n${modelCommandUsage(commandName)}`,\n );\n return 1;\n}\n\nexport { loadDotenvConfig, type DotenvLoadOptions } from './dotenv-loader';\n"],"names":["loadDotenvConfig","options","dotEnvConfigFile","join","process","existsSync","dotenv","DEFAULT_OPENAI_BASE_URL","MODEL_VERIFY_SEPARATOR","modelCommandUsage","commandName","assertNoModelVerifyOptions","args","arg","Error","formatTable","headers","rows","widths","header","index","Math","row","formatRow","cell","displayEndpoint","endpoint","url","URL","shellSingleQuote","value","buildChatCompletionsUrl","baseURL","normalizedBaseURL","buildCurlCommand","modelConfig","payload","JSON","buildCurlDedupKey","buildModelVerifyCurlCommands","configs","commandMap","Map","item","key","existing","formatModelVerifyFailureOutput","message","curlCommands","details","curlSection","baseUrlNote","chalk","getDefaultDeps","io","intent","globalModelConfigManager","runConnectivityTest","runModelVerifyCommand","deps","defaultModelConfig","locateModelConfig","planningModelConfig","insightModelConfig","baseURLs","modelRows","config","check","String","result","checkLabels","requestEndpoints","display","Set","intents","passedChecks","error","runModelCommand","rawArgs","console","action","restArgs","mergedDeps"],"mappings":"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"}