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