@midscene/cli 1.14.0 ā 1.15.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 +418 -398
- 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 +422 -398
- package/dist/lib/index.js.map +1 -1
- package/package.json +10 -9
package/dist/es/index.mjs
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
/*! For license information please see index.mjs.LICENSE.txt */
|
|
2
|
-
import { ReportMergingTool, createReportCliCommands
|
|
2
|
+
import { ReportMergingTool, createReportCliCommands } from "@midscene/core";
|
|
3
3
|
import { runToolsCLI } from "@midscene/shared/cli";
|
|
4
4
|
import { basename, dirname as external_node_path_dirname, extname, join, posix, relative as external_node_path_relative, resolve as external_node_path_resolve, sep as external_node_path_sep, win32 } from "node:path";
|
|
5
5
|
import { interpolateEnvVars, midsceneYamlTargetKeys, parseYamlScript as yaml_parseYamlScript, resolveWebTarget } from "@midscene/core/yaml";
|
|
6
6
|
import { getDebug } from "@midscene/shared/logger";
|
|
7
7
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
8
|
-
import { lstat, readdir, readlink, realpath } from "node:fs/promises";
|
|
8
|
+
import { lstat, mkdir, readFile, readdir, readlink, realpath, rename, rm, writeFile } from "node:fs/promises";
|
|
9
9
|
import { EventEmitter } from "node:events";
|
|
10
10
|
import node_stream from "node:stream";
|
|
11
11
|
import { StringDecoder } from "node:string_decoder";
|
|
12
12
|
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
|
-
import {
|
|
15
|
+
import { loadDotenvConfig, runModelCommand } from "@midscene/test/internal/model-command";
|
|
16
|
+
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
16
17
|
import { getMidsceneRunSubDir } from "@midscene/shared/common";
|
|
17
18
|
import { buildChromeArgs, buildDownloadBehavior, defaultViewportHeight, defaultViewportWidth } from "@midscene/web/puppeteer-agent-launcher";
|
|
18
19
|
import puppeteer from "puppeteer";
|
|
@@ -20,10 +21,12 @@ import { createYamlPlayer as yaml_runtime_createYamlPlayer } from "@midscene/tes
|
|
|
20
21
|
import { stripVTControlCharacters } from "node:util";
|
|
21
22
|
import { createRequire } from "node:module";
|
|
22
23
|
import { serializeError } from "@midscene/shared/agent-tools/error-formatter";
|
|
23
|
-
import {
|
|
24
|
+
import { spawn } from "node:child_process";
|
|
25
|
+
import { homedir } from "node:os";
|
|
26
|
+
import { createServer } from "node:http";
|
|
27
|
+
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
24
28
|
import { classifyWorkflowDocumentFormat } from "@midscene/core/internal/test-runner";
|
|
25
29
|
import * as __rspack_external_assert from "assert";
|
|
26
|
-
import * as __rspack_external_crypto from "crypto";
|
|
27
30
|
import * as __rspack_external_fs from "fs";
|
|
28
31
|
import * as __rspack_external_os from "os";
|
|
29
32
|
import * as __rspack_external_path from "path";
|
|
@@ -2475,232 +2478,6 @@ var __webpack_modules__ = {
|
|
|
2475
2478
|
]
|
|
2476
2479
|
};
|
|
2477
2480
|
},
|
|
2478
|
-
"../../node_modules/.pnpm/dotenv@16.4.5/node_modules/dotenv/lib/main.js" (module, __unused_rspack_exports, __webpack_require__) {
|
|
2479
|
-
const fs = __webpack_require__("fs");
|
|
2480
|
-
const path = __webpack_require__("path");
|
|
2481
|
-
const os = __webpack_require__("os");
|
|
2482
|
-
const crypto = __webpack_require__("crypto");
|
|
2483
|
-
const packageJson = __webpack_require__("../../node_modules/.pnpm/dotenv@16.4.5/node_modules/dotenv/package.json");
|
|
2484
|
-
const version = packageJson.version;
|
|
2485
|
-
const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg;
|
|
2486
|
-
function parse(src) {
|
|
2487
|
-
const obj = {};
|
|
2488
|
-
let lines = src.toString();
|
|
2489
|
-
lines = lines.replace(/\r\n?/mg, '\n');
|
|
2490
|
-
let match;
|
|
2491
|
-
while(null != (match = LINE.exec(lines))){
|
|
2492
|
-
const key = match[1];
|
|
2493
|
-
let value = match[2] || '';
|
|
2494
|
-
value = value.trim();
|
|
2495
|
-
const maybeQuote = value[0];
|
|
2496
|
-
value = value.replace(/^(['"`])([\s\S]*)\1$/mg, '$2');
|
|
2497
|
-
if ('"' === maybeQuote) {
|
|
2498
|
-
value = value.replace(/\\n/g, '\n');
|
|
2499
|
-
value = value.replace(/\\r/g, '\r');
|
|
2500
|
-
}
|
|
2501
|
-
obj[key] = value;
|
|
2502
|
-
}
|
|
2503
|
-
return obj;
|
|
2504
|
-
}
|
|
2505
|
-
function _parseVault(options) {
|
|
2506
|
-
const vaultPath = _vaultPath(options);
|
|
2507
|
-
const result = DotenvModule.configDotenv({
|
|
2508
|
-
path: vaultPath
|
|
2509
|
-
});
|
|
2510
|
-
if (!result.parsed) {
|
|
2511
|
-
const err = new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`);
|
|
2512
|
-
err.code = 'MISSING_DATA';
|
|
2513
|
-
throw err;
|
|
2514
|
-
}
|
|
2515
|
-
const keys = _dotenvKey(options).split(',');
|
|
2516
|
-
const length = keys.length;
|
|
2517
|
-
let decrypted;
|
|
2518
|
-
for(let i = 0; i < length; i++)try {
|
|
2519
|
-
const key = keys[i].trim();
|
|
2520
|
-
const attrs = _instructions(result, key);
|
|
2521
|
-
decrypted = DotenvModule.decrypt(attrs.ciphertext, attrs.key);
|
|
2522
|
-
break;
|
|
2523
|
-
} catch (error) {
|
|
2524
|
-
if (i + 1 >= length) throw error;
|
|
2525
|
-
}
|
|
2526
|
-
return DotenvModule.parse(decrypted);
|
|
2527
|
-
}
|
|
2528
|
-
function _log(message) {
|
|
2529
|
-
console.log(`[dotenv@${version}][INFO] ${message}`);
|
|
2530
|
-
}
|
|
2531
|
-
function _warn(message) {
|
|
2532
|
-
console.log(`[dotenv@${version}][WARN] ${message}`);
|
|
2533
|
-
}
|
|
2534
|
-
function _debug(message) {
|
|
2535
|
-
console.log(`[dotenv@${version}][DEBUG] ${message}`);
|
|
2536
|
-
}
|
|
2537
|
-
function _dotenvKey(options) {
|
|
2538
|
-
if (options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0) return options.DOTENV_KEY;
|
|
2539
|
-
if (process.env.DOTENV_KEY && process.env.DOTENV_KEY.length > 0) return process.env.DOTENV_KEY;
|
|
2540
|
-
return '';
|
|
2541
|
-
}
|
|
2542
|
-
function _instructions(result, dotenvKey) {
|
|
2543
|
-
let uri;
|
|
2544
|
-
try {
|
|
2545
|
-
uri = new URL(dotenvKey);
|
|
2546
|
-
} catch (error) {
|
|
2547
|
-
if ('ERR_INVALID_URL' === error.code) {
|
|
2548
|
-
const err = new Error('INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development');
|
|
2549
|
-
err.code = 'INVALID_DOTENV_KEY';
|
|
2550
|
-
throw err;
|
|
2551
|
-
}
|
|
2552
|
-
throw error;
|
|
2553
|
-
}
|
|
2554
|
-
const key = uri.password;
|
|
2555
|
-
if (!key) {
|
|
2556
|
-
const err = new Error('INVALID_DOTENV_KEY: Missing key part');
|
|
2557
|
-
err.code = 'INVALID_DOTENV_KEY';
|
|
2558
|
-
throw err;
|
|
2559
|
-
}
|
|
2560
|
-
const environment = uri.searchParams.get('environment');
|
|
2561
|
-
if (!environment) {
|
|
2562
|
-
const err = new Error('INVALID_DOTENV_KEY: Missing environment part');
|
|
2563
|
-
err.code = 'INVALID_DOTENV_KEY';
|
|
2564
|
-
throw err;
|
|
2565
|
-
}
|
|
2566
|
-
const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`;
|
|
2567
|
-
const ciphertext = result.parsed[environmentKey];
|
|
2568
|
-
if (!ciphertext) {
|
|
2569
|
-
const err = new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`);
|
|
2570
|
-
err.code = 'NOT_FOUND_DOTENV_ENVIRONMENT';
|
|
2571
|
-
throw err;
|
|
2572
|
-
}
|
|
2573
|
-
return {
|
|
2574
|
-
ciphertext,
|
|
2575
|
-
key
|
|
2576
|
-
};
|
|
2577
|
-
}
|
|
2578
|
-
function _vaultPath(options) {
|
|
2579
|
-
let possibleVaultPath = null;
|
|
2580
|
-
if (options && options.path && options.path.length > 0) if (Array.isArray(options.path)) {
|
|
2581
|
-
for (const filepath of options.path)if (fs.existsSync(filepath)) possibleVaultPath = filepath.endsWith('.vault') ? filepath : `${filepath}.vault`;
|
|
2582
|
-
} else possibleVaultPath = options.path.endsWith('.vault') ? options.path : `${options.path}.vault`;
|
|
2583
|
-
else possibleVaultPath = path.resolve(process.cwd(), '.env.vault');
|
|
2584
|
-
if (fs.existsSync(possibleVaultPath)) return possibleVaultPath;
|
|
2585
|
-
return null;
|
|
2586
|
-
}
|
|
2587
|
-
function _resolveHome(envPath) {
|
|
2588
|
-
return '~' === envPath[0] ? path.join(os.homedir(), envPath.slice(1)) : envPath;
|
|
2589
|
-
}
|
|
2590
|
-
function _configVault(options) {
|
|
2591
|
-
_log('Loading env from encrypted .env.vault');
|
|
2592
|
-
const parsed = DotenvModule._parseVault(options);
|
|
2593
|
-
let processEnv = process.env;
|
|
2594
|
-
if (options && null != options.processEnv) processEnv = options.processEnv;
|
|
2595
|
-
DotenvModule.populate(processEnv, parsed, options);
|
|
2596
|
-
return {
|
|
2597
|
-
parsed
|
|
2598
|
-
};
|
|
2599
|
-
}
|
|
2600
|
-
function configDotenv(options) {
|
|
2601
|
-
const dotenvPath = path.resolve(process.cwd(), '.env');
|
|
2602
|
-
let encoding = 'utf8';
|
|
2603
|
-
const debug = Boolean(options && options.debug);
|
|
2604
|
-
if (options && options.encoding) encoding = options.encoding;
|
|
2605
|
-
else if (debug) _debug('No encoding is specified. UTF-8 is used by default');
|
|
2606
|
-
let optionPaths = [
|
|
2607
|
-
dotenvPath
|
|
2608
|
-
];
|
|
2609
|
-
if (options && options.path) if (Array.isArray(options.path)) {
|
|
2610
|
-
optionPaths = [];
|
|
2611
|
-
for (const filepath of options.path)optionPaths.push(_resolveHome(filepath));
|
|
2612
|
-
} else optionPaths = [
|
|
2613
|
-
_resolveHome(options.path)
|
|
2614
|
-
];
|
|
2615
|
-
let lastError;
|
|
2616
|
-
const parsedAll = {};
|
|
2617
|
-
for (const path of optionPaths)try {
|
|
2618
|
-
const parsed = DotenvModule.parse(fs.readFileSync(path, {
|
|
2619
|
-
encoding
|
|
2620
|
-
}));
|
|
2621
|
-
DotenvModule.populate(parsedAll, parsed, options);
|
|
2622
|
-
} catch (e1) {
|
|
2623
|
-
if (debug) _debug(`Failed to load ${path} ${e1.message}`);
|
|
2624
|
-
lastError = e1;
|
|
2625
|
-
}
|
|
2626
|
-
let processEnv = process.env;
|
|
2627
|
-
if (options && null != options.processEnv) processEnv = options.processEnv;
|
|
2628
|
-
DotenvModule.populate(processEnv, parsedAll, options);
|
|
2629
|
-
if (lastError) return {
|
|
2630
|
-
parsed: parsedAll,
|
|
2631
|
-
error: lastError
|
|
2632
|
-
};
|
|
2633
|
-
return {
|
|
2634
|
-
parsed: parsedAll
|
|
2635
|
-
};
|
|
2636
|
-
}
|
|
2637
|
-
function config(options) {
|
|
2638
|
-
if (0 === _dotenvKey(options).length) return DotenvModule.configDotenv(options);
|
|
2639
|
-
const vaultPath = _vaultPath(options);
|
|
2640
|
-
if (!vaultPath) {
|
|
2641
|
-
_warn(`You set DOTENV_KEY but you are missing a .env.vault file at ${vaultPath}. Did you forget to build it?`);
|
|
2642
|
-
return DotenvModule.configDotenv(options);
|
|
2643
|
-
}
|
|
2644
|
-
return DotenvModule._configVault(options);
|
|
2645
|
-
}
|
|
2646
|
-
function decrypt(encrypted, keyStr) {
|
|
2647
|
-
const key = Buffer.from(keyStr.slice(-64), 'hex');
|
|
2648
|
-
let ciphertext = Buffer.from(encrypted, 'base64');
|
|
2649
|
-
const nonce = ciphertext.subarray(0, 12);
|
|
2650
|
-
const authTag = ciphertext.subarray(-16);
|
|
2651
|
-
ciphertext = ciphertext.subarray(12, -16);
|
|
2652
|
-
try {
|
|
2653
|
-
const aesgcm = crypto.createDecipheriv('aes-256-gcm', key, nonce);
|
|
2654
|
-
aesgcm.setAuthTag(authTag);
|
|
2655
|
-
return `${aesgcm.update(ciphertext)}${aesgcm.final()}`;
|
|
2656
|
-
} catch (error) {
|
|
2657
|
-
const isRange = error instanceof RangeError;
|
|
2658
|
-
const invalidKeyLength = 'Invalid key length' === error.message;
|
|
2659
|
-
const decryptionFailed = 'Unsupported state or unable to authenticate data' === error.message;
|
|
2660
|
-
if (isRange || invalidKeyLength) {
|
|
2661
|
-
const err = new Error('INVALID_DOTENV_KEY: It must be 64 characters long (or more)');
|
|
2662
|
-
err.code = 'INVALID_DOTENV_KEY';
|
|
2663
|
-
throw err;
|
|
2664
|
-
}
|
|
2665
|
-
if (decryptionFailed) {
|
|
2666
|
-
const err = new Error('DECRYPTION_FAILED: Please check your DOTENV_KEY');
|
|
2667
|
-
err.code = 'DECRYPTION_FAILED';
|
|
2668
|
-
throw err;
|
|
2669
|
-
}
|
|
2670
|
-
throw error;
|
|
2671
|
-
}
|
|
2672
|
-
}
|
|
2673
|
-
function populate(processEnv, parsed, options = {}) {
|
|
2674
|
-
const debug = Boolean(options && options.debug);
|
|
2675
|
-
const override = Boolean(options && options.override);
|
|
2676
|
-
if ('object' != typeof parsed) {
|
|
2677
|
-
const err = new Error('OBJECT_REQUIRED: Please check the processEnv argument being passed to populate');
|
|
2678
|
-
err.code = 'OBJECT_REQUIRED';
|
|
2679
|
-
throw err;
|
|
2680
|
-
}
|
|
2681
|
-
for (const key of Object.keys(parsed))if (Object.prototype.hasOwnProperty.call(processEnv, key)) {
|
|
2682
|
-
if (true === override) processEnv[key] = parsed[key];
|
|
2683
|
-
if (debug) true === override ? _debug(`"${key}" is already defined and WAS overwritten`) : _debug(`"${key}" is already defined and was NOT overwritten`);
|
|
2684
|
-
} else processEnv[key] = parsed[key];
|
|
2685
|
-
}
|
|
2686
|
-
const DotenvModule = {
|
|
2687
|
-
configDotenv,
|
|
2688
|
-
_configVault,
|
|
2689
|
-
_parseVault,
|
|
2690
|
-
config,
|
|
2691
|
-
decrypt,
|
|
2692
|
-
parse,
|
|
2693
|
-
populate
|
|
2694
|
-
};
|
|
2695
|
-
module.exports.configDotenv = DotenvModule.configDotenv;
|
|
2696
|
-
module.exports._configVault = DotenvModule._configVault;
|
|
2697
|
-
module.exports._parseVault = DotenvModule._parseVault;
|
|
2698
|
-
module.exports.config = DotenvModule.config;
|
|
2699
|
-
module.exports.decrypt = DotenvModule.decrypt;
|
|
2700
|
-
module.exports.parse = DotenvModule.parse;
|
|
2701
|
-
module.exports.populate = DotenvModule.populate;
|
|
2702
|
-
module.exports = DotenvModule;
|
|
2703
|
-
},
|
|
2704
2481
|
"../../node_modules/.pnpm/emoji-regex@8.0.0/node_modules/emoji-regex/index.js" (module) {
|
|
2705
2482
|
module.exports = function() {
|
|
2706
2483
|
return /\uD83C\uDFF4\uDB40\uDC67\uDB40\uDC62(?:\uDB40\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDB40\uDC73\uDB40\uDC63\uDB40\uDC74|\uDB40\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F|\uD83D\uDC68(?:\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68\uD83C\uDFFB|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFE])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83D\uDC68|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D[\uDC66\uDC67])|[\u2695\u2696\u2708]\uFE0F|\uD83D[\uDC66\uDC67]|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|(?:\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708])\uFE0F|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C[\uDFFB-\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFB\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)\uD83C\uDFFB|\uD83E\uDDD1(?:\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])|\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1)|(?:\uD83E\uDDD1\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB-\uDFFE])|(?:\uD83E\uDDD1\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)(?:\uD83C[\uDFFB\uDFFC])|\uD83D\uDC69(?:\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFC-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|(?:\uD83E\uDDD1\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)(?:\uD83C[\uDFFB-\uDFFD])|\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D\uDC41\uFE0F\u200D\uD83D\uDDE8|\uD83D\uDC69(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|(?:(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)\uFE0F|\uD83D\uDC6F|\uD83E[\uDD3C\uDDDE\uDDDF])\u200D[\u2640\u2642]|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD6-\uDDDD])(?:(?:\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|\u200D[\u2640\u2642])|\uD83C\uDFF4\u200D\u2620)\uFE0F|\uD83D\uDC69\u200D\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|\uD83C\uDFF3\uFE0F\u200D\uD83C\uDF08|\uD83D\uDC15\u200D\uD83E\uDDBA|\uD83D\uDC69\u200D\uD83D\uDC66|\uD83D\uDC69\u200D\uD83D\uDC67|\uD83C\uDDFD\uD83C\uDDF0|\uD83C\uDDF4\uD83C\uDDF2|\uD83C\uDDF6\uD83C\uDDE6|[#\*0-9]\uFE0F\u20E3|\uD83C\uDDE7(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF])|\uD83C\uDDF9(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF])|\uD83C\uDDEA(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA])|\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])|\uD83C\uDDF7(?:\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC])|\uD83D\uDC69(?:\uD83C[\uDFFB-\uDFFF])|\uD83C\uDDF2(?:\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF])|\uD83C\uDDE6(?:\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF])|\uD83C\uDDF0(?:\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF])|\uD83C\uDDED(?:\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA])|\uD83C\uDDE9(?:\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF])|\uD83C\uDDFE(?:\uD83C[\uDDEA\uDDF9])|\uD83C\uDDEC(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE])|\uD83C\uDDF8(?:\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF])|\uD83C\uDDEB(?:\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7])|\uD83C\uDDF5(?:\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE])|\uD83C\uDDFB(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA])|\uD83C\uDDF3(?:\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF])|\uD83C\uDDE8(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF])|\uD83C\uDDF1(?:\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE])|\uD83C\uDDFF(?:\uD83C[\uDDE6\uDDF2\uDDFC])|\uD83C\uDDFC(?:\uD83C[\uDDEB\uDDF8])|\uD83C\uDDFA(?:\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF])|\uD83C\uDDEE(?:\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9])|\uD83C\uDDEF(?:\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5])|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u261D\u270A-\u270D]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC70\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDCAA\uDD74\uDD7A\uDD90\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD0F\uDD18-\uDD1C\uDD1E\uDD1F\uDD30-\uDD36\uDDB5\uDDB6\uDDBB\uDDD2-\uDDD5])(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDED5\uDEEB\uDEEC\uDEF4-\uDEFA\uDFE0-\uDFEB]|\uD83E[\uDD0D-\uDD3A\uDD3C-\uDD45\uDD47-\uDD71\uDD73-\uDD76\uDD7A-\uDDA2\uDDA5-\uDDAA\uDDAE-\uDDCA\uDDCD-\uDDFF\uDE70-\uDE73\uDE78-\uDE7A\uDE80-\uDE82\uDE90-\uDE95])|(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDED5\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEFA\uDFE0-\uDFEB]|\uD83E[\uDD0D-\uDD3A\uDD3C-\uDD45\uDD47-\uDD71\uDD73-\uDD76\uDD7A-\uDDA2\uDDA5-\uDDAA\uDDAE-\uDDCA\uDDCD-\uDDFF\uDE70-\uDE73\uDE78-\uDE7A\uDE80-\uDE82\uDE90-\uDE95])\uFE0F|(?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85\uDFC2-\uDFC4\uDFC7\uDFCA-\uDFCC]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66-\uDC78\uDC7C\uDC81-\uDC83\uDC85-\uDC87\uDC8F\uDC91\uDCAA\uDD74\uDD75\uDD7A\uDD90\uDD95\uDD96\uDE45-\uDE47\uDE4B-\uDE4F\uDEA3\uDEB4-\uDEB6\uDEC0\uDECC]|\uD83E[\uDD0F\uDD18-\uDD1F\uDD26\uDD30-\uDD39\uDD3C-\uDD3E\uDDB5\uDDB6\uDDB8\uDDB9\uDDBB\uDDCD-\uDDCF\uDDD1-\uDDDD])/g;
|
|
@@ -3024,7 +2801,7 @@ var __webpack_modules__ = {
|
|
|
3024
2801
|
},
|
|
3025
2802
|
"./src/index.ts" (__unused_rspack_module, __unused_rspack___webpack_exports__, __webpack_require__) {
|
|
3026
2803
|
var package_namespaceObject = {
|
|
3027
|
-
rE: "1.
|
|
2804
|
+
rE: "1.15.0"
|
|
3028
2805
|
};
|
|
3029
2806
|
var brace_expansion = __webpack_require__("../../node_modules/.pnpm/brace-expansion@2.0.1/node_modules/brace-expansion/index.js");
|
|
3030
2807
|
const MAX_PATTERN_LENGTH = 65536;
|
|
@@ -8636,9 +8413,11 @@ Homepage: https://midscenejs.com
|
|
|
8636
8413
|
Github: https://github.com/web-infra-dev/midscene
|
|
8637
8414
|
|
|
8638
8415
|
Usage:
|
|
8416
|
+
$0 model siwc <login|refresh> [options]
|
|
8639
8417
|
$0 [options] <path-to-yaml-script-file-or-directory>
|
|
8640
8418
|
$0 [options] --files <yaml-file1> <yaml-file2 ...>
|
|
8641
|
-
$0 [options] --config <path-to-config-yaml-file
|
|
8419
|
+
$0 [options] --config <path-to-config-yaml-file>
|
|
8420
|
+
$0 model verify Check model connectivity and Midscene compatibility`).options({
|
|
8642
8421
|
files: {
|
|
8643
8422
|
type: 'array',
|
|
8644
8423
|
string: true,
|
|
@@ -8696,7 +8475,7 @@ Usage:
|
|
|
8696
8475
|
type: 'boolean',
|
|
8697
8476
|
description: 'Whether to dismiss the HarmonyOS keyboard after text input'
|
|
8698
8477
|
}
|
|
8699
|
-
}).version('version', 'Show version number', "1.
|
|
8478
|
+
}).version('version', 'Show version number', "1.15.0").help().epilogue(`For complete list of configuration options, please visit:
|
|
8700
8479
|
⢠Web options: https://midscenejs.com/automate-with-scripts-in-yaml#the-web-part
|
|
8701
8480
|
⢠Android options: https://midscenejs.com/automate-with-scripts-in-yaml#the-android-part
|
|
8702
8481
|
⢠HarmonyOS options: https://midscenejs.com/automate-with-scripts-in-yaml#the-harmony-part
|
|
@@ -10943,18 +10722,6 @@ Native cases/steps Test projects use the midscene-test command.`).wrap(yargs().t
|
|
|
10943
10722
|
globalConfig: pickYamlTargetConfig(options)
|
|
10944
10723
|
};
|
|
10945
10724
|
}
|
|
10946
|
-
var main = __webpack_require__("../../node_modules/.pnpm/dotenv@16.4.5/node_modules/dotenv/lib/main.js");
|
|
10947
|
-
var main_default = /*#__PURE__*/ __webpack_require__.n(main);
|
|
10948
|
-
function loadDotenvConfig(options = {}) {
|
|
10949
|
-
const dotEnvConfigFile = join(options.cwd ?? process.cwd(), '.env');
|
|
10950
|
-
if (!(0, __rspack_external_node_fs_5ea92f0c.existsSync)(dotEnvConfigFile)) return;
|
|
10951
|
-
options.log?.(` Env file: ${dotEnvConfigFile}`);
|
|
10952
|
-
main_default().config({
|
|
10953
|
-
path: dotEnvConfigFile,
|
|
10954
|
-
debug: options.dotenvDebug,
|
|
10955
|
-
override: options.dotenvOverride
|
|
10956
|
-
});
|
|
10957
|
-
}
|
|
10958
10725
|
const warnRetryReport = getDebug('execution-summary', {
|
|
10959
10726
|
console: true
|
|
10960
10727
|
});
|
|
@@ -12723,172 +12490,431 @@ defineYamlBatchTest(test, testOptions);
|
|
|
12723
12490
|
const success = printExecutionSummary(results, summaryPath);
|
|
12724
12491
|
return success ? exitCode : 1;
|
|
12725
12492
|
}
|
|
12726
|
-
const
|
|
12727
|
-
|
|
12728
|
-
|
|
12729
|
-
|
|
12730
|
-
|
|
12731
|
-
|
|
12732
|
-
|
|
12733
|
-
|
|
12734
|
-
|
|
12735
|
-
|
|
12736
|
-
|
|
12493
|
+
const defaultCredentialsPath = ()=>join(homedir(), '.midscene', 'siwc.json');
|
|
12494
|
+
async function readCredentialsFile(path) {
|
|
12495
|
+
const content = await readFile(path, 'utf8').catch((error)=>{
|
|
12496
|
+
if ('ENOENT' === error.code) return;
|
|
12497
|
+
throw new Error('Unable to read the local SIWC credentials file.');
|
|
12498
|
+
});
|
|
12499
|
+
if (void 0 === content) return;
|
|
12500
|
+
try {
|
|
12501
|
+
const value = JSON.parse(content);
|
|
12502
|
+
if (!value || 'string' != typeof value.credentials?.ext_agent_host_id || !value.credentials.ext_agent_host_id) throw new Error('Invalid SIWC credentials record.');
|
|
12503
|
+
return value;
|
|
12504
|
+
} catch {
|
|
12505
|
+
throw new Error('Invalid local SIWC credentials file.');
|
|
12506
|
+
}
|
|
12737
12507
|
}
|
|
12738
|
-
function
|
|
12739
|
-
|
|
12740
|
-
if (normalizedBaseURL.endsWith('/chat/completions')) return normalizedBaseURL;
|
|
12741
|
-
return `${normalizedBaseURL}/chat/completions`;
|
|
12508
|
+
async function readRegistration(path) {
|
|
12509
|
+
return (await readCredentialsFile(path))?.credentials;
|
|
12742
12510
|
}
|
|
12743
|
-
function
|
|
12744
|
-
const
|
|
12745
|
-
|
|
12746
|
-
|
|
12747
|
-
|
|
12748
|
-
|
|
12749
|
-
|
|
12750
|
-
|
|
12751
|
-
]
|
|
12511
|
+
async function saveRegistration(path, value) {
|
|
12512
|
+
const saved = await readCredentialsFile(path);
|
|
12513
|
+
const now = new Date().toISOString();
|
|
12514
|
+
const record = {
|
|
12515
|
+
...saved,
|
|
12516
|
+
created_at: saved?.created_at ?? now,
|
|
12517
|
+
updated_at: now,
|
|
12518
|
+
credentials: value
|
|
12752
12519
|
};
|
|
12753
|
-
|
|
12754
|
-
|
|
12755
|
-
|
|
12756
|
-
|
|
12757
|
-
|
|
12758
|
-
|
|
12759
|
-
|
|
12760
|
-
|
|
12761
|
-
|
|
12762
|
-
|
|
12763
|
-
apiKey: modelConfig.openaiApiKey || '',
|
|
12764
|
-
modelName: modelConfig.modelName
|
|
12765
|
-
});
|
|
12766
|
-
}
|
|
12767
|
-
function buildModelVerifyCurlCommands(configs) {
|
|
12768
|
-
const commandMap = new Map();
|
|
12769
|
-
for (const item of configs){
|
|
12770
|
-
const key = buildCurlDedupKey(item.modelConfig);
|
|
12771
|
-
const existing = commandMap.get(key);
|
|
12772
|
-
if (existing) {
|
|
12773
|
-
existing.intents.push(item.intent);
|
|
12774
|
-
continue;
|
|
12775
|
-
}
|
|
12776
|
-
commandMap.set(key, {
|
|
12777
|
-
intents: [
|
|
12778
|
-
item.intent
|
|
12779
|
-
],
|
|
12780
|
-
curl: buildCurlCommand(item.modelConfig),
|
|
12781
|
-
usesDefaultBaseURL: !item.modelConfig.openaiBaseURL
|
|
12520
|
+
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
12521
|
+
try {
|
|
12522
|
+
await writeFile(temporary, `${JSON.stringify(record, null, 2)}\n`, {
|
|
12523
|
+
mode: 384,
|
|
12524
|
+
flag: 'wx'
|
|
12525
|
+
});
|
|
12526
|
+
await rename(temporary, path);
|
|
12527
|
+
} finally{
|
|
12528
|
+
await rm(temporary, {
|
|
12529
|
+
force: true
|
|
12782
12530
|
});
|
|
12783
12531
|
}
|
|
12784
|
-
return [
|
|
12785
|
-
...commandMap.values()
|
|
12786
|
-
];
|
|
12787
12532
|
}
|
|
12788
|
-
function
|
|
12789
|
-
|
|
12790
|
-
|
|
12791
|
-
|
|
12792
|
-
|
|
12793
|
-
}
|
|
12794
|
-
|
|
12795
|
-
|
|
12796
|
-
|
|
12797
|
-
|
|
12798
|
-
|
|
12799
|
-
'',
|
|
12800
|
-
|
|
12801
|
-
|
|
12802
|
-
|
|
12803
|
-
|
|
12804
|
-
|
|
12805
|
-
|
|
12806
|
-
].join('\n');
|
|
12533
|
+
async function lockCredentials(path) {
|
|
12534
|
+
await mkdir(external_node_path_dirname(path), {
|
|
12535
|
+
recursive: true,
|
|
12536
|
+
mode: 448
|
|
12537
|
+
});
|
|
12538
|
+
const lockPath = `${path}.lock`;
|
|
12539
|
+
try {
|
|
12540
|
+
await mkdir(lockPath, {
|
|
12541
|
+
mode: 448
|
|
12542
|
+
});
|
|
12543
|
+
} catch (error) {
|
|
12544
|
+
if ('EEXIST' === error.code) throw new Error(`Another SIWC command holds ${lockPath}. If it was interrupted, remove this lock only after confirming that no SIWC command is running.`);
|
|
12545
|
+
throw new Error('Unable to lock the local SIWC credentials file.');
|
|
12546
|
+
}
|
|
12547
|
+
return ()=>rm(lockPath, {
|
|
12548
|
+
recursive: true,
|
|
12549
|
+
force: true
|
|
12550
|
+
});
|
|
12807
12551
|
}
|
|
12808
|
-
function
|
|
12809
|
-
|
|
12810
|
-
|
|
12811
|
-
|
|
12812
|
-
|
|
12813
|
-
|
|
12814
|
-
|
|
12552
|
+
function requireCredentials(value) {
|
|
12553
|
+
if (!value || [
|
|
12554
|
+
'client_id',
|
|
12555
|
+
'subject',
|
|
12556
|
+
'issuer',
|
|
12557
|
+
'access_token',
|
|
12558
|
+
'refresh_token',
|
|
12559
|
+
'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 Error('No valid SIWC credentials. Run midscene model siwc login first.');
|
|
12561
|
+
return value;
|
|
12562
|
+
}
|
|
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
|
+
async function loginWithOpenAI(options, dependencies) {
|
|
12567
|
+
const hostId = options.hostId ?? `urn:uuid:${randomUUID()}`;
|
|
12568
|
+
const existingClientId = options.clientId;
|
|
12569
|
+
const state = randomBytes(32).toString('base64url');
|
|
12570
|
+
const nonce = randomBytes(32).toString('base64url');
|
|
12571
|
+
const verifier = randomBytes(32).toString('base64url');
|
|
12572
|
+
const signal = options.signal;
|
|
12573
|
+
signal.throwIfAborted();
|
|
12574
|
+
const server = createServer();
|
|
12575
|
+
const callback = new Promise((resolve, reject)=>{
|
|
12576
|
+
const consumed = {
|
|
12577
|
+
value: false
|
|
12578
|
+
};
|
|
12579
|
+
server.on('request', (request, response)=>{
|
|
12580
|
+
response.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
|
12581
|
+
response.setHeader('Cache-Control', 'no-store');
|
|
12582
|
+
response.setHeader('Referrer-Policy', 'no-referrer');
|
|
12583
|
+
const url = new URL(request.url || '/', 'http://127.0.0.1');
|
|
12584
|
+
if ('GET' !== request.method || '/auth/callback' !== url.pathname) return void response.writeHead(404).end('Not found');
|
|
12585
|
+
if (1 !== url.searchParams.getAll('state').length || url.searchParams.get('state') !== state) return void response.writeHead(400).end('Invalid login state. Use the original authorization link.');
|
|
12586
|
+
if (consumed.value) return void response.writeHead(409).end('Authorization already received.');
|
|
12587
|
+
if (url.searchParams.has('error')) {
|
|
12588
|
+
consumed.value = true;
|
|
12589
|
+
response.writeHead(400).end('Authorization was not granted. Return to the terminal.');
|
|
12590
|
+
reject(new Error('OpenAI authorization was denied or failed.'));
|
|
12591
|
+
return;
|
|
12592
|
+
}
|
|
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
|
+
consumed.value = true;
|
|
12597
|
+
response.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
12598
|
+
response.end(`<!doctype html>
|
|
12599
|
+
<html lang="en">
|
|
12600
|
+
<head>
|
|
12601
|
+
<meta charset="utf-8">
|
|
12602
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
12603
|
+
<title>Authorization received Ā· Midscene</title>
|
|
12604
|
+
<style>
|
|
12605
|
+
* { box-sizing: border-box; }
|
|
12606
|
+
body { margin: 0; min-height: 100vh; display: grid; place-items: center; padding: 24px; background: #f6f7f9; color: #202124; font-family: system-ui, sans-serif; }
|
|
12607
|
+
main { width: 100%; max-width: 480px; padding: 40px 28px; text-align: center; background: white; border: 1px solid #e5e7eb; border-radius: 20px; box-shadow: 0 8px 32px #00000008; }
|
|
12608
|
+
h1 { margin: 0 0 20px; font-size: 24px; line-height: 1.4; }
|
|
12609
|
+
h1 span { color: #16834a; }
|
|
12610
|
+
p { margin: 8px 0 0; color: #596273; font-size: 15px; line-height: 1.6; }
|
|
12611
|
+
</style>
|
|
12612
|
+
</head>
|
|
12613
|
+
<body>
|
|
12614
|
+
<main>
|
|
12615
|
+
<h1><span aria-hidden="true">ā</span> Authorization received</h1>
|
|
12616
|
+
<p>You can close this tab.</p>
|
|
12617
|
+
<p>Check your terminal for the sign-in result and access token.</p>
|
|
12618
|
+
</main>
|
|
12619
|
+
</body>
|
|
12620
|
+
</html>`);
|
|
12621
|
+
resolve({
|
|
12622
|
+
code,
|
|
12623
|
+
clientId
|
|
12815
12624
|
});
|
|
12816
|
-
}
|
|
12817
|
-
|
|
12818
|
-
|
|
12819
|
-
|
|
12820
|
-
|
|
12821
|
-
|
|
12625
|
+
});
|
|
12626
|
+
server.on('error', reject);
|
|
12627
|
+
});
|
|
12628
|
+
callback.catch(()=>void 0);
|
|
12629
|
+
const abort = ()=>server.emit('error', new Error('OpenAI login cancelled or timed out.'));
|
|
12630
|
+
signal.addEventListener('abort', abort, {
|
|
12631
|
+
once: true
|
|
12632
|
+
});
|
|
12822
12633
|
try {
|
|
12823
|
-
|
|
12824
|
-
|
|
12825
|
-
|
|
12826
|
-
|
|
12827
|
-
|
|
12828
|
-
|
|
12829
|
-
|
|
12830
|
-
|
|
12831
|
-
const
|
|
12832
|
-
const
|
|
12833
|
-
|
|
12834
|
-
|
|
12835
|
-
{
|
|
12836
|
-
|
|
12837
|
-
modelConfig: defaultModelConfig
|
|
12634
|
+
await new Promise((resolve, reject)=>{
|
|
12635
|
+
server.once('error', reject);
|
|
12636
|
+
server.listen(options.port ?? 0, '127.0.0.1', ()=>{
|
|
12637
|
+
server.removeListener('error', reject);
|
|
12638
|
+
resolve();
|
|
12639
|
+
});
|
|
12640
|
+
});
|
|
12641
|
+
signal.throwIfAborted();
|
|
12642
|
+
const redirectUri = `http://127.0.0.1:${server.address().port}/auth/callback`;
|
|
12643
|
+
const authorization = new URL(`${ISSUER}/api/accounts/authorize`);
|
|
12644
|
+
authorization.search = new URLSearchParams({
|
|
12645
|
+
client_id: existingClientId ?? 'dynamic_agent_client',
|
|
12646
|
+
...existingClientId ? {} : {
|
|
12647
|
+
agent_name_hint: 'Midscene'
|
|
12838
12648
|
},
|
|
12839
|
-
|
|
12840
|
-
|
|
12841
|
-
|
|
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'
|
|
12842
12667
|
},
|
|
12843
|
-
{
|
|
12844
|
-
|
|
12845
|
-
|
|
12846
|
-
|
|
12847
|
-
|
|
12848
|
-
|
|
12849
|
-
|
|
12850
|
-
|
|
12851
|
-
|
|
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'
|
|
12852
12678
|
});
|
|
12853
|
-
if (result.
|
|
12854
|
-
|
|
12855
|
-
|
|
12856
|
-
|
|
12857
|
-
|
|
12858
|
-
|
|
12859
|
-
|
|
12860
|
-
|
|
12861
|
-
|
|
12862
|
-
|
|
12863
|
-
|
|
12864
|
-
|
|
12865
|
-
|
|
12866
|
-
|
|
12867
|
-
|
|
12868
|
-
|
|
12679
|
+
if (!result.ok) throw new Error(`OpenAI token exchange failed (HTTP ${result.status}). Run midscene model siwc login again.`);
|
|
12680
|
+
const tokens = await result.json();
|
|
12681
|
+
if (!tokens || 'string' != typeof tokens.access_token || !tokens.access_token || 'string' != typeof tokens.refresh_token || !tokens.refresh_token || 'string' != typeof tokens.id_token || 'string' != typeof tokens.scope || 'number' != typeof tokens.expires_in || !Number.isFinite(tokens.expires_in) || tokens.expires_in <= 0 || 'string' != typeof tokens.token_type || 'bearer' !== tokens.token_type.toLowerCase()) throw new Error('OpenAI returned an invalid token response.');
|
|
12682
|
+
const scopes = tokens.scope.split(/\s+/);
|
|
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
|
+
};
|
|
12714
|
+
} finally{
|
|
12715
|
+
signal.removeEventListener('abort', abort);
|
|
12716
|
+
server.closeAllConnections();
|
|
12717
|
+
await new Promise((resolve)=>server.close(()=>resolve()));
|
|
12718
|
+
}
|
|
12719
|
+
}
|
|
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.');
|
|
12869
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
|
+
};
|
|
12870
12768
|
}
|
|
12871
|
-
|
|
12769
|
+
const USAGE = `Usage:
|
|
12770
|
+
midscene model siwc login [--no-open] [--port <port>]
|
|
12771
|
+
midscene model siwc refresh
|
|
12772
|
+
|
|
12773
|
+
Sign in with ChatGPT or refresh the saved credentials.
|
|
12774
|
+
Full credentials are saved to ~/.midscene/siwc.json. Only the access token is printed to stdout.
|
|
12775
|
+
Midscene does not collect your credentials.
|
|
12776
|
+
|
|
12777
|
+
Options (login):
|
|
12778
|
+
--no-open Print the authorization URL without opening a browser
|
|
12779
|
+
--port <port> Loopback callback port (default: an available port)
|
|
12780
|
+
--help, -h Show this help
|
|
12781
|
+
`;
|
|
12782
|
+
async function openBrowser(url) {
|
|
12783
|
+
const command = 'darwin' === process.platform ? 'open' : 'win32' === process.platform ? 'rundll32' : 'xdg-open';
|
|
12784
|
+
const args = 'win32' === process.platform ? [
|
|
12785
|
+
'url.dll,FileProtocolHandler',
|
|
12786
|
+
url
|
|
12787
|
+
] : [
|
|
12788
|
+
url
|
|
12789
|
+
];
|
|
12790
|
+
await new Promise((resolve, reject)=>{
|
|
12791
|
+
const child = spawn(command, args, {
|
|
12792
|
+
stdio: 'ignore',
|
|
12793
|
+
shell: false
|
|
12794
|
+
});
|
|
12795
|
+
child.once('error', reject);
|
|
12796
|
+
child.once('spawn', ()=>{
|
|
12797
|
+
child.unref();
|
|
12798
|
+
resolve();
|
|
12799
|
+
});
|
|
12800
|
+
});
|
|
12801
|
+
}
|
|
12802
|
+
async function runSiwcCommand(args, io = {
|
|
12872
12803
|
stdout: console.log,
|
|
12873
12804
|
stderr: console.error
|
|
12874
|
-
}) {
|
|
12875
|
-
|
|
12876
|
-
|
|
12877
|
-
...getDefaultDeps(io),
|
|
12878
|
-
...deps
|
|
12879
|
-
};
|
|
12880
|
-
if (!action || '--help' === action || '-h' === action) {
|
|
12881
|
-
io.stdout(MODEL_COMMAND_USAGE);
|
|
12805
|
+
}, credentialsPath = defaultCredentialsPath()) {
|
|
12806
|
+
if (!args.length || args.includes('--help') || args.includes('-h')) {
|
|
12807
|
+
io.stdout(USAGE);
|
|
12882
12808
|
return 0;
|
|
12883
12809
|
}
|
|
12884
|
-
|
|
12885
|
-
if ('
|
|
12886
|
-
io.stderr(
|
|
12810
|
+
const [action, ...options] = args;
|
|
12811
|
+
if ('login' !== action && 'refresh' !== action) {
|
|
12812
|
+
io.stderr(`Unknown SIWC command: ${action}\n${USAGE}`);
|
|
12887
12813
|
return 1;
|
|
12888
12814
|
}
|
|
12889
|
-
|
|
12890
|
-
|
|
12815
|
+
const portOptions = {
|
|
12816
|
+
port: 0,
|
|
12817
|
+
noOpen: false
|
|
12818
|
+
};
|
|
12819
|
+
const seen = new Set();
|
|
12820
|
+
for(let index = 0; index < options.length; index++){
|
|
12821
|
+
const option = options[index];
|
|
12822
|
+
if ('login' !== action || seen.has(option) || ![
|
|
12823
|
+
'--port',
|
|
12824
|
+
'--no-open'
|
|
12825
|
+
].includes(option)) {
|
|
12826
|
+
io.stderr(`Invalid midscene model siwc arguments.\n${USAGE}`);
|
|
12827
|
+
return 1;
|
|
12828
|
+
}
|
|
12829
|
+
seen.add(option);
|
|
12830
|
+
if ('--no-open' === option) portOptions.noOpen = true;
|
|
12831
|
+
else {
|
|
12832
|
+
const value = options[++index];
|
|
12833
|
+
if (!value || !/^\d+$/.test(value) || Number(value) > 65535) {
|
|
12834
|
+
io.stderr('--port must be an integer between 0 and 65535.');
|
|
12835
|
+
return 1;
|
|
12836
|
+
}
|
|
12837
|
+
portOptions.port = Number(value);
|
|
12838
|
+
}
|
|
12839
|
+
}
|
|
12840
|
+
const unlock = await lockCredentials(credentialsPath).catch((error)=>{
|
|
12841
|
+
io.stderr(error.message);
|
|
12842
|
+
});
|
|
12843
|
+
if (!unlock) return 1;
|
|
12844
|
+
const controller = new AbortController();
|
|
12845
|
+
const cancel = ()=>controller.abort();
|
|
12846
|
+
const timer = setTimeout(cancel, 300000);
|
|
12847
|
+
process.once('SIGINT', cancel);
|
|
12848
|
+
process.once('SIGTERM', cancel);
|
|
12849
|
+
try {
|
|
12850
|
+
io.stderr(source_default().cyan.bold(`š Starting OpenAI SIWC ${action}...`));
|
|
12851
|
+
const saved = await readRegistration(credentialsPath);
|
|
12852
|
+
const credentials = await (async ()=>{
|
|
12853
|
+
if ('refresh' === action) return refreshOpenAICredentials(requireCredentials(saved), controller.signal);
|
|
12854
|
+
const registration = saved ?? {
|
|
12855
|
+
ext_agent_host_id: `urn:uuid:${randomUUID()}`
|
|
12856
|
+
};
|
|
12857
|
+
if (!saved) await saveRegistration(credentialsPath, registration);
|
|
12858
|
+
const result = await loginWithOpenAI({
|
|
12859
|
+
port: portOptions.port,
|
|
12860
|
+
hostId: registration.ext_agent_host_id,
|
|
12861
|
+
clientId: registration.client_id,
|
|
12862
|
+
signal: controller.signal,
|
|
12863
|
+
onAuthorization: async (url)=>{
|
|
12864
|
+
io.stderr(`\n${source_default().bold('Continue with ChatGPT:')}\n${url}\n\n${source_default().gray('Waiting for authorization...')}`);
|
|
12865
|
+
if (!portOptions.noOpen) try {
|
|
12866
|
+
await openBrowser(url);
|
|
12867
|
+
} catch {
|
|
12868
|
+
io.stderr('Open the URL above in your browser to continue.');
|
|
12869
|
+
}
|
|
12870
|
+
}
|
|
12871
|
+
});
|
|
12872
|
+
if (registration.subject && result.subject !== registration.subject) throw new Error('Login identity does not match the saved account.');
|
|
12873
|
+
return result;
|
|
12874
|
+
})();
|
|
12875
|
+
await saveRegistration(credentialsPath, credentials);
|
|
12876
|
+
io.stderr('\nš your Access token:');
|
|
12877
|
+
io.stdout(credentials.access_token);
|
|
12878
|
+
const expiresAt = new Intl.DateTimeFormat('en-GB', {
|
|
12879
|
+
dateStyle: 'medium',
|
|
12880
|
+
timeStyle: 'long'
|
|
12881
|
+
}).format(credentials.expires_at);
|
|
12882
|
+
const remainingMinutes = Math.ceil((credentials.expires_at - Date.now()) / 60000);
|
|
12883
|
+
const expiresIn = new Intl.RelativeTimeFormat('en', {
|
|
12884
|
+
numeric: 'always'
|
|
12885
|
+
}).format(60 === remainingMinutes ? 1 : remainingMinutes, 60 === remainingMinutes ? 'hour' : 'minute');
|
|
12886
|
+
io.stderr(`\nExpires at: ${expiresAt} (${expiresIn})`);
|
|
12887
|
+
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
|
+
return 0;
|
|
12889
|
+
} catch {
|
|
12890
|
+
io.stderr(source_default().red(controller.signal.aborted ? `OpenAI ${action} cancelled or timed out.` : `OpenAI ${action} failed. Check the local credentials file permissions or run midscene model siwc login to authorize again.`));
|
|
12891
|
+
return 1;
|
|
12892
|
+
} finally{
|
|
12893
|
+
clearTimeout(timer);
|
|
12894
|
+
process.removeListener('SIGINT', cancel);
|
|
12895
|
+
process.removeListener('SIGTERM', cancel);
|
|
12896
|
+
await unlock();
|
|
12897
|
+
}
|
|
12891
12898
|
}
|
|
12899
|
+
const model_command_runModelCommand = (rawArgs, deps, io = {
|
|
12900
|
+
stdout: console.log,
|
|
12901
|
+
stderr: console.error
|
|
12902
|
+
})=>{
|
|
12903
|
+
const [, action, ...restArgs] = rawArgs;
|
|
12904
|
+
if ('siwc' === action) return runSiwcCommand(restArgs, io);
|
|
12905
|
+
const withSiwcUsage = (message)=>message.replace(' midscene model verify\n', ' midscene model verify\n midscene model siwc <login|refresh> [options]\n');
|
|
12906
|
+
return runModelCommand(rawArgs, {
|
|
12907
|
+
loadDotenv: ()=>loadDotenvConfig({
|
|
12908
|
+
dotenvDebug: true,
|
|
12909
|
+
dotenvOverride: true,
|
|
12910
|
+
log: io.stdout
|
|
12911
|
+
}),
|
|
12912
|
+
...deps
|
|
12913
|
+
}, {
|
|
12914
|
+
stdout: (message)=>io.stdout(withSiwcUsage(message)),
|
|
12915
|
+
stderr: (message)=>io.stderr(withSiwcUsage(message))
|
|
12916
|
+
}, 'midscene');
|
|
12917
|
+
};
|
|
12892
12918
|
const probeWorkflowFormat = (file)=>{
|
|
12893
12919
|
try {
|
|
12894
12920
|
const content = (0, __rspack_external_node_fs_5ea92f0c.readFileSync)(file, 'utf8').replace(/\$\{[^}\r\n]*\}/g, 'MIDSCENE_ENV');
|
|
@@ -12937,7 +12963,7 @@ defineYamlBatchTest(test, testOptions);
|
|
|
12937
12963
|
extraCommands: createReportCliCommands()
|
|
12938
12964
|
});
|
|
12939
12965
|
if ('model' === firstArg) {
|
|
12940
|
-
const exitCode = await
|
|
12966
|
+
const exitCode = await model_command_runModelCommand(rawArgs);
|
|
12941
12967
|
process.exit(exitCode);
|
|
12942
12968
|
return;
|
|
12943
12969
|
}
|
|
@@ -13013,9 +13039,6 @@ defineYamlBatchTest(test, testOptions);
|
|
|
13013
13039
|
assert (module) {
|
|
13014
13040
|
module.exports = __rspack_external_assert;
|
|
13015
13041
|
},
|
|
13016
|
-
crypto (module) {
|
|
13017
|
-
module.exports = __rspack_external_crypto;
|
|
13018
|
-
},
|
|
13019
13042
|
fs (module) {
|
|
13020
13043
|
module.exports = __rspack_external_fs;
|
|
13021
13044
|
},
|
|
@@ -16300,9 +16323,6 @@ defineYamlBatchTest(test, testOptions);
|
|
|
16300
16323
|
YError: e1
|
|
16301
16324
|
};
|
|
16302
16325
|
module.exports = ue;
|
|
16303
|
-
},
|
|
16304
|
-
"../../node_modules/.pnpm/dotenv@16.4.5/node_modules/dotenv/package.json" (module) {
|
|
16305
|
-
module.exports = JSON.parse('{"name":"dotenv","version":"16.4.5","description":"Loads environment variables from .env file","main":"lib/main.js","types":"lib/main.d.ts","exports":{".":{"types":"./lib/main.d.ts","require":"./lib/main.js","default":"./lib/main.js"},"./config":"./config.js","./config.js":"./config.js","./lib/env-options":"./lib/env-options.js","./lib/env-options.js":"./lib/env-options.js","./lib/cli-options":"./lib/cli-options.js","./lib/cli-options.js":"./lib/cli-options.js","./package.json":"./package.json"},"scripts":{"dts-check":"tsc --project tests/types/tsconfig.json","lint":"standard","lint-readme":"standard-markdown","pretest":"npm run lint && npm run dts-check","test":"tap tests/*.js --100 -Rspec","test:coverage":"tap --coverage-report=lcov","prerelease":"npm test","release":"standard-version"},"repository":{"type":"git","url":"git://github.com/motdotla/dotenv.git"},"funding":"https://dotenvx.com","keywords":["dotenv","env",".env","environment","variables","config","settings"],"readmeFilename":"README.md","license":"BSD-2-Clause","devDependencies":{"@definitelytyped/dtslint":"^0.0.133","@types/node":"^18.11.3","decache":"^4.6.1","sinon":"^14.0.1","standard":"^17.0.0","standard-markdown":"^7.1.0","standard-version":"^9.5.0","tap":"^16.3.0","tar":"^6.1.11","typescript":"^4.8.4"},"engines":{"node":">=12"},"browser":{"fs":false}}');
|
|
16306
16326
|
}
|
|
16307
16327
|
};
|
|
16308
16328
|
var __webpack_module_cache__ = {};
|