@percher/core 0.2.2 → 0.2.4

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.
Files changed (82) hide show
  1. package/dist/commands/billing.d.ts +43 -0
  2. package/dist/commands/billing.d.ts.map +1 -0
  3. package/dist/commands/billing.js +65 -0
  4. package/dist/commands/billing.js.map +1 -0
  5. package/dist/commands/data.d.ts +10 -0
  6. package/dist/commands/data.d.ts.map +1 -1
  7. package/dist/commands/data.js +33 -3
  8. package/dist/commands/data.js.map +1 -1
  9. package/dist/commands/delete.d.ts +15 -0
  10. package/dist/commands/delete.d.ts.map +1 -0
  11. package/dist/commands/delete.js +26 -0
  12. package/dist/commands/delete.js.map +1 -0
  13. package/dist/commands/deploys.d.ts +45 -0
  14. package/dist/commands/deploys.d.ts.map +1 -0
  15. package/dist/commands/deploys.js +73 -0
  16. package/dist/commands/deploys.js.map +1 -0
  17. package/dist/commands/diagnose.d.ts +75 -0
  18. package/dist/commands/diagnose.d.ts.map +1 -0
  19. package/dist/commands/diagnose.js +102 -0
  20. package/dist/commands/diagnose.js.map +1 -0
  21. package/dist/commands/doctor.d.ts.map +1 -1
  22. package/dist/commands/doctor.js +58 -13
  23. package/dist/commands/doctor.js.map +1 -1
  24. package/dist/commands/export.d.ts +39 -0
  25. package/dist/commands/export.d.ts.map +1 -0
  26. package/dist/commands/export.js +107 -0
  27. package/dist/commands/export.js.map +1 -0
  28. package/dist/commands/import-project.d.ts +21 -0
  29. package/dist/commands/import-project.d.ts.map +1 -0
  30. package/dist/commands/import-project.js +134 -0
  31. package/dist/commands/import-project.js.map +1 -0
  32. package/dist/commands/insights.d.ts +44 -0
  33. package/dist/commands/insights.d.ts.map +1 -0
  34. package/dist/commands/insights.js +54 -0
  35. package/dist/commands/insights.js.map +1 -0
  36. package/dist/commands/mcp.d.ts +29 -0
  37. package/dist/commands/mcp.d.ts.map +1 -0
  38. package/dist/commands/mcp.js +35 -0
  39. package/dist/commands/mcp.js.map +1 -0
  40. package/dist/commands/publish.d.ts +35 -1
  41. package/dist/commands/publish.d.ts.map +1 -1
  42. package/dist/commands/publish.js +163 -9
  43. package/dist/commands/publish.js.map +1 -1
  44. package/dist/commands/push.d.ts.map +1 -1
  45. package/dist/commands/push.js +8 -4
  46. package/dist/commands/push.js.map +1 -1
  47. package/dist/commands/rename.d.ts +21 -0
  48. package/dist/commands/rename.d.ts.map +1 -0
  49. package/dist/commands/rename.js +18 -0
  50. package/dist/commands/rename.js.map +1 -0
  51. package/dist/commands/reset-superuser.d.ts +16 -0
  52. package/dist/commands/reset-superuser.d.ts.map +1 -0
  53. package/dist/commands/reset-superuser.js +17 -0
  54. package/dist/commands/reset-superuser.js.map +1 -0
  55. package/dist/error-classifier.d.ts +16 -0
  56. package/dist/error-classifier.d.ts.map +1 -1
  57. package/dist/error-classifier.js +135 -3
  58. package/dist/error-classifier.js.map +1 -1
  59. package/dist/errors.d.ts +1 -1
  60. package/dist/errors.d.ts.map +1 -1
  61. package/dist/errors.js.map +1 -1
  62. package/dist/index.d.ts +14 -3
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +14 -3
  65. package/dist/index.js.map +1 -1
  66. package/dist/integrations.d.ts +24 -0
  67. package/dist/integrations.d.ts.map +1 -0
  68. package/dist/integrations.js +106 -0
  69. package/dist/integrations.js.map +1 -0
  70. package/dist/plans.d.ts +43 -0
  71. package/dist/plans.d.ts.map +1 -1
  72. package/dist/plans.js +83 -1
  73. package/dist/plans.js.map +1 -1
  74. package/dist/qr.d.ts +5 -0
  75. package/dist/qr.d.ts.map +1 -0
  76. package/dist/qr.js +10 -0
  77. package/dist/qr.js.map +1 -0
  78. package/dist/templates.d.ts +24 -0
  79. package/dist/templates.d.ts.map +1 -1
  80. package/dist/templates.js +331 -0
  81. package/dist/templates.js.map +1 -1
  82. package/package.json +1 -1
