midsummer-sol 0.3.4 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/package.json +1 -1
  2. package/sol-mcp.js +201 -19
  3. package/sol.js +239 -31
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "midsummer-sol",
3
- "version": "0.3.4",
3
+ "version": "0.3.7",
4
4
  "description": "Sol — agent-native version control (a new git). CLI, MCP server, and no-filesystem SDK.",
5
5
  "bin": { "sol": "./sol.js", "sol-mcp": "./sol-mcp.js", "sol-secret-mcp": "./sol-secret-mcp.js" },
6
6
  "main": "./index.js",
package/sol-mcp.js CHANGED
@@ -3036,6 +3036,7 @@ __export(exports_lib, {
3036
3036
  allLocalNodes: () => allLocalNodes,
3037
3037
  addInclude: () => addInclude,
3038
3038
  actor: () => actor,
3039
+ acquireLockAt: () => acquireLockAt,
3039
3040
  acquireLock: () => acquireLock,
3040
3041
  LazyStore: () => LazyStore,
3041
3042
  DEFAULT_IGNORE: () => DEFAULT_IGNORE
@@ -3269,9 +3270,9 @@ function pidAlive(pid) {
3269
3270
  return e?.code === "EPERM";
3270
3271
  }
3271
3272
  }
3272
- function acquireLock() {
3273
- const lockFile = join5(lockDir(), "lock");
3274
- const deadline = Date.now() + 15000;
3273
+ function acquireLockAt(lockFile, opts = {}) {
3274
+ const deadline = Date.now() + (opts.timeoutMs ?? 15000);
3275
+ const onTimeout = opts.onTimeout ?? "die";
3275
3276
  for (;; ) {
3276
3277
  try {
3277
3278
  writeFileSync5(lockFile, String(process.pid), { flag: "wx" });
@@ -3294,8 +3295,11 @@ function acquireLock() {
3294
3295
  } catch {
3295
3296
  continue;
3296
3297
  }
3297
- if (Date.now() > deadline)
3298
- die("repo is locked by another sol process (timed out)");
3298
+ if (Date.now() > deadline) {
3299
+ if (onTimeout === "undefined")
3300
+ return;
3301
+ die(opts.timeoutMsg ?? "repo is locked by another sol process (timed out)");
3302
+ }
3299
3303
  sleepSync(25);
3300
3304
  }
3301
3305
  }
@@ -3311,6 +3315,9 @@ function acquireLock() {
3311
3315
  process.on("exit", release);
3312
3316
  return release;
3313
3317
  }
3318
+ function acquireLock() {
3319
+ return acquireLockAt(join5(lockDir(), "lock"));
3320
+ }
3314
3321
  function loadStore() {
3315
3322
  return new LazyStore(objectsDir());
3316
3323
  }
@@ -4156,9 +4163,12 @@ var init_lib = __esm(() => {
4156
4163
  var exports_remote = {};
4157
4164
  __export(exports_remote, {
4158
4165
  writeBundle: () => writeBundle,
4166
+ splitLeafPacks: () => splitLeafPacks,
4159
4167
  saveRemote: () => saveRemote,
4160
4168
  remoteRefs: () => remoteRefs,
4161
4169
  remotePushPack: () => remotePushPack,
4170
+ remotePushGiantLeaf: () => remotePushGiantLeaf,
4171
+ remotePushBlobs: () => remotePushBlobs,
4162
4172
  remotePush: () => remotePush,
4163
4173
  remotePromote: () => remotePromote,
4164
4174
  remoteHead: () => remoteHead,
@@ -4182,12 +4192,13 @@ __export(exports_remote, {
4182
4192
  loadRemote: () => loadRemote,
4183
4193
  forksList: () => forksList,
4184
4194
  forkMeta: () => forkMeta,
4195
+ classifyNodes: () => classifyNodes,
4185
4196
  accessSet: () => accessSet,
4186
4197
  accessGet: () => accessGet
4187
4198
  });
4188
4199
  import { appendFileSync as appendFileSync4, existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "node:fs";
4189
4200
  import { join as join6 } from "node:path";
4190
- import { gzipSync as gzipSync3 } from "node:zlib";
4201
+ import { gunzipSync as gunzipSync3, gzipSync as gzipSync3 } from "node:zlib";
4191
4202
  async function call(cfg, token, path, init) {
4192
4203
  const res = await fetch(endpoint(cfg, path), {
4193
4204
  ...init,
@@ -4197,6 +4208,147 @@ async function call(cfg, token, path, init) {
4197
4208
  throw new Error(`remote ${path} -> ${res.status}: ${(await res.text().catch(() => "")).slice(0, 200)}`);
4198
4209
  return res.json();
4199
4210
  }
4211
+ function decodePackLine(line) {
4212
+ const map = JSON.parse(gunzipSync3(Buffer.from(line, "base64")).toString("utf8"));
4213
+ return Object.values(map);
4214
+ }
4215
+ async function exportStreaming(cfg, token) {
4216
+ const res = await fetch(endpoint(cfg, "/export?stream=1"), {
4217
+ headers: { authorization: `Bearer ${token}`, "content-type": "application/json" }
4218
+ });
4219
+ if (!res.ok)
4220
+ throw new Error(`remote /export -> ${res.status}: ${(await res.text().catch(() => "")).slice(0, 200)}`);
4221
+ if (!res.body)
4222
+ return res.json();
4223
+ const reader = res.body.getReader();
4224
+ const td = new TextDecoder;
4225
+ let buf = "";
4226
+ let header;
4227
+ const nodes = [];
4228
+ const consumeLine = (line) => {
4229
+ if (line === "")
4230
+ return;
4231
+ if (!header) {
4232
+ header = JSON.parse(line);
4233
+ if (Array.isArray(header.nodes))
4234
+ for (const n of header.nodes)
4235
+ nodes.push(n);
4236
+ return;
4237
+ }
4238
+ for (const n of decodePackLine(line))
4239
+ nodes.push(n);
4240
+ };
4241
+ for (;; ) {
4242
+ const { done, value } = await reader.read();
4243
+ if (value)
4244
+ buf += td.decode(value, { stream: true });
4245
+ let nl;
4246
+ while ((nl = buf.indexOf(`
4247
+ `)) >= 0) {
4248
+ consumeLine(buf.slice(0, nl));
4249
+ buf = buf.slice(nl + 1);
4250
+ }
4251
+ if (done)
4252
+ break;
4253
+ }
4254
+ buf += td.decode();
4255
+ if (buf.length)
4256
+ consumeLine(buf);
4257
+ if (!header)
4258
+ throw new Error("remote /export returned an empty stream");
4259
+ return { ...header, nodes };
4260
+ }
4261
+ function classifyNodes(nodes) {
4262
+ const trees = [];
4263
+ const leaves = [];
4264
+ let total = 0;
4265
+ for (const n of nodes) {
4266
+ total += nodeBytes(n);
4267
+ (n.kind === "tree" ? trees : leaves).push(n);
4268
+ }
4269
+ return { trees, leaves, total };
4270
+ }
4271
+ function splitLeafPacks(leaves, maxBytes) {
4272
+ const packs = [];
4273
+ let cur = [];
4274
+ let size = 0;
4275
+ for (const n of leaves) {
4276
+ const b = nodeBytes(n);
4277
+ if (cur.length && size + b > maxBytes) {
4278
+ packs.push(cur);
4279
+ cur = [];
4280
+ size = 0;
4281
+ }
4282
+ cur.push(n);
4283
+ size += b;
4284
+ }
4285
+ if (cur.length)
4286
+ packs.push(cur);
4287
+ return packs;
4288
+ }
4289
+ async function pool(tasks, limit) {
4290
+ const out = new Array(tasks.length);
4291
+ let next = 0;
4292
+ const worker = async () => {
4293
+ for (;; ) {
4294
+ const i = next++;
4295
+ if (i >= tasks.length)
4296
+ return;
4297
+ out[i] = await tasks[i]();
4298
+ }
4299
+ };
4300
+ await Promise.all(Array.from({ length: Math.min(limit, tasks.length) }, worker));
4301
+ return out;
4302
+ }
4303
+ async function retryTransient(fn, attempts = 5) {
4304
+ let last;
4305
+ for (let i = 0;i < attempts; i++) {
4306
+ try {
4307
+ return await fn();
4308
+ } catch (e) {
4309
+ last = e;
4310
+ const m = e instanceof Error ? e.message : String(e);
4311
+ if (!/-> 5dd|reset|fetch failed|network|ECONN|terminated|timeout/i.test(m))
4312
+ throw e;
4313
+ await new Promise((r) => setTimeout(r, 400 * (i + 1)));
4314
+ }
4315
+ }
4316
+ throw last;
4317
+ }
4318
+ function canonicalPackBytesClient(entries) {
4319
+ const sorted = [...entries].sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
4320
+ const obj = {};
4321
+ for (const [h, n] of sorted)
4322
+ obj[h] = n;
4323
+ return JSON.stringify(obj);
4324
+ }
4325
+ async function remotePushGiantLeaf(cfg, token, node) {
4326
+ const hash = hashNode(node);
4327
+ const body = canonicalPackBytesClient([[hash, node]]);
4328
+ const packId = hashString(body);
4329
+ const packBody = gzipSync3(Buffer.from(body, "utf8")).toString("base64");
4330
+ const path = `/push/blobs?giant=${encodeURIComponent(hash)}&packId=${encodeURIComponent(packId)}`;
4331
+ return retryTransient(() => call(cfg, token, path, { method: "POST", body: packBody }));
4332
+ }
4333
+ async function remotePushPack(cfg, token, body) {
4334
+ const { trees, leaves, total } = classifyNodes(body.nodes);
4335
+ if (total <= SMALL_PUSH_BYTES) {
4336
+ return retryTransient(() => call(cfg, token, "/push/pack", { method: "POST", body: gzipSync3(JSON.stringify(body)), headers: { "content-encoding": "gzip" } }));
4337
+ }
4338
+ const giants = leaves.filter((n) => nodeBytes(n) > GIANT_LEAF_BYTES);
4339
+ const normal = leaves.filter((n) => nodeBytes(n) <= GIANT_LEAF_BYTES);
4340
+ const blobPacks = splitLeafPacks(normal, BLOB_PACK_BYTES);
4341
+ const tasks = [
4342
+ ...blobPacks.map((pack) => () => remotePushBlobs(cfg, token, pack)),
4343
+ ...giants.map((node) => () => remotePushGiantLeaf(cfg, token, node))
4344
+ ];
4345
+ await pool(tasks, BLOB_PACK_CONCURRENCY);
4346
+ return retryTransient(() => call(cfg, token, "/push/pack", {
4347
+ method: "POST",
4348
+ body: gzipSync3(JSON.stringify({ ...body, nodes: trees })),
4349
+ headers: { "content-encoding": "gzip" }
4350
+ }));
4351
+ }
4200
4352
  function loadRemote(solDir2) {
4201
4353
  const p = join6(solDir2, "remote.json");
4202
4354
  return existsSync6(p) ? JSON.parse(readFileSync6(p, "utf8")) : undefined;
@@ -4217,9 +4369,13 @@ async function writeBundle(solDir2, bundle, from = 0) {
4217
4369
  writeFileSync6(join6(solDir2, "HEAD"), JSON.stringify({ head: bundle.head, seq: bundle.seq, logTip: bundle.tip }));
4218
4370
  return fresh.length;
4219
4371
  }
4220
- var endpoint = (cfg, path) => `${cfg.url.replace(/\/+$/, "")}${path}${path.includes("?") ? "&" : "?"}repo=${encodeURIComponent(cfg.repo)}`, remoteHead = (cfg, token) => call(cfg, token, "/head"), remoteGate = (cfg, token, branch) => call(cfg, token, `/head?gateState=true${branch ? `&branch=${encodeURIComponent(branch)}` : ""}`), remoteExport = (cfg, token) => call(cfg, token, "/export"), remoteRefs = (cfg, token) => call(cfg, token, "/refs"), remotePush = (cfg, token, body) => call(cfg, token, "/push", { method: "POST", body: JSON.stringify(body) }), remotePromote = (cfg, token, branch) => call(cfg, token, "/promote", { method: "POST", body: JSON.stringify({ branch }) }), remotePushPack = (cfg, token, body) => call(cfg, token, "/push/pack", { method: "POST", body: gzipSync3(JSON.stringify(body)), headers: { "content-encoding": "gzip" } }), mrOpen = (cfg, token, body) => call(cfg, token, "/mr", { method: "POST", body: JSON.stringify(body) }), mrList = (cfg, token) => call(cfg, token, "/mrs"), mrDiff = (cfg, token, id) => call(cfg, token, `/mr/diff?id=${id}`), mrGet = (cfg, token, id) => call(cfg, token, `/mr?id=${id}`), mrAction = (cfg, token, action, body) => call(cfg, token, `/mr/${action}`, { method: "POST", body: JSON.stringify(body) }), accessGet = (cfg, token) => call(cfg, token, "/access"), ownerSet = (cfg, token, ownerHandle) => call(cfg, token, "/owner", { method: "POST", body: JSON.stringify({ ownerHandle }) }), accessSet = (cfg, token, body) => call(cfg, token, "/access", { method: "POST", body: JSON.stringify(body) }), policyCheck = (cfg, token, paths) => call(cfg, token, "/policy/check", { method: "POST", body: JSON.stringify({ paths }) }), policyGet = (cfg, token) => call(cfg, token, "/policy"), policyUpsert = (cfg, token, rule) => call(cfg, token, "/policy?write=1", { method: "POST", body: JSON.stringify({ op: "upsert", rule }) }), policyRemove = (cfg, token, pattern) => call(cfg, token, "/policy?write=1", { method: "POST", body: JSON.stringify({ op: "remove", pattern }) }), recipientsForPath = (cfg, token, path) => call(cfg, token, `/recipients?path=${encodeURIComponent(path)}`), recipientsForAudience = (cfg, token, audience, recovery) => call(cfg, token, `/recipients?audience=${encodeURIComponent(JSON.stringify(audience))}${recovery?.length ? `&recovery=${encodeURIComponent(recovery.join(","))}` : ""}`), remoteEnvPush = (cfg, token, bundle) => call(cfg, token, "/env/push", { method: "POST", body: JSON.stringify(bundle) }), remoteEnvAnchor = (cfg, token) => call(cfg, token, "/env/anchor"), remoteEnvPull = (cfg, token) => call(cfg, token, "/env/pull"), forkMeta = (cfg, token, parent) => call(cfg, token, "/fork-meta", { method: "POST", body: JSON.stringify({ parent }) }), forksList = (cfg, token) => call(cfg, token, "/forks"), saveRemote = (solDir2, cfg) => writeFileSync6(join6(solDir2, "remote.json"), JSON.stringify(cfg, null, 2));
4372
+ var endpoint = (cfg, path) => `${cfg.url.replace(/\/+$/, "")}${path}${path.includes("?") ? "&" : "?"}repo=${encodeURIComponent(cfg.repo)}`, remoteHead = (cfg, token) => call(cfg, token, "/head"), remoteGate = (cfg, token, branch) => call(cfg, token, `/head?gateState=true${branch ? `&branch=${encodeURIComponent(branch)}` : ""}`), remoteExport = (cfg, token) => exportStreaming(cfg, token), remoteRefs = (cfg, token) => call(cfg, token, "/refs"), remotePush = (cfg, token, body) => call(cfg, token, "/push", { method: "POST", body: JSON.stringify(body) }), remotePromote = (cfg, token, branch) => call(cfg, token, "/promote", { method: "POST", body: JSON.stringify({ branch }) }), SMALL_PUSH_BYTES, BLOB_PACK_BYTES, BLOB_PACK_CONCURRENCY = 6, GIANT_LEAF_BYTES, nodeBytes = (n) => JSON.stringify(n).length, remotePushBlobs = (cfg, token, nodes) => retryTransient(() => call(cfg, token, "/push/blobs", { method: "POST", body: gzipSync3(JSON.stringify({ nodes })), headers: { "content-encoding": "gzip" } })), mrOpen = (cfg, token, body) => call(cfg, token, "/mr", { method: "POST", body: JSON.stringify(body) }), mrList = (cfg, token) => call(cfg, token, "/mrs"), mrDiff = (cfg, token, id) => call(cfg, token, `/mr/diff?id=${id}`), mrGet = (cfg, token, id) => call(cfg, token, `/mr?id=${id}`), mrAction = (cfg, token, action, body) => call(cfg, token, `/mr/${action}`, { method: "POST", body: JSON.stringify(body) }), accessGet = (cfg, token) => call(cfg, token, "/access"), ownerSet = (cfg, token, ownerHandle) => call(cfg, token, "/owner", { method: "POST", body: JSON.stringify({ ownerHandle }) }), accessSet = (cfg, token, body) => call(cfg, token, "/access", { method: "POST", body: JSON.stringify(body) }), policyCheck = (cfg, token, paths) => call(cfg, token, "/policy/check", { method: "POST", body: JSON.stringify({ paths }) }), policyGet = (cfg, token) => call(cfg, token, "/policy"), policyUpsert = (cfg, token, rule) => call(cfg, token, "/policy?write=1", { method: "POST", body: JSON.stringify({ op: "upsert", rule }) }), policyRemove = (cfg, token, pattern) => call(cfg, token, "/policy?write=1", { method: "POST", body: JSON.stringify({ op: "remove", pattern }) }), recipientsForPath = (cfg, token, path) => call(cfg, token, `/recipients?path=${encodeURIComponent(path)}`), recipientsForAudience = (cfg, token, audience, recovery) => call(cfg, token, `/recipients?audience=${encodeURIComponent(JSON.stringify(audience))}${recovery?.length ? `&recovery=${encodeURIComponent(recovery.join(","))}` : ""}`), remoteEnvPush = (cfg, token, bundle) => call(cfg, token, "/env/push", { method: "POST", body: JSON.stringify(bundle) }), remoteEnvAnchor = (cfg, token) => call(cfg, token, "/env/anchor"), remoteEnvPull = (cfg, token) => call(cfg, token, "/env/pull"), forkMeta = (cfg, token, parent) => call(cfg, token, "/fork-meta", { method: "POST", body: JSON.stringify({ parent }) }), forksList = (cfg, token) => call(cfg, token, "/forks"), saveRemote = (solDir2, cfg) => writeFileSync6(join6(solDir2, "remote.json"), JSON.stringify(cfg, null, 2));
4221
4373
  var init_remote = __esm(() => {
4222
4374
  init_file_store2();
4375
+ init_store();
4376
+ SMALL_PUSH_BYTES = 24 * 1024 * 1024;
4377
+ BLOB_PACK_BYTES = 16 * 1024 * 1024;
4378
+ GIANT_LEAF_BYTES = 24 * 1024 * 1024;
4223
4379
  });
4224
4380
 
4225
4381
  // src/bin/test-gate.ts
@@ -9620,21 +9776,41 @@ function tokenClaims(token) {
9620
9776
  return {};
9621
9777
  }
9622
9778
  }
9623
- async function loadStoredToken() {
9624
- if (!existsSync20(CRED_PATH))
9625
- return;
9626
- let creds;
9779
+ function readCreds() {
9627
9780
  try {
9628
- creds = JSON.parse(readFileSync20(CRED_PATH, "utf8"));
9781
+ return JSON.parse(readFileSync20(CRED_PATH, "utf8"));
9629
9782
  } catch {
9630
9783
  return;
9631
9784
  }
9632
- if (!creds.accessToken)
9785
+ }
9786
+ function accessTokenFresh(accessToken) {
9787
+ if (!accessToken)
9788
+ return false;
9789
+ const exp = tokenClaims(accessToken).exp;
9790
+ return typeof exp === "number" && exp * 1000 > Date.now() + 30000;
9791
+ }
9792
+ async function loadStoredToken() {
9793
+ if (!existsSync20(CRED_PATH))
9633
9794
  return;
9634
- const exp = tokenClaims(creds.accessToken).exp;
9635
- if (typeof exp === "number" && exp * 1000 > Date.now() + 30000)
9795
+ let creds = readCreds();
9796
+ if (!creds?.accessToken)
9797
+ return;
9798
+ if (accessTokenFresh(creds.accessToken))
9799
+ return creds.accessToken;
9800
+ if (!(creds.refreshToken && creds.webUrl))
9636
9801
  return creds.accessToken;
9637
- if (creds.refreshToken && creds.webUrl) {
9802
+ const release = acquireLockAt(join19(dirname5(CRED_PATH), "credentials.lock"), {
9803
+ timeoutMs: 1e4,
9804
+ onTimeout: "undefined"
9805
+ });
9806
+ try {
9807
+ if (release) {
9808
+ const fresh = readCreds();
9809
+ if (fresh?.accessToken && accessTokenFresh(fresh.accessToken))
9810
+ return fresh.accessToken;
9811
+ if (fresh)
9812
+ creds = fresh;
9813
+ }
9638
9814
  try {
9639
9815
  const res = await fetch(`${creds.webUrl}/api/auth/refresh`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ refreshToken: creds.refreshToken }) });
9640
9816
  if (res.ok) {
@@ -9645,8 +9821,10 @@ async function loadStoredToken() {
9645
9821
  }
9646
9822
  }
9647
9823
  } catch {}
9824
+ return creds.accessToken;
9825
+ } finally {
9826
+ release?.();
9648
9827
  }
9649
- return creds.accessToken;
9650
9828
  }
9651
9829
  function authExpired() {
9652
9830
  return die("session expired — run `sol auth login` (or set SOL_TOKEN)");
@@ -12389,8 +12567,12 @@ WARNING: "${path}" was committed as PLAINTEXT before this seal — the pre-seal
12389
12567
  const days = Number(patAction) || 90;
12390
12568
  const name = (Number(patAction) ? args[2] : patAction) || undefined;
12391
12569
  const res = await fetch(`${creds.webUrl}/api/auth/pat`, { method: "POST", headers: { authorization: `Bearer ${token}`, "content-type": "application/json" }, body: JSON.stringify({ days, ...name ? { name } : {} }) });
12392
- if (!res.ok)
12393
- die(`could not mint a PAT (${res.status})`);
12570
+ if (!res.ok) {
12571
+ const reason = await res.json().then((b) => b?.error?.detail || b?.error?.message).catch(() => {
12572
+ return;
12573
+ });
12574
+ die(`could not mint a PAT (${res.status})${reason ? `: ${reason}` : ""}`);
12575
+ }
12394
12576
  const out = await res.json();
12395
12577
  console.log(`
12396
12578
  Personal Access Token "${out.name}" (id ${out.id}, expires in ${days} days) — store it now, it is not shown again:
package/sol.js CHANGED
@@ -2269,6 +2269,7 @@ __export(exports_lib, {
2269
2269
  allLocalNodes: () => allLocalNodes,
2270
2270
  addInclude: () => addInclude,
2271
2271
  actor: () => actor,
2272
+ acquireLockAt: () => acquireLockAt,
2272
2273
  acquireLock: () => acquireLock,
2273
2274
  LazyStore: () => LazyStore,
2274
2275
  DEFAULT_IGNORE: () => DEFAULT_IGNORE
@@ -2502,9 +2503,9 @@ function pidAlive(pid) {
2502
2503
  return e?.code === "EPERM";
2503
2504
  }
2504
2505
  }
2505
- function acquireLock() {
2506
- const lockFile = join4(lockDir(), "lock");
2507
- const deadline = Date.now() + 15000;
2506
+ function acquireLockAt(lockFile, opts = {}) {
2507
+ const deadline = Date.now() + (opts.timeoutMs ?? 15000);
2508
+ const onTimeout = opts.onTimeout ?? "die";
2508
2509
  for (;; ) {
2509
2510
  try {
2510
2511
  writeFileSync4(lockFile, String(process.pid), { flag: "wx" });
@@ -2527,8 +2528,11 @@ function acquireLock() {
2527
2528
  } catch {
2528
2529
  continue;
2529
2530
  }
2530
- if (Date.now() > deadline)
2531
- die("repo is locked by another sol process (timed out)");
2531
+ if (Date.now() > deadline) {
2532
+ if (onTimeout === "undefined")
2533
+ return;
2534
+ die(opts.timeoutMsg ?? "repo is locked by another sol process (timed out)");
2535
+ }
2532
2536
  sleepSync(25);
2533
2537
  }
2534
2538
  }
@@ -2544,6 +2548,9 @@ function acquireLock() {
2544
2548
  process.on("exit", release);
2545
2549
  return release;
2546
2550
  }
2551
+ function acquireLock() {
2552
+ return acquireLockAt(join4(lockDir(), "lock"));
2553
+ }
2547
2554
  function loadStore() {
2548
2555
  return new LazyStore(objectsDir());
2549
2556
  }
@@ -3389,9 +3396,12 @@ var init_lib = __esm(() => {
3389
3396
  var exports_remote = {};
3390
3397
  __export(exports_remote, {
3391
3398
  writeBundle: () => writeBundle,
3399
+ splitLeafPacks: () => splitLeafPacks,
3392
3400
  saveRemote: () => saveRemote,
3393
3401
  remoteRefs: () => remoteRefs,
3394
3402
  remotePushPack: () => remotePushPack,
3403
+ remotePushGiantLeaf: () => remotePushGiantLeaf,
3404
+ remotePushBlobs: () => remotePushBlobs,
3395
3405
  remotePush: () => remotePush,
3396
3406
  remotePromote: () => remotePromote,
3397
3407
  remoteHead: () => remoteHead,
@@ -3415,12 +3425,13 @@ __export(exports_remote, {
3415
3425
  loadRemote: () => loadRemote,
3416
3426
  forksList: () => forksList,
3417
3427
  forkMeta: () => forkMeta,
3428
+ classifyNodes: () => classifyNodes,
3418
3429
  accessSet: () => accessSet,
3419
3430
  accessGet: () => accessGet
3420
3431
  });
3421
3432
  import { appendFileSync as appendFileSync3, existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "node:fs";
3422
3433
  import { join as join5 } from "node:path";
3423
- import { gzipSync as gzipSync2 } from "node:zlib";
3434
+ import { gunzipSync as gunzipSync2, gzipSync as gzipSync2 } from "node:zlib";
3424
3435
  async function call(cfg, token, path, init) {
3425
3436
  const res = await fetch(endpoint(cfg, path), {
3426
3437
  ...init,
@@ -3430,6 +3441,147 @@ async function call(cfg, token, path, init) {
3430
3441
  throw new Error(`remote ${path} -> ${res.status}: ${(await res.text().catch(() => "")).slice(0, 200)}`);
3431
3442
  return res.json();
3432
3443
  }
3444
+ function decodePackLine(line) {
3445
+ const map = JSON.parse(gunzipSync2(Buffer.from(line, "base64")).toString("utf8"));
3446
+ return Object.values(map);
3447
+ }
3448
+ async function exportStreaming(cfg, token) {
3449
+ const res = await fetch(endpoint(cfg, "/export?stream=1"), {
3450
+ headers: { authorization: `Bearer ${token}`, "content-type": "application/json" }
3451
+ });
3452
+ if (!res.ok)
3453
+ throw new Error(`remote /export -> ${res.status}: ${(await res.text().catch(() => "")).slice(0, 200)}`);
3454
+ if (!res.body)
3455
+ return res.json();
3456
+ const reader = res.body.getReader();
3457
+ const td = new TextDecoder;
3458
+ let buf = "";
3459
+ let header;
3460
+ const nodes = [];
3461
+ const consumeLine = (line) => {
3462
+ if (line === "")
3463
+ return;
3464
+ if (!header) {
3465
+ header = JSON.parse(line);
3466
+ if (Array.isArray(header.nodes))
3467
+ for (const n of header.nodes)
3468
+ nodes.push(n);
3469
+ return;
3470
+ }
3471
+ for (const n of decodePackLine(line))
3472
+ nodes.push(n);
3473
+ };
3474
+ for (;; ) {
3475
+ const { done, value } = await reader.read();
3476
+ if (value)
3477
+ buf += td.decode(value, { stream: true });
3478
+ let nl;
3479
+ while ((nl = buf.indexOf(`
3480
+ `)) >= 0) {
3481
+ consumeLine(buf.slice(0, nl));
3482
+ buf = buf.slice(nl + 1);
3483
+ }
3484
+ if (done)
3485
+ break;
3486
+ }
3487
+ buf += td.decode();
3488
+ if (buf.length)
3489
+ consumeLine(buf);
3490
+ if (!header)
3491
+ throw new Error("remote /export returned an empty stream");
3492
+ return { ...header, nodes };
3493
+ }
3494
+ function classifyNodes(nodes) {
3495
+ const trees = [];
3496
+ const leaves = [];
3497
+ let total = 0;
3498
+ for (const n of nodes) {
3499
+ total += nodeBytes(n);
3500
+ (n.kind === "tree" ? trees : leaves).push(n);
3501
+ }
3502
+ return { trees, leaves, total };
3503
+ }
3504
+ function splitLeafPacks(leaves, maxBytes) {
3505
+ const packs = [];
3506
+ let cur = [];
3507
+ let size = 0;
3508
+ for (const n of leaves) {
3509
+ const b = nodeBytes(n);
3510
+ if (cur.length && size + b > maxBytes) {
3511
+ packs.push(cur);
3512
+ cur = [];
3513
+ size = 0;
3514
+ }
3515
+ cur.push(n);
3516
+ size += b;
3517
+ }
3518
+ if (cur.length)
3519
+ packs.push(cur);
3520
+ return packs;
3521
+ }
3522
+ async function pool(tasks, limit) {
3523
+ const out = new Array(tasks.length);
3524
+ let next = 0;
3525
+ const worker = async () => {
3526
+ for (;; ) {
3527
+ const i = next++;
3528
+ if (i >= tasks.length)
3529
+ return;
3530
+ out[i] = await tasks[i]();
3531
+ }
3532
+ };
3533
+ await Promise.all(Array.from({ length: Math.min(limit, tasks.length) }, worker));
3534
+ return out;
3535
+ }
3536
+ async function retryTransient(fn, attempts = 5) {
3537
+ let last;
3538
+ for (let i = 0;i < attempts; i++) {
3539
+ try {
3540
+ return await fn();
3541
+ } catch (e) {
3542
+ last = e;
3543
+ const m = e instanceof Error ? e.message : String(e);
3544
+ if (!/-> 5dd|reset|fetch failed|network|ECONN|terminated|timeout/i.test(m))
3545
+ throw e;
3546
+ await new Promise((r) => setTimeout(r, 400 * (i + 1)));
3547
+ }
3548
+ }
3549
+ throw last;
3550
+ }
3551
+ function canonicalPackBytesClient(entries) {
3552
+ const sorted = [...entries].sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
3553
+ const obj = {};
3554
+ for (const [h, n] of sorted)
3555
+ obj[h] = n;
3556
+ return JSON.stringify(obj);
3557
+ }
3558
+ async function remotePushGiantLeaf(cfg, token, node) {
3559
+ const hash = hashNode(node);
3560
+ const body = canonicalPackBytesClient([[hash, node]]);
3561
+ const packId = hashString(body);
3562
+ const packBody = gzipSync2(Buffer.from(body, "utf8")).toString("base64");
3563
+ const path = `/push/blobs?giant=${encodeURIComponent(hash)}&packId=${encodeURIComponent(packId)}`;
3564
+ return retryTransient(() => call(cfg, token, path, { method: "POST", body: packBody }));
3565
+ }
3566
+ async function remotePushPack(cfg, token, body) {
3567
+ const { trees, leaves, total } = classifyNodes(body.nodes);
3568
+ if (total <= SMALL_PUSH_BYTES) {
3569
+ return retryTransient(() => call(cfg, token, "/push/pack", { method: "POST", body: gzipSync2(JSON.stringify(body)), headers: { "content-encoding": "gzip" } }));
3570
+ }
3571
+ const giants = leaves.filter((n) => nodeBytes(n) > GIANT_LEAF_BYTES);
3572
+ const normal = leaves.filter((n) => nodeBytes(n) <= GIANT_LEAF_BYTES);
3573
+ const blobPacks = splitLeafPacks(normal, BLOB_PACK_BYTES);
3574
+ const tasks = [
3575
+ ...blobPacks.map((pack) => () => remotePushBlobs(cfg, token, pack)),
3576
+ ...giants.map((node) => () => remotePushGiantLeaf(cfg, token, node))
3577
+ ];
3578
+ await pool(tasks, BLOB_PACK_CONCURRENCY);
3579
+ return retryTransient(() => call(cfg, token, "/push/pack", {
3580
+ method: "POST",
3581
+ body: gzipSync2(JSON.stringify({ ...body, nodes: trees })),
3582
+ headers: { "content-encoding": "gzip" }
3583
+ }));
3584
+ }
3433
3585
  function loadRemote(solDir2) {
3434
3586
  const p = join5(solDir2, "remote.json");
3435
3587
  return existsSync5(p) ? JSON.parse(readFileSync5(p, "utf8")) : undefined;
@@ -3450,9 +3602,13 @@ async function writeBundle(solDir2, bundle, from = 0) {
3450
3602
  writeFileSync5(join5(solDir2, "HEAD"), JSON.stringify({ head: bundle.head, seq: bundle.seq, logTip: bundle.tip }));
3451
3603
  return fresh.length;
3452
3604
  }
3453
- var endpoint = (cfg, path) => `${cfg.url.replace(/\/+$/, "")}${path}${path.includes("?") ? "&" : "?"}repo=${encodeURIComponent(cfg.repo)}`, remoteHead = (cfg, token) => call(cfg, token, "/head"), remoteGate = (cfg, token, branch) => call(cfg, token, `/head?gateState=true${branch ? `&branch=${encodeURIComponent(branch)}` : ""}`), remoteExport = (cfg, token) => call(cfg, token, "/export"), remoteRefs = (cfg, token) => call(cfg, token, "/refs"), remotePush = (cfg, token, body) => call(cfg, token, "/push", { method: "POST", body: JSON.stringify(body) }), remotePromote = (cfg, token, branch) => call(cfg, token, "/promote", { method: "POST", body: JSON.stringify({ branch }) }), remotePushPack = (cfg, token, body) => call(cfg, token, "/push/pack", { method: "POST", body: gzipSync2(JSON.stringify(body)), headers: { "content-encoding": "gzip" } }), mrOpen = (cfg, token, body) => call(cfg, token, "/mr", { method: "POST", body: JSON.stringify(body) }), mrList = (cfg, token) => call(cfg, token, "/mrs"), mrDiff = (cfg, token, id) => call(cfg, token, `/mr/diff?id=${id}`), mrGet = (cfg, token, id) => call(cfg, token, `/mr?id=${id}`), mrAction = (cfg, token, action, body) => call(cfg, token, `/mr/${action}`, { method: "POST", body: JSON.stringify(body) }), accessGet = (cfg, token) => call(cfg, token, "/access"), ownerSet = (cfg, token, ownerHandle) => call(cfg, token, "/owner", { method: "POST", body: JSON.stringify({ ownerHandle }) }), accessSet = (cfg, token, body) => call(cfg, token, "/access", { method: "POST", body: JSON.stringify(body) }), policyCheck = (cfg, token, paths) => call(cfg, token, "/policy/check", { method: "POST", body: JSON.stringify({ paths }) }), policyGet = (cfg, token) => call(cfg, token, "/policy"), policyUpsert = (cfg, token, rule) => call(cfg, token, "/policy?write=1", { method: "POST", body: JSON.stringify({ op: "upsert", rule }) }), policyRemove = (cfg, token, pattern) => call(cfg, token, "/policy?write=1", { method: "POST", body: JSON.stringify({ op: "remove", pattern }) }), recipientsForPath = (cfg, token, path) => call(cfg, token, `/recipients?path=${encodeURIComponent(path)}`), recipientsForAudience = (cfg, token, audience, recovery) => call(cfg, token, `/recipients?audience=${encodeURIComponent(JSON.stringify(audience))}${recovery?.length ? `&recovery=${encodeURIComponent(recovery.join(","))}` : ""}`), remoteEnvPush = (cfg, token, bundle) => call(cfg, token, "/env/push", { method: "POST", body: JSON.stringify(bundle) }), remoteEnvAnchor = (cfg, token) => call(cfg, token, "/env/anchor"), remoteEnvPull = (cfg, token) => call(cfg, token, "/env/pull"), forkMeta = (cfg, token, parent) => call(cfg, token, "/fork-meta", { method: "POST", body: JSON.stringify({ parent }) }), forksList = (cfg, token) => call(cfg, token, "/forks"), saveRemote = (solDir2, cfg) => writeFileSync5(join5(solDir2, "remote.json"), JSON.stringify(cfg, null, 2));
3605
+ var endpoint = (cfg, path) => `${cfg.url.replace(/\/+$/, "")}${path}${path.includes("?") ? "&" : "?"}repo=${encodeURIComponent(cfg.repo)}`, remoteHead = (cfg, token) => call(cfg, token, "/head"), remoteGate = (cfg, token, branch) => call(cfg, token, `/head?gateState=true${branch ? `&branch=${encodeURIComponent(branch)}` : ""}`), remoteExport = (cfg, token) => exportStreaming(cfg, token), remoteRefs = (cfg, token) => call(cfg, token, "/refs"), remotePush = (cfg, token, body) => call(cfg, token, "/push", { method: "POST", body: JSON.stringify(body) }), remotePromote = (cfg, token, branch) => call(cfg, token, "/promote", { method: "POST", body: JSON.stringify({ branch }) }), SMALL_PUSH_BYTES, BLOB_PACK_BYTES, BLOB_PACK_CONCURRENCY = 6, GIANT_LEAF_BYTES, nodeBytes = (n) => JSON.stringify(n).length, remotePushBlobs = (cfg, token, nodes) => retryTransient(() => call(cfg, token, "/push/blobs", { method: "POST", body: gzipSync2(JSON.stringify({ nodes })), headers: { "content-encoding": "gzip" } })), mrOpen = (cfg, token, body) => call(cfg, token, "/mr", { method: "POST", body: JSON.stringify(body) }), mrList = (cfg, token) => call(cfg, token, "/mrs"), mrDiff = (cfg, token, id) => call(cfg, token, `/mr/diff?id=${id}`), mrGet = (cfg, token, id) => call(cfg, token, `/mr?id=${id}`), mrAction = (cfg, token, action, body) => call(cfg, token, `/mr/${action}`, { method: "POST", body: JSON.stringify(body) }), accessGet = (cfg, token) => call(cfg, token, "/access"), ownerSet = (cfg, token, ownerHandle) => call(cfg, token, "/owner", { method: "POST", body: JSON.stringify({ ownerHandle }) }), accessSet = (cfg, token, body) => call(cfg, token, "/access", { method: "POST", body: JSON.stringify(body) }), policyCheck = (cfg, token, paths) => call(cfg, token, "/policy/check", { method: "POST", body: JSON.stringify({ paths }) }), policyGet = (cfg, token) => call(cfg, token, "/policy"), policyUpsert = (cfg, token, rule) => call(cfg, token, "/policy?write=1", { method: "POST", body: JSON.stringify({ op: "upsert", rule }) }), policyRemove = (cfg, token, pattern) => call(cfg, token, "/policy?write=1", { method: "POST", body: JSON.stringify({ op: "remove", pattern }) }), recipientsForPath = (cfg, token, path) => call(cfg, token, `/recipients?path=${encodeURIComponent(path)}`), recipientsForAudience = (cfg, token, audience, recovery) => call(cfg, token, `/recipients?audience=${encodeURIComponent(JSON.stringify(audience))}${recovery?.length ? `&recovery=${encodeURIComponent(recovery.join(","))}` : ""}`), remoteEnvPush = (cfg, token, bundle) => call(cfg, token, "/env/push", { method: "POST", body: JSON.stringify(bundle) }), remoteEnvAnchor = (cfg, token) => call(cfg, token, "/env/anchor"), remoteEnvPull = (cfg, token) => call(cfg, token, "/env/pull"), forkMeta = (cfg, token, parent) => call(cfg, token, "/fork-meta", { method: "POST", body: JSON.stringify({ parent }) }), forksList = (cfg, token) => call(cfg, token, "/forks"), saveRemote = (solDir2, cfg) => writeFileSync5(join5(solDir2, "remote.json"), JSON.stringify(cfg, null, 2));
3454
3606
  var init_remote = __esm(() => {
3455
3607
  init_file_store();
3608
+ init_store();
3609
+ SMALL_PUSH_BYTES = 24 * 1024 * 1024;
3610
+ BLOB_PACK_BYTES = 16 * 1024 * 1024;
3611
+ GIANT_LEAF_BYTES = 24 * 1024 * 1024;
3456
3612
  });
3457
3613
 
3458
3614
  // src/bin/test-gate.ts
@@ -9388,21 +9544,41 @@ function tokenClaims(token) {
9388
9544
  return {};
9389
9545
  }
9390
9546
  }
9391
- async function loadStoredToken() {
9392
- if (!existsSync19(CRED_PATH))
9393
- return;
9394
- let creds;
9547
+ function readCreds() {
9395
9548
  try {
9396
- creds = JSON.parse(readFileSync19(CRED_PATH, "utf8"));
9549
+ return JSON.parse(readFileSync19(CRED_PATH, "utf8"));
9397
9550
  } catch {
9398
9551
  return;
9399
9552
  }
9400
- if (!creds.accessToken)
9553
+ }
9554
+ function accessTokenFresh(accessToken) {
9555
+ if (!accessToken)
9556
+ return false;
9557
+ const exp = tokenClaims(accessToken).exp;
9558
+ return typeof exp === "number" && exp * 1000 > Date.now() + 30000;
9559
+ }
9560
+ async function loadStoredToken() {
9561
+ if (!existsSync19(CRED_PATH))
9562
+ return;
9563
+ let creds = readCreds();
9564
+ if (!creds?.accessToken)
9401
9565
  return;
9402
- const exp = tokenClaims(creds.accessToken).exp;
9403
- if (typeof exp === "number" && exp * 1000 > Date.now() + 30000)
9566
+ if (accessTokenFresh(creds.accessToken))
9404
9567
  return creds.accessToken;
9405
- if (creds.refreshToken && creds.webUrl) {
9568
+ if (!(creds.refreshToken && creds.webUrl))
9569
+ return creds.accessToken;
9570
+ const release = acquireLockAt(join18(dirname5(CRED_PATH), "credentials.lock"), {
9571
+ timeoutMs: 1e4,
9572
+ onTimeout: "undefined"
9573
+ });
9574
+ try {
9575
+ if (release) {
9576
+ const fresh = readCreds();
9577
+ if (fresh?.accessToken && accessTokenFresh(fresh.accessToken))
9578
+ return fresh.accessToken;
9579
+ if (fresh)
9580
+ creds = fresh;
9581
+ }
9406
9582
  try {
9407
9583
  const res = await fetch(`${creds.webUrl}/api/auth/refresh`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ refreshToken: creds.refreshToken }) });
9408
9584
  if (res.ok) {
@@ -9413,8 +9589,10 @@ async function loadStoredToken() {
9413
9589
  }
9414
9590
  }
9415
9591
  } catch {}
9592
+ return creds.accessToken;
9593
+ } finally {
9594
+ release?.();
9416
9595
  }
9417
- return creds.accessToken;
9418
9596
  }
9419
9597
  function authExpired() {
9420
9598
  return die("session expired — run `sol auth login` (or set SOL_TOKEN)");
@@ -12157,8 +12335,12 @@ WARNING: "${path}" was committed as PLAINTEXT before this seal — the pre-seal
12157
12335
  const days = Number(patAction) || 90;
12158
12336
  const name = (Number(patAction) ? args[2] : patAction) || undefined;
12159
12337
  const res = await fetch(`${creds.webUrl}/api/auth/pat`, { method: "POST", headers: { authorization: `Bearer ${token}`, "content-type": "application/json" }, body: JSON.stringify({ days, ...name ? { name } : {} }) });
12160
- if (!res.ok)
12161
- die(`could not mint a PAT (${res.status})`);
12338
+ if (!res.ok) {
12339
+ const reason = await res.json().then((b) => b?.error?.detail || b?.error?.message).catch(() => {
12340
+ return;
12341
+ });
12342
+ die(`could not mint a PAT (${res.status})${reason ? `: ${reason}` : ""}`);
12343
+ }
12162
12344
  const out = await res.json();
12163
12345
  console.log(`
12164
12346
  Personal Access Token "${out.name}" (id ${out.id}, expires in ${days} days) — store it now, it is not shown again:
@@ -13672,21 +13854,41 @@ function tokenClaims2(token) {
13672
13854
  return {};
13673
13855
  }
13674
13856
  }
13675
- async function loadStoredToken2() {
13676
- if (!existsSync20(CRED_PATH2))
13677
- return;
13678
- let creds;
13857
+ function readCreds2() {
13679
13858
  try {
13680
- creds = JSON.parse(readFileSync20(CRED_PATH2, "utf8"));
13859
+ return JSON.parse(readFileSync20(CRED_PATH2, "utf8"));
13681
13860
  } catch {
13682
13861
  return;
13683
13862
  }
13684
- if (!creds.accessToken)
13863
+ }
13864
+ function accessTokenFresh2(accessToken) {
13865
+ if (!accessToken)
13866
+ return false;
13867
+ const exp = tokenClaims2(accessToken).exp;
13868
+ return typeof exp === "number" && exp * 1000 > Date.now() + 30000;
13869
+ }
13870
+ async function loadStoredToken2() {
13871
+ if (!existsSync20(CRED_PATH2))
13872
+ return;
13873
+ let creds = readCreds2();
13874
+ if (!creds?.accessToken)
13685
13875
  return;
13686
- const exp = tokenClaims2(creds.accessToken).exp;
13687
- if (typeof exp === "number" && exp * 1000 > Date.now() + 30000)
13876
+ if (accessTokenFresh2(creds.accessToken))
13688
13877
  return creds.accessToken;
13689
- if (creds.refreshToken && creds.webUrl) {
13878
+ if (!(creds.refreshToken && creds.webUrl))
13879
+ return creds.accessToken;
13880
+ const release = acquireLockAt(join20(dirname7(CRED_PATH2), "credentials.lock"), {
13881
+ timeoutMs: 1e4,
13882
+ onTimeout: "undefined"
13883
+ });
13884
+ try {
13885
+ if (release) {
13886
+ const fresh = readCreds2();
13887
+ if (fresh?.accessToken && accessTokenFresh2(fresh.accessToken))
13888
+ return fresh.accessToken;
13889
+ if (fresh)
13890
+ creds = fresh;
13891
+ }
13690
13892
  try {
13691
13893
  const res = await fetch(`${creds.webUrl}/api/auth/refresh`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ refreshToken: creds.refreshToken }) });
13692
13894
  if (res.ok) {
@@ -13697,8 +13899,10 @@ async function loadStoredToken2() {
13697
13899
  }
13698
13900
  }
13699
13901
  } catch {}
13902
+ return creds.accessToken;
13903
+ } finally {
13904
+ release?.();
13700
13905
  }
13701
- return creds.accessToken;
13702
13906
  }
13703
13907
  function authExpired2() {
13704
13908
  return die("session expired — run `sol auth login` (or set SOL_TOKEN)");
@@ -16441,8 +16645,12 @@ WARNING: "${path}" was committed as PLAINTEXT before this seal — the pre-seal
16441
16645
  const days = Number(patAction) || 90;
16442
16646
  const name = (Number(patAction) ? args[2] : patAction) || undefined;
16443
16647
  const res = await fetch(`${creds.webUrl}/api/auth/pat`, { method: "POST", headers: { authorization: `Bearer ${token}`, "content-type": "application/json" }, body: JSON.stringify({ days, ...name ? { name } : {} }) });
16444
- if (!res.ok)
16445
- die(`could not mint a PAT (${res.status})`);
16648
+ if (!res.ok) {
16649
+ const reason = await res.json().then((b) => b?.error?.detail || b?.error?.message).catch(() => {
16650
+ return;
16651
+ });
16652
+ die(`could not mint a PAT (${res.status})${reason ? `: ${reason}` : ""}`);
16653
+ }
16446
16654
  const out = await res.json();
16447
16655
  console.log(`
16448
16656
  Personal Access Token "${out.name}" (id ${out.id}, expires in ${days} days) — store it now, it is not shown again: