@sentropic/h2a-runtime 0.81.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 (253) hide show
  1. package/dist/account-pool.d.ts +168 -0
  2. package/dist/account-pool.d.ts.map +1 -0
  3. package/dist/account-pool.js +385 -0
  4. package/dist/account-pool.js.map +1 -0
  5. package/dist/agents-projection.d.ts +56 -0
  6. package/dist/agents-projection.d.ts.map +1 -0
  7. package/dist/agents-projection.js +82 -0
  8. package/dist/agents-projection.js.map +1 -0
  9. package/dist/attach.d.ts +75 -0
  10. package/dist/attach.d.ts.map +1 -0
  11. package/dist/attach.js +432 -0
  12. package/dist/attach.js.map +1 -0
  13. package/dist/auth-bundle.d.ts +21 -0
  14. package/dist/auth-bundle.d.ts.map +1 -0
  15. package/dist/auth-bundle.js +112 -0
  16. package/dist/auth-bundle.js.map +1 -0
  17. package/dist/auth-diagnostics.d.ts +18 -0
  18. package/dist/auth-diagnostics.d.ts.map +1 -0
  19. package/dist/auth-diagnostics.js +23 -0
  20. package/dist/auth-diagnostics.js.map +1 -0
  21. package/dist/auth-login.d.ts +9 -0
  22. package/dist/auth-login.d.ts.map +1 -0
  23. package/dist/auth-login.js +21 -0
  24. package/dist/auth-login.js.map +1 -0
  25. package/dist/auth-refresh.d.ts +32 -0
  26. package/dist/auth-refresh.d.ts.map +1 -0
  27. package/dist/auth-refresh.js +83 -0
  28. package/dist/auth-refresh.js.map +1 -0
  29. package/dist/auth-tools.d.ts +55 -0
  30. package/dist/auth-tools.d.ts.map +1 -0
  31. package/dist/auth-tools.js +163 -0
  32. package/dist/auth-tools.js.map +1 -0
  33. package/dist/browser-bridge-local.d.ts +27 -0
  34. package/dist/browser-bridge-local.d.ts.map +1 -0
  35. package/dist/browser-bridge-local.js +51 -0
  36. package/dist/browser-bridge-local.js.map +1 -0
  37. package/dist/browser.d.ts +53 -0
  38. package/dist/browser.d.ts.map +1 -0
  39. package/dist/browser.js +61 -0
  40. package/dist/browser.js.map +1 -0
  41. package/dist/conductor-launch.d.ts +179 -0
  42. package/dist/conductor-launch.d.ts.map +1 -0
  43. package/dist/conductor-launch.js +414 -0
  44. package/dist/conductor-launch.js.map +1 -0
  45. package/dist/config.d.ts +182 -0
  46. package/dist/config.d.ts.map +1 -0
  47. package/dist/config.js +334 -0
  48. package/dist/config.js.map +1 -0
  49. package/dist/conv-guard.d.ts +76 -0
  50. package/dist/conv-guard.d.ts.map +1 -0
  51. package/dist/conv-guard.js +131 -0
  52. package/dist/conv-guard.js.map +1 -0
  53. package/dist/convsync.d.ts +24 -0
  54. package/dist/convsync.d.ts.map +1 -0
  55. package/dist/convsync.js +108 -0
  56. package/dist/convsync.js.map +1 -0
  57. package/dist/cred-health.d.ts +96 -0
  58. package/dist/cred-health.d.ts.map +1 -0
  59. package/dist/cred-health.js +170 -0
  60. package/dist/cred-health.js.map +1 -0
  61. package/dist/delegate.d.ts +406 -0
  62. package/dist/delegate.d.ts.map +1 -0
  63. package/dist/delegate.js +739 -0
  64. package/dist/delegate.js.map +1 -0
  65. package/dist/enroll.d.ts +91 -0
  66. package/dist/enroll.d.ts.map +1 -0
  67. package/dist/enroll.js +248 -0
  68. package/dist/enroll.js.map +1 -0
  69. package/dist/fanout.d.ts +63 -0
  70. package/dist/fanout.d.ts.map +1 -0
  71. package/dist/fanout.js +115 -0
  72. package/dist/fanout.js.map +1 -0
  73. package/dist/forward.d.ts +66 -0
  74. package/dist/forward.d.ts.map +1 -0
  75. package/dist/forward.js +141 -0
  76. package/dist/forward.js.map +1 -0
  77. package/dist/gitdiff.d.ts +31 -0
  78. package/dist/gitdiff.d.ts.map +1 -0
  79. package/dist/gitdiff.js +133 -0
  80. package/dist/gitdiff.js.map +1 -0
  81. package/dist/h2a-bridge.d.ts +102 -0
  82. package/dist/h2a-bridge.d.ts.map +1 -0
  83. package/dist/h2a-bridge.js +333 -0
  84. package/dist/h2a-bridge.js.map +1 -0
  85. package/dist/h2a-jobs.d.ts +235 -0
  86. package/dist/h2a-jobs.d.ts.map +1 -0
  87. package/dist/h2a-jobs.js +388 -0
  88. package/dist/h2a-jobs.js.map +1 -0
  89. package/dist/h2a-ping.d.ts +38 -0
  90. package/dist/h2a-ping.d.ts.map +1 -0
  91. package/dist/h2a-ping.js +58 -0
  92. package/dist/h2a-ping.js.map +1 -0
  93. package/dist/index.d.ts +98 -0
  94. package/dist/index.d.ts.map +1 -0
  95. package/dist/index.js +5887 -0
  96. package/dist/index.js.map +1 -0
  97. package/dist/interactive-throttle.d.ts +164 -0
  98. package/dist/interactive-throttle.d.ts.map +1 -0
  99. package/dist/interactive-throttle.js +194 -0
  100. package/dist/interactive-throttle.js.map +1 -0
  101. package/dist/lineage-client.d.ts +77 -0
  102. package/dist/lineage-client.d.ts.map +1 -0
  103. package/dist/lineage-client.js +117 -0
  104. package/dist/lineage-client.js.map +1 -0
  105. package/dist/lineage-lease.d.ts +152 -0
  106. package/dist/lineage-lease.d.ts.map +1 -0
  107. package/dist/lineage-lease.js +303 -0
  108. package/dist/lineage-lease.js.map +1 -0
  109. package/dist/llm-gateway-runtime/accounts.d.ts +48 -0
  110. package/dist/llm-gateway-runtime/accounts.d.ts.map +1 -0
  111. package/dist/llm-gateway-runtime/accounts.js +226 -0
  112. package/dist/llm-gateway-runtime/accounts.js.map +1 -0
  113. package/dist/llm-gateway-runtime/index.d.ts +4 -0
  114. package/dist/llm-gateway-runtime/index.d.ts.map +1 -0
  115. package/dist/llm-gateway-runtime/index.js +58 -0
  116. package/dist/llm-gateway-runtime/index.js.map +1 -0
  117. package/dist/llm-gateway-runtime/model-catalog.d.ts +36 -0
  118. package/dist/llm-gateway-runtime/model-catalog.d.ts.map +1 -0
  119. package/dist/llm-gateway-runtime/model-catalog.js +146 -0
  120. package/dist/llm-gateway-runtime/model-catalog.js.map +1 -0
  121. package/dist/llm-gateway-runtime/proxy-anthropic.d.ts +3 -0
  122. package/dist/llm-gateway-runtime/proxy-anthropic.d.ts.map +1 -0
  123. package/dist/llm-gateway-runtime/proxy-anthropic.js +139 -0
  124. package/dist/llm-gateway-runtime/proxy-anthropic.js.map +1 -0
  125. package/dist/llm-gateway-runtime/proxy-openai.d.ts +117 -0
  126. package/dist/llm-gateway-runtime/proxy-openai.d.ts.map +1 -0
  127. package/dist/llm-gateway-runtime/proxy-openai.js +1132 -0
  128. package/dist/llm-gateway-runtime/proxy-openai.js.map +1 -0
  129. package/dist/llm-gateway-runtime/session-ledger.d.ts +32 -0
  130. package/dist/llm-gateway-runtime/session-ledger.d.ts.map +1 -0
  131. package/dist/llm-gateway-runtime/session-ledger.js +62 -0
  132. package/dist/llm-gateway-runtime/session-ledger.js.map +1 -0
  133. package/dist/llm-gateway-runtime/sticky.d.ts +40 -0
  134. package/dist/llm-gateway-runtime/sticky.d.ts.map +1 -0
  135. package/dist/llm-gateway-runtime/sticky.js +254 -0
  136. package/dist/llm-gateway-runtime/sticky.js.map +1 -0
  137. package/dist/llm-mesh.d.ts +102 -0
  138. package/dist/llm-mesh.d.ts.map +1 -0
  139. package/dist/llm-mesh.js +433 -0
  140. package/dist/llm-mesh.js.map +1 -0
  141. package/dist/migrate-candidates.d.ts +35 -0
  142. package/dist/migrate-candidates.d.ts.map +1 -0
  143. package/dist/migrate-candidates.js +128 -0
  144. package/dist/migrate-candidates.js.map +1 -0
  145. package/dist/migrate.d.ts +125 -0
  146. package/dist/migrate.d.ts.map +1 -0
  147. package/dist/migrate.js +561 -0
  148. package/dist/migrate.js.map +1 -0
  149. package/dist/plugin-manifest.d.ts +143 -0
  150. package/dist/plugin-manifest.d.ts.map +1 -0
  151. package/dist/plugin-manifest.js +233 -0
  152. package/dist/plugin-manifest.js.map +1 -0
  153. package/dist/plugin.d.ts +274 -0
  154. package/dist/plugin.d.ts.map +1 -0
  155. package/dist/plugin.js +919 -0
  156. package/dist/plugin.js.map +1 -0
  157. package/dist/pod-liveness.d.ts +51 -0
  158. package/dist/pod-liveness.d.ts.map +1 -0
  159. package/dist/pod-liveness.js +80 -0
  160. package/dist/pod-liveness.js.map +1 -0
  161. package/dist/profile-menu.d.ts +7 -0
  162. package/dist/profile-menu.d.ts.map +1 -0
  163. package/dist/profile-menu.js +52 -0
  164. package/dist/profile-menu.js.map +1 -0
  165. package/dist/profiles.d.ts +23 -0
  166. package/dist/profiles.d.ts.map +1 -0
  167. package/dist/profiles.js +66 -0
  168. package/dist/profiles.js.map +1 -0
  169. package/dist/protocol-local.d.ts +27 -0
  170. package/dist/protocol-local.d.ts.map +1 -0
  171. package/dist/protocol-local.js +12 -0
  172. package/dist/protocol-local.js.map +1 -0
  173. package/dist/pty.d.ts +26 -0
  174. package/dist/pty.d.ts.map +1 -0
  175. package/dist/pty.js +45 -0
  176. package/dist/pty.js.map +1 -0
  177. package/dist/readiness.d.ts +38 -0
  178. package/dist/readiness.d.ts.map +1 -0
  179. package/dist/readiness.js +169 -0
  180. package/dist/readiness.js.map +1 -0
  181. package/dist/registry.d.ts +258 -0
  182. package/dist/registry.d.ts.map +1 -0
  183. package/dist/registry.js +570 -0
  184. package/dist/registry.js.map +1 -0
  185. package/dist/relaunch.d.ts +52 -0
  186. package/dist/relaunch.d.ts.map +1 -0
  187. package/dist/relaunch.js +76 -0
  188. package/dist/relaunch.js.map +1 -0
  189. package/dist/restore.d.ts +165 -0
  190. package/dist/restore.d.ts.map +1 -0
  191. package/dist/restore.js +530 -0
  192. package/dist/restore.js.map +1 -0
  193. package/dist/run.d.ts +29 -0
  194. package/dist/run.d.ts.map +1 -0
  195. package/dist/run.js +206 -0
  196. package/dist/run.js.map +1 -0
  197. package/dist/secrets.d.ts +25 -0
  198. package/dist/secrets.d.ts.map +1 -0
  199. package/dist/secrets.js +98 -0
  200. package/dist/secrets.js.map +1 -0
  201. package/dist/session-restore.d.ts +26 -0
  202. package/dist/session-restore.d.ts.map +1 -0
  203. package/dist/session-restore.js +122 -0
  204. package/dist/session-restore.js.map +1 -0
  205. package/dist/skills-sync.d.ts +126 -0
  206. package/dist/skills-sync.d.ts.map +1 -0
  207. package/dist/skills-sync.js +255 -0
  208. package/dist/skills-sync.js.map +1 -0
  209. package/dist/smoke.d.ts +23 -0
  210. package/dist/smoke.d.ts.map +1 -0
  211. package/dist/smoke.js +138 -0
  212. package/dist/smoke.js.map +1 -0
  213. package/dist/soft-refresh.d.ts +107 -0
  214. package/dist/soft-refresh.d.ts.map +1 -0
  215. package/dist/soft-refresh.js +426 -0
  216. package/dist/soft-refresh.js.map +1 -0
  217. package/dist/sync-status.d.ts +22 -0
  218. package/dist/sync-status.d.ts.map +1 -0
  219. package/dist/sync-status.js +41 -0
  220. package/dist/sync-status.js.map +1 -0
  221. package/dist/sync.d.ts +77 -0
  222. package/dist/sync.d.ts.map +1 -0
  223. package/dist/sync.js +302 -0
  224. package/dist/sync.js.map +1 -0
  225. package/dist/throttle-signatures.d.ts +43 -0
  226. package/dist/throttle-signatures.d.ts.map +1 -0
  227. package/dist/throttle-signatures.js +116 -0
  228. package/dist/throttle-signatures.js.map +1 -0
  229. package/dist/tmux.d.ts +286 -0
  230. package/dist/tmux.d.ts.map +1 -0
  231. package/dist/tmux.js +970 -0
  232. package/dist/tmux.js.map +1 -0
  233. package/dist/tunnel.d.ts +25 -0
  234. package/dist/tunnel.d.ts.map +1 -0
  235. package/dist/tunnel.js +127 -0
  236. package/dist/tunnel.js.map +1 -0
  237. package/dist/workspace-merge.d.ts +17 -0
  238. package/dist/workspace-merge.d.ts.map +1 -0
  239. package/dist/workspace-merge.js +153 -0
  240. package/dist/workspace-merge.js.map +1 -0
  241. package/dist/workspace-sync-incremental.d.ts +64 -0
  242. package/dist/workspace-sync-incremental.d.ts.map +1 -0
  243. package/dist/workspace-sync-incremental.js +199 -0
  244. package/dist/workspace-sync-incremental.js.map +1 -0
  245. package/dist/workspace-sync.d.ts +3 -0
  246. package/dist/workspace-sync.d.ts.map +1 -0
  247. package/dist/workspace-sync.js +187 -0
  248. package/dist/workspace-sync.js.map +1 -0
  249. package/dist/workspace.d.ts +70 -0
  250. package/dist/workspace.d.ts.map +1 -0
  251. package/dist/workspace.js +173 -0
  252. package/dist/workspace.js.map +1 -0
  253. package/package.json +44 -0
