@percher/core 0.2.6 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/dist/ai-files-manifest.d.ts +28 -0
  2. package/dist/ai-files-manifest.d.ts.map +1 -0
  3. package/dist/ai-files-manifest.js +96 -0
  4. package/dist/ai-files-manifest.js.map +1 -0
  5. package/dist/commands/account.d.ts +51 -0
  6. package/dist/commands/account.d.ts.map +1 -0
  7. package/dist/commands/account.js +88 -0
  8. package/dist/commands/account.js.map +1 -0
  9. package/dist/commands/ai-files.d.ts +73 -0
  10. package/dist/commands/ai-files.d.ts.map +1 -0
  11. package/dist/commands/ai-files.js +179 -0
  12. package/dist/commands/ai-files.js.map +1 -0
  13. package/dist/commands/billing.d.ts +1 -1
  14. package/dist/commands/billing.d.ts.map +1 -1
  15. package/dist/commands/billing.js +1 -1
  16. package/dist/commands/billing.js.map +1 -1
  17. package/dist/commands/continue.d.ts +48 -0
  18. package/dist/commands/continue.d.ts.map +1 -0
  19. package/dist/commands/continue.js +121 -0
  20. package/dist/commands/continue.js.map +1 -0
  21. package/dist/commands/create.d.ts +1 -1
  22. package/dist/commands/create.d.ts.map +1 -1
  23. package/dist/commands/create.js +1 -1
  24. package/dist/commands/create.js.map +1 -1
  25. package/dist/commands/dashboard.d.ts +15 -0
  26. package/dist/commands/dashboard.d.ts.map +1 -0
  27. package/dist/commands/dashboard.js +33 -0
  28. package/dist/commands/dashboard.js.map +1 -0
  29. package/dist/commands/data-export.d.ts +21 -0
  30. package/dist/commands/data-export.d.ts.map +1 -0
  31. package/dist/commands/data-export.js +36 -0
  32. package/dist/commands/data-export.js.map +1 -0
  33. package/dist/commands/data.d.ts +3 -3
  34. package/dist/commands/data.d.ts.map +1 -1
  35. package/dist/commands/data.js +1 -1
  36. package/dist/commands/data.js.map +1 -1
  37. package/dist/commands/delete.d.ts +1 -1
  38. package/dist/commands/delete.d.ts.map +1 -1
  39. package/dist/commands/delete.js +1 -1
  40. package/dist/commands/delete.js.map +1 -1
  41. package/dist/commands/deploys.d.ts +2 -2
  42. package/dist/commands/deploys.d.ts.map +1 -1
  43. package/dist/commands/deploys.js +21 -5
  44. package/dist/commands/deploys.js.map +1 -1
  45. package/dist/commands/dev.d.ts +1 -9
  46. package/dist/commands/dev.d.ts.map +1 -1
  47. package/dist/commands/dev.js +79 -24
  48. package/dist/commands/dev.js.map +1 -1
  49. package/dist/commands/diagnose.d.ts +1 -1
  50. package/dist/commands/diagnose.d.ts.map +1 -1
  51. package/dist/commands/diagnose.js +1 -1
  52. package/dist/commands/diagnose.js.map +1 -1
  53. package/dist/commands/doctor.d.ts +75 -3
  54. package/dist/commands/doctor.d.ts.map +1 -1
  55. package/dist/commands/doctor.js +822 -10
  56. package/dist/commands/doctor.js.map +1 -1
  57. package/dist/commands/domains.d.ts +1 -1
  58. package/dist/commands/domains.d.ts.map +1 -1
  59. package/dist/commands/domains.js +1 -1
  60. package/dist/commands/domains.js.map +1 -1
  61. package/dist/commands/env-scan.d.ts +2 -0
  62. package/dist/commands/env-scan.d.ts.map +1 -0
  63. package/dist/commands/env-scan.js +92 -0
  64. package/dist/commands/env-scan.js.map +1 -0
  65. package/dist/commands/env.d.ts +1 -1
  66. package/dist/commands/env.d.ts.map +1 -1
  67. package/dist/commands/env.js +1 -1
  68. package/dist/commands/env.js.map +1 -1
  69. package/dist/commands/export.d.ts +1 -1
  70. package/dist/commands/export.js +1 -1
  71. package/dist/commands/generate.d.ts +1 -1
  72. package/dist/commands/generate.d.ts.map +1 -1
  73. package/dist/commands/generate.js +14 -9
  74. package/dist/commands/generate.js.map +1 -1
  75. package/dist/commands/github.d.ts +60 -0
  76. package/dist/commands/github.d.ts.map +1 -0
  77. package/dist/commands/github.js +112 -0
  78. package/dist/commands/github.js.map +1 -0
  79. package/dist/commands/import-project.d.ts +1 -1
  80. package/dist/commands/import-project.d.ts.map +1 -1
  81. package/dist/commands/import-project.js +1 -1
  82. package/dist/commands/import-project.js.map +1 -1
  83. package/dist/commands/init.d.ts +1 -1
  84. package/dist/commands/init.d.ts.map +1 -1
  85. package/dist/commands/init.js +1 -1
  86. package/dist/commands/init.js.map +1 -1
  87. package/dist/commands/insights.d.ts +1 -1
  88. package/dist/commands/insights.d.ts.map +1 -1
  89. package/dist/commands/insights.js +1 -1
  90. package/dist/commands/insights.js.map +1 -1
  91. package/dist/commands/login.d.ts +1 -1
  92. package/dist/commands/login.d.ts.map +1 -1
  93. package/dist/commands/login.js +1 -1
  94. package/dist/commands/login.js.map +1 -1
  95. package/dist/commands/logs.d.ts +1 -1
  96. package/dist/commands/logs.d.ts.map +1 -1
  97. package/dist/commands/logs.js +1 -1
  98. package/dist/commands/logs.js.map +1 -1
  99. package/dist/commands/mcp.d.ts +1 -1
  100. package/dist/commands/mcp.d.ts.map +1 -1
  101. package/dist/commands/mcp.js +1 -1
  102. package/dist/commands/mcp.js.map +1 -1
  103. package/dist/commands/open.d.ts +1 -1
  104. package/dist/commands/open.d.ts.map +1 -1
  105. package/dist/commands/open.js +1 -1
  106. package/dist/commands/open.js.map +1 -1
  107. package/dist/commands/publish-failure.d.ts +31 -0
  108. package/dist/commands/publish-failure.d.ts.map +1 -0
  109. package/dist/commands/publish-failure.js +150 -0
  110. package/dist/commands/publish-failure.js.map +1 -0
  111. package/dist/commands/publish-node.d.ts +16 -0
  112. package/dist/commands/publish-node.d.ts.map +1 -0
  113. package/dist/commands/publish-node.js +42 -0
  114. package/dist/commands/publish-node.js.map +1 -0
  115. package/dist/commands/publish.d.ts +105 -3
  116. package/dist/commands/publish.d.ts.map +1 -1
  117. package/dist/commands/publish.js +746 -158
  118. package/dist/commands/publish.js.map +1 -1
  119. package/dist/commands/push.d.ts +45 -8
  120. package/dist/commands/push.d.ts.map +1 -1
  121. package/dist/commands/push.js +233 -22
  122. package/dist/commands/push.js.map +1 -1
  123. package/dist/commands/redeploy.d.ts +28 -0
  124. package/dist/commands/redeploy.d.ts.map +1 -0
  125. package/dist/commands/redeploy.js +421 -0
  126. package/dist/commands/redeploy.js.map +1 -0
  127. package/dist/commands/rename.d.ts +1 -1
  128. package/dist/commands/rename.d.ts.map +1 -1
  129. package/dist/commands/rename.js +1 -1
  130. package/dist/commands/rename.js.map +1 -1
  131. package/dist/commands/reproduce.d.ts +64 -0
  132. package/dist/commands/reproduce.d.ts.map +1 -0
  133. package/dist/commands/reproduce.js +211 -0
  134. package/dist/commands/reproduce.js.map +1 -0
  135. package/dist/commands/reset-superuser.d.ts +1 -1
  136. package/dist/commands/reset-superuser.d.ts.map +1 -1
  137. package/dist/commands/reset-superuser.js +1 -1
  138. package/dist/commands/reset-superuser.js.map +1 -1
  139. package/dist/commands/restore.d.ts +79 -0
  140. package/dist/commands/restore.d.ts.map +1 -0
  141. package/dist/commands/restore.js +164 -0
  142. package/dist/commands/restore.js.map +1 -0
  143. package/dist/commands/resume.d.ts +1 -1
  144. package/dist/commands/resume.d.ts.map +1 -1
  145. package/dist/commands/resume.js +1 -1
  146. package/dist/commands/resume.js.map +1 -1
  147. package/dist/commands/rollback.d.ts +21 -8
  148. package/dist/commands/rollback.d.ts.map +1 -1
  149. package/dist/commands/rollback.js +12 -6
  150. package/dist/commands/rollback.js.map +1 -1
  151. package/dist/commands/status.d.ts +33 -0
  152. package/dist/commands/status.d.ts.map +1 -0
  153. package/dist/commands/status.js +48 -0
  154. package/dist/commands/status.js.map +1 -0
  155. package/dist/commands/unsuspend.d.ts +35 -0
  156. package/dist/commands/unsuspend.d.ts.map +1 -0
  157. package/dist/commands/unsuspend.js +27 -0
  158. package/dist/commands/unsuspend.js.map +1 -0
  159. package/dist/commands/versions.d.ts +1 -1
  160. package/dist/commands/versions.d.ts.map +1 -1
  161. package/dist/commands/versions.js +1 -1
  162. package/dist/commands/versions.js.map +1 -1
  163. package/dist/commands/wait-deploy.d.ts +92 -0
  164. package/dist/commands/wait-deploy.d.ts.map +1 -0
  165. package/dist/commands/wait-deploy.js +226 -0
  166. package/dist/commands/wait-deploy.js.map +1 -0
  167. package/dist/env-scan-source.d.ts +39 -0
  168. package/dist/env-scan-source.d.ts.map +1 -0
  169. package/dist/env-scan-source.js +332 -0
  170. package/dist/env-scan-source.js.map +1 -0
  171. package/dist/error-classifier.d.ts.map +1 -1
  172. package/dist/error-classifier.js +67 -4
  173. package/dist/error-classifier.js.map +1 -1
  174. package/dist/errors.d.ts +8 -1
  175. package/dist/errors.d.ts.map +1 -1
  176. package/dist/errors.js +2 -0
  177. package/dist/errors.js.map +1 -1
  178. package/dist/event-renderer.d.ts +17 -0
  179. package/dist/event-renderer.d.ts.map +1 -0
  180. package/dist/event-renderer.js +130 -0
  181. package/dist/event-renderer.js.map +1 -0
  182. package/dist/index.d.ts +16 -1
  183. package/dist/index.d.ts.map +1 -1
  184. package/dist/index.js +15 -0
  185. package/dist/index.js.map +1 -1
  186. package/dist/plans.d.ts +20 -0
  187. package/dist/plans.d.ts.map +1 -1
  188. package/dist/plans.js +15 -0
  189. package/dist/plans.js.map +1 -1
  190. package/dist/poll-deployment.d.ts +59 -0
  191. package/dist/poll-deployment.d.ts.map +1 -0
  192. package/dist/poll-deployment.js +93 -0
  193. package/dist/poll-deployment.js.map +1 -0
  194. package/dist/publish-retry.d.ts +29 -0
  195. package/dist/publish-retry.d.ts.map +1 -0
  196. package/dist/publish-retry.js +224 -0
  197. package/dist/publish-retry.js.map +1 -0
  198. package/dist/recovery.d.ts +356 -0
  199. package/dist/recovery.d.ts.map +1 -0
  200. package/dist/recovery.js +300 -0
  201. package/dist/recovery.js.map +1 -0
  202. package/dist/stream-utils.d.ts +21 -0
  203. package/dist/stream-utils.d.ts.map +1 -0
  204. package/dist/stream-utils.js +41 -0
  205. package/dist/stream-utils.js.map +1 -0
  206. package/dist/structured-error-codes.d.ts +30 -0
  207. package/dist/structured-error-codes.d.ts.map +1 -0
  208. package/dist/structured-error-codes.js +86 -0
  209. package/dist/structured-error-codes.js.map +1 -0
  210. package/dist/tarball.d.ts +11 -0
  211. package/dist/tarball.d.ts.map +1 -1
  212. package/dist/tarball.js +30 -9
  213. package/dist/tarball.js.map +1 -1
  214. package/dist/templates/ai-files/claude-md.d.ts +7 -0
  215. package/dist/templates/ai-files/claude-md.d.ts.map +1 -0
  216. package/dist/templates/ai-files/claude-md.js +78 -0
  217. package/dist/templates/ai-files/claude-md.js.map +1 -0
  218. package/dist/templates/ai-files/cursor-percher-mdc.d.ts +7 -0
  219. package/dist/templates/ai-files/cursor-percher-mdc.d.ts.map +1 -0
  220. package/dist/templates/ai-files/cursor-percher-mdc.js +111 -0
  221. package/dist/templates/ai-files/cursor-percher-mdc.js.map +1 -0
  222. package/dist/templates/ai-files/index.d.ts +8 -0
  223. package/dist/templates/ai-files/index.d.ts.map +1 -0
  224. package/dist/templates/ai-files/index.js +4 -0
  225. package/dist/templates/ai-files/index.js.map +1 -0
  226. package/package.json +6 -5
