@pmate/cli 0.36.1 → 0.37.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/README.md +20 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +307 -99
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -313,3 +313,23 @@ A registration failure fails the release command, while preserving already uploa
|
|
|
313
313
|
### Android release signing
|
|
314
314
|
|
|
315
315
|
`pmate service apk release <service> --env prod --variant release` resolves signing credentials from that service's server-side environment (host variables and local signing environment variables are not used). Configure `ANDROID_KEYSTORE_BASE64`, `ANDROID_KEYSTORE_PASSWORD`, `ANDROID_KEY_ALIAS`, and `ANDROID_KEY_PASSWORD` as Service Secrets, plus `ANDROID_SIGNING_CERT_SHA256` as the expected certificate fingerprint. Android SDK `apksigner` must be installed. The CLI signs the Gradle release APK and verifies its certificate before uploading; missing credentials, invalid signatures, or fingerprint mismatches stop publication. Keystore files use restricted temporary storage and are removed after signing. Existing apps must retain their compatible signing key. Debug builds keep their existing Gradle signing behavior.
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
### iOS TestFlight builds
|
|
319
|
+
|
|
320
|
+
On macOS with Xcode, a signed-in Apple developer account, valid signing profiles and an existing App Store Connect app:
|
|
321
|
+
|
|
322
|
+
```sh
|
|
323
|
+
# First build on this machine: import the highest Build already used in Apple.
|
|
324
|
+
pmate service ios build td:time-duck-ios --scheme TimeDuck --team D9D59BE76S --last-build 2026101501
|
|
325
|
+
# Subsequent builds on this machine:
|
|
326
|
+
pmate service ios build td:time-duck-ios --scheme TimeDuck --team D9D59BE76S
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
`service ios build` archives Release and uploads directly to App Store Connect for TestFlight. It preserves the marketing version (for example `0.1.0`) and increments only the integer Build by one. It does not publish an App Store release. `uploaded-processing` means Apple accepted the upload; processing and TestFlight download availability require separate verification.
|
|
330
|
+
|
|
331
|
+
Register an `ios` service with `repository` and `dir`. `--cwd` overrides the repository root; `--project` / `--workspace` paths are relative to the app directory. Exactly one discovered project/workspace is required unless explicitly selected. Always provide `--scheme`; `--team` overrides the project's signing team. `--json` returns version, Build, archive and log paths after upload.
|
|
332
|
+
|
|
333
|
+
The first invocation requires `--last-build <highest-used-build>` (0 only for a new app). The next Build is one greater than the maximum of the project value, imported value and locally reserved sequence. A previous date-based Build therefore continues as that integer plus one. Import the current Apple maximum again after uploads outside this CLI or on another machine; this command does not query Apple build history. Do not lower or delete the sequence to reuse a number.
|
|
334
|
+
|
|
335
|
+
Sequences, retained archives, derived data and logs live in `~/.pmate/ios-builds/`. Project/source version files are not changed. A bundle-level exclusive lock prevents simultaneous builds on the same machine; it does not coordinate different machines. Build/upload failures keep the reserved number and logs; retry consumes the next number. After an interrupted process, remove a leftover `.lock` file only after confirming no build is still running. Builds require automatic signing; resolve Apple account/certificate/profile errors in Xcode. Extension or App targets with fixed plist versions must produce matching archive metadata or the upload is stopped.
|
package/dist/index.d.ts
CHANGED
|
@@ -1003,6 +1003,19 @@ type ServiceCommandArgs = {
|
|
|
1003
1003
|
dryRun: boolean;
|
|
1004
1004
|
baseUrl?: string;
|
|
1005
1005
|
json: boolean;
|
|
1006
|
+
} | {
|
|
1007
|
+
type: Command.Service;
|
|
1008
|
+
action: "ios-build";
|
|
1009
|
+
namespace: string;
|
|
1010
|
+
name: string;
|
|
1011
|
+
scheme: string;
|
|
1012
|
+
project?: string;
|
|
1013
|
+
workspace?: string;
|
|
1014
|
+
team?: string;
|
|
1015
|
+
cwd?: string;
|
|
1016
|
+
lastBuild?: number;
|
|
1017
|
+
baseUrl?: string;
|
|
1018
|
+
json: boolean;
|
|
1006
1019
|
} | {
|
|
1007
1020
|
type: Command.Service;
|
|
1008
1021
|
action: "apk-release";
|
package/dist/index.js
CHANGED
|
@@ -8818,8 +8818,8 @@ __export(index_exports, {
|
|
|
8818
8818
|
parseCommandArgs: () => parseCommandArgs
|
|
8819
8819
|
});
|
|
8820
8820
|
module.exports = __toCommonJS(index_exports);
|
|
8821
|
-
var
|
|
8822
|
-
var
|
|
8821
|
+
var import_node_fs29 = require("fs");
|
|
8822
|
+
var import_node_path34 = __toESM(require("path"));
|
|
8823
8823
|
|
|
8824
8824
|
// src/managedSession.ts
|
|
8825
8825
|
var import_node_fs = require("fs");
|
|
@@ -14267,9 +14267,9 @@ ${indent}\`\`\``;
|
|
|
14267
14267
|
static richTextToPlain(richText) {
|
|
14268
14268
|
return richText.map((item) => item.plain_text ?? item.text?.content ?? "").join("").trim();
|
|
14269
14269
|
}
|
|
14270
|
-
static async requestJson(
|
|
14270
|
+
static async requestJson(path34, init) {
|
|
14271
14271
|
const token = this.getToken();
|
|
14272
|
-
const response = await fetch(`${this.baseUrl}${
|
|
14272
|
+
const response = await fetch(`${this.baseUrl}${path34}`, {
|
|
14273
14273
|
method: init.method,
|
|
14274
14274
|
headers: {
|
|
14275
14275
|
Authorization: `Bearer ${token}`,
|
|
@@ -16974,8 +16974,8 @@ function descriptorPathSegments(descriptor) {
|
|
|
16974
16974
|
return ["chains", normalized.chain, "namespaces", normalized.ns, normalized.kind, normalized.name];
|
|
16975
16975
|
}
|
|
16976
16976
|
function buildPathUrl(baseUrl, pathSegments, query) {
|
|
16977
|
-
const
|
|
16978
|
-
const url = new URL(
|
|
16977
|
+
const path34 = `/${pathSegments.map((segment) => encodeURIComponent(segment)).join("/")}`;
|
|
16978
|
+
const url = new URL(path34, baseUrl);
|
|
16979
16979
|
if (query) {
|
|
16980
16980
|
for (const [key, value] of Object.entries(query)) {
|
|
16981
16981
|
url.searchParams.set(key, String(value));
|
|
@@ -18404,12 +18404,19 @@ function readNamespaceFromPayload(payload) {
|
|
|
18404
18404
|
}
|
|
18405
18405
|
|
|
18406
18406
|
// src/commands/ServiceCommandHandler.ts
|
|
18407
|
-
var
|
|
18407
|
+
var import_node_fs25 = require("fs");
|
|
18408
18408
|
var import_promises5 = require("fs/promises");
|
|
18409
|
-
var
|
|
18410
|
-
var
|
|
18409
|
+
var import_node_path30 = __toESM(require("path"));
|
|
18410
|
+
var import_node_child_process13 = require("child_process");
|
|
18411
18411
|
var import_promises6 = require("readline/promises");
|
|
18412
18412
|
|
|
18413
|
+
// src/services/IosBuildService.ts
|
|
18414
|
+
var import_node_child_process11 = require("child_process");
|
|
18415
|
+
var import_node_crypto7 = require("crypto");
|
|
18416
|
+
var import_node_fs23 = require("fs");
|
|
18417
|
+
var import_node_os12 = __toESM(require("os"));
|
|
18418
|
+
var import_node_path28 = __toESM(require("path"));
|
|
18419
|
+
|
|
18413
18420
|
// src/services/AndroidApkReleaseService.ts
|
|
18414
18421
|
var import_node_child_process10 = require("child_process");
|
|
18415
18422
|
var import_node_crypto6 = require("crypto");
|
|
@@ -18900,6 +18907,153 @@ function capitalize(value) {
|
|
|
18900
18907
|
return value.slice(0, 1).toUpperCase() + value.slice(1);
|
|
18901
18908
|
}
|
|
18902
18909
|
|
|
18910
|
+
// src/services/IosBuildService.ts
|
|
18911
|
+
function iosBuildInteger(value) {
|
|
18912
|
+
if (!/^(0|[1-9]\d*)$/.test(String(value)) || !Number.isSafeInteger(Number(value))) {
|
|
18913
|
+
throw new Error(`iOS Build must be a non-negative safe integer: ${String(value)}`);
|
|
18914
|
+
}
|
|
18915
|
+
return Number(value);
|
|
18916
|
+
}
|
|
18917
|
+
async function runXcode(args, cwd, logPath) {
|
|
18918
|
+
return new Promise((resolve, reject) => {
|
|
18919
|
+
const child = (0, import_node_child_process11.spawn)("xcodebuild", args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
18920
|
+
let output = "";
|
|
18921
|
+
child.stdout.on("data", (chunk) => {
|
|
18922
|
+
if (args.includes("-json") || args.includes("-version")) output += chunk.toString();
|
|
18923
|
+
(0, import_node_fs23.appendFileSync)(logPath, chunk);
|
|
18924
|
+
});
|
|
18925
|
+
child.stderr.on("data", (chunk) => (0, import_node_fs23.appendFileSync)(logPath, chunk));
|
|
18926
|
+
child.on("error", reject);
|
|
18927
|
+
child.on("close", (code) => code === 0 ? resolve(output) : reject(new Error(`xcodebuild failed (${code}); see ${logPath}`)));
|
|
18928
|
+
});
|
|
18929
|
+
}
|
|
18930
|
+
var IosBuildService = class {
|
|
18931
|
+
constructor(deps = {}) {
|
|
18932
|
+
this.deps = deps;
|
|
18933
|
+
}
|
|
18934
|
+
async build(service, options) {
|
|
18935
|
+
if ((this.deps.platform ?? process.platform) !== "darwin") throw new Error("iOS TestFlight builds require macOS and Xcode");
|
|
18936
|
+
if (!("type" in service) || service.type !== "ios") throw new Error("iOS build requires an ios service");
|
|
18937
|
+
if (!options.scheme?.trim() || options.scheme.startsWith("-")) throw new Error("An explicit --scheme is required");
|
|
18938
|
+
if (options.project && options.workspace) throw new Error("Use only one of --project or --workspace");
|
|
18939
|
+
if (options.team && !/^[A-Z0-9]{10}$/.test(options.team)) throw new Error("Invalid Apple team ID");
|
|
18940
|
+
if (options.lastBuild !== void 0) iosBuildInteger(options.lastBuild);
|
|
18941
|
+
const repository = service.repository ?? service.mobile?.repository;
|
|
18942
|
+
const dir = service.dir ?? service.mobile?.dir;
|
|
18943
|
+
if (!dir || !repository && !options.cwd) throw new Error("iOS service requires repository and dir, or --cwd and dir");
|
|
18944
|
+
const repoRoot = options.cwd ? import_node_path28.default.resolve(options.cwd) : resolveRepositoryLocalRoot(repository);
|
|
18945
|
+
const appDir = import_node_path28.default.resolve(repoRoot, dir);
|
|
18946
|
+
if (!(0, import_node_fs23.existsSync)(appDir)) throw new Error(`iOS app dir not found: ${appDir}`);
|
|
18947
|
+
const explicit = options.project ?? options.workspace;
|
|
18948
|
+
const candidates = explicit ? [import_node_path28.default.resolve(appDir, explicit)] : (0, import_node_fs23.readdirSync)(appDir).filter((name) => /\.(xcodeproj|xcworkspace)$/.test(name)).map((name) => import_node_path28.default.join(appDir, name));
|
|
18949
|
+
if (candidates.length !== 1 || !(0, import_node_fs23.existsSync)(candidates[0])) throw new Error("Specify exactly one existing --project or --workspace; project discovery is ambiguous or empty");
|
|
18950
|
+
const project = candidates[0];
|
|
18951
|
+
const expectedExtension = options.workspace ? ".xcworkspace" : options.project ? ".xcodeproj" : import_node_path28.default.extname(project);
|
|
18952
|
+
if (![".xcodeproj", ".xcworkspace"].includes(expectedExtension) || import_node_path28.default.extname(project) !== expectedExtension) throw new Error("Invalid Xcode project/workspace extension");
|
|
18953
|
+
const root = this.deps.stateRoot ?? import_node_path28.default.join(import_node_os12.default.homedir(), ".pmate", "ios-builds");
|
|
18954
|
+
(0, import_node_fs23.mkdirSync)(root, { recursive: true, mode: 448 });
|
|
18955
|
+
const preflightLog = import_node_path28.default.join(root, `preflight-${process.pid}-${Date.now()}.log`);
|
|
18956
|
+
const run = this.deps.run ?? runXcode;
|
|
18957
|
+
await run(["-version"], appDir, preflightLog);
|
|
18958
|
+
const common2 = [expectedExtension === ".xcworkspace" ? "-workspace" : "-project", project, "-scheme", options.scheme, "-configuration", "Release", "-destination", "generic/platform=iOS"];
|
|
18959
|
+
if (options.team) common2.push(`DEVELOPMENT_TEAM=${options.team}`);
|
|
18960
|
+
const settings = JSON.parse(await run([...common2, "-showBuildSettings", "-json"], appDir, preflightLog));
|
|
18961
|
+
const apps = settings.filter((item) => item.buildSettings.PRODUCT_TYPE === "com.apple.product-type.application");
|
|
18962
|
+
if (apps.length !== 1) throw new Error("Scheme must contain exactly one application target");
|
|
18963
|
+
const cfg = apps[0].buildSettings;
|
|
18964
|
+
const bundleId = cfg.PRODUCT_BUNDLE_IDENTIFIER;
|
|
18965
|
+
const version = cfg.MARKETING_VERSION;
|
|
18966
|
+
const team = cfg.DEVELOPMENT_TEAM;
|
|
18967
|
+
if (!bundleId || !/^\d+\.\d+(?:\.\d+)?$/.test(version ?? "") || !/^[A-Z0-9]{10}$/.test(team ?? "")) throw new Error("Missing or invalid bundle ID, MARKETING_VERSION or signing team; use --team if needed");
|
|
18968
|
+
if (service.mobile && "bundleId" in service.mobile && service.mobile.bundleId !== bundleId) throw new Error("Service bundle ID does not match application target");
|
|
18969
|
+
const currentBuild = iosBuildInteger(cfg.CURRENT_PROJECT_VERSION);
|
|
18970
|
+
const key = (0, import_node_crypto7.createHash)("sha256").update(bundleId).digest("hex");
|
|
18971
|
+
const statePath = import_node_path28.default.join(root, `${key}.json`);
|
|
18972
|
+
const lockPath = import_node_path28.default.join(root, `${key}.lock`);
|
|
18973
|
+
let lock;
|
|
18974
|
+
try {
|
|
18975
|
+
lock = (0, import_node_fs23.openSync)(lockPath, "wx", 384);
|
|
18976
|
+
} catch (error) {
|
|
18977
|
+
if (error.code === "EEXIST") throw new Error(`iOS build already locked: ${lockPath}. Remove only after confirming no build is running.`);
|
|
18978
|
+
throw error;
|
|
18979
|
+
}
|
|
18980
|
+
try {
|
|
18981
|
+
(0, import_node_fs23.writeFileSync)(lock, JSON.stringify({ pid: process.pid, bundleId }));
|
|
18982
|
+
const previous = (0, import_node_fs23.existsSync)(statePath) ? JSON.parse((0, import_node_fs23.readFileSync)(statePath, "utf8")) : void 0;
|
|
18983
|
+
if (previous && previous.bundleId !== bundleId) throw new Error("iOS build state bundle ID mismatch");
|
|
18984
|
+
if (!previous && options.lastBuild === void 0) throw new Error("First build on this machine requires --last-build <highest Apple build>; use 0 only for a new app. Import again after external uploads or changing machines.");
|
|
18985
|
+
const build = Math.max(currentBuild, previous ? iosBuildInteger(previous.build) : 0, options.lastBuild ?? 0) + 1;
|
|
18986
|
+
iosBuildInteger(build);
|
|
18987
|
+
const buildDir = import_node_path28.default.join(root, key, `${version}-${build}`);
|
|
18988
|
+
(0, import_node_fs23.mkdirSync)(buildDir, { recursive: true, mode: 448 });
|
|
18989
|
+
const archivePath = import_node_path28.default.join(buildDir, "App.xcarchive");
|
|
18990
|
+
const logPath = import_node_path28.default.join(buildDir, "build.log");
|
|
18991
|
+
const state = { bundleId, version, build, status: "reserved", archivePath, logPath };
|
|
18992
|
+
const persist = () => {
|
|
18993
|
+
const temp = `${statePath}.${process.pid}.tmp`;
|
|
18994
|
+
(0, import_node_fs23.writeFileSync)(temp, JSON.stringify(state, null, 2) + "\n", { mode: 384 });
|
|
18995
|
+
(0, import_node_fs23.renameSync)(temp, statePath);
|
|
18996
|
+
};
|
|
18997
|
+
persist();
|
|
18998
|
+
(0, import_node_fs23.writeFileSync)(logPath, `Service ${service.id}: ${bundleId} ${version} (${build})
|
|
18999
|
+
Preflight log: ${preflightLog}
|
|
19000
|
+
`, { mode: 384 });
|
|
19001
|
+
try {
|
|
19002
|
+
await run([...common2, `CURRENT_PROJECT_VERSION=${build}`, `MARKETING_VERSION=${version}`, "-archivePath", archivePath, "-derivedDataPath", import_node_path28.default.join(buildDir, "DerivedData"), "-allowProvisioningUpdates", "archive"], appDir, logPath);
|
|
19003
|
+
const applications = import_node_path28.default.join(archivePath, "Products", "Applications");
|
|
19004
|
+
const builtApps = (0, import_node_fs23.readdirSync)(applications).filter((name) => name.endsWith(".app"));
|
|
19005
|
+
if (builtApps.length !== 1) throw new Error("Archive must contain exactly one application");
|
|
19006
|
+
const plist = (0, import_node_fs23.readFileSync)(import_node_path28.default.join(applications, builtApps[0], "Info.plist"));
|
|
19007
|
+
const info = await (this.deps.readPlist ?? readPlist)(plist);
|
|
19008
|
+
if (info.CFBundleIdentifier !== bundleId || info.CFBundleShortVersionString !== version || String(info.CFBundleVersion) !== String(build)) throw new Error("Archive metadata does not match requested bundle, version and Build; upload stopped");
|
|
19009
|
+
const exportPlist = import_node_path28.default.join(buildDir, "ExportOptions.plist");
|
|
19010
|
+
(0, import_node_fs23.writeFileSync)(exportPlist, `<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>method</key><string>app-store-connect</string><key>destination</key><string>upload</string><key>teamID</key><string>${team}</string><key>signingStyle</key><string>automatic</string><key>manageAppVersionAndBuildNumber</key><false/><key>uploadSymbols</key><true/></dict></plist>`);
|
|
19011
|
+
await run(["-exportArchive", "-archivePath", archivePath, "-exportOptionsPlist", exportPlist, "-exportPath", import_node_path28.default.join(buildDir, "export"), "-allowProvisioningUpdates"], appDir, logPath);
|
|
19012
|
+
state.status = "uploaded-processing";
|
|
19013
|
+
persist();
|
|
19014
|
+
return { serviceId: service.id, bundleId, version, build, status: "uploaded-processing", archivePath, logPath };
|
|
19015
|
+
} catch (error) {
|
|
19016
|
+
state.status = "failed";
|
|
19017
|
+
persist();
|
|
19018
|
+
(0, import_node_fs23.appendFileSync)(logPath, `
|
|
19019
|
+
${String(error)}
|
|
19020
|
+
`);
|
|
19021
|
+
throw new Error(`iOS Build ${build} failed; sequence reserved. See ${logPath}. ${String(error)}`);
|
|
19022
|
+
}
|
|
19023
|
+
} finally {
|
|
19024
|
+
(0, import_node_fs23.closeSync)(lock);
|
|
19025
|
+
(0, import_node_fs23.unlinkSync)(lockPath);
|
|
19026
|
+
}
|
|
19027
|
+
}
|
|
19028
|
+
};
|
|
19029
|
+
async function readPlist(input) {
|
|
19030
|
+
return new Promise((resolve, reject) => {
|
|
19031
|
+
const child = (0, import_node_child_process11.spawn)("plutil", ["-convert", "json", "-o", "-", "--", "-"], { stdio: ["pipe", "pipe", "pipe"] });
|
|
19032
|
+
let output = "";
|
|
19033
|
+
let error = "";
|
|
19034
|
+
child.stdout.on("data", (chunk) => {
|
|
19035
|
+
output += chunk.toString();
|
|
19036
|
+
});
|
|
19037
|
+
child.stderr.on("data", (chunk) => {
|
|
19038
|
+
error += chunk.toString();
|
|
19039
|
+
});
|
|
19040
|
+
child.on("error", reject);
|
|
19041
|
+
child.on("close", (code) => {
|
|
19042
|
+
if (code !== 0) {
|
|
19043
|
+
reject(new Error(`Cannot read archive plist: ${error}`));
|
|
19044
|
+
return;
|
|
19045
|
+
}
|
|
19046
|
+
try {
|
|
19047
|
+
resolve(JSON.parse(output));
|
|
19048
|
+
} catch (err2) {
|
|
19049
|
+
reject(err2);
|
|
19050
|
+
}
|
|
19051
|
+
});
|
|
19052
|
+
child.stdin.on("error", reject);
|
|
19053
|
+
child.stdin.end(input);
|
|
19054
|
+
});
|
|
19055
|
+
}
|
|
19056
|
+
|
|
18903
19057
|
// src/services/DeveloperLogsClient.ts
|
|
18904
19058
|
init_sessionStore();
|
|
18905
19059
|
init_namespacedRef();
|
|
@@ -18987,10 +19141,10 @@ async function requestDeveloperLogTool(tool, params, fetcher = fetch, delegate)
|
|
|
18987
19141
|
}
|
|
18988
19142
|
|
|
18989
19143
|
// src/services/ServiceRuntimeRunner.ts
|
|
18990
|
-
var
|
|
18991
|
-
var
|
|
18992
|
-
var
|
|
18993
|
-
var
|
|
19144
|
+
var import_node_fs24 = require("fs");
|
|
19145
|
+
var import_node_os13 = __toESM(require("os"));
|
|
19146
|
+
var import_node_path29 = __toESM(require("path"));
|
|
19147
|
+
var import_node_child_process12 = require("child_process");
|
|
18994
19148
|
var import_node_readline = require("readline");
|
|
18995
19149
|
var import_chalk3 = __toESM(require("chalk"));
|
|
18996
19150
|
var import_dotenv = require("dotenv");
|
|
@@ -19489,14 +19643,14 @@ var ServiceRuntimeRunner = class {
|
|
|
19489
19643
|
}
|
|
19490
19644
|
resolveRepoRoot(service, cwd) {
|
|
19491
19645
|
if (cwd) {
|
|
19492
|
-
return
|
|
19646
|
+
return import_node_path29.default.resolve(process.cwd(), cwd);
|
|
19493
19647
|
}
|
|
19494
19648
|
const repository = this.getType(service) === "vite" || this.getType(service) === "static" ? this.getRepoStaticSite(service).repository.trim() : this.getType(service) === "fc" ? this.getFc(service).repository?.trim() : this.getType(service) === "ios" || this.getType(service) === "android" ? void 0 : this.getNetwork(service).repository?.trim();
|
|
19495
19649
|
if (repository) {
|
|
19496
19650
|
const repoName = repository.replace(/\/+$/, "").split("/").pop()?.replace(/\.git$/i, "");
|
|
19497
19651
|
if (repoName) {
|
|
19498
|
-
const candidate =
|
|
19499
|
-
if ((0,
|
|
19652
|
+
const candidate = import_node_path29.default.join(import_node_os13.default.homedir(), "dev", "pmate", repoName);
|
|
19653
|
+
if ((0, import_node_fs24.existsSync)(candidate)) {
|
|
19500
19654
|
return candidate;
|
|
19501
19655
|
}
|
|
19502
19656
|
}
|
|
@@ -19509,13 +19663,13 @@ var ServiceRuntimeRunner = class {
|
|
|
19509
19663
|
if (!dir) {
|
|
19510
19664
|
throw new Error(`Missing service dir for ${service.id}`);
|
|
19511
19665
|
}
|
|
19512
|
-
return
|
|
19666
|
+
return import_node_path29.default.resolve(repoRoot, dir);
|
|
19513
19667
|
}
|
|
19514
19668
|
async deployVite(service, options) {
|
|
19515
19669
|
this.assertDeployableStaticSite(service);
|
|
19516
19670
|
const appDir = this.resolveLocalDir(service, options.cwd);
|
|
19517
|
-
const packageJsonPath =
|
|
19518
|
-
if (!(0,
|
|
19671
|
+
const packageJsonPath = import_node_path29.default.join(appDir, "package.json");
|
|
19672
|
+
if (!(0, import_node_fs24.existsSync)(packageJsonPath)) {
|
|
19519
19673
|
throw new Error(`No package.json found at ${packageJsonPath}`);
|
|
19520
19674
|
}
|
|
19521
19675
|
const pkg2 = this.loadPackageJson(packageJsonPath);
|
|
@@ -19530,8 +19684,8 @@ var ServiceRuntimeRunner = class {
|
|
|
19530
19684
|
this.resolveRepoRoot(service, options.cwd),
|
|
19531
19685
|
buildEnv
|
|
19532
19686
|
);
|
|
19533
|
-
const distDir =
|
|
19534
|
-
if (!(0,
|
|
19687
|
+
const distDir = import_node_path29.default.join(appDir, "dist");
|
|
19688
|
+
if (!(0, import_node_fs24.existsSync)(distDir)) {
|
|
19535
19689
|
throw new Error(`No dist directory found at ${distDir}`);
|
|
19536
19690
|
}
|
|
19537
19691
|
const site = this.getSite(service);
|
|
@@ -19544,8 +19698,8 @@ var ServiceRuntimeRunner = class {
|
|
|
19544
19698
|
async deployStatic(service, options) {
|
|
19545
19699
|
this.assertDeployableStaticSite(service);
|
|
19546
19700
|
const appDir = this.resolveLocalDir(service, options.cwd);
|
|
19547
|
-
const srcDir =
|
|
19548
|
-
if (!(0,
|
|
19701
|
+
const srcDir = import_node_path29.default.join(appDir, "src");
|
|
19702
|
+
if (!(0, import_node_fs24.existsSync)(srcDir)) {
|
|
19549
19703
|
throw new Error(`No src directory found at ${srcDir}`);
|
|
19550
19704
|
}
|
|
19551
19705
|
const site = this.getSite(service);
|
|
@@ -19579,8 +19733,8 @@ var ServiceRuntimeRunner = class {
|
|
|
19579
19733
|
this.validateFcConfig(service, fc);
|
|
19580
19734
|
const repoRoot = this.resolveRepoRoot(service, options.cwd);
|
|
19581
19735
|
const appDir = this.resolveLocalDir(service, options.cwd);
|
|
19582
|
-
const packageJsonPath =
|
|
19583
|
-
if (!(0,
|
|
19736
|
+
const packageJsonPath = import_node_path29.default.join(appDir, "package.json");
|
|
19737
|
+
if (!(0, import_node_fs24.existsSync)(packageJsonPath)) {
|
|
19584
19738
|
throw new Error(`No package.json found at ${packageJsonPath}`);
|
|
19585
19739
|
}
|
|
19586
19740
|
const pkg2 = this.loadPackageJson(packageJsonPath);
|
|
@@ -19633,15 +19787,15 @@ var ServiceRuntimeRunner = class {
|
|
|
19633
19787
|
const repoRoot = this.resolveRepoRoot(service, options.cwd);
|
|
19634
19788
|
const appDir = this.resolveLocalDir(service, options.cwd);
|
|
19635
19789
|
await this.ensurePnpmWorkspaceRoot(repoRoot);
|
|
19636
|
-
const configPath =
|
|
19637
|
-
if (!(0,
|
|
19790
|
+
const configPath = import_node_path29.default.join(appDir, fc.configFile ?? "s.yaml");
|
|
19791
|
+
if (!(0, import_node_fs24.existsSync)(configPath)) {
|
|
19638
19792
|
throw new Error(`Missing FC config file at ${configPath}`);
|
|
19639
19793
|
}
|
|
19640
|
-
const packageJsonPath =
|
|
19641
|
-
if (!(0,
|
|
19794
|
+
const packageJsonPath = import_node_path29.default.join(appDir, "package.json");
|
|
19795
|
+
if (!(0, import_node_fs24.existsSync)(packageJsonPath)) {
|
|
19642
19796
|
throw new Error(`No package.json found at ${packageJsonPath}`);
|
|
19643
19797
|
}
|
|
19644
|
-
const rawConfig = (0,
|
|
19798
|
+
const rawConfig = (0, import_node_fs24.readFileSync)(configPath, "utf8");
|
|
19645
19799
|
if (!rawConfig.includes(fc.functionName)) {
|
|
19646
19800
|
throw new Error(`FC config ${configPath} does not mention functionName ${fc.functionName}`);
|
|
19647
19801
|
}
|
|
@@ -19931,7 +20085,7 @@ var ServiceRuntimeRunner = class {
|
|
|
19931
20085
|
const serverAddress = await resolveServerAddress(server);
|
|
19932
20086
|
const templateName = exposeType === "websocket" ? "nginx.ws.template" : exposeType === "mixed" ? "nginx.mixed.template" : "nginx.rest.template";
|
|
19933
20087
|
const templatePath = this.resolveTemplatePath(templateName);
|
|
19934
|
-
const template = (0,
|
|
20088
|
+
const template = (0, import_node_fs24.readFileSync)(templatePath, "utf8");
|
|
19935
20089
|
const host = `${subdomain}.${("domain" in network ? network.domain?.trim() : void 0) || "pmate.chat"}`;
|
|
19936
20090
|
const rendered = template.replace(/\$\{\{PORT\}\}/g, String(port)).replace(/\$\{\{HOSTS\}\}/g, host).replace(/\$\{\{HOST\}\}/g, host);
|
|
19937
20091
|
const remotePath = `/etc/nginx/sites-enabled/${subdomain}.conf`;
|
|
@@ -19963,11 +20117,11 @@ var ServiceRuntimeRunner = class {
|
|
|
19963
20117
|
}
|
|
19964
20118
|
if (config.ensurePackageJson) {
|
|
19965
20119
|
const appDir = this.resolveLocalDir(service, options.cwd);
|
|
19966
|
-
const packageJsonPath =
|
|
19967
|
-
if (!(0,
|
|
20120
|
+
const packageJsonPath = import_node_path29.default.join(appDir, "package.json");
|
|
20121
|
+
if (!(0, import_node_fs24.existsSync)(packageJsonPath)) {
|
|
19968
20122
|
throw new Error(`Missing package.json at ${packageJsonPath}`);
|
|
19969
20123
|
}
|
|
19970
|
-
const pkgStat = (0,
|
|
20124
|
+
const pkgStat = (0, import_node_fs24.statSync)(packageJsonPath);
|
|
19971
20125
|
if (!pkgStat.isFile()) {
|
|
19972
20126
|
throw new Error(`package.json is not a file at ${packageJsonPath}`);
|
|
19973
20127
|
}
|
|
@@ -20378,9 +20532,9 @@ var ServiceRuntimeRunner = class {
|
|
|
20378
20532
|
return tokens;
|
|
20379
20533
|
}
|
|
20380
20534
|
readPathValue(source, pathExpr) {
|
|
20381
|
-
const
|
|
20535
|
+
const path34 = pathExpr.split(".").map((segment) => segment.trim()).filter(Boolean);
|
|
20382
20536
|
let cursor = source;
|
|
20383
|
-
for (const segment of
|
|
20537
|
+
for (const segment of path34) {
|
|
20384
20538
|
if (!cursor || typeof cursor !== "object" || Array.isArray(cursor)) {
|
|
20385
20539
|
return void 0;
|
|
20386
20540
|
}
|
|
@@ -20423,12 +20577,12 @@ var ServiceRuntimeRunner = class {
|
|
|
20423
20577
|
}
|
|
20424
20578
|
resolveTemplatePath(templateName) {
|
|
20425
20579
|
const candidates = [
|
|
20426
|
-
|
|
20427
|
-
|
|
20428
|
-
|
|
20580
|
+
import_node_path29.default.join(__dirname, "..", "templates", templateName),
|
|
20581
|
+
import_node_path29.default.join(__dirname, "..", "src", "templates", templateName),
|
|
20582
|
+
import_node_path29.default.join(__dirname, "templates", templateName)
|
|
20429
20583
|
];
|
|
20430
20584
|
for (const candidate of candidates) {
|
|
20431
|
-
if ((0,
|
|
20585
|
+
if ((0, import_node_fs24.existsSync)(candidate)) {
|
|
20432
20586
|
return candidate;
|
|
20433
20587
|
}
|
|
20434
20588
|
}
|
|
@@ -20437,7 +20591,7 @@ var ServiceRuntimeRunner = class {
|
|
|
20437
20591
|
writeRemoteConfig(server, content, remotePath) {
|
|
20438
20592
|
return new Promise((resolve, reject) => {
|
|
20439
20593
|
const target = `pmate@${server}`;
|
|
20440
|
-
const child = (0,
|
|
20594
|
+
const child = (0, import_node_child_process12.spawn)("ssh", [target, "sudo", "tee", remotePath], {
|
|
20441
20595
|
stdio: ["pipe", "inherit", "inherit"]
|
|
20442
20596
|
});
|
|
20443
20597
|
child.on("error", reject);
|
|
@@ -20452,7 +20606,7 @@ var ServiceRuntimeRunner = class {
|
|
|
20452
20606
|
reloadNginx(server) {
|
|
20453
20607
|
return new Promise((resolve, reject) => {
|
|
20454
20608
|
const target = `pmate@${server}`;
|
|
20455
|
-
const child = (0,
|
|
20609
|
+
const child = (0, import_node_child_process12.spawn)("ssh", [target, "sudo", "nginx", "-s", "reload"], {
|
|
20456
20610
|
stdio: ["ignore", "inherit", "inherit"]
|
|
20457
20611
|
});
|
|
20458
20612
|
child.on("error", reject);
|
|
@@ -20460,7 +20614,7 @@ var ServiceRuntimeRunner = class {
|
|
|
20460
20614
|
});
|
|
20461
20615
|
}
|
|
20462
20616
|
loadPackageJson(packageJsonPath) {
|
|
20463
|
-
const raw = (0,
|
|
20617
|
+
const raw = (0, import_node_fs24.readFileSync)(packageJsonPath, "utf8");
|
|
20464
20618
|
const parsed = JSON.parse(raw);
|
|
20465
20619
|
if (!parsed || typeof parsed !== "object") {
|
|
20466
20620
|
throw new Error(`Invalid package.json at ${packageJsonPath}`);
|
|
@@ -20469,7 +20623,7 @@ var ServiceRuntimeRunner = class {
|
|
|
20469
20623
|
}
|
|
20470
20624
|
writePackageJson(packageJsonPath, data) {
|
|
20471
20625
|
const serialized = JSON.stringify(data, null, 2);
|
|
20472
|
-
(0,
|
|
20626
|
+
(0, import_node_fs24.writeFileSync)(packageJsonPath, `${serialized}
|
|
20473
20627
|
`, "utf8");
|
|
20474
20628
|
}
|
|
20475
20629
|
validateFcConfig(service, fc) {
|
|
@@ -20496,8 +20650,8 @@ var ServiceRuntimeRunner = class {
|
|
|
20496
20650
|
}
|
|
20497
20651
|
}
|
|
20498
20652
|
async ensurePnpmWorkspaceRoot(repoRoot) {
|
|
20499
|
-
const workspacePath =
|
|
20500
|
-
if (!(0,
|
|
20653
|
+
const workspacePath = import_node_path29.default.join(repoRoot, "pnpm-workspace.yaml");
|
|
20654
|
+
if (!(0, import_node_fs24.existsSync)(workspacePath)) {
|
|
20501
20655
|
throw new Error(`Missing pnpm-workspace.yaml at ${workspacePath}`);
|
|
20502
20656
|
}
|
|
20503
20657
|
}
|
|
@@ -20571,16 +20725,16 @@ var ServiceRuntimeRunner = class {
|
|
|
20571
20725
|
if (!privateKeyPath) {
|
|
20572
20726
|
throw new Error("Missing ssl.prv in ~/.pmate/config.yaml.");
|
|
20573
20727
|
}
|
|
20574
|
-
if (!(0,
|
|
20728
|
+
if (!(0, import_node_fs24.existsSync)(certificatePath)) {
|
|
20575
20729
|
throw new Error(`SSL certificate file not found: ${certificatePath}`);
|
|
20576
20730
|
}
|
|
20577
|
-
if (!(0,
|
|
20731
|
+
if (!(0, import_node_fs24.existsSync)(privateKeyPath)) {
|
|
20578
20732
|
throw new Error(`SSL private key file not found: ${privateKeyPath}`);
|
|
20579
20733
|
}
|
|
20580
20734
|
return {
|
|
20581
20735
|
certName: host,
|
|
20582
|
-
certificate: (0,
|
|
20583
|
-
privateKey: (0,
|
|
20736
|
+
certificate: (0, import_node_fs24.readFileSync)(certificatePath, "utf8"),
|
|
20737
|
+
privateKey: (0, import_node_fs24.readFileSync)(privateKeyPath, "utf8")
|
|
20584
20738
|
};
|
|
20585
20739
|
}
|
|
20586
20740
|
resolveFcProtocol(fc, certConfig) {
|
|
@@ -20639,7 +20793,7 @@ var ServiceRuntimeRunner = class {
|
|
|
20639
20793
|
}
|
|
20640
20794
|
runCommandWithEnv(command, args, cwd, env) {
|
|
20641
20795
|
return new Promise((resolve, reject) => {
|
|
20642
|
-
const child = (0,
|
|
20796
|
+
const child = (0, import_node_child_process12.spawn)(command, args, {
|
|
20643
20797
|
cwd,
|
|
20644
20798
|
stdio: "inherit",
|
|
20645
20799
|
env: { ...process.env, ...env ?? {} }
|
|
@@ -20650,7 +20804,7 @@ var ServiceRuntimeRunner = class {
|
|
|
20650
20804
|
}
|
|
20651
20805
|
runShellCommand(command, cwd, env) {
|
|
20652
20806
|
return new Promise((resolve, reject) => {
|
|
20653
|
-
const child = (0,
|
|
20807
|
+
const child = (0, import_node_child_process12.spawn)(command, [], {
|
|
20654
20808
|
cwd,
|
|
20655
20809
|
stdio: "inherit",
|
|
20656
20810
|
shell: true,
|
|
@@ -20661,10 +20815,10 @@ var ServiceRuntimeRunner = class {
|
|
|
20661
20815
|
});
|
|
20662
20816
|
}
|
|
20663
20817
|
runRemoteCommand(server, command) {
|
|
20664
|
-
if (server === "local") return this.runCommandWithEnv("bash", ["-c", command],
|
|
20818
|
+
if (server === "local") return this.runCommandWithEnv("bash", ["-c", command], import_node_os13.default.homedir());
|
|
20665
20819
|
return new Promise((resolve, reject) => {
|
|
20666
20820
|
const target = `pmate@${server}`;
|
|
20667
|
-
const child = (0,
|
|
20821
|
+
const child = (0, import_node_child_process12.spawn)("ssh", [target, "bash -ls"], { stdio: ["pipe", "inherit", "inherit"] });
|
|
20668
20822
|
child.stdin.on("error", reject);
|
|
20669
20823
|
child.stdin.end(`${command}
|
|
20670
20824
|
`);
|
|
@@ -20955,6 +21109,16 @@ var ServiceCommandHandler = class {
|
|
|
20955
21109
|
);
|
|
20956
21110
|
return;
|
|
20957
21111
|
}
|
|
21112
|
+
case "ios-build": {
|
|
21113
|
+
const record = await registry.getService(args.namespace, args.name);
|
|
21114
|
+
const result = await new IosBuildService().build(record, args);
|
|
21115
|
+
if (args.json) this.renderJson(result, true);
|
|
21116
|
+
else {
|
|
21117
|
+
ui.success(`Uploaded iOS ${result.version} (${result.build}); Apple processing, TestFlight availability not yet verified`);
|
|
21118
|
+
this.renderJson(result, true);
|
|
21119
|
+
}
|
|
21120
|
+
return;
|
|
21121
|
+
}
|
|
20958
21122
|
case "apk-release": {
|
|
20959
21123
|
const record = await registry.getService(args.namespace, args.name);
|
|
20960
21124
|
const apkRelease = new AndroidApkReleaseService(registry);
|
|
@@ -21057,7 +21221,7 @@ var ServiceCommandHandler = class {
|
|
|
21057
21221
|
format: "dotenv"
|
|
21058
21222
|
}
|
|
21059
21223
|
);
|
|
21060
|
-
const outputPath =
|
|
21224
|
+
const outputPath = import_node_path30.default.resolve(process.cwd(), args.output);
|
|
21061
21225
|
await (0, import_promises5.writeFile)(outputPath, payload.content, "utf8");
|
|
21062
21226
|
ui.success(`Wrote env file for ${args.appId} to ${outputPath}`);
|
|
21063
21227
|
this.renderJson({ ...payload, output: outputPath }, args.json);
|
|
@@ -21283,11 +21447,11 @@ var ServiceCommandHandler = class {
|
|
|
21283
21447
|
return (baseUrl || process.env.PMATE_APPS_API_BASE_URL || "https://apps-api.pmate.chat").replace(/\/+$/, "");
|
|
21284
21448
|
}
|
|
21285
21449
|
readJsonFile(filePath) {
|
|
21286
|
-
const absolutePath =
|
|
21287
|
-
if (!(0,
|
|
21450
|
+
const absolutePath = import_node_path30.default.resolve(process.cwd(), filePath);
|
|
21451
|
+
if (!(0, import_node_fs25.existsSync)(absolutePath)) {
|
|
21288
21452
|
throw new Error(`JSON file not found: ${absolutePath}`);
|
|
21289
21453
|
}
|
|
21290
|
-
const raw = (0,
|
|
21454
|
+
const raw = (0, import_node_fs25.readFileSync)(absolutePath, "utf8");
|
|
21291
21455
|
try {
|
|
21292
21456
|
return JSON.parse(raw);
|
|
21293
21457
|
} catch (error) {
|
|
@@ -21811,7 +21975,7 @@ var ServiceCommandHandler = class {
|
|
|
21811
21975
|
removeRemoteConfig(server, remotePath) {
|
|
21812
21976
|
return new Promise((resolve, reject) => {
|
|
21813
21977
|
const target = `pmate@${server}`;
|
|
21814
|
-
const child = (0,
|
|
21978
|
+
const child = (0, import_node_child_process13.spawn)("ssh", [target, "sudo", "rm", "-f", remotePath], {
|
|
21815
21979
|
stdio: ["ignore", "inherit", "inherit"]
|
|
21816
21980
|
});
|
|
21817
21981
|
child.on("error", (error) => {
|
|
@@ -21829,7 +21993,7 @@ var ServiceCommandHandler = class {
|
|
|
21829
21993
|
reloadNginx(server) {
|
|
21830
21994
|
return new Promise((resolve, reject) => {
|
|
21831
21995
|
const target = `pmate@${server}`;
|
|
21832
|
-
const child = (0,
|
|
21996
|
+
const child = (0, import_node_child_process13.spawn)("ssh", [target, "sudo", "nginx", "-s", "reload"], {
|
|
21833
21997
|
stdio: ["ignore", "inherit", "inherit"]
|
|
21834
21998
|
});
|
|
21835
21999
|
child.on("error", (error) => {
|
|
@@ -21902,9 +22066,9 @@ function sleep3(ms) {
|
|
|
21902
22066
|
}
|
|
21903
22067
|
|
|
21904
22068
|
// src/commands/VercelCommandHandler.ts
|
|
21905
|
-
var
|
|
22069
|
+
var import_node_child_process14 = require("child_process");
|
|
21906
22070
|
var import_node_util = require("util");
|
|
21907
|
-
var execFileAsync = (0, import_node_util.promisify)(
|
|
22071
|
+
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process14.execFile);
|
|
21908
22072
|
function parseDeploymentTimestamp(deployment) {
|
|
21909
22073
|
const candidates = [deployment.createdAt, deployment.readyAt, deployment.ready];
|
|
21910
22074
|
for (const value of candidates) {
|
|
@@ -22095,9 +22259,9 @@ var RoleCommandHandler = class {
|
|
|
22095
22259
|
};
|
|
22096
22260
|
|
|
22097
22261
|
// src/commands/AccountCommandHandler.ts
|
|
22098
|
-
var
|
|
22099
|
-
var
|
|
22100
|
-
var
|
|
22262
|
+
var import_node_fs26 = require("fs");
|
|
22263
|
+
var import_node_crypto8 = require("crypto");
|
|
22264
|
+
var import_node_path31 = __toESM(require("path"));
|
|
22101
22265
|
init_sessionStore();
|
|
22102
22266
|
var AccountCommandHandler = class {
|
|
22103
22267
|
async handle(args) {
|
|
@@ -22105,9 +22269,9 @@ var AccountCommandHandler = class {
|
|
|
22105
22269
|
case "bot-keygen": {
|
|
22106
22270
|
const keyFile = this.generateBotKeyFile(args.app, args.name);
|
|
22107
22271
|
const outPath = args.out ? this.resolveFilePath(args.out) : this.resolveDefaultBotKeyPath(args.app, args.name);
|
|
22108
|
-
(0,
|
|
22109
|
-
(0,
|
|
22110
|
-
(0,
|
|
22272
|
+
(0, import_node_fs26.mkdirSync)(import_node_path31.default.dirname(outPath), { recursive: true });
|
|
22273
|
+
(0, import_node_fs26.writeFileSync)(outPath, JSON.stringify(keyFile, null, 2), { mode: 384 });
|
|
22274
|
+
(0, import_node_fs26.chmodSync)(outPath, 384);
|
|
22111
22275
|
if (!args.json) {
|
|
22112
22276
|
ui.success(`Bot key saved to ${outPath}`);
|
|
22113
22277
|
}
|
|
@@ -22667,10 +22831,10 @@ var AccountCommandHandler = class {
|
|
|
22667
22831
|
throw new Error("Missing visible apps input");
|
|
22668
22832
|
}
|
|
22669
22833
|
const absolutePath = this.resolveFilePath(args.filePath);
|
|
22670
|
-
if (!(0,
|
|
22834
|
+
if (!(0, import_node_fs26.existsSync)(absolutePath)) {
|
|
22671
22835
|
throw new Error(`JSON file not found: ${absolutePath}`);
|
|
22672
22836
|
}
|
|
22673
|
-
const raw = (0,
|
|
22837
|
+
const raw = (0, import_node_fs26.readFileSync)(absolutePath, "utf8");
|
|
22674
22838
|
let parsed;
|
|
22675
22839
|
try {
|
|
22676
22840
|
parsed = JSON.parse(raw);
|
|
@@ -22684,19 +22848,19 @@ var AccountCommandHandler = class {
|
|
|
22684
22848
|
return parsed;
|
|
22685
22849
|
}
|
|
22686
22850
|
resolveFilePath(filePath) {
|
|
22687
|
-
return
|
|
22851
|
+
return import_node_path31.default.resolve(process.cwd(), filePath);
|
|
22688
22852
|
}
|
|
22689
22853
|
resolveDefaultBotKeyPath(app, name) {
|
|
22690
|
-
return
|
|
22854
|
+
return import_node_path31.default.join(this.homeDir(), ".pmate", "bots", app, `${name}.bot-key.json`);
|
|
22691
22855
|
}
|
|
22692
22856
|
resolveDefaultAccessKeyPath(app, name) {
|
|
22693
|
-
return
|
|
22857
|
+
return import_node_path31.default.join(this.homeDir(), ".pmate", "access-keys", app, `${name}.access-key.json`);
|
|
22694
22858
|
}
|
|
22695
22859
|
homeDir() {
|
|
22696
22860
|
return process.env.HOME || process.env.USERPROFILE || process.cwd();
|
|
22697
22861
|
}
|
|
22698
22862
|
generateBotKeyFile(app, name) {
|
|
22699
|
-
const { publicKey, privateKey } = (0,
|
|
22863
|
+
const { publicKey, privateKey } = (0, import_node_crypto8.generateKeyPairSync)("ed25519");
|
|
22700
22864
|
return {
|
|
22701
22865
|
type: "pmate-bot-key",
|
|
22702
22866
|
version: 1,
|
|
@@ -22710,14 +22874,14 @@ var AccountCommandHandler = class {
|
|
|
22710
22874
|
}
|
|
22711
22875
|
renameDefaultBotKeyFile(app, name, newName) {
|
|
22712
22876
|
const oldPath = this.resolveDefaultBotKeyPath(app, name);
|
|
22713
|
-
if (!(0,
|
|
22877
|
+
if (!(0, import_node_fs26.existsSync)(oldPath)) {
|
|
22714
22878
|
return void 0;
|
|
22715
22879
|
}
|
|
22716
22880
|
const keyFile = this.readBotKeyFile(oldPath, app, name);
|
|
22717
22881
|
const newPath = this.resolveDefaultBotKeyPath(app, newName);
|
|
22718
|
-
(0,
|
|
22719
|
-
(0,
|
|
22720
|
-
(0,
|
|
22882
|
+
(0, import_node_fs26.mkdirSync)(import_node_path31.default.dirname(newPath), { recursive: true, mode: 448 });
|
|
22883
|
+
(0, import_node_fs26.writeFileSync)(newPath, JSON.stringify({ ...keyFile, name: newName }, null, 2));
|
|
22884
|
+
(0, import_node_fs26.chmodSync)(newPath, 384);
|
|
22721
22885
|
return newPath;
|
|
22722
22886
|
}
|
|
22723
22887
|
readBotKeyFile(filePath, expectedApp, expectedName) {
|
|
@@ -22725,10 +22889,10 @@ var AccountCommandHandler = class {
|
|
|
22725
22889
|
throw new Error("Missing bot key file");
|
|
22726
22890
|
}
|
|
22727
22891
|
const absolutePath = this.resolveFilePath(filePath);
|
|
22728
|
-
if (!(0,
|
|
22892
|
+
if (!(0, import_node_fs26.existsSync)(absolutePath)) {
|
|
22729
22893
|
throw new Error(`Bot key file not found: ${absolutePath}`);
|
|
22730
22894
|
}
|
|
22731
|
-
const parsed = JSON.parse((0,
|
|
22895
|
+
const parsed = JSON.parse((0, import_node_fs26.readFileSync)(absolutePath, "utf8"));
|
|
22732
22896
|
if (parsed.type !== "pmate-bot-key" || parsed.version !== 1 || parsed.algorithm !== "ed25519" || !parsed.publicKey || !parsed.privateKey) {
|
|
22733
22897
|
throw new Error(`Invalid PMate bot key file: ${absolutePath}`);
|
|
22734
22898
|
}
|
|
@@ -22745,10 +22909,10 @@ var AccountCommandHandler = class {
|
|
|
22745
22909
|
throw new Error("Missing access key file");
|
|
22746
22910
|
}
|
|
22747
22911
|
const absolutePath = this.resolveFilePath(filePath);
|
|
22748
|
-
if (!(0,
|
|
22912
|
+
if (!(0, import_node_fs26.existsSync)(absolutePath)) {
|
|
22749
22913
|
throw new Error(`Access key file not found: ${absolutePath}`);
|
|
22750
22914
|
}
|
|
22751
|
-
const parsed = JSON.parse((0,
|
|
22915
|
+
const parsed = JSON.parse((0, import_node_fs26.readFileSync)(absolutePath, "utf8"));
|
|
22752
22916
|
if (parsed.type !== "pmate-access-key" || parsed.version !== 1 || !parsed.id || !parsed.app || !parsed.secret || !parsed.expiresAt) {
|
|
22753
22917
|
throw new Error(`Invalid PMate access key file: ${absolutePath}`);
|
|
22754
22918
|
}
|
|
@@ -22770,9 +22934,9 @@ var AccountCommandHandler = class {
|
|
|
22770
22934
|
createdAt: result.key.createdAt
|
|
22771
22935
|
};
|
|
22772
22936
|
const outPath = out ? this.resolveFilePath(out) : this.resolveDefaultAccessKeyPath(result.key.app, result.key.name);
|
|
22773
|
-
(0,
|
|
22774
|
-
(0,
|
|
22775
|
-
(0,
|
|
22937
|
+
(0, import_node_fs26.mkdirSync)(import_node_path31.default.dirname(outPath), { recursive: true });
|
|
22938
|
+
(0, import_node_fs26.writeFileSync)(outPath, JSON.stringify(keyFile, null, 2), { mode: 384 });
|
|
22939
|
+
(0, import_node_fs26.chmodSync)(outPath, 384);
|
|
22776
22940
|
}
|
|
22777
22941
|
renderAccessKeyCreateResult(result, asJson, out) {
|
|
22778
22942
|
if (asJson) {
|
|
@@ -22818,12 +22982,12 @@ var AccountCommandHandler = class {
|
|
|
22818
22982
|
});
|
|
22819
22983
|
}
|
|
22820
22984
|
signBotChallenge(challenge, privateKey) {
|
|
22821
|
-
const key = (0,
|
|
22985
|
+
const key = (0, import_node_crypto8.createPrivateKey)({
|
|
22822
22986
|
key: Buffer.from(privateKey, "base64url"),
|
|
22823
22987
|
format: "der",
|
|
22824
22988
|
type: "pkcs8"
|
|
22825
22989
|
});
|
|
22826
|
-
return (0,
|
|
22990
|
+
return (0, import_node_crypto8.sign)(
|
|
22827
22991
|
null,
|
|
22828
22992
|
Buffer.from(JSON.stringify({
|
|
22829
22993
|
id: challenge.id,
|
|
@@ -22857,8 +23021,8 @@ function encodePathSegment2(value) {
|
|
|
22857
23021
|
}
|
|
22858
23022
|
|
|
22859
23023
|
// src/commands/OrgCommandHandler.ts
|
|
22860
|
-
var
|
|
22861
|
-
var
|
|
23024
|
+
var import_node_fs27 = require("fs");
|
|
23025
|
+
var import_node_path32 = __toESM(require("path"));
|
|
22862
23026
|
init_sessionStore();
|
|
22863
23027
|
var OrgCommandHandler = class {
|
|
22864
23028
|
async handle(args) {
|
|
@@ -23028,11 +23192,11 @@ var OrgCommandHandler = class {
|
|
|
23028
23192
|
if (!filePath) {
|
|
23029
23193
|
throw new Error(`Missing ${itemLabel} input`);
|
|
23030
23194
|
}
|
|
23031
|
-
const absolutePath =
|
|
23032
|
-
if (!(0,
|
|
23195
|
+
const absolutePath = import_node_path32.default.resolve(process.cwd(), filePath);
|
|
23196
|
+
if (!(0, import_node_fs27.existsSync)(absolutePath)) {
|
|
23033
23197
|
throw new Error(`JSON file not found: ${absolutePath}`);
|
|
23034
23198
|
}
|
|
23035
|
-
const raw = (0,
|
|
23199
|
+
const raw = (0, import_node_fs27.readFileSync)(absolutePath, "utf8");
|
|
23036
23200
|
let parsed;
|
|
23037
23201
|
try {
|
|
23038
23202
|
parsed = JSON.parse(raw);
|
|
@@ -23103,10 +23267,10 @@ function encodePathSegment3(value) {
|
|
|
23103
23267
|
}
|
|
23104
23268
|
|
|
23105
23269
|
// src/commands/HostCommandHandler.ts
|
|
23106
|
-
var
|
|
23107
|
-
var
|
|
23108
|
-
var
|
|
23109
|
-
var
|
|
23270
|
+
var import_node_fs28 = require("fs");
|
|
23271
|
+
var import_node_os14 = require("os");
|
|
23272
|
+
var import_node_path33 = __toESM(require("path"));
|
|
23273
|
+
var import_node_child_process15 = require("child_process");
|
|
23110
23274
|
var BLOCK_START = "# >>> pmate host env >>>";
|
|
23111
23275
|
var BLOCK_END = "# <<< pmate host env <<<";
|
|
23112
23276
|
var PROFILE_BLOCK = `${BLOCK_START}
|
|
@@ -23118,7 +23282,7 @@ fi
|
|
|
23118
23282
|
${BLOCK_END}`;
|
|
23119
23283
|
var HostCommandHandler = class {
|
|
23120
23284
|
async handle(args) {
|
|
23121
|
-
const hostId = normalizeHostId(args.host ?? process.env.PMATE_HOST_ID ?? (0,
|
|
23285
|
+
const hostId = normalizeHostId(args.host ?? process.env.PMATE_HOST_ID ?? (0, import_node_os14.hostname)());
|
|
23122
23286
|
const client = new ServiceRegistryClient(args.baseUrl);
|
|
23123
23287
|
if (args.action === "env-list") {
|
|
23124
23288
|
const items = await client.listHostEnv(hostId);
|
|
@@ -23149,7 +23313,7 @@ async function applyHostEnvRemote(host, values) {
|
|
|
23149
23313
|
const content = serializeHostEnv(values);
|
|
23150
23314
|
const script = buildRemoteApplyScript(Buffer.from(content, "utf8").toString("base64"));
|
|
23151
23315
|
await new Promise((resolve, reject) => {
|
|
23152
|
-
const child = (0,
|
|
23316
|
+
const child = (0, import_node_child_process15.spawn)("ssh", [target, "bash -s"], { stdio: ["pipe", "pipe", "pipe"] });
|
|
23153
23317
|
let stdout = "";
|
|
23154
23318
|
let stderr = "";
|
|
23155
23319
|
child.stdout.on("data", (chunk) => {
|
|
@@ -24194,6 +24358,7 @@ function parseServiceArgs(tokens) {
|
|
|
24194
24358
|
json
|
|
24195
24359
|
};
|
|
24196
24360
|
}
|
|
24361
|
+
if (action === "ios") return parseServiceIosArgs(rest);
|
|
24197
24362
|
if (action === "apk") {
|
|
24198
24363
|
return parseServiceApkArgs(rest, { baseUrl, json });
|
|
24199
24364
|
}
|
|
@@ -24965,6 +25130,48 @@ function parseServiceEnvArgs(appId, tokens) {
|
|
|
24965
25130
|
}
|
|
24966
25131
|
throw new Error(`Unknown service env action: ${action}`);
|
|
24967
25132
|
}
|
|
25133
|
+
function parseServiceIosArgs(tokens) {
|
|
25134
|
+
const rest = [...tokens];
|
|
25135
|
+
if (rest.shift() !== "build") throw new Error("Usage: service ios build <ns:service> --scheme <scheme>");
|
|
25136
|
+
const ref = rest.shift();
|
|
25137
|
+
if (!ref || ref.startsWith("-")) throw new Error("Missing iOS service ref");
|
|
25138
|
+
const parsed = parseNamespacedRef(ref, { entityLabel: "service ref" });
|
|
25139
|
+
const values = {};
|
|
25140
|
+
let json = false;
|
|
25141
|
+
const allowed = ["--scheme", "--project", "--workspace", "--team", "--cwd", "--last-build", "--base-url"];
|
|
25142
|
+
while (rest.length) {
|
|
25143
|
+
const token = rest.shift();
|
|
25144
|
+
if (token === "--json") {
|
|
25145
|
+
json = true;
|
|
25146
|
+
continue;
|
|
25147
|
+
}
|
|
25148
|
+
if (!allowed.includes(token)) throw new Error(`Unknown service ios build option: ${token}`);
|
|
25149
|
+
if (values[token] !== void 0) throw new Error(`Duplicate option: ${token}`);
|
|
25150
|
+
values[token] = requireValue4(rest, token);
|
|
25151
|
+
}
|
|
25152
|
+
if (!values["--scheme"]) throw new Error("An explicit --scheme is required");
|
|
25153
|
+
if (values["--project"] && values["--workspace"]) throw new Error("Use only one of --project or --workspace");
|
|
25154
|
+
let lastBuild;
|
|
25155
|
+
if (values["--last-build"] !== void 0) {
|
|
25156
|
+
const value = values["--last-build"];
|
|
25157
|
+
if (!/^(0|[1-9]\d*)$/.test(value) || !Number.isSafeInteger(Number(value))) throw new Error("--last-build must be a non-negative safe integer");
|
|
25158
|
+
lastBuild = Number(value);
|
|
25159
|
+
}
|
|
25160
|
+
return {
|
|
25161
|
+
type: "service" /* Service */,
|
|
25162
|
+
action: "ios-build",
|
|
25163
|
+
namespace: parsed.namespace,
|
|
25164
|
+
name: parsed.objectId,
|
|
25165
|
+
scheme: values["--scheme"],
|
|
25166
|
+
project: values["--project"],
|
|
25167
|
+
workspace: values["--workspace"],
|
|
25168
|
+
team: values["--team"],
|
|
25169
|
+
cwd: values["--cwd"],
|
|
25170
|
+
lastBuild,
|
|
25171
|
+
baseUrl: values["--base-url"],
|
|
25172
|
+
json
|
|
25173
|
+
};
|
|
25174
|
+
}
|
|
24968
25175
|
|
|
24969
25176
|
// src/logsCommandParser.ts
|
|
24970
25177
|
init_namespacedRef();
|
|
@@ -26433,7 +26640,7 @@ function requireValue11(tokens, option) {
|
|
|
26433
26640
|
|
|
26434
26641
|
// src/index.ts
|
|
26435
26642
|
var pkg = JSON.parse(
|
|
26436
|
-
(0,
|
|
26643
|
+
(0, import_node_fs29.readFileSync)(import_node_path34.default.resolve(__dirname, "..", "package.json"), "utf8")
|
|
26437
26644
|
);
|
|
26438
26645
|
var HELP_TEXT = [
|
|
26439
26646
|
import_chalk4.default.bold(`pmate v${pkg.version}`),
|
|
@@ -26598,6 +26805,7 @@ var HELP_TEXT = [
|
|
|
26598
26805
|
` ${import_chalk4.default.gray(" delete <ns:objectId> [--base-url <url>] [--json]")}`,
|
|
26599
26806
|
` ${import_chalk4.default.gray(" default base URL: https://apps-api.pmate.chat")}`,
|
|
26600
26807
|
` ${import_chalk4.default.cyan("service")} Manage service registry entries via apps-api`,
|
|
26808
|
+
" ios build <ns:service> --scheme <name> [--team <id>] [--last-build <n>] [--cwd <repo>] [--project <path>|--workspace <path>] [--json]",
|
|
26601
26809
|
` ${import_chalk4.default.gray(" list [--namespace <ns>] [--repo <owner/name|https://github.com/owner/name>] [--server <name>] [--base-url <url>] [--json]")}`,
|
|
26602
26810
|
` ${import_chalk4.default.gray(" get <ns:objectId> [--base-url <url>] [--json]")}`,
|
|
26603
26811
|
` ${import_chalk4.default.gray(" view <ns:objectId> [--base-url <url>]")}`,
|