@@ -0,0 +1,426 @@
1
+ /**
2
+ * Soft credential refresh: push fresh local creds INTO a running session Pod and
3
+ * relaunch the CLI in place — WITHOUT recreating the Pod (keeps HOME parity, the
4
+ * conversation, the node, no image pull). Fixes the recurring ~8h token logout
5
+ * (the CLI can't self-refresh OAuth in a headless Pod). Also patches the auth
6
+ * Secret so the fresh token survives a future Pod restart.
7
+ *
8
+ * Steps (all via kubectl against the configured tunnel):
9
+ * 1. materialize each bundled cred file into $HOME/<rel> in the Pod,
10
+ * 2. patch session-<id>-auth Secret keys (durability),
11
+ * 3. respawn the Pod's tmux `main` pane: `<cli> --resume <newestConv>` with a
12
+ * UTF-8 locale, dropping to a shell on exit (never kills the session).
13
+ *
14
+ * Unchanged-creds gating (`skipIfUnchanged`, used by `refresh --all/--watch`):
15
+ * the respawn INTERRUPTS the Pod's CLI, so it must only happen when the pushed
16
+ * creds actually differ. We sha256 the bundle and compare it with the previous
17
+ * pass (`previousHash`, in-memory --watch state) and with the hash recorded in
18
+ * the Pod ($HOME/.remote-creds.sha256, written after every push). Identical
19
+ * creds = silent no-op: nothing pushed, no Secret patch, NO respawn.
20
+ */
21
+ import { spawnSync } from "node:child_process";
22
+ import { createHash } from "node:crypto";
23
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
24
+ import { homedir } from "node:os";
25
+ import { join } from "node:path";
26
+ import { collectProfileAuth } from "./auth-bundle.js";
27
+ import { collectToolAuth, localCredsExistFor } from "./auth-tools.js";
28
+ import { getTunnel } from "./config.js";
29
+ import { buildHealthProbeCommand, isProbeableTool, parseHealthResult, } from "./cred-health.js";
30
+ /** Secret data key for a HOME-relative cred path (matches the orchestrator). */
31
+ function credentialSecretKey(rel) {
32
+ return rel.replace(/^\.+/, "").replace(/\//g, "_");
33
+ }
34
+ function expandHome(p) {
35
+ return p.startsWith("~") ? join(homedir(), p.slice(1)) : p;
36
+ }
37
+ function kubeEnv(tunnel) {
38
+ const env = { ...process.env };
39
+ if (tunnel.kubeconfig)
40
+ env.KUBECONFIG = expandHome(tunnel.kubeconfig);
41
+ return env;
42
+ }
43
+ /** Run kubectl, optionally piping `input` to stdin. Throws on non-zero. */
44
+ function kubectl(tunnel, args, input) {
45
+ const r = spawnSync("kubectl", ["-n", tunnel.namespace, ...args], {
46
+ encoding: "utf8",
47
+ env: kubeEnv(tunnel),
48
+ ...(input !== undefined ? { input } : {}),
49
+ });
50
+ if (r.status !== 0) {
51
+ throw new Error(`kubectl ${args.join(" ")} failed: ${(r.stderr || r.stdout || "").trim()}`);
52
+ }
53
+ return r.stdout;
54
+ }
55
+ /** Exec a bash -lc script in the session-agent container. */
56
+ function execPod(tunnel, pod, script, input) {
57
+ const base = ["exec"];
58
+ if (input !== undefined)
59
+ base.push("-i");
60
+ return kubectl(tunnel, [...base, pod, "-c", "session-agent", "--", "bash", "-lc", script], input);
61
+ }
62
+ /**
63
+ * Exec a tool's argv DIRECTLY in the session-agent container — NO `bash -lc`
64
+ * string-concat (the argv comes from `buildHealthProbeCommand`, static tokens
65
+ * only). Returns the exit code + stdout; NEVER throws (a non-zero exit IS the
66
+ * 401 signal we want to read). Used by the additive pod-side health probe.
67
+ */
68
+ function execPodArgv(tunnel, pod, argv) {
69
+ const r = spawnSync("kubectl", ["-n", tunnel.namespace, "exec", pod, "-c", "session-agent", "--", ...argv], { encoding: "utf8", env: kubeEnv(tunnel) });
70
+ return { status: r.status ?? 1, stdout: r.stdout ?? "" };
71
+ }
72
+ /** `<cli> --resume <id>` per profile, when the profile has a verified resume form. */
73
+ function resumeCommand(profile, convId) {
74
+ const q = (s) => `'${s.replace(/'/g, "'\\''")}'`;
75
+ switch (profile) {
76
+ case "codex":
77
+ return `codex resume ${q(convId)}`;
78
+ case "agy":
79
+ case "antigravity":
80
+ return `agy --resume ${q(convId)}`;
81
+ case "claude":
82
+ case "claude-code":
83
+ return `claude --resume ${q(convId)}`;
84
+ default:
85
+ return undefined;
86
+ }
87
+ }
88
+ /** HOME-relative file in the Pod recording the sha256 of the last pushed bundle. */
89
+ export const CREDS_HASH_FILE = ".remote-creds.sha256";
90
+ /**
91
+ * Bundle rels that are CONFIG, not credentials. They ride along on a push but
92
+ * must NOT gate it: `.claude.json` is rewritten constantly by every running
93
+ * local claude session (statsig/lastUsed churn), so hashing it would make
94
+ * `--watch` respawn the Pod CLI on nearly every pass. Only the actual
95
+ * credential files decide "changed".
96
+ */
97
+ const CONFIG_ONLY_RELS = new Set([
98
+ ".claude.json",
99
+ ".claude/settings.json",
100
+ ".codex/config.toml",
101
+ ".gemini/antigravity-cli/settings.json",
102
+ ]);
103
+ /**
104
+ * Deterministic sha256 of an auth bundle (profile + sorted rel -> base64 value),
105
+ * SKIPPING config-only rels (see CONFIG_ONLY_RELS). Values stay opaque: the
106
+ * hash never leaks any secret material.
107
+ */
108
+ export function hashAuthBundle(profile, bundle) {
109
+ const h = createHash("sha256");
110
+ h.update(profile);
111
+ h.update("\0");
112
+ for (const rel of Object.keys(bundle).sort()) {
113
+ if (CONFIG_ONLY_RELS.has(rel))
114
+ continue;
115
+ h.update(rel);
116
+ h.update("\0");
117
+ h.update(bundle[rel]);
118
+ h.update("\0");
119
+ }
120
+ return h.digest("hex");
121
+ }
122
+ /**
123
+ * Push the profile's fresh creds into the Pod, patch the Secret, and relaunch
124
+ * the CLI in place. `bundle` is the already-collected creds (rel -> content).
125
+ */
126
+ export async function softRefreshSession(sessionId, profile, options = {}) {
127
+ const stderr = options.stderr ?? process.stderr;
128
+ const tunnel = getTunnel();
129
+ if (!tunnel) {
130
+ throw new Error("soft refresh needs a tunnel configured (remote config tunnel …)");
131
+ }
132
+ const pod = `session-${sessionId}`;
133
+ const secret = `${pod}-auth`;
134
+ const bundle = await collectProfileAuth(profile);
135
+ const rels = Object.keys(bundle);
136
+ if (rels.length === 0) {
137
+ throw new Error(`no local credentials found for profile "${profile}"`);
138
+ }
139
+ // 0. unchanged-creds gating: identical bundle = silent no-op WITHOUT respawn
140
+ // (the respawn interrupts the Pod's CLI session — only worth it for new creds).
141
+ const hash = hashAuthBundle(profile, bundle);
142
+ if (options.skipIfUnchanged) {
143
+ let podHash;
144
+ if (options.previousHash === hash) {
145
+ podHash = hash; // in-memory state matches: skip even the Pod read
146
+ }
147
+ else {
148
+ try {
149
+ podHash = execPod(tunnel, pod, `cat "$HOME/${CREDS_HASH_FILE}" 2>/dev/null || true`).trim();
150
+ }
151
+ catch {
152
+ // Pod state unreadable — fall through; the push will surface the real error.
153
+ }
154
+ }
155
+ if (podHash === hash) {
156
+ // Creds unchanged — but if the Pod's CLI DIED (pane dropped to the
157
+ // wrapper shell, e.g. it exited on a 401 before fresh creds landed),
158
+ // "unchanged" must still bring the session back: respawn with the creds
159
+ // already in place. Probe ONLY when we already read the Pod this pass
160
+ // (previousHash mismatch): the in-memory watch match stays zero-I/O.
161
+ let paneDead = false;
162
+ let paneCmd = "";
163
+ if (options.previousHash !== hash) {
164
+ try {
165
+ // The relaunch wrapper keeps the CLI as a CHILD of a bash script, so
166
+ // pane_current_command alone reads "bash" even when claude is alive.
167
+ // Dead = pane at bash/sh AND no child process (idle drop-to-shell).
168
+ // /proc PPid scan because the runtime image has no ps/pgrep.
169
+ const probe = execPod(tunnel, pod, `pp=$(tmux display -p -t main "#{pane_pid}" 2>/dev/null); ` +
170
+ `cmd=$(tmux display -p -t main "#{pane_current_command}" 2>/dev/null); ` +
171
+ `kids=$(awk -v p="$pp" '$1=="PPid:" && $2==p {c++} END{print c+0}' /proc/[0-9]*/status 2>/dev/null); ` +
172
+ `echo "$cmd $kids"`)
173
+ .trim()
174
+ .split(/\s+/);
175
+ paneCmd = probe[0] ?? "";
176
+ const kids = Number(probe[1] ?? "1") || 0;
177
+ paneDead = (paneCmd === "bash" || paneCmd === "sh") && kids === 0;
178
+ }
179
+ catch {
180
+ // tmux unreadable — treat as alive; the next changed-creds pass heals it.
181
+ }
182
+ }
183
+ if (!paneDead) {
184
+ return {
185
+ changed: false,
186
+ hash,
187
+ filesPushed: [],
188
+ secretKeysPatched: [],
189
+ convId: undefined,
190
+ respawned: false,
191
+ };
192
+ }
193
+ stderr.write(`[remote] ${pod}: creds unchanged but the CLI is down (pane at ${paneCmd || "?"}) — respawning\n`);
194
+ const convId = detectNewestConversation(tunnel, pod);
195
+ const respawned = convId
196
+ ? respawnPane(tunnel, pod, profile, convId, stderr)
197
+ : false;
198
+ if (!convId) {
199
+ stderr.write(`[remote] no conversation found to resume in ${pod}\n`);
200
+ }
201
+ return {
202
+ changed: false,
203
+ hash,
204
+ filesPushed: [],
205
+ secretKeysPatched: [],
206
+ convId,
207
+ respawned,
208
+ };
209
+ }
210
+ }
211
+ // 1. materialize each cred file into the Pod's HOME.
212
+ const filesPushed = [];
213
+ for (const rel of rels) {
214
+ // collectProfileAuth already returns base64 — decode it ONCE into the file
215
+ // (do NOT re-encode, or the file ends up containing base64 text).
216
+ execPod(tunnel, pod, `mkdir -p "$(dirname "$HOME/${rel}")" && base64 -d > "$HOME/${rel}" && chmod 600 "$HOME/${rel}"`, bundle[rel]);
217
+ filesPushed.push(rel);
218
+ }
219
+ stderr.write(`[remote] pushed ${filesPushed.length} cred file(s) into ${pod}\n`);
220
+ // Record the pushed bundle's hash in the Pod so future --all/--watch passes
221
+ // can no-op (and NOT respawn) when the local creds haven't changed.
222
+ try {
223
+ execPod(tunnel, pod, `printf %s '${hash}' > "$HOME/${CREDS_HASH_FILE}"`);
224
+ }
225
+ catch (error) {
226
+ stderr.write(`[remote] warn: could not record creds hash in Pod: ${String(error).slice(0, 120)}\n`);
227
+ }
228
+ // 2. patch the Secret so the fresh creds survive a Pod restart.
229
+ const secretKeysPatched = [];
230
+ const runDir = join(process.env.XDG_RUNTIME_DIR ??
231
+ join(homedir(), ".config", "sentropic", "remote-cli"), "sentropic-remote-run");
232
+ mkdirSync(runDir, { recursive: true });
233
+ let patchN = 0;
234
+ for (const rel of rels) {
235
+ const key = credentialSecretKey(rel);
236
+ // Secret data values ARE base64, and collectProfileAuth already returns
237
+ // base64 — use it as-is (do NOT re-encode).
238
+ // Patch via a temp --patch-file so large creds (the full account file) don't
239
+ // blow the command-line length limit (inline -p) nor hit the spawn stdin
240
+ // /dev/stdin quirk.
241
+ const patchFile = join(runDir, `patch-${process.pid}-${patchN++}.json`);
242
+ try {
243
+ writeFileSync(patchFile, JSON.stringify({ data: { [key]: bundle[rel] } }), "utf8");
244
+ kubectl(tunnel, [
245
+ "patch",
246
+ "secret",
247
+ secret,
248
+ "--type",
249
+ "merge",
250
+ "--patch-file",
251
+ patchFile,
252
+ ]);
253
+ secretKeysPatched.push(key);
254
+ }
255
+ catch (error) {
256
+ stderr.write(`[remote] warn: could not patch Secret key ${key}: ${String(error).slice(0, 120)}\n`);
257
+ }
258
+ finally {
259
+ rmSync(patchFile, { force: true });
260
+ }
261
+ }
262
+ stderr.write(`[remote] patched ${secretKeysPatched.length} Secret key(s) (durable across restart)\n`);
263
+ // 3. detect the newest conversation in the Pod and respawn the CLI in tmux.
264
+ const convId = detectNewestConversation(tunnel, pod);
265
+ let respawned = false;
266
+ if (convId) {
267
+ respawned = respawnPane(tunnel, pod, profile, convId, stderr);
268
+ }
269
+ else {
270
+ stderr.write(`[remote] creds pushed; no conversation found to resume\n`);
271
+ }
272
+ return {
273
+ changed: true,
274
+ hash,
275
+ filesPushed,
276
+ secretKeysPatched,
277
+ convId,
278
+ respawned,
279
+ };
280
+ }
281
+ /** Newest conversation id present in the Pod (claude projects / codex rollouts). */
282
+ function detectNewestConversation(tunnel, pod) {
283
+ return execPod(tunnel, pod, `ls -t "$HOME"/.claude/projects/*/*.jsonl "$HOME"/.codex/sessions/**/*.jsonl 2>/dev/null | head -1 | xargs -r basename | sed 's/\\.jsonl$//; s/^rollout-//'`)
284
+ .trim()
285
+ .split("\n")
286
+ .filter(Boolean)[0];
287
+ }
288
+ /**
289
+ * Write a relaunch script + respawn the durable tmux pane (drop-to-shell on
290
+ * exit so the session is never killed). Single-quoted heredoc keeps it intact.
291
+ */
292
+ function respawnPane(tunnel, pod, profile, convId, stderr) {
293
+ const resume = resumeCommand(profile, convId);
294
+ if (!resume) {
295
+ stderr.write(`[remote] creds pushed but ${profile} has no verified resume command for ${convId}; restart the CLI in the Pod manually\n`);
296
+ return false;
297
+ }
298
+ const script = [
299
+ `cat > "$HOME/.remote-relaunch.sh" <<'RL'`,
300
+ `#!/bin/bash`,
301
+ `export LANG=C.UTF-8 LC_ALL=C.UTF-8`,
302
+ `cd "$WORKSPACE_PATH" 2>/dev/null || true`,
303
+ resume,
304
+ `printf '\\n[remote] %s exited — shell.\\n' "$0"`,
305
+ `exec bash -l`,
306
+ `RL`,
307
+ `chmod +x "$HOME/.remote-relaunch.sh"`,
308
+ `tmux respawn-pane -t main -k "$HOME/.remote-relaunch.sh" 2>/dev/null && echo respawned || echo no-tmux`,
309
+ ].join("\n");
310
+ const out = execPod(tunnel, pod, script).trim();
311
+ const respawned = out.endsWith("respawned");
312
+ stderr.write(respawned
313
+ ? `[remote] relaunched ${profile} (--resume ${convId}) in the Pod's tmux\n`
314
+ : `[remote] creds pushed but no tmux 'main' to respawn — restart the CLI in the Pod manually\n`);
315
+ return respawned;
316
+ }
317
+ /**
318
+ * Probe ONE covered tool in a live Pod and, if it's unauthenticated, push the
319
+ * tool's LOCAL creds (re-bundle + materialize + Secret patch — the same
320
+ * mechanism as today, an extra trigger). Pure-ish: all IO is injected via
321
+ * `deps`. Returns what happened (probe result + whether/what was pushed).
322
+ * Exported for tests.
323
+ */
324
+ export async function probeAndPushToolHealth(tool, deps) {
325
+ const stderr = deps.stderr ?? { write: () => true };
326
+ const probe = deps.exec(buildHealthProbeCommand(tool));
327
+ const health = parseHealthResult(tool, probe.status, probe.stdout);
328
+ if (health.ok) {
329
+ return {
330
+ tool,
331
+ health,
332
+ pushed: false,
333
+ filesPushed: [],
334
+ secretKeysPatched: [],
335
+ };
336
+ }
337
+ // ok:false → re-bundle + push this tool's LOCAL creds (same path as today).
338
+ // Tool/status only — NEVER a secret value.
339
+ stderr.write(`[remote] pod 401: ${health.reason} — pushing fresh ${tool} creds\n`);
340
+ const { bundle } = await deps.collect([tool]);
341
+ const rels = Object.keys(bundle);
342
+ const filesPushed = [];
343
+ const secretKeysPatched = [];
344
+ for (const rel of rels) {
345
+ deps.materialize(rel, bundle[rel]);
346
+ filesPushed.push(rel);
347
+ const key = credentialSecretKey(rel);
348
+ deps.patchSecretKey(key, bundle[rel]);
349
+ secretKeysPatched.push(key);
350
+ }
351
+ if (filesPushed.length === 0) {
352
+ stderr.write(`[remote] no local ${tool} creds to push — run \`${tool} login\` locally\n`);
353
+ }
354
+ return {
355
+ tool,
356
+ health,
357
+ pushed: filesPushed.length > 0,
358
+ filesPushed,
359
+ secretKeysPatched,
360
+ };
361
+ }
362
+ /**
363
+ * Production wiring for `probeAndPushToolHealth` against a configured tunnel +
364
+ * live pod. Builds the default `ToolHealthDeps` (real kubectl exec / patch /
365
+ * collectToolAuth) and probes EACH covered tool, pushing on a 401. Best-effort:
366
+ * a probe/push error for one tool is logged and the others continue. Returns the
367
+ * per-tool actions. NOT pure (does IO); the pure decision lives in
368
+ * `probeAndPushToolHealth` + cred-health. Used by the watch loop.
369
+ */
370
+ export async function probePodCredHealth(sessionId, tools = ["gh", "npm", "docker"], options = {}) {
371
+ const stderr = options.stderr ?? process.stderr;
372
+ const tunnel = getTunnel();
373
+ if (!tunnel)
374
+ throw new Error("pod cred health probe needs a tunnel configured");
375
+ const pod = `session-${sessionId}`;
376
+ const secret = `${pod}-auth`;
377
+ const runDir = join(process.env.XDG_RUNTIME_DIR ??
378
+ join(homedir(), ".config", "sentropic", "remote-cli"), "sentropic-remote-run");
379
+ const deps = {
380
+ exec: (argv) => execPodArgv(tunnel, pod, argv),
381
+ collect: (t) => collectToolAuth(t),
382
+ materialize: (rel, base64) => {
383
+ execPod(tunnel, pod, `mkdir -p "$(dirname "$HOME/${rel}")" && base64 -d > "$HOME/${rel}" && chmod 600 "$HOME/${rel}"`, base64);
384
+ },
385
+ patchSecretKey: (key, base64) => {
386
+ mkdirSync(runDir, { recursive: true });
387
+ const patchFile = join(runDir, `tool-patch-${process.pid}-${key}.json`);
388
+ try {
389
+ writeFileSync(patchFile, JSON.stringify({ data: { [key]: base64 } }), "utf8");
390
+ kubectl(tunnel, [
391
+ "patch",
392
+ "secret",
393
+ secret,
394
+ "--type",
395
+ "merge",
396
+ "--patch-file",
397
+ patchFile,
398
+ ]);
399
+ }
400
+ finally {
401
+ rmSync(patchFile, { force: true });
402
+ }
403
+ },
404
+ stderr,
405
+ };
406
+ const actions = [];
407
+ for (const tool of tools) {
408
+ if (!isProbeableTool(tool))
409
+ continue;
410
+ // GUARD (slice-2 regression fix): only probe/push a tool when the LOCAL
411
+ // machine actually has non-empty creds for it. With no local creds, pushing
412
+ // is pointless — the Pod's probe stays 401 and the next pass repeats the same
413
+ // useless push forever. No local creds → skip ENTIRELY (no probe, no push, no
414
+ // log). Tools WITH valid local creds are byte-identical to before.
415
+ if (!localCredsExistFor(tool))
416
+ continue;
417
+ try {
418
+ actions.push(await probeAndPushToolHealth(tool, deps));
419
+ }
420
+ catch (error) {
421
+ stderr.write(`[remote] ${pod}: ${tool} health probe/push errored: ${String(error).slice(0, 120)}\n`);
422
+ }
423
+ }
424
+ return actions;
425
+ }
426
+ //# sourceMappingURL=soft-refresh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"soft-refresh.js","sourceRoot":"","sources":["../src/soft-refresh.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ export type SyncState = "synced" | "pending" | "degraded" | "blocked";
2
+ export type ClassMetrics = {
3
+ pendingBytes: number;
4
+ pendingCount: number;
5
+ oldestPendingAge: number;
6
+ lastAckedAt: string | null;
7
+ estimatedCatchup: number;
8
+ };
9
+ export type SyncStatus = {
10
+ state: SyncState;
11
+ safeToClose: boolean;
12
+ updatedAt: string;
13
+ conv: ClassMetrics;
14
+ hot: ClassMetrics;
15
+ cold: ClassMetrics;
16
+ };
17
+ export declare function syncStatusPath(sessionId: string): string;
18
+ export declare function readSyncStatus(sessionId: string): SyncStatus | null;
19
+ export declare function writeSyncStatus(sessionId: string, status: SyncStatus): void;
20
+ export declare function emptyMetrics(): ClassMetrics;
21
+ export declare function mergedState(classes: SyncState[]): SyncState;
22
+ //# sourceMappingURL=sync-status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-status.d.ts","sourceRoot":"","sources":["../src/sync-status.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;AAEtE,MAAM,MAAM,YAAY,GAAG;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,YAAY,CAAC;IACnB,GAAG,EAAE,YAAY,CAAC;IAClB,IAAI,EAAE,YAAY,CAAC;CACpB,CAAC;AAEF,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAOnE;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CAM3E;AAED,wBAAgB,YAAY,IAAI,YAAY,CAQ3C;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,SAAS,CAK3D"}
@@ -0,0 +1,41 @@
1
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ export function syncStatusPath(sessionId) {
5
+ return join(homedir(), ".remote", "sync-status", `${sessionId}.json`);
6
+ }
7
+ export function readSyncStatus(sessionId) {
8
+ const p = syncStatusPath(sessionId);
9
+ try {
10
+ return JSON.parse(readFileSync(p, "utf8"));
11
+ }
12
+ catch {
13
+ return null;
14
+ }
15
+ }
16
+ export function writeSyncStatus(sessionId, status) {
17
+ const p = syncStatusPath(sessionId);
18
+ mkdirSync(join(homedir(), ".remote", "sync-status"), { recursive: true });
19
+ const tmp = `${p}.tmp`;
20
+ writeFileSync(tmp, JSON.stringify(status, null, 2), "utf8");
21
+ renameSync(tmp, p);
22
+ }
23
+ export function emptyMetrics() {
24
+ return {
25
+ pendingBytes: 0,
26
+ pendingCount: 0,
27
+ oldestPendingAge: 0,
28
+ lastAckedAt: null,
29
+ estimatedCatchup: 0,
30
+ };
31
+ }
32
+ export function mergedState(classes) {
33
+ if (classes.includes("blocked"))
34
+ return "blocked";
35
+ if (classes.includes("degraded"))
36
+ return "degraded";
37
+ if (classes.includes("pending"))
38
+ return "pending";
39
+ return "synced";
40
+ }
41
+ //# sourceMappingURL=sync-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-status.js","sourceRoot":"","sources":["../src/sync-status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAqBjC,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,SAAS,OAAO,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,MAAM,CAAC,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;IACpC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAe,CAAC;IAC3D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,SAAiB,EAAE,MAAkB;IACnE,MAAM,CAAC,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;IACpC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,aAAa,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1E,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC;IACvB,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAC5D,UAAU,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO;QACL,YAAY,EAAE,CAAC;QACf,YAAY,EAAE,CAAC;QACf,gBAAgB,EAAE,CAAC;QACnB,WAAW,EAAE,IAAI;QACjB,gBAAgB,EAAE,CAAC;KACpB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAoB;IAC9C,IAAI,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IAClD,IAAI,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;QAAE,OAAO,UAAU,CAAC;IACpD,IAAI,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IAClD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
package/dist/sync.d.ts ADDED
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Conversation sync between local and a session Pod (`remote sync`): copy the
3
+ * conversation .jsonl in either direction over `kubectl exec`, base64 on the
4
+ * wire (encoded exactly ONCE — never double-encode, that was a real bug), with
5
+ * an ahead-GUARD so the side that has MORE of the conversation is never
6
+ * silently overwritten (--force to override), and a `.bak-<epoch>` backup of
7
+ * whatever gets overwritten, taken BEFORE writing. Conversation content is
8
+ * transferred verbatim but never printed.
9
+ */
10
+ import { type TunnelConfig } from "./config.js";
11
+ export type SyncDirection = "push" | "pull";
12
+ export type SyncDecision = {
13
+ allow: true;
14
+ } | {
15
+ allow: false;
16
+ reason: string;
17
+ };
18
+ /**
19
+ * Pure GUARD: never overwrite the side that has MORE of the conversation.
20
+ * `pull` overwrites local → refuse if local is ahead; `push` overwrites the
21
+ * Pod → refuse if remote is ahead. `--force` overrides (a backup is still
22
+ * taken either way).
23
+ */
24
+ export declare function decideSyncAction(args: {
25
+ localLines: number;
26
+ remoteLines: number;
27
+ direction: SyncDirection;
28
+ force: boolean;
29
+ }): SyncDecision;
30
+ /** Local path of a conversation file for a workspace (claude cwd encoding). */
31
+ export declare function localConvFile(workspacePath: string, convId: string, home?: string): string;
32
+ /** Pod-side conversation path, relative to $HOME (used as "$HOME/<rel>"). */
33
+ export declare function remoteConvRel(workspacePath: string, convId: string): string;
34
+ /** Like execPod but never throws — returns {stdout, status}. */
35
+ export declare function execPodRaw(tunnel: TunnelConfig, pod: string, script: string, input?: string): {
36
+ stdout: string;
37
+ status: number;
38
+ };
39
+ type ConvSyncState = {
40
+ offset: number;
41
+ prefixHash: string;
42
+ generation: number;
43
+ lastAckedToken?: number;
44
+ updatedAt: string;
45
+ };
46
+ export declare function readConvSyncState(convId: string, home?: string): ConvSyncState | undefined;
47
+ export declare function writeConvSyncState(state: ConvSyncState, convId: string, home?: string): void;
48
+ export type SyncResult = {
49
+ ok: true;
50
+ direction: SyncDirection;
51
+ convId: string;
52
+ lines: {
53
+ local: number;
54
+ remote: number;
55
+ };
56
+ /** Path of the .bak-<epoch> taken (undefined if nothing was overwritten). */
57
+ backup: string | undefined;
58
+ /** Destination written (local path, or "$HOME/<rel>" on the Pod). */
59
+ written: string;
60
+ incremental: boolean;
61
+ } | {
62
+ ok: false;
63
+ reason: string;
64
+ };
65
+ /**
66
+ * Copy the conversation .jsonl between local and the Pod. `pull`: Pod → local
67
+ * (guard: refuse if local is ahead). `push`: local → Pod (guard: refuse if
68
+ * remote is ahead). Both back up the overwritten file as `.bak-<epoch>` first.
69
+ */
70
+ export declare function syncConversation(args: {
71
+ sessionId: string;
72
+ workspacePath: string;
73
+ direction: SyncDirection;
74
+ force: boolean;
75
+ }): SyncResult;
76
+ export {};
77
+ //# sourceMappingURL=sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAeH,OAAO,EAAa,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAK3D,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5C,MAAM,MAAM,YAAY,GAAG;IAAE,KAAK,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9E;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,aAAa,CAAC;IACzB,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,YAAY,CAsBf;AAED,+EAA+E;AAC/E,wBAAgB,aAAa,CAC3B,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,MAAkB,GACvB,MAAM,CAER;AAED,6EAA6E;AAC7E,wBAAgB,aAAa,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE3E;AA+BD,gEAAgE;AAChE,wBAAgB,UAAU,CACxB,MAAM,EAAE,YAAY,EACpB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,MAAM,GACb;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAapC;AAYD,KAAK,aAAa,GAAG;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAUF,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,MAAM,GACZ,aAAa,GAAG,SAAS,CAQ3B;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,MAAM,GACZ,IAAI,CAON;AAED,MAAM,MAAM,UAAU,GAClB;IACE,EAAE,EAAE,IAAI,CAAC;IACT,SAAS,EAAE,aAAa,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,6EAA6E;IAC7E,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,qEAAqE;IACrE,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;CACtB,GACD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,aAAa,CAAC;IACzB,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,UAAU,CAuMb"}