@@ -0,0 +1,33 @@
1
+ import type { PlatformHealth } from "@percher/client";
2
+ import { z } from "zod/v3";
3
+ import type { Context } from "../context";
4
+ export declare const statusInputSchema: z.ZodObject<{
5
+ json: z.ZodOptional<z.ZodBoolean>;
6
+ }, "strip", z.ZodTypeAny, {
7
+ json?: boolean | undefined;
8
+ }, {
9
+ json?: boolean | undefined;
10
+ }>;
11
+ export type StatusInput = z.infer<typeof statusInputSchema>;
12
+ /**
13
+ * Phase 6.3 — derived top-line state for `percher status`. The CLI
14
+ * folds the API response (which always returns `status: "ok"` if it's
15
+ * answering at all) into one of three buckets the user actually
16
+ * cares about: everything green, the worker degraded but the API
17
+ * still serving reads, or the API itself unreachable.
18
+ */
19
+ export type StatusVerdict = "operational" | "worker-degraded" | "unreachable";
20
+ export interface StatusResult {
21
+ verdict: StatusVerdict;
22
+ /**
23
+ * Human-readable summary line. Mirrors what the CLI prints.
24
+ */
25
+ summary: string;
26
+ /** Raw `/health` response, when reachable. */
27
+ health: PlatformHealth | null;
28
+ /** Underlying error message when verdict === "unreachable". */
29
+ reachError?: string;
30
+ }
31
+ export declare function status(ctx: Context, _input?: StatusInput): Promise<StatusResult>;
32
+ export declare function deriveVerdict(health: PlatformHealth): StatusVerdict;
33
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAC3B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,eAAO,MAAM,iBAAiB;;;;;;EAE5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG,iBAAiB,GAAG,aAAa,CAAC;AAE9E,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,aAAa,CAAC;IACvB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAC9B,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,WAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CAmB1F;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,aAAa,CAKnE"}
@@ -0,0 +1,48 @@
1
+ import { z } from "zod/v3";
2
+ export const statusInputSchema = z.object({
3
+ json: z.boolean().optional(),
4
+ });
5
+ export async function status(ctx, _input = {}) {
6
+ let health;
7
+ try {
8
+ health = await ctx.client.platform.health();
9
+ }
10
+ catch (err) {
11
+ return {
12
+ verdict: "unreachable",
13
+ summary: `API unreachable (${err.message})`,
14
+ health: null,
15
+ reachError: err.message,
16
+ };
17
+ }
18
+ const verdict = deriveVerdict(health);
19
+ return {
20
+ verdict,
21
+ summary: summarize(verdict, health),
22
+ health,
23
+ };
24
+ }
25
+ export function deriveVerdict(health) {
26
+ if (health.status !== "ok")
27
+ return "worker-degraded";
28
+ if (health.worker?.circuitBreaker?.open)
29
+ return "worker-degraded";
30
+ if (health.worker?.health && health.worker.health.ok === false)
31
+ return "worker-degraded";
32
+ return "operational";
33
+ }
34
+ function summarize(verdict, health) {
35
+ if (verdict === "operational")
36
+ return "All systems operational";
37
+ const breaker = health.worker?.circuitBreaker;
38
+ const workerHealth = health.worker?.health;
39
+ if (breaker?.open) {
40
+ return `Worker circuit breaker open (${breaker.failuresInWindow}/${breaker.threshold} failures in last ${Math.round(breaker.windowMs / 1000)}s) — deploys will fail fast until it recovers`;
41
+ }
42
+ if (workerHealth && workerHealth.ok === false) {
43
+ const lastErr = workerHealth.lastErr ? ` — last error: ${workerHealth.lastErr}` : "";
44
+ return `Worker unreachable (${workerHealth.consecutiveErrs} consecutive failed health checks)${lastErr}`;
45
+ }
46
+ return "API responded but reported a non-ok status";
47
+ }
48
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAG3B,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CAC7B,CAAC,CAAC;AAwBH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,GAAY,EAAE,SAAsB,EAAE;IACjE,IAAI,MAAsB,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;IAC9C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,aAAa;YACtB,OAAO,EAAE,oBAAqB,GAAa,CAAC,OAAO,GAAG;YACtD,MAAM,EAAE,IAAI;YACZ,UAAU,EAAG,GAAa,CAAC,OAAO;SACnC,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACtC,OAAO;QACL,OAAO;QACP,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC;QACnC,MAAM;KACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAsB;IAClD,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI;QAAE,OAAO,iBAAiB,CAAC;IACrD,IAAI,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI;QAAE,OAAO,iBAAiB,CAAC;IAClE,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,KAAK;QAAE,OAAO,iBAAiB,CAAC;IACzF,OAAO,aAAa,CAAC;AACvB,CAAC;AAED,SAAS,SAAS,CAAC,OAAsB,EAAE,MAAsB;IAC/D,IAAI,OAAO,KAAK,aAAa;QAAE,OAAO,yBAAyB,CAAC;IAChE,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC;IAC9C,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAC3C,IAAI,OAAO,EAAE,IAAI,EAAE,CAAC;QAClB,OAAO,gCAAgC,OAAO,CAAC,gBAAgB,IAAI,OAAO,CAAC,SAAS,qBAAqB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,+CAA+C,CAAC;IAC9L,CAAC;IACD,IAAI,YAAY,IAAI,YAAY,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;QAC9C,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,kBAAkB,YAAY,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrF,OAAO,uBAAuB,YAAY,CAAC,eAAe,qCAAqC,OAAO,EAAE,CAAC;IAC3G,CAAC;IACD,OAAO,4CAA4C,CAAC;AACtD,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { z } from "zod/v3";
2
+ import type { Context } from "../context";
3
+ export declare const unsuspendInputSchema: z.ZodObject<{
4
+ app: z.ZodString;
5
+ }, "strip", z.ZodTypeAny, {
6
+ app: string;
7
+ }, {
8
+ app: string;
9
+ }>;
10
+ export type UnsuspendInput = z.infer<typeof unsuspendInputSchema>;
11
+ export interface UnsuspendResult {
12
+ ok: true;
13
+ app: {
14
+ id: string;
15
+ name: string;
16
+ status: string;
17
+ };
18
+ /** True if this call flipped the app from suspended to active.
19
+ * False if the app was already active (idempotent). */
20
+ flipped: boolean;
21
+ summary: string;
22
+ }
23
+ /**
24
+ * Unsuspend an app. Used from CLI ("Lock removed") and from the
25
+ * agent surface (`percher_unsuspend` MCP tool) when an auto-suspend
26
+ * is the recovery action the agent should take after the user has
27
+ * fixed the underlying failure.
28
+ *
29
+ * Server-side this triggers the cooldown threshold in
30
+ * FailureLoopGuard: if the app fails N=3 more times within an hour,
31
+ * it auto-suspends again (faster than the standard N=8) — discourages
32
+ * the "unsuspend → resume the loop → re-burn the budget" antipattern.
33
+ */
34
+ export declare function unsuspend(ctx: Context, input: UnsuspendInput): Promise<UnsuspendResult>;
35
+ //# sourceMappingURL=unsuspend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unsuspend.d.ts","sourceRoot":"","sources":["../../src/commands/unsuspend.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAC3B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,eAAO,MAAM,oBAAoB;;;;;;EAE/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,IAAI,CAAC;IACT,GAAG,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAClD;4DACwD;IACxD,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAU7F"}
@@ -0,0 +1,27 @@
1
+ import { z } from "zod/v3";
2
+ export const unsuspendInputSchema = z.object({
3
+ app: z.string().min(1).describe("App name or id to unsuspend"),
4
+ });
5
+ /**
6
+ * Unsuspend an app. Used from CLI ("Lock removed") and from the
7
+ * agent surface (`percher_unsuspend` MCP tool) when an auto-suspend
8
+ * is the recovery action the agent should take after the user has
9
+ * fixed the underlying failure.
10
+ *
11
+ * Server-side this triggers the cooldown threshold in
12
+ * FailureLoopGuard: if the app fails N=3 more times within an hour,
13
+ * it auto-suspends again (faster than the standard N=8) — discourages
14
+ * the "unsuspend → resume the loop → re-burn the budget" antipattern.
15
+ */
16
+ export async function unsuspend(ctx, input) {
17
+ const result = await ctx.client.apps.unsuspend(input.app);
18
+ return {
19
+ ok: true,
20
+ app: result.app,
21
+ flipped: result.flipped,
22
+ summary: result.flipped
23
+ ? `Unsuspended ${result.app.name}. If it fails 3 more times in the next hour, the cooldown threshold will auto-suspend it again — fix the cause before retrying.`
24
+ : `${result.app.name} was already active (no change).`,
25
+ };
26
+ }
27
+ //# sourceMappingURL=unsuspend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unsuspend.js","sourceRoot":"","sources":["../../src/commands/unsuspend.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAG3B,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,6BAA6B,CAAC;CAC/D,CAAC,CAAC;AAYH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAY,EAAE,KAAqB;IACjE,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1D,OAAO;QACL,EAAE,EAAE,IAAI;QACR,GAAG,EAAE,MAAM,CAAC,GAAG;QACf,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,OAAO,EAAE,MAAM,CAAC,OAAO;YACrB,CAAC,CAAC,eAAe,MAAM,CAAC,GAAG,CAAC,IAAI,iIAAiI;YACjK,CAAC,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,kCAAkC;KACzD,CAAC;AACJ,CAAC"}
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import { z } from "zod/v3";
2
2
  import type { Context } from "../context";
