@rness/cli 0.3.0 → 0.5.0

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
@@ -6,6 +6,8 @@ Not an agent — no LLM loop.
6
6
 
7
7
  ## Install
8
8
 
9
+ npm create rness # prompts for the organization, then the repositories for your workspace
10
+ npm create rness acme # the organization github.com/acme, no first prompt
9
11
  npm i -g @rness/cli # the command is `rness`
10
12
  npx @rness/cli --help
11
13
 
@@ -14,14 +16,182 @@ Inside a workspace, every `rness` delegates to the copy pinned in
14
16
 
15
17
  ## Commands
16
18
 
17
- rness context [--scope <name>] [--json] Resolve the context for a scope
18
- rness validate Check .rness/ and every generated block
19
- rness sync [--scope <name>] [--check] [--pull] -y Clone repositories, write the blocks
19
+ rness create [<org>] [--repos a,b] [--pm npm|pnpm|yarn|bun] [--ssh|--https] [--skip-install] -y
20
+ rness add <repo> [--scopes apps/web,packages/ui] [--ssh|--https] -y
21
+ rness sync [--all] [--scope <name>] [--check] [--pull] -y
22
+ rness upgrade [<version>] -y
23
+ rness login [--setup-git|--no-setup-git]
24
+ rness logout
25
+ rness context [--scope <name>] [--json]
26
+ rness validate
20
27
 
