midsummer-sol 0.1.3 → 0.1.4
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.js +64 -22
package/package.json
CHANGED
package/sol.js
CHANGED
|
@@ -885,7 +885,6 @@ import {
|
|
|
885
885
|
readdirSync as readdirSync3,
|
|
886
886
|
readFileSync as readFileSync3,
|
|
887
887
|
readlinkSync,
|
|
888
|
-
rmdirSync,
|
|
889
888
|
symlinkSync,
|
|
890
889
|
unlinkSync,
|
|
891
890
|
writeFileSync as writeFileSync3
|
|
@@ -1403,7 +1402,6 @@ import {
|
|
|
1403
1402
|
readdirSync as readdirSync5,
|
|
1404
1403
|
readFileSync as readFileSync6,
|
|
1405
1404
|
readlinkSync as readlinkSync3,
|
|
1406
|
-
rmdirSync as rmdirSync2,
|
|
1407
1405
|
symlinkSync as symlinkSync3,
|
|
1408
1406
|
unlinkSync as unlinkSync4,
|
|
1409
1407
|
writeFileSync as writeFileSync6
|
|
@@ -1581,20 +1579,42 @@ async function snapshotTree(repo) {
|
|
|
1581
1579
|
function sleepSync(ms) {
|
|
1582
1580
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
1583
1581
|
}
|
|
1582
|
+
var LOCK_STALE_MS = 5000;
|
|
1583
|
+
function pidAlive(pid) {
|
|
1584
|
+
if (!pid || pid < 1)
|
|
1585
|
+
return false;
|
|
1586
|
+
try {
|
|
1587
|
+
process.kill(pid, 0);
|
|
1588
|
+
return true;
|
|
1589
|
+
} catch (e) {
|
|
1590
|
+
return e?.code === "EPERM";
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1584
1593
|
function acquireLock() {
|
|
1585
|
-
const
|
|
1594
|
+
const lockFile = join6(solDir2, "lock");
|
|
1586
1595
|
const deadline = Date.now() + 15000;
|
|
1587
1596
|
for (;; ) {
|
|
1588
1597
|
try {
|
|
1589
|
-
|
|
1598
|
+
writeFileSync6(lockFile, String(process.pid), { flag: "wx" });
|
|
1590
1599
|
break;
|
|
1591
1600
|
} catch {
|
|
1592
1601
|
try {
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1602
|
+
const st = lstatSync3(lockFile);
|
|
1603
|
+
if (Date.now() - st.mtimeMs > LOCK_STALE_MS) {
|
|
1604
|
+
let ownerPid = 0;
|
|
1605
|
+
try {
|
|
1606
|
+
ownerPid = parseInt(readFileSync6(lockFile, "utf8").trim(), 10) || 0;
|
|
1607
|
+
} catch {}
|
|
1608
|
+
if (!pidAlive(ownerPid)) {
|
|
1609
|
+
try {
|
|
1610
|
+
unlinkSync4(lockFile);
|
|
1611
|
+
} catch {}
|
|
1612
|
+
continue;
|
|
1613
|
+
}
|
|
1596
1614
|
}
|
|
1597
|
-
} catch {
|
|
1615
|
+
} catch {
|
|
1616
|
+
continue;
|
|
1617
|
+
}
|
|
1598
1618
|
if (Date.now() > deadline)
|
|
1599
1619
|
die("repo is locked by another sol process (timed out)");
|
|
1600
1620
|
sleepSync(25);
|
|
@@ -1606,7 +1626,7 @@ function acquireLock() {
|
|
|
1606
1626
|
return;
|
|
1607
1627
|
released = true;
|
|
1608
1628
|
try {
|
|
1609
|
-
|
|
1629
|
+
unlinkSync4(lockFile);
|
|
1610
1630
|
} catch {}
|
|
1611
1631
|
};
|
|
1612
1632
|
process.on("exit", release);
|
|
@@ -2570,12 +2590,15 @@ async function main() {
|
|
|
2570
2590
|
const args = argv.slice(1);
|
|
2571
2591
|
const wantsHelp = args.includes("--help") || args.includes("-h");
|
|
2572
2592
|
const SUBHELP = {
|
|
2573
|
-
commit: `sol commit "<
|
|
2574
|
-
sol commit -m "<
|
|
2593
|
+
commit: `sol commit "<msg>" commits the whole working tree; works as-is for a solo author.
|
|
2594
|
+
sol commit -m "<msg>" <file>... commit ONLY those paths (correct attribution for concurrent agents)
|
|
2595
|
+
|
|
2596
|
+
(once OTHER authors have committed to this repo, a whole-tree commit is refused to avoid mis-attribution
|
|
2597
|
+
\u2014 scope to files \`sol commit -m "<msg>" <files>\` or force with --whole-tree).
|
|
2575
2598
|
|
|
2576
2599
|
flags:
|
|
2577
2600
|
-m <message> message when scoping to files
|
|
2578
|
-
--whole-tree
|
|
2601
|
+
--whole-tree force a whole-tree commit even after OTHER authors are present (the guard above)
|
|
2579
2602
|
--force commit even with unresolved <<<<<<< conflict markers
|
|
2580
2603
|
|
|
2581
2604
|
examples:
|
|
@@ -2584,14 +2607,21 @@ examples:
|
|
|
2584
2607
|
push: `sol push push the current branch to the configured remote (converging \u2014 never FF-rejected)
|
|
2585
2608
|
sol push <repo> if no remote is set, use the hosted Sol (${DEFAULT_REMOTE_URL}) + this repo name, then push
|
|
2586
2609
|
sol push --create <repo> same, explicit form (creates the repo on first push)
|
|
2610
|
+
sol push --public <repo> create + push + make it public in ONE step (else new repos are private)
|
|
2611
|
+
|
|
2612
|
+
flags:
|
|
2613
|
+
--create explicit "creates the repo on first push" (the bare \`<repo>\` form does this too)
|
|
2614
|
+
--public after a successful create/push, set public access (same op as \`sol access public\`)
|
|
2587
2615
|
|
|
2588
2616
|
notes:
|
|
2589
2617
|
a remote already configured? the <repo>/--create arg is ignored \u2014 \`sol push\` just syncs.
|
|
2618
|
+
new repos are PRIVATE by default; \`--public\` is the one-step opt-in to share (or \`sol access public\` later).
|
|
2590
2619
|
set SOL_TOKEN (or \`sol auth login\`) first; the push registers the current branch's head on the remote.
|
|
2591
2620
|
|
|
2592
2621
|
examples:
|
|
2593
2622
|
sol push # remote already configured
|
|
2594
|
-
sol push alice/app # one step: configure hosted remote + push
|
|
2623
|
+
sol push alice/app # one step: configure hosted remote + push (private)
|
|
2624
|
+
sol push --create --public alice/app # create + push + share, all at once`,
|
|
2595
2625
|
pull: `sol pull fetch + converge the current branch from the configured remote
|
|
2596
2626
|
sol pull <dir> OFFLINE: converge another local repo's .sol on disk into this one (multi-agent, no network)
|
|
2597
2627
|
|
|
@@ -2634,7 +2664,8 @@ tip: \`sol push <repo>\` configures the hosted remote for you in one step.`,
|
|
|
2634
2664
|
sol mr comment <id> -m msg [--path f --line N] | check <id> --run -- <cmd> | merge <id> [--force] | close <id>`,
|
|
2635
2665
|
fork: "sol fork [<url>] <parent-repo> <new-repo> [dir] \u2014 your own copy (all branches + history + a parent link)",
|
|
2636
2666
|
forks: "sol forks \u2014 list the forks of the current repo",
|
|
2637
|
-
access: "sol access [show | public | private | add <userId> <read|write|admin> | remove <userId>]",
|
|
2667
|
+
access: "sol access [show | public | private | add <userId> <read|write|admin> | remove <userId>]\n tip: to share a NEW repo in one step, `sol push --public <repo>` (create + push + public) instead of pushing then `sol access public`.",
|
|
2668
|
+
share: "sol share \u2014 there is no separate share command: a new repo goes public in one step with `sol push --public <repo>`,\n or flip an existing one with `sol access public`. (new repos are private by default.)",
|
|
2638
2669
|
auth: "sol auth [login [<web-url>] | logout | status | whoami | set-handle <name> | pat [days]]",
|
|
2639
2670
|
clone: "sol clone [<url>] <owner>/<repo> [dir] \u2014 default dir = <repo> (url defaults to the hosted Sol)"
|
|
2640
2671
|
};
|
|
@@ -2648,7 +2679,7 @@ tip: \`sol push <repo>\` configures the hosted remote for you in one step.`,
|
|
|
2648
2679
|
if (t)
|
|
2649
2680
|
process.env.SOL_TOKEN = t;
|
|
2650
2681
|
}
|
|
2651
|
-
const release = new Set(["add", "track", "commit", "checkpoint", "rm", "gc", "branch", "tag", "switch", "merge", "pull", "run", "seal"]).has(cmd) && existsSync10(solDir2) ? acquireLock() : undefined;
|
|
2682
|
+
const release = new Set(["add", "track", "commit", "checkpoint", "rm", "gc", "branch", "tag", "switch", "merge", "pull", "push", "restore", "checkout", "run", "seal"]).has(cmd) && existsSync10(solDir2) ? acquireLock() : undefined;
|
|
2652
2683
|
try {
|
|
2653
2684
|
switch (cmd) {
|
|
2654
2685
|
case "init": {
|
|
@@ -2727,10 +2758,12 @@ tip: \`sol push <repo>\` configures the hosted remote for you in one step.`,
|
|
|
2727
2758
|
commitRoot = await repo.head();
|
|
2728
2759
|
} else {
|
|
2729
2760
|
const optedIn = wholeTreeOptIn || process.env.SOL_ALLOW_WHOLE_TREE === "1";
|
|
2730
|
-
const
|
|
2761
|
+
const otherActors = [...new Set((await log.history()).map((o) => o.by).filter((b) => !!b && b !== actor2))];
|
|
2762
|
+
const otherActorPresent = otherActors.length > 0;
|
|
2731
2763
|
if (process.env.SOL_ACTOR && !optedIn && otherActorPresent) {
|
|
2732
|
-
|
|
2733
|
-
|
|
2764
|
+
const others = otherActors.join(", ");
|
|
2765
|
+
die(`refusing a whole-tree commit as "${actor2}" \u2014 other author(s) have committed here (${others}), so sweeping the whole tree would mis-attribute their pending files to you. (this guard only fires because they're present; a solo author commits the whole tree freely.)
|
|
2766
|
+
scope to your files: sol commit -m "${message}" <your files> (or a per-agent SolWorkspace/MCP)
|
|
2734
2767
|
if you truly own the whole tree: add --whole-tree or set SOL_ALLOW_WHOLE_TREE=1`);
|
|
2735
2768
|
}
|
|
2736
2769
|
const snap = await snapshotTree(repo);
|
|
@@ -2767,8 +2800,11 @@ tip: \`sol push <repo>\` configures the hosted remote for you in one step.`,
|
|
|
2767
2800
|
const { repo, log } = open();
|
|
2768
2801
|
const refs = existsSync10(refsPath()) ? await loadRefs(log) : undefined;
|
|
2769
2802
|
const head = await repo.head();
|
|
2803
|
+
const headOp = head ? [...await log.history()].reverse().find((o) => o.rootAfter === head) : undefined;
|
|
2804
|
+
const headBy = headOp?.by ?? "?";
|
|
2770
2805
|
console.log(`repo ${cwd2}`);
|
|
2771
|
-
console.log(`on ${refs ? refs.current : "main"} head ${head.slice(0, 14)} seq ${await log.seq()}
|
|
2806
|
+
console.log(`on ${refs ? refs.current : "main"} head ${head ? `${head.slice(0, 14)} by ${headBy}` : "(empty)"} seq ${await log.seq()}`);
|
|
2807
|
+
console.log(`you: ${actor2}`);
|
|
2772
2808
|
const ch = workingChanges(loadStore(), head);
|
|
2773
2809
|
const dirty = ch.added.length + ch.modified.length + ch.removed.length;
|
|
2774
2810
|
if (!dirty) {
|
|
@@ -3407,9 +3443,9 @@ tip: \`sol push <repo>\` configures the hosted remote for you in one step.`,
|
|
|
3407
3443
|
}
|
|
3408
3444
|
case "push": {
|
|
3409
3445
|
const { log } = open();
|
|
3446
|
+
const wantPublic = args.includes("--public");
|
|
3410
3447
|
if (!loadRemote(solDir2)) {
|
|
3411
|
-
const
|
|
3412
|
-
const repoName = ci >= 0 ? args[ci + 1] : args.find((a) => !a.startsWith("-"));
|
|
3448
|
+
const repoName = args.find((a) => !a.startsWith("-"));
|
|
3413
3449
|
if (repoName) {
|
|
3414
3450
|
saveRemote(solDir2, { url: DEFAULT_REMOTE_URL, repo: repoName });
|
|
3415
3451
|
console.log(`remote set: ${DEFAULT_REMOTE_URL} (repo ${repoName})`);
|
|
@@ -3468,6 +3504,10 @@ tip: \`sol push <repo>\` configures the hosted remote for you in one step.`,
|
|
|
3468
3504
|
} else {
|
|
3469
3505
|
console.log(`pushed ${res.applied} op(s) -> remote (branch ${branch} @ ${(convergedHead || "").slice(0, 12)})${mergedNote}${conflictNote}${prod ? `; production=${prod}` : ""}`);
|
|
3470
3506
|
}
|
|
3507
|
+
if (wantPublic) {
|
|
3508
|
+
const a = await accessSet(cfg, token, { visibility: "public" });
|
|
3509
|
+
console.log(`${cfg.repo} is now ${a.visibility}`);
|
|
3510
|
+
}
|
|
3471
3511
|
break;
|
|
3472
3512
|
}
|
|
3473
3513
|
case "pull": {
|
|
@@ -4056,6 +4096,7 @@ remotes (self-hostable backend; token in SOL_TOKEN or via sol auth login):
|
|
|
4056
4096
|
sol clone [<url>] <owner>/<repo> [dir] clone a remote repo (checks out PRODUCTION); default dir = <repo>
|
|
4057
4097
|
sol push / sol pull sync your commits with the remote (push registers your branch's head)
|
|
4058
4098
|
sol push <repo> one-step share: no remote set? use the hosted Sol + <repo>, then push
|
|
4099
|
+
sol push --public <repo> create + push + make public in one step (new repos are private by default)
|
|
4059
4100
|
sol promote [branch] point the remote's production branch at <branch> (default: current)
|
|
4060
4101
|
sol remote <url> <repo> set the remote (no arg: show it; url defaults to the hosted Sol)
|
|
4061
4102
|
sol fork [<url>] <parent> <new> [dir] make your own copy of a repo (all branches + history + a parent link)
|
|
@@ -4076,7 +4117,8 @@ git interop (adopt Sol without leaving GitHub):
|
|
|
4076
4117
|
sol git export <repo> write Sol's tree back as a git commit (+ deletes), then \`git push\`
|
|
4077
4118
|
|
|
4078
4119
|
for concurrent agents, use scoped commits (sol commit -m "msg" file) or a per-agent workspace.
|
|
4079
|
-
a whole
|
|
4120
|
+
a solo author commits the whole tree freely; once OTHER authors are present the whole-tree commit is refused
|
|
4121
|
+
(--whole-tree / SOL_ALLOW_WHOLE_TREE=1 to override) so one agent can't sweep everyone's pending files.
|
|
4080
4122
|
|
|
4081
4123
|
a ref is a branch/tag name, a commit hash-prefix, an op seq number, or HEAD. content is SHA-256
|
|
4082
4124
|
addressed; history is a tamper-evident hash-chained op-log. attribute changes with SOL_ACTOR=you.`);
|