rigline 1.0.0-alpha.6 → 1.0.0-alpha.9
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/README.md +25 -0
- package/dist/engine.js +19 -16
- package/dist/lock.js +88 -0
- package/dist/remote.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,6 +8,12 @@ capability-scoped context to write against, and harvests the identifiers those p
|
|
|
8
8
|
from whichever extension version you have installed — so an extension update is a diff to read
|
|
9
9
|
rather than a breakage to chase.
|
|
10
10
|
|
|
11
|
+
**This modifies files belonging to Anthropic's extension, on your machine, and that is your call to
|
|
12
|
+
make.** [Anthropic compliance](https://github.com/Rigline/Rigline/blob/main/docs/anthropic-compliance.md)
|
|
13
|
+
is a straight account of what Rigline does and does not do — no credentials, no network calls, no
|
|
14
|
+
rerouting of Claude usage, no redistribution — and how we read Anthropic's terms. `rigline restore`
|
|
15
|
+
puts every install back to Anthropic's own bytes and needs nothing but Node.
|
|
16
|
+
|
|
11
17
|
## Install
|
|
12
18
|
|
|
13
19
|
npm install -g rigline
|
|
@@ -26,6 +32,25 @@ The extension updates about weekly and an update installs a fresh directory, whi
|
|
|
26
32
|
the injection. Run `install` again afterwards; a window that is already open keeps running the old
|
|
27
33
|
directory until *Developer: Reload Window*.
|
|
28
34
|
|
|
35
|
+
**That is the whole of Rigline, and it needs nothing else.** Everything below is optional.
|
|
36
|
+
|
|
37
|
+
## Optional: let an extension do the remembering
|
|
38
|
+
|
|
39
|
+
rigline vscode-setup
|
|
40
|
+
|
|
41
|
+
If running `install` after every update is a chore, this installs a companion extension that does it
|
|
42
|
+
for you — into every VS Code found on your `PATH`, Insiders, VSCodium, Cursor and Windsurf included,
|
|
43
|
+
from a VSIX that ships inside the engine. Nothing is downloaded, and the companion moves when the
|
|
44
|
+
engine does. It injects as it goes, so it is a step *instead of* `install` rather than after it.
|
|
45
|
+
|
|
46
|
+
From then on it watches for the extension update and re-injects behind it, so there is nothing to
|
|
47
|
+
remember after one.
|
|
48
|
+
|
|
49
|
+
**Decline it and lose nothing.** `install` remains complete on its own, and is the right answer if
|
|
50
|
+
you would rather not add an extension, cannot install one, or simply prefer running the thing
|
|
51
|
+
yourself. `rigline vscode-setup --remove` goes back to that — it takes the companion out and leaves
|
|
52
|
+
your injection alone, and `rigline restore` is what undoes the injection itself.
|
|
53
|
+
|
|
29
54
|
## Commands
|
|
30
55
|
|
|
31
56
|
rigline install inject every version, bake plugins, report drift, record the baseline
|
package/dist/engine.js
CHANGED
|
@@ -4,6 +4,7 @@ import { homedir } from "node:os";
|
|
|
4
4
|
import { dirname, join, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { UserError } from "./errors.js";
|
|
7
|
+
import { withHomeLock } from "./lock.js";
|
|
7
8
|
import { releaseAgeProblem, resolveVersion } from "./registry.js";
|
|
8
9
|
export const ENGINE_PACKAGE = "@rigline/core";
|
|
9
10
|
export const ENGINE_BIN = "rigline-engine";
|
|
@@ -120,27 +121,32 @@ async function resolveEngine(options) {
|
|
|
120
121
|
return await resolveVersion({ name: ENGINE_PACKAGE, version: null, tag: options.tag ?? "latest" }, options.registry);
|
|
121
122
|
}
|
|
122
123
|
async function installEngine(prefix, spec, options) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
const home = options.home ?? riglineHome();
|
|
125
|
+
await withHomeLock({ home, what: options.label ?? "rigline", ...options.lock }, async () => {
|
|
126
|
+
mkdirSync(prefix, { recursive: true });
|
|
127
|
+
const node = options.nodePath ?? process.execPath;
|
|
128
|
+
const argv = engineInstallArgv({ npmCli: findNpmCli(node), prefix, specs: [spec] });
|
|
129
|
+
const run = await capture(node, argv, options.spawnImpl);
|
|
130
|
+
if (run.code !== 0) {
|
|
131
|
+
throw new UserError(`installing ${spec} into ${prefix} failed (npm exited ${run.code}).\n${run.output.trim()}`);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
129
134
|
}
|
|
130
135
|
function engineAt(state, options) {
|
|
131
136
|
const spawnImpl = options.spawnImpl ?? nodeSpawn;
|
|
137
|
+
const node = options.nodePath ?? process.execPath;
|
|
132
138
|
return {
|
|
133
139
|
version: state.version,
|
|
134
140
|
entry: state.entry,
|
|
135
141
|
run: (argv) => new Promise((done, fail) => {
|
|
136
|
-
const child = spawnImpl(
|
|
142
|
+
const child = spawnImpl(node, [state.entry, ...argv], {
|
|
137
143
|
stdio: ["inherit", "inherit", "inherit"],
|
|
138
144
|
});
|
|
139
145
|
child.on("error", fail);
|
|
140
146
|
child.on("close", (code) => done(code ?? 1));
|
|
141
147
|
}),
|
|
142
148
|
json: async (argv) => {
|
|
143
|
-
const run = await capture(
|
|
149
|
+
const run = await capture(node, [state.entry, ...argv], spawnImpl, "inherit");
|
|
144
150
|
if (run.code !== 0) {
|
|
145
151
|
throw new UserError(`the engine exited ${run.code} for \`${argv.join(" ")}\``);
|
|
146
152
|
}
|
|
@@ -195,14 +201,11 @@ export async function updateEngine(options = {}) {
|
|
|
195
201
|
reason: problem,
|
|
196
202
|
};
|
|
197
203
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
outcome: "withheld",
|
|
202
|
-
|
|
203
|
-
to: resolved.version,
|
|
204
|
-
reason: withheld,
|
|
205
|
-
};
|
|
204
|
+
if (installed !== null) {
|
|
205
|
+
const withheld = releaseAgeProblem(resolved, options.registry);
|
|
206
|
+
if (withheld !== null) {
|
|
207
|
+
return { outcome: "withheld", from: installed, to: resolved.version, reason: withheld };
|
|
208
|
+
}
|
|
206
209
|
}
|
|
207
210
|
try {
|
|
208
211
|
await installEngine(prefix, `${ENGINE_PACKAGE}@${resolved.version}`, options);
|
package/dist/lock.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { closeSync, mkdirSync, openSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { UserError } from "./errors.js";
|
|
4
|
+
const STALE_MS = 60_000;
|
|
5
|
+
const WAIT_MS = 30_000;
|
|
6
|
+
const POLL_MS = 250;
|
|
7
|
+
export function lockPath(home) {
|
|
8
|
+
return join(home, ".lock");
|
|
9
|
+
}
|
|
10
|
+
function alive(pid) {
|
|
11
|
+
try {
|
|
12
|
+
process.kill(pid, 0);
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
return error.code === "EPERM";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function readHolder(path) {
|
|
20
|
+
try {
|
|
21
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
22
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
23
|
+
return null;
|
|
24
|
+
const { pid, since, what } = parsed;
|
|
25
|
+
if (typeof pid !== "number" || typeof since !== "string" || typeof what !== "string") {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
return { pid, since, what };
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function isStale(holder, now, staleMs, isAlive) {
|
|
35
|
+
if (holder === null)
|
|
36
|
+
return true;
|
|
37
|
+
const since = Date.parse(holder.since);
|
|
38
|
+
const old = Number.isNaN(since) || now - since >= staleMs;
|
|
39
|
+
return old && !isAlive(holder.pid);
|
|
40
|
+
}
|
|
41
|
+
export class HomeLockedError extends UserError {
|
|
42
|
+
holder;
|
|
43
|
+
constructor(path, holder) {
|
|
44
|
+
super(holder === null
|
|
45
|
+
? `another Rigline process is using ${path}. Try again, or delete that file if nothing is.`
|
|
46
|
+
: `${holder.what} (pid ${holder.pid}) has been working under Rigline's home since ` +
|
|
47
|
+
`${holder.since}. Try again when it has finished, or delete ${path} if it has not.`);
|
|
48
|
+
this.holder = holder;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export async function withHomeLock(options, work) {
|
|
52
|
+
const { home, what, waitMs = WAIT_MS, staleMs = STALE_MS, pollMs = POLL_MS, now = Date.now, isAlive = alive, sleep = (ms) => new Promise((done) => setTimeout(done, ms)), } = options;
|
|
53
|
+
const path = lockPath(home);
|
|
54
|
+
mkdirSync(home, { recursive: true });
|
|
55
|
+
const deadline = now() + waitMs;
|
|
56
|
+
for (;;) {
|
|
57
|
+
const holder = { pid: process.pid, since: new Date(now()).toISOString(), what };
|
|
58
|
+
try {
|
|
59
|
+
const fd = openSync(path, "wx");
|
|
60
|
+
try {
|
|
61
|
+
writeFileSync(fd, JSON.stringify(holder, null, 2));
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
closeSync(fd);
|
|
65
|
+
}
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (error.code !== "EEXIST")
|
|
70
|
+
throw error;
|
|
71
|
+
const held = readHolder(path);
|
|
72
|
+
if (isStale(held, now(), staleMs, isAlive)) {
|
|
73
|
+
rmSync(path, { force: true });
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (now() >= deadline)
|
|
77
|
+
throw new HomeLockedError(path, held);
|
|
78
|
+
await sleep(pollMs);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
return await work();
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
rmSync(path, { force: true });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=lock.js.map
|
package/dist/remote.js
CHANGED
|
@@ -108,7 +108,7 @@ async function updateOne(name, source, options) {
|
|
|
108
108
|
}
|
|
109
109
|
export function formatUpdates(updates) {
|
|
110
110
|
if (updates.length === 0)
|
|
111
|
-
return "no plugins
|
|
111
|
+
return "no plugins to update — the bundled ones move with the engine";
|
|
112
112
|
return updates.map(updateLine).join("\n");
|
|
113
113
|
}
|
|
114
114
|
function updateLine(update) {
|