21
- Exit codes: 0 success, 1 handled error, 2 bad usage. `RNESS_DEBUG=1` adds
22
- stack traces; `RNESS_NO_DELEGATE=1` skips the delegation. `sync` asks for
23
- confirmation in a terminal; pass `-y`/`--yes` in scripts. `--check` writes
24
- nothing and exits 1 when a block is out of date — use it in CI.
28
+ `create` never runs inside a workspace. `add`, `sync` and `create` ask for
29
+ confirmation in a terminal; pass `-y`/`--yes` in scripts. `sync --check`
30
+ writes nothing and exits 1 when a block is out of date — use it in CI.
31
+
32
+ ### Log in
33
+
34
+ rness login # shows a code and https://github.com/login/device
35
+ rness logout
36
+
37
+ `rness login` connects rness to your GitHub account (OAuth device flow: you
38
+ approve a code in a browser, on any machine). Logged in, `create` lists the
39
+ private repositories you can access, offers your organizations to choose
40
+ from, and says as whom it looks at one (`member acme (as you)`). The
41
+ `create` wizard offers the login itself when it would otherwise list
42
+ anonymously.
43
+
44
+ - The login is one file, `~/.config/rness/auth.json` (`$XDG_CONFIG_HOME`,
45
+ `%APPDATA%` on Windows), readable by you only. The access token lives 8
46
+ hours and is renewed on its own. `GITHUB_TOKEN`, then `GH_TOKEN`, win over
47
+ it — CI needs no login.
48
+ - rness asks for `repo` and `read:org`: GitHub has no read-only scope for
49
+ private repositories. It only lists and clones.
50
+ - A new workspace has to reach GitHub before teammates can join it. Logged
51
+ in, the `create` wizard offers to do it: it creates the private repository
52
+ `<org>/.rness` and pushes the context with `git`. Declined, refused by
53
+ GitHub, anonymous or with `--yes`, it prints the two manual steps instead
54
+ — create the empty repository on github.com, then `git remote add origin …
55
+ && git push`. No other tool is ever needed.
56
+ - An organization that restricts OAuth apps hides its private repositories
57
+ until an owner approves "Rness"; `create` says so, with the link.
58
+ - Over HTTPS, rness's own clones and pulls carry the login — through the
59
+ environment of that one git command, never in a URL or `.git/config`. For
60
+ your own `git pull` and `git push`, `login` offers to make rness git's
61
+ credential helper for github.com (`--setup-git`, `--no-setup-git`);
62
+ `logout` undoes it. It needs a global install (`npm i -g @rness/cli`): a
63
+ copy run through `npx` lives in a cache. Over SSH none of this is needed.
64
+ The helper names `node` by its absolute path: after a Node upgrade through
65
+ a version manager (mise, nvm), run `rness login --setup-git` again.
66
+ - `rness logout` forgets the login; revoke the authorization itself in
67
+ GitHub's settings (the command prints the link).
68
+
69
+ ### SSH or HTTPS
70
+
71
+ `create` and `add` test your SSH access to github.com once
72
+ (`ssh -T git@github.com`) and write `git@github.com:` URLs when GitHub accepts
73
+ your key, `https://github.com/` ones otherwise; `--ssh` and `--https` decide
74
+ without the test. In a terminal `ssh` may ask for your key's passphrase;
75
+ without one (`--yes`, CI) it never prompts, so a key that needs a passphrase
76
+ counts as no access unless an agent holds it.
77
+
78
+ `rness.json` is authoritative: a URL written there is cloned as written, never
79
+ converted. In a workspace whose catalogue clones over SSH, `create` (joining)
80
+ and `sync` stop before the first clone when the SSH test fails and say how to
81
+ set up a key; `add` offers "Clone <name> over HTTPS instead?" (or `--https`),
82
+ which writes an HTTPS URL for that repository only.
83
+
84
+ Without an agent, a protected key is asked for again by every `git clone`.
85
+ Load it once per session: `ssh-add --apple-use-keychain ~/.ssh/id_ed25519` on
86
+ macOS, `ssh-add ~/.ssh/id_ed25519` elsewhere.
87
+
88
+ ### Colour
89
+
90
+ In a terminal `create`, `login`, `add`, `sync` and `upgrade` run as one
91
+ session: an intro, a line per finished step, spinners, a "Next" box, an
92
+ outro — in the same gutter as the questions. A spinner never animates while
93
+ git might ask for a passphrase: rness first tests SSH unattended, and clones
94
+ animate only when that passed. The help and the banner are coloured. Through a pipe, in CI, or with `NO_COLOR=1`, the output is plain text —
95
+ the same bytes as before 0.5.0; `FORCE_COLOR=1` paints it anyway. No
96
+ dependency does this: `util.styleText`, and a banner kept as a constant.
97
+
98
+ ### Upgrade
99
+
100
+ rness upgrade # the latest release; `rness upgrade 0.5.0` for another
101
+ npx @rness/cli@latest upgrade # from a workspace pinned below 0.5.0
102
+
103
+ The version is written in one place, `.rness/package.json`. `upgrade` pins it
104
+ there (exact, the rest of the file untouched), installs with the workspace's
105
+ package manager, then syncs through the new copy; a failed install restores
106
+ the file. Commit `.rness`; teammates pull and install in `.rness`. Until they
107
+ do, every command warns that the workspace pins one version and holds
108
+ another. It is never delegated to the pinned copy, which is what it replaces
109
+ — so a workspace pinned below 0.5.0, whose copy has no such command, starts
110
+ with the `npx` form.
111
+
112
+ An upgrade touches `.rness` only: a generated block is current when its hash
113
+ is, whatever wrote it, so no `AGENTS.md` of the organization changes unless
114
+ its content does.
115
+
116
+ Exit codes: 0 success, 1 failure, 2 usage — or a refusal without a TTY.
117
+ `RNESS_DEBUG=1` adds stack traces; `RNESS_NO_DELEGATE=1` skips the delegation.
118
+
119
+ ## 0.5.0 — the organization is the workspace; rness.json is its catalogue
120
+
121
+ - `create <org>` names the organization, not a directory any more (`--org
122
+ <org>` and the prompt "What is your GitHub organization named?" do the
123
+ same). The workspace directory is `./<org>`, with the organization's exact
124
+ name; `--dir` is removed. With `npm create`, flags go after `--`:
125
+ `npm create rness acme -- --yes`.
126
+ - SSH first: `create` and `add` write `git@github.com:` URLs when
127
+ `ssh -T git@github.com` accepts your key, HTTPS ones otherwise (0.4.0
128
+ wrote HTTPS unless `--ssh`). `--https` is new. A private `.rness` you reach
129
+ over SSH is now found when joining. See "SSH or HTTPS" above.
130
+ - `create` does not offer repositories whose name starts with a dot
131
+ (`.github`, …).
132
+ - Colour, a banner and progress lines in a terminal (see "Colour" above);
133
+ nothing changes for scripts.
134
+ - `rness login` / `rness logout` are new: private repositories are listed,
135
+ and cloned over HTTPS with the login (see "Log in" above). Without a login
136
+ the note reads `rness login lists the private ones you can access`.
137
+ - `rness upgrade` is new (see "Upgrade" above). From 0.4.0:
138
+ `npx @rness/cli@latest upgrade`.
139
+ - The header of a generated block no longer names the CLI version
140
+ (`<!-- rness · scope: … -->`), and `sync` decides by the hash like
141
+ `validate`: upgrading rewrites no `AGENTS.md`. Blocks written by 0.2–0.4
142
+ stay as they are until their content changes. A 0.4.0 CLI calls the new
143
+ header stale — upgrade every copy together.
144
+ - The scaffold's CI workflow reads the version from `package.json` instead of
145
+ naming it; `upgrade` migrates the old line.
146
+ - `.rness/rness.json` is the organization's catalogue — every repository
147
+ rness knows about, shared by the team. Your workspace is the part of it
148
+ you cloned into `org/`; two teammates can clone different repositories,
149
+ and nothing but `org/` records the choice. A choice never removes a
150
+ repository from the catalogue.
151
+ - `create` lists the organization's repositories to search and pick from,
152
+ with every catalogue repository pre-selected when joining (a catalogue
153
+ repository the listing does not return is still offered, marked
154
+ `in .rness`). A picked catalogue repository is cloned; a picked new one is
155
+ added to the catalogue, as `rness add` does. Without a picker, `--repos`
156
+ is the selection; a join without `--repos` clones the whole catalogue. A
157
+ cancelled or declined prompt writes nothing and exits 0.
158
+ - The listing needs no credentials. Without a `GITHUB_TOKEN` (or `GH_TOKEN`)
159
+ only public repositories are listed — and a personal account always lists
160
+ only its public ones; add the others later with `rness add <repo>`.
161
+ - `rness sync` works on your clones: it writes the blocks of the repositories
162
+ in `org/`, never of `rness.json` as a whole. In a terminal it asks "Do you
163
+ want to sync as well?" with the catalogue repositories you have not cloned
164
+ ("Select all", or pick some); what you pick is cloned first. With `--yes`,
165
+ or `--check`, nothing is cloned and one `not cloned:` line names them
166
+ (`--check` does not fail on it). `rness sync --all` clones them all without
167
+ asking. A clone `rness.json` does not know gets no block and a
168
+ `not in rness.json:` line pointing at `rness add`.
169
+ - A workspace pinned to 0.4.x clones every catalogue repository on sync —
170
+ `create` runs the pinned copy's sync after joining.
171
+ - Joining a workspace whose `.rness` pins an older `@rness/cli` than the one
172
+ you run says so, and that `rness upgrade` moves it.
173
+ - Repository and scope names take what GitHub allows, in lowercase: letters,
174
+ digits, `.`, `_` and `-` (`my_lib`, `angular.js`). A name that differs only
175
+ by case is declared in lowercase. A 0.4.0 CLI rejects a `rness.json` that
176
+ holds a `.` or a `_` in a name — upgrade every copy together.
177
+ - Organization names follow GitHub's rule — letters, digits and single
178
+ hyphens, not at either end, at most 39 characters — in `--org` and in
179
+ `rness.json`'s `"org"`; the case is kept as typed.
180
+ - `"org"` in `rness.json` now accepts uppercase letters: a 0.4.0 CLI rejects
181
+ a workspace whose `"org"` has any, so upgrade every copy together.
182
+
183
+ ## 0.4.0 — create, add, the shims
184
+
185
+ - `npm create rness <org>` starts a new workspace for `github.com/<org>` or
186
+ joins its existing `.rness`.
187
+ - `rness add <repo>` clones (or adopts) a repository under `org/`, declares
188
+ it and any `--scopes` sub-directories, then syncs.
189
+ - `create-rness` and `@rness/create` are two-file shims pinned to the exact
190
+ same version as `@rness/cli`, so the short `npm create` commands run this
191
+ CLI.
192
+ - A new workspace's scaffold ships a `pnpm-workspace.yaml`
193
+ (`minimumReleaseAgeExclude: ['@rness/cli']`) — pnpm 12 otherwise refuses a
194
+ version published less than 24h earlier.
25
195
 
