@pmate/cli 0.21.7 → 0.21.9
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.d.ts +12 -0
- package/dist/index.js +355 -44
- package/package.json +4 -2
package/dist/index.d.ts
CHANGED
|
@@ -620,6 +620,18 @@ type AppCommandArgs = {
|
|
|
620
620
|
toId?: string;
|
|
621
621
|
baseUrl?: string;
|
|
622
622
|
json: boolean;
|
|
623
|
+
} | {
|
|
624
|
+
type: Command.App;
|
|
625
|
+
action: "icon";
|
|
626
|
+
id: string;
|
|
627
|
+
preset?: "android" | "ios";
|
|
628
|
+
width?: number;
|
|
629
|
+
height?: number;
|
|
630
|
+
format?: "png" | "webp" | "svg" | "ico";
|
|
631
|
+
output?: string;
|
|
632
|
+
outDir?: string;
|
|
633
|
+
baseUrl?: string;
|
|
634
|
+
json: boolean;
|
|
623
635
|
};
|
|
624
636
|
type ServiceCommandArgs = {
|
|
625
637
|
type: Command.Service;
|
package/dist/index.js
CHANGED
|
@@ -9435,8 +9435,8 @@ __export(index_exports, {
|
|
|
9435
9435
|
parseCommandArgs: () => parseCommandArgs
|
|
9436
9436
|
});
|
|
9437
9437
|
module.exports = __toCommonJS(index_exports);
|
|
9438
|
-
var
|
|
9439
|
-
var
|
|
9438
|
+
var import_node_fs20 = require("fs");
|
|
9439
|
+
var import_node_path25 = __toESM(require("path"));
|
|
9440
9440
|
var import_chalk4 = __toESM(require("chalk"));
|
|
9441
9441
|
|
|
9442
9442
|
// src/cli.ts
|
|
@@ -14585,9 +14585,9 @@ ${indent}\`\`\``;
|
|
|
14585
14585
|
static richTextToPlain(richText) {
|
|
14586
14586
|
return richText.map((item) => item.plain_text ?? item.text?.content ?? "").join("").trim();
|
|
14587
14587
|
}
|
|
14588
|
-
static async requestJson(
|
|
14588
|
+
static async requestJson(path26, init) {
|
|
14589
14589
|
const token = this.getToken();
|
|
14590
|
-
const response = await fetch(`${this.baseUrl}${
|
|
14590
|
+
const response = await fetch(`${this.baseUrl}${path26}`, {
|
|
14591
14591
|
method: init.method,
|
|
14592
14592
|
headers: {
|
|
14593
14593
|
Authorization: `Bearer ${token}`,
|
|
@@ -16223,8 +16223,8 @@ function descriptorPathSegments(descriptor) {
|
|
|
16223
16223
|
return ["chains", normalized.chain, "namespaces", normalized.ns, normalized.kind, normalized.name];
|
|
16224
16224
|
}
|
|
16225
16225
|
function buildPathUrl(baseUrl, pathSegments, query) {
|
|
16226
|
-
const
|
|
16227
|
-
const url = new URL(
|
|
16226
|
+
const path26 = `/${pathSegments.map((segment) => encodeURIComponent(segment)).join("/")}`;
|
|
16227
|
+
const url = new URL(path26, baseUrl);
|
|
16228
16228
|
if (query) {
|
|
16229
16229
|
for (const [key, value] of Object.entries(query)) {
|
|
16230
16230
|
url.searchParams.set(key, String(value));
|
|
@@ -17173,9 +17173,155 @@ var StoreCommandHandler = class {
|
|
|
17173
17173
|
};
|
|
17174
17174
|
|
|
17175
17175
|
// src/commands/AppCommandHandler.ts
|
|
17176
|
+
var import_node_fs17 = require("fs");
|
|
17177
|
+
var import_node_path22 = __toESM(require("path"));
|
|
17178
|
+
init_sessionStore();
|
|
17179
|
+
|
|
17180
|
+
// src/appIcon.ts
|
|
17176
17181
|
var import_node_fs16 = require("fs");
|
|
17177
17182
|
var import_node_path21 = __toESM(require("path"));
|
|
17178
|
-
|
|
17183
|
+
var import_sharp = __toESM(require("sharp"));
|
|
17184
|
+
init_namespacedRef();
|
|
17185
|
+
var ANDROID_ICON_SPECS = [
|
|
17186
|
+
{ width: 48, height: 48, name: "mdpi" },
|
|
17187
|
+
{ width: 72, height: 72, name: "hdpi" },
|
|
17188
|
+
{ width: 96, height: 96, name: "xhdpi" },
|
|
17189
|
+
{ width: 144, height: 144, name: "xxhdpi" },
|
|
17190
|
+
{ width: 192, height: 192, name: "xxxhdpi" },
|
|
17191
|
+
{ width: 512, height: 512, name: "play-store" }
|
|
17192
|
+
];
|
|
17193
|
+
var IOS_ICON_SPECS = [
|
|
17194
|
+
{ width: 120, height: 120, name: "iphone-notification-2x" },
|
|
17195
|
+
{ width: 152, height: 152, name: "ipad-app-2x" },
|
|
17196
|
+
{ width: 167, height: 167, name: "ipad-pro-app-2x" },
|
|
17197
|
+
{ width: 180, height: 180, name: "iphone-app-3x" },
|
|
17198
|
+
{ width: 1024, height: 1024, name: "app-store" }
|
|
17199
|
+
];
|
|
17200
|
+
var pngToIco = require("png-to-ico").default;
|
|
17201
|
+
function normalizeAppIcon(icon, cwd) {
|
|
17202
|
+
if (typeof icon !== "string" || !icon.trim()) {
|
|
17203
|
+
throw new Error("App icon must be a non-empty SVG string or .svg file path.");
|
|
17204
|
+
}
|
|
17205
|
+
const trimmed = icon.trim();
|
|
17206
|
+
if (looksLikeSvgMarkup(trimmed)) {
|
|
17207
|
+
return trimmed;
|
|
17208
|
+
}
|
|
17209
|
+
if (trimmed.startsWith("data:")) {
|
|
17210
|
+
return normalizeSvgDataUrl(trimmed);
|
|
17211
|
+
}
|
|
17212
|
+
if (/^https?:\/\//i.test(trimmed)) {
|
|
17213
|
+
throw new Error("App icon must be SVG source, not a remote URL.");
|
|
17214
|
+
}
|
|
17215
|
+
const absolutePath = import_node_path21.default.resolve(cwd, trimmed);
|
|
17216
|
+
if (!(0, import_node_fs16.existsSync)(absolutePath)) {
|
|
17217
|
+
if (trimmed.toLowerCase().endsWith(".svg")) {
|
|
17218
|
+
throw new Error(`App icon SVG file not found: ${absolutePath}`);
|
|
17219
|
+
}
|
|
17220
|
+
throw new Error("App icon must be inline SVG, an SVG data URL, or a .svg file path.");
|
|
17221
|
+
}
|
|
17222
|
+
if (import_node_path21.default.extname(absolutePath).toLowerCase() !== ".svg") {
|
|
17223
|
+
throw new Error(`App icon file must use the .svg extension: ${absolutePath}`);
|
|
17224
|
+
}
|
|
17225
|
+
const svg = (0, import_node_fs16.readFileSync)(absolutePath, "utf8").trim();
|
|
17226
|
+
if (!looksLikeSvgMarkup(svg)) {
|
|
17227
|
+
throw new Error(`App icon file is not valid SVG: ${absolutePath}`);
|
|
17228
|
+
}
|
|
17229
|
+
return svg;
|
|
17230
|
+
}
|
|
17231
|
+
function prepareAppPayload(payload, cwd) {
|
|
17232
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
17233
|
+
throw new Error("App payload must be a JSON object.");
|
|
17234
|
+
}
|
|
17235
|
+
const normalized = { ...payload };
|
|
17236
|
+
if (normalized.icon !== void 0) {
|
|
17237
|
+
normalized.icon = normalizeAppIcon(normalized.icon, cwd);
|
|
17238
|
+
}
|
|
17239
|
+
return normalized;
|
|
17240
|
+
}
|
|
17241
|
+
function createAppIconExportPlans(input) {
|
|
17242
|
+
const { namespace, objectId } = parseNamespacedRef(input.id, {
|
|
17243
|
+
entityLabel: "app ref"
|
|
17244
|
+
});
|
|
17245
|
+
if (input.preset) {
|
|
17246
|
+
const specs = input.preset === "android" ? ANDROID_ICON_SPECS : IOS_ICON_SPECS;
|
|
17247
|
+
if (!input.outDir) {
|
|
17248
|
+
throw new Error("App icon preset export requires --out-dir.");
|
|
17249
|
+
}
|
|
17250
|
+
const outDir = import_node_path21.default.resolve(input.cwd, input.outDir);
|
|
17251
|
+
(0, import_node_fs16.mkdirSync)(outDir, { recursive: true });
|
|
17252
|
+
return specs.map((spec) => ({
|
|
17253
|
+
width: spec.width,
|
|
17254
|
+
height: spec.height,
|
|
17255
|
+
format: "png",
|
|
17256
|
+
outputPath: import_node_path21.default.join(
|
|
17257
|
+
outDir,
|
|
17258
|
+
`${namespace}-${objectId}-${spec.name}-${spec.width}x${spec.height}.png`
|
|
17259
|
+
)
|
|
17260
|
+
}));
|
|
17261
|
+
}
|
|
17262
|
+
if (!input.width || !input.height) {
|
|
17263
|
+
throw new Error("App icon export requires either a preset or an explicit size.");
|
|
17264
|
+
}
|
|
17265
|
+
const format = input.format || "png";
|
|
17266
|
+
if (!input.output) {
|
|
17267
|
+
throw new Error("Single-size app icon export requires --output.");
|
|
17268
|
+
}
|
|
17269
|
+
const resolvedOutput = import_node_path21.default.resolve(input.cwd, input.output);
|
|
17270
|
+
(0, import_node_fs16.mkdirSync)(import_node_path21.default.dirname(resolvedOutput), { recursive: true });
|
|
17271
|
+
return [
|
|
17272
|
+
{
|
|
17273
|
+
width: input.width,
|
|
17274
|
+
height: input.height,
|
|
17275
|
+
format,
|
|
17276
|
+
outputPath: resolvedOutput
|
|
17277
|
+
}
|
|
17278
|
+
];
|
|
17279
|
+
}
|
|
17280
|
+
async function exportAppIcon(svg, plans) {
|
|
17281
|
+
for (const plan of plans) {
|
|
17282
|
+
const bytes = await renderAppIcon(svg, plan.width, plan.height, plan.format);
|
|
17283
|
+
(0, import_node_fs16.writeFileSync)(plan.outputPath, bytes);
|
|
17284
|
+
}
|
|
17285
|
+
}
|
|
17286
|
+
async function renderAppIcon(svg, width, height, format) {
|
|
17287
|
+
if (format === "svg") {
|
|
17288
|
+
return Buffer.from(svg);
|
|
17289
|
+
}
|
|
17290
|
+
const pipeline = (0, import_sharp.default)(Buffer.from(svg)).resize(width, height, {
|
|
17291
|
+
fit: "contain",
|
|
17292
|
+
background: { r: 0, g: 0, b: 0, alpha: 0 }
|
|
17293
|
+
});
|
|
17294
|
+
if (format === "webp") {
|
|
17295
|
+
return pipeline.webp().toBuffer();
|
|
17296
|
+
}
|
|
17297
|
+
if (format === "ico") {
|
|
17298
|
+
const pngBuffer = await pipeline.png().toBuffer();
|
|
17299
|
+
return pngToIco([pngBuffer]);
|
|
17300
|
+
}
|
|
17301
|
+
return pipeline.png().toBuffer();
|
|
17302
|
+
}
|
|
17303
|
+
function looksLikeSvgMarkup(value) {
|
|
17304
|
+
return /<svg[\s>]/i.test(value);
|
|
17305
|
+
}
|
|
17306
|
+
function normalizeSvgDataUrl(value) {
|
|
17307
|
+
const match = /^data:([^;,]+)?(?:;charset=[^;,]+)?(?:;(base64))?,(.*)$/i.exec(value);
|
|
17308
|
+
if (!match) {
|
|
17309
|
+
throw new Error("Invalid app icon data URL.");
|
|
17310
|
+
}
|
|
17311
|
+
const mimeType = (match[1] || "").toLowerCase();
|
|
17312
|
+
if (mimeType !== "image/svg+xml") {
|
|
17313
|
+
throw new Error(`App icon data URL must be image/svg+xml. Received: ${mimeType || "unknown"}`);
|
|
17314
|
+
}
|
|
17315
|
+
const payload = match[3] || "";
|
|
17316
|
+
const decoded = match[2] ? Buffer.from(payload, "base64").toString("utf8") : decodeURIComponent(payload);
|
|
17317
|
+
const svg = decoded.trim();
|
|
17318
|
+
if (!looksLikeSvgMarkup(svg)) {
|
|
17319
|
+
throw new Error("App icon data URL does not contain valid SVG markup.");
|
|
17320
|
+
}
|
|
17321
|
+
return svg;
|
|
17322
|
+
}
|
|
17323
|
+
|
|
17324
|
+
// src/commands/AppCommandHandler.ts
|
|
17179
17325
|
init_namespacedRef();
|
|
17180
17326
|
var AppCommandHandler = class {
|
|
17181
17327
|
async handle(args) {
|
|
@@ -17206,7 +17352,7 @@ var AppCommandHandler = class {
|
|
|
17206
17352
|
return;
|
|
17207
17353
|
}
|
|
17208
17354
|
case "create": {
|
|
17209
|
-
const payload = this.
|
|
17355
|
+
const payload = this.preparePayload(args.filePath);
|
|
17210
17356
|
await this.requireNamespace(payload, baseUrl);
|
|
17211
17357
|
const record = await this.request(
|
|
17212
17358
|
baseUrl,
|
|
@@ -17220,7 +17366,7 @@ var AppCommandHandler = class {
|
|
|
17220
17366
|
return;
|
|
17221
17367
|
}
|
|
17222
17368
|
case "update": {
|
|
17223
|
-
const payload = this.
|
|
17369
|
+
const payload = this.preparePayload(args.filePath);
|
|
17224
17370
|
const pathname = this.buildAppPath(args.id);
|
|
17225
17371
|
const record = await this.request(
|
|
17226
17372
|
baseUrl,
|
|
@@ -17233,6 +17379,39 @@ var AppCommandHandler = class {
|
|
|
17233
17379
|
this.renderJson(record, args.json);
|
|
17234
17380
|
return;
|
|
17235
17381
|
}
|
|
17382
|
+
case "icon": {
|
|
17383
|
+
const record = await this.request(
|
|
17384
|
+
baseUrl,
|
|
17385
|
+
token,
|
|
17386
|
+
"GET",
|
|
17387
|
+
this.buildAppPath(args.id)
|
|
17388
|
+
);
|
|
17389
|
+
if (!record.icon) {
|
|
17390
|
+
throw new Error(`App icon is missing for ${args.id}`);
|
|
17391
|
+
}
|
|
17392
|
+
const plans = createAppIconExportPlans({
|
|
17393
|
+
id: args.id,
|
|
17394
|
+
preset: args.preset,
|
|
17395
|
+
width: args.width,
|
|
17396
|
+
height: args.height,
|
|
17397
|
+
format: args.format,
|
|
17398
|
+
output: args.output,
|
|
17399
|
+
outDir: args.outDir,
|
|
17400
|
+
cwd: process.cwd()
|
|
17401
|
+
});
|
|
17402
|
+
await exportAppIcon(record.icon, plans);
|
|
17403
|
+
ui.success(`Exported ${plans.length} icon file${plans.length > 1 ? "s" : ""} for ${args.id}`);
|
|
17404
|
+
this.renderJson(
|
|
17405
|
+
plans.map((plan) => ({
|
|
17406
|
+
width: plan.width,
|
|
17407
|
+
height: plan.height,
|
|
17408
|
+
format: plan.format,
|
|
17409
|
+
outputPath: plan.outputPath
|
|
17410
|
+
})),
|
|
17411
|
+
args.json
|
|
17412
|
+
);
|
|
17413
|
+
return;
|
|
17414
|
+
}
|
|
17236
17415
|
case "delete": {
|
|
17237
17416
|
const pathname = this.buildAppPath(args.id);
|
|
17238
17417
|
const record = await this.request(
|
|
@@ -17291,11 +17470,11 @@ var AppCommandHandler = class {
|
|
|
17291
17470
|
}).token;
|
|
17292
17471
|
}
|
|
17293
17472
|
readJsonFile(filePath) {
|
|
17294
|
-
const absolutePath =
|
|
17295
|
-
if (!(0,
|
|
17473
|
+
const absolutePath = this.resolveFilePath(filePath);
|
|
17474
|
+
if (!(0, import_node_fs17.existsSync)(absolutePath)) {
|
|
17296
17475
|
throw new Error(`JSON file not found: ${absolutePath}`);
|
|
17297
17476
|
}
|
|
17298
|
-
const raw = (0,
|
|
17477
|
+
const raw = (0, import_node_fs17.readFileSync)(absolutePath, "utf8");
|
|
17299
17478
|
try {
|
|
17300
17479
|
return JSON.parse(raw);
|
|
17301
17480
|
} catch (error) {
|
|
@@ -17303,6 +17482,13 @@ var AppCommandHandler = class {
|
|
|
17303
17482
|
throw new Error(`Invalid JSON in ${absolutePath}: ${message}`);
|
|
17304
17483
|
}
|
|
17305
17484
|
}
|
|
17485
|
+
preparePayload(filePath) {
|
|
17486
|
+
const absolutePath = this.resolveFilePath(filePath);
|
|
17487
|
+
return prepareAppPayload(this.readJsonFile(filePath), import_node_path22.default.dirname(absolutePath));
|
|
17488
|
+
}
|
|
17489
|
+
resolveFilePath(filePath) {
|
|
17490
|
+
return import_node_path22.default.resolve(process.cwd(), filePath);
|
|
17491
|
+
}
|
|
17306
17492
|
async request(baseUrl, token, method, pathname, body) {
|
|
17307
17493
|
const response = await fetch(`${baseUrl}${pathname}`, {
|
|
17308
17494
|
method,
|
|
@@ -17375,15 +17561,15 @@ function readNamespaceFromPayload(payload) {
|
|
|
17375
17561
|
}
|
|
17376
17562
|
|
|
17377
17563
|
// src/commands/ServiceCommandHandler.ts
|
|
17378
|
-
var
|
|
17564
|
+
var import_node_fs19 = require("fs");
|
|
17379
17565
|
var import_promises5 = require("fs/promises");
|
|
17380
|
-
var
|
|
17566
|
+
var import_node_path24 = __toESM(require("path"));
|
|
17381
17567
|
var import_promises6 = require("readline/promises");
|
|
17382
17568
|
|
|
17383
17569
|
// src/services/ServiceRuntimeRunner.ts
|
|
17384
|
-
var
|
|
17570
|
+
var import_node_fs18 = require("fs");
|
|
17385
17571
|
var import_node_os8 = __toESM(require("os"));
|
|
17386
|
-
var
|
|
17572
|
+
var import_node_path23 = __toESM(require("path"));
|
|
17387
17573
|
var import_node_child_process10 = require("child_process");
|
|
17388
17574
|
var import_node_readline = require("readline");
|
|
17389
17575
|
var import_chalk3 = __toESM(require("chalk"));
|
|
@@ -17496,14 +17682,14 @@ var ServiceRuntimeRunner = class {
|
|
|
17496
17682
|
}
|
|
17497
17683
|
resolveRepoRoot(service, cwd) {
|
|
17498
17684
|
if (cwd) {
|
|
17499
|
-
return
|
|
17685
|
+
return import_node_path23.default.resolve(process.cwd(), cwd);
|
|
17500
17686
|
}
|
|
17501
17687
|
const repository = this.getDeploy(service).repository?.trim();
|
|
17502
17688
|
if (repository) {
|
|
17503
17689
|
const repoName = repository.replace(/\/+$/, "").split("/").pop()?.replace(/\.git$/i, "");
|
|
17504
17690
|
if (repoName) {
|
|
17505
|
-
const candidate =
|
|
17506
|
-
if ((0,
|
|
17691
|
+
const candidate = import_node_path23.default.join(import_node_os8.default.homedir(), "dev", "pmate", repoName);
|
|
17692
|
+
if ((0, import_node_fs18.existsSync)(candidate)) {
|
|
17507
17693
|
return candidate;
|
|
17508
17694
|
}
|
|
17509
17695
|
}
|
|
@@ -17516,12 +17702,12 @@ var ServiceRuntimeRunner = class {
|
|
|
17516
17702
|
if (!dir) {
|
|
17517
17703
|
throw new Error(`Missing deploy.dir for ${service.id}`);
|
|
17518
17704
|
}
|
|
17519
|
-
return
|
|
17705
|
+
return import_node_path23.default.resolve(repoRoot, dir);
|
|
17520
17706
|
}
|
|
17521
17707
|
async deployVite(service, options) {
|
|
17522
17708
|
const appDir = this.resolveLocalDir(service, options.cwd);
|
|
17523
|
-
const packageJsonPath =
|
|
17524
|
-
if (!(0,
|
|
17709
|
+
const packageJsonPath = import_node_path23.default.join(appDir, "package.json");
|
|
17710
|
+
if (!(0, import_node_fs18.existsSync)(packageJsonPath)) {
|
|
17525
17711
|
throw new Error(`No package.json found at ${packageJsonPath}`);
|
|
17526
17712
|
}
|
|
17527
17713
|
const pkg2 = this.loadPackageJson(packageJsonPath);
|
|
@@ -17530,8 +17716,8 @@ var ServiceRuntimeRunner = class {
|
|
|
17530
17716
|
throw new Error(`Missing package name in ${packageJsonPath}`);
|
|
17531
17717
|
}
|
|
17532
17718
|
await this.runCommand("pnpm", ["--filter", pkgName, "build"], this.resolveRepoRoot(service, options.cwd));
|
|
17533
|
-
const distDir =
|
|
17534
|
-
if (!(0,
|
|
17719
|
+
const distDir = import_node_path23.default.join(appDir, "dist");
|
|
17720
|
+
if (!(0, import_node_fs18.existsSync)(distDir)) {
|
|
17535
17721
|
throw new Error(`No dist directory found at ${distDir}`);
|
|
17536
17722
|
}
|
|
17537
17723
|
const bucketName = this.getDeploy(service).bucket?.trim();
|
|
@@ -17542,8 +17728,8 @@ var ServiceRuntimeRunner = class {
|
|
|
17542
17728
|
}
|
|
17543
17729
|
async deployStatic(service, options) {
|
|
17544
17730
|
const appDir = this.resolveLocalDir(service, options.cwd);
|
|
17545
|
-
const srcDir =
|
|
17546
|
-
if (!(0,
|
|
17731
|
+
const srcDir = import_node_path23.default.join(appDir, "src");
|
|
17732
|
+
if (!(0, import_node_fs18.existsSync)(srcDir)) {
|
|
17547
17733
|
throw new Error(`No src directory found at ${srcDir}`);
|
|
17548
17734
|
}
|
|
17549
17735
|
const bucketName = this.getDeploy(service).bucket?.trim();
|
|
@@ -17661,7 +17847,7 @@ var ServiceRuntimeRunner = class {
|
|
|
17661
17847
|
const serverAddress = await resolveServerAddress(server);
|
|
17662
17848
|
const templateName = serviceType === "ws" ? "nginx.ws.template" : "nginx.rest.template";
|
|
17663
17849
|
const templatePath = this.resolveTemplatePath(templateName);
|
|
17664
|
-
const template = (0,
|
|
17850
|
+
const template = (0, import_node_fs18.readFileSync)(templatePath, "utf8");
|
|
17665
17851
|
const host = `${subdomain}.${deploy.domain?.trim() || "pmate.chat"}`;
|
|
17666
17852
|
const rendered = template.replace(/\$\{\{PORT\}\}/g, String(port)).replace(/\$\{\{HOSTS\}\}/g, host).replace(/\$\{\{HOST\}\}/g, host);
|
|
17667
17853
|
const remotePath = `/etc/nginx/sites-enabled/${subdomain}.conf`;
|
|
@@ -17693,11 +17879,11 @@ var ServiceRuntimeRunner = class {
|
|
|
17693
17879
|
}
|
|
17694
17880
|
if (config.ensurePackageJson) {
|
|
17695
17881
|
const appDir = this.resolveLocalDir(service, options.cwd);
|
|
17696
|
-
const packageJsonPath =
|
|
17697
|
-
if (!(0,
|
|
17882
|
+
const packageJsonPath = import_node_path23.default.join(appDir, "package.json");
|
|
17883
|
+
if (!(0, import_node_fs18.existsSync)(packageJsonPath)) {
|
|
17698
17884
|
throw new Error(`Missing package.json at ${packageJsonPath}`);
|
|
17699
17885
|
}
|
|
17700
|
-
const pkgStat = (0,
|
|
17886
|
+
const pkgStat = (0, import_node_fs18.statSync)(packageJsonPath);
|
|
17701
17887
|
if (!pkgStat.isFile()) {
|
|
17702
17888
|
throw new Error(`package.json is not a file at ${packageJsonPath}`);
|
|
17703
17889
|
}
|
|
@@ -18120,9 +18306,9 @@ var ServiceRuntimeRunner = class {
|
|
|
18120
18306
|
return tokens;
|
|
18121
18307
|
}
|
|
18122
18308
|
readPathValue(source, pathExpr) {
|
|
18123
|
-
const
|
|
18309
|
+
const path26 = pathExpr.split(".").map((segment) => segment.trim()).filter(Boolean);
|
|
18124
18310
|
let cursor = source;
|
|
18125
|
-
for (const segment of
|
|
18311
|
+
for (const segment of path26) {
|
|
18126
18312
|
if (!cursor || typeof cursor !== "object" || Array.isArray(cursor)) {
|
|
18127
18313
|
return void 0;
|
|
18128
18314
|
}
|
|
@@ -18165,12 +18351,12 @@ var ServiceRuntimeRunner = class {
|
|
|
18165
18351
|
}
|
|
18166
18352
|
resolveTemplatePath(templateName) {
|
|
18167
18353
|
const candidates = [
|
|
18168
|
-
|
|
18169
|
-
|
|
18170
|
-
|
|
18354
|
+
import_node_path23.default.join(__dirname, "..", "templates", templateName),
|
|
18355
|
+
import_node_path23.default.join(__dirname, "..", "src", "templates", templateName),
|
|
18356
|
+
import_node_path23.default.join(__dirname, "templates", templateName)
|
|
18171
18357
|
];
|
|
18172
18358
|
for (const candidate of candidates) {
|
|
18173
|
-
if ((0,
|
|
18359
|
+
if ((0, import_node_fs18.existsSync)(candidate)) {
|
|
18174
18360
|
return candidate;
|
|
18175
18361
|
}
|
|
18176
18362
|
}
|
|
@@ -18202,7 +18388,7 @@ var ServiceRuntimeRunner = class {
|
|
|
18202
18388
|
});
|
|
18203
18389
|
}
|
|
18204
18390
|
loadPackageJson(packageJsonPath) {
|
|
18205
|
-
const raw = (0,
|
|
18391
|
+
const raw = (0, import_node_fs18.readFileSync)(packageJsonPath, "utf8");
|
|
18206
18392
|
const parsed = JSON.parse(raw);
|
|
18207
18393
|
if (!parsed || typeof parsed !== "object") {
|
|
18208
18394
|
throw new Error(`Invalid package.json at ${packageJsonPath}`);
|
|
@@ -18211,7 +18397,7 @@ var ServiceRuntimeRunner = class {
|
|
|
18211
18397
|
}
|
|
18212
18398
|
writePackageJson(packageJsonPath, data) {
|
|
18213
18399
|
const serialized = JSON.stringify(data, null, 2);
|
|
18214
|
-
(0,
|
|
18400
|
+
(0, import_node_fs18.writeFileSync)(packageJsonPath, `${serialized}
|
|
18215
18401
|
`, "utf8");
|
|
18216
18402
|
}
|
|
18217
18403
|
runCommand(command, args, cwd) {
|
|
@@ -18551,7 +18737,7 @@ var ServiceCommandHandler = class {
|
|
|
18551
18737
|
format: "dotenv"
|
|
18552
18738
|
}
|
|
18553
18739
|
);
|
|
18554
|
-
const outputPath =
|
|
18740
|
+
const outputPath = import_node_path24.default.resolve(process.cwd(), args.output);
|
|
18555
18741
|
await (0, import_promises5.writeFile)(outputPath, payload.content, "utf8");
|
|
18556
18742
|
ui.success(`Wrote env file for ${args.appId} to ${outputPath}`);
|
|
18557
18743
|
this.renderJson({ ...payload, output: outputPath }, args.json);
|
|
@@ -18580,11 +18766,11 @@ var ServiceCommandHandler = class {
|
|
|
18580
18766
|
return (baseUrl || process.env.PMATE_APPS_API_BASE_URL || "https://apps-api.pmate.chat").replace(/\/+$/, "");
|
|
18581
18767
|
}
|
|
18582
18768
|
readJsonFile(filePath) {
|
|
18583
|
-
const absolutePath =
|
|
18584
|
-
if (!(0,
|
|
18769
|
+
const absolutePath = import_node_path24.default.resolve(process.cwd(), filePath);
|
|
18770
|
+
if (!(0, import_node_fs19.existsSync)(absolutePath)) {
|
|
18585
18771
|
throw new Error(`JSON file not found: ${absolutePath}`);
|
|
18586
18772
|
}
|
|
18587
|
-
const raw = (0,
|
|
18773
|
+
const raw = (0, import_node_fs19.readFileSync)(absolutePath, "utf8");
|
|
18588
18774
|
try {
|
|
18589
18775
|
return JSON.parse(raw);
|
|
18590
18776
|
} catch (error) {
|
|
@@ -19870,7 +20056,7 @@ function parseAppArgs(tokens) {
|
|
|
19870
20056
|
const rawAction = rest.shift();
|
|
19871
20057
|
const action = rawAction === "view" ? "view" : rawAction;
|
|
19872
20058
|
if (!action) {
|
|
19873
|
-
throw new Error("Missing app action. Use: app list|get|view|create|update|rename|move|delete");
|
|
20059
|
+
throw new Error("Missing app action. Use: app list|get|view|create|update|rename|move|icon|delete");
|
|
19874
20060
|
}
|
|
19875
20061
|
let baseUrl;
|
|
19876
20062
|
let json = false;
|
|
@@ -19949,6 +20135,108 @@ function parseAppArgs(tokens) {
|
|
|
19949
20135
|
}
|
|
19950
20136
|
return { type: "app" /* App */, action, id, baseUrl, json };
|
|
19951
20137
|
}
|
|
20138
|
+
if (action === "icon") {
|
|
20139
|
+
const rawRef = rest.shift();
|
|
20140
|
+
if (!rawRef || rawRef.startsWith("-")) {
|
|
20141
|
+
throw new Error(
|
|
20142
|
+
"Usage: app icon <ns:objectId> [--android|--ios|--size <w>x<h>|--<w>x<h>] [--format <png|webp|svg|ico>] [--output <path>] [--out-dir <path>] [--base-url <url>] [--json]"
|
|
20143
|
+
);
|
|
20144
|
+
}
|
|
20145
|
+
const id = parseNamespacedRef(rawRef, {
|
|
20146
|
+
entityLabel: "app ref"
|
|
20147
|
+
}).fullId;
|
|
20148
|
+
let preset;
|
|
20149
|
+
let width;
|
|
20150
|
+
let height;
|
|
20151
|
+
let format;
|
|
20152
|
+
let output;
|
|
20153
|
+
let outDir;
|
|
20154
|
+
while (rest.length > 0) {
|
|
20155
|
+
const token = rest.shift();
|
|
20156
|
+
const inlineSize = parseInlineSizeToken(token);
|
|
20157
|
+
if (inlineSize) {
|
|
20158
|
+
if (preset || width || height) {
|
|
20159
|
+
throw new Error("App icon export mode already set. Use exactly one of --android, --ios, --size <w>x<h>, or --<w>x<h>.");
|
|
20160
|
+
}
|
|
20161
|
+
width = inlineSize.width;
|
|
20162
|
+
height = inlineSize.height;
|
|
20163
|
+
continue;
|
|
20164
|
+
}
|
|
20165
|
+
switch (token) {
|
|
20166
|
+
case "--android":
|
|
20167
|
+
if (preset || width || height) {
|
|
20168
|
+
throw new Error("App icon export mode already set. Use exactly one of --android, --ios, --size <w>x<h>, or --<w>x<h>.");
|
|
20169
|
+
}
|
|
20170
|
+
preset = "android";
|
|
20171
|
+
break;
|
|
20172
|
+
case "--ios":
|
|
20173
|
+
if (preset || width || height) {
|
|
20174
|
+
throw new Error("App icon export mode already set. Use exactly one of --android, --ios, --size <w>x<h>, or --<w>x<h>.");
|
|
20175
|
+
}
|
|
20176
|
+
preset = "ios";
|
|
20177
|
+
break;
|
|
20178
|
+
case "--size": {
|
|
20179
|
+
if (preset || width || height) {
|
|
20180
|
+
throw new Error("App icon export mode already set. Use exactly one of --android, --ios, --size <w>x<h>, or --<w>x<h>.");
|
|
20181
|
+
}
|
|
20182
|
+
const size = parseSizeValue(requireValue5(rest, token));
|
|
20183
|
+
width = size.width;
|
|
20184
|
+
height = size.height;
|
|
20185
|
+
break;
|
|
20186
|
+
}
|
|
20187
|
+
case "--format": {
|
|
20188
|
+
const value = requireValue5(rest, token);
|
|
20189
|
+
if (value !== "png" && value !== "webp" && value !== "svg" && value !== "ico") {
|
|
20190
|
+
throw new Error(`Invalid app icon format "${value}". Use png, webp, svg, or ico.`);
|
|
20191
|
+
}
|
|
20192
|
+
format = value;
|
|
20193
|
+
break;
|
|
20194
|
+
}
|
|
20195
|
+
case "--output":
|
|
20196
|
+
output = requireValue5(rest, token);
|
|
20197
|
+
break;
|
|
20198
|
+
case "--out-dir":
|
|
20199
|
+
outDir = requireValue5(rest, token);
|
|
20200
|
+
break;
|
|
20201
|
+
case "--base-url":
|
|
20202
|
+
baseUrl = requireValue5(rest, token);
|
|
20203
|
+
break;
|
|
20204
|
+
case "--json":
|
|
20205
|
+
json = true;
|
|
20206
|
+
break;
|
|
20207
|
+
default:
|
|
20208
|
+
throw new Error(`Unknown app icon option: ${token}`);
|
|
20209
|
+
}
|
|
20210
|
+
}
|
|
20211
|
+
if (!preset && (!width || !height)) {
|
|
20212
|
+
throw new Error("Missing app icon export mode. Use one of --android, --ios, --size <w>x<h>, or --<w>x<h>.");
|
|
20213
|
+
}
|
|
20214
|
+
if (preset && output) {
|
|
20215
|
+
throw new Error("App icon preset export writes multiple files. Use --out-dir instead of --output.");
|
|
20216
|
+
}
|
|
20217
|
+
if (preset && !outDir) {
|
|
20218
|
+
throw new Error("App icon preset export requires --out-dir.");
|
|
20219
|
+
}
|
|
20220
|
+
if (!preset && outDir) {
|
|
20221
|
+
throw new Error("Single-size app icon export writes one file. Use --output instead of --out-dir.");
|
|
20222
|
+
}
|
|
20223
|
+
if (!preset && !output) {
|
|
20224
|
+
throw new Error("Single-size app icon export requires --output.");
|
|
20225
|
+
}
|
|
20226
|
+
return {
|
|
20227
|
+
type: "app" /* App */,
|
|
20228
|
+
action,
|
|
20229
|
+
id,
|
|
20230
|
+
preset,
|
|
20231
|
+
width,
|
|
20232
|
+
height,
|
|
20233
|
+
format,
|
|
20234
|
+
output,
|
|
20235
|
+
outDir,
|
|
20236
|
+
baseUrl,
|
|
20237
|
+
json
|
|
20238
|
+
};
|
|
20239
|
+
}
|
|
19952
20240
|
if (action === "create") {
|
|
19953
20241
|
let filePath;
|
|
19954
20242
|
while (rest.length > 0) {
|
|
@@ -20091,6 +20379,26 @@ function parseBareRenameId2(value, entityName) {
|
|
|
20091
20379
|
}
|
|
20092
20380
|
return normalized;
|
|
20093
20381
|
}
|
|
20382
|
+
function parseSizeValue(value) {
|
|
20383
|
+
const match = /^(\d+)x(\d+)$/i.exec(value.trim());
|
|
20384
|
+
if (!match) {
|
|
20385
|
+
throw new Error(`Invalid app icon size "${value}". Use <width>x<height>.`);
|
|
20386
|
+
}
|
|
20387
|
+
return {
|
|
20388
|
+
width: Number(match[1]),
|
|
20389
|
+
height: Number(match[2])
|
|
20390
|
+
};
|
|
20391
|
+
}
|
|
20392
|
+
function parseInlineSizeToken(token) {
|
|
20393
|
+
if (!token.startsWith("--")) {
|
|
20394
|
+
return null;
|
|
20395
|
+
}
|
|
20396
|
+
const value = token.slice(2);
|
|
20397
|
+
if (!/^\d+x\d+$/i.test(value)) {
|
|
20398
|
+
return null;
|
|
20399
|
+
}
|
|
20400
|
+
return parseSizeValue(value);
|
|
20401
|
+
}
|
|
20094
20402
|
|
|
20095
20403
|
// src/serviceCommandParser.ts
|
|
20096
20404
|
init_namespacedRef();
|
|
@@ -20982,7 +21290,7 @@ function requireValue10(tokens, option) {
|
|
|
20982
21290
|
|
|
20983
21291
|
// src/index.ts
|
|
20984
21292
|
var pkg = JSON.parse(
|
|
20985
|
-
(0,
|
|
21293
|
+
(0, import_node_fs20.readFileSync)(import_node_path25.default.resolve(__dirname, "..", "package.json"), "utf8")
|
|
20986
21294
|
);
|
|
20987
21295
|
var HELP_TEXT = [
|
|
20988
21296
|
import_chalk4.default.bold(`pmate v${pkg.version}`),
|
|
@@ -21112,6 +21420,9 @@ var HELP_TEXT = [
|
|
|
21112
21420
|
` ${import_chalk4.default.gray(" update <ns:objectId> --file <path> [--base-url <url>] [--json]")}`,
|
|
21113
21421
|
` ${import_chalk4.default.gray(" rename --from <id> --to <id> --ns <namespace> [--base-url <url>] [--json]")}`,
|
|
21114
21422
|
` ${import_chalk4.default.gray(" move --from <id> --ns <namespace> --to-ns <namespace> [--to <id>] [--base-url <url>] [--json]")}`,
|
|
21423
|
+
` ${import_chalk4.default.gray(" icon <ns:objectId> --android --out-dir <path> [--base-url <url>] [--json]")}`,
|
|
21424
|
+
` ${import_chalk4.default.gray(" icon <ns:objectId> --ios --out-dir <path> [--base-url <url>] [--json]")}`,
|
|
21425
|
+
` ${import_chalk4.default.gray(" icon <ns:objectId> (--size <w>x<h>|--<w>x<h>) --output <path> [--format <png|webp|svg|ico>] [--base-url <url>] [--json]")}`,
|
|
21115
21426
|
` ${import_chalk4.default.gray(" delete <ns:objectId> [--base-url <url>] [--json]")}`,
|
|
21116
21427
|
` ${import_chalk4.default.gray(" default base URL: https://apps-api.pmate.chat")}`,
|
|
21117
21428
|
` ${import_chalk4.default.cyan("service")} Manage service registry entries via apps-api`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pmate/cli",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.9",
|
|
4
4
|
"description": "CLI utilities for the pMate project",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -46,7 +46,9 @@
|
|
|
46
46
|
"jsqr": "^1.4.0",
|
|
47
47
|
"openai": "^6.16.0",
|
|
48
48
|
"playwright": "^1.58.2",
|
|
49
|
+
"png-to-ico": "^3.0.1",
|
|
49
50
|
"pngjs": "^7.0.0",
|
|
51
|
+
"sharp": "^0.34.5",
|
|
50
52
|
"tslib": "^2.8.1",
|
|
51
53
|
"yaml": "^2.8.2"
|
|
52
54
|
},
|
|
@@ -56,8 +58,8 @@
|
|
|
56
58
|
"@types/node": "^25.0.3",
|
|
57
59
|
"@types/pngjs": "^6.0.5",
|
|
58
60
|
"rimraf": "^6.1.2",
|
|
59
|
-
"tsup": "^8.5.0",
|
|
60
61
|
"ts-node": "^10.9.2",
|
|
62
|
+
"tsup": "^8.5.0",
|
|
61
63
|
"typescript": "^5.9.3",
|
|
62
64
|
"vitest": "^4.0.17"
|
|
63
65
|
}
|