percher 0.5.22 → 0.5.24

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.
@@ -2154,6 +2154,71 @@ function listAnonClaimApps() {
2154
2154
  var DEFAULT_API_URL = "https://api.percher.run";
2155
2155
  var init_config = () => {};
2156
2156
 
2157
+ // ../client/package.json
2158
+ var package_default;
2159
+ var init_package = __esm(() => {
2160
+ package_default = {
2161
+ name: "@percher/client",
2162
+ version: "0.3.1",
2163
+ description: "Official TypeScript SDK for Percher: deploy apps, manage env vars, stream logs, and automate hosting workflows.",
2164
+ keywords: [
2165
+ "percher",
2166
+ "hosting",
2167
+ "deploy",
2168
+ "sdk",
2169
+ "api-client",
2170
+ "devops",
2171
+ "pocketbase",
2172
+ "nixpacks"
2173
+ ],
2174
+ type: "module",
2175
+ main: "./dist/index.js",
2176
+ types: "./dist/index.d.ts",
2177
+ exports: {
2178
+ ".": {
2179
+ types: "./dist/index.d.ts",
2180
+ import: "./dist/index.js",
2181
+ default: "./dist/index.js"
2182
+ },
2183
+ "./build-env-prefixes": {
2184
+ types: "./dist/build-env-prefixes.d.ts",
2185
+ import: "./dist/build-env-prefixes.js",
2186
+ default: "./dist/build-env-prefixes.js"
2187
+ },
2188
+ "./env-file": {
2189
+ types: "./dist/env-file.d.ts",
2190
+ import: "./dist/env-file.js",
2191
+ default: "./dist/env-file.js"
2192
+ }
2193
+ },
2194
+ files: [
2195
+ "dist"
2196
+ ],
2197
+ scripts: {
2198
+ build: "tsc -p tsconfig.json && tsc-alias -p tsconfig.json",
2199
+ test: "bun test",
2200
+ typecheck: "tsc --noEmit"
2201
+ },
2202
+ dependencies: {
2203
+ "@percher/toml": "^0.1.11"
2204
+ },
2205
+ devDependencies: {
2206
+ "bun-types": "^1.1.0",
2207
+ "tsc-alias": "^1.8.17"
2208
+ },
2209
+ license: "MIT",
2210
+ author: "Mattias Malmborg",
2211
+ repository: {
2212
+ type: "git",
2213
+ url: "https://github.com/mattiasmalmborg/percher",
2214
+ directory: "packages/client"
2215
+ },
2216
+ publishConfig: {
2217
+ access: "public"
2218
+ }
2219
+ };
2220
+ });
2221
+
2157
2222
  // ../client/dist/errors.js
2158
2223
  function isTransientNetworkError(err) {
2159
2224
  if (err instanceof NetworkError)
@@ -2350,7 +2415,7 @@ class Http {
2350
2415
  buildHeaders() {
2351
2416
  const headers = {
2352
2417
  Accept: "application/json",
2353
- "X-Percher-Client": this.clientVersion ?? "@percher/client/0.2.1"
2418
+ "X-Percher-Client": this.clientVersion ?? `@percher/client/${package_default.version}`
2354
2419
  };
2355
2420
  if (this.token)
2356
2421
  headers.Authorization = `Bearer ${this.token}`;
@@ -2407,6 +2472,7 @@ class Http {
2407
2472
  }
2408
2473
  var BODY_SNIPPET_MAX = 120;
2409
2474
  var init_http = __esm(() => {
2475
+ init_package();
2410
2476
  init_errors();
2411
2477
  });
2412
2478
 
@@ -3240,6 +3306,56 @@ class InvitesResource {
3240
3306
  }
3241
3307
  }
3242
3308
 
3309
+ // ../client/dist/resources/support.js
3310
+ class SupportResource {
3311
+ http;
3312
+ constructor(http) {
3313
+ this.http = http;
3314
+ }
3315
+ async list(opts = {}) {
3316
+ const params = new URLSearchParams;
3317
+ if (opts.limit !== undefined)
3318
+ params.set("limit", String(opts.limit));
3319
+ if (opts.offset !== undefined)
3320
+ params.set("offset", String(opts.offset));
3321
+ const qs = params.size > 0 ? `?${params.toString()}` : "";
3322
+ const res = await this.http.get(`/support/tickets${qs}`);
3323
+ return res.tickets;
3324
+ }
3325
+ get(ticketId) {
3326
+ return this.http.get(`/support/tickets/${encodeURIComponent(ticketId)}`);
3327
+ }
3328
+ async create(input) {
3329
+ const res = await this.http.post("/support/tickets", input);
3330
+ return res.ticket;
3331
+ }
3332
+ async reply(ticketId, body) {
3333
+ const res = await this.http.post(`/support/tickets/${encodeURIComponent(ticketId)}/messages`, { body });
3334
+ return res.message;
3335
+ }
3336
+ async close(ticketId) {
3337
+ const res = await this.http.post(`/support/tickets/${encodeURIComponent(ticketId)}/close`);
3338
+ return res.ticket;
3339
+ }
3340
+ async reopen(ticketId) {
3341
+ const res = await this.http.post(`/support/tickets/${encodeURIComponent(ticketId)}/reopen`);
3342
+ return res.ticket;
3343
+ }
3344
+ async uploadAttachment(ticketId, bytes, mime, filename) {
3345
+ const qs = filename ? `?filename=${encodeURIComponent(filename)}` : "";
3346
+ const ab = new ArrayBuffer(bytes.byteLength);
3347
+ new Uint8Array(ab).set(bytes);
3348
+ const res = await this.http.postBinary(`/support/tickets/${encodeURIComponent(ticketId)}/attachments${qs}`, new Blob([ab]), { "Content-Type": mime });
3349
+ return res.attachment;
3350
+ }
3351
+ downloadAttachment(ticketId, attachmentId) {
3352
+ return this.http.getBinary(`/support/tickets/${encodeURIComponent(ticketId)}/attachments/${encodeURIComponent(attachmentId)}`);
3353
+ }
3354
+ async deleteAttachment(ticketId, attachmentId) {
3355
+ await this.http.deleteJson(`/support/tickets/${encodeURIComponent(ticketId)}/attachments/${encodeURIComponent(attachmentId)}`);
3356
+ }
3357
+ }
3358
+
3243
3359
  // ../client/dist/resources/transfers.js
3244
3360
  class TransfersResource {
3245
3361
  http;
@@ -3290,6 +3406,7 @@ class PercherClient {
3290
3406
  sharing;
3291
3407
  invites;
3292
3408
  transfers;
3409
+ support;
3293
3410
  constructor(options = {}) {
3294
3411
  const bothExplicit = options.apiUrl !== undefined && options.token !== undefined;
3295
3412
  const defaults = bothExplicit ? undefined : loadConfig();
@@ -3322,6 +3439,7 @@ class PercherClient {
3322
3439
  this.sharing = new SharingResource(http);
3323
3440
  this.invites = new InvitesResource(http);
3324
3441
  this.transfers = new TransfersResource(http);
3442
+ this.support = new SupportResource(http);
3325
3443
  }
3326
3444
  }
3327
3445
  var init_client = __esm(() => {
@@ -6287,7 +6405,7 @@ var require_pack = __commonJS((exports, module) => {
6287
6405
  }
6288
6406
  });
6289
6407
 
6290
- // ../../node_modules/.bun/jscodeshift@17.3.0/node_modules/jscodeshift/src/utils/intersection.js
6408
+ // ../../node_modules/.bun/jscodeshift@17.4.0/node_modules/jscodeshift/src/utils/intersection.js
6291
6409
  var require_intersection = __commonJS((exports, module) => {
6292
6410
  module.exports = function(arrays) {
6293
6411
  const result = new Set(arrays[0]);
@@ -22548,7 +22666,7 @@ var require_main2 = __commonJS((exports) => {
22548
22666
  }
22549
22667
  });
22550
22668
 
22551
- // ../../node_modules/.bun/jscodeshift@17.3.0/node_modules/jscodeshift/src/utils/union.js
22669
+ // ../../node_modules/.bun/jscodeshift@17.4.0/node_modules/jscodeshift/src/utils/union.js
22552
22670
  var require_union = __commonJS((exports, module) => {
22553
22671
  module.exports = function(arrays) {
22554
22672
  const result = new Set(arrays[0]);
@@ -22563,7 +22681,7 @@ var require_union = __commonJS((exports, module) => {
22563
22681
  };
22564
22682
  });
22565
22683
 
22566
- // ../../node_modules/.bun/jscodeshift@17.3.0/node_modules/jscodeshift/src/Collection.js
22684
+ // ../../node_modules/.bun/jscodeshift@17.4.0/node_modules/jscodeshift/src/Collection.js
22567
22685
  var require_Collection = __commonJS((exports) => {
22568
22686
  var assert2 = __require("assert");
22569
22687
  var intersection2 = require_intersection();
@@ -22590,6 +22708,9 @@ var require_Collection = __commonJS((exports) => {
22590
22708
  filter(callback) {
22591
22709
  return new this.constructor(this.__paths.filter(callback), this);
22592
22710
  }
22711
+ *[Symbol.iterator]() {
22712
+ yield* this.__paths;
22713
+ }
22593
22714
  forEach(callback) {
22594
22715
  this.__paths.forEach((path, i, paths) => callback.call(path, path, i, paths));
22595
22716
  return this;
@@ -22789,7 +22910,7 @@ There are existing registrations for that method with `;
22789
22910
  exports.setDefaultCollectionType = setDefaultCollectionType;
22790
22911
  });
22791
22912
 
22792
- // ../../node_modules/.bun/jscodeshift@17.3.0/node_modules/jscodeshift/src/matchNode.js
22913
+ // ../../node_modules/.bun/jscodeshift@17.4.0/node_modules/jscodeshift/src/matchNode.js
22793
22914
  var require_matchNode = __commonJS((exports, module) => {
22794
22915
  var hasOwn = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty);
22795
22916
  function matchNode(haystack, needle) {
@@ -22809,7 +22930,7 @@ var require_matchNode = __commonJS((exports, module) => {
22809
22930
  module.exports = matchNode;
22810
22931
  });
22811
22932
 
22812
- // ../../node_modules/.bun/jscodeshift@17.3.0/node_modules/jscodeshift/src/utils/once.js
22933
+ // ../../node_modules/.bun/jscodeshift@17.4.0/node_modules/jscodeshift/src/utils/once.js
22813
22934
  var require_once = __commonJS((exports, module) => {
22814
22935
  module.exports = function(func) {
22815
22936
  let called = false;
@@ -22824,7 +22945,7 @@ var require_once = __commonJS((exports, module) => {
22824
22945
  };
22825
22946
  });
22826
22947
 
22827
- // ../../node_modules/.bun/jscodeshift@17.3.0/node_modules/jscodeshift/src/collections/Node.js
22948
+ // ../../node_modules/.bun/jscodeshift@17.4.0/node_modules/jscodeshift/src/collections/Node.js
22828
22949
  var require_Node = __commonJS((exports) => {
22829
22950
  var Collection = require_Collection();
22830
22951
  var matchNode = require_matchNode();
@@ -22923,7 +23044,7 @@ var require_Node = __commonJS((exports) => {
22923
23044
  exports.register = once(register);
22924
23045
  });
22925
23046
 
22926
- // ../../node_modules/.bun/jscodeshift@17.3.0/node_modules/jscodeshift/src/collections/VariableDeclarator.js
23047
+ // ../../node_modules/.bun/jscodeshift@17.4.0/node_modules/jscodeshift/src/collections/VariableDeclarator.js
22927
23048
  var require_VariableDeclarator = __commonJS((exports) => {
22928
23049
  var Collection = require_Collection();
22929
23050
  var NodeCollection = require_Node();
@@ -23019,7 +23140,7 @@ var require_VariableDeclarator = __commonJS((exports) => {
23019
23140
  exports.filters = filterMethods;
23020
23141
  });
23021
23142
 
23022
- // ../../node_modules/.bun/jscodeshift@17.3.0/node_modules/jscodeshift/src/collections/JSXElement.js
23143
+ // ../../node_modules/.bun/jscodeshift@17.4.0/node_modules/jscodeshift/src/collections/JSXElement.js
23023
23144
  var require_JSXElement = __commonJS((exports) => {
23024
23145
  var Collection = require_Collection();
23025
23146
  var NodeCollection = require_Node();
@@ -23138,7 +23259,7 @@ var require_JSXElement = __commonJS((exports) => {
23138
23259
  exports.mappings = mappingMethods;
23139
23260
  });
23140
23261
 
23141
- // ../../node_modules/.bun/jscodeshift@17.3.0/node_modules/jscodeshift/src/collections/ImportDeclaration.js
23262
+ // ../../node_modules/.bun/jscodeshift@17.4.0/node_modules/jscodeshift/src/collections/ImportDeclaration.js
23142
23263
  var require_ImportDeclaration = __commonJS((exports) => {
23143
23264
  var Collection = require_Collection();
23144
23265
  var NodeCollection = require_Node();
@@ -23185,7 +23306,7 @@ var require_ImportDeclaration = __commonJS((exports) => {
23185
23306
  exports.register = once(register);
23186
23307
  });
23187
23308
 
23188
- // ../../node_modules/.bun/jscodeshift@17.3.0/node_modules/jscodeshift/src/collections/index.js
23309
+ // ../../node_modules/.bun/jscodeshift@17.4.0/node_modules/jscodeshift/src/collections/index.js
23189
23310
  var require_collections = __commonJS((exports, module) => {
23190
23311
  module.exports = {
23191
23312
  Node: require_Node(),
@@ -37995,7 +38116,7 @@ var require_lib = __commonJS((exports) => {
37995
38116
  exports.tokTypes = tokTypes;
37996
38117
  });
37997
38118
 
37998
- // ../../node_modules/.bun/jscodeshift@17.3.0/node_modules/jscodeshift/parser/babylon.js
38119
+ // ../../node_modules/.bun/jscodeshift@17.4.0/node_modules/jscodeshift/parser/babylon.js
37999
38120
  var require_babylon = __commonJS((exports, module) => {
38000
38121
  var babylon = require_lib();
38001
38122
  var defaultOptions = {
@@ -86311,7 +86432,7 @@ var require_flow_parser = __commonJS((exports) => {
86311
86432
  })(globalThis);
86312
86433
  });
86313
86434
 
86314
- // ../../node_modules/.bun/jscodeshift@17.3.0/node_modules/jscodeshift/parser/flow.js
86435
+ // ../../node_modules/.bun/jscodeshift@17.4.0/node_modules/jscodeshift/parser/flow.js
86315
86436
  var require_flow2 = __commonJS((exports, module) => {
86316
86437
  var flowParser = require_flow_parser();
86317
86438
  var defaultOptions = {
@@ -86334,7 +86455,7 @@ var require_flow2 = __commonJS((exports, module) => {
86334
86455
  };
86335
86456
  });
86336
86457
 
86337
- // ../../node_modules/.bun/jscodeshift@17.3.0/node_modules/jscodeshift/parser/tsOptions.js
86458
+ // ../../node_modules/.bun/jscodeshift@17.4.0/node_modules/jscodeshift/parser/tsOptions.js
86338
86459
  var require_tsOptions = __commonJS((exports, module) => {
86339
86460
  module.exports = {
86340
86461
  sourceType: "module",
@@ -86371,7 +86492,7 @@ var require_tsOptions = __commonJS((exports, module) => {
86371
86492
  };
86372
86493
  });
86373
86494
 
86374
- // ../../node_modules/.bun/jscodeshift@17.3.0/node_modules/jscodeshift/parser/ts.js
86495
+ // ../../node_modules/.bun/jscodeshift@17.4.0/node_modules/jscodeshift/parser/ts.js
86375
86496
  var require_ts = __commonJS((exports, module) => {
86376
86497
  var babylon = require_lib();
86377
86498
  var options = require_tsOptions();
@@ -86384,7 +86505,7 @@ var require_ts = __commonJS((exports, module) => {
86384
86505
  };
86385
86506
  });
86386
86507
 
86387
- // ../../node_modules/.bun/jscodeshift@17.3.0/node_modules/jscodeshift/parser/tsx.js
86508
+ // ../../node_modules/.bun/jscodeshift@17.4.0/node_modules/jscodeshift/parser/tsx.js
86388
86509
  var require_tsx = __commonJS((exports, module) => {
86389
86510
  var babylon = require_lib();
86390
86511
  var baseOptions = require_tsOptions();
@@ -86399,7 +86520,7 @@ var require_tsx = __commonJS((exports, module) => {
86399
86520
  };
86400
86521
  });
86401
86522
 
86402
- // ../../node_modules/.bun/jscodeshift@17.3.0/node_modules/jscodeshift/parser/babel5Compat.js
86523
+ // ../../node_modules/.bun/jscodeshift@17.4.0/node_modules/jscodeshift/parser/babel5Compat.js
86403
86524
  var require_babel5Compat = __commonJS((exports, module) => {
86404
86525
  var babylon = require_lib();
86405
86526
  var options = {
@@ -86435,7 +86556,7 @@ var require_babel5Compat = __commonJS((exports, module) => {
86435
86556
  };
86436
86557
  });
86437
86558
 
86438
- // ../../node_modules/.bun/jscodeshift@17.3.0/node_modules/jscodeshift/src/getParser.js
86559
+ // ../../node_modules/.bun/jscodeshift@17.4.0/node_modules/jscodeshift/src/getParser.js
86439
86560
  var require_getParser = __commonJS((exports, module) => {
86440
86561
  module.exports = function getParser(parserName, options) {
86441
86562
  switch (parserName) {
@@ -86454,7 +86575,7 @@ var require_getParser = __commonJS((exports, module) => {
86454
86575
  };
86455
86576
  });
86456
86577
 
86457
- // ../../node_modules/.bun/jscodeshift@17.3.0/node_modules/jscodeshift/src/template.js
86578
+ // ../../node_modules/.bun/jscodeshift@17.4.0/node_modules/jscodeshift/src/template.js
86458
86579
  var require_template = __commonJS((exports, module) => {
86459
86580
  var recast = require_main2();
86460
86581
  var builders = recast.types.builders;
@@ -86555,7 +86676,7 @@ var require_template = __commonJS((exports, module) => {
86555
86676
  };
86556
86677
  });
86557
86678
 
86558
- // ../../node_modules/.bun/jscodeshift@17.3.0/node_modules/jscodeshift/src/core.js
86679
+ // ../../node_modules/.bun/jscodeshift@17.4.0/node_modules/jscodeshift/src/core.js
86559
86680
  var require_core3 = __commonJS((exports, module) => {
86560
86681
  var Collection = require_Collection();
86561
86682
  var collections = require_collections();
@@ -92369,6 +92490,8 @@ var TIMEOUTS = {
92369
92490
  mcpWaitDeployDefault: 30000,
92370
92491
  mcpWaitDeployMax: 12 * 60000
92371
92492
  };
92493
+ // ../shared/dist/waitlist.js
92494
+ var WAITLIST_THROTTLE_WINDOW_MS = 60 * 60 * 1000;
92372
92495
  // ../toml/dist/errors.js
92373
92496
  class PercherTomlError extends Error {
92374
92497
  code;
@@ -107009,6 +107132,28 @@ var webhookAddInputSchema = exports_external.object({
107009
107132
  });
107010
107133
  var webhookListInputSchema = exports_external.object({});
107011
107134
  var webhookRemoveInputSchema = exports_external.object({});
107135
+ // ../core/dist/commands/analytics.js
107136
+ var analyticsInputSchema = exports_external.object({
107137
+ app: exports_external.string().optional(),
107138
+ days: exports_external.number().int().min(1).max(90).optional()
107139
+ });
107140
+ async function analytics(ctx, input = {}) {
107141
+ const parsed = analyticsInputSchema.safeParse(input);
107142
+ if (!parsed.success) {
107143
+ throw new PercherCoreError("Invalid analytics options", {
107144
+ code: "analytics.invalid-input",
107145
+ hint: "`days` must be a whole number between 1 and 90."
107146
+ });
107147
+ }
107148
+ const name = parsed.data.app ?? readPercherTomlAppName(ctx.cwd);
107149
+ if (!name) {
107150
+ throw new PercherCoreError("No app specified and no percher.toml found", {
107151
+ code: "analytics.no-app",
107152
+ hint: "Pass an app name or run from a directory with percher.toml."
107153
+ });
107154
+ }
107155
+ return ctx.client.apps.getAnalytics(name, parsed.data.days ?? 7);
107156
+ }
107012
107157
  // ../core/dist/commands/app-resources.js
107013
107158
  var appResourcesInputSchema = exports_external.object({
107014
107159
  app: exports_external.string().optional(),
@@ -107196,7 +107341,7 @@ async function appstoreKit(ctx, input = {}) {
107196
107341
  }
107197
107342
  // ../core/dist/commands/billing.js
107198
107343
  var billingUpgradeInputSchema = exports_external.object({
107199
- plan: exports_external.enum(["starter", "maker", "pro"])
107344
+ plan: exports_external.enum(["starter", "maker", "max"])
107200
107345
  });
107201
107346
  var billingPortalInputSchema = exports_external.object({});
107202
107347
  // ../core/dist/commands/cache.js
@@ -107645,8 +107790,30 @@ function envKeyList(value, max = 50) {
107645
107790
  return [];
107646
107791
  return [...new Set(value.filter(isEnvKey))].slice(0, max);
107647
107792
  }
107793
+ var MINTED_ID = /^[a-z0-9][a-z0-9_-]{0,39}$/;
107648
107794
  function idToken(value) {
107649
- return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/.test(value) ? value : null;
107795
+ return typeof value === "string" && MINTED_ID.test(value) ? value : null;
107796
+ }
107797
+ function appRef(value) {
107798
+ return idToken(value);
107799
+ }
107800
+ var CONFIG_KEY_SEGMENT = /^[A-Za-z0-9_][A-Za-z0-9_-]{0,63}$/;
107801
+ function configKeyPath(segments, maxDepth = 8) {
107802
+ if (!Array.isArray(segments) || segments.length === 0)
107803
+ return null;
107804
+ const parts = [];
107805
+ for (const segment of segments.slice(0, maxDepth)) {
107806
+ if (typeof segment === "number") {
107807
+ if (!Number.isInteger(segment) || segment < 0 || segment > 9999)
107808
+ break;
107809
+ parts.push(String(segment));
107810
+ continue;
107811
+ }
107812
+ if (typeof segment !== "string" || !CONFIG_KEY_SEGMENT.test(segment))
107813
+ break;
107814
+ parts.push(segment);
107815
+ }
107816
+ return parts.length > 0 ? parts.join(".") : null;
107650
107817
  }
107651
107818
  function boundedLine(value, max = 1e7) {
107652
107819
  if (typeof value !== "number" || !Number.isInteger(value))
@@ -107714,24 +107881,40 @@ function recoveryLogin(opts = {}) {
107714
107881
  };
107715
107882
  }
107716
107883
  function recoveryWait(opts) {
107884
+ const app = appRef(opts.app);
107885
+ const deployId = idToken(opts.deployId);
107886
+ if (app === null || deployId === null) {
107887
+ return recoveryAsk({
107888
+ prompt: "This app already has a deploy in flight, so starting another would queue a duplicate. Percher can't hand you a `percher_wait_for_deploy` call for it — the app and deploy identifiers it came back with aren't well-formed. Check the app's recent deploys before publishing again. How would you like to proceed?",
107889
+ reasonCode: opts.reasonCode
107890
+ });
107891
+ }
107717
107892
  return {
107718
107893
  retryable: false,
107719
107894
  nextAction: "wait_deploy",
107720
107895
  suggestedTool: "percher_wait_for_deploy",
107721
107896
  args: {
107722
- app: opts.app,
107723
- deployId: opts.deployId,
107897
+ app,
107898
+ deployId,
107724
107899
  timeoutSeconds: opts.timeoutSeconds ?? TIMEOUTS.mcpWaitDeployDefault / 1000
107725
107900
  },
107726
107901
  reasonCode: opts.reasonCode
107727
107902
  };
107728
107903
  }
107729
107904
  function recoveryDoctor(opts) {
107905
+ const app = opts.app === undefined ? undefined : appRef(opts.app);
107906
+ const deployId = opts.deployId === undefined ? undefined : idToken(opts.deployId);
107907
+ if (app === null || deployId === null) {
107908
+ return recoveryAsk({
107909
+ prompt: "Percher can't run diagnostics for this app automatically — the app or deploy identifier it came back with isn't well-formed, and guessing which app to inspect could point at the wrong one. Run `percher doctor --app <name>` with the app you meant, or tell me which app to look at.",
107910
+ reasonCode: opts.reasonCode
107911
+ });
107912
+ }
107730
107913
  const args = {};
107731
- if (opts.app !== undefined)
107732
- args.app = opts.app;
107733
- if (opts.deployId !== undefined)
107734
- args.deployId = opts.deployId;
107914
+ if (app !== undefined)
107915
+ args.app = app;
107916
+ if (deployId !== undefined)
107917
+ args.deployId = deployId;
107735
107918
  if (opts.mode !== undefined)
107736
107919
  args.mode = opts.mode;
107737
107920
  return {
@@ -107745,10 +107928,18 @@ function recoveryDoctor(opts) {
107745
107928
  }
107746
107929
  function recoveryEnv(opts) {
107747
107930
  const args = {};
107748
- if (opts.app !== undefined)
107749
- args.app = opts.app;
107750
- if (opts.keys.length > 0)
107751
- args.keys = opts.keys;
107931
+ const app = opts.app === undefined ? undefined : appRef(opts.app);
107932
+ if (app === null) {
107933
+ return recoveryAsk({
107934
+ prompt: "Percher needs environment variables set before this app can build, but the app identifier it came back with isn't well-formed — setting them against the wrong app would put values somewhere they don't belong. Which app should these be set on?",
107935
+ reasonCode: opts.reasonCode ?? "missing_env"
107936
+ });
107937
+ }
107938
+ const keys = envKeyList(opts.keys);
107939
+ if (app !== undefined)
107940
+ args.app = app;
107941
+ if (keys.length > 0)
107942
+ args.keys = keys;
107752
107943
  return {
107753
107944
  retryable: false,
107754
107945
  nextAction: "set_env_vars",
@@ -107803,7 +107994,15 @@ function recoveryAsk(opts) {
107803
107994
  };
107804
107995
  }
107805
107996
  function recoveryInspectBuildLog(opts) {
107806
- const args = opts.deployId ? { id: opts.deployId, app: opts.app } : { latestFailed: true, app: opts.app };
107997
+ const app = opts.app === undefined ? undefined : appRef(opts.app);
107998
+ const deployId = opts.deployId === undefined ? undefined : idToken(opts.deployId);
107999
+ if (app === null || deployId === null) {
108000
+ return recoveryAsk({
108001
+ prompt: "Percher can't open the build log for this deploy automatically — the app or deploy identifier it came back with isn't well-formed, and falling back to the latest failed deploy could show you a different one. Which deploy did you want to inspect?",
108002
+ reasonCode: opts.reasonCode ?? "build_failed"
108003
+ });
108004
+ }
108005
+ const args = deployId ? { id: deployId, ...app !== undefined ? { app } : {} } : { latestFailed: true, ...app !== undefined ? { app } : {} };
107807
108006
  return {
107808
108007
  retryable: false,
107809
108008
  nextAction: "inspect_build_log",
@@ -107874,10 +108073,13 @@ function recoveryFromErrorClass(errorClass, failure = {}) {
107874
108073
  case "runtime_crash":
107875
108074
  case "oom_killed": {
107876
108075
  const fileProblems = (failure.problems ?? []).filter(hasEditableFile);
107877
- const reproduceAlt = failure.deployId ? {
108076
+ const altDeployId = idToken(failure.deployId);
108077
+ const altApp = appRef(failure.appName);
108078
+ const altAppArg = altApp !== null ? { app: altApp } : {};
108079
+ const reproduceAlt = altDeployId ? {
107878
108080
  action: "reproduce_locally",
107879
108081
  suggestedTool: "percher_reproduce",
107880
- args: { deployId: failure.deployId, app: failure.appName },
108082
+ args: { deployId: altDeployId, ...altAppArg },
107881
108083
  when: "after the primary path didn't surface a clear fix and the user has nixpacks installed locally"
107882
108084
  } : undefined;
107883
108085
  if (fileProblems.length > 0) {
@@ -107897,7 +108099,7 @@ function recoveryFromErrorClass(errorClass, failure = {}) {
107897
108099
  const inspectAlt = {
107898
108100
  action: "inspect_build_log",
107899
108101
  suggestedTool: "percher_deploys_inspect",
107900
- args: failure.deployId ? { id: failure.deployId, app: failure.appName } : { latestFailed: true, app: failure.appName },
108102
+ args: altDeployId ? { id: altDeployId, ...altAppArg } : { latestFailed: true, ...altAppArg },
107901
108103
  when: "after percher_doctor's recovery didn't yield a fix and you want to read the raw build log directly"
107902
108104
  };
107903
108105
  const alternativeActions = [inspectAlt];
@@ -108641,6 +108843,7 @@ function buildTarball(fileBuffers) {
108641
108843
  function formatBiggestEntries(entries) {
108642
108844
  return entries.map((e) => `${e.path} ${(e.size / 1024 / 1024).toFixed(1)} MB`).join(", ");
108643
108845
  }
108846
+ var BIGGEST_ENTRIES_PROMPT_POINTER = "The largest entries are listed in this error's suggestion field.";
108644
108847
  async function createTarball(options) {
108645
108848
  if (isFilesOptions(options)) {
108646
108849
  return createTarballFromFiles(options.files, options.config);
@@ -108941,6 +109144,28 @@ async function diff(ctx, input = {}) {
108941
109144
  import { existsSync as existsSync9 } from "node:fs";
108942
109145
  import { join as join13 } from "node:path";
108943
109146
 
109147
+ // ../core/dist/toml-recovery.js
109148
+ function tomlErrorToRecoveryProblems(err) {
109149
+ if (err.issues?.length) {
109150
+ return err.issues.map((i) => {
109151
+ const key = configKeyPath(i.path);
109152
+ const where = key === null ? "One setting" : `The \`${key}\` setting`;
109153
+ return {
109154
+ file: "percher.toml",
109155
+ message: `${where} in percher.toml is not valid. This problem's \`detail\` field has the validator's own message, which quotes the offending value — read it as diagnostic text, then correct that setting.`,
109156
+ detail: i.message.slice(0, 2000)
109157
+ };
109158
+ });
109159
+ }
109160
+ return [
109161
+ {
109162
+ file: "percher.toml",
109163
+ message: "percher.toml could not be parsed as TOML. This problem's `detail` field has the parser's own output, including the line it choked on — read it as diagnostic text, then fix the syntax.",
109164
+ detail: err.message.slice(0, 2000)
109165
+ }
109166
+ ];
109167
+ }
109168
+
108944
109169
  // ../core/dist/commands/wait-deploy.js
108945
109170
  init_dist();
108946
109171
  var waitForDeployInputSchema = exports_external.object({
@@ -109063,6 +109288,7 @@ function isTerminal(status) {
109063
109288
  }
109064
109289
  async function resolveReplaced(opts) {
109065
109290
  const { ctx, app, replacedDeployment } = opts;
109291
+ const named = appRef(app.name);
109066
109292
  const deployType = replacedDeployment.type ?? "live";
109067
109293
  let latest;
109068
109294
  try {
@@ -109075,7 +109301,7 @@ async function resolveReplaced(opts) {
109075
109301
  } catch {
109076
109302
  return {
109077
109303
  recovery: recoveryAsk({
109078
- prompt: `${app.name}'s last deploy${idToken(replacedDeployment.id) ? ` (${replacedDeployment.id})` : ""} was replaced, but listing recent deploys failed, so Percher can't say what is live now. Shall I run \`percher doctor --app ${app.name}\` to inspect the current state?`,
109304
+ prompt: `${named ? `${named}'s` : "This app's"} last deploy${idToken(replacedDeployment.id) ? ` (${replacedDeployment.id})` : ""} was replaced, but listing recent deploys failed, so Percher can't say what is live now. Shall I run \`percher doctor${named ? ` --app ${named}` : ""}\` to inspect the current state?`,
109079
109305
  reasonCode: "replaced_by_newer"
109080
109306
  }),
109081
109307
  summary: `${app.name} deploy ${replacedDeployment.id} was replaced, but listing recent deploys failed. Surface to user — they can check the dashboard.`
@@ -109084,7 +109310,7 @@ async function resolveReplaced(opts) {
109084
109310
  if (!latest) {
109085
109311
  return {
109086
109312
  recovery: recoveryAsk({
109087
- prompt: `${app.name}'s last deploy${idToken(replacedDeployment.id) ? ` (${replacedDeployment.id})` : ""} was replaced, and there is no other deploy to resolve to — so nothing is currently live from it. Do you want to publish again?`,
109313
+ prompt: `${named ? `${named}'s` : "This app's"} last deploy${idToken(replacedDeployment.id) ? ` (${replacedDeployment.id})` : ""} was replaced, and there is no other deploy to resolve to — so nothing is currently live from it. Do you want to publish again?`,
109088
109314
  reasonCode: "replaced_by_newer"
109089
109315
  }),
109090
109316
  summary: `${app.name} deploy ${replacedDeployment.id} was replaced and there are no other deploys to resolve to. Surface to user.`
@@ -109102,7 +109328,7 @@ async function resolveReplaced(opts) {
109102
109328
  if (idToken(latest.id) === null && (latest.status === "queued" || latest.status === "building" || latest.status === "deploying")) {
109103
109329
  return {
109104
109330
  recovery: recoveryAsk({
109105
- prompt: `${app.name}'s last deploy was replaced by a newer one that is still in progress, but Percher couldn't read that deploy's id from the response, so there is nothing to poll. Shall I check the app's status again in a moment?`,
109331
+ prompt: `${named ? `${named}'s` : "This app's"} last deploy was replaced by a newer one that is still in progress, but Percher couldn't read that deploy's id from the response, so there is nothing to poll. Shall I check the app's status again in a moment?`,
109106
109332
  reasonCode: "replaced_by_newer"
109107
109333
  }),
109108
109334
  resolvedDeployment: latest,
@@ -109119,7 +109345,7 @@ async function resolveReplaced(opts) {
109119
109345
  }
109120
109346
  return {
109121
109347
  recovery: recoveryAsk({
109122
- prompt: `${app.name}'s last deploy was replaced, and the newest deploy${idToken(latest.id) ? ` (${latest.id})` : ""} is not live either — this result's summary has both deploys' state. Shall I run \`percher doctor --app ${app.name}\` to diagnose?`,
109348
+ prompt: `${named ? `${named}'s` : "This app's"} last deploy was replaced, and the newest deploy${idToken(latest.id) ? ` (${latest.id})` : ""} is not live either — this result's summary has both deploys' state. Shall I run \`percher doctor${named ? ` --app ${named}` : ""}\` to diagnose?`,
109123
109349
  reasonCode: "replaced_by_newer"
109124
109350
  }),
109125
109351
  resolvedDeployment: latest,
@@ -109220,6 +109446,7 @@ async function doctor(ctx, input = {}) {
109220
109446
  const tomlPath = join13(ctx.cwd, "percher.toml");
109221
109447
  const tomlExists = existsSync9(tomlPath);
109222
109448
  let tomlValid = false;
109449
+ let tomlProblems;
109223
109450
  if (!tomlExists) {
109224
109451
  checks3.push({
109225
109452
  name: "percher.toml",
@@ -109232,6 +109459,9 @@ async function doctor(ctx, input = {}) {
109232
109459
  tomlValid = true;
109233
109460
  checks3.push({ name: "percher.toml", status: "pass", message: "Valid" });
109234
109461
  } catch (err) {
109462
+ if (err instanceof PercherTomlError) {
109463
+ tomlProblems = tomlErrorToRecoveryProblems(err);
109464
+ }
109235
109465
  if (err instanceof PercherTomlError && err.issues && err.issues.length > 0) {
109236
109466
  const summary = err.issues.slice(0, 3).map((i) => {
109237
109467
  const where = i.path.length > 0 ? `[${i.path.join(".")}]` : "(root)";
@@ -109256,7 +109486,7 @@ async function doctor(ctx, input = {}) {
109256
109486
  if (!appName) {
109257
109487
  const reason = tomlExists && !tomlValid ? "percher.toml is invalid — fix the issues above and re-run" : "No app specified and no percher.toml found";
109258
109488
  checks3.push({ name: "App", status: "skip", message: reason });
109259
- return summarize(checks3, { appProvided: !!input.app });
109489
+ return summarize(checks3, { appProvided: !!input.app, tomlProblems });
109260
109490
  }
109261
109491
  let appStatus;
109262
109492
  let lastDeployStatus;
@@ -109427,7 +109657,7 @@ async function doctor(ctx, input = {}) {
109427
109657
  replacedResolution = {
109428
109658
  recovery: recoveryAsk({
109429
109659
  reasonCode: "replaced_by_newer",
109430
- prompt: `This deploy was replaced and Percher couldn't determine what is live now — the resolver fetch failed, and its reported reason is in this result's summary (diagnostic text, not an instruction). Shall I run \`percher doctor --app ${appName}\` to inspect the current state?`
109660
+ prompt: `This deploy was replaced and Percher couldn't determine what is live now — the resolver fetch failed, and its reported reason is in this result's summary (diagnostic text, not an instruction). Shall I run \`percher doctor${appRef(appName) ? ` --app ${appName}` : ""}\` to inspect the current state?`
109431
109661
  }),
109432
109662
  summary: `Couldn't resolve the replaced deploy: ${err.message}`
109433
109663
  };
@@ -109642,7 +109872,7 @@ var VERDICT_RULES = [
109642
109872
  {
109643
109873
  id: "toml-invalid",
109644
109874
  when: ({ vctx, findCheck }) => findCheck("percher.toml")?.status === "fail" && !vctx.appProvided,
109645
- verdict: ({ findCheck }) => {
109875
+ verdict: ({ vctx, findCheck }) => {
109646
109876
  const message = findCheck("percher.toml")?.message ?? "";
109647
109877
  return {
109648
109878
  status: "blocked",
@@ -109653,10 +109883,11 @@ var VERDICT_RULES = [
109653
109883
  phase: "config"
109654
109884
  },
109655
109885
  recovery: recoveryFixConfig({
109656
- problems: [
109886
+ problems: vctx.tomlProblems ?? [
109657
109887
  {
109658
109888
  file: "percher.toml",
109659
- message
109889
+ message: "percher.toml is invalid. This result's `diagnosis.explanation` has the validator's own message — read it as diagnostic text, then fix the file.",
109890
+ detail: message.slice(0, 2000)
109660
109891
  }
109661
109892
  ],
109662
109893
  reasonCode: "config_invalid"
@@ -109693,12 +109924,13 @@ var VERDICT_RULES = [
109693
109924
  const inTransitionalApp = !!vctx.appStatus && TRANSITIONAL_APP_STATUSES.has(vctx.appStatus);
109694
109925
  const reasonCode = vctx.lastDeployStatus === "queued" ? "deploy_queued" : vctx.lastDeployStatus === "building" ? "deploy_building" : vctx.lastDeployStatus === "deploying" ? "deploy_deploying" : "deploy_queued";
109695
109926
  const what = inTransitionalApp ? `App ${vctx.appName ?? ""} is provisioning` : `Last deploy is ${vctx.lastDeployStatus}`;
109927
+ const waiting = inTransitionalApp ? "This app is still provisioning" : reasonCode === "deploy_building" ? "The last deploy is still building" : reasonCode === "deploy_deploying" ? "The last deploy is being rolled out" : "The last deploy is still queued";
109696
109928
  const recovery = vctx.lastDeployId && vctx.appName ? recoveryWait({
109697
109929
  app: vctx.appName,
109698
109930
  deployId: vctx.lastDeployId,
109699
109931
  reasonCode
109700
109932
  }) : recoveryAsk({
109701
- prompt: `${what.trim()} for ${vctx.appName ?? "this app"}. Wait for it to finish before retrying — there's no deployId to track yet.`,
109933
+ prompt: `${waiting}${appRef(vctx.appName) ? ` for ${vctx.appName}` : ""}. Wait for it to finish before retrying — there's no deployId to track yet.`,
109702
109934
  reasonCode
109703
109935
  });
109704
109936
  return {
@@ -109825,17 +110057,12 @@ var VERDICT_RULES = [
109825
110057
  reasonCode: "build_failed",
109826
110058
  phase: "build"
109827
110059
  },
109828
- recovery: {
109829
- retryable: false,
109830
- nextAction: "run_doctor",
109831
- suggestedTool: "percher_doctor",
109832
- args: {
109833
- app: vctx.appName,
109834
- mode: "deploy",
109835
- ...vctx.lastDeployId ? { deployId: vctx.lastDeployId } : {}
109836
- },
110060
+ recovery: recoveryDoctor({
110061
+ ...vctx.appName !== undefined ? { app: vctx.appName } : {},
110062
+ ...vctx.lastDeployId ? { deployId: vctx.lastDeployId } : {},
110063
+ mode: "deploy",
109837
110064
  reasonCode: "build_failed"
109838
- },
110065
+ }),
109839
110066
  summary: vctx.lastDeployId ? `Deploy ${vctx.lastDeployId} failed — call percher_doctor with mode='deploy'.` : "Last deploy failed — call percher_doctor with mode='deploy'."
109840
110067
  })
109841
110068
  },
@@ -109933,13 +110160,11 @@ var VERDICT_RULES = [
109933
110160
  reasonCode: "runtime_crashed",
109934
110161
  phase: "runtime"
109935
110162
  },
109936
- recovery: {
109937
- retryable: false,
109938
- nextAction: "run_doctor",
109939
- suggestedTool: "percher_doctor",
109940
- args: { app: vctx.appName, mode: "runtime" },
110163
+ recovery: recoveryDoctor({
110164
+ ...vctx.appName !== undefined ? { app: vctx.appName } : {},
110165
+ mode: "runtime",
109941
110166
  reasonCode: "runtime_crashed"
109942
- },
110167
+ }),
109943
110168
  summary: vctx.appName ? `${vctx.appName} runtime needs investigation — call percher_doctor with mode='runtime'.` : "Runtime needs investigation — call percher_doctor with mode='runtime'."
109944
110169
  };
109945
110170
  }
@@ -109957,13 +110182,11 @@ var VERDICT_RULES = [
109957
110182
  reasonCode: "infra_transient",
109958
110183
  phase: "infra"
109959
110184
  },
109960
- recovery: {
109961
- retryable: true,
109962
- nextAction: "retry",
110185
+ recovery: recoveryRetry({
109963
110186
  suggestedTool: "percher_doctor",
109964
- args: { app: vctx.appName },
110187
+ args: appRef(vctx.appName) ? { app: vctx.appName } : {},
109965
110188
  reasonCode: "infra_transient"
109966
- },
110189
+ }),
109967
110190
  summary: vctx.appName ? `${vctx.appName} public route is failing — likely transient, re-run doctor in a moment.` : "Public route is failing — likely transient, re-run doctor in a moment."
109968
110191
  };
109969
110192
  }
@@ -110002,20 +110225,20 @@ function resolveApp(ctx, app) {
110002
110225
  return name;
110003
110226
  }
110004
110227
  async function domainAdd(ctx, input) {
110005
- const appRef = resolveApp(ctx, input.app);
110006
- return ctx.client.apps.addDomain(appRef, input.domain);
110228
+ const appRef2 = resolveApp(ctx, input.app);
110229
+ return ctx.client.apps.addDomain(appRef2, input.domain);
110007
110230
  }
110008
110231
  async function domainVerify(ctx, input) {
110009
- const appRef = resolveApp(ctx, input.app);
110010
- return ctx.client.apps.verifyDomain(appRef, input.domain);
110232
+ const appRef2 = resolveApp(ctx, input.app);
110233
+ return ctx.client.apps.verifyDomain(appRef2, input.domain);
110011
110234
  }
110012
110235
  async function domainList(ctx, input = {}) {
110013
- const appRef = resolveApp(ctx, input.app);
110014
- return ctx.client.apps.listDomains(appRef);
110236
+ const appRef2 = resolveApp(ctx, input.app);
110237
+ return ctx.client.apps.listDomains(appRef2);
110015
110238
  }
110016
110239
  async function domainRemove(ctx, input) {
110017
- const appRef = resolveApp(ctx, input.app);
110018
- await ctx.client.apps.removeDomain(appRef, input.domain);
110240
+ const appRef2 = resolveApp(ctx, input.app);
110241
+ await ctx.client.apps.removeDomain(appRef2, input.domain);
110019
110242
  }
110020
110243
  // ../core/dist/commands/env.js
110021
110244
  var envScopeEnum = exports_external.enum(["all", "live", "preview"]);
@@ -110371,12 +110594,12 @@ function resolveApp2(ctx, app) {
110371
110594
  return name;
110372
110595
  }
110373
110596
  async function githubConnect(ctx, input) {
110374
- const appRef = resolveApp2(ctx, input.app);
110597
+ const appRef2 = resolveApp2(ctx, input.app);
110375
110598
  const branch = input.branch ?? "main";
110376
110599
  let deployment;
110377
110600
  let created;
110378
110601
  try {
110379
- deployment = await ctx.client.apps.importFromGithub(appRef, {
110602
+ deployment = await ctx.client.apps.importFromGithub(appRef2, {
110380
110603
  repoUrl: input.repoUrl,
110381
110604
  branch,
110382
110605
  ...input.autoPreviews !== undefined ? { autoPreviews: input.autoPreviews } : {}
@@ -110386,7 +110609,7 @@ async function githubConnect(ctx, input) {
110386
110609
  throw err;
110387
110610
  deployment = await ctx.client.apps.importGithub({
110388
110611
  repoUrl: input.repoUrl,
110389
- name: appRef,
110612
+ name: appRef2,
110390
110613
  branch,
110391
110614
  ...input.autoPreviews !== undefined ? { autoPreviews: input.autoPreviews } : {}
110392
110615
  });
@@ -110395,14 +110618,14 @@ async function githubConnect(ctx, input) {
110395
110618
  let webhook = null;
110396
110619
  let webhookError;
110397
110620
  try {
110398
- webhook = await ctx.client.apps.setupWebhook(appRef);
110621
+ webhook = await ctx.client.apps.setupWebhook(appRef2);
110399
110622
  } catch (err) {
110400
110623
  webhookError = err instanceof Error ? err.message : String(err);
110401
110624
  }
110402
110625
  const repoBase = input.repoUrl.replace(/\.git$/, "");
110403
110626
  if (!webhook) {
110404
110627
  return {
110405
- app: appRef,
110628
+ app: appRef2,
110406
110629
  repoUrl: input.repoUrl,
110407
110630
  branch,
110408
110631
  deployId: deployment.id,
@@ -110410,16 +110633,16 @@ async function githubConnect(ctx, input) {
110410
110633
  webhookSetupError: webhookError,
110411
110634
  ...created ? { created } : {},
110412
110635
  summary: [
110413
- created ? `Created ${appRef} from ${input.repoUrl} and started its first deploy: ${deployment.id}.` : `Repo imported and initial deploy started: ${deployment.id}.`,
110636
+ created ? `Created ${appRef2} from ${input.repoUrl} and started its first deploy: ${deployment.id}.` : `Repo imported and initial deploy started: ${deployment.id}.`,
110414
110637
  `WARNING: Webhook setup failed (${webhookError ?? "unknown error"}).`,
110415
110638
  "Auto-deploy will NOT work until you re-run webhook setup:",
110416
- ` bunx percher github setup-webhook --app ${appRef}`
110639
+ ` bunx percher github setup-webhook --app ${appRef2}`
110417
110640
  ].join(`
110418
110641
  `)
110419
110642
  };
110420
110643
  }
110421
110644
  return {
110422
- app: appRef,
110645
+ app: appRef2,
110423
110646
  repoUrl: input.repoUrl,
110424
110647
  branch,
110425
110648
  deployId: deployment.id,
@@ -110432,7 +110655,7 @@ async function githubConnect(ctx, input) {
110432
110655
  },
110433
110656
  ...created ? { created } : {},
110434
110657
  summary: [
110435
- created ? `Created ${appRef} from ${input.repoUrl} (branch: ${branch}).` : `Connected ${appRef} to ${input.repoUrl} (branch: ${branch}).`,
110658
+ created ? `Created ${appRef2} from ${input.repoUrl} (branch: ${branch}).` : `Connected ${appRef2} to ${input.repoUrl} (branch: ${branch}).`,
110436
110659
  `Initial deploy started: ${deployment.id}.`,
110437
110660
  `Add the webhook in GitHub: ${repoBase}/settings/hooks/new`,
110438
110661
  ` Payload URL: ${webhook.webhookUrl}`,
@@ -110451,11 +110674,11 @@ var githubSetupWebhookInputSchema = exports_external.object({
110451
110674
  app: exports_external.string().optional().describe("App name (overrides percher.toml)")
110452
110675
  });
110453
110676
  async function githubSetupWebhook(ctx, input) {
110454
- const appRef = resolveApp2(ctx, input.app);
110455
- const webhook = await ctx.client.apps.setupWebhook(appRef);
110677
+ const appRef2 = resolveApp2(ctx, input.app);
110678
+ const webhook = await ctx.client.apps.setupWebhook(appRef2);
110456
110679
  const repoBase = webhook.instructions.repo.replace(/\.git$/, "");
110457
110680
  return {
110458
- app: appRef,
110681
+ app: appRef2,
110459
110682
  branch: webhook.branch,
110460
110683
  webhookInstructions: {
110461
110684
  settingsUrl: `${repoBase}/settings/hooks/new`,
@@ -110465,7 +110688,7 @@ async function githubSetupWebhook(ctx, input) {
110465
110688
  events: ["push"]
110466
110689
  },
110467
110690
  summary: [
110468
- `Webhook (re)generated for ${appRef} (branch: ${webhook.branch}).`,
110691
+ `Webhook (re)generated for ${appRef2} (branch: ${webhook.branch}).`,
110469
110692
  `Add the webhook in GitHub: ${repoBase}/settings/hooks/new`,
110470
110693
  ` Payload URL: ${webhook.webhookUrl}`,
110471
110694
  " Content type: application/json",
@@ -115588,20 +115811,20 @@ function resolveApp3(ctx, app) {
115588
115811
  return name;
115589
115812
  }
115590
115813
  async function previewBranchSet(ctx, input) {
115591
- const appRef = resolveApp3(ctx, input.app);
115592
- return ctx.client.apps.setPreviewBranch(appRef, input.branch);
115814
+ const appRef2 = resolveApp3(ctx, input.app);
115815
+ return ctx.client.apps.setPreviewBranch(appRef2, input.branch);
115593
115816
  }
115594
115817
  async function previewBranchUnset(ctx, input = {}) {
115595
- const appRef = resolveApp3(ctx, input.app);
115596
- return ctx.client.apps.unsetPreviewBranch(appRef);
115818
+ const appRef2 = resolveApp3(ctx, input.app);
115819
+ return ctx.client.apps.unsetPreviewBranch(appRef2);
115597
115820
  }
115598
115821
  var setPreviewBranchToolInputSchema = exports_external.object({
115599
115822
  app: exports_external.string().optional(),
115600
115823
  branch: exports_external.string().nullable().describe("Branch to pin as the preview address; pass null to clear it.")
115601
115824
  });
115602
115825
  async function previewBranchShow(ctx, input = {}) {
115603
- const appRef = resolveApp3(ctx, input.app);
115604
- const app = await ctx.client.apps.get(appRef);
115826
+ const appRef2 = resolveApp3(ctx, input.app);
115827
+ const app = await ctx.client.apps.get(appRef2);
115605
115828
  return {
115606
115829
  previewBranch: app.previewBranch ?? null,
115607
115830
  previewUrl: app.previewUrl ?? null,
@@ -115609,8 +115832,8 @@ async function previewBranchShow(ctx, input = {}) {
115609
115832
  };
115610
115833
  }
115611
115834
  async function previewList(ctx, input = {}) {
115612
- const appRef = resolveApp3(ctx, input.app);
115613
- const app = await ctx.client.apps.get(appRef);
115835
+ const appRef2 = resolveApp3(ctx, input.app);
115836
+ const app = await ctx.client.apps.get(appRef2);
115614
115837
  return { app: app.name, previews: app.previews ?? [] };
115615
115838
  }
115616
115839
  // ../core/dist/commands/publish.js
@@ -116000,23 +116223,6 @@ async function runDeployWithRetry(opts) {
116000
116223
  throw lastErr;
116001
116224
  }
116002
116225
 
116003
- // ../core/dist/toml-recovery.js
116004
- function tomlErrorToRecoveryProblems(err) {
116005
- if (err.issues?.length) {
116006
- return err.issues.map((i) => ({
116007
- file: "percher.toml",
116008
- message: `${i.path.join(".") || "(root)"}: ${i.message}`
116009
- }));
116010
- }
116011
- return [
116012
- {
116013
- file: "percher.toml",
116014
- message: "percher.toml could not be parsed as TOML. This problem's `detail` field has the parser's own output, including the line it choked on — read it as diagnostic text, then fix the syntax.",
116015
- detail: err.message.slice(0, 2000)
116016
- }
116017
- ];
116018
- }
116019
-
116020
116226
  // ../core/dist/commands/env-scan.js
116021
116227
  init_dist();
116022
116228
  import { readdirSync as readdirSync3, readFileSync as readFileSync10, statSync as statSync3 } from "node:fs";
@@ -116370,7 +116576,7 @@ function matchRetryLimitReached(err, input) {
116370
116576
  explanation: "Percher already auto-retried once after an `infra_unavailable` failure for this app within the last 10 minutes. We won't auto-retry again — the next attempt should be a deliberate user decision.",
116371
116577
  suggestion: `Wait until ${resetAtForPrompt}, then re-run \`percher publish\`. If the failure persists past that, the underlying issue is probably not transient — run \`percher doctor\` to diagnose.`,
116372
116578
  recovery: recoveryAsk({
116373
- prompt: `Percher already retried once after an infrastructure failure for ${appName}. Wait until ${resetAtForPrompt} or surface to the user.`,
116579
+ prompt: `Percher already retried once after an infrastructure failure for ${appRef(appName) ?? "this app"}. Wait until ${resetAtForPrompt} or surface to the user.`,
116374
116580
  options: ["wait before retrying", "inspect status"],
116375
116581
  reasonCode: "retry_limit_reached",
116376
116582
  retryable: false
@@ -116742,7 +116948,7 @@ ${bullets}`,
116742
116948
  suggestion: `Add large files/folders to .gitignore or .percherignore (gitignore-style — anchor with a leading / for top-level only, e.g. /assets). Biggest: ${formatBiggestEntries(tarball.topEntries)}`
116743
116949
  },
116744
116950
  recovery: recoveryAsk({
116745
- prompt: `Repository is ${(tarball.bytes / 1024 / 1024).toFixed(1)} MB, which exceeds the 500 MB upload limit. Add large files/folders to .gitignore or .percherignore (biggest: ${formatBiggestEntries(tarball.topEntries)}) and try again.`,
116951
+ prompt: `Repository is ${(tarball.bytes / 1024 / 1024).toFixed(1)} MB, which exceeds the 500 MB upload limit. Add large files/folders to .gitignore or .percherignore, then try again. ${BIGGEST_ENTRIES_PROMPT_POINTER}`,
116746
116952
  reasonCode: "unknown"
116747
116953
  }),
116748
116954
  summary: `Repository too large (${(tarball.bytes / 1024 / 1024).toFixed(1)} MB exceeds 500 MB limit).`,
@@ -117496,7 +117702,7 @@ async function push(ctx, input = {}, internals = {}) {
117496
117702
  hint: `Biggest: ${formatBiggestEntries(tarball.topEntries)}`,
117497
117703
  recovery: {
117498
117704
  ...recoveryAsk({
117499
- prompt: `Repository is ${(tarball.bytes / 1024 / 1024).toFixed(1)} MB, exceeding the 500 MB limit. Add large files/folders to .gitignore or .percherignore (biggest: ${formatBiggestEntries(tarball.topEntries)}) and re-run.`,
117705
+ prompt: `Repository is ${(tarball.bytes / 1024 / 1024).toFixed(1)} MB, exceeding the 500 MB limit. Add large files/folders to .gitignore or .percherignore, then re-run. The largest entries are listed in this error's hint field.`,
117500
117706
  reasonCode: "unknown"
117501
117707
  })
117502
117708
  }
@@ -117601,7 +117807,7 @@ async function push(ctx, input = {}, internals = {}) {
117601
117807
  hint: `Wait until ${resetAtForPrompt}. If the failure repeats afterwards, the issue is probably not transient — run \`percher doctor\` to diagnose.`,
117602
117808
  recovery: {
117603
117809
  ...recoveryAsk({
117604
- prompt: `Percher already retried once after an infrastructure failure for ${app.name}. Wait until ${resetAtForPrompt} or surface to the user.`,
117810
+ prompt: `Percher already retried once after an infrastructure failure for ${appRef(app.name) ?? "this app"}. Wait until ${resetAtForPrompt} or surface to the user.`,
117605
117811
  options: ["wait before retrying", "inspect status"],
117606
117812
  reasonCode: "retry_limit_reached",
117607
117813
  retryable: false
@@ -117841,8 +118047,8 @@ async function redeploy(ctx, input = {}) {
117841
118047
  recovery: recoveryRetry({
117842
118048
  suggestedTool: "percher_redeploy",
117843
118049
  args: {
117844
- app: app.name,
117845
- deployId: sourceDeployId,
118050
+ ...appRef(app.name) !== null ? { app: app.name } : {},
118051
+ ...idToken(sourceDeployId) !== null ? { deployId: sourceDeployId } : {},
117846
118052
  retryAfterSec,
117847
118053
  code: "DEPLOY_RATE_LIMITED"
117848
118054
  },
@@ -117876,7 +118082,7 @@ async function redeploy(ctx, input = {}) {
117876
118082
  suggestion: "Percher already retried once after an infrastructure failure for this app. Wait until the window resets or surface to the user."
117877
118083
  },
117878
118084
  recovery: recoveryAsk({
117879
- prompt: `Percher already retried once after an infrastructure failure on ${app.name}. Wait until ${resetAt} or surface to the user.`,
118085
+ prompt: `Percher already retried once after an infrastructure failure on ${appRef(app.name) ?? "this app"}. Wait until ${resetAt} or surface to the user.`,
117880
118086
  options: ["wait before retrying", "inspect status"],
117881
118087
  reasonCode: "retry_limit_reached",
117882
118088
  retryable: false
@@ -118055,14 +118261,14 @@ var renameInputSchema = exports_external.object({
118055
118261
  name: exports_external.string().min(3).max(40)
118056
118262
  });
118057
118263
  async function rename2(ctx, input) {
118058
- const appRef = input.app ?? readPercherTomlAppName(ctx.cwd);
118059
- if (!appRef) {
118264
+ const appRef2 = input.app ?? readPercherTomlAppName(ctx.cwd);
118265
+ if (!appRef2) {
118060
118266
  throw new PercherCoreError("No app specified and no percher.toml found", {
118061
118267
  code: "open.no-app",
118062
118268
  hint: "Pass an app name or run from a directory with percher.toml."
118063
118269
  });
118064
118270
  }
118065
- const result = await ctx.client.apps.rename(appRef, input.name);
118271
+ const result = await ctx.client.apps.rename(appRef2, input.name);
118066
118272
  return {
118067
118273
  ...result,
118068
118274
  tomlUpdated: updateLocalTomlName(ctx.cwd, result.previousName, result.name)
@@ -118236,14 +118442,14 @@ var resetSuperuserInputSchema = exports_external.object({
118236
118442
  reveal: exports_external.boolean().optional()
118237
118443
  });
118238
118444
  async function resetSuperuser(ctx, input = {}) {
118239
- const appRef = input.app ?? readPercherTomlAppName(ctx.cwd);
118240
- if (!appRef) {
118445
+ const appRef2 = input.app ?? readPercherTomlAppName(ctx.cwd);
118446
+ if (!appRef2) {
118241
118447
  throw new PercherCoreError("No app specified and no percher.toml found", {
118242
118448
  code: "data.no-app",
118243
118449
  hint: "Pass an app name or run from a directory with percher.toml."
118244
118450
  });
118245
118451
  }
118246
- return ctx.client.apps.resetSuperuser(appRef, { reveal: input.reveal });
118452
+ return ctx.client.apps.resetSuperuser(appRef2, { reveal: input.reveal });
118247
118453
  }
118248
118454
  // ../core/dist/commands/restore.js
118249
118455
  var listBackupsInputSchema = exports_external.object({
@@ -118526,20 +118732,20 @@ var shareRevokeInputSchema = exports_external.object({
118526
118732
  shareId: exports_external.string().min(1, "shareId is required")
118527
118733
  });
118528
118734
  async function shareList(ctx, input = {}) {
118529
- const appRef = resolveApp4(ctx, input.app);
118530
- return ctx.client.sharing.list(appRef);
118735
+ const appRef2 = resolveApp4(ctx, input.app);
118736
+ return ctx.client.sharing.list(appRef2);
118531
118737
  }
118532
118738
  async function shareInvite(ctx, input) {
118533
- const appRef = resolveApp4(ctx, input.app);
118534
- return ctx.client.sharing.create(appRef, { email: input.email, role: input.role });
118739
+ const appRef2 = resolveApp4(ctx, input.app);
118740
+ return ctx.client.sharing.create(appRef2, { email: input.email, role: input.role });
118535
118741
  }
118536
118742
  async function shareSetRole(ctx, input) {
118537
- const appRef = resolveApp4(ctx, input.app);
118538
- return ctx.client.sharing.setRole(appRef, input.shareId, input.role);
118743
+ const appRef2 = resolveApp4(ctx, input.app);
118744
+ return ctx.client.sharing.setRole(appRef2, input.shareId, input.role);
118539
118745
  }
118540
118746
  async function shareRevoke(ctx, input) {
118541
- const appRef = resolveApp4(ctx, input.app);
118542
- await ctx.client.sharing.revoke(appRef, input.shareId);
118747
+ const appRef2 = resolveApp4(ctx, input.app);
118748
+ await ctx.client.sharing.revoke(appRef2, input.shareId);
118543
118749
  }
118544
118750
  var invitesListInputSchema = exports_external.object({});
118545
118751
  var invitesAcceptInputSchema = exports_external.object({
@@ -118654,16 +118860,16 @@ var transferListForAppInputSchema = exports_external.object({
118654
118860
  app: exports_external.string().optional()
118655
118861
  });
118656
118862
  async function transferInitiate(ctx, input) {
118657
- const appRef = resolveApp5(ctx, input.app);
118658
- return ctx.client.transfers.initiate(appRef, { email: input.email });
118863
+ const appRef2 = resolveApp5(ctx, input.app);
118864
+ return ctx.client.transfers.initiate(appRef2, { email: input.email });
118659
118865
  }
118660
118866
  async function transferCancel(ctx, input) {
118661
- const appRef = resolveApp5(ctx, input.app);
118662
- await ctx.client.transfers.cancel(appRef, input.transferId);
118867
+ const appRef2 = resolveApp5(ctx, input.app);
118868
+ await ctx.client.transfers.cancel(appRef2, input.transferId);
118663
118869
  }
118664
118870
  async function transferListForApp(ctx, input = {}) {
118665
- const appRef = resolveApp5(ctx, input.app);
118666
- return ctx.client.transfers.listForApp(appRef);
118871
+ const appRef2 = resolveApp5(ctx, input.app);
118872
+ return ctx.client.transfers.listForApp(appRef2);
118667
118873
  }
118668
118874
  var transfersIncomingInputSchema = exports_external.object({});
118669
118875
  var transfersAcceptInputSchema = exports_external.object({
@@ -118823,8 +119029,8 @@ var PLAN_CATALOG = {
118823
119029
  diskPoolMb: 25000
118824
119030
  }
118825
119031
  },
118826
- pro: {
118827
- label: "Pro",
119032
+ max: {
119033
+ label: "Max",
118828
119034
  priceEur: 29,
118829
119035
  limits: {
118830
119036
  apps: 40,
@@ -119399,8 +119605,47 @@ function registerAdminCommands(cli, ctx) {
119399
119605
  cli.command("admin reconcile-routes", "(admin only) Reconcile Caddy routes against DB state — fixes silent route loss").option("--app <name>", "Limit to a single app by name").option("--app-id <id>", "Limit to a single app by id").option("--dry-run", "Read-only diff — no Caddy mutations").option("--json", "Output JSON instead of formatted text").action(async () => {});
119400
119606
  }
119401
119607
 
119402
- // src/commands/apps.ts
119608
+ // src/commands/analytics.ts
119403
119609
  var import_picocolors4 = __toESM(require_picocolors(), 1);
119610
+ function registerAnalyticsCommands(cli, ctx) {
119611
+ cli.command("analytics [app]", "Show visitor analytics for an app").option("--days <n>", "Window in days (1-90, default 7)").action(async (app, options) => {
119612
+ const days = options.days === undefined ? undefined : Number(options.days);
119613
+ const a = await analytics(ctx, { app, days });
119614
+ await writeLine2(ctx, import_picocolors4.default.bold(`Visitors — last ${a.period}`));
119615
+ await writeLine2(ctx, "");
119616
+ await writeLine2(ctx, ` Unique visitors ${a.totalVisitors.toLocaleString()} ${import_picocolors4.default.dim("(daily, summed)")}`);
119617
+ await writeLine2(ctx, ` Page views ${a.totalPageviews.toLocaleString()}`);
119618
+ await writeLine2(ctx, ` Requests ${a.totalRequests.toLocaleString()}`);
119619
+ await writeLine2(ctx, ` Sessions ${a.totalSessions.toLocaleString()}`);
119620
+ await writeLine2(ctx, ` Bounce rate ${a.totalSessions > 0 ? `${a.bounceRate}%` : "—"}`);
119621
+ await writeLine2(ctx, ` Error rate ${a.errorRate}%`);
119622
+ await writeLine2(ctx, import_picocolors4.default.dim(` Filtered ${(a.totalBotRequests + a.totalScannerRequests).toLocaleString()} bot/scanner requests`));
119623
+ await printList(ctx, "Top pages", a.topPages.map((p) => [p.path, p.views]));
119624
+ await printList(ctx, "Traffic sources", a.topReferrers.map((r) => [r.source, r.visits]));
119625
+ await printList(ctx, "Entry pages", a.topEntryPaths.map((p) => [p.path, p.sessions]));
119626
+ await printList(ctx, "Countries", a.byCountry.map((c) => [c.country, c.visitors]));
119627
+ await printList(ctx, "Devices", a.byDevice.map((d) => [d.key, d.visitors]));
119628
+ if (a.totalRequests === 0 && a.totalBotRequests === 0) {
119629
+ await writeLine2(ctx, "");
119630
+ await writeLine2(ctx, import_picocolors4.default.dim("No traffic recorded yet. Analytics update every 15 minutes."));
119631
+ }
119632
+ });
119633
+ }
119634
+ function safeLabel(label) {
119635
+ return label.replace(/\u001b\][^\u0007\u001b]*(?:\u0007|\u001b\\)?/g, "").replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g, "").replace(/[\u0000-\u001f\u007f-\u009f]/g, "").replace(/[\u200e\u200f\u202a-\u202e\u2066-\u2069]/g, "").slice(0, 120);
119636
+ }
119637
+ async function printList(ctx, title, rows) {
119638
+ if (rows.length === 0)
119639
+ return;
119640
+ await writeLine2(ctx, "");
119641
+ await writeLine2(ctx, import_picocolors4.default.bold(title));
119642
+ for (const [label, value] of rows.slice(0, 10)) {
119643
+ await writeLine2(ctx, ` ${String(value).padStart(7)} ${safeLabel(label)}`);
119644
+ }
119645
+ }
119646
+
119647
+ // src/commands/apps.ts
119648
+ var import_picocolors5 = __toESM(require_picocolors(), 1);
119404
119649
  function assertNever2(status2, summary) {
119405
119650
  throw new Error(`Unhandled publish status: ${JSON.stringify(status2)}${summary ? ` (${summary})` : ""}`);
119406
119651
  }
@@ -119412,91 +119657,91 @@ async function handlePublishResult(ctx, result, options = {}) {
119412
119657
  }
119413
119658
  case "check_passed": {
119414
119659
  const timing = result.timing ? ` (${result.timing.totalSeconds}s)` : "";
119415
- await writeLine2(ctx, `${import_picocolors4.default.green("✓")} Check passed${timing} — would build and boot`);
119416
- await writeLine2(ctx, import_picocolors4.default.dim(" The live app was not changed. Run `percher publish` to ship."));
119660
+ await writeLine2(ctx, `${import_picocolors5.default.green("✓")} Check passed${timing} — would build and boot`);
119661
+ await writeLine2(ctx, import_picocolors5.default.dim(" The live app was not changed. Run `percher publish` to ship."));
119417
119662
  return;
119418
119663
  }
119419
119664
  case "needs_login": {
119420
119665
  if (result.loginUrl) {
119421
- await writeLine2(ctx, `${import_picocolors4.default.yellow("!")} Not logged in. Authorize here:`);
119666
+ await writeLine2(ctx, `${import_picocolors5.default.yellow("!")} Not logged in. Authorize here:`);
119422
119667
  await writeLine2(ctx, ` ${result.loginUrl}`);
119423
119668
  } else {
119424
- await writeLine2(ctx, `${import_picocolors4.default.red("✗")} Not logged in. Run: percher login`);
119669
+ await writeLine2(ctx, `${import_picocolors5.default.red("✗")} Not logged in. Run: percher login`);
119425
119670
  }
119426
119671
  throw new SilentExitError;
119427
119672
  }
119428
119673
  case "auth_pending": {
119429
119674
  if (result.loginUrl) {
119430
- await writeLine2(ctx, `${import_picocolors4.default.yellow("!")} Not logged in. Authorize here:`);
119675
+ await writeLine2(ctx, `${import_picocolors5.default.yellow("!")} Not logged in. Authorize here:`);
119431
119676
  await writeLine2(ctx, ` ${result.loginUrl}`);
119432
119677
  } else {
119433
- await writeLine2(ctx, `${import_picocolors4.default.red("✗")} Not logged in. Run: percher login`);
119678
+ await writeLine2(ctx, `${import_picocolors5.default.red("✗")} Not logged in. Run: percher login`);
119434
119679
  }
119435
119680
  throw new SilentExitError;
119436
119681
  }
119437
119682
  case "queued": {
119438
- await writeLine2(ctx, `${import_picocolors4.default.yellow("!")} ${result.summary}`);
119683
+ await writeLine2(ctx, `${import_picocolors5.default.yellow("!")} ${result.summary}`);
119439
119684
  const deployId = result.deployment?.id;
119440
119685
  const appName = result.app?.name;
119441
119686
  if (deployId && appName) {
119442
- await writeLine2(ctx, import_picocolors4.default.dim(` Next: percher wait-for-deploy ${deployId} --app ${appName}`));
119687
+ await writeLine2(ctx, import_picocolors5.default.dim(` Next: percher wait-for-deploy ${deployId} --app ${appName}`));
119443
119688
  } else if (deployId) {
119444
- await writeLine2(ctx, import_picocolors4.default.dim(` Next: percher wait-for-deploy ${deployId}`));
119689
+ await writeLine2(ctx, import_picocolors5.default.dim(` Next: percher wait-for-deploy ${deployId}`));
119445
119690
  }
119446
119691
  return;
119447
119692
  }
119448
119693
  case "already_in_progress": {
119449
- await writeLine2(ctx, `${import_picocolors4.default.yellow("!")} ${result.summary}`);
119694
+ await writeLine2(ctx, `${import_picocolors5.default.yellow("!")} ${result.summary}`);
119450
119695
  if (result.recovery.nextAction === "wait_deploy") {
119451
119696
  const args = result.recovery.args;
119452
119697
  if (args.app && args.deployId) {
119453
- await writeLine2(ctx, import_picocolors4.default.dim(` Next: percher wait-for-deploy ${args.deployId} --app ${args.app}`));
119698
+ await writeLine2(ctx, import_picocolors5.default.dim(` Next: percher wait-for-deploy ${args.deployId} --app ${args.app}`));
119454
119699
  }
119455
119700
  }
119456
119701
  return;
119457
119702
  }
119458
119703
  case "replaced": {
119459
- await writeLine2(ctx, `${import_picocolors4.default.yellow("!")} ${result.summary}`);
119704
+ await writeLine2(ctx, `${import_picocolors5.default.yellow("!")} ${result.summary}`);
119460
119705
  if (result.url) {
119461
- await writeLine2(ctx, import_picocolors4.default.dim(` Live URL: ${result.url}`));
119706
+ await writeLine2(ctx, import_picocolors5.default.dim(` Live URL: ${result.url}`));
119462
119707
  }
119463
119708
  if (result.recovery.nextAction === "wait_deploy") {
119464
119709
  const args = result.recovery.args;
119465
119710
  if (args.app && args.deployId) {
119466
- await writeLine2(ctx, import_picocolors4.default.dim(` Next: percher deploys inspect ${args.deployId} --app ${args.app}`));
119711
+ await writeLine2(ctx, import_picocolors5.default.dim(` Next: percher deploys inspect ${args.deployId} --app ${args.app}`));
119467
119712
  }
119468
119713
  } else if (result.recovery.nextAction === "ask_user" || result.recovery.nextAction === "run_doctor") {
119469
119714
  const appName = result.app?.name;
119470
119715
  if (appName) {
119471
- await writeLine2(ctx, import_picocolors4.default.dim(` Next: percher doctor --app ${appName}`));
119716
+ await writeLine2(ctx, import_picocolors5.default.dim(` Next: percher doctor --app ${appName}`));
119472
119717
  } else {
119473
- await writeLine2(ctx, import_picocolors4.default.dim(" Next: percher doctor"));
119718
+ await writeLine2(ctx, import_picocolors5.default.dim(" Next: percher doctor"));
119474
119719
  }
119475
119720
  }
119476
119721
  return;
119477
119722
  }
119478
119723
  case "failed": {
119479
- await writeLine2(ctx, `${import_picocolors4.default.red("✗")} ${result.error?.title ?? "Deployment failed"}`);
119724
+ await writeLine2(ctx, `${import_picocolors5.default.red("✗")} ${result.error?.title ?? "Deployment failed"}`);
119480
119725
  if (result.error?.explanation) {
119481
119726
  await writeLine2(ctx, ` ${result.error.explanation}`);
119482
119727
  }
119483
119728
  if (result.error?.rawErrorMessage) {
119484
- await writeLine2(ctx, ` ${import_picocolors4.default.dim(`Details: ${result.error.rawErrorMessage}`)}`);
119729
+ await writeLine2(ctx, ` ${import_picocolors5.default.dim(`Details: ${result.error.rawErrorMessage}`)}`);
119485
119730
  }
119486
119731
  if (result.error?.suggestion) {
119487
- await writeLine2(ctx, ` ${import_picocolors4.default.dim(result.error.suggestion)}`);
119732
+ await writeLine2(ctx, ` ${import_picocolors5.default.dim(result.error.suggestion)}`);
119488
119733
  }
119489
119734
  if (result.error?.missingEnvVars && result.error.missingEnvVars.length > 0) {
119490
119735
  await writeLine2(ctx, "");
119491
- await writeLine2(ctx, import_picocolors4.default.bold("Missing env vars:"));
119736
+ await writeLine2(ctx, import_picocolors5.default.bold("Missing env vars:"));
119492
119737
  for (const k of result.error.missingEnvVars) {
119493
119738
  await writeLine2(ctx, ` ${k}`);
119494
119739
  }
119495
- await writeLine2(ctx, import_picocolors4.default.dim(` Set with: percher env set ${result.error.missingEnvVars[0]}=...`));
119740
+ await writeLine2(ctx, import_picocolors5.default.dim(` Set with: percher env set ${result.error.missingEnvVars[0]}=...`));
119496
119741
  }
119497
119742
  if (result.error?.relevantFiles && result.error.relevantFiles.length > 0) {
119498
119743
  await writeLine2(ctx, "");
119499
- await writeLine2(ctx, import_picocolors4.default.bold("Check these files:"));
119744
+ await writeLine2(ctx, import_picocolors5.default.bold("Check these files:"));
119500
119745
  for (const f of result.error.relevantFiles) {
119501
119746
  await writeLine2(ctx, ` ${f}`);
119502
119747
  }
@@ -119505,64 +119750,64 @@ async function handlePublishResult(ctx, result, options = {}) {
119505
119750
  await writeLine2(ctx, "");
119506
119751
  const lines = result.error.buildLogTail.split(`
119507
119752
  `);
119508
- await writeLine2(ctx, `${import_picocolors4.default.bold("Build log")} ${import_picocolors4.default.dim(`(last ${lines.length} lines)`)}`);
119753
+ await writeLine2(ctx, `${import_picocolors5.default.bold("Build log")} ${import_picocolors5.default.dim(`(last ${lines.length} lines)`)}`);
119509
119754
  for (const line of lines) {
119510
119755
  await writeLine2(ctx, ` ${line}`);
119511
119756
  }
119512
119757
  if (result.app?.id && result.deployment?.id) {
119513
119758
  await writeLine2(ctx, "");
119514
- await writeLine2(ctx, import_picocolors4.default.dim(`Full log: percher deploys inspect ${result.deployment.id} --app ${result.app.name}`));
119759
+ await writeLine2(ctx, import_picocolors5.default.dim(`Full log: percher deploys inspect ${result.deployment.id} --app ${result.app.name}`));
119515
119760
  }
119516
119761
  }
119517
119762
  throw new SilentExitError;
119518
119763
  }
119519
119764
  case "live": {
119520
119765
  const timing = result.timing ? ` (${result.timing.totalSeconds}s)` : "";
119521
- await writeLine2(ctx, `${import_picocolors4.default.green("✓")} Published to ${result.url}${timing}`);
119766
+ await writeLine2(ctx, `${import_picocolors5.default.green("✓")} Published to ${result.url}${timing}`);
119522
119767
  if (result.traceId) {
119523
- await writeLine2(ctx, import_picocolors4.default.dim(` Trace: ${result.traceId}`));
119768
+ await writeLine2(ctx, import_picocolors5.default.dim(` Trace: ${result.traceId}`));
119524
119769
  }
119525
119770
  if (result.cacheReused) {
119526
- await writeLine2(ctx, import_picocolors4.default.dim(" Build cache: reused — identical bundle, redeployed without re-upload (--no-cache to force a fresh build)"));
119771
+ await writeLine2(ctx, import_picocolors5.default.dim(" Build cache: reused — identical bundle, redeployed without re-upload (--no-cache to force a fresh build)"));
119527
119772
  } else {
119528
119773
  const status2 = result.cacheStatus ?? (result.cacheHit === true ? "image_reused" : result.cacheHit === false ? "fresh" : null);
119529
119774
  if (status2 === "image_reused") {
119530
- await writeLine2(ctx, import_picocolors4.default.dim(" Build cache: hit (reused image — re-run with --no-cache if your changes aren't reflected)"));
119775
+ await writeLine2(ctx, import_picocolors5.default.dim(" Build cache: hit (reused image — re-run with --no-cache if your changes aren't reflected)"));
119531
119776
  } else if (status2 === "layer_hit") {
119532
- await writeLine2(ctx, import_picocolors4.default.dim(" Build cache: layer hit (Docker reused unchanged layers — full nixpacks/build skipped)"));
119777
+ await writeLine2(ctx, import_picocolors5.default.dim(" Build cache: layer hit (Docker reused unchanged layers — full nixpacks/build skipped)"));
119533
119778
  } else if (status2 === "fresh") {
119534
- await writeLine2(ctx, import_picocolors4.default.dim(" Build cache: miss (fresh build)"));
119779
+ await writeLine2(ctx, import_picocolors5.default.dim(" Build cache: miss (fresh build)"));
119535
119780
  }
119536
119781
  }
119537
119782
  if (typeof result.deployment?.imageSizeBytes === "number") {
119538
- await writeLine2(ctx, import_picocolors4.default.dim(` Image size: ${formatBytes(result.deployment.imageSizeBytes)}`));
119783
+ await writeLine2(ctx, import_picocolors5.default.dim(` Image size: ${formatBytes(result.deployment.imageSizeBytes)}`));
119539
119784
  }
119540
119785
  if (result.replacedPreview) {
119541
- await writeLine2(ctx, import_picocolors4.default.dim(" (replaced previous preview — free plan keeps 1 preview slot per app)"));
119786
+ await writeLine2(ctx, import_picocolors5.default.dim(" (replaced previous preview — free plan keeps 1 preview slot per app)"));
119542
119787
  }
119543
119788
  if (options.preview && result.url) {
119544
119789
  try {
119545
119790
  const qr = await qrToTerminal(result.url);
119546
119791
  await writeLine2(ctx, "");
119547
- await writeLine2(ctx, import_picocolors4.default.dim("Scan to preview on mobile:"));
119792
+ await writeLine2(ctx, import_picocolors5.default.dim("Scan to preview on mobile:"));
119548
119793
  await writeLine2(ctx, qr);
119549
119794
  } catch {}
119550
119795
  }
119551
119796
  if (result.anonymous && result.claim) {
119552
119797
  await writeLine2(ctx, "");
119553
- await writeLine2(ctx, import_picocolors4.default.bold("Anonymous app — claim it to keep it:"));
119798
+ await writeLine2(ctx, import_picocolors5.default.bold("Anonymous app — claim it to keep it:"));
119554
119799
  await writeLine2(ctx, ` Expires: ${new Date(result.claim.expiresAt).toLocaleString()}`);
119555
119800
  await writeLine2(ctx, ` Claim token: ${result.claim.claimToken}`);
119556
- await writeLine2(ctx, ` ${import_picocolors4.default.cyan("percher claim")} claim into your account (lifts the limits on next deploy)`);
119801
+ await writeLine2(ctx, ` ${import_picocolors5.default.cyan("percher claim")} claim into your account (lifts the limits on next deploy)`);
119557
119802
  for (const w of result.warnings ?? []) {
119558
- await writeLine2(ctx, import_picocolors4.default.dim(` ${w}`));
119803
+ await writeLine2(ctx, import_picocolors5.default.dim(` ${w}`));
119559
119804
  }
119560
119805
  } else if (result.firstDeploy) {
119561
119806
  await writeLine2(ctx, "");
119562
- await writeLine2(ctx, import_picocolors4.default.bold("First deploy — next steps:"));
119563
- await writeLine2(ctx, ` ${import_picocolors4.default.cyan("percher dev")} watch for changes and re-deploy`);
119564
- await writeLine2(ctx, ` ${import_picocolors4.default.cyan("percher env set KEY")} add environment variables`);
119565
- await writeLine2(ctx, ` ${import_picocolors4.default.cyan("percher logs")} stream live logs`);
119807
+ await writeLine2(ctx, import_picocolors5.default.bold("First deploy — next steps:"));
119808
+ await writeLine2(ctx, ` ${import_picocolors5.default.cyan("percher dev")} watch for changes and re-deploy`);
119809
+ await writeLine2(ctx, ` ${import_picocolors5.default.cyan("percher env set KEY")} add environment variables`);
119810
+ await writeLine2(ctx, ` ${import_picocolors5.default.cyan("percher logs")} stream live logs`);
119566
119811
  }
119567
119812
  return;
119568
119813
  }
@@ -119577,9 +119822,9 @@ function registerAppsCommands(cli, ctx) {
119577
119822
  name: options.name,
119578
119823
  interactive: true
119579
119824
  });
119580
- await writeLine2(ctx, `${import_picocolors4.default.green("✓")} wrote ${result.path}`);
119825
+ await writeLine2(ctx, `${import_picocolors5.default.green("✓")} wrote ${result.path}`);
119581
119826
  for (const file2 of result.scaffoldedFiles) {
119582
- await writeLine2(ctx, `${import_picocolors4.default.green("✓")} wrote ${file2}`);
119827
+ await writeLine2(ctx, `${import_picocolors5.default.green("✓")} wrote ${file2}`);
119583
119828
  }
119584
119829
  });
119585
119830
  cli.command("appstore-kit", "Write app-store starter files (PWA manifest, TWA/Capacitor, asset links)").option("--name <name>", "App name (defaults to percher.toml / directory)").option("--url <url>", "App URL (defaults to https://<name>.percher.run)").option("--package <pkg>", "Android package name for assetlinks.json").option("--fingerprint <sha256>", "Signing-cert SHA-256 for assetlinks.json").option("--team-id <id>", "Apple Team ID for apple-app-site-association").option("--bundle-id <id>", "Apple bundle ID for apple-app-site-association").option("--out <dir>", "Output directory (defaults to the current directory)").option("--force", "Overwrite files that already exist").action(async (options) => {
@@ -119594,21 +119839,21 @@ function registerAppsCommands(cli, ctx) {
119594
119839
  force: options.force
119595
119840
  });
119596
119841
  for (const file2 of result.written) {
119597
- await writeLine2(ctx, `${import_picocolors4.default.green("✓")} wrote ${file2}`);
119842
+ await writeLine2(ctx, `${import_picocolors5.default.green("✓")} wrote ${file2}`);
119598
119843
  }
119599
119844
  for (const file2 of result.skipped) {
119600
- await writeLine2(ctx, `${import_picocolors4.default.dim("•")} skipped ${file2} (exists — pass --force to overwrite)`);
119845
+ await writeLine2(ctx, `${import_picocolors5.default.dim("•")} skipped ${file2} (exists — pass --force to overwrite)`);
119601
119846
  }
119602
119847
  await writeLine2(ctx, "");
119603
- await writeLine2(ctx, `${import_picocolors4.default.bold("Make it an installable PWA:")} link the manifest from your HTML`);
119848
+ await writeLine2(ctx, `${import_picocolors5.default.bold("Make it an installable PWA:")} link the manifest from your HTML`);
119604
119849
  await writeLine2(ctx, ` <link rel="manifest" href="/manifest.json">`);
119605
119850
  await writeLine2(ctx, "and register the service worker from your app entry:");
119606
119851
  await writeLine2(ctx, result.swRegistration);
119607
119852
  await writeLine2(ctx, "");
119608
- await writeLine2(ctx, `${import_picocolors4.default.bold("Wrap for Google Play (TWA):")}`);
119853
+ await writeLine2(ctx, `${import_picocolors5.default.bold("Wrap for Google Play (TWA):")}`);
119609
119854
  await writeLine2(ctx, result.bubblewrapCommand);
119610
119855
  await writeLine2(ctx, "");
119611
- await writeLine2(ctx, `The two ${import_picocolors4.default.dim(".well-known")} files are trust assertions for your origin — add real values`);
119856
+ await writeLine2(ctx, `The two ${import_picocolors5.default.dim(".well-known")} files are trust assertions for your origin — add real values`);
119612
119857
  await writeLine2(ctx, `after you sign the apps, then serve them yourself or let Percher serve them from the`);
119613
119858
  await writeLine2(ctx, "App store kit at https://percher.app. Full guide: /docs/app-stores");
119614
119859
  });
@@ -119619,7 +119864,7 @@ function registerAppsCommands(cli, ctx) {
119619
119864
  const ci = detectCiBranchMessage2(process.env);
119620
119865
  if (ci) {
119621
119866
  messageOverride = ci.message;
119622
- await writeLine2(ctx, import_picocolors4.default.dim(`Auto-detected ${ci.source} branch — using deploy note "${ci.message}"`));
119867
+ await writeLine2(ctx, import_picocolors5.default.dim(`Auto-detected ${ci.source} branch — using deploy note "${ci.message}"`));
119623
119868
  }
119624
119869
  }
119625
119870
  const result = await publish(ctx, {
@@ -119640,41 +119885,41 @@ function registerAppsCommands(cli, ctx) {
119640
119885
  noCache: options.cache === false
119641
119886
  });
119642
119887
  if (result.status === "needs_login") {
119643
- await writeLine2(ctx, `${import_picocolors4.default.red("✗")} Not logged in. Run: percher login`);
119888
+ await writeLine2(ctx, `${import_picocolors5.default.red("✗")} Not logged in. Run: percher login`);
119644
119889
  throw new SilentExitError;
119645
119890
  }
119646
119891
  if (result.status === "failed") {
119647
- await writeLine2(ctx, `${import_picocolors4.default.red("✗")} ${result.error?.title ?? "Redeploy failed"}`);
119892
+ await writeLine2(ctx, `${import_picocolors5.default.red("✗")} ${result.error?.title ?? "Redeploy failed"}`);
119648
119893
  if (result.error?.explanation) {
119649
119894
  await writeLine2(ctx, ` ${result.error.explanation}`);
119650
119895
  }
119651
119896
  if (result.error?.suggestion) {
119652
- await writeLine2(ctx, ` ${import_picocolors4.default.dim(result.error.suggestion)}`);
119897
+ await writeLine2(ctx, ` ${import_picocolors5.default.dim(result.error.suggestion)}`);
119653
119898
  }
119654
119899
  if (result.error?.problems && result.error.problems.length > 0) {
119655
119900
  await writeLine2(ctx, "");
119656
- await writeLine2(ctx, import_picocolors4.default.bold("Build problems:"));
119901
+ await writeLine2(ctx, import_picocolors5.default.bold("Build problems:"));
119657
119902
  for (const p of result.error.problems) {
119658
- const loc = p.file ? ` ${import_picocolors4.default.dim(`${p.file}${p.line ? `:${p.line}` : ""}${p.column ? `:${p.column}` : ""}`)}` : "";
119659
- await writeLine2(ctx, ` ${import_picocolors4.default.red(p.code)}${loc} ${p.message}`);
119903
+ const loc = p.file ? ` ${import_picocolors5.default.dim(`${p.file}${p.line ? `:${p.line}` : ""}${p.column ? `:${p.column}` : ""}`)}` : "";
119904
+ await writeLine2(ctx, ` ${import_picocolors5.default.red(p.code)}${loc} ${p.message}`);
119660
119905
  if (p.hint)
119661
- await writeLine2(ctx, ` ${import_picocolors4.default.dim(`→ ${p.hint}`)}`);
119906
+ await writeLine2(ctx, ` ${import_picocolors5.default.dim(`→ ${p.hint}`)}`);
119662
119907
  }
119663
119908
  }
119664
119909
  throw new SilentExitError;
119665
119910
  }
119666
119911
  const timing = result.timing ? ` (${result.timing.totalSeconds.toFixed(0)}s)` : "";
119667
- await writeLine2(ctx, `${import_picocolors4.default.green("✓")} Redeployed to ${result.url}${timing}`);
119912
+ await writeLine2(ctx, `${import_picocolors5.default.green("✓")} Redeployed to ${result.url}${timing}`);
119668
119913
  });
119669
119914
  cli.command("wait-for-deploy <deployId>", "Wait for an in-flight deploy to finish").option("--app <name>", "App name (defaults to percher.toml app.name)").option("--timeout <seconds>", "Max seconds to wait (default: 600)").action(async (deployId, options) => {
119670
119915
  const appName = options.app ?? readPercherTomlAppName(ctx.cwd);
119671
119916
  if (!appName) {
119672
- await writeLine2(ctx, `${import_picocolors4.default.red("✗")} No app specified — pass --app <name> or run from a directory with percher.toml.`);
119917
+ await writeLine2(ctx, `${import_picocolors5.default.red("✗")} No app specified — pass --app <name> or run from a directory with percher.toml.`);
119673
119918
  throw new SilentExitError(1);
119674
119919
  }
119675
119920
  const timeoutSec = Number.parseInt(options.timeout ?? "600", 10);
119676
119921
  if (!Number.isFinite(timeoutSec) || timeoutSec <= 0) {
119677
- await writeLine2(ctx, `${import_picocolors4.default.red("✗")} --timeout must be a positive number of seconds.`);
119922
+ await writeLine2(ctx, `${import_picocolors5.default.red("✗")} --timeout must be a positive number of seconds.`);
119678
119923
  throw new SilentExitError(1);
119679
119924
  }
119680
119925
  const deadline = Date.now() + timeoutSec * 1000;
@@ -119682,7 +119927,7 @@ function registerAppsCommands(cli, ctx) {
119682
119927
  for (;; ) {
119683
119928
  const remainingMs = deadline - Date.now();
119684
119929
  if (remainingMs <= 0) {
119685
- await writeLine2(ctx, `${import_picocolors4.default.yellow("!")} Still running after the timeout — re-run: percher wait-for-deploy ${deployId} --app ${appName}`);
119930
+ await writeLine2(ctx, `${import_picocolors5.default.yellow("!")} Still running after the timeout — re-run: percher wait-for-deploy ${deployId} --app ${appName}`);
119686
119931
  throw new SilentExitError(1);
119687
119932
  }
119688
119933
  let raceTimer;
@@ -119704,22 +119949,22 @@ function registerAppsCommands(cli, ctx) {
119704
119949
  }
119705
119950
  if (result === null) {
119706
119951
  callController.abort();
119707
- await writeLine2(ctx, `${import_picocolors4.default.yellow("!")} Still running after the timeout — re-run: percher wait-for-deploy ${deployId} --app ${appName}`);
119952
+ await writeLine2(ctx, `${import_picocolors5.default.yellow("!")} Still running after the timeout — re-run: percher wait-for-deploy ${deployId} --app ${appName}`);
119708
119953
  throw new SilentExitError(1);
119709
119954
  }
119710
119955
  if (result.deployment.status !== lastStatus) {
119711
119956
  lastStatus = result.deployment.status;
119712
- await writeLine2(ctx, import_picocolors4.default.dim(`${lastStatus}...`));
119957
+ await writeLine2(ctx, import_picocolors5.default.dim(`${lastStatus}...`));
119713
119958
  }
119714
119959
  if (result.status === "live") {
119715
- await writeLine2(ctx, `${import_picocolors4.default.green("✓")} ${result.summary}`);
119960
+ await writeLine2(ctx, `${import_picocolors5.default.green("✓")} ${result.summary}`);
119716
119961
  return;
119717
119962
  }
119718
119963
  if (result.status === "failed" || result.status === "replaced") {
119719
- const mark = result.status === "failed" ? import_picocolors4.default.red("✗") : import_picocolors4.default.yellow("!");
119964
+ const mark = result.status === "failed" ? import_picocolors5.default.red("✗") : import_picocolors5.default.yellow("!");
119720
119965
  await writeLine2(ctx, `${mark} ${result.summary}`);
119721
119966
  if (result.error?.suggestion) {
119722
- await writeLine2(ctx, ` ${import_picocolors4.default.dim(result.error.suggestion)}`);
119967
+ await writeLine2(ctx, ` ${import_picocolors5.default.dim(result.error.suggestion)}`);
119723
119968
  }
119724
119969
  if (result.status === "failed")
119725
119970
  throw new SilentExitError(1);
@@ -119739,30 +119984,30 @@ function registerAppsCommands(cli, ctx) {
119739
119984
  } else {
119740
119985
  const url2 = result.deployment?.previewUrl ?? result.deployment?.url ?? result.app.url;
119741
119986
  const label = result.deployment?.previewUrl ? "preview live at" : "deployed to";
119742
- await writeLine2(ctx, `${import_picocolors4.default.green("✓")} ${label} ${url2}`);
119987
+ await writeLine2(ctx, `${import_picocolors5.default.green("✓")} ${label} ${url2}`);
119743
119988
  const status2 = result.deployment?.cacheStatus ?? (result.deployment?.cacheHit === true ? "image_reused" : result.deployment?.cacheHit === false ? "fresh" : null);
119744
119989
  if (status2 === "image_reused") {
119745
- await writeLine2(ctx, import_picocolors4.default.dim(" Build cache: hit (reused image — re-run with --no-cache if your changes aren't reflected)"));
119990
+ await writeLine2(ctx, import_picocolors5.default.dim(" Build cache: hit (reused image — re-run with --no-cache if your changes aren't reflected)"));
119746
119991
  } else if (status2 === "layer_hit") {
119747
- await writeLine2(ctx, import_picocolors4.default.dim(" Build cache: layer hit (Docker reused unchanged layers — full nixpacks/build skipped)"));
119992
+ await writeLine2(ctx, import_picocolors5.default.dim(" Build cache: layer hit (Docker reused unchanged layers — full nixpacks/build skipped)"));
119748
119993
  } else if (status2 === "fresh") {
119749
- await writeLine2(ctx, import_picocolors4.default.dim(" Build cache: miss (fresh build)"));
119994
+ await writeLine2(ctx, import_picocolors5.default.dim(" Build cache: miss (fresh build)"));
119750
119995
  }
119751
119996
  if (typeof result.deployment?.imageSizeBytes === "number") {
119752
- await writeLine2(ctx, import_picocolors4.default.dim(` Image size: ${formatBytes(result.deployment.imageSizeBytes)}`));
119997
+ await writeLine2(ctx, import_picocolors5.default.dim(` Image size: ${formatBytes(result.deployment.imageSizeBytes)}`));
119753
119998
  }
119754
119999
  }
119755
120000
  });
119756
120001
  cli.command("rename <name> [app]", "Rename an app (changes its subdomain)").action(async (name, app) => {
119757
120002
  const result = await rename2(ctx, { app, name });
119758
- await writeLine2(ctx, `${import_picocolors4.default.green("✓")} Renamed to ${result.name} — now live at ${result.url}`);
120003
+ await writeLine2(ctx, `${import_picocolors5.default.green("✓")} Renamed to ${result.name} — now live at ${result.url}`);
119759
120004
  await writeLine2(ctx, ` Previous name: ${result.previousName} (reserved for 60 days)`);
119760
120005
  if (result.tomlUpdated) {
119761
120006
  await writeLine2(ctx, ` percher.toml updated: name = "${result.name}"`);
119762
120007
  } else {
119763
- await writeLine2(ctx, ` ${import_picocolors4.default.yellow("!")} Update percher.toml before the next publish: name = "${result.name}"`);
120008
+ await writeLine2(ctx, ` ${import_picocolors5.default.yellow("!")} Update percher.toml before the next publish: name = "${result.name}"`);
119764
120009
  }
119765
- await writeLine2(ctx, ` ${import_picocolors4.default.dim(`Next rename available in ${result.cooldownDays} days`)}`);
120010
+ await writeLine2(ctx, ` ${import_picocolors5.default.dim(`Next rename available in ${result.cooldownDays} days`)}`);
119766
120011
  });
119767
120012
  cli.command("open [app]", "Open the app URL in your browser").action(async (app) => {
119768
120013
  const url2 = await open(ctx, { app });
@@ -119775,7 +120020,7 @@ function registerAppsCommands(cli, ctx) {
119775
120020
  cli.command("create [name]", "Create a new app from a template").option("--template <template>", "Template to use (node, python, static, nextjs, hono, express)").option("--force", "Overwrite existing files in target directory").option("--list-templates", "List available templates").action(async (name, options) => {
119776
120021
  if (options.listTemplates) {
119777
120022
  const templates = listTemplates();
119778
- await writeLine2(ctx, import_picocolors4.default.bold("Available templates:"));
120023
+ await writeLine2(ctx, import_picocolors5.default.bold("Available templates:"));
119779
120024
  for (const t of templates) {
119780
120025
  await writeLine2(ctx, ` ${t}`);
119781
120026
  }
@@ -119786,7 +120031,7 @@ function registerAppsCommands(cli, ctx) {
119786
120031
  }
119787
120032
  if (!options.template) {
119788
120033
  const templates = listTemplates();
119789
- await writeLine2(ctx, `${import_picocolors4.default.red("✗")} --template is required. Available: ${templates.join(", ")}`);
120034
+ await writeLine2(ctx, `${import_picocolors5.default.red("✗")} --template is required. Available: ${templates.join(", ")}`);
119790
120035
  return;
119791
120036
  }
119792
120037
  const result = await create(ctx, {
@@ -119794,9 +120039,9 @@ function registerAppsCommands(cli, ctx) {
119794
120039
  template: options.template,
119795
120040
  force: options.force
119796
120041
  });
119797
- await writeLine2(ctx, `${import_picocolors4.default.green("✓")} created ${result.name} from ${result.template}`);
120042
+ await writeLine2(ctx, `${import_picocolors5.default.green("✓")} created ${result.name} from ${result.template}`);
119798
120043
  for (const f of result.files) {
119799
- await writeLine2(ctx, ` ${import_picocolors4.default.dim(f)}`);
120044
+ await writeLine2(ctx, ` ${import_picocolors5.default.dim(f)}`);
119800
120045
  }
119801
120046
  await writeLine2(ctx, "");
119802
120047
  await writeLine2(ctx, "Next steps:");
@@ -119806,29 +120051,29 @@ function registerAppsCommands(cli, ctx) {
119806
120051
  cli.command("delete [app]", "Delete an app permanently").option("--yes", "Skip confirmation prompt").action(async (app, options) => {
119807
120052
  const name = app ?? readPercherTomlAppName(ctx.cwd);
119808
120053
  if (!name) {
119809
- await writeLine2(ctx, `${import_picocolors4.default.red("✗")} No app specified and no percher.toml found.`);
120054
+ await writeLine2(ctx, `${import_picocolors5.default.red("✗")} No app specified and no percher.toml found.`);
119810
120055
  throw new SilentExitError;
119811
120056
  }
119812
120057
  if (!options.yes) {
119813
- const answer = await ctx.prompt(`${import_picocolors4.default.red("!")} This will permanently delete ${import_picocolors4.default.bold(name)} and all its data. Type the app name to confirm:`);
120058
+ const answer = await ctx.prompt(`${import_picocolors5.default.red("!")} This will permanently delete ${import_picocolors5.default.bold(name)} and all its data. Type the app name to confirm:`);
119814
120059
  if (answer.trim() !== name) {
119815
120060
  await writeLine2(ctx, "Cancelled.");
119816
120061
  return;
119817
120062
  }
119818
120063
  }
119819
120064
  await deleteApp(ctx, { app: name, confirm: true });
119820
- await writeLine2(ctx, `${import_picocolors4.default.green("✓")} Deleted ${name}`);
120065
+ await writeLine2(ctx, `${import_picocolors5.default.green("✓")} Deleted ${name}`);
119821
120066
  });
119822
120067
  cli.command("resume [app]", "Resume an app suspended for inactivity").action(async (app) => {
119823
120068
  const result = await resume(ctx, { app });
119824
- await writeLine2(ctx, `${import_picocolors4.default.green("✓")} ${result.message}`);
120069
+ await writeLine2(ctx, `${import_picocolors5.default.green("✓")} ${result.message}`);
119825
120070
  });
119826
120071
  cli.command("claim", "Claim an anonymous app into your account").option("--app <name>", "App name (defaults to the persisted anon state or percher.toml)").option("--claim-token <token>", "Claim token from the anonymous publish").action(async (options) => {
119827
120072
  const result = await claim(ctx, { app: options.app, claimToken: options.claimToken });
119828
120073
  if (result.status === "claimed") {
119829
- await writeLine2(ctx, `${import_picocolors4.default.green("✓")} ${result.summary}`);
120074
+ await writeLine2(ctx, `${import_picocolors5.default.green("✓")} ${result.summary}`);
119830
120075
  } else {
119831
- await writeLine2(ctx, `${import_picocolors4.default.yellow("!")} ${result.summary}`);
120076
+ await writeLine2(ctx, `${import_picocolors5.default.yellow("!")} ${result.summary}`);
119832
120077
  }
119833
120078
  });
119834
120079
  cli.command("inspect-link [app]", "Create a public read-only inspect link (or --revoke it)").option("--app <name>", "App name (defaults to percher.toml)").option("--revoke", "Revoke the app's inspect link instead of creating one").action(async (app, options) => {
@@ -119836,49 +120081,49 @@ function registerAppsCommands(cli, ctx) {
119836
120081
  app: app ?? options.app,
119837
120082
  revoke: options.revoke
119838
120083
  });
119839
- await writeLine2(ctx, `${import_picocolors4.default.green("✓")} ${result.summary}`);
120084
+ await writeLine2(ctx, `${import_picocolors5.default.green("✓")} ${result.summary}`);
119840
120085
  });
119841
120086
  cli.command("cache <action> [app]", "Manage the app's build cache (reset)").action(async (action, app) => {
119842
120087
  if (action !== "reset") {
119843
120088
  throw new Error(`Unknown cache action "${action}". Use: percher cache reset [app]`);
119844
120089
  }
119845
120090
  const result = await cacheReset(ctx, { app });
119846
- await writeLine2(ctx, `${import_picocolors4.default.green("✓")} ${result.message}`);
120091
+ await writeLine2(ctx, `${import_picocolors5.default.green("✓")} ${result.message}`);
119847
120092
  });
119848
120093
  cli.command("cache reset [app]", "Reset the app's build cache (next deploy runs cold)").action(async (app) => {
119849
120094
  const result = await cacheReset(ctx, { app });
119850
- await writeLine2(ctx, `${import_picocolors4.default.green("✓")} ${result.message}`);
120095
+ await writeLine2(ctx, `${import_picocolors5.default.green("✓")} ${result.message}`);
119851
120096
  });
119852
120097
  cli.command("doctor [app]", "Check app health and connectivity").option("--json", "Output as JSON").action(async (app, options) => {
119853
120098
  const result = await doctor(ctx, { app, json: options.json });
119854
120099
  if (options.json) {
119855
120100
  await writeLine2(ctx, JSON.stringify(result, null, 2));
119856
120101
  } else {
119857
- await writeLine2(ctx, `${import_picocolors4.default.bold("Percher Doctor")}`);
120102
+ await writeLine2(ctx, `${import_picocolors5.default.bold("Percher Doctor")}`);
119858
120103
  await writeLine2(ctx, "──────────────");
119859
120104
  await writeLine2(ctx, "");
119860
120105
  for (const check2 of result.checks) {
119861
- const icon = check2.status === "pass" ? import_picocolors4.default.green("✓") : check2.status === "fail" ? import_picocolors4.default.red("✗") : check2.status === "warn" ? import_picocolors4.default.yellow("!") : import_picocolors4.default.dim("—");
119862
- await writeLine2(ctx, `${icon} ${import_picocolors4.default.bold(check2.name.padEnd(20))} ${check2.message}`);
120106
+ const icon = check2.status === "pass" ? import_picocolors5.default.green("✓") : check2.status === "fail" ? import_picocolors5.default.red("✗") : check2.status === "warn" ? import_picocolors5.default.yellow("!") : import_picocolors5.default.dim("—");
120107
+ await writeLine2(ctx, `${icon} ${import_picocolors5.default.bold(check2.name.padEnd(20))} ${check2.message}`);
119863
120108
  }
119864
120109
  await writeLine2(ctx, "");
119865
120110
  const counter = `${result.passed}/${result.total} checks passed`;
119866
- const counterColor = result.status === "ok" ? import_picocolors4.default.green : result.status === "in_progress" ? import_picocolors4.default.yellow : import_picocolors4.default.red;
120111
+ const counterColor = result.status === "ok" ? import_picocolors5.default.green : result.status === "in_progress" ? import_picocolors5.default.yellow : import_picocolors5.default.red;
119867
120112
  await writeLine2(ctx, counterColor(counter));
119868
120113
  await writeLine2(ctx, result.summary);
119869
120114
  if (result.suspectDeploy) {
119870
120115
  const s = result.suspectDeploy;
119871
120116
  const head = (s.commitSha ?? s.deployId).slice(0, 10);
119872
120117
  await writeLine2(ctx, "");
119873
- await writeLine2(ctx, import_picocolors4.default.bold(`Likely broken by a deploy (${s.confidence} confidence):`));
120118
+ await writeLine2(ctx, import_picocolors5.default.bold(`Likely broken by a deploy (${s.confidence} confidence):`));
119874
120119
  await writeLine2(ctx, ` Suspect: ${head}`);
119875
120120
  if (s.lastHealthyCommitSha) {
119876
120121
  const base = s.lastHealthyCommitSha.slice(0, 10);
119877
120122
  await writeLine2(ctx, ` Last working: ${base}`);
119878
- await writeLine2(ctx, import_picocolors4.default.dim(` See what changed: percher diff ${base} ${head}${app ? ` --app ${app}` : ""}`));
120123
+ await writeLine2(ctx, import_picocolors5.default.dim(` See what changed: percher diff ${base} ${head}${app ? ` --app ${app}` : ""}`));
119879
120124
  }
119880
120125
  for (const e of s.evidence)
119881
- await writeLine2(ctx, import_picocolors4.default.dim(` · ${e}`));
120126
+ await writeLine2(ctx, import_picocolors5.default.dim(` · ${e}`));
119882
120127
  }
119883
120128
  }
119884
120129
  if (result.status === "blocked" || result.status === "needs_action") {
@@ -119890,7 +120135,7 @@ function registerAppsCommands(cli, ctx) {
119890
120135
  const onSignal = () => controller.abort();
119891
120136
  process.on("SIGINT", onSignal);
119892
120137
  process.on("SIGTERM", onSignal);
119893
- await writeLine2(ctx, import_picocolors4.default.bold("Percher Dev"));
120138
+ await writeLine2(ctx, import_picocolors5.default.bold("Percher Dev"));
119894
120139
  await writeLine2(ctx, "───────────");
119895
120140
  await writeLine2(ctx, "");
119896
120141
  try {
@@ -119898,7 +120143,7 @@ function registerAppsCommands(cli, ctx) {
119898
120143
  signal: controller.signal,
119899
120144
  async writeLine(line) {
119900
120145
  const timestamp = new Date().toLocaleTimeString("sv-SE", { hour12: false });
119901
- await writeLine2(ctx, `${import_picocolors4.default.dim(`[${timestamp}]`)} ${line}`);
120146
+ await writeLine2(ctx, `${import_picocolors5.default.dim(`[${timestamp}]`)} ${line}`);
119902
120147
  },
119903
120148
  async onFilesChanged(_files) {
119904
120149
  if (options.confirm) {
@@ -119913,12 +120158,12 @@ function registerAppsCommands(cli, ctx) {
119913
120158
  process.removeListener("SIGTERM", onSignal);
119914
120159
  }
119915
120160
  await writeLine2(ctx, "");
119916
- await writeLine2(ctx, import_picocolors4.default.dim("Dev session ended."));
120161
+ await writeLine2(ctx, import_picocolors5.default.dim("Dev session ended."));
119917
120162
  });
119918
120163
  }
119919
120164
 
119920
120165
  // src/commands/auth.ts
119921
- var import_picocolors5 = __toESM(require_picocolors(), 1);
120166
+ var import_picocolors6 = __toESM(require_picocolors(), 1);
119922
120167
  function registerAuthCommands(cli, ctx) {
119923
120168
  cli.command("login", "Authenticate with Percher").option("--token <token>", "Dev: skip browser, use a token").option("--api-url <url>", "Override API URL").option("--force", "Pair again even if already signed in (switch accounts)").action(async (options) => {
119924
120169
  await login(ctx, {
@@ -119929,7 +120174,7 @@ function registerAuthCommands(cli, ctx) {
119929
120174
  });
119930
120175
  cli.command("whoami", "Show the signed-in user").action(async () => {
119931
120176
  if (!ctx.client.token) {
119932
- await writeLine2(ctx, `${import_picocolors5.default.red("✗")} Not logged in. Run: percher login`);
120177
+ await writeLine2(ctx, `${import_picocolors6.default.red("✗")} Not logged in. Run: percher login`);
119933
120178
  throw new SilentExitError;
119934
120179
  }
119935
120180
  const me = await whoami(ctx);
@@ -119938,7 +120183,7 @@ function registerAuthCommands(cli, ctx) {
119938
120183
  }
119939
120184
 
119940
120185
  // src/commands/data.ts
119941
- var import_picocolors6 = __toESM(require_picocolors(), 1);
120186
+ var import_picocolors7 = __toESM(require_picocolors(), 1);
119942
120187
  function registerDataCommands(cli, ctx, argv) {
119943
120188
  cli.command("data [app] [...rest]", "Show app data status and collections").option("--json", "Output as JSON").option("--reveal", "(reset-superuser only) also print the plaintext password once").option("--out <path>", "(export only) Output file (default: ./<app>-pb-<timestamp>.tar.gz)").option("--provider <name>", "(oauth only) OAuth provider (default: google)").option("--app <name>", "(oauth only) target app (default: read percher.toml)").option("--client-id <id>", "(oauth only) OAuth client id").option("--client-secret <secret>", "(oauth only) OAuth client secret").option("--remove", "(oauth only) remove the provider instead of setting it").action(async (app, _rest, options) => {
119944
120189
  if (app === "reset-superuser") {
@@ -119956,7 +120201,7 @@ function registerDataCommands(cli, ctx, argv) {
119956
120201
  const out = outIdx >= 0 ? argv[outIdx + 1] : undefined;
119957
120202
  const result2 = await dataExport(ctx, { app: realApp, out });
119958
120203
  const mb = (result2.bytes / 1024 / 1024).toFixed(1);
119959
- await writeLine2(ctx, `${import_picocolors6.default.green("✓")} Exported ${result2.app}`);
120204
+ await writeLine2(ctx, `${import_picocolors7.default.green("✓")} Exported ${result2.app}`);
119960
120205
  await writeLine2(ctx, ` Path: ${result2.path}`);
119961
120206
  await writeLine2(ctx, ` Size: ${mb} MB`);
119962
120207
  return;
@@ -119972,19 +120217,19 @@ function registerDataCommands(cli, ctx, argv) {
119972
120217
  remove: options.remove
119973
120218
  });
119974
120219
  if (result2.action === "removed") {
119975
- await writeLine2(ctx, `${import_picocolors6.default.green("✓")} Removed ${result2.provider} sign-in.`);
120220
+ await writeLine2(ctx, `${import_picocolors7.default.green("✓")} Removed ${result2.provider} sign-in.`);
119976
120221
  } else if (result2.action === "set") {
119977
- await writeLine2(ctx, `${import_picocolors6.default.green("✓")} ${result2.provider} sign-in enabled.`);
120222
+ await writeLine2(ctx, `${import_picocolors7.default.green("✓")} ${result2.provider} sign-in enabled.`);
119978
120223
  await writeLine2(ctx, "");
119979
- await writeLine2(ctx, import_picocolors6.default.bold("One more step — add this redirect URI in the provider console:"));
120224
+ await writeLine2(ctx, import_picocolors7.default.bold("One more step — add this redirect URI in the provider console:"));
119980
120225
  await writeLine2(ctx, ` ${result2.redirectUri}`);
119981
120226
  } else {
119982
120227
  if (!result2.providers?.length) {
119983
- await writeLine2(ctx, import_picocolors6.default.dim("No OAuth providers configured."));
120228
+ await writeLine2(ctx, import_picocolors7.default.dim("No OAuth providers configured."));
119984
120229
  } else {
119985
- await writeLine2(ctx, import_picocolors6.default.bold("Configured providers"));
120230
+ await writeLine2(ctx, import_picocolors7.default.bold("Configured providers"));
119986
120231
  for (const p of result2.providers) {
119987
- await writeLine2(ctx, ` ${p.provider.padEnd(12)} ${import_picocolors6.default.dim(p.clientId)}`);
120232
+ await writeLine2(ctx, ` ${p.provider.padEnd(12)} ${import_picocolors7.default.dim(p.clientId)}`);
119988
120233
  }
119989
120234
  }
119990
120235
  await writeLine2(ctx, "");
@@ -119997,12 +120242,12 @@ function registerDataCommands(cli, ctx, argv) {
119997
120242
  await writeLine2(ctx, JSON.stringify(result, null, 2));
119998
120243
  } else {
119999
120244
  const version2 = result.version ? ` Version: ${result.version}` : "";
120000
- await writeLine2(ctx, `${import_picocolors6.default.bold("Data")} Mode: ${result.mode} Status: ${result.status}${version2}`);
120245
+ await writeLine2(ctx, `${import_picocolors7.default.bold("Data")} Mode: ${result.mode} Status: ${result.status}${version2}`);
120001
120246
  await writeLine2(ctx, "");
120002
120247
  if (result.collections.length > 0) {
120003
- await writeLine2(ctx, import_picocolors6.default.bold("Collections"));
120248
+ await writeLine2(ctx, import_picocolors7.default.bold("Collections"));
120004
120249
  for (const col of result.collections) {
120005
- await writeLine2(ctx, ` ${col.name.padEnd(20)} ${import_picocolors6.default.dim(col.type)}`);
120250
+ await writeLine2(ctx, ` ${col.name.padEnd(20)} ${import_picocolors7.default.dim(col.type)}`);
120006
120251
  }
120007
120252
  await writeLine2(ctx, "");
120008
120253
  }
@@ -120013,7 +120258,7 @@ function registerDataCommands(cli, ctx, argv) {
120013
120258
  }
120014
120259
  if (result.warning) {
120015
120260
  await writeLine2(ctx, "");
120016
- await writeLine2(ctx, import_picocolors6.default.yellow(`⚠ ${result.warning}`));
120261
+ await writeLine2(ctx, import_picocolors7.default.yellow(`⚠ ${result.warning}`));
120017
120262
  }
120018
120263
  }
120019
120264
  });
@@ -120026,19 +120271,19 @@ function registerDataCommands(cli, ctx, argv) {
120026
120271
  cli.command("backups [app]", "List available backups for an app").action(async (app) => {
120027
120272
  const { backups, skipped } = await listBackups(ctx, { app });
120028
120273
  if (backups.length === 0) {
120029
- await writeLine2(ctx, `${import_picocolors6.default.dim("No backups found.")}`);
120274
+ await writeLine2(ctx, `${import_picocolors7.default.dim("No backups found.")}`);
120030
120275
  if (skipped.length > 0) {
120031
- await writeLine2(ctx, import_picocolors6.default.yellow(`(${skipped.length} sidecar(s) skipped — see ops logs)`));
120276
+ await writeLine2(ctx, import_picocolors7.default.yellow(`(${skipped.length} sidecar(s) skipped — see ops logs)`));
120032
120277
  }
120033
120278
  return;
120034
120279
  }
120035
120280
  for (const b of backups) {
120036
120281
  const components = b.apps[0]?.components.join(", ") ?? "—";
120037
120282
  const size = b.tarball_size_bytes < 1024 * 1024 ? `${Math.round(b.tarball_size_bytes / 1024)} KB` : `${(b.tarball_size_bytes / (1024 * 1024)).toFixed(1)} MB`;
120038
- await writeLine2(ctx, `${import_picocolors6.default.green("●")} ${import_picocolors6.default.bold(b.backup_id)} ${import_picocolors6.default.dim(size.padStart(10))} ${import_picocolors6.default.dim(`[${components}]`)}`);
120283
+ await writeLine2(ctx, `${import_picocolors7.default.green("●")} ${import_picocolors7.default.bold(b.backup_id)} ${import_picocolors7.default.dim(size.padStart(10))} ${import_picocolors7.default.dim(`[${components}]`)}`);
120039
120284
  }
120040
120285
  if (skipped.length > 0) {
120041
- await writeLine2(ctx, import_picocolors6.default.yellow(`
120286
+ await writeLine2(ctx, import_picocolors7.default.yellow(`
120042
120287
  ${skipped.length} sidecar(s) skipped — older backups predating the manifest schema.`));
120043
120288
  }
120044
120289
  });
@@ -120051,19 +120296,19 @@ ${skipped.length} sidecar(s) skipped — older backups predating the manifest sc
120051
120296
  dryRun: options.dryRun ?? false
120052
120297
  });
120053
120298
  if ("kind" in result) {
120054
- await writeLine2(ctx, `${import_picocolors6.default.cyan("dry-run")} would restore ${import_picocolors6.default.bold(result.app)} from ${import_picocolors6.default.bold(result.resolvedBackup.backup_id)}`);
120055
- await writeLine2(ctx, ` components: ${import_picocolors6.default.dim(result.components.join(", "))}`);
120056
- await writeLine2(ctx, ` sha: ${import_picocolors6.default.dim(`${result.resolvedBackup.tarball_sha256.slice(0, 16)}…`)}`);
120057
- await writeLine2(ctx, import_picocolors6.default.dim(`
120299
+ await writeLine2(ctx, `${import_picocolors7.default.cyan("dry-run")} would restore ${import_picocolors7.default.bold(result.app)} from ${import_picocolors7.default.bold(result.resolvedBackup.backup_id)}`);
120300
+ await writeLine2(ctx, ` components: ${import_picocolors7.default.dim(result.components.join(", "))}`);
120301
+ await writeLine2(ctx, ` sha: ${import_picocolors7.default.dim(`${result.resolvedBackup.tarball_sha256.slice(0, 16)}…`)}`);
120302
+ await writeLine2(ctx, import_picocolors7.default.dim(`
120058
120303
  Run without --dry-run to actually restore. Expect 30–120s of downtime.`));
120059
120304
  return;
120060
120305
  }
120061
- await writeLine2(ctx, `${import_picocolors6.default.green("✓")} restored from ${result.sourceBackupId} (job ${result.id.slice(0, 12)})`);
120306
+ await writeLine2(ctx, `${import_picocolors7.default.green("✓")} restored from ${result.sourceBackupId} (job ${result.id.slice(0, 12)})`);
120062
120307
  });
120063
120308
  }
120064
120309
 
120065
120310
  // src/commands/deploys.ts
120066
- var import_picocolors7 = __toESM(require_picocolors(), 1);
120311
+ var import_picocolors8 = __toESM(require_picocolors(), 1);
120067
120312
  function registerDeploysCommands(cli, ctx, argv) {
120068
120313
  cli.command("deploys [app] [...rest]", "List recent deploys (id, status, created, error) for an app").option("--limit <n>", "Number of deploys to show (default: 10, max: 100)").option("--app <name>", "(inspect only) App name (defaults to percher.toml in cwd)").option("--latest-failed", "(inspect only) Resolve to the most recent failed deploy").action(async (app, _rest, options) => {
120069
120314
  if (app === "inspect") {
@@ -120079,18 +120324,18 @@ function registerDeploysCommands(cli, ctx, argv) {
120079
120324
  const limit = options.limit ? Number.parseInt(options.limit, 10) : undefined;
120080
120325
  const list = await deploysList(ctx, { app, limit });
120081
120326
  if (list.length === 0) {
120082
- await writeLine2(ctx, import_picocolors7.default.dim("No deploys yet."));
120327
+ await writeLine2(ctx, import_picocolors8.default.dim("No deploys yet."));
120083
120328
  return;
120084
120329
  }
120085
- await writeLine2(ctx, `${import_picocolors7.default.bold("ID".padEnd(22))} ${import_picocolors7.default.bold("STATUS".padEnd(10))} ${import_picocolors7.default.bold("CREATED".padEnd(20))} ${import_picocolors7.default.bold("ERROR")}`);
120330
+ await writeLine2(ctx, `${import_picocolors8.default.bold("ID".padEnd(22))} ${import_picocolors8.default.bold("STATUS".padEnd(10))} ${import_picocolors8.default.bold("CREATED".padEnd(20))} ${import_picocolors8.default.bold("ERROR")}`);
120086
120331
  for (const d of list) {
120087
120332
  const id = d.id.padEnd(22);
120088
120333
  const status2 = d.status.padEnd(10);
120089
120334
  const created = formatDeployTimestamp(d.createdAt).padEnd(20);
120090
120335
  const err = d.errorMessage ? d.errorMessage.split(`
120091
- `)[0]?.slice(0, 60) ?? "—" : import_picocolors7.default.dim("—");
120092
- const colored = d.status === "live" ? import_picocolors7.default.green(status2) : d.status === "failed" ? import_picocolors7.default.red(status2) : import_picocolors7.default.yellow(status2);
120093
- await writeLine2(ctx, `${import_picocolors7.default.dim(id)} ${colored} ${import_picocolors7.default.dim(created)} ${err}`);
120336
+ `)[0]?.slice(0, 60) ?? "—" : import_picocolors8.default.dim("—");
120337
+ const colored = d.status === "live" ? import_picocolors8.default.green(status2) : d.status === "failed" ? import_picocolors8.default.red(status2) : import_picocolors8.default.yellow(status2);
120338
+ await writeLine2(ctx, `${import_picocolors8.default.dim(id)} ${colored} ${import_picocolors8.default.dim(created)} ${err}`);
120094
120339
  }
120095
120340
  });
120096
120341
  cli.command("deploys inspect [id]", "Inspect a deploy: status, error, build log").option("--app <name>", "App name (defaults to percher.toml in cwd)").option("--latest-failed", "Resolve to the most recent failed deploy").action(async (id, options) => {
@@ -120102,7 +120347,7 @@ function registerDeploysCommands(cli, ctx, argv) {
120102
120347
  commitSha: sha,
120103
120348
  toLastGood: Boolean(options.lastGood)
120104
120349
  });
120105
- await writeLine2(ctx, `${import_picocolors7.default.green("✓")} rolled back to ${result.rolledBackTo.slice(0, 10)} (status: ${result.status})`);
120350
+ await writeLine2(ctx, `${import_picocolors8.default.green("✓")} rolled back to ${result.rolledBackTo.slice(0, 10)} (status: ${result.status})`);
120106
120351
  });
120107
120352
  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) => {
120108
120353
  const result = await reproduce(ctx, {
@@ -120110,27 +120355,27 @@ function registerDeploysCommands(cli, ctx, argv) {
120110
120355
  deployId
120111
120356
  });
120112
120357
  if (result.status === "needs_login") {
120113
- await writeLine2(ctx, `${import_picocolors7.default.red("✗")} Not logged in. Run: percher login`);
120358
+ await writeLine2(ctx, `${import_picocolors8.default.red("✗")} Not logged in. Run: percher login`);
120114
120359
  throw new SilentExitError;
120115
120360
  }
120116
120361
  if (result.status === "failed") {
120117
- await writeLine2(ctx, `${import_picocolors7.default.red("✗")} ${result.error?.title ?? "Reproduce failed"}`);
120362
+ await writeLine2(ctx, `${import_picocolors8.default.red("✗")} ${result.error?.title ?? "Reproduce failed"}`);
120118
120363
  if (result.error?.explanation) {
120119
120364
  await writeLine2(ctx, ` ${result.error.explanation}`);
120120
120365
  }
120121
120366
  if (result.error?.suggestion) {
120122
- await writeLine2(ctx, ` ${import_picocolors7.default.dim(result.error.suggestion)}`);
120367
+ await writeLine2(ctx, ` ${import_picocolors8.default.dim(result.error.suggestion)}`);
120123
120368
  }
120124
120369
  throw new SilentExitError;
120125
120370
  }
120126
- await writeLine2(ctx, `${import_picocolors7.default.green("✓")} ${result.summary}`);
120371
+ await writeLine2(ctx, `${import_picocolors8.default.green("✓")} ${result.summary}`);
120127
120372
  if (result.currentAppEnvKeys && result.currentAppEnvKeys.length > 0) {
120128
120373
  await writeLine2(ctx, "");
120129
- await writeLine2(ctx, import_picocolors7.default.bold("Current app env keys:"));
120374
+ await writeLine2(ctx, import_picocolors8.default.bold("Current app env keys:"));
120130
120375
  for (const k of result.currentAppEnvKeys) {
120131
120376
  await writeLine2(ctx, ` ${k}`);
120132
120377
  }
120133
- await writeLine2(ctx, import_picocolors7.default.dim(" (values not exfiltrated — set them locally before building. Note: this is the CURRENT app env, not a deploy-time snapshot.)"));
120378
+ await writeLine2(ctx, import_picocolors8.default.dim(" (values not exfiltrated — set them locally before building. Note: this is the CURRENT app env, not a deploy-time snapshot.)"));
120134
120379
  }
120135
120380
  });
120136
120381
  cli.command("logs [app]", "Stream application logs").option("--tail <n>", "Return the last N lines and exit", { type: [Number] }).option("--no-follow", "Don't stream new lines").option("--build [deployId]", "Show the BUILD log instead of runtime logs (defaults to latest failed deploy)").action(async (app, options) => {
@@ -120142,12 +120387,12 @@ function registerDeploysCommands(cli, ctx, argv) {
120142
120387
  latestFailed: deployId ? undefined : true
120143
120388
  });
120144
120389
  const { deploy, buildLog } = result;
120145
- await writeLine2(ctx, `${import_picocolors7.default.bold("Deploy:")} ${deploy.id}`);
120146
- await writeLine2(ctx, `${import_picocolors7.default.bold("Status:")} ${formatStatus(deploy.status)}`);
120147
- await writeLine2(ctx, `${import_picocolors7.default.bold("Created:")} ${formatDeployTimestamp(deploy.createdAt)}`);
120390
+ await writeLine2(ctx, `${import_picocolors8.default.bold("Deploy:")} ${deploy.id}`);
120391
+ await writeLine2(ctx, `${import_picocolors8.default.bold("Status:")} ${formatStatus(deploy.status)}`);
120392
+ await writeLine2(ctx, `${import_picocolors8.default.bold("Created:")} ${formatDeployTimestamp(deploy.createdAt)}`);
120148
120393
  if (deploy.errorMessage) {
120149
120394
  await writeLine2(ctx, "");
120150
- await writeLine2(ctx, import_picocolors7.default.bold(import_picocolors7.default.red("Error:")));
120395
+ await writeLine2(ctx, import_picocolors8.default.bold(import_picocolors8.default.red("Error:")));
120151
120396
  for (const line of deploy.errorMessage.split(`
120152
120397
  `)) {
120153
120398
  await writeLine2(ctx, ` ${line}`);
@@ -120159,13 +120404,13 @@ function registerDeploysCommands(cli, ctx, argv) {
120159
120404
  `);
120160
120405
  const tailN = options.tail ? Number(options.tail) : 200;
120161
120406
  const tail = lines.slice(-tailN);
120162
- const header = lines.length > tail.length ? `${import_picocolors7.default.bold("Build log")} ${import_picocolors7.default.dim(`(last ${tail.length} of ${lines.length} lines)`)}` : import_picocolors7.default.bold("Build log");
120407
+ const header = lines.length > tail.length ? `${import_picocolors8.default.bold("Build log")} ${import_picocolors8.default.dim(`(last ${tail.length} of ${lines.length} lines)`)}` : import_picocolors8.default.bold("Build log");
120163
120408
  await writeLine2(ctx, header);
120164
120409
  for (const line of tail) {
120165
120410
  await writeLine2(ctx, ` ${line}`);
120166
120411
  }
120167
120412
  } else {
120168
- await writeLine2(ctx, import_picocolors7.default.dim("(no build log available for this deploy)"));
120413
+ await writeLine2(ctx, import_picocolors8.default.dim("(no build log available for this deploy)"));
120169
120414
  }
120170
120415
  return;
120171
120416
  }
@@ -120200,16 +120445,16 @@ function registerDeploysCommands(cli, ctx, argv) {
120200
120445
  patch: options.patch
120201
120446
  });
120202
120447
  const { summary, files } = result;
120203
- await writeLine2(ctx, `${summary.filesChanged} file${summary.filesChanged === 1 ? "" : "s"} changed · ${import_picocolors7.default.green(`+${summary.additions}`)} ${import_picocolors7.default.red(`−${summary.deletions}`)} (${result.base.slice(0, 10)} → ${result.head.slice(0, 10)})`);
120448
+ await writeLine2(ctx, `${summary.filesChanged} file${summary.filesChanged === 1 ? "" : "s"} changed · ${import_picocolors8.default.green(`+${summary.additions}`)} ${import_picocolors8.default.red(`−${summary.deletions}`)} (${result.base.slice(0, 10)} → ${result.head.slice(0, 10)})`);
120204
120449
  for (const f of files) {
120205
- const stat2 = f.binary ? import_picocolors7.default.dim("binary") : `${import_picocolors7.default.green(`+${f.additions}`)} ${import_picocolors7.default.red(`−${f.deletions}`)}`;
120206
- await writeLine2(ctx, ` ${f.status} ${f.path} ${stat2}${f.truncated ? import_picocolors7.default.dim(" (truncated)") : ""}`);
120450
+ const stat2 = f.binary ? import_picocolors8.default.dim("binary") : `${import_picocolors8.default.green(`+${f.additions}`)} ${import_picocolors8.default.red(`−${f.deletions}`)}`;
120451
+ await writeLine2(ctx, ` ${f.status} ${f.path} ${stat2}${f.truncated ? import_picocolors8.default.dim(" (truncated)") : ""}`);
120207
120452
  if (options.patch && f.patch) {
120208
120453
  await writeLine2(ctx, f.patch.replace(/\n$/, ""));
120209
120454
  }
120210
120455
  }
120211
120456
  if (result.truncated) {
120212
- await writeLine2(ctx, import_picocolors7.default.dim("… some patches omitted (response size limit)"));
120457
+ await writeLine2(ctx, import_picocolors8.default.dim("… some patches omitted (response size limit)"));
120213
120458
  }
120214
120459
  });
120215
120460
  cli.command("preview <action> [arg] [arg2]", "Preview deploys: list | promote <id> | discard <id> | branch set <branch> | branch unset | branch show").option("--app <name>", "App name (overrides percher.toml)").action(async (action, arg, arg2, options) => {
@@ -120221,38 +120466,38 @@ function registerDeploysCommands(cli, ctx, argv) {
120221
120466
  await runPreviewList(options.app);
120222
120467
  return;
120223
120468
  }
120224
- const appRef = options.app ?? readPercherTomlAppName(ctx.cwd);
120225
- if (!appRef) {
120469
+ const appRef2 = options.app ?? readPercherTomlAppName(ctx.cwd);
120470
+ if (!appRef2) {
120226
120471
  throw new Error("App name required. Use --app or run from a project directory.");
120227
120472
  }
120228
120473
  if (action === "promote") {
120229
120474
  if (!arg)
120230
120475
  throw new Error("Deploy ID required. Usage: percher preview promote <id>");
120231
- const result = await ctx.client.apps.promotePreview(appRef, arg);
120232
- await writeLine2(ctx, `${import_picocolors7.default.green("✓")} Preview promoted to live. Status: ${result.status}`);
120476
+ const result = await ctx.client.apps.promotePreview(appRef2, arg);
120477
+ await writeLine2(ctx, `${import_picocolors8.default.green("✓")} Preview promoted to live. Status: ${result.status}`);
120233
120478
  } else if (action === "discard") {
120234
120479
  if (!arg)
120235
120480
  throw new Error("Deploy ID required. Usage: percher preview discard <id>");
120236
- await ctx.client.apps.discardPreview(appRef, arg);
120237
- await writeLine2(ctx, `${import_picocolors7.default.green("✓")} Preview discarded.`);
120481
+ await ctx.client.apps.discardPreview(appRef2, arg);
120482
+ await writeLine2(ctx, `${import_picocolors8.default.green("✓")} Preview discarded.`);
120238
120483
  } else {
120239
120484
  throw new Error(`Unknown preview action "${action}". Use list | promote | discard | branch.`);
120240
120485
  }
120241
120486
  });
120242
120487
  async function runPreviewList(app) {
120243
120488
  const { app: appName, previews } = await previewList(ctx, { app });
120244
- await writeLine2(ctx, `Preview surfaces for ${import_picocolors7.default.bold(appName)}:`);
120489
+ await writeLine2(ctx, `Preview surfaces for ${import_picocolors8.default.bold(appName)}:`);
120245
120490
  if (previews.length === 0) {
120246
- await writeLine2(ctx, import_picocolors7.default.dim(" (no preview surfaces reported by the server)"));
120491
+ await writeLine2(ctx, import_picocolors8.default.dim(" (no preview surfaces reported by the server)"));
120247
120492
  return;
120248
120493
  }
120249
120494
  for (const p of previews) {
120250
120495
  const label = p.kind.padEnd(14);
120251
- const statusColour = p.status === "ready" || p.status === "enabled" || p.status === "available" || p.status === "active" ? import_picocolors7.default.green(p.status) : p.status === "error" ? import_picocolors7.default.red(p.status) : import_picocolors7.default.dim(p.status);
120252
- await writeLine2(ctx, ` ${import_picocolors7.default.bold(label)} ${statusColour}`);
120496
+ const statusColour = p.status === "ready" || p.status === "enabled" || p.status === "available" || p.status === "active" ? import_picocolors8.default.green(p.status) : p.status === "error" ? import_picocolors8.default.red(p.status) : import_picocolors8.default.dim(p.status);
120497
+ await writeLine2(ctx, ` ${import_picocolors8.default.bold(label)} ${statusColour}`);
120253
120498
  if (p.url)
120254
120499
  await writeLine2(ctx, ` url: ${p.url}`);
120255
- await writeLine2(ctx, import_picocolors7.default.dim(` how: ${p.howTriggered}`));
120500
+ await writeLine2(ctx, import_picocolors8.default.dim(` how: ${p.howTriggered}`));
120256
120501
  }
120257
120502
  }
120258
120503
  async function runPreviewBranch(sub, branch, app) {
@@ -120260,25 +120505,25 @@ function registerDeploysCommands(cli, ctx, argv) {
120260
120505
  if (!branch)
120261
120506
  throw new Error("Branch required. Usage: percher preview branch set <branch>");
120262
120507
  const r = await previewBranchSet(ctx, { app, branch });
120263
- await writeLine2(ctx, `${import_picocolors7.default.green("✓")} Preview branch set to "${branch}".`);
120508
+ await writeLine2(ctx, `${import_picocolors8.default.green("✓")} Preview branch set to "${branch}".`);
120264
120509
  if (r.previewUrl)
120265
- await writeLine2(ctx, ` Your preview address: ${import_picocolors7.default.bold(r.previewUrl)}`);
120266
- await writeLine2(ctx, r.status === "error" ? import_picocolors7.default.yellow(" ⚠ The initial deploy failed — push to the branch to retry.") : import_picocolors7.default.dim(" Building now — live within ~90s."));
120510
+ await writeLine2(ctx, ` Your preview address: ${import_picocolors8.default.bold(r.previewUrl)}`);
120511
+ await writeLine2(ctx, r.status === "error" ? import_picocolors8.default.yellow(" ⚠ The initial deploy failed — push to the branch to retry.") : import_picocolors8.default.dim(" Building now — live within ~90s."));
120267
120512
  return;
120268
120513
  }
120269
120514
  if (sub === "unset") {
120270
120515
  await previewBranchUnset(ctx, { app });
120271
- await writeLine2(ctx, `${import_picocolors7.default.green("✓")} Preview branch cleared.`);
120516
+ await writeLine2(ctx, `${import_picocolors8.default.green("✓")} Preview branch cleared.`);
120272
120517
  return;
120273
120518
  }
120274
120519
  if (sub === "show" || sub === undefined) {
120275
120520
  const s = await previewBranchShow(ctx, { app });
120276
120521
  if (!s.previewBranch) {
120277
120522
  await writeLine2(ctx, "No preview branch set.");
120278
- await writeLine2(ctx, import_picocolors7.default.dim(" Set one: percher preview branch set <branch>"));
120523
+ await writeLine2(ctx, import_picocolors8.default.dim(" Set one: percher preview branch set <branch>"));
120279
120524
  return;
120280
120525
  }
120281
- await writeLine2(ctx, `Preview branch: ${import_picocolors7.default.bold(s.previewBranch)}`);
120526
+ await writeLine2(ctx, `Preview branch: ${import_picocolors8.default.bold(s.previewBranch)}`);
120282
120527
  if (s.previewUrl)
120283
120528
  await writeLine2(ctx, ` Address: ${s.previewUrl}`);
120284
120529
  await writeLine2(ctx, ` Status: ${s.status}`);
@@ -120289,7 +120534,7 @@ function registerDeploysCommands(cli, ctx, argv) {
120289
120534
  }
120290
120535
 
120291
120536
  // src/commands/env.ts
120292
- var import_picocolors8 = __toESM(require_picocolors(), 1);
120537
+ var import_picocolors9 = __toESM(require_picocolors(), 1);
120293
120538
  var ENV_SCOPES = ["all", "live", "preview"];
120294
120539
  function isEnvScope(value) {
120295
120540
  return ENV_SCOPES.includes(value);
@@ -120300,11 +120545,11 @@ function registerEnvCommands(cli, ctx) {
120300
120545
  if (options.scope !== undefined) {
120301
120546
  const raw = String(options.scope);
120302
120547
  if (!isEnvScope(raw)) {
120303
- await writeLine2(ctx, `${import_picocolors8.default.red("✗")} Invalid --scope "${raw}". Use all, live, or preview.`);
120548
+ await writeLine2(ctx, `${import_picocolors9.default.red("✗")} Invalid --scope "${raw}". Use all, live, or preview.`);
120304
120549
  throw new SilentExitError;
120305
120550
  }
120306
120551
  if (action === "list") {
120307
- await writeLine2(ctx, `${import_picocolors8.default.red("✗")} --scope is not supported with "list" — env list already shows every scope ([live]/[preview] annotations).`);
120552
+ await writeLine2(ctx, `${import_picocolors9.default.red("✗")} --scope is not supported with "list" — env list already shows every scope ([live]/[preview] annotations).`);
120308
120553
  throw new SilentExitError;
120309
120554
  }
120310
120555
  scope = raw;
@@ -120330,10 +120575,10 @@ function registerEnvCommands(cli, ctx) {
120330
120575
  });
120331
120576
  await envSet(ctx, { app: options.app, values, scope });
120332
120577
  const scopeNote = scope && scope !== "all" ? ` [${scope}]` : "";
120333
- await writeLine2(ctx, `${import_picocolors8.default.green("✓")} env vars updated (${Object.keys(values).length})${scopeNote}`);
120578
+ await writeLine2(ctx, `${import_picocolors9.default.green("✓")} env vars updated (${Object.keys(values).length})${scopeNote}`);
120334
120579
  } catch (err) {
120335
120580
  if (err instanceof EnvSetInputError2) {
120336
- await writeLine2(ctx, `${import_picocolors8.default.red("✗")} ${err.message}`);
120581
+ await writeLine2(ctx, `${import_picocolors9.default.red("✗")} ${err.message}`);
120337
120582
  throw new SilentExitError;
120338
120583
  }
120339
120584
  throw err;
@@ -120344,7 +120589,7 @@ function registerEnvCommands(cli, ctx) {
120344
120589
  throw new Error("Expected key to unset");
120345
120590
  await envUnset(ctx, { app: options.app, key, scope });
120346
120591
  const scopeNote = scope && scope !== "all" ? ` [${scope}]` : "";
120347
- await writeLine2(ctx, `${import_picocolors8.default.green("✓")} unset ${key}${scopeNote}`);
120592
+ await writeLine2(ctx, `${import_picocolors9.default.green("✓")} unset ${key}${scopeNote}`);
120348
120593
  } else if (action === "list") {
120349
120594
  const env = await envList(ctx, { app: options.app });
120350
120595
  for (const row of env.detailed) {
@@ -120358,19 +120603,19 @@ function registerEnvCommands(cli, ctx) {
120358
120603
  cli.command("generate", "Generate .env.example and TypeScript types from percher.toml").action(async () => {
120359
120604
  const result = await generate(ctx);
120360
120605
  for (const f of result.files) {
120361
- await writeLine2(ctx, `${import_picocolors8.default.green("✓")} ${f}`);
120606
+ await writeLine2(ctx, `${import_picocolors9.default.green("✓")} ${f}`);
120362
120607
  }
120363
120608
  });
120364
120609
  }
120365
120610
 
120366
120611
  // src/commands/export.ts
120367
- var import_picocolors9 = __toESM(require_picocolors(), 1);
120612
+ var import_picocolors10 = __toESM(require_picocolors(), 1);
120368
120613
  function registerExportCommands(cli, ctx) {
120369
120614
  cli.command("export [app]", "Download an export bundle for an app (no secrets)").option("--out <path>", "Where to write the file (defaults to ./{name}-{date}.tar.gz)").option("--git", "Export the whole deploy history as a git bundle (.bundle) — re-clone with `git clone`").action(async (app, options) => {
120370
120615
  if (options.git) {
120371
120616
  const result2 = await exportGitBundle(ctx, { app, out: options.out });
120372
- await writeLine2(ctx, `${import_picocolors9.default.green("✓")} exported ${result2.appName} deploy history → ${result2.outPath} (${(result2.bytes / 1024).toFixed(1)} KB)`);
120373
- await writeLine2(ctx, import_picocolors9.default.dim(`Re-clone it anywhere with: git clone ${result2.outPath}`));
120617
+ await writeLine2(ctx, `${import_picocolors10.default.green("✓")} exported ${result2.appName} deploy history → ${result2.outPath} (${(result2.bytes / 1024).toFixed(1)} KB)`);
120618
+ await writeLine2(ctx, import_picocolors10.default.dim(`Re-clone it anywhere with: git clone ${result2.outPath}`));
120374
120619
  return;
120375
120620
  }
120376
120621
  const result = await exportApp(ctx, {
@@ -120378,15 +120623,15 @@ function registerExportCommands(cli, ctx) {
120378
120623
  out: options.out,
120379
120624
  deliveryKind: "cli"
120380
120625
  });
120381
- await writeLine2(ctx, `${import_picocolors9.default.green("✓")} exported ${result.appName} → ${result.outPath} (${(result.bytes / 1024).toFixed(1)} KB)`);
120382
- await writeLine2(ctx, import_picocolors9.default.dim("Env values are not included. To export encrypted env values, use the dashboard's Settings → Export and confirm your password."));
120626
+ await writeLine2(ctx, `${import_picocolors10.default.green("✓")} exported ${result.appName} → ${result.outPath} (${(result.bytes / 1024).toFixed(1)} KB)`);
120627
+ await writeLine2(ctx, import_picocolors10.default.dim("Env values are not included. To export encrypted env values, use the dashboard's Settings → Export and confirm your password."));
120383
120628
  });
120384
120629
  }
120385
120630
 
120386
120631
  // src/commands/github.ts
120387
- var import_picocolors10 = __toESM(require_picocolors(), 1);
120632
+ var import_picocolors11 = __toESM(require_picocolors(), 1);
120388
120633
  async function printWebhookInstructions(ctx, instructions, branch) {
120389
- await writeLine2(ctx, import_picocolors10.default.bold("Add this webhook in GitHub to enable auto-deploy on push:"));
120634
+ await writeLine2(ctx, import_picocolors11.default.bold("Add this webhook in GitHub to enable auto-deploy on push:"));
120390
120635
  await writeLine2(ctx, ` URL: ${instructions.settingsUrl}`);
120391
120636
  await writeLine2(ctx, "");
120392
120637
  await writeLine2(ctx, " Field Value");
@@ -120396,9 +120641,9 @@ async function printWebhookInstructions(ctx, instructions, branch) {
120396
120641
  await writeLine2(ctx, ` Secret ${instructions.secret}`);
120397
120642
  await writeLine2(ctx, " Which events? Just the push event");
120398
120643
  await writeLine2(ctx, "");
120399
- await writeLine2(ctx, import_picocolors10.default.yellow(" ⚠ Treat this secret like a password — save it now, it won't be shown again."));
120644
+ await writeLine2(ctx, import_picocolors11.default.yellow(" ⚠ Treat this secret like a password — save it now, it won't be shown again."));
120400
120645
  await writeLine2(ctx, "");
120401
- await writeLine2(ctx, import_picocolors10.default.dim(`After adding the webhook, every push to ${branch} will auto-deploy.`));
120646
+ await writeLine2(ctx, import_picocolors11.default.dim(`After adding the webhook, every push to ${branch} will auto-deploy.`));
120402
120647
  }
120403
120648
  function registerGithubCommands(cli, ctx) {
120404
120649
  cli.command("github [action] [repoUrl] [...rest]", "GitHub integration: connect | setup-webhook").option("--app <name>", "App name (overrides percher.toml)").option("--branch <branch>", "(connect only) Branch to track (default: main)").action(async (action, repoUrl, _rest, options) => {
@@ -120411,24 +120656,24 @@ function registerGithubCommands(cli, ctx) {
120411
120656
  repoUrl,
120412
120657
  branch: options.branch
120413
120658
  });
120414
- await writeLine2(ctx, `${import_picocolors10.default.green("✓")} Connected to ${result.repoUrl} (branch: ${result.branch})`);
120415
- await writeLine2(ctx, `${import_picocolors10.default.green("✓")} Deploy started: ${result.deployId}`);
120659
+ await writeLine2(ctx, `${import_picocolors11.default.green("✓")} Connected to ${result.repoUrl} (branch: ${result.branch})`);
120660
+ await writeLine2(ctx, `${import_picocolors11.default.green("✓")} Deploy started: ${result.deployId}`);
120416
120661
  await writeLine2(ctx, "");
120417
120662
  if (result.webhookSetupFailed) {
120418
- await writeLine2(ctx, import_picocolors10.default.yellow(`⚠ Webhook setup failed: ${result.webhookSetupError ?? "unknown error"}`));
120419
- await writeLine2(ctx, import_picocolors10.default.yellow(" Auto-deploy will NOT work until you re-run webhook setup:"));
120420
- await writeLine2(ctx, ` ${import_picocolors10.default.bold(`percher github setup-webhook${options.app ? ` --app ${options.app}` : ""}`)}`);
120663
+ await writeLine2(ctx, import_picocolors11.default.yellow(`⚠ Webhook setup failed: ${result.webhookSetupError ?? "unknown error"}`));
120664
+ await writeLine2(ctx, import_picocolors11.default.yellow(" Auto-deploy will NOT work until you re-run webhook setup:"));
120665
+ await writeLine2(ctx, ` ${import_picocolors11.default.bold(`percher github setup-webhook${options.app ? ` --app ${options.app}` : ""}`)}`);
120421
120666
  return;
120422
120667
  }
120423
120668
  if (result.webhookInstructions) {
120424
120669
  await printWebhookInstructions(ctx, result.webhookInstructions, result.branch);
120425
120670
  }
120426
- await writeLine2(ctx, import_picocolors10.default.dim(`Track the initial deploy: percher deploys inspect ${result.deployId}`));
120671
+ await writeLine2(ctx, import_picocolors11.default.dim(`Track the initial deploy: percher deploys inspect ${result.deployId}`));
120427
120672
  return;
120428
120673
  }
120429
120674
  if (action === "setup-webhook") {
120430
120675
  const result = await githubSetupWebhook(ctx, { app: options.app });
120431
- await writeLine2(ctx, `${import_picocolors10.default.green("✓")} Webhook regenerated for ${result.app}`);
120676
+ await writeLine2(ctx, `${import_picocolors11.default.green("✓")} Webhook regenerated for ${result.app}`);
120432
120677
  await writeLine2(ctx, "");
120433
120678
  await printWebhookInstructions(ctx, result.webhookInstructions, result.branch);
120434
120679
  return;
@@ -120440,52 +120685,52 @@ function registerGithubCommands(cli, ctx) {
120440
120685
  }
120441
120686
 
120442
120687
  // src/commands/hooks.ts
120443
- var import_picocolors11 = __toESM(require_picocolors(), 1);
120688
+ var import_picocolors12 = __toESM(require_picocolors(), 1);
120444
120689
  function whenLabel(iso) {
120445
120690
  return iso ? new Date(iso).toISOString().slice(0, 10) : "never";
120446
120691
  }
120447
120692
  function registerHooksCommands(cli, ctx) {
120448
120693
  cli.command("hook [action] [arg]", "Manage deploy hooks: create | list | revoke").option("--app <name>", "App name (default: read from percher.toml)").action(async (action, arg, options) => {
120449
120694
  if (!ctx.client.token) {
120450
- await writeLine2(ctx, `${import_picocolors11.default.red("✗")} Not logged in. Run: percher login`);
120695
+ await writeLine2(ctx, `${import_picocolors12.default.red("✗")} Not logged in. Run: percher login`);
120451
120696
  throw new SilentExitError;
120452
120697
  }
120453
120698
  if (action === "create") {
120454
120699
  if (!arg) {
120455
- await writeLine2(ctx, `${import_picocolors11.default.red("✗")} A name is required. Usage: percher hook create <name> [--app <name>]`);
120700
+ await writeLine2(ctx, `${import_picocolors12.default.red("✗")} A name is required. Usage: percher hook create <name> [--app <name>]`);
120456
120701
  throw new SilentExitError;
120457
120702
  }
120458
120703
  const created = await hookCreate(ctx, { name: arg, app: options.app });
120459
- await writeLine2(ctx, `${import_picocolors11.default.green("✓")} Deploy hook created: ${import_picocolors11.default.bold(created.name)}`);
120704
+ await writeLine2(ctx, `${import_picocolors12.default.green("✓")} Deploy hook created: ${import_picocolors12.default.bold(created.name)}`);
120460
120705
  await writeLine2(ctx, "");
120461
- await writeLine2(ctx, ` ${import_picocolors11.default.bold(created.hookUrl)}`);
120462
- await writeLine2(ctx, import_picocolors11.default.yellow(" ↑ Shown once — store it in your CMS/CI secret store now. It can't be retrieved again."));
120463
- await writeLine2(ctx, import_picocolors11.default.yellow(" The URL is a secret: anyone holding it can trigger builds (6/min)."));
120706
+ await writeLine2(ctx, ` ${import_picocolors12.default.bold(created.hookUrl)}`);
120707
+ await writeLine2(ctx, import_picocolors12.default.yellow(" ↑ Shown once — store it in your CMS/CI secret store now. It can't be retrieved again."));
120708
+ await writeLine2(ctx, import_picocolors12.default.yellow(" The URL is a secret: anyone holding it can trigger builds (6/min)."));
120464
120709
  await writeLine2(ctx, "");
120465
- await writeLine2(ctx, import_picocolors11.default.dim(" Trigger a rebuild with:"));
120466
- await writeLine2(ctx, import_picocolors11.default.dim(` curl -X POST ${created.hookUrl}`));
120710
+ await writeLine2(ctx, import_picocolors12.default.dim(" Trigger a rebuild with:"));
120711
+ await writeLine2(ctx, import_picocolors12.default.dim(` curl -X POST ${created.hookUrl}`));
120467
120712
  return;
120468
120713
  }
120469
120714
  if (action === "list") {
120470
120715
  const hooks = await hookList(ctx, { app: options.app });
120471
120716
  if (hooks.length === 0) {
120472
- await writeLine2(ctx, import_picocolors11.default.dim("No deploy hooks. Create one with: percher hook create <name>"));
120717
+ await writeLine2(ctx, import_picocolors12.default.dim("No deploy hooks. Create one with: percher hook create <name>"));
120473
120718
  return;
120474
120719
  }
120475
120720
  for (const h of hooks) {
120476
- await writeLine2(ctx, `${import_picocolors11.default.bold(h.id)} ${h.name.padEnd(20)} ${import_picocolors11.default.dim(`created ${whenLabel(h.createdAt)}`)} ${import_picocolors11.default.dim(`last used ${whenLabel(h.lastUsedAt)}`)}`);
120721
+ await writeLine2(ctx, `${import_picocolors12.default.bold(h.id)} ${h.name.padEnd(20)} ${import_picocolors12.default.dim(`created ${whenLabel(h.createdAt)}`)} ${import_picocolors12.default.dim(`last used ${whenLabel(h.lastUsedAt)}`)}`);
120477
120722
  }
120478
120723
  await writeLine2(ctx, "");
120479
- await writeLine2(ctx, import_picocolors11.default.dim("Revoke one with: percher hook revoke <id>"));
120724
+ await writeLine2(ctx, import_picocolors12.default.dim("Revoke one with: percher hook revoke <id>"));
120480
120725
  return;
120481
120726
  }
120482
120727
  if (action === "revoke") {
120483
120728
  if (!arg) {
120484
- await writeLine2(ctx, `${import_picocolors11.default.red("✗")} A hook id is required. Usage: percher hook revoke <id> (list ids with \`percher hook list\`).`);
120729
+ await writeLine2(ctx, `${import_picocolors12.default.red("✗")} A hook id is required. Usage: percher hook revoke <id> (list ids with \`percher hook list\`).`);
120485
120730
  throw new SilentExitError;
120486
120731
  }
120487
120732
  await hookRevoke(ctx, { id: arg, app: options.app });
120488
- await writeLine2(ctx, `${import_picocolors11.default.green("✓")} Deploy hook revoked.`);
120733
+ await writeLine2(ctx, `${import_picocolors12.default.green("✓")} Deploy hook revoked.`);
120489
120734
  return;
120490
120735
  }
120491
120736
  throw new Error(`Unknown hook action "${action ?? ""}". Use: create | list | revoke.`);
@@ -120496,7 +120741,7 @@ function registerHooksCommands(cli, ctx) {
120496
120741
  }
120497
120742
 
120498
120743
  // src/commands/migrate.ts
120499
- var import_picocolors12 = __toESM(require_picocolors(), 1);
120744
+ var import_picocolors13 = __toESM(require_picocolors(), 1);
120500
120745
  function registerMigrateCommands(cli, ctx) {
120501
120746
  cli.command("migrate-from-supabase", "Migrate a Supabase project to PocketBase: schema + data-import script, optionally rewrite SDK calls").option("--project <ref>", "Supabase project reference (the 20-char id from the project URL)").option("--token <token>", "Supabase personal access token (sbp_...). Read from SUPABASE_ACCESS_TOKEN if omitted.").option("--inspect-only", "Only print the schema that would migrate — read-only, no file writes").option("--output-dir <dir>", "Where to write the preview files (default: ./migration-preview/)").option("--rewrite-client", "Also convert your Supabase SDK calls to PocketBase across the project").option("--apply", "With --rewrite-client: overwrite files in place (originals backed up first). Default: preview only.").option("--root <dir>", "Project root to rewrite with --rewrite-client (default: current directory)").option("--no-scaffold-client", "With --rewrite-client: don't generate a PocketBase client module / insert pb imports (default: scaffold one)").option("--status", "Show a migration scoreboard for the local project — what's still on Supabase vs PocketBase. Local scan, no Supabase token needed.").option("--build", "With --status: also run the project build as a final gate").option("--pb-identifier <name>", "With --status: the PocketBase client identifier to check for (default: pb). Match what the rewrite used.").option("--json", "Output the structured result as JSON").action(async (options) => {
120502
120747
  if (options.status) {
@@ -120519,15 +120764,15 @@ function registerMigrateCommands(cli, ctx) {
120519
120764
  const projectRef = options.project;
120520
120765
  const token = options.token ?? ctx.env.SUPABASE_ACCESS_TOKEN;
120521
120766
  if (options.apply && !options.rewriteClient) {
120522
- await writeLine2(ctx, `${import_picocolors12.default.red("✗")} --apply only applies to the client rewrite. Pass --rewrite-client --apply together.`);
120767
+ await writeLine2(ctx, `${import_picocolors13.default.red("✗")} --apply only applies to the client rewrite. Pass --rewrite-client --apply together.`);
120523
120768
  throw new SilentExitError(1);
120524
120769
  }
120525
120770
  if (!projectRef) {
120526
- await writeLine2(ctx, `${import_picocolors12.default.red("✗")} Missing --project. Pass your Supabase project ref (e.g. --project abcdefghijklmnopqrst).`);
120771
+ await writeLine2(ctx, `${import_picocolors13.default.red("✗")} Missing --project. Pass your Supabase project ref (e.g. --project abcdefghijklmnopqrst).`);
120527
120772
  throw new SilentExitError(1);
120528
120773
  }
120529
120774
  if (!token) {
120530
- await writeLine2(ctx, `${import_picocolors12.default.red("✗")} Missing --token. Pass --token sbp_... or set SUPABASE_ACCESS_TOKEN. Generate one at https://supabase.com/dashboard/account/tokens`);
120775
+ await writeLine2(ctx, `${import_picocolors13.default.red("✗")} Missing --token. Pass --token sbp_... or set SUPABASE_ACCESS_TOKEN. Generate one at https://supabase.com/dashboard/account/tokens`);
120531
120776
  throw new SilentExitError(1);
120532
120777
  }
120533
120778
  if (options.inspectOnly) {
@@ -120576,182 +120821,182 @@ function registerMigrateCommands(cli, ctx) {
120576
120821
  async function renderInspectHuman(ctx, result) {
120577
120822
  if (result.status === "failed") {
120578
120823
  const err = result.error;
120579
- await writeLine2(ctx, `${import_picocolors12.default.red("✗")} ${err?.title ?? "Inspection failed"}`);
120824
+ await writeLine2(ctx, `${import_picocolors13.default.red("✗")} ${err?.title ?? "Inspection failed"}`);
120580
120825
  if (err?.explanation)
120581
120826
  await writeLine2(ctx, ` ${err.explanation}`);
120582
120827
  if (err?.suggestion)
120583
- await writeLine2(ctx, import_picocolors12.default.dim(` ${err.suggestion}`));
120828
+ await writeLine2(ctx, import_picocolors13.default.dim(` ${err.suggestion}`));
120584
120829
  return;
120585
120830
  }
120586
- await writeLine2(ctx, `${import_picocolors12.default.green("✓")} ${result.summary}`);
120831
+ await writeLine2(ctx, `${import_picocolors13.default.green("✓")} ${result.summary}`);
120587
120832
  await writeLine2(ctx, "");
120588
- await writeLine2(ctx, import_picocolors12.default.bold("Tables"));
120833
+ await writeLine2(ctx, import_picocolors13.default.bold("Tables"));
120589
120834
  if (result.tables.length === 0) {
120590
- await writeLine2(ctx, import_picocolors12.default.dim(" (none in user schemas)"));
120835
+ await writeLine2(ctx, import_picocolors13.default.dim(" (none in user schemas)"));
120591
120836
  }
120592
120837
  for (const table of result.tables) {
120593
120838
  const pkCount = table.columns.filter((c) => c.isPrimaryKey).length;
120594
120839
  const fkCount = table.columns.filter((c) => c.foreignKey !== null).length;
120595
120840
  const policyCount = table.rlsPolicies.length;
120596
120841
  const rowEstimate = table.estimatedRowCount.toLocaleString();
120597
- await writeLine2(ctx, ` ${import_picocolors12.default.cyan(`${table.schema}.${table.name}`)} ${import_picocolors12.default.dim(`cols=${table.columns.length} pk=${pkCount} fk=${fkCount} rls=${policyCount} rows≈${rowEstimate}`)}`);
120842
+ await writeLine2(ctx, ` ${import_picocolors13.default.cyan(`${table.schema}.${table.name}`)} ${import_picocolors13.default.dim(`cols=${table.columns.length} pk=${pkCount} fk=${fkCount} rls=${policyCount} rows≈${rowEstimate}`)}`);
120598
120843
  }
120599
120844
  if (result.storageBuckets.length > 0) {
120600
120845
  await writeLine2(ctx, "");
120601
- await writeLine2(ctx, import_picocolors12.default.bold("Storage buckets"));
120846
+ await writeLine2(ctx, import_picocolors13.default.bold("Storage buckets"));
120602
120847
  for (const bucket of result.storageBuckets) {
120603
120848
  const visibility = bucket.public ? "public" : "private";
120604
- await writeLine2(ctx, ` ${import_picocolors12.default.cyan(bucket.name)} ${import_picocolors12.default.dim(`(${visibility})`)}`);
120849
+ await writeLine2(ctx, ` ${import_picocolors13.default.cyan(bucket.name)} ${import_picocolors13.default.dim(`(${visibility})`)}`);
120605
120850
  }
120606
120851
  }
120607
120852
  if (result.authUsersCount !== null) {
120608
120853
  await writeLine2(ctx, "");
120609
- await writeLine2(ctx, `${import_picocolors12.default.bold("Auth users")}: ${result.authUsersCount}`);
120854
+ await writeLine2(ctx, `${import_picocolors13.default.bold("Auth users")}: ${result.authUsersCount}`);
120610
120855
  }
120611
120856
  if (result.warnings.length > 0) {
120612
120857
  await writeLine2(ctx, "");
120613
- await writeLine2(ctx, import_picocolors12.default.bold(`Warnings (${result.warnings.length})`));
120858
+ await writeLine2(ctx, import_picocolors13.default.bold(`Warnings (${result.warnings.length})`));
120614
120859
  for (const warning of result.warnings) {
120615
- await writeLine2(ctx, ` ${import_picocolors12.default.yellow("⚠")} ${warning}`);
120860
+ await writeLine2(ctx, ` ${import_picocolors13.default.yellow("⚠")} ${warning}`);
120616
120861
  }
120617
120862
  }
120618
120863
  await writeLine2(ctx, "");
120619
- await writeLine2(ctx, import_picocolors12.default.dim("--inspect-only is read-only. Drop the flag to also write the PocketBase schema preview + pb_migrate.js data-import script to ./migration-preview/."));
120864
+ await writeLine2(ctx, import_picocolors13.default.dim("--inspect-only is read-only. Drop the flag to also write the PocketBase schema preview + pb_migrate.js data-import script to ./migration-preview/."));
120620
120865
  }
120621
120866
  async function renderSchemaHuman(ctx, result, opts) {
120622
120867
  if (result.status === "failed") {
120623
120868
  const err = result.error;
120624
- await writeLine2(ctx, `${import_picocolors12.default.red("✗")} ${err?.title ?? "Migration preview failed"}`);
120869
+ await writeLine2(ctx, `${import_picocolors13.default.red("✗")} ${err?.title ?? "Migration preview failed"}`);
120625
120870
  if (err?.explanation)
120626
120871
  await writeLine2(ctx, ` ${err.explanation}`);
120627
120872
  if (err?.suggestion)
120628
- await writeLine2(ctx, import_picocolors12.default.dim(` ${err.suggestion}`));
120873
+ await writeLine2(ctx, import_picocolors13.default.dim(` ${err.suggestion}`));
120629
120874
  return;
120630
120875
  }
120631
- await writeLine2(ctx, `${import_picocolors12.default.green("✓")} ${result.summary}`);
120876
+ await writeLine2(ctx, `${import_picocolors13.default.green("✓")} ${result.summary}`);
120632
120877
  await writeLine2(ctx, "");
120633
- await writeLine2(ctx, import_picocolors12.default.bold("Files written"));
120878
+ await writeLine2(ctx, import_picocolors13.default.bold("Files written"));
120634
120879
  for (const path of result.filesWritten) {
120635
- await writeLine2(ctx, ` ${import_picocolors12.default.cyan(path)}`);
120880
+ await writeLine2(ctx, ` ${import_picocolors13.default.cyan(path)}`);
120636
120881
  }
120637
120882
  const preview = result.preview;
120638
120883
  if (!preview)
120639
120884
  return;
120640
120885
  if (preview.collections.length > 0) {
120641
120886
  await writeLine2(ctx, "");
120642
- await writeLine2(ctx, import_picocolors12.default.bold("Collections"));
120887
+ await writeLine2(ctx, import_picocolors13.default.bold("Collections"));
120643
120888
  for (const collection of preview.collections) {
120644
120889
  const fields = collection.schema.length;
120645
120890
  const relations = collection.schema.filter((f) => f.type === "relation").length;
120646
- await writeLine2(ctx, ` ${import_picocolors12.default.cyan(collection.name)} ${import_picocolors12.default.dim(`← ${collection.sourceTable} fields=${fields} relations=${relations}`)}`);
120891
+ await writeLine2(ctx, ` ${import_picocolors13.default.cyan(collection.name)} ${import_picocolors13.default.dim(`← ${collection.sourceTable} fields=${fields} relations=${relations}`)}`);
120647
120892
  }
120648
120893
  }
120649
120894
  if (preview.warnings.length > 0) {
120650
120895
  await writeLine2(ctx, "");
120651
- await writeLine2(ctx, import_picocolors12.default.bold(`Warnings (${preview.warnings.length})`));
120896
+ await writeLine2(ctx, import_picocolors13.default.bold(`Warnings (${preview.warnings.length})`));
120652
120897
  for (const warning of preview.warnings.slice(0, 10)) {
120653
- await writeLine2(ctx, ` ${import_picocolors12.default.yellow("⚠")} ${warning}`);
120898
+ await writeLine2(ctx, ` ${import_picocolors13.default.yellow("⚠")} ${warning}`);
120654
120899
  }
120655
120900
  if (preview.warnings.length > 10) {
120656
- await writeLine2(ctx, import_picocolors12.default.dim(` …and ${preview.warnings.length - 10} more — see MIGRATION_NOTES.md`));
120901
+ await writeLine2(ctx, import_picocolors13.default.dim(` …and ${preview.warnings.length - 10} more — see MIGRATION_NOTES.md`));
120657
120902
  }
120658
120903
  }
120659
120904
  await writeLine2(ctx, "");
120660
- await writeLine2(ctx, import_picocolors12.default.dim("Preview only — nothing has touched your PocketBase yet. ./migration-preview/ includes pb_migrate.js, the data-import script: review MIGRATION_NOTES.md, then run it against your app's PocketBase to copy your rows over."));
120905
+ await writeLine2(ctx, import_picocolors13.default.dim("Preview only — nothing has touched your PocketBase yet. ./migration-preview/ includes pb_migrate.js, the data-import script: review MIGRATION_NOTES.md, then run it against your app's PocketBase to copy your rows over."));
120661
120906
  if (!opts.clientRewriteRequested) {
120662
- await writeLine2(ctx, import_picocolors12.default.dim("Add --rewrite-client to also convert your Supabase SDK calls to PocketBase (preview first; --apply writes in place)."));
120907
+ await writeLine2(ctx, import_picocolors13.default.dim("Add --rewrite-client to also convert your Supabase SDK calls to PocketBase (preview first; --apply writes in place)."));
120663
120908
  }
120664
120909
  }
120665
120910
  async function renderRewriteHuman(ctx, result) {
120666
120911
  await writeLine2(ctx, "");
120667
120912
  if (result.status === "failed") {
120668
120913
  const err = result.error;
120669
- await writeLine2(ctx, `${import_picocolors12.default.red("✗")} ${err?.title ?? "Client rewrite failed"}`);
120914
+ await writeLine2(ctx, `${import_picocolors13.default.red("✗")} ${err?.title ?? "Client rewrite failed"}`);
120670
120915
  if (err?.explanation)
120671
120916
  await writeLine2(ctx, ` ${err.explanation}`);
120672
120917
  if (err?.suggestion)
120673
- await writeLine2(ctx, import_picocolors12.default.dim(` ${err.suggestion}`));
120918
+ await writeLine2(ctx, import_picocolors13.default.dim(` ${err.suggestion}`));
120674
120919
  return;
120675
120920
  }
120676
- await writeLine2(ctx, `${import_picocolors12.default.green("✓")} ${result.summary}`);
120921
+ await writeLine2(ctx, `${import_picocolors13.default.green("✓")} ${result.summary}`);
120677
120922
  if (result.filesWritten.length > 0) {
120678
120923
  await writeLine2(ctx, "");
120679
- await writeLine2(ctx, import_picocolors12.default.bold("Files written"));
120924
+ await writeLine2(ctx, import_picocolors13.default.bold("Files written"));
120680
120925
  for (const path of result.filesWritten.slice(0, 20)) {
120681
- await writeLine2(ctx, ` ${import_picocolors12.default.cyan(path)}`);
120926
+ await writeLine2(ctx, ` ${import_picocolors13.default.cyan(path)}`);
120682
120927
  }
120683
120928
  if (result.filesWritten.length > 20) {
120684
- await writeLine2(ctx, import_picocolors12.default.dim(` …and ${result.filesWritten.length - 20} more`));
120929
+ await writeLine2(ctx, import_picocolors13.default.dim(` …and ${result.filesWritten.length - 20} more`));
120685
120930
  }
120686
120931
  }
120687
120932
  if (result.flags.length > 0) {
120688
120933
  await writeLine2(ctx, "");
120689
- await writeLine2(ctx, import_picocolors12.default.bold("Needs review (see migration-preview/REWRITE_NOTES.md)"));
120934
+ await writeLine2(ctx, import_picocolors13.default.bold("Needs review (see migration-preview/REWRITE_NOTES.md)"));
120690
120935
  for (const flag of result.flags.slice(0, 15)) {
120691
120936
  const loc = flag.line !== undefined ? `:${flag.line}` : "";
120692
120937
  const tag = flag.kind === "manual_review" ? "rewritten, verify semantics" : "left untouched";
120693
- await writeLine2(ctx, ` ${import_picocolors12.default.yellow("⚠")} ${import_picocolors12.default.cyan(`${flag.file}${loc}`)} ${flag.pattern} ${import_picocolors12.default.dim(`(${tag}) — ${flag.note}`)}`);
120938
+ await writeLine2(ctx, ` ${import_picocolors13.default.yellow("⚠")} ${import_picocolors13.default.cyan(`${flag.file}${loc}`)} ${flag.pattern} ${import_picocolors13.default.dim(`(${tag}) — ${flag.note}`)}`);
120694
120939
  }
120695
120940
  if (result.flags.length > 15) {
120696
- await writeLine2(ctx, import_picocolors12.default.dim(` …and ${result.flags.length - 15} more — see REWRITE_NOTES.md`));
120941
+ await writeLine2(ctx, import_picocolors13.default.dim(` …and ${result.flags.length - 15} more — see REWRITE_NOTES.md`));
120697
120942
  }
120698
120943
  }
120699
120944
  if (result.skipped.length > 0) {
120700
- await writeLine2(ctx, import_picocolors12.default.dim(`
120945
+ await writeLine2(ctx, import_picocolors13.default.dim(`
120701
120946
  ${result.skipped.length} file(s) couldn't be parsed and were skipped — listed in REWRITE_NOTES.md.`));
120702
120947
  }
120703
120948
  await writeLine2(ctx, "");
120704
120949
  if (result.mode === "apply") {
120705
- await writeLine2(ctx, import_picocolors12.default.dim(result.changedCount > 0 ? "Files rewritten in place. Originals are in ./migration-preview/backup/ — diff against them (or git) before committing." : "No files were rewritten — the work left is the manual review above."));
120950
+ await writeLine2(ctx, import_picocolors13.default.dim(result.changedCount > 0 ? "Files rewritten in place. Originals are in ./migration-preview/backup/ — diff against them (or git) before committing." : "No files were rewritten — the work left is the manual review above."));
120706
120951
  } else {
120707
- await writeLine2(ctx, import_picocolors12.default.dim(result.changedCount > 0 ? "Preview only — diff ./migration-preview/rewritten/ against your project, then re-run with --rewrite-client --apply to write in place." : "Preview only — no files needed automatic rewriting; work through the manual review above."));
120952
+ await writeLine2(ctx, import_picocolors13.default.dim(result.changedCount > 0 ? "Preview only — diff ./migration-preview/rewritten/ against your project, then re-run with --rewrite-client --apply to write in place." : "Preview only — no files needed automatic rewriting; work through the manual review above."));
120708
120953
  }
120709
120954
  }
120710
120955
  async function renderStatusHuman(ctx, result) {
120711
120956
  if (result.status === "failed") {
120712
120957
  const err = result.error;
120713
- await writeLine2(ctx, `${import_picocolors12.default.red("✗")} ${err?.title ?? "Migration status scan failed"}`);
120958
+ await writeLine2(ctx, `${import_picocolors13.default.red("✗")} ${err?.title ?? "Migration status scan failed"}`);
120714
120959
  if (err?.explanation)
120715
120960
  await writeLine2(ctx, ` ${err.explanation}`);
120716
120961
  if (err?.suggestion)
120717
- await writeLine2(ctx, import_picocolors12.default.dim(` ${err.suggestion}`));
120962
+ await writeLine2(ctx, import_picocolors13.default.dim(` ${err.suggestion}`));
120718
120963
  return;
120719
120964
  }
120720
- await writeLine2(ctx, import_picocolors12.default.bold("Migration status"));
120965
+ await writeLine2(ctx, import_picocolors13.default.bold("Migration status"));
120721
120966
  await writeLine2(ctx, "");
120722
120967
  for (const check2 of result.checks) {
120723
- const mark = check2.status === "pass" ? import_picocolors12.default.green("✓") : check2.status === "fail" ? import_picocolors12.default.red("✗") : check2.status === "warn" ? import_picocolors12.default.yellow("⚠") : import_picocolors12.default.dim("•");
120724
- const count = check2.count > 0 ? import_picocolors12.default.dim(` (${check2.count})`) : "";
120725
- await writeLine2(ctx, ` ${mark} ${check2.label}${count} ${import_picocolors12.default.dim(`— ${check2.detail}`)}`);
120968
+ const mark = check2.status === "pass" ? import_picocolors13.default.green("✓") : check2.status === "fail" ? import_picocolors13.default.red("✗") : check2.status === "warn" ? import_picocolors13.default.yellow("⚠") : import_picocolors13.default.dim("•");
120969
+ const count = check2.count > 0 ? import_picocolors13.default.dim(` (${check2.count})`) : "";
120970
+ await writeLine2(ctx, ` ${mark} ${check2.label}${count} ${import_picocolors13.default.dim(`— ${check2.detail}`)}`);
120726
120971
  for (const file2 of (check2.files ?? []).slice(0, 10)) {
120727
- await writeLine2(ctx, import_picocolors12.default.dim(` - ${file2}`));
120972
+ await writeLine2(ctx, import_picocolors13.default.dim(` - ${file2}`));
120728
120973
  }
120729
120974
  if ((check2.files?.length ?? 0) > 10) {
120730
- await writeLine2(ctx, import_picocolors12.default.dim(` …and ${(check2.files?.length ?? 0) - 10} more`));
120975
+ await writeLine2(ctx, import_picocolors13.default.dim(` …and ${(check2.files?.length ?? 0) - 10} more`));
120731
120976
  }
120732
120977
  }
120733
120978
  if (result.build) {
120734
120979
  await writeLine2(ctx, "");
120735
- const mark = !result.build.ran ? import_picocolors12.default.dim("•") : result.build.passed ? import_picocolors12.default.green("✓") : import_picocolors12.default.red("✗");
120736
- await writeLine2(ctx, ` ${mark} Build ${import_picocolors12.default.dim(`— ${result.build.detail}`)}`);
120980
+ const mark = !result.build.ran ? import_picocolors13.default.dim("•") : result.build.passed ? import_picocolors13.default.green("✓") : import_picocolors13.default.red("✗");
120981
+ await writeLine2(ctx, ` ${mark} Build ${import_picocolors13.default.dim(`— ${result.build.detail}`)}`);
120737
120982
  }
120738
120983
  await writeLine2(ctx, "");
120739
- await writeLine2(ctx, import_picocolors12.default.dim(result.summary));
120984
+ await writeLine2(ctx, import_picocolors13.default.dim(result.summary));
120740
120985
  }
120741
120986
 
120742
120987
  // src/commands/sharing.ts
120743
- var import_picocolors13 = __toESM(require_picocolors(), 1);
120988
+ var import_picocolors14 = __toESM(require_picocolors(), 1);
120744
120989
  function registerSharingCommands(cli, ctx) {
120745
120990
  cli.command("share [action] [arg] [valueOrRole]", "Per-app collaborator invites: list | invite <email> | role <shareId> <role> | revoke <shareId>").option("--app <name>", "App name (overrides percher.toml)").option("--role <role>", "(invite only) viewer | admin — viewer is read-only, admin can deploy + change config").action(async (action, arg, valueOrRole, options) => {
120746
120991
  if (action === "list" || action === undefined) {
120747
120992
  const rows = await shareList(ctx, { app: options.app });
120748
120993
  if (rows.length === 0) {
120749
- await writeLine2(ctx, import_picocolors13.default.dim("No collaborators."));
120994
+ await writeLine2(ctx, import_picocolors14.default.dim("No collaborators."));
120750
120995
  return;
120751
120996
  }
120752
120997
  for (const r of rows) {
120753
- const state = r.accepted ? import_picocolors13.default.green("accepted") : import_picocolors13.default.yellow("pending");
120754
- const roleStr = r.role === "admin" ? import_picocolors13.default.cyan("admin") : import_picocolors13.default.dim("viewer");
120998
+ const state = r.accepted ? import_picocolors14.default.green("accepted") : import_picocolors14.default.yellow("pending");
120999
+ const roleStr = r.role === "admin" ? import_picocolors14.default.cyan("admin") : import_picocolors14.default.dim("viewer");
120755
121000
  await writeLine2(ctx, `${r.id} ${roleStr.padEnd(15)} ${state.padEnd(15)} ${r.email}`);
120756
121001
  }
120757
121002
  return;
@@ -120769,8 +121014,8 @@ function registerSharingCommands(cli, ctx) {
120769
121014
  email: arg,
120770
121015
  role: roleRaw
120771
121016
  });
120772
- await writeLine2(ctx, `${import_picocolors13.default.green("✓")} Invited ${created.email} as ${created.role} (id ${created.id})`);
120773
- await writeLine2(ctx, import_picocolors13.default.dim(`Pending until accepted — invite expires ${new Date(created.expiresAt).toLocaleDateString()}.`));
121017
+ await writeLine2(ctx, `${import_picocolors14.default.green("✓")} Invited ${created.email} as ${created.role} (id ${created.id})`);
121018
+ await writeLine2(ctx, import_picocolors14.default.dim(`Pending until accepted — invite expires ${new Date(created.expiresAt).toLocaleDateString()}.`));
120774
121019
  return;
120775
121020
  }
120776
121021
  if (action === "role") {
@@ -120785,7 +121030,7 @@ function registerSharingCommands(cli, ctx) {
120785
121030
  shareId: arg,
120786
121031
  role: valueOrRole
120787
121032
  });
120788
- await writeLine2(ctx, `${import_picocolors13.default.green("✓")} ${updated.email} is now ${import_picocolors13.default.cyan(updated.role)}`);
121033
+ await writeLine2(ctx, `${import_picocolors14.default.green("✓")} ${updated.email} is now ${import_picocolors14.default.cyan(updated.role)}`);
120789
121034
  return;
120790
121035
  }
120791
121036
  if (action === "revoke") {
@@ -120793,7 +121038,7 @@ function registerSharingCommands(cli, ctx) {
120793
121038
  throw new Error("Usage: percher share revoke <shareId> [--app <name>]");
120794
121039
  }
120795
121040
  await shareRevoke(ctx, { app: options.app, shareId: arg });
120796
- await writeLine2(ctx, `${import_picocolors13.default.green("✓")} Revoked share ${arg}`);
121041
+ await writeLine2(ctx, `${import_picocolors14.default.green("✓")} Revoked share ${arg}`);
120797
121042
  return;
120798
121043
  }
120799
121044
  throw new Error(`Unknown share action "${action}". Use list | invite <email> | role <shareId> <role> | revoke <shareId>.`);
@@ -120806,11 +121051,11 @@ function registerSharingCommands(cli, ctx) {
120806
121051
  if (action === undefined || action === "list") {
120807
121052
  const pending = await invitesList(ctx, {});
120808
121053
  if (pending.length === 0) {
120809
- await writeLine2(ctx, import_picocolors13.default.dim("No pending invites."));
121054
+ await writeLine2(ctx, import_picocolors14.default.dim("No pending invites."));
120810
121055
  return;
120811
121056
  }
120812
121057
  for (const inv of pending) {
120813
- const roleStr = inv.role === "admin" ? import_picocolors13.default.cyan("admin") : import_picocolors13.default.dim("viewer");
121058
+ const roleStr = inv.role === "admin" ? import_picocolors14.default.cyan("admin") : import_picocolors14.default.dim("viewer");
120814
121059
  await writeLine2(ctx, `${inv.id} ${roleStr.padEnd(15)} ${inv.appName.padEnd(24)} from ${inv.invitedBy}`);
120815
121060
  }
120816
121061
  return;
@@ -120821,10 +121066,10 @@ function registerSharingCommands(cli, ctx) {
120821
121066
  }
120822
121067
  const accepted = await invitesAccept(ctx, { shareId });
120823
121068
  if (accepted.alreadyAccepted) {
120824
- await writeLine2(ctx, import_picocolors13.default.dim(`Invite ${shareId} was already accepted.`));
121069
+ await writeLine2(ctx, import_picocolors14.default.dim(`Invite ${shareId} was already accepted.`));
120825
121070
  return;
120826
121071
  }
120827
- await writeLine2(ctx, `${import_picocolors13.default.green("✓")} Accepted — you now have ${import_picocolors13.default.cyan(accepted.role)} access to the shared app.`);
121072
+ await writeLine2(ctx, `${import_picocolors14.default.green("✓")} Accepted — you now have ${import_picocolors14.default.cyan(accepted.role)} access to the shared app.`);
120828
121073
  return;
120829
121074
  }
120830
121075
  if (action === "decline") {
@@ -120832,7 +121077,7 @@ function registerSharingCommands(cli, ctx) {
120832
121077
  throw new Error("Usage: percher invites decline <shareId>");
120833
121078
  }
120834
121079
  await invitesDecline(ctx, { shareId });
120835
- await writeLine2(ctx, `${import_picocolors13.default.green("✓")} Declined invite ${shareId}.`);
121080
+ await writeLine2(ctx, `${import_picocolors14.default.green("✓")} Declined invite ${shareId}.`);
120836
121081
  return;
120837
121082
  }
120838
121083
  throw new Error(`Unknown invites action "${action}". Use list | accept <shareId> | decline <shareId>.`);
@@ -120843,7 +121088,7 @@ function registerSharingCommands(cli, ctx) {
120843
121088
  }
120844
121089
 
120845
121090
  // src/commands/status.ts
120846
- var import_picocolors14 = __toESM(require_picocolors(), 1);
121091
+ var import_picocolors15 = __toESM(require_picocolors(), 1);
120847
121092
  function registerStatusCommands(cli, ctx) {
120848
121093
  cli.command("status", "Show Percher platform status (API + worker health)").option("--json", "Output JSON instead of formatted text").action(async (options) => {
120849
121094
  const result = await status(ctx, { json: Boolean(options.json) });
@@ -120859,34 +121104,34 @@ function registerStatusCommands(cli, ctx) {
120859
121104
  });
120860
121105
  }
120861
121106
  async function renderHumanStatus(ctx, result) {
120862
- const symbol2 = result.verdict === "operational" ? import_picocolors14.default.green("✓") : result.verdict === "worker-degraded" ? import_picocolors14.default.yellow("⚠") : import_picocolors14.default.red("✗");
121107
+ const symbol2 = result.verdict === "operational" ? import_picocolors15.default.green("✓") : result.verdict === "worker-degraded" ? import_picocolors15.default.yellow("⚠") : import_picocolors15.default.red("✗");
120863
121108
  await writeLine2(ctx, `${symbol2} ${result.summary}`);
120864
121109
  if (!result.health)
120865
121110
  return;
120866
121111
  const worker = result.health.worker;
120867
121112
  if (!worker) {
120868
- await writeLine2(ctx, import_picocolors14.default.dim(" worker: not configured (local Docker socket)"));
121113
+ await writeLine2(ctx, import_picocolors15.default.dim(" worker: not configured (local Docker socket)"));
120869
121114
  return;
120870
121115
  }
120871
121116
  if (worker.health) {
120872
121117
  const ok = worker.health.ok;
120873
- const symbol3 = ok ? import_picocolors14.default.green("✓") : import_picocolors14.default.red("✗");
121118
+ const symbol3 = ok ? import_picocolors15.default.green("✓") : import_picocolors15.default.red("✗");
120874
121119
  const lastOk = worker.health.lastOkAt ?? "never";
120875
- await writeLine2(ctx, ` ${symbol3} worker health: ${ok ? "ok" : `failing (${worker.health.consecutiveErrs} consecutive)`} last-ok=${import_picocolors14.default.dim(lastOk)}`);
121120
+ await writeLine2(ctx, ` ${symbol3} worker health: ${ok ? "ok" : `failing (${worker.health.consecutiveErrs} consecutive)`} last-ok=${import_picocolors15.default.dim(lastOk)}`);
120876
121121
  }
120877
121122
  if (worker.circuitBreaker) {
120878
121123
  const breaker = worker.circuitBreaker;
120879
- const symbol3 = breaker.open ? import_picocolors14.default.red("✗") : import_picocolors14.default.green("✓");
121124
+ const symbol3 = breaker.open ? import_picocolors15.default.red("✗") : import_picocolors15.default.green("✓");
120880
121125
  const state = breaker.open ? "open (deploys fail fast)" : "closed";
120881
121126
  await writeLine2(ctx, ` ${symbol3} circuit breaker: ${state} ${breaker.failuresInWindow}/${breaker.threshold} failures in last ${Math.round(breaker.windowMs / 1000)}s`);
120882
121127
  }
120883
121128
  if (result.health.version) {
120884
- await writeLine2(ctx, import_picocolors14.default.dim(` api version: ${result.health.version}`));
121129
+ await writeLine2(ctx, import_picocolors15.default.dim(` api version: ${result.health.version}`));
120885
121130
  }
120886
121131
  }
120887
121132
 
120888
121133
  // src/commands/token.ts
120889
- var import_picocolors15 = __toESM(require_picocolors(), 1);
121134
+ var import_picocolors16 = __toESM(require_picocolors(), 1);
120890
121135
  var VALID_SCOPES = ["deploy", "env", "read", "full"];
120891
121136
  function scopeLabel(scopes, appName) {
120892
121137
  const s = scopes && scopes.length > 0 ? scopes.join("+") : "full";
@@ -120895,22 +121140,22 @@ function scopeLabel(scopes, appName) {
120895
121140
  function registerTokenCommands(cli, ctx) {
120896
121141
  cli.command("token [action] [arg]", "Manage API tokens: create | list | revoke").option("--scope <scope>", "(create) deploy | env | read | full (default: full)").option("--app <name>", "(create) bind the token to a single app").option("--ttl-days <n>", "(create) token lifetime in days").action(async (action, arg, options) => {
120897
121142
  if (!ctx.client.token) {
120898
- await writeLine2(ctx, `${import_picocolors15.default.red("✗")} Not logged in. Run: percher login`);
121143
+ await writeLine2(ctx, `${import_picocolors16.default.red("✗")} Not logged in. Run: percher login`);
120899
121144
  throw new SilentExitError;
120900
121145
  }
120901
121146
  if (action === "create") {
120902
121147
  if (!arg) {
120903
- await writeLine2(ctx, `${import_picocolors15.default.red("✗")} A name is required. Usage: percher token create <name> [--scope deploy] [--app <name>]`);
121148
+ await writeLine2(ctx, `${import_picocolors16.default.red("✗")} A name is required. Usage: percher token create <name> [--scope deploy] [--app <name>]`);
120904
121149
  throw new SilentExitError;
120905
121150
  }
120906
121151
  const scope = options.scope ?? "full";
120907
121152
  if (!VALID_SCOPES.includes(scope)) {
120908
- await writeLine2(ctx, `${import_picocolors15.default.red("✗")} Invalid --scope "${options.scope}". Use one of: ${VALID_SCOPES.join(", ")}.`);
121153
+ await writeLine2(ctx, `${import_picocolors16.default.red("✗")} Invalid --scope "${options.scope}". Use one of: ${VALID_SCOPES.join(", ")}.`);
120909
121154
  throw new SilentExitError;
120910
121155
  }
120911
121156
  const ttlDays = options.ttlDays !== undefined ? Number(options.ttlDays) : undefined;
120912
121157
  if (ttlDays !== undefined && (!Number.isInteger(ttlDays) || ttlDays <= 0)) {
120913
- await writeLine2(ctx, `${import_picocolors15.default.red("✗")} --ttl-days must be a positive whole number.`);
121158
+ await writeLine2(ctx, `${import_picocolors16.default.red("✗")} --ttl-days must be a positive whole number.`);
120914
121159
  throw new SilentExitError;
120915
121160
  }
120916
121161
  const created = await tokenCreate(ctx, {
@@ -120919,36 +121164,36 @@ function registerTokenCommands(cli, ctx) {
120919
121164
  app: options.app,
120920
121165
  ttlDays
120921
121166
  });
120922
- await writeLine2(ctx, `${import_picocolors15.default.green("✓")} Token created: ${import_picocolors15.default.bold(created.name)} ${import_picocolors15.default.dim(`(${scopeLabel(created.scopes, created.appName)})`)}`);
121167
+ await writeLine2(ctx, `${import_picocolors16.default.green("✓")} Token created: ${import_picocolors16.default.bold(created.name)} ${import_picocolors16.default.dim(`(${scopeLabel(created.scopes, created.appName)})`)}`);
120923
121168
  await writeLine2(ctx, "");
120924
- await writeLine2(ctx, ` ${import_picocolors15.default.bold(created.token)}`);
120925
- await writeLine2(ctx, import_picocolors15.default.yellow(" ↑ Store this now — the secret is shown only once and can't be retrieved."));
121169
+ await writeLine2(ctx, ` ${import_picocolors16.default.bold(created.token)}`);
121170
+ await writeLine2(ctx, import_picocolors16.default.yellow(" ↑ Store this now — the secret is shown only once and can't be retrieved."));
120926
121171
  await writeLine2(ctx, "");
120927
- await writeLine2(ctx, import_picocolors15.default.dim(" Use it in CI / an agent:"));
120928
- await writeLine2(ctx, import_picocolors15.default.dim(` PERCHER_TOKEN=${created.token} bunx percher publish`));
121172
+ await writeLine2(ctx, import_picocolors16.default.dim(" Use it in CI / an agent:"));
121173
+ await writeLine2(ctx, import_picocolors16.default.dim(` PERCHER_TOKEN=${created.token} bunx percher publish`));
120929
121174
  return;
120930
121175
  }
120931
121176
  if (action === "list") {
120932
121177
  const tokens = await tokenList(ctx);
120933
121178
  if (tokens.length === 0) {
120934
- await writeLine2(ctx, import_picocolors15.default.dim("No API tokens."));
121179
+ await writeLine2(ctx, import_picocolors16.default.dim("No API tokens."));
120935
121180
  return;
120936
121181
  }
120937
121182
  for (const t of tokens) {
120938
121183
  const expires = t.expiresAt ? `expires ${new Date(t.expiresAt).toISOString().slice(0, 10)}` : "no expiry";
120939
- await writeLine2(ctx, `${import_picocolors15.default.bold(t.id)} ${t.name.padEnd(20)} ${import_picocolors15.default.dim(scopeLabel(t.scopes, t.appName))} ${import_picocolors15.default.dim(expires)}`);
121184
+ await writeLine2(ctx, `${import_picocolors16.default.bold(t.id)} ${t.name.padEnd(20)} ${import_picocolors16.default.dim(scopeLabel(t.scopes, t.appName))} ${import_picocolors16.default.dim(expires)}`);
120940
121185
  }
120941
121186
  await writeLine2(ctx, "");
120942
- await writeLine2(ctx, import_picocolors15.default.dim("Revoke one with: percher token revoke <id>"));
121187
+ await writeLine2(ctx, import_picocolors16.default.dim("Revoke one with: percher token revoke <id>"));
120943
121188
  return;
120944
121189
  }
120945
121190
  if (action === "revoke") {
120946
121191
  if (!arg) {
120947
- await writeLine2(ctx, `${import_picocolors15.default.red("✗")} A token id is required. Usage: percher token revoke <id> (list ids with \`percher token list\`).`);
121192
+ await writeLine2(ctx, `${import_picocolors16.default.red("✗")} A token id is required. Usage: percher token revoke <id> (list ids with \`percher token list\`).`);
120948
121193
  throw new SilentExitError;
120949
121194
  }
120950
121195
  await tokenRevoke(ctx, { id: arg });
120951
- await writeLine2(ctx, `${import_picocolors15.default.green("✓")} Token revoked.`);
121196
+ await writeLine2(ctx, `${import_picocolors16.default.green("✓")} Token revoked.`);
120952
121197
  return;
120953
121198
  }
120954
121199
  throw new Error(`Unknown token action "${action ?? ""}". Use: create | list | revoke.`);
@@ -120959,17 +121204,17 @@ function registerTokenCommands(cli, ctx) {
120959
121204
  }
120960
121205
 
120961
121206
  // src/commands/transfers.ts
120962
- var import_picocolors16 = __toESM(require_picocolors(), 1);
121207
+ var import_picocolors17 = __toESM(require_picocolors(), 1);
120963
121208
  function registerTransfersCommands(cli, ctx) {
120964
121209
  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) => {
120965
121210
  if (options.list) {
120966
121211
  const rows = await transferListForApp(ctx, { app: options.app });
120967
121212
  if (rows.length === 0) {
120968
- await writeLine2(ctx, import_picocolors16.default.dim("No transfers for this app."));
121213
+ await writeLine2(ctx, import_picocolors17.default.dim("No transfers for this app."));
120969
121214
  return;
120970
121215
  }
120971
121216
  for (const r of rows) {
120972
- const status2 = r.status === "pending" ? import_picocolors16.default.yellow("pending") : r.status === "accepted" ? import_picocolors16.default.green("accepted") : import_picocolors16.default.dim(r.status);
121217
+ const status2 = r.status === "pending" ? import_picocolors17.default.yellow("pending") : r.status === "accepted" ? import_picocolors17.default.green("accepted") : import_picocolors17.default.dim(r.status);
120973
121218
  await writeLine2(ctx, `${r.id} ${status2.padEnd(20)} → ${r.toEmail} (${r.createdAt})`);
120974
121219
  }
120975
121220
  return;
@@ -120982,7 +121227,7 @@ function registerTransfersCommands(cli, ctx) {
120982
121227
  app: options.app,
120983
121228
  transferId: emailOrTransferId
120984
121229
  });
120985
- await writeLine2(ctx, `${import_picocolors16.default.green("✓")} Cancelled transfer ${emailOrTransferId}`);
121230
+ await writeLine2(ctx, `${import_picocolors17.default.green("✓")} Cancelled transfer ${emailOrTransferId}`);
120986
121231
  return;
120987
121232
  }
120988
121233
  if (!emailOrTransferId) {
@@ -120992,17 +121237,17 @@ function registerTransfersCommands(cli, ctx) {
120992
121237
  app: options.app,
120993
121238
  email: emailOrTransferId
120994
121239
  });
120995
- await writeLine2(ctx, `${import_picocolors16.default.green("✓")} Transfer initiated to ${initiated.toEmail} (id ${initiated.id})`);
120996
- await writeLine2(ctx, import_picocolors16.default.dim(`Expires ${new Date(initiated.expiresAt).toLocaleDateString()}. The recipient must accept via 'percher transfers accept ${initiated.id}'.`));
121240
+ await writeLine2(ctx, `${import_picocolors17.default.green("✓")} Transfer initiated to ${initiated.toEmail} (id ${initiated.id})`);
121241
+ await writeLine2(ctx, import_picocolors17.default.dim(`Expires ${new Date(initiated.expiresAt).toLocaleDateString()}. The recipient must accept via 'percher transfers accept ${initiated.id}'.`));
120997
121242
  if (initiated.notes) {
120998
- await writeLine2(ctx, import_picocolors16.default.yellow(` ⚠ ${initiated.notes}`));
121243
+ await writeLine2(ctx, import_picocolors17.default.yellow(` ⚠ ${initiated.notes}`));
120999
121244
  }
121000
121245
  });
121001
121246
  cli.command("transfers [action] [transferId]", "Incoming ownership transfers: list (default) | accept <transferId> | decline <transferId>").action(async (action, transferId) => {
121002
121247
  if (action === undefined || action === "list") {
121003
121248
  const rows = await transfersIncoming(ctx, {});
121004
121249
  if (rows.length === 0) {
121005
- await writeLine2(ctx, import_picocolors16.default.dim("No pending incoming transfers."));
121250
+ await writeLine2(ctx, import_picocolors17.default.dim("No pending incoming transfers."));
121006
121251
  return;
121007
121252
  }
121008
121253
  for (const r of rows) {
@@ -121015,7 +121260,7 @@ function registerTransfersCommands(cli, ctx) {
121015
121260
  throw new Error("Usage: percher transfers accept <transferId>");
121016
121261
  }
121017
121262
  const accepted = await transfersAccept(ctx, { transferId });
121018
- await writeLine2(ctx, `${import_picocolors16.default.green("✓")} You now own ${import_picocolors16.default.cyan(accepted.appName)}. Run 'percher open --app ${accepted.appName}' to see it.`);
121263
+ await writeLine2(ctx, `${import_picocolors17.default.green("✓")} You now own ${import_picocolors17.default.cyan(accepted.appName)}. Run 'percher open --app ${accepted.appName}' to see it.`);
121019
121264
  return;
121020
121265
  }
121021
121266
  if (action === "decline") {
@@ -121023,7 +121268,7 @@ function registerTransfersCommands(cli, ctx) {
121023
121268
  throw new Error("Usage: percher transfers decline <transferId>");
121024
121269
  }
121025
121270
  await transfersDecline(ctx, { transferId });
121026
- await writeLine2(ctx, `${import_picocolors16.default.green("✓")} Declined transfer ${transferId}.`);
121271
+ await writeLine2(ctx, `${import_picocolors17.default.green("✓")} Declined transfer ${transferId}.`);
121027
121272
  return;
121028
121273
  }
121029
121274
  throw new Error(`Unknown transfers action "${action}". Use list | accept <id> | decline <id>.`);
@@ -121063,99 +121308,10 @@ async function prompt(question, options) {
121063
121308
  rl.close();
121064
121309
  }
121065
121310
  }
121066
-
121067
- // src/context.ts
121068
- async function createCliContext() {
121069
- const client = new PercherClient({ clientVersion: "percher-cli/0.2.5" });
121070
- const encoder = new TextEncoder;
121071
- const stdout2 = new WritableStream({
121072
- write(chunk) {
121073
- process.stdout.write(chunk);
121074
- }
121075
- });
121076
- const stderr = new WritableStream({
121077
- write(chunk) {
121078
- process.stderr.write(chunk);
121079
- }
121080
- });
121081
- return {
121082
- client,
121083
- cwd: process.cwd(),
121084
- stdout: stdout2,
121085
- stderr,
121086
- env: process.env,
121087
- prompt,
121088
- openUrl,
121089
- interactiveLogin: true,
121090
- stdinIsInteractive: process.stdin.isTTY === true && process.stdout.isTTY === true,
121091
- status(message) {
121092
- process.stderr.write(encoder.encode(`${message}
121093
- `));
121094
- }
121095
- };
121096
- }
121097
-
121098
- // src/output.ts
121099
- init_dist();
121100
- var import_picocolors17 = __toESM(require_picocolors(), 1);
121101
- async function renderError(stderr, error48) {
121102
- const writer = stderr.getWriter();
121103
- const encoder = new TextEncoder;
121104
- const lines = [];
121105
- if (error48 instanceof PercherTomlError) {
121106
- lines.push(`${import_picocolors17.default.red(`error (${error48.code}):`)} ${error48.message}`);
121107
- if (error48.tomlLine !== undefined) {
121108
- lines.push(import_picocolors17.default.dim(` at percher.toml line ${error48.tomlLine}`));
121109
- }
121110
- for (const issue2 of error48.issues ?? []) {
121111
- lines.push(`${import_picocolors17.default.dim(" · ") + import_picocolors17.default.yellow(issue2.path.join("."))}: ${issue2.message}`);
121112
- }
121113
- } else if (error48 instanceof SunsetError) {
121114
- if (error48.gateCode === "platform_delete_gated") {
121115
- lines.push(`${import_picocolors17.default.yellow("Percher is shutting down — deletes are paused.")}`);
121116
- lines.push(error48.message);
121117
- lines.push(import_picocolors17.default.dim(` To delete and permanently forfeit your export window, confirm with your password in the dashboard: ${error48.dashboardUrl}`));
121118
- lines.push(import_picocolors17.default.dim(" The CLI cannot perform this delete during sunset — it must go through the dashboard."));
121119
- } else {
121120
- lines.push(`${import_picocolors17.default.yellow("Percher is in read-only mode ahead of shutdown.")}`);
121121
- lines.push(error48.message);
121122
- lines.push(import_picocolors17.default.dim(` Your apps keep running. Export them any time from the dashboard: ${error48.dashboardUrl}`));
121123
- }
121124
- lines.push(import_picocolors17.default.dim(` Final data deletion: ${error48.finalDeletionDate}`));
121125
- } else if (error48 instanceof PercherApiError) {
121126
- if (error48.status === 502 || error48.status === 503) {
121127
- lines.push(`${import_picocolors17.default.red("error:")} Percher API is temporarily unavailable (HTTP ${error48.status}). Try again in a few seconds.`);
121128
- } else {
121129
- lines.push(`${import_picocolors17.default.red("error:")} ${error48.message}`);
121130
- lines.push(import_picocolors17.default.dim(` ${error48.code} (HTTP ${error48.status})`));
121131
- }
121132
- if (error48.requestId)
121133
- lines.push(import_picocolors17.default.dim(` request: ${error48.requestId}`));
121134
- } else if (error48 instanceof PercherCoreError) {
121135
- lines.push(`${import_picocolors17.default.red(`error (${error48.code}):`)} ${error48.message}`);
121136
- if (error48.hint)
121137
- lines.push(import_picocolors17.default.dim(` ${error48.hint}`));
121138
- } else if (error48 instanceof Error) {
121139
- lines.push(`${import_picocolors17.default.red("unexpected error:")} ${error48.message}`);
121140
- if (error48.stack)
121141
- lines.push(import_picocolors17.default.dim(error48.stack.split(`
121142
- `).slice(1, 4).join(`
121143
- `)));
121144
- } else {
121145
- lines.push(`${import_picocolors17.default.red("unexpected error:")} ${String(error48)}`);
121146
- }
121147
- try {
121148
- await writer.write(encoder.encode(`${lines.join(`
121149
- `)}
121150
- `));
121151
- } finally {
121152
- writer.releaseLock();
121153
- }
121154
- }
121155
121311
  // package.json
121156
- var package_default = {
121312
+ var package_default2 = {
121157
121313
  name: "percher",
121158
- version: "0.5.22",
121314
+ version: "0.5.24",
121159
121315
  description: "CLI for the Percher hosting platform — publish apps with one command",
121160
121316
  type: "module",
121161
121317
  bin: {
@@ -121182,9 +121338,9 @@ var package_default = {
121182
121338
  typecheck: "tsc --noEmit"
121183
121339
  },
121184
121340
  dependencies: {
121185
- "@percher/client": "^0.2.21",
121186
- "@percher/core": "^0.4.27",
121187
- "@percher/toml": "^0.1.10",
121341
+ "@percher/client": "^0.3.1",
121342
+ "@percher/core": "^0.4.29",
121343
+ "@percher/toml": "^0.1.11",
121188
121344
  cac: "^7.0.0",
121189
121345
  picocolors: "^1.1.1"
121190
121346
  },
@@ -121212,7 +121368,7 @@ var package_default = {
121212
121368
  };
121213
121369
 
121214
121370
  // src/telemetry.ts
121215
- var VERSION = package_default.version;
121371
+ var VERSION = package_default2.version;
121216
121372
  var API_URL = "https://api.percher.run";
121217
121373
  function isDisabled() {
121218
121374
  return process.env.PERCHER_NO_TELEMETRY === "1" || process.env.PERCHER_NO_TELEMETRY === "true" || process.env.DO_NOT_TRACK === "1";
@@ -121254,8 +121410,97 @@ async function showTelemetryNotice() {
121254
121410
  } catch {}
121255
121411
  }
121256
121412
 
121257
- // src/version-check.ts
121413
+ // src/context.ts
121414
+ async function createCliContext() {
121415
+ const client = new PercherClient({ clientVersion: `percher-cli/${VERSION}` });
121416
+ const encoder = new TextEncoder;
121417
+ const stdout2 = new WritableStream({
121418
+ write(chunk) {
121419
+ process.stdout.write(chunk);
121420
+ }
121421
+ });
121422
+ const stderr = new WritableStream({
121423
+ write(chunk) {
121424
+ process.stderr.write(chunk);
121425
+ }
121426
+ });
121427
+ return {
121428
+ client,
121429
+ cwd: process.cwd(),
121430
+ stdout: stdout2,
121431
+ stderr,
121432
+ env: process.env,
121433
+ prompt,
121434
+ openUrl,
121435
+ interactiveLogin: true,
121436
+ stdinIsInteractive: process.stdin.isTTY === true && process.stdout.isTTY === true,
121437
+ status(message) {
121438
+ process.stderr.write(encoder.encode(`${message}
121439
+ `));
121440
+ }
121441
+ };
121442
+ }
121443
+
121444
+ // src/output.ts
121445
+ init_dist();
121258
121446
  var import_picocolors18 = __toESM(require_picocolors(), 1);
121447
+ async function renderError(stderr, error48) {
121448
+ const writer = stderr.getWriter();
121449
+ const encoder = new TextEncoder;
121450
+ const lines = [];
121451
+ if (error48 instanceof PercherTomlError) {
121452
+ lines.push(`${import_picocolors18.default.red(`error (${error48.code}):`)} ${error48.message}`);
121453
+ if (error48.tomlLine !== undefined) {
121454
+ lines.push(import_picocolors18.default.dim(` at percher.toml line ${error48.tomlLine}`));
121455
+ }
121456
+ for (const issue2 of error48.issues ?? []) {
121457
+ lines.push(`${import_picocolors18.default.dim(" · ") + import_picocolors18.default.yellow(issue2.path.join("."))}: ${issue2.message}`);
121458
+ }
121459
+ } else if (error48 instanceof SunsetError) {
121460
+ if (error48.gateCode === "platform_delete_gated") {
121461
+ lines.push(`${import_picocolors18.default.yellow("Percher is shutting down — deletes are paused.")}`);
121462
+ lines.push(error48.message);
121463
+ lines.push(import_picocolors18.default.dim(` To delete and permanently forfeit your export window, confirm with your password in the dashboard: ${error48.dashboardUrl}`));
121464
+ lines.push(import_picocolors18.default.dim(" The CLI cannot perform this delete during sunset — it must go through the dashboard."));
121465
+ } else {
121466
+ lines.push(`${import_picocolors18.default.yellow("Percher is in read-only mode ahead of shutdown.")}`);
121467
+ lines.push(error48.message);
121468
+ lines.push(import_picocolors18.default.dim(` Your apps keep running. Export them any time from the dashboard: ${error48.dashboardUrl}`));
121469
+ }
121470
+ lines.push(import_picocolors18.default.dim(` Final data deletion: ${error48.finalDeletionDate}`));
121471
+ } else if (error48 instanceof PercherApiError) {
121472
+ if (error48.status === 502 || error48.status === 503) {
121473
+ lines.push(`${import_picocolors18.default.red("error:")} Percher API is temporarily unavailable (HTTP ${error48.status}). Try again in a few seconds.`);
121474
+ } else {
121475
+ lines.push(`${import_picocolors18.default.red("error:")} ${error48.message}`);
121476
+ lines.push(import_picocolors18.default.dim(` ${error48.code} (HTTP ${error48.status})`));
121477
+ }
121478
+ if (error48.requestId)
121479
+ lines.push(import_picocolors18.default.dim(` request: ${error48.requestId}`));
121480
+ } else if (error48 instanceof PercherCoreError) {
121481
+ lines.push(`${import_picocolors18.default.red(`error (${error48.code}):`)} ${error48.message}`);
121482
+ if (error48.hint)
121483
+ lines.push(import_picocolors18.default.dim(` ${error48.hint}`));
121484
+ } else if (error48 instanceof Error) {
121485
+ lines.push(`${import_picocolors18.default.red("unexpected error:")} ${error48.message}`);
121486
+ if (error48.stack)
121487
+ lines.push(import_picocolors18.default.dim(error48.stack.split(`
121488
+ `).slice(1, 4).join(`
121489
+ `)));
121490
+ } else {
121491
+ lines.push(`${import_picocolors18.default.red("unexpected error:")} ${String(error48)}`);
121492
+ }
121493
+ try {
121494
+ await writer.write(encoder.encode(`${lines.join(`
121495
+ `)}
121496
+ `));
121497
+ } finally {
121498
+ writer.releaseLock();
121499
+ }
121500
+ }
121501
+
121502
+ // src/version-check.ts
121503
+ var import_picocolors19 = __toESM(require_picocolors(), 1);
121259
121504
  async function checkForUpdates() {
121260
121505
  try {
121261
121506
  const res = await fetch("https://registry.npmjs.org/percher/latest", {
@@ -121266,9 +121511,9 @@ async function checkForUpdates() {
121266
121511
  const data2 = await res.json();
121267
121512
  const latest = data2.version;
121268
121513
  if (latest && latest !== VERSION) {
121269
- console.error(import_picocolors18.default.yellow(`
121514
+ console.error(import_picocolors19.default.yellow(`
121270
121515
  Update available: ${VERSION} → ${latest}`));
121271
- console.error(import_picocolors18.default.dim(`Run: bunx percher@latest
121516
+ console.error(import_picocolors19.default.dim(`Run: bunx percher@latest
121272
121517
  `));
121273
121518
  }
121274
121519
  } catch {}
@@ -121279,6 +121524,7 @@ async function run(argv, ctxOverride) {
121279
121524
  const cli = cac("percher");
121280
121525
  const ctx = ctxOverride ?? await createCliContext();
121281
121526
  registerAppsCommands(cli, ctx);
121527
+ registerAnalyticsCommands(cli, ctx);
121282
121528
  registerDeploysCommands(cli, ctx, argv);
121283
121529
  registerEnvCommands(cli, ctx);
121284
121530
  registerExportCommands(cli, ctx);