@granica/myelin-beam-client 0.1.0-alpha.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/beamUp.js ADDED
@@ -0,0 +1,252 @@
1
+ import { request } from "undici";
2
+ import { spawn } from "node:child_process";
3
+ import { readRolloutBytes, findCurrentClaudeRollout, findCurrentCodexRollout, trimIncompleteTail, } from "./rollout.js";
4
+ import { syncHarness } from "./harnessSync.js";
5
+ /// Orchestrate the full beam-up flow.
6
+ ///
7
+ /// Steps:
8
+ /// 1. Locate the current session's rollout (mtime heuristic).
9
+ /// 2. Rsync-diff the harness allowlist against router state; PUT
10
+ /// changed files, finalize the manifest.
11
+ /// 3. Mint three signed PUT URLs from the router (workspace,
12
+ /// transcript, artifacts).
13
+ /// 4. PUT: empty workspace tar (users clone repos inside the beam via
14
+ /// the GitLab/GitHub connectors already wired in /integrations —
15
+ /// we intentionally do NOT ship the user's cwd), the rollout
16
+ /// bytes, and an empty artifacts tar (deferred: "import to
17
+ /// running session" flow later).
18
+ /// 5. Finalize the mint → router creates BeamMeta + launches the VM.
19
+ /// 6. Return `{beam_id, beam_url, session_uuid, harness}`.
20
+ export async function beamUp(cfg, opts = {}) {
21
+ if (!cfg.silo_audit_url) {
22
+ throw new Error("No silo assigned to your account yet. Finish onboarding at https://myelin.granica.ai then re-run authenticate.");
23
+ }
24
+ const silo = cfg.silo_audit_url.replace(/\/+$/, "");
25
+ // 1. Locate rollout. Kind determines the search root and filename
26
+ // shape; both locators return the same RolloutHit envelope.
27
+ const agentKind = opts.agentKind ?? "claude";
28
+ const hit = agentKind === "codex"
29
+ ? await findCurrentCodexRollout()
30
+ : await findCurrentClaudeRollout();
31
+ if (!hit) {
32
+ const where = agentKind === "codex"
33
+ ? "~/.codex/sessions/"
34
+ : "~/.claude/projects/";
35
+ throw new Error(`Couldn't locate the current ${agentKind === "codex" ? "Codex" : "Claude"} session's transcript under ${where}. ` +
36
+ `Bridge process.cwd()=${process.cwd()}. ` +
37
+ `Have you sent at least one turn in ${agentKind === "codex" ? "Codex" : "Claude Code"} from this working directory before invoking beam_up?`);
38
+ }
39
+ // 2. Harness rsync — do this first so the beam boot picks up the new
40
+ // manifest. On the router side, sandbox_launcher checks for a
41
+ // manifest existence before stamping the harness-prefix key; we
42
+ // want that check to see the FRESH manifest, not yesterday's.
43
+ // agentKind selects both the local root (~/.claude vs ~/.codex)
44
+ // AND the router-side GCS prefix (harness/claude/ vs harness/codex/).
45
+ const harness = await syncHarness(cfg, agentKind);
46
+ // 3. Mint the three signed PUTs.
47
+ const mintRes = await request(`${silo}/api/mcp/beam/from-local/mint`, {
48
+ method: "POST",
49
+ headers: {
50
+ "content-type": "application/json",
51
+ authorization: `Bearer ${cfg.token}`,
52
+ },
53
+ body: "{}",
54
+ });
55
+ if (mintRes.statusCode >= 400) {
56
+ const detail = await mintRes.body.text().catch(() => "");
57
+ throw new Error(`beam mint failed: ${mintRes.statusCode} ${detail || ""}`);
58
+ }
59
+ const mint = (await mintRes.body.json());
60
+ // 4. PUT the three objects in parallel:
61
+ // - workspace: empty gzip tar (router expects application/gzip)
62
+ // - transcript: raw jsonl bytes from the rollout
63
+ // - artifacts: empty gzip tar
64
+ const rawRolloutBytes = await readRolloutBytes(hit);
65
+ // Beam-up runs inside a turn: the tail of the rollout is the `beam_up`
66
+ // tool call itself, whose matching tool-result can't exist yet. Trim
67
+ // that dangling call before upload so the resumed session ends on a
68
+ // clean turn — avoids codex's "Conversation interrupted" banner and
69
+ // matches the between-turn snapshot semantics that fork gets for free.
70
+ const rolloutBytes = trimIncompleteTail(rawRolloutBytes, hit.agent);
71
+ const emptyTgz = await emptyGzipTar();
72
+ await Promise.all([
73
+ putBytes(mint.workspace_put_url, emptyTgz, "application/gzip"),
74
+ putBytes(mint.transcript_put_url, rolloutBytes, "application/x-ndjson"),
75
+ putBytes(mint.artifacts_put_url, emptyTgz, "application/gzip"),
76
+ ]);
77
+ // 5. Finalize the upload session. Router marks it complete and hands
78
+ // back an `upload_token` — this DOES NOT launch the beam yet. The
79
+ // `mint.beam_id` from step 3 was a pre-mint placeholder; the real
80
+ // beam_id + pod are created by the launch step below.
81
+ // Normalize the laptop cwd to /<basename> before sending. The pod's
82
+ // resolve_workspace_dir() takes the first '/' segment as the workspace
83
+ // slug: raw '/Users/jvalluru/aladdin-v2' → 'Users' (wrong repo name,
84
+ // '~/workspace/Users' on the pod). Sending '/aladdin-v2' instead
85
+ // makes the pod resolve to '~/workspace/aladdin-v2' as expected.
86
+ // Router stores the cwd verbatim in BeamMeta; no other consumer parses
87
+ // the full laptop path structurally.
88
+ const normalizedCwd = `/${shortNameHint(hit.project_cwd)}`;
89
+ const finalizeBody = {
90
+ finalize_token: mint.finalize_token,
91
+ project_cwd: normalizedCwd,
92
+ // The verbatim laptop cwd the rollout was recorded under.
93
+ // Codex embeds this in session_meta + per-turn environment
94
+ // context; router uses it to stamp a hydration-plan sed rewrite
95
+ // (laptop cwd → pod workspace dir) so `codex resume --last`'s
96
+ // cwd filter matches. Claude is path-indexed and ignores this
97
+ // field. Sent for both kinds for wire uniformity.
98
+ original_cwd: hit.project_cwd,
99
+ name_hint: shortNameHint(hit.project_cwd),
100
+ agent_kind: hit.agent, // "claude" | "codex"
101
+ session_uuid: hit.session_uuid,
102
+ };
103
+ const finRes = await request(`${silo}/api/mcp/beam/from-local/mint/finalize`, {
104
+ method: "POST",
105
+ headers: {
106
+ "content-type": "application/json",
107
+ authorization: `Bearer ${cfg.token}`,
108
+ },
109
+ body: JSON.stringify(finalizeBody),
110
+ });
111
+ if (finRes.statusCode >= 400) {
112
+ const detail = await finRes.body.text().catch(() => "");
113
+ throw new Error(`beam finalize failed: ${finRes.statusCode} ${detail || ""}`);
114
+ }
115
+ const fin = (await finRes.body.json());
116
+ if (!fin.upload_token) {
117
+ throw new Error("beam finalize returned no upload_token");
118
+ }
119
+ // 6. Launch. Atomically creates the BeamMeta + boots the pod. Returns
120
+ // the AUTHORITATIVE beam_id (the mint-time id was a pre-reservation
121
+ // that this call may or may not honor — always trust the launch
122
+ // response). Same shape audit-web's composer uses when a user
123
+ // clicks "Beam" after a terminal upload.
124
+ const launchBody = {};
125
+ if (opts.model && opts.model.trim()) {
126
+ launchBody.model = opts.model.trim();
127
+ }
128
+ const launchRes = await request(`${silo}/api/mcp/beam/from-local/upload/${encodeURIComponent(fin.upload_token)}/launch`, {
129
+ method: "POST",
130
+ headers: {
131
+ "content-type": "application/json",
132
+ authorization: `Bearer ${cfg.token}`,
133
+ },
134
+ body: JSON.stringify(launchBody),
135
+ });
136
+ if (launchRes.statusCode >= 400) {
137
+ const detail = await launchRes.body.text().catch(() => "");
138
+ throw new Error(prettyLaunchError(launchRes.statusCode, detail, silo, agentKind));
139
+ }
140
+ const launch = (await launchRes.body.json());
141
+ if (!launch.beam_id) {
142
+ throw new Error("beam launch returned no beam_id");
143
+ }
144
+ const beam_id = launch.beam_id;
145
+ const session_uuid = launch.session_uuid || hit.session_uuid;
146
+ // Beam URL must use session_uuid (not beam_id) + ?view=sandbox —
147
+ // matches how audit-web's own composer builds URLs
148
+ // (NewBeamLanding.tsx:3510). A `/sessions/<beam_id>` URL half-loads
149
+ // but shows the wrong session view; the session_uuid form is the
150
+ // authoritative shape.
151
+ const beam_url = `${silo}/sessions/${encodeURIComponent(session_uuid)}?view=sandbox`;
152
+ return {
153
+ beam_id,
154
+ beam_url,
155
+ session_uuid,
156
+ harness,
157
+ };
158
+ }
159
+ async function putBytes(url, bytes, contentType) {
160
+ const res = await request(url, {
161
+ method: "PUT",
162
+ headers: { "content-type": contentType },
163
+ body: bytes,
164
+ });
165
+ if (res.statusCode >= 400) {
166
+ const detail = await res.body.text().catch(() => "");
167
+ throw new Error(`PUT failed: ${res.statusCode} ${detail || ""}`);
168
+ }
169
+ await res.body.dump().catch(() => { });
170
+ }
171
+ /// Deterministic empty gzipped tar. Uses `tar czf` via the system tar
172
+ /// so we don't add a heavy tar dep just for two ~1KB payloads. Falls
173
+ /// back to a precomputed 45-byte gzip-of-empty-tar if the tar binary
174
+ /// isn't available (Windows dev, no-tar containers).
175
+ let cachedEmptyTgz = null;
176
+ async function emptyGzipTar() {
177
+ if (cachedEmptyTgz)
178
+ return cachedEmptyTgz;
179
+ try {
180
+ const buf = await new Promise((resolve, reject) => {
181
+ const proc = spawn("tar", ["-cz", "-T", "/dev/null"], {
182
+ stdio: ["ignore", "pipe", "ignore"],
183
+ });
184
+ const chunks = [];
185
+ proc.stdout.on("data", (c) => chunks.push(c));
186
+ proc.on("error", reject);
187
+ proc.on("close", (code) => {
188
+ if (code === 0)
189
+ resolve(Buffer.concat(chunks));
190
+ else
191
+ reject(new Error(`tar exited ${code}`));
192
+ });
193
+ });
194
+ cachedEmptyTgz = buf;
195
+ return buf;
196
+ }
197
+ catch {
198
+ // Pre-baked empty gzipped tar (from `tar cz -T /dev/null | xxd`).
199
+ // 45 bytes; deterministic across systems.
200
+ cachedEmptyTgz = Buffer.from([
201
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xed, 0xc1,
202
+ 0x01, 0x0d, 0x00, 0x00, 0x00, 0xc2, 0xa0, 0xf7, 0x4f, 0x6d, 0x0e, 0x37,
203
+ 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x0d,
204
+ 0xd1, 0x83, 0x30, 0x00, 0x28, 0x00, 0x00,
205
+ ]);
206
+ return cachedEmptyTgz;
207
+ }
208
+ }
209
+ /// Format a router launch failure into a message the user can act on
210
+ /// without knowing the router's error taxonomy. The two common
211
+ /// preconditions (missing agent OAuth, missing GitLab) already surface
212
+ /// as recognizable JSON bodies — we recognize those and point at the
213
+ /// connector page directly. Anything else falls back to the raw status
214
+ /// + body so a debug session still has the wire detail.
215
+ function prettyLaunchError(status, bodyText, silo, agentKind) {
216
+ let parsed = null;
217
+ try {
218
+ parsed = JSON.parse(bodyText);
219
+ }
220
+ catch {
221
+ // Non-JSON body → fall through to the raw path.
222
+ }
223
+ const err = parsed?.error;
224
+ const connectorsUrl = `${silo}/connectors`;
225
+ if (status === 428 && err === "no_coding_agent") {
226
+ const kind = parsed?.agent_kind || agentKind;
227
+ const label = kind === "codex" ? "Codex" : "Claude";
228
+ return (`Beam blocked: your ${label} account is not connected to Myelin yet. ` +
229
+ `Open ${connectorsUrl} and click "Connect ${label}" (one-time OAuth). ` +
230
+ `Then retry beam_up.`);
231
+ }
232
+ if (status === 428 && err === "gitlab_not_connected") {
233
+ return (`Beam blocked: GitLab is not connected on this silo. ` +
234
+ `Open ${connectorsUrl} and connect GitLab, then retry beam_up. ` +
235
+ `(This only kicks in when you pass a repo — if you didn't, this is a router bug.)`);
236
+ }
237
+ if (status === 401) {
238
+ return (`Beam blocked: your Myelin bridge token is invalid or expired. ` +
239
+ `Run \`npx @granica/myelin-beam-mcp logout\` then re-run the \`authenticate\` tool.`);
240
+ }
241
+ // Fallback: keep the raw wire response so we can diagnose later.
242
+ return `beam launch failed: ${status} ${bodyText || ""}`;
243
+ }
244
+ /// Short human-readable label for the beam pill in the UI — just the
245
+ /// basename of the user's cwd. Matches how the audit-web composer
246
+ /// names beams so an MCP beam-up is visually indistinguishable in the
247
+ /// nav from a browser-composed beam.
248
+ function shortNameHint(cwd) {
249
+ const parts = cwd.split(/[\/\\]/).filter(Boolean);
250
+ return parts[parts.length - 1] || "beam";
251
+ }
252
+ //# sourceMappingURL=beamUp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"beamUp.js","sourceRoot":"","sources":["../src/beamUp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { AgentKind, McpConfig } from "./types.js";
2
+ export interface HarnessFile {
3
+ path: string;
4
+ sha256: string;
5
+ size: number;
6
+ }
7
+ export interface HarnessDiffResp {
8
+ signed_puts: Array<{
9
+ path: string;
10
+ url: string;
11
+ }>;
12
+ deletes: string[];
13
+ }
14
+ export interface HarnessSyncResult {
15
+ uploaded: number;
16
+ deleted: number;
17
+ unchanged: number;
18
+ total_files: number;
19
+ }
20
+ export declare function buildLocalManifest(agentKind?: AgentKind): Promise<HarnessFile[]>;
21
+ export declare function syncHarness(cfg: McpConfig, agentKind?: AgentKind): Promise<HarnessSyncResult>;
22
+ //# sourceMappingURL=harnessSync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harnessSync.d.ts","sourceRoot":"","sources":["../src/harnessSync.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AA+BvD,MAAM,WAAW,WAAW;IAE1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAMD,wBAAsB,kBAAkB,CACtC,SAAS,GAAE,SAAoB,GAC9B,OAAO,CAAC,WAAW,EAAE,CAAC,CAkCxB;AAWD,wBAAsB,WAAW,CAC/B,GAAG,EAAE,SAAS,EACd,SAAS,GAAE,SAAoB,GAC9B,OAAO,CAAC,iBAAiB,CAAC,CAsD5B"}
@@ -0,0 +1,199 @@
1
+ import { promises as fs } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join, relative, sep } from "node:path";
4
+ import { request } from "undici";
5
+ import { sha256File } from "./rollout.js";
6
+ /// Harness allowlist — matches the router-side enforcer in
7
+ /// services/router/src/handlers/mcp_harness.rs::ALLOWED_PREFIXES.
8
+ /// Bridge and router agree on the same allowlist so a compromised
9
+ /// bridge can't stuff arbitrary paths, and a well-behaved bridge
10
+ /// doesn't waste manifest slots on files the router would reject.
11
+ ///
12
+ /// Portable-user-content only — no env-specific config, no secrets.
13
+ /// See router-side comment for the exclusion rationale.
14
+ const ALLOWED_DIRS = ["skills", "agents", "commands"];
15
+ /// Per-agent top-level allowlist. Claude uses CLAUDE.md, Codex uses
16
+ /// AGENTS.md — same shape, different filename. Both agents' auxiliary
17
+ /// config files (settings.local.json, config.toml, auth.json) are
18
+ /// deliberately excluded.
19
+ const CLAUDE_ALLOWED_FILES = ["CLAUDE.md"];
20
+ const CODEX_ALLOWED_FILES = ["AGENTS.md"];
21
+ /// Root directory + top-level allowlist per agent kind. Both agents
22
+ /// share the ALLOWED_DIRS shape (skills/, agents/, commands/) — only
23
+ /// the root path and the exact-match top-level files differ.
24
+ function harnessRoot(agentKind) {
25
+ if (agentKind === "codex") {
26
+ return { root: join(homedir(), ".codex"), allowedFiles: CODEX_ALLOWED_FILES };
27
+ }
28
+ return { root: join(homedir(), ".claude"), allowedFiles: CLAUDE_ALLOWED_FILES };
29
+ }
30
+ /// File-size cap in bytes. Matches router-side MAX_FILE_BYTES.
31
+ const MAX_FILE_BYTES = 5 * 1024 * 1024;
32
+ /// Walk the local harness root (`~/.claude/` for claude, `~/.codex/`
33
+ /// for codex), hash each file in the per-agent allowlist, return
34
+ /// the manifest. Silently skips oversize files (documented cap) —
35
+ /// better to sync partial than reject the whole batch.
36
+ export async function buildLocalManifest(agentKind = "claude") {
37
+ const { root, allowedFiles } = harnessRoot(agentKind);
38
+ const files = [];
39
+ // Exact-match top-level files.
40
+ for (const name of allowedFiles) {
41
+ const p = join(root, name);
42
+ const meta = await tryStat(p);
43
+ if (meta && meta.size <= MAX_FILE_BYTES) {
44
+ files.push({
45
+ path: name,
46
+ sha256: await sha256File(p),
47
+ size: meta.size,
48
+ });
49
+ }
50
+ }
51
+ // Recursive directories.
52
+ for (const dir of ALLOWED_DIRS) {
53
+ const dirRoot = join(root, dir);
54
+ const listing = await tryReadDirRecursive(dirRoot);
55
+ for (const absPath of listing) {
56
+ const meta = await tryStat(absPath);
57
+ if (!meta || meta.size > MAX_FILE_BYTES)
58
+ continue;
59
+ const rel = relative(root, absPath).split(sep).join("/");
60
+ files.push({
61
+ path: rel,
62
+ sha256: await sha256File(absPath),
63
+ size: meta.size,
64
+ });
65
+ }
66
+ }
67
+ return files;
68
+ }
69
+ /// Diff manifest against router, upload changed files, finalize.
70
+ /// Returns a summary with counts for the tool result.
71
+ ///
72
+ /// `agentKind` selects the local harness root AND the per-agent
73
+ /// GCS prefix (`harness/<kind>/`) the router uploads land under.
74
+ /// Claude and Codex have separate manifests, separate toggle maps,
75
+ /// and separate storage — a single user with both agents installed
76
+ /// keeps two independent harness sets, mirroring the two tabs the
77
+ /// /skills UI renders.
78
+ export async function syncHarness(cfg, agentKind = "claude") {
79
+ const local = await buildLocalManifest(agentKind);
80
+ const silo = cfg.silo_audit_url.replace(/\/+$/, "");
81
+ const kindQuery = `?agent_kind=${encodeURIComponent(agentKind)}`;
82
+ // Diff.
83
+ const diffRes = await request(`${silo}/api/mcp/harness/manifest${kindQuery}`, {
84
+ method: "POST",
85
+ headers: {
86
+ "content-type": "application/json",
87
+ authorization: `Bearer ${cfg.token}`,
88
+ },
89
+ body: JSON.stringify({ files: local }),
90
+ });
91
+ if (diffRes.statusCode >= 400) {
92
+ const detail = await diffRes.body.text().catch(() => "");
93
+ throw new Error(`harness manifest diff failed: ${diffRes.statusCode} ${detail || ""}`);
94
+ }
95
+ const diff = (await diffRes.body.json());
96
+ // PUT changed files in parallel (small pool — laptop uploads).
97
+ const puts = diff.signed_puts.map((entry) => uploadOne(entry, agentKind));
98
+ await runPool(puts, 8);
99
+ // Finalize.
100
+ const finRes = await request(`${silo}/api/mcp/harness/manifest/finalize${kindQuery}`, {
101
+ method: "POST",
102
+ headers: {
103
+ "content-type": "application/json",
104
+ authorization: `Bearer ${cfg.token}`,
105
+ },
106
+ body: JSON.stringify({ files: local }),
107
+ });
108
+ if (finRes.statusCode >= 400) {
109
+ const detail = await finRes.body.text().catch(() => "");
110
+ throw new Error(`harness manifest finalize failed: ${finRes.statusCode} ${detail || ""}`);
111
+ }
112
+ return {
113
+ uploaded: diff.signed_puts.length,
114
+ deleted: diff.deletes.length,
115
+ unchanged: local.length - diff.signed_puts.length,
116
+ total_files: local.length,
117
+ };
118
+ }
119
+ /// Wrap a single file upload into a thunk (deferred so the pool can
120
+ /// throttle concurrency without every fetch firing at once).
121
+ function uploadOne(entry, agentKind) {
122
+ return async () => {
123
+ const { root } = harnessRoot(agentKind);
124
+ const abs = join(root, entry.path);
125
+ const bytes = await fs.readFile(abs);
126
+ const res = await request(entry.url, {
127
+ method: "PUT",
128
+ headers: { "content-type": "application/octet-stream" },
129
+ body: bytes,
130
+ });
131
+ if (res.statusCode >= 400) {
132
+ const detail = await res.body.text().catch(() => "");
133
+ throw new Error(`PUT ${entry.path} failed: ${res.statusCode} ${detail || ""}`);
134
+ }
135
+ // Discard body to release the socket.
136
+ await res.body.dump().catch(() => { });
137
+ };
138
+ }
139
+ async function runPool(thunks, concurrency) {
140
+ const iterator = thunks[Symbol.iterator]();
141
+ const workers = Array.from({ length: Math.min(concurrency, thunks.length) })
142
+ .map(async () => {
143
+ while (true) {
144
+ const next = iterator.next();
145
+ if (next.done)
146
+ return;
147
+ await next.value();
148
+ }
149
+ });
150
+ await Promise.all(workers);
151
+ }
152
+ async function tryStat(path) {
153
+ try {
154
+ const st = await fs.stat(path);
155
+ if (!st.isFile())
156
+ return null;
157
+ return { size: st.size };
158
+ }
159
+ catch {
160
+ return null;
161
+ }
162
+ }
163
+ async function tryReadDirRecursive(root) {
164
+ const out = [];
165
+ async function walk(dir) {
166
+ let entries;
167
+ try {
168
+ entries = await fs.readdir(dir);
169
+ }
170
+ catch {
171
+ return;
172
+ }
173
+ for (const name of entries) {
174
+ // Skip hidden files/directories (dot-files) — never intended for
175
+ // harness sync. This also silently drops .git, .DS_Store, and any
176
+ // secret files (`.credentials.json` etc.) as a defense-in-depth
177
+ // layer alongside the router's allowlist.
178
+ if (name.startsWith("."))
179
+ continue;
180
+ const p = join(dir, name);
181
+ let stat;
182
+ try {
183
+ stat = await fs.stat(p);
184
+ }
185
+ catch {
186
+ continue;
187
+ }
188
+ if (stat.isDirectory()) {
189
+ await walk(p);
190
+ }
191
+ else if (stat.isFile()) {
192
+ out.push(p);
193
+ }
194
+ }
195
+ }
196
+ await walk(root);
197
+ return out;
198
+ }
199
+ //# sourceMappingURL=harnessSync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harnessSync.js","sourceRoot":"","sources":["../src/harnessSync.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { CONFIG_PATH, loadConfig, saveConfig, clearConfig, startAuth, type AuthStart, type AuthCallbackResult, } from "./auth.js";
2
+ export type { McpConfig, AgentKind } from "./types.js";
3
+ export { beamUp, type BeamUpResult, type BeamUpOptions } from "./beamUp.js";
4
+ export { syncHarness, buildLocalManifest, type HarnessFile, type HarnessSyncResult, } from "./harnessSync.js";
5
+ export { findCurrentClaudeRollout, findCurrentCodexRollout, encodeCwdForClaude, trimIncompleteTail, type RolloutHit, } from "./rollout.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,UAAU,EACV,UAAU,EACV,WAAW,EACX,SAAS,EACT,KAAK,SAAS,EACd,KAAK,kBAAkB,GACxB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,KAAK,WAAW,EAChB,KAAK,iBAAiB,GACvB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,wBAAwB,EACxB,uBAAuB,EACvB,kBAAkB,EAClB,kBAAkB,EAClB,KAAK,UAAU,GAChB,MAAM,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { CONFIG_PATH, loadConfig, saveConfig, clearConfig, startAuth, } from "./auth.js";
2
+ export { beamUp } from "./beamUp.js";
3
+ export { syncHarness, buildLocalManifest, } from "./harnessSync.js";
4
+ export { findCurrentClaudeRollout, findCurrentCodexRollout, encodeCwdForClaude, trimIncompleteTail, } from "./rollout.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,UAAU,EACV,UAAU,EACV,WAAW,EACX,SAAS,GAGV,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,MAAM,EAAyC,MAAM,aAAa,CAAC;AAC5E,OAAO,EACL,WAAW,EACX,kBAAkB,GAGnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,wBAAwB,EACxB,uBAAuB,EACvB,kBAAkB,EAClB,kBAAkB,GAEnB,MAAM,cAAc,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { AgentKind } from "./types.js";
2
+ export interface RolloutHit {
3
+ agent: "claude" | "codex";
4
+ path: string;
5
+ session_uuid: string;
6
+ project_cwd: string;
7
+ }
8
+ export declare function encodeCwdForClaude(cwd: string): string;
9
+ export declare function findCurrentClaudeRollout(cwd?: string): Promise<RolloutHit | null>;
10
+ export declare function findCurrentCodexRollout(cwd?: string): Promise<RolloutHit | null>;
11
+ export declare function readRolloutBytes(hit: RolloutHit): Promise<Buffer>;
12
+ export declare function trimIncompleteTail(bytes: Buffer, agentKind: AgentKind): Buffer;
13
+ export declare function sha256File(path: string): Promise<string>;
14
+ //# sourceMappingURL=rollout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rollout.d.ts","sourceRoot":"","sources":["../src/rollout.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAmB5C,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC;IAE1B,IAAI,EAAE,MAAM,CAAC;IAGb,YAAY,EAAE,MAAM,CAAC;IAErB,WAAW,EAAE,MAAM,CAAC;CACrB;AAID,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAItD;AAKD,wBAAsB,wBAAwB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAgCvF;AAiBD,wBAAsB,uBAAuB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAiCtF;AA+ED,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAEvE;AAkBD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,MAAM,CAK9E;AAuGD,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAK9D"}