@skillbuilder/minitool-core 0.0.0-beta-20261027053041051
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/README.md +74 -0
- package/lib/di/index.js +21 -0
- package/lib/di/modules.js +23 -0
- package/lib/di/tokens.js +12 -0
- package/lib/index.js +29 -0
- package/lib/runtime.js +42 -0
- package/lib/services/index.js +19 -0
- package/lib/services/minitool-api.js +125 -0
- package/lib/services/minitool-contract.js +76 -0
- package/lib/services/minitool-service.js +161 -0
- package/lib/services/upload-service.js +183 -0
- package/lib/utils/config-merger.js +63 -0
- package/lib/utils/cos-uploader.js +120 -0
- package/lib/utils/env-helper.js +4 -0
- package/lib/utils/helper.js +46 -0
- package/lib/utils/loading.js +52 -0
- package/lib/utils/logger.js +83 -0
- package/lib/utils/open-browser.js +32 -0
- package/lib/utils/openChrome.applescript +78 -0
- package/lib/utils/output.js +19 -0
- package/lib/utils/port-finder.js +43 -0
- package/lib/utils/project-validator.js +233 -0
- package/lib/utils/request.js +73 -0
- package/lib/utils/table.js +29 -0
- package/lib/utils/to-hump.js +5 -0
- package/lib/utils/tracker/ai-apm.js +119 -0
- package/lib/utils/tracker/apm.js +73 -0
- package/lib/utils/tracker/cli-apm.js +188 -0
- package/lib/utils/tracker/device.js +30 -0
- package/lib/utils/tracker/index.js +21 -0
- package/lib/utils/tracker/tracker.js +2 -0
- package/package.json +32 -0
- package/types/di/index.d.ts +3 -0
- package/types/di/modules.d.ts +9 -0
- package/types/di/tokens.d.ts +9 -0
- package/types/index.d.ts +14 -0
- package/types/runtime.d.ts +24 -0
- package/types/services/index.d.ts +3 -0
- package/types/services/minitool-api.d.ts +30 -0
- package/types/services/minitool-contract.d.ts +103 -0
- package/types/services/minitool-service.d.ts +29 -0
- package/types/services/upload-service.d.ts +48 -0
- package/types/utils/config-merger.d.ts +6 -0
- package/types/utils/cos-uploader.d.ts +45 -0
- package/types/utils/env-helper.d.ts +1 -0
- package/types/utils/helper.d.ts +8 -0
- package/types/utils/loading.d.ts +15 -0
- package/types/utils/logger.d.ts +27 -0
- package/types/utils/open-browser.d.ts +2 -0
- package/types/utils/output.d.ts +7 -0
- package/types/utils/port-finder.d.ts +2 -0
- package/types/utils/project-validator.d.ts +25 -0
- package/types/utils/request.d.ts +26 -0
- package/types/utils/table.d.ts +9 -0
- package/types/utils/to-hump.d.ts +1 -0
- package/types/utils/tracker/ai-apm.d.ts +68 -0
- package/types/utils/tracker/apm.d.ts +21 -0
- package/types/utils/tracker/cli-apm.d.ts +47 -0
- package/types/utils/tracker/device.d.ts +8 -0
- package/types/utils/tracker/index.d.ts +5 -0
- package/types/utils/tracker/tracker.d.ts +22 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
15
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
16
|
+
};
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.UploadService = void 0;
|
|
19
|
+
exports.normalizeUploadPermit = normalizeUploadPermit;
|
|
20
|
+
require("reflect-metadata");
|
|
21
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
22
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
23
|
+
const minitool_cli_auth_1 = require("@skillbuilder/minitool-cli-auth");
|
|
24
|
+
const inversify_1 = require("inversify");
|
|
25
|
+
const tokens_1 = require("../di/tokens");
|
|
26
|
+
const cos_uploader_1 = require("../utils/cos-uploader");
|
|
27
|
+
const minitool_api_1 = require("./minitool-api");
|
|
28
|
+
const minitool_contract_1 = require("./minitool-contract");
|
|
29
|
+
/** 与具体业务无关的上传服务:获取临时凭证并把本地文件传输到 COS。 */
|
|
30
|
+
let UploadService = class UploadService {
|
|
31
|
+
createCos;
|
|
32
|
+
constructor(createCos) {
|
|
33
|
+
this.createCos = createCos;
|
|
34
|
+
}
|
|
35
|
+
async upload(input) {
|
|
36
|
+
const filePath = node_path_1.default.resolve(input.filePath);
|
|
37
|
+
const stat = node_fs_1.default.lstatSync(filePath);
|
|
38
|
+
if (!stat.isFile() || stat.size === 0)
|
|
39
|
+
throw new Error("上传文件必须是非空普通文件");
|
|
40
|
+
const uploader = new cos_uploader_1.Uploader({
|
|
41
|
+
bizName: input.bizName,
|
|
42
|
+
scene: input.scene,
|
|
43
|
+
getToken: input.getPermit,
|
|
44
|
+
createCos: this.createCos,
|
|
45
|
+
});
|
|
46
|
+
const body = node_fs_1.default.createReadStream(filePath);
|
|
47
|
+
let result;
|
|
48
|
+
try {
|
|
49
|
+
result = await uploader.post({
|
|
50
|
+
Body: body,
|
|
51
|
+
FilePath: filePath,
|
|
52
|
+
fileInfo: {
|
|
53
|
+
name: node_path_1.default.basename(filePath),
|
|
54
|
+
size: stat.size,
|
|
55
|
+
type: input.contentType ?? "application/octet-stream",
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
if (error instanceof minitool_contract_1.MiniToolError ||
|
|
61
|
+
error instanceof minitool_cli_auth_1.AuthenticationRequiredError ||
|
|
62
|
+
error instanceof minitool_cli_auth_1.CredentialStoreUnavailableError)
|
|
63
|
+
throw error;
|
|
64
|
+
throw new Error("文件上传失败,请检查网络和上传权限");
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
body.destroy();
|
|
68
|
+
}
|
|
69
|
+
if (!result.fileId)
|
|
70
|
+
throw new Error("上传服务未返回 fileId");
|
|
71
|
+
// ROS 的附带地址可能是 HTTP 或临时签名链接。上传已经成功,业务以 fileId 为准;
|
|
72
|
+
// 仅向调用方提供可安全展示的持久地址,缺少地址不应阻断后续预览/提审。
|
|
73
|
+
const url = safeArtifactUrl(result.previewUrl) ?? safeArtifactUrl(result.url);
|
|
74
|
+
return { fileId: result.fileId, url, cloudType: result.cloudType };
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
exports.UploadService = UploadService;
|
|
78
|
+
exports.UploadService = UploadService = __decorate([
|
|
79
|
+
(0, inversify_1.injectable)(),
|
|
80
|
+
__param(0, (0, inversify_1.inject)(tokens_1.MINITOOL_TOKENS.cosClientFactory)),
|
|
81
|
+
__metadata("design:paramtypes", [Function])
|
|
82
|
+
], UploadService);
|
|
83
|
+
function safeArtifactUrl(value) {
|
|
84
|
+
if (typeof value !== "string" || !value.trim())
|
|
85
|
+
return undefined;
|
|
86
|
+
try {
|
|
87
|
+
const parsed = new URL(value);
|
|
88
|
+
if (parsed.protocol === "https:" &&
|
|
89
|
+
!parsed.username &&
|
|
90
|
+
!parsed.password &&
|
|
91
|
+
!parsed.search &&
|
|
92
|
+
!parsed.hash)
|
|
93
|
+
return parsed.href;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// 上传返回的可选展示地址格式无效时,仍保留已经成功上传的 fileId。
|
|
97
|
+
}
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
/** ROS 上传授权允许 snake_case 与 camelCase;对外只返回文件标识。 */
|
|
101
|
+
function normalizeUploadPermit(data) {
|
|
102
|
+
const raw = data.uploadTempPermits ?? data.upload_temp_permits;
|
|
103
|
+
if (!Array.isArray(raw) || !raw.length)
|
|
104
|
+
throw new Error("没有可用上传授权");
|
|
105
|
+
const permits = raw.map((item) => {
|
|
106
|
+
if (!(0, minitool_api_1.isRecord)(item))
|
|
107
|
+
throw new Error("上传授权格式无效");
|
|
108
|
+
const value = (camel, snake) => item[camel] ?? item[snake];
|
|
109
|
+
const fileIds = value("fileIds", "file_ids");
|
|
110
|
+
const uploadAddr = value("uploadAddr", "upload_addr");
|
|
111
|
+
if (!Array.isArray(fileIds) ||
|
|
112
|
+
!fileIds.length ||
|
|
113
|
+
fileIds.some((id) => typeof id !== "string" || !id) ||
|
|
114
|
+
typeof uploadAddr !== "string" ||
|
|
115
|
+
!uploadAddr ||
|
|
116
|
+
typeof item.token !== "string" ||
|
|
117
|
+
!item.token ||
|
|
118
|
+
typeof item.bucket !== "string" ||
|
|
119
|
+
!item.bucket.trim() ||
|
|
120
|
+
typeof item.region !== "string" ||
|
|
121
|
+
!item.region.trim())
|
|
122
|
+
throw new Error("上传授权字段不完整");
|
|
123
|
+
const host = new URL(uploadAddr.includes("://") ? uploadAddr : ["https:", "", uploadAddr].join("/"));
|
|
124
|
+
if (host.protocol !== "https:" || host.username || host.password || host.search || host.hash)
|
|
125
|
+
throw new Error("上传地址不安全");
|
|
126
|
+
const secretId = value("secretId", "secret_id");
|
|
127
|
+
const secretKey = value("secretKey", "secret_key");
|
|
128
|
+
if (item.bucket !== "unknown" &&
|
|
129
|
+
(typeof secretId !== "string" || typeof secretKey !== "string" || !secretId || !secretKey))
|
|
130
|
+
throw new Error("上传授权缺少临时密钥");
|
|
131
|
+
const numeric = (raw, fallback) => {
|
|
132
|
+
const number = raw === undefined ? fallback : Number(raw);
|
|
133
|
+
if ((raw !== undefined && typeof raw !== "number" && typeof raw !== "string") ||
|
|
134
|
+
(typeof raw === "string" && !raw.trim()) ||
|
|
135
|
+
!Number.isFinite(number) ||
|
|
136
|
+
number < 0)
|
|
137
|
+
throw new Error("上传授权数值无效");
|
|
138
|
+
return number;
|
|
139
|
+
};
|
|
140
|
+
const expireTime = numeric(value("expireTime", "expire_time"), Date.now() + 300_000);
|
|
141
|
+
if (expireTime <= Date.now())
|
|
142
|
+
throw new Error("上传授权已过期");
|
|
143
|
+
return {
|
|
144
|
+
secretId: typeof secretId === "string" ? secretId : undefined,
|
|
145
|
+
secretKey: typeof secretKey === "string" ? secretKey : undefined,
|
|
146
|
+
token: item.token,
|
|
147
|
+
uploadAddr,
|
|
148
|
+
bucket: item.bucket,
|
|
149
|
+
region: item.region,
|
|
150
|
+
fileIds: fileIds,
|
|
151
|
+
expireTime,
|
|
152
|
+
qos: numeric(item.qos, 0),
|
|
153
|
+
cloudType: numeric(value("cloudType", "cloud_type"), 0),
|
|
154
|
+
masterCloudId: numeric(value("masterCloudId", "master_cloud_id"), 0),
|
|
155
|
+
uploadId: numeric(value("uploadId", "upload_id"), 0),
|
|
156
|
+
cdnDomain: typeof value("cdnDomain", "cdn_domain") === "string"
|
|
157
|
+
? String(value("cdnDomain", "cdn_domain"))
|
|
158
|
+
: undefined,
|
|
159
|
+
};
|
|
160
|
+
});
|
|
161
|
+
const policy = data.uploadLimitPolicy ?? data.upload_limit_policy;
|
|
162
|
+
if (policy !== undefined && !(0, minitool_api_1.isRecord)(policy))
|
|
163
|
+
throw new Error("上传限制策略无效");
|
|
164
|
+
const maxSize = (0, minitool_api_1.isRecord)(policy) ? (policy.maxSize ?? policy.max_size) : undefined;
|
|
165
|
+
const fileTypes = (0, minitool_api_1.isRecord)(policy) ? (policy.fileTypes ?? policy.file_types) : undefined;
|
|
166
|
+
if (maxSize !== undefined &&
|
|
167
|
+
(typeof maxSize !== "number" || !Number.isFinite(maxSize) || maxSize < 0))
|
|
168
|
+
throw new Error("上传大小限制无效");
|
|
169
|
+
if (fileTypes !== undefined &&
|
|
170
|
+
(!Array.isArray(fileTypes) ||
|
|
171
|
+
fileTypes.some((type) => typeof type !== "string" || !type.trim())))
|
|
172
|
+
throw new Error("上传文件类型限制无效");
|
|
173
|
+
return {
|
|
174
|
+
uploadTempPermits: permits,
|
|
175
|
+
uploadLimitPolicy: (0, minitool_api_1.isRecord)(policy)
|
|
176
|
+
? {
|
|
177
|
+
// 平台以 0 表示不限大小;本地产物仍受 10 MiB 上限约束。
|
|
178
|
+
maxSize: typeof maxSize === "number" && maxSize > 0 ? maxSize : undefined,
|
|
179
|
+
fileTypes: fileTypes,
|
|
180
|
+
}
|
|
181
|
+
: undefined,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.mergeProjectConfigs = mergeProjectConfigs;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
/**
|
|
10
|
+
* 历史 Agent/云函数配置工具,当前小工具 CLI 不调用。
|
|
11
|
+
* 合并 project.config.json 和 agent-cloudbase-functions.json 配置。
|
|
12
|
+
* 将 project.config.json 中的白名单属性(agentId、version)合并到 agent-cloudbase-functions.json
|
|
13
|
+
*/
|
|
14
|
+
function mergeProjectConfigs(workDir) {
|
|
15
|
+
const projectConfigPath = path_1.default.join(workDir, "project.config.json");
|
|
16
|
+
const agentConfigPath = path_1.default.join(workDir, "agent-cloudbase-functions.json");
|
|
17
|
+
// 检查文件是否存在
|
|
18
|
+
if (!fs_1.default.existsSync(projectConfigPath)) {
|
|
19
|
+
throw new Error(`project.config.json 文件不存在: ${projectConfigPath}`);
|
|
20
|
+
}
|
|
21
|
+
if (!fs_1.default.existsSync(agentConfigPath)) {
|
|
22
|
+
throw new Error(`agent-cloudbase-functions.json 文件不存在: ${agentConfigPath}`);
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
// 读取配置文件
|
|
26
|
+
const projectConfigContent = fs_1.default.readFileSync(projectConfigPath, "utf8");
|
|
27
|
+
const agentConfigContent = fs_1.default.readFileSync(agentConfigPath, "utf8");
|
|
28
|
+
const projectConfig = JSON.parse(projectConfigContent);
|
|
29
|
+
const agentConfig = JSON.parse(agentConfigContent);
|
|
30
|
+
// 合并白名单属性
|
|
31
|
+
let hasChanges = false;
|
|
32
|
+
if (projectConfig.agentId && projectConfig.agentId !== agentConfig.agentId) {
|
|
33
|
+
agentConfig.agentId = projectConfig.agentId;
|
|
34
|
+
hasChanges = true;
|
|
35
|
+
}
|
|
36
|
+
if (projectConfig.version && projectConfig.version !== agentConfig.version) {
|
|
37
|
+
agentConfig.version = projectConfig.version;
|
|
38
|
+
hasChanges = true;
|
|
39
|
+
}
|
|
40
|
+
if (projectConfig.desc && projectConfig.desc !== agentConfig.desc) {
|
|
41
|
+
agentConfig.desc = projectConfig.desc;
|
|
42
|
+
hasChanges = true;
|
|
43
|
+
}
|
|
44
|
+
// 如果有变更,写回文件
|
|
45
|
+
if (hasChanges) {
|
|
46
|
+
const updatedContent = JSON.stringify(agentConfig, null, 2);
|
|
47
|
+
fs_1.default.writeFileSync(agentConfigPath, updatedContent, "utf8");
|
|
48
|
+
console.log("✅ 配置文件合并完成,已更新 agent-cloudbase-functions.json");
|
|
49
|
+
if (projectConfig.agentId) {
|
|
50
|
+
console.log(` agentId: ${projectConfig.agentId}`);
|
|
51
|
+
}
|
|
52
|
+
if (projectConfig.version) {
|
|
53
|
+
console.log(` version: ${projectConfig.version}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
if (error instanceof SyntaxError) {
|
|
59
|
+
throw new Error(`配置文件格式错误,请检查 JSON 格式是否正确: ${error.message}`);
|
|
60
|
+
}
|
|
61
|
+
throw new Error(`合并配置文件时发生错误: ${error.message}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Uploader = void 0;
|
|
7
|
+
const cos_nodejs_sdk_v5_1 = __importDefault(require("cos-nodejs-sdk-v5"));
|
|
8
|
+
const to_hump_1 = require("./to-hump");
|
|
9
|
+
class Uploader {
|
|
10
|
+
// 上传业务,用于和 scene 指定上传云服务的 bucket,需业务方从媒体中台申请
|
|
11
|
+
bizName;
|
|
12
|
+
// 上传场景,用于和 bizName 指定上传云服务的 bucket,需业务方从媒体中台申请
|
|
13
|
+
scene;
|
|
14
|
+
// 获取上传 token 的异步请求
|
|
15
|
+
getToken;
|
|
16
|
+
enableResume;
|
|
17
|
+
createCos;
|
|
18
|
+
constructor(args) {
|
|
19
|
+
this.bizName = args.bizName;
|
|
20
|
+
this.scene = args.scene;
|
|
21
|
+
this.getToken = args.getToken;
|
|
22
|
+
this.enableResume = args.enableResume;
|
|
23
|
+
this.createCos = args.createCos ?? ((options) => new cos_nodejs_sdk_v5_1.default(options));
|
|
24
|
+
}
|
|
25
|
+
getPermit(file, fileInfo, fileFormat, persistentOps, persistentNotifyUrl) {
|
|
26
|
+
return this.getToken().then(({ uploadTempPermits, uploadLimitPolicy }) => {
|
|
27
|
+
const fileType = fileInfo.type;
|
|
28
|
+
const fileSize = fileInfo.size;
|
|
29
|
+
if (uploadLimitPolicy) {
|
|
30
|
+
const { fileTypes, maxSize } = uploadLimitPolicy;
|
|
31
|
+
if (fileTypes?.length &&
|
|
32
|
+
!fileTypes
|
|
33
|
+
.reduce((a, c) => [...a, ...c.split("/")], [])
|
|
34
|
+
.some((f) => fileType.endsWith(f))) {
|
|
35
|
+
throw new Error(`文件类型 ${fileType} 不在可上传的文件类型 ${fileTypes.join(", ")} 中。`);
|
|
36
|
+
}
|
|
37
|
+
if (maxSize !== undefined && fileSize > maxSize) {
|
|
38
|
+
throw new Error(`文件尺寸(${fileSize / (1024 * 1024)} mb)超过最大尺寸(${maxSize / (1024 * 1024)} mb)。`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (!uploadTempPermits?.length) {
|
|
42
|
+
throw new Error("token 获取失败。");
|
|
43
|
+
}
|
|
44
|
+
const [permit] = uploadTempPermits.sort((a, b) => b.qos - a.qos);
|
|
45
|
+
return permit;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
post(options) {
|
|
49
|
+
const startTime = Date.now();
|
|
50
|
+
const { fileFormat, onInstanceCreated, ...rest } = options;
|
|
51
|
+
return this.getPermit(options.Body, options.fileInfo, fileFormat)
|
|
52
|
+
.then((permit) => {
|
|
53
|
+
const [fileId] = permit.fileIds;
|
|
54
|
+
const cosInitParams = {
|
|
55
|
+
Domain: permit.uploadAddr,
|
|
56
|
+
getAuthorization(_, callback) {
|
|
57
|
+
callback({
|
|
58
|
+
TmpSecretId: permit.secretId || "null",
|
|
59
|
+
TmpSecretKey: permit.secretKey || "null",
|
|
60
|
+
SecurityToken: permit.token,
|
|
61
|
+
StartTime: Math.floor(startTime / 1000),
|
|
62
|
+
ExpiredTime: Math.floor(permit.expireTime / 1000),
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
if (permit.bucket === "unknown") {
|
|
67
|
+
// @ts-ignore 特殊参数,自建通道时没有 bucket
|
|
68
|
+
cosInitParams.AppId = "1251524319";
|
|
69
|
+
}
|
|
70
|
+
const cos = this.createCos(cosInitParams);
|
|
71
|
+
if (onInstanceCreated) {
|
|
72
|
+
onInstanceCreated(cos);
|
|
73
|
+
}
|
|
74
|
+
return cos
|
|
75
|
+
.uploadFile({
|
|
76
|
+
Bucket: permit.bucket,
|
|
77
|
+
Region: permit.region,
|
|
78
|
+
Key: fileId,
|
|
79
|
+
...rest,
|
|
80
|
+
})
|
|
81
|
+
.then((resp) => {
|
|
82
|
+
const payload = {
|
|
83
|
+
...resp,
|
|
84
|
+
bizName: this.bizName,
|
|
85
|
+
scene: this.scene,
|
|
86
|
+
cloudType: permit.cloudType,
|
|
87
|
+
fileId,
|
|
88
|
+
sliceSize: rest.SliceSize || 1048576,
|
|
89
|
+
isSlice: options.fileInfo.size > (rest.SliceSize || 1048576),
|
|
90
|
+
tokenFail: false,
|
|
91
|
+
};
|
|
92
|
+
if (permit.cdnDomain) {
|
|
93
|
+
const cdnLink = `${permit.cdnDomain}/${fileId}`;
|
|
94
|
+
payload.cdnDomain = permit.cdnDomain;
|
|
95
|
+
payload.url = cdnLink;
|
|
96
|
+
payload.previewUrl = cdnLink;
|
|
97
|
+
payload.staticUrl = cdnLink;
|
|
98
|
+
}
|
|
99
|
+
// 返回所有自定义 header( 'x-ros' 自检服务约定)
|
|
100
|
+
if (resp?.headers) {
|
|
101
|
+
Object.keys(resp.headers).forEach((v) => {
|
|
102
|
+
if (v.startsWith("x-ros-")) {
|
|
103
|
+
const key = (0, to_hump_1.toHump)(v.split("x-ros-")[1]);
|
|
104
|
+
// @ts-ignore
|
|
105
|
+
payload[key] = resp.headers?.[v];
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return payload;
|
|
110
|
+
})
|
|
111
|
+
.catch((e) => {
|
|
112
|
+
throw e;
|
|
113
|
+
});
|
|
114
|
+
})
|
|
115
|
+
.catch((e) => {
|
|
116
|
+
throw e;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
exports.Uploader = Uploader;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.toHump = void 0;
|
|
7
|
+
exports.getFixedFormatTime = getFixedFormatTime;
|
|
8
|
+
exports.getNonce = getNonce;
|
|
9
|
+
exports.getMd5 = getMd5;
|
|
10
|
+
exports.guid = guid;
|
|
11
|
+
exports.retry = retry;
|
|
12
|
+
const node_crypto_1 = require("node:crypto");
|
|
13
|
+
const dayjs_1 = __importDefault(require("dayjs"));
|
|
14
|
+
var to_hump_1 = require("./to-hump");
|
|
15
|
+
Object.defineProperty(exports, "toHump", { enumerable: true, get: function () { return to_hump_1.toHump; } });
|
|
16
|
+
function getFixedFormatTime(format, timeDistance = 0) {
|
|
17
|
+
return (0, dayjs_1.default)().add(timeDistance, "millisecond").format(format);
|
|
18
|
+
}
|
|
19
|
+
/** 安全随机数;可用于请求 nonce,不使用 Math.random。 */
|
|
20
|
+
function getNonce() {
|
|
21
|
+
return (0, node_crypto_1.randomBytes)(8).toString("hex");
|
|
22
|
+
}
|
|
23
|
+
/** 仅供非安全场景的兼容性摘要,不应用于签名或密码存储。 */
|
|
24
|
+
function getMd5(value) {
|
|
25
|
+
return (0, node_crypto_1.createHash)("md5").update(value).digest("hex");
|
|
26
|
+
}
|
|
27
|
+
function guid() {
|
|
28
|
+
return (0, node_crypto_1.randomUUID)();
|
|
29
|
+
}
|
|
30
|
+
async function retry(fn, maxRetries = 5, delay = 0) {
|
|
31
|
+
if (!Number.isInteger(maxRetries) || maxRetries < 1) {
|
|
32
|
+
throw new RangeError("maxRetries 必须是正整数");
|
|
33
|
+
}
|
|
34
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
35
|
+
try {
|
|
36
|
+
return await fn();
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
if (attempt === maxRetries)
|
|
40
|
+
throw error;
|
|
41
|
+
if (delay > 0)
|
|
42
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
throw new Error("重试次数已用尽");
|
|
46
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Loading = void 0;
|
|
7
|
+
exports.loadingFactory = loadingFactory;
|
|
8
|
+
exports.execWithLoading = execWithLoading;
|
|
9
|
+
const ora_1 = __importDefault(require("ora"));
|
|
10
|
+
class Loading {
|
|
11
|
+
spinner = (0, ora_1.default)({ discardStdin: false });
|
|
12
|
+
set text(value) {
|
|
13
|
+
this.spinner.text = value;
|
|
14
|
+
}
|
|
15
|
+
start(text) {
|
|
16
|
+
this.spinner.start(text);
|
|
17
|
+
}
|
|
18
|
+
stop() {
|
|
19
|
+
this.spinner.stop();
|
|
20
|
+
}
|
|
21
|
+
succeed(text) {
|
|
22
|
+
this.spinner.succeed(text);
|
|
23
|
+
}
|
|
24
|
+
fail(text) {
|
|
25
|
+
this.spinner.fail(text);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.Loading = Loading;
|
|
29
|
+
function loadingFactory() {
|
|
30
|
+
return new Loading();
|
|
31
|
+
}
|
|
32
|
+
async function execWithLoading(task, options = {}) {
|
|
33
|
+
const loading = loadingFactory();
|
|
34
|
+
try {
|
|
35
|
+
loading.start(options.startTip || "执行中...");
|
|
36
|
+
const result = await task((text) => {
|
|
37
|
+
loading.text = text;
|
|
38
|
+
});
|
|
39
|
+
if (options.successTip)
|
|
40
|
+
loading.succeed(options.successTip);
|
|
41
|
+
else
|
|
42
|
+
loading.stop();
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
if (options.failTip)
|
|
47
|
+
loading.fail(options.failTip);
|
|
48
|
+
else
|
|
49
|
+
loading.stop();
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.logger = exports.Logger = void 0;
|
|
7
|
+
const console_1 = require("console");
|
|
8
|
+
const util_1 = require("util");
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const log_symbols_1 = __importDefault(require("log-symbols"));
|
|
11
|
+
const ora_1 = __importDefault(require("ora"));
|
|
12
|
+
const terminal_link_1 = __importDefault(require("terminal-link"));
|
|
13
|
+
class Logger {
|
|
14
|
+
c = {
|
|
15
|
+
_times: new Map(),
|
|
16
|
+
log(a, ...args) {
|
|
17
|
+
// @ts-ignore
|
|
18
|
+
this.debug((0, util_1.format)(a, ...args));
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
verboseEnabled;
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
const { verbose } = options;
|
|
24
|
+
this.verboseEnabled = verbose || false;
|
|
25
|
+
}
|
|
26
|
+
breakLine() {
|
|
27
|
+
console.log();
|
|
28
|
+
}
|
|
29
|
+
log(...args) {
|
|
30
|
+
console.log(`▶️ ${chalk_1.default.gray(`[${new Date().toLocaleString()}]`)} ${args.join(" ")}`);
|
|
31
|
+
}
|
|
32
|
+
info(msg) {
|
|
33
|
+
console.log(`▶️ ${chalk_1.default.gray(`[${new Date().toLocaleString()}]`)} ${log_symbols_1.default.info} ${msg}`);
|
|
34
|
+
}
|
|
35
|
+
success(msg) {
|
|
36
|
+
console.log(`▶️ ${chalk_1.default.gray(`[${new Date().toLocaleString()}]`)}${log_symbols_1.default.success} ${msg}`);
|
|
37
|
+
}
|
|
38
|
+
warn(msg) {
|
|
39
|
+
console.log(`▶️ ${chalk_1.default.gray(`[${new Date().toLocaleString()}]`)}${log_symbols_1.default.warning} ${msg}`);
|
|
40
|
+
}
|
|
41
|
+
error(msg) {
|
|
42
|
+
console.log(`▶️ ${chalk_1.default.red(`[${new Date().toLocaleString()}]`)}${log_symbols_1.default.error} ${msg}`);
|
|
43
|
+
}
|
|
44
|
+
verbose(...args) {
|
|
45
|
+
if (this.verboseEnabled) {
|
|
46
|
+
const msg = args.join(" ▶️ ");
|
|
47
|
+
console.log(`${chalk_1.default.bold("[debug]")} ${chalk_1.default.gray(`[${new Date().toLocaleString()}]`)} ${msg}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* alias for verbose
|
|
52
|
+
*/
|
|
53
|
+
debug(...args) {
|
|
54
|
+
return this.verbose(...args);
|
|
55
|
+
}
|
|
56
|
+
genClickableLink(link) {
|
|
57
|
+
if (terminal_link_1.default.isSupported) {
|
|
58
|
+
const clickablelink = (0, terminal_link_1.default)(link, link);
|
|
59
|
+
return chalk_1.default.bold.cyan(clickablelink);
|
|
60
|
+
}
|
|
61
|
+
return chalk_1.default.bold.underline.cyan(link);
|
|
62
|
+
}
|
|
63
|
+
printClickableLink(link) {
|
|
64
|
+
const clickLink = this.genClickableLink(link);
|
|
65
|
+
this.info(clickLink);
|
|
66
|
+
}
|
|
67
|
+
async time(label, fn) {
|
|
68
|
+
const promise = typeof fn === "function" ? fn() : fn;
|
|
69
|
+
if (this.verboseEnabled) {
|
|
70
|
+
this.c.log(label);
|
|
71
|
+
console_1.Console.prototype.time.call(this.c, label);
|
|
72
|
+
const r = await promise;
|
|
73
|
+
console_1.Console.prototype.timeEnd.call(this.c, label);
|
|
74
|
+
return r;
|
|
75
|
+
}
|
|
76
|
+
return promise;
|
|
77
|
+
}
|
|
78
|
+
spinner(text, color = "cyan") {
|
|
79
|
+
return (0, ora_1.default)({ text, color }).start();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.Logger = Logger;
|
|
83
|
+
exports.logger = new Logger();
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.openBrowser = openBrowser;
|
|
7
|
+
const node_child_process_1 = require("node:child_process");
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const open_1 = __importDefault(require("open"));
|
|
10
|
+
/** 打开本地预览页。参数通过 execFileSync 的 argv 传递,不经过 shell。 */
|
|
11
|
+
async function openBrowser(host, port) {
|
|
12
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
13
|
+
throw new RangeError("端口号无效");
|
|
14
|
+
const url = new URL(`http://${host}:${port}`);
|
|
15
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) {
|
|
16
|
+
throw new Error("浏览器地址无效");
|
|
17
|
+
}
|
|
18
|
+
if (process.platform === "darwin") {
|
|
19
|
+
try {
|
|
20
|
+
(0, node_child_process_1.execFileSync)("osascript", [node_path_1.default.join(__dirname, "openChrome.applescript"), url.toString()], {
|
|
21
|
+
stdio: "ignore",
|
|
22
|
+
timeout: 2500,
|
|
23
|
+
});
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Chrome 不可用时改由系统默认浏览器打开。
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
await (0, open_1.default)(url.toString());
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
property targetTab: null
|
|
2
|
+
property targetTabIndex: -1
|
|
3
|
+
property targetWindow: null
|
|
4
|
+
|
|
5
|
+
on run argv
|
|
6
|
+
set theURL to item 1 of argv
|
|
7
|
+
|
|
8
|
+
with timeout of 2 seconds
|
|
9
|
+
tell application "Google Chrome"
|
|
10
|
+
|
|
11
|
+
if (count every window) = 0 then
|
|
12
|
+
make new window
|
|
13
|
+
end if
|
|
14
|
+
|
|
15
|
+
-- 1: Looking for tab running debugger
|
|
16
|
+
-- then, Reload debugging tab if found
|
|
17
|
+
-- then return
|
|
18
|
+
set found to my lookupTabWithUrl(theURL)
|
|
19
|
+
if found then
|
|
20
|
+
set targetWindow's active tab index to targetTabIndex
|
|
21
|
+
tell targetTab to reload
|
|
22
|
+
tell targetWindow to activate
|
|
23
|
+
set index of targetWindow to 1
|
|
24
|
+
return
|
|
25
|
+
end if
|
|
26
|
+
|
|
27
|
+
-- 2: Looking for Empty tab
|
|
28
|
+
-- In case debugging tab was not found
|
|
29
|
+
-- We try to find an empty tab instead
|
|
30
|
+
set found to my lookupTabWithUrl("chrome://newtab/")
|
|
31
|
+
if found then
|
|
32
|
+
set targetWindow's active tab index to targetTabIndex
|
|
33
|
+
set URL of targetTab to theURL
|
|
34
|
+
tell targetWindow to activate
|
|
35
|
+
return
|
|
36
|
+
end if
|
|
37
|
+
|
|
38
|
+
-- 3: Create new tab
|
|
39
|
+
-- both debugging and empty tab were not found
|
|
40
|
+
-- make a new tab with url
|
|
41
|
+
tell window 1
|
|
42
|
+
activate
|
|
43
|
+
make new tab with properties {URL:theURL}
|
|
44
|
+
end tell
|
|
45
|
+
end tell
|
|
46
|
+
end timeout
|
|
47
|
+
end run
|
|
48
|
+
|
|
49
|
+
-- Function:
|
|
50
|
+
-- Lookup tab with given url
|
|
51
|
+
-- if found, store tab, index, and window in properties
|
|
52
|
+
-- (properties were declared on top of file)
|
|
53
|
+
on lookupTabWithUrl(lookupUrl)
|
|
54
|
+
tell application "Google Chrome"
|
|
55
|
+
-- Find a tab with the given url
|
|
56
|
+
set found to false
|
|
57
|
+
set theTabIndex to -1
|
|
58
|
+
repeat with theWindow in every window
|
|
59
|
+
set theTabIndex to 0
|
|
60
|
+
repeat with theTab in every tab of theWindow
|
|
61
|
+
set theTabIndex to theTabIndex + 1
|
|
62
|
+
if (theTab's URL as string) contains lookupUrl then
|
|
63
|
+
-- assign tab, tab index, and window to properties
|
|
64
|
+
set targetTab to theTab
|
|
65
|
+
set targetTabIndex to theTabIndex
|
|
66
|
+
set targetWindow to theWindow
|
|
67
|
+
set found to true
|
|
68
|
+
exit repeat
|
|
69
|
+
end if
|
|
70
|
+
end repeat
|
|
71
|
+
|
|
72
|
+
if found then
|
|
73
|
+
exit repeat
|
|
74
|
+
end if
|
|
75
|
+
end repeat
|
|
76
|
+
end tell
|
|
77
|
+
return found
|
|
78
|
+
end lookupTabWithUrl
|