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 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
- mkdirSync(prefix, { recursive: true });
124
- const argv = engineInstallArgv({ npmCli: findNpmCli(), prefix, specs: [spec] });
125
- const run = await capture(process.execPath, argv, options.spawnImpl);
126
- if (run.code !== 0) {
127
- throw new UserError(`installing ${spec} into ${prefix} failed (npm exited ${run.code}).\n${run.output.trim()}`);
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(process.execPath, [state.entry, ...argv], {
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(process.execPath, [state.entry, ...argv], spawnImpl, "inherit");
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
- const withheld = releaseAgeProblem(resolved, options.registry);
199
- if (withheld !== null) {
200
- return {
201
- outcome: "withheld",
202
- ...(installed === null ? {} : { from: installed }),
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 are installed";
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rigline",
3
- "version": "1.0.0-alpha.6",
3
+ "version": "1.0.0-alpha.9",
4
4
  "description": "Install, update and develop plugins for the Claude Code VS Code extension",
5
5
  "keywords": [
6
6
  "rigline",