@cloudbase/toolbox 0.7.16-beta.2 → 0.7.17-beta.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/lib/auth/index.d.ts +15 -0
- package/lib/auth/index.js +43 -7
- package/lib/auth/oauth.d.ts +11 -0
- package/lib/auth/oauth.js +123 -0
- package/lib/auth/web-auth.d.ts +2 -0
- package/lib/auth/web-auth.js +6 -5
- package/lib/web/web.d.ts +6 -0
- package/lib/web/web.js +16 -9
- package/package.json +2 -2
package/lib/auth/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { DeviceFlowOptions } from './oauth';
|
|
1
2
|
import { Credential, RequestConfig } from '../types';
|
|
2
3
|
export * from './common';
|
|
3
4
|
export * from './credential';
|
|
4
5
|
export * from './web-auth';
|
|
6
|
+
export * from './oauth';
|
|
5
7
|
export interface AuthSupervisorOptions {
|
|
6
8
|
/**
|
|
7
9
|
* 是否在内存中缓存 credential 信息
|
|
@@ -20,15 +22,28 @@ export interface AuthSupervisorOptions {
|
|
|
20
22
|
*/
|
|
21
23
|
throwError?: boolean;
|
|
22
24
|
}
|
|
25
|
+
export type AuthFlowType = 'default' | 'web' | 'device';
|
|
23
26
|
export interface WebAuthOptions {
|
|
24
27
|
/**
|
|
25
28
|
* 请在初始化实例时指定
|
|
26
29
|
* @deprecated
|
|
27
30
|
*/
|
|
28
31
|
throwError?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* 授权方式
|
|
34
|
+
* - 'default': 自动选择,优先 web,浏览器打开失败或无浏览器环境时降级到 device
|
|
35
|
+
* - 'web': 强制使用网页回调授权
|
|
36
|
+
* - 'device': 强制使用 Device Flow 授权
|
|
37
|
+
* @default 'default'
|
|
38
|
+
*/
|
|
39
|
+
mode?: AuthFlowType;
|
|
29
40
|
getAuthUrl?: (rawUrl: string) => string;
|
|
30
41
|
noBrowser?: boolean;
|
|
31
42
|
callbackTimeout?: number;
|
|
43
|
+
/** Device Flow 的客户端 ID */
|
|
44
|
+
client_id?: string;
|
|
45
|
+
/** Device Flow 回调,获取设备码后通知调用方 */
|
|
46
|
+
onDeviceCode?: DeviceFlowOptions['onDeviceCode'];
|
|
32
47
|
}
|
|
33
48
|
export interface LoginByApiSecretOptions {
|
|
34
49
|
/**
|
package/lib/auth/index.js
CHANGED
|
@@ -25,12 +25,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
26
|
exports.AuthSupevisor = exports.AuthSupervisor = void 0;
|
|
27
27
|
const web_auth_1 = require("./web-auth");
|
|
28
|
+
const oauth_1 = require("./oauth");
|
|
28
29
|
const common_1 = require("./common");
|
|
29
30
|
const credential_1 = require("./credential");
|
|
31
|
+
const web_1 = require("../web");
|
|
30
32
|
const error_1 = require("../error");
|
|
31
33
|
__exportStar(require("./common"), exports);
|
|
32
34
|
__exportStar(require("./credential"), exports);
|
|
33
35
|
__exportStar(require("./web-auth"), exports);
|
|
36
|
+
__exportStar(require("./oauth"), exports);
|
|
34
37
|
class AuthSupervisor {
|
|
35
38
|
/**
|
|
36
39
|
* 单例模式,全局缓存
|
|
@@ -80,7 +83,7 @@ class AuthSupervisor {
|
|
|
80
83
|
*/
|
|
81
84
|
loginByWebAuth(options = {}) {
|
|
82
85
|
return __awaiter(this, void 0, void 0, function* () {
|
|
83
|
-
const { getAuthUrl, throwError, noBrowser, callbackTimeout } = options;
|
|
86
|
+
const { getAuthUrl, throwError, noBrowser, callbackTimeout, mode: authFlowType, client_id, onDeviceCode } = options;
|
|
84
87
|
if (this.cacheCredential && this.needCache && !this.isCacheExpire()) {
|
|
85
88
|
return this.cacheCredential;
|
|
86
89
|
}
|
|
@@ -88,12 +91,45 @@ class AuthSupervisor {
|
|
|
88
91
|
let credential = yield (0, credential_1.checkAndGetCredential)(this.requestConfig);
|
|
89
92
|
if (credential)
|
|
90
93
|
return credential;
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
// 根据授权方式获取凭证
|
|
95
|
+
switch (authFlowType) {
|
|
96
|
+
case 'device':
|
|
97
|
+
credential = yield (0, oauth_1.getAuthTokenByDeviceFlow)({ client_id, onDeviceCode });
|
|
98
|
+
break;
|
|
99
|
+
case 'web':
|
|
100
|
+
credential = yield (0, web_auth_1.getAuthTokenFromWeb)({
|
|
101
|
+
getAuthUrl,
|
|
102
|
+
noBrowser,
|
|
103
|
+
callbackTimeout
|
|
104
|
+
});
|
|
105
|
+
break;
|
|
106
|
+
case 'default':
|
|
107
|
+
default: {
|
|
108
|
+
const isNoBrowser = noBrowser || (0, web_1.isTruthyFlag)(process.env.TCB_NO_BROWSER);
|
|
109
|
+
if (isNoBrowser) {
|
|
110
|
+
credential = yield (0, oauth_1.getAuthTokenByDeviceFlow)({ client_id, onDeviceCode });
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
try {
|
|
114
|
+
credential = yield (0, web_auth_1.getAuthTokenFromWeb)({
|
|
115
|
+
getAuthUrl,
|
|
116
|
+
callbackTimeout,
|
|
117
|
+
onOpenFailed: () => { }
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
catch (e) {
|
|
121
|
+
if ((e === null || e === void 0 ? void 0 : e.code) === 'BROWSER_OPEN_FAILED') {
|
|
122
|
+
credential = yield (0, oauth_1.getAuthTokenByDeviceFlow)({ client_id, onDeviceCode });
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
throw e;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
credential = (0, common_1.resolveCredential)(credential);
|
|
97
133
|
try {
|
|
98
134
|
yield (0, common_1.checkAuth)(credential, this.requestConfig);
|
|
99
135
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Credential } from "../types";
|
|
2
|
+
export interface DeviceFlowOptions {
|
|
3
|
+
client_id?: string;
|
|
4
|
+
onDeviceCode?: (data: {
|
|
5
|
+
user_code: string;
|
|
6
|
+
verification_uri?: string;
|
|
7
|
+
device_code: string;
|
|
8
|
+
expires_in: number;
|
|
9
|
+
}) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function getAuthTokenByDeviceFlow(options?: DeviceFlowOptions): Promise<Credential>;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.getAuthTokenByDeviceFlow = void 0;
|
|
13
|
+
const net_1 = require("../net");
|
|
14
|
+
const web_1 = require("../web");
|
|
15
|
+
const error_1 = require("../error");
|
|
16
|
+
const coding_1 = require("../coding");
|
|
17
|
+
const system_1 = require("../system");
|
|
18
|
+
const web_auth_1 = require("./web-auth");
|
|
19
|
+
const OAUTH_ENDPOINT = process.env.TCB_OAUTH_ENDPOINT || 'https://tcb-api.cloud.tencent.com/qcloud-tcb/v1/oauth';
|
|
20
|
+
const DEFAULT_CLIENT_ID = 'cloudbase-toolbox';
|
|
21
|
+
const POLL_ERROR_CODES = {
|
|
22
|
+
AUTHORIZATION_PENDING: 'authorization_pending',
|
|
23
|
+
SLOW_DOWN: 'slow_down',
|
|
24
|
+
EXPIRED_TOKEN: 'expired_token',
|
|
25
|
+
ACCESS_DENIED: 'access_denied',
|
|
26
|
+
};
|
|
27
|
+
const SUCCESS_CODE = 'NORMAL';
|
|
28
|
+
function fetchDeviceCode(options = {}) {
|
|
29
|
+
const { client_id = DEFAULT_CLIENT_ID } = options;
|
|
30
|
+
return (0, net_1.postFetch)(`${OAUTH_ENDPOINT}/device/code`, { client_id });
|
|
31
|
+
}
|
|
32
|
+
function fetchPollToken(options) {
|
|
33
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
34
|
+
const { device_code, client_id = DEFAULT_CLIENT_ID } = options;
|
|
35
|
+
const mac = yield (0, system_1.getMacAddress)();
|
|
36
|
+
return (0, net_1.postFetch)(`${OAUTH_ENDPOINT}/token`, {
|
|
37
|
+
device_code,
|
|
38
|
+
client_id,
|
|
39
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
|
|
40
|
+
device_info: {
|
|
41
|
+
mac,
|
|
42
|
+
os: (0, system_1.getOSInfo)(),
|
|
43
|
+
hash: (0, coding_1.md5Encoding)(mac),
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function sleep(ms) {
|
|
49
|
+
return new Promise((resolve) => {
|
|
50
|
+
setTimeout(resolve, ms);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function getAuthTokenByDeviceFlow(options = {}) {
|
|
54
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
55
|
+
const { client_id, onDeviceCode } = options;
|
|
56
|
+
const deviceCodeResp = yield fetchDeviceCode({ client_id });
|
|
57
|
+
if (deviceCodeResp.code !== SUCCESS_CODE) {
|
|
58
|
+
throw new error_1.CloudBaseError('Device Flow fetchDeviceCode failed', {
|
|
59
|
+
code: deviceCodeResp.code,
|
|
60
|
+
requestId: deviceCodeResp.reqId,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
const { device_code, user_code, expires_in, interval } = deviceCodeResp.result;
|
|
64
|
+
// const defaultVerificationUri = deviceCodeResp.result.verification_uri || `${CLI_AUTH_BASE_URL}#/cli-auth`;
|
|
65
|
+
// 暂时不消费 verification_uri,由客户端控制跳转
|
|
66
|
+
const defaultVerificationUri = `${web_auth_1.CLI_AUTH_BASE_URL}#/cli-auth`;
|
|
67
|
+
// from=cli 后续考虑改用 client_id,因为 toolbox 作为公共依赖,可能被不同 client 所引用
|
|
68
|
+
const verification_uri = `${defaultVerificationUri}?user_code=${user_code}&from=cli&mode=device`;
|
|
69
|
+
// 打印授权信息,引导用户操作
|
|
70
|
+
console.log('\n\n若链接未自动打开,请手动复制至浏览器,或尝试其他登录方式:');
|
|
71
|
+
console.log(`\n${verification_uri}`);
|
|
72
|
+
console.log(`\n用户码: ${user_code}\n`);
|
|
73
|
+
// 尝试自动打开授权页面
|
|
74
|
+
yield (0, web_1.openUrl)(verification_uri);
|
|
75
|
+
if (onDeviceCode) {
|
|
76
|
+
onDeviceCode({ user_code, verification_uri, device_code, expires_in });
|
|
77
|
+
}
|
|
78
|
+
let pollInterval = interval;
|
|
79
|
+
const deadline = Date.now() + expires_in * 1000;
|
|
80
|
+
while (Date.now() < deadline) {
|
|
81
|
+
yield sleep(pollInterval * 1000);
|
|
82
|
+
let resp;
|
|
83
|
+
try {
|
|
84
|
+
resp = yield fetchPollToken({ device_code, interval: pollInterval, client_id });
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
throw new error_1.CloudBaseError('Device Flow 轮询请求失败', { original: e });
|
|
88
|
+
}
|
|
89
|
+
const { code, result } = resp;
|
|
90
|
+
// 服务端返回业务错误(result 中携带 error 字段)
|
|
91
|
+
if (result && 'error' in result) {
|
|
92
|
+
const { error, error_description } = result;
|
|
93
|
+
if (error === POLL_ERROR_CODES.AUTHORIZATION_PENDING) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (error === POLL_ERROR_CODES.SLOW_DOWN) {
|
|
97
|
+
pollInterval += 5;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (error === POLL_ERROR_CODES.EXPIRED_TOKEN) {
|
|
101
|
+
throw new error_1.CloudBaseError('设备码已过期,请重新发起授权');
|
|
102
|
+
}
|
|
103
|
+
if (error === POLL_ERROR_CODES.ACCESS_DENIED) {
|
|
104
|
+
throw new error_1.CloudBaseError('用户拒绝了授权请求');
|
|
105
|
+
}
|
|
106
|
+
throw new error_1.CloudBaseError(error_description || `Device Flow 授权失败,错误: ${error}`, {
|
|
107
|
+
code: error,
|
|
108
|
+
requestId: resp.reqId,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
// 服务端返回成功的 Credential
|
|
112
|
+
if (code === SUCCESS_CODE && result) {
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
throw new error_1.CloudBaseError(`Device Flow 授权失败,错误码: ${code}`, {
|
|
116
|
+
code,
|
|
117
|
+
requestId: resp.reqId,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
throw new error_1.CloudBaseError('授权超时,用户未在有效期内完成授权,请重试');
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
exports.getAuthTokenByDeviceFlow = getAuthTokenByDeviceFlow;
|
package/lib/auth/web-auth.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Credential } from '../types';
|
|
2
|
+
export declare const CLI_AUTH_BASE_URL = "https://tcb.cloud.tencent.com/dev";
|
|
2
3
|
export declare function getAuthTokenFromWeb(options?: {
|
|
3
4
|
getAuthUrl?: (rawUrl: string) => string;
|
|
4
5
|
noBrowser?: boolean;
|
|
5
6
|
callbackTimeout?: number;
|
|
7
|
+
onOpenFailed?: () => void;
|
|
6
8
|
}): Promise<Credential>;
|
package/lib/auth/web-auth.js
CHANGED
|
@@ -9,16 +9,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.getAuthTokenFromWeb = void 0;
|
|
12
|
+
exports.getAuthTokenFromWeb = exports.CLI_AUTH_BASE_URL = void 0;
|
|
13
13
|
const common_1 = require("./common");
|
|
14
14
|
const coding_1 = require("../coding");
|
|
15
15
|
const web_1 = require("../web");
|
|
16
16
|
const system_1 = require("../system");
|
|
17
|
-
|
|
17
|
+
exports.CLI_AUTH_BASE_URL = 'https://tcb.cloud.tencent.com/dev';
|
|
18
18
|
// 打开云开发控制台,获取授权
|
|
19
19
|
function getAuthTokenFromWeb(options = {}) {
|
|
20
20
|
return __awaiter(this, void 0, void 0, function* () {
|
|
21
|
-
const { getAuthUrl, noBrowser, callbackTimeout } = options;
|
|
21
|
+
const { getAuthUrl, noBrowser, callbackTimeout, onOpenFailed } = options;
|
|
22
22
|
const mac = yield (0, system_1.getMacAddress)();
|
|
23
23
|
const os = (0, system_1.getOSInfo)();
|
|
24
24
|
const macHash = (0, coding_1.md5Encoding)(mac);
|
|
@@ -31,7 +31,7 @@ function getAuthTokenFromWeb(options = {}) {
|
|
|
31
31
|
+ '&from=cli';
|
|
32
32
|
const encodedCallbackUrl = encodeURIComponent(callbackUrl);
|
|
33
33
|
// 授权链接
|
|
34
|
-
const rawAuthUrl = `${
|
|
34
|
+
const rawAuthUrl = `${exports.CLI_AUTH_BASE_URL}?authCallbackUrl=${encodedCallbackUrl}#/cli-auth?${encodedQuery}`;
|
|
35
35
|
let cliAuthUrl = rawAuthUrl;
|
|
36
36
|
if (getAuthUrl) {
|
|
37
37
|
try {
|
|
@@ -44,7 +44,8 @@ function getAuthTokenFromWeb(options = {}) {
|
|
|
44
44
|
return cliAuthUrl;
|
|
45
45
|
}, 'login', {
|
|
46
46
|
noBrowser,
|
|
47
|
-
callbackTimeout
|
|
47
|
+
callbackTimeout,
|
|
48
|
+
onOpenFailed
|
|
48
49
|
});
|
|
49
50
|
const credential = (0, common_1.resolveCredential)(query);
|
|
50
51
|
return credential;
|
package/lib/web/web.d.ts
CHANGED
|
@@ -6,5 +6,11 @@ export type CheckFn = (query: IQuery) => Promise<void>;
|
|
|
6
6
|
export interface GetDataFromWebOptions {
|
|
7
7
|
noBrowser?: boolean;
|
|
8
8
|
callbackTimeout?: number;
|
|
9
|
+
/** 浏览器打开失败时的回调,若提供则中止等待并抛出错误 */
|
|
10
|
+
onOpenFailed?: () => void;
|
|
9
11
|
}
|
|
12
|
+
export declare function isTruthyFlag(value?: string): boolean;
|
|
13
|
+
export declare function openUrl(url: string, options?: {
|
|
14
|
+
skipBrowserFallback?: boolean;
|
|
15
|
+
}): Promise<boolean>;
|
|
10
16
|
export declare function getDataFromWeb<T extends IQuery>(getUrl: GetUrlFn, type: 'login' | 'getData', options?: GetDataFromWebOptions): Promise<T>;
|
package/lib/web/web.js
CHANGED
|
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.getDataFromWeb = void 0;
|
|
15
|
+
exports.getDataFromWeb = exports.openUrl = exports.isTruthyFlag = void 0;
|
|
16
16
|
const open_1 = __importDefault(require("open"));
|
|
17
17
|
const query_string_1 = __importDefault(require("query-string"));
|
|
18
18
|
const http_1 = __importDefault(require("http"));
|
|
@@ -59,6 +59,7 @@ function isTruthyFlag(value) {
|
|
|
59
59
|
}
|
|
60
60
|
return ['1', 'true', 'yes', 'on'].includes(String(value).trim().toLowerCase());
|
|
61
61
|
}
|
|
62
|
+
exports.isTruthyFlag = isTruthyFlag;
|
|
62
63
|
function isVSCodeEnvironment() {
|
|
63
64
|
return Boolean(process.env.VSCODE_IPC_HOOK_CLI
|
|
64
65
|
|| process.env.VSCODE_PID
|
|
@@ -93,14 +94,13 @@ function openUrlByBrowserEnv(url) {
|
|
|
93
94
|
function shouldUseBrowserEnvFallback() {
|
|
94
95
|
return process.platform === 'linux' && isVSCodeEnvironment();
|
|
95
96
|
}
|
|
96
|
-
function openUrl(url) {
|
|
97
|
+
function openUrl(url, options = {}) {
|
|
97
98
|
return __awaiter(this, void 0, void 0, function* () {
|
|
99
|
+
const { skipBrowserFallback = false } = options;
|
|
98
100
|
try {
|
|
99
101
|
const child = yield (0, open_1.default)(url, { url: true });
|
|
100
|
-
if (child === null || child === void 0 ? void 0 : child.once) {
|
|
102
|
+
if ((child === null || child === void 0 ? void 0 : child.once) && !skipBrowserFallback) {
|
|
101
103
|
child.once('error', (error) => __awaiter(this, void 0, void 0, function* () {
|
|
102
|
-
const code = (error === null || error === void 0 ? void 0 : error.code) || 'UNKNOWN';
|
|
103
|
-
console.warn(`自动打开浏览器失败(${code})。`);
|
|
104
104
|
if (shouldUseBrowserEnvFallback()) {
|
|
105
105
|
yield openUrlByBrowserEnv(url);
|
|
106
106
|
}
|
|
@@ -109,13 +109,14 @@ function openUrl(url) {
|
|
|
109
109
|
return true;
|
|
110
110
|
}
|
|
111
111
|
catch (e) {
|
|
112
|
-
if (shouldUseBrowserEnvFallback()) {
|
|
112
|
+
if (!skipBrowserFallback && shouldUseBrowserEnvFallback()) {
|
|
113
113
|
return openUrlByBrowserEnv(url);
|
|
114
114
|
}
|
|
115
115
|
return false;
|
|
116
116
|
}
|
|
117
117
|
});
|
|
118
118
|
}
|
|
119
|
+
exports.openUrl = openUrl;
|
|
119
120
|
// 从 Web 页面中获取数据
|
|
120
121
|
function getDataFromWeb(getUrl, type, options = {}) {
|
|
121
122
|
var _a, _b;
|
|
@@ -127,13 +128,19 @@ function getDataFromWeb(getUrl, type, options = {}) {
|
|
|
127
128
|
throw new error_1.CloudBaseError('callbackTimeout must be a positive number');
|
|
128
129
|
}
|
|
129
130
|
const url = getUrl(port);
|
|
130
|
-
console.log('\n\n
|
|
131
|
-
console.log(`\n${url}`);
|
|
131
|
+
console.log('\n\n若链接未自动打开,请手动复制至浏览器,或尝试其他登录方式:');
|
|
132
|
+
console.log(`\n${url}\n`);
|
|
132
133
|
if (!noBrowser) {
|
|
133
134
|
// 对 url 转码, 避免 wsl 无法正常打开地址
|
|
134
135
|
// https://www.npmjs.com/package/open#url
|
|
135
136
|
// https://github.com/sindresorhus/open/blob/master/index.js#L48
|
|
136
|
-
|
|
137
|
+
const skipBrowserFallback = !!options.onOpenFailed;
|
|
138
|
+
const opened = yield openUrl(url, { skipBrowserFallback });
|
|
139
|
+
if (!opened && options.onOpenFailed) {
|
|
140
|
+
server.close();
|
|
141
|
+
options.onOpenFailed();
|
|
142
|
+
throw new error_1.CloudBaseError('浏览器打开失败', { code: 'BROWSER_OPEN_FAILED' });
|
|
143
|
+
}
|
|
137
144
|
}
|
|
138
145
|
return new Promise((resolve, reject) => {
|
|
139
146
|
let finished = false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudbase/toolbox",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.17-beta.0",
|
|
4
4
|
"description": "The toolbox for cloudbase",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -67,4 +67,4 @@
|
|
|
67
67
|
}
|
|
68
68
|
},
|
|
69
69
|
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
|
70
|
-
}
|
|
70
|
+
}
|