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.
- package/dist/bin/percher.js +778 -532
- package/dist/package.json +4 -4
- package/dist/src/cli-utils.d.ts.map +1 -1
- package/dist/src/cli-utils.js.map +1 -1
- package/dist/src/commands/analytics.d.ts +4 -0
- package/dist/src/commands/analytics.d.ts.map +1 -0
- package/dist/src/commands/analytics.js +66 -0
- package/dist/src/commands/analytics.js.map +1 -0
- package/dist/src/commands/apps.js.map +1 -1
- package/dist/src/context.d.ts.map +1 -1
- package/dist/src/context.js +2 -1
- package/dist/src/context.js.map +1 -1
- package/dist/src/env-set-input.d.ts.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/templates/capsule/src/lib/ai-client.ts +193 -0
- package/dist/templates/capsule/src/lib/ai.ts +13 -89
- package/package.json +4 -4
package/dist/bin/percher.js
CHANGED
|
@@ -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 ??
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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", "
|
|
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" &&
|
|
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
|
|
107723
|
-
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 (
|
|
107732
|
-
args.app =
|
|
107733
|
-
if (
|
|
107734
|
-
args.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
|
-
|
|
107749
|
-
|
|
107750
|
-
|
|
107751
|
-
|
|
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
|
|
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
|
|
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:
|
|
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:
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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
|
-
|
|
109830
|
-
|
|
109831
|
-
|
|
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
|
-
|
|
109938
|
-
|
|
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
|
|
110006
|
-
return ctx.client.apps.addDomain(
|
|
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
|
|
110010
|
-
return ctx.client.apps.verifyDomain(
|
|
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
|
|
110014
|
-
return ctx.client.apps.listDomains(
|
|
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
|
|
110018
|
-
await ctx.client.apps.removeDomain(
|
|
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
|
|
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(
|
|
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:
|
|
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(
|
|
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:
|
|
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 ${
|
|
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 ${
|
|
110639
|
+
` bunx percher github setup-webhook --app ${appRef2}`
|
|
110417
110640
|
].join(`
|
|
110418
110641
|
`)
|
|
110419
110642
|
};
|
|
110420
110643
|
}
|
|
110421
110644
|
return {
|
|
110422
|
-
app:
|
|
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 ${
|
|
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
|
|
110455
|
-
const webhook = await ctx.client.apps.setupWebhook(
|
|
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:
|
|
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 ${
|
|
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
|
|
115592
|
-
return ctx.client.apps.setPreviewBranch(
|
|
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
|
|
115596
|
-
return ctx.client.apps.unsetPreviewBranch(
|
|
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
|
|
115604
|
-
const app = await ctx.client.apps.get(
|
|
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
|
|
115613
|
-
const app = await ctx.client.apps.get(
|
|
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
|
|
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
|
|
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
|
|
118059
|
-
if (!
|
|
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(
|
|
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
|
|
118240
|
-
if (!
|
|
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(
|
|
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
|
|
118530
|
-
return ctx.client.sharing.list(
|
|
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
|
|
118534
|
-
return ctx.client.sharing.create(
|
|
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
|
|
118538
|
-
return ctx.client.sharing.setRole(
|
|
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
|
|
118542
|
-
await ctx.client.sharing.revoke(
|
|
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
|
|
118658
|
-
return ctx.client.transfers.initiate(
|
|
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
|
|
118662
|
-
await ctx.client.transfers.cancel(
|
|
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
|
|
118666
|
-
return ctx.client.transfers.listForApp(
|
|
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
|
-
|
|
118827
|
-
label: "
|
|
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/
|
|
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, `${
|
|
119416
|
-
await writeLine2(ctx,
|
|
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, `${
|
|
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, `${
|
|
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, `${
|
|
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, `${
|
|
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, `${
|
|
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,
|
|
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,
|
|
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, `${
|
|
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,
|
|
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, `${
|
|
119704
|
+
await writeLine2(ctx, `${import_picocolors5.default.yellow("!")} ${result.summary}`);
|
|
119460
119705
|
if (result.url) {
|
|
119461
|
-
await writeLine2(ctx,
|
|
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,
|
|
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,
|
|
119716
|
+
await writeLine2(ctx, import_picocolors5.default.dim(` Next: percher doctor --app ${appName}`));
|
|
119472
119717
|
} else {
|
|
119473
|
-
await writeLine2(ctx,
|
|
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, `${
|
|
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, ` ${
|
|
119729
|
+
await writeLine2(ctx, ` ${import_picocolors5.default.dim(`Details: ${result.error.rawErrorMessage}`)}`);
|
|
119485
119730
|
}
|
|
119486
119731
|
if (result.error?.suggestion) {
|
|
119487
|
-
await writeLine2(ctx, ` ${
|
|
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,
|
|
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,
|
|
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,
|
|
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, `${
|
|
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,
|
|
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, `${
|
|
119766
|
+
await writeLine2(ctx, `${import_picocolors5.default.green("✓")} Published to ${result.url}${timing}`);
|
|
119522
119767
|
if (result.traceId) {
|
|
119523
|
-
await writeLine2(ctx,
|
|
119768
|
+
await writeLine2(ctx, import_picocolors5.default.dim(` Trace: ${result.traceId}`));
|
|
119524
119769
|
}
|
|
119525
119770
|
if (result.cacheReused) {
|
|
119526
|
-
await writeLine2(ctx,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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, ` ${
|
|
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,
|
|
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,
|
|
119563
|
-
await writeLine2(ctx, ` ${
|
|
119564
|
-
await writeLine2(ctx, ` ${
|
|
119565
|
-
await writeLine2(ctx, ` ${
|
|
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, `${
|
|
119825
|
+
await writeLine2(ctx, `${import_picocolors5.default.green("✓")} wrote ${result.path}`);
|
|
119581
119826
|
for (const file2 of result.scaffoldedFiles) {
|
|
119582
|
-
await writeLine2(ctx, `${
|
|
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, `${
|
|
119842
|
+
await writeLine2(ctx, `${import_picocolors5.default.green("✓")} wrote ${file2}`);
|
|
119598
119843
|
}
|
|
119599
119844
|
for (const file2 of result.skipped) {
|
|
119600
|
-
await writeLine2(ctx, `${
|
|
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, `${
|
|
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, `${
|
|
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 ${
|
|
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,
|
|
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, `${
|
|
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, `${
|
|
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, ` ${
|
|
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,
|
|
119901
|
+
await writeLine2(ctx, import_picocolors5.default.bold("Build problems:"));
|
|
119657
119902
|
for (const p of result.error.problems) {
|
|
119658
|
-
const loc = p.file ? ` ${
|
|
119659
|
-
await writeLine2(ctx, ` ${
|
|
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, ` ${
|
|
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, `${
|
|
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, `${
|
|
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, `${
|
|
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, `${
|
|
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, `${
|
|
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,
|
|
119957
|
+
await writeLine2(ctx, import_picocolors5.default.dim(`${lastStatus}...`));
|
|
119713
119958
|
}
|
|
119714
119959
|
if (result.status === "live") {
|
|
119715
|
-
await writeLine2(ctx, `${
|
|
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" ?
|
|
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, ` ${
|
|
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, `${
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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, `${
|
|
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, ` ${
|
|
120008
|
+
await writeLine2(ctx, ` ${import_picocolors5.default.yellow("!")} Update percher.toml before the next publish: name = "${result.name}"`);
|
|
119764
120009
|
}
|
|
119765
|
-
await writeLine2(ctx, ` ${
|
|
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,
|
|
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, `${
|
|
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, `${
|
|
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, ` ${
|
|
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, `${
|
|
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(`${
|
|
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, `${
|
|
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, `${
|
|
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, `${
|
|
120074
|
+
await writeLine2(ctx, `${import_picocolors5.default.green("✓")} ${result.summary}`);
|
|
119830
120075
|
} else {
|
|
119831
|
-
await writeLine2(ctx, `${
|
|
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, `${
|
|
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, `${
|
|
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, `${
|
|
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, `${
|
|
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" ?
|
|
119862
|
-
await writeLine2(ctx, `${icon} ${
|
|
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" ?
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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, `${
|
|
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,
|
|
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
|
|
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, `${
|
|
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
|
|
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, `${
|
|
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, `${
|
|
120220
|
+
await writeLine2(ctx, `${import_picocolors7.default.green("✓")} Removed ${result2.provider} sign-in.`);
|
|
119976
120221
|
} else if (result2.action === "set") {
|
|
119977
|
-
await writeLine2(ctx, `${
|
|
120222
|
+
await writeLine2(ctx, `${import_picocolors7.default.green("✓")} ${result2.provider} sign-in enabled.`);
|
|
119978
120223
|
await writeLine2(ctx, "");
|
|
119979
|
-
await writeLine2(ctx,
|
|
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,
|
|
120228
|
+
await writeLine2(ctx, import_picocolors7.default.dim("No OAuth providers configured."));
|
|
119984
120229
|
} else {
|
|
119985
|
-
await writeLine2(ctx,
|
|
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)} ${
|
|
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, `${
|
|
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,
|
|
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)} ${
|
|
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,
|
|
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, `${
|
|
120274
|
+
await writeLine2(ctx, `${import_picocolors7.default.dim("No backups found.")}`);
|
|
120030
120275
|
if (skipped.length > 0) {
|
|
120031
|
-
await writeLine2(ctx,
|
|
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, `${
|
|
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,
|
|
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, `${
|
|
120055
|
-
await writeLine2(ctx, ` components: ${
|
|
120056
|
-
await writeLine2(ctx, ` sha: ${
|
|
120057
|
-
await writeLine2(ctx,
|
|
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, `${
|
|
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
|
|
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,
|
|
120327
|
+
await writeLine2(ctx, import_picocolors8.default.dim("No deploys yet."));
|
|
120083
120328
|
return;
|
|
120084
120329
|
}
|
|
120085
|
-
await writeLine2(ctx, `${
|
|
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) ?? "—" :
|
|
120092
|
-
const colored = d.status === "live" ?
|
|
120093
|
-
await writeLine2(ctx, `${
|
|
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, `${
|
|
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, `${
|
|
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, `${
|
|
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, ` ${
|
|
120367
|
+
await writeLine2(ctx, ` ${import_picocolors8.default.dim(result.error.suggestion)}`);
|
|
120123
120368
|
}
|
|
120124
120369
|
throw new SilentExitError;
|
|
120125
120370
|
}
|
|
120126
|
-
await writeLine2(ctx, `${
|
|
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,
|
|
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,
|
|
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, `${
|
|
120146
|
-
await writeLine2(ctx, `${
|
|
120147
|
-
await writeLine2(ctx, `${
|
|
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,
|
|
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 ? `${
|
|
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,
|
|
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 · ${
|
|
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 ?
|
|
120206
|
-
await writeLine2(ctx, ` ${f.status} ${f.path} ${stat2}${f.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,
|
|
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
|
|
120225
|
-
if (!
|
|
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(
|
|
120232
|
-
await writeLine2(ctx, `${
|
|
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(
|
|
120237
|
-
await writeLine2(ctx, `${
|
|
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 ${
|
|
120489
|
+
await writeLine2(ctx, `Preview surfaces for ${import_picocolors8.default.bold(appName)}:`);
|
|
120245
120490
|
if (previews.length === 0) {
|
|
120246
|
-
await writeLine2(ctx,
|
|
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" ?
|
|
120252
|
-
await writeLine2(ctx, ` ${
|
|
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,
|
|
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, `${
|
|
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: ${
|
|
120266
|
-
await writeLine2(ctx, r.status === "error" ?
|
|
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, `${
|
|
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,
|
|
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: ${
|
|
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
|
|
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, `${
|
|
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, `${
|
|
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, `${
|
|
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, `${
|
|
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, `${
|
|
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, `${
|
|
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
|
|
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, `${
|
|
120373
|
-
await writeLine2(ctx,
|
|
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, `${
|
|
120382
|
-
await writeLine2(ctx,
|
|
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
|
|
120632
|
+
var import_picocolors11 = __toESM(require_picocolors(), 1);
|
|
120388
120633
|
async function printWebhookInstructions(ctx, instructions, branch) {
|
|
120389
|
-
await writeLine2(ctx,
|
|
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,
|
|
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,
|
|
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, `${
|
|
120415
|
-
await writeLine2(ctx, `${
|
|
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,
|
|
120419
|
-
await writeLine2(ctx,
|
|
120420
|
-
await writeLine2(ctx, ` ${
|
|
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,
|
|
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, `${
|
|
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
|
|
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, `${
|
|
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, `${
|
|
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, `${
|
|
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, ` ${
|
|
120462
|
-
await writeLine2(ctx,
|
|
120463
|
-
await writeLine2(ctx,
|
|
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,
|
|
120466
|
-
await writeLine2(ctx,
|
|
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,
|
|
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, `${
|
|
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,
|
|
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, `${
|
|
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, `${
|
|
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
|
|
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, `${
|
|
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, `${
|
|
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, `${
|
|
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, `${
|
|
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,
|
|
120828
|
+
await writeLine2(ctx, import_picocolors13.default.dim(` ${err.suggestion}`));
|
|
120584
120829
|
return;
|
|
120585
120830
|
}
|
|
120586
|
-
await writeLine2(ctx, `${
|
|
120831
|
+
await writeLine2(ctx, `${import_picocolors13.default.green("✓")} ${result.summary}`);
|
|
120587
120832
|
await writeLine2(ctx, "");
|
|
120588
|
-
await writeLine2(ctx,
|
|
120833
|
+
await writeLine2(ctx, import_picocolors13.default.bold("Tables"));
|
|
120589
120834
|
if (result.tables.length === 0) {
|
|
120590
|
-
await writeLine2(ctx,
|
|
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, ` ${
|
|
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,
|
|
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, ` ${
|
|
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, `${
|
|
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,
|
|
120858
|
+
await writeLine2(ctx, import_picocolors13.default.bold(`Warnings (${result.warnings.length})`));
|
|
120614
120859
|
for (const warning of result.warnings) {
|
|
120615
|
-
await writeLine2(ctx, ` ${
|
|
120860
|
+
await writeLine2(ctx, ` ${import_picocolors13.default.yellow("⚠")} ${warning}`);
|
|
120616
120861
|
}
|
|
120617
120862
|
}
|
|
120618
120863
|
await writeLine2(ctx, "");
|
|
120619
|
-
await writeLine2(ctx,
|
|
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, `${
|
|
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,
|
|
120873
|
+
await writeLine2(ctx, import_picocolors13.default.dim(` ${err.suggestion}`));
|
|
120629
120874
|
return;
|
|
120630
120875
|
}
|
|
120631
|
-
await writeLine2(ctx, `${
|
|
120876
|
+
await writeLine2(ctx, `${import_picocolors13.default.green("✓")} ${result.summary}`);
|
|
120632
120877
|
await writeLine2(ctx, "");
|
|
120633
|
-
await writeLine2(ctx,
|
|
120878
|
+
await writeLine2(ctx, import_picocolors13.default.bold("Files written"));
|
|
120634
120879
|
for (const path of result.filesWritten) {
|
|
120635
|
-
await writeLine2(ctx, ` ${
|
|
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,
|
|
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, ` ${
|
|
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,
|
|
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, ` ${
|
|
120898
|
+
await writeLine2(ctx, ` ${import_picocolors13.default.yellow("⚠")} ${warning}`);
|
|
120654
120899
|
}
|
|
120655
120900
|
if (preview.warnings.length > 10) {
|
|
120656
|
-
await writeLine2(ctx,
|
|
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,
|
|
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,
|
|
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, `${
|
|
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,
|
|
120918
|
+
await writeLine2(ctx, import_picocolors13.default.dim(` ${err.suggestion}`));
|
|
120674
120919
|
return;
|
|
120675
120920
|
}
|
|
120676
|
-
await writeLine2(ctx, `${
|
|
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,
|
|
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, ` ${
|
|
120926
|
+
await writeLine2(ctx, ` ${import_picocolors13.default.cyan(path)}`);
|
|
120682
120927
|
}
|
|
120683
120928
|
if (result.filesWritten.length > 20) {
|
|
120684
|
-
await writeLine2(ctx,
|
|
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,
|
|
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, ` ${
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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, `${
|
|
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,
|
|
120962
|
+
await writeLine2(ctx, import_picocolors13.default.dim(` ${err.suggestion}`));
|
|
120718
120963
|
return;
|
|
120719
120964
|
}
|
|
120720
|
-
await writeLine2(ctx,
|
|
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" ?
|
|
120724
|
-
const count = check2.count > 0 ?
|
|
120725
|
-
await writeLine2(ctx, ` ${mark} ${check2.label}${count} ${
|
|
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,
|
|
120972
|
+
await writeLine2(ctx, import_picocolors13.default.dim(` - ${file2}`));
|
|
120728
120973
|
}
|
|
120729
120974
|
if ((check2.files?.length ?? 0) > 10) {
|
|
120730
|
-
await writeLine2(ctx,
|
|
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 ?
|
|
120736
|
-
await writeLine2(ctx, ` ${mark} Build ${
|
|
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,
|
|
120984
|
+
await writeLine2(ctx, import_picocolors13.default.dim(result.summary));
|
|
120740
120985
|
}
|
|
120741
120986
|
|
|
120742
120987
|
// src/commands/sharing.ts
|
|
120743
|
-
var
|
|
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,
|
|
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 ?
|
|
120754
|
-
const roleStr = r.role === "admin" ?
|
|
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, `${
|
|
120773
|
-
await writeLine2(ctx,
|
|
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, `${
|
|
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, `${
|
|
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,
|
|
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" ?
|
|
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,
|
|
121069
|
+
await writeLine2(ctx, import_picocolors14.default.dim(`Invite ${shareId} was already accepted.`));
|
|
120825
121070
|
return;
|
|
120826
121071
|
}
|
|
120827
|
-
await writeLine2(ctx, `${
|
|
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, `${
|
|
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
|
|
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" ?
|
|
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,
|
|
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 ?
|
|
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=${
|
|
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 ?
|
|
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,
|
|
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
|
|
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, `${
|
|
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, `${
|
|
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, `${
|
|
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, `${
|
|
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, `${
|
|
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, ` ${
|
|
120925
|
-
await writeLine2(ctx,
|
|
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,
|
|
120928
|
-
await writeLine2(ctx,
|
|
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,
|
|
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, `${
|
|
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,
|
|
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, `${
|
|
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, `${
|
|
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
|
|
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,
|
|
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" ?
|
|
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, `${
|
|
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, `${
|
|
120996
|
-
await writeLine2(ctx,
|
|
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,
|
|
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,
|
|
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, `${
|
|
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, `${
|
|
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
|
|
121312
|
+
var package_default2 = {
|
|
121157
121313
|
name: "percher",
|
|
121158
|
-
version: "0.5.
|
|
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.
|
|
121186
|
-
"@percher/core": "^0.4.
|
|
121187
|
-
"@percher/toml": "^0.1.
|
|
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 =
|
|
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/
|
|
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(
|
|
121514
|
+
console.error(import_picocolors19.default.yellow(`
|
|
121270
121515
|
Update available: ${VERSION} → ${latest}`));
|
|
121271
|
-
console.error(
|
|
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);
|