26
196
  ## 0.3.0 — what changed for a 0.2.0 workspace
27
197
 
package/dist/bin/rness.js CHANGED
@@ -1,38 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  VERSION,
4
- findWorkspace,
4
+ driftWarning,
5
+ findDelegate,
5
6
  reportError,
6
7
  run
7
- } from "../chunk-4KNZT4G4.js";
8
+ } from "../chunk-WSGPDTPQ.js";
8
9
 
9
10
  // src/bin/rness.ts
10
11
  import { pathToFileURL } from "url";
11
12
 
12
- // src/core/delegate.ts
13
- import { readFile } from "fs/promises";
14
- import { join } from "path";
15
- async function findDelegate(cwd, ownVersion) {
16
- let root;
17
- let rnessDir;
18
- try {
19
- const ws = await findWorkspace(cwd);
20
- root = ws.root;
21
- rnessDir = ws.rnessDir;
22
- } catch {
23
- return null;
24
- }
25
- const pkgDir = join(rnessDir, "node_modules", "@rness", "cli");
26
- let version;
27
- try {
28
- version = JSON.parse(await readFile(join(pkgDir, "package.json"), "utf8")).version;
29
- } catch {
30
- return null;
31
- }
32
- if (typeof version !== "string" || version === ownVersion) return null;
33
- return { entry: join(pkgDir, "dist", "index.js"), version, root };
34
- }
35
-
36
13
  // src/core/node-version.ts