@@ -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"}
@@ -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;
@@ -23,6 +25,14 @@ export interface DataResult {
23
25
  totalRecords: number;
24
26
  };
25
27
  adminUrl: string | null;
28
+ /**
29
+ * Set when one of the underlying calls failed in a way that's recoverable
30
+ * via redeploy/superuser reset. Lets the CLI render a partial result with
31
+ * a helpful next step instead of bombing the entire `data` command on a
32
+ * single 503 from /collections — the user usually still wants to see the
33
+ * sidecar mode + version + admin URL.
34
+ */
35
+ warning: string | null;
26
36
  }
27
37
  export declare function data(ctx: Context, input?: DataInput): Promise<DataResult>;
28
38
  //# sourceMappingURL=data.d.ts.map
@@ -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,CAqBnF"}
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;IACxB;;;;;;OAMG;IACH,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,wBAAsB,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,GAAE,SAAc,GAAG,OAAO,CAAC,UAAU,CAAC,CAmDnF"}
@@ -13,16 +13,46 @@ export async function data(ctx, input = {}) {
13
13
  hint: "Pass an app name or run from a directory with percher.toml.",
14
14
  });
15
15
  }
16
+ // status + admin-link are the always-available signals — they don't require
17
+ // PocketBase admin auth on the server side.
16
18
  const status = await ctx.client.data.getStatus(name);
17
- const collections = await ctx.client.data.getCollections(name);
18
- const stats = await ctx.client.data.getStats(name);
19
19
  const adminLink = await ctx.client.data.getAdminLink(name);
20
+ // Collections + stats hit the PB sidecar with the cached admin token.
21
+ // If that token is missing or invalid (e.g. the PB superuser was rotated
22
+ // out from under us), the API returns 503 PB_AUTH_FAILED. Treat that as a
23
+ // partial result rather than a fatal error: surface the sidecar's mode +
24
+ // version + admin URL and tell the user the next step.
25
+ let collections = [];
26
+ let stats = { collections: 0, totalRecords: 0 };
27
+ let warning = null;
28
+ try {
29
+ const cols = await ctx.client.data.getCollections(name);
30
+ collections = cols.map((c) => ({ name: c.name, type: c.type }));
31
+ stats = await ctx.client.data.getStats(name);
32
+ }
33
+ catch (err) {
34
+ // Only the API's explicit PB_AUTH_FAILED code is treated as recoverable —
35
+ // any other failure (generic 5xx, transport, INTERNAL_ERROR) bubbles so
36
+ // the user sees the real cause instead of a misleading "try reset-
37
+ // superuser" suggestion. The HTTP status alone is too broad: the API
38
+ // client maps generic 5xx without a code to INTERNAL_ERROR with status
39
+ // 503, which would otherwise be indistinguishable from PB auth here.
40
+ const code = err.code;
41
+ if (code === "PB_AUTH_FAILED") {
42
+ warning = `Could not authenticate with PocketBase — collections + stats unavailable. Try \`percher data reset-superuser ${name}\` to rotate the credentials.`;
43
+ }
44
+ else {
45
+ throw err;
46
+ }
47
+ }
20
48
  return {
21
49
  mode: status.mode,
22
50
  status: status.status,
23
- collections: collections.map((c) => ({ name: c.name, type: c.type })),
51
+ version: status.version ?? null,
52
+ collections,
24
53
  stats,
25
54
  adminUrl: adminLink.adminUrl ?? null,
55
+ warning,
26
56
  };
27
57
  }
28
58
  //# sourceMappingURL=data.js.map
@@ -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;AAWH,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,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"}
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;AAqBH,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,4EAA4E;IAC5E,4CAA4C;IAC5C,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACrD,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAE3D,sEAAsE;IACtE,yEAAyE;IACzE,0EAA0E;IAC1E,yEAAyE;IACzE,uDAAuD;IACvD,IAAI,WAAW,GAA0C,EAAE,CAAC;IAC5D,IAAI,KAAK,GAAG,EAAE,WAAW,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;IAChD,IAAI,OAAO,GAAkB,IAAI,CAAC;IAElC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QACxD,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAChE,KAAK,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,0EAA0E;QAC1E,wEAAwE;QACxE,mEAAmE;QACnE,qEAAqE;QACrE,uEAAuE;QACvE,qEAAqE;QACrE,MAAM,IAAI,GAAI,GAAyB,CAAC,IAAI,CAAC;QAC7C,IAAI,IAAI,KAAK,gBAAgB,EAAE,CAAC;YAC9B,OAAO,GAAG,gHAAgH,IAAI,+BAA+B,CAAC;QAChK,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,IAAI;QAC/B,WAAW;QACX,KAAK;QACL,QAAQ,EAAE,SAAS,CAAC,QAAQ,IAAI,IAAI;QACpC,OAAO;KACR,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":"AAAA,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,CA2JzF"}
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"}