@percher/core 0.2.1 → 0.2.3
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/commands/billing.d.ts +43 -0
- package/dist/commands/billing.d.ts.map +1 -0
- package/dist/commands/billing.js +65 -0
- package/dist/commands/billing.js.map +1 -0
- package/dist/commands/data.d.ts +2 -0
- package/dist/commands/data.d.ts.map +1 -1
- package/dist/commands/data.js +1 -0
- package/dist/commands/data.js.map +1 -1
- package/dist/commands/delete.d.ts +15 -0
- package/dist/commands/delete.d.ts.map +1 -0
- package/dist/commands/delete.js +26 -0
- package/dist/commands/delete.js.map +1 -0
- package/dist/commands/deploys.d.ts +45 -0
- package/dist/commands/deploys.d.ts.map +1 -0
- package/dist/commands/deploys.js +73 -0
- package/dist/commands/deploys.js.map +1 -0
- package/dist/commands/diagnose.d.ts +75 -0
- package/dist/commands/diagnose.d.ts.map +1 -0
- package/dist/commands/diagnose.js +102 -0
- package/dist/commands/diagnose.js.map +1 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +58 -13
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/export.d.ts +39 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +107 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/import-project.d.ts +21 -0
- package/dist/commands/import-project.d.ts.map +1 -0
- package/dist/commands/import-project.js +134 -0
- package/dist/commands/import-project.js.map +1 -0
- package/dist/commands/insights.d.ts +44 -0
- package/dist/commands/insights.d.ts.map +1 -0
- package/dist/commands/insights.js +54 -0
- package/dist/commands/insights.js.map +1 -0
- package/dist/commands/mcp.d.ts +29 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +35 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/publish.d.ts +35 -1
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +163 -9
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +8 -4
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/rename.d.ts +21 -0
- package/dist/commands/rename.d.ts.map +1 -0
- package/dist/commands/rename.js +18 -0
- package/dist/commands/rename.js.map +1 -0
- package/dist/commands/reset-superuser.d.ts +16 -0
- package/dist/commands/reset-superuser.d.ts.map +1 -0
- package/dist/commands/reset-superuser.js +17 -0
- package/dist/commands/reset-superuser.js.map +1 -0
- package/dist/detect.d.ts.map +1 -1
- package/dist/detect.js.map +1 -1
- package/dist/error-classifier.d.ts +16 -0
- package/dist/error-classifier.d.ts.map +1 -1
- package/dist/error-classifier.js +135 -3
- package/dist/error-classifier.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +14 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -3
- package/dist/index.js.map +1 -1
- package/dist/integrations.d.ts +24 -0
- package/dist/integrations.d.ts.map +1 -0
- package/dist/integrations.js +106 -0
- package/dist/integrations.js.map +1 -0
- package/dist/plans.d.ts +36 -0
- package/dist/plans.d.ts.map +1 -1
- package/dist/plans.js +76 -1
- package/dist/plans.js.map +1 -1
- package/dist/qr.d.ts +5 -0
- package/dist/qr.d.ts.map +1 -0
- package/dist/qr.js +10 -0
- package/dist/qr.js.map +1 -0
- package/dist/templates.d.ts +24 -0
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +331 -0
- package/dist/templates.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { Context } from "../context";
|
|
3
|
+
export declare const billingUpgradeInputSchema: z.ZodObject<{
|
|
4
|
+
plan: z.ZodEnum<["starter", "maker", "pro"]>;
|
|
5
|
+
}, "strip", z.ZodTypeAny, {
|
|
6
|
+
plan: "starter" | "maker" | "pro";
|
|
7
|
+
}, {
|
|
8
|
+
plan: "starter" | "maker" | "pro";
|
|
9
|
+
}>;
|
|
10
|
+
export type BillingUpgradeInput = z.infer<typeof billingUpgradeInputSchema>;
|
|
11
|
+
export declare const billingPortalInputSchema: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
|
|
12
|
+
export type BillingPortalInput = z.infer<typeof billingPortalInputSchema>;
|
|
13
|
+
export interface BillingUpgradeResult {
|
|
14
|
+
url: string;
|
|
15
|
+
summary: string;
|
|
16
|
+
}
|
|
17
|
+
export interface BillingPortalResult {
|
|
18
|
+
url: string | null;
|
|
19
|
+
summary: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* AI-driven billing (MCP).
|
|
23
|
+
*
|
|
24
|
+
* Returns a Polar checkout URL for the requested plan. The AI assistant
|
|
25
|
+
* is expected to surface this URL back to the user so they can confirm
|
|
26
|
+
* payment in their browser — Percher never changes subscriptions server-
|
|
27
|
+
* side without an explicit user click.
|
|
28
|
+
*
|
|
29
|
+
* Designed as the "action" companion to `percher_app_insights`: when the
|
|
30
|
+
* insights tool suggests a plan change, the assistant can immediately
|
|
31
|
+
* hand the user a one-click URL.
|
|
32
|
+
*/
|
|
33
|
+
export declare function billingUpgrade(ctx: Context, input: BillingUpgradeInput): Promise<BillingUpgradeResult>;
|
|
34
|
+
/**
|
|
35
|
+
* AI-driven subscription management (MCP).
|
|
36
|
+
*
|
|
37
|
+
* Returns the user's Polar customer portal URL. Users on the free plan
|
|
38
|
+
* who have never subscribed get `url: null` — there's nothing for Polar
|
|
39
|
+
* to show them, and the AI should tell them to use `percher_billing_upgrade`
|
|
40
|
+
* instead.
|
|
41
|
+
*/
|
|
42
|
+
export declare function billingPortal(ctx: Context, _input?: BillingPortalInput): Promise<BillingPortalResult>;
|
|
43
|
+
//# sourceMappingURL=billing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billing.d.ts","sourceRoot":"","sources":["../../src/commands/billing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,eAAO,MAAM,yBAAyB;;;;;;EAEpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,eAAO,MAAM,wBAAwB,gDAAe,CAAC;AACrD,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,oBAAoB,CAAC,CAc/B;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,OAAO,EACZ,MAAM,GAAE,kBAAuB,GAC9B,OAAO,CAAC,mBAAmB,CAAC,CAqB9B"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { PercherCoreError } from "../errors";
|
|
3
|
+
export const billingUpgradeInputSchema = z.object({
|
|
4
|
+
plan: z.enum(["starter", "maker", "pro"]),
|
|
5
|
+
});
|
|
6
|
+
export const billingPortalInputSchema = z.object({});
|
|
7
|
+
/**
|
|
8
|
+
* AI-driven billing (MCP).
|
|
9
|
+
*
|
|
10
|
+
* Returns a Polar checkout URL for the requested plan. The AI assistant
|
|
11
|
+
* is expected to surface this URL back to the user so they can confirm
|
|
12
|
+
* payment in their browser — Percher never changes subscriptions server-
|
|
13
|
+
* side without an explicit user click.
|
|
14
|
+
*
|
|
15
|
+
* Designed as the "action" companion to `percher_app_insights`: when the
|
|
16
|
+
* insights tool suggests a plan change, the assistant can immediately
|
|
17
|
+
* hand the user a one-click URL.
|
|
18
|
+
*/
|
|
19
|
+
export async function billingUpgrade(ctx, input) {
|
|
20
|
+
try {
|
|
21
|
+
const res = await ctx.client.billing.upgrade(input.plan);
|
|
22
|
+
return {
|
|
23
|
+
url: res.url,
|
|
24
|
+
summary: `Open this URL to upgrade to ${input.plan}: ${res.url}`,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
throw new PercherCoreError(`Could not create a checkout session: ${err.message}`, {
|
|
29
|
+
code: "billing.checkout-failed",
|
|
30
|
+
hint: "Billing may be disabled on this Percher instance, or the Polar service is temporarily unavailable.",
|
|
31
|
+
cause: err,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* AI-driven subscription management (MCP).
|
|
37
|
+
*
|
|
38
|
+
* Returns the user's Polar customer portal URL. Users on the free plan
|
|
39
|
+
* who have never subscribed get `url: null` — there's nothing for Polar
|
|
40
|
+
* to show them, and the AI should tell them to use `percher_billing_upgrade`
|
|
41
|
+
* instead.
|
|
42
|
+
*/
|
|
43
|
+
export async function billingPortal(ctx, _input = {}) {
|
|
44
|
+
try {
|
|
45
|
+
const res = await ctx.client.billing.portal();
|
|
46
|
+
if (!res.url) {
|
|
47
|
+
return {
|
|
48
|
+
url: null,
|
|
49
|
+
summary: "No billing portal yet — you're on the free plan. Use percher_billing_upgrade to subscribe.",
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
url: res.url,
|
|
54
|
+
summary: `Open this URL to manage your subscription: ${res.url}`,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
throw new PercherCoreError(`Could not load the billing portal: ${err.message}`, {
|
|
59
|
+
code: "billing.portal-failed",
|
|
60
|
+
hint: "Billing may be disabled on this Percher instance, or the Polar service is temporarily unavailable.",
|
|
61
|
+
cause: err,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=billing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billing.js","sourceRoot":"","sources":["../../src/commands/billing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChD,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;CAC1C,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAarD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,GAAY,EACZ,KAA0B;IAE1B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzD,OAAO;YACL,GAAG,EAAE,GAAG,CAAC,GAAG;YACZ,OAAO,EAAE,+BAA+B,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,GAAG,EAAE;SACjE,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,gBAAgB,CAAC,wCAAyC,GAAa,CAAC,OAAO,EAAE,EAAE;YAC3F,IAAI,EAAE,yBAAyB;YAC/B,IAAI,EAAE,oGAAoG;YAC1G,KAAK,EAAE,GAAG;SACX,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,GAAY,EACZ,SAA6B,EAAE;IAE/B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QAC9C,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,GAAG,EAAE,IAAI;gBACT,OAAO,EACL,4FAA4F;aAC/F,CAAC;QACJ,CAAC;QACD,OAAO;YACL,GAAG,EAAE,GAAG,CAAC,GAAG;YACZ,OAAO,EAAE,8CAA8C,GAAG,CAAC,GAAG,EAAE;SACjE,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,gBAAgB,CAAC,sCAAuC,GAAa,CAAC,OAAO,EAAE,EAAE;YACzF,IAAI,EAAE,uBAAuB;YAC7B,IAAI,EAAE,oGAAoG;YAC1G,KAAK,EAAE,GAAG;SACX,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
|
package/dist/commands/data.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export type DataInput = z.infer<typeof dataInputSchema>;
|
|
|
14
14
|
export interface DataResult {
|
|
15
15
|
mode: string;
|
|
16
16
|
status: string;
|
|
17
|
+
/** PocketBase image tag (e.g. "0.25.0"). null for non-pocketbase modes. */
|
|
18
|
+
version: string | null;
|
|
17
19
|
collections: Array<{
|
|
18
20
|
name: string;
|
|
19
21
|
type: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../src/commands/data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,eAAO,MAAM,eAAe;;;;;;;;;EAG1B,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IACrD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,wBAAsB,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,GAAE,SAAc,GAAG,OAAO,CAAC,UAAU,CAAC,
|
|
1
|
+
{"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../src/commands/data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,eAAO,MAAM,eAAe;;;;;;;;;EAG1B,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IACrD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,wBAAsB,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,GAAE,SAAc,GAAG,OAAO,CAAC,UAAU,CAAC,CAsBnF"}
|
package/dist/commands/data.js
CHANGED
|
@@ -20,6 +20,7 @@ export async function data(ctx, input = {}) {
|
|
|
20
20
|
return {
|
|
21
21
|
mode: status.mode,
|
|
22
22
|
status: status.status,
|
|
23
|
+
version: status.version ?? null,
|
|
23
24
|
collections: collections.map((c) => ({ name: c.name, type: c.type })),
|
|
24
25
|
stats,
|
|
25
26
|
adminUrl: adminLink.adminUrl ?? null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data.js","sourceRoot":"","sources":["../../src/commands/data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1B,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CAC7B,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"data.js","sourceRoot":"","sources":["../../src/commands/data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1B,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CAC7B,CAAC,CAAC;AAaH,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,GAAY,EAAE,QAAmB,EAAE;IAC5D,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,IAAI,sBAAsB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC1D,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,gBAAgB,CAAC,4CAA4C,EAAE;YACvE,IAAI,EAAE,aAAa;YACnB,IAAI,EAAE,6DAA6D;SACpE,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACrD,MAAM,WAAW,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;IAC/D,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACnD,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAE3D,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,IAAI;QAC/B,WAAW,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACrE,KAAK;QACL,QAAQ,EAAE,SAAS,CAAC,QAAQ,IAAI,IAAI;KACrC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { Context } from "../context";
|
|
3
|
+
export declare const deleteInputSchema: z.ZodObject<{
|
|
4
|
+
app: z.ZodOptional<z.ZodString>;
|
|
5
|
+
confirm: z.ZodBoolean;
|
|
6
|
+
}, "strip", z.ZodTypeAny, {
|
|
7
|
+
confirm: boolean;
|
|
8
|
+
app?: string | undefined;
|
|
9
|
+
}, {
|
|
10
|
+
confirm: boolean;
|
|
11
|
+
app?: string | undefined;
|
|
12
|
+
}>;
|
|
13
|
+
export type DeleteInput = z.infer<typeof deleteInputSchema>;
|
|
14
|
+
export declare function deleteApp(ctx: Context, input: DeleteInput): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=delete.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../src/commands/delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,eAAO,MAAM,iBAAiB;;;;;;;;;EAO5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,wBAAsB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB/E"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { readPercherTomlAppName } from "../app-name";
|
|
3
|
+
import { PercherCoreError } from "../errors";
|
|
4
|
+
export const deleteInputSchema = z.object({
|
|
5
|
+
app: z.string().optional().describe("App name to delete (defaults to percher.toml app name)"),
|
|
6
|
+
confirm: z
|
|
7
|
+
.boolean()
|
|
8
|
+
.describe("Must be true to confirm deletion. This is a destructive operation that permanently removes the app, its container, database, domains, env vars, and deploy history."),
|
|
9
|
+
});
|
|
10
|
+
export async function deleteApp(ctx, input) {
|
|
11
|
+
const name = input.app ?? readPercherTomlAppName(ctx.cwd);
|
|
12
|
+
if (!name) {
|
|
13
|
+
throw new PercherCoreError("No app specified and no percher.toml found", {
|
|
14
|
+
code: "env.no-app",
|
|
15
|
+
hint: "Pass an app name or run from a directory with percher.toml.",
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
if (!input.confirm) {
|
|
19
|
+
throw new PercherCoreError(`Deletion requires explicit confirmation. Set confirm: true to delete "${name}" permanently.`, {
|
|
20
|
+
code: "delete.confirmation-required",
|
|
21
|
+
hint: `This will permanently delete the app "${name}" and all its data. Pass confirm: true to proceed.`,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
await ctx.client.apps.delete(name);
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=delete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete.js","sourceRoot":"","sources":["../../src/commands/delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wDAAwD,CAAC;IAC7F,OAAO,EAAE,CAAC;SACP,OAAO,EAAE;SACT,QAAQ,CACP,qKAAqK,CACtK;CACJ,CAAC,CAAC;AAGH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAY,EAAE,KAAkB;IAC9D,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,IAAI,sBAAsB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC1D,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,gBAAgB,CAAC,4CAA4C,EAAE;YACvE,IAAI,EAAE,YAAY;YAClB,IAAI,EAAE,6DAA6D;SACpE,CAAC,CAAC;IACL,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACnB,MAAM,IAAI,gBAAgB,CACxB,yEAAyE,IAAI,gBAAgB,EAC7F;YACE,IAAI,EAAE,8BAA8B;YACpC,IAAI,EAAE,yCAAyC,IAAI,oDAAoD;SACxG,CACF,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AACrC,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Deployment } from "@percher/client";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import type { Context } from "../context";
|
|
4
|
+
export declare const deploysListInputSchema: z.ZodObject<{
|
|
5
|
+
app: z.ZodOptional<z.ZodString>;
|
|
6
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
7
|
+
}, "strip", z.ZodTypeAny, {
|
|
8
|
+
app?: string | undefined;
|
|
9
|
+
limit?: number | undefined;
|
|
10
|
+
}, {
|
|
11
|
+
app?: string | undefined;
|
|
12
|
+
limit?: number | undefined;
|
|
13
|
+
}>;
|
|
14
|
+
export type DeploysListInput = z.infer<typeof deploysListInputSchema>;
|
|
15
|
+
export declare const deploysInspectInputSchema: z.ZodEffects<z.ZodObject<{
|
|
16
|
+
app: z.ZodOptional<z.ZodString>;
|
|
17
|
+
/** Specific deploy ID. If omitted, requires `latestFailed: true`. */
|
|
18
|
+
id: z.ZodOptional<z.ZodString>;
|
|
19
|
+
/** Resolve to the most recent failed deploy on this app. */
|
|
20
|
+
latestFailed: z.ZodOptional<z.ZodBoolean>;
|
|
21
|
+
}, "strip", z.ZodTypeAny, {
|
|
22
|
+
app?: string | undefined;
|
|
23
|
+
id?: string | undefined;
|
|
24
|
+
latestFailed?: boolean | undefined;
|
|
25
|
+
}, {
|
|
26
|
+
app?: string | undefined;
|
|
27
|
+
id?: string | undefined;
|
|
28
|
+
latestFailed?: boolean | undefined;
|
|
29
|
+
}>, {
|
|
30
|
+
app?: string | undefined;
|
|
31
|
+
id?: string | undefined;
|
|
32
|
+
latestFailed?: boolean | undefined;
|
|
33
|
+
}, {
|
|
34
|
+
app?: string | undefined;
|
|
35
|
+
id?: string | undefined;
|
|
36
|
+
latestFailed?: boolean | undefined;
|
|
37
|
+
}>;
|
|
38
|
+
export type DeploysInspectInput = z.infer<typeof deploysInspectInputSchema>;
|
|
39
|
+
export interface DeploysInspectResult {
|
|
40
|
+
deploy: Deployment;
|
|
41
|
+
buildLog: string | null;
|
|
42
|
+
}
|
|
43
|
+
export declare function deploysList(ctx: Context, input?: DeploysListInput): Promise<Deployment[]>;
|
|
44
|
+
export declare function deploysInspect(ctx: Context, input: DeploysInspectInput): Promise<DeploysInspectResult>;
|
|
45
|
+
//# sourceMappingURL=deploys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploys.d.ts","sourceRoot":"","sources":["../../src/commands/deploys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,eAAO,MAAM,sBAAsB;;;;;;;;;EAGjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,eAAO,MAAM,yBAAyB;;IAGlC,qEAAqE;;IAErE,4DAA4D;;;;;;;;;;;;;;;;;;EAU5D,CAAC;AACL,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CAMzB;AAaD,wBAAsB,WAAW,CAC/B,GAAG,EAAE,OAAO,EACZ,KAAK,GAAE,gBAAqB,GAC3B,OAAO,CAAC,UAAU,EAAE,CAAC,CAGvB;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,oBAAoB,CAAC,CAsC/B"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { readPercherTomlAppName } from "../app-name";
|
|
3
|
+
import { PercherCoreError } from "../errors";
|
|
4
|
+
export const deploysListInputSchema = z.object({
|
|
5
|
+
app: z.string().optional(),
|
|
6
|
+
limit: z.number().int().positive().max(100).optional(),
|
|
7
|
+
});
|
|
8
|
+
export const deploysInspectInputSchema = z
|
|
9
|
+
.object({
|
|
10
|
+
app: z.string().optional(),
|
|
11
|
+
/** Specific deploy ID. If omitted, requires `latestFailed: true`. */
|
|
12
|
+
id: z.string().optional(),
|
|
13
|
+
/** Resolve to the most recent failed deploy on this app. */
|
|
14
|
+
latestFailed: z.boolean().optional(),
|
|
15
|
+
})
|
|
16
|
+
.superRefine((val, ctx) => {
|
|
17
|
+
if (!val.id && !val.latestFailed) {
|
|
18
|
+
ctx.addIssue({
|
|
19
|
+
code: z.ZodIssueCode.custom,
|
|
20
|
+
message: "either `id` or `latestFailed: true` must be provided",
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
function resolveAppName(ctx, app) {
|
|
25
|
+
const name = app ?? readPercherTomlAppName(ctx.cwd);
|
|
26
|
+
if (!name) {
|
|
27
|
+
throw new PercherCoreError("No app specified and no percher.toml found", {
|
|
28
|
+
code: "deploys.no-app",
|
|
29
|
+
hint: "Pass --app <name> or run from a directory with percher.toml.",
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return name;
|
|
33
|
+
}
|
|
34
|
+
export async function deploysList(ctx, input = {}) {
|
|
35
|
+
const name = resolveAppName(ctx, input.app);
|
|
36
|
+
return ctx.client.apps.listDeploys(name, { limit: input.limit ?? 10 });
|
|
37
|
+
}
|
|
38
|
+
export async function deploysInspect(ctx, input) {
|
|
39
|
+
const name = resolveAppName(ctx, input.app);
|
|
40
|
+
let deployId = input.id;
|
|
41
|
+
if (!deployId && input.latestFailed) {
|
|
42
|
+
// Pull a recent slice and pick the newest failed entry. listDeploys
|
|
43
|
+
// returns newest-first per the API.
|
|
44
|
+
const recent = await ctx.client.apps.listDeploys(name, { limit: 25 });
|
|
45
|
+
const failed = recent.find((d) => d.status === "failed");
|
|
46
|
+
if (!failed) {
|
|
47
|
+
throw new PercherCoreError(`No failed deploys found for "${name}"`, {
|
|
48
|
+
code: "deploys.no-failed",
|
|
49
|
+
hint: "Try `percher deploys` to list recent deploys, or omit --latest-failed.",
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
deployId = failed.id;
|
|
53
|
+
}
|
|
54
|
+
if (!deployId) {
|
|
55
|
+
throw new PercherCoreError("Deploy ID is required", {
|
|
56
|
+
code: "deploys.no-id",
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const deploy = await ctx.client.apps.getDeployment(name, deployId);
|
|
60
|
+
// getBuildLog 404s for deploys that haven't reached the build stage —
|
|
61
|
+
// treat that as "no log yet" rather than an error so the caller can render
|
|
62
|
+
// the deploy state gracefully.
|
|
63
|
+
let buildLog = null;
|
|
64
|
+
try {
|
|
65
|
+
const log = await ctx.client.apps.getBuildLog(name, deployId);
|
|
66
|
+
buildLog = log.length > 0 ? log : null;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
buildLog = null;
|
|
70
|
+
}
|
|
71
|
+
return { deploy, buildLog };
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=deploys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploys.js","sourceRoot":"","sources":["../../src/commands/deploys.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { Context } from "../context";
|
|
3
|
+
export declare const diagnoseInputSchema: z.ZodObject<{
|
|
4
|
+
app: z.ZodOptional<z.ZodString>;
|
|
5
|
+
/**
|
|
6
|
+
* How much of the crash log tail to include in the response. Defaults to
|
|
7
|
+
* 30 lines — enough for most AI explanations, small enough to keep the
|
|
8
|
+
* MCP tool response under a sensible token budget. 0 returns no log lines.
|
|
9
|
+
*/
|
|
10
|
+
logLines: z.ZodOptional<z.ZodNumber>;
|
|
11
|
+
/**
|
|
12
|
+
* Staleness threshold for "recent-crash" state. If the most recent crash
|
|
13
|
+
* report is older than this and the app is not currently in status=crashed,
|
|
14
|
+
* we treat the app as healthy — the stored analysis is almost certainly
|
|
15
|
+
* unrelated to whatever the user is currently troubleshooting. Defaults to
|
|
16
|
+
* 24 hours.
|
|
17
|
+
*/
|
|
18
|
+
recencyHours: z.ZodOptional<z.ZodNumber>;
|
|
19
|
+
}, "strip", z.ZodTypeAny, {
|
|
20
|
+
app?: string | undefined;
|
|
21
|
+
logLines?: number | undefined;
|
|
22
|
+
recencyHours?: number | undefined;
|
|
23
|
+
}, {
|
|
24
|
+
app?: string | undefined;
|
|
25
|
+
logLines?: number | undefined;
|
|
26
|
+
recencyHours?: number | undefined;
|
|
27
|
+
}>;
|
|
28
|
+
export type DiagnoseInput = z.infer<typeof diagnoseInputSchema>;
|
|
29
|
+
export interface DiagnoseResult {
|
|
30
|
+
/**
|
|
31
|
+
* Overall state: "crashed" if the app is currently in status=crashed and
|
|
32
|
+
* a matching crash report exists; "recent-crash" if the app recovered or
|
|
33
|
+
* is multi-instance but we still have a recent crash report; "healthy" if
|
|
34
|
+
* no recent crash report was found.
|
|
35
|
+
*/
|
|
36
|
+
state: "crashed" | "recent-crash" | "healthy";
|
|
37
|
+
app: {
|
|
38
|
+
name: string;
|
|
39
|
+
status: string;
|
|
40
|
+
url: string;
|
|
41
|
+
};
|
|
42
|
+
crash: {
|
|
43
|
+
id: string;
|
|
44
|
+
exitCode: number;
|
|
45
|
+
oomKilled: boolean;
|
|
46
|
+
severity: "critical" | "warning" | "info" | null;
|
|
47
|
+
createdAt: string;
|
|
48
|
+
/** Human-readable analysis from the crash analyzer, if one ran. */
|
|
49
|
+
explanation: string | null;
|
|
50
|
+
/** Suggested fix from the crash analyzer, if one ran. */
|
|
51
|
+
suggestion: string | null;
|
|
52
|
+
analysisStatus: "completed" | "skipped" | "failed" | "pending";
|
|
53
|
+
logTail: string[];
|
|
54
|
+
} | null;
|
|
55
|
+
/**
|
|
56
|
+
* Short human-readable summary the AI assistant can echo back to the
|
|
57
|
+
* user without having to synthesize explanation + suggestion itself.
|
|
58
|
+
*/
|
|
59
|
+
summary: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* AI-native crash diagnosis tool (MCP).
|
|
63
|
+
*
|
|
64
|
+
* Reads the most recent crash report for an app and returns the stored AI
|
|
65
|
+
* analysis (explanation + suggestion) alongside exit code, OOM flag, and a
|
|
66
|
+
* tail of the crash log. Designed to be called from Claude Code / Cursor /
|
|
67
|
+
* other MCP clients when a user says "my app isn't working" — the assistant
|
|
68
|
+
* can surface the diagnosis + proposed fix without the user leaving chat.
|
|
69
|
+
*
|
|
70
|
+
* The crash analyzer already runs inside the API and stores results on the
|
|
71
|
+
* `crash_reports` row; this command is a thin formatter on top of the
|
|
72
|
+
* existing `/apps/:appRef/crash-report` endpoint.
|
|
73
|
+
*/
|
|
74
|
+
export declare function diagnose(ctx: Context, input?: DiagnoseInput): Promise<DiagnoseResult>;
|
|
75
|
+
//# sourceMappingURL=diagnose.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnose.d.ts","sourceRoot":"","sources":["../../src/commands/diagnose.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,eAAO,MAAM,mBAAmB;;IAE9B;;;;OAIG;;IAEH;;;;;;OAMG;;;;;;;;;;EAMH,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAIhE,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,KAAK,EAAE,SAAS,GAAG,cAAc,GAAG,SAAS,CAAC;IAC9C,GAAG,EAAE;QACH,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IACF,KAAK,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,OAAO,CAAC;QACnB,QAAQ,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAAC;QACjD,SAAS,EAAE,MAAM,CAAC;QAClB,mEAAmE;QACnE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,yDAAyD;QACzD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,cAAc,EAAE,WAAW,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;QAC/D,OAAO,EAAE,MAAM,EAAE,CAAC;KACnB,GAAG,IAAI,CAAC;IACT;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,GAAE,aAAkB,GAAG,OAAO,CAAC,cAAc,CAAC,CAsE/F"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { readPercherTomlAppName } from "../app-name";
|
|
3
|
+
import { PercherCoreError } from "../errors";
|
|
4
|
+
export const diagnoseInputSchema = z.object({
|
|
5
|
+
app: z.string().optional(),
|
|
6
|
+
/**
|
|
7
|
+
* How much of the crash log tail to include in the response. Defaults to
|
|
8
|
+
* 30 lines — enough for most AI explanations, small enough to keep the
|
|
9
|
+
* MCP tool response under a sensible token budget. 0 returns no log lines.
|
|
10
|
+
*/
|
|
11
|
+
logLines: z.number().int().min(0).max(200).optional(),
|
|
12
|
+
/**
|
|
13
|
+
* Staleness threshold for "recent-crash" state. If the most recent crash
|
|
14
|
+
* report is older than this and the app is not currently in status=crashed,
|
|
15
|
+
* we treat the app as healthy — the stored analysis is almost certainly
|
|
16
|
+
* unrelated to whatever the user is currently troubleshooting. Defaults to
|
|
17
|
+
* 24 hours.
|
|
18
|
+
*/
|
|
19
|
+
recencyHours: z
|
|
20
|
+
.number()
|
|
21
|
+
.positive()
|
|
22
|
+
.max(24 * 30)
|
|
23
|
+
.optional(),
|
|
24
|
+
});
|
|
25
|
+
const DEFAULT_RECENCY_HOURS = 24;
|
|
26
|
+
/**
|
|
27
|
+
* AI-native crash diagnosis tool (MCP).
|
|
28
|
+
*
|
|
29
|
+
* Reads the most recent crash report for an app and returns the stored AI
|
|
30
|
+
* analysis (explanation + suggestion) alongside exit code, OOM flag, and a
|
|
31
|
+
* tail of the crash log. Designed to be called from Claude Code / Cursor /
|
|
32
|
+
* other MCP clients when a user says "my app isn't working" — the assistant
|
|
33
|
+
* can surface the diagnosis + proposed fix without the user leaving chat.
|
|
34
|
+
*
|
|
35
|
+
* The crash analyzer already runs inside the API and stores results on the
|
|
36
|
+
* `crash_reports` row; this command is a thin formatter on top of the
|
|
37
|
+
* existing `/apps/:appRef/crash-report` endpoint.
|
|
38
|
+
*/
|
|
39
|
+
export async function diagnose(ctx, input = {}) {
|
|
40
|
+
const name = input.app ?? readPercherTomlAppName(ctx.cwd);
|
|
41
|
+
if (!name) {
|
|
42
|
+
throw new PercherCoreError("No app specified and no percher.toml found", {
|
|
43
|
+
code: "diagnose.no-app",
|
|
44
|
+
hint: "Pass an app name or run from a directory with percher.toml.",
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
const app = await ctx.client.apps.get(name);
|
|
48
|
+
const report = await ctx.client.apps.getCrashReport(name);
|
|
49
|
+
const logLimit = input.logLines ?? 30;
|
|
50
|
+
const recencyMs = (input.recencyHours ?? DEFAULT_RECENCY_HOURS) * 60 * 60 * 1000;
|
|
51
|
+
// Stale report on a non-crashed app is treated as no report: a weeks-old
|
|
52
|
+
// crash explanation is almost certainly not why the user is asking about
|
|
53
|
+
// "my app isn't working" today. If the app IS currently crashed we always
|
|
54
|
+
// surface the latest report regardless of age — it's the active outage.
|
|
55
|
+
const reportIsStale = !!report &&
|
|
56
|
+
app.status !== "crashed" &&
|
|
57
|
+
Date.now() - new Date(report.createdAt).getTime() > recencyMs;
|
|
58
|
+
if (!report || reportIsStale) {
|
|
59
|
+
const staleNote = reportIsStale
|
|
60
|
+
? ` Latest crash report is older than ${input.recencyHours ?? DEFAULT_RECENCY_HOURS}h and was ignored.`
|
|
61
|
+
: "";
|
|
62
|
+
return {
|
|
63
|
+
state: "healthy",
|
|
64
|
+
app: { name: app.name, status: app.status, url: app.url },
|
|
65
|
+
crash: null,
|
|
66
|
+
summary: app.status === "live"
|
|
67
|
+
? `${app.name} is live. No recent crash reports.${staleNote}`
|
|
68
|
+
: `${app.name} is in status "${app.status}" but no recent crash reports were found.${staleNote}`,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const state = app.status === "crashed" ? "crashed" : "recent-crash";
|
|
72
|
+
// Use explicit start index so logLines=0 returns []. `slice(-0)` is
|
|
73
|
+
// equivalent to `slice(0)` in JS (returns everything), which would blow
|
|
74
|
+
// past the caller's token budget.
|
|
75
|
+
const logs = report.logs ?? [];
|
|
76
|
+
const logTail = logLimit === 0 ? [] : logs.slice(Math.max(logs.length - logLimit, 0));
|
|
77
|
+
const parts = [];
|
|
78
|
+
parts.push(state === "crashed"
|
|
79
|
+
? `${app.name} is currently crashed (exit ${report.exitCode}${report.oomKilled ? ", OOM killed" : ""}).`
|
|
80
|
+
: `${app.name} has a recent crash (exit ${report.exitCode}${report.oomKilled ? ", OOM killed" : ""}).`);
|
|
81
|
+
if (report.explanation)
|
|
82
|
+
parts.push(report.explanation);
|
|
83
|
+
if (report.suggestion)
|
|
84
|
+
parts.push(`Suggestion: ${report.suggestion}`);
|
|
85
|
+
return {
|
|
86
|
+
state,
|
|
87
|
+
app: { name: app.name, status: app.status, url: app.url },
|
|
88
|
+
crash: {
|
|
89
|
+
id: report.id,
|
|
90
|
+
exitCode: report.exitCode,
|
|
91
|
+
oomKilled: report.oomKilled,
|
|
92
|
+
severity: report.severity,
|
|
93
|
+
createdAt: report.createdAt,
|
|
94
|
+
explanation: report.explanation,
|
|
95
|
+
suggestion: report.suggestion,
|
|
96
|
+
analysisStatus: report.analysisStatus,
|
|
97
|
+
logTail,
|
|
98
|
+
},
|
|
99
|
+
summary: parts.join(" "),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=diagnose.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnose.js","sourceRoot":"","sources":["../../src/commands/diagnose.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,eAAO,MAAM,iBAAiB;;;;;;;;;EAG5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,KAAK,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAErD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,GAAE,WAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CAoMzF"}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { PercherTomlError, parseFile } from "@percher/toml";
|
|
1
4
|
import { z } from "zod";
|
|
2
5
|
import { readPercherTomlAppName } from "../app-name";
|
|
3
6
|
export const doctorInputSchema = z.object({
|
|
@@ -41,14 +44,62 @@ export async function doctor(ctx, input = {}) {
|
|
|
41
44
|
});
|
|
42
45
|
return summarize(checks);
|
|
43
46
|
}
|
|
44
|
-
// 4.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
// 4. percher.toml — validated *before* app resolution. readPercherTomlAppName
|
|
48
|
+
// returns null on parse errors AND when the file is missing, so if we waited
|
|
49
|
+
// until after app resolution to validate, an invalid TOML would skip out as
|
|
50
|
+
// "no app" and never get a real failure check. Run validation independently
|
|
51
|
+
// so the user sees the actual issue list either way.
|
|
52
|
+
const tomlPath = join(ctx.cwd, "percher.toml");
|
|
53
|
+
const tomlExists = existsSync(tomlPath);
|
|
54
|
+
let tomlValid = false;
|
|
55
|
+
if (!tomlExists) {
|
|
47
56
|
checks.push({
|
|
48
|
-
name: "
|
|
49
|
-
status: "
|
|
50
|
-
message: "
|
|
57
|
+
name: "percher.toml",
|
|
58
|
+
status: "warn",
|
|
59
|
+
message: "Not found in current directory",
|
|
51
60
|
});
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
try {
|
|
64
|
+
await parseFile(tomlPath);
|
|
65
|
+
tomlValid = true;
|
|
66
|
+
checks.push({ name: "percher.toml", status: "pass", message: "Valid" });
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
if (err instanceof PercherTomlError && err.issues && err.issues.length > 0) {
|
|
70
|
+
const summary = err.issues
|
|
71
|
+
.slice(0, 3)
|
|
72
|
+
.map((i) => {
|
|
73
|
+
const where = i.path.length > 0 ? `[${i.path.join(".")}]` : "(root)";
|
|
74
|
+
return `${where} ${i.message}`;
|
|
75
|
+
})
|
|
76
|
+
.join("; ");
|
|
77
|
+
const more = err.issues.length > 3 ? ` (+${err.issues.length - 3} more)` : "";
|
|
78
|
+
checks.push({
|
|
79
|
+
name: "percher.toml",
|
|
80
|
+
status: "fail",
|
|
81
|
+
message: `${summary}${more}`,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
checks.push({
|
|
86
|
+
name: "percher.toml",
|
|
87
|
+
status: "fail",
|
|
88
|
+
message: err.message,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// 5. App resolution
|
|
94
|
+
// If --app is given, we don't depend on percher.toml. Otherwise we can only
|
|
95
|
+
// pull the app name when the TOML actually parsed — readPercherTomlAppName
|
|
96
|
+
// would re-attempt the parse and return null silently otherwise.
|
|
97
|
+
const appName = input.app ?? (tomlValid ? readPercherTomlAppName(ctx.cwd) : null);
|
|
98
|
+
if (!appName) {
|
|
99
|
+
const reason = tomlExists && !tomlValid
|
|
100
|
+
? "percher.toml is invalid — fix the issues above and re-run"
|
|
101
|
+
: "No app specified and no percher.toml found";
|
|
102
|
+
checks.push({ name: "App", status: "skip", message: reason });
|
|
52
103
|
return summarize(checks);
|
|
53
104
|
}
|
|
54
105
|
// 5. Fetch diagnostics from the API
|
|
@@ -113,13 +164,7 @@ export async function doctor(ctx, input = {}) {
|
|
|
113
164
|
status: "pass",
|
|
114
165
|
message: diag.env.count > 0 ? `${diag.env.count} set (${diag.env.keys.join(", ")})` : "None set",
|
|
115
166
|
});
|
|
116
|
-
// percher.toml
|
|
117
|
-
const hasToml = readPercherTomlAppName(ctx.cwd) !== null;
|
|
118
|
-
checks.push({
|
|
119
|
-
name: "percher.toml",
|
|
120
|
-
status: hasToml ? "pass" : "warn",
|
|
121
|
-
message: hasToml ? "Valid" : "Not found in current directory",
|
|
122
|
-
});
|
|
167
|
+
// (percher.toml validation now happens up-front — see step 4)
|
|
123
168
|
// Last deploy
|
|
124
169
|
if (diag.lastDeploy) {
|
|
125
170
|
const deployOk = diag.lastDeploy.status === "live";
|