@midscene/cli 1.16.2 → 1.16.6-beta-20261023073257.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/index.mjs +73 -172
- package/dist/es/index.mjs.map +1 -1
- package/dist/lib/framework/index.js +6 -2
- package/dist/lib/framework/index.js.map +1 -1
- package/dist/lib/index.js +78 -172
- package/dist/lib/index.js.map +1 -1
- package/package.json +10 -10
package/dist/es/index.mjs
CHANGED
|
@@ -13,18 +13,18 @@ import { fileURLToPath as external_url_fileURLToPath } from "url";
|
|
|
13
13
|
import node_process, { cwd as external_node_process_cwd } from "node:process";
|
|
14
14
|
import lodash_merge from "lodash.merge";
|
|
15
15
|
import { loadDotenvConfig, runModelCommand } from "@midscene/test/internal/model-command";
|
|
16
|
-
import { createHash,
|
|
16
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
17
17
|
import { getMidsceneRunSubDir } from "@midscene/shared/common";
|
|
18
18
|
import { buildChromeArgs, buildDownloadBehavior, defaultViewportHeight, defaultViewportWidth } from "@midscene/web/puppeteer-agent-launcher";
|
|
19
19
|
import puppeteer from "puppeteer";
|
|
20
20
|
import { createYamlPlayer as yaml_runtime_createYamlPlayer } from "@midscene/test/internal/yaml-runtime";
|
|
21
|
-
import { stripVTControlCharacters } from "node:util";
|
|
21
|
+
import { inspect, stripVTControlCharacters } from "node:util";
|
|
22
22
|
import { createRequire } from "node:module";
|
|
23
23
|
import { serializeError } from "@midscene/shared/agent-tools/error-formatter";
|
|
24
24
|
import { spawn } from "node:child_process";
|
|
25
|
+
import { SiwcError, createSiwcAuthorization, exchangeSiwcCode, parseSiwcCallback, refreshSiwcCredentials, withSiwcStage } from "@midscene/core/ai-model";
|
|
25
26
|
import { homedir } from "node:os";
|
|
26
27
|
import { createServer } from "node:http";
|
|
27
|
-
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
28
28
|
import { classifyWorkflowDocumentFormat } from "@midscene/core/internal/test-runner";
|
|
29
29
|
import * as __rspack_external_assert from "assert";
|
|
30
30
|
import * as __rspack_external_fs from "fs";
|
|
@@ -2800,9 +2800,7 @@ var __webpack_modules__ = {
|
|
|
2800
2800
|
module.exports = (string, columns, options)=>String(string).normalize().replace(/\r\n/g, '\n').split('\n').map((line)=>exec(line, columns, options)).join('\n');
|
|
2801
2801
|
},
|
|
2802
2802
|
"./src/index.ts" (__unused_rspack_module, __unused_rspack___webpack_exports__, __webpack_require__) {
|
|
2803
|
-
var package_namespaceObject = {
|
|
2804
|
-
rE: "1.16.2"
|
|
2805
|
-
};
|
|
2803
|
+
var package_namespaceObject = JSON.parse('{"rE":"1.16.6-beta-20261023073257.0"}');
|
|
2806
2804
|
var brace_expansion = __webpack_require__("../../node_modules/.pnpm/brace-expansion@2.0.1/node_modules/brace-expansion/index.js");
|
|
2807
2805
|
const MAX_PATTERN_LENGTH = 65536;
|
|
2808
2806
|
const assertValidPattern = (pattern)=>{
|
|
@@ -8475,7 +8473,7 @@ Usage:
|
|
|
8475
8473
|
type: 'boolean',
|
|
8476
8474
|
description: 'Whether to dismiss the HarmonyOS keyboard after text input'
|
|
8477
8475
|
}
|
|
8478
|
-
}).version('version', 'Show version number', "1.16.
|
|
8476
|
+
}).version('version', 'Show version number', "1.16.6-beta-20261023073257.0").help().epilogue(`For complete list of configuration options, please visit:
|
|
8479
8477
|
• Web options: https://midscenejs.com/automate-with-scripts-in-yaml#the-web-part
|
|
8480
8478
|
• Android options: https://midscenejs.com/automate-with-scripts-in-yaml#the-android-part
|
|
8481
8479
|
• HarmonyOS options: https://midscenejs.com/automate-with-scripts-in-yaml#the-harmony-part
|
|
@@ -12490,19 +12488,31 @@ defineYamlBatchTest(test, testOptions);
|
|
|
12490
12488
|
const success = printExecutionSummary(results, summaryPath);
|
|
12491
12489
|
return success ? exitCode : 1;
|
|
12492
12490
|
}
|
|
12491
|
+
function describeSiwcError(error) {
|
|
12492
|
+
return inspect(error, {
|
|
12493
|
+
depth: null,
|
|
12494
|
+
colors: false,
|
|
12495
|
+
maxArrayLength: null,
|
|
12496
|
+
maxStringLength: null
|
|
12497
|
+
});
|
|
12498
|
+
}
|
|
12493
12499
|
const defaultCredentialsPath = ()=>join(homedir(), '.midscene', 'siwc.json');
|
|
12494
12500
|
async function readCredentialsFile(path) {
|
|
12495
12501
|
const content = await readFile(path, 'utf8').catch((error)=>{
|
|
12496
12502
|
if ('ENOENT' === error.code) return;
|
|
12497
|
-
throw new
|
|
12503
|
+
throw new SiwcError('Unable to read the local SIWC credentials file.', {
|
|
12504
|
+
cause: error
|
|
12505
|
+
});
|
|
12498
12506
|
});
|
|
12499
12507
|
if (void 0 === content) return;
|
|
12500
12508
|
try {
|
|
12501
12509
|
const value = JSON.parse(content);
|
|
12502
|
-
if (!value || 'string' != typeof value.credentials?.ext_agent_host_id || !value.credentials.ext_agent_host_id) throw new
|
|
12510
|
+
if (!value || 'string' != typeof value.credentials?.ext_agent_host_id || !value.credentials.ext_agent_host_id) throw new SiwcError('Invalid SIWC credentials record.');
|
|
12503
12511
|
return value;
|
|
12504
|
-
} catch
|
|
12505
|
-
throw new
|
|
12512
|
+
} catch (error) {
|
|
12513
|
+
throw new SiwcError('Invalid local SIWC credentials file.', {
|
|
12514
|
+
cause: error
|
|
12515
|
+
});
|
|
12506
12516
|
}
|
|
12507
12517
|
}
|
|
12508
12518
|
async function readRegistration(path) {
|
|
@@ -12541,8 +12551,10 @@ defineYamlBatchTest(test, testOptions);
|
|
|
12541
12551
|
mode: 448
|
|
12542
12552
|
});
|
|
12543
12553
|
} catch (error) {
|
|
12544
|
-
if ('EEXIST' === error.code) throw new
|
|
12545
|
-
throw new
|
|
12554
|
+
if ('EEXIST' === error.code) throw new SiwcError(`Another SIWC command holds ${lockPath}. If it was interrupted, remove this lock only after confirming that no SIWC command is running.`);
|
|
12555
|
+
throw new SiwcError('Unable to lock the local SIWC credentials file.', {
|
|
12556
|
+
cause: error
|
|
12557
|
+
});
|
|
12546
12558
|
}
|
|
12547
12559
|
return ()=>rm(lockPath, {
|
|
12548
12560
|
recursive: true,
|
|
@@ -12557,18 +12569,13 @@ defineYamlBatchTest(test, testOptions);
|
|
|
12557
12569
|
'access_token',
|
|
12558
12570
|
'refresh_token',
|
|
12559
12571
|
'id_token'
|
|
12560
|
-
].some((key)=>'string' != typeof value[key] || !value[key]) || !Array.isArray(value.scopes) || !value.scopes.every((scope)=>'string' == typeof scope) || !Number.isFinite(value.expires_at)) throw new
|
|
12572
|
+
].some((key)=>'string' != typeof value[key] || !value[key]) || !Array.isArray(value.scopes) || !value.scopes.every((scope)=>'string' == typeof scope) || !Number.isFinite(value.expires_at)) throw new SiwcError('No valid SIWC credentials. Run midscene model siwc login first.');
|
|
12561
12573
|
return value;
|
|
12562
12574
|
}
|
|
12563
|
-
const ISSUER = 'https://auth.openai.com';
|
|
12564
|
-
const RESOURCE = 'https://api.openai.com/v1';
|
|
12565
|
-
const SCOPES = 'openid profile email offline_access resource.invoke chatgpt.tokens.use.direct';
|
|
12566
12575
|
async function loginWithOpenAI(options, dependencies) {
|
|
12567
12576
|
const hostId = options.hostId ?? `urn:uuid:${randomUUID()}`;
|
|
12568
12577
|
const existingClientId = options.clientId;
|
|
12569
|
-
const
|
|
12570
|
-
const nonce = randomBytes(32).toString('base64url');
|
|
12571
|
-
const verifier = randomBytes(32).toString('base64url');
|
|
12578
|
+
const context = {};
|
|
12572
12579
|
const signal = options.signal;
|
|
12573
12580
|
signal.throwIfAborted();
|
|
12574
12581
|
const server = createServer();
|
|
@@ -12582,17 +12589,24 @@ defineYamlBatchTest(test, testOptions);
|
|
|
12582
12589
|
response.setHeader('Referrer-Policy', 'no-referrer');
|
|
12583
12590
|
const url = new URL(request.url || '/', 'http://127.0.0.1');
|
|
12584
12591
|
if ('GET' !== request.method || '/auth/callback' !== url.pathname) return void response.writeHead(404).end('Not found');
|
|
12585
|
-
if (
|
|
12592
|
+
if (!context.attempt) return void response.writeHead(400).end('Authorization has not started.');
|
|
12586
12593
|
if (consumed.value) return void response.writeHead(409).end('Authorization already received.');
|
|
12587
|
-
|
|
12588
|
-
|
|
12589
|
-
|
|
12590
|
-
|
|
12594
|
+
const parsed = (()=>{
|
|
12595
|
+
try {
|
|
12596
|
+
return parseSiwcCallback(new URL(request.url || '/', context.attempt.redirectUri).toString(), context.attempt);
|
|
12597
|
+
} catch (error) {
|
|
12598
|
+
response.writeHead(400).end('Invalid authorization callback.');
|
|
12599
|
+
if (url.searchParams.has('error')) {
|
|
12600
|
+
consumed.value = true;
|
|
12601
|
+
reject(error);
|
|
12602
|
+
}
|
|
12603
|
+
return;
|
|
12604
|
+
}
|
|
12605
|
+
})();
|
|
12606
|
+
if (!parsed) {
|
|
12607
|
+
if (!response.writableEnded) response.writeHead(400).end('Invalid login state. Use the original authorization link.');
|
|
12591
12608
|
return;
|
|
12592
12609
|
}
|
|
12593
|
-
const code = url.searchParams.get('code');
|
|
12594
|
-
const clientId = url.searchParams.get('client_id') || existingClientId;
|
|
12595
|
-
if (!code || !clientId || 'dynamic_agent_client' === clientId || existingClientId && clientId !== existingClientId || 1 !== url.searchParams.getAll('code').length || url.searchParams.getAll('client_id').length > 1) return void response.writeHead(400).end('Invalid authorization callback.');
|
|
12596
12610
|
consumed.value = true;
|
|
12597
12611
|
response.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
12598
12612
|
response.end(`<!doctype html>
|
|
@@ -12618,10 +12632,7 @@ defineYamlBatchTest(test, testOptions);
|
|
|
12618
12632
|
</main>
|
|
12619
12633
|
</body>
|
|
12620
12634
|
</html>`);
|
|
12621
|
-
resolve(
|
|
12622
|
-
code,
|
|
12623
|
-
clientId
|
|
12624
|
-
});
|
|
12635
|
+
resolve(parsed);
|
|
12625
12636
|
});
|
|
12626
12637
|
server.on('error', reject);
|
|
12627
12638
|
});
|
|
@@ -12640,132 +12651,22 @@ defineYamlBatchTest(test, testOptions);
|
|
|
12640
12651
|
});
|
|
12641
12652
|
signal.throwIfAborted();
|
|
12642
12653
|
const redirectUri = `http://127.0.0.1:${server.address().port}/auth/callback`;
|
|
12643
|
-
const
|
|
12644
|
-
|
|
12645
|
-
|
|
12646
|
-
|
|
12647
|
-
|
|
12648
|
-
},
|
|
12649
|
-
ext_agent_host_id: hostId,
|
|
12650
|
-
response_type: 'code',
|
|
12651
|
-
redirect_uri: redirectUri,
|
|
12652
|
-
scope: SCOPES,
|
|
12653
|
-
resource: RESOURCE,
|
|
12654
|
-
state,
|
|
12655
|
-
nonce,
|
|
12656
|
-
code_challenge_method: 'S256',
|
|
12657
|
-
code_challenge: createHash('sha256').update(verifier).digest('base64url')
|
|
12658
|
-
}).toString();
|
|
12659
|
-
await options.onAuthorization(authorization.toString());
|
|
12660
|
-
const { code, clientId } = await callback;
|
|
12661
|
-
signal.throwIfAborted();
|
|
12662
|
-
const request = dependencies?.fetch ?? fetch;
|
|
12663
|
-
const result = await request(`${ISSUER}/api/accounts/oauth/token`, {
|
|
12664
|
-
method: 'POST',
|
|
12665
|
-
headers: {
|
|
12666
|
-
'Content-Type': 'application/x-www-form-urlencoded'
|
|
12667
|
-
},
|
|
12668
|
-
body: new URLSearchParams({
|
|
12669
|
-
grant_type: 'authorization_code',
|
|
12670
|
-
client_id: clientId,
|
|
12671
|
-
code,
|
|
12672
|
-
code_verifier: verifier,
|
|
12673
|
-
redirect_uri: redirectUri,
|
|
12674
|
-
resource: RESOURCE
|
|
12675
|
-
}),
|
|
12676
|
-
signal,
|
|
12677
|
-
redirect: 'error'
|
|
12654
|
+
const { attempt, url } = await createSiwcAuthorization({
|
|
12655
|
+
hostId,
|
|
12656
|
+
clientId: existingClientId,
|
|
12657
|
+
redirectUri,
|
|
12658
|
+
agentName: 'Midscene'
|
|
12678
12659
|
});
|
|
12679
|
-
|
|
12680
|
-
|
|
12681
|
-
|
|
12682
|
-
|
|
12683
|
-
if (!scopes.includes('chatgpt.tokens.use.direct')) throw new Error('ChatGPT plan usage was not authorized. Run midscene model siwc login again and grant access.');
|
|
12684
|
-
const keys = dependencies?.keys ?? createRemoteJWKSet(new URL(`${ISSUER}/.well-known/jwks.json`));
|
|
12685
|
-
const { payload } = await jwtVerify(tokens.id_token, keys, {
|
|
12686
|
-
issuer: ISSUER,
|
|
12687
|
-
audience: clientId,
|
|
12688
|
-
algorithms: [
|
|
12689
|
-
'RS256'
|
|
12690
|
-
],
|
|
12691
|
-
requiredClaims: [
|
|
12692
|
-
'sub',
|
|
12693
|
-
'exp',
|
|
12694
|
-
'iat',
|
|
12695
|
-
'nonce'
|
|
12696
|
-
]
|
|
12697
|
-
});
|
|
12698
|
-
if (payload.nonce !== nonce || !payload.sub) throw new Error('OpenAI login identity or nonce did not match.');
|
|
12699
|
-
signal.throwIfAborted();
|
|
12700
|
-
return {
|
|
12701
|
-
ext_agent_host_id: hostId,
|
|
12702
|
-
client_id: clientId,
|
|
12703
|
-
subject: payload.sub,
|
|
12704
|
-
issuer: ISSUER,
|
|
12705
|
-
...'string' == typeof payload.email ? {
|
|
12706
|
-
email: payload.email
|
|
12707
|
-
} : {},
|
|
12708
|
-
access_token: tokens.access_token,
|
|
12709
|
-
refresh_token: tokens.refresh_token,
|
|
12710
|
-
id_token: tokens.id_token,
|
|
12711
|
-
scopes,
|
|
12712
|
-
expires_at: Date.now() + 1000 * tokens.expires_in
|
|
12713
|
-
};
|
|
12660
|
+
context.attempt = attempt;
|
|
12661
|
+
await options.onAuthorization(url);
|
|
12662
|
+
const result = await withSiwcStage('Authorization callback', ()=>callback);
|
|
12663
|
+
return await exchangeSiwcCode(attempt, result, signal, dependencies);
|
|
12714
12664
|
} finally{
|
|
12715
12665
|
signal.removeEventListener('abort', abort);
|
|
12716
12666
|
server.closeAllConnections();
|
|
12717
12667
|
await new Promise((resolve)=>server.close(()=>resolve()));
|
|
12718
12668
|
}
|
|
12719
12669
|
}
|
|
12720
|
-
async function refreshOpenAICredentials(saved, signal, dependencies) {
|
|
12721
|
-
const issuer = 'https://auth.openai.com';
|
|
12722
|
-
signal.throwIfAborted();
|
|
12723
|
-
const response = await (dependencies?.fetch ?? fetch)(`${issuer}/api/accounts/oauth/token`, {
|
|
12724
|
-
method: 'POST',
|
|
12725
|
-
headers: {
|
|
12726
|
-
'Content-Type': 'application/x-www-form-urlencoded'
|
|
12727
|
-
},
|
|
12728
|
-
body: new URLSearchParams({
|
|
12729
|
-
grant_type: 'refresh_token',
|
|
12730
|
-
client_id: saved.client_id,
|
|
12731
|
-
refresh_token: saved.refresh_token,
|
|
12732
|
-
resource: 'https://api.openai.com/v1'
|
|
12733
|
-
}),
|
|
12734
|
-
signal,
|
|
12735
|
-
redirect: 'error'
|
|
12736
|
-
});
|
|
12737
|
-
if (!response.ok) throw new Error('OpenAI refresh failed.');
|
|
12738
|
-
const tokens = await response.json();
|
|
12739
|
-
if (!tokens || 'string' != typeof tokens.access_token || !tokens.access_token || 'string' != typeof tokens.refresh_token || !tokens.refresh_token || 'number' != typeof tokens.expires_in || !Number.isFinite(tokens.expires_in) || tokens.expires_in <= 0 || 'string' != typeof tokens.token_type || 'bearer' !== tokens.token_type.toLowerCase() || 'string' != typeof tokens.scope) throw new Error('OpenAI returned an invalid refresh response.');
|
|
12740
|
-
const scopes = tokens.scope.split(/\s+/);
|
|
12741
|
-
if (!scopes.includes('chatgpt.tokens.use.direct')) throw new Error('ChatGPT plan usage is no longer authorized.');
|
|
12742
|
-
if (void 0 !== tokens.id_token) {
|
|
12743
|
-
if ('string' != typeof tokens.id_token || !tokens.id_token) throw new Error('Invalid refreshed ID token.');
|
|
12744
|
-
const keys = dependencies?.keys ?? createRemoteJWKSet(new URL(`${issuer}/.well-known/jwks.json`));
|
|
12745
|
-
const { payload } = await jwtVerify(tokens.id_token, keys, {
|
|
12746
|
-
issuer,
|
|
12747
|
-
audience: saved.client_id,
|
|
12748
|
-
algorithms: [
|
|
12749
|
-
'RS256'
|
|
12750
|
-
],
|
|
12751
|
-
requiredClaims: [
|
|
12752
|
-
'sub',
|
|
12753
|
-
'exp',
|
|
12754
|
-
'iat'
|
|
12755
|
-
]
|
|
12756
|
-
});
|
|
12757
|
-
if (payload.sub !== saved.subject) throw new Error('Refreshed identity does not match the saved account.');
|
|
12758
|
-
}
|
|
12759
|
-
signal.throwIfAborted();
|
|
12760
|
-
return {
|
|
12761
|
-
...saved,
|
|
12762
|
-
access_token: tokens.access_token,
|
|
12763
|
-
refresh_token: tokens.refresh_token,
|
|
12764
|
-
id_token: tokens.id_token ?? saved.id_token,
|
|
12765
|
-
scopes,
|
|
12766
|
-
expires_at: Date.now() + 1000 * tokens.expires_in
|
|
12767
|
-
};
|
|
12768
|
-
}
|
|
12769
12670
|
const USAGE = `Usage:
|
|
12770
12671
|
midscene model siwc login [--no-open] [--port <port>]
|
|
12771
12672
|
midscene model siwc refresh
|
|
@@ -12838,7 +12739,7 @@ Options (login):
|
|
|
12838
12739
|
}
|
|
12839
12740
|
}
|
|
12840
12741
|
const unlock = await lockCredentials(credentialsPath).catch((error)=>{
|
|
12841
|
-
io.stderr(error
|
|
12742
|
+
io.stderr(describeSiwcError(error));
|
|
12842
12743
|
});
|
|
12843
12744
|
if (!unlock) return 1;
|
|
12844
12745
|
const controller = new AbortController();
|
|
@@ -12848,31 +12749,31 @@ Options (login):
|
|
|
12848
12749
|
process.once('SIGTERM', cancel);
|
|
12849
12750
|
try {
|
|
12850
12751
|
io.stderr(source_default().cyan.bold(`🔄 Starting OpenAI SIWC ${action}...`));
|
|
12851
|
-
const saved = await readRegistration(credentialsPath);
|
|
12752
|
+
const saved = await withSiwcStage('Read credentials', ()=>readRegistration(credentialsPath));
|
|
12852
12753
|
const credentials = await (async ()=>{
|
|
12853
|
-
if ('refresh' === action) return
|
|
12754
|
+
if ('refresh' === action) return withSiwcStage('Refresh', ()=>refreshSiwcCredentials(requireCredentials(saved), controller.signal));
|
|
12854
12755
|
const registration = saved ?? {
|
|
12855
12756
|
ext_agent_host_id: `urn:uuid:${randomUUID()}`
|
|
12856
12757
|
};
|
|
12857
|
-
if (!saved) await saveRegistration(credentialsPath, registration);
|
|
12858
|
-
const result = await loginWithOpenAI({
|
|
12859
|
-
|
|
12860
|
-
|
|
12861
|
-
|
|
12862
|
-
|
|
12863
|
-
|
|
12864
|
-
|
|
12865
|
-
|
|
12866
|
-
|
|
12867
|
-
|
|
12868
|
-
|
|
12758
|
+
if (!saved) await withSiwcStage('Save registration', ()=>saveRegistration(credentialsPath, registration));
|
|
12759
|
+
const result = await withSiwcStage('Login', ()=>loginWithOpenAI({
|
|
12760
|
+
port: portOptions.port,
|
|
12761
|
+
hostId: registration.ext_agent_host_id,
|
|
12762
|
+
clientId: registration.client_id,
|
|
12763
|
+
signal: controller.signal,
|
|
12764
|
+
onAuthorization: async (url)=>{
|
|
12765
|
+
io.stderr(`\n${source_default().bold('Continue with ChatGPT:')}\n${url}\n\n${source_default().gray('Waiting for authorization...')}`);
|
|
12766
|
+
if (!portOptions.noOpen) try {
|
|
12767
|
+
await openBrowser(url);
|
|
12768
|
+
} catch {
|
|
12769
|
+
io.stderr('Open the URL above in your browser to continue.');
|
|
12770
|
+
}
|
|
12869
12771
|
}
|
|
12870
|
-
}
|
|
12871
|
-
|
|
12872
|
-
if (registration.subject && result.subject !== registration.subject) throw new Error('Login identity does not match the saved account.');
|
|
12772
|
+
}));
|
|
12773
|
+
if (registration.subject && result.subject !== registration.subject) throw new SiwcError('Login identity does not match the saved account.');
|
|
12873
12774
|
return result;
|
|
12874
12775
|
})();
|
|
12875
|
-
await saveRegistration(credentialsPath, credentials);
|
|
12776
|
+
await withSiwcStage('Save credentials', ()=>saveRegistration(credentialsPath, credentials));
|
|
12876
12777
|
io.stderr('\n🔑 your Access token:');
|
|
12877
12778
|
io.stdout(credentials.access_token);
|
|
12878
12779
|
const expiresAt = new Intl.DateTimeFormat('en-GB', {
|
|
@@ -12886,8 +12787,8 @@ Options (login):
|
|
|
12886
12787
|
io.stderr(`\nExpires at: ${expiresAt} (${expiresIn})`);
|
|
12887
12788
|
io.stderr(`\n${source_default().green(`✅ SIWC ${action} succeeded.`)}\n\nYou can use this access token as your model API Key in Midscene by setting MIDSCENE_MODEL_API_KEY.\n\nFull credentials are saved locally to:\n${source_default().cyan(credentialsPath)}\n\n${source_default().gray('Midscene does not collect your credentials.')}`);
|
|
12888
12789
|
return 0;
|
|
12889
|
-
} catch
|
|
12890
|
-
io.stderr(source_default().red(controller.signal.aborted ? `OpenAI ${action} cancelled or timed out
|
|
12790
|
+
} catch (error) {
|
|
12791
|
+
io.stderr(source_default().red(controller.signal.aborted ? `OpenAI ${action} cancelled or timed out.\n${describeSiwcError(error)}` : `OpenAI ${action} failed.\n${describeSiwcError(error)}`));
|
|
12891
12792
|
return 1;
|
|
12892
12793
|
} finally{
|
|
12893
12794
|
clearTimeout(timer);
|