37
14
  var MIN_NODE_MAJOR = 24;
38
15
  function nodeMajor(version) {
@@ -50,18 +27,39 @@ process.stdout.on("error", (e) => {
50
27
  });
51
28
  async function main() {
52
29
  if (!isSupportedNode(process.versions.node)) {
53
- process.stderr.write(`rness needs Node ${MIN_NODE_MAJOR} or newer (running ${process.version})
54
- `);
30
+ process.stderr.write(
31
+ `rness needs Node ${MIN_NODE_MAJOR} or newer (running ${process.version})
32
+ `
33
+ );
55
34
  return 1;
56
35
  }
57
36
  const argv = process.argv.slice(2);
58
- const skipDelegation = argv[0] === "create" || process.env["RNESS_NO_DELEGATE"] === "1";
37
+ const own = [
38
+ "create",
39
+ "upgrade",
40
+ "update",
41
+ "login",
42
+ "logout",
43
+ "git-credential"
44
+ ].includes(argv[0] ?? "");
45
+ const noDelegate = process.env["RNESS_NO_DELEGATE"] === "1";
46
+ const skipDelegation = own || noDelegate;
47
+ const command = argv[0];
48
+ if (command !== void 0 && !command.startsWith("-") && !own && !noDelegate) {
49
+ const warning = await driftWarning(process.cwd());
50
+ if (warning !== null) process.stderr.write(`${warning}
51
+ `);
52
+ }
59
53
  const delegate = skipDelegation ? null : await findDelegate(process.cwd(), VERSION);
60
54
  if (delegate !== null) {
61
- const broken = (reason) => new Error(`@rness/cli ${delegate.version} pinned in ${delegate.root}/.rness is not installed correctly (${reason}); reinstall in .rness/`);
55
+ const broken = (reason) => new Error(
56
+ `@rness/cli ${delegate.version} pinned in ${delegate.root}/.rness is not installed correctly (${reason}); reinstall in .rness/`
57
+ );
62
58
  if (process.env["RNESS_DEBUG"] === "1") {
63
- process.stderr.write(`rness: delegating to @rness/cli ${delegate.version} (${delegate.entry})
64
- `);
59
+ process.stderr.write(
60
+ `rness: delegating to @rness/cli ${delegate.version} (${delegate.entry})
61
+ `
62
+ );
65
63
  }
66
64
  let mod;
67
65
  try {
@@ -69,7 +67,8 @@ async function main() {
69
67
  } catch (e) {
70
68
  throw broken(e instanceof Error ? e.message : String(e));
71
69
  }
72
- if (typeof mod["run"] !== "function") throw broken("dist/index.js does not export run");
70
+ if (typeof mod["run"] !== "function")
71
+ throw broken("dist/index.js does not export run");
73
72
  const delegateRun = mod["run"];
74
73
  const code = await delegateRun(argv);
75
74
  return typeof code === "number" ? code : 1;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/bin/rness.ts","../../src/core/delegate.ts","../../src/core/node-version.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { pathToFileURL } from 'node:url'\nimport { run } from '../cli.ts'\nimport { VERSION } from '../version.ts'\nimport { findDelegate } from '../core/delegate.ts'\nimport { isSupportedNode, MIN_NODE_MAJOR } from '../core/node-version.ts'\nimport { reportError } from '../report.ts'\n\n// `rness context | head`: once the reader has its lines it closes the pipe and\n// every further write emits EPIPE on stdout. Nothing is left to deliver, so end\n// quietly with success. This is the one process.exit() in the code base — it\n// runs only when there is no output left to drain (see main() below).\nprocess.stdout.on('error', (e: NodeJS.ErrnoException) => {\n if (e.code === 'EPIPE') process.exit(0)\n throw e\n})\n\n// Set process.exitCode rather than calling process.exit(): process.exit()\n// terminates before Node drains an async stdout pipe, truncating piped output\n// at the ~64 KiB pipe buffer (breaks `rness context --json | jq`, `$(...)`).\nasync function main(): Promise<number> {\n if (!isSupportedNode(process.versions.node)) {\n process.stderr.write(`rness needs Node ${MIN_NODE_MAJOR} or newer (running ${process.version})\\n`)\n return 1\n }\n const argv = process.argv.slice(2)\n const skipDelegation = argv[0] === 'create' || process.env['RNESS_NO_DELEGATE'] === '1'\n const delegate = skipDelegation ? null : await findDelegate(process.cwd(), VERSION)\n if (delegate !== null) {\n const broken = (reason: string): Error =>\n new Error(`@rness/cli ${delegate.version} pinned in ${delegate.root}/.rness is not installed correctly (${reason}); reinstall in .rness/`)\n if (process.env['RNESS_DEBUG'] === '1') {\n process.stderr.write(`rness: delegating to @rness/cli ${delegate.version} (${delegate.entry})\\n`)\n }\n let mod: Record<string, unknown>\n try {\n mod = (await import(pathToFileURL(delegate.entry).href)) as Record<string, unknown>\n } catch (e) {\n throw broken(e instanceof Error ? e.message : String(e))\n }\n if (typeof mod['run'] !== 'function') throw broken('dist/index.js does not export run')\n const delegateRun = mod['run'] as (argv: string[]) => Promise<unknown>\n const code = await delegateRun(argv)\n return typeof code === 'number' ? code : 1\n }\n return run(argv)\n}\n\nmain()\n .then((code) => {\n process.exitCode = code\n })\n .catch((e: unknown) => {\n process.exitCode = reportError(e)\n })\n","import { readFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { findWorkspace } from './workspace.ts'\n\nexport interface Delegate {\n /** Absolute path of the pinned copy's `dist/index.js`. */\n entry: string\n version: string\n /** Workspace root (the directory holding `.rness/`). */\n root: string\n}\n\n/**\n * The `@rness/cli` pinned in `<workspace>/.rness/package.json` (installed under\n * `.rness/node_modules`), when it exists and is not the running version. The\n * pinned copy is the only one that should ever act on a workspace.\n */\nexport async function findDelegate(cwd: string, ownVersion: string): Promise<Delegate | null> {\n let root: string\n let rnessDir: string\n try {\n const ws = await findWorkspace(cwd)\n root = ws.root\n rnessDir = ws.rnessDir\n } catch {\n return null\n }\n const pkgDir = join(rnessDir, 'node_modules', '@rness', 'cli')\n let version: unknown\n try {\n version = (JSON.parse(await readFile(join(pkgDir, 'package.json'), 'utf8')) as { version?: unknown }).version\n } catch {\n return null\n }\n if (typeof version !== 'string' || version === ownVersion) return null\n return { entry: join(pkgDir, 'dist', 'index.js'), version, root }\n}\n","export const MIN_NODE_MAJOR = 24\n\nexport function nodeMajor(version: string): number {\n const major = Number.parseInt(version.split('.')[0] ?? '', 10)\n return Number.isNaN(major) ? 0 : major\n}\n\nexport function isSupportedNode(version: string): boolean {\n return nodeMajor(version) >= MIN_NODE_MAJOR\n}\n"],"mappings":";;;;;;;;;AACA,SAAS,qBAAqB;;;ACD9B,SAAS,gBAAgB;AACzB,SAAS,YAAY;AAgBrB,eAAsB,aAAa,KAAa,YAA8C;AAC5F,MAAI;AACJ,MAAI;AACJ,MAAI;AACF,UAAM,KAAK,MAAM,cAAc,GAAG;AAClC,WAAO,GAAG;AACV,eAAW,GAAG;AAAA,EAChB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,SAAS,KAAK,UAAU,gBAAgB,UAAU,KAAK;AAC7D,MAAI;AACJ,MAAI;AACF,cAAW,KAAK,MAAM,MAAM,SAAS,KAAK,QAAQ,cAAc,GAAG,MAAM,CAAC,EAA4B;AAAA,EACxG,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,OAAO,YAAY,YAAY,YAAY,WAAY,QAAO;AAClE,SAAO,EAAE,OAAO,KAAK,QAAQ,QAAQ,UAAU,GAAG,SAAS,KAAK;AAClE;;;ACpCO,IAAM,iBAAiB;AAEvB,SAAS,UAAU,SAAyB;AACjD,QAAM,QAAQ,OAAO,SAAS,QAAQ,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,EAAE;AAC7D,SAAO,OAAO,MAAM,KAAK,IAAI,IAAI;AACnC;AAEO,SAAS,gBAAgB,SAA0B;AACxD,SAAO,UAAU,OAAO,KAAK;AAC/B;;;AFGA,QAAQ,OAAO,GAAG,SAAS,CAAC,MAA6B;AACvD,MAAI,EAAE,SAAS,QAAS,SAAQ,KAAK,CAAC;AACtC,QAAM;AACR,CAAC;AAKD,eAAe,OAAwB;AACrC,MAAI,CAAC,gBAAgB,QAAQ,SAAS,IAAI,GAAG;AAC3C,YAAQ,OAAO,MAAM,oBAAoB,cAAc,sBAAsB,QAAQ,OAAO;AAAA,CAAK;AACjG,WAAO;AAAA,EACT;AACA,QAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,QAAM,iBAAiB,KAAK,CAAC,MAAM,YAAY,QAAQ,IAAI,mBAAmB,MAAM;AACpF,QAAM,WAAW,iBAAiB,OAAO,MAAM,aAAa,QAAQ,IAAI,GAAG,OAAO;AAClF,MAAI,aAAa,MAAM;AACrB,UAAM,SAAS,CAAC,WACd,IAAI,MAAM,cAAc,SAAS,OAAO,cAAc,SAAS,IAAI,uCAAuC,MAAM,yBAAyB;AAC3I,QAAI,QAAQ,IAAI,aAAa,MAAM,KAAK;AACtC,cAAQ,OAAO,MAAM,mCAAmC,SAAS,OAAO,KAAK,SAAS,KAAK;AAAA,CAAK;AAAA,IAClG;AACA,QAAI;AACJ,QAAI;AACF,YAAO,MAAM,OAAO,cAAc,SAAS,KAAK,EAAE;AAAA,IACpD,SAAS,GAAG;AACV,YAAM,OAAO,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;AAAA,IACzD;AACA,QAAI,OAAO,IAAI,KAAK,MAAM,WAAY,OAAM,OAAO,mCAAmC;AACtF,UAAM,cAAc,IAAI,KAAK;AAC7B,UAAM,OAAO,MAAM,YAAY,IAAI;AACnC,WAAO,OAAO,SAAS,WAAW,OAAO;AAAA,EAC3C;AACA,SAAO,IAAI,IAAI;AACjB;AAEA,KAAK,EACF,KAAK,CAAC,SAAS;AACd,UAAQ,WAAW;AACrB,CAAC,EACA,MAAM,CAAC,MAAe;AACrB,UAAQ,WAAW,YAAY,CAAC;AAClC,CAAC;","names":[]}
1
+ {"version":3,"sources":["../../src/bin/rness.ts","../../src/core/node-version.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { pathToFileURL } from 'node:url'\n\nimport { run } from '../cli.ts'\nimport { findDelegate } from '../core/delegate.ts'\nimport { MIN_NODE_MAJOR, isSupportedNode } from '../core/node-version.ts'\nimport { driftWarning } from '../core/pinned.ts'\nimport { reportError } from '../report.ts'\nimport { VERSION } from '../version.ts'\n\n// `rness context | head`: once the reader has its lines it closes the pipe and\n// every further write emits EPIPE on stdout. Nothing is left to deliver, so end\n// quietly with success. This is the one process.exit() in the code base — it\n// runs only when there is no output left to drain (see main() below).\nprocess.stdout.on('error', (e: NodeJS.ErrnoException) => {\n if (e.code === 'EPIPE') process.exit(0)\n throw e\n})\n\n// Set process.exitCode rather than calling process.exit(): process.exit()\n// terminates before Node drains an async stdout pipe, truncating piped output\n// at the ~64 KiB pipe buffer (breaks `rness context --json | jq`, `$(...)`).\nasync function main(): Promise<number> {\n if (!isSupportedNode(process.versions.node)) {\n process.stderr.write(\n `rness needs Node ${MIN_NODE_MAJOR} or newer (running ${process.version})\\n`\n )\n return 1\n }\n const argv = process.argv.slice(2)\n // `create` makes a workspace and `upgrade` replaces the pinned copy: both\n // are served by the copy the user invoked (spec 0006 §3). The login concerns\n // the machine, not a workspace — and git runs `git-credential` from inside\n // a clone, on every fetch: no delegation, and no warning there either.\n const own = [\n 'create',\n 'upgrade',\n 'update',\n 'login',\n 'logout',\n 'git-credential',\n ].includes(argv[0] ?? '')\n const noDelegate = process.env['RNESS_NO_DELEGATE'] === '1'\n const skipDelegation = own || noDelegate\n const command = argv[0]\n if (\n command !== undefined &&\n !command.startsWith('-') &&\n !own &&\n !noDelegate\n ) {\n const warning = await driftWarning(process.cwd())\n if (warning !== null) process.stderr.write(`${warning}\\n`)\n }\n const delegate = skipDelegation\n ? null\n : await findDelegate(process.cwd(), VERSION)\n if (delegate !== null) {\n const broken = (reason: string): Error =>\n new Error(\n `@rness/cli ${delegate.version} pinned in ${delegate.root}/.rness is not installed correctly (${reason}); reinstall in .rness/`\n )\n if (process.env['RNESS_DEBUG'] === '1') {\n process.stderr.write(\n `rness: delegating to @rness/cli ${delegate.version} (${delegate.entry})\\n`\n )\n }\n let mod: Record<string, unknown>\n try {\n mod = (await import(pathToFileURL(delegate.entry).href)) as Record<\n string,\n unknown\n >\n } catch (e) {\n throw broken(e instanceof Error ? e.message : String(e))\n }\n if (typeof mod['run'] !== 'function')\n throw broken('dist/index.js does not export run')\n const delegateRun = mod['run'] as (argv: string[]) => Promise<unknown>\n const code = await delegateRun(argv)\n return typeof code === 'number' ? code : 1\n }\n return run(argv)\n}\n\nmain()\n .then((code) => {\n process.exitCode = code\n })\n .catch((e: unknown) => {\n process.exitCode = reportError(e)\n })\n","export const MIN_NODE_MAJOR = 24\n\nexport function nodeMajor(version: string): number {\n const major = Number.parseInt(version.split('.')[0] ?? '', 10)\n return Number.isNaN(major) ? 0 : major\n}\n\nexport function isSupportedNode(version: string): boolean {\n return nodeMajor(version) >= MIN_NODE_MAJOR\n}\n"],"mappings":";;;;;;;;;;AACA,SAAS,qBAAqB;;;ACDvB,IAAM,iBAAiB;AAEvB,SAAS,UAAU,SAAyB;AACjD,QAAM,QAAQ,OAAO,SAAS,QAAQ,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,EAAE;AAC7D,SAAO,OAAO,MAAM,KAAK,IAAI,IAAI;AACnC;AAEO,SAAS,gBAAgB,SAA0B;AACxD,SAAO,UAAU,OAAO,KAAK;AAC/B;;;ADKA,QAAQ,OAAO,GAAG,SAAS,CAAC,MAA6B;AACvD,MAAI,EAAE,SAAS,QAAS,SAAQ,KAAK,CAAC;AACtC,QAAM;AACR,CAAC;AAKD,eAAe,OAAwB;AACrC,MAAI,CAAC,gBAAgB,QAAQ,SAAS,IAAI,GAAG;AAC3C,YAAQ,OAAO;AAAA,MACb,oBAAoB,cAAc,sBAAsB,QAAQ,OAAO;AAAA;AAAA,IACzE;AACA,WAAO;AAAA,EACT;AACA,QAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AAKjC,QAAM,MAAM;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,SAAS,KAAK,CAAC,KAAK,EAAE;AACxB,QAAM,aAAa,QAAQ,IAAI,mBAAmB,MAAM;AACxD,QAAM,iBAAiB,OAAO;AAC9B,QAAM,UAAU,KAAK,CAAC;AACtB,MACE,YAAY,UACZ,CAAC,QAAQ,WAAW,GAAG,KACvB,CAAC,OACD,CAAC,YACD;AACA,UAAM,UAAU,MAAM,aAAa,QAAQ,IAAI,CAAC;AAChD,QAAI,YAAY,KAAM,SAAQ,OAAO,MAAM,GAAG,OAAO;AAAA,CAAI;AAAA,EAC3D;AACA,QAAM,WAAW,iBACb,OACA,MAAM,aAAa,QAAQ,IAAI,GAAG,OAAO;AAC7C,MAAI,aAAa,MAAM;AACrB,UAAM,SAAS,CAAC,WACd,IAAI;AAAA,MACF,cAAc,SAAS,OAAO,cAAc,SAAS,IAAI,uCAAuC,MAAM;AAAA,IACxG;AACF,QAAI,QAAQ,IAAI,aAAa,MAAM,KAAK;AACtC,cAAQ,OAAO;AAAA,QACb,mCAAmC,SAAS,OAAO,KAAK,SAAS,KAAK;AAAA;AAAA,MACxE;AAAA,IACF;AACA,QAAI;AACJ,QAAI;AACF,YAAO,MAAM,OAAO,cAAc,SAAS,KAAK,EAAE;AAAA,IAIpD,SAAS,GAAG;AACV,YAAM,OAAO,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;AAAA,IACzD;AACA,QAAI,OAAO,IAAI,KAAK,MAAM;AACxB,YAAM,OAAO,mCAAmC;AAClD,UAAM,cAAc,IAAI,KAAK;AAC7B,UAAM,OAAO,MAAM,YAAY,IAAI;AACnC,WAAO,OAAO,SAAS,WAAW,OAAO;AAAA,EAC3C;AACA,SAAO,IAAI,IAAI;AACjB;AAEA,KAAK,EACF,KAAK,CAAC,SAAS;AACd,UAAQ,WAAW;AACrB,CAAC,EACA,MAAM,CAAC,MAAe;AACrB,UAAQ,WAAW,YAAY,CAAC;AAClC,CAAC;","names":[]}