@percher/core 0.2.6 → 0.3.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 (206) 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 +1 -1
  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 +77 -23
  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 +63 -1
  54. package/dist/commands/doctor.d.ts.map +1 -1
  55. package/dist/commands/doctor.js +792 -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 +142 -0
  110. package/dist/commands/publish-failure.js.map +1 -0
  111. package/dist/commands/publish-node.d.ts +13 -0
  112. package/dist/commands/publish-node.d.ts.map +1 -0
  113. package/dist/commands/publish-node.js +38 -0
  114. package/dist/commands/publish-node.js.map +1 -0
  115. package/dist/commands/publish.d.ts +87 -3
  116. package/dist/commands/publish.d.ts.map +1 -1
  117. package/dist/commands/publish.js +589 -156
  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 +215 -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 +417 -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 +20 -8
  148. package/dist/commands/rollback.d.ts.map +1 -1
  149. package/dist/commands/rollback.js +11 -6
  150. package/dist/commands/rollback.js.map +1 -1
  151. package/dist/commands/unsuspend.d.ts +35 -0
  152. package/dist/commands/unsuspend.d.ts.map +1 -0
  153. package/dist/commands/unsuspend.js +27 -0
  154. package/dist/commands/unsuspend.js.map +1 -0
  155. package/dist/commands/versions.d.ts +1 -1
  156. package/dist/commands/versions.d.ts.map +1 -1
  157. package/dist/commands/versions.js +1 -1
  158. package/dist/commands/versions.js.map +1 -1
  159. package/dist/commands/wait-deploy.d.ts +92 -0
  160. package/dist/commands/wait-deploy.d.ts.map +1 -0
  161. package/dist/commands/wait-deploy.js +225 -0
  162. package/dist/commands/wait-deploy.js.map +1 -0
  163. package/dist/env-scan-source.d.ts +39 -0
  164. package/dist/env-scan-source.d.ts.map +1 -0
  165. package/dist/env-scan-source.js +332 -0
  166. package/dist/env-scan-source.js.map +1 -0
  167. package/dist/error-classifier.d.ts.map +1 -1
  168. package/dist/error-classifier.js +67 -4
  169. package/dist/error-classifier.js.map +1 -1
  170. package/dist/errors.d.ts +8 -1
  171. package/dist/errors.d.ts.map +1 -1
  172. package/dist/errors.js +2 -0
  173. package/dist/errors.js.map +1 -1
  174. package/dist/index.d.ts +14 -1
  175. package/dist/index.d.ts.map +1 -1
  176. package/dist/index.js +13 -0
  177. package/dist/index.js.map +1 -1
  178. package/dist/plans.d.ts +11 -0
  179. package/dist/plans.d.ts.map +1 -1
  180. package/dist/plans.js +10 -0
  181. package/dist/plans.js.map +1 -1
  182. package/dist/poll-deployment.d.ts +47 -0
  183. package/dist/poll-deployment.d.ts.map +1 -0
  184. package/dist/poll-deployment.js +57 -0
  185. package/dist/poll-deployment.js.map +1 -0
  186. package/dist/recovery.d.ts +356 -0
  187. package/dist/recovery.d.ts.map +1 -0
  188. package/dist/recovery.js +299 -0
  189. package/dist/recovery.js.map +1 -0
  190. package/dist/stream-utils.d.ts +21 -0
  191. package/dist/stream-utils.d.ts.map +1 -0
  192. package/dist/stream-utils.js +41 -0
  193. package/dist/stream-utils.js.map +1 -0
  194. package/dist/templates/ai-files/claude-md.d.ts +7 -0
  195. package/dist/templates/ai-files/claude-md.d.ts.map +1 -0
  196. package/dist/templates/ai-files/claude-md.js +78 -0
  197. package/dist/templates/ai-files/claude-md.js.map +1 -0
  198. package/dist/templates/ai-files/cursor-percher-mdc.d.ts +7 -0
  199. package/dist/templates/ai-files/cursor-percher-mdc.d.ts.map +1 -0
  200. package/dist/templates/ai-files/cursor-percher-mdc.js +111 -0
  201. package/dist/templates/ai-files/cursor-percher-mdc.js.map +1 -0
  202. package/dist/templates/ai-files/index.d.ts +8 -0
  203. package/dist/templates/ai-files/index.d.ts.map +1 -0
  204. package/dist/templates/ai-files/index.js +4 -0
  205. package/dist/templates/ai-files/index.js.map +1 -0
  206. package/package.json +5 -5
