@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.
- package/dist/account-pool.d.ts +168 -0
- package/dist/account-pool.d.ts.map +1 -0
- package/dist/account-pool.js +385 -0
- package/dist/account-pool.js.map +1 -0
- package/dist/agents-projection.d.ts +56 -0
- package/dist/agents-projection.d.ts.map +1 -0
- package/dist/agents-projection.js +82 -0
- package/dist/agents-projection.js.map +1 -0
- package/dist/attach.d.ts +75 -0
- package/dist/attach.d.ts.map +1 -0
- package/dist/attach.js +432 -0
- package/dist/attach.js.map +1 -0
- package/dist/auth-bundle.d.ts +21 -0
- package/dist/auth-bundle.d.ts.map +1 -0
- package/dist/auth-bundle.js +112 -0
- package/dist/auth-bundle.js.map +1 -0
- package/dist/auth-diagnostics.d.ts +18 -0
- package/dist/auth-diagnostics.d.ts.map +1 -0
- package/dist/auth-diagnostics.js +23 -0
- package/dist/auth-diagnostics.js.map +1 -0
- package/dist/auth-login.d.ts +9 -0
- package/dist/auth-login.d.ts.map +1 -0
- package/dist/auth-login.js +21 -0
- package/dist/auth-login.js.map +1 -0
- package/dist/auth-refresh.d.ts +32 -0
- package/dist/auth-refresh.d.ts.map +1 -0
- package/dist/auth-refresh.js +83 -0
- package/dist/auth-refresh.js.map +1 -0
- package/dist/auth-tools.d.ts +55 -0
- package/dist/auth-tools.d.ts.map +1 -0
- package/dist/auth-tools.js +163 -0
- package/dist/auth-tools.js.map +1 -0
- package/dist/browser-bridge-local.d.ts +27 -0
- package/dist/browser-bridge-local.d.ts.map +1 -0
- package/dist/browser-bridge-local.js +51 -0
- package/dist/browser-bridge-local.js.map +1 -0
- package/dist/browser.d.ts +53 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +61 -0
- package/dist/browser.js.map +1 -0
- package/dist/conductor-launch.d.ts +179 -0
- package/dist/conductor-launch.d.ts.map +1 -0
- package/dist/conductor-launch.js +414 -0
- package/dist/conductor-launch.js.map +1 -0
- package/dist/config.d.ts +182 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +334 -0
- package/dist/config.js.map +1 -0
- package/dist/conv-guard.d.ts +76 -0
- package/dist/conv-guard.d.ts.map +1 -0
- package/dist/conv-guard.js +131 -0
- package/dist/conv-guard.js.map +1 -0
- package/dist/convsync.d.ts +24 -0
- package/dist/convsync.d.ts.map +1 -0
- package/dist/convsync.js +108 -0
- package/dist/convsync.js.map +1 -0
- package/dist/cred-health.d.ts +96 -0
- package/dist/cred-health.d.ts.map +1 -0
- package/dist/cred-health.js +170 -0
- package/dist/cred-health.js.map +1 -0
- package/dist/delegate.d.ts +406 -0
- package/dist/delegate.d.ts.map +1 -0
- package/dist/delegate.js +739 -0
- package/dist/delegate.js.map +1 -0
- package/dist/enroll.d.ts +91 -0
- package/dist/enroll.d.ts.map +1 -0
- package/dist/enroll.js +248 -0
- package/dist/enroll.js.map +1 -0
- package/dist/fanout.d.ts +63 -0
- package/dist/fanout.d.ts.map +1 -0
- package/dist/fanout.js +115 -0
- package/dist/fanout.js.map +1 -0
- package/dist/forward.d.ts +66 -0
- package/dist/forward.d.ts.map +1 -0
- package/dist/forward.js +141 -0
- package/dist/forward.js.map +1 -0
- package/dist/gitdiff.d.ts +31 -0
- package/dist/gitdiff.d.ts.map +1 -0
- package/dist/gitdiff.js +133 -0
- package/dist/gitdiff.js.map +1 -0
- package/dist/h2a-bridge.d.ts +102 -0
- package/dist/h2a-bridge.d.ts.map +1 -0
- package/dist/h2a-bridge.js +333 -0
- package/dist/h2a-bridge.js.map +1 -0
- package/dist/h2a-jobs.d.ts +235 -0
- package/dist/h2a-jobs.d.ts.map +1 -0
- package/dist/h2a-jobs.js +388 -0
- package/dist/h2a-jobs.js.map +1 -0
- package/dist/h2a-ping.d.ts +38 -0
- package/dist/h2a-ping.d.ts.map +1 -0
- package/dist/h2a-ping.js +58 -0
- package/dist/h2a-ping.js.map +1 -0
- package/dist/index.d.ts +98 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5887 -0
- package/dist/index.js.map +1 -0
- package/dist/interactive-throttle.d.ts +164 -0
- package/dist/interactive-throttle.d.ts.map +1 -0
- package/dist/interactive-throttle.js +194 -0
- package/dist/interactive-throttle.js.map +1 -0
- package/dist/lineage-client.d.ts +77 -0
- package/dist/lineage-client.d.ts.map +1 -0
- package/dist/lineage-client.js +117 -0
- package/dist/lineage-client.js.map +1 -0
- package/dist/lineage-lease.d.ts +152 -0
- package/dist/lineage-lease.d.ts.map +1 -0
- package/dist/lineage-lease.js +303 -0
- package/dist/lineage-lease.js.map +1 -0
- package/dist/llm-gateway-runtime/accounts.d.ts +48 -0
- package/dist/llm-gateway-runtime/accounts.d.ts.map +1 -0
- package/dist/llm-gateway-runtime/accounts.js +226 -0
- package/dist/llm-gateway-runtime/accounts.js.map +1 -0
- package/dist/llm-gateway-runtime/index.d.ts +4 -0
- package/dist/llm-gateway-runtime/index.d.ts.map +1 -0
- package/dist/llm-gateway-runtime/index.js +58 -0
- package/dist/llm-gateway-runtime/index.js.map +1 -0
- package/dist/llm-gateway-runtime/model-catalog.d.ts +36 -0
- package/dist/llm-gateway-runtime/model-catalog.d.ts.map +1 -0
- package/dist/llm-gateway-runtime/model-catalog.js +146 -0
- package/dist/llm-gateway-runtime/model-catalog.js.map +1 -0
- package/dist/llm-gateway-runtime/proxy-anthropic.d.ts +3 -0
- package/dist/llm-gateway-runtime/proxy-anthropic.d.ts.map +1 -0
- package/dist/llm-gateway-runtime/proxy-anthropic.js +139 -0
- package/dist/llm-gateway-runtime/proxy-anthropic.js.map +1 -0
- package/dist/llm-gateway-runtime/proxy-openai.d.ts +117 -0
- package/dist/llm-gateway-runtime/proxy-openai.d.ts.map +1 -0
- package/dist/llm-gateway-runtime/proxy-openai.js +1132 -0
- package/dist/llm-gateway-runtime/proxy-openai.js.map +1 -0
- package/dist/llm-gateway-runtime/session-ledger.d.ts +32 -0
- package/dist/llm-gateway-runtime/session-ledger.d.ts.map +1 -0
- package/dist/llm-gateway-runtime/session-ledger.js +62 -0
- package/dist/llm-gateway-runtime/session-ledger.js.map +1 -0
- package/dist/llm-gateway-runtime/sticky.d.ts +40 -0
- package/dist/llm-gateway-runtime/sticky.d.ts.map +1 -0
- package/dist/llm-gateway-runtime/sticky.js +254 -0
- package/dist/llm-gateway-runtime/sticky.js.map +1 -0
- package/dist/llm-mesh.d.ts +102 -0
- package/dist/llm-mesh.d.ts.map +1 -0
- package/dist/llm-mesh.js +433 -0
- package/dist/llm-mesh.js.map +1 -0
- package/dist/migrate-candidates.d.ts +35 -0
- package/dist/migrate-candidates.d.ts.map +1 -0
- package/dist/migrate-candidates.js +128 -0
- package/dist/migrate-candidates.js.map +1 -0
- package/dist/migrate.d.ts +125 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/migrate.js +561 -0
- package/dist/migrate.js.map +1 -0
- package/dist/plugin-manifest.d.ts +143 -0
- package/dist/plugin-manifest.d.ts.map +1 -0
- package/dist/plugin-manifest.js +233 -0
- package/dist/plugin-manifest.js.map +1 -0
- package/dist/plugin.d.ts +274 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +919 -0
- package/dist/plugin.js.map +1 -0
- package/dist/pod-liveness.d.ts +51 -0
- package/dist/pod-liveness.d.ts.map +1 -0
- package/dist/pod-liveness.js +80 -0
- package/dist/pod-liveness.js.map +1 -0
- package/dist/profile-menu.d.ts +7 -0
- package/dist/profile-menu.d.ts.map +1 -0
- package/dist/profile-menu.js +52 -0
- package/dist/profile-menu.js.map +1 -0
- package/dist/profiles.d.ts +23 -0
- package/dist/profiles.d.ts.map +1 -0
- package/dist/profiles.js +66 -0
- package/dist/profiles.js.map +1 -0
- package/dist/protocol-local.d.ts +27 -0
- package/dist/protocol-local.d.ts.map +1 -0
- package/dist/protocol-local.js +12 -0
- package/dist/protocol-local.js.map +1 -0
- package/dist/pty.d.ts +26 -0
- package/dist/pty.d.ts.map +1 -0
- package/dist/pty.js +45 -0
- package/dist/pty.js.map +1 -0
- package/dist/readiness.d.ts +38 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +169 -0
- package/dist/readiness.js.map +1 -0
- package/dist/registry.d.ts +258 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +570 -0
- package/dist/registry.js.map +1 -0
- package/dist/relaunch.d.ts +52 -0
- package/dist/relaunch.d.ts.map +1 -0
- package/dist/relaunch.js +76 -0
- package/dist/relaunch.js.map +1 -0
- package/dist/restore.d.ts +165 -0
- package/dist/restore.d.ts.map +1 -0
- package/dist/restore.js +530 -0
- package/dist/restore.js.map +1 -0
- package/dist/run.d.ts +29 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +206 -0
- package/dist/run.js.map +1 -0
- package/dist/secrets.d.ts +25 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +98 -0
- package/dist/secrets.js.map +1 -0
- package/dist/session-restore.d.ts +26 -0
- package/dist/session-restore.d.ts.map +1 -0
- package/dist/session-restore.js +122 -0
- package/dist/session-restore.js.map +1 -0
- package/dist/skills-sync.d.ts +126 -0
- package/dist/skills-sync.d.ts.map +1 -0
- package/dist/skills-sync.js +255 -0
- package/dist/skills-sync.js.map +1 -0
- package/dist/smoke.d.ts +23 -0
- package/dist/smoke.d.ts.map +1 -0
- package/dist/smoke.js +138 -0
- package/dist/smoke.js.map +1 -0
- package/dist/soft-refresh.d.ts +107 -0
- package/dist/soft-refresh.d.ts.map +1 -0
- package/dist/soft-refresh.js +426 -0
- package/dist/soft-refresh.js.map +1 -0
- package/dist/sync-status.d.ts +22 -0
- package/dist/sync-status.d.ts.map +1 -0
- package/dist/sync-status.js +41 -0
- package/dist/sync-status.js.map +1 -0
- package/dist/sync.d.ts +77 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +302 -0
- package/dist/sync.js.map +1 -0
- package/dist/throttle-signatures.d.ts +43 -0
- package/dist/throttle-signatures.d.ts.map +1 -0
- package/dist/throttle-signatures.js +116 -0
- package/dist/throttle-signatures.js.map +1 -0
- package/dist/tmux.d.ts +286 -0
- package/dist/tmux.d.ts.map +1 -0
- package/dist/tmux.js +970 -0
- package/dist/tmux.js.map +1 -0
- package/dist/tunnel.d.ts +25 -0
- package/dist/tunnel.d.ts.map +1 -0
- package/dist/tunnel.js +127 -0
- package/dist/tunnel.js.map +1 -0
- package/dist/workspace-merge.d.ts +17 -0
- package/dist/workspace-merge.d.ts.map +1 -0
- package/dist/workspace-merge.js +153 -0
- package/dist/workspace-merge.js.map +1 -0
- package/dist/workspace-sync-incremental.d.ts +64 -0
- package/dist/workspace-sync-incremental.d.ts.map +1 -0
- package/dist/workspace-sync-incremental.js +199 -0
- package/dist/workspace-sync-incremental.js.map +1 -0
- package/dist/workspace-sync.d.ts +3 -0
- package/dist/workspace-sync.d.ts.map +1 -0
- package/dist/workspace-sync.js +187 -0
- package/dist/workspace-sync.js.map +1 -0
- package/dist/workspace.d.ts +70 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +173 -0
- package/dist/workspace.js.map +1 -0
- 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"}
|