percher 1.1.0 → 1.2.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/bin/percher.js +953 -641
- package/dist/package.json +3 -3
- package/dist/src/cli-utils.d.ts +1 -0
- package/dist/src/cli-utils.d.ts.map +1 -1
- package/dist/src/cli-utils.js +9 -0
- package/dist/src/cli-utils.js.map +1 -1
- package/dist/src/commands/deploys.d.ts.map +1 -1
- package/dist/src/commands/deploys.js +9 -1
- package/dist/src/commands/deploys.js.map +1 -1
- package/dist/src/commands/telemetry.d.ts +4 -0
- package/dist/src/commands/telemetry.d.ts.map +1 -0
- package/dist/src/commands/telemetry.js +27 -0
- package/dist/src/commands/telemetry.js.map +1 -0
- package/dist/src/index.d.ts +6 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +9 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/telemetry.d.ts +28 -3
- package/dist/src/telemetry.d.ts.map +1 -1
- package/dist/src/telemetry.js +107 -24
- package/dist/src/telemetry.js.map +1 -1
- package/dist/templates/astro/Dockerfile +10 -4
- package/dist/templates/capsule/Dockerfile +10 -4
- package/dist/templates/eleventy/Dockerfile +10 -4
- package/dist/templates/hugo/Dockerfile +10 -4
- package/dist/templates/preact/Dockerfile +10 -4
- package/dist/templates/solid/Dockerfile +10 -4
- package/dist/templates/vite-react/Dockerfile +10 -4
- package/dist/templates/vite-vue/Dockerfile +10 -4
- package/package.json +3 -3
package/dist/bin/percher.js
CHANGED
|
@@ -4751,7 +4751,7 @@ var init_build_env_prefixes = __esm(() => {
|
|
|
4751
4751
|
});
|
|
4752
4752
|
|
|
4753
4753
|
// ../client/dist/config.js
|
|
4754
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync4, renameSync, writeFileSync as writeFileSync4 } from "node:fs";
|
|
4754
|
+
import { chmodSync, closeSync, existsSync as existsSync4, fchmodSync, mkdirSync as mkdirSync4, openSync, readFileSync as readFileSync4, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
4755
4755
|
import { homedir } from "node:os";
|
|
4756
4756
|
import { dirname as dirname4, join as join6 } from "node:path";
|
|
4757
4757
|
function home() {
|
|
@@ -4800,12 +4800,32 @@ function readAnonClaims() {
|
|
|
4800
4800
|
}
|
|
4801
4801
|
function saveConfig(partial2) {
|
|
4802
4802
|
const p = configPath();
|
|
4803
|
-
mkdirSync4(dirname4(p), { recursive: true });
|
|
4803
|
+
mkdirSync4(dirname4(p), { recursive: true, mode: 448 });
|
|
4804
|
+
if (process.platform !== "win32") {
|
|
4805
|
+
chmodSync(dirname4(p), 448);
|
|
4806
|
+
if (existsSync4(p))
|
|
4807
|
+
chmodSync(p, 384);
|
|
4808
|
+
}
|
|
4804
4809
|
const current = readFile2();
|
|
4805
4810
|
const merged = { ...current, ...partial2 };
|
|
4806
4811
|
const tmp = `${p}.tmp-${process.pid}-${Date.now()}`;
|
|
4807
|
-
|
|
4808
|
-
|
|
4812
|
+
let fd;
|
|
4813
|
+
let created = false;
|
|
4814
|
+
try {
|
|
4815
|
+
fd = openSync(tmp, "wx", 384);
|
|
4816
|
+
created = true;
|
|
4817
|
+
if (process.platform !== "win32")
|
|
4818
|
+
fchmodSync(fd, 384);
|
|
4819
|
+
writeFileSync4(fd, JSON.stringify(merged, null, 2), "utf8");
|
|
4820
|
+
closeSync(fd);
|
|
4821
|
+
fd = undefined;
|
|
4822
|
+
renameSync(tmp, p);
|
|
4823
|
+
} finally {
|
|
4824
|
+
if (fd !== undefined)
|
|
4825
|
+
closeSync(fd);
|
|
4826
|
+
if (created)
|
|
4827
|
+
rmSync2(tmp, { force: true });
|
|
4828
|
+
}
|
|
4809
4829
|
}
|
|
4810
4830
|
function getAnonClaim(app) {
|
|
4811
4831
|
return readAnonClaims()[app] ?? null;
|
|
@@ -4830,7 +4850,7 @@ var package_default;
|
|
|
4830
4850
|
var init_package = __esm(() => {
|
|
4831
4851
|
package_default = {
|
|
4832
4852
|
name: "@percher/client",
|
|
4833
|
-
version: "1.
|
|
4853
|
+
version: "1.2.0",
|
|
4834
4854
|
description: "Official TypeScript SDK for Percher: deploy apps, manage env vars, stream logs, and automate hosting workflows.",
|
|
4835
4855
|
keywords: [
|
|
4836
4856
|
"percher",
|
|
@@ -5384,10 +5404,11 @@ class AppsResource {
|
|
|
5384
5404
|
deleteIconImage(appRef) {
|
|
5385
5405
|
return this.http.deleteJson(`/apps/${encodeURIComponent(appRef)}/icon/image`);
|
|
5386
5406
|
}
|
|
5387
|
-
uploadPreviewImage(appRef, bytes, mime) {
|
|
5407
|
+
uploadPreviewImage(appRef, bytes, mime, orientation) {
|
|
5388
5408
|
const ab = new ArrayBuffer(bytes.byteLength);
|
|
5389
5409
|
new Uint8Array(ab).set(bytes);
|
|
5390
|
-
|
|
5410
|
+
const query = orientation ? `?orientation=${orientation}` : "";
|
|
5411
|
+
return this.http.postBinary(`/apps/${encodeURIComponent(appRef)}/preview/image${query}`, new Blob([ab]), { "Content-Type": mime });
|
|
5391
5412
|
}
|
|
5392
5413
|
deletePreviewImage(appRef) {
|
|
5393
5414
|
return this.http.deleteJson(`/apps/${encodeURIComponent(appRef)}/preview/image`);
|
|
@@ -6262,6 +6283,55 @@ var init_env_file = __esm(() => {
|
|
|
6262
6283
|
};
|
|
6263
6284
|
});
|
|
6264
6285
|
|
|
6286
|
+
// ../client/dist/telemetry-consent.js
|
|
6287
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync5, readFileSync as readFileSync5, renameSync as renameSync2, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "node:fs";
|
|
6288
|
+
import { dirname as dirname5, join as join7 } from "node:path";
|
|
6289
|
+
function consentPath() {
|
|
6290
|
+
return join7(dirname5(configPath()), "telemetry.json");
|
|
6291
|
+
}
|
|
6292
|
+
function envOptOut() {
|
|
6293
|
+
if (process.env.PERCHER_NO_TELEMETRY === "1" || process.env.PERCHER_NO_TELEMETRY === "true") {
|
|
6294
|
+
return "PERCHER_NO_TELEMETRY";
|
|
6295
|
+
}
|
|
6296
|
+
if (process.env.DO_NOT_TRACK === "1")
|
|
6297
|
+
return "DO_NOT_TRACK";
|
|
6298
|
+
return null;
|
|
6299
|
+
}
|
|
6300
|
+
function readStored() {
|
|
6301
|
+
try {
|
|
6302
|
+
const parsed = JSON.parse(readFileSync5(consentPath(), "utf8"));
|
|
6303
|
+
if (typeof parsed?.enabled === "boolean" && typeof parsed.decidedAt === "string" && typeof parsed.version === "string") {
|
|
6304
|
+
return { enabled: parsed.enabled, decidedAt: parsed.decidedAt, version: parsed.version };
|
|
6305
|
+
}
|
|
6306
|
+
} catch {}
|
|
6307
|
+
return null;
|
|
6308
|
+
}
|
|
6309
|
+
function telemetryState() {
|
|
6310
|
+
const optOut = envOptOut();
|
|
6311
|
+
const stored = readStored();
|
|
6312
|
+
return { enabled: optOut === null && stored?.enabled === true, envOptOut: optOut, stored };
|
|
6313
|
+
}
|
|
6314
|
+
function saveTelemetryAnswer(enabled, version2) {
|
|
6315
|
+
const p = consentPath();
|
|
6316
|
+
mkdirSync5(dirname5(p), { recursive: true, mode: 448 });
|
|
6317
|
+
if (process.platform !== "win32")
|
|
6318
|
+
chmodSync2(dirname5(p), 448);
|
|
6319
|
+
const answer = { enabled, decidedAt: new Date().toISOString(), version: version2 };
|
|
6320
|
+
const tmp = `${p}.tmp-${process.pid}-${Date.now()}`;
|
|
6321
|
+
try {
|
|
6322
|
+
writeFileSync5(tmp, `${JSON.stringify(answer, null, 2)}
|
|
6323
|
+
`, { mode: 384, flag: "wx" });
|
|
6324
|
+
if (process.platform !== "win32")
|
|
6325
|
+
chmodSync2(tmp, 384);
|
|
6326
|
+
renameSync2(tmp, p);
|
|
6327
|
+
} finally {
|
|
6328
|
+
rmSync3(tmp, { force: true });
|
|
6329
|
+
}
|
|
6330
|
+
}
|
|
6331
|
+
var init_telemetry_consent = __esm(() => {
|
|
6332
|
+
init_config();
|
|
6333
|
+
});
|
|
6334
|
+
|
|
6265
6335
|
// ../client/dist/index.js
|
|
6266
6336
|
var init_dist = __esm(() => {
|
|
6267
6337
|
init_build_env_prefixes();
|
|
@@ -6272,6 +6342,7 @@ var init_dist = __esm(() => {
|
|
|
6272
6342
|
init_apps();
|
|
6273
6343
|
init_auth();
|
|
6274
6344
|
init_logs();
|
|
6345
|
+
init_telemetry_consent();
|
|
6275
6346
|
});
|
|
6276
6347
|
|
|
6277
6348
|
// ../../node_modules/.bun/ignore@7.0.5/node_modules/ignore/index.js
|
|
@@ -10582,7 +10653,7 @@ var require_util = __commonJS((exports) => {
|
|
|
10582
10653
|
return path;
|
|
10583
10654
|
}
|
|
10584
10655
|
exports.normalize = normalize2;
|
|
10585
|
-
function
|
|
10656
|
+
function join19(aRoot, aPath) {
|
|
10586
10657
|
if (aRoot === "") {
|
|
10587
10658
|
aRoot = ".";
|
|
10588
10659
|
}
|
|
@@ -10614,7 +10685,7 @@ var require_util = __commonJS((exports) => {
|
|
|
10614
10685
|
}
|
|
10615
10686
|
return joined;
|
|
10616
10687
|
}
|
|
10617
|
-
exports.join =
|
|
10688
|
+
exports.join = join19;
|
|
10618
10689
|
exports.isAbsolute = function(aPath) {
|
|
10619
10690
|
return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
|
|
10620
10691
|
};
|
|
@@ -10787,7 +10858,7 @@ var require_util = __commonJS((exports) => {
|
|
|
10787
10858
|
parsed.path = parsed.path.substring(0, index + 1);
|
|
10788
10859
|
}
|
|
10789
10860
|
}
|
|
10790
|
-
sourceURL =
|
|
10861
|
+
sourceURL = join19(urlGenerate(parsed), sourceURL);
|
|
10791
10862
|
}
|
|
10792
10863
|
return normalize2(sourceURL);
|
|
10793
10864
|
}
|
|
@@ -92667,13 +92738,13 @@ function iconColourHex(token) {
|
|
|
92667
92738
|
}
|
|
92668
92739
|
// ../shared/dist/legal.js
|
|
92669
92740
|
var LEGAL_DOCUMENT_VERSIONS = {
|
|
92670
|
-
terms: "1.0.
|
|
92741
|
+
terms: "1.0.3",
|
|
92671
92742
|
aup: "1.0.1",
|
|
92672
|
-
privacy: "1.0.
|
|
92673
|
-
cookies: "1.0.
|
|
92674
|
-
dpa: "1.
|
|
92675
|
-
subprocessors: "1.
|
|
92676
|
-
dataPortability: "1.0"
|
|
92743
|
+
privacy: "1.0.4",
|
|
92744
|
+
cookies: "1.0.4",
|
|
92745
|
+
dpa: "1.1",
|
|
92746
|
+
subprocessors: "1.2",
|
|
92747
|
+
dataPortability: "1.0.4"
|
|
92677
92748
|
};
|
|
92678
92749
|
var CURRENT_TERMS_VERSION = LEGAL_DOCUMENT_VERSIONS.terms;
|
|
92679
92750
|
// ../shared/dist/legal-changes.js
|
|
@@ -92796,6 +92867,7 @@ var SWITCHES = [
|
|
|
92796
92867
|
},
|
|
92797
92868
|
bool("PERCHER_ALLOW_NO_CAPTCHA", ["dashboard"], "allow production without Turnstile", ONE),
|
|
92798
92869
|
bool("PERCHER_ALLOW_UNVERIFIED_SIGNUP", ["dashboard"], "allow production without Resend", ONE),
|
|
92870
|
+
bool("PERCHER_DASHBOARD_SECRET_BUILD", ["dashboard"], "allow an ephemeral BetterAuth key during `next build` only, never at runtime", ONE),
|
|
92799
92871
|
bool("PERCHER_ALLOW_NO_ALERTING", ["api"], "boot without an alert channel", ONE),
|
|
92800
92872
|
{
|
|
92801
92873
|
kind: "presence",
|
|
@@ -106437,7 +106509,7 @@ var ENV_NAME_SHAPE = "UPPER_SNAKE_CASE, at most 256 characters";
|
|
|
106437
106509
|
var buildSchema = exports_external.object({
|
|
106438
106510
|
command: exports_external.string().optional(),
|
|
106439
106511
|
output: exports_external.string().refine((v) => !v.startsWith("/"), "must be a relative path (no leading slash)").optional(),
|
|
106440
|
-
pass_env: exports_external.array(exports_external.string().regex(ENV_NAME_RE2, `must be a valid env var name (${ENV_NAME_SHAPE})`).refine((name) => !name.startsWith("BUILDKIT_"), "BUILDKIT_*
|
|
106512
|
+
pass_env: exports_external.array(exports_external.string().regex(ENV_NAME_RE2, `must be a valid env var name (${ENV_NAME_SHAPE})`).refine((name) => !name.startsWith("BUILDKIT_") && name !== "PERCHER_BUILD_SECRET_DIGEST", "BUILDKIT_* and PERCHER_BUILD_SECRET_DIGEST are reserved by Percher and cannot be passed to the build")).max(50, "at most 50 build env vars can be exposed").optional()
|
|
106441
106513
|
}).strict();
|
|
106442
106514
|
var webSchema = exports_external.object({
|
|
106443
106515
|
port: exports_external.number().int().min(1024).max(65535),
|
|
@@ -107774,20 +107846,32 @@ function manifestStarter({ name, themeColorHex }) {
|
|
|
107774
107846
|
theme_color: themeColorHex
|
|
107775
107847
|
}, null, 2);
|
|
107776
107848
|
}
|
|
107777
|
-
function serviceWorkerStarter() {
|
|
107778
|
-
|
|
107779
|
-
|
|
107780
|
-
|
|
107849
|
+
function serviceWorkerStarter(assets = []) {
|
|
107850
|
+
if (assets.some((path) => !path.startsWith("/") || path.startsWith("//") || path.includes("\\"))) {
|
|
107851
|
+
throw new Error("Offline assets must be same-origin absolute paths");
|
|
107852
|
+
}
|
|
107853
|
+
return `// sw.js — public offline shell. Serve from /sw.js.
|
|
107854
|
+
// Add public assets, e.g. ["/offline.html", "/app.js", "/app.css"]. Never add private pages or API data.
|
|
107855
|
+
// Include "/" only if the page is public and identical for signed-in and anonymous visitors.
|
|
107856
|
+
// Bump CACHE whenever the asset list or its content changes. Test an offline reload.
|
|
107857
|
+
const CACHE = "percher-public-shell-v1";
|
|
107858
|
+
const SHELL = ${JSON.stringify(assets)};
|
|
107781
107859
|
|
|
107782
107860
|
self.addEventListener("install", (event) => {
|
|
107783
|
-
event.waitUntil(caches.open(CACHE).then((cache) =>
|
|
107861
|
+
event.waitUntil(caches.open(CACHE).then((cache) => Promise.all(SHELL.map(async (path) => {
|
|
107862
|
+
const response = await fetch(path, { credentials: "omit", redirect: "error" });
|
|
107863
|
+
if (!response.ok || /private|no-store/i.test(response.headers.get("cache-control") || "")) {
|
|
107864
|
+
throw new Error("Offline asset is unavailable or private: " + path);
|
|
107865
|
+
}
|
|
107866
|
+
await cache.put(path, response);
|
|
107867
|
+
}))));
|
|
107784
107868
|
self.skipWaiting();
|
|
107785
107869
|
});
|
|
107786
107870
|
|
|
107787
107871
|
self.addEventListener("activate", (event) => {
|
|
107788
107872
|
event.waitUntil(
|
|
107789
107873
|
caches.keys().then((keys) =>
|
|
107790
|
-
Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k))),
|
|
107874
|
+
Promise.all(keys.filter((k) => k.startsWith("percher-public-shell-") && k !== CACHE).map((k) => caches.delete(k))),
|
|
107791
107875
|
),
|
|
107792
107876
|
);
|
|
107793
107877
|
self.clients.claim();
|
|
@@ -107795,8 +107879,10 @@ self.addEventListener("activate", (event) => {
|
|
|
107795
107879
|
|
|
107796
107880
|
self.addEventListener("fetch", (event) => {
|
|
107797
107881
|
if (event.request.method !== "GET") return;
|
|
107882
|
+
const url = new URL(event.request.url);
|
|
107883
|
+
if (url.origin !== self.location.origin || !SHELL.includes(url.pathname + url.search)) return;
|
|
107798
107884
|
event.respondWith(
|
|
107799
|
-
caches.match(event.request).then((cached) => cached || fetch(event.request)),
|
|
107885
|
+
caches.open(CACHE).then((cache) => cache.match(event.request)).then((cached) => cached || fetch(event.request)),
|
|
107800
107886
|
);
|
|
107801
107887
|
});`;
|
|
107802
107888
|
}
|
|
@@ -107965,17 +108051,17 @@ async function claim(ctx, input = {}) {
|
|
|
107965
108051
|
};
|
|
107966
108052
|
}
|
|
107967
108053
|
// ../core/dist/commands/create.js
|
|
107968
|
-
import { join as
|
|
108054
|
+
import { join as join9, resolve as resolve5 } from "node:path";
|
|
107969
108055
|
|
|
107970
108056
|
// ../core/dist/templates.js
|
|
107971
|
-
import { existsSync as existsSync5, mkdirSync as
|
|
107972
|
-
import { basename as basename2, join as
|
|
107973
|
-
var __dirname = "
|
|
108057
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync6, readdirSync, readFileSync as readFileSync6, statSync, writeFileSync as writeFileSync6 } from "node:fs";
|
|
108058
|
+
import { basename as basename2, join as join8, normalize, relative as relative2, resolve as resolve4 } from "node:path";
|
|
108059
|
+
var __dirname = "D:\\dev\\Percher-codex-clone\\packages\\core\\dist";
|
|
107974
108060
|
var TEMPLATES_DIR = resolve4(import.meta.dirname ?? __dirname, "../templates");
|
|
107975
108061
|
var PLACEHOLDER = "__APP_NAME__";
|
|
107976
108062
|
function listTemplates() {
|
|
107977
108063
|
return readdirSync(TEMPLATES_DIR).filter((name) => {
|
|
107978
|
-
const full =
|
|
108064
|
+
const full = join8(TEMPLATES_DIR, name);
|
|
107979
108065
|
return statSync(full).isDirectory();
|
|
107980
108066
|
});
|
|
107981
108067
|
}
|
|
@@ -107992,13 +108078,13 @@ function scaffoldTemplate(input) {
|
|
|
107992
108078
|
throw new Error(`Target directory "${absTarget}" is not empty. Use --force to overwrite.`);
|
|
107993
108079
|
}
|
|
107994
108080
|
}
|
|
107995
|
-
const srcDir =
|
|
108081
|
+
const srcDir = join8(TEMPLATES_DIR, template);
|
|
107996
108082
|
const files = [];
|
|
107997
108083
|
function copyDir(src, dest) {
|
|
107998
|
-
|
|
108084
|
+
mkdirSync6(dest, { recursive: true });
|
|
107999
108085
|
for (const entry of readdirSync(src)) {
|
|
108000
|
-
const srcPath =
|
|
108001
|
-
const destPath =
|
|
108086
|
+
const srcPath = join8(src, entry);
|
|
108087
|
+
const destPath = join8(dest, entry);
|
|
108002
108088
|
const rel = relative2(absTarget, normalize(destPath));
|
|
108003
108089
|
if (rel.startsWith("..") || resolve4(destPath) === resolve4(absTarget, "..", basename2(absTarget))) {
|
|
108004
108090
|
throw new Error(`Template contains path traversal: ${entry}`);
|
|
@@ -108009,11 +108095,11 @@ function scaffoldTemplate(input) {
|
|
|
108009
108095
|
if (existsSync5(destPath) && !force) {
|
|
108010
108096
|
continue;
|
|
108011
108097
|
}
|
|
108012
|
-
let content =
|
|
108098
|
+
let content = readFileSync6(srcPath, "utf-8");
|
|
108013
108099
|
content = content.replaceAll(PLACEHOLDER, appName);
|
|
108014
108100
|
content = content.replace(/\r\n/g, `
|
|
108015
108101
|
`);
|
|
108016
|
-
|
|
108102
|
+
writeFileSync6(destPath, content, "utf-8");
|
|
108017
108103
|
files.push(relative2(absTarget, destPath).replace(/\\/g, "/"));
|
|
108018
108104
|
}
|
|
108019
108105
|
}
|
|
@@ -108046,7 +108132,7 @@ async function create(ctx, input) {
|
|
|
108046
108132
|
});
|
|
108047
108133
|
}
|
|
108048
108134
|
} else {
|
|
108049
|
-
targetDir =
|
|
108135
|
+
targetDir = join9(ctx.cwd, input.name);
|
|
108050
108136
|
}
|
|
108051
108137
|
try {
|
|
108052
108138
|
const result = scaffoldTemplate({
|
|
@@ -108101,6 +108187,144 @@ async function dashboard(ctx, input = {}) {
|
|
|
108101
108187
|
await ctx.openUrl(url2);
|
|
108102
108188
|
return url2;
|
|
108103
108189
|
}
|
|
108190
|
+
// ../core/dist/untrusted.js
|
|
108191
|
+
import { relative as relative3 } from "node:path";
|
|
108192
|
+
function boundedSeconds(value, maxSeconds = 86400) {
|
|
108193
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
108194
|
+
return null;
|
|
108195
|
+
if (value <= 0 || value > maxSeconds)
|
|
108196
|
+
return null;
|
|
108197
|
+
return Math.ceil(value);
|
|
108198
|
+
}
|
|
108199
|
+
function boundedCount(value, max = 1e6) {
|
|
108200
|
+
if (typeof value !== "number" || !Number.isInteger(value))
|
|
108201
|
+
return null;
|
|
108202
|
+
if (value < 0 || value > max)
|
|
108203
|
+
return null;
|
|
108204
|
+
return value;
|
|
108205
|
+
}
|
|
108206
|
+
var ISO_INSTANT = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?(?:Z|([+-])(\d{2}):(\d{2}))$/;
|
|
108207
|
+
function daysInMonth(year, month) {
|
|
108208
|
+
return new Date(Date.UTC(year, month, 0)).getUTCDate();
|
|
108209
|
+
}
|
|
108210
|
+
function isoInstant(value, opts = {}) {
|
|
108211
|
+
if (typeof value !== "string")
|
|
108212
|
+
return null;
|
|
108213
|
+
const match = ISO_INSTANT.exec(value);
|
|
108214
|
+
if (!match)
|
|
108215
|
+
return null;
|
|
108216
|
+
const [, y, mo, d, h, mi, s, , offH, offM] = match;
|
|
108217
|
+
const year = Number(y);
|
|
108218
|
+
const month = Number(mo);
|
|
108219
|
+
const day = Number(d);
|
|
108220
|
+
if (month < 1 || month > 12)
|
|
108221
|
+
return null;
|
|
108222
|
+
if (day < 1 || day > daysInMonth(year, month))
|
|
108223
|
+
return null;
|
|
108224
|
+
if (Number(h) > 23 || Number(mi) > 59)
|
|
108225
|
+
return null;
|
|
108226
|
+
if (s !== undefined && Number(s) > 59)
|
|
108227
|
+
return null;
|
|
108228
|
+
if (offH !== undefined && (Number(offH) > 14 || Number(offM) > 59))
|
|
108229
|
+
return null;
|
|
108230
|
+
const ms = new Date(value).getTime();
|
|
108231
|
+
if (Number.isNaN(ms))
|
|
108232
|
+
return null;
|
|
108233
|
+
const now = (opts.now ?? Date.now)();
|
|
108234
|
+
const delta = ms - now;
|
|
108235
|
+
if (delta < -(opts.maxPastMs ?? 60 * 60000))
|
|
108236
|
+
return null;
|
|
108237
|
+
if (delta > (opts.maxFutureMs ?? 48 * 60 * 60000))
|
|
108238
|
+
return null;
|
|
108239
|
+
return value;
|
|
108240
|
+
}
|
|
108241
|
+
var ERROR_CODE = /^[A-Z][A-Z0-9_]{0,63}$/;
|
|
108242
|
+
function errorCode(value) {
|
|
108243
|
+
return typeof value === "string" && ERROR_CODE.test(value) ? value : null;
|
|
108244
|
+
}
|
|
108245
|
+
function quotaKind(value) {
|
|
108246
|
+
return value === "live" || value === "preview" ? value : null;
|
|
108247
|
+
}
|
|
108248
|
+
function isEnvKey(value) {
|
|
108249
|
+
return typeof value === "string" && /^[A-Za-z_][A-Za-z0-9_]{0,127}$/.test(value);
|
|
108250
|
+
}
|
|
108251
|
+
function envKeyList(value, max = 50) {
|
|
108252
|
+
if (!Array.isArray(value))
|
|
108253
|
+
return [];
|
|
108254
|
+
return [...new Set(value.filter(isEnvKey))].slice(0, max);
|
|
108255
|
+
}
|
|
108256
|
+
var MINTED_ID = /^[a-z0-9][a-z0-9_-]{0,39}$/;
|
|
108257
|
+
function idToken(value) {
|
|
108258
|
+
return typeof value === "string" && MINTED_ID.test(value) ? value : null;
|
|
108259
|
+
}
|
|
108260
|
+
function appRef(value) {
|
|
108261
|
+
return idToken(value);
|
|
108262
|
+
}
|
|
108263
|
+
var CONFIG_KEY_SEGMENT = /^[A-Za-z0-9_][A-Za-z0-9_-]{0,63}$/;
|
|
108264
|
+
function configKeyPath(segments, maxDepth = 8) {
|
|
108265
|
+
if (!Array.isArray(segments) || segments.length === 0)
|
|
108266
|
+
return null;
|
|
108267
|
+
const parts = [];
|
|
108268
|
+
for (const segment of segments.slice(0, maxDepth)) {
|
|
108269
|
+
if (typeof segment === "number") {
|
|
108270
|
+
if (!Number.isInteger(segment) || segment < 0 || segment > 9999)
|
|
108271
|
+
break;
|
|
108272
|
+
parts.push(String(segment));
|
|
108273
|
+
continue;
|
|
108274
|
+
}
|
|
108275
|
+
if (typeof segment !== "string" || !CONFIG_KEY_SEGMENT.test(segment))
|
|
108276
|
+
break;
|
|
108277
|
+
parts.push(segment);
|
|
108278
|
+
}
|
|
108279
|
+
return parts.length > 0 ? parts.join(".") : null;
|
|
108280
|
+
}
|
|
108281
|
+
function boundedLine(value, max = 1e7) {
|
|
108282
|
+
if (typeof value !== "number" || !Number.isInteger(value))
|
|
108283
|
+
return null;
|
|
108284
|
+
if (value < 1 || value > max)
|
|
108285
|
+
return null;
|
|
108286
|
+
return value;
|
|
108287
|
+
}
|
|
108288
|
+
function parseRemoteEnvProblems(value, max = 50) {
|
|
108289
|
+
if (!Array.isArray(value))
|
|
108290
|
+
return [];
|
|
108291
|
+
const out = [];
|
|
108292
|
+
const scanLimit = Math.min(value.length, max * 20);
|
|
108293
|
+
for (let i = 0;i < scanLimit; i++) {
|
|
108294
|
+
const entry = value[i];
|
|
108295
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry))
|
|
108296
|
+
continue;
|
|
108297
|
+
const { key, file: file2, line } = entry;
|
|
108298
|
+
if (!isEnvKey(key) || !isSafeRelativePath(file2))
|
|
108299
|
+
continue;
|
|
108300
|
+
const parsedLine = boundedLine(line);
|
|
108301
|
+
out.push({ key, file: file2, ...parsedLine !== null ? { line: parsedLine } : {} });
|
|
108302
|
+
if (out.length >= max)
|
|
108303
|
+
break;
|
|
108304
|
+
}
|
|
108305
|
+
return out;
|
|
108306
|
+
}
|
|
108307
|
+
var PATH_SEGMENT = /^[A-Za-z0-9._@+ ()'[\]-]+$/;
|
|
108308
|
+
var RESERVED_DEVICE_NAME = /^(?:CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(?:\.|$)/i;
|
|
108309
|
+
function isSafeRelativePath(value) {
|
|
108310
|
+
if (typeof value !== "string" || value.length === 0 || value.length > 1024)
|
|
108311
|
+
return false;
|
|
108312
|
+
if (value.includes("\x00"))
|
|
108313
|
+
return false;
|
|
108314
|
+
const segments = value.replace(/\\/g, "/").split("/");
|
|
108315
|
+
return segments.every((s) => {
|
|
108316
|
+
if (s === "." || s === ".." || !PATH_SEGMENT.test(s))
|
|
108317
|
+
return false;
|
|
108318
|
+
if (/[ .]$/.test(s))
|
|
108319
|
+
return false;
|
|
108320
|
+
return !RESERVED_DEVICE_NAME.test(s);
|
|
108321
|
+
});
|
|
108322
|
+
}
|
|
108323
|
+
function projectRelative(cwd, abs) {
|
|
108324
|
+
const rel = relative3(cwd, abs);
|
|
108325
|
+
return isSafeRelativePath(rel) ? rel : null;
|
|
108326
|
+
}
|
|
108327
|
+
|
|
108104
108328
|
// ../core/dist/commands/data.js
|
|
108105
108329
|
var dataInputSchema = exports_external.object({
|
|
108106
108330
|
app: exports_external.string().optional(),
|
|
@@ -108126,7 +108350,9 @@ async function data(ctx, input = {}) {
|
|
|
108126
108350
|
} catch (err) {
|
|
108127
108351
|
const code = err.code;
|
|
108128
108352
|
if (code === "PB_AUTH_FAILED") {
|
|
108129
|
-
|
|
108353
|
+
const ref = appRef(name) ?? "<app>";
|
|
108354
|
+
const verdict = err.extra?.verdict;
|
|
108355
|
+
warning = verdict === "rejected" ? `Percher has no working PocketBase admin credentials for this app; collections and counts are unavailable. The app owner can issue new ones with \`percher data reset-superuser ${ref}\`.` : `PocketBase authentication failed; collections and counts are unavailable. Retry \`percher data ${ref}\`; if this continues, contact support.`;
|
|
108130
108356
|
} else {
|
|
108131
108357
|
throw err;
|
|
108132
108358
|
}
|
|
@@ -108253,7 +108479,7 @@ function isTransientPollError(err) {
|
|
|
108253
108479
|
if (isTransientNetworkError(err))
|
|
108254
108480
|
return true;
|
|
108255
108481
|
if (err instanceof PercherApiError)
|
|
108256
|
-
return err.status === 502 || err.status === 503 || err.status === 504;
|
|
108482
|
+
return err.status === 429 || err.status === 502 || err.status === 503 || err.status === 504;
|
|
108257
108483
|
return err instanceof TypeError && /terminated/i.test(err.message);
|
|
108258
108484
|
}
|
|
108259
108485
|
async function pollDeployment(opts) {
|
|
@@ -108287,7 +108513,7 @@ async function pollDeployment(opts) {
|
|
|
108287
108513
|
throw err;
|
|
108288
108514
|
if (attempt === 1) {
|
|
108289
108515
|
streakStartedAt = Date.now();
|
|
108290
|
-
opts.ctx.status("Lost contact with Percher; retrying while the deploy continues...");
|
|
108516
|
+
opts.ctx.status(err instanceof PercherApiError && err.status === 429 ? "Percher asked this client to slow down; retrying while the deploy continues..." : "Lost contact with Percher; retrying while the deploy continues...");
|
|
108291
108517
|
}
|
|
108292
108518
|
if (Date.now() - startedAt > timeoutMs)
|
|
108293
108519
|
opts.onTimeout(deployment);
|
|
@@ -108321,143 +108547,204 @@ async function drainNewEvents(opts, deployId, seenEventIds) {
|
|
|
108321
108547
|
} catch {}
|
|
108322
108548
|
}
|
|
108323
108549
|
|
|
108324
|
-
// ../core/dist/
|
|
108325
|
-
|
|
108326
|
-
|
|
108327
|
-
|
|
108328
|
-
|
|
108329
|
-
|
|
108330
|
-
|
|
108331
|
-
|
|
108332
|
-
|
|
108333
|
-
|
|
108334
|
-
|
|
108335
|
-
|
|
108336
|
-
|
|
108337
|
-
|
|
108338
|
-
|
|
108339
|
-
|
|
108340
|
-
|
|
108341
|
-
|
|
108342
|
-
|
|
108343
|
-
|
|
108344
|
-
|
|
108345
|
-
|
|
108346
|
-
|
|
108347
|
-
|
|
108348
|
-
|
|
108349
|
-
|
|
108350
|
-
|
|
108351
|
-
|
|
108352
|
-
|
|
108353
|
-
|
|
108354
|
-
|
|
108355
|
-
|
|
108356
|
-
|
|
108357
|
-
|
|
108358
|
-
|
|
108359
|
-
|
|
108360
|
-
|
|
108361
|
-
|
|
108362
|
-
|
|
108363
|
-
|
|
108364
|
-
|
|
108365
|
-
|
|
108366
|
-
|
|
108367
|
-
|
|
108368
|
-
|
|
108369
|
-
|
|
108370
|
-
|
|
108371
|
-
|
|
108372
|
-
|
|
108373
|
-
|
|
108374
|
-
|
|
108375
|
-
|
|
108376
|
-
|
|
108377
|
-
|
|
108378
|
-
|
|
108379
|
-
|
|
108380
|
-
|
|
108381
|
-
}
|
|
108382
|
-
|
|
108383
|
-
|
|
108384
|
-
|
|
108385
|
-
|
|
108386
|
-
|
|
108387
|
-
|
|
108388
|
-
|
|
108389
|
-
}
|
|
108390
|
-
|
|
108391
|
-
|
|
108392
|
-
|
|
108393
|
-
|
|
108394
|
-
|
|
108395
|
-
|
|
108396
|
-
|
|
108397
|
-
|
|
108398
|
-
|
|
108399
|
-
|
|
108400
|
-
|
|
108401
|
-
|
|
108402
|
-
|
|
108403
|
-
|
|
108404
|
-
|
|
108405
|
-
|
|
108406
|
-
|
|
108407
|
-
|
|
108408
|
-
|
|
108409
|
-
|
|
108410
|
-
|
|
108411
|
-
|
|
108550
|
+
// ../core/dist/structured-error-codes.js
|
|
108551
|
+
var MAPPINGS = {
|
|
108552
|
+
PB_PROVISION_FAILED: {
|
|
108553
|
+
errorClass: "pb_provision_failed",
|
|
108554
|
+
phase: "infra",
|
|
108555
|
+
cause: "pb_provision_failed",
|
|
108556
|
+
title: "Database admin setup failed",
|
|
108557
|
+
explanation: () => "The platform could not set up the database admin account.",
|
|
108558
|
+
suggestion: "Publishing again after the platform failure is resolved is safe."
|
|
108559
|
+
},
|
|
108560
|
+
BUILD_SECRET_KEY_UNAVAILABLE: {
|
|
108561
|
+
errorClass: "build_secrets_unavailable",
|
|
108562
|
+
phase: "infra",
|
|
108563
|
+
cause: "build_secret_key_unavailable",
|
|
108564
|
+
title: "Percher could not protect build secrets",
|
|
108565
|
+
explanation: () => "Percher's key for build secrets is unavailable, so it refused the build rather than run it without them. This is a platform issue and does not count against your app.",
|
|
108566
|
+
suggestion: "Contact support. Removing the non-public keys from [build] pass_env lets the build run without them."
|
|
108567
|
+
},
|
|
108568
|
+
BUILD_SECRETS_UNSUPPORTED_NIXPACKS: {
|
|
108569
|
+
errorClass: "build_secrets_unavailable",
|
|
108570
|
+
phase: "infra",
|
|
108571
|
+
cause: "build_secrets_unsupported_nixpacks",
|
|
108572
|
+
title: "This builder cannot mount build secrets",
|
|
108573
|
+
explanation: () => "Percher is building this app with its fallback builder, which cannot keep build secrets out of the image, so it refused the build. It does not count against your app.",
|
|
108574
|
+
suggestion: "Remove the non-public keys from [build] pass_env, or deploy again later."
|
|
108575
|
+
},
|
|
108576
|
+
SHARED_NETWORK_FORBIDDEN: {
|
|
108577
|
+
errorClass: "infra_unavailable",
|
|
108578
|
+
phase: "infra",
|
|
108579
|
+
cause: "shared_network_forbidden",
|
|
108580
|
+
title: "Percher refused unsafe network placement",
|
|
108581
|
+
explanation: () => "This app is still on Percher's shared bridge. Percher refused to start another container there. This is a platform issue and does not count against your app.",
|
|
108582
|
+
suggestion: "Contact support. A Percher operator must migrate the app to its own network before you retry."
|
|
108583
|
+
},
|
|
108584
|
+
PLATFORM_DISK_LOW: {
|
|
108585
|
+
errorClass: "platform_at_capacity",
|
|
108586
|
+
phase: "infra",
|
|
108587
|
+
cause: "disk_headroom_exhausted",
|
|
108588
|
+
title: "Platform storage is too low",
|
|
108589
|
+
explanation: () => "Percher paused new deploys: the platform is nearly out of disk. Not your app — nothing to change. Deployed apps keep running; only new deploys are paused.",
|
|
108590
|
+
suggestion: "The operator is alerted automatically. Run `percher publish` again in ~15 min; this failure does not count against your app. Still failing after an hour: https://percher.app/status"
|
|
108591
|
+
},
|
|
108592
|
+
PLATFORM_DISK_UNKNOWN: {
|
|
108593
|
+
errorClass: "platform_at_capacity",
|
|
108594
|
+
phase: "infra",
|
|
108595
|
+
cause: "disk_state_unreadable",
|
|
108596
|
+
title: "Platform storage state is unknown",
|
|
108597
|
+
explanation: () => "Percher paused new deploys: it can't read how much disk is left, and won't assume there's room. Not your app. Deployed apps have not been stopped by this.",
|
|
108598
|
+
suggestion: "Run `percher publish` again in ~5 min; this failure does not count against your app. Still failing after 15 min: https://percher.app/status"
|
|
108599
|
+
},
|
|
108600
|
+
PLAN_POOL_EXCEEDED: {
|
|
108601
|
+
errorClass: "plan_pool_exceeded",
|
|
108602
|
+
phase: "start",
|
|
108603
|
+
cause: "plan_pool_exceeded",
|
|
108604
|
+
title: "Plan resource pool is full",
|
|
108605
|
+
explanation: (raw) => raw ? `${trimMessage(raw)} The platform is fine — the account is at its plan's limit.` : "This deploy would commit more RAM, vCPU or disk than your plan's pool allows across all your apps. The platform is fine — the account is at its limit.",
|
|
108606
|
+
suggestion: "Reduce `[resources]` in this app's percher.toml, delete an app you no longer need, or upgrade at https://percher.app/account/billing. Retrying unchanged will fail the same way."
|
|
108607
|
+
},
|
|
108608
|
+
PLACEMENT_UNDETERMINED: {
|
|
108609
|
+
errorClass: "infra_unavailable",
|
|
108610
|
+
phase: "infra",
|
|
108611
|
+
cause: "placement_undetermined",
|
|
108612
|
+
title: "Percher could not place your app's container",
|
|
108613
|
+
explanation: (raw) => raw ? `${trimMessage(raw)} This is a platform-side read of Percher's own container placement, not anything in your app.` : "Percher could not read which internal network this app's current containers are on, and refused to guess rather than risk splitting the app from its database across two networks.",
|
|
108614
|
+
suggestion: "Nothing to change in your app. Retry in a minute; if it persists, the platform is having trouble reaching its container daemon and Percher's operators are alerted."
|
|
108615
|
+
},
|
|
108616
|
+
PLATFORM_MEMORY_LOW: {
|
|
108617
|
+
errorClass: "platform_at_capacity",
|
|
108618
|
+
phase: "infra",
|
|
108619
|
+
cause: "box_admission_rejected",
|
|
108620
|
+
title: "Platform is at capacity",
|
|
108621
|
+
explanation: (raw) => raw ? `Percher couldn't fit this deploy on the box right now: ${trimMessage(raw)} Not your app — nothing to change. Deployed apps keep running.` : "Percher couldn't fit this deploy on the box right now — the admission gate refused to start the container. Not your app — nothing to change. Deployed apps keep running.",
|
|
108622
|
+
suggestion: "Run `percher publish` again in a few minutes; capacity frees up as other deploys finish, and this failure does not count against your app. Still failing after an hour: https://percher.app/status"
|
|
108623
|
+
},
|
|
108624
|
+
PB_BRIDGE_UNAVAILABLE: {
|
|
108625
|
+
errorClass: "infra_unavailable",
|
|
108626
|
+
phase: "infra",
|
|
108627
|
+
cause: "pb_bridge_unreachable",
|
|
108628
|
+
title: "PocketBase is unreachable from the platform",
|
|
108629
|
+
explanation: (raw) => raw ? `Percher could not reach your app's database through its internal bridge: ${trimMessage(raw)}. This is a platform-side issue — your data and your code are both fine.` : "Percher could not reach your app's database through its internal bridge. This is a platform-side issue — your data and your code are both fine.",
|
|
108630
|
+
suggestion: "Wait a minute and run `percher publish` again; this failure does not count against your app. Your data was not touched — the platform could not read it, which is not the same as it being missing. Still failing after 15 min: https://percher.app/status"
|
|
108631
|
+
},
|
|
108632
|
+
PB_ROTATION_UNVERIFIED: {
|
|
108633
|
+
errorClass: "infra_unavailable",
|
|
108634
|
+
phase: "infra",
|
|
108635
|
+
cause: "pb_rotation_unverified",
|
|
108636
|
+
title: "PocketBase admin password was rotated but not confirmed",
|
|
108637
|
+
explanation: (raw) => raw ? `${trimMessage(raw)} The rotation itself ran, so the previously stored admin password may now be stale. Your app data was not modified — only the admin credential changed.` : "Percher rotated your app's PocketBase admin password, then could not confirm the rotation. The previously stored admin password may now be stale. Your app data was not modified — only the admin credential changed.",
|
|
108638
|
+
suggestion: "Run the superuser reset again (`percher data reset-superuser` or the dashboard's reset button) — the rotation is idempotent, and a successful re-run stores a fresh working credential. This failure does not count against your app."
|
|
108639
|
+
},
|
|
108640
|
+
PB_OPERATION_BUSY: {
|
|
108641
|
+
errorClass: "pb_operation_busy",
|
|
108642
|
+
phase: "start",
|
|
108643
|
+
cause: "pb_backup_restore_busy",
|
|
108644
|
+
title: "PocketBase is finishing another data operation",
|
|
108645
|
+
explanation: (raw) => raw ? `${trimMessage(raw)} The operation is scoped to this app and must finish before Percher can create the deploy checkpoint.` : "PocketBase is already creating a backup or restoring data for this app, so Percher could not create the deploy checkpoint yet.",
|
|
108646
|
+
suggestion: "Wait for the current backup, export or restore to finish, then publish again. If no operation is visible after a few minutes, contact support before retrying repeatedly."
|
|
108647
|
+
},
|
|
108648
|
+
CANARY_FAILED: {
|
|
108649
|
+
errorClass: "runtime_crash",
|
|
108650
|
+
phase: "start",
|
|
108651
|
+
cause: "canary_failed",
|
|
108652
|
+
title: "Your app went live, then failed its post-deploy check",
|
|
108653
|
+
explanation: (raw) => raw ? `${trimMessage(raw)} The container started and passed its health check, so this is something that only shows up once the app is serving real requests.` : "Percher swapped your new version in, then its own public URL did not answer correctly. The container started and passed its health check, so this is something that only shows up once the app is serving real requests.",
|
|
108654
|
+
suggestion: "Run `percher doctor` — it re-probes the container and the public route and will say which half is failing. The container log from the moment it was rolled back is in the build log below. Retrying unchanged will usually fail the same way."
|
|
108655
|
+
},
|
|
108656
|
+
APP_DATA_MOUNT_CONFLICT: {
|
|
108657
|
+
errorClass: "app_data_mount_conflict",
|
|
108658
|
+
phase: "start",
|
|
108659
|
+
cause: "app_data_mount_conflict",
|
|
108660
|
+
title: "Your image has a file where Percher mounts /app/data",
|
|
108661
|
+
explanation: () => "The image built, but Docker could not start the container: every Percher app gets its persistent data volume mounted at /app/data, and this image has a file (or a link to one) at /app or /app/data, so that directory cannot be created.",
|
|
108662
|
+
suggestion: "Install the binary somewhere else, such as /usr/local/bin/app, and point ENTRYPOINT/CMD at it. If the project has a file named `data` at its root, rename it. Retrying unchanged will fail the same way."
|
|
108663
|
+
},
|
|
108664
|
+
WORKER_UNAVAILABLE: {
|
|
108665
|
+
errorClass: "infra_unavailable",
|
|
108666
|
+
phase: "infra",
|
|
108667
|
+
cause: "worker_fetch_failed",
|
|
108668
|
+
title: "Build worker is unreachable",
|
|
108669
|
+
explanation: (raw) => raw ? `The Percher build worker did not respond in time: ${trimMessage(raw)}. This is a platform-side issue, not your code.` : "The Percher build worker did not respond in time. This is a platform-side issue, not your code.",
|
|
108670
|
+
suggestion: "Wait 30 seconds and run `percher publish` again. If retry #2 also fails, pause 5 minutes before the next attempt — back-to-back retries during a partial outage just waste your time. After 3 spaced-out retries, check status.percher.app or contact support. Your build never started."
|
|
108671
|
+
},
|
|
108672
|
+
WORKER_UNAVAILABLE_PERSISTENT: {
|
|
108673
|
+
errorClass: "infra_unavailable",
|
|
108674
|
+
phase: "infra",
|
|
108675
|
+
cause: "worker_circuit_breaker_open",
|
|
108676
|
+
title: "Build worker is in a crash loop",
|
|
108677
|
+
explanation: () => "The Percher build worker has failed repeatedly in the last minute and the platform's circuit breaker has flipped open. Retrying immediately would just queue more failures.",
|
|
108678
|
+
suggestion: "Wait 2-3 minutes for the platform to recover, then retry. If it persists past 5 minutes check status.percher.app or contact support — this is a platform incident, not your code."
|
|
108679
|
+
},
|
|
108680
|
+
BUILD_TIMEOUT: {
|
|
108681
|
+
errorClass: "build_failed",
|
|
108682
|
+
phase: "build",
|
|
108683
|
+
cause: "build_timeout",
|
|
108684
|
+
title: "Build timed out",
|
|
108685
|
+
explanation: () => "The build ran longer than the 10-minute platform limit and was aborted.",
|
|
108686
|
+
suggestion: "Common causes: a slow `postinstall` hook, very large dependency tree, or a Dockerfile that pulls a huge base image. Try `bun install` locally and check what's slow. If it's legitimately a heavy build, contact support to discuss raising the limit for your account."
|
|
108687
|
+
},
|
|
108688
|
+
BUILD_FAILED: {
|
|
108689
|
+
errorClass: "build_failed",
|
|
108690
|
+
phase: "build",
|
|
108691
|
+
cause: "build_nonzero_exit",
|
|
108692
|
+
title: "Build failed",
|
|
108693
|
+
explanation: (raw) => raw ? `The build process returned a non-zero exit code: ${trimMessage(raw)}. Read the build log below for the exact error.` : "The build process returned a non-zero exit code. Read the build log below for the exact error.",
|
|
108694
|
+
suggestion: "Run your build locally to reproduce: `bun install && bun run build`. The error in the build log usually points to a missing dependency, a TypeScript/syntax error, or a misconfigured framework setting."
|
|
108695
|
+
},
|
|
108696
|
+
WORKER_ERROR: {
|
|
108697
|
+
errorClass: "infra_unavailable",
|
|
108698
|
+
phase: "infra",
|
|
108699
|
+
cause: "worker_internal_error",
|
|
108700
|
+
title: "Platform error inside the build worker",
|
|
108701
|
+
explanation: (raw) => raw ? `The build worker hit an internal error: ${trimMessage(raw)}. This is a Percher bug, not your code.` : "The build worker hit an internal error. This is a Percher bug, not your code.",
|
|
108702
|
+
suggestion: "Retry once — transient platform errors usually clear within a minute. If it repeats, please report it: include the deploy ID and we'll investigate. Your account is not at fault."
|
|
108703
|
+
},
|
|
108704
|
+
WORKER_ENDPOINT_MISSING: {
|
|
108705
|
+
errorClass: "infra_unavailable",
|
|
108706
|
+
phase: "infra",
|
|
108707
|
+
cause: "worker_endpoint_missing",
|
|
108708
|
+
title: "Platform worker is mid-upgrade",
|
|
108709
|
+
explanation: (raw) => raw ? `${trimMessage(raw)} The platform's worker process predates an endpoint this request needs — a rolling-deploy skew, not your code.` : "The platform's worker process predates an endpoint this request needs — a rolling-deploy skew, not your code.",
|
|
108710
|
+
suggestion: "Retry in a few minutes; the operator finishes the platform deploy worker-first. This failure does not count against your app."
|
|
108711
|
+
},
|
|
108712
|
+
DEPLOY_STALLED: {
|
|
108713
|
+
errorClass: "infra_unavailable",
|
|
108714
|
+
phase: "infra",
|
|
108715
|
+
cause: "deploy_stalled",
|
|
108716
|
+
title: "Deploy stalled",
|
|
108717
|
+
explanation: () => "The platform abandoned this deploy — likely because the API or build worker restarted mid-build. Your code is fine; the build never finished.",
|
|
108718
|
+
suggestion: "Run `percher publish` again. The new deploy will start fresh. If you see this repeatedly across multiple publishes, check status.percher.app — it implies a recurring platform issue."
|
|
108412
108719
|
}
|
|
108413
|
-
|
|
108414
|
-
|
|
108415
|
-
|
|
108416
|
-
if (typeof value !== "number" || !Number.isInteger(value))
|
|
108720
|
+
};
|
|
108721
|
+
function classifyByErrorCode(code, rawMessage) {
|
|
108722
|
+
if (!code)
|
|
108417
108723
|
return null;
|
|
108418
|
-
|
|
108724
|
+
const mapping = MAPPINGS[code];
|
|
108725
|
+
if (!mapping)
|
|
108419
108726
|
return null;
|
|
108420
|
-
return
|
|
108421
|
-
|
|
108422
|
-
|
|
108423
|
-
|
|
108424
|
-
|
|
108425
|
-
|
|
108426
|
-
|
|
108427
|
-
|
|
108428
|
-
|
|
108429
|
-
|
|
108430
|
-
continue;
|
|
108431
|
-
const { key, file: file2, line } = entry;
|
|
108432
|
-
if (!isEnvKey(key) || !isSafeRelativePath(file2))
|
|
108433
|
-
continue;
|
|
108434
|
-
const parsedLine = boundedLine(line);
|
|
108435
|
-
out.push({ key, file: file2, ...parsedLine !== null ? { line: parsedLine } : {} });
|
|
108436
|
-
if (out.length >= max)
|
|
108437
|
-
break;
|
|
108438
|
-
}
|
|
108439
|
-
return out;
|
|
108440
|
-
}
|
|
108441
|
-
var PATH_SEGMENT = /^[A-Za-z0-9._@+ ()'[\]-]+$/;
|
|
108442
|
-
var RESERVED_DEVICE_NAME = /^(?:CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(?:\.|$)/i;
|
|
108443
|
-
function isSafeRelativePath(value) {
|
|
108444
|
-
if (typeof value !== "string" || value.length === 0 || value.length > 1024)
|
|
108445
|
-
return false;
|
|
108446
|
-
if (value.includes("\x00"))
|
|
108447
|
-
return false;
|
|
108448
|
-
const segments = value.replace(/\\/g, "/").split("/");
|
|
108449
|
-
return segments.every((s) => {
|
|
108450
|
-
if (s === "." || s === ".." || !PATH_SEGMENT.test(s))
|
|
108451
|
-
return false;
|
|
108452
|
-
if (/[ .]$/.test(s))
|
|
108453
|
-
return false;
|
|
108454
|
-
return !RESERVED_DEVICE_NAME.test(s);
|
|
108455
|
-
});
|
|
108727
|
+
return {
|
|
108728
|
+
title: mapping.title,
|
|
108729
|
+
explanation: mapping.explanation(rawMessage),
|
|
108730
|
+
suggestion: mapping.suggestion,
|
|
108731
|
+
errorClass: mapping.errorClass,
|
|
108732
|
+
phase: mapping.phase,
|
|
108733
|
+
cause: mapping.cause,
|
|
108734
|
+
relevantFiles: [],
|
|
108735
|
+
missingEnvVars: []
|
|
108736
|
+
};
|
|
108456
108737
|
}
|
|
108457
|
-
function
|
|
108458
|
-
const
|
|
108459
|
-
|
|
108738
|
+
function trimMessage(raw) {
|
|
108739
|
+
const firstLine = raw.split(`
|
|
108740
|
+
`)[0] ?? "";
|
|
108741
|
+
return firstLine.length > 200 ? `${firstLine.slice(0, 200)}…` : firstLine;
|
|
108460
108742
|
}
|
|
108743
|
+
var PB_PROVISION_FAILED_ASK = {
|
|
108744
|
+
prompt: "The platform could not set up the database admin account. Publishing again after the platform failure is resolved is safe.",
|
|
108745
|
+
reasonCode: "pb_provision_failed",
|
|
108746
|
+
retryable: true
|
|
108747
|
+
};
|
|
108461
108748
|
|
|
108462
108749
|
// ../core/dist/recovery.js
|
|
108463
108750
|
function recoveryNone(opts = {}) {
|
|
@@ -108688,12 +108975,25 @@ function unprovenPlatformClaim(failure) {
|
|
|
108688
108975
|
}
|
|
108689
108976
|
function recoveryFromErrorClass(errorClass, failure) {
|
|
108690
108977
|
switch (errorClass) {
|
|
108978
|
+
case "build_secrets_unavailable":
|
|
108979
|
+
return recoveryAsk({
|
|
108980
|
+
prompt: "Percher cannot protect build secrets for this build. Should I remove the non-public keys from [build] pass_env and publish again, or wait and publish later?",
|
|
108981
|
+
options: [
|
|
108982
|
+
"remove non-public keys from [build] pass_env and publish again",
|
|
108983
|
+
"wait and publish later"
|
|
108984
|
+
],
|
|
108985
|
+
reasonCode: "build_secrets_unavailable"
|
|
108986
|
+
});
|
|
108691
108987
|
case "missing_env":
|
|
108692
108988
|
return recoveryEnv({
|
|
108693
108989
|
app: failure.appName,
|
|
108694
108990
|
keys: failure.missingEnvVars ?? [],
|
|
108695
108991
|
reasonCode: "missing_env"
|
|
108696
108992
|
});
|
|
108993
|
+
case "pb_provision_failed":
|
|
108994
|
+
if (failure.classSource !== "code")
|
|
108995
|
+
return unprovenPlatformClaim(failure);
|
|
108996
|
+
return recoveryAsk(PB_PROVISION_FAILED_ASK);
|
|
108697
108997
|
case "infra_unavailable":
|
|
108698
108998
|
if (failure.classSource !== "code")
|
|
108699
108999
|
return unprovenPlatformClaim(failure);
|
|
@@ -108786,7 +109086,7 @@ function recoveryFromErrorClass(errorClass, failure) {
|
|
|
108786
109086
|
|
|
108787
109087
|
// ../core/dist/commands/publish-failure.js
|
|
108788
109088
|
init_dist();
|
|
108789
|
-
import { join as
|
|
109089
|
+
import { join as join10 } from "node:path";
|
|
108790
109090
|
|
|
108791
109091
|
// ../core/dist/error-classifier.js
|
|
108792
109092
|
function extractEnvVarsFromLog(combined) {
|
|
@@ -108804,6 +109104,19 @@ function extractModuleFromLog(combined) {
|
|
|
108804
109104
|
return match?.[1];
|
|
108805
109105
|
}
|
|
108806
109106
|
var patterns = [
|
|
109107
|
+
{
|
|
109108
|
+
pattern: /PB_PROVISION_FAILED|the platform could not set up the database admin account/i,
|
|
109109
|
+
result: {
|
|
109110
|
+
title: "Database admin setup failed",
|
|
109111
|
+
explanation: "The platform could not set up the database admin account.",
|
|
109112
|
+
suggestion: "Publishing again after the platform failure is resolved is safe.",
|
|
109113
|
+
errorClass: "pb_provision_failed",
|
|
109114
|
+
phase: "infra",
|
|
109115
|
+
cause: "pb_provision_failed",
|
|
109116
|
+
relevantFiles: [],
|
|
109117
|
+
missingEnvVars: []
|
|
109118
|
+
}
|
|
109119
|
+
},
|
|
108807
109120
|
{
|
|
108808
109121
|
pattern: /DEPLOY_STALLED:/,
|
|
108809
109122
|
result: {
|
|
@@ -109172,171 +109485,9 @@ ${buildLog ?? ""}`;
|
|
|
109172
109485
|
return null;
|
|
109173
109486
|
}
|
|
109174
109487
|
|
|
109175
|
-
// ../core/dist/structured-error-codes.js
|
|
109176
|
-
var MAPPINGS = {
|
|
109177
|
-
PLATFORM_DISK_LOW: {
|
|
109178
|
-
errorClass: "platform_at_capacity",
|
|
109179
|
-
phase: "infra",
|
|
109180
|
-
cause: "disk_headroom_exhausted",
|
|
109181
|
-
title: "Platform storage is too low",
|
|
109182
|
-
explanation: () => "Percher paused new deploys: the platform is nearly out of disk. Not your app — nothing to change. Deployed apps keep running; only new deploys are paused.",
|
|
109183
|
-
suggestion: "The operator is alerted automatically. Run `percher publish` again in ~15 min; this failure does not count against your app. Still failing after an hour: https://percher.app/status"
|
|
109184
|
-
},
|
|
109185
|
-
PLATFORM_DISK_UNKNOWN: {
|
|
109186
|
-
errorClass: "platform_at_capacity",
|
|
109187
|
-
phase: "infra",
|
|
109188
|
-
cause: "disk_state_unreadable",
|
|
109189
|
-
title: "Platform storage state is unknown",
|
|
109190
|
-
explanation: () => "Percher paused new deploys: it can't read how much disk is left, and won't assume there's room. Not your app. Deployed apps have not been stopped by this.",
|
|
109191
|
-
suggestion: "Run `percher publish` again in ~5 min; this failure does not count against your app. Still failing after 15 min: https://percher.app/status"
|
|
109192
|
-
},
|
|
109193
|
-
PLAN_POOL_EXCEEDED: {
|
|
109194
|
-
errorClass: "plan_pool_exceeded",
|
|
109195
|
-
phase: "start",
|
|
109196
|
-
cause: "plan_pool_exceeded",
|
|
109197
|
-
title: "Plan resource pool is full",
|
|
109198
|
-
explanation: (raw) => raw ? `${trimMessage(raw)} The platform is fine — the account is at its plan's limit.` : "This deploy would commit more RAM, vCPU or disk than your plan's pool allows across all your apps. The platform is fine — the account is at its limit.",
|
|
109199
|
-
suggestion: "Reduce `[resources]` in this app's percher.toml, delete an app you no longer need, or upgrade at https://percher.app/account/billing. Retrying unchanged will fail the same way."
|
|
109200
|
-
},
|
|
109201
|
-
PLACEMENT_UNDETERMINED: {
|
|
109202
|
-
errorClass: "infra_unavailable",
|
|
109203
|
-
phase: "infra",
|
|
109204
|
-
cause: "placement_undetermined",
|
|
109205
|
-
title: "Percher could not place your app's container",
|
|
109206
|
-
explanation: (raw) => raw ? `${trimMessage(raw)} This is a platform-side read of Percher's own container placement, not anything in your app.` : "Percher could not read which internal network this app's current containers are on, and refused to guess rather than risk splitting the app from its database across two networks.",
|
|
109207
|
-
suggestion: "Nothing to change in your app. Retry in a minute; if it persists, the platform is having trouble reaching its container daemon and Percher's operators are alerted."
|
|
109208
|
-
},
|
|
109209
|
-
PLATFORM_MEMORY_LOW: {
|
|
109210
|
-
errorClass: "platform_at_capacity",
|
|
109211
|
-
phase: "infra",
|
|
109212
|
-
cause: "box_admission_rejected",
|
|
109213
|
-
title: "Platform is at capacity",
|
|
109214
|
-
explanation: (raw) => raw ? `Percher couldn't fit this deploy on the box right now: ${trimMessage(raw)} Not your app — nothing to change. Deployed apps keep running.` : "Percher couldn't fit this deploy on the box right now — the admission gate refused to start the container. Not your app — nothing to change. Deployed apps keep running.",
|
|
109215
|
-
suggestion: "Run `percher publish` again in a few minutes; capacity frees up as other deploys finish, and this failure does not count against your app. Still failing after an hour: https://percher.app/status"
|
|
109216
|
-
},
|
|
109217
|
-
PB_BRIDGE_UNAVAILABLE: {
|
|
109218
|
-
errorClass: "infra_unavailable",
|
|
109219
|
-
phase: "infra",
|
|
109220
|
-
cause: "pb_bridge_unreachable",
|
|
109221
|
-
title: "PocketBase is unreachable from the platform",
|
|
109222
|
-
explanation: (raw) => raw ? `Percher could not reach your app's database through its internal bridge: ${trimMessage(raw)}. This is a platform-side issue — your data and your code are both fine.` : "Percher could not reach your app's database through its internal bridge. This is a platform-side issue — your data and your code are both fine.",
|
|
109223
|
-
suggestion: "Wait a minute and run `percher publish` again; this failure does not count against your app. Your data was not touched — the platform could not read it, which is not the same as it being missing. Still failing after 15 min: https://percher.app/status"
|
|
109224
|
-
},
|
|
109225
|
-
PB_ROTATION_UNVERIFIED: {
|
|
109226
|
-
errorClass: "infra_unavailable",
|
|
109227
|
-
phase: "infra",
|
|
109228
|
-
cause: "pb_rotation_unverified",
|
|
109229
|
-
title: "PocketBase admin password was rotated but not confirmed",
|
|
109230
|
-
explanation: (raw) => raw ? `${trimMessage(raw)} The rotation itself ran, so the previously stored admin password may now be stale. Your app data was not modified — only the admin credential changed.` : "Percher rotated your app's PocketBase admin password, then could not confirm the rotation. The previously stored admin password may now be stale. Your app data was not modified — only the admin credential changed.",
|
|
109231
|
-
suggestion: "Run the superuser reset again (`percher data reset-superuser` or the dashboard's reset button) — the rotation is idempotent, and a successful re-run stores a fresh working credential. This failure does not count against your app."
|
|
109232
|
-
},
|
|
109233
|
-
PB_OPERATION_BUSY: {
|
|
109234
|
-
errorClass: "pb_operation_busy",
|
|
109235
|
-
phase: "start",
|
|
109236
|
-
cause: "pb_backup_restore_busy",
|
|
109237
|
-
title: "PocketBase is finishing another data operation",
|
|
109238
|
-
explanation: (raw) => raw ? `${trimMessage(raw)} The operation is scoped to this app and must finish before Percher can create the deploy checkpoint.` : "PocketBase is already creating a backup or restoring data for this app, so Percher could not create the deploy checkpoint yet.",
|
|
109239
|
-
suggestion: "Wait for the current backup, export or restore to finish, then publish again. If no operation is visible after a few minutes, contact support before retrying repeatedly."
|
|
109240
|
-
},
|
|
109241
|
-
CANARY_FAILED: {
|
|
109242
|
-
errorClass: "runtime_crash",
|
|
109243
|
-
phase: "start",
|
|
109244
|
-
cause: "canary_failed",
|
|
109245
|
-
title: "Your app went live, then failed its post-deploy check",
|
|
109246
|
-
explanation: (raw) => raw ? `${trimMessage(raw)} The container started and passed its health check, so this is something that only shows up once the app is serving real requests.` : "Percher swapped your new version in, then its own public URL did not answer correctly. The container started and passed its health check, so this is something that only shows up once the app is serving real requests.",
|
|
109247
|
-
suggestion: "Run `percher doctor` — it re-probes the container and the public route and will say which half is failing. The container log from the moment it was rolled back is in the build log below. Retrying unchanged will usually fail the same way."
|
|
109248
|
-
},
|
|
109249
|
-
APP_DATA_MOUNT_CONFLICT: {
|
|
109250
|
-
errorClass: "app_data_mount_conflict",
|
|
109251
|
-
phase: "start",
|
|
109252
|
-
cause: "app_data_mount_conflict",
|
|
109253
|
-
title: "Your image has a file where Percher mounts /app/data",
|
|
109254
|
-
explanation: () => "The image built, but Docker could not start the container: every Percher app gets its persistent data volume mounted at /app/data, and this image has a file (or a link to one) at /app or /app/data, so that directory cannot be created.",
|
|
109255
|
-
suggestion: "Install the binary somewhere else, such as /usr/local/bin/app, and point ENTRYPOINT/CMD at it. If the project has a file named `data` at its root, rename it. Retrying unchanged will fail the same way."
|
|
109256
|
-
},
|
|
109257
|
-
WORKER_UNAVAILABLE: {
|
|
109258
|
-
errorClass: "infra_unavailable",
|
|
109259
|
-
phase: "infra",
|
|
109260
|
-
cause: "worker_fetch_failed",
|
|
109261
|
-
title: "Build worker is unreachable",
|
|
109262
|
-
explanation: (raw) => raw ? `The Percher build worker did not respond in time: ${trimMessage(raw)}. This is a platform-side issue, not your code.` : "The Percher build worker did not respond in time. This is a platform-side issue, not your code.",
|
|
109263
|
-
suggestion: "Wait 30 seconds and run `percher publish` again. If retry #2 also fails, pause 5 minutes before the next attempt — back-to-back retries during a partial outage just waste your time. After 3 spaced-out retries, check status.percher.app or contact support. Your build never started."
|
|
109264
|
-
},
|
|
109265
|
-
WORKER_UNAVAILABLE_PERSISTENT: {
|
|
109266
|
-
errorClass: "infra_unavailable",
|
|
109267
|
-
phase: "infra",
|
|
109268
|
-
cause: "worker_circuit_breaker_open",
|
|
109269
|
-
title: "Build worker is in a crash loop",
|
|
109270
|
-
explanation: () => "The Percher build worker has failed repeatedly in the last minute and the platform's circuit breaker has flipped open. Retrying immediately would just queue more failures.",
|
|
109271
|
-
suggestion: "Wait 2-3 minutes for the platform to recover, then retry. If it persists past 5 minutes check status.percher.app or contact support — this is a platform incident, not your code."
|
|
109272
|
-
},
|
|
109273
|
-
BUILD_TIMEOUT: {
|
|
109274
|
-
errorClass: "build_failed",
|
|
109275
|
-
phase: "build",
|
|
109276
|
-
cause: "build_timeout",
|
|
109277
|
-
title: "Build timed out",
|
|
109278
|
-
explanation: () => "The build ran longer than the 10-minute platform limit and was aborted.",
|
|
109279
|
-
suggestion: "Common causes: a slow `postinstall` hook, very large dependency tree, or a Dockerfile that pulls a huge base image. Try `bun install` locally and check what's slow. If it's legitimately a heavy build, contact support to discuss raising the limit for your account."
|
|
109280
|
-
},
|
|
109281
|
-
BUILD_FAILED: {
|
|
109282
|
-
errorClass: "build_failed",
|
|
109283
|
-
phase: "build",
|
|
109284
|
-
cause: "build_nonzero_exit",
|
|
109285
|
-
title: "Build failed",
|
|
109286
|
-
explanation: (raw) => raw ? `The build process returned a non-zero exit code: ${trimMessage(raw)}. Read the build log below for the exact error.` : "The build process returned a non-zero exit code. Read the build log below for the exact error.",
|
|
109287
|
-
suggestion: "Run your build locally to reproduce: `bun install && bun run build`. The error in the build log usually points to a missing dependency, a TypeScript/syntax error, or a misconfigured framework setting."
|
|
109288
|
-
},
|
|
109289
|
-
WORKER_ERROR: {
|
|
109290
|
-
errorClass: "infra_unavailable",
|
|
109291
|
-
phase: "infra",
|
|
109292
|
-
cause: "worker_internal_error",
|
|
109293
|
-
title: "Platform error inside the build worker",
|
|
109294
|
-
explanation: (raw) => raw ? `The build worker hit an internal error: ${trimMessage(raw)}. This is a Percher bug, not your code.` : "The build worker hit an internal error. This is a Percher bug, not your code.",
|
|
109295
|
-
suggestion: "Retry once — transient platform errors usually clear within a minute. If it repeats, please report it: include the deploy ID and we'll investigate. Your account is not at fault."
|
|
109296
|
-
},
|
|
109297
|
-
WORKER_ENDPOINT_MISSING: {
|
|
109298
|
-
errorClass: "infra_unavailable",
|
|
109299
|
-
phase: "infra",
|
|
109300
|
-
cause: "worker_endpoint_missing",
|
|
109301
|
-
title: "Platform worker is mid-upgrade",
|
|
109302
|
-
explanation: (raw) => raw ? `${trimMessage(raw)} The platform's worker process predates an endpoint this request needs — a rolling-deploy skew, not your code.` : "The platform's worker process predates an endpoint this request needs — a rolling-deploy skew, not your code.",
|
|
109303
|
-
suggestion: "Retry in a few minutes; the operator finishes the platform deploy worker-first. This failure does not count against your app."
|
|
109304
|
-
},
|
|
109305
|
-
DEPLOY_STALLED: {
|
|
109306
|
-
errorClass: "infra_unavailable",
|
|
109307
|
-
phase: "infra",
|
|
109308
|
-
cause: "deploy_stalled",
|
|
109309
|
-
title: "Deploy stalled",
|
|
109310
|
-
explanation: () => "The platform abandoned this deploy — likely because the API or build worker restarted mid-build. Your code is fine; the build never finished.",
|
|
109311
|
-
suggestion: "Run `percher publish` again. The new deploy will start fresh. If you see this repeatedly across multiple publishes, check status.percher.app — it implies a recurring platform issue."
|
|
109312
|
-
}
|
|
109313
|
-
};
|
|
109314
|
-
function classifyByErrorCode(code, rawMessage) {
|
|
109315
|
-
if (!code)
|
|
109316
|
-
return null;
|
|
109317
|
-
const mapping = MAPPINGS[code];
|
|
109318
|
-
if (!mapping)
|
|
109319
|
-
return null;
|
|
109320
|
-
return {
|
|
109321
|
-
title: mapping.title,
|
|
109322
|
-
explanation: mapping.explanation(rawMessage),
|
|
109323
|
-
suggestion: mapping.suggestion,
|
|
109324
|
-
errorClass: mapping.errorClass,
|
|
109325
|
-
phase: mapping.phase,
|
|
109326
|
-
cause: mapping.cause,
|
|
109327
|
-
relevantFiles: [],
|
|
109328
|
-
missingEnvVars: []
|
|
109329
|
-
};
|
|
109330
|
-
}
|
|
109331
|
-
function trimMessage(raw) {
|
|
109332
|
-
const firstLine = raw.split(`
|
|
109333
|
-
`)[0] ?? "";
|
|
109334
|
-
return firstLine.length > 200 ? `${firstLine.slice(0, 200)}…` : firstLine;
|
|
109335
|
-
}
|
|
109336
|
-
|
|
109337
109488
|
// ../core/dist/commands/publish-failure.js
|
|
109338
109489
|
function tomlPathFor(cwd) {
|
|
109339
|
-
return
|
|
109490
|
+
return join10(cwd, "percher.toml");
|
|
109340
109491
|
}
|
|
109341
109492
|
async function buildFailureError(ctx, app, deployment, signal) {
|
|
109342
109493
|
let buildLogTail;
|
|
@@ -109521,13 +109672,13 @@ async function deploysInspect(ctx, input) {
|
|
|
109521
109672
|
}
|
|
109522
109673
|
// ../core/dist/commands/dev.js
|
|
109523
109674
|
import { existsSync as existsSync8 } from "node:fs";
|
|
109524
|
-
import { join as
|
|
109675
|
+
import { join as join13 } from "node:path";
|
|
109525
109676
|
|
|
109526
109677
|
// ../core/dist/tarball.js
|
|
109527
109678
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
109528
109679
|
import { createHash as createHash2 } from "node:crypto";
|
|
109529
|
-
import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as
|
|
109530
|
-
import { join as
|
|
109680
|
+
import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync7, statSync as statSync2 } from "node:fs";
|
|
109681
|
+
import { join as join11, relative as relative4, sep as sep2 } from "node:path";
|
|
109531
109682
|
import { Readable } from "node:stream";
|
|
109532
109683
|
import { createGzip } from "node:zlib";
|
|
109533
109684
|
function isFilesOptions(options) {
|
|
@@ -109551,7 +109702,7 @@ var ALWAYS_IGNORE_SECRETS = [".env", ".env.*"];
|
|
|
109551
109702
|
function* walk(dir, baseDir, ig) {
|
|
109552
109703
|
const entries = readdirSync2(dir);
|
|
109553
109704
|
for (const name of entries) {
|
|
109554
|
-
const full =
|
|
109705
|
+
const full = join11(dir, name);
|
|
109555
109706
|
const rel = relative4(baseDir, full).split(sep2).join("/");
|
|
109556
109707
|
if (ig.ignores(rel))
|
|
109557
109708
|
continue;
|
|
@@ -109640,20 +109791,20 @@ async function createTarball(options) {
|
|
|
109640
109791
|
}
|
|
109641
109792
|
const ig = import_ignore.default();
|
|
109642
109793
|
ig.add(DEFAULT_IGNORE);
|
|
109643
|
-
const gitignorePath =
|
|
109794
|
+
const gitignorePath = join11(options.cwd, ".gitignore");
|
|
109644
109795
|
if (existsSync6(gitignorePath)) {
|
|
109645
|
-
ig.add(
|
|
109796
|
+
ig.add(readFileSync7(gitignorePath, "utf8"));
|
|
109646
109797
|
}
|
|
109647
|
-
const percherignorePath =
|
|
109798
|
+
const percherignorePath = join11(options.cwd, ".percherignore");
|
|
109648
109799
|
if (existsSync6(percherignorePath)) {
|
|
109649
|
-
ig.add(
|
|
109800
|
+
ig.add(readFileSync7(percherignorePath, "utf8"));
|
|
109650
109801
|
}
|
|
109651
109802
|
if (options.extraIgnore)
|
|
109652
109803
|
ig.add(options.extraIgnore);
|
|
109653
109804
|
ig.add(ALWAYS_IGNORE_SECRETS);
|
|
109654
109805
|
const fileBuffers = [];
|
|
109655
109806
|
for (const rel of walk(options.cwd, options.cwd, ig)) {
|
|
109656
|
-
const data2 =
|
|
109807
|
+
const data2 = readFileSync7(join11(options.cwd, rel));
|
|
109657
109808
|
fileBuffers.push({ path: rel, size: data2.length, data: data2 });
|
|
109658
109809
|
}
|
|
109659
109810
|
return buildTarball(fileBuffers);
|
|
@@ -109675,8 +109826,8 @@ function createTarballFromFiles(files, _config) {
|
|
|
109675
109826
|
|
|
109676
109827
|
// ../core/dist/watcher.js
|
|
109677
109828
|
var import_ignore2 = __toESM(require_ignore(), 1);
|
|
109678
|
-
import { existsSync as existsSync7, readFileSync as
|
|
109679
|
-
import { join as
|
|
109829
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8, watch } from "node:fs";
|
|
109830
|
+
import { join as join12, sep as sep3 } from "node:path";
|
|
109680
109831
|
var DEFAULT_IGNORE2 = [
|
|
109681
109832
|
"node_modules",
|
|
109682
109833
|
".git",
|
|
@@ -109696,9 +109847,9 @@ function createWatcher(options) {
|
|
|
109696
109847
|
const debounceMs = options.debounceMs ?? 500;
|
|
109697
109848
|
const ig = import_ignore2.default();
|
|
109698
109849
|
ig.add(DEFAULT_IGNORE2);
|
|
109699
|
-
const gitignorePath =
|
|
109850
|
+
const gitignorePath = join12(options.cwd, ".gitignore");
|
|
109700
109851
|
if (existsSync7(gitignorePath)) {
|
|
109701
|
-
ig.add(
|
|
109852
|
+
ig.add(readFileSync8(gitignorePath, "utf8"));
|
|
109702
109853
|
}
|
|
109703
109854
|
if (options.extraIgnore)
|
|
109704
109855
|
ig.add(options.extraIgnore);
|
|
@@ -109736,7 +109887,7 @@ var devInputSchema = exports_external.object({
|
|
|
109736
109887
|
app: exports_external.string().optional()
|
|
109737
109888
|
});
|
|
109738
109889
|
async function loadConfig2(ctx, appOverride) {
|
|
109739
|
-
const tomlPath =
|
|
109890
|
+
const tomlPath = join13(ctx.cwd, "percher.toml");
|
|
109740
109891
|
if (!existsSync8(tomlPath)) {
|
|
109741
109892
|
throw new PercherCoreError("No percher.toml found. Run: percher init", {
|
|
109742
109893
|
code: "dev.no-app",
|
|
@@ -109932,7 +110083,7 @@ async function diff(ctx, input = {}) {
|
|
|
109932
110083
|
}
|
|
109933
110084
|
// ../core/dist/commands/doctor.js
|
|
109934
110085
|
import { existsSync as existsSync9 } from "node:fs";
|
|
109935
|
-
import { join as
|
|
110086
|
+
import { join as join14 } from "node:path";
|
|
109936
110087
|
|
|
109937
110088
|
// ../core/dist/toml-recovery.js
|
|
109938
110089
|
function tomlErrorToRecoveryProblems(err) {
|
|
@@ -110233,7 +110384,7 @@ async function doctor(ctx, input = {}) {
|
|
|
110233
110384
|
});
|
|
110234
110385
|
return summarize(checks3, { appProvided: !!input.app });
|
|
110235
110386
|
}
|
|
110236
|
-
const tomlPath =
|
|
110387
|
+
const tomlPath = join14(ctx.cwd, "percher.toml");
|
|
110237
110388
|
const tomlExists = existsSync9(tomlPath);
|
|
110238
110389
|
let tomlValid = false;
|
|
110239
110390
|
let tomlProblems;
|
|
@@ -111249,8 +111400,8 @@ var forgejoSetupWebhookInputSchema = exports_external.object({
|
|
|
111249
111400
|
app: exports_external.string().optional().describe("App name (overrides percher.toml)")
|
|
111250
111401
|
});
|
|
111251
111402
|
// ../core/dist/commands/generate.js
|
|
111252
|
-
import { existsSync as existsSync10, mkdirSync as
|
|
111253
|
-
import { join as
|
|
111403
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync7, writeFileSync as writeFileSync7 } from "node:fs";
|
|
111404
|
+
import { join as join15 } from "node:path";
|
|
111254
111405
|
var generateInputSchema = exports_external.object({
|
|
111255
111406
|
cwd: exports_external.string().optional()
|
|
111256
111407
|
});
|
|
@@ -111336,7 +111487,7 @@ function generateTypeScript(config2) {
|
|
|
111336
111487
|
}
|
|
111337
111488
|
async function generate(ctx, input = {}) {
|
|
111338
111489
|
const cwd = input.cwd ?? ctx.cwd;
|
|
111339
|
-
const tomlPath =
|
|
111490
|
+
const tomlPath = join15(cwd, "percher.toml");
|
|
111340
111491
|
if (!existsSync10(tomlPath)) {
|
|
111341
111492
|
throw new PercherCoreError("No percher.toml found", {
|
|
111342
111493
|
code: "dev.no-app",
|
|
@@ -111346,14 +111497,14 @@ async function generate(ctx, input = {}) {
|
|
|
111346
111497
|
const config2 = await parseFile(tomlPath);
|
|
111347
111498
|
const files = [];
|
|
111348
111499
|
const envExample = generateEnvExample(config2);
|
|
111349
|
-
|
|
111500
|
+
writeFileSync7(join15(cwd, ".env.example"), envExample);
|
|
111350
111501
|
files.push(".env.example");
|
|
111351
111502
|
const tsTypes = generateTypeScript(config2);
|
|
111352
|
-
|
|
111503
|
+
writeFileSync7(join15(cwd, "percher.generated.d.ts"), tsTypes);
|
|
111353
111504
|
files.push("percher.generated.d.ts");
|
|
111354
111505
|
const agentInstructions = generateAgentInstructions(config2);
|
|
111355
|
-
|
|
111356
|
-
|
|
111506
|
+
mkdirSync7(join15(cwd, ".percher"), { recursive: true });
|
|
111507
|
+
writeFileSync7(join15(cwd, ".percher", "agent-instructions.md"), agentInstructions);
|
|
111357
111508
|
files.push(".percher/agent-instructions.md");
|
|
111358
111509
|
return { files };
|
|
111359
111510
|
}
|
|
@@ -111559,11 +111710,17 @@ async function hookRevoke(ctx, input) {
|
|
|
111559
111710
|
await ctx.client.hooks.revoke(app.id, input.id);
|
|
111560
111711
|
}
|
|
111561
111712
|
// ../core/dist/commands/import-project.js
|
|
111562
|
-
import { existsSync as existsSync11, readFileSync as
|
|
111563
|
-
import { join as
|
|
111713
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "node:fs";
|
|
111714
|
+
import { join as join16 } from "node:path";
|
|
111564
111715
|
|
|
111565
111716
|
// ../core/dist/static-docker.js
|
|
111566
111717
|
var STATIC_DOCKER_PORT = 8080;
|
|
111718
|
+
var INIT_TEMPLATE_FRAMEWORKS = new Set([
|
|
111719
|
+
"astro",
|
|
111720
|
+
"preact",
|
|
111721
|
+
"solid",
|
|
111722
|
+
"vite"
|
|
111723
|
+
]);
|
|
111567
111724
|
function renderStaticFrameworkDockerfile(input) {
|
|
111568
111725
|
return `# Multi-stage build: a Bun stage compiles the static bundle, then a
|
|
111569
111726
|
# minimal Caddy image serves it. This project uses runtime = "docker"
|
|
@@ -111581,7 +111738,7 @@ RUN bun install && bun run build
|
|
|
111581
111738
|
# Build pinned Caddy source with refreshed security modules. The scratch
|
|
111582
111739
|
# runtime carries no shell, package manager, or file capabilities and listens
|
|
111583
111740
|
# on the unprivileged port :${STATIC_DOCKER_PORT}.
|
|
111584
|
-
FROM golang:1.26.
|
|
111741
|
+
FROM golang:1.26.9-alpine3.23@sha256:6750308561953d451210ed1a8086d36c8c8bdcbf672d38d9d0d424b8a7a0216c AS caddy-source
|
|
111585
111742
|
ADD --checksum=sha256:78e660d838e45c0b8c89338b325946238f153641aa9925df9ef2ad5e8eced31b \\
|
|
111586
111743
|
https://github.com/caddyserver/caddy/commit/b2693fb63a30e6d7be0972c3645e9a2c0a500e93.patch /tmp/caddy-cel-go.patch
|
|
111587
111744
|
RUN go mod download github.com/caddyserver/caddy/v2@v2.11.4 \\
|
|
@@ -111591,9 +111748,15 @@ WORKDIR /src
|
|
|
111591
111748
|
RUN apk add --no-cache patch \\
|
|
111592
111749
|
&& patch -p1 < /tmp/caddy-cel-go.patch \\
|
|
111593
111750
|
&& go get github.com/go-chi/chi/v5@v5.3.0 github.com/google/cel-go@v0.29.2 \\
|
|
111594
|
-
github.com/klauspost/compress@v1.18.7 go.opentelemetry.io/otel@v1.
|
|
111595
|
-
|
|
111596
|
-
|
|
111751
|
+
github.com/klauspost/compress@v1.18.7 go.opentelemetry.io/otel@v1.45.0 \\
|
|
111752
|
+
go.opentelemetry.io/otel/sdk/log@v0.21.0 \\
|
|
111753
|
+
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc@v0.21.0 \\
|
|
111754
|
+
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp@v0.21.0 \\
|
|
111755
|
+
go.opentelemetry.io/otel/exporters/stdout/stdoutlog@v0.21.0 \\
|
|
111756
|
+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@v1.45.0 \\
|
|
111757
|
+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp@v1.45.0 \\
|
|
111758
|
+
golang.org/x/crypto@v0.57.0 golang.org/x/net@v0.60.0 \\
|
|
111759
|
+
golang.org/x/text@v0.42.0 google.golang.org/grpc@v1.83.2 \\
|
|
111597
111760
|
&& go mod tidy \\
|
|
111598
111761
|
&& CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o /out/caddy ./cmd/caddy \\
|
|
111599
111762
|
&& printf '%s\\n' \\
|
|
@@ -111631,6 +111794,15 @@ ${body}
|
|
|
111631
111794
|
}
|
|
111632
111795
|
`;
|
|
111633
111796
|
}
|
|
111797
|
+
var TEMPLATE_GO_PINS = readGoPins(renderStaticFrameworkDockerfile({ outputDir: "dist" }));
|
|
111798
|
+
function readGoPins(dockerfile) {
|
|
111799
|
+
const modules = {};
|
|
111800
|
+
for (const m of dockerfile.matchAll(/([a-z0-9-]+(?:\.[a-z0-9-]+)+\/[^\s@\\]+)@v(\d[^\s\\]*)/g)) {
|
|
111801
|
+
if (m[1] && m[2])
|
|
111802
|
+
modules[m[1]] = m[2];
|
|
111803
|
+
}
|
|
111804
|
+
return { go: /^FROM golang:(\d+\.\d+\.\d+)/m.exec(dockerfile)?.[1] ?? null, modules };
|
|
111805
|
+
}
|
|
111634
111806
|
|
|
111635
111807
|
// ../core/dist/commands/import-project.js
|
|
111636
111808
|
var importInputSchema = exports_external.object({
|
|
@@ -111638,9 +111810,9 @@ var importInputSchema = exports_external.object({
|
|
|
111638
111810
|
app: exports_external.string().optional().describe("App name to import into")
|
|
111639
111811
|
});
|
|
111640
111812
|
function detectProvider(cwd) {
|
|
111641
|
-
if (existsSync11(
|
|
111813
|
+
if (existsSync11(join16(cwd, "vercel.json")))
|
|
111642
111814
|
return "vercel";
|
|
111643
|
-
if (existsSync11(
|
|
111815
|
+
if (existsSync11(join16(cwd, "netlify.toml")))
|
|
111644
111816
|
return "netlify";
|
|
111645
111817
|
return null;
|
|
111646
111818
|
}
|
|
@@ -111976,7 +112148,7 @@ function mapVercelFramework(framework) {
|
|
|
111976
112148
|
return map2[framework ?? ""] ?? { runtime: "node" };
|
|
111977
112149
|
}
|
|
111978
112150
|
function parseVercelJson(cwd) {
|
|
111979
|
-
const raw =
|
|
112151
|
+
const raw = readFileSync9(join16(cwd, "vercel.json"), "utf-8");
|
|
111980
112152
|
const config2 = JSON.parse(raw);
|
|
111981
112153
|
const warnings = [];
|
|
111982
112154
|
const envVars = [];
|
|
@@ -112032,12 +112204,12 @@ health = "/api/health"
|
|
|
112032
112204
|
return { toml, warnings, envVars, staticBuild, redirects, headers };
|
|
112033
112205
|
}
|
|
112034
112206
|
function parseNetlifyToml(cwd) {
|
|
112035
|
-
const raw =
|
|
112207
|
+
const raw = readFileSync9(join16(cwd, "netlify.toml"), "utf-8");
|
|
112036
112208
|
const warnings = [];
|
|
112037
112209
|
const envVars = [];
|
|
112038
112210
|
const buildCommand = raw.match(/command\s*=\s*"([^"]+)"/)?.[1] ?? "bun run build";
|
|
112039
112211
|
const publishDir = raw.match(/publish\s*=\s*"([^"]+)"/)?.[1] ?? "dist";
|
|
112040
|
-
const hasPackageJson = existsSync11(
|
|
112212
|
+
const hasPackageJson = existsSync11(join16(cwd, "package.json"));
|
|
112041
112213
|
const looksLikeStaticOutput = publishDir === "dist" || publishDir === "build" || publishDir === "public";
|
|
112042
112214
|
let toml = `[app]
|
|
112043
112215
|
name = "imported-app"
|
|
@@ -112085,23 +112257,23 @@ health = "/health"
|
|
|
112085
112257
|
}
|
|
112086
112258
|
}
|
|
112087
112259
|
const sidecarPath = (name) => {
|
|
112088
|
-
const root =
|
|
112260
|
+
const root = join16(cwd, name);
|
|
112089
112261
|
if (existsSync11(root))
|
|
112090
112262
|
return root;
|
|
112091
|
-
const published =
|
|
112263
|
+
const published = join16(cwd, publishDir, name);
|
|
112092
112264
|
return existsSync11(published) ? published : null;
|
|
112093
112265
|
};
|
|
112094
112266
|
const redirectCollector = newRedirectCollector();
|
|
112095
112267
|
const redirectsPath = sidecarPath("_redirects");
|
|
112096
112268
|
if (redirectsPath) {
|
|
112097
|
-
parseNetlifyRedirectsFile(
|
|
112269
|
+
parseNetlifyRedirectsFile(readFileSync9(redirectsPath, "utf-8"), redirectCollector, warnings);
|
|
112098
112270
|
}
|
|
112099
112271
|
parseNetlifyTomlRedirects(raw, redirectCollector, warnings);
|
|
112100
112272
|
const redirects = finishRedirects(redirectCollector, warnings, "netlify");
|
|
112101
112273
|
const headerTable = {};
|
|
112102
112274
|
const headersPath = sidecarPath("_headers");
|
|
112103
112275
|
if (headersPath) {
|
|
112104
|
-
parseNetlifyHeadersFile(
|
|
112276
|
+
parseNetlifyHeadersFile(readFileSync9(headersPath, "utf-8"), headerTable, warnings);
|
|
112105
112277
|
}
|
|
112106
112278
|
const headers = finishHeaders(headerTable, warnings, "_headers");
|
|
112107
112279
|
if (/^\s*\[\[headers\]\]/m.test(raw)) {
|
|
@@ -112170,11 +112342,11 @@ ${serialize(extras)}`;
|
|
|
112170
112342
|
mapped.headerPaths = Object.keys(headers).length;
|
|
112171
112343
|
}
|
|
112172
112344
|
}
|
|
112173
|
-
const tomlPath =
|
|
112345
|
+
const tomlPath = join16(ctx.cwd, "percher.toml");
|
|
112174
112346
|
if (existsSync11(tomlPath)) {
|
|
112175
112347
|
result.warnings.unshift("percher.toml already exists — overwriting with imported config");
|
|
112176
112348
|
}
|
|
112177
|
-
|
|
112349
|
+
writeFileSync8(tomlPath, toml);
|
|
112178
112350
|
const scaffoldedFiles = [];
|
|
112179
112351
|
if (result.staticBuild) {
|
|
112180
112352
|
const targets = [
|
|
@@ -112188,12 +112360,12 @@ ${serialize(extras)}`;
|
|
|
112188
112360
|
}
|
|
112189
112361
|
];
|
|
112190
112362
|
for (const { name, content } of targets) {
|
|
112191
|
-
const target =
|
|
112363
|
+
const target = join16(ctx.cwd, name);
|
|
112192
112364
|
if (existsSync11(target)) {
|
|
112193
112365
|
result.warnings.push(`${name} already exists — left untouched. The imported config expects a multi-stage static build; merge by hand if needed.`);
|
|
112194
112366
|
continue;
|
|
112195
112367
|
}
|
|
112196
|
-
|
|
112368
|
+
writeFileSync8(target, content, "utf-8");
|
|
112197
112369
|
scaffoldedFiles.push(name);
|
|
112198
112370
|
}
|
|
112199
112371
|
}
|
|
@@ -112215,12 +112387,12 @@ function parsesAsPercherToml(source) {
|
|
|
112215
112387
|
}
|
|
112216
112388
|
}
|
|
112217
112389
|
// ../core/dist/commands/init.js
|
|
112218
|
-
import { existsSync as existsSync13, writeFileSync as
|
|
112219
|
-
import { basename as basename3, join as
|
|
112390
|
+
import { existsSync as existsSync13, writeFileSync as writeFileSync9 } from "node:fs";
|
|
112391
|
+
import { basename as basename3, join as join18 } from "node:path";
|
|
112220
112392
|
|
|
112221
112393
|
// ../core/dist/detect.js
|
|
112222
|
-
import { existsSync as existsSync12, readFileSync as
|
|
112223
|
-
import { join as
|
|
112394
|
+
import { existsSync as existsSync12, readFileSync as readFileSync10 } from "node:fs";
|
|
112395
|
+
import { join as join17 } from "node:path";
|
|
112224
112396
|
function readPackageJson(src) {
|
|
112225
112397
|
const raw = src.text("package.json");
|
|
112226
112398
|
if (raw === null)
|
|
@@ -112392,10 +112564,10 @@ function detectFromSource(src) {
|
|
|
112392
112564
|
}
|
|
112393
112565
|
async function detectFramework(cwd) {
|
|
112394
112566
|
const src = {
|
|
112395
|
-
has: (...names) => names.some((n) => existsSync12(
|
|
112567
|
+
has: (...names) => names.some((n) => existsSync12(join17(cwd, n))),
|
|
112396
112568
|
text: (name) => {
|
|
112397
112569
|
try {
|
|
112398
|
-
return
|
|
112570
|
+
return readFileSync10(join17(cwd, name), "utf8");
|
|
112399
112571
|
} catch {
|
|
112400
112572
|
return null;
|
|
112401
112573
|
}
|
|
@@ -112404,7 +112576,7 @@ async function detectFramework(cwd) {
|
|
|
112404
112576
|
return detectFromSource(src);
|
|
112405
112577
|
}
|
|
112406
112578
|
function refineBunRuntime(detected, cwd) {
|
|
112407
|
-
if (detected.runtime === "node" && (existsSync12(
|
|
112579
|
+
if (detected.runtime === "node" && (existsSync12(join17(cwd, "bun.lockb")) || existsSync12(join17(cwd, "bun.lock")))) {
|
|
112408
112580
|
return { ...detected, runtime: "bun" };
|
|
112409
112581
|
}
|
|
112410
112582
|
return detected;
|
|
@@ -112549,7 +112721,7 @@ function scanEnvVars(_cwd) {
|
|
|
112549
112721
|
return {};
|
|
112550
112722
|
}
|
|
112551
112723
|
async function init(ctx, input = {}) {
|
|
112552
|
-
const path =
|
|
112724
|
+
const path = join18(ctx.cwd, "percher.toml");
|
|
112553
112725
|
const exists = existsSync13(path);
|
|
112554
112726
|
if (exists && !input.force) {
|
|
112555
112727
|
throw new PercherCoreError("percher.toml already exists", {
|
|
@@ -112594,7 +112766,7 @@ async function init(ctx, input = {}) {
|
|
|
112594
112766
|
if (Object.keys(envVars).length > 0) {
|
|
112595
112767
|
config2.env = envVars;
|
|
112596
112768
|
}
|
|
112597
|
-
|
|
112769
|
+
writeFileSync9(path, serialize(config2), "utf8");
|
|
112598
112770
|
const scaffoldedFiles = [];
|
|
112599
112771
|
if (detected?.staticBuild) {
|
|
112600
112772
|
const targets = [
|
|
@@ -112610,10 +112782,10 @@ async function init(ctx, input = {}) {
|
|
|
112610
112782
|
}
|
|
112611
112783
|
];
|
|
112612
112784
|
for (const { name: name2, content } of targets) {
|
|
112613
|
-
const target =
|
|
112785
|
+
const target = join18(ctx.cwd, name2);
|
|
112614
112786
|
if (existsSync13(target) && !input.force)
|
|
112615
112787
|
continue;
|
|
112616
|
-
|
|
112788
|
+
writeFileSync9(target, content, "utf8");
|
|
112617
112789
|
scaffoldedFiles.push(name2);
|
|
112618
112790
|
}
|
|
112619
112791
|
}
|
|
@@ -113690,7 +113862,7 @@ function collectWarnings2(collections, rlsPolicies, unsupported) {
|
|
|
113690
113862
|
}
|
|
113691
113863
|
// ../core/dist/commands/migrate-supabase-rewrite.js
|
|
113692
113864
|
import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
|
|
113693
|
-
import { dirname as
|
|
113865
|
+
import { dirname as dirname7, join as join21, relative as relative7, resolve as resolve9, sep as sep5 } from "node:path";
|
|
113694
113866
|
|
|
113695
113867
|
// ../core/dist/commands/migrate-supabase-recipes.js
|
|
113696
113868
|
var AUTH_RECIPES = {
|
|
@@ -113956,7 +114128,7 @@ function isSupabaseClientModule(source) {
|
|
|
113956
114128
|
// ../core/dist/commands/migrate-supabase-status.js
|
|
113957
114129
|
var import_cross_spawn = __toESM(require_cross_spawn(), 1);
|
|
113958
114130
|
import { readFile as readFile4 } from "node:fs/promises";
|
|
113959
|
-
import { join as
|
|
114131
|
+
import { join as join20, relative as relative6, resolve as resolve8 } from "node:path";
|
|
113960
114132
|
|
|
113961
114133
|
// ../core/dist/commands/migrate-supabase-sdk.js
|
|
113962
114134
|
var import_jscodeshift2 = __toESM(require_core3(), 1);
|
|
@@ -114893,7 +115065,7 @@ function applyRewrite(path, replacement, kind, pattern, note, flags, state) {
|
|
|
114893
115065
|
// ../core/dist/commands/migrate-supabase-walker.js
|
|
114894
115066
|
var import_ignore3 = __toESM(require_ignore(), 1);
|
|
114895
115067
|
import { mkdir as mkdir2, readdir, readFile as readFile3, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
|
|
114896
|
-
import { dirname as
|
|
115068
|
+
import { dirname as dirname6, isAbsolute as isAbsolute3, join as join19, relative as relative5, resolve as resolve7, sep as sep4 } from "node:path";
|
|
114897
115069
|
var REWRITABLE_EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
114898
115070
|
var DEFAULT_EXCLUDES = [
|
|
114899
115071
|
"node_modules",
|
|
@@ -115045,7 +115217,7 @@ async function* walkTree(dir, rootAbs, ig) {
|
|
|
115045
115217
|
return;
|
|
115046
115218
|
}
|
|
115047
115219
|
for (const name of entries) {
|
|
115048
|
-
const abs =
|
|
115220
|
+
const abs = join19(dir, name);
|
|
115049
115221
|
const rel = relative5(rootAbs, abs).split(sep4).join("/");
|
|
115050
115222
|
if (rel.length > 0 && ig.ignores(rel))
|
|
115051
115223
|
continue;
|
|
@@ -115207,7 +115379,7 @@ async function writeWalkResult(input) {
|
|
|
115207
115379
|
if (rel.length === 0 || isParentTraversal || isAbsolute3(rel)) {
|
|
115208
115380
|
throw new Error(`writeWalkResult refused path-traversing file "${file2.relativePath}" → "${abs}" (would escape targetDir "${targetAbs}").`);
|
|
115209
115381
|
}
|
|
115210
|
-
await mkdir2(
|
|
115382
|
+
await mkdir2(dirname6(abs), { recursive: true });
|
|
115211
115383
|
await writeFile2(abs, file2.rewritten, "utf8");
|
|
115212
115384
|
written.push(file2.relativePath);
|
|
115213
115385
|
}
|
|
@@ -115482,7 +115654,7 @@ function buildPrompt(state, failing, warning, build) {
|
|
|
115482
115654
|
async function readManualRlsCount(rootAbs) {
|
|
115483
115655
|
for (const dir of ["migration-preview", "."]) {
|
|
115484
115656
|
try {
|
|
115485
|
-
const notes = await readFile4(
|
|
115657
|
+
const notes = await readFile4(join20(rootAbs, dir, "MIGRATION_NOTES.md"), "utf8");
|
|
115486
115658
|
const m = notes.match(/\*\*RLS policies:\*\*\s*\d+\s*\((\d+)\s*need manual review\)/);
|
|
115487
115659
|
if (m?.[1] !== undefined)
|
|
115488
115660
|
return Number.parseInt(m[1], 10);
|
|
@@ -115494,7 +115666,7 @@ async function readManualRlsCount(rootAbs) {
|
|
|
115494
115666
|
async function runProjectBuild(rootAbs) {
|
|
115495
115667
|
let hasBuildScript = false;
|
|
115496
115668
|
try {
|
|
115497
|
-
const pkg = JSON.parse(await readFile4(
|
|
115669
|
+
const pkg = JSON.parse(await readFile4(join20(rootAbs, "package.json"), "utf8"));
|
|
115498
115670
|
hasBuildScript = typeof pkg.scripts?.build === "string";
|
|
115499
115671
|
} catch {
|
|
115500
115672
|
return { ran: false, passed: false, detail: "No readable package.json — nothing to build." };
|
|
@@ -115526,7 +115698,7 @@ ${tail}` : ""}`
|
|
|
115526
115698
|
async function detectPackageManager(rootAbs) {
|
|
115527
115699
|
const has = async (name) => {
|
|
115528
115700
|
try {
|
|
115529
|
-
await readFile4(
|
|
115701
|
+
await readFile4(join20(rootAbs, name), "utf8");
|
|
115530
115702
|
return true;
|
|
115531
115703
|
} catch {
|
|
115532
115704
|
return false;
|
|
@@ -115656,8 +115828,8 @@ async function rewriteSupabaseClient(ctx, input) {
|
|
|
115656
115828
|
flags.push(...scaffold.flags);
|
|
115657
115829
|
}
|
|
115658
115830
|
const reportNeeded = hadRewrites || flags.length > 0 || walk2.skipped.length > 0;
|
|
115659
|
-
const backupDir = apply && changed.length > 0 ?
|
|
115660
|
-
const rewriteTarget = apply ? rootAbs : input.outputDir ? resolve9(ctx.cwd, input.outputDir) :
|
|
115831
|
+
const backupDir = apply && changed.length > 0 ? join21(rootAbs, "migration-preview", "backup") : null;
|
|
115832
|
+
const rewriteTarget = apply ? rootAbs : input.outputDir ? resolve9(ctx.cwd, input.outputDir) : join21(rootAbs, "migration-preview", "rewritten");
|
|
115661
115833
|
const filesWritten = [];
|
|
115662
115834
|
let clientModuleWritten = null;
|
|
115663
115835
|
try {
|
|
@@ -115677,19 +115849,19 @@ async function rewriteSupabaseClient(ctx, input) {
|
|
|
115677
115849
|
changedOnly: true
|
|
115678
115850
|
});
|
|
115679
115851
|
for (const rel of out.written) {
|
|
115680
|
-
filesWritten.push(relative7(ctx.cwd,
|
|
115852
|
+
filesWritten.push(relative7(ctx.cwd, join21(rewriteTarget, rel)));
|
|
115681
115853
|
}
|
|
115682
115854
|
if (scaffold?.clientModule) {
|
|
115683
|
-
const moduleAbs =
|
|
115684
|
-
await mkdir3(
|
|
115855
|
+
const moduleAbs = join21(rewriteTarget, scaffold.clientModule.relativePath);
|
|
115856
|
+
await mkdir3(dirname7(moduleAbs), { recursive: true });
|
|
115685
115857
|
await writeFile3(moduleAbs, scaffold.clientModule.content, "utf8");
|
|
115686
115858
|
clientModuleWritten = relative7(ctx.cwd, moduleAbs).split(sep5).join("/");
|
|
115687
115859
|
filesWritten.push(clientModuleWritten);
|
|
115688
115860
|
}
|
|
115689
115861
|
}
|
|
115690
115862
|
if (reportNeeded) {
|
|
115691
|
-
const reportAbs =
|
|
115692
|
-
await mkdir3(
|
|
115863
|
+
const reportAbs = join21(rootAbs, "migration-preview", "REWRITE_NOTES.md");
|
|
115864
|
+
await mkdir3(dirname7(reportAbs), { recursive: true });
|
|
115693
115865
|
const analysis = analyzeMigration(walk2.files.map((f) => ({
|
|
115694
115866
|
relativePath: f.relativePath,
|
|
115695
115867
|
kind: f.kind,
|
|
@@ -115941,7 +116113,7 @@ function failure3(rootAbs, mode, err, suggestion) {
|
|
|
115941
116113
|
}
|
|
115942
116114
|
// ../core/dist/commands/migrate-supabase-schema.js
|
|
115943
116115
|
import { mkdir as mkdir4, writeFile as writeFile4 } from "node:fs/promises";
|
|
115944
|
-
import { isAbsolute as isAbsolute4, join as
|
|
116116
|
+
import { isAbsolute as isAbsolute4, join as join22, relative as relative8, resolve as resolve10 } from "node:path";
|
|
115945
116117
|
|
|
115946
116118
|
// ../core/dist/commands/migrate-supabase-scripts.js
|
|
115947
116119
|
function generateDataImportScripts(preview) {
|
|
@@ -116461,10 +116633,10 @@ async function writePreviewFiles(ctx, outputDir, preview) {
|
|
|
116461
116633
|
const schemaJson = renderSchemaJson(preview);
|
|
116462
116634
|
const notesMd = renderMigrationNotes(preview);
|
|
116463
116635
|
const scripts = generateDataImportScripts(preview);
|
|
116464
|
-
const schemaPath =
|
|
116465
|
-
const notesPath =
|
|
116466
|
-
const migratePath =
|
|
116467
|
-
const storagePath =
|
|
116636
|
+
const schemaPath = join22(outputDir, "pb_schema.json");
|
|
116637
|
+
const notesPath = join22(outputDir, "MIGRATION_NOTES.md");
|
|
116638
|
+
const migratePath = join22(outputDir, "pb_migrate.js");
|
|
116639
|
+
const storagePath = join22(outputDir, "pb_migrate_storage.js");
|
|
116468
116640
|
await writeFile4(schemaPath, schemaJson, "utf8");
|
|
116469
116641
|
await writeFile4(notesPath, notesMd, "utf8");
|
|
116470
116642
|
await writeFile4(migratePath, scripts.pbMigrate, "utf8");
|
|
@@ -116697,7 +116869,7 @@ async function previewList(ctx, input = {}) {
|
|
|
116697
116869
|
// ../core/dist/commands/publish.js
|
|
116698
116870
|
init_dist();
|
|
116699
116871
|
import { existsSync as existsSync15 } from "node:fs";
|
|
116700
|
-
import { join as
|
|
116872
|
+
import { join as join25 } from "node:path";
|
|
116701
116873
|
|
|
116702
116874
|
// ../shared/dist/pow.js
|
|
116703
116875
|
import { createHash as createHash3 } from "node:crypto";
|
|
@@ -117101,8 +117273,8 @@ async function runDeployWithRetry(opts) {
|
|
|
117101
117273
|
|
|
117102
117274
|
// ../core/dist/commands/env-scan.js
|
|
117103
117275
|
init_dist();
|
|
117104
|
-
import { readdirSync as readdirSync3, readFileSync as
|
|
117105
|
-
import { join as
|
|
117276
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync11, statSync as statSync3 } from "node:fs";
|
|
117277
|
+
import { join as join23 } from "node:path";
|
|
117106
117278
|
function platformInjectedClientEnvKeys(config2) {
|
|
117107
117279
|
const data2 = config2.data;
|
|
117108
117280
|
if (!data2)
|
|
@@ -117160,7 +117332,7 @@ function scanForMissingBuildEnvRefs(cwd, existingKeys) {
|
|
|
117160
117332
|
continue;
|
|
117161
117333
|
let content;
|
|
117162
117334
|
try {
|
|
117163
|
-
content =
|
|
117335
|
+
content = readFileSync11(join23(cwd, rel), "utf8");
|
|
117164
117336
|
} catch {
|
|
117165
117337
|
continue;
|
|
117166
117338
|
}
|
|
@@ -117192,7 +117364,7 @@ function* walkDir(dir, base) {
|
|
|
117192
117364
|
for (const name of entries) {
|
|
117193
117365
|
if (SKIP_DIRS.has(name))
|
|
117194
117366
|
continue;
|
|
117195
|
-
const full =
|
|
117367
|
+
const full = join23(dir, name);
|
|
117196
117368
|
let st;
|
|
117197
117369
|
try {
|
|
117198
117370
|
st = statSync3(full);
|
|
@@ -117209,6 +117381,47 @@ function* walkDir(dir, base) {
|
|
|
117209
117381
|
|
|
117210
117382
|
// ../core/dist/commands/publish-api-error.js
|
|
117211
117383
|
init_dist();
|
|
117384
|
+
|
|
117385
|
+
// ../core/dist/commands/publish-rate-limit.js
|
|
117386
|
+
var RATE_LIMIT_CODES = new Set([
|
|
117387
|
+
"DEPLOY_RATE_LIMIT",
|
|
117388
|
+
"DEPLOY_RATE_LIMITED",
|
|
117389
|
+
"RATE_LIMIT_EXCEEDED"
|
|
117390
|
+
]);
|
|
117391
|
+
function matchRateLimit(err) {
|
|
117392
|
+
if (!RATE_LIMIT_CODES.has(err.code))
|
|
117393
|
+
return null;
|
|
117394
|
+
const wait = boundedSeconds(err.extra?.retryAfterSec);
|
|
117395
|
+
return {
|
|
117396
|
+
title: "Rate limit hit",
|
|
117397
|
+
explanation: err.message,
|
|
117398
|
+
suggestion: "Wait a few minutes and try again. If you need a higher cap, upgrade at https://percher.app/account/billing.",
|
|
117399
|
+
recovery: recoveryAsk({
|
|
117400
|
+
prompt: `Percher is rate-limiting deploys for this app${wait ? `; wait at least ${wait}s` : " — the window is up to an hour"}. This is a burst/hourly limit, not the daily deploy quota. Wait and retry, or upgrade the plan for a higher cap — which would you like?`,
|
|
117401
|
+
options: ["wait and retry", "upgrade", "cancel"],
|
|
117402
|
+
reasonCode: "quota_exceeded"
|
|
117403
|
+
}),
|
|
117404
|
+
summary: `Rate limit hit: ${err.message}`
|
|
117405
|
+
};
|
|
117406
|
+
}
|
|
117407
|
+
function matchConnectionRateLimit(err) {
|
|
117408
|
+
if (err.code !== "OAUTH_GRANT_RATE_LIMITED")
|
|
117409
|
+
return null;
|
|
117410
|
+
const wait = boundedSeconds(err.extra?.retryAfterSec) ?? 60;
|
|
117411
|
+
return {
|
|
117412
|
+
title: "Connection rate limit hit",
|
|
117413
|
+
explanation: err.message,
|
|
117414
|
+
suggestion: `Wait ${wait}s, then publish again. Nothing in the app needs to change.`,
|
|
117415
|
+
recovery: recoveryAsk({
|
|
117416
|
+
prompt: `Percher is limiting requests on this connection; wait at least ${wait}s, then publish again. Nothing in the app needs to change. Wait and retry, or cancel?`,
|
|
117417
|
+
options: ["wait and retry", "cancel"],
|
|
117418
|
+
reasonCode: "quota_exceeded"
|
|
117419
|
+
}),
|
|
117420
|
+
summary: `Connection rate limit hit. Retry in ${wait}s.`
|
|
117421
|
+
};
|
|
117422
|
+
}
|
|
117423
|
+
|
|
117424
|
+
// ../core/dist/commands/publish-api-error.js
|
|
117212
117425
|
var DEPLOY_GATE_CODES = new Set([
|
|
117213
117426
|
"RETRY_LIMIT_REACHED",
|
|
117214
117427
|
"REQUIRED_ENV_MISSING",
|
|
@@ -117228,6 +117441,9 @@ function classifyPublishApiError(err, input) {
|
|
|
117228
117441
|
const rateLimit = matchRateLimit(err);
|
|
117229
117442
|
if (rateLimit)
|
|
117230
117443
|
return buildResult(rateLimit, input);
|
|
117444
|
+
const connectionLimit = matchConnectionRateLimit(err);
|
|
117445
|
+
if (connectionLimit)
|
|
117446
|
+
return buildResult(connectionLimit, input);
|
|
117231
117447
|
const concurrent = matchConcurrentLimit(err);
|
|
117232
117448
|
if (concurrent)
|
|
117233
117449
|
return buildResult(concurrent, input);
|
|
@@ -117313,27 +117529,6 @@ function matchPlanLimit(err) {
|
|
|
117313
117529
|
summary: `Plan limit reached: ${err.message}`
|
|
117314
117530
|
};
|
|
117315
117531
|
}
|
|
117316
|
-
var RATE_LIMIT_CODES = new Set([
|
|
117317
|
-
"DEPLOY_RATE_LIMIT",
|
|
117318
|
-
"DEPLOY_RATE_LIMITED",
|
|
117319
|
-
"RATE_LIMIT_EXCEEDED"
|
|
117320
|
-
]);
|
|
117321
|
-
function matchRateLimit(err) {
|
|
117322
|
-
if (!RATE_LIMIT_CODES.has(err.code))
|
|
117323
|
-
return null;
|
|
117324
|
-
const wait = boundedSeconds(err.extra?.retryAfterSec);
|
|
117325
|
-
return {
|
|
117326
|
-
title: "Rate limit hit",
|
|
117327
|
-
explanation: err.message,
|
|
117328
|
-
suggestion: "Wait a few minutes and try again. If you need a higher cap, upgrade at https://percher.app/account/billing.",
|
|
117329
|
-
recovery: recoveryAsk({
|
|
117330
|
-
prompt: `Percher is rate-limiting deploys for this app${wait ? `; wait at least ${wait}s` : " — the window is up to an hour"}. This is a burst/hourly limit, not the daily deploy quota. Wait and retry, or upgrade the plan for a higher cap — which would you like?`,
|
|
117331
|
-
options: ["wait and retry", "upgrade", "cancel"],
|
|
117332
|
-
reasonCode: "quota_exceeded"
|
|
117333
|
-
}),
|
|
117334
|
-
summary: `Rate limit hit: ${err.message}`
|
|
117335
|
-
};
|
|
117336
|
-
}
|
|
117337
117532
|
var PLATFORM_DISK_CODES2 = new Set(["PLATFORM_DISK_LOW", "PLATFORM_DISK_UNKNOWN"]);
|
|
117338
117533
|
function matchPlatformDisk(err) {
|
|
117339
117534
|
if (!PLATFORM_DISK_CODES2.has(err.code))
|
|
@@ -117691,22 +117886,22 @@ function inFlightPublishResult(p, why) {
|
|
|
117691
117886
|
}
|
|
117692
117887
|
|
|
117693
117888
|
// ../core/dist/commands/publish-node.js
|
|
117694
|
-
import { existsSync as existsSync14, readFileSync as
|
|
117695
|
-
import { join as
|
|
117889
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12 } from "node:fs";
|
|
117890
|
+
import { join as join24 } from "node:path";
|
|
117696
117891
|
function resolveNodeVersion(cwd) {
|
|
117697
|
-
const nvmrc =
|
|
117892
|
+
const nvmrc = join24(cwd, ".nvmrc");
|
|
117698
117893
|
if (existsSync14(nvmrc)) {
|
|
117699
117894
|
try {
|
|
117700
|
-
const v =
|
|
117895
|
+
const v = readFileSync12(nvmrc, "utf8").trim();
|
|
117701
117896
|
if (v)
|
|
117702
117897
|
return { value: v, source: ".nvmrc" };
|
|
117703
117898
|
} catch {}
|
|
117704
117899
|
}
|
|
117705
|
-
const pkgPath =
|
|
117900
|
+
const pkgPath = join24(cwd, "package.json");
|
|
117706
117901
|
if (!existsSync14(pkgPath))
|
|
117707
117902
|
return null;
|
|
117708
117903
|
try {
|
|
117709
|
-
const pkg = JSON.parse(
|
|
117904
|
+
const pkg = JSON.parse(readFileSync12(pkgPath, "utf8"));
|
|
117710
117905
|
const engines = pkg?.engines?.node;
|
|
117711
117906
|
if (typeof engines === "string" && engines.length > 0) {
|
|
117712
117907
|
return { value: engines, source: "engines.node" };
|
|
@@ -117714,6 +117909,34 @@ function resolveNodeVersion(cwd) {
|
|
|
117714
117909
|
} catch {}
|
|
117715
117910
|
return { value: DEFAULT_NODE_VERSION_LABEL, source: "default" };
|
|
117716
117911
|
}
|
|
117912
|
+
function emitPreflight(ctx, config2, tarball) {
|
|
117913
|
+
const lines = [];
|
|
117914
|
+
const fw = config2.app.framework ?? (config2.app.runtime === "docker" ? "Dockerfile" : config2.app.runtime);
|
|
117915
|
+
lines.push(`Detected: ${fw} (runtime=${config2.app.runtime})`);
|
|
117916
|
+
if (config2.app.runtime === "bun") {
|
|
117917
|
+
lines.push("Bun: 1.x (default)");
|
|
117918
|
+
} else if (config2.app.runtime === "node") {
|
|
117919
|
+
const node = resolveNodeVersion(tarball.cwd);
|
|
117920
|
+
if (node) {
|
|
117921
|
+
lines.push(`Node: ${node.value} (${node.source})`);
|
|
117922
|
+
}
|
|
117923
|
+
}
|
|
117924
|
+
if (config2.build?.command) {
|
|
117925
|
+
lines.push(`Build: ${config2.build.command}`);
|
|
117926
|
+
}
|
|
117927
|
+
if (config2.web?.health) {
|
|
117928
|
+
lines.push(`Health: ${config2.web.health}`);
|
|
117929
|
+
}
|
|
117930
|
+
if (config2.web?.port) {
|
|
117931
|
+
lines.push(`Port: ${config2.web.port}`);
|
|
117932
|
+
}
|
|
117933
|
+
const sizeKb = (tarball.bytes / 1024).toFixed(1);
|
|
117934
|
+
lines.push(`Bundle: ${tarball.fileCount} files, ${sizeKb} KB`);
|
|
117935
|
+
lines.push(`Target: ${config2.app.name}`);
|
|
117936
|
+
for (const line of lines) {
|
|
117937
|
+
ctx.status(line);
|
|
117938
|
+
}
|
|
117939
|
+
}
|
|
117717
117940
|
|
|
117718
117941
|
// ../core/dist/commands/publish.js
|
|
117719
117942
|
var publishInputSchema = exports_external.object({
|
|
@@ -117729,7 +117952,7 @@ var publishInputSchema = exports_external.object({
|
|
|
117729
117952
|
var SUSPICIOUS_BYTES = 50 * 1024 * 1024;
|
|
117730
117953
|
var SUSPICIOUS_FILES = 1e4;
|
|
117731
117954
|
function tomlPathFor2(cwd) {
|
|
117732
|
-
return
|
|
117955
|
+
return join25(cwd, "percher.toml");
|
|
117733
117956
|
}
|
|
117734
117957
|
async function buildReplacedResult2(opts) {
|
|
117735
117958
|
const { ctx, app, deployment, tarball, cwd } = opts;
|
|
@@ -117885,7 +118108,7 @@ ${bullets}`,
|
|
|
117885
118108
|
bundle: { fileCount: 0, bytes: 0 }
|
|
117886
118109
|
};
|
|
117887
118110
|
}
|
|
117888
|
-
if (preParsedConfig && !input.dryRun && !existsSync15(
|
|
118111
|
+
if (preParsedConfig && !input.dryRun && !existsSync15(join25(ctx.cwd, "percher.toml"))) {
|
|
117889
118112
|
return {
|
|
117890
118113
|
status: "failed",
|
|
117891
118114
|
fileCount: 0,
|
|
@@ -118202,7 +118425,8 @@ async function finalizeDeploy(opts) {
|
|
|
118202
118425
|
}
|
|
118203
118426
|
});
|
|
118204
118427
|
} catch (err) {
|
|
118205
|
-
|
|
118428
|
+
const serverError = err instanceof PercherApiError && err.status >= 500 && err.status < 600;
|
|
118429
|
+
if (err instanceof DeployPollLostContactError || serverError)
|
|
118206
118430
|
return inFlightPublishResult(inFlight(), "lost_contact");
|
|
118207
118431
|
if (!timedOut)
|
|
118208
118432
|
throw err;
|
|
@@ -118521,7 +118745,7 @@ async function handleAnonymousPublish(ctx, config2, input, tarball) {
|
|
|
118521
118745
|
async function loadOrGenerate(ctx, preParsed) {
|
|
118522
118746
|
if (preParsed)
|
|
118523
118747
|
return structuredClone(preParsed);
|
|
118524
|
-
const tomlPath =
|
|
118748
|
+
const tomlPath = join25(ctx.cwd, "percher.toml");
|
|
118525
118749
|
if (!existsSync15(tomlPath)) {
|
|
118526
118750
|
ctx.status("No percher.toml found — detecting framework...");
|
|
118527
118751
|
const generated = await init(ctx);
|
|
@@ -118545,32 +118769,6 @@ async function ensureApp2(ctx, config2) {
|
|
|
118545
118769
|
throw err;
|
|
118546
118770
|
}
|
|
118547
118771
|
}
|
|
118548
|
-
function emitPreflight(ctx, config2, tarball) {
|
|
118549
|
-
const lines = [];
|
|
118550
|
-
const fw = config2.app.framework ?? (config2.app.runtime === "docker" ? "Dockerfile" : config2.app.runtime);
|
|
118551
|
-
lines.push(`Detected: ${fw} (runtime=${config2.app.runtime})`);
|
|
118552
|
-
if (config2.app.runtime === "node" || config2.app.runtime === "bun") {
|
|
118553
|
-
const node = resolveNodeVersion(tarball.cwd);
|
|
118554
|
-
if (node) {
|
|
118555
|
-
lines.push(`Node: ${node.value} (${node.source})`);
|
|
118556
|
-
}
|
|
118557
|
-
}
|
|
118558
|
-
if (config2.build?.command) {
|
|
118559
|
-
lines.push(`Build: ${config2.build.command}`);
|
|
118560
|
-
}
|
|
118561
|
-
if (config2.web?.health) {
|
|
118562
|
-
lines.push(`Health: ${config2.web.health}`);
|
|
118563
|
-
}
|
|
118564
|
-
if (config2.web?.port) {
|
|
118565
|
-
lines.push(`Port: ${config2.web.port}`);
|
|
118566
|
-
}
|
|
118567
|
-
const sizeKb = (tarball.bytes / 1024).toFixed(1);
|
|
118568
|
-
lines.push(`Bundle: ${tarball.fileCount} files, ${sizeKb} KB`);
|
|
118569
|
-
lines.push(`Target: ${config2.app.name}`);
|
|
118570
|
-
for (const line of lines) {
|
|
118571
|
-
ctx.status(line);
|
|
118572
|
-
}
|
|
118573
|
-
}
|
|
118574
118772
|
// ../core/dist/commands/publish-from-repo.js
|
|
118575
118773
|
init_dist();
|
|
118576
118774
|
var publishFromRepoInputSchema = exports_external.object({
|
|
@@ -118601,7 +118799,7 @@ var publishInlineInputSchema = exports_external.object({
|
|
|
118601
118799
|
// ../core/dist/commands/push.js
|
|
118602
118800
|
init_dist();
|
|
118603
118801
|
import { existsSync as existsSync16 } from "node:fs";
|
|
118604
|
-
import { join as
|
|
118802
|
+
import { join as join26 } from "node:path";
|
|
118605
118803
|
var pushInputSchema = exports_external.object({
|
|
118606
118804
|
dryRun: exports_external.boolean().optional(),
|
|
118607
118805
|
force: exports_external.boolean().optional(),
|
|
@@ -118612,7 +118810,7 @@ var SUSPICIOUS_BYTES2 = 50 * 1024 * 1024;
|
|
|
118612
118810
|
var SUSPICIOUS_FILES2 = 1e4;
|
|
118613
118811
|
var PLACEHOLDER_PREFIXES = ["add-your-", "your-key-here"];
|
|
118614
118812
|
async function loadOrGenerate2(ctx) {
|
|
118615
|
-
const tomlPath =
|
|
118813
|
+
const tomlPath = join26(ctx.cwd, "percher.toml");
|
|
118616
118814
|
if (!existsSync16(tomlPath)) {
|
|
118617
118815
|
ctx.status("No percher.toml found — detecting framework...");
|
|
118618
118816
|
const result = await init(ctx);
|
|
@@ -118724,7 +118922,7 @@ async function push(ctx, input = {}, internals = {}) {
|
|
|
118724
118922
|
fileCount: tarball.fileCount,
|
|
118725
118923
|
recovery: RECOVERY_NONE,
|
|
118726
118924
|
summary: `Dry run for ${config2.app.name}: ${tarball.fileCount} files, ${(tarball.bytes / 1024).toFixed(1)} KB — no deploy performed.`,
|
|
118727
|
-
configPath:
|
|
118925
|
+
configPath: join26(ctx.cwd, "percher.toml"),
|
|
118728
118926
|
bundle: { fileCount: tarball.fileCount, bytes: tarball.bytes }
|
|
118729
118927
|
};
|
|
118730
118928
|
}
|
|
@@ -118912,7 +119110,7 @@ ${deployment.errorMessage}` : "";
|
|
|
118912
119110
|
fileCount: tarball.fileCount,
|
|
118913
119111
|
recovery: RECOVERY_NONE,
|
|
118914
119112
|
summary,
|
|
118915
|
-
configPath:
|
|
119113
|
+
configPath: join26(ctx.cwd, "percher.toml"),
|
|
118916
119114
|
bundle: { fileCount: tarball.fileCount, bytes: tarball.bytes }
|
|
118917
119115
|
};
|
|
118918
119116
|
}
|
|
@@ -119241,8 +119439,8 @@ function failureResult(error48) {
|
|
|
119241
119439
|
};
|
|
119242
119440
|
}
|
|
119243
119441
|
// ../core/dist/commands/rename.js
|
|
119244
|
-
import { existsSync as existsSync17, readFileSync as
|
|
119245
|
-
import { join as
|
|
119442
|
+
import { existsSync as existsSync17, readFileSync as readFileSync13, writeFileSync as writeFileSync10 } from "node:fs";
|
|
119443
|
+
import { join as join27 } from "node:path";
|
|
119246
119444
|
var renameInputSchema = exports_external.object({
|
|
119247
119445
|
app: exports_external.string().optional(),
|
|
119248
119446
|
name: exports_external.string().min(3).max(40)
|
|
@@ -119262,17 +119460,17 @@ async function rename3(ctx, input) {
|
|
|
119262
119460
|
};
|
|
119263
119461
|
}
|
|
119264
119462
|
function updateLocalTomlName(cwd, oldName, newName) {
|
|
119265
|
-
const p =
|
|
119463
|
+
const p = join27(cwd, "percher.toml");
|
|
119266
119464
|
if (!existsSync17(p))
|
|
119267
119465
|
return false;
|
|
119268
119466
|
try {
|
|
119269
|
-
const src =
|
|
119467
|
+
const src = readFileSync13(p, "utf8");
|
|
119270
119468
|
if (parse5(src).app.name !== oldName)
|
|
119271
119469
|
return false;
|
|
119272
119470
|
const next = src.replace(new RegExp(`(name\\s*=\\s*")${oldName}(")`), `$1${newName}$2`);
|
|
119273
119471
|
if (next === src || parse5(next).app.name !== newName)
|
|
119274
119472
|
return false;
|
|
119275
|
-
|
|
119473
|
+
writeFileSync10(p, next);
|
|
119276
119474
|
return true;
|
|
119277
119475
|
} catch {
|
|
119278
119476
|
return false;
|
|
@@ -119281,9 +119479,9 @@ function updateLocalTomlName(cwd, oldName, newName) {
|
|
|
119281
119479
|
// ../core/dist/commands/reproduce.js
|
|
119282
119480
|
init_dist();
|
|
119283
119481
|
var import_cross_spawn3 = __toESM(require_cross_spawn(), 1);
|
|
119284
|
-
import { mkdtempSync, writeFileSync as
|
|
119482
|
+
import { mkdtempSync, writeFileSync as writeFileSync11 } from "node:fs";
|
|
119285
119483
|
import { tmpdir } from "node:os";
|
|
119286
|
-
import { join as
|
|
119484
|
+
import { join as join28 } from "node:path";
|
|
119287
119485
|
function runProcessExit(cmd, args, opts = {}) {
|
|
119288
119486
|
return new Promise((resolve11, reject) => {
|
|
119289
119487
|
const child = import_cross_spawn3.default(cmd, args, {
|
|
@@ -119377,9 +119575,9 @@ async function reproduce(ctx, input) {
|
|
|
119377
119575
|
const envMap = await ctx.client.env.list(app.id);
|
|
119378
119576
|
currentAppEnvKeys = Object.keys(envMap).sort();
|
|
119379
119577
|
} catch {}
|
|
119380
|
-
const tmpDir = mkdtempSync(
|
|
119381
|
-
const tarballPath =
|
|
119382
|
-
|
|
119578
|
+
const tmpDir = mkdtempSync(join28(tmpdir(), `percher-reproduce-${input.deployId}-`));
|
|
119579
|
+
const tarballPath = join28(tmpDir, "source.tar.gz");
|
|
119580
|
+
writeFileSync11(tarballPath, bytes);
|
|
119383
119581
|
let extracted = false;
|
|
119384
119582
|
try {
|
|
119385
119583
|
extracted = await runProcessExit("tar", ["-xzf", tarballPath, "-C", tmpDir]) === 0;
|
|
@@ -119977,6 +120175,7 @@ var SOURCE_EXTS2 = new Set([
|
|
|
119977
120175
|
".py"
|
|
119978
120176
|
]);
|
|
119979
120177
|
var DEFAULT_MAX_FILE_BYTES = 1024 * 1024;
|
|
120178
|
+
var IMPORT_META_CONSTANTS = new Set(["DEV", "PROD", "MODE", "BASE_URL", "SSR"]);
|
|
119980
120179
|
// ../core/dist/plans.js
|
|
119981
120180
|
var PB_INITIAL_FOOTPRINT_MB = 256;
|
|
119982
120181
|
var EGRESS_GB_PER_MONTH = {
|
|
@@ -120183,6 +120382,15 @@ async function writeLine2(ctx, line) {
|
|
|
120183
120382
|
writer.releaseLock();
|
|
120184
120383
|
}
|
|
120185
120384
|
}
|
|
120385
|
+
async function writeErrLine(ctx, line) {
|
|
120386
|
+
const writer = ctx.stderr.getWriter();
|
|
120387
|
+
try {
|
|
120388
|
+
await writer.write(new TextEncoder().encode(`${line}
|
|
120389
|
+
`));
|
|
120390
|
+
} finally {
|
|
120391
|
+
writer.releaseLock();
|
|
120392
|
+
}
|
|
120393
|
+
}
|
|
120186
120394
|
async function writeUnknownCommand(ctx, name) {
|
|
120187
120395
|
const writer = ctx.stderr.getWriter();
|
|
120188
120396
|
try {
|
|
@@ -121578,6 +121786,7 @@ function registerDeploysCommands(cli, ctx, argv) {
|
|
|
121578
121786
|
rebuild: Boolean(options.rebuild)
|
|
121579
121787
|
});
|
|
121580
121788
|
await writeLine2(ctx, `${import_picocolors8.default.green("✓")} rolled back to ${result.rolledBackTo.slice(0, 10)} (status: ${result.status})`);
|
|
121789
|
+
await writeLine2(ctx, "Env vars are not rolled back; the current values stay.");
|
|
121581
121790
|
});
|
|
121582
121791
|
cli.command("reproduce <deployId>", "Download a deploy's source and reproduce the build locally").option("--app <name>", "App name (defaults to percher.toml app.name)").action(async (deployId, options) => {
|
|
121583
121792
|
const result = await reproduce(ctx, {
|
|
@@ -121656,6 +121865,8 @@ function registerDeploysCommands(cli, ctx, argv) {
|
|
|
121656
121865
|
for (const v of list) {
|
|
121657
121866
|
const message = v.message.split(`
|
|
121658
121867
|
`)[0]?.trim() ?? "";
|
|
121868
|
+
const note = Array.from((v.note ?? "").replace(/[\p{Cc}\p{Cf}\p{Cs}\p{Zl}\p{Zp}]/gu, " ").replace(/\s+/g, " ").trim());
|
|
121869
|
+
const noteLabel = note.length > 80 ? `${note.slice(0, 79).join("")}…` : note.join("");
|
|
121659
121870
|
const channelLabels = {
|
|
121660
121871
|
cli: "CLI",
|
|
121661
121872
|
mcp: "MCP",
|
|
@@ -121670,7 +121881,7 @@ function registerDeploysCommands(cli, ctx, argv) {
|
|
|
121670
121881
|
const marker = target && target.mode !== "in-window" ? target.mode === "rebuild" ? "[rebuild required]" : "[not a rollback target]" : null;
|
|
121671
121882
|
if (marker && target)
|
|
121672
121883
|
reasons.set(marker, target.explanation);
|
|
121673
|
-
await writeLine2(ctx, `${v.sha} ${v.timestamp} ${message}${suffix}${marker ? ` ${marker}` : ""}`);
|
|
121884
|
+
await writeLine2(ctx, `${v.sha} ${v.timestamp} ${message}${noteLabel ? ` · ${noteLabel}` : ""}${suffix}${marker ? ` ${marker}` : ""}`);
|
|
121674
121885
|
}
|
|
121675
121886
|
for (const [marker, explanation] of reasons) {
|
|
121676
121887
|
await writeLine2(ctx, `
|
|
@@ -122376,8 +122587,209 @@ async function renderHumanStatus(ctx, result) {
|
|
|
122376
122587
|
}
|
|
122377
122588
|
}
|
|
122378
122589
|
|
|
122379
|
-
// src/commands/
|
|
122590
|
+
// src/commands/telemetry.ts
|
|
122591
|
+
init_dist();
|
|
122380
122592
|
var import_picocolors16 = __toESM(require_picocolors(), 1);
|
|
122593
|
+
|
|
122594
|
+
// src/telemetry.ts
|
|
122595
|
+
init_dist();
|
|
122596
|
+
import { createInterface } from "node:readline";
|
|
122597
|
+
// package.json
|
|
122598
|
+
var package_default2 = {
|
|
122599
|
+
name: "percher",
|
|
122600
|
+
version: "1.2.0",
|
|
122601
|
+
description: "CLI for the Percher hosting platform — publish apps with one command",
|
|
122602
|
+
type: "module",
|
|
122603
|
+
bin: {
|
|
122604
|
+
percher: "./dist/bin/percher.js"
|
|
122605
|
+
},
|
|
122606
|
+
main: "./dist/src/index.js",
|
|
122607
|
+
types: "./dist/src/index.d.ts",
|
|
122608
|
+
exports: {
|
|
122609
|
+
".": {
|
|
122610
|
+
types: "./dist/src/index.d.ts",
|
|
122611
|
+
import: "./dist/src/index.js",
|
|
122612
|
+
default: "./dist/src/index.js"
|
|
122613
|
+
}
|
|
122614
|
+
},
|
|
122615
|
+
files: [
|
|
122616
|
+
"dist",
|
|
122617
|
+
"THIRD-PARTY-NOTICES.txt"
|
|
122618
|
+
],
|
|
122619
|
+
publishConfig: {
|
|
122620
|
+
access: "public"
|
|
122621
|
+
},
|
|
122622
|
+
scripts: {
|
|
122623
|
+
build: `tsc -p tsconfig.json && tsc-alias -p tsconfig.json && bun build bin/percher.ts --outfile dist/bin/percher.js --target node --format esm --metafile=bundle-meta.json && node ../../scripts/bundle-notices.mjs . bundle-meta.json && node scripts/copy-templates.mjs && node -e "require('fs').chmodSync('dist/bin/percher.js',0o755)"`,
|
|
122624
|
+
test: "bun test",
|
|
122625
|
+
typecheck: "tsc --noEmit"
|
|
122626
|
+
},
|
|
122627
|
+
dependencies: {
|
|
122628
|
+
"@percher/client": "^1.2.0",
|
|
122629
|
+
"@percher/core": "^1.2.0",
|
|
122630
|
+
"@percher/toml": "^1.0.3",
|
|
122631
|
+
cac: "^7.0.0",
|
|
122632
|
+
picocolors: "^1.1.1"
|
|
122633
|
+
},
|
|
122634
|
+
devDependencies: {
|
|
122635
|
+
"bun-types": "^1.1.0",
|
|
122636
|
+
"tsc-alias": "^1.8.17"
|
|
122637
|
+
},
|
|
122638
|
+
engines: {
|
|
122639
|
+
bun: ">=1.0.0",
|
|
122640
|
+
node: ">=22.0.0"
|
|
122641
|
+
},
|
|
122642
|
+
license: "FSL-1.1-MIT",
|
|
122643
|
+
author: "Mattias Malmborg",
|
|
122644
|
+
repository: {
|
|
122645
|
+
type: "git",
|
|
122646
|
+
url: "https://github.com/mattiasmalmborg/percher"
|
|
122647
|
+
},
|
|
122648
|
+
keywords: [
|
|
122649
|
+
"hosting",
|
|
122650
|
+
"deploy",
|
|
122651
|
+
"cli",
|
|
122652
|
+
"paas",
|
|
122653
|
+
"bun",
|
|
122654
|
+
"ai"
|
|
122655
|
+
]
|
|
122656
|
+
};
|
|
122657
|
+
|
|
122658
|
+
// src/telemetry.ts
|
|
122659
|
+
var VERSION = package_default2.version;
|
|
122660
|
+
function trackEvent(command, result, errorType) {
|
|
122661
|
+
if (!telemetryState().enabled)
|
|
122662
|
+
return;
|
|
122663
|
+
const body = JSON.stringify({
|
|
122664
|
+
cliVersion: VERSION,
|
|
122665
|
+
command,
|
|
122666
|
+
result,
|
|
122667
|
+
errorType,
|
|
122668
|
+
os: `${process.platform}-${process.arch}`,
|
|
122669
|
+
bunVersion: typeof Bun !== "undefined" ? Bun.version : undefined
|
|
122670
|
+
});
|
|
122671
|
+
fetch(`${resolveApiUrl()}/telemetry/cli`, {
|
|
122672
|
+
method: "POST",
|
|
122673
|
+
headers: { "Content-Type": "application/json" },
|
|
122674
|
+
body,
|
|
122675
|
+
signal: AbortSignal.timeout(3000)
|
|
122676
|
+
}).catch(() => {});
|
|
122677
|
+
}
|
|
122678
|
+
function lineAsker(input, output, onInterrupt = () => process.exit(130)) {
|
|
122679
|
+
return (question, signal) => new Promise((resolve11) => {
|
|
122680
|
+
const rl = createInterface({
|
|
122681
|
+
input,
|
|
122682
|
+
output,
|
|
122683
|
+
terminal: input.isTTY === true
|
|
122684
|
+
});
|
|
122685
|
+
let settled = false;
|
|
122686
|
+
const finish = (answer) => {
|
|
122687
|
+
if (settled)
|
|
122688
|
+
return;
|
|
122689
|
+
settled = true;
|
|
122690
|
+
signal.removeEventListener("abort", onAbort);
|
|
122691
|
+
if (answer === null)
|
|
122692
|
+
output.write(`
|
|
122693
|
+
`);
|
|
122694
|
+
resolve11(answer);
|
|
122695
|
+
rl.close();
|
|
122696
|
+
};
|
|
122697
|
+
const onAbort = () => finish(null);
|
|
122698
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
122699
|
+
rl.on("line", (line) => finish(line));
|
|
122700
|
+
rl.on("close", () => finish(null));
|
|
122701
|
+
rl.on("SIGINT", () => {
|
|
122702
|
+
finish(null);
|
|
122703
|
+
onInterrupt();
|
|
122704
|
+
});
|
|
122705
|
+
rl.setPrompt(question);
|
|
122706
|
+
rl.prompt();
|
|
122707
|
+
if (signal.aborted)
|
|
122708
|
+
finish(null);
|
|
122709
|
+
});
|
|
122710
|
+
}
|
|
122711
|
+
var askOnStdin = lineAsker(process.stdin, process.stderr);
|
|
122712
|
+
var ANSWER_TIMEOUT_MS = 20000;
|
|
122713
|
+
var CI_VARS = ["CONTINUOUS_INTEGRATION", "BUILD_NUMBER", "TF_BUILD", "JENKINS_URL"];
|
|
122714
|
+
var AGENT_VARS = [
|
|
122715
|
+
"CLAUDECODE",
|
|
122716
|
+
"CURSOR_AGENT",
|
|
122717
|
+
"CURSOR_TRACE_ID",
|
|
122718
|
+
"CODEX_SANDBOX",
|
|
122719
|
+
"CODEX_CI",
|
|
122720
|
+
"GITHUB_ACTIONS"
|
|
122721
|
+
];
|
|
122722
|
+
function isUnattended(env) {
|
|
122723
|
+
const ci = env.CI;
|
|
122724
|
+
if (ci !== undefined && ci !== "" && ci !== "0" && ci !== "false")
|
|
122725
|
+
return true;
|
|
122726
|
+
return [...CI_VARS, ...AGENT_VARS].some((key) => Boolean(env[key]));
|
|
122727
|
+
}
|
|
122728
|
+
var realTimer = (fire, ms) => {
|
|
122729
|
+
const handle = setTimeout(fire, ms);
|
|
122730
|
+
return () => clearTimeout(handle);
|
|
122731
|
+
};
|
|
122732
|
+
async function askTelemetryConsentOnce(ctx, argv, command, question) {
|
|
122733
|
+
if (command === "telemetry" || argv.includes("--json"))
|
|
122734
|
+
return;
|
|
122735
|
+
if (ctx.stdinIsInteractive !== true || !question.stderrIsTTY)
|
|
122736
|
+
return;
|
|
122737
|
+
if (isUnattended(ctx.env))
|
|
122738
|
+
return;
|
|
122739
|
+
const state = telemetryState();
|
|
122740
|
+
if (state.envOptOut || state.stored)
|
|
122741
|
+
return;
|
|
122742
|
+
const timeout = new AbortController;
|
|
122743
|
+
const cancel = (question.setTimer ?? realTimer)(() => timeout.abort(), ANSWER_TIMEOUT_MS);
|
|
122744
|
+
let answer;
|
|
122745
|
+
try {
|
|
122746
|
+
answer = await question.ask("Send anonymous usage statistics to Percher? (y/N) ", timeout.signal);
|
|
122747
|
+
} finally {
|
|
122748
|
+
cancel();
|
|
122749
|
+
}
|
|
122750
|
+
if (answer === null)
|
|
122751
|
+
return;
|
|
122752
|
+
const enabled = /^y(es)?$/i.test(answer.trim());
|
|
122753
|
+
try {
|
|
122754
|
+
saveTelemetryAnswer(enabled, VERSION);
|
|
122755
|
+
} catch {
|
|
122756
|
+
await writeErrLine(ctx, "Could not save the answer; you will be asked again.");
|
|
122757
|
+
return;
|
|
122758
|
+
}
|
|
122759
|
+
await writeErrLine(ctx, enabled ? "Usage statistics on. Turn off: percher telemetry off" : "Usage statistics off. Turn on: percher telemetry on");
|
|
122760
|
+
}
|
|
122761
|
+
function telemetryStatusLine(state = telemetryState()) {
|
|
122762
|
+
const stored = state.stored ? `stored answer: ${state.stored.enabled ? "on" : "off"}` : null;
|
|
122763
|
+
if (state.envOptOut) {
|
|
122764
|
+
return `Telemetry: off (env opt-out: ${state.envOptOut}${stored ? `; ${stored}` : ""})`;
|
|
122765
|
+
}
|
|
122766
|
+
if (state.stored)
|
|
122767
|
+
return `Telemetry: ${state.stored.enabled ? "on" : "off"} (stored answer)`;
|
|
122768
|
+
return "Telemetry: off (undecided)";
|
|
122769
|
+
}
|
|
122770
|
+
|
|
122771
|
+
// src/commands/telemetry.ts
|
|
122772
|
+
function registerTelemetryCommands(cli, ctx) {
|
|
122773
|
+
cli.command("telemetry [action]", "Anonymous usage statistics: on | off | status").action(async (action) => {
|
|
122774
|
+
if (action === "on" || action === "off") {
|
|
122775
|
+
try {
|
|
122776
|
+
saveTelemetryAnswer(action === "on", VERSION);
|
|
122777
|
+
} catch {
|
|
122778
|
+
await writeErrLine(ctx, `${import_picocolors16.default.red("✗")} Could not save the answer.`);
|
|
122779
|
+
throw new SilentExitError;
|
|
122780
|
+
}
|
|
122781
|
+
} else if (action !== undefined && action !== "status") {
|
|
122782
|
+
await writeErrLine(ctx, `${import_picocolors16.default.red("✗")} Usage: percher telemetry on|off|status`);
|
|
122783
|
+
throw new SilentExitError;
|
|
122784
|
+
}
|
|
122785
|
+
const state = telemetryState();
|
|
122786
|
+
const overridden = action === "on" && state.envOptOut !== null;
|
|
122787
|
+
await writeLine2(ctx, `${telemetryStatusLine(state)}${overridden ? "; the environment variable still wins." : ""}`);
|
|
122788
|
+
});
|
|
122789
|
+
}
|
|
122790
|
+
|
|
122791
|
+
// src/commands/token.ts
|
|
122792
|
+
var import_picocolors17 = __toESM(require_picocolors(), 1);
|
|
122381
122793
|
var VALID_SCOPES = ["deploy", "env", "read", "full"];
|
|
122382
122794
|
function scopeLabel(scopes, appName) {
|
|
122383
122795
|
const s = scopes && scopes.length > 0 ? scopes.join("+") : "full";
|
|
@@ -122386,22 +122798,22 @@ function scopeLabel(scopes, appName) {
|
|
|
122386
122798
|
function registerTokenCommands(cli, ctx) {
|
|
122387
122799
|
cli.command("token [action] [arg]", "Manage API tokens: create | list | revoke").option("--scope <scope>", "(create) deploy | env | read | full (default: full, account-wide unless --app)").option("--app <name>", "(create) bind the token to a single app").option("--ttl-days <n>", "(create) token lifetime in days (default 90, max 365)").action(async (action, arg, options) => {
|
|
122388
122800
|
if (!ctx.client.token) {
|
|
122389
|
-
await writeLine2(ctx, `${
|
|
122801
|
+
await writeLine2(ctx, `${import_picocolors17.default.red("✗")} Not logged in. Run: percher login`);
|
|
122390
122802
|
throw new SilentExitError;
|
|
122391
122803
|
}
|
|
122392
122804
|
if (action === "create") {
|
|
122393
122805
|
if (!arg) {
|
|
122394
|
-
await writeLine2(ctx, `${
|
|
122806
|
+
await writeLine2(ctx, `${import_picocolors17.default.red("✗")} A name is required. Usage: percher token create <name> [--scope deploy] [--app <name>]`);
|
|
122395
122807
|
throw new SilentExitError;
|
|
122396
122808
|
}
|
|
122397
122809
|
const scope = options.scope ?? "full";
|
|
122398
122810
|
if (!VALID_SCOPES.includes(scope)) {
|
|
122399
|
-
await writeLine2(ctx, `${
|
|
122811
|
+
await writeLine2(ctx, `${import_picocolors17.default.red("✗")} Invalid --scope "${options.scope}". Use one of: ${VALID_SCOPES.join(", ")}.`);
|
|
122400
122812
|
throw new SilentExitError;
|
|
122401
122813
|
}
|
|
122402
122814
|
const ttlDays = options.ttlDays !== undefined ? Number(options.ttlDays) : undefined;
|
|
122403
122815
|
if (ttlDays !== undefined && (!Number.isInteger(ttlDays) || ttlDays <= 0)) {
|
|
122404
|
-
await writeLine2(ctx, `${
|
|
122816
|
+
await writeLine2(ctx, `${import_picocolors17.default.red("✗")} --ttl-days must be a positive whole number.`);
|
|
122405
122817
|
throw new SilentExitError;
|
|
122406
122818
|
}
|
|
122407
122819
|
const created = await tokenCreate(ctx, {
|
|
@@ -122410,36 +122822,36 @@ function registerTokenCommands(cli, ctx) {
|
|
|
122410
122822
|
app: options.app,
|
|
122411
122823
|
ttlDays
|
|
122412
122824
|
});
|
|
122413
|
-
await writeLine2(ctx, `${
|
|
122825
|
+
await writeLine2(ctx, `${import_picocolors17.default.green("✓")} Token created: ${import_picocolors17.default.bold(created.name)} ${import_picocolors17.default.dim(`(${scopeLabel(created.scopes, created.appName)})`)}`);
|
|
122414
122826
|
await writeLine2(ctx, "");
|
|
122415
|
-
await writeLine2(ctx, ` ${
|
|
122416
|
-
await writeLine2(ctx,
|
|
122827
|
+
await writeLine2(ctx, ` ${import_picocolors17.default.bold(created.token)}`);
|
|
122828
|
+
await writeLine2(ctx, import_picocolors17.default.yellow(" ↑ Store this now — the secret is shown only once and can't be retrieved."));
|
|
122417
122829
|
await writeLine2(ctx, "");
|
|
122418
|
-
await writeLine2(ctx,
|
|
122419
|
-
await writeLine2(ctx,
|
|
122830
|
+
await writeLine2(ctx, import_picocolors17.default.dim(" Use it in CI / an agent:"));
|
|
122831
|
+
await writeLine2(ctx, import_picocolors17.default.dim(` PERCHER_TOKEN=${created.token} bunx percher publish`));
|
|
122420
122832
|
return;
|
|
122421
122833
|
}
|
|
122422
122834
|
if (action === "list") {
|
|
122423
122835
|
const tokens = await tokenList(ctx);
|
|
122424
122836
|
if (tokens.length === 0) {
|
|
122425
|
-
await writeLine2(ctx,
|
|
122837
|
+
await writeLine2(ctx, import_picocolors17.default.dim("No API tokens."));
|
|
122426
122838
|
return;
|
|
122427
122839
|
}
|
|
122428
122840
|
for (const t of tokens) {
|
|
122429
122841
|
const expires = t.expiresAt ? `expires ${new Date(t.expiresAt).toISOString().slice(0, 10)}` : "no expiry";
|
|
122430
|
-
await writeLine2(ctx, `${
|
|
122842
|
+
await writeLine2(ctx, `${import_picocolors17.default.bold(t.id)} ${t.name.padEnd(20)} ${import_picocolors17.default.dim(scopeLabel(t.scopes, t.appName))} ${import_picocolors17.default.dim(expires)}`);
|
|
122431
122843
|
}
|
|
122432
122844
|
await writeLine2(ctx, "");
|
|
122433
|
-
await writeLine2(ctx,
|
|
122845
|
+
await writeLine2(ctx, import_picocolors17.default.dim("Revoke one with: percher token revoke <id>"));
|
|
122434
122846
|
return;
|
|
122435
122847
|
}
|
|
122436
122848
|
if (action === "revoke") {
|
|
122437
122849
|
if (!arg) {
|
|
122438
|
-
await writeLine2(ctx, `${
|
|
122850
|
+
await writeLine2(ctx, `${import_picocolors17.default.red("✗")} A token id is required. Usage: percher token revoke <id> (list ids with \`percher token list\`).`);
|
|
122439
122851
|
throw new SilentExitError;
|
|
122440
122852
|
}
|
|
122441
122853
|
await tokenRevoke(ctx, { id: arg });
|
|
122442
|
-
await writeLine2(ctx, `${
|
|
122854
|
+
await writeLine2(ctx, `${import_picocolors17.default.green("✓")} Token revoked.`);
|
|
122443
122855
|
return;
|
|
122444
122856
|
}
|
|
122445
122857
|
throw new Error(`Unknown token action "${action ?? ""}". Use: create | list | revoke.`);
|
|
@@ -122450,17 +122862,17 @@ function registerTokenCommands(cli, ctx) {
|
|
|
122450
122862
|
}
|
|
122451
122863
|
|
|
122452
122864
|
// src/commands/transfers.ts
|
|
122453
|
-
var
|
|
122865
|
+
var import_picocolors18 = __toESM(require_picocolors(), 1);
|
|
122454
122866
|
function registerTransfersCommands(cli, ctx) {
|
|
122455
122867
|
cli.command("transfer [emailOrTransferId]", "Transfer ownership of an app: `transfer <email>` initiates; `transfer --cancel <id>` cancels; `transfer --list` shows history").option("--app <name>", "App name (overrides percher.toml)").option("--cancel", "Cancel a pending transfer by id (positional arg is the transferId)").option("--list", "List the transfer history for this app").action(async (emailOrTransferId, options) => {
|
|
122456
122868
|
if (options.list) {
|
|
122457
122869
|
const rows = await transferListForApp(ctx, { app: options.app });
|
|
122458
122870
|
if (rows.length === 0) {
|
|
122459
|
-
await writeLine2(ctx,
|
|
122871
|
+
await writeLine2(ctx, import_picocolors18.default.dim("No transfers for this app."));
|
|
122460
122872
|
return;
|
|
122461
122873
|
}
|
|
122462
122874
|
for (const r of rows) {
|
|
122463
|
-
const status2 = r.status === "pending" ?
|
|
122875
|
+
const status2 = r.status === "pending" ? import_picocolors18.default.yellow("pending") : r.status === "accepted" ? import_picocolors18.default.green("accepted") : import_picocolors18.default.dim(r.status);
|
|
122464
122876
|
await writeLine2(ctx, `${r.id} ${status2.padEnd(20)} → ${r.toEmail} (${r.createdAt})`);
|
|
122465
122877
|
}
|
|
122466
122878
|
return;
|
|
@@ -122473,7 +122885,7 @@ function registerTransfersCommands(cli, ctx) {
|
|
|
122473
122885
|
app: options.app,
|
|
122474
122886
|
transferId: emailOrTransferId
|
|
122475
122887
|
});
|
|
122476
|
-
await writeLine2(ctx, `${
|
|
122888
|
+
await writeLine2(ctx, `${import_picocolors18.default.green("✓")} Cancelled transfer ${emailOrTransferId}`);
|
|
122477
122889
|
return;
|
|
122478
122890
|
}
|
|
122479
122891
|
if (!emailOrTransferId) {
|
|
@@ -122483,17 +122895,17 @@ function registerTransfersCommands(cli, ctx) {
|
|
|
122483
122895
|
app: options.app,
|
|
122484
122896
|
email: emailOrTransferId
|
|
122485
122897
|
});
|
|
122486
|
-
await writeLine2(ctx, `${
|
|
122487
|
-
await writeLine2(ctx,
|
|
122898
|
+
await writeLine2(ctx, `${import_picocolors18.default.green("✓")} Transfer initiated to ${initiated.toEmail} (id ${initiated.id})`);
|
|
122899
|
+
await writeLine2(ctx, import_picocolors18.default.dim(`Expires ${new Date(initiated.expiresAt).toLocaleDateString()}. The recipient must accept via 'percher transfers accept ${initiated.id}'.`));
|
|
122488
122900
|
if (initiated.notes) {
|
|
122489
|
-
await writeLine2(ctx,
|
|
122901
|
+
await writeLine2(ctx, import_picocolors18.default.yellow(` ⚠ ${initiated.notes}`));
|
|
122490
122902
|
}
|
|
122491
122903
|
});
|
|
122492
122904
|
cli.command("transfers [action] [transferId]", "Incoming ownership transfers: list (default) | accept <transferId> | decline <transferId>").action(async (action, transferId) => {
|
|
122493
122905
|
if (action === undefined || action === "list") {
|
|
122494
122906
|
const rows = await transfersIncoming(ctx, {});
|
|
122495
122907
|
if (rows.length === 0) {
|
|
122496
|
-
await writeLine2(ctx,
|
|
122908
|
+
await writeLine2(ctx, import_picocolors18.default.dim("No pending incoming transfers."));
|
|
122497
122909
|
return;
|
|
122498
122910
|
}
|
|
122499
122911
|
for (const r of rows) {
|
|
@@ -122506,7 +122918,7 @@ function registerTransfersCommands(cli, ctx) {
|
|
|
122506
122918
|
throw new Error("Usage: percher transfers accept <transferId>");
|
|
122507
122919
|
}
|
|
122508
122920
|
const accepted = await transfersAccept(ctx, { transferId });
|
|
122509
|
-
await writeLine2(ctx, `${
|
|
122921
|
+
await writeLine2(ctx, `${import_picocolors18.default.green("✓")} You now own ${import_picocolors18.default.cyan(accepted.appName)}. Run 'percher open ${accepted.appName}' to see it.`);
|
|
122510
122922
|
return;
|
|
122511
122923
|
}
|
|
122512
122924
|
if (action === "decline") {
|
|
@@ -122514,7 +122926,7 @@ function registerTransfersCommands(cli, ctx) {
|
|
|
122514
122926
|
throw new Error("Usage: percher transfers decline <transferId>");
|
|
122515
122927
|
}
|
|
122516
122928
|
await transfersDecline(ctx, { transferId });
|
|
122517
|
-
await writeLine2(ctx, `${
|
|
122929
|
+
await writeLine2(ctx, `${import_picocolors18.default.green("✓")} Declined transfer ${transferId}.`);
|
|
122518
122930
|
return;
|
|
122519
122931
|
}
|
|
122520
122932
|
throw new Error(`Unknown transfers action "${action}". Use list | accept <id> | decline <id>.`);
|
|
@@ -122543,9 +122955,9 @@ async function openUrl(url2) {
|
|
|
122543
122955
|
|
|
122544
122956
|
// src/prompt.ts
|
|
122545
122957
|
import { stdin as input, stdout as output } from "node:process";
|
|
122546
|
-
import { createInterface } from "node:readline/promises";
|
|
122958
|
+
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
122547
122959
|
async function prompt(question, options) {
|
|
122548
|
-
const rl =
|
|
122960
|
+
const rl = createInterface2({ input, output });
|
|
122549
122961
|
try {
|
|
122550
122962
|
const suffix = options?.default ? ` [${options.default}]` : "";
|
|
122551
122963
|
const answer = await rl.question(`${question}${suffix} `);
|
|
@@ -122555,111 +122967,6 @@ async function prompt(question, options) {
|
|
|
122555
122967
|
}
|
|
122556
122968
|
}
|
|
122557
122969
|
|
|
122558
|
-
// src/telemetry.ts
|
|
122559
|
-
init_dist();
|
|
122560
|
-
// package.json
|
|
122561
|
-
var package_default2 = {
|
|
122562
|
-
name: "percher",
|
|
122563
|
-
version: "1.1.0",
|
|
122564
|
-
description: "CLI for the Percher hosting platform — publish apps with one command",
|
|
122565
|
-
type: "module",
|
|
122566
|
-
bin: {
|
|
122567
|
-
percher: "./dist/bin/percher.js"
|
|
122568
|
-
},
|
|
122569
|
-
main: "./dist/src/index.js",
|
|
122570
|
-
types: "./dist/src/index.d.ts",
|
|
122571
|
-
exports: {
|
|
122572
|
-
".": {
|
|
122573
|
-
types: "./dist/src/index.d.ts",
|
|
122574
|
-
import: "./dist/src/index.js",
|
|
122575
|
-
default: "./dist/src/index.js"
|
|
122576
|
-
}
|
|
122577
|
-
},
|
|
122578
|
-
files: [
|
|
122579
|
-
"dist",
|
|
122580
|
-
"THIRD-PARTY-NOTICES.txt"
|
|
122581
|
-
],
|
|
122582
|
-
publishConfig: {
|
|
122583
|
-
access: "public"
|
|
122584
|
-
},
|
|
122585
|
-
scripts: {
|
|
122586
|
-
build: `tsc -p tsconfig.json && tsc-alias -p tsconfig.json && bun build bin/percher.ts --outfile dist/bin/percher.js --target node --format esm --metafile=bundle-meta.json && node ../../scripts/bundle-notices.mjs . bundle-meta.json && node scripts/copy-templates.mjs && node -e "require('fs').chmodSync('dist/bin/percher.js',0o755)"`,
|
|
122587
|
-
test: "bun test",
|
|
122588
|
-
typecheck: "tsc --noEmit"
|
|
122589
|
-
},
|
|
122590
|
-
dependencies: {
|
|
122591
|
-
"@percher/client": "^1.1.0",
|
|
122592
|
-
"@percher/core": "^1.1.0",
|
|
122593
|
-
"@percher/toml": "^1.0.3",
|
|
122594
|
-
cac: "^7.0.0",
|
|
122595
|
-
picocolors: "^1.1.1"
|
|
122596
|
-
},
|
|
122597
|
-
devDependencies: {
|
|
122598
|
-
"bun-types": "^1.1.0",
|
|
122599
|
-
"tsc-alias": "^1.8.17"
|
|
122600
|
-
},
|
|
122601
|
-
engines: {
|
|
122602
|
-
bun: ">=1.0.0",
|
|
122603
|
-
node: ">=22.0.0"
|
|
122604
|
-
},
|
|
122605
|
-
license: "FSL-1.1-MIT",
|
|
122606
|
-
author: "Mattias Malmborg",
|
|
122607
|
-
repository: {
|
|
122608
|
-
type: "git",
|
|
122609
|
-
url: "https://github.com/mattiasmalmborg/percher"
|
|
122610
|
-
},
|
|
122611
|
-
keywords: [
|
|
122612
|
-
"hosting",
|
|
122613
|
-
"deploy",
|
|
122614
|
-
"cli",
|
|
122615
|
-
"paas",
|
|
122616
|
-
"bun",
|
|
122617
|
-
"ai"
|
|
122618
|
-
]
|
|
122619
|
-
};
|
|
122620
|
-
|
|
122621
|
-
// src/telemetry.ts
|
|
122622
|
-
var VERSION = package_default2.version;
|
|
122623
|
-
function isDisabled() {
|
|
122624
|
-
return process.env.PERCHER_NO_TELEMETRY === "1" || process.env.PERCHER_NO_TELEMETRY === "true" || process.env.DO_NOT_TRACK === "1";
|
|
122625
|
-
}
|
|
122626
|
-
function trackEvent(command, result, errorType) {
|
|
122627
|
-
if (isDisabled())
|
|
122628
|
-
return;
|
|
122629
|
-
const body = JSON.stringify({
|
|
122630
|
-
cliVersion: VERSION,
|
|
122631
|
-
command,
|
|
122632
|
-
result,
|
|
122633
|
-
errorType,
|
|
122634
|
-
os: `${process.platform}-${process.arch}`,
|
|
122635
|
-
bunVersion: typeof Bun !== "undefined" ? Bun.version : undefined
|
|
122636
|
-
});
|
|
122637
|
-
fetch(`${resolveApiUrl()}/telemetry/cli`, {
|
|
122638
|
-
method: "POST",
|
|
122639
|
-
headers: { "Content-Type": "application/json" },
|
|
122640
|
-
body,
|
|
122641
|
-
signal: AbortSignal.timeout(3000)
|
|
122642
|
-
}).catch(() => {});
|
|
122643
|
-
}
|
|
122644
|
-
async function showTelemetryNotice() {
|
|
122645
|
-
if (isDisabled())
|
|
122646
|
-
return;
|
|
122647
|
-
const { homedir: homedir2 } = await import("node:os");
|
|
122648
|
-
const { existsSync: existsSync18, mkdirSync: mkdirSync7, writeFileSync: writeFileSync11 } = await import("node:fs");
|
|
122649
|
-
const { join: join28 } = await import("node:path");
|
|
122650
|
-
const configDir = join28(homedir2(), ".percher");
|
|
122651
|
-
const markerFile = join28(configDir, ".telemetry-notice-shown");
|
|
122652
|
-
if (existsSync18(markerFile))
|
|
122653
|
-
return;
|
|
122654
|
-
console.error("Percher sends non-account-linked usage stats (CLI version, command, result, error type, OS, Bun version).");
|
|
122655
|
-
console.error(`Disable with PERCHER_NO_TELEMETRY=1 or DO_NOT_TRACK=1
|
|
122656
|
-
`);
|
|
122657
|
-
try {
|
|
122658
|
-
mkdirSync7(configDir, { recursive: true });
|
|
122659
|
-
writeFileSync11(markerFile, new Date().toISOString());
|
|
122660
|
-
} catch {}
|
|
122661
|
-
}
|
|
122662
|
-
|
|
122663
122970
|
// src/context.ts
|
|
122664
122971
|
async function createCliContext() {
|
|
122665
122972
|
const client = new PercherClient({ clientVersion: `percher-cli/${VERSION}` });
|
|
@@ -122693,52 +123000,52 @@ async function createCliContext() {
|
|
|
122693
123000
|
|
|
122694
123001
|
// src/output.ts
|
|
122695
123002
|
init_dist();
|
|
122696
|
-
var
|
|
123003
|
+
var import_picocolors19 = __toESM(require_picocolors(), 1);
|
|
122697
123004
|
async function renderError(stderr, error48) {
|
|
122698
123005
|
const writer = stderr.getWriter();
|
|
122699
123006
|
const encoder = new TextEncoder;
|
|
122700
123007
|
const lines = [];
|
|
122701
123008
|
if (error48 instanceof PercherTomlError) {
|
|
122702
|
-
lines.push(`${
|
|
123009
|
+
lines.push(`${import_picocolors19.default.red(`error (${error48.code}):`)} ${error48.message}`);
|
|
122703
123010
|
if (error48.tomlLine !== undefined) {
|
|
122704
|
-
lines.push(
|
|
123011
|
+
lines.push(import_picocolors19.default.dim(` at percher.toml line ${error48.tomlLine}`));
|
|
122705
123012
|
}
|
|
122706
123013
|
for (const issue2 of error48.issues ?? []) {
|
|
122707
|
-
lines.push(`${
|
|
123014
|
+
lines.push(`${import_picocolors19.default.dim(" · ") + import_picocolors19.default.yellow(issue2.path.join("."))}: ${issue2.message}`);
|
|
122708
123015
|
}
|
|
122709
123016
|
} else if (error48 instanceof SunsetError) {
|
|
122710
123017
|
if (error48.gateCode === "platform_delete_gated") {
|
|
122711
|
-
lines.push(`${
|
|
123018
|
+
lines.push(`${import_picocolors19.default.yellow("Percher is shutting down — deletes are paused.")}`);
|
|
122712
123019
|
lines.push(error48.message);
|
|
122713
|
-
lines.push(
|
|
122714
|
-
lines.push(
|
|
123020
|
+
lines.push(import_picocolors19.default.dim(` To delete and permanently forfeit your export window, confirm with your password in the dashboard: ${error48.dashboardUrl}`));
|
|
123021
|
+
lines.push(import_picocolors19.default.dim(" The CLI cannot perform this delete during sunset — it must go through the dashboard."));
|
|
122715
123022
|
} else {
|
|
122716
|
-
lines.push(`${
|
|
123023
|
+
lines.push(`${import_picocolors19.default.yellow("Percher is in read-only mode ahead of shutdown.")}`);
|
|
122717
123024
|
lines.push(error48.message);
|
|
122718
|
-
lines.push(
|
|
123025
|
+
lines.push(import_picocolors19.default.dim(` Your apps keep running. Export them any time from the dashboard: ${error48.dashboardUrl}`));
|
|
122719
123026
|
}
|
|
122720
|
-
lines.push(
|
|
123027
|
+
lines.push(import_picocolors19.default.dim(` Final data deletion: ${error48.finalDeletionDate}`));
|
|
122721
123028
|
} else if (error48 instanceof PercherApiError) {
|
|
122722
123029
|
if (error48.status === 502 || error48.status === 503) {
|
|
122723
|
-
lines.push(`${
|
|
123030
|
+
lines.push(`${import_picocolors19.default.red("error:")} Percher API is temporarily unavailable (HTTP ${error48.status}). Try again in a few seconds.`);
|
|
122724
123031
|
} else {
|
|
122725
|
-
lines.push(`${
|
|
122726
|
-
lines.push(
|
|
123032
|
+
lines.push(`${import_picocolors19.default.red("error:")} ${error48.message}`);
|
|
123033
|
+
lines.push(import_picocolors19.default.dim(` ${error48.code} (HTTP ${error48.status})`));
|
|
122727
123034
|
}
|
|
122728
123035
|
if (error48.requestId)
|
|
122729
|
-
lines.push(
|
|
123036
|
+
lines.push(import_picocolors19.default.dim(` request: ${error48.requestId}`));
|
|
122730
123037
|
} else if (error48 instanceof PercherCoreError) {
|
|
122731
|
-
lines.push(`${
|
|
123038
|
+
lines.push(`${import_picocolors19.default.red(`error (${error48.code}):`)} ${error48.message}`);
|
|
122732
123039
|
if (error48.hint)
|
|
122733
|
-
lines.push(
|
|
123040
|
+
lines.push(import_picocolors19.default.dim(` ${error48.hint}`));
|
|
122734
123041
|
} else if (error48 instanceof Error) {
|
|
122735
|
-
lines.push(`${
|
|
123042
|
+
lines.push(`${import_picocolors19.default.red("unexpected error:")} ${error48.message}`);
|
|
122736
123043
|
if (error48.stack)
|
|
122737
|
-
lines.push(
|
|
123044
|
+
lines.push(import_picocolors19.default.dim(error48.stack.split(`
|
|
122738
123045
|
`).slice(1, 4).join(`
|
|
122739
123046
|
`)));
|
|
122740
123047
|
} else {
|
|
122741
|
-
lines.push(`${
|
|
123048
|
+
lines.push(`${import_picocolors19.default.red("unexpected error:")} ${String(error48)}`);
|
|
122742
123049
|
}
|
|
122743
123050
|
try {
|
|
122744
123051
|
await writer.write(encoder.encode(`${lines.join(`
|
|
@@ -122750,7 +123057,7 @@ async function renderError(stderr, error48) {
|
|
|
122750
123057
|
}
|
|
122751
123058
|
|
|
122752
123059
|
// src/version-check.ts
|
|
122753
|
-
var
|
|
123060
|
+
var import_picocolors20 = __toESM(require_picocolors(), 1);
|
|
122754
123061
|
function isNewerStableVersion(latest, current) {
|
|
122755
123062
|
const stable = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
|
|
122756
123063
|
if (typeof latest !== "string" || latest.length > 64 || current.length > 64 || !stable.test(latest) || !stable.test(current))
|
|
@@ -122777,16 +123084,16 @@ async function checkForUpdates() {
|
|
|
122777
123084
|
const data2 = await res.json();
|
|
122778
123085
|
const latest = data2.version;
|
|
122779
123086
|
if (isNewerStableVersion(latest, VERSION)) {
|
|
122780
|
-
console.error(
|
|
123087
|
+
console.error(import_picocolors20.default.yellow(`
|
|
122781
123088
|
Update available: ${VERSION} → ${latest}`));
|
|
122782
|
-
console.error(
|
|
123089
|
+
console.error(import_picocolors20.default.dim(`Run: bunx percher@latest
|
|
122783
123090
|
`));
|
|
122784
123091
|
}
|
|
122785
123092
|
} catch {}
|
|
122786
123093
|
}
|
|
122787
123094
|
|
|
122788
123095
|
// src/index.ts
|
|
122789
|
-
async function run(argv, ctxOverride) {
|
|
123096
|
+
async function run(argv, ctxOverride, options = {}) {
|
|
122790
123097
|
const cli = cac("percher");
|
|
122791
123098
|
const ctx = ctxOverride ?? await createCliContext();
|
|
122792
123099
|
registerAppsCommands(cli, ctx);
|
|
@@ -122802,6 +123109,7 @@ async function run(argv, ctxOverride) {
|
|
|
122802
123109
|
registerMigrateCommands(cli, ctx);
|
|
122803
123110
|
registerSharingCommands(cli, ctx);
|
|
122804
123111
|
registerStatusCommands(cli, ctx);
|
|
123112
|
+
registerTelemetryCommands(cli, ctx);
|
|
122805
123113
|
registerTokenCommands(cli, ctx);
|
|
122806
123114
|
registerTransfersCommands(cli, ctx);
|
|
122807
123115
|
registerAdminCommands(cli, ctx);
|
|
@@ -122809,10 +123117,14 @@ async function run(argv, ctxOverride) {
|
|
|
122809
123117
|
cli.version(VERSION);
|
|
122810
123118
|
const command = argv[0] ?? "help";
|
|
122811
123119
|
try {
|
|
122812
|
-
await showTelemetryNotice();
|
|
122813
123120
|
cli.parse(["percher", "percher", ...argv], { run: false });
|
|
122814
123121
|
const requested = argv[0];
|
|
122815
123122
|
if (cli.matchedCommand) {
|
|
123123
|
+
await askTelemetryConsentOnce(ctx, argv, cli.matchedCommand.name, {
|
|
123124
|
+
ask: options.askTelemetry ?? askOnStdin,
|
|
123125
|
+
stderrIsTTY: options.stderrIsTTY ?? process.stderr.isTTY === true,
|
|
123126
|
+
setTimer: options.setTimer
|
|
123127
|
+
});
|
|
122816
123128
|
await cli.runMatchedCommand();
|
|
122817
123129
|
} else if (requested === undefined || requested === "help") {
|
|
122818
123130
|
cli.outputHelp();
|