@pmate/cli 0.22.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +145 -12
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -17233,6 +17233,7 @@ init_sessionStore();
|
|
|
17233
17233
|
|
|
17234
17234
|
// src/appIcon.ts
|
|
17235
17235
|
var import_node_fs15 = require("fs");
|
|
17236
|
+
var import_node_os7 = __toESM(require("os"));
|
|
17236
17237
|
var import_node_path20 = __toESM(require("path"));
|
|
17237
17238
|
var import_sharp = __toESM(require("sharp"));
|
|
17238
17239
|
init_namespacedRef();
|
|
@@ -17254,6 +17255,9 @@ var IOS_ICON_SPECS = [
|
|
|
17254
17255
|
var pngToIco = require("png-to-ico").default;
|
|
17255
17256
|
var APP_ICON_VARIANT_KEY_PATTERN = /^[a-z0-9]+$/;
|
|
17256
17257
|
var DATA_URL_PATTERN = /^data:([^;,]+);base64,([a-z0-9+/=]+)$/i;
|
|
17258
|
+
var DEFAULT_APP_ICON_VARIANT_BUCKET = "parrot-static";
|
|
17259
|
+
var DEFAULT_APP_ICON_VARIANT_PUBLIC_BASE_URL = "https://parrot-static.pmate.chat";
|
|
17260
|
+
var DEFAULT_APP_ICON_VARIANT_NAME = "webp512x512";
|
|
17257
17261
|
function normalizeAppIcon(icon, cwd) {
|
|
17258
17262
|
if (typeof icon !== "string" || !icon.trim()) {
|
|
17259
17263
|
throw new Error("App icon must be a non-empty http(s) URL.");
|
|
@@ -17270,7 +17274,7 @@ function normalizeAppIcon(icon, cwd) {
|
|
|
17270
17274
|
}
|
|
17271
17275
|
throw new Error("App icon must be an http(s) URL. Upload SVG files to OSS first and use the public link.");
|
|
17272
17276
|
}
|
|
17273
|
-
function prepareAppPayload(payload, cwd) {
|
|
17277
|
+
async function prepareAppPayload(payload, cwd, options) {
|
|
17274
17278
|
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
17275
17279
|
throw new Error("App payload must be a JSON object.");
|
|
17276
17280
|
}
|
|
@@ -17281,6 +17285,14 @@ function prepareAppPayload(payload, cwd) {
|
|
|
17281
17285
|
if (normalized.icons !== void 0) {
|
|
17282
17286
|
normalized.icons = normalizeAppIconVariants(normalized.icons);
|
|
17283
17287
|
}
|
|
17288
|
+
if (typeof normalized.icon === "string" && shouldAutoGenerateDefaultVariant(normalized.icon)) {
|
|
17289
|
+
normalized.icons = await withDefaultSvgIconVariant(
|
|
17290
|
+
normalized.icon,
|
|
17291
|
+
normalized.icons,
|
|
17292
|
+
cwd,
|
|
17293
|
+
options?.appRef || inferAppRefFromPayload(normalized)
|
|
17294
|
+
);
|
|
17295
|
+
}
|
|
17284
17296
|
return normalized;
|
|
17285
17297
|
}
|
|
17286
17298
|
function normalizeAppIconVariants(input) {
|
|
@@ -17373,17 +17385,35 @@ async function renderAppIconVariant(input) {
|
|
|
17373
17385
|
(0, import_node_fs15.mkdirSync)(import_node_path20.default.dirname(outputPath), { recursive: true });
|
|
17374
17386
|
(0, import_node_fs15.writeFileSync)(outputPath, bytes);
|
|
17375
17387
|
}
|
|
17388
|
+
if (input.name) {
|
|
17389
|
+
return {
|
|
17390
|
+
value: await uploadAppIconVariantAsset({
|
|
17391
|
+
appRef: input.appRef,
|
|
17392
|
+
icon: input.icon,
|
|
17393
|
+
name: input.name,
|
|
17394
|
+
format: input.format,
|
|
17395
|
+
bytes,
|
|
17396
|
+
sourcePath: outputPath
|
|
17397
|
+
}),
|
|
17398
|
+
...outputPath ? { outputPath } : {}
|
|
17399
|
+
};
|
|
17400
|
+
}
|
|
17376
17401
|
return {
|
|
17377
17402
|
value: createImageDataUrl(bytes, input.format),
|
|
17378
17403
|
...outputPath ? { outputPath } : {}
|
|
17379
17404
|
};
|
|
17380
17405
|
}
|
|
17381
|
-
function loadAppIconVariantFile(
|
|
17382
|
-
const absolutePath = import_node_path20.default.resolve(cwd, filePath);
|
|
17406
|
+
async function loadAppIconVariantFile(input) {
|
|
17407
|
+
const absolutePath = import_node_path20.default.resolve(input.cwd, input.filePath);
|
|
17383
17408
|
const format = inferVariantFormatFromPath(absolutePath);
|
|
17384
|
-
const bytes = (0, import_node_fs15.readFileSync)(absolutePath);
|
|
17385
17409
|
return {
|
|
17386
|
-
value:
|
|
17410
|
+
value: input.name ? await uploadAppIconVariantAsset({
|
|
17411
|
+
appRef: input.appRef,
|
|
17412
|
+
icon: input.icon,
|
|
17413
|
+
name: input.name,
|
|
17414
|
+
format,
|
|
17415
|
+
sourcePath: absolutePath
|
|
17416
|
+
}) : createImageDataUrl((0, import_node_fs15.readFileSync)(absolutePath), format),
|
|
17387
17417
|
format,
|
|
17388
17418
|
absolutePath
|
|
17389
17419
|
};
|
|
@@ -17430,6 +17460,17 @@ async function renderAppIcon(svg, width, height, format) {
|
|
|
17430
17460
|
function looksLikeSvgMarkup(value) {
|
|
17431
17461
|
return /<svg[\s>]/i.test(value);
|
|
17432
17462
|
}
|
|
17463
|
+
function looksLikeSvgUrl(value) {
|
|
17464
|
+
try {
|
|
17465
|
+
const url = new URL(value);
|
|
17466
|
+
return url.pathname.toLowerCase().endsWith(".svg");
|
|
17467
|
+
} catch {
|
|
17468
|
+
return false;
|
|
17469
|
+
}
|
|
17470
|
+
}
|
|
17471
|
+
function shouldAutoGenerateDefaultVariant(icon) {
|
|
17472
|
+
return looksLikeSvgMarkup(icon) || looksLikeSvgUrl(icon);
|
|
17473
|
+
}
|
|
17433
17474
|
function inferVariantFormatFromPath(filePath) {
|
|
17434
17475
|
const ext = import_node_path20.default.extname(filePath).toLowerCase();
|
|
17435
17476
|
switch (ext) {
|
|
@@ -17468,6 +17509,90 @@ function isSupportedImageDataUrl(value) {
|
|
|
17468
17509
|
const mimeType = match[1]?.toLowerCase();
|
|
17469
17510
|
return mimeType === "image/png" || mimeType === "image/webp" || mimeType === "image/svg+xml" || mimeType === "image/x-icon";
|
|
17470
17511
|
}
|
|
17512
|
+
async function withDefaultSvgIconVariant(icon, icons, cwd, appRef) {
|
|
17513
|
+
const variant = await renderAppIconVariant({
|
|
17514
|
+
icon,
|
|
17515
|
+
name: DEFAULT_APP_ICON_VARIANT_NAME,
|
|
17516
|
+
appRef,
|
|
17517
|
+
width: 512,
|
|
17518
|
+
height: 512,
|
|
17519
|
+
format: "webp",
|
|
17520
|
+
cwd
|
|
17521
|
+
});
|
|
17522
|
+
return {
|
|
17523
|
+
...icons || {},
|
|
17524
|
+
[DEFAULT_APP_ICON_VARIANT_NAME]: variant.value
|
|
17525
|
+
};
|
|
17526
|
+
}
|
|
17527
|
+
function inferAppRefFromPayload(payload) {
|
|
17528
|
+
const id = payload.id;
|
|
17529
|
+
const namespace = payload.namespace;
|
|
17530
|
+
if (typeof id !== "string" || !id.trim()) {
|
|
17531
|
+
return void 0;
|
|
17532
|
+
}
|
|
17533
|
+
if (typeof namespace !== "string" || !namespace.trim()) {
|
|
17534
|
+
return void 0;
|
|
17535
|
+
}
|
|
17536
|
+
return `${namespace.trim().toLowerCase()}:${id.trim()}`;
|
|
17537
|
+
}
|
|
17538
|
+
async function uploadAppIconVariantAsset(input) {
|
|
17539
|
+
const objectKey = buildAppIconVariantObjectKey(input);
|
|
17540
|
+
const sourcePath = input.sourcePath || writeTempAppIconVariantFile(objectKey, input.format, input.bytes);
|
|
17541
|
+
const shouldCleanup = !input.sourcePath;
|
|
17542
|
+
try {
|
|
17543
|
+
await AliyunService.uploadFile(sourcePath, DEFAULT_APP_ICON_VARIANT_BUCKET, objectKey);
|
|
17544
|
+
} finally {
|
|
17545
|
+
if (shouldCleanup) {
|
|
17546
|
+
(0, import_node_fs15.rmSync)(import_node_path20.default.dirname(sourcePath), { recursive: true, force: true });
|
|
17547
|
+
}
|
|
17548
|
+
}
|
|
17549
|
+
return `${DEFAULT_APP_ICON_VARIANT_PUBLIC_BASE_URL}/${objectKey}`;
|
|
17550
|
+
}
|
|
17551
|
+
function buildAppIconVariantObjectKey(input) {
|
|
17552
|
+
return import_node_path20.default.posix.join(
|
|
17553
|
+
"apps",
|
|
17554
|
+
`${resolveAppIconAssetStem(input.icon, input.appRef)}-${input.name}.${extensionForIconFormat(input.format)}`
|
|
17555
|
+
);
|
|
17556
|
+
}
|
|
17557
|
+
function resolveAppIconAssetStem(icon, appRef) {
|
|
17558
|
+
if (icon) {
|
|
17559
|
+
try {
|
|
17560
|
+
const url = new URL(icon);
|
|
17561
|
+
const ext = import_node_path20.default.posix.extname(url.pathname);
|
|
17562
|
+
const basename = import_node_path20.default.posix.basename(url.pathname, ext);
|
|
17563
|
+
const normalized = sanitizeAppIconAssetStem(basename);
|
|
17564
|
+
if (normalized) {
|
|
17565
|
+
return normalized;
|
|
17566
|
+
}
|
|
17567
|
+
} catch {
|
|
17568
|
+
}
|
|
17569
|
+
}
|
|
17570
|
+
if (appRef) {
|
|
17571
|
+
const { objectId } = parseNamespacedRef(appRef, {
|
|
17572
|
+
entityLabel: "app ref"
|
|
17573
|
+
});
|
|
17574
|
+
const normalized = sanitizeAppIconAssetStem(objectId);
|
|
17575
|
+
if (normalized) {
|
|
17576
|
+
return normalized;
|
|
17577
|
+
}
|
|
17578
|
+
}
|
|
17579
|
+
throw new Error("Unable to determine an asset key for the app icon variant upload.");
|
|
17580
|
+
}
|
|
17581
|
+
function sanitizeAppIconAssetStem(value) {
|
|
17582
|
+
return value.trim().replace(/[^a-z0-9._-]+/gi, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").toLowerCase();
|
|
17583
|
+
}
|
|
17584
|
+
function extensionForIconFormat(format) {
|
|
17585
|
+
return format === "ico" ? "ico" : format;
|
|
17586
|
+
}
|
|
17587
|
+
function writeTempAppIconVariantFile(objectKey, format, bytes) {
|
|
17588
|
+
if (!bytes) {
|
|
17589
|
+
throw new Error("App icon variant upload requires bytes or a source file.");
|
|
17590
|
+
}
|
|
17591
|
+
const tempDir = (0, import_node_fs15.mkdtempSync)(import_node_path20.default.join(import_node_os7.default.tmpdir(), "pmate-app-icon-"));
|
|
17592
|
+
const tempPath = import_node_path20.default.join(tempDir, import_node_path20.default.basename(objectKey) || `variant.${extensionForIconFormat(format)}`);
|
|
17593
|
+
(0, import_node_fs15.writeFileSync)(tempPath, bytes);
|
|
17594
|
+
return tempPath;
|
|
17595
|
+
}
|
|
17471
17596
|
|
|
17472
17597
|
// src/commands/AppCommandHandler.ts
|
|
17473
17598
|
init_namespacedRef();
|
|
@@ -17509,7 +17634,7 @@ var AppCommandHandler = class {
|
|
|
17509
17634
|
return;
|
|
17510
17635
|
}
|
|
17511
17636
|
case "create": {
|
|
17512
|
-
const payload = this.preparePayload(args.filePath);
|
|
17637
|
+
const payload = await this.preparePayload(args.filePath);
|
|
17513
17638
|
await this.requireNamespace(payload, baseUrl);
|
|
17514
17639
|
const record = await this.request(
|
|
17515
17640
|
baseUrl,
|
|
@@ -17523,7 +17648,7 @@ var AppCommandHandler = class {
|
|
|
17523
17648
|
return;
|
|
17524
17649
|
}
|
|
17525
17650
|
case "update": {
|
|
17526
|
-
const payload = this.preparePayload(args.filePath);
|
|
17651
|
+
const payload = await this.preparePayload(args.filePath, args.id);
|
|
17527
17652
|
const pathname = this.buildAppPath(args.id);
|
|
17528
17653
|
const record = await this.request(
|
|
17529
17654
|
baseUrl,
|
|
@@ -17582,7 +17707,13 @@ var AppCommandHandler = class {
|
|
|
17582
17707
|
if (existingIcons[name] && !args.force) {
|
|
17583
17708
|
throw new Error(`App icon variant "${name}" already exists for ${args.id}. Re-run with --force to overwrite it.`);
|
|
17584
17709
|
}
|
|
17585
|
-
const source = args.filePath ?
|
|
17710
|
+
const source = args.filePath ? await loadAppIconVariantFile({
|
|
17711
|
+
filePath: args.filePath,
|
|
17712
|
+
cwd: process.cwd(),
|
|
17713
|
+
name,
|
|
17714
|
+
appRef: args.id,
|
|
17715
|
+
icon: record.icon
|
|
17716
|
+
}) : await this.renderIconVariantFromApp(record, args);
|
|
17586
17717
|
const updatedIcons = {
|
|
17587
17718
|
...existingIcons,
|
|
17588
17719
|
[name]: source.value
|
|
@@ -17679,9 +17810,9 @@ var AppCommandHandler = class {
|
|
|
17679
17810
|
throw new Error(`Invalid JSON in ${absolutePath}: ${message}`);
|
|
17680
17811
|
}
|
|
17681
17812
|
}
|
|
17682
|
-
preparePayload(filePath) {
|
|
17813
|
+
async preparePayload(filePath, appRef) {
|
|
17683
17814
|
const absolutePath = this.resolveFilePath(filePath);
|
|
17684
|
-
return prepareAppPayload(this.readJsonFile(filePath), import_node_path21.default.dirname(absolutePath));
|
|
17815
|
+
return prepareAppPayload(this.readJsonFile(filePath), import_node_path21.default.dirname(absolutePath), { appRef });
|
|
17685
17816
|
}
|
|
17686
17817
|
resolveFilePath(filePath) {
|
|
17687
17818
|
return import_node_path21.default.resolve(process.cwd(), filePath);
|
|
@@ -17779,6 +17910,8 @@ var AppCommandHandler = class {
|
|
|
17779
17910
|
}
|
|
17780
17911
|
return renderAppIconVariant({
|
|
17781
17912
|
icon: record.icon,
|
|
17913
|
+
name: args.name,
|
|
17914
|
+
appRef: args.id,
|
|
17782
17915
|
width: args.width,
|
|
17783
17916
|
height: args.height,
|
|
17784
17917
|
format: args.format || "png",
|
|
@@ -17806,7 +17939,7 @@ var import_promises6 = require("readline/promises");
|
|
|
17806
17939
|
|
|
17807
17940
|
// src/services/ServiceRuntimeRunner.ts
|
|
17808
17941
|
var import_node_fs17 = require("fs");
|
|
17809
|
-
var
|
|
17942
|
+
var import_node_os8 = __toESM(require("os"));
|
|
17810
17943
|
var import_node_path22 = __toESM(require("path"));
|
|
17811
17944
|
var import_node_child_process10 = require("child_process");
|
|
17812
17945
|
var import_node_readline = require("readline");
|
|
@@ -17978,7 +18111,7 @@ var ServiceRuntimeRunner = class {
|
|
|
17978
18111
|
if (repository) {
|
|
17979
18112
|
const repoName = repository.replace(/\/+$/, "").split("/").pop()?.replace(/\.git$/i, "");
|
|
17980
18113
|
if (repoName) {
|
|
17981
|
-
const candidate = import_node_path22.default.join(
|
|
18114
|
+
const candidate = import_node_path22.default.join(import_node_os8.default.homedir(), "dev", "pmate", repoName);
|
|
17982
18115
|
if ((0, import_node_fs17.existsSync)(candidate)) {
|
|
17983
18116
|
return candidate;
|
|
17984
18117
|
}
|