@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/LICENSE +202 -0
- package/README.md +44 -0
- package/dist/auth.d.ts +17 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +286 -0
- package/dist/auth.js.map +1 -0
- package/dist/beamUp.d.ts +14 -0
- package/dist/beamUp.d.ts.map +1 -0
- package/dist/beamUp.js +252 -0
- package/dist/beamUp.js.map +1 -0
- package/dist/harnessSync.d.ts +22 -0
- package/dist/harnessSync.d.ts.map +1 -0
- package/dist/harnessSync.js +199 -0
- package/dist/harnessSync.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/rollout.d.ts +14 -0
- package/dist/rollout.d.ts.map +1 -0
- package/dist/rollout.js +319 -0
- package/dist/rollout.js.map +1 -0
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +55 -0
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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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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"}
|