rigline 1.0.0-alpha.13 → 1.0.0-alpha.15

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
@@ -36,6 +36,12 @@ directory until *Developer: Reload Window*.
36
36
 
37
37
  **That is the whole of Rigline, and it needs nothing else.** Everything below is optional.
38
38
 
39
+ **Where it works:** VS Code on Windows, Linux and macOS, in a local window, with Node 22.12 or
40
+ newer. **Not supported yet:** remote windows, other editors built on VS Code, a portable VS Code, and
41
+ a custom extensions directory. In any of them, Claude Code's panel stays as it ships.
42
+ [Where Rigline works](https://github.com/Rigline/Rigline/blob/main/docs/support.md) lists each, and
43
+ how to ask for one.
44
+
39
45
  ## Optional: let an extension do the remembering
40
46
 
41
47
  rigline vscode-setup
@@ -60,11 +66,13 @@ your injection alone, and `rigline restore` is what undoes the injection itself.
60
66
 
61
67
  rigline install inject every version, bake plugins, report drift, record the baseline
62
68
  rigline check the same report, writing nothing
69
+ rigline watch install, and again whenever an extension update lands, until stopped
63
70
  rigline status per version: vanilla or patched, by backup
64
71
  rigline restore every version back to the extension's own bytes
72
+ rigline vscode-setup install the companion into VS Code; --remove takes it out
65
73
 
66
74
  rigline add SPEC install a plugin from a directory or npm, and re-inject
67
- rigline remove N delete a plugin rigline installed, and re-inject
75
+ rigline remove N delete a plugin from ~/.rigline/plugins, and re-inject
68
76
  rigline disable N switch a plugin off, and re-inject
69
77
  rigline enable N switch it back on, and re-inject
70
78
  rigline update move the engine and every npm plugin to what its tag resolves to
@@ -81,9 +89,8 @@ your injection alone, and `rigline restore` is what undoes the injection itself.
81
89
  rigline --help the whole surface, from the engine
82
90
 
83
91
  `add` never runs a package manager for a plugin: a published plugin is one bundled ES module and a
84
- manifest, so there is nothing to resolve. A version must reach a minimum age (a day, by default)
85
- before `add` or `update` will take it, and a withheld version is named rather than skipped in
86
- silence. `update` applies the same rule to the engine, and says which version it moved from so
92
+ manifest, so there is nothing to resolve. A version must be a day old before `add` or `update` will
93
+ take it, unless you pass `--now`, and a withheld version is named rather than skipped in silence. `update` applies the same rule to the engine, and says which version it moved from so
87
94
  going back is one command.
88
95
 
89
96
  ## How it is put together
@@ -100,7 +107,7 @@ project that can declare dependencies does not need a delivery mechanism.
100
107
 
101
108
  ## Writing a plugin
102
109
 
103
- npm create rigline-plugin
110
+ npm create rigline-plugin my-plugin
104
111
 
105
112
  [The authoring guide](https://github.com/Rigline/Rigline/blob/main/docs/authoring.md) is the long
106
113
  form. [Anchors](https://github.com/Rigline/Rigline/blob/main/docs/anchors.md) is what to read when
package/dist/commands.js CHANGED
@@ -37,7 +37,8 @@ export async function updateCommand(args, options = {}) {
37
37
  throw new UserError(problem);
38
38
  notice(engine.version, wrapper);
39
39
  const code = await engine.run(["update", ...forward]);
40
- return moved.outcome === "failed" ? 1 : code;
40
+ const needsYou = moved.outcome === "failed" || (moved.outcome === "withheld" && moved.by === "node");
41
+ return needsYou ? 1 : code;
41
42
  }
42
43
  export function notice(engine, wrapper = wrapperVersion()) {
43
44
  const line = newerWrapper(wrapper, engine);
package/dist/engine.js CHANGED
@@ -4,8 +4,10 @@ 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 { belowFloor } from "./floor.js";
7
8
  import { withHomeLock } from "./lock.js";
8
9
  import { releaseAgeProblem, resolveVersion } from "./registry.js";
10
+ export { belowFloor } from "./floor.js";
9
11
  export const ENGINE_PACKAGE = "@rigline/core";
10
12
  export const ENGINE_BIN = "rigline-engine";
11
13
  export function riglineHome(env = process.env) {
@@ -59,7 +61,7 @@ export function majorProblem(wrapper, engine) {
59
61
  if (ours === null || theirs === null || ours === theirs)
60
62
  return null;
61
63
  return (`rigline ${ours}.x cannot run engine ${theirs}.x (${ENGINE_PACKAGE} ${engine}). ` +
62
- "Run npm i -g rigline@latest.");
64
+ "Run npm i -g rigline@latest, then rigline vscode-setup if you use the companion.");
63
65
  }
64
66
  export function compareVersions(a, b) {
65
67
  const x = parseVersion(a);
@@ -118,14 +120,12 @@ export function handOffProblem(wrapper, engine) {
118
120
  }
119
121
  export function findNpmCli(execPath = process.execPath, exists = existsSync) {
120
122
  const here = dirname(execPath);
121
- const candidates = [
122
- join(here, "node_modules", "npm", "bin", "npm-cli.js"),
123
- join(here, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js"),
124
- ];
123
+ const npmIn = (...dir) => join(here, ...dir, "node_modules", "npm", "bin", "npm-cli.js");
124
+ const candidates = [npmIn(), npmIn("..", "lib"), npmIn("..", "libexec", "lib")];
125
125
  const found = candidates.find(exists);
126
126
  if (found === undefined) {
127
127
  throw new UserError(`no npm was found beside ${execPath}, so the engine cannot be installed. ` +
128
- `Run: npm install --prefix ${engineDir()} --save-exact --ignore-scripts ${ENGINE_PACKAGE}@latest`);
128
+ `Run: npm install --prefix "${engineDir()}" --save-exact --ignore-scripts ${ENGINE_PACKAGE}@latest`);
129
129
  }
130
130
  return found;
131
131
  }
@@ -246,9 +246,19 @@ export async function updateEngine(options = {}) {
246
246
  };
247
247
  }
248
248
  if (installed !== null) {
249
- const withheld = releaseAgeProblem(resolved, options.registry);
250
- if (withheld !== null) {
251
- return { outcome: "withheld", from: installed, to: resolved.version, reason: withheld };
249
+ const node = nodeFloorProblem(resolved, options);
250
+ if (node !== null) {
251
+ return {
252
+ outcome: "withheld",
253
+ from: installed,
254
+ to: resolved.version,
255
+ reason: node,
256
+ by: "node",
257
+ };
258
+ }
259
+ const age = releaseAgeProblem(resolved, options.registry);
260
+ if (age !== null) {
261
+ return { outcome: "withheld", from: installed, to: resolved.version, reason: age, by: "age" };
252
262
  }
253
263
  }
254
264
  try {
@@ -266,6 +276,16 @@ export async function updateEngine(options = {}) {
266
276
  ? { outcome: "installed", to: resolved.version }
267
277
  : { outcome: "moved", from: installed, to: resolved.version };
268
278
  }
279
+ function nodeFloorProblem(resolved, options) {
280
+ const running = options.nodeVersion ?? (options.nodePath === undefined ? process.versions.node : null);
281
+ if (running === null || resolved.enginesNode === null)
282
+ return null;
283
+ const floor = belowFloor(running, resolved.enginesNode);
284
+ if (floor === null)
285
+ return null;
286
+ return (`${ENGINE_PACKAGE} ${resolved.version} needs Node ${floor} or newer, and ` +
287
+ `${options.nodePath ?? process.execPath} is Node ${running}. Install a newer Node to move to it.`);
288
+ }
269
289
  export function formatEngineUpdate(update, home = riglineHome()) {
270
290
  switch (update.outcome) {
271
291
  case "installed":
package/dist/floor.js ADDED
@@ -0,0 +1,21 @@
1
+ import { readFileSync } from "node:fs";
2
+ export function belowFloor(running, range) {
3
+ const floor = /^>=\s*(\d+)\.(\d+)\.(\d+)$/.exec((range ?? "").trim());
4
+ if (floor === null)
5
+ return null;
6
+ const have = running.split(".").map(Number);
7
+ for (let at = 0; at < 3; at++) {
8
+ const want = Number(floor[at + 1]);
9
+ const got = have[at] ?? 0;
10
+ if (got > want)
11
+ return null;
12
+ if (got < want)
13
+ return floor.slice(1).join(".");
14
+ }
15
+ return null;
16
+ }
17
+ export function enginesNode() {
18
+ const manifest = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
19
+ return manifest?.engines?.node;
20
+ }
21
+ //# sourceMappingURL=floor.js.map
package/dist/index.js CHANGED
@@ -1,30 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import { notice, updateCommand, versionCommand } from "./commands.js";
3
- import { ensureEngine } from "./engine.js";
4
- import { UserError } from "./errors.js";
5
- async function main(argv) {
6
- const [command, ...rest] = argv;
7
- switch (command) {
8
- case "--version":
9
- case "-v":
10
- return versionCommand();
11
- case "update":
12
- return await updateCommand(rest);
13
- default: {
14
- const engine = await ensureEngine();
15
- notice(engine.version);
16
- return await engine.run(argv);
17
- }
18
- }
2
+ import { belowFloor, enginesNode } from "./floor.js";
3
+ const floor = belowFloor(process.versions.node, enginesNode());
4
+ if (floor === null) {
5
+ const { runWrapper } = await import("./main.js");
6
+ process.exitCode = await runWrapper(process.argv.slice(2));
7
+ }
8
+ else {
9
+ console.error(`rigline: Rigline needs Node ${floor} or newer, and ${process.execPath} is Node ` +
10
+ `${process.versions.node}; https://nodejs.org has one.`);
11
+ process.exitCode = 1;
19
12
  }
20
- main(process.argv.slice(2)).then((code) => {
21
- process.exitCode = code;
22
- }, (error) => {
23
- if (error instanceof UserError) {
24
- console.error(`rigline: ${error.message}`);
25
- process.exitCode = 1;
26
- return;
27
- }
28
- throw error;
29
- });
30
13
  //# sourceMappingURL=index.js.map
package/dist/lock.js CHANGED
@@ -2,6 +2,7 @@ import { closeSync, mkdirSync, openSync, readFileSync, rmSync, statSync, writeFi
2
2
  import { join } from "node:path";
3
3
  import { UserError } from "./errors.js";
4
4
  const STALE_MS = 60_000;
5
+ const HELD_MAX_MS = 10 * 60_000;
5
6
  const WAIT_MS = 30_000;
6
7
  const POLL_MS = 250;
7
8
  export function lockPath(home) {
@@ -41,8 +42,8 @@ function writtenAt(path) {
41
42
  }
42
43
  export function isStale(holder, fileTime, now, staleMs, isAlive) {
43
44
  const since = holder === null ? fileTime : Date.parse(holder.since);
44
- const old = Number.isNaN(since) || now - since >= staleMs;
45
- return old && (holder === null || !isAlive(holder.pid));
45
+ const age = Number.isNaN(since) ? Number.POSITIVE_INFINITY : now - since;
46
+ return age >= HELD_MAX_MS || (age >= staleMs && (holder === null || !isAlive(holder.pid)));
46
47
  }
47
48
  export class HomeLockedError extends UserError {
48
49
  holder;
package/dist/main.js ADDED
@@ -0,0 +1,40 @@
1
+ import { parseArgs } from "node:util";
2
+ import { notice, updateCommand, versionCommand } from "./commands.js";
3
+ import { ensureEngine } from "./engine.js";
4
+ import { UserError } from "./errors.js";
5
+ export async function runWrapper(argv) {
6
+ try {
7
+ return await dispatch(argv);
8
+ }
9
+ catch (error) {
10
+ if (!(error instanceof UserError))
11
+ throw error;
12
+ console.error(`rigline: ${error.message}`);
13
+ return 1;
14
+ }
15
+ }
16
+ async function dispatch(argv) {
17
+ const [command, ...rest] = argv;
18
+ switch (command) {
19
+ case "--version":
20
+ case "-v":
21
+ noArguments(rest);
22
+ return versionCommand();
23
+ case "update":
24
+ return await updateCommand(rest);
25
+ default: {
26
+ const engine = await ensureEngine();
27
+ notice(engine.version);
28
+ return await engine.run(argv);
29
+ }
30
+ }
31
+ }
32
+ function noArguments(args) {
33
+ try {
34
+ parseArgs({ args: [...args], options: {}, strict: true, allowPositionals: false });
35
+ }
36
+ catch (error) {
37
+ throw new UserError(`${error.message}\n\`rigline --help\` lists what each command takes.`);
38
+ }
39
+ }
40
+ //# sourceMappingURL=main.js.map
package/dist/registry.js CHANGED
@@ -17,6 +17,7 @@ export async function resolveVersion(spec, options = {}) {
17
17
  if (typeof tarball !== "string" || tarball.length === 0) {
18
18
  throw new UserError(`${spec.name}@${version} has no tarball on ${registryOf(options)}`);
19
19
  }
20
+ const node = release.engines?.node;
20
21
  return {
21
22
  name: spec.name,
22
23
  version,
@@ -24,6 +25,7 @@ export async function resolveVersion(spec, options = {}) {
24
25
  tarball,
25
26
  integrity: integrityOf(release, `${spec.name}@${version}`),
26
27
  ageMinutes,
28
+ enginesNode: typeof node === "string" ? node : null,
27
29
  };
28
30
  }
29
31
  export function releaseAgeProblem(resolved, options = {}) {
@@ -35,7 +37,7 @@ export function releaseAgeProblem(resolved, options = {}) {
35
37
  return (`${resolved.name}@${resolved.version} was published ${describeAge(resolved.ageMinutes)} ago, ` +
36
38
  `and rigline waits ${describeAge(minimum)} before installing a version. That window is where ` +
37
39
  "a compromised publish is usually caught, and it costs little here because the urgent repair " +
38
- "— an anchor an extension update retired — is a local edit rather than a release (D44). " +
40
+ "— an anchor an extension update retired — is a local edit rather than a release. " +
39
41
  "Wait, or pass --now.");
40
42
  }
41
43
  export function describeAge(minutes) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rigline",
3
- "version": "1.0.0-alpha.13",
3
+ "version": "1.0.0-alpha.15",
4
4
  "description": "Install, update and develop plugins for the Claude Code VS Code extension",
5
5
  "keywords": [
6
6
  "rigline",