3
3
  export declare const versionsInputSchema: z.ZodObject<{
4
4
  app: z.ZodOptional<z.ZodString>;
@@ -1 +1 @@
1
- {"version":3,"file":"versions.d.ts","sourceRoot":"","sources":["../../src/commands/versions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,eAAO,MAAM,mBAAmB;;;;;;EAA2C,CAAC;AAC5E,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,GAAE,aAAkB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAS7F"}
1
+ {"version":3,"file":"versions.d.ts","sourceRoot":"","sources":["../../src/commands/versions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,eAAO,MAAM,mBAAmB;;;;;;EAA2C,CAAC;AAC5E,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,GAAE,aAAkB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAS7F"}
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import { z } from "zod/v3";
2
2
  import { readPercherTomlAppName } from "../app-name";
3
3
  import { PercherCoreError } from "../errors";
4
4
  export const versionsInputSchema = z.object({ app: z.string().optional() });
@@ -1 +1 @@
1
- {"version":3,"file":"versions.js","sourceRoot":"","sources":["../../src/commands/versions.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,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAS5E,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,GAAY,EAAE,QAAuB,EAAE;IACpE,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;IACD,OAAO,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;AAC5C,CAAC"}
1
+ {"version":3,"file":"versions.js","sourceRoot":"","sources":["../../src/commands/versions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAC3B,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAS5E,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,GAAY,EAAE,QAAuB,EAAE;IACpE,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;IACD,OAAO,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,92 @@
1
+ import type { App, Deployment } from "@percher/client";
2
+ import { z } from "zod/v3";
3
+ import type { Context } from "../context";
4
+ import { type PublishRecovery } from "../recovery";
5
+ import type { PublishError } from "./publish";
6
+ export declare const waitForDeployInputSchema: z.ZodObject<{
7
+ app: z.ZodString;
8
+ deployId: z.ZodString;
9
+ timeoutSeconds: z.ZodOptional<z.ZodNumber>;
10
+ pollIntervalSeconds: z.ZodOptional<z.ZodNumber>;
11
+ }, "strip", z.ZodTypeAny, {
12
+ app: string;
13
+ deployId: string;
14
+ timeoutSeconds?: number | undefined;
15
+ pollIntervalSeconds?: number | undefined;
16
+ }, {
17
+ app: string;
18
+ deployId: string;
19
+ timeoutSeconds?: number | undefined;
20
+ pollIntervalSeconds?: number | undefined;
21
+ }>;
22
+ export type WaitForDeployInput = z.infer<typeof waitForDeployInputSchema>;
23
+ export interface WaitForDeployResult {
24
+ /**
25
+ * - `live` — deployment reached terminal success.
26
+ * - `failed` — deployment reached terminal failure (`error` populated).
27
+ * - `replaced` — deployment was superseded. Resolver looks at the
28
+ * latest deploy on the app and routes accordingly:
29
+ * • newer live exists → `nextAction: "none"`, `url` is the
30
+ * current live URL, `summary` includes the resolved deployId.
31
+ * • newer deploy still in flight → `nextAction: "wait_deploy"`
32
+ * pointing at the newer deployId.
33
+ * • no newer / latest also failed → `nextAction: "ask_user"`.
34
+ * Never `retry` — replaced isn't a transient failure of this row.
35
+ * - `still_running` — timeout hit before terminal status; agent should
36
+ * call this tool again with the same deployId.
37
+ */
38
+ status: "live" | "failed" | "replaced" | "still_running";
39
+ app: {
40
+ id: string;
41
+ name: string;
42
+ url: string;
43
+ };
44
+ deployment: Deployment;
45
+ /** Live or preview URL on success. */
46
+ url?: string;
47
+ /** Wall-clock seconds spent in this single wait call. */
48
+ elapsedSeconds: number;
49
+ /**
50
+ * Total seconds since the deployment was first created (server-side
51
+ * `createdAt`). Helps the agent answer "is this stuck?" — combined
52
+ * with status, an `elapsedSeconds`-since-create > 600 in `building`
53
+ * is a strong signal of a real stall, not just a slow build.
54
+ */
55
+ totalElapsedSeconds: number;
56
+ error?: PublishError;
57
+ recovery: PublishRecovery;
58
+ summary: string;
59
+ }
60
+ /**
61
+ * FUTURE11 Phase 2 — short-poll a specific deploy without starting a new
62
+ * one. Returns terminal results when the deploy reaches live/failed
63
+ * within the timeout, otherwise returns `still_running` with a
64
+ * `wait_deploy` recovery so the agent loops without growing the call
65
+ * stack. Never starts a new deploy.
66
+ */
67
+ export declare function waitForDeploy(ctx: Context, input: WaitForDeployInput): Promise<WaitForDeployResult>;
68
+ /**
69
+ * FUTURE11 review round 5 — resolve what `replaced` actually means by
70
+ * looking at the latest deploy. Two API code-paths produce `replaced`:
71
+ *
72
+ * 1. `markPreviousLiveAsReplaced` — a NEWER live deploy succeeded
73
+ * and pushed this row aside. App is live; route to nextAction='none'.
74
+ * 2. `markLiveDeployReplaced` — reconcile saw the container is gone
75
+ * (CONTAINER_NOT_FOUND). No newer live exists; app is effectively
76
+ * down. Route to ask_user.
77
+ *
78
+ * Resolver also handles the in-flight case where the newer deploy is
79
+ * still building — point at it via wait_deploy. Matches FUTURE12 Phase
80
+ * 4's locked behavior for `replaced_by_newer`.
81
+ */
82
+ export declare function resolveReplaced(opts: {
83
+ ctx: Context;
84
+ app: App;
85
+ replacedDeployment: Deployment;
86
+ }): Promise<{
87
+ recovery: PublishRecovery;
88
+ url?: string;
89
+ summary: string;
90
+ resolvedDeployment?: Deployment;
91
+ }>;
92
+ //# sourceMappingURL=wait-deploy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wait-deploy.d.ts","sourceRoot":"","sources":["../../src/commands/wait-deploy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEvD,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAC3B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EACL,KAAK,eAAe,EAKrB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAG9C,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;EAmBnC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,MAAM,WAAW,mBAAmB;IAClC;;;;;;;;;;;;;OAaG;IACH,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,eAAe,CAAC;IACzD,GAAG,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,UAAU,EAAE,UAAU,CAAC;IACvB,sCAAsC;IACtC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAC5B,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,eAAe,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,mBAAmB,CAAC,CAoE9B;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,GAAG,CAAC;IACT,kBAAkB,EAAE,UAAU,CAAC;CAChC,GAAG,OAAO,CAAC;IACV,QAAQ,EAAE,eAAe,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,CAAC,EAAE,UAAU,CAAC;CACjC,CAAC,CA4ED"}
@@ -0,0 +1,226 @@
1
+ import { TIMEOUTS } from "@percher/shared/timeouts";
2
+ import { z } from "zod/v3";
3
+ import { RECOVERY_NONE, recoveryAsk, recoveryNone, recoveryWait, } from "../recovery";
4
+ import { classifyDeploymentFailure } from "./publish-failure";
5
+ export const waitForDeployInputSchema = z.object({
6
+ app: z.string().describe("App name (the value of [app].name in percher.toml)"),
7
+ deployId: z.string().describe("Deploy id returned by percher_publish (e.g. dep_abc123)"),
8
+ timeoutSeconds: z
9
+ .number()
10
+ .int()
11
+ .min(1)
12
+ .max(TIMEOUTS.mcpWaitDeployMax / 1000)
13
+ .optional()
14
+ .describe(`Maximum wall-clock seconds to wait before returning still_running (default ${TIMEOUTS.mcpWaitDeployDefault / 1000}, max ${TIMEOUTS.mcpWaitDeployMax / 1000}). Keeping this short is the point — the agent can resume in another short call without holding a long-lived connection.`),
15
+ pollIntervalSeconds: z
16
+ .number()
17
+ .int()
18
+ .min(1)
19
+ .max(30)
20
+ .optional()
21
+ .describe("Seconds between polls (default 2)."),
22
+ });
23
+ /**
24
+ * FUTURE11 Phase 2 — short-poll a specific deploy without starting a new
25
+ * one. Returns terminal results when the deploy reaches live/failed
26
+ * within the timeout, otherwise returns `still_running` with a
27
+ * `wait_deploy` recovery so the agent loops without growing the call
28
+ * stack. Never starts a new deploy.
29
+ */
30
+ export async function waitForDeploy(ctx, input) {
31
+ const timeoutSeconds = input.timeoutSeconds ?? TIMEOUTS.mcpWaitDeployDefault / 1000;
32
+ const pollIntervalSeconds = input.pollIntervalSeconds ?? 2;
33
+ // Fetch app + initial deployment in parallel; the app row is needed for
34
+ // the URL on success and for failure-classification context.
35
+ const [app, initial] = await Promise.all([
36
+ ctx.client.apps.get(input.app),
37
+ ctx.client.apps.getDeployment(input.app, input.deployId),
38
+ ]);
39
+ const startedAt = Date.now();
40
+ const deadlineMs = startedAt + timeoutSeconds * 1000;
41
+ let deployment = initial;
42
+ while (!isTerminal(deployment.status)) {
43
+ if (Date.now() >= deadlineMs) {
44
+ return buildStillRunningResult(app, deployment, startedAt);
45
+ }
46
+ await new Promise((r) => setTimeout(r, pollIntervalSeconds * 1000));
47
+ try {
48
+ deployment = await ctx.client.apps.getDeployment(input.app, input.deployId);
49
+ }
50
+ catch (err) {
51
+ // Transient fetch errors during a short wait don't justify
52
+ // throwing — return still_running so the agent retries. The
53
+ // deploy may already be live server-side; the agent's next call
54
+ // will pick that up.
55
+ const msg = err.message ?? String(err);
56
+ if (msg.startsWith("network error:")) {
57
+ return buildStillRunningResult(app, deployment, startedAt, msg);
58
+ }
59
+ throw err;
60
+ }
61
+ }
62
+ if (deployment.status === "failed") {
63
+ const { error, recovery, summary } = await classifyDeploymentFailure({
64
+ ctx,
65
+ app,
66
+ deployment,
67
+ });
68
+ return {
69
+ status: "failed",
70
+ app: { id: app.id, name: app.name, url: app.url },
71
+ deployment,
72
+ elapsedSeconds: Math.round((Date.now() - startedAt) / 1000),
73
+ totalElapsedSeconds: secondsSinceCreated(deployment),
74
+ error,
75
+ recovery,
76
+ summary,
77
+ };
78
+ }
79
+ if (deployment.status === "replaced") {
80
+ return await buildReplacedResult({ ctx, app, deployment, startedAt });
81
+ }
82
+ const url = deployment.previewUrl ?? deployment.url ?? app.url;
83
+ return {
84
+ status: "live",
85
+ app: { id: app.id, name: app.name, url: app.url },
86
+ deployment,
87
+ url,
88
+ elapsedSeconds: Math.round((Date.now() - startedAt) / 1000),
89
+ totalElapsedSeconds: secondsSinceCreated(deployment),
90
+ recovery: RECOVERY_NONE,
91
+ summary: `${app.name} deploy ${deployment.id} is live → ${url}`,
92
+ };
93
+ }
94
+ function isTerminal(status) {
95
+ return status === "live" || status === "failed" || status === "replaced";
96
+ }
97
+ /**
98
+ * FUTURE11 review round 5 — resolve what `replaced` actually means by
99
+ * looking at the latest deploy. Two API code-paths produce `replaced`:
100
+ *
101
+ * 1. `markPreviousLiveAsReplaced` — a NEWER live deploy succeeded
102
+ * and pushed this row aside. App is live; route to nextAction='none'.
103
+ * 2. `markLiveDeployReplaced` — reconcile saw the container is gone
104
+ * (CONTAINER_NOT_FOUND). No newer live exists; app is effectively
105
+ * down. Route to ask_user.
106
+ *
107
+ * Resolver also handles the in-flight case where the newer deploy is
108
+ * still building — point at it via wait_deploy. Matches FUTURE12 Phase
109
+ * 4's locked behavior for `replaced_by_newer`.
110
+ */
111
+ export async function resolveReplaced(opts) {
112
+ const { ctx, app, replacedDeployment } = opts;
113
+ // Filter the lookup to the same deploy kind. A `replaced` row is
114
+ // always a live deploy in the API (both markPreviousLiveAsReplaced
115
+ // and markLiveDeployReplaced enforce eq(deployments.type, "live")),
116
+ // so without `type` filtering a newer preview deploy could hijack
117
+ // recovery — the resolver might wait on the preview, surface a
118
+ // preview URL as if it were the canonical live, or fall back to
119
+ // ask_user when previews push the real successor past the limit.
120
+ // Default to "live" when the row's type is missing for any reason.
121
+ const deployType = replacedDeployment.type ?? "live";
122
+ let latest;
123
+ try {
124
+ const deploys = await ctx.client.apps.listDeploys(app.name, {
125
+ limit: 5,
126
+ type: deployType,
127
+ });
128
+ // Skip the row we already know is replaced; pick the most recent
129
+ // deploy that's not this one. The list is ordered desc by createdAt.
130
+ latest = deploys.find((d) => d.id !== replacedDeployment.id);
131
+ }
132
+ catch {
133
+ return {
134
+ recovery: recoveryAsk({
135
+ prompt: `${app.name} deploy ${replacedDeployment.id} was replaced, but listing recent deploys failed. Check the dashboard or run \`percher doctor --app ${app.name}\` to inspect current state.`,
136
+ reasonCode: "replaced_by_newer",
137
+ }),
138
+ summary: `${app.name} deploy ${replacedDeployment.id} was replaced, but listing recent deploys failed. Surface to user — they can check the dashboard.`,
139
+ };
140
+ }
141
+ if (!latest) {
142
+ return {
143
+ recovery: recoveryAsk({
144
+ prompt: `${app.name} deploy ${replacedDeployment.id} was replaced and there are no other deploys to resolve to. Surface this to the user — they may need to redeploy.`,
145
+ reasonCode: "replaced_by_newer",
146
+ }),
147
+ summary: `${app.name} deploy ${replacedDeployment.id} was replaced and there are no other deploys to resolve to. Surface to user.`,
148
+ };
149
+ }
150
+ if (latest.status === "live") {
151
+ const url = latest.previewUrl ?? latest.url ?? app.url;
152
+ return {
153
+ recovery: recoveryNone({ reasonCode: "replaced_by_newer", url }),
154
+ url,
155
+ resolvedDeployment: latest,
156
+ summary: `${app.name} deploy ${replacedDeployment.id} was replaced — the app is live at ${url} via deploy ${latest.id}.`,
157
+ };
158
+ }
159
+ if (latest.status === "queued" || latest.status === "building" || latest.status === "deploying") {
160
+ const reasonCode = latest.status === "queued"
161
+ ? "deploy_queued"
162
+ : latest.status === "building"
163
+ ? "deploy_building"
164
+ : "deploy_deploying";
165
+ return {
166
+ recovery: recoveryWait({ app: app.name, deployId: latest.id, reasonCode }),
167
+ resolvedDeployment: latest,
168
+ summary: `${app.name} deploy ${replacedDeployment.id} was replaced; the newer deploy ${latest.id} is ${latest.status} — call percher_wait_for_deploy.`,
169
+ };
170
+ }
171
+ // latest is failed or another replaced — there's no clear current
172
+ // live state. Surface to user; FUTURE12's expanded doctor will own
173
+ // deeper diagnostics.
174
+ return {
175
+ recovery: recoveryAsk({
176
+ prompt: `${app.name} deploy ${replacedDeployment.id} was replaced, and the latest deploy ${latest.id} is also ${latest.status}. Run \`percher doctor --app ${app.name}\` to diagnose, or surface this to the user.`,
177
+ reasonCode: "replaced_by_newer",
178
+ }),
179
+ resolvedDeployment: latest,
180
+ summary: `${app.name} deploy ${replacedDeployment.id} was replaced; the latest deploy ${latest.id} is ${latest.status}. Surface to user.`,
181
+ };
182
+ }
183
+ async function buildReplacedResult(opts) {
184
+ const { ctx, app, deployment, startedAt } = opts;
185
+ const resolved = await resolveReplaced({ ctx, app, replacedDeployment: deployment });
186
+ return {
187
+ status: "replaced",
188
+ app: { id: app.id, name: app.name, url: app.url },
189
+ deployment,
190
+ url: resolved.url,
191
+ elapsedSeconds: Math.round((Date.now() - startedAt) / 1000),
192
+ totalElapsedSeconds: secondsSinceCreated(deployment),
193
+ recovery: resolved.recovery,
194
+ summary: resolved.summary,
195
+ };
196
+ }
197
+ function buildStillRunningResult(app, deployment, startedAt, networkHint) {
198
+ return {
199
+ status: "still_running",
200
+ app: { id: app.id, name: app.name, url: app.url },
201
+ deployment,
202
+ elapsedSeconds: Math.round((Date.now() - startedAt) / 1000),
203
+ totalElapsedSeconds: secondsSinceCreated(deployment),
204
+ recovery: recoveryWait({
205
+ app: app.name,
206
+ deployId: deployment.id,
207
+ reasonCode: deployment.status === "queued"
208
+ ? "deploy_queued"
209
+ : deployment.status === "building"
210
+ ? "deploy_building"
211
+ : deployment.status === "deploying"
212
+ ? "deploy_deploying"
213
+ : "deploy_timeout",
214
+ }),
215
+ summary: networkHint
216
+ ? `${app.name} deploy ${deployment.id} still ${deployment.status} (transient: ${networkHint}) — call percher_wait_for_deploy again.`
217
+ : `${app.name} deploy ${deployment.id} still ${deployment.status} — call percher_wait_for_deploy again.`,
218
+ };
219
+ }
220
+ function secondsSinceCreated(deployment) {
221
+ const t = Date.parse(deployment.createdAt);
222
+ if (Number.isNaN(t))
223
+ return 0;
224
+ return Math.max(0, Math.round((Date.now() - t) / 1000));
225
+ }
226
+ //# sourceMappingURL=wait-deploy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wait-deploy.js","sourceRoot":"","sources":["../../src/commands/wait-deploy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * A single env reference discovered by the source scanner.
3
+ *
4
+ * The scanner inspects an extracted upload tarball before queueing a deploy.
5
+ * Phase 6c uses these references to enforce the `[env]` contract in
6
+ * `percher.toml`: any static key not declared in `required`, `optional`, or
7
+ * `ignore` blocks the deploy with a `fix_config` problem.
8
+ */
9
+ export interface EnvReference {
10
+ /** The env key referenced. Empty string when `shape` is `"dynamic"`. */
11
+ key: string;
12
+ /** File path relative to the source root (forward slashes). */
13
+ file: string;
14
+ /** 1-based line number where the reference appears. */
15
+ line: number;
16
+ /** Verbatim source line (trimmed) for diagnostics. */
17
+ context: string;
18
+ /** Static (literal key) vs dynamic (e.g. `process.env[varName]`). */
19
+ shape: "static" | "dynamic";
20
+ }
21
+ export interface ScanOptions {
22
+ /** Optional list of relative paths to skip on top of the built-in defaults. */
23
+ exclude?: string[];
24
+ /**
25
+ * Files larger than this in bytes are skipped silently (default 1 MiB).
26
+ * Prevents pathological inputs (minified bundles, huge JSON blobs) from
27
+ * dominating scan time.
28
+ */
29
+ maxFileBytes?: number;
30
+ }
31
+ /**
32
+ * Recursively scans `rootDir` for env references in supported source files.
33
+ *
34
+ * Read-only: never modifies the source tree. Tolerant of unreadable files and
35
+ * directories — they are skipped silently. Files exceeding `maxFileBytes` are
36
+ * skipped to bound runtime on pathological inputs (minified bundles, etc.).
37
+ */
38
+ export declare function scanSourceForEnvRefs(rootDir: string, opts?: ScanOptions): Promise<EnvReference[]>;
39
+ //# sourceMappingURL=env-scan-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-scan-source.d.ts","sourceRoot":"","sources":["../src/env-scan-source.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,wEAAwE;IACxE,GAAG,EAAE,MAAM,CAAC;IACZ,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,KAAK,EAAE,QAAQ,GAAG,SAAS,CAAC;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA6QD;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,WAAW,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC,CA+CzB"}