midsummer-sol 0.3.6 → 0.3.8
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/package.json +1 -1
- package/sol-mcp.js +109 -20
- package/sol.js +147 -32
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "midsummer-sol",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
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
|
|
3273
|
-
const
|
|
3274
|
-
const
|
|
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
|
-
|
|
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
|
}
|
|
@@ -4160,6 +4167,7 @@ __export(exports_remote, {
|
|
|
4160
4167
|
saveRemote: () => saveRemote,
|
|
4161
4168
|
remoteRefs: () => remoteRefs,
|
|
4162
4169
|
remotePushPack: () => remotePushPack,
|
|
4170
|
+
remotePushGiantLeaf: () => remotePushGiantLeaf,
|
|
4163
4171
|
remotePushBlobs: () => remotePushBlobs,
|
|
4164
4172
|
remotePush: () => remotePush,
|
|
4165
4173
|
remotePromote: () => remotePromote,
|
|
@@ -4184,6 +4192,7 @@ __export(exports_remote, {
|
|
|
4184
4192
|
loadRemote: () => loadRemote,
|
|
4185
4193
|
forksList: () => forksList,
|
|
4186
4194
|
forkMeta: () => forkMeta,
|
|
4195
|
+
exportStreaming: () => exportStreaming,
|
|
4187
4196
|
classifyNodes: () => classifyNodes,
|
|
4188
4197
|
accessSet: () => accessSet,
|
|
4189
4198
|
accessGet: () => accessGet
|
|
@@ -4250,6 +4259,37 @@ async function exportStreaming(cfg, token) {
|
|
|
4250
4259
|
throw new Error("remote /export returned an empty stream");
|
|
4251
4260
|
return { ...header, nodes };
|
|
4252
4261
|
}
|
|
4262
|
+
async function exportManifest(cfg, token) {
|
|
4263
|
+
const res = await fetch(endpoint(cfg, "/export/manifest"), {
|
|
4264
|
+
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" }
|
|
4265
|
+
});
|
|
4266
|
+
if (res.status === 404)
|
|
4267
|
+
return exportStreaming(cfg, token);
|
|
4268
|
+
if (!res.ok)
|
|
4269
|
+
throw new Error(`remote /export/manifest -> ${res.status}: ${(await res.text().catch(() => "")).slice(0, 200)}`);
|
|
4270
|
+
const m = await res.json();
|
|
4271
|
+
if (!Array.isArray(m.packIds))
|
|
4272
|
+
return exportStreaming(cfg, token);
|
|
4273
|
+
const tasks = [
|
|
4274
|
+
...(m.packIds ?? []).map((packId) => () => fetchPack(cfg, token, packId, false)),
|
|
4275
|
+
...(m.legacyNodes ?? []).map((hash) => () => fetchPack(cfg, token, hash, true))
|
|
4276
|
+
];
|
|
4277
|
+
const packNodes = await pool(tasks, CLONE_PACK_CONCURRENCY);
|
|
4278
|
+
const nodes = [];
|
|
4279
|
+
for (const ns of packNodes)
|
|
4280
|
+
for (const n of ns)
|
|
4281
|
+
nodes.push(n);
|
|
4282
|
+
return { schema: m.schema, head: m.head, seq: m.seq, tip: m.tip, ops: m.ops, nodes, refs: m.refs, checkout: m.checkout };
|
|
4283
|
+
}
|
|
4284
|
+
async function fetchPack(cfg, token, id, legacy) {
|
|
4285
|
+
return retryTransient(async () => {
|
|
4286
|
+
const path = `/pack/${encodeURIComponent(id)}${legacy ? "?legacy=1" : ""}`;
|
|
4287
|
+
const res = await fetch(endpoint(cfg, path), { headers: { authorization: `Bearer ${token}` } });
|
|
4288
|
+
if (!res.ok)
|
|
4289
|
+
throw new Error(`remote /pack -> ${res.status}: ${(await res.text().catch(() => "")).slice(0, 200)}`);
|
|
4290
|
+
return decodePackLine((await res.text()).trim());
|
|
4291
|
+
});
|
|
4292
|
+
}
|
|
4253
4293
|
function classifyNodes(nodes) {
|
|
4254
4294
|
const trees = [];
|
|
4255
4295
|
const leaves = [];
|
|
@@ -4307,13 +4347,34 @@ async function retryTransient(fn, attempts = 5) {
|
|
|
4307
4347
|
}
|
|
4308
4348
|
throw last;
|
|
4309
4349
|
}
|
|
4350
|
+
function canonicalPackBytesClient(entries) {
|
|
4351
|
+
const sorted = [...entries].sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
|
|
4352
|
+
const obj = {};
|
|
4353
|
+
for (const [h, n] of sorted)
|
|
4354
|
+
obj[h] = n;
|
|
4355
|
+
return JSON.stringify(obj);
|
|
4356
|
+
}
|
|
4357
|
+
async function remotePushGiantLeaf(cfg, token, node) {
|
|
4358
|
+
const hash = hashNode(node);
|
|
4359
|
+
const body = canonicalPackBytesClient([[hash, node]]);
|
|
4360
|
+
const packId = hashString(body);
|
|
4361
|
+
const packBody = gzipSync3(Buffer.from(body, "utf8")).toString("base64");
|
|
4362
|
+
const path = `/push/blobs?giant=${encodeURIComponent(hash)}&packId=${encodeURIComponent(packId)}`;
|
|
4363
|
+
return retryTransient(() => call(cfg, token, path, { method: "POST", body: packBody }));
|
|
4364
|
+
}
|
|
4310
4365
|
async function remotePushPack(cfg, token, body) {
|
|
4311
4366
|
const { trees, leaves, total } = classifyNodes(body.nodes);
|
|
4312
4367
|
if (total <= SMALL_PUSH_BYTES) {
|
|
4313
4368
|
return retryTransient(() => call(cfg, token, "/push/pack", { method: "POST", body: gzipSync3(JSON.stringify(body)), headers: { "content-encoding": "gzip" } }));
|
|
4314
4369
|
}
|
|
4315
|
-
const
|
|
4316
|
-
|
|
4370
|
+
const giants = leaves.filter((n) => nodeBytes(n) > GIANT_LEAF_BYTES);
|
|
4371
|
+
const normal = leaves.filter((n) => nodeBytes(n) <= GIANT_LEAF_BYTES);
|
|
4372
|
+
const blobPacks = splitLeafPacks(normal, BLOB_PACK_BYTES);
|
|
4373
|
+
const tasks = [
|
|
4374
|
+
...blobPacks.map((pack) => () => remotePushBlobs(cfg, token, pack)),
|
|
4375
|
+
...giants.map((node) => () => remotePushGiantLeaf(cfg, token, node))
|
|
4376
|
+
];
|
|
4377
|
+
await pool(tasks, BLOB_PACK_CONCURRENCY);
|
|
4317
4378
|
return retryTransient(() => call(cfg, token, "/push/pack", {
|
|
4318
4379
|
method: "POST",
|
|
4319
4380
|
body: gzipSync3(JSON.stringify({ ...body, nodes: trees })),
|
|
@@ -4340,11 +4401,13 @@ async function writeBundle(solDir2, bundle, from = 0) {
|
|
|
4340
4401
|
writeFileSync6(join6(solDir2, "HEAD"), JSON.stringify({ head: bundle.head, seq: bundle.seq, logTip: bundle.tip }));
|
|
4341
4402
|
return fresh.length;
|
|
4342
4403
|
}
|
|
4343
|
-
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) =>
|
|
4404
|
+
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)}` : ""}`), CLONE_PACK_CONCURRENCY = 12, remoteExport = (cfg, token) => exportManifest(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));
|
|
4344
4405
|
var init_remote = __esm(() => {
|
|
4345
4406
|
init_file_store2();
|
|
4407
|
+
init_store();
|
|
4346
4408
|
SMALL_PUSH_BYTES = 24 * 1024 * 1024;
|
|
4347
4409
|
BLOB_PACK_BYTES = 16 * 1024 * 1024;
|
|
4410
|
+
GIANT_LEAF_BYTES = 24 * 1024 * 1024;
|
|
4348
4411
|
});
|
|
4349
4412
|
|
|
4350
4413
|
// src/bin/test-gate.ts
|
|
@@ -9745,21 +9808,41 @@ function tokenClaims(token) {
|
|
|
9745
9808
|
return {};
|
|
9746
9809
|
}
|
|
9747
9810
|
}
|
|
9748
|
-
|
|
9749
|
-
if (!existsSync20(CRED_PATH))
|
|
9750
|
-
return;
|
|
9751
|
-
let creds;
|
|
9811
|
+
function readCreds() {
|
|
9752
9812
|
try {
|
|
9753
|
-
|
|
9813
|
+
return JSON.parse(readFileSync20(CRED_PATH, "utf8"));
|
|
9754
9814
|
} catch {
|
|
9755
9815
|
return;
|
|
9756
9816
|
}
|
|
9757
|
-
|
|
9817
|
+
}
|
|
9818
|
+
function accessTokenFresh(accessToken) {
|
|
9819
|
+
if (!accessToken)
|
|
9820
|
+
return false;
|
|
9821
|
+
const exp = tokenClaims(accessToken).exp;
|
|
9822
|
+
return typeof exp === "number" && exp * 1000 > Date.now() + 30000;
|
|
9823
|
+
}
|
|
9824
|
+
async function loadStoredToken() {
|
|
9825
|
+
if (!existsSync20(CRED_PATH))
|
|
9826
|
+
return;
|
|
9827
|
+
let creds = readCreds();
|
|
9828
|
+
if (!creds?.accessToken)
|
|
9758
9829
|
return;
|
|
9759
|
-
|
|
9760
|
-
if (typeof exp === "number" && exp * 1000 > Date.now() + 30000)
|
|
9830
|
+
if (accessTokenFresh(creds.accessToken))
|
|
9761
9831
|
return creds.accessToken;
|
|
9762
|
-
if (creds.refreshToken && creds.webUrl)
|
|
9832
|
+
if (!(creds.refreshToken && creds.webUrl))
|
|
9833
|
+
return creds.accessToken;
|
|
9834
|
+
const release = acquireLockAt(join19(dirname5(CRED_PATH), "credentials.lock"), {
|
|
9835
|
+
timeoutMs: 1e4,
|
|
9836
|
+
onTimeout: "undefined"
|
|
9837
|
+
});
|
|
9838
|
+
try {
|
|
9839
|
+
if (release) {
|
|
9840
|
+
const fresh = readCreds();
|
|
9841
|
+
if (fresh?.accessToken && accessTokenFresh(fresh.accessToken))
|
|
9842
|
+
return fresh.accessToken;
|
|
9843
|
+
if (fresh)
|
|
9844
|
+
creds = fresh;
|
|
9845
|
+
}
|
|
9763
9846
|
try {
|
|
9764
9847
|
const res = await fetch(`${creds.webUrl}/api/auth/refresh`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ refreshToken: creds.refreshToken }) });
|
|
9765
9848
|
if (res.ok) {
|
|
@@ -9770,8 +9853,10 @@ async function loadStoredToken() {
|
|
|
9770
9853
|
}
|
|
9771
9854
|
}
|
|
9772
9855
|
} catch {}
|
|
9856
|
+
return creds.accessToken;
|
|
9857
|
+
} finally {
|
|
9858
|
+
release?.();
|
|
9773
9859
|
}
|
|
9774
|
-
return creds.accessToken;
|
|
9775
9860
|
}
|
|
9776
9861
|
function authExpired() {
|
|
9777
9862
|
return die("session expired — run `sol auth login` (or set SOL_TOKEN)");
|
|
@@ -12514,8 +12599,12 @@ WARNING: "${path}" was committed as PLAINTEXT before this seal — the pre-seal
|
|
|
12514
12599
|
const days = Number(patAction) || 90;
|
|
12515
12600
|
const name = (Number(patAction) ? args[2] : patAction) || undefined;
|
|
12516
12601
|
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 } : {} }) });
|
|
12517
|
-
if (!res.ok)
|
|
12518
|
-
|
|
12602
|
+
if (!res.ok) {
|
|
12603
|
+
const reason = await res.json().then((b) => b?.error?.detail || b?.error?.message).catch(() => {
|
|
12604
|
+
return;
|
|
12605
|
+
});
|
|
12606
|
+
die(`could not mint a PAT (${res.status})${reason ? `: ${reason}` : ""}`);
|
|
12607
|
+
}
|
|
12519
12608
|
const out = await res.json();
|
|
12520
12609
|
console.log(`
|
|
12521
12610
|
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
|
|
2506
|
-
const
|
|
2507
|
-
const
|
|
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
|
-
|
|
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
|
}
|
|
@@ -3393,6 +3400,7 @@ __export(exports_remote, {
|
|
|
3393
3400
|
saveRemote: () => saveRemote,
|
|
3394
3401
|
remoteRefs: () => remoteRefs,
|
|
3395
3402
|
remotePushPack: () => remotePushPack,
|
|
3403
|
+
remotePushGiantLeaf: () => remotePushGiantLeaf,
|
|
3396
3404
|
remotePushBlobs: () => remotePushBlobs,
|
|
3397
3405
|
remotePush: () => remotePush,
|
|
3398
3406
|
remotePromote: () => remotePromote,
|
|
@@ -3417,6 +3425,7 @@ __export(exports_remote, {
|
|
|
3417
3425
|
loadRemote: () => loadRemote,
|
|
3418
3426
|
forksList: () => forksList,
|
|
3419
3427
|
forkMeta: () => forkMeta,
|
|
3428
|
+
exportStreaming: () => exportStreaming,
|
|
3420
3429
|
classifyNodes: () => classifyNodes,
|
|
3421
3430
|
accessSet: () => accessSet,
|
|
3422
3431
|
accessGet: () => accessGet
|
|
@@ -3483,6 +3492,37 @@ async function exportStreaming(cfg, token) {
|
|
|
3483
3492
|
throw new Error("remote /export returned an empty stream");
|
|
3484
3493
|
return { ...header, nodes };
|
|
3485
3494
|
}
|
|
3495
|
+
async function exportManifest(cfg, token) {
|
|
3496
|
+
const res = await fetch(endpoint(cfg, "/export/manifest"), {
|
|
3497
|
+
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" }
|
|
3498
|
+
});
|
|
3499
|
+
if (res.status === 404)
|
|
3500
|
+
return exportStreaming(cfg, token);
|
|
3501
|
+
if (!res.ok)
|
|
3502
|
+
throw new Error(`remote /export/manifest -> ${res.status}: ${(await res.text().catch(() => "")).slice(0, 200)}`);
|
|
3503
|
+
const m = await res.json();
|
|
3504
|
+
if (!Array.isArray(m.packIds))
|
|
3505
|
+
return exportStreaming(cfg, token);
|
|
3506
|
+
const tasks = [
|
|
3507
|
+
...(m.packIds ?? []).map((packId) => () => fetchPack(cfg, token, packId, false)),
|
|
3508
|
+
...(m.legacyNodes ?? []).map((hash) => () => fetchPack(cfg, token, hash, true))
|
|
3509
|
+
];
|
|
3510
|
+
const packNodes = await pool(tasks, CLONE_PACK_CONCURRENCY);
|
|
3511
|
+
const nodes = [];
|
|
3512
|
+
for (const ns of packNodes)
|
|
3513
|
+
for (const n of ns)
|
|
3514
|
+
nodes.push(n);
|
|
3515
|
+
return { schema: m.schema, head: m.head, seq: m.seq, tip: m.tip, ops: m.ops, nodes, refs: m.refs, checkout: m.checkout };
|
|
3516
|
+
}
|
|
3517
|
+
async function fetchPack(cfg, token, id, legacy) {
|
|
3518
|
+
return retryTransient(async () => {
|
|
3519
|
+
const path = `/pack/${encodeURIComponent(id)}${legacy ? "?legacy=1" : ""}`;
|
|
3520
|
+
const res = await fetch(endpoint(cfg, path), { headers: { authorization: `Bearer ${token}` } });
|
|
3521
|
+
if (!res.ok)
|
|
3522
|
+
throw new Error(`remote /pack -> ${res.status}: ${(await res.text().catch(() => "")).slice(0, 200)}`);
|
|
3523
|
+
return decodePackLine((await res.text()).trim());
|
|
3524
|
+
});
|
|
3525
|
+
}
|
|
3486
3526
|
function classifyNodes(nodes) {
|
|
3487
3527
|
const trees = [];
|
|
3488
3528
|
const leaves = [];
|
|
@@ -3540,13 +3580,34 @@ async function retryTransient(fn, attempts = 5) {
|
|
|
3540
3580
|
}
|
|
3541
3581
|
throw last;
|
|
3542
3582
|
}
|
|
3583
|
+
function canonicalPackBytesClient(entries) {
|
|
3584
|
+
const sorted = [...entries].sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
|
|
3585
|
+
const obj = {};
|
|
3586
|
+
for (const [h, n] of sorted)
|
|
3587
|
+
obj[h] = n;
|
|
3588
|
+
return JSON.stringify(obj);
|
|
3589
|
+
}
|
|
3590
|
+
async function remotePushGiantLeaf(cfg, token, node) {
|
|
3591
|
+
const hash = hashNode(node);
|
|
3592
|
+
const body = canonicalPackBytesClient([[hash, node]]);
|
|
3593
|
+
const packId = hashString(body);
|
|
3594
|
+
const packBody = gzipSync2(Buffer.from(body, "utf8")).toString("base64");
|
|
3595
|
+
const path = `/push/blobs?giant=${encodeURIComponent(hash)}&packId=${encodeURIComponent(packId)}`;
|
|
3596
|
+
return retryTransient(() => call(cfg, token, path, { method: "POST", body: packBody }));
|
|
3597
|
+
}
|
|
3543
3598
|
async function remotePushPack(cfg, token, body) {
|
|
3544
3599
|
const { trees, leaves, total } = classifyNodes(body.nodes);
|
|
3545
3600
|
if (total <= SMALL_PUSH_BYTES) {
|
|
3546
3601
|
return retryTransient(() => call(cfg, token, "/push/pack", { method: "POST", body: gzipSync2(JSON.stringify(body)), headers: { "content-encoding": "gzip" } }));
|
|
3547
3602
|
}
|
|
3548
|
-
const
|
|
3549
|
-
|
|
3603
|
+
const giants = leaves.filter((n) => nodeBytes(n) > GIANT_LEAF_BYTES);
|
|
3604
|
+
const normal = leaves.filter((n) => nodeBytes(n) <= GIANT_LEAF_BYTES);
|
|
3605
|
+
const blobPacks = splitLeafPacks(normal, BLOB_PACK_BYTES);
|
|
3606
|
+
const tasks = [
|
|
3607
|
+
...blobPacks.map((pack) => () => remotePushBlobs(cfg, token, pack)),
|
|
3608
|
+
...giants.map((node) => () => remotePushGiantLeaf(cfg, token, node))
|
|
3609
|
+
];
|
|
3610
|
+
await pool(tasks, BLOB_PACK_CONCURRENCY);
|
|
3550
3611
|
return retryTransient(() => call(cfg, token, "/push/pack", {
|
|
3551
3612
|
method: "POST",
|
|
3552
3613
|
body: gzipSync2(JSON.stringify({ ...body, nodes: trees })),
|
|
@@ -3573,11 +3634,13 @@ async function writeBundle(solDir2, bundle, from = 0) {
|
|
|
3573
3634
|
writeFileSync5(join5(solDir2, "HEAD"), JSON.stringify({ head: bundle.head, seq: bundle.seq, logTip: bundle.tip }));
|
|
3574
3635
|
return fresh.length;
|
|
3575
3636
|
}
|
|
3576
|
-
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) =>
|
|
3637
|
+
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)}` : ""}`), CLONE_PACK_CONCURRENCY = 12, remoteExport = (cfg, token) => exportManifest(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));
|
|
3577
3638
|
var init_remote = __esm(() => {
|
|
3578
3639
|
init_file_store();
|
|
3640
|
+
init_store();
|
|
3579
3641
|
SMALL_PUSH_BYTES = 24 * 1024 * 1024;
|
|
3580
3642
|
BLOB_PACK_BYTES = 16 * 1024 * 1024;
|
|
3643
|
+
GIANT_LEAF_BYTES = 24 * 1024 * 1024;
|
|
3581
3644
|
});
|
|
3582
3645
|
|
|
3583
3646
|
// src/bin/test-gate.ts
|
|
@@ -9513,21 +9576,41 @@ function tokenClaims(token) {
|
|
|
9513
9576
|
return {};
|
|
9514
9577
|
}
|
|
9515
9578
|
}
|
|
9516
|
-
|
|
9517
|
-
if (!existsSync19(CRED_PATH))
|
|
9518
|
-
return;
|
|
9519
|
-
let creds;
|
|
9579
|
+
function readCreds() {
|
|
9520
9580
|
try {
|
|
9521
|
-
|
|
9581
|
+
return JSON.parse(readFileSync19(CRED_PATH, "utf8"));
|
|
9522
9582
|
} catch {
|
|
9523
9583
|
return;
|
|
9524
9584
|
}
|
|
9525
|
-
|
|
9585
|
+
}
|
|
9586
|
+
function accessTokenFresh(accessToken) {
|
|
9587
|
+
if (!accessToken)
|
|
9588
|
+
return false;
|
|
9589
|
+
const exp = tokenClaims(accessToken).exp;
|
|
9590
|
+
return typeof exp === "number" && exp * 1000 > Date.now() + 30000;
|
|
9591
|
+
}
|
|
9592
|
+
async function loadStoredToken() {
|
|
9593
|
+
if (!existsSync19(CRED_PATH))
|
|
9526
9594
|
return;
|
|
9527
|
-
|
|
9528
|
-
if (
|
|
9595
|
+
let creds = readCreds();
|
|
9596
|
+
if (!creds?.accessToken)
|
|
9597
|
+
return;
|
|
9598
|
+
if (accessTokenFresh(creds.accessToken))
|
|
9599
|
+
return creds.accessToken;
|
|
9600
|
+
if (!(creds.refreshToken && creds.webUrl))
|
|
9529
9601
|
return creds.accessToken;
|
|
9530
|
-
|
|
9602
|
+
const release = acquireLockAt(join18(dirname5(CRED_PATH), "credentials.lock"), {
|
|
9603
|
+
timeoutMs: 1e4,
|
|
9604
|
+
onTimeout: "undefined"
|
|
9605
|
+
});
|
|
9606
|
+
try {
|
|
9607
|
+
if (release) {
|
|
9608
|
+
const fresh = readCreds();
|
|
9609
|
+
if (fresh?.accessToken && accessTokenFresh(fresh.accessToken))
|
|
9610
|
+
return fresh.accessToken;
|
|
9611
|
+
if (fresh)
|
|
9612
|
+
creds = fresh;
|
|
9613
|
+
}
|
|
9531
9614
|
try {
|
|
9532
9615
|
const res = await fetch(`${creds.webUrl}/api/auth/refresh`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ refreshToken: creds.refreshToken }) });
|
|
9533
9616
|
if (res.ok) {
|
|
@@ -9538,8 +9621,10 @@ async function loadStoredToken() {
|
|
|
9538
9621
|
}
|
|
9539
9622
|
}
|
|
9540
9623
|
} catch {}
|
|
9624
|
+
return creds.accessToken;
|
|
9625
|
+
} finally {
|
|
9626
|
+
release?.();
|
|
9541
9627
|
}
|
|
9542
|
-
return creds.accessToken;
|
|
9543
9628
|
}
|
|
9544
9629
|
function authExpired() {
|
|
9545
9630
|
return die("session expired — run `sol auth login` (or set SOL_TOKEN)");
|
|
@@ -12282,8 +12367,12 @@ WARNING: "${path}" was committed as PLAINTEXT before this seal — the pre-seal
|
|
|
12282
12367
|
const days = Number(patAction) || 90;
|
|
12283
12368
|
const name = (Number(patAction) ? args[2] : patAction) || undefined;
|
|
12284
12369
|
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 } : {} }) });
|
|
12285
|
-
if (!res.ok)
|
|
12286
|
-
|
|
12370
|
+
if (!res.ok) {
|
|
12371
|
+
const reason = await res.json().then((b) => b?.error?.detail || b?.error?.message).catch(() => {
|
|
12372
|
+
return;
|
|
12373
|
+
});
|
|
12374
|
+
die(`could not mint a PAT (${res.status})${reason ? `: ${reason}` : ""}`);
|
|
12375
|
+
}
|
|
12287
12376
|
const out = await res.json();
|
|
12288
12377
|
console.log(`
|
|
12289
12378
|
Personal Access Token "${out.name}" (id ${out.id}, expires in ${days} days) — store it now, it is not shown again:
|
|
@@ -13797,21 +13886,41 @@ function tokenClaims2(token) {
|
|
|
13797
13886
|
return {};
|
|
13798
13887
|
}
|
|
13799
13888
|
}
|
|
13800
|
-
|
|
13801
|
-
if (!existsSync20(CRED_PATH2))
|
|
13802
|
-
return;
|
|
13803
|
-
let creds;
|
|
13889
|
+
function readCreds2() {
|
|
13804
13890
|
try {
|
|
13805
|
-
|
|
13891
|
+
return JSON.parse(readFileSync20(CRED_PATH2, "utf8"));
|
|
13806
13892
|
} catch {
|
|
13807
13893
|
return;
|
|
13808
13894
|
}
|
|
13809
|
-
|
|
13895
|
+
}
|
|
13896
|
+
function accessTokenFresh2(accessToken) {
|
|
13897
|
+
if (!accessToken)
|
|
13898
|
+
return false;
|
|
13899
|
+
const exp = tokenClaims2(accessToken).exp;
|
|
13900
|
+
return typeof exp === "number" && exp * 1000 > Date.now() + 30000;
|
|
13901
|
+
}
|
|
13902
|
+
async function loadStoredToken2() {
|
|
13903
|
+
if (!existsSync20(CRED_PATH2))
|
|
13810
13904
|
return;
|
|
13811
|
-
|
|
13812
|
-
if (
|
|
13905
|
+
let creds = readCreds2();
|
|
13906
|
+
if (!creds?.accessToken)
|
|
13907
|
+
return;
|
|
13908
|
+
if (accessTokenFresh2(creds.accessToken))
|
|
13909
|
+
return creds.accessToken;
|
|
13910
|
+
if (!(creds.refreshToken && creds.webUrl))
|
|
13813
13911
|
return creds.accessToken;
|
|
13814
|
-
|
|
13912
|
+
const release = acquireLockAt(join20(dirname7(CRED_PATH2), "credentials.lock"), {
|
|
13913
|
+
timeoutMs: 1e4,
|
|
13914
|
+
onTimeout: "undefined"
|
|
13915
|
+
});
|
|
13916
|
+
try {
|
|
13917
|
+
if (release) {
|
|
13918
|
+
const fresh = readCreds2();
|
|
13919
|
+
if (fresh?.accessToken && accessTokenFresh2(fresh.accessToken))
|
|
13920
|
+
return fresh.accessToken;
|
|
13921
|
+
if (fresh)
|
|
13922
|
+
creds = fresh;
|
|
13923
|
+
}
|
|
13815
13924
|
try {
|
|
13816
13925
|
const res = await fetch(`${creds.webUrl}/api/auth/refresh`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ refreshToken: creds.refreshToken }) });
|
|
13817
13926
|
if (res.ok) {
|
|
@@ -13822,8 +13931,10 @@ async function loadStoredToken2() {
|
|
|
13822
13931
|
}
|
|
13823
13932
|
}
|
|
13824
13933
|
} catch {}
|
|
13934
|
+
return creds.accessToken;
|
|
13935
|
+
} finally {
|
|
13936
|
+
release?.();
|
|
13825
13937
|
}
|
|
13826
|
-
return creds.accessToken;
|
|
13827
13938
|
}
|
|
13828
13939
|
function authExpired2() {
|
|
13829
13940
|
return die("session expired — run `sol auth login` (or set SOL_TOKEN)");
|
|
@@ -16566,8 +16677,12 @@ WARNING: "${path}" was committed as PLAINTEXT before this seal — the pre-seal
|
|
|
16566
16677
|
const days = Number(patAction) || 90;
|
|
16567
16678
|
const name = (Number(patAction) ? args[2] : patAction) || undefined;
|
|
16568
16679
|
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 } : {} }) });
|
|
16569
|
-
if (!res.ok)
|
|
16570
|
-
|
|
16680
|
+
if (!res.ok) {
|
|
16681
|
+
const reason = await res.json().then((b) => b?.error?.detail || b?.error?.message).catch(() => {
|
|
16682
|
+
return;
|
|
16683
|
+
});
|
|
16684
|
+
die(`could not mint a PAT (${res.status})${reason ? `: ${reason}` : ""}`);
|
|
16685
|
+
}
|
|
16571
16686
|
const out = await res.json();
|
|
16572
16687
|
console.log(`
|
|
16573
16688
|
Personal Access Token "${out.name}" (id ${out.id}, expires in ${days} days) — store it now, it is not shown again:
|