mbler 0.2.2 → 0.2.3-alpha.r2
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 +10 -11
- package/bin/mcx-tsc.js +1 -1
- package/dist/{build.esm.js → build.esm.mjs} +14 -9
- package/dist/build.esm.mjs.map +1 -0
- package/dist/build.js +13 -8
- package/dist/build.js.map +1 -1
- package/dist/index.d.ts +56 -2
- package/dist/{index.esm.js → index.esm.mjs} +742 -23
- package/dist/index.esm.mjs.map +1 -0
- package/dist/index.js +740 -21
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
- package/dist/build.esm.js.map +0 -1
- package/dist/index.esm.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ var os = require('node:os');
|
|
|
8
8
|
var readline = require('readline');
|
|
9
9
|
var npmRegistryFetch = require('npm-registry-fetch');
|
|
10
10
|
var node_child_process = require('node:child_process');
|
|
11
|
+
var AdmZip = require('adm-zip');
|
|
11
12
|
|
|
12
13
|
function _interopNamespaceDefault(e) {
|
|
13
14
|
var n = Object.create(null);
|
|
@@ -56,12 +57,13 @@ var types = /*#__PURE__*/Object.freeze({
|
|
|
56
57
|
templateMblerConfig: templateMblerConfig
|
|
57
58
|
});
|
|
58
59
|
|
|
59
|
-
var MBLERVersion = { commit: `commit
|
|
60
|
+
var MBLERVersion = { commit: `commit fc1fee7f44a0ee08388276ad9288cbb5c0263414
|
|
61
|
+
Merge: f36a4b5 c393c36
|
|
60
62
|
Author: Ruanhor <3915264929@qq.com>
|
|
61
|
-
Date: Sun Apr
|
|
63
|
+
Date: Sun Apr 26 17:05:29 2026 +0800
|
|
62
64
|
|
|
63
|
-
|
|
64
|
-
`, version: "0.2.
|
|
65
|
+
Merge branch 'master' of github.com:RuanhoR/mbler
|
|
66
|
+
`, version: "0.2.3-alpha.r2" };
|
|
65
67
|
|
|
66
68
|
var ZhLang = {
|
|
67
69
|
description: `MBLER
|
|
@@ -118,6 +120,9 @@ git https://github.com/RuanhoR/mbler/`,
|
|
|
118
120
|
enabled: "工作目录功能已启用",
|
|
119
121
|
invalidParam: "参数错误,请使用 on 或 off",
|
|
120
122
|
},
|
|
123
|
+
publish: {
|
|
124
|
+
askTip: "输入MCBE游戏绝对目录 (如 /sdcard/Android/data/com.mojang.minecraftpe/files/games),以进行下一步: "
|
|
125
|
+
}
|
|
121
126
|
};
|
|
122
127
|
|
|
123
128
|
var enLang = {
|
|
@@ -168,6 +173,9 @@ var enLang = {
|
|
|
168
173
|
enabled: "Work dir enabled",
|
|
169
174
|
invalidParam: "Invalid parameter, use on or off",
|
|
170
175
|
},
|
|
176
|
+
publish: {
|
|
177
|
+
askTip: "Plase input the absolute path of your MCBE game directory (like /sdcard/Android/data/com.mojang.minecraftpe/files/games) for next step: "
|
|
178
|
+
}
|
|
171
179
|
};
|
|
172
180
|
|
|
173
181
|
const configPath = path.join(require("node:os").homedir(), ".cache/mbler/lang.db");
|
|
@@ -383,6 +391,16 @@ async function FileExsit(file) {
|
|
|
383
391
|
}
|
|
384
392
|
return false;
|
|
385
393
|
}
|
|
394
|
+
async function ReadProjectMblerConfig(project) {
|
|
395
|
+
const fileExport = await import(path__namespace.join(project, BuildConfig.ConfigFile));
|
|
396
|
+
const file = fileExport.default || {};
|
|
397
|
+
for (const key in file) {
|
|
398
|
+
if (!(key in templateMblerConfig)) {
|
|
399
|
+
throw new Error(`[read config]: read config from '${project}' error: Unexpected '${key}'`);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return file;
|
|
403
|
+
}
|
|
386
404
|
async function readFileAsJson(filePath) {
|
|
387
405
|
try {
|
|
388
406
|
const content = await fs__namespace.readFile(filePath, 'utf-8');
|
|
@@ -468,7 +486,7 @@ const input = (function () {
|
|
|
468
486
|
curr(currstr);
|
|
469
487
|
curr = null;
|
|
470
488
|
currstr = '';
|
|
471
|
-
console.log('');
|
|
489
|
+
console.log('\n');
|
|
472
490
|
return;
|
|
473
491
|
}
|
|
474
492
|
if (raw === 'backspace') {
|
|
@@ -534,6 +552,15 @@ function runCommand(param, cwd, stdio) {
|
|
|
534
552
|
});
|
|
535
553
|
return promise;
|
|
536
554
|
}
|
|
555
|
+
async function fileExists(file) {
|
|
556
|
+
try {
|
|
557
|
+
await fs__namespace.stat(file);
|
|
558
|
+
return true;
|
|
559
|
+
}
|
|
560
|
+
catch {
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
537
564
|
|
|
538
565
|
const logFile = path.join(os.homedir(), ".cache/mbler/latest.log");
|
|
539
566
|
function _clean(promise) {
|
|
@@ -689,7 +716,8 @@ class WorkDirManage {
|
|
|
689
716
|
const config = {
|
|
690
717
|
tmpdir: path__namespace.join(os.tmpdir(), ".mbler"),
|
|
691
718
|
mcxVersion: "0.0.2-beta.r7",
|
|
692
|
-
mcxCoreVersion: "0.0.6"
|
|
719
|
+
mcxCoreVersion: "0.0.6",
|
|
720
|
+
defaultPmnxBASE: "https://d.pmnx.qzz.io"
|
|
693
721
|
};
|
|
694
722
|
|
|
695
723
|
/**
|
|
@@ -1016,29 +1044,720 @@ function langCommand(cliParam, workdir) {
|
|
|
1016
1044
|
return 0;
|
|
1017
1045
|
}
|
|
1018
1046
|
|
|
1019
|
-
function
|
|
1020
|
-
|
|
1021
|
-
|
|
1047
|
+
function createFullZip(dir) {
|
|
1048
|
+
const zip = new AdmZip();
|
|
1049
|
+
zip.addLocalFolder(dir);
|
|
1050
|
+
return zip;
|
|
1051
|
+
}
|
|
1052
|
+
async function createZipWithMoreFolder(dir) {
|
|
1053
|
+
const zip = new AdmZip();
|
|
1054
|
+
for (const folder of dir) {
|
|
1055
|
+
await zip.addLocalFolderPromise(folder[0], {
|
|
1056
|
+
zipPath: folder[1]
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
return zip;
|
|
1060
|
+
}
|
|
1061
|
+
async function generateRelease(build) {
|
|
1062
|
+
if (!build.outdirs)
|
|
1063
|
+
throw new Error("invalid Build");
|
|
1064
|
+
if (node_process.env.BUILD_MODULE !== "release")
|
|
1065
|
+
return;
|
|
1066
|
+
let zip;
|
|
1067
|
+
if (build.module == "all") {
|
|
1068
|
+
zip = await createZipWithMoreFolder([
|
|
1069
|
+
[build.outdirs?.behavior, "behavior"],
|
|
1070
|
+
[build.outdirs?.resources, "resources"]
|
|
1071
|
+
]);
|
|
1072
|
+
}
|
|
1073
|
+
else if (build.module == "behavior") {
|
|
1074
|
+
zip = createFullZip(build.outdirs?.behavior);
|
|
1075
|
+
}
|
|
1076
|
+
else {
|
|
1077
|
+
zip = createFullZip(build.outdirs?.resources);
|
|
1078
|
+
}
|
|
1079
|
+
await zip.writeZipPromise(build.outdirs?.dist);
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
class ConfigManger {
|
|
1083
|
+
static defaultConfigPoint = path.join(os.homedir(), ".config", "_config.json");
|
|
1084
|
+
static cacheValue = {};
|
|
1085
|
+
static lockPromise = null;
|
|
1086
|
+
static lockResolver = null;
|
|
1087
|
+
static cacheTTL = 5000; // 5秒缓存
|
|
1088
|
+
static lastAccess = 0;
|
|
1089
|
+
static currentConfigPath = '';
|
|
1090
|
+
static async acquireLock() {
|
|
1091
|
+
while (this.lockPromise) {
|
|
1092
|
+
await this.lockPromise;
|
|
1093
|
+
}
|
|
1094
|
+
this.lockPromise = new Promise((resolve) => {
|
|
1095
|
+
this.lockResolver = resolve;
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
static releaseLock() {
|
|
1099
|
+
if (this.lockResolver) {
|
|
1100
|
+
this.lockResolver();
|
|
1101
|
+
this.lockPromise = null;
|
|
1102
|
+
this.lockResolver = null;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
static isCacheValid(configPath) {
|
|
1106
|
+
return this.currentConfigPath === configPath &&
|
|
1107
|
+
Date.now() - this.lastAccess < this.cacheTTL &&
|
|
1108
|
+
Object.keys(this.cacheValue).length > 0;
|
|
1109
|
+
}
|
|
1110
|
+
static async loadConfigToCache(configPath) {
|
|
1111
|
+
try {
|
|
1112
|
+
await this.acquireLock();
|
|
1113
|
+
if (this.isCacheValid(configPath)) {
|
|
1114
|
+
this.releaseLock();
|
|
1115
|
+
return;
|
|
1116
|
+
}
|
|
1117
|
+
const configData = await readFileAsJson(configPath);
|
|
1118
|
+
this.cacheValue = configData;
|
|
1119
|
+
this.currentConfigPath = configPath;
|
|
1120
|
+
this.lastAccess = Date.now();
|
|
1121
|
+
this.releaseLock();
|
|
1122
|
+
}
|
|
1123
|
+
catch (error) {
|
|
1124
|
+
this.releaseLock();
|
|
1125
|
+
this.cacheValue = {};
|
|
1126
|
+
throw error;
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
static async saveCacheToFile(configPath) {
|
|
1130
|
+
try {
|
|
1131
|
+
await this.acquireLock();
|
|
1132
|
+
await writeJSON(configPath, this.cacheValue);
|
|
1133
|
+
this.lastAccess = Date.now();
|
|
1134
|
+
this.releaseLock();
|
|
1135
|
+
}
|
|
1136
|
+
catch (error) {
|
|
1137
|
+
this.releaseLock();
|
|
1138
|
+
throw error;
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
static async getConfigPoint() {
|
|
1142
|
+
try {
|
|
1143
|
+
const file = await fs$1.readFile(path.join(config.tmpdir, "_config_point.json"));
|
|
1144
|
+
const configPoint = JSON.parse(file.toString());
|
|
1145
|
+
return configPoint.point;
|
|
1146
|
+
}
|
|
1147
|
+
catch {
|
|
1148
|
+
if (!await fileExists(this.defaultConfigPoint)) {
|
|
1149
|
+
await writeJSON(this.defaultConfigPoint, {});
|
|
1150
|
+
}
|
|
1151
|
+
await writeJSON(path.join(config.tmpdir, "_config_point.json"), {
|
|
1152
|
+
point: this.defaultConfigPoint,
|
|
1153
|
+
update: new Date()
|
|
1154
|
+
});
|
|
1155
|
+
return this.defaultConfigPoint;
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
static async setConfigPoint(point) {
|
|
1159
|
+
if (!await fileExists(point)) {
|
|
1160
|
+
throw new Error("[mbler config]: can't bind config file: " + point);
|
|
1161
|
+
}
|
|
1162
|
+
await writeJSON(path.join(config.tmpdir, "_config_point.json"), {
|
|
1163
|
+
point,
|
|
1164
|
+
update: new Date()
|
|
1165
|
+
});
|
|
1166
|
+
}
|
|
1167
|
+
static async getKey(key) {
|
|
1168
|
+
try {
|
|
1169
|
+
const configPath = await this.getConfigPoint();
|
|
1170
|
+
// 使用缓存,避免频繁文件读取
|
|
1171
|
+
if (!this.isCacheValid(configPath)) {
|
|
1172
|
+
await this.loadConfigToCache(configPath);
|
|
1173
|
+
}
|
|
1174
|
+
return this.cacheValue[key];
|
|
1175
|
+
}
|
|
1176
|
+
catch (error) {
|
|
1177
|
+
return undefined;
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
static async setKey(key, value) {
|
|
1181
|
+
try {
|
|
1182
|
+
const configPath = await this.getConfigPoint();
|
|
1183
|
+
// 确保缓存是最新的
|
|
1184
|
+
if (!this.isCacheValid(configPath)) {
|
|
1185
|
+
await this.loadConfigToCache(configPath);
|
|
1186
|
+
}
|
|
1187
|
+
this.cacheValue[key] = value;
|
|
1188
|
+
this.saveCacheToFile(configPath).catch(error => {
|
|
1189
|
+
Logger.e('ConfigManger', 'Failed to save config to file:' + error);
|
|
1190
|
+
});
|
|
1191
|
+
return true;
|
|
1192
|
+
}
|
|
1193
|
+
catch (error) {
|
|
1194
|
+
return false;
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
static async init(defaultConfig = {}) {
|
|
1198
|
+
const configPath = await this.getConfigPoint();
|
|
1199
|
+
if (!await fileExists(configPath)) {
|
|
1200
|
+
await writeJSON(configPath, defaultConfig);
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
class TokenManger {
|
|
1206
|
+
static setToken(newToken) {
|
|
1207
|
+
ConfigManger.setKey("token", newToken);
|
|
1208
|
+
this.task = this.requestAPI();
|
|
1209
|
+
}
|
|
1210
|
+
static getToken() { return ConfigManger.getKey("token"); }
|
|
1211
|
+
static isLogin = false;
|
|
1212
|
+
static task;
|
|
1213
|
+
static isLoading = true;
|
|
1214
|
+
static user = null;
|
|
1215
|
+
static async init() {
|
|
1216
|
+
const token = await this.getToken();
|
|
1217
|
+
if (token) {
|
|
1218
|
+
this.task = this.requestAPI();
|
|
1219
|
+
await this.task;
|
|
1220
|
+
}
|
|
1221
|
+
else {
|
|
1222
|
+
this.isLoading = false;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
static async waitVeirfy() {
|
|
1226
|
+
if (!this.task)
|
|
1227
|
+
await this.init();
|
|
1228
|
+
console.debug(this.task);
|
|
1229
|
+
return await this.task;
|
|
1230
|
+
}
|
|
1231
|
+
static async requestAPI() {
|
|
1232
|
+
this.isLoading = true;
|
|
1233
|
+
this.isLogin = false;
|
|
1234
|
+
const token = await this.getToken();
|
|
1235
|
+
const result = await fetch(`${config.defaultPmnxBASE}/token/${token}/verify`, {
|
|
1236
|
+
method: "GET",
|
|
1237
|
+
credentials: "omit",
|
|
1238
|
+
});
|
|
1239
|
+
this.isLoading = false;
|
|
1240
|
+
const body = await result.json();
|
|
1241
|
+
if (result.ok) {
|
|
1242
|
+
this.isLogin = true;
|
|
1243
|
+
this.user = body.data;
|
|
1244
|
+
}
|
|
1245
|
+
else {
|
|
1246
|
+
this.isLogin = false;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
class PublishManger {
|
|
1252
|
+
static async publish(projectPath, options) {
|
|
1253
|
+
if (TokenManger.isLoading)
|
|
1254
|
+
await TokenManger.init();
|
|
1255
|
+
if (!await fileExists(projectPath)) {
|
|
1256
|
+
throw new Error("Project path does not exist");
|
|
1257
|
+
}
|
|
1258
|
+
const { onProgress = (p) => { }, onMessage = (m) => { }, build, tag } = options;
|
|
1259
|
+
onProgress(1);
|
|
1260
|
+
onMessage("Publishing...");
|
|
1261
|
+
if (build == "enable") {
|
|
1262
|
+
onMessage("Building project...");
|
|
1263
|
+
await this.buildProject(projectPath);
|
|
1264
|
+
onProgress(30);
|
|
1265
|
+
}
|
|
1266
|
+
const mblerConfig = await ReadProjectMblerConfig(projectPath);
|
|
1267
|
+
const pkgData = await readFileAsJson(path.join(projectPath, "package.json"));
|
|
1268
|
+
const outputPath = path.join(config.tmpdir, "mbler/0b09/release.zip");
|
|
1269
|
+
const option = {
|
|
1270
|
+
outdirs: {
|
|
1271
|
+
behavior: mblerConfig.outdir?.behavior || path.join(projectPath, "dist", "behavior"),
|
|
1272
|
+
resources: mblerConfig.outdir?.resources || path.join(projectPath, "dist", "resources"),
|
|
1273
|
+
dist: outputPath
|
|
1274
|
+
},
|
|
1275
|
+
module: "all"
|
|
1276
|
+
};
|
|
1277
|
+
if (!option.outdirs.behavior || !option.outdirs.resources) {
|
|
1278
|
+
throw new Error("Build output directories not found");
|
|
1279
|
+
}
|
|
1280
|
+
if (!await fileExists(option.outdirs.behavior) || !await fileExists(option.outdirs.resources)) {
|
|
1281
|
+
throw new Error("Build output directories do not exist");
|
|
1282
|
+
}
|
|
1283
|
+
if (await fileExists(option.outdirs.behavior)) {
|
|
1284
|
+
option.module = "behavior";
|
|
1285
|
+
}
|
|
1286
|
+
if (await fileExists(option.outdirs.resources)) {
|
|
1287
|
+
if (option.module == "behavior") {
|
|
1288
|
+
option.module = "all";
|
|
1289
|
+
}
|
|
1290
|
+
else {
|
|
1291
|
+
option.module = "resources";
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
let readmePath = path.join(projectPath, "README.md");
|
|
1295
|
+
if (await fileExists(path.join(projectPath, "README.md"))) {
|
|
1296
|
+
readmePath = path.join(projectPath, "README.md");
|
|
1297
|
+
}
|
|
1298
|
+
if (await fileExists(path.join(projectPath, "readme.md"))) {
|
|
1299
|
+
readmePath = path.join(projectPath, "readme.md");
|
|
1300
|
+
}
|
|
1301
|
+
if (await fileExists(path.join(projectPath, "Readme.md"))) {
|
|
1302
|
+
readmePath = path.join(projectPath, "Readme.md");
|
|
1303
|
+
}
|
|
1304
|
+
if (await fileExists(path.join(projectPath, "README.MD"))) {
|
|
1305
|
+
readmePath = path.join(projectPath, "README.MD");
|
|
1306
|
+
}
|
|
1307
|
+
if (await fileExists(path.join(projectPath, "readme.MD"))) {
|
|
1308
|
+
readmePath = path.join(projectPath, "readme.MD");
|
|
1309
|
+
}
|
|
1310
|
+
if (await fileExists(path.join(projectPath, "Readme.MD"))) {
|
|
1311
|
+
readmePath = path.join(projectPath, "Readme.MD");
|
|
1312
|
+
}
|
|
1313
|
+
if (await fileExists(path.join(projectPath, "README.markdown"))) {
|
|
1314
|
+
readmePath = path.join(projectPath, "README.markdown");
|
|
1315
|
+
}
|
|
1316
|
+
if (await fileExists(path.join(projectPath, "readme.markdown"))) {
|
|
1317
|
+
readmePath = path.join(projectPath, "readme.markdown");
|
|
1318
|
+
}
|
|
1319
|
+
if (await fileExists(path.join(projectPath, "Readme.markdown"))) {
|
|
1320
|
+
readmePath = path.join(projectPath, "Readme.markdown");
|
|
1321
|
+
}
|
|
1322
|
+
if (await fileExists(path.join(projectPath, "README"))) {
|
|
1323
|
+
readmePath = path.join(projectPath, "README");
|
|
1324
|
+
}
|
|
1325
|
+
if (!await fileExists(readmePath)) {
|
|
1326
|
+
throw new Error("README file not found");
|
|
1327
|
+
}
|
|
1328
|
+
const metadata = {
|
|
1329
|
+
readme: await fs$1.readFile(readmePath, "utf-8"),
|
|
1330
|
+
scope: (mblerConfig.name.split("/").length > 1 ? mblerConfig.name.split("/")[0] : ""),
|
|
1331
|
+
name: mblerConfig.name.split("/").length > 1 ? mblerConfig.name.split("/")[1] : pkgData.name,
|
|
1332
|
+
version: mblerConfig.version,
|
|
1333
|
+
version_tag: tag || "latest"
|
|
1334
|
+
};
|
|
1335
|
+
if (!metadata.name || !metadata.version || !metadata.readme || !metadata.scope) {
|
|
1336
|
+
throw new Error("Invalid metadata");
|
|
1337
|
+
}
|
|
1338
|
+
if (!/^@\w+\/\w+$/.test(mblerConfig.name)) {
|
|
1339
|
+
console.log(mblerConfig.name);
|
|
1340
|
+
throw new Error("Package name must be in the format of @scope/name");
|
|
1341
|
+
}
|
|
1342
|
+
await generateRelease(option);
|
|
1343
|
+
onProgress(70);
|
|
1344
|
+
onMessage("Publishing to marketplace...");
|
|
1345
|
+
const session = await PublishManger.createSession(metadata);
|
|
1346
|
+
await PublishManger.publishToMarketplace(outputPath, session);
|
|
1347
|
+
onProgress(100);
|
|
1348
|
+
onMessage("Publish successful");
|
|
1349
|
+
onMessage(`+ ${pkgData.name}@${metadata.version} (${metadata.version_tag})`);
|
|
1350
|
+
}
|
|
1351
|
+
static async unpublish(scope, name, version) {
|
|
1352
|
+
if (TokenManger.isLoading)
|
|
1353
|
+
await TokenManger.waitVeirfy();
|
|
1354
|
+
if (!TokenManger.isLogin)
|
|
1355
|
+
throw new Error("Not logged in");
|
|
1356
|
+
const token = await TokenManger.getToken();
|
|
1357
|
+
if (!token)
|
|
1358
|
+
throw new Error("Failed to get token");
|
|
1359
|
+
const response = await fetch(`${config.defaultPmnxBASE}/unpublish/${scope}/${name}/${version}`, {
|
|
1360
|
+
method: "POST",
|
|
1361
|
+
headers: {
|
|
1362
|
+
"Authorization": `Bearer ${token}`
|
|
1363
|
+
}
|
|
1364
|
+
});
|
|
1365
|
+
if (!response.ok) {
|
|
1366
|
+
throw new Error("Failed to unpublish package");
|
|
1367
|
+
}
|
|
1368
|
+
const result = await response.json();
|
|
1369
|
+
if (result.code !== 200) {
|
|
1370
|
+
throw new Error(`Failed to unpublish package: ${result.data}`);
|
|
1371
|
+
}
|
|
1372
|
+
return true;
|
|
1373
|
+
}
|
|
1374
|
+
static async createSession(metadata) {
|
|
1375
|
+
if (TokenManger.isLoading)
|
|
1376
|
+
await TokenManger.waitVeirfy();
|
|
1377
|
+
if (!TokenManger.isLogin)
|
|
1378
|
+
throw new Error("Not logged in");
|
|
1379
|
+
const token = await TokenManger.getToken();
|
|
1380
|
+
if (!token)
|
|
1381
|
+
throw new Error("Failed to get token");
|
|
1382
|
+
const response = await fetch(`${config.defaultPmnxBASE}/publish/session/${metadata.scope}/${metadata.name}/create`, {
|
|
1383
|
+
method: "POST",
|
|
1384
|
+
headers: {
|
|
1385
|
+
"Content-Type": "application/json",
|
|
1386
|
+
"Authorization": `Bearer ${token}`
|
|
1387
|
+
},
|
|
1388
|
+
body: JSON.stringify(metadata)
|
|
1389
|
+
});
|
|
1390
|
+
const session = await response.json();
|
|
1391
|
+
console.log(session);
|
|
1392
|
+
if (!response.ok) {
|
|
1393
|
+
throw new Error("Failed to create publish session");
|
|
1394
|
+
}
|
|
1395
|
+
if (typeof session.data !== "object" || typeof session.data.sessionKey !== "string") {
|
|
1396
|
+
throw new Error(`${response.status} ${response.statusText}: ${session.data}`);
|
|
1397
|
+
}
|
|
1398
|
+
return session.data.sessionKey;
|
|
1399
|
+
}
|
|
1400
|
+
static async publishToMarketplace(zipPath, session) {
|
|
1401
|
+
const formData = new FormData();
|
|
1402
|
+
const fileBit = await fs$1.readFile(zipPath);
|
|
1403
|
+
let fileName = path.basename(zipPath);
|
|
1404
|
+
if (fileName.endsWith(".mcaddon")) {
|
|
1405
|
+
fileName = fileName.slice(0, -".mcaddon".length) + ".zip";
|
|
1406
|
+
}
|
|
1407
|
+
formData.append("file", new File([fileBit], fileName, { type: "application/zip" }));
|
|
1408
|
+
const token = await TokenManger.getToken();
|
|
1409
|
+
if (!token)
|
|
1410
|
+
throw new Error("Failed to get token");
|
|
1411
|
+
const response = await fetch(`${config.defaultPmnxBASE}/publish/session/${session}/upload`, {
|
|
1412
|
+
method: "POST",
|
|
1413
|
+
headers: {
|
|
1414
|
+
"Authorization": `Bearer ${token}`
|
|
1415
|
+
},
|
|
1416
|
+
body: formData
|
|
1417
|
+
});
|
|
1418
|
+
if (!response.ok) {
|
|
1419
|
+
throw new Error("Failed to upload zip file");
|
|
1420
|
+
}
|
|
1421
|
+
const result = await response.json();
|
|
1422
|
+
if (result.code !== 200) {
|
|
1423
|
+
throw new Error(`Failed to upload zip file: ${result.data}`);
|
|
1424
|
+
}
|
|
1425
|
+
return true;
|
|
1426
|
+
}
|
|
1427
|
+
static async buildProject(projectPath) {
|
|
1428
|
+
const pkgData = await readFileAsJson(path.join(projectPath, "package.json"));
|
|
1429
|
+
if (!pkgData) {
|
|
1430
|
+
throw new Error("package.json not found");
|
|
1431
|
+
}
|
|
1432
|
+
if (!pkgData.scripts || !pkgData.scripts.build) {
|
|
1433
|
+
throw new Error("No build script found in package.json");
|
|
1434
|
+
}
|
|
1435
|
+
const pkgManager = pkgData.packageManager || "npm";
|
|
1436
|
+
await new Promise((resolve, reject) => {
|
|
1437
|
+
const child = node_child_process.spawn(pkgManager, ["run", "build"], { cwd: projectPath });
|
|
1438
|
+
child.on("close", (code) => {
|
|
1439
|
+
if (code === 0) {
|
|
1440
|
+
resolve(void 0);
|
|
1441
|
+
}
|
|
1442
|
+
else {
|
|
1443
|
+
reject(new Error(`Build failed with code ${code}`));
|
|
1444
|
+
}
|
|
1445
|
+
});
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
function parsePackage$2(pkg) {
|
|
1451
|
+
const result = /^(@[^/@\s]+)\/([^@\s]+)@(.+)$/.exec(pkg);
|
|
1452
|
+
if (!result)
|
|
1453
|
+
return null;
|
|
1454
|
+
return {
|
|
1455
|
+
scope: result[1],
|
|
1456
|
+
name: result[2],
|
|
1457
|
+
version: result[3],
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
1460
|
+
async function unpublishCommand(cliParam, work) {
|
|
1461
|
+
const pkg = cliParam.params[1];
|
|
1462
|
+
if (!pkg) {
|
|
1463
|
+
showText(i18n.help.unpublish);
|
|
1464
|
+
return -1;
|
|
1465
|
+
}
|
|
1466
|
+
const parsed = parsePackage$2(pkg);
|
|
1467
|
+
if (!parsed) {
|
|
1468
|
+
showText(i18n.help.unpublish);
|
|
1469
|
+
return -1;
|
|
1470
|
+
}
|
|
1471
|
+
try {
|
|
1472
|
+
await PublishManger.unpublish(parsed.scope, parsed.name, parsed.version);
|
|
1473
|
+
showText(`Package ${parsed.scope}/${parsed.name}@${parsed.version} unpublished successfully`);
|
|
1474
|
+
return 0;
|
|
1475
|
+
}
|
|
1476
|
+
catch (error) {
|
|
1477
|
+
showText(`Unpublish failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1478
|
+
return -1;
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
async function publishCommand(cliParam, work) {
|
|
1483
|
+
const tag = cliParam.params.find(p => p.startsWith("-tag="))?.split("=")[1] || "latest";
|
|
1484
|
+
try {
|
|
1485
|
+
await PublishManger.publish(work, {
|
|
1486
|
+
build: "enable",
|
|
1487
|
+
tag,
|
|
1488
|
+
onProgress: (progress) => showText(`Progress: ${progress}%`),
|
|
1489
|
+
onMessage: (message) => showText(message)
|
|
1490
|
+
});
|
|
1491
|
+
return 0;
|
|
1492
|
+
}
|
|
1493
|
+
catch (error) {
|
|
1494
|
+
showText(`Publish failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1495
|
+
return -1;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
class GamePath {
|
|
1500
|
+
static async askPath(autoset = true) {
|
|
1501
|
+
const result = await input(i18n.publish.askTip);
|
|
1502
|
+
if (!result) {
|
|
1503
|
+
throw new Error("No path provided");
|
|
1504
|
+
}
|
|
1505
|
+
if (autoset) {
|
|
1506
|
+
ConfigManger.setKey("gamePath", result);
|
|
1507
|
+
}
|
|
1508
|
+
return result;
|
|
1509
|
+
}
|
|
1510
|
+
static async getPath() {
|
|
1511
|
+
const path = await ConfigManger.getKey("gamePath");
|
|
1512
|
+
return path || null;
|
|
1513
|
+
}
|
|
1514
|
+
static async clearPath() {
|
|
1515
|
+
await ConfigManger.setKey("gamePath", "");
|
|
1516
|
+
}
|
|
1517
|
+
static async getPathWithASK() {
|
|
1518
|
+
let path = await this.getPath();
|
|
1519
|
+
if (!path) {
|
|
1520
|
+
path = await this.askPath();
|
|
1521
|
+
}
|
|
1522
|
+
return path;
|
|
1523
|
+
}
|
|
1022
1524
|
}
|
|
1023
1525
|
|
|
1024
|
-
function
|
|
1025
|
-
|
|
1026
|
-
|
|
1526
|
+
function parsePackage$1(pkg) {
|
|
1527
|
+
const result = /^(@[^/@\s]+)\/([^@\s]+)@(.+)$/.exec(pkg);
|
|
1528
|
+
if (!result)
|
|
1529
|
+
return null;
|
|
1530
|
+
return {
|
|
1531
|
+
scope: result[1],
|
|
1532
|
+
name: result[2],
|
|
1533
|
+
version: result[3],
|
|
1534
|
+
};
|
|
1535
|
+
}
|
|
1536
|
+
async function uninstallCommand(cliParam, work) {
|
|
1537
|
+
const pkg = cliParam.params[1];
|
|
1538
|
+
if (!pkg) {
|
|
1539
|
+
showText(i18n.help.uninstall);
|
|
1540
|
+
return -1;
|
|
1541
|
+
}
|
|
1542
|
+
const parsed = parsePackage$1(pkg);
|
|
1543
|
+
if (!parsed) {
|
|
1544
|
+
showText(i18n.help.uninstall);
|
|
1545
|
+
return -1;
|
|
1546
|
+
}
|
|
1547
|
+
const gamePoint = await GamePath.getPathWithASK();
|
|
1548
|
+
if (!gamePoint) {
|
|
1549
|
+
showText(i18n.help.uninstall);
|
|
1550
|
+
return -1;
|
|
1551
|
+
}
|
|
1552
|
+
try {
|
|
1553
|
+
const id = `${parsed.scope.slice(1)}-${parsed.name}-${parsed.version}`;
|
|
1554
|
+
const behaviorDir = path.join(gamePoint, "behavior_packs", id);
|
|
1555
|
+
const resourceDir = path.join(gamePoint, "resource_packs", id);
|
|
1556
|
+
await fs$1.rm(behaviorDir, { recursive: true, force: true });
|
|
1557
|
+
await fs$1.rm(resourceDir, { recursive: true, force: true });
|
|
1558
|
+
// Remove from installed packages
|
|
1559
|
+
const installed = (await ConfigManger.getKey("installedPackages")) || [];
|
|
1560
|
+
const filtered = installed.filter((pkg) => pkg.id !== id);
|
|
1561
|
+
await ConfigManger.setKey("installedPackages", filtered);
|
|
1562
|
+
showText(`Package ${parsed.scope}/${parsed.name}@${parsed.version} uninstalled successfully`);
|
|
1563
|
+
return 0;
|
|
1564
|
+
}
|
|
1565
|
+
catch (error) {
|
|
1566
|
+
showText(`Uninstall failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1567
|
+
return -1;
|
|
1568
|
+
}
|
|
1027
1569
|
}
|
|
1028
1570
|
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1571
|
+
class InstallManger {
|
|
1572
|
+
static async download(scope, name, version, outputPath) {
|
|
1573
|
+
const response = await fetch(`${config.defaultPmnxBASE}/package/${scope}/${name}/v/${version}/download`);
|
|
1574
|
+
if (!response.ok) {
|
|
1575
|
+
throw new Error(`Failed to download package: ${response.status} ${response.statusText}`);
|
|
1576
|
+
}
|
|
1577
|
+
const buffer = await response.arrayBuffer();
|
|
1578
|
+
await fs$1.writeFile(outputPath, Buffer.from(buffer));
|
|
1579
|
+
}
|
|
1580
|
+
static async info(scope, name) {
|
|
1581
|
+
const response = await fetch(`${config.defaultPmnxBASE}/package/${scope}/${name}/info`);
|
|
1582
|
+
if (!response.ok) {
|
|
1583
|
+
throw new Error(`Failed to get package info: ${response.status} ${response.statusText}`);
|
|
1584
|
+
}
|
|
1585
|
+
const data = await response.json();
|
|
1586
|
+
if (data.code !== 200) {
|
|
1587
|
+
throw new Error(`Failed to get package info: ${data.data}`);
|
|
1588
|
+
}
|
|
1589
|
+
return data.data;
|
|
1590
|
+
}
|
|
1591
|
+
static async versionInfo(scope, name, version) {
|
|
1592
|
+
const response = await fetch(`${config.defaultPmnxBASE}/package/${scope}/${name}/v/${version}/info`);
|
|
1593
|
+
if (!response.ok) {
|
|
1594
|
+
throw new Error(`Failed to get package version info: ${response.status} ${response.statusText}`);
|
|
1595
|
+
}
|
|
1596
|
+
const data = await response.json();
|
|
1597
|
+
if (data.code !== 200) {
|
|
1598
|
+
throw new Error(`Failed to get package version info: ${data.data}`);
|
|
1599
|
+
}
|
|
1600
|
+
return data.data;
|
|
1601
|
+
}
|
|
1032
1602
|
}
|
|
1033
1603
|
|
|
1034
|
-
function
|
|
1035
|
-
|
|
1036
|
-
|
|
1604
|
+
function parsePackage(pkg) {
|
|
1605
|
+
const result = /^(@[^/@\s]+)\/([^@\s]+)(?:@(.+))?$/.exec(pkg);
|
|
1606
|
+
if (!result)
|
|
1607
|
+
return null;
|
|
1608
|
+
const info = {
|
|
1609
|
+
scope: result[1],
|
|
1610
|
+
name: result[2],
|
|
1611
|
+
};
|
|
1612
|
+
if (result[3]) {
|
|
1613
|
+
info.version = result[3];
|
|
1614
|
+
}
|
|
1615
|
+
return info;
|
|
1616
|
+
}
|
|
1617
|
+
function pickLatestVersion(versions) {
|
|
1618
|
+
const validVersions = versions.filter(isVaildVersion);
|
|
1619
|
+
if (validVersions.length > 0) {
|
|
1620
|
+
return validVersions.sort(compareVersion).reverse()[0] || "";
|
|
1621
|
+
}
|
|
1622
|
+
return versions[0] || "";
|
|
1623
|
+
}
|
|
1624
|
+
async function installCommand(cliParam, work) {
|
|
1625
|
+
const pkg = cliParam.params[1];
|
|
1626
|
+
if (!pkg) {
|
|
1627
|
+
showText(i18n.help.install);
|
|
1628
|
+
return -1;
|
|
1629
|
+
}
|
|
1630
|
+
const parsed = parsePackage(pkg);
|
|
1631
|
+
if (!parsed) {
|
|
1632
|
+
showText(i18n.help.install);
|
|
1633
|
+
return -1;
|
|
1634
|
+
}
|
|
1635
|
+
const { scope, name } = parsed;
|
|
1636
|
+
let version = parsed.version || "";
|
|
1637
|
+
const gamePoint = await GamePath.getPathWithASK();
|
|
1638
|
+
if (!gamePoint) {
|
|
1639
|
+
showText(i18n.help.install);
|
|
1640
|
+
return -1;
|
|
1641
|
+
}
|
|
1642
|
+
const packageJsonPath = path.join(work, "package.json");
|
|
1643
|
+
if (!await fs$1.stat(packageJsonPath).catch(() => null)) {
|
|
1644
|
+
showText("Install failed: work directory must contain package.json");
|
|
1645
|
+
return -1;
|
|
1646
|
+
}
|
|
1647
|
+
const packageJson = JSON.parse(await fs$1.readFile(packageJsonPath, "utf-8"));
|
|
1648
|
+
if (!packageJson.scripts?.build) {
|
|
1649
|
+
showText("Install failed: package.json must contain a build script");
|
|
1650
|
+
return -1;
|
|
1651
|
+
}
|
|
1652
|
+
showText(`Installing package ${pkg}...`);
|
|
1653
|
+
try {
|
|
1654
|
+
if (!version) {
|
|
1655
|
+
const pkgInfo = await InstallManger.info(scope, name);
|
|
1656
|
+
if (!pkgInfo.versions || pkgInfo.versions.length === 0) {
|
|
1657
|
+
showText(`Package ${scope}/${name} not found`);
|
|
1658
|
+
return -1;
|
|
1659
|
+
}
|
|
1660
|
+
const versionCandidates = pkgInfo.versions
|
|
1661
|
+
.map((item) => item.name)
|
|
1662
|
+
.filter((name) => typeof name === "string" && name.length > 0);
|
|
1663
|
+
version = pickLatestVersion(versionCandidates);
|
|
1664
|
+
if (!version) {
|
|
1665
|
+
showText(`Package ${scope}/${name} has no available version`);
|
|
1666
|
+
return -1;
|
|
1667
|
+
}
|
|
1668
|
+
showText(`Using latest version ${version}`);
|
|
1669
|
+
}
|
|
1670
|
+
const tmpDir = path.join(config.tmpdir, "tmp_mbler_install", `${Date.now()}`);
|
|
1671
|
+
await fs$1.mkdir(tmpDir, { recursive: true });
|
|
1672
|
+
await InstallManger.download(scope, name, version, path.join(tmpDir, "package.zip"));
|
|
1673
|
+
const zip = new AdmZip(path.join(tmpDir, "package.zip"));
|
|
1674
|
+
zip.extractAllTo(tmpDir, true);
|
|
1675
|
+
async function findAddonRoots(dir) {
|
|
1676
|
+
const results = [];
|
|
1677
|
+
async function walk(p) {
|
|
1678
|
+
const entries = await fs$1.readdir(p, { withFileTypes: true });
|
|
1679
|
+
for (const entry of entries) {
|
|
1680
|
+
const entryPath = path.join(p, entry.name);
|
|
1681
|
+
if (entry.isFile() && entry.name === "manifest.json") {
|
|
1682
|
+
try {
|
|
1683
|
+
const manifest = JSON.parse(await fs$1.readFile(entryPath, "utf-8"));
|
|
1684
|
+
const type = manifest?.modules?.[0]?.type;
|
|
1685
|
+
if (type === "data") {
|
|
1686
|
+
results.push({ root: path.dirname(entryPath), type: "behavior" });
|
|
1687
|
+
}
|
|
1688
|
+
else if (type === "resources") {
|
|
1689
|
+
results.push({ root: path.dirname(entryPath), type: "resource" });
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
catch {
|
|
1693
|
+
// ignore invalid manifest
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
if (entry.isDirectory()) {
|
|
1697
|
+
await walk(entryPath);
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
await walk(dir);
|
|
1702
|
+
return results;
|
|
1703
|
+
}
|
|
1704
|
+
const addons = await findAddonRoots(tmpDir);
|
|
1705
|
+
if (addons.length === 0) {
|
|
1706
|
+
throw new Error("No valid addon found in package");
|
|
1707
|
+
}
|
|
1708
|
+
const id = `${scope.slice(1)}-${name}-${version}`;
|
|
1709
|
+
const installed = (await ConfigManger.getKey("installedPackages")) || [];
|
|
1710
|
+
for (const addon of addons) {
|
|
1711
|
+
const packDir = addon.type === "behavior" ? "behavior_packs" : "resource_packs";
|
|
1712
|
+
const dest = path.join(gamePoint, packDir, id);
|
|
1713
|
+
await fs$1.mkdir(dest, { recursive: true });
|
|
1714
|
+
await fs$1.cp(addon.root, dest, { recursive: true });
|
|
1715
|
+
installed.push({ id, scope, name, version, type: addon.type });
|
|
1716
|
+
}
|
|
1717
|
+
await ConfigManger.setKey("installedPackages", installed);
|
|
1718
|
+
await fs$1.rm(tmpDir, { recursive: true, force: true });
|
|
1719
|
+
await new Promise((resolve, reject) => {
|
|
1720
|
+
const child = node_child_process.spawn("pnpm", ["build"], { cwd: work });
|
|
1721
|
+
child.on("close", (code) => {
|
|
1722
|
+
if (code === 0)
|
|
1723
|
+
resolve();
|
|
1724
|
+
else
|
|
1725
|
+
reject(new Error(`Build failed with code ${code}`));
|
|
1726
|
+
});
|
|
1727
|
+
});
|
|
1728
|
+
showText(`Package ${scope}/${name}@${version} installed successfully as ${id}`);
|
|
1729
|
+
return 0;
|
|
1730
|
+
}
|
|
1731
|
+
catch (error) {
|
|
1732
|
+
showText(`Install failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1733
|
+
return -1;
|
|
1734
|
+
}
|
|
1037
1735
|
}
|
|
1038
1736
|
|
|
1039
|
-
function loginCommand(cliParam, work) {
|
|
1040
|
-
|
|
1041
|
-
|
|
1737
|
+
async function loginCommand(cliParam, work) {
|
|
1738
|
+
let token = cliParam.params[1];
|
|
1739
|
+
if (!token) {
|
|
1740
|
+
showText(i18n.help.login);
|
|
1741
|
+
token = await input("Token: ", true);
|
|
1742
|
+
}
|
|
1743
|
+
if (!token) {
|
|
1744
|
+
showText("Token is required");
|
|
1745
|
+
return -1;
|
|
1746
|
+
}
|
|
1747
|
+
try {
|
|
1748
|
+
TokenManger.setToken(token.trim());
|
|
1749
|
+
await TokenManger.waitVeirfy();
|
|
1750
|
+
if (!TokenManger.isLogin) {
|
|
1751
|
+
showText("Login failed: invalid token");
|
|
1752
|
+
return -1;
|
|
1753
|
+
}
|
|
1754
|
+
showText(`Login successful: ${TokenManger.user?.name || "unknown user"}`);
|
|
1755
|
+
return 0;
|
|
1756
|
+
}
|
|
1757
|
+
catch (error) {
|
|
1758
|
+
showText(`Login failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1759
|
+
return -1;
|
|
1760
|
+
}
|
|
1042
1761
|
}
|
|
1043
1762
|
|
|
1044
1763
|
// `showText` moved to `utils` to avoid circular dependency with `build`.
|