@midscene/test 1.15.5 → 1.16.0
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/dist/es/1~77.mjs +14 -3
- package/dist/es/1~77.mjs.map +1 -1
- package/dist/es/cli/model-command.mjs +14 -3
- package/dist/es/cli/model-command.mjs.map +1 -1
- package/dist/es/test-cli.mjs +1 -1
- package/dist/lib/0~703.js +14 -3
- package/dist/lib/0~703.js.map +1 -1
- package/dist/lib/cli/model-command.js +14 -3
- package/dist/lib/cli/model-command.js.map +1 -1
- package/dist/lib/test-cli.js +1 -1
- package/package.json +13 -13
package/dist/es/1~77.mjs
CHANGED
|
@@ -28,7 +28,7 @@ export const __webpack_modules__ = {
|
|
|
28
28
|
const modelCommandUsage = (commandName)=>`Usage:
|
|
29
29
|
${commandName} model verify
|
|
30
30
|
|
|
31
|
-
Verify model connectivity and Midscene compatibility for default, planning, and insight models.
|
|
31
|
+
Verify model connectivity and Midscene compatibility for default, locate, planning, and insight models.
|
|
32
32
|
Reads .env from the current working directory, overriding shell environment variables.
|
|
33
33
|
Shows endpoints, model names, request counts, and per-check results with duration.
|
|
34
34
|
Returns exit code 0 on success and 1 on failure.
|
|
@@ -156,6 +156,7 @@ Options:
|
|
|
156
156
|
deps.loadDotenv();
|
|
157
157
|
io.stdout('');
|
|
158
158
|
const defaultModelConfig = deps.getModelConfig('default');
|
|
159
|
+
const locateModelConfig = deps.getModelConfig('locate');
|
|
159
160
|
const planningModelConfig = deps.getModelConfig('planning');
|
|
160
161
|
const insightModelConfig = deps.getModelConfig('insight');
|
|
161
162
|
const curlCommands = buildModelVerifyCurlCommands([
|
|
@@ -163,6 +164,10 @@ Options:
|
|
|
163
164
|
intent: 'default',
|
|
164
165
|
modelConfig: defaultModelConfig
|
|
165
166
|
},
|
|
167
|
+
{
|
|
168
|
+
intent: 'locate',
|
|
169
|
+
modelConfig: locateModelConfig
|
|
170
|
+
},
|
|
166
171
|
{
|
|
167
172
|
intent: 'planning',
|
|
168
173
|
modelConfig: planningModelConfig
|
|
@@ -184,10 +189,15 @@ Options:
|
|
|
184
189
|
insightModelConfig,
|
|
185
190
|
'Image understanding'
|
|
186
191
|
],
|
|
192
|
+
[
|
|
193
|
+
'locate',
|
|
194
|
+
locateModelConfig,
|
|
195
|
+
'AI locate'
|
|
196
|
+
],
|
|
187
197
|
[
|
|
188
198
|
'default',
|
|
189
199
|
defaultModelConfig,
|
|
190
|
-
'
|
|
200
|
+
'Text response'
|
|
191
201
|
]
|
|
192
202
|
].map(([intent, config, check])=>{
|
|
193
203
|
const baseURL = config.openaiExtraConfig?.baseURL ?? config.openaiBaseURL;
|
|
@@ -208,9 +218,10 @@ Options:
|
|
|
208
218
|
], modelRows));
|
|
209
219
|
io.stdout('');
|
|
210
220
|
for (const [endpoint, index] of baseURLs)io.stdout(`[${index}] Base URL: ${endpoint}`);
|
|
211
|
-
io.stdout('\nRunning
|
|
221
|
+
io.stdout('\nRunning 4 checks...\n');
|
|
212
222
|
const result = await deps.verifyModel({
|
|
213
223
|
defaultModelConfig,
|
|
224
|
+
locateModelConfig,
|
|
214
225
|
planningModelConfig,
|
|
215
226
|
insightModelConfig
|
|
216
227
|
});
|
package/dist/es/1~77.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"1~77.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, 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 planningModelConfig = deps.getModelConfig('planning');\n const insightModelConfig = deps.getModelConfig('insight');\n const curlCommands = buildModelVerifyCurlCommands([\n { intent: 'default', modelConfig: defaultModelConfig },\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 ['default', defaultModelConfig, 'AI locate'],\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 3 checks...\\n');\n\n const result = await deps.verifyModel({\n defaultModelConfig,\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","planningModelConfig","insightModelConfig","baseURLs","modelRows","config","check","String","result","checkLabels","requestEndpoints","display","Set","intents","passedChecks","error","runModelCommand","rawArgs","console","action","restArgs","mergedDeps"],"mappings":";;;;;;;;;;;;;;;QAWO,SAASA,iBAAiBC,UAA6B,CAAC,CAAC;YAC9D,MAAMC,mBAAmBC,AAAAA,IAAAA,oBAAAA,IAAAA,AAAAA,EAAKF,QAAQ,GAAG,IAAIG,QAAQ,GAAG,IAAI;YAC5D,IAAI,CAACC,AAAAA,IAAAA,kBAAAA,UAAAA,AAAAA,EAAWH,mBACd;YAGFD,QAAQ,GAAG,GAAG,CAAC,aAAa,EAAEC,kBAAkB;YAChDI,OAAO,MAAM,CAAC;gBACZ,MAAMJ;gBACN,OAAOD,QAAQ,WAAW;gBAC1B,UAAUA,QAAQ,cAAc;YAClC;QACF;QCdA,MAAMM,0BAA0B;QAChC,MAAMC,yBAAyB;QAC/B,MAAMC,oBAAoB,CAACC,cAAwB,CAAC;EAClD,EAAEA,YAAY;;;;;;;;;AAShB,CAAC;QAmBD,SAASC,2BAA2BC,IAAc,EAAEF,WAAmB;YACrE,KAAK,MAAMG,OAAOD,KAChB,MAAM,IAAIE,MAAM,CAAC,mBAAmB,EAAEJ,YAAY,eAAe,EAAEG,KAAK;QAE5E;QAEA,SAASE,YAAYC,OAAiB,EAAEC,IAAgB;YACtD,MAAMC,SAASF,QAAQ,GAAG,CAAC,CAACG,QAAQC,QAClCC,KAAK,GAAG,CAACF,OAAO,MAAM,KAAKF,KAAK,GAAG,CAAC,CAACK,MAAQA,GAAG,CAACF,MAAM,CAAC,MAAM;YAEhE,MAAMG,YAAY,CAACD,MACjBA,IACG,GAAG,CAAC,CAACE,MAAMJ,QAAUI,KAAK,MAAM,CAACN,MAAM,CAACE,MAAM,GAC9C,IAAI,CAAC,MACL,OAAO;YACZ,OAAO;gBAACG,UAAUP;mBAAaC,KAAK,GAAG,CAACM;aAAW,CAAC,IAAI,CAAC;QAC3D;QAEA,SAASE,gBAAgBC,QAAgB;YACvC,IAAI;gBACF,MAAMC,MAAM,IAAIC,IAAIF;gBACpBC,IAAI,QAAQ,GAAG;gBACfA,IAAI,QAAQ,GAAG;gBACfA,IAAI,MAAM,GAAG;gBACbA,IAAI,IAAI,GAAG;gBACX,OAAOA,IAAI,QAAQ;YACrB,EAAE,OAAM;gBACN,OAAO;YACT;QACF;QAEA,SAASE,iBAAiBC,KAAa;YACrC,OAAO,CAAC,CAAC,EAAEA,MAAM,OAAO,CAAC,MAAM,SAAS,CAAC,CAAC;QAC5C;QAEA,SAASC,wBAAwBC,OAAgB;YAC/C,MAAMC,oBAAqBD,AAAAA,CAAAA,WAAWzB,uBAAsB,EAAG,OAAO,CACpE,QACA;YAEF,IAAI0B,kBAAkB,QAAQ,CAAC,sBAC7B,OAAOA;YAET,OAAO,GAAGA,kBAAkB,iBAAiB,CAAC;QAChD;QAEA,SAASC,iBAAiBC,WAAyB;YACjD,MAAMC,UAAU;gBACd,OAAOD,YAAY,SAAS;gBAC5B,UAAU;oBAAC;wBAAE,MAAM;wBAAQ,SAAS;oBAAe;iBAAE;YACvD;YAEA,OAAO;gBACL,CAAC,aAAa,EAAEN,iBAAiBE,wBAAwBI,YAAY,aAAa,GAAG,GAAG,CAAC;gBACzF,CAAC,KAAK,EAAEN,iBAAiB,CAAC,sBAAsB,EAAEM,YAAY,YAAY,IAAI,IAAI,EAAE,GAAG,CAAC;gBACxF,CAAC,KAAK,EAAEN,iBAAiB,kCAAkC,GAAG,CAAC;gBAC/D,CAAC,KAAK,EAAEA,iBAAiBQ,KAAK,SAAS,CAACD,SAAS,MAAM,KAAK;aAC7D,CAAC,IAAI,CAAC;QACT;QAEA,SAASE,kBAAkBH,WAAyB;YAClD,OAAOE,KAAK,SAAS,CAAC;gBACpB,SAASF,YAAY,aAAa,IAAI5B;gBACtC,QAAQ4B,YAAY,YAAY,IAAI;gBACpC,WAAWA,YAAY,SAAS;YAClC;QACF;QAEO,SAASI,6BACdC,OAA8D;YAE9D,MAAMC,aAAa,IAAIC;YAEvB,KAAK,MAAMC,QAAQH,QAAS;gBAC1B,MAAMI,MAAMN,kBAAkBK,KAAK,WAAW;gBAC9C,MAAME,WAAWJ,WAAW,GAAG,CAACG;gBAChC,IAAIC,UAAU;oBACZA,SAAS,OAAO,CAAC,IAAI,CAACF,KAAK,MAAM;oBACjC;gBACF;gBACAF,WAAW,GAAG,CAACG,KAAK;oBAClB,SAAS;wBAACD,KAAK,MAAM;qBAAC;oBACtB,MAAMT,iBAAiBS,KAAK,WAAW;oBACvC,oBAAoB,CAACA,KAAK,WAAW,CAAC,aAAa;gBACrD;YACF;YAEA,OAAO;mBAAIF,WAAW,MAAM;aAAG;QACjC;QAEA,SAASK,+BACPC,OAA2B,EAC3BC,YAA+B;YAE/B,MAAMC,UAAUF,SAAS,UAAU;YACnC,MAAMG,cAAcF,aACjB,GAAG,CAAC,CAACL;gBACJ,MAAMQ,cAAcR,KAAK,kBAAkB,GACvC,yDACA;gBACJ,OAAOS,MAAM,IAAI,CACf,CAAC,EAAE,EAAET,KAAK,OAAO,CAAC,IAAI,CAAC,QAAQQ,YAAY,EAAE,EAAER,KAAK,IAAI,EAAE;YAE9D,GACC,IAAI,CAAC;YAER,OAAO;gBACLS,MAAM,GAAG,CAAC,IAAI,CAAC;gBACf5C;gBACA;gBACAyC;gBACA;gBACAzC;gBACA;gBACA;gBACA;gBACA;gBACA0C;aACD,CAAC,IAAI,CAAC;QACT;QAEA,SAASG,eAAeC,EAAkB;YACxC,OAAO;gBACL,YAAY;oBACVtD,iBAAiB;wBACf,aAAa;wBACb,gBAAgB;wBAChB,KAAKsD,GAAG,MAAM;oBAChB;gBACF;gBACA,gBAAgB,CAACC,SAAWC,yBAAyB,cAAc,CAACD;gBACpE,aAAaE;YACf;QACF;QAEA,eAAeC,sBACb9C,IAAc,EACd+C,IAAsB,EACtBL,EAAkB,EAClB5C,WAAmB;YAEnB,IAAI;gBACF,IAAIE,KAAK,QAAQ,CAAC,aAAaA,KAAK,QAAQ,CAAC,OAAO;oBAClD0C,GAAG,MAAM,CAAC7C,kBAAkBC;oBAC5B,OAAO;gBACT;gBAEAC,2BAA2BC,MAAMF;gBACjC4C,GAAG,MAAM,CACP;gBAEFK,KAAK,UAAU;gBACfL,GAAG,MAAM,CAAC;gBAEV,MAAMM,qBAAqBD,KAAK,cAAc,CAAC;gBAC/C,MAAME,sBAAsBF,KAAK,cAAc,CAAC;gBAChD,MAAMG,qBAAqBH,KAAK,cAAc,CAAC;gBAC/C,MAAMX,eAAeT,6BAA6B;oBAChD;wBAAE,QAAQ;wBAAW,aAAaqB;oBAAmB;oBACrD;wBAAE,QAAQ;wBAAY,aAAaC;oBAAoB;oBACvD;wBAAE,QAAQ;wBAAW,aAAaC;oBAAmB;iBACtD;gBAED,MAAMC,WAAW,IAAIrB;gBACrB,MAAMsB,YACJ;oBACE;wBAAC;wBAAYH;wBAAqB;qBAAgB;oBAClD;wBAAC;wBAAWC;wBAAoB;qBAAsB;oBACtD;wBAAC;wBAAWF;wBAAoB;qBAAY;iBAC7C,CACD,GAAG,CAAC,CAAC,CAACL,QAAQU,QAAQC,MAAM;oBAC5B,MAAMlC,UAAUiC,OAAO,iBAAiB,EAAE,WAAWA,OAAO,aAAa;oBACzE,MAAMvC,WAAWM,UACbP,gBAAgB0C,OAAOnC,YACvB;oBACJ,IAAI,CAAC+B,SAAS,GAAG,CAACrC,WAAWqC,SAAS,GAAG,CAACrC,UAAUqC,SAAS,IAAI,GAAG;oBACpE,OAAO;wBAACR;wBAAQU,OAAO,SAAS;wBAAEC;wBAAO,CAAC,CAAC,EAAEH,SAAS,GAAG,CAACrC,UAAU,CAAC,CAAC;qBAAC;gBACzE;gBACA4B,GAAG,MAAM,CACPvC,YAAY;oBAAC;oBAAc;oBAAS;oBAAS;iBAAW,EAAEiD;gBAE5DV,GAAG,MAAM,CAAC;gBACV,KAAK,MAAM,CAAC5B,UAAUN,MAAM,IAAI2C,SAC9BT,GAAG,MAAM,CAAC,CAAC,CAAC,EAAElC,MAAM,YAAY,EAAEM,UAAU;gBAE9C4B,GAAG,MAAM,CAAC;gBAEV,MAAMc,SAAS,MAAMT,KAAK,WAAW,CAAC;oBACpCC;oBACAC;oBACAC;gBACF;gBAEA,MAAMO,cAAc;oBAClB,MAAM;oBACN,QAAQ;oBACR,UAAU;gBACZ;gBACAf,GAAG,MAAM,CACPvC,YACE;oBAAC;oBAAc;oBAAS;oBAAU;oBAAY;iBAAO,EACrDqD,OAAO,MAAM,CAAC,GAAG,CAAC,CAACF,QAAU;wBAC3BA,MAAM,MAAM;wBACZG,WAAW,CAACH,MAAM,IAAI,CAAC;wBACvBA,MAAM,MAAM,GAAG,SAAS;wBACxBC,OAAOD,MAAM,YAAY;wBACzB,GAAGA,MAAM,UAAU,CAAC,GAAG,CAAC;qBACzB;gBAGL,MAAMI,mBAAmB,IAAI5B;gBAC7B,KAAK,MAAMwB,SAASE,OAAO,MAAM,CAAE;oBACjC,KAAK,MAAM1C,YAAYwC,MAAM,SAAS,CAAE;wBACtC,MAAMK,UAAU9C,gBAAgBC;wBAChC,IAAI,CAAC4C,iBAAiB,GAAG,CAACC,UACxBD,iBAAiB,GAAG,CAACC,SAAS,IAAIC;wBACpCF,iBAAiB,GAAG,CAACC,SAAU,GAAG,CAACL,MAAM,MAAM;oBACjD;oBACA,IAAI,CAACA,MAAM,MAAM,IAAIA,MAAM,OAAO,EAChCZ,GAAG,MAAM,CAAC,CAAC,EAAE,EAAEY,MAAM,MAAM,CAAC,EAAE,EAAEA,MAAM,OAAO,EAAE;gBACnD;gBACA,IAAII,iBAAiB,IAAI,GAAG,GAAG;oBAC7BhB,GAAG,MAAM,CAAC;oBACV,KAAK,MAAM,CAAC5B,UAAU+C,QAAQ,IAAIH,iBAChChB,GAAG,MAAM,CAAC,CAAC,EAAE,EAAE;2BAAImB;qBAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE/C,UAAU;gBAEzD;gBACA4B,GAAG,MAAM,CAAC;gBACV,MAAMoB,eAAeN,OAAO,MAAM,CAAC,MAAM,CAAC,CAACF,QAAUA,MAAM,MAAM,EAAE,MAAM;gBACzEZ,GAAG,MAAM,CACP,CAAC,eAAe,EAAEc,OAAO,YAAY,CAAC,UAAU,EAAEM,aAAa,SAAS,EAAEN,OAAO,MAAM,CAAC,MAAM,GAAGM,aAAa,QAAQ,CAAC;gBAGzH,IAAIN,OAAO,MAAM,EAAE;oBACjBd,GAAG,MAAM,CAAC;oBACV,OAAO;gBACT;gBAEAA,GAAG,MAAM,CAACR,+BAA+BsB,OAAO,OAAO,EAAEpB;gBACzD,OAAO;YACT,EAAE,OAAO2B,OAAO;gBACd,MAAM5B,UAAU4B,iBAAiB7D,QAAQ6D,MAAM,OAAO,GAAGR,OAAOQ;gBAChErB,GAAG,MAAM,CACP;oBACE9C;oBACA;oBACA;oBACAuC;oBACAvC;iBACD,CAAC,IAAI,CAAC;gBAET,OAAO;YACT;QACF;QAEO,eAAeoE,gBACpBC,OAAiB,EACjBlB,IAAgC,EAChCL,KAAqB;YACnB,QAAQwB,QAAQ,GAAG;YACnB,QAAQA,QAAQ,KAAK;QACvB,CAAC,EACDpE,cAAc,eAAe;YAE7B,MAAM,GAAGqE,QAAQ,GAAGC,SAAS,GAAGH;YAChC,MAAMI,aAAa;gBACjB,GAAG5B,eAAeC,GAAG;gBACrB,GAAGK,IAAI;YACT;YAEA,IAAI,CAACoB,UAAUA,AAAW,aAAXA,UAAuBA,AAAW,SAAXA,QAAiB;gBACrDzB,GAAG,MAAM,CAAC7C,kBAAkBC;gBAC5B,OAAO;YACT;YAEA,IAAIqE,AAAW,aAAXA,QACF,OAAOrB,sBAAsBsB,UAAUC,YAAY3B,IAAI5C;YAGzD,IAAIqE,AAAW,WAAXA,QAAmB;gBACrBzB,GAAG,MAAM,CACP,GAAG5C,YAAY,sFAAsF,CAAC;gBAExG,OAAO;YACT;YAEA4C,GAAG,MAAM,CACP,CAAC,QAAQ,EAAE5C,YAAY,gBAAgB,EAAEqE,OAAO,IAAI,EAAEtE,kBAAkBC,cAAc;YAExF,OAAO;QACT"}
|
|
1
|
+
{"version":3,"file":"1~77.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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"}
|
|
@@ -19,7 +19,7 @@ const MODEL_VERIFY_SEPARATOR = '────────────────
|
|
|
19
19
|
const modelCommandUsage = (commandName)=>`Usage:
|
|
20
20
|
${commandName} model verify
|
|
21
21
|
|
|
22
|
-
Verify model connectivity and Midscene compatibility for default, planning, and insight models.
|
|
22
|
+
Verify model connectivity and Midscene compatibility for default, locate, planning, and insight models.
|
|
23
23
|
Reads .env from the current working directory, overriding shell environment variables.
|
|
24
24
|
Shows endpoints, model names, request counts, and per-check results with duration.
|
|
25
25
|
Returns exit code 0 on success and 1 on failure.
|
|
@@ -147,6 +147,7 @@ async function runModelVerifyCommand(args, deps, io, commandName) {
|
|
|
147
147
|
deps.loadDotenv();
|
|
148
148
|
io.stdout('');
|
|
149
149
|
const defaultModelConfig = deps.getModelConfig('default');
|
|
150
|
+
const locateModelConfig = deps.getModelConfig('locate');
|
|
150
151
|
const planningModelConfig = deps.getModelConfig('planning');
|
|
151
152
|
const insightModelConfig = deps.getModelConfig('insight');
|
|
152
153
|
const curlCommands = buildModelVerifyCurlCommands([
|
|
@@ -154,6 +155,10 @@ async function runModelVerifyCommand(args, deps, io, commandName) {
|
|
|
154
155
|
intent: 'default',
|
|
155
156
|
modelConfig: defaultModelConfig
|
|
156
157
|
},
|
|
158
|
+
{
|
|
159
|
+
intent: 'locate',
|
|
160
|
+
modelConfig: locateModelConfig
|
|
161
|
+
},
|
|
157
162
|
{
|
|
158
163
|
intent: 'planning',
|
|
159
164
|
modelConfig: planningModelConfig
|
|
@@ -175,10 +180,15 @@ async function runModelVerifyCommand(args, deps, io, commandName) {
|
|
|
175
180
|
insightModelConfig,
|
|
176
181
|
'Image understanding'
|
|
177
182
|
],
|
|
183
|
+
[
|
|
184
|
+
'locate',
|
|
185
|
+
locateModelConfig,
|
|
186
|
+
'AI locate'
|
|
187
|
+
],
|
|
178
188
|
[
|
|
179
189
|
'default',
|
|
180
190
|
defaultModelConfig,
|
|
181
|
-
'
|
|
191
|
+
'Text response'
|
|
182
192
|
]
|
|
183
193
|
].map(([intent, config, check])=>{
|
|
184
194
|
const baseURL = config.openaiExtraConfig?.baseURL ?? config.openaiBaseURL;
|
|
@@ -199,9 +209,10 @@ async function runModelVerifyCommand(args, deps, io, commandName) {
|
|
|
199
209
|
], modelRows));
|
|
200
210
|
io.stdout('');
|
|
201
211
|
for (const [endpoint, index] of baseURLs)io.stdout(`[${index}] Base URL: ${endpoint}`);
|
|
202
|
-
io.stdout('\nRunning
|
|
212
|
+
io.stdout('\nRunning 4 checks...\n');
|
|
203
213
|
const result = await deps.verifyModel({
|
|
204
214
|
defaultModelConfig,
|
|
215
|
+
locateModelConfig,
|
|
205
216
|
planningModelConfig,
|
|
206
217
|
insightModelConfig
|
|
207
218
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli/model-command.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, 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 planningModelConfig = deps.getModelConfig('planning');\n const insightModelConfig = deps.getModelConfig('insight');\n const curlCommands = buildModelVerifyCurlCommands([\n { intent: 'default', modelConfig: defaultModelConfig },\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 ['default', defaultModelConfig, 'AI locate'],\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 3 checks...\\n');\n\n const result = await deps.verifyModel({\n defaultModelConfig,\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","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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"}
|
|
1
|
+
{"version":3,"file":"cli/model-command.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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"}
|
package/dist/es/test-cli.mjs
CHANGED
package/dist/lib/0~703.js
CHANGED
|
@@ -30,7 +30,7 @@ exports.modules = {
|
|
|
30
30
|
const modelCommandUsage = (commandName)=>`Usage:
|
|
31
31
|
${commandName} model verify
|
|
32
32
|
|
|
33
|
-
Verify model connectivity and Midscene compatibility for default, planning, and insight models.
|
|
33
|
+
Verify model connectivity and Midscene compatibility for default, locate, planning, and insight models.
|
|
34
34
|
Reads .env from the current working directory, overriding shell environment variables.
|
|
35
35
|
Shows endpoints, model names, request counts, and per-check results with duration.
|
|
36
36
|
Returns exit code 0 on success and 1 on failure.
|
|
@@ -158,6 +158,7 @@ Options:
|
|
|
158
158
|
deps.loadDotenv();
|
|
159
159
|
io.stdout('');
|
|
160
160
|
const defaultModelConfig = deps.getModelConfig('default');
|
|
161
|
+
const locateModelConfig = deps.getModelConfig('locate');
|
|
161
162
|
const planningModelConfig = deps.getModelConfig('planning');
|
|
162
163
|
const insightModelConfig = deps.getModelConfig('insight');
|
|
163
164
|
const curlCommands = buildModelVerifyCurlCommands([
|
|
@@ -165,6 +166,10 @@ Options:
|
|
|
165
166
|
intent: 'default',
|
|
166
167
|
modelConfig: defaultModelConfig
|
|
167
168
|
},
|
|
169
|
+
{
|
|
170
|
+
intent: 'locate',
|
|
171
|
+
modelConfig: locateModelConfig
|
|
172
|
+
},
|
|
168
173
|
{
|
|
169
174
|
intent: 'planning',
|
|
170
175
|
modelConfig: planningModelConfig
|
|
@@ -186,10 +191,15 @@ Options:
|
|
|
186
191
|
insightModelConfig,
|
|
187
192
|
'Image understanding'
|
|
188
193
|
],
|
|
194
|
+
[
|
|
195
|
+
'locate',
|
|
196
|
+
locateModelConfig,
|
|
197
|
+
'AI locate'
|
|
198
|
+
],
|
|
189
199
|
[
|
|
190
200
|
'default',
|
|
191
201
|
defaultModelConfig,
|
|
192
|
-
'
|
|
202
|
+
'Text response'
|
|
193
203
|
]
|
|
194
204
|
].map(([intent, config, check])=>{
|
|
195
205
|
const baseURL = config.openaiExtraConfig?.baseURL ?? config.openaiBaseURL;
|
|
@@ -210,9 +220,10 @@ Options:
|
|
|
210
220
|
], modelRows));
|
|
211
221
|
io.stdout('');
|
|
212
222
|
for (const [endpoint, index] of baseURLs)io.stdout(`[${index}] Base URL: ${endpoint}`);
|
|
213
|
-
io.stdout('\nRunning
|
|
223
|
+
io.stdout('\nRunning 4 checks...\n');
|
|
214
224
|
const result = await deps.verifyModel({
|
|
215
225
|
defaultModelConfig,
|
|
226
|
+
locateModelConfig,
|
|
216
227
|
planningModelConfig,
|
|
217
228
|
insightModelConfig
|
|
218
229
|
});
|
package/dist/lib/0~703.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"0~703.js","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, 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 planningModelConfig = deps.getModelConfig('planning');\n const insightModelConfig = deps.getModelConfig('insight');\n const curlCommands = buildModelVerifyCurlCommands([\n { intent: 'default', modelConfig: defaultModelConfig },\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 ['default', defaultModelConfig, 'AI locate'],\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 3 checks...\\n');\n\n const result = await deps.verifyModel({\n defaultModelConfig,\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","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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"}
|
|
1
|
+
{"version":3,"file":"0~703.js","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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"}
|
|
@@ -90,7 +90,7 @@ var __webpack_exports__ = {};
|
|
|
90
90
|
const modelCommandUsage = (commandName)=>`Usage:
|
|
91
91
|
${commandName} model verify
|
|
92
92
|
|
|
93
|
-
Verify model connectivity and Midscene compatibility for default, planning, and insight models.
|
|
93
|
+
Verify model connectivity and Midscene compatibility for default, locate, planning, and insight models.
|
|
94
94
|
Reads .env from the current working directory, overriding shell environment variables.
|
|
95
95
|
Shows endpoints, model names, request counts, and per-check results with duration.
|
|
96
96
|
Returns exit code 0 on success and 1 on failure.
|
|
@@ -218,6 +218,7 @@ Options:
|
|
|
218
218
|
deps.loadDotenv();
|
|
219
219
|
io.stdout('');
|
|
220
220
|
const defaultModelConfig = deps.getModelConfig('default');
|
|
221
|
+
const locateModelConfig = deps.getModelConfig('locate');
|
|
221
222
|
const planningModelConfig = deps.getModelConfig('planning');
|
|
222
223
|
const insightModelConfig = deps.getModelConfig('insight');
|
|
223
224
|
const curlCommands = buildModelVerifyCurlCommands([
|
|
@@ -225,6 +226,10 @@ Options:
|
|
|
225
226
|
intent: 'default',
|
|
226
227
|
modelConfig: defaultModelConfig
|
|
227
228
|
},
|
|
229
|
+
{
|
|
230
|
+
intent: 'locate',
|
|
231
|
+
modelConfig: locateModelConfig
|
|
232
|
+
},
|
|
228
233
|
{
|
|
229
234
|
intent: 'planning',
|
|
230
235
|
modelConfig: planningModelConfig
|
|
@@ -246,10 +251,15 @@ Options:
|
|
|
246
251
|
insightModelConfig,
|
|
247
252
|
'Image understanding'
|
|
248
253
|
],
|
|
254
|
+
[
|
|
255
|
+
'locate',
|
|
256
|
+
locateModelConfig,
|
|
257
|
+
'AI locate'
|
|
258
|
+
],
|
|
249
259
|
[
|
|
250
260
|
'default',
|
|
251
261
|
defaultModelConfig,
|
|
252
|
-
'
|
|
262
|
+
'Text response'
|
|
253
263
|
]
|
|
254
264
|
].map(([intent, config, check])=>{
|
|
255
265
|
const baseURL = config.openaiExtraConfig?.baseURL ?? config.openaiBaseURL;
|
|
@@ -270,9 +280,10 @@ Options:
|
|
|
270
280
|
], modelRows));
|
|
271
281
|
io.stdout('');
|
|
272
282
|
for (const [endpoint, index] of baseURLs)io.stdout(`[${index}] Base URL: ${endpoint}`);
|
|
273
|
-
io.stdout('\nRunning
|
|
283
|
+
io.stdout('\nRunning 4 checks...\n');
|
|
274
284
|
const result = await deps.verifyModel({
|
|
275
285
|
defaultModelConfig,
|
|
286
|
+
locateModelConfig,
|
|
276
287
|
planningModelConfig,
|
|
277
288
|
insightModelConfig
|
|
278
289
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli/model-command.js","sources":["webpack/runtime/compat_get_default_export","webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/cli/dotenv-loader.ts","../../../src/cli/model-command.ts"],"sourcesContent":["// getDefaultExport function for compatibility with non-ESM modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};\n","__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","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, 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 planningModelConfig = deps.getModelConfig('planning');\n const insightModelConfig = deps.getModelConfig('insight');\n const curlCommands = buildModelVerifyCurlCommands([\n { intent: 'default', modelConfig: defaultModelConfig },\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 ['default', defaultModelConfig, 'AI locate'],\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 3 checks...\\n');\n\n const result = await deps.verifyModel({\n defaultModelConfig,\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":["__webpack_require__","module","getter","definition","key","Object","obj","prop","Symbol","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","existing","formatModelVerifyFailureOutput","message","curlCommands","details","curlSection","baseUrlNote","chalk","getDefaultDeps","io","intent","globalModelConfigManager","runConnectivityTest","runModelVerifyCommand","deps","defaultModelConfig","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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"}
|
|
1
|
+
{"version":3,"file":"cli/model-command.js","sources":["webpack/runtime/compat_get_default_export","webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/cli/dotenv-loader.ts","../../../src/cli/model-command.ts"],"sourcesContent":["// getDefaultExport function for compatibility with non-ESM modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};\n","__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","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":["__webpack_require__","module","getter","definition","key","Object","obj","prop","Symbol","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","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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"}
|
package/dist/lib/test-cli.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@midscene/test",
|
|
3
3
|
"description": "Midscene Test: an extensible YAML testing framework and Node SDK.",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.16.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/web-infra-dev/midscene.git",
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
"tsx": "^4.19.2",
|
|
59
59
|
"yargs": "17.7.3",
|
|
60
60
|
"zod": "^3.25.1",
|
|
61
|
-
"@midscene/
|
|
62
|
-
"@midscene/
|
|
61
|
+
"@midscene/core": "1.16.0",
|
|
62
|
+
"@midscene/shared": "1.16.0"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@rslib/core": "^0.18.3",
|
|
@@ -71,20 +71,20 @@
|
|
|
71
71
|
"puppeteer": "24.6.0",
|
|
72
72
|
"typescript": "^5.8.3",
|
|
73
73
|
"vitest": "3.0.5",
|
|
74
|
-
"@midscene/
|
|
75
|
-
"@midscene/
|
|
76
|
-
"@midscene/
|
|
77
|
-
"@midscene/
|
|
78
|
-
"@midscene/
|
|
74
|
+
"@midscene/android": "1.16.0",
|
|
75
|
+
"@midscene/computer": "1.16.0",
|
|
76
|
+
"@midscene/ios": "1.16.0",
|
|
77
|
+
"@midscene/web": "1.16.0",
|
|
78
|
+
"@midscene/harmony": "1.16.0"
|
|
79
79
|
},
|
|
80
80
|
"peerDependencies": {
|
|
81
81
|
"playwright": "^1.45.0",
|
|
82
82
|
"puppeteer": "24.6.0",
|
|
83
|
-
"@midscene/
|
|
84
|
-
"@midscene/
|
|
85
|
-
"@midscene/harmony": "1.
|
|
86
|
-
"@midscene/ios": "1.
|
|
87
|
-
"@midscene/web": "1.
|
|
83
|
+
"@midscene/android": "1.16.0",
|
|
84
|
+
"@midscene/computer": "1.16.0",
|
|
85
|
+
"@midscene/harmony": "1.16.0",
|
|
86
|
+
"@midscene/ios": "1.16.0",
|
|
87
|
+
"@midscene/web": "1.16.0"
|
|
88
88
|
},
|
|
89
89
|
"peerDependenciesMeta": {
|
|
90
90
|
"@midscene/android": {
|