@@ -0,0 +1,211 @@
1
+ import { mkdtempSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { PercherApiError } from "@percher/client";
5
+ import { z } from "zod/v3";
6
+ import { readPercherTomlAppName } from "../app-name";
7
+ export const reproduceInputSchema = z.object({
8
+ app: z
9
+ .string()
10
+ .optional()
11
+ .describe("App name. Defaults to the [app].name in the current directory's percher.toml."),
12
+ deployId: z
13
+ .string()
14
+ .describe("Deploy id (e.g. dep_abc123) to reproduce. Required — usually the failed deploy you want to debug."),
15
+ });
16
+ /**
17
+ * FUTURE1 Phase 4.2 — download a deploy's source tarball + (optionally)
18
+ * run nixpacks against it locally. The intended use is "build failed
19
+ * in cloud, reproduce here to fix" — surfaced via
20
+ * `recovery.alternativeActions[]` after `inspect_build_log` didn't
21
+ * yield a clear fix.
22
+ *
23
+ * Privacy invariant: env values are NEVER fetched. The endpoint
24
+ * surfaces only env *keys* so the user knows what to set locally; the
25
+ * values come from their own machine (or stay missing if compile-only
26
+ * errors are what we're debugging).
27
+ */
28
+ export async function reproduce(ctx, input) {
29
+ const appName = input.app ?? readPercherTomlAppName(ctx.cwd);
30
+ if (!appName) {
31
+ return {
32
+ status: "failed",
33
+ deployId: input.deployId,
34
+ error: {
35
+ title: "App name required",
36
+ explanation: "reproduce needs either an explicit `app` argument or a percher.toml in the current directory.",
37
+ suggestion: "Pass --app=<name>, or cd into a project that has a percher.toml.",
38
+ },
39
+ summary: "App name required.",
40
+ };
41
+ }
42
+ let app;
43
+ try {
44
+ app = await ctx.client.apps.get(appName);
45
+ }
46
+ catch (err) {
47
+ if (err instanceof PercherApiError) {
48
+ if (err.status === 401) {
49
+ return {
50
+ status: "needs_login",
51
+ deployId: input.deployId,
52
+ summary: "Not logged in — run `bunx percher login` first.",
53
+ };
54
+ }
55
+ if (err.status === 404) {
56
+ return {
57
+ status: "failed",
58
+ deployId: input.deployId,
59
+ error: {
60
+ title: "App not found",
61
+ explanation: `Could not resolve app "${appName}".`,
62
+ suggestion: "Check the app name with `bunx percher whoami` or your dashboard.",
63
+ },
64
+ summary: "App not found.",
65
+ };
66
+ }
67
+ }
68
+ throw err;
69
+ }
70
+ ctx.status(`Downloading source for ${input.deployId}...`);
71
+ let bytes;
72
+ try {
73
+ bytes = await ctx.client.apps.downloadSource(app.id, input.deployId);
74
+ }
75
+ catch (err) {
76
+ if (err instanceof PercherApiError && err.code === "TARBALL_GONE") {
77
+ return {
78
+ status: "failed",
79
+ appName: app.name,
80
+ deployId: input.deployId,
81
+ error: {
82
+ title: "Source has been retention-evicted",
83
+ explanation: "Percher only keeps deploy tarballs for a bounded window per plan; this deploy's source is gone.",
84
+ suggestion: "Run `bunx percher publish` to upload a fresh source, then reproduce that deploy in the future.",
85
+ },
86
+ summary: "Source tarball is gone.",
87
+ };
88
+ }
89
+ if (err instanceof PercherApiError && err.status === 404) {
90
+ return {
91
+ status: "failed",
92
+ appName: app.name,
93
+ deployId: input.deployId,
94
+ error: {
95
+ title: "Deploy not found",
96
+ explanation: `No deploy with id ${input.deployId} on app ${app.name}.`,
97
+ suggestion: "Check the id with `bunx percher deploys list`.",
98
+ },
99
+ summary: "Deploy not found.",
100
+ };
101
+ }
102
+ throw err;
103
+ }
104
+ // Privacy: fetch env KEYS only, never values. The endpoint we hit
105
+ // here returns masked values; we discard them and pass keys to the
106
+ // user so they know what to set locally. Failures are non-fatal —
107
+ // the source extraction is the load-bearing artifact.
108
+ // env.list returns `Record<string, string>` (key → masked value)
109
+ // for the CURRENT app env, not a snapshot from the failed deploy.
110
+ // Percher doesn't persist a per-deploy env snapshot today (the
111
+ // image-cache hash includes them but the values aren't stored). We
112
+ // discard the values; only the keys reach the user — privacy
113
+ // invariant on Phase 4.2 — and the result+CLI text both label this
114
+ // as "current app" so a user who rotated keys between failure and
115
+ // reproduce isn't misled into chasing the wrong missing var. Codex
116
+ // P2 follow-up on 3b609f9.
117
+ let currentAppEnvKeys = [];
118
+ try {
119
+ const envMap = await ctx.client.env.list(app.id);
120
+ currentAppEnvKeys = Object.keys(envMap).sort();
121
+ }
122
+ catch {
123
+ /* env list unavailable; reproduce can still proceed */
124
+ }
125
+ // Write to tmp + extract. Use system `tar` because:
126
+ // - macOS/Linux ship it
127
+ // - Windows 10+ has `tar.exe` since 1803 (April 2018)
128
+ // - Bun.spawn streams correctly across platforms
129
+ // Fall back to "saved tarball, extract manually" if tar isn't
130
+ // available — the user keeps the bytes either way.
131
+ const tmpDir = mkdtempSync(join(tmpdir(), `percher-reproduce-${input.deployId}-`));
132
+ const tarballPath = join(tmpDir, "source.tar.gz");
133
+ writeFileSync(tarballPath, bytes);
134
+ let extracted = false;
135
+ try {
136
+ const proc = Bun.spawn(["tar", "-xzf", tarballPath, "-C", tmpDir], {
137
+ stdout: "pipe",
138
+ stderr: "pipe",
139
+ });
140
+ const exitCode = await proc.exited;
141
+ extracted = exitCode === 0;
142
+ }
143
+ catch {
144
+ /* tar not on PATH — fall through */
145
+ }
146
+ const envHint = currentAppEnvKeys.length > 0
147
+ ? `Current app env keys (NOT a deploy-time snapshot): ${currentAppEnvKeys.join(", ")}`
148
+ : "No env vars currently configured on the app.";
149
+ if (!extracted) {
150
+ ctx.status(`Source saved to ${tarballPath}`);
151
+ return {
152
+ status: "extracted",
153
+ appName: app.name,
154
+ deployId: input.deployId,
155
+ tmpDir,
156
+ tarballPath,
157
+ currentAppEnvKeys,
158
+ summary: `Source saved to ${tarballPath}. Extract with: tar -xzf ${tarballPath} -C ${tmpDir}`,
159
+ };
160
+ }
161
+ ctx.status(`Extracted to ${tmpDir}`);
162
+ // Try nixpacks if installed — best-effort. If it's not on PATH OR
163
+ // returns non-zero, the user is left with the extracted source and
164
+ // a clear next step (run their own build command). builtLocally
165
+ // tracks the outcome for the agent's recovery loop.
166
+ let builtLocally = false;
167
+ let nixpacksAvailable = false;
168
+ try {
169
+ const which = Bun.spawn(["nixpacks", "--version"], { stdout: "pipe", stderr: "pipe" });
170
+ const code = await which.exited;
171
+ nixpacksAvailable = code === 0;
172
+ }
173
+ catch {
174
+ /* not installed */
175
+ }
176
+ if (nixpacksAvailable) {
177
+ ctx.status("Running nixpacks build locally...");
178
+ try {
179
+ const build = Bun.spawn(["nixpacks", "build", tmpDir, "--name", `repro-${input.deployId}`], {
180
+ cwd: tmpDir,
181
+ stdout: "inherit",
182
+ stderr: "inherit",
183
+ });
184
+ const code = await build.exited;
185
+ builtLocally = code === 0;
186
+ }
187
+ catch {
188
+ /* spawn failed despite version check passing — rare but tolerable */
189
+ }
190
+ }
191
+ // Codex P2 follow-up on 3b609f9: pin the redeploy suggestion to
192
+ // THIS deploy id, not the bare `redeploy` form. Without it,
193
+ // `bunx percher redeploy` resolves to the LATEST LIVE deploy —
194
+ // typically the previous successful one, not the failed source the
195
+ // user just reproduced. That'd retry the wrong source entirely.
196
+ const buildHint = builtLocally
197
+ ? `Build succeeded locally — the cloud failure may have been infra-flake. Retry the same source server-side: \`bunx percher redeploy ${input.deployId} --app ${app.name}\``
198
+ : nixpacksAvailable
199
+ ? "nixpacks build also failed locally — same error as cloud. Iterate on the source in the tmp dir, then `bunx percher publish` from the project root once fixed."
200
+ : `nixpacks not on PATH. Install from https://nixpacks.com to reproduce the build, or run your own build command in ${tmpDir}.`;
201
+ return {
202
+ status: builtLocally ? "extracted_and_built" : "extracted",
203
+ appName: app.name,
204
+ deployId: input.deployId,
205
+ tmpDir,
206
+ currentAppEnvKeys,
207
+ builtLocally,
208
+ summary: `Source extracted to ${tmpDir}. ${envHint} ${buildHint}`,
209
+ };
210
+ }
211
+ //# sourceMappingURL=reproduce.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reproduce.js","sourceRoot":"","sources":["../../src/commands/reproduce.ts"],"names":[],"mappings":"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"}
@@ -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 resetSuperuserInputSchema: z.ZodObject<{
4
4
  app: z.ZodOptional<z.ZodString>;
@@ -1 +1 @@
1
- {"version":3,"file":"reset-superuser.d.ts","sourceRoot":"","sources":["../../src/commands/reset-superuser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,eAAO,MAAM,yBAAyB;;IAEpC;;;;;;;OAOG;;;;;;;;EAEH,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,OAAO,EACZ,KAAK,GAAE,mBAAwB,GAC9B,OAAO,CAAC,oBAAoB,CAAC,CAS/B"}
1
+ {"version":3,"file":"reset-superuser.d.ts","sourceRoot":"","sources":["../../src/commands/reset-superuser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,eAAO,MAAM,yBAAyB;;IAEpC;;;;;;;OAOG;;;;;;;;EAEH,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,OAAO,EACZ,KAAK,GAAE,mBAAwB,GAC9B,OAAO,CAAC,oBAAoB,CAAC,CAS/B"}
@@ -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 resetSuperuserInputSchema = z.object({
@@ -1 +1 @@
1
- {"version":3,"file":"reset-superuser.js","sourceRoot":"","sources":["../../src/commands/reset-superuser.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,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChD,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1B;;;;;;;OAOG;IACH,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CAC/B,CAAC,CAAC;AAUH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,GAAY,EACZ,QAA6B,EAAE;IAE/B,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,IAAI,sBAAsB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5D,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,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,cAAc,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;AAC1E,CAAC"}
1
+ {"version":3,"file":"reset-superuser.js","sourceRoot":"","sources":["../../src/commands/reset-superuser.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,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChD,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1B;;;;;;;OAOG;IACH,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CAC/B,CAAC,CAAC;AAUH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,GAAY,EACZ,QAA6B,EAAE;IAE/B,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,IAAI,sBAAsB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5D,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,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,cAAc,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;AAC1E,CAAC"}
@@ -0,0 +1,79 @@
1
+ import type { BackupSidecar, RestoreJob } from "@percher/client";
2
+ import { z } from "zod/v3";
3
+ import type { Context } from "../context";
4
+ /**
5
+ * Per-app restore (B.2 — CLI/MCP entry point on top of B.1.4).
6
+ *
7
+ * Two surfaces:
8
+ * - listBackups(ctx, input) → resolved sidecar list, no mutation
9
+ * - restore(ctx, input) → kicks off + polls until terminal
10
+ *
11
+ * `--dry-run`: validates source resolves to a backup the user actually
12
+ * has + lists what would be restored. Doesn't touch the API beyond
13
+ * GET /apps/:app/backups. Equivalent to "show me what restore <date>
14
+ * would do" — the documented v1 affordance from plan-doc B.2.
15
+ */
16
+ export declare const listBackupsInputSchema: z.ZodObject<{
17
+ app: z.ZodOptional<z.ZodString>;
18
+ }, "strip", z.ZodTypeAny, {
19
+ app?: string | undefined;
20
+ }, {
21
+ app?: string | undefined;
22
+ }>;
23
+ export type ListBackupsInput = z.infer<typeof listBackupsInputSchema>;
24
+ export declare function listBackups(ctx: Context, input: ListBackupsInput): Promise<{
25
+ backups: BackupSidecar[];
26
+ skipped: Array<{
27
+ filename: string;
28
+ reason: string;
29
+ }>;
30
+ }>;
31
+ /**
32
+ * Public input — what shows up in MCP `inputSchema` for AI assistants
33
+ * + on CLI flags. Only fields a real caller should set live here. Test
34
+ * + internal injection (poll cadence, timeout overrides) goes via the
35
+ * second `RestoreInternals` parameter so they don't leak into the
36
+ * public schema. Pre-fix had `_pollIntervalMs`/`_timeoutMs` on this
37
+ * schema with underscore-prefixes — visible to MCP, confusing for AI
38
+ * tooling that didn't know "_" meant "test-only".
39
+ */
40
+ export declare const restoreInputSchema: z.ZodObject<{
41
+ app: z.ZodOptional<z.ZodString>;
42
+ /** "latest" | "YYYY-MM-DD" | "<backup_id>" — passed through to API. */
43
+ source: z.ZodString;
44
+ /** Defaults to ["appdata", "pocketbase"] when undefined — matches API. */
45
+ components: z.ZodOptional<z.ZodArray<z.ZodEnum<["appdata", "pocketbase"]>, "many">>;
46
+ /** Validate source + show what would happen, but don't actually start
47
+ * the restore. */
48
+ dryRun: z.ZodDefault<z.ZodBoolean>;
49
+ }, "strip", z.ZodTypeAny, {
50
+ dryRun: boolean;
51
+ source: string;
52
+ app?: string | undefined;
53
+ components?: ("pocketbase" | "appdata")[] | undefined;
54
+ }, {
55
+ source: string;
56
+ app?: string | undefined;
57
+ dryRun?: boolean | undefined;
58
+ components?: ("pocketbase" | "appdata")[] | undefined;
59
+ }>;
60
+ export type RestoreInput = z.infer<typeof restoreInputSchema>;
61
+ /**
62
+ * Internal-only knobs for tests + future automation. NOT part of the
63
+ * Zod schema → not surfaced to MCP / CLI users. Production callers
64
+ * pass nothing; tests pass small intervals so they don't sleep.
65
+ */
66
+ export interface RestoreInternals {
67
+ /** Poll cadence between status checks (ms). Default 2000. */
68
+ pollIntervalMs?: number;
69
+ /** Hard cap before throwing restore.timeout (ms). Default 35 min. */
70
+ timeoutMs?: number;
71
+ }
72
+ export interface DryRunResult {
73
+ kind: "dry-run";
74
+ app: string;
75
+ resolvedBackup: BackupSidecar;
76
+ components: ("appdata" | "pocketbase")[];
77
+ }
78
+ export declare function restore(ctx: Context, input: RestoreInput, internals?: RestoreInternals): Promise<RestoreJob | DryRunResult>;
79
+ //# sourceMappingURL=restore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"restore.d.ts","sourceRoot":"","sources":["../../src/commands/restore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,sBAAsB;;;;;;EAEjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,wBAAsB,WAAW,CAC/B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,gBAAgB,GACtB,OAAO,CAAC;IAAE,OAAO,EAAE,aAAa,EAAE,CAAC;IAAC,OAAO,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAAC,CAS7F;AAID;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB;;IAE7B,uEAAuE;;IAEvE,0EAA0E;;IAE1E;uBACmB;;;;;;;;;;;;EAEnB,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,6DAA6D;IAC7D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,SAAS,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,aAAa,CAAC;IAC9B,UAAU,EAAE,CAAC,SAAS,GAAG,YAAY,CAAC,EAAE,CAAC;CAC1C;AAED,wBAAsB,OAAO,CAC3B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,YAAY,EACnB,SAAS,GAAE,gBAAqB,GAC/B,OAAO,CAAC,UAAU,GAAG,YAAY,CAAC,CAiFpC"}
@@ -0,0 +1,164 @@
1
+ import { z } from "zod/v3";
2
+ import { readPercherTomlAppName } from "../app-name";
3
+ import { PercherCoreError } from "../errors";
4
+ /**
5
+ * Per-app restore (B.2 — CLI/MCP entry point on top of B.1.4).
6
+ *
7
+ * Two surfaces:
8
+ * - listBackups(ctx, input) → resolved sidecar list, no mutation
9
+ * - restore(ctx, input) → kicks off + polls until terminal
10
+ *
11
+ * `--dry-run`: validates source resolves to a backup the user actually
12
+ * has + lists what would be restored. Doesn't touch the API beyond
13
+ * GET /apps/:app/backups. Equivalent to "show me what restore <date>
14
+ * would do" — the documented v1 affordance from plan-doc B.2.
15
+ */
16
+ export const listBackupsInputSchema = z.object({
17
+ app: z.string().optional(),
18
+ });
19
+ export async function listBackups(ctx, input) {
20
+ const name = input.app ?? readPercherTomlAppName(ctx.cwd);
21
+ if (!name) {
22
+ throw new PercherCoreError("No app specified and no percher.toml found", {
23
+ code: "logs.no-app",
24
+ hint: "Pass --app <name> or run from a directory with percher.toml.",
25
+ });
26
+ }
27
+ return ctx.client.apps.listBackups(name);
28
+ }
29
+ const RESTORE_COMPONENTS = ["appdata", "pocketbase"];
30
+ /**
31
+ * Public input — what shows up in MCP `inputSchema` for AI assistants
32
+ * + on CLI flags. Only fields a real caller should set live here. Test
33
+ * + internal injection (poll cadence, timeout overrides) goes via the
34
+ * second `RestoreInternals` parameter so they don't leak into the
35
+ * public schema. Pre-fix had `_pollIntervalMs`/`_timeoutMs` on this
36
+ * schema with underscore-prefixes — visible to MCP, confusing for AI
37
+ * tooling that didn't know "_" meant "test-only".
38
+ */
39
+ export const restoreInputSchema = z.object({
40
+ app: z.string().optional(),
41
+ /** "latest" | "YYYY-MM-DD" | "<backup_id>" — passed through to API. */
42
+ source: z.string().min(1),
43
+ /** Defaults to ["appdata", "pocketbase"] when undefined — matches API. */
44
+ components: z.array(z.enum(RESTORE_COMPONENTS)).optional(),
45
+ /** Validate source + show what would happen, but don't actually start
46
+ * the restore. */
47
+ dryRun: z.boolean().default(false),
48
+ });
49
+ export async function restore(ctx, input, internals = {}) {
50
+ const name = input.app ?? readPercherTomlAppName(ctx.cwd);
51
+ if (!name) {
52
+ throw new PercherCoreError("No app specified and no percher.toml found", {
53
+ code: "logs.no-app",
54
+ hint: "Pass --app <name> or run from a directory with percher.toml.",
55
+ });
56
+ }
57
+ if (input.dryRun) {
58
+ // Resolve source against listBackups so we can show what WOULD restore.
59
+ // Trust the API's source-resolution logic — don't reimplement
60
+ // "latest"/date/id matching here. Hits the same logic the worker
61
+ // /restore/inspect would; just no GPG decrypt + no orchestration.
62
+ const { backups } = await ctx.client.apps.listBackups(name);
63
+ const resolved = resolveSource(backups, input.source);
64
+ if (!resolved) {
65
+ throw new PercherCoreError(`No backup matches "${input.source}" for app "${name}"`, {
66
+ code: "restore.source-not-found",
67
+ hint: 'Try "latest", a date "YYYY-MM-DD", or a full backup id.',
68
+ });
69
+ }
70
+ return {
71
+ kind: "dry-run",
72
+ app: name,
73
+ resolvedBackup: resolved,
74
+ components: input.components ?? ["appdata", "pocketbase"],
75
+ };
76
+ }
77
+ let job = await ctx.client.apps.startRestore(name, {
78
+ source: input.source,
79
+ components: input.components,
80
+ });
81
+ const pollInterval = internals.pollIntervalMs ?? 2000;
82
+ // 35 min default — generous enough for big PB volumes (typically 30
83
+ // min worker watchdog cleans up orphans, so we want CLI to stay alive
84
+ // long enough that "timed out" never fires when the actual job is
85
+ // still running). Override via internals.timeoutMs in tests.
86
+ const timeoutMs = internals.timeoutMs ?? 35 * 60 * 1000;
87
+ const startedAt = Date.now();
88
+ let lastStatus = job.status;
89
+ const TERMINAL = ["done", "rolled_back", "failed"];
90
+ while (!TERMINAL.includes(job.status)) {
91
+ if (Date.now() - startedAt > timeoutMs) {
92
+ throw new PercherCoreError("Restore timed out from the CLI's perspective. The job may still be running on the server — check the dashboard or `percher backups` to confirm before retrying.", {
93
+ code: "restore.timeout",
94
+ hint: "If the job is genuinely stuck, the watchdog will mark it failed within 5 min so you can retry from a clean slate.",
95
+ });
96
+ }
97
+ if (job.status !== lastStatus) {
98
+ ctx.status(`${humanizeStatus(job.status)}...`);
99
+ lastStatus = job.status;
100
+ }
101
+ await new Promise((r) => setTimeout(r, pollInterval));
102
+ job = await ctx.client.apps.getRestoreJob(name, job.id);
103
+ }
104
+ if (job.status === "failed") {
105
+ throw new PercherCoreError(`Restore failed: ${job.errorMessage ?? "unknown error"}`, {
106
+ code: "restore.failed",
107
+ hint: "Run `percher logs` or check the dashboard for details.",
108
+ });
109
+ }
110
+ if (job.status === "rolled_back") {
111
+ throw new PercherCoreError(`Restore rolled back: ${job.errorMessage ?? "an error during restore was caught and undone"}`, {
112
+ code: "restore.rolled-back",
113
+ hint: "Your app is back to its pre-restore state. Check logs for what failed.",
114
+ });
115
+ }
116
+ return job;
117
+ }
118
+ /**
119
+ * Match a `source` spec ("latest" | "YYYY-MM-DD" | "<id>") against the
120
+ * sidecar list. Mirrors the API's resolveBackup logic but on the
121
+ * sidecar projection instead of file metadata. Used only by --dry-run.
122
+ */
123
+ function resolveSource(backups, source) {
124
+ if (backups.length === 0)
125
+ return null;
126
+ const sorted = [...backups].sort((a, b) => b.backup_id.localeCompare(a.backup_id));
127
+ if (source === "latest")
128
+ return sorted[0] ?? null;
129
+ if (/^\d{4}-\d{2}-\d{2}$/.test(source)) {
130
+ // Reject malformed dates. Two cases:
131
+ // 1. Hard parse failure ("9999-99-99") — Date.parse returns NaN.
132
+ // 2. Calendar overflow ("2026-02-30") — Date.parse SILENTLY rolls
133
+ // to "2026-03-02". The regex can't tell them apart, and
134
+ // isNaN(parse) returns false because the rolled date is valid.
135
+ // Defense: compare the roundtripped ISO prefix to the input.
136
+ const ts = Date.parse(`${source}T00:00:00Z`);
137
+ if (Number.isNaN(ts))
138
+ return null;
139
+ const roundtrip = new Date(ts).toISOString().slice(0, 10);
140
+ if (roundtrip !== source)
141
+ return null;
142
+ return sorted.find((b) => b.backup_id.startsWith(`${source}_`)) ?? null;
143
+ }
144
+ return sorted.find((b) => b.backup_id === source) ?? null;
145
+ }
146
+ function humanizeStatus(status) {
147
+ switch (status) {
148
+ case "preparing":
149
+ return "Preparing";
150
+ case "quiescing":
151
+ return "Stopping containers";
152
+ case "restoring_data":
153
+ return "Restoring data";
154
+ case "starting":
155
+ return "Starting containers";
156
+ case "verifying":
157
+ return "Verifying health";
158
+ case "rolling_back":
159
+ return "Rolling back";
160
+ default:
161
+ return status;
162
+ }
163
+ }
164
+ //# sourceMappingURL=restore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"restore.js","sourceRoot":"","sources":["../../src/commands/restore.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAC3B,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C;;;;;;;;;;;GAWG;AAEH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC3B,CAAC,CAAC;AAGH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,GAAY,EACZ,KAAuB;IAEvB,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,8DAA8D;SACrE,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,kBAAkB,GAAG,CAAC,SAAS,EAAE,YAAY,CAAU,CAAC;AAE9D;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1B,uEAAuE;IACvE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACzB,0EAA0E;IAC1E,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC1D;uBACmB;IACnB,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;CACnC,CAAC,CAAC;AAsBH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,GAAY,EACZ,KAAmB,EACnB,YAA8B,EAAE;IAEhC,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,8DAA8D;SACrE,CAAC,CAAC;IACL,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjB,wEAAwE;QACxE,8DAA8D;QAC9D,iEAAiE;QACjE,kEAAkE;QAClE,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QACtD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,gBAAgB,CAAC,sBAAsB,KAAK,CAAC,MAAM,cAAc,IAAI,GAAG,EAAE;gBAClF,IAAI,EAAE,0BAA0B;gBAChC,IAAI,EAAE,yDAAyD;aAChE,CAAC,CAAC;QACL,CAAC;QACD,OAAO;YACL,IAAI,EAAE,SAAS;YACf,GAAG,EAAE,IAAI;YACT,cAAc,EAAE,QAAQ;YACxB,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC;SAC1D,CAAC;IACJ,CAAC;IAED,IAAI,GAAG,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE;QACjD,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,UAAU,EAAE,KAAK,CAAC,UAAU;KAC7B,CAAC,CAAC;IAEH,MAAM,YAAY,GAAG,SAAS,CAAC,cAAc,IAAI,IAAI,CAAC;IACtD,oEAAoE;IACpE,sEAAsE;IACtE,kEAAkE;IAClE,6DAA6D;IAC7D,MAAM,SAAS,GAAG,SAAS,CAAC,SAAS,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IACxD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,IAAI,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC;IAE5B,MAAM,QAAQ,GAA2B,CAAC,MAAM,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAC;IAE3E,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,SAAS,EAAE,CAAC;YACvC,MAAM,IAAI,gBAAgB,CACxB,iKAAiK,EACjK;gBACE,IAAI,EAAE,iBAAiB;gBACvB,IAAI,EAAE,mHAAmH;aAC1H,CACF,CAAC;QACJ,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YAC9B,GAAG,CAAC,MAAM,CAAC,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC/C,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC;QAC1B,CAAC;QACD,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC;QACtD,GAAG,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI,GAAG,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,IAAI,gBAAgB,CAAC,mBAAmB,GAAG,CAAC,YAAY,IAAI,eAAe,EAAE,EAAE;YACnF,IAAI,EAAE,gBAAgB;YACtB,IAAI,EAAE,wDAAwD;SAC/D,CAAC,CAAC;IACL,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;QACjC,MAAM,IAAI,gBAAgB,CACxB,wBAAwB,GAAG,CAAC,YAAY,IAAI,+CAA+C,EAAE,EAC7F;YACE,IAAI,EAAE,qBAAqB;YAC3B,IAAI,EAAE,wEAAwE;SAC/E,CACF,CAAC;IACJ,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,SAAS,aAAa,CAAC,OAAwB,EAAE,MAAc;IAC7D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,MAAM,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IACnF,IAAI,MAAM,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IAClD,IAAI,qBAAqB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACvC,qCAAqC;QACrC,mEAAmE;QACnE,oEAAoE;QACpE,6DAA6D;QAC7D,oEAAoE;QACpE,kEAAkE;QAClE,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,MAAM,YAAY,CAAC,CAAC;QAC7C,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YAAE,OAAO,IAAI,CAAC;QAClC,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1D,IAAI,SAAS,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC;QACtC,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC;IAC1E,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC;AAC5D,CAAC;AAED,SAAS,cAAc,CAAC,MAA4B;IAClD,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,WAAW;YACd,OAAO,WAAW,CAAC;QACrB,KAAK,WAAW;YACd,OAAO,qBAAqB,CAAC;QAC/B,KAAK,gBAAgB;YACnB,OAAO,gBAAgB,CAAC;QAC1B,KAAK,UAAU;YACb,OAAO,qBAAqB,CAAC;QAC/B,KAAK,WAAW;YACd,OAAO,kBAAkB,CAAC;QAC5B,KAAK,cAAc;YACjB,OAAO,cAAc,CAAC;QACxB;YACE,OAAO,MAAM,CAAC;IAClB,CAAC;AACH,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 resumeInputSchema: z.ZodObject<{
4
4
  app: z.ZodOptional<z.ZodString>;
@@ -1 +1 @@
1
- {"version":3,"file":"resume.d.ts","sourceRoot":"","sources":["../../src/commands/resume.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,eAAO,MAAM,iBAAiB;;;;;;EAE5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,wBAAsB,MAAM,CAC1B,GAAG,EAAE,OAAO,EACZ,KAAK,GAAE,WAAgB,GACtB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAS9C"}
1
+ {"version":3,"file":"resume.d.ts","sourceRoot":"","sources":["../../src/commands/resume.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,eAAO,MAAM,iBAAiB;;;;;;EAE5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,wBAAsB,MAAM,CAC1B,GAAG,EAAE,OAAO,EACZ,KAAK,GAAE,WAAgB,GACtB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAS9C"}
@@ -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 resumeInputSchema = z.object({
@@ -1 +1 @@
1
- {"version":3,"file":"resume.js","sourceRoot":"","sources":["../../src/commands/resume.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;CAC3B,CAAC,CAAC;AAGH,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,GAAY,EACZ,QAAqB,EAAE;IAEvB,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,OAAO,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC"}
1
+ {"version":3,"file":"resume.js","sourceRoot":"","sources":["../../src/commands/resume.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,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC3B,CAAC,CAAC;AAGH,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,GAAY,EACZ,QAAqB,EAAE;IAEvB,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,OAAO,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC"}
@@ -1,22 +1,34 @@
1
1
  import type { Deployment } from "@percher/client";
2
- import { z } from "zod";
2
+ import { z } from "zod/v3";
3
3
  import type { Context } from "../context";
4
+ /**
5
+ * Public input — surfaces in MCP `inputSchema` for AI assistants + on
6
+ * CLI flags. Internal knobs (poll cadence, timeout overrides) used to
7
+ * sit here with `_pollIntervalMs`/`_timeoutMs` underscore-prefix names.
8
+ * Visible to MCP, confusing for AI tooling. Moved to a separate
9
+ * `RollbackInternals` parameter — same shape used by push/restore.
10
+ */
4
11
  export declare const rollbackInputSchema: z.ZodObject<{
5
12
  app: z.ZodOptional<z.ZodString>;
6
13
  commitSha: z.ZodString;
7
- _pollIntervalMs: z.ZodOptional<z.ZodNumber>;
8
- _timeoutMs: z.ZodOptional<z.ZodNumber>;
9
14
  }, "strip", z.ZodTypeAny, {
10
15
  commitSha: string;
11
16
  app?: string | undefined;
12
- _pollIntervalMs?: number | undefined;
13
- _timeoutMs?: number | undefined;
14
17
  }, {
15
18
  commitSha: string;
16
19
  app?: string | undefined;
17
- _pollIntervalMs?: number | undefined;
18
- _timeoutMs?: number | undefined;
19
20
  }>;
20
21
  export type RollbackInput = z.infer<typeof rollbackInputSchema>;
21
- export declare function rollback(ctx: Context, input: RollbackInput): Promise<Deployment>;
22
+ /**
23
+ * Internal-only knobs for tests + future automation. NOT part of the
24
+ * Zod schema → not surfaced to MCP / CLI users. Production callers
25
+ * pass nothing; tests pass small intervals so they don't sleep.
26
+ */
27
+ export interface RollbackInternals {
28
+ /** Poll cadence between deployment status checks (ms). Default 2000. */
29
+ pollIntervalMs?: number;
30
+ /** Hard cap before throwing push.deploy-timeout (ms). Default 5 min. */
31
+ timeoutMs?: number;
32
+ }
33
+ export declare function rollback(ctx: Context, input: RollbackInput, internals?: RollbackInternals): Promise<Deployment>;
22
34
  //# sourceMappingURL=rollback.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rollback.d.ts","sourceRoot":"","sources":["../../src/commands/rollback.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,mBAAmB;;;;;;;;;;;;;;;EAK9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,wBAAsB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,CAkCtF"}
1
+ {"version":3,"file":"rollback.d.ts","sourceRoot":"","sources":["../../src/commands/rollback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;EAG9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,QAAQ,CAC5B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,aAAa,EACpB,SAAS,GAAE,iBAAsB,GAChC,OAAO,CAAC,UAAU,CAAC,CAkCrB"}
@@ -1,13 +1,18 @@
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
+ /**
5
+ * Public input — surfaces in MCP `inputSchema` for AI assistants + on
6
+ * CLI flags. Internal knobs (poll cadence, timeout overrides) used to
7
+ * sit here with `_pollIntervalMs`/`_timeoutMs` underscore-prefix names.
8
+ * Visible to MCP, confusing for AI tooling. Moved to a separate
9
+ * `RollbackInternals` parameter — same shape used by push/restore.
10
+ */
4
11
  export const rollbackInputSchema = z.object({
5
12
  app: z.string().optional(),
6
13
  commitSha: z.string().min(1),
7
- _pollIntervalMs: z.number().int().positive().optional(),
8
- _timeoutMs: z.number().int().positive().optional(),
9
14
  });
10
- export async function rollback(ctx, input) {
15
+ export async function rollback(ctx, input, internals = {}) {
11
16
  const name = input.app ?? readPercherTomlAppName(ctx.cwd);
12
17
  if (!name) {
13
18
  throw new PercherCoreError("No app specified and no percher.toml found", {
@@ -16,8 +21,8 @@ export async function rollback(ctx, input) {
16
21
  });
17
22
  }
18
23
  let deployment = await ctx.client.apps.rollback(name, input.commitSha);
19
- const pollInterval = input._pollIntervalMs ?? 2000;
20
- const timeoutMs = input._timeoutMs ?? 5 * 60 * 1000;
24
+ const pollInterval = internals.pollIntervalMs ?? 2000;
25
+ const timeoutMs = internals.timeoutMs ?? 5 * 60 * 1000;
21
26
  const startedAt = Date.now();
22
27
  while (deployment.status !== "live" && deployment.status !== "failed") {
23
28
  if (Date.now() - startedAt > timeoutMs) {
@@ -1 +1 @@
1
- {"version":3,"file":"rollback.js","sourceRoot":"","sources":["../../src/commands/rollback.ts"],"names":[],"mappings":"AACA,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;IAC1C,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5B,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACvD,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CACnD,CAAC,CAAC;AAGH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,GAAY,EAAE,KAAoB;IAC/D,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,8DAA8D;SACrE,CAAC,CAAC;IACL,CAAC;IACD,IAAI,UAAU,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAEvE,MAAM,YAAY,GAAG,KAAK,CAAC,eAAe,IAAI,IAAI,CAAC;IACnD,MAAM,SAAS,GAAG,KAAK,CAAC,UAAU,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;IACpD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE7B,OAAO,UAAU,CAAC,MAAM,KAAK,MAAM,IAAI,UAAU,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACtE,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,SAAS,EAAE,CAAC;YACvC,MAAM,IAAI,gBAAgB,CAAC,oBAAoB,EAAE;gBAC/C,IAAI,EAAE,qBAAqB;gBAC3B,IAAI,EAAE,0CAA0C;aACjD,CAAC,CAAC;QACL,CAAC;QACD,GAAG,CAAC,MAAM,CAAC,GAAG,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC;QACtC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC;QACtD,UAAU,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,IAAI,UAAU,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACnC,MAAM,IAAI,gBAAgB,CAAC,iBAAiB,EAAE;YAC5C,IAAI,EAAE,oBAAoB;YAC1B,IAAI,EAAE,4CAA4C;SACnD,CAAC,CAAC;IACL,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC"}
1
+ {"version":3,"file":"rollback.js","sourceRoot":"","sources":["../../src/commands/rollback.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAC3B,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAC7B,CAAC,CAAC;AAeH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,GAAY,EACZ,KAAoB,EACpB,YAA+B,EAAE;IAEjC,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,8DAA8D;SACrE,CAAC,CAAC;IACL,CAAC;IACD,IAAI,UAAU,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAEvE,MAAM,YAAY,GAAG,SAAS,CAAC,cAAc,IAAI,IAAI,CAAC;IACtD,MAAM,SAAS,GAAG,SAAS,CAAC,SAAS,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;IACvD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE7B,OAAO,UAAU,CAAC,MAAM,KAAK,MAAM,IAAI,UAAU,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACtE,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,SAAS,EAAE,CAAC;YACvC,MAAM,IAAI,gBAAgB,CAAC,oBAAoB,EAAE;gBAC/C,IAAI,EAAE,qBAAqB;gBAC3B,IAAI,EAAE,0CAA0C;aACjD,CAAC,CAAC;QACL,CAAC;QACD,GAAG,CAAC,MAAM,CAAC,GAAG,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC;QACtC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC;QACtD,UAAU,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,IAAI,UAAU,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACnC,MAAM,IAAI,gBAAgB,CAAC,iBAAiB,EAAE;YAC5C,IAAI,EAAE,oBAAoB;YAC1B,IAAI,EAAE,4CAA4C;SACnD,CAAC,CAAC;IACL,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC"}